sendEmail messages not being received by Exchange server

I agree with Damon.  I use Exchange and haven't noticed any issues with receiving emails (either from Craft system or withing my own plugins).  I am using Mandrill to send them.  You might have greater success if you fill out more of the model (fromEmail etc) but more than likely it's using php mail that is the core issue.

Email deliverability is these days a complex thing, much better to leave it to the experts, although for best results even then you still need to pay attention to your Mandrill & domain setups (SPF/DKIM etc).


Images not showing after Craft 2 to 3 migration

I did manage to fix this problem with some support, however I now have a few other problems as a cause of this and I think it is best to try and start a fresh post on it. I had to change the baseUrl which fixed the error but now have broken images across the website and duplicate folders in my assets. Argghh.


We were experiencing this issue, as well as the randomly logging out issue.

As I answered on that question, we fixed both these issues by switching session.auto_start to Off

session.auto_start = 0


Escaping characters in entry.postDate|date

You'll actually need to add a double backwards slash in front of each character you want to escape, i.e.

{{ entry.postDate|date('jS F Y \\a\\t ga') }}

See the official Twig docs for more info.


Last header tag overwrites any previous tag with same name - expected behaviour?

It looks like if you want to specify multiple values for a link header, you need to comma separate them: https://stackoverflow.com/a/25416118/684


PHPUnit testing for Craft plugins or controllers

There is a good example of testing with PHPUnit in a Craft plugin here: https://github.com/selvinortiz/craft.loath and an example on how to stub out the global craft() class here: Stub out the global craft() object for PHPUnit tests

Note that testing in Craft should be a lot easier/convenient in the upcoming Craft 3.


How can I add custom validation rules to my plugin settings?

The gist is, you will need to give your plugin a dedicated Settings model class, where you will have complete control over its validation rules and errors.

Here’s how to set that up:

Create a new Model class whose defineAttributes() method is based on your plugin's current defineSettings() method.

class Foo_SettingsModel extends BaseModel
{
    protected function defineAttributes()
    {
        return array(
            'settingName' => AttributeType::String,
            // ...
        );
    }
}

Give your plugin a getSettingsModel() function (instead of defineSettings()), which will return a new instance of your settings model.

class FooPlugin extends BasePlugin
{
    // ...

    protected function getSettingsModel()
    {
        return new Foo_SettingsModel();
    }
}

To perform the actual validation, you have two options:

Create a custom validator class (see the ones in craft/app/validators/ for examples), and make your plugin’s rules() method include it:

class FooPlugin extends BasePlugin
{
    // ...

    public function rules()
    {
        $rules = parent::rules();
        $rules[] = array('settingName', 'Craft\Foo_CustomValidatorClassName');

        return $rules;
    }
}

Override your plugin’s validate() method and just perform the validation directly from there:

class FooPlugin extends BasePlugin
{
    // ...

    public function validate($attributes = null, $clearErrors = true)
    {
        $validates = parent::validate($attributes, $clearErrors);

        if ($attributes === null || in_array('settingName', $attributes))
        {
            // Perform custom validation here
            // ...

            if (!$settingNameValidates)
            {
                $this->addError('settingName', 'Error message');
                $validates = false;
            }
        }

        return $validates;
    }
}

Finally, the last thing you’ll need to ensure is that your plugin’s settings template is actually showing the error messages associated with this setting:

{{ forms.textField({
    ...
    errors: info.getErrors('settingName')
}) }}


Show content if logged in user is in userGroup1 OR userGroup2?

According to the docs, the userModel.isInGroup() method takes a "user group model, its handle, or ID" – in other words, it can only test for membership in a single group, not several.

What you want in this case is the or operator:

{% if currentUser and (currentUser.isInGroup('userGroup1') or currentUser.isInGroup('userGroup2')) %}
    {# secret content #}
{% endif %}


Channel index page linked in a structure

The reason why there's no automatically set url and template for channel index pages is because Craft tries to be as flexible as possible. In many use cases for channel sections you don't want a index page at all or you could also need multiple of them.

But you already chose a very solid approach by setting up a structure section for your navigation. And no, you don't have to create useless singles for each channel index. I'd rather use entries of that structure itself and would just set up a new entry type for your indexes. See this article in the Craft Docs on how to link templates to entry types.

I think it's really nice to have all the site main pages together in one structure. You can easily add fields to your index pages and have your client edit all this rather static content in here.

You can also add you home page to that structure. See this Q/A for how to do that.


The init method is automatically called whenever a service class is instantiated. Think of it as akin to a constructor.

If this is not want you want, just rename the method (to initialize, for example).


I ended up doing it this way:

$records = Plugin_CollectionSectionRecord::model()
        ->findAllByAttributes(['collectionId' => $collectionId]);
}

$ids = [];
foreach($records as $value) {
    $ids[] = $value->sectionId;
}

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->elementType = 'Entry';
$criteria->sectionId = $ids;
$result = $criteria->all();
return $result;


Table 'craft.craft_elementindexsettings' doesn't exist

This issue has actually come up before in the Slack channel...

  "That’s a brand new Craft 2.5 table, so you’ve probably tried to do the update before, it failed and you didn’t clear out all tables from the database when you retried." - Brad Bell

As you mentioned, your auto-update did not complete successfully. So the logical next step (as you tried) is to perform a manual update.

But here's the catch... Before you made your 2nd (manual) attempt, you didn't roll back your database. Between attempts, you would need to drop all tables and re-import your original database SQL file.


Correct behaviour for 'Login as user' when user cannot access CP?

You're correct... it should be sending to postLoginRedirect, but there is actually a bug where after impersonating:

// Get the current user
$currentUser = craft()->userSession->getUser();

is returning a cached copy of the previous user (who had CP access).

Will get that fixed for the next release.

Update:

Fixed for the next post 2.2.2596 release!


How do you pull out second level structure entries based on their parent?

Ok I've worked this out from above and adding some other code and now it works as below:

{% set parent = entry.getAncestors().first() %}
{% for subnav in craft.entries.section('mySection').descendantOf(parent).descendantDist('1') %}

{{ subnav.title }}

{% endfor %}

Hope this assists someone else.


Easiest way to make a table in the Control Panel sortable?

As far as I'm aware, there is currently no native way to do this without using an ElementType index page.

You have two options for implementing this yourself. One is to sort the table on user input with the help of the registerCpRoutes hook (with AJAX or by reloading the page). The other is to use a JavaScript solution, as discussed in this question.


entries.saveEntry not triggered by ResaveElements task

It's because the ResaveElements task calls craft()->elements->saveElement() directly, instead of going through the element specific save method (like craft()->entries->saveEntry().

You'll want to listen to the onSaveElement event, check that it's an Entry you're looking at, then perform any specific business logic that you need.


The template path specified in the admin should point to a single file, not a directory.

index files are only loaded when the Craft router falls all the way through element URIs and starts to look in your templates folder—in your case, that would be local.domain.com/pupil-of-the-month.

I suspect the confusion lies in that the router does eagerly look inside a directory for index.* templates in some narrow cases (as stated above), but that the pattern is not adopted by Category/Section/Tag settings.

See the official Routing Docs for more information about the process Craft uses to resolve the template for a request.


How to edit the Date Created value that appears on the Entry page

If you want to edit that value in the database, then you'll need to find the corresponding row in the craft_elements table and update the dateCreated column there.


Including two category slugs in entries’ URLs

Set your section’s Entry URL Format to this:

{mySection.first.slug}/{myCategory.first.slug}/{slug}

mySection.first and myCategory.first will give you the first-selected category within those respective fields. Adding .slug after that will give you the slug of those categories.

If either of those category groups can have nested categories, you may want to change .first to .last, so the last-selected category’s slug makes it into the URL rather than the first one.

Category URLs

If you would like to make category URLs like this:

mywebsite.nl/employees/health

you will need to create a new Route in Settings → Routes, with the following URL pattern:

and point the route to whatever template you want to load when these URLs are matched.

Then from your template, you can fetch the actual category models like this:

{% set mySection = craft.categories({
    group: 'mySection',
    slug: matches[1]
}).first() %}

{% set myCategory = craft.categories({
    group: 'myCategory',
    slug: matches[2]
}).first() %}

{# Make sure the URL contained valid slugs #}
{% if not mySection or not myCategory) %}
    {% exit 404 %}
{% endif %}

From there you can do whatever you want with them. For example, you can display every entry that is related to both of them:

{% set entries = craft.entries({
    relatedTo: ['and', {targetElement: mySection}, {targetElement: myCategory}]
}) %}

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

You can repeat all of those steps to do a similar thing for URLs that just have one category in them as well (just omit the second route token, and ignore all the myCategory stuff in the example code).


For loop that can accept array and element query without throwing .all deprecation error

Instead of trying to loop through an object (your Query) you could just execute it

{% set component = {
    headline: headline ?? entry.headline ?? null,
    body: body ?? entry.body ?? null,
    items: items ?? entry.items.all() ?? null
} %}

{{ component.headline }}

{{ component.body }}

{% for item in component.items %}
    {{ item.name }}
{% endfor %}

or if that doesn't fit your case, you can do it before

{% set relatedEntry = entry.callToAction.one() %}
{% include '_components/callToAction' with {
    entry: relatedEntry,
    items: relatedEntry.items.all()
} %}

You could as well check if your variable is an object of type EntryQuery

{% if className(entry) == 'craft\elements\db\EntryQuery' %}
    {% set entry = entry.one() %}
{% endif %}

Note: since you had multiple of these deprecated errors I wasn't exactly sure which one you wanted to "solve" and which one is "intended"


Login Page Logo and Site Icon gives 404 in Craft 3

This has been fixed for the next release. Only affects environments where the web server is not sending missing resource requests (like /cpresources/XXXXXX/logo.svg) to index.php.


Can you add duplicates fields in a structured section's GUI?

An entry type can only use a field once, regardless the tab. But you can reuse the field in other entry types of that same section or reuse it in other sections.

