Getting "Invalid verification code." on password reset I've had a few users on our site have trouble resetting their password. They go through our "Forget password" flow on the front-end of our site, receive the reset email, which allows them to create a new password. But when they try sign in, they receive the following error:

Based on this thread, I thought the issue might have been our email parsing… but here's the weird thing… same thing happens when I grab the reset link directly from the control panel using this dialog then pasting it directly in a different (signed-out) browser:

I'm getting the exact same error every time I try it with either method (this was definitely working a few months ago on the same server, PHP version, etc).  Any ideas? Thanks!

In case anyone makes the same silly mistake I did, I had a conditional on the sign-in page that redirected folks to the page they were on before signing in.

After a password reset, it was trying to redirect back to the Set Password screen with the reset token, resulting in the error.

I just adjusted the conditional so it ignored the referrer URL if it was a control panel action. Problem solved.


Restoring a database results in error I downloaded a database backup from the craft back-end. I've tried to restore it via PHPMyAdmin and MySQL workbench, and I get the following errors:

ERROR 1227 (42000) at line 18: Access denied; you need (at least one of) the SUPER, SYSTEM_VARIABLES_ADMIN or SESSION_VARIABLES_ADMIN privilege(s) for this operation

At line 18 in the SQL file:

SET @@SESSION.SQL_LOG_BIN= 0;

Is there any way I can get around this so I can restore this database? I'm desperate right now.

Here is the Solution. Clearing a couple of lines from the SQL file made it work.


Conditions in an if statement 'or' I'm trying to do something like this for all the SEO meta info in my head

{% if entry or category is defined %}
 ...
{% endif %}

What's the right way to code this?

The reason for this is I have archive pages which won't be using some the meta information.

You have to type out the complete statement for each condition, i.e.

{% if entry is defined or category is defined %}
    ...
{% endif %}


Sprout Import: Place entries under a parent in structure section? When using the Sprout Import plugin, how do you place an imported entry underneath a specific parent entry in a structure section?

Turns out you can set the "parentId" field under the "attributes" list in the import JSON data to set a structure parent (to the entry id of the parent entry). This probably means you need to chunk your import into separate steps, for example:

Import only the top-level entries.
Run a query on the CMS to retrieve entry id's for those just-imported top-level entries.
Using the list from step 2, assign the proper entry id to the 2nd-level children and import those.
Run a query to get the 2nd-level entries you just imported.
etc. etc. for each level of your hierarchy

Would be awesome if you could just nest the JSON structures and the import plugin magically figured out how to set parent ids, but that sounds like a lot of work to implement for a free plugin :)


How to reference a user related to entry input in if statement I am having trouble referencing a user in an if statement:

{% set otherEntry = craft.entries().section('other').userFieldInOtherEntry("{externalVariable}").one() %}

{% if currentUser is relatedTo(otherEntry) %}
    do something
{% endif %}

I've tried using the for loop method described in the Craft 3 documentation, but I get a recursive loop error.

You could do:

{% set otherEntry = craft.entries().section('other').userFieldInOtherEntry("{externalVariable}").one() %}

{% if currentUser in otherEntry.userFieldInOtherEntry %}
    do something
{% endif %}


Get a specific entry by its index or number? I'm working with a carousel that has two rows. Each item in the carousel displays an icon for a "media partner". This works all fine and dandy until there's an odd number of media partners, because then there is an extra unnecessary space in the carousel. I have the issue solved logically, but I can't figure out how to implement it into Craft. I need a way to access a certain entry plainly by its number, i.e. the fifth entry, seventh entry, ninth entry, etc.

There are 11 entries that I am working with, however that number may change in the future. So, first, I grab how many current entries there are in the particular field that I am looking for:

{% set entryNum = craft.entries.section('partners').featuredMediaPartner('1') | length %}

This returns 11.

Next, this number gets divided by two, because I want to display the middle partner in the empty space that the carousel creates if there's an odd number (I already have coded to determine what to do if there's an odd number). So 11/2 = 5. How would I access the fifth entry? Thanks.

I don't know whether you've seen the batch Twig filter? You may find if useful to accomplish exactly what you are trying to do, but with a little bit less hassle.

From the example in the link above:

{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}

{% for row in items|batch(3, 'No item') %}

        {% for column in row %}
            {{ column }}
        {% endfor %}

{% endfor %}

And here's what it outputs:

        a
        b
        c

        d
        e
        f

        g
        No item
        No item

Essentially you're grouping your values into rows of a specific number (in this example it's 5). Then you can specify what to put in any empty spaces. So here it's the words 'No Item', but in your case it could be the middle partner entry.


Is it safe to make the 'app' folder writeable by the web server? To allow one-click updates on my site, I need to make the Craft 'app' folder writeable by the web server.
Doesn't this present a security concern...to allow the actual php files of the application to be writeable by the web server?

Security is not off or on, it's layers of an onion.  Anything you do has to factor into the bigger picture.

My layers include:

What user does the web server run as? What user is your content?
Your web server should never be the same as the user who owns the files, typically www-data, www, apache, etc. Also, don't run apache as the user nobody.
Are you using PHP-FPM, su-PHP, fastcgi, or Apache w/ mod_PHP baked in? I prefer fpm since you can run every website as a separate pool and separate PHP ini / open_basedir configs, increasing security. If a site gets hacked, only that pool is affected.
What are the permissions of the folders/files? Shouldn't ever be 777, ideally 750 for directories and 640 for files. The web server should be added to the user's group and given read permissions (so it can only access static files).
Who else has access to the server? Deploy users via SSH, (s)FTP, MySQL replication users, etc. Limit access to need-to-know.

Just to play devil's advocate, if you do disable the one-click updates in production, that also makes it harder to simply update, which could be worse than not updating at all. I've seen well-meaning WP Devs tend to fall into this trap, keeping their client's sites in a sort-of cryogenic state. While Craft is not as widely used, the same security precautions apply.

I'm reminded of Brad's post about security here:

  Perhaps the biggest thing Craft has going for it, in the event a security related bug is that we can get the fix deployed quickly to our users with Craft's one-click auto-updating. In addition to that, we can mark a release as "critical", which gets special attention in the control panel.

Craft probably needs to eventually write files to the server anyway, including cache files, and assets, so unless you're shutting off access there as well, I'd wager it's the same risk—maybe even more so—because asset files/uploads tend to be in the web root unless you do some file aliasing.

And without question, if you're not backing up your database and filesystem regularly, you're asking for trouble. Let's assume you are hacked. If you don't have good back-ups, whether auto-update is on or off is not going to help you.


Need help with a Twig template error I am getting the following Twig error and I cannot figure out what is wrong. I'm new to Twig.

Twig Syntax Error – Twig\Error\SyntaxError

Unexpected "minify" tag (expecting closing tag for the "cache" tag defined near line 5).

in /var/www/craft/templates/index.twig at line 5

{% extends "_layouts/_layout.twig" %}

{% import "_partials/_macros.twig" as macros %}

{% block content %}

{% cache using key "home-page" %}{%- minify -%}

{% set banner = entry.homeBanner.one() %}

{{banner.heading}}

      {% set bannerBtn = banner.button.one() %}
``

It looks like you're missing a closing {% endcache %} tag towards the end of your template.

(See https://docs.craftcms.com/v3/dev/tags/cache.html)

You might also be missing a closing {% endminify %} tag too, but I'm not sure if you've posted the whole of your template or not.


Module: EVENT_AFTER_SAVE_ELEMENT won't detect changed user group Event::on(\craft\services\Elements::class, \craft\services\Elements::EVENT_AFTER_SAVE_ELEMENT, function(Event $event) {
    if ($event->element instanceof \craft\elements\User) {
        $user = Craft::$app->getUsers()->getUserById($event->element->id);
        if ($user->isInGroup('abc')) {
            // Do something
        } else {
            // Do something different
        }
    }
});

I created a C3 module on pluginfactory.io. The code above works just fine in detecting when a user is saved/updated.

For sake of simplicity I have two user groups 1) abc 2) def.

When I uncheck the user from def and check him into abc (or the other way around), and save the user, my code won't register that the user group has changed, but thinks the user is still in the previously selected group. If I re-save the user (without changing groups), suddenly the code sees the correct group.

I assume that despite using EVENT_AFTER_SAVE_ELEMENT, the system still saves the user group change to the db after the Event triggers. Could that be it?

It's weird though, because any other field I pull from the user, e.g. if I change their email, my code is able to pick up the new email on the first save, so why not the change in user group?

Any assistance is greatly appreciated,
cheers!

Edit: Made problem clearer to understand.

That's because groups are not attached to the user directly. At the moment Craft stores the user it hasn't handled the permissions not the user groups.

The workflow is

Handling all kinds of variables and data
Fetch/Create a user
Populate the new field values for the user
Store the user
Handle verifications such as sending them an email
Assign the user to groups and grant them permissions

At the moment your event triggers the user wasn't assigned to the groups.

If you want to change users depending on their groups you should use the  Users::EVENT_AFTER_ASSIGN_USER_TO_GROUPS event


Pass Craft Instance to A Vendor PHP File I created my own class file that I include in one of my Craft Services files. Is there any way I can access the craft instance inside of this file?

For example, inside my Craft service I create a new instance of my class like:

$wally = new Wally();

Inside of the actual Wally class file, I'd like to be able to create a variable in my constructor that gives me access to stuff like this:

craft()->elements->getCriteria(ElementType::Entry);

For further clarification:

class Wally {
   function __construct($craftInstance) {
      $this->craft = $craftInstance;
   }
}

If it sounds like I don't know what I'm doing, I probably don't. Any help would be appreciated. Using Craft 2.6 btw.

You don't need to pass anything in there.

The function craft() is just a public function that returns the static app. It does not matter if you call it inside a plugin or somewhere else.

namespace Craft; //<----- added namespace
class Wally {
   function __construct() {
      $criteria = craft()->elements->getCriteria(ElementType::Entry);
   }
}

If you don't want to be in the same namespace you can do

class Wally {
   function __construct() {
      $criteria = \Craft\craft()->elements->getCriteria(\Craft\ElementType::Entry);
   }
}

If you really want to save the app in an extra variable for whatever reasons you would do

new Wally(craft());


Set Craft variables in the markup I am trying to use Twig methods/loops while working on a design/template. To avoid touching the CP or the DB i did set variables directly in the template, like :

