Unit Testing with current version of Craft Has Craft stopped shipping with unit tests? I'm trying to unit test a plugin and have noticed from several previous answers (e.g PHPUnit testing for Craft plugins or controllers) that it was possible at some stage using Craft's local tests. Have these since been removed? There no longer seems to be a tests folder inside the latest download of Craft/app.

What is the current best practice for setting up Unit Tests using Craft 2 in a plugin?

I found a bootstrap that works for me apart from an issue with the database connection.

I can't find the link now but here is the code:

<?php
define("CRAFT_ENVIRONMENT", "dev");
// PHP sucks at resolving symlinked directories
define('CRAFT_BASE_PATH', realpath(str_replace("plugins/myplugin", '', dirname(__FILE__))).'/');

// Define app constants
defined('CRAFT_APP_PATH')          || define('CRAFT_APP_PATH',          CRAFT_BASE_PATH.'app/');
defined('CRAFT_CONFIG_PATH')       || define('CRAFT_CONFIG_PATH',       CRAFT_BASE_PATH.'config/');
defined('CRAFT_VENDOR_PATH')       || define('CRAFT_VENDOR_PATH',       CRAFT_BASE_PATH.'app/vendor/');
defined('CRAFT_FRAMEWORK_PATH') || define('CRAFT_FRAMEWORK_PATH', CRAFT_APP_PATH.'framework/');;
defined('CRAFT_PLUGINS_PATH')      || define('CRAFT_PLUGINS_PATH',      CRAFT_BASE_PATH.'plugins/');
defined('CRAFT_STORAGE_PATH')      || define('CRAFT_STORAGE_PATH',      CRAFT_BASE_PATH.'storage/');
defined('CRAFT_TEMPLATES_PATH')    || define('CRAFT_TEMPLATES_PATH',    CRAFT_BASE_PATH.'templates/');
defined('CRAFT_TRANSLATIONS_PATH') || define('CRAFT_TRANSLATIONS_PATH', CRAFT_BASE_PATH.'translations/');
defined('YII_TRACE_LEVEL')          || define('YII_TRACE_LEVEL', 3);
// Load Yii
require_once CRAFT_APP_PATH.'framework/yiit.php';
Yii::$enableIncludePath = false;
require_once CRAFT_APP_PATH.'Craft.php';
require_once CRAFT_APP_PATH.'etc/web/WebApp.php';
require_once CRAFT_APP_PATH.'Info.php';
// Craft shits itself if there is no server or request path
$_SERVER['HTTP_HOST'] = 'dev.copywheat';
$_SERVER['REQUEST_URI'] = '/';
// Load test database
$config = require_once CRAFT_APP_PATH.'etc/config/test.php';
$config['params']['dbConfig'] = array();

// Create app instance
$app = new \Craft\WebApp($config);
// Load plugin
require_once CRAFT_PLUGINS_PATH.'myplugin/mypluginPlugin.php';

I'm still having issues with the db connection.  Have had to resort to modifying the craft/app/etc/config/defaults.db in order to override the environment file which does work for me at the moment.


Dynamic Blitz Template not loading currentUser I have a Dynamic Blitz template with a currentUser if statement within it. For some reason, it's not working and I can't figure out why.
It gets loaded in as follows:
{{ craft.blitz.getTemplate('brands/_followButton', { elementId: entry.id }) }}

The code on the page running it:
 {% if currentUser %}
  {% if craft.follow.check({ elementId: entry.id }) %}
    favoriteUnfollow
  {% else %}
    favorite_borderFollow
  {% endif %}

  {% else %}
    favorite_borderFollow
{% endif %}

Your code looks correct. Test with the following code in brands/_followButton.
{{ currentUser ? 'Logged in' : 'Logged out' }}

For some reason, it's not working and I can't figure out why.

Can you explain what specifically is not working? And provide the version of Blitz that you're using?


What does update actually do? Upon updating CraftCMS via CP > Utilities or Terminal, does this update only the vendor folder? Or does it also change structure in the DB and other files outside the vendor?

DB and eventually others files are updated in the first request after the Update.


Creating fields programmatically / with PlugIn Anyone here who can provide a working sample of how to create several fields and groups using a plugin? It don't need to be fancy, nothing new, just re use the stuff thats built-in and it need to work somehow so I can extend it from there.

Is there something to consider if I create for example some profile fields for the Users Group? Or lets say I want a job portal and have field set name "job profile" which contains fields for job description, location, skills, ...

I feel a bit ashamed asking for such a comprehensive "task" but as I am not really a developer a lot things I am doing is hoping my try & error attempts succeed (sometime). Having said that, I am a real pro in destroying any environment to the ground and finding bugs but I am also very experienced in installing and configuring servers and apps - guess why this is...

This is the reason why I need to be able to create my playground as fast as possible and no, I don't want to copy/paste files, I want to learn extending the system.

You can look at Craft's own InstallController and InstallService to see how Craft programmatically creates the default content you see during a fresh install.


How to get the previous or next two entries with images and To expand upon How to get the previous or next two entries I was wondering how I can also add the image and an input from each entry.

I have simplified the html-structure in this example but essentially what I want to add is:
entry.portfolioImage.one and row.kundenavn from row in entry.kunde.

The links and entry title is working.

