populateModel usage with eager loading related records When retrieving a record in a service and returning a model the static methods: populateModel and populateModels on BaseModel are useful.

When eager loading a related model in the record query, for example:

$variantRecord = Market_VariantRecord::model()->with('product')->findById($id);

This will load the related Record's product into the $variantRecord accessible with $variantRecord->product

when casting this variant record into a Model, using VariantModel::populateModel, will the product also being retrieved be cast into the model->product (AttributeType::Mixed) of the variant?

Or should we manually be manually assigning the ->product attribute as casting it to a model with populate model like this?

$variant = Market_VariantRecord::model()->with('product')->findById($id);
$variantModel = Market_VariantModel::populateModel($variant);
$variantModel->product = Market_ProductModel::populateModel($variant->product);
return $variantModel;

This seems clunky and am looking for suggestions on improving the population of models from records with eager loading of relations.

This is one area

Would like to know if this is the best solution? Upvote if you think it is.

The solution is make your own custom populateModel method to catch the eager loaded attributes

/**
 * @param array|Market_VariantRecord $values
 *
 * @return $this
 */
public static function populateModel($values)
{
    $model = parent::populateModel($values);
    if (is_object($values) && $values instanceof Market_ProductRecord) {
        $model->product = $values->product;
    }

    return $model;
}

I suppose the Craft core BaseModel could check if the value of type BaseRecord and do the same?


Craft 3 - Save new Entry with Relations (Categories/Assets) i simply can not get my "job" done. I try to create a plugin for Craft, a simple one. It reads some date and should create new entries out of this data. First i stuck on a stupid save-mistake, someone here helped me get my error :)

Now, i  can create and save an entry.
But i can not add a category, an image or a matrix-based entry.
I tried several old craft 2 tutorials and got with the categories to Craft::$app->relations->saveRelations, but after i got rid of all errors, it saves my entry, create a ne row in my database relation table, but in the entry, i can not find the relation. The database row in relations seems to be incorrect. Last version of my code: i tried to convert this Problem/Example into my version in craft 3. No success :(

I think, if i understand my problem, i will be able to create several relations, because categories, Assets and so on are just types of fields. And they are related to the entries - am i right ?

I'll show my current code.....has anyone a good advise or an example how to add a relation(category) and how to create and add an image asset ?

    $section = Craft::$app->sections->getSectionByHandle('mychannelentryhandle');
    $entryTypes = $section->getEntryTypes();
    $entryType = $entryTypes[0];
    $entry = new \craft\elements\Entry();
    //      $entry->id = $uid;
    $entry->title = $title;
    $entry->sectionId = $section->id;
    $entry->typeId = $entryType->id;
    $entry->siteId = 1;
    $entry->authorId = 1;
    $entry->enabled = true;  // TODO:  Decide if published or not
    $entry->postDate = $postDate;
    $entry->slug = 'test_' . $uid;

    if (Craft::$app->elements->saveElement($entry)) {
        echo 'New entry created successfully.';

        // SAVE CATEGORY RELATION
        $field = Craft::$app->fields->getFieldByHandle('categoryfieldhandle');
        try {
            $relationsIds[] = $entry->id;
            Craft::$app->relations->saveRelations($field, $category, $relationsIds);
        } catch (\Exception $e) {
            // Something went wrong – return `false` or an error message, log the exception?
            print_r('ERROR WHILE SAVING CATEGORY');
            print_r($e->getMessage());
            return false;
        }

        // Save the new Relation
        Craft::$app->elements->saveElement($entry);

        $result = true;
    } else {
        echo 'Could not save the new entry.' . "\n";
        print_r($entry->getErrors());
        $result = false;
    }

Just testing code, not pretty but functional ;)

This should be as simple as setting the relation field (category, asset, etc.) value on the entry as an array of related element IDs before saving.
$entry->setFieldValue('categoryFieldHandle', [$categoryId1, $categoryId2]);

Craft::$app->elements->saveElement($entry);

This will work for all relation fields:
$entry->setFieldValue('assetFieldHandle', [$assetId1, $assetId2]);

Craft::$app->elements->saveElement($entry);


Problem with matrix if statement for hero image other content ends up in my div Been playing with a hero matrix field and can't seem to get this working correctly when all block types are not used.

I have four possible block types for this hero matrix field. Image, Heading, Sub Heading, and CTA. If I use each block type it will display fine, if I don't use all of the block types the rest of the page content ends up in my container for the headings and cta of the hero image.

Example I create a hero but don't add a cta button so I only used 3 of the 4 types.

Pretty sure this has something to do with my if statement.

Here is my code.

{% for block in entry.hero %}
    {% if block.type == "image" %}
        {% set image = block.heroimage.first() %}

                {% elseif block.type == "heading" %}

{{ block.heading }}

                {% elseif block.type == "subHeading" %}

{{ block.heading }}

                {% elseif block.type == "cta" %}
                    {% set ctaURL = block.ctaURL.first() %}
                    {% set ctaText = block.ctaText %}
                    {% if ctaURL %}
                        {{ ctaText }}
                    {% endif %}

    {% endif %}
{% endfor %}

Here is a screenshot of my matrix config.

The problem is that the if statements are not nested properly. If your matrix does not contain the cta block for example then the 2 closing div tags will never be output, which obviously destroys the layout.

The bigger question for me is why you would want to separate the fields into different blockTypes at all. An easier approach would be to create one blockType (called 'imageHero' for example) with all 5 fields in it ('image', 'heading', 'subHeading', 'ctaEntry', 'ctaText'), which you could access directly. In this way, the blockTypes could be used for different 'types' of hero, rather than for separating out a single hero's fields.

{% for block in entry.hero %}

    {% if blocktype == 'imageHero'%}

        {% set image = block.heroimage.first() %}
        {% if image %}

                    {% if block.heading %}

{{ block.heading }}

                    {% endif %}
                    {% if block.subHeading %}

{{ block.heading }}

                    {% endif %}
                    {% if block.ctaEntry and block.ctaText %}
                        {{ block.ctaText }}
                    {% endif %}

        {% endif %}

    {% endif %}

{% endfor %}


Issue with Vagrant/Homestead setup (server requirements message) I'm trying to set up a local dev environment for Craft. I've been following this video So far, I've installed Composer, Vagrant, and Virtual Box. I have my Homestead.yaml file set up and going to homestead.dev is loading craft, but now I'm getting the following message about not meeting the server requirements and a blank list of requirements.

Any thoughts on what I'm missing, or where I could look? Thanks!

You can look at server logs and craft logs to begin with. You can also check the browser console.


Querying an array of users I need to displaying some notifications on a Craft site, and I want to allow users to mark each notification as read. There won't be many users or notifications, so a plugin seems overkill. Instead I've created a Notifications section with a Read by (Users) field called notificationReadBy.

Notifications display on the template inside an entry form, with a hidden multi-select field containing all users who've marked the notification as read (i.e. already submitted the form) plus the current user's id in case they want to mark as read too. That works fine to build a list of users who've already read the notification.

Next step is to hide the notification altogether if the current user has already marked it as read, and is listed in the Read by field. So I thought something like this might work...

