Hook into Entry::EVENT_AFTER_SAVE when user saves it I want to send a Mail to an admin, whenever a User of group ‚Cook‘ creates an Entry of Type ‚Recipe‘.
For that I created an Event reacting on Entry::EVENT_AFTER_SAVE :
Event::on(
    Entry::class,
    Entry::EVENT_AFTER_SAVE, static function (ModelEvent $event) {

    /** Entry @entry */
    $entry = $event->sender;

    /** @var User $user */
    $user = Craft::$app->getUser();

    if (!$event->isNew) {
        if ($user->identity->isInGroup('cook') &&
            ElementHelper::isDraftOrRevision($entry) &&
            $entry->getStatus() === Entry::STATUS_DISABLED) {

            $cpUrl = $entry->cpEditUrl;
            $bodyText = "Dear Admin. /n $userName has created $entry.";

        };
    }
});

As your can see - I tried a lot to only react on the first creation of the entry. But all this does not the trick.
Behind the scenes craft created an Entry (and saves it initially) even when the Cook presses the ’New Entry’ Button.
So I don’t know hook in only when the message is first saved directly by the user pressing the ‚Create‘ Button after entering the content.
It would be nice if your could help me with that.
Thanks in advance.
Michael

Your code is pretty close. You want to make sure it's a new entry and not a draft or a revision so you could use:
Event::on(
    Entry::class,
    Entry::EVENT_AFTER_SAVE, static function (ModelEvent $event) {

    /** Entry @entry */
    $entry = $event->sender;

    /** @var User $user */
    $user = Craft::$app->getUser();

    if (
        $event->isNew // Makes sure it's a new entry
        $user->identity->isInGroup('cook') && // Makes sure your user is in the cook group
        !ElementHelper::isDraftOrRevision($entry) && // Makes it's not a draft or a revision
    ) {
            $cpUrl = $entry->cpEditUrl;
            $bodyText = "Dear Admin. /n $userName has created $entry.";
    }
});


Does it make sense to use Craft with relative links? A coworker of mine asked me if I could make the links in Craft relative.

Which is not a big deal for navigation links,

relative url:

{% set relativeUrl = entry.url|replace((siteUrl), '/') %}

but it gets trickier for links inside a Rich Text field.

Craft is set up to use absolute links, is there a reason behind that?

I would question the need to have relative URLs to begin with.

As Brad mentions, Craft neatly solves the multi-environment problem for you.

