(How) can I fill a visitor's cart by using a link? When using Commerce 2, is there a way to fill a client's cart with the contents of an (inactive) cart, by using a link? Use case: abandoned cart email.

There is currently no way to populate a cart from a link as a standard feature of Commerce 2.

But you could accomplish this in 2 ways.

1) With a smart template to add all items from the order to the current cart.

Create a template that takes an order number param like so:

cart/start-order?number=5bnuy8843nuy48bdy48u3bd84

In that template you would do the following:

{# @var craft \craft\web\twig\variables\CraftVariable #}

{% set number = craft.app.request.param('number') %}
{% set previousOrder = craft.orders.number(number).one() %}

{% if not order %}
  {% redirect 'shop' %}
{% endif %}

{# Get the current cart if there is one, also make sure the cart is persisted so we can add to it. #}

{% if cart is not defined %}
  {% set cart = craft.commerce.getCarts().getCart(true) %}
{% endif %}

{# Clears the current cart. This is optional as you may just want to add the items to the cart #}
{% do cart.setLineItems([]) %}

{% for oldLineItem in previousOrder.lineItems %}

  {% set lineItem = craft.commerce.lineitems.createLineItem(cart.id, oldLineItem.purchasableId, oldLineItem.options, oldLineItem.qty, oldLineItem.note) %}

  {% do cart.addLineItem(lineItem) %}

{% endfor %}

{% do craft.app.elements.saveElement(cart) %}

{%  for lineItem in cart.lineItems %}
  {{  lineItem.qty }} * {{ lineItem.description }}
{%  endfor %}

This add all items from the cart in the email link to the current cart. You would do a similar thing to populate addresses etc if you wish.

2) Alternatively, if you just want to replace the current cart in the users session with the incomplete cart you passed in from the email’s number param, you could do the following

Create a plugin/model with a controller action.
The controller action would accept an order number param (like the template example above)
The controller action would do the following:

Code:

$request = Craft::$app->getRequest();
$session = Craft::$app->getSession();

$number = $request->getParam('number');
$order = \craft\commerce\elements\Order:find()->number($number)->one();

if ($order && !$order->isCompleted){
  // Forget the current cart in their session.
  \craft\commerce\Plugin::getInstance()->getCarts()->forgetCart();

  $session->set('commerce_cart', $number);
  return $this->redirect('shop/cart');
}

return $this->redirect('shop/cartLoadError');

This should get you mostly there. Let me know if you have any questions.


Populate a record with a model? In a follow up to my earlier question, I'm focusing on cleaning up my code to keep it incredibly simple. I've moved on from the Controller and Model to the Model and Service. Typically you would have something like the following in a service method:

$record = new MyRecord();

$record->name           = $model->name;
$record->anotherField   = $model->anotherField;
// etc..

Which works and you end up defining the attributes in _attributes as an array like below:

[*:_attributes] => array
    (
        'uid' => '0'
        'name' => 'My Name'
        'anotherField' => 'Another Field'
    )

The logic that I am looking for, which I'm sure someone will tell me I am just using the wrong method, can be seen in the controller.

public function actionSave()
    {
        // require a post request
        $this->requirePostRequest();

        // assign the fields from post request
        $post = craft()->request->getPost('fields');

        // create a model, populate with the assigned fields
        $model = MyModel::populateModel($post);

        // validate attributes
        if ($model->validate()) {

            // save the record
            craft()->myPlugin->saveNew($model);

            // redirect to posted url
            $this->redirectToPostedUrl();
        }

        // grab errors from the model
        $errors = $model->getErrors();

        // return errors to the template as 'errors'
        craft()->urlManager->setRouteVariables(array(
            'errors'    => $errors
        ));
    }

In the controller code, I'm populating the model with the post with this line:

`$model = MyModel::populateModel($post);`

Is there a way to create the same behavior for the method in the service to "automagically" assign the model attributes to the $record?

Yii does have the equivalent populateRecord and populateRecords on CActiveRecord.

However, on both the populateModel(s) and populateRecord(s) methods, you should be very cautious of blindly mass-assigning untrusted inputs to models/records that participate in the Active Record pattern (like Craft/Yii does).

They can be susceptible to what's known as the Mass Assignment Vulnerability that many popular frameworks have been a victim of.

Yii does what it can to protect against this, but my personal preference is to explicitly assign each individual attributes on the model/record in the case of untrusted input to avoid the issue altogether.


Successfully installed Craft 3 via Composer but can't access site or admin I've been Googling around all day looking for answers to this, but I'm actually stuck.

Ok, so basically I installed Craft 3 via Composer into a subdomain on my development server (apache). Everything ran successfully, there are no error logs, and the database has been populated with all of the tables. The problem is that if I go to http://craft.domain.com/index.php?p=admin (or /index/admin or /admin or /) all I get is a 500 Internal server error, and not one with the fancy craft background either. It does say email webmaster@craft.domain.com... on the error screen so I know SOMETHING's clicking, but I can't work my way around this.

I tried installing Craft once before manually by extracting the zip, uploading all the files, and then changing the .env file, and I got a similar result. I still wasn't able to access the admin OR the install page, and that's why I ended up trying it all through Composer instead.

Running the latest version of Apache, PHP 7, MYSQL with InnoDB; I run a ton of wordpress and laravel sites on this server without issue so I'm not clear on what makes Craft so different. I have WHM and shell access. Open to all suggestions.

Directory structure looks like this:

/home/username/public_html/craft (this is also the subdomain path)
  - config
  - modules
  - storage
  - templates
  - vendor
  - web
  - (and then the composer/env files)

Directory permissions are 775, the rest of the files are default Craft 3 installation. I've also made sure my PHP 7 memory limit is at 128MB (it was set to 32MB for some reason).
Thank you for any advice in advance!

On the public_html/index.php file, try replacing

define('CRAFT_BASE_PATH', dirname(__DIR__));

with

define('CRAFT_BASE_PATH', ('/home/pathToCraftFolder/craft'));

The structure of my folders is as follow:

/home/username/
  - public_html, or just html / this is where the content of your web folder goes
  - config
  - modules
  - storage
  - templates
  - vendor
  - (and then the composer/env files)

Then on the index.php file (which can be found under /home/username/public_html/index.php),you'd replace:

 define('CRAFT_BASE_PATH', dirname(__DIR__));

with

 define('CRAFT_BASE_PATH', ('/home/pathToCraftFolder/'));

If you only have access to the public_html folder or would like to install craft3 on a subfolder this is how you can structure it:

on your public_html (/home/username/public_html),
create a craft subfolder((/home/username/public_html/craft)), then send your craft3 web folder into the public_html. It should look like this:

/home/username/public_html/
               -index.php
               -web.config
               -.htaccess
               /craft/

In your index.php file, replace

define('CRAFT_BASE_PATH', dirname(__DIR__));

with

 define('CRAFT_BASE_PATH', ('/home/pathToCraftFolder/craft'));

Hope this helps.


How to get the order number of the current entry in a structure Is there a way in Craft to get the order property of the current entry when it's in a structure? For example say you have the following structure:

Parent Page

Child One
Child Two
Child Three

Is there a way to get what order property Child Two is in relation to its siblings so in this case it would be 1 (zero based)?

In my templates I am wanting to be able to grab the order property for the current entry by doing something like {{ entry.getOrder() }}.

There's a positionedBefore parameter for craft.entries. Use this with a criteria model filtered to
show the entries you are interested in only.

{% set parent = entry.getParent() %}
{% set siblingsIds = parent.getChildren().ids() %}

{% set positionedBefore = craft.entries({
    id: siblingsIds,
    order: 'lft desc',
    positionedBefore: entry,
}).total() %}

With the first two lines I'm getting the IDs of all of the entry's siblings. I then prepare a craft.entries criteria model using the siblings' IDs with the id parameter to query just for those entries, order them by lft in descending order (that's the db column Craft stores position info) and further filter with positionedBefore to get just the entries before entry.

Calling the total method returns the number of entries found.


Limit/offset output of Matrix field by 50% Is it possible to limit and offset the output of a Matrix field by 50%? I wanted to split the data across two columns.

This is what I have used, some math and the slice filter.

{% set midIndex = ((entry.myMatrixField|length) / 2)|round - 1 %}

{% set blocksA = entry.myMatrixField|slice(0, midIndex) %}
{% set blocksB = entry.myMatrixField|slice(midIndex, null) %}


Can the Element API export Craft Commerce orders as the element? Background: We are considering using Craft Commerce to replace Shopify for more custom fields.
However Craft Commerce does not have simple export buttons for exporting orders, so we are looking at the Element API plugin to build our custom plugin to get each order as a order-xxx.json

Question: Can i use Craft Commerce orders as a element in the Element API plugin?

Orders are in fact an ElementType in Craft.

See Commerce_OrderElementType.php inside the commerce plug-in folder. With that said you should have no problem retrieving orders like you would any other element type in Craft.

There are also services available that make it very easy to retrieve orders so you may find a simple PHP plug-in will get the job done in terms of returning JSON for an order(s).


Can you store a table field within a matrix field? I am looking to use a matrix field for my site navigation that has some dropdowns. Using a table field within a matrix field seems like it would work but I don't know how to access the table inside the matrix field in my template. Is this possible.

I figured it out. You can do it. Here is my example.

{% for block in mainNavigation.navigationLink %}

                {{ block.linkName }}

                {% if block.childLinks | length %}

                    {% for row in block.childLinks %}
                        {{ row.childLinkName }}
                     {% endfor %}

                 {% endif %}

        {% endfor %}


Matrix field input error from within the control panel I have a matrix field with around 26 block types with between 2-6 fields on each. It seems like I've hit a limit and cannot add any more fields to any of the block types. I can add a field if i delete one.

I get the following error "Undefined index: type"

/var/www/dev/domain.co.uk/craft/app/fieldtypes/MatrixFieldType.php(118)

I'm running Craft Pro 2.1.2563

Matt

There's about a 99% chance you're bumping into your php.ini file's max_input_vars value and/or its post_max_size. If you bump those to higher values, it should start working as expected.

Note that you may have to restart your web server for the settings to take affect.


Route directly to an S3-hosted asset? I have a multi-site Craft install.  I'd like to offer the ability for each site to upload an icon that will be used for favicons.  I noticed a lot of 404s for both /favicon.ico and /apple-touch-icon.png, which appear to be hardcoded in various clients.  I'd thus like to route those URLs directly to the requisite asset files, but on a per-site basis.

I can't just put the files into /web, since that's shared among all the sites, and I want site-specific icons.
I'm hosting assets on S3 (since we are using a load balancer).

Is there a way to do this in Craft without writing a custom plugin to do so?

If there exists a plugin to do this, or if I could do it via Craft directly, that would be wonderful, but I cannot find a way to do so.

If I have to write a custom plugin, I'd have it basically:

Respond to those URLs
Look up the requisite field in Globals for the assets on a per-site basis.
Download the asset file locally (and cache locally in /storage)
Set the proper MIME type
Serve the file from /storage directly

Does this sound reasonable or would there be a better way to handle this?

Thanks in advance for any assistance!

By default browsers look for a favicon at the root of your web directory. So if you do not add a link in your head section, but you have a favicon.ico at webroot, you will still see it.

However, a favicon can technically be located anywhere. If you have a multi-site installation you can reference it like this (note that you should always use a fully qualified URL or you may run into issues with IE):

<link rel="shortcut icon" href="https://yourbucket.s3.amazonaws.com/{{currentSite.handle}}/favicons/favicon.ico" type="image/x-icon">

Insert Craft's current site handle tag so that you can use the same code on all sites. Then you'll just need to create a paths in your S3 bucket to match.

Regarding multiple sizes, you should include more than just a 16x16 favicon. This is what I have which is all related to icon display:

<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#888888">
<meta name="msapplication-TileColor" content="#ffc40d">
<meta name="theme-color" content="#ffffff">

The manifest, mask-icon, msapplication-TileColor, and theme-color all refer to how your favicon will appear on platforms including Safari, Windows and Android devices.

You could add an image transform to your asset volume which creates each of the required sizes, or you could direct your client to upload multiple sizes. It may be easier to send them to this site to generate favicons which they can then upload to their assets volume.

Disclaimer, I have no affiliation to the Favicon Generator site -- I just use it frequently. I generated the code you see above using this tool. If you're looking for a reliable way to generate your favicon size variations and the associated code, this online tool does the trick.


How do I redirect from a legacy category URL to a new catgory URL using Retour I've installed the Retour plugin and have been able to get most of my redirects working, but have now run into a situation where the old site had multi-level category hierarchy eg: /category/category-slug/sub-category-slug url structure and the new site only has a single level eg: /news/category/category-slug

How can I create a Regex to grab the last segment off the url, if there is more than one level of category, to use for my redirect ?

Looks like if there was only one level then the following would do:

/category/* to /news/category/$1

Any tips or ideas ?

Despite having used regex's for years, I still almost never get them right from memory. A great tool for testing them out is: https://regexr.com/

What you'll need to use is a capture group. Something like this for the source:

/category/(.*$)

And then the resulting URI would be something like this:

/news/category/$1

Each set of () is a capture group; they are numbered sequentially, so $1 will output the first capture group.

If you go to https://regexr.com/ it will even explain every part of the regex, and what it does:


How do I output all field values from a Category on the current entry? I'm building my first site with Craft, so this is a bit newbie question.

I've build a category with a some fields.
How can I output all fields form the selected category on the current entry?

Thank you very much.

If you have a Categories field on your entry called myCategoryField, you can check if it has any Categories selected by going:

{% if entry.myCategoryField | length %}
    There are categories selected!
{% endif %}

If you want to loop through all of the selected categories for that field, you can do:

{% if entry.myCategoryField | length %}

        {% for category in entry.myCategoryField %}

{{ category.title }}

        {% endif %}

{% endif %}

For more info you can read more in the templating section of the Categories field's documentation.


Dukt twitter issue Craft 2.5 Weird issue when upgrading Craft to 2.5 that the settings link in the revised dashboard redirects to the settings page for the Dukt twitter add-on. If I disable the add-on then works just fine. Anybody else experiencing this problem?

While we wait for an official update, you can patch this pretty easily. Just open up craft/plugins/twitter/TwitterPlugin.php and find this function:

public function getSettingsHtml()
{
    // ...
}

Delete the function entirely, and replace it with this:

public function getSettingsUrl()
{
    return 'twitter/settings';
}


How to reference Twig form values from within controller action? I have a form and a hidden action that successfully runs the controller action.  I now want to include 2 variables (form input values) and then use those values while I am in the controller action.  How do I grab the value (theemailaddress) that was in the form?

Twig template file:

<form method="post" action="" accept-charset="UTF-8">

<input type="hidden" name="action" value="psychEmail/publication/pubRequest">
        <input type="text" name="theemailaddress" value="" size="100">

        <!-- ...-->

        <input class="btn submit" type="submit" value="{{ 'Submit'|t }}">
    </form>

function IN controller:

public function actionPubRequest()
{
    $this->requirePostRequest();

    $email = new EmailModel();
    $email->fromEmail = 'email@school.edu';
    $email->toEmail = 'myemail@gmail.com';
    $email->subject = 'Publication Request';
    $email->body    = 'Your requested publication is attached.';

    craft()->email->sendEmail($email);
}

You can use craft()->request->getPost('name') to grab any posted values from your form submission.


Entries from matrix should not appear in loop I have a Matrix with an entry field.
I have a Loop which is looping entries out of a section.

Goal: The entries from the matrix should not appear in the loop of the section.  So the Matrix defines Starred Member Profiles and the loop trough the section is a teamlist, but the two starred profiles should not appear in there.

I tried something like this but i stuck right now...

{# Get the profiles which are in the matrix #}
{% set highlightProfiles = entry.highlightProfiles.all() %}

{% for profile in highlightProfiles %}

 {# Just a test to see the ID of each single entry #}
 {{ profile.hightlightProfileTeaser.one().id() }}

 {% set highlightProfileSingleIds = profile.hightlightProfileTeaser.all().ids() %}

{% endfor %}

{# set something up for the entry ids which should not join in the endloop #}
{% set highlightProfileIdsString = highlightProfileSingleIds | join(', not ') %}

{% set teamMembers = craft.entries({
 section: 'profiles',
 limit: 50
}) %}

{% set allProfilesWithOutHighlights = teamMembers.id('and, not '~highlightProfileIdsString).orderBy('title asc') %}
{% for member in allOtherMembers.orderBy('title asc').all()  %}
 {{ member.title }} {{ member.id() }} {{ loop.index }}
{% endfor %}

You are on the right track, to “collect” the entry IDs you have to use the merge filter in your Matrix loop though.

{% set highlightProfileSingleIds = [] %}

{% for profile in highlightProfiles %}
    {% set ids = profile.hightlightProfileTeaser.ids() %}
    {% set highlightProfileSingleIds = highlightProfileSingleIds|merge(ids) %}
{% endfor %}


Treat links in redactor/rich text field differently depending on entry type I'm looking for a way to handle links to entries in a Redactor (Rich text) field such that a different class gets added to the  tag depending on the type of the entry.

For example, a link to an "event" entry type would be modified to look like: Event name.

I assume that some kind of plugin would be needed for this, but I have been able to find anything similar I could learn from. So any pointers to this end would be really helpful!

Many thanks.

This is a high-level question, with multiple problems to solve. First, you'll need to parse the HTML from the Rich Text field for <a> tags. Then, you'll need to find a way to match the href attributes for those <a> tags to entries, and finally, you'll need to set classnames to the <a> tags (depending on the matched entry's Entry Type) and spit the rewritten HTML back out (saving it back to the entry if you want the classnames to persist).

Please note that the code in this answer isn't really tested, and there are multiple ways to skin your particular cat (or cats, as it were). Hopefully you'll take something away from it, though.

First, in order to parse the HTML for <a> tags, I'd recommend using DOMDocument, which is a built-in HTML parser (PHP5+). DOMDocument makes it super easy to find all the <a> tags in your HTML, and is much safer than using regular expressions. Here's how that could look:

$dom = new \DOMDocument();
$dom->loadHTML($html);
$links = $dom->getElementsByTagName('a');

Next step is looping through all the <a> tags; attempting to pull Entries from their href attributes. Note that those href attributes might be reference tags (e.g. {entry:11:url}) or either a relative or absolute URL, and ideally, your code should account for all of those variations:

foreach ($links as $link) {

    $href = $link->getAttribute('href');

    if (strpos($href, '{entry') !== false && strpos($href, 'url}') !== false) {
        // Reference tag – get entry by ID
        $reference = explode(':', $href);
        $entryId = (int) $reference[1];
        $entry = craft()->entries->getEntryById($entryId);
    } else {
        // URL. Make sure the site url is stripped and get entry by URI
        $uri = ltrim(str_replace(craft()->config->get('siteUrl');, '', $link->getAttribute('href')), '/');
        $entry = craft()->elements->getElementByUri($uri);
    }

}

Finally, DOMDocument makes it super easy to add a classname back to the <a> tags, depending on the Entry Type:

// Add classnames based on the Entry Type handle
switch ($entry->type) {
    case 'work' :
        $link->setAttribute('class', 'work-link');
        break;
    case 'news' :
        $link->setAttribute('class', 'news-link');
        break;
}

One minor gotcha to note, is that DOMDocument will add HTML structural tags ('html', 'head', and 'body') to your HTML. A bit of regex takes care of that:

$html = preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $dom->saveHTML());

Here's the complete code, for context:

// Create a new DOMDocument instance and find all <a> tags. I like to use mb_convert_encoding to ensure that the HTML content is UTF-8 encoded, but your mileage may vary
$dom = new \DOMDocument();
$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$links = $dom->getElementsByTagName('a');

// Loop through the <a> tags
if ($links && $links->length > 0) {

    foreach ($links as $link) {

        // Try to get entry from href attribute
        $href = $link->getAttribute('href');

        if (strpos($href, '{entry') !== false && strpos($href, 'url}') !== false) {
            // Reference tag – get entry ID
            $reference = explode(':', $href);
            $entryId = (int) $reference[1];
            $entry = craft()->entries->getEntryById($entryId);
        } else {
            // URL. Make sure the site url is stripped
            $uri = ltrim(str_replace(craft()->config->get('siteUrl'), '', $link->getAttribute('href')), '/');
            $entry = craft()->elements->getElementByUri($uri);
        }

        if ($entry && $entry->elementType == ElementType::Entry) {

            // Add classnames based on the Entry Type handle
            switch ($entry->type) {
                case 'work' :
                    $link->setAttribute('class', 'work-link');
                    break;
                case 'news' :
                    $link->setAttribute('class', 'news-link');
                    break;
            }

        }

    }

}

Phew. That takes care of the HTML, but depending on your need, you'll have to decide on an approach for when and where you want to rewrite your Rich Text content – i.e., where and when you'll place and call the above code (or something like it).

You have two options (it isn't clear from your question which of these you want):

a) Add the classnames when the entry is saved (permanent)
b) Add the classnames when the content is output in a template (not permanent)

If you want to add the classnames to links in the Rich Text content permanently, I'd recommend creating an event listener for the entries.saveEntry event, rewriting the HTML before re-saving the entry. The below would go in your plugin's primary class:

public function init() {
    parent::init();
    craft()->on('entries.saveEntry', array($this, 'onSaveEntry');
}

public function onSaveEntry(Event $e) {
    $entry = $e->params['entry'];
    $html = isset($entry->richTextField) ? $entry->richTextField->getRawContent() : false;
    if ($html) {
        ...
        $entry->getContent()->setAttribute('richTextField', $html);
        if ($entry->validate()) {
            craft()->elements->saveElement($entry);
        }
    }
}

For option b), I'd suggest writing a Template Variable method or a custom Twig Filter. A Template Variable would be a little bit easier, and would enable you to do the following in your template to print the HTML with the classes added:

{{ craft.myPlugin.addEntryTypeClasses(entry.richTextField) }}

A custom Twig filter offers a marginally more elegant template syntax:

{{ entry.richTextField|addEntryTypeClasses }}

Here, addEntryTypeClasses is a custom function (a template variable method or a Twig filter, respectively) that you'd have to create, where you could add the above code. To avoid the need to use the |raw filter in your template, add the following when you return the $html variable:

public function addEntryTypeClasses($html) {
    ...
    return TemplateHelper::getRaw($html);
}

In all of the above examples, ... is where you'd put the DOMDocument code. Ideally, all of that code would go in a custom Service method, making it easy to perform the DOMDocument operation on any HTML content from basically anywhere (Twig Filters, Variable methods, hooks and/or event listeners), as well as keeping your plugin code tidy and nice, but it isn't a must.

Keep in mind though that with option b), it would be a very good idea to cache the output, as the operation can be costly if you have a lot of links in your Rich Text field (Craft will have to hit the database for each link, every pageload).

Finally, if you don't fancy the DOMDocument approach, you could look at different approaches entirely – for instance, you could write a custom Redactor plugin, which could do basically the same thing as the above DOMDocument solution, only with JavaScript (jQuery) instead of PHP. Here's an example of a Craft Redactor plugin you could take a look at to get started: https://github.com/jpdevries/styles

If you're interested in more examples regarding custom Twig filters and DOMDocument specifically, I wrote a plugin called Retcon HTML which streamlines a lot of common HTML rewriting operations. It won't help with your specific issue, but the attr filter comes pretty close :)


Where can I run custom PHP scripts? My Craft CMS installation has public and craft in the root of the directory, with the domain pointing to the public folder. Where could I put custom PHP scripts that I can run in the browser?

I've tried it in the craft/templates folder (/craft/templates/m53ford). In this folder I can see the HTML or XML files by visiting http://feeds.rhinodigital.co.uk/m53ford/index.xml for example, but a PHP script doesn't run in the same place.

I've also tried in folders at the same root level (alongside the public and craft folders), and also within the public folder itself. Where in the directory tree could I place a PHP file that I can visit in the browser?

If you're simply looking to execute some custom scripts that are completely standalone (i.e. they don't interact with the Craft application in any way), those files should live somewhere below your public web root (i.e. the public folder, or wherever your index.php file resides). It isn't possible to have Craft route PHP files from the /templates directory.

However, the problem with reading/executing arbitrary PHP files from the public root, is that Craft basically "hijacks" all requests to anything below that folder, and routes those requests to index.php. In order to read or execute an arbitrary PHP file below the public root, you'll need to override Craft's URL routing, for example by using RewriteRule directives in your .htacesss file if you're using Apache (also see this related thread).

On the contrary, if your custom PHP scripts do need to interact with Craft in any way, you'll need to write a custom plugin.

Fortunately, Craft plugins are relatively simple to build (as long as you're familiar with PHP, of course). You can use the excellent Plugin Factory to quickly scaffold a bare-bones plugin with a thoroughly commented codebase or use the Business Logic plugin as a starting point. Downloading other available plugins and looking at their source code is probably not a bad idea, either.

In any case, before digging in you should definitely skim through the official docs on plugin development. By far, the most important thing to know about Craft plugins is how they are structured and what the various moving parts do. That knowledge will also give you a nice overview of what a plugin's capabilities are, and how you'd go about implementing a certain feature in your own plugin.

One thing that the official docs doesn't really mention, which might be relevant to your use case, is how you make use of third party scripts or libraries. There are lots of ways to skin that particular cat, but the headline is that using using Composer if you can is the best option, though a simple require() call in your plugin's main class' init method will of course also work.

Also, keep in mind that when you're using third party scripts inside a Craft plugin, you will need to deal with namespacing.

Edit

The upcoming Craft 3 release adds another option for running PHP business logic – Yii2 Modules.


Set Variable if Current Field is Empty I'm trying to set a variable within my templates which does the following:

  If this field is empty then use another field instead.

I have 2x Fields called affiliateUrl and offerLink, if offerLink is empty, then use the affiliateUrl instead.

The current code I'm running is this:

{% set affiliateLink = relatedEntry.offerLink ? relatedEntry.affiliateLink : relatedEntry.affiliateUrl %}

Error Code:

  Calling unknown method: craft\elements\Entry::affiliateLink()

The rest of the code is below:

        {% set affiliateLink = relatedEntry.offerLink ? relatedEntry.affiliateLink : relatedEntry.affiliateUrl %}

        {% if relatedEntry.offerCode is empty %}

            Get This Offerchevron_right

        {% else %}

                window.aPopunder = [
                    ['{{ relatedEntry.offerLink }}']
                ];

            Reveal Code & Visitchevron_right

        {% endif %}

Required a different operator

{% set affiliateLink = relatedEntry.offerLink ?? relatedEntry.affiliateUrl %}


Date Archives: Last 6 months From the Creating an Archive Page for Entries article in the Craft docs. How could I limit it so that it grouped by month but limited things to 6 months?:

{% set allEntries = craft.entries.section('blog').limit(null) %}
{% for date, entries in allEntries | group("postDate|date('F Y')") %}

{{ date }}

        {% for entry in entries %}

{{ entry.getLink() }}

        {% endfor %}

{% endfor %}

100% untested, but something like this should work using Twig's date_modify filter.

{% set startDate = now|date_modify("-6 months") %}

{% set allEntries = craft.entries.section('blog').limit(null).postDate('>= ' ~ startDate) %}

{% for date, entries in allEntries | group("postDate|date('F Y')") %}

{{ date }}

        {% for entry in entries %}

{{ entry.getLink() }}

        {% endfor %}

{% endfor %}


Display entries tagged with specific tag(s) before other entries What I'm trying to accomplish is looping through a collection of entries and have them display (with pagination) in the following order:

entries tagged as Featured in alphabetical (title) order
entries tagged as New in alphabetical (title) order
all remaining entries in alphabetical (title) order

The following worked:

craft.entries({ order: 'featuredLightswitchField desc, title asc' })

However it fails to work if you try to use two lightswitch fields (e.g. featured and new) in addition to the title ordering, and I'd prefer to keep things tag-based instead of needing to add lightswitch fields.

I also attempted at creating a custom array of entry IDs in the proper order and passing it to craft.entries.id(), however I can't find a way of keeping the entries in the order of the array passed to craft.entries.id().

craft.entries({ id: [3, 1, 2] })
// can't find a way to keep entries in [3, 1, 2] order.

If there was a way to order the entries based off the id array that would solve the problem, unless there's a better solution.

Can provide additional information if my explanation is insufficient.

You're on the right track with merging arrays of entry IDs. And to keep the sort order with your new craft.entries call there's in fact a method for that: fixedOrder(true).


Namespace error with fixture (solved) I've set up a fixture, but Codeception can't find it. What am I missing or doing wrong?
% php vendor/bin/codecept run functional
...
  [Error] Class 'myname\tests\fixtures\MyFixture' not found

In the sample code the namespaces are under myname but I've also tried using just tests\fixtures, tests\unit\fixtures, and myname\tests\unit\fixtures.
Thinking it's something to do with the namespace or most likely something to do with the Craft fixtures docs saying

Fixtures…can be defined in the fixturesMethod defined in the codeception.yml file.

My codeception.yml doesn't have fixturesMethod. I can imagine that's a way of doing some of this but haven't found any documentation describing what that configuration should look like.
I've also tried loading the fixture on the command line
% ./craft fixture/load ImageAssetFixture
Exception 'yii\base\InvalidConfigException' with message 'Invalid fixture namespace: "tests\unit\fixtures". Please, check your FixtureController::namespace parameter'

% ./craft fixture/load ImageAssetFixture --namespace='myname\tests\fixtures'
Exception 'yii\base\InvalidConfigException' with message 'Invalid fixture namespace: "myname\tests\fixtures". Please, check your FixtureController::namespace parameter'

Here's the setup:
tests/functional/AssetFixtureCest.php
<?php

use FunctionalTester;
use myname\tests\fixtures\MyFixture;

class AssetFixtureCest
{
    public function _fixtures()
    {
        return [
            'myFixture' => [
                'class' => MyFixture::className(),
                'dataFile' => codecept_data_dir() . 'my-fixture.php',
            ],
        ];
    }

    public function testMyFixture(FunctionalTester $I)
    {
        $myFixture = $I->grabFixture('myFixture');
        codecept_debug($myFixture);
    }
}

tests/fixtures/MyFixture.php
<?php

namespace myname\tests\fixtures;

use Craft;
use craft\test\fixtures\elements\...;

class MyFixture extends ...
{
    /**
     * {@inheritdoc}
     */
    public $dataFile = __DIR__ . '/data/my-fixture.php';
}

