To edit the HTML code for your Craft site you're going to want to edit the appropriate template file in your Craft install. If you're running on Craft 2 your template files can be found in .craft/templates/; if you're on Craft 3 your templates are found in ./templates/.

You'll need to look in the Craft control panel and see which twig template the section containing the entry (or page) you're trying to edit references. You'll find that info if you try to edit the respective section in Settings > Sections.

I will mention as well, if you're looking to include some code that belongs in the HEAD of the html, you'll probably find that in a "master" twig template that contains the <head></head>. Once you find the twig template specified in the section settings in the Craft control panel, most of the time you'll see an {% extends %} tag which links back to that "base" template.

Hopefully that helps! I'd recommend maybe reading through the Craft docs, starting with Templating Overview. Cheers!


Asset indexing failing with large asset source

I've done this before with a combination of a Craft console command that replicates the indexing process, and a external way that processes each row.

<?php
namespace Craft;

class BusinessLogicCommand extends BaseCommand
{
    /**
    * ./craft/app/etc/console/yiic businesslogic --offset=x --sourceId=y --sessionId=z
    */
    public function actionIndex ($offset = '', $sourceId = '', $sessionId = '')
    {
        if ( $offset ) {
            $index = craft()->assetIndexing->processIndexForSource($sessionId, $offset, $sourceId);

            if ( $index ) {
                echo sprintf("Processed offset: %s for source %s", $offset, $sourceId);

                return 1;
            }
        }

        return 0;
    }

    /**
    * ./craft/app/etc/console/yiic businesslogic getIndexOverview
    */
    public function actionGetIndexOverview ($sourceIds = '')
    {

        // Get batches
        $batches   = array();
        $sessionId = craft()->assetIndexing->getIndexingSessionId();

        // Selection of sources or all sources?
        if ( $sourceIds ) {
            $sourceIds = explode(',', $sourceIds);
        }
        else {
            $sourceIds = craft()->assetSources->getViewableSourceIds();
        }

        $missingFolders = array();
        $grandTotal     = 0;

        foreach ($sourceIds as $sourceId) {
            // Get the indexing list
            $indexList = craft()->assetIndexing->getIndexListForSource($sessionId, $sourceId);

            if ( !empty($indexList['error']) ) {
                echo json_encode($indexList);

                return 0;
            }

            if ( isset($indexList['missingFolders']) ) {
                $missingFolders += $indexList['missingFolders'];
            }

            $batch = array();

            for ($i = 0; $i < $indexList['total']; $i++) {
                $batch[] = [
                    'sessionId' => $sessionId,
                    'sourceId'  => $sourceId,
                    'total'     => $indexList['total'],
                    'offset'    => $i,
                    'process'   => 1,
                ];
            }

            $batches[] = $batch;
        }

        // Overview

        $assetsSourcesBeingIndexed = $sourceIds;
        $assetsMissingFolders      = $missingFolders;

        $job = [
            'batches' => $batches,
            'total'   => $grandTotal,
        ];

        echo json_encode($job);

    }

    public function actionCleanUpIndex ()
    {
        // Get overview
        $sourceIds      = $assetsSourcesBeingIndexed;
        $missingFiles   = craft()->assetIndexing->getMissingFiles($sourceIds, $sessionId);
        $missingFolders = $assetsMissingFolders;

        $responseArray = array();

        if ( !empty($missingFiles) || !empty($missingFolders) ) {
            $responseArray['confirm'] = craft()->templates->render('assets/_missing_items', array( 'missingFiles' => $missingFiles, 'missingFolders' => $missingFolders ));
            $responseArray['params']  = array( 'finish' => 1 );
        }
        // Clean up stale indexing data (all sessions that have all recordIds set)
        $sessionsInProgress = craft()->db->createCommand()
                                        ->select('sessionId')
                                        ->from('assetindexdata')
                                        ->where('recordId IS NULL')
                                        ->group('sessionId')
                                        ->queryScalar();

        if ( empty($sessionsInProgress) ) {
            craft()->db->createCommand()->delete('assetindexdata');
        }
        else {
            craft()->db->createCommand()->delete('assetindexdata', array( 'not in', 'sessionId', $sessionsInProgress ));
        }

    }

}

This is done in 3 steps:

Trigger getIndexOverview, which returns an JSON object string that lists all your index rows.
Use the JSON object in some way, and create a job for each row, which should trigger step #3.
Trigger the index command with the parameters  --offset=x --sourceId=y --sessionId=z

I have used Laravel + Queue backed by Redis for this. If you have huge files, or a lot of data to process, think about spinning up a big VPS somewhere.

That way you can run 10-12 jobs in parallel, export your database/file structure, and tear the VPS down after. With hourly pricing, you'd end up paying a few $.


Please read the docs about ElementCriteriaModel craft.assets returns you an element of type ElementCriteriaModel so you can define criteria for your search. In order to fetch the elements you have to do first(), find(), last() or certain other functions.

craft.assets.filename('flag.jpg').first()


Searching for a way to add products to cart and sending the csrf token with ajax

So normally in your main layout twig template, you'd have something like this to set some JS variables to hold the current CSRF token:
    {# Super preliminary JS Stuff #}
    <script type="text/javascript">
        window.csrfTokenName = "{{ craft.app.config.general.csrfTokenName|e('js') }}";
        window.csrfTokenValue = "{{ craft.app.request.csrfToken|e('js') }}";
        window.cartId = "{{ cart.id }}";
    </script>

Then, in your add-to-cart JS, you do something along the lines of this to add those variables to the form data:
   formData.push({
     name: window.csrfTokenName,
     value: window.csrfTokenValue
   })

Craft's own documentation for this is here:
https://craftcms.com/knowledge-base/enabling-csrf-protection#updating-your-javascript


Prevent shipping tax from doubling when item qty goes from 1 to 2 in cart

Since taxes apply currently only at the line item level, the tax is correctly applying to the item+shipping cost of the line item.

The shipping max cost is making a order level change to the baseShipping after all line items have been adjusted.

We are looking into supporting order level taxes only, to avoid this, and will update this answer soon with a solution.

Thanks.

UPDATE: This has been fixed, see answer here: Craft Commerce: Calculating (Product + Shipping) + Tax?


Craft Commerce: Calculating (Product + Shipping) + Tax?

Currently there is a caveat, that order level shipping costs can not have tax applied to them.

This means if you have any 'base shipping costs' applied  to the cart the tax engine can not see them because they are not on a line item. Currently the tax adjuster system only adds tax to line items.

This is an issue that will be fixed in a future release but will mean there needs to be a change to the way the adjustment system works. We would need to allow tax to be added to adjustments.

If you want tax on shipping cost you would need to make all shipping rules only add tax to line items. Let me know if you have further questions and I will update my answer.

UPDATE: see other answer for the final solution.


Craft Commerce returned orders stock and refund handling

Update:

Commerce 2 now supports partial refunds for payment gateways that support partial refunds.

Partial refunds on Commerce is definitely on our radar... it's the 8th most requested feature here: https://github.com/craftcms/commerce/issues

Unfortunately, it's pretty impractical right now in Commerce 1 with our reliance on the Omnipay  ibrary for all of our payment gateways. They support the "lowest common denominator" and since many of the gateways they support don't support partial refunds, they don't support it at all.

The upcoming Commerce 2 makes Onmipay an optional requirement... meaning that it's much easier to write a plugin for an arbitrary gateway and add support for the ones that support partial refunds.  For example, the 1st party Commerce 2 Stripe plugin implements it and can be used as an example for other payment gateway plugis.

None of that doesn't help you now, though. You can always do a partial refund directly with the gateway if they support it (Stripe, Sagepay, etc.), but the downside is that won't sync back to Commerce 1 so order totals/inventory/etc. won't be updated on our end.

If the payment gateway has callback hooks for that type of thing, you can write a plugin that helps alleviate some of these issues, but none of it is a really clean solution.


If you are trying to cover the site from the public while working on it live, i personally would name my coming soon template file 503.html

Then in the General Settings section of the control panel, switch System Status off.

That way so long as you are logged in, you can access the site as normal but anyone else visiting your site will see the contents of the 503.html template.


The easiest way around this is to include the required library in your main-project composer.json and let it handle it rather than your modules composer.json.

The alternative is to require your module properly in your main composer file as you do with plugins and install it via path which kinda breaks the idea of it


Create custom field type with dynamic data

You should have a look at the LJ Dynamic Fields plugin, it sounds like everything you’re after is already doable with this plugin and if not, you can study the code or build upon it.


Running console (yiic) under MAMP (Windows)

I may have found my own solution. It looks as if Yiic was not picking up the .env file.

The file ... craft\app\etc\console\yiic .. must therefore be changed from ...

#!/usr/bin/env php
<?php
require_once(dirname(__FILE__).'/yiic.php');

... to ...

#!/usr/bin/env php
<?php
require_once('../../../../vendor/autoload.php');
try {
    $dotenv = new Dotenv\Dotenv('../../../../');
    $dotenv->load();
    $dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS']);
} catch (Exception $e) {
    exit('Could not find a .env file.' . $e);
}
require_once(dirname(__FILE__).'/yiic.php');

Hope this is of use to other people.


If you haven't seen it already there is some good Docs on the Craft site on sections and how to use them.

Without seeing your Structure settings it is hard to pin-point exactly what the issue is here, but creating a Structure and adding your templates to it is a very easy procedure. I have attached a screenshot of a Structure for a project I am currently working on, so you can reference in case you have made a mistake somewhere.

So in my templates I have created a folder called services, and in there I have my index.twig and _entry.twig files. Try matching your Structure settings with the screenshot and see if that fixes your problems.


Why am I getting only one result with "craft()->db->createCommand()"?

Completely untested but it looks like you're just querying a row instead of all.

Update queryRow() to queryAll()


When 'generateTransformsBeforePageLoad' => true is set, is it taking a long time to load because of the sheer number of images you have or is it only on the first request when all of the transforms are being generated?

After they are generated, they are quickly served directly by Apache (or whatever your webserver is) and Craft doesn't get involved.

If it's because of the sheer number of images you have, then you'll probably want to look at a front-end method of lazy loading the images as they are needed.


Ajax - How to create a Category through actionTrigger

Of course there is, otherwise we would not be able to store categories in Craft. The controller that handles categories is called CategoriesController and it contains the function actionSaveCategory so you route will be actions/categories/saveCategory

To create/save a user you have the UsersController.. You may guess the function is actionSaveUser => actions/users/saveUser

It's the same for every element in Craft. EntriesController, actionSaveEntry => actions/entries/saveEntry

But this all has nothing to do with an endpoint or an API, it's just basic Craft code


Just posting this here since I saw an official answer in Github:

  This changed in Beta 7:

  UserException reports are now styled like other exceptions when Dev
  Mode is enabled, with the full stack trace shown.

  You can see your 404 template by requesting it directly, e.g. http://mysite.dev/404

https://github.com/craftcms/cms/issues/1613#issuecomment-293366095


Limit countries shown in checkout shipping to countries assigned in shipping zones settings?

You would need to either

1) Delete all the countries you don't use within your site in commerce > settings > countries.