{% set service =
  {
    'titel':
      [
        'Titel1',
        'Titel2'
      ],
  }
%}

{% for i in 0..1 %}

{{ service.headerQuote }}

{% endfor %}

It would be much better to be able to use the standard Craft methods, so if I move from templating/designing towards the DB i wouldn't have to touch the methods in my markup.  Unfortunately, I can't make it work... I tried:

{% set service = { titel:
                   [
                     'hallo',
                     'hallo2'
                    ]
                  }
                %}

{% for entry in craft.entries
      .section('service') %}

{{ entry.titel }}

{% endfor %}

I tried to set the service entry to craft.entries.section('service') but that doesn't work as well... what am I missing?

Is it even possible to overwrite Craft variables in the markup?

Just use a variable that has the same keys as the entry model will have.

{% set entries = [{title: 'hello', ...}, {title: 'hello2', ...}] %}

{% for entry in entries %}
    {# stuff #}
{% endfor %}

Then, when you want to move to using the database, just switch what  entries is set to:

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

That way the template will still look exactly the same.


Assets and S3 - Reduced Redundacy Storage is it possible for S3 bucket assets to use reduced redundancy storage?

I was under impression that it was a bucket setting, but after doing some reading, it seems that this is a per-object setting and should be performed while uploading to a bucket. I'll add this to the list and you can expect to see this in Craft 2.2!


Dynamic routing via plugin with special characters After learning how to use dynamic routing in MyPluginPlugin.php, I quickly ran into the issue of if the URL has special characters from url_encode:

public function registerSiteRoutes()
{
return array(
    'myroute/(?P<myVariable>[-\w]+)' => array('action' => 'myPlugin/myAction')
);
}

And then I process some variables before rendering the template in myPlugin/myAction:

 public function actionLoadTemplate( array $variables = array() )
 {
   /*
   process & build $variables
   */

    $this->renderTemplate('layouts/forums/_entry', $variables);

 }

This works great if the route looks like myroute/check-this-out, however if the route had a special character in it, say it was "check-this-out!", it understandably leads to a 404. But with using url_encode on the URL so it looks like myroute/check-this-out%21, it still leads to a 404.

When testing manually in the CP route section, if I create myroute/* with the token, I don't get a 404 with those special characters after using url_encode.

Is there a way to modify (?P<myVariable>[-\w]+) so that it works with url-safe special characters just like the CP version does?

You'll need to update the regex to allow for additional characters.  For example, if you want to allow !, then this will work:

return array(
    'myroute/(?P<myVariable>[-\w\!]+)' => array('action' => 'myPlugin/myAction')
);

Note that \w is essentially the same as writing [A-Za-z0-9_].


Show events until Midnight I have a list of events which should list each event until midnight of said day.

So If a gig happens at 17:00 it should still be visible until midnight of the same day.

I came relatively close (I guess) but it's not working:

{% set nocturne = now|date_modify('today 00:00') %}
{% set midnight = nocturne|date('Y-m-d H:i') %}

This should set {{ midnight }} to midnight of the current day.

Then I translate the custom date field of each gig into the same format as {{ mdinight }}

{% set getFirstGig = craft.entries.section('gigEntry').first() %}
{% set gigDate = getFirstGig.konzertDatum|date('Y-m-d H:i') %}

Then I want to call the whole thing:

 {% set upcoming = craft.entries.section('gigEntry').gigDate('>= ' ~midnight) %}
 {% for entry in upcoming %}
   <h2>{{ entry.title }}</h2>
 {% endfor %}

The code actualy works but my test entry with the time 17:00 is not displayed here. What am I missing? And is there a neater way of writing things?

Have you tried?:

 {% set upcoming = craft.entries.section('gigEntry').status('pending', 'live').gigDate('>= ' ~midnight) %}


Craft 3 / Commerce 2 + Multisite & COMMERCE_PAYMENT_CURRENCY I've just done a Craft 2 > Craft 3 upgrade, and it previously used different locales to change the default currency, depending on the locale.

In Craft 2, I would set COMMERCE_PAYMENT_CURRENCY in my index.php file (https://docs.craftcms.com/commerce/v2/payment-currencies.html#switching-currencies) so that it would set the currency for that specific site.

But in Commerce 2 this doesn't seem to be working at all.

My current setup is:

/web
   index.php
   /europe
      index.php

And in the /europe/index.php I've got:

<?php
/**
 * Craft web bootstrap file
 */

// Set path constants
define('CRAFT_BASE_PATH', dirname(__DIR__, 2));
define('CRAFT_VENDOR_PATH', CRAFT_BASE_PATH.'/vendor');

define('CRAFT_SITE', 'en_eur');

define('COMMERCE_PAYMENT_CURRENCY', 'EUR');

// Load Composer's autoloader
require_once CRAFT_VENDOR_PATH.'/autoload.php';

// Load dotenv?
if (class_exists('Dotenv\Dotenv') && file_exists(CRAFT_BASE_PATH.'/.env')) {
    Dotenv\Dotenv::create(CRAFT_BASE_PATH)->load();
}

// Load and run Craft
define('CRAFT_ENVIRONMENT', getenv('ENVIRONMENT') ?: 'production');
$app = require CRAFT_VENDOR_PATH.'/craftcms/cms/bootstrap/web.php';
$app->run();

I know this  file is being picked up when I go to http://website.local/europe, because if I delete the contents, or change the CRAFT_SITE it reacts correctly.

Just not working for Commerce 2.

It seems the const COMMERCE_PAYMENT_CURRENCY isn't checked and set inside Commerce 2 (It was in Commerce 1)

So I created a PR for this issue:

https://github.com/craftcms/commerce/pull/999


Entry form with field translated accross multiple sites I've got 2 sites, each with a locale (dutch - nl & french - fr). I've set up a frontend entry form that works nicely for all not translated fields, I am however struggling to get it to accept submissions for the translated field in both languages at once.

So what should the name of my HTML input tag be so I can use a translated field in a front-end entry form ?

EDIT:

As this might not have been clear before, the goal is to be able to send content for both languages in a single form submit. As I understand this will involve some PHP code, which is certainly no problem, but I'm a bit stuck as to what to add where so any pointers are much appreciated.

EXAMPLE

I've got a field with the handle description

I've tried the following input names but without result
 - fields[description][nl]
 - fields[nl][description]

Craft has no default process to handle such things, you need to do that on your own. Here is a form and a controller example how it could be achieved.

{# just to show you.. you can edit existing one as well #}
{% set entry1 = craft.entries.id(11).siteId(1).one() %}
{% set entry2 = craft.entries.id(11).siteId(2).one() %}

<form method="post">
    {{ csrfInput() }}
    <input type="hidden" name="action" value="my-module/save">

    {# if you have an ID use it #}
    {% if entry1 is defined and entry1 is not null %}
        <input type="hidden" name="id" value="{{ entry1.id }}">
    {% endif %}

    {# the site ids you want to store, you can as well use the handle or whatever you want #}
    <input type="hidden" name="siteId[]" value="1">
    <input type="hidden" name="siteId[]" value="2">

    {# title #}

Title Site 1

    <input type="text" name="title[1]" value="{{ entry1.title?? '' }}">

Title Site 2

    <input type="text" name="title[2]" value="{{ entry2.title?? '' }}">

    {# a field #}

Headline site 1

    <input type="text" name="fields[1][headline]" value="{{ entry1.headline?? '' }}">

Headline site 2

    <input type="text" name="fields[2][headline]" value="{{ entry2.headline?? '' }}">

    {# slug #}

Slug site 1

    <input type="text" name="slug[1]" value="{{ entry1.slug?? '' }}">

Slug site 2

    <input type="text" name="slug[2]" value="{{ entry2.slug?? '' }}">

    <input type="submit">
</form>

And then in your Controller

/**
 * actionIndex
 *
 *
 * @throws \Throwable
 * @throws \craft\errors\ElementNotFoundException
 * @throws \yii\base\Exception
 * @throws \yii\web\BadRequestHttpException
 * @return \yii\web\Response
 */
public function actionIndex(): \yii\web\Response
{
    // grab all the sites you want to store
    $request = Craft::$app->getRequest();
    $sites = $request->getRequiredBodyParam('siteId');

    $elements = Craft::$app->getElements();
    // change the handle of course
    $section = Craft::$app->getSections()->getSectionByHandle('news');
    // only applies to me, you need to figure out the type on your own
    // note: don't just grab things by id if you use the project config
    // please check if those variables are not null
    $type = $section->getEntryTypes()[0];

    $elementId = null;
    $element = null;
    foreach ($sites as $siteId) {
        // TODO check if the site exists
        $elementId = (int)$request->getBodyParam('id', $elementId);
        if ($elementId !== 0) {
            $element = $elements->getElementById($elementId, Entry::class, (int) $siteId);
        }

        if ($element === null) {
            $element = new Entry(
                [
                    'sectionId' => $section->id,
                    'typeId'    => $type->id
                ]
            );
        }

        $this->populateModel($element, $siteId);
        if($elements->saveElement($element) === false){
            // handle errors somehow
            echo "
";
            var_dump($element->getErrors());
            echo "
";
            die();
        }

        $elementId = $element->id;
    }

    return $this->asJson(['success' => true]);
}

/**
 * populateModel
 *
 * @param \craft\base\ElementInterface $element
 * @param                              $siteId
 */
public function populateModel(ElementInterface $element, $siteId)
{
    /** @var Entry $element */
    $request = Craft::$app->getRequest();
    $element->title = $request->getBodyParam('title.' . $siteId, $element->title);
    $element->slug = $request->getBodyParam('slug.' . $siteId, $element->slug);
    $element->enabled = $request->getBodyParam('enabled.' . $siteId, $element->enabled);
    // and so on.. continue with the rest you want to post + the author..

    $fieldsLocation = 'fields.' . $siteId;
    $element->setFieldValuesFromRequest($fieldsLocation);
}


Can Craft Commerce receive back the address used in paypal express checkout? When I checkout via Paypal express if I don't already have an address added to the cart model, when the paypal checkout process returns back there will be no address attached to the order.

The reason I want the address to be sent back afterwards is that i want to send customers to paypal from the cart page so there is no address form prior to leaving to paypal. This flow is something possible on say, woocommerce.

Well, after a transaction, you can log the full gateway response etc using this event: https://craftcommerce.com/docs/events-reference#commerce_transactions.onsavetransaction

(See what you can get at/log at https://craftcommerce.com/docs/transaction-model)

craft()->on('commerce_transactions.onSaveTransaction', function ($event){

      $transaction = $event->params['transaction'];
      BusinessLogicPlugin::log($transaction->response);

 });

...that should hopefully get you started on grabbing the data you need (I presume PayPal returns the address) - and then adding that to the cart at that point.


Sharing files with logged-in users Our client is a builder and they need a way for their contractors to login and upload/download documents about each build (plans, building consents, information etc). They (the builder) also need a way to share build information with their clients whose homes they're building (plans, progress, photos etc.).

Ideally:

contractors would be able to log in and we'd present them with a front-end page listing their projects, view the projects' files and maybe allow them to upload files? We want to be able to restrict the contractors only to builds that they're working on. If absolutely necessary it might be ok if the contractors needed to upload files through the CP rather than the front-end.
clients would be able to log in to a front-end page and see their build(s) and any files that the contractors/builder had uploaded and made visible to them. they shouldn't be able to view other clients' builds' information.

Is it possible to do this with vanilla Craft, or would we have to write a plugin to enable us to do this?

How easy is it to associate a user/users/groups with entries/asset sources?

Welcome to the community Adam.

Yes. Completely possible with vanilla Craft.

There's always more than one way to achieve things but here's a first round.

Start by creating two User Groups; Contractors and Clients. This will allow you to define permissions as needed.

Your 'Property Projects' Section could contain fields like.

Title
Description
Images
Related Contractors (user)
Related Client (user)
Etc as needed

Craft has a custom field type called 'User' which will allow you to relate all of the relevant contractors to the project and also define who the client is, all within the 'project' Entry.

Because the client also has a user profile and is able to log in to Craft (front end only), you can present their project information to them in the 'client area' of the site.

If you actually want the client to edit their project Entry, then you could also consider making them the Author of their Entry. (Just an idea)

Have a look at Craft's relatedTo documentation


Sluggish Asset Indexing Craft is a miracle for us but we've hit a snag.

We have about 100 products with around 10 images each.

We've scraped the images from the old site and put them into the appropriately named folders in assets.

Each time we add more, we have to run the asset reindexer which will only reindex the entire section. So even if we add a single folder of images, the entire lot are indexed again. If you've ever used the asset indexer you'll know that for whatever reason it is dog slow.

Why is this?
Could the immediate folder not be quickly indexed when the asset viewer is opened in the back end or at least provide an option to reindex that specific entrys assets?

Future projects may have 10,000 assets+ so it is a concern.

I'm open to better workflow suggestions as well.

Thanks :)

One way to get around this is to create a new Asset Source (let’s call it “Staging”).

Add the folder of unindexed images/assets into Staging, our new asset source (which should otherwise be empty).
Now, run the asset indexer via Settings > Update Asset Indexes only for our new source, Staging.
Once the indexer has done its job, go to the Assets section of the Control Panel and drag the freshly indexed folder of images/assets from Staging to the desired Asset Source.

Repeat the above process every time you wish to index a bunch of new assets without touching the previously indexed assets.


When posting entries, how do I post tags as well as the title and url? I'm trying to create a post where in the entry loop I can also post tags.

TITLE
   TAGS

But in my current understanding there's no way to get the post's tags where I'd assume it would be something like entry.tags

In the Control Panel you'd need to create a Tag Group first, which would hold all of you created tags. You then need to create a Tags field where you'd select the Tag Group created above as the source.

Once you've done all that, you can now add it to the fields of the entry type in question. If you gave the Tags field the name tags when you create it, you could then refer to it in your template as entry.tags.

In you template, you'd then loop through you tags and output them like this:

{% for tag in entry.tags %}
    {{ tag.title }}
{% endfor %}


Update fields structure from local to production How can I change fields/sections structure locally while having a live site and then upload the new structure, without wiping the production site?
In Wordpress ACF you can just Sync the fields structure, here it seems I need to close down the website for development?!

There are lots of similar questions with good discussion on this:

What's the best practice for handling data migration and organization across development environments?

Recommended workflow when developing new websites

What's the best practice for upgrading a versioned Craft site?

Ultimately, we want to building something in core to help manage this and the upcoming Craft 3 has taken a step in that direction with Content Migrations.


Headless Live Preview with Gatsby I am following Andrew Welch's post here - https://twitter.com/nystudio107/status/1168220713158942720?lang=en

I used the gatsby-config.js file he shared here - https://gist.github.com/monachilada/af7e92a86e0d27ba47a8597ac4e4b105

However, when I edit fields in the Live Preview, I get the following error:

UNHANDLED REJECTION Schema must contain uniquely named types but contains multiple types named "Craft".

    const { createHttpLink } = require('apollo-link-http');
    const fetch = require('node-fetch');
    const store = require('store');
    const sourceNodes = require('gatsby/dist/utils/source-nodes');
    require('dotenv').config();

    const craftGqlUrl = process.env.CRAFT_GQL_URL;
    const craftGqlToken = process.env.CRAFT_GQL_TOKEN;

    module.exports = {
      plugins: [
        {
          resolve: 'gatsby-source-graphql',
          options: {
            typeName: 'Craft',
            fieldName: 'craft',
            createLink: () =>
              createHttpLink({
                uri: `${craftGqlUrl}`,
                headers: {
                  Authorization: `Bearer ${craftGqlToken}`,
                },
                fetch: (uri, options) => {
                  const token = store.get('X-Craft-Token');
                  return fetch(
                    `${uri}${token !== undefined ? `?token=${token}` : ''}`,
                    options,
                 );
                },
              }),
          },
        },
      ],
      developMiddleware: app => {
        app.use('*', (req, res, next) => {
          if (req.query.token) {
            store.set('X-Craft-Token', req.query.token);
            sourceNodes();
          }
          next();
        });
      },
    };

Any ideas?

Thanks

they updated the sourceNodes cycle since this config, you will need to update the schema first doing something like this:

An example using the body hook if you need it:

createSchemaCustomization({ refresh: true }).then(() => {
    sourceNodes({ webhookBody });
});


Check saved entry is a brand new entry I have a plugin using the onBeforeSaveEntry event. Is there a simple way to check if the entry being saved is a brand new entry i.e. Add New Entry or if it currently exists and is just being updated?

You should be able to check for this like so:

if($params['isNewEntry'])
{
 // The entry is new
{
else
{
 // The entry is being updated
{

See here in the docs: http://buildwithcraft.com/docs/plugins/events-reference#entries-onBeforeSaveEntry


How do I output a dropdown's values if I'm on a registration form not tied to an entry? I have a registration form on my website. All of the fields here so far have been text, which is no problem. However, I've just been asked to create a dropdown box for users to select their salutation (mr, mrs, doctor etc).

I've created the field in the backend and linked it to Users, and I can update it through a front-end form no problem using the value="users/saveUser" method, and pulling the values using currentUser.salutation.options. However, when registering, I can't output the values like that as the user isn't signed in (because they're registering), so there is no currentUser. I can't use the documentation code:

{% for option in entry.dropdownFieldHandle.options %}
    <li>{{ option }}</li>
{% endfor %}

as there is no entry, I'm just on a template with a registration form on it. How do I output the dropdown options?

The not so elegant way to do this is to re-use the field somewhere else, where you do have access to (e.g. a global set). But you probably want to avoid this, right?

What you can do instead is to access the field directly using craft.fields. This function returns the FieldModel of your field, containing all of its properties. The FieldModel property you want is attributes.settings, where you can access your dropdown options like this:

{% set dropdownField = craft.fields.getFieldbyHandle('dropdownFieldHandle') %}

{% for option in dropdownField.settings.options %}
    {{ option.value }}
{% endfor %}


Updating the cart with a different variant after a variant is in the cart This would seem straightfoward but I can't figure it out. The situation:

I have a product with two colour variants (yellow, orange).
On the product page, the customer adds the default variant (yellow)
to the cart.
On the cart page, the customer should have the option of changing the
colour. But if I select a new colour from the drop-down and update
the cart, a new item is placed in the cart rather than the existing
item being replaced.

I could have them select the colour before they add it to the cart but that seems a little messy since I'd be bringing part of the transaction onto the product page. And they would not be able to update the colour if they changed their mind.

I suspect that I need to use an event to somehow delete the existing item and replace it with the new one. But I have no idea how to accomplish that.

Probably easiest to show all the other variants available on the cart page next to the line item, then use ajax to remove the current line item, and add the new variant (with same line item options like qty, notes and options).

Hope that makes sense.


How to handle the SECURITY_KEY? 1) Where To Put It

I’m wondering how I should proceed with the SECURITY_KEY which is now mandatory in Craft 3 (RC2). It has to be the same for one project, right? So it should be shared across developers. But is it a good idea to commit it to a Git repository? I guess not.

But then – where do I put it without losing it and still sharing it with team mates.

2) What If It Changed?

Due to some circumstances I lost the security key of the initial installation and added a new one. Could that lead to unwanted behavior down the road?

Enlightenment of any kind is much appreciated :)

First a bit of history:

Craft 2 has a similar concept where it would store a unique random security key in craft/storage/runtime/validation.key.  Alternatively, you could set the validationKey config setting if you wanted to provide your own key instead of use the one generated by Craft.

Craft 2 would essentially only use this for encrypting cookie data in a browser for authenticated sessions.  Because everything in craft/storage/runtime is temporary and could be deleted at any time, it is important in load-balanced environments to use the validationKey config setting and set it to the same key for every web instance behind the load balancer.

It is possible that a plugin might go through craft()->security->encrypt(), which takes the key into account, someone nukes the validation.key file that lives in craft/storage/runtime and Craft is unable to decrypt the data at that point.

Craft 3 decided that it would probably need to start encrypting data in the database at some point in the future, so it needed a more reliable way to guarantee there was a security key it could use.

So craft/storage/runtime/validation.key was deprecated and moved to craft/storage/security.key, the validationKey config settings was renamed to securityKey and Craft 3 now requires it to be set in some form or another.

Craft 3 also added another way to set it if you use the composer starter project via the .env SECURITY_KEY environment variable, so that you can use 'securityKey' => getenv('SECURITY_KEY'), in your craft/config/general.php file.

Now to the questions:

  It has to be the same for one project, right?

It should be, if you want to reliably encrypt and decrypt data across environments.

  So it should be shared across developers.

It should be, if you want to reliably encrypt and decrypt data across developers.

  But is it a good idea to commit it to a Git repository?

Definitely not a public repository.  Arguable in a private one.

  Due to some circumstances I lost the security key of the initial installation and added a new one. Could that lead to unwanted behavior down the road?

You wouldn't be able to decrypt any data that was encrypted with the old key.

In the case of session cookies, the worst that would happen is you'd lose your current session.  In the case of encrypted database email settings, you'd have to re-save your email settings.  In the case that a plugin is doing some custom encrypting and decrypting and relying on Craft's craft\services\Security class to to it, you wouldn't be able to decrypt that data.


Do I need to change the date.timezone in php.ini? If I set my timezone in server and in Craft general settings, do I still need to change it in php.ini? And how does Craft handle timezone in those three places?

This is from the Craft Documentation about Date/Time Fields:

  Note: Dates and times [...] are displayed in the site’s timezone, however they get stored in UTC like all other dates in Craft.

Additionally you can also use the date filter, which allows you to specify a different timezone for the output (list of supported timezones). If you want to bind a timezone to a specific site locale (multi-lingual sites), there's a dedicated Q/A on how to do that.


Creating a web app notification system/center? Ok, just thinking out loud and trying to work out the best way to achieve something. I have a community site with roughly 25k users.

I would like to create a way to send messages to those users within the front end of the site. This might be, for example, a notification of a new entry based on a set of criteria or even a message from the site admin to every user advising of a new feature.

The user should be able to view the message, which marks it as read and have the ability to delete it. All messages would live in a notifications page.

I’m just wondering if this is something that’s possible to achieve using Craft (via a plugin) or are there other services that are better suited to this. I can’t find anything online, everything seems geared towards push notifications which is slightly different to what I’m after.

Any thoughts as I can’t quite get my head around a way to achieve this.

Thanks!

I created a simple plugin for you to start. Note that is not a ready-to-use plugin so you'll have to change the things you need. This is just meant to be a starting point for you.
You can use the code and change it how you like it but mostly it is for learning purposes. So please don't expect it to be "finished" since I don't know your needs and your structure. The most important thing: my css/design skills are awful so I did not style anything.. you have to do it on your own

Feel free to say what I could/should change for you.

So here it is Usernotifications

So let's go through it:
My idea was to create a section called usernotifications (you don't have to do it by yourself, it is created onAfterInstall and can be changed later) that has different entry types for different messages and purposes. If you want to change the section handle make sure to change the $criteria->section = 'usernotifications'; in my service. This way you can create messages/notifications how you like and make them really dynamic. Please read this for some ideas and instructions Overview

Craft renders all those entries unless the user dismisses them or if they are too old. Check my service for the logic.

The Controller:has two methods, the first is actionRemoveNotificationto remove a message/notification for a user. So you'll display a dismiss button and as soon as someone clicks it, you create a record in your database with the userId and the entryId, so you know which user removed a notification.

/**
 * Remove a notification for a user
 */
public function actionRemoveNotification(){
    $this->requirePostRequest();
    $this->requireAjaxRequest();

    $response = [
        'success'   => false,
        'message'   => 'could not find entry'
    ];
    if($entryId = craft()->request->getParam('id')){
        $userId = craft()->userSession->getUser()->id;

        $record = UsernotificationsRecord::model()->find('entryId = :entryId AND userId = :userId', array(
            'entryId'   => $entryId,
            'userId'    => $userId
        ));
        if($record){
            // user already removed the message... there must be something wrong or he/ she cheated :P
            // you can actually ignore this... just wanted to show you how to search for entries^^
        }else{
            $record  = new UsernotificationsRecord();
        }

        $record->userId = $userId;
        $record->entryId = $entryId;

        if($record->save()){
            $response['success'] = true;
            $response['message'] = 'notification removed for user';
         }else{
            $response['message'] = 'could not save record';
            $response['errors'] = $record->getErrors();
        }
    }

    $this->returnJson($response);
}

The service:
The service should return all notifications for the current User or nothing at all if the user is not logged in

/**
 * Get all notifications for the current user
 *
 * @return string
 */
public function getAllNotificationsForUser(){
    // check if user is logged in...
    if(!craft()->userSession->getUser()){
        return false;
    }

    // fetch all removed notifications of the user to not fetch them in our query
    $removedNotifications = craft()->db->createCommand()
        ->select('entryId')
        ->from('usernotifications')
        ->where(['userId' => craft()->userSession->getUser()->id])
        ->queryColumn();

    $criteria = craft()->elements->getCriteria(ElementType::Entry);
    $criteria->section = 'usernotifications';
    // user should not be overwhelmed with too many notification when they didn't log in for too long
    // so show only notifications 14 days ago, you can change this length or remove it if you want
    // but make sure to change the value in the task as well.
    // you could also create settings for the plugin where you can store how long
    // then entries should be displayed
    $someDateInThePast = date('U', strtotime("-14 days"));

    $criteria->postDate = "> " . $someDateInThePast;
    // exclude the ids of the removed notifications
    if($removedNotifications){
        $criteria->id = 'and, not ' . implode(', not ', $removedNotifications);
    }

    return $criteria->find();
}

Your Variable
In your variable you'll get the html for those notifications, you can find the templates in templates/entries/entrytypes

public function getAllNotificationsForUser(){
    $html = '';
    $entries = craft()->usernotifications->getAllNotificationsForUser();
    if($entries){
        // I get a deprecated hint here, but the solution stated in craft throws
        // an exception... So I'm using this one
        $oldPath = craft()->path->getTemplatesPath();
        $newPath = craft()->path->getPluginsPath() . 'usernotifications/templates';
        craft()->path->setTemplatesPath($newPath);
        $html = craft()->templates->render('entries/index.twig', array(
            'entries' => $entries
        ));
        craft()->path->setTemplatesPath($oldPath);
    }
    return $html;
}

I guess these are the most important parts, you can check the rest in my git repo and ask/request whatever you like.

You can render the entries with

{% set notifications = craft.usernotifications.getAllNotificationsForUser() %}
{{ notifications|raw }}

Before I forget it: I created a task to remove too old records. You can access it via /admin/actions/usernotifications/removeOldRecords


How should I set an arbitrary session value in a plugin? I have some data I want to store in session:

$data = array
(
    'id' => '23539471'
    'token' => 'cf310b81-9431-4303-804d-c1635ae29278'
);

In my plugin I am doing this:

new HttpCookie('myCookieName', $data, [ 'expire' => time() + 3600 ]);

When I dd() that I can see the cookie as expected:

Craft\HttpCookie#1
(
    [name] => 'myCookieName'
    [value] => array
    (
        'id' => '23539471'
        'token' => 'cf310b81-9431-4303-804d-c1635ae29278'
    )
    [domain] => ''
    [expire] => 1427328767
    [path] => '/'
    [secure] => true
    [httpOnly] => true
    [CComponent:_e] => null
    [CComponent:_m] => null
)

However on the next page load the cookie is null:

$cookie = craft()->request->getCookie('myCookieName');

What am I doing wrong here?

::::EDIT::::

I have also tried this which seems to make the cookie available in the current request, but it's null on the next request:

$cookie = new HttpCookie('myCookieName', $data);
$cookie->expire = time() + 3600;

craft()->request->cookies->add('myCookieName', $cookie);

Try this:

$cookie = new HttpCookie('myCookieName', $data, [ 'expire' => time() + 3600 ]);
craft()->request->getCookies()->add( $cookie->name, $cookie );

Your code for reading the cookie is fine.

Also – since you use the term "session value" and tagged your question w/ "security" – I'd like to point out that cookies and session variables are different things. The former is stored on the client (the browser), the latter only on the server.

Unless you specifically need a cookie (e.g. because you want the data to persist a certain amount of time) it might be better to just create a basic session variable, using the HttpSession service:

craft()->httpSession->add( $key, $value ); // Set the session variable
craft()->httpSession->get( $key ); // Get the session variable

Read more about session variables vs. cookies here


{% cache %} tag creates duplicate entries When using the {% cache %} tag on an entry type, it caches the first entry on the page (multiple pages as well), and uses the cached entry for every other instance of that entry type on the page. What am I doing wrong? Here's an example. This one isn't complex so not sure it's worth caching, but I have some entries that have multiple relational fields.

{% for block in entry.contentMatrix %}
  {% if block.type == "copyOneCol" %}
    {% cache %}
        .... output entry type data
    {% endcache %}

  ...next if, etc.
{% endfor %}

Correct... by default the {% cache %} tag will use the current page's URI (including querystring) for the cache key.  If you want to change that behavior you'll need to specify the using key parameter and supply a unique key.  Although you might want to make something more unique than just block.id as that will be an integer and could potentially easily be overwritten.


Can Craft or any of its plugins render down flat html files for speed Having spoken to our 'ops guy' I've come to understand that if Craft CMS can render flat html or cache some of the pages then it makes thing a lot quicker to load etc.

Is this something craft can do? Also will it will be ok using assets from a cloud front CDN setup too?

I've come to understand that if Craft CMS can render flat html or cache some of the pages then it makes thing a lot quicker to load

I believe you're looking for the the {% cache %} tag, which will – on the first page request – capture the compiled HTML output inbetween the opening and closing of the tag, and write it to the database. On subsequent requests, Craft will pull and output the compiled (flat) HTML using a single SQL query (until the cache expires, is cleared manually or any entries, categories etc. involved is changed). This means that for most pageloads, complex database queries or taxing PHP operations (Twig templates are compiled to PHP) can be eliminated, which is usually a performance boost.

  will it will be ok using assets from a cloud front CDN setup too?

Yep, Craft is easily set up with external asset sources, and S3/CloudFront is supported. You will need a Craft Pro license, though.


Craft admin page 403 at home I can access Craft admin page at work, but I cannot access the admin page at home.
Is it limited by IP address in the code?
The whole site is hosted at vendor's server with Plesk web host edition.

I am new to Craft. Does anybody have any suggestion?

I find the answer. The config is in "Additional nginx directives"


Multiple Asset source question I'm creating a new Craft 3 site and migrating a lot of content from a wordpress setup. I'm wondering if it is better to separate assets into more sources - thus reducing the number of files per directory. An example would be a different asset source per section. Or is it fine to lump them all into one large directory? Is there any performance advantage to the former?

For most sites, there probably wouldn't be a difference, but if you're purely looking at performance and your site has a large number of assets, then 20 sources with 5,000 images and folders in each is going to scale better than a single source with 100,000 images and folders in them.


/login page in Craft keeps redirecting to home page. What could be causing that? I have a structured channel setup to house all the static pages on the site. All of the pages display fine on the page entry template. But the /login page does not. It displays fine in Live Preview. But on the actual site it immediately redirects to the homepage.

Is /login a protected keyword or something?

My .htaccess file is quite normal

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)/$ /$1 [L,R=301]

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

And I have the Sprout SEO plugin handling some basic redirects. I'm at a loss to know where to even look next.  Any ideas?

This isn't a login form to get into Craft by the way. It's to login to a separate website entirely.

/login is the default login page for Craft (see https://craftcms.com/docs/config-settings and scroll down to the users section ). You can override it by adding something like this to your config file:

'loginPath' => 'members/login',


Avoid notice of variable's not being set within dev mode With dev mode on, I'm getting a warning there is no entry.title, which I know about already because that's what I'm testing for:

{% if entry.title | length == 0 %}
    {% set entry = craft.entries.section('sectionname').slug(craft.request.segment(2)).first() %}
{% endif %}

Is there a way to test for a variable without dev mode triggering this notice?

I've had luck using "is defined" before using other logic operators.

This should do it:

{% if entry.title is defined and entry.title | length == 0 %}
    {% set entry = craft.entries.section('sectionname').slug(craft.request.segment(2)).first() %}
{% endif %}


Request must accept JSON in response I am getting error message while loading entries from dashboard. Please see attached screenshot -

While looking into browser console, I found the error message Request must accept JSON in response (using Craft v3.1.18)

That means your server/browser isn't setting the proper application/json accept header for AJAX requests.


Verbb Navigation Plugin Assistance needed Good morning and thank you in advance. I have one Verbb navigation called main. Underneath that, I have three nodes (see image). Under those nodes are regular links. I want my template to pull only the links from a specific node (Business Environment) under the main navigation, how would I go about doing that?

I know this is the code to get all nav:
{% set nodes = craft.navigation.nodes('main').all() %} but how would I access just Business Environment?

One way would be to use the positionedBefore query. Let's say Node 2 has the ID of 1000. Then you would write it like this

{% set nodes = craft.navigation.nodes('main')
    .positionedBefore(1000)
    .all() %}

The output would be all nodes before the node with the ID of 1000. In your case just Business Environment.

With descendantOf you would get all items within your specified node for example. Just check the documentation an all available node queries.

To get the ID of the node just use the inspector in the control panel, go to your navigation and look for the data-id="XXXX" of the node element.


Is it possible to use Craft as a webservice when no useragent is sent? We're setting up craft as a webservice. One of my programmers came to me and said every "slug" he called from the frontend application resulted in a 302 redirect to the login page of craft CP. Afer some investigation it seems that he didnt sent any Useragent string from his frontend curl client. After adding a useragent string everything went ok. Is this normal behaviour or can it be configured?

Thanks

This post may help:
Is it possible to disable `requireUserAgentAndIpForSession` for specific instances?

Take a look at the requireUserAgentAndIpForSession config-setting.

Post from phone, sorry if formatting is a bit off.


How to truncate auto-generated entry titles? I am auto-generating entry titles based on a plain text field. Now, I would like to limit the characters the title contains of (e.g. 50 chars while the plain text field has 500 chars). How could I do that?

Thank you very much for your help.

You can use any Twig in the dynamic title field. There are plugins that provide Twig filters to truncate text, but here’s how you can do simple truncation without any plugin.

{{ object.myTextField|length > 50 ? object.myTextField|slice(0, 50) ~ '...' : object.myTextField }}


CraftCMS + GraphQL - orderBy Random? I'm currently building my first JAM stack app using Nuxt as my front-end framework and Craft as my headless CMS. I'm leveraging Apollo as my GraphQL interface client. I have it pulling data back just fine, but I want to shuffle the output to always be random. I know I could do this in a typical twig query with .orderBy('RAND()') but I need a way to shuffle that with my GraphQL endpoint.

I know I can do this on the frontend by shuffling my <li>s or something, but would love for this to be more performant if I can help it.

I've tried a few variations of the below but can't seem to get it to work. Is this possible?

apollo: {
  entries: {
    query: stories,
    prefetch: true,
    variables: {
       "section": ["stories"],
       "orderBy": "RAND()"
    }
  }
},

RAND() is the correct way to do this. (At least, it works in 3.5). One catch is that GraphQL queries are cached by default, so your results will always be returned in the same random order until your cache is invalidated. If you're using Nuxt with a static target, your entries will be displayed in the same order until the cache is expired and your app is regenerated.
Example of a working query:
{
  entries(section: "posts", orderBy:"RAND()") {
    title
    id
  }
}


How to set different file size upload limits for different Asset Sources How can I set file size upload limits on a per-Asset Source basis?

I can only see how to set a global maxUploadFileSize in the general config.

I'm sure I must be missing something obvious though, as this is something that was even possible in EE1! :-) It's a common use case to have a lower limit on some upload destinations to keep clients from uploading massive files (e.g. images), and to allow a higher limit on certain upload destinations for general file downloads.

Otherwise, we will have to use a 10mb limit for everything globally (as an example), and the clients can then upload images up to that size (which can happen with high resolution images). This will waste storage space and potentially run into PHP memory issues when resizing high resolution images for the front-end. :-/

Craft has no settings defined for it. The only setting it has is maxUploadFileSize which you have already mentioned. So the solution of this is using Imager Plugin. There is also a blog from nystudio107 on this. Let Client upload image of any size or dimension, but you could resize them using Imager plugin.


Mysql error when defining related tables I'm trying to create some relational tables from 2 records but I keep getting the mysql error:

CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]: General error: 1005 Can't create table 'craft1.#sql-75c_603' (errno: 150)

The relationship is between 2 tables: quote and quotedItem. I based the code off the relationship seen here: http://www.yiiframework.com/doc/guide/1.1/en/database.arr between the Post and User tables.

The tables create fine without the relationship defined (I've also noticed they create with the relationship but I also get the above mysql error when installing the plugin so I presume it hasn't worked correctly).

I'm new to making plugins so there's probably something obvious I'm doing wrong but if someone could point me in the right direction that would be great.

Here's my code for quoted Item:

class ShoppingCart_QuotedItemRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'shoppingcart_quoteditems';
    }

    protected function defineAttributes()
    {
        return array(
            'quoteId' => AttributeType::String,
            'itemId' => AttributeType::String,
            'quantity' => AttributeType::String
        );
    }

    public function defineRelations()
    {
        return array(
            'quote' => array(static::BELONGS_TO, 'ShoppingCart_QuoteRecord', 'quoteId'),
        );
    }
}

And here's quote:

class ShoppingCart_QuoteRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'shoppingcart_quotes';
    }

    protected function defineAttributes()
    {
        return array(
            'title' => AttributeType::String,
            'country' => AttributeType::String,
            'name' => AttributeType::String,
            'email' => AttributeType::String,
            'company' => AttributeType::String,
            'phone' => AttributeType::String,
            'department' => AttributeType::String,
            'reference' => AttributeType::String,
            'questions' => AttributeType::String
        );
    }

    public function defineRelations()
    {
        return array(
            'quotedItems' => array(static::HAS_MANY, 'ShoppingCart_QuotedItemsRecord', 'quoteId'),
        );
    }
}

Ok, so I figured it out.

The reason it fails to create the tables is because the foreign key has to be of the same type as the primary key it's relating to.

So I just changed:

'quoteId' => AttributeType::String,

to:

'quoteId' => AttributeType::Number,

And it worked!


Getting line items + shipping and their tax rates in a plugin I'm working with an external system that requires me to send line items + their tax rate, and the shipping as it's own line item + any tax applied to the shipping.

The format expected looks something like this:

"items": [
    {
        "reference": "11",
        "name": "The Fleece Awakens",
        "quantity": 13,
        "unit_price": 5250,
        "tax_rate": 2500
    },
    {
        "reference": "9",
        "name": "Romper for a Red Eye",
        "quantity": 3,
        "unit_price": 4000,
        "tax_rate": 2500
    },
    {
        "reference": "2",
        "name": "Fedex",
        "quantity": 1,
        "unit_price": 180,
        "tax_rate": 1500
    }
]

The price and tax rate is multiplied by 100.

This gets even more complex depending on how the tax and shipping rules are setup.

I'm trying to cover everything, like:

When a shipping method has a base rate + perItemRate
When tax is applied either to item price or item shipping cost, or item price + shipping cost
When tax is included

How would I consistently get it in the format like this, while accounting for any adjusters (tax, shipping, sale/discounts)?

I noticed adjusters has optionsJson->lineItemsAffected, which might be interesting.

However, I don't know enough about the complex tax/shipping system or how adjusters work in general to foresee all the different combinations of adjusters.

Currently there is a limitation on the adjuster system, in that there is no history of every adjustment made to the order for most adjusters. This is a limitation of the current architecture. I will walk through the current architecture and explain the changes that we plan to release in Craft Commerce 2:

Let's start with an order that looks like this:

itemA price        10
      shippingCost 0
      discount     0
      tax          0
      total        10

itemA price        20
      shippingCost 0
      discount     0
      tax          0
      total        20

baseShippingCost   0
baseTax            0
baseDiscount       0

order total        30

In the above order, there has been no adjustments run "over" the order yet. Lets think of this as the state of the order at the beginning of a recalculation.

When the adjuster recalculation starts it clears all the adjustable values (shippingCost, discount, tax) from the line items, and also clears the 'base' values on the order (baseShippingCost, baseDiscount, baseTax). It also resets the totals.

The first adjuster to run is the discount adjuster. It will apply any matching discounts by modifying the amount of discount on each line item as well as the baseDiscount amount for order level discounts.

Let's say that 2 discounts were applied to the order. One that takes $1 off each line item and $10 of the total order, and the second discount taking $5 off the total order and $2 off each line item. Since there are 2 discounts, the discount adjuster is deciding to return 2 adjustments to the order, one totaling $12 and the other totaling $9. The order will then look like this:

itemA price        10
      shippingCost 0
      discount     -3
      tax          0
      total        7

itemA price        20
      shippingCost 0
      discount     -3
      tax          0
      total        17

Adjustment1 (discount) -12
Adjustment2 (discount) -9

baseShippingCost   0
baseTax            0
baseDiscount       -15

order total        9

The total of the order is now $9. Currently, there are 2 ways to get to this total:

1)

all line item totals (which include each line item's `shippingCost`, `tax`, `discount`)
+
`baseShippingCost`, `baseDiscount`, `baseTax`

2)

adjustments
+
all line item price

Both of these methods get to the same total, but the problem with this is that BOTH methods still do not contain all the information. For example, the line itemA's discount is -3 but it is made up of 2 discount adjustments, -1 and -2. There currently is no way to fully know how that value came to be. The same goes for the total adjustment amount, it only stored a total adjustment made to the order, with no breakdown.

This is not ideal, but we tried to combat it with an attribute on the adjustment model:

OrderAdjustmentModel::optionsJson

This column stores an array of line item IDs (in array key lineItemsAffected) that the adjustment modified, and it also stores information about the modification rules used like the discount's attributes.

For example, in AdjustmentA all of the attributes (pricing rules) of the discount are serialized into a json array and stored in the optionsJson.

Unfortunately the 2 ideas above do not help in understanding all adjustments made to order without a lot of work.

Solution in Craft Commerce 2:

We have been working on Commerce 2 with a solution to the above issues.

1) All adjustments made to the line items are now stored as seperate adjustments on the order. For example, in the above order AdjustmentA would
create 3 adjustments, 1 to each line item for the discount, and one to the order.

All adjustments will be for single amount, not combined amounts. This means adjusters will no longer do this: $order->baseShippingCost += 10. They will not mutate the value, only add a new adjustment of 10.

2) All line item fields that an adjuster modifies are removed. This means shippingCost, tax, discounts are no longer stored on the line item. The attributes will be replaced with magic getters that add up all adjustments made to the line item for a particular adjustment type. We will probably still store them in the database but in a column called 'adjustment totals' as json (so you can still do DB queries on them).

