Setting Variable to Specific Category for Loop How do you set a variable to a specific category so you can do an if or for loop? I have entries(staffList) associated to categories (Management,Staff,Other,PartTime)and I am trying to set a variable to do a loop off of and pull the entry fields but am having a difficult time setting the variable to pull just a specific category.

Below is what I am using to set:

{% set staff = craft.entries.section('staffList').relatedTo('Management') %}

Any ideas where I am going wrong?

You can't just pass a string (category name) to relatedTo, you need to pass either the category ID or a complete CategoryModel (you can also pass in an array of IDs or models, if there are several categories).

This means that unless you already have the category IDs or models, you'll need to pull them using the craft.categories service:

{% set management = craft.categories.title('Management').first() %}

{% set staff = craft.entries.section('staffList').relatedTo(management) %}

If you have multiple category groups, and there's a possibility you might have multiple categories across those groups with the same name, you should specify which category group the category you're after belongs to:

{% set management = craft.categories.group('categoryGroupHandle').title('Management').first() %}

If you want to pull staff entries related to multiple categories, here's how that could look:

{% set categories = craft.categories.title(['Management', 'Staff', 'Other']) %}

{% set staff = craft.entries.section('staffList').relatedTo(categories) %}

Finally, keep in mind that if you're rendering a category template (i.e. you've requested the URL for a particular category and Craft has rendered the template specified in the relevant category group's template path), Craft will actually populate the template with a category variable, containing the CategoryModel for the category belonging to that URL. If this is the case, then you simply need to do this:

{% set staff = craft.entries.section('staffList').relatedTo(category) %}


3 for 2 etc with Craft Commerce I'm looking for a way to offer "3 for 2 / 2 for 1" type options with Commerce.

I've been able to achieve this just by adding a default value to a coupon code input.

It works, kinda. If we need to stick with that then we'd be satisfied but where it fails is; say I have 6 items in the cart, I only get the offer on the first 3.

This is a small store with just a handful of items for sale so not a deal breaker but I can imagine other larger stores doing similar.

Adjusters have been mentioned on Twitter but unsure how to follow that through.

The documentation for adjusters is here:

https://craftcommerce.com/docs/adjusters
and
https://craftcommerce.com/docs/order-adjustment-model

Essentially your plugin creates a class that fulfils the interface at:
plugins/commerce/Commerce/Adjusters/Commerce_AdjusterInterface.php - which is very simple:

interface Commerce_AdjusterInterface
{
    /**
     * The adjust method modifies the order values (like baseShippingCost),
     * and records all adjustments by returning one or more orderAdjusterModels
     * to be saved on the order.
     *
     * @param Commerce_OrderModel $order
     * @param array $lineItems
     *
     * @return \Craft\Commerce_OrderAdjustmentModel[]
     */
    public function adjust(Commerce_OrderModel &$order, array $lineItems = []);
}

Your plugin (in your main plugin .php file) then just registers your new adjuster to hook it up.  I'd suggest storing your actual adjuster classes in files in an Adjusters folder, lime Commerce itself does.

//at the top, to include your class
require_once('Adjusters/BusinessLogic_TestAdjuster.php');

...

//this registers the adjuster with Commerce so it will be applied to carts
public function commerce_registerOrderAdjusters(){
  return [
    new \Commerce\Adjusters\BusinessLogic_TaxRemover
  ];
}

The adjuster (as you can see) has access to the order and line items and you can basically use any code you like to implement your discount offer, e.g. checking if the qty of something is over 3, and then reducing the price.  It builds & returns Commerce_OrderAdjustmentModels which describe the adjustment(s) made by the adjuster.

Here's a super simple example adjuster:

<?php

namespace Commerce\Adjusters;
use Craft\Commerce_LineItemModel;
use Craft\Commerce_OrderAdjustmentModel;
use Craft\Commerce_OrderModel;

class BusinessLogic_TaxRemover implements Commerce_AdjusterInterface {

 public function adjust(Commerce_OrderModel &$order, array $lineItems = []){

    $myAdjuster = new Commerce_OrderAdjustmentModel();

    $order->baseDiscount = $order->baseDiscount - 5;

    $myAdjuster->type = "Test";
    $myAdjuster->name = "Test Adjuster";
    $myAdjuster->description = "Takes $5 off the order";
    $myAdjuster->amount = -5.0;
    $myAdjuster->orderId = $order->id;
    //if your Adjuster affects lineItems rather than the total, you record the ids here
    $myAdjuster->optionsJson = ['lineItemsAffected'=>null];
    $myAdjuster->included = false;

    return [$myAdjuster];

   }

}

My advice is to being it to generate plugin scaffolding using: http://pluginfactory.io/ - to get you started - removes a lot of the pain points in getting started (such as the cursed capitilisation issues which I swear are the hardest part about getting started with plugins!).

Alternatively, you can take the scaffolding from https://github.com/lindseydiloreto/craft-businesslogic - and put in there, which makes good sense as this pretty much the textbook definition of business logic.  Just clone that, add your adjuster class, and do the registration and away you go!

As long as you're comfortable with PHP implementing a discounter like you describe is really quite simple.  If you're not then it's a perfect way to get more comfortable and I'm sure folks will be happy to help along the way!


Getting Invalid Type Error with no Key Path Value I am trying to specify a section to publish an article to and am using the addMetaData method in Article.php which my article class is extending.

The method in the base class allows for nested array properties by using a dot delimeter syntax so I have this right now in the init method of my article class

$this->addMetadata("links.sections", "https://news-api.apple.com/sections/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");

Here is the doc from apple news https://developer.apple.com/documentation/apple_news/code

[code] => 400
[message] => HTTP/1.1 400 Bad Request
[response] => stdClass Object
    (
        [errors] => Array
            (
                [0] => stdClass Object
                    (
                        [code] => INVALID_TYPE
                    )

            )

    )

)
 in /app/vendor/chapter-three/apple-news-api/src/PublisherAPI/Base.php on line 197

The error message isn't providing a key path with a field name so I'm a little lost as to debuggin the error.

If anyone has experience with the plugin and can point me in the right direction that would be great!

EDIT

This is a case of staring at your computer for too long. The sections needs to be an array so it should've been