test/fixtures/data/my-fixture.php
<?php

return [
    ...
];

I needed to define the namespace. In composer.json:
"autoload": {
    "psr-4": {
        "myname\\tests\\fixtures": "tests/fixtures"
    }
}

and then
composer install

That uncovered some other problems with the above code (to anyone else who's trying to figure out fixtures, looking through the Craft repo's own tests directory is helpful), but that's separate from this question.


List all entries except 'hidden' So I hope I am explaining this correctly.

I have some entries with a tag called 'hidden'. In my blog layout I want to exclude those entries.

I have the following:

{% set tag = craft.tags.name('hidden') %}
{% set hiddenentry = craft.entries.section('blog').relatedTo(tag).find() %}
{% set allentry = craft.entries.section('blog').limit(5).find() %}
{% set unhidden = allentry|without(hiddenentry) %}

{% for entry in unhidden %}

{{ entry.title }}

Posted on {{ entry.postDate.format('F d, Y') }}

            {{ entry.body.getPage(1) }}
            {# {{ entry.summary }} #}
            <button>Continue reading</button>

{% endfor %}

Not really the answer you are looking for, but it might be easier to change the tag hidden to a checkbox or lightswitch field and add a condition to the for..in loop, as explained in the twig documentation, which may do what you need and keep the query count lower.

{% for entry in allEntries if not entry.hidden %}
    ...
{% endfor %}


Offset not working correctly I have an entry that has a entries field where 9 entries have been added.

The first three entries are displayed in one block at the top of the page and this works correctly using .limit(3)

However further down the page I expect 6 entries to be displayed when using .offset(3). That is not happening. Instead you only see the next 3 entries, not the next 6. If I change to offset(6) I still see 3 entries but it's the final three entries.

Below is simplified relevant code.

{% for secondaryEntry in entry.secondaryFeatures.offset(3).all() %}

        {{ secondaryEntry.title }}

{% endfor %}

Please advise.

edit
Here is the code calling the first three entries at the top of the page

{% for secondaryEntry in entry.secondaryFeatures.limit(3).all() %}

      more code

{% endfor %}

You will need to reset your .limit(null) on the second call (the one with your offset) due to the way Element Queries work.


Element API "Last Updated Sections" I am trying to create an endpoint that outputs the latest updated entry in each section (Themes, Videos and Screensavers) like this:

url/updated.json

{
 themes: '2016-08-22',
 videos: '2016-08-18',
 screensavers: '2016-07-20'
}

I can't think of how to do this but here is my code so far, no dice so far tho:

'updates.json' => [
        'elementType' => 'Entry',
        'paginate' => false,
        'criteria' => ['section' => '', 'limit' => null,],
        'transformer' => function(EntryModel $entry) {
            $screensaversLastUpdated = $entry->dateUpdated;
            $videosLastUpdated = $entry->dateUpdated;
            $themesLastUpdated = $entry->dateUpdated;
            return [
                'themes' => $screensaversLastUpdated ,
                'screensavers' => $screensaversLastUpdated ,
                'videos' => $videosLastUpdated
            ];
        }
    ]

Any help would be much appreciated!

Thanks!

This isn't really a job for the Element API plugin. You can accomplish what you want much easier by just creating a plain ol' Twig template (e.g. templates/updates.json) that pulls the three latest updated entries from each section, and returns the entries' dateUpdated values as JSON:

{% header "Content-Type: application/json" %}

{%- spaceless -%}

{% set sections = ['themes', 'videos', 'screensavers'] %}
{% set entries = [] %}
{% for section in sections %}
    {% set entry = craft.entries.section(section).order('dateUpdated desc').limit(1).first() %}
    {% set entries = entry ? entries|merge([entry]) : entries %}
{% endfor %}

{
  {%- for entry in entries -%}
    "{{ entry.section.handle }}":"{{ entry.dateUpdated }}"{% if not loop.last %},{% endif %}
  {%- endfor -%}
}

{%- endspaceless -%}

The above would render a JSON payload exactly like the one you have in your example.

If you really want to use Element API for some reason, you can get pretty close to what you want (but not quite). Here's why it's difficult to do, though:

For one, the transformer property function operates on a single entry at the time, which is a problem in terms of your desired payload.

Second, with Element API you don't have full control of the JSON format the plugin returns – so you'll never get the payload to look exactly like you want (the actual data will be wrapped in a data property).

Third and most importantly, an Element API endpoint requires a single query (or actually, a single ElementCriteriaModel) to pull your elements. Unfortunately, it isn't possible to tell an ElementCriteriaModel to return 1 entry from each section – notice that even in the template example above, there's actually three queries – one for each section (they're just wrapped in a for loop to keep the code small and DRY).

So basically, you'll need to "trick" Element API to return something similar to your example JSON.

First, since you can't use a single ElementCriteriaModel to query your 3 latest entries, you'll actually need three of those suckers. The Element API plugin gives you one, but you'll need to create the other two inside the transformer function – you just need to ensure that Element API actually runs that function, and only runs it once, by pulling a single entry (in other words; you'll need a limit => 1 for your endpoint's criteria). It makes sense to have the endpoint pull one of the three entries you need, so you'll avoid a redundant database query. Then, you'll have to pull the other two entries "manually". Finally, for the return value you can return an array similar to what you have in your example:

'updates.json' => [
    'elementType' => 'Entry',
    'paginate' => false,
    'criteria' => ['section' => 'themes', 'order' => 'dateUpdated desc', 'limit' => 1],
    'transformer' => function(EntryModel $entry) {

        $themesEntry = $entry;

        $videosEntry = craft()->elements->getCriteria(ElementType::Entry, [
            'section' => 'videos',
            'order' => 'dateUpdated desc',
            'limit' => 1
        ])->first();

        $screensaversEntry = craft()->elements->getCriteria(ElementType::Entry, [
            'section' => 'screensavers',
            'order' => 'dateUpdated desc',
            'limit' => 1
        ])->first();

        return [
            'themes' => date('Y-m-d', $themesEntry->dateUpdated->getTimestamp()),
            'videos' => date('Y-m-d', $videosEntry->dateUpdated->getTimestamp()),
            'screensavers' => date('Y-m-d', $screensaversEntry->dateUpdated->getTimestamp()),
        ];
    }
]

The above would return a JSON payload looking something like this:

{"data":[{"themes":"2016-02-23","videos":"2016-08-23","screensavers":"2016-07-16"}]}


Hooking into event when record deleted from custom plugin We are using a plugin that has created its own record for storing private messages. When deleting a message, it's calling the delete() method on an instance of the record, which appears to be using the CActiveRecord's implementation of delete.

In CActiveRecord::delete(), it has a $this->afterDelete() method, which looks like it should be raising an onAfterDelete event, but I haven't had any luck hooking into it.

I've tried craft()->on('pluginhandle_messageRecord.onAfterDelete', ...) and craft()->on('cActiveRecord.onAfterDelete', ...), but neither gets triggered.

Does anyone know the correct syntax for hooking into this event for this specific custom record?

Thanks!

Because that's a Yii event, it doesn't follow the same component.eventName syntax that the other Craft events use and you can't use craft->on() to register to listen to it.

Completely untested, but you should be able to use the traditional Yii method, though. Something like this:

$myRecord->onAfterDelete = function (Event $event) {
    // Do your thing.
};


How to check if object property exists On Craft 3 I am iterating over matrix elements and I need to check if a certain property "videoPoster" exists. It's an asset field, so I actually want to check its length, but I'am already failing to check if it's available...

"ce" is one block of my matrix, this is what I'm doing:

{{ ce.videoPoster is defined ? 'yes' : 'no' }}

Works, gives me a nice "Yes"

Anything else does not work:

{{ ce.videoPoster is defined and ce.videoPoster ? 'yes' : 'no' }}
{{ ce.videoPoster is defined and ce.videoPoster is not null ? 'yes' : 'no' }}
{{ ce.videoPoster is defined and ce.videoPoster | length ? 'yes' : 'no' }}

Exception: Calling unknown method: craft\elements\MatrixBlock::videoPoster()

What is the correct check? I just want to know if I can call ce.videoPost.one()

Just found the trick: When you access an object in array style, you can test via is defined

e.g.

{{ object['property'] is defined ? 'defined' : 'not defined' }}

and you will get reliable results.


Is there a way to see a fields variant relation in templates? To clarify; if I have a disabled variant, and I'm looping through categories (colors) on each product like {% for variantColor in variantColors  %}...{% endfor %} which makes it still show up in the list of colors.

Is there a way to, in that for loop, check if the variant it's connected to is enabled/disabled and then disregard, similar to {% for variant in variants if variant.enabled %}?

yes, you could filter the variants for the status enabled to output only the enabled variants of the product.

 {% set products = craft.products.all() %}
 {% for product in products %}
     {{ product.title }}

     {% set variants = product.variants|filterByValue('enabled', "1") %}
     {%- for purchasable in variants -%}
          {{ purchasable.id }} - {{ purchasable.description }} - {{ purchasable.price }}

     {%- endfor -%}
 {% endfor %}


Plugin, getting an entry when one of multiple category fields and/or lightswitches are not empty/turned on Basically I’m trying to get en Entry where, either one (or more) of three lightswitches is (are) turned on, or where either categoryOne or categoryTwo is not empty.

This is what I got so far, but it doesn't seem to work.

$criteria = [
    'section' => 'news',
    'limit' => 100,
    'search' => ‘lightswitchOne:1 OR lightswitchTwo:1 OR lightswitchThree:1’,
    'categoryOne' => ':notempty:’,
    'categoryTwo’ => ':notempty:’,
    'id' => 'and, not 1, not 2’//Also exclude a couple id's
];
$criteriaModel = craft()->elements->getCriteria("Entry");
$criteriaModel->setAttributes($criteria);
return $criteriaModel->find();

Is there a way to do this is in Craft?

If found the answer. You can use an * operator in the search query to check if a category field is not empty. So the solution in my case was:

'search' => lightswitchOne:1 OR lightswitchTwo:1 OR lightswitchThree:1 OR categoryOne:* OR categoryTwo:*


Contact Form Plugin how to send to different recipients based on country selected? I'm trying to build a plugin that fires on the Contact Form Plugins contactForm.beforeSendwhat the plugin needs to do is add a recipient email if certain countries are selected. So far I have created the below which checks for the countries and checks against an array to see if they are in it. This works, so far so good. What I need to do now is somehow add to the contact form plugins $toEmails variable if a country is in the array but I'm not sure how to do this from within the event listener. Is this even possible?

public function init() {
    craft()->on('contactForm.beforeSend', function(ContactFormEvent $event) {
        $message = $event->params['message'];
        $ukArray = array('United Kingdom','Wales','Scotland','Ireland');
        $country = '';
        foreach ($message as $key => $field) {
            if($key == 'messageFields') {
                $country = $field['Country'];
            }
        }
        if (in_array($country, $ukArray)) {
            // add an email address to the contact form $toEmails array
        }
    });
}

I'm not sure this is going to be possible with the current version (1.8.1) of Contact Form.

The only thing passed into the beforeSend event is the message, so it doesn't really give you a chance to modify other properties of the email.

Maybe you could pull it off by dynamically adding these addresses with some JavaScript that checked selected countries and added to post, but those values are expected to be |hashed.

You could always just fork the plugin and modify it for your specific needs, too.


Multi domains in Craft 2 licensing I've setup multi domains with a single Craft 2 core folder using the Path Constants methods in the docs. It's seems like a nice setup as I only have to update the core Craft files once when there's an update for all the domains. But under this setup all the domains have their own separate control panel and plugins but share the core Craft app folder.

The reason I've done this is that my client wants to be able to add micro sites in the same install as their main company site for specific marketing.

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

So am I right in assuming that my setup will be ok for example.com, example.co.uk, subdomain1.example.com, subdomain2.example.com, example.fr etc, but if the client wants to use a completely different domain name for the micro site like brilliantmarketingurl.com then the license won't be valid? As each site has it's own control panel and database with this setup.

Is this the case and is this still applicable with Craft 3 introducing multi-sites support?

Would appreciate some advice on this, as I need to discuss the potential implications with my client. Thanks.

Using this technique to share the Craft app folder across different sites, results in each site having their own config folder. Since the license.key file that determines the licensing status of Craft lives in the config folder, setting up your sites like this will require a separate license for each site, as the domain2 site won't have access to the license.key file in domain1/config.


Twig: Show specific entry by url my url looks like this:

domain.tld/slug-1/slug-2/variable

And in Twig it shall look for an entry with the slug slug-1 and show it, then an entry with the slug slug-2 and show it and look up for a global where the name of the global is variable.

How do I do this? I found:

{% set entry = craft.entries.section('section_handle').slug('my_slug') %}

But how do I tell Twig whats the first and whats the second slug?

There are always 2 slugs and one variable.

Thank you!

You can get the parts of your URL using the getSegment() function. https://craftcms.com/docs/templating/craft.request#getSegment

Then use the slug parameter and the criteria model’s first() method to grab the entry. Global Sets are already preloaded, to access a set dynamically grab it from the _context variable.

{% set entryA = craft.entries.slug(craft.request.getSegment(1)).first() %}
{% set entryB = craft.entries.slug(craft.request.getSegment(2)).first() %}
{% set globalSet = _context[craft.request.getSegment(3)] ?? null %}

{% if not entryA or not entryB or not globalSet %}
    {% exit 404 %}
{% endif %}

{{ entryA.title }} - {{ globalSet.myField }}


Importing Unix timestamp into date time field I have a Unix timestamp in my feed (1457685095000) and I am trying to map it to the date created entry field and I have another that I am trying to map to a time input field.

The created date is setting the date to a wildly incorrect value and the time field isn't getting set at all.

Any help would be appreciated.

According to the documentation for the Date field, Feed Me uses date_parse to parse a provided date string into a proper date object. date_parse uses strtotime underneath, which seems to have issues with timestamps.

I just wanted to confirm your timestamp though, as it seems to not produce the correct date for me using:

echo date('Y-m-d H:i:s', 1457685095000);

// Results in
48162-03-15 06:23:20


Stop currency translation in Craft Commerce I’m creating a multi language site (Arabic/English) with Craft Commerce. On the Arabic cart page I output item.total with the help of

{{ item.total|commerceCurrency(cart.paymentCurrency, convert=true, format=true) }}

The price is output like so: "دولار أمريكي 5.00".

It translates $ to دولار أمريكي) دولار أمريكي = American dollar) and I want it to be output like so: "$5.00".

