How to list different product types separately in order summary? I have two product types, 1) Materials and 2) Services.

Is there a way on the order summary page to list out the "materials" line items first, followed by the "services" line items below? I can't seem to figure out how to access the product type from cart.lineItems.

Here is what I'm currently using but it seems a little clunky:

Products

{% for item in cart.lineItems %}
    {% set variant = item.purchasable %}
    {% set product = variant.product %}
    {% if product.type == 'materials' %}

{{ product.title }} - {{ variant.sku }} ({{ item.qty }})

    {% endif %}
{% endfor %}

Services

{% for item in cart.lineItems %}
    {% set variant = item.purchasable %}
    {% set product = variant.product %}
    {% if product.type == 'services' %}

{{ product.title }} - {{ variant.sku }} ({{ item.qty }})

    {% endif %}
{% endfor %}

You'll need to query the Purchasable in order to sort by Type.

Hope this helps. Let me know if you find a better way.

Products

{% for item in order.lineItems %}
    {% if item.purchasable.product.getType().name == 'Products' %}
        {% set variant = item.purchasable %}
        {% set product = variant.product %}

{{ product.title }} - ({{ item.qty }})

    {% endif %}
{% endfor %}

Services

{% for item in order.lineItems %}
    {% if item.purchasable.product.getType().name == 'Services' %}
        {% set variant = item.purchasable %}
        {% set product = variant.product %}

{{ product.title }} - ({{ item.qty }})

    {% endif %}
{% endfor %}

Others

{% for item in order.lineItems %}
    {% if item.purchasable.product.getType().name not in ['Products','Services'] %}
        {% set variant = item.purchasable %}
        {% set product = variant.product %}

{{ product.title }} - ({{ item.qty }})

    {% endif %}
{% endfor %}


Craft 3 - Twig search query for entry with custom EntriesField containing disabled entry I have a search form with 1 simple input (q)
I also have 2 sections: SECTION-A & SECTION-B
The search is for entries within SECTION A.
The entry I'm searching for contains a custom entriesField from SECTION B which can contain a posible disabled entry because they have an expiryDate. But they still need to be searchable when typing something the disabled entry contains. I can't figure out what to do with my query.
This is what i have so far: (works perfectly with an enabled related entry from SECTION B)
{% set entries = craft.entries.section('SECTION-A').search(searchParam).orderBy('score').all() %}

what I am trying:
{% set entries = craft.entries.section('SECTION-A').search(searchParam ~ ' status: any').orderBy('score').all() %}

But this is not working, can anybody help me out here? :)
Screenshot clarification:

When searching october (expired entry from SECTION-B), it is NOT showing me results.
When searching november (entry from SECTION-B), I get the results I am expecting

some technical information:

PHP version  7.3.22
Database driver & version    MariaDB 10.3
Craft edition & version  Craft Pro 3.5.14

The search parameter only searches the element that is the subject of your element query (i.e., the source entry) - it does not search related entries. For that you need to use the relatedTo parameter. (I'm not sure why you're getting a successful result for the November entry - perhaps the source entry also has the word November in it somewhere?).
Additionally, you'll need to use the anyStatus parameter to fetch expired entries.
So to pull this off, you'll need to conduct the search against the other section and then find related entries, i.e.:
{% set sectionBEntryIds = craft.entries.section('SECTION-B').search(searchParam).anyStatus().orderBy('score').ids() %}

{% set sectionAEntries = craft.entries.section('SECTION-A').relatedTo(sectionBEntryIds).all() %}


How to show events for "This Weekend, Next Week" Stemming from this original post I was able to show Events using a custom date field and successfully got "Today" and "Tomorrow" events to show up.

How would I go about doing some custom Math to show "This Weekend" or "Next Week" events? I'm sure I have to first write a conditional on determine what the current Day is, but not sure the best way to do this.

Twigs date_modify filter accepts every string, which is supported by phps strtotime().

These strings are called Relative Formats.

.

Examples:

In the original post you used:

{% set day = date('m/d/Y') %}
{% set nextDay = day|date_modify('+1 day') %}

but with Relative Formats you can also do something like this:

{% set var = day|date_modify('now') %}
{% set var = day|date_modify('10 September 2000') %}
{% set var = day|date_modify('+1 week') %}
{% set var = day|date_modify('+1 week 2 days 4 hours 2 seconds') %}
{% set var = day|date_modify('next Thursday') %}
{% set var = day|date_modify('last Monday') %}
{% set var = day|date_modify('next month') %}

(These are equivalents of the php-doc examples )

.

So this weekend would be:

{% set var = day|date_modify('next Friday') %}

Or Saturday, personally I start my weekends on Thursday, so it's hard to give a good example ;)

Next weekend:

{% set var = day|date_modify('next Friday +7 days') %}

Next weekend in one year, half an hour ago:

{% set var = day|date_modify('next Friday +1 year -30 minutes') %}


How do you list categories in a category group? I would like to output a list of all the categories in a category group on the front end. How do I do that?

You can fetch your categories on the front end using craft.categories, which has a group param that can be used to filter categories by a given group handle.

{% set categories = craft.categories.group('myGroupHandle') %}

If the category group is limited to a single level, you can loop through those using a basic for-loop:

    {% for category in categories %}

{{ category.title }}

    {% endfor %}

