How to prevent select user groups from reordering structures I need to create a plugin that prevents select user groups from reordering a Structure content type in the Entries listing. I could quickly do this by just hiding the a.move.icon element, but I worry about the longevity of this solution. For example, if this class were to change then the plugin wouldn't hide the element any longer.

Is there a better, more long-term fix that someone might recommend here?

You should be able to ensure that the elements aren't moved by using the structures.onBeforeMoveElement hook in a plugin:

public function init()
{
    craft()->on('structures.beforeMoveElement', function(Event $event)
    {
        $user = craft()->userSession->getUser();

        if (!$user->isInGroup('allowedGroup'))
        {
            $event->performAction = false;
        }
    });
}

I've had a quick look through the source but I'm not 100% sure if this is also run when an element's parent is changed. I would think it is.

You could also create your own custom user permission that can be assigned to individual users or multiple groups for more flexibility:

public function init()
{
    craft()->on('structures.beforeMoveElement', function(Event $event)
    {
        $user = craft()->userSession->getUser();

        if (!$user->can('pluginName-moveElements'))
        {
            $event->performAction = false;
        }
    });
}

public function registerUserPermissions()
{
    return array(
        'pluginName-moveElements' => array('label' => Craft::t('Move Elements in Structures')),
    );
 }


ERROR: relation "info" does not exist Be warned this is sort of Docker related question too.

I try to build local development environment using Docker (nginx 1.16.1 + postgresql 12.1.0) running Craft 3.4.1.
I've came up to a point where I run the setup wizard over browser. During the steps (connection to database is skipped) I fill the required fields and proceed to final "Next" button where the installation step begins.

  Install failed  Please check your logs for more info.

PHPerrors log shows:

An Error occurred while handling another error:
PDOException: SQLSTATE[42P01]: Undefined table: 7 ERROR:  relation "queue" does not exist ...

Web log shows:

There is a lot of it but it's about info table and to summarise to one general sentence I've picked this line

ERROR:  relation "info" does not exist ...

What I'm able to:

I'm able to connect to database over docker CLI and PGadmin
I'm able to create database over docker CLI and PGadmin

What I'm not able to:

I'm not able to successfully run ./craft setup which reports back this failure SQLSTATE[08006] [7] could not translate host name "postgresql" to address: Name or service not known

Every image lives in it's own container and are connected over external network. If more info required I'd be happy to provide.

Is there anyone Who have experienced similar issue?

A compatibility issue with Craft 3.4.1 and maybe earlier. Running PostgreSQL12+ (PgSQL12+) couldn't finish Craft's installation setup.

I have no idea what change in PgSQL12+ could cause this behavior but downgrading PgSQL12 to lower versions like PostgreSQL11 worked as expected.

However Craft's docs states that PostgreSQL 9.5+ should meet Craft’s server requirements.

I can't take this for answer to my question because if this is an incompatibility issue than downgrading doesn't answer my question. I believe you guys get to it and crack it as always.

Anyway here are the logs when tried to run Craft with PgSQL12+.

phperrors.log
web.log

Update

Running Craft 3.4.6 along with PostgreSQL 12.2 seems to remove the problem. I checked the CHANGELOG-v3 and I couldn't find anything related to PgSQL database.


How to replace characters in string How do I replace ";" with "," in string "Red; Green; Blue"?

{% set rgb = "Red; Green; Blue" %}

Twig has a replace filter for that. You can replace strings/vars with strings/vars:

{% set string = "I love apples" %}
{% set var = "like" %}
{% set string = string|replace({'apples': 'kiwis', 'love': var}) %}

