Showing entries within a category I have a list of people with skills defined by a category group.

I have a category field within the 'people' Section so when a new person (entry) is created, the category field allows the admin to assign appropriate skills to that new profile.

I have a series of category entries for the 'skills' available.

When a user visits an "Our Skills" entry page, I need to display the title (name) of the people who have had that particular skill assigned to their profile via the category field.

I think this is reverse relations but I can't get my head around it?

Would appreciate some help.

Many thanks

Martin

Yes, you're right, one could call this a reverse-relation. The field you created the relation from is the "people" entry and you're looking at it from the category element's page.

To describe this direction in Craft relatedTo parameter, you define whether the element you're dealing with is the sourceElement of that relation, which means it is the element with the field, or if it is a targetElement, one of the elements selected in the field.

So on your "Our Skills" page you have a category element that got selected from a categories field, this makes it a targetElement. I'm also setting that categories field's handle here.

{% set individuals = craft.entries({
    section: 'people',
    relatedTo: {
        targetElement: category,
        field: 'mySkillsFieldHandle'
    }
}) %}

In most cases you could just use the element property, because there are no other relations between both elements. But I actually like to always define my relations precisely, because who knows what we add to the site in the future, that potentially breaks loose definitions.


Is it possible to repopulate radio button inputs with values from submission.fieldMetaData using the standard form.render() method? I've been able to repopulate text fields this way, but the radio buttons remain unchecked even when there was a value submitted.

{% for field in application.fieldMetaData %}
    {% set submittedValues = submittedValues|merge({ (field.handle): attribute(application, field.handle).value}) %}
{% endfor %}

{% if form|length %}
    {{ craft.freeform.form(form.handle, {
      returnUrl: '/',
      overrideValues: submittedValues
    }).render() }}
{% endif %}

A little messy but here's how I got it to work in case anyone else has this question. It even seems to respect the conditional display rules on my form.

{% if form|length %}
    {{ form.renderTag }}

    {% if form.hasErrors %}

        {{ "Error! Please review the form and try submitting again."|t('freeform') }}

        {% if form.errors|length %}

            {% for error in form.errors %}

{{ error }}

            {% endfor %}

        {% endif %}

    {% endif %}

    {% for row in form %}

        {% for field in row %}
          {% set columnClass = "freeform-column " ~ form.customAttributes.columnClass %}
          {% if field.type == "submit" %}
            {% set columnClass = columnClass ~ " freeform-column-content-align-" ~ field.position %}
          {% endif %}
          {% if field.type == "radio_group" %}

                {{ field.renderLabel() }}

                 {{ field.renderInstructions() }}

                {% if field.errors %}

                    {% for error in field.errors %}

{{ error }}

                    {% endfor %}

                {% endif %}
                {% for option in field.options %}

                        <input type="radio" class="freeform-input"
                          name="{{ field.handle }}" type="radio"
                          id="form-input-{{ field.handle }}-{{ loop.index }}"
                          value="{{ option.value }}"
                          {{ option.value == submittedValues[field.handle] ? 'checked' : '' }}>
                        {{ option.label }}

                {% endfor %}

          {% else %}

            {{ field.render({
              class: field.type != "submit" ? "freeform-input" : "",
              labelClass: "freeform-label" ~ (field.inputOnly ? " freeform-input-only-label" : "") ~ (field.required ? " freeform-required" : ""),
              errorClass: "freeform-errors",
              instructionsClass: "freeform-instructions",
              overrideValue: field.handle ? submittedValues[field.handle] : ''
            }) }}

          {% endif %}
        {% endfor %}

    {% endfor %}
    <input type="hidden" name="statusId" value="2">
    <input type="hidden" name="submissionId" value="{{ application.id }}">
    <input type="hidden" name="userId" value="{{ currentUser.id }}">
    <input type="hidden" name="userEmail" value="{{ currentUser.email }}">
    {{ form.renderClosingTag }}
    {% endif %}

{% js %}
    var act = document.getElementsByName('action');
    act[0].value = 'freeform/submissions/save'
{% endjs %}


Is it possible to order related entries by relevance of tags? I have a set of entries which each have 0 or more tags (no limit). For each entry, I am aiming to display 3 'related entries', related by the tags field.

e.g. If entry 1 is tagged with 'apples', 'bananas' and 'pears', then the first results returned would be entries with all 3 tags, followed by 2, followed by just one, then those with none of the tags.

Is this possible? I can't see anything in the documentation about ordering by relevance of the 'relatedTo' parameter.

I've made a plugin called Similar that does exactly this. Give it a go.


Add custom column to Entry Type index page I'm creating a plugin and I've been following the Mijingo Craft plugin development videos. I reached the point where a column was added to the entry index page. I was looking to do something similar but on the Entry type index page /admin/settings/sections/{ID}/entrytypes.

I was unable to find anything in the documentation (maybe I missed something). But is there a method I can use to do this? or is it not possible?

Unfortunately, that's not possible currently.

If you feel that a hook for customizing the Entry Type listing/index should exist, you can add a feature request.


How to configure Craft From for a form roulet/carousel? I'm making a website where the 1st submit of the form needs to be sent to 6 people on a list, the second submit of that same form needs to be send to the next 6 persons on the list, and so on.

I also would like to store the number of form submits every person in the list has received.

I was wondering if this is feasible with the Contact Form plugin.

It's not natively possible, but it's doable.

The plugin does support dynamic recipients.  I'm assuming that the rotating list of people is going to be stored in a database table or some other data source, though.  So in order to pull in the next correct group of people to send to, you'd need to write a plugin so you could run your own business logic.

  I also would like to store the number of form submits every person in the list has received.

When grabbing the next group of people to send to, your plugin would increment a tally for each of the people in the list and save it back out to a database table or other data store.


Grab first  or  src from Rich Text field I've had the unfortunate task of importing thousands of entries in to Craft from Worpdress.

Each of the entries has a Rich Text field and within the first couple of paragraphs there is an image.

I need to use this image as the overview thumbnail on a listing page. How can I extract that image tag, or just the src attribute from it to get the url.

Example of the image tag

<img class="size-full wp-image-13325" src="https://www.domain.com/news/wp-content/uploads/2015/12/image.jpg" alt="etc" width="620" height="385" />

I have tried to use the |split filter and some others with no success.

Thanks in advance for any help/direction

I had the exact same issue with a client and their horrendous Wordpress blog posts.

In the end, after importing all the posts and copying over the related images, I wrote a simple plugin that lets you use a |firstImage filter to grab the first image from a rich text field. I've added it to GitHub: https://github.com/DigitalSquid/Craft-CMS-First-Image

A rough example of how to use it:
{% set firstImage = entry.body|firstImage %}
{% if firstImage|length %}
    <img src="{{ firstImage }}" />
{% endif %}


Filter locations by zip in Smart Map I am making a simple call to entries near a zip code using this url format:

example.com/locations-zip-ajax?near=93003

My template includes this code:

{% set target = craft.request.getParam('near') %}
{% set params = {
  target: target|length ? target : 37212,
  range: target|length ? 30 : 7000,
} %}
{% set locations = craft.entries.section('locationsFeed').address(params).order('distance').find() %}

{% for location in locations %}
      //do some stuff

{% else %}

No results found

{% endfor %}

But no matter what zip code is entered, the results are always in the same order and the nearest entry is always 5,828.2 miles away even if the result entry's zip code is identical to the 'near' param.

I have ensured that my target param is being populated correctly and that my range param is working as well.

This turned out to be an issue with a misconfigured Google API key.
When setting up your Google API keys, make sure that:

Your domain is whitelisted (including any staging servers).
Your keys have granted permission to the necessary functionality.

Be sure to read the full instructions for how to set up your Google API keys.


Fields associated with a category group I have set up a custom field for a category group. How do I get the value of this field in a loop that goes through entries? Each category in the group could have this field's value set as true or false (light switch). I.e. I need to see what category is related to the current entry and then look up the value of this field - there is some html I want to show only if the value is true.

You can access a category's fields exactly like you
access an entry's fields.

{{ category.myLightswitch }}

So this should do it for your categories loop:

{% for entry in entries %}
    {% for category in entry.myCategoriesField %}

        {{ category }}

        {% if category.myLightswitch %}
            EXTRA
        {% endif %}

    {% endfor %}
{% endfor %}


No plugin exists with the handle "contact-form-extensions" I have installed and used the Contact Form Extensions plugin a few times but this time it's not working. I can see it in the CP but when I click to install it throws the error:

  Invalid plugin – craft\errors\InvalidPluginException
  'No plugin exists with the handle "contact-form-extensions".'

When I install it the command line does say something about Carbon 1 being deprecated but not sure if it's related?

Try running ./craft project-config/rebuild from your terminal.


Why would I choose Channels as opposed to Structures I can sort structures alphabetically, by date or by specific field. Structures also allow me to sort by the order specified in the Control Panel. So what is the use case for using Channels instead of Structures? Is there a performance cost? Is there anything that I'm missing?

From the official docs:

Channels are used for streams of similar content
Structures are good for times when you need to store multiple similar entries, and they need to be sorted into a specific order. They can also be hierarchical.

As per your question there's no performance overhead involved, but as a general rule it's usually a good idea to keep things as simple as possible.
Channels are bit simpler than Structures and if the content is neither hierarchical nor multi-level, there's no reason to use the latter. A blog is a good example of an appropriate use case for a Channel.


Internal Server Error on Locale This is my first multi locale Craft setup and everything is cool on my local/dev environment. However running into difficulty on the production server (Digital Ocean/ServerPilot). My default (EN) version plays nice but my German locale returns a 500 when isSystemOn => 'true'. When set to false it displays the appropriate 'Unavailable' (503) template. So it looks like everything is setup fine as far as Digital Ocean, ServerPilot, domain, etc... so I'm wondering if there's something amiss in my config?

I have 2 "apps" setup in ServerPilot for my default (EN) and German sites. My German app contains a public folder with an index.php that contains the server path:

$craftPath = '../../sitenamecom/craft';

I've also tried a full server path but the above seems to work fine anyway.

Then in my config/general.php I've got all the usual stuff, plus:

// production (DE)
'sitename.de' => array(
    'backupDbOnUpdate'                  => true,
    'devMode'                           => false,
    'environmentVariables' => array(
        'baseUrl'                       => 'https://sitename.de',
        'basePath'                      => realpath(dirname(__FILE__)) . '/../../public',
    ),
    'isSystemOn'                        => false,
    'siteUrl'                           => 'https://sitename.de/',
),

So as I say, everything working fine on my local server but with isSystemOn set to true on production I get an Internal Server Error. Anyone got any suggestions on how best to debug this?

Okay I'm all set now. Turns out it was a production environment/MySQL issue, and it's cropped up before on Digital Ocean. See Brad's fix here.

I just added

'initSQLs' => array("SET SESSION sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';")

to my config/db.php and good to go.


Updating Array Variable In Extending Template I have a layout template:

{% set bodyClass = [] %}

    {{ block('pageBody') }}

And a template that extends it:

{% extends 'layout' %}

{% set bodyClass = bodyClass|merge(['myclass']) %}

{% block pageBody %}

Hello World

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab, consequuntur!

{% endblock %}

When I try to append to the bodyClass variable I get the error:

  Variable "bodyClass" does not exist.

However if I call the variable:

{% for class in bodyClass %}
  {{ class  }}
{% endfor %}

The template renders fine. Why can't I merge onto a variable that's been set in an extended template? Is there another way to do this?

You can only access the parent's variable, not modify it. You could however simply add another block specifically for the body class, like so:

Layout template:

    {{ block('pageBody') }}

Template that extends it:

{% extends 'layout' %}

{% block body_class %}{{ parent() }} myclass{% endblock %}

{% block pageBody %}

Hello World

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab, consequuntur!

{% endblock %}

Note the call to parent() that includes the default classes defined in the parent template.


What are Search Targets and Search Query Input in seomatic? I am not sure this is the right place to ask this question but I really need to know it, but I couldn't ask it on the plugin's Github as this is not an issue. I am new on Craft CMS and my client need SEOmatic installed on it. I have configured everything but I couldn't understand the need of Search Targets and Search Query Input in Site Meta Section. It would be very helpful if someone helps me out for this with some real-life examples.

This has to do with Google Structured Data and how your site appears in search results. If you use these fields, your site will show a custom search box in Google Search results, like this:

Basically, the Search Target is a URL where you'd want any search queries to get directed to. So in the example above for Pinterest and a custom search for "Pizza", the Search Target might be:

pinterest.com/search?q={search_term_string}

The Search Query Input basically just tells Google what the placeholder is that you used in the Search Target. So in our example it would be:

search_term_string

I'd recommend reading this
page from the Google Search docs in order to better understand what Search Targets and the Search Query Input are.


Craft CMS Log Rotation Does Craft have somewhere where I can setup log rotation for the craft.log file, or do admins have to manage that manually?

Craft will automatically 'rotate' logs into new files as each current file reaches the maximum size (~1MB).

This is why, if you look in /craft/storage/runtime/logs, you may see craft.log, craft.1.log, craft.2.log, etc.

(I think Craft keeps a max of 5 files around before rotating them off into the ether.)

At present, the max size, naming convention, and max log file count, are all not configurable. (To my great chagrin.)

If you want to rename/archive logs for your own purposes, you'll have to create your own workflow for that, either by scripting it and running it as a cron job, or by using a third-party aggregation/archival service such as Papertrail.


Access table from Globals in script In the Globals I have a field which is of type table. I was trying to access this table in the javascript in order to get the values of particular rows.

<script>
        var myTable = "{{ craft.globals.getSetByHandle('myGlobal').myTable }}";
</script>

It returns a string "Array". If I put [] around the expression, it will return ["Array"].

Do you know a way to get access to this table?

If you want to get the values of your global table field, you need to loop through it.

somewhere in a template:

{% js %}
    var table = '
    {% for row in global.tableFieldName %}
      ({{ row.column1 }} - {{ row.column2 }}){% if not loop.last %},{% endif %}
    {% endfor %}'
{% endjs %}

Output:

<script>var table = '(Value 1-1 - Value 1-2),(Value 2-1 - Value 2-2)';</script>

You should check out the documentation about Craft 3 Table Fields, {% js %} Tag and for removing the trailing comma Remove trailing comma from an output loop?


Use Asset field as a Global Set Variable? Is it possible to use an Asset field as a Global Set? For example:

{{ companyInfo.logo }}

If companyInfo if your global and logo if your Asset field, then you'd use it just like you would anywhere else keeping in mind that all relation fields (Assets, Users, Entries, Tags, Categories, etc.) always return a collection of results.

So, companyInfo.logo.first() will return the first asset and if you want to loop through them all:

{% for image in companyInfo.logo %}
    {{ image.getUrl() }}<br />
{% endfor %}


first() don't seem to work while using an image transform This "standard" set-up for rendering asset transforms works as expected.

                {% set image = entry.articleImages %}

                {% if image | length %}

                            {% for asset in image %}

                            {% endfor %}

                {% endif %}

When I only want the first image out of the assets loop I would normally add .first() to the set image loop.

{% set image = entry.articleImages.first() %}

However this ends op in unclear error messages while rendering on the front-end ( Impossible to invoke a method ("getUrl") on a string variable ("111"))!
Or am I missing out on something?

This is where it goes wrong:

{% for asset in image %}

Because you use first(), you end up with an AssetFileModel, not an array of those.
If you than loop through that AssetFileModel with a for-loop, you get all the values of that Model. They don't have a method called getUrl(), which is what causes the error.

So, skip the for-loop and just use:

{% set image = entry.articleImages.first() %}
<img src="{{ asset.url('worksList') }}" />

The url() method is an alias for getUrl().


How would you handle subscriptions and recurring payments using Craft Commerce? I am building a VOD site using Craft and Craft Commerce. This is the first time I'm working with their Commerce plugin.

I would like to:

Create subscription products and assign them to plan types.
Subscription period of any duration and frequency.
Trial period to lower a price barrier for subscribers.
Payments processed via external gateways.

What is the best way of implementing such functionality?

At time of writing (22/08/16) - there is no support for subscriptions in Commerce.  You could attempt to do them as traditional products but I suspect it would be a lot of custom work.

It's a very frequently requested feature and I suspect proper subscriptions support will come to Commerce sooner rather than later.  Hopefully much sooner!

In the meantime, the solution most people seem to talk about is the Charge plugin instead of Commerce (or alongside Commerce for other commerce needs): https://squarebit.co.uk/software/craft/charge


Pull in stock from Craft Commerce cart I'm trying to set a max amount on the quantity in the cart. My problem is I want a customer to be able to update their quantity while in the cart, but I can't seem to figure out how to set the max quantity according to the stock on hand. I've done this with variant.stock in the items page, but can't seem to find anything that allows me to pull that information according to the lineItem in the cart page.

Assuming your lineItem variable is called item, you're looking for

item.purchasable.stock

You might also need to pay attention to maxQty which is the maximum number of a purchasable item that a user is allowed to buy. I limit customers to a maximum of 10 and create a select box on the basket form with a Remove option at the bottom:

{% set maxQuantity = item.purchasable.stock > 10 ? 10 : item.purchasable.stock %}
{% if item.purchasable.maxQty %}
    {% set maxQuantity = maxQuantity > item.purchasable.maxQty ? item.purchasable.maxQty : maxQuantity %}
{% endif %}

<select name="qty">
    {% for i in 1..maxQuantity %}
        <option value="{{ i }}" {{ i == item.qty ? 'selected' }}>{{ i }}</option>
    {% endfor %}
    <option value="0">Remove</option>
</select>


Include with -- variable does not exist error New guy here. Can someone explain what the proper way to pass a variable into an include? I'm using this:

{% include "singlepage" with { currentPage:'leaders' } %}

.. and I'm getting a Variable "currentPage" does not exist error.

In short, I have a template that I want to use for my singles pages, but I need to set a couple of variables for the template to use. So, I have a template for each single that simply sets the current page and then includes the single page template. Is this a good approach?

Thanks for any help.

You're probably looking for:

{% include "singlepage" with { 'currentPage': 'leaders' } %}

The key (the part before the :) needs to be a string, hence in quotes.

The relevant Twig template docs are here: https://twig.symfony.com/doc/2.x/tags/include.html#include


How to create a new Entry model in Craft 3? I want to create a new entry for a section named 'drinks'(having section id= 6). The namespace of the file having this code is namespace putyourlightson\userguide\controllers;.I copied this code from StackExchange. My first line is getting an error(Class 'Craft\EntryModel' not found).

$entry = new \Craft\EntryModel();

$attributes = [
  'section'   => $yourSectionId,
  'entryType' => $yourEntryTypeId,
  'field1'    => 'foo',
  'field2'    => 'bar'
];

$entry->getContent()->setAttributes($attributes);

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

How should you import it?

Just so there's an answer here. OP was using Craft 2 code in a Craft 3 environment.

The code should be something like this in Craft 3:

$entry = new Entry();
$entry->sectionId = $yourSectionId;
$entry->typeId = $yourEntryTypeId;
$entry->authorId = $yourAuthorId;
$entry->enabled = true;
$entry->title = "Foo";
$entry->setFieldValues([
    'field1' => "Foo",
    'field2' => "Bar",
]);
$success = Craft::$app->elements->saveElement($entry);
if (!$success) {
    Craft::error('Couldn’t save the entry "'.$entry->title.'"', __METHOD__);
}


Solved: Unable to get composer running to install CraftCMS for last weeks really I try to install composer, but it keeps complaining about SSL-problems. I searched everywhere on Google and found lots of people having the same problems, but for some reason they found solutions which don't work here.

I really need to install composer, 'cause I like to use Craft 3, but whatever I try, it's not working. There is no composer-forum, and I guess all you guys needed to use composer to install Craft 3 too, so I hope somebody here can help. I'm really out of ideas at the moment...

I tried this:

SETUP VIA WINDOWS INSTALLER