If the category group can be multi-level, you might be better off using the {% nav %} tag instead:

    {% nav category in categories %}

            {{ category.title }}

            {# Output a nested
 if this category has any children #}
            {% ifchildren %}

{% children %}

            {% endifchildren %}

    {% endnav %}


Twig array key errors I'm dumping this var but cant nail down this error:

{% set submissions = craft.tumblr.submissions %}

    {% for key,submits in submissions %}
        // KEY GIVES ERROR 1

{{ dump(submits.key)  }}

        // BLOG_NAME GIVES ERROR 2

{{ dump(submits.blog_name)  }}

    {% endfor %}

Error 1:

  Key "key" for array with keys "blog_name, id, post_url, slug, type, date, timestamp, state, format, reblog_key, short_url, summary, recommended_source, recommended_color, followed, is_anonymous, anonymous_name, anonymous_email, is_submission, liked, note_count, title, url, link_author, excerpt, publisher, can_send_in_message, can_reply" does not exist

Error 2:

  Method "blog_name" for object "Twig_Markup" does not exist

Thoughts?

EDIT: Adding scheme for craft.tumblr.submissions:

array(7) {
  [1]=>
  array(28) {
    ["blog_name"]=>
    array(1) {
      [0]=>
      string(11) ""
    }
    ["id"]=>
    array(1) {
      [0]=>
      int()
    }
    ["post_url"]=>
    array(1) {
      [0]=>
      string(64) ""
    }
    ["slug"]=>
    array(1) {
      [0]=>
      string(23) ""
    }
    ["type"]=>
    array(1) {
      [0]=>
      string(4) ""
    }
    ["date"]=>
    array(1) {
      [0]=>
      string(23) ""
    }
    ["timestamp"]=>
    array(1) {
      [0]=>
      int()
    }
    ["state"]=>
    array(1) {
      [0]=>
      string(10) ""
    }
    ["format"]=>
    array(1) {
      [0]=>
      string(4) ""
    }
    ["reblog_key"]=>
    array(1) {
      [0]=>
      string(8) ""
    }
    ["short_url"]=>
    array(1) {
      [0]=>
      string(30) ""
    }
    ["summary"]=>
    array(1) {
      [0]=>
      string(23) ""
    }
    ["recommended_source"]=>
    array(1) {
      [0]=>
      string(11) ""
    }
    ["recommended_color"]=>
    array(1) {
      [0]=>
      string(11) ""
    }
    ["followed"]=>
    array(1) {
      [0]=>
      bool(false)
    }
    ["is_anonymous"]=>
    array(1) {
      [0]=>
      bool(true)
    }
    ["anonymous_name"]=>
    array(1) {
      [0]=>
      string(13) ""
    }
    ["anonymous_email"]=>
    array(1) {
      [0]=>
      string(30) ""
    }
    ["is_submission"]=>
    array(1) {
      [0]=>
      bool(true)
    }
    ["liked"]=>
    array(1) {
      [0]=>
      bool(false)
    }
    ["note_count"]=>
    array(1) {
      [0]=>
      int(0)
    }
    ["title"]=>
    array(1) {
      [0]=>
      string(23) ""
    }
    ["url"]=>
    array(1) {
      [0]=>
      string(54) ""
    }
    ["link_author"]=>
    array(1) {
      [0]=>
      string(11) ""
    }
    ["excerpt"]=>
    array(1) {
      [0]=>
      string(11) ""
    }
    ["publisher"]=>
    array(1) {
      [0]=>
      string(11) ""
    }
    ["can_send_in_message"]=>
    array(1) {
      [0]=>
      bool(false)
    }
    ["can_reply"]=>
    array(1) {
      [0]=>
      bool(true)
    }
  }
  [2]=>

I'm not sure what submissions is, but submits inside your loop is an array so you can access its values using submits['blog_name'].


Popover on specific words in entry I need to create a popover on words to explain in more detail what that word actually means. It's kind of a dictionary for technical words.

But I would like to avoid to many queries and use it like Twig's translate filter. But the customer needs to add new descriptions to specific words from CP.

Could I please get some advice or pointer for further development?

You can use this plugin to make it possible for you customer to add new descriptions to a translation file: https://github.com/boboldehampsink/translate.


403 when editing the Homepage When I edit the homepage in the cp I get a error 403.

But only on the homepage. Very weird. The Error is:

  You don't have permission to access
  /public/admin/entries/homepage/2-homepage on this server.

That is totaly the wrong path so I guess it must be a .htaccess problem.
It's on a server where I have not much rights.

I couldn't point the url above the webroot to /public/ so I wrote (or better yet pasted) a htaccess in the webroot to redirect into /public/ and I guess thats the problem:

- .htaccess {# points to public #}
- craft
- public
-- .htaccess

In the root htaccess I wrote:

    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^domain.com$ [NC,OR]
    RewriteCond %{HTTP_HOST} ^www.domain.com$
    RewriteCond %{REQUEST_URI} !public/
    RewriteRule (.*) /public/$1 [L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^([^\.]+)$ $1.html [NC,L]

And then in /public/.htaccess I wrote:

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Remove file extensions on hostfactory
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)\.html$ /$1 [L,R=301]

    # Send would-be 404 requests to Craft
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (.+) index.php?p=$1 [QSA,L]

  RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
  RewriteRule ^(.*)$ http://%1%{REQUEST_URI} [R=301,QSA,NC,L]

</IfModule>

And in /public/index.php there is $craftPath = '../craft';

Is there something strange in there? I have no idea about htacces.

If I understand this correctly, you can't put the /craft folder above the public root, so you put both /craft and /public inside public root? That's no point, and you need a lot more .htaccess vodoo to get that sorted out. But, if the craft folder has to stay inside the public webroot, you should just move it there, and not have an additional /public folder inside it. Your setup should look like this:

craft
index.php
.htaccess

In your /public/index.php file you the path to craft to $craftPath = './craft'; and use the default .htaccess file that comes with Craft (or a more complex one).


Import Plugin Entries Not Loading I am using the import plugin by Bob Oldehampsink to load entries. I have 47 fields that I'm uploading. The import shows as uploaded successfully in the admin and the file is my assets but the entries do not load.  If I reduce the number of fields the entry appears as it should. I can't find any errors related to the plugin. Any ideas as to what might be going wrong?

I was able to get to the bottom of this and the problem was that 2 columns in my CSV file had the same name.

If you have a similar problem check the "craft_import_log" table in your database. You will find the error reporting for the import plugin there. The error I received was "Columns and data did not match, could be due to malformed CSV row."

I ran my CSV through an online checker and it informed me that there were duplicate column headings. Renaming one of the columns fixed my issue.


Including a variant segment in product type template route in Commerce 2 Is it possible to use a wildcard when specifying the template path for a Commerce 2 product type?

I want to format my URLs as /type/product/variant
Where 'variant' could be a custom field applied to the product variant.

In the settings for my product type I've tried a few things, including setting the "Category URI Format" to variations of /type/{slug}/{variant.customField} all to no avail. The template isn't found as soon as I include that third segment to the URL.

I feel this should be possible to do with Routes but having much the same problem.

Have figured out that instead of setting a template path in my product type I can leave that blank and use a custom route instead.

So in this case I set up a route for

/type/*/* -> shop/variant

It means I need to fetch the product entry manually in the template but that's no problem and it seems to be working.


Updating plugin/module config via template/controller I have a plugin/module that synchronises local records with a Google Sheet managed by the client.
The URL for this doc is stored in a module config.
I want the client to be able to update this via the control panel so have created a template for editing and a controller to update the config. This is all fine locally but in the client environment where allowAdminChanges is set to false this won't save and I get the following error:
 Changes to the project config are not possible while in read-only mode.

I have tried both Craft::$app->projectConfig->set('my-module', $settings); and Craft::$app->getSystemSettings()->saveSettings('mud-module', $settings) with same ersponse.
Is there a way to update config when allowAdminChanges is set to 0/false?

In short, no. If this is config or data which are environment specific, they are as likely not good candidates for project config. Perhaps consider moving them to a table and access them through a Record/Model?


Displaying Image Assets to Home Entries I am trying to display the assets on entries.

I created the assets group.
Uploaded the bunch of images to that group.
Added assets to the entries.

Now I didn't understood how to write a code in templates/index.html to display those images.

Right now I want to understood how to display one image.

Thanks

Have you tried something like this?

{% set image = entry.myImageFieldHandle.first() %}

Or, if you want a specific image:

{% set image = entry.myImageFieldHandle[3] %}


Problem connecting to MySQL db on installing Craft 3 Having installed Craft 3 using Composer, I'm attempting to run the Craft setup. Everytime I try, I get the following error:

Testing database credentials... failed: SQLSTATE[HY000] [1044] Access denied for user 'xxx'@'%' to database 'xxx'

where xxx is the correct username and db name.
Obviously, it can't connect to the database for some reason. I have also attempted the entire install manually without Composer and get the same issue with not connecting to the db. I have also tried manually editing the .env file with the correct details after installing with Composer. No joy.

Looking at the error, I would expect the user to read: user 'xxx'@'localhost' given that I specified localhost as the server. I'm not sure why it specifies '@' instead but I suspect that is a clue.

I believe that port 3306 is used as the default TCP/IP port and that simply not specifying a port will not lead it to use Unix Socket instead. I raise this as I believe it may also be a clue to the issue - I can connect to the database using Querious (mysql database software) but only if I specify using Unix Socket instead of TCP/IP and leave the socket path blank (this using an SSH tunnel). My knowledge in this area is very shaky but I thought it might be worth mentioning in case it helps. I'm using Serverpilot.

Hopefully someone can shine a light?

Thanks for your help Brad. After going round and round in circles, deleting the database and setting up a new one sorted it. Ah the mysteries!


Make "original" images unaccessable? Is it possible to make the uploaded "original" images unaccessable for site visitors and make only the transforms public?

I'd like to upload images in full quality, to

get the best possible quality out of Craft transforms
make Craft's assets folder basically a backup for my Lighroom library
no manual transforms necessary before upload (Craft does the job anyways for all the required 2x, 1x and thumbnail sizes)

The images should be secure in that folder, otherwise I run into possible copyright problems (images would be in printable size and quality).

Since all asset transforms are saved in a sub-directory starting with an underscore, you could simply test for that.

  Original:
  /assets/folder/house.png

  Transforms:
  /assets/folder/_900x500/house.png
  /assets/folder/_thumb/house.png

Plugin:

If your asset-folder is above public_html, and the request is routed through Craft, you could create a plugin, which checks for the underscore:

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

    //Is resource request?
    if (craft()->request->isResourceRequest()) {

        //Parent directory
        $parent_dir = dirname(craft()->request->getUrl());

        //Check if parent dir doesn't beginns with an underscore
        if (!preg_match('/^[\/\\]*([_]+)/U', $parent_dir)) {
            exit(403);
        }
    }
}

.htaccess:

If your assets can be accessed directly, you'd have to use .htaccess to deny the request. I'm really bad at RewriteConditions, so this would possible be a question for StackOverflow.


Pagination returns a result after last page has been reached I understand I can use a conditional to output a 'next page' link only when next pages exist, but I am finding a problem with direct URLs.

Let's say you have 20 entries and 10 per page.

The URL /news (or /news/p1) and /news/p2 should work, but I would expect /news/p3 to show no results or a message.

What I'm finding however is that /news/p3 and beyond returns the last entry in the collection.

Here's the template code:

{% paginate craft.entries.section(listingSection).order('postDate desc').limit(2) as entriesOnPage %}

Loading the latest content...

        {% for entry in entriesOnPage %}

            {% if entry.section.handle != 'tweet' %}

                {% switch entry.tileSize %}

                    {% case "Single" %}

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

                    {% case "Double Horizontal" %}

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

                    {% case "Double Vertical" %}

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

                    {% case "Quadruple" %}

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

                    {% case "Quote" %}

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

                {% endswitch %}

            {% else %}

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

            {% endif %}

        {% endfor %}

    {% if paginate.nextUrl %}

                Load more posts

    {% endif %}

{% endpaginate %}

You can detect if the requested page is greater than the total available pages for the current list of entries like so:

{% paginate entries as entriesOnPage %}

    {% if craft.request.getPageNum() > paginate.totalPages %}

No results

    {% else %}
        {% for entry in entriesOnPage %}
            ...
        {% endfor %}
    {% endif %}

{% endpaginate %}

Rather than displaying a message, you could also choose to put an {% exit 404 %} tag in there.


What are the risks of using Gmail for sending email from Craft CMS? In Craft's documentation for email it says:

  Gmail might be a good option depending on your appetite for risk.

But I don't see anything about what the risks are. Is there a risk of getting hacked, or just an increased likelihood of messages not getting through?

I have a small and internal user base, so Gmail might work for me, but I'm trying to make a quick decision and this is making me have to dig for info so I don't make a bad call.

https://craftcms.com/guides/why-doesnt-craft-send-emails

It's mostly tongue-in-cheek, I think.

When sending emails from Craft using Gmail, Craft uses SMTP, which Gmail considers "insecure", hence the reason they make you enable the "less secure apps" from your Google account.


Is there a way to purge old data in a table? I have some tables in a matrix field and when I created the columns, originally I named them Col 1, Col 2, etc. That made sense to me but later I decided that since the client will be entering data, it'd make more sense to name them Column 1, Column 2, etc.

I renamed them but that was after I'd entered a bunch of content. So now in the Craft admin, I only see the new names, but when I loop through the entries in the table rows, all of the old entries are there as well.

Is there a way to purge that old data? I want to give the client to enter in the number of a column so you can add a highlight class to it. Right now, as I loop through the entries in those rows, it hits both old and new content and you can't get a dependable count.

Your interpretation of why you're getting duplicate columns when looping your  your table field is wrong.

Each column is always accessible via a automatically generated column handle (col1, col2, etc.), so that's not old and left over column names you're seeing there.

If you define custom handles in the Table field's settings, the cells' data will  be available via those properties as well, so you can then access the value via both row.price and row.col1. This feature is designed with direct access of columns / cells in mind, but gets in the way when looping through a row's cells.

But Craft doesn't force you to define custom handles, so you can simply remove them in your field's settings and your "old data" will be removed.


links to another website without my own url If I want to link another website, I am doing this with a normal text field.

{{ entry.subHeading }}

subHeading is the link.

If I now click on the text on my website, I get something like this: www.example.com/https://www.google.ch

How can I get my own website text out of the link?

I assume that subHeading is a plain text field.
Just a guess, are you inserting a string that can be interpreted as a relative path on your website?

Ex. google.com vs https://www.google.com

The first will lead to yoursite.com/google.com, the latter should work fine.


Multiple Matrix blocks on one page I'm having a bit of difficulty having 2 different matrix blocks show on one page, and they both have Entries fields. As soon as I put {% set entry for my first Matrix, the second Matrix disappears.

{% for block in entry.sideMenu %}
 {% if block.type == "sideMenuItem" %}
    {% set entry = block.sideLink.first() %}
      {% if entry %}
         {{ block.sideTitle }}
      {% endif %}
 {% endif %}
{% endfor %}

If you look at how Craft handles routing you will see this:

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

So Craft is assigning that entry variable that you are referencing on line 1 of your code. Then on line 3, you do {% set entry = block.sideLink.first() %} which overwrites that initial entry variable with this new value, which is why the second matrix disappears.

What you probably want to do is this:

{% for block in entry.sideMenu %}
  {% if block.type == "sideMenuItem" %}
    {% set sideLinkEntry = block.sideLink.first() %}
    {% if sideLinkEntry %}
       {{ block.sideTitle }}
    {% endif %}
  {% endif %}
{% endfor %}

And your original entry will be unmodified.


Defining TwigExtension globals in Craft 3 A long time ago I asked how I could utilise the getGlobal method in Craft 2.x.

I'd like to know how best to do this in Craft 3?

There are two ways to achieve this:

Option 1

Add this in your plugin/module main init function:

      $twig = Craft::$app->view->getTwig(null, ['safe_mode' => false]);
      $twig->addGlobal('foo', 'bar');

That's it. You can call 'foo' in your template like this {{ foo }} // Output 'bar'

Option 2 (best)

In the root of your plugin/module directory, create a folder called 'twigextensions' if you don't already have one. Create a new file where you're going to define your globals. MyGlobals.php for example.

At the very top of your main plugin/module file, you need to add this:

use modules\myawesomemodile\twigextensions\MyGlobals;

... remembering to amend the plugin/module name, and the name of the file you created.

Then add the following to the same file. Only this needs to be inside your main init function:

Craft::$app->view->registerTwigExtension(new MyGlobals());

Lastly, in your new MyGlobals.php file. Add the following. Again changning the plugin/module names where appropriate.

namespace modules\myawesomemodule\twigextensions;

use modules\myawesomemodule\MyAwesomeModule;

use Craft;

class MyGlobals extends \Twig_Extension implements \Twig_Extension_GlobalsInterface
{

    public function getGlobals()
    {
        return array(
            'foo' => 'bar',
        );
    }
}

That's it. Like before, you can call 'foo' in your template like this {{ foo }} // Output 'bar'

Now I don't understand the technical reasons as to why one is better than the other. I prefer Option 2 because it permits me to add multiple globals. However, my old post was answered by Mats Mikkel Rummelhoff; who goes into this in a little more detail.


Concurrent actions in admin I have a plugin the imports data from an external database. This is more of a onetime action. But what I've noticed is that when this process runs, I cannot do anything in the admin section anymore. (and its not CPU or apache blocking) All my other requests (on other tabs) are blocked until the long running process is done. Is this normal behaviour? If so,can that be changed with config settings?

Thanks in advance

It sounds like delegating the import to a Task would be the best way to implement this. More info in this answer:

Does Craft have a server-side queuing system?


Skipping the shipping part for custom virtual product We've a WIP that need virtual products. Not the official plugin, but a custom product type that don’t required any shipping. I was wondering if anyone ever try to do so, and how.

I was thinking about a Switch Field for that particular Product Type that will affect all the checkout process (if no product need shipping in the order, of course) : don't show the "Shipping Address" form, skip the Shipping Method choice and hide any shipping mentions on the order and emails.

Note: I already have a custom module to do custom actions on event for this product type. Extend Craft Commerce is a possibility.

There is nothing stopping you selling products that are not physical. Just mark the product as free shipping, and skip the address and shipping method selection screens entirely (the front-end is totally up to you).

p.s Product Type settings even have a 'has dimensions' checkbox you can turn off.

p.p.s check out https://github.com/craftcms/digital-products


How to pull an entry's SEOmatic meta tags with Element API? How do I get the meta tags used in SEOmatic for a specific entry and returning it through the Element API?

Not sure how the PHP code looks like when using SEOmatic's PHP API.

SEOmatic has some functions that it uses for its controller endpoints for returning metadata for SPAs and the like:

SEOmatic headless SPA APIs

We can leverage these for our own purposes in an Element API endpoint, if for some reason you can't ping the SEOmatic controller via AJAX or use GraphQL to access the data.

use nystudio107\seomatic\helpers\Container as ContainerHelper;
use nystudio107\seomatic\models\MetaJsonLdContainer;
use nystudio107\seomatic\models\MetaLinkContainer;
use nystudio107\seomatic\models\MetaScriptContainer;
use nystudio107\seomatic\models\MetaTitleContainer;
use nystudio107\seomatic\models\MetaTagContainer;

...

        $result = ContainerHelper::getContainerArrays(
            [
                MetaTitleContainer::CONTAINER_TYPE,
                MetaTagContainer::CONTAINER_TYPE,
                MetaLinkContainer::CONTAINER_TYPE,
                MetaScriptContainer::CONTAINER_TYPE,
                MetaJsonLdContainer::CONTAINER_TYPE,
            ],
            $uri,
            $siteId,
            true
        );

You'll get an array of the container information back, and you can do whatever you like with it.


How can I make a product field required? I have a drop-down of variants of a product. For example t-shirts:

{% for purchasable in product.variants %}
    <option value="">Select</option>
    <option {% if purchasable.stock <= 0 and purchasable.unlimitedStock == false %}disabled {% endif %} value="{{ purchasable.purchasableId }}">
        {{ purchasable.title }} &ndash; {{ purchasable.price|currency(cart.currency) }}
    </option>
{% endfor %}

As you can see I have "Select" as the default option, and it has no value. I can validate the form client side no problem, but I am curious if there is a way to designate that fields as required from the server-side? I can mark the field as required in the layout builder, but that is for the CP only I think.

If I do not choose an option, I get this error:

Craft\Exception
Not a purchasable ID

Which makes perfect sense since I am not passing it a valid purchasable element.

I can mark the field as required in the layout builder, but that is for the CP only I think.

Nope... marking the field as required makes it required globally (regardless of if it's coming from the control panel or the front-end).


How to overrule Craft error messages with custom setError()? I know I can set errors and notices in the CP with setError() and setNotice().
But it gets overruled by the default error message in my event.

I have a beforeDeleteEntry event which stops the deleting of an entry by using $event->performAction = false;. This makes Craft display a default error "can't delete entry". I'd like to add more to that error or show another one, but using setError() does not work. A setNotice() is shown however so I suppose the error gets replaced by the default Craft error.

Can I do something about that?

I recently asked P&T about a similar issue, although it was for the onBeforeSaveEntry event (not onBeforeDeleteEntry) -- it turns out that you need to set the error on a specific field, not the entry as a whole. I don't know if this is the case for your problem, but perhaps worth trying something like this:

$event->params['entry']->addError('title', 'Cannot delete because blah blah blah');


Radio button field no setting default checked I have two radio buttons on a field. I check one of the options as default so it's checked automatically, but when I visit the entry it's on, both of them are unchecked.

I've tried deleting and remaking and it still doesn't select default.

Has anyone had this issue?

I've got a radio button field on Commerce products that is also not visibly showing as checked. Inspecting shows the correct input is actually showing checked="". Manually adjusting this to checked="checked" makes the radio button re-appear. Doesn't help us in the meantime, but perhaps it's as simple as that to fix the issue? Safari and Firefox on Mac.


How do I order by _entry type_ when looping through entries in a template? I currently have multiple sections like so:

{% for craft.entries.section('mySection').type('oneType')... %}
   ... {% endfor %}
{% for craft.entries.section('mySection').type('anotherType')... %}
   ... {% endfor %}

But I would rather have a single loop like this:

{% for craft.entries.section('mySection').order('type, title')... %}

However, the type referred to in order() will in the DB query refer to craft_elements.type which in this case always will be Entry.

Is it possible to order by entry type?

You could use Craft's group filter for this, but you'll lose the ability to
use the paginate tag to paginate your entries by doing so.

{% set entries = craft.entries.section('mySection').order('title') %}
{% set entriesByType = entries|group('type') %}

{% for type, entriesInType in entriesByType %}

{{ type }}

    {% for entry in entriesInType %}

{{ entry.title }}

    {% endfor %}
{% endfor %}


How can I test if there are any changes in the Drag/Drop Field Layout Designer? I've created a custom element type with it's own Field Layout Designer. The details about the custom element can be modified in the same form where the Field Layout Designer exists.  There is also a chance a person navigates away from the form without saving the changes after the Field Layout Designer is updated.

For example, if someone reorders one of the fields, doesn't save, and navigates away, the change they made in the editor may never get saved.  That's fine, and they have every right to, but I'd like to trigger a popup to warn them if they are about to navigate away without saved changes.

I can do this for all input fields with a snippet like the following.  The following snippet changes the isDirty variable to true and I can trigger a popup with my message if that value is true:

$(':input').change(function () {
  isDirty = true;
});

I can't seem to figure out how to do this for the Field Layout Designer.  How can I set the value isDirty to true whenever someone reorders a field or tab, or perhaps drags anything.

I've tried to add a listener:

this.addListener($('.tab.sel.draggable'), 'mouseup', '_onMouseUp');

And test for variables such as Craft.FieldLayoutDesigner.fieldDrag and !Craft.dragger.dragging but I haven't had any success and don't feel I'm approaching the problem with the right paradigm in my head.

Any ideas on how I can test if any items have been updated in the Field Layout Designer?

As of Craft 2.2, all you need to do is add a data-confirm-unload attribute to the <form> element that wraps your Field Layout Designer:

<form method="post" accept-charset="UTF-8" data-confirm-unload>

Craft will then automatically check to see if there were any changes in the form on page unload, and give the user a chance to stay on the page (same way the Edit Entry page works).


Is there a way to auto-create a sections Site Settings and propagate when a new site is created? My customer will have lots of sections and lots of entries in each, for each site. Over time they will add new language sites.

When a new site is added, there are no sections or entries for it. So we have to then edit each section to set the URL format and template, even though they will be the same for each site.

I would like to make it as easy and quick for the customer to add new sites.

So is there a way I can automatically set these values, enable the site, and propagate entries, for every section (or even certain sections), every time they create a new Site?

Thanks

You can use the EVENT_AFTER_SAVE_SITE event to create/update the new section settings

Event::on(
    Sites::class,
    Sites::EVENT_AFTER_SAVE_SITE,
    function (SiteEvent $event) {
        if ($event->isNew) {

            ProjectConfigHelper::ensureAllSitesProcessed();
            $siteId = $event->site->id;

            // array of sections you want to propagate
            $sectionsToActivate = [
                'test',
            ];

            foreach ($sectionsToActivate as $sectionHandle){

                $section = Craft::$app->getSections()->getSectionByHandle($sectionHandle);
                if($section !== null){
                    /** @var \craft\models\Section_SiteSettings[] $siteSettings */
                    $siteSettings = $section->getSiteSettings();

                    // the site you want to clone from
                    $siteSettings[$siteId] = clone $siteSettings[1];
                    $siteSettings[$siteId]->id = null;
                    $siteSettings[$siteId]->siteId = $siteId;

                    // eventually you want to change some other settings ¯\_(ツ)_/¯
                    //$siteSettings[$siteId]->template = '';
                    //$siteSettings[$siteId]->uriFormat = '';
                    //$siteSettings[$siteId]->hasUrls = '';

                    $section->setSiteSettings($siteSettings);
                   Craft::$app->getSections()->saveSection($section);
                }
            }
        }
    }
);