Keep in mind, that you (currently [*]) always have the same field settings shared with each instance you use it. Meaning that you'll always end up with the same field name and instructions, which sometimes (I'd say often) is a deal breaker for reusability.

[*]: if I remember this right, I already made P&T aware of this and made a feature request


What's the easiest way to migrate my local site data to my live site?

Go into Utilities
Click "Database Backup", this will give you a .zip file of your data
Go into phpMyAdmin of your production site
Find your database, click Import
Upload .zip file


Is it possible to create a Craft boilerplate out of a project.yaml file?

I have just setup a Craft 3 boilerplate and got it working great. So yeah... this does work.


For Craft 3, at least for now.

1) Make a custom plugin
2) Add event to modify the query in control panel

// Register element query prepare event
Event::on(ElementQuery::class, ElementQuery::EVENT_BEFORE_PREPARE,
    function(Event $event)  {
        if (Craft::$app->getRequest()->getIsCpRequest()) {
            if ($event->sender->elementType === 'craft\elements\Entry') {

                if(isset($_POST['criteria']['search'])&&strpos($_POST['criteria']['search'],'author')!==false)
                {
                    $email = explode(':',$_POST['criteria']['search']);
                    if(isset($email[1]))
                    {
                        $user = User::find()->email($email[1])->one();

                        if($user)
                        {

                            $event->sender->query->andWhere(['authorId'=>$user->id]);
                            $event->sender->subQuery->andWhere(['authorId'=>$user->id]);
                            $event->sender->search = null;
                        }
                    }

                }
            }
        }
    }
);

So when you type author:[Email] it can fetch in the CMS.


Craft Commerce 2.0.2 Shipping Rule Names & Descriptions—How?

You have to can loop through the shipping rules for the shipping method.

{% for handle, method in cart.availableShippingMethods %}

  {% if method.shippingRules|length %}
    {% for shippingRule in method.shippingRules %}
      {{ shippingRule.name }}
      {{ shippingRule.description }}
    {% endfor %}
  {% endif %}

{% endfor %}


Set base 'shop' URL to something else in Commerce

The way the demo templates are set up they are a series of included sub-templates.
So you would need to change the references to shop/ for each of the {% include %} statements.

Try doing a find/replace on the templates within your /store directory. See what that does.

Craft/Commerce places no restriction on how you name things. You don't need to use the demo templates at all if you don't want to. You can name them and place them wherever you like.


I do not have the /craft/storage/runtime/logs/ folder

Looks like that article needs to be updated for Craft 3.

In Craft 3, the logs folder is at storage/logs and you can search for [error] and find the one related to email sending.


Select entry from any locale with Entries Field

Is there any way around this?

Not that I'm aware of.  You can currently only select entries from the locale you're viewing (same as source), or an explicit one specified in the settings.

You can't browse across multiple locales and select entries.


How do you page through entries in a structure?

Craft provides many helpful methods to traverse structure sections and your use case is no exception.

Instead of using .getPrev() or .getNext(), try .getPrevSibling() or .getNextSibling() (or their aliases .prevSibling and .nextSibling.)

You could create prev/next links with something like this:

{% set prev = entry.prevSibling %}
{% if prev %}
    {{ prev.title }}
{% endif %}

{% set next = entry.nextSibling %}
{% if next %}
    {{ next.title }}
{% endif %}


The Find and Replace tool only works with "textual" field types (i.e. Plain Text and Rich Text fields).  Table fields store their content JSON encoded in the database so the tool ignores it.


Developer of Charge here.

The full details on the setup for Connect are detailed here : https://squarebit.co.uk/software/craft/charge/usage/reference/connect-setup

Setting up a Charge to use Connect takes 4 broad steps -

Configuring your Stripe Account, and Charge settings - this involves putting your platform keys into Charge, and the callback url(s) in to the Stripe settings
Adding the Charge Connect field to the elements you want to use as the parent elements for the connected accounts.
Updating your templates to reference these connected fields.
Actually connecting accounts to use - using the field you created in step 2.

From the sounds of it - the issue you're having is in either step 3, or 4.