If you come from an environment like WordPress where it makes absolute URLs for you and then you end up having to do a find-replace on the content when switching environments (since there's no "built-in" way to do it), I can see where your co-worker might think relative is the way to go. However, even WP has good reason to do so.

Back before content management systems, relative URLs were probably the way to go because they are easier to code by hand; if you used conventions like "../" your pages also worked locally. Now with systems like Craft and local testing servers, creating links within content is much more consistent.

Craft even has a find/replace tool in the CP if you want to make sure those staging/production URLs absolutely get switched over in the database as well.

Speaking of consistency, google wants you to be as efficient as possible. From an SEO perspective, this moz post goes over the pros and cons of relative vs absolute but a few points worth summarizing:

Scrapers can be a big problem stealing your content. Absolute URLs make it a bit more difficult for the laziest of them.
By using things like rel="canonical" and letting Craft spit out your main base URL consistently, you can help avoid duplicate content issues.

Yoast gives some thoughts on this as well but I'll leave you with one takeaway:

  The few minutes a developer might save by using relative URLs are offset by countless hours an SEO might be spending to solve the issues caused.


Activate correct locale upon front-end entry Is there any way, upon creating new entries via front-end form, to not have all locale versions of those entries active?

I’d like to think I could just set something like
<input type="hidden" name="locale" value="en_us”>

Using <input type="hidden" name="locale" value="en_us”> works as expected, as long as the section settings for that entry has all the locales set to be disabled by default.

Upon creating a new entry via front-end form, only the specific locale was activated. However, creating the entry in the admin panel, they are all left off by default. (Seems correct, just remind any clients that might be using the admin panel to enable whichever locale).


Craft storage dir as linked in capistrano I'm having some issues with my install of craft on a staging server.

I'm deploying using Capistrano, and (perhaps wrongly) added craft/storage to linked_dirs (to share between deployments). Since doing that, things have not been going so well with my installation. I keep get "No response" screen in the browser, (Error code: ERR_EMPTY_RESPONSE). I also get "unknown error occurred" messages when browsing round the admin doing normal things like updating entries etc.

The only consistent error is this one:

2015/03/26 17:33:53 [error] [exception.Craft\HttpException.503] exception 'Craft\HttpException' in /var/www/50th/releases/20150326171328/craft/app/etc/web/WebApp.php:916
Stack trace:
#0 /var/www/50th/releases/20150326171328/craft/app/etc/web/WebApp.php(230): Craft\WebApp->_enforceSystemStatusPermissions()
#1 /var/www/50th/releases/20150326171328/craft/app/framework/base/CApplication.php(180): Craft\WebApp->processRequest()
#2 /var/www/50th/releases/20150326171328/craft/app/index.php(62): CApplication->run()
#3 /var/www/50th/releases/20150326171328/public/index.php(19): require_once('/var/www/50th/r...')
#4 {main}
REQUEST_URI=/
---
2015/03/26 17:33:53 [error] [application]

It's worth noting that the site is in offline-mode.

Any ideas?!

Update:
I've found a consistent error message when trying to access "/admin/users":
[DOMAINNAME...]/index.php/admin/actions/elementIndex/getElements net::ERR_EMPTY_RESPONSE

Is being returned by jQuery trying to access elements. Does this help at all?

I've eventually tracked down this peculiar ERR_EMPTY_RESPONSE to a php error buried deep in my apache logs - zend_mm_heap corrupted. So as it turns out nothing to do with Capistrano (or Craft) at all! This zend_mm_heap error is a php memory error that I seem to have solved by upgrading my version of PHP after unsuccessfully upping my output_buffer as suggested in this stackoverflow thread. It's worth noting that there was nothing in the Craft log about this error, no mention of anything untoward. The Craft\HttpException I found above is, I believe, essentially a '404' error as my site was running in 'offline mode' so requests had to be run through _enforceSystemStatusPermissions.

I did initially have some issues with permissions with Capistrano where my server was setting new files created by craft to 644 www-data:www-data and therefore my user couldn't edit these files, despite being in the www-data group, and so Capistrano was having a hard time removing old releases. I've fixed this by adding a umask 002 to /etc/apache2/envvar.

Now that the zend_mm_heap error seems to be behaving, and my permissions are correct, deploying with Capistrano is working rather well. For future reference, my linked dirs config looks like this:

set :linked_dirs, %w{public/resources craft/config/local craft/storage}

public/resources is my (single) craft asset source, craft/config/local are my server-specific config files as suggested here which is working nicely for me. craft/storage is obviously the craft storage folder that is shared between releases.

In terms of deployment, I think Capistrano (and other tools of a similar ilk) is great. I'm hosting my repository in a private BitBucket account and push new changes onto the master branch. I have two stages set up :production and :staging which deploy to two different vhosts on my server (staging domain is test.domain.com).

My stage configuration looks like this (for staging)

set :stage, :staging

set :deploy_to, "/var/www/domain-test"

# Allow user to define which branch to push, e.g.
# BRANCH=my-new-feature cap staging deploy
set :branch, ENV['BRANCH'] || "staging"

This last line is really helpful if I want to quickly test something on the master branch without having to merge it into the staging branch and push and deploy.

I hope that helps someone who comes across a similar issue. If anyone wants more info about my Capistrano set up, let me know. I highly recommend it as a method of deployment, once you figure out your (inevitable and strange) server errors!


Filter Sprout Forms entries based on field value, using parameters I'm trying to display form entries from Sprout Forms that match a specific criterium:

{% for formEntry in craft.sproutForms.entries.formHandle('formHandle').fieldname('fieldvalue') %}
    {{formEntry.fieldname}}
{% endfor %}

This does not filter the entries.

This works:

{% for formEntry in craft.sproutForms.entries.formHandle('formHandle') %}
    {% if fieldname == 'fieldvalue'%}
        {{formEntry.fieldname}}
    {% endif %}
{% endfor %}

Can this not be done with parameters, or am I using the wrong Entry model?

Filtering entries is now possible as of Sprout Forms v2.2.0:

{% for formEntry in craft.sproutForms.entries.formHandle('contact').firstName('Zoe') %}
  ...
{% endfor %#}


{% requireLogin %} not working on extended (layout template) files? Is there a reason {% requireLogin %} is not firing when using in a parent include?
if I place {% requireLogin %} inside the main template file and then use this like so:
homework.twig
{% extends "_layouts/members-page-layout.twig" %}
It is not firing? But if I put it inside the homework.twig file directly it fires....
Was hoping t avoid having to add it to multiple templates and just add it to a 'members' parent layout template.
EDIT 18 July 2020
Editing the post to include more information
Here is the parent template I am trying to include the {% requireLogin %} that is not firing.
{# -- Layout template for HTML pages -- #}
{% extends "_boilerplate/_layouts/base-html-layout.twig" %}

{# -- Any <meta> tags that should be included in the  #}
{% block headMeta %}
{% endblock headMeta %}

{# -- Any <link> tags that should be included in the  #}
{% block headLinks %}
{% endblock headLinks %}

{# -- Any CSS that should be included before  -- #}
{% block headCss %}
  {# Google fonts #}
  <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap" rel="stylesheet">

  {% include "_inline-css/site-fonts.css" %}
{% endblock headCss %}

{% requireLogin %}

{# -- Page body -- #}
{% block bodyHtml %}
  {% set primarySectionColor      = 'navy' %}
  {% set secondarySectionColor    = 'pink' %}

  {% if currentUser.admin %}
    {#    TODO: Make this hide#}

          {{ svg('@icons/bx/bx-info-circle.svg', class="w-5 h-5 flex-shrink-0 fill-current mr-1") }}
          You are currently logged in as an admin role so you have unrestricted access and therefore you will not see the filtering of content and homework like a member user
          (parent) would see.

        <button class="bg-green-500 text-white p-2">
          {{ svg('@icons/bx/bx-info-circle.svg', class="w-8 h-8 flex-shrink-0 fill-current") }}
        </button>

  {% endif %}

    {# -- Info header, including _navbar.twig -- #}
    {% include "_globals/header.twig" %}

    {# - IE notice for Old browsers - #}
    {% include '_globals/ie-notice.twig' %}

      {# -- Primary content block -- #}
      {% block content %}
      {% endblock %}

    {# -- Content that appears below the primary content block -- #}
    {% block subcontent %}
    {% endblock %}

    {# -- Info footer -- #}
    {% include "_globals/footer.twig" %}

{% endblock bodyHtml %}

Try inside this in the main template file:
{% block access %}
    {% requireLogin %}
{% endblock %}


Pixel & Tonic's Contact Form Plugin - POST submissions to Zapier Webhook URL? I'm still a beginner with PHP so please bear with me. I've installed the Contact Form plugin for Craft by Pixel and Tonic, and have it configured to work as expected.

However, I'd like to have the form also send the data to a Zapier webhook URL so that it will automatically create a lead in my CRM system.

Putting the webhook URL in the "action" field but does send the data correctly to Zapier, but the user goes to a status page (see screnshot below) and the form isn't mailed out to my email.

Any help or suggestions on the best way to accomplish this?

By putting the webhook URL in the action field, you're completely bypassing the Contact Form's processing of the POST request and getting back a JSON response from Zapier.

I believe the correct way to do this would be to write your own custom Craft plugin that listens to Contact Form's beforeSend event.

When that event gets fired, you can make an AJAX request (using Guzzle or whatever) to Zapier's URL passing in the required fields, get the response back, do with it what you will, then let the Contact Form's controller finish executing and send them email.


Search Commerce Products by Tags this is my first Craft CMS & Commerce build and I need some help with searching products by a tag field.

I have a tag field ("designTag") for each product and I would like to have a search bar that will only query the tag field for a specific product type ("designMedia") and return those products with matching or similar tag titles and also account for pagination. I can't seem to get it to work. Any help would be appreciated.

SEARCH FORM CODE:

<form action="{{ url('design/keywords') }}">
    Search By Keywords

        <input type="search" name="q" class="form-control input-lg" placeholder="Enter Keyword...">
        <button class="btn" type="submit"></button>

</form>

RESULTS CODE:

{% set tagTitle = craft.request.getParam('q') %}
{% set tag = craft.tags.title(tagTitle) %}
{% set product = craft.commerce.products.search(query).order('score') %}

{% if product|length %}

{% paginate craft.commerce.products.type('designMedia').limit(48) as mediaProducts %}

{% for product in mediaProducts %}

{% endfor %}
{% else %}

No Results

{% endif %}

/*pagination code */
{% endpaginate %}

This should work (set the limit to whatever you want/need):

{% set tag = craft.tags.title(tagTitle).first() %}

{% paginate craft.commerce.products.relatedTo(tag).limit(10) as pageInfo, products %}

    {% for product in products %}

        {{ product.title }} etc

    {% endfor %}

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

For more on the pagination side of things, see: https://craftcms.com/docs/templating/paginate


Complex logic on a ElementCriteriaModel parameter? How would you combine multiple logical expressions on a ElementCriteriaModel parameter?
You could query twice and then merge the two returned arrays of entry models,
but that probably wouldn't allow me to later use the {% paginate %} tag or methods like getNext().

Here's an example where I'd like to filter events starting in calender week 32 or 34:

{% set dateParamCw32 = 'and, >= 2014-08-04 00:00:00, < 2014-08-10 23:59:59' %}
{% set dateParamCw34 = 'and, >= 2014-08-18 00:00:00, < 2014-08-24 23:59:59' %}

{% set startDateParam = 'date range cw32 OR cw34' %}

{% set entries = craft.entries.section('events').startDate(startDateParam) %}

For most cases you can find a workaround to combine such conditions by making use of entry IDs.

In this example query for each time range condition separately and use the ids() method to "collect" your entries. All matching IDs are then passed to the id parameter of a final ElementCriteriaModel, that you can also use for Pagination etc.:

{% set dateParamCw32 = 'and, >= 2014-08-04 00:00:00, < 2014-08-10 23:59:59' %}
{% set dateParamCw34 = 'and, >= 2014-08-18 00:00:00, < 2014-08-24 23:59:59' %}

{% set entriesCw32Ids = craft.entries.section('events').startDate(dateParamCw32).ids() %}
{% set entriesCw34Ids = craft.entries.section('events').startDate(dateParamCw34).ids() %}

{% set idParam = [] %}
{% set idParam = idParam|merge(entriesCw32Ids) %}
{% set idParam = idParam|merge(entriesCw34Ids) %}

{% set entries = craft.entries.id(idParam) %}


Force redactor to only display HTML editor view Is there a way to make a Rich Text field using Redactor, to only display and allow input in the HTML view?

The first step I tried was making a redactor config file that looks like this:

{
    "buttons": ["html"],
    "plugins": [],
    "toolbarFixed": false
}

But the default view is still the WYSIWYG view. I want the HTML button to be toggled by default...

Many thanks for any help!

I don’t think it’s possible at all and what’s the benefit of using a WYSIWYG field at all when you don’t use any of the features.

I’d rather use a plain text field or even better, a field type which is optimized for code editing like the Simple Text field type.

https://github.com/craftcms/simple-text/tree/v1


Default price in Craft Commerce I have a Product Type where every product has the same price. What are some good ways to avoid having to enter this same price for every product?

I don't think there is a way to do this from the back end data entry point of view currently - that is, right now, the price field is required, and indeed is required to be a number.  You can't use e.g. the value of another field in there (in which case you could have used e.g. a global or something to store a single record of the price elsewhere).

The other solution would be to deal with it via a plugin - you could listen to https://craftcommerce.com/docs/events-reference#commerce_lineitems.onpopulatelineitem and intercept this product, setting the price for the lineItem in there.  You could make a setting field in your plugin to store the actual price so there was a UI based way of changing it globally easily.  However, you'd still have to enter some price in the product when entering the product, something token like 0.01 perhaps.  Not perfect from a UI point of view and easily  misunderstood by anyone coming new to the back end.

Ideally, and it might we worth making a feature request to P & T for this - a price field that can parse twig and pulls values from elsewhere - this could certainly be very handy.

Another option would be to write a product importer and deal with the data entry that way - so that the price is set at the time of import of the data.  There's a gist that would get you started with here - https://gist.github.com/lukeholder/bbff8caef82078fc76e4

All said and done, though, this might be just one of those instances where you have to just deal with the data entry (at the moment) - it all depends of course whether the extra work/maintenance of an importer or plugin is worth doing (which all depends on how many products you're talking about and perhaps how often the price would change).


Getting admin to work on a "dev-sounding" subdomain I am trying to migrate my local dev environment to a staging site, but am receiving You’re running Craft Pro with a Craft Personal license.

Initially I tried access the admin at http://project.staging.company.com/ where I received the upgrade message. After emptying all caches, I then tried accessing from http://staging.project.company.com and still get the upgrade message.

Looking at this answer I assumed Craft would recognise this as a "dev-sounding" domain.

Is this still the case? What do I need to go to get this working on staging?

At this stage I am not sure what will be the final domain, so I was hoping not to purchase the licence until we were ready to go live.

As Luke referenced in his answer, you're mixing up two separate (but related) concepts.

Craft License

Your license key is stored in your craft/config/license.key file. This file is automatically generated the first time you run Craft, and should always remain a part of your website. If you're using version control, make sure that it's included in your repo.

Craft Edition

The "edition" of your website refers to which version you're using: Free, Client, or Pro.

Whichever edition you are currently using will be stored in your database for immediate reference... However, your site will frequently ping Pixel & Tonic's servers to ensure that it's using the correct edition. While your license key does not directly contain which edition you should be using, it let's P&T's servers know which website is performing the request, and replies back with the proper edition information.

How it relates to your situation...

Using a "dev-sounding" domain name only does one thing:

You can share the same license key file between domains without getting a warning message at the top of each page.

The P&T servers will associate your license with only the non-dev-sounding domain... any dev-sounding domains will not be recording with your license key data. So if you try to use the same license key file on multiple non-dev-sounding domains, all but one will display this message:

  The license located at config/license.key belongs to [OTHER DOMAIN]. Transfer it to this domain?

In regards to your edition... As long as they're sharing the same license, every environment for your site will share the same edition.

As Luke mentioned, when developing a site in your local environment, you can set the local path to end with craft.dev. This is considered a very special circumstance... Craft will recognize this special domain and allow you to upgrade your edition for free!

Of course, since that special domain is only available locally, you'll be expected to properly purchase your edition when you move your site to any other domain name (regardless of whether it's "dev-sounding").

Again, Luke's solution is correct... Purchase the license now, and don't worry about what the final domain will be. It's not relevant. Switching your license to the correct domain is incredibly easy and painless, and happens with a single click.

UPDATE:

It's now possible to try Craft Pro from any non-public domain:

  http://pixelandtonic.com/blog/test-craft-client-pro-from-any-non-public-domain


Is it possible to share a field between Matrix Blocks? I'm attempting to do some advanced layout orchestration using Matrix blocks. I'll need different blocks types – Image, Quote, Body – that can be combined, but they'll each need to share a few fields that allow their layout to be customized, such as Padding, Alignment, or Width. I'd rather not have to create the exact same field (e.g. Alignment could be a 4-option Dropdown) for each block type, but there doesn't seem to be a way to share fields between Matrix Blocks the same way you can share them between Sections.

Am I correct that there is no better way to accomplish this than just recreate the shared fields for each block type?

Correct - currently Matrix fields are not global, so you'll need to recreate them if you want to use them in other Matrix blocks.


Category Nav tag with control over the depth that {% children %} goes Looking at the docs, i can see that the category {% nav %} loop uses the categoryGroup as the starting point. But, i want the first top level category within the category group as the starting point.

So, i want to do something like:

{% set categories = craft.categories.slug('my-first-top-level-cat).first().getChildren() %}

{% nav category in categories %}

        {{ category.title }}

 {% ifchildren %}

   {% children %}

 {% endifchildren %}
{% endnav %}

But, doing this only returns the top level, no sub levels.

Also, i want to be able to limit the amount of children output as the there are a few sub trees.

i did {% set categories = craft.categories.descendantOf(1066) %}

And then i did

{% nav category in categories.level(2) %}
{% endnav %}

Always get the answer myself just after i post these damn things.


Display image asset of a variant Craft Commerce newbie question....

How do I display the image asset for a custom variant field?

I'd like to display the associated image of a product variant in the cart.

Ok so in general this depends on if the custom field is on the product or the variant.   Assume the filed is called masterImage either way.

If the field is on a variant:
{% set image = lineItem.purchasable.product.masterImage.first() %}

If the field is on the product:
{% set image = lineItem.purchasable.masterImage.first() %}

Remember (either way) you should always check there is an image first really:

{% if lineItem.purchasable.product.masterImage|length %} ... set the image

In practise I usually have code that falls back to the product image if the variant image is not set for whatever reason:

    {% set image = lineItem.purchasable.masterImage.first() ?? null %}
    {# Fallback on product image if there is no variant image set #}
    {% if not image|length %}
        {% set image = lineItem.purchasable.product.masterImage.first() ?? null %}
    {% endif %}

(Note here I am not doing the initial test on the asset because I am instead using the null coalescing operator)


Error creating new volume When creating a new volume (same process as the other 3 volume I currently have and which are fine, I'm running into the following error).

Any help would be appreciated.

This is most likely due to a conflict between your database and project config. Conflicts can arise even if the useProjectConfigFile config setting isn’t enabled, if any changes were made directly on the database rather than through the appropriate service.

To fix, first make sure you’re on the latest version of Craft, and then run the following command from your terminal:

./craft project-config/rebuild

Then try recreating your volume.


Display number based on position in structure hierarchy I'm stuck. For a new site I need to display numbers before the main h1 title and in the submenu's.

Example:

1 Home
2 About

01 history
02 strategy (active page)
03 profile
etc. etc.

In the active page (strategy) the title needs to be 02 strategy.
So the number is based on the position inside the parent. If I reorder strategy to the first position, the number needs to be 1.

Can someone help me please?

preview http://durk.com/example.png

What about using CSS for your numbers? (Yes, this answer makes your question sound a bit off-topic, sorry for that, hehe)

Mainmenu:

.mainNav {
  counter-reset: mainNav;
}
.mainNav-item {
  counter-increment: mainNav;
}
.mainNav-item::before {
  content: counter(mainNav) ". ";
}

Submenu (double digits):

.subNav {
  counter-reset: subNav;
}
.subNav-item {
  counter-increment: subNav;
}
.subNav-item::before {
  content: counter(subNav, decimal-leading-zero) ". ";
  color: gold;
}

.

Edit:

Misunderstood that
 problem at first. I don't think that there's a dedicated function in Craft to get the position in a structure.

But you can get the entry position like so. You have to get all the structure entries that are on the same structure level with a new ElementCriteriaModel (→ new DB Queries) and then you do a conditional to find the position of the current entry.

{# Get the IDs of all the sections #}
{% set currentSectionId = entry.id %}
{% set allSectionIds = craft.entries.section('myStructure').level(1).ids() %}

{# Define var before loop #}
{% set sectionNumber = '' %}

{# Loop through all section IDs and find the one that matches the current #}
{% for key, sectionId in allSectionIds if sectionId == currentSectionId %}

    {# Use array key to calculate the section's position #}
    {% set sectionNumber = key + 1 %}

{% endfor %}

{# Save the section's position in a custom data attribute #}

Test

Use CSS to style your heading:

h1:before {
  content: attr(data-section-no) ". ";
}


How to remove relationships between entries I have a channel called 'Activities' and a channel called 'Members'.
In 'Activities' I have a field called 'Accepted'. I want to be able to add and remove 'Members' to the field 'Accepted'.
I want to do this in a plugin that I am building. Is there a method to add or delete a single 'Member' to the 'Accepted' field of an 'Activity'?
Basically is should remove the relationship between the Activity and the Member, but it should not remove the Member itself.

A relationship field expects an array of element IDs. So to remove a user, you'd just get all the current IDs and remove
the one you want to delete.

// Get the current member IDs
$members = $entry->accepted->ids();

// Remove a member ID
$key = array_search(52, $members);
if ($key !== false) {
    unset($members[$key]);
}

// Add a new member ID
$members[] = 83;

// Update the member IDs
$entry->setContentFromPost(array(
    'accepted' => $members
));


How can I retrieve all the related entries? I have 3 entries. One of the entries got the entries field where Ive attached the two other entries. Im trying to display all the related entries whether entry 1, 2 or 3 is chosen.

For example:

Entry 1: Has the entries field. When this entry is selected, I need to show entry 2 and 3
Entry 2:  When this entry is selected I need to show entry 1 and 3
Entry 3:  When this entry is selected I need to show entry 1 and 2

Is this possible?

This is more tricky then I first thought. I hope this is what you need:

In case the current entry element is not the source of your relation, find that source entry first. This entry then goes into the relatedTo parameter of a new ElementCriteriaModel, where we exclude the current entry entry using its ID.

Code is probably more easy to understand than my wobbling english explanation:

{{ entry.title }}

{{ entry.body }}

{# Get `allRelatedEntries` if this `entry` is the "source element" of the relation #}
{% set allRelatedEntries = craft.entries.relatedTo({ sourceElement: entry }) %}

{# Get `allRelatedEntries` if this `entry` is the "target element" of the relation #}
{% if not allRelatedEntries|length %}
    {% set sourceElement = craft.entries.relatedTo({ targetElement: entry }).first() %}

    {# Any relations at all? #}
    {% if sourceElement %}
        {% set targetElements = craft.entries.relatedTo({ sourceElement: sourceElement }).id('not ' ~ entry.id).find() %}
        {% if targetElements|length %}
            {% set allRelatedEntries = targetElements|merge([sourceElement]) %}
        {% endif %}
    {% endif %}

{% endif %}

{% if allRelatedEntries|length %}

Related Entries

        {% for relatedEntry in allRelatedEntries %}

{{ relatedEntry.title }}

        {% endfor %}

{% endif %}


Payment form submission is returning a curl error! ([curl] 3:  malformed [url]) I'm submitting a payment form via standard POST submission (not AJAX) to the current page URL, with an action value of commerce/payments/pay. To double-check my work, I've also copy + pasted their example form, and I'm still getting the same result.

I've done this a thousand times before, so I'm going a bit crazy trying to figure out what's different in this project. Any help is greatly appreciated!

For those interested, here was the solution: The payment method for Authorize.net wasn't completely filled in; there was no endpoint set. Thus, it was of course failing when it sent the curl request, because there was no protocol, domain, or anything else other than the query string for the payment form.

Providing the payment method with an endpoint in the CP resolved this issue.


Comparing current date to multiple entry matrix date fields I need to display an entry only if the date fields within its Matrix are current. So, only display if the startDate is greater than or equal to today and the endDate is less than or equal to today.

Currently I have this code and am having trouble finding how to compare multiple dates.

{% for entry in craft.entries.section('sectionHandle') %}
    {% set present = now|date %}
    {% set entryStart = entry.matrixHandle.startDate %}
    {% set entryEnd = entry.matrixHandle.endDate %}

    {% if entryStart >= present and entryEnd <= present %}
        display entry information
    {% endif %}
{% endif %}

I'm guessing that I'm using the operators incorrectly or that "and" isn't allowed. Either way, it's not displaying anything and yes, I have a made sure that I have an entry with dates that would activate this if statement.

I think you've just got a logic problem.

{% if entryStart >= present and entryEnd <= present %}

The only way this will ever evaluate to true is if the entryStart and entryEnd DateTime fields have the exact same date and time (down to the second) that the present time is, which seems like a very small window.

You're essentially looking for an entry that lives in the future and in the past.


Uploading Lottie animation files with images For anyone who doesn't know what Lottie is, it is a video player created by the AirBnB team that lets you play vector animation exported from Adobe After Effects. It exports as json-files that you can easily add to any web project. This is easy to upload in craft cms or include in a static website. However sometimes I have images included in the animation that needs to be linked correctly in the json-file.

I was just wondering if anyone else have encountered this problem and found a solution?

How are the images included in the json-file? Do you need upload the images as well? I assume, that you do have a problem with the image paths? Please describe your workflow and folder structure.

Frontend-Side you could always replace some strings via twig. In your case you could replace a path-segment from the json field.


Eager Loading breaking focus point I’m trying to cut down the amount of database queries on the homepage of a site I’m working on, and am attempting to do some eager loading of an asset set in a supertable field. However, although the queries drop, the focus point no longer works when I do, so I think it’s not setup right. Can anyone explain what I’m doing wrong please?

{% set mainCarousel = entry.mainCarousel.find({
 with: [
 'carouselImage'
 ]
}) %}
{% for row in mainCarousel %}
{% set image = row.carouselImage[0] ?? null %}
{% set transformedImages = craft.imager.transformImage(image,
  [
      { width: 1920, ratio: 3/1, jpegQuality: 60 },
      { width: 1440, ratio: 3/1 },
      { width: 1024, ratio: 3/1 },
      { width: 750, ratio: 4/3, jpegQuality: 60 },
  ],
  {
      format: 'jpg',
      allowUpscale: false,
      mode: 'crop',
      jpegQuality: 80,
      position: image.focusPctX ~ '% ' ~ image.focusPctY ~ '%',
      interlace: true
  }
) %}

 {% if image | length %}
 <picture>
   <source srcset="{{ craft.imager.srcset(transformedImages) }}">

 </picture>
 {% endif %}

{{ row.heading }}

{{ row.text }}

   {% set button = row.button %}
   {% if button|length %}View Project{% endif %}

{% endfor %}

It has been answered in another thread here: enter link description here, but basically you have to use another focal point plugin called FocalPointField-Craft which then works with eager loading. And I've found also uses a lot less database queries than the other plugin too.


ElementAPI: Include author/user data with entry? Searched around online and couldn't find an answer to this question. I want to be able to include some data from the author/user account associated with an entry.

Here is the basic working version of an individual entry, which does display the 'author' name, but no idea how to display that user's data, such as; profile photo, first name, last name, and custom member fields.

'listings/<entryId:\d+>.json' => function($entryId) {
  return [
    'elementType' => 'Entry',
    'criteria' => ['id' => $entryId],
    'first' => true,
    'transformer' => function(EntryModel $entry) {
      return [
        (int)$entry->id,
        (int)$entry->year,
        $entry->manufacturer,
        $entry->model,
        $entry->author
      ];
    },
  ];

$entry->author is going to be a UserModel object, so you've got access to all of its properties and methods depending on what you need to return.

i.e. $entry->author->firstName, $entry->author->lastName, etc.

Here's a list of them from a Twig perspective, but the same things are available to PHP. https://craftcms.com/docs/templating/usermodel


Unable to locate message source for category '' I'm trying to update my plugin to Craft 3 and I'm getting this error message:

Unable to locate message source for category ''.

What does it mean?!

In Craft 2, the Craft default Yii translation method was overridden to only require one value, the value of the translatable string:

Craft::t('My Translatable String');

In Craft 3, Craft reverts to using the default Yii behavior for the Translate method t() and there are two arguments needed.  The first argument is now the category and the second is the translatable string:

Craft::t('pluginhandle', 'My Translatable String');

Adding the category back allows translations from different contexts to avoid conflicts with each other.

Craft uses the app category.  Yii uses the yii category.  And plugins should use their own plugin handles.

The front-end of a site should use site category, and the actual translation files should be saved in translations/locale/category.php (whether that's in craft/ or craft/plugins/mypluginname/)

(Hat tip to Brandon Kelly for helping me understand this better on Craft Slack)


Show Other Entries in Same Category So, I am new.  I want to display the 1-n most recent posts in the same category as the current entry (preferably excluding the current entry) -- i.e., I want to have a section with Other Similar Posts at the bottom of an entry page.

I only have one Category Group = beerStyles. There are multiple categories to choose from in beerStyles. Each news entry includes a field called "style" to assign a category from beerStyles. Each entry can have only 1 category.  (Note there is no entries field in news entry, just the style field to select a category form beerStyles.)

Related Posts

                {% set category = craft.categories.slug('beerStyles') %}
                {% set entries = craft.entries.relatedTo(category) %}
                {% for entry in entries %}

{{entry.title}}

                {% endfor %}

No error is thrown, but other news entries with the same category assigned are not appearing.  Nothing is appearing.

Any thoughts?  And thank you in advance for your time and assistance.

P.S.  I know I left off the .limit(x) to limit the number of posts  I just want to get it working first.

Based on your description, this should do it:

{% if entry.style|length %}

Related Posts

            {% set styleCategory = entry.style.first() %}
            {% set relatedEntries = craft.entries.section('news').relatedTo({
                targetElement: styleCategory,
                field: 'style'
            }).id('not '~entry.id) %}
            {% for relatedEntry in relatedEntries %}

{{relatedEntry.title}}

            {% endfor %}

{% endif %}

A few notes:

– I've wrapped the whole "Related posts" in a conditional testing if the entry.style categories field is not empty (if entry.style|length).

– Personal preference, but for readability's sake I find its usually a good idea to be specific/semantic with variable names (e.g. styleCategory instead of just category, relatedEntries instead of entries etc).

– The targetElement and field attributes for the relatedTo() parameter are not required – you could just do .relatedTo(styleCategory). Being specific is a minor performance win and avoids wrong results if you have multiple relations involving the same elements.

– The id('not '~entry.id) prevents the current entry from being returned


Upload failed. The error message was: "SQLSTATE[23000]: Integrity constant violation: I'm getting this strange error when I'm trying to upload an .jpg in the assets.

Env: Localhost
Craft version 3.1.8

MAMP:
- PHP(7.2.7)
- MySQL: 5.7.21

Upload failed. The error message was: "SQLSTATE[23000]: Integrity constant violation:

Does this only happen with this one particular image? If so, try renaming the image file to golden-pothos-2.jpg or something and then uploading again.


Migrating entries to custom element and UID I want to migrate some entries and its content to a new table in our plugin. Below is a simplified SQL that seems to do the trick.

insert into craft_myplugintable(id, field1, field2, field3, dateCreated, dateUpdated, uid)
select entries.id, content.field_field1, content.field_field2, content.field_field3, entries.dateCreated, entries.dateUpdated, entries.uid
from craft_elements elements
  join craft_entries entries on elements.id = entries.id
  join craft_entrytypes types on entries.typeId = types.id
  join craft_content content on entries.id = content.elementId
where types.handle = 'mySection';

update craft_elements elements
  join craft_myplugintable myplugin on elements.id = myplugin.id
set type = 'Plugin_ElementType';

Is this method safe? Should i use a migration file and let craft set the UID? I'm not sure what the UID field is for. Keep in mind that we no longer need the entries, so they can be deleted if this is wrong.

If you want to be future-proof, then yes - you should create a new UUID for each new row.

You can use MySQL's uuid() method for that.


How to create an element query in PHP with custom field names in variables? I'm trying to dynamically create an element query in PHP where the custom field criteria come from an array of strings. Something like this...?

$users = User::find();
$users->group($groupList);

foreach ($criteria as $key => $value) {
    // Each key value is the handle for a custom field and the
    // field value used as criteria
    $users->$key($value);
}

$users->all();

How can I use a custom field name in a string as a parameter?

Edit If I don't chain the criteria, I also get a deprecation error when I iterate over $users...Is there a better way to do that, too?

You can use the Craft::configure() for that.. imagine your array looks like

$criteria = [
    'age' => ['and', '>=30', '<=50'],
    'enabled' => false
    'status' => null,
]

You can do

$query = User::find();
Craft::configure($query, $criteria);

$users = $query->all();

Or you can just use the parameter based syntax instead of the functional one

$users->$key = $value;

You'll receive a deprecated error when you don't fetch your users. Your so called $users variable hast actually nothing to do with users it's a really bad name because one might think users store information about a list of users but that's not the case. $users is a Query that looks for elements with the type craft\elements\user in your database. You'll receive your list of users when you execute your Query with one(), all() or something like that.


How do I enable editing of entries with a front-end form? Users can currently submit entries using a form on the front-end. How would I go about showing the current logged in user's existing entries and allow them to edit them through another front-end form?

Add another hidden input field "entryId" to your form. There's nice example code in the Docs:

You can modify the form to save existing entries by adding an “entryId” hidden input to the form:
<input type="hidden" name="entryId" value="{{ entry.id }}">


Migrate contents between fields? Right now, we have a matrix field called post body which has all our content, we have text, image, video etc... In our image block we have 2 fields, An asset field called image & a plain text field called caption.

When I first set this site up, admittedly I didn't plan it properly at all and now we allow several images to be uploaded in the single field but we want to be able to set individual captions for each of the images.

Not sure if this is the right way to phrase the question, how would I go about moving the contents of postbody.block.caption to postbody.block.image.caption for every image in that field?

This is going to be a once off migration of content so I'm not sure if it warrants a plugin, would I be able to do this with a quick & dirty sql/yii query?

Here is what I have so far

<?php

$craft = require '../craft/app/bootstrap.php';

$criteria = $craft->elements->getCriteria(Craft\ElementType::MatrixBlock);
$criteria->fieldId = $craft->fields->getFieldByHandle('postBody')->id;
$criteria->type = "image";
$criteria->limit = null;
$blocks = $criteria->find();

foreach ($blocks as $block) {
    if ($block->caption) {
        foreach($block->image as $image) {
            $block->image->imageCaption = $block->caption;
        }
        $block->caption = NULL;
        $craft->matrix->saveBlock($block);
    }
}
?>

But that gives Property "Craft\MatrixBlockModel.caption" is not defined.

Edit  I’m now at the stage where the contents of the caption field has been saved to each of the images in that block, but i cant seem to set the caption field on the block to null. Here is what I have at this stage

 $craft = require '../craft/app/bootstrap.php';

$criteria = $craft->elements->getCriteria(Craft\ElementType::MatrixBlock);
$criteria->fieldId = $craft->fields->getFieldByHandle('postBody')->id;
$criteria->type = "image";
$criteria->limit = null;
$blocks = $criteria->find();

foreach ($blocks as $block) {
    if ($block->caption) {

        $caption = "<p>".$block->getContent()->caption."</p>";
        foreach($block->image as $image) {
            $asset = $craft->elements->getElementById($image->id);
            if(empty($asset->getContent()->imageCaption)) {
                $asset->getContent()->imageCaption = $caption;
                $success = $craft->assets->storeFile($asset);
            }
        }

        $block->setContent(array(
            'caption' => '',
        ));

        $success = $craft->matrix->saveBlock($block);

        dd($success);

    }
}

I've managed to achieve something which works in the least technical way I can think of. Other solutions might involve migrating data directly within the database, but I figure this is a good starting point.

Let me know how you get on.

Create an RSS feed, ensuring that the data you want to move is populating the feed.

Here's a RSS template from the CraftCMS documentation...

Grab the FeedMe plugin...

Within FeedMe, the Entry Title is required for it to work, so, if the Entry Title is what you need to move to another field, you will need to make sure  it appears more than once within a RSS-item e.g.

        {% for entry in craft.entries.section('location').find() %}
        <item>
            <title>{{ entry.title }}</title>
            <link>{{ entry.title }}</link>
            ...
        </item>
        {% endfor %}

In my test example, I have a Channel called 'Locations' - the Title field contains names of Cities.

I want to migrate these Titles, to a new custom field called 'City name'.

Within FeedMe, create a new Feed, choosing the destination Section and Content Type. Hit the 'Save & Continue' button.
The last screen of FeedMe allows you to 'field match' (assign) the different RSS tag content, to a specific field e.g. 'Link' -> 'City name'. See image below...

In my test, I migrated field contents between two fields within the SAME Section and Content-Type.

Thoughts welcome.


How can I use CSRF protection with AJAX routes? I have CSRF enabled on my install. I have a bunch of frontend forms so CSRF is pretty important for me. However, when I use AJAX to post to a controller action I get the following error.

Bad Request

The CSRF token could not be verified.

Here is my AJAX that I am testing with.

$(function(){
    var data = { id: 100 };
    $.post("/actions/gistClient/exampleAjax", data, function(response) {
        console.log("success", response, data);
    });
});

Here is my controller action

public function actionExampleAjax()
{
    $this->requireAjaxRequest();
    // ... whatever your AJAX does...
    $response = array('response' => 'Round trip via AJAX!');
    $this->returnJson($response);
}

Is it possible to disable CSRF for certain routes? Alternatively, how would I pass CSRF through AJAX to the controller action?

Disabling the CSRF protection is probably a bad idea... it's in there for a reason. But you can easily add your CSRF data to an AJAX call, and it works very well!

The key is to make your CSRF token name & value available in your front-end JS. You can then pass that into your AJAX call as an additional value.

There are a few ways to push the CSRF data to the front-end, so this is simply one example:

{% set csrfToken = {
    csrfTokenName: craft.app.config.general.csrfTokenName,
    csrfTokenValue: craft.app.request.csrfToken,
} %}

<script type="text/javascript">
    window.Craft = {{ csrfToken|json_encode|raw }};
</script>

Once you've got those values on the front-end, adding them to your AJAX call is relatively easy:

$(function(){
    var data = { id: 100 };
    data[window.Craft.csrfTokenName] = window.Craft.csrfTokenValue; // Append CSRF Token
    $.post("/actions/gistClient/exampleAjax", data, function(response) {
        console.log("success", response, data);
    });
});

That's it! Your AJAX call should now submit properly.


Undefined property: Craft\WebApp::$cache Just ran into this error while developing a plugin.  Not doing much of anything special, just editing and renaming a few templates.

What could cause this error to happen?

Upon looking at the craft/runtime/logs/phperrors.log I noticed the following error being reported:

PHP Fatal error:  Maximum function nesting level of '200' reached, aborting! in /Users/username/path/to/website/craft/app/framework/base/CModule.php

To fix the issue, I updated the following line in my php.ini:

xdebug.max_nesting_level=200

To something higher:

xdebug.max_nesting_level=500

This error might be thrown in other scenarios as well as this fix seems pretty specific to having XDebug installed. In any case, these steps helped me get it resolved.


How to initiate a date picker when template is loaded remotely from controller action I have a controller action which returns JSON with a rendered template.  This template uses date picker form fields.  How can the date picker JS be initialized since the html is returned through JSON. I would have thought since the date picker field includes the necessary JS within {%js%} tag during template render it would be initialized. Could it be the timing of render vs adding the template html to the DOM? (Craft 3)

$view = $this->getView();

$html = $view->renderTemplate('_modal',['values' => $values, 'siteId' => $siteId]);
$headHtml = $view->getHeadHtml();

return $this->asJson(['html' => $html, 'headHtml' => $headHtml]);

Since your controller returns json you'll probably have a Javascript function that calls this controller so you can initialize it at the moment you receive the html.

Besides that you can as well include Javascript in your Craft::$app->getView()->registerJs class.


Related Entries not sorting correctly Craft 3.0.16.1

I have a relationship that is outputting entries, but the entries are not in the order as selected.

{# not sorting correctly  - **fieldName** is a variable passed via include #}
{% set relatedEntries = publications.relatedTo(
  {sourceElement: country, field: fieldName}
) %}

I copied this code from the documentation and change field names to work

{# copied from docs but template errors #}
{% set relatedEntries = craft.entries({
    relatedTo: { sourceElement: country, field: fieldName },
    orderBy:     "sortOrder",
    limit:     null
}) %}

but this throws a database exception error

  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sortOrder' in
  'order clause'

How do I get related entries to sort on the order as selected in the entries field?

********************UPDATE**********************

HERE IS THE FULL TEMPLATE

      {{ categoryTitle  }}

      {# start tabbed content #}

       {# inside categories list countries #}
      {% for country in countries.all() %}

{{ country.title }}

      {% endfor %} {# end countries loop #}

        {% for country in countries.all() %}

            {# finally output entries that are in the category and country #}

            {# not sorting correctly #}
            {% set relatedEntries = publications.relatedTo(                   {sourceElement: country, field: fieldName}
            ) %}

            {# error about method ('all') on an array #}
            {# {% set relatedEntries = country[fieldName].all() %} #}

            {#
            // copied from docs but template errors
            {% set relatedEntries = craft.entries({
                relatedTo: { sourceElement: country, field: fieldName },
                orderBy:     "sortOrder",
                limit:     null
            }) %} #}

            {% for entry in relatedEntries.all() %}

              {% for block in entry.Documents.all() %}
                {% if block.externalLink|length %}
                  {# use external link field #}
                  {% set link = block.externalLink %}
                {% else %}
                  {# use uploaded file #}
                  {% set link = block.pdfOrFile.one().url ?? null%}
                {% endif %}

{{ entry.title }}

              {% endfor %}{#end link #}

              {% else %}
              {# output nothing for no results #}
            {% endfor %}

        {% endfor %}

      {# end tabbed content #}

When you include templates and pass multiple different variables to them with different types that may have a different field layout it is required to code defensively and check for variable types. Actually you should never print something without further checking. Anything bad that could happen will happen. Especially in such a case

{% if country[fieldhandle] is defined and country[fieldhandle] is not empty and  %}
    {% for element in country.getFieldValue(fieldhandle).all() %}
        {{ element.title }}
    {% endfor %}
{% endif %}

You may as well include a instance of check

{% if element.getFieldValue(fieldHandle) is instance of('craft\\elements\\db\\ElementQuery') %}
    {% set relatedElements = element.getFieldValue(fieldHandle).all() %}
{% else %}
    // eager loading, the result is an array, no need to fetch it
    {% set relatedElements = element.getFieldValue(fieldHandle) %}
{% endif %}


Filtering entries by multiple URL parameters on the front end This answer here helped me get multiple same-name parameters to filter entries:
(ex: ?recipe=healthy&recipe=salad&recipe=dinner)

Now I'm looking for a way to get multiple different parameters to help sort entries on the front-end. Something like:
?collection=healthy&ingredient=salad&meal=dinner

The code I have now is this (pretty much verbatim what came from the answer linked above):

{# Get search parameters from the URL query string #}
{% set collectionQuery = craft.request.getParam('collection') %}

{# Start an array of search parameters (relationParam) with 'and' to match ALL categories #}
{% set relationParam = ['and'] %}

{# Find categories that match the slugs in your query string... #}
{% for querySlug in collectionQuery %}
  {% set collectionCategory = craft.categories.group('recipes').slug(querySlug) %}

  {# And merge them into the relationParam array. #}
  {% set relationParam = relationParam|merge({targetElement: collectionCategory}) %}
{% endfor %}

{# Avoid passing an array with `['and']` only #}
{% if relationParam|length < 2 %}
  {% set relationParam = '' %}
{% endif %}

{% set searchParams = {
  relatedTo:  relationParam,
  section: 'generalContent',
  type: 'recipes'
} %}

<form action="/recipes">
  {% for category in craft.categories.group('recipes').level('1') %}
    <div style="display: inline-block;">
    <select name="{{ category.slug }}[]" onchange="this.form.submit()">
      <option name="{{ category.slug }}[]" value="">Any {{ category.title }}</option>

      {% for categoryChild in category.children() %}
        <option name="{{ category.slug }}[]" value="{{ categoryChild.slug }}"{{ categoryChild.slug in collectionQuery  ? 'selected'}}>{{ categoryChild.title }}</option>
      {% endfor %}
    </select>
    </div>
  {% endfor %}
</form>

{% paginate craft.entries(searchParams) as entries %}
    ...
{% endpaginate %}

You need to get each parameter from the URL separately and repeat the steps you already did for "collection":

Get search parameters from the URL query string
Find categories that match the slugs in your query string
And merge them into the relationParam array.


Optimising search query I was wondering if there is a better way to do a query like this, it takes a long time to execute and I know setting the parent variable is where there's a problem but am not sure of an alternative way to do it. Appreciate any and all help!

{% set searchResults = craft.entries.section(['post']).search(searchQuery).with(['catalogue', 'postImage']).where({'field_show': 1}).limit(20) %}
{% paginate searchResults as pageInfo, pageEntries %}

  {% for item in pageEntries %}
        {% set image = item.postImage[0] ?? null %}
    {% set catalogue = item.catalogue[0] ?? null %}

              {{ item.title }}

    {% endfor %}

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

                Showing page {{ pageInfo.currentPage }} of {{ pageInfo.totalPages }} pages

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

    {% for item in pageEntries %}
      {% set image = item.postImage[0] ?? null %}
      {% set catalogue = item.catalogue[0] ?? null %}
      {% set parent = craft.entries.section('parents').relatedTo({targetElement: catalogue, field: 'catalogue'}).one() %}

{{ item.title }}

{{ parent.title }}

{{ parent.date|date('l jS F Y') }} {{ parent.time and parent.archived != '1'  ? parent.time|time('g:iA') }}

    {% endfor %}

It's hard to see what you're actually trying to achieve here, but yes it's likely to do with the amount of queries you're doing on the page.
I would either re-consider the page layout (without knowing what you're trying to achieve by having two loops it's hard to judge) OR pull some, or all of the results asynchronously using javascript so the page rendering isn't blocked whilst the query is being processed.
The newly released sprig plugin may be of interest.


Assets Control Panel searches only current folder, not sub folders? When searching for an Asset in the Assets Control Panel, does it search everywhere? Does it search in subfolders? e.g., If I search for the title "image_1", does my search need to be done on the folder "image_1" is in for the search to return anything?

Correct... currently searching in Assets does not search across sub-folders.


How do I add input a value to a Craft/Twig request? This might be a stretch.

How do I (literally) modify the (Tableau API) javascript function below to print a specific fieldName ("Country") so that it can place the "Country" into the CraftCMS/Twig request below?

{% set results = craft.entries.section('posts').search('countryName:"Value Goes Here"') %}

You can't.

The Craft / Twig code runs server-side, before the page is delivered to the browser. The JavaScript runs client side, after the page is delivered to the browser.


How to reference specific asset subfolders in Craft 3 I am having trouble referencing specific subfolders in Assets in Craft 3 using Twig.  The query that I've written is still showing all content, when I only want it to show content from a specific subfolder.

The folder layout: /files/asset-volume/subfolder/file.txt

When I try this query code, I still get all of the files from the volume, when I only want the subfolder.  How do I only reference the specific subfolder?

{% set subFolders = craft.assets()
                    .volume('asset-volume')
                    .includeSubfolders('subfolder')
                    .all()
%}

Have a look at the folderId parameter. https://docs.craftcms.com/v3/dev/element-queries/asset-queries.html#parameters

To find which id your folder has, it looks like you have to look in the assets table - someone might have an easier solution.


URI not shown anymore for structures? In the entry list for structures, there used to be a column where my client could see the URI of each entry. Now that it dissapeared because of what seems to be the last update of Craft CMS, how can I bring it back? Thanks

For each entry type, there is now an easy way to manage what columns are shown. From the admin, if you choose Entries from the main navigation, and then go to the entry type in question, you should see a small cog in the bottom left:

Click that and you'll be shown a number of checkboxes with columns that can displayed. Click the URI checkbox to enable that column, and then click Save. You can also drag and drop the column names here to re-arrange them.

You should now see that column in the entry list:


Custom Email Based on Product Ordered I am looking for a way to send an email based on the products ordered.

I was thinking this needed to be done using custom order statuses. Although, this would require the ability to change the default order status based on product. And with multiple product types ordered, would require the ability to set multiple statuses to an order.

So, at this stage the ideal solution would be a custom email that is sent conditionally based on products ordered.

I have so far been unable to find a solution for this. Any chance this is possible?

This is not hard to implement in Commerce if you're ok with all the content going in one email (which I think is a better approach from the customer's point of view - we used to do this as separate emails years ago but customers got irritated that one order results in a whole bunch of emails). If you do want to trigger multiple separate emails, that's a tiny bit harder - but would not be difficult to implement in a plugin.

(Sketch of that approach - listen to onOrderComplete and loop through cart items.  For any that match your desired triggers, parse a template and then email it off with Craft's email functions - simple stuff as far as plugins go - let me know if you want me to expand on this).

But back to the one email approach - your order emails (receipts or any triggered by any other status) - are full twig templates - that means you can use conditionals, includes etc, to achieve what you want.

Basically, in your email template, you can loop through the products in the order and set content into some 'storage' variables (with the content being directly written in that template, or stored in fields on your products, or stored in entries in a section, or using include of other templates - anything really!).... and then output that content later in the email.

We do this to produce a 'Super Receipt' - basically a receipt that includes a whole lot of additional 'How To Get The Best From Your XXX' type content.  This content comes, in our case, from 3 levels - the product category for stuff that applies to e.g. all monitors, the brand, for brand specific stuff (how to get support from NEC, for example), and finally from product specific content.  This allows for very quickly building up libraries of this content, and a nice hierarchy of concerns.  Categories, Brands and Products each have a field autoResponder that holds this content.

Clients love it.

Here's the code.

    {# Store items we've seen already so we don't get duplicate AR content #}
    {% set seenItems = [] %}
    {% set seenProductCategories = [] %}
    {% set seenProductBrands = [] %}

    {# And store the actual content we need to print out at the end in groups #}
    {% set arProductContent = {} %}
    {% set arBrandContent = {} %}
    {% set arCatgeoryContent = {} %}

    {% for lineItem in order.lineItems %}

        {# No need to look at the same products twice... #}
        {% if lineItem.purchasable.product.id not in seenItems %}

            {% set seenItems = seenItems|merge([lineItem.purchasable.product.id]) %}

                {# Category AR content #}
                {% if lineItem.purchasable.product.id not in suppressedIds %}
                    {% for category in lineItem.purchasable.product.categories %}
                        {% if category.autoResponder|length %}
                            {% set arCatgeoryContent = arCatgeoryContent|merge({(category.title):category.autoResponder})  %}
                        {% endif %}
                    {% endfor %}
                {% endif %}

                {# Brand AR Content #}
                {% set brand = lineItem.purchasable.product.brand.first() %}
                {% if brand|length %}
                    {% if brand.id not in seenProductBrands %}
                        {% if brand.autoResponder|length %}
                            {% set seenProductBrands = seenProductBrands|merge([brand.id]) %}
                            {% set arBrandContent = arBrandContent|merge({(brand.title):brand.autoResponder})  %}
                        {% endif %}
                    {% endif %}
                {% endif %}

                {# Product AR Content  #}
                {% set product = lineItem.purchasable.product %}
                {% if product.autoResponder|length and product.type != "service" %}
                    {% set preamble = 'See also: ' ~ product.title ~ ' wiki' %}
                    {% set arProductContent = arProductContent|merge({(product.title): preamble|raw ~ product.autoResponder})  %}
                {% endif %}

         {% endif %}

    {% endfor %}

    {# Now, print it all out, most general & needed to most specific #}

    {% for key, content in arCatgeoryContent %}
        {{ emailLib.paraWithHeader ( key, content|raw )}}
    {% endfor %}

    {% for key, content in arBrandContent %}
        {{ emailLib.paraWithHeader ( key, content|raw )}}
    {% endfor %}

    {% for key, content in arProductContent %}
        {{ emailLib.paraWithHeader ( key, content|raw )}}
    {% endfor %}


loop.index is not returning correct number inside nested loop with inline if conditional I'm getting incorrect loop.index results with the following code. It returns "1" for each entry returned. Is there a different way this needs to be written?

{% for order in orders %}
    {% for refund in order.transactions if refund.type == "refund" %}
        {{ loop.index  }}
    {% endfor %}
{% endfor %}

I'm guessing you want to display the loop index of the outer for loop.

To do that, Twig provides a loop.parent context.

{% for order in orders %}
    {% for refund in order.transactions if refund.type == "refund" %}
        {{ loop.parent.loop.index  }}
    {% endfor %}
{% endfor %}

If you're looking to get the total number of times the inner loop gets hit, then something like this should work:

{% set loopCount = 0 %}

{% for order in orders %}
    {% for refund in order.transactions if refund.type == "refund" %}
        {% set loopCount = loopCount + 1 %}
        {{ loopCount  }}
    {% endfor %}
{% endfor %}


Your server doesn’t meet the following requirements to run Craft Here's the error I get: "You have an insecure version of crypt installed. Please update PHP to 5.3.7 or later". I have PHP Version 5.3.2 installed on my Ubuntu server and cannot switch to a new version no matter what I do. I also tried to php5enmod mcrypt and it gives me command not found. I did the classic

sudo apt-get update;
sudo apt-get install php5;
sudo apt-get install php5-mcrypt;

Can you guys help? My phpinfo() doesnt show mcrypt as being installed even though it is installed actually. I am out of ideas

I would install PHP via a third-party repository. I use Ondřej Surý's and I'm pretty happy with it. It installs PHP 5.5.

https://launchpad.net/~ondrej/+archive/ubuntu/php5


How to access a previous matrix field value I have a case where I need to access a previous matrix field's value (if it exists) in the next matrix loop result.

{% for contentBlock in entry.contentBlocks %}
    {% if contentBlock.prev %}
        {% if contentBlock.getPrev().matrixField | length %}
            {{ contentBlock.getPrev().matrixField }}
        {% endif %}
    {% endif
    ....
{% endfor %}

I know that my issue is with {{ contentBlock.getPrev().matrixField }} but I am not sure how to access the field value after trying several methods.  I'm sure it's something simple.

Thanks :)

As it turns out, you must use bracketed and not dotted syntax for matrix fields.

{% for contentBlock in entry.contentBlocks %}
    {% if contentBlock.prev %}
        {% if contentBlock.prev[matrixField] is defined %}
            {{ contentBlock.prev[matrixField] }}
        {% endif %}
    {% endif
    ....
{% endfor %}

fixed this issue for me :)


How can I update a custom Order field in a plugin controller? I have a custom plugin in which I have a controller action which I want to validate some values and then update a custom field on the Cart.

I can get the values from the form and validate them, I'm just missing how to get them saved to the Cart.

I've tried the following:

$this->cart->setRawPostContent('orderShippingOptions', $myValue);
$this->cart->setContentFromPost('fields');

And also just:

$this->cart->setContentFromPost(['orderShippingOptions' => $myValue]);

I'm feel like I'm in the right area, I'm just missing something simple.

Any help gratefully received.

Turns out I really overthought this one! It's exactly the same as you would usually do it in Craft.

$this->cart->getContent()->setAttributes(array(
    'orderShippingOptions' => $myValue
));

craft()->commerce_orders->saveOrder($this->cart);

The bit I was missing was craft()->commerce_orders->saveOrder($this->cart);


Dynamically Extend Layout Does anyone know a proper way to DRY up an entry template so that it can dynamically become an item listing vs. an entry page? For a (non-working) example:

{% if item is defined %}{% else %}{% extends [entry.locale ~ "/layout", "_layout"] %}{% endif %}

Not sure why this isn’t working, but it would allow creating one template per section/type and then conditional output based on how you called it in a template, i.e. {% include "entry" with { "item": "small" }

Instead of having to create a separate template to output a smaller/truncated version of an item in a list.

Can't be done. In short, a template is either extending another template (i.e. it contains an {% extends %} tag anywhere in the code), or it doesn't. Twig doesn't permit the {% extends %} tag inside an {% if %} statement, which makes "conditional extending" impossible.

Using a ternary operation to conditionally select which template to extend is allowed, though:

{% extends item is defined ? "_item" : [entry.locale ~ "/layout", "_layout"] %}

Not sure how you apply the concept of {% extends %}, but personally, I very rarely have more than one "layout" template, and that layout template is fairly stark – usually it'll only contain the stuff that all pages across the site should have, i.e. the basic HTML structure (, global header/footer etc) and global CSS/JavaScript). Sure, some designs require more layouts, but usually that's not where the DRY stuff happens – instead, I tend to use "partial" templates (and macros) a lot, to re-use markup.

Assuming your _layout.html contains the basic HTML framework for your site, and that the entry page and an entry inside a listing is basically the same thing (the former just displays more content), you could do something like this to create a template that works for both listings and single entries:

{# Template /news/index.html #}
{% extends '_layout.html' %}
{% block main %}
    {% if entry is defined %}
        {# Single entry #}
        {% include '_partials/entry' %}
    {% else %}
        {# Listing, pull entries and include the entry partial with a "teaser" flag #}
        {% set entries = craft.entries.section('news') %}

            {% for entry in entries %}

{% include '_partials/entry' with { teaser: true } %}

            {% endfor %}

    {% endif %}
{% endblock %}

{# Template /_partials/entry.html #}
{% set isTeaser = teaser is defined and teaser %}

{{ entry.title }}

    {% if entry.thumbnail|length %}

    {% endif %}

{{ entry.excerpt }}

    {% if isTeaser %}
        {# Just a teaser – display a "Read more" link #}
        Read more
    {% else %}
        {# Not a teaser – render the content in full #}
        {{ entry.body }}
    {% endif %}


Craft CMS 2 Task progression In Craft CMS 2, I've created a task for updating Craft Commerce products from an Excel file. Everything's working correctly except the UI progression bar. The progression bar in control panel stays at 0 %, even if in database, I see steps increasing. Then when task is finished, it goes directly to the end (100 %) and disappear. I noticed that it will happen if I call for

craft()->commerce_products->saveProduct($product);

If I comment this line, progression works correctly. Any idea on what's going wrong?

As far as I know you should manually set the progress value. The task can't determine the progress by itself, unless you are returning/polling this data.

An example of how we use it, for sending activation mails to multiple users;

public function execute($queue)
{
    // creation date of user
    $days = ($this->data['creationDate']) ? $this->data['creationDate'] : 7;

    // get pending users
    $dateFilter = (new \DateTime( $days . ' days ago'))->format(\DateTime::ATOM);
    $users = \craft\elements\User::find()->status('pending')->dateCreated(" < {$dateFilter}")->all();

    // loop
    $totalUsers = count($users);
    $currentUser = 0;

    foreach($users as $user) {
        // send mail
        $result = $this->sendReminderMail($user);

        // update progress
        $this->setProgress($queue, $currentUser++ / $totalUsers);
    }
}

setProgress is used to update the progress, which will be visible in the CP.


How do I import database using the CMS Craft 3 We have previously used the Schematic plugin for database import inside a Docker container for a Python script. This is the command used for the importing,

$/data/craft/craft schematic/export --file=schema.yml

The /data/craft/craft means we run the command inside the preceding folder and the command itself is schematic/export --file=schema.yml. The file schema.yml has the database definition.

How do you import and export the databases using the Craft 3.1.x?

Now we use the Craft 3.1 which doesn't work with the Schematic.  I have this project config file after we upgraded to the Craft 3,

dateModified: 1552904208
plugins:
  aws-s3:
    edition: standard
    enabled: true
    schemaVersion: '1.2'
  redactor:
    edition: standard
    enabled: true
    schemaVersion: 2.2.2

For syncing your database in Craft 3 you can use the Project Config please read the instructions in the link how to use it and what it does

  Once that’s enabled, Craft will start storing the project config in a config/project.yaml file. Any time something changes that is managed by the project config, that file will get updated to match. And any time Craft detects that project.yaml has been updated on its own (e.g. if it was changed in a Git commit that was recently pulled down), any changes in it will be propagated to the local Craft install.

So store the file in your directory and Craft will do the rest


Filtering dynamic criteria with AND I have a form that is set up to include multiple values with checkboxes. I pass all those values into an array

{% set specsQuery = craft.request.getParam('specs') %}
{% set productSpecsCategory = craft.categories.group('productSpecs').slug(specsQuery) %}

My problem is the code below is seeing those values as OR instead of AND. I'm assuming it's because it's all put on the one targetElement line. The amount of criteria a user can select is essentially infinite (as in the max right now is 5 but more categories can be added in craftcms admin area and are created dynamically on the site as more options.

{% set entries = craft.commerce.products('footwear').relatedTo('and',
      { targetElement: productSpecsCategory, field: 'productSpecs' }
) %}

edit: I have tried

{% set entries = craft.commerce.products('footwear').relatedTo(['and',
  { targetElement: productSpecsCategory, field: 'productSpecs' }
]) %}

edit: This works with 2 criteria, but no more or less (obviously). Is there a way to loop the targetElement?

{% set entries = craft.commerce.products('footwear').relatedTo([ 'and',
  {targetElement: productSpecsCategory[0], field: 'productSpecs'},
  {targetElement: productSpecsCategory[1], field: 'productSpecs'}
]) %}

This is untested as I dont have anything set that meets these conditions to try it out in, but I think you can use something like this:

{% set targetElements = ['and'] %}
{% for category in productSpecsCategory %}
    {% set targetElements = targetElements|merge([{ targetElement: category }]) %}
{% endfor %}

{% set entries = craft.commerce.products('footwear').relatedTo(targetElements) %}

Inspired by this answer.


How to get my license.key after deleting it via Git I purchased a Craft Client license for a staging server we have, which uses Git for deployment.

Unfortunately, I forgot to copy the license.key file to my local copy of the site before my last git push, and it got clobbered.

Is there a way to retrieve my license? I have contacted Craft support, which I assume is the only way to get it back, however if there is a way to get it back on my own that would be helpful.

Right now, contacting support@buildwithcraft.com is the only way to retrieve a lost license.key file.

In the future, once we add user accounts to buildwithcraft.com, we'll also be adding order and license management, so you'll be able to retrieve it from there as well.

I've responded to your ticket with your proper license.key file.


Query when installing plugins When installing a plugin from the plugin store I get a popup telling me:
"The git command requires the command line developer tools. Would you like to install the tools now?"
"Choose Install to continue. Chose Get Xcode to install Xcode and the command line developer tools from the app store."
Options are "Get Xcode", "Not now" and "Install"
I tried just clicking Install, and it seems to have worked but I got another prompt like above at the end. So I don't know if something will fail sometime down the line or not.
After installing on my local Mamp setup it's not obvious which files I need to upload to the live server as there isn't a plugin folder. I only installed 2 plugins and 12 folders in the Vendor folder showed updated files in them.

I don't use git, I don't use the command line and I didn't install Composer and I don't really want to in the future either. I just want to keep things simple. Does this mean that I won't be able to use Craft now unless I become a command line expert?
Even the Mijingo video tutorials which have been really easy to follow in the past have started to be full of complicated command line stuff.
I have a rubbish memory and only use a few keyboard shortcuts as I can't remember more than 3 or 4, so remembering all those command line options isn't going to happen.

Going to mark this as off-topic (Craft issues should be posted to https://github.com/craftcms/cms/issues, which I realize is a bit ironic to tell you considering you said you don’t want to use Git, but you don’t need to use Git to open a GitHub account).

That said, the issue is that Craft 3 uses Composer behind the scenes to install plugins and update itself right now. So even though you don’t need to directly interface with Composer now that the zip option is available, the system you are running on must at least be able to run Composer. And Git is a requirement of Composer.

For this and other reasons, we are planning on making Craft 3 less reliant on Composer in a future update.

  After installing on my local Mamp setup it's not obvious which files I need to upload to the live server as there isn't a plugin folder.

You need to upload the entire vendor/ folder.

  Does this mean that I won't be able to use Craft now unless I become a command line expert?

As long as your system is capable of running Composer (which it sounds like you’ve taken care of), no you shouldn’t need to run any commands from the terminal yourself.

  Even the Mijingo video tutorials which have been really easy to follow in the past have started to be full of complicated command line stuff.

Up until the Craft 3 GA launch on April 4, the only way to install Craft 3 was via the terminal. Now that a zip download is available from https://craftcms.com, that’s not the case anymore.


Test for entry status I want to test for the existence of disabled entries for the logged in author in a section. So far I have this:

{% set profileEntryQuery = craft.entries()
    .section('profileEntry')
    .status(null) %}

{% set entries = profileEntryQuery.all() %}

{% for entry in entries %}

  {% if currentUser.id == entry.author.id %}

But the status(null) checks for disabled and enabled entries. How can I get a positive if only disabled entries exist but not enabled.

status('disabled') should do the trick for you

{% set entries = craft.entries()
    .section('profileEntry')
    .status('disabled')
    .all() %}


Only display the first tier of categories I'm looking to only display the top level parent categories on my Template, but unfortunately, everything I try seems to error out and still display all of the children categories as well.

My current code looks like the following:

  {% set categories = craft.categories.group('discounts') %}
        {% for category in categories.all() %}

You can filter elements based on their hierarchy level with the level attribute

{% for category in categories.level(1).all() %}

Will only show top tier categories

{% for category in categories.level(2).all() %}

only all the first tier children of all first tier categories (so tier 2) and so on.

Additional note

You can also search for descendants elements

element.getDescendants(1) //<-- include the level based on the parents level

With the parameter  1 it is the same as

element.getChildren()


How to add a custom button at the right section of an entry? I would like to add a custom button in the right section of a channels entry form in the CMS.
The this button needs to go to a custom url of the plugin that I have created. The link should contain entry ID so that I can use this ID in my plugin.

You can add custom HTML in exactly the spot you’re hoping to, using the (undocumented) cp.entries.edit.right-pane template hook.

To use it, add this to your plugin’s primary class:

public function init()
{
    craft()->templates->hook('cp.entries.edit.right-pane', function(&$context) {
        /** @var EntryModel $entry **/
        $entry = $context['entry'];

        // Make sure this is the correct section
        if ($entry->sectionId == 5) {
            // Return the button HTML
            $url = UrlHelper::getUrl('some/path/'.$entry->id);
            return 'My Button!';
        }
    });
}


Two sections sharing one category group Just getting starting with templating so apologies if this is obvious. Couldn't find a solution through search.

I have set up two sections. These contain different sets of information.

I have one category group that lists UK counties like so..

Country (Parent)
 - County (Child)
 - County (Child)
 - County (Child)

Both sections have this category group as a category field.

Now I want to create a Category page, but that only lists the entries for the section you are currently viewing.

Lets say for example you are viewing a list of entries, or single entry in section 1.
On an index page I'm displaying the category like so..

{% for category in entry.county.level(2) %}{{ category.title }}{% endfor %}

and on a single page like so...

{% set category = entry.county.last() %}
{{ category.title }}

Now I want to link to the category page plate that will show a list of entries within that category.

I have my category group set up like so...

Now I'm not quite sure how to set this up so that it will only show the relevant sections entries.

Does that make sense?

Please ask if it's not clear and I can hopefully clarify!

Thanks.

I'd say you want to look at the relations https://craftcms.com/docs/relations#the-relatedTo-param

You would then use craft.entries.section( 'sectionHandle' ).relatedTo( category ) to grab all entries related to the category you are viewing.


Arrow functions in twig or craft cms I tried to use reduce function in template, but got error. It seems twig/craftcms can not work with such functions.
Reduce function docs: https://docs.craftcms.com/v3/dev/filters.html#reduce

My example:

{% set data = craft.entries.section("faqChannel").orderBy('faqOrder').all() %}
{% set types = data|reduce((acc, v) => v.faqType is not in acc ? acc|merge([v.faqType]) : acc, []) %}

Error:
Twig Syntax Error – Twig_Error_Syntax
An opened parenthesis is not properly closed. Unexpected token "punctuation" of value "," ("punctuation" expected with value ")").

Twig didn’t add arrow function support until v2.10.0, so you need to be on at least Craft 3.1.29 if you want to use them.


How can I test my 500 error page works I had a problem with my cloud server and all my sites were generating 500 errors. The response page the visitor saw varied between Craft 2, Craft 3 and ExpressionEngine 2 sites.
The Craft 3 ones showed:

  CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]:
  General error: 1 Can't create/write to file '/tmp/#sql3fa0.MYI'
  (Errcode: 28)

Some showed what I think is the generic cPanel page:

  Internal Server Error

  The server encountered an internal error or misconfiguration and was
  unable to complete your request.

  Please contact the server administrator at webmaster@scottish6days.com
  to inform them of the time this error occurred, and the actions you
  performed just before this error.

  More information about this error may be available in the server error
  log.

  Additionally, a 500 Internal Server Error error was encountered while
  trying to use an ErrorDocument to handle the request.

Others a blank page.

I now want to have a nice/useful/descriptive 500 error page shown if it happens again.
So after reading up on this, in particular, the  NY Studio 107 article on error pages I want to be able to test if my new 500 error page does get shown when there is a real 500 error situation.

The hosting company suggested adding a 500.html file in the public_html, adding ErrorDocument 500 /500.html to my .htaccess and creating a script file with this in it:

<?php
ini_set('display_errors', 0);
throw new Exception('Test');
?>

and calling it in the browser, but it didn't work. Their response was:

  Sorry, in this case the HTTP 500 error is not caught by Apache in a
  way it can handle it. There are ways of doing PHP error handling in
  PHP, but it will depend on the application to do it for you.

Any suggestions as to how to test that it's all working properly?

Any chance you're out of disk space? That error shows that it can't create a file in /tmp which means either the permissions on /tmp are wrong, or potentially you're out of disk space.

c.f.: https://stackoverflow.com/questions/11997012/mysql-cant-create-write-to-file-tmp-sql-3c6-0-myi-errcode-2-what-does

As for the error page, if you just want to view the page, you can navigate to it directly... if you want to force a 500 error to happen, just change the db password in your .env to something that doesn't work.


multi-environment config for {siteURL} not working I've got a multi-environment setup working well for my database - that was easy to setup and i've had no issues getting it to work. Now i'm trying to set the siteUrl as a multi-config variable and no luck. I have this in general.php

return array(

  '.dev' => array(
    'environmentVariables' => array(
      'siteUrl'  => 'http://mysite.dev:8888',
    )
  ),
  '.org' => array(
    'environmentVariables' => array(
      'siteUrl'  => 'http://mysite.org',
    )
  )

);

and i've set "Site URL" in settings > General Settings to {siteUrl}

the problem is, now when I load the front-end of my site, it is outputting my links with {siteURL} written out as the actual links, eg

<a href="{siteUrl}/about#start">

All the documentation and posts I can find seem to imply that this is all I should have to do. Any idea what's missing?

Multi-environment configs require you to create a '*' config array, even if it’s empty because there aren’t any config settings that you need to set globally. Simply having that '*' array is how Craft is able to determine that this is a multi-environment config, as opposed to '.dev' and '.org' just being normal config settings. (See the warning on http://buildwithcraft.com/docs/multi-environment-configs.)

So this should work for you:

<?php

return array(

    '*' => array(
        // It's OK if this is empty, as long as it's here.
    ),

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

    '.org' => array(
        'environmentVariables' => array(
            'siteUrl'  => 'http://mysite.org',
        )
    )
);


Accessing a Matrix field in a Global I have created a Global with the handle 'footer', which contains a Matrix field 'links' and I'm trying to get this into a template, but I can't seem to get this to work.

The 'links' Matrix contains a Block Type 'navigation' with a Plain Text field 'navigationHeadline' and a Table field 'navigationLinks'. The table field contains 'linkLabel' and 'linkUrl' columns (see attached image).

I'm ultimately creating my navigation in a Global with a Headline and some Links and the Headline should only appear if each block has some links;

{% for block in entry.links %}
    {% if entry.navigationLinks | length %}

{{ block.navigationHeadline }}

        {# Show the navigationHeadline if it has links #}

            {% for row in entry.navigationLinks %}

{{ row.linkLabel }} - {{ row.linkUrl }}

            {% endfor %}

    {% endif %}
{% endfor %}

I wondered if anyone can help me resolve my obvious errors. Thanks.

Not sure why you're referencing "entry" in your code, I'd think the code should be:

{% for block in footer.links %}
  {% if block.navigationLinks | length %}

{{ block.navigationHeadline }}

    {# Show the navigationHeadline if it has links #}

      {% for row in block.navigationLinks %}

{{ row.linkLabel }} - {{ row.linkUrl }}

      {% endfor %}

  {% endif %}
{% endfor %}

So, "links" is the matrix on your "footer" global, which you can access from anywhere. You loop over that, get a block for each matrix block, and access "navigationHeadline" and "navigationLinks" through that.


Getting Information from POST data I am trying to create a plugin that takes the POST data from an order and emails it to the end user. Which property should I use to do this? I've already tried to use getContent() and getContentFromPost().

So for example if I have

         <input type="text" name="dataName" value="">

And in my plugin I'm trying to get the value of "dataName" which property should I use?

HttpRequestService's getPost is what you're looking for:

$value = craft()->request->getPost('dataName');


"Conditional" field layout - hiding or showing fields based on another field is it somehow possible to conditionally show or hide fields in the entry editor based on the value of another previously entered field?

An example of this use would be:

A section has Lightswitch field "hasVideo"
User is populating a new entry for this section and sets "hasVideo" to the value of choice.
If user sets hasVideo to true, show a text field "productVideo" so that the user can set a link to a youtube video, but hide assets field "productImage".
If user sets hasVideo to false, how Assets field "productImage" so that the user can upload an image, but hide "productVideo" field.

This would really help from a usability perspective since the client will only be shown the fields that need to be filled based on what was filled earlier on - creating a sort of entry creation "wizard".

Naturally there might be an alternative route to achieve the same level of usability that I'm missing.

Thanks in advance!

The feature request for this has been very popular since day one, so hopefully it's something P&T adds to the core some day.

For the interim I just published a small plugin called Reasons, which will add simple field/value based conditionals to entry type field layouts - pretty much exactly like the OP describes it.

Download from GitHub: https://github.com/mmikkel/Reasons-Craft

Edit
P&T retired the UserVoice page; here is the new home for the feature request


Cannot edit entries after Redactor 2.0.1 upgrade I upgraded to Redactor 2.0.1 on Craft Pro 3.0.6, and now I'm getting this error anytime I try to edit an entry that uses Redactor.

InvalidConfigException('Redactor plugin not found: '.$plugin);

I have disabled and re-enabled the plugin to no avail.

Any assistance would be greatly appreciated. I'm unsure how to make it happy, and I haven't found anything in my Google/StackExchange searching.

I have an unfair advantage since I got to look at your logs from your support ticket, but here's the error from the logs:

  [error][yii\base\InvalidConfigException] yii\base\InvalidConfigException: Redactor plugin not found: source

If you look at the Changelog for Redactor 2.0.0, you'll see the "Source" plugin is no longer needed.

The upgrade from Redactor 1.x to 2.0 should have tried to clean up any craft/config/redactor/*.json config files you might have removing references to the "Source" plugin, but something might have went wrong (permission errors are the most common cause of that).

Regardless, if you update those Redactor config files, the error should go away.


How to only list translated categories? I have a site for a company that is established in US markets and expanding into markets overseas.  The site has products and those products are grouped into categories.

Due to the costs of translating content, the client has opted to translate some of their product content but not all of it.  Out of 8 categories of products, only 2 of those categories have been translated.

Category 1 => translated
Category 2
Category 3
Category 4
Category 5 => translated
Category 6
Category 7
Category 8

As far as I can tell, there is no way to output only a list of categories that have been translated in a specific locale. Unlike entries, Categories don't appear to have a way to be enabled or disabled - they are all active for all locales.

Am I missing anything?  Or has anybody come up with a way to display only translated categories?  Ideally, in the secondary locale, I would be able to display a list of only the two categories that have been translated:

Category 1 => translated
Category 5 => translated

Correct, categories don’t have the same localization features that entries do, as it is expected that all categories will always live in all locales. And Craft doesn’t actually keep a record of which content has been directly translated, vs. which content was just copied over to a locale when one of the other locales was edited.

If you really want to only show the categories that have been translated, you’d have to add a custom “Translated” Lightswitch field to your category group’s field layout (which would be translatable so each locale gets its own value), and then factor it into any category queries:

craft.categories.group('groupHandle').translated(1)


How do I force SSL on Craft? I've installed a ssl cert on a site and it's working. Now I want to redirect visitors coming in on http over to https

I've put this code and only this code in the htaccess file, but it's causing a redirect loop.

<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.mysite.com/$1 [R,L]

</IfModule>

My original htaccess looked like this:

<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{HTTP_HOST} ^mysite.com [NC]
RewriteRule ^(.*)$ https://www.mysite.com/$1 [L,R=301]

# 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>

Could Craft somehow be causing the redirect loop?

In the Craft CP I have the Site URL set to https://www.mysite.com

For reference, this worked:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R,L]


getPost() on array element in plugin I'm trying to get data submitted from a form using craft()->request->getPost(), but I'm not sure what to do when the form name is an array element.

For example, I have a form with:

<label for="notes">Notes</label>
<textarea rows="6" id="notes" name="fields[notes]"></textarea>

and want to use:

craft()->request->getPost('fields[notes]');

but when I do this I don't get any data from the form, and I'm not sure what to put in getPost() to let it know its an array.

It will work fine if I change the name to "notes" and use getPost('notes'), but I'd like to be able to get it working with the array.

You can use dot notation:

craft()->request->getPost('fields.notes');


How to order by related entry type field Two sections:

1) Products

2) Authors

The "Products" section pulls the author name from the "Authors" section through an entries type field.

I'd like to order the products first by authors (alphabetically), then by title.

Craft does not accept the author field handle in the

.order('authors','title').

Here is the "Products" template code:

{% set entries = craft.commerce.products.type('Products').relatedTo(category) %}
{% for entry in entries.order('author','title').limit(0) %}

What is missing?

Try something slightly different in concept...

On your Products template - /products/index.html - start with your Authors first and then look for the Products which are related to them.

Hopefully this will help you make sense of it...

{# find all author entries #}
{% set authors = craft.entries.section('authors').order('title') %}

    {# loop through all author entries #}
    {% for author in authors %}

{{ author.title }}

        {# find any products which may be related to the author #}
        {% set products = craft.entries.section('products').relatedTo(author) %}

            {# Loop through related products by this author #}
            {% for relatedProduct in products %}

{{ relatedProduct.title }}

            {% endfor %}

    {% endfor %}

Also, if your Authors will ever need to log in to Craft, then consider adding them as CMS Users instead of entries. I know this has licence fee implications, but it's just a thought.

Craft's relatedTo parameter is the key concept to understand. More info here: https://craftcms.com/docs/relations