Simple template routes with plugins I'm attempting to create a simple route that points to a template from a plugin. Note that I am attempting to create a page on the front-facing website, not the admin panel. Plugin (named: 'curious') is created with the following method:

craft/plugins/curious/CuriousPlugin.php

class CuriousPlugin extends BasePlugin
{

    ...

    public function registerSiteRoutes()
    {
        return array(
            'example' => 'curious/templates/example'
        );
    }
}

I created a template in craft/plugins/curious/templates/example.html, however, going to the URL /example just gives me a 404. If I visit /admin/curious/example it does exist and works. However, I don't want the page to be in the admin, I would like it to be visible on the front-facing website.

Any thoughts?

This is a tricky topic for a simple answer. Off the top of my head, there are two general approaches you may fall in for this. I'll keep my answer fairly broad:

URL-Enabled Elements

You can create an Element Type and implement the method routeRequestForMatchedElement. This approach would require you to have a Custom Element, and have a need for that Element to be URL-enabled like Entries or Categories or Products.

For this approach you would not need to setup registerSiteRoutes .The Element API would handle the front-end request in the same way other URL-enabled Element requests are handled.

Your Plugin has a template in the plugins template folder that you want to render on the front-end of the website

The main concept to understand here is that Craft has two Template Modes: Site and CP. Depending on which template mode is active, Craft will look in different places for the template to load.

To set the template mode in your code, you can use the following:

// Tell craft to look in the front end craft/templates folder for a template
craft()->templates->setTemplateMode(TemplateMode::Site);

// Tell craft to look in the back end craft/app/templates folder for a template (where you can later update the path to target your plugin template folder)
craft()->templates->setTemplateMode(TemplateMode::CP);

Once you are on the right side of the fence, you can then render the template you want and return it to the page. You would do this in one of two places:

Controllers If your logic is taking place in a controller you will need to render the template and return the rendered template to the page as a variable. Two scenarios in controllers where this may occur:

Responding to a page with errors:

$output = craft()->curiousService->getTemplate()

craft()->urlManager->setRouteVariables(array(
  'stuff' => $output
));

Rendering a template as the final step in the controller action (this may apply to CP scenarios more than front-end scenarios).

$this->renderTemplate('curious/example', array(
    'stuff' => $stuff,
));

Variables If your logic is taking place in the middle of the page, you may need to create a custom variable that renders your template and outputs it when the variable is called:

{{ craft.myCuriousPlugin.getMyTemplate() }}

Be sure to Flip/Flop

In either case, since you are flip/flopping on which part of the site is rendering your templates, you'll want to be sure to set your Template Mode back to the Site side of things once you are done:

// Front end page calls your plugin

// Switch things to look for templates on the back end
craft()->templates->setTemplateMode(TemplateMode::CP);

// Do your thing
//
// Note the template path is just the name of the plugin
// and the folder in the plugins template folder that you
// want to access, and that we are omitting the word 'templates'
$html = craft()->templates->render('curious/example', array(
  'someVariable'      => $yourResults,
  'someOtherVariable' => $someOtherStuff
));

// Switch things back to look for templates on the front-end
// so that any template-related things that after you behave
// as normal
craft()->templates->setTemplateMode(TemplateMode::Site);


Group entries by section, then order and combine sections I'm grouping search results by section, based on Aaron Berkowitz's answer to this question. My code looks like this:

{% set entries = craft.entries({search: {query: keywords} }) %}
{% set entriesBySection = entries | group('section') %}

{% for section, entries in entriesBySection %}

{{ section }}

        {% for entry in entries %}

{{ entry.title }}

        {% endfor %}

{% endfor %}

That works great but I need to show the sections in a specific order, and merge some sections into one. So I'd end up with, for example:

News
Products
Reports
Other (everything else combined)

Any idea how to do this?

Thanks loads for any help!

You could do this in a few steps (warning: this is untested code):

1) Define an array of the sections you cited, in the correct order:

{# Define a list of the sections you want to display in the correct order #}
{% set sections = ['news', 'products', 'reports'] %}

2) Identify the entries that do not belong in any of these sections:

{# Create an array of entries that do not belong to any of the sections above #}
{% set otherEntries = [] %}

{# We first loop on the entries by section to identify the "other" entries #}
{% for section, entries in entriesBySection %}
    {% if section in sections %}
        {% for entry in entries %}
            {% set otherEntries = otherEntries|merge([entry])}
        {% endfor %}
    {% endif %}
{% endfor %}

3) Display all the entries of each section in the correct order

{# Loop on the sections in the right order #}
{% for section in sections %}
    {# Obtain all the entries for the current section #}
    {% set entries = entriesBySection[section] %}
    {% for entry in entries %}
        ...
    {% endfor %}
{% endfor %}

4) Display the rest of the entries

{# Finally display the "other" entries #}
{% for entry in otherEntries %}
    ...
{% endfor %}


Display only one entry with checkbox checked in my pagination loop? I have multiple entries in 'blog' that have the 'featurePost' checkbox field checked. I want to display ONLY the latest, but this code is displaying multiple fields.

{% paginate craft.entries({ section: 'blog', order: 'featurePost desc, postDate desc', limit: 5 }) as entriesOnPage %}

{% for entry in entriesOnPage %}
    {% if craft.entries.section('blog').featurePost.limit(1) | length %}

    {% else %}

    {% endif %}
{% endfor %}

{% include "partials/blog/_blog-pagination" %}

{% endpaginate %}

This should show all entries, paginated, with featured posts at the start, but not pinned to the top of each page.

{% paginate craft.entries({ section: 'blog', order: 'featurePost desc, postDate desc', limit: 5 }) as entriesOnPage %}

    {% for entry in entriesOnPage %}
        <article class="flex-container-small{% if entry.featurePost %} pinned{% endif %}">
    {% endfor %}

    {% include "partials/blog/_blog-pagination" %}

{% endpaginate %}

Ordering by featurePost is good, but you can perform the check a lot more simply.


entries related To category not working with getNext function I currently have one entry in a page and would like to display next and previous arrows. The next/previous entries should be in the same section and related to the same category. To do this I have written this code:

{% set category = craft.categories.group('seasons').title(entry.season.title).first() %}
{% set arrowsParams = craft.entries.section('blog').relatedTo(category) %}
{% set nextEntry = entry.getNext(arrowParams) %}
{% set prevEntry = entry.getPrev(arrowParams) %}

For each entry I have added a field 'season' which maps to a category.

When retrieving the nextEntry, the relatedTo category is being neglected and an entry in a different category is being returned.

When I do

craft.entries.section('blog').relatedTo(category).first()

it works fine and actually retrieves the first entry in that category.

Am  I missing something? Could this be done?

The code you currently have (after fixing the arrowsParams typo) will only work on entries that happen to have the very first category in the Seasons category group selected, because this title parameter is actually not getting any value right now:

.title(entry.season.title)

Why? Because entry.season is not going to return the first category you have selected in your Season categories field; it’s going to return an ElementCriteriaModel that is prepped to return the category(s) selected in your Season categories field, once one of its output functions have been called.

Since entry.season is an ElementCriteriaModel, entry.season.title is actually just going to return its current title parameter value (which is null by default). So this:

{% set category = craft.categories.group('seasons').title(entry.season.title).first() %}

is really the same as this:

{% set category = craft.categories.group('seasons').title(null).first() %}

which is the same as not including the parameter at all, so it’s also the same as this:

{% set category = craft.categories.group('seasons').first() %}

So category is always going to be set to the first category in the group, and arrowsParams is going to be set to all of the Blog entries that are related to that first category.

If the entry you’re viewing doesn’t have that first category selected, then its getNext() and getPrev() functions won’t return anything. (If they can’t determine where the current entry fits into the parameters that were passed in, they can’t determine what the next and previous entries would be.)

Anyway, to fix this, you would need to put a .first() call inside that title param when setting the current category:

{% set category = craft.categories.group('seasons').title(entry.season.first().title).first() %}

But here’s the thing: entry.season.first() is already the category you’re trying to get. So there’s no point in fetching it through a second craft.categories query in the first place. This will do just fine:

{% set category = entry.season.first() %}


Craft CMS and JS/MVVM frameworks Does anyone have an experience in using Craft in conjunction with a front-end JS MVC/MVVM framework such as Backbone, Ember or Angular?

We have a project coming up where there may be a lot of JS state management requirement and AJAX calls required, and would like to access back-end data using JSON. However, there is also a strong need for a robust easy-to-use CMS as there will be a lot of content to administer.

My first port of call for this type of application would be some more general server-side framework such as Laravel, but Craft has sort of spoiled us with the content management side of things.

Is there any way of marrying Craft with this kind of project?

I am just starting out on a project using both Craft and Angular. I ran into an early issue because both Angular and Twig use the {{ }} braces, but that was quickly fixed by changing Angular’s settings like in this Stackover flow post.

As Christian noted in the comments, though, you could also use the {% verbatim %} tag to keep Craft from parsing Angular tags.

Aside from that, I haven't had issues since Craft gives you full frontend control and there are no CMS scripts on the frontend to worry about. And (so far) Craft's dynamic routes make it really quite easy to set up your templates, JSON, etc.


Relationship between channels/entries I need some help. Really struggle understanding how to build relationships between channels/entries.

Context/Want:
I have two separate channels:
1. "Plant catalogue"
2. "Plant care"
I want to re-use the entries from "Plant catalogue" across the whole website wherever I need it accesing speicifc fields.

What I have:
"Plant care" has an entries field type to help build the relationship with "Plant catalogue": field name: "plantEntity"
"Plant catalogue" has an asset field that include a photo of the plant. fiel name: "plantPhoto"

My code so far:

{% set plantcareListing = craft.entries().section('plantCare').limit(null)%}
{% set entries = plantcareListing.all() %}

{% for entry in entries %}
  {% for image in entry.plantEntity %} {# how do I access the asset field "plantPhoto" here? :( #}
    <img src="{{ image.getUrl() }}" alt="{{ image.title }}">
  {% endfor %}

  {{ entry.title }}
{% endfor %}

I would appreciate your advice. Thank you!

It’s important to understand that using relations fields in the template works similar to using craft.entries. They return a element query object, for example entry.plantEntity in your entries loop. And you need to call the all() method on them to query for the elements. The same applies to querying for the asset elements in your inner entries loop.

{% set plantCares = craft.entries
    .section('plantCare')
    .all() %}

{% for plantCare in plantCares %}
    {% for plantEntity in plantCare.plantEntity.all() %}
        {% for plantPhoto in plantEntity.plantPhoto.all() %}

        {% endfor %}

        {{ plantEntity.title }}
    {% endfor %}
{% endfor %}

You should generally avoid querying in a loop though, and use eager-loading instead.

{% set plantCares = craft.entries
    .section('plantCare')
    .with([
        'plantEntity',
        'plantEntity.plantPhoto'
    ])
    .all() %}

{% for plantCare in plantCares %}
    {% for plantEntity in plantCare.plantEntity %}
        {% for plantPhoto in plantEntity.plantPhoto %}

        {% endfor %}

        {{ plantEntity.title }}
    {% endfor %}
{% endfor %}

If you’ve got many plantCare entries consider paginating through them.


Problem with Twig group function, group by year I am trying to apply the group filter so that my year headings appear in DESC order. They are appearing in ASC order instead, and applying |reverse doesn't work. Suggestions?

{% set entries = craft.entries.section('blat')
.orderBy('postDate DESC')
.limit(null)
.with ( ['thumbnail'] )
%}
{% set entriesByYear = entries|group('postDate.year') %}
{% for year, entriesInYear in entriesByYear %}

    <h2>{{ year }}</h2>

  {% for entry in entriesInYear %}

{% endfor %}

You don't mention where you tried putting the reverse filter, but perhaps you put it in the wrong place? It should go here:

{% set entriesByYear = entries|reverse|group('postDate.year') %}

I've tested this and it works ok for me.


mkdir(): Permission denied I have an old craft 2 installation.
When I want to go to the frontend I get:

mkdir(): Permission denied

745     {
746         if ($permissions == null)
747         {
748             $permissions = craft()->config->get('defaultFolderPermissions');
749         }
750
751         $path = static::normalizePathSeparators($path);
752
753         if (!static::folderExists($path, false, $suppressErrors))
754         {
755             $oldumask = $suppressErrors ? @umask(0) : umask(0);
756
757             if ($suppressErrors ? !@mkdir($path, $permissions, true) : !mkdir($path, $permissions, true))
758             {
759                 Craft::log('Tried to create a folder at '.$path.', but could not.', LogLevel::Error);
760                 return false;
761             }
762
763             // Because setting permission with mkdir is a crapshoot.
764             $suppressErrors ? @chmod($path, $permissions) : chmod($path, $permissions);
765             $suppressErrors ? @umask($oldumask) : umask($oldumask);
766             return new Folder($path);
767         }
768
769         Craft::log('Tried to create a folder at '.$path.', but the folder already exists.', LogLevel::Warning);

Where can I read out the folder which has no permission?
I've set recursive chmod 777 on all files in the project (yes I know).
But this doesn't change anything.
It is on a local MAMP Pro install. Nothing is showing in any logs.
But the Control Panel is working fine.
I also tried to change the user in mamp. As suggested on discord. But that does nothing.
♂

It's been a while since I was in Craft 2, but I'd suggest checking out this page in the docs:
https://craftcms.com/docs/2.x/folder-structure.html
Do all of the directories mentioned on that page exist on your site? Specifically, I'd confirm that you have the craft/storage folder created in your installation. If it's not there, make it and try to load the page again.


How can I get Craft Commerce talk to the API of a CRM? A client wants his CRM connected to Craft Commerce. If someone finishes an order the customer data and the ordered products should be send to the CRM.
I assume I have to write a plugin for that, but I have no idea how to start and how the grab the desired data. Can anyone point me to the right direction? The CRM used is https://www.bitrix24.com/ if that's of importance.

You will need to write a plugin to perform the integration. If you make it generic enough, you could share it with the community.

Your plugin would use Craft Commerce events to run PHP code at specific points in the code, which would communicate with the third-party systems.

Docs on event:
https://craftcommerce.com/docs/events-reference

The event you would probably be most interested in is the onOrderComplete event:

https://craftcommerce.com/docs/events-reference#commerce_orders.onordercomplete


Categories field limit doesn't disable Add a Category button I set the Branch Limit for a Category Field Type to 1, but after adding one category, the Add a Category button is still active.

Shouldn't it be disabled after reaching the limit?

Thanks!

The button is still there and enabled but if you try to add a second category, you'll find that it just replaces the initial one.


How to encode links in email templates? We have a problem related to the escaping of the & in the password reset URL not being escaped. This link:

http://yoursite/admin/actions/users/setpassword?code=abcd&id=1234

somehow, this arrives at our server as:

http://yoursite/admin/actions/users/setpassword?code=abcd&amp;id=1234

Then the code is abcd and a parameter called amp;id is 1234, and the error "Bad Request Param "id" doesn't exist." is thrown.

In our case, we have craft setup to send HTML emails, and have an email template like:

Click here to reset your password: {{link}}

and end up with the link http://yoursite/admin/actions/users/setpassword?code=abcd&id=1234 directly in the HTML - this is WRONG, since the & symbol has to be escaped as &amp;.

Now, we could compensate, and make our template:

Click here to reset: {{link|escape}}

Then we get correct HTML, but the plain-text version of the email is now broken.

Is there no way to have separate templates for text and html emails in craft?

How do other people solve this?

Is there no way to have separate templates for text and html emails in craft?

Correct.  That's something we're going to try to address in Craft 3.

Regarding the ampersand encoding, I can't reproduce that locally.  Maybe your email client is encoding it or there is an overly aggressive mod_security / Suhosin / Apache setting that's doing it?


Is it possible to use Twig in a Rich Text Field? I have global variables that I want to include in Rich Text Fields, for example:

This is a rich text field, call us today at at {{ phone.number }}!

This doesn't work out-of-the-box, are there any ways of doing this? I am coming from working in another CMS where this was possible to do.

Update: Specifically in this case I the global variable includes a javascript code that dynamically updates the phone number with a tracking number:

<script type="text/javascript">{ document.write(_ibp_phone_number); }</script>

The Technical Way

You can do this using Twig's template_from_string function and the raw filter.  In your template code you would output your Rich Text field:

{{ include(template_from_string(entry.richTextFieldHandle)|raw) }}

And within your Rich Text field you could reference your global with the same syntax you normally would in Twig as you described above:

This is a rich text field, call us today at {{ phone.number }}!

A More User-Friendly Way

The Rich Variables Plugin allows you to give a user control over adding Global Variables to your Rich Text field.

This approach will be a bit more limited in what you can accomplish, but probably toward a more user-friendly outcome for your content managers.


When using Active Record with a View (instead of Table) the primary key is not set in the record after save I ran into a problem when saving a new record.  I have created a test below.  When saving a record using a db table, the record is saved to the db and the primary key id is updated in the record object.  Works as expected.

But if the record is saved to a db view (instead of a table), the record is saved to the db but the primary key id is not updated in the record object.  Not working as expected.

I have a third party table that I am using in a plugin that I am developing (in this example, I am calling this third party table craft_test).  I can't changed the name of the table but have created a view name with "craft_" in order to use all of crafts features.

In my example, I have a custom primary key (Member_ID) that I am using.  The issue still exists if I use the the default primary key of id (by changing the Member_ID column to id in the db, removing the primaryKey() method in the Record declaration, removing Member_ID from defineAttributes in the Record, and rennaming  Member_ID to id in the model defineAttributes).

Does anyone have an idea why the view is not working?  Is there a bug in the craft code?  Or is there a setting that I need to modify?

Accd_TestModel.php

namespace Craft;

class Accd_TestModel extends BaseModel
{

public function defineAttributes()
{
    return array(
        'Member_ID'    => array(AttributeType::Number),
        'FirstName' => array(AttributeType::String, 'maxLength' => 40, 'required' => true),
    );
}
}

Accd_testRecord.php

class Accd_TestRecord extends BaseRecord
{
/**
 * Gets the database table name
 *
 * @return string
 */
public function getTableName()
{
   // REMOVE COMMENT BELOW USE VIEW INSTEAD OF TABLE
   // return 'test_view';
   return 'test';
}

public function primaryKey()
{
   return 'Member_ID';
}

/**
 * Define columns for our database table
 *
 * @return array
 */
public function defineAttributes()
{
    return array(
        'Member_ID'    => array(AttributeType::Number, 'column' => ColumnType::PK),
        'FirstName' => array(AttributeType::String, 'maxLength' => 40, 'required' => true),
    );
}

}

Code:

    $test_model = new Accd_TestModel();
    $test_model->setAttribute('FirstName', 'Bill');

    $test_record = new Accd_TestRecord();
    $test_record->setAttributes($test_model->getAttributes());
    if ($test_record->save())
    {
        // update Member_ID on model (for new record)
        $test_model->setAttribute('Member_ID', $test_record->Member_ID);

        echo '
Model FirstName: '.$test_model->getAttribute('FirstName');
        echo '
Model Member_ID: '.$test_model->getAttribute('Member_ID');
        echo '
Record FirstName: '.$test_record->getAttribute('FirstName');
        echo '
Record Member_ID: '.$test_record->getAttribute('Member_ID');

    }
    else
    {
        echo 'failure';
    }

Database:

CREATE TABLE IF NOT EXISTS `craft_test` (
`Member_ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`FirstName` varchar(40) NOT NULL DEFAULT '',
`dateCreated` datetime NOT NULL,
`dateUpdated` datetime NOT NULL,
`uid` char(36) NOT NULL DEFAULT '0',
PRIMARY KEY (`Member_ID`)
 ) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

Database View:

  CREATE VIEW craft_test_view AS SELECT * FROM craft_test

Output to screen when using view:

Model FirstName: Bill
Model Member_ID:
Record FirstName: Bill
Record Member_ID:

Output to screen when using table:

Model FirstName: Bill
Model Member_ID: 7
Record FirstName: Bill
Record Member_ID: 7

This should do what you're looking for:

if ($test_record->save())
{
    // update Member_ID on model (for new record)
    $test_model->setAttribute('Member_ID', craft()->db->getLastInsertId());

    ...
}


How to apply a filter on a title with several concatenated elements? Here's an example:

{% set title = entry.artistFirstName | capitalize ~ ' ' ~ entry.title | capitalize %}

It outputs Firstname title instead of Firstname Title whereas filters are applied on the two concatenated elements. Why and how to add this filter on each of these elements?

I've tested these three methods (the first one from AbbeyDesign's answer,) and they seem to work fine with variables, I can't see any obvious reason why they wouldn't:

{% set entry = { artistFirstName: 'hello', title: 'world' } %}

{% set title = (entry.artistFirstName|capitalize) ~ ' ' ~ (entry.title|capitalize) %}
{% set title = "#{entry.artistFirstName|capitalize} #{entry.title|capitalize}" %}
{% set title = [entry.artistFirstName|capitalize, entry.title|capitalize]|join(' ') %}

Results in "Hello World" in each case.


Front-end entry form not updating custom fields in existing entry I have an entry form (in a local v3.5.13.1 dev context) configured to update existing entries in a particular section. On load, it’s reflecting existing data (input via the Craft control panel) perfectly well, including Assets and Matrix field content.
But when I'm logged in as a Super Admin and I submit changes, it will receive updates to the entry Title field but not to any custom fields.
No errors are being reported and it redirects to the desired page.
At present, I have stripped it right back to the Title field and a single Plain Text custom field but no joy. Here is my form code:
{% set entry = entry ?? create('craft\\elements\\Entry') %}
{% set redirectUrl = "partners/#{entry.id}" %}
{% set sectionId = entry.sectionId %}

<form method="post" accept-charset="UTF-8">
    {{ csrfInput() }}
    {{ actionInput('entries/save-entry') }}
    {{ redirectInput(redirectUrl) }}
    {{ hiddenInput('sectionId', '5') }}
    {{ hiddenInput('enabled', '1') }}
    {{ hiddenInput('entryId', entry.id) }}
    {{ hiddenInput('entryVariable', 'badEntry'|hash) }}

    {% macro errorList(errors) %}
        {% if errors %}

            {% for error in errors %}

{{ error }}

            {% endfor %}

        {% endif %}
    {% endmacro %}

            Organisation name
            {{ input('text', 'title', entry.title, {
                required: true,
                class: entry.hasErrors('title') ? 'text error' : 'text'
            }) }}
            {{ _self.errorList(entry.getErrors('title')) }}

                Headline:
                Optional, 10 words or fewer recommended

            {{ input('text', 'headline', entry.headline, {
                required: true,
                class: entry.hasErrors('headline') ? 'text error' : 'text'
            }) }}
            {{ _self.errorList(entry.getErrors('headline')) }}

        <input type="submit" class="btn" value="Update page">

</form>

No updates to the Headline field get saved. Updates to the Title field do. Where can I be going wrong?

You need to pass your custom fields in a fields array, try:
{{ input('text', 'fields[headline]', entry.headline, {
    required: true,
    class: entry.hasErrors('headline') ? 'text error' : 'text'
}) }}

See example here - https://craftcms.com/knowledge-base/entry-form


How to persist locale in site navigation? I've got a bi-lingual site running nicely, /en/news/news-item displays the English version and /cy/news/news-item displays the Welsh version. Great!

I can't figure out how to get the URLs for the main site navigation to respect the currently used locale though. For example;

If I'm on the page: http://craft.dev/cy/news/news-title the main site navigation should list 'news' and 'artist' with a /cy preceding the URL (/cy/news and /cy/artists). Instead it's spitting out /en/news and /en/artists.

The code I'm using to get the URLs for the navigation is:

News

Artists

What am I doing wrong?

Did you configure siteUrl in general.php, so Craft knows what the base URL is for your entries?

return array(
    'siteUrl' => array(
        'en' => 'http://craft.dev/en/',
        'cy' => 'http://craft.dev/cy/',
    ),
);


Switching plugin license from Craft 2 to Craft 3 Dumb question, but...

I purchased a plugin when using Craft 2 and now I'm trying to input the license for it in Craft 3. There's an input for the license key when I go to settings -> plugins, but the submit for it says "buy now" and tries to put the plugin in my cart. How do I give Craft 3 the license?

That's a question for the plugin developer. If the plugin isn't purchased through the plugin store, then each developer is responsible for generating Craft 3 licenses on their own.

It's probably safe to say that your Craft 2 license number is not going to match your Craft 3 license number. Contact the plugin's developer for more information.


AJAX user registration and JSON returned I am using business logic plugin to tap into save user event and create some profile field values.

public function init(){
    parent::init();

    craft()->on('users.saveUser', function(Event $event) {

    }
}

On save user returns a success object.
I want to return more user profile data to the page on success.

In the save User function I have tried:

 if (craft()->request->isAjaxRequest())
 {
     $return = array(
         'success' => true,
         'errorCode' => 1,
         'uid' => $user->id,
         'urn' => $URN
     );

     return json_encode($return);
 }

but I still get the usual response of success and user id.
How do i get a thank you 'page' into the process so I can feedback to the user?

Instead of listening to the onSaveUser event, I'd update my user save form on the front-end to point to a controller's action in my plugin.

You can run similar logic to what Craft's UsersController->actionSaveUser() is doing and return any custom JSON encoded data you want back to your template.


Failed Stale Template Cache Delete Error...Analyzed...But No Answer For some of the extremely static content on my site, I use caching to prevent multiple db queries during every single load. I also have multiple locale on my site with custom cache key names. The actual execution of cache is running perfectly at the moment on all the locales.

However, when I make changes to any entry which has cache enabled, I get stale template delete failed error. When I take look at the actual MySQL table, I see that the requisite cache row is deleted from the table upon making changes to an entry. So I don't quite understand why this error shows up.

Here is a typical cache directive I used in my templates

 {% cache globally using key  "www-features" for 24 hours %}

When I look at the runtime logs, I see following error which I am assuming it to be associated with this problem

  2018/11/03 19:10:42 [error] [application] Encountered an error running task 17 (DeleteStaleTemplateCaches), step 13 of 29: An exception was thrown:
  CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'dateCreated' in order clause is ambiguous.
  The SQL statement executed was:
  SELECT elements.id, entries.sectionId, entries.typeId, entries.authorId, entries.postDate, entries.expiryDate
  FROM craft_elements elements
  JOIN craft_elements_i18n elements_i18n ON elements_i18n.elementId = elements.id
  JOIN craft_content content ON content.elementId = elements.id
  JOIN craft_entries entries ON entries.id = elements.id
  JOIN craft_sections sections ON sections.id = entries.sectionId
  LEFT JOIN craft_structures structures ON structures.id = sections.structureId
  LEFT JOIN craft_structureelements structureelements ON (structureelements.structureId = structures.id) AND (structureelements.elementId = entries.id)
  WHERE ((((elements_i18n.locale = :locale) AND (content.locale = :locale)) AND (elements.archived = 0)) AND (elements_i18n.enabled = 1)) AND (sections.handle IN ('policies'))
  GROUP BY elements.id
  ORDER BY dateCreated. Bound with :locale='en_us'

Looks like you have dateCreated field in multiple tables used in the join and the statement is not explicitly defining the identity of the table ???

Can anyone help me to fix this?

OK, I found the answer to this. Here is the correct answer.

Basically change dateCreated to elements.dateCreated in my template.


Reference a field name relatedTo(category) I'm building a site for a property developer that has multiple housing developments. And on each development page there is a siteplan image that shows a CGI of the all the houses on the development.

I'm trying to implement the Donkeytail plugin https://straightupcraft.com/craft-plugins/donkeytail-lite which lets you add pinpoints on images and have made some progress and have it working. However, every development page needs it's own siteplan, and so I have to create a field for each one.

So my question is how do I reference it in the template dynamically? As currently it is being called via variable.fieldname.topLeftStyles

But this only references the exact fieldname of one siteplan. So I need a dynamic way so that it can be different on each development page.

{% set sitemap = craft.entries.section('sitePlan') %}
{% for variable in sitemap.relatedTo(category) %}
    {{ variable.title }}
{% endfor %}

Not sure I totally get your problem, but you can use bracket style notation to use variables for field handles, i.e.

{{ variable[someVariableHandle].topLeftStyles }}

or even something like

{{ variable[someVariableHandle][someVariableProperty] }}

As for actually outputting the fields in a sane manner, you'll need to map the variable field handles to your entries in some way – one way to do it is to simply use a naming convention for the field handles where you use some unique property on the entries (e.g. the entry ID) as part of the handle:

...
{% set fieldHandle = 'fieldname-'~entry.id' %}
{% if variable[fieldHandle] is defined %}
    {{ variable[fieldHandle].topLeftStyles }}
{% endif %}
...

Another way is to manually set up a map in your template – here's an example using entry IDs for keys, and the field handles for values:

{% set fieldHandles = {
    35: 'someFieldHandle',
    464: 'anotherFieldHandle'
} %}
...
{% for variable in sitemap.relatedTo(category) %}
    {% set fieldHandle = fieldHandles[variable.id] ?? null %}
    {% if fieldHandle and variable[fieldHandle] is defined %}
        {{ variable.title }}
    {% endif %}
{% endfor %}


Asset transform messing with color I have a client with very stringent concerns over acurate color in thier images.

They have noticed that image resized from Craft have some dramatic color discrepancies, which I have verified. It's nothing you'd probably notice in a regular photo, but these are actual fabric swatch images, so the color is crucial.

Anyway - first thing I verified was that Imagick was being used, which it is.

However, when I look at the file info, it still says something about gd-jpeg which concerning...not sure if that is just a red herring though.

❯ file resized-720.jpeg
resized-720.jpeg: JPEG image data, JFIF standard 1.01, comment: "CREATOR: gd-jpeg v1.0 (using IJ"

Is this more appropriately posted as an issue for https://github.com/avalanche123/Imagine?

Somehow, the images were created thinking Imagick wasn't installed. Clearing the cache forced the images to be recreated and seems to have fixed it.


Redactor ImagePosition - no custom styles I have ImagePosition within Redactor set up, but ideally, I'd like custom settings rather than the default "none, left, right, center"

Redactor file reads:

{
  "buttons": ["html","formatting", "bold", "italic", "unorderedlist",
  "orderedlist", "link", "image", "video"],
  "plugins": ["fullscreen", "video"],
  "toolbarFixed": true,
  "imagePosition": {
    "left": "image-left",
    "right": "image-right"
  }
}

The above activates the ImagePosition within the Edit screen of the image but no custom styles. Cleared CP cache, no luck. Thanks.

I was being a dummy. It works and does what it's meant to.

I just thought you could make your own drop parameters and classes.

You can only change the classes added, the "left, center, right" are not editable.


Why can't we log items regardless of dev mode? Unfortunatley we need to develop an Import plug-in helper with dev mode OFF, for - according to Brad at P&T:

  devMode uses a lot more memory on a given PHP request and importing
  probably already requires significant memory, so my guess is that
  you're running into your php.ini file's memory_limit or
  max_execution_time limits or something similar.

The Import plug-in is definitely a resource pig. We can't get it to work at all in dev mode. But we need to develop a plug-in using its hooks to help with things it can't handle in our situation (tables, dates, switches) so we're in a tough spot.

According to this article:

  If you want a log item to appear regardless of whether devMode is
  enabled, you can pass true as the third argument to the log() method.

SproutActivePlugin::log('Log this with or without devMode enabled.', LogLevel::Info, true);

Is this accurate?

We've tried it. Doesn't seem to work as advertised. Nothing in the logs unless dev mode is turned on. And when it's turned on the Import plug-in hangs up. We're in a tough spot.

What else should we be looking for? What else can we try?

Craft CMS 2.6.2930 / Import 0.8.28 / PHP 5.6.10

EDIT:
We have increased memory_limit 10 fold (1280M)
We have increased max_execution_time 100 fold (30000)
We have increased max_input_time 100 fold (60000)

If you want a log item to appear regardless of whether devMode is enabled, you can pass true as the third argument to the log() method.
Is this accurate?

Yes. Logs will only get flushed to disk is a proper craft()->end() occurs, firing the onEndRequest event.  So if you're doing something in your plugin like calling PHP's exit() directly or there is some other logic that inadvertently kills the request, then they won't get written to the log file.
I'd suspect that's what you're running into.


Can you remove the `translatable` option to a matrix field without losing locale content? One of our admins have mistakenly added the translatable option to an important matrix field. We were using this option to synchronise content blocks across multiple languages.

This is the our main “Block Matrix Field” which is used across many of our templates and we have more than 5 different languages.

If I try to remove the translatable flag to the block matrix, it overwrites all the content for the matrix for all the languages on the first save.

We need to revert this change somehow, but we’re unsure of when the error was introduced.

Is it possible to make sure that the flag is disabled without losing all our content? I have a DB backup of before we did any saves with the flag disabled, which means that we can follow a recipe if you have one.

This isn't a direct answer but one possible path if an easier option doesn't surface.

Sprout Import supports importing content into a Matrix field and multiple imports can be created to handle multiple locales while still matching things back to the parent entry.

Example of importing content to a Matrix field
Example of importing content for multiple locales
Example of how to generate a JSON Import file in your Craft templates. While this example uses fake data, you could update the loop to output the content you need to re-import.

It's likely you'll have a handful of relationships in your content that you'll need to make sure resolve correctly as well if you re-import your content, so depending on how your content is set up there may be a few more tricky parts to work through. But this is one possible path if other options seem like the may require a lot of manual steps.

Be sure to test this in a development environment and make the appropriate backups before modifying any live content.


Template not found 404 Having a strange issue - devMode is set to false, if I go to /404, I get the custom 404 page, which is great.

If I go to /something1234567890 (which is definitely not an entry's URL) I get the following exception:

yii\web\NotFoundHttpException: Template not found: something1234567890

I have tried adding debugging to my base templates but it looks like it is not getting as far as them.

Does anyone have an idea where I can start trying to debug this?

Stack trace:

2018-09-17 00:36:15 [xxx.xxx.xx.x][-][xxxxxxxxxxxxxxxxx][trace][yii\web\Application::handleRequest] Route requested: 'templates/render'
2018-09-17 00:36:15 [xxx.xxx.xx.x][-][xxxxxxxxxxxxxxxxx][trace][yii\base\Controller::runAction] Route to run: templates/render
2018-09-17 00:36:15 [xxx.xxx.xx.x][-][xxxxxxxxxxxxxxxxx][trace][yii\base\InlineAction::runWithParams] Running action: craft\controllers\TemplatesController::actionRender()
2018-09-17 00:36:15 [xxx.xxx.xx.x][-][xxxxxxxxxxxxxxxxx][trace][nystudio107\seomatic\{closure}] ErrorHandler::EVENT_BEFORE_HANDLE_EXCEPTION
2018-09-17 00:36:15 [xxx.xxx.xx.x][-][xxxxxxxxxxxxxxxxx][error][yii\web\HttpException:404] yii\web\NotFoundHttpException: Template not found: something1234567890 in /home/vagrant/code/craft-site/vendor/craftcms/cms/src/controllers/TemplatesController.php:70
2018-09-17 00:36:15 [xxx.xxx.xx.x][-][xxxxxxxxxxxxxxxxx][error][yii\web\HttpException:404] yii\web\NotFoundHttpException: Template not found: something1234567890 in /home/vagrant/code/craft-site/vendor/craftcms/cms/src/controllers/TemplatesController.php:70
Stack trace:
#0 [internal function]: craft\controllers\TemplatesController->actionRender('something123456...', Array)
#1 /home/vagrant/code/craft-site/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#2 /home/vagrant/code/craft-site/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#3 /home/vagrant/code/craft-site/vendor/craftcms/cms/src/web/Controller.php(103): yii\base\Controller->runAction('render', Array)
#4 /home/vagrant/code/craft-site/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction('render', Array)
#5 /home/vagrant/code/craft-site/vendor/craftcms/cms/src/web/Application.php(282): yii\base\Module->runAction('templates/rende...', Array)
#6 /home/vagrant/code/craft-site/vendor/yiisoft/yii2/web/Application.php(103): craft\web\Application->runAction('templates/rende...', Array)
#7 /home/vagrant/code/craft-site/vendor/craftcms/cms/src/web/Application.php(271): yii\web\Application->handleRequest(Object(craft\web\Request))
#8 /home/vagrant/code/craft-site/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#9 /home/vagrant/code/craft-site/public/index.php(22): yii\base\Application->run()
#10 {ma    in}

Update 1

The devMode toggling works on another Craft 3 site I have, just not this particular one. Maybe it is a database issue?

Update 2

I have tried this codebase with a fresh database and still getting the same error, so it must be something to do with my codebase?

There's a long discussion of all of this in the Handling Errors Gracefully in Craft CMS article, if it helps.


inline image transforms Is it possible to set an inline image transform's settings from the output of a Matrix block field?

For example

{% set transform = {
  mode: 'block.imageMode',
  width: block.imageWidth,
  position: 'block.imageFocus'
} %}

Each time I've tried this, the image gives 404. I know the surrounding code works as expected because hard coding the values, the image displays as we'd expect.

This works just fine:

{% set transform = {
  mode: 'crop',
  width: 300,
  quality: 70,
  position: 'center-center'
} %}

EDIT:

Some surrounding code for context

{% for image in block.image %}

{% set transform = {
  mode: block.imageMode,
  width: block.imageWidth,
  quality: 70,
  position: block.focalPoint
} %}

    {% if image.title %}{{ image.title }}{% endif %}

{% endfor %}

Try it without the quotes:

{% set transform = {
       mode: block.imageMode,
       width: block.imageWidth,
       position: block.imageFocus
   }
%}


Display non Craft CMS data with Twig I have a Craft CMS installation in which there will be other data imported into the db (via Cronjob). It's data from a Weatherstation which will be displayed without further editing in the Control Panel.

Is it possible to display this data via Twig bypassing the Control Panel all-together?

something like:

{% for entry in special.db.section('xyz') %}
  {{ entry.value }}
{% endfor %}

My first thought would be to do this via the Element API Plugin and then use JavaScript to display this data. But maybe that's not necessary?

You can create a custom plugin to fetch the data. Pluginfactory.io is a good place to create your basic files/folders. Then you'll insert this function in your PluginVariable.

public function getData($table){
    return craft()->db->createCommand()
        ->select('*')
        ->from($table)
        ->where('some Conditions)
        ->queryAll();
}

and in your template

{% for entry in craft.pluginName.getData('xyz') %}
    {{ entry.someField }}
{% endfor %}

Note: this only works when the table prefix is the same as your craft tables. So before your from($table) Craft will automatically include a $tablePrefix (usually craft_) you can either name your custom tables craft_ as well or change the prefix before your query

$oldPrefix = craft()->db->tablePrefix;
craft()->db->tablePrefix = '';
// handle your queries
craft()->db->tablePrefix = $oldPrefix;


Easiest way to make a table in the Control Panel sortable? I know that if i have created a new ElementType for my plugin that I can use the same template that Entries uses:

{% extends "_layouts/elementindex" %}
{% set title = "Entries"|t %}
{% set elementType = 'Entry' %}

and that will give me the standard layout with a sortable table.

However is there anyway to tap into the sortable stuff for my own custom table in my plugin.

Or is it simply a case of having to work the sortable stuff myself (which is fine)? I just thought I would ask as I would prefer to use the native stuff if it is easy to hook into.

As far as I'm aware, there is currently no native way to do this without using an ElementType index page.

You have two options for implementing this yourself. One is to sort the table on user input with the help of the registerCpRoutes hook (with AJAX or by reloading the page). The other is to use a JavaScript solution, as discussed in this question.


Is there a name for Craft 3's database model? I find Craft 3 to have a very clever database structure which eschews all of the issues I've seen with EAV.
Despite contrasting the differences between Craft 3's model and EAV, I'm having difficulty generating buy-in from the rest of my team who dismisses it as EAV. After so many times around the block, I wonder if it would help to point to a named database structure. I'm not looking for a comparison against EAV, rather a definition to provide my team.
Looked quite a bit and can't seem to pin down a name for Craft 3's database structure, is there one? If so, what?
Anecdote: one particularly snarky team member has taken to calling it "The Junk Drawer" after the content table. I sort of love it!

Craft uses a fairly typical relational model, where most1 custom fields have a single column in the content table, set to an appropriate column type chosen by the field type. Unlike EAV, which crams all data into a fixed schema, Craft’s schema will change over time based on the custom fields you’ve configured.
There are both performance and storage benefits to this. However there’s one significant downside, which is that it’s possible to hit row size limits if you have hundreds of custom fields (particularly with MySQL). We’re going to address that in Craft 4 by splitting the content table up into multiple tables – one per field layout.
1There are a couple exceptions: relational fields (like Entries) which store their data in the relations table, and fields that store nested elements such as Matrix and a couple third party field types, which create their own field-specific content-esque tables.


Include CSS file inline rather than as a link I use a pre-processor to generate my CSS. I would like to include the resulting file inline, rather than as a link. Is there a way of doing this  via the twig tags in Craft?

I know I could move the CSS file to the templates folder, so that I could use the include function, but is that the only and/or preferred method of achieving what I want to do? Or am I just best moving the inlining into my pre-processing? Thanks!

To simple insert raw CSS into a template from a file, you'll want to use Twig's source function. So:

<style>{{ source('file.css') }}</style>

For that file to be located somewhere outside of the templates folder, you'll have two options.

Create a symlink to the file or its folder in the templates folder. Twig will be none the wiser.
Add a Path to the existing template loader in Craft. This will require a custom plugin, and some digging to see how the loader is referenced in Craft. But in the end, your command will look something like this:

$loader->addPath('path/to/other/files');

The really cool part is that paths can have namespaces in Twig. You can do this:

$loader->addPath('path/to/public', 'public');

And that let's you do this in your templates:

{{ source('@public/css/site.css') }}


Outputting Sprout Form from a matrix field I'm using Sprout Forms to handle email forms on a website. Hardcoding a template with the code: {{ craft.sproutForms.displayForm('contactForm') }} works fine like in the docs.

But I have a Matrix field to build the page content so I'd like to add a new field type to that I can select which form to display. There is a Matrix field type 'sprout forms' which lets me select the form fine in the CMS.

My question is how do I now display the form in the template. I can't see any documentation on how to do this?

I've tried things like {{ block.customForm.displayForm() }} but with no luck.

Has anyone tried this before?
Thanks.

Sure, you'll likely want to use:

{% for block in entry.pageBody %}
    {% if block.type == 'form' %}
        {{ craft.sproutForms.displayForm(block.form.first.handle) }}
    {% endif %}
{% endif %}


Craft CMS Project Config file override Hi I am using Craft CMS with the project config file to keep various environments in sync. This generally works fine but I have run into an issue with plugin settings.

I am using the Contact Forms Extension and want to use the reCaptcha on my production site but I want it disabled locally. If I enable it on my production site, it gets disabled every time I deploy as I have it disabled locally.

Is there anyway I can create a plugin settings config to override the project config file?

I'm not familiar with this plugin, but could probably do this in your template where the plugin is called:

// Get the environment
{% set env = getenv('ENVIRONMENT') %}

// If anything but dev, then load the plugin
{% if env != 'dev' %}
    {% include ['_recaptcha/frontend'] %}
{% endif %}

You can also get the environment from the config:

{% set env = craft.app.config.env %}


How can I redirect HTTP to HTTPS with a local self assigned SSL certificate in my .htaccess? I'm having trouble redirecting HTTP to HTTPS with a self assigned SSL certificate generated by MAMP PRO using the following code in my .htaccess:

RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

or

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

I have also tried the patrol plugin for Craft, enabling/disabling dev mode and included  'enableCsrfProtection' => true, in my general.php

Also the .htaccess provided by Craft doesn't work for me.

The condition should be:

RewriteCond %{HTTPS} !=on

(You were missing the = sign.)

For more info, see our help article, How do I force SSL for CP requests?


How to setup custom site routes created by user in plugin settings In my plugin settings I am allowing the user to specify url formats for each locale along with desired template path, much like sections.  How do I then register those custom url formats as site routes?

You can use the registerSiteRoutes() hook in your plugin's primary class.

Assuming your custom URL formats are stored in an array in a plugin setting called customRoutes (use AttributeType::Mixed for arrays), and that the array consists of key/value paired routes and template paths:

public function registerSiteRoutes()
{
    return $this->getSettings()['customRoutes'] ?: array();
}

Here's how the customRoutes array should look:

array(
    'some-custom-route' => '_some-custom-template',
    'another-custom-route' => '_another-custom-template',
),


Get entry by language in a module I'm trying to get some auto translating done in a craft module. In order to do this I need to get the entry of another language in a save event. Is it possible to get this by language?

Event::on(
        Entry::class,
        Entry::EVENT_AFTER_SAVE,
        function(ModelEvent $event) {
            $entry = $event->sender;
            $isNew = $event->isNew;

            if ($isNew) {
                $enEntry = $entry->getEntryByLanguage('en');
            }
        }
    );

Something like this should work:

Event::on(
        Entry::class,
        Entry::EVENT_AFTER_SAVE,
        function(ModelEvent $event) {
            $entry = $event->sender;
            $isNew = $event->isNew;

            if ($isNew) {
                $enEntry = Entry::find()->id($entry->id)->site('enSiteHandle')->one();
            }
        }
    );

Just swap out enSiteHandle with your actual English site handle.


Uploading to a date subfolder within a Matrix I'm working on a site that will have lots of content. Our customer now asked to have their images upload into a different subfolder, by date.
I can get it done in a "normal" Assets field, but in a Matrix I get an error: Could not resolve the subpath “{postDate|date("d-m-Y")}”, with postDate being the name of the generated folder.
I did the exact same thing in a "regular" Assets field and that does its job as it should.

Can anyone tell me what I'm doing wrong?

Thanks!

You're probably looking for:

{owner.postDate|date("d-m-Y")}

See the note about dynamic subfolders on an Assets field inside of Matrix here: https://craftcms.com/docs/assets-fields#dynamic-subfolder-paths


Multiple Images/Assets from Super Table? I have a Super Table "previewImage" with a field "previewImageFile".

This does not work:

  {% for subsection in entry.descendants.descendantDist(1).all() %}

{{ subsection.shortTitle }}

      {% set image = subsection.previewImage.previewImageFile.one() %}
      {% if subsection.previewImage.previewImageType.value == 'jpg' %}
      {% set transformedImages = craft.imager.transformImage(image,
        [
          { height: 250, jpegQuality: 90 }
        ],
          {
            format: 'jpg',
            allowUpscale: false,
            resizeFilter: 'lanczos',
            effects: { unsharpMask: [0, 0.55, 0.55, 0.008] },
            interlace: false,
            convertToRGB: true
          }
      ) %}

      {% else %}

      {% set transformedImages = craft.imager.transformImage(image,
        [
          { height: 250 }
        ],
          {
            format: 'png',
            allowUpscale: false,
            resizeFilter: 'lanczos',
            effects: { unsharpMask: [0, 0.55, 0.55, 0.008] },
            interlace: false,
            convertToRGB: true
          }
      ) %}
      {% endif %}

      {% for image in subsection.previewImageFile %}

        <picture>
          <source class="img-fluid" srcset="{{ craft.imager.srcset(transformedImages) }}" type="image/jpeg">

        </picture>
        {% endfor %}

    {% endfor %}

I get the a (link) without images.

I guess previewImageFile.one() is wrong.
But setting it to all throws a error:

get_class() expects parameter 1 to be object, array given

Its hard to see without the full context of field setup, but as you state, I believe its {% set image = subsection.previewImage.previewImageFile.one() %} that's the issue.

I can see further down you're correctly looking through subsection.previewImage, which are Super Table blocks. Like so:

{% for block in subsection.previewImage %}
   {% set image = block.previewImageFile.one() %}

The issue is that you're trying to call previewImageFile on the Super Table block element query, which isn't going to work.

Maybe you're trying to just look at the first block available and get the image from that? If so, you could do:

{% set image = subsection.previewImage[0].previewImageFile.one() %}


Redactor can't add image into toolbar In my rich text field I want to add images to the toolbar.
I have done everything that has to be done: I've added "image" to the Standar.json file and selected the Standard.json to be used for the field.

The thing is, the 'image upload' is not added to the toolbar.
If I add "list" to the Standard.json the list does get added into the toolbar.

Does anyone know why this happens and how I can fix this?

You probably don't have an Asset Volume defined yet. The button won't show up if there are no Asset Volumes, regardless of what your Redactor config specifies.

  To add an Asset Volume, visit the Settings section of the admin, click "Assets," and then "Volumes." By default, only the "Local" Volume type is available, but you can install additional Volume types through the Plugin Store.


Hook in before template gets rendered The only thing I don't really like about Craft is that I have to fetch and eventually manipulate my entry in the twig template itself.

Is there a way to hook into the current request before the template gets rendered? Like that I could create some kind of Controller where I fetch all the necessary data, prepare it and then pass it on to the template.

Additionally this would allow me to use another template engine then twig.

If this is not possible, would there be a way to only use craft as the backend? From outside of craft, is there an easy way to find out if there are matching entries/singles/categories to the current request? I'm not looking for a complete answer here, more some key points where I could start looking.

Is there a way to hook into the current request before the template gets rendered?

Sure, Craft does this in several places in the Control Panel itself.

A plugin can registerCpRoutes hook.  From there, you'd define an action route like so:

return array(
    'cocktails/(?P<widgetId>\d+)' => array('action' => 'cocktails/editCocktail'),
);

That will send the request to your plugin's CocktailsController's actionEditCocktail.  There you can prep any data that you want and pass it off to a template to render:

$variables['name'] = 'Martini';
$variables['ingredients'] = array('Gin', 'Olives');

$this->renderTemplate('cocktails/_edit', $variables);

And access those from the template via {{ entry.name }} or:

{% for ingredient in entry.ingredients %}
    {{ ingredient }}
{% endfor %}

  Additionally this would allow me to use another template engine then twig.

I'm not seeing how you'd easily be able to do this, though.

Update:

There is also the Template Hooks, which might get you where you're going.


Related Entry within SuperTable A year ago I asked this question and thanks to the answers I got things working.  A year later and i'm trying to do the same thing on a different website...  only this time it's not working!

Section Handle = artist
SuperTable Field Handle = lineUp
SuperTable 'Entry' Field = artistPerforming, which is linked to Section 'artist'

Here's my code:

{% set reverseRelatedElements = craft.supertable.getRelatedElements({
    relatedTo: {
        targetElement: entry,
        field: 'lineUp.artistPerforming'
    },
    section: 'artist'
}) %}

{% for eventLineUp in reverseRelatedElements %}
    {{ eventLineUp.title }}
{% endfor %}

My SuperTable field is assigned to a Section with handle stageEvent, so from my stageEvent entry i'm trying to pull related entry information from Section artist.

Nothing is outputting but I don't get any errors??

Can anyone see what i'm doing wrong?

Many thanks

Martin

You can’t use this special Twig function to get reverse related entries at all, as you’re not dealing with reverse relations in that context.

From a stageEvent entry you can just loop through the Super Table rows and get the related artist entry like so:

{% for row in entry.lineUp %}
    {% set artistPerforming = row.artistPerforming.first() %}

    {% if artistPerforming %}
        {{ artistPerforming.title }}
    {% endif %}
{% endfor %}


craft()->urlManager->setRouteVariables() loads the correct template but does not pass in the variables everybody!

setRouteVariables() doesn't appear to be working for me.  If I boil my controller function down to just this:

public function actionSubmitForm() {
    craft()->urlManager->setRouteVariables([
        'foo' => 'bar'
    ]);
}

and my <form method="post" action="" accept-charset="UTF-8"> contains the following (among other inputs):

<input type="hidden" name="action" value="directoryForms/submitForm">

it hits the correct endpoint and then reloads the appropriate template, but it doesn't give me access to the {{ foo }} variable in that template.  It just tells me Variable "foo" does not exist.  I know it's hitting the right controller endpoint because if I die() there it dies as expected.

Any thoughts?  I've really boiled this one down as far as I can.  I must be missing something obvious.  Thanks!

My solution was to load the template directly rather than routing to the controller first.  Essentially, in my registerSiteRoutes() function I replaced this:

"payment" => ['action' => 'someController/doPayment']

with

"payment" => 'templateDir/core/payment'

I didn't even realize you could do this in registerSiteRoutes()!


How do I add an HTML tag to the formatting drop-down in the rich text editor? I would like to add the 'code' tag to the formatting drop-down. My rich text field is using Standard.json and I am able to remove the current tags, but it's not letting me add additional tags. I have tried adding 'code' like this in my Standard.json file:

formattingTags: ['p', 'blockquote', 'pre', 'code', 'h1', 'h2' 'h3', 'h4', 'h5']

Any thoughts?

(I realize 'pre' and 'code' are similar but if possible I would like to use code to be semantic.)

Might formattingAdd be something you can use for this? Not tested, just a quick look at Redactor's options page.

formattingAdd: [
    {
        tag: 'code',
        title: 'Code'
    }
]


How can I get the parent category group via the category to create multiple relation criteria? I have two category groups: Cars and Colors

My Cars category looks like this:

Cars
    Sports Cars
        Corvette
        Lamborghini
        Porche

My Colors category looks like this:

Colors
    Red
    Black
    White

I have two drop-down menus; one to choose the car, the other to choose the color.

I can choose the car, or the color just fine. My url's look like this:

mysite.com/cars/corvette/
- or -
mysite.com/cars/red/

It's when I need to relate both categories. Since my two category groups have different levels, I'm not sure how to get the parent group of Colors from red. Which is what I think I need in order for the relatedTo to work properly.

For example, a url might look like this:

mysite.com/cars/corvette/  {# no problem #}
- or -
mysite.com/cars/red/       {# no problem #}

I'm able to get to the categories on my template:

{#
 # Get the primary category GROUP via url segment.
 # Example: Cars
 #
 #}
{% set primaryCategoryGroup = craft.categories.slug(craft.request.getSegment(2)).first() %}

{#
 # Get the primary CATEGORY via url segment.
 # Ex: Corvette or Red
 #}
{% set primaryCategory = craft.categories.slug(craft.request.getSegment(3)).first() %}

Then I check if I need to compare both relationships:

{% if craft.request.getSegment(4)|length %}
      {% set secondaryCategorySlug = craft.categories.slug(craft.request.getSegment(4)) %}
      {% set secondaryCategory = craft.categories.slug(craft.request.getSegment(4)).first() %}

      {{ secondaryCategory.title }}  {# Red or Corvette #}
      {{ primaryCategory }}  {# Red or Corvette #}

      {# Begin the fail #}
      {% set entries = craft.entries.section('mySection').relatedTo('and',
          { sourceElement: secondaryCategory, field:'title' },
          { targetElement: primaryCategory }
      ) %}
{% else %}
    {# Only get single relationship. #}
    {% set entries = craft.entries.relatedTo(primaryCategory) %}
{% endif %}

Not 100% sure if this is correct, but this works.

Example urls:

www.mysite.com/cars/corvette/red
www.mysite.com/cars/red/corvette
...
www.mysite.com/cars/red/
www.mysite.com/cars/corvette

Here is my template:

{#
 # Check if there are multiple url segments to compare against.
 # This means we need to check against multiple relationships.
 # Ex: All Corvettes that are also Red
 #
 #}
{% if craft.request.getSegment(4)|length %}
    {% set secondaryCategory = craft.categories.slug(craft.request.getSegment(4)).first() %}
    {% set entries = craft.entries.section('mySection').relatedTo(['and', { targetElement: primaryCategory }, { targetElement: secondaryCategory }]) %}
{% else %}
    {#
     # Get all products related to the current category.
     # Ex: All Sports Cars that are related to Corvettes.
     #
     #}
    {% set entries = craft.entries.relatedTo(primaryCategory) %}
{% endif %}

...

{% if entries|length %}
    ...
{% endif %}

What i'm not sure about is both are using targetElement instead of one being sourceElement.


Deprecated Warning: safeHtml I'm getting a Deprecated Warning on the property safeHtml:

  The embedded asset property safeHtml is now deprecated. Use a
  combination of the isSafe() method and the code property instead.

This is picked up on line 4 and 11 of the code below:

        {# if video embed #}
        {% if embed.type == 'video' %}

                {% set vidcode = embed.isSafe() | trim('<iframe') %}
                {% set vid = '<iframe class="aspect-ratio--object" ' ~ vidcode %}
                {{ vid|raw }}

        {# else if rich embed #}
        {% else %}

                {{ embed.isSafe()|raw }}

        {% endif %}

How do I update this code correctly to remove the warning?

Since it appears that you are using Craft CMS 3, you can try the purify filter.

In your code, change this line:

{% set vidcode = embed.isSafe() | trim('<iframe') %}

To something like:

{% set vidcode = embed | purify | trim('<iframe') %}

Documentation:


Is Craft suitable for large websites? We're looking at various CMSs for our large 11,000+ page website. We'd like to use a CMS that is appropriate for sites of that size and that will give us room to grow without impacting site speed, page load time, etc. I see other questions on this forum regarding site size, but all use CraftCMS terminology, such as 'channels', 'structures', etc to describe size.
How can CraftCMS size capability be described in pages?

Thank you.

Yes, absolutely. Craft have some case studies which illustrate that top brands trust them to handle large amounts of content while making it easy to publish more: https://craftcms.com/news/case-studies

My second point is a huge selling point for a good CMS. If your publishing process is difficult or confusing then you as an admin/client become frustrated. Craft is fast and easy to digest from a publishers point of view.

(Wes mentioned relationships)
By design, you should only need to add a piece of content to your CMS once - and then 'relate' different entries together as new ones are published. e.g. A new 'Service' (entry) is related to an 'office location' (entry) where that service is provided.  Then, when a visitor is viewing that office location they are able to see any services which may be available there. (Just a simplified example)

Planning and designing what your content-types consist of and how different types of content can work together to tell a story to a visitor - will mitigate any major shifts in direction which often happen on-the-fly (the most expensive way to build a website).

Coming back to your question.
Craft is very good at storing your content efficiently.

Craft uses Twig as a template language, which requests content from Craft. The trick is to only tell Twig to get what you need for any given page. This isn't hard once you understand the approach.

Images are also uploaded once and then re-saved (image transforms) into different sizes as they are used differently through your site.

The effect is that instead of slowly trying to load an image which is large in size, a small image is loaded quickly.

Hope that helps. I'm sure you'll get more answers too...


Use a single template to display entries from different channels? I'd like to use a single template to pull in entries from different channels based on url segment 2. But it seems I need to change the section attribute to match the url segment:

{% for entry in craft.entries.section('mySection').find() %}

Given that the url segment and section handle are different, can this be done?

For example the url segment might be hotel-california and the section handle of the section I want entries from could be hotelCalifornia or hotelsInCalifornia.

Personally, in situations like this (particularly if there's only a few items, and no easy way to automate it with inflection or similar,) I just use an array as a kind of dictionary.

{% set sectionHandlesBySegment = {
    'hotel-california': 'hotelsCalifornia',
    'hotel-florida':    'hotelsFlorida' }
%}

{% if not craft.request.getSegment(2) in sectionHandlesBySegment|keys %}
    {% exit 404 %}
{% endif %}

{% set sectionHandle = sectionHandlesBySegment[craft.request.getSegment(2)] %}
{% set hotels = craft.entries.section(sectionHandle) %}


Restict admin user permissions to only editing users he/she has created. I'm thinking about building an app with Craft that requires users from a specific user group (Business User Usergroup) to be able to log in on the frontend and add users from another user group (Client Usergroup) from the frontend, these users would be their clients and they would be able to view information set by the admin aswell as upload certain documents that they admin can then view.

// If anyone is interested the application will be a portal where coaches/therapists can register, log in and become a user( of the business user Usergroup) and can then add their clients as users (Client Usergroup) and then they can communicate digitally before meeting. Obviously, i don't want therapists to be able to see clients that don't belong to them. For this, i need to restrict user permissions as to make sure that the therapist can only see and edit users/clients that belong to him.

What i cant seem to figure out is if there is a way either with Craft or a plugin that makes sure that the admin cannot edit/see users added by other users in their user group. This means that if a user/coach from the Business Usergroup logs in and adds a client/user (from the Client Usergroup) only he/she, all super users and the client he added can login, view and edit these account details.

Has anyone come across a plugin or have experience with this type of "user-specific" bound user permission? If the explanation is too vague or unclear then i would be happy to explain in more detail. Hope someone can help with this and thanks in advance!

I believe what you're looking for is 'isInGroup'

{% if currentUser.isInGroup('groupHandle') %}

It will also accept an actual group object or a group ID as well.

There is also getGroups:

{{ entry.author.getGroups() }}

So you have several options:

If all users only have one group (so they can not be in two) you can check if the current user is in the same group like the first group of entry.author. But this has the disadvantage if someone wants to change his/her group the entry will become invisible for the previous group
You could attach the group handle to an entry. So you field create a plainTextField and insert the group handle of the author to this entry -> if the author changes his/her group the entry will still be available for the author as well as everyone who was able to see the entry before.

If you choose the second option you would be able to include the search condition in a ElementCriteriaModel very smooth. And search only entry that is connected by the current group of the currentUser. If someone should be able to see entries but he/she is not allowed to edit them you would just include the isInGroup condition to check if she/he may proceed.

For example your user list could look like that

{% set users = craft.users.find() %}
{% for user in users %}
    <!-- everyone can see the firstName of each user -->
    {{ user.firstName }}
    {% if currentUser %}
        <!-- but only logged in users can see the last name -->
        {{ user.lastName }}
        {% set groupsOfUser = user.getGroups() %}
        {% if groupsOfUser|length %}
            {% if currentUser.isInGroup(groupsOfUser[0]) %}
                <!-- only users of the same group can edit/delete or whatever -->
            {% endif %}
        {% endif %}
    {% endif %}
{% endfor %}


Search not searching certain words - how to fix? I have an entry about the U.K. TV show "Being Human". If I search "being" on the back end, it comes up with 0 results, same on the front end search.
Searching for "Human" brings it up, but "Being Human" does not.
Doe the default "title" field not get indexed by search? How can I enable it if so?

Try to specify the fields you need to search.
{% set queryParams = {
        search: {
            query: 'title:*' ~ name ~ '* OR description:*' ~ name ~ '* OR previewText:*' ~ name ~ '* OR tag:*' ~ name ~ '*'
        }
    } %}

{% set entries = craft.entries(queryParams).section('entries').all() %}


Errors after upgrade to Craft 2.5 I'm having a few irregular errors after upgrading via the automatic update. When trying to visit the dashboard I get:

Internal Server Error
Passed variable is not an array or object, using empty array instead

My max_execution_time and memory_limit are set pretty high, and I believe the upgrade completed successfully. But I cannot view the dashboard or settings pages. Everything else seems to be fine.
Do I have to fully revert and re-try the upgrade via the manual method?

To answer my own question: There may be some plugin incompatibilities when upgrading to Craft 2.5. This is to be expected, given all the variables. In my case the Placid plugin was not yet compatible, as seen when I put Craft into devMode.

So either:

Wait on the upgrade until your plugins are current, or
Disable lagging plugins as necessary if you need the bleeding edge.


Customizing Stripe payment form for Craft Commerce 2 Where does one start with matching the style of the Stripe payment form to the rest of the site?

Using Stripe for Craft Commerce Plug-In

 {{ plan.getGateway().getPaymentFormHtml({})|raw }}

This enables the form with Card Holder info and credit card details.

The following code allows the customisation of the fields using Stripe Elements. This code will replace the payment form with gateway.getPaymentFormHtml({}).

The thing to note here is this is provided unsupported as it is custom code and does not use the payment form template or javascript file that is in the Commerce Stripe plugin. This also means that it will not benefit from any updates to the plugin that change those files.

{% set cart = craft.commerce.carts.cart %}
{% set gateway = cart.gateway %}
{% set scenario = 'payment' %}
{% set billingAddress = cart.billingAddress ?? null %}

<form method="POST" id="payment-form">
  <input type="hidden" name="action" value="commerce/payments/pay"/>
  {{ redirectInput('/shop/customer/order?number='~cart.number) }}
  <input type="hidden" name="cancelUrl" value="{{ '/shop/checkout/payment'|hash }}"/>
  <input type="hidden" name="orderEmail" value="{{ cart.email }}"/>
  <input type="hidden" name="gatewayId" value="{{ cart.gatewayId }}"/>
  {{ csrfInput() }}

      <input type="text"
             name='firstName'
             maxlength="70"
             placeholder="{{'First Name'|t('commerce')}}"
             autocomplete="false"
             class="card-holder-first-name{{(paymentForm is defined and paymentForm.getErrors('firstName') ? ' error')}}"
             value="{{paymentForm is defined and paymentForm ? paymentForm.firstName : ''}}"
      >
      <input type="text"
             name="lastName"
             maxlength=70",
             placeholder="{{'Last Name'|t('commerce')}}"
             autocomplete="false"
             class="card-holder-last-name{{ (paymentForm is defined and paymentForm.getErrors('lastName') ? ' error') }}"
             value="{{paymentForm is defined and paymentForm ? paymentForm.lastName : ''}}"
      >

    {% set errors = paymentForm is defined and paymentForm ? paymentForm.getErrors() : [] %}
    {% if errors|length %}

        {% for error in errors %}

{{ error|join('
')|raw }}

        {% endfor %}

    {% endif %}

  <input type="submit">
</form>

{% do view.registerJsFile('https://js.stripe.com/v3/') %}
{% js %}
// Create a Stripe client.
var stripe = Stripe('{{ gateway.publishableKey ?? '' }}');

  // Create an instance of Elements.
  var elements = stripe.elements();

  // Custom styling can be passed to options when creating an Element.
  // (Note that this demo uses a wider set of styles than the guide below.)
  var style = {
    base: {
      color: '#000000',
      fontSize: '16px',
      '::placeholder': {
        color: '#CCCCCC'
      }
    },
    invalid: {
      color: '#fa755a',
      iconColor: '#fa755a'
    }
  };

  var classes = {
    base: 'StripeElement',
    complete: 'StripeElement--complete',
    empty: 'StripeElement--empty',
    focus: 'StripeElement--focus',
    invalid: 'StripeElement--invalid',
    webkitAutoFill: 'StripeElement--webkit-autofill'
  };

  var card = elements.create('cardNumber', {
    style: style,
    classes: classes
  });
  card.mount('#card-number');

  var expiry = elements.create('cardExpiry', {
    style: style,
    classes: classes
  });
  expiry.mount('#card-expiry');

  var cvc = elements.create('cardCvc', {
    style: style,
    classes: classes
  });
  cvc.mount('#card-cvc');

  // Handle real-time validation errors from the card Element.
  function cardErrors(event) {
    var displayError = document.getElementById('card-errors');
    if (event.error) {
      displayError.textContent = event.error.message;
    } else {
      displayError.textContent = '';
    }
  };

  card.addEventListener('change', cardErrors);
  expiry.addEventListener('change', cardErrors);
  cvc.addEventListener('change', cardErrors);

  // Handle form submission.
  var form = document.getElementById('payment-form');
  form.addEventListener('submit', function(event) {
    event.preventDefault();
    var firstName = document.querySelector('.card-holder-first-name');
    var lastName = document.querySelector('.card-holder-last-name');

    var paymentData = {
      billing_details: {
        name: firstName.value + ' ' + lastName.value,
        email: '{{ cart.email }}',
        address: {
          line1: '{{ billingAddress.address1 ?? '' }}',
          city: '{{ billingAddress.city ?? '' }}',
          state: '{{ billingAddress.state ?? '' }}',
          postal_code: '{{ billingAddress.zipCode ?? '' }}',
          country: '{{ billingAddress.country.iso ?? '' }}'
        }
      }
    };

    stripe.createPaymentMethod('card', card, paymentData).then(function(result) {
      if (result.error) {
      // Inform the user if there was an error.
      var errorElement = document.getElementById('card-errors');
        errorElement.textContent = result.error.message;
      } else {
          var form = document.getElementById('payment-form');
          var hiddenInput = document.createElement('input');
          hiddenInput.setAttribute('type', 'hidden');
          hiddenInput.setAttribute('name', 'paymentMethodId');
          hiddenInput.setAttribute('value', result.paymentMethod.id);
          form.appendChild(hiddenInput);

          // Submit the form
          form.submit();
      }
    });
  });

{% endjs %}


How do I pull the last level of a category? This allows me to pull the related categories from a second category group for a given category.

{% set entries = craft.entries.relatedTo(category).find() %}
{% set tags = craft.tags.relatedTo(entries).find() %}
{% set markets = craft.categories.group('markets').relatedTo(entries).find() %}

BUT, it pulls both the parent and descendant categories from markets.  And, some markets do not have descendants - so adding `.level(2) would not capture those that only have a level(1) assigned.

I want to list only the last level assigned whether it is level(1) or level(2).

I tried adding .last() but that generates a CException error.

{% set markets = craft.categories.group('markets').last().relatedTo(entries).find() %}

What am I missing?

Thanks

.last is the opposite of .first, meaning that you can only apply it at the end of your query.

{% set markets = craft.categories.group('markets').relatedTo(entries).last() %}

And similar to .first, the .last method will only return a single Entry.


Passing optional variable values into an include template Craft noob here. I have an include file which I'm calling a "component" so I can reuse it across the site.

The component is set up like this.

{{ heading }}

{{ bodyContent }}
{% if ctaUrl is defined %}
  Click
{% endif %}

Then on the template, I include it and pass some variables. In this case, the variables are from a Matrix field.

{% set matrixBlocks = entry.fullContentMatrix.all() %}

{% for block in matrixBlocks %}
    {% if block.type == 'imageRight' %}

        {% include '_components/imageRight' with {
            heading: block.heading,
            bodyContent: block.bodyContent,
            ctaUrl: block.ctaUrl
        } %}

...etc

This all works great. Until I have fields that are optional and have no value, in this case, the ctaUrl.

What is the best way to write this so that variables are only passed to the include if they are defined?

So in this case with ctaUrl, the issue is that you are checking if ctaUrl is defined.

As long as a field exists, is defined will return true as in reality, the variable block.ctaUrl exists, it's just empty. To check for content in your case, you should test for NULL. So if ctaUrl is not NULL show your link.

In terms of organisation of your code, it's up to you. Like Roi said, you don't need the with. Your matrixBlocks will be available within the component.

Whether you set stuff up in the main template is up to you. Personally, I would do my logic in the component rather than the main templates.

You will need to be consistant with your naming but the component is where you need this logic so if it's is in there, you don't need to dig around to understand what is going on. If you remove a component, the logic goes with it as suppose to have a bloated main template and also, you don't do a lot of stuff before you need to. No need in checking if you have a ctaUrl if your _components/imageRight isn't requested...

One reason for doing the logic in the main template would be that you would have less duplication (maybe) but from experience, I'm not sure it's worth it. So In your case:

{% set matrixBlocks = entry.fullContentMatrix.all() %}

{% for block in matrixBlocks %}
    {% if block.type == 'imageRight' %}
        {% include '_components/imageRight' %}
    {% elseif block.type == 'imageLeft' %}
        {% include '_components/imageLeft' %}
    {% elseif block.type == 'someOtherBlock' %}
        ....
    {% endif %}
{% endfor %}

Then do your work in your components:

{{ block.heading }}

{{ block.bodyContent }}
{% if ctaUrl is not NULL %}
  Click
{% endif %}

Doing this, you will have a lean and clear main template and the component level logic will live in the component where it belongs. My opinion only though... Plenty of ways to do this :)

So to your question:

  What is the best way to write this so that variables are only passed to the include if they are defined?

In your case, don't worry about that, check if they are defined when you need to use them, at component level.


Live Preview and Twig blocks I'm using {% block content %} and a main layout file.

When using live preview the "entire" frame isn't shown (including layout files).

Is there an easy way to get the whole template in?

The problem was my attempt to ajaxify the page.

I had my block content wrapped around
{% if not craft.request.isAjax %}

the page also gets loaded in via Ajax into the control panel, I changed the code to:
{% if not craft.request.isAjax or craft.request.isLivePreview %}

Now it works.


Can I reassign the client account? I would like to give my client the client account, but first, I would like to assign my colleague as the client, just so we can see what the process looks like and see how our client will view Craft. Will it be possible to update the name and email address afterwards?

Sounds like you just need to initially setup the client account with your colleague's email/password/name.

Have them do their testing and when you're ready to give it to your client, either just update the email/password/name or delete the client account and re-create it for your client.


User restricted access to content via a common template I’d like be able to restrict access to the content of entries, served by a common template, subject to the user group of the logged in user.

For example:

There is a template T
User U is a member of group G
Users V and W are members of group H
Entry A, served by template T, is accessible by group G, and therefore only by user U
Entry B, also served by template T, is accessible by group H, and therefore only by users V and W

Is this possible with Craft out of the box? Or does it require custom plugins?

I’ve done this kind of thing in ExpressionEngine using its ZooVisitor add-on, but this is my first foray into user restricted access to content with Craft.

If a sticking point is the single template, could it be done with a template per user group?

I'd be grateful if someone could point me in the right direction, as I can’t quite see the wood for the trees on this scope in the Docs.

Update:

I infer from https://craftcms.com/docs/templating/usermodel I can…

{% if currentUser.isInGroup(group) %} {# do something… #} {% endif %}

So I guess I just need a way to feed entry specific stuff into the conditional.

Or more ideally, dynamically write the conditional as an outcome of creating a new entry that has user group requirements assigned to it. Though that sounds a bit tricky.

I assume you have basic knowledge in creating plugins, if not I suggest visiting the docs.

It's actually pretty easy to achieve your goal.

Create a Plugin and store an array of permission's in it's settings

<!-- first permission -->
<input class="text" type="text" name="permissions[]" value="" autofocus="" autocomplete="off">

<!-- 2nd permission -->
<input class="text" type="text" name="permissions[]" value="" autofocus="" autocomplete="off">

<!-- 3rd permission -->
<input class="text" type="text" name="permissions[]" value="" autofocus="" autocomplete="off">

<!-- 4th permission -->
<input class="text" type="text" name="permissions[]" value="" autofocus="" autocomplete="off">

<!-- just include some javascript logic to add fields if needed to create them dynamically -->

Register those permission from your plugin

public function registerUserPermissions()
{
    $permissions = array();
    foreach($this->getSettings()['permissions'] as $rule){
        $permissions[$rule] = array('label' => $rule);
    }

    return $permissions;
}

Create a custom fieldType that displays all usergroups dynamically or (for testing) create a simple dropdown field that stores user groups

Add this field to a matrix element -> you can store a required group for each element
Go to your usergroups and add the permissions to those groups then you can check if the currentuser is in the group you selected in your dropdown
After you understood the process you can create a custom field type to store/link usergroups (so you can store multiple and select them based on all existing usergroups dynamically)
Make your plugin settings more complex.. in my example you can only store the handle not the label of your permission so if you type "Permission to do that" instead of "permissionToDoSomething" it will break


For performance is using a macro better than using include with the 'with' statement? Which is better for performance? Using a macro for output in a for loop or calling an include using the 'with' params to pass data? Both are loading a file. But is stacking up a bunch of code in a macro file faster than using an small include file?

{% for item in entries %}
    {% include "/components/_item" with { item: item } %}
{% endfor %}

OR

{% for item in entries %}
    {{ helpers.item(item) }}
{% endfor %}

If I had to guess, I'd say a macro would be faster because it's accessing the file system less.  In any case, unless you're dealing with hundreds of thousands of calls, I'd also guess that's the difference is going to be negligible for most use cases.


Can a Category Entry have an Author This may be basic but not finding the right way to do it so here is a question.
I have a category page where each has its own url and have custom fields on it. But for some reason the author is not showing on the right side like the entries do?

Is there a reason categories dont have authors or am i missing something?

Categories do not have authors. They should not be considered "content" like entries in channels or structures, but as a way to relate entries or assets together. If you needed to associate a category with a particular person, you can create a user field and add it to the categories custom fields.


How to get a field value from next/prev asset asset.getNext().fieldName does not work but might help explain what I was aiming for.

To be more specific, for where there are multiple images in an entry, I want to assign a class to each depending on its next image’s field value. Also, the field I’m trying to get a value from is a radio field.

I found this to work for what I needed:

{% set nextImage = image.getNext() %}
{% if nextImage %}

    {% if nextImage.checkboxFieldHandle.contains('value') %}
        {# Do something #}
    {% endif %}

{% endif %}


Using Twig's replace filter with getURL() I have a template that displays in both http and https environments. When pulling my images for the https environment I need to be able to change the URL to https:// from http://. I tried

{{ gist.gistImage.first().getUrl('gistUnit')|replace({'http': https}) }}

based on the documentation here twig.sensiolabs.org/doc/filters/replace.html

Not working. Is this because getURL() gets processed after the filter?

Try using:

{{ gist.gistImage.first().getUrl('gistUnit')|replace({'http': 'https'}) }}


siteUrl config and Control Panel conflict I have set up environments in my config file and included a different siteUrl for each. This is so I can change the navigation on the fly.

I have noticed however that siteUrl when defined in the CP is being used instead of the defined value in the config file. I was under the impression that the CP setting for siteUrl got ignored. What am I doing wrong?

I'm using the same database across both environments - would that be an issue?

return array(
     '*' => array(
         // ...
     ),
     // Local Dev
     'test.dev' => array(
         // ...
         'devMode' => true,
         'environmentVariables' => array(
             'assetsBasePath' => '/Users/joebloggs/Sites/work/example/shop/assets/',
             'assetsUrl' => 'http://test.dev/shop/assets/',
             'siteUrl'  => 'http://test.dev/',
         )
     ),
     // Live Dev
     'test.example.com' => array(
         // ...
         'environmentVariables' => array(
             'assetsUrl' => 'http://test.example.com/shop/assets/',
             'assetsBasePath' => '/home/example/public_html/shop/assets/',
             'siteUrl'  => 'http://test.example.com/',
         )
     ),
);

The siteUrl config setting is not meant to be added under environmentVariables.

'test.dev' => array(
    'siteUrl' => 'http://test.dev/',
    'environmentVariables' => array(
        // ...
    ),
    // ...
)


Adding a new site returns 503 Service Unavailable I successfully installed Craft locally and my primary site is up and running, but when I add a new site with it's own base URL I cannot access it due to a HTTP 503 error.

I've tried various combinations of environment variables and aliases for the URL, and for now I just added the base URL directly in the CP (anotherdomain.locahost).

I also played around with various vhosts settings in XAMPP (Win 10) to no avail. It seems to me that the vhost is properly configured.

Here is my stack trace, any help would be much appreciated.

2020-01-21 15:07:49 [-][-][-][trace][yii\web\Application::handleRequest] Route requested: 'templates/render'
2020-01-21 15:07:49 [-][-][-][trace][yii\base\Controller::runAction] Route to run: templates/render
2020-01-21 15:07:49 [-][-][-][error][yii\web\HttpException:503] craft\web\ServiceUnavailableHttpException in D:\Web\dev\folder\vendor\craftcms\cms\src\web\Controller.php:163
Stack trace:
#0 D:\Web\dev\folder\vendor\craftcms\cms\src\controllers\TemplatesController.php(72): craft\web\Controller->beforeAction(Object(yii\base\InlineAction))
#1 D:\Web\dev\folder\vendor\yiisoft\yii2\base\Controller.php(155): craft\controllers\TemplatesController->beforeAction(Object(yii\base\InlineAction))
#2 D:\Web\dev\folder\vendor\craftcms\cms\src\web\Controller.php(187): yii\base\Controller->runAction('render', Array)
#3 D:\Web\dev\folder\vendor\yiisoft\yii2\base\Module.php(528): craft\web\Controller->runAction('render', Array)
#4 D:\Web\dev\folder\vendor\craftcms\cms\src\web\Application.php(299): yii\base\Module->runAction('templates/rende...', Array)
#5 D:\Web\dev\folder\vendor\yiisoft\yii2\web\Application.php(103): craft\web\Application->runAction('templates/rende...', Array)
#6 D:\Web\dev\folder\vendor\craftcms\cms\src\web\Application.php(284): yii\web\Application->handleRequest(Object(craft\web\Request))
#7 D:\Web\dev\folder\vendor\yiisoft\yii2\base\Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#8 D:\Web\dev\folder\web\index.php(21): yii\base\Application->run()
#9 {main}

System status was disabled, enabling it solved the problem.

Now I'm not sure if this is the expected behavior, what threw me off was that I could still preview the primary site. I'm guessing on multi-site installs it's best to disable at the site level rather than the system level.


Possible to speed up infinite scrolling? In the control panel, on the entries index page, Craft uses infinite scrolling. However, when you scroll to the bottom, there's a slight delay while you wait for more entries to load.

Unfortunately, I'm in a situation where I have to scroll through thousands of entries. I'm cleaning up some imported data, and yesterday I managed to clean up entries from A - M. Today I need to pick up where I left off, and clean up entries from N - Z.

Is there anything I can do to speed up the scrolling process? (besides having a faster server)

You could open up the console and paste this then scroll to the bottom once.

After that, this will keep scrolling to the #footer every time a response from the getMoreElements request comes in.

$(document).ajaxComplete(function (event, xhr, options) {
  if (options.url.slice(-15) === "getMoreElements") {
    setTimeout(function () {
      $('#footer')[0].scrollIntoView(true)
    }, 10)
  }
})


Twig Filter in Plugin/PHP? Is it possible to access twig filters in a plugin? I want to be able to preview how text will look in a textarea using Markdown for a discussion board, it'd be awesome to use ajax to send the content to the controller then return the filtered version for preview, something like:

public function actionPreviewContentAjax()
{
    $this->requireAjaxRequest();

    $content = craft()->request->getPost('textarea_content');

    // somehow apply twig's "| markdown" filter to $content

    $this->returnJson( $content );
}

Is twig accessible in this manner or am I out of my mind? :X

In one of your plugin services, you can render a template that handles the markdown filter and return the results as an HTML string. You can then send that HTML to wherever you want to preview.

In your controller:

public function actionPreviewContentAjax()
{
    $this->requireAjaxRequest();

    $content = craft()->request->getPost('textarea_content');

    $html = craft()->pluginName_serviceName->renderMarkdown($content);

    $return = array('html' => $html);

    $this->returnJson( $return );
}

In your service:

public function renderMarkdown($content)
{
    $rendered = craft()->templates->render('path/to/template', array('content' => $content));
    return $rendered;
}

All you would need is a small template that renders your content. If you have this template in your plugin folder, be sure to adjust the template path as described here.

Template:

{{content|markdown}}

If you need more help with the writing services, let me know!


Show a table field's column headers in template? The question says it all.

I have:

A Matrix field, with
A subfield with the type table
Two columns within that table of which I want to show the column headers in my template

Is that possible and if yes, how?

You don't get table headings with the entry model data (use the dump() function while in dev mode to see yourself). But there's the method getFieldByHandle() that returns a field model containing info to any field settings.

This is how you access the headings of a table field

Craft 2

{% set field = craft.fields.getFieldByHandle('myTableField') %}

{% for column in field.settings.columns %}

{{ column.heading }}

{% endfor %}

Craft 3

{% set field = craft.app.fields.getFieldByHandle('myTableField') %}

{% for column in field.settings.columns %}

{{ column.heading }}

{% endfor %}

Table field in Matrix block:

As fields within matrix blocks don't necessarily have unique field handles, you can't use the getFieldByHandle() method to get the field model. Use the unique field ID with the method getFieldById() instead. To look that ID up, open up the source code of the field in the matrix field's field settings. There's a div with the class matrixconfigitem and a data attribute data-id, which is the ID you are looking for.

(PS: use the code in your matrix loop for the block type containing your table "sub" field to output in the right place, but it actually works everywhere!)