Download and run windows installer Composer 1.6.5: https://getcomposer.org/download/
I see the path is pointing to the right php.exe (and the same php I use on wamp server). I don't use a proxy.
Running composer after install shows me composer is installed.
Running composer diagnose however shows me a WARNING after https connectivity to packagist: The https://packagist.org/packages.json file could not be downloaded: SSL operation failed with code 1.
So in php I checked if php_openssl is enabled and it is.
I also have this in my php.ini file, pointing to a cacert.pem certificate, downloaded from http://curl.haxx.se/ca/cacert.pem :
openssl.cafile="C:\caCertificates\cacert.pem"
openssl.capath="C:\caCertificates\"
Running php --ini on cmdline I verified it's definitely the edited php.ini file that is active on command line/composer use (C:\wamp64\bin\php\php7.1.16)
I turned firewall off
I even uninstalled wamp-server with all php-stuff and installed a complete fresh, updated, install of Wamp server (3.1.3 64b)
But composer diagnose keeps throwing the warning, and composer is not working.

SETUP VIA PHP INSTALLER
Than I tried the php-install script I found on getcomposer.org and run this from command line:
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
 - It returns with:
Installer verified. All settings correct for using Composer. Downloading...
Composer (version 1.6.5) successfully installed to: C:\proj-folder\test\composer.phar
 - But when I run composer diagnose in that test-folder it's still throwing the warning.

BTW I tried above with administrative rights too.

I understand this has something to do with SSLv3, but whatever I tried, nothing seems to fix the issue. And again I tried a lot of things that worked for other people having this issue.

I'm out of ideas and composer obviously is not really helpful in showing a clue to what's the problem here.

Is there anybody here that can help me with this, or give me some direction on where to look? This is taking a lot of time and effort last weeks without luck and I'd like to start building websites with Craft 3 using composer!! Thanks!

Yes, after lots of trying I finally found the solution!! It was ESET causing the problem

