Refused to display/connect when previewing entry on multi-site setup I run a multi-site setup with four sites; one on unicorn.com and one on all-your-base.com.

The way Craft CMS licensing works, you can run multiple sites on different domains on the same license/install, but you have to edit all the sites from just one domain - unicorn.com in this case.

When I click "live preview", from unicorn.com all I see is a sad pixelated face and a message saying "all-your-base.com refused to connect."

Console says: "Refused to display 'https://all-your-base.com/...' in a frame because it set X-Frame-Options to 'sameorigin'

X-Frame-Options

The problem is that your site's .htaccess file prevents unicorn.com from viewing all-your-base.com in an iframe because the X-Frame-Options header has not been configured for allowing this.

Securing Craft mentions that you have to add headers for:

Strict-Transport-Security
X-Frame-Options X-XSS-Protection
X-Content-Type-Options
Content-Security-Policy
Public-Key-Pins

So although the following .htaccess-code doesn't add all of these headers, they together make the "refused to connect" go away, by allowing the frame-ancestor to be unicorn.com even when previewing content on all-your-base.com in an iframe (which is how the preview works).

# Unset X-Frame-Options that has been previously set possibly by Craft
Header unset X-Frame-Options

# Allow unicorn.com to request itself in iframes (for live preview)
Header always set X-Frame-Options "ALLOW-FROM https://unicorn.com/"

# Allow unicorn.com to request all-your-base.com in iframes (for live preview)
Header set Content-Security-Policy "frame-ancestors unicorn.com"

These headers are simply strings that are attached to - you guessed it - the header of the HTML-page that is transmitted from the server.

It's up to the  browser how to interpret them. Content-Security-Policy is for instance not supported on IE11.

In this case, Content-Security-Policy is used by Chrome and X-Frame-Options is used by IE11 and others.

Access-Control-Allow-Origin

You might also get error messages in the console because Access-Control-Allow-Origin is missing. Here is an example of setting that header. Note that it doesn't support multiple domains, so here I serve the local development address together with the production site address using the SetEnvIf command:

SetEnvIf Origin "http(s)?://?(unicorn.com|unicorn.local)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header merge Vary Origin
Header set Access-Control-Allow-Credentials "true"


Restrict Permissions Per Entry Rather Than Per Section Working in Craft 3, I have a requirement to restrict user access (in the CP) per Entry rather than per Section.

In this case Users in Craft will belong to Department (Channel) and Resources (Channel). They should only be able to administer the Entries in those Channels for which they are responsible.

I suppose the ideal would be to create a User Group per Department and then be able to select the individual Entries within the Department & Resources Channels here:

Is that possible via a Module/Plugin and if so can anyone provide any pointers on how to get started? I presume I'd need to loop through all Entries in the Channels and output them in this list to be selectable and then have access permissions applied. Any examples or pointers would be appreciated.

Any other suggestions or workarounds also welcome.

Thanks

There is a way - it's actually quite easy but you need to dig deep into Craft 3 to do it. In general: there is only one single Javascript function that fetches all elements for all index pages

updateElements: function() {
    // Ignore if we're not fully initialized yet
    if (!this.initialized) {
        return;
    }

    this.setIndexBusy();

    var params = this.getViewParams();

    Craft.postActionRequest(this.settings.updateElementsAction, params, $.proxy(function(response, textStatus) {
        this.setIndexAvailable();

        if (textStatus === 'success') {
            this._updateView(params, response);
        }
        else {
            Craft.cp.displayError(Craft.t('app', 'An unknown error occurred.'));
        }

    }, this));
},

As you can see: the route is a variable in the defaults

updateElementsAction: 'element-indexes/get-elements'

In order to change this route you'll have to overwrite it in a JS file

Craft.BaseElementIndex.defaults.updateElementsAction = 'your-awesome-plugin-route';

Not let's take a look at the controller: After certain other functions the final call that searches for all elements is the following ElementIndexController line 379

$responseData['html'] = $elementType::indexHtml(
    $this->_elementQuery,  // <-- this is the important part
    $disabledElementIds,
    $this->_viewState,
    $this->_sourceKey,
    $this->_context,
    $includeContainer,
    $showCheckboxes
);

All you have to do is to extend this Controller, build a custom one, link the route to your custom controller and change the $this->_elementQuery. You can include your custom search parameters, include hidden content fields, create custom relation fields for groups and search for elements related to this user group or whatever you want.

It seems a little bit tricky at first but it's actually pretty easy. I've done it several times


How can I get the correct folder ID when uploading assets from a front-end form/plugin? I am able to upload images to an S3 bucket successfully, but I am really stumped as to how it's working.

I have a file input in my template, and in my controller I am doing this:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = craft()->config->get('section', 'mySection');
$criteria->limit = 1;
$criteria->status = null;
$criteria->authorId = craft()->request->getPost('userId');
// have also tried getting the user like this for sanity:
// get the same id either way
//$user = craft()->userSession->getUser();
//$criteria->authorId = $user->id;

$entry = $criteria->first();

if ($entry) {
    ...
    $folder = craft()->assets->findFolder(array(
        'sourceId' => $this->assetSourceId,
    ));
    error_log($folder->id) // 207

    $upload = craft()->assets->insertFileByLocalPath(
        $tempFilePath,
        $photo->getName(),
        $folder->id,
        AssetConflictResolution::KeepBoth
     );
     if ($upload->isSuccess()) {
         ...
     }
}

Everything works great. The image is uploaded to the correct S3 bucket.
But when doing the same in the CP the folder ID is actually different. It looks like it should be 27?

I'm also trying to upload another file my-file.json from the same front-end form. I am using the same technique above to get the folder ID, and it is returning the same number (207). However the asset is getting uploaded to the correct folder (207). But in my case that is actually the incorrect folder. It should be uploading to 27.

Is there a more effective way to get the correct folder Id? Thank you for any suggestions!

FWIW, folder 207 is the first folder in my asset list...

UPDATE

I'm just going to keep updating this as I fumble through it and revise it to an answer when I figure it out.

Using craft()->assets->findFolder() will find the first folder that matches a given criteria.

Since in my above example, I am only passing in the source Id, that means that I'm only going to get back the first folder for that resource. (In my case that would be folder 207).

By changing the findFolder() params to this:

$user = craft()->userSession->getUser();
...
$folder = craft()->assets->findFolder(array(
    'sourceId' => $this->assetSourceId,
    'name' => $user->username
));

I am now getting the correct folder Id back. How I was able to upload images successfully prior to this is still a mystery.

if ($upload->isSuccess()) {
     $fileId = $upload->getDataItem('fileId');
     $asset = craft()->assets->getFileById($fileId);
     $folderId = $asset->getFolder()->getAttribute('id');
     ...
 }


url with .html extension I just realized that my page is also accessible when there is a .html extension on the url.

The url would be:
http://musikwerkluzern.ch/programm

but http://musikwerkluzern.ch/programm.html works as well but some tags are not being rendered with the html extension attached.

And for some reason the google search results link to the page with .html attached.

That's bad. How can I prevent this? Is this a .htaccess thing?

Okay. I found the solution If anyone runs into the same issue.
This must be a server problem which I can't solve so there is a htaccess solution to remove the .html extension:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\.html$ /$1 [L,R=301]


Matrix Field - variable entry does not exist I'm trying to loop a Matrix field that has one block containing 3 items.

{% for block in entry.galeria.type('itemsGaleria') %}
    {% if block.titulo|length %}
        {{ block.titulo.first }}
    {% endif %}
{% endfor %}

But Craft always throws the error variable entry does not exist.

I read the Matrix section from Craft 3 docs but cannot fix this problem.

Any clues?

Thank you @Robin :) but I managed to do it myself.
It works nicely :)

{% set entries = craft.entries.section("galeria").all() %}

{% for entry in entries %}
    {% for block in entry.galeriaMatrix.all() %}
        {% switch block.type %}
            {% case "itemsGaleria" %}
                {% for image in block.fotografia %}
                    <img src="{{image.url}}" alt="{{image.title}}" />
                {% endfor %}

                {{ block.titulo }}
                {{ block.texto }}
            {% default %}
        {% endswitch %}
    {% endfor %}
{% endfor %}


User Profile page - one user can only access one page I'm new to craft, in fact I haven't built a site with it yet, but am looking forward to getting my teeth into this interesting and seemingly very flexible CMS.

I am about to embark on a medium sized website build and have a question that is kind of a deal breaker for choosing the CMS. My question is:

Can I create users that have their own profile page, who can only edit their own page and do nothing else? This won't be a public registration, but administered internally, however they must be able to maintain a profile containing images, text and predetermined layouts that they can choose between.

Thanks guys!

Brad,
Thanks so much for your response.

Regarding the URL - could it be configured to be something like mydomain.com/members/membername instead of passing a url option?

Absolutely.

From the template side you'd do something like this to ensure they're editing their own page:

{# assuming the URL structure for the profile page looks something like http://mydomain.com/users?id=5 %}

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

{% if editUserId and currentUser.id == editUserId %}
    You're in!
{$ else %}
    {% exit 403 %}
{% endif %}

Where currentUser is a global variable that's a UserModel object.


Trouble calling service methods from primary module class I created a module for Craft 3 using pluginfactory.io, and am unable to call services from the primary module class using $this->orders->method() as shown in the documentation.

The services appear to be registered correctly, as I'm able to call the service methods elsewhere in the module using the SiteModule::$instance->orders->method() format.

The line use modules\sitemodule\services\Orders as OrdersService is at the top of the module class, but removing makes no difference in being able to call those service methods from the primary module class.

What is the correct way to call service methods from a primary module class?

Where do you instantiate your orders component?

As long as you're calling the setComponents early in your init method as follow, you should be able to access it anywhere in your plugin class:

...
parent::init();
self::$plugin = $this;
$this->setComponents([
        'orders' => OrdersService::class,
]);
...


Show context of match in search result I'm currently implementing a search function on a website based on craft, and I was wondering if there was a way of actually showing a small snippet of text that shows in what context the search actually matched.

At the moment the result simply returns the corresponding {{entry.title}}, however I would like the result to also display something like [...] a chain of mountains with the highest *peak* being around x-thousand metres [...] when 'peak' was the search term.

Is there any way to achieve this within Craft?
thanks

Yes, there is. There is a plugin called Weighted Search which I have used a few times which does exactly that. https://straightupcraft.com/craft-plugins/weighted-search


When saving an Entry, I receive a JSON response rather than returning to the Entry's edit page Craft Version: 3.0.4
Web Server: Apache + Nginx proxy

Recently I've been working on a couple of Craft 3 projects for deployment on my institution's web servers. In the past, I've successfully launched numerous Craft 2 projects without server-specific issues, save for needing to set the Apache HTTP_ACCEPT header as follows in .htaccess (because it wasn't already configured by httpd.conf):