$this->addMetadata("links.sections", ["https://news-api.apple.com/sections/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]);

and not

$this->addMetadata("links.sections", "https://news-api.apple.com/sections/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");


Template path from plugin not working from CP I've written a plugin that sends and email to a user group on new entry save in a particular section. Most entries will be made from the front end, and the plugin is working great in that regard. However, if a user attempts to create a new entry from the CP, the plugin cannot find my designated template path and throws an error. Any help is greatly appreciated.

if ($isNewEntry and $entrySection == 'Questions')
{
    //Get all users from UserGroup
    $user_criteria = craft()->elements->getCriteria(ElementType::User);
    $user_criteria->groupId = '9';
    $users = $user_criteria->find();
    $template = '/email/question-email.html';

    //Send to each User
    foreach ($users as $user)
    {
        //Send Mail
        $email = new EmailModel();
        $email->toEmail = $user->email;
        $email->subject = 'New Question';
        $email->htmlBody = "{% extends '{$template}' %}".
            "{% set body %}".
            '

A new question has been posted by '.$entry->author->name.':

Question: '.$entry->title.'

Description: '.$entry->questionDetails.'

Attachments: '.$entry->questionAttachments->total().'

url.'" title="'.$entry->title.'">Answer Question

                        '
            ."{% endset %}";

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

You probably just need to call PathService::setTemplatesPath() and pass in the path to your plugin's template folder.

It would look something like:

// Save the current template path.
$originalPath = craft()->path->getTemplatesPath();

// Set your plugin's template path.
$path = craft()->path->getPluginsPath().'/mypluginfolder/templates/';
craft()->path->setTemplatesPath($path);

//Send to each User
foreach ($users as $user)
{
    //Send Mail
    $email = new EmailModel();
    $email->toEmail = $user->email;
    $email->subject = 'New Question';
    $email->htmlBody = "{% extends '{$template}' %}".
        "{% set body %}".
        '

A new question has been posted by '.$entry->author->name.':

Question: '.$entry->title.'

Description: '.$entry->questionDetails.'

Attachments: '.$entry->questionAttachments->total().'

url.'" title="'.$entry->title.'">Answer Question

                    '
        ."{% endset %}";

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

// Reset the original path.
craft()->path->setTemplatesPath($originalPath);


Translate field name displayed within validation error messages? I'm adding validation errors to fields using addErrors($record->getErrors()) which then read like e.g. "My Field cannot be blank."

But I'm wondering where I can translate the field name, which I think is generated from the ID attribute myField?

Update:

Template:

<input type="hidden" name="action" value="example/saveExample">

{{ forms.textField({
    label: 'My Field'|t,
    id: 'myField',
    name: 'myField',
    value: null,
    errors: example is defined ? example.errors('myField'),
}) }}

Service function called from actionSaveExample:

public function saveExample(ExampleModel $example)
{
    $exampleRecord = ExampleRecord::model()->findById($example->id);
    $exampleRecord->myField = $example->myField;

    if ($exampleRecord->validate())
    {
        // save
    }
    else
    {
        $rule->addErrors($ruleRecord->getErrors());

        return false;
    }
}

Craft/Yii sees that string as:

'{attribute} cannot be blank.' => '{attribute} darf nicht leer sein.',

Where {attribute} is the dynamic part - in this case your field handle.

If I'm understanding your question correctly, there isn't a way to translate that field handle in this context because you're in charge of what it's called to begin with.

I would assume, for example, if you're using the German locale, that you would already have a German context field handle name in place already.


Setting up VueJS in a Craft CMS Project Trying to better my skills and take a simple Craft-driven website to a more modern level with some VueJS. I'm still very much learning Vue.

The course I've been following recommends using vue-cli to create an app, and it runs it's own development server. I've done this and things work well. However, I'm not sure the best way to integrate that with Craft. Do I run the vue create project-name inside of my Craft project? What does that do for the development server? I'm currently using MAMP for Craft, do they clash?

I would like to eventually get to a point where I use GraphQL and then in theory my Vue app can live elsewhere and this is not a problem. However, whilst I'm learning I just want to add some very basic things to my existing site, like responsive menu toggles, dropdown menus etc. I understand I could just use the CDN and do things that way, but then I'm not sure of the best approach to take with templates and components etc.

As you can probably tell, very much at a beginner level so I would appreciate any thoughts. I've read LOTS of guides, but none seem to really answer these questions.

Many thanks in advance!

They don't need to clash per se, but it depends on the way you build your project structure. I'm front-end focussed, so my setup is like:

    projectfolder
      src
      public

public

(you could also call this 'craft' or 'dist' or another name to indicate this is the final product) This is where Craft is installed with the craft installer. So nothing Craft related will be outside that folder and therefor should not crash with the vue installer.

src

Src countains all front-end source files, like es6 javascript files, Scss files, static assets, fonts etc. I use webpack to transpile these to a folder inside public\web, so the destination files will always be inside public\web and therefore available for use in templates.

using the vue installer?

The vue installer is probably mostly/only focussed on projects where you only use front end. But apart from that; it has it's own project structure you probably can't change. You might be able to find a way to use the installer and make the created structure work with Craft, but you still need other tools, like webpack, to make it work without having all front end sources inside the folders created with the craft installer.

So my advise

is leave that installer and learn how to create your own structure. That way you are way more flexible, don't need to use their rules and can leave things behind you don't need and add your own config. In the end you will install more front-end tools and configuration files for linters, transpilers etc. anyway and you can't do that with the vue installer alone. So just create your own setup with all your configs and use that for every new project would be my advise.


Including customer details in Stripe emails I have one of those questions that I have no idea how to phrase…

Basically Stripe is sending an email notification to my client's ‘accounts department’ on successful payment, but it only includes their email address and the value of their order.

What they’ve requested is that it also includes the customer name in the email that goes to the accounts department.

From looking at the details of a Stripe Event I can see that none of the address details are in there, so either we’ve messed up with out integration, or commerce doesn’t send this info?

Is anyone able to shed some light on that?

Probably best to use Craft Commerce emails, then you can put whatever information you want into the emails to your accounts departments. Like: {{ order.billingAddress.fullName }}

If the billing address information is not being sent to Stripe, you might need to modify your checkout.js or stripe.js payment screen implementation to ensure it sends the billing address information through along with the card info: https://stripe.com/docs/stripe.js#stripe-create-token

Unlike other gateways, Stripe is building the card request within javascript to make the token, when our server sends the payment request, it only uses that token - no billing address is sent again.


8-bit png's get distorted when you upload them When I upload an 8-bit transparent png, it gets distorted. Is there any way to fix that?

The fix for distorted 8bit png's is to install imagick on the server.

I'm running whm/cpanel and there were some hoops to jump through to get it installed. I installed it in terminal and then in WHM, I had to enable it for PHP.


How can I get spaces to render in my dropdown option label template call? Example

Let entry.dropdownOption.label = "option with spaces"

{% for entry in entry.dropdown %}
    {{ entry.dropdownOption.label }}
{% endfor %}

renders as in my template: "optionwithspaces"

How can I instead get it to render as "option with spaces"?

It turns out this was happening because i'd set some values for my content and then changed the structure of the field. I just reset the values for my content and all was well.


SmartMap throwing back javascript error On my _entry page, I'm trying to get the SmartMap plugin to generate a dynamic map, but it gets stuck on 'Loading Map'

Using this as the code:

{{ craft.smartMap.map(entry.theEventAddress) }}

and the console tells me:

  Uncaught SyntaxError: Unexpected token ,

The documentation is a bit vague on this, and it doesn't really tell you how to fix JavaScript errors. Also, I have API keys in the settings.

EDIT

Here's the output as requested. Using the latest version of the plugin. (2.3.1)

<script type="text/javascript">
/*<![CDATA[*/
if (logSmartMap) {console.log("[smartmap-mapcanvas-1] Drawing map...");}
smartMap.createMap("smartmap-mapcanvas-1", {"center":smartMap.coords(-48.876667,-123.393333),"scrollwheel":false});
if (logSmartMap) {console.log("[smartmap-mapcanvas-1.2497.theEventAddress] Drawing marker...");}
smartMap.createMarker("smartmap-mapcanvas-1.2497.theEventAddress", {"mapId":"smartmap-mapcanvas-1","map":smartMap.map["smartmap-mapcanvas-1"],"position":smartMap.coords(,)});
if (logSmartMap) {console.log("[smartmap-mapcanvas-1] Fitting bounds...");}
smartMap.fitBounds("smartmap-mapcanvas-1");
/*]]>*/
</script>

This is the problem line:

smartMap.createMarker("smartmap-mapcanvas-1.2497.theEventAddress", {"mapId":"smartmap-mapcanvas-1","map":smartMap.map["smartmap-mapcanvas-1"],"position":smartMap.coords(,)});

There are actually two problems happening here...

(1) For a location to appear on a map, it's critical for the latitude & longitude to be filled in. Within the UX of the Address field, this can happen a few different ways... as you mentioned, clicking "Select Address" is one of the main ways of doing so. Additionally, the address selection modal will be automatically triggered when you tab between fields.

(2) There is apparently a small bug in Smart Map, which I'll go ahead and patch. If a location is lacking coordinates, the JS shouldn't attempt to render that marker. It should just be omitted from the map instead.

Hope that helps. I'll release a patch shortly.

UPDATE:

I just pushed out Smart Map v2.3.2, which fixes the bug. Instead of triggering a JS error, the marker will simply be skipped. In that case, the JS console will output the following message:

  Unable to draw marker, invalid coordinates.

Hope that helps! For anyone who may need further support on this issue, feel free to email support@doublesecretagency.com.


Combining custom field filters on craft.entries I'm building a book store which filters entries based on GET variables. For the most part, this is working well just merging in various filters to a params variable and then passing craft.entries(params).

The tricky bit is that each book can have an ebook price and a paperback price.

If the books are being filtered to a certain format, then I just do this:

{% if 'ebook' in formats %}
    {% set params = params|merge({ 'ebookRrp' : 'and, > '~price_from~', < '~price_to }) %}
{% endif %}
{% if 'pback' in formats %}
    {% set params = params|merge({ 'paperbackRrp' : 'and, > '~price_from~', < '~price_to }) %}
{% endif %}

That works well for an individual format, but if no format is selected it becomes too specific.

It seems like I need to do something like the following, but this doesn't seem to have any effect on what is returned:

{% set params = params|merge(['or',
    { 'ebookRrp' : 'and, > '~price_from~', < '~price_to },
    { 'paperbackRrp' : 'and, > '~price_from~', < '~price_to }
]) %}

Any ideas?

--- Update with full code: ---

{# Prep the entries params #}
{% set params = {
    section: 'books',
    with: [
        'bookAuthor',
        'booksImage',
    ]
} %}

{# Prep the relatedTo params #}
{% set relatedToParams = ['and'] %}

{# need to do a bunch of checks here and combine them into one relatedto query #}

{% if craft.request.getParam('author') %}
    {% set relatedToParams = relatedToParams|merge([{
        targetElement: craft.request.getParam('author'),
        field: 'bookAuthor'
    }]) %}
{% endif %}

{% if entry.section.handle == 'collections' %}
    {% set relatedToParams = relatedToParams|merge([{
        sourceElement: entry
    }]) %}
{% endif %}

{% if craft.request.getParam('genre') %}
    {% set relatedToParams = relatedToParams|merge([{
        targetElement: ['and']|merge(craft.request.getParam('genre')),
        field: 'genre'
    }]) %}
{% endif %}

{# Merge in the relatedTo params if they exist #}
{% if relatedToParams|length > 1 %}
    {% set params = params|merge({ 'relatedTo' : relatedToParams }) %}
{% endif %}

{% set maxPrice = 10000 %} {# temp high number before working out highest value #}
{% set price_from = craft.request.getParam('price_from') ?? 0 %}
{% set price_to = craft.request.getParam('price_to') ?? maxPrice %}

{% set formats = craft.request.getParam('format') ?? null %}
{% if 'ebook' in formats %}
    {% set params = params|merge({ 'isEbook' : 1 }) %}
    {% set params = params|merge({ 'ebookRrp' : 'and, > '~price_from~', < '~price_to }) %}
{% endif %}
{% if 'pback' in formats %}
    {% set params = params|merge({ 'isPaperback' : 1 }) %}
    {% set params = params|merge({ 'paperbackRrp' : 'and, > '~price_from~', < '~price_to }) %}
{% endif %}

{#
{% if formats is null %}
    somehow filter price for both formats as an or?
    This doesn't work:
    {% set params = params|merge(['or',
        { 'ebookRrp' : 'and, > '~price_from~', < '~price_to },
        { 'paperbackRrp' : 'and, > '~price_from~', < '~price_to }
    ]) %}
{% endif %}
#}

{# Finally get the relevant books #}
{% set books = craft.entries(params) %}

It's currently not possible to combine criteria params like this (see Complex logic on a ElementCriteriaModel parameter?).
As a workaround you'd prepare an idParam object, similar to your relatedToParam and collect all matching element IDs
making use of additional criteria models and the ids method.

{# Prep the id param #}
{% set idParam = [] %}

{% if formats is null %}
    {% set ebookEntryIds = craft.entries({
        section: 'books',
        ebookRrp: 'and, > '~price_from~', < '~price_to
    }).ids() %}

    {% set pbackEntryIds = craft.entries({
        section: 'books',
        paperbackRrp: 'and, > '~price_from~', < '~price_to
    }).ids() %}

    {% set idParam = ebookEntryIds|merge(pbackEntryIds) %}
{% endif %}

{# Merge in the id param #}
{% if idParam|length %}
    {% set params = params|merge({ id: idParam }) %}
{% endif %}


Undefined property: Craft\WebApp::$request What could cause Craft to not define the request object?

I have a local site that recently stopped loading (front-end and back-end)

The error is happening in WebApp.php (line 630) when $this->request->getBrowserLanguages() method runs.  The script fails because $this->request is not defined.

Other Craft installations work just fine.  This site works just fine on a dev and live server with the same code. Any ideas on what I bumped?

This issue turned out to be caused by a config setting that had been added to general.php

'cacheMethod' => 'apc'

The server had APC installed but my local environment did not.  There were a couple options to fix this:

Install Alternative PHP Cache (APC)
Setup a multi-environment config and override the cacheMethod setting with another method that is supported in your local environment, such as 'cacheMethod' => 'file'
Remove this line as it may not be needed anyway. apc is PHP byte caching and if your running PHP 5.5, that's enabled by default and apc is unnecessary


How do I cache with the ElementAPI plugin? I'm replacing some templates with the ElementAPI plugin.

In my templates, I would use the cache tag, and rely on Craft to handle invalidation when the elements changed.

How can I achieve a similar thing now that I'm using the ElementAPI plugin?

The ElementAPI plugin itself doesn't seem to offer a caching solution, but there is a fork from a guy which has implemented a caching option cache and cacheTime:
https://github.com/theolampert/ElementAPI

Update: They have introduced a new cache option in the official ElementAPI plugin (version 1.5).


divisible by strange issue I have an unusual issue with a for loop divisible by. The code works fine initially as it loops through the entries and closes a the list element on the 4th loop. However when it gets to the end of the entries and there are less than 4 left it strangely adds them into the source but does not display them visually on the page or in chromes inspector?? Has anyone come across this before? Nothing is hiding the last entries and if I change divisible by to different values it does the same only show entries that are divisible by and loses the remainder.

The code is as follows:

{% if loop.first %}

{% endif %}

..... code pulling out entry details into divs

{% if loop.index is divisible by(4) and not loop.last %}

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

{% endif %}

You have some other bug somewhere else. Check the code pulling out entry details to be sure that it doesn't modify loop or the list you are looping on, or the current item.

This code works:

{% for item in (1..10) %}
  {% if loop.first %} [ {% endif %}
  {{ item }}
  {% if loop.index is divisible by(4) and not loop.last %}
    ] [
  {% endif %}
  {% if loop.last %} ] {% endif %}
{% endfor %}

And outputs this:

[ 1 2 3 4 ] [ 5 6 7 8 ] [ 9 10 ]

You can simplify it a bit:

{% for item in (1..10) %}
  {% if loop.first %} [ {% endif %}
  {{ item }}
  {% if loop.index is divisible by(4) or loop.last %}
    ] [
  {% endif %}
{% endfor %}

You also can use the batch filter, which I think makes things slightly clearer:

{% for b in (1..10) | batch(4) %}
  [
    {% for item in b %}
      {{ item }}
    {% endfor %}
  ]
{% endfor %}


Does Craft have a styleguide for the admin, like Bootstrap? Does Craft have a styleguide for the admin, like Bootstrap? I'd like to know how to create plugin interfaces that match with Craft

Already found the answer, it seems it has a js lib called Garnish for UI interaction, and some grid system. Both are undocumented as of yet.


Add Record without deleting data I'm working on a plugin, and would like to add a new Record, I already have some records, and would like to keep the data.

Is there a way to activate a new record type, without re-installing the plugin, thus loosing the other records data?

The Craft website has a great article on this, you can simply use migrations following this document:

https://craftcms.com/docs/plugins/migrations

  If your schema changes over the life of your plugin, you can write a migration to keep existing installations updated with the latest schema. Craft automatically checks for new migrations whenever a plugin’s version number changes.

In summary, you can create the Record and define all the attributes, then create a migration that would create the new table with those attributes. This would be done in the safeUp() method that utilizes Craft create command:

craft()->db->createCommand()->createTable('new_table_record_name', $attributesArray, null, true);

When the plugin is updated, it will run the migrations and create the table.


How can I group entries based on a string value? I have been able to successfully group entries via dateTime field. I have been trying to carry over the same type of logic only instead of using .date just key off of the field value.

{# Get all entries from the section. #}
{% set entries = craft.entries.section('mySection') %}

{# Loop through the entries and get the user account associated with each. #}
{% for profiles in entries %}
    {% set users = profiles.userAccount %}

    {% if users|length %}
        {% for year, user in users|group('myYearField') %}

{{ year }}
 {# 2016 #}

                {% for user in users %}

{{ user.name }}

                {% endfor %}

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

Currently what I am getting is:

2016
    User
2016
    User
...

What I am trying to get (I swear I had this working at one point) is this:

2016
    User
    User

2017
   User
   User

EDIT

It seems my trouble comes from not accessing the users field correctly.
Updated (but still incorrect) example:

{% set allProfiles = craft.entries.section('mySection') %}
{% if allProfiles|length %}
    {{ allProfiles|length }} {# 3 #}
    {% for profiles in allProfiles %}
        {% for profile in profiles.userAccount %}
            {% for year, user in profile|group('yearFieldHandle') %}

{{ year }}
  {# No output #}

{{ profile.firstName }}
    {# Bob #}

{{ profile.myYear }}
 {# 2016 #}
            {% endfor %}
        {% endfor %}
    {% endfor %}
{% endif %}

There’s a lot to unpack here. Before I get to the solution, I’ll step through each of your examples, line-by-line. Hopefully that will help explain the behavior you’re seeing.

First Example

{# Get all entries from the section. #}
{% set entries = craft.entries.section('mySection') %}

Your comment is correct – this will fetch all the entries from section mySection and assign them to a new variable called entries.

{# Loop through the entries and get the user account associated with each. #}
{% for profiles in entries %}

Start a loop on the entries variable. Within the loop, each profile entry will be represented by a profiles variable.

Note: it’s misleading to call your variable “profiles” rather than “profile”, since it will only represent a single profile entry.

    {% set users = profiles.userAccount %}

Fetch all of the user accounts related to the current profile (profiles) via the userAccount field, and assign them to a new variable called users.

    {% if users|length %}

Only do the following if there were any related users…

        {% for year, user in users|group('myYearField') %}

Turn the users (users) that are related to the current profile (profiles) into a multi-dimensional array, grouped by their myYearField field values. Then start a loop on that multi-dimensional array. Within the loop, each myYearField value will be represented by a year variable, and the array of grouped user accounts will be represented by a user variable.

Note: This is where things start really going wrong. You want to group all of your users by year – not just the ones that are related to the current profile. Also, your choice of “user” as a variable name here is misleading since it’s actually an array of grouped users; not a single user.

{{ year }}
 {# 2016 #}

Output the current grouping’s myYearField value, which was assigned to a year variable.

                {% for user in users %}

Start a loop on the same users variable that you’re already in the middle of looping through. users here refers to the same users variable that was defined in the 3rd codeblock.

{{ user.name }}

Output the profile entry’s (profiles) URL, and the current user’s (user) name.

                {% endfor %}

Close out the loop on the user accounts related to the current profile entry (users).

        {% endfor %}

Close out the loop on the multi-dimensional array of year-user groupings based on the user accounts related to the current profile entry (users).

    {% endif %}

Close out the conditional checking if there any users related to the current profile entry.

{% endfor %}

Close out the loop on the profile entries (entries).

Second Example

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

Fetch all the entries from section mySection and assign them to a new variable called allProfiles.

{% if allProfiles|length %}

Only do the following if there were any profile entries…

    {{ allProfiles|length }} {# 3 #}

Output the total number of profile entries.

    {% for profiles in allProfiles %}

Start a loop on the allProfiles variable. Within the loop, each profile entry will be represented by a profiles variable.

Note: as with the last example, it’s misleading to call your variable “profiles” rather than “profile”, since it will only represent a single profile entry.

        {% for profile in profiles.userAccount %}

Fetch all of the user accounts related to the current profile (profiles) via the userAccount field. Start a loop on the user accounts. Within the loop, each user account will be represented by a profile variable.

Note: it’s misleading to call your variable “profile”, since it is actually the user account, not the profile entry.

            {% for year, user in profile|group('yearFieldHandle') %}

Turn the current user account (profile) into a multi-dimensional array, grouped by its properties’ yearFieldHandle sub-property values (which isn’t a thing). Then start a loop on that multi-dimensional array. Within the loop, each yearFieldHandle value will be represented by a year variable, and the array of grouped user account properties will be represented by a user variable.

Note: Since profile is a single element (the user account) and not an array of elements, nothing about this is likely doing what you expected it to.

{{ year }}
  {# No output #}

Output the current grouping’s yearFieldHandle value, which was assigned to a year variable. (This is the equivalent of doing something like profile.id.yearFieldHandle. It’s expected that you wouldn’t get any output.)

{{ profile.firstName }}
    {# Bob #}

Output the user account’s first name.

{{ profile.myYear }}
 {# 2016 #}

Output the user account’s myYear custom field value.

            {% endfor %}

Close out the loop on the multi-dimensional array of year-user account property groupings based on the current user account defined by the parent loop.

        {% endfor %}

Close out the loop on the user accounts related to the current profile entry.

    {% endfor %}

Close out the loop on the profile entries (allProfiles).

{% endif %}

Close out the conditional checking if there any profile entries.

Solution

To group all of your profiles by their related user accounts’ custom date fields, first let’s load the profiles, eager-loaded with their related user accounts:

{% set profiles = craft.entries({
    section: 'userProfiles',
    with: ['userAccount']
}) %}

Next let’s group the profiles by the myYear custom field on their related user accounts:

{% set profilesByYear = profiles|group('userAccount[0].myYear') %}

Now we can loop through the years, and their corresponding user profiles/accounts:

{% for year, yearProfiles in profilesByYear %}

{{ year }}

    {% for profile in yearProfiles %}
        {% set user = profile.userAccount[0] ?? null %}
        {% if user %}

{{ user.firstName }}

{{ user.myYear }}

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


{% paginate %} Tag issue, no prevUrl / nextUrl Data I have problem with the {% paginate%} tag.

Setting:

Craft CMS 3.0.18
Channel with 3 Entries -> handle "newsBlog"

{% paginate craft.entries.section('newsBlog') as pageInfo, pageEntries %}

When i open the second Entry in the News-Channel i get following values

pageInfo.total: 3
pageInfo.currentPage: 1 -> Why?
pageInfo.nextUrl: empty -> Why?
pageInfo.prevUrl: empty -> Why?

I did a lot of Craft 2 projects without pagination problems.

What did I do wrong? Thx for help!

You need to set a limit on your element query, so Craft knows how many entries you want per page:

{% paginate craft.entries.section('newsBlog').limit(1) as pageInfo, pageEntries %}

If you don't set a limit, {% paginate %} will return a single page with all entries, because the default value for limit in Craft 3 is null (in Craft 2, the default was 100).


How to limit the amount of entries that is output when limit doesn't work I'm trying to limit the amount of entries on my page.

Currently I want 1 news entry (based on if the checkbox is ticked, there could be 10 entries with the checkbox ticked but I only want to show 1 (the latest)) The issue is that the limit() twig function doesn't work with my elementCriteriaModel.

My code:

{# Checking if any news entries have the FeaturedEntry field completed #}
{% for entry in craft.entries.section('news').level(2) %}

    {% if entry.featured.contains('isFeatured') %}

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

News *Chosen*

{{ entry.title }}

                        {{entry.author}}

    {% else %}
            Do Something Else Here

If I attach the limit function to {% for entry in craft.entries.section('news').level(2).limit(1) %}, this only limits the amount of entries my selector can check for the featured field. If i put limit(1) it only checks the first news entry for the field.

I know i can use the slice function but I'm having some trouble inserting it into the right place.

Any help would be very appreciated.

What you probably want is to order them by date and than select the first.

{% set news = craft.entries.section('news').order('postDate desc').first() %}

And if you want to use limit somewhere (but I don't think this is what you are trying to get here) you should add limit(1) to this {% set news = craft.entries.section('news').limit(1) %}


Showing Entries based on an Entries Field but also showing a slug in the url I have a Channel 'Articles' with an Entries field which is linked to entries in 'Staff Members' channel.  This is so that articles can be assigned to different members of staff.

I have the generic listing (all articles) page working fine I just need to show the same page but filtered by a selected staff memeber. E.g. When viewing an individual article by a member of staff I would like to link to a page which only lists the articles assigned to that particular staff member.  This far would be ok but if possible I would also like to get the staff member name into the url... http://mysite/staff/joe-bloggs/articles

Any suggestions?

Many thanks

Set up a custom route in config/routes.php so that that URI points to your existing listing template:

return [
    'staff/<route_staffMember>/articles' => ['template' => '_listings/default'],
];

Then simply use the relatedTo parameter to alter which entries get listed. The template will have access to a variable called route_staffMember which you can use to fork logic between the filtered/non-filtered views. (I like to prefix these variables with route_ because when I look at the code 6 months later I always scratch my head wondering where this magic variable is being set!)

For example:

_listings/default

{% set staffMemberId = route_staffMember is defined ? craft.entries.section('staff').slug(route_staffMember).ids() : null %}
{% set entries = craft.entries.section('articles').relatedTo(staffMemberId).all() %}

{# ...or, if you're using pagination: #}
{% paginate craft.entries.section('articles').relatedTo(staffMemberId).limit(12) as pageInfo, entries %}


Sorting categories into list put single one at end of it I'm trying to put a single category (as long as it's populated) at the end of an category list. My code is working so far, is there an easy way to pick out the 'Others' category and append it to the list?

{% set entryIds = craft.entries.section('toys').ids() %}
{% set categories = craft.categories.relatedTo({ sourceElement:
    entryIds }).craft.categories.group('brands').find() %}

{% for category in categories %}
    {% set image = craft.assets.relatedTo(category).first() %}

        {% if image|length %}

        {% else %}

        {% endif %}

{% endfor %}

Thanks for any help, ready to learn something new.

Is there a reason why you don’t just drag the “Others” category to the end in the CP? The sort order will be the same for the front-end with your code:

{% set entryIds = craft.entries.section('toys').ids() %}
{% set categories = craft.categories({
    relatedTo: { sourceElement: entryIds },
    group: 'brands',
}) %}

I corrected your code, you never append another .craft.categories criteria model to a criteria model. Have a look at the docs for the allowed syntax:

https://craftcms.com/docs/templating/elementcriteriamodel

Update:

Here’s example code that should work to list the categories alphabetically with the special “Others” category listed at the end.

{% set entryIds = craft.entries.section('toys').ids() %}

{% set categories = craft.categories({
    relatedTo: { sourceElement: entryIds },
    group: 'brands',
    order: 'title',
}) %}

{% set otherCategory = null %}

{% for category in categories %}
    {% if category.id == 72 %}
        {% set otherCategory = category %}
    {% else %}
        {{ category.title }}
    {% endif %}
{% endfor %}

{% if otherCategory %}
    {{ otherCategory.title }}
{% endif %}


Twig's 'start with' conditional returns 500 error The following is within the index template on a site in production:

{% set domain = craft.request.getServerName() %}
{% if domain starts with 'subdomain' %}
    {% include 'subdomain/index' %}
{% else %}
    {% include 'homepage' %}
{% endif %}

It works without any problem on that server. I've just copied the site over to a another server for development, but that same code seems to be responsible for a 500 error. The following works, so I think it's to do with Twig's start with conditional:

{% set domain = craft.request.getServerName() %}
{% if domain == 'subdomain.domain.com' %}
    {% include 'subdomain/index' %}
{% else %}
    {% include 'homepage' %}
{% endif %}

I'm assuming it's a PHP setting - does anyone know which?

I was able to reproduce this without the starts with, and editing the usual PHP settings which cause problems with uploading assets sorted this out.


Install Redactor plugin I would like to install a Redactor plugin (http://imperavi.com/redactor/plugins/font-color/)

I placed the fontcolor.js into craft/app/ressources/lib/redactor/plugins
and added
    buttons: ['fontcolor', 'backcolor']
    plugins: ['fontcolor'] to my redactor json config file.
but without success.

Am I missing something ?

Thanks a lot,

Antoine

If this is still required I've built a plugin, Redactor Extras: https://github.com/elliotlewis/Redactor-Extras

You can add your own JS via the Plugin. I'm updating and adding plugins from Redactors site regularly. I'll add font-color next. But good use with your own code.


Get saved content of entry I hooked up into the content.saveContent event. I want to save the entry content into a different application. Is there a way to retrieve the content as a Json (Or something more friendly). I think do $event->params['content']->title is now the best option.

$event->params['content] will return a ContentModel object.

You can either access specific fields directly, like you found, or loop through them all with ->getAttributes() grabbing the ones you want to save externally.


Issues with compiled_templates - call to undefined method We have two web servers in a load-balanced setup. Both servers are sharing a single database. The craft and public folders are rsync-ed between the two so changes on those folders in web1 propagate into web2.

We tried loading a video atom using the following twig:

{% case "video" %}
{{ atom.video(block) }}

It works on our local dev copy and on our staging server at AWS, however on our live site we get this error:

I tried copying the whole runtime folder from AWS over to the live web servers but that did not fix it.

Any ideas on how to get it to work? I would consider myself a novice to Craft so pardon me if there's something obvious that I'm missing.

Thanks in advance.

Update:

Already figured out that it was an error with the upload. Some macros were not properly copied over into live.

It is my bad. Thanks to Brad Bell, I went to check whether the macros were on both the staging and live servers. The macros existed on staging but not on live.


Admin CSS missing from hosted site The development site works locally, but on a live hosted server the admin CSS is missing. The admin JS shows up. The public-facing site works fine on the live server and I can log in to the admin area, but with the admin CSS missing, the admin area is nearly useless. A few potentially useful pieces of information:

I followed the Craft "Troubleshooting Missing CSS/JS in the Control Panel" instructions  and verified that zlib.output_compression is disabled.
The generated CSS links from the admin area look like they should work, especially since the JS links do work.
I'm running PHP 7.0.0 locally, using MAMP.
The hosting company, Nexcess, is running PHP 7.0.6 on my account.

I'm stumped as to why this is happening or how to troubleshoot it.

A single line in my gitignore file, CSS was preventing the Admin CSS from deploying to the live server. While this problem isn't specific to Craft, I'm leaving it here in case others encounter the issue.

That line only prevented folders named CSS from deploying, which is why the public site CSS deployed without a problem, making the problem less obvious.


When uploading an asset, the asset is not selected after upload When uploading an asset through either the assets tab, or within an asset field - the asset uploads successfully but doesn't get automatically selected for insertion.

To get around the issue I have to search for asset filename and then select it.

Ideally the asset should get selected by default ready to be inserted by clicking 'select'.

We have quite a few assets, so there is auto pagination/load more which might be causing the issue?

Any help appreciated.

We have quite a few assets, so there is auto pagination/load more which might be causing the issue?"

You nailed it. It's a known issue and we just haven't figured out a clean way to to select the items when they show up in a different page.

I'd suggest adding this to the feedback site as it feels more like a feature request than anything.


Template routes not respecting URL Say i have a url: /lists/more-lists/even-more-lists and i have a route  lists/more-lists/*.

If i hit example.com/lists/more-lists/even-more-lists then it tries to load the index template (not the template defined by my route)

If i hit  example.com/lists/more-lists/foo then it loads the template defined by my template route.

I should add, that the url segments are category url's.

So, if a category section has "has it's own url's" ticked, then the template defined there will take precedence over the custom route. Custom routes i'm told are a fallback and will not override already set routing.


How do I get an empty table field type to display on the front-end so a user can update their profile? I'm building out a front end form that users will use to fill out with their information. Naturally, when a user first signs up, their profile fields are empty and a blank space appears for them to add in their info later.

This works well on all fields on my form except the table field type. If nothing has been entered the loop will simply display nothing. I've been reading through this answer, but so far checking is empty or |length is not working on my form.

Here's my current loop, which works great as long as the info is entered through the CP. How do I get an empty table field type to display on the front-end so a user can update their profile?

    Institution
    {% for row in currentUser.providerCredentials %}
      <input type="text" id="accreditingInstitution" class="name-field" name="fields[providerCredentials][0][col1]" value="{{row.accreditingInstitution}}">
    {% endfor %}

As far as I know, I don't think that there is any built-in way to replicate the table fieldtype functionality on the front-end (i.e. with 'add row','remove row', etc.) I think that you would have to create your own buttons and then manipulate the dom using javascript.

In your template you would want something like this:

Provider Credentials

    {% for row in currentUser.providerCredentials %}

            Institution
            <input type="text" class="name-field" name="fields[providerCredentials][{{ loop.index }}][col1]" value="{{ row.accreditingInstitution }}">

    {% endfor %}

Add row

And in your javascript file:

$('.add-row').on('click', function(event){
    $(this).preventDefault();

    var rowCount = $('.js-providerCredentials').children('tr').length,
        newRow = '
Institution	<input type="text" class="name-field" name="fields[providerCredentials][' + (rowCount+1) + '][col1]" value="">
';

    $('.js-providerCredentials').append(newRow);
})

Not sure how your table is set up exactly, but the principle should be the same. For another example of how to format the dom for form submission see the answer to this question,  editing a table field type from the front end.


admin pages loading but not site itself, strange error I installed Craft CMS and all seemed great! But the pages are not loading.. So bascially /admin loads but /index.php genrates an error.

In dutch: "Interne Serverfout Craft\EntryModel en zijn behaviors hebben geen method of closure met naam "heading"."

Which translated is something like: "Craft\EntryModel and its behaviours have no method or closure with the name heading"

Any idea what's wrong? Can be server-level..
I run it on a mac mini server, but mysql, php and apache (plus php-modules) are installed, and again, admin works nicely..
best,
Bart

Apparently this is a bug in the latest release.  Oops!

The default homepage template on a fresh install is referencing an entry.heading that no longer exists.

Fixed it for the next release, but in the meantime, you can change line 23 of craft/templates/index.html from:

{{ entry.heading }}

to:

{{ entry.title }}


How to redirect if entry has children? How can i redirect to the first child if an entry has children?

Something like this:

{% if entry.hasDescendants() %}
    {% redirect "{{ entry.getChildren().first().url }}" %}
{% endif %}

You have twig syntax ({{...}}) inside twig syntax ({%...%}), which is never right. Does this work?

{% if entry.hasDescendants() %}
  {% redirect entry.getChildren().first().url %}
{% endif %}


Two domains with two different templates I'm trying to build a website for a client that has actually two websites for different locations and we need to merge both of them in a new one.

The only thing is that he wants to keep the domains separate, which means that when you visit website1.com you will see templates for that website and the url address will display website1.com/.../ etc. And when you visit website2.com while you browse the site you will retain this url.

Is this possible with Craft? Because we will have one database and install but 2 domains with one parked over the other. Can this be done with the Multi-environment technique described here?

https://craftcms.com/docs/multi-environment-configs

Multi-environment-configs: don't think so.

Maybe a solution is to use json template files on website1.com that you read (without craft) on website2.com?


Guest Entries required fields fatal error I've built a comment system using Guest Entries. If I set fields to be required and don't fill them in in the front-end, I get the following error.

Fatal error: Class 'Craft\GuestEntriesEvent' not found in ****/craft/plugins/guestentries/controllers/GuestEntriesController.php on line 136

New entries are set to disabled by default, I have tried with all fields set to required, and with only certain fields set to required.

This has been fixed in Guest Entries 1.5.1.


Entry Count plugin performance / template optimisation I'm using Ben Croker's Entry Count plugin in my News channel but have noticed a huge performance hit, I know its probably down to how I am loading the entries in to my template so I am looking for some help on that please.

The News Channel where it is being used has tens of thousands of entries.

The server logs highlighted this query https://pastebin.com/02uF0Mat taking up to 3 minutes to complete, therefore maxing the CPU and crashing the site.

My template code:

{% cache globally using key "trending-news" %}
    {% set countedEntries = craft.entryCount.entries.section('news').limit(4) %}

    {% for entry in countedEntries %}

{{ entry.postDate.format('jS F Y') }}
{{ entry.title }}

    {% endfor %}
{% endcache %}

So I think this code is loading thousands of news entries and then displaying only 4, is there a way I can just load the top 4 only?

Massive thanks in advance!

This is untested since I don't have time to load the plugin, so please comment if there is a syntax error but changing this

// get all records from DB ordered by count descending
$entryCountRecords = EntryCountRecord::model()->findAll(array(
    'order'=>'count desc'
));

To something like the following should work

public function getEntries($limit = null)
{
    $where = [
        'order' => 'count desc'
    ];
    if($limit !== null){
        $where['limit'] = $limit;
    }
    // get all records from DB ordered by count descending
    $entryCountRecords = EntryCountRecord::model()->findAll($where);
   ....

And in your template

{% set countedEntries = craft.entryCount.entries(4).section('news').limit(4) %}

Explanation: you include a limit to the query that fetches all entries to load the same amount like the ElementCriteriaModel

Edit: Maybe you have to change this to

public function getEntries($limit = null)
{
    return craft()->entryCount->getEntries($limit);
}


Is there a way to print the tax and discount percentages? Is there a way to get the tax and discount percentage when applicable?

See my answer here for discount percentages:
How can I display the Discount Value onSale of Products

For tax, I think there answer is actually currently the same, I don't think you can currently retrieve tax rates in templates as such (you can get tax categories models, but not, it seems, the rate models.  So again, twig maths might be your best bet here.


Return the contents of a template from a custom Twig function in a module? I have a template that outputs some JSON formatted data.

I've built a simple module that needs to get the contents of that template, decode the JSON data, and return it.

I believe I need to use craft()->templates->render('path/to/template'), but when I do, I get:

Call to undefined function modules\importjsonmodule\twigextensions\craft()

The only explanation I can think of is that craft() cannot be used in a module in the way it can in a plugin. Is that the case? If so, is there any way I can access the Templates service from within a module?

Thanks

There is no craft() function in Craft 3 and there are no Modules in Craft 2.
You probably mix the two different versions.
Craft 3
Craft::$app->getView()->renderTemplate('/path/to/template');

Of course you need to be in the correct namespace. You have to either use Craft or do \Craft
Craft 2
craft()->templates->render('path/to/template')


Non-admins not authorized to create entry via frontend I'm currently working on a page that should allow registered users to upload an image and save it as an entry in a section called "bildupload", together with some additional fields.

Creating/saving the entry works just fine as long as I am logged in as an administrator, however, when logging in as a regular user, Craft throws an error telling me that I am not authorized and lack rights to access the page.

Above image shows what permissions the default-user currently has.

I tried adding some permissions, however I does not make a difference, the error still appears on submitting the form to upload/create an entry.

Any help?

Craft won't allow any anonymous entry creation at all.

You can use something like the Guest Entries plugin that simulates an "anonymous" entry because you select a user account ahead of time to assign all anonymously created entries to.


How do you determine if the current field is required from a fieldtype class? I need to run validation in a custom fieldtype but only if a field is required.

How do you determine if the current field is required from within a fieldtype class?

In Craft 1/2, there isn't a particularly clean or easy way to do this because $this->model->required is only set in the context of when it's being called from a field layout.

In the context of a field type, Craft doesn't set it because required is a concept that belongs to a field layout and a given field can belong to multiple field layouts.

This whole process has been refactored and is much cleaner in Craft 3 so that $this->model->required works in the context of a field type where you'd expect it to.


Cannot remove index.php from URLs either I am having the same issue as here: Another index.php?p= problem

I tried to post this as a comment rather than open another post but I don't have enough reputation...

I have tried all of the suggestions listed and none of them are working. Without index.php I get 404's.

I've tried with the rule applied to the config file it breaks the admin (doesn't load asset files like css - they're still pointing to URLs with index.php) and I still get 404's on the entry URLs.

I also double checked with the hosting company on the apache configurations and the AllowOverride All is indeed set.

Here is my htaccess file:

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

I did try to add RewriteBase / just below RewriteEngine On and nothing happened. I also verified the file is actually working by adding invalid stuff in it and getting a server error.

I've tried this in my general.php config file:

return array(
    'omitScriptNameInUrls' => true,
);

And this version:

return array(
    '*' => array (
        'omitScriptNameInUrls' => true
    )
);

When I do, it doesn't fix the index.php issue, but it removes index.php from the admin, but breaks the admin because the assets 404 (mentioned above).

Any other suggestions out there?

Thanks!

In this case, the Apache mod_rewrite module wasn't installed, so the <IfModule mod_rewrite.c> block of Craft's default .htaccess file was being skipped not allowing index.php to be rewritten in the URL.


One-Page Commerce Checkout Does anyone have any recommendation on how to reformat the Commerce checkout workflow to be on one page? More specifically, how to pass the address/customer info before the payment info without having to redirect to another page? I've tried combining them into one form without success.

Thanks!

You won't be able to collect all information and take payment using one form/action. The customer information is added to the cart using commerce/cart/updateCart and payment is processed using commerce/payments/pay.

To show the available shipping methods to a customer, Craft Commerce needs to know the shipping address to match against the rules you've set up. For this reason you'll need to split your checkout into separate forms.

I have built a "one-page" checkout with 'Customer Details', 'Delivery Method' and 'Payment' sections. Each section has a 'Continue' button that submits the information and updates the cart. Each section has the same redirection URL (/checkout) so the user lands back on the "one-page".

If you wanted to do away with 'Continue' buttons, you could use Javascript to submit the customer's information and receive a JSON response. This would include the available shipping methods and the cart totals which you'd use to update the page without the need for a refresh.


How do I change the URL used to access Craft? By default you can access Craft on a site by visiting /admin. How can I change the URL for this to something else?

For example: /new-url

The cpTrigger config setting is what you're looking for.


Can I create a number with decimal point in number field? Could number for example 7.5 be created in number field?

As per the docs, there is a setting where you can specify how many decimal places are allowed. So yes, 7.5 can be represented in a number field.


Detect if it is a new user or not in PHP I have been developing some plugin code for my registration form in Craft 3.

The event will be EVENT_AFTER_SAVE_ELEMENT, but I do not know how to detect if it is a new user or not.
In Craft 2, the way was if ($event->params['isNewUser']).

It would be grateful for me if someone tell me how to do it.

In Craft 3 you would do:

Event::on(
    Elements::class,
    Elements::EVENT_AFTER_SAVE_ELEMENT,
    function(Event $event) {
        if ($event->element instanceof \craft\elements\User && $event->isNew) {
            // It's a new user
        }
    }
);


Hundreds or thousands of posts in a structured channel? I'm looking to create a very simple website where a user can add items to their own personal feed over time. So by default it's date ordered, but you maintain the ability to sort the order a little bit if you want. So one day I might add a photo, the next day I add a video, the next day I add some text. All these items build up over time and it just gets bigger and bigger. Over the years you might end up with hundreds or thousands of items that you've added.

Matrix seems perfectly suited to something like this – where each item is a new block. Except for the fact that all of the items would need to live in a single entry. And I just can't see scaling that very big if everything is in one entry.

So the alternative would be to have each item live as a new post in a channel. This seems like it would make more sense, especially as you get into the hundreds or thousands of items. But then how would I maintain the ability to sort the entries? If I made the channel structured, would that be adequate? And would there not be any issues with having it scale to hundred or thousands of posts in a structured channel?

Basically I'm just trying to lay a firm foundation where I could theoretically have hundreds/thousands of users each with hundreds/thousands of posts.

Also, this is basically a vision for a web app that would have a front end interface on it. Is something like this pushing Craft past it's abilities for what it was designed for?

We've seen Craft installs running with several hundred thousands of users / assets / entries, etc.

But when you're getting into that range, there are several other things to take into account such as hardware resources and architectural designs, various caching methods (Varnish, memcached, Craft's template caching), etc.

Having said that, it makes more sense to me to have them as entries and if you need the ability to have them sortable, then a structured section is the ways to go.

But there is a known issue with the UI for structured sections in the control panel where it will bog down when you get into the thousands of entries range.  We're addressing that for the upcoming Craft 2.2 release, so it shouldn't be a problem for long.


Upload failed for filename. The path doesn't exist I have an issue with uploading files. I got an error message:

  Upload failed for filename. Path directory structure
  "/public_html/assets/images /" set for this resource doesn't exist.

The Path should be good, because the public_html folder is directly in the root and the /assets/images folder is in it, permissions are 777.

It worked well on my local, it stopped working when I moved the site live. All pictures uploaded on my local are displaying on the live site...I just can't upload new

I suspect your public root is something else/more than just /public_html. Try adding the following to your index.php file, and refresh your site:

die($_SERVER['DOCUMENT_ROOT']);

Your real public root path should now display in your browser.


View all discounts applied to a cart and remove discounts from cart On my cart page I have the coupon code form to apply discounts to the order. Underneath the form I want to list all the coupons applied to the cart with an button to remove each discount.

As in the example template I can access the latest coupon applied to the cart, but am no sure how to list all that have been applied to the cart and how to remove a coupon if a customer wanted to.

Thank you.

You've sort of answered your own question - Commerce only allows one coupon on a cart at any time (currently).

To determine if a coupon is set, and print the code, it's basically:

{% if cart.couponCode %}
     {{ cart.couponCode }}
{% endif %}

You CAN have discounts applied as well as coupons, though.
With those, you can look through with something like this (removing the coupons if you've previously printed that)

{% for adjustment in cart.adjustments if adjustment.type == "Discount" %}
    {% set isCoupon = adjustment.optionsJson.code ? true : false %}
        {{ adjustment.description }}
        ...etc


Selecting a random entry based on conditional I'm trying to select 1 random entry out of a section based on whether a field has content (schedulerBio). I can get all the entries based on the conditional but can't seem to randomize a single selection. Any help would be appreciated.

{% for entry in craft.entries.section('team').limit(1).find() if entry.schedulerBio|length %}
... stuff
{% endfor }

I Think this should work:

{% for entry in craft.entries.section('team').limit(1).order('RAND()').schedulerBio(':notempty:') %}
   ...stuff
{% endfor %}


Craft Commerce 2, turning on debug bar and getting error Craft Commerce 2, turning on debug bar and getting an error.
"Invalid Configuration, Payment currency not allowed."

Could anyone shed any light of this error please?

Delete all active carts in the control panel and the errors will go away.

You changed the base currency and some exisiting carts are referencing a non exisiting currency. This should only be happening in development as you wouldn’t change the base currency for an existing site.


Permanently connecting templates to entries I have site with a few hundred entries in a structure section. About 95% of these pages use the same template, with the remainder (about 20 pages) needing a special template. I manage this the following way:

The section (main) has a template called _entry.html which consists only of the following:

{% extends [entry.uri, "main/_main"] %}

I then match the uri in the templates folder and that template also extends main/_main with a block override to deal with the special case.

All of this works very well until the content editor moves the special entry in the structure, and then of course it loses its template and goes back to the default.

Is there way I can "lock" an entry to a specific template (which still inherits) so that when the entry is moved in the structure it does not "disconnect".

I guess I'd refactor this and introduce different entry types for your section. Besides being able to "lock" each entry type to a specific template (→ "How can I give each Entry Type its own template?"), you can also have the CP fields (slightly) different. Maybe it makes sense to have extra fields for your special entries, if it doesn't, just add the same fields to each entry type.

Another option would be to have a dropdown field to select the template, but I like that the entry type dropdown is in the right panel in the CP.


How to install Craft on live server parallel to existing CMS? I'm in the process of migrating a large, complex site to Craft.

I've reached the stage where I like to install Craft in parallel to my existing CMS with the intention of allowing users to help me check the content is migrated correctly.

It's also with a view to keep parts of the existing site in the current CMS and migrate them later.

Do you have advice on how to best do this? How would I redirect requests for different parts of the site to the correct CMS?

Is the existing CMS live?
Often depends on your whole development process.
Quick thoughts would be to use a subdomain of live site.,e eg 'staging.mysite.com'

For managing redirects the answer is it depends.
Typically htaccess 301 redirects
Redirect 301 /my old url  https://www.mynewurl.com/my old url

You can replace the latter domain name with subdomain name eg as above 'staging' and then switch to real domain when live.

Depending upon Craft version you could use Retour for Craft2 or check the plugin directory for redirect management solutions.

Or if the original url structure is solid then you can always make craft match in section  settings :)


Craft Commerce - explain transaction types and statuses to me Commerce transactions have multiple possible types and statuses:
    const TYPE_AUTHORIZE = 'authorize';
    const TYPE_CAPTURE = 'capture';
    const TYPE_PURCHASE = 'purchase';
    const TYPE_REFUND = 'refund';

    const STATUS_PENDING = 'pending';
    const STATUS_REDIRECT = 'redirect';
    const STATUS_PROCESSING = 'processing';
    const STATUS_SUCCESS = 'success';
    const STATUS_FAILED = 'failed';

Can someone explain to me what each of them means?

A transaction can have a single type and status at one time.
A transaction can also have a parent transaction relationship.
There are 4 transaction types:
authorize, capture, purchase, refund.
A transaction type determines what the transactions is and how it is counted when determining the order totals.
authorize is used when the customer is not immediately charged to their card, but an authorization is placed on their card. The authorization will be captured at a later time, and the customers card will then be charged. An authorization can have the status of pending, redirect, processing, success, or failed.
capture transactions are always child transactions of authorize transactions. You can only capture a authorize transaction that had a success status. A capture can have the status of success, or failed.
purchase is used when the customer is getting charged immediately to their card. An purchase can have the status of pending, redirect, processing, success, or failed.
refund is used when the customer was refunded an amount. It can only be the child of a capture or purchase transactions that have the status of success
There are 5 transaction statuses:
pending, redirect, processing, success, failed.
pending status is for when a transaction is first created. It could then be changed to different status once the gateway/customer determines what the transaction should be. For example, changed to the redirect status when the customer is being redirected off-site.
redirect status us when the customer was sent off-site. A redirect status should only be used for authorized and capture transactions as capture and refunds are usually done by an admin from the control panel and not taken off-site to perform those actions.
processing is a semi-successful status introduced due to asynchronous nature of some gateways where knowing if the transaction is successful or failed will happen at a later time. For example, in stripe the customer might be returned from a SCA offsite back to the craft site and we will update the transaction as processing before the webhook comes in with the results of the payment from the bank. When we get a processing status on an authorize or purchase transaction we still
'complete' the cart and turn it into an order. So the only way for a customer to complete a cart is to have a successful purchase or authorization or a successful processing transaction.
success status means the transaction was successful. This covers the authorization, capture, purchase and refund.
failed status means the transaction failed. This covers authorization, capture, purchase and refund.
We will update the docs with the above.


What value does actionInput provide? What is the value in using a hidden actionInput vs an action attribute on a form?
<form method="post">
    {{ actionInput('path/to/controller-action') }}
</form>

vs
<form method="post" action="/actions/path/to/controller-action">
</form>

I understand you get the actionTrigger automatically added, but you could use the actionUrl function to generate that as well.

Just like a lot of things in Craft or in computer science in general, there's multiple ways to do things.
The actionInput function is just a shortcut for outputting a hidden input:
<input type="hidden" name="whatever" value="whatever">

...much like {{ entry.title }} in Twig is really a shortcut in PHP for something like <?php echo twig_escape_filter(...) ?>.
However, the 2 snippets you posted are not necessarily equivalent.
When you put the "action" attribute on a form:
<form method="post" action="whatever">

... a web browser is actually sending a POST request to that location, versus when you leave it off:
<form method="post">

... a browser will POST to the current page.
The difference seems subtle but say your form has errors, you probably don't want to be POSTing to your controller "directly", you want to reload the same page. (And you don't typically don't want someone bookmarking your controller action.)
The older Craft 2.X documentation has a great little bit about this:

When you leave your form’s ‘action’ attribute blank, browsers will default to the current request’s URL. Which is great in the event that your controller needs to reload the previous page without a redirect. A common example of this is when the user’s input didn’t validate, and you want to pass the errors back to your template (ideally tucked away within a model).

Craft/Yii handles the nitty-gritty routing behind the scenes.


Authorize and charge later with Stripe I know Commerce allows you to just authorize and pay later but the 'pay later' scenario requires re-entering the credit card details. We have a flow that requires the admin to be able to charge a user later without having the credit card details. As such, we would use Stripe's API to create a customer to save the token for future use, as follows:

User places order, providing credit card info to Stripe.
Authorize with Stripe and create a customer in Stripe so as to save the token for later use (ie. do not charge the card right away).
Allow the admin to go into the Craft CP at a later point and charge the customer's card (by making use of the token saved at Stripe). Do this via a status change perhaps?

We're a little lost conceptually how to proceed with this with Craft Commerce. Do we need to write our own custom gateway to do this?

We considered using the built-in Stripe gateway and setting it to "Credit Card Payment Type: Authorize Only" but that means we get the "Make Payment" button on the Order Detail screen and it also isn't doing anything to create the customer on the Stripe end with a saved token.

Late answer but this is currently possible (Aug 2018).  Make sure you do have your Stripe payment method set to 'Authorize Only (Manually Capture)'.

Go to your order, and you'll find a Capture button in the Transactions box at the bottom.  Just hit that button anytime to charge the card.


Translated entry page can not be found (404) I have installed and built a website using Craft CMS (with latest updates). I'm running this Craft installation on a Homestead virtual machine.

When I enabled localization and translated entries in a structure section, the url opens a 404 page.

In my setup I have two locales nb_no and en. Both locales are enabled for the section artists. Both locales are using the same url structure for entries: artists\{slug}.

My configuration for siteUrl is as follows:

'siteUrl' => array(
    'en' => 'http://page.craft.dev/en/',
    'nb_no' => 'http://page.craft.dev',
),

Debbuging the problem I see that there is an error in TemplatesController.php. In the function actionRender($template, array $variables = array()) the value of $template is en/artists/andre-bratten instead of artists/_entry as is the case for the main language entry.

Further exploration of logs shows me that Craft executes the following query which returns no result:

[09:30:32.080][trace][system.db.CDbCommand]
Querying SQL: SELECT `elements`.`id`, `elements`.`type`
FROM `craft_elements` `elements`
JOIN `craft_elements_i18n` `elements_i18n` ON elements_i18n.elementId = elements.id
WHERE (elements_i18n.uri = :uri) AND (elements_i18n.locale = :locale) AND (elements_i18n.enabled = 1) AND (elements.enabled = 1) AND (elements.archived = 0). Bound with :uri='en/artists/andre-bratten', :locale='nb_no'
in /home/vagrant/web/ekko/craft/app/services/ElementsService.php (151)
in /home/vagrant/web/ekko/craft/app/etc/web/UrlManager.php (281)
in /home/vagrant/web/ekko/craft/app/etc/web/UrlManager.php (140)

In my database elements_i18n table the entries both have uri = "artists/andre-bratten" and different locales.

As well there is a problem with wrong locale in the sql query (nb_no instead of en). I set CRAFT_LOCALE to en in the public\en\index.php file.

As I'm rereading this I think that there could be a problem with resolving the correct path (i.e. removing en/ from the front) when the locale is set incorrectly. Does anyone have any ideas why this is happening?

Update

I found out that when I access http:\\page.craft.dev\en\ I am loading public\en\index.php. However when I access http:\\page.craft.dev\en\artists\{slug} it loads public\index.php. So I assume there is a problem with Homestead and nginx?

The issue appeared to be the nginx config. I updated it according to an example from here (Locales sub directories on Nginx) and it started working.


Best practice on how to hand over variables with links? You sometime want to pass specific info from one page to another.

How do you do that with twig and are there explicit Craft methods to do this?
Can you do this without exposing the variable in the URL?

You're options are:

Adding it as a parameter in the querystring (e.g. domain.com?key1=value1)
Submitting it though a form and grabbing the value from POST.
Setting the value in a cookie and retrieving it on the next page using something like this plugin.
Writing a plugin with a Twig function that wraps Craft's craft()->userSession->setFlash() method, which will store a key->value pair in session for exactly one request.  You can retrieve the value via craft.userSession.getFlash().

Note that there are limitations to the size and complexity of the data you can pass along with all of these, but especially options 3 and 4, so it's best to stick with simple strings.


Optimize queries with list of image thumbnails I have a list of entries with a thumbnail (Image Asset) added to the entry. Within my entries loop I check if there's any image (thumbnail) added for the entry.

{% set thumbnail = entry.thumbnail.first() %}

Since each check requires one new query the queries keep adding up if the list has for example 100 entries. That's 100 queries just for displaying one thumbnail next to my entry.

Is there a better way of doing this? Could I for example get the URL to the image asset some other way then running a query on the AssetModel?

I think that's how it works and there's no way around this. That asset is related to your entry and to get the asset's URL property, Craft has to do a query to the database.

That means: each page with lots of thumbnails should be cached. :(


User activation email markdown not (correctly) parsed, no link etc Our User registration is not working correct anymore.

This is how the Email content part looks after registering a new public user:

Hallo craft_user.friendlyName,
danke für das Einrichten eines Benutzerkontos auf craft_siteName. Um
Ihr Konto zu aktivieren, klicken Sie bitte auf den folgenden Link:

craft_link

Wenn Sie diese Email nicht erwartet haben, ignorieren Sie sie
bitte einfach.

The markers are not parsed at all. The same result occurs, if I send a second activation mail via the CP.
It does already work. We started with Craft 2.3 and did all updates to 2.6. I have no idea when and why this happens.

This is the part of the (original) /app/translations/de.php:

'account_activation_body' => 'Hallo {{user.friendlyName}},

danke für das Einrichten eines Benutzerkontos auf {{siteName}}. Um
Ihr Konto zu aktivieren, klicken Sie bitte auf den folgenden Link:

{{link}}

Wenn Sie diese E-Mail nicht erwartet haben, ignorieren Sie sie
bitte einfach.',

Any suggestions?

Ok, i found the Email "Message" settings where the wrong markers where ("craft_link" instead of "link" etc.) located.


How to show homepage for 404 errors Instead of a standard 404 error page, I want to send 404s to the homepage and can't seem to get this working right.

If I add {% redirect '/' %} in my 404.html template, that works except it gives a 302 found code instead of a 404.

If I change it to {% redirect '/' 404 %} I get the correct 404 code but just a white screen instead of the homepage.

I've also tried adding ErrorDocument 404 / to the .htaccess file, but that doesn't take effect as I think the Craft 404 mechanism overrides that.

Can anyone help? Thanks.

404 isn’t a valid redirect status code, which is why {% redirect '/' 404 %} isn’t working correctly.

It sounds like you want to:

first issue a 301 or 302 redirect to the homepage
then serve the homepage, but with a 404 status

If I’m understanding you correctly, even if you could pull it off (maybe doable with a query string or something stored on the session), that would be bad for a couple reasons:

the actual not-found resource is returning the wrong status code (301/302 instead of 404)
now the homepage (a valid resource) is also returning the wrong status code (404 instead of 200)

The biggest issue is the 2nd one, as it would have devastating SEO consequences. Any time Google hits a missing resource and is redirected back to the homepage, it will start remembering that your homepage doesn’t exist.

A better solution would be to keep the URL pointed at the missing resource (so no redirect response), but to just render your homepage via the 404 template.

That should be as simple as creating a 404.html template with this:

{% include "index" %}

(or whatever your actual homepage template is called.)

If you have a Homepage Single section, you may need to pass that in as well:

{% include "index" with {
    entry: craft.entries.section('homepage').one()
} %}

(replace .one() with .first() if this is for Craft 2.)


Using pagination with Ajax load.. help need on JS I am using a template which has a 'LOAD MORE' button beneath a grid of entries (entries 1-10). Click this link loads addition content to the grid via AJAX from another page (which contains entries 11-20). There is also a second page which has entries 21-30. The code looks like this:

  Load More
  No more items to show

There is JS that will change the p2.html to p3.html when it is clicked, so the second time the button is click the 3rd set of entries is added.

I would like some help as to how to change the JS to make this work with craft pagination. I can add

{{ paginate.nextUrl ~ queryString }}

as the target URL which works for the first click (the link is rendering out as URL/section/p2 and the content is added via AJAX in the correct place) but I need this to change to /p3 etc with each click.

It seems as though the LOAD MORE function in the JS is mentioned in 2 places:

/* isotope load more function
------------------------------------------------*/
function isotopeLoadMore(grid,el,url,datas) {

  el.parent(".load-isotope").addClass('loading');

  if (url === '#' || !url) { url = srvars.ajaxurl }
  var addData = ''; if (datas) { addData = { action:'sr_load_more', o:datas }; }
  jQuery.ajax({
    type:'POST',      // this might lead to issues for html template
    url:url,
    data: addData,
    dataType:"html",
    error: function () {
      el.parent(".load-isotope").addClass("disabled");
    },
    success: function(response) {
      //console.log(response);
      if (response) {
        setTimeout(function(){
          var items = jQuery( jQuery(response).find('#'+grid.attr('id')).html());
          items.imagesLoaded(function(){
            grid.append( items ).isotope( 'appended', items);
            animateOnScroll(false);
            // init video bg for appended items
            if(jQuery().phatVideoBg) { grid.find('.videobg-section').phatVideoBg(); }
          });
          el.parent(".load-isotope").removeClass('loading');
        },500);
      } else {
        el.parent(".load-isotope").addClass("disabled");
      }
    }
  });

}

AND:

/* Load More isotope
------------------------------------------------*/
var loadMore = jQuery('.load-isotope:not(.disabled) a');

loadMore.click(function(){
  var el = jQuery(this);
  if(el.data("loadpage") === undefined) { el.data("loadpage","2"); }
  else { el.data("loadpage", parseInt(el.data("loadpage"),10)+1); }
  var   related = el.data('related-grid');
  var   href = el.attr('href').replace("/2", '/'+el.data("loadpage"));
  href = href.replace("2", el.data("loadpage"));
  var datas = '';
  if(el.data("options") !== undefined && el.data("options")) { datas = el.data('options').replace("paged=2", "paged="+el.data("loadpage")); }
  isotopeLoadMore(jQuery('#'+related),el,href,datas);
  return false;
});

Any help with this would be massively appreciated

Could you just append a new "load more" button to the page as part of the requested AJAX template and remove the old one when it's successfully loaded?

Each load more button might look something like this:

{% if paginate.nextUrl %}
  Load More
{% endif %}

Then in your isotopeLoadMore() function, you can remove the load more button that was clicked on if you get a successful response and replace it with a button from the AJAX response. Something like this:

/* isotope load more function
------------------------------------------------*/
function isotopeLoadMore(grid,el,url,datas) {

  // create a var for the button's parent so we can reuse it
  var loadIsotope = el.parent(".load-isotope")
  loadIsotope.addClass('loading');

  if (url === '#' || !url) { url = srvars.ajaxurl }
  var addData = ''; if (datas) { addData = { action:'sr_load_more', o:datas }; }
  jQuery.ajax({
    type:'POST',      // this might lead to issues for html template
    url:url,
    data: addData,
    dataType:"html",
    error: function () {
      loadIsotope.addClass("disabled");
    },
    success: function(response) {
      //console.log(response);
      if (response) {
        setTimeout(function(){
          var items = jQuery( jQuery(response).find('#'+grid.attr('id')).html());
          // Find the new Load More button in the response
          var loadMode = jQuery( jQuery(response).find('.load-isotope').html());
          items.imagesLoaded(function(){
            grid.append( items ).isotope( 'appended', items);
            // add the new Load More button
            loadIsotope.append( loadMode );
            // remove the original Load More button
            el.remove();
            animateOnScroll(false);
            // init video bg for appended items
            if(jQuery().phatVideoBg) { grid.find('.videobg-section').phatVideoBg(); }
          });
          loadIsotope.removeClass('loading');
        },500);
      } else {
        loadIsotope.addClass("disabled");
      }
    }
  });

}

But you'll have to attach your 'click' handler a little differently to make sure the dynamically loaded buttons will fire the same click function.

So instead of using...

var loadMore = jQuery('.load-isotope:not(.disabled) a');

loadMore.click(function(){ ...

That code will only attach the click function once when the page loads to the first button, but not any buttons that are dynamically added to the page thru your AJAX call.

Here's an example JSFiddle of a script that creates buttons and an alert. Only the original button works. New buttons don't do anything.

https://jsfiddle.net/alexroper/0aup8e5d/

But this JSFiddle uses delegated events like this...

$('.load-isotope').on('click', '.sr-button' ,function(event){ ...

... to attached click events to any $('.sr-button') element that's a child of $('.load-isotope'):

https://jsfiddle.net/alexroper/uq613627/1/

It's a similar problem to this Stack Overflow question:
Jquery event handler not working on dynamic content

So here's a stab at what that might look like in your code. I'm not totally sure what everything in your code does, so you still may have to tweak it.

$('.load-isotope').on('click', '.sr-button' ,function(event){
  event.preventDefault();
  var el = $(this);
  var related = $('#' + el.data('related-grid'));
  var href = el.attr('href')
  var datas = '';
  if ( ! el.parents('.load-isotope').hasClass('disabled') ) {
    isotopeLoadMore(related,el,href,datas);
  }
});


Search return wrong result with special character like æ ø å in Craft 3 I had created search in Craft 3, but it returns wrong result when I enter some special characters in it, for example æ ø å. Looking for the solution.

Not possible natively.  Craft runs each search keyword through a "normalization" process before saving in its search index and part of that is converting characters like å to their ASCII counterparts a.  You can see the whole process here:

https://github.com/craftcms/cms/blob/develop/src/helpers/Search.php#L41

If this were solely in the context of the searching on the front-end of the site, you could use/write a plugin that integrated with a 3rd party search service like Algolia that offered more flexible options, but that wouldn't affect Control Panel searching until this was implemented: https://github.com/craftcms/cms/issues/833


Migrated site - now dashboard keeps logging out I've migrated my client's Craft site to a new host. On the front end the site is fine and working as it should.

The problem is with the Craft admin area. I can login, but once logged I can’t access anything other than the main dashboard screen. Clicking any of the links within the dashboard brings up the login screen.

One thing I did try was to remove the cpTrigger I’d set in /craft/config/general.php. For a moment I thought this had worked. I could access other areas of the dashboard, but then the dashboard logged me out after a minute or so.

I’ve seen a few other posters with similar issues and have tried solutions that worked for them…

I’ve delete the contents of craft/storage/runtime.

I’ve checked the file permissions of craft/storage/runtime. They are currently set to 774.

I’ve tried adding the following to craft/config/general.php:

'overridePhpSessionLocation' => false
As described here - Craft constantly logging out from admin area

and also:

'overridePhpSessionLocation' => true

As described here - Migrated Server, Now Getting Logged Out Constantly

I’ve tried 'requireMatchingUserAgentForSession' => false too.

Nothing has worked so far.

Is there anything else I could try?

I understand this is likely to be related to permissions for where session files are stored. Are there any questions I should be asking the the hosting company in relation to this? The site is on a shared hosting account.

Any help or advice with this much appreciated.

* An update... *

I looked at /admin/utils/phpinfo in the dashboard to check where session.save_path is pointing.

By default it's set to /tmp.

If I add 'overridePhpSessionLocation' => true to craft/config/general.php, the session location is set to:

/var/www/vhtdocs/xxxxxxxx/craft/storage/runtime/sessions/
/tmp

That looks a bit odd to me? There are two paths – the craft path and the default session path below it. Is that correct?

Just a question; are you on a Nginx server?
I had a similar issue before with a client site. the Nginx configuration was removing the cookies when it cached the page, which redirected to the login page after logging in.


Get entry field data via a plugin. ( For specific image assets ) I've got an asset field on a channel entry called "thumbnail". I'm using it to return a singular image, specifically for an image transform called "thumb". Looks a little like this:

{{ entry.thumbnail.first().getUrl('thumb') }}

This works fine. But I'd like to get the same information/url using a plugin (so I can perform some colour validation and other checks via PHP).

Can anyone share some wisdom on how best to approach this please?

Assuming you already have the entry (see Working with Elements for info about how to do that), you can interact with it exactly the same way you do in your templates - just with PHP syntax instead of Twig.

$asset = $entry->thumbnail->first();

if ($asset)
{
    $url = $asset->getUrl('thumb');
}

Though the transform won’t necessarily be available yet, since getUrl() will not generate transforms that don’t already exist by default.

That behavior can be customized with the generateTransformsBeforePageLoad config setting though, and lucky for you, you can even override the actual config value from PHP before calling getUrl(), using ConfigService::set():

craft()->config->set('generateTransformsBeforePageLoad', true);
$url = $asset->getUrl('thumb');


Control Panel Session 60 second Timeout I’m on Craft v. 2.6.2983 and am encountering the well flagged issue of the control panel logging me out after 60 seconds. I've tried most of the solutions posted here and have had no success in resolving the issue.

We're three months into a Craft deploy this issue has only begun to arise in the last number of weeks.

I’m using redis for session storage and my sessions are remembered when I refresh or open a new tab. Using redis-cli monitor I can see sessions are being stored with the correct TTL.

I've set the following in my config/general.php:

'appId' => 'itmaapp123',
'defaultCookieDomain' => '.itma.ie',
'validationKey' => [REDACTED],
'overridePhpSessionLocation' => 'tcp://redis:6379',
'cacheMethod' => 'redis',
'userSessionDuration' => false

I've also set the relevant variables in config/rediscache.php (including timeout => 300).

Any insights on what might be causing the issue? My stack is Craft 2.6.2983 on nginx with php7.0-fpm and redis for session storage. nginx, php and redis are all running in docker containers behind an nginx reverse proxy. I'm using Cloudflare with SSL for cache and CDN.

Any help much appreciated.

I've had issues before where I was getting logged out and could not fathom why: it turned out (after weeks) that when I was using Inspector Tools' Responsive Design mode, Chrome sends different UA strings... which by default causes Craft to log the user out.

It's a long shot; but see if you're only experiencing the issue when you've got your inspector open and in mobile device simulation / responsive design modes.

The fix for me was to add the following to config/general.php

'requireMatchingUserAgentForSession' => false, // stop Craft logging you out of the CMS while using Chrome's "Device Mode" view for responsive development


How to delete old content in a Craft CMS database? I've create 20+ websites in Craft and each time I've always started with the use of another site's database. (time saving to not have to create all fields, sections and more)
On my latest site's database I see I have content from a very old website, build 2016.
What is the best way to delete old content from the database in a safe way?
craft_elements = 52mb
craft_content = 34mb
craft_element_sites = 34mb

"Personally I'd just use the 'select all' checkbox at the top of entries and delete them all at once."
That's what I've been doing - deleting all entries in the system. I've always thought that by deleting entries in the system it would clean the database.
But when looking in the database there is so much information that is still saved even though everything is deleted in the system.
Now I have a database that is over 300mb, with old content in Matrix columns, craft_elements and more.
UPDATE:
This is what I've done so far:

In craft_elements I deleted all rows with type LIKE "Entry".
Emptied all my matrix tables.

Because I cleaned all entries from the craft_elements I also removed the single section entries. By saving those sections again the entries where created.
The database have gone from 318mb to 16mb. The system seems to be working great now and I hope it will be in the future :)


Is it possible to change the amount of time a user stays logged in for? Our users are getting logged out after 1 day.

Is there a setting that changes how long users’ sessions will last?

Yes! The setting is called userSessionDuration, and you can set it in your general.php config file, on a per-environment basis.

Here's an explanation of the values that you can use for the setting—for posterity: integers (in seconds), a PHP duration expression, an initialized DateInterval object, or 0 for "indefinite."

The link above points to the Craft 3 documentation, but the same setting is available in Craft 2.


Twig Error Runtime When Using Conditionals I am trying to create a meta include to display all my meta data and I have the following set up so far:

3. {% if craft.request.getSegment(1) != "404" and craft.request.getSegment(1) != "databytes" %}
  4. <title>{% if entry.metaTitle is defined %}{{ entry.metaTitle }}{% else %}{{ entry.title }}{% endif %} | {{ siteName }}</title>
  5. {% if entry.metaDescription is defined %}
  6. <meta name="description" content="{{ entry.metaDescription }}">
  7. {% endif %}
8. {% endif %}

{# 404 =============== #}

{% if craft.request.getSegment(1)  == "404" %}
  <title>Page Missing | {{ siteName }}</title>
  <meta name="description" content="Sorry the page you are looking for is missing, it may have been moved or deleted, please use the navigation to search for similar content.">
{% endif %}

If I visit main pages the code works and if I go to example.com/404 the code works, but if I visit an invalid url example.com/foo then I get a Twig Error Runtime error:

Craft doesn't redirect to 404 if a page isn't found, it just loads the 404 template and keeps the invalid uri.

What currently happens:

example.com/invalid_uri
Craft doesn't find it and shows the 404 template
Your conditional checks if 404 is in the uri, but it isn't.
Your code now tries to access the entry variable which isn't defined.
This results in a Twig error.

.

Solution:

Add an entry is defined to your conditional:

{% if craft.request.getSegment(1) != "404" and craft.request.getSegment(1) != "databytes" and entry is defined %}
....
{% endif %}


Image processing in headless mode How to do image processing & transformations when not using templates?

I'm using react-static for the frontend.

Is it possible to have CraftCMS process the images upon upload?

Check out the Imager Pre-transform plugin, or I can recommend a service like https://www.imgix.com where you can perform transforms using url parameters.