3) All order level baseShipping, baseTax, baseDiscount attributes will be removed and replaced with order level adjustments.

This means adjusters now will return more adjustment models (with more lines added to the order), but the developer can still use the magic methods to show the total amount of shipping, discounts, tax at the line item or order level.

Let me know if you have any questions, and I will update the answer.

Note1: The migration to the new order calculation system will turn each of the line item's adjustable amounts (shippingCost, tax, discount) and baseX amounts into line item adjustments. We will archive the old adjustment totals (or zero them out so they can still show the data). This will not lose any order information, nor add any, but will be compatible with the new order recalculation adjustment system.

Note2: The issue with supplying information to 3rd parties ONLY as line items is that there can be order level adjustments that live outside of the line items. You would need to figure out how to send that additional pricing information yourself.


Paypal Express shows a 404 error when returning to Commerce's completePayment endpoint I'm trying to use Paypal Express with Craft Commerce.
It sends us Paypal's login page correctly, with the right amounts, but when you click pay it returns us to this URL:

/index.php/actions/commerce/payments/completePayment?commerceTransactionId=43&commerceTransactionHash=cbcad33abe2cf99ea63c8ee49c0ee9af&token=EC-0N649078HJ025430W&PayerID=VAWBVKWL78DHN

but shows a 404 error with no other errors