I don’t think it is possible to stop the filter to translate the $ symbol. I don’t know Arabic, but it seems weird to me that the filter does that. If you agree you could consider sending a feature request to Pixel and Tonic.

To work around the issue now you could set the format option to false and only use the commerceCurrency filter to convert the value. Then format it for the output manually with help of the number_format filter.

{% set total = item.total|commerceCurrency(cart.paymentCurrency, convert=true, format=false) %}

{% if cart.paymentCurrency.iso == 'usd' %}
    ${{ total|number_format(2, '.', ',') }}
{% elseif cart.paymentCurrency.iso == 'eur' %}
    {{ total|number_format(2, '.', ',') }} EUR
{% endif %}

Here is how you can apply the manual formatting to select currencies only.

{% if cart.paymentCurrency.iso == 'usd' %}
    {% set total = item.total|commerceCurrency(cart.paymentCurrency, convert=true, format=false) %}
    ${{ total|number_format(2, '.', ',') }}
{% elseif cart.paymentCurrency.iso == 'eur' %}
    {% set total = item.total|commerceCurrency(cart.paymentCurrency, convert=true, format=false) %}
    {{ total|number_format(2, '.', ',') }} EUR
{% else %}
    {{ item.total|commerceCurrency(cart.paymentCurrency, convert=true, format=true) }}
{% endif %}

