How do you limit the number of items that show up in a navigation? I have a nav set up which works find but I need to limit the number of top level links to the first 5 in the loop (right now we have 9 plus oodles of children links!). Have tried various flavours of loop.index etc but have run out of ideas.

Here’s my basic nav code:

{% set ppages = craft.entries.section('productPages').level('<=2') %}

  {% nav page in ppages %}

      {{ page.getLink() }}
  {% ifchildren %}

      {% children %}

  {% endifchildren %}

  {% endnav %}

If this is a Craft 2 project, unfortunately there's not a super clean way to do that. You'd have to get the last top-level item in the nav you want to show, and add a bunch of conditions in the code to make sure the entry you're outputting isn't after that 5th entry.

{# get the 5th top-level page #}
{% set fifthElement = craft.entries()
    .section('productPages')
    .level(1)
    .nth(4) %}

{# get all the pages #}
{% set ppages = craft.entries()
    .section('productPages')
    .level('<=2')
    .find() %}

{# now display the nav, but only output HTML for entries that are
   within the first 5 top-level entries #}

{% nav page in ppages %}
    {% if page.lft < fifthElement.rgt %}

        {{ page.getLink() }}
    {% endif %}
  {% ifchildren %}
    {% if page.lft < fifthElement.rgt %}
{% endif %}
    {% children %}
    {% if page.lft < fifthElement.rgt %}
{% endif %}
  {% endifchildren %}
    {% if page.lft < fifthElement.rgt %}

    {% endif %}
  {% endnav %}

In Craft 3 it would be quite a bit simpler, though, as you have the power to modify the element query however you want. So we can first get the 5th top-level entry’s rgt value, and then when running our main ppages query, only get the ones whose lft value is less than that 5th entry’s rgt value.

{# get the 5th top-level page #}
{% set fifthElement = craft.entries()
    .section('productPages')
    .level(1)
    .nth(4) %}

{# get all the pages that are within the first 5 top-level pages #}
{% set ppages = craft.entries()
    .section('productPages')
    .level('<=2')
    .andWhere(['<', 'lft', fifthElement.rgt])
    .all() %}

{# now output the navigation normally #}

  {% nav page in ppages %}

      {{ page.getLink() }}
  {% ifchildren %}

      {% children %}

  {% endifchildren %}

  {% endnav %}

If you’re unfamiliar with these lft and rgt values, they are attributes of structured entries, which define their position within the structure, using the “Nested Set” model. The easiest way to think of it is, your structure section is like an XML file, and each of the entries are nodes within it. Each entry has a opening tag and a closing tag on different lines, and lft and rgt are those line numbers.

1 <top_entry>
2   <nested_entry>
3   </nested_entry>
4 </top_entry>

So when we do {% if page.lft < fifthElement.rgt %}, we are saying “make sure that this node’s ‘opening tag’ comes before the fifth element’s ‘closing tag’”.


Is it possible to disable `requireUserAgentAndIpForSession` for specific instances? I'm working on building a post-receive hook and a third party service's attempt to post data to a webhook keeps 302ing to the Craft login page when I attempt to use EmailModel and sendEmail().

The only thing Craft logs during this failed transaction is a series of errors that read...

  Someone tried to restore a session from a cookie without presenting an IP address or userAgent string.

I noticed the requireUserAgentAndIpForSession setting in UserSessionService->_checkVitals(), disabled it, and of course the problem went away.

I'd prefer not to disable this setting in general—is there a way to programmatically unrequire it for a specific plugin receiving posts?

Currently, no... it's an all or nothing thing and you'll have to disable it globally.


Is it possible to use environment variables as the cache duration? I'm trying to set a cache duration as an environment variable.

I set up various environment variables and know they are being referenced correctly in my templates because I can print out the values of the variables in my templates.

return array(

    '*' => array(
        'environmentVariables' => array(
            'cacheDuration'          => '1 week',
            'cacheDurationQuantity'  => 1,
            'cacheDurationUnit'      => 'week',
        ),
    ),
);

I initially started with this cache tag...

{% cache for craft.config.environmentVariables.cacheDuration unless craft.config.devMode %}...{% endcache %}

...but I got this error:

  Unexpected token "name" of value "craft" ("number" expected)

This had me thinking that perhaps Craft or Twig is expecting an integer immediately after the "for" keyword. Thus, I broke my cacheDuration variable into two parts: quantity which is an integer and unit which is a string.

{% cache for craft.config.environmentVariables.cacheDurationQuantity craft.config.environmentVariables.cacheDurationUnit unless craft.config.devMode %}...{% endcache %}

I still got same error as above. This inspired me to try:

{% set duration = 1 %}
{% cache for duration craft.config.environmentVariables.cacheDurationUnit unless craft.config.devMode %}...{% endcache %}

This time, I got a slightly different error.

  Unexpected token "name" of value "duration" ("number" expected)

This has me thinking that any variable evaluated as expected in the duration parameter of the cache tag. However, other variables (like craft.config.devMode) are interpreted fine for other parameters of the cache tag.

Can anyone else confirm this or share insight into this?

The error you're encountering is part of how Pixel & Tonic wrote the cache tag. In short, variables will not work for what you're trying to accomplish. For the longer explanation...

Tags in Twig are made up of tokens. So strings, numbers, variables, functions, tags, and filters are all tokens. When Twig is parsing a tag, it reads all the tokens and checks it against rules in a Twig extension.

The cache tag after seeing a 'for' token will only accept a number as the next token. Not a variable, or a string, or anything else. The bit typed into the template must be a number. For the duration unit, it checks what's there against a list they have already defined. Again, a variable won't work here. Likewise, the 'until' token looks for a date that can be parsed by PHP's strtotime, and 'unless' looks for something that gives it true or false.

You can accomplish the very same thing using built-in config settings. The cacheDuration setting will do as it says using the PHP Interval Specification. If you have a specific cache storage you'd like to use you can use cacheMethod. Setting these will let you use the cache tag without any extra configuration on the tag itself.


How can I convert a dropdown field value to an integer within a hash? I'm setting up a twitter feed with craft.twitter.get() and I need to convert a dropdown value to an integer. Here is my code far:

{% set cacheTime = sidebarBlock.twitterFeedCacheTime %}
{% set tweets = craft.twitter.get('statuses/user_timeline.json?screen_name='~ sidebarBlock.twitterFeedUsername ~'&count='~ sidebarBlock.twitterFeedTweetCount ~ '&', { exclude_replies: sidebarBlock.twitterFeedIncludeReplies, include_rts: sidebarBlock.twitterFeedIncludeRetweets, }, {}, true, cacheTime) %}
{% if tweets %}

{{ sidebarBlock.twitterFeedTitle }}

  {% for tweet in tweets %}

      @{{ tweet.user.screen_name|raw }}: {{ tweet.created_at |nicetime('m-d-Y') }}

      {{tweet| tweetLink(newWindow='true') | raw}}

  {% endfor %}

{% endif %}

It seems that no matter what I try on twitterFeedcacheTime it returns a string to integer conversion error:

Object of class Craft\SingleOptionFieldData could not be converted to int

/craft/app/etc/cache/FileCache.php(85)

73      * @param  string  $value  The value to be cached
74      * @param  integer $expire The number of seconds in which the cached value will expire. 0 means never expire.
75      * @return boolean true    If the value is successfully stored into cache, false otherwise
76      */
77     protected function setValue($key, $value, $expire)
78     {
79         if (!$this->_gced && mt_rand(0, 1000000) < $this->getGCProbability())
80         {
81             $this->gc();
82             $this->_gced = true;
83         }
84
85         if($expire <= 0)
86         {
87             $expire = 31536000; // 1 year
88         }
89
90         $expire += time();
91
92         $cacheFile = $this->getCacheFile($key);
93
94         if ($this->directoryLevel > 0)
95         {
96             IOHelper::createFolder(IOHelper::getFolderName($cacheFile), IOHelper::getWritableFolderPermissions());
97         }

Give this a shot:

 {% set cacheTime = sidebarBlock.twitterFeedCacheTime.value %}


Debugging an error ("An unknown error occurred") I keep getting an error when I am in the CP. The stack trace of the error that is logged is below:

2016/08/02 01:12:05 [error] [exception.Craft\HttpException.404] exception 'Craft\HttpException' in /home/capspacedevhosti/craft/app/controllers/TemplatesController.php:59
Stack trace:
#0 [internal function]: Craft\TemplatesController->actionRender('(X(1)S(q325ycaz...', Array)
#1 /home/capspacedevhosti/craft/app/framework/web/actions/CAction.php(109): ReflectionMethod->invokeArgs(Object(Craft\TemplatesController), Array)
#2 /home/capspacedevhosti/craft/app/framework/web/actions/CInlineAction.php(47): CAction->runWithParamsInternal(Object(Craft\TemplatesController), Object(ReflectionMethod), Array)
#3 /home/capspacedevhosti/craft/app/framework/web/CController.php(308): CInlineAction->runWithParams(Array)
#4 /home/capspacedevhosti/craft/app/framework/web/CController.php(286): CController->runAction(Object(CInlineAction))
#5 /home/capspacedevhosti/craft/app/framework/web/CController.php(265): CController->runActionWithFilters(Object(CInlineAction), Array)
#6 /home/capspacedevhosti/craft/app/framework/web/CWebApplication.php(282): CController->run('render')
#7 /home/capspacedevhosti/craft/app/framework/web/CWebApplication.php(141): CWebApplication->runController('templates/rende...')
#8 /home/capspacedevhosti/craft/app/etc/web/WebApp.php(279): CWebApplication->processRequest()
#9 /home/capspacedevhosti/craft/app/framework/base/CApplication.php(184): Craft\WebApp->processRequest()
#10 /home/capspacedevhosti/craft/app/index.php(62): CApplication->run()
#11 /home/capspacedevhosti/public_html/index.php(19): require_once('/home/capspaced...')
#12 {main}
REQUEST_URI=/(X(1)S(q325ycazg05lq11ktn1hqjtx))/copyright
---
2016/08/02 01:12:05 [error] [application]

Anyone knows how to fix it?

The error you're showing from the logs is a simple 404 error that occurs whenever someone or something requests a URI on your site that doesn't resolve to anything.  In this case it is /(X(1)S(q325ycazg05lq11ktn1hqjtx))/copyright, which is likely a script probing for vulnerabilities, which is very common for any public website.

We've moved those 404 messages into their own 404.log file in the upcoming Craft 3 so they don't clutter the main log file.

The underlying error message for An unknown error occurred will be elsewhere in your log files.  Sometimes it's helpful to nuke all of your logs (or move them somewhere else), then reproduce the issue as you'll have left to sift through.


Sprout forms internal server error after migration We migrated our dev-Website to the new server for launching the project. Everything works fine except Sprout Forms. We can't access the Plugins settings page in the control panel.

Error says internal server error, the template „sproutforms/settings/general“ can't be found.

After we call craft.sproutForms.someFunction() in the template our front-end stops rendering. Any ideas?

It sounds like you're missing some necessary Sprout Forms files.  Maybe they weren't FTP/copied over to the new server properly or if you're deploying from Git, maybe there was a gitignore rule that they got caught up in.

If you enable devMode, you'll probably get an error message on the front-end.

Regardless, I'd grab a fresh copy of the plugin and replace the existing craft/plugins/sproutforms folder with the new one.


LJ Dynamic Fields - Only Seeing Latest 100 entries I am currently creating various component entries (under channels). Once created I can then add that specific component to structure (know on my site as a work page).

On the work page I have a dynamic field (see https://github.com/lewisjenkins/craft-dynamic-fields) that allows a user to scroll through these already created component entries in a dropdown list style. That drop down is a dynamic dropdown generated through LJ dynamic fields with the following code in the CMS.

{% include '_dynamicfields/sections' ignore missing %}

From there the included file is as follows...

{% for channel in craft.entries.section('components') %}
   { "value":"{{ channel.id }}" , "label":"{{ channel | raw }}" }
   {% if not loop.last %},{% endif %}
{% endfor %}

Unfortunately as of late I'm only seeing the latest 100 entries. I haven't seen any reference in either craft CMS documentation or the LJ Dynamic fields documentation.

By default Craft 2 will only return 100 elements (entries, assets, users, etc.) at a time.

If you want to get more than that, you need to use limit(null).

{% for channel in craft.entries.section('components').limit(null) %}

This behavior changed in Craft 3 so there is no default limit.


Setting absolute path to asset files for Craft 3 multisite Currently, I have 3 sites set up in Craft 3 beta. I'm trying to get all my sites to share one asset folder by using absolute path. However, I can't seem to set it up. The siteUrl is not parsing as I would expect. I would get "{siteUrl}/assets/people/image.png".

Found out the answer on how to get all my sites on Craft 3 to share one image folder. So you have to set your base url for all your site to your original site's base url.

Then in your code, you just put {{ siteUrl }} before your image url. Kind of confusing since siteUrl is actually the Base Url in the dashboard.


Is it possible to use a lightswitch (or something similar) for different post types? My question is closely related to this question: How do I display entries from multiple entry types?

I would like to have two blog post types:

Regular blog posts (Title, Body, etc)
External links posts (Link out to another site)

I imagined I would display them in the same area, by dates created, like a standard blog, but clicking some of the links would go to external link.

I want to make this as easy as possible for my client, so I would like them to be in the same place if possible, but also open to any other solutions. Has anyone done something like this?

I thought about the possibility of two channels, and combining all posts and ordering by date? Not completely sure how I would go about that though.

I think your answer is in the post you referenced. Create two entry types in a single section, each of which contains different fields (and/or some in common). Then loop through all the entries and test for entry type in the output.

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

{% for entry in entries %}
    {% if entry.type ==  'linklist' %}

{{ entry.title }}

    {% else %}

{{ entry.title }}

    {% endif %}

    {{entry.body }

{% endfor %}

The user will have to understand how to change the entry type in Craft's UI (it's the dropdown in the right column, but it's pretty clear once they've been shown how to do it once).


Getting Asset FolderId I need to list out the image options from a specific assets folder for users to select from. Previously, I accomplished that like this:

{% set folder_id = 5 %}

{% for image in craft.assets.kind('image').folderId(folder_id).order('filename') %}

    {# do stuff, like print image.filename #}

{% endfor %}

And to get the folder ID pre-2.5, I could inspect the folders menu when viewing assets in CP. Now, that appears to be gone in Craft 2.5+ and struggling to find the ID. I'm assuming there must be a way to view the folderId as Craft has a folderId() filter for craft.assets.

Does anyone know?

Thanks,

You can always check the craft_assetfolders table in the database, find the folder name you're looking for and grab its id.

And it's probably just a typo in your original code, you're setting the folder ID to a variable named folder_id, but you're passing id into .folderId(id).


On Entry Creation Redirect Is it possible to redirect a certain user type to the entry front-end page once they click on Save from the CP?

You'd have to write a custom plugin, that adds an event listener for the entries.saveEntry event.

The event listener should go into your plugin's primary class' init method. You'll need to make sure the event listener only fires for Control Panel requests, and that the currently logged in user is assigned to the user group you want to apply this to.

You should also make sure the entry being saved has a URL (not all entry types do) before attempting to redirect.

Here's a working example:

public function init()
{

    parent::init();

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

    // Only add the event listener for Control Panel requests, and if the user is in a certain group
    if (craft()->request->isCpRequest() && $user && $user->isInGroup('userGroupHandle')) {

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

            // Redirect to the entry
            $entry = $event->params['entry'];
            if ($entry->url) {
                craft()->request->redirect($entry->url);
            }

        });

    }

}

Obviously, you'll need to change the userGroupHandle string to the handle for your particular user group.

If you're unfamiliar with plugin development for Craft, I'd recommend taking a look at the Business Logic template plugin or the pluginfactory.io generator app – as well as the official docs on the subject, of course.


Using elementSelect for third-party elements I'm writing plugin to extend Craft Commerce and I want to present a good way to select products in CP.

I know I could use dropdown select for this, but using Craft's elementSelect would make the UI much nicer. Is there a way to use elementSelect dialog / fieldtype not just for entries or assets, but for custom data set (in my case, Commerce Products)?

If you are referring to a custom field to select products, one already exists:

If you are building a general attribute field for a plugin, taking a look at the B BaseElementFieldType.php within the craft/app filder (that the product field above extends) will give you clues on how to recreate the UI to select products (or any other element).

The key file being craft/app/templates/_includes/forms/elementSelect.html which will show you how to instantiate the Craft.BaseElementSelectInput js class.

Hope that points you in the right direction. I am not sure if there is a full guide out there on how to useCraft.BaseElementSelectInput js, but hopefully the above will get you most of the way there by looking at the code.


How to integrate with Hubspot For a big client we are required to set up their blog on their Hubspot account. However we are developing everything else in Craft.

What is the easiest way to make the connection so the client can use Hubspot to post blog entries into our Craft site?

I would say you would need to create a custom plugin and use Hubspot webhooks.

Within this plugin you would have a controller method that, when you visit the action url, will take a response and convert it to an entry.

So for example if your plugin was called hubspotentries, you could have a controller HubspotEntries_WebhookController.php

Then inside that:

<?php
namespace Craft;

class HubspotEntries_WebhookController extends BaseController {

 protected $allowAnonymous = true;

 public function actionSync()
 {

   // If the webhook POSTS to the action url, get the php input and decode it
   $webhook = json_decode(file_get_contents('php://input'), true);

   //....Go through webhook and save entries etc.

 }
}

Your action url would be something like http://domain.com/actions/hubspotEntries/webhook/sync

You can then give this url to Hubspot and set up it so when an event occurs, they will POST or GET to this url and you'll be able to create the entries. I did something similar to this before (not hubspot but another service) and it works like a charm.

Useful Craft Documentation links

Plugin development introduction
Controllers in plugins

Hubspot Help

How do I use webhooks with HubSpot Workflows

Hope that helps you get started!


Search forms handling of plurals Is it possible to include plural variations? For example if I search for 'apple' but the entry includes 'apples' it appears.

Or would this need to be approached by getting these variations into the entries via tags or similar?

{% set query = craft.request.getParam('q') %}
{% set entries = craft.entries.search(query).order('score').section('newsEvents') %}

{% if entries|length %}

{{ entries|length }} result(s):

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

    {% else %}

Your search for "{{ query }}" didn't return any results.

{% endif %}

You can certainly achieve a very rudimentary form of plural search by following the recommendations in AbbeyDesign's answer.

However, it comes with an important caveat: it won't work for plurals which do not include the singular form.

For example, baby / babies, knife / knives, or tooth / teeth.

If you need to accommodate this requirement, Craft's built-in search probably isn't going to cut it, and you'll need to go with something a bit more sophisticated.

I haven't personally tried it, but I think Search Plus can handle this. It's basically a convenient wrapper around Algolia, a very sophisticated search tool, which automatically handles pluralisation.


Cycle outputs plain text, should be html Is this normal behavior?

{% for entry in entries %}
{{ cycle(['

', '2', '3', '4'], loop.index0) }}
{% endfor %}

Results in:

&lt;div&gt;&lt;/div&gt;

2

3

4

is there a way to force the output as it is?

You can use Twig's "raw" filter to prevent the HTML from being escaped

{{ cycle(['

', '2', '3', '4'], loop.index0)|raw }}


Get all categories from a CategoryGroupModel through API When I get a CategoryGroup with the following

$category = craft()->categories->getGroupByHandle('productEnvironment');

I Just can't figure out how to get the categories inside this category.
Is-there any kind of relation/method ?

Thanks

Nicolas

I've found that it was not the best way to to it.
I used the following code and it cleared my problem.

$criteria = craft()->elements->getCriteria(ElementType::Category);
$criteria->group = 'productEnvironment';
$entries = $criteria->find();

foreach ($entries as $entry) {
    echo $entry->title . '
';
    echo $entry->mountingPreview[0] . '
'; // Dirty get the first assets
}

Hope this helps someone


Possible to use REGEX Backreference in Routes I've searched and searched for info on regex backreferences in Craft's route.php file to no avail.

I have a URL structure required by the client along the lines of:

/ [state] / [section] / region / [region]

so for examples:

/ hawaii / hotels / region / oahu
/ hawaii / retaurants / region / oahu
/ hawaii / hotels / region / maui

The "hotels" and "restaurants" are each their own Section (Channels) and require their own templates.

Given the above URL examples, I've created template directories:

/hotels/region.html
/restaurants/region.html

I can manage the routes if I spell them all out. But what I'd like to do is use regex backreferences in the target templates.
Tried but it fails. tried something like the following:

'hawaii/(.*)/region' => '\1/region'

The '\1' backreference doesn't catch the first group of either "hotels" or "restaurants."

Is it simply not possible to use backrefs in the target route templates...or is my syntax not correct for Craft?

Is it simply not possible to use backrefs in the target route templates

Right now it's not possible (although might be worth a feature request).

The right hand side of the route key => value pair can currently only be a static string that is a path to the template to load or an array that defines a controller action to load.


Is it possible to use the field layout designer in settings screen? I working on a simple inquiries plugin and i want it to use custom element types to manage the inquiries.

I have the basics working but I'm trying to setup the option to add/edit custom fields on the inquiry and thats where I'm running into some issues.  Most of the other examples I'm finding (including Pixel & Tonic's own events plugin) all seem to have a second model that sets up the field layout and passes it to the elements like Calendars -> Events, or Entry Types -> Entries, Category Groups-> Categories.

But what I'm hoping to do is just have one single field layout for all inquiries (more like how users work currently) and ideally this would be editable through the plugin settings (instead of the tab)  is that currently possible?

I can include this:

{% include "_includes/fieldlayoutdesigner" with {
  fieldLayout: fieldLayout
} only %}

but i need to get a field layout somehow? and then hook into the save?

There are two ways to retrieve field layouts: by ID (FieldsService::getLayoutById()) and by element type (FieldsService::getLayoutByType()). The former is useful for element types that manage multiple field layouts and have their own way of storing those field layout IDs (e.g. sections, asset sources, etc.), and the latter is useful for element types that only need to manage a single field layout (e.g. users, or in your case, inquires.)

So, here’s what your plugin’s getSettingsHtml() method should look like:

public function getSettingsHtml()
{
    $fieldLayout = craft()->fields->getLayoutByType('Inquiry');

    return craft()->templates->render('inquires/_settings', array(
        'settings' => $this->getSettings(),
        'fieldLayout' => $fieldLayout
    ));
}

That “inquiries/_settings” template would map to craft/plugins/inquiries/templates/_settings.html, and that should include this:

{% include "_includes/fieldlayoutdesigner" with {
    fieldLayout: fieldLayout
} only %}

And your plugin’s prepSettings() method should look something like this:

public function prepSettings($settings)
{
    // Delete the old field layout
    craft()->fields->deleteLayoutsByType('Foo');

    // Extract the field layout data from the settings
    $postedFieldLayout = isset($settings['fieldLayout']) ? $settings['fieldLayout'] : array();
    $requiredFields = isset($settings['requiredFields']) ? $settings['requiredFields'] : array();

    // Save the field layout
    $fieldLayout = craft()->fields->assembleLayout($postedFieldLayout, $requiredFields, true);
    $fieldLayout->type = 'Foo';
    craft()->fields->saveLayout($fieldLayout);

    // Return the settings without the field layout data
    unset($settings['fieldLayout'], $settings['requiredFields']);
    return $settings;
}

Finally, whatever model your element type’s populateElementModel() method returns should have a getFieldLayout() method that looks like this:

public function getFieldLayout()
{
    return craft()->fields->getLayoutByType('Inquiry');
}

Note: The Field Layout Designer will only work within the plugin settings in Craft 2.2.2596 or later.


Getting Twig_Error_Runtime when trying to open the website I am trying to set up a Craft CMS website with Twig. I have done all the setup but getting the following error when I try to open the website-

Twig_Error_Runtime

Variable "global" does not exist in "503" at line 9 (C:\wamp64\www\west\craft\app\vendor\twig\twig\lib\Twig\Template.php:436)

#0 C:\wamp64\www\west\craft\storage\runtime\compiled_templates\00\0009102817a5d89769207212bd179d422974c0400e0d4db894ad4fd45f35fd9d.php(29): Twig_Template->getContext(Array, 'global')
#1 C:\wamp64\www\west\craft\app\vendor\twig\twig\lib\Twig\Template.php(381): __TwigTemplate_37e5d62204d15e3586ae79a226d2d0e58f18084c697fef1d38e94cf3b75f8fb0->doDisplay(Array, Array)
#2 C:\wamp64\www\west\craft\app\etc\templating\BaseTemplate.php(26): Twig_Template->displayWithErrorHandling(Array, Array)
#3 C:\wamp64\www\west\craft\app\vendor\twig\twig\lib\Twig\Template.php(355): Craft\BaseTemplate->displayWithErrorHandling(Array, Array)
#4 C:\wamp64\www\west\craft\app\vendor\twig\twig\lib\Twig\Template.php(366): Twig_Template->display(Array)
#5 C:\wamp64\www\west\craft\app\vendor\twig\twig\lib\Twig\Environment.php(347): Twig_Template->render(Array)
#6 C:\wamp64\www\west\craft\app\services\TemplatesService.php(248): Twig_Environment->render('503', Array)
#7 C:\wamp64\www\west\craft\app\controllers\BaseController.php(74): Craft\TemplatesService->render('503', Array)
#8 C:\wamp64\www\west\craft\app\controllers\TemplatesController.php(200): Craft\BaseController->renderTemplate('503', Array)
#9 C:\wamp64\www\west\craft\app\framework\web\actions\CInlineAction.php(49): Craft\TemplatesController->actionRenderError()
#10 C:\wamp64\www\west\craft\app\framework\web\CController.php(308): CInlineAction->runWithParams(Array)
#11 C:\wamp64\www\west\craft\app\framework\web\CController.php(286): CController->runAction(Object(CInlineAction))
#12 C:\wamp64\www\west\craft\app\framework\web\CController.php(265): CController->runActionWithFilters(Object(CInlineAction), Array)
#13 C:\wamp64\www\west\craft\app\framework\web\CWebApplication.php(282): CController->run('renderError')
#14 C:\wamp64\www\west\craft\app\framework\base\CErrorHandler.php(368): CWebApplication->runController('templates/rende...')
#15 C:\wamp64\www\west\craft\app\etc\errors\ErrorHandler.php(281): CErrorHandler->renderError()
#16 C:\wamp64\www\west\craft\app\framework\base\CErrorHandler.php(351): Craft\ErrorHandler->renderError()
#17 C:\wamp64\www\west\craft\app\framework\base\CErrorHandler.php(218): CErrorHandler->renderException()
#18 C:\wamp64\www\west\craft\app\etc\errors\ErrorHandler.php(145): CErrorHandler->handleException(Object(Craft\HttpException))
#19 C:\wamp64\www\west\craft\app\framework\base\CErrorHandler.php(131): Craft\ErrorHandler->handleException(Object(Craft\HttpException))
#20 C:\wamp64\www\west\craft\app\framework\base\CApplication.php(750): CErrorHandler->handle(Object(CExceptionEvent))
#21 [internal function]: CApplication->handleException(Object(Craft\HttpException))
#22 {main}

Here's the code from where the exception is thrown-

<title>
    echo twig_escape_filter($this->env, $this->getAttribute((isset($context["global"]) ? $context["global"] : $this->getContext($context, "global")), "siteName", array()), "html", null, true);
    </title>

Template 503 -

<!doctype html>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="robots" content="noindex">

    <title>{{ global.siteName }}</title>

{{ global.siteName }}

siteName is automatically global... you don't need to include the global. prefix.

<title>{{ siteName }}</title>


Allow a dropdown custom field to be edit from a front end form I want users to be able select their own name prefix/title (Mr, Mrs, Dr. etc) as part of front end form that allows them to update their user details.

I have created a custom dropdown field and populated this with the titles that users can select. However, I'm having trouble displaying the dropdown on the front end form that allows users to select one option.

Any guidance on how I would go about achieving this would be helpful!

EDIT

This is what I had initially. However, this just prints each option as a separate select?

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

{% for option in dropdownField.settings.options %}
    <select id="honorific" name="honorific">
        <option value="{{ option.value }}">{{ option.label }}</option>
    </select>
{% endfor %}

Just to make sure I am on the same page, you have a select field within the control panel that is populated with the prefix/title(s)?

If that's the case, within your registration form, you could do something like this. Not tested, but should be close...

{% set options = craft.fields.getFieldByHandle('yourFieldHandle').getFieldType.options %}

{% if options|length %}
    <label for="prefix">Prefix</label>
    <select id="prefix" name="fields[prefix]">
        {% for option in options %}
            <option value="{{ option.value }}">{{ option.label }}</option>
        {% endfor %}
    </select>
{% endif %}

EDIT

Good catch by @Benjammin'. You will need the options part to actually access the values. I've updated my answer, give it another try.


Method to grab URL hash in Twig or craft.request? Is there a method or recommended way of grabbing URL hash parameters with Twig?

For example, if my URL is http://mysite.com/my-page#hash, is there a way to grab the hash?

This isn't possible because the #hash-part isn't even send to the server. See this question on StackOverflow: Can PHP read the hash portion of the URL?

But if you use ?hash=something instead, these functions will help you: getParam() and getQuery()


Feed Me: How to format JSON for a table field? I need to import some data into a table field.

What's the best way to format the data in my original JSON feed?

I've tried this:

[
    "first value",
    "second value",
]

and this:

[
    {"col1": "first value"},
    {"col2": "second value"},
]

But neither of those JSON formats seems to be importing the data properly.

How should I format my data in JSON, so that it may be imported into a table field?

MORE INFORMATION

I've read these docs, but apparently I'm still doing something wrong.

My current JSON looks like this...

"altTitles": [
    {
        "title": "Morke"
    }
]

Where altTitles is my table field, and title is the only column.

I believe I've properly mapped the field...

Yet for whatever reason, the table fields are not populating...

You can do this a number of ways, and essentially just need an array-like structure to represent the rows.

"Table": [
  {
    "one": "Option1",
    "two": "Option2"
  },
  {
    "one": "Option3",
    "two": "Option4"
  }
]

This issue might be related to your feed however. In the dropdown, you have <altTitles> eg: - there should be a value from your feed in there. According to your example, you should also be mapping to altTitles/title.

My example above shows:


How to apply a discount on cart weight ? I'd like to create a discount that is applied based on the weight of a cart.

I'm working with bottles & boxes of wine, so a bottle of wine has a weight of 1kg & a box of wine can have a weight of 6kg (6 bottles) or 12kg (12 bottles).

I need the discount to only apply when a customer from a certain customer group purchases 12 or more bottles of wine, but can't work with quantity because a quantity of 1 could be 1 bottle of wine or 1 box of wine (which could contain 6 or 12 bottles), hence my desire to apply the discount by weight of the entire cart instead.

I think the best way to do this would be to set up a rule for the customer group, apply it to all products & set a minimum of 1 to ensure that the rule always runs & then add logic in a plugin that fires on some or other discount event that then checks the weight and applies or doesn't apply the discount.

Something like:

craft()->on('commerce_discounts.onBeforeMatchLineItem',
  function($event){
    if( cartWeight >= 12){
       return;
    }else{
       $event->performAction = false;
    }
 });

But I'm having trouble figuring out which event to use & how to put all the pieces together to achieve my desired result.

Any pointers in the right direction would be greatly appreciated.

It would probably be best to make your own shipping adjuster. I have attached an example plugin here: http://jmp.sh/wC95ZPj

The adjuster plugin I have linked can look at the weight of the cart, it's line item contents, and then reduce (or add to) the shipping costs after the regular shipping calculations are run.


Populating craft database from non-craft data Is it possible to populate the Craft content database table with entries from a non-craft database (which isn't a live database, it's a local copy). I've set up my channels etc for the incoming data which corresponds to the data from my other db.

Brad is absolutely correct. Better off trying for an existing plugin (we have one in the works over here for very robust Wordpress migrating!!!), but here are a few steps to get you started:

Move the non-craft database tables over to a staging server with your craft tables, and rename them with a craft_ prefix (or whatever you have set as your prefix in config).
Query for the entries

$query = craft()->db->createCommand()
->select('*')
->from('your-non-craft-post-table')
->where('any conditions')
->queryAll();

Loop through the query results, creating new Entry Models and assigning columns to your fields.

//Go through each $query result
foreach($query as $old_entry){
    $entry = new EntryModel();
    $entry->sectionId = your_craft_section_id;
    $entry->authorId = your_craft_user_id;
    $entry->postDate = $old_entry['old_post_date_column'];
    $entry->enabled = true;

    //This is the fun part - mapping content to your custom fields
    $entry->getContent()->title = $old_entry['old_title_column'];
    $entry->getContent()->yourCustomFieldHandle = $old_entry['column_to_map_to_custom_field'];

    //Use the Entries service to save the new entry
    $success = craft()->entries->saveEntry($entry);
}

Obviously there is more to it than that...mapping user accounts, categories, tags, assets, etc. But hopefully this will help you get started!


How can I return data from a Matrix field in random order? I have a slideshow with slide data managed in a Matrix field. How would I go about randomizing the data returned in the template so the slides show in a different order on each page load?

You can either have MySQL select the blocks in a random order using RAND():

{% set blocks = entry.myMatrixField.order('RAND()') %}

Or you can shuffle the blocks from Twig instead, using Craft’s shuffle() function:

{% set blocks = shuffle(entry.myMatrixField) %}

There’s not a major performance difference either way in most cases, however if you also want to limit the number of blocks, each method will give you different results.

For example, this will pick 3 blocks at random out of the entire set:

{% set blocks = entry.myMatrixField.limit(3).order('RAND()') %}

But this will always pick the first 3 blocks, and those 3 blocks will be displayed in a random order:

{% set blocks = shuffle(entry.myMatrixField.limit(3)) %}


Force donation purchasable to replace donation of the same amount in cart I'm using Craft Commerce Lite and the donation purchasable. I don't want users to ever be able to add more than one donation to the cart. Using the Lite edition stops users from being able to add donations with different amounts since those would need two line items. In the docs for Adding the Donation to the Cart

Customers can add more than one donation to the cart, but the line item will be replaced if the optionSignature remains the same, just like it does for any purchasables added to the cart. (For example, two donations added for the same amount will be consolidated into one line item with a quantity of 2.)
Once the donation is in the cart, the donation amount can also be updated using the standard line item option update form. You would normally hide the qty field, because while it continues to work it’s most common for customers to donate a single amount.

How do I disable this consolidation feature?
Adding <input type="hidden" name="options[random]" value="{{ random()|hash }}"> works, but that displays in the CP so it's not clean.
I also tried showing a commerce/cart/update-cart form on the donation page that redirects to the payment page (there's no user viewable cart on this site) when the cart isn't empty, it works, but it seems like this shouldn’t be necessary. It also can show the user incorrect data if they enter an invalid donation amount, switch to a valid amount, go to the payment page, and then hit the back button. The invalid update cart form will be shown.
What's the simplest way to streamline all of this this?

Anywhere you have a add donation to cart form, you could check if the donation is in the cart and update it instead:
{% set cart = craft.commerce.carts.cart %}

<form method="post" action="">
    <input type="hidden" name="action" value="commerce/cart/update-cart">
    {{ csrfInput() }}
    {% if cart.lineItems %}
        {% set donation = cart.lineItems|first %} <!-- since we know only the donation is in the cart -->
        <input type="hidden" name="cartUpdatedNotice" value="Donation updated.">
        <input type="hidden" name="lineItems[{{ donation.id }}]" value="{{ donation.id }}">
        <input id="donation-amount" type="text" name="lineItems[{{ donation.id }}][options][donationAmount]"
               value="{{ donation.options.donationAmount }}" placeholder="10">
        <input type="submit" value="Update Donation"/>
    {% else %}
        <input type="hidden" name="cartUpdatedNotice" value="Donation added.">
        <input type="hidden" name="purchasableId" value="{{ craft.commerce.donation.id }}">
        <input id="donation-amount" type="text" name="options[donationAmount]" value="" placeholder="10">
        <input type="submit" value="Add Donation"/>
    {% endif %}
</form>


Login form error messages I am looking for some advice on how best to add error messages (eg incorrect password / username) on a custom Login form.

Tried the usual routes for documentation but have seen nothing as yet - all help appreciated.

Dan

Any login validation errors are passed back to the form and available via the Twig {{ errorMessage }} variable.

You can see an example of how to display them on a login form here: http://buildwithcraft.com/docs/templating/login-form


Load balancing nodes configuration (3 Craft nodes behind F5 Big-IP) Craft deployed and working well on one node.  Now looking into load balancing 3 nodes behind a LTM device (F5 Big-IP).  The server environment is the following:

a GlusterFS replicated "shared" file-system mounted on the 3 nodes
Craft app is installed in the shared file-system (so is the webroot and all common files, but Craft itself is outside of the webroot).
a replicated MySQL database cluster hosting the Craft database (Percona xtraDB Cluster)
Each node is configured with PHP ZendOpcache and Nginx.

Currently, Craft Vhost is enabled only on one node because I have the following questions:

What should I do with the "storage" folder.  Currently, it's on the shared file-system, so all the node share the same folder, is this an issue, i.e. should I move it to a local file system and set CRAFT_STORAGE_PATH accordingly?
overridePhpSessionLocation: I set to true (so it does not write to 'storage/runtime/sessions'
CacheMethod, is this for OPcaching or user data caching?  (Currently ZendOpcache is configured at the PHP level, but I still see entries in the storage/runtime/cache folder)

Any pitfalls I should be aware or specific configuration needed for a load-balanced set of Craft instances?

Thanks.

Update:

Am I allowed to change my mind on this? :)

My original answer didn't take into account storage/logo, storage/backups and anything else that a plugin might be storing in the craft/storage folder.

So it's probably going to be much cleaner and safer to have CRAFT_STORAGE_PATH point to the shared file system.

I would set CRAFT_STORAGE_PATH and have them write to their local file systems.  That way you have independent logs for each web server.
With that set to 'true', the PHP session files will go to whether PHP/nginx are configured to send them.
The cacheMethod config setting it purely for data caching. It's set to 'file' by default which will write to craft/storage/runtime/cache, but you can change that to some other caching method.

Actually, if you do #1, then 2 and 3 would resolve themselves, no? You'd want to make sure that your load balancer has stick sessions enabled so that a user would get sent to the same web server for subsequent requests.


How to UNION Craft blog articles array with Social Media plugin (non craft table) I'm looking for a way to merge blog article entries with social media entries (non craft table). The idea is to output both entry types together from the same array, this will allow me to order all entries by date.

The social media plugin that I have put together queries the non-craft social media table but I need a solution to UNION the two tables together.

How would I go about doing this within Craft?

My table structure for craft_archived_social_items

id int
unique_id varchar(255) NOT NULL,
account varchar(255) NOT NULL,
category varchar(255) NOT NULL,
title text NOT NULL,
content text NOT NULL,
image varchar(255) NOT NULL,
link text NOT NULL,
date datetime NOT NULL,
status varchar(255) NOT NULL DEFAULT 'inactive',
date_cached datetime NOT NULL,

PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

My service file is currently using the db->createCommand to query the table

    $social_items = craft()->db->createCommand()
        ->select('title, content, date, category, id, account, unique_id, image, link')
        ->from('archived_social_items')
        ->offset($offset)
        ->limit($counter)
        ->order('date desc')
        ->queryAll();

More of general guidance than an answer, but I'd look into creating a custom ElementType and overriding the modifyElementsQuery in your ElementType to latch onto the main query Craft uses to grab elements.  From there you'd be able to join in whatever other data you need.


How can I show multiple entry types in the same page I have two entry types in a same structure, I want to get some values from these two types in a same page. Is it possible?

I tried:

{% set entry = craft.entries.section('businessProfile').type(['type1','type2]) %}

And the entry.type still shows only one type. Thanks in advance!

And just to make sure: you are iterating over the results, right? because it actually should be:

{% set entries = craft.entries.section('businessProfile').type(['type1','type2]).all() %}
{% for entry in entries %} ...
... Here {{entry.type}} should be different
{% endfor %}


Linking a page in a multilanguage website Currently i am using this approach for linking to a entry(single page) of a structure, for example a cta button on the homepage which links to the contact page.

{% cache %}
    {% set single = craft.entries.id('14').first() %}
    {% if single %}
        {{ "Neem contact op" | translate }}
    {% endif %}
{% endcache %}

Or for example a "read more" button next to some results of some structure without a index. Because thats set in the structure "pages" as a entry type.

But i feel like this is somewhat hacky..
Of course you can link to a page like so:

{{ url('contact') }}"

or

href="{{ "contact"|translate }}

But url() doesn't translate and what if the slug is changed by a user of the CP.
So what is the usual way to get around this, are there any best practises?
So far i can only think of linking all the individual buttons/links to entry fields in the CP, which the user has to fill in.

You could write a Twig macro to get the localized URL from an entry ID. Import it from _layout.html for example.

{% macro urlFromId(entryId) %}
    {%- set entry = craft.entries.id(entryId).first() %}
    {{- entry ? entry.url -}}
{% endmacro %}

{% import _self as macros %}

Then use this more concise code everywhere in your templates.

{{ "Neem contact op"|t }}


How can I populate a dropdown via Globals? I have a registration form that has a dropdown that allows the user to choose a color. This form input will be the same across all pages that allow a user to register.

So, I thought using a Global to populate that dropdown would make sense as the client could manage the color choices. In essence, this is what I'm trying to do:

{% if colorOptions.color | length %}

        Color Option
        <select id="colorOption" class="form-control">
            <option value=""> Select</option>
            {% for option in colorOptions.color %}
                <option value="{{ option }}">{{ option }}</option>
            {% endfor %}
        </select>

{% endif %}

But I'm struggling with what kind of field type color needs/should/could be within the colorOptions group.

You could always just use a Table field, you could then just add a colour to each row, this allows you to give a descriptive name to each colour but also keep the value as the hex or whatever format your colours are in...

Then its easy to loop like:

{% for option in colorOptions.color %}
   <option value="{{ option.value }}">{{ option.name }}</option>
{% endfor %}

Of course you dont have to have two columns, you could just have the one and loop over the HEX values or whatever, it's totally up to you and how you want to do it. But personally I think the table field is a solid way to go!


Filter entries by date range in plugin In CP, there is the widget called "Recent Entries". You can pull out entries from your sections, limit a count of entries.

I'm building a plugin that has very similar widget to recent entries widget.

THE GOAL

Filter entries by date range within widget.

I was only able to set a fixed date range within widget's class ...

// To get entries from a specific date range
$criteria->postDate = array('and', '> 2016-06-10 23:59:59', '< 2016-06-30 00:00:00');

Have a look at the Dashboard Widget section in the Craft docs. It’ll walk you through defining your “Start Date” and “End Date” settings, and how to render the settings template, where you can make use of the dateTimeField Twig macros to render the Date and Time fields.

An important step to make your widget save the values properly to the database. You’ll have to convert them DateTime objects to string before save in the prepSettings(), and convert them back in the getSettingsHtml() before rendering the settings template. There’s this convenient DateTime::createFromString() helper that you should proabably want to use.

Another thing that you’ll probably find useful is the mySqlDateTime() method that prepares a DateTime object for use in criteria model parameters.

Have a look at P&T Store Hours plugin, that shows some of these methods in code context.


Import Large Number of records - 300,000 Is there a best way to import a large number of records - About 315000 to be precise. The company provides a service which is only available to specific addresses.
So I had set up a section to contain the addresses where the service is available. With fields - Address1, Address2, Postcode, Town
Just got the data to import and discovered that there is 315000 records - I got the data in csv format. Tried with Import and FeedMe Plugins but run into memory issues straight away. CSV file is nearly 15MB and when converted to XML for feedme it is 52MB

So what are my options for importing?

Could I ramp up the resources on a Digital Ocean Server for the time that the import is required?

Or should I try another method to try this lookup service on the website?

Increasing your servers available memory and execution time can help increase the number of items you can import in a single file, but with the number of records you're dealing with it's likely, you'll need to break your import into multiple steps.

Consider breaking up the import files into smaller batches that can be imported within your environment's memory limit. As a first step, you could manually test for a limit of the max number of entries you can import.

This could be done by going back to the source that provided you the large CSV file and asking if they can provide you several CSV files of 50,000 records each, 10,0000 records each, or whatever number of records you found to be your limit. You may end up with lots of files and that's fine.

You could potentially also write a script that takes your CSV file or converted XML file and breaks it into several files on your own.

I'm not familiar with all the options in the Import and FeedMe plugins. My firm develops Sprout Import and we support batch importing multiple files. Sprout Import also requires data to be in a specific JSON format, which would likely require writing an additional script in your case as well.


Make fields conditionally required Is there a way to make fields conditionally required?

I have a toggle field that is used to hide or show a section (see screenshot: "Section 3 enabled"). If the section is shown (toggle: on), the fields need to be filled. If the section is hidden (toggle: off), the  fields can be left empty.
Note that the section's fields can be of any type (text, matrix, entry, assets, ...).

I already tried the plugin Reasons, and unfortunately it's not what I need because it will still return a validation error for the hidden but required fields.

My Craft CMS version is 2.6.2983.

There is currently not a native way to do this in Craft. You should consider adding a thumbs-up/comment to this feature request if you'd like to see it, though.


Mark a cart as complete from a plugin How do I mark a cart as complete from a plugin?
I tried setting isComplete = 1, it gets moved to orders but it is still a cart.

When you take a look at the BookingsController you can see exactly what happens when you need to mark an order as complete

public function actionCompleteOrder(): Response
{
    $this->requireAcceptsJson();
    $orderId = Craft::$app->getRequest()->getParam('orderId');

    $order = Plugin::getInstance()->getOrders()->getOrderById($orderId);

    if ($order && !$order->isCompleted && $order->markAsComplete()) {
        $date = new \DateTime($order->dateOrdered);
        return $this->asJson(['success' => true, 'dateOrdered' => $date]);
    }

    return $this->asErrorJson(Craft::t('commerce', 'Could not mark the order as completed.'));
}

So all you need to do is

$order->markAsComplete();

Which does

$this->isCompleted = true;
$this->dateOrdered = Db::prepareDateForDb(new \DateTime());
$this->reference = 'xxxx';
$this->orderStatusId = y;

You need to set the 4 parameters above to complete an order correctly, not just the isComplete


SEOmatic Errors in Debug - Can they be fixed? SEOmatic throws up a lot of errors in the debug. Most of the errors seem to be with fields I haven't filled in as they are not required like Facebook App ID etc.

Is this an issue or do I just ignore? Disabling the plugin reduced the number of database requests to 50 instead of the 140 being shown and improved the speed slightly so just wondering if the errors are creating performance issues?

These errors will only appear with devMode on, and they are intended to help you diagnose potential problems with the meta tags and other information that you are rendering.

These are all soft errors, though. You can safely ignore them if they are a result of you not filling in things that you don't intend to fill in.

You can read more about it here: https://github.com/nystudio107/craft-seomatic#meta-object-validation


Craft 3 Registration Form Success Redirect Is it possible to redirect the user to a specific page after they successfully complete the front-end registration form (before activation)? I've tried adding:

<input type="hidden" name="action" value="users/save-user">
<input type="hidden" name="redirect" value="registration-successful">

But it currently just redirects back to the home page. I really need to redirect the user to a page that prompts them to check their email for the activation link. It currently feels a bit broken.

Am I missing something?

Any help much appreciated.

The URL needs to be hashed. Try using {{ redirectInput('registration-successful') }}
As documented here: https://docs.craftcms.com/v3/dev/functions.html#redirectinput-url


Using the Search Filter with Entry Fields I've got an entry page and at the bottom I would like to show related products and then underneath that, related offers.

As the main page entry isn't assigned to any categories I've tried to use a custom field and the search param to show 'related' entries and this works fine, BUT, when I try and make it dynamic, it doesn't pull through. I'm guessing this is some kind of parsing order and that the custom field isn't parsed before the loop is created.

This works fine:

{# To exclude certain category #}
{% set categories = craft.categories.slug('not preferential') %}
{% set entries = craft.entries.section('offers').search('A1').relatedTo(categories) %}

But this doesn't when I try and make it dynamic:

{% set categories = craft.categories.slug('not preferential') %}
{% set entries = craft.entries.section('offers').search('{{ entry.model }}').relatedTo(categories) %}

I've also tried setting the variable first but it doesn't work:

{% set range = "{{ entry.model }}" %}
{% set categories = craft.categories.slug('not preferential') %}
{% set entries = craft.entries.section('offers').search(range).relatedTo(categories) %}

Hopefully this makes sense. Still in the basics I suppose of Craft but worth the learning curve.

{% set range = entry.model %}
{% set categories = craft.categories.slug('not preferential') %}
{% set entries = craft.entries.section('offers').search('title:' ~ range).relatedTo(categories) %}

If you are only searching titles I would add that in the search params as it will help the effectiveness of the search. In addition when setting a variable you do not need to use the {{}} syntax as it will automatically use the string representation of the the field if it is a text field.


Multi-site - Setup remote asset volume to store assets on a per site/folder basis We've got a Craft 3 (3.1.25) multi-site setup with three sites:

Corporate (default)
Brand A (brandA)
Brand B (brandB)

For this install there's 3 users setup with the following access to the sites.

User 1 (Admin - all sites)
User 2 (Brand A)
User 3 (Brand B)

The site has an 'Images' asset volume (remote - S3) for images to be uploaded into.

What we're trying to work out is how to make images uploaded into an asset volume specific to the site they relate to and make sure users can only see/edit images relating to the site they have access to.

It would be even better we could store them like below:

S3/images/brandA/
S3/images/brandB/

When you access the asset folder via the CP you can see the site switcher at the top - but when uploading via this area the assets are automatically appearing under all sites (when logged in as User 1).

This is especially important for the 'Products' section we've created that uses a 'Product Image' asset field that we've created. We want User 2 & 3 to see only the images relating to the site that the entry they're editing is related to.

There is a similar question below but doesn't entirely answer how to achieve the above.
How to setup assets in multi site where each site should have its own assets?

You do this by adding {site.handle} to the fields upload location.

This will create the subfolders like you mentioned. It will also create a default subfolder for the default site, but that's ok.

Note: Make sure to rebuild your Asset Indexes in Utilities > Asset Indexes after you add the subfolder path.

That should ensure that they point to the subfolder next time you go to upload an image. I had trouble with it still pointing to the root volume directory, but that fixed it.

Also, all your current images will still work, but they will no longer show when adding/editing assets (As they are still in the root folder) You will need to copy these to the appropriate subfolder and rebuild your asset indexes (or upload again) if you want them to appear. Don't delete them from the root folder until you have updated the entries that access them.

I haven't been able to find a way to dynamically change the volumes. As far as I know it is not possible, but using subfolders works well.


Get address data of Order This used to work in commerce 1: {{ order.billingAddress.firstName }} within a for loop of craft.orders.all().

No longer in Commerce 2. If I use this syntax I get

  Impossible to access an attribute ("firstName") on a null variable.

But when I do: {{ dump(order.billingAddress) }} then I see the firstName and it is clearly not null:

object(craft\commerce\models\Address)#2512 (35) {
  …
  ["firstName"]=> string(6) "Sophie"
  …
}

Why can't I access this? Also | first doesn't help. I also tried several versions with customer or primaryBillingAddress as mentioned in the changes doc But no luck so far.

My guess is that one of the orders in the array of orders has a null billingAddress, and the dump you did was for just one of the orders.

If you are going to access the address firstName through the order's billing address, but the address might be null you need to do a check.

You can either do:

{% if order.billingAddress %}
 {{ order.billingAddress.firstName }}
{% endif %}

Or use the null-coalescing operator in twig:

{{ order.billingAddress.firstName ?? '' }}

https://twig.symfony.com/doc/2.x/templates.html#other-operators

Hope that helps.


How to access Craft classes within plugin? I'm working with dates and want to use Craft's Class reference to speed things along but am struggling to figure out how to use them. For example, this works as expected and returns the siteUrl:

return craft()->getSiteUrl();

But how do I format something more complicated? Say I want to format a date variable, how do I use any of these classes: https://craftcms.com/classreference/etc/dates/DateTime? I've tried all sorts but generally end up with:

Craft\WebApp and its behaviors do not have a method or closure named...

craft() is just a wrapper for the singleton instance of the WebApp class and you can call its methods directly like you're doing with getSiteUrl().

You'll notice that getSiteUrl() actually isn't in that list of WebApp methods, though.  That's because it (along with other methods) live in AppBehavior, which is a Yii behavior and is very similar to a trait that gets it's methods "mixed-in" to the class that wants them (in this case, WebApp).

Stand-alone classes that are't attached to WebApp (craft()), like DateTime, you can instantiate like:

$dateTime = new DateTime();
$dateTime->format(...);

Or if they are static classes (like the Helpers), you can use directly like:

$lowerCaseString = StringHelper::toLowerCase('ABC');


Multiple sections, limiting the structure by depth I'm trying to pull entries from multiple sections for a list on a page, which works fine:

{% if section is not defined %}
    {% set section = 'events,checklists,opinionPieces,whitepapers' %}
{% endif %}

{% set params = {
    section: section
} %}

The problem I'm having is that the 'checklists' section is a structure with child entries allowed, and I only want the top level entries. I hoped I could get away with this:

{% if section is not defined %}
    {% set section = 'events,checklists,opinionPieces,whitepapers' %}
{% endif %}

{% set params = {
    section: section,
    level: 1
} %}

But that then only returns checklists and whitepapers, presumably because they are structures and the other two are channels.

Is there an easy way of getting this to work, or would I have to pull all entries separately into an array?

or would I have to pull all entries separately into an array?

This one.

100% untested, but something like this should work:

{% set ids = [] %}

{% set ids = ids|merge(craft.entries.section('events,opinionPieces,whitepapers').ids()) %}

{% set ids = ids|merge(craft.entries.section('checklists').level(1).ids()) %}

{% set entries = craft.entries.id(ids).find() %}


remove text from Redactor Was just wondering how (or if) you can remove text from redactor:
(there might be a better way to deal with this all together - im option to suggestions)
the use case is I just want to have the video option (youtube links) for a specific field.
I have altered my config.json file for redactor:
 {
  "buttons": ["video"],
  "plugins": ["video"],
  "linkNewTab": true,
  "toolbarFixed": true
}

But this still allows text to added (so maybe this is not possible?):

If you just want to add YouTube or Vimeo videos to your page, you'd be better off using a dedicated plugin such as Embedded assets.
It allows you to treat videos as regular assets.


Cache tag won’t cache in a template The image tag shown below prevents my {% cache %} tag from caching the loop it's in. If I remove the image tag or change the data-resource attribut to a normal src, it works fine. The class="lazy" and data-original are from the jQuery plugin Lazy Load 1.9.3. Regardless, the page still renders fine. Does anyone have any ideas what's wrong?

Thank you for your help for a first-time poster.

<img class="lazy" data-original="{{ image.getUrl(transform) }}" alt="{{ project.title }}" />

EDIT: Now showing just the code that seems to be the problem rather than the whole loop.

The {% cache %} tag will detect if there are any ungenerated image transforms within it. If there are, it will hold off on caching the template until the next request, so that the temporary image URL won’t get cached.


Freeform - Grouping fields from the form builder I'm using Freeform for a fairly large form. I'm trying to use a combination of the form builder (Composer) feature and custom HTML and I'm not sure it's possible! I'll give you the scenario and you can tell me if I need to write it all without the Composer.

I want the desired layout I have in the form builder but I need to wrap some field rows (in a <fieldset> or <div> so I can hide them (so I can show them when certain radios are checked).

I don't see how I can add this to the middle of the {% for row in form %} loop as there will be 10+ rows that need wrapping, including custom HTML field for the legend. i.e. there is no grouping mechanism I can check against that I can see.

I'd love to hear any suggestions so I can still leverage some of the form builder :)

Many thanks.

It sounds like what you would ultimately need here is some sort of grouping of fields feature, which Freeform currently does not have. So I would lean towards saying there isn't a way you can do this purely in Composer, unless of course you had a very stripped down Formatting template, and relied heavily on HTML blocks inside Composer to simulate what you're after. :)


Passing params to Freeform’s `render` tag Can you pass params to Freeform’s render tag ?

E.g. {{ entry.form.render({ entry: entry }) }} ?

Trying to access entry data inside formatting templates...but I get Variable "entry" does not exist error.

That isn't possible right now, the things you can pass to the render tag are listed here under customAttributes.

We might, however, add such functionality in the future.


Paginate entries ordered by a custom date field I'm trying to paginate a bunch of events based on their start date. The order part is working correctly, but each consecutive page just loads the exact same entries. My code:

{% set events = craft.entries().section('events').limit(2).order('startDate asc') %}
{% paginate events as pageInfo, pageEntries %}

{% for event in events %}
    // Event content
{% endfor %}

{% if pageInfo.prevUrl %}Previous Page{% endif %}
{% if pageInfo.nextUrl %}Next Page{% endif %}

What am I missing?

Pulling it all inside paginate fixed it:

{% paginate craft.entries().section('events').limit(2).order('startDate asc') as events %}

{% for event in events %}
    // Event content
{% endfor %}

{% if paginate.prevUrl %}Previous Page{% endif %}
{% if paginate.nextUrl %}Next Page{% endif %}


Plugin Settings: Attribute Types, and how to receive I have two small arrays I want to store in my plugin settings, though I'm stuck at what the AttributeType:: should be, as well as how to retrieve the actual values. The arrays I want to store look like:

'active_features' => [
     'change_team' => false,
     'change_series' => false,
     'change_user_settings' => false,
],

In my plugin file (pluginHandlePlugin.php) I see I can do something like this:

protected function defineSettings()
{
    return array(
        'pluginSettings1'   => array(AttributeType::Mixed, 'active_features' => array('change_team' => false, 'change_series' => false, 'change_user_settings' => false)),
    );
}

And getting those values through the service file works like:

public function getPluginSettings()
{
$plugin = craft()->plugins->getPlugin('pluginHandle');
$settings = $plugin->getSettings();

return $settings;
}

Which works, and returns the object for me. However I'm unable to get to the actual key/values from the original array, I've tried:

return $settings->pluginSettings1;

But that returns NULL

So, how do I retrieve the keys/values, and also, what is the best AttributeType:: to set for this? I've looked around for AttributeType:: options and details but didn't find anything.

Any help would be GREAT! Thanks!

As far as accessing the value of the setting, try:

$plugin = craft()->plugins->getPlugin('pluginHandle');
$plugin->getSettings()->getAttribute('pluginSettings1');

If you need to access your settings throughout your services file you might also create a global variable.

class PluginHandleService extends BaseApplicationComponent
{
    public $settings = array();

    public function __construct()
    {
        $this->settings = $this->initSettings();
    }

    private function initSettings()
    {
        $plugin = craft()->plugins->getPlugin('pluginHandle');

        $settings = array();
        $settings['pluginSettings1'] = $plugin->getSettings()->getAttribute('pluginSettings1');

        return $settings;
    }

    // other service methods

}

Which you can then access like this:

$this->settings['pluginSettings1']

Thanks to André Elvan for help on setting up a constructor.

Update

As far as assigning a value however, I think you may have to use the 'default' property. In the past I have successfully done this with strings like so:

protected function defineSettings()
{
    return array(
        'pluginSettings1' => array(AttributeType::String, 'default' => "value"),
}

I presume you could do the same with 'Mixed', although never tried:

protected function defineSettings()
{
    return array(
        'pluginSettings1' => array(AttributeType::Mixed, 'default' => 'active_features' => array('change_team' => false, 'change_series' => false, 'change_user_settings' => false))),
}

It might make more sense to break the array into individual settings, however.

protected function defineSettings()
{
    return array(
        'change_team'          => array(AttributeType::Bool, 'default' => 'false',
        'change_series'        => array(AttributeType::Bool, 'default' => 'false',
        'change_user_settings' => array(AttributeType::Bool, 'default' => 'false',
    ),
}


Using Feedme to import data from ExpressionEngine 2 Im trying to get started using the Feedme plugin to import data from EE. Im working with the json template provided here: https://sgroup.com.au/plugins/feedme/guides/migrating-from-expressionengine

Ive been able to import data but when I include a rich text field Im getting errors – eg.

IGS Updates Test: Unexpected token "punctuation" of value ":" ("end of print statement" expected) in "
On Thursday the 1st of December last, the Limerick Chapter of the Irish Georgian Society opened the Christmas season with a fundraising Christmas party. The event was held in The Georgian House, No. 2 Pery Square, in Limerick. This historic house was built between 1835 and 1838 by Pery Tontine Company, forming part of a terrace known collectively as the Tontine Building. It was the last Georgian terrace built in Limerick. The building was restored by Limerick Civic Trust, with the support of the Irish Georgian Society and opened in 1999. We were delighted to give our guests an opportunity to enjoy an evening in the magnificent first floor rooms of the house and we thank Limerick City and County Council for use the venue.

Some of the entries are importing, but the majority are not. Any help / pointers appreciated...

I had a similar error when I was doing an import; some entries made it and others didn't. It ended up I had an error in my JSON file so be sure to lint it first.


Contact Form on Windows Azure I’m having some trouble getting a contact form to submit and function correctly. The live instance of the site is on Windows Azure but my staging and local versions are unix. Is there any reason for Pixel & Tonic's Contact Form plugin not to function because of the platform? Perhaps it's a lower level issue with PHP setup or my Craft configuration on Windows.

Any help much appreciated.

No, there's no reason why Contact Form would fail on Windows Azure specifically.

If I had to guess, I bet you've got your email settings "Protocol" configured to use PHP Mail. That particular method of handling email is notoriously unreliable, which would explain why it's working on some of your servers but not others.

I'd highly recommend switching to an SMTP service. I've personally had a very good experience with Mailgun, and I've heard others rave about SparkPost. Regardless of which service you choose, SMTP is far superior to PHP mail.

I'd also suggest reading the full Craft article on Troubleshooting Email Errors...


Add an assets field to a field type plugin template I have a plugin that creates custom field types to extend crafts default field types.

The custom field types hold several different form input fields. For example the field may be called video and contain a title field, caption field, video slug field and a thumbnail field.

When the plugin is installed these fields appear in the default field dropdown list.

I now want to change the html template in the plugin so that the thumbnail field makes use of the default assets field built into craft. So I'd like to know if there is a way to add an asset field to the twig template in my plugin. The plugin does not need to use it's own database so can make use of the existing assets database.

Is this possible?

This is what I have so far.

In the fieldtype class:

public function getInputHtml($name, $value)
{
    // If value is null, we set it to an array to prevent template errors
    if(!$value)
    {
      $value = array();
    }
    return craft()->templates->render('customfieldtypes/video', array(
        'name'  => $name,
        'value' => $value
    ));
}
public function prepValueFromPost($value)
{
    return json_encode($value);
}

public function prepValue($value)
{
    return json_decode($value, TRUE);
}

In the video template:

Title
<input class="text nicetext fullwidth" type="text" name="{{ name }}[title]" value="{% if value %}{{ value.title }}{% else %}{{ null }}{% endif %}">

Description
<textarea class="text nicetext fullwidth" type="text" name="{{ name }}[description]">{% if value %}{{ value.description }}{% else %}{{ null }}{% endif %}</textarea>

Caption
<input class="text nicetext fullwidth" type="text" name="{{ name }}[caption]" value="{% if value %}{{ value.caption }}{% else %}{{ null }}{% endif %}">

Link
<input class="text nicetext fullwidth" type="text" name="{{ name }}[link]" value="{% if value %}{{ value.link }}{% else %}{{ null }}{% endif %}">

Thumb
<input class="text nicetext fullwidth" type="text" name="{{ name }}[thumb]" value="{% if value %}{{ value.thumb }}{% else %}{{ null }}{% endif %}">

I don't know enough about your end goal, but it almost sounds like what you're trying to do is what Craft already does very well. If you're just trying to save some time on Assets custom field creation, you might consider checking out FieldManager which lets you import/export fields across projects.


How can I access variables only declared in a template? I have a template inside a folder located in /templates that is not related to any entry or section:

/folder/test.html

{% extends '_layout' %}

{% set title            = 'myTitle' %}
{% set description      = 'myDescription' %}
{% set variable3        = 'hello' %}

{% block content %}

{{ title }}

{{ description }}

{% endblock %}

If I want to create a link to this page, I can use the url() function like this:

test

How can I access the variables declared on this page?

I don't think you can. I think the best way to access 'unattached' variables is to create a global field set and attach fields to it. That way you'll be able to access them anywhere.

{{ globalSet1.title }}
{{ globalSet1.description }}
{{ globalSet1.variable3 }}


Craft - suitability for membership/user registration and document upload/management I have created about 30 sites using Perch but find it somewhat frustrating, especially the support. Considering using Craft for my next project but can it handle:
Membership/user registration. Ie can i restrict certain sections of the iste to registered individuals or members?
Document management. The prospective client I am tinhking of using for my first Craft site also holds lots (~100) of mainly PDFs some for public use and others for members only. They need a fairly simnple way for the end usuer to be able to upload an keep them oraganised.

Would Craft satisfy these criteria?

Cheers - Mark

Should be no problem, Craft has extensive user management capabilities, check https://craftcms.com/features/users & https://craftcms.com/docs/users as well as solid asset management https://craftcms.com/features/assets

Log in to the demo https://demo.craftcms.com/ or do a local install, as long as your test domain has .dev in the url you can fully test Craft with purchasing a license.


Trouble with buildElementQuery customization I’m having a problem modifying an ElementCriteria with buildElementsQuery.
The results elements totally mixes locale results. I can only get english URL for example.

Here is a gist of my code.
https://gist.github.com/nicolasbinet/7e3835b13c3e9c21d2fa754243242b41

is there something particular to do when using buildElementQuery ?

Nicolas

I finally found that my problem was a SQL one not a CMS one. The where conditions grouping were the source of the problem.

Here is the final code I used.

https://gist.github.com/nicolasbinet/7e3835b13c3e9c21d2fa754243242b41


Craft 3 Command setup failing I've installed Craft 3 through composer and I'm attempting to work through the setup process within terminal:

MacBook-Pro:~ jamie$ ~/Documents/repos/__craft-3-beta/craft setup
Which database driver are you using? [mysql,pgsql,?]: mysql
Database server name or IP address: [localhost]
Database port: [3306]
Database username: [root]
Database password:
Database name: craft-three
Database table prefix:
Testing database credentials... failed: SQLSTATE[HY000] [2002] No such file or directory
Which database driver are you using? [mysql,pgsql,?]: mysql
Database server name or IP address: [localhost] 127.0.0.1
Database port: [3306]
Database username: [root]
Database password:
Database name: [craft-three]
Database table prefix:
Testing database credentials... failed: SQLSTATE[HY000] [2002] Connection refused

Notice that I get 2 different errors when using the IP add and localhost for DB_SERVER

I also cant get it working when manually filling in the .env file.

I'm using MAMP Pro on a Mac.

Any help on this will be greatly appreciated.

I was just confronted with this too. You most likely need to specify the unixSocket attribute for the MAMP Pro database. Take a look at my problem and solution at Craft CLI RC1 Connectivity Issues with MAMP Pro. Though if you're not able to get it working at all even manually I would definitely suggest resetting your root password in MAMP Pro as well.


Add Heap Analytics code to HTML I'm trying to add Heap Analytics as a tracking tool to my Craft site. From Heap's installation docs, I need to add this code snippet before the closing  tag. I'm pretty new to using Craft, can someone please explain how to go about doing this?

Code snippet:

<script type="text/javascript">
    window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])};
    heap.load("YOUR_APP_ID");
</script>

Heap installation instructions: https://docs.heapanalytics.com/docs/installation

All of the HTML that Craft uses to render your site lives in your craft/templates folder.

In one of the files in that folder, you're presumably defining your <head></head> tags.

You'd want to find where that is and insert your JavaScript analytics tracking code before the closing head as per the Head Analytics installation docs.

Probably worth a read through the Craft does to familiarize yourself with Craft concepts as well.


How can I link to a controller and return/redirect with data? We have a plugin that logs you in and out of a third party service.

Login is easy as we can POST to a controller action, but for logout, we have a link that goes straight to an action URL.

My problem is that I want to redirect back to the URL the user submitted from, with some data that we use in the view (a message to say you've logged out).

redirectToPostedUrl() is no good as this isn't a POST request, and from what I can see in the docs redirect() doesn't give us the option to add data and anything we add in craft()->urlManager->setRouteVariables() doesn't come through when you redirect.

And if we don't redirect or return, we get a 404 as the user is just left on the controller action URL, for which there is no template output.

$_SERVER['HTTP_REFERER'] should give you the url the request was coming from, which you can then pass in to the redirect function. You could add some URL parameters like ?message='Logged%20out!' and have your template check for the message existing and display it.

Be aware that this function comes with a small caveat from the PHP documentation:

  The address of the page (if any) which referred the user agent to the
  current page. This is set by the user agent. Not all user agents will
  set this, and some provide the ability to modify HTTP_REFERER as a
  feature. In short, it cannot really be trusted.

Here's the full documentation on the PHP $_SERVER array.

Edit: Other Option Using a Form

It might be a little bit clunky, but you would have the like to logout actually be a form submit. You could post the current URL to your controller and use craft()->urlManager->setRouteVariables(array('message' => $myMessageObject)); to send your message back to the user.

Form:

<form method="post" action="" accept-charset="UTF-8">
    <input type="hidden" name="action" value="{{ actionUrl('pluginHandle/controller/method', { return: 'this/page/url' }) }}">
    <input type="submit" value="{{ 'Log out'|t }}">
</form>

All you'll need is a little bit of CSS to style the submit link, or even somme javascript to trigger the form submit. If you wanted to get even fancier, you could AJAX the request and avoid the form all together.

Here's the documentation on posting to controller actions for reference.


Best approach for these redirects Working on a Wordpress migration at the moment, and all the post URLs are http://sitename.com/post-url, and I need to redirect them to their new home at http://sitename.com/article/entry-url. It's difficult because there obviously isn't a catch-all URL segment I can use because all posts use first segment. I could go the .htaccess route but there are >2k posts so that would be monstrous. There's a good Redirect Manager plugin where you can set a catch all template and then handle redirect in there, but I'd have to separately write a bunch of Route exceptions for other pages on the site. Anyone think of a good approach?

You can use Yii2 routing

    Event::on(
        UrlManager::class,
        UrlManager::EVENT_REGISTER_SITE_URL_RULES,
        function(RegisterUrlRulesEvent $event) {

        $event->rules[] = [
            'pattern' => '/<route:\S+>',
            'route'   => 'controller/to/redirect'
        ];
    }
);

And in your Controller

public function actionRedirect($route){
    if($entry = Entry::find()->oldUrl($route)->one()){ //<- insert your criteria to mach old with new URLs
        return $this->redirect($entry->getUrl());
    }
}


Plugin migration quandary When exactly are migrations run?  I wrote a migration that runs and does its job properly if I run it via the command line: yiic migrate up Seomatic but despite the version number and schema number being bumped, Craft doesn’t run the migration automatically`

Instead, my code dies because of a column that doesn’t exist in the db.  Shouldn't the migration be found and run before my plugin's code is?

Here's the version numbers returned by my plugin:

public function getVersion()
{
    return '1.0.5';
}

public function getSchemaVersion()
{
    return '1.0.1';
}

The previous installed version was '1.0.4' and '1.0.0', respectively.  Shouldn't this be enough for Craft to find and run the migration?

The migration is named:

m151225_000000_seomatic_addHumansField.php

...with a class of the same name, and again, it's found and runs properly if manually run it via yiic -- what am I missing?

Actually, everything was working fine with the migration.  The issue was that I had code inside of a Twig extension and inside of a rendering hook that was being executed before the migration tried to take place.

I wrapped it in if (!craft()->request->isCpRequest()) to only do its thing when it's a non-CP request (which is how it probably should have been anyway), and all is well.


Commerce 2: View all orders of a specific product? I'm looking to generate a sales report for a specific product

It should not be linked to a user's orders but just a general list of sales for that product and its variants

Is this possible?

Thanks

Yes it is possible but I don't think there is currently a plugin that will do it for you. There are a couple of reporting plugins in the store though I'm not sure whether they work with Commerce elements. Either way, I imagine you would need to write your own query.

There is a Commerce reporting plugin in the works from Foster Commerce but it appears to have been delayed. Going by the previous version that one should include the sort of report you want.

You could also roll your own plugin to generate the report or even, to be honest, create a Twig template which will output the data you want as a csv (or whatever).


Protect mutliple pages in a template folder I made a member area with multiple pages in a member-area template folder.
I d like to protecte all pages in this folder.

I know the isLoggedIn function to protect a page in a twig template.
http://buildwithcraft.com/docs/templating/craft.session

{% if craft.session.isLoggedIn %}
    Logout
{% endif %}

But what would be the best way to protect multiple pages ?

Put the control on _layouts/base or _layouts/site templates?
Note: And above that problem I got an ajax login form (I can make a dedicated login page if it is more simple and no choice)

You pretty much nailed it.

If you have multiple pages that require an authenticated session, then have all of those pages extend a template that uses something like {% requireLogin %}.


How do you install a plugin through the command line? I would like to install and enable plugins through the command line in a way that can be scripted, and which replicates the same steps as are taken when you install a plugin through the Craft control panel.

What commands are necessary to accomplish this?

Would this be enough?

composer require repo/plugin-name
./craft install/plugin plugin-slug

Will these two commands also install the plugin migrations, enable the plugin, and update the project.yaml file, just as if I had installed the plugin through the control panel?

Is it also necessary to run ./craft migrate/all? Or ./craft clear-caches/all?

Also, is it possible to install a specific plugin edition (e.g. Lite or Pro) through the command line?

Thanks!

Will these two commands also install the plugin migrations

There will be no migrations to run since it is a brand new installation.

  enable the plugin, and update the project.yaml file, just as if I had installed the plugin through the control panel?

Yes to both.

  Is it also necessary to run ./craft migrate/all? Or ./craft clear-caches/all?

Nope.

  Also, is it possible to install a specific plugin edition (e.g. Lite or Pro) through the command line?

Not natively. If a plugin has multiple editions, it will default to whatever is listed in their getEditions() method, which is typically going to be "Lite" first.


How to limit Expanded Search (plugin) query results for pagination? I'm using the Expanded Search plugin to display results of the search query with an excerpt of the text matching the query for each result. I want to limit the results to 10 per page, using pagination.

Does anyone know how I could limit the expandedResults from the Expanded Search plugin?

The pagination works with the following code, but the Expanded Search results does not respect the limit to entries and instead shows all the results without limit, on each paginated page (e.g. If I have 14 results, the pagination will have 2 pages available, but all 14 results are displayed across both pages):

Expanded Search Results

  {# Get the user's search query from the 'q' query-string param #}
  {% set searchQuery = craft.app.request.getParam('q') %}

  {# Fetch entries that match the search query #}
  {% set entries = craft.entries()
      .search(searchQuery)
      .orderBy('score')
      .limit(10) %}

  {# Paginate the entries #}
  {% paginate entries as pageInfo, pageEntries %}

  {# Get the search result excerpts from the Expanded Results plugin #}
  {% set expandedResults = craft.expandedSearch.search(searchQuery, { length: 155 }) %}

  {% if entries|length %}

      {# List search result entries #}
      {% for result in expandedResults %}

{{ result.entry.title }}

              {{ result.entry.url }}

          {% if result.matchedValue %}
{{ result.matchedValue | raw }}
{% else %}
No preview available
{% endif %}

{% if result.entry.dateUpdated %}{{ result.entry.dateUpdated | date("j F Y") }}{% else %}{{ result.entry.postDate | date("j F Y") }}{% endif %}

      {% endfor %}

      {# Display pagination if there are more than 10 entries #}
      {% if pageInfo.prevUrl or pageInfo.nextUrl %}
            {% if pageInfo.prevUrl %}Previous Page{% endif %}
            {% for page, url in pageInfo.getPrevUrls(2) %}
                {{ page }}
            {% endfor %}
            {{ pageInfo.currentPage }}
            {% for page, url in pageInfo.getNextUrls(2) %}
                {{ page }}
            {% endfor %}
            {% if pageInfo.nextUrl %}Next Page{% endif %}

Page {{ pageInfo.currentPage }} of {{ pageInfo.totalPages }}

      {% endif %}

  {% endif %}

I've also tried it this way, where I'm using the expandedResults for the result excerpt only. As expected, the pagination and entry limit is this time respected, but this time, every result excerpt displays the excerpts from all the results (e.g. I have 14 results, split between 2 paginated pages but each result features all 14 excerpts):

Entry Results with Partial Expanded Search Result

{# List search result entries #}
{% for entry in pageEntries %}

{{ entry.title }}

        {{ entry.url }}

    {# Show excerpt with matching value if available #}
    {% for result in expandedResults %}
        {% if result.matchedValue %}
{{ result.matchedValue | raw }}
{% else %}
No preview available
{% endif %}
    {% endfor %}

{% if entry.dateUpdated %}{{ entry.dateUpdated | date("j F Y") }}{% else %}{{ entry.postDate | date("j F Y") }}{% endif %}

{% endfor %}

This was solved in my latest release: https://github.com/mustasj/expanded-search/releases

So if you want to use it with pagination, you can now add limit and offset to the settings param.

So in your case the top of your template would look something like this:

{% set limit = 10 %}
{% set entries = craft.entries()
    .search(searchQuery)
    .orderBy('score')
    .limit(limit) %}

{# Paginate the entries #}
{% paginate entries as pageInfo, pageEntries %}
{% set expandedResults = craft.expandedSearch.search(searchQuery, {
    length: 155,
    offset: (pageInfo.currentPage - 1) * limit,
    limit: limit
}) %}


Display user photo within a User field type I'm making a staff page with a User field type, so you select all the users you want displayed. I'm having trouble showing each user's photo though. This works:
{% for user in block.staffList.all() %}
   Image URL: {{ craft.assets.id(user.photoId).one().url }}
{% endfor %}

But seems far from the ideal solution. Is there a more graceful way to do this?

It works very simply. Note that the photo field is an asset object, not an array of asset objects.
{% for user in block.staffList.all() %}
   Image URL: {{ user.photo.getUrl() }}
{% endfor %}


Craft Routing / Variables with Differing URI I have a project which I'm working on which is going to need something similar to the following URL Structure :

site.com/dfe45c4f/news_title, site.com/gyb66dx/news_title or site.com/76vvg3s/news_title

The First URI Segment is the 'Partner Reference' - a hash value for every partner we work with, the second is a simple entry url title. Every page will show the same data, but each page will be 'branded' differently based on the Partner Reference.

What I need to do is to query a Craft channel a set a variety of variables (styling/logo/branding etc) for the page based on the partner reference.

How would I set routing to look up the first Segment URI to look at the partner section and return all the field information as variables?

Thanks

Go to Settings → Routes, and create a route like this:

Then create that partners/_partner_news_entry.html template. (You can change the location/name of the template if you want; just make sure it matches the “Load this template” setting within your route.)

In that template, you can get the Partner Reference and the entry slug like this:

{% set partnerRef = matches[1] %}
{% set entrySlug = matches[2] %}

I’m not sure how you have it set up, but assuming you have a “Partners” channel with a “Partner Reference” custom field, which stores all of the styling/logo/branding info you need, you can get those relevant entries like this:

{% set partnerEntry = craft.entries({
    section: 'partners',
    partnerReference: partnerRef
}).first() %}

Make sure it’s a valid Partner Reference like this:

{% if not partnerEntry %}
    {% exit 404 %}
{% endif %}

Do the same stuff for the News entry:

{% set newsEntry = craft.entries({
    section: 'news',
    slug: entrySlug
}).first() %}

{% if not newsEntry %}
    {% exit 404 %}
{% endif %}

If there’s an Entries field that actually ties the News entries to a specific partner and you want to ensure that you’re not viewing a News entry that should have only been associated with someone else, you can tweak that like this:

{% set newsEntry = craft.entries({
    section: 'news',
    slug: entrySlug,
    relatedTo: {targetElement: partnerEntry},
}).first() %}


Element API endpoint without a resourceKey The output of a Element API endpoint allows you to change the resourceKey which contains the json data. Is it possible to return just the data without a resourceKey at all?

Values such as null, false and empty string just default to data.

Not cleanly... the Fractal library that the Element API plugin uses to serialize that data has 'data' hard-coded as the root element if no other resource key is passed in.


Form Redirecting to Homepage I'm trying to make a form redirect to the current page, but it's currently redirecting itself to the homepage every time the form submits? I can't see anything wrong with the code itself?

<form method="post" accept-charset="UTF-8" enctype="multipart/form-data">
  {{ csrfInput() }}
  <input type="hidden" name="action" value="users/save-user">
  <input type="hidden" name="redirect" value="{{ craft.request.url }}">
  <input type="hidden" name="userId" value="{{ currentUser.id }}">
  <textarea id="summernote" name="fields[userAbout]">{{ currentUser.userAbout }}</textarea>
  <button class="btn btn-primary btn-md semi-bold mt-4" type="submit">Save Profile</button>
</form>

I believe its because you are not "hashing" the URL. Either use this https://docs.craftcms.com/v3/dev/functions.html#redirectinput-url or as that the link also mentions:

<input type="hidden" name="redirect" value="{{ craft.app.request.absoluteUrl|hash }}">


Create a link list with URLs, categories, entries Sorry if this question has already been asked.

I'm building a website for a client. In their footer they have several list of links, and I want them to be able to change them (so not hardcoded into the code).

I'm using a "Entries" type for those linklist. It is intuitive but there are two main caveats:

They have links like "Blog" that must link to an index of a channel. Unfortuantely, because this is not an entry, it does not appear. I've found an ugly workaround that consists of creating a single "Blog", but this is a bit annoying because there is an additional single appearing for them, and this means that I need to rename the channel to something else (because Blog become already taken by the single).
They have some links that point to an external URL (they host their FAQ on ZenDesk, so the FAQ is an external URL). It therefore does not work.

For the last point, I've found a nice plugin called LinkIt (https://github.com/fruitstudios/LinkIt) that nearly tackles the problem, but with one major drawback: contrary to the Entries type, it only allows to add one link (using a matrix feels a bit overkill here and is less intuitive than the Entries stuff).

Did I miss something? I think it would be really good if Craft could have a new field type "URL", that would allow to enter a free URL with a label. It would give much more flexibility to the system.

Thanks!

Michael,

This is a very common development pattern that we run into. Our primary method of resolving is to create a global set with matrix of links. Using a matrix allows you to extend the matrix item with whatever fields you would need based on the ui, such as an icon class.

Matrix Layout:

Links (link)
│
└─ Link Text (text)
└─ Link Entry (entries, limit 1)
└─ Link URL (text)
└─ Link Target (dropdown, [_self, _blank])

I think that a matrix is a good choice, even considering user experience, because they can be collapsed after setup and it is likely that your user is familiar with them from other areas of the site.

If you don't want to use vanilla Craft you could use the Linkit plugin to add a more robust link field-type. It even lets you set the title, click behavior and works with various types of entries, assets, and custom URLs.

Linkit plugin for Craft https://github.com/fruitstudios/LinkIt

Good luck!