It depends. If the only files you want to upload are .jpg, .jpeg, and .png, then yes: set allowedFileExtensions in your config/general.php

'allowedFileExtensions' => 'jpg, jpeg, png',

If you want those to be the only image filetypes, but  to allow other files for non-image assets, then that is harder (and I don't know how to do it).


How to query EntryQuery for multiple sections with varying types

I'm not sure you can achieve this in one query.
The easiest solution I can think of to your problem requires 3 queries:
{% set peopleIds = craft.entries().section('people').type('Employees').ids() %}
{% set insightIds = craft.entries().section('insights').ids() %}
{% set query = craft.entries().id(peopleIds|merge(insightIds))

The first 2 queries are executed and return the ids of the matching entries. You can then create  your EntryQuery using the ids as the only criterion.


Different Single as homepage for other languages

You don't need to use the "This is for the homepage" checkbox at all. All it does is to (1) set up
a route to the entry template specified in the section settings and (2) make the entry model available to this template
from an entry variable.

You can just deselect this option and configure the above manually. The simplest way to set up a route for the homepage is
to create a template index.html in craft/templates/ and Craft picks it up automatically for hompage requests (→ see pt. 6 in the routing docs).

You can now query for the entry model (your "single") depending on the requested locale to solve (2) and then include a template partial
into the index.html to solve (1).

{% if craft.locale == 'fr' %}
    {% set entry = craft.entries.section('home_fr').first() %}
    {% include '_pages/home_fr' %}
{% elseif craft.locale == 'de' %}
    {% set entry = craft.entries.section('home_de').first() %}
    {% include '_pages/home_de' %}
{% else %}
    {% set entry = craft.entries.section('home').first() %}
    {% include '_pages/home' %}
{% endif %}


Set the application name for Craft in New Relic

This method uses New Relic's PHP API, rather than php.ini, and is working well for me. It seems to be catching everything that I need to monitor. Any applications I've not done this for remain in the default application.

Just stick this in Craft's index.php file:

if (extension_loaded('newrelic')) {
    newrelic_set_appname('NAME OF SITE');
}

I haven't tried it with ExpressionEngine or Wordpress, but the principle would be the same?


Append a string to the end of all rich text links

I found a way to use the RetconHTML plugin for what I wanted. I just utilized the Attr filter and it worked the way I wanted. I had to also remove the way Retcon would add spaces to appended strings.

{{ block.paragraph | retconAttr('a', { href : '?some_text' ~ entry.title|kebab }, false) }}

By including false, it adds to the already existing href, rather than overwriting it.


Contact Form plugin - Not receiving emails at separate addresses

First, make sure you're on the latest version of the plugin. There was a bug related to sending to multiple addresses that got fixed several months ago.

If that's not it, have them check their SPAM folder to see if it's getting trapped in there.

If it's not in SPAM, then try using a different email protocol under Settings->Email in the control panel, preferably SMTP based.  phpmail is notoriously unreliable at email delivery.


In Craft 2 the log files are usually stored in craft/storage/runtime/logs/ and named after the plugin handle.

If the Import plugin does nothing special, you should find its logs in craft/storage/runtime/logs/import.log


Can we use PHP in our templates instead of Twig?

No this is not possible.

If you really need PHP, you can write a plugin with one of the following:

a Template Variable class, which can be accessed from your templates via craft.myPluginHandle
a Twig extension, which can add its own global filters, functions, or tags, etc.
a controller, which can execute PHP before calling $this->renderTemplate(...)
a template hook, which allows PHP code to access and modify the current template variable context, wherever the {% hook %} tag is placed.

Why Twig anyway?

The question to use php in templates comes up every once in a while, but there are good reasons for not using it.

For example if Craft changes its code you don't have to worry about your templates. The upcoming update of the Yii framework would force you to rewrite all your templates if they were written in php.

There is also a great article from Fabien Potencier on this topic -- tl;dr php is too verbose for templating.


I would recommend running the Craft queue in a background process as detailed here. This way it won't bog down your control panel and can just chug away in the background.


One thing that’s important to remember is that there’s no inherent relationship between your templates and sections. Any template can show any entry from any section. So there’s no such thing as the “current section”.

If your section has the “Entries in this section have their own URLs” setting checked, and you’re requesting one of that section’s entries’ URLs, then the section’s template will get loaded automatically and an entry variable will be available to it, which represents the entry whose URL you requested. But that doesn’t mean that you couldn’t have loaded the same template some other way (e.g. via an {% include %} tag from another template), or that you couldn’t have manually fetched that entry some other way (e.g. with craft.entries).

Anyway, if you have an EntryModel variable set (such as that entry variable that you’d automatically get when accessing an entry’s URL), you can access it’s section using entry.getSection(), and you can access that section’s handle via its handle property.

{% set section = entry.getSection() %}
{% set handle = section.handle %}

That could be simplified to:

{% set handle = entry.getSection().handle %}

or even:

{% set handle = entry.section.handle %}


Displaying related image assets from another section with twig

The best way is to change home team ID and away team ID to entry fields that select the club. If you are using a plugin like Feed Me to bring in the matches, and you have all of the club IDs already entered in the club entries, you should be able to import matches and use the home and away IDs and match them to the appropriate Club in Feed Me.

Once that is done, you won't have to loop through all of the Clubs, just pull the one from each of the matches home and away fields like so:

{% for team in fixture.homeTeamId.limit(1).all() %}
    {% for logo in team.clubLogo.limit(1).all() %}
        <img src="{{ logo.getUrl() }}" />
    {% endfor %}
{% endfor %}

If your API isn't brought in with Feed Me (or akin) and you can't change the home/away fields to entry selectable fields, you can select a team by matching its ID field to the given ID field like so:

{% set awayId = fixture.awayTeamId %}
{% set awayTeam = craft.entries.section('clubs').search('clubId:'~awayId).limit(1) %}
  {% for team in awayTeam %}
    ...


You call it a bit of a hack, but I think the way you're doing it is the proper way:

$criteria->dateCreated = '>= ' . $fromDate->format(DateTime::MYSQL_DATETIME);
$criteria->dateUpdated = '<= ' . $toDate->format(DateTime::MYSQL_DATETIME);

Your last example would work (with a syntax change) if you were you looking for a date range within a single field (either dateCreated or dateUpdated) like so, but not across two fields:

$criteria->dateUpdated = array('and', '>='.$fromDate->format(DateTime::MYSQL_DATETIME), '<='.$toDate->format(DateTime::MYSQL_DATETIME));


did you register the routes in your plugin's init() ?


The Volumes service is available via craft.app.volumes, so you can do
{% set volume = craft.app.volumes.getVolumeByHandle('volumeHandle') %}
{{ volume.name }}

or
{% set volume = craft.app.volumes.getVolumeById(123) %}
{{ volume.name }}

Additionally, if you have an Asset model available, you can get its volume like this:
{% set volume = asset.volume %}
{{ volume.name }}


What you're currently seeing is the correct URL for a GET form submission.

You'll need to update the onsubmit handler to construct the URL you wish to visit, based on the selected option.

Here's how to do that, using the omnipresent jQuery. You could probably do the same in plain JS, but I have no idea which browsers you're targeting:

(function ($) {
    $('#sitemap').on('change', function (event) {
        var $select = $(this);
        var $form = $select.closest('form');
        var url = '/desired/path/to/development' + $select.val();

        window.location.href = url;
    });
}(jQuery));

Don't forget to remove the current handler from the markup.


Any login validation errors are passed back to the form and available via the Twig {{ errorMessage }} variable.

You can see an example of how to display them on a login form here: http://buildwithcraft.com/docs/templating/login-form


Import CSV with FeedMe - multiple categories

You can do this with CSV by setting a dataDelimiter in a config/feed-me.php files (see docs).
If you set the dataDelimiter to be | for example, then your CSV could be:
"title", "Categories"
"Some title", "Category1|Category2"

Doing this should let you import multiple categories from CSV.


Update 3.5.15.1 - Fixed a bug where the Updates utility wasn’t showing “Update” buttons

Not a URL, but you can do it from the command line by running either:
php craft update craft

Or
composer update craftcms/cms

You can see the issue for this here: https://github.com/craftcms/cms/issues/7091


How to pull all images in [body] Matrix fields

I think you need to use a {% switch %} statement like the following:

{% switch matrixBlock.type %}

{% case "text" %}

    {{ matrixBlock.textField | markdown }}

{% case "image" %}

    {{ matrixBlock.image[0].getImg() }}

{% default %}

A font walks into a bar.

The bartender says, “Hey, we don’t serve your type in here!”

{% endswitch %}

You can read more here: http://buildwithcraft.com/docs/templating/switch


Password-Protect Sections - Newbie Template Code

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

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

{% block content %}

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

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

    {% else %}

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

    {% endif %}

{% endblock %}

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


How can I fix Image Exception error when uploading image via front-end form while Preparse plugin enabled?

Fixed in Preparse Plugin v 1.0.3.

View changelog at: https://github.com/aelvan/Preparse-Field-Craft/blob/craft3/CHANGELOG.md#103---2018-10-24


As a silly mistake, I had set the allowUpdates config setting to false...hope this helps anyone making the same silly mistake.


How to perform a redirect and pass on data?

I would suggest either using query parameters in your redirect method, or storing it in session variables (which should be no different than regular PHP).


Structure unable to find _entry template

Got to the bottom of it!

The _entry template relies on the fact there's a template error.

On the production site I'd added,

'suppressTemplateErrors' => true

to the config, which stopped the behaviour that was being relied upon to serve the generic template


Translating the option labels of Dropdown, Checkboxes or Multi-select fields

Use the translate (or t) filter:

{{label|t}}

See how to translate static text in templates


Advanced search returning 'Key "1" for array with keys "0" does not exist'

This error could happen if the p param is set to something, but the value doesn’t actually contain a hyphen.

If you want to find out what the problematic value is, enable Dev Mode and then check your browser console > Application Log > GET Info or POST Info (depending on the request type).

To make your code more bulletproof you can replace this line:

{% set price = price ? price|split('-') %}

with:

{% set price = (price and price matches '/-/') ? price|split('-') %}


Short answer: Cannot be done :)

Twig doesn't really have a "parse order", it compiles to plain old PHP. Upon compilation, this would make your Twig snippet basically this:

$myVariable = 'someInitialValue';
echo $entry->title . $myVariable;
$myVariable = 'example';
echo $entry->title . $myVariable;

Obviously, setting $myVariable to "example" the second time won't affect the output of the first echo statement.

An obvious way of accomplishing basically the same thing, would be to wrap the whole template in a {% set %} call – then your options would be many, you could for instance use placeholders and the replace filter:

{% set myContent %}
   {{ entry.title | myFilter }} {# myFilter appends placeholder "%myPlaceholder%" to the input #}
{% endset %}
{# Echo the template, replacing the placeholders with whatever #}
{{ myContent | replace( { "%myPlaceholder%", craft.myPlugin.getVar } ) }}

I get that this is not what you really want though.

If you really want to get your hands dirty you can see if you can find a way to manipulate the Twig internals.

You can access the Twig instance in your plugin via the TemplatesService:

$twig = craft()->templates->twig;

I guess that one way to achieve what you want is to write your own Lexer – fairly ambitious stuff for doing something easily done by extends, embeds or by output buffering/set and replace as per my example above.

Other concepts to look at (which may or may not be useful to your quest!) are Template hooks, global Twig variables, template embedding or Twig's StringLoader extension.

Finally, I'm quite curious about the actual use case here. If you'd care to elaborate, I'd love to know why you need/want to do this.


In Craft 2, check if plugin is available for Craft 3?

click the updates link in the footer and you should see this at the bottom of the page.


Get random entries, excluding an array of entry IDs, where a relational field is not empty

That seems to be a bug / limitation of the :notempty: statement, it's probably adding its own ID criteria in the background and the merge doesn't work as expected. I'd suggest you file a bug report.

In the meantime you could just add the "not empty asset field" criteria making use of the relatedTo parameter as a workaround. See How can I get only those entries with non-empty assets field for example code.


Can someone demystify tasks with a little more detail?

One pitfall: In Craft 2 I've had troubles with a script that created thousands of entries.

The CP totally hung because it could not process the numerous tasks that were created (tasks of the like 'updateUri' ..). This was probably due to time limits and memory consumption; tasks were not processed anymore.

To solve that I created a task runner plugin, that processes a limited amount of tasks in the background through a cron job, and disables task execution on control panel access.


CORS preflight check failing for AJAX login form on different domain

Thanks for posting your headers, that was very helpful. I experienced a very similar problem just the other day and was able to work it out via trial and error and a whole bunch of Google/StackOverflow searches. I'm hoping I can save you the trouble.
So your .htaccess was not set up to respond to OPTIONS requests, which are sent as a preflight before every request. That can be fixed with the following:
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>

This just ensures that the server sends an OK response to a preflight request.
Now you have to make sure that your origin domain is actually allowed to make the request you're attempting. Here's how I solved this:
<ifModule mod_headers.c>
    Header always set Access-Control-Allow-Headers "X-Requested-With, Authorization, Content-Type, Request-Method"
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    SetEnvIf Origin "^http(s)?://(.+\.)?(localhost|staging.example.com|example.com)(:[0-9]+)?$" origin_is=$0
    Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
</ifModule>

We're updating the headers to allow certain kinds of requests through which would normally be disabled by default. You can adjust these as necessary, for example you may not need to allow Authorization headers or DELETE requests.
Then we whitelist a specified list of origins that should be allowed to make the request in these magic lines:
SetEnvIf Origin "^http(s)?://(.+\.)?(localhost|staging.example.com|example.com)(:[0-9]+)?$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

Update the list of allowed Origins, including a pipe between each. You may be able to wildcard entire domains. What this is doing is saying "if a request is from staging.example.com, set the appropriate Access-Control-Allow-Origin header."

Update from the future: Craft 3.5 includes an allowed GraphQL origins config setting which should presumably make this much simpler. You should be able to leave out both of the origin-related headers in my second example.


I would also think about spam bot registrations, which can be a big problem, and the more popular your site, the more attempts you're likely to see.

I'd add something like Google recaptcha to avoid that...we use this: https://github.com/aberkie/craft-recaptcha - and have had zero spam registrations since launch.

With our old site we used to get them all the time (not Craft but a form is a form, so that doesn't really matter I think!)


Using Recaptcha v2 and Guest Entries plugin

I don't have much experience with these, but here are some potential plugins you can look into for Craft 2 that use various anti-spam techniques:

https://straightupcraft.com/craft-plugins/snaptcha

https://straightupcraft.com/craft-plugins/sprout-invisible-captcha

https://straightupcraft.com/craft-plugins/google-recaptcha

https://straightupcraft.com/craft-plugins/spam-guard


Feedme Skip Entry if No Matching Elements on Field

isValid should be set on the $event variable, not on $event->element:
Event::on(Process::class, Process::EVENT_STEP_BEFORE_ELEMENT_SAVE, function (FeedProcessEvent $event) {
    $myField=$event->feedData['myfieldname'];
    if ($myField == null){
        $event->isValid=false;
    }
});

I haven't tested but looking at the feed-me source code, this should work.


Craft Commerce: Bakery site with multiple bakeries (How to handle product types and variants)

The way I see it, you have 2 very similar but different problems:

How does each bakery log in and maintain their own inventory?
How does each cake get built up in the admin?

You can pull off the separation of locations by using different permissions. Each Bakery would have their own user login (Store #1, Store #2, etc.) By setting the appropriate permissions, when Bakery #1 logs in, they won't see any other entries/products other than their own. To filter this on the front end, you just ask Craft to filter by the appropriate author. So in other words, Author = Bakery location.

We built a site where we had different, individual teachers logging into the same Craft install. Every teacher could post their own class schedule. We also didn't want them to view or edit each other's classes (but everyone on the front end could see and filter them for example). In your case, it would basically work the same way.

The second problem is much more complex.  I don't think you're that far off from creating a single "cake" product or different cake products (depending on what you're selling), for example, and then creating different options (variants) around those basic types. How you build up those products really depends on how you manage inventory, SKUs, variants, prices, etc. If some of those options don't have prices (eg butter creme frosting doesn't cost more than vanilla), it's just an additional option, that gets to be a bit easier in terms of options. You can just pull those options off with Craft fields.

Another way to pull it off on the admin side would be if the bakeries know how to upload an Excel/CSV file to the site, they could update their availability from that. It would be up to the you, the developer, to figure out what data is available and some sort of function to make it work. That's a pretty common use case for, say, a manufacturer with thousands of SKUs. To change pricing one-by-one in the Craft admin would take hours. But a CSV lets you bump all the prices up or down with just a couple Excel macros, which you can then export out.

FWIW, these are both non-trivial problems to solve. There's probably other ways to do so but Craft gives you some tools to help pull this off.


Fetch all entries with dateUpdated after a given time

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

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

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

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


I know this is a while ago but I ran in to the same issue and thought I'd share the culprit in my particular case.

I adjusted the upload_max_filesize value via a php.ini file from the default 2MB to 10MB, but I misspelled the configuration as upload_max_filesize = 10MB instead of upload_max_filesize = 10M. I was seeing the exact same error as the original poster. Once I fixed the setting, changing 10MB to 10M, the error went away.


Craft Admin assets do not load over https (Apache & Cloudflare)

The solution I came to was creating an Origin Certificate on Cloudflare, installing on it on my VPS, and setting the SSL setting to Full Strict. Now admin assets are loading perfectly over https. No .htaccess or Patrol plugin required.

If someone knows more about why this worked and the previous approach didn't I would love to hear your thoughts. Thanks @MattStein for clueing me towards this approach.


Commerce: Is shippingZone available as a variable on the shipping method selection template?

If postie plugin can't add zone support, then you will need to limit the selections options on the front end and need the zones available in twig.

Postie implementing it would be the first preferance, as there would be no backend validation if you just hid the options in twig.

There currently is no craft.commmerce.shippingZones twig variable. You would need to expose it with a plugin.

Creating a plugin at the http://pluginfactory.io with a variable class.

To the variable class within the plugin add:

public function getAllShippingZones()
{
  return craft()->commerce_shippingZones->getAllShippingZones();
}

public function matchZoneAndAddress($zone, Commerce_AddressModel $address)
{
    if ($zone->countryBased)
    {
        $countryIds = $zone->getCountryIds();

        if (in_array($address->countryId, $countryIds))
        {
            return true;
        }
    }
    else
    {
        $states = [];
        $countries = [];
        $stateAbbrs = [];
        $stateNames = [];
        foreach ($zone->states as $state)
        {
            $states[] = $state->id;
            $stateNames[] = $state->name;
            $stateAbbrs[] = $state->abbreviation;
            $countries[] = $state->countryId;
        }

        if ($countries){
            array_filter($countries);
        }
        if ($stateNames){
            array_filter($stateNames);
        }
        if ($stateAbbrs){
            array_filter($stateAbbrs);
        }

        $countryAndStateMatch = (in_array($address->countryId, $countries) && in_array($address->stateId, $states));
        $countryAndStateNameMatch = (in_array($address->countryId, $countries) && $this->_inArrayCaseinsensitive($address->getStateText(), $stateNames));
        $countryAndStateAbbrMatch = (in_array($address->countryId, $countries) && $this->_inArrayCaseinsensitive($address->getStateAbbreviation(), $stateAbbrs));

        if ($countryAndStateMatch || $countryAndStateNameMatch || $countryAndStateAbbrMatch)
        {
            return true;
        }
    }

    return false;
}

/**
 * @param $needle
 * @param $haystack
 * @return bool
 */
private function _inArrayCaseinsensitive($needle, $haystack) {
    return in_array(strtolower($needle), array_map('strtolower', $haystack), false);
}

This would allow you to do:

{% set doesMatch = craft.yourPlugin.matchZoneAndAddress(zone, cart.shippingAddress) %}

To determine if an address is within a zone.

Hope that helps.


Matrix field alternative (or easier field grouping)

Depending on the amount of data required (and if it makes logical sense to have the data separate from the entry) I'd probably set this up as a "Contact details" section itself, using the individual matrix fields from your example.

Then if some other entry type needs it, set up an entries field in that entry type, limited to entries in the "contact details" section and limited to the number of contact detail entries you want to show, probably 1 going by your example.

This way you can still have your details grouped (as a separate entry) but it's more compact as a single field rather than a matrix in the fields of the entry you're using the contact details in.

The trade-off is that you need to enter your contact details entry before you try to add it to your "x" entry field.

If it makes no logical sense to have the fields as entirely different sections and your fields are getting cumbersome I think the best way is to put them in a different tab to at least remove the clutter.


Build a Property Management System with Craft CMS?

I think the other answers here are accurate in saying that this could very well be a use case for a more specific application.

That said, I'm optimistic that this would be possible on Craft with enough planning, budget, and development time; and our own efforts while building out the Sprout Plugin Suite have had use cases like this in mind.

So, I'm going to try to answer how I'd approach this as a Craft project. There would be lots of questions to get answered along the way, so this is really just a very top level overview of where I'd start to look and ask questions.

I'll frame my answer around the plugins I'm most familiar with.  It's likely with the amount of custom development necessary, multiple plugins or starting points would take a similar amount of time.

1. site visitors can search for rental units (on Map too)

For this requirement, I'd look into Smart Map and reach out to Lindsey DiLoreto if you have specific questions.

2. tenants can pay rent online

This is possible in some form or another with Charge, Sprout Commerce, and Craft Commerce.  The latter two are in private and public beta as of this posting. Specific details would need to be explored.

3. tenants can make maintenance requests online

A fairly customized workflow is possible for this with normal Entries or Sprout Forms, and Sprout Email. When an Entry for a particular Section is saved via an Entry Form, or an Entry for a Form is submitted via a Form, an event will fire and you can setup a Notification in Sprout Email to trigger emails to different people (admins and users) based on different types of maintenance requests.  You have full control over the custom HTML in your notification emails and can customize the message with the information from the submitted Entry or Form.

If you have any specific questions around Sprout Commerce, Sprout Email, or Sprout Forms, you can reach out to Barrel Strength Design.

Views into your Element Listing Pages can be controlled via Element Hooks to help manage workflows and bring key information to the top level, such as the status of a request. For more specific needs, a plugin may be necessary.

4. landlords can look at their accounts (income, expenses, past due, etc)

This should be possible with default Craft functionality for a member section and you can pull data in from any native elements or any plugins that store their data in the database.

5. landlords, agents or admins can add new properties to the system

This is possible with the default Craft functionality and customizing Member Group permissions to your needs.  You'll need to build out the front-end interface, but all you need to allow people to submit data is there.  If you want to trigger email notifications when certain types of properties get added, Sprout Email has a customizable Event API so you could write or ask someone to write a custom plugin that allows emails to be triggered when certain conditions are met.

6. landlords or agents can book showings

It's unclear to me if this ties in to Calendar needs as discussed in #9 or a more general Event question where you have a showing and many people can come. Craft is pretty powerful out of the box and more specific event needs would probably need to be described for a more specific answer.

7. automated notifications/emails (rent due, maintenance complete, etc)

We have automated notifications planned (at some point) for Sprout Email, and we've worked on a some projects where we have initial implementations of this behavior. Some custom development would be needed here, but it's possible, and the notifications could be managed right in Sprout Email to make their content easy to update and manage for a non-technical user.

For things like notifying somebody when maintenance is complete, you could create a custom Email Event to allow you to trigger an email to the particular user who submitted the request when a maintenance work updates a custom field from one status to another.

8. lease document signing (DocuSign API or?)

Some Digital Signature programs allow you to embed certain document types on your website and allow them to be submitted by multiple people. I've seen various implementations of this functionality - however in my quick search around I'm not finding many examples that don't rely to some extent on the API.  I still believe they exist, but for now, my list to you is just this one:

PandaDoc

9. calendar to schedule maintenance

There are a few Calendar plugins to take a look at (in alphabetical order):

Calendar by Solspace
Calendars by Top Shelf Craft
Venti by Tipping Media

I'd recommend reaching out to each respective developer for specific questions.

10. e-commerce and accounting integration (possibly Quickbooks and banks API)

This is probably going to be the most challenging item to answer without a more detailed spec and understanding of what your budget is. Custom e-commerce and complex integrations, in general, take time to do well and get things right – especially when people are making decisions around the financial data and using that data to run the business.

Both Sprout Commerce and Craft Commerce are being built out with flexibility in mind, but I don't think either of them will be planning to integrate with Quickbooks, Xero, or banks before the more standard Commerce use cases are solved, if at all. It's likely what you're looking for here is a more custom integration targeted specifically at your accounting software and banks.

Depending on your budget, you may want to consider alternative workflows here too that combine programmatic and manual steps.

It's worth noting that most of us plugin developers are pretty friendly, and if you found yourself in a situation where Calendars or SmartMap weren't communicating with Sprout Email in a way that allowed you to send notifications like you need to, or vice versa, we like hearing about it and learning how we can help make the whole plugin ecosystem play more nicely together.

In general, I think these types of applications are a very fine line between custom application and CMS. On one hand, they are going to require customization and the app may be the better route.  On the other hand, enough components of the project revolve around general content management needs that a CMS like Craft already solves well and saves the trouble of managing your own admin UI and many other concepts that are already well established in Craft.

With a project as complex as yours, it may be worthwhile to consider a hybrid approach too. It may be that 80% of your project is a really good fit for Craft and that it makes sense to outsource certain functionality like Scheduling or Commerce to another platform.  Over time, maybe there will be an opportunity to migrate those types of features to Craft at a lower cost.

A lot of trade-offs like that are hard to make, but putting a price tag on your options helps you and your client choose the best approach for the current budget, and prioritize improvements down the road.


Could you please let us know where do you output the {% sproutseo 'optimize' %}tag?

The {% do craft.sproutSeo.meta({ tag need to be called before the optimize tag. I'll give you a basic example of how you can do this:

Let's say that you have a _layout where you have the html head tag:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
    {% block head %}
        {% sproutseo 'optimize' %}
        ...
        ...
    {% endblock %}
    ...
</head>

In your section/_entry (extends your layout) When you need to override something with the do craft.sproutSeo.meta you may need to call it before the {% sproutseo 'optimize' %} so you can do something like:

{% extends "_layout" %}
{% set title = entry.title ~ " - Blog" %}

{% block head %}

    {% set cardEntryImage = craft.imager.transformImage(entry.blogHeaderImage.first(), { width: 1920, ratio: 16/9 }) %}
    {% do craft.sproutSeo.meta({
        ogImage: siteUrl ~ cardEntryImage.getUrl(),
        ogImageSecure: siteUrl ~ cardEntryImage.getUrl(),
        ogImageHeight: cardEntryImage.getHeight(),
        ogImageWidth: cardEntryImage.getWidth(),
        ogImageType: cardEntryImage.getMimeType(),
        twitterImage: siteUrl ~ cardEntryImage.getUrl()
    }) %}
    {{ parent() }}

    ...
    ...
{% endblock %}

Best,

-Andre L


Freeform email notifications going to spam

Email that is sent from your server is often marked as spam, this is not anything to do with either Craft or Freeform but with the fact that the server you use may not be trusted by email providers. Read the article Brad recommended for some background info. Then do yourself a favour and use a transactional email service to deal with all the hassle for you, sending email from their servers. A lot of them are free for smaller use. I use Postmark, you can easily implement it in Craft, see https://postmarkapp.com/support/article/1019-use-postmark-to-send-emails-from-craft
I've also used Sendgrid in Craft with succes (with a little help from this page https://mediasurgery.co.uk/video-episodes/craft/make-sure-those-crucial-emails-get-delivered-with-craft-cms-and-sendgrid). Others include Mailgun, Sparkpost, or just Google 'transactional email service' for others.


Want to add a custom field in craft commerce plugin

Yes, you can create new field and you can add it in it.
To add it, please go to admin panel and then follow the below mentioned sections.
settings > product type > add/select product > product fields

Then you can add custom field on field layout.
I hope this will help.


Has anyone been able to get auto-complete working with plugins + PHPStorm?

Absolutely, just add the app directory as a external library.

Double click on the 'external libraries'.
Add the interpreter
Include the path to the 'app' directory


Access site via local dev IP address => certain image path (xxx.dev) isn't replaced with IP address

This issue suddenly fixed itself without any changes that I'm aware of... Thanks anyway!


Setting User's Groups - Integrity Constraint Error

UserGroupsService->assignUserToGroups takes a user's ID as the first parameter and you're passing in the entire UserModel.

Change it to this and it should work:

craft()->userGroups->assignUserToGroups($user->id, $groupIds);


Handling path to assets (css, js) between local and production environment

If you are using Craft 3 Multi-Environment then you can add environment specific aliases to those assets in your general config file.

Eg:

// Live (production) environment
'live' => [
    // Craft defined config settings
    'allowUpdates' => false,
    'backupOnUpdate' => false,
    'devMode' => false,
    'enableTemplateCaching' => true,
    'isSystemOn' => true,
    // Aliases parsed in sites’ settings, volumes’ settings, and Local
    volumes’ settings
    'aliases' => [
      '@assetPath' => '/dist/'
    ],
    // Custom site-specific config settings
    'custom' => [

    ]
],

Then, in your templates you can use {{ alias('@assetPath/js/' }}.

Note, while I've called the alias assetPath, you can name it anything you like.


Users not fully deleting from the database

It definitely does sound like a missing foreign key constraint issue.
The Craft 2 version of the Fix FKs plugin for Craft 3 still lives here: https://github.com/pixelandtonic/craft-fixfks
I'd give that a whirl.


Possible to view field handles on entry type field definition page?

You can use the CP Field Links plugin to view the handles on the edit entry pages.

And it sounds like this feature request is very close to what you're asking for.  You might want to throw a vote its way.


There’s no such thing as a “global” widget right row, however your widget’s getBodyHtml() method can return false, which tells Craft to not show the widget at all. So you could just have the widget assigned to all the user accounts you want, and always return false unless there’s actually something to display.


How to create an element query in PHP with custom field names in variables?

You can use the Craft::configure() for that.. imagine your array looks like

$criteria = [
    'age' => ['and', '>=30', '<=50'],
    'enabled' => false
    'status' => null,
]

You can do

$query = User::find();
Craft::configure($query, $criteria);

$users = $query->all();

Or you can just use the parameter based syntax instead of the functional one

$users->$key = $value;

You'll receive a deprecated error when you don't fetch your users. Your so called $users variable hast actually nothing to do with users it's a really bad name because one might think users store information about a list of users but that's not the case. $users is a Query that looks for elements with the type craft\elements\user in your database. You'll receive your list of users when you execute your Query with one(), all() or something like that.


Okay. I found the solution If anyone runs into the same issue.
This must be a server problem which I can't solve so there is a htaccess solution to remove the .html extension:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\.html$ /$1 [L,R=301]


How can I set a Neo field's values from my plugin/module?

I just figured it out.

For anyone facing the same issue, here is what worked for me:

$entryData = self::setupEntryData($item);
$entry = new Entry($entryData);

$fieldValues = [
    'blocks' => [ // Neo field handle
        'new0' => [
            'modified' => 1,
            'type' => 'wysiwyg', // Neo block handle
            'enabled' => 1,
            'collapsed' => 0,
            'level' => 0,
            'fields' => [
                'grayBackground' => true, // Neo block field
                'wysiwyg' => '
test
', // Neo block field
                'buttons' => null, // Neo block field
            ]
        ]
    ],
];

$entry->setFieldValues($fieldValues);


In craft/config/redactor you can either add your own setting file, or edit one of the ones already there. If you add the following JSON:

"formattingAdd": [
  {
    "tag": "sup",
    "title": "Superscript"
  }
]

You will have access to a Superscript choice under the Formatting buttons dropdown menu. This will wrap the selected text in a <sup> tag.

While I'm not sure if this is the correct way of adding stuff like this, I just tested it and it does work.


"An unknown error occurred." when changing profile avatar image

Check the log files in craft/storage/runtime/logs and search for [error] or [warning] to see the underlying error message.


Craft 3.1 no longer updates all entries on section save - workaround?

Even if it's building another plugin to somehow listen for a save section event (is there such an event) manually triggering the same thing?

There's an EVENT_AFTER_SAVE_SECTION event you can listen to where you can put a ResaveElements job in the queue for the specific entries in the section that was just saved.


How to install cURL on Ubuntu 14.04 to run Craft CMS?

You likely need to go to /etc/php5 and either make a link or move curl.ini from the mods-available folder to mods-enabled. (Just did this myself on Ubuntu 14.04 server)

Also, be sure to restart apache:
    su apache2ctl restart


Suspended users who have never logged in

This probably works: craft.users.status('suspended').lastLoginDate(':empty:')


Not super familiar the LinkIt, but there is a feature request for email obfuscation here: https://github.com/fruitstudios/craft-linkit/issues/58 you can comment on and you can make a new one for phone numbers.

In the meantime, some other plugins might do what you're looking for:

https://plugins.craftcms.com/obfuscator

https://plugins.craftcms.com/phone-number

Or you could make your own custom link types: https://github.com/fruitstudios/craft-linkit#custom-link-types


How can I join plugin data with entry data based on uri?

You could of course manually create a select query with a join that fetches entries ordered by hits descending, however you will basically be recreating what $criteria->find() does above. I imagine this will introduce an almost negligible performance improvement, but it really depends on how many entries you are returning.

The following code should give you a starting point:

$query = craft()->db->createCommand()
    ->from('pluginTable pluginTable')
    ->join('elements_i18n elements_i18n', 'elements_i18n.uri = pluginTable.uri')
    ->join(...)
    ->order('pluginTable.hits desc');


How do I change the value of a select box field of a certain entry through the front-end?

Twig
You can check for certain URL parameters via
{% set param = craft.app.getRequest().getParam('id') %}
{% if param is not null %}
    {% set entry = craft.app.getElements().getElementById(param) %}
    {# insert your new field value! not the label, the value/handle
    {% do entry.setFieldValue('fieldHandle', 'Reserved') %}
    {% do craft.app.getElements().saveElement(entry) %}
{% endif %}

However I would not recommend doing this in Twig
PHP
You can see how to use Template Variables and create your boilerplate at pluginfactory.io
{% do craft.myAwesomePlugin.saveEntry(entry, {fieldHandle: 'yourValue'}) %}

And your function could be
public function saveEntry($entry, $attributes = []){
    $entry->getContent()->setAttributes($attributes);
    craft()->entries->saveEntry($entry);
}

That's just a "quick" version, I suggest you to take a look at EntriesController to see how Craft handles it in detail


I found out, what my mistake was: If you import only one locale the fields must not be mandatory, otherwise you get an error about fields (the ones in the other locale) being empty.


Here are the required Craft 3 file permissions: https://docs.craftcms.com/v3/installation.html#step-2-set-the-file-permissions


How do I render pages with dynamic content from a json feed

To fetch an entry from your module and pass it to a template you can do:

$entry = \craft\elements\Entry::find()->one();
return \Craft::$app->getView()->renderTemplate('path/to/template', ['entry' => $entry]);

Then in your template, you will have access to an entry variable.


Is it possible to execute ./craft via cron?

Explanation for answer
Since the craftcms supplied CLI file aka ./craft uses the following to identify it's location:
// Set path constants
define('CRAFT_BASE_PATH', __DIR__);
// the above finds out the file path where the ./craft file is stored in

There's no need for any cd commands in your crontab, so all you have to do, is make sure that you either have your ./craft file marked as executable (chmod +x path/to/craft) or explicitly call it with php or usr/bin/env php
Since according to your observations /home/USERNAME/CRAFTROOT/craft backup doesn't work in cron, I'd suggest using the following instead
TLDR: (solution)
/usr/bin/env php /home/USERNAME/CRAFTROOT/craft backup
Potential causes of issue
Now I won't guarantee that this was the cause, but it might just be the case.

The User running the cronjobs was different compared to the one you used in ssh => different permissions => somehow broke execution through cron.
The /home/USERNAME/CRAFTROOT/craft file was not marked as executable (fix -> chmod +x /home/USERNAME/CRAFTROOT/craft), though in that case one of your ssh commands should've also failed.
The different environment in Bash/Shell (for example, a different $PATH or the .bashrc not being read) in cron compared to ssh may also have been the cause behind this issue.

Other than that I'm not sure what could've caused the issue...
Q: But just to be sure, (please add a comment as answer), you ran your ssh commands without the # infront of them, right? Otherwise it'd make sense that your ssh commands wouldn't output any failures since your commands would simply be read as comments. [A in comments]
How to figure out what the cause was
Adjust your non-working crontabs by adding  >> /home/USERNAME/CRAFTROOT/cron.log to the back of the cronjob, this'll store the output of your command into the file located at /home/USERNAME/CRAFTROOT/cron.log, this would help in identifying what caused your issue in the first place.


How do I convert a date stored in UTC to the user's current locale in PHP?

Here is some code used in the past.

$timezone = new \DateTimeZone(craft()->getTimeZone());
$newdate = \DateTime::createFromFormat(DateTime::MYSQL_DATETIME, $mysqlDate, $timezone);

I have typically done this before assigning to a model.


How can I set a BOGOF (Buy One, Get One Free) promotion using Craft Commerce?

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

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

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

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


The "variant" method can receive multiple ids using the following: craft.variants().id([1,2,3]).

Following that, you can easily populate an array while looping through your (specific) products, and then use that array to fetch the variants.

Official documentation


Flush Compiled Templates Migration File breaks Craft Migration Setup from 3.0 to 3.3

rmdir(/app/b4mhz722nsjh4/storage/runtime/compiled_templates/f1): Input/output error

If Craft or PHP tries to delete a folder that doesn't exist, it won't fail, but will gracefully continue execution.

That error is an underlying operating system error that suggests there might be some probably with your disk:

https://askubuntu.com/questions/325343/unable-to-delete-file-rm-cannot-remove-input-output-error

https://askubuntu.com/questions/74105/how-do-you-repair-an-input-output-error-in-an-ntfs-partition

Craft attempts to clear the compiled templates folder at the end of an update, so the good news is, for all practical purposes, the update probably completed successfully.


Per the original poster's 2nd edit, the issue had to do with their company firewall blocking the request to S3.

  After more troubleshooting and testing this on a remote server it looks like the issue has to do with our company firewall not allowing receipt of the response object from the S3 request. We had no issues uploading assets to S3 from the same codebase on a remote server and are now looking into a work-around with the firewall.


Can't link to entries or assets via a Redactor field

Currently, there is no Craft 3.1 compatible version of the Redactor plugin. There's a branch in the Redactor plugin repository that has such a version, but that will be released only when Craft 3.1 is released as well.


Had the same issue running OS X running native Apache.  It could be PHP config issue.  Increase the max_nesting_level to something like 200.  Add/edit the following line in your php.ini:

xdebug.max_nesting_level = 200


First, I might over complicate things. Maybe there is a simple solution by using:
https://docs.craftcms.com/v3/relations.html#passing-multiple-relation-criteria

But my project has a complex query built in. So I did a custom db query class. And here is one of them might working for you.

If you know how to create a twig function in craft 3 in modules, the following code will make sense to you. If you don't know how to do it. Here is the link how to do it https://docs.craftcms.com/v3/extend/extending-twig.html#register-a-twig-extension.

    /**
     * This function is a twig extension

     * @param $IdArray      array           the array of element ids used for related to
     *                                      you can join as many ids you want to.
     *                                      e.g. [
     *                                              [
     *                                                "or", ["1234","23123"]
     *                                              ],
     *                                              [
     *                                                "and", ["234","1232","12332"]
     *                                              ],
     *                                              ...
     *                                            ]
     * @return object                       craft element query object
     */
    public function elementMultiRelatedTo($elementQuery, $IdArray=[]){
        if($IdArray!=[]&&isset($IdArray[0])&&is_array($IdArray[0])){
            foreach ($IdArray as $key=>$value){
                if(isset($value[0])&&is_string($value[0])&&isset($value[1])&&is_array($value[1])&&$value[1]!=[]){
                    if($value[0]=="or"){
                        $elementQuery = $elementQuery->orWhere($this->multipleRelatedToQuery($value[1]));
                    }elseif($value[0] == "and"){
                        $elementQuery = $elementQuery->andWhere($this->multipleRelatedToQuery($value[1]));
                    }
                }
            }
        }

        return $elementQuery;
    }

    /**
     *
     * @param $id
     * @return \yii\db\Expression
     */
    public function multipleRelatedToQuery($id){
        $sourceString = "";
        $targetString = "";
        if($id==[""]||$id==""||$id==[]){
            return new \yii\db\Expression("1");
        }
        if(is_array($id)){
            foreach($id as $key => $v){
                if($key == 0){
                    $sourceString = "`sources2`.`sourceId` = '$v'";
                    $targetString = "`targets2`.`targetId` = '$v'";
                }else{
                    $sourceString = $sourceString . " OR `sources2`.`sourceId` = '$v'";
                    $targetString = $targetString . " OR `targets2`.`targetId` = '$v'";
                }

            }
        }else{
            $sourceString = "`sources2`.`sourceId` = '$id'";
            $targetString = "`targets2`.`targetId` = '$id'";
        }

        return new \yii\db\Expression("
        (
            `elements`.`id` IN
            (
                SELECT
                    `sources2`.`targetId`
                FROM
                    `craft_relations` `sources2`
                WHERE
                    $sourceString
            )
            OR `elements`.`id` IN
            (
                SELECT
                    `targets2`.`sourceId`
                FROM
                    `craft_relations` `targets2`
                WHERE
                    $targetString
            )
        )");
    }

Then in twig

{% set typeQuery = craft.app.request.getParam('t') %}
{% set areaQuery = craft.app.request.getParam('a') %}

{% set cat1 = craft.categories.group('investigatorTypes').slug(typeQuery).one().id %}
{% set cat2 = craft.categories.group('areaOfResearch').slug(areaQuery).one().id %}

{% set researchers = craft.entries()
    .section('investigatorBios')
    .orderBy('bioLastName asc')
    .limit(12)
%}

{# here cat1 and cat2 is not array, make sure put them in [] #}
{# if you want more than one id, you can try ".ids()" instead of ".one().id" #}

{% set researchers = elementMultiRelatedTo(resaerchers,
[
  [
     "and",
     [cat1]
  ],
  [
     "and",
     [cat2]
  ]
]) %}
{% researchersArray = researchers.all() %}


How can I make Craft's caching aware of fields used inside a plugin?

The {% cache %} tag tracks Elements (Users, Entries, Categories, Tags, etc.), so if you're want to do this using a plugin, then the plugin's field type needs to inherit from BaseElementFieldType and needs to implement its own element type.

From there, it's just a matter of calling craft()->templateCache->includeElementInTemplateCaches().


Is it safe to remove the craft/storage/runtime/sessions folder?

Docs

runtime/ – Pretty much everything in here is there for caching and logging purposes. Nothing that Craft couldn’t live without, if the folder happened to get deleted.

It is safe to delete it


Grouping entries and ordering by section

The suggestion to order by sectionId resolved my issue here.

{% set entries = craft.entries().section('extendedEvents, festivalEvents'). orderBy({sectionId: SORT_ASC}) %}

As it was, I was working with two sections, so my quick fix was to simply reverse the order of the output of the for loop...

{% for section, eventsInSection in allEventsBySection | reverse %}


Craft 3 plugin backwards compatibility and maintenance for 2.x

This seems to be the common pattern emerging amongst developers in the same position...

A single repo:

craft-(pluginname)

Containing multiple branches:

v1 (your old Craft 2 version)
v2 (your new Craft 3 version)

There are a handful of variations to this, but that's it in a nutshell.

Examples:

https://github.com/craftcms/contact-form *
https://github.com/barrelstrength/craft-sprout-notes
https://github.com/doublesecretagency/craft-cpcss
https://github.com/marionnewlevant/craft-snitch
https://github.com/carlcs/craft-deleteentryversions

* P&T has mentioned that they didn't prefix their own repos with craft-, since the repo is owned by craftcms. But they said they would have, if they were a 3rd party developer.

Some people have prefixed their repos with craft3-, but that has been discouraged by the folks at P&T. It would force you to abandon this repo (or leave the name confusing) when Craft 4 is eventually released.


Rendering templates from plugin controller

Figured it out!

I went ahead and took at look at how Craft's renderTemplate method works which cleared it up for me. A few things...

Craft caches CSS/JS resources inside of getHeadHtml() and getFootHtml() which are automatically injected into every template rendered through renderTemplate. Injecting these into my template fixed the JS problem.
I forgot to set both the Content-Type and charset headers. Not necessary in my case but it's something that's good to add anyway.
To keep things consistent with the way Craft does things I outputted everything into a buffer and terminated the request.

Here's the working concept of my renderPluginTemplate method; I borrowed lots from Craft's renderTemplate function within the BaseController:

protected function renderPluginTemplate($template, $variables = array())
  {

    // Grab the new template's content
    // ---------------------------------------------------------------------

    $oldPath = craft()->path->getTemplatesPath();
    $newPath = craft()->path->getPluginsPath() . 'plugin/templates';

    // Set template path to the plugin directory (temporarily)
    craft()->path->setTemplatesPath($newPath);

    // Attempt to fetch the template content/extension.
    if(!$templatePath = craft()->templates->findTemplate($template)) {
      throw new Exception(Craft::t('Could not find template with path: ' . $template));
    } else {
      $templateContent   = file_get_contents($templatePath);
      $templateExtension = IOHelper::getExtension($templatePath, 'html');
    }

    // Reset the template path
    craft()->path->setTemplatesPath($oldPath);

    // Set up the headers and render the template
    // ---------------------------------------------------------------------

    if($output = craft()->templates->renderString($templateContent, $variables)) {
      // Set the MIME type for the request based on the matched template's file extension (unless the
      // Content-Type header was already set, perhaps by the template via the {% header %} tag)
      if (!HeaderHelper::isHeaderSet('Content-Type')) {
        HeaderHelper::setContentTypeByExtension($templateExtension);
      }

      // Set the charset header
      HeaderHelper::setHeader(array('charset' => 'utf-8'));

      // Are we serving HTML or XHTML?
      if(in_array(HeaderHelper::getMimeType(), array('text/html', 'application/xhtml+xml'))) {
        // Are there any header/footer nodes in the queue?
        $headHtml = craft()->templates->getHeadHtml();
        $footHtml = craft()->templates->getFootHtml();

        if($headHtml) {
          if(($endHeadPos = mb_stripos($output, '')) !== false) {
            $output = mb_substr($output, 0, $endHeadPos).$headHtml.mb_substr($output, $endHeadPos);
          } else {
            $output .= $headHtml;
          }
        }
        if($footHtml) {
          if(($endBodyPos = mb_stripos($output, '')) !== false) {
            $output = mb_substr($output, 0, $endBodyPos).$footHtml.mb_substr($output, $endBodyPos);
          } else {
            $output .= $footHtml;
          }
        }
      }

      // Output it into a buffer, in case TasksService wants to close the connection prematurely
      ob_start();
      echo $output;

      // End the request
      craft()->end();
    }
  }


Turn a tag field id into an instance of the tag model

You need to get the FieldModel using the $tagFieldId variable, and then pass that model into the function as follows:

$tagFieldRecord = FieldRecord::model()->findById($tagFieldId);

$tagFieldModel = FieldModel::populateModel($tagFieldRecord);

craft()->relations->saveRelations($tagFieldModel, $entryRecord['elementId'], $tags);


Multiple contact forms - Different recipients

But, is it possible to do this when there is another contact form on the site?

Yes?

  If so, what do I need to add to the department dropdown code?

The dropdown code shouldn't have anything to do with it, if I'm following you correctly.

Both forms just need to have a toEmail hidden input as specified in the docs: https://github.com/pixelandtonic/ContactForm#dynamically-adding-email-recipients-requires-craft-25


Difference between {% includeCssFile %} or including it with

The difference is that {% includeJsFile %} can be called in any template or template part and will add the script to the bottom of the page (as explained here).

So you could, for example, have a field that requires some JavaScript, say an image slider. You'll only need the JavaScript for that slider, not for the rest of the website, so in the template part where you set up the HTML for the slider, you also include the needed JS with {% includeJsFile %} and it will get added to the bottom of the page.

Where as with just including it in the head will load it on all pages.

It's the same for {% includeCss %}.


Craft stores all dates in UTC to the database, no matter what timezone you configure in settings.

When (user) models are populated, e.g. for use in the CP, DateTime objects are created from these values and the objects’ timezones are set to the one you configured.

If you want to output a user’s dateCreated attribute in a date format that includes the timezone you can easily do that in a template using the date Twig filter. This will return the date in ISO 8601 format using your configured default timezone.

{{ user.dateCreated|date('c') }}

You can also output the date formatted for a diffenent timezone.

{{ user.dateCreated|date('c', 'Europe/Paris') }}


What files need to be moved for Craft3 plugins?

So you're really close; composer.json is a list of packages and their semvers that should be installed. composer.lock has a manifest of what is actually installed.

In order to deploy to production, you just need a step that executes composer install to install what is in the composer.lock file. Check out the Deploying Craft CMS section of this article for a more thorough run-through.


How can I use two different templates for a single entry?

You do not need to create a separate section for course work.

What you want to have is two different urls, that both display the same entry, but in two different ways, with two different templates. So for example /course-slug will be the url for info page for the entry with slug course-slug, and /course-slug/lesson will be the url for the work page for the entry.

If you think of this as two different routes, you have set one of those routes up in the course section. You can set up a second one as a dynamic route. You want to route [slug]/lesson (uri) to _work (template) (or whatever your template is). Then in the _work.twig template, you will need to fetch the entry from the slug:

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

Check if no entry is set, and if not {% exit 404 %}. And then proceed as normal.


How to update Matrix field on frontend for user profile

Turns out it was a validation issue, it works fine when you make the new contact's first+last name mandatory.


You could try twig's reverse filter.

{% for asset in entry.assetsField|reverse %}
    <img src="{{ asset.url }}" alt="{{ asset.title }}"/>
{% endfor %}


The first thing I would try is to "Disable" every single one of your plugins. If that solves the problem, go through and re-"Enable" each one of them until you have pinpointed which plugin is causing the problem.

If that doesn't help at all, the next thing I'd do is to open up the Craft logs, and see if anything suspicious is in there...

/craft/storage/runtime/logs/

And if you're still not seeing anything noteworthy, then it might be worth submitting a ticket from the "Get Help" widget on your dashboard. Be sure to include your logs, database export, and files.


Install Failed - Numerous Errors when running with pgsql

This was due to the following problems on the system, some where 'gotchas' and some were just generic mistakes on my part. Putting this as an answer to help others.

Nanobox's boxfile.yml was classing anything that was in storage/logs/web.log as an [error] but obviously, it's not an error but rather a stack trace. This was fixed by changing it to the following within the boxfile:

    craft[stacktrace]: /app/storage/logs/web.log
    craft[error]: /app/storage/logs/phperrors.log
    php[error]: /data/var/log/php/php_error.log
    php[fpm]: /data/var/log/php-fpm.log

db.php driver was set to mysql when it was using postgres as a database. Changing it to the following 1/2 fixed the problem of installing.

'driver'      => 'pgsql'

.env was correctly set to DB_DRIVER="pgsql" but obviously this didn't match up to what my database was booting up as. This was the final piece of the puzzle.

Essentially the learning from this was make sure your db.php is nicely matched up to your .env file especially regarding your database structure. Make sure you properly set your logs to report as the correct things. As you can see above there are numerous log's craft reports into.


You can set usePathInfo = true in your Craft general settings to force Craft to use PATH_INFO to generate URLs.

https://craftcms.com/docs/config-settings#usePathInfo

Dukt OAuth should now show you a redirect URI without a query string (Facebook doesn’t like slashes in the query string and automatically encodes them) and you should be able to make the URIs match exactly.


How can an entry type have its own search and categories?

Sure it's possible. There's the type() parameter available for ElementCriteriaModels to filter the returned results by only those entry types you need.

To separate the categories, I'd set up multiple category groups, one for each of your entry types.

For example:

{% set categoryParams = {
    group: 'audioCategories',
    slug: 'podcasts'
} %}

{% set category = craft.categories(categoryParams).first() %}
{% set searchTerm = 'basketball OR soccer' %}

{% set entryParams = {
    section: 'news',
    type: 'audio',
    relatedTo: category,
    search: searchTerm
} %}

{% for entry in craft.entries(entryParams) %}
    <li>{{ entry.title }}</li>
{% endfor %}

This would list only those entries which are

in a section named "news"
given an entry type (and entry template) "audio"
related to a category from the category group "audioCategories" (I additionally filter for just the one cat. named "podcasts" to keep the example a little simpler)
matching to the search term "basketball OR soccer"


Is it possible to update a Twig variable based on user interaction without page refresh?

I'm the developer of Charge.

You need to go about the form setup a slightly different way.

By design there are 2 ways to define the value for a Charge, either

Setting the planAmount variable in your twig template, and passing it to the setCheckoutOptions/setPaymentOptions method.
Passing the allowDynamic : true setting to the setCheckoutOptions/setPaymentOptions method. With the allowDynamic option passed, you can then pass the planAmount as a hidden or visible input in your payment form, named planAmount. This can be directly updated by your users, and is explicitly designed for variable amount and charity donation form setups.

An example of this behaviour is included in the demo templates in template/charge/donation.html and is also visible here : http://demos.squarebit.co.uk/charge/donation


Using Twig to merge an array and remove duplicates

You could do it in the twig template

But a template isn't really the best place for something that is this codey, it gets pretty unreadable pretty quick.

Notes:

I start by building arrays of models and afterwards I convert those arrays to the comma seperated strings you require.
It is case-sensitive, you probably have models or makes likes HILUX, hilux, Hilux, these sort of duplicates will not be removed. You can achieve a modicum of case-insensitivity by chucking a few lowers in there.
Instead of hand-writing the json, this uses twig's json_encode filter, tweak the options to suit you.

The template code:

{% set makes = craft.entries.section(['usedCars','vansTrucks']).level(2) %}
{% set data = {} %}

{% for make in makes %}
    {% set make_name = make.make %}

    {# Get the list of models for this make if we have already seen it before, otherwise an empty array #}
    {% if data[make_name] is not defined %}
        {% set data = data|merge({(make_name):[]})%}
    {% endif %}
    {% set make_data = data[make_name] %}

    {# Build an array of model names #}
    {% set models = make.getChildren() %}
    {% for model in models %}
        {% set model_name = model.model %}

        {# Only add this model if we have not seen it before #}
        {% if model_name not in make_data %}
            {% set make_data = make_data|merge([model_name]) %}
        {% endif %}
    {% endfor %}

    {% set data = data|merge({(make_name):make_data}) %}
{% endfor %}

{# Replace the arrays of models we previously built with a comma seperated string of models #}
{% for make_name, models in data %}
    {% set models_string = models|join(",") %}
    {% set data = data|merge({(make_name):models_string}) %}
{% endfor %}

{{ data|json_encode(constant('JSON_PRETTY_PRINT'))|raw }}

Or do it with code

Add your own plugin as per the instructions here: Setting things up

Add a template variable as per the instructions here: Template variables

Your template variable could look like this:

namespace Craft;

class MyAwesomePluginVariable
{
    public function getSearchData()
    {
        // find all makes
        $make_criteria = craft()->elements->getCriteria(ElementType::Entry);
        $make_criteria->level = 2;
        $make_criteria->section = array('usedCars','vansTrucks');

        $data = array();

        $makes = $make_criteria->find();
        foreach ($makes as $make)
        {
            // build array of models for this make
            $model_names = array();
            $models = $make->getChildren();
            foreach ($models as $model) {
                $model_names[]= $model->model;
            }

            // combine with previous models for this make and remove duplicates
            if (array_key_exists($make->make, $data)) {
                $data[$make->make] = array_unique( array_merge($data[$make->make], $model_names) );
            } else {
                $data[$make->make] = array_unique( $model_names );
            }
        }

        // convert model arrays to comma separated strings
        foreach ($data as $make => $models) {
            $data[$make] = implode(",", $models);
        }

        return $data;
    }

}

And then in your template:

{{ craft.myAwesomePlugin.getSearchData|json_encode(constant('JSON_PRETTY_PRINT'))|raw }}


404 isn’t a valid redirect status code, which is why {% redirect '/' 404 %} isn’t working correctly.

It sounds like you want to:

first issue a 301 or 302 redirect to the homepage
then serve the homepage, but with a 404 status

If I’m understanding you correctly, even if you could pull it off (maybe doable with a query string or something stored on the session), that would be bad for a couple reasons:

the actual not-found resource is returning the wrong status code (301/302 instead of 404)
now the homepage (a valid resource) is also returning the wrong status code (404 instead of 200)

The biggest issue is the 2nd one, as it would have devastating SEO consequences. Any time Google hits a missing resource and is redirected back to the homepage, it will start remembering that your homepage doesn’t exist.

A better solution would be to keep the URL pointed at the missing resource (so no redirect response), but to just render your homepage via the 404 template.

That should be as simple as creating a 404.html template with this:

{% include "index" %}

(or whatever your actual homepage template is called.)

If you have a Homepage Single section, you may need to pass that in as well:

{% include "index" with {
    entry: craft.entries.section('homepage').one()
} %}

(replace .one() with .first() if this is for Craft 2.)


I know it sounds silly, but try refreshing or clicking another tab (like entries). What you should see is in the attached screenshot.

Click that box and click "Try" and you should be all set.


Found out how to do this. If anyone else needs something similar then adding the new {{ jsClass }} part of input template into a function and calling it after extra elements are added also makes them sortable.


You need to define a route via action param otherwise Craft has no idea where to route the request. Your data variable only only contains

data = [
    'test' => 'test',
    TOKEN_NAME => TOKEN_VALUE
]

and nothing else

You need to include action => plugin-handle/controller-id/function-name

You can see a working example at basic ajax how to


Craft Commerce: Custom address model validations

You can do either or both of:

Use javascript validation on the front end (with e.g. parsley)
Create a plugin with a method that is registered as a listener on this event:
https://craftcommerce.com/docs/events-reference#commerce_addresses.onbeforesaveaddress

...and perform validation on the address model before it is saved.  use $event->performAction = false; to stop the address actually being saved if need be.

General notes on how to listen to events and get at parameters is here.

Most likely, the best way will be to use a bit of both front end and back end validation here to make sure you're getting the data you want in the format you want in to your address fields.

(If you don't yet have a business logic plugin for your Commerce setup, then create one using either of two excellent options:
Business Logic Plugin or via http://pluginfactory.io)


For your example, you should exclude the fictional towns in the initial query itself rather than filtering them out afterwards:

{% set towns = craft.entries.section('towns').townFictional('not 1').relatedTo(counties) %}

So that no towns with the Fictional Lightswitch on will be returned from the database at all.

You would use the |without filter, if you already had an array of items and you wanted to filter some of them out while looping over them. This works best with arrays of simple primitives, such as an array of entry IDs, rather than whole objects.

You would be more likely to use an {% if %} tag if you still wanted to fetch the entry, but wanted to adjust some part of your output based on a condition. So for example if you had an array of entries from different sections, and they might not all have URLs, you might decide to output a link or just the title:

{% for entry in entries %}
    {% if entry.getUrl() %}

{{ entry.title }}

    {% else %}

{{ entry.title }}

    {% endif %}
{% endfor %}


If you're specifically looking to trim the entryversions table, there is a plugin that can automate the process for you.

Beyond that, you can safely truncate these tables as well when moving between environments:

assetindexdata
assettransformindex
sessions
templatecaches
templatecachecriteria
templatecacheelements


Email Field Being Dropped on sendMessage (Craft Contact Form Plugin)

If you go with the message[] array syntax, then all other fields must use the same syntax.

https://github.com/craftcms/contact-form#adding-additional-fields

So this should solve it:

<input id="fromEmail" type="email" name="message[fromEmail]" value="{% if message is defined %}{{ message.messageFields['fromEmail'] }}{% endif %}" required>


It may depend on where in the request you need to access the element.

If you need to access the element in a Field class on your Entry Edit page, the getInputHtml method may be useful:

public function getInputHtml($value, ElementInterface $element = null): string
{
    // $element stuff

    return Craft::$app->getView()->renderTemplate('my-plugin/field',
    [
        'field' => $this,

        // make the Element available in your field's input template
        'element' => $element
    ]);
}

If you need to access the element in a Field class after the Element is saved there is the afterSaveElement method:

public function afterElementSave(ElementInterface $element, bool $isNew)
{
    parent::afterElementSave($element, $isNew);

    // $element stuff
}

Note: The Entry ID may not exist while a new entry is being created. Accessing the Element via the afterElementSave ensures that the Entry has been saved and has an ID.


Problem solved.

Admin was a bit confusing on the UX side, I didn't realize that edit entry types and clicking the name of the section leads to different pages.


Count number of consecutive years in category array

If you don't want to build a custom plugin, this would be a good time to use Twig's reduce filter, which takes a set of values and iterates over them to output a single value based on supplied logic.
Using Andy's answer as the basis might look like this (untested):
{% set currentYear = now|date('Y') %}

{% set loyaltyScore = currentUser.years.all()|reduce(
    (score, year) =>
        score == 0 and year == currentYear or
        score > 0 and year == currentYear - 1 ?
        score + 1,
        0
) %}

I'm assuming your data is already in the right order if it's a category group.


Macro _self no longer possible in Craft 3

You have to import the macro

{# include the richtext macro on multiple occasion into another macro #}
{% macro memberMolecule( entryParam ) %}
    {% import _self as macro %}
    {{ macro.richText(entryParam.body) }}
{% endmacro %}


Downloading file from custom element action

It's because those element actions happen over an AJAX and you have to jump through a few hoops to send a file to the browser over an AJAX request.


There is no "this over that" for Craft specifically. Your own statement "it depends" plays a big role. I would read this pretty extensive post on DigitalOcean "Apache vs Nginx: Practical Considerations" as a good start on what software to use.

https://www.digitalocean.com/community/tutorials/apache-vs-nginx-practical-considerations

For me personally this has always been the key difference in choosing between the two:

  In terms of real world use-cases, one of the most common comparisons between Apache and Nginx is the way in which each server handles requests for static and dynamic content.

Usually you don't need all the extra features Apache comes bundled with. You have your Craft website, that's it. Nginx (for me) has all the functions it requires and can handle a lot of requests on lower hardware. For me personally that's key.


Have you seen this on the Craft site: http://buildwithcraft.com/help/why-index.php ?


Craft multisite site without www redirects unwanted to default site

The problem is that the force ww redirect isn't setup correctly. This must be setup like this:

    # Force www
    # -----------------------
    RewriteCond %{HTTP_HOST} ^domain-a.com [NC]
    RewriteRule ^(.*)$ https://www.domain-a.com/$1 [L,R=301,NC]

    RewriteCond %{HTTP_HOST} ^domain-b.com [NC]
    RewriteRule ^(.*)$ https://www.domain-b.com/$1 [L,R=301,NC]

Also both domains must point to the same server.


Transform different image if specific id or order

You could do that like so:

{% for asset in entry.portraits %}
    {% if loop.index == 2 or loop.index == 4 %}
        {% set transform = 'largeIndexHalf' %}
    {% else %}
        {% set transform = 'largeIndex' %}
    {% endif %}

{% endfor %}

or more simply (making use of the containment operator (in) and the ternary operator (? :):

{% for asset in entry.portraits %}
    {% set transform = loop.index in [2, 4] ? 'largeIndexHalf' : 'largeIndex' %}

{% endfor %}


Moved from local to remote "Service Unavailable Our site is temporarily unavailable. Please try again later."

That error means your site is currently offline.  Check in the control panel under Settings->General to see if it's set to offline there.  Or maybe you're using the isSystemOn config setting and it's set to off on your production environment there.


Set a limit of nulll on your entry query which means no limit. If you do not specify a limit, you will only see 100 entries.

.limit(null)


Front End entry form, mapping a hidden field to a plugin?

It's probably too late to help the OP (sorry @shorn), but here's how you would pull this off with Smart Map...

Add the Address field on the front-end.

Street Address
<input type="text" name="fields[myAddressField][street1]" value="">

Apartment or Suite
<input type="text" name="fields[myAddressField][street2]" value="">

City
<input type="text" name="fields[myAddressField][city]" value="">

State
<input type="text" name="fields[myAddressField][state]" value="">

Zip Code
<input type="text" name="fields[myAddressField][zip]" value="">

Country
<input type="text" name="fields[myAddressField][country]" value="">

Latitude
<input type="text" name="fields[myAddressField][lat]" value="">

Longitude
<input type="text" name="fields[myAddressField][lng]" value="">

Then, you can inject the lat and lng values using JavaScript. Since you're doing that dynamically, you could even make those inputs hidden.

That's it!


JS - Determine that an entry is unsaved?

Craft checks if a form has changed using javascript in the cp.js file, see line 196.

You could replicate this quite easily yourself though, here is a stripped down example:

var initialFormValues = $('form[data-confirm-unload]').serialize();

function checkFormChanged() {
    if ($('form[data-confirm-unload]').serialize() != initialFormValues) {
        return true;
    }

    return false;
}


I know this is of course not an elegant solution, but it works for now. I used twig filters to extract url and alt tag of the image.

            {% if entry.featuredImage|length %}
                {{ snippets.image( entry.featuredImage.first(),entry.url )}}
            {% else %} {# For the tumblr imports, extract first image #}
                {# Extract the src #}
                {% set spliter = entry.articleBody.type('text').first().text|split('src="') %}
                {% set spliter = spliter[1]|split('"') %}
                {% set firstTumblrImage = spliter[0] %}

                {# Extract the alt-Tag of the Featured Image#}
                {% set spliter = entry.articleBody.type('text').first().text|split('alt="') %}
                {% set spliter = spliter[1]|split('"') %}
                {% set imageAltTag = spliter[0] %}

            {% endif %}


The Team section’s landing page could just be a template living at about/team/index.html. From there if you want to output info about the team members, just fetch it manually:

{% for teamMember in craft.entries.section('team') %}

{{ teamMember.title }}

    ...
{% endfor %}

If you want to give each Team entry its own page on the site, go into the Team section’s settings and check the “Entries in this section have their own URLs” checkbox. Set the URL Format to about/team/{slug}, and the Template to about/team/_entry. Then create an about/team/_entry.html template with whatever info you want. (The current Team entry will be available to the template as an entry variable.)

From the index, within that for-loop, you can link to the team member’s pages like this:

{{ teamMember.title }}


Fixed it, it was just a matter of proper capitalization

var url = Craft.getActionUrl('baseinstagram/main/testy');

Should have been

var url = Craft.getActionUrl('Baseinstagram/Main/Testy');


Craft rich text field excerpt with slice and raw breaks when cut inside double tags

Raw doesn't close tags tags for you, it just allows them to be processed rather than printed only.

The only reason you are seeing closing tags I'm guessing is because your inspector (chrome/safari) is adding them in. Do "view source" to see the real output.

Your problem here is just rogue open tags


So after looking through other craft websites to see how they where able to get images to work I discovered what I did wrong when setting up the asset source. The thing is though that the craft documentation is not very helpful in this regard, also there are no errors or helpful output to indicate that my configuration might be the issue.


Set status 'pending' on Guest Entry form

The "status" on an entry isn't explicitly set like that. It is determined based on other properties of the entry. You will need to set these other criteria for the entry to appear as "pending".

An entry is 'pending' if it is enabled and has Post and Expiration Dates in the future.

More information on status' and the criteria can be found on the docs.

UPDATE: Depending on your use case, the following answer may be helpful: Editorial workflow / custom status


How to programmatically add fields to matrix blocks?

I'm assuming that you're attempting to create the Matrix, with one or more blocks each containing one or more fields, from scratch? I.e. you're not simply trying to populate content into an already defined Matrix?

Going off of that assumption:

PS: Sorry, this code is not based on your code - I simply took working code from one of my other projects.

// Create a field-group to store our Matrix in
$fieldGroup       = new FieldGroupModel();
$fieldGroup->name = 'My Field Group';
craft()->fields->saveGroup($fieldGroup);

// Create the matrix-field itself
$field          = new FieldModel();
$field->groupId = $fieldGroup->getAttribute('id');
$field->name    = 'My Matrix';
$field->handle  = 'myMatrix';
$field->type    = 'Matrix';

// Create a rich-text field which we will need later
$textField = ['name' => 'Text Field',
              'handle'       => 'label',
              'required'     => TRUE,
              'instructions' => '',
              'type'         => 'RichText',
              'typesettings' => ['configFile'            => 'Standard.json',
                                 'cleanupHtml'           => 1,
                                 'purifyHtml'            => 1,
                                 'columnType'            => 'text',
                                 'availableAssetSources' => '',
                                 'availableTransforms'   => '']];

// Define block-types for our Matrix-field
$blockTypes = ['new1' => ['name'   => 'My text field',
                          'handle' => 'myTextField',
                          'fields' => ['new1' => $textField]];

// Assign block-types to the Matrix-field
$field->settings = ['blockTypes' => $blockTypes];

// Finish the creation of the Matrix-field by saving it
craft()->fields->saveField($field)

I've had to adjust the code slightly to not give you the full 125 lines of code I use (mostly due to having a LOT of different block-types with a lot of different fields) - but hopefully you can get the gist of how it's working and solve your issue based on this :)

PPS: It seems like your code should work fine if you only tweak the order in which you do things slightly.

Create Field Group
Create Matrix Field
Define Block Fields
Create Block Types using defined Block Fields
Assign Block Types to Matrix Field
Save Matrix Field


I am trying to have a vertical tab with hidden content

You might be better using entry.slug instead of entry.title as there could be spaces in the title.
Or add a filter to the title - e.g. entry.title|kebab  which would remove any spaces.
Also this line:
{% if siteMainUrl.text  == href %} active {% else %} hide {% endif %}

Where is href coming from? Is it to have a different active tab to the initial one?
Typically I use:
{% for item in tabdata %}
   <div class="tab-pane fade {{ loop.index == 1 ? 'show active' }}" id="{{ item.title|kebab }}" role="tabpanel" aria-labelledby="v-pills-{{ item.title|kebab }}-tab">

  </div>
{% endfor %}

Which leaves the first tab active.


When extending Craft's active record classes (like UserRecord), you'll only be able to setup the AR relationships one way (from your class to Craft's i.e. MyPortal_ProxyUserRecord => UserRecord) as you've done.  Setting up the relationship in the other direction (UserRecord => MyPortal_ProxyUserRecord) would require hacking core Craft files (not recommended).

For queries going in that direction, you'd need to go outside the scope of AR and use QueryBuilder to manually build the SQL query you need.

As for your current structure, I'm not sure what the purpose of MyPortal_ProxyUserRecord is.  Why not just have MyPortal_UserProfileRecord and MyPortal_OrganizationProfileRecord link to UserRecord directly? Maybe you just want a single place for that interaction instead of two, I suppose.


Migrations running on their own causing site down

Plugin Migrations run as soon as Yii finds a new file. So as soon as there is a new file in your migration folder Craft starts to migrate it the next time someone triggers Craft tasks (usually by entering the CP). When you hit the Update button in your CP there could be that chance that a plugin makes an update -> pulls those migration files.

What do you mean with "how to debug this"?

If you want to remove the task, you can clear the task table and get rid of it. You can take a look at the plugins migration folder and see if there are any new files.


How do you get all the categories in a category group in a controller?

I found the answer. This might help others:

$categories = craft()->elements->getCriteria(ElementType::Category)->group('handle');


Because the descendantOf function expects a parameter of type ElementInterface or an integer

 /**
 * Sets the [[descendantOf]] property.
 *
 * @param int|ElementInterface|null $value The property value
 *
 * @return static self reference
 */
public function descendantOf($value);

You'll have to insert the ID of your accessories element or the element itself

{% accessories = craft.categories.title('accessories').first() %}
{% set categoryGroup = craft.categories.group('productTypes').descendantOf(accessories) %}


Eager load Imager transforms in Craft Commerce

Eager loading is used to reduce the number of database queries. Imager doesn't store any data in the database, it's completely file-based, so it's not relevant to use eager-loading in relation to it.


Extending options for different plugin types?

IMHO, there is no need for this and I don't think that there is a problem that needs to be solved by Craft based on what you've outlined. Additionally, putting this on the roadmap may lead to a confusing API or at the very least, it might get us into a convoluted plugin philosophy like in EE, where we have plugins, modules, addons, etc.

Currently, plugins can decide whether they have settings or not and the API that enables that seems very eloquent to me.

Having said that, properly categorizing plugins will be very important and I would bet that the plugin store will address that; )

Please let me know if I misunderstood what you were trying to communicate.


You were close!  In Craft 3, postDate is a full PHP DateTime object, so it'd need to be something like:

{% set allEntriesByYear = allEntries|group('postDate|date("Y")') %}


Why is a "Brute Force Protection" dialog appearing on login? How to remove?

That message definitely isn't coming from Craft natively.

My guess is either:

You've got a Craft plugin installed that's listening to onBeforeLogin and/or onLogin events and is performing some custom business logic
Your host has some library/extension/etc. installed globally that is affecting your Craft install.


Fatal error: Allowed memory size of 134mb exhausted

If your plugin is pulling in a lot of data, then most likely you're just bumping into your php.ini file's memory_limit setting.  Try bumping that up to 256M and see if that helps.


It would be fairly easy to create a hidden field and read the page URL into it.
As for the title, is your form an embed inside the channel entries call? Then it's pretty easy to get the title as well. If not you may have to be a bit creative.

Check the hidden field config, it even tells you how to pull in the current url.


Why do I get Access denied on s3 asset source images?

I'm not really an AWS expert, but I think if you want to allow public access to an S3 bucket, then it should look something like this:

{
    "Sid": "PublicReadGetObject",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::bucketname/*"
}


How to get one random entry from among multiple sections

According to the docs, you can pass an array into the "section" parameter:

{% set entries = craft.entries.section(['quotes','didYouKnow']) %}

There are several ways to get a random entry, but the method you've chosen is a very good one, because it randomizes during the initial SQL call:

.order('RAND()')

Lastly, you can cut out some of the extra legwork from your original example by calling first instead of find:

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

When you retrieve your entry using first, you'll get a single entry instead of an array of entries. You can skip the for loop entirely, since you're already dealing with a single entry.

So to put that all together...

{% set randomEntry = craft.entries.section(['quotes','didYouKnow']).order('RAND()').first() %}

<h1>{{ randomEntry.title }}</h1>


I finally got this working, here is how, I'm sure there is a better way or ways I can improve, but for now it works.

First, we need to create an empty array like so...

{% set posts = [] %}

Then we need to loop through the entries, and for each entry create an array containing the entry.postDate and the entry data. We then need to push that entry array into the main posts array

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

    {#-- We store the date reference for our custom sort function, and the entry data --#}
    {% set entry = [{
        'date': entry.postDate,
        'data': entry
    }] %}

    {#-- Now we push that entry array into the posts array --#}
    {% set posts = posts|merge(entry) %}

{% endfor %}

So now we've got an array containing just our craft entries. Next we need to install the Dukt Twitter plugin and the Dukt oAuth Plugin. Set them both up via the documentation and then we move on to the next step.

We now need to pull in our tweets, which with these two plugins is surprisingly easy. It looks pretty much the same as the above chunk of code, except we're executing a GET request to Twitter instead of pulling in the entries from Craft. Also, because the tweets created_at variable is formatted in a non-standard way, we need to reformat it into the same format as the craft entry date.

{% for tweetRef in craft.twitter.get('statuses/user_timeline', {
    count: 10,
}) %}

    {% set tweet = [{
        'date': tweetRef.created_at|date('Y-m-d'),
        'data': tweetRef
    }] %}
    {% set posts = posts|merge(tweet) %}

{% endfor %}

So now, our posts array contains both our entries and tweets, but not in date order. With a bit of research I discovered this post which explains Twig can't sort multi-dimensional arrays. Luckily @victor_in in that post provided a solution. He has developed a plugin with improves twigs sort functionality. So download his plugin, then as he mentioned, modify the plugin to add the custom sort function.

This is the custom sort function that I used.

private function sortByDate($a, $b)
{
    return strtotime($a['date']) < strtotime($b['date']);
}

I should also note that Craft was giving me an error with array('TwigBetterSortTwigExtension', 'sortByDate') saying the class 'TwigBetterSortTwigExtension' not being found. I fixed this by replacing it with array(__CLASS__, 'sortByDate').

So now, we can loop through our posts array using the custom sort filter to order them by the date.

{% for entry in posts|sort('dateSort') %}
    {{ entry.date }}
{% endfor %}


Help with correct syntax regarding multisort please

Try removing the quotes on 2nd/3rd arguments:

{% set rides = furniture|multisort(furniture['price'],[SORT_DESC],[SORT_REGULAR]) %}


Craft 3 protecting a page with a password?

I don't think this is possible with Craft out the box, and would require a module / plugin to add this functionality.

Firstly to write the password to the database and encrypt it, so it's not stored in plain text.

Then it would just be a case of having a controller (https://docs.craftcms.com/v3/extend/coding-guidelines.html#controllers) that checks this password from a form submission and unlocks the ability to edit the entry again.


Add a user to a userfield when there's a pending user on there

To mark this as answered, my solution was to use this code to get the current employee IDs regardless of status.

$employees   = $currentUser->employees->status('*')->ids();


How do you delete assets from a frontend form?

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

Craft 2:

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

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

Craft 3:

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

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

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

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

Craft 2:

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

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

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

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

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

Craft 3:

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

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

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

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

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

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

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