{{ string }} {# Prints "I like kiwis" #}

Your example:

{% set rgb = "Red; Green; Blue" %}
{% set rgb = rgb|replace({';': ','}) %}

{{ rgb }} {# Prints "Red, Green, Blue" #}


Using Craft CMS to manage content within an app Has anyone used Craft to manage content within a Cordova/PhoneGap app?  How successful was it? What are the pitfalls?

We have an idea to create a website managed by Craft CMS.  We then want to "wrap" the website in Cordova/PhoneGap so we can make it available as an App. Our immediate questions:

• Is it possible?

• Is the App immediately updated as a website would be or are there further steps?

• Are there any issues to look out for?

Many thanks for any advice.

Martin

Several people are using Craft to power apps.

Here's a good hangout: Craft Powered Apps, with Aaron Berkowitz discussing how PBS Kids uses Craft to power multiple apps, by making the data in Craft available to the apps available via a JSON API. I believe they use Cordova, which runs independently of Craft but grabs all the data for the app via the API.

Aaron also discusses how publishing content via Craft helps them get around some app-update requirements.


How do I use URL parameters to filter a channel by a date field? How do I filter entries by URL parameters. The url is /booking?month=november&year=2015.

So far I have:

{% set allEntries = craft.entries.section('availability').order('availabilityStartDate').limit(null) %}

where availabilityStartDate is a date field.

How do I return entries that match November and 2015.

Thanks for any ideas or code!

You can pull query string variables using the HttpRequestService, like this:

{% set month = craft.request.query('month') %}
{% set year = craft.request.query('year') %}

Then, you'll need to construct a DateTime object from those query parameters:

{% set queryDate = date([month, year]|join(' ')) %}

...and finally, include that date object in the criteria for craft.entries (notice the >= operator, which means we should get entries with a availabilityStartDate value equal or higher to the queryDate date object:

{% set allEntries = craft.entries.section('availability').order('availabilityStartDate').availabilityStartDate('>= ' ~ queryDate.date).limit(null) %}

EDIT

Filtering out entries with date field values matching the queried month and year exactly is a bit more complicated.

There are probably several ways to approach this, but a thing that works is to use both a start date (i.e. the first day of the queried month) as well as an end date (i.e. the last day of the queried month) for the criteria.

Being that the queryDate variable actually already refers to the first day of the queried month, we'll just need to add 1 month for the end date:

{% set queryStartDate = date([month, year]|join(' ')) %}
{% set queryEndDate = queryStartDate.date|date_modify('+1 month') %}

And here's the element criteria with both date object statements:

{% set allEntries = craft.entries.section('availability ').order('availabilityStartDate').availabilityStartDate('and', '>= ' ~ queryStartDate.date, '< ' ~ queryEndDate.date).limit(null) %}

Here's everything together, for cohesion:

{# Get month and year query parameters #}
{% set month = craft.request.query('month') %}
{% set year = craft.request.query('year') %}

{# Create DateTime objects for the start date and end date (+1 month) #}
{% set queryStartDate = date([month, year]|join(' ')) %}
{% set queryEndDate = queryStartDate.date|date_modify('+1 month') %}

{# Get the entries #}
{% set allEntries = craft.entries.section('availability ').order('availabilityStartDate').availabilityStartDate('and', '>= ' ~ queryStartDate.date, '< ' ~ queryEndDate.date).limit(null) %}

PS: When you have multiple parameters to craft.entries like this, you might want to consider the object syntax for readability:

{% set allEntries = craft.entries({
    section : 'availability',
    order : 'availabilityStartDate',
    availabilityStartDate : 'and, >= ' ~ queryStartDate.date ~ ', < ' ~ queryEndDate.date,
    limit : null
}) %}


How do you create two grid columns like the Entry Edit page within a plugin? I have a plugin where I'd like my CP tab to output two columns just like the entry page.  On the left, would be the body and then there would be a nice dividing gap and then on the right there would be a aside column.

I've tried to copy the way that the Entry Edit page works but I must be missing something.

Extending _layouts/cp wraps everything on the page with the following three divs which don't seem to get output at all on the Entry Edit page:

             ...

How does the entry page get rid of these divs?  I'm not seeing anything obvious in the templates.

EDIT To be clear, I can create the layout that I want, except for the fact that the layout I want gets wrapped in the above code and makes the gutter disappear and messes up the padding around the two columns on the page.  If I extend the {% extends '_layouts/cp' %} and remove those three divs above, just like the Edit and Entry page seems to do somehow, the page displays just as I want it to.

What I can't figure out is how the Entry page is getting rid of those three divs. It uses {% extends '_layouts/cp' %} and those divs do not appear on the Edit an Entry page.

templates/layouts/_cp.html has

nested inside the {% block main %} (line  207) and templates/entries/_edit.html is overriding that block main, so those grid,item,pane divs get overridden too.


Different setPasswordSuccessPath for different user groups I have a couple different user groups that access the front end of our site and I would like to send them to different paths depending on their user group after setting their password. Is it possible to access a user's group in general config for this purpose?

A bit heavy handed, but the only way I can think of do pull this off, would be to write a custom plugin with it's own controller action that you change the link in the email to point to instead of Craft's UsersController->actionSetPassword().

That method would be nearly identical to Craft's except at the end, you'd redirect based on the user groups (if any) the $userToProcess is a member of, instead of just the setPasswordSuccessPath config setting.


What is the best way to format a button macro? I'm looking for a solution on the best way to setup a macro for buttons that are defined in a matrix fieldtype.

My current code works fine but it really isn't that DRY and I feel it could be made better.

I first import my button macro in a main template:

{% import "_includes/buttons" as buttons %}

Then my code for the matrix field is:

{% for block in cube.button %}
   {{ buttons.span(block.buttonType, block.buttonText, block.buttonColour, block.buttonSize, block.fullWidthButton, cube.linkUrl) }}
{% endfor %}

And now my not so DRY button macro

{% macro span(type, text, colour, size, fullWidth, url) -%}
    {{ text }}
{% endmacro -%}

{% macro a(type, text, colour, size, fullWidth, url) -%}
    {{ text }}
{% endmacro -%}

{% macro button(type, text, colour, size, fullWidth, url) -%}
    <button class="btn {{ type }} {{ colour }} {{ size }}{% if fullWidth %} btn-block{% endif %}">{{ text }}</button>
{% endmacro -%}

{% macro submit(type, text, colour, size, fullWidth, url) -%}
    <input type="submit" class="btn {{ type }} {{ colour }} {{ size }}{% if fullWidth %} btn-block{% endif %}" value="{{ text }}"/>
{% endmacro -%}

You could factor out that repeated class=... like so:

{% macro _btnClass(type, colour, size, fullWidth) %}
    class="btn {{ type }} {{ colour }} {{ size }}{% if fullWidth %} btn-block{% endif %}"
{% endmacro %}

{% macro span(type, text, colour, size, fullWidth, url) -%}
{% import _self as self %}
    <span {{ self._btnClass(type, colour, size, fullWidth) }}>{{ text }}</span>
{% endmacro -%}

It is not clear from your question how you decide which buttons macro to call. Perhaps that code needs to be made DRYer.


User specific content Is it possible to show specific content or an entry for an individual user?

Here's what I'm trying to achieve:

A logged in user make a request to receive information on a particular service. An administrator then reviews the request and creates an entry specific for that user - whatever is contained within that entry is then displayed on the users dashboard for them to view.

How would I go about achieving this?

Thanks!

Using the example in your comments, a simple solution would be to create a "Potential Jobs" Entry field and assign it to the Users field layout.

Whoever is doing the manually checking to find a match between tradesmen and clients would assign entries to that field that match the requirements.

The front-end user's dashboard would check that field to see if it has any entries in it and display them to the user.

You could probably get more elaborate with a Category field (that had Entry fields in it) if you want to group by more fine-grained details.


How to convert this 'array' With FeedMe I'm importing some data and one of them I get is a complete array that is stored a plain text field.

This array is a bunch of car options what I want to add in a table like this:

{% set options = entry.optionsAndAccessoiries %}

output {{ options }} = ["Achterbank in delen neerklapbaar","Airbag bestuurder","Airconditioning","Anti-slipregeling","Antiblokkeersysteem","Centrale deurvergrendeling afstandbediend","Elektrisch bedienbare ramen voor","Elektrisch verstelbare buitenspiegels","Getint glas","Hoofdsteunen achter","Hoofdsteunen voor","In hoogte verstelbare bestuurdersstoel","Metallic lak","Middenarmsteun voor","Mistlampen voor","Radio\/CD speler","Startonderbreker","Stoelverwarming bestuurder","Stoffen bekleding","Stuurbekrachtiging","Trekhaak","Verstelbaar stuurwiel"]

{% set items = options %}

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

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

  {% endfor %}

But this is causing an error:

array_chunk() expects parameter 1 to be array, string given

Who can help me out to point me in the right direction?

Sounds like your options simply need to be JSON decoded. Unfortunately, Twig doesn't ship with a json_decode filter, so you'll need a plugin – there are several that fit the bill over at StraightUpCraft.

Using the JSON Decode Filter plugin, here's how your code would look:

{% set options = entry.optionsAndAccessoiries|json_decode %}
...


Entry not showing up in search results? Can anyone help identify why one on my entries is not being picked up in my search query?

The search terms are 'hampshire 10k' and the craft_searchindex table for this entry looks like this:

The search string param is added as follows (along with a handful of other params) for any search

{ search: '*hampshire* *10k*' }

Unsure if it's down to bug or a templating issue? The keyword search forms part of quite complex entry search.

As Brad mentioned, you’re not getting any results because your search query requires both keywords to be present, but you don’t have any fields that have both keywords.

You can solve this with a hidden Preparse field that stores the content of all fields you want to be cross-searchable. Call it something like “Keywords”, and set its Twig template to:

{{ entry.myCustomFieldHandle }}
{{ entry.myOtherCustomFieldHandle }}

If you want to include any Matrix or relational fields in the keywords, remember the same templating rules apply here as on your front end templates:

{% for block in entry.myMatrixFieldHandle %}
    {{ block.myBlockFieldHandle }}
{% endfor %}

{% for relatedEntry in entry.myEntriesFieldHandel %}
    {{ relatedEntry.title }}
    {{ relatedEntry.myRelatedEntryFieldHandle }}
{% endfor %}

Once you’ve got that field set up and added to your section(s), you’ll need to re-save all of your existing entries before it takes effect. An easy way to do that is to go into the sections’ settings and just click the Save button, which will trigger a “Resaving Elements” background task for all entries in the section.


Are there any intentions to release multisite support for Craft? We're currently using a single ExpressionEngine install with MultiSite Manager to serve up 11 total sites that share the same database. After investigating Craft, and asking questions via Twitter it appears that multisite may not be supported.

Is there a way to use a single Craft install to serve up completely separate sites, or any knowledge of this being implemented in the future?

Currently multi-site support isn't planned.

If you want simple multi-site-support you could use the localization feature with a custom locale. But with Craft's auto-update's and easy deployment, why would you want to use MSM anyway?

.

  ...we're pretty against adding it to Craft... at least in the way EE did
  it, where everything is stored on a per-site basis.

  However you should take a look at Craft's localization features. With
  that, all content can be stored on a per-locale basis, all users can
  have per-locale permissions, each locale can have its own base URL,
  templates, routes, etc.. Really is pretty powerful. The only thing
  missing is the ability to define custom locales, in the event that
  your localized sites aren't defined by territory/language, but some
  other factor. That's the main thing that's going to be changing ;)

https://plus.google.com/105300374023226628031/posts/CS2uUefs6Ah

.

  Correct. Initially it did have an MSM-type feature, but we ended up
  taking it out, due to it overcomplicating the system.

  If you want to run multiple domains/sites off of Craft, you’ll just
  need multiple Craft installs.

https://plus.google.com/102316327373347344486/posts/71qEZ4SANhP

EDIT:

You can however use one craft/app/ folder for multiple sites. But since you'd still need multiple databases, configs, plugins, templates... it's probably not worth it, except that you would save a few bytes on files.


XML Feed Parsing Plugin Are there any plugins like this Expression Engine feed parser?

https://devot-ee.com/add-ons/feed-parser

Temporarily cache an XML file from a website or API and list the results using standard Twig templating tags.

We're actually working on a plugin for just this at the moment for one of our projects. It's more for populating channels from a feed (like Solspace Importer or DataGrab), but exposing this via a template tag is something we'll look at including.

Interested if anyone else has come across anything though!

Edit: Feed Me plugin added.


Order a list of categories by number of entries, key not being set I have modified the code from this question but I'm running into an issue.

{% set allCats = craft.categories.group('blogCategories').level(1).find() %}

{% set catArray = {} %}

{% for cat in allCats %}

    {% set catArray = catArray | merge({(cat.id):craft.entries.relatedTo(cat).total() }) %}

{% endfor %}

{% set catArray = catArray|sort|reverse %}

{% for catId, count in catArray %}

    {% set category = craft.categories.id(catId).first() %}

        {{ category.title }} {{count}}

{% endfor %}

Ive ran a dump on the catArray array and the category id isnt being set as the key, regardless of if I try put brackets around it to protect it, I only get the value in the array.

If you encounter problems with assigning variables as the keys, what about putting all your values into objects?

{% set catObjects = [] %}

{% for cat in allCats %}

    {% set catObject = [
        { catId: cat.id, catCount: craft.entries.relatedTo(cat).total()  }
    ] %}

    {% set catObjects = catObjects|merge(catObject) %}

{% endfor %}


How to use a dynamically defined field handle in a plugin? I'm trying to make a plugin more generally usable (so others could use it). For my own use, it uses a category field called 'mainCategory' but I'd like that the field handle could be specified in the plugin settings.

What I imagine is:

$url = $entry->mainCategory->last()->uri;

becomes:

$plugin = craft()->plugins->getPlugin('myPlugin');
$settings = $plugin->getSettings();
$fieldHandle = $settings->categoryFieldHandle;

$url = $entry->getField($fieldHandle)->last()->uri;

Any suggestions?

This should do it for you:

$entry->$fieldHandle->last()->uri;


Is it possible to merge entry parameters to create a dynamic field handle? I'm trying to merge a field name prefix with entry.section to make a dynamic field handle. I'm guessing I'm setting the sectionMatrix variable incorrectly, causing the code to fail without error. Any tips would be greatly appreciated.

Channels:

News
Press

Each channel has is own matrix field, with different blocks:

entryMatrixNews
entryMatrixPress

Template Code:

{% set newsSectionIds = craft.entries.section('news').ids() %}
{% set pressSectionIds = craft.entries.section('press').ids() %}
{% set entryIds = newsSectionIds|merge(pressSectionIds) %}
{% set entries = craft.entries.id(entryIds) %}

{% for entry in entries %}
   {% set sectionMatrix = 'entry.entryMatrix' ~ entry.section %}
   {# sectionMatrix then equals either entry.entryMatrixNews or entry.entryMatrixPress #}

   {% for block in sectionMatrix %}
      {# my matrix code #}
   {% endfor %}

{% endfor %}

The Twig attribute function solved this for me. Thanks to @tom-bauer

Revised Template Code:

{% set newsSectionIds = craft.entries.section('news').ids() %}
{% set pressSectionIds = craft.entries.section('press').ids() %}
{% set entryIds = newsSectionIds|merge(pressSectionIds) %}
{% set entries = craft.entries.id(entryIds) %}

{% for entry in entries %}

    {% set sectionMatrix = attribute(entry, "entryMatrix"~entry.section) %}

    {% for block in sectionMatrix %}

        {# my matrix code #}

    {% endfor %}

{% endfor %}


Is there a master list of user/group permissions in Craft? Has anybody compiled (or is it be possible to extract from the system) a master list of all the various permissions one can assign in Craft?

You can go straight to the source and get the list from UserPermissionsService->getAllPermissions().


Define elements that bust cache on save I wrote a little plugin that reads some entries (Events) from my events section, orders them in a calendar-like structure and returns them to be displayed in a template. (I couldn't do this twig-only)

Now, I would like to use {% cache %} on the calendar. I know that Craft normally busts the template cache when updating/adding an element if it detects, that those elements were used in a cached template.

Now as I am fetching the elements in my custom plugin with

$criteria = craft()->elements->getCriteria(ElementType::Entry);

I assume, that Craft can't know when to bust the cached output of the plugin.

Is there a way (with the TemplateCacheService for example) to make sure that this cache also gets busted when saving/updating one of my events?

I wonder if I can do this just in twig or if I would have to put a hook before the element gets saved? How does it relate to the cache key?

Any hints, which direction to take?

Thank you,

Robert

Update:

Okay, I'll provide some more details:

My Plugin looks (simplified) like this:

// MyPluginVariable.php:
public function getMonthWithEvents($month, $year)
{
    ...
    $criteria = craft()->elements->getCriteria(ElementType::Entry);
    $criteria->section = 'events';
    $criteria->date = ['and', '>='.$monthStart, '<='.$monthEnd];
    $eventsInMonth = $criteria->find();

    $month = [];

    // Put events in month array with dates etc....

    return $month;
}

In the twig template, I do this:

// agenda.twig
...

{% cache %}

    {% set month = craft.migrosAgenda.getMonthWithEvents(10, 2016) %}

    {% for day in month %}
        ...
        <div class="day">
            {{ day.date|date('d.M.Y') }}
            {{ day.event.title }}
            {{ day.event.date }}
        </div>
    {% endfor %}

{% endcache %}

Hard to know for sure without seeing some plugin code, but my guess is you'll want to use the TemplateCacheService's includeElementInTemplateCaches similar to how BaseTemplate's _includeElementInTemplateCaches does.


Log in User (Craft 3) How can I log in a user in Craft 3 and redirect to a url?

This doesn't work:

$username = $user->username;
$userCode = $user->teamCode;
Craft::$app->user->login($username, $userCode);

Thanks!

Documentation for this can be found in Craft's class reference.

# Login as user, by id
Craft::$app->getUser()->loginByUserId($user->id);

# Redirect
return Craft::$app->getResponse()->redirect($url)->send();

Or, if you're in a controller:

return $this->redirect($url);


What is the proper way to set an asset's upload location using the slugs/ structure path of the entry it is selected from? I'm working on a site with a structure containing products that are nested within "category"(not actually using craft's categories, just an entry type) pages. On each category or sub-category page I display each product within as a card with an image and title. The image is selected/uploaded from an asset field on the entry.

In the interest of making the image folders as clean as possible I'm trying to set the asset upload locations based on the path of the entry it is uploaded from.

For example: I have a product at the following location: mysite.dev/products/doors/wood. When I upload the image to be used in the product list view I'd like the path to be mysite.dev/assets/img/products/doors/wood/imagefilename.jpg.

My default image upload location is set to mysite.dev/assets/img/.

Is there a simple method I have missed in the documentation to pull the path of the entry as the asset field upload location?

Or is it necessary to make a loop to go through the parent entries' slugs one by one, and if so what is my point of reference when creating the loop? Am I acting on the asset or the entry when I use a variable like {slug}?

I'm tinkering with it and testing, but it is tedious since I must upload the image each time I try something new, and I feel like I might have missed something in the documentation.

Edit:

After a bit more tinkering I was able to get something that almost works. By setting upload location with the Images location in the dropdown and then {parent.uri}/{slug} I get something like mysite.dev/assets/img/doors/wood/imagefilename.jpg. This is missing the structure slug products at the beginning.

To include the handle of the slug, and the path within the structure based on the entry uploaded from in the upload location field I've found the following to work:

{section.handle}/{parent.uri}/{slug}

If there are any gotchas on this let me know and I'll edit this answer to include them.


Feedme Skip Entry if No Matching Elements on Field Is there a way on Feedme to not import an entry if it doesn't have a match on a specific field?

e.g. If there is no entry that it matches up to, don't import it.

Here's the current code I have:
Event::on(Process::class, Process::EVENT_STEP_BEFORE_ELEMENT_SAVE, function (FeedProcessEvent $event) {
    $myField=$event->feedData['myfieldname'];
    if ($myField == null){
        $event->element->isValid=false;
    }
});

The above processes the data getting loaded in. Here's code for processing data after it's parsed.
Event::on(Fields::class, Fields::EVENT_AFTER_PARSE_FIELD, function(FieldEvent $event) {
    if ($event->fieldHandle['merchants'] == null) {
    $event->isValid=false;
    }
});

isValid should be set on the $event variable, not on $event->element:
Event::on(Process::class, Process::EVENT_STEP_BEFORE_ELEMENT_SAVE, function (FeedProcessEvent $event) {
    $myField=$event->feedData['myfieldname'];
    if ($myField == null){
        $event->isValid=false;
    }
});

I haven't tested but looking at the feed-me source code, this should work.


# of MySQL connections Assume my website (developed using Craft, of course) is expected to have 1000 concurrent access. How many MySQL db connections would be required to open? Does Craft control this or does it depend on program code?

In MySQL, there's a "max_connections" variable: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_connections

In Apache (assuming you're using Apache) there's a "MaxClients" directive: http://web.archive.org/web/20160415001028/http://www.genericarticles.com/mediawiki/index.php?title=How_to_optimize_apache_web_server_for_maximum_concurrent_connections_or_increase_max_clients_in_apache

I'm not sure of your situation, but you may need to modify both of these to achieve what you need. These settings are not controlled through CraftCMS, but instead need to be controlled by editing your server environment configs.


How to save a global set’s content? I’m trying to set data within a global set. I’ve tried the following without success:

$globalSetHandle = 'settings';
$globalSet = craft()->globals->getSetByHandle($globalSetHandle);

$globalSet->fieldHandle = 'value';

craft()->globals->saveContent(GlobalSetModel $globalSet);

Try using setContentFromPost, as mentioned in this answer from Brandon.

$content = array(
    'fieldHandle' => 'value',
);

$globalSet->setContentFromPost($content);
craft()->globals->saveContent($globalSet);


How can I relate a FK back to the purchasable ID? First off, HUGE thank you to Lindsey for opening this door for me. Opened up a whole new layer!

I have (another) plugin that is listening to the commerce_orders.onOrderComplete event. My plugin inspects the order, and if a specific product was purchased, I capture that and save the pre-defined values in a custom table.

For example, if you buy "Product A", you get to pick two "Product B" options (these options are defined in commerce/CP).

I am able to create the custom table and save the correct values - so far so good.

However, I would like to relate not only the user back to the user account, but the "Product A" back to the purchasable.

Something like this:

MyPluginRecord.php

public function defineAttributes()
{
    return array(
        'id' => array(AttributeType::Number, 'default' => null),
        'packageId' => array(AttributeType::Number, 'default' => null),
        ...
    );
}

public function defineRelations()
{
    return array(
        'element' => array(static::BELONGS_TO, 'ElementRecord', 'id', 'required' => true, 'onDelete' => static::CASCADE),
        'package' => array(static::BELONGS_TO, '????', 'id', 'required' => true, 'onDelete' => static::CASCADE),
    );
}

I'm not sure how to get back to the commerce purchasable record?

Thank you for any suggestions!

Thanks Brad for breaking it down like that. It helped me take a step back and see what it was I was doing.

Here is the relationship I have that is working:

public function defineAttributes()
{
    return array(
        'id' => AttributeType::Number,
        'packageId' => AttributeType::Number,
        ...
    );
}

public function defineRelations()
{
    return array(
        'element' => array(static::BELONGS_TO, 'ElementRecord', 'id', 'required' => true, 'onDelete' => static::CASCADE),
        'package' => array(static::BELONGS_TO, 'Commerce_PurchasableRecord', 'onDelete' => static::CASCADE),
    );
}

Now I am able to have a relation to the user id element and back to my product package.


Just how do Macros work in Craft 3? Here is what I am doing, but it's not working:

In a template:

{% import 'macros/global' as macros %}
{{ macros sizedImage(image, alt, 't600X450', 600, 450, link, 'someClass') }}

The I have a macro file with code like this:

{% macro sizedImage(img, alt, transform, declaredWidth, declaredHeight, link, class) %}
...bunch of code...
{% endmacro %}

It's not clear from Craft's documentation what I should be doing differently. I did note that macros have to be included in the template where they are called, instead of a parent template, and that is fixed.

The error I get is:

  Twig Syntax Error – Twig_Error_Syntax
  Unexpected token "name" of value "sizedImage" ("end of print statement" expected).

To answer your title question... macros in Craft 3 work exactly the same as they do in Craft 2. :)

The problem you are hitting is here:

{{ macros sizedImage...

Seems like a straightforward typo. I'm assuming you meant to write it like this:

{{ macros.sizedImage...

For that reason (among others), I would recommend not naming your imported macros as "macros". It just invites confusion, and leads to silly typos like this one.

Personally, I often name them "render" (though you can truly name them anything) because it's more obvious what's happening.

{% import 'macros/global' as render %}
{{ render.sizedImage... }}


How to fetch matrix block items with the ElementAPI I have a question regarding the ElementAPI:

I am sucessfully using the ElementAPI to fetch assets related to an entry. Now I have a matrix field where a user can add multiple galleries (multiple block of selected assets) and i want to retrieve the images. But I can't figure out how to use it for nested matrix blocks...

This is how I fetch the data with ElementAPI from non-matrix assets fields (elementapi.php config)

'endpoints' => [
    'api/v1/installation-views/<sourceEntryId:\d+>/<fieldHandle:[A-Za-z]+>' => function($sourceEntryId, $fieldHandle) {
        return [
            'elementType' => 'Asset',
            'criteria' => [
                'relatedTo' => [
                    'sourceElement' => $sourceEntryId,
                    'field' => $fieldHandle
                ],
                'kind' => 'image'
            ],
            'transformer' => function(AssetFileModel $asset) {
                return [
                    'id' => $asset->id,
                    'title' => $asset->title,
                    ...
                ];
            },
        ];
    }
]

How would I do that for asset fields nested inside a matrix block? I guess I can't use the nested field handle as handle?

Matrix blocks are elements just like entries and assets, so this is just a matter of setting the elementType to 'MatrixBlock', and adjusting your criteria to fetch the block you’re after.

The simplest approach would be to pass the Matrix block ID in the URL, and apply it to the id criteria parameter.

'endpoints' => [
    'matrix-blocks/<blockId:\d+>' => function($blockId) {
        return [
            'elementType' => 'MatrixBlock',
            'criteria' => [
                'id' => $blockId
            ],
            'first' => true, // Just return one Matrix block
            'transformer' => function(MatrixBlock $block) {
                $assetsInfo = [];

                foreach ($block->myAssetsfield as $asset) {
                    $assetsInfo[] = [
                        'url' => $asset->url,
                        'title' => $asset->title,
                        // ...
                    ];
                }

                return [
                    'id' => $block->id,
                    'assets' => $AssetsInfo,
                    // ...
                ];
            },
        ];
    }
]

You can also use the following criteria parameters to narrow down the search:

fieldId (the Matrix field ID)
ownerId (the owner element ID, e.g. your entry’s ID)
ownerLocale (the locale of the owner element that the block should have been saved to)
type (the block type handle)


Creating Custom User Permissions Is it possible to define custom user permissions in Craft?
I'm building some front end templates that are only accessible by logged in users. Certain parts of this members page should only be accessible to users with specific permissions. I know that I can create multiple groups and use template logic to show / hide certain pages. However, being able to define custom user permissions would be much more flexible.

Say I want to allow a user to access a form, but I don't want to grant access to the other permissions from a different user group.

{% if currentUser.can('submitForm') %}
    {# show form here #}
{% endif %}

You can create a plugin that adds custom user permissions via the registerUserPermissions hook.


Non-CP routes in plugins I'm building a custom plugin and need to have a publicly accessible (non-CP) route as part of the plugin.

From what I've read I can use registerSiteRoutes() to register non-CP routes within my plugin. However, I'm getting a 404 when I do this. Here's my setup.

MyPlugin.php

<?php
namespace Craft;

  class MyPlugin extends BasePlugin {

    ...

    public function registerSiteRoutes() {
      return array(
        'myroute'           => array('action' => 'myplugin/access/index')
      );
    }
  }

MyPlugin_AccessController.php

<?php
namespace Craft;

class Portal_AccessController extends BaseController {

  protected $allowAnonymous = true;

  public function index() {

    $templatesPath = craft()->path->pluginsPath().'myplugin/templates/site/';
    craft()->path->setTemplatesPath($templatesPath);

    $this->renderTemplate('index');

  }

}

I have setup a basic template at /plugins/myplugin/templates/site/index.html

Hitting mydomain.com/myplugin results in a 404. Am I thinking of this correctly? Is it possible to have a non-CP route that points to a controller method in my plugin?

There are a couple of issues here:

Any action methods in the controller class needs the "action" prefix to their name, i.e. actionIndex() for your "index" action, or actionLogin() for another action route calling "myPlugin/access/login".

In other words, rename your index() method in the controller to actionIndex(), and it should work.

For action routes, plugin handles should be written in camelcase – i.e. "myPlugin/access/index", not "myplugin/access/index".
You have a minor typo in the controller:

$templatesPath = craft()->path->pluginsPath().'myplugin/templates/site/';

should read

$templatesPath = craft()->path->getPluginsPath().'myplugin/templates/site/';

Addendum: this might just be a typo in the question text, but your controller class is actually named "Portal_AccessController", so it will obviously not respond to the route "myPlugin/access/index" – the latter should read "portal/access/index" (or the controller should be renamed "MyPlugin_AccessController)


Archiving custom element types I am building a custom element type and want to have the ability to archive entries. I can create a custom status for 'archived' but I've noticed base elements already have an 'archived' status I could use. Is it wise to do this? I don't know if Craft will delete elements marked as 'archived' after a period?

I believe the answer is to roll my own archiving method – archived elements results in them not being accessible in element indexes.


Filter Entries field by category in the CP? Is it possible to filter relations entries field by a category in a control panel entry?

I would like to relate two channels using the entries field but I have 100 source entries so I was hoping to be able to filter the list by category to narrow down what is shown in the target entry. Similar to how EE Playa works which allows me to choose the category in which to pull the related entries from when I initially set up the custom field.

Here's a similar thread:
Filtering/searching for entries by category in the control panel

No, this is currently not possible, but there's a feature request on Craft's feedback site
that you could vote for: "Entry Type" setting for Entries fields.

As Lewis Jenkins mentioned there, you could use his plugin Dynamic Fields as
a workaround solution in the meantime. Something like this should give you a dropdown field with
all "myOtherChannel" entries related to a category "myCategory".

{% set category = craft.categories.group('myCategoryGroup').slug('myCategory') %}
{% set entries = craft.entries.section('myOtherChannel').relatedTo(category).order('title asc') %}

{% for entry in entries %}
    { "value": "{{ entry.id }}", "label": "{{ entry.title|raw }}" }
    {% if not loop.last %}, {% endif %}
{% endfor %}


Live Preview is jumping to top of page on edit When using the live preview pane, each time I type something or move a block, the preview pane jumps to the top. When editing anywhere but the top, this is not ideal. Is this a bug? I also checked it on a previous site that I had built in Craft and I'm getting the same behavior.

I have also noticed this happening, but the Craft Stack Exchange is not the best place to post about potential bugs/issues.

It might be worth submitting a bug report to support@pixelandtonic.com as they are best suited to take things from here!


How can I make Sprout Invisible Captcha plugin works using AJAX submission? I set up a custom form template using Sprout Forms and added {{ craft.sproutInvisibleCaptcha.protect() }} inside the <form> tag.

I can see there are several captcha tags added by the plugin, but as I am using AJAX submission:

How can I make it works using this submission method?
How do I handle redirect to my 'redirect' location?

I did these tests:

Prevent a form from being submmitted if a user does not have JavaScript enabled ;
Block form submissions by robots who auto-fill all of your form fields ;
Require minimum time to fill out your form ;

All of these tests have failed, spam haven't been caught.

This is the captcha field inside custom Sprout Form template:

{{ craft.sproutInvisibleCaptcha.protect() }}
<input type="hidden" name="redirectOnFailure" value="{{ craft.request.getUrl() }}">

And this is the AJAX post inside JS script:

var postUrl  = 'sproutForms/entries/saveEntry',
    formData = {
    action: postUrl,
    handle: 'test',
    fields: {}
};

formData['fields[minPrice]'] = $scope.results.minPrice;
formData['fields[maxPrice]'] = $scope.results.maxPrice;

formData = $.param(formData);

$.ajax({
    type     : 'POST',
    url      : postUrl,
    data     : formData,
    dataType : 'json',
    encode   : true
});

Based on your code - and our comments - you need something like this:

<form id="someFormId">
    <input type="hidden" name="action" value="sproutForms/entries/saveEntry">
    <input type="hidden" name="handle" value="test">
    <input type="hidden" name="redirectOnFailure" value="{{ craft.request.getUrl() }}">
    <!---some more fields-->
    <button type="button" id="sendRequest" value="send">
</form>

and your javascript like

$("#sendRequest").click(function(){
    var formData = $("#someFormId").serialize();
    //proceed with your ajax request
});

reason: The Plugin checks all your variables in your $_POST request. Depending on the keys in this array it checks for spam. In your request you send none of those fields -> the plugin validation does not trigger. You can check which fields are injected with the {{ craft.sproutInvisibleCaptcha.protect() }} function and add them manually in your request as well but it's easier to just serialize the form. Maybe you have to play around with the action url. I'm used to craft 3 since two months and it could be I messed up the exact parameter in a serialize for craft2


Entry versioning history only shows last 10 versions My client wants to look through very old versions of a certain page entry, but the Recent Versions drop-down is only showing a max of 10 entries. I know that older versions of the page exist because if I change the last number in the URL (of a more recent version) and I can access much older entries. Unfortunately, the are organized numerically by date (and not by page) so it makes them almost impossible to sift through.

Any help would be appreciated.

I don't think Craft has any other place for viewing old entry versions. You can browse the entire set by examining a table in the database, or write a plugin to display all of them.

Quick and Dirty

If you're just digging into this specific entry, you can pop into the craft_entryversions table and search for the entryId matching the one in question. You'll see all versions past and present, each with a bunch of JSON in the data column with that version's content.

Let's say you fancy one of those entries and its id column is 346 in the database. As you've already discovered, you can modify the entry's edit URL from...

/admin/entries/projects/1234-gobias-industries-about-page

...to...

/admin/entries/projects/1234-gobias-industries-about-page/versions/346

...and view it from the Craft control panel.

This is a bit uncomfortable of course, but you can quickly share a bunch of URLs with your client that'd make it easy to visit and inspect each version.

Slower and Cleaner

Otherwise you'd have to whip up a plugin to display additional version details in the control panel, probably linking to each version for further examination. If you're comfortable taking a stab at the plugin, it'd be fairly quick work displaying the results of craft()->entryRevisions->getVersionsByEntryId() however you like.

Versioneer

I just made a quick Field Type plugin for this. It's v0.0.1, but it may be worth checking out!

https://github.com/workingconcept/versioneer-craft-plugin


Disabled entries don't show up in the Linked entries field when displayed in the CP entry list As you can see, I would like to see what challenge the submission is linked to, even though that Challenge might now be turned off (expired or disabled).  Is this a bug or intended behaviour.  Is there an easy way to get this up and running?

If it's intended, I'm not sure why there's a green "enabled" dot on it, there's no way they can be anything else.

In my screenshot, the field Challenge is an entry field and the entry "Challenge #1" is assigned to, but is now expired and not showing in the table.

Yeah, I agree it's a bit awkward UI-wise in this scenario, but that's currently expected behavior.

You'd probably be able to pull off what you're looking for with a custom plugin by listening to the getEntryTableAttributeHtml hook, though.

In there, you'd check if the passed in attribute is the one for your entries field type, grab the current status and modify the HTML that gets returned based on the status.


I am getting this message when creating a new project Cannot create cache directory /home/cmnd/.composer/cache/repo/https---repo.packagist.org/, or directory is not writable. Proceeding without cache

That's not an error, but a Composer warning, saying that it can't create its cache folder at /home/cmnd/.composer/cache/repo/https---repo.packagist.org/ (probably because of a permissions issue.
You can ignore it, but Composer will just run slower.


Migrating Content from ExpressionEngine that used Matrix Image haystack Technique So I'm taking the plunge from ExpressionEngine to Craft! I have a ton of entries I need to move over, though - and I used the "matrix image haystack" technique for my blog posts to inject the images into the body copy.

I love the Matrix layouts in Craft and would like to essentially put those images in their own "block" and separate the text around the haystack tags into their own blocks as well. I'm doubting that's possible, but I'm not going to be able to re-publish hundreds of blog posts a new way, either :)

Has anyone attempted anything like this?

You have two main options with a challenge like this.

1) Do it manually - While this option doesn't always sound like the most glamourous option, it's often a fairly reasonable step to consider during migrations. Writing and debugging advanced code that is only going to be used once can be costly, and there are plenty of people who specialize in data entry who are capable of helping out if you prepare a good workflow and take the time to train them on the task.

2) Do it programatically - If you take this approach, you'll probably be using an Extract, transform, load Process (also referred to as an ETL Process). In your case this would roughly equate to:

Extract the data from your ExpressionEngine database
Transform that data – processing the body and EE matrix haystack fields and updating the data into the format which you wish to import it into Craft
Load your new data into Craft (using a script or import plugin like Brad mentioned in the above comments)

My firm runs a database migration service, The Great Migration, and I can confirm that it is possible to migrate ExpressionEngine sites using a "matrix image haystack" technique to Craft CMS. We have used both programmatic and manual steps in doing so for images, videos, and quotes using this technique. Your final solution will largely depend on the format and integrity of your source data and the desired format of your target data.


Order Entries Query by a Featured Light Switch I have a case studies filter that allows you to filter case studies by category. I've attached a light switch field called 'featured' to the entries that allow you to set it as featured. The idea is to have featured case studies appear first when filter results are returned. I've seen a few post discussing using an innerJoin to do this but I'm not sure if that's the right path or not. Any help would be greatly appreciated.

You're looking for the filter filter. Here's how you might do it:
{% set entriesInCat = craft.entries.relatedTo(category).all() %}
{% set featuredEntriesInCat = entriesInCat|filter(entry => entry.yourLightswitchFieldHandle) %}
{% set remainder = entriesInCat|without(featuredEntriesInCat) %}

{% for item in featuredEntriesInCat %}

{{ item.title }}

{% endfor %}

{% for item in remainder %}

{{ item.title }}

{% endfor %}


Call to Craft Users Login Function from Plugin I am migrating a 15,000+ users from a site's old CMS (WP), and have them set to Pending. All of their user information will be created and on Craft CMS, except their passwords will not be set, as migrating that is too complicated (for what I can tell.) So, the process I have in mind for previous users to login is:

User enters Username/Email & PW, submits login form.
Form sends data to custom Custom Plugin Controller, and follows logic:

If user status==active, simply send form data to Craft's users/login and proceed as usual.
If user status==pending:

Check if username/email exists in previous users list.

If it does not, simply send form data to Craft's users/login and proceed as usual.
If it does exist, it sends the Activation Email and redirects to special URL stating "You've been sent an email to re-activate your account on this new website."

Is this possible to do? It would be fairly straight forward to write, except I am unsure if there is a way to communicate with Craft's users/login via a plugin. Is it something like craft()->users->login( $userNameOrEmail, $userPassword )?

I'm aware I could also just send out batch email activation links to the migrated users, but I'd much prefer this method, in case they miss those e-mails and try logging in at a future date. Alternatively I could have a "Are you a user of the old site? Click here to re-activate your account." Link on the login page, but again users may not notice it and think they cant login anymore.

Any help would be hugely appreciated! Thanks.

What you're attempting to do is very doable.  This should be of help to you:

https://craftcms.com/classreference/services/UsersService

You can use the Yii forward function to pass along a request to another action handler:

    $this->forward('users/sendNewEmailVerifyEmail', false);


Is there an easy-to-follow tutorial to learn Craft CMS basics? I am thinking about using Craft CMS for building multiple websites for my organization. A friend recommended Craft CMS, but I am finding it very hard to get started, unlike GRAV, which was very easy to install and use.

I am looking for a tutorial that steps users through the building of a simple website from A to Z and in the process explain why Craft CMS should be given more consideration over other tools.

I looked online only to find a few such tutorials, but you have to pay to access them. I am in the evaluation phase and do not want to pay for something that I might not use.

If anyone knows of a good and comprehensive tutorial, please let me know.

Craft has an official getting started tutorial now! https://craftcms.com/docs/getting-started-tutorial/
Original Answer:

Welcome to Craft!  Here's a collection of developer resources to help get you up to speed on Craft quickly.
The most important thing to note about Craft CMS and Craft Commerce is that they are platforms used to build bespoke solutions for clients. They are light on traditional "out of the box" features and instead invest heavily on providing developers with a modern toolset and content teams with a great author experience. In practice this means that every Craft site is made specifically for the client which is great and what clients and developers appreciate about it. The downside is if you are brand new to Craft and have no context to the original build out or developer, it can be a challenge to get up to speed.
Craft Official Docs & Support

Craft CMS docs - Pretty comprehensive and worth reading if you're new to Craft.
Craft Commerce docs
Craft Slack group - All of the Craft dev team is on Slack every day with 5,000+ other Craft developers and users from all over the world.
Craft StackExchange - You'e obviously found this one, but this is the best place to ask "how do I" implementation questions.

Learn Craft
Free and commercial.

Mijingo Craft's Tutorials — Mijingo is an official Craft education & training partner. They have a number of excellent free and paid resources for Craft & Craft Commerce. Ryan Irelan, Mijingo's founder, also offers customized training online or on-site.
Tuts+ Tutorials — Not official but very good and low cost (most are $9).
Craft Straight Up Hangouts — Hosted by Ben Parizek, these are live learning sessions with Ben and a member of the Craft team.
Studio 107 Blog — Andrew Welch is a prolific Craft developer and the creator of the SEOMatic plugin. He has some excellent developer-oriented posts on Craft that are worth reading.


Embedding video using Craft 3 'Video Embedder' plugin In the example from the documentation on Craft 3 'Video Embedder' plugin:

{% set embed = craft.videoEmbedder.getEmbedUrl('https://www.youtube.com/watch?v=6xWpo5Dn254') %}

{% if embed | length %}
    <iframe src="{{ embed }}"></iframe>
{% endif %}

Can someone please give me full (i.e. total beginner!) instructions on how to get the URL to load.

Worked it out - just needed to use the right syntax, for example:

{% set embed = craft.videoEmbedder.getEmbedUrl(entry.videoEmbed) %}

{% if embed | length %}

{% endif %}


Sprout Forms - Can't save front end entry edits This this might be a bug but want to ask here first in case I'm missing something minor.

Description

Trying to front edit a Sprout Form entry that was created in the front end. I'm using the same form code with additional <input type="hidden" name="entryId" value="###"> field. But it doesn't save the entry, it creates a new entry.

I compared my form fields with plugin backend and think I have everything I need:

<input type="hidden" name="action" value="sprout-forms/entries/save-entry">
<input type="hidden" name="handle" value="handle_name">
<input type="hidden" name="entryId" value="275">

Steps to reproduce

Create form entry
Edit in front end
Submit form
Original entry is unchanged and there's a new entry

Additional info

Craft version: Craft CMS 3.0.18
PHP version: 7.2.1
Database driver & version: MySQL 5.6.34
Plugins & versions: Sprout Forms 3.0.0-beta.20

Craft 3

This feature is available in Sprout Forms for Craft 3 (since v3.0.0-beta.22).

The feature is disabled by default and can be enabled in the Sprout Forms settings.

Craft 2

This feature exists in Sprout Forms for Craft 2 via the enableEditFormEntryViaFrontEnd config override.


Sort by MatrixBlock Field / Eager load Matrix Field Is there currently a way to eager load the matrix values of an element and thus be able to sort by one of its fields? Imagine you have something like the following:

$criteria = craft()->elements->getCriteria(ElementType::MatrixBlock);
$criteria->type = 'mytype';
$blocks = $criteria->find();

This would create a select from the following tables:

FROM   `craft_elements` `elements`
   JOIN `craft_elements_i18n` `elements_i18n`
     ON elements_i18n.elementid = elements.id
   JOIN `craft_matrixblocks` `matrixblocks`
     ON matrixblocks.id = elements.id
   JOIN `craft_matrixblocktypes` `matrixblocktypes`
     ON matrixblocktypes.id = matrixblocks.typeid

Obviously the most important table for my task at hand is missing: the craft_matrixcontent_mytype table. I'm not able order by mytable.myfield and additionally, while looping those blocks I have the N+1 problem where it builds an extra query to fetch the actual block content.

Am I somehow able to load all MatrixBlocks of a given type, INCLUDING the values?

After digging through some code I found out, that the MatrixBlockElementType just returns a content table (which is necessary for the eager loading part), if a fieldId is given:

$criteria = craft()->elements->getCriteria(ElementType::MatrixBlock);
$criteria->type = 'mytype';
$criteria->fieldId = 90;
$criteria->order = 'field_mytype_myfield';

$blocks = $criteria->find();

Like this everything seems to work as expected and I'm again in love with crafts flexibility :)

Am I right that I have to pass in the id? I can't do it via the field handle 'mymatrixfieldhandle', correct?


Twig: length giving 1 on empty array? This code worked fine in Craft 2:

 {% set entries = craft.entries.section('events').relatedTo(category).orderBy('startDate asc').limit(3) %}
 {% set extraEntries = craft.entries.section('events').relatedTo(category).orderBy('startDate asc').offset(3) %}
 {% if extraEntries | length %}
    //do some stuff
 {% endif %}

In Craft 3, {{ extraEntries | length }} is giving a value a 1 even when there are no entries. For example, {% for e in extraEntries %}>>>{{e}}{%endfor%} doesn't output anything. Any thoughts?

Because extraEntries is not an array of elements but an object of type EntryQuery in order to fetch the entries you have to do

{% set extraEntries = craft.entries.section('events').relatedTo(category).orderBy('startDate asc').offset(3).all() %}

It was actually the same in Craft 2 but the class had an iterable and countable interface, so when you do |length it actually does .total() (to execute the command and fetch the number of rows) These automatic mechanisms are removed/deprecated now thus you have to know what kind of object you use

When you take a look at your cp you'll see a bunch of deprecated warnings.

I suggest you to read the docs


HTTP ERROR 500 after move to live I moved a site from staging to live. Prior to moving, I logged in to the Craft admin and cleared the caches. I copied the db to the new one and moved the folders like so…

from…
home/subdomains/craft
home/subdomains/staging

to…
home/craft
home/public_html

I’m getting this in the server log…
[28-May-2017 20:01:30 UTC] PHP Fatal error:  Class 'Craft\DbCommand' not found in /home/wenzelspine/craft/app/etc/db/DbConnection.php on line 28

And this in storage/runtime/logs…
2017/05/28 21:33:05 [error] [php] Declaration of Craft\DbCommand::addForeignKey() should be compatible with CDbCommand::addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = NULL, $update = NULL) (/home/wenzelspine/craft/app/etc/db/DbCommand.php:15)

2017/05/28 21:33:05 [trace] [system.CModule] Loading "errorHandler" application component

Howard, craft itself doesn't break like that, in my experience.

set devMode true in config/general.php
try the url again, then look at the logs for craft and php, in craft/storage/runtime/logs
if you don't see anything, look at the php log on the server..

The clue should be there.


relatedTo - return only entries that have a related entry where a field in the related entry has a value Trying to figure out if this is possible in 1 step. I want to return locations that have a related entry in the relatedMerchant field, but I'd also want to make sure that a field in the relatedMerchant entry has a value...

$locationsRelatedToCategories = Entry::find()
   ->section('locations')
   ->limit(100000)
   ->relatedTo([
     // somewhat unrelated, but this entry should be related to a specific category
     'targetElement' => $categoryElement, 'field' => 'locationOffers.category'
   ])
   ->relatedMerchant(':notempty:') // has to have a related merchant
   // somewhere here, how can I return only relatedMerchant entries where relatedMerchant.someField :notempty:
   ->all();

If your craft_searchindex table (assume your table prefix is "craft_") isn't too big (I think 100k rows is big), and your field in "relatedMerchant" is the 'title' field.

You can do it in one step.

$locationsRelatedToCategories = Entry::find()
   ->section('locations')
   ->limit(100000)
   ->relatedTo([
     'targetElement' => $categoryElement, 'field' => 'locationOffers.category'
   ])
   ->search('-relatedMerchant:*')
   ->all();

Otherwise, I don't think its possible. You will need to query the relatedMerchant entry to get ids().
And relate the id array to locationsRelatedToCategories.

$relatedMerchantQuery = Entry::find()->section('relatedMerchangt');
$relatedMerchantQuery->field = ':notempty:';
$relatedMerchantIds = $relatedMerchantQuery->ids();

$locationsRelatedToCategories = Entry::find()
   ->section('locations')
   ->limit(100000)
   ->relatedTo($relatedMerchantIds)
   ->all();

Craft only store element relations via craft_relations table. The elementQuery->relatedTo() method is the only way to access it, without creating custom query or tables.


Upgraded to PHP 7.1 - GD or ImageMagick is required This question relates to two Craft sites running on the same server. One site is a production version, the other is staging. I use WHM v68 and EasyApache 4.

Production is on PHP 5.5.38. Staging is on PHP 7.0.25. Both sites report the following from admin/utils/phpinfo:

imagick module version: 3.4.3
Imagick compiled with ImageMagick version: ImageMagick 6.9.4-1 Q16 x86_64 2016-10-13

I would like to upgrade both sites to PHP 7.1, starting with staging. When I use WHM's MultiPHP Manager to update staging to PHP 7.1, I'm unable to load Craft. I receive this message:

--

Can’t run Craft CMS :(

Your server doesn’t meet the following requirements to run Craft CMS:

GD or ImageMagick is required, however ImageMagick is recommended as it adds animated GIF support, and preserves 8-bit and 24-bit PNGs during image transforms.
GD or ImageMagick is required, however ImageMagick is recommended as it adds animated GIF support, and preserves 8-bit and 24-bit PNGs during image transforms.
Please talk to your host/IT department about upgrading your server.

--

I don't understand this because I know that ImageMagick and imagick are both installed and enabled. And imagick 3.4.3 (since RC1) should support PHP 7.1.

Thanks!

You'll need to contact your hosting provider. There are any number of ways that this "PHP switching" can be implemented, but typically each major PHP version has its own separate modules/config files that tell it which modules to use.

So the fact that ImageMagick is installed on the server doesn't mean that it's globally available to every PHP version that's running. So, contact your hosting provider.

Also, I feel obligated to mention that boutique hosting environments can be a source of issues like this, so I try to avoid them, as per: How Agencies & Freelancers Should Do Web Hosting


RelatedTo does not seem to work! I've looked at several answers on here regarding this one and as far as I can make out this should bring out all entries related to a specific category slug. However it just seems to be returning all entries regardless of whether it's related or not? What am I missing? Also in case it makes a difference there is one category level but an entry can have multiple categories selected.

{% set category = craft.categories.group('markets').slug('marine').first() %}
{% set products = craft.entries.section('products').realtedTo(category).limit(null) %}
{% for product in products %}
    {{ product.title }}
{% endfor %}

There's a typo in your code :) You misspelled relatedTo. I'm assuming that's the issue because other that than, the code looks like it should work.


Possible cache issue? Not sure I have a very simple plugin to update a user's name. All I'm doing is:

    $userId = craft()->userSession->id;

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

    $post = craft()->request->getPost();

    $user->firstName = $post["firstName"];

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

The problem I'm having is after the page reloads from the POST submit the output still shows the old name, but then if I refresh the page without resubmitting I see the new name. Point is the name is changing but it seems like maybe this data is cached somewhere? How do I fix this?

UPDATE

To be more clear, this does not happen on all field types. First name and last name do it. Custom plain text fields do not. Category fields do it.

UPDATE

Here is the code I'm using in one of my controllers. This will not be the final code but I don't feel like that matters. Just these few lines will produce the problem I'm talking about:

public function actionEditContactInfo()
{
    $userId = craft()->userSession->id;

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

    $post = craft()->request->getPost();

    $user->firstName = $post["firstName"];

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

For AJAX calls I'm doing a little bit more:

public function actionEditContactInfo()
{
    $userId = craft()->userSession->id;

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

    $post = craft()->request->getPost();

    $user->firstName = $post["firstName"];

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

    $editUser = $user->getContent();

    $template = craft()->templates->render("users/profile-form/_contact-info-form", array("editUser" => $editUser));

    $this->returnJson(array('template' => $template ));
}

In both cases not all fields display the updated content after a form submit is made. Custom plain text fields seem to be ok. The default user model first name and last name are problematic as are category fields. Not sure about other field types.

UPDATE

It has been brought to my attention that I should probably be more clear in my explanation of this problem. Here it goes from top to bottom:

I have a form with 2 fields:

A category field
A plain text field

The form submits data to the server by making an AJAX call to a controller. The controller updates the info as needed then renders a template representing the updated form. The template is then encapsulated in a JSON array and passed to a JavaScript function which updates the user's browser.

The problem I'm experiencing is not that the data is not saving, but that when the new form is re-rendered not all of the fields reflect the changes. But if I refresh the page after saving everything is fine.

Of the two fields mentioned above, plain text works as expected but the category field is problematic. Even if I dump() the user data in the re-rendered template I am given the old value for the category field even though it was updated. Here is my controller code:

public function actionEditNetworks()
{

    $userId = craft()->userSession->id;

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

    $user->setContentFromPost('fields');

    // Save the user
    $result = craft()->users->saveUser($user);

    $editUser = $user->getContent();

    $template = craft()->templates->render("users/profile-form/_network-links-form", array("editUser" => $editUser));

    $this->returnJson(array('template' => $template));
}

If I had to guess, you need to add a $this->redirectToPostedUrl(); after your call to craft()->users->saveUser($user); to cause a proper redirect/page refresh after submission.

Update:

If you're doing this over AJAX, then you're going to be responsible for updating any DOM elements with their new values, or reloading/redirecting the page so they'll display.

In a non-AJAX context, a call to $this->redirectToPostedUrl(); will do the redirect for you.


Show the entry category, that´s it I have a simple and maybe a stupid question, it´s my first Craft Website...

I´m listing  a news section, the news can be categorized ( max two categories ), and i don´t know how to show the category name in the news list items, the category field name is "categoriaNot"

{% for blog in craft.entries.section('noticias').limit(10) %}

            {% set imagen = blog.imagenNot.first() %}

            {% if imagen %}

            {% endif %}

                **** CATEGORY NAME HERE ****

             {{ blog.title }}

             Publicado el: {{ blog.postDate.format('d.m.Y') }}

                {{ blog.resumenNot }}

{% endfor %}

Any help will be welcome!

thanks!

Sebastián.

If I'm following you correctly, it should be as simple as this:

    {% if blog.myCategoryFieldHandle|length %}

            {% for category in blog.myCategoryFieldHandle %}

{{ category.title }}
            {% endfor %}

    {% endif %}


Go from local to public with Craft 3 Is there a lesson/tutorial/docs about going from local to production on server with Craft 3?

What files should be uploaded  ( and which not )
What folders need to be one level up of the root - where to change the paths to this folders
Change config files.
Go from dev to production.

Thank you!

Check out the discussion in this SE question: Best way to deploy Craft3 to webserver?


Ajax front-end user registration form - not returning JSON I have a user registration form on the front-end of a site that is working well as a standard form, but when I try to use ajax submission, I'm getting an HTML response rather than JSON.

Here's the javascript I'm using

$('#register-form').submit(function(ev) {
    ev.preventDefault();

    var data = $(this).serialize();

    $.post('/', data, function(response) {

        console.log(response);

        if (response.success) {
            alert("success")
        } else {
            alert(response.error);
        }
    });
});

And this is my form code:

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

Create An Account

By creating an account, you will be able to save your application progress. This is an optional step.

    Email
    <input type="text" class="form-control" id="email" name="email" {%- if account is defined %} value="{{ account.email }}"{% endif %}>

        Password
        <input id="password" class="form-control" type="password" name="password">

        Confirm Password
        <input id="password2" class="form-control" type="password" name="password2">

<input type="submit" id="form-submit" value="Register" class="btn btn-grey"/>

Already have an account? Sign In

The response var is returning the entire html page rather than JSON data.

Is there a way to trigger a JSON response for the registration form?

Note, I'm able to use similar code for a front-end login form and it's working well.

EDIT

Craft 2.4 is now available, and the users/saveUser action will now return a JSON response for AJAX requests.

The original answer is still valid for <2.4 builds:

No, there's no way to trigger a JSON response for the users/saveUser action.

Other UsersController actions (such as users/login do return JSON for AJAX requests, so it might be an idea to submit this as a feature request – as a consistency measure, if nothing else.

For now, here's a couple of ideas for workarounds:

Write a plugin with a controller that wraps the UsersController->actionSaveUser() method somehow, or implements its own action (you could basically just copy paste the whole actionSaveUser() from the core UsersController, changing it to return some JSON at the end).
You can set the redirect parameter (using a hidden input field in your form) to point to a template where you build the JSON yourself.

Form:

<form method="post" action="" id="register-form" accept-charset="UTF-8">
     <!-- Your form code -->
     <input type="hidden" name="redirect" value="{{ url( 'path/to/your/template/or/a/route' }}">
</form>

Template for the redirect (notice the JSON header):

{% header "Content-Type: application/json" %}
{
    "success" : {{ account is defined and account.id ? true : false }}
}


Eager load commerce order data in custom ElementExporter for Users I have a custom ElementExporter for users and I'd like to include commerce order data. The below code works, but it's a ton of queries. I don't have a ton of experience with SQL queries, how can this be optimized with eager loading?
public function export(ElementQueryInterface $query): array
{
    $results = [];

    /** @var ElementQuery $query */

    foreach ($query->each() as $user) {
        /** @var User $user */

        $orderTotal = '';
        $orderDate = '';
        $order = Order::find()
            ->user($user)
            ->hasPurchasables('41599')
            ->orderBy('dateOrdered desc')
            ->dateOrdered('>= 2020-09-01')
            ->isCompleted(true)
            ->one();

        if ($order) {
            $orderTotal = '$' . $order->totalPrice;
            $orderDate = $order->dateOrdered->format('m/d/Y h:iA T');
        }

        $results[] = [
            'Donation Amount' => $orderTotal ?? '',
            'Donation Date' => $orderDate ?? '',
        ];
    }

    return $results;
}

Your code looks like it actually has a further problem that you're only fetching the first order for a given user. Rather than eager loading (which is basically creating joins so that the relevant data is fetched in one go), why not use a hash map to look up each customer's set of orders? So, outside your users loop, create a separate query to fetch all orders and use GROUP BY to group them by user id. You can then reference each sub-array by its user id inside the users loop.


Building a nav from the top level page of a structure There are several questions like this already but none seem to cover my use case. I have a structure that looks something like this:

Landing Page 1
- Case Studies
-- Case Study 1
-- Case Study 2
-- Case Study 3
- Sub page
- Sub page
Landing Page 2
- Case Studies
-- Case Study 1
-- Case Study 2
-- Case Study 3
- Sub page
- Sub page

If I'm in any child of the 'Landing Pages' I want to create a nav (as per the docs) but I cant' seem to get all of the pages I need. I can easily get the top level 'landing page':

entry.getAncestors().level(1).first()

I can then get the descendants separately:

entry.getAncestors().level(1).first().getDescendants()

However, I want a single ElementCriteriaModel with everything from the 'landing page' down in it.

Is this possible?

Untested, but you should be able to do this by using the merge filter in Twig to join the two results together.

Get the top level 'landing page' for this section:

{% set landingPage = entry.getAncestors().level(1) %}

Get the descendants for this section:

{% set childPages = entry.getAncestors().level(1).getDescendants() %}

Merge the two results together:

{% set sectionPages = [ landingPage ]|merge(childPages) %}

Now create the navigation for the section using the sectionPages variable:

        {% nav page in sectionPages %}

                {{ page.getLink() }}

                {% ifchildren %}

                        {% children %}

                {% endifchildren %}

        {% endnav %}


Grouping entries by category - most efficient approach I have come up with the following code to group entries by categories;

{% set hotTubs = craft.entries()
  .section('newHotTubs')
  .orderBy('price asc')
%}

{% set hotTubsCats = craft.categories()
  .group('newHotTubBrands')
  .relatedTo(hotTubs)
%}

{% block content %}

  {# Loop the categories #}
  {% for category in hotTubsCats.all() %}

    {{ category.title }}

    {# Get and loop through entries related to this cat #}
    {% set hotTubs = craft.entries.relatedTo(category) %}
    {% for entry in hotTubs.all() %}
      {% include '_includes/blocks/related-tub-new' %}
    {% endfor %}

  {% endfor %}

{% endblock %}

However, I wanted to check if this is the most efficient approach or if this could be improved upon?

One improvement I think could be made is to fetch only the ID's of your hotTubs entries at the start of your code. You're only using this to get the related categories and fetching ID's is faster than fetching the actual elements. The relatedTo parameter works perfectly with ID's. So that could speed up things.
You can drop the orderBy parameter also as this doesn't have any effect there.

{% set hotTubs = craft.entries()
  .section('newHotTubs')
  .ids()
%}


Passing null values to queries, Sprout Reports In Sprout Reports, I am creating a list of users in my settings file. I first need an option with value of null.
{% set options = [] %}
{% set options = options|merge([{ label: 'All users', value: null }]) %}
{% for user in users %}
  {% set fullname = user.firstName ~ " " ~ user.lastName %}
    {% set options = options|merge([{ label: fullname, value: user.id }]) %}
{% endfor %}

{{ forms.selectField({
    label: "Choose one"|t,
    name: "userId",
    options: options,
    })
}}

Then, in my index file (the report), I do something like this:
{% set purchases = craft.entries()
  .section('purchases')
  .authorId(settings.userId)
  .all()
%}

My query runs fine if there is a value for userId, but when it is null, the query does not return any results.
What am I doing wrong?
Do I need to do something like this?

Yes, the post you linked to is what you need to do.
I don't know where your settings variable is set so you may need to adjust a bit but something like this should get you started:
{# Start your element query #}
{% set query = craft
    .entries()
    .section('purchases')
%}

{# Check if settings.userId, if so, apply it to the query #}
{% if settings.userId %}
    {% set query = query.authorId(settings.userId) %}
{% endif %}

{# Run the query #}
{% set purchases = query.all() %}


What are the issues with adding Craft Commerce to an existing Craft CMS site? We are about to start on a project that will include a complex product catalogue, with multiple product categories and loads of relationships. The client has stated that they may want to create an online store to sell catalogue items at some point in the future.

The thought at this point is to create the product catalogue using standard Craft CMS features like Structures and Categories. Anyone have any experience or guidance on adding Craft Commerce to an existing Craft site? Will I have to recreate product structures or can build the initial version in a manner that allows Craft Commerce to work with an existing product schema?

You should absolutely use Craft Commerce to store the products, as there is no migration from Entries -> Craft Commerce products.

You don’t need to pay for a Craft Commerce license if you are not utilizing a payment gateway, so there is really no downside to getting things into Commerce in the first place, and plenty of downside to needing it and not having it already in Commerce down the road.


Is it possible to load different "_layouts" with ajax for certain pages? I've followed this tutorial by Zack Spear. It leaves your website with a header and footer that stay constant while the body of the page is loaded in through AJAX when you change pages.

Is it possible to have different _layout templates used for different pages with this approach or can it only be one? Could I hide the header on certain URLs? Could I show a secondary menu on specific urls?

Although you couldn't necessarily change the _layout template with the method given in that tutorial. What you could do is include the header
the templates rather than in the _layout.html for each template and change it out with another header in the other specific URLs. Or hide it, or whatever you like.


Reusable Matrix block types I'm building a site that makes heavy use of the Matrix field, which overall has been great. The one problem I'm running into is that I find myself duplicating a lot of Matrix blocks.

To use the documentation as an example, you see block types such as heading, pull quote, image, etc. Building out a complex site, I find myself using these common types of blocks in various matrixes(matricies?) and the only way I've found to do so is literally to just type the same thing over and over, which leads to more time and potential inconsistencies, not to mention the maintenance.

Obviously I could just define one large matrix field and put it everywhere, but I'd like to have more control than that across different pages. Is there a way to reuse these components or perhaps a different style that is recommended?

Similar question here, but I'm specifically asking about entire blocks, not individual fields.

A few options to consider:

Use one big Matrix Block and a plugin such as Pimp My Matrix to get added control over which blocks display in which channels and various other details.
If you find yourself reusing the same Matrix Patterns from site to site, consider a plugin that can help you manage reusable schema such as Sprout Import or The Architect, both plugins let you define your schema in a JSON format that you can tweak and import multiple times.
I'll add this here as a third option as I think it's relevant for consideration but it probably doesn't address your question about reusing blocks: Consider using a plugin like Neo which takes a Matrix-style approach but let's your blocks use global fields which you can reuse as well as several other features.


Craft Commerce: Custom Rounding (for taxes and discounts) Is there a way to change the default rounding behaviour of Craft Commerce? Here in Switzerland we always round to 5 cents, so amounts like 9.99 or 9.22 don't really exist. Here's an example:

9.99 must be rounded to 10.00
9.98 must be rounded to 10.00
9.97 must be rounded to 9.95
9.96 must be rounded to 9.95
9.95 must stay the same
9.94 must be rounded to 9.95
9.93 must be rounded to 9.95
9.92 must be rounded to 9.90
9.91 must be rounded to 9.90

Product Prices
The product prices we enter manually do follow the guidelines mentioned above, so we never get "odd" prices when putting products to the cart.

Shipping Costs
Shipping costs are also no problem (it's either free or fixed price shipping).

Taxes
Taxes are a problem for us since we use a tax rate of 7.7% which can lead to "odd" tax amounts (and this leads to "odd" total amounts).

Discounts
Discounts can also be a problem since they also sometimes lead to "odd" numbers (i.e. a 10% coupon on 14.85 is giving a discount result of -1.49 instead of -1.50).

So is there any way to change the rounding behaviour (either globally or for taxes only)? On the frontend part I think we could achieve that with the Twig filter "number_format", but we need the correct amounts also in the backend.

The rounding you are talking about is for cash. The rounding is to nearest 5c because it is likely the smallest denomination in circulation.

Online, with electronic transactions, the base will always be whole cents (or localised equivalent.) Credit Card and electronic purchases can always transaction in these minor units.

Craft Commerce doesn't have the ability to round to cash-based values (likely to be used in POS), but you are welcome to make a feature request here: http://feedback.craftcms.com/


Get image asset relative url I'm using image transforms and getUrl() method to display an image.
I'm using Blitz to export my site as a static website.
My problem is that .html pages get cached with images that have full urls like this: http://localhost:8888/assets/pages/_medium/image.jpg
There must be an obvious solution that i'm missing... I've tried getPath but it doesn't support image transforms. Removing http://localhost:8888 should do the job.. but how?

Have a look at the settings of your asset's volume:
Go to: Settings > Assets > [Your Volume Name here]
Check the field "Base URL". If it starts with the alias @web, remove it.
E.g. "@web/images" -> "/images".
Now check the URL of your image again.


Using Craft Prune - convert JSON Array of Objects to simple array I am using the Craft Prune plugin (https://github.com/mattstauffer/craftcms-prune) to pull out a JSON array of entries from a section of my site with the aim of integrating the data into a typeahead search field as autocomplete data.

The problem I seem to be having is that the JSON array structure needs to be simplified for use as a datasource with typeahead.

So the current JSON data structure is:

[{"title":"Entry One Title"}, {"title":"Entry Two Title"}, {"title":"Entry Three Title"}]

What I actually need the structure to be is:

["Entry One Title", "Entry Two Title", "Entry Three Title"]

I have already tried using the jQuery $.each() function and the jQuery $.map() function but they both simply return an array of objects not a simple JS array.

Any help appreciated.

Ended up working out a solution:

var myData = {{ craft.entries.section('sectionName').find() | prune(['title']) | json_encode() | raw  }};
    var myArray = $.makeArray(myData);
    var newArray = new Array();
    $.each( arr, function(key, value) {
        $.each(value, function(keyNew, valueNew){
            newArray.push(valueNew);
        });
    });


Massive template cache cleaning tasks always get stuck We have a site where we use lots of {% cache %} tags.

Now we almost constantly have a "cleaning template cache" task running, and they often get stuck.

Manual DB intervention and resetting the stuck task to status pending, will sometimes make it move again.

I've tried the various solution here, but nothing helps: DeleteStaleTemplateCaches job getting stuck

Also - there are no error entries in craft.log or phperrors.log when it gets stuck.

Looking at the craft_templatecaches table I find lots of duplicate looking entries:

select id,locale, cacheKey, expiryDate, path from craft_templatecaches;

...
| 685607 | nb     | zDzBdD8bc1y4pf9Sx2HFyL3i24YypSp2sAq5 | 2015-09-08 08:23:21 | site:kompetansetilbud/ny-i-hr-rollen-i-trondheim-10013                                   |
| 685610 | nb     | TMz4eh7FPiINSUixwkpXb0SlkHnZ3b5TSgXm | 2015-09-08 08:23:22 | site:kompetansetilbud/ny-i-hr-rollen-i-trondheim-10013                                   |
| 685611 | nb     | HgLIHHgf56nM1ggOdTmaLlaylu5JeOeM1dNm | 2015-09-08 08:23:23 | site:kompetansetilbud/ny-i-hr-rollen-i-trondheim-10013                                   |
| 685612 | nb     | SLHUKfUz2fF5Jmpy0kWPpvMxFBHH8PvlVJEt | 2015-09-08 08:23:25 | site:kompetansetilbud/ny-i-hr-rollen-i-trondheim-10013                                   |
| 685614 | nb     | oEU6fe1tGc7MdI8WSQIXsf2wZZd9qmXG8P9H | 2015-09-08 08:23:26 | site:kompetansetilbud/ny-i-hr-rollen-i-trondheim-10013                                   |
...

Is this normal?

Anything else I can do to debug why the task gets stuck?

Update:

Using keyed and global caching reduced the size of the craft_templatecaches table from ~20k rows to ~300. However, this did not stop the task getting stuck.

As suggested by Fred, using the {% cache globally using key craft.request.path %} trick from https://craftcms.stackexchange.com/a/8335/342 gets rid of almost all duplicates!

But for the last few I notice that the duplicates all expire within seconds of each other, maybe this is an instance of https://en.wikipedia.org/wiki/Cache_stampede ?

I.e. several clients request the pages "simultaneously" and the page is rendered several times, and each version is cached?


Reusable Field-Configurations In my current website-project I have to repeat myself very often. In several sections / matrix fields I use field configurations like:

Spacing (Dropdown: small, medium, large)
Buttons (SuperTable with Linkits)
...

The problem is if I add a new spacing option, I have to change multiple field configurations.

Is there an approach to move this field configuration to a "component" and use a "Buttons" field instead of creating a SuperTable field for each section again?

If you're looking to reuse existing fields, you may want to take a look at the Neo plugin: https://github.com/spicywebau/craft-neo


Craft Cookies Plugin: Font Loading Detection I'm trying to re-write my font loading detection using the Craft Cookies Plugin. I'm unsure if what I have done is correct...

Before

<!--# if expr="$HTTP_COOKIE=/fonts\-loaded\=1/" -->
<html class="fonts-loaded" lang="{{ craft.app.language |slice(0,2) }}" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<!--# else -->
<html lang="{{ craft.app.language |slice(0,2) }}" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<!--# endif -->

After

{% if getCookie('fonts-loaded') %}
    {% do setCookie('fonts-loaded', fontLoad, now | date_modify("+1 hour").timestamp) %}
    <html class="fonts-loaded" lang="{{ craft.app.language |slice(0,2) }}" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
{% else %}
      <html lang="{{ craft.app.language |slice(0,2) }}" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
{% endif %}

So I'm not sure what you're accomplishing with this line:

    {% do setCookie('fonts-loaded', fontLoad, now | date_modify("+1 hour").timestamp) %}

I guess trying to give the cookie a longer duration if it was already set? Probably it's best to set the fonts-loaded cookie via JavaScript once FontFaceObserver tells us that the font as been loaded; there's probably no reason to do it here.

FWIW, we can start using the font-display property as a progressive enhancement now to want to eliminate FOUT on modern browsers. Older browsers will still work, but they'll get FOUT: https://caniuse.com/#search=font-display


Urgent question - failed plugin and now getting internal server error Hope someone can help asap - we tried to install a plugin through craftcms 3 (Cache Flag) and now getting 'The configuration for the "gc" component must contain a "class" element.'

Does anyone know how to resolve this?

This seems unrelated to the Cache Flag plugin, and more likely due to some bad code in config/app.php (or app.web.php).

If you have either of those files, check what changes you’ve made to it/them recently.


Tracking SQL queries from CP changes We are developing over multiple environments and production stages, and we would like to be able to track the SQL changes that happen when we make a change to entries etc. on the CP. The changes don't seem to be very easily transferable between productions e.g. Development and Staging through something like an mysql_delta.

Is this possible already? If not, what would the process be to record the SQL?

Lots of smart people have tried to solve this problem and it's a tough one to solve, not just in Craft land but WP, and others. And because of Craft's flexible nature, a change in one table usually means many more updates from others.

Arguably the most reliable way I've seen it done is to use a "top-down" approach. Make the content changes in the production data first, then pull them down to staging/dev since production is essentially the "canonical" version.

If you need to make a new field or whatever to keep things current, add it on both sides. Then do your logic part—anything that could cause something to break the front end—on staging.

If you write your own logic for plugins, look into migrations; Craft can handle schema changes pretty well from an old schema to a new one if you write them properly. If you find your developers are doing this a lot, you may need to revisit your content strategy.

Keeping the content in sync is one thing; doing dev work on a rapidly changing schema is tough no matter how you keep things in sync.

Some tools to help with this:

Audit Log
Schematic
Field Manager

This thread has more details on different environments; here's another that talks about some tools I just linked up above.

Would be awesome for P&T to pull a rabbit out of their hat and have some sort of sync across environments as a first party solution, like they did with Commerce. If you want to see something like that, vote for it.


Trouble finding my custom asset bundle I have a custom asset bundle defined in my plugin. My plugin has a controller. The controller has a action:

/**
 * Handle a request going to our plugin's index action URL,
 * e.g.: actions/entries-by-group/entries-by-group
 *
 * @return mixed
 * @throws \yii\base\InvalidConfigException
 */
public function actionIndex()
{
    $this->view->registerAssetBundle(EntriesByGroupAssets::class);
    $result = 'Welcome to the EntriesByGroupController actionIndex() method';

    return $result;
}

As you can see, I am trying to register my asset bundle. I am getting the error:

  ReflectionException

  Class recoveryfinder\entriesbygroup\assets\EntriesByGroupAssets does
  not exist

My namespaces are:

namespace recoveryfinder\entriesbygroup\controller;

or in the asset bundle file:

namespace recoveryfinder\entriesbygroup\assets;

My plugin is in a folder called (note the hyphens):

  vendor\recoveryfinder\entries-by-group\src\controllers\EntriesByGroupController.php

It seems that when code gets to ClassLoader::LoadClass I get the error referenced above - class does not exist. It looks to me like ClassLoader is using the namespace as the path spec (without the hyphens) rather than the actual location (with the hyphens).

I must need to set paths in my plugin? If not what is wrong here?

The autoloader won't load files in the folder assets as already stated it is all explained here. Your asset bundles should be in a folder called resources otherwise Yii won't register them in its classmap automatically.

Your namespaces must represent your folder structure. The namespace ns\prefix\controller must be for a file in vendor\ns\prefix\src\controller so your namespace ns\prefix\assets will search for a file in ...vendor\ns\prefix\src\assets but you have to include the resource on your own via require_once since Yii won't so it because you are in the wrong folder

The easiest solution would be to create a folder resources and use the namespace ns\prefix\src\resources

Or you can - like stated in the docs - use your plugin root path as well recoveryfinder\entriesbygroup


License for multiple domains My usual development process is to build the site locally, then upload on a staging subdomain for client reviews, going back and fourth between the two environments for updates, before deploying it on its live environment.

From what I understand you can use the one licence key on multiple environments but you can only then login to the Admin area from one of them? Which would cause me issue when I have the staging site up but I'm working between that and the dev version?

Alternatively I can use Craft for 'Free' using a appropriate .dev or staging URL (eg. http://staging-subdomain.mysite.com) and then make sure the paid for license is in place on the live version. Is that correct?

From what I understand you can use the one licence key on multiple
  environments but you can only then login to the Admin area from one of
  them?

Sort of – you'll be able to login everywhere, but if the license isn't valid the CP will be blocked behind a "upgrade-wall", prompting you to purchase a valid license or downgrading to Personal (the free edition). The frontend will continue to function as normal, though.

  Alternatively I can use Craft for 'Free' using a appropriate .dev or
  staging URL (eg. http://staging-subdomain.mysite.com) and then make
  sure the paid for license is in place on the live version. Is that
  correct?

Yes. From the docs:

  You’re allowed to run a single Craft license on multiple domains (e.g.
  example.com and example.fr), so long as they’re all a part of the same
  website. In order to enforce that, Craft does have one technical
  limitation: you may only access Craft’s control panel from one public
  domain per Craft license. (There is no such restriction on non-public
  domains, though.)

One of the ways Craft determines if the domain is non-public, is if the site has a dev-sounding sub domain (e.g. dev, local, stage, or staging). This means that a Client or Pro license registered to www.mysite.com should also work for a staging site at http://stage.mysite.com.


How might I select specific data in an entry to be displayed when selecting an entry? I am not sure if i need a plugin to accomplish a specific task or if Craft CMS can natively handle this task.

I created bios for people in a channel of bios. Each entry has a Job Title text field to store a single job title.

So, I want to store a list or array of multiple job titles for each person/entry. Then, I want to choose which job title is rendered when I use the entries field to select a bio to render.

Any ideas? Thank you.

Probably a dozen different ways to tackle this, but I'd probably use Craft's flexible relationship engine by creating a Bios channel and a Jobs channel.

The Bios channel would have an entries field assigned to its field layout with its source set to Jobs and it would allow for multiple selections.

Then, when creating a new Bios entry, you'd select the Job(s) for that person.

You could pull them out into the front-end however you'd like at that point.


How to get the previous or next two entries I have an entry which needs to display the previous two entries, and the next two. I see in the docs there's a nextSiblingOf method. That does nothing in a channel, and in a structure it will only return one entry. I can't see an ability to "offset" the result.

1: Is this possible in a channel

2: What do I need to do to grab the previous/next two entries in either case?

In a channel this would be the next entries where the post date is before and after the current entry’s post date. The element queries for it would look like so:

{% set query = craft.entries
    .section('blog')
    .id('not ' ~ entry.id)
    .limit(2) %}

{% set prevEntries = clone(query)
    .postDate('<= ' ~ entry.postDate.timestamp)
    .order('postDate desc')
    .all() %}

{% set nextEntries = clone(query)
    .postDate('>= ' ~ entry.postDate.timestamp)
    .order('postDate asc')
    .all() %}

If your sort the entries by title, something like this should work:

{% set prevEntries = clone(query)
    .title('<= ' ~ entry.title)
    .order('title desc')
    .all() %}

{% set nextEntries = clone(query)
    .title('>= ' ~ entry.title)
    .order('title asc')
    .all() %}