SetEnv HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

On my initial attempts to deploy Craft 3 to the same servers (and using the same HTTP_ACCEPT configuration in .htaccess), I ran into odd behavior when trying to log in: initially the login process would fail silently (i.e. submitting username/password would process but remain on the login page), but if I were to then immediately refresh the login page a logged-in session would be established and Craft would load the CP.

Once the CP loaded, Craft would throw an error ("An unknown error occurred") and subsequent attempts to navigate through the CP would throw the same error and content such as lists of entries would not appear. Further investigation of network request showed that /actions (POST, GET) requests were failing with 400 Bad Request, with a response of "Request must accept JSON in response".

Initially, before I happened upon actual bad request response details, the folks at Craft thought the issue might pertain to an inconsistent use of HTTPS, or something specific to the use of the Nginx proxy server.

As a test, however, I added application/json to the HTTP_ACCEPT header as follows:

SetEnv HTTP_ACCEPT text/html,application/xhtml+xml,application/xml,application/json;q=0.9,*/*;q=0.8

Low and behold, I was then able to successfully log in and navigate the CP. All of the POST and GET requests to /actions responded successfully.

Now, however, I've ended up in spot where saving an entry successfully commits the change to the database but renders the raw JSON response of the POST request on the page rather than returning to the entry's edit page. Here's an example of what I see in the browser when saving an entry:

{"success":true,"id":15107,"title":"test","slug":"test","cpEditUrl":"https://my.domain.com/operations/entries/generalPages/15107-test","authorUsername":"admin","dateCreated":"2018-05-02T10:08:10-05:00","dateUpdated":"2018-05-02T10:08:10-05:00","postDate":null}

The url in the browser remains on the entry such that if I refresh the page, I'm returned to the entry in the CP and see the updated content.

At this point, I'm stuck and not sure if the HTTP_ACCEPT header is configured incorrectly, or if the Nginx proxy server is indeed interfering with the requests.

Many thanks in advance for thoughts/assistance.

IMO, the issue is with this line.

SetEnv HTTP_ACCEPT text/html,application/xhtml+xml,application/xml,application/json;q=0.9,*/*;q=0.8

With ACCEPT headers set to a fixed value, it's impossible for Craft 3 to distinguish between Json and HTML requests. This must have been a necessity with the nginx proxy setup.

I suggest removing this line from Apache and configure nginx to pass through the original request headers.

If you use proxy_pass, trying adding this line

location / {
    ...
    proxy_pass_request_headers on;
    ...
}

OR, if you prefer a more direct approach

location / {
    ...
    proxy_set_header Accept $http_accept;
    ...
}


Multi-Environment Uploads I am curious how those of you who use a multi-environment development process handle user upload directories? If you use a remote database and one developer uploads some test content, the shared database will think there are files on the other developers machine and they won't be there. This would also show its face if you access a staging server. The database would be out of sync with the upload directory.

Thanks for your input.

We use rsync. This can either be run manually with your CLI or you could set up a cron to run it every so often. It's significantly faster than FTP.


Got 404 on login attempt (Template not found: actions/users/login) Can't login in my staging env since last full plugins/craft update (still able to in my local env).

I already...

Remove my composer.lock and /vendor files and re-install all components
Rename/delete the /web/cpresources directory
Flush all caches throught CLI command
Try to login in Private mode and other browsers

And still, I got this as a response :

{"error":"Template not found: 403.shtml"}

Here's the trace

https://pastebin.com/63NA1XQy

Here's the composer.json

{
    "require": {
        "craftcms/cms": "3.1.31",
        "vlucas/phpdotenv": "^2.4.0",
        "spicyweb/craft-neo": "2.3.5",
        "anubarak/craft-relabel": "1.3.1",
        "ether/seo": "3.5.4",
        "verbb/super-table": "2.1.20",
        "verbb/expanded-singles": "1.0.7",
        "verbb/field-manager": "2.1.0",
        "craftcms/redactor": "2.3.3.2",
        "verbb/navigation": "1.1.12",
        "pennebaker/craft-architect": "2.3.1",
        "marionnewlevant/twig-perversion": "2.1.0",
        "clubstudioltd/craft-inline-svg": "v0.1.1",
        "marionnewlevant/snitch": "3.0.0",
        "selvinortiz/patrol": "3.1.3",
        "fruitstudios/linkit": "1.1.11",
        "craftcms/contact-form": "2.2.5",
        "tomodomo/twig-pluralize-extension": "^2.0",
        "verbb/icon-picker": "^1.0",
        "craftcms/element-api": "^2.5"
    },
    "config": {
        "optimize-autoloader": true,
        "platform": {
            "php": "7.2.10"
        }
    },
    "autoload": {
        "psr-4": {
            "modules\\businesslogic\\": "modules/businesslogic/src/"
        }
      },
    "scripts": {
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ]
    }
}

–

EDIT: Apparently this issue happens on all our Craft sites (both 2 and 3) on our server, will update as soon as I found out more

Never too late to answer (but fixed weeks ago), apparently our host performed an update on the server that activated several new mod_security rules. One of them was affecting the login.


Template variables from active record on NULL value I have a template variable thus

public function twitterAccounts()
{
  return craft()->myPlugin->getAllAccounts();
  //Craft::dd(craft()->myPlugin->getAllAccounts());
}

it calls the following service method

public function getAllAccounts()
{
  return MyPlugin_TwitterAccountRecord::model()->findAll(array('order'=>'oauth_token'));
}

the record is set up like this:

class MyPlugin_TwitterAccountRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'myPlugin_twitter_accounts';
    }

    protected function defineAttributes()
    {
        return array(
            'screen_name' => AttributeType::String,
            'id_str'      => AttributeType::String,
            'password'    => AttributeType::String,
            'oauth_token' => AttributeType::String, // this is a reference to the oauth_tokens table ID, not the actual token.
            'last_tweet'  => AttributeType::DateTime,
            'is_company'  => AttributeType::Bool,

            //array(AttributeType::Enum, 'values' => "alcohol,mixer,other"),
        );
    }
}

I have three records in that table 2 have values for the oauth_token field and one is NULL.

Before the NULL value row the following worked fine in a template...

{% set twitterAccounts = craft.myPlugin.twitterAccounts %}
{% for thisAccount in twitterAccounts %}
  {% if thisAccount.oauth_token is defined and thisAccount.oauth_token is not null %}
    {{ thisAccount.screen_name }} is oauthed
  {% else %}
    {{ thisAccount.screen_name }} is not oauthed
  {% endif %}
{% endfor %}

but now I'm getting (devmode)

Craft\MyPlugin_TwitterAccountRecord and its behaviors do not have a method or closure named "oauth_token".

The annoying thing is when I comment out the return in the template variable method and use the Craft::dd() I see all three rows with their proper attributes, including the null value.

If I change the conditional to

{% if false and thisAccount.oauth_token is defined and thisAccount.oauth_token is not null %}

...in order to always be false and skip into the else, it lists all three accounts in there!

Can someone point me in the right direction here please? Is there a bug in templating with a NULL value in an activerecord collection? (this happens whether or not I have that orderBy oauth_token in the findAll())

Cheers.

You don't want to expose a Record to the template level for many reasons, including inadvertently letting a template call record.save() causing unexpected updates to the database, but you also don't want to expose any unnecessary data to the template (like "password", in your example).  A bit more info on that here.

This is where Models come in. They don't have the database overhead and you can limit what gets exposed to the template as well as combine data from multiple records into one model for easy consumption.

Create a MyPlugin_TwitterAccountModel class like so:

class MyPlugin_TwitterAccountModel extends BaseModel
{
    protected function defineAttributes()
    {
        return array(
            'oauth_token' => AttributeType::String,
            'screenname'  => AttributeType::String,
        );
    }
}

(include any other attributes your template might access)

Then change your getAllAccounts() method in your service to:

public function getAllAccounts()
{
    $records = MyPlugin_TwitterAccountRecord::model()->findAll(array('order'=>'oauth_token'));
    return MyPlugin_TwitterAccountModel::populateModels($records);
}

Now your template will only have access to your plugin's model.

Having typed all of that, I literally have no idea if it will solve your original issue.

It might be some odd bug with Twig's interactions with Records that you're the first to run into since we highly discourage exposing Records to templates.


How to stop rendering errors being output in production Due to a server problem one of our production sites was unable to connect to the DB.

While it was unable to connect the site was outputting the error:

An exception has been thrown during the rendering of a template ("Craft can't connect to the database with the credentials in craft/config/db.php.") in "_layout" at line 10.

I'm a little concerned that a production site is outputting this kind of information, is there a way to stop these errors being output?

(The site is currently running Craft Pro 2.1.2561 with dev mode disabled.)

Thanks.

DB connection errors are the only case where Craft will output the full error even if Dev Mode is disabled, because 99.99% of the time those only occur when Craft is not yet installed, and it’s an extremely common issue people run into when installing Craft.

If you really want to prevent those errors from showing up on the front end, you can edit your index.php file so that it catches them, and displays something different:

<?php

// Path to your craft/ folder
$craftPath = '../craft';

// Do not edit below this line
$path = rtrim($craftPath, '/').'/app/index.php';

if (!is_file($path))
{
    exit('Could not find your craft/ folder. Please ensure that $craftPath is set correctly in '.__FILE__);
}

try
{
    require_once $path;
}
catch (Craft\DbConnectException $e)
{
    exit('An error occurred.');
}


Is it possible to listen for Yii events? I want to listen for Yii events like onBeginRequest and onEndRequest. I've tried the following but nothing happens :(

\Yii::app()->onBeginRequest = create_function('$event', 'die ("YO") ;');

and

craft()->attachEventHandler('onBeginRequest', array($this, 'dieyo'));

and

craft()->getEventHandlers('onBeginRequest')->add(array($this, 'dieyo'));

-

public function dieyo($event)
{
    craftdd($event);
}

Thanks in advance

Yii fires the onBeginRequest event before Craft has had a chance to do any of the plugin bootstrapping / initialization, which is why it's not firing in your plugin.