I've tried using the show errors code mentioned in this post:
Handoff to Paypal Express with Craft Commerce

and I've tried it with Stripe which fully works.

There are no log error logs in PHP or Craft's logs, just a 404.

I've tried changing different products, different shipping types, I've uninstalled all other plugins.

It just fails with Paypal, and works with Stripe.

Any ideas?

You have a variable name collision. You must change your Craft token name to something else, as per documentation


Sprout email notifications - setting the recipient? I have a front end form for logged in users to submit new entries. I want that user to receive a notification (via Sprout Email) that there entry has been received (it's pending/disabled on submission).

I'm struggling with what my recipient variable should be!?

I should add, it's the logged in user that added the entry that I want to receive the notification.

Sprout Email allows Craft Object Syntax for the object that is available when an event is triggered, in the same way you would use the Sections Title Format field.

If your form is submitting an Entry the dynamic object being submitted is an EntryModel and all values from that EntryModel will be available to several of your fields in Sprout Email. To access those values you can use one of two syntaxes:

Shorthand Object Syntax

{title} or {customFieldHandle}

Object Syntax

{{ object.title }} or {{ object.customFieldHandle }} - this longer form can also be used in more advanced twig constructs like {% if object.title %}

Both of these syntaxes are comparable to how you would use {{ entry.title }} or {{ entry.customFieldHandle }} in your templates.

This is described with a few more examples on the Dynamic Values page of the docs


Craft 2.6 Asset Upload 500 Error (on Heroku with Amazon S3) I just upgraded a fully functional Craft website to Craft 2.6. The site is hosted on Heroku with assets uploading to S3. So all of that has been working really well for about 10 months. Now I can't upload any more assets. When I choose a file from my computer, the upload hangs.

I get a console error:

Failed to load resource: the server responded with a status of 500 (Internal Server Error)

https://www.example.com/index.php?p=admin/actions/assets/uploadFile Failed to load resource: the server responded with a status of 500 (Internal Server Error)

Here is the response body of the 500 error:

PHP Error [500]

Trying to get property of non-object (/app/craft/app/services/AssetsService.php:707)

#0 /app/craft/app/etc/errors/ErrorHandler.php(184): Craft\ErrorHandler->handleError()
#1 /app/craft/app/framework/base/CErrorHandler.php(133): Craft\ErrorHandler->handleError()
#2 /app/craft/app/framework/base/CApplication.php(834): Craft\ErrorHandler->handle()
#3 /app/craft/app/etc/web/WebApp.php(688): Craft\WebApp->handleError()
#4 /app/craft/app/services/AssetsService.php(707): Craft\WebApp->handleError()
#5 /app/craft/app/controllers/AssetsController.php(52): Craft\AssetsService->uploadFile()
#6 /app/craft/app/framework/web/actions/CInlineAction.php(49): Craft\AssetsController->actionUploadFile()
#7 /app/craft/app/framework/web/CController.php(308): CInlineAction->runWithParams()
#8 /app/craft/app/framework/web/CController.php(286): Craft\AssetsController->runAction()
#9 /app/craft/app/framework/web/CController.php(265): Craft\AssetsController->runActionWithFilters()
#10 /app/craft/app/framework/web/CWebApplication.php(282): Craft\AssetsController->run()
#11 /app/craft/app/etc/web/WebApp.php(818): Craft\WebApp->runController()
#12 /app/craft/app/etc/web/WebApp.php(288): Craft\WebApp->_processActionRequest()
#13 /app/craft/app/framework/base/CApplication.php(185): Craft\WebApp->processRequest()
#14 /app/craft/app/index.php(62): Craft\WebApp->run()
#15 /app/public/index.php(19): require_once()

Any ideas? This happens when I try to upload an asset directly in the Assets section of Craft as well as uploading a new image to an Entry.

Thanks.

My answer to my own question: after exhausting all avenues I ended up having to abandon Heroku for this Craft site. I moved the Craft site over to a DV server at Media Temple, moved over the database, and everything is working fine.


Create Craft 3 Plugin - Call to a member function getVersion() on null I'm trying to write my own Craft 3 TWIG filter / functions.

For this purpose I created a scaffolding using pluginfactory.io. I did a composer require and composer correctly symlinks the plugin to the Craft project's vendor directory. When I click "Plugins" in the CP it shows my plugin, but when I click "Install" I get the error:

Call to a member function getVersion() on null

caused in /var/www/html/vendor/craftcms/cms/src/services/Plugins.php on line 328 which is

$info = [
  'handle' => $handle,
  'version' => $plugin->getVersion(), // <== Line 328
  'schemaVersion' => $plugin->schemaVersion,
  'installDate' => Db::prepareDateForDb(new \DateTime()),
];

Why? I compared my composer.json with e.g. nystudio107/craft3-pathtools and it's almost identical and I definitely have a version defined in my composer.json.

Why is $plugin null?

Any suggestions?

Shame on me, but maybe still interesting to others who have this problem: I'm using vagrant and the path of my extension was NOT mounted to the vagrant box. That's all. So it simply could not find the files. Maybe a warning by craft would be nice, that it can not load the plugin files

Though I do not understand, why it found the composer.json to read the basic information, which showed up in the Plugins section of Craft...


Border Radius on Image Transforms Is it possible to add a border-radius to an Image Transform?

I'm using circular profile photos for a client. I could do the radius in CSS, but it'd be nice for the image to output with the radius.

I'd just crop it with CSS, as you alluded to. https://www.webpagefx.com/blog/web-design/circular-images-css/


How do I get get clean URLs in twig templates When I use {{page.link}} or {{page.url}} in a template I am getting

http://mysite.dev/index.php?p=pages/about/history

when what I really want is

http://mysite.dev/pages/about/history

The latter URL works fine on the site, but I'm using the above code to try and create a menu, and don't want the menu item linking to the first URL.

How do I fix this?

Documentation for removing index.php

http://buildwithcraft.com/help/remove-index.php

You might have to adjust the .htaccess code depending on your server.


Dynamic placeholder text in 'Plain text' field type I have a plain text field that encourages content editors to write a more detailed description of a piece of content.

However, on the front-end the template will use the post title by default if this is not supplied.

I would like the placeholder text for this optional plain text field to represent the reality of this in the Dashboard - i.e. to display the title field as the placeholder if the field has not otherwise been used.

When editing the field, I have tried setting the 'Placeholder Text' as {title}, but this does not work - it displays "{title}"!

Is this possible? I don't want to create a new custom type of field as it seems a little heavy handed for this use case.

When editing the field, I have tried setting the 'Placeholder Text' as {title}, but this does not work - it displays "{title}"!

Yeah, those placeholder text fields aren't dynamically parsed as Twig templates like others (Entry URL Format, etc.).

  I don't want to create a new custom type of field as it seems a little heavy handed for this use case.

Unfortunately that's all that is coming to mind for me for this use-case.


assets.beforeSaveEntry to upload file to secondary location in addition to Amazon S3 Is it possible to grab file (mp3) that has been submitted as part of an entry and post it to a secondary location (Soundcloud via their API) before it gets stored to my asset source at Amazon S3?

To complicate things a bit, I am uploading multiple files per entry, each one having its own field. One is the produced file (field name mp3File) selection) and the others are the raw recordings (field name rawFiles).

