How do you delete assets from a frontend form? I am creating a proof of concept for a client in Craft where the client's customers can manage projects from the frontend of a Craft install.

I have a frontend form working correctly for creating a new project and uploading files attached to it... however the missing piece to the puzzle for full management is being able to delete a project entry and also being able to manage the media attached to the project entry.

Is it possible to let a frontend user manage assets, in particular deleting assets from their username folder?

You can create a POST request via Ajax to the assets/deleteFile (Craft 2) / assets/delete-asset (Craft 3) controller action:

Craft 2:

$('#delete-file').click(function() {
    var data = {
        fileId: 100
    };

    $.post('/actions/assets/deleteFile', data, function(response) {
        // ...
    });
});

Craft 3:

$('#delete-file').click(function() {
    var data = {
        assetId: 100
    };

    $.post('/actions/assets/delete-asset', data, function(response) {
        // ...
    });
});

That controller action will require that the user is logged-in with a user account that has the “Remove files” permission on the asset source that the file belongs to. Granting that permission to a user will give them the ability to delete any file within that asset source though - not just their own. So this is probably not a good idea.

A better way of doing this would be to write a module that provides its own controller that does its own permission checking and handles the file deletion. It would look something like this:

Craft 2:

class MyController extends BaseController
{
    public function actionDeleteFile()
    {
        $this->requireAjaxRequest();

        $fileId = craft()->request->getRequiredPost('fileId');
        $file = craft()->assets->getFileById($fileId);

        if (!$file)
        {
            throw new Exception('Could not find a file with the ID '.$fileId);
        }

        // Make sure that it's within a folder with the same name as the user's username
        if ($file->getFolder()->name != craft()->userSession->getUser()->username)
        {
            throw new HttpException(403);
        }

        $response = craft()->assets->deleteFiles($fileIds);
        $this->returnJson($response->getResponseData());
    }
}

Craft 3:

use Craft;
use craft\web\Controller;
use yii\web\BadRequestHttpException;
use yii\web\ForbiddenHttpException;
use yii\web\Response;

class MyController extends Controller
{
    public function actionDeleteFile(): Response
    {
        $this->requireAcceptsJson();

        $assetId = Craft::$app->request->getRequiredBodyParam('fileId');
        $asset = Craft::$app->assets->getAssetById($assetId);

        if (!$asset) {
            throw new BadRequestHttpException('Invalid asset ID: ' . $assetId);
        }

        // Make sure that it's within a folder with the same name as the user's username
        if ($asset->getFolder()->name != Craft::$app->user->getIdentity()->username) {
            throw new ForbiddenHttpException('Not permitted to delete asset ' . $assetId);
        }

        $success = Craft::$app->elements->deleteElement($asset);
        return $this->asJson([
            'success' => $success,
        ]);
    }
}

With that in place, you’d just need to update your JavaScript to point to your deleteFile/delete-file action instead of Craft’s.


Use dropdown field to add a class to the body tag I have a website where I want to be able to use different background colors on different project pages.
The way I want to achieve this is by having a dropdown field in Craft to select from a set of values on each project page. I will then use that to set a class on the body tag so that I can style that accordingly with css.
I have created a dropdown field with the handle backgroundColor.
I have added the these options as values:

default
yellow
green
blue
purple

and here is the code in the template
{% if entry.backgroundColor.default %}
  {% set bodyClass = "page-project" %}
{% elseif entry.backgroundColor.yellow %}
  {% set bodyClass = "page-project bg-yellow" %}
{% elseif entry.backgroundColor.green %}
  {% set bodyClass = "page-project bg-green" %}
{% elseif entry.backgroundColor.blue %}
  {% set bodyClass = "page-project bg-blue" %}
{% elseif entry.backgroundColor.purple %}
  {% set bodyClass = "page-project bg-purple" %}
{% endif %}

This gives me and error saying that the property does not exist.
Does anyone know what the problem might be, or if there is a better way to do this.
Thanks!

Set up your dropdown so that the Option Labels are default, yellow, green, etc. and the Values are page-project, page-project bg-yellow, page-project bg-green, etc. Then, you can
{% set bodyClass = entry.backgroundColor %}

This will set bodyClass to whatever the value is for the chosen dropdown element, i.e. page-project if you pick the default option.


Second Site Channel URL Route Not Working I've set up a Resources section on a multi-site installation with the type of Channel. For Site 1 I've set the routing rule to be {resourcesParent.one.slug}/{slug} and for Site 2 I've set the rule as {slug}.

So for Site 1 a resource titled sample-resource would resolve to www.example.com/parent-slug/sample-resource.

And for Site 2 it would resolve as sub.example.com/sample-resource

Everything works fine for Site 1, however when I go to sub.example.com/sample-resource I get a "template not found: sample-resource" 404 error.

Seems like this should work so I'm not sure what I'm doing wrong.

EDIT: I just noticed that sub.example.com/parent-slug/sample-resource is resolving, so I think something's wrong with my config. Here's what I have for my dev environment in craft/config/general.php:

'dev' => [
    'siteUrl' => array(
        '//www.example.com',
        '//sub.example.com'
    ),

I forgot to add define('CRAFT_SITE', 'sub'); to the index.php file on my Site 2 web root.


How to display entries with a custom date field within month I've got a date field, and I'm grabbing the month and year from the url. What I'm after is to display all the events within the month, but for some reason I can't get this to work

{% set firstDayOfMonth = month ~ "/1/" ~ year|date("U") %} // returns the 1st day of the month in unixtime

{% set daysInThisMonth = firstDayOfMonth|date('t') %}
{% set lastDayOfMonth = month ~ "/" ~ daysInThisMonth ~ "/" ~ year|date("U") %} // returns the last day of the month in unixtime

{% set thisMonthsEvents = craft.entries.section('events').eventDate('and', '>= ' ~ firstDayOfMonth, '<= ' ~ lastDayOfMonth) %}

{% if thisMonthsEvents | length %}
    some events
{% else %}
    no events
{% endif %}

Any ideas? firstDayOfMonth and lastDayOfMonth return dates fine, but no matter how I format them, the result is no entries returned. For example, firstDayOfTheMonth could output "2014-6-01" and lastDayOfMonth could output "2014-6-30", and I've got an event with an eventDate of 18th June 2014, but nothing is being returned.

Solution 1:

The problem with your code is, that you apply the date filter exclusively to the year variable:

  {% set firstDayOfMonth = month ~ "/1/" ~ year|date("U") %}

To get this right you have to first compose your "datetime string" and then apply the filter to the string as a whole:

{% set firstDayOfMonth = (month ~ "/1/" ~ year)|date("U") %}

That's also the reason why it works for you to apply it inside the query.

.

Solution 2:

You don't need to pass a unix datetime formated string to your query, a string in format "Y-m-d" is just fine:

{% set firstDayOfMonth = year ~ "-" ~ month ~ "-01" %}
{% set daysInThisMonth = firstDayOfMonth|date('t') %}

{% set lastDayOfMonth = year ~ "-" ~ month ~ "-" ~ daysInThisMonth %}

{% set thisMonthsEvents = craft.entries.section('events').eventDate('and', '>= ' ~ firstDayOfMonth, '<= ' ~ lastDayOfMonth) %}


How to skip the first X entries using ElementAPI I'm looking how to use the .offset(x) parameters of craft.entries inside the ElementAPI.

I tried to add 'offset' => 1 before, after and inside the criteria array, but the x first entries are still added to the JSON.

How can I skip the first X entries of ElementAPI?

It seems that offset doesn't work when pagination is set to true (which is its default value).

If you don't need pagination for your results, you can add your offset in criteria and set pagination to false.

If you do need pagination and the desired offset is the same as elementsPerPage, you can get your results starting from page 2. You'll have to make sure you're actually getting page 2 because it will return page 1 if there aren't enough results for a second page.

If you do need pagination and the desired offset is different from elementsPerPage, you can try what I've done here.

'stories.json' => function() {
    // Get get IDs of the "offset" entries. Set `limit` to the offset amount you want.
    $excludedIds = Entry::find()
        ->section('stories')
        ->limit(4)
        ->ids();

    // Form value for id in criteria to exclude the above entries.
    $criteriaId = count($excludedIds) ? ('and, not ' . implode(', not ', $excludedIds)) : null;

    return [
        'elementType' => Entry::class,
        'criteria' => [
            'section' => 'stories',
            'id' => $criteriaId,
        ],
        'pretty' => true,
        'elementsPerPage' => 6,
        'transformer' => function(Entry $entry) {
            return [
                'title' => $entry->title,
                'url' => $entry->url,
                'id' => $entry->id,
            ];
        },
    ];
},

I found that you can still paginate results with a value for id set in criteria. So, I've excluded the IDs of the offset entries and paginated the results with those entries removed.

I've also made an issue on the Element API Github repo to see if there's a better solution:
https://github.com/craftcms/element-api/issues/99


Server respond is very long (25s). Is there a way to optimize the perfomance? Normally I'm not working that much with craft and twig syntax, so first of all sorry if I'm not that much into that topic (normaly html,css,js,jquery).
There is one site which need so much time to load. There is much data to load but that can't be the reason that the server needs about 25s to respond.
Do you have any performance tips?

Live Example: http://www.christlicher-gesundheitskongress.de/program.html

Probably this snippet is responsible for the long load time.

{% set z = 0 %}
            {% for program_block in craft.categories.group('program_block') %}

{{ program_block.title }}

{{ program_block.program_date | date ('l, d. F') }} von {{ program_block.time_start | date('H:i')  }} - {{ program_block.time_end | date('H:i')  }}

                    {# {% cache %} #}
                    {% for program in craft.entries.section('program_point').order('program_number') %}
                        {% if program.block.first().title == program_block.title %}

                                {% if program.type == "seminar" %}{% endif %}

 {# {% if program.type != "seminar" %}#{% endif %} #} {{ program.program_number }}{{ program.title }}

                                {% if program.short_description|length %}

{{ program.short_description }}

                                {% endif %}
                                {% if program.type == "seminar" %}{% endif %}
                                {% if program.program_referents|length %}

                                    {% set i = 1 %}
                                    {% for referent in program.program_referents %}
                                         {{ referent.referent_name }} {% if i != program.program_referents|length %}// {% endif %}
                                    {% set i = i+1 %}
                                    {% endfor %}

                                {% endif %}

                        {% endif %}
                    {% endfor %}
                    {# {% endcache %} #}

            {% set z = z+1 %}
            {% endfor %}

Let me try to turn this into a more valuable StackExchange for others who will find it, since the original poster apparently got help and solved his problem without participating here. We can see so on the linked site, which is now running as expected for Craft, quite rapidly.

What I'd like to do is suggest a less awkward way to approach his task. We can do that much more nicely due to Craft's actual abilities, and then there will be no need for index counters, full table scans of data, and so forth as in the original code. The site will then also be more efficient, offering something to the original question, while also having some convenient extra features.

The data involved is naturally hierarchic - Seminar 'plenums' with sessions within them. The sessions are run by speakers, whose information pages are linked. Speakers might be in more than one session, and it's possible that sessions would also be repeated in other plenums, so both should be independent data sets. By using Matrix variables, we can make it very easy to organize plenums, and provide per-plenum information such as times.

Thus in Craft terms, we should have a Channel of plenums, a Channel of sessions, and a Channel of speakers. To give the ordering flexibility, own times etc., and ease of relations, the plenums channel should have a Matrix variable for sessions scheduling. The sessions channel can even more easily use an Entries variable to hold details of the speakers for each session. Otherwise, the channels will contain simple Craft types for each of their data fields.

Such a design will replace the originally posted code with something like the following, which is much more simple and direct:

{% foreach plenum in plenums %}
  ...print plenum fields information
  {% foreach session in plenum.sessions %}
    ...print general session fields information
    ...print plenum-session-specific information, such as location and times
    {{ speaker.speaker_name }}
     {% if not loop.last %}// {% endif %}
  {% endfor %}
{% endfor %}

Note that highly efficient database queries will eliminate table scans and index variables, with the Craft-Twig loop variable supporting clean formatting of single or multiple speakers.

The Craft control panel will let us just pull around sessions within plenums to reorder them, while entering  any changed times or places very easily. Simple variations on this design would allow us as easily to change speakers when necessary for instances of a session, for example.

Since the sessions are only related to plenums, they can appear in multiple plenums just by adding them to the Matrix variable for each. Not much extra work would allow the Matrix variables to hold or over-ride date-times, locations, or even alternate speakers, etc., adding flexibility.

The original poster's site presentation design was appreciated as quite nice for its type, and this picture should show that Craft makes an appropriate data and code design for it just as nice and clean.


Maps plugin not rendering I'm currently trying to implement a Google maps via Maps  plugin: https://github.com/ethercreative/simplemap
I'm following documentation entirely, using PRO version of the plugin. In the plugin settings, I've chosen Google maps style map and Geo service is Google Maps with their own API key. Everything is displaying correctly in the settings menu.
Next, I have created a field, using map field type provided from Maps plugin - and there, everything seems alright; map is showing with default location displayed.
I have section Hompage (where the client wants a Google map to be) and assigned that field to that section.
The last step should be displaying that map on the site. In the plugin documentation, it says that I need to simply call that field via twig.
I was using {{ craft.entries().section("home").one().mapGoogle }} (mapGoogle is a field handle)but it is showing me an error Object of class ether\simplemap\models\Map could not be converted to string
I'm keep getting different errors, but it all came to that last one which I cannot solve, and I don't know what am I doing wrong. Did I miss something in the installation or documentation? Or, simply I don't know how to use twig tags :)
EDIT: When I'm using embed part from the plugin documentation nothing happens.
Dumping this variable I'm getting this info:
object(ether\simplemap\models\Map)[724]
  public 'id' => null
  public 'ownerId' => int 239
  public 'ownerSiteId' => int 1
  public 'fieldId' => string '37' (length=2)
  public 'zoom' => int 13
  public 'distance' => null
  public 'lat' => null
  public 'lng' => null
  public 'address' => string '' (length=0)
  public 'parts' =>
    object(ether\simplemap\models\Parts)[871]
      public 'number' => string '' (length=0)
      public 'address' => string '' (length=0)
      public 'city' => string '' (length=0)
      public 'postcode' => string '' (length=0)
      public 'county' => string '' (length=0)
      public 'state' => string '' (length=0)
      public 'country' => string '' (length=0)
      public 'planet' => string 'Earth' (length=5)
      public 'system' => string 'the Solar System' (length=16)
      public 'arm' => string 'Orion Arm' (length=9)
      public 'galaxy' => string 'Milky Way' (length=9)
      public 'group' => string 'the Local Group' (length=15)
      public 'cluster' => string 'Virgo Cluster' (length=13)
      public 'supercluster' => string 'Laniakea Supercluster' (length=21)
  public 'what3words' => null
  private '_errors' (yii\base\Model) => null
  private '_validators' (yii\base\Model) => null
  private '_scenario' (yii\base\Model) => string 'default' (length=7)
  private '_events' (yii\base\Component) =>
    array (size=0)
      empty
  private '_eventWildcards' (yii\base\Component) =>
    array (size=0)
      empty
  private '_behaviors' (yii\base\Component) => null

The field is returning a Map object, you then need to call what you want from there. The relevant documentation parts are:
Embedded map - https://docs.ethercreative.co.uk/maps/rendering/embed/#from-a-map-field
Static map - https://docs.ethercreative.co.uk/maps/rendering/static/#from-a-map-field
Based on your code and dump:
{# Fetch the Map object #}
{% set myMapField = craft.entries()
    .section("home")
    .one()
    .mapGoogle %}

{# The below should render Milky Way based on your dump #}
{{ myMapField.galaxy }}


Fetch all entries with dateUpdated after a given time I'm trying to grab all Entries that were updated after a given date and time.

It seems like I can grab all entries updated after a date using the following:

{% set timeLimit = now | date_modify("-2 min") %}
{% set updated = craft.entries({ dateUpdated: '>=' ~ timeLimit }).section('myChannel') %}

The problem is, that code doesn't actually seem to take time into account at all, just date. timeLimit in the above will always return something like 2014-08-21.

You concatenate the strings '>=' and what is output from the timeLimit var (the same as {{ timeLimit }} does). So it gets concatenated to '>=2014-08-22'.

You need to change two things. First add a space after the comparison operator and second, change the date output to include the time by using the date filter. I suggest to use  'c' as the value for the formatting parameter, as it returns the datetime ISO 8601 formatted.

{% set timeLimit = now|date_modify('-2 min') %}

{# Get entries with `dateUpdated('>= 2014-08-22T01:55:00+02:00')` #}
{% set updated = craft.entries.dateUpdated('>= ' ~ timeLimit|date('c')) %}


Setting Shipping Country and Method for the Cart screen I want to set the default countryId and a default shipping method (there is only one shipping method but contain several rules), so that the cart screen shows this and we can then present a shipping cost. I then want to build a facility for the customer to change their country on the cart screen so that they can update their country to see the new shipping costs.

Any ideas how to approach this?

If I pass in the following;

<input type="hidden" name="shippingAddress[countryId]" value="77">
<input type="hidden" name="shippingMethod" value="standard">

on the product/add to cart screen then the flash messages shows;

  ERROR! CART NOT COMPLETELY UPDATED.

And whilst it sets the shippingAddress[countryId] on that product page (I checked for it) when I navigate to the cart screen the shippingAddress[countryId] is then not set at all.

I agree with the basic underlying premise that showing full cost - i.e. with shipping - is essential on the cart index page for a modern checkout.  Customers loathe what they see as a hidden surprise if they hit shipping costs later in the process and your completion rate plummets in my experience.

Luke's answer is right about the root cause of the issue - Commerce currently requires a customer with a valid 'address' before you can set shipping on the cart, and a 'valid address' is (out of the box) at least a firstName and a lastName .  You can definitely throw a countryId in a hidden field as a default, but only in addition to setting the address details.

This has become without doubt my least favourite thing about Commerce as it currently stands, and I have spent WAY more time on solving this problem than anything else in Commerce.  To be fair I really should have discussed this a lot more with Luke (Holder, not Pearce above!) - as I really think this needs to be solved (by Commerce) sooner rather than later for a sophisticated checkout flow to be achievable without too much hassle.  But at a certain point I was too far along to contemplate going back to scratch with it (as I will have to once a proper fix comes I guess!).  So I've hacked away at it and have it all working pretty well.

So what ARE your options (I've tried all three, and none of them are great):

From easiest to hardest:

Create a fake 'address' for your customer (I used Mr. Shipping Quote for this) - i.e. set firstName Shipping, lastName Quote, and have a select for your countries I guess -> so that you can then use standard methods to set shipping on the cart, and then on your shipping page basically update this with the actual details when you get them
(as per Luke above) - Write an adjuster to add this shipping until such a point as you do get an address, then once you do make the adjuster adjsut to $0...as you'll then have shipping set on the cart appropriately 'for real'
(And this is what we do on imagescience.com.au for a variety of reasons) - write a shipping plugin of your own and ALSO give this an option of being called by controller separate to all the regular shipping stuff in Commerce- so that you can then do a bit of fancy JS on the front end to add the result of the controller to the cart totals on your cart page.  That is, fake it totally until late in your checkout when you really do have an address.

Of these, I think option 1 is about as far as you want to hack in most situations.  Depending on your checkout flow, and how fancy you want to get with address handling/customer address libraries, this can potentially be a relatively simple fix for the issue.  It works for basic scenarios, you basically just need, on your address input form, to test for the Shipping Quote values for those fields and if those are set, display blank fields for user input.

Option 2 was where I started and could also work, but it gets fairly fiddly fast and it seems a shame to just abandon all of Commerce's shipping stuff - seemed more future proof to not do it this way, to me.  But in retrospect I'm not sure about that, this may have proved easier than option 3 in the end...

Option 3 is actually diabolical to get right.  But, it can solve pretty much all the problems.  However, there are MANY complications with it because if you allow a flow back and forth between your cart/shipping/payment pages, there are a bunch of scenarios that arise to do with the possibility that the cart MAY or MAY NOT not have shipping on it, and/or DOES/DOES NOT have coupons, discounts applied - to make it work consistently you have to do stuff like manually summing the lineItem totals etc.  It works, but it's a right mess if you want a really flexible checkout flow - and it's a lot more complicated than it initially looks.  Add to that address handling such as deleting addresses, adding new ones for billing/shipping etc along the way, and you'll end up writing well more JS than you like (well, I have).  Whilst I have it working, I honestly can't recommend it as an approach.

In retrospect, I wish I had persisted with option 1 as an approach for longer, although it soon gets complicated by the same issues as option 3 really, if you allow back and forth flow between your cart pages (and how can you not?).

(Oh, and shipping currently doesn't deal with tax, which further complicated getting your totals/tax right with these approaches)

To really solve this area I'd love to see:

(The biggie!) Set shipping on the cart without requiring irrelevant
data at that point like names - shipping should only need what it
actually needs for each shipping option (including custom plugins).
Some basic address controllers - that are separate from the cart - add/remove (and addresses should be elements with fields etc!)
Tax support with shipping

I wish I could give you a 'this is how you do it' answer but it's really pretty complicated and far from ideal at this point.  If you CAN live without shipping being shown in the cart (and just use e.g. a chart as Luke suggests) - your life will be vastly simpler, that's for sure!


How can I add attributes to craft field? In my template I have this,

{{
    craft.fields
    .getFieldByHandle('ethnicOrigin')
    .getFieldType()
    .getInputHtml( 'fields[ethnicOrigin]', '') | raw
}}

which returns me a <select> html element Is it possible to add specific class to this select and or data- attribute?

If you take a look at the FieldType documentation it looks as the you need to create a template for the input field in which you can specify the actual html for your field. The example in the documentation is for a textarea but in your case you can substitute this for a select.

Then once you've done that just override the getInputHtml method to render your new template.


Forgotpassword: wrong url in reset password e-mail I have a forgotpassword form on my front-end. This works and generates reset password e-mails. But the reset url leads not to the front-end, but to the back-end.

What I've already tried to fix the problem:

setpassword template on the front-end (setpassword.html)
route setpassword to setpassword.html

What am I doing wrong?

Thanks for your help!

I've found the problem: The test account had control panel privileges and was therefore redirected to the control panel set password page. This behaviour makes absolutely sense.


Alternative title, multi lingual single if I have a single that is available in English & Spanish, how do you edit the title for the non default language?

Single titles are not translatable in the control panel since Craft version 2, they need to be set in your translations file.


Fetch matrix field attributes in controller In my controller I fetched the Entry

$criteria = craft()->elements->getCriteria('Entry');
$criteria->section = 'offlineEvents';
$event = $criteria->first();

The Entry has a matrix field with handler called eventDetails

How can I access this matrix field values? I want to do it like I'd do it in template:

{% for items in entry.eventDetails %}
    {% if items.getType() == 'heading' %}
     ....

but I cant do

foreach ($event->eventDetails as $details) { ... }

it's giving me empty array.

Your code looks right. I'd double check for spelling mistakes and check if there's a entry model returned from your $event = $criteria->first(); by outputting its ID with return $event->id.


How to get a URL segment I can see that craft.request.firstSegment and craft.request.lastSegment works but craft.request.secondSegment does not. Is there a way (as in ExpressionEngine) to get the value of any URL segment?

Craft 2: craft.request.getSegment(2), see Craft docs

Craft 3: craft.app.request.getSegment(2)


Random Fatal errors header already sent So i am developing a site on Mamp Pro locally.
All good.
Its version controlled with github.
When deployed to staging server it was fine initially.
Last Friday deployed again and now i keep getting fatal errors:

    Stack trace:
#0 /home/mywebpath/vendor/yiisoft/yii2/web/Response.php(339): yii\web\Response->sendHeaders()
#1 /home/mywebpath/vendor/yiisoft/yii2/web/ErrorHandler.php(135): yii\web\Response->send()
#2 /home/mywebpath/vendor/craftcms/cms/src/web/ErrorHandler.php(156): yii\web\ErrorHandler->renderException(Object(yii\base\ErrorException))
#3 /home/mywebpath/vendor/yiisoft/yii2/base/ErrorHandler.php(262): craft\web\ErrorHandler->renderException(Object(yii\base\ErrorException))
#4 [internal function]: yii\base\ErrorHandler->handleFatalError()
#5 {main}
  thrown in /home/mywebpath/vendor/yiisoft/yii2/web/Response.php on line 366
[02-Jul-2018 20:59:04 UTC] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes) in /home/mywebpath/vendor/composer/composer/src/Composer/DependencyResolver/RuleWatchGraph.php on line 52
[02-Jul-2018 20:59:04 UTC] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /home/mywebpath/vendor/craftcms/cms/src/i18n/Locale.php on line 822
[02-Jul-2018 20:59:04 UTC] PHP Fatal error:  Uncaught yii\web\HeadersAlreadySentException: Headers already sent. in /home/mywebpath/vendor/yiisoft/yii2/web/Response.php:366

I am going to do a vanilla setup on the server just to test but if anyone has any ideas or similar experience do share any resolutions/ideas.
thx

Solved: this turned out to be a WHM/CPanel bug that was loading imagick module twice.
Known fixes were found here: https://forums.cpanel.net/threads/php-warning-module-already-loaded-in-unknown-on-line-0.612735/


Multiple Sections Within a Structure Group? My site has a set of different layout types with their own templates:

Brochure
Listing
Plain Text

And the site has a group of hierarchical pages that each use a different layout:

About (Brochure Layout)

Employees (Listing Layout)
History (Plain Text Layout)

So I've got these 3 pages each with different content and layout requirements that all need to be grouped together under the "About" page. But I can only use one Section type per Structure.

I don't want to use Entry Types, because I need to use the layouts in multiple places on the site. For example there's a Brochure-type page in the site's 'Careers' area. If something about the layout changes I don't want to have to go through multiple Entry Types to make the changes over and over.

First, try to separate the concepts of content modeling (how things are set up in the CP) and information architecture (how things are going to be presented to the user on the front-end site) in your mind. Are these “Employees” and “History” pages inherently sub-content of “About”, or is that just how you’re choosing to display it on the front-end? My hunch is it’s the latter, in which case a Structure section is not the right choice to begin with - and perhaps 3 separate Single sections makes more sense.

  I don't want to use Entry Types, because I need to use the layouts in multiple places on the site. For example there's a Brochure-type page in the site's 'Careers' area. If something about the layout changes I don't want to have to go through multiple Entry Types to make the changes over and over.

I feel you on the annoyance for having to maintain multiple (near-)identical field layouts, and this is something we hope to make better someday, but I would argue that it’s not going to be as bad in practice to keep these field groups in sync as you’re fearing – or at least not bad enough that the currently-possible workarounds will be worth the effort.


How do I maintain the on value with a lightswitch-field as a plugin setting? I'm trying to use a lightswitch-field in a plugin-config. But for some reason it won't get saved. The lightswitch-field is always off.

protected function defineSettings()
{
    return array(
        ...
        'mailerShowInfo' => array(AttributeType::Bool, 'default' => false),
        ...
    );
}

_settings.html:

{{  forms.lightswitchField({
        label: "Show info in sidebar?"|t,
        id: 'mailerShowInfo',
        name: 'mailerShowInfo',
        value: settings.mailerShowInfo,
        required: true
    })
}}

I tried to use AttributeType::Number instead, and even used settype() in prepSettings() to force a boolean conversion, but still nothing.

Any idea what I'm missing?

I believe you need to set the on value of your lightswitch field.

on: (settings.mailerShowInfo is defined) ? settings.mailerShowInfo : ""

So, the full settings of your twig code would be:

{{  forms.lightswitchField({
        label: "Show info in sidebar?"|t,
        id: 'mailerShowInfo',
        name: 'mailerShowInfo',
        value: settings.mailerShowInfo,
        required: true
        on: (settings.mailerShowInfo is defined) ? settings.mailerShowInfo : ""
    })
}}


Craft Commerce: Filter completed orders based on a product category field I know how to filter entries or products based on a category field with .relatedTo, but how can I filter completed orders based on a product category field?
In this specific case, I have an overview of completed orders. I would like to select a category and show only those orders that contain products with the selected category.
Any hints in the right direction are greatly appreciated!

Managed to solve this.
First, I've added a categories field to the order form.
On the checkout page I loop over all lineItems in the cart and collect the category id's like this.
{% set categoryIds = [] %}

{% for item in cart.lineItems %}
  {% set categories = item.purchasable.product.productGroup.leaves().all()%}
  {% for category in categories %}
    {% if category.id not in categoryIds %}
      {% set categoryIds = categoryIds|merge([category.id]) %}
    {% endif %}
  {% endfor %}
{% endfor %}

Then, I submit the categories using a hidden input.
{% for catId in categoryIds %}
  <input type="hidden" name="fields[productGroup][]" value="{{ catId }}">
{% endfor %}

Now I have categories assigned to an order and can easily filter them using .relatedTo(), like this:
{% set ordersQuery = craft.orders().relatedTo(...) %}


Can I use Rich Text field on frontend safely? I have done a commenting system based on entries. It works fine but I would like to allow to registered users to do basic formatting on their comments. What type of checking is done on the Rich Text Field? Is it protected by SQL Injection or XSS?

All data that goes to the database is properly parameterized by Craft / Yii and ultimately PDO to prevent against SQL Injection attacks.

For obvious reasons, the Rich Text Field is the only field type that Craft will return the "raw" content for, meaning exactly what was saved in the database.  All other field types will get automatically encoded by Twig on output.

If you're accepting guest input from a Rich Text Field on the front-end, you'll probably want to have it post to a plugin and run it through CHtmlPurifier to remove any malicious looking bits before getting saved to the database.

$purifier = new \CHtmlPurifier();
$purifier->purify($html)


Struggling to get Entry index number so I can output "X of Total Entries" I have a channel which has multiple entries in, I'm trying to output the current entry number, for example "1 out of 20 posts"

Currently this is what I am working with:

{% set total = craft.entries().section('spotlights').count() %}
{% set params = {section: 'spotlights', order: 'postDate desc', limit: null} %}
{% set entry_prev = entry.getPrev(params) %}
{% set entry_next = entry.getNext(params) %}

      {% if entry_prev %}

        Previous

      {% else %}

        Previous

      {% endif %}

ENTRY NUMBER HERE of {{ total }}

      {% if entry_next %}

        Next

      {% else %}

        Next

      {% endif %}

I have my next and previous links working as well as the total entries in the section, I just can't work out how to get the current entry in the section. Any help would be appreciated.

I have tried a few different solutions on here but they all seem to be outdated.

Thanks.

I don't know if this is the best solution or if it will even work, but this would be my first try.

{% for spotlight in craft.entries().section('spotlights').all() %}
  {# Check to see if the title of the "spotlight" matches the current entry's title #}
  {% if spotlight.title == entry.title %}

{{ loop.index }} of {{ total }}

  {% endif %}
{% endfor %}


Get globals from other sites How would I get the global variables associated to other sites?

For example, in an instance where I have three websites under the group languages for the English, Spanish, and French versions. I have specified companyInformation globals that contain: phone, address, and logo.

From a template I would like:
- Loop through all sites under the group languages
- Get companyInformation global values for each of the sites

Thanks in advance

The function is

public function getSetByHandle(string $globalSetHandle, int $siteId = null)

Thus you can grab all the siteIds you want and loop through them

{% for siteId in sites %}
    {% set setForSiteId = craft.app.getGlobals().getSetByHandle('handle', siteId) %}
{% endfor %}


Is it possible to use `guestEntries.beforeSave` to validate custom fields? Say, I want to validate an email address or a special URL e.g. YouTube URL, would I be able to use a custom plugin with the guestEntries.beforeSave hook to do this?

The basic hook call:

craft()->on( 'guestEntries.beforeSave', function(Event $event) {
    // Call custom validation here
});

Would I be able to setup a model with the method defineAttributes and run that?

The only model passed into the guestEntries.beforeSave event and returns from it is Craft's EntryModel, so even if you create your own MyEntryModel inside the event with it's own attributes and validation rules, there's not a clean way to return that model back to the controller to return to the template to display.

The easiest thing to do would be to fork the plugin and change GuestEntriesController so that's its using a custom MyEntryModel that extends Craft's EntryModel and adds it's additional attributes/validation rules as required.


How do I access a custom Asset field within an Assets field? I want to setup a custom field to assign a URL for an image.  On the front end I want to output the image and then wrap it in a URL. I've setup a field called 'logoURL' but not sure how to access that data.  What I have isn't working and I've tried several variations.

{% set logo = { width: 175, height: 120, mode: 'fit' } %}

{% for image in entry.logos %}

{% endfor %}

You’re close. When you do this:

{% for image in entry.logos %}
    ...
{% endfor %}

The code within the for-loop will be repeated for each asset that was assigned to your entry.logos field. Each time, the image variable will be assigned the current asset.

So, if you want to access a field that is on the asset (e.g. your logoUrl field), you would access that via image.logoUrl rather than logos.logoUrl. (You don’t actually have a variable named logos, so if you have Dev Mode enabled, that template would be giving you an error about that.)

{% set logo = { width: 175, height: 120, mode: 'fit' } %}

{% for image in entry.logos %}

{% endfor %}


Using Label instead of Value from Dropdown I currently have this dropdown on a front-end form, the problem I'm having is that it's saving the value where I really want to use the label in my templates such as United Kingdom instead of unitedKingdom. Is this something I should be changing in my Dashboard/Front-end/Dropdown?

          Location
          <select class="form-control" id="listingType" name="fields[location][]" required>
            {% set locationField = craft.fields.getFieldbyHandle('location') %}
            {% for option in locationField.settings.options %}
            <option value="{{ option.value }}">
              {{ option.label }}
            </option>
            {% endfor %}
          </select>

Front End

{{ entry.author.location|ucwords }}

The value of dropdown fields are set in the dashboard. How to set the value is dependent on the form plugin or craft field type you are using.


Creating Task cause Site/CP to hang I have created a simple task to automatically create some entries if they don't exist and this all works fine.

The only issue I am having is that if the task is still running and I try and load a page on the site or another control panel page it simply hangs until the task is complete.

I can't seem to get the task to run in a similar way to the task that runs when updating a section's Entry URL Format (the task spinner and progress bar in the top right).

I have tried using the PowerNap example and changing the total steps to 100 (to give me enough time to see what is happening) and the same thing happens.

Do I have to create it as a specific background task?

Edit: To show sample code

<?php
namespace Craft;

/**
 * Inception task
 */
class Bounce_TotalUpdateTask extends BaseTask
{
    /**
     * Defines the settings.
     *
     * @access protected
     * @return array
     */
    protected function defineSettings()
    {
        return array();
    }

    /**
     * Returns the default description for this task.
     *
     * @return string
     */
    public function getDescription()
    {
        return 'Update Total Record';
    }

    /**
     * Gets the total number of steps for this task.
     *
     * @return int
     */
    public function getTotalSteps()
    {
        return craft()->elements->getCriteria(ElementType::User, array(
            "group" => "members",
            "limit" => null,
        ))->total();
    }

    /**
     * Runs a task step.
     *
     * @param int $step
     * @return bool
     */
    public function runStep($step)
    {
        $users = craft()->elements->getCriteria(ElementType::User, array(
            "group" => "members",
            "limit" => null,
        ))->find();

        $section = craft()->sections->getSectionByHandle("totals");

        if (isset($users[$step])) {
            $user = $users[$step];

            $record = craft()->elements->getCriteria(ElementType::Entry, array(
                    "section" => "totals",
                    "authorId" => $user->id,
                )
            )->first();

            if (!$record) {
                $entry = new EntryModel();
                $entry->sectionId  = $section->id;
                $entry->typeId     = $section->id;
                $entry->authorId   = $user->id;
                $entry->enabled    = true;

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

            return true;
        } else {
            return "Result " . ($step) . " does not exist";
        }
    }
}

I recently dealt with a similar issue. Keep your tasks small and quick by using subtasks or by creating multiple instances of your main task. In your example, make each entry creation its own tasks or subtask, so that each step only has one process to take care of. You can have a private array on the task object to store an array of entry data to iterate through.

private $entry_data = array();
//Fill array with the data you want to use

public function runStep($step)
{

    $new_entry = $entry_data[$step];
    //code to create new entry

    return true;

}


Accessing global field values in site loop I have a drop down select that I am populating with sites.
I've added a lightswitch field (handle 'showInDropDown') to the global set 'siteInfo' with the hope of getting the lightswitch value per site in the loop below.
{% for siteId in currentSite.group.sites %}
    {% set setForSiteId = craft.app.getGlobals().getSetByHandle('siteInfo', SiteId =5) %}
    {{ setForSiteId.showInDropdown }}
{% endfor %}

The lightswitch field is set to translate per site.
setForSiteId is always coming back null:
Impossible to access an attribute ("showInDropdown") on a null variable.

I am not sure if I am missing something. Is there another way to access global variables/values per site?

Finally solved with the following code:
    <select  class="form-control " name="sites" id="company-select">
    <option value="" class="small text-muted">Please select your company</option>
    {% for site in allSites %}
        {% set siteId = site.id %}
        {% set groupId = site.getGroup().id %}
        {% if groupId == '5' %}
            {% set setForSiteId = craft.app.getGlobals().getSetByHandle('siteInfo', siteId) %}
            {% if setForSiteId.showInDropdown == '1' %}
            <option value="{{baseUrl ~ '/' ~ site.handle }}">{{site.name}}</option>
                {% endif %}
        {% endif %}
    {% endfor %}
    </select>


Fetching Customer firstName lastName I want to output a list of all orders of all customers, including customers first name and last name. But can't get these two elements.

What I have now is:

{% set pastOrders = craft.commerce.orders.find() %}
{% for order in pastOrders %}
    {% for item in order.lineItems %}

      {{ order.id }}
      {{ craft.commerce.customer.firstName }}
      {{ craft.commerce.customer.lastName }}
      {{ item.description }}
      {{ order.email }}
      {{ item.options.evenement }}
      {{ item.note }}
      {{order.orderStatus.name}}
      {{ order.datePaid.localeDate() }}

    {% endfor %}
  {% endfor %}

But as you may guess, I've got an error in both fields firstName lastName

Not all orders will necessarily have a craft user associated with the order.

But all orders must have a billing address, so:

order.billingAddress.firstName and order.billingAddress.lastName will get you what you need.


Not able to contact Solspace support Does anyone have an idea how to contact Solspace support? The email seems to be dead for about a week now.

Sorry about the trouble. We typically answer most tickets within 24 hours:

Freeform: https://solspace.com/craft/freeform/support/
Calendar: https://solspace.com/craft/calendar/support/

If the Support Form isn't working for you, you can always email us directly at support@solspace.com :)


Display entries if Field Date is past Now I'm trying to make a upcoming and past event page. I can make an if statement for displaying the past or upcoming events but the problem is if I limit the amount of entries to be displayed I realised it is still going through the other entries (obviously) so it wont display anything. So I was looking for any filter of some kind to only display entries based on field value greater then other value.

Something in the range of:

{% for entry in craft.entries.section('event').limit(2).search('eventDate < now') %}
<!-- Past events go here -->
{% endfor %}

You can combine a for and if statement in twig, so you don't necessarily need to use search.

Something like this might get you a bit closer:

{% for entry in craft.entries.section('event') if entry.postDate < now %}
{# output your event here #}
{% endfor %}

It'll just output what meets the criteria if your if statement. The bonus is it doesn't mess with your loop variables either, so things like loop.index work out as you'd expect (or at least they have for me so far).

EDIT: Here's an alternate way of doing it, which neatens up the query and adds ordering...

{% set events = craft.entries({
    section:  'event',
    startDate:  '< ' ~ now,
    order: 'postDate desc'
}).find() %}

{% for event in events %}
...
{% endfor %}


Getting unique values from Smart Map addresses Using the following filter will return all the entries for 'CA'.

{% set params = {
    filter: {
        state: 'CA'
    }
} %}

{% set meetings = craft.entries.address(params) %}

Let's say that I get 10 returns for 'CA':

Los Angeles
Los Angeles
Los Angeles
San Diego
San Diego
San Diego
San Diego
San Diego
San Diego
Modesto

How would I go about one getting 3 results returned for each unique value for City? I only want to list each city once rather than 3 Los Angeles, 16 San Diego's and 1 Modesto. Like this:

Los Angeles
San Diego
Modesto

It could also be helpful to have them like this:

3 Los Angeles
6 San Diego
1 Modesto

I am building a navigation by state with a list of cities under each state. To look like this:

CA
   Los Angeles
   San Diego
   Modesto
KS
   Wichita
   Kansas City

You'd actually need to retrieve all of the entries to count them in this way. Once you've got the complete collection, it would simply be a matter of looping through them and adding everything up.
1. Get all entries in California.
{% set params = {
    filter: {
        state: 'CA'
    }
} %}

{% set meetings = craft.entries.address(params).all() %}

2. Loop through those entries. Build a new array based on the results.
{% set stateMeetings = {} %}

{% for meeting in meetings %}

    {% set city = meeting.address.city %}

    {% if stateMeetings[city] is defined %}
        {% set cityTotal = stateMeetings[city] + 1 %}
    {% else %}
        {% set cityTotal = 1 %}
    {% endif %}

    {% set stateMeetings = stateMeetings|merge{(city):cityTotal} %}

{% endfor %}

3. By the time that's all done, you'll have a neat associative array, where each key is a different city, and the value of each represents how many times it was found.
{
    "Los Angeles": 3,
    "San Diego": 6,
    "Modesto": 1,
}

You'll definitely want to cache all of that... it'll be a pretty expensive process.
To be honest, I'd actually recommend doing all of this work in a plugin (or module). It's a fairly easy thing to achieve with a template hook. This kind of heavy merging is ill-fitted for Twig, but much simpler in PHP.


Upload Image Custom Field Is there any "Upload Image" custom field in craft so that I will be able to make my images editable. Thanks for the answer.

I believe you are looking for the Assets Field


Craft 3 dashboard error Having successfully installed Craft 3 via composer – all seems fine but on each load of the admin dashboard I get a notice on red bg:
'An unknown error occurred' need I worry about this? couldn't see anything obvious in the logs – all else seems fine – using php 7.0.0 / Mamp Pro and used the line below when setting up:

PATH="/Applications/MAMP/bin/php/php7.0.0/bin:$PATH"

Or is a fresh install required?

If you're running MAMP (and even if you're not, but it's common you are) and you're getting this error when connecting to Craft's web services:

  cURL error 35: Unknown SSL protocol error in connection to craftcms.com:443

Then you're running an older version of MAMP and you need to upgrade to the latest.

The older versions are trying to connect over https using an older SSL protocol (TLS 1.0) that is no longer supported.


How do I select entries that have a particular block type? Say I have a channel with a matrix field which has two block types - text and video. Is there any way to select all entries in that channel that have a video block?

Idea 1

Use the Low Blocks plugin to query all video block types, then get their owner IDs, then use those to grab the entries.

Idea 2

Add a light switch field to the channel called "hasVideo". Update this field programmatically using the onSave event and a plugin. Then I can select all entries if hasVideo is true.

Is there a better way I'm missing?

You can write a custom plugin and make use of Craft's buildElementsQuery, to be able to add custom query statements to your ElementCriteriaModel.

Give your plugin a Template Variable addVideoBlockCriteria and with some lines of plugin code you will be able to do something like this from your template:

{% set criteria = craft.entries.section('blog').order('title asc') %}
{% set entries = craft.businesslogic.addVideoBlockCriteria(criteria) %}

{% for entry in entries %}
    ...
{% endfor %}

Make your Template Variable pass the criteria to a plugin service, run them through buildElementsQuery, and you can now modify the query with some DbCommands.

public function addVideoBlockCriteria(ElementCriteriaModel $criteria)
{
    $query = craft()->elements->buildElementsQuery($criteria);

    $query->andWhere('matrixcontent_article.field_video_videoUrl is not null');

    $query->join('matrixblocks matrixblocks', 'matrixblocks.ownerId = entries.id');
    $query->join('matrixcontent_article matrixcontent_article', 'matrixcontent_article.elementId = matrixblocks.id');

    $results = $query->queryAll();

    return EntryModel::populateModels($results);
}

Use join to bring in the "matrixblocks" table and also your Matrix field's content table "matrixcontent_mymatrixfield". And add the conditions with andWhere;  the Matrix field's fields column names follow a scheme "field_myMatrixBlockType_myFieldInMatrixBlock".


Trigger off canvas menu upon adding product to cart When a user clicks 'Add to Cart', rather than redirecting them to the cart page, I would like to trigger an off-canvas menu that contains the contents of their shopping cart.

I'm using http://git.blivesta.com/drawer for the menu implementation

The menu can be toggled/opened with javascript, but I'm unsure as to how to trigger the opening of the menu when a user clicks 'Add to Cart'.

Ideally, I would like this to be possible, without reloading the page.

Has anyone implemented anything similar?

The basic recipe is this, and you can definitely do it without a page reload:

In your JS, listen for the form's submit event
Prevent the default submit
Ajax submit the form's data to the Commerce addToCart controller
Grab the result of this call...
If error - show error
If cart - JS pass the cart's data off to your drawer system and print out the cart that way (that's the quickest way, or you can just ajax load in a Craft template to where you have your drawer, the cart variable in that will have the updated cart data)


Multiple Stocks levels I was wondering if Craft supported having multiple stocks for one product.
Something that Magento for example would call stock source.
I'm looking at having one set of stock associated to trade customers, and one for regular customers.
I could potentially look at them being different products and have different stock for it, but that would mean all the products would be duplicated, which is not great.
Thanks for your time

Commerce doesn't support multiple stock sources out of the box but you might be able to work around it, depending on your requirements.
To expand on your thoughts...
Variants
How are your products set up? Commerce doesn't add a product directly to a user's cart, it's actually Purchasable—with Variants being one of the purchasables in Commerce.
If your products don't have a lot of variations that you'd use a Variants for, one way to pull off what you're trying to do is just filter variants based on the user that's logged in—creating multiple groups could be one way.
That way, you could take advantage of Craft's native stock-handling features directly.
Depending on how many products are in the store, you might also choose to use something like a CSV import/export and not touch the Commerce backend directly.
Multiple stock level fields
Another slightly cleaner way to pull this off would be turn off Commerce's native stock handling (set it to unlimited) and roll your own validation with your own inventory fields.
From a code standpoint, that's a more intensive way to go but it does put you more in charge of how you handle inventory. Commerce provides multiple events to let you handle the stock validation and it might be a good way to go depending on your rules (eg you might want to reallocate inventory from a trade customer to handle a regular customer in some cases or vice versa).


What is the value of hosting assets on Amazon S3? Best practices question Starting a substantial project. There will be 100s of images on the site. Few are part of the general design, but there are slide carousels, photos, etc.

I understand what S3 is, but I'm wondering what the specific value is of using it over local storage. There is plenty of room on the web host's server.

S3 isn't a CDN, right? Or will it replicate files across to other locations?

Should I expect S3 to be faster than local storage? And is my S3 bucket backed up elsewhere, should Amazon's servers crash?

Thanks.

S3 is not itself a CDN, no.  However, you can use Cloudflare as a free and very easy to set up CDN service in front of it.

For me the main advantages of S3 are that it's a centralised repository of assets I can point all my environments to (so dev, staging, production all point to the same S3 and thus have identical assets available to them with no need to sync anything).  I trust Amazon to runs servers more than I do individual other companies, simply by virtue of their size and experience (and I know some of the engineers as well - they're smart people!).

With Cloudflare in front of S3, you get CDN and static asset caching benefits, and images are served very quickly.


Loop through entry titles in a select field Im trying to use entry titles as options in a select field, similar to the last example on this page in the docs

My code looks like this:

<label>Position Applying For</label>
<select name="{{ positionApplyingFor.handle }}">
{% for entry in craft.entries.section('careers') %}
  <option value="{{ entry.title }}">
    {{ entry.title }}
  </option>
{% endfor %}
</select>

Although the form submits, the fields content is not shown in the admin.

If I add {% if field.value == entry.handle %} selected{% endif %}, I get the error "Variable "field" does not exist"

Any help appreciated!

Martin

The issue is you don't have a variable named input. I changed the code a little by included a variable named value. To make sure the proper option is selected you would need to pass back to the template a url param name with the currently selected option value.  If there is a param and it has a value it will add that value to the variable value if not value will be blank.

<label>Position Applying For</label>
{% set value = craft.request.getParam("name")?? "" %}
<select name="{{ positionApplyingFor.handle }}">
    {% for entry in craft.entries.section('careers') %}
        <option value="{{ entry.title }}" {% if value == entry.title %}selected{% endif %}>
            {{ entry.title }}
        </option>
    {% endfor %}
</select>


Stalled / Stuck Task (GeneratePendingTransforms) I’ve got a task running called “GeneratePendingTransforms”, but it seems to stop/get stuck just over 65% of the way through and never completes. Is there anything that could cause this

I’ve got Task Manager plugin (https://github.com/boboldehampsink/taskmanager - Which is amazing) installed to see whats happening and tried restarting the task multiple times but it gets stuck at the same point every time.

It gets stuck on Step 121 of 205.

Any ideas how I could debug this?

There is a support article about stuck tasks.

In your case I would take a look at your logs, especially the phplog.
You find them at: yourdomain.com/cp-path/utils/logs

https://craftcms.com/support/stuck-tasks


Template Hook + Caching A portion of my template is being cached, using the standard cache tag:

{% cache %}

Within that cached section of the template, I'm using a template hook:

{% hook 'myTemplateHook' %}

Within that template hook, I'm collecting entry data via a normal ElementCriteriaModel:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = 'mySection';
$myEntries = $criteria->find();

So I'm wondering... Will my PHP-based query be cached?

I'd like for the caching of this query to mimic any caching that would take place via Twig templates. What do I need to do to ensure this follows normal caching patterns?

EDIT:

Great answers so far, but they don't really touch on the heart of my concern...

Normal Twig-based caches actually record all criteria and elements used in the cached HTML, for the purpose of invalidating that cache when either of those things change. What I really want to know is, would my PHP-based criteria and resulting elements be recorded as well? Since the criteria is built in PHP, and the elements are collected in PHP, I'd like to know if they are being stored (as they normally would via Twig).

This obviously makes a difference for cache invalidation... If an element fetched via PHP is updated or deleted, I'd like to be confident that the cache would be cleared as well.

Yes, sort of. As soon as the rendering template hits a {% cache %} tag, Craft grabs the outputted content from the cache table in the database and ignores all other template logic until the {% endcache %} tag.

So, the results of the PHP-based query will be cached with the rest of whatever is between the tags only for use in that instance as HTML.

If your hook is called anywhere else, Craft will make the PHP query again, as the query itself is not cached independently. In order to accomplish that (if you want), you will need to implement some plugin-level cacheing using the Cache Service.

For cache invalidation, here is what Brandon has to say:

Any time ElementCriteriaModel::find() et al. is called within {% cache %} tags, it will be recorded, however you will have to manually register any elements that should be associated with the cache, using this code:

// Don't initialize the CacheService if we don't have to
$cacheService = craft()->getComponent('templateCache', false);

if ($cacheService)
{
    $cacheService->includeElementInTemplateCaches($elementId);
}


How to upload animated webp files? I've created an asset volume to which I can successfully upload jpgs, pngs, static webps etc. But when I try to upload an animated webp I get the alert:
Upload failed. The error message was: “The file “example.webp” does not appear to be an image.”

The file is an image and I've tried various different animated webps. It seems to just be them specifically. Weirdly, if I copy and paste the same animated webp into a redactor field it displays there and animates and saves fine. Does craft not supported animated webps as a file type or do I maybe need to change something in my config?

ImagickException: data encoding scheme is not supported `/var/www/html/storage/runtime/temp/P0LY_Test60a2e191a91de2.16978634.webp' @ error/webp.c/ReadWEBPImage/338 in /var/www/html/vendor/pixelandtonic/imagine/src/Imagick/Imagine.php:69

Based on that error, it definitely seems like an Imagick bug or Imagick configuration issue.
https://github.com/ImageMagick/ImageMagick/issues/22
https://github.com/gographics/imagick/issues/181
I don't understand most of that (sounds like you might have a webp image that is using some extended features that Imagick doesn't know about), but hopefully it points you in the right direction.


How can I add data from my custom plugin to the Entry Listing table? I've built a custom field that stores its data in its own table. How can I get data from MyCustomField to show up as a column in the Entries Table?

A few details:

My custom field is made up of multiple fields, in this case a Date/Time field and a text field.
My custom field is is returned as an array by the prepValue() function in my fieldtype file.
Not every entry will have a record in my custom field's table

How can I check to see if a value exists in my custom field's table for an entry and display it in the Entries Table?

I'm copying this over from the Craft Slack channel so we don't lose the solution.

It will take two hooks in the MyCustomPlugin.php file to display data from the custom field.

public function modifyEntryTableAttributes(&$attributes, $source)
{
    if ($source == '*')
    {
        unset($attributes['expiryDate']);
        $attributes['whatever-you-want'] = Craft::t('Desired Table Heading');
    }
}

The modifyEntryTableAttributes() hook will allow us to add/remove columns from the Entries Listing table. Note that the $attributes[] variable will automatically try to match up whatever you put in there with an attribute on the entries.

This is probably all you need to drop a standard custom field into the table. However, since my custom field is an array, holding multiple bits of data, we need to use the getEntryTableAttributes() hook to dig in to my custom field and grab the item we want to display.

public function getEntryTableAttributeHtml(EntryModel $entry, $attribute)
{
    if ($attribute == 'whatever-you-want')
    {
        // Assign my custom field to a variable
        $fieldData = $entry->mycustomfield;

        // Check if there is any data in the variable
        if (!empty($fieldData)) {

            // If so, return the specific key AS A STRING to the table
            return $fieldData['myCustomDate']->format('n/j/y');
        }

        return '';
    }
}

Notice the return ''; at the end? That's important, because if an entry DOESN'T have my custom field attached to it, it will try to look up the $attribute['whatever-you-want'] assuming that's a legitimate DB column, and will fail.


Multiple category output in auto generated entry title I have a single category field (named category) in my section and I am looking to use the auto-generated title to keep these entries titled consistently. I have had success outputting the first category selection into the entry field but I can't seem to grab multiple selections. I have tried a for loop and I can of course getting working on a template but I am thinking the syntax must be different for the auto generated title field.

Here is an example of the tree I am working with.

Category One

Sub Cat One
Sub Cat Two

In a example entry I will have Sub Cat One selected and of course Category One since it is an ancestor of the Sub Cat One. I would like to output something like (Category One / Sub Cat One) in the entry title but I either get no results or just the first one. I have tried the following code:

{% for categoryList in category %}{{categoryList.title}}{% if loop.index < loop.length %}/{% endif %}{% endfor %}

and

{category.first().title}

Which of course contains only the first selected category title.

How do you output more than one category selection in an entry title field?

Thanks!

I think you could also use Twig's join filter instead of the for loop:

{{ object.category | join('/') }}


Craft Commerce auto SKU with product ID I can use {product.slug} for generating Auto SKU but does anyone know how to use the Product ID

Using SKU{product.id} the create product form won't submit because no SKU is generated. I think it might be because there's an error there but I can't see anything in the logs.

Using just SKU{id} allows the form to submit but the result is just "SKU", so it isn't fetching the product id.

I've tried ID and id in both cases.

I'm sure this should work. Any ideas?

I'm going to guess that the id for the new product entry doesn't yet exist when the sku is being generated....it's probably created at the end of the save process once Craft is sure the save will complete.

Do you really need to use the ID in the sku?

(I don't immediately see an obvious in built way around it either, but perhaps someone cleverer than me will come along with a good idea).


Multiple editableTable in a CP page Is it already built-in in CraftCMS to make multiple editableTable in a CP page? The behaviour would be similar to Matrix field where you add a matrix block, the matrix block appears. What I want is that instead of the matrix block, editableTable appears. Is it possible?

I think you are looking for a plugin called Table Maker. I've never tried it but it looks like it lets your users create tables in the cp.

Table Maker


How to change/discount total amount before payment I want to change the total amount programatically before the payments are processed.

If the total amount is $1000 I want to give a discount based on a point system in the users profile. You can add the desired amount based on his total points. If he adds 20 he gets a $20 discount and only have to pay $800.

What is the best way to implement this? I have tried with commerce_transactions.onCreateTransaction but no luck.

Have you looked into building an adjuster for this purpose? You can find the documentation here


Creating private items Currently I have a light switch on some commerce items that make them private to users that are not logged in but they still show up when the user is logged out. Is there a way to do this? I'm continuing development for a webpage and I'm still new to craft so I am sort of lost

Assuming yourLightSwitchFieldHandle is labeled "Hide From Logged Out Users", something like this should work.  Completely untested.

{# if the user is logged in OR they are not logged in but we don't want to hide the current product #}

{% if currentUser OR (not currentUser and not yourProduct.yourLightSwitchFieldHandle) %}
        Show Product
{% endif %}


Element criteria with a table field I am trying to build a query to fetch some entries which I want filter on a table field between multiple values. The table field is "filterTestWidth" and has 1 col named "size" (number type). There are multiple entries with multiple values in col1.

{% set items = craft.entries({
    "section": "catalog",
    "filterTestWidth": [
        "and",
        "> 180",
        "< 190"
    ]
}) %}

Unfortunate the Craft manual is limited on this subject. Does anyone know a more detailed article about element criteria?

Table fields in Craft just store their data in a JSON blob in the content table. That means you can't really query them like you would other things, you'd have to load in all of the data, and then filter it out as an array.

While you can certainly do this in Twig, it smacks of a bit too much "business logic" in Twig; so at this point I might be considering one of two options:

Refactoring it out to a
plugin
or
module
that will do the heavy lifting behind the scenes
Changing the schema I'm using to store the data in something that
can be queried, such as moving the data into entries or a matrix block or perhaps a
SuperTable

Or if either of these seems like too much work, or you're not storing a whole lot of data in the Table field, then I'd just do it in Twig by looping through all of the rows in the table, and selecting only the ones that meet my criteria.


Proper way to implement array of objects as Craft field I'm using Craft CMS for the first time and have a question about the proper/best practice to store what would equate to an array of objects (or just a plain array) in Javascript or JSON.

Say I'd like to store a number of entries with the following shape :

{
    newspaper: 'New York Times',
    article: 'How to Write Web Apps Without Help From Stack Overflow',
    authors: [
        {
            name: 'John Doe',
            title: 'Staff Writer'
        },
        {
            name: 'Jane Smith',
            title: 'Editor'
        }
    ],
    categories: [ 'technology', 'web' ]
}

I understand that I store the first two properties as "Plain Text" fields, but I am confused on the proper way to store the authors and categories arrays as Fields. It looks like I would use a Matrix Field for this, but in the demo I'm referencing, they use Matrix fields in a slightly different way.

Just looking for validation that I should use matrix fields in this case, or some direction otherwise.

Probably you would want to use:

Users field as a relation to one or more of the users
Categories field as a relation to one or more of the categories


Limit by entry type in Matrix field After a finally returning to an extensive recode of my first commercial Craft based site, I have successfully implemented several new (to me) features from the latest builds. One thing I am making extensive use of is entry types.

To make it easy to update my layouts, I have a section called "Widgets". Each widget entry simply points to a template file via an assets select. Presently I have three kinds of widgets: a side bar widget (Which is more compact as the name would suggest), a primary (much larger widget), and a full page widget. To keep things organized, I am sorting this based on three entry types.

So each page of my site (ex: site.com/blog) is part of a structure, which inside the structure is a Matrix field. This allows me to insert inline text, images, and the previously mentioned widgets via a related entry, though there is one major problem... I cannot seem to limit the Widget block in the Matrix field based on entry types.

My workaround I suppose would be to have 3 separate sections: one for each type of Widget, but that solution is far from elegant. SO, any ideas, guys? Here's to hoping that this would be a fairly minor update that could make it out in an upcoming release... Thanks in advance guys!

I suggest to refactor again and use dropdown fields for the path to your partials instead of the channel entries with asset fields you currently have. This also keeps your twig files more secure (your asset folder is above the web root!).

Set up three dropdown fields ('pathSideBar', 'pathPrimary' and 'pathFullPage') to do
the grouping and one Matrix Block for each of them.

{% if block.type == 'widgetSideBar' %}

    {% set widgetPath = '_includes/sidebar/' ~ block.pathSideBar ~ '.html' %}
    {% include widgetPath only %}

{% elseif block.type == 'widgetPrimary' %}

    {# ... #}

{% endif %}


Plugin fieldtype settings for ColumnType::LongText being inserted as varchar(255) I’m working on a plugin that includes a custom fieldtype. Using Craft 2.6.2911, the fieldtype’s defineSettings looks like this:

public function defineSettings ( ) {
    return [
        'json_data'  =>  [
            AttributeType::String,
            'column'  =>  ColumnType::LongText
        ]
    ];
}

When I add this field to a tab group, the new column is added to the craft_content table as a varchar(255), not a text-type column.  Is there an additional definition I need somewhere?

I did not see a relevant note in the changelog for builds of Craft between 2.6.2911 and 2.6.2944.

When reviewing my database schema, I'm seeing the following:

| field_jsonSync             | varchar(255)           | YES  |     | NULL    |                |

The Field Types defineSettings method defines the settings that get handled when you create the field. Those field settings get stored as json in the craft_fields.settings column when the field is created.

The BaseFieldType class has another method you can add to your class called defineContentAttribute. In your case, getting the column to be the data type you want would look like:

public function defineContentAttribute()
{
    return [
        AttributeType::String,
        'column'  =>  ColumnType::LongText
    ];
}

The PlainTextFieldType and RichTextFieldType classes have similar use cases which modify the default defineContentAttribute value if you want to take a look at a few more examples.


How to get the name of a product's sale I am still new to craft commerce and I have 5 sales, some are flat and others are percent. When i loop through my products, I am trying to get the type of the sale for the current product. How do i get that?

{% for sale in variant.sales %}
  {{sale.name}}<br>
{% endfor %}


Multilingual Dates where date format varies I have a multilingual site, and I want my dates formatted like:

English site: July 13, 2014
Italian site: 13 lulgio 2014

So the month name is translated, and the order of the elements changes as well.

Craft has extended Twig's date filter so that it translates the output (no documentation for this, but the code is in craft/app/etc/templating/twigextensions/CraftTwigExtension.php around line 152). So {{now|date('F j, Y')}} will give July 13, 2014 on the English site, and luglio 13, 2014 on the Italian site. Which is half way there. In order to get the Italian date to use the Italian format (j F Y), we need a translation for the format string. Add this to craft/translations/it.php. (For more info see Static Translations):

'F j, Y' => 'j F Y',

To output the translated date:

{{now|date('F j, Y'|t)}}


Is it possible to duplicate a entry as a draft entry? I need a function to duplicate an (published) entry as a draft entry. Now if I duplicate an entry it will immediately publics that entry an that fires some plugin events (sends mails) I didn't want.

You can refer to this How can I copy an entry?

Just change:

$newEntry->enabled = $oldEntry->enabled;

to

$newEntry->enabled = false;

To start out building your own plugin: https://pluginfactory.io/, be sure to select the Services lightswitch.


Ran the Craft Updater from Control Panel the current install is no longer working The previous install (3.0.34) was working fine until I ran the updater from the CP. The update failed with error (couldn't access web.config (IIS))

Now loading CP or front end displays the following error:
Warning: require(C:...Craft\Projects\Untye/vendor/craftcms/cms/bootstrap/web.php): failed to open stream: No such file or directory in C:\Users\rickn\Documents\Craft\Projects\Untye\web\index.php on line 20

Fatal error: require(): Failed opening required 'C:...\Craft\Projects\Untye/vendor/craftcms/cms/bootstrap/web.php' (include_path='.;C:\php\pear') in C:...Craft\Projects\Untye\web\index.php on line 20

Is there a way to finish the install manually and keep the previous work and data?

{
"require": {
   "craftcms/cms": "3.0.35",
 "vlucas/phpdotenv": "^2.4.0",
    "craftcms/redactor": "^2.1",
"craftcms/mailgun": "^1.3",
   "craftcms/contact-form": "^2.2",
"indigoviking/liquid-letters": "^1.2",
    "lukeyouell/craft-contactformtemplates": "1.0.1",
"doublesecretagency/craft-bootstrap": "4.1.1.2",
   "fatfish/navigation": "1.0.7",
 "superbig/craft3-templateselect": "^2.0",
   "craftcms/ckeditor": "1.0.0-beta.2",
 "verbb/navigation": "^1.0",
   "enupal/slider": "1.2.2",
 "studioespresso/craft-dumper": "^1.3",
   "topshelfcraft/wordsmith": "^3.0",
 "craftcms/craft": "^1.0"
 },
 "autoload": {
 "psr-4": {
    "modules\": "modules/"
  }
 },
 "config": {
   "optimize-autoloader": true,
 "platform": {
     "php": "7.0"
  }
 },
 "scripts": {
  "post-root-package-install": [
 "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
   ]
 }
}

Try running
    composer update
Via the command line while in ssh

Or download the latest Craft files and replace your files. Mainly vendor/craftcms it looks like.

Edit:

Update composer.json to this and try running composer update again.

{
  "require": {
    "craftcms/cms": "^3.0.35",
    "vlucas/phpdotenv": "^2.4.0",
    "craftcms/redactor": "^2.1",
    "craftcms/mailgun": "^1.3",
    "craftcms/contact-form": "^2.2",
    "indigoviking/liquid-letters": "^1.2",
    "lukeyouell/craft-contactformtemplates": "1.0.1",
    "doublesecretagency/craft-bootstrap": "4.1.1.2",
    "fatfish/navigation": "1.0.7",
    "superbig/craft3-templateselect": "^2.0",
    "craftcms/ckeditor": "1.0.0-beta.2",
    "verbb/navigation": "^1.0",
    "enupal/slider": "1.2.2",
    "studioespresso/craft-dumper": "^1.3",
    "topshelfcraft/wordsmith": "^3.0"
  },
  "autoload": {
    "psr-4": {
      "modules\\": "modules/"
    }
  },
  "config": {
    "optimize-autoloader": true,
    "platform": {
      "php": "7.0"
    }
  },
  "scripts": {
    "post-root-package-install": [
      "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
    ]
  }
}


Require password from current user on front-end form? I have a controller that handles some font-end form validation. From that controller I’d like to require the current (already logged in) user’s password to update any field. How can I do that?

You'd create a Craft plugin with a controller that has an action, say actionSaveForm().

Your controller would look for a password POST variable and if that existed, validate that against the currently logged in user's account to make sure it was a match.  If so, continue onto Craft's saveUser or saveEntry, etc. (whatever you're using the form for, otherwise, return a validation error message.

Roughly like this:

public function actionSaveForm()
{
    $this->requirePostRequest();
    craft()->userSession->requireLogin();

    $password = craft()->request->getRequiredPost('password');
    {# grab anything else you need from POST #}

    $currentUser = craft()->userSession->getUser();

    if (craft()->users->validatePassword($currentUser->password, $password))
    {
        {# saveEntry, saveUser, etc. then redirect #}
    }

    // Invalid Password
    craft()->urlManager->setRouteVariables(array(
        'error' => "Invalid Password"
    ));
}


Display rich text redactor menubar with superscript/subscript buttons So my client want to have superscript and subscript buttons in the redactor toolbar like this :
https://imperavi.com/redactor/examples/buttons-sup-and-sub/

I saw this Stack Exchange post here but it is just talking about the shortcuts and the way of making a plugin.

I tried adding the plugin directly in Craft Redactor config file plugins: ['fullscreen','video','table','scriptbuttons'], but of course did not work so easily.

Got Craft 2.4.2699. Is it possible ?
Is it on the Craft roadmap ?
Or should I make a feature request ?

The definitive and most simple answer for all Redactor Inline Styles requests is this great plugin by carlcs:
https://github.com/carlcs/craft-redactorinlinestyles

Just perfect!

(found it thanks to this post How to add a non-breaking space to a rich text field?)


Dealing with listing multiple categories I'm looking to use multiple category group as a navigation. The categories have 3 levels and will be listed depending on set variable. I can list all categories in the group with the following:

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

    {% nav category in categories %}

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

{% children %}

            {% endifchildren %}

    {% endnav %}

However I would like to list only the third level categories after the top level and secondary level have been set. I've tried playing around with descendantOf but to no avail. Any help to point me in the right direction much appreciated.

---Final code----
After @damon input below this is what I ended up with more or less the same:

{# ---- Set Default Segment ---- #}
{% set segment = 'default' %}
{# ---- If url segment is set, set segment to it ---- #}
{% if craft.request.getSegment(2) %}
    {% set segment = craft.request.getSegment(2) %}
{% endif %}
{% set cat = craft.categories.group('categories').slug(segment).first() %}
{% for categories in craft.categories.group('categories').descendantOf(cat).level(2) %}
    {% if categories.getDescendants().total() != 0 %}
        {% for category in craft.categories.group('categories').descendantOf(categories.id).level(3) %}
            ...
        {% endfor %}
    {% endif %}
{% endfor %}

I don't think I quite follow you, so forgive me if I'm way off. I have created (what I think) is something similar to what you are looking for. This is how I did it - maybe it will help...

Let's say I have the following url:

http://www.mystore.com/toys/my-awesome-toy

twig

{% set cat = craft.categories.slug(craft.request.getSegment(2)).first() %} {# Toys #}

{% for toys in craft.categories.group('toys').descendantOf(cat).level(2) %}
    {% if toys.getDescendants().total() != 0 %}
        {% for subToys in craft.categories.group('toys').descendantOf(toys.id).level(3) %}
            ...
        {% endfor %}
    {% endif %}
{% endfor %}


Craft 3: admin is showing "500 Internal Server Error" (apache) after fresh install Just successfully installed a fresh copy of Craft CMS via web browser method under:
http://kultursprung.org

Frontend is visible but the admin is showing raw Apache error 500 after fresh install:
http://kultursprung.org/admin

The 500 error is not the styled Craft dev one but instead the raw Apache version.

Server requirements check passed:
http://kultursprung.org/server/checkit.php

Did set all correct file and folder permissions.

Database is showing all necessary tables as expected after a successful fresh install.

phperrors.log:

UPDATE:

When I access the admin via this URL:
http://kultursprung.org/index.php?p=admin
i can provide an additional Craft styled error:

Any help?

(/usr/local/lib/php/extensions/no-debug-non-zts-20170718/imagick.so.so: cannot open shared object file: Permission denied))

Does that file exist on your server?  I'm asking because the .so.so extension seems odd.  If it does and it's just .so, it might be as simple as editing your php.ini file so it's loading imagick.so instead of imagick.so.so.

If you php.ini file is already loading imagick.so and that file exists on your server at that location, then check the permissions of that file and make sure that whatever user:group your web server is running as has permissions to read that file.


Password-Protect Sections - Newbie Template Code I was wondering if someone could take a minute for a newbie to show the template code for a section index page that needs to be private - accessible only to "clients" (pre-registered as users in a private group - I am assuming).

I am confused about the permissions in the users section of the CP as they only show permissions to edit/view assets etc.... not templates.

Someone asked a similar question and the response was "yes it is easy - go look at the User Model" - however I am just starting to figure all this out so need a little help!!

Thank you

Here is a basic template that requires login, and checks that the currently logged-in user is in the user group 'clients'.

{% extends "_layout" %}
{% requireLogin %}

{% block content %}

    {% if currentUser.isInGroup( craft.userGroups.getGroupByHandle('clients') ) %}

        {# Only viewable by logged in users that belong to 'clients' user group #}
        Restricted client content goes here.

    {% else %}

        {# Viewable by logged in users not in the user group 'clients' #}
        Notice to other user groups that this content is restricted to clients only.

    {% endif %}

{% endblock %}

You may also want to check out this question for more examples.


Reverse count with paginated entries? I have this loop

{% paginate craft.entries.section('sermons').limit('10') as pageInfo, entries %}

        {% for sermon in entries %}
        ...
        {{ loop.revindex }}
        ...
        {% endfor %}

That outputs a count from 10 to 1 - however, there's more entries than just the 10.

How can I output the count of what would be absolute total results?

You already pass the array keys into the loop as a variable pageInfo. There's a couple of properties you can access from it, see the Craft docs:

Templating Reference → {% paginate %}

Of interest for you is probably these:

  pageInfo.total – The total number of elements across all pages
  pageInfo.currentPage – The current page number.

It looks like there's no property for the "current element number" and you need to do some math to output it:

{% paginate craft.entries.limit(10) as pageInfo, pageEntries %}
    {% for entry in pageEntries %}

        {% set index = (pageInfo.currentPage - 1) * 10 + loop.index0 %}

        {{ pageInfo.total - index }} of
        {{ pageInfo.total }}

    {% endfor %}
{% endpaginate %}


Search with string, entry type and category So I have been asked to build a resource center just like hubspot's. (insert rolleyes here)

https://www.hubspot.com/resources

You can click into a type, and then click at topic and it creates a url like so:

https://www.hubspot.com/resources/ebook/advertising

You can also click a topic and then click type and it will do the same.

I have this part pretty much working. In my category template I have done this:

    Filter by type:

    case studies

    ebooks

    webinars

    whitepapers

then I have a routes for each type setup like this:

if the URI looks like this:

/resources/case-studies/*

Loads this template:

resources/_filtered.html

and in that template filtered I do the filtering like this:

        {% set resourceType = craft.request.segment(2) %}

        {% set resourceCatSlug = craft.request.segment(3) %}

        {% set resourceCat = craft.categories.slug(resourceCatSlug).first() %}

        {{resourceType}}

        {{resourceCat}}

        {% set filteredEntries = craft.entries({
            section:    ['resources'],
            orderBy:    {postDate: SORT_DESC},
            type:       resourceType,
            relatedTo:  resourceCat,
        }).all() %}

This all works and I planned to do the same method on the resources types to get to the filtered template.

But where I'm stuck is it was also requested that you could filter your search even further with a string search.

So from this:

<form class="control txt-center" action="{{ url('resources/search/results-filtered') }}">

<input class="input margin-auto-left-right display-block is-narrow-field " type="search" name="q" placeholder="Search">

<button class="margin-top-1 button is-info" role="button" type="submit"> Search the library </button>

How can I pass entry type and category to my filtered results?

{% set searchTerm = craft.app.request.getParam('q') %}
{% set searchResults = craft.entries.search(searchTerm).section('resources').orderBy('score') %}

PS, If anyone has a more elegant way to handle this I'm all ears!

A more elegant way to handle it would be to use PHP, rely on a more complex routing system with default values, don't rely on segments but on routing parameters. Use real URLs instead of relative paths and build your URLs dynamically instead of just inserting the string hard coded in order to be able to filter by multiple values without loosing the already set filters.

However - to include your parameters all you need to do

{% set searchResults = craft.entries
    .search(searchTerm)
    .section('resources')
    .type(resourceType)
    .orderBy('score')
    .relatedTo(resourceCat)
%}

I would create a dynamic rule

$event->rules[] = [
    'pattern'  => '/resources/<type:\S+>/<category:\S+>/<search:[a-zA-Z0-9-]+>',
    'route'    => 'module/search/search-elements',
    'defaults' => [
        'type' => '',
        'category' => '',
        'search' => '',
    ]
];

and search in PHP

public function actionSearchElements(string $type, string $category, string $search)

You can then create a Twig function/filter in order to change those

ebooks

to something like

ebooks

And build your URL based on your route params + overwrite the new category. It will make it more re-usable and easier to change in the future if you would like to add an additional parameter or need to change the order you only need to change one place instead of many


Having issues looping through variants for Commerce 2 Products using Element API I'm currently able to return the first variant using the following:

<?php
use craft\commerce\elements\Product;
use craft\helpers\UrlHelper;

return [
  'endpoints' => [
    'products.json' => function () {
      return [
        'elementType' => Product::class,
        'paginate' => false,
        'pretty' => true,
        'criteria' => [
          // 'id' => $productId,
        ],
        'transformer' => function (Product $product) {
          foreach ( $product->getVariants() as $variant ) {
            $variants = [
              'type'          => $product->type->name,
              'title'         => $variant->title,
              'sku'           => $variant->sku,
              'price'         => $variant->price,
              'stock'         => $variant->stock,
              'intro'         => $variant->intro,
              'purchasableId' => (int) $variant->purchasableId,
              'status'        => $variant->status,
              'url'           => $variant->url,
              // 'productPhoto'  => $variant->productPhoto
            ];
          }

          return $variants;
        }
      ];
    }
  ]
]
?>

Which returns:

{
  data: [
    {
      type: "Shakes",
      title: "3 Month Supply",
      sku: "mvn-a03m",
      price: "100.0000",
      stock: "0",
      intro: "
This is another 3M variant
",
      purchasableId: 37,
      status: "enabled",
      url: "https://testing.kamicrafted.com/shop/products/mavenshake?variant=37"
    }
  ]
}

The issue is that I currently have 3 variants set for this product yet the json response only shows the last variant within the set.

I'm completely stumped at this point and was hoping someone had a similar experience with a solution to this issue.

You are setting the whole $variants variable to last item in the array instead of creating the $variants array variable and pushing items into it. Should be this:

'transformer' => function (Product $product) {
          $variants = [];

          foreach ( $product->getVariants() as $variant ) {
            $variants[] = [
              'type'          => $product->type->name,
              'title'         => $variant->title,
              'sku'           => $variant->sku,
              'price'         => $variant->price,
              'stock'         => $variant->stock,
              'intro'         => $variant->intro,
              'purchasableId' => (int) $variant->purchasableId,
              'status'        => $variant->status,
              'url'           => $variant->url,
              // 'productPhoto'  => $variant->productPhoto
            ];
          }

          return $variants;
        }


Craft 3 Controller - Redirect with errors I cant get this to work.

I'm doing a post to a controller action. From that action I'm doing a lot of validations, db insert, file uploads etc.

Coming from Laravel Im used to is doing like this: (example with no real variables or methods. Just for visual presentation)

if(!$something->success){
  return redirect("controller-url")->withErrors([$model->errors]);
}
// Next steps...

From what I can read in the docs Craft recommend posting to the same page to handle errors and pass the model down to the view, but then doing a page refresh will do the same again so I dont like that.

My Controller is like this :

Index (Renders index template)

public function actionIndex()
{
    $this->renderTemplate("plugin/settings");
}

Post

public function actionPost()
{
  if(!$model->validate()){
    Craft::$app->getSession()->setError($message);
    return $this->redirect("url-to-index-action");
  }
}

So the setError() works fine but I want to the pass all the errors from the model to the index action after a redirect and then down to the twig template. How?

In Yii2 there is no such a thing as "redirectWithErrors". Usually you have 2 scenarios

There are validation errors -> display the form again with those errors
Successful -> redirect

The first one doesn't require a redirect. You just return null in your controller and Yii2 will route the to the url specified via GETparameter.

So you can have a form like this.

<form method="post" action="myUrl">
    <input type="hidden" name="action" value="mycontroller/save">
</form>

Yii2 will do the following

Route to your controller and hit the save action
When you redirect or return a valid object of type yii\web\Response or a string that can be converted into that it will return the Response
When you don't return something it will route the request a second time with your get param myUrl, that can be treated as you routeWithErrors function because you set the errors Craft::$app->getUrlManager()->setRouteParams(['model' => $model]) and have then access to these parameters.

If you really dislike this, you could use return Craft::runAction('controller/action', $parameters); to run a controller action with certain values

Edit
Usually you want to leave action="myUrl" blank and don't insert it. That way Yii2 will automatically reload the current route so you can dispaly your errors


Error Updating Freeform Pro I recently renewed many licenses on a previously-neglected site and have been attempting to update both the CMS and the plugins. Despite having a Freeform Pro license, the plugin appears as Freeform Lite on the update list, and attempting to update it from 2.3.0 to 2.5.27 results in the error message:

Error: Client error: POST https://api.craftcms.com/v1/composer-whitelist resulted in a 400 Bad Request response: {"message":"Unknown package/constraint: solspace/craft3-freeform@2.5.27"}

The CMS is currently at 3.0.41.1, and attempting to update to 3.4.22.1 results in an error also referencing Freeform:

Composer output: Package "craftcms/vue-asset" listed for update is not installed. Ignoring.
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

Problem 1
- The requested package solspace/craft3-freeform 2.5.27 exists as solspace/craft3-freeform[3.0.0-beta.1, 2.5.24, 2.5.23, 2.5.22, 2.5.21, 2.5.20, 2.5.19, 2.5.18, 2.5.17, 2.5.16, 2.5.15, 2.5.14, 2.5.13, 2.5.12, 2.5.11, 2.5.10, 2.5.9, 2.5.8, 2.5.7, 2.5.6, 2.5.5, 2.5.4, 2.5.3, 2.5.2, 2.5.1, 2.5.0, 2.4.3, 2.4.2, 2.4.1, 2.4.0, 2.3.4, 2.3.3, 2.3.2, 2.3.1, 2.3.0, 2.2.2, 2.2.1, 2.2.0, 2.1.1, 2.1.0, 2.0.4, 2.0.3, 2.0.2, 2.0.1, 2.0.0, 2.0.0-beta.20, 2.0.0-beta.19, 2.0.0-beta.18, 2.0.0-beta.17, 2.0.0-beta.1, 2.0.0-beta.2, 2.0.0-beta.3, 2.0.0-beta.4, 2.0.0-beta.5, 2.0.0-beta.6, 2.0.0-beta.7, 2.0.0-beta.8, 2.0.0-beta.9, 2.0.0-beta.10, 2.0.0-beta.11, 2.0.0-beta.12, 2.0.0-beta.13, 2.0.0-beta.14, 2.0.0-beta.15, 2.0.0-beta.16] but these are rejected by your constraint.
Problem 2
- The requested package solspace/craft3-freeform (locked at 2.3.0, required as 2.5.27) is satisfiable by solspace/craft3-freeform[2.3.0] but these conflict with your requirements or minimum-stability.

Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.

Based on other posts here, I've attempted to remove the "3" from "craft3-freeform" in the composer.json file (since reverted) and have attempted to run "composer update", though it appears I do not have shell access here. I know these are some outdated versions here, but any recommendations?

Thanks for your time and insight.

Figured it out, posting here in case someone runs into something similar. I opened up the composer.json file in the root and removed the line "solspace/craft-freeform": "2.5.27" from the "required" list.

From there, I was able to update Craft, and then I was able to update Freeform. All is well, and all up-to-date!