Unzip zipped file after upload I'd like to know if it is possible to have Craft unzip a .zip file that authenticated users have uploaded through the CP. The idea is that the users will prepare several static html, javascript, css and image files -- rather than having them upload them all individually, I'd like to let them zip them, and upload a single file instead, and then use Craft to unzip them in place them in an appropriate spot. I've got the upload figured out - I'm just not sure how to move ahead with the unzip, or if it's even possible.

I'm 99% sure the answer is yes, that's doable, although you'd need a custom plugin to handle the bulk of it. You'd need to write some PHP to handle the unzip, and place the files where you want them.

You may want to create a plugin with a Controller to handle the file upload & unzip.  You can use Craft's Zip::compress() and Zip::unzip() methods to help. Your form will be able to POST directly to that controller.


Best way to find a deprecation error with no location hint? I'm getting a deprecation error about looping through element queries, but in my markup all my loops are being setup with the .all() method, so I'm struggling to find this.

And it's one that is coming up every time I clear my errors, so the CMS clearly still thinks it's an issue.

If you look in the list of file paths in the stack trace there will be one or more files in the storage/runtime/compiled_templates directory. In this particular case there is storage/runtime/compiled_templates/1a/1a584296a...ccfb9.php under number 33 in the stack trace. (If you're using PhpStorm you can press shift twice and paste in the filename to find it quickly.)

This file contains a PHP class that Twig compiles when the template is first accessed, so that it doesn't need to parse through all the twig code converting it into PHP every time the template is accessed.

At the top of this file is a comment that contains the path of the actual twig template. If needed, you might be able to work out a bit more context by looking at code at the line number in the stack trace (it's the :43 for this one). There will be another comment above this line that will tell you the line number in the twig template.

There may also be some clues in the stack trace itself. For example, here (again number 33 in the stack trace) you have the line:

twig_array_merge([], craft\elements\db\CategoryQuery)

This suggests to me that you're using the |merge filter, on a craft.categories query without calling .all() on it first, which also makes sense given the deprecation notice.


Cache cart globally using key cart.id I want to cache the cart using key cart.id to save some db queries.

{% set cart = craft.commerce.cart %}

{% cache globally using key "cartId=" ~ cart.id ?? "empty" until cart.dateUpdated %}

{% if cart.lineItems | length %}
    {% for item in cart.lineItems %}
        {{ item.sku }},
    {% endfor %}
{% else %}

Cart has no items

{% endif %}

{% endcache %}

Is caching by cart.id sufficient?
Is there any potential risk?
What is {% set cart = craft.commerce.cart %} actually setting when cart.id = null ?

Is caching by cart.id sufficient?

I would not cache the cart, it could always be changing. You would need to create a cache ID based on every aspect of the cart that could change (line items, coupons, addresses, sales, discounts, tax etc). Bottom line, do not cache dynamic things like the cart.

  Is there any potential risk?

See above.

  What is {% set cart = craft.commerce.cart %} actually setting when cart.id = null ?

You are getting an order model returned, it just does not get an ID until the first item is added to the cart and the order/cart is saved to the database.


Check if Category is in Query I'm trying to check to see if a particular category (In a loop) exists in a query submitted from a search form.

At the moment I'm doing this inside a 'childCategory' loop...

{% for childCategory in childCategories %}

    {% for category in query %}{% if childCategory.slug == category.slug %} checked{% endif %}{% endfor %}

{% endfor %}

EUGH right?

Is there a better way (Surely!) to check to see if the cateogry exists? I've tried the length and indexOf filters as well as an IN conditional. Nothing works.

The query is being set like this:

{% set query = craft.request.getParam('category') %}
{% set queryCategories = craft.categories.group('opportunities').slug(query) %}

I would construct an array of the category slugs outside of your childCategories loop (using twig's merge filter:

{% set queryCatSlugs = [] %}
{% for category in query %}
  {% set queryCatSlugs = queryCatSlugs|merge([category.slug]) %}
{% endfor %}

Then your childCategories loop can use twig's containment operator (in) for the test:

{% for childCategory in childCategories %}
  {% if childCategory.slug in queryCatSlugs %}
    ... matches ...
  {% endif %}
{% endfor %}


Pull in images to table rows using craft.assets wilst looping through a table field Thanks in advance to anyone who can help me :)

I have a table field in Craft that my template is pulling in data from... the issue is that in the box at the start of each row I want a different image.

Every time the loop goes round and pulls in the data for each row from the table in Craft I also want it to pull in an image using craft.assets.folderId().

Here is my code so far.. it pulls in the individual images but it repeats the the first row of the table for each image... and then it pulls in all the the images again repeating the next row and so on...
please see this link to get an idea of what I mean: http://code7dev11.co.uk/how-it-works

{% if entry.howItWorksTable | length %}

            {% for row in entry.howItWorksTable %}
                {% for image in craft.assets.folderId('2') %}

                    {{ row.stages }}
                    {{ row.skillsLearnt }}
                    {{ row.classes }}
                    {{ row.price }}

                {% endfor %}
            {% endfor %}

{% endif %}

Here is what I would do:

First, make an actual array of all the images (using find to make the array):

{% set imageArray = craft.assets.folderId('2').find() %}

Then, loop over the entries:

{% for row in entry.howItWorksTable %}

and in that loop each time you display a row, you can get the corresponding image (using the loop variable index0):

{% set image = imageArray[loop.index0] %}

Putting it all together:

{% set imageArray = craft.assets.folderId('2').find() %}

{% if entry.howItWorksTable | length %}

    {% for row in entry.howItWorksTable %}

        {{ row.stages }}
        {{ row.skillsLearnt }}
        {{ row.classes }}
        {{ row.price }}

    {% endfor %}

{% endif %}


Delete Entry from Front End I have this form

    <form method="post" enctype="multipart/form-data">
        {{ csrfInput() }}
        <input type="hidden" name="action" value="entries/save-entry">
        <input type="hidden" name="entryId" value="{{ entry.id }}">

          Listing Title
          <input type="text" name="title" class="form-control" value="{{ entry.title }}">

   </form>

Is it possible to add a delete button to this form or will it have to be within another form targeting a different end point for entries?

I have a single form with a Save button and a Delete button.

The action value is set to entries/save-entry by default. However, when the user clicks the Delete button, the action value is changed to entries/delete-entry via JavaScript.

I'm using Vue.js to accomplish this as follows:

<input type="hidden" name="action" id="action" v-bind:value="'entries/' + actionVal">

Delete button:

<button type="submit" value="delete" v-on:click="actionVal = 'delete-entry'">Delete</button>

In my Vue instance:

new Vue({
    el: '#app',
    delimiters: ['${', '}'],
    data: {
        actionVal: 'save-entry'
    },
});


Using renderTemplate for a plain text twig template Is there a suitable way to use renderTemplate or another method to output a twig template that is plain text?

I'm working with a plugin that has an email template for both HTML and plain text, previewing the HTML version is simple, doing the same with the plain text works but the visual preview within Craft shows the content without any of the spacing or line breaks because its rendering as HTML, viewing the source the correct output with the line breaks and spacing etc is shown. I was wondering if there was a way to output the plain text template in a similar way.

This is isn't an elegant way of doing it, but essentially as per Stephen's suggestion, if you can wrap the plain text output in a pre block, the preview will visually render the spacing and line breaks in HTML mode in the same way it would be received via email.

You could do this in the actionPreview function itself, or alternatively in the template use a condition that would only be true when the email template is being accessed in Craft for this purpose. I chose to use craft.request

{% if craft.request.getLastSegment == 'emailPreviewPlain' %}
    {{ '<pre>' | raw }}
{% endif %}

{# Plain text template content goes here #}

{% if craft.request.getLastSegment == 'emailPreviewPlain' %}
    {{ '</pre>' | raw }}
{% endif %}

This then wraps the entire twig template in pre tags when being previewed in Craft, but otherwise should not render when sent as an email.


EVENT_AFTER_SAVE_ELEMENT triggered twice after saving an entry I had this weird issue, not sure if that happens to you guys or not.

I'm trying to catch the 'after entry is saved' event, using the following code in my custom module's init() function (The module's code is generated by pluginfactory.io):

Event::on(
    \craft\services\Elements::class,
    \craft\services\Elements::EVENT_AFTER_SAVE_ELEMENT,
    function (\craft\events\ElementEvent $event) {
        $element = $event->element;
        if($element instanceof \craft\elements\Entry) {
            // Craft::info('Entry saved: ' . $element->title, __METHOD__);
        }
    }
);

It seems to work by testing on saving an entry in the CP, but the weird thing is this event seems to be triggered twice according to the log:

2019-08-16 12:21:52 [-][1][-][info][modules\testmodule\{closure}] Entry saved: Document translation
2019-08-16 12:21:52 [-][1][-][info][modules\testmodule\{closure}] Entry saved: Document translation
2019-08-16 12:21:52 [-][1][-][info][application] $_GET = [
    'p' => 'admin/entries/pages/234-document-translation'
]

I also tried Andrew's answer in this post: https://craftcms.stackexchange.com/a/26815/7136, but same thing happened.

Event::on(
    \craft\elements\Entry::class,
    \craft\elements\Entry::EVENT_AFTER_SAVE,
    function (\craft\events\ModelEvent $event) {
        Craft::debug(
            '\craft\elements\Entry::EVENT_AFTER_SAVE',
            __METHOD__
        );
    }
);

I certainly don't want my custom code to be executed twice. Is this a bug or an expected behavior?

Craft version: 3.2.10

You can find out what the stack trace is (who is calling you) by putting something like this in your anonymous function:

                try {
                    throw new \Exception();
                } catch (\Exception $e) {
                    Craft::debug(
                        'EVENT_AFTER_SAVE_ELEMENT '.$e->getTraceAsString(),
                        __METHOD__
                    );
                }

Then search on EVENT_AFTER_SAVE_ELEMENT in your web.log


Matrix fields in an entry form I'm attempting to get matrix fields to populate in an entry form (using the guest post plugin) and so far just can't seem to crack it. I've tried various examples, but all assume that you're just editing an existing entry, not posting a new one (that a non-cp user would never need to edit).

The code is so wrong there's little point posting it.

I'm sure if someone can point me in the right direction, I'd be able to take it from there.

The matrix field is called traveller, the block is called travellerDetails and the field I want to try is called travellerTitle (a dropdown field).

The trick, as it were, is to define the Matrix field in your form as an array.

First, the Matrix entry field itself:

<input type="hidden" name="fields[traveller][new1][type]" value="travellerDetails">
<input type="hidden" name="fields[traveller][new1][enabled]" value="1">

Then, for each field within the Matrix block:

<input type="text" name="fields[traveller][new1][fields][travellerTitle]" value="">

If you are going to allow for more than one Matrix field in your form, you must increment the block number, e.g. "new1" becomes "new2". You can do that with some JS.


Zero dollar Commerce orders - payment how-to? My commerce site is setup using Stripe Checkout for payment.

It's possible a customer might have a zero-balance cart after discounts are applied to their order.

What are my options to allow the user to checkout without adding their credit card info into Stripe Checkout's overlay?

Stripe, like most payment providers will most likely not allow $0 payments.

In this case, the typical approach is to set up the manual gateway in addition to stripe,  and detect if order.totalPrice is $0, then simply change the paymentMethodId to the manual gateway id as you submit. The manual gateway will happily complete with a $0 total.


How can I restrict access to the Control Panel to a single port? Is it possible to limit access to the Control Panel to a specific HTTP port, which is shielded from public access? We want external visitors to only be able to access the front end site.

First set your web server up so the full website gets loaded from both the external ports (e.g. 80 and 443) as well as your internal port (let's say 9001).

Then open your index.php file, and add this to the top of it:

// Restrict CP access to port 9001
if (
    $_SERVER['SERVER_PORT'] != 9001 &&
    isset($_GET['p']) &&
    preg_match('^\/*admin(\/.*)?$/', $_GET['p'])
) {
    http_response_code(403);
    echo 'You are not authorized to access this area.';
    exit();
}


Is it possible to have Craft send an email on page load? I'm very new to this so any guidance is greatly appreciated. I've written a couple of simple plugins that will send an email to admins on new user save and and on new entry creation. What I need to do now is to send a weekly email with all new entries created in the last 7 days to a particular user group. I figured it'd be pretty efficient to set up an online CRON job to hit a site url, and on load of that page, trigger my email plugin. However, I'm not sure how to trigger my plugin on page load. Any suggestions?

I would think you would want to instead set up a controller action, as URLs route directly to them, rather than use the overhead of a page.

Your cron job could hit the corresponding Controller Action URL (i.e., http://mysite.com/actions/myplugin/entriescreated/weekly), and run your job like that. Note, where you're accessing an action via cron you would have to set it to allow anonymous access to the action.

http://buildwithcraft.com/docs/plugins/controllers


How to recover lost entries Good afternoon. It would appear that i've lost all my entries! I still have the headings, but where I would have entries added (the pages i would edit on my site), i have nothing!

It is all still on my site thank goodness. Is there anyway I can recover this, Or revert back to a previous "backup" point as you would in windows?

Any chance you've got xDebug installed on the server?  If so, sounds like you're running into this: Why are some entry detail views empty in the control panel?

If that's the case, your data isn't missing from the database, so there's no need to revert.  You just need to fix the underlying problem so the edit entry page can display properly.


Snipcart plugin don't recognize the webhook URL I've cloned the GitHub repository but the plugin won't recognize the webhook URL and the order is not placed.

I think it's because your passing "localhost" as your URL, so when the snipcart server tries to get the URL you supplied it's actually tying to fetch it on its own host.

From my experience with these kinda things you need to supply a publicity accessible URL, so if you can find your public ip then maybe try that instead of localhost or upload to a staging server and then test it with a real domain.


Escaping characters in entry.postDate|date {{ entry.postDate|date('jS F Y \at ga') }}This should produce this 5th November 2016 at 4ambut it doesn't seem to recognise the escape and adds 30 instead of at.

You'll actually need to add a double backwards slash in front of each character you want to escape, i.e.

{{ entry.postDate|date('jS F Y \\a\\t ga') }}

See the official Twig docs for more info.


How to return integer in text form Is it possible to return an integer in it's text form?  I searched the Twig documentation and Stack Overflow and did not find a solution.

I could of course just set it up and create it based on the loop index,

{% if loop.index == '1' %}
{% set place = 'one' %}

but that breaks if the user creates entries above whatever number I set up to so it is not a real solution of any value. I am wondering if someone here might have a solution?

The return would be "one" in place of '1', "two" in place of '2', etc.

Thanks for any help!

As far as I know there is no convertNumberToWords type method in twig or php. However, you could create your own. This could be done in several ways: twig macro, custom twig filter, or plugin variable.

Here is an example of a simple twig macro that uses a lookup table:

{% macro numberToWords(integer) %}

    {% digits = {
        0:  'zero',
        1:  'one',
        2:  'two',
        3:  'three',
        4:  'four',
        5:  'five',
        6:  'six',
        7:  'seven',
        8:  'eight',
        9:  'nine',
        10: 'ten',
        11: 'eleven',
        12: 'twelve',
        13: 'thirteen',
        14: 'fourteen',
        15: 'fifteen',
        16: 'sixteen',
        17: 'seventeen',
        18: 'eighteen',
        19: 'nineteen',
    } %}

    {{ digits[integer] }}

{% endmacro %}

{% import _self as macros %}

{% for i in 1..19 %}
    {{ macros.numberToWords(loop.index) }}
{% endfor %}

Creating a custom twig filter or a plugin would of course allow you to use php, which might provide a more robust solution. Doing a quick search I see there are a few examples that people have written, like this one.

If you want to do this via a plugin, you might try using lindseydiloreto's excellent craft-businesslogic as a starting point. Then just paste the php methods (from the example linked above) into the variables file (along with any dependency methods).


Paginate an array with multiple filters I'm looking to paginate and array with multiple filters.

The current url would be: www.example.com/search?filter=shorts&type=leather&brand=sams

Clicking on the next next link returns: www.example.com/search/p2 effectively removing the search params.

Is there a way to paginate the full url without removing the search params?

{% paginate test.limit(4) as entries %}
    {% for entry in entriesOnPage %}

{{ entry.title }}

            {{ entry.body }}

    {% endfor %}

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

    {% if paginate.nextUrl %}
        Next Page
    {% endif %}
{% endpaginate %}

Try using this (completely untested):

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

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

Update:

The above will only work once Craft 2.3 comes out.

In the meantime, it's a bit sketchy, but you can open up craft/app/variables/HttpRequestVariable.php and add this method:

public function getQueryStringWithoutPath()
{
    return craft()->request->getQueryStringWithoutPath();
}

And it should work.  But please be aware that file could be overwritten by a 2.2 build release at any point during an auto-update and your template would break.


Can visitors 'force download' images stored on Amazon S3? I have a large set of images, stored on Amazon S3. I would like my users to be able to download the original version of an image, rather then watching it in the browser. I know I can do this with PHP by setting headers and such, and I already have a plugin + controller waiting to do this, but is there an easy way to access the image data stored in S3, or should I do this directly through the S3 Class?

Headers passed back from S3 have to be set either on object upload or copy. You would have to use S3::copyObject() to copy the object to itself and set the appropriate headers (mentioned here: https://stackoverflow.com/a/25933113/2040791)

Alternatively, you can use the method mentioned in the same thread and force a download using HTML5, but that won't work on all browsers.


How to get data from Matrix field type? I have a channel which is basically a list of articles. Each article is associated with one or more companies. When I click on companies I want to show all  the articles related to the company. I got that working, but now I want to show all the articles which have download types inside of them. So I had created a Matrix field type. I want to search for articles which will return all the articles which have those and then exclude them from other list which won't have any download association.

Is this doable?

Well done for working out how to display "reverse-related" entries!

In order to separate your Articles into two lists i.e.

Articles with downloads
Articles without downloads

... I would do something like the following on your Company single-entry template...

{% set relatedArticles = craft.entries.section('article').relatedTo() %}

{# Related articles which have downloads #}
{% for articleEntry in relatedArticles %}

    {# check for Matrix content #}
    {% if articleEntry.downloads != '' %}

{{ articleEntry.title }}

            {# loop through any matrix items #}
            {% for block in articleEntry.downloads %}

                {# get the first asset into a variable named 'download' #}
                {% set download = block.downloadFile.first() %}

{{ block.downloadName }}

            {% endfor %}

    {% endif %}
{% endfor %}

{# Articles which do not have downloads #}
{% for articleEntry in allArticles %}

    {# check for blank Matrix content #}
    {% if articleEntry.downloads == '' %}

        {# display article titles #}

{{ articleEntry.title }}

    {% endif %}
{% endfor %}

Hope that helps. Let us know how you get on...


How do you only show children of “active” parent using categories? I'm currently using Categories to organize an inventory of products. The hierarchy is straightforward: category, subcategory, and single product entry. The url structure I'm are hoping to use reflects the hierarchy: homepage.com/products/category/subcategory/product

So far, I've been able to display the proper slug for the products section and output a simple nav menu. What I'd like to do now is refine the nav so that it only display the subcategories (children) of the category (parent) of product I'm on. I think I'm looking for something like this but for Categories + Channel section rather than a Structure section.

I have an idea of how to achieve this, but I'm having trouble getting it to work. I think what I'd like to do is pull the url segment, match it to a category, then display its children. I know I'm missing something basic or haven't fully grasped the syntax of Twig. Any help would be appreciated, thank you!

-- edit --

Marion's suggestion was right on:

{% set categories = craft.categories.group('products').level('1') %}

  {% for category in categories %}
    {% set activeCat = (category.slug == craft.request.segment(2)) %}

      {{ category.title }}
      {% if activeCat %}
        {% set subCategories = craft.categories.descendantOf(category) %}

          {% for subCat in subCategories %}

{{subCat.title}}

          {% endfor %}

       {% endif %}

  {% endfor %}

I think you don't need or want the {% nav %} tag. This should work with your current nav:

{% set categories = craft.categories.group('products') %}

  {% for category in categories %}
    {% set thisIsActiveCategory = (category.slug == craft.request.segment(2)) %}

      {{ category.title }}
      {% if thisIsActiveCategory %}
        {# loop over the subcategories #}
        {% set subCategories = craft.categories.descendantOf(category) %}

          {% for subCat in subCategories %}

{{subCat.title}}

          {% endfor %}

      {% endif %}

  {% endfor %}


Javascript console logs in devMode not always showing? Im only recently noticed that sites with devMode turned on add some helpful information about queries and other logs into the console.

See this screenshot:

but im also noticing that some of our sites that also have devMode turned on locally arent showing the console log information?  I cant seem to figure out what else needs to be on to get this output to show up?

related to this is there anyway to add more grouping to the "logs" group to mirror the exiting log folder (ie. give plugins their own group of logs)

Any ideas?

All that's required for the logging and profiling information to show up in the browser's console is that devMode is enabled.

If it's not showing up then, then either you're enabling it in the wrong place in a multi-environment config or you've got a plugin that's explicitly removing the logging routes.


Booking/Reservation system I'm about to start building out a site that requires a booking system to be setup within it. The site owner should be able to set availability (days and times with time-frames) and allow the visitors to book/reserve available services within the available time-frames and make a payment.

Any suggestions on how one might go about this? I know the request is pretty vague, just looking for some broad strokes to help me kick off in the right direction.

As far as 'setting availablity' goes, one could choose to make a 'services' section with a Matrix field that contains a DateTimeField. That way, the client can create a new Matrix Block for each available time span. The {% cache %} tag will help you in speeding this up on the frontend.

For booking, you could:

A: Make a 'bookings' section, with a related content field 'service'. Fill this section with a frontend Entry form, set post and expiry dates to the time span of the visitor's choice. You could now display a list of bookings on the frontend (behind a login), or have your client manage bookings through the CP's content tab.
B: Make a plugin to handle bookings. This would be my preferred approach.

To handle payments, you could use an existing plugin (Charge, for example) or build your own.


Showing a list of selected/related categories to entry I have a suitability category field in my product type that allows the content writer to select which categories this product is associated with.

But my issue is that on the product page I need to show a full list of these 'suitability' categories with a "yes" or "no" next to them indicating which categories are appropriate.

So I am getting the 'suitability' category group:

{% set suitability = craft.categories.group('suitability').all() %}

Then I am simply looping over this list, but I'm unsure how to format my if statement to dictate if this category needs a "yes" or a "no". I know it's probably something to do with the .relatedTo() parameter but I'm not quite sure on the formatting.

{% for category in suitability %}

{{ category.title }}

        {% if product.relatedTo(category) %}Yes{% else %}No{% endif %}

{% endfor %}

Thanks!

Indigo Viking's code will achieve what you want, but if you want a simpler solution (possibly a bit more performant since there's no second for loop), you could do this instead:

{% set suitability = craft.categories.group('suitability').all() %}

{% for category in suitability %}

        {{ category.title }}

        {% if category.id in product.suitability.ids() %}
            Yes
        {% else %}
            No
        {% endif %}

{% endfor %}

The way this works is product.suitability.ids() returns an array of the element ids of the categories selected in your suitability field. Then you just check if the current category's id is in that array.

Fast, simple, and easy!


Display something in the middle of post content My entry content is displayed like this:
{% for block in entry.postContent.all() %}

        {% if block.type == 'text' %}
      {{ block.text }}
    {% elseif block.type == 'image' %}
      {% for image in block.image.all() %}

      {% endfor %}
    {% endif %}

{% endfor %}

I want to display some other things in the middle of this content but have no idea how I can do it. Maybe someone have any thoughts?

There is a loop variable available within your for loop which you might be able to use here (docs). This starts at 1 and increment everytime your loop runs.
Using loop.index, you could do something like this:
{# Fetch your blocks #}
{% set blocks = entry.postContent.all() %}

{# Counts how many blocks you have in total #}
{% set totalBlocks = blocks | length %}

{# Divide your totalBlocks by 2 and round to find the 50% mark in your content #}
{% set halfwayPoint = (totalBlocks / 2) | round %}

{% for block in blocks %}

        {# When you reach halfway, inject #}
        {% if loop.index == halfwayPoint %}
            the content you want to inject
        {% endif %}

        {% if block.type == 'text' %}
            {{ block.text }}
        {% elseif block.type == 'image' %}
            {% for image in block.image.all() %}

            {% endfor %}
        {% endif %}

{% endfor %}


Using a matrix Date/Time field to sort entries I'm trying to use Supersort to sort entries using a date/time field within a matrix, for example 1/6/2019 12:00AM. These dates will always be in the future. I want to sort it so that the entries that are closest to happening are displayed first.

To make it more complicated, entries with multiple dates in the matrix are split apart, so I need to sort each of these according to their date (from https://gocrowsnest.ca/events):

I can't figure out how exactly I'm supposed to do this. What I have:

{% set pageEntries = craft.entries.section('events') | supersort('sortAs', '{dates.type("date").first.startDate.getTimestamp}') %}
{% for event in pageEntries %}
    {% for block in event.dates.all() %}
        {# Event code #}
    {% endfor %}
{% endfor %}

This is sorting each entry by the first matrix date, not by each date.

Matrix handle: dates, block: date, field (date/time): startDate.

So you're not getting your matrix field's value properly. You're going to want to access it the same way you'd output the data:

{{ event.dates.type('date').first().startDate.getTimestamp() }}

So what we're doing here is getting the dates matrix field, selecting the blocks that have a type of date, getting the first of those blocks (should be our only block but .first is still needed), then get the startDate field in that block. During my testing I found you needed something like .getTimestamp() or .atom() to turn the date into a string that SuperSort could understand (not sure why the examples in our docs don't use that, will definitely look into that though).

So something like this should achieve what you're looking for:

{% set pageEntries = craft.entries.section('events') | supersort('sortAs', '{dates.type("date").first.startDate.getTimestamp}') %}
{% for event in pageEntries %}
    {# event data #}
{% endfor %}

Hopefully that helps!

  Fwiw, I work for TopShelfCraft (who made the SuperSort plugin)


Change markup depending on category of an entry I am trying to output a news section, where I want to display the news categories as classes of the article. That works fine. But I also want to change the markup for a certain category out of the group mainly for responsive layout issues as I use Bootstrap as framework. How would I do this?

Here is my code so far:

{% for entry in craft.entries.section('news').limit(5).find() %}
{% set categories = craft.categories.relatedTo(entry) %}

    {% if category.slug == 'media' %} {# ------- I MEAN THIS LINE, slug or ID would be perfect #}

{{ entry.title }}

        {% set info = entry.info|nl2br %}

{{ info|raw }}

        {{ entry.body }}

    {% else %}

{{ entry.title }}

        {% set info = entry.info|nl2br %}

{{ info|raw }}

        {{ entry.body }}

            {% for image in entry.images %}

            {% endfor %}

    {% endif %}
{% endfor %}

You're currently fetching an array of categories even if there's only one it, I think. You either need to loop through the array or try replacing this:

{% set categories = craft.categories.relatedTo(entry) %}

with this:

{% set category = craft.categories.relatedTo(entry).first %}

Alternatively, you could scrap the category conditional altogether and use entry types, with different entry types set up for different layouts (that's my preferred method). You could also try testing for the presence of images as well (because that seems to be the only difference in the markup):

{% if entry.images | length %}
    <aside class="newsimages col-xs-12 col-md-6">
        {% for image in entry.images %}
            <img src="{{ image.getUrl('medium') }}" alt="{{ entry.title }}" class="col-xs-12{% if loop.first %}{% else %} col-sm-6{% endif %}" />
        {% endfor %}
    </aside>
{% endif %}

Or have I misunderstood and it's the conditional you're having problems with?


nginx conf for craft I have php70 with nginx on vestacp. My admin panel work when url example.com/index.php?p=admin/dashboard, but not work example.com/admin/dashboard

My nginx conf:

server {
     listen      ip:80;
     server_name example.com www.example.com;
     root        /home/User/web/example.com/public_html;
     index       index.php index.html index.htm;
     access_log  /var/log/nginx/domains/example.com.log combined;
     access_log  /var/log/nginx/domains/example.com.bytes bytes;
     error_log   /var/log/nginx/domains/example.com.error.log error;

     location / {
        location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ {
            expires     max;
        }

        location ~ [^/]\.php(/|$) {
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            if (!-f $document_root$fastcgi_script_name) {
                return  404;
            }

            fastcgi_pass    127.0.0.1:9002;
            fastcgi_index   index.php;
            include         /etc/nginx/fastcgi_params;
     }
}

location ~* "/\.(htaccess|htpasswd)$" {
    deny    all;
    return  404;
}

location /vstats/ {
    alias   /home/User/web/example.com/stats/;
    include /home/User/conf/web/example.com.auth*;
}

include     /etc/nginx/conf.d/phpmyadmin.inc*;
include     /etc/nginx/conf.d/phppgadmin.inc*;
include     /etc/nginx/conf.d/webmail.inc*;

include     /home/User/conf/web/nginx.example.com.conf*;

}

I added a few new lines to conf:

server {
    listen      ip:80;
    server_name example.com www.example.com;
    root        /home/User/web/example.com/public_html;
    index       index.php index.html index.htm;
    access_log  /var/log/nginx/domains/example.com.log combined;
    access_log  /var/log/nginx/domains/example.com.bytes bytes;
    error_log   /var/log/nginx/domains/example.com.error.log error;

    location / {
        location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ {
            expires     max;
        }

        location / {
            try_files $uri/index.html $uri $uri/ /index.php?$query_string;
        }

        location ~ [^/]\.php(/|$) {
            try_files $uri $uri/ /index.php?$query_string;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            if (!-f $document_root$fastcgi_script_name) {
                return  404;
            }

            fastcgi_pass    127.0.0.1:9002;
            fastcgi_index   index.php;
            include         /etc/nginx/fastcgi_params;
       }
}

location ~* "/\.(htaccess|htpasswd)$" {
    deny    all;
    return  404;
}

location /vstats/ {
    alias   /home/User/web/example.com/stats/;
    include /home/User/conf/web/example.com.auth*;
}


Cache won't bust on new entry I have a problem with a pair of cache tags refusing to bust the cache when a new entry is added.  The cache works well otherwise.
This is the template code I'm using... it's fairly complex as we're using it to filter products in an ecommerce site.

{% cache %}
  {% set products = craft.entries.section('products').relatedTo(entry).find({limit: 1000}) %}
  {% include 'shop/partials/_product_list' with { products : products } %}
{% endcache %}

This is the include.

{% for product in products %}
    {% set cat_classes = '' %}

    {% for cat in product.category %}
        {% set cat_classes = cat_classes ~ ' ' ~ cat.slug %}
    {% endfor %}

    {% for cat in product.collection %}
        {% set cat_classes = cat_classes ~ ' ' ~ cat.slug %}
    {% endfor %}

    {% for range in craft.entries.find({limit: 1000, section: 'priceRange'}) %}
        {% if product.price >= range.priceFrom and product.price <= range.priceTo %}
            {% set cat_classes = cat_classes ~ ' ' ~ range.slug %}
        {% endif %}
    {% endfor %}

    {% for days in craft.entries.find({limit: 1000, section: 'leadInTimes'}) %}
        {% if product.leadInTime >= days.leadInFromDays and product.leadInTime <= days.leadInToDays %}
            {% set cat_classes = cat_classes ~ ' ' ~ days.slug %}
        {% endif %}
    {% endfor %}

    {% if product.canBePurchasedOnline %}
        {% set cat_classes = cat_classes ~ ' buy-online' %}
    {% else %}
        {% set cat_classes = cat_classes ~ ' special-order' %}
    {% endif %}

        <picture>
            ...
        </picture>

{{ product.title }}

            £{{ product.price }}

{% endfor %}

If anyone has any ideas I'd be grateful!

I fixed it - there was a stuck task preventing the stale template cache from being deleted.


Tax validation in Craft Commerce We have a single page checkout where the items are selected and addressed added before going to
a cart summary page showing amount owed and tax due.
Was confused as to why certain codes were not working (checking validation against the service at https://ec.europa.eu/taxation_customs/vies/vatResponse.html) and realised the Craft Commerce submission requires the country ISO code as a prefix, eg a UK submission 166211234 needs to be GB166211234. Similarly dots and spacesm, etc need to be stripped out. Unless these are in the correct format relevant rules (eg do not add VAT if valid VAT tax record is added will not kick in).
So as a means for preparing this data thought could tap into an event to update this value.
The Addresses::EVENT_BEFORE_SAVE_ADDRESS event won't work as the update cart method/action saves the address after adding the line items and relevant calculations are made.
So then looked into the LineItems::EVENT_POPULATE_LINE_ITEM event so can theoretically fetch and update the address associated with the cart/line item being saved (we are only ever adding one item to the cart at a time. If an item already exists in the cart then that is removed first.
The following code looked promising (would ideally like to limit to EU tax zone but for now assume all sales are EU):
    // get line item
    $lineItem = $event->lineItem;
    // get order
    $order = $lineItem->order;
    // get billing address
    $billingAddress = $order->billingAddressId;
    // get billing address by ID
    $address = craftCommerce::getInstance()->getAddresses()->getAddressById($billingAddress);
    // get billing address tax ID
    $taxId = $address->businessTaxId;
    // get billing address country ID
    $countryId = $address->countryId;
    // get country ISO
    $country = craftCommerce::getInstance()->getCountries()->getCountryById($countryId);
    $countryIso = $country->iso;
    // strip non-alphanumeric characters from tax ID
    $cleanTax = preg_replace("/[^A-Za-z0-9 ]/", '', $taxId);
    // specify new, ideal tax ID
    $newTax = $countryIso . $cleanTax;
    // check if tax is specified, is not already optimised if not already changed
    if ($cleanTax !== '' && strpos($cleanTax,$countryIso) === false && $address->businessTaxId != $newTax) $address->businessTaxId = $newTax;
    // save the new address
    craftCommerce::getInstance()->getAddresses()->saveAddress($address, false);

However, whilst this saves the new address it doesn't update the existing one.
Any thoughts? Am I doing about this completely the wrong way.
Would rather than do this in the front-end JS if at all possible.
Cheers,
Cole
Craft 3.4.29.1.
Craft Commerce 3.1.12

Probably best to just update the address business tax ID if it is in the wrong format automatically for them.
Event::on(Order::class, Order::EVENT_BEFORE_SAVE, function(\craft\events\ModelEvent $event) {
    /** @var Order $order */
    $order = $event->sender;
    if ($order->getShippingAddress() && $order->getShippingAddress()->businessTaxId) {
        $newTaxId = "AU".$order->shippingAddress->businessTaxId; // transform this into the right format based on address country (if it is not already)
        $address = $order->shippingAddress;
        $address->businessTaxId = $newTaxId;
        $order->setShippingAddress($address); //  address will save when order saves.
    }
});


Updating a Lightswitch value not working via front-end Everything looks OK but when I submit my form, field doesn't get updated.

When the page reloads, the checkbox is still showing old state.

<label>Some Option</label>
<label><input type="checkbox" name="fields[membersAvailableForCollaborativeProjects]" value="1" {% if user.membersAvailableForCollaborativeProjects %}checked="checked"{% endif %} /> Yes

membersAvailableForCollaborativeProjects is a Lightswitch field type. Treating it the same as a Checkbox.

Any ideas?

My form action is users/saveUser as I'm trying to update a users profile via the front-end.

After taking a look at this it would seem like the lightswitch is always expecting a value to determine the whether the state should be on or off. When you don’t check the input and submit the form, the field doesn’t get sent to the controller, the value isn’t replaced (as there technically is no value) and nothing updates.

You can work around this by providing a hidden input above the checkbox with a constant value of 0, so when the form submits and there is no check in the checkbox a 0 will get sent and will override the 1 if already set. If you check the input then the hidden field will be overwritten when the form submits and will update the field value of 0 to a 1 and will light the switch.

I hope that makes sense.

Here is the complete form I have used with success:

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

 <label>Some Option</label>
 <label>
  <input type="hidden" name="fields[membersAvailableForCollaborativeProjects]" value="0">
  <input type="checkbox" name="fields[membersAvailableForCollaborativeProjects]" value="1" {% if user.membersAvailableForCollaborativeProjects %}checked{% endif %} /> Yes
 </label>
 <input type="submit" value="Save user">
</form>


Custom environment variable to main assets folder I have a multilingual website which has a folder structure such as:

craft
assets
  js
  css
nl
fr

My goal is to link to the assets folder for my js en css files, but as I'm running a multilingual site I have set my config file to:

return array(
    'omitScriptNameInUrls' => true,

    'siteUrl' => array(
        'nl' => 'http://krycer.be/nl/',
        'fr' => 'http://krycer.be/fr/',
    ),
);

Which means that my {{ siteUrl }} is set to krycer.be/nl or krycer.be/fr
But my assets folder is one level higher.
I tried adding this code to the config.php:

'environmentVariables' => array(
    'baseUrl'  => 'http://krycer.be/',
),

and accessing it by using

{{ baseUrl }} or {{ craft.config.environmentVariables.baseUrl }}

But nothing seems to work. How can I create an abolute link to my assets folder?

The easiest way to do this would be to just use root-relative resource URLs:

<link rel="stylesheet" type="text/css" href="/assets/css/styles.css">
<script type="text/javascript src="/assets/js/scripts.js"></script>

The only disadvantage of that is it makes your site slightly less portable – you wouldn’t be able to run it from a subdirectory on a web server without updating all your URLs in the templates.

So a better option would be to define a custom config variable that holds the value of your site’s base URL in craft/config/general.php;

return array(
    'omitScriptNameInUrls' => true,

    'siteUrl' => array(
        'nl' => 'http://krycer.be/nl/',
        'fr' => 'http://krycer.be/fr/',
    ),

    'baseUrl' => 'http://krycer.be/',
),

And then access that config setting from your templates via craft.config:

<link rel="stylesheet" type="text/css" href="{{ craft.config.baseUrl }}assets/css/styles.css">
<script type="text/javascript src="{{ craft.config.baseUrl }}assets/js/scripts.js"></script>

Note that there’s no reason to place that custom config variable within the environmentVariables config setting. The top level of the config array will work just fine for this.


Listing Sub Categories of a Category related to an entry I've been scratching my head over this for awhile and I can't seem to solve it.

What I'm after is the following.

I have various categories with numerous sub categories. I'd like to be able to display the sub categories of a category that is related to an entry which is how my navigation  is set up.

The code at the moment look like this...

{% set entries = craft.entries.find({ section: 'landingPages' }) %}
   {% for entry in entries %}

{{ entry.title }}

           {% set drop = entry.category.find({ }) %}

               {% for category in drop %}

{{ category.title }}

               {% endfor %}

    {% endfor %}

At the moment it's just generating the category which is selected within the entry, i've tried various variations on descendant commands and nothing seems to work.

Any help would be greatly appreciated.

Check out the documentation for craft.categories which should do what you need. In this example, I assume that 'landing pages' are the same as your level 1 categories, in which case you don't need to deal with the entries at all:

{% set categories = craft.categories.group('navCategories') %}

    {# using 'nav' and 'children' will create a recursive loop #}
    {% nav category in categories %}

            {% if category.level == 1 %}
                {{ category.title }}
            {% else %}
                {{ category.title }}
            {% endif %}
            {% ifchildren %}

                        {% children %}

            {% endifchildren %}

    {% endnav %}

If you only want a certain number of levels then you can use a level filter. I.e:

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

Update: If level 1 categories are not the same as your landing pages then you can access the category through entry.category.first, and then category decendants using: {% for category in category.descendants %}...{% endfor %} or {% nav category in category.descendants %}...{% endnav %} using the recursive method above.

    {% set entries = craft.entries.section('landingPages') %}
    {% for entry in entries %}
        {% set category = entry.category.first %}

            {{ entry.title }}

                {# using 'nav' and 'children' will create a recursive loop, if more levels are needed #}
                {% nav category in category.descendants %}

                        {{ category.title }}
                        {% ifchildren %}

                                {% children %}

                        {% endifchildren %}

                {% endnav %}

    {% endfor %}

As a side-note: Keep in mind that you can also define css dynamically within the loop rather than defining it on the elements directly, if you wanted to.

{% set css %}
    a.{{ category.slug }} {
        border-color: {{ category.supplierColour }};
        color:{{ category.supplierColour }};
    }
{% endset %}
{% includeCss css %}


Asset volumes with dynamic subfolder I have an asset volume which uses dynamic subfolders on the field level.

I have an assets volume called 'members'.
In a matrix field on the user profile tab there is an image field that sets its upload directory to be {owner.author.username} within the members volume.
In my filesystem there is a folder called web/images/members/username (where username is the actual username). This folder contains images.

FYI this Craft site is 5 years old and has recently gone through a major upgrade to Craft v3.

I cannot get the assets field to show existing images when trying to edit it in the admin panel.

I have tried various permutations in the dynamic subfolder path, for example:

{owner.username}
{currentUser.username}
{{currentUser.username}}

But in no situation can I get the field to see the images in the subfolder.

There are fields in matrixes and also basic asset fields not in matrixes that have this issue, so I need assistance in how to set up the matrix asset fields and non-matrix asset fields to be able to see the same folder.

Many thanks

For a field outside the Matrix, you can use {username} and for the one in the Matrix, use {owner.username} - see docs

You might need to run Utilities → Asset Indexes.


Live Preview Button missing I upgraded to Craft 2.1 and there is no Preview Button visible.

It must have something to do with the configuration I created for the different sections because when I use the same craft installation with a clean database the preview button is visible.

For the Live Preview button to show up, two criteria must be met:

Your section’s “Entries in this section have their own URLs” setting must be checked
Your section’s “Entry Template” setting must be set to a valid template path
For Craft 3.2+ pro version, a preview target must be specified. This happens automatically, but might be missing due to a bug. Add a preview target with Primary entry page => {url} to fix this.

Make sure both of those are set correctly, and the Live Preview button will show up.


Query entries based on date's year I'm trying to pull all events where the event's year from endDate matches the queried year (yearParam). When I have this, it's pulling in all events before the yearParam:

{% set pastEntries = craft.entries({endDate: '<' ~ yearParam}).section('events').order('endDate desc') %}

However, what I'd really like is to get entries that match the yearParam (so if the yearParam is 2017, then any events from 2017 should be pulled in).

Any and all help is greatly appreciated. Thanks!

Filtering entries by year with pagination example:

{% set query = craft.request.getQuery() %}
{% set selectedYear = query.year is defined ? query.year|abs : FALSE %}

{% paginate craft.entries({
   section: 'news',
   postDate: '>=' ~ selectedYear,
   postDate: '<' ~ (selectedYear + 1)
}).limit(12) as pageInfo, newsEntries %}


How to update a field when a user views a page I am building a site for a client that is effectively a job board with a messaging system.  They want to know who the last person in their office was to view a specific application and to know who/when someone views specific pages/messages.

I am hoping for something like

set x = users.lastvisitor
if currentuser != users.last vistor
insert currentuser & date into users.lastvisitor

Can anyone point me in a direction?  I am new to Craft so don't know how it handles things like this. I am using matrix blocks for the messages, is it possible to add an entry through a query instead of a form?

There's a plugin called Entry Count that updates a counter record every time a visitor loads a page. You might be able to adapt that plugin to check to see if the user is logged in, and if so, record their user ID to the database instead of incrementing the hit counter. The value is displayed on the Craft entry list dashboard, you would just want to adapt that to display the username instead of the ID.


User activation success page localization problem I have a website with Craft CMS 2 and this site has 2 diffrent languages.

There is no problem with sending e-mail for account activation for each language but when a user tries to activate his/her account via e-mail for my second language which is English they see English mail template but link directs them to my first and default language which is Turkish.

Same problem with the "Forgot my password" link. I tried to add array to activateAccountSuccessPath for each local like siteUrl but could not take any solution. Always directs to tr locale after activation from English mail template.

When you register/save the user you need to set their preferredLocale, so Craft adjusts the verification URL sent with the activation email accordingly. Craft uses the primary locale for a user who doesn’t have a preferred locale set.

You can do this using a hidden input field in the registration form, or via a users.beforeSaveUser event listener.

<input type="hidden" name="preferredLocale" value="{{ craft.locale }}">

Once your users have a preferred locale set, your localized activateAccountSuccessPath config should work.


Prettiest way to loop through nested relations? Let’s say i have a location channel, a studio and a team channel. A location contains one or several studios and a studio contains one or several teams.
I can display all the teams in a location by looping through all the studios related to the location and then through all the teams related to the studio.

Is it somehow possible to do that in one loop?  To only loop through all the teams related to the location ( which aren’t related directly to each other)?

location/_entry.html :

{% set studios = craft.entries.section('studio').relatedTo(entry) %}

{% block content %}

Studios in {{entry.title}}

    {% for studio in studios %}

{{studio.title}}

    {% endfor %}

All teams in {{entry.title}}

      {% for studio in studios %}
      {% for team in craft.entries.section('team').relatedTo(studio) %}

{{team.title}}

      {% endfor %}
    {% endfor %}

{% endblock %}

Would this work?

{% set teams = craft.entries.section('team').relatedTo(studios) %}

All teams in {{entry.title}}

{% for team in teams %}

{{team.title}}

{% endfor %}


CDbCommand failed to execute the SQL statement I get an error when trying to load a page occasionally. It appears to have a problem getting the "type" property from a matrix block.

CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]:
General error: 2006 MySQL server has gone away. The SQL statement
executed was: SELECT `id`, `groupId`, `name`, `handle`, `context`,
`instructions`, `translatable`, `type`, `settings`
FROM `craft_fields`
WHERE (handle = :handle) AND (context = :context)
ORDER BY `name`. Bound with :handle='type',
:context='matrixBlockType:6'

A 2006: MySQL server has gone away error generally means that (for any number of reasons) the connection between Craft/PHP and the MySQL server has been dropped.

Common causes are from connection timeouts and misconfigured MySQL settings like max_packet_size.

There are lots of debugging tips in the above link from MySQL's official documentation that would be a good place to start.  Additionally you might want to involve your host and let them know what you're seeing.


Error: "Tried to read the folder contents...but could not" I'm stumped on this one, for the longest time when I clicked the "Clear Caches" button in the CP, everything seemed to be ok. But I recently checked the craft.log after noticing things didn't seem to be clearing out after all. There was this error:

2016-02-16 09:25:11

Level   error
Category    application
Message Tried to read the folder contents of /home/[company-name]/craft/storage/runtime/temp/, but could not.
2016-02-16 09:25:11

Level   error
Category    application
Message Tried to read the folder contents of /home/[company-name]/craft/storage/runtime/assets/, but could not.
2016-02-16 09:25:11

Level   error
Category    application
Message Tried to read the folder contents of /home/[company-name]/craft/storage/runtime/cache/, but could not.

The permissions are as follows:

/craft 755
  /storage 777
     /runtime 777
        /temp 777
           empty
        /assets 777
           empty
        /cache 777
           09f795de983f214fb0e9188dd884ec27.bin 664
           +another 26 of the same - these don't clear when I click the clear cache button

I tried changing the 664 to 777 but it reverted itself back to 664.

Any thoughts are appreciated!

Try changing the 664 through SSH instead of manually. These three folders need the right permissions:

craft/app/
craft/config/
craft/storage/

source: https://craftcms.com/docs/installing

Edit: Have you tried removing the content of the Cache?


Is there a maximum number of fields in a single matrix? I have a matrix with around 75 fields in it. They are spread over multiple "Block Types". When I create a new field and save the matrix, the new fields aren't being saved.

Even worse, when I create a new block type with new fields and then drag it up in the order, when I save, Craft is deleting (trimming) the last fields in the matrix - even though they are old (already created) fields.

Does Craft have a limit on the number of fields in a single matrix?

Thanks for your help!

This is because of a combination of settings in your php.ini file:  max_input_vars & post_max_size, not any limitation in Craft CMS. c.f.:

Matrix field input error from within the control panel


How do I use an if/else statement to determine if a field is filled in? I know how to add images into my template:

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

But what if my client doesn't have feature images for all of his entries? I need some function that'll tell the website, if there is no image, omit
.

Perhaps some kind of if/else statement that I can use:

{% if
    //the featureimage for this entry is filled in
%}
    {% set image = entry.featureImage.first() %}

{% endif %}

You can use the length Twig filter for that.

{% if entry.featureImage | length %}
    {% set image = entry.featureImage.first() %}

{% endif %}

If there isn't an image in featureImage then nothing will be output.


Is MariaDB required with Craft CMS or can MySQL be used? I am currently installing Craft CMS on a test server, The server runs other development services. Currently installed on the server is MySQL 14.4.
Is Mariadb are requirement or can I utilise MySQL?

Both MySQL and Mariadb are fine to use https://docs.craftcms.com/v3/requirements.html#required-php-extensions


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

appeared today, nothing has been updated or changed.

Any ideas?

Sounds like some of Craft's files are missing.  Try downloading the latest release from buildwithcraft.com and re-uploading the craft/app folder and see if that solves it.

Update:

Since the OP sent a ticket into support, I'll update that re-uploading the craft/app folder solved the problem.


Get entries by date range from plugin Im trying to figure out how to get Entries by date range, say I would like to get all Entries from one week back, or I would like to get all Entries from a specific month.

I find some Twig examples, but haven't successfully been able to translate them to php for use in my plugin.

Any help is much appreciated.

Ok, found an answer that made it a bit clearer:
How do I get elements using getCriteria from a specific date?

Basically you can do this:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->postDate = '>'. date('Y-m-d', strtotime("-4 weeks"));
$result = $criteria->find();

To get Entries form a month back.


How get entries between mutiple dates, ordered by date? I'll import (event) entries with feedme in craftcms 2 from an API who I'll can't change. The api gives the startdate off the events in a dateTime object. The allDay events have a date without time. Because the api also names those date elements different (start/dateTime, start/date) I'll have to import the data separate in to two fields for start and for end.
The question is how I'll get all those events (entries) together ordered by date? From the current date to the currentdate plus 6 months. The entries with the allDay date have an empty field for startDate and endDate and visa versa.
This is what I'll have but that didn't work.
  'api/calendar' => function() {

            // Fetch global set 'agenda'

            return [
                'elementType' => 'Entry',
                'criteria' => [
                    'limit' => 0,
                    'startDate' => ['and', '>=' . date('U') . ""],
                    'endDate' => ['and', '<=' . date('U', strtotime("+6 months")) . ""],

                    'startDateAllDay' => ['and', '>=' . date('U') . ""],
                    'endDateAllDay' => ['and', '<=' . date('U', strtotime("+6 months")) . ""],
                    'order'     => 'startDatum asc'
                ],

Perhaps you could use the Preparse Field plugin, so that you only need to deal with one field. That would make the query easier.
https://plugins.craftcms.com/preparse-field


List Categories AND their associated entries I'm trying to create a category driven gallery. My setup (right or wrong) is this:-

Users create a new entry within a Structure, title it, add photos and then assign the entry to a pre-defined gallery category.

My idea is that when a visitor lands on the 'Gallery' page they see a 'grid' of latest photos (this is a question which might appear here later), with a navigation on the side which allows them to browse the other galleries (categories).

So, how do I list out the categories and associated entries for this navigation?

Example:

Category Title One

Title of the entry1
Title of the entry2
Title of the entry3

Category Title Two

Title of the entry4
Title of the entry5

Category Title Three

Title of the entry6
Title of the entry7
Title of the entry8

Category Title Four

Title of the entry9

I don't want the Category Titles to be links, just the entries within them.

I hope this makes sense and someone can provide a solution.

Many thanks

Martin

Something along the line of this should do that:

    {% for category in craft.categories.group('categoryGroupHandle') %}

        {{ category.title }}
        {% for entry in craft.entries.section('sectionHandle').relatedTo(category) %}
            {% if loop.first %}
{% endif %}

{{ entry.title }}

            {% if loop.last %}
{% endif %}
        {% endfor %}

    {% endfor %}

Probably some bugs in here, but should be enough to get you started.

Also probably smart to wrap this in {% cache %} tags, as it probably will trigger a lot of database queries.


Order entries by "recently modified" date time in control panel Ok somebody ought to start asking the Totally Green questions, it may as well be me.  How do I show the date and time an entry was last manipulated in the control panel?

I produce video more than websites, though that is rapidly changing, and will so even more as the canvas becomes the new After Effects, so I am giving up and in and I'm going to ask all the questions total neophytes would, if it's okay with you people.  Eventually I'm gonna announce my first plug-in, pat myself on the back, and drink a shot.

Short answer is, you can't.

Long answer... Ian has some good advice, but the Post Date won't update automatically. Your best solution would be to update the Post Date every time you update something else in the entry. Then your Post Date will essentially represent your "last changed" date.

Bonus answer... If/when you do build your own plugin, you can look into building your own Element Type. (Just a fair warning... building a custom Element Type is a little more advanced.) When you're setting up your Element Type, you can define which columns appear in that "list" view. One of the native columns in every Craft DB table is "dateUpdated", which contains exactly the information you're looking for. Just output that as one of your columns, and voila!

There's no rush, I can wait answer... the ability to add custom, sortable columns to the entries (and users, assets, etc.) index pages is coming “Real Soon Now”™.


FeedMe for Craft 3: Tweaking attribute values from feed before FeedMe process it I have a feed where one element is to be mapped to a multiselect field on an entry. However, I would need to tweak the values I'm importing (basically making them lowercase), to match the values of my multiselect field in Craft (I would rather map to value than label, although even if I mapped to labels, I would need to do some changes to the imported values).

I've tried more or less every event that FeedMe offers me (and some I've found in the code that are not in the docs), but I cannot find any that seems to do the trick.

I tried to use EVENT_BEFORE_PARSE_FIELD, which seemed to be a logical point (though the event is not mentioned in the docs), but as it doesn't actually set the  values changed in the event back to the values passed in (as it does with other events), the changes are simply lost. So no luck.

On my last try, I tried using EVENT_STEP_BEFORE_ELEMENT_SAVE, and changing the $contentData. But, as far as I can see, this variable is not passed in to the actual save function, it's just passed to an aftersave function, that for entries seems to do absolutely nothing.

So, short of having to loop through the entire feed, searching for the attribute in question in every node, and changing each of them, I can't see how to achieve this. It would make sense to be able to tweak the values passed in at some point where the code is handling one node, but I can't seem to find it.

If anyone have any ideas, I would really appreciate it.

Have you tried EVENT_AFTER_PARSE_FIELD https://github.com/craftcms/feed-me/issues/516

use yii\base\Event;
use craft\feedme\services\Fields;
use craft\feedme\events\FieldEvent;

Event::on(Fields::class, Fields::EVENT_AFTER_PARSE_FIELD, function(FieldEvent $event) {
    if ($event->fieldHandle === 'myAssetsField' && $event->fieldValue !== null) {
        // $event->fieldValue will be an array of assets (already saved).
        // Modify the assets or the field value as needed...
    }
});


How is Craft able to reuse fields in different Sections? (As opposed to EE.) I'm a HUGE fan of Craft, but I'm wondering how specifically it's able to reuse fields in different sections. I'm very accustomed to an ExpressionEngine environment, where each field's shortname has to be unique. How did P&T build Craft so this is possible?

In Craft, all fields are stored in the craft_content table. They are otherwise independent of any field groups, layouts, sections, or anything else.

PRO: This means that any element (Entry, Category, Asset, etc) can access any field.

CON: This means that every element carries the weight of every field. Though if those fields are all empty, the performance impact is almost non-existent.

There's historically been some minor debates over whether or not this tradeoff is worth it... But I think it's safe to say that the benefits vastly outweigh any negligible performance issues. The simple fact is, having all fields in the same table is rarely (never?) the true bottleneck.


On entry save/update, centered text returns to left in rich text field How am I to center text with in the rich text field type? It centers while I'm editing, then returns to left align on save (and on output). I feel like I'm missing something obvious here...

When you create a Rich Text field, there is an option for Clean up HTML which is checked by default. The description underneath the setting reads:

  Removes spans, empty tags, and most style attributes on save.

When aligning text in Redactor, inline styling is added, which would be stripped away on save if the Clean up HTML option is checked.

You'll just need to uncheck this option in your field settings if you want inline styles to be saved.


Craft assets indexing wants to remove files that still exist Launching the task Update Asset Indexes in CP Settings / Tools.

At the end of this task Craft propose a dialog box for you to check some missing files that has been removed from the asset source/folder.

But for some reason my Craft is constantly asking me to remove some files that still exist in folder. And if I click OK then it ask me on each indexing task to remove a randomly chosen file.

One interesting clue is that this bug does not happen locally on my Mac Yosemite / MAMP local dev.

Charset and Collation look right to me:

Any idea how to fix that? (server/mysql config?)
Very annoying for me because I got metadata attached to assets so I cannot permit random deletion. It also remove the asset files inserted into entries.

This has happened to me in a couple scenarios:

When I am working across multiple environments and forgot to sync assets properly. (i.e. I pulled down the database snapshot from production, but not the newer assets that would be referenced in its Assets index).
When one environment uses a case-sensitive file system (e.g. a unix host), and one uses a case-insensitive file system (e.g. OSX). In this case, syncs and Git pulls may fail to differentiate between files with similar names, and both Git and Assets may bugger up.

Those are the first two things I'd double-check.


Change maxRevisions setting per section We'd like to keep the revisions feature active on our site, but a few of our content sections are synced with a third-party API and we don't need to keep a revision at all. Is there a way to set maxRevisions to 0 for specific sections?

Not currently, I'm afraid as that config setting is taken into account on a per-element (i.e. entry), basis.

Feel free to make a feature request for it here, though: https://github.com/craftcms/cms/issues


Partially pay for an order Is there any way of partially paying for an order, so if the order total amount was $100, how can I programmatically set the payment amount to $50, and then pay the other $50 using another payment method later on?
There was a way to do this in Craft2 using a custom plugin, but in Craft3 I tried using the Payments::EVENT_BEFORE_PROCESS_PAYMENT event but the variable is null in the $event->transaction.
Please let me know, thank you!

This is certainly still possible with Commerce 3 and Craft 3.
In a custom plugin or module you can listen for the Transactions::EVENT_AFTER_CREATE_TRANSACTION event and amend the transaction amount at this point in the process.
For example, you could make your own payment form that includes a partPaymentAmount field and posts to the normal commerce/payments/pay controller action.
Then in your event listener to the Transactions::EVENT_AFTER_CREATE_TRANSACTION event, test to see if a value exists for the partPaymentAmount POST variable. If it does, amend the transaction amount accordingly. The payment should proceed with the amended amount, so long as it is still a valid amount to pay against the current order (i.e. it is less than or equal to the total amount of the order).
The nice thing about doing it this way is that it is a very light touch on Commerce and still utilises the existing Commerce workflow for processing the transaction and making sure that everything is updated properly.


Display Category Entries Based on Slug I am trying to display entries based on the last segment in the url and then paginate.

Here's what I have so far but it's not displaying any entries:

{% set categorySlug = craft.request.getLastSegment() %}
  {% set category = craft.categories.slug(categorySlug).first() %}

  {% if category %}

      {% set entries = craft.entries.relatedTo(category).limit(12) %}

      {% paginate entries as entries %}

      {% for entry in entries %}

      {% for block in entry.resource.type('resource') %}

{{ block.resourceTitle }}

{{ block.resourceAuthors }}

{{ block.resourceSummary }}
      Read more

      {% endfor %}

      {% endfor %}

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

      {% if paginate.nextUrl %}
          Next
      {% endif %}
      {% endpaginate %}

  {% endif %}

Even this simplified version outputs zero entries:

  {% set categorySlug = craft.request.getLastSegment() %}
  {% set category = craft.categories.slug(categorySlug).first() %}

  {% set entries = craft.entries.relatedTo(category) %}

  {# Output them #}
  {% for entry in entries %}
      {{ entry.title }}
  {% endfor %}

Any thoughts?

I'm rolling the same thing with

{% extends 'layouts/_entry_layout' %}
{# sets the name for the css file to be pulled #}
{% set pageType = 'search' %}
{% set catSlug = craft.request.getSegment(2) %}
{% set title = craft.categories.slug(catSlug).first() ~ ' Channel' %}

{% block content %}

{% paginate craft.entries.relatedTo(category) as entriesOnPage %}

{{ category.title }}

    {% for entry in entriesOnPage %}
        {% include 'modules/_article_listing' %}
    {% endfor %}

{% include 'inc/_pagination' %}

{% endpaginate %}

{% endblock %}

my article listing module contains the usual things like {{ entry.title }} etc

and pagination include is like so:

{% if paginate %}

{% set currentPage = paginate.currentPage %}
{% set startPage = currentPage - 2 %}
{% set endPage = currentPage + 2 %}
{% if query is defined %}
{% set queryString = "?q=" ~ query %}
{% endif %}

{% if paginate.totalPages > 1 %}

        {% if paginate.prevUrl %}

                &larr;
                Previous Page

        {% endif %}
        {% for i in startPage..endPage %}
        {% if paginate.getPageUrl( i ) %}
        {% if i != currentPage %}

                Page {{ i }}

        {% else %}

            Current Page {{ i }}

        {% endif %}

    {% endif %}
    {% endfor %}

    {% if paginate.nextUrl %}

            Next Page
            &rarr;

    {% endif %}

{% endif %}

{% endif %}