EVENT_BEFORE_SAVE_ELEMENT Saving element loop in task/queue

The easiest solution for this would be either a special fieldvalue or a property in a behavior that you set in your job.

class MyJob extends BaseJob
{
    public function execute($queue): bool
    {
        // do some api calls here
        // ....
        $entry->isSavedViaApi = true;
        $entry->setFieldValue('browsershotUrl', [$assetId]);
        Craft::$app->elements->saveElement($entry)
        // ....
    }
}

and in your event, you can check against this property

Event::on(
  Elements::class,
  Elements::EVENT_BEFORE_SAVE_ELEMENT,
  function(Event $event) {
    $entry = $event->sender;
    if ($event->element instanceof \craft\elements\Entry) {
      $entry = $event->element;
      if (ElementHelper::isDraftOrRevision($entry)) {
        return;
      }

      if(($entry->sectionId == 18) && ($entry->typeId == 26)) {

        // make sure to check the section first, because otherwise the entry
        // may not have this property of field
        if($entry->isSavedViaApi === true){
            return;
        }
        //

        $signpostURL = $entry->getFieldValue('signpostUrl');
        if(($signpostURL) && ($signpostURL !== '')) {
          Craft::$app->queue->push(new BrowshotTask([
            'description' => 'Grabbing screenshot for '.$signpostURL,
            'url' => $signpostURL,
            'entryId' => $entry->id
          ]));
        }
      }
    }
  }
);

If you want to use a behavior instead of a field value for this

use yii\base\Behavior;

class MyBehavior extends Behavior
{
    public $isSavedViaApi = false;
}

and in your main module file

use craft\elements\Entry;
use craft\events\DefineBehaviorsEvent;

Event::on(
    Entry::class,
    Entry::EVENT_DEFINE_BEHAVIORS,
    static function(DefineBehaviorsEvent $event) {
        //so every element gets a isSubscribed attached
        $event->behaviors[] = MyBehavior::class;
    }
);


Setting cookies from CTA before directing to url?

One option would be to set a query string on your cta urls, have your template look for that, and change your content accordingly.

You can't set a cookie in Twig directly but there's a few plugins to help you out, like the handy LJ Cookies.

So on your CTA you might do something like:

Buy now!

Then in the template it links to:

