How to set up CloudFront for a Craft site I'm now setting myself to learn about AWS. Has anybody documented their experiences with CloudFront and Craft?

I'm running Amazon S3+CloudFront on several of my sites. There's not really much to it, Craft makes it really easy:

Create a new Amazon S3 assets source in Craft.
Set up CloudFront in AWS (see this link).
In the settings for your assets source, add your cloudfront url (something like http://xxxxxxxxx.cloudfront.net/, or your custom CNAME if you set one up) to the "URL Prefix" field.

That should be it!


What is the difference between calling .first and .first()? So here's something I've never been able to figure out about Twig. Does it make a difference if you include the parenthesis for functions or not?

In other words, what is the difference – if any – between calling

craft.entries.first

and

craft.entries.first()

?

On a more philosophical note; Put yourself in the shoes of first(). It's a method, doing an important job in the Craft eco-system. It prides itself on returning the most important and relevant data in an efficient and unencumbered way. Serving up entry after entry, asset after asset, 24 hours a day, all week, all year. Never complaining, never getting any praise.

And you want to treat it like a simple property? As a dumb, brainless reference to some data that some other method created? How do you think that makes first() feel? How would you feel if someone treated you as property?

I know you're a good person with a big heart, Mats. I'm sure you will do the right thing and treat first() with the respect it deserves, even though it means that you'll have to write a few more characters a day.


Craft Ordering breaking nth child? For some reason, when outputting 'featured' tagged items, It's ordering them I presume by alphabetical values which is breaking the nth-child CSS set on every 3rd element? Is there anyway to stop this?

Getting Tagged Categories

  {% set tag = craft.tags.slug('featured').first() %}
  {% set categories = craft.categories.relatedTo(tag) %}

  {% if categories|length %}
    {% for category in categories.limit(3) %}

        {% for asset in category.universityImage %}

        {% endfor %}

          {% for asset in category.universityLogo %}

          {% endfor %}

{{ category.description }}

    {% endfor %}
  {% endif %}

CSS

.university-widget:nth-child(3n)
{
  margin-right:0px;
}

Picture Evidence

I can't see why Craft would be breaking a CSS rule, you're outputting three items and is it outputting the 3 items on the page?

If yes, then the CSS rule might need to be fixed. If not, then I don't understand your problem.


Order entries by multiple matrix date field I have a section with trainings. In each training entry there's a matrix field where we add a new block with a training date, location, etc. So each training has multiple matrix blocks.
When I run an entry query and get all the matrix date fields inside of them, I can output it like this:

Training 1

October 1, 2020
October 5, 2020

Training 2

October 5, 2020
October 10, 2020

Training 3

October 7, 2020
October 10, 2020

Now I would like to group and order the trainings by date like this:

October 1, 2020

Training 1

October 5, 2020

Training 1
Training 2

October 7, 2020

Training 3

October 10, 2020

Training 2
Training 3

I'm close but can't get the dates sorted the right way. The template currently outputs:

October 1, 2020

Training 1

October 5, 2020

Training 1
Training 2

October 10, 2020

Training 2
Training 3

October 7, 2020

Training 3

The code I have right now:
{# Query entries that have training dates #}
{% set allEntries = craft.entries
.section('trainings')
.trainingDates(':notempty:')
.all() %}

{# Merge all training dates in an array #}
{% set startDates = [] %}
{% for entry in allEntries %}
    {% for training in entry.trainingDates.all() %}
        {% if training.startDate | date('d mm Y') not in startDates %}
            {% set startDates = startDates | push(training.startDate | date('d mm Y')) %}
        {% endif %}
    {% endfor %}
{% endfor %}

{# Show all trainings grouped by date #}

    {% for startDate in startDates %}

{{ startDate }}

                {% for entry in allEntries %}
                    {% for date in entry.trainingDates.all() %}
                        {% if date.startDate | date('d mm Y') == startDate %}

{{ entry.title }}

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

    {% endfor %}

Any idea on how to sort the array by date? And this can probably done much easier. If you have any tips, I would love to hear from you! :-)

The quick and easy answer would be to just add the Twig sort filter to your startDates array. e.g:
{# Query entries that have training dates #}
{% set allEntries = craft.entries
    .section('trainings')
    .with('trainingDates')
    .trainingDates(':notempty:')
    .all()
%}

{# Merge all training dates in an array #}
{% set startDates = [] %}
{% for entry in allEntries %}
    {% for training in entry.trainingDates %}
        {% if training.startDate | date('d mm Y') not in startDates %}
            {% set startDates = startDates | push(training.startDate | date('d mm Y')) %}
        {% endif %}
    {% endfor %}
{% endfor %}

{# Show all trainings grouped by date#}

    {% for startDate in startDates|sort %}

{{ startDate }}

                {% for entry in allEntries %}
                    {% for date in entry.trainingDates %}
                        {% if date.startDate | date('d mm Y') == startDate %}

{{ entry.title }}

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

    {% endfor %}

(I've also added eager loading to improve performance).
You could potentially attack the problem from the opposite direction by using a matrixblock element query like this:
{# Query matrix blocks that have training dates #}
{% set matrixBlocksQuery = craft.matrixBlocks().field('trainingDates') %}
{% set uniqueDatesSorted = matrixBlocksQuery.all()|map(block => block.startDate|date('d mm Y'))|unique|sort %}

{# Query entries that have training dates #}
{% set allEntries = craft.entries
    .section('trainings')
    .with('trainingDates')
    .trainingDates(':notempty:')
    .all()
%}

{# Show all trainings grouped by date#}

    {% for startDate in uniqueDatesSorted %}

{{ startDate }}

                {% for entry in allEntries %}
                    {% for date in entry.trainingDates %}
                        {% if date.startDate|date('d mm Y') == startDate %}

{{ date.id }}{{ entry.title }}

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

    {% endfor %}

...but to be honest I think I prefer your approach, as it's more readable and doesn't require an extra .all() query to get the dates


Add ordinal suffix to loop.index I'd like to add an ordinal suffix to the loop.index variable within a for loop.

For example:

1st
2nd
3rd

This can also be done with a Twig macro, rather than creating a plugin:

{% macro ordinal(number) %}
    {%- spaceless %}
    {% if ((number % 100) >= 11) and ((number % 100) <= 13) %}
        {{ number }}th
    {% else %}
        {% set ends = ['th','st','nd','rd','th','th','th','th','th','th'] %}
        {{ number ~ ends[number % 10] }}
    {% endif %}
    {% endspaceless -%}
{% endmacro %}

{% from _self import ordinal %}

{% for entry in craft.entries %}
    <li>{{ ordinal(loop.index) }}</li>
{% endfor %}


Include additional fields in Craft form notification template I'm using the Craft Contact Form template.  I need to include some additional content in the notification email.  I can't for the life of me find how to do this.

I've seen this:
P&T Contact Form Plugin: HTML Email Templates

But not sure how to apply the additional fields.  Here is my contact form template (markup removed for ease of reading).

{# form markup #}
    <select class='form__input' name="message[About]">
        {% nav link in craft.entries.section('products') %}
            <option value="{{ link.title }}">{{ link.title }}</option>
        {% endnav %}
        <option value="Services">Services</option>
        <option value="Consultancy">Consultancy</option>
        <option value="General enquiry">General enquiry</option>
    </select>

        Your email address
        <input class='form__input' id='fromEmail' name='fromEmail' type='email' placeholder='Email address' value='{% if message is defined %}{{ message.fromEmail }}{% endif %}'>

        Details
        <textarea class='form__input form__input--textarea' id='message' name='message' placeholder='Tell us more'>{% if message is defined %}{{ message.message }}{% endif %}</textarea>

{# form closing markup/submit button #}

any help, phenomenally appreciated!

Thanks
Dave

If you look at the GitHub page for the ContactForm plugin under the section for adding additional fields, you'll see that you need to change the name of the message field to message[body]. Try that to see if it helps. I would imagine leaving it as message means that it overwrites the values of the other additional fields.


Syntax for permissions in comments.php config file (comments plugin by Verbb) I'm using the comments plugin (https://plugins.craftcms.com/comments)
I'd like to define the 'permissions' settings via the config/comments.php file but can't find docs on the syntax to use.
The default reads
    // Permissions
   'permissions' => [],

which doesn't give me much to go on.
Could someone please provide an example I could work out the correct syntax from?
Many thanks!

I've just updated the docs, but because it uses the UID of element "groups" it might be annoying in practice to use the config file to set this up, as it requires a bit of manual looking-up of these values.
As the plugin settings are stored in Project Config, you'll probably get the same value setting it through the control panel.
'permissions' => [
    'craft\elements\Asset' => [
        // Volume UIDs
        '33974e79-b3b6-47ec-af91-4519fe4985be',
        'c194716f-aa74-40b6-8426-c835599cbe93',
    ],

    'craft\elements\Category' => [
        // Category Group UIDs
        'a27827c8-4810-433c-acb9-261b53d46281',
        '346194f0-6da8-4f46-a20b-795631ee9a5f',
    ],

    'craft\elements\Entry' => [
        // Section UIDs
        'a8d4bdf1-164e-4ddc-aaad-640026b8d3bf',
    ],

    'craft\elements\User' => [
        // User Group UIDs
        'c23a4d8d-47f0-4e71-927c-d5897ec9c9f8',
    ],
],


Count entries with condition I would like to count comments that belong to a certain entry (product). I have this so far:

{% set comments = craft.entries().section('productcomments').all() %}
 {% set counter = 0 %}
  {% for comment in comments %}
   {% if comment.title|length and comment.title == entry.title %}
    {% set counter = counter+1 %}
    {{ counter }} comments
   {% endif %}
  {% endfor %}

This actually works, but it displays the results after each other (like this:1 comment 2 comment 3 comment - if i have 3 comments).
I would like to show only "3 comments". Actually display the max result.
Twig's "loop.last" doesn't work because of the condition.
Is there another way?
Thank you very much!

Assuming that your products are entries, and comments are related to a product through an entries field, you can use the following:

{% set comments = craft.entries.section('productcomments').relatedTo(entry).all() %}

This uses the relatedTo parameter.

You can then return how many comments are found by using:

{% if comments|length %}
    <p>{{ comments|length }} comments</p>
{% endif %}


Is it possible to include a thumbnail in an element type index view? I'm trying to emulate what Assets does, in which a thumbnail of the asset is shown along side the title of the asset. When I alter the _toString() method of the model, what I get is the URL instead of the image (the URL is being added to the data-label attribute of the containing div).

Is there a way to accomplish what I'm after?

(Hopefully it is okay for me to answer my own question)

In the ElementType file, the getSources method needs to have the hasThumbs key set, as follows:

public function getSources($context = null)
{
    $sources = array(
        '*' => array(
            'label'    => Craft::t('All assets'),
            'hasThumbs' => true,
        )
    );
}

In your Model file you have to have a getThumbUrl method defined, as follows:

public function getThumbUrl()
{
    return $this->thumbnailUrl;
}

This results in a small thumbnail next to the title of your element, as seen in the screenshot:

The Craft 2 Class Reference helped put this together for me.


Add Database Relationship to craft_user table from a Plugin Within a plugin I'd like to create a table which is linked to the craft_users table, keyed on the id. In my table I have a user_id column which I'd like to be unique and only contain Id's which exist in the craft_users table. Now, I know I can't monkey about with the craft_users table or the UserRecord, so I wondered what the best way of doing this would be?

Here is my Record code:

<?php
namespace Craft;

class MyPlugin_MerchantRecord extends BaseRecord
{
  public function getTableName()
  {
    return 'myplugin_merchants';
  }

  protected function defineAttributes()
  {
    return [
      'user_id' => [AttributeType::Number, 'required' => true],
      ...
    ];
  }

  public function defineIndexes()
  {
      return [
          ['columns' => ['user_id'], 'unique' => true],
      ];
  }

  public function defineRelations()
  {
    return [
      'user' => [static::BELONGS_TO, 'UserRecord', 'user_id'],
    ];
  }
}

If I try and install the plugin I get the following error:

SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint.
The SQL statement executed was: ALTER TABLE `craft_myplugin_merchants`
ADD CONSTRAINT `craft_myplugin_merchant_user_id_fk` FOREIGN KEY (`user_id`)
REFERENCES `craft_users` (`id`) ON DELETE SET NULL

I've tried it with/without the defineIndexes() call, and get the same error. I'm sure I'm missing something simple, but can't see the wood for the trees!

As always any help gratefully received.

So it looks like I had a couple of things wrong here.

The type of relationship I was trying to use.
The fact that I was trying to add an extra unique index.

To solve the problem, what I ended up doing was this:

Remove my user_id column.
Remove the defineIndexes() definition altogether (you'll see why in a minute).
Change my Relationship definition to this:

public function defineRelations()
{
  return [
    'user' => [static::BELONGS_TO, 'UserRecord',  'id', 'required' => true,
                    'onDelete' => static::CASCADE],
  ];
}

So what this ended up doing was using the id column that is automatically created as both a Primary Key and a Foreign Key related to the craft_user table.

The reason I wanted to do this, is my new table which I'm creating is actually just an extension of the craft_user table anyway. There will be only ever one row per User (which is made unique by the Primary Key index) and the Foreign Key makes sure that only values that exist in the craft_users id column are allowed in the id column of my new table.

For completeness, the reason I was getting the error mentioned in the question was because I'd set the user_id column to 'required' => true. As I'd not used the onDelete property on the relationship, it was defaulting to ON DELETE SET NULL. It can't be both, either it must always have a value or it can be set to NULL.


How to allow non-admin user group to copy password reset URL and activation URL? So, as an Admin, I can copy either the password reset URL or the activation URL. But giving another user group all of the Users permissions (e.g. using select all in the permissions tab when editing the group) does not give them the same functionality. Is this a config setting or just not possible?

Currently that's the expected behavior.  Feel free to add your voice to this feature request, though: https://github.com/craftcms/cms/issues/942

Of course you could always write a plugin that exposed whatever features you wanted to whomever you wanted bypassing Craft's logic.


Need help with filling matrix images into CSS grid I want to fill in shuffled images into a CSS grid as a project overview. The images come from a matrix and have 4 different formats. So I want to fill in a format A image into the fitting grid cell (callA1, cellA2 ...). With my code, I have the problem, that the loop iterates over all items and fills all the A-sized cells with the same image. I would like to have image1 in cellA1 and image2 in cellA2 and image3 in cellB1 and so on.

{% for allProjects in craft.entries.section(['projekteZitate']).all() %}
   {% set shuffledProjects = shuffle(allProjects) %}

       {% for block in shuffledProjects.projekteMatrix %}
                    {% if block.type == 'A' %}
                         {% set image = block.A.one() %}
                             {% if image %}

                              {% endif %}
                    {% endif %}

                    {% if block.type == 'A' %}
                         {% set image = block.A.one() %}
                             {% if image %}

                              {% endif %}
                    {% endif %}

                    {% if block.type == 'B' %}
                         {% set image = block.A.one() %}
                             {% if image %}

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

There is no way to determine if an image should go to the first block.type == 'A' if statement or the second one

you could write something like this:

{% switch block.type %}
    {% case 'A' %}
        {# images for both case a1 + a2. write the loop number into the class name #}

 {# your image #}

    {% case 'B' %}
        {# images for case b #}

 {# your image #}

{% endswitch %}

That's just one of lots of possibilities


Custom fields on users Is it possible somehow to add custom fields to the users in Craft?
I'd like the users to be able to add a Bio.

I know I can add a channel but wondering if I am missing something obvious or if this would be a good feature request?

I'd like to add, bio, social media info ect, about the author etc.

Yes you can. Go to Settings > Users then the Fields tab.

Here you can add fields just like you would for categories or entry types.


Where do I add custom css for the Redactor editor in the Craft control panel? Redactor provides a way to specify custom styles for content in the editor. It's documented here: http://imperavi.com/redactor/examples/typography/

How can I use this approach with Craft?

I've already added custom formatting options to the menu via formattingAdd. That works great and the corresponding custom CSS classes are being applied in the formatting menu, the editor text, and on the site front-end.

But the items in the formatting menu and the editor just look like standard paragraph elements and don't reflect any of the styles that they will have on the site.

Where do I put the CSS so that when my clients are working in the control panel, the styles are visible in the formatting menu and the editor when they are applied?

I ended up using the Control Panel CSS plugin by Lindsey D to do this. Installed the plugin and then pointed it to an external style sheet with my redactor styles.

Here's the plugin:
https://github.com/lindseydiloreto/craft-cpcss

And in my stylesheet, the styles look like the below. These examples are for a custom item I added to the redactor formatting menu with a class pullquote__quote. The first style the menu item in the formatting dropdown menu. The second style the text in the editor.

.redactor-dropdown .redactor-formatting-p-pullquote__quote {
  font-size: 20px;
  font-weight: bold;
  color: #ffcb00;
}

.redactor-editor .pullquote__quote {
  font-size: 20px;
  font-weight: bold;
  color: #ffcb00;
}

And my redactor config file for this rich text field has this corresponding addition:

formattingAdd: [
    {
        tag: 'p',
        title: 'Big quote',
        class: 'pullquote__quote'
    }
]


How do you manage master db across multiple environments? I've just started my first Craft site locally and it's all good so far.. a little further down the road, I'll need to then deploy regularly to integration/test/stage and production environments.

I'm just wondering how people manage DB syncing across these different environments?

See carlcs' comment. What's the best practice for handling data migration and organization across development environments? is one of the biggest discussions on this S/E.


Craft Commerce Worldpay 302 error I'm having problems with Worldpay and their notorious dislike of 302 redirects with the payment response. I remember this problem last year and Luke fixed it but it seems to have returned.

Within Commerce_PaymentsService there is code targeting Worldpay and some other gateways, around line 15 in the latest version. I believe that was to avoid the 302 redirect problem from last year.

What I've done is added code to log where the script is getting to, and it seems it never even reaches this code. You can see from the sample below

    if ($success && $transaction->status == Commerce_TransactionRecord::STATUS_SUCCESS)
    {
        CommercePlugin::log('Commerce_PaymentsService line 814', LogLevel::Info, true);
        craft()->commerce_orders->updateOrderPaidTotal($transaction->order);
    }
    CommercePlugin::log('Commerce_PaymentsService line 817', LogLevel::Info, true);

    // For gateways that call us directly and usually do not like redirects.
    // TODO: Move this into the gateway adapter interface.
    $gateways = array(
        'AuthorizeNet_SIM',
        'Realex_Redirect',
        'SecurePay_DirectPost',
        'WorldPay',
    );

The line saying 'Commerce_PaymentsService line 817' is not being reached. I've currently following the trail and am several functions within Commerce_OrdersService applying the same metho to figure out what's happening.

Does anyone know of a change in the way Commerce works that would bypass this Worldpay test?

I made a support request yesterday, but no response yet, which is why I'm on here.

UPDATE: the latest version of Craft Commerce has changed things with Customer Emails. I've got some deprecated notices in the control panel. I'm wondering whether there is an email problem which is stopping things proceeding.

UPDATE 2: rolled back two versions before the email change and it's still broken. I've noticed that when you update an order's status in the control panel the email is not being sent and a popup error saying 'Undefined' is being shown. There's nothing in any of the logs so it looks like more trawling through Craft Commerce. Test emails from Craft work fine, and the email template is fine when viewed on the front end.

UPDATE 3: Removing the emails from the order statuses allows the order status to be updated fine. So there is definitely a problem with the emails. Then I've updated to use a test email containing nothing but static text - that processes, send and even redirects from Worldpay fine. It's my order email template.

This was an email template problem. While the template loaded fine in a browser, it was failing when being sent by Craft Commerce on a status update. The only clue was when manually updating a status in the control panel and receiving a Javascript alert saying 'Undefined'. Nothing in any logs. I feel quite fortunate to have happened to have stumbled across the solution with only a day's work.


Contact Form email address set in the plugin's setting being changed by Linode server? I've used the contact form plugin many times with no issues. However I have a site set up on a Linode server and for some reason the from address that I have added in the contact form settings e.g. contactus@mydomain.com is being changed to contactus@localhost.localdomain when the email comes through.

Anyone any ideas what is causing this. I'm thinking it's a server setting but wanted to check the plugin wasn't causing anything weird to happen as well.

Cheers

Generally I've been dissatisfied with the craft cms contact form plugin. Fortunately, this doesn't need to be handled with the CMS. I've been using third party tools such as Formbucket to handle form submissions for several reasons.

Reliability is extremely important. Most clients will not tolerate any mistakes regarding lead generation in contact forms.
Feature rich. Craft's contact form plugin is very barebones and ugly. With formbucket and other form submission collection tools, you can add as many fields as you want instead of segmenting message. There is also honeypot and recaptcha fields to keep out spam.

I know this is probably not the answer you were looking for, and I wish you the best with getting it to work, just wanted to let you know how i've been doing it.

PS: I don't work for Formbucket.


Unable to access site after importing DB I am installing a repo locally, and everything runs smoothly and I am able to see the site and login to my admin. But when I import my DB then visit my site: I get the error message "Site unavailable".

When I visit my admin area I see the message: "To complete the update, some changes must be made to your database." I click proceed and see the message "Craft couldn’t backup the database. How would you like to proceed?".

I am using Craft 3. But the DB I am importing from uses the older 'craft_' prefix, which I defined in the setup. Am stumped on how to solve this.

Ended up getting this resolved, by downloading DB from Craft staging site and importing, instead of using Sequel Pro. Also make sure your configs in general.php are correct.


How can I change the size of an individual pin? I tried this code, but getting 'unexpected token errors". Probably something to do using incorrect syntax.

{% js %}
    var markerId = 'smartmap-mapcanvas-1.{{ entry.id }}.addressInformation';
    smartMap.marker[markerId].setOptions({
        'icon':       '{{ icon }}';
        'scaledSize:' 'new google.maps.Size(32,32)'
    });
{% endjs %}

The error in the console

var markerId = 'smartmap-mapcanvas-1.550.addressInformation';
smartMap.marker[markerId].setOptions({
    'icon':       '//d1ado4imyxw3fk.cloudfront.net/html/pin-otto-12.png';
    'scaledSize:' 'new google.maps.Size(32,32)'
})

Looks like there are a couple of notable errors in your JS:

You have a semi-colon at the end of your icon line. That should be a comma.
On the scaledSize line, you have the colon inside the quote. That should be outside.
Added per Robin's comment... Since you're doing this in JS (not Twig), the new google.maps.Size(32,32) shouldn't be enclosed in quotes. Pass in the JS object directly.

Try something like this...

{% js %}
    var markerId = 'smartmap-mapcanvas-1.{{ entry.id }}.addressInformation';
    smartMap.marker[markerId].setOptions({
        'icon':       '{{ icon }}',
        'scaledSize': new google.maps.Size(32,32)
    });
{% endjs %}


Plugin Controller - Save Entry I'm testing with the Foxycart Plugin to update the stock in my code.
In the FoxyCartController.php I'm able to filter through the XML feed ok and get the new Stock value but it's doesn't seem to be getting saved.

My current code which picks up the correct product and $newQuantity value ok - but it doesn't seem to save the $newQuantity value back to the stock field in the entry.

//If you have custom code to run for each product, put it here:

$service = craft()->entries;

    $entry = $service->getEntryById($product_code);

    if ($entry != null) {
        $attrs = $entry->getContent();

        $newQuantity = $attrs['stock'] - $product_quantity;

        $entry->getContent()->setAttributes(array(
            'stock' => $newQuantity
        ));

        $service->saveEntry($entry);

        //return $entry;
    } else {
        //return null;
    }

You're likely getting a validation error that you're not checking for.  Do something like this:

if ($entry->validate())
{
    $service->saveEntry($entry);
}
else
{
    $errors = $entry->getErrors();
}

Update:

Do you get anything if you do:

if (!$service->saveEntry($entry))
{
    $errors = $entry->getErrors();
}


devMode false, Internal Server Error Wanted to test 404 error page on our beta site. Set devMode to "false" in general.php. When I do this and try to view inner pages on the website, we receive an "internal server error". All inner pages fail and report "internal server error". The homepage works fine. Any ideas?

Cleared the log files found at /storage/logs/web.log.

Tried to view an inner page on the website which threw a Craft "Internal Server Error".

Inside the log, I saw "Twig\Error\SyntaxError: Unknown "dump" function".

Googled that message and found the solution.

For other newcomers, when you set devMode to true, the dump() function is available in your templates. When you set devMode to false, dump() is not available and Craft throws an internal server error.

That is why my inner pages would not load.


Is it possible to find what Matrix Block aren't being used I have a fairly large site with the main content on most pages driven by a Matrix field. That Matrix field has 15 or so different Blocks - we are now doing a tidy up on the site and going to try and reduce the number of blocks available - is there any way to tell if a block isn't being used?

Nothing native, but it can easily be done with an SQL query – something like this should work (note that all the queries assume that your dbPrefix setting is set to 'craft_'):

SELECT bt.id, bt.handle, f.handle as field
FROM craft_matrixblocktypes bt
INNER JOIN craft_fields f ON bt.fieldId = f.id
LEFT JOIN craft_matrixblocks b ON bt.id = b.typeId
WHERE b.id IS NULL

The above query would output a table with the id and handle for all block types that aren't used, in addition to the handle for the Matrix field they belong to, i.e.

id  | handle             | field
------------------------------------------
13  | someMatrixBlock    | someMatrixField
4   | anotherMatrixBlock | someMatrixField

To actually execute the SQL query, you can use a free tool like Sequel Pro or phpMyAdmin – or write a custom plugin that uses the QueryBuilder.

Edit

Upon request in the comments below, here's a query that will list all block types that are in use (i.e. there are blocks for each particular block type). The number of blocks for each type is listed in a count column:

SELECT COUNT(bt.id) as count, bt.id, bt.handle, f.handle as field
FROM craft_matrixblocktypes bt
INNER JOIN craft_fields f ON bt.fieldId = f.id
LEFT JOIN craft_matrixblocks b ON bt.id = b.typeId
WHERE b.id IS NOT NULL
GROUP BY bt.id

...and here's a query that will output a table with all blocks, sorted by block type, with the ID and URI for the entry which "owns" each particular block:

SELECT b.id as blockId, bt.handle as blockType, bt.id as blockTypeId, e.id as entryId, i18n.uri as entryUri, f.handle as field
FROM craft_matrixblocks b
INNER JOIN craft_entries e on e.id = b.ownerId
INNER JOIN craft_fields f on f.id = b.fieldId
INNER JOIN craft_matrixblocktypes bt on bt.id = b.typeId
LEFT JOIN craft_elements_i18n i18n on i18n.elementId = e.id
ORDER BY bt.id

Note that for Craft 3, you'll need to replace the craft_elements_i18n table with craft_elements_sites:

SELECT b.id as blockId, bt.handle as blockType, bt.id as blockTypeId, e.id as entryId, sites.uri as entryUri, f.handle as field
FROM craft_matrixblocks b
INNER JOIN craft_entries e on e.id = b.ownerId
INNER JOIN craft_fields f on f.id = b.fieldId
INNER JOIN craft_matrixblocktypes bt on bt.id = b.typeId
LEFT JOIN craft_elements_sites sites on sites.elementId = e.id
ORDER BY bt.id

If you want to limit that last query to only list the blocks for a particular block type, you can replace the ORDER BY statement with this:

WHERE bt.id = 2

Where 2 is the ID for the block type you want to list, obviously.


How can I use operators within Twig hashes? I'm looking for documentation on how to use operators in hashes in Twig. Someone mentioned this in the Google+ support group the other day and I was unable to find any docs or examples.

{% set eventParams = {
  section       : 'events',
  order         : 'startDateTime asc',
  startDateTime : '>= '~now
} %}

If I wanted the startDateTime hash to use a range of dates, say ( >= now and <= now + 5 days ) how would I specify that in the startDateTime hash?

Is this a hash syntax that is specific to Twig?

That's what you are after:

startDateTime : 'and', '>= ' ~ now, '<= ' ~ now | date_modify('+5 days')


Can Auth0 be used for Craft Commerce user accounts? I want to use Auth0 to provide a Single Sign On (SSO) for a couple of services I am integrating, one of which is Craft Commerce user accounts. Does Craft Commerce allow something like Auth0 for authentication?

Craft Commerce and Craft does not support that service out of the box. A Craft plugin would need to created using their PHP Package: https://github.com/auth0/auth0-PHP


Changing the output of ElementAPI Wondering if it's possible to change the output that I'm getting from ElementAPI. I'm using the API to preload assets, and this is the output I'm getting.

{
    "data": [
        {
            "title": "Hero",
            "url": "assets/hero.jpg"
        },
        {
            "title": "Profile",
            "url": "assets/profile.jpg"
        }
    ]
}

This is great, and pretty much exactly what I need, but I'm wondering if there is a way to change the output so that, rather than using "data": I can use "manifest": which is what my preloader needs. Ideally, my code should look like this:

{
    manifest: [
        {
            "title": "Hero",
            "url": "assets/hero.jpg"
        },
        {
            "title": "Profile",
            "url": "assets/profile.jpg"
        }
    ]
}

Here also is my elementapi.php file:

namespace Craft;

return [
    'endpoints' => [
        'assets/data/assets.json' => [
            'paginate' => false,
            'elementType' => ElementType::Asset,
            'criteria' => [
                'limit' => null,
            ],
            'transformer' => function(AssetFileModel $asset) {
                return [
                    'id' => $asset->title,
                    'url' => $asset->url,
                ];
            },
       ],
    ]
];

If there isn't a way to do this in the plugin, is there a way to get the endpoint file and change it some other way?

Thanks!

Update:

Looks like changing the 'data' key is a bit more difficult I suspected.  We just released Element API 1.3.0 that exposes the internal $resourceKey necessary to change that value.

In your elementapi.php config file, something like this should work:

UPDATE: This is the final code that I used, and it works perfectly.

return [
    'defaults' => [
        'resourceKey' => 'manifest'
    ],

    'endpoints' => [
        'assets/data/assets.json' => [
            'paginate' => false,
            'elementType' => ElementType::Asset,

            'transformer' => function(AssetFileModel $asset) {
                return [
                    'id' => $asset->title,
                    'src' => $asset->url,
                ];
            },
       ],
    ]
];

The output you're seeing is defined by your craft/config/elementapi.php config file.

You can read about that configuration file and all of the available settings in the Element API documentation at: https://github.com/craftcms/element-api/tree/v1#setup


Can Craft convert requested url to lowercase? I think this is normally done with .htaccess or httpd.conf, but it's a bit cumbersome on shared hosting, and it seems like a simple trick for a CMS...

Can Craft convert requested URLs to lowercase? So if the slug for the page is /meditate, but someone types in /Meditate that Craft delivers /meditate page?

I don't quite understand how one uses customAsciiCharMappings -- it seems unrelated to this.

Depending on how your server is configured, this may be a non-issue. I just checked several sites, and they all work fine with mixed-case URLs.

If that's not the case with your site, you'll need to implement a simple plugin, which checks the URL, converts it to lowercase, and redirects the visitor (if required).

Hopefully it goes without saying that it would be much better to do this in your .htaccess or httpd.conf.

Caveats dispensed with, here's an example plugin.

<?php namespace Craft;

class PoorMansRedirectPlugin extends BasePlugin
{
    // Standard plugin methods omitted for brevity.

    /**
     * Runs before *every* request.
     */
    public function init()
    {
        $path = craft()->request->getPath();
        $lowerPath = strtolower($path);

        // If the current path is not lowercased, redirect.
        if ($path !== $lowerPath) {
            $url = UrlHelper::getUrl($lowerPath);
            craft()->request->redirect($url);
        }
    }
}

Just to be clear, this is a really bare-bones solution.

It doesn't make any distinction between site URLs and admin URLs, doesn't account for query strings, will break POST requests, and a lot more besides.

As such it is in no way production ready, and would require quite a bit of additional work to account for all of these edge (and not so edge) cases. See my previous note regarding .htaccess being vastly preferable.


Easy way to stage template updates for a live site Im curious if folks have found an easy way to stage template updates for a craft site thats already live.

Locally we have branch in the repo for the new version which works fine but on the server if we want to share them with the client ideally wed just use stage.sitedomain.com or something

Right now we are replicating the whole craft setup in a different location for a sub-domain and just sharing the database, is there a way to share a craft folder and just have a different templates folder?

Eventually looking to move some of this to vagrant cloud for staging environments but not quite there yet

There is a PHP Constant CRAFT_TEMPLATES_PATH which you can use to specify your Template Path. Would that help you out any?

If you look at the top of the page linked above, it explains where you set the Constants.


Posting to plugin's action controller using Ajax + JSON I want to use Ajax to POST to a plugin endpoint and get a JSON response. The plugin I'm using is for Mailchimp newsletter subscriptions and should allow for JSON responses:
https://github.com/aelvan/mailchimp-subscribe-craft
I have my form:
<form action="" method="post" novalidate>
  {{ csrfInput() }}
  <input id="email" name="email" type="email" >
  <input type="submit" value="Subscribe">

and my Javascript:
let json = this.toJSONString(this.form);
let xhr = new XMLHttpRequest();
xhr.onload = () => {
  if (xhr.status >= 200 && xhr.status < 300) {
    // ...
  } else {
     // ...
  }
};
xhr.open("POST", "actions/mailchimp-subscribe/audience/subscribe", true);
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xhr.send(json);

This hits the endpoint, but I get a 404 response:

HTTP 404 – Not Found – yii\web\NotFoundHttpException
Template not found: actions/mailchimp-subscribe/audience/subscribe

As I understand it, I'm correctly posting to the relevent plugin controller action by using the URL actions/mailchimp-subscribe/audience/subscribe. Furthermore, if I do a GET to that URL I get a 400 response which would lead me to believe the routing is correct.
The error therefore seems to be in the Craft controller/routing handler. It's not detecting that I've send a JSON string and that I want a JSON response (and not a template/HTML response).
What's going on here?

In your newsletter form, you should have:

<input type="hidden" name="action" value="mailchimp-subscribe/audience/subscribe">
<input type="hidden" name="language" value="{{ craft.app.locale.id }}">

Then, you should POST to '/' + userLocale (where userLocale = $('[name="language"]').val()).

I am using jQuery, and this is working fine:

$.post({
    url: '/' + userLocale,
    dataType: 'json',
    data: $('#form').serialize(),
    success: function(response) {
        console.log(response);
    },
    error: function(response) {
        console.log(response);
    }
});


Using Asset folder variables with Sprout Forms Is it possible to use auto-generated subfolders within an asset field? Each time I've tried, I get errors upon submitting the form that seem unrelated and nothing posts.

I'm just trying to do a simple job application form on our website and I'd like to have the file dumped into the subfolders: {jobID}\{email} (jobID is passed in as a hidden field), this way I know there won't be any issues uploading files unless the same person applies multiple times to the same position in which I guess I could put a timestamp on there as well.

I've tried those simple variables mentioned above, and I've also tried {object.jobID}\{object.email} but no luck.

Any help would be appreciated! Thanks!

I have created a form with a hidden field and asset field. The hidden field appears before the asset field:

We'll want to make the email field required because we are using that field in the auto-generated subfolders. (if it is empty it will generate an error)

Place a forward slash in before the folder location of the asset field: /{jobid}/{email}

The object variable will not work in the Sprout Forms context. We can use the field handle ({id}, {name}...) or the form object ({form.id}, {form.name}...) if you want to be more specific.

Regards.


best approach to call entries from two different sections I have set up two sections for two different kinds of events. On one page I want to differentiate between them on the other page I want to list them all. How do I organise that best on the back end and how do I call it best in the template?

big events
small events
overview for all events

You can create one section called events with the different entryTypes small and big.

You'll fetch all events with

craft.entries.section('events')

when you want to fetch only small events you'll do

craft.entries.section('events').type('small')

and the same for the big one.

However you can call entries from multiple sections too

craft.entries.section(['smallEvents', 'bigEvents'])

I suggest you to read the docs these Cheet Sheets are a good place to learn templating too


Using a variable with array of ElementIds to use in craft.entries.id I want to show entries according to an array.

This is my code:

            {% set navsubids = craft.amNav.getNavRaw("hoofdnavigatie", {startFromId:25})  %}
            {% set navsubidarray = [] %}

            {% for navsubid in navsubids %}
                    {% set navsubidarray = navsubidarray|merge([navsubid.elementId]) %}
            {% endfor %}

            {% set navsubidarray = navsubidarray|join(', ') %}

            {{ navsubidarray }}

            {% for entry in craft.entries.id('{{ navsubidarray }}') %}
                {{ entry.title }}

            {% endfor %}

The variable gives back that what I need (18, 17, 19, 4, 20, 21, 22, 23) but the for entry loop isn't returning anything.

I'm not sure where you're getting the id's from but you will need to pass an array into craft.entries.id so try:

{% set navsubids = craft.amNav.getNavRaw("hoofdnavigatie", {startFromId:25})  %}

{% set navsubidarray = [] %}

{% for navsubid in navsubids %}
  {% set navsubidarray = navsubidarray|merge([navsubid.elementId]) %}
{% endfor %}

{% for entry in craft.entries.id( navsubidarray ) %}
  {{ entry.title }}<br>
{% endfor %}

When you used {% set navsubidarray = navsubidarray|join(', ') %} you were creating a string of the values in the array.


Date Limit on Element Query I have a custom date field - "renewalDate" - which I want to use to limit an entry query. Here is the code:

When I run that it throws this error:

"Calling unknown method: craft\behaviors\ElementQueryBehavior::renewalDate()"

{% set dateStart = now|date('Y-m-d H:i')  %}
{% set dateEnd = date('next month') | date('Y-m-d H:i')  %}

{% set list = craft.users()
.group('members')
.status(null)
.orderBy('lastName asc')
.renewalDate('and', '>= #{dateStart}', '< #{dateEnd}')

%}

However, this works fine:

{% set list = craft.users()
.group('members')
.status(null)
.orderBy('lastName asc')
.renewalDate('>= #{dateStart}')
%}

So, the problem seems to be related to using "and".

I have done this before using Craft 2, but it is not working with Craft 3. I am missing something here?

Thanks!

Have you tried

.renewalDate('and', ">= #{dateStart}", "< #{dateEnd}")

The difference is the double quotes. I don't think that single quotes support interpolation.

However, I'm not sure in that case why the version without the 'and' works.

OK, have just tried it myself...
I created a section called test and added a single date field called renewalDate and the code below works on a simple listing page.

{% set dateStart = now|date('Y-m-d H:i')  %}
{% set dateEnd = date('next month') | date('Y-m-d H:i')  %}

{% set params = {
  section : 'test',
  orderBy : 'renewalDate asc',
  renewalDate : ['and', ">= #{dateStart}", "< #{dateEnd}"]
} %}

Entries

{% for entry in craft.entries(params).all() %}

{{ entry.title }}

{% endfor %}


Having trouble setting/getting slug I am trying to get a certain segment of the url. For this i’m setting the slug as a variable which seems to be best practice:

{% set slug = craft.request.getSegment(2) %}

I am interested in the second segment which is http://example.com/projects/awesome-project where “awesome-project” is the desired part.

I never had any problem with this method but in this case it always returns the first segment (projects), regardless of the passed argument. The site is localized.

I have tried various things but am completely clueless, atm. This can be reproduced with every template.

What could be the culprit?

Got it:

Had already set the slug variable to the first segment in _layout.twig thus overriding everything after. m(


Output Entry Type and/ or Category Total noob here.  Have managed to output entries and paginate them with this:

{% paginate craft.entries.section('woods').limit(5) as entries %}
    {% for entry in entries %}
        ....
    {% endfor %}
{% endpaginate %}

But I cannot figure out how to output just entries of a certain type or entries from a certain category within a category group.

{% paginate craft.entries.section('woods').type('mahogony').limit(5) %}
    {% for entry in entries %}
        ...
    {% endfor %}
{% endpaginate %}

ain't flying me anywhere but into a tree.

I'm sure it is something basic.

When you added the type param to your {% paginate %} tag, you removed the as entries bit, which is required. (That tells the tag what variable name it should give to the current page’s entries.)

{% paginate craft.entries.section('woods').type('mahogony').limit(5) as entries %}


Is there a way to reduce the search index? Our search index table (craft_searchindex) is over 500,000 rows and it takes a few hours to index. We have 11 languages, and each is being fully indexed. It takes a few hours to complete.

Are there any ways we can reduce the search index size (e.g. only one language, or specific sections?

Craft 3:

In Craft 3, a plugin can listen to the Element::EVENT_REGISTER_SEARCHABLE_ATTRIBUTES event to modify the searchable attribute list of an element before it is indexed.

Craft 2:

Currently the answer is no.

Maybe it makes the case for some sort of onBeforeSearchIndex event a plugin could listen to and influence what Craft adds to the search index, but given that manually updating the search index isn't meant to be a common occurrence (Craft keeps them up to date in normal element saving operations), I'd probably add the workflow you're using that requires frequent updates to the feature request.


Update and add POST URL parameters How do you add extra parameters or update a parameter to an exiting POST URL?

ADD EXTRA PARAMETER

For example, I have a page at this address:

http://localhost/shop/?color=red&order=title

And in my twig I want to add a parameter like this:

http://localhost/shop/?color=red&order=title&sort=asc
http://localhost/shop/?color=red&order=title&sort=asc&limit=10

UPDATE PARAMETER

For example, I have a page at this address:

http://localhost/shop/?color=red&order=title

And in my twig I want to update a parameter like this:

http://localhost/shop/?color=blue&order=title
http://localhost/shop/?color=red&order=price

If i'm not mistaking, craft.request.getQuery() returns an array filled with all $_GET key-value pairs, so in your case:

{% set queryParams = craft.request.getQuery() %}

will let you do:

// Do with it what you like ...
{% set queryParams = queryParams | without(key) %} // Removes a param
{% set queryParams = queryParams | merge([{ 'limit' : 10 }]) %} // Adds a param
{% set queryParams['order'] = 'price' %} // Changes a param

After doing what you need to do, you can rebuild the query string:

{% set queryString = '' %}
{% for key, value in queryParams %}
    {% set queryString = loop.first ? '?' ~ key ~ '=' ~ value : '&amp;' ~ key ~ '=' ~ value %}
{% endfor %}
{% redirect(siteUrl ~ queryString) %}


Updating an entry after an event action within onSaveEntry I have written a plugin that checks if an entry has been saved and sends the user an email from within an onSaveEntry event. This works fine. But I now need to also update this entry to flag that an email has been sent so that subsequent saves do not fire the event. I have tried adding the following but these lines do not work and cause an infinite loop.

$entry->setContentFromPost(['emailSent' => '1']);
craft()->entries->saveEntry($entry);

Is there a way to re-save the entry after the email has been sent so as just to update the emailSent field?

Full function:

public function onSaveEntry(Event $event)
{
    $entry = $event->params['entry'];
    if($entry->sectionId == '12345') {
        if($user = craft()->users->getUserById($entry->authorId)) {
            if($entry->enabled && !$user->admin && !$entry->emailSent) {

                // Send Approved Mail
                $email = new EmailModel();
                $email->fromEmail = 'me@mydomain.com';
                $email->toEmail = $user->email;
                $email->subject = 'Email Subject';
                $email->body    = '
Email Body
';
                craft()->email->sendEmail($email);

                $entry->setContentFromPost(['emailSent' => '1']);
                craft()->entries->saveEntry($entry);
            }
        }
    }
    return $event;
}

Use craft()->elements->saveElement the second time. It will work exactly the same but won't trigger the onSaveEntry event.


How can I filter/find on an ElementCriteriaModel? I have already retrieved a set of all Entries from a channel and need to now find one in the template without hitting the DB again (this find is in a loop so I don't want to grab a single result from the DB for every iteration through the loop):

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

How can I then pluck out a specific entry by field match? I've tried this:

{% set entry = entries.find('myFieldHandle', mySearchVariable) %}

This doesn't seem to work for me though in that I don't get a match.

Update
I may have found a better option for you. Looks like you were on the right track after all, but with some quotes misplaced.

{% set entries = craft.entries.section('mySection') %}
{% set entry = entries.find({myFieldHandle:'mySearchVariable'})[0] %}
{{ entry.title }}

Although looks like this hits the database again (thanks carlcs).

Maybe back to the orginal answer then, which was:

{% set entries = craft.entries.section('mySection') %}
{% for entry in entries if entry.myFieldHandle == 'myVariable' %}
    {{ entry.title }}
{% endfor %}


Access pages data with fewer queries by creating an object in Twig I have the goal of querying the database once for page info, and then using it to display information on select pages site wide.

For example, I want to link the the contact page with using a url slug like "contact" (which might change)

My thinking is to query for pages, then loop over them in twig and create an object which I can access as needed.

Here is some example code, my question is - is the best Craft like way to do this? Am I missing something that should make this easier?

{#
 # Query for all the Pages
 #}
{% set navigation = craft.entries.section('pages').all() %}

{#
 # Create an object to quickly reference pages without making additional DB queries #}
 #}
{% set pageData = [] %}

{% for item in navigation %}
    {#
     # create a dynamic property if one doesn't already exist
     #}
    {% if not attribute(pageData, 'pageId' ~ item.id) is defined %}
        {% set pageData = pageData|merge({ ('pageId' ~ item.id) : {
        'id':item.id,
        'slug':item.slug,
        'url':item.url,
        'title':item.title} }) %}
    {% endif %}

{% endfor %}

{#
 # Use the pageData object to reference pages by id as needed
 #}

Request a Bid

So the only database queries in your example will be generated by:

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

So creating an additional object doesn't serve the stated purpose of saving db queries. You can just access the navigation array directly.


Setting up multiple category listing pages on site with multiple locales I’m having trouble setting up an additional category listing page from the one I have set with the Category URI Format and Template settings in my Category Site Settings. Specifically, I’m having trouble getting it to work on my site which has two locales.

I have one category group called “practices” and one structure section-type called “people.”  I’m trying to create two different pages that each loops through all the entries for a particular category.

In other words, domain.com/people/practices/categoryA and domain.com/people/foo/practices/categoryA would both display all the “people” entries that belong to categoryA.

For my “practices” category group, I have the Category URIs to be people/practices/{slug} and the template to be people/_practices. Everything on this category listing page at domain.com/people/practices/categoryA works as intended.

I can also get the category listing page at domain.com/people/foo/practices/categoryA to work by using the last segment (which would be the category slug) and setting the category object by passing in the category slug like this:

{% set categorySlug = craft.app.request.segments|last %}
{% set category = craft.categories.slug(categorySlug).one() %}

However, this category listing page is throwing an error when I’m using it with my language switcher code, which uses:

{% if category is defined %}
    {% set entry = category %}
{% endif %}

{% set supportedSites = entry.getSupportedSites %}

{% for siteForEntry in supportedSites %}
  {% if siteForEntry | length == 1 %}

    {# if category #}
    {% set site = craft.app.getSites.getSiteById(siteForEntry) %}
    {% set entryForOtherSite = craft.categories.id(entry.id).site(site).one() %}

  {% elseif siteForEntry | length == 2 %}
    {# if standard entry #}
    {% set site = craft.app.getSites.getSiteById(siteForEntry.siteId) %}
    {% set entryForOtherSite = craft.entries.id(entry.id).site(site).one() %}
  {% endif %}

  {% if site.id == entry.siteId %}
      {# This is the current language, mark it as active and don't retrieve its url #}

{{site.name}}

  {% else %}
      {# if it's not the current site, get the entry for this site #}

{{site.name}}</a

  {% endif %}

{% endfor %}

Specifically, I get the “Variable ‘entry’ does not exist” error. I’m not sure why I’m getting this error because I’m setting the “entry” variable with the “category” variable. This works with domain.com/people/practices/categoryA, but it’s not working for the other category listing page.

        {% if category is defined %}
            {% set entry = category %}
        {{ entry.id }}
        {% endif %}

Not sure what to do. I also set a route so that domain.com/people/foo/practices/{slug} loads the people/_practices template, but it still doesn’t work.

The reason it's not working is that domain.com/people/foo/practices/categoryA is not a match for the Category group's URI settings. Only urls that match people/practices/{slug} are going to have that category variable defined.

And this code:

{% if category is defined %}
    {% set entry = category %}
{% endif %}

... won't define an entry if there's no category defined.

If your People structure is a flat list (no nested children), you could create a URL rules like this:

'people/<person:{slug}>/practices/<category:{slug}>' => [
    'template' => 'test/4'
],

That would take a URI like domain.com/people/foo/practices/categoryA and use the  people/_practices template and populate variables for person and category.

{{ person }}    {# foo #}
{{ category }}  {# categoryA #}

Then you could make an category query like this:

{% set cat = craft.categories().group('practices').slug(category).one() %}

{{ cat.title }}

But personally, I've found it easier to use query strings to filter a list instead of URL segments. You could possibly use a URL like this:

domain.com/people/foo?practice=categroyA

or...

domain.com/people/foo/practice?cat=categroyA

or even...

domain.com/people/practice?person=foo&cat=categroyA

Whichever works the most logically for your use case. Then use craft.app.request to pull the "categoryA" string from the URL:

{% set categorySlug = craft.app.request.getQueryParam('cat') ?? null %}

{% if categorySlug %}
  {{ categorySlug }}   {# "categoryA" #}
{% endif %}

Then use the categorySlug string in an entry or category query:

{% set cat = craft.categories().group('practices').slug(category).one() %}

Update

It's important to note, if you want a variable you set to be available in a layout template ({% extend %} tag), you need to define it outside of a {% block %} tag.

{% extends "_layouts/index" %}

{% set categorySlug = craft.app.request.getQueryParam('cat') ?? null %}

{% block content %}
  Content...
{% endblock %}

The categorySlug variable should now be available in the _layouts/index template context as well.

<body>
  <div class="language-switcher">
    {% if categorySlug is defined %}
      Yes it is!
    {% endif %}
  </div>

  {% block content %}
  {% endblock %}
</body>


Sum up number fields in the loop I don't understand what's going on in my code logic because I'm getting unexpected output.

I'd like to sum up three number fields in entries loop for the given post date range. Output gets almost doubled as expected. What am I missing here?

{# Get this month's data #}
{% set thisMonthStatistic = orders.postDate('and', '>= ' ~ firstDayOfMonth, '<= ' ~ lastDayOfMonth) %}

{% if thisMonthStatistic|length %}
{# We must set variable "0" and pass it in for the looping's variable #}
{% set thisMonthEarning = 0 %}

  {# looping in "thisMonthStatistic" #}
  {% for statistic in thisMonthStatistic %}

    {% set aPrice = statistic.aPrice %}
    {% set bPrice = statistic.bPrice %}
    {% set cPrice = statistic.cPrice %}

    {% if aPrice > 0 %}
      {% set aPrice = aPrice + thisMonthEarning %}
    {% endif %}

    {% if bPrice > 0 %}
      {% set bPrice = bPrice + thisMonthEarning %}
    {% endif %}

    {% if cPrice > 0 %}
      {% set cPrice = cPrice + thisMonthEarning %}
    {% endif %}

    {% set thisMonthEarning = aPrice + bPrice + cPrice %}

  {% endfor %}

{% else %}

  {% set thisMonthEarning = 0 %}

{% endif %}

{# Final variable output #}
{{ thisMonthEarning }}

Hmm... hard to tell because I can’t know what your expected result would be. But if I had to guess then you might be looking for just this:

{% if aPrice %}
    {% set thisMonthEarning = thisMonthEarning + aPrice %}
{% endif %}


Order Confirmation Email in Craft Commerce I am having trouble with emails being sent by Commerce.  Checked my settings in Craft and sent test email.  Works.  Followed Commerce docs in setting up email.  Double checked, but still does not work.  My problem may be in my email file.  Not sure what to have here.  Anyone have an example of an email file I could look at to see what I need to do?

To setup/debug email issues in commerce it's easiest to set up the simplest possible email and work your way up from there.

Check your Craft email settings - can you send & receive the test email
successfully?
In Commerce settings, double check you have created your email correctly and that your template exists at the path you set.  To simplify
things initially, avoid using any variables in the subject and hardcode
an address to which the email will be sent
Check your order status and make sure you have assigned your email to it and that a '1' then appears in the 'Has Emails' column for that
status.  It's easiest to start with an email attached to the default
order status so you can be sure orders coming in will trigger the
email.
Create the simplest possible template for the email and just put in it something like Huzzah, my email sent! as an initial end
to end test that the email is being triggered when you submit an order

At that point, your basic email should really send!

If not, check your commerce.log file for errors.  If you have parsing errors in your email template, they will appear here, or you may get the somewhat obscure [info] [plugin] [Forced] Can't send email if no status or emails exist. which indicates something is wrong with the set up of the status <> email relationship.

Most of the errors typically come from template errors in the actual email - trying to get data not available for example.  commerce.log will help with that, but another handy way of debugging your email templates is to put something like this at the top of them:

{# for debugging! #}
{% if craft.request.getSegment(1)=="order-completed-debug" %}
     {% set order = craft.commerce.orders.id(craft.request.getSegment(2)).first() %}
     {# No order supplied or other issue?  Load latest order by default... #}
     {% if not order %}
         {% set order = craft.commerce.orders.first() %}
     {% endif %}
{% endif %}

Set up a route in Craft settings so that /order-completed-debug/(number) points at your email template and then you will be able to use your browser for much easier testing by going to /order-completed-debug/<someValidOrderID> in your browser rather than having to submit an order each time you change your email template!

Finally, I very strongly recommend you use a transactional email service like mailgun, sparkpost, postmark, mandrill etc. as if you use default phpemail you're very likely to run into email delivery issues.  The logging systems these (often free!) transactional email systems supply are worth their weight in gold as you can see if the email is even being sent or not very simply, which helps with issue diagnosis a LOT.

For completeness, although this is a lot more advanced & less general:

Another gotcha to note is that custom order fields are not necessarily set yet in the order completed email template as it seems this is actually sent out BEFORE the final order save occurs so if you're e.g. setting some data in a plugin method listening to onBeforeOrderComplet=, you'll need to work around this....so to reference those fields you have to use a special approach (currently?)

{% set ISOrderNumber = order.content.ISOrderNumber ?? 'NONE' %}

Not the use of .content there - I need to use this to get at the field that is not yet saved in to the order as such.


Apache 404 on basic install I am getting an Apache 404 page when trying to install Craft after following all instructions. I've run out of ideas.

Set up a droplet running LAMP on Ubuntu 14.04 with Digital Ocean
'craft' folder is sitting in public at 'var/www' and index.php on
the same level is pointing to $craftPath = './craft';
.htaccess is running
set folder permissions
Created MySQL database with new user
Updated db.php
Have tried example.com/admin and
example.com/index.php/admin and
example.com/index.php?p=admin

Have tried moving craft folder above root and updating $craftPath but with no luck. Not sure what else to try?

Ended up the document root was looking for var/www/html instead of just var/www

This fixed it for me https://stackoverflow.com/a/23175981/851669


Craft 3 query children of sub categories Just wondering how to only query the children on subcategories. For example...

Food
├── Fruit
│   ├── Apples
│   ├── Bananas
│   └── Oranges
└── Vegetables
    ├── Brussels sprouts
    ├── Carrots
    └── Celery

I only want to query the children directly of fruit without having to get all of the food then have to loop around. As this isn't the best practice.

I thought it would be something like ...

{% set categories = craft.categories()
.group('food')
.ancestorOf('fruit')
.all() %}

Thanks in advance.

Building on Robin's comment, the ancestorOf parameter expects either an element ID or the actual element as a parameter.

For example, if you know the ID of the fruit category is 3, you'd use .anscestorOf(3), or if you already had the fruit category element, you'd do like so:

{% set fruitCategory = craft.categories.id(3).one() %}

{% set categories = craft.categories()
    .group('food')
    .ancestorOf(fruitCategory)
    .all() %}


My license.key file missing. How do I transfer the license to other servers? I just purchased a license from the CP of my production server. Then I went to the config directory to retrieve the license.key for my local dev server but it was not there. I can't find it anywhere. I installed the site the other day using Craft-2.1.2563 and when I went back to look at the zip I see that the license file is missing there as well.

What do I do? How can I transfer the license to my dev environment as well?

FYI - I set up my dev environment a few weeks ago and it was based on a slightly older build of Craft 2 - I'm not sure which version. But, it does have a license.key file. That doesn't do me any good because I purchased a license on the production server.

Thanks.

The Craft .zip does not come with the license.key file. From the Craft docs: "It doesn’t get one until you have finished installing Craft and it has made its first call to our web service to check for updates."

Make sure your Craft edition upgrade went through, it seems a bit odd that you upgraded editions and you don't have a license.key file.

Double check that it's not there (craft/config/license.key), maybe by using a search tool on the whole Craft install.

Also try checking for Craft updates to see if that triggers the generation of a license.key file. Restarting the server may help, but you did say it was a production server so that might not be convenient.

If all else fails, I'm sure if you contact P&T (support@buildwithcraft.com) they can sort it out.


Plugins: Time/Date specific functions Other than setting up a cron job, does Craft include any functionality for plugins to run a function at a specific time and/or date?

Some actions could include:

Bulk activate/deactivate entries
Messaging all users every monday (or once a week)
Hitting an API (account update, eCommerce, data updating etc.)

Let me know if a question like this already exists. I searched and couldn't find any related information.

Not really, and it'd be pretty difficult for PHP to truly emulate a cron service since, by nature, it requires something to execute it before it can run (in Craft's case, a browser request).

The closest thing in Craft would be its "Tasks" (completely undocumented).  Tasks are usually meant for things that might take a long time for PHP to complete and it's not critical that they happen right away.

But you can't schedule, them... if there is a task in a "pending" state in the craft_tasks table, the next request to the control panel is going to trigger it.

I suppose we could add something like a "runDate" property to tasks where you could be at least guaranteed they wouldn't run until the first control panel request was made after that given date.


Retrieve matrix content from Entries form field in an Entry I'm trying to retrieve matrix content from a an Entry field chooseTemplateExample which has a matrix block component with a Entry field component.

{% for template in entry.chooseTemplateExample %}
  View {{ template.title }} example

  {% for block in template.component %}

     //Trying to get the matrix content within this "template"
     {% for block in entry.chooseComponentForTemplate %}
      {% for component in block.component %}
        {{ title }}
      {% endfor %}
    {% endfor %}

  {% endfor %}

{% endfor %}

Thank you in advance.

When you loop through your matrix, you're missing the s on components. Should be:

{% for block in template.components %}

Each item in that loop will be one of the blocks in the matrix. So then you can call {{ block.component }} directly.

FYI, in your code, it looks like that component field in the matrix block is loopable as well? If you're trying to loop through an entries field, and then loop through a matrix field per entry, and then loop through another field per matrix block, expect some serious performance issues.

Full code reference:

{% for template in entry.chooseTemplateExample %}
  View {{ template.title }} example

  {% for block in template.components %}
    {% for component in block.component %} {!-- Is this field really loopable? --}
      {{ title }}
    {% endfor %}
  {% endfor %}
{% endfor %}


Admin entries 404'ing I've transferred a Craft site (I didn't build it) from staging to production. All the existing content is showing up on the front end but when I try to edit entries that aren't static pages, the edit pages are 404'ing.

Any tips would be greatly appreciated.

-- Update

Here's the contents of the .htaccess

<IfModule mod_rewrite.c>
RewriteEngine On

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

general.php

<?php

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

return array(

);

I was able to trace my particular issue to an old PCRE version which resulted in long regular expression strings to silently fail... causing the 'preg_match' to not match the edit entries string found here:

craft/app/etc/web/UrlManager.php:387

A quick test:

<?php
$path = 'entries/homepage/1-homepage';
$regexPattern = 'entries\/(?P<sectionHandle>(?:[a-zA-Z][a-zA-Z0-9_]*))\/(?P<entryId>\d+)(?:-(?:[\p{L}\p{N}\._\-]+))?';

var_dump(preg_match('/^'.$regexPattern.'$/u', $path, $match));

Should output in: 1 but was resulting in 0

The server is running a very old (from 2006) version of PCRE (PHP 5.3.3).

Running the following command:

pcretest -C

Resulted in the following version:

PCRE version 6.6 06-Feb-2006
Compiled with
  UTF-8 support
  No Unicode properties support
  Newline character is LF
  Internal link size = 2
  POSIX malloc threshold = 10
  Default match limit = 10000000
  Default recursion depth limit = 10000000
  Match recursion uses stack

This issue does not persist with the following version of PCRE (from a local Vagrant box):

PCRE version 7.8 2008-09-05
Compiled with
  UTF-8 support
  Unicode properties support
  Newline sequence is LF
  \R matches all Unicode newlines
  Internal link size = 2
  POSIX malloc threshold = 10
  Default match limit = 10000000
  Default recursion depth limit = 10000000
  Match recursion uses stack

I ended up solving this issue by migrating the sever to an AWS instance running PHP v5.6  You could also upgrade/rebuild PHP, but there are too many dependencies on the server.  I also tried increasing the backtrace and recursion limits, without success:

I tried adding this to my .htaccess file (FYI: I do not recommend setting this to 2Mb):

php_value pcre.backtrack_limit 2097152
php_value pcre.recursion_limit 2097152

Hope this helps


Can a user photo support a remote image? User photos don't appear to be saved in the same method as Assets. I'm looking to do a remote image (from plugin) as the user profile image. I've got the path saving to the column, but the image is not rendering. Anything special I need to do to get that to render?

$user = craft()->users->getUserById($userId);
$user->photo = $gravatarPath;
craft()->users->saveUser($user);

Can a user photo support a remote image?

The answer to that is currently "no", in Craft 2.x.  However in the upcoming Craft 3.x, user profile photos are proper "assets" and can be saved to any asset source.

  I'm looking to do a remote image (from plugin) as the user profile image. I've got the path saving to the column, but the image is not rendering. Anything special I need to do to get that to render?

I assume you're just ignoring Craft's default user profile "photo" field and adding your own custom one?  Or are you trying to replace the profile field that Craft uses to display profile photos in the CP with your own custom one.  I'm having a hard time seeing how you'd pull off the later in the current version of Craft 2.x.


Missing templates using PHPStorm when using Craft 3 Following code show a warning "template not found" in PHPStorm:

{% extends '_layout' %}.

When adding .twig this warning dissapears, but I'd like to keep extends and includes filetype agnostic.

What I've tried so far:

Installing the Symfony plugin and enabling Twig support.
Adding the templates folder as a Twig directory, as described here.
Invalidate PHPStorm cache and restart.

Is there a way to fix this? Or should I use {% extends '_layout.twig' %}?

Using PHPStorm 2019.1

Was probably a bug in PHPStorm 2019.1.

Currently using 2019.1.2 and the warning disappeared.


Entry Form - Multiple Entries & Entry Type & Parent I'm building an MVP for a project, I'm using Craft and would like to:

Create an experiment (/experiments/)
Create multiple cages for the animals within the previous created experiment eg. 5 cages - (/experiments//cages/)
Add a number of rodents to each of those cages e.g. Create 5 cages with 5 rodents in each. (/experiments//cages/)
Add measurements to a specific mouse e.g., weight / tumour size (/experiments//cages/)

I was thinking this would be possible by using an entry form to add entries to a structure that looks like this-

--Experiments
----Cage 1
------Rodent 1
------Rodent 2
------Rodent 3
----Cage 2
------Rodent 1
------Rodent 2
------Rodent 3
...

My question is -

Is it possible to create multiple entries per form eg.
Create 5 cages with 5 rodents in each. from a single entry form submission?
Is it possible to assign the entry type in an entry form? (eg. Cage or Mouse entry type?)
Is it possible to assign the parent to an entry via the entry form?

If these aren't possible with an entry form + structure what would be the best way to approach it?

Thanks!

A little bit late but here it is.

Go to https://pluginfactory.io/ and create a plugin, you need at least 1 Controller but you can leave the Name value blank to create a controller with the same Name as your PluginHandle. So if you choose "Experiment" as your Pluginhandle your controller will be ExperimentController
Click on "Build my Plugin" and save the files in your craft/plugins/>>pluginhandle<< folder (of course you need to create the >>pluginhandle<< directory eg.) Then you should install the plugin
Insert this function in your controller. Note: this is just an example. I don't know your fieldLayout or some special behaviors you might want to include. So this is just the basic logic with some error handling. Important please read the commands and change the entryType Ids

     /**
     * @var    bool|array Allows anonymous access to this controller's actions.
     * @access protected
     */
    protected $allowAnonymous = true;

    /**
     * @throws Exception
     */
    public function actionSaveEntry(){
        $this->requirePostRequest();
        // check for an Id if there is one we update an existing entry if not we create a new one
        if($entryId = craft()->request->getParam('entryId')){
            if(!$entry = craft()->elements->getElementById($entryId)){
                throw new Exception('Could not find entry with id');
            }
        }else{
            $entry = new EntryModel();
            $entry->typeId = craft()->request->getParam('entryType');
            $entry->sectionId = craft()->request->getParam('sectionId');
        }

        // set the content of your entry/experiment
        $entryData = craft()->request->getParam('experiment');
        $entry->getContent()->setAttributes($entryData['fields']);

        // try to save the entry
        if(craft()->entries->saveEntry($entry)){
            // fetch the id from the experiment so we can use that as a parentId for the cage
            $entryId = $entry->id;
            // if everything is alright we can save the cage like we did it with the experiment
            if($cagesData = craft()->request->getParam('cage')){

                foreach ($cagesData as $cageData){
                    // like with the entry check for an Id
                    if($cageId = $cageData['entryId']){
                        if(!$cage = craft()->elements->getElementById($cageId)){
                            throw new Exception('Could not find cage with the given Id');
                        }
                    }else{
                        $cage = new EntryModel();
                        $cage->typeId = 5; // you could as well create a hidden field like for the entry itself but you must change the typeId to your entryType Id
                        $cage->sectionId = craft()->request->getParam('sectionId');
                    }

                    // save the content as well
                    $cage->getContent()->setAttributes($cageData['fields']);
                    // and set the parent
                    $cage->parentId = $entryId;
                    if(craft()->entries->saveEntry($cage)){
                        $cageId = $cage->id;
                        // and now we are going to do the same with the animals
                        if($rodentsData = $cageData['rodents']) {
                            $rodentErrors = [];
                            foreach ($rodentsData as $rodentData){
                                // like with the entry check for an Id
                                if (array_key_exists('entryId', $rodentData) && $rodentId = $rodentData['entryId']) {
                                    if (!$rodent = craft()->elements->getElementById($rodentId)) {
                                        throw new Exception('Could not find rodent with the given Id');
                                    }
                                } else {
                                    $rodent = new EntryModel();
                                    $rodent->typeId = 6; // change your entryType Id here
                                    $rodent->sectionId = craft()->request->getParam('sectionId');
                                }

                                $rodent->getContent()->setAttributes($rodentData['fields']);
                                $rodent->parentId = $cageId;
                                if (!craft()->entries->saveEntry($rodent)) {
                                    // important note: if there are any errors here, the cage and the experiment is
                                    // saved anyway so you should insert the id of the experiment to the form in order
                                    // to update the form instead of creating a new one again and again
                                    // if the experiment has an Id but the cage/rodent not you'll just update the experiment
                                    // and create new cages/rodents for it
                                    $rodentErrors[] = $rodent->getErrors();
                                }else{
                                    $rodentErrors = [];
                                }
                            }
                            // include errors of each item
                            craft()->urlManager->setRouteVariables(array(
                                'rodentErrors' => $rodentErrors
                            ));
                        }
                    }else{
                        craft()->urlManager->setRouteVariables(array(
                            'cageErrors' => $cage->getErrors()
                        ));
                    }
                }

            }
        }else{
            // handle your errors here.. you can use $entry->getErrors()
            // return them to your frontend and render them in twig or something
            craft()->urlManager->setRouteVariables(array(
                'experimentErrors' => $entry->getErrors()
            ));
        }
    }

Your html template. Note I don't know your fieldLayout so I just included the title and the craft default body field. In order to add your custom fields to the html you need to set the input name to entryType[fields][fieldHandle] so if you want to add a field with the handle foo to the cage you would name it cage[fields][foo]. As for the routing: your action must be pluginHandle/functionName

<form method="post" accept-charset="UTF-8">
    {{ getCsrfInput() }}
    {# your plugin name and function name #}
    <input type="hidden" name="action" value="test/saveEntry">
    {# the id of your section for me its 4 you need to change this value to your section Id #}
    <input type="hidden" name="sectionId" value="4">
    {# enable the entry #}
    <input type="hidden" name="enabled" value="1">
    {# id of your entry leave it blank to insert a new entry, insert a id to change an existing entry#}
    <input type="hidden" name="entryId" value="">
    {# the id of your experiments entryType for me its 4 you need to change this value #}
    <input type="hidden" name="entryType" value="4">

    Title
    <input id="title" type="text" name="experiment[fields][title]">
    <input  type="text" name="experiment[fields][body]">

    {# now the cage as a children #}

        cage title
        <input type="text" name="cage[0][fields][title]">
        <input type="text" name="cage[0][fields][body]">
        <input type="hidden" name="cage[0][entryId]" value="">
        {# and some Rodents as children for the cages #}

            Rodent title
            <input type="text" name="cage[0][rodents][0][fields][title]">
            <input type="text" name="cage[0][rodents][0][fields][body]">
            <input type="hidden" name="cage[0][rodents][0][entryId]" value="">

            Rodent title
            <input type="text" name="cage[0][rodents][1][fields][title]">
            <input type="text" name="cage[0][rodents][1][fields][body]">
            <input type="hidden" name="cage[0][rodents][1][entryId]" value="">

            Rodent title
            <input type="text" name="cage[0][rodents][2][fields][title]">
            <input type="text" name="cage[0][rodents][2][fields][body]">
            <input type="hidden" name="cage[0][rodents][2][entryId]" value="">

    {# and another cage #}

        cage title
        <input type="text" name="cage[1][fields][title]">
        <input type="text" name="cage[1][fields][body]">
        <input type="hidden" name="cage[1][entryId]" value="">
        {# and some Rodents as children for the cages #}

            Rodent title
            <input type="text" name="cage[1][rodents][0][fields][title]">
            <input type="text" name="cage[1][rodents][0][fields][body]">
            <input type="hidden" name="cage[1][rodents][0][entryId]" value="">

    <input type="submit" value="Publish">

{# just as an example how you could display errors or at least receive the error object #}
{% if rodentErrors is defined %}
    {% for errors in rodentErrors %}
        {% for fieldHandle, errorMessages in errors  %}
            {{ fieldHandle }} {{ errorMessages[0] }}

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

Please let me know when you need help and feel free to ask what you like.
If done right, you'll end up with a structure like this with your form (note: please ignore the titles...)


How to extend a variable name to fetch another? I’m trying to append a 'x2' to the end of a variable name to call another variable.

For example:

imgSize might be set to predefined variables like imgColumn4, imgColumn6 or imgColumn8.
In that case I would also have imgColumn4x2, imgColumn6x2 and imgColumn8x2.

So to simplify the variables in a loop, I’d like to do something like:

{% set imageUrl = image.getUrl(imgSize) %}
{% set imageUrlx2 = image.getUrl(imgSize ~ 'x2') %}

imgSize ~ 'x2' does not work.

How can I join 'imgSize' and 'x2'?

There may be other ways also, but one approach is to use an array with key value pairs, which can be assigned and accessed more dynamically.

{{ sizeVars[imgSize ~ 'x2'] }}

You also might want to check out twigs attribute method.

{{ attribute(array, item) }}

Where 'item' can be a concatenated string.


Save user profile fields through plugin I have added additional fields to my user profiles. I need to be able to change and save these via my plugin. How do I set those values before calling

craft()->users->saveUser($user);

You use setContentFromPost method on the UserModel if you are posting to your plugin, like so (this is what happens when you save your profile in the control panel):

$user->setContentFromPost('fields');

There is also a setContent method, which takes a ContentModel or an array:

$user->setContent([ 'fields' => ['field1' => 'value'] ])

Behind the scenes, that runs the setAttributes method.


What is the best way to setup Right to Left language locale in Craft? I have been working on setting up locales for left-to-right languages. I wanted to know the support Craft provides for Right-to-Left languages like Arabic, Hebrew etc. Is there a plugin that could help or is it just the way other locales are setup (but with additional step)?

Any insight on this topic is helpful!
Thanks!

You can use the locale as per below examples in your _layout.twig file.

<body class="{% if craft.locale == 'ar' %}rtl{% endif %}">

Your css would then be:

.rtl {direction:rtl}

And in your <html> like so:

<!DOCTYPE html>
<html dir="{% if craft.locale == 'ar' %}rtl{% endif %} lang="
{{craft.locale}}">
<head>

https://www.w3.org/International/questions/qa-html-dir


Output of matrix field twice in one template I have a matrix field with with multiple types. In a template I want to loop through all of the entries of that matrix field as you normally would. However, I also want to access a bit of data from a specific type and I can not figure out how to do it. Very frustrating. Definitely would have been a no brainer to do in craft 2.5.
The matrix field has a type of "Instructor" which is an entry type. The entry has a field of "FullName".
So I want to say, if there is an entry type of Instructors in the matrix field, then output the full name of each entry selected in the matrix field of type "Instructor".
I've tried various methods based on the matrix blocks documentation and nothing works. The closest I get is the ID of the matrix field entry. Here is one example that should explain what I'm attempting to do.
  {% set classBlocks = entry.classBlocks %}
  {% if classBlocks|length %}
    {% set instructors = clone(classBlocks).type('instructors').all() %}
    by <strong>
      {% for instructor in instructors %}
        {% if not loop.first %}, {% endif %}
        {{ instructor.fullName }}
      {% endfor %}
    </strong>
  {% endif %}

Is this even possible? Seems pretty simple. At least in craft 2.5 I could have just looped through the matrix field and if the type was 'instructors' loop through the instructor entries outputting their full name.

Here's how I would approach this...
{% set classBlocks = entry.classBlocks.all() %}

{% if classBlocks | length %}
    {% set instructors = classBlocks | filter(block => block.type == 'instructors') %}
    by <strong>{{ instructors | map(instructor => instructor.fullName) | join(', ', ', and ') }}</strong>
{% endif %}

We're using several filters here to achieve what I think you're trying to achieve:

| filter is used to filter just the instructor blocks out of all your class blocks; this saves us from having to do the cloning and extra query mess and is more performant.
| map is used to take our array of instructor blocks and return an array containing just their names.
| join is used to take the array of names and create a nice readable list (I find it cleaner than doing the loop checking like you're doing) - e.g. "Bob, Matt, and Susy"

Let me know in the comments if you have any questions or if I'm not understanding the question correctly!


Setting up Craft's registration e-mails to be sent through GMail I have a question regarding how to set up Craft 3 RC8 to send e-mails through a corporate GMail account.

My website's MX records are pointed at GMail through Cloudflare
I tried setting this up when my website was on Craft 2.6. I set that up and my e-mail got blocked. I unblocked it through GSuite admin account and switched back to php.
When sending through PHP mail, my e-mails get sent into spam.

So I need help on whether how to get my e-mails sent through GMail without my account getting banned, or how to make the e-mails sent through PHP not go into spam.

I'm not sure how your email address got blocked, but Craft 3 uses the same SMTP smtp.gmail.com server and settings that Craft 2 uses when you choose to send email via "Gmail"

https://github.com/craftcms/cms/blob/develop/src/mail/transportadapters/Gmail.php#L91-L97

Sending email through PHPMail/sendmail is notoriously unreliable and I wouldn't recommend it on production.

https://craftcms.com/support/troubleshooting-email-errors#dealing-with-email-delivery-issues

And there are quite a few steps you can take to help ensure your email doesn't get treated as SPAM: https://blog.codinghorror.com/so-youd-like-to-send-some-email-through-code/


404 page redirect to homepage Is it possible to set up a 404 page, and after x amount of time redirecting to the homepage?

{% extends "_layout/_layout" %}

{% set metaTitle = "404" %}

{% set bodyId           = "404" %}
{% set bodyClass        = "404" %}

{% block content %}

Page not found

What’s worse, a hilarious 404 page can’t be found either.

{% endblock %}

You could always use a meta refresh

<meta http-equiv="refresh" content="5; url=http://example.com/">

With the number being the amount of time in seconds until the redirect


Craft pieces accessed by people on domain only How would you make portions of Craft available to only people that are "on the domain" and then let the rest of it be public facing? Would you need two instances of craft on 2 servers? One which is accessible only from "inside" and another that the public can access?

Would this entail running Craft on one webserver and the front-end on another? This way the webserver with craft on it is only accessed internally but the front-end can be accessed outside?

edit: I only want CP to be accessed by people who are logged in and on the work network. For security reasons a simple login is not enough.

The back end of craft (admin log in section or Control Panel) can only be accessed by admins and users that an admin has added. The front end will always be visible to everyone while the backend will be password protected.

If you want separate front end displays for people who have usernames and passwords and those who dont, you could add a front end log in form:

http://buildwithcraft.com/docs/templating/login-form

And display one set of templates for those who are logged in and another for those who arent logged in via conditional statements e.g.

{% if currentUser %}
  Protected info
{% else %}
  public Info
{% endif %}

If this isn't quite the answer you're looking for could you be slightly more specific with what you mean by "on the domain". Describe exactly what you're looking to achieve.


How do I upload photos into my assets list/library? I get the following error message when I try to upload the photo:

"Upload failed. The error message was: Error uploading the file: The file system path "/home/tramweb/public_html/craft/assets/content/" set for this source does not exist."

Have you created that directory? If not, you need to. Also be sure the directory you create has the correct permissionss (writable by the web server)


Wrapping a matrix block I'm outputting fields from a Matrix block, how do I then wrap all outputted fields from 'artworkFile' that are available with some HTML. Not individually wrapped, but all outputted fields.

Current Code:

{% case "artworkFile" %}
{% for asset in block.downloadUrl %}

{% endfor %}

Desired Output

Matrix Setup

Without understanding your use case or having the context of your full template and/or Matrix loop, it's very difficult to provide a good answer.

If you only want to "wrap all outputted blocks" in a container, it's as easy as putting the Matrix loop in a container, e.g.:

    {% for block in entry.myMatrixFieldHandle %}
        {% switch block.type %}
            {% case 'artworkText' %}
                ...
            {% case 'artworkImageSingle' %}
                ...
            {% case 'artworkImageDouble' %}
                ...
            {% case 'artworkFile' %}
            {% for asset in block.downloadUrl %}

            {% endfor %}
        {% endswitch %}
    {% endfor %}

Based on your comments, however, I suspect that what you really want is to only wrap the markup for all your artworkFile blocks.

If that is the case, you have to consider the fact that if you want to wrap all of the artworkFile blocks in a single container and not the other block types, then these artworkFile blocks will necessarily be removed from the general flow/chronology of your overall Matrix field. This may be exactly what you want, or a side effect you haven't considered - just wanted to mention it. (It also means that you could actually just move the artworkFile blocks to a separate Matrix field altogether, if that'd make any of this easier).

Anyway, there's at least a couple of ways to achieve the above with your current setup.

First, you can opt to remove the artworkFile switch case from your current Matrix loop, and create a secondary loop which only loops on artworkFile blocks somewhere else in your template. This secondary loop is then wrapped in a
 container, e.g.:

    {% for block in entry.myMatrixFieldHandle.type('artworkFile') %}
        {% for asset in block.downloadUrl %}

        {% endfor %}
    {% endfor %}

Another option is to leave the artworkFile switch in your current Matrix loop, but instead of outputting the markup directly, you can cache it in an array – this is done by first putting the markup in a variable by wrapping the block markup in a {% set %} tag pair, adding that variable to an array declared outside the loop, and finally print the collected markup by joining the contents of that array inside a container, e.g.:

{# Create an empty array outside the loop #}
{% set artworkFileBlocks = [] %}
{% for block in entry.myMatrixFieldHandle %}
    {% switch block.type %}
        {% case 'artworkText' %}
            ...
        {% case 'artworkImageSingle' %}
            ...
        {% case 'artworkImageDouble' %}
            ...
        {% case 'artworkFile' %}
        {# Cache the markup for the artworkFile block to a variable #}
        {% set artworkFileBlock %}
            {% for asset in block.downloadUrl %}

            {% endfor %}
        {% endset %}
        {# Add the single artworkFile block markup to the array #}
        {% set artworkFileBlocks = artworkFileBlocks|merge([artworkFileBlock]) %}
    {% endswitch %}
{% endfor %}

{# Echo all artworkFile markup in a common container, whereever you need it #}

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


How to use Twig to pull drop down menu items for a front end form I have this code that will pull the values of a checkbox or radio from the fields in Craft CMS, do be used and selected by a user on a front end form.

    {% for checkboxOption in currentUser.fieldHandle.options %}
        {% set checked = false %}

        {% if checkboxOption.selected %}
            {% set checked = true %}
        {% endif %}

            <input type="radio" value="{{ checkboxOption.value }}" name="fields[fieldHandle]" {% if checked %} checked="checked" {% endif %}>{{ checkboxOption.label }}

    {% endfor %}

How do I do the same for a dropdown list?

You'd do something very similar:

<select name="fields[fieldHandle]">

    {% for dropdownOption in currentUser.fieldHandle.options %}
        <option value="{{ dropdownOption.value }}"{% if option.selected %} selected{% endif %}>{{ dropdownOption.label }}</option>
    {% endfor %}

</select>


Delete plugin record via ajax I have been staring at this SE post on deleting using ajax and it's just not clicking.

For example, I have a plugin with a list of users. Each user has a delete icon next to it.

template

....

....

To get my javascript, I am using the init method in my main plugin class.

MyPlugin.php

if (craft->request->isCpRequeset() && craft()->userSession->isLoggedIn())
{
    craft->template->includeJsResource('path/to/script.js');
}

So far, so good.

script.js

....
$('.delete-user').click(function(event){
    var data = {
        'id': event.currentTarget.dataset.id,
        'csrfToken': // this is where i'm stuck
    };

    $.ajax({
        'type': 'post',
        'contentType': 'application/x-www-form-urlencoded; charset=utf-8',
        'cache': false,
        'url': 'path/to/deleteUser',
        'data': data,
        'dataType': 'json',
         'timeout': 5000
    }).done(function(status){
        console.log(status);
    }).fail(function(error){
        console.log(error);
    })
});
....

UserController.php

....
actionDeleteUser() {
    $this->requireAjaxRequest();
    error_log(__FUNCTION__); // to just test im getting this far.
}

As you can imagine, I'm getting a 400 Bad Request The CSRF token could not be verified.

I can put a hidden text field on my users "home page" using {{getCsrfInput}} and then grab that value no problem.

If I log out my data object, I can see it's got the correct id and csrfToken but I will still get the 400.

I'm not sure how to pass the token along with the data to the controller just yet it seems.

Thank you for your time!

EDIT

Well, I believe I see my error, but I'm confident there is a better way (at least I think there could be)

I wasn't passing the correct name/key.

script.js

var data = {
     'id': event.currentTarget.dataset.id,
     'CRAFT_CSRF_TOKEN': $('[name="CRAFT_CSRF_TOKEN"]).val()
}

I'm getting a 404 back now - great. I can figure that out. What i'm curious about is: Is there a better way?

I've put the {{getCsrfInput}} on my "home" page of my plugin that lists the users. I feel like i've just done a nice hack-job on my plugin.

Here's how you can pass in the CSRF token via JavaScript: http://buildwithcraft.com/help/csrf-protection#updating-your-javascript