The first thing to look in is the Charge > Logs - which will detail exactly what's happening, and why things aren't going to the connected accounts. It's almost certainly due to either the proper information isn't being passed through on the request (ie. a template issue) or an account issue (ie. the connected accounts aren't in the appropriate mode to accept payments)


You can limit which blocks you want by type and only show the first of each, if they exist. Something like this might do the trick:

{% for entry in craft.entries.section('blog').limit(3).find() %}

        {% set textBlock = entry.blogBody.type('text').first() %}
        {% set imgBlock = entry.blogBody.type('blogimage').first() %}

        {% if textBlock %}
            {{ textBlock.text|hacksaw(chars='100', append='...') }}
        {% endif %}

        {% if imgBlock %}

        {% endif %}

{% endfor %}

{% endblock %}


There's no current way for Feed Me to handle this I'm afraid. My best suggestion would be to write a module to fetch the XML from your first URL, parse that, find the <link> note, fetch and parse that, and output the XML. You'd then point Feed Me to that URL, with the actual content of the feed that's required.
It could be a Craft module, a standalone PHP script, or even JS - whatever you're comfortable work.
It's a bit of extra work, but custom development would be the only option for the moment.
EDIT: The other option you could create a template (let's say my-feed.xml) in your templates directory with:
{% header "Content-Type: application/xml;" %}

{% set params = {
    url: 'http://path.to/feed/',
    type: 'xml',
} %}

{% set feed = craft.feedme.feed(params) %}

{% for node in feed %}
    {# Find the node that contains the link #}
    {% set link = node.myLinkNode %}

    {# Get the raw data and output it #}
    {% set xml = craft.feedme.getDataTypes().getRawData(link) %}

    {{ xml }}


Display category entries based on Segment?

How do I list the entries related to a category? article on the Craft site should get you headed in the right direction.

  You can fetch the entries related to a given category with a little
  help from craft.entries and the relatedTo param.

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

{# Output them #}
{% for entry in entries %}
    {{ entry.title }}
{% endfor %}


Yes, it's possible with a plugin.
Make it listen to onBeforeSaveEntry events and look up how often the entry was already saved by counting entry versions using the getVersionsByEntryId method.
If it is the entry's second save (one version exists) overwrite the slug with an empty string (will generate a new slug from the title).

You should add another conditional to only make it generate a new slug for all locales but the one you first saved in (not exactly sure how to get this), but this should get you started:

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

    craft()->on('entries.onBeforeSaveEntry', function(Event $event)
    {
        $entry = $event->params['entry'];

        if ($entry->id)
        {
            $versionsCount = count(craft()->entryRevisions->getVersionsByEntryId($entry->id, $entry->locale));
        }

        // $localeFirstSave = ??
        // if ($entry->locale != $localeFirstSave)

        if (isset($versionsCount) && $versionsCount == 1)
        {
            $entry->slug = '';
        }
    });
}

another variant you could try is to always load the JS that generates the slug on the fly:

public function init()
{
    if (craft()->request->isCpRequest())
    {
        craft()->templates->includeJs('window.slugGenerator = new Craft.SlugGenerator("#title", "#slug");');
    }
}


Using a loop in a loop, how do I check if child loop is empty to not display parent loop?

You'd further filter your returned artist entries down like this:

{% set artists = craft.entries.section('artists').intoCollection('1') %}

Then remove all the unnecessary "intoCollection" conditionals in your loop.


Craft Commerce Product Options that increase price, but do NOT affect stock

Worked it out!
Putting this in a custom module should do the trick..., obviously changing the price modifier and option name to your needs.
        Event::on(
            LineItems::class,
            LineItems::EVENT_POPULATE_LINE_ITEM,
            function(LineItemEvent $event) {
                // @var LineItem $lineItem
                $lineItem = $event->lineItem;
                // @var bool $isNew
                $isNew = $event->isNew;

                if (isset($lineItem->options['giftWrapped']) && $lineItem->options['giftWrapped'] == 'yes') {
                    $lineItem->price += 20;
                }
            }
        );


Including and reusing common code per matrix block

Take 2:

I think you may be looking to use a Twig Macro. Here's the gist of that approach, but I don't fully understand the layout of your template yet, so my answer may be a bit out of context until I do.

You'll need to create your macro higher in your template or in a separate file and import it into the template you are working in. (See the docs linked above on how to import a macro for your situation.)

You could then simplify your block output to something like this.  I'm using fairly verbose names for this example but you can shorten the variable names to have less code if you prefer.

{% import "_widgets/_settings.html" as settingsMacro %}
{% set settings = block.settings %}

  ... content here ...

In your macro file, you'll take those settings and process them as you need to:

_widgets/_settings.html

{% macro backgroundColor(settings) %}
  {% set backgroundColor = '' %}

  {% for row in settings %}
    background-color: {{ row.backgroundColor }};
  {% endfor %}
{% endmacro %}

{% macro backgroundImage(settings) %}
  {% set backgroundImage = '' %}

  {% for row in settings %}
    background-image: url({{ row.backgroundImage.first().url }});
  {% endfor %}
{% endmacro %}

I think, due to how many matrix blocks are fairly short templates, the amount of code you may need to avoid reuse still may be a couple lines after you import a macro and call the specific macros.  But like the include, this could be another way to help ensure consistency in the code being used in multiple places. Hopefully this gives you a few more ideas to work with to find a satisfying solution. I'd need to have a better understanding of your full template picture to offer a more specific solution on how to implement.

Take 1:

If I understand correctly, I believe one way you can do what you are trying to do is by using the include with syntax and making sure you are passing your variables to the include template:

{% include "_widgets/_settings" with {
  backgroundColor: backgroundColor,
  backgroundImage: backgroundImage
} %}

This assumes that you are defining backgroundColor and backgroundImage before your include, and that the include has the following code:

 ... content here ...

Also note, adding the underscore to your folder path hides all files within it.  So, if you don't want to have to add underscores in front of every file, you still have hidden files if they are within a hidden folder, i.e: _widgets/settings


Add entry author meta field in entries listing table in CP?

You're on the right track with both of those. First, modifyEntryTableAttributes.

  Gives plugins a chance to modify the table columns shown on the entry index page.

This lets you name the column for your author info. It's a hook, so you'll need to add a public method with the same name as that that to your main plugin class. You can also limit the column to particular section by checking the $source variable.

public function modifyEntryTableAttributes(&$attributes, $source)
{
    if ($source == 'section:3')
    {
        $attributes['author'] = Craft::t('Author');
    }
}

Then to actually return the information, you need getEntryTableAttributeHtml.

  Gives plugins a chance to customize the HTML of the table cells on the entry index page.

Here you can just check for the attribute you set above, and return a string.

public function getEntryTableAttributeHtml(EntryModel $entry, $attribute)
{
    if ($attribute == 'author')
    {
        return $entry->getAuthor()->getFriendlyName();
    }
}


Get 404 Error When Going to mydomain.com/news

The .htaccess file was missing the period in the filename when initially installed.


Archive Not Returning Correct Date Variable

Your postDate.day will output the number of the day in the month so if your entries postdate is 02.dd.yyyy your output is 2. Your array index will be something like [2, 3, 5, 12, 31] thus these values will be converted from the timestamp into a readable day with<h2>{{ day | date('Y-m-d') }}</h2>. The first timestamp (0) is 01.01.1970 because of your timezone you'll see only values around this date since you convert all the day numbers from 1-31 as timestamps to your given format.

In case you did not know: the day variable represents only the keys of the array and with | group('postDate.day') you define the keys. I think your goal is to have keys like [ 'Monday', 'Tuesday'.... 'Sunday']. Your filter will look like:

{% for day, entriesInDay in agendaEntries | group('postDate|date('l')') %}


I can't say for certain if you would see faster session handling if you added the overridePhpSessionLocation config key, but have you added the appId and validationKey configuration keys? I was recently having issues in my load balanced environments because these keys were not set.


Category Sources plugin not showing headings

I just tested this on the fresh install of the latest Craft 2.6.2989 and the latest Category Sources zip and it working as expected where "Food" and "Drinks" are the Category Group names.

I'd double check you're running the latest of each.


Get Entrys relationships regardless of context

So I found this: https://github.com/iainurquhart/craft_introvert which is exactly what I wanted to do. Hope this helps anyone else!


We recently built our first multi lingual site on craft and used FeedMe to import our entries. What we did was to set up fields such as enDesc, deDesc, svDesc. Then when we imported our entries into craft mapping all of our multi lingual content to be spread across these fields in a single locale. Once this was done we then ran a script across the database to copy the content from deDesc into enDesc on the entry with the locale set to de, and then the same for sv also. Here is the script we built to get this job done. We ran this script from a program called CodeRunner on Mac, but you could equally FTP this up to the server and navigate your browser to it to run the script.

<?php
$servername = "XX.XXX.XXX.XXX";
$username = "db_username";
$password = "db_password";
$dbname = "db_name";
date_default_timezone_set('GMT');

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
$fieldName = "Desc";
$failed = 0;
$result = $conn->query("SELECT * FROM craft_content WHERE field_de".$fieldName." <> '' AND locale = 'en';");
while($row = $result->fetch_assoc()) {
    $elementId = $row['elementId'];
    $desc = $row['field_de'.$fieldName];
    if ($conn->query("UPDATE craft_content SET field_en".$fieldName."='".$desc."', field_de".$fieldName."=NULL WHERE elementId='".$elementId."' AND locale = 'de';") === TRUE) {
        echo "Updated ".$row['title']." - de";
    } else {
        echo "Failed ".$row['title']." - de";
        $failed++;
    }
}
$result = $conn->query("SELECT * FROM craft_content WHERE field_swe".$fieldName." <> '' AND locale = 'en';");
while($row = $result->fetch_assoc()) {
    $elementId = $row['elementId'];
    $desc = $row['field_swe'.$fieldName];
    if ($conn->query("UPDATE craft_content SET field_en".$fieldName."='".$desc."', field_swe".$fieldName."=NULL WHERE elementId='".$elementId."' AND locale = 'sv';") === TRUE) {
        echo "Updated ".$row['title']." - swe";
    } else {
        echo "Failed ".$row['title']." - swe";
        $failed++;
    }
}
if ($failed == 0) {
    if ($conn->query("UPDATE craft_content SET field_de".$fieldName."=NULL, field_swe".$fieldName."=NULL;") === TRUE) {
        echo "Cleanup Complete";
    } else {
        echo "Cleanup Failed";
    }
}
?>

Once this had completed, the values from deDesc and svDesc should have copied into enDesc on both the German and Swedish locale entries. The values for deDesc and svDesc will also be removed if there were no errors. All you need to do now is rename your field from enDesc to desc and delete the deDesc and svDesc fields from craft. Rinse and repeat this process for each field and cleanup the fields afterwards and that should sort out your multilingual import for you.

Just make sure to take database backups frequently in case there are any errors though.


URL validation without http:// or https:// in the URL

To conditionally add http:// to strings that don't begin with http:// or https://, use the Twig matches operator in a comparison.

To keep the syntax compact, I use a ternary operator within the set tag.

{# Set an example URL without `http://` #}
{% set url = 'example.com' %}

{# "Correct" the URL notation #}
{% set url = url matches '/^(https?:\\/\\/)/' ? url : 'http://' ~ url %}

{# Test output #}
{{ url }}

.

This is no full URL check in my regular expression, but you could simply work that in too. Pay attention to the escaping character \ that (currently) needs to be escaped itself, so you end up with \\ (it's wrong in the Twig docs).


You might be looking at a simple race conditions issue, i.e. that the rowAddOrRemove event is triggering before the new .myfieldwrapper elements have been attached to the DOM. To make sure that you don't call new Craft.MyField too early, you can try to wrap the constructor call in an anonymous requestAnimationFrame handler:

$('#tableId').on('rowAddOrRemove', function(event){
    Garnish.requestAnimationFrame(function () {
        $('.myfieldwrapper').each(function() {
            new Craft.MyField(this);
        });
    });
});

Not sure if race conditions is even the issue here, but it's the only thing I can think of, based on the code you provided.

Some other thoughts:

I'm not sure you actually need the rowAddOrRemove event – unless other parts of your application also needs to respond to that event, I'd opt for calling a simple function, rather than triggering a jQuery event, e.g.:

# ... field gets added dynamically

Garnish.requestAnimationFrame(initFields); // Instead of $('#tableId').trigger('rowAddOrRemove');

function initFields()
{
    $('.myfieldwrapper').each(function () {
        new Craft.MyField(this);
    });
}

Sidenote: If you do need to trigger an event, I'd recommend looking at Signals.js, which is a much more robust Event/Promises implementation than $.trigger.

Another thing: I'm not sure if calling Craft.MyField(this) on DOM elements that have previously been initialized is an issue, but if it is, you could add a simple classname to the initialized elements, to prevent them being initialized multiple times:

function initFields()
{
    $('.myfieldwrapper:not(.js-initialized)').each(function () {
        $(this).addClass('js-initialized');
        new Craft.MyField(this);
    });
}

Finally, if you need to destroy instances of Craft.MyField that have previously been initialized but are now removed (to prevent memory leaks or other bugs), you could do something like this (note that this example assumes that your Craft.MyField class has a destroy method that e.g. removes event listeners, deletes variables etc):

var myFieldInstances = [];

function initFields()
{
    // Destroy removed fields
    var currentFieldInstances = myFieldInstances.slice(0);
    myFieldInstances = [];
    for (var i = 0; i < currentFieldInstances.length; ++i) {
        var field = currentFieldInstances[i],
            $el = field.$el;
        if (!$el.closest('#tableId').length) {
            // This field's DOM element is no longer a child of #tableId – kill it with fire
            field.destroy();
        } else {
            myFieldInstances.push(field);
        }
    }
    // Init new fields
    $('.myfieldwrapper:not(.js-initialized)').each(function () {
        $(this).addClass('js-initialized');
        myFieldInstances.push(new Craft.MyField(this));
    });
}


Ajax Post to guestEntries/saveEntries from another domain

TL;DR: Turns out this was an issue with my apache config on the Craft instance.

Explanation: I did a little digging around the GuestEntriesController.php file and noticed that Craft was not picking up that my POST was coming from an ajax request. After looking into that more I manually added the ajax header to my post request:

$.ajax({
    url: config.default.apiUrl,
    type: 'POST',
    data: data,
    headers: {'X-Requested-With': 'XMLHttpRequest'}
}).done(function(){ ... });

This got me a little closer, but now on request I was getting hit with this error:

  Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers in preflight response.

That led me to a simple Google search that had the answer in another Stack Overflow question.

In my Craft App's .htaccess file I added in the following 2 headers:

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "X-Requested-With, Content-Type"

Now my POST request from another server fires fine, and Craft returns with a successful json response.


Encrypt ID on front-end and decrypt on back-end

<input type="hidden" name="foo" value="{{ 'bar'|hash }}">

and then in your controller:

if (($foo = craft()->request->getPost('foo')) !== null)
{
    $foo = craft()->security->validateData($foo);
}

at which point $foo will either be null (not present), false (present but tampered with), or a string (present and valid)


how to create Super Table field programmatically

Not sure if this is the answer without playing, but try saving the plain text field before adding it to the super table:

    // Create the super table field

    $superTable = new SuperTableField([
        'name' => 'supertablefield',
        'handle' => 'supertablefield',
        'groupId' => 1
    ]);

    Craft::$app->getFields()->saveField($superTable);

    // Create plain text field

    $field = new \craft\fields\PlainText([
        "name" => 'textfield',
        "handle" => 'textfield',
        "translationMethod" => "site",
        "multiline" => 0,
    ]);

    // Save the plain text field

    Craft::$app->getFields()->saveField($field);

    // Retrieve the plain text field

    $plainTextField = Craft::$app->fields->getFieldByHandle("textfield")

    // Create and set the block

    $blockModel = new SuperTableBlockTypeModel();
    $blockModel->fieldId = $superTable->id;
    $blockModel->uid = StringHelper::UUID();
    $blockModel->setFields([$plainTextField]);
    $superTable->setBlockTypes([$blockModel]);
    SuperTable::$plugin->service->saveBlockType($blockModel);


Unable to backup the database or apply migrations

Sounds like your php-fpm container 3 needs to install the MySQL client tools in it, so that mysqldump is available in the container similar to https://github.com/craftcms/docker/blob/main/7.4/dev.Dockerfile#L18


Updating plugins from the admin doesn't work

Memory is definitely not fine, the error message is very explicit:

  Allowed memory size of 134217728 bytes exhausted

You need to raise your PHP memory limits. Craft requires a minimum of 256MB to run properly.

  https://docs.craftcms.com/v3/requirements.html


Charge object not available on first page load

My current workaround: Get the select field directly and set its options to the select field options:

{% set donationRecurrenceOptions = craft.fields.getFieldByHandle('donationRecurrence').settings.options %}


Is there a way to create a "sticky" asset

Based on adding a lightswicht ("sticky") as suggested by @carlcs I think you should be able to solve this by just using two order fields.

{% set images = craft.assets.folderId(18).order('sticky desc, filename asc').limit(240); %}

You should be able to save some twig code, maybe even requests. Haven't tested this though.


Google considers SMTP to be an "insecure" protocol when connecting to their servers.  You have to explicitly go into your account settings as described here and enable access for these "insecure" requests.


Why does MultiAdd think I am not sending any items?

Ok you're getting there, but your form syntax is a bit off.  MultiAdd needs you to post a form with an array named items.

Here's an example of precisely the sort of thing you need to post:

action
multiAdd/multiAdd

items[1][purchasableId]
854
items[1][qty]
0

items[4][purchasableId]
857
items[4][qty]
1

items[5][purchasableId]
858
items[5][qty]
0

items[6][purchasableId]
859
items[6][qty]
1

redirect
/cart

So you're posting items, each keyed by an index value to keep the attributes together.  This can be anything really.  You just have to send for each the purchasableId and a qty (if you send 0 the item will just be ignored).

items[whatever][purchasableId]
items[whatever][qty]

Just send as many of those pairs as you need, exactly like that (i.e. it must be called items, and the word whatever you can replace with an arbitrary key).  Each pair must have a value for purchasableId and qty (so for the first example the purchasableId is 854 and the qty is 0, so this one won't be added to the cart).

(You don't need to use the options syntax to add products as such, that's only required if you're using options with those products.  Maybe get your form working without options first, so just adding products) and then we can add them if you need them!)


How do you get entry's data from Ajax form submission with Guest plugin?

According to the README, the Guest Entries plugin accepts an AJAX request, and returns an object containing the entry ID, title, and so forth.

As such, if you're using jQuery, you should be able to do something like this (not tested):

$.ajax({
    data: $('#theform').serialize(),
    method: 'POST',
    done: function (data) {
        // The entry title.
        console.log(data.title);
    }
});

I'm not sure how Guest Entries handles errors, so you'll probably want to check the data.success property, before proceeding.


Controller action works but still gives 404

You're definitely on the right track, just missing out on a couple of key details!

The actionUrl function generates a normal, full URL... it's not (by default) an AJAX call. So your browser is actually navigating away from the previous page, and pinging your controller as a completely separate HTTP request.

Which leads to the next detail... Unless you're pinging your controller via AJAX, you need to then have your controller tell the request where to go. Don't forget, the request will continue on, even after your controller has worked its magic.

  ... if the controller doesn’t call a response function, Craft will continue on with its request routing...

So you'll usually want to make sure your controller ends with a directive on what to do next. The most common options are:

Render a template
Redirect to a new page
Return a JSON response (if you called it via AJAX)

The list of options to render/redirect/return are mixed in with the other BaseController Methods in the documentation...


Entries from different category levels brought together

What you want is the categories that do not have descendants. I would loop over all the categories, and skip the ones with descendants. The test to skip is:

{% if not category.hasDescendants %} ... {% endif %}

Documentation for hasDescendants.


Plugin fieldtype settings for ColumnType::LongText being inserted as varchar(255)

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

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

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

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


You're running out of PHP memory as it's processes the image when it's uploaded. The fix for this is to increase the memory limit, which you normally need to get your hosting company to do for you. Try 512mb.


I can't say officially (since I had nothing to do with building it), but it looks like the Punycode library specifically does this:

  This library converts a Unicode encoded domain name to a IDNA ASCII form and vice-versa.

I'm not sure why it's getting flagged as malware, it seems like a pretty harmless little library. If you're still concerned about it, I'd either reach out to Pixel & Tonic directly (support@craftcms.com) or post an issue on the Punycode repo.


Not directly, but you can indirectly change it. An entry's status is defined by several attributes.

An entry is considered "live" if it is enabled (both on a per-locale and global basis), it has a post date in the past and an expiration date that doesn't exist or hasn't been met yet.

If the entry is enabled and the post date is greater than the current date, then the entry's status is "pending".

If it is enabled and the current date is past the expiration date, then it has a status of "expired".

If an entry is not enabled at all, it has a status of "disabled".


Is there documentation on Craft.AdminTable?

None of Craft's JavaScript is documented.  Your best bet is going to be to look at the source itself in craft/app/resources/js.


Craft will do some minimal cleansing of potentially malicious uploaded images by default by saving them back out to the file system and will try to sanitize SVG files as well.

It also has a relatively tame list of file extensions it allows on upload: https://docs.craftcms.com/v3/config/config-settings.html#allowedfileextensions

Anything outside of that, it'd be up to a Craft plugin or something on the server-side to do additional analysis and cleansing.


What is the difference between the Translation Methods when creating a new site

Not translatable
The field will always have the same value for each site
Translate for each site
Easy: the field can have a different value for each of your sites. Create 10 sites, all can have different content
Translate for each site group
Every site group can have a different value, create 10 site that are all in the same group, all elements will have the same value, create a site in another group -> that one can have a different value than the other 10
Translate for each language
Create 10 sites, all in the same language, all will have the same content, no matter what group they are in. Create another site in another language  -> this one can have different content
Custom
You can define custom behaviors
edit  a few more words about this
There will appear an <input>below that option and Craft will actually do this
if ($field->getTranslationKey($siteElement) === $field->getTranslationKey($element)) {
    // Copy the master element's value over
    $siteElement->setFieldValue($field->handle, $element->getFieldValue($field->handle));
}

Which basically means: it will render both object templates (the original element, and the element for all other sites) and injects the element into the template..
For example if you have something like { title } in your template, Craft will render those templates, and will compare them. If the titles are the same, the field is translatable and may contain a different value for all sites, if the titles are equally, the content of your field will be equally as well.


The data is not stored twice in the database. I suspect that craft adds the additional array items when prepping the data, so that they can be referenced by either column number or column title.

But with a little finesse, you might be able to do something like the following, using key, value in the for loop and twig's slice filter:

    {% for row in entry.myTableFieldHandle %}

            {% for key, data in row if key|slice(0,3) == 'col' %}
                {{ data }}
            {% endfor %}

    {% endfor %}


The redirect url always needs to be hashed:

https://docs.craftcms.com/v3/dev/functions.html#redirectinput

In Twig you can use {{ redirectInput(url) }} which is a shortcut of

<input type="hidden" name="redirect" value="{{ url|hash }}">

But if you need the same in a PHP code you can use

Craft::$app->getSecurity()->hashData($url)

based on the code how the Twig filter defined in craftcms/cms/src/web/twig/Extension.php.


Make your own little plugin that listens to the plugin's contactForm.beforeSend event
and sends the confirmation mail.


If it were me, I wouldn't use Cloudfront to offload the entire site. You can certainly use it for static assets but cache invalidation is really expensive. And as you've found it, troublesome. By offloading the static assets, you can keep the web server focused on just serving page requests.

From your previous question, it sounds you get a fair amount of traffic.

Cache invalidation is a tricky subject. I don't know how often you update but a middle-of-the-road approach is using nginx or varnish.

I like nginx myself because of its flexible fastcgi_cache. Even if you set the cache low, like 1 minute, you've just increased your capacity by an order of magnitude without much additional work on your part.

nginx also allows you to cache by location so you could exclude caching any page with a form. POST requests by default bypass the cache.


How do I stop my relative links to stylesheets breaking?

The links aren't working because that's a relative URL, from whatever the last "directory"-looking segment in your page's URL is.

To resolve this, you can either make your URLs root-relative:

<link href="/css/myStylesheet.css" rel="stylesheet">

Or relative to the site URL:

<link href="{{ siteUrl }}css/myStylesheet.css" rel="stylesheet">

The drawback to root-relative URLs is, it makes your site a little less portable - you'll only be able to install the site in a web root (no subfolders) without having to update all URLs.

If this is a localized site with multiple site URLs, then both solutions could have issues. In that case it might make sense to define your own custom config setting in craft/config/general.php:

'baseResourceUrl' => "http://example.com/",

Then call glad from your templates instead of siteUrl:

<link href="{{ craft.config.baseResourceUrl }}css/myStylesheet.css" rel="stylesheet">


Fetch locale entries does not work in Live Preview

Resolved here.
Does not appear on the doc, although.


Prevent direct access to a web page except redirect

Add a Preparse field to the entry type you're posting to and make it generate a random string on entry save.
Then make the Guest Entries plugin redirect to a URL with that Preparse field's value in the query string.

<input type="hidden" name="redirect" value="success?id={id}&key={preparseField}">

You should now be able to get that entry model in your template and test against that Preparse field's value.

{% set query = craft.request.getQuery() %}
{% set entry = craft.entries.id(query.id).preparseField(query.preparseField).first() %}

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


Get an Asset's Filename Without the Extension

A simple method would be to trim the extension from the filename (see this fiddle):

{{ assetsField.filename|trim('.' ~ assetsField.extension, 'right') }}


Craft 3: fetching a single Matrix row by its dropdown field value

That is most likely happening because usage is ambiguous for your logos field, so Craft isn’t sure which column to assign the condition to. If you give the usage field within your img block type a different handle, you should get reliable results.


Solution for Freeform 3:

Preventing duplicated field id:
Set a custom field id prefix. Using the form handle might be a good id, as long as you're not using the same form twice on the same page.

Preventing duplicated form id:
Manually adjust the form id parameter to something unique.


Entry validation not working when Publish live changes permission is not checked

As carlcs said, this is already working as expected. Craft only enforces content validation rules for enabled entries (not including drafts), and most people prefer that behavior.

If you want to change that behavior, you can write a plugin that provides its own entry validation logic, by latching onto the entries.onBeforeSaveEntry event. Just put this in your plugin’s primary class:

public function init()
{
    craft()->on('entries.beforeSaveEntry', function(Event, $event) {
        /** @var EntryModel $entry */
        $entry = $event->params['entry'];

        // If the entry is disabled, the content won't be validated automatically
        if (!$entry->enabled)
        {
            $validates = craft()->content->validateContent($entry);

            if (!$validates)
            {
                // Add the content validation errors
                $entry->addErrors($entry->getContent()->getErrors());

                // Prevent the entry from getting saved
                $event->performAction = false;
            }
        }
    });
}


Entirely possible I'm misunderstanding what you're looking for, but I think something like this should do it:

{% set entries = craft.entries.section('news').order('articleOrder desc').find() %}
{% set matchCount = 0 %}

{% for entry in entries %}
    {% if entry.currentYear2 == currentEdition.currentYear2 and currentEdition.currentMonth2 in entry.currentMonth2 and matchCount <= 5 %}
        {% set matchCount = matchCount + 1 %}
        <h2>{{ entry.title }}</h2>
    {% endif %}
{% endfor %}


Element API: Remove additional set of square brackets from JSON-String

Though I don't know the Element API, have you tried

    return $allOverChallenges;

instead of

    return [
        $allOverChallenges
    ];

It looks like you are simply adding these brackets (Array) on the return.


Custom Plugin Field Using Wrong Template Path

In order to render a template within your custom plugin you need to include your plugin handle in front of your template path. For example

plugin-handle/_components/fields/Regionselect_settings


How to output a Rich Text Link to an Entry as a tooltip?

Not really a craft solution — but easiest might be jquery/ajax. Since you know the base URI, you can just create a selector for the route, and work from there.

$("a[href*='route/to/definitions/']").each(function(){
    var $element = $(this);

    // ajax load content and place content in DOM somewhere
    $.get( $element.attr('href'), function(data) {
        $element.attr('data-definition') = data;
    });

    // click handler to prevent default link behavior
    $element.on('click', function(event){
        event.preventDefault()
    });

    // hover handler to display/hide definition
    $element.hover(displayDefinition, hideDefinition);
});

You could also potentially do a search/replace in twig, but it is not nearly as easy to manipulate as in js.


Fixed using the following code. Also includes fetching an API via Guzzle and parsing it into Craft CMS. Thought I would post here so that if anyone else is having trouble they can decipher bits from this.

                // Set a base URI
                $baseUri = 'http://domain.com';
                // Build the Guzzle Client and use the Base URI
                $client = Craft::createGuzzleClient([
                  'base_uri' => $baseUri,
                ]);
                // Send a Request!
                $response = $client->request('GET', '/mytestendpoint/');
                // Parse the Response
                $json = $response->getBody();
                // Decode it
                $data = json_decode($json, true);

                // Set some values from it!
                $user->setFieldValue('userTotalFollowers', $data['followers'] );
                $user->setFieldValue('userEngagementRating', $data['details']['total_likes'] );
                $user->setFieldValue('verified', $data['is_verified'] );

                $number = 0;
                $matrixData = array();

                foreach($data['media'] as $element) {
                  $number++;
                  $matrixData["new$number"] = array(
                    'type' => 'media',
                    'enabled' => true,
                    'fields' => array(
                      'mediaDescription' => $element['likes'],
                      'mediaLikes' => count($element),
                    )
                  );
                }
                $user->setFieldValue('recentPosts', $matrixData);


This is going to be a bug in the Store Hours plugin.  I've created an issue in the repo here so we can keep track of it: https://github.com/pixelandtonic/StoreHours/issues/3


Not sure how you implemented the cache tags, but I would recommend to cache each "eDrops" entry individually.

I also took out {% if entry.slides|length %}. Testing if entry.slides.first() returns anything is all you need. In order to make the length filter work, Craft internally applys find() to the criteria model behind entry.slides. This means you (unintentionally) added an additional database query (besides first(), which you need anyways).

Keep in mind, that assets are elements in Craft and relationship queries take their time (→ also explained in the Matrix field example here: What are the most common template performance gotchas to avoid?).

{% nav entry in eDrops %}

    {% cache globally using key 'eDrops-' ~ entry.id %}
        {% set image = entry.slides.first() %}

        {% if image %}
            <img src="{{ image.getUrl('fitThumb') }}">
        {% else %}
            {{ entry.title }}
        {% endif %}
    {% endcache %}

    {% ifchildren %}
        {% children %}
    {% endifchildren %}

{% endnav %}


How to set stability version in plugins?

Looks like you haven't submitted your plugin repo to Packagist yet. You'll need to do that before it's available via Composer (per the docs).

Update and additional info:
Like you mentioned in the comments, and like it's mentioned in the docs (see the Tip box here), Packagist is optional for your Craft plugin. You can totally add your plugin to the plugin store and install it via the plugin store without ever putting it on Packagist. If you use Craft's Update button in the dashboard, everything will work like you'd expect. The reason is because Craft is using its own logic (relying on Craftnet) to resolve packages. However, once you switch to running composer commands in the CLI to do your updating/installing/whatever, those packages won't be found (because they don't exist on Packagist yet) and Composer is obviously not using Craftnet's logic.

So if you want to be able to install/update your plugin via Composer, I'd recommend adding your plugin to Packagist. It honestly doesn't take that long (like 2 min maybe) and it will save you from quite a few support tickets down the road of people using Composer and having issues with your plugin's "absence".


Restrict user editing permissions within a user's group?

This is not currently possible through the CP (as far as I know anyway). You would need to write a plugin; and I haven't seen any plugins on straightupcraft that address this particular need, but have a look. You could also contact someone through the professionals section that might be able to help.

If you were to write your own plugin you might have a look at the documentation on hooks and events. In particular the onSaveUser event, where you could potentially force a user into a particular group, based on specific conditions that you establish.

In your plugIns init function you could create a listener for the onSaveUser event.

public function init()
{
    parent::init();
    craft()->on('users.saveUser', function(Event $event)
    {
        $user = $event->params['user'];
        if ($allMyConditionsAreMet)
        {
            if($allMyGroupConditionsAreMet)
            {
                $groupId = 1;
            }
            else
            {
                $groupId = 2;
            }
            craft()->userGroups->assignUserToGroups($user->id, $groupId);
        };
    });
}


Seems I needed to put in the order filter

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

{% set categoriesGroup = categories|group('title|slice(0,
1)|replace("/[^a-zA-Z]/", "0-9") | upper') %}

{% for letter, categoriesInGroup in categoriesGroup %}
{{ letter }}
{% for category in categoriesInGroup %}

{{ category.title }}

{% endfor %}

{% endfor %}


Craft 3: .find() deprecated but .all() fails?

Assuming mixedContent is a Matrix or relational field, the correct syntax would be:

{% set mixedContent = entry.mixedContent.with([
    'singleImage:image',
    'imageGallery:images',
    'download:file',
    'relatedItems:relatedItems'
]).all() %}


An ElementCriteriaModel is "used to fetch elements with a given set of parameters." {{ entry.blogCategory }} hasn't yet fetched your category element.

You need:

{{ entry.blogCategory.first }}

if you are positive there is exactly one of them, or more generally:

{% for c in entry.blogCategory %}

{{ c }}

{% endfor %}


In Craft almost everything is an element type (assets, categories, entries, global sets, matrix blocks, tags and users). If something is an element type you can attach arbitrary fields to it. Using fields like the Assets field, Entries field, Categories field or Users field you can define relations (link) between these elements.

For fetching elements via an API there are these options:

the built-in GraphQL API
the Element API plugin
creating a plugin or module and define API endpoints freestyle using the element query builders

Other non-API options for querying elements are:

use element queries in Twig and dump the result
use element queries in PHP and dump the result to the browser
use the REPL via the CLI with ./craft shell


bootstrap.php how to include environment variables for multi-environment configuration?

So debugging this a bit, it looks like its to do with the namespace declaration on the original template I was using for the migration. Basically when declaring namespace Craft this causes problems with loading the environment variables that were previously being masked by the die() error, as I was including Bootstrap too early.

Uncaught Error: Class 'Craft\Dotenv' not found

In my case, the phpdotnev stuff is going to need to appear before including Bootstrap otherwise Craft isn't going to be able to connect to the DB.

Moving the vendor and environment variables stuff up before loading the bootstrap file from the Craft folder, seems to work. This a test file I have now:

<?php
namespace Craft;

// Avoid errors when running through console
$_SERVER['HTTP_HOST'] = 'example.craft.localdev';
$_SERVER['REQUEST_URI'] = '/index.php';

// Get the root dir so we can run migrations without worrying about path
$root_dir = dirname(dirname(__FILE__));

// We need to load in phpdotenv stuff before initialising bootstrap.php, otherwise you won't be able to connect to the DB
require_once($root_dir . '/vendor/autoload.php');

// If APP_SECRETS is not set, load in .env file
if (!getenv('APP_SECRETS'))
{
    $envFile = '.env';

    if (file_exists($root_dir . '/' . $envFile))
    {
        // PHP dotenv: https://github.com/vlucas/phpdotenv
        $dotenv = new \Dotenv\Dotenv($root_dir);
        $dotenv->load();
        $dotenv->required(['APP_SECRETS', 'CRAFT_ENVIRONMENT']);
    }
    else
    {
        die("$envFile does not exist.");
    }
}

// Set the Craft environment with the value set in .env
define('CRAFT_ENVIRONMENT', getenv('CRAFT_ENVIRONMENT'));

// Initialize Craft without letting it take over the whole request
$app = require $root_dir . '/craft/app/bootstrap.php';

// Set Craft to Craft Personal so we don't get any errors about Client/Pro-only components not being available
$app->getInfo()->edition = 0;

// CUSTOM CODE FOR MIGRATIONS AND OTHER CRAFT RELATED STUFF GOES BELOW THIS LINE

This then appears to be using the proper environment variables. I tested by editing my db.php file to break one of the variables being pulled from secrets.json to cause the DB connection error and it does appear to be work, as the try and catch statement is responding appropriately.

Not sure if there will be an issue not having the namespace Craft declared or not, but I appear to be able to interact with Craft through $app still. Would this be correct?


Craft on Vagrant: Please advise on initial install (Craft can’t connect to the database with the credentials in craft/config/db.php.)

Solution found:

I copied again all files to the root folder, changed the very same settings and got it to work - for whatever reason it seems that with the first attempt some files where missing.

@Brad: Thanks for taking the time - the shared infos are of help anyway.


How to hide the assets section of the control panel to specific user groups?

Using a custom module it's easy to simply hide the Assets section of the CP.
Users would still be able to access the assets library by entering manually the URL but this is good enough for my use case.
Here's the (simplified) code:
<?php

namespace modules\appModule;

use Craft;
use craft\events\RegisterCpNavItemsEvent;
use craft\web\twig\variables\Cp;
use yii\base\Module;

class AppModule extends Module
{
    public function init()
    {
        $user = Craft::$app->getUser();
        if (
            Craft::$app->getRequest()->isCpRequest
            && !$user->isGuest
            && $user->getIdentity()->isInGroup('editors')
        ) {
            Event::on(Cp::class, Cp::EVENT_REGISTER_CP_NAV_ITEMS, function(RegisterCpNavItemsEvent $event) {
                $event->navItems = array_filter($event->navItems, static fn ($item) => $item['url'] !== 'assets');
            });
        }
    }
}


You could setup a template at /menu that grabs the PDF you want and redirects with a 302 (not a 301 since it isn't permanent).

The URL would change, but that might be ok if you only care about having an easy link to share.

If redirecting is a deal breaker, you could make a small plugin that returns the file with proper headings, then call that from the /menu template.


Contact Form sent via Ajax with file upload returns 302 instead of 200 status code

Solved it (finally).

First part of the problem (the 302 redirect) was this missing configuration in the ajax call:

$.post({
  // ...
  dataType: 'json',
  // ...
};

It tells the server what you expect to get back from it.

Now this was fine, but I ran into a second issue. The xhr request stalled in Safari. When that happend and I reloaded the page (causing the request to finally stop), the email was sent anyways. The reason:

Safari seems to have an issue with this:

const formData = new FormData(formElement);

I found a hint here https://stackoverflow.com/a/8797289/2342963 that fixes the issue: appending the data manually to the FormData instance instead of passing the form element to the constructor.

So this is the working code:

const formData = new FormData();

$.each($('input, textarea, select'), function(i, element) {
    if (element.files) {
      $.each(element.files, function(i, file) {
        formData.append(element.name, file);
      });
    } else {
      formData.append(element.name, element.value);
    }
});

$.post({
    url: '/',
    data: formData,
    type: 'POST',
    cache: false,
    contentType: false,
    processData: false,
    dataType: 'json',

    success: response => {
        if (response.success) {
        // Show success
        } else {
        // Show error
        }
    },
    error: () => {
        // Show error
    }
});

Turns out, it wasn’t a Craft issue after all ¯_(ツ)_/¯


There currently isn't any built in Location field for Calendar. We made add something like that in the future, but for now you can definitely use other third party field types such as VZ Address to handle this. :)


Craft CMS 3 storage doesn't exist or isn't writable by PHP

I'm on Windows 10, so if you're on another version of Windows, things might be slightly different.

If you right click on that "storage" folder, go to Properties, click the "Security" tab, click "Edit", click "Add", type "Everyone", then click "Check Names" so it finds the user (an underline will show up under it), click "OK", make sure "Everyone" is selected, then check the "Full control" checkbox under permissions, click "OK", then "OK" to close the final modal, that will give everyone access to write to that folder (including PHP/Craft).

Obviously, don't do this on production, but since you're on a local box you should be fine.


How can i get API-Response that includes localized data using json_expand?

It was pretty easy :)

{% header "Content-Type: application/json" %}
{% set basics = craft.entries.section('basics').locale('en').find('') %}
{{ basics | json_expand | raw  }}

all it needed was: .locale('en')


I think I have a solution to this problem, but it's kind of complicated. There may be a way to simplify this more.

The basic idea is to loop thru your Store Hours field (entry.hoursOfOperation) and build a new array (storeHours) where the days are combined if the current loop's open and close times match the previous loop's.

{% set days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] %}
{% set daysShort = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] %}

{# Setup a new array storeHours to hold all you hashes
   and prev variables to compare things to in the loop
 #}
{% set storeHours, prevDays, prevDaysShort, prevOpen, prevClose = [],[],[],"","" %}

{# Loop thru your Store Hours field and build the storeHours array #}
{% for item in entry.hoursOfOperation %}

  {# in the first loop, setup the prevOpen and prevClose variables  #}
  {% if loop.first %}
    {% set prevOpen, prevClose = item.open, item.close %}
  {% endif %}

  {# If the prev hours match the current item's hours,
     add that day's name to the array of prev days, ["Monday","Tuesday"] etc.
   #}
  {% if prevOpen == item.open and prevClose == item.close %}
    {% set prevDays = prevDays|merge([days[loop.index0]]) %}
    {% set prevDaysShort = prevDaysShort|merge([daysShort[loop.index0]]) %}

  {# If it doesn’t match, add prev variables as a hash to the storeHours array,
     Then set all prev variables with current item
   #}
  {% else %}

    {% set storeHours = storeHours|merge([{
      "days" : prevDays,
      "daysShort" : prevDaysShort,
      "open" : prevOpen,
      "close" : prevClose
    }]) %}

    {% set prevDays = [days[loop.index0]] %}
    {% set prevDaysShort = [daysShort[loop.index0]] %}
    {% set prevOpen = item.open %}
    {% set prevClose = item.close %}

  {% endif %}

  {# If last loop, add prev variables as a hash to the storeHours array
     because there won’t be another loop to compare it to.
   #}
  {% if loop.last %}
    {% set storeHours = storeHours|merge([{
      "days" : prevDays,
      "daysShort" : prevDaysShort,
      "open" : prevOpen,
      "close" : prevClose
    }]) %}
  {% endif %}

{% endfor %}

The storeHours data format will be something like this:

[
  {
    "days" : ["Monday", "Tuesday", "Wednesday"],
    "daysShort" : ["Mo","Tu","We"],
    "open" : "2017-01-21 09:30:00.000000",
    "close" : "2017-01-21 21:00:00.000000"
  },
  {
    "days" : ["Thursday","Friday"],
    "daysShort" : ["Th","Fr"],
    "open" : "2017-01-21 09:00:00.000000",
    "close" : "2017-01-21 17:00:00.000000"
  }
]

Once you've created your storeHours array, you can use it to output your HTML code.

  {% for item in storeHours %}

      {{ item.days|first }}{% if item.days|length > 1 %} - {{ item.days|last }}{% endif %}

    {% if item.open|length and item.close|length %}

        {{ item.open|date('h:i a') }} - {{ item.close|date('h:i a') }}

    {% else %}

        Closed

    {% endif %}

  {% endfor %}

BONUS ROUND: if you want to make Monday the first day of the week, reorder the days and daysShort arrays at the top like this:

{% set days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] %}
{% set daysShort = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] %}

Then add this line just below it:

{% set hoursMondayFirst = entry.hoursOfOperation|slice(1,entry.hoursOfOperation|length)|merge(entry.hoursOfOperation|slice(0,1)) %}

That makes a new variable using Twig's slice filter to take Monday - Saturday (slice(1|length)) and merge Sunday at the end (slice(0,1)).

Now just use this as the start of your loop:

{% for item in hoursMondayFirst %}


Is there an alternative to the Dukt Rest plugin for Craft 3?

You can use the Fetch plugin to make HTTP requests from a template in Craft 3 and there's a Cookies plugin for reading and writing cookies via a template that should do what you want.


Craft CMS login 500 (Internal Server Error)

remove /craft/storage/runtime and remove all craft_session and database


Filtering values from Twig for my custom field type

Would this help?

Filter Entries by Field Value

Than it would be something like this I suppose (never used it myself):

{% for item in entry.myCustomField.search('category':'main') %}
    {{ item.category }}
{% endfor %}


Assuming your lineItem variable is called item, you're looking for

item.purchasable.stock

You might also need to pay attention to maxQty which is the maximum number of a purchasable item that a user is allowed to buy. I limit customers to a maximum of 10 and create a select box on the basket form with a Remove option at the bottom:

{% set maxQuantity = item.purchasable.stock > 10 ? 10 : item.purchasable.stock %}
{% if item.purchasable.maxQty %}
    {% set maxQuantity = maxQuantity > item.purchasable.maxQty ? item.purchasable.maxQty : maxQuantity %}
{% endif %}

<select name="qty">
    {% for i in 1..maxQuantity %}
        <option value="{{ i }}" {{ i == item.qty ? 'selected' }}>{{ i }}</option>
    {% endfor %}
    <option value="0">Remove</option>
</select>


There's no way to configure the default Rich Text Field's use of HTMLPurifier, unfortunately, though it has been brought up before and is on our list to look at exposing to a config file post Craft 3 launch.

Your custom plugin could create it's own CHtmlPurifier instance and configure it however you want, though.

$purifier = new \CHtmlPurifier();
$purifier->setOptions(array(
    'Attr.AllowedFrameTargets' => array('_blank'),
    'HTML.AllowedComments' => array('pagebreak'),
    ...
));

$value = $purifier->purify($value);


Imager scrset matrix field, art directed content

It looks like you're looping through block.workImageMobile.all(), and that's not what you want to be doing.

You're only putting in one image into each, right, and they're paired?

So, You want the main for-loop for the blocks, then render the images in each block together "work image 1, work mobile image 1" then move to the next block, and the next image pair.

{% for block in entry.workVisuals.all() %}
    {% if block.type == "imageContent" %}

        {% set picDesktop = block.workImage.one() %}
        {% set picMobile = block.workImageMobile.one() %}

        {% set imgDesk = craft.imager.transformImage(picDesktop, {
            mode: 'crop',
            width: 1920,
        }) %}

        {% set imgMob = craft.imager.transformImage(picMobile, {
            mode: 'crop',
            width: 768,
            height: 1272,
        }) %}

        <picture>
            <source media="(max-width: 767px)" srcset="{{ imgMob }}">
            <source media="(min-width: 768px)" srcset="{{ imgDesk }}">
            <img src="{{ picDesktop.url }}" alt="{{ pic.title }}">
        </picture>
    {% endif %}
{% endfor %}

Also note - on the desktop size, you've got it set to crop, but only have a width set, you might want to add a height to that too. Otherwise, it might be better with a resize function.


How to setup Bootstrap 4 Carousel with Craft CMS 3?

A matrix field for an image carousel needs only a single asset field, one that will accept more than one image. In my experience there is rarely a requirement for anything more. 99% of carousels I have implemented look like this:

If you wanted to add text to accompany each slide you could add a text field directly to the image asset and then display that with each carousel slide:


This seems missing from the other answers, so I feel the need to emphasize it here...

Craft does not require .dev for local development.

Your local URL can be anything you want, Craft does not care. There is no requirement whatsoever on using a .dev domain. That being said, it is recommended that you use a "dev-sounding" URL, in order to avoid the licensing nag warning. And even if you're getting the warning, it won't have any negative impact on the capabilities your site.

The other answers already address what to do if you do use a .dev URL, so I won't cover it here. You may also want to read this helpful answer on a related question.


Are there callbacks for elementSelect macro?

You can get the Element Select’s JS instance like so:

$(document).ready(function() {
  var elementSelect = $('#your-container-id').data('elementSelect');
});

(Replace your-container-id with whatever ID you passed into the id param when calling forms.elementSelect or forms.elementSelectField.)

Then you can attach a selection callback to that:

elementSelect.on('selectElements', function(elements) {
  // ...
});

There’s also a 'removeElements' callback function you can add.


Try this in dev or back up first.
Set 'useProjectConfigFile' => true, in your config/general.php and refresh your Control Panel. This will place a project.yaml file in your config/ folder.
Open that file and look for a portion that looks like this:
email:
  fromEmail: your@email.com
  fromName: Sitename
  transportType: craft\mail\transportadapters\Sendmail

If only your transportType is empty, add craft\mail\transportadapters\Sendmail as above (you'll be able to change that later if needed). If more is missing, set at least what you see above and if you have no email section at all, add it.
Save the file then refresh your Control Panel. It should say Changes to project.yaml must be applied to the loaded project config. Hit the Sync changes button.
Next, check if you can access your mail settings again and if so set 'useProjectConfigFile' back to false.


Output SuperTable fields from inside Matrix field

Your Super Table field is probably configured as a static field.

Have a look at the documentation for how to access subfields in this case:  https://verbb.io/craft-plugins/super-table/docs/guides/templating-examples#static-super-table-field

So instead of the for-loop you’d have sth. like this: {{ block.package.packageName }}


Is bizName your categories field?  Try something like:

{% for category in block.bizName.all() %}
   {{ category.title }}
{% endfor %}


Importing Blog posts from Drupal into Craft

There are several import plugins that can help you import your data from Drupal, but they may require a bit more configuration than the InstaBlog plugin that is designed for a specific use case.

Import Plugins

Import plugins like FeedMe and Import provide you a user interface where you can map fields from your import file to fields in Craft. Assuming you can export your data into one of the supported formats for those plugins, you could map the fields from your export and use the interface to import your data.

Sprout Import is also a versatile import plugin, but does not currently have a UI. It requires that you get your data into an import-friendly JSON format, and also gives you a lot of flexibility in how you resolve relationships or handle custom data types.

Drupal Views Data Export is a good plugin to help output your Drupal data in the format that can be used by one of the above import plugins.

Extract, Transform, Load

A good process to follow as you migrate your data is referred to as the ETL Process (Extract, Transform, Load). You need to:

Extract - Get your data out of the previous system (in your case Drupal)
Transform - Transform that data into a format that can be imported into the new system (this can be done with Drupal Views Data Export module, or some other method that can help transform the data in the Drupal Database to importable files for one of the Craft import plugins)
Load - Import your data into Craft (this step is handled by the Craft import plugins)

For large amounts of data, you'll likely run into several other challenges as well. Many of those questions will likely be documented by the specific import plugin you choose. When working with large migrations, while many things can be handled programmatically, don't forget to consider what can be handled manually as well. Often, a few parts of a migration are easier to do manually and being open to a bit of manual work can help make some of the programmatic steps less complicated.


Your templates will be private if they are contained in a folder prefixed with a "_"... So just make a folder like that, and put everything else inside of it:

/craft/templates/_everything/myprivatefile.html


Best practice for multi site development, staging, etc. [Special snowflake details inside]

If I'm understanding correctly, there are two methods I'd suggest;

Instead of reconciling the data from Site A, I would first setup Site A with Site B + C and add any new fields/sections you will need, then set up project config so that any structural changes you make locally on development will be made remotely. Put it live, then you/the client can populate the content on the live Craft install whilst you work on developing the theme locally.
I would look into the Feed Me plugin. On Site A you can set up Element API to expose all your content in JSON, then on you development site use Feed Me to import the content from Site A. This may take some setting up initially depending on the complexity of your site, but once it's done it's just a case of clicking a button to pull the latest content.

Hope that helps!


How do I sort and display by a matrix date field?

I have not worked with Lowblocks yet, but it looks like it returns an array of Matrix block models. To get to the related entries you need to access the owner property.

{% set entryIds = [] %}
{% for sown in allSown %}
    {% set blockId = sown.id %}
    {% set entryIds = entryIds|merge([blockId.owner.id]) %}
{% endfor %}

But do you really need those entry IDs? You could just display the blocks?

{% for sown in allSown %}
    {{ sown.owner.title }}
    {{ sown.sowingLocation }}
    {{ sown.sowingDate }}
{% endfor %}


How do you raise/fake an event so that you can test handlers listening for it?

I believe events can be raised programmatically by calling them from services. You could mimic this behavior by creating an Event object ($event) and calling calling craft()->entries->onBeforeSaveEntry($event) yourself, for example. You would need to set up the event context and create a new Event object as the code does in the Entries Service below.

$event = new Event($this, array(
  'entry'      => $entry,
  'isNewEntry' => $isNewEntry
));

$this->onBeforeSaveEntry($event);

Check out the Entries Service around lines 165 to see how Craft uses events in that context.

For each event call, the code is calling $this->raiseEvent('eventName', $event);

I bet with some minimal setup, you could write a test controller that can call these events at will. Good question - I've been going down the manual path for all my testing too!


Not a true answer, but a work around:

{% for tweet in timeline.data %}
    {% if loop.index <= 5 %}
        ...
    {% endif %}
{% endfor %}


That’s definitely the way to do it!

I would set the URL segments to their own variables, to clean it up a little bit.

{% set entrySlug = craft.request.getSegment(2) %}
{% set imageOffset = craft.request.getSegment(4) - 1 %}

Also, probably a good idea to include the section param when fetching the entry, in case you have another entry in another section with the same slug.

{% set entry = craft.entries.section('mySection').slug(entrySlug).first() %}

The only issue I can think of is, if the 4th segment is greater than the total number of assets, you’re going to get an error. You can grab the asset using the offset param instead, and check to see if anything is returned:

{% set image = entry.images.offset(imageOffset).first() %}

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


I think what you want to look at is the Regular Expression option under the advanced tab for your my-site/news node. You could add something like:

 /^my-site\/news/i

This way all of the child pages in that section will mark the News page as active.

MW


It's a very subtle difference, but what you have added there is a variable, when what you are looking for is a string.

<img src="{{ asset.getUrl('squarePhoto') }}" alt="{{ asset.title }}">

Notice the difference?

Your current way would be correct if you had set a variable somewhere with the name of squarePhoto, like so:

{% set squarePhoto = 'transformName' %}

Check out the Twig Primer in the Craft docs.


|replace filter is matching too many characters

Something like this should work:

{{ snippet|replace('/<img([\w\W]+?)>/', '') }}

You can see an explanation of the regex in the right column of https://regex101.com/r/DvJHeY/1


Modify ElementCriteria and use it for paginate

You can only modify a craft.entries criteria model with a field (→ Add custom element criteria attribute without a field type?). So what you have to do is to create a new custom field type with your plugin, and add such a field to the entry types you want to filter.

{% set entries = craft.entries.section('mySection').country('germany') %}

With that criteria model you would pass the value "germany" to your custom field type field "country". The field type would then use this parameter to modify criteria model's query.

Here's some code that should help you putting this together:

public function getInputHtml($name, $value)
{
    return;
}

public function modifyElementsQuery(DbCommand $query, $params)
{
    if ($params !== null)
    {
        craft()->businessLogic->addCountryCriteria($query, $params);
    }
}

You don't want the field to show up as an input field in the edit entry view, so we let getInputHtml() return null.

In our modifyElementsQuery method we just check if there's a parameter submitted (the country), before passing the criteria model's query to the service.

public function addCountryCriteria(DbCommand $query, $country)
{
    // Add conditons to the query
    $query->andWhere($conditions, $params);
}

Your addCountryCriteria service can basically stay untouched, except that you can remove buildElementsQuery(), queryAll() and populateModels(). As you only want to modify the query.


How do I enforce stronger password requirements on my Craft site?

In Craft 2

Plugins have the power to enforce stricter password requirements using the users.onBeforeSetPassword event.

Add this code to your plugin’s primary class:

public function init()
{
    craft()->on('users.beforeSetPassword', array($this, 'onBeforeSetPassword'));
}

public function onBeforeSetPassword(Event $e)
{
    $user = $e->params['user'];
    $password = $e->params['password'];

    // Perform our custom validation logic here
    $validates = true;

    if (mb_strlen($password) < 7)
    {
        $validates = false;
    }
    else if (!preg_match('/(\d.*[A-Z]|[A-Z].*\d)/', $password))
    {
        $validates = false;
    }

    if (!$validates)
    {
        // Set $performAction = false so the password doesn't get saved
        $e->performAction = false;

        // Add a custom validation error on the user
        $user->addError('newPassword', 'Sorry but your password must contain an uppercase letter, a number, a haiku, a gang sign, hieroglyph, and the blood of a virgin.');
    }
}

In Craft 3

Craft 3 doesn't have a beforeSetPassword event, but you can pull the same thing off from your plugin's init() method using the User element's User::EVENT_BEFORE_VALIDATE event like so:

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

    Event::on(
        User::class,
        User::EVENT_BEFORE_VALIDATE,
        function (ModelEvent $event) {
            // Check if a new password was set on the user
            if ($event->sender->newPassword) {
                $password = $event->sender->newPassword;
                $errors = [];

                // Perform our custom validation logic here
                $validates = true;

                if (mb_strlen($password) < 7) {
                    $validates = false;
                    $errors[] = 'Too Short!';
                }
                else if (!preg_match('/(\d.*[A-Z]|[A-Z].*\d)/', $password)) {
                    $validates = false;
                    $errors[] = 'Too Simple!';
                }

                $event->isValid = $validates;

                if (!$validates) {
                    /** @var User $user */
                    $user = $event->sender;
                    foreach ($errors as $error) {
                        $user->addError('newPassword', $error);
                    }
                }
            }
        }
    );
}


Workflow for re-using data structures and setups across projects?

You have several options to solve this issue.

Import fields via plugin

You could create a plugin that handles all these function in it's installation callback so you would install the plugin and have all the things you need. But this requires basic knowledge in plugin development.

Advantage When Pixel and Tonic make any changes in their install process it won't infect your code and if you are going to make multiple plugins you'll be able to import the required fields whenever you use them. That's the way we handle it and when someone installs my Form Builder Plugin it includes a form relation field to each entries with type pages and some crop rules automatically.

Disadvantage You need to learn php

Import via SQL

Install a new Craft instance, insert all your default values you would like to have for a new project and export the entire database. When you start a new project you would skip the installation process but import your included database and the files of your "default" instance.

Advantage This is an easy and really fast way to achieve your goal. It does not require any knowledge or skills

Disadvantage You should pay attention when Pixel and Tonic makes core changes in their default structure or things could eventually break in the future (even if it is really unlikely because they are really careful with those things)

Search for a plugin from someone else that handles it

I know there is a Feed Me Plugin maybe this does what you want but I never really used plugins that are not created in my company so I can't tell something about it


This is most likely due to a conflict between your database and project config. Conflicts can arise even if the useProjectConfigFile config setting isn’t enabled, if any changes were made directly on the database rather than through the appropriate service.

To fix, first make sure you’re on the latest version of Craft, and then run the following command from your terminal:

./craft project-config/rebuild

Then try recreating your volume.


Allow user to delete their own account from the front end

I think you hit the nail on the head!

Including the userId in the front-end form give them the perfect opportunity to delete whoever they want... even just by guessing random IDs. That definitely seems like a huge security risk.

But if you build your own custom controller, you can guarantee that the current user is the one being deleted.


Shouldn't the asset "View source" privilege limit access to an asset source in the selection modal?

There is an issue filed for this here https://github.com/craftcms/cms/issues/887 that you might want to consider thumbs-upping or commenting on.


Are there any benefits to using Yii's Aliases vs getenv?

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

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

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

1) Create your environment key and value.

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

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

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

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

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

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

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

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


Assuming this is for Craft 2, there are a few cookie plugins that let you read and write cookie values from a Twig template.

https://github.com/nystudio107/cookies

https://github.com/lewisjenkins/craft-lj-cookies

https://straightupcraft.com/craft-plugins/?q=cookie


Set asset uploader from frontend entry form

I ended up using an event to accomplish this:
use yii\base\Event;
use craft\elements\Asset;
use craft\events\AssetEvent;

Event::on(Asset::class, Asset::EVENT_BEFORE_HANDLE_FILE, function (AssetEvent $event) {
    $asset = $event->sender;
    // This does not work because it doesn't set the `uploaderId` property on the asset
    // $asset->setUploader(Craft::$app->getUser()->getIdentity()); // Doesn't work, bug?

    // So I just set the `uploaderId` directly
    if (is_null($asset->uploaderId)) {
        $asset->uploaderId = Craft::$app->getUser()->getId();
    }
});


You've probably got "Purify HTML" checked under your Rich Text field's settings and that's stripping iframe code.

You should really only have that checked if you're accepting input from untrusted sources using the Rich Text field on the front-end of your site.  Presumably all users using the control panel are trusted enough not to input malicious code into the site.


Doing it through a plugin would be the easiest way.

https://docs.craftcms.com/v3/extend/plugin-guide.html

Your plugin would have a controller that the form would post to, then you'd grab the data from POST and do whatever you want with it.

A good example of this you can look at is the Guest Entries plugin, that allows "anonymous" front-end entry submissions.


Can Craft handle thousands of entries and high traffic?

Sure... we've seen Craft installs with several hundred thousands entries/users/assets/etc. running in the wild, but there really isn't a simple answer to this question.

As you pointed out, performance can be heavily dependent on the server and environment that Craft is running on, but that goes for any web application.

Performance on the front-end is also dependent on your templating architecture and code.  Craft does provide some {% cache %} tags that can help with some of the more database dependent parts of your templates.

If traffic to your site is significant enough, you can also consider putting something like Varnish in front of Craft as well as another layer of caching.


P&T Contacform plugin: what's the use of the errors if fields are not required?

In the ContactFormModel you can manage the attribute 'required' => true.

The example just show how to use the getErrors for each field.

You can delete the ones you dont need.


Structure Navigation with 3 possible Link Types using Nav tag

I am doing something similar in using a Structure for the main navigation.  Below is what I have.  Hope it helps!
{% set menu = craft.entries.section('navMain').all() %}

{% nav link in menu %}

    {% if link.menuEntry|length %}
        {% if craft.app.request.absoluteUrl ==  link.menuEntry[0].url %}
        {{ link.title }}
        {% else %}
            {{ link.title }}
         {% endif %}
    {% else %}
        {% if craft.app.request.absoluteUrl ==  link.menuCustomURL %}
            {{ link.title }}
        {% else %}
            {{ link.title }}
        {% endif %}
    {% endif %}
    {% ifchildren %}

            {% children %}

    {% endifchildren %}

{% endnav %}


Possible FeedMePro Anomaly When Trying to Update Table

I believe this has been fixed in the latest version of Feed Me (3.0.2.1 at time of writing). Do feel free to comment if not, or get in touch via the Feed Me support channels


Getting error: "Matrix do not have a method or closure named..."

On your screenshot of the block type powitanie you see two fields: powitanieNaglowek and powitanieTresc. Yet, in your code you try to use

{{ block.naglowekPowitanie }}

The error simply says: in the block type you are using there is no field called naglowekPowitanie.

Changing that H2 to

{{ block.powitanieNaglowek }}

should do the trick.


How to set multiple 'active' classes from query string

Perhaps not the most elegant solution, but this works:

{% set queryTags = craft.request.getQuery('tags') %}

{% for tag in craft.tags.relatedTo(resourceTags) %}
   <a
     href="{% if tags == "" %}/learning/resources?tags={{ tag.title|kebab }}{% else %}/learning/resources?tags={{ tags|replace(' ', '+') }}+{{ tag.title|kebab }}{% endif %}"
     class="btn btn-default{% if tag.title|kebab in queryTags %} disabled{% endif %}"
   >{{ tag.title }}</a>
{% endfor %}

Adding disabled rather than active.


Could you double check your General Settings (Settings > General) to make sure there's no "/index.php" in the Site URL field?


I found the solution: The moveFiles function provide the function to move a file:

/**
* Move or rename files.
*
* @param        $fileIds
* @param        $folderId
* @param string $filename If this is a rename operation or not.
* @param array  $actions  Actions to take in case of a conflict.
*
* @throws Exception
* @return bool|AssetOperationResponseModel
*/

public function moveFiles($fileIds, $folderId, $filename = '', $actions = array())

So for me the following code works to move the file with the id 21 to the folder with the id 3:

craft()->assets->moveFiles([21], 3,'', "keepBoth");


Can a plugin add JS or CSS to the control panel?

One way to accomplish this is to write some code in the init() function of your root plugin file. So in your example that might look something like:

class MyFunkyPlugin extends BasePlugin
{

  function init() {

    // check we have a cp request as we don't want to this js to run anywhere but the cp
    // and while we're at it check for a logged in user as well
    if ( craft()->request->isCpRequest() && craft()->userSession->isLoggedIn() )
    {

      // the includeJs method lets us add js to the bottom of the page
      craft()->templates->includeJs('var Myjs = "foo";');

      // the includeCss method will add css in the head
      craft()->templates->includeCss('.myFunkyPluginSelector { color: red; }');

      // the includeJsResource method will add a js file to the bottom of the page
      craft()->templates->includeJsResource('myPlugin/js/app.js');

      // the includeCssResource method will add a link in the head
      craft()->templates->includeCssResource('myFunkyPlugin/css/style.css');

    }

  }

  // rest of the root plugin file
  ...

}

The nice thing about the init() function is it gets run before anything else in your plugin does and it gets run on every request as every installed plugin is initialised on every request.

More detail on the available functions can be found in the TemplatesService docs.