It is produced file (mp3File) that I am wanting to push to SoundCloud with their API.

I have a handle on how the SoundCloud API works, what I need is to pass the file path to the SoundCloud API. I think it is probably better to do this while the file still resides on the server before it makes its way to Amazon. Or it could be that I don't understand the flow here at all.

So this works. Now that I have the understanding of how things fire I can clean this up. I am open to better solutions.

    craft()->on('entries.onSaveEntry', function(Event $event)
    {

        $entry = $event->params['entry'];
        if($event->params['isNewEntry'] && $entry->getSection() == 'Episode')

        {

        require_once(craft()->path->getPluginsPath().'cranberry/vendor/soundcloud/autoload.php');

        $clientID       =   'xxxxxxxxxxxxxxx';
        $clientSecret   =   'xxxxxxx';
        $username       =   'xxxx';
        $password       =   'xxxx';

        $facade = new \Njasm\Soundcloud\SoundcloudFacade($clientID, $clientSecret);
        $facade->userCredentials($username, $password);

        $mp3File = $entry->mp3File->first()->filename;
        $url = $entry->mp3File->first()->getUrl();

        $mp3TempFile = "/var/www/pathway/craft/storage/tmp/".$mp3File;

        // download the file from amazon s3 and push it to soundcloud
        $fp = file_put_contents($mp3TempFile, fopen($url, 'r'));

        $params = array(
            'track[title]' => iconv('UTF-8', 'ASCII//TRANSLIT', $entry->title),
            'track[sharing]' => 'public',
            //'track[asset_data]' => $mp3Filepath,
            'track[description]' => iconv('UTF-8', 'ASCII//TRANSLIT', strip_tags($entry->summary)),
            'track[track_type]' => 'podcast',
            'track[genre]' => 'Learning',
            'track[license]' => 'all-rights-reserved'
            );

        $response = $facade->upload($mp3TempFile, $params);
        $fp = unlink($mp3TempFile);

        }

    });


