Mixed relation criteria operator in Element API I have a bit complex filtering system that I try to replicate using Element API.

So far I have this which works to a degree:

Example url: training.json?reg=124&cat=99&prod=129&erp=

'criteria' => [
            'section' => 'resources',
            'search' => (craft()->request->getParam('q') ? craft()->request->getParam('q')."*" : ''),
            'relatedTo' => [
                'and',
                craft()->request->getParam('reg'),
                (craft()->request->getParam('cat') ? craft()->request->getParam('cat') : craft()->request->getParam('reg')),
                (craft()->request->getParam('prod') ? craft()->request->getParam('prod') : craft()->request->getParam('reg')),
                (craft()->request->getParam('erp') ? craft()->request->getParam('erp') : craft()->request->getParam('reg')),
            ],
  ...

First of all, I don't know how to deal with optional parameters, I've solved this by re-using the only required parameter - reg. It works, but it's not nice, any help here would be appreciated.

The real issue though is that I need the reg parameter to be an array with all the elements in the array having OR operator.

So basically:

Example url: training.json?reg[]=124&reg=[]=123&cat=99&prod=129&erp=

(reg[0] OR reg[1]) AND cat AND prod AND erp

Is there any way I can achieve this in element API? I've tried something in the lines of the following, but with no luck.

'relatedTo' => [
     'and',
      ['targetRegions' => ['or', 124, 99]],
      105,
      231,
 ],

Something like this should work to combine 'and' and 'or' conditions.

'relatedTo' => [
    'and',
    ['element' => [124, 99]],
    ['element' => 105],
    ['element' => 231],
],

And this is the pattern I’m usually using to build the relatedTo param dynamically. I also try to be explicit about the direction (sourceElement / targetElement) and the relation’s field.

$query = craft()->request->getQuery();

$relatedToParam = ['and'];

if ($query['reg']) {
    $relatedToParam[] = [
        'targetElement' => $query['reg'],
        'field' => 'myRegFieldHandle',
    ];
}

// repeat for all our other relatedTo conditions

$relatedToParam = count($relatedToParam) > 1 ? $relatedToParam : null,

return [
    'criteria' => [
        'section' => 'resources',
        'relatedTo' => $relatedToParam,
    ],
];


Craft install locally with MAMP Pro/versioning So... I've installed Craft online, and I wanted to start templating my CraftCMS, but I'd like to work with a repository so I can have some kind of versioning. The easiest way to do so seemed to me to install Craft on our hosting server, download those files locally and connect to the online database using MAMP pro.
I had some issues with our host (I had to whitelist my IP address), but now I can connect to the database using Sequel Pro, but when I try to access my craft CMS site through my local files I get the Craft message "service unavailable".
My Mamp Pro is pointed to my public folder (2 folders in my site: public and craft, and it's pointed to this public folder with the index file in it) and I've used the same db settings as the ones I've used in Sequal Pro. Still: no succes.
The only thing I've changed after downloading the site are these db setting for the host - as I had to do in Sequal Pro.
Can anyone help a newbie in Craft CMS out here :-)? Thanks!

Here is how I have set things up in the past using a similar workflow.

When you get Craft out of the box (for lack of better words) as you said there is a craft folder and a public folder. Generally you want your craft folder above/out of your web root. That would look something like this:

craft/
   |- app
   |- config
 ...

public/
   |- index.php
   |- robots.txt

When setting up your site, point your doc_root to the public/ folder.
For example, if you are using a Mac, a default doc_root might look like this:

<VirtualHost *:80>
    ServerName sitename.dev
    DocumentRoot "/Applications/MAMP/htdocs/sitename/public"
</VirtualHost>

If successful, you should see a screen saying that your site is unavailable. I can't remember the exact verbiage.

From there I will edit two files:

/craft/config/db.php
/craft/config/general.php

db.php

return array(
    '*' => array(  // Global configuration
        'tablePrefix' => ''
    ),
    '.dev' => array( // local configuration
        'server' => '127.0.0.1',
        'user' => 'username',
        'password' => 'password',
        'database' => 'database'
    ),
    '.com' => array( // production configuration
        'server' => 'hostname',
        'user' => 'username',
        'password' => 'password',
        'database' => 'database'
    )
);

general.php

return array(
    '*' => array(
        'cpTrigger' => 'your-custom-admin-url',
        'omitScriptNameInUrls' => true
    ),
        '.dev' => array(  // local configuration
            'devMode' => true,
            'siteUrl' => 'http://mysite.dev/',
            'environmentVariables' => array(
                'basePath' => $_SERVER['DOCUMENT_ROOT'] . '/',
                'baseUrl' => 'http://mysite.dev/',
        )
    ),
    '.com' => array(  // production configuration
        'siteUrl' => 'http://www.mysite.com/',
        'environmentVariables' => array(
            'basePath' => $_SERVER['DOCUMENT_ROOT'] . '/',
            'baseUrl' => 'http://www.mysite.com'
        )
    )
);

By having the multi-environment settings like the above, you can commit your code and it will run regardless of environment.

Generally, I like to have a local database which is separate from my production db. My luck, I'll do something silly and delete something on accident. Depending on how big of changes you are making post-launch, I am sure there are good/better/best ways to migrate the updates to your production db vs. manually entering it. But, if they are simple things (added a new field) I usually just do that manually on my production server.

It sounds like if you can connect to your production db via a 3rd party tool, but not your application there might be some permission trouble (just a guess). Try working locally with everything first, then move it to your other server.

Hope this helps!


Craft Forum/Community options Im curious to hear more about what any of you are using to build "community"/ "forum" messaging options into your craft cms site.

From what I've found it seems there would be a couple of interesting options

Socialize Plugin https://dukt.net/craft/socialize

Its in private beta and I haven't tried it but it seems to have a lot of the features we would need.

The big option questions would be how to manage any group messaging features but maybe you just have a "posts" channel that users can post to?

Muut https://muut.com/

Allows for an iframe type integration. Seems to be pretty full featured but wouldnt really share any data (or logins) with craft, would be a visual integration only

Any other options

We aren't looking for a ton of features, just a way for people to login, share text and photos, and comment on those posts.

Curious if anyone out there has tackled a problem like this?

Out side of what you have mentioned, I recall Discourse being mentioned on the Google+ Community. That's written in Ruby, though.

Vanilla Forums is open-source and written in PHP. It's also comes with SSO (Single-Sign On), and can be embedded into another site via an iframe.

There are currently no announced forum plugins native to Craft.


Entries as Flat Files Is there any way to have some entries exist as flat files? Considering moving my blog over from Statamic where I have a boatload of Markdown files that work as article entries.

It seems like Craft can do anything! I wonder if there's someway to do a combo thing where some entries exist in the database and others in a directory.

Is this possible?

Short answer; no. Craft uses a database to store and query for content and doesn't have any way to work with content in files the way that Statamic has.

But, migrating the content into Craft's database doesn't have to be a big challenge.

If you're a PHP-developer, creating a plugin that parses the files and inserts the content into Craft is a breeze.

If you're not a developer (or if you are, but prefer a less code intensive way of doing it), there're several plugins for Craft that helps you importing content into the Craft database. Have a look at Feed Me from engram design, or Import from Bob Olde Hampsink. You'd then create a simple front-end template in Statamic that queries the data you want to move over, outputs xml or json, and then send that data to the plugin which do the heavy lifting.

I've used Feed Me on several occasions to move data from ExpressionEngine sites to Craft, and it works great.


TWIG dataUrl function for resized images Craft provides the very nice TWIG function dataUrl(pathToImg) to inline images in e-mails.
E.g.

will base64 encode the image into the src. This also works if you pass in an instance of an asset:
{% set myAsset = craft.assets.id(123).one() %}

My question: Does anyone know how to resize the image first, if you pass in an asset?
This does not work:

Found it, this works:
<img src="{{ dataUrl('@webroot' ~ myAsset.getUrl({width: 200, height: 200}, true)) }}" />

Two important things:

prepend the @webroot alias to the myAsset.getUrl(...)
set the second parameter of getUrl(...) to true: myAsset.getUrl({...}, true). This ensures that a real file path is returned by getUrl, because the image is transformed immediately on call. This is necessary for the dataUrl function to work. If it is not present, Craft might return a URL pointing to the image transformation controller (which transforms when called for the first time, this might go wrong here).


Field type for selecting a section (channel / structure / single)? I have a channel for products but in some cases I would like to associate a product with a structure containing documentation and release note pages:

http://mysite.com/product/my-product/docs/getting-started
                          ^--------^      ^-------------^
                              |                  |
                           channel             topic (from 'myProductStructure')

Is there an existing field type (or plugin) which would allow selection of the associated structure type:

Title: My Product  [text]
Doc Source: My Product Structure  [drop-down]

Such that I could use that in templates?

{% for entry in craft.entries.section( product.docSource ).slug( craft.request.lastSegment ) %}
    ...
{% endfor %}

You can do this with LJ DynamicFields. In the plugin docs is an example of how to create a dropdown that is a list of all sections.


How can I add a dynamic number of rows to a matrix via plugin? I would like to have a front-end profile form on my website that contains a matrix field. The matrix field will allow the user to add an infinite number of awards they have earned. How can I do this?

There are a few threads on this that I found helpful. I think they are all worth looking at and reading through as my solution may vary based on your needs.

Save matrix block to new entry in plugin
How to save a matrix content of a new entry in my plugin?
Saving new Matrix data

Here is the complete solution that ended up working. If there are improvements, please let me know so I can update the thread accordingly!

twig

<form method="post" accept-charset="utf-8">

    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="profile/saveProfile">

    ...

    {% if profile.awardsHonors|length %}

            {% for block in profile.awardsHonors %}

                        <input type="hidden" name="fields[awardsHonors][{{ block.id }}][type]" value="award">
                        <input type="hidden" name="fields[awardsHonors][{{ block.id }}][enabled]" value="1">

                        <input type="text" name="fields[awardsHonors][{{ block.id }}][fields][award]" class="form-control" value="{{ block.award }}">

                {% set blockId = block.id %}
            {% endfor %}

    {% else %}

    {% endif %}

    ...

    <button type="button" id="addAward">Add Award</button>

    ...
</form>

javascript

var awardBlockNum = 1;

$('#addAward').click(function (event) {
    var markup = '
' +
        '
' +
        '<input type="hidden" name="fields[awardsHonors][new' + awardBlockNum + '][type]" value="award">' +
        '<input type="hidden" name="fields[awardsHonors][new' + awardBlockNum + '][enabled]" value="1">' +
        '<input type="text" name="fields[awardsHonors][new' + awardBlockNum + '][fields][award]" class="form-control">' +
        '
' +
        '
';

        $(markup).appendTo($('#awards'));

        awardBlockNum++;
    });

MyPluginController

public function actionSaveProfile()
{
    $criteria = craft()->elements->getCriteria(ElementType::Entry);
    $criteria->section = 'mySectionHandle';
    $criteria->limit = 1;
    $criteria->status = null;
    $criteria->authorId = craft()->request->getPost('userId');

    $entries = $criteria->find();

    if ($entries) {
        foreach ($criteria as $entry) {
            $awardMatrixData = array();

            /**
             * New matrix fields are prefixed with 'new1'.
             * To compensate we need to create a new increment variable to correctly store the matrix number
             * while looping the correct amount of times.
             */

            foreach (craft()->request->getPost('fields.awardsHonors') as $key => $value) {
                $i = 1;

                if ($key == 'new' . $i) { // Check for a new award.
                    $awardMatrixData['new' . $i] = array(
                        'type' => 'award',
                        'enabled' => true,
                        'fields' => array(
                            'award' => craft()->request->getPost('fields.awardsHonors.new' . $i . '.fields.award')
                        )
                    );
                } else { // Update existing awards.
                    $awardMatrixData[$key] = array(
                        'type' => 'award',
                        'enabled' => true,
                        'fields' => array(
                            'award' => craft()->request->getPost('fields.awardsHonors.' . $key . '.fields.award')
                        )
                    );
                }

                $i++;
            }

            $entry->setContentFromPost(array(
                ...
                ...
                'awardsHonors' => $matrixData
            ));

            if (craft()->entries->saveEntry($entry)) {
                // Profile updated!;
            } else {
                // Could not save entry (profile).
            }
        }
    } else {
        // No entry found that matches criteria.
    }
}


Custom fields with Mandrill Form Plugin Is it possible to add custom fields to a contact from that is using the
MandrillForm Plugin?

I need to add a field for phone

Looking at the code for the plugin, it doesn't look like it.

You'd either need to make a feature request or fork the plugin, add the functionality yourself and make a pull request.


ElementCriteriaModel andWhere() with different comparison operators? I'm trying to do an andWhere() "OR" group for a special criteria value so basically when building an element criteria I want to just set `$criteria->upcomingOnly to true, and then have the following code run. The problem is that the greater than or equal to is being added to the value and its doing an equals comparison on it which obviously is going to return nothing. Anyone have any idea what I need to change to get desired result?

if($criteria->upcomingOnly) {
    $today = new DateTime('now');
    $query->andWhere(['or',
        ['events.eventStart' => '>= ' . $today->format('Y-m-d')],
        ['events.eventEnd' => '>= ' . $today->format('Y-m-d')]
    ]);
}

Basically the resulting query is

WHERE ((elements_i18n.locale = :locale) AND (elements.archived = 0)) AND ((`events`.`eventStart`=:pzek8XoNML) OR (`events`.`eventEnd`=:pZQmaPhwE8))
GROUP BY `elements`.`id`
ORDER BY `eventStart` ASC LIMIT 50. Bound with :locale='en_us', :pzek8XoNML='>= 2017-10-06', :pZQmaPhwE8='>= 2017-10-06'

And I would like to get something like this:

WHERE ((elements_i18n.locale = :locale) AND (elements.archived = 0)) AND ((`events`.`eventStart`>=:pzek8XoNML) OR (`events`.`eventEnd`>=:pZQmaPhwE8))
GROUP BY `elements`.`id`
ORDER BY `eventStart` ASC LIMIT 50. Bound with :locale='en_us', :pzek8XoNML='2017-10-06', :pZQmaPhwE8='2017-10-06'

You shouldn't need to use nested array syntax, just normal strings should work.

100% untested, but something like this:

if ($criteria->upcomingOnly) {
    $query->andWhere(['or',
            'events.eventStart >= :date',
            'events.eventEnd => :date',
        ],
        [':date' => new DateTime('now')->format('Y-m-d')]
    );
}

(I fixed the missing quote, so no, there is nothing else I would like to add to this post...)


How could I create a button to loop through entries and re-save them? I've been working on a plugin that updates entry data based on the results from a json array using entries.onBeforeSaveEntry.

Since this API might put out different information in the future, I would like to create an "update all" button that would cycle through all entries and just save them rather than going through each one and clicking save manually.

Any ideas?

If all you want to do is re-save all of the entries without making any modifications to them, you're best bet is to use the completely undocumented TaskService and trigger the ResaveElements task.

There is an example of how to do it from your plugin here.


Trigger FeedMe import from plugin I'm importing Real Estate properties every 30 minutes into my website.
I have a controller action that triggers this but the import is extremely slow and blocks the entire site.

Right now I'm triggering the imports with this code:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_exec($ch);
curl_close($ch);

Where the url is the cron url of the feed.

FeedMe shows you what the import trigger endpoint that you can use with curl in the feed configuration.


Importing data from an old EE site I've been asked to export some data from an old EEv2 site into a Craft site. I thought I should be able to export the data from EE into CSV or XML and then use FeedMe to import it.
But one of the fields on the EE site was using Content Elements which is similar to Matrix or the EE Fluid field.
So before I convert that data into a format that is compatible with EE6, I need to know what format it should be in to get it into Craft.
The Content Elements field was made of headings, an Assets field, a pill field which set alignment for the image, and a Wygwam (RTE) field.
Is the best option to set this up as a Matrix field in EE? Or would I be better using a Fluid field?
Also, has anyone done any exports from EE to Craft using Smart Export which involved Matrix fields in Craft? If so, any things to look out for?

I have found that the best format for me is XML, it's easy to write and read. I've moved a number of EE2 sites to Craft by writing export templates using XML and importing into Craft with Feedme.
It's relatively simple, but can be tedious and time consuming as there are often tweaks needed either in the XML or the feedme settings.
I have a blog article I wrote a while ago that may help. Importing Entries into a Multi Lingual Craft CMS Website
I did not use the Smart Export Pro plugin but wrote my XML export templates by hand, I wasn't aware of that this existed.

I haven't used Content Elements but you say that it is similar to Craft Matrix so in that case I would look at the docs for importing into a Matrix field.https://docs.craftcms.com/feed-me/v4/guides/importing-into-matrix.html#setup-your-feed.
I have used this once to import into Craft 3 from a Craft 2 site with the code below. You should be able to use this as a model for your EE feed that you want to put into a Craft matrix. Below is the relevant bit from a larger feed.
    <recollection_asset_type_matrix>
      {% for block in entry.recollectionAssetType %}
        <matrix_block>
          {% switch block.type %}

            {# video #}
            {% case "video" %}
              <subtitle_file>{{ block.subtitleFile.first().url() }}</subtitle_file>
              <subtitle_label><![CDATA[{{ block.subtitleLabel }}]]></subtitle_label>
              <vimeo_url><![CDATA[{{ block.vimeoFilmURL }}]]></vimeo_url>
              <runtime><![CDATA[{{ block.runTime }}]]></runtime>
              <poster_image>{{ block.posterImage.first().url() }}</poster_image>

            {# picture gallery #}
            {% case "pictureGallery" %}
              <gallery_bg_image>{{ block.backgroundImage.first().url() }}</gallery_bg_image>
              <gallery_audio_file>{{ block.audioFile.first().url() }}</gallery_audio_file>
              <gallery_images>
                {% for image in block.images %}
                  <image>{{ image.url() }}</image>
                {% endfor %}
              </gallery_images>

            {# memoir #}
            {% case "memoir" %}
              <memoir_tagline><![CDATA[{{ block.tagline }}]]></memoir_tagline>
              <memoir_bg_image>{{ block.backgroundImage.first().url() }}</memoir_bg_image>
              <memoir_copy><![CDATA[{{ block.copy }}]]></memoir_copy>
              <memoir_audio_file>{{ block.audioFile.first().url() }}</memoir_audio_file>

          {% endswitch %}
        </matrix_block>
      {% endfor %}
    </recollection_asset_type_matrix>

Unfortunately this import is long done so I am unable to take screenshots of the feedme settings.


Class 'Craft\DbCommand' not found Getting this error in the PHP error log file while trying to install craft.

PHP Fatal error:  Class 'Craft\DbCommand' not found

The file is there, permissions are granted and I've tried reinstalling twice. Any other ideas?

Even thought this is an old thread, I recently ran in to the same problem. It turned out to be a PHP version issue. I was running MAMP with PHP 7.0.0, once I switched that host to 5.6.10 it worked perfectly.


Craft Commerce WorldPay integration callback failure WorldPay looks to be a terrible Payment Gateway, but it's supported by Craft Commerce so we've gone ahead. Right now, I'm having a problem with the callback's failing with a 302 error.

Our site is updating the order following payment, but the user gets stuck on WorldPay's default thank you page.

Has anyone used Craft Commerce with WorldPay yet?

I have answered your support ticket and given you the fix for worldpay. It will be included in the next release of Craft Commerce.


Hiding Craft's admin folder from search engines I am trying to hide our website's admin directory from public view as best as possible and have read the Securing Craft article. I am pleased at how easy it is to make a directory that isn't easy to guess using the cpTrigger setting in the config file.

My next thought was to disallow the directory on the robot.txt file, keeping crawlers from indexing the login page. The obvious issue with that is that now the "hidden" admin directory is in public view from our robot.txt file, not exactly what I want to do.

Is there something in the metatags or HTML of the Craft admin pages that blocks crawlers from indexing the admin pages? I was hoping to see the following on the admin pages:

But they are not present. Is there a reason we would not want these by default on the Craft admin pages?

Please advise on the best practice here, perhaps I am overthinking things, or there is another way altogether to accomplish this.

So if you follow best practices, the craft/ directory should just never be in the public webserver root to begin with. And if it's not, search engines are never going to be able to index it.

The directories should look like this:

craft/
public/

In other words, they are side by site, and Craft is never in the public web server directory.

Craft's admin/login pages already send the x-robots-tag: none header, which tells Google et al not to index those pages. Learn More

I've created many Craft sites over the years, all of them still have /admin as the AdminCP trigger, none of them have been indexed, and I've had zero security breaches/brute force attacks.

Doesn't mean it can't/won't happen, but security through obscurity only gets you so far anyway. Put the craft/ directory out of the public root, follow the advice in Hardening Craft CMS Permissions and sleep well at night.


Temporarily access to an entry I have an entry that is currently password protected and I am looking for a way to give access to this entry to non-craft users temporarily access either by a token or something else. Anyone know how I can accomplish this?

You can share a temporary link to an unpublished entry by clicking on the "Share" button, right next to the "Live Preview" button.

Craft 2:

Craft 3:

This creates a link with a token that will expire automatically. The length of time that the token lasts for can be controlled with the defaultTokenDuration config setting.


Add a shippingCost per lineItem rather than per Order Each Product is associated with a Merchant (a standard Craft User), who has 2-3 flat-rate Shipping Prices. There could be any number of Merchants and Products.

What I would like to do is on the Shipping page of Checkout, allow the Customer to select a Shipping method from a drop-down per LineItem. Then when they progress to the Payment Page, these Shipping Costs are Totalled and applied to the overall Order.

I've got everything up to the drop-downs on the Shipping page and I'm able to save the value the Customer has chosen to the LineItem via the Options collection, but that's where I've hit a brick wall.

I've had a look at Adjusters and I don't think that's the solution. It was suggested on the Craft Slack that Custom Shipping Methods might be a way forward, but they are very heavily geared towards applying the Shipping Method to the whole Order rather than having separate ones for each lineItem.

Does anyone have any ideas on a suitable approach? Any help gratefully received.

You're right that the shipping calculator API doesn't lend itself to different rates per lineItem as such, but I'm pretty sure you can still make it do what you want.  (But longer term I agree that getLineItemRate(lineItem) or similar should be an option!).

Basically, for now, you can do anything you like in the getBaseRate() and return the total from that to the cart.  (Personally I implement all my shipping stuff as services so I can call them from controllers etc too (e.g. to do shipping quotes via a controller separate to the cart as such - but in this I'll do ti right here).  But you can also do stuff like save data to the order in here, which should be enough to make this work for you.

Basically, you'd loop through the lineItems, looking at the value in the dropdown for each and using that to sum up your shipping costs.  You probably want to list out the chosen options and costs for each lineitem separately on that payment page, so you'll also need to save that data somewhere too...so create a holder variable lineItemShippingOptions in your order for that data and set it as part of the process so you can refer to it in the later templates.

(I'd use a non-editable incognito field for that so your order processors can't accidentally change the data later - https://github.com/mmikkel/IncognitoField-Craft (I have PR-ed an improvement to that for just this sort of thing).

(Obviously - this is untested sketch code!)

public function getBaseRate()
{
   $shippingCost = 0;
   $dataToSave = [];

   $this->cart = craft()->commerce_cart->getCart();
   foreach ($cart->lineItems as $item){
        $shippingOptionChosen = item.options.whatever;
        if($shippingOptionChosen == "service1" ){
            $shippingCost = $20;
            $dataToSave[item.id] = ['chosenOption'=>$shippingOptionChosen, 'chosenPrice'=>20];
        }
        elseif ($shippingOptionChosen == "service2"){
            etc
        }
   }
   //Save this data in to the order
   $this->cart->setContentFromPost(['lineItemShippingOptions'=>$dataToSave]);
}

Then in your templates you can refer to lineItemShippingOptions to print the info out to the customer.  (That's probably not the ideal way to build the template data you're saving but should serve the purpose to demonstrate the idea...basically you can build any format array you like really).

I use this same approach in a very complicated custom shipping calculator (crazily this is some 2000 lines of php!) - that deals with multiple services (including some table data ones and some live quote ones), plus grouping & boxing services & caching to make it all work as quickly as possible (live rates always being much slower of course...).  Because at times the checkout flow gets awkward relying on Commerce's template tags, I also have an actionShippingOptions controller that calls the same services and returns the quotes & data that way (also very handy for debugging purposes!) - but you may well not need that.  But even with the existing API you can do damn near anything you like between setting the rate and saving data to the order and showing it later, basically.


How can I return all user custom profile fields in an array? Hi I'm sure this is simple but it is eluding me. I want to pull out all the user profile fields with twig as an array that I can then check. When I dump the user or currentUser data it does not show any of the profile fields I have created in the returned object. I can however still access these fields with user.customfieldname. How can I return the array I am looking for?

Custom field data is accessible via currentUser.getContent().

Using dump:

{{ dump(currentUser.getContent()) }}

Or to output as string:

{{ currentUser.getContent()|join(', ') }}


You don’t have the proper credentials to access this page when trying to insert a guest entry i get the following response from craft:
You don’t have the proper credentials to access this page.

What can i do to avoid this and still insert the entry to craft? It needs to be an anonymous user.

Thanks in advance,

Be sure that you're testing as a logged-out user on the front-end of the site.  If you have an active Craft session, you could be getting a false positive error message.


Protect entry so that only a "relatedTo" user can see it I would like to protect the viewing of an _entry.html page so that only a user related to that entry can view it.

You could loop through the users and compare them.

{% set authorized = false %}
{% for user in entry.authorizedUsers %}
    {% if user == currentUser %}
        {% set authorized = true %}
    {% endif %}
{% endfor %}

{% if not authorized %}
    {% redirect 403 %}
{% endif %}


Change Save entry to Publish possible? I have a client that can't remember that save equals publish... is there a way to change the text from save to publish?

Use JavaScript to find the button and change the text. Fortunately, the Craft CP contains jQuery, so it should be fairly easy.

This plugin should help speed up the process... Your JavaScript will get loaded on every page of the control panel.

  https://github.com/lindseydiloreto/craft-cpjs


Pass Craft Field String to Javascript Function I tried different solutions given here, here and here.
But I get nothing to work, probably missing something small and simple.

I have this variable in a .js File:

var dziFilesUrl = 'http://localhost:3000/openseadragon/highres/{{ entry.kelimImageData }}/';
var dziData = '<?xml version="1.0" encoding="UTF-8"?><Image xmlns="http://schemas.microsoft.com/deepzoom/2008" Format="jpeg" Overlap="1" TileSize="254"><Size Height="5097" Width="3600"/></Image>';

where I want the entry.kelimImageData coming from the Backend.

In my .twig File i have this:

{% includeJsFile "/assets/js/openseadragon-custom-dzi.js" %}

{% set myJs %}
  tileSourceFromData('{{entry.kelimImageData}}');
{% endset %}

{% includeJs myJs %}`

My openseadragon-custom-dzi.js File looks like this (modified to make it not so long)

var dziFilesUrl = 'http://localhost:3000/openseadragon/highres/{{ entry.kelimImageData }}/';
var dziData = '<?xml version="1.0" encoding="UTF-8"?><Image xmlns="http://schemas.microsoft.com/deepzoom/2008" Format="jpeg" Overlap="1" TileSize="254"><Size Height="5097" Width="3600"/></Image>';

var tileSourceFromData = function(data, filesUrl) {
  var $xml = $($.parseXML(data));
  var $image = $xml.find('Image');
  var $size = $xml.find('Size');
  var dzi = {
    Image: {
      xmlns: $image.attr('xmlns'),
      Url: filesUrl,
      Format: $image.attr('Format'),
      Overlap: $image.attr('Overlap'),
      TileSize: $image.attr('TileSize'),
      Size: {
        Height: $size.attr('Height'),
        Width: $size.attr('Width')
      }
    }
   return dzi;
  };
}

var viewer = OpenSeadragon({
  ...
  tileSources: tileSourceFromData(dziData, dziFilesUrl)
});

Craft 3

According to the craft 3 js docs

We'll need to adjust some things as follows:

Twig

We no longer need to tell craft which File we're editing as it
now simply inserts a <script> tag with the contents at the end of the <body> tag. (Can be adjusted, see position docs.)

{% js %}
// define our vars
var dziFilesUrl = "http://localhost:3000/openseadragon/highres/{{ entry.kelimImageData }}/";
var dziData = "{{ entry.kelimImageData }}";
//Call our function
tileSourceFromData(dziData, dziFilesUrl);
{% endjs %}

JS

Remove the dziFilesUrl declaration from openseadragon-custom-dzi.js & and other files you declare this variable in.

And that about sums it up, if any error does occur, please inform me of whatever error your browser console spews out or otherwise any error craft presents.

Craft 2

I'm gonna guess that you're using craft cms v2 as I only found includeJS in the craft 2 docs.

Adjust your twig as follows:

{%includeJsFile "" %}

{% set myJs %}
  // here you're setting the vars which use the twig variables for them to be available later on in the JS
  var dziFilesUrl = "http://localhost:3000/openseadragon/highres/{{ entry.kelimImageData }}/";
  var dziData = "{{ entry.kelimImageData }}";

  // you could even still call your `tileSourceFromData` function here
  tileSourceFromData(dziData, dziFilesUrl);
{% endset %}

{% includeJs myJs %}`

And then remove the setting of those vars in your openseadragon-custom-dzi.js.

This should:

Set the vars with the backend data in the Footer (see docs)
Call the tileSources function in the footer as you did before.
Allow you to use the dziFilesUrl and the dziData variables anywhere else in your JS with the backend data as their values.

Beware: This JS is getting executed in the footer according to the docs so you need to either start your function call from within the twig myJs or when the full page is loaded, otherwise you'll run into undefined variables.

Sidenote: you were calling your tileSourceFromData function in the twig with only one arg, instead of the required two before, this is also adjusted in my answer.


Composer Issue When Doing exec From a Plugin Curious if anyone has ever run into this before or may know what's going on here. I'm using the execWidget plugin (https://github.com/marionnewlevant/craft-exec_widget) to run a script from Craft's dashboard that does a composer install when the lock file has changed.

Here is the configuration for the plugin:

<?php
return [
  '*' => [
  ],
  'local' => [
    'commands' => [
      'composer install' => '../scripts/ed-day_composer.sh',
      'pull assets from production' => '../scripts/pull_assets.sh',
    ]
  ],
  'staging' => [
    'commands' => [
      'composer install' => '../scripts/ed-day_composer.sh',
      'push everything production' => '../scripts/push_all.sh',
    ]
  ],
];

Here is the composer install script:

#!/bin/bash

# Get the directory of the currently executing script
DIR="$(dirname "${BASH_SOURCE[0]}")"

# Include files
INCLUDE_FILES=(
            "common/defaults.sh"
            ".env.sh"
            "common/common_env.sh"
            "common/common_db.sh"
            )
for INCLUDE_FILE in "${INCLUDE_FILES[@]}"
do
    if [ -f "${DIR}/${INCLUDE_FILE}" ]
    then
        source "${DIR}/${INCLUDE_FILE}"
    else
        echo "File ${DIR}/${INCLUDE_FILE} is missing, aborting."
        exit 1
    fi
done

(cd "${LOCAL_ROOT_PATH}/craft"; HOME=${LOCAL_HOME} composer install)

I get a returned 127 error from the queue.log and the scrip does not actually work:

2018-06-25 14:10:12 [50.200.203.190][1][-][info][craft\queue\QueueLogBehavior::beforeExec]  [45] ExecWidget Task (attempt: 1) - Started
2018-06-25 14:10:12 [50.200.203.190][1][-][info][marionnewlevant\execwidget\jobs\ExecWidgetTask::log] executing ../scripts/ed-day_composer.sh
2018-06-25 14:10:12 [50.200.203.190][1][-][info][marionnewlevant\execwidget\jobs\ExecWidgetTask::log] returned 127

Based on testing I've done, I'm almost certain the issue is with composer because I get a success error message when I remove just the composer part of the line.

Composer works when SSHing in. Any idea what might be causing the issue?

The problem seems to be tied to Composer not having the correct permissions. Will work with our IT team to update so Composer can write appropriately when initiated from Craft.


DRYer way to set column widths than with if statement? Craft n00b here. Really loving it so far, but am wondering if how I'm implementing this is the best option, or if maybe there's a more sophisticated solution.

Inside our content builder switch statement, I'd like to provide my client with the ability to build an image-and-text block wherein they can change the widths of the columns via a dropdown (columnWidth), which will then set the widths of the children. Here's what I have so far, but it seems a little inelegant, especially if I were to do more options or columns. Any pointers or is this the best way to handle this?

{% if content.columnWidth == "1-2" %}
  {% set columnOneWidth = "1-of-3" %}
  {% set columnTwoWidth = "2-of-3" %}
{% elseif content.columnWidth == "1-1" %}
  {% set columnOneWidth = "1-of-2" %}
  {% set columnTwoWidth = "1-of-2" %}
{% elseif content.columnWidth == "2-1" %}
  {% set columnOneWidth = "2-of-3" %}
  {% set columnTwoWidth = "1-of-3" %}
{% endif %}

    [...]

    [...]

Thanks!

One way to make it a bit more DRY and flexible is to use the split filter to create an array of column widths.

Also, you can save a bit of markup by calculating the total number of columns, and using a for loop to output the actual column DOM nodes:

{% set columns = content.columnWidth|default('1-2')|split('-') %}
{% set totalColumns = columns[0] + columns[1] %}

    {% for column in columns %}

           [...]

    {% endfor %}


Craft 3 CMS How to get all Channel Names/Titles I am trying to retrieve all Channel sections' names/titles.
This snippet contains the troublesome part.

{% set allSections = craft.entries.all() %}
    {% for section in allSections %}
        {% if section.getType() == "channel" %}
            {% set navBarLinks = navBarLinks|merge([section.title]) %}
        {% endif %}
    {% endfor %}

allSections contains all entries (I know I am looking for sections but I don't know how to loop all sections. craft.sections returns null, since it's deprecated from Craft 2). ex:

I can see the entry inside a channel 'webshop/overview'. But I want to get only the name of the channel ('webshop' in this case).
Also I have 2 other channels without any entries inside them.

navBarLinks currently returns null. I want it to contain the names/titles of the Channel sections regardless if they contain entries inside them.

P.S. I am doing all this so I can have a dynamic navigation bar(similar to breadcrumbs but not exactly). I know Structure will make my life a lot easier but it doesn't quite fit my needs.

This kind of solved my problem:

{% for section in craft.app.sections.allSections %}
    {% if section.type == 'channel' %}
        {% set navBarLinks = navBarLinks|merge([section]) %}
    {% endif %}
{% endfor %}


Craft Commerce Categories and Product Types I've just started using craft Commerce. What's the accepted way to categorise products? For example, i’m trying to replicate this structure:

Bags and Scarves (shows all products in this category)

Hand Bags (shows just hand bags)
Overnight Bags (shows just overnight)
Purses (shows just purses)
Scarfs (shows just scarfs)

Hair Accessories (shows all accessories)

Hair Bands (shows just hair bands)
Hair Clips (etc)
Brushes (etc)
Combs (etc)

Hair Care Products (shows all hair care ‘HERO’ products)

Product with variations
Product with variations
Product with variations
Product with variations
etc
etc

Jewellery  (all jewellery, no sub cats)
Gift Vouchers

£10
£20

I can’t see how product types would work?  I added the top level as if they were “departments” but the front end doesn’t show these?

Any help and code examples appreciated to get me going.

Thanks

Product Types are really for the content of the fields and field layout of each product plus basic options for things like tax/shipping category defaults.

For full categorization in menus and site structure, we recommend putting a category field on all products.

For example you might have a category field names 'department' which you put all products into.

You then could show all products related to that category: https://craftcms.com/support/entries-related-to-category

Let me know if you need more guidance in the comments.


How to list/filter by the entry type column in the entries listing I've got a 'media' channel that has two entry types: Film and Report.

I have set the listing of entries to have the entry type column, but it doesn't seem to have any options for making it clickable to sort by entry type. Is this a current feature?

Thanks

Currently it's not possible to sort by custom fields on element index pages.

Feel free to add a thumbs-up/comment on this feature request, though: https://github.com/craftcms/cms/issues/987


Reference tags inside Matrix textfield I have a {{ siteURL }} tag inside a matrix plain text field. But it does only render as a tag and isn't beeing converted to the actual url.

Accoring to this post one way is to use reference tags.

So I write inside the matrix plain text field: { globalset:siteUrl } and in the template I write:

{% for block in siteDropdown.matrixDropdown %}

     {{ block.cellLinkTitle }}

{% endfor %}

But this does nothing but print the tag itself. What am I missing?

You'll need to use the global set's element ID as the reference part (second segment) of the reference tag:

{globalset:51:siteUrl}

To find the global set's ID, go to CP -> Settings -> Globals. The URL to the global set will have the ID as the last segment (e.g. http://yoursite.craft.dev/admin/settings/globals/51)

Also, see the this SE thread for a little more info: How can I use a reference tag with a global set using the handle (not the ID)


How to fetch ALL users related to a field, including unverified/pending? My users have a relationship field called Organisation.

In my plugin, I am trying to fetch all users that are related to the organisation I pass it.

BUT I would like ALL users including Pending, so those who have not verified their email and set a password yet.

I am using this code but it only fetches the verified users, not pending.

$entries = craft\elements\User::find();

$relatedEntries = $entries->relatedTo(
    array(
        'targetElement' => $organisationId,
        'field'         => 'organisation'
    )
);

// Fetch all entries
$relatedEntries = $relatedEntries->all();

I have also tried using combinations of ->status(null), e.g.

$entries = craft\elements\User::find();

    $relatedEntries = $entries->relatedTo(
        array(
            'targetElement' => $organisationId,
            'field'         => 'organisation'
        )
    );

    // Fetch all entries
    $relatedEntries = $relatedEntries->status(null)->all();

or

$entries = craft\elements\User::find()->status(null);

    $relatedEntries = $entries->relatedTo(
        array(
            'targetElement' => $organisationId,
            'field'         => 'organisation'
        )
    );

    // Fetch all entries
    $relatedEntries = $relatedEntries->all();

But they always just show all verified users.

Thanks

Edit

I have changed my logic and database setup a bit, and so decided to try using just template tags now. And so I got it working OK using .status(null with template tags.

{% set organisations = craft.entries().section('suppliers').authorId(currentUser.id) %}

{% for organisation in organisations %}

    {% set users = craft.users().relatedTo(organisation).status(null %}
    {% for user in users %}
        {{user.firstName}} {{user.lastName}} - {{user.status}}<br/>
    {% endfor%}

{% endfor%}

The correct syntax or finding all users regardless of status is status(null), which you've tried, but the issue isn't your status parameter, it's the targetElement parameter of your relatedTo query.

It only accepts the actual element you're looking to query, not its ID.

If you already have the target element object, you can just pass that in. If you don't, you can retrieve it with:

$targetElement = Craft::$app->getElements()->getElementById($organizationId);

Then use:

array(
    'targetElement' => $targetElement,
    'field'         => 'organisation'
)

Relevant docs here: https://docs.craftcms.com/v3/relations.html#the-relatedto-parameter


AWS Serialisation error on front end Getting the following error on a site I'm developing:

  Instances of craft\awss3\S3Client cannot be serialized

Unsure if this is an issue with the Amazon S3 plugin or at my bucket's end. Any ideas much appreciated.

Turned out to be a minor bug in 3.3.0.1. Fixed now.


Is it possible to create composite Primary Keys in Records? I've structured my Record as such:

<?php
namespace Craft;

class MyPlugin_MerchantOrderRecord extends BaseRecord
{
  public function getTableName()
  {
    return 'myplugin_merchant_orders';
  }

  protected function defineAttributes()
  {
    return [
      'merchant_id' => AttributeType::Number,
      'order_id' => AttributeType::Number,
      ...
    ];
  }

  public function primaryKey()
  {
    return ['merchant_id', 'order_id'];
  }

  public function defineRelations()
  {
      return [
        'merchant' => [static::BELONGS_TO, 'MyPlugin_MerchantRecord', 'merchant_id',
               'required' => true, 'onDelete' => static::CASCADE],
        'order' => [static::BELONGS_TO, 'Commerce_OrderRecord', 'order_id',
               'required' => true, 'onDelete' => static::CASCADE],
      ];
  }
}

When I install the plugin I get the following error:

Illegal offset type in isset or empty

If I take away the primaryKey() definition, the table is created successfully and the foreign keys are in place as expected.

Any help gratefully received.

It would appear not unfortunately.

Craft's BaseRecord (which a Record should inherit from) tries to uses the primaryKey() method call to help create the table, but assumes a single column name is returned, not an Array and so falls over on the isset call.

$pk = $this->primaryKey();

if (isset($columns[$pk]))
{
  $columns[$pk]['primaryKey'] = true;
  $addIdColumn = false;
}
else
{
  $addIdColumn = true;
}

I'm guessing it should work because the CActiveRecord (which the BaseRecord inherits from) states in it's comments for PrimaryKey:

@return mixed the primary key of the associated database table.

If the key is a composite one consisting of several columns, it should
return the array of the key column names.

EDIT: In the end I added the following to my Plugin file, and that seemed to do the trick:

public function onAfterInstall()
{
  craft()->db->createCommand()->dropColumn('myplugin_merchant_order', 'id');
  craft()->db->createCommand()->addPrimaryKey('myplugin_merchant_order', ['merchant_id', 'order_id']);
}

I also had to add this to my Record, to stop the validation failing because I'd removed the id column in the above step:

public function primaryKey()
{
    return null;
}


Get related posts excluding current post in Craft 3 In Craft 3, I’m trying to get the five most recent other posts sharing a category with the current post.  I got this far:

{% for relatedPost in craft.entries.relatedTo(entry.newsCategory).limit(5) %}

That gets five posts sharing the current post’s category, including the current post if it happens to be recently posted.  How do I exclude the current post from the returned results, without just dropping it from the five so I’m only left with four?

To exclude the current post use the current post's id with a "not" clause in the id query param.

{% for relatedPost in craft.entries.relatedTo(entry.newsCategory).id('not ' ~ entry.id).limit(5).all() %}


How to set a meta-tag from a plugin I'm writing a Craft CMS plugin with a widget. This widget needs to make calls to the Github-API trough javascript (Selectize is used in the widget). Now, the problem is that it's not allowed, made clear by this message in the console:

  Refused to connect to 'https://api.github.com/search/repositories/?q=i' because it violates the following Content Security Policy directive: "default-src 'self' data:". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

I can fix this by setting the Content-Security-Policy meta tag in the head:

<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://api.github.com/">

I've attempted to add this with the following code in the template for my widget:

{% set meta %}
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' https://api.github.com/">
{% endset %}
{% includeHeadHtml(meta) %}

Is this correct or is there a better way?

It looks as if there is a Content-Security-Policy header that may also do what you want. You should be able to use the header tag to set this:

{% header "script-src: 'self' https://api.github.com/;" %}


Removing old Block Handles in Matrix Field? I'm working with Matrix field. When adding and then removing and then adding again fields with the same handle I get the following error.

Handle "videoUrl" has already been taken.

Is there a way of removing old handles that are not being used?

This is actually creating issues adding new fields with a different handle too. Even though a new handle is being used an old handle is being referenced as a blocker to adding a new field. I get the following error when trying to save a post.

Invalid field handle: embed

'Embed' isn't even listed in the field manager area as a field handle but the problem persists.

Tried it here and seems ok. Once you have deleted your field, are you saving your matrix block (top right button)?

If you delete the field then try to recreate it straight away (or any other field for that matter), it gives you the Handle "videoUrl" has already been taken. until you save the block. Once you save, you should be ok to recreate.

It does seem a bit buggy though as you need to save in between each action... Might need to see what the craft guys think.


Craft 3: with Base URL of site set up using @web alias, how to display the original URL in Twig templates? I use the @web alias in the configuration of the base URLs of the sites (following Craft 3 documentation). The @web alias allows a shortcut  to the URL of the folder that contains the index.php file loaded for the request.

However, when I loop through craft.app.sites.getAllSites using Twig, the value of baseUrl still prints @web in the URL, instead of transform it in its original URL.

As a workaround, I could get the original URL using {{ craft.app.sites.currentSite.baseUrl ~ site.uri | replace({ '@web': '' }) }}. But this seems a very dirty solution that will probably not always work.

So luckily for us, Brandon recently added the alias() Twig function. It should show up in Craft CMS 3 RC7. You can see the discussion about it here: https://github.com/craftcms/cms/issues/2327


Upgrading Craft 2 to 3 hits database error I'm working on upgrading a site from Craft2 to Craft3. I've run through the steps in the upgrade guide and when I go to /admin and press the button to run the Database upgrade I get the following error.

Integrity constraint violation: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'createfoldersinvolume:bb790fdf-3d6b-46b8-a023-b9f7ffe8aba5' for key 'craft_userpermissions_name_unq_idx'
The SQL being executed was: UPDATE craft_userpermissions SET name='createfoldersinvolume:bb790fdf-3d6b-46b8-a023-b9f7ffe8aba5', dateUpdated='2019-02-04 17:39:32' WHERE id=173

Migration: craft\migrations\m180518_173000_permissions_to_uid

I can restore the database so that's fine but not sure how best to proceed with the upgrade. Any help would be appreciated!

This sounds like a bug, you should probably report it directly to support@craftcms.com.

I'm posting this as an answer however, because you may be able to skirt the issue... Try updating to Craft 3.0.x first, and then updating the 3.1.x from there.


API access to Craft 3 Does anyone know if there is an API for CRAFT 3 where I can authenticate to and query (CRUD) all the elements (channels, etc.) I have created on it?

Thanks.

You may have a look at the headless documentation which offers a few solutions / ways such as a GraphQL API, REST-API & a JSON API


Paginate children in a structure Or to say it in a different way, paginate siblings.

I found this question on stack: How do you page through child entries in a structure? which seems to have a working answer. However trying this myself I get no results.

To be clear, I have a two level structure.

Level 1 (parent)
 - Level 2 (child)
 - Level 2 (child)
 - and so on..

How can I paginate the children?

Thank you for any input!

There are many ways to paginate:

To show next / prev links

#1
getPrevSibling getNextSibling

{% if entry.getPrevSibling %}
    {{entry.getPrevSibling.title}}
{% endif %}

{% if entry.getNextSibling %}
    {{entry.getNextSibling.title}}
{% endif %}

#2 getPrev getNext

{% set params = {section: entry.section.handle} %}
{% set prev = entry.getPrev(params) %}
{% set next = entry.getNext(params) %}

{% if prev %}
    {{prev.title}}
{% endif %}

{% if next %}
    {{next.title}}
{% endif %}

To show list of children (single type page)

{% paginate %}

{% paginate craft.entries.section('sectionHandle').limit(10) as pageEntries %}

{% for entry in pageEntries %}

{{ entry.title }}

        {{ entry.body }}

{% endfor %}

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


How do I grab the label of a LJ Dynamic dropdown fields I have a LJ Dynamic field set up with the following code that pulls in a list of stores to an event section.

{% for store in craft.entries.section('stores').order('title') %}
  { "value":"{{ store.storeId }}" , "label":"{{ store.title | raw}}"}
  {% if not loop.last %},{% endif %}
{% endfor %}

My problem is that I can't seem to get the label from the field to display on the front end as I keep getting the error message
"Impossible to access an attribute ("label") on a string variable ("412")" 412 is the value of the particular store I have selected for my event.

Where: {{ entry.stores.label }}

If I add just { entry.stores } it will output the value.

This is probably something very obvious but got me a bit stuck.

The LJ Dynamic fieldtype only stores the value as a string, or as an array of values for the multiselect fieldtypes. The label in the settings is there just for displaying in the admin.

Either you could use the store.title instead of store.storeId as the value, if you only need the store name in your template. Or you could use the store id to get the data from the store in your template. Something like:

{% set store = craft.entries.section('stores').storeId(entry.stores).first() %}
{{ store.title }}


How to deal with client input your site? Our client wants a poll on the website.
How can I deal with client input and update the entry?
I found the Entry-Form example, but this doesn't work.

The title field is the question and the admin can add answers with a matrix field. Within the matrix fields there are 2 fields: answer and totalVotes

This is my code:

<form class="poll__questions" method="post" action="" accept-charset="UTF-8">
    <input type="hidden" name="action" value="entries/saveEntry">
    <input type="hidden" name="redirect" value="{{ craft.request.getPath }}?success=√">
    <input type="hidden" name="sectionId" value="38">
    <input type="hidden" name="entryId" value="{{ pollentry.id }}">
    <input type="hidden" name="enabled" value="1">

    {{% for answer in pollentry.answers %}

<input type="radio" name="fields[answers][{{ answer.id }}][fields][totalVotes]" value="{{ answer.totalVotes + 1 }}"> {{ answer.answer }}

    {% endfor %}}

    <button type="submit" class="button button--third">Vote</button>
</form>

Does anybody know why this doesn't work. It submits the form without visible errors, but it doens't update the entry.

Not an answer, but one thing that might help you diagnose the problem, is to output all the return errors (without having to define it on each field as the example templates do).

{% macro errorList(errors) %}
    {% if errors %}

            {% for error in errors %}

{{ error }}

            {% endfor %}

    {% endif %}
{% endmacro %}

{% from _self import errorList %}

{% if account is defined %}
    {{ errorList(account.getAllErrors()) }}
{% endif %}

{% if entry is defined %}
    {{ errorList(entry.getAllErrors()) }}
{% endif %}

A User Model named 'account' is returned by default from a failed users/saveUser post action, and an Entry Model named 'entry' is returned by default from a failed entries/saveEntry post action.

I have also found the Inpector plug-in extremely helpful. You can then add {{ account | inspect }} or {{ entry | inspect }} within the if statements above to get more info.


How to insert widgets in Rich Text field I want to insert widgets in arbitrary positions in one of my rich text fields. A good example would be a contact form inserted in the middle of the content. These widgets would be ad units, forms and similar dynamically rendered templates.

I know about the widget plugin for Redactor, but this is only suitable for static HTML. I need something a bit closer to an {% include %} tag.

How would you approach this problem?

I considered using WordPress-like [insert-widget-here] tags, or perhaps iFrames, but I'd prefer a more elegant solution.

Have you considered using the Matrix field type? You could create a block for rich text, then a block for a contact form (or whatever widget).

You could probably achieve what you're asking with a plugin but in my experience doing things like this in Redacter are prone to weirdness/bugginess (especially when in the hands of a client).


Push/Add asset to Entry by asset filename I have spreadsheet that i'm parsing and I have available to me a Entry ID and the Asset filename. Can I add the asset to the entry by the filename?

Im targeting the product entry with:

$product = Entry::find()->section('products')->id($data[3]);
$productImages = ($product->productImages) ? $product->productImages : array();
$name = explode('?', basename($data[2]));
$pushImageId = Asset::find()->filename($name[0])->id; // $name[0] = myfilename.jpg
$productImages = array_push($productImages,$pushImageId);
$product->productImages($productImages);
Craft::$app->elements->saveElement($product);

The handle for the asset field is:
productImages

So when I run this i get this error:

Exception: Argument 1 passed to craft\services\Elements::saveElement() must implement interface craft\base\ElementInterface, instance of craft\elements\db\EntryQuery given

Thanks!

Solution

Based on the answer from robin in this question How to save Asset field of an Entry programmatically in Craft 3

It you just need to attach the asset ID's as an array. So i re-wrote the following code to create a new array of the original asset ids plus the new one. And it worked!

$product = Entry::find()->section('products')->id($data[3])->one();
$name = explode('?', basename($data[2]));
$pushImageIds = Asset::find()->filename($name[0])->ids(); // $name[0] = myfilename.jpg
$newProductImagesArray = array();
foreach($product->productImages as $image){
    array_push($newProductImagesArray,$image->id);
}
array_push($newProductImagesArray,$pushImageIds[0]);

$product->setFieldValue('productImages',$newProductImagesArray);
Craft::$app->getElements()->saveElement($product);

You need to fetch the asset and set it as a field value to your entry

$pushImageId = Asset::find()->filename($name[0])->ids();

id will display the id property of your query. You won't fetch any results with it ids() will return an array of asset ids

$product->setFieldValue ('productImages', $productImages);

Will set the relation and finally you can store the element

Edit: and you need to fetch the product as well. Currently you just create the query for it


Plugin I created is asking for a license key I created a plugin for a client website and I keep getting license issues popping up in the admin panel for it. Not sure what I am doing wrong here. Obviously I have no intention of putting it into the store, I just want to remove this license key issue.

Craft checks only the handle of your plugin to detect license mismatches. So when your plugin has the handle importer and another plugin in the store has the same handle it will mark it. (since importer is already a plugin in the store it's likely the issue)

The only ways to solve it

change your handle to data-importer or whatever might be unique
make a module out of it
(include CSS to remove the message and ignore it)


I get error on live site after Preparse-field update After I've turned devMode on live site to see what's going on a fatal error appeared: Using $this when not in object context in /var/www/vhosts/site.com/craft/plugins/preparsefield/PreparseFieldPlugin.php on line 112

The weird thing is, that with same settings on local machine it runs as expected.

This was due to version 0.3.0 breaking PHP 5.3 support. A fix has been implemented in 0.3.1 that fixes this.


What is the advantages/disadvantages of separating macro imports? In my base template extended by all other templates, I import my macros, so I have global use of them throughout my templates.

Question 1:
Am I taking a performance hit for including these whether or not I'm using them in a given template?

Question 2:
Is there any advantage to splitting up macros into different imports, other than just organization?

In other words, is there any reason not to do:

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

And have _includes/macros have all my macros?

This is definitely not a great answer, but I'm not sure anyone's going to be able to really answer this. You'd have to set up some sort of unit tests or something. And it will probably depend a lot on your server environment. But think of all the class files that need to get loaded just to handle a simple request. Personally, I think that the performance hit on an import statement is going to be pretty tiny regardless. I would opt for importing them in whatever way makes the most sense from a development/organizational perspective.


Site is inaccessible after updating plugin with new attribute/db column I'm making some modifications to a custom plugin. I added a new attribute to one of the models and created a migration to add a new database column to hold that attribute. I updated the version number in my main plugin file and everything ran smoothly in my local environment. However in production I get "an unknown error occured" with no actual PHP errors or Craft errors being logged. This is after Craft tries to perform it's database updates (and tells me it went successfully).

Running Craft 2.6.2982

Adding getSchemaVersion() to my main plugin file and returning a schema version did the trick.

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


How can I get an entry's child category, and not the parent category? I have a category page that lists all of the entries related to that category. And then I want to display the selected child category for the entry... in brief like so:

Entry Title 1
Entry 1's Child-Category

Entry Title 2
Entry 2's Child-Category

Entry Title 3
Entry 3's Child-Category

My category field handle is chooseLocation

Here is what I have in my template:
(snippet taken from here)

{% for entry in entries %}
    {% if entry.chooseLocation | length %}
    {% for category in entry.chooseLocation %}
         {{ category }}
            {% if not loop.last %}, {% endif %}
        {% endfor %}
    {% endif %}
{% endfor %}

The problem is, this get's the parent category (which is the category page we're on) but not the child category. I need to show just the child category and not the parent.

I've looked through the docs but I can't find anything on how to do this.

Your Categories field (entry.chooseLocation) is an Element Criteria Model. It's exactly the same as if you'd done something like this...

craft.categories.group('myGroup')

The Element Criteria Model (ECM) is an important concept in Craft, because it's the driver for most content handling. Once you've got a bearing on what the ECM is, you can use it fairly consistently.

For example, just like craft.entries, you can apply .find, .first, and other methods to retrieve a specific subset of elements. And depending on the element type, you can chain other filtering methods in between...

craft.categories.group('myGroup').limit(3).find()

Returning to your question, entry.chooseLocation will be an ECM for Categories. So it's just a matter of filtering it appropriately.

{% for category in entry.chooseLocation.level(2).find() %}

Here is the full list of filtering options you can use on a Categories ECM.


Single template for multiple locales How can I avoid local specific templates? I'd like just to use one template for all languages.

My current template structure is extending layouts/_base, and layout/_site [similar to happylager demo] and I have a template called 'help' but i dont want to set 'fr/help', 'es/help'

The best solution is to use sections / entries for ALL of your templates. This not only allows to automatically route each local's page URL to a single template, it also makes it possible to set local specific slugs for each entry. You might not care for translated URLs, but Google does.

So what I recommend is to restructure your setup a bit, add a bunch of singles or even better, a structure named "pages", and set the path to your templates in the section settings.


Offset setting a limit? If have this in a template:
{% for mainLink in section.links.limit(4).all() %}
{% endfor %}

{% for secondaryLink in section.links.offset(4).all() %}
{% endfor %}

Which I thought would show the first four in the first list and however many from 5 onwards in the secondary list. But what's happening is that the second list is limiting the output to 4 as well. Why is that?

When you're setting section.links.limit(4), Craft will persist that limit in subsequent calls.
This was a change introduced in Craft 3, which you can read about Changes in Craft 3 | Cloning Element Queries

You have two options to resolve your issue:
Use clone() to create a new version of the ElementCriteriaModel for each time you use section.links
{% for mainLink in clone(section.links).limit(4).all() %}
{% endfor %}

{% for secondaryLink in clone(section.links).offset(4).all() %}
{% endfor %}

Or unset the limit your limit in the second for loop
{% for mainLink in section.links.limit(4).all() %}
{% endfor %}

{% for secondaryLink in section.links.limit(null).offset(4).all() %}
{% endfor %}


onSaveUser event - cannot redirect or access errors I've written a plugin which intercepts the user.onSaveUser event.

However if the save fails the user is still redirected to the page specified in the 'redirect' parameter/field of the form. How do I redirect them back to the form with errors and then access those errors?

I've tried craft()->urlManager->setRouteVariables() but still being returned to the page/URL specified in the original form. If I remove the redirect field from the front-end form I am redirected to the form on error but cannot access the errors sent in craft()->urlManager->setRouteVariables().

Any thoughts on how to specify the redirect destination depending on success/fail and also how to send/access the offending error messages in my original template?

As your code is running during an event, returning the request to the page has to be done a bit more explicitly than when you are performing similar behavior in a controller.

While I'm probably not the best person to explain this in detail, the code below gives one example I was provided in how you can set your route variables and end the request:

if ($errors)
{
    craft()->urlManager->setRouteVariables(array(
        'errors' => $errors
    ));

    $route = craft()->urlManager->parseUrl(craft()->request);
    craft()->runController($route);
    craft()->end();
}


`entry` variable pre-populated on a frontend form I set up a single page "Add Post" in the CP.

In following the example here https://craftcms.com/docs/templating/entry-form my form title input field is pre-populated with "Add Post" This is obviously not the desired result. When I include an entry id for modifying the post it grabs the title from the post as it should.

How do I create a new post without having a form pre-populated with "Add Post" in the title input field?

{% macro errorList(errors) %}
    {% if errors %}

            {% for error in errors %}

{{ error }}

            {% endfor %}

    {% endif %}
{% endmacro %}

{% from _self import errorList %}

Title
<input id="title" type="text" name="title"
    {%- if entry is defined %} value="{{ entry.title }}"{% endif -%}>

This example code makes use of the entry variable set by Craft to retrieve previously posted data. The variable is set if you encounter validation errors or if you want to modify upon an already saved entry (→ include the entryId in a hidden input field).

The problem is, that you already have an entry variable available to your template because every entry page comes pre-populated with one (if the URI is matched).

Easiest fix would probably be to make this single entry not match the URI set in the section's CP settings. Another idea would be to only fix that title problem and modify the example code:

{% if entry is defined %} value="{{ entry.title }}"{% endif %}

and use this instead:

{% if entry.errors is defined %} value="{{ entry.title }}"{% endif %}


Allow user to mark entry as favorite, starred, or saved for later We're building a site with a recipes catalog and we'd like to allow logged in users to "star" an entry/recipe and then be able to view a list of their "starred" or "favorited" entries. Any suggestions on how to go about this?

This definitely sounds like plugin territory. I'd recommend Curate, by Lori Goldberg.

* Disclaimer: I haven't personally tried it.


Prevent users for assigning permissions I want to control what users are able to do. For example, prevent access to the CP when the system is off.

How can I prevent users for assigning extra permissions but still allow them to assign users to User Groups?

At present, it would appear that anyone able to assign User Groups is also able to assign whatever permissions they want.

I’ve built a plugin to allow control over what user groups can allocate permissions.

https://github.com/joshuabaker/Sanction


MAMP: Craft can’t connect to the database with the credentials in craft/config/db.php I'm using MAMP to setup a local environment for CraftCMS on Windows 10.

I have followed all necessary procedures and have my local host and my database setup but I'm still receiving the error mentioned above.

My db.php is shown as this:

 return array(

        // The database server name or IP address. Usually this is 'localhost' or '127.0.0.1'.
       'server' => 'localhost',

        // The name of the database to select.
        'database' => 'mycraft',

       // The database username to connect with.
       'user' => 'root',

       // The database password to connect with.
      'password' => 'root',

       // The prefix to use when naming tables. This can be no more than 5 characters.
      'tablePrefix' => 'craft',

 );

Even if i make a custom user or password I still get the error above when I try to connect to http://localhost:8888/admin

I've checked many times and all my credentials as well as my local host port is correct. Craft can clearly connect to host which is why I'm getting the error from craft but for some reason, despite the details in db.php being correct it cant connect to the database...

Anyone know what's going on?

The default MAMP DB credentials are indeed root:root, as you have already, so you should check that you did indeed create a database (set up as detailed here) and that it is indeed called ‘mycraft’.

Also check in your MAMP control panel that the MySQL is actually running:


Assign a number 1-N for each entry that is returned I need to assign a # 1-N for each entry that is returned -- i.e., 1, 2, 3, 4, ....   (This is for a sort plugin.)

How do I template for that?

If you're in the context of a Twig loop, you have access to the loop variable, which has a number of “magic” properties set.

This allows you to do something like:

{% set posts = craft.entries({ section: 'posts' }).all %}
{% for post in posts %}
  Post #{{ loop.index }} is named: {{ post.title }}<br>
{% endfor %}


Can I access the code with my admin I am an in-house graphic designer with virtually no experience with building websites.
My company has a website built with Craft for which we have an admin to change some content like images and tables.
Some pages are not customizable at all. It seems they might have been constructed only with CSS and HTML. I don't know.
Now, we need to change these pages and build new ones using the same structure.
The agency that built the website seems to be ghosting on us so we have to find a solution to make the changes ourselves.

How do I access the code with my admin?
Is is possible that the guy who built the site restricted our access to the code?

The template and CSS code cannot be accessed from the admin. You'll need to gain access to the git repository that (hopefully) exists with your site's code.

Or in a less-preferable scenario, you could access the server via FTP and modify the templates and CSS there.

You might want to take a read through the Craft CMS docs as a starting point to get a little better idea of how the files are structured.


Update issues on local laravel valet When I update any plugins ( and I mean any not specific) the vendor package vlucas (Php DotEnv) is removed and the site subsequently breaks complaining it cannot find the DOT env file. Manual update via composer update doesn't work/resolve

It seems the only remedy for this is to manually add from production site the vlucas package and the vendor/composer package

Is there something broken in these packages?

Can you update your composer.json to require "vlucas/phpdotenv": "^3.4.0" and run composer update again.


Redactor video plugin not working I've tried to integrate the video.js plugin for Redactor. It allows me to insert a YouTube embed (as expected) but the iframe code is stripped out on save. Using the latest Craft 2.5 build. Anyone experienced the same? Solutions?

You've probably got "Purify HTML" checked under your Rich Text field's settings and that's stripping iframe code.

You should really only have that checked if you're accepting input from untrusted sources using the Rich Text field on the front-end of your site.  Presumably all users using the control panel are trusted enough not to input malicious code into the site.


Craft ordering tags by 'name' I've got a problem with some tasks. Craft tells me 'Deleting Stale Template Caches Failed' multiple times.

When I look in the Deprecated Warnings, it tells me two things:

Ordering tags by ‘name’ has been deprecated. Order by ‘title’ instead.
The Tag ‘name’ property has been deprecated. Use ‘title’ instead.

I don't know if this will fix my task errors, but it would be nice to solve this problem as well.

This warning is strange to me because I don't ordering my tags by name, I order them by title:

        <select class="multiselect multiselect-activitytype" multiple="multiple" data-search-filter="activityType" name="activityType">
            {% for tag in tags.order('title') %}
                {% set selected = (filters['activityType'] is defined and tag.title in filters['activityType']) ? 'selected': ''  %}
                <option value="{{ tag.title }}" {{ selected }}>{{ tag.title }}</option>
            {% endfor %}
        </select>

        <select class="multiselect multiselect-place" multiple="multiple" data-search-filter="activityLocation" name="activityLocation">
            {% for location in locations.order('title') %}
                {% set selected = (filters['activityLocation'] is defined and location.title in filters['activityLocation']) ? 'selected': '' %}
                <option value="{{ location.title }}" {{ selected }}>{{ location.title }}</option>
            {% endfor %}
        </select>

Any ideas?

First problem sounds like it might be a permissions issue. Have you made sure you've got the right permissions on the /craft/storage folder? More details can be found here in the docs:

Step 2: Set the permissions

Possibly related to the above, did you originally use name and then updated it to title at a later time? One possible reason for your deprecated messages might be the template is cached with the old name value. Sorting out the above should at least enable you to clear the cache, using the Clear caches button in the Settings.


How to build a transactional email platform with Craft I am building a transactional email platform for my users.
Users will subscribe to a digital program and receive daily emails depending on their profile and parameters.

This email sending will be:
 - time based
 - and user subscription based
 (and not event based)

So I found these plugins:

Sprout Email > for marketing campaigns and email notifications triggered by events
Postmaster for Craft CMS > looks more promising for my need but the plugin seems not maintained anymore (last commit was two years ago)

So do you know another recent Craft plugin to do transactional emails?
Or would you recommend another approach/idea?

This answer will focus on using Sprout Email for transactional emails.

Transactional Emails

First, a comment on semantics. I feel the question above is drawing a distinction between two types of transactional emails: triggered and automated.

Triggered emails are a one-off, context-specific email that get sent to a user when an event is triggered by a user's interaction with the application. Automated emails, which seems to be the focus of the question, are transactional in the sense that they are a one-to-one email to a user, however the email is not triggered by a specific event, but by an automated process. A user takes an action to be placed in a specific category, and transactional emails are sent to all users in that category on a specified schedule.

Triggered emails are fairly easy to build a UI around in Craft. Automated emails are harder, as you probably have to rely on either tasks (which can't be reliably scheduled at a specific time) or cron-jobs (which require extra steps by web developers to be setup).

Sprout Email and triggered emails

While the default Sprout Email Notifications are mostly User and Entry events, Sprout Email is not at all limited to User and Entry events.

Sprout Email has a flexible Custom Event API where you can create custom triggers for the events you need to support with as many settings as you wish to control them with. An example can be seen in Sprout Forms, which adds a trigger for When a form entry is submitted.

Sprout Email allows you to manage the Triggered Email content right in Craft alongside your other content and all the admin needs to do is select the event which should trigger them being sent.

Sprout Email and automated emails

You'll have to create a custom script and setup a cron to ping your script to send out emails on the schedule you require, but Sprout Email can support (and is in use for) advanced transactional campaigns that allow content editors to easily manage email content in a Craft-friendly way, and programmatically send out transactional email on a regular schedule.

You can programmatically trigger a Notification Email using the Sprout Email service layer. Notification Emails are sent via the SproutEmail_DefaultMailerService using the sendNotification() method:

craft()->sproutEmail_defaultMailer->sendNotification($campaign, $object);

$campaign needs to be a SproutEmail_CampaignModel
$object is any object that you want to make available to your email templates.

At some point we'll probably add additional support in Sprout Email around dynamic list management and automated emails. Right now, it's quite possible, it just requires a custom integration that fits your needs.


Permission for "Send an activation email now?" Option So it looks like site admins have the ability to choose whether or not to send a new user an Activation Email through the "Send an activation email now?" checkbox option on the Register a new user page on the Craft back-end.

I want to give this permission to a set of "Managers" user/permission group, but I cannot find the option that allows this permission to be usable.

The "Manager" user group currently has all "User" permissions checked.  Is the only way to fix this to make them an admin?

Was hoping to be a bit more granular. Any insights would be appreciated, thank you in advance.

This feature was added in Craft 3.3.5, see the changelog.

If you update to this version, your users should be able to control whether the activation email is sent.


How to return errors from a model back to the template? I'm working on validation with Craft. I have a custom plugin that allows public submission. However, I want to make sure I can pass errors back to the template. The key is trying to make my code as "dynamic" as possible. Meaning I do not want to specify the errors one by one.

Here is my sample controller (keep in mind I don't care what the model says is required, trying to keep the code agnostic of the model.)

public function actionSave() {

    // require a post request
    $this->requirePostRequest();

    // assign specific fields we are listening for
    $post = craft()->request->getPost('fields');

    // create a new Request model
    $model = MyModel::populateModel($post);

    // validate the model attributes
    if ($model->validate()) {
        // save the record

        // redirect to posted url
    }

    $errors = $model->getAllErrors();

    craft()->urlManager->setRouteVariables(array(
        'errors'    => $errors
    ));

}

Using $model->getAllErrors() returns an array as listed below:

array
(
    0 => 'Name cannot be blank.'
    1 => 'Details cannot be blank.'
    2 => 'Email is not a valid email address.'
)

How would I return that array to specific fields in my template like so:

{% if errors.email is defined ? errors.email | length %}
    <div data-alert class="alert">
        {{ errors.email }}
    </div>
{% endif %}

If you use $model->getErrors() instead, it should return a multidimensional array of errors indexed by attribute name like so:

array(
    'email' => array(
        'First email error.',
        'Second email error.',
    ),
    'firstName' => array(
        'First firstName error.',
    ),
);

Or you can get errors specifically for an attribute like so:

$model->getErrors('email');

Then update the template to check for the errors, since it is now a multidimensional array that's returned.

{% if errors.email is defined ? errors.email | length %}
<div data-alert class="alert">
    {% for error in errors.email %}
        {{ error }}
    {% endfor %}
</div>
{% endif %}


Add user to entry user field [RSVP to Event] I'm trying to add an RSVP button for users to register their attendance at my events. Once they click the button, I would like them to be added to a field called eventAttendees on the event entry.

My guess is this could be done as a form submission, and this is as far as I've gotten before getting a bit lost. I'm not sure how to pass the user ID to the entry.

<form method="post" accept-charset="UTF-8">
  {{ csrfInput() }}
  {{ hiddenInput('entryId', event.id) }}
  {{ hiddenInput('userId', currentUser.id) }}
  <input type="hidden" name="fields[eventAttendees][]">
  <button type="submit" class="btn btn-primary">RSVP</button>
</form>

Would appreciate any help!

You could do this:

<form method="post" accept-charset="UTF-8">
    {{ csrfInput() }}
    {{ actionInput('entries/save-entry') }}
    {{ hiddenInput('entryId', event.id) }}
    {% for user in event.eventAttendees %}
        <input type="hidden" name="fields[eventAttendees][]" value="{{ user.id }}">
    {% endfor %}
    <input type="hidden" name="fields[eventAttendees][]" value="{{ currentUser.id }}">
    <button type="submit" class="btn btn-primary">RSVP</button>
</form>

The for loop in there is to ensure that you do not remove any value (users) that have already RSVP. This is similar to Uploading Files from Front-End Entry Forms


How does "hash" work in the context of the Contact Form? I'm running into some trouble using the "hash" filter in Craft's first-party Contact Form plugin. The docs suggest using it like so:

<input type="hidden" name="redirect" value="{{ 'contact/thanks?from={fromName}'|hash }}">

On my development site, this is redirecting an otherwise-successful form to domain.com/hash_valuecontact/thanks?from=name, and therefore a 404 page.

Changing the value to '/contact/thanks?from{fromName}'|hash results in a more proper URL (domain.com/hash_value/contact/thanks?from=name) but still a 404. The docs suggest the hash is prepended to the value so the URL is expected but I'm not sure how the hash is decoded. And since the name is sent via the query string, I'm not sure what the hash is really doing in the first place, or why it's important.

So my question—is the hash actually important in this context, and if so how is it implemented? The docs don't contribute much information there. Thanks for any help.

In the context of the Contact Form plugin, the |hash filter is only necessary if you want to dynamically add recipients to the submission.

It is there to ensure that data isn't tampered with between the time it is submitted and the time that it hits Craft.  Contact Form cares about this so malicious people/scripts don't turn your Craft install into an email SPAM relay machine.

Looks like you're using it on the hidden redirect parameter... it's completely unnecessary there, so you can remove it.


Redirects are Overriding each other I am trying to redirect a bunch of uploads from our old website that no longer exist. I have one specific section I wanted to route to a resources page, but the rest will simply redirect to the home page.

/uploads*: redirects to homepage

/uploads/resources*: redirects to resources

The first redirect is overruling the second, and everything in /uploads is redirecting to the home page. Is there a way to make a special exception for this one folder? Through using a regex or something else?

Try swapping the order of your Routes (drag and drop), so that specific resources are routed first and the homepage fallback is positioned second.

Routes are applied in the order the appear in Settings > Routes i.e. If a route criteria is matched then Craft doesn't seem to try the rest.

More info here: https://craftcms.com/docs/routing just in case you haven't seen it already.

Let us know if that helps.


Complex search (and/or) I want to search for Entries of the same channel/section based on their type. Let's say, we are a travel company with two types of tours: "guided group" and "private". For group tours, we want trips associated with a particular provider. For private tours, we want trips less than a certain price (this field is N/A for group journeys).

In the controller:

$criteriaFilters = [
    'section'=>'journeys',
    'relatedTo'=> [<ids of the provider>],// Should be for group only
    'price'=>'<10', // Should be for private tour only
];
$criteria = craft()->elements->getCriteria(
    ElementType::Entry,
    $criteriaFilters
);

I need to retrieve them all in an ElementCriteriaModel object, via one SQL query. I don't want to get all private and guided tours separately and then combine them because that doesn't work well with pagination.

What do you think is best to achieve this? (in PHP, not Twig)

I don't think it's possible to do what you're trying to do in a single ElementCriteriaModel query.

These questions are basically asking the same thing and can confirm it.


Local storage in Twig Quick question: Is it possible to access the browser's local storage in Twig?

In JavaScript you would do something like:

localStorage.getItem('myItem');

Thanks!

No. Twig gets compiled into PHP and is run entirely on the server.


Programmatically add entries to an Entry field (craft\fields\Entries) I work with Craft 3.

I have an Entries field in a global set, I am able to get the global set, and then "read" this field programmatically (in a plugin) to fetch the entries that where added to it by looping on the EntryQuery data.

I want to be able to add another entry to this field via a php function in my plugin (so not in a Twig template), and then save the it, in the global set. The documentation is not very concrete on this part.
Thanks

here is my code :

            $reglages = \craft\elements\GlobalSet::find()
            ->handle('reglages')
            ->one();

            $entries = [$entry->id];

            foreach($reglages->curatedHomepageArticles as $tmpEntry)  {
                $entries[] = $tmpEntry->id;
            }
            $entries = array_unique($entries);
            $entries = array_slice($entries, 0, 10);

            $reglages->curatedHomepageArticles = $entries;

            $return = Craft::$app->getGlobals()->saveSet($reglages);
            var_dump($return);

The point is to add the $entry to the entry field called curatedHomepageArticles with no duplicates. I am able to read the entries that are already in the entry field, and then build the array of ids called $entries but nothing happens when I try to update the field with a new list of entries id $entries. $return is true and there are no exceptions thrown. Can you help me understand what is going on here ?

If you are running on Craft 3, as of 3.4.0 (and really since the release of craft 3), setting custom field values directly like

$entry->customField = [id1, id2, id3];

is broken. You should instead use the setFieldValue or setFieldValues methods.

in your case, a relationship field can be set like so:

$reglages->setFieldValue(curatedHomepageArticles, [<ARRAY OF ENTRY IDs]);


How can I create a zip file containing all assets from an entry gallery? I'm trying to create a download button which will allow the user to quickly download all assets in an entries image gallery.

I've started by adding each image url into an array stored in a data attr of the button.

<button data-zip-download='[{% for imageUrl in entry.collectionImages %}"{{imageUrl.url}}"{% if not loop.last %},{% endif %}{% endfor %}]'>Download Gallery</button>

I've then tried to get these URL's using jQuery, and zipping/downloading the zip with JSZip.js and FileSaver.js. Although through a bit of research I think I've found this is generally achieved through PHP.

I'm wondering if anyone on here has had a similar situation and has an idea of how I can achieve this.

I'm not too experienced with ajax but have also read that ajax is necessary to get this working via jQuery.

So far my JavaScript looks as so:

$('[data-zip-download]').each(function(){
var $this = $(this),
    urls = $this.data('zip-download'),
    files = [],
    zip = new JSZip();

for (var i = 0; i < urls.length; i++) {
    files.push(urls[i]);
};

zip.file(files);

$this.on('click', function(){
    zip.generateAsync({type:"blob"}).then(function (blob) { // 1) generate the zip file
        saveAs(blob, "gallery.zip"); // 2) trigger the download
    }, function (err) {
        $this.text(err);
    });
});
});

This however only creates a corrupted zip file as I imagine I need to actually load those images via ajax.

I suppose it would be ideal to simply use a craft/twig function if such a thing exists, although anything that works would be great haha.

Sorry for not having much knowledge on this specific need, really hoping someone out there can help!

Thanks!

I would recommend that you do this in php/plugin vs javascript. If you aren't familiar with writing plugins, you can start with the BusinessLogic Plugin — which is a great little plugin template, all set up with example controller, variables, and service file with example actions, methods, etc. Just drop in into your plugins folder, and activate, and you should be good to go.

There are a couple ways to set this up.

Use a form, plugin action, and pass urls or a gallery reference ID in a hidden form input field.

set up an HTML form (w/ your download/submit button)

add your custom action to your form (i.e. <input type="hidden" name="action" value="businessLogic/downloadZip">
add a hidden form input, and encode urls as a comma-separated list, or encode a gallery reference ID (i.e. <input type="hidden" name="galleryid" value="{{ entry.id }}">)

add a form action handler to your plugins controller file

lookup the urls (if not already provided)
generate the zip file and initiate download

Create a custom route, encoded with gallery ID reference

add a route to your config/routes.php file
set up your route action in your plugin's controller file with the gallery ID encoded as part of the route (i.e. 'downloads/gallery/13')
lookup urls using the gallery reference ID, generate zip, and initiate download

The advantage of using a custom route is that you can then just link to it from anywhere, or email the link to your client, without having to use a form. So, I would probably recommend using a route, unless you need to protect access for some reason.

To set up the plugin form action or route handler — have a look at the examples in the BusinessLogic plugin, which provides examples/instructions for both.

To grab the the URLs in your plugin method — you can use crafts built-in methods, as described in the craft docs.

To create the zip — doing a quick google search should come back with some good examples (like this one that has a sample HTML form and the php zip code).

Hopefully that will get you started — let me know if you get stuck, and I might be able to help further.


How to upload asset element on its own, not into an asset field (frontend form) I have read this resource about uploading a file to an asset field on an entry, but I believe I need to create the asset on its own because the asset has 2 custom fields associated with it that I want to define upon its creation. Is there not something like an 'assets/save-asset' action I can use to save an asset element without having to attach it to an entry?
Update: With Marion's help I'm a little closer. Side note: apparently 'assets/save-asset' is now depreciated in favor of 'assets/upload' so I'm using that. Here is my form:
              <form method='post' accept-charset='UTF-8' enctype='multipart/form-data'>
                {{ csrfInput() }}
                {{ actionInput('assets/upload') }}
                {{ hiddenInput('folderId', 7) }}
                {{ hiddenInput('title', 'cool image')}}
                <input name='file' type='file'>
                <input type='submit' value='submit'>
              </form>

But I am now getting a 400 Bad Request error with the message No file was uploaded. What am I doing wrong to not have my file attach correctly?

There is indeed an assets/save-asset controller. You will find it in vendor/craftcms/cms/src/controllers/AssetsController.php, the actionSaveAsset() function.


How to exclude multiple email addresses from an order query When doing an order query I want to omit results for 2 particular email addresses.

It's easy enough to omit results for one address;

$order = Order::find()
    ->isCompleted(true)
    ->email('not email1@somedomain.com')
    ->all();

But how do I omit more than one email? If I pass an array using ['not', 'email1@somdomain.com', 'email2@otherdomain.com'] then an error is returned stating that email expects a string.
For example, this doesn't work

$order = Order::find()
        ->isCompleted(true)
        ->email(['not', 'email1@somedomain.com', 'email2@otherdomain.com'])
        ->all();

Nor does passing in an array of addresses within the array

$order = Order::find()
        ->isCompleted(true)
        ->email(['not', ['email1@somedomain.com', 'email2@otherdomain.com']])
        ->all();

If I pass in 2 email criteria then only the latter is applied.

There has to be a neat way to do this.

Agree it'd be nice if it worked that way... I'm not sure why it doesn't, but replacing it with a regular where clause and not in works ok:

$order = Order::find()
    ->isCompleted(true)
    ->where(['not in', 'email', ['nobody@example.com','no@example.com']])
    ->all();


Create an object of objects with merge I'm trying to create an object of objects, can anyone suggest why object would only contain the last iteration of "itemObject"?

{% set object = {} %}

{% for entry in craft.entries.section("mainSectionsStructure") %}
    {% set itemObject = {
            "title": entry.title,
            "link": entry.url
        } %}

    {% set object = object|merge(itemObject) %}

{% endfor %}

{{ object|json_encode() }}

You're overriding the "title" and "link" properties on every iteration. Wrap the itemObject object in [] to merge the whole object into your array.

{% set object = object|merge([itemObject]) %}