Tie two select fields together based on category and subcategory? I'm trying to set up a front end form where the user can choose their city and state, which are stored as categories. I have states set up as the parent category and individual cities as sub categories.

I'd like to have the two select fields tied together so that when the state is chosen, the other select field is narrowed down to the cities in that state and vice-versa, e.g. if a user chooses Chicago only the Illinois select field should be available.

Is this possible?

Example:

Choose San Francisco --> California is selected

Choose Colorado -- > Only Colorado cities are available

Here are the two select fields. I know that using .level('1') and .level('2') is the wrong way, but I'm not sure what the correct way would be.

{% set userOptionIds = user.providerArea.limit(null).ids() %}

Parent locationCategories:
{% set parentCategories = craft.categories.group('locationCategories').level('1') %}

    <select id="providerArea" name="fields[providerArea][]">
    {% for category in parentCategories %}
        <option id="locationCategories" name="fields[locationCategories]" {% if category.id in userOptionIds %}selected{% endif %} value="{{ category.id }}">{{ category.title }}</option>
    {% endfor %}
    </select>

Child locationCategories:
{% set childCategories = craft.categories.group('locationCategories').level('2') %}

    <select id="providerArea" name="fields[providerArea][]">
    {% for category in childCategories %}
       <option id="locationCategories" name="fields[locationCategories]" {% if category.id in userOptionIds %}selected{% endif %} value="{{ category.id }}">{{ category.title }}</option>
    {% endfor %}}
    </select>

You would need to implement a front-end solution of some kind using jquery and ajax, that listens to the .on('change') event on the select fields and loads the corresponding data from a template (i.e. '_cityState.html') using the selected city or state as post parameters; and then repopulate the other select field's options dynamically by setting the html of the select field to the return results.

Have a look at jquery's .on('change') event and $.ajax() and .html() methods for more information on the jQuery part.

In your ajax only '_cityState.html' template you would want something like this: (note: this assumes that cities and states have a 'code' field that will represent the drop-down option value, but modify as needed):