Today, after re-installing PHP7 from scratch (so not using wamp and its contents anymore to make sure it's not wamp setup causing these problems) and reinstalling composer again I found that even that didn't work.

So I figured it really should be in some anti-virus or other blocking tool. And I only use ESET Internet Security.

But I tried disabling both ESET firewall as well as ESET anti-virus several times before and it didn't solve the mystery of the composer connectionproblems.
So I took a closer look at ESET advanced settings more deeply now and it seems that eventhough I thought I turned everything in ESETs scans and blocks completely off (and that's exactly what ESET displays!), there happens to be a SSL-scan that wasn't turned off by that so still actively preventing composer to work.

I would say scanning SSL communication by ESET shouldn't cause any problems anyhow, just some milliseconds delay or whatever, but it obviously was completely blocking composer from reaching packagist through ssl. Turning off the SSL scan made composer reach packagist with https immediately, so finally this solves the mystery.

For people coming here with the same problem with composer and SSL and use ESET too, if you set up php and composer correctly then you should try this on ESET to solve the problem:

Enter ESET advanced setup (via rightclick menu on ESET icon)
Open up 'Web and email', and then the 'SSL/TLS' tab
Click the 'edit'-link after 'List of SSL/TLS filtered applications' (a dialog opens)
Check if the php.exe composer is using is in the list and if it does: make sure the scan action is set to 'ignore'. That prevents ESET from scanning the traffic via SSL from php.exe. Perhaps you could also add some certificate to the 'List of known certificates' here, but I solved it this way instead.
If php.exe is not in the list you should get a dialog window when running
a composer command like 'composer diagnose' that needs ssl with packagist and ESET shows a dialog box for the new connection: set it to IGNORE. Scan will result in composer throwing warnings and having no connection with packagist.
If php.exe is not in the list and you don't get the ESET dialog window for the new connection when running 'composer diagnose' either you probably don't have the 'SSL/TLS protocol filtering mode' in ESET (under 'web and email'/SSL/TLS tab in advanced settings) set to interactive mode. Set it to interactive mode than.
If none of the above worked you could also choose to fully turn off all SSL/TLS protocol filtering in ESET by just switching it off in 'web and email' --> SSL/TLS --> Enable SSL/TLS protocol filtering. Then ESET isn't scanning any SSL anymore.

Hopefully this helps more people having the same issue.

Thisone really caused me headages for last weeks because it hold me back from switching to craft 3. So I'm really excited I finally got an answer to this now!! Finally it's time to move to the more important stuff!


How can I set the HTTP status code? I'm trying to set a 404 HTTP status response code for an ajax controller action in a plugin.

Yii has a response object where the code can be set, but I can't find a way to access it from craft.

I believe HeaderHelper::setHeader() is what you're looking for in the context of a plugin.


Craft Commerce: How to manage different prices with one product? I am looking for a solution that would allow me to sell events tickets within Craft. I'm am currently making a prototype with Craft Commerce plugin for this project, but I am already facing a major problem.

When creating a new product, I can set a ticket price + a total number of tickets available at this price. When using variants, I can define multiple tickets + a total of available tickets under each price. But it does not seems possible to synchronize a total number of available tickets with different price options (full price and other at reduced fare).

How can I do ? Is it possible to set the total number of tickets available outside of variants, such as the variants would be only used to set the price ?

Craft Commerce support has answered to my question :

  Currently Craft Commerce can not have a stock counter that covers more
  than one variant. The default product+variant system is not designed
  for tickets. We hope to see a ticket 'Purchasable' plugin in the
  future. You can request this feature here.

  If you have some plugin experience, you could get around this issue
  you described with a plugin that updates the stock of other variants
  when someone purchases a variant using the onOrderVariant event. You
  would likely need to know the relationships between SKUs for the
  different variants within your plugin, and deduct the stock from the
  related variants like so:

craft()->db->createCommand()->update('commerce_variants',
  ['stock' => new \CDbExpression('stock - :qty', [':qty' => $theQty ])],
  'id = :variantId',
  [':variantId' => $variantId]);

  Check out the Stock Notifier plugin as it matches closely to the
  events you would need to use.


My website isn't working: "Craft does not support backtracking to this version. Please upload Craft 2750 or later." I have been unable to access both my website and my login page. What's going on?

Sounds like your database was updated but your filesystem was not - that could happen perhaps if the development version and the live version share the same database, and only the development version ran the update.

To fix it try renaming the craft/app folder to craft/app-old, then copy the craft/app folder from the latest Craft download into your site.

Backup your stuff first.


Should my controllers actions 'return' when rendering templates? When developing a plugin, I am registering routes to controller actions. Other frameworks, read that as Laravel, typically return the controllers response.

public function showProfile($id)
{
    $user = User::find($id);

    return View::make('user.profile', array('user' => $user));
}

However, in Craft to render a template from your controller you would use the following.

public function actionPluginIndex()
{
   $this->renderTemplate('pluginName/template');
}

At the same time, you can also return the rendered template this way and it functions as expected.

public function actionPluginIndex()
{
   return $this->renderTemplate('pluginName/template);
}

Looking at craft/app/controllers/BaseController.php templateRender method shows that it accepts a parameter return.

So the real question is to return or not to return and I was hoping for a more technical explanation on the way Craft handles this.

There’s no reason for your controller to return anything – it will never be checked.

Many requests call more than one controller action, so it would be pretty presumptuous for Craft to think that it should actually output whatever your controller returns, and end the request.

Instead, it’s up to the actual controller to do the outputting, and end the request, if that’s what it wants to do.

$this->renderTemplate() comes from BaseController, and it’s basically a wrapper for craft()->templates->render(), except that after calling that, it will also set the Content-Type header for the request, insert any queued-up JS and CSS, and then end the request.


How to set up a social media feed with vue and axios What I need is a social media stream with posts with the Hashtag mnbasel form Instagram, Facebook and Twitter. I couldn’t find any good tutorial. Hopefully someone can tell me what I miss or lead me to a tutorial.

I signed in to Instagram as a developer and got my Access Token using Client ID and Client Secret.

Then I created a Vue component with this script:

 module.exports = {
  name: 'socialstream',
  data: function () {
    return {
      instagram: []
    }
  },
  mounted () {
    this.getEntries();
  },
  methods: {
    getEntries () {
      axios.get('https://api.instagram.com/v1/tags/search?q=mnbasel&access_token={access-token}')
        .then(response => {
          this.instagram = response.data.data
        })
    }
  }
}

What I get from this is the following error message:

Uncaught (in promise) Error: Request failed with status code 400

And this is the Result I get when I paste the URL in the Browser:

"code": 400,
"error_type": "OAuthPermissionsException",
"error_message": "This request requires scope=public_content, but this access token is not authorized with this scope. The user must re-authorize your application with scope=public_content to be granted this permissions."

I also installed the OAuth Plugin from Dukt and configured Instagram. Not sure if I need that and how to connect this information with the Vue component.

It looks like one of two things:

Its possible that you are not actually injecting your accesstokem with the following string

'https://api.instagram.com/v1/tags/search?q=mnbasel&access_token={access-token}'

I believe it should be :

`https://api.instagram.com/v1/tags/search?q=mnbasel&access_token=${access-token}`

If you still get the oauth error, you may need to ensure that your access token has the correct scope in instagrams dev


Trouble getting entries after a certain date I have a bunch of entries with field eventStartDate and eventEndDate.

First I set the date

    {% set fwd = entry.eventStartDate|date_modify('+30 min')|date('c') %}

When I output the value of fwd, it is 2017-04-21T08:30:00-05:00

So that's 4/21/2017 at 8:30 AM

Then I query the channel to get events after that date/time

{% set upcomingEvents = craft.entries
  .section('schedule')
  .order('eventStartDate asc')
  .eventStartDate('> ' ~ fwd )
  .limit(5)
  .status('live, pending')
%}

I get events on 4/21/17 starting at 8:00AM, not what I intended.

Is my syntax wrong?

This code works - I double checked. I had a bug further down in my code.

{% set fwd = entry.eventStartDate|date_modify('+30 min')|date('c') %}

{% set upcomingEvents = craft.entries
  .section('schedule')
  .order('eventStartDate asc')
  .eventStartDate('> ' ~ fwd )
  .limit(5)
  .status('live, pending')
%}


Best way to access getPrev and getNext asset fields I am trying to access the previous and the next blog entries from my current post. Here is what I am doing

{% set params = craft.entries.section('blog').order('postDate asc') %}

    {% set prevEntry = entry.getPrev(params) %}
    {% set nextEntry = entry.getNext(params) %}
    {# to access url, I use #}
    {{ prevEntry.url }}
    {# to access title, I use #}
    {{ prevEntry.title }}

The above works. However, I have an asset under the field handle "featuredImage". I need to access the image URL as well as I need to apply a transform. I am not sure the code below is the most efficient way to do this. Also the image transform is giving me an error (server returns 500 error)

{% for asset in prevEntry.featuredImage %}
    {% do asset.setTransform({ mode:crop, width: 200, height: 200 }) %}

{% endfor %}

Is looping the best way to access the data considering that there is only one image? Is there a better way? Image transform is also not working (not sure why).

If you need only one image, you can use the first() function instead of a for loop.

I recommend to check always if a image or entry exists, befor use it. Otherwise you risk to get an error. During development you should turn on the devMode, to see all errors (https://craftcms.com/docs/config-settings).

Here is a code example i would use in your case:

{% if prevEntry %}

    {% set asset = prevEntry.featuredImage.first() %}

    {% if asset|length %}

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

    {% else %}

    {% endif %}
{% else %}

{% endif %}


Remove expiration date field from section / entry type By default, every entry has an expiration date field allowing the editor to expire an entry. I'm currently working on an application with some sections where expiring an entry doesn't make much sense and could break some parts of the site. So I would like to remove the "expiration date" field from some sections, to prevent someone accidentally setting an expiration date, or setting the field believing it to work in a different way than it actually does.
I've found no way to properly remove the field, at least there's no option for that in the section settings. I could just hide the field with CSS and JS, but of course that's not a solid solution. I would prefer a solution that's enforced on API level. Maybe it can be done with a hook? Or is there a better solution for this (except building a custom element type from scratch)?

I'm not aware of any straightforward way to remove that field from the Entry form. You might want to consider creating a small site module, adding some logic in an event handler to re-write/remove the contents of the expiration date as necessary?


CORS preflight check failing for AJAX login form on different domain I've built an AJAX frontend login form, using plain Javascript. It works fine until I try and access it from a different domain.
The problem is CORS and the preflight request. The preflight request doesn't contain the payload from the login form so Craft routes it to a template called 'users/login'. But as the template doesn't exist, the preflight test is returned 'not ok'. CORS looks to be setup okay on the server, because I can get around the problem by creating an empty template for users/login. Then the login form works (preflight test returns ok, so browser proceeds with main request).
What I really need is for the preflight check to not trigger a 404 and return ok, even without the proper payload. Which means Craft not routing that to a template.
Console message without template existing:
Access to XMLHttpRequest at 'http://server.localhost/users/login' from origin 'http://test.localhost' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

General (under headers tab in Chrome):
Request URL: http://server.localhost/users/login
Request Method: OPTIONS
Status Code: 404 Not Found
Remote Address: [::1]:80
Referrer Policy: origin-when-cross-origin

Response header:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-Requested-With, Content-Type, origin, authorization, accept, client-security-token
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT
Access-Control-Allow-Origin: http://test.localhost
Access-Control-Expose-Headers: Content-Security-Policy, Location
Access-Control-Max-Age: 600
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Date: Tue, 14 Jul 2020 05:49:33 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.41 (Win64) PHP/7.0.33
Transfer-Encoding: chunked
X-Powered-By: Craft CMS

I'm using Wampserver and adding those via .htaccess.

Thanks for posting your headers, that was very helpful. I experienced a very similar problem just the other day and was able to work it out via trial and error and a whole bunch of Google/StackOverflow searches. I'm hoping I can save you the trouble.
So your .htaccess was not set up to respond to OPTIONS requests, which are sent as a preflight before every request. That can be fixed with the following:
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>

This just ensures that the server sends an OK response to a preflight request.
Now you have to make sure that your origin domain is actually allowed to make the request you're attempting. Here's how I solved this:
<ifModule mod_headers.c>
    Header always set Access-Control-Allow-Headers "X-Requested-With, Authorization, Content-Type, Request-Method"
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    SetEnvIf Origin "^http(s)?://(.+\.)?(localhost|staging.example.com|example.com)(:[0-9]+)?$" origin_is=$0
    Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
</ifModule>

We're updating the headers to allow certain kinds of requests through which would normally be disabled by default. You can adjust these as necessary, for example you may not need to allow Authorization headers or DELETE requests.
Then we whitelist a specified list of origins that should be allowed to make the request in these magic lines:
SetEnvIf Origin "^http(s)?://(.+\.)?(localhost|staging.example.com|example.com)(:[0-9]+)?$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

Update the list of allowed Origins, including a pipe between each. You may be able to wildcard entire domains. What this is doing is saying "if a request is from staging.example.com, set the appropriate Access-Control-Allow-Origin header."

Update from the future: Craft 3.5 includes an allowed GraphQL origins config setting which should presumably make this much simpler. You should be able to leave out both of the origin-related headers in my second example.


How to lock down entries to specific levels of Craft CMS Structure I’m trying to figure out a way to setup a simple Craft section: a list of projects, each project consisting of a title and a type; and a series of photos, each photo having a description.

BUT... If I do this as a structure, the client is then able to nest them in a nonsensical hierarchy, like adding projects as children of photos. I know that's going to confuse my client. A way to lock down entries to a specific level would be sufficient, but I'm not finding that...

Honestly, the answer to your question is to not use structures to solve this problem. Here's a rule of thumb on structures: don't mix content types in the same structure ("content type" being a generic term for specific pieces of your content; e.g. blog posts, projects, testimonials, slides, pages, etc). So, for example, in your case a structure shouldn't contain projects and slides. However, if you had maybe projects and sub-projects, that could work well as a structure.

There are at least two options that I think are a better way to solve this. I personally would prefer #2 but figured I'd give you both of the ones I thought of off the top of my head.

Create a channel of slides, and then from your project entries in your structure use an entries field to select the slides that should belong to that project entry. However, this could and probably would become an editing nightmare because all the slides of all the projects will be all in the same channel and depending on your editing workflow it could be a pain to try to edit one particular slide.
Utilize a Table, SuperTable, Matrix, or Neo field to create the slides inline in the project entries. All of these field types have the concept of repeating rows or blocks that you could use to create something like a collection of slides. A pro tip: if you want to keep the slides "separate" from your project content, you could always create a new tab in the field layout for your "slider builder".

Hopefully that helps... If you need me to clarify anything I said feel free to comment below and I can update this answer accordingly!

Update:

Here are some screenshots explaining what I was referring to in #2:

First create your matrix field:

Then when you're editing your projects you can "create" slides inline:

You can learn more about querying your new slides matrix field here: https://docs.craftcms.com/v3/matrix-fields.html


Recursive array search I've got a plugin where I've written a simple recursive array search to find an array within a nested/multidimensional array which contains a certain value.

private function arraySearch($haystack,$needle,$depth = 1)
{
    foreach($haystack AS $key => $value) {
        if (is_array($value) && array_key_exists('title',$value) && strtolower($value['title']) == strtolower($needle)) {
            return $value;
        } elseif(is_array($value)) {
            $depth++;
            $this->arraySearch($value,$needle,$depth);
        }
    }
}

With some logging within this function I can see that it works and returns at the correct time but when I call it in my plugin methods the response is empty.

For example, if I wanted to get an array which contains a title key and the value for this is venue, I'd type:

$result = $this->arraySearch($array,'Venue');

Applied to the following array:

Array
(
    [pages] => Array
        (
            [0] => Array
                (
                    [elements] => Array
                        (
                            [0] => Array
                                (
                                    [type] => text
                                    [isRequired] => 1
                                    [name] => question1
                                    [title] => Venue
                                )

                            [1] => Array

So this should return the array $array['pages'][0]['elements][0] but $result` seems to be empty.

Any suggestions?

Cole

This line:

$this->arraySearch($value,$needle,$depth);

should be

return $this->arraySearch($value,$needle,$depth);


How to set a button's active state in WYSIWG (Redactor plugin)? I am currently writing a simple Redactor plugin which adds a button to the WYSIWYG editor in Craft 2. This one just formats the selected text like the existing bold button does.

Now, my problem is that whenever some text is marked as bold and the text cursor is placed onto that text then the bold button's state is changed to active – as expected.

Like so:

But the same thing with my custom button that is added by my plugin won't work. The button's state is still inactive.

Have a look on the official docs of Redactor about how to Add Sup and Sub Buttons which comes with an example plugin (including the plugin code and the resulting WYSIWYG). There a superscript (x²) button is added. Even that one doesn't work.

As you can see in the screenshot the superscript button (x²) doesn't change its state to active.

So, my question is:
How can I achieve that the superscript button (x²) gets active when placing the text cursor onto superscripted text within the WYSIWYG editor?

This superscript button is just an example. Of course, the solution should work for all other custom buttons as well.
Also, I assume that my code base is the same that you can find on the how-to page I linked above.

I've done this by adding my button to activeButtons and adding my inline tag to activeButtonsState after adding button in init function.

In below example, cite is the tag and citebtn is the button's name.

init: function()
{
    ...
    if($.inArray("citebtn", this.opts.activeButtons) < 0) {
        this.opts.activeButtons.push("citebtn");
        this.opts.activeButtonsStates.cite = "citebtn";
}


Display Entries Belonging to User I'm trying to group entries by user on my front-end. I've got so far but I can't seem to get any further to my goal of displaying what type of entries they have posted e.g. lifestyle or tech. to do this I need to get the users entries and query a service dropdown field, if they have lifestyle selected in multiple entries only display 1x tag showing the user has lifestyle posts.

So far I'm just trying to query each user's entries.

{% set listings = craft.entries.section('listings') %}
{% set users = craft.users.group('influencer').all() %}

      {% for user in users %}

          {% if user.getPhoto() %}

          {% else %}

          {% endif %}

{{ user.username }}

            {{ user.location }}

{{ user.richText }}

        {% for user, entries in craft.entries.find() | group('users') %}
          {% for entry in entries %}
            {{ entry.title }}
          {% endfor %}
        {% endfor %}

      {% endfor %}

First off, the easiest way to fetch the user's entries is to do something like this:

{% set entries = craft.entries.authorId(user.id) %}
{% for entry in entries %}
    {{ entry.title }}
{% endfor %}

The way this works is you fetch all the entries that were authored by the current user in your main for loop by using authorId(user.id). In my opinion, this is a lot cleaner and more performant over what you were trying to accomplish with the  group filter.

Now, to answer your question; here's how I would solve it.

First, create an empty array:

{% set services = [] %}

Then, inside your user's entries for loop, we'd grab the value of the service field (for each entry) and see if it exists in the services array. If not, we'll merge it in.

{% set dropdown = entry.service.label %}
{% if dropdown not in services %}
     {% set services = services | merge([ dropdown ]) %}
{% endif %}

Then we can use this array to display whatever we want on the frontend:

{% for item in services %}
    {{ item }}
{% endfor %}

So, all together:

{% set users = craft.users.group('influencer').all() %}

{% for user in users %}

    {% if user.getPhoto() %}

    {% else %}

    {% endif %}

{{ user.username }}

    {{ user.location }}

{{ user.richText }}

    {% set entries = craft.entries.authorId(user.id) %}
    {% set services = [] %}
    {% for entry in entries %}
        {% set dropdown = entry.services.label %}
        {% if dropdown not in services %}
            {% set services = services | merge([ dropdown ]) %}
        {% endif %}
    {% endfor %}

    {% for item in services %}
        {{ item }}
    {% endfor %}

{% endfor %}


Is this Garnish message bad? "Element was added to more than one selector" My plugin has a field type. When I'm editing an entry which uses that field type, this message gets logged to the console:

Element was added to more than one selector

I can tell that it's Garnish which is generating this message, but what does it mean? Is it a problem that needs to be fixed? And if so, how would I go about fixing it?

That message comes from from Garnish.Select, when the same DOM element is associated with two separate Garnish.Select instances. The JS will log that message right before destroying the first Garnish.Select instance.

It’s not necessarily a bad thing, but could be an indication of something in your JS that could be improved. (Craft’s own JS causes several of those messages, so don’t worry too much about it if everything seems to be working correctly.)


Public profile page: requesting user ID with dynamic url routing I've been referring to this question (can't comment as I don't have enough cred) — The most popular answer works, but I can't for the life of me figure out why Brad's version doesn't. When I try I get the following error:

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

{# Not Working (Brad's version) #}
{% set notWorkingUserId = craft.request.getSegment(2) %}
{% set notWorkingUser = craft.users.id(notWorkingUserId) %}

{# Working #}
{% set workingUser = craft.users.username( craft.request.segment(2) ).first() %}

{% block content %}

Not Working: {{ notWorkingUser.email }}

Working: {{ workingUser.email }}

    {{notWorkingUser}} {# Throws an error #}
    {{workingUser}}

{% endblock %}

I'm using the dynamic url routing mentioned in the most popular example, with the username. I also tried:

craft.users.username(notWorkingUserId)

Which throws the same error.

Now as far as I understand it (I've only been working with Craft for a short period), .first() is only required when it's an array. But Brad's version is passing the actual ID so shouldn't be required?

Can anyone explain this in simple terms? Craft is great, but for beginners it can get a bit confusing.

.first() is going to be required... I updated the answer in the original question, but here it is for posterity, too:

{% set notWorkingUserId = craft.request.getSegment(2) %}
{% set notWorkingUser = craft.users.id(notWorkingUserId).first() %}

Then you can access the properties of that user because it's a proper UserModel now:

notWorkingUser.email


Adding CC to contact form plugin I'm trying to edit the Contact Form plugin to copy the email to another address.

Looking in apps/models/EmailModel.php I see this for CC:

'cc' => array(AttributeType::Mixed);

Looking in apps/services/EmailService.php I see this:

// Add any CC's
if (!empty($emailModel->cc))
{
    foreach ($emailModel->cc as $cc)
    {
        if (!empty($cc['email']))
        {
            $ccEmail = $cc['email'];

            $ccName = !empty($cc['name']) ? $cc['name'] : '';
            $email->addCC($ccEmail, $ccName);
        }
    }
}

So, it looks like 'cc' needs to be array? I tried it without it being an array and I get an invalid foreach error. At the moment, I've added this line to ContactFormService.php (after line 45):

$email->cc = array('email' => 'name@domain.com');

I've also tried this:

$email->cc = array('email' => 'name@domain.com', 'name' => 'My Name');

The problem: The email is not being CC'd and I'm not getting any errors. Anyone?

From that code snippet, it looks like you're needing nested arrays. Try setting your $email->cc property like this.

$email->cc = array(
    array('name' => 'Your Name', 'email' => 'name@example.com')
);

The code snippet is iterating over $email->cc as $cc as the first array, then going into cc['name'] and cc['email'] as a nested array.


Displaying updated date for entry I want to display the date an entry has been updated if it is later than the post date. So I've tried this:
   {% if entry.dateUpdated >= entry.post %}
      Updated on {{ entry.dateUpdated.format('j M y') }}
   {% endif %}

But the date updated displays even if it is earlier. Is this because the date is not strictly a value as such?

Date fields in Craft are returned as DateTime objects, so you need to convert these to a string to be able to compare them.
The best way of doing this is to use the yyyy-mm-dd format as this makes it is easy to compare dates.
So your comparison would become:
   {% if (entry.dateUpdated|date('Y-m-d')) >= (entry.postDate|date('Y-m-d')) %}
      Updated on {{ entry.dateUpdated.format('j M y') }}
   {% endif %}


Update globals Matrix via a plugin We are working on a plugin that needs to update the contents of a Matrix field within Globals. I've found little snippets of code but none that do exactly this. Can anyone provide some code or point me to some code that does such? I can't seem to figure it out. Code sample of what I have so far is below. This does not result in any sort of area, but also does not seem to do anything.

As you can see, I'm trying to force all couponCodes to be 'BBB' whilst copying all other values. Maybe there is a much better way? It would be nice to just update the code value without recreating the full set of data if possible.

$couponCodeMatrix = craft()->globals->getSetByHandle('couponCodes');
$newMatrixData = array();

foreach($couponCodeMatrix->couponCodes as $block) {
    $blockData = array(
        'type' => $block->getType()->handle,
        'enabled' => true,
        'fields' => array(
            'couponCode' => 'BBB',
            'couponType' => $block->couponType,
            'couponValue' => $block->couponValue,
            'expiryDate' => $block->expiryDate,
            'singleUse' => $block->singleUse,
        )
    );

    $newMatrixData[$block->id] = $blockData;
}

$couponCodeMatrix->setContentFromPost($newMatrixData);

craft()->globals->saveContent($couponCodeMatrix);

I do believe you're missing the handle to the top-level Matrix field when you're passing into setContentFromPost. You should be doing:

$couponCodeMatrix->setContentFromPost(array('couponCodes', $newMatrixData));

Otherwise, you're just passing:

'45623':
    'type': 'blockTypeHandle'
    'enabled': true,
    'fields': []

to the global set, instead of:

'couponCodes':
    '45623':
        'type': 'blockTypeHandle'
        'enabled': true,
        'fields': []

To deal with fetching even disabled blocks, you'll need to actually query the Matrix field's block directly, rather than looking just at the Global Sets content.

$criteria = craft()->elements->getCriteria(ElementType::MatrixBlock);
$criteria->fieldId = craft()->fields->getFieldByHandle('couponCodes')->id;
$criteria->ownerId = $couponCodeMatrix->id; // This is your Global Set id
$criteria->status = null;
$blocks = $criteria->find();

foreach($blocks as $block) { }

More complete code for anyone interested. We loop through the matrix and recreate it only if we updated the disabled status on the matching item.

Thanks again!

    $couponCodeMatrix = craft()->globals->getSetByHandle('couponCodes');

    $criteria = craft()->elements->getCriteria(ElementType::MatrixBlock);
    $criteria->fieldId = craft()->fields->getFieldByHandle('couponCodes')->id;
    $criteria->ownerId = $couponCodeMatrix->id; // This is your Global Set id
    $criteria->status = null;
    $matrixBlocks = $criteria->find();

    $b_updateMatrix = false;
    $o_foundCoupon = false;
    $newMatrixData = array('couponCodes'=>array());
    foreach($matrixBlocks as $block){

        $b_enabled = $block->enabled;
        if ($this->couponCheckMatch($s_code, $block)) {
            $o_foundCoupon = $block;
            if($block->singleUse==1){
                $b_updateMatrix=true;
                $b_enabled = false;
            }
        }

        $blockData = array(
            'type' => $block->getType()->handle,
            'enabled' => $b_enabled,
            'fields' => array(
                'couponCode' => $block->couponCode,
                'couponType' => $block->couponType,
                'couponValue' => $block->couponValue,
                'expiryDate' => $block->expiryDate,
                'singleUse' => $block->singleUse,
            )
        );
        $newMatrixData['couponCodes'][$block->id] = $blockData;
    }
    if($b_updateMatrix) {
        $couponCodeMatrix->setContentFromPost($newMatrixData);
        craft()->globals->saveContent($couponCodeMatrix);
    }


Trying to set an image URL using a global asset field I've setup a global asset field so that a client can change a placeholder image that gets used throughout their website. I'm using the Imager plugin for my image transforms.

The following image code works fine when using a hard-coded image URL:

{% set image = 'http://placehold.it/450x225/eee/eee' %}
{% set transformedImage = craft.imager.transformImage(image, { width: 450, height: 225, }) %}

But when I replace the hard-coded image URL with the global asset field, it doesn't work:

{% set image = '{{ websitePreferences.placeholderImage.first.url }}' %}
{% set transformedImage = craft.imager.transformImage(image, { width: 450, height: 225, }) %}

I get this error message (Craft/Exception):

  Source folder “/Users/stephen/Repositories/repo-name-here/public_html./” does not exist

If I output the global asset field elsewhere in the template, I get this:

  http://my-locl-url.dev/uploads/images/Placeholders/placeholder-image.png

If I hard-code this URL into my image code, the image works fine. So does anyone have any idea why using a global for the placeholder image in this way isn't working? Thanks for any help.

I don't know if it's the cause of the error you're receiving or not, but this is incorrect Twig syntax:

{% set image = '{{ websitePreferences.placeholderImage.first.url }}' %}

You'll never need to nest {{ }} inside of {% %} or visa versa in Twig.

This is correct:

{% set image = websitePreferences.placeholderImage.first() %}


How to output a resized Assets image in a template How can I output a resized Assets image in a single entry template? I'm trying to do what I used to do all the time with CE Image in EE. I'm hoping that I don't have to create an image transform in the CP for every single piece of image resizing that I want to do as that would be very time consuming (compared to using the CE Image parameters for width and height in EE).

Here's my current code:

{% for asset in entry.mainImage %}

{% endfor %}

And for EE users, here's what I'm trying to replicate from EE:

{cf_static_image}
  {exp:ce_img:single src="{url}" width="800" height="600" alt="{alt_text}"}
{/cf_static_image}

Thanks for any help in advance.

Update: Here's what my final code looked like after following the example in the link provided by Brad below:

{% set thumb = {
  mode: 'fit',
  width: 800,
  height: 600,
  quality: 80
} %}

{% for asset in entry.mainImage %}

{% endfor %}

You can dynamically create and specify transform parameters from your templates to achieve what you're looking for.  See here for an example.


Group Sites by Groups We have many country websites on different continents. In the CP we group the sites, where the group has the name of the continent.

Now we want to group the sites in the templates in the same way.

I have this, but it is not giving back the group.

{% set allSites = craft.app.sites.getAllSites() %}
{% set allGroups = allSites|group('allSites.group') %}

{% for group, countriesInGroup in allGroups %}
    {{ group }}

    {% for country in countriesInGroup %}
        {{country.language}}

    {% endfor %}
{% endfor %}

Hope somebody can point me in the right direction.

Thanks!

You can use the Site component

PHP

$groups = Craft::$app->getSites()->getAllGroups();
foreach ($groups as $group){
    echo $group->name;
    foreach ($group->getSites() as $site){
        echo $site->name;
    }
}

Twig

{% set groups = craft.app.getSites().getAllGroups() %}
{% for group in groups %}
    {{ group.name }}
    {% for site in group.getSites() %}
        {{ site.name }}
    {% endfor %}
{% endfor %}


Test for empty field? How do I check if a field is blank? I thought it was something like

{% if article.articleImage is defined %}

Which in this case is testing an Assets field (and seems to work). Tried the same for a text field (in a Matrix row), and that doesn't work, as far as I can tell:

{{ if block.heading is defined }}

The answer depends on the field type.

Assets, Categories, Checkboxes, Dropdown, Entries, Matrix, Multi-select, Radio Buttons, Tags, and Users fields each will each appear to be non-empty, even if they don’t have a user-submitted value, because in each case, entry.myFieldHandle will still return something:

an ElementCriteriaModel object for Assets, Categories, Entries, Matrix, Tags, and Users fields
a MultiOptionsFieldData object for Checkboxes and Multi-select fields
a SingleOptionFieldData object for Dropdown and Radio Buttons fields

So for each of those field types, you will need to use the |length filter to determine if there is an actual value.

{% if not entry.myCheckboxesField|length %}

Also worth noting for Assets, Categories, Entries, Matrix, Tags, and Users fields: ElementCriteriaModel objects also have a total() function which can be used to speed up your template if all you need to know is whether any elements were selected, and you won’t be actually looping through them further down in the template. (|length actually fetches the elements first, and then returns the count of the results, but stores those elements internally in case they are going to be looped through later on. .total() runs a separate, much less expensive DB query, but the results won’t be cached internally for later use.)

{% if not entry.myCategoriesField.total() %}

Color, Date/Time, Lightswitch, Number, Plain Text, Rich Text, and Table fields will each return an empty value (null, an empty string/array, etc.) when there is no value, so each of those field types are safe to use with the is empty test:

{% if entry.myTextField is empty %}

This would also work:

{% if not entry.myTextField %}

In all cases, the |length filter will work as expected, so if you don’t want to remember this stuff, just use that.


Element API: return a related entry's asset data with entry data I'm producing a list of entries that each use an image asset from a related entry.  I see in this ticket how to get an asset's data, and I've combined that with how I learned to output an object's data from a Mijingo video (via the array_map function).

This approach works, but seeing how there are about 130 entries in the section, the load time is a whopping 2.31s versus when I take out the related entry's asset code, which then results in a much smaller 500ms load time.  Is there a better way to access this data?

Here's the code I'm using:

return [
    'endpoints' => [
        'api/pressEntries.json' => [
            'elementType' => 'Entry',
            'criteria' => ['section' => 'pressEntries'],
            'transformer' => function(EntryModel $entry) {
                return [
                    'id'                => $entry->id,
                    'title'             => $entry->title,
                    'slug'              => $entry->slug,
                    'postDate'          => $entry->postDate,
                    'type'              => $entry->type->name,
                    'url'               => $entry->url,
                    'jsonUrl'           => UrlHelper::getUrl("api/press/{$entry->id}.json"),
                    'relatedRecording'  => array_map(function(EntryModel $album) {
                        $data = [
                            'id'    => $album->id,
                            'title' => $album->title,
                        ];

                        $artwork = $album->recordingArtwork->first();
                        if ($artwork) {
                            $data['artwork'] = $artwork->url;
                        }
                        return $data;

                    }, $entry->relatedRecording->find()),
                ];
            },
        ]
    ]
];

Sounds like you need to do some eager loading of your assets.

Here are some examples of how to do that in the Element API plugin:

ElementAPI handling CategoryModel and returning a heirachical object

Getting categories in elementAPI performance issues and help required


Is it possible to add to cart with a radio input rather than a select? I'd like to add a digital product to the cart using a radio input but all the template examples I see use select. Is it possible to use a radio input?

With Select (working):

<select name="purchasableId">
  {% for digitalProduct in entry.fontProducts.all() %}
    <option value="{{digitalProduct.purchasableId}}">{{digitalProduct.slug}}</option>
  {% endfor %}
</select>

With radio inputs (not working):

{% for digitalProduct in entry.exampleProducts.all() %}

      <input
        type="radio"
        name="product"
        value="{{digitalProduct.purchasableId}}" />

{% endfor %}

The name for your input needs to be purchasableId:

{% for digitalProduct in entry.exampleProducts.all() %}

      <input
        type="radio"
        name="purchasableId"
        value="{{digitalProduct.purchasableId}}" />

{% endfor %}


User profile validation control I have a front end reg form saving profiles via ajax. All user and profile data is getting stored ok.
The problem comes with validation.

It seems Craft likes to load the errors for certain fields first:

{% macro errorList(errors) %}

So the above fills the error array with username, title and email before processing the others. In ajax am getting the errors like:

function appendErrors(errors) {

$.each(errors, function(field, errors) {
    appendError(field, errors);
});
}

function appendError(field, errors) {
var $errorList = $( '<ul class="errors" />' );

$.each(errors, function(index, error) {
    $errorList.append($('<li>').html(error));
    console.log(error);
});

$("input[name=" + field + "]").after($errorList);
}

I'd like to show all the errors to the user at once on page for sensible UX reasons. So quick brain check - is Craft really doing this? If so, then I see my options like:

Could switch to a pure jQuery/js validation
Could try and manipulate the error array and reindex based on tabindex?
Could try overriding Craft's validation thru plugin

Has anyone else come at this in earnest?

So the solution for me in the end was to add the profile field name as a class name eg 'address1';

 <input id="address1" name="fields[address1]" type="text"
placeholder="address line 1" class="form-control input-md address1" req="">

Then in my ajax post when looping through the errors looking for the element class name

$("." + field).after($errorList);

as opposed to the input field name.

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

    //reset error messages
    $(this).find( '.errors' ).remove();

    if (response.success) {
        console.log("success")
    } else {
        event.preventDefault();
        console.log(response.errors);
        //reset errors
        $('.submit').append('Please check the form for errors');
        appendErrors(response.errors);
    }

});

function appendErrors(errors) {

$.each(errors, function(field, errors) {
    appendError(field, errors);
});
}

function appendError(field, errors) {
   var $errorList = $( '
' );
   $.each(errors, function(index, error) {
     $errorList.append($('
').html(error));
   });
   $("." + field).after($errorList);
   // $('input[name='+ field +']').after($errorList);
}


Uploaded images stripped of EXIF data I've got a clean install of Craft with just some initial configuration of entry types, categories, assets, etc.

Any image I upload (either directly into assets or via an image field) has almost all of its EXIF metadata stripped out. Most of the removed info I don't care about, but it's essential that the embedded copyright, geolocation and keyword information is retained.

I assume this has something to do with the configuration of ImageMagick/GD (whichever Craft uses by default). Is there a way I can create a configuration that specifies which EXIF fields to preserve in the uploaded image?

Currently the images are being cleansed on upload, which means that they are being re-saved to make sure that no malicious code is injected in them.

Unfortunately, this means that EXIF data gets stripped out along the way, but since Craft already uses PEL, it should be possible to tweak Craft a bit so that the EXIF metadata is preserved during this operation. We'll look into that and, hopefully, this will make it to a release pretty soon.


Is there a way to programmatically clean/Redactorize HTML? I'm writing a plugin to import posts from a WordPress blog. The source post formats are inconsistent, some coming with full HTML (<p> tags wrapping each paragraph, etc.), while others seem to just have tags added for formatting and emphasis—newlines but no paragraphs or <br>'s.

 $entry->setContentFromPost(array(
     'body' => $post['content'],
 ));

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

So without any adjustment, many of the imported posts look crappy on the front end when they're just dropped into a template with {{ entry.body }} (Rich Text). The missing paragraph tags look less than stellar, and they're semantically uncool as well.

Redactor handles this just fine, so viewing and saving the imported entry in Craft is a breeze. It's not ideal, however, to go back and manually re-save each entry. Is there a way to re-save a cleaned Rich Text field via Craft's API, or do I need to standardize post HTML prior to my Craft import process?

The Purify setting for the Rich Text field will run the data through HTMLPurifier, which will do its best to try and remove any malicious code from the data.

I think what you're seeing is Redactor itself cleaning the rogue HTML elements before submitting to POST/Craft.

I think it's probably going to be easier trying to clean your HTML with some custom logic/regex before saving rather than trying to figure out how to spin up Redactor to do the cleaning.


Override a model method via plugin I'm new to Craft and PHP, so thanks ahead of time!

Is it possible to override a model method with a plugin? For example, I want to add some additional functionality to the getUrl method of the AssetFileModel class. Can this be done in a plugin?

Thanks!

Kind of, but probably not what you're looking for.

Your plugin can certainly do a MyAssetFileModel extends AssetFileModel and (in the scope of your plugin) take advantage of existing AssetFileModel methods and properties and override them as needed.  But that's the catch... it will only be within the executing scope of your plugin.

There's no native way (without hacking core Craft files) to tell Craft "use MyAssetFileModel wherever you would normally use AssetFileModel".

However, this is certainly much more doable in the upcoming Craft 3 release since Yii 2 has native support for things like service locators and dependency injection.


Use a UUID for an entry ID in a URL path When setting an "Entry URI Format" for a section, the values currently available to me are {slug} which is the title of the entry, or {id} which is usually a small number. I'd like to have an obfuscated hash/uuid for this path, eg c54be165-fc95-4583-bef8-2e2abd2a4f37. Is this possible without writing any custom code?

You can use {uid} inside the "Entry URI Format" field.


How to remove something from the slug/url I got entries in the folder /cms/web/videos/ and the url display this: http://domainname.com/cms/web/videos/barco-auro.

I want it to be this: http://domainname.com/videos/barco-auro.

How do I create clean URLs? I don't find anything on this, or nothing that helps my problem.

In Craft 3 under your section config change the URL config

https://docs.craftcms.com/v3/sections-and-entries.html#sections

SEE: Entry URI Formats

You can select what template it should render and what URL config you want

Failing this you can use something more advanced:

https://docs.craftcms.com/v3/routing.html#advanced-routing-with-url-rules


Craft 2 - Returning Value in Variable from Plugin to Template I am working with a plugin and wish to return a value from it to the template that calls it.  In my plugin, there is an XML feed call, and I would like to pass a value(variable) from the plugin to the template that indicates whether there is any value for a particular XML query.

I currently am working with a Plugin Variable, which returns the XML output.
How would I be able to return a second value to the template for a conditional 'display/don't display' value?  Would I need to set up a second class, apart from my main XML feed variable?

Thanks.

Here is the basic layout of the plugin:

XmlFeedVariable.php

<?php
namespace Craft;

class AiPaginationTestVariable
{
        public function AiPaginationTestVariable($optional = null)
        //the '$optional' in the function call passes to the xml call for the query
            {
            //performs XML call and string assembly here, then echo's to output
            //if there is no xml returned, I would like to do this:
            //$xml_return='no';
            //then have the template pick it up.
            }
}

You're close, you don't necessarily need another class or function. Though you may want to change the function name to be a bit more clear, and you may want to move the heavy XML logic into a separate private method.

class AiPaginationTestVariable
{
        public function getXml($params = null)
        {
            // Compile XML
            $xml = $this->_compileXml($params);

            // Return XML or false
            if ($xml) {
                return $xml;
            } else {
                return false;
            }
        }

        private function _compileXml($params = null)
        {
            // Whatever it takes to compile XML
        }
}

Then in your Twig template, you can do something like this...

{% set xml = craft.aiPaginationTest.getXml() %}

{% if false == xml %}
    {# Handle no XML #}
{% endif %}

This might not directly answer your question, but I hope it's enough to get  you back on track.


Trying to set a default asset folder using Craft tags I'm trying to set a default asset folder for a field, as per the Craft docs on Dynamic Subfolder Paths.

I'm trying to set the asset path for each blog article to be /our-journal/{date}/{slug}, where date is the creation date of the article, falling back to today's date otherwise.

/our-journal/{ entry.dateCreated|date('Y-m') ?: now|dateCreated('Y-m') }/{ slug }

I'm getting an internal server error:

  Could not resolve the subpath “our-journal/{ entry.dateCreated|date('Y-m') ?: now|dateCreated('Y-m') }/{ slug }”.

I've tried using both single and double braces around the variables, as the docs are a bit ambiguous.

I had a stupid typo in my path, where instead of the date filter, I'd typed dateCreated:

/our-journal/{ entry.dateCreated|date('Y-m') ?: now|dateCreated('Y-m') }/{ slug }

Should be:

/our-journal/{ entry.dateCreated|date('Y-m') ?: now|date('Y-m') }/{ slug }

Which, with what I learnt about fallbacks (see below), can be shortened to:

/our-journal/{ entry.dateCreated|date('Y-m') }/{ slug }

And if you're doing this within a matrix field, the entry is referred to by owner - so you'd need:

/our-journal/{ owner.dateCreated|date('Y-m') }/{ owner.slug }/

Fallbacks

I also found that I didn't need to provide fallbacks for entry tags - I'd thought that I'd need to provide a fallback for when a new entry hasn't yet been saved (to stop it falling over on e.g. dateCreated).

It appears that Craft is clever enough to hold the image in some temporary store, then move it into the intended folder when the article is first saved.


Search for entries with matrix field's value I'm building a travel site containing Campaigns (entries)

Each of these campaigns has multiple travel dates, modelled by a matrix field of

[startDate, endDate, price]

I want to do the following searches:

Campaigns with a travel startDate next month
Campaigns whose duration is more than 30 days and less than 60 days (endDate-startDate)
One of those criteria
All of the above criteria

How can this be achieved with twig template or custom controller?

PS: I looked into relations but that only applies to entries field, not matrix, and neither does it allow filtering on the related entity.

What I've done in a comparable situation is to add a hidden field to that entry type. And then I save Matrix data to that field on every save by listening to onBeforeSaveEntry events.

This makes the access to such processed Matrix data in templates or other parts of your plugin (i.e. getEntryTableAttributeHtml) very easy.

Event handler

craft()->on('entries.onBeforeSaveEntry', function(Event $event)
{
    $entry = $event->params['entry'];

    if($entry->section['handle'] == 'campaigns')
    {
        $longTravels = false;

        foreach ($entry->travelDates as $travelDate)
        {
            $duration = $travelDate->startDate->diff($travelDate->endDate);

            if ($duration > new DateInterval('P30D') && $duration < new DateInterval('P60D'))
            {
                $longTravels = true;
                break;
            }
        }

        // Set value for hidden field 'longTravels'
        $entry->getContent()->longTravels = $longTravels;
    }
});

CSS for hidden field type

{% set hiddenField %}
    #fields-{{ id }}-field {
        display: none;
    }
{% endset %}

{% includeCss hiddenField %}


Redactor removes article tag from HTML while saving I'm using Craft 3.x. I'm using the Redactor plugin in Craft. I have an HTML with an article tag (HTML tag). As soon as I save it, Redactor strips the article tag because of which UI goes for toss.

.
.
.

After saving, It simply removes these tags and keeps the inner tags.

You can specify custom HTML Purifier configs so that it will allow article tags and the Redactor fields will respect the configs.
See: https://github.com/craftcms/redactor#html-purifier-configs


Group by related entries field I have a channel of Musical Compositions:

Now all these Compositions have a Composer.
All the composers are stored in a separate channel and then linked with their respective compositions trough a entries field

Now I like to order the compositions by the last name of the composer and group them together alphabetically.

I managed them to be grouped but not by the composer name, just by the composition title which is not helpful.

{% set allEntries = craft.entries.section('komposition').limit(null) %}
{% set allEntriesByPrename = allEntries | group('title|upper|slice(0, 1)') %}

{% for letter, allEntriesSorted in allEntriesByPrename %}
 {# stuff #}
{% endfor %}

Now the catch here is the group filter I guess but when I use the handle of the entries field it doesn't work:

| group('composerRelated.lastName|upper|slice(0, 1)')

If I write it like this, the group filter simply is ignored. Since the entries field returns an array I have no idea how to write this. And I think at the time of the group filter the entries field is not available yet.

I also tried to grab all composers from the section and put it into the group filter:

{% set allEntries = craft.entries.section('komposition').limit(null) %}
{% set composerName = craft.entries.section('komponist').limit(null) %}

{% set allEntriesByPrename = allEntries | group('composerName.lastName|upper|slice(0, 1)') %}

This is too much inception for me. How do I have to write this?

I think you're nearly there! Give this a shot:

{% set allEntriesByPrename = allEntries | group('{{ (object.composerName | first).lastName | upper | slice(0, 1) }}') %}

I believe the issue is that relations always come back as an array, and you were trying to immediately access a property of the Element at index 0 in that array, rather than de-referencing it, then accessing the property.

Be aware that this may generate some deprecation errors, as using the first filter will attempt to use the relational field as an array. Instead, I'd recommend eager-loading your Composers along with the Compositions--in addition to being safer, moving forward, it avoids an N+1 problem, and will dramatically reduce the number of queries on your page:

{# See: `with`, including the `composerName` field handle: #}
{% set allEntries = craft.entries({
  section: 'komposition',
  limit: null,
  with: [
    ['composerName']
  ]
}).all()

{# Grouping by a relation will no longer trigger another query per Composition: #}
{% set allEntriesByPrename = allEntries | group('{{ (object.composerName | first).lastName | upper | slice(0, 1) }}') %}

  Note: I had to use the expanded syntax for the group filter ({{ ... }}) in order to enforce the proper order of operations. In that context, object is a reference to the Composition.


Front-end form submission feedback on buttons I have some front-end forms including login and sign up which are modal windows. Currently I have no feedback for the user if they are successful or not.

What's the best method to return something in the modal?

In an ideal world I'd like to update the buttons somehow, like these:

http://demo.tutorialzine.com/2013/10/buttons-built-in-progress-meters/
or
http://lab.hakim.se/ladda/

Any ideas or help would be appreciated.

I'd say your best bet would be to use something like jQuery to make an AJAX call and then manipulate the DOM depending on what response you get. Something like:

$('#loginForm').submit( function (e) {

  // Stop the form actually doing anything or refreshing the page
  e.preventDefault();

  // Get the inputs from the form
  var data = $(this).serialize(),
      button = $(this).find('button');

  // Find our button and change its text or something...
  button.text('Logging you in');

  $.ajax({
    method: 'POST',
    url: '/',
    data: data,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
  })
  .success(function(data) {

    // They are now logged in, reflect that in your button
    button.text('Logged in!');

    // Maybe redirect them aswell?
    window.location.href = "/some/members/area";

  })
  .error(function(data) {

    // Something went wrong, we can now show that
    button.text('Something went wrong');

  });
});

Thats a adapted version of code I found from this question: Front end login form with ajax so I haven't actually tested it, but it sounds like it should work and then changing the button is just straight forward jquery.

If you get errors with CSRF protection, then you should find a solution here: http://buildwithcraft.com/help/csrf-protection#updating-your-javascript

Hope that helps get you started!


Adding a drop-down to Contact Form plugin The GitHub page shows how to add additional fields as input fields or checkboxes. But I can't figure out how to add drop-down fields. Is this possible?

<select>
  <option value="——" selected="selected">Please select...</option>
  <option name="message[model][]" value="volvo">Volvo</option>
  <option name="message[model][]" value="saab">Saab</option>
  <option name="message[model][]" value="mercedes">Mercedes</option>
  <option name="message[model][]" value="audi">Audi</option>
</select>

Thanks!

It's definitely possible, your HTML is just a little off...

When dealing with a dropdown menu, the name attribute should be part of the select tag, not each individual option:

<select name="message[model]">
  <option value="——" selected="selected">Please select...</option>
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
  <option value="audi">Audi</option>
</select>


Using Routes to load Controller I love routes, but they appear limited to loading a specific template. What about loading a plugin/controller based on the route instead? Anyway to do this in craft or am I limited to .htaccess?

This is indeed possible.  You'll want to update your route to point to an array with and identify your controller path with the key action:

public function registerCpRoutes()
{
  return array(
    'pluginname/edit' => array('action' => 'pluginName/editItem'),
  );
}

In your plugin controller, you can create any number of variables you need and hand them off to your template as the second argument in the renderTemplate() method:

// PluginNameController.php
public function actionEditItem()
{
    // Create any variables you want available in your template
    $variables['variable'] = craft()->request->getSegment(3);

    $variables['items'] = craft()->pluginName->getAllItems();

    // Load a particular template and with all of the variables you've created
    $this->renderTemplate('pluginname/_edit', $variables);
}

Check out the article How do I setup a route to point to a controller action instead of a template? on Straight Up Craft for a more in depth discussion that also goes into how to render the template from your control, hand off variables to your template, and reference multiple controllers.


Proper order of multi-environment config When using a multi-environment config, Craft will select which settings to use based on a partial string match of the server name.

Given the following example, does "dev.mywebsite.com" need to be first or last? Clearly it contains "mywebsite.com" within it, so the order is relevant.

return array(
    // All environments
    '*' => array(
        'tablePrefix' => 'craft',
    ),
    // Production
    'mywebsite.com' => array(
        'server'   => 'localhost',
        'user'     => 'xxxxxxxx',
        'password' => 'xxxxxxxx',
        'database' => 'prod_mywebsite',
    ),
    // Development
    'dev.mywebsite.com' => array(
        'server'   => 'localhost',
        'user'     => 'xxxxxxxx',
        'password' => 'xxxxxxxx',
        'database' => 'dev_mywebsite',
    ),
);

In my experience, the order does matter. The array is read in order from top to bottom testing the domain for a match against each criteria (key). If there's a match, that array of settings is used and continues down the list which can possibly overwrite variables that were previously set.

For example, in this configuration...

// Development
'dev.mywebsite.com' => array(
    'database' => 'dev_mywebsite',
),
// Production
'mywebsite.com' => array(
    'database' => 'prod_mywebsite',
),

Your development site http://dev.mywebsite.com will be matched twice, once with the full url dev.mywebsite.com and second as a partial match for mywebsite.com. The second match will overwrite the database setting and your dev site won't load properly.

The Craft documentation example uses the domain criteria of '.com' and '.dev' with '.com' coming after '.dev'. That works fine because example.com will never partially match example.dev.

The "Foolproof Multi-Environment Config" from Craft Cookbook works by matching specific server name local.domainname.com or stage.domainname.com first and using a the production environment as a fallback.

Similarly, you could use the config order to your advantage and only change settings as they're needed. The '*' array can be used as a default/fallback environment.

For example:

// Default environment: Production
'*' => array(
    'tablePrefix' => 'craft',
    'server'   => 'localhost',
    'user'     => 'xxxxxxxx',
    'password' => 'xxxxxxxx',
    'database' => 'prod_mywebsite',
),
// Development
'dev.' => array(
    'user'     => 'xxxxxxxx',
    'password' => 'xxxxxxxx',
    'database' => 'dev_mywebsite',
),

Notice this setup doesn't change the server setting since the dev environment also uses localhost.

Also, I've seen setups where the fallback settings are the Development or Local instead of Production. The advantage there is that your Production
site data would be protected from errors in your config file. I've actually made a mistake where the staging server domain didn't match the config file and changes from the staging server were happening to the Production database (bad!) because it was the fallback.

Here's an example from Focus Lab's EE Master Config for ExpressionEngine:
https://github.com/focuslabllc/ee-master-config/blob/master/config/config.env.php

As a side note, the "Foolproof Multi-Environment Config" could probably use the strtolower() function on the 'HTTP_HOST' variable as Bjørn Børresen points out in a commit message in the EE Master Config.

switch (strtolower($_SERVER['HTTP_HOST']))

  HTTP_HOST should be made lowercase as one cannot guarantee that it always is (most browsers do this, but not all) - in which case the wrong config will be loaded


Fuzzy Search and Plurals So I’ve got fuzzy search activated and it works as it should... going to the right; but if I have a plural? Is there no way around this?

Search term: "intaglio" = 12 results … includes "intaglio" and "intaglios”

Search term: "intaglios" = 9 results … only includes “intaglios”

With the current search implementation in Craft, if you have subLeft and subRight set to true for the defaultSearchTermOptions config setting and you search for "intaglios", what Craft does behind the scenes is search for *intaglios*, which would never match intaglio because the wildcard is after the last s.


insertAll() not inserting entries into database I've created a database table that I am trying to add information too with a onBeforeSaveElement() hook.

For some reason, although it is telling me that the rows have been added in the $result, there is no records being added. Interestingly, if I run the page an then manually add a record to the database table, the id has been incremented by the amount of rows $result says it has added.. so it seems that the rows are being added then instantly deleted?

Thanks

SQL

CREATE TABLE `craft_lsclearance` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `path` text,
  `cleared` int(11) DEFAULT '0',
  `dateCreated` datetime DEFAULT NULL,
  `dateUpdated` datetime DEFAULT NULL,
  `uid` char(36) DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=229 DEFAULT CHARSET=latin1;

Function

craft()->on('elements.onBeforeSaveElement', function(Event $event)
    {
        $element = $event->params['element'];
        $paths = craft()->plugin->getPaths($element);

        foreach ($paths as $path) {
            $newPath = preg_replace('/site:/', '', $path['path'], 1);

            if ($path['locale'] != 'en') {
                $newPath = $path['locale'] . '/' . $newPath;
            }

            $model[] = array(UrlHelper::getSiteUrl($newPath));
        }

        $result = craft()->db->createCommand()->insertAll('lsclearance', array('path'), $model);

        print_r($model);
        echo '<br /> Inserted: ' . $result . 'row(s)';

        exit;

    }
);

Output

Array (
  [0] => Array ( [0] => http://mydomain.com/ )
  [1] => Array ( [0] => http://mydomain.com/ )
  [2] => Array ( [0] => http://mydomain.com/results?q=regatta+ac )
  [3] => Array ( [0] => http://mydomain.com/watch/regatta-ac-titanium/19225 )
  [4] => Array ( [0] => http://mydomain.com/mens-watches )
  [5] => Array ( [0] => http://mydomain.com/mens-watches/p2 )
  [6] => Array ( [0] => http://mydomain.com/watch/ac-ii/19282 )
)
Inserted: 7 rows

All of the element saving events (like onBeforeElementSave(), onElementSave(), onBeforeSaveEntry()..., etc. are all wrapped in a database transaction (specifically encapsulated in the CDbTransaction class).

When you call exit, you're killing the request and never giving a chance for the transaction to commit to the database.


Custom navigation URLs? I have an existing ExpressionEngine site which I'm looking at moving to using Craft instead.

Only really had a quick cursory glance at the system so far but a little confused on a few things.

Say I have top level menu with items such as

Home
Services
Company

For instance let's say in the services menu there are some subpages such as :

Service 1
Service 2

and so on and so forth. On the existing site we are using the Structure module which allowed each of those services to have a url such as :

http://www.mysite.com/services/service_1

and so on. That's all fine so far as long as each item in that menu is a service. Now let's say for arguments sake that we want a standard informational page thrown into that menu. From what I see so far with Craft (really hoping I'm wrong on this though) if I create say an information section and then link to that in my navigation then I'm going to end up with something like:

http://www.mysite.com/information/my_information_page

What I'd like to end up with however would be :

http://www.mysite.com/services/my_information_page

This makes more sense to me with the menus and URLs for people to remember.

I know I must be missing something obvious here but just not quite seeing what yet?

The above is just a very literal example but the whole site is like this in that the subsections can contain any kind of content but they still follow a good URL scheme.

Thanks in advance for any help with this.

Best wishes,

Mark

I think you're looking for entry types - when you open a new 'services' entry you could pick from "service" or "service-information" entry types. Each entry type has it's own field layouts.

This would mean your URLs can follow the same structure without writing new routes etc.


How do I set a variable in Twig if a value exists more than once in a loop? How do I loop through entries and set a variable if there are any duplicates in the data I'm checking?

In my case, I need to check if there are more than one entry per year. The variable I'm setting will be used in a conditional later in the template.

This can be accomplished by creating an array of the entry years. Inside the loop that's building the array, a conditional checks if the year already exists in the array... if the year exists, a variable is set to TRUE which you can use in your conditional later down on the page.

{% set files = craft.entries.section("downloads") %}
{% set dropdown = 0 %}
{% set yearArray = [] %}

{% for entry in files %}
    {% set year = entry.fileDate | date('Y') %}
    {% if year in yearArray %}
        {% set dropdown = 1 %}
    {% endif %}
    {% set yearArray = yearArray | merge([year]) %}
{% endfor %}

{% if dropdown %}
    Do this
{% else %}
    Do this
{% endif %}