How to show details on linked entry in CP (another field inside related entry) I'm using a row layout SuperTable to create a recipe (ingredients are entries). Is there a way to show another field inside the related entry (not just the title) together with the entry title? I need to specify the measure (like tbsp) otherwise it's hard to know the quantity for what you're editing.

I need to show Spinach (100g) in the ingredient row. The 100g is a field called ingredientMeasure inside the ingredient entry.

I was thinking about using a hook (modifyEntryTableAttributes) - but not sure how to do it with ST. This is my hook example (from Ben's plugin tutorial):

// ADD A HOOK - modify the contents of table cell on entry index page
public function getEntryTableAttributeHtml(EntryModel $entry, $attribute) {
    // check if setting is enabled and we're editing our column
    if ($this->getSettings()->showCountOnEntryIndex AND $attribute == 'count')
    {
        return 'C<b>' . craft()->entryCount->getCount($entry->id)->count . '</b>';
    }
}

Basically, what you want to do isn't possible – there's no hook for modifying the content in a relational field's input HTML.

The getEntryTableAttributeHtml hook is meant for element indexes (such as the entry list displayed at /admin/settings/entries), not relational fields (such as Entries, which is what you're using in your SuperTable).

The easiest workaround would be to add the value of the ingredientMeasure field to your ingredient entries' title format.

First, you'll need to create a new custom field for your entries' titles – the handle title is reserved, so call it ingredientTitle or the like, and add it to your "Ingredients" section's entry type. Make the field required. Second, you'll need to go into your "Ingredients" section's entry type settings, and uncheck the "Show the Title field" checkbox. Then, add something like this to "Title format" field:

{ingredientTitle} ({ingredientMeasure})

Finally – you'll need to re-save all your ingredient entries in order to have their titles updated to the new format.

Obviously, the big tradeoff with this method is that your ingredient won't actually be titled "Spinach" anymore – it'll be titled something like "Spinach (100g)". In your templates, this can easily be remedied by using

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

Instead of the normal entry.title, wherever appropriate.

Also, unless you want the ingredientsMeasure field to be part of the Ingredient entries' slugs, you should replace {slug} in your Ingredients Section's URL format with {ingredientsTitle|kebab}.


Best practice for multi site development, staging, etc. [Special snowflake details inside] The story:

Site A is running in Craft 3 as the primary site. (On a subdomain, if that matters.)
Sites B and C are running in ExpressionEngine 2.
I need to write new HTML/CSS for sites B & C.
I need to import and cut/paste content from sites B & C so that eventually, sites A, B & C are all running under the original Site A Craft install.

Meanwhile, content will continue to be updated on Site A.

So...

If I clone Site A and develop with it, on my local PC, importing content from the other two sites, I can get all three sites running together. However, then I'm unsure how to reconcile the data from Site A with my development version.

What do folks recommend?

If I'm understanding correctly, there are two methods I'd suggest;

Instead of reconciling the data from Site A, I would first setup Site A with Site B + C and add any new fields/sections you will need, then set up project config so that any structural changes you make locally on development will be made remotely. Put it live, then you/the client can populate the content on the live Craft install whilst you work on developing the theme locally.
I would look into the Feed Me plugin. On Site A you can set up Element API to expose all your content in JSON, then on you development site use Feed Me to import the content from Site A. This may take some setting up initially depending on the complexity of your site, but once it's done it's just a case of clicking a button to pull the latest content.

Hope that helps!


Check if an order has been refunded in a template? What’s the best way to check if an order has been refunded in a template?

For example, you may want to:

Add a message to the order/invoice template if a refund has been successful.
Determine the status of the refund; perhaps displaying a different message if the status is not yet success.
Show the time of refund.

You get the related transactions for an order with

order.transactions

So you can loop over the transactions like so:

{% for transaction in order.transactions %}
  {{ transaction.type|title }}
  {{ transaction.status|title }}
  {{ transaction.amount|currency(transaction.currency) }} ({{ transaction.currency }})
  {{ transaction.paymentMethod.name }}
  {{ transaction.dateUpdated|date }} // could also format to get the time
{% endfor %}

The transaction 'type' column can hold the following values:

authorize, capture, purchase, refund

The transaction 'status' column can hold the following values:

pending, direct, success, failed

Some of those transaction types have a 'parent' transaction, for example, if you authorized a credit card, you would have a authorize transaction. When you capture that authorization you get a capture transactions whose parent is the authorize transaction.

{{ transaction.type }} // could return 'refund'
{% set parent = transaction.parent %}
{{ parent.type }} // could return 'purchase' or 'capture'

Let me know if you have further questions and I will improve the answer.


Control Panel URLs being rewritten to use index.php?p=admin/ All of our control panel URLs are being rewritten to show index.php?p=admin/***

While this isn't a problem in itself we can't determine why it's happening when the frontend of the website itself is writing the URLs cleanly and correctly without the index.php?p=

We have in our .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>

There are no other Rewrites happening.

A possibly related issue we're seeing is that there are no entries, assets or categories listing in the control panel. Looking through the logs I can see the following

2015/09/22 16:55:23 [error] [exception.Craft\HttpException.400] exception 'Craft\HttpException' with message 'Param “elementType” doesn’t exist.' in /var/www/vhosts/mysite.org/httpdocs/craft/app/services/HttpRequestService.php:586
Stack trace:
#0 /var/www/vhosts/mysite.org/httpdocs/craft/app/controllers/BaseElementsController.php(50): Craft\HttpRequestService->getRequiredParam('elementType')
#1 /var/www/vhosts/mysite.org/httpdocs/craft/app/controllers/ElementIndexController.php(68): Craft\BaseElementsController->getElementType()
#2 /var/www/vhosts/mysite.org/httpdocs/craft/app/framework/web/CWebApplication.php(281): Craft\ElementIndexController->init()
#3 /var/www/vhosts/mysite.org/httpdocs/craft/app/etc/web/WebApp.php(784): CWebApplication->runController('elementIndex/ge...')
#4 /var/www/vhosts/mysite.org/httpdocs/craft/app/etc/web/WebApp.php(276): Craft\WebApp->_processActionRequest()
#5 /var/www/vhosts/mysite.org/httpdocs/craft/app/framework/base/CApplication.php(184): Craft\WebApp->processRequest()
#6 /var/www/vhosts/mysite.org/httpdocs/craft/app/index.php(62): CApplication->run()
#7 /var/www/vhosts/mysite.org/httpdocs/index.php(40): require_once('/var/www/vhosts...')
#8 {main}
REQUEST_URI=/index.php?p=admin/actions/elementIndex/getElements
HTTP_REFERER=http://mysite.org/index.php?p=admin/entries/pages
---
2015/09/22 16:55:23 [error] [application] Param “elementType” doesn’t exist.

******************************************************************************************************

We do have PATH_INFO installed and enabled on the server.

Craft Pro 2.4.2692

Any help would be much appreciated.
Many thanks

With regard to the 2nd part of the original question, whereby no entry, asset, category or user lists were display, this turned out to be a post_max_size issue in the php.ini config. It had been incorrectly set to 64MB rather than 64M.


Is there an option to edit title-parameter for Links in Redactor? How can i edit Link-Title in Redactor Richtext-Editor? Didn't found any config options or plugins for that. Can't believe this is not possible yet.

There's currently no out-of-the-box way with Redactor, other than to open the HTML source view and adjust the <a> tag manually to be something like <a href="yourlink" title="yourtitle">.

All Redactor settings regarding links can be found here: http://imperavi.com/redactor/docs/settings/links/


External liblary class inside plugin controller I have external library handling connection to mail delivery system. Its just php class. How do i include and use this class inside craft plugin controller?

library.php:

class mail_delivery{
    //code
}

plugin/controllers/controller.php:

namespace Craft;

class controller extends BaseController
{

    public function init()
    {
        require_once 'library.php';
    }

    public function method(){
        $object = $rest = new mail_delivery();
    }

I get PHP error:

  ( ! ) Fatal error: Class 'Craft\FmRestAPI' not found in
  C:\xampp\htdocs\mmimport\craft\plugins\freshmailintegration\controllers\FreshmailIntegration_EmailController.php
  on line 27

Is this some kind of namespace issue?

Is this some kind of namespace issue?

Yes.  If the class you're trying to instantiate is in a namespace, you'd need to indicate that:

$object = $rest = new \the\library\class\namespace\mail_delivery();

If it does not use namespaces, it's probably in the global namespace:

$object = $rest = new \mail_delivery();

If you do it like in your example:

$object = $rest = new mail_delivery();

Then PHP will assuming it's in the current file's namespace (Craft) and you'll get the error that you see.


Craft Commerce: Adding Number fields to product and cart pages I'm adding the following to my _product.html template to try and add a number to a line item in the same way that notes are added. I'm using the following input:

<input type="number" name="numberDistance" step="any" placeholder="Add your minimum distance in mm..." id="distanceNum">

I then add the following to my cart template to output the field:

Min distance: {{ item.numberDistance }}

When I add an item to my cart I get the following error:

Craft\Commerce_LineItemModel and its behaviors do not have a method or closure named "numberDistance".

What is going wrong and how do I fix this?

You will need to make use of lineitem "options" param. You can attach as many options as you like using something like:

<input type="number" name="options[numberDistance]" step="any" placeholder="Add your minimum distance in mm..." id="distanceNum">

And then these will be avaliable on the lineItem once saved using

{{ item.options['numberDistance'] }}

More info can be found on the Craft Commerce docs


How to add custom fields for products in Craft Commerce and output them in the cart You can add a note field for a product on the 'Add to Basket' form in Craft Commerce, and output this on the basket page using {{ item.note }}

So I'm using the note field field to record the page URL for the product, so I can link back to the product page for each line item in the basket.

However, I'd rather use a custom field or a dynamic field to do this instead. Is this possible? If so, how?

I've tried creating a dynamic field named 'test-page-url', but that didn't work when trying to output {{ item.testPageUrl }} on the basket page. I got this error:

  Craft\Commerce_LineItemModel and its behaviors do not have a method or
  closure named "testPageUrl".

I also tried creating a new product field (productPageUrl) and using this name, but that didn't work either. I got this error when trying to use {{ item.productPageUrl }} on the basket page:

  Craft\Commerce_LineItemModel and its behaviors do not have a method or
  closure named "productPageUrl".

Thanks for any help in advance.

Ok, I think your thinking on this stuff is a little wrong at the moment.

The way Commerce works is that when you add to a cart, it creates a lineItem.  That data model holds basic stuff like qty and a description and so on, enough to just manage and print out the basics of the cart.  However, it also  (for each lineItem) maintains a reference to the purchasable itself - with this purchasable being a purchasable variant (either a products defaultVariant if it is a non-variant product, or the actual variant chosen if it is a variant product).

From this purchasable, you can get back to the full product and/or variant model.

So, you don't actually need any custom fields to get the product's url.  You can print it simply  by working back from the lineitem to the product or variant as you need, using:

lineItem.purchasable.product.url - for the product url
lineItem.purchasable.url - for a specific variant url

In more general terms, in Commerce settings, you can add as many fields to products as you like, and indeed you can also add custom fields to variants.  It's hugely powerful.  And from then on you can get to those fields using the same approach:

lineItem.purchasable.product.fieldHandle - for product fields
lineItem.purchasable.fieldHandle - for variant fields.

The Commerce example templates are very handy for figuring this stuff out, and it's well worth spending a couple hours with the default Commerce install and those templates to see how it all hangs together.


Specify a specific entry type in paramaters I am currently searching within multiple sections on my site.
I'd like to limit one of the sections (courses) to a specific entry type (cce)?
Any help would be greatly appreciated!
{% set params = {
    section: [ 'pages', 'careerPaths', 'courses', 'events', 'eventsIndex', 'news', 'newsIndex', 'programs', 'allPrograms', 'facultyStaff', 'facultyStaffIndex' ],
    limit: siteVariables.resultsLimitForPagination
} %}

{# if search term exists, search #}
{% set searchTerm = craft.app.request.getParam('q') %}

{% if searchTerm|length %}
    {% set params = params|merge({ groupBy: ['title'] }) %}
    {% set params = params|merge({ search : searchTerm, orderBy: 'score DESC'}) %}
{% endif %}

{# Query #}
{% set query = craft.entries(params)  %}

{% paginate query as pageInfo, entries %}

I suspect the only way to do this at the moment would be to look up the EntryTypes for each of the desired Sections, and filter just by those—ignoring the section query facet. For example:
{# Map some Section handles into actual Section models, using the Sections Service: #}
{% set sections = ['one', 'two', 'three'] | map((h) => craft.app.sections.getSectionByHandle(h) %}

{# List your "forbidden" Entry Types in a `sectionHandle:entryTypeHandle` format, so we can filter them out, in a moment: #}
{% set forbiddenEntryTypes = [
  'mySection:entryTypeOne',
  'mySection:entryTypeTwo'
] %}

{% set entryTypes = [] %}

{% for section in sections %}
  {# Get the section's EntryTypes, filtering out any that don't match our "magic" `section:entryType` values in the "forbidden" array, above: #}
  {% set permittedSectionEntryTypes = section.getEntryTypes() | filter((t) => "#{section.handle}:#{t.handle}" not in forbiddenEntryTypes) %}

  {# Merge anything that passes the test into the main `entryTypes` array: #}
  {% set entryTypes = entryTypes | merge(permittedSectionEntryTypes) %}
{% endfor %}

{% set query = craft.entries({
  type: entryTypes
}) %}

This basically works by assuming you want all types from all sections, then filtering out just a few types from that list.
For example, your forbiddenEntryTypes might look something like this, if you had three other Entry Types in the Courses section…
{% set forbiddenEntryTypes = [
  'courses:night',
  'courses:takeHome'
] %}

…where cce is allowed, because it's returned by the section, but not explicitly filtered out. You'll have to update this if/when you add more Course types!

Keep in mind that the sectionHandle:entryTypeHandle format is not a Craft feature, it's just a way for us to compare Section + Entry Type handles at once (note the way we build the "#{section.handle}:#{t.handle}" string when filtering down the Entry types)!


Deploying Craft 3 site to production has working admin but 503 front end I have just deployed my first Craft 3 site to a production server with the following process:

Deploying git repo to the server
Then running the composer command to download the required files for Craft etc that were ignored from git (from https://nystudio107.com/blog/setting-up-a-craft-cms-3-project)
Set up database etc

However, when I open the site in the browser it is returning a 503 Service Unavailable error yet the admin side of the site works absolutely fine.


Search Through Matrix Field Trying to search through a Matrix field, currently I am using conditionals to accomplish this, but if it's faster would like to limit the results to only what I need.

{% for row in entry.matrixField.search(matrixChild1:"mykeyword") %}

{% set getResults = row.matrixChild1.one.slug %}

{% endfor %}

I could not find much on this, I know a search() parametered can be used in Matrix but after all my trying still nothing.  Hopefully it's possible?

Please take a look at the docs there are so many examples how to search here at stack exchange and it's well explained in the docs. I posted the same answer multiple times within the last days

entry.matrixField.search("mykeyword").all()

As I already said a few times too "looping through queries is still deprecated". Please fetch your results with .all() or .one(). There is no reason to fill your logs with deprecated warnings - you won't have any benefits


Sub-pages not loading on Windows server I just moved a site over to a Windows server, and my homepage loads fine, but sub pages are not loading. I just get an error message that the connection was reset. Adding index.php to the url doesn't do anything.

My control panel is also loading fine, it's just the sub-pages.

I don't really have any experience with Windows servers, so I'm not sure how to go about troubleshooting this. I'm using the standard craft web.config file. Is there a config setting I need to add?

Figured it out. Config was set to force https, but the new server didn't have an ssl certificate set up. Fixing that cleared up the issue.


Count users, not enough memory error Hi I have like 2000 users in my system. I would like to get a count of the active ones, so I did it like this:

{{craft.users.group('member').count()}}

but this returns the count with the limit of 100, so when I remove the limit

{{craft.users.group('member').limit(null).count()}}

I got this error

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /Users/alex/git/website/craft/app/models/BaseModel.php on line 280

Use .total() instead of .count()

.count() will query the db and fetch all fields for all items relating to your criteria and then count them.

.total() only counts the number of items your criteria returns without all of their fields.


Sprout Email: object variables will not resolve in text and redactor field since upgrade I upgraded my whole system: Craft Pro (2.9.2 to 3.5.15.1), Sprout Email Pro (2.4.7 to 4.4.8), and several others.
After fixing many errors, most occurred with Sprout Email, I now have only one major problem with Sprout Email: object variables will not resolve in text and redactor field in emails, although it has worked for several years before!
Object variables in the subject line will resolve and be shown in emails, all others are output as text: { myObjectVariable }
I changed the syntax to {{ object.myVariable }}, but it doesn't work with that either!
One interesting thing: When I click on preview in the notification email list, the {{ object.myVariable }} are replaced with the values from the last submitted form (= saved entry), the { myObjectVariable } are not.
What is going wrong?

This is expected behavior. This feature was changed due to a security vulnerability and is mentioned in the upgrade notes.
To create similar behavior you will need to confine your dynamic variables in the Control Panel to the Subject or Default Body fields. Any other custom variables will need to be added and managed in a custom email template.
We will consider adding back improved functionality in the CP workflow if we can find a way to do it safely.


Is there an accessible callback when Craft.EntryTypeSwitcher is run? When a user switches Entry Types in the control panel, Craft.EntryTypeSwitcher is trigged which in turn triggers onTypeChange.

Is there callback that a plugin can access to run some additional JavaScript after this event has occurred?

===== Edit to show Current work-around as per Justin's answer below ======

(function($) {
    Craft.LoadOverride = Garnish.Base.extend({
        init: function()
        {
            this.addListener(Garnish.$win, 'resize', 'runOverride');
        },
        runOverride: function()
        {
            console.log('Triggered!');
        }
    });
})(jQuery);

It looks like the only easy way to hook into that event is to listen for the resize event on the window object. On line 55, it is triggering resize(). Looking at the code, I am not seeing any other way to listen for these events. The Craft.EntryTypeSwitcher class is getting instantiated but not getting assigned to any variable in the DOM, so there is no way for you to grab that instance (from what I can tell anyway).