Explanation: As soon as you create a new site, you'll loop all sections you want to activate and grab their "main" site settings, copy them and set the id to null in order to create a new record


Control Panel Date Format I am based in the UK Where we format dates for 17th May 2018 like so:

17/5/18

Is it possible for the Craft Control Panel to use a specified format when listing entries' post date / expiry date etc?

Craft 2

The Date/Time formatting in the Control Panel is based on what locale you're using when you're viewing the Control Panel.

If you have eb_gb as a locale defined under Settings->Sites->Locales, then you can go to your user account settings and set your "Preferred Locale" settings to "British English" and you'll get the DD/MM/YYYY format.

Craft 3

The locale you're site is currently set to and the language your viewing the Control Panel in are now separate concepts.  If you go to your user account, click "Preferences", you'll see a "Language" dropdown, which controls the date/time formatting of the Control Panel for you.

If you want to set this globally for users that haven’t yet set their preferred language then you can use the defaultCpLanguage config setting. This is also useful when migrating a site from Craft 2.x to 3.x as all existing users won’t have set that preference and it will default to en-US.


Module's control panel sub-navigation is not working properly I have a problem with setting up a CP navigation for my module.
In module's init() I declared the following:
Event::on(
    Cp::class,
    Cp::EVENT_REGISTER_CP_NAV_ITEMS,
    function (RegisterCpNavItemsEvent $event) {
        $event->navItems[] = [
            'url' => 'asset-checker-module/broken-links',
            'label' => 'Checker',
            'subnav' => [
                'broken-links' => [
                    'url' => 'asset-checker-module/broken-links',
                    'label' => 'Fehlerhafte Links'
                ],
                'unused-assets' => [
                    'url' => 'asset-checker-module/unused-assets',
                    'label' => 'Nicht verwendete Dateien'
                ]
            ]
        ];
    }
);