{# Set parameters for prev/next elements list #}
{% set params = craft.entries.section('portfolio').order('title desc') %}

{# Get the prev/next elements #}
{% set prevEntry = entry.getPrev(params) %}
{% set nextEntry = entry.getNext(params) %}

{# And make sure to only output the links if the element exists #}
{% if prevEntry %}

{{ prevEntry.title }}

      {% for row in entry.kunde %}
          {{ row.kundenavn }}
      {% endfor %}

{% endif %}
{% if nextEntry %}

{{ nextEntry.title }}

      {% for row in entry.kunde %}
          {{ row.kundenavn }}
      {% endfor %}

{% endif %}

Bratvold, what do you mean with "input"?

Actually, if you can get the title of your prev-item, you can get everything of it.

{% if prevEntry %}

{{ prevEntry.title }}

      {% for row in prevEntry.kunde.all() %}
          {{ row.kundenavn }}
      {% endfor %}

{% endif %}


Best solution for articles tag page? I've been looking through the forum, it seems there's a few methods to retrieve a tags page but I'm looking for the most efficient method (both speed- and template-wise).

Using tag.name with tag token (routes), which utilises the original article template for rendering.
Using tag.slug
Using tag.title and search()

I think #2 is the simplest and prettiest solution, but it's relatively undocumented as far as I can tell. Also, I'm using two tag groups and, although I've mentioned to my client to not add duplicate tags (e.g. { 'group 1': 'stress' } { 'group 2': 'Stress' } I'm interested to know the following:

Is #2 the most optimised solution?
Can it be used with a tag token route?
Will duplicate tags accross tag groups cause conflicts?

In my tests using #1 (the urls are ugly in my opinion), Depression seems to return no results, perhaps because there is already a depression tag in another tag group.

Ideally, I want one place for all tags, without having to create two different routes for both tag groups.

Ok, so I'm tenatively answering my own question — although I'd like someone to shed light on the duplicate tag problem — it seems like a combination of #1 and #2 works quite nicely, although I haven't extensively tested it yet.

{% if tag is defined %}
  {% set tag = craft.tags.slug(tag).first() %}
  {% set entries = craft.entries.section('articles').relatedTo(tag) %}
{% else %}
  {% set entries = craft.entries.section('articles') %}
{% endif %}

{% for entry in entries %}
  ...
{% endfor %}

This is working with the route articles/tag/[tag token] set up. The only issue is when a tag that has a duplicate slug, whether that be within the same tag group or a different one, it returns no results (I'm not great at reading logs, but it doesn't seem to throw any errors either).

Any additions to this answer would be appreciated!


Mobile app authentication We're trying to use Craft as a CMS for a mobile app and to accomplish this we need to convert entries to JSON. We use the Element API plugin for this and it works like a charm.

The problem is that we don't want certain content to be visible to everyone, so we want to require the users to log in. Is it possible for a user to log in via the mobile app, then get some kind of sessionToken which is stored in the database and then every time the user send a request to the API it sends the token along with it to make sure the user of the mobile app is authenticated (like the token in the craft_sessions table)? If so how can this be accomplished?

Should this require us to write our own plugin with a custom authentication system or is it possible to get this working with the user logic provided by Craft?

You're basically looking for authentication/authorization for the Element API plugin, which it currently doesn't support.

  Should this require us to write our own plugin with a custom authentication system or is it possible to get this working with the user logic provided by Craft?

I'd probably approach this by forking the Element API plugin and adding authentication/authorization to it instead of adding it to a separate plugin and getting them to interact.

You'd need some way to authenticate the user and once they are authenticated you could authorize future requests either based on a custom token or using authorization headers and each could have their own expiration logic.


Plugin field-type not getting javascript variable? Updating a plugin from Craft 2 to Craft 3, so this logic used to work.

The plugin has a field-type which is a button. When clicked, the button does stuff depending upon another field. That handle(ID) of that other field must be passed to the button's javascript.

The field's class seems to register the js correctly. When the page loads, it gives a popup with the correct info.

var startDateFieldHandle = 'fields-startDate';
alert( 'startDateFieldHandle = '+startDateFieldHandle);

Above that is
<script src="http://f.dev/cpresources/6991e452/occurrencesField.js?v=1527142206"></script>
which pulls in the following:

jQuery(document).ready(function(){
    $("#fields-cal37_choose_dates_button").click( function() {
        alert('Clicked the button');
...
        var start_date = new Date( jQuery("#"+startDateFieldHandle).val() );

The alerts happen in the correct sequence, but nevertheless my guess is that because <script cpresources> bit occurs earlier on the page than where startDateFieldHandle is set, that somehow it still isn't set when the button is clicked.

I don't see, using Craft::$app->View->registerJs($js); that there is any way to say "put this first."

It did used to work under Craft 2. Any tips?

Update
At present, I'm seeing in occurrencesField.js that as soon as I reference the startDateFieldHandle variable in any way, such as alert(startDateFieldHandle); the script quits.

Also, in the Firefox console, I'm getting:

jQuery.Deferred exception: startDateFieldHandle is not defined @http://f.test/cpresources/6d6f7137/occurrencesField.js?v=1527518592:101:2
l/</e@http://f.test/cpresources/61c13c0c/jquery.js?v=1527049453:1:29794
l/</t<@http://f.test/cpresources/61c13c0c/jquery.js?v=1527049453:1:30109
 undefined  jquery.js:1:31365
Double-instantiating a menu button on an element garnish.js:1:2512

Keep in mind Craft parses the html of your field, it won't be used directly, thus the ID of your field can't be "#"+startDateFieldHandleunless you ignored Crafts methods or modified it in another way. Your fields ID will be more like

id="fields-{{the id you specified}}"

Thus you should make sure you used the correct selector like

jQuery("#fields-"+startDateFieldHandle)

usually your order should be fine.. If you want to pass certain variables to Javascript you should do that in Twig because Craft::$app->View->registerJs($js); won't pass variables in a global scope (you can't access them in your JS files in certain cases).

However you should avoid using those globals in Craft. It's better to create a custom Garnish object for the logic

Craft::$app->getView()->registerJs('new Garnish.MyAwesomeField(id, options)');

That will make sure everything is initialized and ready to use
Your js could look like this

Garnish.MyAwesomeField = Garnish.Base.extend(
    {
        $theOtherField: null,
        $field: null,
        options: {},
        init: function (fieldId, settings) {
            this.options = $.extend({}, Garnish.MyAwesomeField.defaults, settings);
            this.$theOtherField = $('#' + this.options.fieldSelector + this.options.theOtherFieldHandle);
            this.$field = $('#' + this.options.fieldSelector + fieldId);
            this.addListener(this.$field, 'click', $.proxy(this, 'clickEvent'));
        },
        clickEvent: function(){
            alert( this.$theOtherField.val() );

        }
    },
    {
        defaults:{
            fieldSelector: 'fields-',
            otherFieldHandle: ''
        }
    }
);


requireLogin not redirecting to login page I'm trying to get the following behavior, but it's not working:

If a user goes to http://example.com/addresses he should get redirected to the Craft login form. So the corresponding template has a {% requireLogin %} tag. After a successful login the user should be redirected to the page he was trying to access (in the case http://example.com/addresses)

Without any additions to the config array the redirect goes to http://example.com/login which is a 404 page.

If I add the loginPath setting to the config file and set it to 'admin/login' it redirects to the correct Craft login form, but after login it's not going to /addresses but /admin/addresses

What settings would lead to the desired behavior?

You should create your /login template with a custom login form. If you do that, the redirect should work as expected:

  craft.session.returnUrl is set to the original URL that included the {% requireLogin %} tag that initiated the redirect to this login form.


Getting PHP error when trying to bind to onEditionChange event I'm trying to bind to the onEditionChange event with something like this:

craft()->on('onEditionChange', function (Event $event) {
    craft()->womb->sendEvent($event);
});

However, i'm getting this error:

Undefined offset: 1

Seems it expects an dot in there. Is there another way to bind to this event?

Because that particular event is raised from AppBehavior (and therefore WebApp), it doesn't follow the same component.eventName syntax that the other Craft events use and you can't use craft->on() to register to listen to it.

You can register to listen to it through the traditional Yii method, though.  This should work for you:

craft()->onEditionChange = function (Event $event) {
    craft()->womb->sendEvent($event);
};


Custom Module: new element type index page access Hello I've created a new element type in a module (a web app within Craft) that I'm developing.
I have initially created my models to be craft\db\ActiveRecord and save directly to their tables, but that is not the "craft" way.
I decided to extend from craft\base\Element and do the needful in order to leverage elementindex.html (search, sort, filter), but going to /admin/custom/index gives a "404 Not Found". Do I need to create my own CustomController.php ?
I did the configuration needed in my Module class, as well as creating the Custom.php and CustomQuery.php according to the documentation here
   // CP Routes
    Event::on(
        UrlManager::class,
        UrlManager::EVENT_REGISTER_CP_URL_RULES,
        function (RegisterUrlRulesEvent $event) {
            $event->rules['custom'] = '/app-module/custom/index';
        }
    );

    // CP Nav Items
    Event::on(
        Cp::class,
        Cp::EVENT_REGISTER_CP_NAV_ITEMS,
        function (RegisterCpNavItemsEvent $event) {
            $event->navItems[] = [
                'url' => 'custom',
                'label' => 'Custom',
                'icon' => 'share',
            ];
        }
    );

    // Custom Element Types
    Event::on(Elements::class,
        Elements::EVENT_REGISTER_ELEMENT_TYPES,
        function(RegisterComponentTypesEvent $event) {
            $event->types[] = Custom::class;
        }
    );

Since this is a module rather than a plugin, you need to register control panel template roots, like so:
  Event::on(
    View::class,
    View::EVENT_REGISTER_CP_TEMPLATE_ROOTS,
    function (RegisterTemplateRootsEvent $e) {
        $e->roots['app-module'] = __DIR__ . '/templates/cp';
    }
  );

(I've swapped 'custom' for 'app-module' as I think it's a little more clear, but feel free to keep it as 'custom') If your module's src folder does not have a templates/cp directory, create one and add an index.twig to it. Now going to admin/app-module should load the index file you've created. (You'll need to update the EVENT_REGISTER_CP_NAV_ITEMS from 'custom' to 'app-module' to have the CP url match the change I've made here.) However, since you're working with an element, it's probably better to create another directory named after your element. Let's use "Item" as a generic element class name, you might have templates/cp/items/index.twig who's contents might look like this:
{% extends elementType is defined ? '_layouts/elementindex' : '_layouts/cp' %}

{% set elementType = 'modules\\appmodule\\elements\\ItemElement' %}
{% set title = 'My Custom Items'|t('app-module') %}

{% block actionButton %}

       {{ "New custom item"|t('app-module')}}

{% endblock %}

You don't need to register this URL via the EVENT_REGISTER_CP_URL_RULES, but you will want to for the element edit and creation page. e.g.
Event::on(
    UrlManager::class,
    UrlManager::EVENT_REGISTER_CP_URL_RULES,
    function (RegisterUrlRulesEvent $event) {
        $event->rules['app-module/items/new'] = 'app-module/items/edit-item';
        $event->rules['app-module/items/<itemId:\d+>' => 'app-module/items/edit-item']
    }
);

Here, the key is the URL you will hit in the control panel and the value is the action URL you'll need to create in your module. src/controllers/ItemsController.php, which will have an actionEditItem function.
Hope this helps!


Smart Map - pass options into markers individually I have successfully got Smart Map up & running and now want to tailor it for my own purposes.

I will have hundreds of markers for businesses, which I am categorising via fields in Craft. What I would love to do is pass custom options to the markers on an individual basis. E.g.:

Marker 1 - Business-type: Retail; Market-sector: Domestic, Educational; Products: Chair, Table, Upholstery.

Marker 2 - Business-type: Manufacturer; Market-sector: Office, Children; Products: Soft seating, Divider panels.

As you see there are 3 keys with multiple values in each.

-

I know I can pass custom options into the map using the API, but can it be done manually in conjunction with the plugin?

It is possible to pass information per-marker for the info Bubble, but I don't see anything in the documentation about custom option data.

OK So without being too verbose about this, here is how I managed to attach information to each marker.

It is important to remember that each marker has an ID which corresponds to the entry / user that it is derived from. Using javascript, I created a small function that when given a numeric ID, it creates the smartmap ID by prepending adn appending characters to the ID. So 43 would return smartmap-mapcanvas-1.43.memberAddress in my case.
Using that function as a building block, I was able to create a craft loop on my template that queries the user, gets their id, gets the relevant information, and then using javascript to apply it to the matching id marker.
Google markers are just javascript objects. So you can add anything to them.... Strings, Arrays, Objects, whatever... Then use javascript to query that extra

If anyone needs help, hit me up and I'll happily share my code.


Do servers in a multi-server Craft setup need to share a storage directory? We have a multi-server Craft setup, for redundancy and scaling.

We've been using an shared NFS drive to to host Craft's storage directory, by setting CRAFT_STORAGE_PATH to a mounted /craft-data/ directory on each machine.

in public/index.php:

define('CRAFT_STORAGE_PATH', '/craft-data/');

One reason it was important to do this was to let Craft share sessions across machines, because they were stored on the filesystem.

Recently, we transitioned from saving our sessions in the filesystem to saving them in redis.

If we can, I'd like to abandon the NFS drive altogether and let each machine have its own /craft-data/ directory, since we no longer need to maintain this shared filesystem for session sharing.

Looking at the contents of the craft /runtime/ directory, we see several subdirectories that seem like they could contain important shared state, such as cache and state:

$ pwd && ls -l
/craft-data/runtime
total 16
drwxrwxr-x   5 craft_user users   45 Jan 19 16:12 assets
drwxrwxr-x   2 craft_user users   49 Jan 26 09:34 cache
drwxrwxr-x 100 craft_user users 4096 Jan 26 09:37 compiled_templates
drwxr-xr-x   2 craft_user users   65 Jan 25 12:12 CSS
drwxr-xr-x   2 craft_user users   65 Jan 25 12:00 HTML
drwxrwxr-x   2 craft_user users 4096 Jan 19 16:12 logs
drwxrwxr-x   2 craft_user users 4096 Jan 26 08:52 sessions
drwxrwxr-x   2 craft_user users   22 Nov 16 14:55 state
drwxrwxr-x   2 craft_user users 4096 Jan 25 15:17 temp
drwxr-xr-x   2 craft_user users   65 Dec 14 11:27 URI

Do any of these directories need to be shared, in a multi-server setup? Is it safe for us to stop using a networked filesystem, for the CRAFT_STORAGE_PATH?

Personally, I'd lean towards keeping the shared NFS volume.  It's not just the runtime folder, but things like backups, site logos, user avatars, etc. and anything else plugins might be storing in storage that you'd want to consider as well.


Setting plugin subnav to active I have written a plugin that works fine, it has a subnav which i build like so:

public function getCpNavItem()
  {
    $item = parent::getCpNavItem();
    $item['subnav'] = [
      'moderateentries' => ['label' => 'Moderate Entries', 'url' => 'entry-moderator'],
      'notificationmessages' => ['label' => 'Messages', 'url' => 'entry-moderator/notificationmessages'],
      'log' => ['label' => 'Log', 'url' => 'entry-moderator/log'],
    ];
    return $item;
  }

Problem i am having is that i cannot get the left hand nav item to display as active, the only one that has the sel class applied is 'moderateentries', if i click on the second or third nav item, the first one is the one that is displayed as active with the sel css class. where am i going wrong?

Based on the docs and this guide, looks like you'll want to set selectedSubnavItem in your Twig template(s) to whatever the key of your selected subnav item is (moderateentries, notificationmessages, etc).

Alternatively, you could do somthing similar to what Andrew Welch does in his SEOmatic plugin; he sets the selectedSubnavItem variable as a keyed item in a variables array and then right after that renders his plugin template (including that variables array in the render method).


Craft CMS combining element-api and Simple Map plugin I've been using element-api for simple lazy-loading of things like blog posts, etc and now want to create a simple 'Postcode lookup' by utilising a custom filter (provided by the Simple Map plugin).

Simple Map provides a custom field that uses the Google Maps API for address data and geolocation.

My question is this: How would I call the custom Simple Map filter using the 'criteria' settings in element-api so that I can pass through a postcode (via a javascript call to the api/customer-search.json?postcode=M25ND) and return results based on the custom field filter?

I'm using Craft 3:

"require": {
    "php": ">=7.0.0",
    "craftcms/cms": "^3.0.0-RC1",
    "vlucas/phpdotenv": "^2.4.0",
    "roave/security-advisories": "dev-master",
    "craftcms/redactor": "1.0.0.1",
    "craftcms/element-api": "^2.5",
    "ether/simplemap": "^3.1"
},

element-api
https://github.com/craftcms/element-api

simple map https://github.com/ethercreative/simplemap

The below will return entries that are within 100 miles of the location provided:

{% set entries = craft.entries.map({
    location: 'M2 5ND',
    country: 'GB',
    radius: 100,
    unit: 'mi'
}).orderBy('distance').all() %}

.. and the simple look up in my element-api.php file currently returns all 'customer' entries:

<?php

namespace Craft;

use craft\elements\Entry;

// https://github.com/ethercreative/simplemap
// https://github.com/craftcms/element-api

return [
    'defaults' => [
        'elementType' => Entry::class,
        'elementsPerPage' => 9,
        'pageParam' => 'pg',
        'resourceKey' => 'entries',
        'transformer' => function(Entry $entry) {
            return [
                'id' => $entry->id,
                'title' => $entry->title,
                'url' => $entry->url,
                'map' => $entry->map,
            ];
        },
    ],
    'endpoints' => [
        'api/customer-search.json' => [
            'criteria' => [
                'section' => 'customers',
                'type' => 'customer',
                // how to include map({}) filtering of results?
            ],
        ],
    ]
];

What I'm struggling to do is combine the two. As far as I can tell; element-api criteria only works with the parameters listed here: https://craftcms.com/docs/templating/craft.entries

Thanks in advance for any direction you can provide.

It should work the same way because the modifyElementsQuery should be fired no matter where the ElementQuery object is build so you could insert the same array in your element-api endpoint as you insert in your CraftVariables entries function to  build the EntryQuery since they both use Yiis configure function to build the

'endpoints' => [
    'api/customer-search.json' => [
        'criteria' => [
            'map' => [
                'location' => 'M2 5ND',
                'country' => 'GB',
                'radius' =>  100,
                'unit' => 'mi'
            ],
            'orderBy' => 'distance'
        ],
    ],
]

should theoretically build the same object like

{% set entries = craft.entries.map({
    location: 'M2 5ND',
    country: 'GB',
    radius: 100,
    unit: 'mi'
}).orderBy('distance') %}


Can you delete rows from a table field on the front end? I have a table field which is attached to users, before I go ahead and create the logic for adding rows on the front end via ajax, I need to know if I am going to be able to easily add the facility to delete specific rows (ideally without saving all the data again each time)?

It may well be that depending on complexity that this is more suited to a matrix field or custom database tables via a plugin.

Either way can it be done?

It can be done, but only by saving the table field's data completely. There's no controller method to remove a table's row(s). Same would apply for a table within a Matrix block, or for a Matrix block itself. All content gets saved by calling the entries controller (saveEntry) with the new content in a POST.

What you could do: Let your users create one or more matrix blocks, create your own plugin, create a controller with an actionDeleteMatrixBlockById() method (for instance) and let that controller call craft()->matrix->deleteBlockById() if one of the blocks has to be deleted.

Bit of a hassle, though. Don't know your use-case, but seems a bit overkill.


How would I list assets from a specific subfolder? In the case that I have an asset folder structure like this:

/images
   /group-a
   /group-b
   /group-c

How would I loop through all of the images in /group-b without having to assign them to entries.

You could use the folderId-parameter in craft.assets:

{% for image in craft.assets.folderId(1) %}

{% endfor %}

You can get the folderId by inspecting the folder-name in the CP.


How to perform a redirect and pass on data? I have a Controller Action which should pass on some data from one (CP) template to another:

Template 1 (plugin/form): Form sends data
Controller redirects data to Template 2
Template 2 (plugin/): Shows data

I don't want to just load the template but redirect to plugin/ and show the data, any ideas?

public function actionTemplate()
{
    $data = 'Something';
    $this->redirect( 'plugin_handle' ); //Pass on $data to 'plugin/'
}

Edit 1:

Or is there a way to send a post-request to a "route" rather then an actionController?

I would suggest either using query parameters in your redirect method, or storing it in session variables (which should be no different than regular PHP).


Add product to cart using checkbox and AJAX As part of my checkout process, I have a list of five optional, add-on services shown as checkbox inputs. The list resides on a standalone page and each service can either be included in the purchase or not (i.e. quantity 1 or 0).

I've considered using the Multi-Add plugin, but I would like any checked services to remain in the cart if the user goes "Back" instead of submitting.

How do I add these services to the cart with AJAX when clicked, and remove when unclicked? I'm very new to Commerce so any and all references, suggestions, and hand-holding here is appreciated.

You are going down the right path with MultiAdd.

I have an application that on a single product page, I am also selling additional products that are an add-on of the parent. They are separate products, but they exist together. As in, the parent product is the only place the additional products are sold.

When the administrator is creating the parent product, I give them the option (mainly checkboxes) "Include option B". When checked, the additional product will show.

This is kind-of how it works...

<form method="post" action="" accept-charset="utf-8">
    <input type="hidden" name="action" value="multiAdd/multiAdd">
    <input type="hidden" name="redirect" value="shop/cart">
    {{ getCsrfInput() }}

...

{# This is the primary/parent product. #}
<select id="myVariant" name="items[1][purchasableId]" class="form-control required" required>
    <option value="">Select Option</option>
   {% for purchasable in product.variants %}
       <option {% if purchasable.stock <= 0 and purchasable.unlimitedStock == false %}disabled {% endif %}
           value="{{ purchasable.purchasableId }}"
           data-position="{{ purchasable.myVariant }}"
           data-price="{% if purchasable.price == 0 %}Free{% else %}
          {{ purchasable.price|currency(cart.currency) }}{% endif %}">
              {{ purchasable.myVariant.label }} &ndash; {% if purchasable.price == 0 %}Free{% else %}{{ purchasable.price|currency(cart.currency) }}{% endif %}
        </option>
    {% endfor %}
</select>
<input type="hidden" name="items[1][qty]" value="1">

...

{# This is the additional product. #}
{% if product.myOption|length %}
<fieldset>
    {% set options = product.myFirstOption %}
    {% if options|length %}
        <legend>Some Optional Thing</legend>

            Option
            <select id="option" name="items[2][purchasableId]" class="form-control required">
                <option value="">Select Option</option>
                    {% for option in options %}
                        <option value="{{ option.defaultVariant.purchasableId }}" data-price="{{ option.defaultVariant.price|currency(cart.currency) }}">
                            {{ option.title }} &ndash; {% if option.defaultVariant.price == 0 %} Included with registration {% else %} {{ option.defaultVariant.price|currency(cart.currency) }}{% endif %}
                        </option>
                   {% endfor %}
            </select>
            <input type="hidden" id="optionQty" name="items[2][qty]" value="0">

    {% endif %}
</fieldset>
{% endif %}

What is important to note is this:

<input type="hidden" name="items[1][qty]" value="1">
...
<input type="hidden" name="items[2][qty]" value="0">

That's how MultiAdd knows what to add to the cart.

Once I choose my options and click "buy" any option(s) are added to my cart until I go into my cart and remove them.

There is an example for ajax in on the MultiAdd page. Instead of a traditional post, you are sending the same information to a url via ajax.

Hope this helps!


Booking paid-for events: How? First off, I appreciate this is open ended and SE isn't the best place for it. For a lack of a better place to ask though, here we are:

I've just built a system for a client where users could sign up an book places on events. None of the bookings on this system require payment, so it's just a case of submitting a form which creates an entry in a channel called "bookings" using:

action=saveEntry

I now have a client who needs to take bookings for events which require payment, and to list those bookings in a front end page. I'm trying to use Charge for payments (only because it seems easy to use, and my client doesn't want to pay monthly for something), which requires the form action:

action=charge/charge

The issue I have is that I want to submit a single form which takes a payment and creates a booking (action=saveEntry) without writing a plugin. I'm not a backend developer, I don't know how to write plugins (despite trying). None of the eCommerce addons available for Craft are out of beta yet.

Is there any way to take a payment and create a booking using template code?

We did something similar with Charge when we were accepting payments for a Craft Virtual Training course. It does involve writing a plugin, but thankfully all of the heavy lifting for saving entries and charging credit cards is handled by the services provided by Craft and Charge, so the only thing the plugin has to worry about is collecting post data and passing it to some APIs.

Start off by creating a primary plugin class (see Setting Things Up in the Craft plugin docs), and then giving your plugin a controller with the following code:

<?php
namespace Craft;

class FooController extends BaseController
{
    public $allowAnonymous = array('actionCreateBooking');

    public function actionCreateBooking()
    {
        // Require this to be a POST request
        $this->requirePostRequest();

        // Require that the user is logged in
        craft()->userSession->requireLogin();

        // Get the user
        $user = craft()->userSession->getUser();

        // Do any custom validation here
        // ...

        // Charge the card
        $charge = new ChargeModel();

        $charge->cardToken     = craft()->request->getRequiredPost('cardToken');
        $charge->cardLast4     = craft()->request->getPost('cardLast4');
        $charge->cardType      = craft()->request->getPost('cardType');
        $charge->cardName      = craft()->request->getPost('cardName');
        $charge->cardExpMonth  = craft()->request->getPost('cardExpMonth');
        $charge->cardExpYear   = craft()->request->getPost('cardExpYear');

        $charge->customerName  = craft()->request->getPost('customerName');
        $charge->customerEmail = craft()->request->getPost('customerEmail');

        $charge->planAmount    = 10000; // In cents

        if ($charge->validate() && craft()->charge->handlePayment($charge))
        {
            // Save the booking entry
            $entry = new EntryModel();
            $entry->sectionId = craft()->request->getPost('sectionId');
            $entry->typeId    = craft()->request->getPost('typeId'); // Not necessary if there's only one entry type
            $entry->authorId  = $user->id;
            $entry->enabled   = true;

            $entry->getContent()->title = 'New Booking: '.$user->username;
            $entry->getContent()->chargeId = $charge->id; // Custom number field
            $entry->getContent()->someOtherCustomField = 'xyz';

            craft()->entries->saveEntry($entry);

            // Redirect the request based on the 'redirect' param
            craft()->userSession->setNotice('Booking saved.');
            $this->redirectToPostedUrl();
        }
        else
        {
            craft()->userSession->setError('Couldn’t charge the card.');

            // Send the charge model to the template
            craft()->urlManager->setRouteVariables(array(
                'charge' => $charge
            ));
        }
    }
}

You might want to hard-code the sectionId and typeId rather than leaving it up to the POST data, and there might be other slight changes you want to make, but that’s the gist.

With the plugin in place, you would just need to create a form that captures all of the info the controller is looking for, and posts it to the foo/createBooking action:

<form method="post" accept-charset="UTF-8">
    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="foo/createBooking">

    <!-- ... -->
</form>


Hook calls within a loop always get the Entry, not the loop context? According to the docs, if I call a hook from within my template:

  The $context argument will be the current Twig context array, which you’re free to manipulate.

So I have a loop in my template where summary is a model I've created:

{% for summary in bookings %}
    {% hook 'microsGetBooking' %}
{% endfor %}

If I understand correctly, the $context variable passed to my hook should be a reference to that summary object. However it always appears to be an EntryModel (in this case the template I'm calling this on has a URL that matches an entry in the CMS).

What am I missing here?

If I understand correctly, the $context variable passed to my hook should be a reference to that summary object.

Nope... $context is a reference to the entire Twig rendering context of which your bookings objects will be a part of.

You can use xDebug or var_dump to see where your bookings objects are stored and loop though them as necessary from PHP.


Feed me plugin, cURL error 60 This is my first time using the Feed Me import plugin (craft 3.5.15.1). I'm pointing the feed URL to a file in the  public root, e.g. http://dev.mydomain.com/import.csv. When executing the import, I get this error:

Unable to reach http://dev.mydomain.com/import.csv. Message: cURL
error 60: SSL certificate problem: unable to get local issuer
certificate

I don't understand why. The site is a subdomain of a site that uses an SSL, but the dev subdomain does not and I've not had any other issues with craft or the browser expecting an SSL.
Any ideas why or more information I can provide?

I would try adding the following to your feed-me.php config file:
<?php

return [
    '*' => [
        'clientOptions' => [
            'verify' => false,
        ],
    ],
];

c.f. https://github.com/craftcms/feed-me/issues/479
As noted here, this is generally not a very secure practice but should be fine if you're just wanting to import a local file.


Find entries with at least 1 image How can I find entries with at least 1 image?

{% set category = craft.categories.group('storyCategories').slug(catSlug).first() %}

{% set params = {
  section : 'stories',
  order : 'postDate desc',
  relatedTo: category,
  limit : '4'
} %}
{% set stories = craft.entries(params).find() %}

I have this, but am not sure how to only get stories entries when the story has an image.

I've just realised I can do this:

{% set params = { section : 'stories', order : 'postDate desc', relatedTo: category, limit : '4', storyImage: ':notempty:' } %}

Where "storyImage" is the field name of my image field


How can I merge entry values to create a select dropdown For the life of me I can't seem to find an answer for this question, so here goes!

I have a field called jobCounty in a section called jobs.

The jobCounty is selected in the CP from a drop down list I've created of english county names.

There are about 30 entries each with most with different county names, but some repeat the same county name.

I want to create a form select, with options that are populated by the jobCounty names that have been selected, but avoid repeats. Ideally in alphabetical order.

This question seems to feel like it might be answering the same question but for for a different purpose.

Related entries from a dynamic dropdown

Any help much appreciated

Stephen

You are using a dropdown field, so you can't query for the counties in use directly from the database. (This would be possible if you had chosen to use categories or tags for them.)

But what you can do is to loop all your jobs entries to collect their respective counties.

{% set entries = craft.entries.section('jobs').limit(null) %}

{% set counties = [] %}
{% for entry in entries %}
    {% set counties = counties|merge({
        (entry.county.value): entry.county.value
    }) %}
{% endfor %}

{% set counties = counties|sort %}

Saving the county name also to the key of each array element makes sure you won't end up getting duplicates. When done use the sort filter to sort the list alphabetically.

Update: there seems to be a problem with getting the dropdown value with entry.county (the shortcut to get the value / calls the field models __toString method) when setting an array key, changing it to entry.county.value works.


Calendar month view error I recently installed Calendar (latest 1.9.3) on my Craft install (latest 2.6.2997)

Within the demo templates provided by Calendar (/calendar_demo/), the month view, I am getting the following error:

Fatal error: Call to a member function getTimestamp() on boolean in ../craft/app/helpers/DbHelper.php on line 597

I have seem to narrow it down to the targetDate variable being set to "today"

{% set targetDate = "today" %}

However, if i go to the following url: /calendar_demo/month/2017/11 the month view works but the test event I have doesn't show.

What am I missing here?

I am on a Pro license and only plugins I have installed are:

Contact Form (P&T)
Focus Point
Instagram Feed (currently disabled)
Minimee
Polls (currently disabled)
Relabel

Again now sure what I am missing here any point in the right direction is welcomed.

UPDATED RESPONSE...

We were finally able to get to the bottom of this and traced the issue down to your site likely having a DateTime field with the handle date, and this was apparently something Craft was choking on.

The next version of Calendar will fix this, but you can resolve this by renaming that DateTime field or patch this by looking for this code in calendar/services/Calendar_EventsService.php around line 100:

public function getEventList($criteria = null)
{
    if (!isset($criteria['allowedCalendarsOnly'])) {
        $criteria['allowedCalendarsOnly'] = false;
    }

And right after that, add this:

    if (isset($criteria['date'])) {
        unset($criteria['date']);
    }

Let me know if this resolves the issue for you. :)


Query entries from two sections, check date field for one section and include pagination I'm having a page that shows entries from two sections: news and events.

The page has the following layout:

All current event entries

Current event entry 1
Current event entry 2

All upcoming event entries

Upcoming event entry 1
Upcoming event entry 2

All news entries and all past event entries combined including pagination

News entry 1
Past event entry 2
Past event entry 3
News entry 4
News entry 5
Past event entry 6

To query the current events i use the following query

{% set entries = craft.entries()
  .section('events')
  .startDate('<= ' ~ now|date('c'))
  .endDate('>= ' ~ now|date('c'))
  .orderBy('startDate asc')
  .all()
%}

To query the upcoming events i use the following query

{% set entries = craft.entries()
  .section('events')
  .startDate('> ' ~ now|date('c'))
  .orderBy('startDate asc')
  .all()
%}

I have no idea how to query all the news and past event entries.

The query should include:

Query all entries from section news
Query all events from section events where entry.endDate < now
Sort all entries by post date
Limit to 9 and include pagination

I came this far with the query:

{% paginate craft.entries()
  .section(['events','news'])
  .limit(9)
  .orderBy('postDate asc')
  as pageInfo, pageEntries
%}

What is missing: checking the event entries to see if the endDate < now.

I can't just add .endDate('< ' ~ now|date('c')) to the query. That would hide all the news entries, ofcourse.

I did search and read a lot of posts on stackexchange. Couldn't find a solution.

Anyone that could help me with this?

Found the solution.

First get entries from section events as ID's and check endDate < now:

{% set eventsIds = craft.entries()
  .section('events')
  .endDate('< ' ~ now|date('c'))
  .ids()
%}

Then get entries from section news as ID's:

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

Then merge those two together:

{% set entryIds = eventsIds|merge(newsIds) %}

Then paginate by id, set orderBy postDate and limit to 9:

{% paginate craft.entries()
  .id(entryIds)
  .orderBy('postDate desc')
  .limit(9)
  as pageInfo, pageEntries
%}


How to handle 'related' categories within import script So I'm attempting to hack together a script to import thousands of entries and hundreds of categories from a bespoke system.

I have imported all the categories (around 800 of them) successfully. The category group they've been imported into has a 'relatedCategories' field so that each category can optionally be related to other categories in the group.

I'm now looking to do a second pass to import the related categories,  this is the part I can't quite get my head round.

The story so far...

    $db = new DbConnection(***);
    $db->charset = 'utf8';

    $entries = $db->createCommand()
        ->setText('SELECT * FROM categories')
        ->queryAll();

    foreach ($entries as $entry)
    {
        $craftcat_criteria = craft()->elements->getCriteria(ElementType::Category);
        $craftcat_criteria->title = $entry['title'];
        $craftcat = $craftcat_criteria->first();

        $related_array = array_filter(explode('#',$entry['similar']));

        $relatedcats = [];

        foreach ($related_array as $related_item)
        {
            $related_criteria = craft()->elements->getCriteria(ElementType::Category);
            $related_criteria->title = $related_item;

            $related_cat = $related_criteria->first();

            $relatedcats[] = $related_cat->id;
        }
    }

So I have the ElementCriteriaModel for the category and an array containing the ID of each of the related categories.

I'm going on the basis that it's craft()->relations->saveRelations() I need to use, but I just can't get the syntax right.

Can someone give me a shove in the right direction?

Sorted. This seems to work...

$catFieldRecord = FieldRecord::model()->findById(4);
$catFieldModel = FieldModel::populateModel($catFieldRecord);

craft()->relations->saveRelations($catFieldModel, $craftcat, $relatedcats);


Pulling images from other sections with image transform I would like to display images from various sections, in this case the home page. I am still coming to terms with Twig/Craft's criteria models and the transform makes it confusing.

{% for image in craft.entries.section('news').limit(1) %}
    {% set transformedImage = craft.imager.transformImage(image, { width: 1420, height: 550, position: image.focalPoint }) %}

    <img src="{{ image.bannerImage.first().getUrl }}" alt="" />
{% endfor %}`

Where do you then define the image? (bannerImage is a asset field)

Try the following, it should do what you are after.

{#
  Generate array of entries comprised of first
  entry from each section
#}
{% set entries = [
  craft.entries.section('news').first(),
  craft.entries.section('other_section').first(),
  craft.entries.section('yet_another_section').first()
] %}

{# Loop through entry array #}
{% for entry in entries %}
  {% if entry.bannerSlider|length %}
    {% set image = entry.bannerSlider.first() %}
    {% set transformedImage = craft.imager.transformImage(image, { width: 1420, height: 550, position: image.focalPoint,  effects: { grayscale: true, gamma: 1.5}, }) %}

  {% endif %}
{% endfor %}


Select Transform Feature with Asset Field If you insert an image in a Rich Text field you get an option to apply a transform, but that is not true when using an Asset field to choose an image, which seems strange. Is there any way to accomplish that?

Asset fields require a transform to be set manually in the template or can be Created in the dashboard and the transform name used in the template.

{% set thumb = {
    mode: 'crop',
    width: 100,
    height: 100,
    quality: 75,
    position: 'top-center'
} %}

<img src="{{ asset.getUrl(thumb) }}">


How to grab the full URL in a plugin? When writing a plugin is there a Craft request to grab the entire url? I want to grab everything including http://domain.com/uri preferably without writing my own function.

I see getUrl but craft()->request->url only returns 'uri' of http://domain.com/uri.

You're probably looking for something like:

craft()->request->getHostInfo().craft()->request->getRequestUri();

Which will return the host name with the schema it's using (http:// or https://) as well as any URI after the host including any querystring parameters.


RegisterUrlRulesEvent rule to display site page Within a plugin I have written I would like to route POST requests to the plugin controller and GET requests to a single entity created in craft.

When the user POSTs to http://example.com/start-your-quote it should be handled by the plugin controller. When the user GETs http://example.com/start-your-quote it should load the single entity with the slug 'site-start-your-quote'

I have written the following in the hopes of achieving this:-

 Event::on(
            UrlManager::class,
            UrlManager::EVENT_REGISTER_SITE_URL_RULES,
            static function (RegisterUrlRulesEvent $event) {
                $event->rules['POST start-your-quote'] = 'craft-lp-api-helper/lpapi';
                $event->rules['GET start-your-quote'] = 'site-start-your-quote';
            }
        );

The POST is working as expected, however the GET results in the following error:-

  Blockquote
  HTTP 404 – Not Found – yii\web\NotFoundHttpException
  Page not found.
  ↵
  Caused by: Invalid Route – yii\base\InvalidRouteException
  Unable to resolve the request "site-start-your-quote".

Hopefully I am missing something simple and someone can point me in the righ direction?

Well I have managed to achieve what I needed, I'm not sure if this is a very good way to do it though.

I've done it by getting the element of the page I needed and passing that to the route along with the template to use.

It all seems to work though.

Event::on(
        UrlManager::class,
        UrlManager::EVENT_REGISTER_SITE_URL_RULES,
        static function (RegisterUrlRulesEvent $event) {
            $element = Craft::$app->getElements()->getElementByUri(
                'site-start-your-quote',
                Craft::$app->getSites()->getCurrentSite()->id,
                true);
            $event->rules['POST start-your-quote'] = 'craft-lp-api-helper/lpapi';
            $event->rules['GET start-your-quote'] = [
                'template' => '_layouts/forms/start-your-quote.twig',
                'variables' => [
                    'entry' => $element,
                ],
            ];
        }
    );


How can I set live preview's minimum interval? New Relic reporting seems to show a strong correlation between the previewEntry method's transactions and a couple of recent apdex-based alerts (the periods indicated by the pink background):

(The third period of previewing shown near the end of the chart is my failed attempt to trigger an alert; perhaps my test entry was less intensive than the client's.)

To cut down on the number of previews generated, how can I set a minimum interval between preview update POSTs, such as five seconds?

Update: we think it likely that the alert from New Relic was not actually a problem in terms of performance, and that it's simply a case of all/most of the requests during those periods taking longer than usual. It's a relatively quiet site, and so the live preview POST requests can easily represent 100% of requests. So I'm not so worried about this now, but it might still be nice to be able to set a minimum interval between live updates when the live update feature has been activated.

If you're referring to the time it takes between you click the "Live Preview" button and the time the actual Live Preview pane shows up, then that's currently not configurable in Craft.

Might be worth adding to the feedback site, though.


How do I upload an asset in Craft 3, via PHP I've found various answers on how to upload assets via PHP for Craft 2, however nothing for Craft 3.

All answers use the method:

craft()->assets->insertFileByLocalPath()

E.g: How to upload an asset from a front-end form

I was wondering what the equivalent is for Craft 3.

Thanks

I finally figured out how to upload an asset, however I still don't understand why the insertFileByLocalPath has been removed.

/**
 * @param UploadedFile $uploadedFile
 * @param int $folderId
 * @return Asset
 * @throws BadRequestHttpException
 * @throws UploadFailedException
 */
protected static function uploadNewAsset(UploadedFile $uploadedFile, $folderId) {

    if (empty($folderId)) {
        throw new BadRequestHttpException('No target destination provided for uploading');
    }

    if ($uploadedFile === null) {
        throw new BadRequestHttpException('No file was uploaded');
    }

    $assets = Craft::$app->getAssets();

    if ($uploadedFile->getHasError()) {
        throw new UploadFailedException($uploadedFile->error);
    }

    // Move the uploaded file to the temp folder
    if (($tempPath = $uploadedFile->saveAsTempFile()) === false) {
        throw new UploadFailedException(UPLOAD_ERR_CANT_WRITE);
    }

    if (empty($folderId)) {
        throw new BadRequestHttpException('The target destination provided for uploading is not valid');
    }

    $folder = $assets->findFolder(['id' => $folderId]);

    if (!$folder) {
        throw new BadRequestHttpException('The target folder provided for uploading is not valid');
    }

    // Check the permissions to upload in the resolved folder.
    $filename = Assets::prepareAssetName($uploadedFile->name);

    $asset = new Asset();
    $asset->tempFilePath = $tempPath;
    $asset->filename = $filename;
    $asset->newFolderId = $folder->id;
    $asset->volumeId = $folder->volumeId;
    $asset->avoidFilenameConflicts = true;
    $asset->setScenario(Asset::SCENARIO_CREATE);

    $result = Craft::$app->getElements()->saveElement($asset);

    return $asset;
}

and then in my controller I can simply use

$service->uploadImage(UploadedFile::getInstanceByName('photo'), 1);


How to handle multiple users simultaneously editing an entry? If 2 or more people are editing a entry at the same time in Craft, the last person to save the entry overrides everyone else's work. It doesn't seem any diffing is involved to resolve this. I'm using matrix fields mostly.

WordPress handles it by locking a post while someone is editing. This is done by sending an AJAX request to the server when auto saving, updating the "last edited" date and which user is editing.

Does anyone know if a plugin exists already for Craft that implements this post locking feature? Or is there something already in Craft to avoid these conflicts?

There currently isn't a native way to do this and I'm not aware of a plugin that's has the functionality.

You can semi-work around it with entry revisions, but if you think this should be a core feature you can vote for it!


Maximum execution time exceeded when adding or updating plugins from the Control Panel When I try to install or update a plugin from the Control Panel, I see a message "Updating Composer Dependencies" and then the following error occurs. I'm running Craft on a local environment. Installing and updating from the command line works fine.

Status: Internal Server Error

Response:
PHP Fatal Error &#039;yii\base\ErrorException&#039; with message &#039;Maximum execution time of 30 seconds exceeded&#039;
in ...\vendor\composer\composer\src\Composer\Util\RemoteFilesystem.php:555

Stack trace:
#0 [internal function]: yii\base\ErrorHandler-&gt;handleFatalError()
#1 {main}

You need to figure out where your PHP config file is located (php.ini) and increase the value for the max_execution_time setting.

The best way to locate your php.ini:

If you can run php from the command line, run php --ini. Alternatively you could use php -i | grep 'Configuration File' to search the full phpinfo(). More info...
If php isn't accessible via the CLI, you can also create a simple php file that contains the following snippet. Load it up in your browser and search the page for "Configuration File". This will give you the location of your php.ini.

<?php

phpinfo();

Other options:

If you're using a Laravel Valet / Homebrew setup, the php.ini file is located at /usr/local/etc/php/X.X/php.ini where X.X is your PHP version number.
If you're running MAMP Pro, I believe they provide a built in editor to customize this - see How do you change php.ini values in MAMP Pro?


1 click auto login for user accounts Looking to setup a public facing, launchpad page that lists out all of the user accounts with a 1 click login.

I realize the security implications of such a page but we need something like this to rapidly test multiple accounts different sections of the site.

What's the best way to create something like this?

As long as you're already aware of the security implications. :)

Write a simple plugin that exposes a controller action where you pass in the user's ID to it.

All that action does is call:

craft()->userSession->loginUserById($userId);


Setting up redirects via Craft routes? I'm trying to set up redirects for some pages (specifically pages in structure sections that are there purely for structure, which I want to redirect to their first child). Craft's  "routes" seem to be geared towards routing a request to a template. But how can I simply route a request to a different URL?

I know I can use Twig's {% redirect %} function, but to be honest I'd much rather just set up all the routes I want redirected outside my template (in order to allow me to avoid (hopefully) unnecessary conditional URL-handling logic in the template). Is this possible at all? If not, is there an elegant way of handling it inside a template?

If you want to "redirect" all parent entries of your structure that have child entries, you could do something like this in your navigation (untested code!):

    {% nav page in pages %}

            {% if page.getChildren()|length %}
                {{ page.title }}
            {% else %}
                {{ page.title }}
            {% endif %}

            {% ifchildren %}

                    {% children %}

            {% endifchildren %}

    {% endnav %}

If you want to exclude specific parent entries from that behavior, you could do this like so:

{% if page.getChildren()|length and page.id('and, not 1, not 2, not 3') %}


Running custom query save result to xml file For an upcoming project one of the requests is that the client needs to be able to run a few custom queries on a specific section (queries are fixed, we know what these will be in advance) and then save the output to an xml file on the server. Bonus points if we can do this with a cron job, say daily.

I've been looking at several plugins (Sprout reports, Zenbu) but none really do what I need out of the box.

How would I go about handling this? Sorry if this question is too general, just looking for pointers on where to start. Any input appreciated!

Sounds like a pretty simple custom Craft plugin.

You'd add a controller to your plugin and an action method and allow anonymous access to it.

Your method would run any queries you need and save the results to an XML file on the server somewhere.

Then setup cron to execution the action at whatever interval you want.

0 3 * * * curl -s -o /dev/null "http://mydomain.com/actions/myplugin/myaction"


Use url segment as querystring parameter [Craft 3] I have mysite.com and I need to use a segment as if it were a querystring parameter for accomplishing this:

mysite.com/us         ->  same template as: mysite.com
mysite.com/pricing/bz ->  same template as: mysite.com/pricing
mysite.com/shops/ag   ->  same template as: mysite.com/shops
mysite.com/team/mx    ->  same template as: mysite.com/team
...etc

In my templates I need to know the value of the country code used in the first segment for using country-specific values for small details like illustrations and prices. Just as if it was a querystring parameter, but it has to be a segment.

Note: This has nothing to do with language. I also have already a multi-site configuration for two languages: mysite.com and mysite.com/es

So I would also need to support these too:

mysite.com/es/us        ->  same template as: mysite.com/es
mysite.com/es/bz/shops  ->  same template as: mysite.com/es/shops
mysite.com/es/ag/team   ->  same template as: mysite.com/es/team
... etc

What's the way to go? How do I tell Craft to render the template for mysite.com/{section_name} when requesting mysite.com/{section_name}/{country_code} So I can use country_code as a parameter from the craft.request segments? I'm struggling with this

You need to define routes for these URLs in settings in the CP or via the config/routes.php file.

return [
    'team/<regionId:[^\/]+>' => ['template' => 'team/_index'],
    'pricing/<regionId:[^\/]+>' => ['template' => 'pricing/_index'],
];

By defining the named subpattern, the template will get loaded with a regionId variable defined.
Use the is defined test or the null-coalescing operator to see if the template got loaded via your custom route or via the entry route. In the case that the template gets loaded via the custom route you also need to define the entry variable manually, null-coalescing operator again allows you to do that quite elegantly.

{% set entry = entry ?? craft.entries.section('pricing').one() %}
{% set regionId = regionId ?? 'en' %}


Nested Entries in Craft 3 In Craft 2 there used to be a Nested Entries field, but this is not available in Craft 3.

I know I can do {parent.uri}/{slug} in the Entry URI Format field, but if I want it to be appended by something static it doesn't work.

i.e. static/{parent.uri}/{slug} doesn't work because children uri sets to static/static/parent/child (edited)

I kind of found a work around, but doesn't seem like it's the correct way to be doing this.

{parent.uri ? parent.uri : 'static'}/{slug}


Sync existing local images Amazon S3 I installed "Amazon S3" in a website. Is there anyway so that I can sync existing local images to Amazon S3?

if you've already set up a volume with your local assets, you can set up another one for s3 and simply drag and drop them over in the "files" section.


How to grab all images from and section I am sure this must be answered somewhere and I cannot find it.  I need to grab all images from a section (channel) and display them in a slide show on the index page (index page is a single,so index page of the entries from that section). I just cannot seem to get this correct.  Here is what I am trying

 {% set storyItem = craft.entries.section('stories').find() %}

so stories is the section where the entries are.

Then

{% for previewImage in storyItem.slideshowImage.offset(1) %}
 ...
{% for previewImage in storyItem.slideshowImage.limit(1) %}
...
{% for previewImage in storyItem.slideshowImage.offset(1) %}

The limit and offset are just to make the slider I have work and that does work on another page so that does not seem to be what is stopping me here.  I am somewhat new to craft and my gut is telling me this is a simple answer but I have been stuck on it for a while now and coming back to it off and on to no avail.

I need to grab all images from a section and display them in a slide show on the index page.

You'll need to:

Loop through all of your story entries.
Grab the image from each story.
Compile those into a Twig array.
Create your slideshow as needed.

Here's how that all ties together...

{% set storyItems = craft.entries.section('stories').find() %}

{% set previewImages = [] %}

{% for item in storyItems %}
    {% set currentImage = item.slideshowImage.first() %}
    {% set previewImages = previewImages | merge(currentImage) %}
{% endfor %}

{# RENDER YOUR SLIDESHOW FROM previewImages #}

You'll need to initialize the previewImages array before beginning the for loop... Loops in Twig have their own scope.

  The limit and offset are just to make the slider I have work...

I'm not sure exactly what you're trying to do with the offset, but I'd recommend manipulating your images array in Twig (as opposed to fancy queries to target specific images).


Database import in admin Control Panel Is it somehow possible to import a dumped database from the admin control panel?

I'm currently dumping my database in the control panel and import the dumped file in the phpmyadmin interface.

I'd prefer dumping and importing in the control panel

Not currently – if you want to, you can add a feature request.

In the meantime, if you're on a Mac I recommend using Sequel Pro for all your importing (and other database related) needs.


Object of class Craft\ElementCriteriaModel could not be converted to string I have set up a Matrix field that contains a block type that contains among other things, an asset field.
Trying to iterate through the contents of that matrix field works just fine for all other field types but produces the following error when it comes across the asset field type.

  Object of class Craft\ElementCriteriaModel could not be converted to string

This is the part of the template that produces the error. "icon" is the asset field type.

{% for block in entry.visitorContentBuilder %}

    {% if block.type == "listItemWithIcon" %}

        {{ block.icon }}

    {% endif %}

{% endfor %}

You'll want to use block.icon.first() if you just want the first Asset, or you could loop through all Assets:

{% for block in entry.visitorContentBuilder %}

    {% if block.type == "listItemWithIcon" %}

        <ul>
            {% for icon in block.icon %}
                <li>{{ icon }}
            {% endfor %}
        </ul>

    {% endif %}

{% endfor %}

See here for a description of the difference between an ElementCriteriaModel and actual fetched elements in Craft.


How do I wrap my Youtube video from a Redactor field with Bootstrap's responsive classes? In my Redactor field, I'd like to programmatically wrap all Youtube videos that have been added through Redactor's video plugin with Bootstrap's responsive embed classes. This answers how to add a single class, but Bootstrap requires multiple classes to be added to the wrapper.

In order to add multiple classes to a video added through Redactor, I'm using the Retcon plugin. This is my solution:

{{ entry.body | retconWrap('iframe', 'div.embed-responsive') | retconAttr( 'div.embed-responsive', { 'class' : 'embed-responsive-16by9' }, false ) }}

There are two Retcon filters applied here. The first filter retconWrap wraps matching selectors in whatever you want. In this case, I'm looking for an iframe and wrapping it in 'div.embed-reponsive':

retconWrap('iframe', 'div.embed-responsive')

The second filter, retconAttr, looks for 'div.embed-responsive' (the wrapper you just applied) and applies any additional classes. The "false" indicates that you do not want to destroy any existing classes, just concatenate them:

retconAttr( 'div.embed-responsive', { 'class' : 'embed-responsive-16by9' }, false )

This may not be the most elegant solution, but it works. Frankly, I feel like we should be able to pass multiple classes through the retconWrap filter, but I've tried and it doesn't work.


Trouble running craft with docker I'm trying to set up craft to run with the docker images provided here. When I run docker-compose build, it gets through a few lines in the Dockerfile and then stops with an invalid reference error after these three lines:

ARG PHP_VERSION
ARG PROJECT_TYPE
FROM php:${PHP_VERSION}-${PROJECT_TYPE}-alpine

I do have php installed, but somehow the arguments aren't getting through to docker correctly. Has anyone gotten the docker images from that repo set up/integrated properly?
I'm no docker wizard so apologies if this is some really simple docker thing.

Ended up using the nitro tutorial instead. Thanks @Oli!


Is it possible to change a user's password in the front-end? Is it currently possible to change a user's password in for example the profile edit form? I'm currently using the forgot password approach for this.

It is! To do so, they will have to be logged in and input their old password. They need the old password to change their email or update a password.

Here's an example:

{% macro errorList(errors) %}
    {% if errors %}
        {% for error in errors %}

{{ error }}

        {% endfor %}
    {% endif %}
{% endmacro %}
{% from _self import errorList %}

<form method="post" action="" id="action-form" accept-charset="UTF-8" enctype="multipart/form-data">
    {{ getCsrfInput() }} {# Remove this if you aren't using CSRF #}
    <input type="hidden" name="action" value="users/saveUser"> {# "users/save-user" if you're on Craft 3 #}
    <input type="hidden" name="redirect" value="{{ entry.url }}">
    <input type="hidden" name="userId" value="{{ currentUser.id }}">

    Current Password
    <input type="text" class="form-control" id="currentPassword" name="password" />
    {% if account is defined %}
        {{ errorList(account.getErrors('currentPassword')) }}
    {% endif %}

    Email
    <input id="email" type="text" name="email" class="text" />
    {% if account is defined %}
        {{ errorList(account.getErrors('email')) }}
    {% endif %}

    New Password
    <input type="text" class="form-control" id="newPassword" name="newPassword" />
    {% if account is defined %}
        {{ errorList(account.getErrors('newPassword')) }}
    {% endif %}
</form>

This is code that would be inserted into a new custom front end template.


Compare custom-field value of entry to registration-form field value before saving user Whoo, that title is a mouthful...

I'm trying to check against a unique school "code" that an athlete has to input to register with a team. The code is different based on what team they are trying to join (referenced in a segment of registration page URL). I have no idea what I'm doing when it comes to PHP or plugins but I hope I'm on the right track. I have no idea how to test what values my variables have.

I've essentially copied the user/SaveUser controller into my own plugin (I have to place user in Athlete user group & related the specific team entry to user, hence custom plugin) and placed snippet below right before the if (craft()->users->saveUser($user)) line.

$teamName = craft()->request->getPost('fields.teamName');
//Get team name, set in hidden input in form, which was grabbed from segment

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = 'teams';
$criteria->title   = $teamName;
$criteria->limit   = 1;
$entry = $criteria->first();
//Find the Team entry in my 'teams' section

$schoolCode = $entry->schoolCode;
//Place value of schoolCode field in variable

$codeInput = craft()->request->getPost('fields.schoolCode');
//Grab user's input

if ($schoolCode == $codeInput)
{
    craft()->users->saveUser($user)
}

This is, of course, not working. I'm not getting any errors either.

Am I anywhere close to doing this right?

Thanks to Brad Bell, I was able to troubleshoot using var_dump. The code above works correctly, but I'm unaware of how to do a simple validation of the code within the controller, w/o creating a new model, & send back a response.

Instead (for simplicity on my end), when the validation fails, I redirected to an error specific page, referencing the code entered & team name.


In a plugin, how to get entry status? This seems like it should be simple to find, but where does Craft store the status of entries (lightswitch for enabled/disabled)?  It doesn't seem to be a column in the entries table, and I can't figure out how the other tables are linked to the entry number.

Background: In a plug-in, I'm trying to filter a query to exclude only those that are disabled.

Is this Craft 2 or Craft 3?

There are a few of ways to get entries but basically you could do something like (Craft 3)

use craft\elements\Entry;

$entries = Entry::find()->status(null)->all();

To find all disabled entries.


How do you clear the templatecachequeries table? I just noticed that on a site we launched a month ago, the templatecachequeries table is now 500mb. I'm only using the {% cache %} tag in one place on the site, around the main content matrix field. There are about 52000 rows in the templatecachequeries table now.

I've cleared the caches from Utilities, and I've re-saved all the sections but the rows are all still there. Are these meant to get deleted at some point? Is it safe to delete the rows?

You can clear the cache from within the settings section of the control panel. It's possible to select which specific caches to clear if necessary.

You can try manually clearing the cache tables. Test it on a backup first but I have done this in the past with no ill effects.

It sounds like you need to be adding the "globally" keyword to your cache tags. This will drastically reduce the number of rows in the cache table and most likely speed things up too.


Filter entries on a page based on related entry This is blowing my mind!

I have a Section with handle practiceAreas.
I have a Section with handle barristers with approximately 100
entries.
barristers has an Entries fieldType called specialisms which is linked to the practiceAreas Section.

On the Barristers page I initially list ALL of the barristers entries, but I need to add a filter which lists the practiceAreas so when I user selects a specific Practice entry the page refreshes to show only the Barristers related to that particular practice.

I'm guessing this is done by adding an entry ID parameter to the URL when the user selects a Practice and then only pulling back the Barristers related to that ID when the page refreshes?

I don't know where to even start with this :/

Thanks for any help.

You can conditionally add parameters to your criteria model. Check if there's a "area" parameter in your query string, e.g. http://example.com/barristers?area=7 before adding the filter.

{% set barristers = craft.entries.section('barristers').limit(null) %}

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

{% if query.area is defined %}
    {% set areaIds = craft.entries.section('practiceAreas').id(query.area).ids() %}
    {% set barristers = barristers.relatedTo({ targetElement: areaIds }) %}
{% endif %}

In case you need to add more than one relatedTo condition, have a look at this discussion:
Dynamic relatedTo queries


Cacheing in PHP code for plugin I have a plugin that makes several and potentially slow database calls that eventually return 8 Entries. Is there a way to make use of Craft's built in caching on the plugin side? Using the {% cache %} tag doesn't seem to be doing the trick.

You want to take a look at the CacheService class which is the service that allows you to programmatically interact with the internal cache.

This is an example of how to set some cache that expires in 3600 seconds (one hour). Set the expiration to 0 if you never want it to expire.

craft()->cache->set('some_key', 'some_value', 3600);

Just checkout CacheService for more details, as there are more methods and parameters to make use of, but this should be the basic use case.

CacheService Class Reference


Is it possible to create category for only one site in a multisite setup? If not, how would you best approach this issue?

For us, different sites have different categories, and also, not always the same number of categories.

At the moment, when we create a category, it gets created for all sites and only thing we can do is rename it on different sites.

If we delete a category from one site, it gets deleted on all sites.

Only solution that I can think of at the moment, is to have all categories created on all sites. In backend, all categories would be visible and available on all sites. On frontend we would just display only those categories which have entries in them, per site.

But we are talking about a lot of categories here, it would be messy.

Any better solutions?

Thanks!

While this was originaly scheduled for Craft 3.2 (https://github.com/craftcms/cms/issues/2122) this was not in the 3.2 release. Brandon mentioned on Discord (link) that categories will be going away altogether in Craft 4.0:

Here is his comment:

  tags and categories are both going away in Craft 4.
  3.x (maybe as early as 3.2) will see a big update to Entries fields, where they will be able to behave more like current Categories and
  Tags fields, if desired and 4.0 will just convert all existing
  categories & tags over to entries

So as it stand, I wouldn't use categories anymore and use structures instead.


Sum of one checkbox field I have a checkbox field which I want the sum of a certain checkbox:

{% set countPremiere = 1 %}
{% set iterationPremiere = 0 %}
{% set premiereTotal = 0 %}

{% for entry in allEntries %} // the normal loop starts

  {% for premiereItem in entry.kompositonsart %} //the checkbox loop starts
    {%- if premiereItem.value == 'ua' -%} // only return checked value with 'ua'
      {{ countPremiere }}
    {% endif %}
  {%- endfor -%}

  {% set premiereTotal = iterationPremiere +  premiereTotal %}

{% endfor %}

{{ premiereTotal }}

That's basically the same as I did here. This doesn't work however. I suspect it is not working because the way checkboxes are looped. Any ideas?

What you could try is putting the "if statement" as a condition in the for loop like this:

{% for premiereItem in entry.kompositonsart if premiereItem.value == 'ua' %} //the checkbox loop starts
    // only return checked value with 'ua'
    {{ countPremiere }}
{% endfor %}

I'm not really sure what you are asking but if my thoughts are right this should do the trick. This way it wont effect any other regular loop functions like a loop.index if you are going to use it.


How to differentiate EVENT_BEFORE_UPDATE_SUBSCRIPTION events in Commerce 2/3 I'm listening for various subscription events to send emails to members. Right now, I'm dealing with subscriptions where a renewal payment fails. I'm specifically listening for EVENT_BEFORE_UPDATE_SUBSCRIPTION.
The problem is it's called twice per attempt (successful or not). Once for the Stripe invoice creation and once for the Stripe transaction attempt. Here's what is might look like:
Day 1: EVENT_BEFORE_UPDATE_SUBSCRIPTION called as the renewal invoice is created. The subscription status is active.
Day 1 (1 hr later): EVENT_BEFORE_UPDATE_SUBSCRIPTION called as the transaction fails. The subscription status is suspended.
Day 2: EVENT_BEFORE_UPDATE_SUBSCRIPTION called as a second renewal invoice is created. The subscription status is suspended.
Day 2 (1 hr later): EVENT_BEFORE_UPDATE_SUBSCRIPTION called as the transaction succeeds. The subscription status is active.
You can see from the above that just listening to the EVENT_BEFORE_UPDATE SUBSCRIPTION will result in two emails being sent each day. Even if I listen for the status being suspended, an unnecessary email will be sent the following day.
What I really want to do is differentiate between the event being called by Stripe creating an invoice and Stripe attempting a transaction.
Has anyone done this or know a variable I can test against to identify whether it's an invoice creation or transaction that has triggered the event?

Could you use one of the following events?
\craft\commerce\stripe\services\Invoices::EVENT_BEFORE_SAVE_INVOICE
\craft\commerce\stripe\services\Invoices::EVENT_SAVE_INVOICE

Maybe looking in the invoice model invoiceData attribute (that is passed to the above event).
If that won't suffice, we should expose a way for you to listen to any Stripe Webhook event that comes into commerce, and react.
Please comment on the ticket for that here if so: https://github.com/craftcms/commerce/issues/1799


How to add jquery.css file in Twig? I am working on a project and the plug in I installed won't work because I don't know how to add the jquery.css file needed.

What I did is to add the file with a <link> command but it's still not working.

Any help would be great.

    {% block body %}
<link type="text/css"  href="{{ asset('home/workspace/fermate/src/CtrlBln/HomepageBundle/Resources/public/js/Minimal-jQuery-Image-Viewer-with-Image-Preloading-ABigImage/bigimage.jquery.css')}}" >

    {% for link in entity.getSidebarImages() %}
        {% if link.image.isFoto() %}

                    {{ link.image.description|raw }}

        {% else %}

                      {{ link.image.title|raw }}

        {% endif %}
    {% endfor %}

{% endblock %}

    {% block body_javascripts %}
        {% javascripts output='aBigImage.js'
        '@CtrlBlnHomepageBundle/Resources/public/js/Minimal-jQuery-Image-Viewer-with-Image-Preloading-ABigImage/abigimage.jquery.min.js'
        %}

        {% endjavascripts %}
    {% endblock %}

Is asset() a Twig macro you've defined?

Are you sure you're just not looking for the global url() method instead?


Does Google Analytics track Live Preview 'page views'? Will Google Analytics be recording page views / events from within a Live Preview? And if so, is there a way to prevent this from happening?

My understanding is that with Google Analytics you can set the domain for which it should track (e.g. to prevent it trying to track any local build events etc), but I am presuming that the domain used for Live Preview views will be the same as as if a user was viewing the front end of the site?

If it would track Live Preview views, I understand that an option for some people would be to wrap their GA code block in a conditional:

{% if not craft.request.isLivePreview %}{# ga code here #}{% endif %}

but because we have custom event tracking built into the JS, which therefore uses the global ga object, it would not be as simple in our case (though is still an option with a few checks for whether ga is defined).

If you're using SEOmatic - no. It's built in that the GA doesn't get spat out for the preview. If you're using a regular template - yes; you may want to wrap your GA code in a conditional so that it's not spat out when requested via the live preview.

{% if not craft.config.devMode and not craft.request.isLivePreview %}{# GA code here #}{% else %}<!-- Google Analytics script is NOT included in LivePreview or if devMode is on -->{% endif %}

[edit] Whoops, saw the other half of your question. What is it about your set up that's stopping you doing this? Sounds more like an Analytics issue than a Craft one to me.


Using guzzle with a plugin and cache I've been adapting the very useful geo plugin to fetch currency and exchange rates based on a visitors IP address. [ see: https://github.com/lukeholder/craft-geo ]

The plugin uses Craft's vendor package "guzzle" to contact 3rd party APIs and request data which then gets cached in JSON form.

When its not cached i was wondering how can you tell if Guzzle is making the request - there are no headers in dev tools and no noticeable traffic for the requests. So how can you see if the Guzzle has guzzled when cache is off?

Update:

Turns out server side initiated remote calls won't be logged in your browser's Network tab (only client-side ones).  Which now that I type that out, seems very obvious.

If Guzzle actually makes a request, then it will show up in your browser's inspector's "Network" tab.


Maintaining a relationship of parent from children selected through entry field Not sure that is the best title but:

I have a Section called Research Projects. An entry from this section contains a number of images, videos and audio entries in a grid chosen through an Entry Field. At the moment when one of the grid items is selected it displays a modal showing the details of that entry. There is no issue is showing a relationship as the info is displayed on the same page.

My question is if I chose to show the entry selected on a new page how would I show the relationship to the page it appears on / came from? Perhaps as a title from the previous page or a back button.

I am not using a structure as video, image items can appear in multiple projects.

Hope that makes sense and thank for any help anyone can offer.

Scott

This is commonly called 'reverse-related entries' and is achievable using relatedTo(). See here for some info...

  My question is if I chose to show the entry selected on a new page how would I show the relationship to the page it appears on / came from? Perhaps as a title from the previous page or a back button.

For example, if a visitor were on an Audio Entry page, your template code would look something like:

{% set relatedResearchProject = craft.entries.section('researchProjects').relatedTo(entry) %}
{% for researchProject in relatedResearchProject %}

{{ researchProject.title }}

{% endfor %}

The above, would display any Research Projects which contain related "audio" Entries.

The logic is as follows:

{% set relatedResearchProject = craft.entries.section('researchProjects').relatedTo(entry) %}

Create a new variable called "relatedResearchProject" (in fact the variable name can be any word which makes sense to you)
The new variable should contain any entries from the Research Projects Section which are related to the the (Audio) entry being displayed on the current page.

Then...

{% for researchProject in relatedResearchProject %}
{{ researchProject.title }}</a
{% endfor %}

Loop through and display any entries which are in the 'relatedResearchProject' variable.

In a different example below, you can see that "Third news entry..." contains the Entry field. then, on /news/we-just-installed-craft you can see the rendered link back to the "Third news entry..." page.

Sorry, Scott - I hope illustrating two examples isn't confusing! Let us know how you get on.

{% for researchProject in relatedResearchProject %}
{{ researchProject.title }}</a
{% endfor %}


Search through multiple ElementTypes Is it possible to search through multiple ElementTypes?

Instead of this:

craft()->elements->getCriteria(ElementType::Entry)->limit(10)->search('Test');

Doing something like this:

craft()->elements->getCriteria(ElementType::Entry, ElementType::Asset, ElementType::Category)->limit(10)->search('Test');

You'd have to do the in multiple queries at this point.

Consider voting for this feature request, which is asking for what you're looking for.


Search using Slug as query parameter I created Search that uses entry slug, but it fails if the slug has more than one dash "-" in it. Has anyone come across this and/or have any ideas for getting around it?

I need to use the slug because they are unique within a section.

If you know your q variable should be a slug, you can use the .slug query parameter:

{# Get the requested entry slug from the URL #}
{% set requestedSlug = craft.app.request.getSegment(3) %}

{# Fetch the entry with that slug #}
{% set entry = craft.entries()
    .slug(requestedSlug|literal)
    .one() %}

More on how to use this in the Craft 3 docs.

I have personally had some unexpected results using Craft's built-in search.


How to set up Google Tag Manager correctly First time user here, can any please provide information on how to best implement the GTM tag container?

Is it possible to hard code it? Or is the only way to add a plugin?

Not sure what you want to know: everything you need is in the
Quick Start Guide. Just copy the two pieces in your layout and you are done.

In Craft you are totally free to do everything you want in your frontend templates. The system won't restrict you at all


Checking if matrix lightswitch is true, and add wrapper div I'm using Zurb foundation's Block Grid and using this in a matrix field where users can (using a block called 'wrapper') add a row and define the number of columns which appear side by side across a desktop device in that row, then using another block 'textBlocks' add divs of text within the row. I also want to add an option of 'fullwidth' a lightswitch field in the 'wrapper' block, so if this is selected then the 'following' will wrap around the row:

…row/text content

but I can't seem to add the closing  in the right place (at the end of the row), code below:

{% set isWrapperOpen = false %}
{% for block in entry.pagedesigner %}

  {% switch block.type %}

    {% case "wrapper" %}

      {% if block['fullwidth'] is defined %}
{% endif %}
      {% if isWrapperOpen %}
{% endif %}

      {% set isWrapperOpen = true %}
      {{ block.heading }}

    {% case "textBlocks" %}

          {{ block.text }}

  {% endswitch %}

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

{% endfor %}

pic added of Publish page

Appreciate semantics need some work but need to figure logic first

UPDATE I've since chatted with Andrew Fairlie from Mutual and got this working (with his guidance) excuse the crude inline styles – just helps aid making visual sense to me :)

{% set isWrapperOpen = false %}
{% set isFullWrapperWidthOpen = false %}
{% for block in entry.pageDesigner %}

{% switch block.type %}

{% case "wrapper" %}
 {# Close the previous fullwidth wrapper before we open a new one #}
  {% if isFullWrapperWidthOpen %}
{% endif %}
  {# Close the previous wrapper before we open a new one #}
  {% if isWrapperOpen %}
{% endif %}

  {% if block.fullWidth %}
    {% set isFullWrapperWidthOpen = true %}

     {% else %}
       {% set isFullWrapperWidthOpen = false %}
    {% endif %}

  {% set isWrapperOpen = true %}

  {% case "heading" %}

    {{ block.text }}

  {% case "textBlocks" %}

      {{ block.text }}

{% endswitch %}

{# If we're on the last block, close up the last wrapper div #}
  {% if loop.last and isWrapperOpen %}{% endif %}
  {% if loop.last and isFullWrapperWidthOpen %}**{% endif %}
{% endfor %}

Judging from your code it looks like you are struggling with one of Matrix's limitations. Nesting. I have seen, and used, a number of hacks to get around this until I stumbled upon the Neo plugin. I, in fact, use it exclusively for this kind of thing; Bootstrap's and Foundation's rows and columns.... or any other flex grid system for that matter.

So this answer's kinda a non answer because I am going to point you in another direction to achieve Foundations Block Grid.

Because Neo can nest and the code is very similar to a structure using levels, you can do something like this for Foundation's Block Grid.

{% for row in entry.neoField.level(1) %}

  {% for column in row.children %}
    {% if loop.first %}
{% endif %}

            {% for field in column.children %}
            stuff... (use your switch code or I prefer per-field includes)
            {% endfor %}

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

{% endfor %}

Will output

So in Neo I have a top level block called "Row". It has no fields, but it could, you can make it as complicated as you want adding class, background etc. This row block can only have one type of child block called "Column" again, no fields if you don't want. This "Column" block can have any number of child blocks including photo, text, video(even a Row! Inception!). For each row you add, you can have as many columns you want(zurb block grid only works to 8). The total amount of columns you add gets added to the medium-up-3 per the {{ loop.total }}.

Neo Field

Note: The code above is a simplified version. Doing the Row > Column > Row works, but the code's a little more complicated.


Is it possible to search an entry for specific table content Considering the way table field content is saved to the database, could I search entries for content in a specific column?

So, say I have a table field containing X column, I would only want to search for an ID, located in column 3. Did anyone ever successfully try this?

I don't believe this is possible using the native search functionality.

You can search for a specific value within a table field using:

{% set results = craft.entries.search('tableField:"ID"') %}

That should return all entries that contain "ID" in tableField, but it doesn't restrict the search to a particular column in the table.

How likely is it that the ID will occur in other columns of the table?


Add Entry Button Not Shown When Overriding Entry Sources We're putting together a site where there is regional content managed by regional editors. I want to override the sources to only list entries from regions they are allocated to. I also want to group them by region rather than having entries for all their regions in one list.

All was going well until I tried to remove the default Craft sources by clearing the $sources array at the start. This has resulted in the New Entry button no longer being displayed.

This is my function at the moment:

public function modifyEntrySources(&$sources, $context)
{
    if ($context == 'index')
    {
        if ($this->regionalEd)
        {
            $sources = [];
            foreach ($this->regions as $region)
            {
                $sources[] = ['heading' => $region['title']];
                $sources['news:'.$region['id']] = [
                    'label'       => 'News',
                    'criteria'    => ['relatedTo' => $region['id'], 'section' => 'news', 'editable' => true],
                    'data'        => array('type' => SectionType::Channel, 'handle' => 'news'),
                    'defaultSort' => array('postDate', 'desc'),
                ];
                $sources['notifications:'.$region['id']] = [
                    'label'       => 'Notifications',
                    'criteria'    => ['relatedTo' => $region['id'], 'section' => 'notifications', 'editable' => true],
                    'data'        => array('type' => SectionType::Channel, 'handle' => 'notifications'),
                    'defaultSort' => array('postDate', 'desc'),
                ];
            }
        }
    }
}

Is there something I need to add/remove, (other than the $sources = []; line!), to allow the New Entry button to be displayed?

The New Entry button is not being displayed as Craft is not seeing my sources as containing publishable entries.

The Craft JS object contains a list of publishableSections which is checked by craft.js using this.getSourceByKey('section:'+section.id). Since my sources use a custom key value they are not included in this list.

So I have resolved the problem by adding my own New Entry button.


Help with correct syntax regarding multisort please Hi hoping for a quick resolution on this as a lot of scripting relies upon it. I have an array:

{% set furniture = [
     {
         'name': 'chair',
         'price': '100'
     },
     {
         'name': 'bench',
         'price': '500'
     }
   ]
%}

and I'm trying to sort it by price:

{% set sorted = furniture|multisort(['price'],['SORT_DESC'],['SORT_REGULAR']) %}

but all I get is this error:

array_multisort(): Argument #2 is expected to be an array or a sort flag.

I've also tried:

{% set rides = furniture|multisort(furniture['price'],['SORT_DESC'],['SORT_REGULAR']) %}

but then I get the error:

Key "price" for array with keys "0, 1" does not exist.

I've kind of run out of ideas as to how to tackle it. Thanks.

Try removing the quotes on 2nd/3rd arguments:

{% set rides = furniture|multisort(furniture['price'],[SORT_DESC],[SORT_REGULAR]) %}


Saving entries with FeedMe doesn't trigger Sprout notification email So I have a FeedMe task that runs and updates a custom field for a select amount of entries.

In my notification email, I have a custom rule like so:

{% if object.customField | length and object.customField == 'hello' %}{{true}} {{else}} {{false}}{% endif %}

When I run the FeedMe task, the email doesn't get sent but I can see the entries get updated. If I go into the entries and save one manually, then the email sends so it seems that the rule works.

I also went in a plugin and set up the following event listener:

Event::on(
    Elements::class,
    Elements::EVENT_AFTER_SAVE_ELEMENT,
    function(ElementEvent $event) {
        if ($event->element instanceof Entry) {
            $entry = $event->element;

            Craft::info('Entry being updated: ' . $entry->title, __METHOD__);
        }
    }
);

and after I run the FeedMe task again, I check the logs and every entry that was updated by the task is logged as being saved.

In conclusion, I know the entries are being updated and saved by the FeedMe task but the notification email custom rule doesn't trigger unless I manually save the entries.

To confirm the behavior here, Sprout Notification Emails only get triggered on web requests and are suppressed for console requests and during updates and migrations. Notification Emails could get triggered in other contexts but you would need to do so programmatically.

Notification Events don't run for console requests

$request = Craft::$app->getRequest();

// Only handle notifications on web requests
if ($request->getIsConsoleRequest()) {
    return false;
}

Notification Events don't run during migrations

if ($request->getIsActionRequest()) {
    $actionSegments = $request->getActionSegments();
    if (
        ArrayHelper::firstValue($actionSegments) === 'updater' ||
        $actionSegments === ['app', 'migrate'] ||
        $actionSegments === ['pluginstore', 'install', 'migrate']
    ) {
        return false;
    }
}

Code examples from Sprout Base Email v1.2.7


Accessing admin panel after deploying local MAMP I've moved my site from a local install to my server. I'm pretty new at this, so hopefully I've followed the right course:

What I did:

Edited db.php to reflect the changes
Edited index.php to point at the craft folder
Had to change the siteUrl in craft_info because it would only show the Craft 404 page (siteUrl was still on localhost)

I can see the correct rendered pages now, but as soon as I type /admin or /index.php/admin or /index.php?p=admin I get an Apache 404 error on the /admin/login url

So what should i do now?

Apparently what I did was correct, because now it works. I had some EE files right next to the index.php files and these messed things up for some reason. Probably the admin.php file.

At least now I know it's easy to deploy Craft with the three steps I mentioned above