It’s probably a good idea to use a Twig macro for this snippet.


Invalidate cache on new order I have a list of orders from Commerce 1 (Craft 2) in the frontend.

I want to invalidate the cache when a new order is placed but it doesn't work:

{% set latestOrder = craft.commerce.orders.first() %}

{% cache until latestOrder.dateOrdered  %}
   Last Updated: {{ orders.dateOrdered.first() | date('D d.m.Y - H:i') }}
   {# stuff #}
{% endcache %}

The Last Updated tag shows always the latest browser reload. What's the error here?

In your code you are only caching until the dateOrdered of the latest order. That means that viewing the page will always be ahead of the cache time.

You could look to do the following:

{% set latestOrder = craft.commerce.orders.order('dateOrdered DESC').first() %}
{% set cacheKey = 'orders-' ~ latestOrder.dateOrdered|date('U') %}

{% cache using key cacheKey %}
  {% set orders = craft.commerce.orders.order('dateOrdered DESC').find() %}

    {% for order in orders %}

{{ order.id }}

    {% endfor %}

{% endcache %}

This creates a cache key based on the date of the latest order. Therefore, when there is a new order this key will change.


Create new users with front end form and put them in a group, need help with start-elevated-session This has been a long process that I set aside to resolve "later". Well, it's "later" and the clients wants to launch asap.

Only registered users can use this site. There are 2 groups: Manager and Employee. Users in Manager group have permission to create new users and put them in Employee group. It all works in the back end.

Using the craft front end User Registration template I added a hidden field for the group id <input type="hidden" name="groups[]" value="2"> and when I submit I get "HTTP 403 – Forbidden, This action may only be performed with an elevated session."

Some googling lead me to this craftcms github comment. I get what it's saying about the elevated session and needing to send the password again but am having issues doing it. First step is testing with hard coded values on my form submit:

$.ajax({
    method: 'POST',
    url: 'https://www.myurl.ca/',
    data: { action: 'users/start-elevated-session', password: 'xxx'}
})
.done(function( msg ) {
    alert( "Data: " + msg );
});

But nothing is alerted. So something is failing.

When I try https://www.myurl.ca/?action=users/start-elevated-session&password=xxx in a new browser tab I get "TypeError, Argument 1 passed to craft\web\User::startElevatedSession() must be of the type string, null given, called in /.../vendor/craftcms/cms/src/controllers/UsersController.php on line 234".

So it can't find password value. It's just getting null. I feel like maybe I don't know what "body params" means in the github comment. Can someone explain it like I'm 5?

Thanks
Amanda

Elevated sessions are basically user sessions where the user has proven to Craft that it’s really them within the last 5 minutes. It’s used whenever Craft is about to increase permissions on a user (either directly or via a user group).

The point of the concept is to reduce the chances of malicious permission escalation.

(In English, that means if a user with certain permissions/groups walks away from their computer, elevated sessions reduces the likelihood that another user could walk up to it, find their own user account, and increase their account permissions. They’d think they’re going to get away with it right up until the point where Craft asks them to re-enter their password, which kicks off the elevated session.)

I’m explaining this so that you understand the risk in what I’m about to say.

Maybe the easiest way for you to work around this issue is to just disable elevated sessions on your site. You can do that by opening config/general.php and adding this:

return [
    'elevatedSessionDuration' => 0,
    // ...
];

If you do that, Craft will no longer require elevated sessions anywhere.

Otherwise, you need to have the user enter their current password as part of the user form, and send that off to Craft’s users/start-elevated-session action.

You can get that action URL, and the CSRF token needed to make any POST requests to Craft, and send them to JavaScript like this:

<script>
    window.startElevatedSessionUrl = "{{ actionUrl('users/start-elevated-session')|e('js')|raw }}";
    window.csrfTokenValue = "{{ craft.app.request.csrfToken|e('js')|raw }}";
</script>

Then in JavaScript (assuming you’re using jQuery),

$.ajax({
    url: window.startElevatedSessionUrl, {
    type: 'POST',
    dataType: 'json',
    headers: {
        'X-CSRF-Token': window.csrfTokenValue
    },
    data: {
        password: $('#current-password-input').val()
    },
    success: function(response, textStatus) {
        // submit the user form...
    }),
    error: function(jqXHR, textStatus) {
        // error handling...
    })
});

