Show lineItem error(s) in template? If I have a lineItem that needs some validation, for example on an option or note filed, how do I show potential errors in a template and repopulate the form with the posted values?

In addition, I’m working with a template that has multiple products on a page and uses MulitAdd to add items to the cart.

I’m validating lineItem data using onBeforeSaveLineItem and adding the error with something similar to $lineItem->addError('text','This is required');.

To repopulate the form fields you can use the data from the post request using something like:

value="{{ craft.request.getPost( 'options.text' ) }}"

I also think I've got a rough solution for handling the errors.

//Add the cart to your event
$cart = craft()->commerce_cart->getCart();

//Check for field/fields
$text = craft()->request->getPost( 'options.text' );

//Check if it's got a value
if ( $text === '' )
{
  $event->performAction = false;

  //Build the handle we'll attach the error to
  //Use the purchasable id (variant) and then add the field we're checking for
  $lineItemErrorHandle = $item->purchasable->id . '-options[text]';

  //Add the error to the cart
  $cart->addError( $lineItemErrorHandle, 'The text is required' );
}

Then in your template you display it with the following where product.defaultVariant.id needs to be the id of the purchasable that attempted to be added to the cart.

{% set string = product.defaultVariant.id ~ '-options[text]' %}
{% if cart.getError( string ) %}
  We've got an error its {{ cart.getError( string ) }}
{% endif %}

I had to build the string outside of the .getError method because string interpolation wouldn't work using '#{product.defaultVariant.id}-options[text]'


How can I set a BOGOF (Buy One, Get One Free) promotion using Craft Commerce? Under Commerce > Promotions there are the two options: Sales & Discounts. Sales is a flat rate with little conditions, so that leaves Discounts.

Discounts have a bit more flexibility, but only seem to have a minimum quantity option. So, if I want to make a buy one get one free deal, I can't limit it to the quantity to a maximum of 2, therefore the discount gets applied to the total amount regardless of how many are added.

Has anyone else had any success setting up a BOGOF deal or anything similar?

I've used a relatively simple plugin to listen to commerce_cart.onBeforeAddToCartwhich checks what is being added to a cart and if it is a certain item it will automatically add the free item to the cart (or increment the qty if it's already in there).

(Of course you must then also listen to commerce_cart.onRemoveFromCart to remove the free item if the 'parent' item is removed...as I recall this is slightly trickier as you get a copy of the order with the item removed (there's no beforeRemoveFromCart yet) - so you jsut have to loop over the whole cart and look at it holistically).

https://craftcommerce.com/docs/events-reference#commerce_cart.onbeforeaddtocart
https://craftcommerce.com/docs/events-reference#commerce_cart.onremovefromcart

(In general a business logic plugin can implement most discount things fairly easily but I am hoping a really nice, flexible GUI driven discounts plugin thingy comes along for Commerce!).


How can I get a user to choose in which group he registers using the User Registration Form Is there a way to allow a registrant to choose a user group in the registration form?

I tried using the following code but it didn't work:

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

As far as I know there is no way to do this without a plugin. However, developing a plugin for this is not difficult. In your form you would want to add an additional drop-down, multi-select, or checkbox field with your groups listed. Then in your plugin, create a listener in your plugin's init method that listens for the users.saveUser event, and dynamically assigns the user to the selected groups based on the user's selection. For an example see this post explaining how to respond to an onSaveUser event, and this post on assigning groups dynamically.


Craft Commerce - For a discount, can we include all products in the product count but not include the sale items that receive the discount? What I would like is for a 10% discount to be applied to non sale items after the total cart quantity reaches 10 total items. I would ONLY like this discount on non sale items. Is that possible?

So, say I have 2 Products in my cart:

Item A - $20 on sale for $10, qty 9
Item B - $20, qty 1

This makes my total in cart quantity 10 line items even though 9 of them are sale items.

What I would like is for Item B to be discounted 10% making my cart total $108

( (10*9) + (20 * .9) ) = 108

As it currently stands, if I toggle Exclude this discount for products that are already on sale it doesn't count the sales items towards the total.

Based on some research, I believe a custom Adjuster is the way to solve this problem.

I created an adjuster with the following:

craft/plugins/myplugin/MypluginPlugin.php

public function commerce_registerOrderAdjusters()
{
  return [
    401 => new \Commerce\Adjusters\Myplugin_TenpercentDiscount
  ];
}

craft/plugins/myplugin/Adjusters/Myplugin_TenPercentDiscount.php

<?php

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

class Myplugin_TenPercentDiscount implements Commerce_AdjusterInterface {

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

    if (empty($lineItems))
    {
        return [];
    }

    if ($order->totalQty >= 10)
    {
        $adjuster = new Commerce_OrderAdjustmentModel();

        $adjuster->type        = 'Item Discount';
        $adjuster->name        = '10% Off Items';
        $adjuster->description = 'Your order qualifies for a 10% discount on all non sale items because you are purchasing at least 10 products.';
        $adjuster->orderId     = $order->id;

        $amountOff = 0;
        foreach ($lineItems as $item)
        {
            if (empty($item->onSale))
            {
                $amountOff += ($item->subtotal + $item->discount) * .1;
            }
        }
        $amountOff = $amountOff * -1;

        $adjuster->amount      = $amountOff;
        $adjuster->optionsJson = ['lineItemsAffected' => null];
        $order->baseDiscount  += $amountOff;

        $adjuster->included = false;

        return [$adjuster];
    }
    return[];
}

}

Does anyone see any flaws with this approach?


API Authentication for Craft CMS I'm building a system which should have two parts CMS using craft cms 2.6.x. and Yii2 for logical modules and administration. I want to login to CMS directly once the user has logged into yii2 system. How can I do that? I can pass uid which in craft_user table. I need to build an API function but where shall I start?
Plus is this a good practice or there is something else I can do.

Thanks,

You are going to have a hard time if you want to use Yii2 with Craft 2 since they are totally incompatible. Craft 2 uses yii and Craft 3 uses Yii2. You can't mix them. Furthermore you'll need multiple custom changes when you want to drop the native Craft user registration and replace it with Yii2 (less powerful) user registration. (what's wrong about Crafts one?)

Do you have certain reasons why you are not going to use existing ways? I can't think of a "fast and easy" way to achieve your goal, especially if you are new to Craft because it's a lot of work to drop existing login/registration mechanisms.

Here is an example how to register users with Craft methods https://docs.craftcms.com/v3/templating/examples/user-registration-form.html you can include the permission to give them CP access and redirect them there


My month/year archive template code no longer works in 3.2 My blog has a date archive page that uses a variation of this code. Here's the code that I've been using:

{% set allEntries = craft.entries.section('entries').type(['posts','reviews']).limit(null).all() %}

{% for year, entriesInYear in allEntries | group("postDate.year") %}

{{ year }}

        {% for month, entriesInMonth in entriesInYear|group("postDate|date('F Y')") %}

{{ month|date('M') }}

        {% endfor %}

{% endfor %}

Basically, it printed out a list of the years and months in a grid a la kottke.org's "Monthly archives" section. But after upgrading to Craft v3.2.9, the code no longer works. Instead, the page just displays a generic "Internal Server Error" message.

Looking at the error logs, the server ran out of memory trying to run the above code. If I remove the above code, then the page loads in a snap.

As a test, I streamlined the code by removing the nested grouping:

{% set allEntries = craft.entries.section('entries').type(['posts','reviews']).limit(null) %}

{% for year, entriesInYear in allEntries | group("postDate.year") %}

{{ year }}

{% endfor %}

But that still results in the same memory-related error.

FWIW, my blog has 4,100+ entries spanning 22 years, so is there just too much content for the above queries to handle? And if so, are there ways to optimize the code and/or make it less dependent on DB queries? While I can increase the server memory, I'd prefer to find a template-based solution first.

This should be fixed for the upcoming 3.2.11 release.

To get the fix early, change your craftcms/cms requirement in composer.json to:

"require": {
  "craftcms/cms": "dev-develop#87fd6ca6b0c57f524c8ccae895c13eb9724d0efa as 3.2.10",
  "...": "..."
}

Then run composer update.


Are there any benefits to using Yii's Aliases vs getenv? As part of a refactor on our code base, we have been looking into our use of Yii 2's aliases and PHP's getenv function. Here's a code example:

Alias

<link rel="icon" sizes="96x96" type="image/png" href="{{ alias("@interfaceAssetsBaseUrl/img/icons/favicon-96x96.png") }}" />

getenv

<link rel="icon" sizes="96x96" type="image/png" href="{{ getenv('INTERFACE_ASSETS_BASE_URL') ~ 'img/icons/favicon-96x96.png' }}"/>

For context, Interface Assets Base URL is set according to your environment; if you're on Dev it will point to your local files. If you're in Staging or Production it'll point to Amazon S3.

My question:

Is there any benefits in terms or performance in using either one of these methods? Does Craft cache one of these, for example?

Performance? Doubtful there is a significant difference, compared to the cost of, say, a database query. It all boils down to accessing arrays, and the number of function calls or string manipulations that it takes to get there is probably not worth worrying about.

The way I see it is that ENV vars are for configuration, and aliases are for convenience / maintainability / legibility of your application's code.

Your example approaches a good explanation for that separation. If you wanted an Environment variable to determine something like an asset volume base URL, I'd recommend this implementation:

1) Create your environment key and value.

MY_ENV_VAR="/some/local/path/or/url"

2) Create an alias, like @interfaceAssetsBaseUrl that consumes the ENV var:

<?php return [
  // Maybe a multi-env config?
  // ...
  'aliases' => [
    '@interfaceAssetsBaseUrl' => getenv('INTERFACE_ASSETS_BASE_URL')
  ],
  // ...
];

3) Use the alias in your volume configuration via the CP's auto-complete feature, i.e. @interfaceAssetsBaseUrl/icons

 I recommend this, mostly because of the nature of Alias parsing vs. manual string concatenation—the alias function is designed to be used as a substitution, and for the substitution to happen properly, irrespective of configuration. Say your ENV key changes, or you want to override it for a particular environment, or (like me) you forget to populate a variable in a new environment? This strikes me as something you shouldn't have to sweat in the template, but rather let the system take care of when it boots up.

There's an additional benefit of relying on aliases as early as possible, which is recursive evaluation. Here's a config snippet from a real project, which is used to defined a number of (local) asset volumes:

'aliases' => [
  '@shared' => getenv('SHARED_PATH'),
  '@private' => '@shared/private',
  '@fonts' => '@private/fonts'
],

(This app uses an atomic deployment strategy, so the path to those files changes regularly, but SHARED_PATH is a reliable symlink.)


How to detect white background In my product catalog, some product images need to have the css attribute object-fit: cover while some need the object-fit: contain:

I'd like to detect the white background, possibly on upload (to ease processing when live). I am currently using Imager for transforms, and I would guess that plugin could have some detection feature, but I can't seem to find it.
Reading around a little suggests reading all four corners of the image and determine white bg if all four pixels are white -- but how can I do this? (and how can I do it on file upload?)
Comments for provided solution
Craft CMS has a powerful event system that lets you attach to events and then perform further actions on the entities involved.
I created a site module based on nystudio107's template, and inserted the provided code.
Then I created the field hasWhiteBackground as a lightswitch field, and ran a console command ./craft assets/resave as documented in the Craft CMS 3 docs, to update the lightswitch for existing assets.
I am using imager to transform the images and to create srcset. I use the hasWhiteBackground to do either a fit or crop mode to the resize operation:
{% set image = p.productImage.one()|default(null) %}
{% set transformedImages = craft.imager.transformImage(image, [
    { width: 269 },
    { width: 246 },
    { width: 209 }
    ], {
        ratio: 1/1,
        position: image.getFocalPoint()|default("50% 50%"),
        mode: image.hasWhiteBackground|default(false) ? 'fit' : 'crop'
    })
%}

I then added the class contain to each image outputted:
 class="{{ image.hasWhiteBackground ? 'contain'}}">

The class simply adds the following css to the image:
.contain {
    object-fit:contain;
}

Here is what the result looks like:

Interesting problem! I took a stab at this, see if this works for your use case.
It relies on ImageMagick to check for the top left pixel color (0,0) after the image is uploaded (but will also work if you hit resave). (If you want to check the other 3 corners, you'll need more code but this should get you started.)
To use it, you'll need a lightswitch field called hasWhiteBackground on your asset volume (or change the name in the script).
The light switch gets switched to true if the top right corner of the image is white or transparent.
The "magic" is in the getImagePixelColor function.
Event::on(
    \craft\elements\Asset::class,
    \craft\elements\Asset::EVENT_BEFORE_SAVE,
    function(\craft\events\ModelEvent $event) {
            $asset = $event->sender;

    if ($asset->kind == 'image' ) {

        if  ($event->isNew) {

            $assetFilePath = $asset->tempFilePath;

        } else {

            $volumePath = $asset->getVolume()->settings['path'];
            $folderPath = '/' . $asset->getFolder()->path;
            $assetFilePath = Craft::getAlias($volumePath) . $folderPath . $asset->filename;

        }

        $img = new \Imagick($assetFilePath);
        $pixel = $img->getImagePixelColor(1, 1);
        $p = $pixel->getColor();

        if (($p['r'] == 255 && $p['g'] == 255 && $p['b'] == 255) || ($p['r'] == 0  && $p['g'] == 0 && $p['b'] == 0 && $p['a'] == 0)) {
            $asset->setFieldValue('hasWhiteBackground','1');

        }
    }

});


Routing generic urls to sections or structures urls I have a structure setup with parents and children.  Can a route be setup in the CP to route a generic url to a specific parent or child of the structure.  For example I have a blog landing page that is a child of the structure.  Can a route be setup, so when a user enters at /blog it takes them to that landing page?

What I would do is create a structure siteUrls which is responsible for page urls. Top-level entries have url of {slug}, nested entries have url of {parent.uri}/{slug}. The top-level entry with slug blog will be the entry with url /blog. Then you can have an entry type of blogLandingPage and use that entry type for this entry.

The other piece of the puzzle is getting the right template for the blogLandingPage. The trick for this is to use a feature of the twig include tag:

  New in version 1.2: The possibility to pass an array of templates has been added in Twig 1.2.

  You can also provide a list of templates that are checked for existence before inclusion. The first template that exists will be included:

So your template for  siteUrls will look like:

{% include ['_siteUrls/'~entry.type, '_siteUrls/default'] %}

And the template for entries (like this one) with type blogLandingPage is _siteUrls/blogLandingPage. Using a list for the {% include %} means some entry types can share templates, and thing fail more gracefully if you have an entry type without a matching template.


Has anyone come up with a solution where matrix blocks can be scheduled and expire? To replace my current CMS I will need to have parts of pages change without the whole page change. As I'm looking at using matrix blocks for building pages, it would seem to be easy for an editor to schedule matrix blocks...

Is there a way to do this or a better approach?

Yes you can definitely do this. The huge advantage of Craft is it's ability to customize nearly everything.

You can create a date field in your matrix block and check if the current date {{ now|date }} is lower than the date specified in the field.

If the condition is true, you'll display the matrix block if not you can either just hide it or you could create a plugin with a template variable  to delete the block.

craft()->matrix->deleteBlockType($yourBlock);


Where to use {% header %} I was running a page speed test and it came up that I'm not leveraging browser caching for images and such. I was hoping to use the header tag to solve this. https://docs.craftcms.com/v3/dev/tags/header.html#app

Where does this go exactly? In the _layout?

This is something that is best done via server-side config. The reason is that when an image is requested, for instance, your Twig code that sets the header never gets run for that request.

You can see how it is done via Nginx here: Nginx-Craft

For Apache (don't use .htaccess pls) go here: html5 boilerplate apache


Getting unknown property: craft\web\Application::GeneralConfig Sorry, rusty at PHP and learning Craft.

My code looks like this:

$url = craft::$app->GeneralConfig->siteUrl();

What am I doing wrong? I want to build a URL for some links in a plugin.

Please use a proper IDE or take a look into the source code to get to know the functions of the class. As you can see in the class reference the function is called

Craft::$app->getConfig()

However - in order to create urls you should use the UrlHelper


Auto generating slug with custom fields or ID I'm not sure I've seen this exact question, but I'm curious how I can include custom and/or auto-generated parts of a slug. It's not the URI creation, but the slug itself.

For Instance:

{slug}-{id} would turn into lorem-ipsum-dolor-sit-amet-123456 on submit

and from there on the variable {{entry.url}} would be example.com/lorem-ipsum-dolor-sit-amet-123456.

I know ExpressionEngine had an option in the Channel preferences called URL Title Prefix, so this would go along those lines, but more auto-generated than a predefined set of words.

If it changes things, this is specifically for Commerce and Digital Products at the moment.

I think for this you'd want to write a custom plugin that listened to the onSaveEntry event.

That plugin would check that the given entry is in the correct section/entry type you're interested in, pull any data from custom fields set on the entry update the slug for that entry accordingly.

I'd probably look at updating the slug directly in the database and avoid going back through craft()->entries->saveEntry() to avoid getting into an infinite save entry loop.


Selecting a subset of entries, based on a relatedTo call I have a question about relations... or maybe its also about making queries. First I want to select a set of data, and then I want to select a subset of that data, based on a relatedTo call.

If I first do this:

{% set caseEntries = craft.entries.section('case').relatedTo({ targetElement: creatorEntry, field: 'creator' }) %}

then, to get a sub-set of what I got before, do this:

{% set caseEntries = caseEntries.relatedTo('and', { targetElement: categoryEntry, field: 'caseCategories' }) %}

it seems as if the first relation is “forgotten” (note that it wont work if I dont include an “and” in the second related to call). Do I have to add the first relation in the second call? Feels like repeating myself... I want to avoid doing this:

{% set caseEntries = caseEntries.relatedTo('and', { targetElement: creatorEntry, field: 'creator' }, { targetElement: categoryEntry, field: 'caseCategories' }) %}

Second relatedTo call overrides first one. Calling it again won't add more parameters to the query.

If you don't want to repeat yourself you can always do this:

{% set creatorRelated = { targetElement: creatorEntry, field: 'creator' } %}

{% set caseEntries = craft.entries.section('case').relatedTo(creatorRelated) %}

{% set caseEntries = caseEntries.relatedTo('and', creatorRelated, { targetElement: categoryEntry, field: 'caseCategories' }) %}


Sprout SEO and Single channels I have Sprout SEO working just great on Channels. However when I go to my channels that are single entries how do I handle custom SEO on a per page basis?

With my channels I can use this code in my templates and it works great:

{% do craft.sproutSeo.meta({
id: entry.id,
default: 'default'}) %}

However when on singles the entry.id throws an error.

Variable "entry" does not exist

Do i need to create my own default for each section listed out under Singles?

EDIT AFTER MORE TESTING
This seems to be effecting just one or two of my singles.
I wrote a little more about my issue but as I was typing so was Ben with the answer :)

Thanks for the help in advanced,

John

The error "Variable "entry" does not exist means that your current page does not have a variable called entry available. This probably means the problem lies in how you have your Sections or routing set up.

If you remove Sprout SEO from this page and just try to access entry.title in your template, you will probably receive the same error. So, it will be best to troubleshoot that and confirm you have the entry variable available to the page before troubleshooting what might be going on in Sprout SEO.

Once you know the Single is setup correctly and able to display data from the entry variable, the next question would become why is the entry variable not available to the particular part of your page where you are trying to hand off information to Sprout SEO. To answer that, it would be helpful if you posted more information on what your template looks like, where outputting the entry variable to the page directly works, and where trying to pass the entry.id variable on the page throws an error.


Getting all entries from all locales Can anyone confirm if I want to pull back a list of all entries regardless of locale I could do something like craft.entries.locale(null) to get 100% of entries?

In my experience this has been problematic - instead have looped through locales to fetch entries then merged and sorted with supersort.

Eg:

{% set locales = craft.i18n.getSiteLocales %}

{% set pages = [] %}

{% for locale in locales %}
    {% set entries = craft.entries.locale(locale.id).localeEnabled(false).find() %}
    {% set pages = pages|merge(entries) %}
{% endfor %}

{% set pages = pages|supersort('rsortAs', "{ postDate|date('Ymd') }") %}

Then loop through pages as you would your entries. Am sure there is a more elegant way of doing this but this is what we had to do for our site.


Craft 3: Search in 15000 entries I have a low-budget project where would like to migrate an academic website to Craft. The website contains 15000 proverbs about women in different cultures that can be searched, (http://www.womeninproverbsworldwide.com/the-proverbs/search/index.php).

I consider making each proverb an entry. Will search perform fast enough with 15000 entries or should I consider a different approach?

Thank you for sharing your opinion!

Craft can handle such an amount of elements really well but you could get in trouble when you are going to import them all at once without changing your web server config or using certain importers since it will extend the defaults max_execution_time. So you'll have to import them step by step or via queue (or via import plugin).

I created a job center with about 60.000 elements currently and everything works smooth


Error after Freeform Pro update to 3.5.10 I am posting this here because the Freeform support form is currently not working.

I get an error when viewing /admin/freeform/dashboard and /admin/freeform/submissions, however /admin/freeform/forms is working. It shows all forms and I can click on them to see the form detail page BUT there seems to be a faulty form - the one with the most submissions. When I click on it the detail page shows up with nothing in it but the title. This request doesn't seem to throw an error in the logs like the others did.

Versions with error

Craft: 3.3.16.3 and 3.4.1
Freeform: 3.5.10

Versions sans error (current production versions)

Craft: 3.3.16.3
Freeform: 3.5.4

It is still working in production right now. The error occurred after doing an update. This is the stack trace for /admin/freeform/dashboard and /admin/freeform/submissions:

2020-01-30 17:56:11 [-][5571][osi4gj30cngblrtejvdpq77h60][error][Error] Error: Call to a member function getForm() on null in /var/www/KLR3/vendor/solspace/craft-freeform/src/Elements/Submission.php:498
Stack trace:
#0 /var/www/KLR3/vendor/solspace/craft-freeform/src/Elements/Submission.php(474): Solspace\Freeform\Elements\Submission->getForm()
#1 /var/www/KLR3/vendor/solspace/craft-freeform/src/Elements/Submission.php(737): Solspace\Freeform\Elements\Submission->getFieldMetadata()
#2 /var/www/KLR3/vendor/solspace/craft-freeform/src/Elements/Submission.php(539): Solspace\Freeform\Elements\Submission->getFieldByIdentifier('enabledForSite')
#3 /var/www/KLR3/vendor/yiisoft/yii2/BaseYii.php(558): Solspace\Freeform\Elements\Submission->__set('enabledForSite', '1')
#4 /var/www/KLR3/vendor/yiisoft/yii2/base/BaseObject.php(107): yii\BaseYii::configure(Object(Solspace\Freeform\Elements\Submission), Array)
#5 /var/www/KLR3/vendor/solspace/craft-freeform/src/Elements/Submission.php(156): yii\base\BaseObject->__construct(Array)
#6 /var/www/KLR3/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1801): Solspace\Freeform\Elements\Submission->__construct(Array)
#7 /var/www/KLR3/vendor/craftcms/cms/src/elements/db/ElementQuery.php(2809): craft\elements\db\ElementQuery->createElement(Array)
#8 /var/www/KLR3/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1453): craft\elements\db\ElementQuery->_createElements(Array)
#9 /var/www/KLR3/vendor/yiisoft/yii2/db/Query.php(238): craft\elements\db\ElementQuery->populate(Array)
#10 /var/www/KLR3/vendor/craftcms/cms/src/db/Query.php(152): yii\db\Query->all(NULL)
#11 /var/www/KLR3/vendor/craftcms/cms/src/elements/db/ElementQuery.php(1482): craft\db\Query->all(NULL)
#12 /var/www/KLR3/vendor/solspace/craft-freeform/src/Controllers/DashboardController.php(23): craft\elements\db\ElementQuery->all()
#13 [internal function]: Solspace\Freeform\Controllers\DashboardController->actionIndex()
#14 /var/www/KLR3/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#15 /var/www/KLR3/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#16 /var/www/KLR3/vendor/craftcms/cms/src/web/Controller.php(178): yii\base\Controller->runAction('index', Array)
#17 /var/www/KLR3/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction('index', Array)
#18 /var/www/KLR3/vendor/craftcms/cms/src/web/Application.php(290): yii\base\Module->runAction('freeform/dashbo...', Array)
#19 /var/www/KLR3/vendor/yiisoft/yii2/web/Application.php(103): craft\web\Application->runAction('freeform/dashbo...', Array)
#20 /var/www/KLR3/vendor/craftcms/cms/src/web/Application.php(275): yii\web\Application->handleRequest(Object(craft\web\Request))
#21 /var/www/KLR3/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#22 /var/www/KLR3/public/index.php(21): yii\base\Application->run()
#23 {main}

You'll need to update to Freeform 3.6+ (Craft 3.4+), which contains a compatibility fix for this issue. :)

https://docs.solspace.com/craft/freeform/v3/setup/changelog.html#_3-6-0-2020-01-23


Strange issue with logging out user on frontend We have a strange issue with a site we are developing. We have a user login on the front-end of the site which takes users to their profile pages. The issue we have is that on a Mac the user is being logged out randomly and irregularly on the local server.

This is odd because on PC this issue is not occurring. And what makes it even more strange is if the user is logged in as admin, whilst the user is logged out on the front-end they are still logged into the CMS?

Has anyone come across this issue before? I've tried some of the suggestions made in this post but nothing seems to be working.

Craft constantly logging out from admin area

Turned out to be a browser issue specific to one mac. And we also noticed that when using chromes device mode it seemed to kill the session!


Using imageoptimize plugin not showing field on entries In Craft CMS, I am using the imageoptimize plugin to optimize images. I have added field imageoptimize & in assets created volume added field in field layout but when I am adding entry it showing me the message 'OptimizedImages fields only work when added to an Asset Volume's layout.'.

I am using Craft CMS version 3.024 and ImageOptimize plugin version 1.4.38.
Why is the field not showing up?

So a per the documentation, the Optimized Images field should only be added to an Asset Volume's layout. You do not need to, and should not add that field to your Section field layouts.

Please verify for me that after doing so, when you double-click on an Asset, you see a display similar to this:

c.f.: https://github.com/nystudio107/craft-imageoptimize/issues/106

Resolution: This ended up just being some local config issues with Asset Volumes, and then getting rid of pending stuck Queue jobs. After that, away we go!


Whats the best way to create short URL's I have a client that wants to create a short URL to an entry for a mail shot. What would be the best way to do this. I have the Reroute plugin installed which would work (setting to a 302 redirect) but I am wondering if that is the best approach.

You could make a plugin that generates a short URL using a link-shortener API (e.g. dev.bitly) for each of your entries and provides the URL as a custom read-only Field Type.


How do I get the Settings System email Address? From a template or a plugin, how can I get the value of admin/Settings/System Email Address ?
Thanks for your help
Nicolas

You can get this in a template like so:

{# returns an array of your email settings #}
{% set settings = craft.systemSettings.email %}

{# returns the email address #}
{{ settings.emailAddress }}

And in a plugin you can do:

craft()->systemSettings->getSetting('email', 'emailAddress')


Notes about your changes On a CP edit entry page there is a box for 'Notes about your changes'.
I have entered text in there and saved the entry, but it doesn't seem show up anywhere. How does it work?

For my answer I am going to assume you are running Craft 3.

Notes are displayed in the sidebar:

When viewing the "Current" version of an entry, you can see the notes in the sidebar from when that entry was last saved.

If you go back through the version history of an entry, the notes in the sidebar will change to the notes left from the previous version of that entry.

You can also add notes to a draft version of an entry:

Once the draft has been published, the draft version will now become the "Current" version, and the notes from the draft should show in the sidebar.


Show Users who have a related entry I'm looking to show the users that have an entry relating to the currentUser, it is currently working but is obviously showing duplicates e.g. admin, admin as admin has 2x posts associated. Is there a better way of doing this and removing the duplicates? I just can't think of it.

{% set submissions = craft.entries.section('submissions').relatedTo({
  targetElement: currentUser,
  field: 'submissionBrand',
}) %}

{% for entry in submissions %}
    <li>{{ entry.author.username }}
{%  endfor %}

Changed this to be :

{% set userBrands = craft.entries.section('brands').relatedTo(currentUser) %}
{% set fans = craft.users.relatedTo(userBrands) %}

{% for user in fans %}
    <li>{{ user.username }}
{%  endfor %}

{% endblock %}


Is it possible to create a new asset from an image URL? I've created several entries from a feed from the eBay API, but I would like to add images to the entry too!

I've managed to get an array of URLs, which I would like to import into an assets field that I have on the new entries.

I saw a post from Brandon from a couple years back suggesting it was not possible to do this, unable to find anything more up-to-date.

Could I grab the image with cURL in my plugin then create a new AssetFileModel and some how sync the temp image file with Craft somehow? As a follow up question how would I add multiple images to the assets field on my new entry?

Thanks
Joe

Download the image into a temp file, then upload it as an asset. You can find $iAssetSourceId by looking at the URL when editing an asset source

$iAssetSourceId = 4;
$iAssetFolderId = (int)craft()->assets->getRootFolderBySourceId($iAssetSourceId)->id;
$sTempFilePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $sFilename;
$sFileContents = file_get_contents($sImageUrl);
file_put_contents($sTempFilePath, $sFileContents);

// Get craft to copy the temp file into the assets folder
$oAssetOperationResponse = craft()->assets->insertFileByLocalPath(
    $sTempFilePath,
    $sFilename,
    $iAssetFolderId,
    AssetConflictResolution::Replace
);

return (int)$oAssetOperationResponse->responseData["fileId"];


Update Cart’s address ID from plugin I want to try and stop Commerce 3 creating a new customer address if it’s a duplicate of an existing one.

As an example, a store selling digital products has a checkout form that simply asks for the customer’s country, no other address info is required. All we need is one select field for the country (pre-checked if we already have that info). We could add radio buttons to select any previously saved addresses as well as a field to select a (new) country. However, even if we do that, there’s still a chance the user opts to enter a new address and actually submits a duplicate.

I’ve created a plugin that catches a duplicate address and then tries to set the cart’s billingAddressId so a new address isn’t created. What I have so far though doesn’t work. It doesn’t successfully update the billingAddressId. If I use Craft::$app->getElements()->saveElement($cart) it results in a loop in this case.

How can I complete this to update the cart, or perhaps the post data in this case, with the matched billingAddressId?

Event::on(Address::class, Address::EVENT_BEFORE_VALIDATE, function (Event $e) {

    $sentCountryId = $e->sender->countryId;
    $currentUser = Craft::$app->user;
    $cart = craft\commerce\Plugin::getInstance()->getCarts()->getCart();
    $addresses = $cart->customer->addresses;
    $match = 0;

    // Only check against saved addresses if currentUser has any
    if ($currentUser and $addresses) {

        // If true, loop saved address to check for a country match
        foreach($addresses as $address) {

            // Loop until a match is found
            if ($match == 0) {

                $savedCountryId = $address->countryId;

                if ($sentCountryId == $savedCountryId) {
                    $match = $address->id;
                }

            }

        };

        // If there’s a match set the cart’s billingAddressId
        // to stop a new address being created:
        if ($match) {

            $cart->billingAddressId = $match; // This doesn’t work on it’s own
            // Craft::$app->getElements()->saveElement($cart); // Results in a loop
            $e->handled = true;

        }

    }

});

If I am reading you right you are wanting to update the shipping address on the order rather than create a new one.

You should be able to do that right now:

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

{% set flashNotice = craft.app.session.getFlash('notice') %}

{% if flashNotice %}

            {{ flashNotice }}

{% endif %}

{% set flashError = craft.app.session.getFlash('error') %}

{% if flashError %}

    {{ flashError }}

{% endif %}

//meat and potatoes:

{{ cart.number }}

Shipping Address ID: {{ cart.shippingAddressId }}

<form method="post">
  <input type="hidden" name="action" value="commerce/cart/update-cart"/>
  <input type="hidden" name="cartUpdatedNotice" value="The address was updated.">
  {{ redirectInput('shop/address') }}
  {{ csrfInput() }}

  {% if cart.shippingAddress.id %}
  <input type="hidden" name="shippingAddress[id]" value="{{cart.shippingAddress.id}}"/>
  {% endif %}

  First Name:
  <input name="shippingAddress[firstName]" value="{{cart.shippingAddress.firstName ?? ''}}"/>

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

As demonstrated above, using an input with name="shippingAddress[id]" with the address ID set as the value will update the address with that ID on the commerce/cart/update-cart controller action just fine.

You would only use name="shippingAddressId" if you wanted to let the user select an existing one.


Element API - remove response limit Using Element API I would like to remove the limit to how many entries are in the JSON output.

The default is 100 entries, with pagination. I would like 1 response with all entries.

Here is my current call:

return [
'endpoints' => [
    'fullmembers.json' => [
        'elementType' => 'User',
        'criteria' => ['group' => 'fullMembership'],
        'transformer' => function(UserModel $member) {
//....

Thanks in advance guys!

You have to set the criteria model's limit parameter to null and disable pagination.

'criteria' => [
    'group' => 'fullMembership',
    'limit' => null,
],
'paginate' => false,


I managed to make an entry type that won't let me create any entries. How can I fix it? I managed to make an entry type that won't let me create any entries.

The site is for a rock band, and the entry type in question is called "Shows". It contains five fields:

Venue
Lineup
Date
Time
Body

Date and Time are Date/Time fields, Lineup and Body are standard text fields. Venue is the field that I think is causing the trouble.

Venue is an Entries field that lets you select one from several venues, which are entries in a separate section (a Structure) called Venues.

When I enter data for a show, I'm able to select from the list of venues. When I try to save the Show, however, I get an error:

  Recoverable error: Object of class Craft\ElementCriteriaModel could not be converted to string.

The most meaningful looking highlighted line in the debug output is:

echo $this->getAttribute((isset($context["object"]) ? $context["object"] : $this->getContext($context, "object")), "venue", array());

All these pages are in the admin section, not in my templates, thus they are pages I can't edit, so I don't know what I can do to fix this error. Is it possible that I have created an entry type that Craft can't handle? There doesn't seem to be anything that unusual about the Venue entry type.

Have I done something obviously wrong? If not, how do I debug this?

It sounds like you're trying to have a dynamic title for that section, based on an Entries field (Venue). Working out the nuances in this situation can be a little tricky, but I think this would do the trick for you:

{{ object.venue.first().title ?? '(no venue)' }}

Let's break down what's happening here...

In this use-case, object is your Entry. Which means that object.venue is your Venue field (which happens to be an Entries field type).

Since Venue is an Entries field type, that means it's going to return an Element Criteria Model. When dealing with an ECM, you need to append something like find() (get all) or first() (get one) to end up with an actual, useful result.

Once we've established that you only need a single Entry (object.venue.first()), let's go ahead and grab the title of that Entry (object.venue.first().title). The good news... that can all be chained together! The bad news... it'll throw an error if the Venue field is left empty.

Which is where ?? '(no venue)' comes in. The double question mark is called a null-coalescing operator. It provides a graceful fallback, suppressing error messages and allowing you to provide a safe alternative.

Put it all together, and you'll have a nice, safe dynamic title.


Access a function in a plugin from another plugin I have some functions in another plugin that I'd like to use. For example, I'm using the shortcodes plugin, and it has a function shortcode_atts that I want to use in my plugin.

If the method you want to access is public and in another service file, you could access it via the service layer:

craft()->theOtherPluginService->method();

The method you are using above, the Scope Resolution Operator should work fine without declaring the \Craft namespace, as the plugins should already be loaded into the Craft namespace.  It's used for accessing static and constant methods:

ClassName::method()

In cases where you may be loading a library that is not in the Craft namespace, the syntax you're using above would be needed, where you declare the full namespace:

\SomeOtherNameSpace\ClassName::method()


Can't Backup Database - "Failed with exit code 1" (WAMP Server) I'm running Craft 3 on a local windows machine, but every time I try to update certain plugins (or Craft itself) I get a message "Couldn't back up the database. How would you like to proceed".

The logs give the following:

[error][craft\controllers\UpdaterController::actionBackup] Error backing up the database: The shell command "mysqldump ..." failed with exit code 1.

(I've removed most of the error code for the actual query, let me know if that would be useful).

I'm aware there are well documented hurdles with mysqldump and localhost, but I've already tried the proposed solutions here, here and here.

The global (not local, though I've tried both) PATH env has been set with the root of my MySQL folder ("C:\wamp64\bin\mysql\mysql5.7.26\bin") and I've restarted my machine, server etc. but still get the same error. I've also confirmed mysqldump is running and available in a normal terminal.

I've also tried the official guide to get MAMP working, tweaked as best I could for Windows (though I'm far from experienced so could have done something wrong) and that did nothing either.

I'm running Windows 10 (if that helps).

Any idea what exit code 1 means?

EDIT: See the comments below, but I've almost cracked this. My remaining issue is that I can't find any examples of the my.cnf file. Doesn't seem to be present on any Craft installs I have access to (though only this one has this issue), which means I can't manually generate it and Craft seems to be failing to do so. Any ideas?

Following up on your last comment:

  So I guess my final question is: how can I either manually create the required file (which is missing) or amend the query?

You can completely override Craft's default backup command by specifying it in the backupCommand config setting.

Craft will attempt to execute whatever is in there when it goes to perform a backup.


index.php in output HTML - channel pages is there are anything to do to delete index.php from output html? This is not very "clear"...

In my case its channel array and I call {{entry.url}}...
Any idea how could be this fixed / avoid?

Big TNX for any idea!

I'd suggest starting with the official docs on removing index.php from your URLS:

https://craftcms.com/support/remove-index.php

https://craftcms.com/support/why-index.php

And if that doesn't help, there are lots more environment-specific things here on Craft SE, you can check.


Craft 3 - Fixing deprecation errors I'm used to writing Twig 1 syntax, and since switching to Craft 3 (which uses Twig 2) I keep getting lots of deprecation errors in the control panel.

Is there any reference material that provides the Twig syntax I should be using instead for Craft 3 sites?

Yes, with the launch of Craft 3, Pixel and Tonic have provided some documentation on switching from Craft 2 syntax to Craft 3.


Checking for duplicates in array I'm trying to remove duplicate image objects from an array, but can't figure out why it won't find it:

{% for image in product.productGallery %}
    {% set productGalleryArray = productGalleryArray|merge([image]) %}
{% endfor %}

{% for variant in product.variants %}
    {% set variantImage = variant.productVariantImage.first() %}
    {% if variantImage|length and variantImage not in productGalleryArray %}
        {% set productGalleryArray = productGalleryArray|merge([variantImage]) %}
    {% endif %}
{% endfor %}

{% if productGalleryArray|length %}

        {% for image in productGalleryArray %}

        {% endfor %}

{% endif %}

Essentially what I'm trying to do is have a productGallery field which you can add images to, but also add an image to the product variant via productVariantImage.

This can cause the issue where by theres duplicate of the same image being output from the slideshow after it's merged the two fields together.

So I need to ideally:

Remove the duplicates before the array is merged, OR
Find and remove duplicates after the array is merged (Via a Twig filter?)

I think you're checking correctly with {% if variantImage not in productGalleryArray %} (this should check if variantImage isn't already in the array), but your code doesn't seem to set a productGalleryArray array in the global scope. Variables initially set inside for loops are scoped only to that for loop.

So I think you just need to declare an empty array at the top of your code outside and before the first for loop:

{% set productGalleryArray = [] %}


How to get IP address without a plugin? I just want to run a couple of conditionals depending on a visitor's IP address. How would I do that?

From PHP:

if (craft()->request->getIpAddress() == '255.255.255.255')
{
    ...
}

Note from the docs that this is a "best guess" and can easily be spoofed, so you shouldn't use it to hide any sensitive information.

We currently don't expose that method at the template level, but we probably should. I'll add that on the Craft 2.3 branch.

If you want to hack it in before then, you can open up craft/app/variables/HttpRequestVariable.php and add:

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

Then from a template you'd use:

{% if craft.request.getIpAddress() == '255.255.255.255' %}
    ...
{% endif %}

Just note that there is a chance that a 2.2 update could overwrite that file at any time.

UPDATE: No hack is required since Craft 2.3.2615.


Customize markup of email select field in Sprout Forms I'm trying to customize the markup of an email select field in Sprout Forms, however, when I customize the markup with Custom HTML, as specified in the documentation, it outputs the actual value rather than purging it to hide them from spam bots. I just need to know what I should include in the options to purge these. Here is the code I've been using.

{% set recipient = form.getField('emailRecipient') %}
<select name="fields[{{ recipient.handle }}]">
  {% for option in recipient.settings.options %}
  <option value="{{ option.value }}" {% if option.value == value.value %}selected{% endif %}>{{ option.label }}</option>
  {% endfor %}
</select>

Any help would be greatly appreciated!

Thanks.

This feature has been added in Sprout Fields v2.1.0

You can now use the Email Select field via custom HTML and obfuscate email addresses. I've added an example in the docs:

{% set contact = craft.sproutForms.form('contact') %}

{% set emailSelectField = contact.getField('emailSelect') %}

<select name="fields[{{ emailSelectField.handle }}]">

  {% set value = contact is defined ? contact.emailSelect.value : null %}

  {% set options = craft.sproutFields.obfuscateEmailAddresses(emailSelectField.settings.options, value) %}

  {% for option in options %}

    {% if value != "" %}
      {% set selected = (option.selected == 1) ? true : false %}
    {% else %}
      {% set selected = (option.default == 1) ? true : false %}
    {% endif %}

    <option value="{{ option.value }}" {% if selected %}selected{% endif %}>{{ option.label }}</option>

  {% endfor %}

</select>


Login attempts limit We've just had a security review of a client's site and one main issue raised is that a brute force attack would reveal the password if the username was guessable (ie "admin").

Could a limit on the number of login attempts be a possible feature in the future? Failing that does are there any suggestions for how we could best protect against this sort of intrusion with the current version of Craft?

Thanks!

There are several config settings which should help...

maxInvalidLogins - The number of invalid login attempts Craft will allow within the specified duration before the account gets locked.
invalidLoginWindowDuration - The amount of time to track invalid login attempts for a user, for determining if Craft should lock an account.
cooldownDuration - The amount of time a user must wait before re-attempting to log in after their account is locked due to too many failed login attempts.

In addition, you may also want to change your cpTrigger from the default.

Lastly, there is a feature request to strengthen password requirements, where Brandon Kelly posted a link on how to enforce your own stricter password requirements.


Sprout Forms file upload Any thoughts why a Sprout Form is not saving a form submission that contains a file.

Not throwing any errors and on submission the user is returned to the form.

This is a local build - I thought it might be a permissions issue with the file upload but nothing in logs, which seem to suggest that the entry/submission is being saved (event is valid, element saved).

However, nothing is being saved in the back-end and the file is not being uploaded to the allotted directory.

Confused - any help appreciated.

For the record, the issue, as determined from the comments above was that the Asset field settings did not allow the type of file that was being uploaded.

Updating the Asset field settings to allow the uploaded file type solved the issue.


Is there a way to localise error messages in Craft Commerce 2? I'm using Craft Commerce v2 on a Craft 3 Pro website.

The checkout process of the shop includes a number of forms such as the ones that visitor can use to add or edit shopping and billing addresses. All the templates are based on the demo templates for Craft Commerce and I haven't created any of those input fields using Settings > Fields.

When an error occurs in one of those forms, an error message is displayed as expected under each input field. However, the error messages are not fully localized. For example, if a field is required and the form is submitted without a value for that field, then the error message appears localised except for the title of the required field.

In this error message: The field "First Name" must not be empty.

the name of the field, First Name, is not localized.

e.g. Το «First Name» δεν μπορεί να είναι κενό.

How can I localize the «First Name» part?

User accounts’ First Name field isn’t required, so I’m guessing this is a custom field?

You can translate your custom field names using static message translations.

Create a new file at translations/<Language>/site.php:

my-project.test/
├── config/
├── ...
└── translations/
    └── <Language>/
        └── site.php

(replace <Language> with your language ID, e.g. el.)

Then have it return an array that translates your custom field names.

<?php

return [
    'First Name' => 'Ονομα',
];


Create nested Category via PHP In a custom plugin, I'm able to easily add a new Category:

$category = new CategoryModel();
$category->groupId = 1;
$category->getContent()->title = $categoryName;
craft()->categories->saveCategory($category);

That part works great! But for some reason, I can't figure out how to save a nested category.

I'd imagine it's something like this, but it doesn't seem to be working:

$childCategory = new CategoryModel();
$childCategory->groupId = 1;
$childCategory->level = 2;
$childCategory->parent = $parentCategory->id;
$childCategory->getContent()->title = $childCategoryName;
craft()->categories->saveCategory($childCategory);

Unfortunately that code is giving me an error:

  Internal Server Error

  Trying to get property of non-object

And if I drop the ->id portion of this line:

$childCategory->parent = $parentCategory;

... it will save the category, but it won't be nested.

What am I doing wrong?

Assuming $parentCategory is an existing CategoryModel (your error says that $parentCategory is not an object):

$childCategory = new CategoryModel();
$childCategory->groupId = 1;
$childCategory->newParentId = $parentCategory->id;
$childCategory->getContent()->title = $childCategoryName;

craft()->categories->saveCategory($childCategory);

You can see how Craft does it for it's own categories in CategoriesController->saveCategory() and CategoriesService->saveCategory().


Images in Matrix field First time working with Craft. I've got a Matrix field for team member profiles, title, name, image, etc.

I've got all the info pulled into the page but can't get the image. Here is my code, any help is appreciated!

{% for block in entry.teamMember %}

{{ block.memberName }}

{{ block.memberTitle }}

hometown: {{ block.memberLocation }}

{% endfor %}

You need to loop through the assets as follows:

{% for asset in block.memberPhoto %}
    <img src="{{ asset.url }}" alt="{{ block.memberName }}" />
{% endfor %}

See https://craftcms.com/docs/assets-fields for more info.


What is the proper way to set `currentSite` in a CP template when managing multi-site stuff in Craft 3? In some of our plugins, we have added multi-site support. For the most part, things are working fine but I'm feeling a bit confused about the correct way to set the currentSite in the Control Panel.

On the front-end, I would expect currentSite could be determined from the Base URL matching that of the request.

On the backend, since all admin behavior is managed from the same base domain, there is nothing specific about the request that would indicate the request is for a certain site so using something like Craft::$app->getSites()->currentSite seems to always give back the same answer.

Does Craft define the currentSite on backend requests? What role should a plugin take in defining the currentSite? Should we go as far as creating the variable for current site itself or could we just set the current site using something like Craft::$app->getSites()->setCurrentSite() and expect Craft to do several things for us?  Should we expect different behavior of the currentSite variable when working with Elements vs other components?

Understandably, these are some broad questions. I'm mostly just looking for a better framework to understand what assumptions Craft makes about multi-site on the CP side of things and how we can best plan our code to take advantage of what is already there instead of reinventing the wheel.

Maybe this is some kind of broad answer too but when you take a look at your CP requests you'll see: the siteHandle is mostly passed as a variable in nearly every request.

As for the URL routes the last segment is always the siteHandle: By default, the current site will be used in every query but as soon as Craft recognizes a bodyParam siteId/criteria['siteId'] (for example in your baseElementIndex) or in the URL route it uses that site for all ElementQueries.

In your CP there is no such a thing like currentSite (at least it is not really used for important logic) instead the current site is determined like

{% for siteId in groupSiteIds %}
    {% if siteId == entry.siteId %}
        // currentSite = entry.siteId
    {% else %}
        // not the current site
    {% endif %}
{% endif %}

When I started I used the same method to check for a parameter siteId within the bodyParam criteria. My plugin routes are always something like this

$event->rules[] = [
    'pattern' => 'form-builder/elements/<action:>/<id:\d+>/<siteHandle:\w+>',
    'route' => 'form-builder/element/<action>',
    'defaults' => [
        'id' => null,
        'siteHandle' => Craft::$app->getSites()->getPrimarySite()->handle,
        'action'    => 'index'
    ]
];

if no siteHandle is passed I use the Craft::$app->getSites()->getPrimarySite()

craft\controllers\EntriesController

public function actionEditEntry(string $sectionHandle, int $entryId = null, int $draftId = null, int $versionId = null, string $siteHandle = null, Entry $entry = null): Response
if ($siteHandle !== null) {
    $variables['site'] = Craft::$app->getSites()->getSiteByHandle($siteHandle);

    if (!$variables['site']) {
        throw new NotFoundHttpException('Invalid site handle: '.$siteHandle);
    }
}

later on, when there is really nothing at all it just assumes the following

if (empty($variables['site'])) {
    /** @noinspection PhpUnhandledExceptionInspection */
    $variables['site'] = Craft::$app->getSites()->getCurrentSite();

    if (!in_array($variables['site']->id, $variables['siteIds'], false)) {
        $variables['site'] = Craft::$app->getSites()->getSiteById($variables['siteIds'][0]);
    }

    $site = $variables['site'];
}

TL:DR

Craft doesn't use the currentSite that much in the CP, it is determined by the current element.siteId or the siteMenu (in js). As soon as you enter the CP the selected site in the menu will be stored in your Javascript session, when you click on create new entry you'll get routed to the site that is specified in your session. I think it's best to manage it the same way


How do I write a conditional for whether a section contains entries? I've got a blog section on a page but want to hide it if there are no blogs.

{% set blog = craft.entries.section('blog').limit(5) %}
{% if blog !='' %}
  <div class="something">
    {% for entry in blog %}
      ...
    {% endfor %}
  </div>
{% endif %}

Your blog variable is an ElementCriteriaModel, which can be treated like an iterable array:

{% if blog|length %}
    Content
{% endif %}


How to enable changing the status of my own Element Type So i have created my own Element Type and enabled statuses with the hasStatuses() method.

In the ControlPanel its shown that each element has its own status, but it cant be changed. See image below:

The checkboxes are missing to select the desired entry and then disable or enable it. Im crawling through the source code but i am not able to find a clue on how to enable this feature.

That's because those buttons are ActionButtons and they have actually nothing to do with statuses in general since the status is not a single field value nor is there a value in the DB that's called status. For example: the status of an entry depends on several different attributes like expiryDate or the date when it's published so there is no way to set those statuses via button there.

You'll need to register the action buttons for your element type. Just take a look at an entries defineActions function.


Attribute default value is always being saved as an int Following block in generateColumnDefinition($config) function in DbHelper.php class is always converting default value to int if it is a numeric value. A numeric value can be a float so this is causing an issue:

if (isset($config['default']) && (is_string($config['default']) || is_bool($config['default']) || is_numeric($config['default'])))
{
    if (is_string($config['default']) && !is_numeric($config['default']))
    {
        $def .= ' DEFAULT "'.$config['default'].'"';
    }
    else
    {
        $def .= ' DEFAULT '.(int)$config['default'];
    }
}

Is there a way that I can override this method? Any help much appreciated.

Many thanks

You can’t override that method from a plugin, if you really think it should be changed I’d suggest to file a feature request.

As a workaround you could try to set a default manually before the attribute is stored to the database or before the value is used. If it’s for a custom field type you could look into utilizing prepValue or prepValueFromPost for example.


HighRes image crop tool (not Aviary plugin) Is there an alternative to the Aviary plugin (https://github.com/fruitstudios/AviaryImageEditor)?

I only need to be able to offer the client a visual interface for cropping (in 1 aspect ratio only).
While Aviary works great, it's limited at the moment to low res images and I really need high res images as I use them full screen.

Does anyone know an alternative?

Aviary is pretty heavy handed. I agree with you, it'd be great to have some tools in Craft to handle cropping. There's been some rumblings in the past of Craft 3 shipping with some new toys but that's a ways away. I don't know of any alternative but you could certainly build your own.

Use something like Jcrop then use some tools server side to crop the actual image. That's relatively straightforward.

Implementing it in Craft is the other tricky piece. Wonder if anyone else in the Craft community has tried doing something like this?


Getting total rows from current Super Table within Matrix Block (too many rows) This is my first site in Craft CMS (coming from EE) and trying to get my head around a few things.

I've got a matrix field that defines various types of content - one of which is a 'Super Table' field. In this 'Super Table' field, I've got one particular field called 'linkText' which I'm trying to pull through. I did successfully BUT it came through 19 times. I've been trying to suss out where this number comes from.

I have a matrix field called 'pageContent'. The particular Block Type I'm looking at is called 'textBlock'. Within that block I then have a rich text field called 'Text' which pulls through perfect i.e. once as expected. I then have the troubled 'Super Table' field called 'callToAction' which has a linkType and linkText in there.

{% for block in entry.pageContent %}

    {% if block.type == "textBlock" %}

        {% for row in block.callToAction %}

             <p>{{ row.linkText }}</p>

        {% endfor %}

    {% elseif block.type == "customTable" %}

        etc, etc,

    {% endif %}

{% endfor %}

I'm sure it's something stupid, it is late! ;) but been checking the docs for a while and can't solve it. Any help would be great.

In this case it was pulling through 19 rows only when 'Static Field' was selected. Everything was fine otherwise, possibly a way that the data was being pulled.


Should the initial selected subfolder in the 'Add an asset' dialog be the Default Upload Location? Using Craft Pro 2.2.2601, I am finding the asset selection mechanic a little confusing from a UX perspective when I am using {slug} in the asset field's 'Default Upload Location' to place assets in correct subfolders.

When I drag assets directly onto an asset field, it places the asset in the specified subfolder correctly. However, when I click on the Add an Asset button, the asset dialog opens with the root asset folder selected, so clicking the Upload files button causes the file to be added to the root folder by default. When this happens, the {slug} subfolder is generated anyway, with no contents.

It would make more sense to me if this generated subfolder was automatically selected so that the asset is placed in there. Having to navigate down the directory structure manually is a bit annoying and fiddly, and if a client forgets to do this then they are left with an empty {slug} subfolder.

Is this a bug or intended behaviour?

The Default Upload Location setting only applies to drag-n-drop uploads, as well as front-end uploads if you are creating your own entry form with a <input type="file"/> input.

If you want to force files to be placed in a {slug} subfolder, you should check the "Restrict uploads to a single folder" checkbox in your Assets field's settings.


Paginate events without setting expiry date Is there a way to paginate events entries without setting an expiry date for each entry? So I currently have:

{% paginate craft.entries.section('eventsEntry').limit(3).orderBy('eventDate') as pageInfo, upcomingEvents %}

{% for event in upcomingEvents if event.eventDate >= now %}
...
{% endfor %}

But this will include past events in the loop but not show them when output. Which results in empty 'slots' on the first page.

You need to include an extra parameter in your initial query to only fetch event entries for events in the future and remove the conditional test from the for loop.

Currently you are fetching all event entries and paginating those, but only displaying event entries where the eventDate is in the future.

Try this instead:

{% paginate craft.entries.section('eventsEntry').limit(3).eventDate('>=' ~ (now | date('Y-m-d H:i'))).orderBy('eventDate') as pageInfo, upcomingEvents %}

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


Do assets have to be below public_html to access them in redactor? I love that Redactor lets you choose existing entries, categories, and assets!

I am trying to set up a 'Files' source whose File System Path would be something like

/home/hostingaccount/craft/files

where index.php is at

/home/hostingaccount/public_html/index.php

But what source URL can point to that file location? It doesn't like http://mydomain.com/files. Or am I all wrong, and the assets have to be in something like this:

/home/hostingaccount/public_html/files

Your Asset source file system path needs to be under your public_html folder, so your 2nd example is correct: /home/hostingaccount/public_html/files.

Then your corresponding Asset source URL would be http://mydomain.com/files.

Correct that, then Asset related functionality (including Redactor's) should start working.


Accessing disabled matrix blocks in a plugin I've got a plugin which needs to be able to access all the blocks in a matrix - regardless of if the blocks are enabled or disabled.

The entry objects returned by the likes of craft()->entries->getEntryById only seem to include blocks in the matrix fields which are enabled.

Is there a way to access this information through a service - or do I need to work out how this is handled in the control panel (which does show disabled matrix blocks)?

Something like this should do it:

$criteria = craft()->elements->getCriteria(ElementType::MatrixBlock);
$criteria->status = null;
$find = $criteria->find();

Setting status to null will return all elements regardless of their status.


Can the Entry URL Format of a Channel intersect with the Entry URL Format of a Structure? Can the Entry URL Format of a Channel intersect with the Entry URL Format of a Structure? For example I have a Structure like this-

Top-Level Entries: products/{slug}

Nested Entries: {parent.uri}/{slug}

Can a Channel Entry URL Format be set so that the scenario below is possible?

products/shirts/reviews <-- review list page
products/shirts/reviews/{slug} <-- individual review

Where the review list page is in the structure? Yes. This works just the way you would expect. Set the review channel url to products/shirts/reviews/{slug} and it will do just that.


Creating default object from empty value - Sprout Email I've only recently started using Craft, so please excuse me if this is an obvious or silly question, but I've run into this problem when trying to create notifications on Sprout Emails (triggered by a Sprout Form).

The error is as follows:

PHP warning

Creating default object from empty value

/var/www/craft/plugins/sproutemail/services/SproutEmailService.php(506)

...
505         // Add a few additional variables to our info table
506         $event->params['variables']['info']->deliveryStatus = $deliveryStatus;
507         $event->params['variables']['info']->message        = $message;
...

I'm really not sure how to fix this - each form worked perfectly before, and it's only since I started using Sprout Email twenty minutes ago!

If I've not included enough code please tell me and I can include more!

Thank you

This error can be triggered when you try to use a variable name in your Sprout Email template or fields that is not present in the submitted Form data.

One example where this can occur is if you try to reuse the example Welcome Notification email provided in Sprout Email and don't update the {email} or {username} values in the HTML and Text body fields.

I'll add it to our list to revisit the error thrown here, as it's not clear at all what is happening from the error provided in the message you received above.


Show entries by a certain category AND certain lightswitch Just thinking of an easy way for the user to manage products on a site.

Therefore is it possible to display entries by a category and a lightswitch toggle?

IE. Category selected is Photocopiers and Lightswitch is color?

Then check for those and display the entries? Stuck on the syntax:

{% set category = craft.categories.slug('copier') %}
{% set colortype = craft.entries().section('products').color(true).all() %}
{% set entries = craft.entries.section('products').relatedTo(category).find()

*Stuck on the syntax here to add to the query the lightswitch field*

 %}

    {# If there are any entries, loop through them #}
    {% if entries | length %}

        {# For each entry we find, do this #}
        {% for entry in entries %}
            <p>{{ entry.title }}</p>
        {% endfor %}

{% endif %}

TIA :)

You can add the lightswitch filter to the entries query like so:

{% set category = craft.categories.slug('copier') %}
{% set entries = craft.entries()
                      .section('products')
                      .relatedTo(category)
                      .color(true)
                      .all() %}

{# If there are any entries, loop through them #}
{% if entries | length %}

    {# For each entry we find, do this #}
    {% for entry in entries %}
        <p>{{ entry.title }}</p>
    {% endfor %}

{% endif %}


Can we log both access & content changes? If we implement CraftCMS, we would need logging of both access and publishing (both successful and failed attempts). The ability to change/remove those logs should not be available to those using the CMS. Is this available in core?

Craft does some basic logging in the form of when an entry was updated/created and the last time a user logged in and from what IP address, etc.  If you want anything more detailed than that, you'd need a plugin.  There is one called Audit Log that might do what you're looking for.


Craft Commerce: Geo Location, and option select dropdown to switch between currencies I would like the user to be able to switch between currencies (and tax), but I haven't figured out how to pull the currencies defined in Craft Commerce into a for loop. (I am pretty new to this platform)

I need this to be changeable on the front end via both a drop down menu in the header and automatic Geo locating.

Any suggestions greatly appreciated :)

The Cart's currency {{cart.currency}} is always the default base currency. You can't change or switch this currency.

This means all prices/discounts/taxes are all stored and entered in that currency.

In the example templates, the price of items is shown in that currency using the currency filter and passing in the cart.currency like so:

Sale Price Subtotal: {{ item.subtotal|commerceCurrency(cart.currency) }}

When the user goes to pay for the order, we allow them to pay in a different currency called the paymentCurrency this is accessible with {{cart.paymentCurrency}}. You can also change the cart's current payment current with the controller action commerce/cart/updateCart and passing a valid currency ISO. The ISO you pass must be a valid one set up in your settings, along with a conversion ratio.

We don't recommend converting the price of all items with the payment currency using the currency filter, as the total will not match the order amount. We recommend only converting the actual payment due or order total.

In addition to changing the payment currency with controller action here are the other ways you can change the payment currency: https://craftcommerce.com/docs/payment-currencies#switching-currencies

By default the payment currency is the default currency of the system.

As for taxes, these are always based on the shipping address. So the only way to switch the tax rate would be by the customer changing their shipping address on the cart. FYI there is an option to base the tax calcs on the billing address:

https://craftcommerce.com/docs/configuration#usebillingaddressfortax

Hope that helps.


How can i detect that an asset has been published from a plugin? So, i must write this Craft plugin to index documents as soon as they are published, though a special publication field used amongst different pages templates (including a matrix field !).

How can i use the publish event to detect that this special kind of assets (assets belonging to a special directory) have been used ?

You could use the entries.onSaveEntry event to get entries as they're saved, check the publish date, then loop through its properties, and check the paths on any assets.

You'd probably also want to hook into entries.onDeleteEntry to de-index any assets from entries that get deleted.

Please note: this won't auto-index entries that were scheduled to publish at a later date.


Specific entry type in entry field This seems like an easy question to answer but I can't seem to figure it out... I'm sure we could do it in Craft 2. Anyway, I'm trying to figure out how to only display a certain entry type in an entry field. For example, I have an entry field labeled "Authors" and in my articles section I want to select and entry from that field to relate to an article, however if I add an entry field to the article entry type, all my entries can be selected... how do I limit it to only the entry type "authors"?

Sorry, it was a dumb question, I just missed the greyed out checkbox with sources in the field settings.


FileLogRoute.php maximum size error When saving a whole bunch of users through a plugin I get the following error:

Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 1835217 bytes) in /home/butpanel/public_html/cmgg/craft/app/etc/logging/FileLogRoute.php on line 59

The users are saved none the less.

Looks like your php.ini file's memory_limit is set to 64M.  Bump it up to something higher like 128M or 256M.


Importing Entries from Craft 2 to Craft 3 with Feed Me - find/replace twig filter issues I'm migrating content from an old Craft 2 site (2.6.2985) to a new Craft 3 site. Cannot do a strait upgrade for various reasons.

I've started to set up an XML feed to use Feedme with. I am running into one issue where the body redactor field has a lot of images in it and I want do a find and replace to remove the absolute url and replace with a relative url so images will load on the front end.

My code:

<root>
{% for entry in craft.entries.section('articlesAndExcerpts').status('not foo').limit(1) %}
  <entry>

    <title><![CDATA[{{ entry.title }}]]></title>

    {# more fields here #}

    <body><![CDATA[{{ entry.body|replace('src="https://memoirs.azrielifoundation.org/': 'src="/'}) }}]]></body>

  </entry>
{% endfor %}}
</root>

With without the |replace('') filter everything is good. However when I use the replace filter it replaces the url as expected but at the same time creates a lot of html entities, like this:

How can I prevent this from happening and still do a find/replace on the image urls?

Also run the content through the |raw filter to get rid of the html entities. I believe that it works without the replace filter because once that is run, twig is parsing the provided text.


Workflow when you only have FTP access? I'm building a simple low traffic website in Craft for a customer whose web server only allows access over ftp. How would you have done the work in practical terms? Building the site locally and then copy everything manually over FTP when done?

Is it for this kind of websites completely wrong to make minor adjustments directly on the server?

I absolutely agree with "build locally and then upload it".

Deploybot can deploy with ftp, and makes it quite painless to keep local and remote and git repo all in sync.

I make debugging sorts of changes directly on the server (tweak the config, tweak .htaccess, that sort of thing), but any change I want to keep I make sure to get into git.


Structure Entry Item's Parent I need to create a hierarchy that looks something to the effect of

parent 1

parent 1 child 1
parent 1 child 2
parent 1 child 3

parent 2

parent 2 child 1
parent 2 child 2
parent 2 child 3

and so on. My initial thinking was to use the matrix field type....a lot but, unfortunately, a matrix field can't have child fields of matrix field, and each child will need a matrix due to not knowing how many items each one could have as well as keeping them all together and allow for easy reordering.

The other requirement is that a "child" can't exist without a "parent", so I essentially only want to allow a user to create a "parent" at the root level and "child" types under each parent.

Is this even possible in Craft? If it is, could you point me in the direction of where it's documented as I can't seem to find anything that resembles what I'm trying to achieve.

Thank you in advance

I came up with a less than ideal workout, but it's still somewhat of a solution

I first created a channel to contain all of the "child" entry types. This allowed each one to have a matrix property to account for the dynamic creation of the "child" fields

I then created another channel with an entry type that also contains an Entry select field which allows for unlimited selection of Entries of the "child" types


Front end entry form drop down field I'm trying to update a drop down field type on an entry using a front end form. However, the input is a hidden field (not a select)

This is the form code I have so far:

<form method="post" accept-charset="UTF-8">
    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="entries/saveEntry">
    <input type="hidden" name="redirect" value="questions/{{ selectedSession }}">
    <input type="hidden" name="enabled" value="1">
    <input type="hidden" name="sectionId" value="{{ question.sectionId }}">
    <input type="hidden" name="entryId" value="{{ question.id }}">
    <input type="hidden" name="fields[questionStatus][]" value="answered">
    <input type="submit" value="submit">
</form>

Where questionStatus is my dropdown field handle.
Any insight would be appreciated
Thanks

Update

I have ensured that the account's user type has the necessary permissions to perform this operation

I think you just need to get rid of the [] on the field name, so your field becomes:

<input type="hidden" name="fields[questionStatus]" value="answered">

As long as the value is the same as the value in the dropdown, it should change.

I think the issue you are getting is because you are telling the field that its value is an array, where you just need to pass the actual value as a string and Craft will figure out the rest :)


List Modules in Craft System Report Plugins and application information is detailed in the system report. Is it possible to get my module to list itself there? And if not, is there somewhere else in the CMS to see modules that a registered and working?

This isn't something that is built-in, probably the cleanest way would be to write a plugin that adds a Utility that lists your installed modules.

You can see all of the installed modules via:

Craft::$app->modules

...but keep in mind that this will include all of your plugins as well, since they are modules, too. You can exclude them by doing something like:

foreach (Craft::$app->modules as $module) {
    if (!is_a($module, Plugin::class)) {
    // This is some kind of module that isn't a plugin
    }
}

Note that this will include other things like the yii\debug\Module and even craft\web\Application too.

If you want to see just the loaded modules for the current request, you can do:

Craft::$app->loadedModules

(not all modules may be loaded for each request)


How to remove entries with craft migration safeDown elegantly I am migrating some entries into Craft for the first time and my saveUp is quite simple where I use the saveElement function. The problem is after doing a few safeUp's and safeDown's I'm getting some appended numbers on my slugs e.g. 'title-07' when ideally it would just stay as 'title'. The slug is based on the title and it appears my safeDown method doesn't hunt down the old slugs wherever they are.
My safeUp() looks something like this:
/*
 * Set the entry and save it
 */

$entry = new Entry();

// Set the entry data
$entry->sectionId   = '4';
$entry->typeId      = '4';
$entry->authorId    = '1';

// Title etc..
$entry->title = $title;

// Run saveElement with data
$success = Craft::$app->elements->saveElement($entry);

My safeDown is a bit less elegant:
/*
 * Find the matching entries to delete
 */

$entry = (new \craft\db\Query())
->select("id")
->from("content")
->where( "title = " . $title )
->all();

/*
 * Loop through matches and delete
 */

foreach( $entry as $key => $value ) {

    $rowsAffected = (new \craft\helpers\Db())
    ->delete('content', 'id=' . $value['id'] );

}

My question is a couple things.
Is there something as nice as saveElement where I can just pass a typeId and id for the content and everything related gets removed?
My current method where I use craft\helpers\Db->delete leaves behind the original slug. If there's not a cleaner method like saveElement, where can I find the slugs to get rid of them as well?
Any pointers greatly appreciated.

As mentioned by Oli, I was able to use deleteElementById($entry->id). Note $id now refers to $elementId
/*
 * Find the matching entries to delete
 */

$entry = (new \craft\db\Query())
->select("elementId")
->from("content")
->where( "title = " . $title )
->all();

/*
 * Delete the entry
 */
foreach( $entry as $key => $value ) {

  // Hard delete.
  $success = Craft::$app->elements->deleteElementById( $value['elementId'], null, null , true );

}

Now entries deleted also deleted slugs that were created. But there was one problem: entries deleted previously before this migration code upgrade were still there. I found that these slugs were kept in the table element_sites and I manually deleted all the old slugs no longer being used. So far there has been no issue with this and the rest of the site functions apparently normally.


In plugin combine a returned entry with custom table data In a plugin I am returning entries from a section. Can I combine a returned entry from that section with data from a custom table into one model for output?

Just have your plugin supply a custom MyPluginEntryModel that extends Craft's EntryModel and merges in any custom attribute definitions from your custom table.

class MyPluginEntryModel extends EntryModel
{
   ....

    protected function defineAttributes()
    {
        return array_merge(parent::defineAttributes(), array(
            'customAttribute1' => AttributeType::Number,
            'customAttribute2' => AttributeType::String,
        ));
    }

   ....
}


Is there a way to override the template caching system? We're in the process of building a restfull backend in craft. We had sluggish response times due to the amount of queries done by craft on a an entry listing. Each entry had about 3 levels of nesting and a list of 9 entries tool 10-13 seconds to render.

I've worked around this by outputting the json using twig and its cache. Now I've noticed that craft uses mysql to cache its data. Due to the amount of queries to this backend we will probably run out of its auto incremental key space. This platform has over 2 million unique visitors a day. (I've seen it before and it will happen)

Now my question is can I override the twig cache subsystem? For example use Redis so I can proactively notify middleware and/or frontend that caches have been cleared, so these tiers can regenerate the cache while still serving stale cache? And/or is there an event available inside the cache system so I can trigger on that?

Thanks in advance

"Due to the amount of queries to this backend we will probably run out of its auto incremental key space."

That would seem to suggest that maybe your template caching strategy should be re-visited?

Are there really going to be 215 million (from a signed int) unique pieces of cached content that need to be saved?

  Now my question is can I override the twig cache subsystem?

Right now, there aren't any hooks or events into the template caching layer, but a few people have asked for it to hook up to external services like memcached, redis, varnish, fastly, etc.  So it's on our list to add some events for plugins to get notified when things like cache expiration occurs for a piece of content.


Project Config On or Off for Production Server Should Project Config ('useProjectConfigFile' => true,) be set to 'true' or 'false' on the Production server? I have been reading Craft CMS documentation, watching some CraftQuest Lessons,  searching here on Stack Exchange, but have not found a suggested best practice.

I performed a Craft CMS update via the Control Panel Update button with Project Config on. The Update crashed.

Even if useProjectConfig was set to false, it's not a good idea to update Craft on a live site because, as you've discovered, errors sometimes happen.

There's even an allowUpdates config setting that we recommend people setting to false on the live site just so someone doesn't accidentally update Craft from the control panel.

The safer workflow is to have a dev/staging/test environment where you can perform the update first, verify that everything still works fine and then you can deploy the update files to the live site with a much smaller chance of something going wrong.

If you've decided to use Project Config, which it seems you have, then you should really not be doing updates on the live site from the control panel.

And you should also have allowAdminChanges set to false so no one can even make Craft settings changes from the control panel.

At that point, Project Config should be enabled in all of your environments and your config/project.yaml and composer.json and composer.lock files should be deployed to your live site along with a deployment process that runs composer install on the live site post-deployment.


Add errors to an event via plugin I'm looking to add some additional functionality to the user registration process. Basically, we will be creating "access codes" that can be used N number of times during registration. Once N users have signed up using that code, the plugin that I'm writing will prevent additional users from registering on the site.

Right now, I'm having trouble determining how to add errors to the users.onBeforeSaveUser event. My plugin checks availability of the access code that was submitted during the registration process, and I'd like to prevent Craft from saving the user if the code isn't valid/enabled. Here's what the code looks like:

<?php

namespace Craft;

class GatekeeperPlugin extends BasePlugin
{

  ...

  function init()
  {

    craft()->on('users.onBeforeSaveUser', function(Event $event) {

      // The user that's about to be saved
      $user = $event->params['user'];

      // Determine if an access code was submitted
      $accessCodeUsed = isset($user->contentFromPost['accessCodeUsed']) ? $user->contentFromPost['accessCodeUsed'] : false ;

      // If there was an access code submitted...
      if ($accessCodeUsed)
      {

        // Look for it in the database
        $queryAccessCode = craft()->db->createCommand()
          ->select('id, field_code, field_limit, field_timesUsed')
          ->from('content')
          ->where(array('field_code' => $accessCodeUsed))
          ->limit('1')
          ->queryRow();

        // If the code was found...
        if($queryAccessCode)
        {

          ...Do some stuff

        }

        // If the code wasn't found...
        else
        {

          // Prevent Craft from saving the user
          // Add error "Sorry, that code is not valid."

        }

      }

    });

  }

}

It appears that error messages can only be added to models, but I'm working with an event here. The event has a user model within it, but trying to add errors to it (e.g. $event->params['user']->addErrors([array of errors])) hasn't worked for me. Any pointers, here?

Events are generally not capable of having an effect on the calling code; they’re just notifications.

You would probably be better off disabling public user registration (Settings > Users > Settings), and creating a custom controller that registers users instead. Your controller would be the one to determine if there are still any slots left, and if so, call craft()->users->saveUser() itself.


Edit links in footer I'm new to Craft and need to add a link to the right side of the footer on this site http://britainthinks.com/

Can't see anything in the dashboard that holds the existing content or any obvious templates

There is a Global called footer but this doesn't have the links, just some contact info.

Would welcome any help!

Thanks

It's pretty much impossible to be sure just by looking at the site. But there's a good chance the links are coded into the templates.

Again, the exact template could be anywhere. It's common to create a partial include it in a layout template but could equally just be within the layout template itself.

You don't say if this is Craft 2 or Craft 3 but you'll need to look in either /craft/templates for Craft 2 or /templates for Craft 3

You may also find you have an add on such as NavEE or A&M Nav installed. This allows the creation of navigation blocks which can be included in templates. So you might find the links in there.


Order tags by count - e.g. Top 10 Tags Used I would like to have a Top 10 Tags list in descending order. I'm able to list and limit 10 tags used, but the order parameters are limited to name, group, and date essentially.

This is based on Matt's answer to your previous question. Likewise this should better be used within cache tags!

This is untested, but it hopefully works out for you.

.

Edit:

This is still untested but it came to my ears, that it could make problems to assign variables as array keys. That's why I changed the code to use objects instead. If anyone uses this code, I'd be happy to hear if it does actually work!

{# Get all tags #}
{% set allTags = craft.tags.find() %}

{# Define object outside the for loop #}
{% set weightedTags = [] %}

{# Iterate over all tags #}
{% for tag in allTags %}

    {# Get total number of tag occurrences in entries belonging to 'blog' section #}
    {% set count = craft.entries.section('blog').relatedTo(tag).total() %}

    {# Save tag id and count to our weightedTags object #}
    {% set weightedTags = weightedTags|merge([{ tagId: tag.id, tagCount: count }]) %}

{% endfor %}

{# Sort, reverse-sort and pick top 10 elements of the array #}
{% set weightedTags = weightedTags|sort|reverse|slice(0, 10) %}

{# To print the top 10 list iterate over our weightedTags array #}
{% for tagObject in weightedTags %}

    {# Get tag object for each tagId #}
    {% set tagEntry = craft.tags.id(tagObject.tagId).first() %}

    {# Return the list item #}
    {{ tagEntry.name }}

{% endfor %}


How can I delete a cookie from a plugin? I am able to set a plain old cookie like this:

$cookie = new HttpCookie('myCookie', 'myValue');

I am trying to delete it like this:

$cookie = new HttpCookie('myCookie', '');
craft()->request->getCookies()->add($cookie->name, $cookie);

When I make a call to check the cookie, I get this returned:

{"name":"myCookie","value":"","domain":"","expire":0,"path":"\/","secure":false,"httpOnly":true}

I can see that the value is getting cleared, but is there any way to just totally wipe out the cooke?

I wasn't able to find an answer for removing a cookie in Craft 3 so after plenty of time of trying to figuring it out, this is my conclusion.

First of all, I am setting the cookie using the following method.

$cookie = new Cookie(['name' => 'cookieName']);
$cookie->value = $data['accessToken'];
$cookie->expire = $data['expires'];
Craft::$app->getResponse()->getCookies()->add($cookie);

I kept running into a problem with the Cookies being set are read-only so in order to remove the cookie need to use response to change and remove cookies.

Craft::$app->response->cookies->remove('cookieName');


What is the most efficient way to code matrix blocks by type in a template? Is this the most efficient way to use matrix in a template where different block types need outputting in various area of the HTML markup? I have 9 block types that need to be placed in various areas of the HTML markup.

{# Create a variable with the matrix fields in #}
{% set var = entry.sofa %}

{# Is the block in the variable #}
{% if var.block.type('twoSeaterSofa') %}

    {# block is in variable #}
    {% for block in var.block.type('twoSeaterSofa') %}
        {# output block #}
        {{ block.price }}
    {% endfor %}

{% endif %}

Grateful for any advice or code.

The right syntax to filter matrix blocks by block type is this (see Craft docs here):

{% for block in entry.myMatrixField.type('text') %}{% endfor %}

So you only have to remove the extra .block you have in your example code. I'd save the returned array of MatrixBlockModels to a variable and use the length filter to see if there are any blocks of this type.

{% set twoSeaterSofas = entry.sofa.type('twoSeaterSofa') %}
{% set threeSeaterSofas = entry.sofa.type('threeSeaterSofas') %}

<!-- some HTML -->

{% if twoSeaterSofas|length %}
    {% for block in twoSeaterSofas %}
    {% endfor %}
{% endif %}

<!-- more HTML -->

{% if threeSeaterSofas|length %}
    {% for block in threeSeaterSofas %}
    {% endfor %}
{% endif %}


Smart Map plugin - Get visitor's lat/lng I'm using the Smart Map plugin but want to render my own maps. All I need is the lat/lng of a visitor yet I can't find it in the documentation. I don't want Smart Maps to render the map for me, I just want the lat/lng.
I'm currently using the Geo plugin to do this but it seems a bit silly having both installed.

Smart Map can indeed do this! It's currently an undocumented feature, because I wasn't sure if/how it was going to evolve over time. However, that feature has changed very little over the past two years, so perhaps it's time to finally document it. :)
UPDATE: As of Smart Map 2.3, this is now a publicly documented feature.


How to display entries relevent to date assigned I am creating a website for not-for-profit organization that help victum of domestic violence. I have a page called Event which display all fund raising events under each month.

I have setup Date/Time field and 12 months tabs and want to know how to display the entries for each month. For example I assign two entries for Feb and when I click on Feb tab, the entries will display.

Here is the sample code I wrote and copied Marion Newlevant's suggestion but when I tested it does not work. Any ideas?

Here is what my html structure to display the entries for each month.

    = '~startDate, '< '~endDate])
    %}

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

          {% else %}

          {% endif %}
        {% endfor %}

{{ entry.title }}

        {{ entry.subtitle }}

{{ entry.eventBlurp}}

        {% for block in entry.eventOrganiser %}
          {% if block.type == "eventDate" %}

              Buy Ticket

{{ block.ticketLeftover }} / {{ block.ticketAvail }} tickets left

          {% endif %}
        {% endfor %}

  {% endfor %}

Assuming your date/time field is called dateField, and your entries channel is events.

You want to fetch just the entries that have dates in that month. You can do that like this:

If you know the year and the month, the start of the month is (~ is the twig string concatination operator):

{% set startDate = year~'-'~month~'-01' %}

And the start of the next month is:

{% set endDate = startDate|date_modify('+1 month')|date('Y-m-d') %}

We use twig's date_modify filter to get the start of the next month, and then twig's date filter to convert that to a string.

Now you can fetch the entries for the month. You want ones where the dateField is on or after startDate and before endDate.

{% set monthEvents = craft.entries
  .section('events')
  .dateField(['and', '>= '~startDate, '< '~endDate])
%}


Applying image transforms to remote images? Is there any way to apply an image transform (either CP-defined or template-defined) to a variable which represents the URL of a remote image? Naturally it would need to be cached locally, but since transforms are already cached I figure it's not too much of a stretch.

My use case is applying a transform to the thumbnail image of a video which is being fetched by an oEmbed plugin.

Imager plugin is capable of resizing remote images