Serving Craft from a sub-directory on Nginx I'm starting with a clean install, empty database.

What would be the changes needed to this Nginx configuration to serve craft from /articles/ instead of "/"?

Any specific configurations that needs to be done to the Craft configs?

I've tried many combinations that all ended in 404.

Current layout:

/srv/www/app/craft <- "craft files"

/srv/www/webroot <- "public webroot"

/srv/www/webroot/articles/ <- "craft index.php here (with path set correctly to craft)".

Thanks.

If you want to just adjust the folder for the webroot set the root to

/srv/www/webroot/articles/

And in the index.php file make sure the path to the craft folder is

'../../app/craft'

UPDATE:

If you instead want to leave the webroot as is and just have craft run from yourdomain.com/articles you would need to also update the index.php to make sure the craft path is set correct

and in the nginx file add this

location /articles/ {
  try_files $uri $uri/ @rewrites;
}

location @rewrites {
   rewrite ^/articles/(.*)$ /articles/index.php?p=$1 last;
}

put that directly above this line, everything else can be the same

location ~ \.php$ {


500 Error with no details on site migration I'm trying to migrate my site from a PHP5.6 host to a PHP 7 host in preparation for Craft 3.

I've copied everything across and am trying to load an exact copy of the production site on a different domain to see if it's all working as expected.

Unfortunately I don't make it far.
On hitting the site I get:

PHP Fatal error:  Uncaught Error: Call to a member function run() on integer in /blah.com.au/htdocs/craft/app/index.php:62

Stack trace:
#0 /blah.com.au/htdocs/public/index.php(19): require_once()
#1 {main}
thrown in /blah.com.au/htdocs/craft/app/index.php on line 62

There are no other details anywhere.
To avoid cache issues I've also done a fresh upload of Craft and then copied across my config, templates, plugins, public directories etc.
Exact same error.

I've updated the config files (db/general) to have the correct paths for this new domain. The database is the same as production.

my .htaccess shouldn't be causing issues.

Any ideas peoples?

It turns out my MySQL wasn't installed correctly.
I'm using RDS but my setup didn't meet the basic requirements to connect to it from PHP.


Setting unknown property when trying to join table I've successfully joined a table to the elements table, however I can't can't seem to add a property so I can use it?

Getting the error Setting unknown property when trying to join table

Entry::find()
    ->addSelect(['craft_event_instances.instance_datetime'])
    ->leftJoin('craft_event_instances', '`craft_event_instances`.`entryId` = `elements`.`id`')

Thanks

You can't set a property of an element in Craft that does not exists in the field layout, as a behavior or as a public/private attribute in the object. Craft/Yii2 will throw an exception because the property instance_datetime probably does not exist.

In order to attach a custom value you have to either create a new element or you have to store this information somewhere else.

Edit

You could the following as well to fetch your values and store them into a new array

$query = Entry::find();
$columns = $query
    ->addSelect('craft_event_instances.instance_datetime')
    ->innerJoin('craft_event_instances', 'craft_event_instances.entryId = elements.id')
    ->createCommand()
    ->queryAll();
$yourCustomValues = [];
foreach($columns as $key => $column){
    $yourCustomValues[$column['id']] = $column['instance_datetime'];
    unset($columns[$key]['instance_datetime']);
}
$elements = $query->populate($columns);


Query Entries by Tag I want to only show entries that match a specific tag. I'm trying to do something like:

{% paginate craft.entries.section('blog').relatedTo('tagName').order('score') as entriesOnPage %}

This doesn't seem to net any results though. Is .relatedTo() not the way to go when limiting results to a specific tag?

You'd need to get the tag model first.

{% set tag = craft.tags.search('My Tag').first() %}
{% if tag %}
    {% set entries = craft.entries.relatedTo(tag) %}
{% endif %}

Replace search with whatever other information you have from the tag, to get more precise results, e.g., slug, id properties.


Redactor - can't link to files I have added the "file" option in my Standard.json file for Redactor.
Wehn using the Redactor field then I can click on the file button and select the image ok.
When I view source the file is linked to fine in the html.
But when I save the entry and go back to view it - the link is stripped out - it just displays:

link text

I have tried disabling Clean Up HTML and Purify HTML on the field options and saving again but still have the same problem - the link is being stripped out.

Anyone have a similiar issue?

I've just had this problem. Are you using AWS for your files?

If yes, check your Asset Source "Base URL" does not start with // otherwise the file link in redactor will start with https://// and break (resulting in null). I simply changed this to https:// and Redactor was happy.

Hope that helps.


Old Posts Not Appearing in XML Sitemap Running into a strange issue with the XML sitemap on our site that I'm hoping for help with. Here is example code for the blog section of our site in our sitemap:

{% set blogPosts = craft.entries.find({ section: 'blogPosts'}) %}

{% for entry in blogPosts %}
    <url>
        <loc>{{ filteredUrl(entry.url) }}</loc>
        <lastmod>{{ entry.dateUpdated.w3c() }}</lastmod>
        <changefreq>{{ changeFreq }}</changefreq>
        <priority>1</priority>
    </url>
{% endfor %}

For context, I believe that the sitemap template was built in roughly early December of last year as part of our site redesign. At the time we had yet to import a variety of older posts from our old website blog, but did so after the sitemap had been coded.

I'd like our sitemap to show those old posts created before the early December timeframe but, unless I change the "post date" to be after Dec 2014, the post will not appear. This is true even if I make a modification to the page, thus changing the dateUpdated field (which populates <lastmod>). If I change the post date of an old post to 2015, it will appear on the sitemap (with <lastmod> showing the current date), but if I change it back to 2014, it disappears.

There's no code that I can see in the template that sets a required post date timeframe for appearing on the sitemap.

What can I do to fix this issue?

Thanks!

Default limit is 100, try adding .limit(null) to the query


How can I cache a list of entries with pending postDates and have it automatically invalidate the cache appropriately? I have a listing page of entries, some of which may have future-dated postDates. I want to cache the output, but only until one of the pending entries goes live.

Easy! Get the most soonest upcoming pending entry, and use until:

{% set firstPending = craft.entries({
  section: 'articles',
  status: 'pending',
  order: 'postDate asc'
}).first %}
{% set cacheUntil = firstPending ? firstPending.postDate : now|date_modify('+1 year') %}

{% set content %}
    {% cache until cacheUntil %}
        ...
    {% endcache %}
{% endset %}

Note that this solution requires Craft 2.2.2598 or later.


How do I add meta title/description and intro text to a Channel index page? I've created a Channel with templates blog/index and blog/_entry. Template blog/index will list the blog articles and blog/_entry will display individual blog articles. I want to have meta title/description and some intro text on the blog/index page that is managed by Craft. How can this be done?

From the docs:

Singles

Singles are used for one-off pages that have unique content requirements, such as:

home page
an About Us page
a Contact Us page

Unlike the other section types, Singles only have one entry associated with them, and they don’t have an editable Author, Slug, Post Date, or Expiration Date.

So create the fields you need, create a new single for your index page, add the fields to the single, set the single to load your index template, then code your new fields into the template


RexEg Pattern to Exclude Using the Retour Plugin The Retour plugin has this setting: Exclude Patterns - Regular expressions to match URLs that should be excluded from Retour.

How would I exclude images (jpg and png) from being handled by Retour? My statistics are full of 404s from old Asset transforms that I used before switching to the Imager plugin.

I tried adding this (which I found here: https://regexr.com/2ukfb): (http:\/\/[0-9a-zA-Z\.\/_-]+.png|http:\/\/[0-9a-zA-Z\.\/_-]+.jpg)

But that didn't seem to work. Images with a 404 were still logged in the Statistics section of Retour. Thanks in advance for any help.

The RegEx is only the URI part; so something like (.*.png|.*.jpg) is probably what you want.


How to output siblings including the current entry? I'm trying to output all the siblings of the current entry in a multi level structure like this:

{% for sibling in entry.siblings() %}

{{ sibling.link }}

{% endfor %}

This works well, but how can I make this output all the siblings and the current entry?

I know I could add:

{{ entry.link }}

before or after the sibling.link, but that would mess up the sort order.

If this can't be done this way, maybe I could somehow output the current branch/depth using a different approach? Any suggestions?

What about doing it like so:

{% set parent = entry.getParent() %}
{% set siblingsPlusMe = parent.getDescendants(1) %}

{% for entry in siblingsPlusMe %}
    <li>{{ entry.link }}</li>
{% endfor %}


Simple average calculating formula 1.{% set totalDeliveryTime = 0 %}
2.{% set countDeliveries = 0 %}
3.{% set averageDeliveryTime = 0 %}
4.{% for order in pastOrders %}
5. to--{{order.orderDeliveryTime}}--
6.{% set totalDeliveryTime = totalDeliveryTime + order.orderDeliveryTime %}
7.  td--{{totalDeliveryTime}}
8.{% set countDeliveries = countDeliveries + 1 %}
9.  tc--{{countDeliveries}}
10.{% endfor %}
11.  result--{{ totalDeliveryTime / countDeliveries }}

Result:
to--15--
td--1
tc--1

to--30--
td--2
tc--2

to--60--
td--3
tc--3

to--60--
td--4
tc--4

result--1

Its a simple average calculating formula. I want to add my all 'orderDeliveryTime' in 'totalDeliveryTime' through line 6. As in line 5, it returns correct values of orderDeliveryTime which is in 'to' you can see in Result but when we add in totalDeliveryTime through line 6 it return 'td' values (please look 'td' in Result). I think it should 15+30+60+60 (addition of all orderDeliveryTime).

Beside this I also used

{% set ratingsSum = 0 %}
{% if pastOrders | length %}
{% for order in pastOrders %}
{% set ratingsSum = ratingsSum + order.orderDeliveryTime %}
{% endfor %}
{% set ratingAverage = ratingsSum / pastOrders | length %}
AverageTime:{{ratingAverage}}
{% endif %}

https://cliveportman.co.uk/code/calculate-average-of-numbers-in-craft-cms-using-twig
But it also returns the same result which is 1.

You can try the following code

{% set ratingsSum = 0 %}
{% for order in pastOrders %}
    {% set ratingsSum = ratingsSum + order.orderDeliveryTime.value %}
{% endfor %}

{% set ratingAverage = ratingsSum / pastOrders | length %}
AverageTime:{{ratingAverage}}

Difference between yours and my code is i have added .value with order.orderDeliveryTime


CDbException: What does this mean? This just happened, but just one time. Any idea why?

CDbException
CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server during query. The SQL statement executed was: SELECT id, groupId, name, handle, context, instructions, translatable, type, settings FROM craft_fields WHERE context = :context ORDER BY name. Bound with :context='global' (/Users/johndoe/Desktop/Projects/websitetest/craft/app/framework/db/CDbCommand.php:543)
0 /Users/johndoe/Desktop/Projects/websitetest/craft/app/framework/db/CDbCommand.php(396): CDbCommand->queryInternal('fetchAll', Array, Array)
... (large, not relevant stack trace)
30 {main}

Looks like it is failing in local development on a Mac. My experience with native MySQL on OS X 10.9.3 is that the MySQL server just stops working once every few weeks, and I have to restart it. Since it's only occasional and I don't use it for anything really critical, I just live with the restarts.


Endless Scroll + Ordering Currently I have infinite scroll setup as below, but when changing the ordering of the entries, then loading more through the infinite scroll it's not loading in the order of the entries.

Set Sort Options

    {% set sortOptions = [
        {
            label: 'Name Z - A',
            value: 'title desc'
        },
        {
            label: 'Name A - Z',
            value: 'title asc'
        },
    ] %}

{% set selectedSortOption = craft.request.getParam('order', 'title asc') %}

Pagination Loop

{% paginate craft.categories.group('university').order(selectedSortOption).limit(1) as pageEntries %}

    {% for category in pageEntries %}

          {% for asset in category.universityLogo %}

          {% endfor %}

{{ category.description }}

    {% endfor %}

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

    {% endpaginate %}

Javascript Adding Query

{% set orderJs %}
  $(function(){

    $('#sortQuery').on("change", function(e){

     // Get the value from the select
     var filter = $(this).val();
     document.location.href = '{{ craft.request.getUrl() }}?order=' + filter;

   });

  });
{% endset %}
{% includeJs orderJs %}

Endless Scroll jQuery

You use q instead of order for the query param in the pagination URLs.


Ah ok, two issues;

the image field is an array so you need to just select the first
you need an 'img' html tag as the twig code will only output the url

{% for entry in craft.entries.section('blog').level(1) %}

{{ entry.title }}

  {{ entry.getLink() }}
  {{ entry.previewText |raw }}
  {% for row in entry.textImages.all() %}

    {{ row.textImageWitdh }}
  {% endfor %}

{% endfor %}


Ajax User registration sometimes forbidden 403 error I am experiencing a very weird issue. I have a simple user registration form:

<form method="post" action="" accept-charset="UTF-8" id="addTeacherForm">
    {{ csrfInput() }}

    <input type="hidden" name="action" value="users/save-user" />
    <input type="hidden" name="fields[schoolId][]" value="{{ currentUser.id }}" />
    <input type="hidden" name="groups[]" value="3" />

        Username *
        <input id="username" type="text" class="form-control" name="username" required />

         E-mailadres *
        <input id="email" type="text" class="form-control" name="email" required />

        Password *
        <input id="password" type="password" class="form-control" name="password" required />

        <button type="submit" class="btn btn-primary btn-submit float-right">
            Docent toevoegen
        </button>

</form>

I have an event listener for the form submit and I submit the form using ajax. This code works perfectly most of the time, but sometimes (like 20% of the time) I get a 403 forbidden error in the console. When I comment out the hidden groups[] input field I don't experience this issue at all.

It is also worth noting that even though I get this error the user is created but doesn't get added to the group.

I'm starting to think this is a craft 3 issue.

Can I somehow solve this?

Thanks!

Can I somehow solve this?

Yes by creating a custom Controller. Because the default UsersController does the following

// Don't validate required custom fields if it's public registration
if (!$thisIsPublicRegistration) {
    $user->setScenario(Element::SCENARIO_LIVE);
}

without SCENARIO_LIVE custom fields won't be stored


Placing an extra empty linebreak in the Redactor Field I've been trying to add extra line breaks to a text field, But this seems impossible?

Most logical thing to do for the user would be to give it an extra linebreak

Those get deleted, sometimes directly, sometimes after opening and then saving again

So far I've tried:

disable cleanup html in the field - still deletes

In the json file settings:

tidyHtml: false - no
cleanSpaces: false - no
removeEmptyTags: false - no
linebreaks: true - this converts ALL breaks to line breaks. Not handy.


Structure - Add class if element has children I have a structure section which I am using to create a navigation menu.
It works ok and produces the list etc.
I want to add a class to the li element if it has children.

Current Code is:

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

    {% nav entry in entries %}

            {# Check for entry type / get related entry #}
            {% if entry.type == 'customLink' %}

                {# Link to a custom url #}
                {{ entry.title }}

            {% else %}

                {# Link to a structure entry #}
                {{ entry.title }}

            {% endif %}

            {# Repeat this for child entries #}
            {% ifchildren %}

{% children %}

            {% endifchildren %}

    {% endnav %}

I want to add class="has-dropdown" to the first list element if it has children.

I have tried this:

But that does not work. How can this be achieved?

The Methods section on the EntryModel documentation page has a lot of useful things for working with navigation for Structure sections. You can use hasDescendants():

{% if entry.hasDescendants() %}class="has-dropdown"{% endif %}


Get entries which have content like '%findme%' I am creating a more robust find and replace plugin which shows what is being replaced and where. I need to query for all entries(or content records?) where content is like '%findme%'.

Should I query for entries or should I query for content records for this find and replace?

I saw that there is an EntryRecord, so I can give it CDBCriteria object to find but there is no ContentRecord so how do I get the content with the string in it?

This is all I have so far.

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->limit = $model->limit;
$relaventContent = ContentService::model()->findAll($criteria);

It's up to you what you prefer to use. Usually people use the ElementCriteriaModel. You could achieve your goal with the record itself too but it requires more code.

The ElementCriteriaModel provides more ready-to-use options and returns populated entries, Records provide only records, so no populated entries with all relations and such

I'm not sure what that $model->limit is supposed to do, maybe this is a setting in your globals, but you would normally search for entries that contains something (you can find the rules here) with this code

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->search = 'yourKeyWord';
$relaventContent = $criteria->find();

If you just want to have all entries you would do

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->limit = null;
$relaventContent = $criteria->find();


Using services/records etc in migrations The database migrations documentation does say:

  Please note that we do not recommend using records directly within a migration

Are there technical reasons for this recommendation, or is it something you'd just rather not support? I don't mind if I'm on my own as far as support goes, but it seems like I might be able to use migrations to deploy schema changes across environments if I was careful?

Of course this would be for private, site-specific plugins, not anything that was going to be publicly released, so I don't think the issue Brandon highlighted here would necessarily apply.

I have successfully created a new tag group using the following:

public function safeUp()
{
    $tagGroup         = new TagGroupModel();
    $tagGroup->name   = Craft::t('Tag Group');
    $tagGroup->handle = 'tagGroup';

    craft()->tags->saveTagGroup($tagGroup);
}

But obviously this is quite a simple example, and a migration that added fields, sections and entry types (like the InstallService does) would be more complicated and should probably check each record was saved properly before continuing with the next bit.

Are all of Craft's services available while a migration is being run?

Are there technical reasons for this recommendation, or is it something you'd just rather not support?

It's more along the lines of it's something you're not going to want to support.

The problem is that things like services and records change over time.  Methods get renamed/deleted, attributes change, parameters change, etc.

By using them in a migration you're assuming that the methods you're calling and the attributes you're using are going to be there for the rest of time and that's not necessarily going to be true.

A year from now if someone tries to run that migration, there's a chance those services and records are going to look very different and the migration is going to fail because it was written for what the service/record looked like a year ago.

You're much better off thinking of migrations as little self-contained applications with as few dependencies on external Craft classes as possible.


How many entries could the Craft database hold before it slows down? I'm pulling in some RSS feeds using Feed Me (https://github.com/engram-design/FeedMe) and turning those in entries automatically using a cron job.

You can imagine that producing a lot of entries over time ;) Is there a known limit for entries in the Craft database?

And is there perhaps a way to automatically delete entries after x amount of time?

Thanks, Bob

It's a bit of a loaded question, but generally it's going to depend more on environmental limitations than anything with Craft itself.

If you're on a shared host along with 200 other accounts using the same MySQL server, then you'll likely notice slowdowns sooner than later.

If you're on a decent host with plenty of memory/disk/CPU available, then we've seen sites with 500k users/entries/assets/etc. running along just fine.

  And is there perhaps a way to automatically delete entries after x amount of time?

As Andrew pointed out in the comments, you could write a simple plugin that exposes a controller action that gets all entries over a certain age and deletes them.  Then you'd setup a CRON job to call that controller endpoint on whatever schedule you want.


Loop for Similar Fields I'm in a situation where I have a handful of matrix blocks which have similar subfields. For example, I have a "Bios" block where there's an intro etc., but the a few repeating fields, imagine:

Bio 1 Name (bioName1)
Bio 1 Image (bioImage1)
Bio 2 Name (bioName2)
Bio 2 Image (bioImage2)
etc. up to 6

In my template, I have some pretty redundent code when outputting these fields, like:

    {% if block.bioName1 %}

            {% for asset in block.bioImage1 %}

            {% endfor %}

{{ block.bioName1 }}

{{ block.bioTitle1 }}

            {{ block.bioText1 }}

    {% endif %}

    {% if block.bioName2 %}

            {% for asset in block.bioImage2 %}

            {% endfor %}

{{ block.bioName2 }}

{{ block.bioTitle2 }}

            {{ block.bioText2 }}

    {% endif %}

I'm wondering if it would be possible to somehow set this up as a loop with a counter to increment the vars (the field names always end with the count)? Any ideas? I tried a few things but have had no luck :/

I think this is an excellent use case for twig's attribute function.

  The attribute function can be used to access a "dynamic" attribute of a variable:

{% for i in 1..6 %}
  {% if attribute(block, 'bioName'~i) %}

        {% for asset in attribute(block, 'bioImage'~i) %}

          ...etc...
        {% endfor %}

  {% endif %}
{% endfor %}


PHPUnit - Warning: "Header already sent" I'm trying to cover the plugin controller with unit tests, but when I try to run phpunit this error shows up:

  Warning: Cannot modify header information - headers already sent by (output started at /craft/app/vendor/phpunit/phpunit/src/Util/Printer.php:172 ) in /craft/craft/app/helpers/JsonHelper.php on line 25

The same error indicates also line 26 and 27 of JsonHelper.php

Did someone already had this issue? Can somebody help me on this?

Thank you very much.

You could try setting PHPUnit's @runInSeparateProcess flag, as discussed here.

However, it sounds as though you're attempting to use PHPUnit for acceptance or integration tests, when it's really designed to be used for unit testing.

A couple of options:

You could mock the JsonHelper::sendJsonHeaders method, but that may prove tricky;
You could use something like Behat for integration testing. This may be more appropriate for a controller.

Stephen


Twig filter to convert special characters to URL friendly characters I have a text field that will accept some German text in it as a headline of a section. I'm using Craft's |kebab filter to turn that into a lowercase-with-dashes id that my navigation scrolls down to when its corresponding link is clicked.

However, when there's a word like "Über Uns", the umlaut Ü stays in the output like so: "über-uns" and this messes up my javascript so the scrolling doesn't work for that one item. Is there a filter or something to have it be output to "ueber-uns"?

My first idea would be to use the url_encode filter than would turn your special characters into a URL friendly format. I imagine these would also be Javascript friendly.

If you'd like to convert the characters to something like "ueber-uns" maybe take a look at the convert_encoding filter in Twig. It uses a function called mb-convert-encoding.

I tried to find what encoding you'd need to use to convert Ü to something javascript friendly but was only able print even more obscure characters.


Localisation for certain languages I was setting up localisation and was a bit confused finding certain langugae locales.
Urdu is spoken in Pakistan also known as Bihari but in Craft or Yii i18n data there is only en_pk. Is this a holder for incoming translations?

Craft only ships with a subset of Yii’s supported locales, to cut down on the filesize. It’s easy to add additional ones though. Instructions are here: http://buildwithcraft.com/help/adding-additional-locales


Control Panel, sorting issue i am new to craft.
Is there a connection between the control panel sorting setting for structure entries and the template output.

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

Changing sort method in control panel should leads to different outputs in template. Is that right?

Goal is to output entries in sort order related to control panel sort settings.

What did i do wrong?

No – whatever you've currently set as the sorting order (Structure, Post Date, Expiry Date, etc) in the element index in the control panel has absolutely no effect on how your entries are output in a template, or how they're ordered.

For Structure sections, the default sorting order in a template is Structure, i.e. hierarchical:

{% for entry in craft.entries.section('myStructureSection') %}
    {# Will output entries in hierarchical, chronological order #}
{% endfor %}

Keep in mind that if your Structure has multiple levels, i.e. child entries, you'll have to jump through a few hoops – using the {% nav %} tag is recommended as its able to output child entries recursively, e.g:

{% nav entry in entries %}

        {{ entry.title }}
        {% ifchildren %}

                {% children %}

        {% endifchildren %}

{% endnav %}

Finally, if you want to output the entries in a non-hierarchical order, you can use the order parameter:

{% for entry in craft.entries.section('myStructureSection').order('postDate desc') %}
    {# Will output newest first #}
{% endfor %}


How to validate a unique field in Craft 3 element? I'm trying to validate a custom Element in my Craft 3 plugin.

Where should I be putting my validation rules?

I have two classes: one for the Element, one for the ActiveRecord (e.g. Invoice and InvoiceRecord).

One of the requirements is that one of its field (e.g. number) has to be unique.

If I override rules() on the Element, the validation occurs normally but the unique rule throws an error:

  Call to undefined method Invoice::getDb()

Now if I try to add the unique rule to my Record's rule() override, it does seem to fail validation and prevent saving the record, but no validation error is thrown (Craft flashes the "saved successfully" message and returns to the element index, but without the new/updated element).

I understand that the record's validation is called only in afterSave, so technically validation has already been done:

public function afterSave(bool $isNew)
{
    if ($isNew)
    {
        $record = new InvoiceRecord();
        $record->id = $this->id;
    }
    else
    {
        $record = InvoiceRecord::findOne($this->id);
    }

    $record->number = $this->number; // Has to be unique

    $record->save();

    parent::afterSave($isNew);
}

But I don't understand how to get my validation working for something rather simple.

I can't find anything about validation in the Craft 3 plugin docs or migration docs from Craft 2, does anybody know?

Here's my findings after some experimentation:

Elements and Records are two entirely separate entities. The Element is basically the core Craft database entry, while the Record is the additional database entry in a new table with your custom fields.
Validation should always be done on the element if you use the pattern used in the question, because saveElement() will call validate on the element itself, not the record. If you validate on the record and it fails ($record->save() returning false), the phantom element will still be created, but no record will refer to it. Yikes.
When validating on the element, some rules such as unique require access to the database, which is not bound to the element per se. In that case, you should map the targetClass parameter of the rule to the record class.
However that does mess up same-model detection with UniqueValidator: the validation will fail even if you're editing the current model, saying its unique field is already in use (by itself!).
Thankfully, you can use the filter parameter of the rule to manually ignore the current ID. However, you must make sure not to use that is the ID is null (i.e. when creating a new element), because != does not work with NULL values in SQL (you should be using IS NOT instead).

Summary: here's how to implement unique validation on the Element class:

    $rules[] =
    [
        'myField',
        'unique',
        'targetClass' => MyRecord::class,
        'filter' => function($query) {
            if ($this->id !== null) {
                $query->andWhere('`id` != :id', [ 'id' => $this->id ] );
            }
        }
    ];

That's it!


How do I update a column value in my custom plugin with a simple controller? I have a custom element type with its own table 'pluginname_entries'. I am trying to find a way to "dummy proof" changing the plugins status. So I came up, aka stole from the Events element type, with the idea of clicking a button that triggers a controller action - similar to the following example to delete my element types entry:

actionDelete()

/**
 * Deletes an entry.
 */
public function actionDelete()
{
    $this->requirePostRequest();

    $entryId = craft()->request->getRequiredPost('entryId');

    if (craft()->elements->deleteElementById($entryId))
    {
        craft()->userSession->setNotice(Craft::t('Entry deleted.'));
        $this->redirectToPostedUrl();
    }
    else
    {
        craft()->userSession->setError(Craft::t('Couldn’t delete entry.'));
    }
}

I love the concept or a quick way to delete or update the entry, more specifically the entry custom status, with a simple click of a button. So I came up with this:

actionDeny()

/**
 * Changes an entry to denied.
 */
public function actionDeny()
{
    $this->requirePostRequest();

    $status = PluginStatus::Denied;

    $entryId = craft()->request->getRequiredPost('entryId');

    $query = craft()->db->createCommand()
        ->select('id, status')
        ->from('entries')
        ->where(array(
            'id' => $entryId
        ))->queryRow();

    if ($query == null) {
        craft()->userSession->setError(Craft::t('Unable to deny the entry.'));
    }
    else {
        craft()->userSession->setNotice(Craft::t('The entry was denied.'));
    }

}

That works as intended, notifying the user but of course doesn't actually update the status column to the new value, is there a better way to handle this? Maybe more of a "Craft" way?

You are currently performing a SELECTinstead of an UPDATE so no data will be updated.

I'm guessing that entryId is the primary key of the record you are trying to update? If so, I would suggest you find the record to make sure it exists, update its attributes (status) and then save it.

// PluginController
public function actionDeny()
{
    $this->requirePostRequest();

    $status  = PluginStatus::Denied;
    $entryId = craft()->request->getRequiredPost('entryId');

    if (craft()->pluginName->updateStatus($entryId, $status)
    {
        craft()->userSession->setError(Craft::t('Unable to deny the entry.'));
    }
    else
    {
        craft()->userSession->setNotice(Craft::t('The entry was denied.'));
    }
}

// PluginService
public function updateStatus($id, $status)
{
    $record = PluginRecord::model()->findById($id);

    if ($record)
    {
        $record->setAttribute('status', $status);

        return $record->save();
    }
    else
    {
        throw new Exception(Craft:t('No record was found with id {id}', array('id' => $id));
    }
}

Unless I'm missing something, that should be about all you need.

@selvinortiz


Output DateTime in that timezone related to the current locale? Is there a way to loop through a day in 15-minute intervals, with the output of said intervals being localizable? This (cringe-worthy) snippet should help explain what I'm trying to accomplish:

    {% for hour in 0..23 %}
        {% for min in [0, 15, 30, 45] %}

            {% set foo = [hour, min]|join(':')|date('H:i')|date_modify('+0 days') %}

{{ foo.localeTime() }}

        {% endfor %}
    {% endfor %}

For me, that snippet results in the unusual error Method "localeTime" for object "DateTime" does not exist.

(This question was asked on g+ by Chris Davis. I copied it to SE, as I didn't want to answer it there!)

You can set the site's timezone in Craft, but you won't get different timezones for your locales with that (locales != timezones btw.). But what you can do is to use Twig's |date filter which allows you to specify a different timezone for the output (list of supported timezones). If you create your own DateTime object by joining strings, you have to add a timezone here to give the filter a basis for its calculations. Make this match your Craft settings.

The following example works without the Localization package.

{# Set locale time to GMT+2 #}
{% set localeTime = "0:00 +0200" %}

        Local Time	Sao Paulo	Manaus

    {% for hour in 0..23 %}
        {% for min in [0, 15, 30, 45] %}

                {# Join time segments in a format supported by strtotime #}
                {% set timeStep = ['+ ', hour, ' hours ', min, ' minutes']|join() %}

                {# Modify the locale time and output in chosen timezone #}
                {{ localeTime|date_modify(timeStep)|date('H:i') }}
                {{ localeTime|date_modify(timeStep)|date('H:i', "America/Sao_Paulo") }}
                {{ localeTime|date_modify(timeStep)|date('H:i', "America/Manaus") }}

        {% endfor %}
    {% endfor %}

To bring the site's currently requested locale into the game, just do some comparisons on craft.locale.

{{ craft.locale == 'de-DE' ? localeTime|date('H:i') }}
{{ craft.locale == 'pt-BR' ? localeTime|date('H:i', "America/Sao_Paulo") }}


Validation on username? Just wondering if theres anyway to force some validation on the username?
I have usernames being displayed locally, and therefore I'm trying to prevent email addresses being used. I have my client side validation set up with regex to prevent an email being used, but some users still seem to have been able to register with the email as there username (presumably they have JS disabled).

It would be nice to enforce that validation server side.

I don't know if there is anything natively in Craft to do this, but you could achieve it quite easily with a single file plugin:

public function init()
{
    craft()->on('users.onBeforeSaveUser', function(Event $event) {
        // Only do anything if it is a front end submission
        if(craft()->request->isSiteRequest())
        {
            $username = craft()->request->getPost('username', null);
            if($this->isEmail($username))
            {
                $event->params['user']->addErrors(array('username' => Craft::t('You cannot use an email')));
                $event->performAction = false;
            }
        }
    });
}


Front-end form, creating a tag Similar to the question asked here:
How to add a tags-field to an entry form?

I am trying to create an entry form that can create tags. I'm using http://selectize.github.io/selectize.js/ for the select element.

I am currently able to submit the form with existing tags but when trying to create a new tag using

$.post('/tags/createTag', data, function(response) {
    if (response.success) {
        alert('Success')
    } else {
        alert('Unable to create that tag.');
    }
});

I get http://talkbox.es:8888/index.php/tags/createTag 404 (Not Found)

Let me know if you guys need any more information, I'll gladly get it for you! Thanks a ton in advance.

It looks like you are not targeting the action URL. When accessing a Craft controller, you'll need to use the actionTrigger in your URL:

Your current URL:

http://talkbox.es:8888/index.php/tags/createTag

Should be updated to:

http://talkbox.es:8888/index.php/actions/tags/createTag

The actionTrigger is configurable so if you plan to reuse your code on multiple sites, you won't want to hard code that value. In twig, you can output that value using the craft.config variable:

{{ craft.config.actionTrigger }}


Can you run two different Stripe accounts as two different gateways in Craft Commerce? Does anyone know if there are any drawbacks/gotchas to having multiple Stripe gateways setup in commerce for Craft Subs?

If I wanted to have two sites on my craft install and for them to both use Commerce Subs, but for each site to have it's own Stripe account (i.e 2 stripe gateways with different API details in both)

Would that work ok with stripe callbacks etc etc etc?

Yes you can, and yes it will work.

This text here is to meet the minimum answer length.


How to route 404 errors for multi-site Craft? Craft looks for /templates/404 by default. What do you do if you have multiple sites?

I pondered this for a while, and then did this, which will direct us to specific entries (not templates, I don't want to create more of those):

{# We need a unique 404 for each site - just take us to the
   right slug relative to current URL #}

{% switch currentSite.id %}

{# siteA.com #}
{% case 1 %}

    {% redirect '/my-404-slug' %}

{# siteB.com #}
{% case 2 %}

    {% redirect '/my-other-404-slug' %}

{% endswitch %}

If you want to use templates instead of entries, just use an {% include %} statement instead of {% redirect %}.


Multiple cart fields/notes First time using Commerce and made pretty good progress, but... I need to collect a few bits of information at the cart stage. The site is selling conference tickets, and I need to know:

if the delegate has any dietary requirements
whether or not they have been asked to speak
and if they have, to provide a short bio

I'm aware of the {{ item.note }} tag, which I'm using for the dietary requirements. But how can I capture the other two fields?

Any help much appreciated!

Paul's suggestion of using order fields worked perfectly.


How do I get the last uri segment from within a plugin (php) I have some validation that checks for a unique email when creating a new person. Works great.

When I am updating the person, it's no surprise the system won't let me save because the email I am saving already exists.

I am working on a condition that compares the id to the existing person to who is getting updated.

Plugin_EmailValidator.php

...
$person = craft()->db->createCommand()
    ->select('id, 'firstName, lastName')
    ->from('people')
    ->where('email=:email', array(':email' => $model['email']))
    ->queryRow();

if(!empty($person)) {
    error_log('MATCH FOUND');

   // I have the id of the existing account. Need to compare against uri segment?
  $existingId = $person['id];
  $segment = craft()->request->getLastSegment();

  error_log($segment); // error!

   $message = Craft::t("Email already exists.");
   $this->addError($model, $attribute, $message);
}

This is what my url looks like:

/admin/plugin/controller/update/123

I see examples of using the request methods within twig, i'm trying to access it via php.

I've also tried this:

craft()->request->getQuery('update');

This will run without error, but it also returns nothing.

Have a look at the segments property (shortcut for getSegments() method) of the HttpRequestServices class, which returns an array of uri segments.

craft()->request->segments;

To get the last segment you could use php's end() or array_pop() method.

$segments = craft()->request->segments;
echo end($segments);


Can I apply adjustments (discounts) through a controller? I have a controller which when called (actions/some-module/discount-link/apply?code=abc) associates abc with an entry and based on some stuff in that entry populates the cart with some specific products. This works well.
What I want to do next is apply a discount to each line item that the cart is populated with. Setting up an adjuster as per the docs was no problem, but when I'm trying to add adjustments to the cart from within my controller I can't figure it out.
The last part of of my controller:
foreach ($cart->getLineItems() as $item) {
    $adjustment = new OrderAdjustment;
    $adjustment->type = 'discount';
    $adjustment->name = '$2 off';
    $adjustment->description = '$2 off everything in the store';
    $adjustment->sourceSnapshot = ['data' => 'value'];
    $adjustment->amount = -2;
    $adjustment->setOrder(Order::findOne($cart));
    $adjustment->setLineItem($item);

    plugin::getInstance()->getOrderAdjustments()->saveOrderAdjustment($adjustment, true);
}

Craft::$app->elements->saveElement($cart);

Is the entire thing wrong? Or am I not saving the adjustments to the cart correctly?

Generally speaking, no. Adjusters should always be computed from the current state of the order—the information required to match an Adjuster should be attached directly to an order.
Your custom Adjuster is registered globally, then applied to Orders that match criteria you've defined in its adjust() method. That method returns an array of Adjustment models, which are then attached to the order.
In this case, what I'd recommend doing is something like this, in your controller:

Looks up the Entry based on a URL param (?code=abc);
Load the current user's Cart;
Set that Entry on the Order via a custom Entry field: $order->setFieldValue('myDiscountEntryField', [$discountEntry]); (noting that the value is an array, due to the way Relational data is saved!)
Save the Cart/Order: Craft::$app->getElements()->saveElement($order);

At this point, Commerce automatically offers each registered Adjuster an opportunity to apply adjustments to the Order.
In your Adjuster, use the attached Discount Entry data to return an Adjustment:
class MyAdjuster implements AdjusterInterface
{
  public function adjust(craft\commerce\elements\Order $order): array
  {
    $discountEntry = $order->myDiscountEntryField->one();

    // We don't want to assume every order contains valid data, and if we can bail early, let's take the opportunity:
    if (!$discountEntry) {
      // The method signature still requires an array returned, but in this case and empty array is basically saying "no adjustments applied."
      return [];
    }

    // Then here, you'll create and return your Adjustment models (like you have in the original question)!
    $adjustments = [];

    // ... Loop over your items...
    foreach ($order->getLineItems() as $lineItem) {
      // ...and use `$adjustments[] = $adjustment` to push new ones into the array, or just `continue;` if you want to skip one.
    }

    // Return anything that was added:
    return $adjustments;
  }
}

If you apply Adjustments outside of this process (the automatic recalculation step, which happens when saving a Cart), you are not likely to have a consistent experience, because they won't be injected the next time a Cart is saved!


Write project config to cloud storage I'm on a read-only filesystem using Google App Engine Flex. I was able to write my cpresources to cloud storage using a plugin, however, now I need to have project.yaml be written elsewhere. perhaps cloud storage as well.
Not sure how to approach this

Untested but you might want to try setting CRAFT_EMPEMERAL to true to skip the filesystem checks.
You can also opt out of automatic project config creation using the Manual YAML File Generation method found in the docs.


Export database from control panel or Sequel Pro? What's the difference? Since I am involved in a multi-developer, version-controlled setup, I do a lot of database importing and exporting from environments.

I notice today that when I export a database from one of our sites using the control panel "Backup Database" function, it comes out at about 3.6MB and takes about 5 minutes to import.

When I export from Sequel Pro, the database dump comes out at about 1.6MB and imports within 30 seconds.

Note, I always dump the existing tables before import.

Just wondering what the difference is here that there is such a difference in file size and import time and which way is best?

Thanks!

The difference in both size and speed is likely because Sequel Pro (and any other MySQL client) is likely using mysqldump, which is a native CLI command and highly optimized.

Craft can't rely on that for a variety of reasons (PHP might have have exec permissions, the account Craft uses to connect to the database might not have the permissions necessary to perform a backup, etc.)

So Craft has it's own fallback where it manually analyzes the database's DML and DDL and writes it out to a file.

Slower?  Yes.  Larger file size?  Yes.  More reliable for Craft?  Yes.

It is on our list somewhere to check if Craft has the ability to access mysqldump and use that, if not fall back to our current method.


Limit slugs to ASCII for auto-generated titles I currently have auto-generated titles that contains accents and so does the slugs.

Since the slug is used to generate assets folders, accents aren't suitable and results in question marks in the folder names.

I was hoping that limitAutoSlugsToAscii would answer this problem but it only affects the javascript for dynamically generated slugs.

Is there a simple way to achieve this ?

Since the slug is used to generate assets folders, accents aren't suitable and results in question marks in the folder names.

You could do something similar to How to rename filename of asset before (or after) upload in Craft 3 in a plugin except you run the folder name through the same ASCII normalization process that auto-generated slugs go though.


Setting User's Groups - Integrity Constraint Error $groupIds = [1, 2];
craft()->userGroups->assignUserToGroups($user, $groupIds);

When executed I get the following error:

CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity     constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`letshacklondon2016`.`craft_usergroups_users`, CONSTRAINT `craft_usergroups_users_userId_fk` FOREIGN KEY (`userId`) REFERENCES `craft_users` (`id`) ON DELETE CASCADE). The SQL statement executed was: INSERT INTO `craft_usergroups_users` (`groupId`, `userId`, `dateCreated`, `dateUpdated`, `uid`) VALUES (:row0_col0, :row0_col1, :row0_col2, :row0_col3, :row0_col4), (:row1_col0, :row1_col1, :row1_col2, :row1_col3, :row1_col4). Bound with :row0_col0=1, :row0_col1=Craft\UserModel::__set_state(array('elementType' => 'User', ... (etc.)

Any Ideas?

UserGroupsService->assignUserToGroups takes a user's ID as the first parameter and you're passing in the entire UserModel.

Change it to this and it should work:

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


Commerce - add new tab to order page in control panel I'm am creating module that provides integration with shipping service for Craft commerce.
UX wise, it would be best if I could put module functionality in separate tab on order page. Mind you, I know that we can add new tabs using order fields functionality. But i wonder if it is possible to "hardcode" new tab using some php code. Some events maybe?

This certainly used to be possible and was quite handy, but I believe since the commerce orders admin area moved to using Vue, there's no easy way to hook in and do this any more.
Would love to be proven wrong, though!


Display/edit Sprout Form entry in front end Following Sprout Forms -- How to link to individual entries on the front-end I found code

{% set formEntryId = craft.request.getSegment(2) %}
{% set entry = craft.sproutForms.getEntryById(formEntryId) %}

but it doesn't have any field data. Including {{ dump(entry) }} in my template shows object(barrelstrength\sproutforms\elements\Entry) with a lot of structure but no user entered values.

I was actually hoping to reuse my custom form templates (Sprout Forms > Settings > General > Default Form Templates) so it'll cycle through all the field templates like when the user filled in the form but I just want to show values if the form has already been submitted.

Sprout Forms improved support of this scenario in v3.0.0-beta.27. You can now use the setEntry variable to populate the Form Entry Model that you want to use in the Form Templates that you output using the displayForm tag. See the docs on Editing Entries for more info.

Populate a Front-End form with existing data

{# Get an instance of the your Form #}
{% set form = craft.sproutForms.getForm('application') %}

{# Get an instance of the Entry you'll be allowing a user to edit #}
{% set entry = craft.sproutForms.getEntryById(123) %}

{# Set the `entry` variable so your Form Templates know how to populate the fields with existing values #}
{% do craft.sproutForms.setEntry(form, entry) %}

{# Output your form as usual #}
{{ craft.sproutForms.displayForm('application') }}

Original Answer

I've looked into this and am considering this a bug. I've opened an issue on Github and will update my answer here when we have a fix in place.


Best practice for volume aliases in config files I'm wondering about using a config/volumes.php file to specify volume aliases (instead of putting them in config/general.php). There's a comment in a previous codebase mentioning https://docs.craftcms.com/v3/config/#overriding-volume-settings but that seems to have been removed and now we can't find documentation about creating additional config files.

I see this somewhat related post and that @robin-schambach posted with a suggestion to put volume aliases alongside of other ones in config/general.php.

Craft 3: how can I set dynamic Base URL in sites setting?

So, is it best practice to create a config/volumes.php just for volume aliases or should they be kept in config/general.php?

Ah, the power of commit history.

@brandon-kelly removed the volumes config section from the v3 docs back in February, mentioning

  Environment variables solve the same problem better.

So it seems that the best practice is to create volume aliases in config/general.php.


linking to staff page, how to setup entry I am fairly new to Craft and I am hoping someone can help point me in the right direction so I am not having to do double the work or have to learn the hard way that there is an easier way.

I am trying to get my head around how I can tie pages together. I am creating a page for videos from speakers but I want the speakers names to be linked back to their staff page.

How do I create an entry that will allow users to select a staff member?

I have already created a staff entry page, that allows all the information to be populated for the staff member. Should that be a categories page instead to allow me to have a the user select the staff member (category) that did the talk for the video? Or is there another way to allow the user to select a field of staff members?

Thanks for the help!

You could have two sections:

Staff
Videos

In the videos channel, create a Staff entries field type, select the Staff channel in that field's options.

When you create a new video, you can relate that video to a staff entry.

When displaying a video entry, you could reference that staff that is tied to that video entry like so:

{{ entry.staff }}

If you wanted to be more specific, you would do something like:

{{ entry.staff.title }}

and to get a link to the staff entry that is related to the video entry:

{{ entry.staff.url }}

Here is an article explaining relationships further: https://straightupcraft.com/articles/introduction-to-relations-and-reverse-relations-in-craft-cms-with-examples and documentation on the Entries field type: https://craftcms.com/docs/entries-fields


Is there a way to return asset data along with entry data? I'm using the Element API plugin to return data from an entry that also contains an image asset. Is there a way to combine the two so I can get the asset URL alongside the entry fields?

Attached below is an approximation of my line of thinking.

        'elementType' => 'Entry',
        'criteria' => ['section' => 'wax'],

        'transformer' => function(EntryModel $entry) {
            return [
                'excerpt' => $entry->excerpt,
                'heroImage' => $asset->heroImage.url // Something like this?
            ];

Yes - you can access it exactly the same way you do in the templates; you just need to use PHP syntax (e.g. -> for accessing sub-properties rather than .).

'transformer' => function(EntryModel $entry) {
    // Begin defining the response data for this entry
    $data = [
        'excerpt' => $entry->excerpt,
    ];

    // Get the first asset in a "heroImage" Assets field (if there is one)
    $heroImage = $entry->heroImage->first();

    if ($heroImage) {
        $data['heroImage'] = $heroImage->url;
    }

    return $data;
})


How can I sum value across entries assigned to a category for a given year across multiple years? I have a category group named country.   Each entry in posts is assigned to a country and a dollar value.  dollar value is a field, NOT a table cell.

On a country's given page (i.e., a category page), I want to sum the total value across all entries assigned to a given country by year (date posted) across all years for which there are entries.

Help...

{% amount = craft.entries
   .postDate(['and', '>=' someDate, '<=' ~ someOtherTime])
   .select('dollar')
   .relatedTo(category)
   .sum()
%}

Loop through all years and sum the value


How do I extend another plugin? I'd like to know if it's possible to extend another plugin with my own.

I'm currently using the Sprout Forms plugin to create a contact form on our website, and I would like to extend that so when a user submits the form, it signs them up to a campaign monitor list as well (with their permission of course).

Is this possible with Craft? If so where in the documentation would I look for more info?

I believe what you asking about are Hooks and Events.  Craft has several of them listed in the Hooks Reference and Events Reference.  And Craft also allows us to add Hooks and Events in our plugins, which other plugins can leverage.

In the case of Sprout Forms, as of this post, we don't have any documented events yet, but there are a couple in there and we'll be formalizing them more in the next release.

You'll probably want to take a look at sproutForms.beforeSaveEntry Event.  Right now, plugins like Sprout Invisible Captcha, SpamGuard, and Snaptcha use that Event to add spam protection to Forms.  You could use that event as well to check for a confirmation field, and add a user to a list in Campaign Monitor.

Here's an example of how SpamGuard uses the event, which you can see here in the init() method of primary plugin class file (I've added the comments myself in relation to your question):

# Check for an Event in your plugins init method
public function init()
{
    # If you add a setting that allows a user to turn your integration on or off
    # you can check for that setting here, before you run your code
    if ($this->getSettings()->getAttribute('enableSproutFormsSupport'))
    {
        # Listen for the event where you want to add behavior
        craft()->on('sproutForms.beforeSaveEntry', function(Event $event)
        {
            # Do your thing!
            $spam = spamGuard()->detectDynamicFormSpam($event->params['entry']);
            if ($spam)
            {
                $event->fakeIt  = true;
                $event->isValid = false;
            }
        });
    }
}


How can I pre-raw a Model attribute for the Twig template? (ie. "myModel.myAttr" without requiring the raw filter) I have a model that returns markup as part of a variable. To escape twig's encoding we have to include the raw filter, like so :

{{ entry.customField | raw }}

The encoded output of that field is never useful, so ideally we'd have a way to get the same result without that twig filter. ie :

{{ entry.customField }}

Looking at the various twig and template components I doubt this is possible, but is there any hidden ways to get this working?

This is all designed to remove that extra requirement from the usage, so wrapping unescape tags around it isn't want I'm looking for.

The easiest way i've found to do that is to use TemplateHelper::getRaw()

So in your "prepValue" function inside your fieldtype class do something like this

function prepValue($value) {
    //any other data manipulation stuff
    return TemplateHelper::getRaw($value);
}

More info on prepValue in the docs.


Prevent automatic title on resave I have a situation where I wish to automatically set entry title based on locale entry was created in. This is what I use for title:

{{ object.locale == "jp" ? "Japanese title" : "English title"}}

This works fine but when I create an entry in Japanese locale and open it in English locale and resave it the title changes to "English title" and that's not what I want, I wish to preserve the original title. Is that possible somehow?

Completely untested, but something like this should work:

{{ !object.id ? (object.locale == "jp" ? "Japanese title" : "English title") : object.title) }}

Basically only run your logic if the entry doesn't have an ID yet (which new entries won't until they are saved).


How can I strip or replace a non-breaking space with Twig? Using Twig, is it possible to strip out or replace a non-breaking space (&nbsp;)?

I've got a string variable price (returned from the currency filter) that prints like this:

Nkr&nbsp;100,00

What I'd like, is to strip the Nkr&nbsp; part, so the string looks like this:

100,00

However, {{ price|replace('Nkr&nbsp;', '') }} does nothing, as the replace filter doesn't appear to actually match the non-breaking space.

{{ price|replace({'Nkr' : ''}) }} strips out the Nkr part, but leaves the &nbsp;; and I'm unable to remove it.

Here's what I tried already (after stripping the Nkr part):

{{ price|replace({'&nbsp;' : ''}) }}
{{ price|raw|replace({'&nbsp;' : ''}) }}
{{ price|striptags }}
{{ price|raw|striptags }}
{{ price|trim }}
{{ price|raw|trim }}
{{ price|replace({ '\u00a0' : '' }) }}
{{- price -}}
{% spaceless %}{{ price }}{% endspaceless %}

Here's a different spin... Try removing everything that's not numeric or comma.

{{ price|replace('/[^0-9,]/', '') }}


Redirect IE7 or below users to a new page Traditional .htaccess or
<!--[if lt IE 7]><meta http-equiv="refresh" content="0; url=/assets/errors/ie.html"/><![endif]-->
methods don't work to redirect users in Craft.

I know it may seem taboo, but quite frankly I don't want anyone who is using IE7 or below to access my site. I'd like to completely redirect them to a separate page/template that will instruct them on why they can't see the site and how to upgrade.

Any ideas how best to achieve this?

Cheers, Mark.

I agree with Bryan, personally I like to use the Browser-Update.org notification.

Nevertheless, if you want to use .htaccess just make sure you put your redirect code before Crafts routing:

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Redirect old Browsers
    RewriteCond %{HTTP_USER_AGENT} "MSIE [1-7]" [NC]
    RewriteRule ^(.*)$ http://something.com/else [R=301,L]

    # Crafts routing
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
    RewriteRule (.+) index.php?p=$1 [QSA,L]
</IfModule>


Show on user profile page the commissions he is connected to I have a commissions section in where we have a matrix field where we add the user (from craft.users) and the function of the user.

A user can be in more that one commissions.

On the user profile page, we want to show besides some personal information, all the commissions in which he is active.

For a single relation field (user type field), we have no problem, but I can't get it working with this matrix field.

Matrix field info:

Section: commissionmembers
Blocktype: members
Fields: member (fieldtype users)
        function (plain text)

This is a working example with a Users field

    {% for user in craft.users.id( craft.request.lastSegment ).limit(1) %}
    {% set commissions = craft.entries.section('commissions').relatedTo(user) %}
        {% for commission in commissions %}
            {{ commission.title }}
        {% endfor %}
    {% endfor %}

Hope you can point me in the right direction how to get it working with the matrix field

You’d need to query for all Matrix Blocks the user is related to using a Matrix Block ElementCriteriaModel. Unfortunatelly this is not possible from the template level by default, you need to either install the plugin Low Blocks, or write some some lines of PHP in a custom plugin.

Here’s how you would have to set the params for the Low Blocks function:

{% set userId = craft.request.lastSegment %}
{% set user = craft.users.id(userId).first() %}

{% if not user %}
    {% exit 404 %}
{% endif %}

{% set matrixField = craft.fields.getFieldByHandle('commissionmembers') %}

{% set blocks = craft.lowblocks.blocks.fieldId(matrixField.id).type('members').relatedTo({
    targetElement: userId,
    field: 'member',
}) %}

{% for block in blocks %}
    {% set commission = block.getOwner() %}

    {{ commission.title }}
    {{ block.function }}
{% endfor %}


relatedTo 'and' with 'or' Is the following possible:

{% set issue = craft.entries(section: 'issues', slug: 'october-2014').first() %}

{% set categoryOne = craft.categories({ group: 'articles', slug: 'category-slug-one'}) %}
{% set categoryTwo = craft.categories({ group: 'articles', slug: 'category-slug-two'}) %}
{% set categoryThree = craft.categories({ group: 'articles', slug: 'category-slug-three'}) %}

{% for entry in craft.entries({
    section: 'articles',
    relatedTo: ['and',
        ['or',
            { targetElement: categoryOne },
            { targetElement: categoryTwo },
            { targetElement: categoryThree }
        ],
        { targetElement: issue }
    ]
}) %}

    {{ entry.title }}

{% endfor %}

Attempting to return articles associated with a specific issue from within one of three specific categories.

It appears the parseRelationParam function looks at the first array element to determine the operator but I'm hoping someone has a solution.

You can simply get all the relevant categories at once and combine your relatedTo parameters with a single and operator:

{% set issue = craft.entries({ section: 'issues', slug: 'october-2014' }).first() %}
{% set categories = craft.categories({ group: 'articles', slug: 'category-slug-one, category-slug-two, category-slug-three' }) %}

{% set entries = craft.entries({
    section: 'articles',
    relatedTo: [
        'and',
        { targetElement: issue },
        { targetElement: categories }
    ]
}) %}


When to add a field to an asset? I've added a field to an asset which in my case is a fallback image for a video asset.

When uploading a video the field for the fallback image isn't visible so the chances of forgetting to set one is bigger.

To set the fallback image I need to edit my video asset and set the fallback image in there.

In my case I'd rather set the fallback image in the entrytype so it won't get forgotten.

When does it make sense to add the field directly to the asset instead of defining a field in a entrytype?

Fieldlayout entrytype version:

Video (asset)
Video Thumbnail (asset)

It makes sense, when it's a task that should only be done once and is directly related to the asset.

A good example I have of an Asset custom field is a field allowing the content-administrator to define where an images focal point is positioned e.g. top left, bottom right, top right, etc.

I'm able to make use of the field EVERY time I display the image asset, but only need to deal with the custom field, once.

Part deux (in answer to your comment):

Yes, in my experience, although you can also double click an asset from the new/edit entry screen and see it's custom fields e.g. "Focal point position". See image below...


How to programmatically set a Smart Map address field in plugin I have a console command plugin that needs to periodically go and fetch property data via API and import it into Craft. I can pull in basic property information but when it comes to importing the data into Smart Map field I'm a bit lost as how to format the data so that it saves correctly. Has anyone had to do this before?

Edit:

I don't think the console command part should make much difference. Basically I am trying to save a smartmapfield on my entry. From the API I am using I get a string with the full address. I first extract the postcode and then use the smartmap plugin to get the Lat and Lng values:

$postcode = $matches[0];

//Look up Latitude and Longitude from Google API

$response = craft()->smartMap->lookupCoords($postcode);
$lat = $response['lat'];
$lng = $response['lng'];

What I am not sure of is how to format this data so that it can be saved to the entry. So for standard fields thats straight forward:

$entry->getContent()->title = $property['RoomName'];

How do I do this for my smartmap field?

I figured it out, didn't realise all I had to do was pass array like so:

$entry->getContent()->address = array('lat' => $lat, 'lng' => $lng, 'street1' => $street1, 'zip' => $postcode);


Return all Matrix blocks with a drop down set to a specified value I'm in the context of an Entry that has a Matrix field, and one of those Matrix fields is a Dropdown called subsection. I want to grab all Matrix blocks that have subsection set to 'intro'.

This is how I'm trying to do it and it definitely doesn't work. Is there another way to accomplish this?

{% for block in entry.myMatrix.subsection('intro') %}
…
{% endfor %}

How about:

{% for block in entry.myMatrix if block.subsection == 'intro' %}
    ...
{% endfor %}


Update checkbox value through plugin (Craft 3) I'm trying to update a checkbox field of an entry through a plugin service. I tried two things:

Get the field values with $entry->fieldHandle->getOptions(), then manipulating the resulting array, then set the value with $entry->fieldHandle->setOptions()

$contact = Entry::find()
    ->id(...)
    ->one();
$newsletters = $contact->contactNewsletterSubscriptions->getOptions();
$newsletters[$index]->selected = false;
$contact->contactNewsletterSubscriptions->setOptions($newsletters);
Craft::$app->elements->saveElement($contact);

(where $index is the array index of the setting I want to change)

-> this didn't work (no error message, changes just didn't get saved)

If I make a var_dump of $contact, the field contactNewsletterSubscriptions is showed as string. I played around with json_decode and serialize to get the right formatting (it looks exactly as when doing a var_dump):

$newsletterSubscriptions = json_decode($contact->contactNewsletterSubscriptions->serialize());
$index = array_search($newsletterName, $newsletterSubscriptions);
unset($newsletterSubscriptions[$index]);
$contact->contactNewsletterSubscriptions = (string)json_encode(array_values($newsletterSubscriptions))

-> doesn't work either; value gets saved as string; if I later call getOptions() on the field I'm getting "Call to a member function getOptions() on string"

Any idea how I can change the status of a checkbox from selected = true to selected = false?

Best regards.

You need to set the field value, not the options

$entry->setFieldValue('fieldHandle' [
    'value1', 'value2'
});
Craft::$app->getElements()->saveElement($entry);

If you want to change the values, the easiest method is

$allOptions = $entry->getFieldValue('fieldHandle');
$selected = [];
foreach($allOptions as $option){
    if($option->selected){
         $selected[] = $option->value;
    }
}
// unset a value
unset($selected[1]);
// add another value
$selected[] = 'anotherValue';
$entry->setFieldValue('fieldHandle', $selected);
Craft::$app->getElements()->saveElement($entry);

edit

Another approach - a little bit less code and more general since it's the same for every single field in Craft - but more resource heavy

$field = Craft::$app->getFields()->getFieldByHandle('handle);
$selected = $field->serializeValue($entry->getFieldValue('handle'), $entry);
$selected[] = 'anotherValue';
$entry->setFieldValue('handle', $selected);
Craft::$app->getElements()->saveElement($entry);


Is it possible to have multiple locales that use different domains instead of subfolders? Instead of:

example.com/en
example.com/de

I'd have:

example.com
example.de

Can I do this with craft? I see this has been discussed, but a complete solution hasn't been posted.

It is discussed here in the craft docs:

There is an example with different toplevel domains.

English and German each get their own domain

craft/
public_html/            --> example.com/
    .htaccess
    index.php
public_de/              --> example.de/
    .htaccess
    index.php


Order products by lowest product variant Currently using Craft Commerce 2.

I was wondering if it's possible to order products by the lowest priced variant. I have tried something along the lines of this, but it doesn't do anything different with the ordering:

{% set products = craft.products.type('accommodation').all() %}

{% for product in products %}
   {% if product.getVariants()|length %}

        {% set variants = craft.variants ({
            product: product,
            orderBy: 'price asc'
        }) %}

        {% for purchasable in variants %}
            // Products Here
        {% endfor %}

    {% endif %}
{% endfor %}

I have also tried something else along the lines of this:

{% set products = craft.commerce.products({
    relatedTo: 'accommodation',
    order: 'defaultPrice asc',
}) %}

I managed to resolve this using the supersort plugin and using 'defaultPrice' instead of 'price'. Example below:

{% set products = craft.products.type('accommodation').all() %}
{% set order = products|supersort('sortAs', '{{ object.defaultPrice }}') %}

{% for product in order %}

    // Products

{% endfor %}


Getting a phpmailerException error due to my attachments when my plugin runs When my plugin runs, I'm getting a PHP Mailer Exception error stating:

"Could not access file: http://s3.amazonaws.com/patternmanager/clients/healthstream/question-attachments/download.jpeg"

The url being pulled in is correct, and the file exists. I'm still very new to this, but the best I can figure out is that this is some kind of permissions issue?

Is there something in my code that might be causing a problem? Am I calling in the attachments correctly? Any direction is greatly appreciated.

public function init()
{
    parent::init();

    //Event: onSaveEntry
    craft()->on('entries.saveEntry', function(Event $event)
    {

        $entry = $event->params['entry'];
        $isNewEntry = $event->params['isNewEntry'];
        $entrySection = $entry->section;

        $attachments = $entry->questionAttachments;

        if ($isNewEntry and $entrySection == 'Questions')  {
            //Get all users from UserGroup
            $user_criteria = craft()->elements->getCriteria(ElementType::User);
            $user_criteria->groupId = '9';
            $users = $user_criteria->find();

            //Send to each User
            foreach ($users as $user) {
                //Send Mail
                $email = new EmailModel();
                $email->toEmail = $user->email;
                $email->subject = 'New Question';
                $email->htmlBody    =

                'A new question has been posted: url.'" title="'.$entry->title.'">'.$entry->title.'';

                if ($attachments) {
                    foreach ($attachments as $attachment) {
                        $email->addAttachment($attachment->url, $attachment->filename, 'base64', $attachment->kind);
                    }
                }

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

PHPMailer is only going work with local files for attachments, so you'll need to download the file from S3 into a known location, and pass that location in as the first parameter to addAttachment.


Plugin init function executed more than once To test a service method, I'm executing it in my init function. The method adds a row to the database table with a mail address and variant id:

$this->waitingListService->add(7, 'email@mailserver.com');

However, when I refresh a page in the control page to execute the plugin init function 2, or sometimes 3 rows are being added to the database table.

Is this the default behaviour and if not, how can I fix it?

Thanks in advance,
Sam

The init function should only trigger once for each instance. So when you do something like new Plugin it is executed multiple times.

But keep in mind that it is executed in every request. So when you are in the CP there are many different ajax calls (every time when you reload entries at the index page for example). You'll create a new row for each of those requests

In order to disable your function you can check

$request = Craft::$app->getRequest();
if (!$request->getIsAjax() && !$request->getIsConsoleRequest()) {

}


Searching Matrix field Can’t believe this is the first time I’ve had to do this, but I need to search a field inside a matrix and I can’t find any documentation on this. Craft 2 if that matters. I had hoped something like

  craft.entries.search('matrixField:field:value')

would work but doesn’t seem to. Anyone help me out?

As far as I know (please correct me if I missed something) you can only search for the entire matrix by matrixField:value and not certain fields within your matrix since the table searchindexes only contains the id of the matrix field itself, not the id of the field inside the matrix.

However: you could create a Query from your ElementCriteriaModel in PHP via

$command = craft()->elements->buildElementsQuery($criteria);

Join your matrixblock table as well as your matrixblock content table

$command->join("your matrixTable", "matrixTable.ownerId = elements.id");
$command->join("your matrixContentTable", "matrixContentTable.elementId = matrixTable.id");

And search for your field with a where

$command->andWhere('MyAwesomeField LIKE %whatever%');


Contact Form: Name and Email in Message Body This is a follow-up to an earlier question from 2014: P&T Contact Form plugin: How can I copy “name” and “email address” in the body of the mail? ... it seems that Craft (and the plugin) has gone through significant updates since that question was answered.

How would I include fromName and fromEmail into the body of the message?

I read about events in the documentation, but it doesn't say where they go. Do I put those into the craft>config>contactform.php file? How would I setup an event to handle that inclusion?

I figured it out by duplicating the field...

<input id="fromName" type="text" name="fromName"
value="{% if message is defined %}{{ message.fromName }}{% endif %}">
{{ message is defined and message ? errorList(message.getErrors('fromName')) }}
<input type="hidden" id="fullName" name="message[Full Name]" class="fullName">

Then duplicating the single entry to the hidden with some Javascript...

    $('#fromName').bind('keypress keyup blur', function() {
      $('#fullName').val($(this).val());
    });

It parses through to the email message as:

Full Name: First Last


Plugin logging in Craft 3 How is logging handled in Craft 3? There's nothing mentioned in the docs at all.

None of the below exist.

Craft::log()
MyPlugin::log()
MyPlugin::$plugin->log()

I'm not seeing any logging in any of the other plugins I've got on this site.

Halp!

You can use Craft::getLogger()->log($message, $level, $category = 'application')

It uses the Yii2 logger, you can change the $category to your plugin handle

Edit: You can use the "short" syntax as well class reference

Craft::warning($message, $category);

or

Craft::info($message, $category);


How to approach a multi-level floorplan On the property development website I'm building for an agency client it has a section on the individual property page that shows the floorplans for the building. Which has different amounts of levels depending on the property. So for example one property might have a basement, a ground floor, and a first floor. Whereas another might have a Ground, First and Second Floor etc.

Each floor has an image of the floorplan as well as text showing the dimensions on the left of it.

On the design visual the floors are switched between using the dropdown button, which lists the available floors.

So I'm not sure how to approach this. Should it be a matrix, or set fields etc, and how will it link up to the dropdown.

Need some guidance on the best approach that's going to make it as simple as possible for the client?

Kind regards

David

I'd use a Matrix field with a block for each floor. Adding a block provides fields for an Asset for the floorplan and a text box for the dimensions.

The dropdown is purely javascript based, and I'd suggest you put all the floors in the HTML on page load, then switch between them using the values of the dropdown field.


Date-time field within tables I'm after using a date-time field within a table cell. I suspect it requires a plugin to make this happen - has anyone done this?

Adding more cell types to the Table Fields isn't possible without a plugin. Even with a plugin it's probably not possible to simply extend the default field. I tried to do this and couldn't find a way to extend upon the javascript necessary to add or delete rows.

So to make more cell types possible I ended up copying the complete Table Field code from Craft core into my plugin.

"Event Table"

Someone might ask what this is good for, you could use Matrix instead!
Sure, but sometimes you're in the need for another hierarchy level or you'd simply opt for the cleaner, more compact view of a table field for your date or time inputs.


Craft 3 plugin to import channel entries I try to finish a plugin which imports data into my craftcms project. I already created a console based method/service, which I trigger/run in my shell. Inside my method(s) I receive data (XML or JSON) I parse my data and try to create and fill an entry of a specific channel I already created.

I tried "saveElement()" which doesn't work.

I found some tuts and informations for craftcms v2 (for example: https://docs.craftcms.com/api/v2/craft-entriesservice.html#public-methods)

Now i am stuck and i can not find any informations on how to solve this with craftcms v3.

Here is my last version of code after hours of different trys :(

$section = Craft::$app->sections->getSectionByHandle('testentry');
$entryTypes = $section->getEntryTypes();
$entryType = $entryTypes[0];
// Save Entry
//$entry = new EntryModel();
$entry = new \craft\elements\Entry();
$entry->sectionId = $section->id;
$entry->typeId = $entryType->id;
//$entry->locale = Craft::$app->i18n->getPrimarySiteLocaleId();
//$entry->authorId = 1;  // TODO:  Set author
$entry->enabled = true;
$entry->postDate = $post['post_date'];
$entry->slug = $post['post_name'];
//      $entry->getContent()->title = $post['post_title'];
//      $entry->setContentFromPost(array(
//          'body' => $postContent,
//          'categoryCareer' => NULL,
//      ));

if (Craft::$app->elements->saveElement($entry)) {
    $result = true;
}
else {
    echo 'Could not save the Job entry.'."\n";
    $result = false;
}

As per your comments, the title field is required and causing a validation error. This can be resolved simply by setting the title.

$entry->title = 'The Title';

You can output the validation errors to be notified of any issues with saving the entries as follows.

if ($result === false) {
    \Craft:dd($entry->getErrors());
}


Is there a safer way to Cycle Through Post Data in Craft Currently, I am doing this.

$responses = array(
        "E" => 0,
        "I" => 0);

foreach ($_POST as $key => $value) {
    if (strlen($value) == 1) {
        ++$responses[$value];
    }
}

Is there a way to use craft()->requests for this? I tried

foreach (craft()->request->getPost() as $key => $value) {
    if (strlen($value) == 1) {
        ++$responses[$value];
    }
}

But this did not seem to work.

I wouldn't consider it as good practice to do what you want, there are good reasons why something like this is not possible by default and why all functions to get all parameters are private.

A better way is to send your parameters inside an array and fetch this or you can create an array of all possible parameters and loop it

$secureParameters = ['A', 'B', 'C'];
$request = Craft::$app->getRequest();
$values = [];
foreach($secureParameters as $key){
    $values[$key] = $request->getBodyParam($key);
}

If you really don't care about receiving unexpected parameters and raising exceptions you can also loop your $_POST directly foreach($_POST as $key => $v){ and fetch the $request->getBodyParam($key) at least


Slow Site Speed and Increasing Memory We are experiencing incredibly slow site performance. Any tips on where to find out how much memory the site is using, or if increasing this will increase internal site loading and navigating speed? Thanks for your help!!!

You're asking a loaded question. When you mention memory, how exactly are you measuring that?

Site performance can depend on a few factors, including the hosting provider, the hardware, what Craft fields you're using, etc. Craft tends to hit the database a lot so if your connection to MySQL isn't good—say on a mediocre shared hosting provider—that's going to slow you down.  Using a lot of Matrix fields can really slow you do or if you're trying to fetch a lot of entries at once.

The best answer I can give without specifics—and the biggest win—is to do some caching with Craft's  {% cache %} tags.  Using the cache will cause Craft to look in its already generated output instead of multiple trips to the db.   Anything that has a  {% for %} loop block is likely going to generate a lot of db queries and is a good candidate for caching (like a navigation menu).

If you're already using caching, make sure you're doing it efficiently. Craft stores the output of the cache tag so you need to put the tags around things that actually output something—you can't cache variables.

For example:

{% cache %}
    {% set whateverEntries = craft.entries.section('whatever') %}
{% endcache %}

{% for theEntry in whateverEntries %}
...

... won't cache anything.  But this would:

{% cache %}
    {% set whateverEntries = craft.entries.section('whatever') %}
    {% for theEntry in whateverEntries %}
        <h1>{{theEntry}}</h1>
           {{theEntry.postExcerpt}}
         ...
    {% endfor %}
{% endcache %}


Using cURL or file_get_contents is empty when calling a Craft page I'm trying to load a page from a Craft based website using file_get_contents or cURL. Using get_file_contents I get an empty result, using cURL I can see that the request is getting redirected to the login page.

If I switch to a different domain that is also running Craft the file_get_contents and cURL both work fine.

Any idea what could possibly be causing the redirect to be happening?

Found a solution. I needed to set the requireUserAgentAndIpForSession to false,  http://buildwithcraft.com/docs/config-settings#requireUserAgentAndIpForSession

Seems like having to disable this for a particular url could open up other issues across the board.


Problem with Adjuster plugin Class not found I'm basically having this problem Getting started with Order Adjuster plugin

I have the file plugins/carbonbalance/carbonBalancePlugin.php containing;

<?php

namespace Craft;
Craft::import('plugins.carbonbalance.adjusters.carbonBalanceAdjuster');

class CarbonBalancePlugin extends BasePlugin {

    ... usual standard plugin methods ...

    public function init()
    {
    }

    public function commerce_registerOrderAdjusters()
    {
        return [
            new CarbonBalanceAdjuster
        ];
    }

}

and then in plugins/carbonbalance/adjusters/carbonBalanceAdjuster.php I have

<?php

namespace Commerce\Adjusters;
use Craft\Commerce_LineItemModel;
use Craft\Commerce_OrderAdjustmentModel;
use Craft\Commerce_OrderModel;

class CarbonBalanceAdjuster implements Commerce_AdjusterInterface {

    public function adjust(Commerce_OrderModel &$order, array $lineItems = []){

    $myAdjuster = new Commerce_OrderAdjustmentModel();

    return [$myAdjuster];

   }

}

But I get the following error message

Class 'Craft\CarbonBalanceAdjuster' not found

I understand this is most likely a naming issue, but I cannot see where I'm going wrong in that respect.

Update
I think I have got past this problem by using;

public function init()
    {
        //Craft::import('plugins.carbonbalance.adjusters.carbonBalanceAdjuster');
        require_once('adjusters/carbonBalanceAdjuster.php');

    }

public function commerce_registerOrderAdjusters()
    {
        return [
            new \Commerce\Adjusters\CarbonBalanceAdjuster
        ];
    }

This appears to load the Adjuster, but shows other errors because I haven't yet completed the adjust method.

carbonBalanceAdjuster.php should be CarbonBalanceAdjuster.php since the name of the class is CarbonBalanceAdjuster and you're probably on a case-sensitive file system, which is why auto-loading can't find it.

The same goes for your other plugin files as well.  You can use https://pluginfactory.io to generate boilerplate plugin code to help avoid case-sensitivity issues like this.


Passing variants to cart in Commerce I'm trying to pass variants to the cart page. They variant field I want to pass is 'varHeight'. I'm using:

<form method="POST">
  <input type="hidden" name="action" value="commerce/cart/update-cart">
   {{ redirectInput('shop/cart') }}
   {{ csrfInput() }}
  {% for item in cart.lineItems %}
   ...
  {{ item.description }}
  {% for lineItem in cart.lineItems %}
   {{ lineItem.purchasable.varHeight }}
  {% endfor %}
    ...
  {% endfor %}

This works but puts the 'varHeight' value on every product in the cart instead of just the product it relates to. I'm sure it's simple but need some input on how to do this.

I think you're asking how to output custom fields based on their type.

Something like this should work:

{% for lineItem in cart.lineItems %}

// Is the current line item a variant purchasable?
{% if className(lineItem.purchasable) == 'craft\commerce\elements\Variant' %}

  {% switch lineItem.purchasable.product.type.handle %}
    {% case "clothing" %}
        // Only output the varHeight if the product type is clothing
        {{ lineItem.description }}<br>
        Var Height: {{ lineItem.purchasable.varHeight }}
    {% default %}
        {{ lineItem.description }}
  {% endswitch %}

{% else %}

  // Non variant purchasable
  {{ lineItem.description %}

{% endif %}

{% endfor %}


Do ImageOptimize Volume Variant Transforms replace Craft's default Asset Transforms? Do the ImageOptimize variants that are set on volumes replace the default image transforms that can be set within Craft asset settings?

I just want to check as the plugin documentation isn't really explicit about this.

No, they do not replace Craft transforms. They are in addition to. And in fact, Image Optimize uses regular old Craft Transforms under the hood.

Image Optimize will do three major things for you:

Every image transform you do, whether it be via Craft image transforms, or the Optimized Images field will be run through a variety of image optimizers that you have installed, including making .webp variants
An Optimized Images field makes it easy to create an image srcset for doing responsive images
You can entirely replace the transform method from your local server to a service like Imgix or Thumbor

These all operate independent of each other, but they can work together as well.


Put SEO and Twitter card Meta tags in the Head section prior the render of the template I need to put some <meta /> tags in the <head /> section of the html document prior the rendering of the template. I would like to know how can I achieve this? I also need to query an external database in order to get some data from it and populate the meta tags. What I want to accomplish is to pass a link to my website and get Twitter to display a card with the image, description, etc.
What is the best option in order to accomplish this task? a Plugin? a Controller? a Module? Does anybody know any good tutorial or guide in order to do this?
I'm using CraftCMS ver 3.4.9
Thank you in advance for your support.

There's a plugin that allows you to connect to an external database, which will allow you to do a query within the template. Remember to cache the result though as it could slow things down otherwise.
Another option is to sync the external data with Craft using feed me, so you can pull the data locally.


How to create an auxiliary page with sub url? I have a section where each item has its own URL e.g:
news/{slug}

The Entry Template for this section is:
news/news_detail

For each post I would like to have a link to a unique page which is populated with information from the fields from the parent post/group, e.g news.

The slug would look something like:
news/{slug}/extra

The Entry Template for the news_detail page has a rich text field:
additional

To make this more complicated, the link to this page will be visible only if the "additional" field is populated with data.
And finally each "extra" page should have a link back to the originating post.

I had considered using a simple JavaScript toggle, but having urls for the content is more important in this instance, so not an option.

Thank you for any pointers as I am not sure where to start.

First you'll want 3 templates — I'll call them 'news/index.html', 'news/_entry.html', and 'news/_extra.html'. (fyi... using an underscore before the template name is good practice as it prevents the template from being loaded directly; which would likely throw an error because no entry would be defined.)

You'll then want to create a custom route to the 'extra' page in settings, using the 'slug' token for one of the segments.

If your url looks like this: news/slug/extra (where 'slug' is the slug token)
Load this template: news/_extra.html

In the '_entry.html' template, you'll want to check your 'additional' field and create a link to your '/extra' page. Note: 'entry.url' should already include the 'news/slug' portion of the uri — we'll just be adding the '/extra' segment.

{% if entry.additional %}
    extra page
{% endif %}

In the '_extra.html' template, there should now be a 'slug' variable available to your entry.

{% set newsEntry = craft.entry.section('news').slug(slug) %}
{% set newsIndex = craft.entry.section('pages').slug('news') %}
{{ newsIndex.title }} — {{ newsEntry.title }}

Note: I don't know in what section your 'news' index page is. I just picked something at random — you'll need to change that to match your structure.


composer update missing zip extension (ext-zip) on MacOS When doing a composer update in a Craft project on MacOS Catalina, I suddenly get the following error:

***** requires ext-zip * -> the requested PHP extension zip is missing from your system.

Any ideas?

The reason seems to be that either the PHP version of the MacOS Catalina update or a version installed via brew do not come bundled with the ext-zip extension.

You can verify that by calling php -m in the Terminal. You will get a list of all modules and it will not contain the word zip

Even if it contains the word zip it does not necessarily mean that composer has it available. This is caused by the way ZSH (shell on MacOS) handles the PATH variable.

In my case I used an alias pointing to the MAMP version of PHP (which has zip enabled), but composer was using the PHP version found in the PATH variable.

Solution for me: Since I am using MAMP I modified the PATH in /etc/paths to have the MAMP PHP PATH on the very beginning, e.g.

/Applications/MAMP/bin/php/php7.1.31/bin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

After that change composer update should work again as expected (make sure you close your Terminal first and do the update in a new Terminal window). Also php -m should now definitely list the zip extension


Display list of assets that are in use? I am very new to Craft and how it works. I've got a client with a Craft site (v2.9.2), that I help them with. It was created by another developer.
I want to help them tidy up their Assets.
There are two folders (on S3) for their Assets. /Media and /Media/Files.
Scanning by eye, I can see /Media/Files appears to be a subset of what's in Media (meaning, they are duplicate files). Before we potentially delete the /Media/Files folder, I'd like to programatically confirm if any of them are being used. My suspicion is only files in /Media/ are used on the site.
Is there any easy way to see which assets are in use, and where they are used?
I found questions on StackExchange re how to see what media is not in use, such as this one. I also found a question on how to do something similar to what I need.
Although, to be honest, I had no idea where I'd utilise such code. I'm not going to get into creating a new plug-in.
For example, this was shared in the answer to that question:
{% set content = craft.entries.section('content').find() %}
{% set usedFotos = craft.assets.relatedTo(content).order('filename').find() %}

{# Loop throught the array of assets #}
{% for usedFoto in usedFotos %}

{{ usedFoto.filename }}

        {# Loop throught entries related to this asset #}
        {% for entry in craft.entries.relatedTo(usedFoto).order('title') %}

{{ entry.title }}

        {% endfor %}

{% endfor %}

Where would I utilise this code?

There's a plugin that you can use to find unused assets: Asset Usage
This gives you an extra column in the Assets listing to indicate if an asset is used or not. Unfortunately, the Craft 2 version doesn't have the console command to delete all unused assets that the Craft 3 version does, but this may help you anyway.


Loading data in Ajax in an entry So, I've created a plugin which has to send dynamic data to an entry in the control panel, using a custom field type

I get an error in the ajax call, saying that the URL to which I'm posting is not found. I've debugged this and the URL generated is the right one, I've tried posting to it from other places and it works just fine

This is what I have so far

The custom field

public function getInputHtml($name, $value)
{

    craft()->templates->includeJsResource('baseinstagram/js/pullajax.js');

    return craft()->templates->render('baseinstagram/input/choosetag', array(
        'name'  => $name,
        'value' => $value
    ));
}

The ajax call

$(document).ready(function () {

var url = Craft.getActionUrl('baseinstagram/main/testy');

$('.pullme').click(function () {
    $.ajax({
        method : 'POST',
        url : url,
        success : function () {
            console.log('ok');
        },
        error : function() {
            console.log('nope');
        }
    });
});

});

the controller

public function actionTesty() {
    $this->requireAjaxRequest();

    $this->returnJson('heyeyeye');

}

Fixed it, it was just a matter of proper capitalization

var url = Craft.getActionUrl('baseinstagram/main/testy');

Should have been

var url = Craft.getActionUrl('Baseinstagram/Main/Testy');


Accessing the raw post data from an $event My users have an accountBalance field and there's a createInvoice service in my plugin. I'd like to autogenerate an invoice whenever the accountBalance changes (including from the admin cp), while optionally passing in extra invoice info. Is there a way to access the raw post data from the event to check for extra info like invoice skus and descriptions?

In my plugin I have the following code.

craft()->on('users.onBeforeSaveUser', function(Event $event)
{
    $userId = $event->params['user']->id;
    $user = craft()->users->getUserById($userId);

    //raw post data is private so these lines are pseudo code
    if ($extraPostData != null)
    {
        $desc = $event->params['_rawPostData']['invoiceDescription'];
        $sku = $event->params['_rawPostData']['invoiceSku'];
    }

    $oldBalance = $user->accountBalance;
    $newBalance = $event->params['user']->accountBalance;
    if ($newBalance != $oldBalance)
    {
        $balanceDelta = $newBalance - $oldBalance;

        //plugin service has defaults if $desc/$sku are empty or null
        craft()->myPlugin->createInvoice($balanceDelta, $userId, $desc, $sku);
    }
}

Alternatively, is there a way to disable adjusting the accountBalance user field from the admin cp?

You can just grab whatever you need either from $_POST or, more preferably, craft()->request->getPost().