If you want to get even fancier you can check to make sure the user doesn’t already have an elevated session by running a POST request to users/get-elevated-session-timeout. If response.timeout is greater than 5 (seconds) or so, then it’s safe to submit the form without prompting them for their current password.


Users not being deleted from the craft_users table? Since upgrading to craft 2.6.2785 when I delete a user it appears to remove the user from the elements and elements_il8n tables but does not delete the user from the users table.

If you then search for the user in the CMS it does not find them however the user has not been removed from the database so if you try and set up a new user with the same email address it fails with "email has already been taken message".

Update on this. I re-imported the database from dev to staging. In my dev deleting users works fine. When importing the database which is fairly large due to 20k+ users I get this error:

SQL Error (1452) Cannot add or update a child row:a foreign key constraint fails ('databaseName'.'#sql-1510_62265', CONSTRAINT 'craft_matrixcontent_newscontent_elementId_fk' FOREIGN KEY ('elementId') REFERENCES 'craft_elements' ('id') ON DELETE CASCADE)

Then when trying to delete a user it says it's deleted removes the elements from the element tables but does not remove the user from the users table.

Solved this one by adding SET foreign_key_checks = 0; to the beginning of the SQL dump and SET foreign_key_checks = 1; at the end of the file. Not sure if it is the best solution but it certainly worked.