{% if craft.request.getParam('cta') is not empty and craft.request.getParam('cta') == "button2" %}
    {# set the cookie #}
    {% do craft.lj_cookies.set('ctabuttonclicked', 'button2') %}
{% endif %}

To check for the cookie later on:

{% if craft.lj_cookies.get( 'ctabuttonclicked' ) == 'button2' %}
 .. do something ...
{% endif %}

As a quick aside, depending on how you want to track things, you may want to throw in a quick redirect to dump the query string off the URL before you spit out the subsequent page. Something like:

{% if craft.request.getParam('cta') is not empty and craft.request.getParam('cta') == "button2" %}
    {# set the cookie #}
    {% do craft.lj_cookies.set('ctabuttonclicked', 'button2') %}

    {# redirect to the same page but dump the query string#}
    {%redirect entry.getUrl() %}
{% endif %}

But it really depends on your campaign and how you want that content shared and consumed.

(Just a quick example: if someone hits your CTA from an e-mail you sent and really likes what they see and posts that link with the special query string to all their friends on Facebook, everyone is going to get that event cookie set and those deals, for better or worse.  It could also piss people off if you don't have it... "What? I don't see those deals you sent me..." But that's a can of worms for another time...)


Bizarre admin menu problem after upgrading craft to latest version

I've had this with the Dukt Analytics plugin, hitting Settings took me straight to the Analytics settings and nothing else.
Not particularly helpful in this instance but an upgrade to the plugin sorted the problem.


There's no block types with SuperTable, so no need to check that. Other that that it works just the same as Matrix fields.

{% for row in superTableField %}
    {{ row.textFieldInSuperTableField }}
{% endfor %}


How can I embed Image into Craft Commerce 2 PDF Receipt

The PDF renderer uses DOMPDF, which expects a HTML document to be supplied. See the DOMPDF documentation to see what types of images are supported.

If you are linking images from a different domain you will need to set the commerce.php config setting of pdfAllowRemoteImages to true.

https://docs.craftcms.com/commerce/v2/configuration.html#pdfallowremoteimages


Exclude categories that are not associated to any entry

You could get the entries ids, then get the categories related to those entries.
Like so:

{%- set ids= craft.entries.section('sectionHandle').ids() -%}

{%- for cat in craft.categories.relatedTo({ sourceElement: ids }).group('categoryHandle') -%}
    {{- cat.title -}}
{%- endfor -%}

That will only print categories with an entry associated with.


What would be the best way to select and display featured entries?

It always depends on your needs, so there is not really a best way to do feature entries, at least if you don't give more specific info on what your exact situation is.

To have a nice overview of which entries are featured, you could add a new section (structure or channel), with an entries field to collect the entries you want to be featured.

Another idea would be to collect the feature entries in a global set using matrix. You need just a single block type, again with an entries field to make the relations.

A Matrix field or a structure section enables you to (optionally) re-order the entries to your liking.


Try reading this.

return $this->render('index', ['twitterFeed' => $twitterFeed]); is correct in craft 3 you just need to do some custom routing in order to reroute the homepage to your controller/action.


I'm not sure I totally get what you're trying to do, but if you want to cut down a bit of the code duplication in here a good start would be to use a Twig macro.

So you'd set up your macro like this:

{% macro input(cssClass) %}
    Viewer
    Photo
    Video
{% endmacro %}

{% from _self import input %}

Then you'd call it like this:

{{ input('dual') }}

If you don't need the value of the radio buttons for anything else, you could always give them the value of your CSS class; that way you don't need to convert them from one thing to another. If you did it this way then you could just call your macro like this:

{% set cssClass = entry.viewerOption %}
{{ input(cssClass) }}

Make sure you've set a default option for your radio button when you've added it in the Control Panel.


Determine if entry has a specific category

You could explicitly try to fetch orange and check whether you get a result or not:
{{ row.rowSettings.rowColour.slug('orange').one() ? 'orange is set' : 'orange is not set' }}


Calling unknown method: craft\models\Section::hasUrls() - Craft 3 upgrade

In Craft 3, hasURL() is part of a section's siteSettings() as a section exists in multiple sites and can have URLs in one site but not another.

If you are not using multi-sites, a drop-in replacement for your code would be:

{% for section in craft.sections.getAllSections() %}
  {% for sectionSettings in section.siteSettings %}
      {# We only care about sections that have URLs #}
      {% if sectionSettings.hasUrls %}

{{ section.name }}

            {# Loop through each of its entries #}
            {% for entry in craft.entries.sectionId(section.id).limit(null) %}

{{ entry.title }}

            {% endfor %}

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


How can you check if "public user registration" is turned on or off on the front-end?

You can write a plugin that has a Template Variable that exposes Craft's SystemSettingsService->getSettings() method like so:

public function isPublicRegistrationEnabled()
{
    return craft()->systemSettings->getSetting('users', 'allowPublicRegistration');
}

You'd call it from the template via:

{% if craft.myAwesomePlugin.isPublicRegistrationEnabled() %}
    Enabled!
{% endif %}


How can I get a related Asset of a specific field?

Found it! The specific field can be targeted like this:

$criteria->childField = 'myFieldHandle';


What is the significance of craft.app.request.segments|last == _self

In Twig 2, _self seems to reference the name of the current template file.

Running {{ dump(_self) }} from the file /templates/test/index.twig would return the string test/index.

So what it's being used for in this example...

{% if craft.app.request.segments|last == _self %}

... is to test wether the last segment in the URL matches the template file. Or "is this template being loaded directly from the file-based routing or is it being included from another template?"

I would point out tho, this only matches the last segment. It assumes your feed.rss file it in the root templates directory. A more flexible approach would be to math the entire URI path.

{% if craft.app.request.pathInfo == _self %}

Later _self is used to generate a link with {{ url(_self) }}. This is the same as {{ url('feed.rss') }} but would update if you changed the file name (e.g. news.rss).

The whole point of that conditional is to replace the {% include 'feed.rss' %} with a <link rel="alternate" type="application/rss+xml" href="{{ url('feed.rss') }}">. Which seems a little complicated. After all, it provoked this Stack Exchange question. ;)


Accessing relation data in an on save entry event

Related elements, such as categories, are stored by Craft as arrays, even if there is only a single element to be stored.

In your entry form, you don't appear to be storing an array, but only the id of the selected category:

<input type="hidden" name="items[{{ index }}][finish]" value="{{ finish.id }}">

This seems to be borne out by what you see when you dump the item variable in your email template. item[finish] should be an array, even if only a single category is assigned to it.

Try using the following in your entry form to see if that helps, which should store the category id as an array:

<input type="hidden" name="items[{{ index }}][finish][]" value="{{ finish.id }}">

You should then be able to access the correct value for the category title using the code in @carlcs answer.


There are a couple of issues here:

Any action methods in the controller class needs the "action" prefix to their name, i.e. actionIndex() for your "index" action, or actionLogin() for another action route calling "myPlugin/access/login".

In other words, rename your index() method in the controller to actionIndex(), and it should work.

For action routes, plugin handles should be written in camelcase – i.e. "myPlugin/access/index", not "myplugin/access/index".
You have a minor typo in the controller:

$templatesPath = craft()->path->pluginsPath().'myplugin/templates/site/';

should read

$templatesPath = craft()->path->getPluginsPath().'myplugin/templates/site/';

Addendum: this might just be a typo in the question text, but your controller class is actually named "Portal_AccessController", so it will obviously not respond to the route "myPlugin/access/index" – the latter should read "portal/access/index" (or the controller should be renamed "MyPlugin_AccessController)


I had a similar problem so created a plugin to add some of these extras to Redactor.

Redactor Extras for Craft

https://github.com/elliotlewis/Redactor-Extras
Available plugin are:

Superscript and Subscript
Word count
Alignment
Custom plugin


Contact Form Plugin how to send to different recipients based on country selected?

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

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

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

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


Splitting a Twig Variable into 2 Variables

If I'm following you, arrays are 0-indexed, so this would work (with a bit of cleanup).

{% set names = 'first-last'|split('-') %}
{% set firstNameSlug = names[0] %}
{% set lastNameSlug = names[1] %}

{{ firstNameSlug }}
{{ lastNameSlug }}


Unable to set cart currency in index.php

How do you render the cart?

Using {{ item.price|commerceCurrency(cart.paymentCurrency,convert=true) }} instead of {{ item.price|commerceCurrency(cart.currency) }} works for me.


How can I get the correct folder ID when uploading assets from a front-end form/plugin?

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


I have a lot of experience with WordPress (8+ years), and I like it. I have less experience with Craft (obviously), though I like it more. Here's my 2 (obviously opinionated) cents regarding WordPress vs. Craft:
Benefits of WordPress (vs. Craft)

Free (although a Craft Pro license is super cheap, and you typically need at least 2 or 3 paid plugins to make WordPress usable as a CMS)
Open source (although Craft is built on Yii, which is open source as well, and the full Craft codebase is on GitHub)
Huge community & amount of resources available – although actually sifting through it all and finding quality content can be a chore/difficult – a lot of it is outdated or just bad practice
High quality 3rd party services available for hosting (WPEngine etc), backups/security (VaultPress) and core management (ManageWP) – although if stuff like that is your thing you can use similar services w/ Craft installs; they're just not tailor-made to Craft
Plugin management. Being able to search for, install and update plugins from the Dashboard is great – although Craft will soon have its own built-in "plugin store" and it will be curated, which is a huge plus. Update: Since writing the original answer, Craft 3 has launched and has a built-in Plugin Store inside the Control Panel, which can be used to browse, install, manage and purchase plugins (all commercial plugins have trial versions, and there are loads of free ones too). If clicking buttons in a virtual Store is not your thing, Craft also has first-party support for Composer)
WooCommerce is good (and free) (although Craft Commerce is out and is looking very nice) Update: Craft Commerce 2 beta is now available, and is looking even nicer
Dashboard is easy to use (though I think Craft's CP is on par and IMO makes a better job with keeping things clean and simple)
The built-in media library/uploader/image editing is pretty good (although it still doesn't have folders. Why on earth doesn't it have folders) ** Update: Craft 3 has a simple image editor for cropping, rotating and focus points**
There's a plugin for everything (...and you'll need them. In my experience, Craft installs typically needs fewer – and more to the point; less CMS-critical – plugins.)
There's a hook or filter for everything (although with their inconsistent naming conventions and priority hell you'll quickly build yourself a maze if you're not careful. Craft primarily uses well-documented events to enable plugin authors to "hook" into and affect the app lifecycle, with fairly self-explanatory names)
WordPress has multi-site (although it's pretty half cooked and loaded with gotchas) **Update: Craft 3 also has multi-site, although it's less geared to the use case of running completely separate sites under the same CMS install, and more to the use case of maintaining separate content for different target groups/languages/etc)
WordPress is stable as hell. Not that there aren't bugs, but breaking changes are very rare (there are "deprecated" APIs still in the codebase that are years old). The WP core team is super focused on backwards compatibility, which means important stuff aren't likely to change in a big way anytime soon – however, it also means that much of the codebase is outdated and/or inconsistent. In comparison, Craft is still fairly young and feels much more in flux. This doesn't mean that Craft breaks all the time – it doesn't, bugs are relatively rare and P&T have generally been super quick w/ bug fixing. Compared to WP, there have been bigger changes between major versions, though (mainly for plugin developers, not so much in terms of frontend code/templating or content modelling). Basically P&T/Craft is dedicated to staying modern and lean whilst WordPress is dedicated to not pissing off a gazillion users. These are very different approaches to building and maintaining a CMS, and one may resonate more for you than the other.
– WordPress has a RESTful API built-in. I haven't tried it, so I couldn't say if it's any good. Craft doesn't have a REST API in core, but for the quick'n'easy stuff there's the first party Element API plugin. Beyond that, since Craft 3 came out it's actually quite easy to go totally headless with Craft.

Weaknesses of WordPress (vs. Craft)

No templating engine (some might deem this a good thing. I'm not among them; personally I recommend Timber for using Twig in WordPress themes)
No advanced custom fields – although ACF exist and is a must-have for any WP install. Full disclosure; I actually prefer parts of ACF to Craft's built-in custom fields – for instance its export/import and conditional functionality is stellar. Update: In place of the WYSIWYG of yore, WordPress has this new-fangled thing called "Gutenberg" now, which is kind of like a core modular content system. I haven't tried it, but personally I think it looks like a total trainwreck. Also, both conditionals and import/export is supposedly coming to Craft core in a 3.x update
WordPress is not really a CMS, but a blogging platform – no matter what they tell you. Unless you're building a traditional blog, you'll constantly be fighting the core (yes WordPress has custom post types and taxonomies, but they feel tacked on and are cumbersome to work with, compared to Craft or ExpressionEngine)
The architecture. Most of WP's core and its theme/plugin APIs aren't even OOP, forget MVC. Again I recommend Timber, which in addition to adding Twig support enables you to build themes using an optional, pseudo-MVC pattern.
There's a lot of redundancy. Even as a fairly basic blog platform, there's a ton of built-in functionality in WP that you'll never need for the average site, and that you will want to remove or deactivate. It's like starting with a Pollock painting and applying paint remover until you have something you can draw on, instead of just starting with a blank sheet of paper in the first place
Obtuse APIs (naming conventions are practically nonexistent and the APIs are super inconsistent, which means you need to look up everything)
Security issues (though its actually very easy to secure a vanilla WP install, its still an issue and always will be, as long as WP is the most popular CMS on the planet)
No built-in multilingual support (WPML is decent, but fairly pricy and also big and slow, in my experience)
WordPress is really strict and opinionated about routing, and creating custom routes is needlessly complex (although Timber will help out here, as well)
Hierarchical content and custom ordering is hell (there are plugins but ew)
Confusing relationship between presentation and content. In WordPress, any custom post types or taxonomies is creating w/ PHP code, usually residing in your theme. This creates a confusing relationship between presentation and content modelling which is totally absent in Craft. The big paradox here is that while Craft isn't really designed to be "theme-able", it still has a much clearer separation between content and presentation than WordPress. (Related WP pro tip: Consider building a custom plugin to add your CPTs and taxonomies, keeping it out of your theme)

Closing notes:
The above is obviously quite opinionated, and I'm clearly partial to Craft.
Obviously, just like with Craft you can basically build anything with WordPress and I find that for the most part, the two systems are fairly interchangeable. There are a few use cases where Craft is obviously the better choice though – e.g. multilingual sites, or sites w/ a lot of hierarchical content. Apart from blogging, I'm having more difficulty coming up with use cases where WordPress is a more obvious choice, which says a lot.
In my opinion, if you're new to both WordPress and Craft, I personally think the experience you'll gain using Craft will be more portable and valuable in the long run, and that you'll experience far fewer gotchas, headaches and WTFs in the process.
Personally, the #1 pro for me w/ Craft vs. WordPress is has been that almost from the get-go (there's a learning cure, sure), I was able to work more effectively w/ Craft than I ever did in WordPress, and I felt more confident about the quality of my work – even with the mentioned 8+ years experience with WordPress. The super consistent codebase/APIs, stellar documentation and Twig templating are important factors here. I also prefer the smaller, super dedicated community around Craft, this SE and the official Slack team being good examples.
PS: I should also note the excellent official support Craft comes with, courtesy of Pixel and Tonic.


This isn't possible with the build in FEEF functionality.

You can however achieve this via a Plugin. What you need to do is to listen to the onBeforeSave event and make an if-statement to check if the entry is being added to your section. Then you can run a loop that checks all the submitted categories against your database and adds new entries for the categories that doesn't exist and re-populates your EntryModel with the new category ID's before insertion.


Dropdowns are not relations, and therefore they don't need to be eager loaded (and indeed can't be eager loaded).

Here's an explanation of what's going on under the hood with eager loading: Speed up your Craft CMS Templates with Eager Loading

TL;DR only things that are relations, which are other Elements are candidates for eager loading. Other fields like Dropdowns, etc. are loaded as part of the content already.


How to skip the first few entries in a "load more" scenario with Element API?

If you always want to skip the first 6 entries, you can modify the Element API endpoint to add 6 to the offset number in the query criteria:
$criteria = [
    'section' => $settings['section'],
    'limit' =>  $settings['limit'],
    'offset' =>  ($settings['limit'] * Craft::$app->request->getParam('offset')) + 6,
    'order' => $settings['orderBy'],
];

That should mean you're always receiving results that are offset by the required amount for your request, plus the first 6 entries that you always want to skip.


Link to Global Fields like you can with the entries fieldtype

Not directly. You could have a dropdown of all the global set handles (use Dynamic Fields Plugin if you don't want to maintain this dropdown manually), and then in your template fetch the global set from its handle with getSetByHandle:

{% set globalSet = craft.globals.getSetByHandle(entry.globalSetHandle.value) %}
{% if globalSet %} {# could conceivably be null #}
  ... {{ globalSet.someField }} ...
{% endif %}


You can access the dynamic variables in your custom fields using single curly brace syntax:

Title: {title}
URL: {url}
Author Name: {author.fullName}
Author Email: {author.email}

There is a longer discussion on Dynamic Values in the docs.


How to do an ajax submission to Craft Contact Form with attachments using FormData?

I have it working now using this:

$('#Form').submit(function(ev) {
// Prevent the form from actually submitting
ev.preventDefault();

var formData = new FormData($(this)[0]);

// Send it to the server
$.ajax('/', {
  url:  '/',
  data: formData,
  type: 'POST',
  async: false,
  cache: false,
  contentType: false,
  processData: false,
  success: function(response) {
    if (response.success) {
      alert('Thank You. Your message has been sent');
    } else {
            // response.error will be an object containing any validation errors that occurred, indexed by field name
            // e.g. response.error.fromName => ['From Name is required']
            alert('An error occurred. Please try again.');
          }
        }
      });
});


Safely calling other plugin code from a different plugin

You could write a wrapper log method that's abstracts the check away so you only have to do it once.  The update your code to use the wrapper.

That method could just use craft()->plugins->getPlugin('pluginHandle') to see if the target plugin is installed and enabled.  Craft will auto-load the plugin's services, so you don't need to check specifically for them.


commerce/cart/updateCart throws page not found

Based on your tag, I assume you're using Commerce 2.

Controller actions in Craft 3 use kebab-case, now, so your action should be commerce/cart/update-cart.


You'd have to write a custom plugin, that adds an event listener for the entries.saveEntry event.

The event listener should go into your plugin's primary class' init method. You'll need to make sure the event listener only fires for Control Panel requests, and that the currently logged in user is assigned to the user group you want to apply this to.

You should also make sure the entry being saved has a URL (not all entry types do) before attempting to redirect.

Here's a working example:

public function init()
{

    parent::init();

    // Get the currently logged in user
    $user = craft()->userSession->getUser();

    // Only add the event listener for Control Panel requests, and if the user is in a certain group
    if (craft()->request->isCpRequest() && $user && $user->isInGroup('userGroupHandle')) {

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

            // Redirect to the entry
            $entry = $event->params['entry'];
            if ($entry->url) {
                craft()->request->redirect($entry->url);
            }

        });

    }

}

Obviously, you'll need to change the userGroupHandle string to the handle for your particular user group.

If you're unfamiliar with plugin development for Craft, I'd recommend taking a look at the Business Logic template plugin or the pluginfactory.io generator app – as well as the official docs on the subject, of course.


Those aren't Craft log files.  I'd suggest contact your host and give them the files so they can look and see what's dumping them and what the root problem is.


I think a custom plugin is going to be the best way to do this.

If the client is adding users from the control panel, they'll either have to put in a dummy email address (username+someUniqueId@domain.com), or you can create a front-end "user management" form for them that fills that in behind the scenes.

Regardless, you'll want to be sure "verify email address" is not enabled for the user at this point.

Then, assuming you'll be providing your own front-end login form for these users, with username/password fields (password not required), you'll want to point the form to your plugin's controller with it's own actionLogin method (very similar to Craft's UsersController->actionLogin), but check to see if password is null and if the username is valid and if the email is in the format of the unique email address that was previous assigned.

If so, prompt for their actual email, then send Craft's activation email and let it go through Craft's usual workflow for setting a password on the user account.


How to rename filename of asset before (or after) upload in Craft 3

Well, after searching for this for more than 2 hours, I bumped into a working solution;

Event::on(
    Elements::class,
    Elements::EVENT_AFTER_SAVE_ELEMENT,
    function(ElementEvent $event)   {

        $asset = $event->element;

        if ($event->isNew && $asset instanceof craft\elements\Asset) {
            $newFileName = 'file-'.md5(time()).'.'.$asset->getExtension();
            Craft::$app->assets->moveAsset($asset, $asset->getFolder(), $newFileName);
        }
    }
);

In case somebody needs it!


How do you correctly handle errors using PT Contact Form when submitting via AJAX?

If you want the error messages to be displayed identical to P&T's template/macro default, this should do the trick:

$(function(){
    $('#ajax-form').submit( function(ev) {

        // Prevent the form from actually submitting
        ev.preventDefault();

        // Remove any existing error messages from previous attempts
        $(this).find( '.errors' ).remove();

        // Get the post data
        var data = $(this).serialize();

        // Send it to the server
        $.post('/', data, function(response) {

            if (response.success) {

                // All good!
                $('#thanks').fadeIn();

            } else {

                // Loop through the errors and append to relevant fields
                for ( var fieldId in response.error ) {

                    // Create the

                    var $errors = $( '
' );

                    // Wrap the error messages in
 tags
                    var messages = $.map(response.error[fieldId], function(message){
                        return '
' + message + '
';
                    });

                    // Append the error messages to the ul
                    $errors.append( messages.join( '' ) );

                    // Select the input element and append the .errors
 after it.
                    $('#'+fieldId).after($errors);

                }

            }
        });
    });
});

Just to be clear though, the PT Contact Form template doesn't force you to display your error messages in any given way; both the contact form template as a whole, and specifically how error messages are displayed (both the template macro for non-Ajax requests and the stuff in the response.error object) is obviously totally up to you.


Can Craft Commerce receive back the address used in paypal express checkout?

Well, after a transaction, you can log the full gateway response etc using this event: https://craftcommerce.com/docs/events-reference#commerce_transactions.onsavetransaction

(See what you can get at/log at https://craftcommerce.com/docs/transaction-model)

craft()->on('commerce_transactions.onSaveTransaction', function ($event){

      $transaction = $event->params['transaction'];
      BusinessLogicPlugin::log($transaction->response);

 });

...that should hopefully get you started on grabbing the data you need (I presume PayPal returns the address) - and then adding that to the cart at that point.


Just put it on the same level as where the other folders reside, just like:

config/...
storage/
templates/
vendor/...
web/...


The default template path can be changed by setting the CRAFT_TEMPLATES_PATH PHP constant in your index.php file:

define('CRAFT_TEMPLATES_PATH', "path/to/templates");

I would recommend, however, that you start with the default path locations, until you are more comfortable with the app. There is a logical system in place which works really well once you get used to it. And you'll notice that the craft app folder (and all the plug-ins) are organized the same way, so once you start building your own sites and plugins it will all feel very integrated. Keep in mind the folder names also match the names of the controller classes, service classes, method names, etc. throughout the system which makes it easier to find your way around.

Another thing to keep in mind is that placing your Craft templates in your web root would make the raw template files publicly accessible from a web browser. So if you decide to go this route, be sure to place a .htaccess file in your templates folder with this:

Deny from all

That will tell Apache to protect the files from direct HTTP traffic, even though they live within the web root.


Yii Toolbar triggering mixed content warning in CP

On a multi-site setup, you need to use the multi-site array syntax for the siteUrl config setting like so:

'siteUrl' => [
    'siteHandle1' => 'https://foo.com/',
    'siteHandle2' => 'https://bar.com/',
],


Thanks to Jason and Ben for the assist. I changed tac a bit and moved the field on to the user (as an Entries field). Now got this working:

{# Display unread notifications for current user #}
{% set notificationsRead = currentUser.userNotificationsRead.ids() %}
{% set notifications = craft.entries
    .id(['not']|merge(notificationsRead))
    .section('notifications')
    .all() %}

{% for entry in notifications %}
    {{ entry.title }}
{% endfor %}


I am a beginner at craft cms,but  I think these would help you:-
1.install the plugin guest entries and look at their code
2.look at this article


Turned out to be a minor bug in 3.3.0.1. Fixed now.


How to access Plugin Variable in Plugin?

You can access your plugin via

Plugin::getInstance()

You can then access its components defined by setComponents

Plugin::getInstance()->componentName

Or

Plugin::getInstance()->get('componentName')

If you just want to get properties of your main plugin class it's the same way as above. Not sure what you mean exactly by Plugin Variable


What's the difference between total function and count function?

.count will return the total results for a particular paginated page (in your case, it should be the same as the resultsLimit variable).

.total will return the number of results in the entire results object regardless of pagination.


Localization Section propagation settings, and related entry field setting "Manage relations on a per-site basis"

This is intended behavior – when you first create a new entry, any content you enter will be copied to other sites, up until the point where you either save the entry, or start editing one of the other sites.
If you’re worried about unintended content getting published in other locales, then I’d suggest you set the other sites’ default statuses to disabled, from your section’s settings. Once Craft 3.5 comes out you’ll have another option as well: sections have a new propagation method option which gives entries control over which sites they should be saved to, so the entry won’t exist at all in other sites up until someone explicitly enables the site for the entry.


Does setting Enabled = 1 override postDate in front end forms?

Setting enabled = 1 will not alter your postDate.

If you were to set a postDate in the future, the entry would simply not show up (by default).
However, if you'd want enabled entries with a future postDate to show up in your template results, use:

{% set futureEntries = craft.entries.status('pending') %}

If you do not use the status criterium, it will default to live, which is all enabled entries with a postDate in the past. So, it all depends on how you display the entries, not so much on how you save them.

Also, see this Craft docs information on status.


What's the best way to clear cached JSON responses from a third-party API?

You can delete a cache as follows:

craft()->fileCache->delete($key);

So instead of caching individual keys, you could perhaps cache an array of keys and delete the entire array when you want to clear out your keys.


There is a Tag Manager plugin you can use for this.

You might also want to consider voting for this feature request if you think this should be in core!


I implemented this recently and what I ended up doing is return the content security headers using PHP. Depending on the request path (is it a CP URL or not?), we send CSP headers with unsafe-inline and unsafe-eval or not.

Of course, the CP is still unsecured, but what you can do is to specify a custom domain for the CP in your configuration (using the "baseCpUrl" key). Something like "admin.yourwebsite.com" that only certain users can access (using IP filtering for example).

It is still not a perfect solution, but we cannot do anything at the CP level unfortunately. I hope this will not be an issue anymore in Craft 3.


How to add recipient drop-down selection field with the Contact Form plugin?

There's no way to do this by default as far as I know so you'd need to create a small plugin to dynamically add the recipient email to the form.

You might be able to shortcut that a little by changing that hidden input value using JavaScript alone depending on what was selected in the dropdown but the toEmail param is meant to be hashed.


How can I efficiently show the number of total results per channel when retrieving search results?

I would try one of the following solutions:

Using Ajax to display the search results and the filter
Use ajax to display your search results. After loading the search results, you'r using a second Ajax call to get the categories filter. With this solution the visitor see the results quickly and get the filter after. You could try to including caching of the filter to speed up the page. Downside: Depending on requests and amount of content, it needs still a lot of server power.

Writing your own Plugin
Write your own search filter plugin with your logic and optimized SQL to get the filter faster. This solution can be combined with the first one.

Using a external Search Engine
A other solution could be to use a external search engine service like Google Custom Search. Some of them offers filters/logic like you need. Craft CMS Example: On https://straightupcraft.com/craft-plugins/ the search is provided by Algolia (https://www.algolia.com/). On the right side is a simple developer filter, including number of entries.
Take also a look at the Algolia Plugin to easy integrate Algolia in Craft CMS -> https://straightupcraft.com/craft-plugins/search-plus-1


Brad Bell mentions this about Craft:

  Being a self-hosted PHP web application is a hard life. Your parents try to raise you correctly and equip you with the tools necessary to successfully go out into the world and live on some stranger's unknown, unfriendly and oftentimes hostile server environment so you can serve up web pages, but things are stacked against you.

It's a great read.

I hate to be that guy, but any reason why you don't want to run something more common like MAMP? I haven't used PHP's built in server but there might be little things here and there that you'll pull your hair out over as to why your installation is functioning weirdly and it ends up being a little "quirk" with the PHP web server performing oddly with Yii or something else.

Unless you're just fooling around, it's ideal to run the same software locally as you do on a "live" web server. Some people go so far as to clone the environment they're in (like Vagrant or some other virtual machine), but MAMP works well.

I've used Home Brew and I've even compiled PHP/MySQL/Apache manually on a Mac. It's a great exercise in dependency management, using the command line, and seeing what other packages all go into PHP—there's a ton!—but that's really where it ends. The web server stack is just a tool and MAMP gives me all of that in a simple download that you can easily move/remove. If you have more than one computer, you're going to have to repeat those home brew commands on every Mac you want to test on; MAMP is 3 clicks and I have the stack.


I forgot to post the solution:

_hero-banner.twig

{% set defaults = {
    heroTitle : options.title is defined ? options.title : 'Title not set',
    titleColor : options.color is defined ? options.color : false,
    heroImage : options.image is defined ? options.image : null,
    heroClasses : options.classes is defined ? options.classes : null,
    heroButton : options.button is defined ? options.button: null
  }
%}

{# Options #}

{% set options = options is defined ? defaults|merge(options) : defaults %}

{# Variables #}

{% set imageMobile = {
    mode: 'crop',
    width: 640,
    height: 500,
    quality: 75,
    position: 'focus-point'
} %}

{# Output #}

      {# Display the entries #}

{{ defaults.heroTitle }}

            {% if defaults.heroButton %}
              {% include "components/_buttons.twig" with options %}
            {% endif %}

I wasn't calling my variables properly...


How to check if currentUser matches a users field within an entry?

I asked this on the Slack chat and Brad Bell kindly came to the rescue.

{% set match = false %}

{% for user in entry.users %}
    {% if user.username == currentUser.username %}
        {% set match = true %}
    {% endif %}
{% endfor %}

{% if match %}
    User matches!
{% endif %}


I think I've already found it:

`craft_info` (`id`, `**version**`, `schemaVersion`, `edition`, `siteName`, `siteUrl`, `timezone`, `on`, `maintenance`, `dateCreated`, `dateUpdated`, `uid)

I can download the correct version from this great service: https://craftcms.com/support/download-previous-versions


Search question: dot (period) and partials

I don't think it will be possible using the native Craft search engine as part of the search keyword normalization process involves removing all punctuation from the keywords: https://github.com/craftcms/cms/blob/develop/src/helpers/Search.php#L62

It might be possible on front-end searching using something like Algolia, though:
 https://plugins.craftcms.com/algolia


If you analyze POST request you will see this:

To get items by id you need to create custom request and add criteria[id]=(int id here) e.x. use this

It is most likely related to the way how ElementCriteriaModel works (99% sure) :

craft.entries({
    id: 123, // search by ID*
    search: 'search query'
})

search by ID*

craft.entries returns ElementCriteriaModel - Source

You won't be able to use default CP search form to get entries by ID


How to add attachments to emails with sprout forms

It looks like your on the right track but there may be a couple issues that we need to address:

The hidden config setting should be set in the craft/config/general.php file
The enableFileAttachments config setting should also be within an array and set to true:

return array(
  'sproutForms' => array(
    'enableFileAttachments' => true,
  )
);

Note: As of this posting, we handle this functionality as a hidden config setting because we are still refining it's behavior. At some point, this will likely be easier to do directly from the Control Panel.


You could build a plugin that loops thru all the entries and creates a new entry programmatically, the only difference in the new entry would be the locale. Look at this: https://craftcms.com/classreference/services/EntriesService#saveEntry-detail, you would include $entry->locale = 'fr_us'; or whatever the shortname/handle of that locale is.

Or export the entries and import them, and setting the locale to french using something like: https://github.com/engram-design/FeedMe


Running plugin migrations when other plugin is installed?

You can have plugin A listen for the EVENT_AFTER_INSTALL_PLUGIN event.
It could then check if $plugin->handle is for plugin B and if so, run any additional install migrations or logic.
You could do something similar for the EVENT_AFTER_UNINSTALL_PLUGIN event to clean things up in the case where one gets uninstalled.


Register Assetbundle in frontend template

Answer:
In order to render all the scripts in your registered assetbundles you need to call this function

{{ endBody() }}

somewhere in your page (preferred at the end of the body)


How to create subfolders in the Assets view?

What you see on the left in the Asset section, are asset-sources and their subfolders. You can configure them in Settings -> Assets -> New Source.

In this example, Drinks and Cars are asset-sources:

You can add subfolders to each asset-source (and each other subfolder) with Rightclick -> New subfolder.


Commerce - How can I get the values of a category field from a plugin?

MyPlugin.php

$record = MyPluginRecord::model()->findByPk($user->id);

if ($record) {
    $field = $record->getAttributes(); // The plugin model.

    if ($lineItems) {
        foreach ($lineItems as $lineItem) {
            foreach ($lineItem->purchasable->product->myCategoryField as $category) {
                error_log($category->title); // My Category
            }
        }
    } else {
        // The cart is empty.
    }
} else {
    // Product not found in cart.
}


How can I render a page template from a queue job/console controller?

Given that it's craft\web\View.php I think you're probably out of luck. Though it's only registerAssetFlashes() that seems to call getSession(), but I'm expecting more than just that may assume a frontend request. You could always ping a controller endpoint via Guzzle to return the Twig-rendered HTML to you though.


How do I send PayPal the Craft Commerce Order Number?

Commerce V1 - You can use a hooks for this:

https://craftcommerce.com/docs/hooks-reference#commerce_modifygatewayrequestdata

or

https://craftcommerce.com/docs/hooks-reference#commerce_modifypaymentrequest

...basically, start by dumping all the data to look at what is being sent, then modify it as you need.  Here's an example of something similar (I truncate the data to make sure it fits the gateway's limits):

    public function commerce_modifyGatewayRequestData($data,$type,$transaction){

        # Dump the data to see what is there....
        BusinessLogicPlugin::log("commerce_modifyGatewayRequestData");
        BusinessLogicPlugin::log("data");
        BusinessLogicPlugin::log($data);
        BusinessLogicPlugin::log("type");
        BusinessLogicPlugin::log($type);
        BusinessLogicPlugin::log("transaction");
        BusinessLogicPlugin::log($transaction);

        # TRUNCATE any over-sized data eWay might not like....
        # Data limits come from here: https://eway.io/api-v3/#direct-connection

       if(isset($data['Customer']['FirstName']))
        $data['Customer']['FirstName'] = substr($data['Customer']['FirstName'], 0, 49);

       ... etc

      return $data;
   }


How can I populate a dropdown via Globals?

You could always just use a Table field, you could then just add a colour to each row, this allows you to give a descriptive name to each colour but also keep the value as the hex or whatever format your colours are in...

Then its easy to loop like:

{% for option in colorOptions.color %}
   <option value="{{ option.value }}">{{ option.name }}</option>
{% endfor %}

Of course you dont have to have two columns, you could just have the one and loop over the HEX values or whatever, it's totally up to you and how you want to do it. But personally I think the table field is a solid way to go!


Why don't you use your current entry as reference?

{% set nutritions = craft.entries.section('nutrition').relatedTo({
    field: 'restaurant',
    targetElement: entry
}).all() %}

Since your current entry is related to the entry you want to fetch this would be the besser solution. However that URL structure is not really optimal to beginn with. There are many cases it will break as soon as someone edits your entries, but filtering by slug alone is never a good idea in such a way.


Fetch target entries that have been linked through an Entries field

You first need to query for all post entries and then query for the authors that are related to a post. You can use the ids() method to get just the author’s element IDs, which is a lot quicker than find().

{% set allPostIds = craft.entries({
    section: 'posts',
    limit: null,
}).ids() %}

{% set people = craft.entries({
    section: 'people',
    relatedTo: {
        sourceElement: allPostIds,
        field: 'people',
    },
}) %}


Can't update or install plugins locally, macOS

error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version

Your local OpenSSL setup is trying to connect with an older, insecure TLS 1.0 protocol, which Github (and a lot of other sites) no longer support.

If you update it to use TLS 1.2, the error will go away.

If you're on something like MAMP/WAMP, etc. then updating to the latest version of their software is usually enough to resolve this issue.


Through Pixel and Tonic's support email, I figured out what the issue was. It turns out one of my entries didn't have a thumbnail and that was why the image was null. Stupid user error! I thought I had checked to make sure everything was good but I guess not. Thanks for the help @BradBell.


The plugin is expecting the POST parameter to be sectionId rather than opiniones. Changing the name attribute on that hidden input to sectionId should fix your issue.

The other requirements when creating entries from front-end template using Guest Entries are outlined below.

Make sure Allow guest submissions? is checked in the plugin’s settings (example.com/admin/settings/plugins/guestentries) and set a default author for the section entries will be saved to.

<input type="hidden" name="sectionId" value="3">

The value for sectionId can be determined by going to section settings in the control panel (example.com/admin/settings/sections) and clicking the name of the section you want to use Guest Entries with. The section’s ID is the last segment of the URL (e.g example.com/admin/settings/sections/3)

<input type="hidden" name="redirect" value="success">

The value for redirect is where a visitor will be redirected upon a successful submission and should be a valid path or URL.

A form field should be added for each field you want to save content to. Be sure to include any fields marked required on the entry type’s settings page (example.com/admin/settings/sections/3/entrytypes/3).

<input type="text" name="title">

title will always be the value for the name attribute for the entry’s title regardless of what Title Field Label is set to for the entry type in the control panel.

<textarea name="fields[body]"></textarea>

Custom fields will use fields[customFieldHandle] as the value for the name attribute.


Set logging path/file to use php://stderr

Pretty sure you'd need to write a plugin that has a class that extends Yii's CLogRoute and override the processLogs method with your own logic.  Something like this:

class StdErrRoute extends \CLogRoute
{
    public function processLogs($logs)
    {
        $strErr = fopen("php://stderr", "w");

        foreach($logs as $log)
        {
            fwrite($strErr, $log[0]);
        }

        fclose($strErr);
    }

}

Your plugin's init() method would register the route via craft()->log->addRoute().


OP solved it with using the correct block names.


Ignore Twig's variable and transform it to Polymer's variable.

You forgot to remove the outer single quotes in your first example

<iron-media-query query="(min-width: 600px)" query-matches="{{'{{queryMatches}}'}}">

Try the raw filter if that doesnt work. More info on it here


Something like this should work:
{# Setup an arry of the field handles you're interested in #}
{% set fields = ['num1', 'num2', 'num3'] %}

{# set some defaults #}
{% set totalAmount = 0 %}
{% set totalFields = 0 %}

{# loop through the field array #}
{% for field in fields %}

    {# grab the field's value from the entry #}
    {% set value = attribute(entry, field) %}

    {# skip over fields that were left blank, but count fields where 0 was entered #}
    {% if value is not empty and value >= 0 %}

        {# add to the running total and running number of fields with values #}
        {% set totalAmount = totalAmount + value %}
        {% set totalFields = totalFields + 1 %}

    {% endif %}
{% endfor %}

{# do some math #}
{% if totalFields > 0 %}
    {{ (totalAmount / totalFields)|number(decimals=1) }}
{% endif %}


Unable to Update Entry - Table doesn't exist

Does your database have InnoDB enabled and are all of Craft's tables using it (except for searchindex, which is MyISAM)? Sounds like maybe when you deleted the field some cascading deletes that are set on foreign keys didn't occur and that can happen if you're on MyISAM (which doesn't have foreign key support).


Have you tried something like this?

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

Or, if you want a specific image:

{% set image = entry.myImageFieldHandle[3] %}


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/


Anonymous access to controller action fails

I think your URL is wrong.

Try /index.php/actions/sitemap/sitemap/xml

Here is the section of the docs that explains URLs for plugin controllers: http://buildwithcraft.com/docs/plugins/controllers#how-controller-actions-fit-into-routing


Trying to replace an asset via replaceFile action in entry form

In short, you have the right idea, but you're not uploading any files with that JavaScript. All you're doing is sending a file id to a Craft action that expects an uploaded file as well.

You can find an insight on how to change your javascript to include file data as well here


Where can I find the robots.txt file in Craft 3?

Ok, so my hosting provider Hyperlane overwrites the robots.txt as long as you visit the file on their address. The file should be in the web/ folder and will show up when you visit it with your domain.


How do you iterate over Matrix field in a Craft 3 plugin?

If you had a Matrix field with a handle of matrix and it had a Matrix Block Type with a handle of MyMatrixBlock and it had a Plain Text field with a handle of text, this is how you'd grab it in PHP.

$entry = Craft::$app->getEntries()->getEntryById(2);

foreach ($entry->matrix as $matrixBlock) {
    if ($matrixBlock->getType()->name === 'MyMatrixBlock') {
        // Grab a text field from the block.
        $textField = $matrixBlock->text;
    }
}


No need to go through the installation process again.

I assume that /dev is a folder under your public html folder that Craft's public index.php file lives in.

So Craft's index.php lives in something like:

/path/to/public_html/dev/index.php

And what you want to do is change the site to live in:

/path/to/public_html/draft1/index.php

If that's the case, you'd just need to move anything in the public_html/dev folder over to public_html/draft1 and update your siteUrl.  That's either been defined in your craft/config/general.php file or under Settings->General in the control panel, depending on how your site is setup.

If you've defined any custom environment paths in craft/config/general.php, they might need to be updated as well.


I think the simplest solution is to use a plugin like Twig Better Sort or SuperSort. With those you would query for your entries as normal and then apply the Twig filter to sort the array.

{% set entries = craft.entries.section('mySection') %}
{% set entries = entries|supersort('natsortAs', '{title}') %}


Per Brad's answer, PHP is setting the cache control headers on the start of a new user session.

If you want to override this, Craft provides the {% header %} tag where you can set your own headers. Whether or not that affects Chrome, I couldn't say.

One other thing to note is cookies. This page mentions "prerendered or prefetched [pages] may set and read their own cookies as if you had visited them before -- even if you don’t visit the prerendered or prefetched pages after all" which seems to imply it'll use the current page's headers for prefetch requests.

If you do any cookie-based server-side tracking or analytics with Craft, that could wreck a little havoc. (Say your funnel is "load XXX page and then load the contact page." You'd be wondering why people visited the contact page but never filled it out.)

If you use prerender, it looks like Chrome will actually execute any javascript on the page as well, which would also defeat any JS based analytics packages.) Scary.

(For what it's worth, I haven't tested any of these techniques at all but it would be interesting to see what kind of mainstream traction they receive. I can see for a web app it might be more useful.)


I can't comment yet to reply directly to Mats Mikkel Rummelhoff's comment above, but just in case someone comes across this and is looking to alternate styles instead of basing it on the loop.index count.

You would do so using the Twig odd or even tests

{{ loop.index is odd ? 'odd' : 'even' }}

or

{{ loop.index is even ? 'even' : 'odd' }}

There is no loop.odd key


Combine image assets widths to get total width

Something like this should work:

{% set totalWidth = 0 %}

{% for asset in myAssetList %}
    {% set totalWidth = totalWidth + asset.width %}
{% endfor %}

{% if totalWidth > 750 %}
    Do this.
{% else %}
    Do that.
{% endif %}


Sounds like you don't have the correct PHP loading in your CLI. You should try using MAMP’s PHP Executable in your Terminal.


Best practise to use translate in admin panel

You could try if the Translate plugin for Craft CMS could help you out. You can use this plugin as a base for you own and save the values in a custom table and use the registerTranslateSources hook to return the values from your table

That would be some kind of mix between options 1 and option 2. Both are totally possible. I created such a plugin for my company too. Collect all uses of |t and check if there is a language fallback in the plugin translation file or if there is one in the default Craft translation file. If not you can mark the word as "untranslated" if yes save the record as translated. In your cp you'll display all your records with the hint if they are translated or not


Some paths and settings being ignored in config

You're close!  Those config settings are off in no-mans-land right now.  If you want them to apply across all of your multi-environment configs, you need to move them into the '*' array like so:

return array(

    '*' => array(
        'omitScriptNameInUrls' => true,
        'setPasswordPath' => 'user/set-password',
        'activateAccountSuccessPath' => 'user/activation-success',
        'autoLoginAfterAccountActivation' => true,
    ),

    '.local' => array(
        'devMode' => true,
        'userSessionDuration' => false
    ),

    '.co.uk' => array(
        'cooldownDuration' => 0,
    ),

    '.com' => array(
        'cooldownDuration' => 0,
    )
);


Possible to use REGEX Backreference in Routes

Is it simply not possible to use backrefs in the target route templates

Right now it's not possible (although might be worth a feature request).

The right hand side of the route key => value pair can currently only be a static string that is a path to the template to load or an array that defines a controller action to load.


Craft Commerce: After add to cart, redirect to specific line item

I created a simple plugin to redirect using the onAddToCart method

  craft()->on('commerce_cart.onAddToCart', function($event){

      $lineItem = $event->params['lineItem'];

      craft()->request->redirect("/commerce/item/?item=" . $lineItem->id);

  });


How do I fix a deprecation error for DateTime::year?

This is how you fix this:

{# Old code #}
{% for year in now.year .. firstEntryEver.postDate.year %}

{# New code #}
{% for year in now.year .. firstEntryEver.postDate |date('Y') %}


German locale & English CP language resulting in disappeared entries

From the docs:

  The default language that the Control Panel should be rendered in, for users that haven’t specified their Preferred Locale.

defaultCpLanguage is only applied for users that don't have a Preferred Locale set in their user profile.

Your entries disappeared because you don't have any entries created in the English locale, yet.  You might need to enable en under the Section's settings before that's an option.


Turns out there was a bug with package names that contained -s. Just fixed that, so /v1/package/craftcms/contact-form works now.


Craft folder deployment outside public folder

Dennis,

I use deploybot with Craft and easily deploy. So on your server settings you'll have this:

name: your site name

Host: ip address or domain.com

port: 22 or similar

destination path: /path/to/site_username/

in the destination path set it to be just above public and then you'll deploy /site_username/craft and site_username/pubic


New Install, Permission Errors With /storage

Disabling SELinux worked, but only as a workaround. This particular issue was on a Staging server, so disabling SEL was not a huge issue.

However, I would highly recommend not anyone else do this for a Production environment unless they really know what they're doing in terms of permissions.

Good luck.


Simple search using a single category in Craft CMS

You can build you categories dropdown using the craft.categories tag.  Your Form submission field will end up looking something like this in HTML:

<form action="search/results" method="get">
    <select name="querySlug">
      <option value="*">All products</option>
      <option value="fruit">Fruit</option>
      <option value="vegetables">Vegetables</option>
      <option value="dairy">Dairy</option>
    </select>
    <input type="submit" value="Search">
</form>

Your results page will behave similar to the example results page in the Craft docs, except, instead of passing your query to a craft.entries.search() tag, you'll want to pass it to the craft.categories.slug() tag and then hand off the CategoryModel you get back to the craft.entries tag using relatedTo():

{% set querySlug = craft.request.getParam('querySlug') %}
{% set category = craft.categories.group('productCategories').slug(querySlug) %}
{% set entries = craft.entries.section('products').relatedTo(category) %}

All categories are returned by default, so you just have to handle that in a conditional if somebody selects that option.  Here is a simple way you could do that:

{% set querySlug = craft.request.getParam('querySlug') %}
{% set category = craft.categories.group('productCategories').slug(querySlug) %}

{% if querySlug == "*" %}
    {% set products = craft.entries.section('products') %}
{% else %}
    {% set products = craft.entries.section('products').relatedTo(category) %}
{% endif %}


Are you opening those templates directly in the browser? When you do this, you are bypassing Craft and the browser does not know what to do with the Craft tags or how to access the database and will show the tags instead of the contents.

Since you have got the CP working, all should be good though. If you go to the CP and click on your bold site name at the top left in the nav,  the default homepage (or yours) should get loaded. What you actually see there is the index.html loaded into the _layout.html and you should not get any twig template tags. If you go ahead now and add some made-up segment to the address in the address field of the browser /xyz e.g., you will see the 404.html loaded into _layout.html.

To see your templates like your visitors will see them, Craft needs to kick in. This will only happen, if you enter a pages complete url in the browser, not a template location on your disk. (If you opended the templates directly, your address will look somethin like file:///Applications/MAMP/htdocs… which will not work. Visible or not, your address should always begin with http:// …)

Have you still got the default news section in the install? You could add 2 or 3 dummy news in the CP, and then navigate to those news entries as described above. The template that gets used to display a single news is news/_entry.html but _entry.html will never be part of the url. It is used by Craft to fill in the contents and your urls will be somtheing like news/dummy-news1, news/dummy-news2. Hope that makes it a bit clearer?


How can I run the Control Panel from a separate domain?

You can use the baseCpUrl config setting to tell Craft to always generate CP URLs pointing to http://admin.domain.com, and you can create a .htaccess redirect on http://domain.com pointing all requests to /admin* to http://admin.domain.com/admin/*, but it is not currently possible to get rid of the CP Trigger segment at this time. Craft relies on that to determine if the current request is a CP request.


Send email notification to admins when a new entry is created

You can do this with a simple module. You can set one up at pluginfactory.io with no controllers and a service called Email. If you're unsure about how modules work or how to set one up, I can highly recommend reading Andrew Welch's guide to creating a module in Craft 3.
In this code, I've called the module 'MyModule' but you can give it any name you like.
Update the main module file's init method to look something like this:
public function init()
{
    parent::init();
    self::$instance = $this;

    // This line calls a private method to register a new email message in Craft
    $this->_registerEmailMessages();
}

At the bottom of your main module file, add the _registerEmailMessages() function:
private function _registerEmailMessages()
{
    Event::on(
        SystemMessages::class,
        SystemMessages::EVENT_REGISTER_MESSAGES,
        function(RegisterEmailMessagesEvent $event) {
            $event->messages = array_merge($event->messages, [
                [
                    'key' => 'my-module_publish-notification',
                    'heading' => Craft::t('my-module', 'my-module_publish-notification_heading'),
                    'subject' => Craft::t('my-module', 'my-module_publish-notification_subject'),
                    'body' => Craft::t('my-module', 'my-module_publish-notification_body'),
                ]
            ]);
        }
    );
}

Note: you'll need to add the correct 'use' statements at the top of your module file, if you're not using an IDE like PhpStorm which adds them automatically:
use craft\events\RegisterEmailMessagesEvent;
use craft\services\SystemMessages;

What this function does is to add a new system email message with the key 'my-module_publish-notification', with translation strings for the heading, subject and body. This email will be accessible and editable in the CP by going to Utilities > System Messages.
Now you need to add some text to those translation strings. Your Pluginfactory module should come with everything set up for this. Look in the src/translations/en folder and you should find a file named after your module, e.g. 'my-module.php'
In this file, you can add the text that you want to be shown in this email by default:
'my-module_publish-notification_heading' => 'When an entry is created:',
'my-module_publish-notification_subject' => 'An entry has been created',
'my-module_publish-notification_body' => "A new entry has been created by {{ username }}.",

The heading is what's shown in the System Messages screen above the email, and the subject and body are exactly what you'd expect: the subject of the email, and the body text of the email.
The next step is to listen to the EVENT_AFTER_SAVE event to trigger the email itself. Add another line to your module's init method:
$this->installEventListeners();

Then add the installEventListeners() method at the bottom of the module file:
protected function installEventListeners()
{
    Event::on(
        Entry::class,
        Element::EVENT_AFTER_SAVE,
        function(ModelEvent $event) {
            /** @var Entry $entry */
            $entry = $event->sender;

            if (ElementHelper::isDraftOrRevision($entry)) {
                return;
            }

            if ($entry->isNew) {
                $this::$instance->emailService->sendEntryNotification($entry);
            }
        }
    );
}

This method adds a listener which will be called after an entry is saved, and if the entry is a new entry (i.e. it's not an existing entry being edited) then it calls the Email service's sendEntryNotification method.
In your Email service, that method would look something like this:
public function sendEntryNotification(Entry $entry)
{
    $admins = User::find()
        ->admin(true)
        ->all();

    foreach($admins as $admin) {
        $mail = Craft::$app->getMailer()
            ->composeFromKey('my-module_publish-notification', [
                'username' => $entry->author->username
            ]);

        $mail
            ->setTo($admin)
            ->send();
    }
}

This method fetches all admins, then loops through each one and sends the notification email as defined by the key that's passed into composeFromKey -  the key should match the string that we set as the key in our _registerEmailMessages function.
You can pass any sort of data through to your email. I've shown how to pass the username of the entry's author, but you can pass any data through in an array as the second argument in composeFromKey. The key of each data can be used in your email's content to show the value. In this example, the translation string for 'my-module_publish-notification_body' shows the username like this:
"A new entry has been created by {{ username }}."

You could pass in the name of the user, the creation date and any other data from the entry.
The last step is to register your module with your Craft app. The Pluginfactory download comes with a config directory with an app.php file inside. Copy the code that starts with 'my-module' - it should look something like this:
'my-module' => [
    'class' => \modules\mymodule\MyModule::class,
    'components' => [
        'email' => [
            'class' => 'modules\mymodule\services\Email',
        ],
    ],
],
'bootstrap' => ['my-module'],

Open up your Craft app.php file, which lives in the root config directory, and add the code from above. This should end up looking something like this:
return [
    '*' => [
        'modules' => [
            'my-module' => [
                'class' => \modules\mymodule\MyModule::class,
                'components' => [
                    'email' => [
                        'class' => 'modules\mymodule\services\Email',
                    ],
                ],
            ],
        ],
        'bootstrap' => ['my-module'],
    ],
];

The final step is to make Composer happy. Add your module to your composer.json's autoload object:
"autoload": {
    "psr-4": {
        "modules\\mymodule\\": "modules/mymodule/src/"
    }
}

Then run composer dump-autoload to rebuild the Composer autoload map.
That should get you almost all of the way there! You may need to update your event listener to make sure it's only calling the email method for the correct section(s) within your site.


You were so close! Instead of asking whether entry.type is equal to the array (which it isn't), you need to find out whether the value of entry.type matches one of the array items.

Here is an updated snippet that should work for you:

{% if entry.type == 'overview' %}
    {{ entry.title }}
{% elseif entry.type.handle in ['availability', 'specifications', 'sitemap', 'location'] %}
    {{ entry.title }}
{% else %}
    {{ entry.title }}
{% endif %}

EDIT:

It's important to recognize that entry.type is actually an object. When checking a value using in, the object's __toString method will not be invoked. Therefore you'll need explicitly specify entry.type.handle when testing whether the type is in an array.


Author list limited to first 100 when assigning to entry?

By default, Craft sets the limit of the ElementCriteriaModel to 100.

If you want to get all/more entries just set the .limit parameter to null or a number:

{% set allUsers = craft.users.limit(null).find() %}

{% set someUsers = craft.users.limit(200).find() %}

A list of all available parameters can be found here: craft.users


Store categories in array by level in controller

You might just have to do it yourself :)

$criteria = craft()->elements->getCriteria(ElementType::Category);
$criteria->group = 'groupHandle';
$categories = $criteria->find();
$sortedCategories = [];

foreach ($categories as $category) {
  if (!isset($sortedCategories[$category->level]) {
    $sortedCategories[$category->level] = [];
  }
  $sortedCategories[$category->level][] = $category;
}
var_dump($sortedCategories);

NOTE: I haven't tested this code, but in theory it should do the trick :)


Navigating through entries within a category when they belong to multiple

I think you are on the right track, in that you will likely need to create a separate route for the entries when they are 'featured', so that you can configure the nav properly in the template. I can think of a few ways to do this (and I'm sure there are more).

Add a custom route, adding a '/featured/' segment to the uri. Then in the entries template test for the segment, and modify the entries ('params') and the nav accordingly.

{% set isFeatured = (craft.request.getSegment(2) == 'featured') ? true : false %}
{% if isFeatured %}
    {% set params = craft.entries.section('galleries').relatedTo(categories.featured) %}
    {% nextUrl = siteUrl ~ '/featured/' ~ entry.getNext(params).slug %}
    {% prevUrl = siteUrl ~ '/featured/' ~ entry.getPrev(params).slug %}
{% else %}
    {% set params = craft.entries.section('galleries').relatedTo(categories.galleries) %}
    {% nextUrl = entry.getNext(params).url %}
    {% prevUrl = entry.getPrev(params).url %}
{% endif %}

Add a parameter to the uri (i.e. ?v=featured and test for the parameter in the template. Then pass the parameter on in the navigation.

{% set isFeatured = (craft.request.getParam('v') == 'featured') ? true : false %}
{% if isFeatured %}
    {% set params = craft.entries.section('galleries').relatedTo(categories.featured) %}
    {% nextUrl = entry.getNext(params).url ~ '?v=featured' %}
    {% prevUrl = entry.getPrev(params).url ~ '?v=featured' %}
{% else %}
    {% set params = craft.entries.section('galleries').relatedTo(categories.galleries) %}
    {% nextUrl = entry.getNext(params).url %}
    {% prevUrl = entry.getNext(params).url %}
{% endif %}

Abstract the relationship by creating a new 'featured' entries channel, with an entries field that links to the actual entry. Then the 'featured' category can link to the 'featured' entries (which are subsequently linked to the real entries). The featured template can then just include the normal entry with a variable isFeatured=true or something.

// In 'featured' entry template
{% include 'mychannel/_entry' with { entry: entry.myEntriesField, isFeatured: true } only %}

// In native entries template
{% if isFeatured %}
    {% set params = craft.entries.section('galleries').relatedTo(categories.featured) %}
{% else %}
    {% set params = craft.entries.section('galleries').relatedTo(categories.galleries) %}
{% endif %}

I'm sure there are more but one those might work.


To answer your questions:

Currently this isn't possible out of the box with Calendar. Perhaps there's some other plugin or custom module that can allow for this.
You cannot save them as Drafts, but they can be submitted as Disabled if you like. Just be sure to include the following hidden fields in your form:

<input type="hidden" name="enabled" value="0" />
<input type="hidden" name="enabledForSite" value="0" />


Cannot hide redactor strikethrough button when table plugin is enabled

The name to use to hide the Strikethrough button is deleted.


How can I place links at the bottom of the site?

It's impossible to give step-by-step instructions since every Craft site is unique and built differently.

The front-end templates for your site live in the craft/templates folder on your server.  You can change the look and layout of your site by editing those files, which are a combination of Twig and HTML.  Thanks like JavaScript, CSS files, images and assets will typically live in your site's public HTML folder.

Here's a list of learning resources to help get you up to speed on Craft and some links at the end in case you need help finding a developer.

Craft Official Docs & Support

Craft CMS docs - The official Craft CMS documentation
Craft Slack group - The Craft development team is in Slack every day along with 6,000+ other Craft developers and users from all over the world.
Craft Stack Exchange - You obviously found this one, but it is generally the best place to ask "how do I" type questions.
Official support email at support@craftcms.com

Learn Craft

Mijingo Craft Tutorials - Mijingo is our official education and training partner. They have a number of excellent free and paid resources for Craft CMS & Craft Commerce. Ryan Irelan, Mijingo's founder, also offers customized training online or on-site.
Tuts+ Tutorials - Not official but very good and low cost (most are $9).

Professional Services

Partner Match - If you need implementation help for Craft, we can match you with one of our Craft Service Partners or Craft Experts. We work with enterprise level partners, freelancers, and everyone in between.


If your look at your browser's console on that page you'll see your getting several JavaScript errors:

Which seems to indicate that some of your files didn't FTP up properly and are corrupted.  Try re-uploading your files and make sure your FTP client is set to transfer in 'auto' mode.


I'd make sure you're running the latest stable version of both the Imagick library as well as the Imagick PHP module.

It's possible that older version had bugs in them which might explain what you're seeing.


How can I prevent an account activation email from being sent to an unverified user who registered via a front end registration form?

After a little more work, I came up with this approach that works-

First, I unchecked the verify email addresses option in the user settings.

Then, I added another trigger in my custom module- on before user save- that sets the new user's status to pending. The two triggers combined produce the functionality I'm looking for:

    Event::on(
        User::class,
        User::EVENT_BEFORE_SAVE,
        function (Event $event) {
            if($event->isNew) {
                $event->sender->pending = true;
            }
        }
    );

    Event::on(
        Users::class,
        Users::EVENT_AFTER_ACTIVATE_USER,
        function (Event $event) {
            $users = Craft::$app->getUsers();
            $users->sendActivationEmail($event->user);
        }
    );


Is it possible to restrict access to an asset to logged in users?

There is two similar plugins that look's like it might do the job (I have tested neither).

Internal Assets
MemberAssets


ActiveRecord Primary Key - Auto increment

You don't need to define an id attribute in your Craft Records Class - it is created automatically for you on plugin install as an auto-incremented int. You do need to define the id attribute on the equivalent Model though, and AttributeType::Number is fine for that.


In your example, for entry in craft.entries.section('homepage') is attempting to loop through homepage entries as though they're an array. In Craft CMS 3, this is now deprecated just like the error message is telling you.

That means you need to use .all() when querying entries, meaning...

{% for entry in craft.entries.section('homepage') %}

...becomes...

{% for entry in craft.entries.section('homepage').all() %}

Ryan Irelan produced a nice video about this and it's also covered explicitly in the Craft 3 docs. If you've worked with Craft 2 and you're just getting started with Craft 3, that Changes in Craft 3 guide is an excellent place to start.


Craft 3 – Render template as if it were a site request

I suspect not without manually manipulating the path before the Request class is initialised.

One possible alternative solution would be to use Guzzle to call the front-end URL rather than the CP URL, which will render the template with getIsSiteRequest set to true, for example:

$client = new \GuzzleHttp\Client();
$response = $client->request('get', 'http://url/front-end-template');
$body = $response->getBody();

It would also be possible to create your own controller which fetches the template and to hit it with a front-end action request:

$response = $client->request('get', 'http://url/actions/plugin/controller/action');


The update process gets extensively logged in craft/storage/runtime/logs, so that's the place to go to start diagnosing.  You can search for [error] once you find where the update logs start and see if it reveals anything useful.

If nothing jumps out at you, you can contact support@craftcms.com and they can help diagnose the issue.


How can I make Sprout Invisible Captcha plugin works using AJAX submission?

Based on your code - and our comments - you need something like this:

<form id="someFormId">
    <input type="hidden" name="action" value="sproutForms/entries/saveEntry">
    <input type="hidden" name="handle" value="test">
    <input type="hidden" name="redirectOnFailure" value="{{ craft.request.getUrl() }}">
    <!---some more fields-->
    <button type="button" id="sendRequest" value="send">
</form>

and your javascript like

$("#sendRequest").click(function(){
    var formData = $("#someFormId").serialize();
    //proceed with your ajax request
});

reason: The Plugin checks all your variables in your $_POST request. Depending on the keys in this array it checks for spam. In your request you send none of those fields -> the plugin validation does not trigger. You can check which fields are injected with the {{ craft.sproutInvisibleCaptcha.protect() }} function and add them manually in your request as well but it's easier to just serialize the form. Maybe you have to play around with the action url. I'm used to craft 3 since two months and it could be I messed up the exact parameter in a serialize for craft2


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

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

Hope that helps.


Charge plugin not showing errors on payment fail

This is how I got the errors to show:

{% if charge is defined and (charge.getErrors or charge.user) %}

        {% if charge.getErrors %}
            {% for error in charge.getErrors('general') %}
                {{ error }}
            {% endfor %}
            {% for key, error in charge.getErrors %}
                {% if key != 'general' %}
                    {{ error[0] }}
                {% endif %}
            {% endfor %}
        {% endif %}
        {% if charge.user %}
            {% for key, error in charge.user.getErrors %}
                {% if key != 'general' %}
                    {{ error[0] }}
                {% endif %}
            {% endfor %}
        {% endif %}

{% endif %}

And then included:

Before the submit button (as they are loaded using JS)


Did you remove the standard title tag placed in the _layout.html file by craft?

This line is probably breaking your SEO title: <title>{% if title is defined %}{{ title }} - {% endif %}{{ siteName }}</title> This is found inside the <head> tags

If not you should navigate to SEO > Site Meta and take a look in the settings there. The only problem then is that you could only disable the site name being placed not the title. But I guess its the line of code you still have in your _layout


Is it possible to delete entries directly from the 'All entries' listing in the control panel?

This is currently not possible natively in Craft, but you might want to consider voting for this feature request, which is very similar.

In the meantime, you could write a custom plugin that provided its own entry index view and allowed you to delete entries any way you wished.


GraphQL mutation or query, how to target specific site?

Aha, it was merely sending in siteId as well when updating, in addition to id. If siteId is missing, it will default to primary site it seems.


Obfuscating email set by global variable

It might be that you're trying to parse the variables within quotes " as you set email so you're actually encoding {{ emailContact.emailField }} as a string, not the value it represents. Something like this should work:

{% set email = "" ~ emailContact.emailField ~ "" %}

{{ email|rot13 }}


Why do Image transformations rename the image?

Craft will use the original file name of the asset after the first request.

The first time an asset is requested, the url for the asset will not include the original file name


Best approach for Craft Commerce 3 - Variants & Custom Text

I built something similar for a client recently (in Craft, but not Craft Commerce) that required two variations (size and colour) where not all variations were compatible (ie. some sizes were only available in some colours, and vice versa).
The approach I took was to mimic Shopify’s approach. Each product has a table with three columns (size, colour, product code). I didn’t include price because it is always the same regardless of variations, but you could add that easily.
On the page Craft outputs a single select input in case JavaScript fails for any reason. Something like:
{% if entry.options|length %}

  <form class="#" action="#" method="post">

      <fieldset class="form__group">

          Options
          <select class="input--select" id="opt__opt" name="options">
            {% for row in entry.options %}
            <option class="sel__option" data-colour="{{ row.colour }}" data-size="{{ row.size }}" data-code="{{ row.code }}" value="{{row.code}} - {{ row.size }} - {{ row.colour }}">Size: {{ row.size }} | Colour: {{ row.colour }}</option>
            {% endfor %}
          </select>

      </fieldset>

      <fieldset class="form__group form__group--submit">
        <button class="button button--submit" type="submit" name="submit" id="opt__btn">Get Quick Quote</button>
      </fieldset>

  </form>

{% endif %}

Then, JavaScript lifts the data out of the select, and creates two new selects and a hidden input (the latter being for the product code). When the selects’ value changes the values in the other input are compared to make sure they are a valid combination (if not that option is disabled):
// holds the option data
let option_data = [];

// holds the inputs so can be manipulated
let size_input,
    colour_input,
    code_input;

// creates the wrapper for the select
function createSelectWrapper(parent)
{
  let sw = document.createElement("div");
  sw.setAttribute("class", "select__wrapper");
  parent.appendChild(sw);
  return sw;
}

// creates the label for the select
function createSelectLabel(parent, target, label)
{
  // create wrapper
  let lw = document.createElement("div");
      lw.setAttribute("class", "label__wrapper");

  // create label
  let lbl = document.createElement("label");
      lbl.setAttribute("class", "label");
      lbl.setAttribute("for", target);
      lbl.innerHTML = label;

  lw.appendChild(lbl);

  parent.appendChild(lw);

}

// creates a select element
function createSelect(parent, id, name, prompt, array)
{
  // create select
  let sel = document.createElement("select");

  // set attributes
  sel.setAttribute("id", id);
  sel.setAttribute("name", name);
  sel.setAttribute("class", "input--select");

  // create default
  let def = document.createElement("option");
  def.innerHTML = prompt;
  def.setAttribute("disabled", "disabled");
  def.setAttribute("selected", "selected");
  sel.appendChild(def);

  let l = array.length;
  for(let i = 0; i < l; i++)
  {
    // create option
    let opt = document.createElement("option");

    // set attributes
    let val = array[i];
    opt.innerHTML = val;
    opt.setAttribute("value", val);
    opt.setAttribute("class", "option");

    // append option
    sel.appendChild(opt);
  }

  // insert select
  parent.appendChild(sel);

  //return reference
  return sel;

}

// creates a hidden field to hold product code
function createHidden(parent, id, name)
{

  // create hidden input
  let inp = document.createElement("input");

  // set attributes
  inp.setAttribute("type", "hidden");
  inp.setAttribute("id", id);
  inp.setAttribute("name", name);

  // add to page
  parent.appendChild(inp);

  // return reference
  return inp;

}

// checks if value is unique
function getValueUnique(arr, val)
{
  let i = arr.length,
      isFound = false;
  while(i--)
  {
    if(arr[i] == val)
    {
      isFound = true;
      break;
    }
  }

  return !isFound;
}

// toggles the enabled/disabled of the submit button
function toggleSubmitBtn(willEnable)
{
  let submitBtn = document.getElementById("opt__btn");

  if(willEnable)
  {
    submitBtn.removeAttribute("disabled");
  }
  else
  {
    submitBtn.setAttribute("disabled", "disabled");
  }
}

// inits the options fields in the form
function initOptions()
{

  // grab the current input
  let orig_options = document.getElementById("opt__opt");
  let orig_options_parent = orig_options.parentNode;
  let orig_options_grandparent = orig_options_parent.parentNode;
  let orig_options_children = orig_options.getElementsByClassName("sel__option");

  // create colour Array
  let colour_arr = [];

  // create size Array
  let size_arr = [];

  // get the current data
  let l = orig_options_children.length;
  for(let i=0; i < l; i++)
  {
    const t = orig_options_children[i].dataset;
    var json = JSON.stringify({
      colour: t.colour,
      size: t.size,
      code: t.code
    })
    option_data.push(json);

    // check if it needs to add to arrays
    if(getValueUnique(colour_arr, t.colour)) { colour_arr.push(t.colour); }
    if(getValueUnique(size_arr, t.size)) { size_arr.push(t.size); }

  }

  //sort arrays (just in case)
  colour_arr.sort();
  size_arr.sort(function(a, b) {return a - b});

  // remove the old dropdown
  orig_options_grandparent.removeChild(orig_options_parent);

  // create select wrapper
  let size_wrapper = createSelectWrapper(orig_options_grandparent);
  let colour_wrapper = createSelectWrapper(orig_options_grandparent);

  // add labels
  createSelectLabel(size_wrapper, "opt__size", "Size", true);
  createSelectLabel(colour_wrapper, "opt__colour", "Colour");

  // create new selects
  size_input = createSelect(size_wrapper, "opt__size", "size", "Choose Size", size_arr);
  colour_input = createSelect(colour_wrapper, "opt__colour", "colour", "Choose Colour", colour_arr);

  // create hidden field to hold product code
  code_input = createHidden(orig_options_grandparent, "opt__code", "product_code");

  // listen for changes on the options
  size_input.addEventListener("change", onOptionChange);
  colour_input.addEventListener("change", onOptionChange);

}

// handles the disabling of an option
function disableOption(option, isSize)
{
  option.setAttribute("disabled", "disabled");
  option.innerHTML = option.getAttribute("value") + " - not available in your chosen " + (isSize ? "colour" : "size");
}

// handles the enabling of an option
function enableOption(option)
{
  option.removeAttribute("disabled");
  option.innerHTML = option.getAttribute("value");
}

// event triggered by one of the option dropdowns changing
function onOptionChange(event)
{

  // identify selects
  let triggerSelect = event.target,
      targetSelect = triggerSelect == size_input ? colour_input : size_input;

  // first set the targetSelect to all be not available
  let targetOptions = targetSelect.getElementsByClassName("option");
  let l = targetOptions.length;
  for(let i = 0; i < l; i++)
  {
    disableOption(targetOptions[i], targetSelect == size_input);
  }

  // next loop through the data to find the ones that should be turned back on
  let triggerProp = triggerSelect == size_input ? "size" : "colour";
  let targetProp = triggerProp == "size" ? "colour" : "size";

  let triggerVal = event.target.value;
  let availVals = [];

  l = option_data.length;
  for(i=0; i < l; i++)
  {
    let jsonData = JSON.parse(option_data[i]);

    if(triggerVal == jsonData[triggerProp])
    {
      availVals.push(jsonData[targetProp]);
    }

  }

  // turn the target select options back on
  l = availVals.length;
  for(i = 0; i < l; i++)
  {
    let m = targetOptions.length;
    for(let j = 0; j < m; j++)
    {
      if(targetOptions[j].getAttribute("value") == availVals[i])
      {
        // found a match
        enableOption(targetOptions[j]);
      }
    }
  }

  // see if there’s a product code
  // (triggerVal is already set)
  targetVal = targetSelect.value;

  l = option_data.length;
  for(i = 0; i < l; i++)
  {
    let jsonData = JSON.parse(option_data[i]);
    if(jsonData[triggerProp] == triggerVal && jsonData[targetProp] == targetVal)
    {
      code_input.value = jsonData.code; // set product code in hidden input
      toggleSubmitBtn(true); // enable submit button
      break;
    }
  }

}

// inits page
function init()
{
  // disable button
  toggleSubmitBtn(false);

  // init options dropdown
  initOptions();

}

// check document is ready
document.addEventListener('DOMContentLoaded', function(){
  init();
})

(Some of that might look verbose, I’ve copied and pasted from a large file and tried to strip out anything irrelevant.)
It’s not live yet, but it’s testing fine.
I think for multi-variant products it’s the simplest approach for the client, because they can update the product in a simple table and it’s reasonably idiot-proof.


You've identified the general problem. The easiest path around this issue is to upgrade to the latest of everything all at once. composer update alone will not upgrade to the latest major release, only to the latest releases defined in your composer.json. To make sure you are upgrading to the latest of everything:

Delete your composer.lock file (might not be necessary but I find it helps get around a few potential support scenarios to take this step)
Review each package in the require array of your composer.json
Update each package to the latest version release.
Once all packages are defined with their latest versions, run composer update

Regarding version constraints in your composer.json file, you'll probably want to require an explicit version of Craft and us the Caret Version Range for plugins:

{
  ...
  "require": {
    "craftcms/cms": "3.1.23",
    "vlucas/phpdotenv": "^2.4.0",
    "barrelstrength/sprout-forms": "^3.0.0-beta.57",
    "barrelstrength/sprout-email": "^4.0.0-beta.18",
    ...
  }
  ...
}

By updating to the latest versions of everything all at once, you'll ensure that the major releases of supporting modules that may be used by more than one plugin are updated and resolved together.

If you continue to run into issues you may want to try to run remove commands first (i.e. `composer remove barrelstrength/sprout-forms barrelstrength/sprout-email) or remove your entire vendor folder and go through the steps above again.

You should not need to require barrelstrength/sprout-base. Once you get the primary list if required packages in your composer.json file sorted out, the correct dependency will get resolved.


How to do multi-day events in Solspace Calendar?

The only way to handle this would be to create 2 (or 3) separate events. The limitation here is the start and end times not being the same for all 3. If they were all the same, you could have done it as an individual event.

The first two are the same time, so those could be paired together, but the Friday one would have to be separate. :)


How to search a matrix block with a specific handle?

You need to specify the fieldId of the Matrix block you're searching through.

{% set MatrixBlocks = craft.matrixBlocks()
.fieldId(123)
.type('metaData')
.search(query)
.all() %}


Plugins can be installed through the Craft control panel.

When you log in to your Craft install, you'll see Plugin Store in the left-hand menu. From there you can find and install (labelled 'Try' as you're first installing as a trial) the MailChimp Commerce plugin.

Once installed go to Settings > Plugins and enter the license you purchased.


Is there any way to create twig extension without a plugin?

Short answer:
No.
Longer answer:
Sorry, no... but there's a good reason for that.
Twig is a template language. PHP is business logic.
In proper application design, you separate the business logic from its presentation typically in some sort of MVC structure.
This sounds complicated, but it really makes your website/application a lot easier to maintain over time and keeps the different components from bumping into each other which can also improve security. (I'm looking at you WordPress where template code is often scattered over various plugins, themes, and much more.)
Plugins (and also modules upcoming in Craft 3) typically fall into the business logic category. Twig does a lot by itself but sometimes you need to write some PHP to do more—which I'm assuming you're trying to avoid.
Twig itself is obviously PHP so if you want to extend it, you need to write PHP.
What kind of Twig extension do you want to create?
The most common is a Twig filter, where a template sends Twig a value and then your code wrestles with the data and sends something back.
This is straightforward to do. If you use pluginfactory.io it will generate the necessary bootstrapping needed to get a Twig filter working.  Also check out Cameron's Spear's write up on the nitty gritty.
On the other end of the spectrum, creating your own tags (such as {% myTwigTag %}) and tests requires more work.
That should be more of a rare occurrence but it has been done (for example Marion Newlevant's Break/Continue extension).


How do I filter entries in CP by Entry Type?

There is a great plugin that allows you to add columns Craft doesn't initially provide.  This plugin also allows you to filter by those, like Entry Type.

https://github.com/mmikkel/DashCols-Craft/


How to programmatically translate an entry in craft 3

There's no clear question here, instead you've thrown up a bunch of code and said that it's not working. My suggestion would be to double check that the entry fields are indeed being populated with the appropriate translations. So double check whether the following line is actually executed.
$entry->setFieldValue($field->handle, $translations[$lang]);

If it is, then I would expect the save to work. One thing to be aware of, is that the saveElement method accepts a boolean value as the third parameter which specifies whether to propagate the entry. This is true by default, meaning that the entry is propogated for each site each time you save it. Maybe consider setting it to false or better yet, modifying your saving strategy to be more performant.
if (('en' !== $lang) && !$elements->saveElement($entry, true, false)) {

See the method signature at: https://github.com/craftcms/cms/blob/e55041d4c24ea827b13ee4367d3fae52df6ab80f/src/services/Elements.php#L746


How can I test if there are any changes in the Drag/Drop Field Layout Designer?

As of Craft 2.2, all you need to do is add a data-confirm-unload attribute to the <form> element that wraps your Field Layout Designer:

<form method="post" accept-charset="UTF-8" data-confirm-unload>

Craft will then automatically check to see if there were any changes in the form on page unload, and give the user a chance to stay on the page (same way the Edit Entry page works).


How long will it take Pixel and Tonic to release Craft 3?

I asked them on twitter regarding this yesterday and got the following answer:

  Hope to have Craft 3 in beta by the end of year.


How to get field values from Global Set using Element API plugin?

There’s no need to go through getContent() to access your field values; you can access them directly from the GlobalSetModel:

'transformer' => function(GlobalSetModel $siteSettings) {
    return [
        'defaultTitle' => $siteSettings->defaultTitle,
        'defaultDescription' => $siteSettings->defaultDescription
    ];
},