2) Simply hide them in twig with something like this:

{% set allowedCountryIds = [1,3,4,6,7] %}
{% for key, country in craft.commerce.countriesList %}
  {% if key in allowedCountryIds %}
    <option value="{{ key }}">{{ option }}</option>
  {% endif %}
{% endfor %}


You could use craft.app.isSystemOn. This will return 1 if the System Status is "on" in Settings -> General in the control panel. It will also respect the value of the isSystemOn config setting if you're setting it in general.php


Assets are not loading anymore (maybe an update)

Solved the complete error with a reset of the imager config file.


It looks like getenv returns a string (or false if the variable is not set, but it is in this case). So you need to cast the string to a boolean value to make it work. However, (bool)'false' gives true, not false. There are two possible solutions:

Set the variable to 0 instead of false in your .env file:

DEV_MODE=0
AUTO_UPDATE=0

And then in your config:

'devMode' => (bool)getenv('DEV_MODE')

This works because (bool)'0' gives false.
Use this solution:

'devMode' => filter_var(getenv('DEV_MODE'), FILTER_VALIDATE_BOOLEAN);

This works because the FILTER_VALIDATE_BOOLEAN filter returns TRUE for "1", "true", "on" and "yes", and returns FALSE for any other value.


In order to get all related items from a BaseRelationField you'll need this syntax

$entry->getFieldValue('contentMatrix')->all()

Thus your loop will be