PHP 7.0 and 7.1 PDF error with Craft Commerce orders Switching over to a new server and using PHP 7.0 (and 7.1) everything looked to be fine until I tried to view an order PDF within an email from Craft Commerce. Even using the email template provided by the plugin. The error is:

No block-level parent found. Not good.

I can't remember which DOM-PDF issue thread I came across it in, but the solution is to remove

<head></head>

from the document. Does anyone know of a more elegant solution? I don't think it's my server.

I came across the same issue yesterday. After a bit of reading and trial and error I found the most elegant way for now is to remove the white space between these elements.

Personally I achieved this by adding comments  between to remove the white space

I piked this up from this thread initially.
https://github.com/barryvdh/laravel-dompdf/issues/389

I think that the proper solution might be to enable the html5 parser, but I think this might be a config setting within the DomPDF? Would this fall ino the Craft Commerce files?

"enable_html5_parser" => true,


Demo repo for an Adjuster module for Commerce 2? Would it be possible for someone to create a demo of a barebones Adjuster module, similar to https://github.com/cliveportman/craftcommerce-adjuster but for Commerce 2?

I'm trying to follow the docs but can't piece everything together.... basically just looking for the most basic module possible for something that adjust the prices of an item in the cart.

I've created a stub/reference Adjuster module, here.

This demonstrates how to register two Adjusters via a custom module—one that creates a 5% discount on each line item, and one that creates a 10% discount on the entire order.

These adjusters have their logic hard-coded, which is to say: in order to modify the behavior/value of the adjustments, you have to change PHP files. Part of the benefit of Craft's built-in adjusters (Taxes, Discounts) is that they base their calculations on input from the merchant/admin, in the control panel.

You can replace the two calls to $adjustment->amount = $myAmount; with anything you like—including Craft API calls that fetch values off of Elements, get plugin or environment settings, inspect LineItem options, etc.

This is where Adjusters become immensely powerful. As long as you can provide a positive or negative value, a label, and a description for your Adjustment, it doesn't matter how you derive it. ✌

This module is not meant to be installed as a plugin, or used as a submodule—it's probably best to just lift the code out and replace the generic classes and namespaces.

  A bit of context: James and I spoke in Slack earlier today, which prompted the creation of this repo. The code has been adapted from private projects, but is now available under the MIT license.


Why won't the Import plugin accept this JSON string? Importing data.csv using Bob Olde Hampsink's Import plugin. Given that data.csv has a 'table' item string thusly:

"Alcohol and Drug Treatment:RX-8450.3300,Counseling:RP-1400.8000-145,Mental Health Services:RR-5150.1500,Support Groups:PNPH-8100.4500-050"

(two columns, four rows. dead simple)

Lindsay tells us here that:

  a table field contains a simple JSON string

So we are writing a plugin using Import's modifyImportRow hook and here's what we have in the hook loop:

...
// Arrange your content in a way that makes sense for your plugin
foreach ($fields as $handle => $value) {

    if ($handle=="myTableHandle"){

      $rows = explode(',', $value);

      $tabledata = array();

      foreach ($rows as $row) {
        $rowarray = explode(':',$row);

        $rowdata['col1'] = $rowarray[0];
        $rowdata['col2'] = $rowarray[1];

        $tabledata[] = $rowdata;
      }

      $value = json_encode($tabledata);

   }

   $content[$handle] = $value;

}
...

The import works, but the table field data (myTableHandle) remains NULL.

What else can we do to that string of table data to get the Import plugin to accept?

You're close!

The method that Lindsey recommends should work with the following method of adding content to a field:

$entry->getContent()->setAttributes(array(
    'tableFieldHandle' => json_encode($tableData)
));

However, the modifyImportRow documentation uses the (arguably better) method:

// Set modified content
$element->setContentFromPost($content);

Using this method, you should add an array to a table field, rather than a JSON strong, since setContentFromPost() will call the relevant methods to format the field data appropriately for the database, so you don't need to worry about it.

So if you use:

$content[$handle] = $tabledata;

then add it to the element using:

// Set modified content
$element->setContentFromPost($content);

it should work.

The full code for the modifyImportRow function in your plugin should now look like this:

/**
 * Modify data just before importing
 *
 * @param BaseElementModel $element  The current element receiving import data.
 * @param array            $map      Mapping of data between CSV -> Craft fields.
 * @param array            $data     Raw data provided for this row.
 */
public function modifyImportRow($element, $map, $data)
{
    // Map data to fields
    $fields = array_combine($map, $data);

    // Initialize content array
    $content = array();

    // Arrange your content in a way that makes sense for your plugin
    foreach ($fields as $handle => $value) {

        if ($handle=="myTableHandle"){

            $rows = explode(',', $value);

            $tabledata = array();

            foreach ($rows as $row) {
                $rowarray = explode(':',$row);

                $rowdata['col1'] = $rowarray[0];
                $rowdata['col2'] = $rowarray[1];

                $tabledata[] = $rowdata;
            }

        }

        $content[$handle] = $tabledata;

    }

    // Set modified content
    $element->setContentFromPost($content);
}


Can I capture the users intent ('Save' vs 'Save and continue editing') for craft()->on('entries.saveEntry' I'm writing a plugin that gathers up all the assets from an entry and saves it out as a zip (along with a JSON file with information from the entry).  Any advice regarding a better method is appreciated, but right now I add the code in on('entries.saveEntry'), like so:

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

        /*
        business logic filling variables removed for the sake of brevity
        */

        if (($entry['sectionId'] == 3) && ($entry['buildPage'] == 1)) {
            file_put_contents(getcwd()."/build/videos.json", json_encode($outputArray));

            $assetGroup[] = getcwd()."/build/videos.json";
            $zipfile = craft()->buildPage->create_zip($assetGroup, $entry['uri'], getcwd()."/build/");
            $filename = substr($zipfile,strrpos($zipfile,'/')+1);

            if (file_exists($zipfile)) {
                craft()->request->sendFile($filename, file_get_contents( $zipfile ), array('forceDownload' => true), false);
            }
        }
    });

Everything works, except that my sendFile call prevents the user from being returned to the Entries page (or stating a new entry, etc.), if that was their intent.  Is it possible to capture where they were headed before I stopped them, so that I can send them on their way?  Or is that not possible the way I have things written?

The 4th parameter to HttpRequestService->sendFile() is $terminate = true, which will by default, terminate the request after the file is sent.

Set that to false and let the request continue as usual and you should be set.


Does Craft Support MariaDB? I am hoping craft supports MariaDB. Also, since version 10, InnoDB transaction engine in MariaDB has been replaced with XtraDB. Will this work with Craft?

Update:
Craft 3 officially supports MariaDB 5.5+ now. https://docs.craftcms.com/v3/requirements.html

Not officially, no.  MariaDB is supposed to a "drop-in" replacement for MySQL, but there are clearly areas where it is not 1:1 compatible with MySQL: https://mariadb.com/kb/en/mariadb/mariadb-vs-mysql-compatibility/

We've seen some installations using MariaDB where a query would actually return incorrect results and the same MySQL query on the same database returns that data correctly.


Unchecking Rich Text option to clean up HTML still cleaning? We've unchecked the clean HTML feature on a few rich text fields but our attempts to add some html (span tags) keep getting cleaned out. Is there another setting we should be looking for?

I came upon this issue when wrapping copy with a span tag in order to style it differently. The span tag was removed when I saved the entry.

By design, Redactor removes span tags that don't have attributes. The fix is to edit the Redactor config file and add the removeWithoutAttr config as an empty array.

removeWithoutAttr: []

This prevents tags without attributes from being removed by Redactor.


Craft 2.4.2669 and SVG's? have update craft and when uploading an SVG to Assets nothing happens? The upload bar pops up but the file is not uploaded and is not on the server?

Any ideas?  Thanks

Check your craft/storage/runtime/logs folder for the underlying error message, but most likely you're using GD instead of Imagick, which is required for SVG thumbnails/transforms to work properly.


Cart completed at gateway redirect issue Recently I've found that when a customer goes to checkout and hits the craft\commerce\controllers actionPay() using a custom gateway that it's automatically marking the cart as complete.

This never used to happen, the gateway would set the cart to complete after receiving a webhook response itself at a later time, allowing the customer to go back and update their cart before checking out properly(or not as they decide).

Now if they hit the back button they, obviously, have an empty cart as it's been marked as completed (along with the automated emails etc that are sent at that time).

Can someone point me in the right direction on how to stop this auto-complete from happening?

Just to update this with an answer, thanks very much to P&T's excellent dev team.

The model implementing RequestResponseInterface returned from the gateway's authorize() method isSuccessful() method should only return true when completing a payment.

I also had to return false on the isProcessing() method during my tests. Not sure what that method does but setting both those methods to return false fixed the issue.

It was a recent change in the commerce code that identified this bug in my gateway's code.


How to filter entries based on Tags I am working on a site that requires a filter option based on tags.
Each entry will have two sets of tags (e.g., colors and shapes)

{% for tag in craft.tags.group('colorTag') %}
{% for tag in craft.tags.group('shapeTag') %}

How can I create a filter that allows you to check boxes for those tags and display the results that match ANY of the selected tags?

The results will show entries with the Blue tag, entries with the Yellow tag and entries with the Triangle tag.

Thanks so much!

You can combine multiple relationship criteria by passing an array to the relatedTo parameter with the keyword 'and' or 'or' as the first array element. You want your entries to be related to any of the tags, so this should work for you:

{% set query = craft.request.getQuery() %}