{% for entry in craft.entries
    .section('notifications')
    .all() %}
    {% set readBy = entry.notificationReadBy %}
    {% if currentUser.id not in readBy %}
        {# Show the notification #}
    {% endif %}{% endfor %}

Does that make sense? Anyway, it's not working as expected and is displaying the entry regardless of whether the current user is listed in Read by or not. Any ideas? Maybe it's neater to move the query up to the entries tag, using the .search() param, but I can't get that working either?

Thanks to Jason and Ben for the assist. I changed tac a bit and moved the field on to the user (as an Entries field). Now got this working:

{# Display unread notifications for current user #}
{% set notificationsRead = currentUser.userNotificationsRead.ids() %}
{% set notifications = craft.entries
    .id(['not']|merge(notificationsRead))
    .section('notifications')
    .all() %}

{% for entry in notifications %}
    {{ entry.title }}
{% endfor %}


In entries query, is there a way to check if matrix block is used? I would like to get entries if a matrix field is used. Something like this:

{% set query = craft.entries()
    .section('blog')
    .search('myMatrixField|length')
    .limit(10)
 %}

In this specific case, yes. You can filter your entry query by whether a specific matrix field has at least one block like so:

{% set query = craft.entries({
    section: 'blog',
    myMatrixField: ':notempty:',
    limit: 10
}) %}

or for fields that have 0 matrix blocks in the specific field:

{% set query = craft.entries({
    section: 'blog',
    myMatrixField: ':empty:',
    limit: 10
}) %}

However these are the only two values available for this specific field type. You cannot, for example, search for entries with 3 or more blocks.


How do I create an advanced route with an ID that hits a controller method I am trying to create a page I can hit to update a record in the db, something I am used to doing in Laravel, but now have to do in Craft.

The controller method would look something like this:

public function updateUserRecordsTest($userId) {

    $user = craft()->users->getUserById($userId);
    // $data['status'] = 'asleep';
    // $user->update($data);

}

So I am trying to make a route in craft/config/routes.php like this

 route => user/update/{id} , controller => UsersController@updateUserRecordsTest;

Any suggestions on how to set this route up in Craft would be greatly appreciated.

*Update

So I will try to clear up what I am trying to do.

I need to update the users db when they pass classes.  This will most likely be a boolean, but may end up being an enum.  So what I would usually set up is a page with a controller method like below

 // sudo code

 public function passedThisClass($id) {
    $user = User::find($id);
    $data['passed'] = 1
    User::update($data);
 });

the route would look like

 // sudo code
 route::get('updatethisclass/{id} => UsersController@updateThisClass);

When I hit that page it updates the value from 0 to 1 flagging that they passed the class.

In your plugin's main class file you'd set the registerCpRoutes method to something like this:

public function registerCpRoutes()
{
    return array(
        'user/update/(?P<userId>\d+)' => array('action' => 'pluginHandleController/updateUserRecordsTest'
        ),
    );
}

The the method in your plugin's controller would look something like this:

public function actionUpdateUserRecordsTest(array $variables = array())

And $variables will be a collection of all of the named regex patterns captured in your route definition.


Staging Server Database Not Connecting I've moved my site to the staging server but now I'm getting a craft can't connect to the database with credentials in craft/config/db.php

Thing is I uploaded this script with the same credentials and it connects just fine. Any idea what I could be doing wrong

<?php
# Fill our vars and run on cli
# $ php -f db-connect-test.php
$dbname = 'name';
$dbuser = 'user';
$dbpass = 'pass';
$dbhost = 'localhost';
$connect = mysql_connect($dbhost, $dbuser, $dbpass) or die("Unable to Connect to '$dbhost'");
mysql_select_db($dbname) or die("Could not open the db '$dbname'");

echo "connected to the database";

My full db.php file with actual db creds removed - updated to include domain from live site.

<?php

/**
 * Database Configuration
 *
 * All of your system's database configuration settings go in here.
 * You can see a list of the default settings in craft/app/etc/config/defaults/db.php
 */

return array(
    '*' => array(
        'tablePrefix' => 'craft',
    ),
    'nec.dev' => array(
        'server' => 'localhost',
        'user' => 'root',
        'password' => 'root',
        'database' => 'nec',
    ),
    'nec.dev.stagingdomain.com' => array(
        'server' => 'localhost',
        'user' => 'staging-user',
        'password' => 'staging-password',
        'database' => 'staging-database',
    ),
    'nec-inc.com' => array(
        'server' => 'localhost',
        'user' => '',
        'password' => '',
        'database' => '',
    ),
);

I can't figure out why this doesn't work on staging when the same credentials work with the connection testing script.

It looks as if you've sorted thing out (dev.nec... vs nec.dev...)

In general, I find the domain name matching in the config files to be confusing and error prone. I always follow the advice in the Foolproof Multi-Environment Config recipe. Put this at the top of your index.php:

switch (strtolower($_SERVER['SERVER_NAME'])) {
  case 'nec.dev':
    define('CRAFT_ENVIRONMENT', 'DEV');
    break;
  case 'nec.dev.stagingdomain.com':
    define('CRAFT_ENVIRONMENT', 'STAGE');
    break;
  case 'nec-inc.com':
    define('CRAFT_ENVIRONMENT', 'LIVE');
    break;
  default:
    exit('No environment defined for '.$_SERVER['SERVER_NAME']);
}

Then your db.php and general.php will look like this:

return array(
  '*' => array(...),
  'DEV' => array(...),
  'STAGE' => array(...),
  'LIVE' => array(...),
);

And when you hit dev.nec.stagingdomain.com you will see that No environment defined error and fairly quickly see what is wrong.

(There is also an Enhanced Foolproof Multi-Environment Config recipe)


Commerce - variant URL param I noticed that variants can have their own URL when i outputted URL of line item in the cart. Url looks something like that:
http://localhost/test/web/test-prod?variant=27

Is there any way to change variant keyword in URL to something else?
When we want to display specific variant on product page, we need to perform specific element query based on variant param ourselves, yes? Because only product variable is injected into template.

Not sure how you're outputting the url to the variant itself, but you can always create your own link and change the variant paramater to whatever you want.
Something link this: {{product.url}}?keywordyouwanttouse={{variant.purchasableId}}
On your product page you can then check if the url has the parameter and get the data of that variant.
I use the following code at the top of my product template and then use the selectedVariantto display sku, images, prices, ...
Here I use the variant querystring but you can change that to whatever querystring paramater you want to use.
{% set variantId = craft.app.request.getQueryParam('variant') ?? false %}
{%  if variantId %}
    {% set selectedVariant = craft.variants.id(variantId).one() %}
{% else %}
    {% set selectedVariant = product.defaultVariant %}
{% endif %}


Copy uploaded image in event assets.onSaveAsset Is there a way to copy an uploaded image in the assets.onSaveAsset event?

I want to make a copy of the original uploaded image (for internal purposes) and after that I want to add a copyright text on the uploaded image (for displaying on webpage).

UPDATE
Some more info, to prevent missunderstandings: I use the BusinessLogicPlugin. Users can upload a picture which should be stored in S3. First I want to make a copy of this picture and after that to the picture should be added a small text. The copy works fine.

But now I want to modify the uploaded image.

My Code:

craft()->on('assets.onSaveAsset', function(Event $event) {

  static $recursionLevel = 0; // is set only once

  if ($recursionLevel == 0 ) {

    if ($event->params["isNewAsset"]) {
      $recursionLevel++;

      $asset = $event->params['asset'];
      $remoteFile = $asset->getUrl();

      // get remote image and store in temp path
      $imageInfo = pathinfo($remoteFile);
      $tempPath  = CRAFT_STORAGE_PATH . 'runtime/temp/' . $imageInfo['basename'];
      file_put_contents($tempPath, fopen($remoteFile, 'r'));

      // only JPEG allowed
      $im = @ImageCreateFromJPEG ($tempPath);
      $textcolor = imagecolorallocate($im, 0, 0, 255);
      // add text and save to original path
      imagestring($im, 5, 0, 0, 'Hello world!', $textcolor);
      imagejpeg($im, $tempPath);

      // update the image, but how?!

    }
  }
});

How can I update the asset with the new generated file in $tempPath?

I'm thinking you should use the onBeforeUploadAsset event for this instead.  You can get the path to the temporary location on disk via the path parameter like so:

$path = $event->params['path'];

From there, you can make a copy of the file and save to whatever location you want.  You can also watermark the original and save it back to the same temporary location and let the request continue.


Send email notification to admins when a new entry is created Anyone written a simple plugin that does this out there? When a user submits a form on the frontend, it creates a new entry (with the default status set to disabled). I'd like to set it up so that the admins receive an email notification every time a new disabled entry is created for that section.

You can do this with a simple module. You can set one up at pluginfactory.io with no controllers and a service called Email. If you're unsure about how modules work or how to set one up, I can highly recommend reading Andrew Welch's guide to creating a module in Craft 3.
In this code, I've called the module 'MyModule' but you can give it any name you like.
Update the main module file's init method to look something like this:
public function init()
{
    parent::init();
    self::$instance = $this;

    // This line calls a private method to register a new email message in Craft
    $this->_registerEmailMessages();
}

At the bottom of your main module file, add the _registerEmailMessages() function:
private function _registerEmailMessages()
{
    Event::on(
        SystemMessages::class,
        SystemMessages::EVENT_REGISTER_MESSAGES,
        function(RegisterEmailMessagesEvent $event) {
            $event->messages = array_merge($event->messages, [
                [
                    'key' => 'my-module_publish-notification',
                    'heading' => Craft::t('my-module', 'my-module_publish-notification_heading'),
                    'subject' => Craft::t('my-module', 'my-module_publish-notification_subject'),
                    'body' => Craft::t('my-module', 'my-module_publish-notification_body'),
                ]
            ]);
        }
    );
}

Note: you'll need to add the correct 'use' statements at the top of your module file, if you're not using an IDE like PhpStorm which adds them automatically:
use craft\events\RegisterEmailMessagesEvent;
use craft\services\SystemMessages;

What this function does is to add a new system email message with the key 'my-module_publish-notification', with translation strings for the heading, subject and body. This email will be accessible and editable in the CP by going to Utilities > System Messages.
Now you need to add some text to those translation strings. Your Pluginfactory module should come with everything set up for this. Look in the src/translations/en folder and you should find a file named after your module, e.g. 'my-module.php'
In this file, you can add the text that you want to be shown in this email by default:
'my-module_publish-notification_heading' => 'When an entry is created:',
'my-module_publish-notification_subject' => 'An entry has been created',
'my-module_publish-notification_body' => "A new entry has been created by {{ username }}.",

The heading is what's shown in the System Messages screen above the email, and the subject and body are exactly what you'd expect: the subject of the email, and the body text of the email.
The next step is to listen to the EVENT_AFTER_SAVE event to trigger the email itself. Add another line to your module's init method:
$this->installEventListeners();

Then add the installEventListeners() method at the bottom of the module file:
protected function installEventListeners()
{
    Event::on(
        Entry::class,
        Element::EVENT_AFTER_SAVE,
        function(ModelEvent $event) {
            /** @var Entry $entry */
            $entry = $event->sender;

            if (ElementHelper::isDraftOrRevision($entry)) {
                return;
            }

            if ($entry->isNew) {
                $this::$instance->emailService->sendEntryNotification($entry);
            }
        }
    );
}

This method adds a listener which will be called after an entry is saved, and if the entry is a new entry (i.e. it's not an existing entry being edited) then it calls the Email service's sendEntryNotification method.
In your Email service, that method would look something like this:
public function sendEntryNotification(Entry $entry)
{
    $admins = User::find()
        ->admin(true)
        ->all();

    foreach($admins as $admin) {
        $mail = Craft::$app->getMailer()
            ->composeFromKey('my-module_publish-notification', [
                'username' => $entry->author->username
            ]);

        $mail
            ->setTo($admin)
            ->send();
    }
}

This method fetches all admins, then loops through each one and sends the notification email as defined by the key that's passed into composeFromKey -  the key should match the string that we set as the key in our _registerEmailMessages function.
You can pass any sort of data through to your email. I've shown how to pass the username of the entry's author, but you can pass any data through in an array as the second argument in composeFromKey. The key of each data can be used in your email's content to show the value. In this example, the translation string for 'my-module_publish-notification_body' shows the username like this:
"A new entry has been created by {{ username }}."

You could pass in the name of the user, the creation date and any other data from the entry.
The last step is to register your module with your Craft app. The Pluginfactory download comes with a config directory with an app.php file inside. Copy the code that starts with 'my-module' - it should look something like this:
'my-module' => [
    'class' => \modules\mymodule\MyModule::class,
    'components' => [
        'email' => [
            'class' => 'modules\mymodule\services\Email',
        ],
    ],
],
'bootstrap' => ['my-module'],

Open up your Craft app.php file, which lives in the root config directory, and add the code from above. This should end up looking something like this:
return [
    '*' => [
        'modules' => [
            'my-module' => [
                'class' => \modules\mymodule\MyModule::class,
                'components' => [
                    'email' => [
                        'class' => 'modules\mymodule\services\Email',
                    ],
                ],
            ],
        ],
        'bootstrap' => ['my-module'],
    ],
];

The final step is to make Composer happy. Add your module to your composer.json's autoload object:
"autoload": {
    "psr-4": {
        "modules\\mymodule\\": "modules/mymodule/src/"
    }
}

Then run composer dump-autoload to rebuild the Composer autoload map.
That should get you almost all of the way there! You may need to update your event listener to make sure it's only calling the email method for the correct section(s) within your site.


Cannot get file upload/attachment with Sprout Forms working I'm trying to add an file upload/attachment to my Sprout Form submission, but even though all the other fields are saved & the Sprout Email notification is sent - the file field remains empty - no file is uploaded & so no attachment is sent.

I've checked that my form contains enctype="multipart/form-data"
I've checked that the file type I'm trying to upload is an allowed file
type for the Asset folder ie: PDF
I've checked that the file is a reasonable size - in this case 3KB
I've checked the craft log, sprout forms log & sprout email log & there are no errors.

Anyone have any ideas ? Below is my form code - what am I missing ?

<form id="careerApplication" method="post" action="" accept-charset="UTF-8"
enctype="multipart/form-data">
{{ getCsrfInput() }}
<input type="hidden" name="action" value="sproutForms/entries/saveEntry">
<input type="hidden" name="handle" value="careerApplication">
<fieldset>
    {{ craft.sproutForms.displayField('careerApplication.fullName') }}
</fieldset>
<fieldset>
    Reference #
    <input type="text" name="fields[refNo]" id="fields-refNo" readonly value="{{ craft.request.getParam('ref') }}" />
</fieldset>
<fieldset>
    {{ craft.sproutForms.displayField('careerApplication.email') }}
</fieldset>
<fieldset>
    {{ craft.sproutForms.displayField('careerApplication.telephone') }}
</fieldset>
<fieldset>
    Motivation
    <textarea name="fields[motivation]" id="fields-motivation" placeholder="Motivation"></textarea>
</fieldset>
<fieldset>
    Submit your CV:
    <input type="file" name="fields[cvUpload]" id="fields-cvUpload" />
</fieldset>
{{ craft.sproutInvisibleCaptcha.protect() }}
<input id="submit_application" name="submit_application" type="button" value="Submit Application" class="red-btn" />
</form>

Figured out what my problem was: I was trying to submit the form via Ajax, that's why the submit button is set to input type="button"
I changed the submit button to input type="submit" & it worked.
Not 100% sure how to make it work with Ajax but pretty sure it has something to do with the way the data is serialized. I don't really need Ajax submission in this instance, so not bothering with it anymore.


Error with uploading Asset Files: net::ERR_EMPTY_RESPONSE In my local MAMP site I'm not able to upload any files.

Gives me this error in Chrome:

http://nobel.craft.dev:8888/index.php?p=admin/actions/assets/uploadFile net::ERR_EMPTY_RESPONSE

This error in Safari

"Failed to load a resource: The network connection was lost."

Things I did

I've checked the asset folder path
makes sure folder is writable
nothing strange in the craft.log
delete cache in admin
download new version of craft and replaced the app folder

That usually means "something fatal happened" when trying to upload the file.  The usual culprits are the php.ini max_execution_time and memory_limit settings, but if it's happening for small files too, something else could be going on.

Have also seen it happen when you're uploading an image in a format your underlying image processing library doesn't support.  i.e. your server has GD installed, but you're trying to upload a 32-bit PNG.  Or you're running an older version of GD or Imagick that has a bug you're hitting.

Check if you have a craft/storage/runtime/logs/phperrors.log file.  Any PHP fatal errors will be logged there.  If not, then check the Apache error logs to see if it's an Apache problem.


Invalidating the cache tag from a plugin? I have a plugin that renders HTML via a hook.  If someone wraps that hook with {% cache %} in their templates, how can my plugin tell Craft to invalidate the cache when something within it changes?

EDIT: Let's say, for example, that I'm getting JSON data from an external source via my plugin.  I could be rendering this via a hook, or using a Variable, it doesn't matter that much.

There's no Element that the content that changes can be linked to, in order to invalidate the cache.

The TemplateCacheService provides a ton of helpful methods to take care of this for you.

While you can explicitly tell Craft to invalidate certain caches (by element ID, etc.) if you need to, I prefer to use includeCriteriaInTemplateCaches() and includeElementInTemplateCaches() to register the appropriate element(s)/criteria with the template cache, so that Craft takes care of cache invalidation according to its own internal logic:

$myCrtieria = ... ; // (some ElementCriteriaModel)

$cacheService = craft()->getComponent('templateCache', false);

if ($cacheService)
{
    $cacheService->includeCriteriaInTemplateCaches($myCriteria);
}

...

p.s. Unless you really need to render HTML via a Twig hook, I strongly suggest refactoring to use a custom Template Variable instead. Doing so is more consistent with Craft's own internal practice. Twig hooks are typically used to change context variables and perform other logic that is specific to changing how Twig renders templates. For doing content-related business logic (e.g. fetching data from the database and rendering it into templates), using Craft's provided structures will save you trouble down the road.


What password requirements exist for new users? My site contains a front-end registration form. New users will be automatically activated, and can begin using their account immediately.

What password requirements exist?

I vaguely remember a 6-character minimum... Is that accurate, and are there any other requirements to be aware of?

Craft 2:

By default Craft enforces a 6 character minimum as the only password requirement, but as of Craft 2.3, a plugin can now listen for the users.onBeforeSetPassword event to enforce any password requirements it wants for newly set/changed passwords and set $performAction = false to cancel the action with any validation errors returned on the UserModel.

Craft 3:

This has changed quite a bit for Craft 3... here is how you would pull it off:

use craft\elements\User;
use yii\base\Event;
use yii\base\ModelEvent;

Event::on(User::class, User::EVENT_BEFORE_VALIDATE, function(ModelEvent $event) {
    /** @var User $user */
    $user = $event->sender;

    if ($user->newPassword !== null) {
        $validates = $this->validateNewPassword($user->newPassword);

        if (!$validates) {
            $user->addError('newPassword', Craft::t('plugin-handle', 'Invalid password'));
            $event->isValid = false;
        }
    }
});


Routing templates thats start with an underscore More of a question than problem; I see this often - templates that start with an underscore. For example, _entry.html.

Reading the Craft docs:

  Note that if any of the URI segments begin with an underscore ('_'), Craft will return a 404, since template path segments that begin with an underscore are considered "hidden" from direct access

I guess I'm confused as to what I am hiding from. Is it so I can't directly browse right to the full path of that template?

mysite.com/something/something/_template.html

If you move the craft folder outside of the site root, I can't really get to that file can I? It just isn't obvious to me (yet) what the benefit is and when/where/why I need to start a template with and underscore.

Thank you!

Yes, prefixing a template's filename with an underscore does what it says on the box – any underscored template will be excluded from route mapping.

A typical use case is to use underscores for partials or wrappers, e.g. "_mainmenu.html" or "_baseTemplate.html" – essentially any template that doesn't make any sense to load up or access directly in the browser via that template's path.

This isn't a mandatory thing, but a good practice.


Control Panel access. Greyed out button The Admin login screen for my site is not working.

The button to submit my credentials is greyed out and submission by pressing enter is not working either.

Not sure how to go about solving this one or updated the site to fix it if there is a bug?

If your look at your browser's console on that page you'll see your getting several JavaScript errors:

Which seems to indicate that some of your files didn't FTP up properly and are corrupted.  Try re-uploading your files and make sure your FTP client is set to transfer in 'auto' mode.


How do I add class to redactor format list? I understand that to add a "note" class to the redactor text editor drop down, I need to use formattingAdd. But each iteration I've tried does not work. The example is: https://imperavi.com/redactor/examples/formatting-add/

I used the example, edited it for my needs, and added it to the header of my layout template on the Craft site...

<style>
.article-note {
  font-style: italic;
  font-size: 1.2em;
  font-weight: 700;
}
</style>

<script>
    $(function()
    {
        $('#redactor').redactor({
            focus: true,
            formatting: ['p', 'blockquote'],
            formattingAdd: {
                "article-note-add": {
                    title: 'Note',
                    args: ['h6', 'class', 'article-note', 'toggle']
                },
            }
        });
    });
</script>

Didn't work. Also, I didn't know how the textarea in the example is used or where it should go.

A lot of people in the discussion boards I've researched have recommended plugins. I am not interested in a plugin. Both Craft CMS and redactor state that there is a way to do it without a plugin. While it hasn't worked from me so far, I'm sure someone has succeeded and I'm hoping to learn.

Your example code attempts to create a new Redactor instance, which (as Brad says) won't work inside the control panel (where Craft has created the Redactor instances already).

You can, however, use the Redactor config files to add an element with the classname .note. To do that, add the following to the file config/redactor/Standard.json (or if you configured your RichText field to use a different configuration, the JSON file corresponding to that configuration):

"formattingAdd": {
  "note": {
    "title": "Note",
    "args": ["p", "class", "note"]
  }
}

Note that the first parameter in the args property is the tag which the .note element will use – according to the Redactor docs, only p, pre, blockquote, div and heading tags can be used with formattingAdd.

Also note that unless you add some custom CSS, the .note will – of course – not be styled any differently than a normal paragraph.

Unfortunately, there's no way to add custom CSS and JavaScript to the Control Panel without a custom plugin – either by writing your own or by installing the CP CSS and CP JS plugins.


relatedTo a category in a matrix field All, I'm very close, but missing something and any help is appreciated. I have a teamMember matrix block allowing me to add employees with some basic info. One of the fields is a category field called 'department'. I"m trying to list the departments, and within them, the employee information.

Currently my output is this:

Marketing
  marketing member one
  marketing member two
  creative member one
  creative member two

Creative
  marketing member one
  marketing member two
  creative member one
  creative member two

However, I'm looking to achieve this:

Marketing
  marketing member one
  marketing member two

Creative
 creative member one
 creative member two

It seems that I'm doing something wrong with relatedTo.

My code:

        {# Fetch entries with a Matrix block to get non empty categories #}
        {% for block in entry.teamMember.all() %}
        {% set category = block.department.one() %}

        {# Fetch entries with a Matrix block related to the category #}
        {% set entries = craft.entries.relatedTo(category).all() %}

{{ category.title }}

            {% for block in entry.teamMember.all() %}

                {% set image = block.photograph.one() %}
                {% if image %}

&nbsp;

                {% endif %}

{{ block.fullName }} {{ block.jobTitle|markdown }}

                {{ block.biography }}

            {% endfor %}

        {% endfor %}

What am I missing? Thank you in advance!

Figured it out:

    {# Fetch entries with a Matrix block to get non empty categories #}
    {% for block in entry.teamMember.all() %}
    {% set category = block.department.one() %}

        {% if category.title != 'Ownership' %}
{{ category.title }}
{% endif %}
        {% for block in entry.teamMember.relatedTo(category).all() %}

            {% set image = block.photograph.one() %}
            {% if image %}

&nbsp;

            {% endif %}

{{ block.fullName }} {{ block.jobTitle|markdown }}

            {{ block.biography }}

        {% endfor %}

    {% endfor %}


Craft CMS FeedMe plugin - Process Instagram JSON Feed I'm trying out the new FeedMe plugin v2 beta 1 and am trying to download my recent photos posted on Instagram via the Instagram API. It appears Instagram is appending a cache parameters to the end of image URLs so when I try to download the images to a CraftCMS Assets folder Craft doesn't allow me to as it's not an allowed file extension.

The specific example is:

Instagram Request to get recent posts:
https://api.instagram.com/v1/users/self/media/recent/?access_token=ACCESS-TOKEN

example URL for image returned:
somepic_n.jpg?ig_cache_key=hghhh....

Does anyone have any bright ideas how to handle this so I can download the images at the URLs or some option / feature then could be incorporated into the plugin to remove querystrings from the end of asset urls?

Thanks

Check out the cleanAssetUrls configuration added just for you!

http://sgroup.com.au/plugins/feedme/getting-started/configuration


How do I get extracts in search results? Say someone searches for "John Smith". Using the search results template on the Craft website, my search results are then a list of titles for entries that include the term "John Smith". For example:

Dave Taylor wins award
New building built

The user experience is quite poor as there is no indication that these articles are in any way related to "John Smith". What I'm trying to do is output the following:

Dave Taylor wins award

...with the help of his brother John Smith,...

New building built

...architect John Smith was instrumental...

i.e. something similar to Google search. Any ideas how I could go about this?

I think creating your own Twig filter is your best bet.
So, create a filter that takes a string, searches that for another string and returns an extract. It could do this by using some regular expression hocus pocus, I guess.

{{ entry.bodyText | extract('your search term', 3, 3) }}

would be how you'd use it (assuming, for example, that your filter accepts the three arguments searchTerm, wordsBefore and wordsAfter).


How can I display just the "lowest" categories assigned to an entry and not parent categories? I have a category tree like this and I have an entry that has four categories A, A1, B, C2:

A

A1
A2

B

B1
B2

C

C1
C2

I want a query that ignores A and returns only A1, B, C2, because I don't want to display the parent category of a sub-category that's also displayed.

{% for category in entry.businessTypes.all() %}{{ category.title }}{% endfor %}
returns all: A, A1, B, C2

{% for category in entry.businessTypes.leaves().all() %}{{ category.title }}{% endfor %}
returns A2, C2 but not B.

{% for category in entry.businessTypes.hasDescendants().all() %}{{ category.title }}{% endfor %}
returns A, B but not A1, C2.

Any suggestions are appreciated.

I could not test it, but you could try:

If you always have a Parent and 2 levels

entry.businessTypes.level('>= 2').all()

If you want to only get the lowest, you could try to assign it with a condition:

{% if categories = entry.businessTypes.children %}
    {% set categories = entry.businessTypes.level('>= 2').all() %}
{% else categories = entry.businessTypes.children %}
    {% set categories = entry.businessTypes.all() %}
{% endif %}

{% for category in categories %} ... {% endfor %}


Schedule plugin not working for Feed Me plugin I have created a schedule to run the 'Feed Me' every 1 minute using the plugin 'Schedule', but it doesn't work. I not sure am I using the correct setting or not. I manage to get the latest data if I manually click the 'Run Feed'.
This is my setting for 'Schedule'

This is my timer

Guessing you didn't do step #4 in the readme? https://github.com/panlatent/schedule/blob/master/README.md

Add a record to system crontab:

* * * * * php /path/to/craft schedules/run 1>> /dev/null 2>&1


How do you route an entry or category URI to a plugin controller? Craft's documentation states: "If the URI matches an entry’s or category’s URI, the section’s/category group’s template will get loaded".  Instead of loading the template, how can I set the route to run an action instead?  In config/routes.php, I set 'entryurl' => array('action' => 'action/path'), but this does not work.

You want to take a look at the registerSiteRoutes() method in your plugin class. In this example, it will route a request to your MyPluginHandle_FooController::actionBar() method. foo is obviously the name of the controller, and bar is the method.

public function registerSiteRoutes()
{
    return array(
        'some/route/here' => array('action' => 'myPluginHandle/foo/bar'),
    );
}

Here is what the controller would look like with these naming conventions:

<?php
namespace

class MyPluginHandle_FooController extends BaseController
{
    // Allow anonymous users (users that aren't logged in) to use this controller
    protected $allowAnonymous = true;

    public function actionBar()
    {
        // do something
    }
}

You can also do the same thing for CP routes.

http://buildwithcraft.com/docs/plugins/templates#dynamic-url-routing


Removing _home_ from Home Page link I have a single page selected as home page by ticking "This is for homepage" but now I find link comes up as  http://example.com/index.php/__home__

I want the home link to only be  http://example.com/index.php/ What do I need to do to get rid of __home__?

__home__ is the pretend URI that Craft uses to identify an element that should be treated as the homepage. The only way you would be able to output it is if you were doing something like this:

You should be doing this instead, though, which will remove it:

or,


Trying to template a post's image on an index page with a default image if it does not exist This is for an index page (again).  I want to list the 10 most recent articles.  There is a field postImage.  It is not required.  If it exists, I want to use postImage.  If not, I want to use a default image.

The relevant code is under the img tag.

I get the following error:  Impossible to invoke a method ("getUrl") on a null variable..  I can verify that 7 of the first 10 entries do have a postImage.

  {% for item in craft.entries.section('posts').limit(10) %}
                              {% set pic = item.postImage.one().getUrl() %}
                              {% if loop.index is divisible by(2) %}

Enterprise

                                        {{ item.title }}

                                       {{ item.introParagraph }}
                                         Read More

                                        by

                                            x
                                        , 2 days ago

                        {% else %}

There are 2 if / else statements.   Only the 1 associated with the img tag is relevant.

Thanks.

Drop the getUrl() from the set and everything works.


How to create CRUD operation in Craft CMS? In front-end can simply create CRUD operation. Pls help

Everything you need for creating and updating content from the front end is covered here: https://docs.craftcms.com/v3/dev/examples/entry-form.html

For deleting you can use the entries/delete-entry action (goes without saying that you'll need to add some kind of security checks).


Caching variables for use elsewhere in a template? I have a long if/else block within a for loop in my template and I wanted to cache all of it (it loops over a Matrix field, checking for different block types on each row and outputs the correct code as needed). However, in one of the ifelse blocks, I have a statement with a RAND parameter that I don't want cached. Something like this:

{% for block in entry.sidebar %}
    {% if block.type == "someType" %}
        ...Presentiaon code
    {% elseif block.type == "someType" %}
        ...Presentiaon code
    {% elseif block.type == "someType" %}
        {% set randomEntry = craft.entries.section("testimonials").order('RAND()').first() %}
    {% endif %}
{% endfor %}

My challenge is trying to find a way to cache this long block but still let the RAND work. My solution was to try to put the matrix field into a variable, cache that variable and then iterate over the variable's data (the thinking here that the variable has all the process-intensive DB queries). Something like this:

{% cache %}
    {% set myField = entry.myMatrixField %}
{% endcache %}

{% for block in myField %}
    {% if block.type == "someType" %}
        ...Presentiaon code
    {% elseif block.type == "someType" %}
        ...Presentiaon code
    {% elseif block.type == "someType" %}
        {% set randomEntry = craft.entries.section("testimonials").order('RAND()').first() %}
    {% endif %}
{% endfor %}

However, this fails after the template has been cached with an error that the 'myField' variable does not exist. So I'm thinking I can't do this?

Is there another way I could cache this? I considered breaking up the ifelse blocks into separate if/endif clauses and caching each except the one with the RAND paramater in it but I'm thinking that won't improve performance much if the for loop still has to run outside of it uncached.

Update: This doesn't work. Leaving it here temporarily in case it helps generate more ideas.

Haven't tried this myself, but you might try returning all testimonials and then use twigs random method to select one after the fact.

{% cache %}

    {% set myField = entry.myMatrixField %}

    {% for block in myField %}
        {% if block.type == "someType" %}
            {% set presentationCode %}
                ...Presentation code
            {% endset %}
        {% elseif block.type == "someType" %}
            {% set presentationCode %}
                ...Presentation code
            {% endset %}
        {% elseif block.type == "someType" %}
            {% set randomEntry = craft.entries.section("testimonials").find() %}
        {% endif %}
    {% endfor %}

{% endcache %}

{% if randomEntry %}
    {% set randomEntry = random(randomEntry) %}
    {% set presentationCode %}
        ...Presentiaon code
    {% endset %}
{% endif %}

{{ presentationCode }}

Note: I suspect that you will need the .find() on the end of the ElementCriteriaModel statement so that it gets converted to an array.

Update: To do this you will also probably need to place all your presentation code in a variable and then display later in the template.


Order/sort a structure section by date too? Is it possible to have a Structure section ordered by date (desc) as a default and then take the custom ordering in the CP into account?

This should work

{% set entries = craft.entries({
    section: 'news',
    sortOrder: 'postDate desc, lft asc',
}) %}


How to utilize unique slugs for plugin elements that extend the BaseElement class I have my slug saving to the element_i18n table but it's not unique. When saving a non-unique slug using the default Craft entries, the slug gets appended with "-1".

How can I extend the default functionality for a slug?

Brad, I realize my initial question is a bit vague, oops! I did however find a solution to why my localized slugs for my plugin element weren't being saved as a unique string.

I traced through ElementHelper::setUniqueUri() and figured out I was missing the getUrlFormat() function. Also to make sure I include {slug} in my urlFormat string in this particular case.


Use official logo and images on my site Can I use official logo and images on my foreign site about Craft CMS? Are there any promo materials in good quality?

Yes, I believe this is perfectly acceptable. As long as you are not pretending to be Craft CMS or Pixel & Tonic, then you shouldn’t run into any legal issues.

They even made their brand assets available for use...

  https://craftcms.com/brand-assets

If you still have questions, send an email to support@craftcms.com.


Fail in JSON using Twig Inside a JSON template I find an entry using id, the result title can't be seen using entry.title but I can see it with entry[0].title. There is another step or encoding I have to use?

If you are using something like this to get your entry:

{% set entry = craft.entries.section('blog').limit(1).id('21') %}

then Craft is actually returning an ElementCriteriaModel object, even if it is only one entry.

To ensure that the entry variable only contains a single entry, you need to call the first() method like this:

{% set entry = craft.entries.section('blog').limit(1).id('21').first() %}

More details in the docs here: https://craftcms.com/docs/templating/elementcriteriamodel


Dedicated channel for news? or a channel for blog with a news category? A clean installation of Craft includes a 'News' channel which has confused me slightly. Would it not be better to have a 'Blog' channel with a 'News' category?

What is the advantage to having a dedicated 'News' channel?

Not every website has a 'blog' section. If your site's other content would, for instance, be something completely different like general company info, it would make perfect sense to have a news section.

In case you do have a blog, it would be perfectly possible to delete the 'news' section and give the blog section an entry type called 'news'. That way you would be able to give news entries their own fields, which is convenient.

It's all possible with Craft, so it really is up to you.


How do I upgrade the MYSQL database from Craft 2 to Craft 3 on GoDaddy How do I upgrade my MySQL DB for a Craft 2 site to Craft 3. I don't have access to the SSH server or use of Composer, so I have to move files via FTP and do everything manually.

My client is on GoDaddy, and need to resolve this as efficiently as possible.

Generally speaking:

Get the Craft 2 site running locally.
Perform the upgrade to Craft 3 locally according to the docs: https://docs.craftcms.com/v3/upgrade.html
Test and make sure everything works as you'd expect.
Upload all fo the files to your web server via FTP (including composer.json, composer.lock, the vendor folder, etc.).
Once load, load the Craft control panel, you'll be prompted to let Craft update the database.
Let Craft update the database.
Done!

Specifics may vary depending on the site and your actual requirements.


Can I select the latest draft of a given entry? I've built a help site for a client that pre-releases articles to a select group of beta users. The updated articles (Craft entries) are published as drafts until features make it out of beta and into the main release.

The client has gone and done the clever thing of using the Live Preview URL (with token) to share with the limited group of beta users who should only have read-only access to the article:

http://cagedwisdom.com/articles/bloopers?token=3p~rKL53nTcTrO_q1XcWsaBNla5VpzTF

Since the token is temporary, this link works for a little while and eventually expires and people can no longer review the draft content.

Is there a way to an entry's latest draft?

Let's pretend that I have a the query parameter &draft=true appended to an article URL, and I use that to selectively get the latest draft:

{% if craft.request.getParam('draft') == true %}
    {% set draftEntry = entry.drafts().order('date desc').first() %}
{% endif %}

Is there a not-imaginary way to do this?

If not, my best ideas are...

To create a plugin that'd allow the editor to create a persistent but removable hash for that specific entry that'd allow access to said draft. (So if the article URL is http://cagedwisdom.com/articles/bloopers, the secret draft share URL would be http://cagedwisdom.com/articles/bloopers&draft=Ag4For3Wom8Ot5eeDd9yAf2g.)
Mess with Craft's token expiration values so the shared links last longer.

Something like this should work:

{% if craft.request.getParam('draft') == true %}
    {% set drafts = craft.entryRevisions.getDraftsByEntryId(entry.id) %}
    {% if drafts|length %}
        {% set entry = drafts[0] %}
    {% endif %}
{% endif %}

{{ entry.title }}
...

This is assuming of course that the entry is published at all. If it's disabled then the uri will return 404. In which case you would have to retrieve the entry in some other way. See this answer for an example.


warning icon on fields I am seeing warning icons on drop down fields like this

Where does this come from?

This warning appears when the content column type of the current selected field is different than the new one -> you'll lose all the information that is currently stored.

For example: changing from number to text won't be a huge problem but changing from text to date will result in a data loss


How to identify the logged in user inside a plugin? I have a public registration form and a plugin which needs to create an entry for a user when that user is activated. That part works. Because I want to force an Admin to activate the user, I am using this code block to stop Craft automatically activating a user on registration:

public function init() {
    craft()->on('users.beforeActivateUser', function(Event $event){
        // stop the user from being able to activate their account. Forces admins to do so.
        $event->performAction = false;
    });
}

But this also stops an Admin from activating the user account via the control panel.

I can't work out how to test that the user performing the event is the admin and therefore skip that blocking code. I'd tried this:

public function init() {
    craft()->on('users.beforeActivateUser', function(Event $event){
        // stop the user from being able to activate their account. Forces admins to do so.
        if($event->params['user']->isInGroup('artists')){
            $event->performAction = false;
        }
    });
}

But that doesn't work, presumably because a new registering user doesn't belong to any group at the point that code is run.

I'm a bit confused by your question.

It sounds as though you want to check if the currently logged-in user is an Administrator, but it looks like you're actually checking whether the newly-registered user is an Administrator.

Here's how to retrieve the currently logged-in User, and check whether they belong to a specific User Group.

function (Event $event)
{
    $user = craft()->userSession->getUser();

    if ($user && $user->isInGroup('groupHandle') {
        // Do stuff.
    }
}

Alternatively, you could check whether the current user has permission to perform a specific action:

if ($user && craft()->userPermissions->doesUserHavePermission($user->id, 'registerUsers')) {
    // Do stuff.
}


Getting entries related to all of supplied categories, if they exist I'm trying to get all entries that related to one .. three categories, as determined whether their id appears in the query string:

{% set sector = craft.categories.id(craft.request.getQuery('sector')) %}
{% set impact = craft.categories.id(craft.request.getQuery('impact')) %}
{% set topic = craft.categories.id(craft.request.getQuery('topic')) %}

{% for post in craft.entries.section(section).relatedTo('and', sector, impact, topic) %}

This works well if each of sector, impact, and topic are present, but will return nothing if any one of sector, impact, topic is null (i.e. getQuery('') is blank). Can anyone suggest a way I can do this?

Completely untested, but try something like:

{% set ids = [] %}

{% if craft.request.getQuery('sector') %}
    {% set ids = ids|merge([craft.request.getQuery('sector')]) %}
{% endif %}

{% if craft.request.getQuery('impact') %}
    {% set ids = ids|merge([craft.request.getQuery('impact')]) %}
{% endif %}

{% if craft.request.getQuery('topic') %}
    {% set ids = ids|merge([craft.request.getQuery('topic')]) %}
{% endif %}

{% if ids|length %}

    {% for post in craft.entries.section(section).relatedTo('and', ids|join(',')) %}
        ....
    {% endfor %}

{% endif %}


How to use form.handle in Custom field.html I've created a custom field.html which is working fine when I manually specify the formHandle for use with submittedEntry as mentioned in the docs:

{% set ourForm = craft.sproutForms.form('formHandle') %}
{% set submittedEntry = craft.sproutForms.getEntry(ourForm) %}

However, I need to be able to have a dynamic formHandle in the field.html as I am using the same file for an infinite number of forms. This means I cannot manually specify the formHandle as it throws the below error (as the form methods for Form A won't exist in Form B)

Craft\SproutForms_EntryModel and its behaviors do not have a method or closure named "fieldNameFormB".

Is there any way I can pass form.handle from form.html into field.html so that I can use the below code, or is there another method by which I'm not forced to manually specify the form?

{% set ourForm = craft.sproutForms.form(form.handle) %}
{% set submittedEntry = craft.sproutForms.getEntry(ourForm) %}

I have updated the plugin files to make this work as I needed it to.

In sproutforms/variables/SproutFormsVariables.php, I have updated the $bodyHtml variable at Line 57 to include handle => $formHandle

$bodyHtml = craft()->templates->render(
    'tab', array(
        'handle'               => $formHandle,
        'tabs'                 => $form->getFieldLayout()->getTabs(),
        'entry'                => $entry,
        'formFields'           => $fields,
        'thirdPartySubmission' => !!$form->submitAction,
        'displaySectionTitles' => $form->displaySectionTitles,
        'renderingOptions'     => $renderingOptions
    )
);

This means in my Template Override in _forms/tabs.html I can pass the handle as a variable

{%- if formField -%}
    {% include "field" with {
        form:                 handle,
        field:                field,
        element:              entry,
        required:             required,
        formField:            formField,
        renderingOptions:     renderingOptions,
        thirdPartySubmission: thirdPartySubmission,
    } only %}
{%- endif %}

I can then do as I required with not needing to specifically specify a form name in my submittedEntry variable

{% set ourForm = craft.sproutForms.form(form) %}
{% set submittedEntry = craft.sproutForms.getEntry(ourForm) %}

The use case for this update is if clients can add forms through the Matrix, you need to get form.handle in submittedEntry so that you can populate the form post using value="{{ attribute(submittedEntry, fieldName) }} if the form fails validation (so that the form doesn't return empty on validation failure).

In the documentation the submittedEntry value is always manually set, however in this example you need to be able to set submittedEntry dynamically so that you can use generic Template Overrides in _forms rather than having overrides per form in _forms/formHandle.


craft()->locale->getTimeFormat() throws Fatal error When requesting the control panel I get the following error:

Fatal error: Call to a member function getTimeFormat() on a non-object in
/craft/app/etc/requirements/RequirementsChecker.php on line 103

Apparently  craft()->locale is null, but I have no idea why this is the case. I'm running craft locally, on http://[PROJECT].craft.dev/ and installed the Pro version.

Any ideas?

Edit: this turned out to be a too restrictive gitignore file, ignoring the craft/app/framework/i18n/data folder

I had the same issue. In our case it were the i18n and the vendor files that were being excluded by a too restrictive gitignore file.

Never use the default gitignore from Craft 3 on a Craft 2 setup. :-)


How should I fix an odd action URL? I have a controller which is eventually going to be set up for POSTs and GETs, but the weird thing is I can only access it through localhost/index.php?p=actions/twoFA/get where twoFA is my plugin handle and get is an action method in my controller. How can I make it so that it behaves like all the examples online where I could go to localhost/actions/twoFA/get? What's happening? Also, my actionTrigger is set to actions and I have not set up any site routes in my plugin.

UPDATE: I have decided to make this work a little differently, and I therefore do not need any help.

In Craft 3 the csrf token validation in the general.php config is set to true by default. In order to request your controller with post requests you have to turn it off or include the token

https://docs.craftcms.com/v3/changes-in-craft-3.html#csrf-token-params

{% set csrfTokenName = craft.app.config.general.csrfTokenName %}
{% set csrfToken = craft.app.request.csrfToken %}
<input type="hidden" name="{{ csrfTokenName }}" value="{{ csrfToken }}">


getCriteria not working as expected with boolean field types I have a custom content table for an elementType. I have a few fields set up in my Record as boolean. The two fields are approved and denied I have to search the database where both fields are set to zero/false.

I can only get the values to show up in $query->params inside my modifyElementsQuery() method when I set $criteria->approved/denied to (string)"1". Setting these to true, false, or (string)"0" cause them to not show up in the $query->params list.

How do I work with a boolean field in this manner? I need Where denied = '0' to show up in the query when i set $criteria->denied = false

I believe this should work (not tested):

$criteria->setAttribute('denied', 'not 1');

As when using the ElementCriteriaModel in Twig, you can't directly check for "falsey" values.


How do I properly access plugin controllers from the front-end? I'm building a plugin, and it all worked just fine until I decided to change the front-end form submission to use AJAX instead of submitting directly. I have a form on the front-end with this action:

<form class="gtPoll__form" method="post" accept-charset="UTF-8" action="">
      <input type="hidden" name="action" value="gtPoll/poll/incrementAnswer">

and a controller at /craft/plugins/controllers/GtPoll_PollController.php with the method actionIncrementAnswer(). I also have the line

protected $allowAnonymous = array('actionIncrementAnswer');

in this controller class.

When I submit that form via AJAX on the front-end (to the URL in the action value), I get a 404 and the rest of my JavaScript breaks, but the controller action DOES complete and the answer count is incremented properly.

If I add this code to my main plugin file:

public function registerSiteRoutes()
    {
        return array(
            'gtPoll/poll/incrementAnswer' => array( 'action' => 'gtPoll/poll/incrementAnswer')
        );
    }

Then I don't get the 404 and my JavaScript all works, but the controller action occurs twice each time the AJAX call is made, so my answer count is doubly incremented.

I'm not really sure what's going on. I also tried using

{{ actionUrl('gtPoll/poll/incrementAnswer') }}

in the hidden action field, but that doesn't work at all.

What's the correct way to access this controller action from the front-end?

Thanks in advance!

Rather than submitting the form, try hitting it with the Craft JS object. Per the docs:

var data = {
    // ...
};

Craft.postActionRequest('cocktailRecipes/ingredients/saveIngredient', data, function(response) {
    // ...
});

https://craftcms.com/docs/plugins/controllers#posting-to-controller-actions-with-javaScript


Multiple craft.entries field parameters I have an for entry loop in which I need to look for two different field conditions. Separately they are:

craft.entries.eventStartDate('>=' ~ (now.date))

craft.entries.genericEventDate(':notempty:’)

How can I ask craft to look to see if either of these parameters is true?

Update

A work around is to pull the query without these paramaters and then run an if statement like:

{% if entry.eventStartDate >= now.date or entry.genericEventDate is not empty %}

This works but it's not really ideal since the query is pulling more entries than necessary.

The only possible core solution to query for these entries in Twig is to query for entry IDs first, process them with merge or without and then pass the IDs to the id parameter of a new ElementCriteriaModel.

Other than your solution implementing a conditional in the for loop this works with paginate and getNext tags, you only query for the entry models you're after, but the cost of this is the extra queries you need to get the IDs.

{% set futureEventsIds = craft.entries.eventStartDate('>=' ~ (now.date)).ids() %}
{% set specialEventsIds = craft.entries.genericEventDate(':notempty:').ids() %}
{% set idParam = futureEventsIds|merge(specialEventsIds) %}

{% set allEvents = craft.entries.id(idParam).order('GREATEST(eventStartDate, genericEventDate) desc') %}


How can I get cookies that were set in javascript from a twig template? The docs state that {{ craft.request.getCookie(name) }} will not work for cookies that were set in JavaScript, because all cookies in Craft go through some validation to ensure that they aren't tampered with.

This is understandable, but is there any way of getting non-Craft cookies with twig?

So far the only solution I have found is to use a plugin, and Lewis Jenkins' LJ Cookies plugin does the trick nicely:

{{ craft.lj_cookies.get( NAME ) }}

It also allows you to set and delete cookies within templates.


Replace throwing Unknown Modifier Error I've tested the regex and seems to work perfectly, but when placing it within Craft it seems to throw the following error:

  preg_replace(): Unknown modifier '/'

  {% set ig = user.instagram|raw|replace('/(?:(?:http|https):\/\/)?(?:www.)?(?:instagram.com|instagr.am)\/([A-Za-z0-9-_\.]+)/im') %}
  {{ ig }}

You'll need to double escape those backslashes in the regex.

  {% set ig = user.instagram|raw|replace('/(?:(?:http|https):\\/\\/)?(?:www.)?(?:instagram.com|instagr.am)\\/([A-Za-z0-9-_\\.]+)/im') %}


Check if current entry is in loop and add class I'm super new to both Craft and Twig so, apologies in advance!!

I'm looking to determine if the current entry I'm on is in a loop being called on that page.

I have loop that goes through the entries of a category and displays them. If I'm currently viewing an entry that also appears in the loop, I would like to add an "active" class to the matching entry in the loop.

I have been trying to possibly match the URL segment with an answer like the one found here but I need to the segment name to by dynamic.

Currently I have:

{% for entry in craft.entries.section('category_name').limit(4) %}

{{ entry.title }}

{% endfor %}

And would like it to be like:

 {% for entry in craft.entries.section('category_name').limit(4) %}
    IF ENTRY IN LOOP MATCHES CURRENT ENTRY PAGE I'M ON.. ADD CLASS

{{ entry.title }}

    ELSE

{{ entry.title }}

    END IF
{% endfor %}

Firstly, from the Craft docs on routing:

  If the URI matches an entry’s or category’s URI, the
  section’s/category group’s template will get loaded, and the matched
  element will be made available to the template via a pre-populated
  entry or category variable.

This means if the page you are viewing relates to an entry, Craft will have already created a variable called entry, which will contain the EntryModel for that entry, so it’s best to avoid re-assigning the entry variable in a template used for an entry to avoid confusion.

To address your question, you need to check if the current URL matches the URL of an entry in the loop, which you can do by checking against [craft.request.url][2] like this:

{% for catEntry in craft.entries.section('category_name').limit(4) %}
    {% if craft.request.url == catEntry.url %}

{{ catEntry.title }}

    {% else %}

{{ catEntry.title }}

    {% endif %}
{% endfor %}


Guest entries - send confirmation mail to person who filled out entry Inherited a site which uses guest entries for a job application form.

Postmaster plugin is being used to notify someone that a new entry has been posted but now they'd also like to send the person who applied for the job a confirmation mail after submitting the guest entries form... any suggestions on how to do this?

Assuming the person that applied has a text field they entered their email address on in the Guest Entry, then the plugin has an onSuccess event that gets fired when a guest entry is successfully saved.

You could listen to that from another plugin, grab their email address from the custom field, then build an email and programatically send it their way.


Best way to use custom enums in my plugin templates? I am building a plugin that's uses an enum for custom statuses. The statuses are integrated within my plugin with the exception of changing the status in my template code.

pluginname/enums/PluginStatus.php

<?php
namespace Craft;

class PluginNameStatus
{
    const Approved = 'approved';
    const Denied   = 'denied';
    const Pending  = 'pending';
}

What is the best method to grab the enum values and display those in a select field for my edit template? All of my templates are rendered via controller actions so I am thinking that assigning the enums to an array and passing to the templates as $variables might be the most practical method.

pluginname/controllers/PluginNameController.php

<?php
namespace Craft;

class PluginNameController extends BaseController
{
    public function actionShowIndex()
    {
        $variables['customStatuses'] = array(
            PluginStatus::Approved => 'Approved',
            PluginStatus::Denied   => 'Denied',
            PluginStatus::Pending  => 'Pending',
        ),
        $this->renderTemplate('pluginname/_edit', $variables);
    }
}

Right now that's probably the best way to do it.

For Craft 2.2, I just added a static getConstants() method to BaseEnum, so you can do can do a PluginStatus::getConstants() call that will return an array with a key => value pair of the enum options and their values that should make that cleaner.


Accessing Eager Loaded Transforms The craft docs on eager loading show you how to eager load image transforms in twig.

{% set entries = craft.entries({
    with: [
        ['coolImage', { withTransforms: ['transform'] }]
    ]
}) %}

However it doesn't show you how to actually USE them once they're eager loaded. Accessing the eager loaded element itself is easy:

{% set teaserImage = entry.coolImage[0] ?? null %}

But I'm not sure how to access the transform from there. Using getUrl('transform') appears to still result in an extra query per element loaded (compared with .url() and no transform).

{{ teaserImage.getUrl("transform") }} {# Boo - extra queries #}

What am I missing?

Thanks :)

It appears that the correct way to do it is using the url() method. I couldn't replicate the extra queries I mentioned in the question, and can confirm with some more testing that it doesn't cause any extra queries.

{{ teaserImage.url("transform") }}

or

{{ teaserImage.getUrl("transform") }}

Source: https://straightupcraft.com/articles/examples-of-eager-loading-elements-in-twig-and-php


Need a simple shopping cart for Stripe I have a Craft site I am building that will be selling about 20 templates with private password protected content (each a product basically). I need to allow people to "buy" access to each page. These purchases are one time and not a recurring subscription. Customers would have lifetime access to each product page content.

I have use plugins like Charge on other sites with Stripe. This works great for single product sites, but we want to allow people to buy access to more than one product. Is there any way to just build a simple cart with Craft to work with Stripe? Is there any cart code samples for Stripe? My needs are simple but I have been having a hard time finding an option without using a 3rd party solution like Foxy or Snipcart. Any ideas or suggestions.

It sounds like Sprout Commerce can handle your situation – multiple products, user-specific access, and Stripe.

At the time of this post, the product is still in private beta but if you'd like to drop us a note with more details (via the 'Request Preview' button on the site), we are happy to talk things through and see if your project would be a good fit.


Model values assignment issue I am setting attributes on my record using a models' attributes (that has validated successfully). I cannot figure out why the values aren't assigned. I can manually assign each one but that's redundant.

// Set attributes
// Massive assignment will only be made for fields which have passed some explicit validation rule.
if ($model->validate())
{
    $purchaseRecord->setAttributes($model->getAttributes());
    var_dump($model->snipInvoiceNumber); // works
    var_dump($purchaseRecord->snipInvoiceNumber); // null value
    $purchaseRecord->snipInvoiceNumber = $model->snipInvoiceNumber; // works
}

UPDATE:
Per comments on this thread I looped through and set the attributes individually. Anyone have insight on why setAttributes didn't work?
Save contact form data to database

This is what I used instead of setAttributes (plural).

foreach ($model->getAttributes() as $key => $value ) {
    $purchaseRecord->setAttribute($key, $value);
}

There is a second parameter  in your setAttributes() function. This parameter is true per default and checks if the attribute is safe so you either have to declare all safe attributes in your records rules or you set the parameter to false.


Craft 3: multisite and assets I am a little bit confused by the documentation about assets. It says that paths are relatives from the directory that holds the index.php file.

Now, I am working on multisite, with the following folders:
    craft/web/site1/
    craft/web/site2/

Is it possible to have:
    craft/assets/site1/
    craft/assets/site2/

Or:
    craft/web/site1/assets/
    craft/web/site2/assets/

Your second example is easy enough... just make sure the assets folder exists under both of the site1 and site2 folders, the under your Asset Volume settings in the Control Panel, set the "File System Path" to the relative assets.


404 – Not Found from _entry.twig templates in subdirectories Following the Minjingo CraftQuest tutorial, I am getting an error on the front end when I access any routes other than the root/home page.

In my template folder, I have a structure that looks like:

templates/
├── index.twig
├── about/
│   └── _entry.twig
├── drinks/
│   └── _entry.twig
└── news/
    └── _entry.twig

In my about section settings, my template is about/_entry, as it is in the tutorial.

The error I get is:

HTTP 404 – Not Found – yii\web\NotFoundHttpException
Template not found: about

Maybe I missed a step? What should I look for?

Templates or folder with an underscore are private and can't be accessed via template routing. Remove the _ and you are fine. For example news/entry instead of news/_entry


Craft 3 - login problem on mobile I have a site which is online now.

This site has a set password, log in, etc. features for my users. My users are able to sign in or set password (all user operations) on any browser but when they try to set the password or login (all user operations) on mobile, the process is never ended and finally crashed.

By default, Craft requires that a browser has a user agent string and IP address for a valid authenticated session, and once that session starts, the user agent string must remain the same for the remainder of the session.

It sounds like whatever browsers are being used on the mobile devices aren't providing this.

You can alter this behavior with these config settings:

https://docs.craftcms.com/v3/config/config-settings.html#requireuseragentandipforsession

https://docs.craftcms.com/v3/config/config-settings.html#requirematchinguseragentforsession


svgicons fieldtype - how to output in template? I've added a field called icons of type svg-icons and am able to select icons I've added without an issue. I am having trouble outputting them on the front end however. I get twig errors no matter what I try.

Have done {{ dump(entry.icons) }} and get the following output where you can see 11 lines from the bottom the path and name of the select svg icon.

How do I output that to the template?

D:\path\to\craft\app\vendor\twig\twig\lib\Twig\Extension\Debug.php:56:
object(Craft\SvgIconsModel)[700]
  protected 'classSuffix' => string 'Model' (length=5)
  protected 'strictAttributes' => boolean true
  private '_classHandle' (Craft\BaseModel) => null
  private '_attributeConfigs' (Craft\BaseModel) =>
    array (size=6)
      'icon' =>
        array (size=2)
          'default' => null
          'type' => string 'string' (length=6)
      'sprite' =>
        array (size=2)
          'default' => null
          'type' => string 'string' (length=6)
      'type' =>
        array (size=2)
          'default' => null
          'type' => string 'string' (length=6)
      'resource' =>
        array (size=2)
          'default' => null
          'type' => string 'string' (length=6)
      'width' =>
        array (size=9)
          'maxLength' => int 11
          'min' => float -2147483648
          'max' => float 2147483647
          'decimals' => int 0
          'default' => null
          'type' => string 'number' (length=6)
          'unsigned' => boolean false
          'length' => int 11
          'column' => string 'integer' (length=7)
      'height' =>
        array (size=9)
          'maxLength' => int 11
          'min' => float -2147483648
          'max' => float 2147483647
          'decimals' => int 0
          'default' => null
          'type' => string 'number' (length=6)
          'unsigned' => boolean false
          'length' => int 11
          'column' => string 'integer' (length=7)
  private '_attributes' (Craft\BaseModel) =>
    array (size=3)
      'icon' => string 'general\design.svg' (length=18)
      'width' => float 512
      'height' => float 512
  private '_extraAttributeNames' (Craft\BaseModel) => null
  private '_errors' (CModel) =>
    array (size=0)
      empty
  private '_validators' (CModel) => null
  private '_scenario' (CModel) => string '' (length=0)
  private '_e' (CComponent) => null
  private '_m' (CComponent) => null

Never used the plugin before, but from glancing at their docs, it looks like entry.icons.icon is what you're looking for.

Any other properties on their SvgIconsModel should work as well.


How to Template Grid for Entries that Switch from Horizontal to Vertical? How do you template to output across a grid that switches between vertical and horizontal posts?  I left the Craft/Trig off.

I know how to loop through and print entries using one orientation.  But how do I get it to change direction after every 2 and then switch back again, repeat...

{% if loop.index is divisible by(2) %}stuff{% else %}other-stuff{% endif %}


Trouble with registerCpRoutes() I'm trying to add some custom routes for my plugin, but it's throwing a 400 CHttpException for invalidActionParams when accessing largeupload/folders/2 as an example.

public function registerCpRoutes ()
{
return array(
    'largeupload'                           => array( 'action' => 'largeUpload/index' ),
    'largeupload/folders'                   => array( 'action' => 'largeUpload/folder/index' ),
    'largeupload/folders/new'               => array( 'action' => 'largeUpload/folder/edit' ),
    'largeupload/folders/(?P<folderId>\d+)' => array( 'action' => 'largeUpload/folder/edit' ),
);
}

My controller is called LargeUpload_Folder, and my action actionEdit().

public function actionEdit ($variables = [ ])
{
    /* Simplified */
    if ( isset($variables['folderId']) ) {
    }
    else {
    }

    $this->renderTemplate('largeupload/LargeUpload_Edit', [
    ]);
}

If I remove $variables = [ ] from the action parameters, it doesn't return an error anymore.

Any ideas?

I believe this is caused by not including array in the function parameter, preceding the variable.

Changing to public function actionEdit (array $variables = [ ]) should do the trick.

As a bonus - can anyone explain why? Seems like the $variables variable needs to be cast as an array?


Make dump() available when devMode is disabled Is there a way to make the dump() function available in my plugin templates when devMode is disabled?

Add this to your plugin/controller:

if (!craft()->config->get('devMode'))
{
    craft()->templates->getTwig()->addExtension(new \Twig_Extension_Debug());
}

Thanks to the all-knowing Brandon Kelly, who posted the solution on Slack.


How to get instance of DiscountModel in Commerce 2? In Commerce i got an instance of DiscountModel Class as follows:

$discount = new Commerce_DiscountModel();

Is this the right way in Commerce 2?

use craft\commerce\models\Discount as DiscountModel;
...
$discount = DiscountModel::instance();

$discount = new \craft\commerce\models\Discount();


Documentation on creating tasks I'm looking to integrate tasks into a plugin I'm building. Whilst I know that the Craft documentation shows the Tasks Service/Model properties/methods in the full class reference, I can't find anything online about best practices regarding using tasks in plugins. Has anyone come across any articles about creating your own tasks?

I'm not aware of any articles or best practice documents.

There is a sample/simple PowerNap plugin that shows the basics of creating and running a task as well as the TaskService class reference documentation which you've already found.


Loop divisible by issue Code:

{% for block in entry.testimonials %}

        {% switch block.type %}

        {% case "testimonials" %}

{{ block.personsName }}

          {{ block.quote }}

          {% for asset in block.image %}

          {% endfor %}

        {% endswitch %}

{% endfor %}

Can anyone point out why the...

loop.index0 is divisible by(2)

I have on the div around the image isn't working. I'm getting the 'last' class on everything. The one on the previous div is working fine.

What am I missing?

EDIT:

I've just realised it's in it's own image loop causing the problem. How can I get around this?

Forgive me if I'm wrong... by using loop.index0 is divisible by(2) are you trying to determine if the instance is odd or even?

1st - returns false
2nd - returns true
3rd - returns false
4th - returns true

If so Twig does support checking if the loop index is even or odd. Perhaps that would be better to use?

{% if loop.index is even %}Even{% endif %}

EDIT

I've just had a read of the Craft docs for the Case and Switch tags and found this:

  If you’re using the {% switch %} tag inside of a {% for %} loop, you won’t be able to access Twig’s loop variable directly inside of the {% switch %} tag. Instead, you can access it like so:

{% for matrixBlock in entry.matrixField %}
    {% set loopIndex = loop.index %}

    {% switch matrixBlock.type %}

        {% case "text" %}

            Loop #{{ loopIndex }}

    {% endswitch %}
{% endfor %}


Per item ie gift wrapped with additional cost in Craft Commerce? How can we do fields linked to products that the user can personalises when buying that product, ie writes in a custom gift message and picks an option of gif wrapped with additional cost.

Our client would have a few of these on each product some with costs and some free of charge.

We have looks at variants but seem we would have to make a new product/variant for each combination which would be a lot not variants, is there another way?

To add additional information about a product when adding to cart, you can use the options param to submit any data you wish along with the product you are adding to cart. You can see documentation on this here: https://craftcommerce.com/docs/add-to-cart#line-item-options-and-notes

As for modifying the price of the product based on those options; this is not possible by default in Craft Commerce, although we have provided a way that a plugin can achieve this.

There are 2 possible solutions:

1) Write a plugin that provides a Purchasable. A purchasable is a Craft Element, which your customers could build or edit on the front-end, and then add it to the cart. This is the more advanced path. You would need to know how to create a Craft Element, allow front end users to create and edit them, then make the element a purchasable. Documentation on Purchasables here: https://craftcommerce.com/docs/purchasables

2) Submit line item option data, but write a plugin that uses the onPopulateLineItem event to modify the price of the line item based on the option data submitted. It would look something like this:

craft()->on('commerce_lineItems.onPopulateLineItem', function($event){

    $purchasable= $event->params['purchasable'];
    $lineItem = $event->params['lineItem'];

    if(isset($lineItem->options['giftWrapped']) && $lineItem->options['giftWrapped'] == 'yes'){
        $lineItem->price = $purchasable->getPrice() + 10; // gift wrap is 10 dollars
        $lineItem->saleAmount = 0;
    }

});

The original price would still show in the product listing, but the option submitted when adding to cart would change the price of the line item when in the cart.

The 2 methods above allow you to have configurable prices of products.


Locked out of my Account, how to get back in? This really stressing me out!
When trying to log back in to Craft CMS today after som time my password wouldn´t work. I have not changed it and when trying to reset the password I´m only getting: "There was a problem sending the password reset email."
Is there anyway to find out through eg. my FTP and looking at the backend what the "user/password" is set to and what my reset email is set to?
I have not changed any of this, but since I´m locked out now something must have happened..

This is not actually my answer since it's from Andrew Welch on Twitter here: https://mobile.twitter.com/nystudio107/status/1063135843441954816

If you have access to MySQL, you can change the admin user password to secret using this hash:
$2y$13$FfVKvo1W1N5Gsba3c19r8uz.x9R5NRQgRu8jmXvOjVCStg1sKGcoW


How do I delete an asset with module or plugin I'm trying to clean-up the assets in a specific folder that aren't being used at all, i.e. aren't related to anything.

I got the list O.K. just using this

$assets = Asset::find()->volume('artists')->kind('image')->limit(50000)->ids();

$assetsToDelete = [];

foreach($assets as $asset) {
    $results = (new Query())
        ->select(['id'])
        ->from(['{{%relations}}'])
        ->where(['targetId' => $asset])
        ->orWhere(['sourceId' => $asset])
        ->all();

    if (!count($results)) {
        $assetsToDelete[]  = $asset;
    }
}

return  $this->asJson($assetsToDelete);

Right now I'm just returning all the ids in an array but I'm hoping to find a command to delete the element AND delete the stored asset on the server

Any ideas?

I'm not sure how well it'd go with 50,000 Assets, but you should just be able to call Craft::$app->elements->deleteElement($asset) for each one, or Craft::$app->elements->deleteElementById($asset->id), as seen in the Elements Class Reference.


Cache and Pagination not working I'm trying to get pagination and cache to work together but it's failing.

Here's my current code:

{% cache globally using key
   craft.request.path ~ '/p' ~ craft.request.getPageNum
   for 2 hours %}

{% paginate topicEntries.limit(10)as pageInfo, topicEntries %}

  {% for topic in topicEntries %}

      more code here

  {% endfor %}

{% endcache %

When I use this I get a template error that Variable pageInfo does not exist.

Then if I put the opening cache tag below the paginate tag. The first page shows 10 entries, but each page after that only displays 1 entry per page.

{% paginate topicEntries.limit(10)as pageInfo, topicEntries %}
 {% cache globally using key
    craft.request.path ~ '/p' ~ craft.request.getPageNum
    for 2 hours %}

  {% for topic in topicEntries %}

      more code here

  {% endfor %}

{% endcache %}

What I need to know is how to cache paginated entries so it's 10 per page and when a new entry is added the cache is cleared correctly.

Please advise.

Based on the code you posted on pastebin, it seems like the problem is that you have your pagination outside of the cache tags (assuming this was the case when you had caching implemented). And, I'm assuming that inside your {% include "_includes/pagination" %} you're trying to access pageInfo, which is created by the pagination tag.

To make this work, and be able to implement efficient caching, you need to include your pagination inside your cache tags. In fact, I would also include the logic that creates the topicEntries element criteria above where you have the pagination tag today, inside the cache tag, as it creates DB queries and is directly related to the content you want to cache.

The overall structure of your code should then be:

{% cache globally using key craft.request.path ~ '/p' ~ craft.request.getPageNum for 2 hours %}
    <ul class="feat-box-container">
        {#  lots of code to get topicEntries #}

        {% paginate topicEntries.limit(10) as pageInfo, topicEntries %}

        {% for topic in topicEntries %}
            {# code that outputs list items #}
        {% endfor %}
    </ul>

    {% include "_includes/pagination" %}
{% endcache %}


Is it possible to delete entries directly from the 'All entries' listing in the control panel? I have a couple of Channels with news entries imported from different RSS feeds. A lot of these entries have a similar topic, so it would be very nice if these entries could be deleted from the 'All entries' listing in the control panel. That way, entries from different sections can be compared by title, and easily deleted.

This is possible when using the listing of a single Channel, but not the 'All entries' listing. Is this still possible somehow.

Combining the entries in a single Channel is not an option unfortunately due to different fields and needs per RSS feed.

This is currently not possible natively in Craft, but you might want to consider voting for this feature request, which is very similar.

In the meantime, you could write a custom plugin that provided its own entry index view and allowed you to delete entries any way you wished.


Simultaneous Variants in Commerce I'm trying to setup a product that has multiple variant groups that need to be simultaneously selectable and that have their own up-charges.

An example use-case: Say I have a digital product that I'm selling that has multiple license options as follows:

Standard vs Pro
Commercial vs Non-Commercial
License levels based on Users

Each of these items would need to be individually selectable, with appropriate up-charges.

Any ideas on how to approach this?

Thanks!

As mentioned on Slack, the best approach to this currently is to set up all your variants and then use the plugin MultiAdd https://github.com/engram-design/MultiAdd to set up a form that can handle this.

Example code is in the ReadMe there.

It uses a single fast controller to update the cart all in one go, so there's no real speed penalty, and it raises events onMultiAddToCart etc. if needed too.


Render generic message/error template from plugin Basically, I'm trying to emulate show_error from EE/CI: http://ellislab.com/codeigniter/user-guide/general/errors.html

I want to just use the generic Craft error template (/craft/app/templates/error.html), like what happens if you have a DB connection error, and have it serve a 500.

This is from with a plugin controller.

What's the best way to do that?

I'm not familiar with EE's show_error, but a plugin's error template logic is identical to the logic that Craft uses to determine what to display.

From your plugin, if you:

throw new HttpException($statusCode, $optionalMessage)

Where $statusCode is 404 or 400, or any proper http status code, Craft will load the corresponding 404.html, 400.html, etc. from the craft/app/templates folder displaying the optional message, or the more generic message supplied in the template if no optional one is provided.

However, if you have supplied your own 404, 400, etc. from your own craft/templates folder, Craft will display that one instead of the one it provides so you can customize these errors for your site.

If you:

throw new Exception($optionalMessage)

while devMode is enabled, then Craft will load exception.html which will display the full error long with stack trace to help in debugging.  If devMode is not enabled, it will display the more generic error.html.

Craft specifically checks for database connection errors and will display the error.html template while setting a 500 status code.


Is there a hook or event we can use to intercept a search request? We are looking to log searches so we can report on them. Are there any hooks or events we can use to get this data?

If not, then how can a custom plugin log the initial search parameters, call the underlying core Craft search, and finally log the results?

We are looking to log searches so we can report on them. Are there any
  hooks or events we can use to get this data?

No, there aren't any hooks or events related to searching, currently (Craft 2.4). Like Brad says, you can add a feature request.

  [...] how can a custom plugin log the initial search parameters, call the
  underlying core Craft search, and finally log the results?

Something like this should work:

class MyPluginVariable
{

    public function search($query = '', ElementType $elementType = null)
    {

        MyPluginPlugin::log($query);

        $criteria = craft()->elements->getCriteria($elementType ?: ElementType::Entry);
        $criteria->search = $query;

        MyPluginPlugin::log(count($criteria));

        return $criteria;

    }

}

...and in the template:

{% set entries = craft.myPlugin.search('body:foo -body:bar') %}


How can I store Craft sessions in the database? Is it possible to configure Craft 3 to store session data in the database? On some other CMS's this can be enabled.

The reason I'm exploring this is because we're having an issue whereby PHP sessions get "locked up" due to a long-running request that forces all other PHP requests under the same session to be queued up until the prior ones complete (until the session files are written and released from their lock). This isn't ideal.

The problem is a common one amongst PHP apps that use file-based sessions. This article explains it in-depth:
https://ma.ttias.be/php-session-locking-prevent-sessions-blocking-in-requests/

I've seen this plugin which appears to support DB sessions in Craft 2, but haven't come across anything for v3.

To store sessions in the DB in Craft 3, you can merge the following into your config/app.php file:

return [
    'components' => [
        'session' => [
            'class' => yii\web\DbSession::class,
            'as session' => craft\behaviors\SessionBehavior::class
        ],
    ],
];

Then you need to manually create a session table in your database. Here's a mysql migration:

CREATE TABLE session
(
    id VARCHAR(255) NOT NULL PRIMARY KEY,
    uid VARCHAR(255) NOT NULL UNIQUE,
    expire INTEGER,
    data BLOB,
    dateCreated TIMESTAMP NOT NULL,
    dateUpdated TIMESTAMP NOT NULL
)

I wrote the Craft 2 DB sessions plugin referenced in the question. The Craft 3 approach is much nicer.


Return entries where field is "not empty" I'm trying to return entries where a specific field named "productSale" is not empty. Unfortunately, my code attempts keep throwing the "Expected name or number" error.

Code attempts include:

{% set products = craft.entries.section('product').productSale.(':notempty:').find() %}
{% set products = craft.entries.section('product').productSale.('not null').find() %}
{% set products = craft.entries.section('product').productSale.('is not null').find() %}
{% set products = craft.entries.section('product').productSale.('not empty').find() %}

Three questions then:

What is the "Expected name or number" error saying exactly?
Where in the available docs (Craft or Twig) would I look for explanation on this? I didn't find much in my first pass.
How do I return entries where a specific field is "not empty"

1. "Expected name or number" error

The parser is expecting a method/property after the dot of productSale, but you are passing arguments. Removing the dot to make it valid:

{% set products = craft.entries.section('product').productSale(':notempty:') %}

.

2. Documentation

There is no documentation on this yet, but it is listed in the release notes:

  Added support for passing “:empty:” and “:notempty:” to
  ElementCriteriaModel parameters when you’re looking for
  empty/non-empty values.


Use of duplicate slugs, without -1 Is there a way/workaround for using "duplicate" slugs?

Example:

shop.com/candy (category page with listing of candy's)
shop.com/about-us/candy (plain page with info about the production process etc)

Normally Craft will automatically generate for the second slug "candy-1".

Craft doesn't care what the individual slug of an entry is on its own, it looks at the whole path to see if there are duplicates.

Therefore having shop.com/candy and shop.com/about-us/candy shouldn't be a problem, provided the Entry URI Format has been setup correctly for your Sections.

It should take into account the slug of parent entries in generating their own. I use this:

{parent.uri ?? ''}/{slug}