So I see my main navigation item, when I click on it, the subnavigation gets expanded, the first element is active. But when I click on the second item, parent item gets collapsed.
I tried to fix it by setting selectedSubnavItem in my template, it did not help.
Any ideas?

Running Craft 3.2.7.

This is working for me.
Module.php
        Event::on(Cp::class, Cp::EVENT_REGISTER_CP_NAV_ITEMS, function (RegisterCpNavItemsEvent $event) {
            $event->navItems[] = [
                'url' => 'modules/video',
                'label' => 'Video-Converter',
                'icon' => '@modules/icon-mask.svg',

                'subnav' => [
                    'inhex' => [
                        'url' => 'modules/video/index',
                        'label' => 'Project-Video',
                    ],
                    'old' => [
                        'url' => 'modules/video/old',
                        'label' => 'Old Projects',
                    ],
                ]
            ];
        });

And in the templates I am using e.g.
{% extends '_layouts/cp' %}

{% set title = 'Old Projects' %}
{% set selectedSubnavItem = 'old' %}


Ability to SUM() or GROUP BY from the template or ElementCriteriaModel After twitter conversation with Brandon Kelly, is it possible to implement something similar to the following?

craft.entries.section("mySection").sum("myCustomField")

You could do this with a plugin. Create a Variable class with a function called sum(), which accepts an ElementCriteriaModel, passes it to ElementsService::buildElementsQuery(), and then replaces the SELECT value on the resulting DbCommand object:

<?php
namespace Craft;

class FooVariable
{
    public function sum(ElementCriteriaModel $criteria, $column)
    {
        $query = craft()->elements->buildElementsQuery($criteria);

        if ($query)
        {
            return $query
                ->select("SUM({$column})")
                ->group(null)
                ->queryScalar();
        }
    }
}

With that in place, you can do this in your templates:

{% set entries = craft.entries.section("mySection") %}
{% set sum = craft.foo.sum(entries, "field_myCustomField") %}

Note that you will need to pass in the field’s full column name (with that “field_” prefix) rather than just its handle. You can figure that out by looking at your craft_content table (or craft_matrixcontent_matrixfieldhandle if it’s a Matrix field). It would have been possible to get the code to be smart enough to figure the column name out for itself, but this way is a bit simpler.


Failed to load resource: net::ERR_CACHE_MISS I've run into this error from time to time:

Failed to load resource: net::ERR_CACHE_MISS

I've seen it being thrown by several different files.  I don't know how to trigger it to better explain how to recreate the problem.

What does it mean?  What can I do to get it to disappear?

Nothing related to Craft at all, but a Chrome bug.  Has been fixed in v40 in the dev channel and will eventually make it into stable.

https://stackoverflow.com/questions/26408931/bizarre-error-in-chrome-developer-console-failed-to-load-resource-neterr-ca

But I agree, it's a really annoying bug.


Modify a field value before the element is saved I have a simple plugin with a field that extends the Craft PlainText field.

Is there some kind of function I can use to modify the field's submitted value before the element is saved?

class myField extends craft\fields\PlainText
{
    private function modifyPostedValue()
    {
        if ($PostedValue == “foo”)  {
            $PostedValue = “bar”;
            // save “bar” to database
        } else {
            // save “foo” to database
        };
    }
}

It turns out that serializeValue is what I needed.

public function serializeValue($value, ElementInterface $element = null)
{
    if ($value == 'foo') {
        $value = 'bar';
    }

    return $value;
}


How possible is it to set write permissions on storage folder on symlinked install? I have setup a site with a symlinked dir on nginx ubuntu server.

When I access the root of the site I get this:

/usr/share/nginx/html/releases/*********/craft/storage isn't writable by PHP. Please fix that.

In my structure I have a folder called current that gets symlinked to the new deployed site in releases and then current should point to that.

Can anyone shed some light on what might be happening here and how I can set the write permissions to the symlinked dir?

Thanks,
Mark

Symlinks don't really have permissions themselves.

If I had to hazard a guess, it sounds like your deployment script isn't setting the correct permissions on the craft/storage directory, maybe because they're being recreated every time you deploy?

I think you have a couple options:

set the permissions on that directory correctly (see the above link) after you deploy
change your deployment strategy a bit.

If it were me, I'd tell the build/deploy script to ignore the craft/storage directory altogether. Setup your own storage folder that won't be touched by your deploy script (say above the releases folder) after you deploy. Then just tell Craft to use that location.

What is the symlink actually pointing to?

You could really deploy anywhere on the server too, not just in the typical nginx web root. (I prefer not to even touch the default distro locations because any update could modify your web files.)

If you want to go down the path of permissions, it gets a little murkier. I don't have a ton of experience with something like Capistrano (or whatever deployment system you're using) but your deploy user needs to have sudo permission to be able to change the permissions so your PHP user—likely the same user as the nginx user but not the same as the deploy user—can write to it.

One way around this is PHP-FPM. Setup a new fpm pool with the user/group of the deploy user then add whatever user you're running nginx as to that user's group (eg usermod -a -G deploy-user www-data)

That will let nginx read files from the deploy user but let FPM write to the folder because it owns it. Then you won't have to worry as much about after-deployment permissions.

It's also cheap jail if you set it up properly.

(If you're running Apache as mod_php with nginx in front of it, it'd be a similar setup.)


Order related entries by number of categories they have in common with current entry I'm creating a list of similar entries which displays entries that share the same categories as the current entry. My code looks like this:

{% set categories = craft.categories.relatedTo(entry) %}
{% set entries = craft.entries.section('projects').relatedTo( categories ).id('not ' ~ entry.id) %}
{% for project in entries %}
    {{ project.title }}

{% endfor %}

Many of my entries have multiple categories. Is it possible to order the list so that entries with the most categories in common with the current entry are at the top? And if not, is there another solution that would do this?

You can use the Similar plugin for this. I don't know that you can do it natively otherwise.


Is it possible to change the time increments in the Date/Time field? We have a date/time field shown to render the time for a series of workshops but right now Craft only increments in 30 minutes.

Is there a way for us to render 15 minute increments?

Not currently, but it should probably be a setting on the field.

Craft currently uses jquery.timepicker (in craft/app/resources/lib/jquery.timepicker/jquery.timepicker.js) and is just going with it's default step: 30.

I'll add it to “The List”™.


How do I omit similar content in a for loop based on the first letter in title? I've got a for loop that is returning the first letter of the entry title. I have a number of entries so I almost get a complete alphabet except that there are too many of many letters. I only want one 'a', one 'b', one 'c' and so
forth. And no letter if there is no entry to correspond to it.

{% for alpha in craft.entries.section('advertisers').order('title asc') %}
    {{ alpha.title|first }}
{% endfor %}

This code returns:
A A A B B B B B C C E E E E G G H H K M M M M P P R R S S S T T U U U W W
but I want it to return A B C E G H K M P R S T U W

Are there any solutions to this?

If you need only unique letters, you can build a new array and only merge first letters that are not already in the array like this:

{% set letterArray = [] %}

{% for alpha in craft.entries.section('advertisers').order('title asc') %}
  {% if alpha.title|first not in letterArray %}
    {% set letterArray = letterArray|merge([alpha.title|first]) %}
  {% endif %}
{% endfor %}

{% for letter in letterArray %}
  {{ letter }}
{% endfor %}


How to duplicate entries How do I duplicate content within craft?

I created one article, and dont want to copy paste content from one to the other, or use one article as template for the others.

When viewing an entry, click the arrow on the save button and you'll see an option to "Save as new entry". This duplicates the entry and you can immediately begin editing the new entry.

I'm not sure if this is what you mean by "use one article as template for the others" or not. It seems you can either manually create a new entry and then copy and paste content, or use this method.


Override permissions for Craft Commerce saveProduct controller I'm creating products using a frontend form which submits to commerce/products/saveProduct but it only works when I'm logged-in. They're really complicated products which need to be created on-the-fly.