{% if craft.request.isAjax %}

    {# retreive the passed city or state code #}
    {% set city = craft.request.getParam('city') %}
    {% set state = craft.request.getParam('state') %}

    {% if city or state %}

        {% if city %}

            {# get all states; set the selected state to parent of 'city' #}
            {% set entries = craft.categories.group('locationCategories').limit(null).level(1) %}
            {% set selected = craft.categories.group('locationCategories).code(city).first().parent %}

        {% else %}

            {# get all cities that are a decendent of 'state'; set selected to first city #}
            {% set entries = craft.categories.group('locationCategories').limit(null).code(state).first().decedents(1) %}
            {% set selected = entries.first() %}

        {% endif %}

        {# build the return html #}
        {% for entry in entries %}
            <option value={{ entry.code }} label="{{ entry.title }}" {% if entry == selected %}selected{% endif %}/>
        {% endfor %}

    {% endif %}

{% endif %}

Not tested. But should be pretty close. If anyone sees any errors, please edit or leave a comment.


Homepage showing up as "index of \" page I am uploading a repo that works fine on a staging server to a live server. When doing so however the homepage is showing as "index of \" page with the public and readme.md directories showing. Like so

When i click on the public link it brings me to  the page, however it has no public assets or css. it is just HTML.

The database is linked properly. their are no errors that are showing as it is on dev mode. What can I do to fix this?

Here is the file structure in my home directory which is proper.

Thank you for your time.

I found the problem, I guess it wasn't craft cms related.

I had to update my apache2 site-enabled config file!


Can plugin copy file into an asset source? I have a plugin and I want it to move some files into a system asset source.

I have a dropdown to select the folder which gets its options like this and shows a dropdown to select a folder Id.

public function getSettingsHtml()
{
      $sourceIds = craft()->assetSources->getAllSourceIds();
      $tree = craft()->assets->getFolderTreeBySourceIds($sourceIds);

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

so in my controller what i want to do is copy a file into that folder?  I have the full path to the file but not sure where to go from there?  Ideally it wouldn't matter if it were local storage or cloud storage it would just move it but I'm a little dumbfounded?

Any thoughts?

UPDATE: So I found this method in the Assets Service insertFileByLocalPath() which seems exactly what i needed

$response = craft()->assets->insertFileByLocalPath($backupsPath, $fileName, $folderId);

which starts to work but i keep getting S3::putObject(): [RequestTimeout] Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.??

As you discovered, the way to go about this is with AssetsService::insertFileByLocalPath().

Not sure about the error you’re getting though. If you haven’t had any luck figuring that out, send your plugin over to support@buildwithcraft.com with instructions on how to reproduce, and we can look into it.


Change which Matrix items displayed I'm supposed to display the most recent 6 items from a Matrix field but provide a slider so that the user can navigate through them (hiding the ones on the left, displaying new ones on the right).

I can think of how to do this in JavaScript, but I'm new to Twig and templating in general. How should I approach this problem?

Updating to show how I would in Javascript using the slick library.

html

js

$('.pictures').slick({
  infinite: true,
  slidesToShow: 6,
  slidesToScroll: 6
});

and how I'm trying to with Twig:

  {% for block in entry.recentWorkExamples %}

         {% set rw_image = block.recentWorkImage.first() %}

   {% endfor %}

The problems is, it doesn't swipe but all of the pictures pile up.

In your Twig example your '.picutures' class name includes a leading dot. Maybe not what you want there.

Also in your slick configuration 'slidesToShow' should be 1 if you only want to show one slide at at time.

And slick is going to operate on the direct descendants of the selector class identified in the configuration so the intervening divs may be causing a problem depending on the associated CSS.


How to check if an Image Transform exists? Is there an elegant way to check if an image transform exists?

{% if craft.imageTransformExists('thumb') %}
  <img src="{{asset.getUrl(thumb)}}" alt="Image">
{% else %}
  This image transform doesn't exists, please create it
{% endif %}

Like Brad says, you'd need to write a custom plugin – specifically, one that provides a Template Variable class exposing the AssetTransformsService's getTransformByHandle method.

The variable class could look something like this:

class MyPluginVariable
{

    public function imageTransformExists($handle)
    {
        // Returns null if transform doesn't exist
        return craft()->assetTransforms->getTransformByHandle($handle);
    }

}

Then, you'd be able to test for named image transforms in your templates like this:

{% if craft.myPlugin.imageTransformExists('thumb') %}
    ...
{% endif %}

If you're not familiar with plugin development, it might be useful to use the Business Logic plugin as a starting point – it even has a Variable class which you could just drop the above imageTransformExists method into.


Is there a way to read IPTC, EXIF or TIFF Metadata in an Image uploaded to Assets? I'm trying to see if I can get the Date Created on images within an image upload. Not the date the file was created, but the date the picture was taken. If I open an image up in Preview, in the Inspector I can see that the IPTC, EXIF, and TIFF Metadata contains the information I'm looking for. Is that possible?

You can create a plugin that does something like this:

$image = new Image();
$image->load('/path/to/image.jpg');
$exif = $image->getExifMetadata();

See here for other methods on the Image class.


Why the entry-Variable doesn't exists after search? I created a search form in my navigation and as it is described in the documentation, I created an search/results.html. And actually it works so far.
Now comes the problem: The results.html extends the "_base.html" and in this file I set the meta properties like this:
{% if entry.seoPageTitle is defined and entry.seoPageTitle != null %}
  {% set metaTitle = entry.seoPageTitle %}
{% else %}
  {% set metaTitle = entry.title %}
{% endif %}

and the HTML:
<title>{{ metaTitle }}</title>

And also this works so far.
But in combination it doesn't work. When I tipe in a Word in my Searchform and click "Search", then it gets "Variable "entry" does not exist". How can I fix it?

Yeh this a common problem I've run into as well.
The issue is the entry variable only gets set when Craft is routing to an entry that's created in the CMS. Since you're routing directly to a template, Craft can't populate the variable.
I can think of a couple workarounds:
If your search form page is defined in the CMS, just grab that (or another page):
{% set entry = craft.entries.section('whatever').slug('search').one() %}

Another option is just to modify your code for fallbacks:
{% if entry is defined %}

    {% if entry.seoPageTitle is defined and entry.seoPageTitle != null %}
      {% set metaTitle = entry.seoPageTitle %}
    {% else %}
      {% set metaTitle = entry.title %}
    {% endif %}

{% else %}

    {# set the fallback from a global for example #}
       {% set metaTitle = defaults.metaTitle %}

{% endif %}

That's a lot of code for basically setting variables. What you could do to help clean this up is to use the null-coalescing operator or default filter in Twig:

{% if entry is defined %}
      {% set metaTitle = entry.seoPageTitle|default(entry.title) %}
{% else %}
      {# set the fallback from a global called "defaults" as an example #}
      {% set metaTitle = defaults.metaTitle %}
{% endif %}


Programmatically update entry field I'm trying to update an entry's field value from a Craft plugin. First I was trying to just update the attributes of the entry like so:

$entry->getContent()->setAttributes(array(
  'metaStatus' => 'Declined'
));

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

But that wouldn't work because getContent doesn't return the data needed in order for the EntryModel to pass validation. The next solution was to loop through each of the fields and grab their field value like so:

$fields = array();

foreach($entry->getFieldLayout()->getFields() as $layout) {
  $handle = $layout->getField()->handle;

  $fields[$handle] = $entry->getFieldValue($handle);
}

$fields['metaStatus'] = 'Declined';

$entry->setContent($fields);

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

Now I'm having issues with matrix/asset fields validating since they come back as empty. Am I going to have to look for matrix/asset fields in my loop above and correctly format the data? There must be an easier way to do this.

I got around this issue by looping through all fields that were of BaseElementFieldType re-setting them to their current value.

$fields = $entry->getFieldLayout()->getFields();

$referentialFields = array();
foreach ($fields as $layoutField) {
  $field = $layoutField->getField();
  if ($field->getFieldType() instanceof BaseElementFieldType) {
    $referentialFields[$field->handle] = $entry->{$field->handle}->ids();
  }
}

$entry->setContentFromPost(array_merge($referentialFields, array(
  'yourField' => 'yourValue'
)));

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


Obtaining SEO metadata for a specific entry under Sprout SEO 3 Under Sprout SEO 2 I am able to obtain the SEO metadata for a specific entry as follows:

{% do craft.sproutSeo.meta({
    id: entry.id,
    title: entry.title,
    description: entry.body|striptags|slice(0, 160)
}) %}
{{ craft.sproutSeo.getOptimizedMeta().basic.title }}
{{ craft.sproutSeo.getOptimizedMeta().basic.description }}

Here I am using the entry's title and body fields as fallback SEO metadata if none is specified on the entry. This is all documented for Sprout SEO 2.

How do I achieve this under Sprout SEO 3? The closest I can get is as follows:

{% do craft.sproutSeo.meta({
    id: entry.id
}) %}
{% sproutseo 'optimize' %}
{{ metadata.meta.search.title }}
{{ metadata.meta.search.description }}

Although this requires disabling automatic metadata rendering, which I require elsewhere, and enabling the custom metadata variable. It also omits the fallback SEO metadata as this now overrides the entry's metadata.

I didn't understand Ben's answer, and I just needed to output an entry's SEO meta title with a fallback to the entry title in a for loop. Maybe this will help someone in a similar situation:

{% for entry in entries %}
    {% if entry.sproutSeoElementMetadata.title %}
        {% set title = entry.sproutSeoElementMetadata.title %}
    {% else %}
        {% set title = entry.title %}
    {% endif %}

{{ title }}

{% endfor %}

Just like entry.sproutSeoElementMetadata.title will grab the meta title of that entry, you can do the same with description: entry.sproutSeoElementMetadata.description

The meta image is a bit more complicated though:

{# get meta image's asset id #}
{% set imageId = entry.sproutSeoElementMetadata.ogImage %}

{# get the actual asset image by its id #}
{% set image = craft.assets.id(imageId).first %}

{# output the image's url #}
{{ image.url }}


Find entries from related section after certain date Does anyone know how to achieve the following in Twig with Craft 3:

My sections
locations and trainings.

I want to find all the entries from locations which has a relation to trainings and a datetime field value from trainings has to be after now.

You’d first get all the relevant entries of your “trainings” section, or better get just their IDs as that’s way better on performance.
Then on another element query you set the relatedTo parameter to only get entries related to these entry IDs.

{% set trainingsEntriesIds = craft.entries({
    section: 'trainings',
    postDate: '>= ' ~ 'now'|date('c'),
}).ids() %}

{% set entries = craft.entries({
    section: 'locations',
    relatedTo: {
        targetElement: trainingsEntriesIds,
        field: 'entriesFieldHandle'
    },
}).all() %}


Multiple checkbox conditional I have a set of checkboxes to show content on a page. I have one instance where I'd like to show something if either checkboxes are selected. I have searched around but couldn't find how to combine multiple value. Here's what I have now

{% if entry.pageOptions.contains('testimonials') %}

And I want it to be something like:

{% if entry.pageOptions.contains('anotheroption|testimonials') %}

That contains test just does a smple string comparison. Try this:

{% if entry.pageOptions.contains('anotheroption')
     or entry.pageOptions.contains('testimonials') %}


Does craft have any build in function for countries like locales? I'm wondering if there's a build in craft function for displaying countries (countryCode and/or name)?

Just like it is for displaying locales:

craft.i18n.getAllLocales()

The closest thing I can think of is to pull the data from the 'territories' part of a craft/app/framework/i18n/data file.

For 'en_us', that would be something like this:

{% set localeData = craft.i18n.getLocaleData('en_us') %}
{% set allTerritories = localeData.getAllTerritories() %}


How to display a specific image from Assets field I have an Assets field with a set of images. What is the best way to find one image that has 'featured' checkbox checked? This is the code I put together to check which of the images is marked 'featured' but I'm not sure how to modify this so to display just the 'featured' image without looping through the entire set. In my use case I need to display a page of entries with featured image for each of them.

{% for image in entry.image %}

   {% if image.featured | length %}(Featured Image){% endif %}
{% endfor %}

I can do something like this and it will display all featured images but will require looping through the entire image set for each entry.

{% for image in entry.image %}
    {% if image.featured | length %}

    {% endif %}
{% endfor %}

Also came up with another solution that goes through the images until it finds a featured image and then stops. If no featured image is found it displays the first one.

{% set break = false %}
{% for image in entry.image if not break %}
    {% if image.featured | length %}

      % set break = true %}
    {% endif %}
{% endfor %}

{% for image in entry.image.limit(1) if not break %}

    {% set break = true %}
{% endfor %}


Display query string SEOmatic I'm using the SEOmatic plugin and would like the query string in my URL to display with the site's title. Is this possible to add with the plugin?

So you can definitely do this via Dynamic Twig Meta


Get the URL of a section? Similar to this question: Output URL to section

Let's say we have a section with handle 'places' which defines its URL format as 'places/{slug}'—how do I get the link to the 'places' index page? The question above goes via the entries in that section but that just gets the url of the first entry in that section.

Here's what I did. You'll need to set sectionId first.

{% set thing = craft.sections.getSectionById(sectionId) %}
{{thing.getUrlFormat()|split('/')[0]}}


Ajax Post to guestEntries/saveEntries from another domain I have a separate Craft install that I am using to communicate to a few different sites. I have a front-end form on another one of these sites (a React.js app) that is looking to post back to this initial Craft install.

My form looks pretty standard:

<form method="post" action="" accept-charset="UTF-8" onSubmit={this.handleSubmit}>
    <input type="hidden" name="action" value="guestEntries/saveEntry"/>
    <input type="hidden" name="sectionId" value="3"/>

    <input id="name" type="text" name="fields[name]" required />
    <input id="userEmail" type="email" name="fields[userEmail]" required/>

    <input type="submit"/>
</form>

handleSubmit() also looks exactly the same as the docs for ContactFormEntries:

handleSubmit(event){
    event.preventDefault();

    var data = $('form').serialize();

    $.post(config.default.apiUrl, data, function(response){
        if(response.success){
            $('body').addClass('success');
        }
        else{
            alert('Sorry, there was an error with your submission. Please validate your submission or try again later.');
        }
    });
}

where config.default.apiUrl = the root url for my Craft app.

My data goes through as a POST request (302 Found) followed by an immediate GET request of '/' (200 OK) where response is the markup from the root of the Craft install.

The kicker is that the data is saved in the database correctly, but the response (and the subsequent GET request) isn't really what I'm looking for. I'd love for it return with a proper json response. I've attempted to include a redirect parameter, but the result is the same.

Is there something I am missing in my ajax request that would throw this off?

TL;DR: Turns out this was an issue with my apache config on the Craft instance.

Explanation: I did a little digging around the GuestEntriesController.php file and noticed that Craft was not picking up that my POST was coming from an ajax request. After looking into that more I manually added the ajax header to my post request:

$.ajax({
    url: config.default.apiUrl,
    type: 'POST',
    data: data,
    headers: {'X-Requested-With': 'XMLHttpRequest'}
}).done(function(){ ... });

This got me a little closer, but now on request I was getting hit with this error:

  Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers in preflight response.

That led me to a simple Google search that had the answer in another Stack Overflow question.

In my Craft App's .htaccess file I added in the following 2 headers:

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "X-Requested-With, Content-Type"

Now my POST request from another server fires fine, and Craft returns with a successful json response.


Non Integer Quantities in Craft Commerce I am making a custom store that can sell half pounds of items. I've noticed through sending calls through my plugin to the craft()->commerce_cart->addToCart($cart, $itemNumber, $qty); to add an item to an order while specifying a non-integer quantity gives me the error: "Qty must be an integer"

Is there any way to allow quantity to be a non-integer? Customers will be ordering in .5 pound increments occasionally. I am not anemic to plugin development and php so I thought I'd ask the community if there was a way around this, or if I'm stuck with Qty being an integer.

Couple other options I've though of:

Make a custom field to deal with qty
Split my items into .5 increments so someone adding 5.5 lbs would really be adding qty 11 and there'd be special things going on with the admin and front-end to hide that fact

Given how much of Commerce internally will be relying on whole number quantities, I think option 2 is the far better plan.

You can't add custom fields to line items currently, and while you might be able to hack something using the options/notes systems, or custom order fields on the order as a whole, I'd think this would get complex to maintain quickly. Best to use your minimum selling measure as the unit and dress it up front end.... that should work well and will be substantially easier to implement and (importantly) maintain.

With stores, even more than websites, long term ease of understanding and maintenance should be a high priority goal.


What is the benefit of using a calendar event plugin or building your own? I am looking at purchasing Venti 2 for events and also what it takes to build my own plugin.  Does anyone know what kind of time it takes to build a plugin that does reoccurring dates, categories and such.  Would I need to put the entries into a separate database table?  Getting back to Venti, has anyone used it and does it have enough custom functionality compared to creating my own?

Depending on your sites' actual event handling needs, building your own plugin similar to Solspace Calendar or Venti to handle this (specifically features like recurrences - the CP UI as well as rendering in front end) could easily take you hundreds of hours to build properly. Unless you need something very different or custom from what either of these plugins offer out of the box, I would imagine the $99 or $49 would pay off real fast if you calculated the time it'd take you, even if you were the most seasoned of developers. :-)

Both Solspace Calendar and Venti offer 30 day satisfaction guarantees, so it might be worth it to consider buying one or both and trying them out to see if they fit your needs. If not, you can always ask for a refund within 30 days, or consider modifying the plugin (rather than completely rebuilding the functionality).


Image Upload & Editing Suddenly not Working I'm getting an error on Image Upload and Asset Editing on an existing site. I'm getting the Upload failed. The error message was: “The file “.../storage/runtime/temp/...” does not appear to be an image.” error that's been discussed previously here. Imagick version 3.4.3 (ImageMagick 6.7.8-9) is installed. Switching the file, the format, or reuploading a previously uploaded image also does not work. Editing old assets w/n Craft yields a generic image icon.

No recent changes to the server that Im aware of. PHP 7.0.33. Craft Pro 3.3.1 ('Im stuck in the upgrade process -- see my other question previously).

Any ideas?

I was able to fix the issue. For some reason Media Temple had switched the PHP implementation from FPM served by NGINX to FPM served by Apache. Switching back to NGINX (or Fast CGI) fixed the issue. I know this issue has come up for others, so a true fix would be great for future reference....


Sort single entries by section with same query order When querying multiple singles by section slugs, is it possible to maintain the order of the results as the query?  Currently the return result is sorted by the default postDate desc.

craft.entries.section(['section1', 'section2', 'section3'])

returns an array with the order
section1, section2, section3

Documentation doesn't seem to answer this question.
https://craftcms.com/docs/templating/craft.entries#order

This is only possible with the id parameter in combination with fixedOrder.

{% set entries = craft.entries.id([307, 287, 488]).fixedOrder(true) %}

You can look up your singles' IDs in the URL of the entries in the CP.

example.com/admin/entries/section1/307-section1/


Upgrading to 3.6 Impossible With PHP 8 Installed After seeing that CraftCMS now supports PHP 8, I went ahead and upgraded my server to PHP 8 (formerly, PHP 7.1 was running).
The problem I am now facing is the following:

PHP 7.1 was too low to upgrade Craft from 3.5 to 3.6.
Craft 3.5 does not support PHP 8, so I cannot use PHP 8 to upgrade to 3.6 either.
I then installed PHP 7.4 alongside PHP 8, as it seems to be the only PHP version that is able to upgrade from 3.5 to 3.6.
Unfortunately, I get the following error whenever I try to run php7.4 craft upgrade all:

root@debian-4gb-nbg1-1:/files# php7.4 craft update all
Fetching available updates ... done
Performing 2 updates:

    - craft 3.5.18 => 3.6.4.1
    - campaign 1.18.1 => 1.18.2

Backup the database? (yes|no) [yes]:
Backing up the database ... done
Performing update with Composer ... done
Applying new migrations ... error: The command "'/files/craft' 'migrate/all' '--no-backup' '--no-content'" failed.

Exit Code: 1(General error)

Working directory: /files

Output:
================

    Craft can’t connect to the database. Check your connection settings.

Yii Migration Tool (based on Yii v2.0.40)

Error Output:
================
PHP Warning:  Module "curl" is already loaded in Unknown on line 0
Exception 'craft\errors\DbConnectException' with message 'Craft CMS requires the PDO_MYSQL driver to operate.'

in /files/vendor/craftcms/cms/src/db/Connection.php:161

Caused by: Exception 'yii\db\Exception' with message 'could not find driver'

in /files/vendor/yiisoft/yii2/db/Connection.php:642

Caused by: Exception 'PDOException' with message 'could not find driver'

in /files/vendor/yiisoft/yii2/db/Connection.php:710

However, pdo_mysql is definitely installed.
Is anyone able to help me?

So probably the best thing you can be doing is doing your updates locally, testing them, and then pushing them to the production environment.
You can read more about it in the Updating Craft CMS without Headaches article.
You still have the opportunity to do this here... get a local dev environment set up, pull down your code and database, then get it up and running on a PHP version such as PHP 7.4 that will work with older versions of Craft as well as Craft 3.6.
Then do the update locally, and deploy the update to your production server.


Homepage in Navigation I've been trying to get the homepage to show up in the navigation...

What I'd like to display is the following...

    About

    Schedule

    Media

    Contact

The following works to create the last 3 pages, but the homepage (about) does not seem to create.

  {% set entries = craft.entries.section('page').find() %}
  {% nav navItem in entries %}

    {{ navItem.title }}

  {% endnav %}

The following works for a semi-dynamic homepage... however the name does not create... (If they change the title of the homepage on the backend, I'd like it to switch on the front-end.

  {{ craft.entries.slug('_home_').entry.title }}

Could anyone with a bit more experience with this give me some insight? I'm confused outta my mind.

I was able to figure out how to get this working...See the following..

{% set entries = craft.entries.section('homepage, page').find() %}
{% nav navItem in entries %}

  {{ navItem.title }}

{% endnav %}


Log Errors from twig template I need to log flash messages from craft.session.getFlashes to a error log file from a particular twig template in Craft 2. Is there a way to do it ?

Create a plugin (https://docs.craftcms.com/v2/plugins/introduction.html,  https://pluginfactory.io/)
Create a Variable class with a log function (https://docs.craftcms.com/v2/plugins/variables.html)
Pass data from getFlashes to your function from within the template like e.g. craft.yourPluginName.yourVariableFunctionName(craft.session.getFlashes)

Tip: Use YourPluginName::log('blabla' . $string); to log anything to /storage/runtime/logs/yourpluginname.log from within your Plugin


Showing entries for light switch So I have three light switches on my blog: 'featured', 'required reading' and 'skills'.

I got the featured section to work with this code:

{% for entry in craft.entries
    .section(['etiquette', 'style','culture','reviews'])
    .where('featured' == 1)
    .orderBy('id desc')
    .limit(4)
    .all()
%}

but when I went to do the reading section I used this code:

{% for entry in craft.entries
    .section([
        'etiquette',
        'style',
        'culture'
    ])
    .where('requiredReading' == 1)
    .orderBy('id desc')
    .limit(12)
    .all()
%}

but i get all the stories with reading and featured switches on. I checked the database and the required reading field is 0. How can I get just the required reading or can you not use more than one lightswitch?

Try rewriting your element query like so...

craft.entries
.section(['etiquette','style','culture'])
.requiredReading(true)
.orderBy('id desc')
.limit(12)
.all()

In Craft you can do queries against fields in this way. The where() isn't necessary for what you're trying to achieve.


Craft commerce and invoices In general what is the best practice to handle invoices from orders made in Craft Commerce ?

You can provide a link to download a PDF version of the invoice.

Set the path to your invoice template in the Commerce General Settings.

Then to provide a frontend link to download the PDF use {{ order.getPdfUrl() }}


Install Craft on VPS with centos 7 and vestacp I downloading craft with Composer, but when i move on example.com/index.php?p=admin nothing happens. In the logs:

PHP Warning:  require_once(): open_basedir restriction in effect. File(/home/User/web/example.com/vendor/autoload.php) is not within the allowed path(s): (/home/User/web/example.com/public_html:/home/User/tmp) in /home/User/web/example.com/public_html/index.php on line 11
PHP Warning:  require_once(/home/User/web/example.com/vendor/autoload.php): failed to open stream: Operation not permitted in /home/User/web/example.com/public_html/index.php on line 11
PHP Fatal error:  require_once(): Failed opening required '/home/User/web/example.com/vendor/autoload.php' (include_path='.:/opt/remi/php70/root/usr/share/pear:/opt/remi/php70/root/usr/share/php:/usr/share/pear:/usr/share/php') in /home/User/web/example.com/public_html/index.php on line 11

I solved this problem. In /home/User/conf/web/example.com.httpd.conf in string

php_admin_value open_basedir/home/User/web/example.com/public_html:/home/Userl/tmp

replace on

php_admin_value open_basedir /home/User/web/example.com:/home/Userl/tmp


Search return wrong result with special character like @#! in Craft 3 I have implemented search in Craft 3, but it returns wrong result when I enter some special characters like @#!. Is there a way to prevent it?

Without knowing more specifics, Craft will "normalize" search keywords before putting them into the search index table in the database, and one of the steps in the normalization process is to remove punctuation, which is why searching for punctuation doesn't work.

You can see the whole normalization process here: https://github.com/craftcms/cms/blob/develop/src/helpers/Search.php#L41-L81


How to loop through a table and set variables based on the existence (or not) of data in those tables? I am using a table field in Craft (price) limited to one row only, used to manage price setting in different currencies on an entry. The three cols each contain number fields with the handles dkk, euro, and usd respectively. (note their column order here can be used to infer a preference later which is useful)

At the template level I am also setting a variable pricepreferredCurrency to specify the currency we want to use for conversions when required. This could be moved to the cp later I guess but it's barcoded at the template level for now.

{% set preferredCurrency = euro %}

If at minimum one price is set I would like to display the price in all three currencies, but I am struggling to create template logic needed to (smartly) support the following functions:

If any price is explicitly set, use that price and do not unset or
recalculate the price based on other prices or currencies. Just take the input at face value.
Otherwise, if a price is empty or unset but at least one other price is set, use the next available price that is set as the price to convert from (using preferredCurrency as the basis for that conversion)
If no prices in any currencies are set, exit and show no prices.

I have already worked out the conversion between currencies, but I am having trouble looping through and making good decisions and logic to handle the different cases. If I can get to the cases I can handle the rest I think.

I feel like this is probably very simple and could even be abstracted I am sure to loop through an expanded set of currencies (without the need to get explicit) but that is beyond me.

I am also open to completely other ways of doing this in craft. Maybe it is better to move currencies into a category group.

I think ultimately I will make decisions about which price to show based on some geolocation, but now I am ready to calculate (where needed) and surface all three. Your help/advice as always much appreciated.

You can see I started to work through this and could immediately tell I was off on the wrong foot. This is really embarrassing to share but...

{% for row in entry.price %}
{% set preferredCurrency = DKK %}

{% set priceDKK = row.dkk %}
{% set priceEURO = row.euro %}
{% set priceUSD = row.usd %}

{% if row.dkk is not empty %}
  {% set priceDKK = row.dkk %}
{% elseif priceEURO is not empty %}
  {% set priceEURO = row.euro %}
  {# abort - it is here I realized this is some dead-end foolery #}

Well. It's not pretty but it works. That improvements could be made on this is both certain and welcome.

{# Check to see if we've for got prices set, Set them if we do... #}

{% set priceDKK = entry.price[0].DKK ? entry.price[0].DKK : false %}
{% set priceEUR = entry.price[0].EUR ? entry.price[0].EUR : false %}
{% set priceUSD = entry.price[0].USD ? entry.price[0].USD : false %}

{# Figure out which price and rate we have to calc with... #}
{# Create our var before the loop (because scope)... #}

{% set basePrice = "" %}

{# We work down the list looking for our first set price #}
{# We will set it as our base price #}
{# We are going to convert all prices to DKK first #}

{% for price in entry.price %}
  {% if priceDKK %}
    {% set basePrice = priceDKK / rateDKK %}
  {% elseif priceEUR %}
    {% set basePrice = priceEUR / rateEUR %}
  {% elseif priceUSD %}
    {% set basePrice = priceUSD / rateUSD %}
  {% endif %}
{% endfor %}

{# Calculate prices if they don't exist... #}
{# We do not calc prices that were manually set... #}

{% for price in entry.price %}
  {% if not priceDKK %}
    {% set priceDKK = basePrice * rateDKK | round(-3,'ceil') %}
  {% endif %}

  {% if not priceEUR %}
    {% set priceEUR = (basePrice * rateEUR) | round(-3,'ceil') %}
  {% endif %}

  {% if not priceUSD %}
    {% set priceUSD = (basePrice * rateUSD) | round(-3,'ceil') %}
  {% endif %}
{% endfor %}

{# end #}


Installation stops with a 500 error I am getting the following error when installing craft .. do you have any clue of what I might be doing wrong or missing out?

The problem was related to the database already having a particular table defined. Was not so easy to find, some type of error would have made my life easier.

Solution: Re-create the database. Re-run the installation process.


Why is it when I update an entry within a structure in the admin, it reorders it to the bottom of the list? I've created a structure and I have about 15 entries in it. They are grouped under an entry named "0234" and each entry below it is named 0234 A, 0234 B, 0234 C, etc. I would like to keep them in this order but if I edit 0234 B and save it, it re-orders it to the bottom.
Is this normal behaviour or am I missing a configuration option somewhere?

It's not intended behaviour when you're editing an existing entry, and it can't be changed - because it's not meant to be doing it. I noticed this on a site last week, but I can't replicate it right now.
I think it'd be worth filing a bug report on https://github.com/craftcms/cms/issues


Entry not saving when date isn't changed using frontend form When editing an entry (actually it's a product within Craft Commerce but everything seems the same) with a pre-existing date, the field looks like this:

<input name="fields[date][date]" value="06/12/2016">

But when re-saving the entry it won't save if I leave the date the same. I'm not getting any errors. If I change the date and re-save the entry, all is fine. Any ideas?

Something I'd not done with the edit form was replace [new1] with [{{variant.id}}] for the variant inputs. As I said, the form was saving correctly anyway if I changed the date, so I didn't expect correcting these to fix the issue I'd been having.

It turns out that correcting these does in fact fix the issue I was having with saving products with an unchanged custom date field. Here is the corrected template code:

{% if variant is defined %}
  <input type="hidden" name="variants[{{ variant.id }}][unlimitedStock]" value="">
  <input type="hidden" name="variants[{{ variant.id }}][minQty]" value="">
  <input type="hidden" name="variants[{{ variant.id }}][maxQty]" value="">
  <input type="text" value="{{ variant.stock }}" name="variants[{{ variant.id }}][stock]">
  <input type="text" value="{{ variant.price | number_format(0) }}" name="variants[{{ variant.id }}][price]">
{% else %}
  <input type="hidden" name="variants[new1][unlimitedStock]" value="">
  <input type="hidden" name="variants[new1][minQty]" value="">
  <input type="hidden" name="variants[new1][maxQty]" value="">
  <input type="text" value="" name="variants[new1][stock]">
  <input type="text" value="" name="variants[new1][price]">
{% endif %}

I'm left wondering whether only having a single default variant allowed still permits new variants to be submitted, which might explain why I wasn't getting any errors? Similarly, I'm not sure why correcting the variant field names fixed the problem I was having with the custom date field not saving when the date wasn't changed.


Faceted navigation plugin problem installing I get the errors below in the Command Prompt when trying to install.

helloswish/craft-faceted-navigation

Does anyone know why this is not working?

  [InvalidArgumentException]
  Could not find a matching version of package helloswish/craft-faceted-navigation. Check the package spelling, your
  version constraint and that the package is available in a stability which matches your minimum-stability (stable).

require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]...

At the time you tried this, that plugin wasn't available on Packagist, hence it's not available to install from the command line (see the tip here in the plugin developer documentation: https://docs.craftcms.com/v3/extend/plugin-store.html#registering-your-plugin).

The plugin author has since added it on Packagist so your command should work now.


How to assign a URL to only one slide in a template using Matrix? So I am using the matrix field to enable uploading of images onto a slideshow for the homepage section. My goal is to have one of the images link to another URL when you click on it. Basic stuff when using plain HTML, but I have it setup like this in Twig:

{% for block in entry.information %}
{% if block.type == "images" %}
   {% set image = block.image.first() %}
       {% if image %}

        {% endif %}
    {% endif %}
{% endfor %}

This code runs fine and my slideshow images are all displayed using just one loop.

The problem is, all my slides are having an anchor field activated. I only want one of the slides to have a link to a URL, however, in the future I may want to add links to other slides so I need something that is also dynamic enough to support this.

My whole entire approach may be wrong and I might need to use a completely different approach... assistance is greatly appreciated.

Try something like:

{% for block in entry.information %}
{% if block.type == "images" %}
    {% set image = block.image.first() %}
        {% if image %}
            {% if block.anchor |length %}

            {% else %}

            {% endif %}
        {% endif %}
    {% endif %}
{% endfor %}

So that only if the block.anchor field has something in it do you generate the link & the image. Otherwise, you just generate the image.


Freeform Mailchimp List - Target using Freeform Pro, set mailchimp key, how do I target a specific list on mailchimp to send and config with please?

Add the mailing list "field" in the form you want by dragging it from the left sidebar to the form.
Click on the field when it's added.
In the right sidebar, change the dropdown field "Mailing Lists" to the list you want.


Custom module controller getView()->renderTemplate() path not working Module loads and controller actions work. Craft 3.3.7.

I'm trying to find out what is the path to use to render the view.

From the Doc:

return $this->renderTemplate('plugin-handle/path/to/template', $variables);

https://docs.craftcms.com/v3/extend/updating-plugins.html#rendering-templates

What I have and doesn't work:

return Craft::$app->getView()->renderTemplate('subscribing-module/src/views/default/index',['mypost' => $request]);

Returned error for above: Unable to find the template “subscribing-module/src/views/default/index”.

Here is the direct path from the root of the website:
modules/subscribingmodule/src/views/default/index.php

Bootstrap in config/app.php:

'bootstrap' => ['subscribing-module'],

The correct path would be

return Craft::$app->getView()->renderTemplate('subscribing-module/templates/default',['mypost' => $request]);

And the file lives in modules/subscribingmodule/src/templates/default/index.twig


Installing Craft 3 without composer So i copied the craft files onto my server. Web directory went into my public folder. I type in my address, I get the Craft service unavailable page, good so far.
I cant get to the installer. http://HOSTNAME/index.php?p=admin takes me to url/admin not found on the server.
What can I look for to see why Im not getting to the install directory.?

Thank you.

Had missed copying over a hidden .env file. Once I copied over to server, install completed. Thanks to all who commented..


"Template not found" in live-preview I am using docker for my local webdev and am new to Craft CMS.

Currently I have a simple setup running, however, my Live-Preview does not seem to work and throws the "Template not found" error.
Yet, when I surf to the same location in my browser all seems dandy and fine.

I'm sure I'm overlooking something simple, but can't seem to find it.

I needed to set the base-url to @web in:

Settings > Websites > "your Website" > base-url

This helps on my local docker-environment. Make sure, that your DEFAULT_SITE_URL="domain.something" is set in your .env file and that you are using the latest 3.2.x version.


Unable to delete plugin In updating a plugin for Craft 3, at one point I also renamed it, and at various times did things in the wrong order. At this point, Craft shows the old, unwanted plugin, but

it is not in composer.json
it is not in the craft_plugins table
none of its tables are in the database
its files have been deleted from craft/vendor/ns/plugin

On the CP Plugins page, if I click Remove, composer updates and says it is successfully removed, but it still shows up under plugins.

How does Craft still know of its existence? Do I need to put it back into composer to do a composer remove?

Although I'm not in favor of the nuclear option in international politics, it works very fine and simply this case. Brad's suggestion of deleting the craft/vendor folder and composer.lock and then running
composer update
solved the problem immediately. Now I appreciate composer.


Get next entry from an entries field I am having the following sections:

1. work (section type: single)
  fields:
    - workList (type: entries from section type workArticles)

2. workArticles (section type: channel)
  fields:
    - workName (type: plain text)

So the first section (work) is an overview page that links to pages of the second section (workArticles). Basically a normal portfolio page.

On my workArticles pages I want to link to the next entry from work.workList. So imagine: my portfolio overview page shows 8 projects, the user clicks on the third entry, the page for the third entry opens, on the bottom of that page I want to link to the fourth entry of the list.

Is that even possible?

There is no inherent order or hierarchy between entries in a Channel section.  You could do some janky things to simulate it, but you're probably much better off converting that workArticles section to a Structured section, which does give its entries both order and hierarchy.

In your case, you might not care about the hierarchy, so you can just keep everything on the first level.

Very similar to https://craftcms.com/support/structure-nav#top-level-only

Then, if you need to, you can take advantage of all of the structure specific properties of EntryModel, (level, parent, nextSibling, etc.)


Craft Commerce - Bad Request The CSRF token could not be verified I've been following the Craft Commerce tutorials at Mijingo, (running on CraftPro 2.6.2983) all good so far, except when I go add a product to my cart. I'm getting a "CSRF token could not be found" error.

The debug message with dev mode on reads:

  Warning: Declaration of Craft\UserSessionService::login($username, $password, $rememberMe = false) should be compatible with CWebUser::login($identity, $duration = 0) in /Users/username/projectname/craft/app/services/UserSessionService.php on line 17

  Warning: Declaration of Craft\UserSessionService::afterLogin() should be compatible with CWebUser::afterLogin($fromCookie) in /Users/username/projectname/craft/app/services/UserSessionService.php on line 17

Is there a step or setup option I'm missing? Atm, it's running on Mamp, but I want to make sure it's workable before I license up and go live.

My bad - sorted it out by adding {{ getCsrfInput() }} - as per https://craftcms.com/support/csrf-protection


Load entries based on category field value while multiple categories are selected I have an activity calendar (more like a weekly schedule) which filters per day and per category. Because there are so many activities I can't just load them all in at once so I use some sort of Ajax pagination kind of way which grabs the category handle.

The problem is, it will always use the first category that has been selected for the event instead of going through them all.

So everything works fine as long as 1 category is selected.

Here is my code:

{% if craft.request.getParam('cat') == '' %}
     {% set selectedCat = 'startingCategory' %}
{% else %}
     {% set selectedCat = craft.request.getParam('cat') %}
{% endif %}
<div class="row activiteitenRow" id="tegelContent">
     {% for category in craft.categories.group('leeftijdsGroepen') %}
          {% set query = {
                 section : 'sportidEvenementen',
                 order : 'evenementDatum desc',
                 limit : null,
                 relatedTo : category,
           } %}
           {% set entries = craft.entries(query) %}
           {% for entry in entries
                  if entry.categories[0]|camel == selectedCat
            %}

            {# activity display code here #}

            {% endfor %}
      {% endfor %}
</div>

I think the problem is in the final forloop:

{% for entry in entries if entry.categories[0]|camel == selectedCat %}

I used .first() Before but it was obvious that would only grab the first category but it needs to use an array but, really can't find a way to fix this.

After looking for some more options I found the solution.

At first I was looking for a solution on how to check if the handle was inside of the array but, didn't manage to output the entire array as a conditional. I ended up changing the filter to filter on the category id (should have done that in the first place).

Now I'm filtering by ID so I could just use:

{% for entry in entries if selectedCat in entry.categories.ids() %}

I got some help from this answer after I figured out to use ID's instead of handle's

Converting array of entries to an array of ids


Redirect to another EntryType template if no Children exist I have a Structure Section called 'products' which uses two EntryTypes, one for 'Product Type Overview' and one for 'Individual Products', where 'Individual Products' are the children of 'Product Type Overview'.  Some 'products' have children and some do not.

I have a menu which then lists the top level entries, so in most cases the user selects 'Product A' they see a list of items (variations) belonging to 'Product A', they then click on an item to see more information about that particular product variation. -  This I have done without a problem using this method.

Here's my problem...  If the user selects a product that DOES NOT have variations (children) I need them to go straight to the child template and miss the parent 'Overview' template.

I hope that makes sense?

Any help very much appreciated.

Fixed!

So bearing in mind i'm using this method to load templates according to entry type, Clive Portman's answer got me thinking about hasDescendants.  In my _entry.html I now have something like this:

{% if entry.hasDescendants() %}
  {% include "products/_types/secType_categoryGroupOverview.html" %}
{% else %}
  {% include "products/_types/secType_productSpecification.html" %}
{% endif %}

...and it seems to work as required :)

Obviously happy to hear of any more elegant solutions.


End user sign in and/or registration template example I'd like to create a template that lets visitors either a) register a new account or b) sign in to an existing account.

There's a sample public registration form here: http://buildwithcraft.com/docs/templating/user-registration-form.

However, I can't find any examples of how to enable a visitor with an existing account to sign into it.

Sorry if I'm missing an obvious link somewhere.

How do you do this? Thanks!

Here is an example of how to do a login form for users with existing accounts: http://buildwithcraft.com/docs/templating/login-form

You can see more form examples in the left hand menu of that page.


Minifying bower_components js files I am currently using spoeken's gulp workflow (https://github.com/spoeken/gulp-craftcms). Everything works well in development, however, when it comes to production, I can't seem to concat/minify my bower_components dependencies, into one file.

I currently have a public/resources/js/app.min.js, however, my bower_component dependencies are sitting in app/resources/bower_components.

How do you go about minifying my dependencies into one file?

Here's what I have in my package.json file:

{
  "name": "craftgulp",
  "version": "0.0.0",
  "authors": [
    ""
  ],
  "license": "MIT",
  "private": true,
  "dependencies": {
    "jquery": "~2.2.0",
    "tether": "^1.4.0",
    "bootstrap": "v4.0.0-alpha.2",
    "gsap": "^1.19.1",
    "scrollmagic": "^2.0.5",
    "slick-carousel": "^1.6.0",
    "waypoints": "^4.0.1"
  },
  "overrides": {
    "bootstrap-sass": {
      "main": [
        "assets/stylesheets/_bootstrap.scss",
        "assets/fonts/bootstrap/*",
        "assets/javascripts/bootstrap.js"
      ]
    },
    "scrollmagic": {
      "main": [
        "./scrollmagic/uncompressed/ScrollMagic.js",
        "./scrollmagic/uncompressed/plugins/animation.gsap.js",
        "./scrollmagic/uncompressed/plugins/debug.addIndicators.js",
        "./scrollmagic/uncompressed/plugins/jquery.ScrollMagic.js"
      ]
    },
    "gsap": {
      "main": [
        "./src/uncompressed/TweenMax.js",
        "./src/uncompressed/TimelineLite.js",
        "./src/uncompressed/jquery.gsap.js"
      ]
    },
    "slick-carousel": {
      "main": [
        "./slick/slick.min.js",
        "./slick/slick.css",
        "./slick/slick-theme.css"
      ]
    },
    "waypoints": {
      "main": [
        "./lib/jquery.waypoints.min.js"
      ]
    }
  }
}

You would need to define them in the Gulp file build uglify task.


Constant exception.Craft\HttpException.404 in logs My log is getting hammered nonstop by the same error about every second (see image). Any idea what's going on here?

Text is:

Level       error
Category    exception.Craft\HttpException.404
Message     Craft\HttpException in /home/folder/public_html/craft/app/controllers/TemplatesController.php:72

You can try installing the Retour plugin to get a log of all of the 404 errors and their referrers, and potentially handle them via redirects.


How do you output images without "?mtime" at the end of the url? For some rss readers they have issues with the images having "mtime" at the end of the url.  Is there a way to output an image without this?

?mtime= should only appear for Assets images that are being served from a source that has Expires settings set (so, either an S3 or Google Cloud source).

This is done done for cache busting purposes. If you upload a new file, it's mtime value will change causing a different URL and, in turn, forcing the user's browser to serve the new image, instead of the cached version.

So to serve images without that you would have to turn off Expires setting for that source.


Help with integrating Snipcart I'm trying to follow this tutorial, but I'm stuck. I create the layout.html file and the index.html and it's still not working. Any ideas?

I'll be the first to admit, I know nothing about snipcart so my answer could be way off.
Since your site works without snipcart, that means you are here:

Once the installation is completed, simply login into the Craft admin and go to Settings. You will have to create the fields that will be required to define a Snipcart product. In the settings, click on Fields and create 2 new fields:

Once you have those fields created, you will need to create a new section - which is a Channel

Name: Store
Handle: store
Section Type: Channel
Uncheck Entries in this section have their own URLs

Once you have that done, and the entry fields created/updated as they suggest, I think where you went wrong is with the actual layout.html file.

You need to include Snipcart’s JavaScript file and jQuery if it is not already included. Use the default CSS theme that you need to include as well.

What that is telling me is to open layout.html that I downloaded with snipcart and copy/paste all of the appropriate javascript into this file:
/path-to-your-project/craft/templates/_layout.html

That's the default layout template that Craft uses. So by pasting all of the necessary javascript & css into that file, each entry will have access to it.
Hope this helps!


How to target Sub-Category of Sub-Category I have 2 Main Groups of Categories, which themselves have Sub-Categories.
The Categories you see have checkboxes to filter entries below.
One Sub-Group has also Children (With Material/ Without Material) ->Screenshot.
I need to collapse these two groups (on click they open/close).
How can i target these Sub-sub-Category Groups?
Right now i use:

{% ifchildren %}
 <ul class="subcategory-list">
   {% children %}
 </ul>
{% endifchildren %}

I tried some with siblings but cannot get it to work. I also need to add extra html fot the arrow to indicate the collapse and to open the sublist.
 If i add it right now, it gets added to all category-groups.
Thanks.

I'm not sure, but I think you might need to use level in your loop.

{% set categories = craft.categories.group('groupHandle').level(3) %}

{% for category in categories %}
    // etc
{% endfor %}

Have a look at the docs:

https://craftcms.com/docs/templating/craft.categories#level


Entry Pagination - Group by Current Category and Order by Post Date I'm new to Craft and am trying to have our entry pagination (next/prev) links go to the next entry within that same category, ordering by date.

So the order part is working as I want it to, it's just remaining in the current category I need help with. Thanks!

Here's what I have so far...

{% set title = entry.title %}
{% set description = entry.body | striptags %}

{% extends "_layout" %}

{% block content %}

{% set entryCategories = entry.newsCategories %}

{% set thisCategory = '' %}

{% for category in entryCategories %}

{% set thisCategory = category.url %}

{% endfor %}

{{ newsHeader.header }}

All
{% nav category in     craft.categories.group('newsCategories') %}{% if category.url == thisCategory %}
{{ category.title }}
{% else %}
{{ category.title }}
{% endif %}{% endnav %}

            All

All

                {% nav category in craft.categories.group('newsCategories') %}

{{ category.title }}

                {% endnav %}

{{ entry.title }}

{{ entry.postDate|date("F j, Y") }} &nbsp;|&nbsp;

              {% if entryCategories | length %}

              {% for category in entryCategories %}

                {{ category.title }}

              {% endfor %}
              {% endif %}

{{ entry.body }}

                {# Set parameters for prev/next elements list #}
                {% set params = {section: 'news', category: category.title, order: 'postDate'} %}

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

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

{% endblock %}

Using the pagination helper might be a cleaner way to achieve your desired results. It would go something like this:

{% paginate craft.entries.section('news').order('postDate').relatedTo({targetElement:categoryId}).limit(5) as entries %}
    {% for item in entries %}
        {# your item code goes here #}
    {% endfor %}
    {% if paginate.prevUrl %}
        Previous
    {% endif %}
    {% if paginate.nextUrl %}
        Next
    {% endif %}
{% endpaginate %}

By using the relatedTo({targetElement:categoryId}) param you will stay in the same category as you paginate entries. This pagination helper handles the urls.  So we are just accessing the paginate.prevUrl & paginate.nextUrl variables provided by the helper.  All you need to do is make sure the categoryId is set to the category you need and set the limit to the number of items to paginate by.

--

For your particular setup I would use two templates.  One for the News landing page and one for the category landing. The news landing page will paginate all News entries.

Once a category is clicked and now uses the template specified in the category setup page, you will limit paginated entries to the current category.  The variable category is reserved for those templates so you can access anything variable associated with the current category you are on. You will only notice slight difference between the two. The Category Template adds the relatedTo({targetElement:category.id}) criteria in addition to setting the active category class.

Here are both templates.

News Landing Template

{% set title = entry.title %}
{% set description = entry.body | striptags %}

{% extends "_layout" %}

{% block content %}

{{ newsHeader.header }}

All

            {% for topic in craft.categories.group('newsCategories') %}

                {{ topic.title }}

            {% endfor %}

            All

All

                {% for topic in craft.categories.group('newsCategories') %}

                    {{ topic.title }}

                {% endfor %}

    {% paginate craft.entries.section('news').order('postDate').limit(5) as entries %}

        {% for item in entries %}

{{ entry.title }}

{{ entry.postDate|date("F j, Y") }} &nbsp;|&nbsp;

                  {% if entryCategories | length %}
                    {% for topic in entryCategories %}
                      {{ topic.title }}
                    {% endfor %}
                  {% endif %}

{{ entry.body }}

        {% endfor %}

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

    {% endpaginate %}

{% endblock %}

Category Template

{% set title = entry.title %}
{% set description = entry.body | striptags %}

{% extends "_layout" %}

{% block content %}

{{ newsHeader.header }}

All

            {% for topic in craft.categories.group('newsCategories') %}

                {{ topic.title }}

            {% endfor %}

            All

All

                {% for topic in craft.categories.group('newsCategories') %}

                    {{ topic.title }}

                {% endfor %}

    {% paginate craft.entries.section('news').order('postDate').relatedTo({targetElement:category.id}).limit(5) as entries %}

        {% for item in entries %}

{{ entry.title }}

{{ entry.postDate|date("F j, Y") }} &nbsp;|&nbsp;

                  {% if entryCategories | length %}
                    {% for topic in entryCategories %}
                      {{ topic.title }}
                    {% endfor %}
                  {% endif %}

{{ entry.body }}

        {% endfor %}

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

    {% endpaginate %}

{% endblock %}


javascript module question Hi hope you can help with this and hoping it's simple. I need to write a javascript module and would like to use the js tag:
{% js %}

        //script here

{% endjs %}

This is good because it only gets embedded when needed and it gets placed at the bottom of the page and I can add craftcms variables to it. The problem I have is that the script is being outputted as
<script>
// script here
</script>

and what I need is
<script type="module">
// script here
</script>

Any idea how I can do that?
Thanks

I'm not sure this is possible, but another approach would be to create a new extendable block in your parent template:
// layout.twig

{% block scripts %}

{% endblock %}

// component.twig

{% extends 'layout.twig' %}

{% block scripts %}
    <script type="module">
        // script here
    </script>
{% endblock %}


Craft constantly logging out from admin area While working in the Craft admin area, on a fresh install, I get logged out every once in a while. It makes the CMS unusable.

I have tried:

Clearing cookies
Resetting the cache in the backend

Sometimes it logs out after 1 pageview, sometimes after 10.

I am using a Macbook Pro, 10.9.3 Mavericks. This happens in both Chrome and Safari (haven't tried Firefox).

I am running Vagrant with: PHP 5.5.12, Ubunutu 14.04, Nginx. Accessing the project locally via http://project.craft.dev

Has anyone experienced something similar? Are there any error logs for this kind of thing?

Update: in the craft.log I get a few errors:

2014/07/11 19:38:56 [trace] [system.db.CDbCommand] Querying SQL: SELECT count(`id`) FROM `craft_tasks` WHERE (level = 0) AND (status = :status). Bound with :status='error'
in /vagrant/dlfm/craft/craft/app/etc/db/DbCommand.php (79)
in /vagrant/dlfm/craft/craft/app/services/TasksService.php (420)
in /vagrant/dlfm/craft/craft/app/controllers/TasksController.php (57)

and

2014/07/11 19:38:55 [error] [php] session_write_close(): Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/vagrant/dlfm/craft/craft/storage/runtime/sessions/) (/vagrant/dlfm/craft/craft/app/services/HttpRequestService.php:852)
Stack trace:
#0 /vagrant/dlfm/craft/craft/app/framework/web/actions/CInlineAction.php(49): Craft\TasksController->actionRunPendingTasks()
#1 /vagrant/dlfm/craft/craft/app/framework/web/CController.php(308): CInlineAction->runWithParams()
#2 /vagrant/dlfm/craft/craft/app/framework/web/CController.php(286): Craft\TasksController->runAction()
#3 /vagrant/dlfm/craft/craft/app/framework/web/CController.php(265): Craft\TasksController->runActionWithFilters()
#4 /vagrant/dlfm/craft/craft/app/framework/web/CWebApplication.php(282): Craft\TasksController->run()
#5 /vagrant/dlfm/craft/craft/app/etc/web/WebApp.php(863): Craft\WebApp->runController()
#6 /vagrant/dlfm/craft/craft/app/etc/web/WebApp.php(252): Craft\WebApp->_processActionRequest()
#7 /vagrant/dlfm/craft/craft/app/framework/base/CApplication.php(180): Craft\WebApp->processRequest()
#8 /vagrant/dlfm/craft/craft/app/index.php(196): Craft\WebApp->run()
#9 /vagrant/dlfm/craft/public/index.php(14): require_once()
REQUEST_URI=/index.php/admin/actions/tasks/runPendingTasks
in /vagrant/dlfm/craft/craft/app/services/HttpRequestService.php (852)
in /vagrant/dlfm/craft/craft/app/controllers/TasksController.php (37)
in /vagrant/dlfm/craft/craft/app/etc/web/WebApp.php (863)

but this one looks most suspect:

24. 2014/07/11 19:41:47 [error] [application] Tried to restore session from a cookie, but the given login name does not match the given uid. UID: 9d515b08-1073-4872-a0e2-155c389a2991 and loginName: Jason.

24. 2014/07/11 19:41:47 [error] [application] Tried to restore session from a cookie, but the given hashed database token value does not appear to belong to the given login name. Hashed db value: 37bb858b4b13b688c4034fe5eb127f75f4931387czozNjoiNDBlNTk0YWQtNGYxZS00NjAxLWEwY2UtZDJlY2NhNDkwMmQ1Ijs= and loginName: Jason.
in /vagrant/dlfm/craft/craft/app/services/UserSessionService.php (797)
in /vagrant/dlfm/craft/craft/app/services/UserSessionService.php (64)
in /vagrant/dlfm/craft/craft/app/etc/web/WebApp.php (592)

My solution is to add:

'overridePhpSessionLocation' => false

Into craft/config/general.php

This tells Craft to use the default location to write sessions, instead of craft/storage/runtime/sessions

For whatever reason, even though  craft/storage/runtime/sessions has CHMOD 777, PHP was saving the session file with 644. When it tried to write to the same file again, it got and error and logged me out.

I also (unsuccessfully) tried giving PHP ownership of the folder, which should in theory fix the problem too.

 sudo chown -R www-data:www-data sessions
 chown: www-data: illegal group name

I am assuming it's a Vagrant/OSX thing.


Update entry Enabled value based on field value when saving I am trying to update the value of the entry enabled field depending on a certain field value.

I am listening to the EVENT_BEFORE_SAVE event and have it updating as such $entry->enabled = $status == 'signedOff' ? true : false;

The issue I have is, if the entry is disabled at the start, then I edit the entry so that the status field to be signedOff, it updates the enabled value to true and it shows the lightbox field as being on, however it does not set a post date, and the entry can't be view on the frontend.

If i then resave the entry, it gets a post date and is visible on the frontend.

It seems that it may not be doing all it needs to when updating the enabled field to  be true on the EVENT_BEFORE_SAVE event.

Is there something I need to call, or another way I need to achieve this for it to be done correctly?

I have other plugins which listen to the EVENT_AFTER_SAVE event, so ideally would like to do it using EVENT_BEFORE_SAVE, so that the entry does not have to be saved.

Looking into this more and understanding how Craft works, a post date needed to be set, it will not do this automatically for you.

On saving the entry, it will check to see if the entry is enabled and has a post date - https://github.com/craftcms/cms/blob/d72c2b0bbddfeced0920f11bfaf4d9891fe62a5a/src/elements/Entry.php#L936

To achieve this I copied how Craft manages the date in the beforeSave function https://github.com/craftcms/cms/blob/develop/src/elements/Entry.php#L1234-L1236

On testing this allowed the entry to be viewed in the frontend and allowed other plugins and modules to do what they needed to with the entry.


Get entries related to same category I have a channel (handle: "infopages") with entries.
I have a channel (handle: "products") with entries.
I have a category (handle: "productTypes")

Every entry from "infopages" has a category field, called "relatedCat". Every entry from "products" has a category field, called "relatedCat".

In the template for the infopages, I want to show all entries from "products" that share the same category. So if an infopage is related to the category "robotmowers", I want to show all the entries from "products" that are also related to "robotmowers". Every entry in every channel can be related to multiple categories, that's where the hard part comes in.

I could loop through the "relatedCat" field and get all the related entries from "products" in a loop. However, if multiple categories are selected, that means I possible get the same product entry more than once. I guess there must be a better way to do this?

If you have infoPage with category A and B lets say.

Then, you have a Product 1 with category A and B also.

If in your template you do (with entry being the current infoPage entry):

{% set relatedProducts = craft.entries.section('products').relatedTo(entry.relatedCat).all() %}

{% for relatedProduct in relatedProducts %}
    {{ relatedProduct.title }} <br>
{% endfor %}

You should not get Product 1 returned to you twice..? Have you actually tried?


If date is between conditional I want to check if it's winter. What is the best way to write a "between dates" conditional?

{% set saison = now|date('m-d') %}
{% if saison (saison >= '11-22' and saison <= '03-21') %}
  Jep it's winter
{% endif %}

This absolutely does not work.
I also tried somthing like:

{% set saison = now|date('m-d') %}
{% if saison > '03-21' %}
  Now it's Spring
{% elseif saison > '06-21' %}
  Now it's Summer
{% elseif saison > '09-23' %}
  Now it's Fall
{% elseif saison > '12-22' %}
  Now it's Winter
{% endif %}

That doesn't work. I guess because twig doesn't recognize the strings as dates?
How do I have to write this?

This conditional should work:

{# Saison Trigger #}
{% set saison = now|date('m-d') %}

{# Check for each Season #}
{% if saison > '11-22' or saison <= '03-21' %}
    {% set currentSaison = 'winter' %}
{% elseif saison > '03-21' and saison <= '06-21' %}
    {% set currentSaison = 'fruehling' %}
{% elseif saison > '06-21' and saison <= '09-22' %}
    {% set currentSaison = 'sommer' %}
{% elseif saison > '09-22' and saison <= '11-22' %}
    {% set currentSaison = 'herbst' %}
{% endif %}

You're right, Twig does just compare the strings. But this is totally fine in this case. A string representing a date and formatted like so "01-13" (don't forget the leading zero) works nicely with the comparison operators.


How do image focal points work in Craft 3? How do image focal points work in Craft 3? I see the dot in the CP, but I'm unable to set the focus.

While we’re piggybacking, here’s a snippet that goes one step further and generates the necessary CSS to position an untransformed image:

{% set img = entry.headingImage.one %}

{% if img %}
    {% set imgStyles = "background-image: url('" ~ img.getUrl() ~ "');"|raw %}

    {% if img.hasFocalPoint %}
        {% set x = img.focalPoint | first * 100 ~ '%' %}
        {% set y = img.focalPoint | last * 100 ~ '%' %}
        {% set imgStyles = imgStyles ~ ' background-position: ' ~ [x, y]|join(' ') %}
    {% endif %}
{% endif %}

Mmm, backgrounds


My plugin settings page is never loaded I have been staring at this all morning, comparing to other plugins online and cannot see why my plugin settings page never registers. I have verified the actual settings model is populated, but settingsHtml() is never called.

Anything obvious I have missed?

class Storefront extends Plugin
{
    public static $plugin;
    public $schemaVersion = '1.0.1';
    public $hasCpSettings = true;
    public function init()
    {
        parent::init();
        self::$plugin = $this;

       $this->setComponents([
        'tracking' => TrackingService::class,
       ]);

        Event::on(
            Plugins::class,
            Plugins::EVENT_AFTER_INSTALL_PLUGIN,
            function (PluginEvent $event) {
                if ($event->plugin === $this) {
                }
            }
        );

        Craft::info(
            Craft::t(
                'storefront',
                '{name} plugin loaded',
                ['name' => $this->name]
            ),
            __METHOD__
        );
    }
    public function hasCpSection()
    {
        return false;
    }
    public function hasSettings()
    {
        return true;
    }
    protected function createSettingsModel()
    {
        return new Settings();
    }

    protected function settingsHtml(): string
    {
        return Craft::$app->view->renderTemplate('storefront/settings',
            [
            'settings' => $this->getSettings()
            ]
        );
    }
}

I overlooked the settings in composer.json to enable a settings page.


Inconsistent search results in multi-site setup (prod/stage) I have set up a staging site alongside production just like the docs describe (Craft Solo 3.4.24). I have copied the "Site Settings" for all sections and set the "Propagation Method" to "Save entries to all sites enabled for this section".
Because Craft had trouble replicating all entries in one queue job I did this manually via CLI using ./craft resave/entries --type=<type> and ./craft resave/entries --section=<section>. This worked beautifully.
But I have an issue with the site search. Locally, when I search e.g. for 'citizen', I get the expected results (count is 60). Same on production. But on stage I get only 2 results back.
When I add .site('*') to the query in Twig I get 120 results back, so the staging site does have all entries.
It appears as if the search index on stage is not complete? Is there anything I can do to refresh it?
Thank you!

The resave/entries command has an optional parameter to rebuild the search indices - may be worth a shot:
./craft resave/entries --update-search-index
https://craftcms.com/docs/3.x/searching.html#rebuilding-your-search-indexes


Trim part of title. Possible bug? I have a title field with value: "Nobel Participatie Portefeuille".

On my template I want to trim the word "Nobel", You can do this with twig trim.

so this is my code:

{{ entry.title | trim('Nobel ') }}

But my output is "Nobel Cashflow Portefeui" (missing "lle")

Other words work normal.

So this would be a better solution:

{{ entry.title|replace('/^Nobel /', '') }}

Thanks for the support Craft!


Autogenerate complex Date / Count based Title I might be pushing the limits of what's possible with Craft's autogenerated title capability, but in any event I'd like to figure out the best path to arrive at my goal.
We have an internal parts inventory with unique IDs assigned in the format YYYYMMDD-NNN. I'd like to recreate that same autogenerated format as we move our data to Craft.
Logically, the concept is "today's date plus the sequential ID incrementing for the parts recorded one that day". Each day, we start back at  '-001' (e.g., '20210305-001', '20210305-002',...).
I've tried the following code in Title Format autogeneration input, but either have the syntax messed up or am pushing beyond the capability of what's possible with autogenerating for a title field in craft.
{# Desired unique ID format = 'YYYYMMDD-NNN' #}
{% if not object.id %}
    {% set uniqueID = craft.entries.section('inventory')
    .order('postDate DESC')
    .first()
    .title %}
    {% set datePortion =  uniqueID | slice(0, 8) %} {# extract YYYYMMDD from string #}
    {% if datePortion == 'now'|date('Y-m-d') %}
        {% set count = uniqueID | slice(9, 3) %}
        {{ datePortion ~ '-' ~ count + 1 }} {# increment today's count for next part #}
    {% else %}
        {{ 'now'|date('Y-m-d') ~ '-001' }} {# first entry in a new day #}
    {% endif %}
{% else %}
    {{ object.title }}
{% endif %}

Hope someone has a more clever and elegant solution - thanks!

Unless I'm missing something, you should be able to use the seq Twig function (docs) and use something like:
{{ 'now'|date('Ymd') ~ '-' ~ seq('now'|date('Ymd'), 3) }}

This would generate the format you need YYYYMMDD-NNN and reset the NNN every new day.
Sequences are stored in your sequences database table.


Missing Position Field Type in Matrix (Craft 3) Anyone know how to get the Position field type to show up in Craft 3 (RC)?

I have a Matrix field, and an Asset/Image field within that, and I'm trying to add a Position field to it.

The "Position" field type is retired in Craft 3.

You can use this: http://plugins.supercooldesign.co.uk/plugin/button-box, or this https://github.com/Rias500/craft3-position-fieldtype instead, or just use plain old dropdowns.


Matrix entries field prevents following Matrix field to display I've setup a Matrix field with an entries field and output the value like this:

{% set entry = block.blockLink.first() %}
 {% if entry %}

Find out more

 {% endif %}

This outputs the value of the entries field OK. However any other Matrix field that follows on from this one fails to display at all. What am I doing wrong? Any advice gratefully received!

My suspicion is that the entry you are defining is confilicting with the entry that Craft gives you as the entry that this template is displaying. Try this, and see if it helps:

{% set blockLinkEntry = block.blockLink.first() %}
{% if blockLinkEntry %}

Find out more

{% endif %}


Is there a way to make conditional input fields in Craft CMS? I'd like to use conditional fields in my CMS instance. I found an old plugin called Reasons. I'm not sure it's compatible with Craft 3, so I'm looking for a workaround.

For what it's worth, it's coming in Craft 4. The author of the Reasons plugin has specifically said he won't port it to Craft 3, as this will be part of core in the next release.


PHP Error - Cannot modify header information - headers already sent Trying to run an import using the Import Plugin but I keep getting errors.
Checking the Logs the error is:

Cannot modify header information - headers already sent (C:\sitepath\craft\app\framework\web\CHttpRequest.php:1490)

The line 1490 is the first setcookie line in the code below:

I have tried downloading the latest version of Craft but has made no difference.

protected function removeCookie($cookie)
{
    if(version_compare(PHP_VERSION,'5.2.0','>='))
        setcookie($cookie->name,'',0,$cookie->path,$cookie->domain,$cookie->secure,$cookie->httpOnly);
    else
        setcookie($cookie->name,'',0,$cookie->path,$cookie->domain,$cookie->secure);
}

Is this a Craft bug or what is the cause of this error?

That error means that something is being output to the browser before it's supposed to be.  In this case, it's cookie information (or more specifically, the removal of a cookie by adding one with the same name, no value and an expired date).

The question is, what's calling removeCookie?  If you enable devMode, then the error should present with a stack trace where you can follow the programs execution to see what's leading up to that call.  My guess is something in the Import plugin (or maybe another plugin) is the culprit.


Reuse chosen dropdown field value on frontend I would like to choose entry values from a frontend dropdown and reuse the chosen value directly.

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

<select name="year">
{% for entry in entries %}
  <option value={{ entry.year }}>{{ entry.year }}</option>
{% endfor %}

How can i get the chosen value and reuse it in the same template?
For example:

{% for entry in entries %}
  {% if entry.year == '2015' %} /* chosen value in dropdown field */
    {{ entry.title }}
  {% endif %}
{% endfor %}

Thanks for any help!

i've got now a working approach to my question above:

{% set field = craft.fields.getFieldByHandle('jahrgang') %}

<form action="tourdefrance.html" method="post">
   <select class="dropdown" name="modelljahr" size="1" onchange="this.form.submit();">
     {% for opt in field.settings.options %}
        <option name="fields[myFieldHandle][]" value="{{ opt.value }}">{{ opt.value }}</option>
     {% endfor %}
   </select>

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

{% set modelljahr = craft.request.getParam('modelljahr') %}

<form action="tourdefrance.html" method="post">
  <select class="dropdown" name="modell" size="1" onchange="this.form.submit();">
    {% for entry in entries %}
      {% if entry.jahrgang == modelljahr %}
        <option value="{{ entry.title }}">{{ entry.title }}</option>
      {% endif %}
    {% endfor %}
  </select>

{{ craft.request.getParam('modell') }}


Simplify templating query with Super Table The following code will return the correct result, but since I'm quite new with Craft, I wanted to know if there was a more preferred/efficient way to filter this in the template (or maybe create a custom function).  Here we go:

{% set currentDay = now|date('l') %}
{% set currentTime = now|date('H:i') %}
{% set entries = craft.categories.group('shows') %}

{% for entry in entries %}
  {% for row in entry.showschedule %}
    {% for day in row.airingDatesEn %}
      {% if day == currentDay|lower %}
        <li>{{ entry.title }}</li>
      {% endif %}
    {% endfor %}
  {% endfor %}
{% endfor %}

As you can see I'm looping over all the entries in the craft category 'shows', then looping over the SuperTable row showschedule, then looping over the checkbox category airingdatesEn to see if it contains the current day.

Seems like I'm doing a lot of barrel rolls here, any ideas on how to simplify?

Thanks!

Someone else will surely better answer the larger question you're asking, but one small point: as you've written it, you're running the filter currentDay|lower many, many times.

Since that will be static for the duration of the loop, even if Craft/twig doesn't have more efficient ways to handle your query, you should do something like {% set currentDayLower = currentDay|lower %} once around line 4 and then compare {% if day == currentDayLower %} in order to avoid re-doing a filter in a loop.


Why two equal variables give false statement? I have two equal variables comparing each other which give me false.
One variable is set out of ancestor entry in structure and another one out of ancestor entry as well.
Both variables are set a bit differently.

{% set parent = craft.entries.ancestorOf(entry).order('lft desc').first() %}

Following variable is set within table field

{% for row in entryId.table %}

    {% set id = row.entryId %}
    {% set rowParent = craft.entries.ancestorOf(id).order('lft desc').first() %}

    {% if rowParent == parent %}
        // Go
    {% endif %}

{% endfor %}

Both variables print the same text with case sensitive letters and spaces.

It seems like you're trying to compare two EntryModel objects. Could you just compare their ids?

{% if rowParent.id == parent.id %}
    // Go
{% endif %}

Also, try using Twig's dump() function to find out exactly what's in those variables/objects.

<pre>
    {{ dump(parent) }}
</pre>

I believe you'll have to have devMode enabled to use dump().


Applying the activation link to linked text in the notification email Whether I use an HTML email template or not, there are a few problems with the activation email link that appears as follows:

http://localhost.dev/actions/users/verifyemail?code=lzxmSVbLE~scHE5KPuL7Rk2oZqJbafyD&id=c3b6d053-2d69-43a8-9b1a-3210df88993c

Links output as URLs in emails unfortunately tend to trigger spam filters. It's best practice to ensure that you're always wrapping links around real text, not the same URL, linked.
More importantly, that link consistently breaks on the /verifyemail... portion (I'm viewing it in the Fastmail web client) and is not clickable. This occurs with the basic notification formatting, and within my HTML email template.

I tried wrapping the link in HTML markup under Email Settings -> Messages, and included autoescape false in my HTML email template (around {{body}}), but I get escaped HTML in the email. (I'm assuming that's because you're using the same message text for multipart HTML & text emails.)

My question is: Is there any way available presently to apply the activation link to linked text rather than outputting it directly in the email?

(This is a subscription site allowing users to self-register, so a non-clickable link in an email is one more hurdle I'd like to avoid.)

Thanks!

Probably this has been fixed since this old question, but just wrapping {{link}} variable in <a> tag lets me send custom HTML notifications with working clickable text links.

Screenshot of messages settings:

Screenshot of custom HTML email received:

Perhaps this won't work for the text version of the email, but if needed I suggest you to use this excellent plugin which let you send custom notifications in both HTML and text versions: Sprout Emails.


Getting related models in templates? I recently started learning plugin development for Craft CMS. I'm creating a plugin that deals with video content. For this example I'll use two models: MyPlugin_ChannelModel and MyPlugin_VideoModel. I've also setup two record classes that mirror the model's properties (MyPlugin_ChannelRecord and MyPlugin_VideoRecord).

The two record classes have a many to many relationship with each other. So, a channel can have multiple videos and a video can be in multiple channels.

I can create, delete and modify records. So far, so good. Now, this next part confuses me. I want to use these records in my templates. Ideally, I'd like to do something like:

{% for video in channel.videos %}
    {{ video.someProperty }}
{% endfor %}

However, I think the "rule" is that only services can interact with records. Models should be used in all other parts of a plugin to pass data (like passing data from a service to a template).

Since models do not have the relationship properties, I've written some getter methods on those models to help me get to my channels/videos from the opposite end of the relationship. An example for MyPlugin_ChannelModel:

public function getVideos()
{
    if ($this->_videos === null && $this->id !== null)
    {
        // Helper method that gets the record for this model
        $channelRecord = craft()->myPlugin_helpers->findRecordForModel($this);

        // Convert each related record back to a model
        $videoModels = array();
        foreach ($channelRecord->videos as $videoRecord) {
            $videoModel = new MyPlugin_VideoModel($videoRecord);
            $videoModels[] = $videoModel;
        }

        $this->_videos = $videoModels;
    }

    return $this->_videos;
}

It's nice that these getter methods automatically map to their corresponding property names ($model->getVideos() becomes model.videos in Twig), but this means I have to write each of these helper methods by hand on all my model classes to get the related models. Am I missing something, is there an easier way?

I would move that logic out of your model class and into a service method.

Your model could still have a thin wrapper for it:

public function getVideos()
{
    return craft()->myPluginVideoService->getVideosByChannelId($this->id);
}

Then your plugin's video service would have:

public function getVideosByChannelId($channelId)
{
    // Can memoize by channelId if this will be called frequently

    $result = craft()->db->createCommand()
        ->select('*')
        ->from('myVideoTable')
        ->where('channelId = :channelId', array(':channelId' => $channelId))
        ->queryAll();

    if ($result)
    {
        return MyPlugin_VideoModel::populateModels($result);
    }
}

The benefits are:

Less cruft in your models, which should (as much as possible) be used as data transport objects
You're not leaking records out of your service layer (like your original model was doing)
You're building a consistent API at your service layer that any other classes (other models, other template variables, other services, other plugins, etc.) can go through.

But essentially you're correct. You'll need to write methods to expose the relationships you're looking for to the templates.

  what is the reason you use the direct database API, in stead of the Record::model()->findAllByPk($pksArray) method?

Habit. Active records, while convenient for simple use-cases, have performance overhead associated with them, so usually I just default to DbCommand.


Updating a matrix row in an existing entry using a frontend form I am trying to update a specific row in a matrix field using a frontend form, I created them using another form with each row following the [new1], [new2] etc. naming format, but I can't seem to get it working using the code below

{% for block in entry.fieldOrderItemsMatrix.all() %}
    <form method="post" accept-charset="UTF-8">
      {{ csrfInput() }}
      {{ actionInput('entries/save-entry') }}
      {{ hiddenInput('sectionId', craft.app.sections.getSectionByHandle('orders').id) }}
      {{ hiddenInput('entryId', entry.id) }}
      <input type="checkbox" name="fields[fieldOrderItemsMatrix][new{{ loop.index }}][fields][fieldOrderDelivered]" {{ delivered ? 'checked' }}>
      Delivered
      <button>Save</button>
    </form>
{% endfor %}

You only use new1, new2 etc for creating new matrix blocks during the submission. For editing existing blocks, you need to use the block's ID in that same place. In your case that would be block.id.


How do I differentiate between entries after merging an array? I'm trying to plot entries from two different Solspace Calendars and an Entries section on one map, and use different markers for all types. Alll of the events are related to the current Entry through a field set on the Event, and a field set on the Entry. I have:

E Waste - Solspace Calendar
Community Events - Solspace Calendar
Disposal Sites - Craft Entry

All three of those above have address fields, and I'll be plotting them on a Smart Map. Currently, I'm doing this:

{# Get all eWaste events related to material #}
{% set eWaste = craft.calendar.events({
calendar: 'eWaste',
    relatedTo: [ 'or', { targetElement: entry.materialCategory }, { targetElement: entry } ],
    rangeStart: now|date('Y-m-d'),
    rangeEnd: now|date_modify('+3 months')
}) %}

{# Get related sites #}
{% set sites = craft.entries.section('sites').relatedTo([ 'or', { targetElement: entry.materialCategory }, { targetElement: entry }]) %}

{# Merge events and entries #}
{% set locations = eWaste|merge( sites ) %}

{{ craft.smartMap.map(locations, options) }}

That gets events from one calendar, and the entries in to a single array so I can plot them on one map, but I can't work out how to differentiate between the types in order to give them different markers. As an example, entry.type doesn't work, and neither does {% if entry.eventField is defined %}

Am I going about this the wrong way? How do I achieve what I'm after? Will the above approach also work for two different calendars + entries?

Not sure if I totally got it but you can check the class of an object via

{% if element is instance of ('craft\\elements\\Entry') %}

or via switch statement

{% switch className(element) %}
    {% case 'craft\\elements\\Entry' %}


Use Craft globals for CSS styles Is it possible to pass a value from Craft global setting into a CSS/SCSS file? So for example I want to be able to allow the end user to customise colours and fonts without having to include it inline.

Has anyone achieved this or similar?

Many thanks

It’s possible to do this by making the entire CSS file a Twig template.

Create a new file in your templates folder, such as templates/css/styles.css, with all the dynamic styles you want.

To prevent the browser from reloading the file on each request, make sure you set cache headers in the template.

{# Tell the browser to cache this page for 30 days #}
{% set expiry = now|date_modify('+30 days') %}

{% header "Cache-Control: max-age=" ~ (expiry.timestamp - now.timestamp) %}
{% header "Pragma: cache" %}
{% header "Expires: " ~ expiry|date('D, d M Y H:i:s', 'GMT') ~ " GMT" %}

Then load the CSS file just like any other from your webpage templates.

<link rel="stylesheet" type="text/css" href="/css/styles.css">


Caching queries not working as expected I think I am not fully understanding how caching works.

Based on this answer I thought I could cache my query and then shuffle the resulting array items.

So I ended up with this code:

{% cache %}
  {% set categoryProducts = craft.entries.section('products').relatedTo(category) %}
{% endcache %}

{% set categoryProducts = shuffle(categoryProducts) %}
{% for entry in categoryProducts %}
  ...
{% endfor %}

But that does give me a "categoryProducts" does not exist error.

Could someone shed some light as to why caching the query doesn't work? I thought caching tags help to reduce the amount of queries being made and I've been studying the documentation, but I don't seem to fully grasp the concept.

The {% cache %} tag only caches the HTML output.  That's it.

In your example above, nothing is cached, and categoryProducts will be an empty set.

One thing you could do is to cache each entry individually:

{% set categoryProducts = craft.entries.section('products').relatedTo(category) %}
{% set categoryProducts = shuffle(categoryProducts) %}

{% for entry in categoryProducts %}
    {% cache globally using key 'product-' ~ entry.id %}
        ...
    {% endcache %}
{% endfor %}


Plugin removed, can't get rid of its data | Control Panel JS I was using the Control Panel JS plugin, and I entered some erroneous JavaScript that caused it to break the control panel. I then deleted its row in the plug-ins table in the database, and all associated files.

However, when I reinstall it, it finds the old JavaScript and things break again. I cannot figure out where this information is stored.

Assuming that you entered your explosive JavaScript directly into the control panel (as opposed to an external file), the answer is... complicated.*

  * Disclaimer: I am not a Project Config expert. This advice has not been thoroughly tested. Please tread carefully when editing your database and/or project.yaml file.

If you're using Project Config, you'll want to update the value stored in your config/project.yaml file. Once you've done that, run ./craft project-config/sync to update your database accordingly.

If you're not using Project Config, then you should be able to just update the database directly. Go to the projectconfig table and change this row...

|  plugins.cp-js.settings.additionalJs  |  (your explosive js)  |

For reference, here's a flowchart to illustrate how the data gets saved...


Uploaded Assets always get 600 File Permission and are therefore not displayed (403 Error) After a fresh manual Craft CMS install everything runs fine, except uploading Assets. Well, the uploading part works fine but uploaded assets always just get file permissions of 600 (adfrw) Group and User are set correctly. Tried it with a fresh install at 2 different hosts still they always get the same permissions.
Does anyone have any idea of what's the problem?

I just missed the "Assets in this volume have public URLs" lightswitch while setting up the volume :)


Top level entry url not rendering category in url I have a structure section called products. The entry url format is

products{category.productCategory.first().slug}/{slug}

However the category never shows in the preview or real world url (real world url gives a 404)

Each product entry only has one category so am puzzled as to why the category doesn't appear in the url.

I imagine it's because you're trying to access productCategory on category instead of the entry. When you're setting entry urls you don't need to use the entry prefix as it automatically expects this to be the case. So try products{productCategory.first().slug}/{slug}


Direct link to asset I'm trying to send some assets links through email, but using {{ siteUrl  ~ file.link }} returns an url with double forwards slashes like: http://example.com//public/assets/sdfsdfds.jpeg

I couldn't find a method on the AssetsFileModel that generates a direct link to it. Is there an easy way to do this?

Thanks!

What if you just use <a href="{{ file.url }}">? So, without the siteUrl part? If your Asset source's URL setting is correct (so, the complete url, preferably using {siteUrl}), that should do the trick ...


What is the correct syntax for using merge to create an array of field values from an element query? I grab some values via an element query:

{% set events = craft.entries.section('events').status('live,pending').order('postDate asc') %}

But I need to put them into an array for use in a Craft-styled form field.

Twig documentation shows how to put text values in an array and merge them:

{% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %}

But how do I do this with the results of my element query?

Just put the field names in parentheses:

{% set options = [] %}
{% for event in events %}
  {% set options = options|merge({ (event.slug):(event.eventTitle) }) %}
{% endfor %}

And now I can output them in the format Craft wants for a form field:

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