foreach($entry->getFieldValue('contentMatrix')->all() as $block){


How can I filter a for loop based on the value of a field?

You don't filter a loop but you can attach certain criteria in your ElementQuery. craft.entries returns you an object of type EntryQuery (link to docs). You can search for specific entries based on different criterias by calling magic methods or function in your Query object.

For example: section('docs') will return only entries in the section docs. You can attach any field handle that is in your field layout as well.. In your case account('05').

{% for line in craft.entries.section('docs').account('05').all() %}
     {{line.lineItemTitle}} ({{line.lineItemTitle}}),
{% endfor %}

If you want to search for multiple values you can include them as array or comma separated string.

craft.entries
    .section(['section1', 'section2')
    .account('and, >=5, <=10')
    .anotherField('or, 3, 5')
    .someThingElse(['or' ,3 , 5])
    .all()

You can see all available syntax examples in the DB::parseParam() function


To convert the results of an Element Query into a custom key/value array you can use the index filter to specify what you want to use as the keys. So for example if you want the category slugs to be the keys and the category titles to be the values you'd do it like this:
{% set indexedCats = craft.categories.all()|index('slug') %}
{{ macro.select('name', indexedCats }}

=================

output:

<select name="name">
    <option value="cat-slug-1">Cat Title 1</option>
    <option value="cat-slug-2">Cat Title 2</option>
</select>

Swap in 'title' if you want the key and value to both be the same.


What is the best way to have an entry for top-level categories?

After a little more consideration, I decided it was best instead not to use categories for this.

I have instead created a 'Product Categories' structure, and the relationships are handled through an entry field.


Here's some things to consider with SEO and URLs:

Readability of the url
Including keywords in the url
Shorter > longer
Fewer /folders/ in the url
Generally don't want any caps in the URL
Separate words with hyphens or underscores

So, "/particular-product-title" would be preferable to "/productCategory/particular-product"

Hope that's helpful!


How to save Order Status Message in Craft Commerce using frontend form

Updating an order from the front end would not be possible unless the user has update/manage order permissions.

As for the message it is simply a message post param that you send along with the status ID.


There are actually two problems happening here...

(1) For a location to appear on a map, it's critical for the latitude & longitude to be filled in. Within the UX of the Address field, this can happen a few different ways... as you mentioned, clicking "Select Address" is one of the main ways of doing so. Additionally, the address selection modal will be automatically triggered when you tab between fields.

(2) There is apparently a small bug in Smart Map, which I'll go ahead and patch. If a location is lacking coordinates, the JS shouldn't attempt to render that marker. It should just be omitted from the map instead.

Hope that helps. I'll release a patch shortly.

UPDATE:

I just pushed out Smart Map v2.3.2, which fixes the bug. Instead of triggering a JS error, the marker will simply be skipped. In that case, the JS console will output the following message:

  Unable to draw marker, invalid coordinates.

Hope that helps! For anyone who may need further support on this issue, feel free to email support@doublesecretagency.com.


What are the differences between an elementCriteriaModel and fetched elements?

The ElementCriteriaModel and the individual element models (like the EntryModel) can be thought of as two different points of time in retrieving data from your database. Essentially, you'll use an ElementCriteriaModel before the database is queried and the element models afterwards.

  Why do I sometimes have to fetch elements with .first(), .last(), .find()…

The ElementCriteriaModel does what it says on the tin — it specifies criteria to find elements. When you call .find(), .first() or .last(), the model sends itself off to the ElementsService which will return element models, based on your criteria.

  …and in other cases the ElementCriteriaModel is enough?

Internally, Craft is using ElementCriteriaModels to work things out. For pagination or the .getPrev() example, Craft is finding elements using the same criteria, but it's using it in a different way to .find() or similar methods.

You can omit the .find() method and treat the ElementCriteriaModel as an array, but it depends on your scenario. As was touched on here, there are performance impacts to consider.


From the docs: http://buildwithcraft.com/docs/plugins/hooks-and-events#listening-for-events

The entry will be in the params property of the $event.  You can access it like so:

$event->params['entry']


What other properties are in the "entry.image" object?

When you reference a relationship field (ie: Entries field, Assets field, etc), you will automatically get an ElementCriteriaModel to represent the "value" of that field.

Since entry.image is giving you an ElementCriteriaModel, you need to specify exactly what you want that ECM to do. If you try to use the ECM without specifying .first (or .last, or .total), then by default it will assume you meant .find. Which is obviously not what you're trying to do in this case.

I highly recommend reading my answer on this other thread... It's not quite a duplicate of your question, but it covers a lot of the same ground.


Exclude entries by array of authors from ElementCriteriaModel

Iterating on Michael's answer: build an array of users to exclude, like this:

{% set excludeArray = null %}
{% for user in entry.winners_exclude %}
    {% if loop.first %}{% set excludeArray = ['and'] %}{% endif %} {# only set this array if exclude users exist #}
    {% set excludeArray = excludeArray |merge(['not ' ~ user.id]) %}
{% endfor %}

{# check if users should be excluded, I'm not sure what happens if you'd do .author(null) #}
{% if excludeUsers %}
    {% paginate craft.entries.section('bildupload').author(excludeUsers) %}
{% else %}
    {% paginate craft.entries.section('bildupload') %}
{% endif %}


You can submit a paymentSourceId to the payments/pay controller action.

<select id="paymentSourceId" name="paymentSourceId">
    {% for storedCard in storedCards %}
        <option value="{{ storedCard.id }}" {% if storedCard.id == cart.paymentSourceId %}selected{% endif %}>{{ storedCard.description }}</option>
    {% endfor %}
</select>


Sort entries in a category via entry field

You are getting this error because the "entries" fields are always considered to be a query (ElementCriteriaModel) that can return more than one element. Therefore, if you know for sure that you will always have one and only one town linked to a business, you could do something like that:

{% set businessesByTown = businesses|group('bizTown[0].title') %}

If the title field is the name of the town you should get an associative array with the town names as keys and the businesses entries as values.

Additional info about sorting, following swthate's comment below:

If you want to sort the businesses, I think it can be done in the original query, so it would become:

{% set businesses = craft.entries.relatedTo(category).order('title') %}

However, the towns cannot be sorted directly since Twig's sort filter uses asort which sorts the array based on values. What you can do then is extract the businessesByTown array's keys in another array:

{% set towns = businessesByTown|keys|sort %}

You now have a sorted list of towns. Using this list, you can loop on all towns and businesses like that:

{% for town in towns %}
    {{ town }}
    {% set townBusinesses = businessesByTown[town] %}
    {% for business in townBusinesses %}
        {{ business.title }}
    {% endfor %}
{% endfor %}


How to do advanced routing to a Craft channel

I'm not sure I understand your question, but to try:

There actually isn't a "standard Craft routing", for channel Sections or anything else.

In your channel's Section settings (available via Settings -> Sections -> [your event section]), there's a field called Entry URL format. Simply add {slug} to that field (and nothing else), and you should be good to go. You can even use the same URL format for different sections.

In regards to your "bonus question", if you set the Entry URL format to simply {slug} for the events section, Craft will probably throw 404 for any /events/{slug} URLs, out of the box.

As for dynamic routes, you're correct that Craft won't populate your template with the entry variable – it will however, populate the template with variables matching the tokens that you add to your route. So, if you e.g. create a dynamic route that uses the <slug> token, you can pull the entry in the template like this:

{# At the top of your template: #}

{% set entry = craft.entries.section('events').slug(slug).first() %}
{% if not entry %}
   {% exit 404 %}
{% endif %}

{# ...the rest of your template #}

Beyond the above – if you're still unsure how to set things up properly, I'd suggest taking a look at the official docs on routing, which explains in detail how Craft evaluates requests and what your different routing options are. Particularly important if you figure that you do need to do some custom routing beyond what the URL format setting can do.


Asset integration with Microsoft Azure Storage in Craft 3

You could try fronting Blob with s3proxy as a workaround.


https://github.com/craftcms/cms/pull/4404

You can now delete objects that implement ElementInterface using this method:

craft\services\Elements::mergeElements()


ERR_EMPTY_RESPONSE when accessing order data (MAMP)

Try updating your MySQL config max_allowed_packet config setting to a higher value and verify the change has been applied to your running instance.

https://dba.stackexchange.com/questions/19135/mysql-error-reading-communication-packets


How to update a field when a user views a page

There's a plugin called Entry Count that updates a counter record every time a visitor loads a page. You might be able to adapt that plugin to check to see if the user is logged in, and if so, record their user ID to the database instead of incrementing the hit counter. The value is displayed on the Craft entry list dashboard, you would just want to adapt that to display the username instead of the ID.


Need help with plugin for SAML authentication

Yii provides a CUserIdentity class that is meant for your typical username/password form/cookie-based authentication.  Craft extends that a bit and uses that in it's own UserIdentity class for logging into the control panel, etc.

But ultimately, Yii's CUserIdentity implements the IUserIdentity interface, which they intend for people to use that want to implement their own custom authentication mechanisms.

So you'd probably want to write a SamlUserIdentity class that implements the interface, provides it's own authentication logic (calls the Identity Provider, etc) and handles the interactions between Yii/Craft and SAML.

If you just want to enforce an authenticated session from the template, use:

{% requireLogin %}

If you want to do it from a controller's action, use:

$this->requireLogin();


Replacement for deprecated EntryRevisions service

Revisions and Drafts are first-class citizens of the Element world in Craft 3.2.

So for your example:

  The plugin has it's own tables the track drafts by it's ID alone, so it's my primary means of looking up drafts.

This is what you would do:

$draft = Entry::find()
    ->draftId(123)
    ->one();


My `for` loop has fallen...and it can't get up

First: grab all entries that match your criteria

Second: grab all Tags related to your entries

PHP

$entryIds = Entry::find()
    ->yourTagField(':notempty:')
    ->fellowStatus('1')
    ->section('fellows')
    ->ids();
$tagsRelatedToEntries = Tag::find()
    ->relatedTo($entryIds)
    ->all();

Twig

{% set entryIds = craft.entries
    .section('fellows')
    .fellowStatus('1')
    .yourTagField(':notempty:')
    .ids()
%}
{% set tags = craft.tags.relatedTo(entryIds).all() %}


Is there a way to split the element-api.php file?

Sure – the element-api.php config file is just plain ol' PHP, and the functions that make up the individual endpoints (e.g. 'news.json' => function () { ...) doesn't necessarily have to be declared inside that file.

One way to split up the file would be to create one PHP class per element type or section. You could compose these classes however you'd like, but something like the below would work (this is an example file containing two endpoints for the news section, and the example assumes that the file is saved under /config/endpoints/News.php):

<?php

namespace config\endpoints;

use craft\elements\Entry;
use craft\helpers\UrlHelper;

class News {

    public static function index()
    {
        return [
            'elementType' => Entry::class,
            'criteria' => ['section' => 'news'],
            'transformer' => function(Entry $entry) {
                ...
            }
        ];
    }

    public static function view($entryId)
    {
        return [
            'elementType' => Entry::class,
            'criteria' => ['id' => $entryId],
            'one' => true,
            'transformer' => function(Entry $entry) {
                 ...
            }
        ];
    }
}

The next step is to make the file available to the element-api.php config file. One way to do it, is to simply include() it at the top of the element-api.php file:

include __DIR__ . '/endpoints/News.php';

Then, you can add the endpoints from the News endpoint class to the config in element-api.php, i.e.:

<?php

include __DIR__ . '/endpoints/News.php';

use config\endpoints\News;

return [
    'endpoints' => [
        // use an anonymous function to prevent errors when running via cli
        'news.json' => function () {
            return News::index();
        },
        'news/<entryId:\d+>.json' => function ($entryId) {
            return News::view($entryId);
        },
    ],
];

If you want to take this a step further, a much nicer approach than using include() is to have Composer autoload your endpoint classes by adding the below to your composer.json file (again, this assumes that the custom endpoint classes live under /config/endpoints/):

"autoload": {
    "psr-4": {
        "config\\endpoints\\": "config/endpoints"
    }
},

After editing your composer.json file, make sure to regenerate the autoloader by running the following from your command line (root of project, same directory where you keep your composer.json:

composer dump-autoload -o

Then, you can strip out the include() statement(s) in your element-api.php file, because Composer will autoload all the classes under /config/endpoints.

Finally, to make things super clean you could of course create a custom Yii Module (or a Craft plugin) to contain your endpoint classes. This would also avoid having to manually add the classpath to the composer.json file, for the autoloading.


Commerce Stripe Card Declined shows white screen with no error

Couldn't get past the white page issue so I made a simple plugin which does the following:

craft()->on('commerce_transactions.onSaveTransaction', function($event) {
    $transaction = $event->params["transaction"];
    if ($transaction->status == "failed") {
        $message = $transaction->response["error"]["message"];
        craft()->userSession->setNotice($message);
        craft()->request->redirect('/order');
    }
});

Thank you for your help Luke Holder. Strange that I couldn't get rid of the white page but at least we found a solution in the end to stop the issue on the live site whilst customers are making payments.


How to send email to user when we change his group?

With a plugin, you can tie into the userGroups.onAssignUserToGroups event and send your update email directly with the details.

https://craftcms.com/docs/plugins/events-reference#userGroups-onAssignUserToGroups


Craft Commerce "state" dropdown field fails to populate with states for new customer addresses

This is caused by the model.countryId not being defined on first page load, which means the options defaults to an empty array. Once the address is saved, the selected country that is saved will provide the countryId and therefore your states dropdown is populated the second time around.
IIRC, the sample Commerce templates hide the text field and show the states dropdown field via JavaScript based on the selected value of the country dropdown.
If you know you're always going to default to United States as the initially selected country, you could ensure your state text field is hidden by default and set a default country ID:
{# Note: example country ID #}
{% set defaultCountryId = 51 %}

{% set options = (model and states[model.countryId] is defined ? states[model.countryId] : states[defaultCountryId]) %}

I think you could get the correct country ID for the USA from craft.commerce.countries.allEnabledCountriesAsList to use with your default var.


Sorting Entries by category, from multiple category groups

Finally figured it out, I had been so close! The following works:

{% set days       = craft.categories.group('day') %}
{% set activities = craft.categories.group('activity') %}

    {% for day in days %}

{{ day.title }}

        {% for activity in activities %}
            {% set entries = craft.entries.section('attractions').relatedTo(
              'and',
              { targetElement: day },
              { targetElement: area }
            ) %}

            {% if entries | length %}

{{ area.title }}

                {% for entry in entries %}

{{ entry.title }} {{ entry.startTime|date('H:i') }}

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


How can I set the dateCreated attribute on an EntryModel?

The dateCreated, dateUpdated and uid columns are more internal Craft auditing columns that Craft manages for every row in every table.

Maybe what you're looking for is the postDate column for entries, which you can set a custom value to.


I haven't tried this tutorial yet, but having learned a lot about Craft 3 from Andrew Welch's other tutorials, I don't doubt this may have some nuggets of useful information to get you on your way.

https://nystudio107.com/blog/enhancing-a-craft-cms-3-website-with-a-custom-module


You're going to want to ensure that all of the sites that are a translation of each other are in the same site group, as detailed here: SEOmatic Multi-Site Language/Locale Support


Get field Matrix field label inside template

Something like this should do what you're looking for:

{% for block in entry.body %}

    {% set fields = block.getFieldLayout().getFields() %}

{{ block.getType().name }}

            {% for field in fields %}

{{ field.getField().name }}
            {% endfor %}

{% endfor %}


Do I need the web.config file in the craft root directory?

If you're not specifically running IIS on Windows, then you can safely remove it.


How to filter entries with null value in date field?

The special strings :empty: and :notempty: might be what you're looking for. The docs list it only against expiryDate, but I think it's available for any field: https://docs.craftcms.com/v3/dev/element-queries/entry-queries.html#expirydate


Feed Me: Importing a relationship between elements

Have you set the type of data you're providing for the category field? Each element field allows you to state what attribute the data you're trying to import. Title, ID or slug, which are shown in a small dropdown field on the feed mapping page.

Can you check this?


I think the easiest way to replicate this behaviour, is to use the Super Table plugin. It lets you create all of Craft's fields within table rows.


Responsive Images Srcset and Sizes based on Image Transform

Merge operates on arrays or array-like things, and you probably handed it the string "card", which is why you got that error.

I think what you want to do here is use the transform in the control panel to define the aspect ratio, but use the img macro to determine which sizes to transform to.

You can calculate the aspect ratio like this:

{% set aspectRatio = asset.getHeight(options.style) / asset.getWidth(options.style) %}

And generate transforms like this:

transform|merge({width: width, height: width * aspectRatio})

You will want to be careful with the native (untransformed) image: only use it if it has the correct aspect ratio.

You will want to be careful with avoiding transforming to a larger image - skip the ones where the width would be greater than the native width and the ones where the height would be greater than the native height.

Using the crop transform instead of the stretch one (that the article uses) is the right thing to do too.

Also, consider the imager plugin, which has some useful srcset supporting features (and postdates that article)


Do assets have to be below public_html to access them in redactor?

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.


Select statement validating as true when no value selected?

You could try setting a minimum length:

'fieldName' =>  array(AttributeType::Name, 'minLength' => 1, 'required' => true),


Can I open one template/route when Craft is offline?

You can add a template called 503.html to your /templates folder. Craft will then use that template whenever the system is turned off, due to the HTTP status being set to 503 (service unavailable).

Inside the 503.html template, you can of course include another template (e.g. blog/post).


Check if page is Search, use different classes

{% set class = (currentPage == "search")? '5col' : '3col' %}
{% for project in pageEntries %}
     {% include '_partials/search-block' with {
        extraClasses: class
     } %}
{% endfor %}


How can I Link a category entry to a Subsite?

{% set home = craft.entries.section('pages').one() %}
{% set sub1 = home.getChildren().one() %}
link: {{ sub.title }}


You need to let Twig interpret the variable as a string. This works for me:

{% set theForm = entry.formSelect %}
{% include ''~theForm~'' %}

Hope this helps!


Check /vendor/craftcms/cms/src/models/FolderCriteria.php Class for reference. I guess most of craft's documentation lies in code.

Or you can visit https://docs.craftcms.com/api/v3/craft-models-foldercriteria.html
which is the same thing.

check table "yourprefix_volumefolders" for data type of FolderCriteria.


Freeform Checkbox Group Field only returns one value

Ok, Solspace support cam through via email with the answer...  Put square brackets after the field name!  Simple :)

<input type="checkbox" name="MyFieldName[]" value="Item1">
<input type="checkbox" name="MyFieldName[]" value="Item2">
<input type="checkbox" name="MyFieldName[]" value="Item3">


Haha. I figured it out. Leaving the answer here in case anyone else runs into the same problem:

$product = Entry::find()->section('products')->relatedTo([
    'element' => $component,
    'field' => 'productComponents'
])->one();

You can pass an object/array to the relatedTo() method as described here:

Relations | Craft 3 Documentation


Just a few side notes:

neither the first entries variable nor the second entries variable is an array - both are objects of kind craft\elements\db\EntryQuery (or craft\ElementCriteriaModel if it's Craft 2).
.search('nonMembers:0') can be shortened to nonMembers(0) (or "0" or "not 1" depending on the type) and search('membersExclusive:1') to membersExclusive(1) it will increase your performance as well

So far about that.. You can merge arrays in Twig via |merge filter

{% set values = [1, 2] %}

{% set values = values|merge(['apple', 'orange']) %}

{# values now contains [1, 2, 'apple', 'orange'] #}

EDIT:

To address your case specifically, you'll need something like this...

{% set entriesOne = craft.entries.section('publications').search('nonMembers:0').find() %}
{% set entriesTwo = craft.entries.section('improveYourClub').search('membersExclusive:1').find() %}

{% set allEntries = entriesOne|merge(entriesTwo) %}


No, Craft doesn't have any native notion of SSO.

(You can technically run multiple sites from the same Craft instance. If you had sites sharing the same server, you could run them from the same Craft installation, thereby enacting a de facto shared user roster by virtue of the shared database. If such sites also shared a root domain, you could gracefully maintain a persistent session between them. However, for the purpose of this answer, I'm assuming such a circumstance does not apply.)

Nonetheless, you could enable a limited/simple SSO-ish setup using a custom plugin...

  What I need is for users of Site A and Site B to be kept in sync in some way so they can use the same username and password to sign-on to both sites.

Trying to keep user records perfectly in sync across multiple sites would not be simple. However, you probably don't need records to be perfectly in sync. Rather, you only need one site to serve as the authoritative source of truth, and the other site(s) only needs to keep track of a unique reference (i.e. a username, or even better, a UUID.):

Master/Slave setup

Let's say Site A is the master, Site B is the slave.
All user registration takes place on the Master site. (User registration is not allowed on the Slave site.)
Login on Master uses Craft's native users/login controller method.
Login on Slave uses a custom controller method that submits a username/password combo to Master, receives back a response, and logs in the user if Master says their credentials are valid. (If a record for this user doesn't already exist on Slave, one is created 'just-in-time' and logged in.)

Caveat — If profile values are allowed to change, then we do need to create a mechanism for the Master site to notify its Slave sites of any changes. (We don't care about the slave sites' copy of the user's password, because only the Master site ever needs to do any authentication. We might care about keeping username/email/profile values in sync, because the Slave sites may want to display User info in templates or something.) This can be accomplished using a custom controller action (authenticated by some secret key, perhaps) by which the Master site triggers the Slave site's users/saveUser method with new/updated content.

  Bonus points if they can automatically signed into to Site B if they are already signed into Site A.

If the sites share a root domain...

Craft decides that a user is "still signed in" by matching a known session ID (stored in the craft_sessions table) to the cookie sent by the user's client.

If your sites share a root domain (e.g. mysite.com and other.mysite.com) then they will be able to share cookies. (You'd use the defaultCookieDomain setting to cause both sites to store cookies under the root domain.) That means the Slave site can read the user's CraftSessionId cookie, validate it with the Master site, and log the user in as appropriate.

If the sites don't share a root domain...

Alas, due to cross-domain restrictions, sites not sharing a root domain are not allowed to receive/read each other's cookies. Automatic multi-site login might still be possible, but it'd involve bouncing the user back and forth in a series of [hopefully imperceptible] redirects between Master and Slave that would accomplish something basically single-sign-on-y (Kerberos, anyone?). It'd be convoluted and weird and I don't have brainpower to think about it anymore tonight.  :-P


Looking at potentially 100,000 users/entries to capture data

We've seen Craft installs with several hundred thousand users/assets/entries running along fine.

You'll just need to be smart with how you're pulling data out on the template side and use caching where appropriate and you should be fine.


Can a field type detect if it's being edited inline in Craft 2.3?

You could check if the whole request is Live Preview using craft.request.isLivePreview.


Automatically add an insurrance for a product

You could write a custom Craft plugin (generate the boilerplate for it at https://pluginfactory.io) - and in that listen to the onPopulateLineItem event.

https://craftcommerce.com/docs/events-reference#commerce_lineitems.onpopulatelineitem

Then, check the purchasableId of the item being added to the cart - if it is the TV, then add the cable to the cart at the same time.  Something like this (untested):

craft()->on('commerce_lineItems.onPopulateLineItem', function($event){

    $purchasable= $event->params['purchasable'];
    $cart =  craft()->commerce_cart->getCart();

    if($purchasable->getPurchasableId() == 123){
        craft()->commerce_cart->addToCart($cart, 456);
    }
}

(See addToCart() in plugins/commerce/services/Commerce_CartService.php for the full function definition).

But the bigger question is should you do this....

Be aware that customers don't generally like this sort of behaviour in cart systems, and you should make sure that if you do do this, you at least take care to remove the accessories when they remove the main item from the cart.

Note that it soon gets complex, because can you be sure the accessory was added this way etc?

My advice is to instead use MultiAdd (https://github.com/engram-design/MultiAdd) to build a form that gives them the option of adding the accessories at the same time as the main product.  Like we do here for example:
https://imagescience.com.au/products/monitors/eizo-coloredge-cs2730


Craft CMS with Foundation 6 workflow / installation

They aren't silly questions. There are a number of ways you might want to do this. Personally what I do on Craft projects when I've used Foundation is use the SASS only version which has directions located here: http://foundation.zurb.com/sites/docs/sass.html

You'll need to be familiar with NPM or Bower and Gulp to get the most out of that. If you're not familiar with them, do some reading there first. I stick exclusively to NPM since I have some other projects that I use Node for as well. This is up to your preference.

I do my local install of Craft in my new repository, set up environment variables and then install on dev/production servers as well. If you have a deploy process to install, I normally do that next so I am done configuring servers. Then I will install any npm packages and set up my gulp build process. I recommend using a lot of the same packages they have in that project like browser-sync. I am surprised I don't see autoprefixer in there which I feel is essential.

Should I on my local machine install Craft, and change Foundation to build to that location?

Yes but when you install Foundation via the method I mentioned above, it will build wherever you set it to build in your Gulpfile.

Should I install them alongside each other (i.e. install Craft in the same folder as my Foundation)?

I would not install the Foundation files in your Craft folder. This could make updates tricky. The core files will be in node_modules folder in your root anyway. When you locate your SASS files to build, I normally put those in public in their own folder at the same level as the compiled CSS. You don't have to deploy them to your server but it's helpful in local.

Should I make a fresh installation of Craft somewhere else, and install Foundation within the Craft structure somehow?

I think the above info probably answers this. Basically all Foundation related content I locate in the public folder wherever you want to locate your assets (sass, js, images, css).


Craft 3 Ubuntu installation error via composer setup

[SOLVED] By upping wait_timeout from 10 to 100 in my.cnf - seems to have fixed the issue.

Initial google searches for "Error while sending QUERY packet" led me to increase max_allowed_packet size, but this didn't fix the problem alone.
Note, I'm not sure if both timeout and packet size need to be increased or just timeout nevertheless I have increased both and it works now.

Another side note on my server setup, the server I had problem with was a default Vesta CP ubuntu/mysql configuration. I had no problem installing Craft 3 on a different ubuntu 16 configured by Serverpilot.io.


You can fetch all elements and slice the last 3 elements from them

{% set entriesList = entry.fieldHandle.find() %}
{% set start = entriesList|length - 3 %}
{% set end = entriesList|length %}
{% set lastThree = entriesList | slice(start, end) %}

Of course you need to make sure you have at least 3 objects your array


How to pull the first block of its kind from a Matrix?

You could try something like this:

{% set block = post.buildStory.type('textEditorBlock').first() %}
    {{block.textEditor}}


How to plan IA for a directory of content thats good for admins and users

a) There are a definitely a few ways to handle this, but I would recommend the following. Put your Countries and Cities in a category group, nesting cities inside countries. Your Fishing Port section should be a Structure, since the entries are not time dependent like entries in a Channel. All you would need is a single category field that relates the fishing port to a city.

b) This can be done with custom routes, as long as each Fishing Port entry is related to one city category.

As for organizing the entries in the admin, you can make use of the modify entry sortable attributes and the modify entry table attributes hooks, which allow you to add an attribute in the sort dropdown and add different columns to the entry view. With this, you can add a City and Country column to the structure listing.


Trying to automatically add a product in Commerce

I tried this myself and it threw an error when there was no cart attached to the session.

I then found these two lines of code in the Craft Commerce CartController.php:

        // Get the cart from the current users session, or return a new cart attached to the session
        $cart = Plugin::getInstance()->getCarts()->getCart(true);

getCart() can be found in the Carts Service

Which lead me to try:

{% set cart = craft.commerce.carts.cart(true) %}

So if you pass in true, you force it to save the cart and thus you seem to be able to create a new (empty) cart.


How to pull a single podcast source, using the Matrix field and loop?

Try using the limit:1

{% for entry in block.podcastChannel.limit(1) %}


How to remove siteName from the og:title tag with SEOMatic

Yes, it's definitely possible to do that, but you'll need to use Twig as per: https://github.com/nystudio107/seomatic/wiki/10.-Dynamic-Twig-SEO-Meta

However, you might not want to do that. Sometimes it's nice to have the site name show up in the title that's shared via social media, to tie in the branding. Your call tho!


Getting the first and last days of a multi-day event - Solspace Calendar

Within the context of calendar.month, if you check out the demo templates, we style the beginning and end of multi-day events with the following code:

{% if event.multiDay %}
    {% set classes = classes ~ " multi_day" %}
    {% set classes = classes ~ (day.containsDate(event.startDateLocalized) ? " first_day") %}
    {% set classes = classes ~ (day.containsDate(event.endDateLocalized) ? " last_day") %}
{% endif %}


How can I route a specific entry in a structure section to its own template?

The cleanest way I have found to achieve this is to create a generic template for the structure as well as a custom template for each entry that needs one, ensuring that its path matches the entry's uri.

I then put this single line of code in the structure's selected template and it works beautifully:

{% extends [entry.uri, '_generic'] %}

This should work as well:

{% include [entry.uri, '_generic'] %}

If the entry's uri exists as a template route then it will be rendered, otherwise the generic template will be.

UPDATE:

I recently extended this on a site to allow for unique custom pages, custom entry type pages and a fallback generic page:

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


How do I install Craft in shared hosting that doesn't allow index.php in a sub-folder

It's not so much that you can't have the index.php file in a subfolder that might be an issue; it is that you can't put the craft folder above your webroot. If you have access to the folder that htdocs is inside, you should try putting the craft folder inside that. It might not be obvious, but there's no reason that the public folder has to be named public. It could be html, htdocs, or whatever. The point is that you're suppose to put the craft folder above this folder.

On many "simple" webhosts, you may not be allowed to do this, and you have to put the craft folder within your public folder. The security risk involved with doing this is, as the warning states, related to your craft folder being directly accessible through a webbrowser. For instance, you're db.php config file would now be "accessible" by going to http://yourdomain.com/craft/config/db.php. As long as your webserver is configured correctly, and there are no security holes to exploit, this doesn't really matter, since the file will be processed as php, and doesn't output anything.

But, in theory, if a malicious user manages to, for instance, crash the php process, he'd see the file as pure text. Or, if he's able to initiate a download of a file inside your public root, he'd be able to get to all your configuration files. But, then again, you might have problems even if the craft folder is outside of your public root.

If you can't put craft outside your public root folder, options 2) and 3) doesn't really help, they just introduce more problems. Just put the files in your public folder, and the craft folder, inside your htdocs folder, and reconfigure the path to craft in index.php to:

$craftPath = './craft';

Then, make sure that inside your craft folder the .htaccess file that comes with Craft is still there. It's suppose to say:

deny from all

This should, under normal circumstances, deny direct access to the craft folder.


Searching Number Field To See If It Falls In A Range

Remember: all parameters in your Query are set to null by default. They won't be parsed into an SQL statement until they have a valid value (not "", not NULL not 0) so you can just pass null values.

{% set range = null %}
{% switch craft.request.getParam('sf') %}
    {% case '1' %}
        {% range = ['and', '>=10000', '<=20000'] %}
    {% case '2' %}
        {% range = ['and', '>=20000', '<=30000'] %}
{% endswitch %}

{% set query = craft.entries
    .city(craft.request.getParam('city')) // <-- if city is null, nothing happens
    .buildingSQFT(range)
    .section('propertiesSection')
%}

Keep in mind query is currently only a Query and not an array of entries. You can still attach your parameters

{% if craft.request.getParam('status') %}
    {% set status = craft.categories.group('propertyStatus').slug(craft.request.getParam('status').one()) %}

    {% do query.relatedTo(['and', {targetElement: status.id}]) %}
{% endif %}

{% set entries = query.all() %}


How to output a structure sub-navigation of current page

You first have to get the corresponding entry in your "menu" structure and then build your navigation upon that item.

Use the relatedTo() parameter for that. The entry entry model that you automatically get on that single template is the targetElement of that relation, but you should be fine using the less specific element property used with the short syntax relatedTo(myEntryModel).

{# Get the (one!) related entry from the "menu" structure #}
{% set menuItem = craft.entries.section('menu').relatedTo(entry).first() %}

{# Check if there are descendant entries #}
{% if menuItem.hasDescendants() %}

    {# Get entries to build the sub-menu #}
    {% set subMenu = craft.entries.descendantOf(menuItem) %}

    {# Output the sub menu #}

        {% nav link in subMenu %}

                {{ link.title }}
                {% ifchildren %}

                        {% children %}

                {% endifchildren %}

        {% endnav %}

{% endif %}


Grouping Super Table Results via Twig Multidimensional Array

For grouping arrays, use Craft's group filter or index filter, then for joining the array items with commas use Twig's join filter.
Something like this might be what you're looking for (I've also added eager loading for improved performance, and I used the map filter as a cleaner way of organising the array, though you could use a nested loop if you find it easier to read):
{% set featuredPeopleGrouped = book.contributors
    .featured(1)
    .with(['contribution','contributor'])
    .all()|group('contribution[0]')
%}

<dl>
    {% for contributionType, rowData in featuredPeopleGrouped %}
        {% set contributors = rowData|map(row => row.contributor[0].title) %}
        <dt>{{ contributionType }}: </dt>
        <dd>{{ contributors|join(', ') }}</dd>
    {% endfor %}
</dl>


Remove related / suggested videos from the end of an embedded YouTube video

This answer isn't specific to the Dukt plugin, but I was going to suggest that passing in the parameter of rel with a value of0 would be the solution. Looking at the docs for YouTube embed shows this is no longer good as of Sept 25, 2018 though:

https://developers.google.com/youtube/player_parameters#rel

Basically, you used to be able to shut off related videos, but YouTube has removed that ability. You're stuck with them if you choose to use YouTube as your video provider.

  After the change [made on Sept 25, 2018], you will not be able to disable related videos. Instead, if the rel parameter is set to 0, related videos will come from the same channel as the video that was just played.


How can I add "and" between multiple categories assigned to an entry?

Loop through the categories of the entry, add and if it's not the last loop index, like so:

{% for category in  entry.categoryFieldHandle %}
 {{category.title}} {{ loop.last ? '': 'and '}}
{% endfor %}


How do I set where the user is sent from a custom login form?

You can set a variable in your craft/config/general.php. Here is the option taken from the default config file which is located here craft/app/etc/config/defaults/general.php:

Updated:

/**
 * The path that users should be redirected to after logging in from the front-end site.
 *
 * This setting will also come into effect if the user visits the Login page (as specified by the loginPath config
 * setting) when they are already logged in.
 */
'postLoginRedirect' => '',

From that page you can also easily route them to another page based on conditionals in twig using {% if something %}{% redirect url %}{% endif %}.

And although I haven't tried this personally, it looks like (based on this answer) you can redirect from within your plugin using the following (perhaps only from a controller however):

$this->redirect('/apply');

I also see in UserSessionService.php line 371 the following which should also work:

$url = UrlHelper::getUrl(craft()->config->getLoginPath());
craft()->request->redirect($url);


Is there a way to set a new property on an entry

You need to create the field in the CMS, however you don't need to assign it to any section. It should still be available to your plugin and twig templates.

The other reason your code will result in an error is because $entry->secretName is in fact being processed by the BaseElementModel magic getter method, so is the same as writing $entry->getFieldValue('secretName'), which obviously cannot be set to a new value.

The proper way to do this is using the setContent method as follows:

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

foreach ($entries as $entry) {
    $entry->setContent(array(
        'secretName' => urlencode($entry->title)
    ));
    craft()->entries->saveEntry($entry);
endforeach;

See this answer for a more thorough explanation on setting entry content.


How do I make the "%20" in URL's show as "-"

Edited

I just learned this, but as of Craft 2.4, there's a kebab filter which you can use here. This would probably give you the effect you're after, where the string "two words" would become "two-words", and punctuation characters are dropped by default.

You could use this like so:

{{ tag.title }}

Original

The %20 string is being added to replace spaces because you're using the url_encode filter on tag.title. A simple (though somewhat naïve) way to to solve this would be to use the replace filter:

{{ tag.title | url_encode | replace({"%20": "-"}) }}

However, you might still see some "%XX" characters in this string if the tag.title has other characters that url_encode might want to replace. The "%" character, for example, gets turned into "%25" after being run through url_encode.

This also probably won't do what you expect, as the link you're generating for the tag won't work unless you're doing special routing. The problem stems from the fact that tags don't have clean URL slugs at this time, so what you're doing right now is probably the best course of action.

You've probably already seen this, but there's a documentation page specially about linking to tags. This is the current best practice I know of. If you're really looking for cleaner slugs in URLs, I'd recommend using categories instead of tags if possible.


craft.commerce.orders returns an element criteria model that defaults to setting the criteria param isCompleted to true.

You would need to set isCompleted to false on the criteria to override it, in order to get someone else's cart from the front-end.


Is this a problem with only the dashboard, or the front-end also? Although this is a bad answer, waiting a few seconds to load the dashboard shouldn't be a huge deal. You will only need to access it a few times while editing content. On the other hand, there are ways to improve the front-end performance such as Eager Loading.

You can approach this conceptually by creating a simpler data model. In my opinion, super table inside of a matrix is a sign that you are doing something wrong. There is a reason that matrix in matrix is not supported, probably because of this performance issues like this.

You can also extend the dashboard or something to implement eager loading or cut down on db queries but I don't think it is worth the effort. Maybe you can mention something to Pixel and Tonic and they will improve this in a later version of craft.


Craft 3: Struggling with CRAFT_BASE_PATH and CRAFT_VENDOR_PATH

You should almost never have to change these from the default settings.

All you need to do is make sure your webserver points to your server root properly (in your case, /home/xxx/public_html/web/)

The default Craft 3 install has web/ as the server root, with Craft and all of it's files/folders in the root directory. If you don't change anything, the index.php that ships with Craft will "just work" once you've pointed the webserver at the web root.

In your case, it sounds like you're saying that you've moved all of Craft into a craft/ directory that sits alongside the web/ directory. Assuming you've moved everything into that folder, you'd do something like this in your index.php:

// Project root path
$root = dirname(__DIR__).'/craft';


When to use " is same as" and when to use "=="

Looking at the Twig code, they have this documented for the “same as” test:

  Checks if a variable is the same as another one (=== in PHP).

I won’t re-hash the details of === vs. == in PHP because there is an excellent comparison here: https://stackoverflow.com/questions/80646/how-do-the-php-equality-double-equals-and-identity-triple-equals-comp

The money quote is:

  == compares the values of variables for equality, type casting as necessary. === checks if the two variables are of the same type AND have the same value.

And here’s what the PHP docs have to say:

http://www.php.net/manual/en/language.operators.comparison.php


There is a reasonable chance that this is due to a specific plugin.

Try disabling all of your plugins, and see if the site is loading quickly again. If it is, try re-enabling each plugin one by one, to determine which of them may be causing the slowdown.

It's important to remember that your plugins actually get loaded on every single page load, both front-end and back-end.


How would I trigger updateElements for BaseElementIndex from a plugin's JavaScript?

The cleanest way to do this would be to provide your own Javascript class that overrides Craft.BaseElementIndex, which stores the latest search value in localStorage:

MyElementTypeIndex = Craft.BaseElementIndex.extend(
{
    onAfterHtmlInit: function()
    {
        // Do we have a stored search value?
        if (this.instanceState.searchVal)
        {
            this.$search.val(this.instanceState.searchVal);
        }

        this.base();
    },

    updateElements: function()
    {
        // Store the current search val in localStorage
        this.setInstanceState('searchVal', this.$search.val());

        this.base();
    }
});

// Register this as the official index class for this element type!
Craft.registerElementIndexClass('MyElementType', MyElementTypeIndex);

Save that file somewhere in your plugin’s resources/ folder (let’s say it’s craft/plugins/myplugin/resources/js/index.js), and then include it from your plugin’s init() function:

class MyPlugin extends BasePlugin
{
    // ...

    public function init()
    {
        if (craft()->request->isCpRequest())
        {
            craft()->templates->includeJsResource('myplugin/js/index.js');
        }
    }
}


How can I search for a user id in a custom field in my entries loop?

You would want to use the relatedTo() method of elementCriteriaModel.

{% set username = craft.request.segment(2) %}
{% set author = craft.users.username(username).first() %}
{% set entries = craft.entries.section('blog').relatedTo(author) %}

If you need both then you may need to merge the results.

{% set username = craft.request.segment(2) %}
{% set author = craft.users.username(username).first() %}
{% set authorEntries = craft.entries.section('blog').authorId(author.id).find() %}
{% set coAuthorEntries = craft.entries.section('blog').relatedTo(author).find() %}

{# merge authorEntries and coAuthorEntries #}
{% set entries = authorEntries|merge(coAuthorEntries) %}

Not tested. But should be close. Update: added .find() which will convert the elementCriteriaModel to an array so that the merge filter works properly.


Order a list of categories by number of entries, key not being set

If you encounter problems with assigning variables as the keys, what about putting all your values into objects?

{% set catObjects = [] %}

{% for cat in allCats %}

    {% set catObject = [
        { catId: cat.id, catCount: craft.entries.relatedTo(cat).total()  }
    ] %}

    {% set catObjects = catObjects|merge(catObject) %}

{% endfor %}


.limit(1) on asset field loop is effecting another loop on same page

Yes, this is actually the intended behaviour, and one that changed when going from Craft 2 to Craft 3. You can read more about it in the Changes in Craft 3 article in the docs. projectImages in this case which is an Asset field (I assume), returns an ElementQuery when you access it with projectPanels.projectImages. The field doesn't automatically return a new model when accessed, it returns the reference to the one and same query.

The workaround is to either reset the parameters you need to reset, like limit() in this case, or use clone as described in that article.

Ie, either something like this:

{% for asset in projectPanels.projectImages.limit(1).all() %}
   ...
{% endfor %}

{% for thumbs in projectPanels.projectImages.limit(null).offset(1).all() %}
   ...
{% endfor %}

Or,

{% for asset in clone(projectPanels.projectImages).limit(1).all() %}
   ...
{% endfor %}

{% for thumbs in clone(projectPanels.projectImages).offset(1).all() %}
   ...
{% endfor %}

Have stumbled on this quite a few times myself alreadym, it's a bit confusing. Hope this helps though. :)


Matrix blocks are saved in the order they appear in the POST data when saving the "owner" element (the entry, or whatever). So just adding jQuery Sortable to your front-end form will be enough.


Is it possible to get a transformed image's file size (bytes, not dimensions)?

I don't think it's possible to get the file size of an image after it's been transformed with Craft's out-of-the-box image transforms.

It looks like the .size property only works on the original asset file.

Even if you did something like this...

{% set image = entry.myAssetField.one() %}

{% set thumb = {
    mode: 'crop',
    width: 100,
    height: 100,
    quality: 75,
    position: 'top-center'
} %}

{% if image %}

  {% do image.setTransform(thumb) %}

  {{ image.width }} x {{ image.height }} = {{ image.size }} bytes

{% endif %}

.height and .width properties will show the transformed dimensions (100 x 100), but .size outputs the original file size, not the transformed size.

I'm guessing this might be a problem for Craft because the transformed image may not have been generated before the template is rendered.

Update

The Imager plugin has a getSize method that should work. Imager transforms are created before the template is rendered.

getSize($unit='b', $precision='2') [float]

{{ image.getSize() }} B
{{ image.getSize('k') }} KB
{{ image.getSize('m', 10) }} MB
{{ image.getSize('g', 10) }} GB


When I had this problem previously, it was the permissions for the index.php that wasn't set to 644.

Another issue I had once was my database password had some characters that wasn't accepted. Instead of getting errors, I just got a blank page saying Internal Server Error. Once I changed the password to omit weird characters, it worked perfectly. Wasted a ton of hours trying to figure that out.

Good luck!


Sounds like Matrix is the right field for your situation. The harder part is how you decide to implement this on the front-end.

While your question doesn't say it explicitly, it is tagged plugin-sproutforms so I'm going to base my answer on how to approach this using Sprout Forms.

Option 1: Custom HTML

If you only have a single form, building things out in Custom HTML might be worth considering.

You can grab the SproutForms_FormModel using the form tag:

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

Or use the displayTab and displayField tags to output parts of your form that you don't need additional control over.

You would need to build out support for the Matrix Field manually, but Sprout Forms can receive data for a Matrix Field that is built in the proper format.

Option 2: Matrix Custom Field Plugin

If you need to reuse your form in multiple places, or have the need to reuse Matrix fields on the front-end, you might consider building a Custom Field for Sprout Forms that adds front-end Matrix support.

There are several examples in the Sprout Fields plugin of how to add front-end Custom Field support for Sprout Forms, it would just require a bit more work to take into account all the scenarios that a Matrix field could present.  Depending on your scenario, you might be able to simplify things and just add support for the fields you need to start off, and add more complexity as the need arose.


"Forbidden" response when saving entry with HTML code in matrix field

There are a couple possible explanations for this.

You have Suhosin installed, a PHP extension that enhances security by placing some pretty drastic restrictions on what data can be sent to the server. To find out, visit /admin/utils/phpinfo and search the page for "suhosin". If you see any references to it, you have it installed. Ask your host to uninstall it, or possibly just configure it to be allow this specific use case (and expect to have similar requests in the future).
Your server (Apache or nginx) may be configured to deny traffic to the request URI. Check your server logs for recent forbidden/403 activity.


(There can be only one code on a cart at any one time)

Here's some example code that does the sort of thing you want:

{% if cart.couponCode %}
    {% set coupon = craft.commerce.getDiscountByCode(cart.couponCode) %}
{% endif %}

{% if cart.totalDiscount or coupon is not null %}

    {% for adjustment in cart.adjustments if adjustment.type == "Discount" %}
        {% set isCoupon = adjustment.optionsJson.code ? true : false %}

    Applied:

        {% if isCoupon %}

                        Coupon: {{ adjustment.optionsJson.code }}

                    {{ adjustment.description }}

            {% set couponPrinted = true %}
        {% else % }
            etc...


Access logged-in user info when `baseCpUrl` is different than `siteUrl`

My guess is you probably need to add the root .domain.com to the defaultCookieDomain config setting.


Craft Installation - Getting a Forbidden Error

When I get this error it is usually because the top craft directory isn't readable. When you unzip the original craft zip file, you get something like this:

drwx------@ 5 m  staff       170 Jul  8 14:47 Craft-2.1.2563

And that top directory (the one that contains craft and public) needs to be readable by apache.


How to get related entry data using ElementCriteriaModel in a plugin

This is called Eager Loading you have to do

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->order('postDate asc');
$criteria->dateUpdated = array('and', '>=' . $this->dateFrom->format(DateTime::MYSQL_DATETIME), '<=' . $this->dateTo->format(DateTime::MYSQL_DATETIME));
$criteria->limit('null');

// ========== start change ===============
$criteria->with = ['video'];
// ========== end change  ================

$responses = $criteria->find();

Edit: One of the most important things when working with Craft.

  I can see that $response->video is an array containing the sourceElement id and field id.

No, no and no. Many people believe this, or many other things, but that is not true. Your $response->video will contain an object of type ElementCriteriaModel. $respose->video is exactly the same type of object like $criteria = craft()->elements->getCriteria();

Thus you can do

$videos = $response->video->order('')->limit(2)->find()


Is there a way to see the total number of entries in a channel in the CP?

You're looking for the Content Stats plugin by Will Browar:

  https://github.com/wbrowar/contentstats


Use only the sub categories from a category input

You could set the level parameter according to your requirements:

{% set cats = craft.categories({
    relatedTo: {
        sourceElement: entry,
        field: 'categoryInput'
    },
    level: 2,
    limit: null
}) %}


How to display entries relevent to date assigned

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])
%}


How to define table attribute in the record file

So how can I define table's attributes in Craft 3 to complete following code:

You don't.
By just defining the table name, that gives Yii enough information to reflect on the underlying columns for the table.
You can add property doc blocks to get type-hinting in your IDE for the columns.
Probably also worth reading: https://docs.craftcms.com/v3/extend/updating-plugins.html


Selecting entries using a checkbox field

Reply via Brandon:

A single field does probably make more sense here, as you have it.

You're close to getting it working, but your syntax for filtering categories based on craft.locale is wrong. Checkboxes fields store the selected option values as JSON, so if you check your craft_content table you will find values like

["option_1","option_2","option_3"]

When you do this:

.regionAvailability(craft.locale)

it is looking for a row where the regionAvailability value is equal to craft.locale. Even if only a single locale is checked, its value would still be ["locale_id"], not just locale_id.

So to fix this just change that parameter to:

.regionAvailability('*"'~craft.locale~'"*')

or

.regionAvailability("*\"#{craft.locale}\"*")

(both do the same thing; just pick the one you think is more clear)


amforms - Specific entry in the notification template

You can get the value of a specific field by using

{{ submission.fieldHandle }}

E.g.

{{ submission.fullName }}


Not really anything to do with Craft or the ElementAPI plugin, but you can enable CORS at the Apache/.htaccess level easy enough.


You should be using it like this:

public function onAfterElementSave()
{
    $fieldHandle = $this->model->handle;
    if ($this->element!==null) {
        $myData = $this->element->title; // just an example
        if ($this->element->$fieldHandle != $myData) {
            $this->element->getContent()->setAttribute($fieldHandle, $myData);
            craft()->elements->saveElement($this->element);
        }
    }
}


How can I get an entry based on category and slug (via url segments)?

An entry is not related to itself, but that's what you added as a filter.
Use the slug parameter instead.

{% set categorySlug = craft.request.getSegment(2) %}
{% set productSlug = craft.request.getSegment(3) %}

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

{% if categorySlug|length %}
    {% set category = craft.categories.group('toys').slug(category).first() %}
    {% if category %}
        {% set entries = entries.relatedTo({ targetElement: category }) %}
    {% endif %}
{% endif %}

{% if productSlug|length %}
    {% set entries = entries.slug(productSlug) %}
{% endif %}

{% set product = entries.first() %}


Getting blank screen at domain.com/admin when installing

I'm documenting this here in case it helps someone else. My white screen during installation was caused by a missing comma between array items in my general.php and db.php config files. Once I added it, the install page loaded correctly.


Split posts by days similar to Product hunt

This seems like a good use case for Craft's group filter. I'd recommend reading this page to get a better understanding of how it's used.

Not quite sure what your code looks like but I'll pull this from the above mentioned support article for explanation purposes:

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

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

{{ month }}

        {% for entry in entries %}

{{ entry.getLink() }}

        {% endfor %}

{% endfor %}

So obviously you'd need to adapt this code to your setup, for example, switch out the month grouping for a per-day grouping. Then what I would do is wrap it in a conditional that only works if the postDate is more than three days older than today's date. Then you could use a similar query as the above to display the first three sections individually, (i.e. one for "Today", one for "Yesterday", and one for the day of the week of the third day).

Might not be the best way (curious to see any other answers that might show up), but just the first thought that came to mind from a quick perusal of your question.


Batch assigning a certain category to existing entries

You could build a plugin to grab all the entries and then assign that new category to it that way.

This bit of code is untested but this should do it, since this is a one off thing, you can get away with just doing it in chunks by setting the limit and offset, otherwise I'd recommend creating this as a Task:

// get all the entries
$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = "news"; // just using news as an example, be sure to set the appropriate section handle
$criteria->limit = null;
// $criteria->offset = null; // you could use the limit and offset field to do this 200 at a time, to avoid it timing out on you
$criteria->status = null;
$entries = $criteria->find();

$categoryIds = array(222); // this would be the new category (its ID) that you want to assign to the entry, be sure to set the correct ID here

for($i=0; $i<count($entries); $i++)
{
    $entry = $entries[$i]; // for easier reference

    // lets get the existing categories that are assigned to this entry and add it to the new category ID you set a few lines above, if there are no existing categories for this, then you can remove this for loop entirely
    for($c=0; $c<count($entry->categoryFieldHandle); $c++)
    {
        $categoryIds[] = $entry->categoryFieldHandle[$c]->id; // be sure to replace categoryFieldHandle with the category field handle for the category field
    }

    // setting the categories to this field, be sure to replace categoryFieldHandle here too
    $entry->getContent()->setAttributes(array(
        'categoryFieldHandle' => $categoryIds,
    ));

    craft()->entries->saveEntry($entry); // save the entry
}


Uploaded assets not saving in specified local folder

This answer really belongs to Brad, as he mentioned it to me. But he doesn't really need the points, does he ;)

What you want in that slot is {owner.slug}, and then the proper sub-path will ensue.

The explanation is deep in the doc page at: http://buildwithcraft.com/docs/assets-fields

To wit,

  "Note that if you are creating the Assets field within a Matrix field,
  the source element is going to be the actual Matrix block, not the
  element that the Matrix field is being created on. So if your Matrix
  field is attached to an entry, and you want to output the entry ID in
  your dynamic subfolder path, you would type {owner.id} not just {id}."


Check Structure for Entry or Child of Entry

The containment operator in does have problems working with whole element models. I'd try and modify your conditional to compare entry IDs instead:

{% if entry.id in craft.entries.section('general').descendantOf(42).ids() %}
    ...
{% endif %}

To include that parent page ID to that array you would do that with the merge filter:

{% set aboutPagesIds = craft.entries.section('general').descendantOf(42).ids() %}
{% set aboutPagesIds = aboutPagesIds|merge([42]) %}


When using the for loop you don't need to set image as it is set in the for loop. You will also need to define transformedImage on every iteration, so move it within the for loop.

Give the below a go, it should do what you are after.

    {% for image in entry.bannerSlider %}
      {% set transformedImage = craft.imager.transformImage(image, { width: 1420, height: 550, position: image.focalPoint,  effects: { grayscale: true, gamma: 1.5}, }) %}

    {% endfor %}


How do I create a list of categories and display the entries relating to each of these categories that have at least one entry?

Here's the link to relevant the "Get Help" article:

How do I list the entries related to a category?

These really helpful tutorials are somewhat hidden on builtwithcraft.com, I overlooked them more than once while thought the normal Documentation articles!

Just use the provided example code in your category loop:

{% set entryIds = craft.entries.section('partners').ids() %}
{% set categories = craft.categories.relatedTo({ sourceElement: entryIds }).groupId(2).find() %}

{% for category in categories %}

{{ category.title }}

            {# Fetch all of the entries related to this category #}
            {% set entries = craft.entries.relatedTo(category) %}

            {# Output them #}
            {% for entry in entries %}

{{ entry.title }}

            {% endfor %}

{% endfor %}


How can I populate a user field via a plugin?

Relational fields are always stored as arrays, even if you are limiting them to only one entry so this should work:

$entry->setContentFromPost(['userAccount' => array($user->id)]);


If you've copied the Craft files to a new place on the server, then you can either leave the database connection information the same and have it keep on pointing to the current database instance, or edit craft/config/db.php (or your .env file if that's how you're setup) to point to a new database instance.

If the new one is a blank database, the Craft installer will want to run.  If you want a copy of your old database on the new one, you'll need to do a database dump from the old one and restore it to the new one.  If you do the latter and you're still getting Craft to prompt to run the installer, then there is something wrong with the database connection settings between Craft/PHP and the database and those should be adjusted.


How can I add banner slider on my home page?

In Craft, you as developer are responsible for everything that is front-end output. So if you want a banner slider, you'll have to add a field on the entry where you can add images, and in your entry's template, write the needed html to display the banner.

I'd suggest going trough the documentation to learn about entries, fields,templates, ... Also you could check out the courses at for example mijngo but those aren't free.

As carlcs pointed out, here are some popular options for displaying banners:

Slick
Flickity


Verbb Comments plugin - Can't see comment replies

I remember having to modify the javascript and templates quite extensively to get it working. Take a look at the code and find out what needs fixing. If you're using Bootstrap you can modify the template so that it uses Bootstraps buttons and dropdowns, and that's how I got it working nicely.


Sorting SuperTable Field with SuperSort?

I think you need to add 'object' to your syntax if you're using {{ }}:

{{ object.tourDates.tourDate }}

Else it's { tourDates.tourDate } I believe.

See the plugins docs under Advanced sorting.


Dates for entries are in different language

If you go to that user's account settings, then click on the "Preferences" tab, I'm guessing they have their "Formatting Locale" setting set to Cymraeg.


Craft CMS - Dynamic Order Field Layout In Sections

Correct, there's no way out of the box to reorder fields on the fly in the Entry editor. If you're comfortable with Craft plugin development though I'm sure it's possible to extend Craft and add the ability to do what you want to do. It's also possible that someone has already developed a plugin that achieves this but I personally don't know of any off the top of my head.

However, it seems in your case that if you want to be able to rearrange fields in the Entry editor, your best solution is to put those fields inside a Matrix field. When you have created several fields inside a Matrix field, those "sub-fields" are then arrangeable in whichever order you prefer in the Entry editor. If you've never used Matrix fields before, here's the documentation link so you can read up on it: https://craftcms.com/docs/matrix-fields

Another alternative would be to use the Neo field - if you decide to use Neo, you can just reference your currently existing fields and then add only the Neo field to the page.

Hopefully that helps to answer your question. Cheers!


Just starting to evaluate - some basic questions

For 1), check out Dukt's oAuth plugin for Craft.  Not aware of any Active Directory plugins that have been written yet and not seeing any on here, either.

The second question is pretty subjective, so not sure if that's the best type of question for SE, but I'll let someone else take a stab at it if they want.


How can I add a dynamic number of rows to a matrix via plugin?

There are a few threads on this that I found helpful. I think they are all worth looking at and reading through as my solution may vary based on your needs.

Save matrix block to new entry in plugin
How to save a matrix content of a new entry in my plugin?
Saving new Matrix data

Here is the complete solution that ended up working. If there are improvements, please let me know so I can update the thread accordingly!

twig

<form method="post" accept-charset="utf-8">

    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="profile/saveProfile">

    ...

    {% if profile.awardsHonors|length %}

            {% for block in profile.awardsHonors %}

                        <input type="hidden" name="fields[awardsHonors][{{ block.id }}][type]" value="award">
                        <input type="hidden" name="fields[awardsHonors][{{ block.id }}][enabled]" value="1">

                        <input type="text" name="fields[awardsHonors][{{ block.id }}][fields][award]" class="form-control" value="{{ block.award }}">

                {% set blockId = block.id %}
            {% endfor %}

    {% else %}

    {% endif %}

    ...

    <button type="button" id="addAward">Add Award</button>

    ...
</form>

javascript

var awardBlockNum = 1;

$('#addAward').click(function (event) {
    var markup = '
' +
        '
' +
        '<input type="hidden" name="fields[awardsHonors][new' + awardBlockNum + '][type]" value="award">' +
        '<input type="hidden" name="fields[awardsHonors][new' + awardBlockNum + '][enabled]" value="1">' +
        '<input type="text" name="fields[awardsHonors][new' + awardBlockNum + '][fields][award]" class="form-control">' +
        '
' +
        '
';

        $(markup).appendTo($('#awards'));

        awardBlockNum++;
    });

MyPluginController

public function actionSaveProfile()
{
    $criteria = craft()->elements->getCriteria(ElementType::Entry);
    $criteria->section = 'mySectionHandle';
    $criteria->limit = 1;
    $criteria->status = null;
    $criteria->authorId = craft()->request->getPost('userId');

    $entries = $criteria->find();

    if ($entries) {
        foreach ($criteria as $entry) {
            $awardMatrixData = array();

            /**
             * New matrix fields are prefixed with 'new1'.
             * To compensate we need to create a new increment variable to correctly store the matrix number
             * while looping the correct amount of times.
             */

            foreach (craft()->request->getPost('fields.awardsHonors') as $key => $value) {
                $i = 1;

                if ($key == 'new' . $i) { // Check for a new award.
                    $awardMatrixData['new' . $i] = array(
                        'type' => 'award',
                        'enabled' => true,
                        'fields' => array(
                            'award' => craft()->request->getPost('fields.awardsHonors.new' . $i . '.fields.award')
                        )
                    );
                } else { // Update existing awards.
                    $awardMatrixData[$key] = array(
                        'type' => 'award',
                        'enabled' => true,
                        'fields' => array(
                            'award' => craft()->request->getPost('fields.awardsHonors.' . $key . '.fields.award')
                        )
                    );
                }

                $i++;
            }

            $entry->setContentFromPost(array(
                ...
                ...
                'awardsHonors' => $matrixData
            ));

            if (craft()->entries->saveEntry($entry)) {
                // Profile updated!;
            } else {
                // Could not save entry (profile).
            }
        }
    } else {
        // No entry found that matches criteria.
    }
}


How can I have a dynamic uri segment for a single?

Assuming that a Single is the right thing to use here (and I'm not sold on that but I trust your judment) the following is what I would do to accomplish what you want.

// config/routes.php
'offers/listing/(?P<entryId>\d)' => 'listing/make-offer'

// templates/listing/make-offer.twig
{% set entry = entry is defined ? entry : craft.entries.id(entryId) %}

Explanation

First, we create an Advanced Route to make the (single) entryId available to the template that will be loaded when the route matches.

Then, in the template, we set the entry variable to either the entry that Craft makes available to the template via its own routing or we fetch one explicitly by using the entryId passed to the template from the custom route. Our route may take precedence over default routing, so the entry is defined check may be removed and rely instead on craft.entries.id(entryId)

End Result

You should be able to navigate to /offers/listing/1234, which will load the templates/listing/make-offer.twig template with the entryId set to 1234.

Hope this helps, feel free to follow up if something isn't clear.


Staging / Production w/ common DB and Assets

Database

I strongly discourage you from letting Staging and Production share a database. That is a recipe for disaster.

Your Staging site should be semi-disposable, and it's likely that your client will treat it as such. The last thing you want is for someone to make a change in Staging without realizing that they are also making the same change in Production. It's counterintuitive and dangerous.

Instead, have your Staging database be a copy of the Production database. There are several ways to do this, and with the right tools it is actually very simple to clone the database on a regular basis.

I'd recommend using Craft Scripts by Andrew Welch to quickly & easily clone the database. I've found the pull_db.sh script to be quite invaluable.

Image Assets

Cloning images is also possible (Craft Scripts has a tool for that), but you may find that it eats up a ton of server space. This, of course, only matters if you're storing your asset files in a local folder. If the assets are cloud-based, then cloning them is a non-issue.

So instead of cloning your image files, I highly recommend the new Untransform plugin by Ben Croker. It allows your Staging server to actual use transformed images directly from the Production server. There won't be any need to store a second copy of those files, the Production site can provide the originals to your Staging environment.

Local Dev

You can use both of the aforementioned techniques on your local machine as well. Indeed, that's what they were originally created for.


Use the Kint plugin to spit out the current tab to the JS debug console:

{% for tab in layout.tabs %}
    {% if tab.name == "Detaljer" %}

        {{ j(tab) }}

        {% for field in tab.getFields %}

You will see that the structure of the tab-object actually contains the UI-elements as well:

Instead of looping tab.getFields, loop tab.elements:

{% set layout = product.getFieldLayout() %}
{% for tab in layout.tabs %}
    {% if tab.name == "Detaljer" %}

        {% for element in tab.elements %}
            {% switch element.className() %}

            {% case "craft\\fieldlayoutelements\\Heading" %}

{{ element.heading }}

            {% case "craft\\fieldlayoutelements\\CustomField" %}

                {% set field = element.getField() %}
                {% if not product.getFieldValue(field.handle) is empty %}

                    {% switch field.className() %}
                        {% case "craft\\fields\\PlainText" %}

                                {{ field.name|t }}
                                {{ product.getFieldValue(field.handle )}}

                        {% case "craft\\fields\\Categories" %}

                                {{ field.name|t }}

                                    {% include 'commerce/_blocks/categoryList' with { categories: product.getFieldValue(field.handle) } %}

                        {% case "craft\\fields\\Dropdown" %}

                                {{ field.name|t }}
                                {{ product.getFieldValue(field.handle).label}}

                        {% case "craft\\fields\\Lightswitch" %}

                                {{ field.name|t }}
                                {% include 'commerce/_blocks/fieldLightswitch' with { data: product.getFieldValue(field.handle) } %}

                        {% case "craft\\fields\\Number" %}

                                {{ field.name|t }}
                                {{ product.getFieldValue(field.handle) }} {{ field.suffix }}

                        {% default %}

                                {{ field.name|t }}
                                {{ field.className() }}

                            {# todo: other non implemented fields #}
                    {% endswitch %}
                {% endif %}

            {% endswitch %}
        {% endfor %}

    {% endif %}
{% endfor %}


You can use something like this in your main layout template. You'll need to add a filter after "segments" to get the URI segment you want, e.g.: |first or |last, and you'll also need to |replace any hyphens and then |capitalize the first letter of the result.

{% set myUri = craft.app.request.segments %}

<title>{{ myUri }}</title>

For more on segments: https://docs.craftcms.com/v3/changes-in-craft-3.html#template-functions


I don't think this is particularly Craft CMS specific problem. Just Apache .htaccess rules in general. I would try to search for and answer of Stack Overflow. I have a strong feeling it's been answered there already.

https://stackoverflow.com/questions/7780859/htaccess-rewrite-to-force-trailing-slash-at-the-end


What you want is the deferPublicRegistrationPassword config setting.

By default, Craft will require a 'password' field to be submitted on front-end, public user registrations. Setting this to true will no longer require it on the initial registration form.
If you have email verification enabled, new users will set their password once they've clicked on the verification link in the email. If you don't, the only way they can set their password is to go through your "forgot password" workflow.


I had to download the CA certificate for curl :

https://curl.haxx.se/docs/caextract.html

And added it to php.ini to get the plugin store working on my local dev env.

curl.cainfo = "x:\path\cacert.pem"

[IIS windows as local dev env]


Assets Content/Fields not available until entry is saved?

This is an issue with Assets fields if you have a dynamic Subfolder path set on in the field’s settings. It’s been fixed for 2.3 though ;)


Private plugin distribution in Craft CMS 3

I haven't done this with Craft, but with another Laravel project I was able to add a package from a private repo as follows:

1) In composer.json, after the require array, add a new repositories array:

"require": [
  ...
],
"repositories": [
  {
   "type": "git",
    "url": "https://github.com/YOURGITHUBNAME/your-repo.git"
  }
]

2) In the "require" array, add your package:

"require": [
  "YOURGITHUBNAME/your-repo": "*",
  ...
]

3) Here's the under-documented trick you need to do to get it all working: you must create a "release" in github, and the release name must be the letter "v" followed by the version number (e.g. v1.0.0). So go to your repo, click the "Releases" link, then click the "Draft a new release" button. It asks for a tag version and a Release title... I can't remember which of these is the critical one, so just put v1.0.0 into both of these fields.

4) Now if you run composer install it should (hopefully) bring in your private repo package. Might need to provide some kind of authorization / API key to access it if it's a private repo (I'm not sure how exactly this works, but should be fairly easy to find documentation on).


Currently, no.  But it is on our radar and we've got some ideas on how we'd want to implement it.


Pulling in Structure entries as radio button options in Sprout Forms

Sprout Forms v2.3.0 adds front-end field support for Entries, Categories, and Tags Relations fields.

By default, the fields will display a multi-select field on the front-end. If the limit is set to 1, they will display a dropdown field.

The fields are designed to work for simple use cases. If you have hundreds or thousands of entries, you may need to consider a custom front-end field type for your scenario.