Implement hreflang with multi sites I currently have multi site config. One site have 2 locales.
My configuration like so:

'example.com' => array(
    'siteUrl' => array(
        'us_en' => 'https://www.example.com/',
        'br_sp' => 'https://www.example.com/sp'
    ),
'example.uk' => array(
    'siteUrl' => array(
        'uk_en' => 'https://www.example.uk/',
    ).

I'm using SEOmatic plugin.
Hreflang requires all site have to list on the head of page.
However, SEO list 2 locales which are us_en and br_sp on a site only.
How can I do it?

You're going to want to ensure that all of the sites that are a translation of each other are in the same site group, as detailed here: SEOmatic Multi-Site Language/Locale Support


Could not find a unique URI for this element Suddenly, I can't add any entry to a structure.
When I try to save a record, I've got "Could not find a unique URI for this element“. Although, the slug is perfectly valid and unique, but whatever I type in the slug field, same message "Could not find a unique URI for this element". I only got a couple of entries in this structure, so I could not have possibly hit the  'maxSlugIncrement' limit…
Thanks for your help.

The Uri of each element must be unique but if your Uri format is something that might not render a unique string you'll receive the error you are facing with.

As soon as you include some unique identifier like { id } or { slug } into your sections uri it will work again. Right now you can only save an entry if the field value is unique


How to use "&" in richtext? I'm currently finishing a client-website which has to be 100% valid HTML5. For one of the sections, I am using a matrix containing rich-text fields. This content is simply shown on the front page.
The name of the client is "E&K".

Requirements:

The output contains HTML tags.
The output has to be 100% valid HTML.

Problem:
The & character won't be escaped to &amp; wherever it is outputted. This breaks the valid requirement. It seems the problem is the input, not the output. However the rich text editor keeps replacing manually placed &amp; with &

How can I get this to work correctly?

You could use Twig's replace filter on output:

{{ entry.richtext_field_handle|replace('&': '&amp;') }}

At first glance I couldn't find a Redactor setting which would help you, but you might want to check yourself, or even write a Redactor plugin.


Error Configuring Email for SMTP I have tried to configure email as an SMTP client for a Google Mail server. When I click test I get a generic "There was an error testing your email settings", but no further info in Craft log files. The error displays almost instantly, so it does not seem like a connection issue, e.g. password / username error.

Craft is latest version and site is hosted with Arcustech.

Google considers SMTP to be an "insecure" protocol when connecting to their servers.  You have to explicitly go into your account settings as described here and enable access for these "insecure" requests.


Where should a plugin create temporary files I have a plugin which needs to create a temporary file. Where should I put it?

PathService has a getTempPath() method which will return the craft/storage/runtime/temp path by default.

Craft uses that for its temporary files and so can plugins.  Just make sure youre plugin is being defensive by giving it a unique filename or create a folder with a unique name and put your files in there, etc.


modifyEntrySources hook, comparing two field values Is it possible with the modifyEntrySources hook to query a field value against another fields value? Example:

Two fields are present on the entry section this hook targets

fieldParentId
fieldId

I want to be able to query in the entry source definition if the fieldParentId is the same as the fieldId to return only those records in the entry list within Craft that match this. I'm not sure this is possible, or if there is a special syntax required within the criteria array to do it?

A workaround solution was to essentially do an criteria on the section, return all the records, use a conditional to find out which records have the same "parent ID" and ID", stores those in an array and then use the id criteria for the entry source.

Something like:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = 'theSection';
$criteria->limit = null;
$articles = $criteria->find();

// For storing matching entries on the conditional below
$ids = [];

foreach ($entries as $entry)
{
     if($entry->fieldParentId == $entry->fieldId)
     {
         array_push($ids, $entry->id);
     }
}

This will then provide a list of ids that match the required criteria, which can then be used for a specific entry source.

This has performance issues though, because you have to get the entire list of entries first each time.


Filter entries by entry type in the CP? We have a channel called Articles with multiple entry types. Is it possible to filter the entries within Craft (on the Entries page) by entry type?

What you can do is to create additional entry sources using the modifyEntrySources hook.

This is a very simple example, that will add a heading and two sources to the end of the
sources list (below your default Singles, Channels and Structure sources).

public function modifyEntrySources(&$sources, $context)
{
    $newSources = array(
        array('heading' => 'Post Formats'),
        array(
            'label' => 'Articles',
            'criteria' => array('section' => 'blog', 'type' => 'article')
        ),
        array(
            'label' => 'Links',
            'criteria' => array('section' => 'blog', 'type' => 'link')
        )
    );

    $sources = array_merge($sources, $newSources);
}

You could also get all entry types of a section with getEntryTypes() and add them programatically if you want.


multi-domain setup: template entry.url includes index.php .htaccess is working fine. If I manually type url.com/about it shows the page. If I manually type url.com/asdf it shows 404 template page.

url.com/index.php/testPathInfo shows success

general.php has

return array(
    '*' => array(
        'omitScriptNameInUrls' => 'true',
    ),
...

but when templates contain {{ url('about') }} or {{ entry.url }} I get url.com/index.php?p=whatever-this-is-supposed-to-be

I'm probably missing something easy and obvious but I've been staring at it all morning. Could it still be the mod_rewrite enabled thing even though it's rewriting fine?

.htaccess content

<IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteBase /

   # Send would-be 404 requests to Craft
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
   RewriteRule (.+) index.php?p=$1 [QSA,L]
</IfModule>

AuthType Basic
AuthName "*DEV*"
AuthUserFile /home/username/public_html/dev/.htpasswd
Require valid-user

OMG I'm so angry at myself!

Not 'omitScriptNameInUrls' => 'true',

but 'omitScriptNameInUrls' => true,


How sell products with Craft and secure the product viewing pages? I have a client that wants to sell products (digital content) that are only viewable in templates. These are not PDFs. The template page will have content on it that is only available to people that purchased the product. This is not a subscription, but a product sale with unlimited viewing after purchase via a secure login area.

What would be a good Ecommerce option for this project as there could be multiple products purchased at once? How can I make it so only validated purchasers can see the template content of the products purchased? Snipcart looks cool but no idea how to secure the templates for valid purchasers. Any better way to do this?

Thanks

Bryan

I can only really comment on securing content as I haven't used any ecommerce solutions with craft. But as far as that is concerned — one approach would be to add a related entries field-type on the users' field layout (called 'purchasedProducts'). When a user purchases a product you can add that entry to their purchased products via a plugin. You can then check the current entry against the related entries to determine if the user is authorized to view the content.

{% set authorized = false %}
{% if currentUser %}
    {% if entry.id in currentUser.purchasedProducts.ids() %}
        {% set authorized = true %}
    {% endfor %}
{% endif %}

{% if not authorized %}

    {% redirect 403 %}

    // or display product teaser and purchase button

{{ product.title }}

{{ product.description }}

    Purchase

{% else %}

     {# output entry #}

{% endif %}

To create an index page of all purchased entries, you can loop through the currentUsers purchased entries.

{% if currentUser %}

        {% for entry in currentUser.purchasedProducts %}

{{ entry.title }}

        {% else %}

You have not purchased any products.

        {% endfor %}

{% endif %}

To show all products and add a 'purchase' or 'view' button depending on whether they've purchased the product or not, you can check if the entry is in a 'purchasedProductsIds' array.

{% set purchasedProductsIds = [] %}
{% if currentUser %}
    {% set purchasedProductsIds = currentUser.purchasedProducts.ids() %}
{% endif %}

    {% for product in craft.entries.section('products') %}

{{ product.title }}

{{ product.description }}

            {% if product.id in purchasedProductsIds %}
                View
            {% else %}
                Purchase
            {% endif %}
    {% endfor %}

Obviously you will also need to add a small plugin to respond to the purchase process (via a hook or event) and update the user's purchasedProducts. Check out usersService saveUser method for more info or search this site for many examples.

As far as ecommerce solutions, I haven't used any with craft so I can't really comment. However, I did find a tutorial on managing your craft inventory with snip cart, which should describe everything you need to create the webhook.


How to add Image or any field on Calendars section for Solspace Calendar? How to add Image or any field on Calendars section for Solspace Calendar?

You'd do it from the Calendar's field layout page (https://solspace.com/craft/calendar/docs/calendars/), just like Craft entries, users, categories, etc.


Validation not running on empty custom fields on an entry (when creating programmatically) I'm trying to save an entry via a controller. They are contact form submission. A few of the fields are required.
    $section = Craft::$app->sections->getSectionByHandle('contactSubmissions');

    $entry = new Entry();
    $entry->sectionId = $section->id;
    $entry->typeId = 2;
    $entry->authorId = 1;
    $entry->enabled = true;

    $entry->setFieldValues([
        'contactName' => ucwords($data['contactName']),
        'contactEmail' => $data['contactEmail'],
        'contactCompany' => $data['contactCompany'],
        'contactMessage' => $data['contactMessage'],
        'contactHeardAbout' => $data['contactHeardAbout'],
        'contactWebsite' => $data['contactWebsite'],
        'contactTitle' => $data['contactTitle'],
    ]);

    if ( Craft::$app->elements->saveElement($entry) ) {
        //$this->sendMail($entry);
    }

    die(var_dump($entry->getErrors()));

    return $entry;

The only validation that runs if the missing title, which in this case is the contactName. All other keys in the $data array are empty strings. They do not trigger any validation and the empty entry saves into the database.
If I were to go into the control panel and try to save the entry, it would fail with the validation errors.
If I were to enter a value for the email field, but have an invalid email, that validation then gets triggered. But none of the empty fields do.

Additional info
Craft version: 3.6.10
PHP version: 7.3.13

You would need to set the entry's scenario to live just before saving the element.
$section = Craft::$app->sections->getSectionByHandle('contactSubmissions');

$entry = new Entry();
$entry->sectionId = $section->id;
$entry->typeId = 2;
$entry->authorId = 1;
$entry->enabled = true;

$entry->setFieldValues([
    'contactName' => ucwords($data['contactName']),
    'contactEmail' => $data['contactEmail'],
    'contactCompany' => $data['contactCompany'],
    'contactMessage' => $data['contactMessage'],
    'contactHeardAbout' => $data['contactHeardAbout'],
    'contactWebsite' => $data['contactWebsite'],
    'contactTitle' => $data['contactTitle'],
]);

$entry->setScenario(Entry::SCENARIO_LIVE);

if ( Craft::$app->elements->saveElement($entry) ) {
    //$this->sendMail($entry);
}

die(var_dump($entry->getErrors()));

return $entry;

Alternatively, If you want to save entries from the front-end, unless you have very specific requirements, you could use:

Guest entries plugin (if users are not logged in)
Make a front-end entry form (if users are logged in)


Nesting level too deep when looping through matrix blocks I'm running into a nesting level error when looping through matrix blocks, and I can't quite figure out what the problem is.

The problem arises when I use a variable to track the type of the previous element in the matrix. I want to be able to know within the sub-template that an element is the nth of the same type (for adding a container around some matrix block types to group them).

If I replace line 7 below with, eg. {% set n = (element.type == 'product') ? n + 1 : 0 %} (ie. a named element type rather than a variable) it works as expected, but as soon as I compare element.type to prevElementType I get a Fatal error: Nesting level too deep - recursive dependency? error. Some of the partial templates that may be included in the loop check prevElementType, but none of them modify it, so I can't see what's causing the recursion. I'd be grateful for any help with this.

{% if entry.contentBlocks is not empty %}
  {% set prevElementType = false %}

  {% for element in entry.contentBlocks %}

  {# Let's use a counter for multiple blocks of the same type: #}
  {% set n = (element.type == prevElementType) ? n + 1 : 1 %}

  {# Include the partial for this block: #}
  {% include "_pageelements/" ~ element.type|lower %}

  {# Track this element's type for the next iteration: #}
  {% set prevElementType = element.type %}

  {% endfor %}
{% endif %}

element.type is not a simple string (add {{dump(element.type)}} to your loop to see this). What you should use is element.type.handle. So the assignment is:

{% set prevElementType = element.type.handle %}

And the comparison (now of two strings, so no recursion) is:

element.type.handle == prevElementType


Combine .htaccess. ( http to https AND www to non www AND remove trailing slash ) extending normal craftcms .htaccess I can't manage to combine all above options. Tested with several options and examples only the most examples focusses only on on problem.

The example of .htaccess below is okay but i cannot manage to add the trailing slash combining with www and the http.

Testing with: https://htaccess.madewithlove.be/

SEO company want's to get rid of trailing slash. I see that most of CraftCMS installs responds to slash AND non slash.

See : craftcms website pricing and pricing/

are also both okay....thats also not ideal while you can introduce duplicate pages for google.

Recap:

http://www.domainname.nl  -> https://domainname.nl
http://www.domainname.nl/products/ -> https://domainname.nl/products (without trailing slash)

https://domainname.nl/products/      https://domainname.nl/products (without trailing slash)

    RewriteEngine On

# Redirect www to non www
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

# Redirect http to https
RewriteCond %{HTTPS} !on

RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Send would-be 404 requests to Craft
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
RewriteRule (.+) index.php?p=$1 [QSA,L]

Update 12/06/2018:

Tested now succesfully following version on my server

# Redirect www to non www
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

# Redirect http to https
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

# redirect /bla/ to /bla
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]


getting active userid by GraphQL? I build a SPA with Craft CMS and Vue.
I use Vue Router and have three Props. Here is my current router-view:

<router-view
  csrfName="{{craft.app.config.general.csrfTokenName}}"
  csrfToken="{{craft.app.request.csrfToken}}"
  userID="{{currentUser.id}}"
/>

I build a user form which works perfect, but it would be great to get the loggedIn userID in the response or via GraphQL. With GraphQL we would have the advantage to check on every site if the user is still signed in

My log-in template looks the same like above but without the UserID. At the moment I make a hard redirect to my application with the userID because I can't get the data from GraphQL. That means I need to reload the whole page just to get the loggedIn userID. This isn't best practice.

Have someone a idea how I could solve this?

with a get request from /index.php?p=admin/actions/users/session-info&dontExtendSession=1 I can get my CSRF Code + the User ID. Great.


Search within Matrix Field I know there are some similar questions out there but none of the solutions suits my needs.

What I'm trying to do is to display some related entries (from another section) within my blog posts.

Here is what I have right now. Not working (obviously, it's me :))

{% for entry in craft.entries %}
    {% for block in entry.matrix %}
        {% if block.type == "body" %}

                {% for recipe in craft.entries.section('recipes').search('block.body:pizza').order('score') %}
                    {{ recipe.title }}
                {% endfor %}

        {% endif %}
    {% endfor %}
{% endfor %}

Please advise.

From your code example, it looks like you have several potential issues.  I'll outline them here and might need to revise this answer after you provide an update on how these things pan out.

It's unclear if you are trying to do this on an individual blog post page that has the entry variable already available to the template or if you are trying to do something more complex. If this is individual blog page as you mention in your comments the entry variable should already be available for you and you may need to remove the for loop ({% for entry in craft.entries %}) you have wrapped around your Matrix field code which is trying to loop through all your blog entries. On an individual entry page in a Section with URLs enabled, you can access your matrix field directly using entry.matrixHandle.

{# You can probably remove this wrapping for loop #}
{% for entry in craft.entries %}

  {# You can start looping through your matrix here #}
  {% for block in entry.matrix %}

    {# This is good, you want to make sure you are within the
       block you are trying to access #}
    {% if block.type == "body" %}

      {# I've moved your SEARCH code below so we can focus on it a bit more easily. We'll put it back here afterwards. #}

    {% endif %}
  {% endfor %}

{# You can probably remove this wrapping endfor loop #}
{% endfor %}

It looks like you also may have the wrong syntax or approach for your search query (.search('block.body:pizza')).  You're right that you can dynamically pass content to the search parameter within your matrix loop but:

You are trying to pass data from a Rich Text field which will not have a single word, but a whole bunch of words and not make for a very specific search.
Your syntax is incorrect.

Try simplifying your search to something hard coded that you know exists to get this to work.

craft.entries.section('recipes').search('pizza')

If that works, they you need to sort out how to pass a more specific dynamic variable.  I don't think this is going to work from your body field, but it could work from a more targeted field.

craft.entries.section('recipes').search(block.keywordField)

In this second example, I'm passing the variable directly, not wrapping it in quotation marks.  And I'm targeting a field called 'keywordField' that just has the word I want to search for not a big paragraph of text.

Alternatively, you may not be trying to search for a keyword available in your Matrix, and you may just be trying to find related content in your Recipes section to your blog post using a Entries Relations Field.  If this is the case, you may not need the search parameter at all, but relatedTo instead.

craft.entries.section('recipes').relatedTo(entry)

In the end, you're loop may look something like this:

{% for block in entry.matrix %}

  {% if block.type == "keywordField" %}

    {# Using a more targeted keyword field for the search #}
    {% for recipe in craft.entries.section('recipes').search(block.keywordField).order('score') %}
        {{ recipe.title }}
    {% endfor %}

  {% endif %}

  {% if block.type == "body" %}

    {# Pulling in entries from an Entries Relations field #}
    {% for recipe in craft.entries.section('recipes').relatedTo(entry) %}
        {{ recipe.title }}
    {% endfor %}

  {% endif %}

{% endfor %}


With Element API, can I combine two entry types into a JSON object, keyed by type? I need to return two types of entry to an external service, and am trying to do so in one endpoint.

I am able to return all of the instances of each type, but it returns an array of mixed types.

"api/myEndpoint" => function() {
        return [
            "criteria" => [
                "type" => ["typeOne", "typeTwo"],
            ],
            "transformer" => new MyTransformer()
        ];
    }

I'm trying to achieve: { "data": { "typeOne": [{...}, {...}], "typeTwo": [{...}, {...}]}}

I can think of various workarounds for this specific case. It would be easy enough to add a type field within the Transformer, and sort them on the other side. Or, simply make two calls. However, our team was planning on using Craft as a headless CMS for future projects, and it's important to me  be able to control the structure of Element API's JSON response.

I'm using Craft as a headless CMS, so I wanted full control over the JSON it was returning. I didn't figure out how to do this with Element API, but it's very simple to write your own plugin that turns entries into JSON. Craft already includes the code to look up entries, so it's easy.

A basic controller might look something like:

class MyCustomPlugin_SomeResourceController {

    public function actionWidgets() {
        $criteria = craft()->elements->getCriteria(ElementType::Entry);
        $widgetEntries = $criteria->section("resources")->type("widget")->find();

        // map entries to PHP objects
        $widgetObjects = array_map(function($entry) {
            return [
                "name" => $entry->title
            ];
        }, $widgetEntries);

        $this->returnJson([
            "widgets" => $widgetObjects,
            "anotherThing" => "somethingElse"
        ]);
    }

}

In the actual app, I'm combining several different types of entries into one endpoint, so I extracted the reading and formatting into services, which I combine in various controller actions.

Presumably, Element API has some other features that I'm missing out on, but I haven't noticed.


Excluding properties from returned ElementModel in plugin We are building a React application for a client, where we are using Craft as the API/Backend. I am building the API as a custom Craft plugin, which will return specific JSON data to React, dependent upon the action route it requests.

I was wondering if there was a way that anyone has managed to prevent the default Entry properties from being included in the response, as it just adds a lot of unnecessary clutter. I have included a visual example at the bottom of this question to further clarify what I mean.

I did try to research if this was answered already, and found this link Ignore fields in search index
I did see it was back in 2016, so was hoping something has changed.

Example:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = "journeys";
$journeys = $criteria->find();

$responseObj = new \stdClass;
$responseObj->journeyOne = $journeys[0];
$this->returnJson($responseObj);

I literally won't be using any of these, as I only require the data from my custom fields(which haven't been created yet).

Unfortunately this is one of Crafts disadvantages, you'll always fetch all fields in your craft_content table no matter if you need those or not, thus it's always a bit slow and query heavy.

Craft 3 introduces a way to solve that issue since it loops the field layout and only fetches the values you need and not all of them.

Anyway: you could do this:

// get an array of all fields in craft_content (note: that only works for fields
// with a record in `craft_content` so you won't fetch relations with that
$attributes = $journeys[0]->getContent()->getAttributes();
$this->returnJson([
    'title'                 => $journeys[0]->getTitle(),
    'myCustomField'         => $attributes['myCustomField'],
    'myAwesomeField'        => $attributes['myAwesomeField'],
    'anotherField'          => $attributes['anotherField'],
    'idsOfARelationField'   => $journeys[0]->relationFieldHandle->ids()
]);

That way you can define which values you need and return them in an array. If you don't need all the other values just leave them empty. If you only want to fetch all custom fields you can use

$this->returnJson($attributes);

Not sure if you'll receive the title that way, you need to try. Othewise - if you need the title and if it's not included

$attributes['title'] = $journes[0]->getTitle();
$this->returnJson($attributes);

solves that.

Edit: by the way if you only need the first element you can do $criteria->first() instead of $criteria->find();


Neater way to merge this conditional entry query? I know there's a better way to do this - eg by merging in the relatedTo parameter if categorySlug is defined rather than defining 2 different queries, but I can't for the life of me figure it out... how would you simplify this?

{% if (categorySlug is defined) and (categorySlug != null) %}
   {# GET CONTENT FROM ONE CATEGORY #}
   {% set contentQuery = craft.entries
   .section(contentSection)
   .isFeatured('not 1')
   .relatedTo(craft.categories.slug(categorySlug))
%}
{% else %}
   {# GET ALL CONTENT #}
   {% set contentQuery = craft.entries
   .section(contentSection)
   .isFeatured('not 1')
   %}
{% endif %}

You can do this:

{% set query = craft.entries.section(contentSection).isFeatured('not 1') %}

{% if (categorySlug is defined) and (categorySlug != null) %}
   {# GET CONTENT FROM ONE CATEGORY #}
   {% set contentQuery = query.relatedTo(craft.categories.slug(categorySlug)).all() %}
{% else %}
   {# GET ALL CONTENT #}
   {% set contentQuery = query.all() %}
{% endif %}

Or if you want to be fancy ;)

{% set query = craft.entries.section(contentSection).isFeatured('not 1') %}

{% set contentQuery = ((categorySlug is defined) and (categorySlug != null)) ? query.relatedTo(craft.categories.slug(categorySlug)).all() : query.all() %}


Craft rich text field excerpt with slice and raw breaks when cut inside double tags I got a rich text field.
I made an excerpt with striptags slice and raw twig functions.
It works fine most of the time (raw close tags when slice cuts inside a
).

{{ textBlock.text | striptags("

") | slice(0, 275) | raw }}...

But sometimes it completely breaks and put plenty of empty tags anywhere :

I noticed that it happened when the original text is cut in the middle of double tags like :

Some text cut by slice function here

Real bug no ?

Raw doesn't close tags tags for you, it just allows them to be processed rather than printed only.

The only reason you are seeing closing tags I'm guessing is because your inspector (chrome/safari) is adding them in. Do "view source" to see the real output.

Your problem here is just rogue open tags


Use variable passed via include to set value of paramenter I have an include setup for Navigation which works fine if I hard code in the level of the structure I want to use. However I need this to be set differently if the nav appears in the head or the footer.

head:

      {% include "_includes/nav"
        with {
          'listType' : 'list-inline pull-right',
          'navLevel'  : '2'
        }
      %}

footer:

      {% include "_includes/nav"
        with {
          'listType' : 'list-unstyled',
          'navLevel'  : '1'
        }
      %}

The include looks like this where I want the .level('<=2') to have the value passed via the include. However when I attempt this .level('<=navLevel') or similar the entire nav does not render.

{% set navigation %}

 {% set pages = craft.entries.section('navigation').level('<=2') %}

   {% nav page in pages %}

       {# Check for entry type / get related entry #}
       {% if page.type == 'internalLink' %}
           {% set linkedEntry = page.sitePageLink.limit(1).first() %}
           {{ page.title }}
       {% endif %}

       {# Check for entry type / get offsiteLinks #}
       {% if page.type == 'offsiteLink' %}
           {{ page.title }}
       {% endif %}

       {% ifchildren %}
         {# dropdown not set up yet #}

              {% children %}

      {% endifchildren %}

   {% endnav %}

 {% endset %}

 {# replace data-uri with active class #}
 {{ navigation|replace({('data-url="' ~ craft.request.getUrl() ~ '"'): 'class="active"'})|raw }}

Please advise

You'll need to concatenate the string you pass to the level parameter, using the ~ operator:

{% set pages = craft.entries.section('navigation').level('<='~navLevel) %}


Combine entries query, but filter one section by field I would like to combine two sections: blog and recipes.

I need to filter the blog section by a field. I have an entries field on on Blog and I want to query those entries where the field is empty.

{% set posts = craft.entries({
   section: ['blog', 'recipes'],
   limit: 12,
   search:'-location:*'
}) %}

This will not return recipes. I think because the location field isn't present on recipes - only blog.

{% set blogIds = craft.entries({
   section: 'blog',
   search: '-location:*',
   limit: null
}).ids() %}

{% set recipeIds = craft.entries({
   section: 'recipes',
   limit: null
}).ids() %}

{% set entries = craft.entries({
   id: blogIds|merge(recipeIds),
   limit: 12,
})

Limit null is only there incase you're paginating, can be removed else.

This is the easiest way I've found for effectively combining element queries.


How Customizable is the Craft Admin Interface? Could a tool like AngularJS or VueJS or React help in significantly customizing the Craft admin interface? Could it help add additional functionalities?

For instance, let's take the example of adding external links to a post. Vox Media's Chorus lets writers save links in a browser bookmarklet (during their research process), and then pop it on the sidebar in their CMS admin interface when the writer clicks on the bookmarklet. The writer could now just select a phrase in his copy, and then click on a link in the sidebar and have it added to the text automatically. No selecting the text and then clicking on Add link and then copy-pasting the link. Lot of steps reduced.

The above cannot be done on Craft by default and in my research I haven't found a plugin that enables such a functionality.

Could the admin interface in Craft be altered to include such functions that aid in speeding up the editorial workflow?

There are some ways to customize the Craft Control Panel interface, but probably not to the extent that you are looking for.

The native way to do this would be through the use of the not-at-all documented template hooks similar to this.

You can search through the craft/app/templates folder for {% hook to get a full list of hooks available and the templates they live in.

For a bit more kludgy solution, you could use plugins like CP CSS and CP JS for injecting arbitrary CSS/JS into Control Panel requests.

Also worth noting this from the license agreement when dealing with plugins that modify Control Panel functionality:

  You may not write a plugin that breaks or tampers with any of Craft’s built-in functionality, including any restrictions related to license/edition enforcement, its core feature set, or its Premium Features.


How can I limit the rows returned in a table field? I want to limit the rows returned here to 4, then if loop.index is 3 display a separator and then output the last row.

Does the table field have that kind of control?

{% for row in registration.registrationCost %}

{{ row.cost }}

Through

{{ row.date }}

{% endfor %}

According to the Craft docs , the table field returns an array of rows.

Assuming that is the case, you should be able to use the Twig slice filter:

{% for row in registration.registrationCost | slice(0, 3) %}

You can then use a simple conditional to output the row divider:

{% if loop.index == 3 %}
    <hr>
{% endif %}

Stephen


Force download a file on page load I have a page that is supposed to render the content and then force a download of a file.

I have {% header "Location: " ~ entry.files[0].url %} in my template which brings up the browser's save dialogue window, but then the page doesn't render.

Is there a way I can delay the setting of the {% header %} tag until after the page has rendered? Or is there a better way of doing what I'm trying to achieve?

The {% header %} tag probably isn't what you want then - they must be rendered first, as they affect what the webserver is returning to the browser in the page headers. Because you're setting the location header to the file url, it is telling the browser to redirect to that instead of rendering the rest of the page (which would still technically be rendered by the server, just never seen by the browser).

It sounds like the better option would be to trigger the redirect to the file location via javascript after the page loads, similar to this approach.


How do I check and indicate that a product is already in the cart? Is it possible to show the complete products list, but indicate which products are already in the cart? What's the conditional?

This StackExchange question explains how to set an array of IDs for items in your cart, and then compare those IDs against the IDs of a list of products: Check if cart contains valid products for discount code


Pagination Slug Issue I've setup a 'posts' section with a collection of posts. I'm trying to add pagination to the individual post entry.

I've copied the pagination code from the Craft CMS documentation but I'm having issues with the slug.

The problem is my slugs are like: posts/post-1, posts/post-2, posts/post-3 etc.

And when you click on the 'previous' or 'next' post the slug is like 'post-3/p3' which the 'p3' on the end.

How do I convert that to my normal post slug of just 'post-3'.

Thanks!

Here is my code:

{% set query = craft.entries()
        .section('posts')
        .limit(1) %}

{% paginate query as pageInfo, pageEntries %}

        {% if pageInfo.prevUrl %}
            Previous Page
        {% endif %}

        {% if pageInfo.nextUrl %}
            Next Page
        {% endif %}

Pagination is for breaking up a list of entries into different pages. p3 means page 3 of entries.

What you're looking for is the ability to link to the next/previous entry. If you are ordering your entries like a blog, by post date, newest first you can create the links like this. On your single entry template:

{% set nextEntry = craft.entries.section('posts')
    .postDate('>= ' ~ entry.postDate.timestamp)
    .order('postDate asc')
    .id('not ' ~ entry.id)
    .one() %}

{% if nextEntry %}
    Next Post
{% endif %}

{% set previousEntry = craft.entries.section('news')
     .postDate('<= ' ~ entry.postDate.timestamp)
     .order('postDate desc')
     .id('not ' ~ entry.id)
     .one() %}

{% if previousEntry %}
    Previous Post
{% endif %}

entry is the current entry you're viewing. If you're ordering the entries in a different way you can adjust the queries. This answer goes into more detail, including how to change this if you're ordering by title.

If you have your entries in a structure section you can do this:

{% if entry.getNextSibling() %}
    Previous Post
{% endif %}

{% if entry.getPrevSibling() %}
    Previous Post
{% endif %}


.htaccess for Craft CMS - removing segment from URL I'm rebuilding a website which needs to run Craft CMS as the main frontend and a bespoke CRM system at the same domain.

The file setup is basically

public_html

cms
crm

To make things a bit more complicated, certain parts of the CRM have to be publically accessible, so there are a few different URL structures that need to run from the CRM, and the rest from Craft. Also, the images/scripts are in the cms folder, so I need to get those to work without cms in the URL.

For the most part this is working lovely, but I can't get force /cms/ to be removed from the URL, so whilst the site works fine without it, it's live on 2 different URLs.

I have the  with the following code:

<IfModule mod_rewrite.c>

    # Force the root to match slug of home page
    # This is the only way I can get the home page to
    # show on the root, as it's within a structure
    RewriteRule ^$ /__home__

    # Get rid of /cms (this doesn't work)
    RewriteRule ^cms/?(.+)$ /$1 [R=301]

    # Sort out frontend files
    RewriteCond %{REQUEST_URI} /assets/.*
    RewriteCond %{REQUEST_URI} !/assets/admin/.*
    RewriteCond %{REQUEST_URI} !/cms/.*
    RewriteCond %{REQUEST_URI} !/crm/web/.*
    RewriteRule ^(.*\.(gif|jpg|jpeg|bmp|svg|png|css|js|json|pdf|eot|woff|woff2|ttf|otf))$ /cms/$1 [QSA,L]

    # Sort out admin files
    RewriteCond %{REQUEST_URI} /assets/admin/.*
    RewriteCond %{REQUEST_URI} !/cms/.*
    RewriteCond %{REQUEST_URI} !/crm/web/.*
    RewriteRule ^(.*\.(gif|jpg|jpeg|bmp|svg|png|css|js|json|pdf|eot|woff|woff2|ttf|otf))$ /crm/web/$1 [QSA,L]

    # Send would-be 404 requests to Craft
    # Need to exclude any that should route to the CRM
    # Any that we exclude, we then need to route below
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
    RewriteCond %{REQUEST_URI} !^/manager/?(.*)$ [NC]
    RewriteCond %{REQUEST_URI} !^/forms/(.+)$ [NC]
    RewriteCond %{REQUEST_URI} !^/partners/?(.*)$ [NC]
    RewriteCond %{REQUEST_URI} !^$ [NC]
    RewriteRule (.+) /cms/index.php/$1 [QSA,L]

    # CRM Admin
    RewriteCond %{REQUEST_URI} ^/manager/?(.*)$ [NC]
    RewriteRule ^manager/?(.*) /crm/web/app.php [QSA,L]

    # Quote Forms
    RewriteCond %{REQUEST_URI} ^/forms/(.+)$ [NC]
    RewriteRule ^forms/(.+) /crm/web/app.php [QSA,L]

    # Signup Forms
    RewriteCond %{REQUEST_URI} ^/partners/?(.*)$ [NC]
    RewriteRule ^partners/?(.*) /crm/web/app.php [QSA,L]

</IfModule>

So I'm fairly sure that the line RewriteRule ^cms/?(.+)$ /$1 [R=301] would work, but the default Craft logic is somehow preventing it - if I comment out the default craft section, then it removes cms from the URLs, but obviously nothing is routed through Craft, so 404s all round.

I'm completely stumped at this point, so any help would be appreciated!

I might be way off here, but if you are wanting to run Craft as your main front-end, could you not place the Craft index.php in the root of the site and then you CRM in a sub-folder of its own?

If you have all you CRM stuff in its own sub-folder, Craft should just ignore that folder as it physically exists. If you've got specific things you need to have in your .htaccess file for you CRM, you could have that .htaccess in the sub-folder, rather than the root. That way you can just have the standard Craft .htaccess file in the root of the site.


Unable to run console commands (Craft 3, MAMP) This was asked and solved previously, but for Craft 2, see Error Connecting To (MAMP Pro) Database in Console Command. Unable to make it work here.

In short, running console commands like ./craft abc in Craft 3 configured using MAMP gives

Craft isn’t installed yet!

Exception 'craft\errors\DbConnectException' with message 'Craft CMS can’t connect to the database with the credentials in config/db.php.'

in /Users/stian/dev/site.no/vendor/craftcms/cms/src/db/Connection.php:179

Stack trace:
#0 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/Connection.php(996): craft\db\Connection->open()
#1 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/Connection.php(983): yii\db\Connection->getMasterPdo()
#2 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/mysql/QueryBuilder.php(380): yii\db\Connection->getSlavePdo()
#3 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/mysql/QueryBuilder.php(399): yii\db\mysql\QueryBuilder->supportsFractionalSeconds()
#4 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/mysql/QueryBuilder.php(57): yii\db\mysql\QueryBuilder->defaultTimeTypeMap()
#5 /Users/stian/dev/site.no/vendor/craftcms/cms/src/db/mysql/QueryBuilder.php(27): yii\db\mysql\QueryBuilder->init()
#6 /Users/stian/dev/site.no/vendor/yiisoft/yii2/base/BaseObject.php(109): craft\db\mysql\QueryBuilder->init()
#7 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/QueryBuilder.php(116): yii\base\BaseObject->__construct(Array)
#8 /Users/stian/dev/site.no/vendor/craftcms/cms/src/db/mysql/Schema.php(71): yii\db\QueryBuilder->__construct(Object(craft\db\Connection), Array)
#9 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/Schema.php(249): craft\db\mysql\Schema->createQueryBuilder()
#10 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/Connection.php(836): yii\db\Schema->getQueryBuilder()
#11 /Users/stian/dev/site.no/vendor/yiisoft/yii2/db/Command.php(580): yii\db\Connection->getQueryBuilder()
#12 /Users/stian/dev/site.no/vendor/craftcms/cms/src/db/Command.php(142): yii\db\Command->update('{{%queue}}', Array, '[[timeUpdated]]...', Array)
#13 /Users/stian/dev/site.no/vendor/craftcms/cms/src/queue/Queue.php(507): craft\db\Command->update('{{%queue}}', Array, '[[timeUpdated]]...', Array, false)
#14 /Users/stian/dev/site.no/vendor/craftcms/cms/src/queue/Queue.php(449): craft\queue\Queue->_moveExpired()
#15 /Users/stian/dev/site.no/vendor/craftcms/cms/src/queue/Queue.php(95): craft\queue\Queue->reserve()
#16 /Users/stian/dev/site.no/vendor/craftcms/cms/src/queue/Command.php(82): craft\queue\Queue->run()
#17 [internal function]: craft\queue\Command->actionRun()
#18 /Users/stian/dev/site.no/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#19 /Users/stian/dev/site.no/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#20 /Users/stian/dev/site.no/vendor/yiisoft/yii2/console/Controller.php(148): yii\base\Controller->runAction('run', Array)
#21 /Users/stian/dev/site.no/vendor/yiisoft/yii2/base/Module.php(528): yii\console\Controller->runAction('run', Array)
#22 /Users/stian/dev/site.no/vendor/yiisoft/yii2/console/Application.php(180): yii\base\Module->runAction('queue/run', Array)
#23 /Users/stian/dev/site.no/vendor/craftcms/cms/src/console/Application.php(93): yii\console\Application->runAction('queue/run', Array)
#24 /Users/stian/dev/site.no/vendor/yiisoft/yii2/console/Application.php(147): craft\console\Application->runAction('queue/run', Array)
#25 /Users/stian/dev/site.no/vendor/yiisoft/yii2/base/Application.php(386): yii\console\Application->handleRequest(Object(craft\console\Request))
#26 /Users/stian/dev/site.no/craft(22): yii\base\Application->run()
#27 {main}

Ideas?

You might not have MAMP's PHP set as your CLI PHP. Have a look at this guide and see if it gets you further


How can I globally set variables? Originally at Include a template that sets variables (global template variables).

I have a number of templates and layouts but every layout includes a global header in which the meta data is displayed.

That data always comes from the entry you are viewing and currently I'm setting the variables the global header needs in each template, such as /news/index.html:

{% extends "_listing_layout" %}

{% set metaTitle = (entry.metaTitle) ? entry.metaTitle : entry.title %}
{% set metaDescription = entry.metaDescription %}
{% set metaKeywords = entry.metaKeywords %}
{% set metaRobots = entry.metaRobots %}
{% set openGraphImage = entry.openGraphImage.first() %}

{% if openGraphImage %}

    {% set ogCrop = {
        mode: 'crop',
        width: 1200,
        height: 630,
        quality: 75,
        position: 'center'
    } %}

    {% set openGraphImage = openGraphImage.getUrl(ogCrop) %}

{% endif %}

{% set listingSection = "newsArticles" %}

{% block content %}

    <h1>{{ entry.title }}</h1>

    {% include '_partials/_isotope.html' %}

{% endblock %}

The layout is super simple:

{% include '_partials/_global_header.html' %}

    {% block content %}{% endblock %}

{% if craft.request.firstSegment %}

    {% include '_partials/_global_footer.html' %}

{% else %}

    {% include '_partials/_home_footer.html' %}

{% endif %}

And then the global header partial:

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html lang="en-gb" dir="ltr" class="no-js"> <!--<![endif]-->

    <head>

        {# meta #}
        <meta charset="utf-8">
        <title>{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}</title>

        {# seo #}
        <meta name="description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}">
        <meta name="keywords" content="{% if metaKeywords is defined %}{{ metaKeywords }}{% endif %}">
        <link rel="canonical" href="{{ siteUrl }}">
        <meta name="robots" content="{% if metaRobots is defined %}{{ metaRobots }}{% else %}index,follow{% endif %}">

        {# open graph #}
        <meta property="og:site_name" content="{{ siteName }}">
        <meta property="og:type" content="website">
        <meta property="og:title" content="{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}">
        <meta property="og:url" content="{{ siteUrl }}">
        <meta property="og:image" content="{% if openGraphImage is defined %}{{ openGraphImage }}{% else %}{{ siteUrl }}images/og-default.jpg{% endif %}">
        <meta property="og:description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}">

...etc

How can I keep this variable code DRY? I've tried putting direct in the header partial and layouts, but that doesn't work. And putting it in an included partial within each template throws a template error on account of the fact the template extends a layout and can't have a body.

There are two basic issues with using includes in this scenario, both of which you have discovered.

The first is that included templates do not have access to variables defined in master templates. You would have to specifically pass the variables to the template in the include statement using the with clause.

The second is that extended templates cannot have a body outside of a declaration block, and includes are by definition expected to return html, and thus throw an error.

A better solution would be to use hierarchical extensions vs includes, placing all shared content together in the parent template. All variables in child templates should bubble up through the template hierarchy and be available to the parent templates. Something like the following for example (I've left out much of the content for brevity).

In _layout.html:

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html lang="en-gb" dir="ltr" class="no-js"> <!--<![endif]-->

<head>
    {# meta #}
    <meta charset="utf-8">
    <title>{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}</title>

    {# set seo #}
    <meta name="description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}">
    <meta name="keywords" content="{% if metaKeywords is defined %}{{ metaKeywords }}{% endif %}">
    <link rel="canonical" href="{{ siteUrl }}">
    <meta name="robots" content="{% if metaRobots is defined %}{{ metaRobots }}{% else %}index,follow{% endif %}">

    {# set open graph #}
    <meta property="og:site_name" content="{{ siteName }}">
    <meta property="og:type" content="website">
    <meta property="og:title" content="{% if metaTitle is defined %}{{ metaTitle }} - {% endif %}{{ siteName }}">
    <meta property="og:url" content="{{ siteUrl }}">
    <meta property="og:image" content="{% if openGraphImage is defined %}{{ openGraphImage }}{% else %}{{ siteUrl }}images/og-default.jpg{% endif %}">
    <meta property="og:description" content="{% if metaDescription is defined %}{{ metaDescription }}{% endif %}">

    ...etc

    {# set default site meta variables, if any #}

    {% block head %}{% endblock %}

</head>

<body>

    {% block content %}{% endblock %}

    {% block footer %}{% endblock %}

</body>

</html>

In _home-layout.html:

{% extends "_layout" %}

{% block head %}
    {# additional home head content #}
{% endblock %}

{% block content %}{% endblock %}

{% block footer %}
    {# home footer content #}
{% endblock %}

In _page-layout.html:

{% extends "_layout" %}

{% block head %}
    {# additional page head content #}
{% endblock %}

{% block content %}{% endblock %}

{% block footer %}
    {# page footer content #}
{% endblock %}

In your index.html template:

{% extends "_home-layout" %}

{# set home meta variables, etc #}

{% block content %}
    {# home body content #}
{% endblock %}

In your page.html template:

{% extends "_page-layout" %}

{# set page meta variables, etc #}

{% block content %}
    {# page body content #}
{% endblock %}

In this way you can group shared content in the parent template and specific content further down the template hierarchy. How best to structure this hierarchy to keep you code 'dry' will be heavily dependent on the needs of your project so I won't even begin to guess.

Keep in mind it is also possible to define blocks of html in variables using conditionals and then place those variables into the block declarations.

{% if craft.request.firstSegment %}

    {% set footer %}
        {# page footer content #}
    {% endset %}

{% else %}

    {% set footer %}
        {# home footer content #}
    {% endset %}

{% endif %}

{% block footer %}
    {{ footer }}
{% endblock %}


How to limit cache folder size My runtime/cache folder is over 200GB in size. Is there a config setting or some way of limiting the size of the folder, rather than the cache duration?

Secondly, what could cause such disk usage?

There's not a native Craft way to do that.

You could setup a cron job that hit a plugin's controller and delete items in cache based on size/date created, etc.

My guess is you've got a plugin installed doing some heavy caching. You can try opening the largest cache files (it's all just serialized text data) and see if the type of data gives you a clue to where it's coming from.


Passing JSON to Twig I'm creating a multilingual site and I'm looking to pass some additional configuration data to Twig so I can use it in my template. For example, suppose I have a configuration like this:

{
  "serviceName":        "arabic",
  "language":           "ar",
  "textDirection":      "rtl",
  "socialMediaButtons": ["twitter", "facebook"],
  ...
}

I'd like to use this data to set the dir attribute in my template:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ craft.locale }}" dir="{{ config.textDirection }}">
<head>
  ...
</head>

Is there an easy way to do this?

My project structure looks like this:

Where each locale directory has a config file.

Thanks!

The answer is to create a custom plugin that will read the json files from the folders in the public directory, parse the json, and pull them into the template.

An alternative solution is to use Craft custom config files. (Not the answer you're looking for but would work very well.)

In craft/config create a file like localedata.php and make it something like this:

<?php

/**
 * Return an array indexed by locale code
 */
return [
    'ar' => [
        'serviceName' => 'arabic',
        'language' => 'ar',
        'textDirection' => 'rtl',
        'socialMediaButtons' => ['twitter', 'facebook'],
    ],
    'en' => [
        //...
    ],
    // etc
];

Then in your template you could do something like this:

{#
    If the current locale is `ar` (Arabic) then this will set `localeData`
    from the array index "ar" in the config file
#}
{% set localeData = craft.config.get(craft.locale, 'localedata') %}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ craft.locale }}" dir="{{ localeData.textDirection }}">
<head>
  ...
</head>


Show related entries that have the same Category, and some of the same tags? I have succesfully shown some related entries that are in the same category, using the following:

{% set relatedEntries = craft.entries({
    section: 'blog',
    relatedTo: entry.blogCategory.first(),
    id: 'not ' ~ entry.id,
    limit: 3
}) %}

(Entries generally only have 1 category on this site).

What I would like to do next, is look for entries that have some of the same  tags as the current entry, making this list of entries even more relevant.

Essentially I want the related entries to be as relevant as possible, this could even go as far as ignoring the Category if there are more tags in common, is that possible by somehow grading the results and displaying the most 'in-common' entries?

If you don't mind using a plugin, Similar would work very well for your use case:

{% set relatedEntries = craft.similar.find({
    element: entry,
    context: entry.tagsField.ids()|merge(entry.blogCategory.ids()),
    criteria: {
        section: 'blog',
        limit: 3
    }
}) %}

Of course, be sure to replace tagsField in the above example with the actual handle for your entry type's Tags field.

By default, the entries returned by Similar will be ordered after the number of relations in common with the current entry (or whatever you pass into the element parameter). In other words, the entry with the most tags and/or categories in common with the current entry will be the first. The entries returned by Similar will also have an entry.count attribute that will indicate the number of relations in common with the current entry.

If you want to do this without a plugin, it's possible to add multiple criteria to the relatedTo parameter in order to pull entries that has either one or more categories or tags in common with the current entry:

{% set relatedEntries = craft.entries({
    section: 'blog',
    relatedTo: ['or', {
        targetElement: entry.blogCategory.ids(),
        field: 'blogCategory'
    }, {
        targetElement: entry.tagsField.ids(),
        field: 'tagsField'
    }],
    id: 'not ' ~ entry.id,
    limit: 3
}) %}

I'm not aware of a way to query the EntriesService for the 3 entries with the most relations in common with entry, though (the lack of such a feature is why the Similar plugin exists). I guess this could be achieved in plain Twig by possibly setting the limit parameter to null (to return all entries – yikes! performance), looping over the returned entries and comparing the number of relations in entry.tagsField and entry.blogCategory to each related entry and add the entries to an array ordered by the amount of relations somehow, but I suspect it will get pretty dirty and quickly go beyond what Twig is designed to comfortably do, logic-wise.


Add product to cart using code in Craft Commerce plugin I want to add a specific product to a cart using its ID. I have created a module which runs on Order::EVENT_BEFORE_ADD_LINE_ITEM event. Here I am able to capture the current cart using the below code
$cart = craft\commerce\Plugin::getInstance()->getCarts()->getCart();

I can also get a Product using the below code
$product = \craft\commerce\elements\Product::find()->id([123]); // here 123 is product id

But I am not sure how to add this product to the existing cart using a code.

I managed to figure this out. Below is the code:
$theOrder = $event->sender;
$myProduct = \craft\commerce\elements\Product::find()->id([$giftProductId])->one();
if($myProduct){
    $myPurchasable = $myProduct->getDefaultVariant();
    $newOrExistingLineItem = \craft\commerce\Plugin::getInstance()->getLineItems()->resolveLineItem($theOrder->id, $myPurchasable->id, []);
    $theOrder->addLineItem($newOrExistingLineItem);
}


How can I get an entry based on category and slug (via url segments)? I am using (and have used) this S.O post to create a product filter/search using multiple categories. Everything is working perfectly.

I am attempting to use the same type of logic to grab ultimately a single entry.

TL;DR: I am trying to get a single entry via 2 url segments.

I believe I am close, but what I am generating for a query isn't quite spot on as I'm always getting into my "No Products" clause.

www.mysite.com/products/toys/my-awesome-toy

{% set category = craft.request.getSegment(2) %}  {# toys #}
{% set product = craft.request.getSegment(3) %}   {# my-awesome-toy #}

{% set relationParam = ['and'] %}

{% if category|length %}
    {% set cat = craft.categories.group('toys').slug(category).first() %}
    {% set relationParam = relationParam|merge([{ targetElement: cat }]) %}
{% endif %}

cat is outputting Toys and my page renders all entries that belong to the Toys category group. Perfect.

{% if product|length %}
    {% set prod = craft.entries.slug(craft.request.getSegment(3)).first() %}
    {% set relationParam = relationParam|merge([{ targetElement: prod }]) %}
{% endif %}

{% set entry = craft.entries.relatedTo(relationParam).first() %}

{% if entry %}
    {{ entry.title }}

{% else %}

No products found.

{% endif %}

prod is outputting My Awesome Toy and now I am getting my "No products found" message. I think it's because prod is outputting My Awesome Toy vs. my-awesome-toy?

I realize I can get any entry via it's slug alone. I am using a dynamic route to create this page that looks like this:

products/tag/slug

I would like to get the entry strictly based on the url segments that way I can make sure my urls are valid. Meaning, I can only find "My Awesome Toy" from the "toys" category. For example:

www.mysite.com/products/toys/my-awesome-toy   {# valid #}
www.mysite.com/products/food/my-awesome-toy   {# invalid #}

An entry is not related to itself, but that's what you added as a filter.
Use the slug parameter instead.

{% set categorySlug = craft.request.getSegment(2) %}
{% set productSlug = craft.request.getSegment(3) %}

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

{% if categorySlug|length %}
    {% set category = craft.categories.group('toys').slug(category).first() %}
    {% if category %}
        {% set entries = entries.relatedTo({ targetElement: category }) %}
    {% endif %}
{% endif %}

{% if productSlug|length %}
    {% set entries = entries.slug(productSlug) %}
{% endif %}

{% set product = entries.first() %}


Add batch filter as variable I want to add a batch filter with a variable number depending on a conditional. Is this even possible this way:

{% if category.id == '647' %}
    {% set nr = '5' %}
{% else %}
    {% set nr = '4' %}
{% endif %}

{% for batch in craft.entries.section('xy')|batch('{{ nr }}') %}
    {{ batch.title }}
{% endfor %}

This doesn't work. How should I write {{ nr }} or is it impossible due to things like parse order?

Thought I would add this as an answer instead as it should solve your issue.

I think the issue with writing it like batch('{{ nr }}') is that twig sees whatever is in '' as a string, so is seeing {{ nr }} as it is.

You also don't need to echo a variable as a function parameter i.e batch({{ nr }}), you just need to reference the variable itself.

So by just writing

{% for batch in craft.entries.section('xy')|batch(nr) %}
 {{ batch.title }}
{% endfor %

Twig will take the value for nr set above and use it as the parameter :)


Plugin to check for broken links? Is there a plugin, similar to https://wordpress.org/plugins/broken-link-checker/ or other Wordpress plugins, that can test links in the database and report which ones are broken? It doesn't have to be pretty or fancy.

nystudio107 has a great plugin that takes care of this and lets you implement redirects when broken links are found.

retour on Github


Migrating from Craft CMS to WordPress I have recently discovered our company website is run on Craft CMS. However, I do not like the system and am much more proficient on WordPress. Is there a way to move our site to WordPress without a huge bill or running/migration issues.

It’s likely possible but would be tricky, since Craft has much more flexible content modeling features than WP. It would be a “lossy” conversion process, similar to converting a WAV file to a low bitrate MP3.

The best way to get content out of Craft is probably to use the Element API plugin. I couldn’t tell you how to get that content into WP though – you may have better luck asking about that on https://wordpress.stackexchange.com/.

Before you go down this road, it’s probably worth spending just a bit of time learning Craft CMS. You never know, you might just end up liking it! Here are a few recent articles that compare the two:

Roller Agency: Why we are moving to Craft CMS
Trajectory: Craft vs Wordpress: The Definitive Guide
OneSignal: Lessons Learned Building Our Website With Craft CMS
Honcho Agency: Why We're Craft CMS Developers

If interested, I highly recommend you check out CraftQuest’s Up and Running with Craft 3 course, which is free.


Can a field type detect if it's being edited inline in Craft 2.3? Is there was a way for a field type to detect if it is being instantiated for the inline editor or even the live preview? I am working on some responsive UI's to make Google Maps for Craft work optimally on all screen sizes, but current smaller screens are posing a problem. Obviously media queries are being used, but was also wondering if I could easily detect where the field type was being instantiated and then I could possible add additional classes to my field type wrapper.

You could check if the whole request is Live Preview using craft.request.isLivePreview.


Get a brand list of products for Javascript filter I´m working on a website of veterinary products. Some of the product fields are "category" and "brand".

I have a product list page that shows a list of products of certain category, and I have a JS list to filter by "brand". So, what I need is to get a list of the brands from the products of this specific category ( without repeating ), so I can create the JS filter.

Any ideas? :)

Presumably you already have the code to retrieve the products you're after.

So just loop through your products and merge your brands in to an array.

If the brands are just strings, just make an array of the strings, otherwise if they are relations, grab the ids.

Something along these (untested) lines:

{% set brandStrings = [] %}
{% set brandIDs = [] %}

{% for product in products %}
    {# Either of these... #}
    {% set brandStrings = brandStrings|merge([product.brand]) %}
    {% set brandIDs = brandIDs|merge([product.brand.first().id]) %}
{% endfor %}

{# If strings, you've now got an array #}
{# If Ids, and you need to get the names, you can do something like... #}

{% set brands = craft.entries.id(brandIDs).find() %}

{% for brand in brands %}
    {% set brandStrings = brandStrings|merge([brand.title]) %}
{% endfor %}


Need a little help with basic understanding of how categories work I am trying to figure out how I can put categories to use, and I need some help.

I have created a category group called "group1", and 2 catogories inside called "cat1" and "cat2". and I have added the catogorie field to some articles in a structure so that I have a few linked to both cat1 and cat2.

My goal simply to make a forloop listing the catogorie title of both cat1 and cat2, and create an UL with the entry.title of the entries in each catogorie.

This is my code:

{% for cat in craft.categories.group('group1') %}

{{ cat.title }}

    {% for entry in craft.entries.section('structure').level(3).relatedTo(cat) %}

{{ entry.title }}

    {% endfor %}
{% endfor %}

This gives me the two catogorie titles, but no entry titles. I dont think I understand how to use the related to group part. Would really appreaciate some help figuring this one out.

Your code is only listing entries in the structure that are nested inside 2 parent entries. Try this:

{% for entry in craft.entries.section('structure').relatedTo(cat) %}

Also, your
 is inside your for loop, it should be:

    {% for entry in craft.entries.section('structure').relatedTo(cat) %}`

{{ entry.title }}

    {% endfor%}


Delete commerce line item with Ajax I am attempting to delete a commerce line item with ajax request but nothing ever deletes. Am i doing something wrong? Vue function below:

removeLine(id) {

            axios.post('', {
                    [this.csrfName]: this.csrfToken,
                    action: 'commerce/cart/update-cart',
                    lineItems: {[id]: {'remove': true}}
            },
            {
                headers: {
                    'X-CSRF-Token': this.csrfToken
                },
            }).then(function (response) {

            }).catch(function (error) {
                // handle error
                console.log(error);
            }).finally(function () {
                // always executed
            });
        }

Issue resolved. Having checked with Yii debug bar i could see that there was no $_post data with the request. It seems axios sends the data as JSON and not usual post data. Switching to vue-resource enabled me to post the data for craft to use, new code:

this.$http.post('', {
    [this.csrfName]: this.csrfToken,
    action: 'commerce/cart/update-cart',
    lineItems: {[id]: {'remove': 1}}
}, {
    emulateJSON: true,
    headers: {
        'X-CSRF-Token': this.csrfToken
    }
})


CSRF Protection in AJAX post I'm having some trouble passing CSRF-tokens in my js-File in Craft 3.

I'm setting the values in my template like so:

<script type="text/javascript">
    window.csrfTokenName = "{{ craft.app.config.general.csrfTokenName }}";
    window.csrfTokenValue = "{{ craft.app.request.csrfToken }}";
</script>

and call them in my .js-file:

var data = {id: 100 };
    data[window.csrfTokenName] = window.csrfTokenValue;
    $.ajax({
        type: 'POST',
        url: href,
        data: data,
        success: function(result){
        },
        error: function(){
        }
    });

before i set the csrf tokens, the server returned an error 400 – bad request – as you would expect.
now i'm getting a 500 internal server error.
What am I doing wrong?

The site works perfect without the ajax-call so I don't think there's an issue with my templates. Tell me if you need to see more of my code.

As Robin Schambach suggested I looked through my stack trace.
The request was fine but php couldn't find the template used on ajax-requests due to a misspelling error.

Fixed the misspelling – works like a charm.


Search across Entries and Assets and paginate combined results I need to search both entries and assets and paginate the combined result.

I have tried:

{% set entries = craft.entries.search(query|trim).order('score').status('pending, live').find() %}
{% set assets = craft.assets.source('documentLibrary').search(query).find() %}
{% set searchResults = entries | merge(assets) %}

But then I can't get paginate to work:

{% paginate searchResults as results %}

... brings up the following error:

  Argument 1 passed to Craft\TemplateHelper::paginateCriteria() must be an instance of Craft\ElementCriteriaModel, array given

If I don't use a .find(), I can't merge the two objects.

Any ideas how this can be achieved?

You're getting that error because as soon as you call find(), you're converting the ElementCriteriaModel into an array of actual fetched elements and the {% paginate %} tag requires an ElementCriteriaModel object to be passed in.

In order to pull off what you're looking for, you'd need to ability to search across multiple element types in one query, which is currently not something Craft supports.

Consider voting for this feature request, which would enable what you're trying to do.


How to configure Craft to use a Proxy? I have a Craft application in an intranet behind a proxy.
I'm struggling to find a way of configuring the application so it can access the internet via the proxy, in particular for the auto updating and license check.

I tried using PHP's stream_context_set_default() but cannot get it to work in the admin section. Here's what i did so far:

I created a connect.php file in my public folder just to check if my script works.
It does as i can get the headers for any site.

$PROXY_HOST = "url.to.my.proxy";
$PROXY_PORT = "8080";
$PROXY_USER = "username";
$PROXY_PASS = "password";

$auth = base64_encode("$PROXY_USER:$PROXY_PASS");
stream_context_set_default(
  array(
    'http' => array(
      'proxy' => "tcp://$PROXY_HOST:$PROXY_PORT",
      'request_fulluri' => true,
      'header' => "Proxy-Authorization: Basic $auth"
    )
  )
);

$url = "https://elliott.buildwithcraft.com/actions/elliott/app/ping";

print_r( get_headers($url) );

From there, i'm stuck. I have no idea what to do next to try and inject that into the auto updating part of Craft. I know it's bad to mess with the core, and don't want to, but i have no idea where to go from here.

Anyone been in a similar situation ?
Am i missing something obvious where all of this wouldn't be needed ? (hope so)

Following @BradBell answer i searched for Guzzle information and found this in the documentation for Guzzle 3.x:

  Requests options can be specified when creating a request or in the request.options parameter of a client. These options can control various aspects of a request including: headers to send, query string data, where the response should be downloaded, proxies, auth, etc.

$request = $client->get($url, $headers, array('proxy' => 'http://proxy.com'));

Maybe this can be put to some use in the current Craft install?
If so, some guidance will be greatly appreciated.

I'm not sure I've got a good answer for you here.

Craft 2.x uses Guzzle 3.x for all of its outbound communications, so I'm not sure if it would be respecting stream_context_set_default at all.

If I recall, the last time someone brought this up, we looked into it and decided we needed to update to the latest Guzzle (5.x) to properly support it (they release quickly).

Unfortunately both Guzzle 4.x and 5.x introduced breaking changes to Craft.  So it'll have to be Craft 3.x before we add outbound proxy connection configuration options to Craft.

The only workarounds I can think of until then are to whitelist the IP address(es) of the Craft install to allow direct outbound access if that's possible.  If you can't do that, then maybe you can just get away with manual updates and let license checking silently fail.


Change/update the default user profile photo/thumbnail folder location I'm having a hell of a time tracking where i can change/update the default user profile photo/thumbnail folder location. I keep reading and have tested that you can create a new volume for assets. My interest is is being able to locate and change the default folder location. Any advice?

In Craft 3 the place that users photos get stored in are proper Asset Volumes, meaning they can be stored in a folder on the local file system, or on S3, Google Cloud, or any other place that a plugin provides a Volume for.

Once you create a Volume for user photos, go to Settings->Users->Settings in the Control Panel and you can choose that Volume under the "User Photo Volume" settings.


Testing for Dropdown Value I've set up a dropdown field for the client to choose from a list of pre-defined addresses.  In my template, I want to determine whether the field has a value or not.  When I look in the database, Entry A has a value of 'NULL', while Entry B has a value of 'address1'.  So the field is working properly in those regards.

However, in my template it's all or nothing.  Here is my code:

{% if event.includeMap|length %}

        Defined? {{ event.includeMap is defined }}

        Not Null? {{ event.includeMap is not null }}

        Null: {{ event.includeMap is null }}

        Value: {{ event.includeMap }}

        {% include 'partials/_inc-map' %}

{% endif %}

I've added the options in the left column to see what the database returns.  In both cases, they return the same values.  However, with Entry A, nothing is returned as a value, and in Entry B, the correct value appears.

I have also tried

{% if event.includeMap is defined %}
{% if event.includeMap is not null %}

Any other thoughts?

If you've got a dropdown field defined with no value as one of the options, it should be as simple as:

{% if entry.dropdownFieldHandle %}
    {{ entry.dropdownFieldHandle }}
{% endif %}


Craft or Twig converting html tags to alt characters Just starting with plugin development. I modified the code from pluginfactory.io for a custom tag:

public function navHTML()
{
    $result = '
Hello again
';
    return $result;
}

and I am getting output. The issue is the html tags are being converted to alt characters. Here is the actual source of my index page:

&lt;h2&gt;Testing&lt;/h2&gt;

IT looks like Craft or Twig is converting the tags to alt characters. I need to create an unordered list html using the plugin. How do I prevent Craft or Twig from converting the HTML to alt characters?

You need to make sure that you're returning the raw version of the HTML.

In Craft 2, it looks like this...

return TemplateHelper::getRaw($result);

In Craft 3, it looks like this...

return Template::raw($result);

If you're doing this in Craft 3, you need to also be sure that you are loading the Template helper class at the top:

use craft\helpers\Template;


Redactor default .json From what I understand, the "Default" .json-file is hardcoded into the plugin, and always selected by default when creating a redactor field. I always use my "Standard.json" or "Simple.json"-files on my projects, the Default config has way too many options for less design savy clients to mess things up, so I try to keep things simple. Is there any way to make my "Standard.json" selected when creating a new field so I don't have to remember to change the .json-source every time?


Controller Action 404 Ok once again I'm having an issue with a plugin controller. It was working once upon a time but stopped sometime in the past year.

I have stripped the plugin down to bare, so there is no reason it shouldn't be working.

File Structure:

craft/plugins/
--webmentionreceiver/
----controllers/
------WebMentionReceiver_EndpointController.php
----WebMentionReceiverPlugin.php

craft/plugins/webmentionreceiver/controllers/WebMentionReceiver_EndpointController.php:

<?php
    namespace Craft;

    class WebMentionReceiver_EndpointController extends BaseController  {
                protected $allowAnonymous = array("actionReceive");

                public function actionReceive()  {
                        $this->redirect('/');
                }
        }
?>

So the plugin name is WebMentionReceiver, the controller is Endpoint and the action is Receive. So index.php?p=actions/webmentionreceiver/endpoint/receive should be the URL. But it 404s.

Any ideas? It is driving me mad!

When your plugin has a camel cased name you need to make sure you camel case your references to it, such as when you try and visit an action url.

I assume this is due to when craft tries to resolve the url it sees pluginname and pluginName as two different things, much like php variables.

I would also assume this is due to the way Craft autoloads classes.

So using webMentionReciever would resolve since it would be a "exact" match to your plugin name.

This isn't limited to just plugin names, the same goes for any service, controller etc references too.


Using MySQL With just InnoDB storage engine What are the implications if I create all the Craft database tables on the InnoDB storage engine? Will there be any performance problem?

Actually, all the tables, but one use InnoDB storage engine. The one that uses myisam engine is table "searchindex".
When you check for index type:
mysql> show index from searchindex where Index_type="FULLTEXT" \G

you get that the "searchindex" table indeed uses FULLTEXT.
I would say that Craft uses some specific fulltext searches specific to myisam engine, therefore not using innodb.
You can change the engine of the "searchindex" table (and see what brakes) with:
mysql> alter table searchindex engine="InnoDB";

and revert with:
alter table searchindex engine="MyISAM";


Advanced routing descriptive URLs to certain pages/posts This is a general SEO question.

In Wordpress you can create a page and define the permalink URL.

In Craft I want to do something similar. I want to build the backend with a common sense structure, but then define friendly URLs after. Eg: a page could exist somewhere like:

domain.com/holidays/french-river-cruises

Is it possible to define custom routing, so I could make the same page/post accessible on a more descriptive URL (for SEO reasons) to load the same page.

domain.com/summer-cruises-in-france

I know you can play with routes, but in this case I don't see how it would work that was as the slug doesn't match the post. But could this be hardcoded somehow in the advanced routes file?

So from an SEO perspective, the first URL is actually better, in that it provides even more context for the search engines to digest, index, and understand.

It's also bad from an SEO perspective to have two pages that have the same content.  As long as you include a canonical URL it won't really hurt you, but the only real benefit I see from using your second URL schema is that it's easier to type.  Which isn't really much of a concern these days imo.


How can I get the current user who purchased a product from the onOrderComplete event? I have a plugin that is listening to the onOrderComplete event within commerce. I am able to get the email address like this:

craft()->on('commerce_orders.onOrderComplete', function (Event $event) {
    $event->params['order']->email;  // name@email.com

});

I can see the Id in the log, but since I'm in the order class, I'm not sure how to get to it. I can see customerId:

$event->params['order']->customerId; // 3

But I've noticed the customerId and the userId are different. I need to be able to get the logged in userId vs. the customerId

When I try getting just the id, I get the product id back (Which is what I would expect).

$event->params['order']->id;  // 123

To relate to the twig side it would be something like this (obviously won't work):

$event->params['order']['currentUser']->id;  // 12

Here is a part of the log:

...
[_attributes:Craft\BaseModel:private] => Array
(
    [enabled] => 1
    [archived] => 0
    [locale] => en_us
    [localeEnabled] => 1
    [itemTotal] => 375
    [baseDiscount] => 0
    [baseShippingCost] => 0
    [totalPrice] => 375
    [totalPaid] => 375.00
    [billingAddressId] => 122
    [shippingAddressId] => 123
    [paymentMethodId] => 1
    [customerId] => 3
    [id] => 12   //  <-- This guy right here
    [orderStatusId] => 1
...

Since this is happening from the onOrderComplete event, the easiest way to get the currently logged in user is with UserSessionService::getUser():

$user = craft()->userSession->getUser();

Alternatively, if you want to go through the actual Commerce_OrderModel that was passed as a parameter on the event, you can do this:

$user = $event->params['order']->getCustomer()->getUser();

Either way, it’s a good idea to verify that $user gets set to something, just in case:

if ($user) {
    // ...
}


Better way to group by category and include full category entry The basic idea is that I want to group my entries by category, but I want more than just the category title—I need the entire category entry. My previous post alludes to this, but I've since discovered the query I was doing there was returning all related entries regardless of their existance in the block.result entry.

I'm currently doing this—which is exactly what I want, but it doesn't see very efficient... Is there a better way?

{% set results = block.result %}
{% for category, catEntries in results | group('infographicSection.first().title') %}
    {% set fullCat = craft.categories.title(category).first() %}

{{fullCat.title}}

{{fullCat.body}}

    {% for entry in catEntries %}

{{entry.title}}

    {% endfor %}

{% endfor %}

Your actual code is pretty effective – the problem is that it's potentially going to result in a lot of queries.

This is a good example of the so-called N+1 query problem, where each iteration over the result set of a single query generates an additional number of queries. Depending on the number of elements involved, this issue can easily result in hundreds of queries, which can be a huge problem for performance.

The solution to the N+1 query problem is eager loading. With eager loading, you explicitly tell Craft which relational data it should include with the first query, unlike with the common "lazy loading" pattern where relational data is queried on a "need to use" basis.

Here's how your code could look, refactored to use eager loading:

{% set results = block.result.find({
    with: ['infographicSection']
}) %}

{% for category, catEntries in results|group('infographicSection[0].title') %}
    {% set fullCat = catEntries[0].infographicSection[0] %}

{{fullCat.title}}

{{fullCat.body}}

        {% for entry in catEntries %}

{{entry.title}}

        {% endfor %}

{% endfor %}

With something like the above, you should see the number of queries drop drastically (remember to turn on devMode in development and you'll see a profiling report in your browser's dev tools console, containing the total number of queries).


Craft SeoMatic GTM  disable on 404 pages I want to disable google tag manager on 404 templates, I am able to add the additional condition for the script "Google Tag Manager Settings"

It has stopped GTM script code, but <noscript> tracking is still rendering on the page.

Is their way inside plugin options/SeoMatic template variable, soi can hide <noscript> code as well on specific pages

SEOmatic author here -- what version of SEOmatic are you using? I think the best way to do this would be to just disable the container from being included in your 404 Twig template:

{% do seomatic.script.get('googleTagManager').include(false) %}

As per: https://github.com/nystudio107/craft-seomatic#google-tag-manager


Using a template from a plugin I'm building a simple contact form plugin and I would like to be able to provide a default form that the developer can include in his own template.

Most plugins I've seen provide the controller and all the code to process a submitted form but you still have to create your own form.

At the moment I tried 2 methods, using a hook and a template variable.
While both work I was wondering if there wasn't a better/cleaner method.

The downside of using a hook is that you can't pass variables to the template (or am I wrong?) and the template variable feels like a misuse of the functionality.

Isn't there a way to use the existing twig import function? Maybe by registering a namespace twig path in the plugin?(https://symfony.com/doc/current/templating/namespaced_paths.html)

You can include a property to your form called $templatePath and users can insert whatever they want in this path with a default value to your own template.

When someone executed your render() function you check if the template file exists in the public template path, if so render it, it not render one of your plugin templates.

If the validation fails, you want to include the validated data with errors to the url manager, so you can access it later. During your render() function you can check if there is data and if it matches your current form element, if so render the data in your url manager instead of the element to display errors.


Problem getting relationship between entries to work I'm having trouble getting what should be a simple relationship between 2 channels to work as expected.

This is how things are set up.

Channel: Apprenticeships - essentially categories, types of apprenticeship (health, construction, catering etc)

Channel: Vacancies - individual apprenticeship courses  - each one related to an Apprenticehips entry.

In my template I can show a list of all Vacancy entries with

{% set vacancies = craft.entries.section("vacancies").find() %}

Then loop through them and output field data. This works fine and shows all the vacancy entries.

But I want to be able to filter the entries returned to those that are related to a specific Apprenticeships entry. This filtering is to be based on the third url segment, which is the slug of the Apprenticeship entry.

{% for cat in craft.entries.title(craft.entries.slug(craft.request.segment(3)).limit(1).find()) %}

This works fine and I can output field data from that Apprenticeship entry. Proving, in my mind, that Craft knows which entry I want.

To get only the Vacancy entries that are related to the Apprenticeship entry represented by cat I thought I could simply use 'cat' in another craft.entries tag.

{% set vacancies = craft.entries.section("vacancies").relatedTo(cat) %}

Then loop through vacancies outputting the vacancy field data. But it doesn't return any Vacancy entries even though there definitely some to return.

Where am I going wrong here?

I recommend doing it like so:

{% set vacancies = craft.entries.section('vacancies').limit(null) %}

{% set catSlug = craft.request.segment(3) %}

{% if catSlug %}
    {% set cat = craft.entries.section('apprenticeships').slug(catSlug).first() %}

    {% if cat %}
        {% set vacancies = vacancies.relatedTo(cat) %}
    {% endif %}
{% endif %}

This sets up an ElementCriteriaModel for all your Vacancies entries (don't use the find() method yet!).
Then checks for the first entry from the Apprenticeships section with a slug of your URL segment.
If there's really such an entry, it further extends the ElementCriteriaModel with a relatedTo parameter.


User defined crop / crop modal for image assets Is it possible to have "user defined" crop areas (drag and crop) for upload assets of type image? What I had in mind is something exactly like craft has in the Admin > Settings > General Settings > Logo

Thanks in advance!

I haven't used this myself, but the Aviary Image Editor plugin should be ideal for what you need:

https://github.com/fruitstudios/AviaryImageEditor

They also tweeted about the plugin with a short video of it doing exactly what you are looking for in Craft. Hope this helps!


Rich text field adding   and normal space char at random Is there any rhyme or reason to the adding of non-breaking spaces or traditional space? It seems to appear at random throughout the content in my rich text fields.

I've found that this happens when you end a line or string with a space and put nothing after it.

"Text " becomes "Text "

If you then copy "Text " and paste it repeatedly you'd end up with "Text Text Text Text Text Text "

Same for a lone space on a line.

" " becomes ";nbsp"


Serve PDF file at url with variable Is it possible to serve a file in a Twig template? I need to get a file (asset) from a global setting, and serve it at a particular URL.

This code is close, but it seems that source() is the wrong method to get the file contents with.

{% header "Content-type: application/pdf" %}
{% header "Content-Disposition: inline" %}

{% set filePath = [craft.config.environmentVariables.assetPath, filesImages.deliveryCalendar[0].filename]|join('') %}

{{ source( filePath ) }}

The variable filePath contains the correct system path to the file (/var/www/site.com/html/assets/file.pdf), but source() is clearly for templates and not files.

How can the contents of a file be streamed out to the browser?

You'd need to make a simple Craft plugin that added a Twig filter/function that wrapped PHP's file_get_contents to grab the file contents, but at that point, you might as well just make it a wrapper for craft()->request->sendFile().


Craft CMS and LiteSpeed (403 error on login) When trying to login to the administration section on a server running LiteSpeed, half the time LiteSpeed triggers a 403 Forbidden message and locks that computer out of the entire server Craft is running on. I'm assuming Craft is doing something that LiteSpeed deems as suspicious but I have no idea what that could be.

I'm currently trying to get access to the logs but the host is taking it's sweet time. In an ideal world I would just find another host but in this situation that's just not feasible. I'm pulling my hair out trying to get this to work, and I found love with Craft that I don't want to deny.

If anyone could point me in the right direction I would be so grateful!

It turns out mod_security was the culprit, so disabling it got Craft working.


Is it possible to use set the locale manually in a template? I've got a generic newsletter template. Now some of our newsletters are in Spanish. It's overkill and more complicated for the admins to bother with settings up different locales, so instead I'd like to set the locale from the entry (currently using a dropdown).

All good, and I can use the |translate(language="") filter to achieve this using the value from the dropdown field.

Now the problem is that when formatting a date, it remains in English because it translated using the current locale, which is English.

I'm wondering whether I can manually override the locale in the template. {% set locale = 'es' %}
or something.

Or if I can force the date to format using a specific locale.

Cheers.

Completely, 100% untested, but I think you'd be able to pull it off with a pretty simple plugin that added a new Twig filter (check the Twig Extensions switch).

From a template you'd something like:

{{ myDate|myDateFilter('es') }}

Then your plugin's myDateFilter method would do something like:

public function myDateFilter(\Twig_Environment $env, $date, $locale)
{
    $localeData = craft()->i18n->getLocaleData($locale);
    $dateFormatter = $localeData->getDateFormatter();

    return $dateFormatter->formatDateTime($date->getTimestamp());
}


Commerce: notification emails to multiple custom recipients fails Notification emails keep failing to send if I have multiple addresses set. I've tried comma separated with and without a space after, both fail the same way.

The error in the craft.log:

Email error: Invalid address: (addAnAddress to): blah@stuff.com,things@stuff.com

The error in the commerce.log:

[Forced] Email “New Order - Admin” could not be sent for order “a713f1d”. Errors:

I've run into the same issue and reported it to Craft Commerce support at the beginning of April. It was acknowledged to be a bug:

  "At the moment we are only able to support a single email address in
  the custom 'TO' field."

and improving it was added to their TODO list.

You can use BCC field (with email addresses separated by comma or semicolon) to send notification email to multiple recipients or you can create multiple emails (hooked up to the same template and status) - one for each custom email address.


How to omit certain tag from (% cache %) How can I omit a certain tag from being cached?

I have a large page with a fair few queries but halfway down I am loading in a Freeform Form field which cannot be cached. But I'd like all of the content below it to be..

Unfortunately, the vanilla {% cache %} tag doesn't have a way to escape or ignore certain content.

The obvious workaround is to have more than one {% cache %} tag, where the content that shouldn't be cached sits between/outside those tags.

If you can't do that, another option is to use output buffering and injection. Basically, you cache whatever you need to cache, but {% set %} the output to a variable, rather than rendering it immediately. The important bit: Inside the content that you cache, you put some sort of placeholder value where you want the un-cached content to render. This placeholder can be whatever you want, but make it something that isn't likely to also appear in the actual content (e.g. "%%%foobarbaz%%%").
Then, {% set %} the un-cached content to a variable also – and finally, when you print the cached content, you use the |replace filter to replace the placeholder value in the cached content with the variable containing the un-cached content. Something like this:

{# Set the cached content to a variable #}
{% set cachedContent %}
    {% cache globally using key craft.request.path %}

        {# A bunch of taxing queries here #}

I am content.

        {# Injection point for un-cached data #}
        %%%foobarbaz%%%

I am more content.

    {% endcache %}
{% endset %}

{# Set the uncached content to a variable #}
{% set uncachedContent %}

I am some content that never is cached wooooo

{% endset %}

{# Print the whole thing #}
{{ cachedContent|replace('%%%foobarbaz%%%', uncachedContent)|raw }}

Finally, there's a plugin which may work for you.


Getting UTC DateTime from Model attributes The AttributeType::DateTime in model attributes saves as UTC which is great, but upon getting that attribute I receive a Craft\DateTime object with my server timezone set for it, and thus the time gets modified accordingly.

Is there any way I could force the model to return a Craft\DateTime object with the UTC timezone?

When I created a getter method public function getDate() it doesn't get called when using object.date inside a Twig template. I have to specifically call object.getDate() instead, which isn't really an option for me.

This is how my model defines attributes:

protected function defineAttributes()
{
    return array(
        'date'  => AttributeType::DateTime,
    )
}

Maybe there are some additional rules I can set for it, that I am not aware of? I couldn't find anything in Yii's documentation.

I can't think of a way to automatically tell Craft to do this, but you could write a simple helper method to do this for you:

public static function convertDateTimeToUTC($dateTime)
{
    return $dateTime->setTimezone(new \DateTimeZone('UTC'));
}


How to use categories with Sprig's "Load More" recipe I am trying out Sprig's "Load More" Recipe and I am trying to call up entries that are related to the current category page. But when I add .relatedTo(category) to the entry model, it doesn't work–it still just shows all the entries in the channel. I've used this .relatedTo(category) method elsewhere on the page (not using Sprig) and it works just fine. How can I call up only the entries that match the current category page in this Sprig recipe?
_category.twig
{{ sprig('_layouts/_categories/_loadMore', {limit: 6}) }}
{{ sprig.script }}

_loadMore.twig
{% set offset = offset ?? 6 %}
{% set limit = limit ?? 10 %}

{% set entries = craft.entries.section('aBetterPost').offset(offset).limit(limit).relatedTo(category).all() %}

{% for entry in entries %}

            {% set featuredImage = entry.featuredImage.one() %}

{{ entry.title }}

{% endfor %}

{% if entries %}

    <input type="hidden" name="offset" value="{{ offset + limit }}">
    <button class="load-button" sprig>Load More</button>

{% endif %}

Try this:
{{ sprig('_layouts/_categories/_loadMore', {limit: 6, category: category}) }}
{{ sprig.script }}
After query category is not defined.


Can I use getCriteria to only return entries with a specified lightswitch field set to on? I'm building a plugin that will check all existing entries in a section when a single entry is flagged as featured and turn off any existing featured entries. I'm using a lightswitch field to turn on and off entries. Is there a way to use the craft()->elements->getCriteria(ElementType::Entry); to return just the entries with the lightswitch field turned on?

Cheers

This should do it:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = 'mySection';
$criteria->myLightswitchField = 1;
$entries = $criteria->find();


Generate PDF's (Printmaker plugin) with a static header and footer Does somebody know if it's possible with Printmaker to provide each page, of the PDF you generate, with a static footer and/or header?

Printmaker makes use of the dompdf library. In the documentation of this library I found that this is supported by the library.

But I cannot find in the Printmaker documentation that you can also use this option with the plugin.

I hope somebody can answer this question.

Or does somebody know a plugin that I can use as an alternative, that does support this?

I've not yet tested specifically with Printmaker (only Laravel + Dompdf), but you should be able to add page numbers by using a special <script> block in the template you use for PDF rendering. This block's PHP instructions will be interpreted by Dompdf as it renders your document, so you can position additional elements like page numbers, headers/footers, etc.

Example of what you'd include in your Twig template:

<script type='text/php'>
if ( isset($pdf) ) {
    $font = $fontMetrics->get_font('Proxima Nova', 'normal');
    $size = 9;
    $y = $pdf->get_height() - 32;
    $x = ($pdf->get_width() / 2) - ($fontMetrics->get_text_width('1 of 1', $font, $size) / 2);
    $pdf->page_text($x, $y, '{PAGE_NUM} of {PAGE_COUNT}', $font, $size);
}
</script>

More on the subject where I first learned about it: https://stackoverflow.com/a/13184208/897279

CRITICAL POINT:

Ensure PHP is enabled in config/printmaker.php...

'isPhpEnabled' => true,