If it were my own plugin controller I was using, then I could just add protected $allowAnonymous = true; but as it isn't, how would I go about this?

You'll have to write a custom plugin and point your frontend form to save to its own controller action that allows anonymous access.

That action can either 1) duplicate Commerce's saveProduct method, which can be brittle if it changes or 2) start a session from a generic user account that has the proper permissions and forward the request onto Commerce's saveProduct method.


Loop over query results in a macro I have a macro for form fields, including select:
{% macro select(name, options) %}
  <select name="{{ name }}">
    {% for key, value in options %}
      <option value="{{ key }}">{{ value }}</option>
    {% endfor %}
  </select>
{% endmacro %}

Which I call with:
{{ macro.select('name', {'key': 'value'}) }}
This works well when I define the key and value, however, I want to populate it from my categories.
{% set categoriesQuery = craft.categories().group('categories').all() %}
How can I make {'key': 'value'} loop over results from categoriesQuery? Hope that makes sense!

To convert the results of an Element Query into a custom key/value array you can use the index filter to specify what you want to use as the keys. So for example if you want the category slugs to be the keys and the category titles to be the values you'd do it like this:
{% set indexedCats = craft.categories.all()|index('slug') %}
{{ macro.select('name', indexedCats }}

=================

output:

<select name="name">
    <option value="cat-slug-1">Cat Title 1</option>
    <option value="cat-slug-2">Cat Title 2</option>
</select>

Swap in 'title' if you want the key and value to both be the same.


Refer to specific section handle when querying an element by 'getCriteria' I can do the following to pull out the page title after parsing in just the page slug:

$page = 'about';
$entry = craft()->elements->getCriteria(ElementType::Entry)->first();
$entry->slug = $page;
echo $entry->title;

(I understand this is overkill, but an answer to this question will help me answer further questions I dare not try to explain here).

The problem is, the slug doesn't need to be unique if other sections/channels/singles use the same name, and the method above only calls out the first instance of "about".

How can I define what section is being referred to?

This will update the handle, but has no effect on the output:

$entry->section->handle = "pages";

This returns a Read Only error:

$entry->section = "Pages"

  Property "Craft\EntryModel.section" is read only.

You've got a couple of incorrect things going on in your current example.

Calling craft()->elements->getCriteria(ElementType::Entry) returns an ElementCriteriaModel.

That's what you want to set the parameters on for the entries you're searching for. Stuart sums it up well here.

By calling ->first() on it immediately, you're going to get the defaults, which for entries is by post date in descending order.

By calling:

$entry->slug = $page;
echo $entry->title;

You are actually updating the slug for the entry that was returned to be something new.

This should work for what you're trying to do:

    $criteria = craft()->elements->getCriteria(ElementType::Entry);
    $criteria->slug = 'about';
    $criteria->section = 'news';
    $entry = $criteria->first();
    $title = $entry->title;


Twig url() function returns CP path when called from plugin I'm using Postmaster plugin to handle my e-mails using a custom service for SendGrid. In my templates I use Twig's url() function to create my URLs.

When they are sent by Postmaster, craft->template->render() method creates a Control Panel url instead of a normal front-end URL. Which results in URL's not found when clicked in the e-mail.

Is this normal behaviour when the render() method is called from the control panel? Or can I override this in Twig templating maybe? The Craft docs don't mention it.

In the meantime I can create my URL's using {{ siteUrl }}, but I'm wondering if I'm missing something here.

Right, url() (and UrlHelper::getUrl(), which url() maps to) will create a URL to either a front-end page or a CP page, depending on where it is being called from.

If you want to get a front-end URL from a CP request, you can use siteUrl() instead. (You can also get a CP URL from a front-end request via cpUrl().)


Matrix fields within matrix field Is there a way to place matrix fields inside of matrix fields or also to setup a matrix field with existing fields?

We are currently trying not to have duplicate content in terms of fields, but that doesnt seem possible?

Matrix within Matrix isn't possible... yet. Brandon has stated a few times that it's part of the long-term plan, and it's really more of the UI which is a stumbling block, rather than any major technical challenge.

  ... Matrix within Matrix is still something we intend to get working at some point.

  … Matrix within Matrix will be coming eventually. The UI is tough, but the code is all in place.

In the meantime, you may want to try using the LJ Dynamic Fields plugin. It may not be exactly what you're dreaming of, but it can certainly help keep things a little DRY-er.


How to get core shipping methods in Commerce 2? How to save an order? Is there an equivalent function to commerce_shippingMethods->getAllCoreShippingMethods() in Commerce 2?

I can only find Commerce::getInstance()->getShippingMethods()->getAllShippingMethods();

I also cant find an equivalent to craft()->commerce_orders->saveOrder($order) anywhere.

Commerce::getInstance()->getShippingMethods()->getAllShippingMethods(); would be the functional equivilant of Commerce 1'scommerce_shippingMethods->getAllCoreShippingMethods() since it returns the the shipping methods defined in the database.

Commerce::getInstance()->getShippingMethods()->getAvailableShippingMethods(); will return the shipping methods in the database as well as any 3rd party defined shipping methods.


Commerce: How can I save a custom SKU from a plugin? I have a plugin that is listening to the onSaveProduct event.

In my product settings, in the "Automatic SKU Format" field I have:

{product.title}

That way I am always working the main product title.

For the SKU, I want to only use the first initial of each word - so I am doing this from my plugin:

...
$default_sku = $event->params['product']->defaultVariant->sku;
$expr = '/(?<=\s|^)[a-z]/i';

preg_match_all($expr, $default_sku, $matches);

$sku = strtoupper(implode('', $matches[0]));

...

error_log((craft()->config->get('devMode')) ? 'SKU: ' . $sku : '');

Let's say I have "My Super Product" as the title. The above is generating this: MSP

Perfect!

But, now I'm not sure how to go back and re-save the product.

Do I need to re-find the product and then set the defaultSku and then re-save it? Not sure how to proceed.

Thank you for any suggestions!

You should be able to do this the usual way of setting entry fields in a plugin I think:

        $product->setContentFromPost(array(
            'sku' => $sku,
        );
        craft()->commerce_products->saveProduct($product);

(Alternatively, you could listen to onBeforeSaveProduct -  https://craftcommerce.com/docs/events-reference#commerce_products.onbeforesaveproduct - and since the product will be saved after that anyway you could probably leave out the call to saveProduct in that case)

I don't think I've tried it with products but it certainly works with orders for much the same sort of thing.


Is it possible to change admin Actions URL (not only the URI) When Craft is in headlessMode my URLS for admin actions (ex. /admin/set-password) are set to the frontend URL (SITE_URL) instead of the Admin URL (SITE_ADMIN_URL).

Is this behavior as expected?

As my Frontend is using angular I would need to add a redirect on the NGINX server to get the links to work which is kind of a strange workaround. Shouldn't the Action urls use the Admin URL by default instead?

Current behavior:

www.example.com/admin/set-password

Expected behavior:

cms.example.com/admin/set-password

ok, just received the answer from the support. will add it here for anyone who has the same issue:

setting the baseCpUrl in the config/general.php does the trick:

'baseCpUrl' => 'https://cms.example.com/',


Element API plugin - Return message when no results are found Per this code example, it's clear that the API is expecting to return results based on an existing element...

'transformer' => function(EntryModel $entry) {
    ...
}

And when the criteria finds a matching entry, everything works great!

But when there is no matching criteria results, the API seems to simply return a generic 404 error.

How can I make the API return a message (in JSON format) instead of a 404?

EDIT:

The solution provided by @megatrond is helpful, but doesn't really nail the problem here. I'm actually using that workaround for a different problem I encountered... I needed to guarantee that the user is authorized to ping my endpoint. In implementing that solution, I created a unique access key, included it in the URL, and checked against it before potentially returning a JSON-formatted error message. Under those circumstances, I was able to run a useless query for the sake of allowing me to return JSON.

However, for the issue in this thread... The criteria will either return some results, or no results, and it's not possible to tell before allowing the query to run.

Consider this scenario...

Someone uses my endpoint to target the entry with id 42. Assuming entry #42 exists, the endpoint will successfully return data for it. But, if entry #42 has been deleted or disabled, the endpoint doesn't give me the opportunity to return an error message. Instead, it just kicks out a "404 Not Found" error. :(

Ideally, I'm looking for a fallback for when the criteria returns zero results.

Currently, Element API is hard coded to throw a 404 exception for single element endpoints, if the element criteria returns no results. There's no way to prevent this exception, add "fallback" criteria or to display an error message.

Note that the 404 exception will only be thrown if the first parameter is set to true, though. If it isn't, Element API will just return an empty dataset.

Obviously, omitting the first parameter is not a real solution to your issue. You still have no way to set a custom error message, and it would also mean that all single element endpoints would return arrays, as well as paging metadata etc, instead of a "flat" JSON object.

On that note, my opinion is that the canonical answer to this question should probably be to fork the Element API plugin and/or add a feature request.

However, if you don't want to go down that road, here's a possible workaround. It's not pretty :)

Basically, what you'll need to do is to catch the HttpException, in order to return a custom payload.

To do this, you'll need to write a custom plugin. Important: make sure that the string returned by your plugin's getName() method comes before "Element API", alphabetically. The reason for this is that you need to make sure that Craft loads your plugin before the Element API plugin (Craft loads plugins alphabetically, by name). I'll pretend you opt for the name "Awesome Plugin", which would work nicely.

Your plugin will need a controller, so create one called "AwesomeController" or the like. This controller should have a method called actionGetElements, taking the same arguments as the ElementApiController's actionGetElements method.

The only purpose of your controller is to "wrap" the ElementApiController, in order to catch the 404 HttpException fired by Element API, and echo a custom error message. Here's how that could look:

<?php
namespace Craft;

class AwesomeController extends BaseController
{

    protected $allowAnonymous = true;

    public function actionGetElements($configFactory = null, array $config = null)
    {
        try {

            // Fire up the Element API controller
            craft()->runController('elementApi/getElements');

        } catch (HttpException $e) {

            switch ($e->statusCode) {

                case 404 :

                    // It's a 404. Output a custom error message
                    JsonHelper::sendJsonHeaders();
                    echo JsonHelper::encode(array(
                        'error' => 'No element found',
                    ));
                    // End the request
                    craft()->end();

            }
        }
    }

}

Obviously, the controller will do nothing by itself – the final thing you need to do is to "hijack" the Element API routes; making them point to your controller instead of the ElementApiController. To achieve this, you'll need to create a method called registerSiteRoutes() in your plugin's primary class. Inside this method, you'll basically want to pull all the routes from the Element API plugin, changing their action value to awesomePlugin/getElements.

This is why it's important to make sure that your plugin is loaded by Craft before the Element API plugin: when an existing route is added to Craft, it'll be ignored. This means that for every endpoint in /config/elementapi.php, you can trick Craft into loading your custom controller instead of ElementApiController!:

Here's how that registerSiteRoutes() method could look:

public function registerSiteRoutes()
{

    $routes = [];

    // Get the Element API plugin
    $elementApiPlugin = craft()->plugins->getPlugin('elementApi');

    if (!$elementApiPlugin) {
        return $routes;
    }

    // Get the Element API routes
    $elementApiRoutes = $elementApiPlugin->registerSiteRoutes();

    // Set action property
    foreach ($elementApiRoutes as $endpoint => $config) {
        $config['action'] = 'awesomePlugin/getElements';
        $routes[$endpoint] = $config;
    }

    return $routes;

}

All in all – not exactly an elegant solution; but if the 404's are a dealbreaker to you, its at least a working workaround that doesn't require you to tamper with the Element API source, and the performance footprint should be minimal, too (no redundant database queries).


Getting Attribute for Homepage I have created a field for the homepage called icon_font.  I now want to access that field in my template.  What is the best way to do that?  Do I have to use a for loop (even though there is only one homepage)?  Any help is appreciated.

Thanks,

Moshe

EDIT:
Note, the template in question is NOT my homepage template, but rather a different template which relates to a different section.

First you'll need to get your homepage entry:

{% set homepage = craft.entries.slug('homepage').first() %}

Then, you can call the field you need from homepage:

{{homepage.entry_icon}}


Set Entries field in Commerce order on completion I'm trying to set an Entries field within a Commerce Order once the order completes.

I've tried using the onOrderComplete event like so;

craft()->on('commerce_orders.onOrderComplete',function($event){
    $order = $event->params['order'];

    $order->setContent(array( 'calculation' => 678 )); // magic number for testing
    craft()->commerce_orders->saveOrder($order);
});

and also;

 craft()->on('commerce_orders.onOrderComplete',function($event){
     $order = $event->params['order'];

     $order->calculation = 678; // magic number for testing
     craft()->commerce_orders->saveOrder($order);
 });

With no luck.

I have also tried setting the field value on the add to cart form with

<input type="hidden" name="fields[calculation][]" value="678">

Again with no luck (I am using MultiAdd so thought it might not be working because of that).

Can anyone point me in the right direction here?

See What is getContent() / the ContentModel, and how do I use it to get and set custom field values?

So you needed to change the method used for setting content on an element.

Also see In a plugin, how do I save an entry with a related entry

So the relation fields always expect an array of ids, even if there is a single relation.

Putting those together you should be able to do:

craft()->on('commerce_orders.onOrderComplete',function($event){
    $order = $event->params['order'];

    $order->setContentFromPost(['entryRelaionFieldName' => [678] ]);

    craft()->commerce_orders->saveOrder($order);
});


Plugin: Making a record's dateUpdated column update to CURRENT_TIMESTAMP When creating a record in a plugin it automatically creates dataCreated and dataUpdated columns.  However, it doesn't look like the dataUpdated columns is updated on update.

Is there a way to set the dataUpdated column attribute to on update CURRENT_TIMESTAMP?

Or is that column manually updated in the background when using active record's update()?

How are you inserting the data?

If you're inserting data through any of Craft's Active Record or DbCommand classes (craft()->db->createCommand()), then Craft will take of updating dateUpdated for you.

If you're using raw SQL, then you'll have to keep track of it yourself.


Using element queries in general.php config file Can i use element queries in general.php file? I wanted to set loginPath based on entry query, like this:

'loginPath' => Craft::$app->entries->section('login')->one()->url ?? false,

Hovewer, i get error:

( ! ) Fatal error: Uncaught Error: Class 'Craft' not found in C:\xampp\htdocs\xxx\config\general.php on line 40

Add this at the top of config/general.php file:

use craft\elements\Entry;

And change your loginPath value to:

'loginPath' => function() {
   return Entry::find()->section('login')->one()->url ?? null;
},


How can I purchase Craft Client or Craft Pro? Docs say 'Right within Craft. If you’re running Craft Personal or Craft Client, there’s a link at the bottom of every page in the CP, which brings up a modal window that handles the whole process.'

I just installed Craft Personal 2.3.2629 and there's no link I can see.

Go to the Settings page (/admin/settings - reached by clicking on the gear icon at the top right of the control panel) and you should see the following link at the bottom of the page:

Click on the Show me link and the modal window pops up.


Do the number of Sites in a Multi-Site have a practical limit? My use case is creating microsites for hundreds to thousands of customers, allowing them to edit/create their own site.  Currently it is set up with channels with auto-created entries for users and no permission to create or edit other's (to mimic a single).  Having similar functionality using Multi-Site
seems nicer so I can use actual singles and allow multiple users to that site.  I noticed when creating multiple sites for testing, that they are all listed on the section page to enable/disable leading me to believe that having hundreds/thousands of Sites enabled is a use-case it was not made for.  Are Multi-Sites able to handle thousands of Sites?

I'd highly discourage this.
We've seen large Craft sites in the half a million to million elements (users, entries, assets, tags, categories, etc.) range. But even those maxed out at 20-30 sites.
Craft's multi-site was designed for sites that share a common information architecture and I'd say even 20-30 sites are pushing what the UI was designed for in the control panel.


Pass active class into dropdown I have a dynamic navigation consisting out of matrix and entries fields:

This then gets printed on the frontend as a dropdown.

No is it somehow possible to add a active class to the dropdown toplevel button?

Basicaly passing the active state back up to the toplevel button.

{% for dropNavi in block.dropdownNavi %}
   {% if loop.first %}

           {{ block.seite }}

   {% endif %}

    {{ dropNavi.title }}

  {% if loop.last %}
{% endif %}
{% endfor %}

The problem is that this is a matrix field so there could be 3 or 6 or 5 dropdowns. Therefore the {{ loop.index }} does not work as a identifier. I can't see a way. Is this even possible?

I tried things like
 and various versions of this.

But stuff like this aplies an active class to all the dropdowns present. Never on the one containing the current url.

<a href="{{ page.getUrl }}" {{craft.request.getSegment(2)|lower==page.slug|lower ? 'class=is-active'|e : ''}}>
    {{ page.title}}
</a>

You can use something like this, only you compare your getSegment() with the related Title field instead of page.slug if your page in your url has the pagetitle name.


How to search based on a Multi select category field in Craft 3? I have a blog section with multi select categories. I want to filter all the blogs based on that multi select categories in the website. When I tried

 .search('category:ui-ux OR category:web-designs')

the result blogs are not accurate.

Category is a multi select field. So multiple category may assigned to one blog. I will list all these categories in my front end and need to filter blogs based on that.

How I will do this search?

For filtering entries based on relationships (categories/assets/users/tags/other entries), you need to use the relatedTo parameter, not the search parameter.

Here's an example: Filter based Search Form


Overwrite value in object in array I'm strangely having trouble with this simple Twig merge problem and can't find anything online. Simplified, let's say you had:

{% set users = [
    {name: 'Bill', age: 25},
    {name: 'Tim', age: 30},
    {name: 'John', age: 32}
] %}

How would you change, for example, Tim's age from 30 to 31?  I've tried:

{% set users = users[1]|merge({age: 31}) %}
{{ users[1].name }}: {{ users[1].age }}

But this doesn't work.

Solution

This won't work in Twig as presented in the question (see why below). Instead, users would need to be changed to an associative array (or "hash" in Twig terminology) with keys that aren't integers, and you'd have to insert a merge inside a merge to change the value:

{% set users = {
    person0: {name: 'Bill', age: 25},
    person1: {name: 'Tim', age: 30},
    person2: {name: 'John', age: 32}
} %}

{% set users = users|merge({ person1: users.person1|merge({age: 1000}) }) %}

{% for key, user in users %}
    {{ key }}: {{ user.name }}, {{ user.age }}

{% endfor %}

...which now outputs:

person0: Bill, 25
person1: Tim, 1000
person2: John, 32

If you need to change the keys' integers dynamically, you will write it like this using Twig's attribute function and using brackets:

{% set loopIndex = 1 %}
{% set users = users|merge({ ('person' ~ loopIndex): attribute(users, 'person' ~ loopIndex)|merge({age: 1000}) }) %}

The Reason Why the Question's Array Doesn't Work

Twig's merge filter uses PHP's array_merge function, and according to PHP's docs when using array_merge:

  If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended.

So trying to overwrite an existing hash in the original array

{% set users = [
    {name: 'Bill', age: 25},
    {name: 'Tim', age: 30},
    {name: 'John', age: 32}
] %}

...will just add the hash as a new, fourth item in the array, not replace the second one.  To demonstrate this clearer, we can rewrite users as a hash like this (it's considered the same as above):

{% set users = {
    0: {name: 'Bill', age: 25},
    1: {name: 'Tim', age: 30},
    2: {name: 'John', age: 32}
} %}

By trying to target the key 1 to change its hash and output users:

{% set users = users|merge([ users.1|merge({age: 1000}) ]) %}

{% for key, user in users %}
    {{ key }}: {{ user.name }}, {{ user.age }}

{% endfor %}

...we get this list:

0: Bill, 25
1: Tim, 30
2: John, 32
3: Tim, 1000

A new "Tim" user will get appended even if you make the keys strings like '0', '1', etc, or using Twig's attribute function. An issue for this was posted in Twig's github and the conversation explains it some more. You can only change a value if it's an array with a list of single values (not a hash) or if it's a key/value hash, and the key can't be an integer.


Cannot install demo site happylager I have trouble installing the demo site 'happylager'.

Windows 8.1
WAMP 3.0.4 - MySQL 5.7.11 - PHP 5.6.19 - Apache 2.4.18

I follow the instructions from https://github.com/pixelandtonic/HappyLager and when I import the .sql file I get this error message.

When I head back to happylager.dev/admin I get the installation screen, not the login screen as it is described in the repo. What do you think is the problem?

Thank you in advance

I updated phpMyAdmin to the latest stable version and it worked!


Order entries by a category field alphabetically then paginate I've tried so many different things and can't get this to work...

{% set makeCat = craft.categories()
   .group('make')
   .orderBy('title')
   .limit(21) %}

{% paginate makeCat as pageInfo, makeCars %}
{% for make in makeCars %}
{% set entries = craft.entries()
   .section('cars')
   .carStatus('Inventory')
   .relatedTo(make)
   .all() %}

    {% for entry in entries %}
       <p>{{ entry.title }}</p>
    {% endfor %}
 {% endfor %}

I'm trying to list cars with a status of 'Inventory', order them by a single category field alphabetically by car make and paginate...is this not possible in Craft?  This code gets me the closest but doesn't paginate properly.  I want the 60+ entries to paginate 21 per page but in order of the make of the car ie. BMW, Ferrari, Porsche, etc.  Not sure what I'm missing or doing wrong but any help and direction would be super!  Thanks!

In order to sort your entries by a related field you need to join the relations and the content table and sort your entries by the column you want. The it doesn't change the behavior of your paginate

{# the field ID of your relation field, you can as well grab it via #}
{# set field = craft.app.getFields().getFieldByHandle('carCategory').id #}
{% set fieldId = >> number << %}
{% set entries = craft
    .entries
    .section('cars')
    .leftJoin('{{%relations}} as myrelations', '[[myrelations.sourceId]] = [[elements.id]] and [[myrelations.fieldId]] = ' ~ fieldId)
    .leftJoin('{{%content}} as mycontent', '[[myrelations.targetId]] = [[mycontent.elementId]]')
    .groupBy('elements.id')
    .orderBy({'mycontent.title': SORT_ASC})
    .all()
%}


How I can output Craft Commerces Stripes Publishable Key in a template I would like to know how I can output Craft Commerces Stripes Publishable Key, which I define under Payment Methods, in a template?

The value I set is settings[publishableKey].

Thanks
Stefan

Like this:

{% if cart.paymentMethod.class == 'Stripe' %}
    {{ cart.paymentMethod.settings.publishableKey }}
{% endif %}


Set transformations image path When using image transforms, Craft not only doesn't seem to be outputting the transformed image, but the image path it's then giving back to the template is: <img src="http://example.com/cpresources/transforms/1" /> which doesn't exist.

I have a upload path defined, which is http://example.com/assets/uploads/ but obviously it isn't respecting that.

I can't find any docs on where to define the transformations image path so wondered if anyone could shed some light?

Craft doesn't have a setting for defining the transformations image path, they are automatically placed in a subfolder inside your created assets source' path. It could look something like this _960x540_crop_center-center_70, if you had a transform like this:

{ width: 960, height: 540, mode: 'crop', position: 'center-center', quality: 70 }

By default, the url you're seeing is what Craft will return if the transform hasn’t been generated yet. The next time the page is loaded, it should reference the actual image file. See Brandon's answer here, for a more thorough explanation.

But, if everything is working, you should see the image in your browser even if it returns the transform url. If the image isn't created, check if the file path for your asset source is correct (guess it is if you can upload images), and that url rewriting is working.


CraftCMS Hosting in Australia? Can anyone recommend a hosting company that can handle running CraftCMS based out of Australia?

I've tried contacting http://www.digitalpacific.com.au/ but they came back to me with this:

"I have had this reviewed by our technicians to ensure we recommend the correct solution. I have been advised that due to the "crypt() with BLOWFISH_CRYPT" requirement, we are unable to host this on a Business hosting plan."

I'm not in Australia, but I've seen Luke Holder (of Perth) repeatedly mention Digital Ocean. I've heard other folks describe having a good experience with Digital Ocean as well.

  Digital Ocean


How can I ensure using actionUrl in my template does not show script name in the generated URL? I have an controller action in my custom plugin that processes Paypal IPN requests. How it works is that you provide a URL for Paypal to send it's notification requests to and then process them and send an acknowledgement.

I'm using {{ actionUrl('plugin/action') }} in my template to generate the URL that I provide to Paypal, but no matter what I try I can't get it to omit the script name from the URL. $omitScriptNameInUrls set to true in config has no effect.

This is necessary, because I need to do some checking in my config file based on the URL to disable CSRF protection for only the Paypal IPN controller action, as described in this question: What's the best way to skip CSRF for plugin controllers?

actionUrl() and UrlHelper::getActionUrl() always force the script name to show up despite the omitScriptNameInUrls config setting for two reasons:

Action URLs are generally not actually visible to end users, so there’s no point in hiding “index.php” from them (computers don’t care about aesthetics).
They are often used for POST requests, and your index.php redirect in .htaccess will drop any POST data associated with the request.

That said, if you really want to create an action URL that omits “index.php”, you can do this:

{{ url(craft.config.actionTrigger ~ '/your/action/path') }}

(I don’t think your use case is valid though. Just commented on it.)


exclude list of entries from craft.entries tag I am attempting to list out all jobs that the current author has created but which are not already to other entries they've created.

I have both an array and a string containing the entry ids to exclude which I can see with these tags:

{# delete later, only here to visually show entry ids while coding #}

array: {{ excludeRelatedJobs|join(', ') }}

{# convert array to string #}
{% set excludedJobs = excludeRelatedJobs|join(', ') %}

string: {{ excludedJobs }}

However when I try to exclude the ids from the list they are not excluded.

current code that should exclude ids:

{% set careers = craft.entries.section('careers').authorId(user.id).id('not' ~ excludedJobs) %}

    {% for job in careers %}

{{ job.title }}

    {% endfor %}

If I manually exclude the id like this it works correctly:

{% set careers = craft.entries.section('careers').authorId(user.id).id('not 581') %}

As @Youniteus stated in a comment to this post the answer was related to this question

I had to tweak the join statements a bit but it's working now.

complete code here:

{# convert array to string  so we can remove these ids #}
{% set excludedJobs = excludeRelatedJobs|join(', not ') %}

{# set careers to all jobs created by author#}
{% if not excludedJobs|length%}
    {% set careers = craft.entries.section('careers').authorId(user.id) %}

{# filter out jobs already added to store#}
{% else %}
    {% set careers = craft.entries.section('careers').authorId(user.id).id('and, not ' ~ excludedJobs) %}
{% endif %}

    {% for job in careers %}

{{ job.title }}

    {% endfor %}


Multi sites: how to give each site a home single entry? I've been experimenting with Craft 3 and Multi Sites and feel like I'm missing something...

My organisation has a main homepage and multiple sub-sites. I have a Single Section called 'home' that creates the main site homepage. I want each sub-site to have an individual homepage too. Each sub-site should only be able to edit their own homepage entry.

If I enable the 'home' Single Section across all sub-sites then they can all edit the main site homepage entry. The 'Propagate entries across all enabled sites?' option isn't available for Single Sections.

So how do I achieve this? :|

It's well explained in your section settings in the CP: there is a table with the settings for each site with a hint to leave the field blank if it should be the homepage.

If you want to have a different entry as the homepage for another site create a new single section for that site.

Single Entry 1

-----------------------------------------------------
| site              | url            | template     |
-----------------------------------------------------
| english, disabled |                |              |
| german, enabled   |                | index.twig   |
-----------------------------------------------------

Single Entry 2

-----------------------------------------------------
| site              | url            | template     |
------------------------------------------------------
| english, enabled  |                | index.twig    |
| german, disabled  |                |               |
------------------------------------------------------

That way users won't be able to edit the homepage entry from other sites.
Another approach would be to limit the site permissions for your editors so they'll only be able to edit the elements which are connected to their site.


Conditional email notification on saveUser action? I have a simple task: one particular front-end form on a website is being used to update a logged in user's profile. It works. It uses the users.onSaveUser action.

I need to fire off notification emails when a particular input value is true on form submission.

My first attempt was a plugin that just watched the onSaveUser event; but while I could get that to send email, I could not seem to get access to the form fields in order to check whether the email should be sent or not. Therefore it sent emails whenever a user was saved, including when edited in the back-end.

Now I'm trying a plugin that has its own action - myPlugin/saveUser. But I am essentially duplicating the code in the existing users/saveUser function... and that makes it brittle. Is there not a way to get this done by just testing a field property and then passing everything over to the built-in users/saveUser action?

How would you do this?

Here's the simple plugin I had, which couldn't get the POST data and was therefor no good...

public function init() {
    craft()->on('users.saveUser', function(Event $event) {
        $this->requirePostRequest();

        // is in the relevant user group?
        if($event->params['user']->isInGroup('applications')){

            // check this is a submission from the front end form
            $applicationFormFrontend = craft()->request->getPost('applicationFormFrontend', 'default');

            // check the Consent field was true on this submission
            $consentGiven = craft()->request->getPost('fields[consentGiven]', 'default');

            NotifyPlugin::log("The value of applicationFormFrontend is $applicationFormFrontend.", LogLevel::Info);
            NotifyPlugin::log("The value of consentGiven is $consentGiven.", LogLevel::Info);

            if($applicationFormFrontend == '1' and $consentGiven == '1') {
                // Write Email

                ...

                // send the mail
                craft()->email->sendEmail($notificationEmail);
            }
        }
    });
}

EDIT, SOLUTION:

The reason I was not getting values for

$consentGiven = craft()->request->getPost('fields[consentGiven]', 'default');

is because it's the wrong style. It's why it was always getting 'default'. The fix is to use dot notation:

$consentGiven = craft()->request->getPost('fields.consentGiven', 'default');

The original code (as above) works with this modification.

You should be able to do something like this using the event:

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

  $testfield = craft()->request->getPost('someField');

  if($testfield)
  {
    // Send the email
  }

});


Font family choice in rich text Is there a way to choose the font family in rich text field? I want to have the option to choose from 2 or 3 font families in the rich text field. Is that possible in craft?

That depends on the RTE you're using and what kind of features it supports. With the default Redactor, it should be possible via the configs in craft/config/redactor/ folder.
Peronally I don't think a content-editor should have that ability. This should be handled with classes or different fields/elements. They can really mess up your site.


How to strip out src="" value with twig from iframe code I have have a text field for copy-pasting an iFrame code.

<iframe src="https://webpagelink.com/l/3556456/2019-03-06/34576" width="100%"
height="500" type="text/html" frameborder="0" allowTransparency="true"
style="border: 0"></iframe>

What I need help with is striping out the URL within the src="" value from the iframe. Is this possible using Twig?

Thank you for your help!

You could write a module that adds a twig extension to return the value of src from the string. That way you get to utilise PHP as per https://stackoverflow.com/questions/17017684/how-to-get-url-from-the-iframe-using-php to get the value.

https://pluginfactory.io/ is a good starting point for adding a module to Craft.

alternative very dirty approach: If the iframe markup is always going to be the same, you could do |replace(), replacing everything before the src=" and everthing after the " width with empty strings. Then replace " width with an empty string.


Custom ElementType empty getSource criteria? I'm trying to add a source on a custom element type that shows entries with an empty assigned_user_id field. This criteria is not honored and returns the same results as the "All Entries" source. Is there any way to specify a null or empty string criteria value?

public function getSources($context = null)
{
    $sources = [
        '*' => [
            'label' => Craft::t('All Entries'),
            'defaultSort' => [
                'test_plugin.dateCreated',
                'desc'
            ]
        ]
    ];

    $sources[] = ['heading' => 'Assignments'];

    $sources['unassigned'] = [
        'label'    => Craft::t('Unassigned'),
        'criteria' => ['assigned_user_id' => '']
    ];

    return $sources;
}

You can set it to ':empty:', as mentioned in the DbHelper::parseParam() documentation.


Help! My plugin is sending my email twice For some reason that I cannot figure out, my plugin is sending 2 emails to each user instead on one. It sends the email on hit of an action url. Below is the function in my controller. Any help is greatly appreciated.

public function actionSendEmail()
{

    $date = new DateTime();
    $date->modify('-7 days');

    // Format datetime for use in query
    $timezone = new \DateTimeZone(DateTime::UTC);
    $date = $date->format(DateTime::MYSQL_DATETIME, $timezone);

    $criteria = craft()->elements->getCriteria(ElementType::Entry);
    $criteria->section = 'guide';
    $criteria->order = 'postDate desc';
    $criteria->dateUpdated = '> '.$date;

    $entries = $criteria->find();

    if ($entries) {

        $user_criteria = craft()->elements->getCriteria(ElementType::User);
        $user_criteria->groupId = '9';
        $users = $user_criteria->find();

        //Send to each User
        foreach ($users as $user) {
            //Send Mail
            $email = new EmailModel();
            $email->toEmail = $user->email;
            $email->subject = 'Updated in the Last 7 Days';
            $email->htmlBody    = 'Body Goes Here';

            craft()->email->sendEmail($email);
        }

    // Redirect after sending to each user in the loop to avoid 404
    $this->redirect('/');

    }
    else {
        // do nothing and redirect to avoid 404
       $this->redirect('/');
    }
}

So, this was being caused by selecting my action url from Chrome's url complete suggestions. Apparently, this submits the page twice. If the action url is typed in, pasted in or hit by my Chron job it works as expected. Firefox url suggest selection causes no problems.


Cache entry but escape dynamic data How can I cache an entry, lets say a product card but exclude an dynamic part? Like amount of favorites / Or link to add or remove to favorite.

{% set item = craft.shortlist.item(entry.id) %}

{% cache globally using key "product-card-" ~ entry.id until entry.dateUpdated %}

   ...

   {{ entry.title }}

   ...

   // EXCLUDE THIS PART FROM CACHE
   {% if item.inList %}

    {% else %}

    {% endif %}

{% endcache %}

In EE CE Cache there you had {exp:ce_cache:escape}. How can I do this in Craft?

Made a feature request for escaping parts of cache for dynamic data.

http://feedback.buildwithcraft.com/forums/285221-feature-requests/suggestions/7412950-exclude-cache-for-dynamic-data

PLEASE VOTE :-)

You would simply not wrap it in cache tags. I don't think there's anything like that EE function to specifically exclude parts within the tag. Might be worth a feature request though?

{% set item = craft.shortlist.item(entry.id) %}

{% cache globally using key "product-card-" ~ entry.id ~ "-1" until entry.dateUpdated %}

    {{ entry.title }}

    ...

{% endcache %}

// EXCLUDE THIS PART FROM CACHE
{% if item.inList %}

{% else %}

{% endif %}

{% cache globally using key "product-card-" ~ entry.id ~ "-2" until entry.dateUpdated %}

    ...

{% endcache %}


Login - Bad Request: The CSRF token could not be verified I have just deployed an existing site to a new server. As the DNS is not pointing there yet, I have simply edited my /etc/hosts file on my Mac to point to the IP address for testing. Whilst checking through everything, it seems the site is functioning well, assets have been copied over etc. The only problem is that I am getting an error when trying to log in.

From the front end I get the message: Bad Request: The CSRF token could not be verified.

    2017/07/17 13:52:33 [error] [exception.Craft\HttpException.400] Craft\HttpException: The CSRF token could not be verified. in /home/forge/www.domain.co.uk/craft/app/services/HttpRequestService.php:1337
Stack trace:
#0 /home/forge/www.domain.co.uk/craft/app/framework/base/CComponent.php(561): Craft\HttpRequestService->validateCsrfToken(Object(CEvent))
#1 /home/forge/www.domain.co.uk/craft/app/framework/base/CApplication.php(212): CComponent->raiseEvent('onbeginrequest', Object(CEvent))
#2 /home/forge/www.domain.co.uk/craft/app/framework/base/CApplication.php(183): CApplication->onBeginRequest(Object(CEvent))
#3 /home/forge/www.domain.co.uk/craft/app/index.php(62): CApplication->run()
#4 /home/forge/www.domain.co.uk/public/index.php(19): require_once('/home/forge/www...')
#5 {main}
REQUEST_URI=/members/login
HTTP_REFERER=http://www.domain.co.uk/members/login
---

And from the craft admin login form, this comes up on the console after clicking Login

VM43:1 POST http://www.domain.co.uk/index.php?p=admin/actions/users/login 400 (Bad Request)

One point to note is that the original website was SSL encrypted but the new server hasn't had an SSL cert installed yet, because DNS is not pointing to it yet. However I have altered the /craft/config/general.php config file to reflect the http:// domain.

Any help would be appreciated.
Running latest craft 2.6.2987

I have exactly the same issue, clearing cookies worked for me


Any way to turn cookies off for the front end? Any way to turn cookies off for the front end?

I think it's only CraftSessionId being set, but still, anyone know?

Cheers,

That's PHP's session cookie and is how PHP keeps track of user data across different requests. It'll only get set if something in your template ultimately invokes any of the methods in Craft's UserSessionService or Yii's CWebUser.


Having trouble with HappyLager I setup HappyLager.devas per these instructions on GitHub, after setting up phpmyAdmin on a local dev box which was no small feat. I want to know why my Images/Assets are missing from the finished install.  Look:

I've checked my database import, which did succeed, in fact, I checked the entries/articles etc, and verified that the images do exist, and I copied the assets folder into apache:

The Article Entry does contain the image, ie skis, and every article/etc I glanced at is like this.  What did I  miss??

Use the web inspector to look at the console log and see if it can find the images
Visit that image's location in the browser. If the location is correct, but it's not rendering, you probably need to check the image's file permissions. If it's set to anything lower than 644, you won't be able to see it. Set the image's permissions to 644
Make sure your assets folder and any subfolders are set to 777 so it can generate the image transforms


Uploading local asset hangs and produces jQuery console error So I have created an Assets field and am using it in one of my Entries. However, when I try to upload an image, it just keeps loading. I am getting the following console error:

Any ideas why? Thanks!

You can use this support article to help debug failed uploads: https://craftcms.com/support/upload-troubleshooting


Hide navigation from one entry but not others I am looking to hide the main navigation for entries that have the lightswitch 'showMainNavigation' disabled. The problem I am facing is when I disable for one entry, the navigation is hidden for all entries. Here is a sample of the code:

{% set mainEntry = craft.entries.slug(craft.app.request.getSegment(1)).one() %}
{% set menuLogo = mainEntry.menuLogo %}
{% set hideMenu = craft.entries.section('mysection').showMainNavigation('not 1').one() %}

{% if hideMenu %}

        {% if menuLogo|length %}

        {% endif %}

{% else %}

        {% if menuLogo|length %}

        {% endif %}
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" onclick="myFunction(this)">

        </button>

list item 1

list item 2

{% endif %}

This line:

{% set hideMenu = craft.entries.section('mysection').showMainNavigation('not 1').one() %}

will return the first entry that it can find in the section 'mysection' that has showMainNavigation off. So as soon as you disable it for one entry, hideMenu will 'return' true, whichever page you're on.

So to fix this, if mainEntry is your current entry, then replace the line by:

{% set hideMenu = mainEntry.showMainNavigation %}


Craft Commerce product item price showing incorrect If I use the following format to show an item price:

.defaultVariant.salePrice|currency('GBP')

or

.defaultVariant.salePrice|commerceCurrency(cart.currency)

the website is returns as:

9,99 £

Instead of what is expected:

  £9.99

Has anyone ever seen this before?
I am using Craft CMS 3.28 and Commerce Pro 2.1.10.

As Jeremy Daalder pointed out, Its the local system settings causing this.
A workaround is available in the link he posted.


Get the current structure entry's order How can I get the number of a current entry in a structure? In other words, I'm getting the total number of entries like so:

{% set count = craft.entries.section('projects').total() %}
<h1>{{ count }}</h1>

But, I'd like to be able to output the number of the current entry in the hierarchy, i.e. something like 1/10. How do I do that?

If you're using a for loop to output the entries you can make use of loop.index which will give you the current iteration of the loop (starting from 1) and loop.length which is the total number of items in the for loop.

So something along the lines of:

{% for entry in craft.entries.section('projects') %}
   {{ entry.title }} - {{ loop.index }} / {{ loop.length }}
{% endfor %}

Or, if you're on a single entry you can use this answer by Marion Newlevant.


Tons of Deleting Stale Template caches My task queue (is that what it's called?) is filled with literally hundreds of "Deleting Stale Template Caches" and every day there seem to be new ones. Along there are also resaving entries tasks and updating URIs.

Because of the amount of tasks, they don't seem to be going down AT ALL. What's the suggestion here?

Bump up the PHP Memory limit and max execution time, then change the 0 in the first failed task to 1.

After that go into craft, click the tasks and hit "try again"