{# Make sure selectedTagIds is an array #}
{% set selectedTagIds = query.tags ?? [] %}

<form action="">
    {% for tag in craft.tags.group('colorTag') %}
        {% set checkedAttr = tag.id in selectedTagIds ? 'checked' : '' %}
        <input type="checkbox" name="tags[]" value="{{ tag.id }}" {{ checkedAttr }}>{{ tag.name }}</input>
    {% endfor %}

    {% for tag in craft.tags.group('shapeTag') %}
        {% set checkedAttr = tag.id in selectedTagIds ? 'checked' : '' %}
        <input type="checkbox" name="tags[]" value="{{ tag.id }}" {{ checkedAttr }}>{{ tag.name }}</input>
    {% endfor %}

    <button type="submit">Search</button>
</form>

{# Prepare our relatedTo parameter #}
{% set relatedToParam = ['or'] %}

{% for id in selectedTagIds %}
    {% set relatedToParam = relatedToParam|merge([{
        targetElement: id
    }]) %}
{% endfor %}

{# Make sure to not pass the param with just the 'or' in the array #}
{% set relatedToParam = relatedToParam|length > 1 ? relatedToParam : null %}

{% set entries = craft.entries({
    section: 'foo',
    relatedTo: relatedToParam,
}) %}

{# Query for the entries and return them #}
{% for entry in entries %}
{% endfor %}


Sorting an array of entries according to a fixed list/array of items I want to sort an array of entries in a specific order. Every entry has a 'shoptype' and I want to show all entries for type A, than type B, than type C.

A partial solution would be to iterate over them (if shoptype == A){ then show all A entries} but than I can't write a conditonal around it to hide the title when there are no shops of type A.

So now I group them using the group twig filter and hide if the group has no items. But I can't sort them the way I like (it's not alphabetical, it's a fixed order that I want).

Idealy I would need something like this in supersort:

'SortAs', ['shoptype A', 'shoptype B', 'shoptype C']

So an array of strings that my shopType field can contain which my array is grouped by.

Edit: This strategy still works, but see my updated answer for more up-to-date solution.
Prior to SuperSort 3.1.0, you might achieve this by using the replace filter inside SuperSort's micro-template:
{% set foo = craft.entries.section('foo')... %}

{% set sortedFoo = foo | supersort('sortAs', '{ shopType | replace({
    'ShopType1': "A",
    'ShopType2': "B",
    'ShopType3': "C"
}) }') %}

The first parameter you pass to SuperSort specifies the sort method.
The second parameter provides a value template for the sorting function: Under the hood, the system parses { shopType | replace(...) } — a Craft 'micro-template' — into {{ object.shopType | replace(...) }}. The plugin parses that template by passing in your array item as the object and uses the output value to sort by.)
n.b. You need to specify that replacement list directly in the object template. Passing in a Twig variable doesn't work because the object template doesn't share the same context as the parent template.
The syntax in your original question would actually not be a bad addition for use-cases like this; I'll look into adding something like that to SuperSort when I get some downtime.
As of SuperSort 3.1.0, you can now use a custom-order sort method, similar to what you originally suggested.


Action URLs from CP to a Craft 3 plugin controller I'm working on my first Craft 3 plugin and want to add some buttons to my plugin's control panel section which post form data to a controller.

My plugin is called 'myplugin', the controller I wish to post to is called 'products' and the method in that controller is 'get-products'. I figured I should therefore create the URL on the forms as

{{ actionUrl('products/get-products') }}

However this yields a 404 error

HTTP 404 – Not Found – yii\web\NotFoundHttpException
Page not found.
↵
Caused by: Invalid Route – yii\base\InvalidRouteException
Unable to resolve the request "https:/local.mydomain.co.uk/index.php?p=admin/actions/products/get-products".
in /Users/pete/projects/myclient/dev/build/vendor/yiisoft/yii2/base/Module.php at line 537

If I call the URL directly by using https://local.mydomain.co.uk/admin/actions/myplugin/products/get-products then it behaves as expected.

Obviously I'm missing some really important bit of knowledge here and would love if someone could help me understand what's going on.

I think you should use {{ actionUrl('myplugin/products/get-products') }}.

But in a form, it's maybe even better/easier to use:

<input type="hidden" name="action" value="myplugin/products/get-products" />


Craft 3 with Multi-Site: how to not set a primary site, or add a first segment to all primary site URLs? I have a main domain, www.mycompany.com, which has several groups of sites, which are available in several languages:

www.mycompany.com/international

www.mycompany.com/sales/en
www.mycompany.com/sales/fr

www.mycompany.com/customers/en
www.mycompany.com/customers/fr

etc.

Once visitor access to www.mycompany.com, he will be auto-redirected to a Site version depending on his IP (probably using a third-party plugin like Country Redirect, once available for Craft 3).

The problem is that when I set up the Sites, Craft force to set a primary Site. So I choose the www.mycompany.com/international version.

But when Site is saved, the Base URL is displayed as www.mycompany.com instead of www.mycompany.com/international, and all URLs in this one are now like www.mycompany.com/my-slug instead of www.mycompany.com/international/my-slug.

This probably can be solved by pre-checking all links, adding the slug inside the URL if current Site id is the www.mycompany.com/international version, then adding a routing rule for all /international/* requests.

But it sounds a bit tricky in the some cases, so I wondered if there was a cleaner way to achieve this?

It looks like you forgot to setup siteUrl in config/general.php for your sites.

'siteUrl' => [
    'landing' => 'https://www.mycompany.com/international',
    'sales-en' => 'https://www.mycompany.com/sales/en',
    'sales-fr' => 'https://www.mycompany.com/sales/fr',
    ...
],


Found cool example of Header Globals what field type is used for this? So I was reading an article on craft, and found an example of something I am trying to do. Creating a global for header/footer links etc so they can be managed dynamically.

Here is a screenshot of what they did.

I setup a Header global and Header goup for a matrix field. Any idea what field type they used to Add a row for the links?

I can't think of any field options in this matrix block to allow this kind of option. Seems like maybe this would be a custom plugin field type or something that creates this kind of option. Clicking Add a row could popup a modal to choose an entry, then inserts the url and label with the label being editable.

Anyone have any idea how they did this?

Update

I ended up finding a Craft menus plugin which is great and is now what I am going to use for my menus. You can find it here if anyone is looking for a menu system:

  https://github.com/familiar-studio/craft-menus

Looks to me like a matrix field with 1 block (Navigation) and two fields :

Plain Text - Headline
Table - 2 columns with headings Label (Single-line text) and URL (Single-line text)

It's worth noting that the table field doesn't have a relations column, so they're probably inserting these URLs manually.


How can I return specific matrix rows based on criteria? So let's say I have the following code. I have the data to retrieve the entry I want, But I only want to return some of the matrix rows based on some criteria.

Example Code:

{% set entryId = 1 %}
{% set entry = craft.entries({ section: "mySection", id: entryId }).first() %}

{% set myMatrixOutput = entry.myMatrixField.find({ someFieldInTheMatix: 2 }) %}

{% for row in myMatrixOutput %}
    {{ row.someFieldInTheMatrix }}
{% endfor %}

I can't get that to work it simply return me everything. I know that I could adjust my loop to be

{% for row in myMatrixOutput if row.someFieldInTheMatix == 2 %}

But say I have a large number of rows in the matrix field it would seem a waste to do all that looping if it could just bring back the correct results?

As far as I know, the only criteria to filter Matrix blocks is the type property or to pick a block by it's position with first, last or the new nth filter.

Yes, it would totally make sense to have more options to grab select Matrix blocks and I am rather confident that this is s.th. that is already on "The List".

Update:

Craft 2.3 change things! Besides using the type property or to pick a block by it's position with first, last or the nth filter it is now possible to query for Matrix blocks using all sorts of custom field parameters.

{% set entry = craft.entries.slug('myMatrixEntry').first() %}

{# Query Matrix blocks using custom field parameters #}
{% set blocks = entry.myMatrixField.type('text').myLightswitchField('1').myDateField('> 2014-12-02') %}

{% for block in blocks %}
...
{% endfor %}


SQLite database support Is there any plan on supporting SQLite someday? It would be very interesting for small instances where we can't create any MySQL or MariaDB databases for example (in my case, free tier hosting coming with the domain name which offers PHP, 30Mb storage but no MySQL database).

Thanks a lot!

We've looked into it before as I agree, it'd be awesome. But as of February 2020, the answer is no because SQLite is missing some key features that Craft currently relies on: https://www.sqlite.org/omitted.html

Specifically the missing joins and alter table commands.


Solspace Calendar Locations I could not find it in the docs and hope someone that has used it before can answer it.

How does Solspace Calendar handle location data.  I did not see a way to add a location like Venti 2 has.  Right now I am using VZ Address in conjunction with Calendar. Is that the best way to go about achieving an address for an event with Solspace Calendar?

There currently isn't any built in Location field for Calendar. We made add something like that in the future, but for now you can definitely use other third party field types such as VZ Address to handle this. :)


Image transform that only crops if image is larger than set dimensions At the moment I have an image 300px wide, my transform is set to a width of 600px so it upscales it.

I would like it to only transform if the image is over 600px.

Is this possible or should I use some other image manipulation thing like phpthumb?

You can easily do this in twig with a conditional that checks if the width of the image is larger than your target transforms width. Try something like this:

{% if image.getWidth()>600 %}

{% else %}

{% endif %}


Best method of text truncating I see that there are Trimmer and Truncate out there that have been created for truncating text but I don't believe either does what it is I'm looking for.

I'd like to truncate text similar to how Hacksaw for ExpressionEngine works. Allowing me to allow or disallow html tags, and making sure that even if it cuts off before a tag is closed, that it automatically properly closes the tag. I wouldn't want to turn the entire lower half of my page into a hyperlink for instance if i happen to truncate mid-link. I do want to maintain the ability for links to be in the text that is being truncated, and multiple paragraphs, etc. however.

Any tips would be fantastic.

https://github.com/ryanshrum/hacksaw

This hasn't been fully tested for production, or documented for that matter, but I have migrated it over and tested basic usage as per the docs on devot-ee. It should, at the very least, give you a nice start.

You can use the Twig filter with: {{ entry.fieldHandle|hacksaw(words="100") }}


Recommended approach for integrating a CDN with a Craft site I realize there are lots of options out there, but I’d be interested to know what service folks would recommend for integrating a CDN with Craft?

It'd also be helpful to know what the general pain points, strengths and/or weaknesses of a given approach are (especially as it relates to Craft integration).

It's extremely easy if you're using a pull CDN like MaxCDN or KeyCDN. You just point your CDN at your server, and then point your Asset Sources at your CDN.


How to output a placeholder image if an Assets field is empty I'm trying to output an Asset image if the Main Image field is populated, and if it's not populated, I'd like to output a placeholder image. But I'm having problems getting the conditional to work - it outputs the image in the Assets field, but when the field is empty, it's not outputting the placeholder image. Any ideas on what I'm doing wrong here? Thanks.

{% if entry.mainImage %}
  {% for asset in entry.mainImage %}

  {% endfor %}
{% else %}

{% endif %}

Thanks to Brad for the answer, and the solution was to use {% if entry.mainImage|length %} instead of {% if entry.mainImage %} for the conditional. So my working code looked like this:

{% if entry.mainImage|length %}
  {% for asset in entry.mainImage %}

  {% endfor %}
{% else %}

{% endif %}


Saving/assigning User Group via a plugin Anyone had experience of assigning a user group when saving or registering a user via a plugin?

Tried various combinations of $user->group = 1, $user->group = [1], $user->setGroups([$group]) and $user->setGroups($group) (where $group is the group I want to assign the user to) but whilst the user is being saved fine the user group is not assigned.

Any thoughts appreciated!

Cheers,

Cole

So it seems the only way to achieve this is to assign the user to the group, not the other way round (assign the group to the user).

So once my user has saved I can call following:

craft()->userGroups->assignUserToGroups($user->id, $groupId);


Excluding a single Entry ID of an Entry Type from craft.entries Any help here would be appreciated… I feel like this is really close.

I have 4 entry types, all of which are shown in a blog index:

Article
Video
Image
Blurb

The most recent entry of the Article type should always be shown as a "featured article" at the top of the page and never repeated again; all the other entry types should appear below this single featured entry in chronological order. The featured article should only appear on the first page of the index, and never appear on subsequent "pages" because this page also uses infinite scroll.

The problem is that the featured article shows up again on all pages past the first. When I remove pagination, but use the same nonFeaturedIds parameter to get my entries, everything works perfectly.

In other words, it seems that the "featured article," is not properly being excluded from Pagination by the entry paramaters I've set up.

Thank you!

{# Define a variable for all the post types #}
  {% set allIds = craft.entries.section('news').ids() %}
{# Define a variable for the featured article: the first occurance of the article entry type #}
  {% set featuredId = craft.entries.type('article').limit('1').ids() %}
{# Define all other entries and entry types as non featured #}
  {% set nonFeaturedIds = allIds|without(featuredId) %}

{# Get the featured entry #}
  {% for entry in craft.entries.id(featuredId) %}
    {% include '/partials/_featuredArticleTemplate' %}
  {% endfor %}

{# set paramaters on an elementCriteriaModel to get all non featured articles and paginate #}
  {% set entries = craft.entries.id(nonFeaturedIds) %}
  {% paginate entries.limit(10) as pageCount, nonfeatured %}

{# Take us to 404 if the page doesn't exist. Otherwise, Infinite scroll keeps repeating the last page of content. #}
  {% if craft.request.getPageNum() > pageCount.totalPages %}
    {% exit 404 %}
  {% endif %}

{# load up the templates for each entry type #}
  {% for entry in nonfeatured %}
    {# all the other articles #}
    {% switch entry.type %}
      {% case "article" %}
          {% include '/partials/_articleTemplate' %}
      {% case "video" %}
        {% include '/partials/_videoTemplate' %}
      {% case "image" %}
        {% include '/partials/_imageTemplate' %}
      {% case "blurb" %}
        {% include '/partials/_blurbTemplate' %}
      {% default %}
    {% endswitch %}
  {% endfor %}

{# infinite scroll loading area #}
  {% if pageCount.nextUrl %}
    {# pagination styles #}
  {% endif %}

You'd have to add a conditional and render the featured entry on the first paginated page only. You can access that info from the pageInfo variable.

Another thing I'd do is to remove the featured entry from the all entries criteria model by prepending the ID with 'not' in the id param (i.e. id('not 72')). You can probably reduce the amount of database queries with that change.

{# Get the featured entry #}
{% set featuredEntry = craft.entries.section('news').type('article').first() %}

{# Get all other entries #}
{% set idParam = 'not ' ~ featuredEntry.id %}
{% set entries = craft.entries.section('news').id(idParam) %}

{% paginate entries.limit(10) as pageInfo, pageEntries %}

{# Render the featured entry for the first page only #}
{% if pageInfo.currentPage == 1 %}
    {% include '/partials/_featuredArticleTemplate' with {
        entry: featuredEntry
    } %}
{% endif %}

{# Render all the other entries #}
{% for entry in pageEntries %}
    {# include partial #}
{% endfor %}


Variable for User Field How do I go about using a dynamic value to access a user field value? I have created a form builder for users and dynamically populate the user field based on the question's associated field.

<input name="fields[{{ question.fieldName }}]">

This works nicely but I am looking to pre-populate this input with a value if the user has previously provided and saved this info but I'm having trouble figuring out how to combine the field name with currentUser.

My first try was:

{% set fieldName = question.fieldName %}
{{ currentUser.fieldName }}

But that returns the following error:

Craft\UserModel and its behaviors do not have a method or closure named "fieldName".

Any help would be greatly appreciated

You could try using the attribute function in twig, this should let you call dynamic properties.

Something like:

{{ attribute(currentUser, question.fieldName) }}


Error declaring TLS Version wrong in Charge plugin I am getting an error notice in the Squarebit Charge plugin saying This server doesn't support TLS 1.2. but when I check with the hosting provider (Arcustech, the best!) they assure me that the server does indeed support TLS 1.2

Further, I do not see any errors when I look at a charge detail in the connected Stripe account. This has just recently popped up on a few of my projects.

Make sense to anyone?

Stripe changed up some API URLs, which threw off Charge's legacy method for detecting TLS compatibility.

The erroneous warning is fixed in Charge 2.2.3.


Non-numeric characters in number field producing errors Craft 3 is giving me this error when my client enters non-numeric characters into a number field on an entry:

  number_format() expects parameter 1 to be float, string given

Is there a way for it to check and remove non-numeric characters from the field on save? Or maybe my setup is incorrect?

Just tested this on a local Craft3 setup and here's what I discovered...

If the decimal points setting on the number field is set to 0, adding text in the number field will display the pretty errors below:

If the decimal points setting is set to anything else, you get the Twig error mentioned in the title of this post:

  number_format() expects parameter 1 to be float, string given

I'd recommend opening an issue in the Craft CMS repo as I imagine the number field ought to display the pretty errors no matter if decimal points is set or not.


How to display decimals as fractions? Is there a twig filter to display decimal values as fractions?

Turns out there's a plugin for that. Typography, by nystudio107.
https://nystudio107.com/docs/typogrify/#using-typogrify
===== edit =====
I was never able to get this working with typogrify. It never treated a period immediately surrounded by numbers as a decimal point.
It did inspire me to write a plugin, Fractionalize, to provide a '|fractionalize' filter which does the trick.


Changing parent page to a single Help!

I have a parent page called 'Employment' that is a structure - it has 8 sub pages beneath it. I have just created a new single template and would like this to replace this page - is this possible?

Would really appreciate the help!

As a more detailed reply to your comment:
You don't need to create a completely new section in order to have a different template. For Example when you create a section called 'Employment' you can have several different entryTypes in this section. In your /admin/settings/sectionsyou can see all your sections (left) and when you click on their entryTypes on the right you'll enter this page /admin/settings/sections/<sectionId>/entrytypes to edit them.

With the following folder structure you can do this:
|-/templates
|--/pages
|---_entry.twig
|---/entryTypes
|----imprint.twig
|----contact.twig
for example your section is called pages and you have the entryTypes imprint and contact.

Your section template is /pages/_entry.twig

{% extends [
    'pages/_entrytypes/' ~ entry.type ~ '.twig'
] %}

so depending on your entryType the template for imprint or contact will be rendered.

In your case you would create a folder Employment and create different entryTypes for the different templates you want to render. In your Employment you render the template depending on your need.
You can also extend your file to

{% extends [
    'pages/_custom/' ~ entry.slug ~ '.twig',
    'pages/_custom/' ~ entry.someCustomFieldValue ~ '.twig',
    'pages/_entrytypes/' ~ entry.type ~ '.twig',
    'pages/_generic.twig'
] %}

so you can choose you template via slug/dropdown lists/checkboxes or whatever you want


Craft on WSL permissions error For anyone installing install Craft on WSL (Windows Services for Linux)...

WSL has enough traction with developers that I decided to take the dive, but I'm having trouble getting permissions straight with my Craft install.

Apparently, PHP doesn't have permission to chmod inside the project folders, which generates this error on login to Craft.

  Failed to change permissions for directory
  "/mnt/c/Users/jimme/sites/mywebsite/craft/web/cpresources/6d799f01":
  chmod(): Operation not permitted

  Caused by: yii\base\ErrorException
  chmod(): Operation not permitted
  in /mnt/c/Users/jimme/sites/jm/vendor/yiisoft/yii2/helpers/BaseFileHelper.php >at line 646

UPDATE:

I've modified my /etc/wsl.conf file to add support for linux metadata, which allows permissions to be set granularly for files in the windows filesystem. I've also changed changed the ownership to myuser:www-data and added group write perms. After making this change it fixed the above error but now I'm getting this on login...

  HTTP 400 – Bad Request – yii\web\BadRequestHttpException Unable to
  verify your data submission.

  in /mnt/c/Users/jimme/sites/jm/vendor/yiisoft/yii2/web/Controller.php

any ideas?

This worked well for me!
Be sure to change directory out of the drive you want to unmount first!
Just to keep it all together here is what you need to do -

sudo nano /etc/wsl.conf

Paste this into the open file -
[automount]
enabled=true
options=metadata,uid=1000,gid=1000,umask=022

Then paste and enter these one at a time into Terminal -

sudo rm /your_craft_path_to/cpresources
cd /var/www
sudo umount /mnt/c
sudo mount -t drvfs C: /mnt/c -o metadata
sudo service apache2 restart

Head to your Craft URL and et voila!


Login redirects to home page after 2.2 update Since updating to Craft 2.2, my front-end login form now redirects to the home page.

The intended behaviour is to just reload the page that the visitor was on when they logged in.

In Craft 2.1 I achieved this by omitting the hidden redirect field. Any idea how to prevent members from being kicked to the home page now?

Thanks!

As Brad said the new default behavior is for users to be redirected wherever postLoginRedirect is set to (if a redirect param was not in the POST data).

You can get your old behavior back by adding that hidden redirect param though, set to craft.request.getPath():

<input type="hidden" name="redirect" value="{{ craft.request.path }}">


Can I disable the control panel? Is it possible? I have a situation where a client needs the production site to be completely shut down and void of content updates. They do all of their content management on a staging site, then publish the database when changes are approved.

Good call Brad! Not sure why that didn't occur to me :)

Here's how that's done:

# Kill admin access
RewriteCond %{REQUEST_URI} ^(.*)/admin
RewriteRule ^(.*)$ - [F,L]