I found the clue for how to do this in the getPrevSibling documentation:

  getPrevSibling() will return the previous sibling whether or not it’s enabled. If you want to get the closest enabled sibling, you can do this instead:

  {% set prevSibling = craft.entries.positionedBefore(entry).order('lft desc').first() %}

So this query will get the number of enabled previous siblings:

{% set prevSibsCount = craft.entries.positionedBefore(entry).total() %}

And then your position will be that + 1:

{% set position = prevSibsCount + 1 %}
{{ position }}


Add Tags to a Newly Created Entry With PHP in Craft 3

Tags are normal Elements like Entries, Categories, Users and so on.. In order to relate a Tag to another element, you need to insert the array of those elements like you are used to with every other field

$existingTag = Tag::find()->title('whatever')->one();

$tag = new Tag();
$tag->groupId = 1; //<-- insert your group id here
$tag->title = "something";
Craft::$app->getElements()->saveElement($tag);

$entry->setFieldValues([
   'tags' => [$tag->id, $existingTag->id]
]);


Search for entries where a field value is between multiple ranges of numbers

I'm not sure if you can do this in the same query but you can use two queries, one for each range, and then merge them, something like this:

{% set range1 = craft.entries({myFieldHandle: ['and', '> 0', '< 10']}) %}
{% set range2 = craft.entries({myFieldHandle: ['and', '> 20', '< 30']}) %}
{% set entries = range1 |merge(range2) %}


Multiple Tag Groups/Sets for separate channels

Sounds like two different relation fields - linked with two different categories - would fit much better for your needs. When you create Tags/Taggroups everybody can insert everything so you can't forbid equal tags unless you hook into an event with php to prevent the saving process. When you create two different tag fields their autocomplete sources will be different but you can insert whatever you want.

So what you want to do is: create 2 different categories: The first News the second Reviews and add your keywords there. Then you create two relation fields, one linked with News, the other one linked with Reviews. Then you add those to your field layout


Exclude category from channel entries (with pagination)

The without filter converts your ElementCriteriaModel into an array of entry models (the same as find() does). But that array won't work with paginate, as explained in the docs:

  This parameter needs to be an actual ElementCriteriaModel object; not an array of elements. So don’t call find() on the object.

So you need to query for the entries you're after directly with an ElementCriteriaModel. The workaround to get there would be to pass an array of entry IDs into the id() parameter.

{% set categoriesToExclude = craft.categories({slug: 'berlin'}) %}
{% set entriesToExcludeIds = craft.entries.section('news').relatedTo(categoriesToExclude).limit(null).ids() %}
{% set entryIds = craft.entries.section('news').limit(null).ids()|without(entriesToExcludeIds) %}

{% set entries = craft.entries.id(entryIds) %}

{% paginate entries as entriesOnPage %}
    ...
{% endpaginate %}

It's not recommended to use the without filter with objects anyways (→ see Brad's answer to a related question)!


Form tabs aren't saving in the Sprout Forms plugin

OK, heard from Barrel Strength / Sprout support. His reply:

  Please create any fields that you need before adding the tab, then
  please go ahead and add the tab(s) then drag and drop the fields that
  you need into the new tab (rename the new tab in this step if you
  want) and finally save the form. The new tabs need to have at least
  one field to be saved with the form.

Since you can't create an empty tab, the trick is to create all your fields first -- or at least, one field per tab. Then, after you create the tabs, you can drag a single field to each tab and save the form. That will keep the tabs. After that, when you create a new field, you can select the tab it belongs to in the dropdown.

I think that's how it also works in the native Craft field manager? So hopefully this helps somebody even if you're not using Sprout Forms (which is awesome, by the way.)


Imager plugin using it's transformImage method doesn't produce correctly resized images

Turns out google chrome dev tools was lying to me when i hovered over the images in a srcset and told me their natural size was different to their real size. All working grand!


What is the correct way to attribute FreeForm submissions to users?

Actually saw that earlier in the documents: https://docs.solspace.com/craft/freeform/v3/overview/relations.html#overview


Have a look at the Searching article in the Docs. If you want to list all entries containing a word that begins with "Second Pres", you have to use this search term "Second Pres*".


Install Zoho CRM SDK (or any SDK for that matter)

Assuming you've got Composer installed from the command line, you can navigate to your project's root (where your project's composer.json file lives), then run this from the command line:

composer require zohocrm/php-sdk

From there, Composer and Craft pretty much take care of everything, including setting up the autoloading and you can reference ZCRMRestClient::initialize(); from your module/service.


assets.beforeSaveEntry to upload file to secondary location in addition to Amazon S3

So this works. Now that I have the understanding of how things fire I can clean this up. I am open to better solutions.

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

        $entry = $event->params['entry'];
        if($event->params['isNewEntry'] && $entry->getSection() == 'Episode')

        {

        require_once(craft()->path->getPluginsPath().'cranberry/vendor/soundcloud/autoload.php');

        $clientID       =   'xxxxxxxxxxxxxxx';
        $clientSecret   =   'xxxxxxx';
        $username       =   'xxxx';
        $password       =   'xxxx';

        $facade = new \Njasm\Soundcloud\SoundcloudFacade($clientID, $clientSecret);
        $facade->userCredentials($username, $password);

        $mp3File = $entry->mp3File->first()->filename;
        $url = $entry->mp3File->first()->getUrl();

        $mp3TempFile = "/var/www/pathway/craft/storage/tmp/".$mp3File;

        // download the file from amazon s3 and push it to soundcloud
        $fp = file_put_contents($mp3TempFile, fopen($url, 'r'));

        $params = array(
            'track[title]' => iconv('UTF-8', 'ASCII//TRANSLIT', $entry->title),
            'track[sharing]' => 'public',
            //'track[asset_data]' => $mp3Filepath,
            'track[description]' => iconv('UTF-8', 'ASCII//TRANSLIT', strip_tags($entry->summary)),
            'track[track_type]' => 'podcast',
            'track[genre]' => 'Learning',
            'track[license]' => 'all-rights-reserved'
            );

        $response = $facade->upload($mp3TempFile, $params);
        $fp = unlink($mp3TempFile);

        }

    });


When deleting a user do the entries they created also get deleted?

Currently, yes, that's how it works.

It's on our list to 1) Add a prompt warning what's about to happen and 2) Add options as to what you want to do with that author's current content (assign to a different user, delete, etc.).

Update:

As of Craft 2.3, when a user's account is deleted you'll be prompted what you want to do with their content.


Not showing the entry somewhere else on the same page

You can exclude the current entry from your query

{% recents = craft.entries.id('not ' ~ entry.id).find() %}


Craft 3: 500 Internal Server Error when trying to install on server

The 503 error on the home page usually indicates that Craft can't connect to the database. That's to be expected when performing a fresh installation.

The internal server error could be a typo or other misconfiguration in the /web/.htaccess file. You can troubleshoot this by renaming the .htaccess file so Apache doesn't use it, then in /config/general.php set

'omitScriptNameInUrls' => false,

index.php?p=admin should work then and you can sort out the .htaccess url rewrites later.


Eager loading category data from a Matrix block

Apologies, I misread the question the first time around.

When eager loading via a matrix block, you need to provide a blocktype as well as the field name.

See: https://docs.craftcms.com/v3/dev/eager-loading-elements.html#eager-loading-elements-related-to-matrix-blocks for more details


Craft Commerce - How To differentiate between Shipping and Billing Addresses?

Not simple at the moment. This is because a single address could be used for both shipping and billing on an order during checkout.

You could subvert an existing address field you don't utilize (like attention or title) to store their preference, but that's pretty hacky.

If the user is logged in you have access to their last used shipping and billing addresses with:

{{ craft.commerce.customer.lastUsedBillingAddress ?? '' }}
{{ craft.commerce.customer.lastUsedshippingAddress ?? '' }}

Do you think an address.usedAs field would be useful? It would likely store something like: billing, shipping, or both. Thoughts?


In my case I have a set number of chosen entries to exclude so I'll show how to do that first.

{# Build array of chosen council leaders to exclude from council member list since they're already being displayed by using the "merge" Twig filter #}
{% set idsToExclude = ['and'] %}

Beginning the array with 'and' is the trick here. Without it this won't work.

{% set idsToExclude = idsToExclude|merge([entry.metropolisCouncilPresident.first.id]) %}
{% set idsToExclude = idsToExclude|merge([entry.metropolisCouncilVicePresident.first.id]) %}
{% set idsToExclude = idsToExclude|merge([entry.metropolisCouncilSecretary.first.id]) %}
{% set idsToExclude = idsToExclude|merge([entry.metropolisCouncilTreasurer.first.id]) %}

{# Convert the idsToExclude array into a comma separated list #}
{% set idsToExcludeString = idsToExclude|join(', not ') %}

{# Echo out the council members without displaying the featured ones #}
{% for councilMember in peopleEntries.id(idsToExcludeString) %}
  {{ councilMember.title }}
{% endfor %}

Let's say you want to exclude entries that you've choosen in a entry field. So there's multiple related entries and you've already looped over them to display them on the page but you want to ensure that they aren't displayed later on the page when you list all the entries out.

It's fairly similar to the above code but just for the sake of clarity.

{# Base array of chosen council members to exclude from council member list since they're already being displayed #}
{% set idsToExclude = ['and'] %}

{% for item in entry.featuredCouncilMembers %}
  {{ entry.title }}

  {# Build array of chosen council leaders to exclude from council member list since they're already being displayed by using the "merge" Twig filter #}
  {% set idsToExclude = idsToExclude|merge([item.id]) %}
{% endfor %}

{# Convert the idsToExclude array into a comma separated list #}
{% set idsToExcludeString = idsToExclude|join(', not ') %}

{# Echo out the council members without displaying the featured ones #}
{% for councilMember in peopleEntries.id(idsToExcludeString) %}
  {{ councilMember.title }}
{% endfor %}


As already mentioned in the comments, getSiblings should do exactly what you need.

{% for sibling in entry.getSiblings() %}
    {{ sibling.title }}
{% endfor %}


I am just starting out on a project using both Craft and Angular. I ran into an early issue because both Angular and Twig use the {{ }} braces, but that was quickly fixed by changing Angular’s settings like in this Stackover flow post.

As Christian noted in the comments, though, you could also use the {% verbatim %} tag to keep Craft from parsing Angular tags.

Aside from that, I haven't had issues since Craft gives you full frontend control and there are no CMS scripts on the frontend to worry about. And (so far) Craft's dynamic routes make it really quite easy to set up your templates, JSON, etc.


Personally, I would just go with S3 (I've ranted about this before), but I understand that sometimes your hand is forced.

I was not even aware that Rackspace supported SSL or that it would be a different domain. I did some digging through their docs and it seems there'll be some work involved to get this feature in and it will cause a change in behaviour, so I'd say this will have to wait until 2.2.


cartCookieDuration and purgeInactiveCartsDuration config settings

If you go to the Commerce documentation for each (https://craftcommerce.com/docs/configuration#cartcookieduration and https://craftcommerce.com/docs/configuration#purgeinactivecartsduration), they both link to the PHP DateInterval class page that describes the various time formats you can use.

For 24 hours, either P24H or P1D should do it.


Can you beta build a Craft site in the www directory while the current site is still running?

In general, developing a website on the live server is a risky thing. Should something go wrong while you're developing then you risk the live website. It's unlikely, but an accidental upload to the wrong folder, or a wonky SQL statement, or anything like that can happen.

If you must run the site on the live server though, your best bet is to set up a sub-domain such as beta.mywebsite.com. Host that on your live server and do all the development there. Then, when it's time to retire the old site and make the new one 'live', you can move the files across from the beta directory to the www one.

I wouldn't recommend doing that though. Much less risky to get your local machine set up as a development server, do it all on that, and then put the site live.


Adding `www` causing it to have index.php

Your .htaccess is basically saying "Redirect example.com to www.example.com".

OK, that's cool. so you want to add www to any request that a comes in. I'm guessing you put this after Craft's code?

This actually needs to come before you send the request to Craft.

So, your example should look like this:

<IfModule mod_rewrite.c>

  #Make sure the rewrite engine is on
  RewriteEngine On

  # Redirect non-www requests to www
  RewriteCond %{HTTP_HOST} ^example\.com [NC]
  RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301,NC]

  # Send would-be 404 requests to Craft
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
  RewriteRule (.+) index.php?p=$1 [QSA,L]

</IfModule>


Upgrading Craft 2 to 3 hits database error

This sounds like a bug, you should probably report it directly to support@craftcms.com.

I'm posting this as an answer however, because you may be able to skirt the issue... Try updating to Craft 3.0.x first, and then updating the 3.1.x from there.


Craft redirects to domain.tld/domain.tld/ on logout

So after a bit of trial an error, setting a protocol for the siteUrl in config/general.php fixed it!

Ex. I had 'siteUrl' => 'domain.tld' so I changed it to 'siteUrl' => '//domain.tld' and all is well.


Display entries tagged with specific tag(s) before other entries

You're on the right track with merging arrays of entry IDs. And to keep the sort order with your new craft.entries call there's in fact a method for that: fixedOrder(true).


Is it achievable to route a dynamic URI with prefixed character?

So from reading your updated answer and looking at the other question you mentioned I would assume you are trying to create prefixed dynamic links that don't conflict with your routing.

For example site.com/about would link to your about page and site.com/#about would load up your custom template.

Problem

The problem with using a # for your trigger is that really this should be used for linking to internal sections on a page and with the above site.com/#about would actually just link to your index page and if you had a <div> with an id of about you'd find the scroll position at that div, so I think using a # is going to give you a lot of headaches.

Solution

Perhaps use the @ sign? if it's good enough for twitter to use then I don't think you'll have many problems using it.

So, in your config/routes.php you would have:

return array(
 '(?P<username>\@\w+)' => '_someCustomTemplate',
);

This will match anything starting with an @, assign it to username a variable in your template and load it up, I tested on the Happy Lager install and it worked like a charm, /about goes to the about page /@about goes to my custom template

So in your custom template you could have the following:

{# This will get rid of the @ #}
{% set username = username|replace({'@': ''}) %}

{% set user = craft.users.username(username) %}

If you want to change the trigger to something else, just change @ in the route expression and then in the replace filter, but that should do the trick for you and avoid conflicts!

on a side note, I replaced @ with a # and it didn't work, just went to the home page


Can't remove index.php without getting a 404, .htaccess redirect not working, why?

Check and make sure your .htaccess file is in your public HTML folder public/.htaccess!

If you do not include your .htaccess file in your public folder, Craft will display your home page properly, but all other pages will fail to load unless you add index.php?p= in your URL.

Without .htaccess:
http://website.com/mypagethatreallyexists = 404

Without .htaccess:
http://website.com/index.php?p=mypagethatreallyexists = Your page

With .htaccess:
http://website.com/mypagethatreallyexists = Your page


Is it possible to determine if a script is running within a task?

TasksService->getRunningTask() will tell you if a task is currently running. However, it doesn't tell you whether a particular chunk of code is being executed by that Task or by some other function.

The most straightforward solution I can think of would be to write yourself a little helper method to check whether the call stack includes a Task class:

static function inTask()
{
   foreach(debug_backtrace() as $caller)
   {
      if (substr($caller['file'], -8) === 'Task.php') return true;
   }
   return false;
}

Add that to your plugin class, and then you can call it anywhere you need:

if (MyPlugin::inTask()) ...


Your server doesn’t meet the following requirements to run Craft

I would install PHP via a third-party repository. I use Ondřej Surý's and I'm pretty happy with it. It installs PHP 5.5.

https://launchpad.net/~ondrej/+archive/ubuntu/php5


You can either cache it globally or in the session

// your cache key
$key = 'akey';

$cache = Craft::$app->getCache();
$value = $cache->get($key);
// check if there is a valid value that is not older than 5 minutes
if ($value !== null && \is_array($value) && isset($value['time']) &&
    $value['time'] >= time() - 3600) {
    return $value['value'];
}

// make your request
$response = Craft::createGuzzleClient(['base_uri' => $myBaseUri])
    ->get('GET', myQueryString, [
    'headers' => [
        'Accept'            => 'application/json',
        'Accept-Encoding'   => 'deflate,gzip'
    ],
    'connect_timeout' => 3,
    'verify' => false,
    'timeout' => 5,
    'http_errors' => false
]);

$json = $response->getBody();
$data = Json::decode($json, true);

// cache it
$cache->set($key,
    'time'  => time(),
    'value' => $data
]);


You would need to attach such functionality to EVENT_AFTER_SAVE event in the custom php module.
The module can be built using plugin factory website. For attaching functionality to the event, you don't need to select any additional components.
https://pluginfactory.io/
You can find useful information about modules here:
https://nystudio107.com/blog/enhancing-a-craft-cms-3-website-with-a-custom-module
Add these statements to use section in main module class:
use yii\base\Event;
use craft\elements\Entry;
use craft\events\ModelEvent;
use craft\helpers\ElementHelper;

Now you need to attach functionality to the event - add this code to modules main class, in init() method:
Event::on(
    Entry::class,
    Entry::EVENT_AFTER_SAVE,
    function(ModelEvent $event) {

        // react only to entries from this section:
        $section_id = 1;

        // entry data
        $entry = $event->sender;

        // if it is not a draft
        if (ElementHelper::isDraftOrRevision($entry)) {
            return;
        }

        // if entry has specific section
        if($entry->sectionId != $section_id){
            return;
        }

});

This code will run when you save entry in a specific section. Now, you need to give ourselves some control.
We will make our code run only when we check a specific field in our entry. Create lightswitch field and add it to entry field layout. Thanks to the code below, an email will be sent only when entry is saved and lightswitch is checked. Lightswitch will be automatically de-checked after saving. Just remember that lightswitch field needs to be disabled by default to keep you from accidentally sending email when you save entry first time.
$lightswitchHandle = 'someField';
if($entry->getFieldValue($lightswitchHandle) !== true){
    return;
}

$entry->setFieldValue($lightswitchHandle, false);
Craft::$app->elements->saveElement($entry);

Now we need to compose our email:
// compose email from entry content
$email_template = 'path_to_email_template.twig';
Craft::$app->getView()->setTemplateMode(\craft\web\View::TEMPLATE_MODE_SITE);
$email_html = Craft::$app->view->renderTemplate(
    $email_template,
    [
        'entry' => $entry,
    ]
);
Craft::$app->getView()->setTemplateMode(\craft\web\View::TEMPLATE_MODE_CP);

This code will inject the contents of entry as entry variable into Twig template path_to_email_template.twig placed in templates directory and render email HTML. I suggest giving your email some nice base template - here are good examples:
https://postmarkapp.com/transactional-email-templates
Now what you will do with such email, is up to you. You could potentially send this email manually using Craft mailer class:
$email_recepients = ['bob@email.com', 'mike@email.com'];
$email_subject = 'interesting content inside!';

Craft::$app
->getMailer()
->compose()
->setTo($email_recepients)
->setSubject($email_subject)
->setHtmlBody($email_html)
->send();

Just try to avoid sending emails using server built-in sendmail functionality, this can lead to your ip/domain being marked as spam-sending. It's good to use some middleman services like mailgun.
https://www.mailgun.com/smtp/
You can attach Craft to mailgun SMTP using the application configuration file:
https://docs.craftcms.com/v3/config/app.html#mailer-component
If you want to test how your emails behave, you can use a mock SMTP server like mailtrap:
http://craftsnippets.com/articles/testing-and-debugging-emails-sent-by-craft-cms-using-mailtrap
An alternative to using SMTP might be mailchimp service or something like that - in that case, instead of using Craft mailer, you would use some dedicated PHP library to send email to service API.


There is a related to parameter you can use

Entry::find()
  ->section($section)
  ->relatedTo(['targetElement' => $image->id, 'field' => 'galleryImages')
  ->all();


redirect index.php in urls on Craft site to NEVER show index.php

As another option that I usually do in addition to .htaccess, is to also add this to the array in craft/config/general.php file, and it'll stop Craft from ever adding the index.php.

'omitScriptNameInUrls' => true

Doing it this way prevents Craft from ever adding in the index.php to redirects, I believe.


Displaying the optional note from a versioned entry

Indeed... EntryModel->revisionNotes is what you're looking for.

Through PHP, that's:

$entry->revisionNotes

Through a template, that's:

{{ entry.revisionNotes }}


You can use the Site component

PHP

$groups = Craft::$app->getSites()->getAllGroups();
foreach ($groups as $group){
    echo $group->name;
    foreach ($group->getSites() as $site){
        echo $site->name;
    }
}

Twig

{% set groups = craft.app.getSites().getAllGroups() %}
{% for group in groups %}
    {{ group.name }}
    {% for site in group.getSites() %}
        {{ site.name }}
    {% endfor %}
{% endfor %}


How do I update a column value in my custom plugin with a simple controller?

You are currently performing a SELECTinstead of an UPDATE so no data will be updated.

I'm guessing that entryId is the primary key of the record you are trying to update? If so, I would suggest you find the record to make sure it exists, update its attributes (status) and then save it.

// PluginController
public function actionDeny()
{
    $this->requirePostRequest();

    $status  = PluginStatus::Denied;
    $entryId = craft()->request->getRequiredPost('entryId');

    if (craft()->pluginName->updateStatus($entryId, $status)
    {
        craft()->userSession->setError(Craft::t('Unable to deny the entry.'));
    }
    else
    {
        craft()->userSession->setNotice(Craft::t('The entry was denied.'));
    }
}

// PluginService
public function updateStatus($id, $status)
{
    $record = PluginRecord::model()->findById($id);

    if ($record)
    {
        $record->setAttribute('status', $status);

        return $record->save();
    }
    else
    {
        throw new Exception(Craft:t('No record was found with id {id}', array('id' => $id));
    }
}

Unless I'm missing something, that should be about all you need.

@selvinortiz


More extensive documentation for defining models for plugins

Have a look at Stephen Lewis' archived blog article about validation. Even better, buy the book.


When posting entries, how do I post tags as well as the title and url?

In the Control Panel you'd need to create a Tag Group first, which would hold all of you created tags. You then need to create a Tags field where you'd select the Tag Group created above as the source.

Once you've done all that, you can now add it to the fields of the entry type in question. If you gave the Tags field the name tags when you create it, you could then refer to it in your template as entry.tags.

In you template, you'd then loop through you tags and output them like this:

{% for tag in entry.tags %}
    {{ tag.title }}
{% endfor %}


It's likely your /craft directory couldn't be found. Out of the box, Craft expects it's /craft directory to be located just outside the root directory of the website. So by the looks of it from your path, your index.php file is located here:

/home/toaeroplano/public_html/

So that would mean you /craft directory should be located here:

/home/toaeroplano/

If I were you I'd put the php.ini settings back to what they were so you get the original error, then make sure you've copied the /craft directory from the installation into the directory above. If you want your /craft directory somewhere else you need to edit the index.php and specify where the directory lives. So edit this line in index.php:

$craftPath = '../craft';

The .. in the path just means the directory above, so if you wanted it to live in the /home directory, you change it to this:

$craftPath = '../../craft';

EDIT: Also make sure the permissions on the /craft/app/, craft/config/ and craft/storage/ are set as per the install docs.


Yii 1 gets its localization data from the Unicode Common Locale Data Repository.  You can see that info in craft/app/vendor/yiisoft/yii/framework/i18n/data/nl_be.php where currencyFormat is defined as 'currencyFormat' => '#,##0.00 ¤'.

¤ is the placeholder for the locale's currency symbol, which puts it after the digits, which explains the behavior you're seeing.

If you want to continue to use that specific locale, you could do a |replace everywhere you output a currency.  Or you could core hack that locale file to alter it and just make a note to check it after updating.  Those locale files do get updated, but it's pretty rarely. Or you could switch to nl.php which appears to have the currency symbol first.


How to setup the structure in Craft for a Property Development site I'm building

I think I follow where you are going. It sounds like you have a few levels so you will indeed be working with a structure (instead of a channel or single).

I'm not sure what "All Developments" means - is that "show me every single entry in this section"?

There are most likely a handful of ways you could go about doing this, here is one way that I would try.

1.) I would make your "County" a category. Call the group "Counties" and then create each county you need (ex: Berkshire). By creating your counties as categories, you can assign (relate) anything back to it. This will allow you to do things like: Get all properties from Berkshire Country. It will be up to you to decide where this relationship lives though. I would put it on the actual property though, that way you have access to it at any level.

2.) Create a few entry types:
        1) Development
        2) Property
        3) Plot
     By doing this, we can have a clean authoring experience by having each entry type have it's own fields. Meaning your Properties entry won't have inputs for the images like your Plots entry would. It just keeps things a lot cleaner for the author.

3.) Build out the structure for each Development. This is what that might look like. Let's say you have a section (of type structure) called "Developments"

 - Entry: (Type Development)
     |- Entry: (Type Property) <-- This is where I would assign County
         |- Entry: (Type Plot)

 - Entry: (Type Development)
     |- Entry: (Type Property)
         |- Entry: (Type Plot)

By setting things up this way, now we can easily do things like, "Get all plots from foo property" or, "get all properties from bar development".

Or, another completely different way would be create categories for each County, Development and Property. You would then have a single section called "Plots" (of type channel). Each entry would then have category inputs to relate the plot back to the correct County and property.

The problem with this approach is that it isn't quite as straight forward to create higher-level things like Properties. I've found that having people go back and forth from categories and entires can be confusing.

Hope this helps get you going!


Is it possible to limit how many times and entry type can be used?

If you're working with frontend forms, set the permission to use any of these entry types to a specific user group or to admins only. Use isInGroup or the admin property:

{% if currentUser.admin %} ... {% endif %}
{% if currentUser.isInGroup('usersWithSomeEntryTypesHidden') %} ... {% endif %}

If you need this in the CP, you could make a plugin that does the same and hides selected entry types from the dropdown using JS. As this is no nice solution, what about making this a feature request to Pixel & Tonic?


How can I check the progress of a large Assets indexing job?

It's not a log per se, but you can check out the craft_assetindexdata table of your database. That will show you all of the assets which Craft has indexed.

You can see how far Craft has gotten by taking a look at the recordId column - all of the already indexed entries are going to have a value in that column while the records still waiting to be indexed are going to have NULL there.


You could use your default "Text" Matrix block type for the sliders and have an extra block type as a "Slide Wrapper", to wrap "Text" (and "Image") blocks which should go into your slider.

See this question for an alternative approach, to recognize succeeding blocks of the same type automatically in the template Solution for Matrix-within-Matrix (or More Robust Table Fields).


How can I dynamically remove some unwanted formatting tags inside rich text fields?

Not a "real" answer because it won't fix your actual content, but I wrote a plugin that would make it easy to remedy your issue on the front end (i.e. in the template) –

To remove all <h1> and <h2> tags (content inside tags will not be removed):

{{ entry.someRedactorField|retconChange(['h1','h2'], false) }}

To change <h1> and <h2> tags into <p> tags:

{{ entry.someRedactorField|retconChange(['h1','h2'], 'p') }}

You can read more about the rectonChange filter used in the two examples above here.


How do I set up same site to use two different subdomains?

Are these two sites exactly the same, and this is just an alias?  If so, you don't need any configuration, just point the DNS to the right place, and it'll work.

If not, can you provide more details on what you're trying to achieve?


Payment Method and Discount elements query by ID in Twig

Commerce 1:

{% set paymentMethod = null %}
{% for pm in craft.commerce.paymentMethods %}
    {% if pm.id == 2 %}{% set paymentMethod = pm %}{% endif %}
{% endfor %}

{{ paymentMethod.name }}

Commerce 2:

{{ craft.commerce.gateways.getGatewayById(2).name }}


Headless CraftCMS as mobile app backend, requiring login

You could make the CSRF token available via an element API endpoint, then submit the form with that;

'api/csrf-token' => [
      'elementType' => GlobalSet::class,
      'paginate' => false,
      'one' => true,
      'transformer' => function () {
        $newCSRFtoken = Craft::$app->request->getCsrfToken(); // sets a new CSRF cookie;
        return [
          'csrfTokenName' => Craft::$app->config->general->csrfTokenName,
          'csrfToken' => $newCSRFtoken,
        ];
      }
    ],

*This code is from a user on the discord server, would have linked to it but you can't ¯_(ツ)_/¯


How to run domain.com and m.domain.com from one install

To use separate templates I created plugin and within init() method I overwritten templatesPath variable. Seems to be working although you need to test it with other plugins as I have tested it with fresh Craft instance only.

public function init() {
    $templatesPath = craft()->templates->getTemplatesPath();

    if(craft()->request->isMobileBrowser()){
        $templatesPath .= 'mobile/';
    } else {
        $templatesPath .= 'desktop/';
    }

    craft()->templates->setTemplatesPath($templatesPath);
}

isMobileBrowser()

This example assumes that templates folder contains mobile and desktop folders. Both have a mirror template files structure. Also you don't need m.domain.com anymore.

But it you need m.domain.com you can use multi environment config variables (environmentVariables) or stick to the previous solution.

return array(
    '*' => array(
        'templatesPath' => 'desktop',
    ),
    'm.domain.com' => array(
        'templatesPath' => 'mobile',
    ),
);

public function init() {
    $templatesPath = craft()->templates->getTemplatesPath();

    if(craft()->config->get('environmentVariables')['templatesPath']){ //this line changed
        $templatesPath .= 'mobile/';
    } else {
        $templatesPath .= 'desktop/';
    }

    craft()->templates->setTemplatesPath($templatesPath);
}

To force mobile URL insert this code to desktop base layout file:

if(craft.request.isMobileBrowser()) {
    {% redirect 'http://m.domain.com' %}
}


Missing buttons after upgrading site to 2.5

I had this same issue and resolved by fixing my Redactor configs. If you have a misconfigured file or a formatting error it causes problems related to rendering later in the page, likely related to Redactor throwing a javascript error.


Create assets subfolder on upload based on entry field value?

Yes that's totally possible, you just need to set your asset field to upload all assets to a single folder and then you can set a sub-folder path that's created dynamically. (→ Restricting Uploads to a Single Folder).

In your case that path would be something like this:

assets/images/projects/{{ object.client.first().friendlyName }}

or with fallback to a default sub-folder:

{{ object.client.first()|length ? object.client.first().friendlyName : 'Other' }}


Can't get current entry's ID - Impossible to access an attribute ("id") on a null variable

Ok, so it came out that the post I used for testing wasn't assigned to any category and that was causing the exception.

I added a validation for checking if the category is defined:

{{ (entry.blogCategory.one().id is defined and entry.blogCategory.one().id is not null) and entry.blogCategory.one().id == category.id ? 'light': 'primary' }}">{{ category.title }}


Install Craft on VPS with centos 7 and vestacp

I solved this problem. In /home/User/conf/web/example.com.httpd.conf in string

php_admin_value open_basedir/home/User/web/example.com/public_html:/home/Userl/tmp

replace on

php_admin_value open_basedir /home/User/web/example.com:/home/Userl/tmp


Of course shortly after posting this I got it working.

Created a feed and used the filename as the unique identifier and voila imported redactor content to images for 2000+ images.


After deleting the users from the UI they are only soft-deleted. If you look at the user id and find the matching element in your elements table, you will see that this element has a populated dateDeleted.
Run garbage collection (./craft gc) to clean up and completely remove everything.


How to use url segments like with Freebie in EE

One easy way to work around this is to use a query string instead of a segment. You'd redirect the user to for instance /contact?receipt=thanks instead of /contact/thanks, and you change the conditional in your template to something like this:

{% if craft.request. getParam('receipt') == 'thanks' %}
    ...thank you message
{% endif %}

I've always felt that this kind of information doesn't belong as a segment-part of an url, and the only reason we did that in EE was the lack of easy access to the query string.

Another way would be to set up a route like you did. Maybe set it to (slug)/thanks, where (slug) is the slug token. Then, at the beginning of your template, before trying to access entry, you check if it exists, and if it doesn't you pull it manually. Like this:

{% if entry is not defined %}
    {% set entry = craft.entries({ slug: craft.request.segment(1) }).first() %}
{% endif %}

Hope one of those solutions helps.


dateTime values in settings page are always a day behind

Update: Sorry dragonslovetacos, I just tested this and found out it doesn't work as expected. It probably is the right way to handle DateTime values when saved with attribute type DateTime.

You're probably not preparing the value for your template. Before saving your date to the database you convert it to a DateTime object and your system timezone gets calculated into the value. You now need to do the same thing before you render that value in your template.

Or maybe it's the other way round, and you're preparing the value for your template and save it unprepared.

Anyways, the helper function DateTime::createFromString() should be applied to your value in both, prepSettings() and getSettingsHtml().


I'd probably prefer to create the data using Craft's control panel UI for tags (→ book type) and dates than typing it all into Excel. So if it was me, I would probably vote for option a or b.

There shouldn't be that much of a speed differences between creating matrix rows or adding new entries for the data entry (there's this nice shortcut for "Save entry and create new one"), so my advice is to keep it simple and use a channel. Besides good performance (no relationship queries etc.), other advantages over matrix rows is to view and sort the books nicely in the CPs index view (customizable since Craft 2.3) or use the default disable switch (with traffic lights on the index view).

This gives your client a nice and flexible UI today and you can start developing a custom plugin that gets their system data into Craft. Even with that future solution it could make sense to have the entries available in Craft, to add website only data or make relations to other entries for example, and your plugin will be more of a custom import solution than a simple view to the external API data.


What is the difference between calling .first and .first()?

On a more philosophical note; Put yourself in the shoes of first(). It's a method, doing an important job in the Craft eco-system. It prides itself on returning the most important and relevant data in an efficient and unencumbered way. Serving up entry after entry, asset after asset, 24 hours a day, all week, all year. Never complaining, never getting any praise.

And you want to treat it like a simple property? As a dumb, brainless reference to some data that some other method created? How do you think that makes first() feel? How would you feel if someone treated you as property?

I know you're a good person with a big heart, Mats. I'm sure you will do the right thing and treat first() with the respect it deserves, even though it means that you'll have to write a few more characters a day.


Craft Commerce: Detecting & Styling Flash Messages?

Getting a flash message also deletes it, so you should set it to a variable first, otherwise, when you try to access it a second time, it will be empty.

So you need to do something like this:

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

      {{ flashNotice }}
      &times;

{% endif %}

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

      {{ flashError }}
      &times;

{% endif %}


Here's a multi environment config guide that I think will accomplish all you need.

It allows for environment specific DB connection parameters, but can also set filepaths, that why you can keep your assets path relative in regards to the path set in the environment.

It's an extension to the multi-environment setup as discussed here.

If you have any questions after reading that, feel free to ask.


You’d start by creating a new EntryModel and copying over the normal properties:

$oldEntry = craft()->entries->getEntryById(100);

$newEntry = new EntryModel();

// core element attributes
$newEntry->enabled = $oldEntry->enabled;
$newEntry->locale = $oldEntry->locale;
$newEntry->localeEnabled = $oldEntry->localeEnabled;
$newEntry->slug = $oldEntry->slug;
$newEntry->uri = $oldEntry->uri;
$newEntry->getContent()->title = $oldEntry->getContent()->title;

// entry attributes
$newEntry->sectionId = $oldEntry->sectionId;
$newEntry->typeId = $oldEntry->typeId;
$newEntry->authorId = $oldEntry->authorId;
$newEntry->postDate = $oldEntry->postDate;
$newEntry->expiryDate = $oldEntry->expiryDate;

// do this if it's in a Structure section
if (($parent = $oldEntry->getParent()) !== null)
{
    $newEntry->parentId = $parent->id;
}

Where things get tricky is copying all of the content. That's tough because many field types expect their values to be in one format when saving, but they return their values in another format when you call $entry->myFieldHandle. For example relationship fields (Entries, Assets, Users, Categories, and Tags) each expect you to set their values as an array of related elements’ IDs when saving an entry, but when you call $entry->myRelationalField (or entry.myRelationalField from a template) you’re getting an ElementCriteriaModel back. So for those, you’d have to do this:

$newEntry->setContentFromPost(array(
    'myAssetsField' => $oldEntry->myAssetsField->ids()
));

There are some other posts that touch on how to format the various field types’ values (i.e. Duplicating Matrix fields with content from another locale), but nothing comprehensive.


Setting up multiple category listing pages on site with multiple locales

The reason it's not working is that domain.com/people/foo/practices/categoryA is not a match for the Category group's URI settings. Only urls that match people/practices/{slug} are going to have that category variable defined.

And this code:

{% if category is defined %}
    {% set entry = category %}
{% endif %}

... won't define an entry if there's no category defined.

If your People structure is a flat list (no nested children), you could create a URL rules like this:

'people/<person:{slug}>/practices/<category:{slug}>' => [
    'template' => 'test/4'
],

That would take a URI like domain.com/people/foo/practices/categoryA and use the  people/_practices template and populate variables for person and category.

{{ person }}    {# foo #}
{{ category }}  {# categoryA #}

Then you could make an category query like this:

{% set cat = craft.categories().group('practices').slug(category).one() %}

{{ cat.title }}

But personally, I've found it easier to use query strings to filter a list instead of URL segments. You could possibly use a URL like this:

domain.com/people/foo?practice=categroyA

or...

domain.com/people/foo/practice?cat=categroyA

or even...

domain.com/people/practice?person=foo&cat=categroyA

Whichever works the most logically for your use case. Then use craft.app.request to pull the "categoryA" string from the URL:

{% set categorySlug = craft.app.request.getQueryParam('cat') ?? null %}

{% if categorySlug %}
  {{ categorySlug }}   {# "categoryA" #}
{% endif %}

Then use the categorySlug string in an entry or category query:

{% set cat = craft.categories().group('practices').slug(category).one() %}

Update

It's important to note, if you want a variable you set to be available in a layout template ({% extend %} tag), you need to define it outside of a {% block %} tag.

{% extends "_layouts/index" %}

{% set categorySlug = craft.app.request.getQueryParam('cat') ?? null %}

{% block content %}
  Content...
{% endblock %}

The categorySlug variable should now be available in the _layouts/index template context as well.

<body>
  <div class="language-switcher">
    {% if categorySlug is defined %}
      Yes it is!
    {% endif %}
  </div>

  {% block content %}
  {% endblock %}
</body>


Is there a tool to migrate Joomla/K2 entries into Craft?

I'm not sure there is anything specifically for importing from Joomla, but you could try the Import plugin. Depending on the data format, you may also be able to use the Feed Me plugin.

Edit: Alternatively, a popular Craft CMS plugin maker (Barrel Strength Design) offers a commercial service to migrate/import data from Joomla -> Craft CMS. It's called The Great Migration. (NOTE: I'm nothing to do with the service, I just had it bookmarked for future reference)


Pushing to an array within a class doesn't work

I feel like we're missing some code in your examples, but in a plugin there is no direct link between a template and its services.  You go through a Variable class, which is responsible for exposing your service's methods.

So in your example, in your plugin's variables folder, you'd have a MyPluginVariable class that has it's own someFunction method like this:

class MyPluginVariable
{
    public function getMyArray()
    {
        return craft()->myPlugin->myArray;
    }

    public function someFunction()
    {
        return craft()->myPlugin->someFunction();
    }
}

Then from your template, assuming you call someFunction() first, you should get the correct output.

{{ craft.myPlugin.someFunction() }}
{{ dump(craft.myPlugin.myArray }}

I'd also recommend refactoring your service to encapsulate the public array in a method and use the PHP [] syntax to avoid the function overhead of array_push().

Something like this:

class MyPluginService extends BaseApplicationComponent
{
    private $_myArray = array('test1', 'test2');

    public function someFunction()
    {
        $someText = "This should end up in myArray";
        $this->_myArray[] = $someText;
    }

    public function getMyArray()
    {
        return $this->_myArray;
    }
}

Then your Variable's getMyArray() method would change to this:

public function getMyArray()
{
    return craft()->myPlugin->getMyArray();
}


Craft CMS plugin action debugging with IntelliJ

Right! setting the mapping to the specific file, in this case the controller of plugin made it all work.

Thanks again for taking the time!


How to extract a list of "copy password reset urls" for all users?

Given a list of elements/User that you want to get the password reset URL for, you can loop through them and call this for each one:

$url = Craft::$app->getUsers()->getPasswordResetUrl($user)'


Element API -- getting table data from a matrix

The table field contains an array of all columns, I don't really see where you problem is since you could just do data[0].flavorText[0].text in your template and as you already explained you can see the direct output so you can just address the index in the array.

$block->flavorText[0]->text

will return you the text within the first column

In order to get the values from all columns you'll have to loop through them

$values = [];
foreach($block->flavorText as $column){
    $values[] = $column['text'];
}


UPDATE:

in this case you should use :empty:, like this:

{% set orphanBooks = craft.entries.section('books').relatedStories(':empty:') %}

ORIGINAL CONTENT

According to the documentation, you can query the entries using your field handle:

  TIP

  Most custom fields support element query parameters as well, named
  after the field handles.

https://docs.craftcms.com/v3/dev/element-queries/#executing-element-queries

You might use :notempty: as a negative selector, just like in this query:

{% set products = craft.entries.section('product').productSale(':notempty:') %}

These SE question and answers might be helpful, too:

Get entries NOT related to categories

Return entries where field is "not empty"


It looks like the folder name of your plugin is wrong. Change it to just say craftimport.

The folder name should be the same as the "Whatever" part of the WhateverPlugin.php file sitting in the plugin's root folder. Except that the folder name should always be all lowercase. That's important, because some systems are case-sensitive while others are not... The only way to be certain your plugins will be detected is to use a lowercase folder name. (see this thread...)


Search against a checkboxes field's options

Checkboxes / multiselect fields store to the database in a very query unfriendly way.
That's why I would really recommend to use categories, tags, lightswitches instead, if you intend to
query against these fields' values.

Here's what you'd have to do to search for entries with selected checkboxes field types' options. You basically have to get all relevant entries first,
go through them to check each checkbox option and store the element ID in case the entry is valid. Using this approach you end
up with an array of IDs which you can now query against.

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

{% set query = craft.request.getQuery() %}

{% if query.mobilityOptions is defined %}
    {% set ids = [] %}

    {% for entry in entries %}
        {% set valid = true %}

        {% for option in query.mobilityOptions %}
            {% if not entry.mobilityOptions.contains(option) %}
                {% set valid = false %}
            {% endif %}
        {% endfor %}

        {% if valid %}
            {% set ids = ids|merge([entry.id]) %}
        {% endif %}
    {% endfor %}

    {% set entries = craft.entries.id(ids) %}
{% endif %}


That would happen if you are using Commerce Lite. You will need Commerce Pro if you want the 'full' cart functionality.


PHP warning on Guzzle failed to open stream: No such file or directory

Files were not added to the deployment branch!


Commerce 2 — Guest checkout error: no customer email address exists on this cart

Switching to the new template structure fixed the issue I had regarding the email not being recognised/saved.


How do I use Guzzle to send a post request from my plugin?

It is pretty obvious now, but indeed the API is looking for json formatted values and I was in fact sending name:value pairs.

Here is what the updated logic looks like for anyone who comes across this.

MyPlugin.php

$client = new \Guzzle\Http\Client('http://api.site.dev/');
$uri = 'my/endpoint/path';
$post_data = array(
    'craft_id' => $event->params['user']->id,
    'name' => $event->params['user']->friendlyName,
    'email' => $event->params['user']->email
);

$data = json_encode($post_data);

$request = $client->post($uri, array(
    'content-type' => 'application/json'
));

$request->setBody($data);
$response = $request->send();


In Craft 2.x, it isn't possible for plugins to provide their own email providers that the entire Craft installation can use.  They can provide their own that they can use for themselves, but it won't affect things like when Craft sends an activation email.

That's changed in the upcoming Craft 3, though.  There is an example Mandrill plugin here on how to pull that off.


Craft goes with Yii 2's defaults in the FileTarget class, which is 10MB for a log file before rotating. https://www.yiiframework.com/doc/api/2.0/yii-log-filetarget#$maxFileSize-detail

You can overwrite that behavior from your config/app.php file by using something like:

'components' => [
        'log' => function() {
            $log = Craft::createObject([
                'class' => yii\log\Dispatcher::class,
                'targets' => [
                    [
                        'class' => craft\log\FileTarget::class,
                        'levels' => !YII_DEBUG ? yii\log\Logger::LEVEL_ERROR | yii\log\Logger::LEVEL_WARNING : yii\log\Logger::LEVEL_ERROR | yii\log\Logger::LEVEL_WARNING | yii\log\Logger::LEVEL_INFO | yii\log\Logger::LEVEL_TRACE | yii\log\Logger::LEVEL_PROFILE,
                        'maxFileSize' => 5120, // 5MB
                    ],
                ],
            ]);

            return $log;
        },
    ],


Try using {{ currentSite.baseUrl }}

I had the same issue when I started using environment variables instead of @web for my sites. I've found the above works for all cases.


(How) can I fill a visitor's cart by using a link?

There is currently no way to populate a cart from a link as a standard feature of Commerce 2.

But you could accomplish this in 2 ways.

1) With a smart template to add all items from the order to the current cart.

Create a template that takes an order number param like so:

cart/start-order?number=5bnuy8843nuy48bdy48u3bd84

In that template you would do the following:

{# @var craft \craft\web\twig\variables\CraftVariable #}

{% set number = craft.app.request.param('number') %}
{% set previousOrder = craft.orders.number(number).one() %}

{% if not order %}
  {% redirect 'shop' %}
{% endif %}

{# Get the current cart if there is one, also make sure the cart is persisted so we can add to it. #}

{% if cart is not defined %}
  {% set cart = craft.commerce.getCarts().getCart(true) %}
{% endif %}

{# Clears the current cart. This is optional as you may just want to add the items to the cart #}
{% do cart.setLineItems([]) %}

{% for oldLineItem in previousOrder.lineItems %}

  {% set lineItem = craft.commerce.lineitems.createLineItem(cart.id, oldLineItem.purchasableId, oldLineItem.options, oldLineItem.qty, oldLineItem.note) %}

  {% do cart.addLineItem(lineItem) %}

{% endfor %}

{% do craft.app.elements.saveElement(cart) %}

{%  for lineItem in cart.lineItems %}
  {{  lineItem.qty }} * {{ lineItem.description }}
{%  endfor %}

This add all items from the cart in the email link to the current cart. You would do a similar thing to populate addresses etc if you wish.

2) Alternatively, if you just want to replace the current cart in the users session with the incomplete cart you passed in from the email’s number param, you could do the following

Create a plugin/model with a controller action.
The controller action would accept an order number param (like the template example above)
The controller action would do the following:

Code:

$request = Craft::$app->getRequest();
$session = Craft::$app->getSession();

$number = $request->getParam('number');
$order = \craft\commerce\elements\Order:find()->number($number)->one();

if ($order && !$order->isCompleted){
  // Forget the current cart in their session.
  \craft\commerce\Plugin::getInstance()->getCarts()->forgetCart();

  $session->set('commerce_cart', $number);
  return $this->redirect('shop/cart');
}

return $this->redirect('shop/cartLoadError');

This should get you mostly there. Let me know if you have any questions.


A site's home page is routed via a special route __home__. I don't believe you can set that in routes.php, but it's worth a shot. Generally, you specify a Single as the homepage, and can set the template there. It defaults to index.


Dynamic Navigation with Condition to check slug

Got it to work! I don't know why I made it so complicated in my head, but I ended up using the link id to match with the properties link.

{% nav link in menu.all %}

        {{ link.title }}{% if link.id == 15 %}{% set totalListings = craft.entries.section('listings').total() %}{{ totalListings }}{% endif %}

{% endnav %}

If someone can show me how to get it by slug that would be awesome! Thanks & hope this will help someone in the future.


Matrix-field with a category-field to output category-title on single entry page

Nevermind, the problem is solved.

I just need to loop over the categories.

{% extends "matrix/default" %}
{% block blockContent %}

        {% for merk in block.merkNaam %}
            {{ merk.title }}
        {% endfor %} #}

{% endblock %}


Set Password on Test User Account in Localhost

If you have access to the database you can change the user's password to: $2y$13$FfVKvo1W1N5Gsba3c19r8uz.x9R5NRQgRu8jmXvOjVCStg1sKGcoW which is 'secret' when un-hashed.

Source: https://twitter.com/nystudio107/status/1063135843441954816


Can I Create a Way for Users to Add Fields from the Dashboard and/or create Fields with Same Handle?

I might get you wrong but they must have the option to add as many youtube embed videos as they want?

Isn't it an option to let your client make a new block for every embed and just loop over the blocks they made?

So your block would look like:

Youtube Video (block)

Youtube embed link
Youtube Description

So they just have to add a block when they want a new one and you can just loop over them like you would with any matrixblock?


Template variables from active record on NULL value

You don't want to expose a Record to the template level for many reasons, including inadvertently letting a template call record.save() causing unexpected updates to the database, but you also don't want to expose any unnecessary data to the template (like "password", in your example).  A bit more info on that here.

This is where Models come in. They don't have the database overhead and you can limit what gets exposed to the template as well as combine data from multiple records into one model for easy consumption.

Create a MyPlugin_TwitterAccountModel class like so:

class MyPlugin_TwitterAccountModel extends BaseModel
{
    protected function defineAttributes()
    {
        return array(
            'oauth_token' => AttributeType::String,
            'screenname'  => AttributeType::String,
        );
    }
}

(include any other attributes your template might access)

Then change your getAllAccounts() method in your service to:

public function getAllAccounts()
{
    $records = MyPlugin_TwitterAccountRecord::model()->findAll(array('order'=>'oauth_token'));
    return MyPlugin_TwitterAccountModel::populateModels($records);
}

Now your template will only have access to your plugin's model.

Having typed all of that, I literally have no idea if it will solve your original issue.

It might be some odd bug with Twig's interactions with Records that you're the first to run into since we highly discourage exposing Records to templates.


For any Elements that do not have URLs, like Tags or Users, you'll need to tell Sprout SEO what metadata to use.

Also, be sure that the do craft.sproutSeo.meta tag comes before the {% sproutseo 'optimize' %} tag. The do tag prepares the metadata for the optimize tag to process:

{% do craft.sproutSeo.meta({
  section: 'Self Help Tips',
  title: tag.title,
  description: metadata.metaDescription|striptags|slice(0, 150),
  canonical: craft.request.url,
  ogTitle: tag.title,
  ogType: 'article',
  ogUrl: craft.request.url,
  ogImage: metaImage.url(),
  ogImageWidth: metaImage.width,
  ogImageHeight: metaImage.height,
  ogImageType: metaImage.mimeType,
  ogSiteName: siteName,
  ogDescription: metadata.description,
  twitterCard: 'summary',
  twitterSite: '@twitteruser',
  twitterCreator: '@twitteruser',
  twitterTitle: tag.title,
  twitterDescription: meta.search.description,
  twitterUrl: craft.request.url,
  twitterImage: metaImage.url()
}) %}

{% sproutseo 'optimize' %}

We do have on our list to simplify this scenario and allow you to add an Element Metadata field to the field layout and just pass the elementId.

{% do craft.sproutSeo.meta({
      elementId: 123
}) %}

{% sproutseo 'optimize' %}


If you need to get the level 2 children for a specific Structure entry, and you have that entry's EntryModel (i.e. you have an entry variable which refers to the Structure entry), you can just use the EntryModel's children property, appending level to only pull children from the desired level:

{% set children = entry.children().level(2) %}

There's also a descendantOf parameter which can be used in a craft.entries query – obviously, this should be the Structure entry's ID (or the complete EntryModel, i.e. entry):

{% set entries = craft.entries({
    'section' : 'yourSectionHandle',
    'level' : 2,
    'descendantOf' : entry.id
}) %}

Also, if you want to pull entries at a certain level in a Structure section but not limit it to children of a certain entry, you can append the level parameter to your basic craft.entries query:

{% set entries = craft.entries.section('yourSectionHandle').level(2) %}

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

Note that craft.entries queries can also be typed out using object notation, if you prefer (I find the object notation more readable, especially if you have a lot of parameters):

{% set entries = craft.entries({
    'section' : 'yourSectionHandle',
    'level' : 2
}) %}


GraphQL private schema returning public schema scope

It turned out that MAMP Pro was stripping out the Authorization header, so every request was treated as a request to the public schema. The solution for MAMP Pro was to enable "Identical PHP version for all hosts (module mode)" in the PHP tab.

Thanks to Brandon Kelly from the Craft forum for pointing me in the right direction.


Can't access Craft CMS admin in Chrome Canary 63.0.3233.0

Context: https://ma.ttias.be/chrome-force-dev-domains-https-via-preloaded-hsts/

The gist is that Chrome is forcing all .dev domains to use https (SSL) via HSTS.

If you want to continue to use a .dev domain, then you'll need to create a valid self-signed SSL cert and make sure OpenSSL is configured properly on your box.

Alternatively, you can swap to using something like craft.test because .test is reserved to never be used as an official top level domain. https://en.wikipedia.org/wiki/.test


Set multiple entries from different categories merge or join?

Use Craft's indexBy and index them by ID. If then you use Twig's merge function the duplicates will be removed and everything will be available in one array.

And may I suggest that if you're going to have a lot of logics in your templates, you'd better move this to a plugin and rewrite it in PHP. Templates should mostly be about templating, not about logics.


The Simplest Answer

{% set products = craft.commerce.products.search(query).order('score') %}

However, I am not sure a search is guaranteed to return ALL the products in quite the same way as using relatedTo, which is how you'd typically get entries/products related to a category - so see below.

A Slightly Fuller Answer

(Using your form)

So in this case I think you're just wanting to get all products related to a category?  As in, you're not actually searching within those right?

If so you can leave out the search parameter below, as all you're doing is finding the products related to the category.

(If you want to search within those products for something else, then you need the search: "some value" part).

Anyway, q has your category (title?) in it (would be better to use the slug in your form values really), so you can just:

{% set categoryTitle= craft.request.getParam('q') %}
{% set category = craft.categories.title(categoryTitle) %}  {# (or better change form values and this to .slug) #}
{% set products = craft.commerce.products({
                relatedTo: category,
                search: "whatever",
                order: "title asc",
            }) %}
{% for product in products %}
    etc...

(Obviously a more typical way of doing this is to do it off a url segment or whatever for an index page, rather than by a search)

A Considerably Fuller Answer

Absolutely you can search products in the same way.  Even better, since about Craft 2.5 when they added the serachScore to the results, you can simultaneously search multiple element types and combine the results.

Here's some sample code that will get you started:

    {% if query|length %}

    {% set product_results = craft.commerce.products.search(query).order('score').limit(limit_for_each_section).with('variants') %}
    {% set article_results = craft.entries.search(query).order('score').limit(limit_for_each_section) %}
    {% set category_results = craft.categories.search(query).order('score').limit(limit_for_each_section) %}

    {% set all_results = [] %}

    {% if product_results|length %}
        {% set all_results = all_results|merge(product_results) %}
    {% endif %}
    {% if article_results|length %}
        {% set all_results = all_results|merge(article_results) %}
    {% endif %}
    {% if category_results|length %}
        {% set all_results = all_results|merge(category_results) %}
    {% endif %}

    {% if all_results|length %}

        {% set all_results = all_results|sortByField('searchScore', 'desc') %}

        {% set items = [] %}

            {% for result in all_results %}

            {% if result.url|length %}

                {% if result.elementType == 'Commerce_Product' %}

                     ... etc


Nobody is going to be harsh here, so don't worry. The Craft community is a nice place :-)

Sounds like the site root is not pointing to the Craft public accessible folder (the one with index.php). Can you open the URL to another file in the site root? Add a test.txt file for example and try to open it in your browser.

Regarding the .htaccess file: You are using apache, right? .htaccess doesn't work for nginx. Might sound obvious, but you wouldn't be the first to make this mistake. ;-)


Second Site Channel URL Route Not Working

I forgot to add define('CRAFT_SITE', 'sub'); to the index.php file on my Site 2 web root.


How can I order search results by the post date?

{% set entries = craft.entries.orderBy('foo').all() %}

There is an orderBy property you can use on the query


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

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

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

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


Is there any way to code multiple lines in a twig file?

You can't execute multiple statements inside of a single {% %} block.
You can have a single statement that is multi-line.
See here for more info: https://stackoverflow.com/questions/31111516/does-twig-support-executing-multiple-statements-inside-a-single-block


I can't get Dukt Videos working in a Matrix field?

I'm sure the documentation could be better, but this has nothing to do with Duct Video, it's simply a user error. Although a common one, when accessing things inside a matrix, assets field, or similar.

When you do:

{% for video in entry.videos %}

video is a matrix block. Not the field you've created inside that matrix blocktype. When accessing video.videoUrl, you get the an object of type Videos_VideoModel, same as when you add this field directly to an entry type. The correct way to do this (disregarding that videoUrl is a bad name for the video field) would be:

{% for block in entry.videos %}
  {% set video = block.videoUrl %}

title: {{ video.title }}

url: {{ video.url }}

embed: {{ video.embed({ width: 300, height: 200 }) }}

{% endfor %}


I have accidentally add "return array.." two times in my config/general.php file. I removed one and its working fine now.


If you have created a Color Field the color selector dialogue uses your browsers built-in color selector.

On Chrome and Firefox, you should see a dialogue like this:

|   |  |

If you click over to the 2nd tab (indicated by the sliders) you'll be able to insert a hex value for the color in the input field at the bottom.

In Firefox you'll need to select RGB Sliders from the dropdown, then you'll see the Hex color input at the bottom.


Is there a way to ask for an email address before you download a file?

You can create a simple form and if the user submits he gets redirect to the thank you page with the download link.
Another option is, to send the user the download link via the entered mail. I think that will save you more from trash mails.


Hard to know without seeing some template code, but the syntax for saving existing tags on a front-end entry form looks like this:

<select multiple name="fields[fieldHandle][]">
    <option value="13">Tag Name One</option>
    <option value="42">Tag Name Two</option>
    <option value="144">Tag Name Three</option>
</select>

Replace fieldHandle with the actual handle of your Tags field and 13, 42 and 144 with the actual Tag IDs.

There's a great list of examples for what front-end inputs need to look like for Craft fields here you might find useful: https://straightupcraft.com/articles/craft-cms-field-guide-twig?view=input


Displaying Craft templates in external apps

You should take a look into Craft's index.php file. It sets some constants, autoloads Composer dependencies and loads the environment file before actually loading the script that you tried. Here is the snippet:
<?php
/**
 * Craft web bootstrap file
 */

// Set path constants
define('CRAFT_BASE_PATH', dirname(__DIR__));
define('CRAFT_VENDOR_PATH', CRAFT_BASE_PATH.'/vendor');

// Load Composer's autoloader
require_once CRAFT_VENDOR_PATH.'/autoload.php';

// Load dotenv?
if (class_exists('Dotenv\Dotenv') && file_exists(CRAFT_BASE_PATH.'/.env')) {
    Dotenv\Dotenv::create(CRAFT_BASE_PATH)->load();
}

// Load and run Craft
define('CRAFT_ENVIRONMENT', getenv('ENVIRONMENT') ?: 'production');
$app = require CRAFT_VENDOR_PATH.'/craftcms/cms/bootstrap/web.php';
$app->run();


PHP notice unserialize(): Unexpected end of serialized data

Update:

Craft 2.6.2957 added support for PHP 7.1

There's a lot going on here, but 1st thing is 1st.

  The vagrant box is running PHP 7.1

Craft 2 (and the current latest version of the Yii framework - 1.1.17) are not yet compatible with PHP 7.1.

When the next Yii 1 release is out that addresses the framework compatibility issues, we'll get Craft 2 up and running on PHP 7.1.

Until then, you'll want to use PHP 7.0.x.


Output entries sorted by category title and paginate

Not exactly what you're looking for, but it gets you closer.  This should display the order you want, but it doesn't have any pagination (and honestly I'm not sure if that's possible using Craft's {% paginate %} tag along with your other requirements.

IMO, you're probably going to be better off doing this in PHP either using a Template Hook or a custom plugin.

Regardless, here's the Twig code:

{% set allProjects = craft.entries({
    section: 'projects',
    order: 'title asc'
}) %}

{% set categories = craft.categories({
    relatedTo: allProjects,
    title: 'not Unknown',
    order: 'title desc'
}) %}

    {% for category in categories %}

{{ category.title }}

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

            {% for entry in relatedEntries %}

{{ entry.title }}

            {% endfor %}

    {% endfor %}


Edit entry, on the front end, with the entries field

There is a in operator to check if a variable is in a string or an array. Your entries field actually contains an object of type ElementQuery instead of an array of elements.

All you need to do is to check if the current entry is in the list of selected entries

{% set selectedEntries = entry.planItems.ids() %}

{% set entries = craft.entries.section('exercises') %}
{% for entry in entries %}
    <li>
        <input
            {% if entry.id in selectedEntries %}checked{%endif%}
            type="checkbox"
            name="fields[planItems][]"
            value="{{ entry.id }}">{{ entry.title }}
    </li>
{% endif %}


So the way I'd recommend doing it is that you use .gitignore to exclude your /vendor directory from the repo; a good starter .gitignore file can be found here: https://github.com/nystudio107/craft-scripts/blob/master/example.gitignore

Then you'll work on it in local dev, update Craft/plugins in local dev, and on deploy to production have it do composer install to update your /vendor dir to match what is is your composer.lock file.

The article Setting up a New Craft CMS 3 Project covers this in the DEPLOYING CRAFT CMS section.

You also might want to get a workflow set up for syncing the database and assets as per the Database & Asset Syncing Between Environments in Craft CMS article.


Sounds like you need to allow anonymous access to your controller action.

I guess it works on your computer because you are logged in and thus have a valid session, but then when you try on mobile, you haven't got a valid session, your controller isn't set up to allow logged out users to post to it and therefore you get redirected to /login

To allow non logged in users access to this action, its simple. Add either one of these just at the top within your controller class.

// Will allow anonymous access to all actions within this controller
protected $allowAnonymous = true;

// Only allow access to specific actions
protected $allowAnonymous = array('action addParticipant');


How to duplicate an element type model with content?

I'm going to look pretty dumb if somebody drops a one-line answer down the line, but here goes: AFAIK, unfortunately, "cloning" an element (e.g. an EntryModel) isn't that simple.

For one, $model->getContent() will pull the entry's ContentModel, which basically contains all the custom fields that are stored in the craft_content table for your particular entry. Unfortunately, certain fieldtypes (e.g. Matrix, relational fields such as Assets, Categories, Entries etc) don't actually store their content in the craft_content table, so their values in the ContentModel will be null. To get to those values, you'll basically have to loop over your original entry's FieldLayout, and pull each field's actual value. You could then apply those values to your new EntryModel's setContentFromPost() method, but remember that relational fields (such as Assets and Entries) should be set as an array of IDs, not an ElementCriteriaModel (which is what you'll get back when you pull the field using $entry->$someFieldHandle or $entry->getFieldValue('someFieldHandle').

If the entry you want to clone has Matrix fields, that's another can of worms. Matrix blocks are actually elements in themselves, and you'll basically need to follow the same pattern as with cloning the entry itself, in order to properly "clone" the blocks.

Unfortunately, I don't have a full, working example typed out, but here's some non-tested boilerplate code to get you started (I conveniently skipped the Matrix part, but there's a link at the bottom which may be helpful):

$newModel = $model->copy();
$newModel->id = null;

// Loop through the original model's custom field values, and set them to the new model; account for relational fields
$customFieldValues = [];
foreach ($model->getFieldLayout()->getFields() as $fieldLayoutField) {
    $field = $fieldLayoutField->getField();
    $fieldType = $field->type;
    $fieldHandle = $field->handle;
    $fieldValue = null;
    switch ($fieldType) {
        case 'Assets': case 'Entries': case 'Categories': case 'Tags': case 'Users':
            $fieldValue = $model->$fieldHandle->ids();
            break;
        case 'Matrix':
            // Whoa, Nelly
            break;
        default:
            $fieldValue = $model->$fieldHandle;
    }
    if ($fieldValue) {
        $customFieldValues[$fieldHandle] = $fieldValue;
    }
}
$newModel->setContentFromPost($customFieldValues);

// Save the new entry
$success = craft()->entries->saveEntry($newModel);

Note that if you need this to work for all element types, you may get away with calling craft()->elements->saveElement($newModel) at the end, instead of the EntriesService::saveEntry method. Otherwise, you'll need to check the $newModel->elementType property, and call the appropriate service's method for saving the element.

Finally, here are some other SE threads that may be of interest:

Getting all content, including Matrix fields from an EntryModel
How to get all (prepped) field values from an entry, indexed by field handle
Duplicating Matrix fields with content from another locale


in the end, for me making

/opt/www/dave/web/tmp

writable for craft was the solution.
thanks for all the feedback!


Yes, absolutely. You can use the {% requireLogin %} tag at the top of any templates that you want to protect, and you can create a login form very easily to allow registered users to login and access the site:

https://docs.craftcms.com/v3/dev/examples/login-form.html


Show multiple products (urls/pages) on the front-end with dynamic titles/descriptions/meta based on a single product in Craft Commerce

Go to admin panel settings->routes
And do something like:

First * is your product-slug, Second * is your variant unique value, could be SKU or some other custom field you have.

Then in your your-product-detail.twig

   {% set productSlug = craft.app.request.segments()[1] %}
   {% set variantUniqueValue = craft.app.request.segments()[2] %}

Then based on these two values, do craft variant query to get variant information and display it.


No, you still have to set the parameters by your own:

{# Get this entry's entry type #}
{% set type = entry.getType() %}

{# Set parameters for prev/next elements list #}
{% set params = craft.entries.section('blog').type(type) %}

{# Get the prev/next elements #}
{% set prevEntry = entry.getPrev(params) %}
{% set nextEntry = entry.getNext(params) %}

{# And make sure to only output the links if the element exists #}
{% if prevEntry %}Previous{% endif %}
{% if nextEntry %}Next{% endif %}


Calling a method from a service in another plugin from an event

Capitalization matters when you're calling services. Try updating this line:

$enquiryModel = craft()->contactformextra->newEnquiry($attribs);

to this:

$enquiryModel = craft()->contactFormExtra->newEnquiry($attribs);

and make sure you update that across all the places where you're calling that service. Try that, and post the results. I'll take another look if you're still seeing the issue, which may still be there; just grabbing at low-hanging fruit right now. :)


Retrieve entries by random sort, then resort alphabetically

I recon Supersort is they way to go, as its got some really handy functions to make this sort of thing a breeze. Try with the following:

{% set entries = craft.entries.section('presenters').order('RAND()').limit(10) %}
{% set sortedEntries = entries | supersort('sortAs', '{lastName}') %}

{% for item in sortedEntries %}
    {{ item.title }}
{% endfor %}


Can two sites use the same channel, but have unique entries?

There is a light switch field in your section settings with the label "propagate entries across all sites". Just turn it off and your entries will be unique


You're running Craft 2.6+ and you've got an older version of the Redactor I plugin installed that isn't Craft 2.6 compatible.  If you download the latest version of the plugin and install it, the error will go away and Redactor I will work as usual.


Is there a way to eager-load fields in a global set?

Do you mean lazy load or eager load?
Usually you lazy load images via JS to speed up site loading but not I'm not sure doing it to one image across multiple pages is all that beneficial?
After the initial load, the browser should cache that image and "reuse" it. However, querying the global set certainly has a cost on the database.
As far as eager loading, not sure it'll make a huge difference here and it looks like you're not outputting any transforms but you might consider using, say, {% cache globally %} tag on your header. That'll save you some db queries that can have an affect on performance.


P&T Contact Form plugin: How to display error messages in other languages via Ajax

Well, the problem was that I was posting my Ajax request to / from all languages, therefore the site replied back with the site main language, which was French in my case.

The following finally works:

// Get the proper url to post to

if ( '{{ craft.i18n.getCurrentLocale() }}' == '{{ craft.i18n.getPrimarySiteLocale() }}' {
     var url = '/';
} else {
     var url = '/{{ craft.i18n.getCurrentLocale() }}/';
}

// Send it to the server

$.post(url, data, function(response) {
     .......
});


How to identify the logged in user inside a plugin?

I'm a bit confused by your question.

It sounds as though you want to check if the currently logged-in user is an Administrator, but it looks like you're actually checking whether the newly-registered user is an Administrator.

Here's how to retrieve the currently logged-in User, and check whether they belong to a specific User Group.

function (Event $event)
{
    $user = craft()->userSession->getUser();

    if ($user && $user->isInGroup('groupHandle') {
        // Do stuff.
    }
}

Alternatively, you could check whether the current user has permission to perform a specific action:

if ($user && craft()->userPermissions->doesUserHavePermission($user->id, 'registerUsers')) {
    // Do stuff.
}


Setting a default entry for the entry field

Entry fields do not natively support setting a default entry, but you can use the Globals section to achieve the same effect.

Create a 'Default Call to Action Entry' field in your globals, and set the default entry for all pages there. For extra functionality, I would then also have a 'Call to Action Entry' field on your entries.

With this set-up, a user can select a custom entry to use on that entry if they want, but by default it will fall-back to the entry selected in the Globals.

Your template would look something like this:

{% set ctaEntry = entry.callToActionEntry.one|default('globalHandle.defaultCallToActionEntry.one') %}

If you didn't want to build the ability to override the default entry on a per-page basis, you can just use the field in the Globals:

{% set ctaEntry = globalHandle.defaultCallToActionEntry.one %}


This is a plugin, yes. It's Spoon by Angell & Co.


Looking at the \craft\commerce\services\OrderAdjustments you should be able to change the ordering of your adjustments by using the \craft\commerce\services\OrderAdjustments::EVENT_REGISTER_ORDER_ADJUSTERS event.

use craft\events\RegisterComponentTypesEvent;
use craft\commerce\services\OrderAdjustments;
use yii\base\Event;

Event::on(
    OrderAdjustments::class,
    OrderAdjustments::EVENT_REGISTER_ORDER_ADJUSTERS,
    function (RegisterComponentTypesEvent $e) {
        $e->types = [
            // Add the adjusters in whatever order you want here. Leaving this empty would disable all adjusters.
        ];
    }
);


In your example, you're attempting to use a SectionModel instance as the key for your grouped entries. This is not possible.

What actually happens behind the scenes is the group filter generates an object where the keys are the string value of the SectionModel instance [1], and the values are the EntryModels associated with that section.

There are a few ways of achieving your desired outcome. Here's one possibility (not tested):

{% set sections = craft.sections.getAllSections() %}
{% set entries = craft.entries.section(['first', 'second', 'third']) %}
{% set grouped = entries|group('section') %}

{% for sectionHandle, sectionEntries in grouped %}
    {% set groupSection = false %}
    {% for section in sections %}
        {% if section.name == sectionHandle %}
            {% set groupSection = section %}
        {% endif %}
    {% endfor %}

    {% if groupSection %}
        <h1>{{ groupSection.name }} - {{ groupSection.handle }}</h1>
    {% endif %}
{% endfor %}

[1]: The SectionModel::__toString method returns the name attribute.


Prevent ALL templates from being rendered directly?

This is very, very simple to do... just put every single template into a specific folder, then prefix the folder with an underscore:

/craft/templates/_everything/

Template files beginning with underscores are hidden, similarly, template folders beginning with an underscore hide everything inside.


How to use different date format in front end form?

For me, saving dates as a timestamp seems to do the trick to correctly save to the date field in Craft. You can still use the datepicker and have the human readable date, just use the altField and altFormat options (https://jqueryui.com/datepicker/#alt-field) to fill a hidden field with the timestamp that gets submitted.


Is there a way to purge old data in a table?

Your interpretation of why you're getting duplicate columns when looping your  your table field is wrong.

Each column is always accessible via a automatically generated column handle (col1, col2, etc.), so that's not old and left over column names you're seeing there.

If you define custom handles in the Table field's settings, the cells' data will  be available via those properties as well, so you can then access the value via both row.price and row.col1. This feature is designed with direct access of columns / cells in mind, but gets in the way when looping through a row's cells.

But Craft doesn't force you to define custom handles, so you can simply remove them in your field's settings and your "old data" will be removed.


I believe the correct syntax you're looking for is:

{{ node.logo.enclosure['@url'] }}

As a handy hint, you can use the following to inspect any element parsed by Feed Me to see how you can access it. Using {{ dump(node.logo.enclosure) }} for example, you can see an array with keys and values for you to access.

This is also outlined in the documentation - https://verbb.io/craft-plugins/feed-me/docs/feature-tour/using-in-your-templates#example-template-code

EDIT: Refer to full code below

{% set params = {
    url: 'http://feedme.craft.dev/stack.xml',
    type: 'xml',
    element: 'node',
} %}

{% for node in craft.feedme.feed(params) %}
    <pre>
        {{ dump(node) }}
    </pre>
{% endfor %}

Produces:

array(2) {
  ["title"]=>
  string(23) "Test Org - Pleae Ignore"
  ["logo"]=>
  array(1) {
    ["enclosure"]=>
    array(3) {
      ["@url"]=>
      string(60) "https://www.mydomain.org/sites/default/files/louis%20xiv.jpg"
      ["@length"]=>
      string(5) "71280"
      ["@type"]=>
      string(10) "image/jpeg"
    }
  }
}

EDIT 2: Looking at your exact feed, I can see the issue. Your feed actually has HTML, which is interpreted as a string. For instance:

<logo>
    &lt;enclosure url="https://www.mydomain.com/1959440_10152076927213038_3915913252856478361_n%5B1%5D_2.jpg" length="35608" type="image/jpeg" /&gt;
</logo>

Notice how is your original example its assumed its all XML? In fact, the logo node contains HTML, which is just treated like a string.


Not currently - we should probably add a new 'translate' argument to that function which would let you opt out of it.

However Craft’s DateTime variables have a rfc1123() function which can be used instead of the Twig |date filter, and its output won’t get automatically translated.

For reference, RFC 1123 defines the date/time specification recommended by RFC 2616 (HTTP 1.1) (which is the same as the one defined by RFC 822, just with 4-digit years).

{{ expiry.rfc1123() }}


Creating Drafts from Frontend Entry Form

Yes. Kind of. Have you had a look at the Entry Form example in the docs? It looks like Craft will always need an entry to begin with, so you can't have a draft version without first creating a normal entry. That being said, you can set this new this entry as disabled, which also can be queried against by using

craft.entries.status('disabled')
I cannot see a documented example on how to list drafted entries, though.

Either way, to build on the Entry Form example, instead of having the action set to entries/saveEntry, change it to entryRevisions/saveDraft.

Something like:

<input type="hidden" name="action" value="entryRevisions/saveDraft">
<input type="hidden" name="redirect" value="draft?id={id}&draft={draftId}">
<input type="hidden" name="sectionId" value="1" />
<input type="hidden" name="enabled" value="0">

Note that you're now posting to the EntryRevisionsController@saveDraft, and that I here set entries to be disabled by default.

Does having them disabled help you do what you want, or do they have to be in Draft mode for some reason?


What about including only those fields you want to search:

craft.entries.section('products').search('productName:'~ query ~' OR productDescription:'~ query ~' OR productFoo:'~ query ~' OR productBar:'~ query)

Yep I know, it's not very nice (but hey, it was you who started listing them). What you could do to make this ugly thing of a search parameter more manageable is to add the conditions programmatically:

{% set searchFields = [
    'productName',
    'productDescription',
    'productFoo',
    'productBar',
] %}

{% set searchParam %}

    {% for searchField in searchFields %}
        {{- not loop.first ? 'OR' -}}
        {{- searchField ~ ':' ~ query -}}
    {% endfor %}

{% endset %}

{% set results = craft.entries.section('products').search(searchParam|trim) %}


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

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


How to upload an asset from a front-end form

That's not going to be possible to do without a plugin.

The plugin would need to provide its own Controller which does the following:

Grab info about the uploaded file using UploadedFile::getInstanceByName()
Determine the ID of the asset folder you want to upload the file to using AssetsService::findFolder()
Move the file into the folder using AssetsService::insertFileByLocalPath()

Here’s an example:

// Get info about the uploaded file
$file = UploadedFile::getInstanceByName('fileInputName');
$tempFilePath = $file->getTempName();

// Find the target folder
$folder = craft()->assets->findFolder(array(
    'sourceId' => 1,
    'path'     => 'subfolder/path/'
));

// Turn the file into a managed asset within that folder
craft()->assets->insertFileByLocalPath(
    $tempFilePath,
    $file->getName(),
    $folder->id,
    AssetConflictResolution::KeepBoth);


First steps with Craft Nitro: hostname not working after successful install

After cheating via discussion in the comments, I’m certain that your DB_SERVER is incorrectly set to 127.0.0.1 when it should in fact be mysql-5.6-3306.database.nitro.
In a perfect world this would have been set for you when Nitro edited .env, but in a pandemic-ravaged dystopia your next best option is to use whatever nitro context indicates.


Figured it out. I assumed that my assets folder needed 744 just as the install instructions mentioned for the Craft CMS folders. But, I need to change the permissions to the standard 755. Once I did, I could view the images.


I needed to extend BaseApplicationComponent in my Services file:

class SiteUtilitiesService extends BaseApplicationComponent
{
    public function videoEmbed($url, $color)
    {
        // some code to get the video ID from the $url variable

        return "<some html embed code with the $color variable>";
    }
}


Internal Server Error Regarding ElementHelper.php.bak

At what point of the update process are you getting that error?

During an auto-update, Craft creates a .bak of every file that it's about to update before doing the file swap.  The last step of the update process (the "Cleaning up..." stage), goes through and deletes all of the .bak files that were created.

I'd also make sure that the craft/app folder is recursively writable by PHP as that's required for auto-updating to work correctly.


What is the correct syntax to check if a user has permission?

The correct permission key is:

commerce-manageProductType:ID_OF_PRODUCT_TYPE_HERE

for example:

commerce-manageProductType:2

Your code would therefore be:

$user = craft()->userSession->getUser();
if (craft()->userPermissions->doesUserHavePermission($user->id, 'commerce-manageProductType:2')) {
    // yes permission
} else {
  // no permission
};

Or you could use UserSessionService::checkPermission(), which is a shortcut for checking permissions for the currently logged-in user:

if (craft()->userSession->checkPermission('commerce-manageProductType:2')


There is a general config setting for it:

'limitAutoSlugsToAscii' => true,

  Limits automatically-generated slugs to ASCII characters. When there
  is an obvious ASCII version of a character (e.g. ñ → n), it will be
  used. Other characters will be removed.


Display something in the middle of post content

There is a loop variable available within your for loop which you might be able to use here (docs). This starts at 1 and increment everytime your loop runs.
Using loop.index, you could do something like this:
{# Fetch your blocks #}
{% set blocks = entry.postContent.all() %}

{# Counts how many blocks you have in total #}
{% set totalBlocks = blocks | length %}

{# Divide your totalBlocks by 2 and round to find the 50% mark in your content #}
{% set halfwayPoint = (totalBlocks / 2) | round %}

{% for block in blocks %}

        {# When you reach halfway, inject #}
        {% if loop.index == halfwayPoint %}
            the content you want to inject
        {% endif %}

        {% if block.type == 'text' %}
            {{ block.text }}
        {% elseif block.type == 'image' %}
            {% for image in block.image.all() %}

            {% endfor %}
        {% endif %}

{% endfor %}


Is it possible to exclude entry from search index?

If you mean entire sections you could define which sections you want to display like this:

{% set query = craft.request.getParam('q') %}
{% set entries = craft.entries.craft.entries.section('section1, section2, section3').search(query).order('score') %}


What's the proper way to create a plugin that uses and extends another author’s plugin?

Adding a new provider to OAuth plugin is a two step process:

Create a new OAuth Provider in lusitanian/oauth

The first thing you will need to do is to add support for your new provider in lusitanian/oauth OAuth PHP library. This library supports OAuth 1 & 2 authentication process and you can give a look at other providers to get started writing your own.

It's usually just a matter of a smart copy paste from the code of another provider, by updating:

scopes
api URL
authorize URL
access token URL

That said, you will sometimes have to adjust your code to support the web service's specificities. Read your web service's authentication docs carefully to understand who they implemented the OAuth authentication process and you'll be good to go.

So when your provider is ready, just perform a pull request on lusitanian/oauth OAuth PHP library on GitHub. We're pulling new providers on each Craft OAuth plugin release, so if you got your provider added to lusitanian's library, it will be automatically part of Craft OAuth plugin's next release.

At this point, if you add your new provider in Craft OAuth plugin code, it won't do anything and the provider will not appear in OAuth settings.

Create an OAuth Provider Source in the OAuth plugin

The OAuth provider source class makes the link between between your lusitanian/oauth provider and Craft OAuth plugin.

You should name the file based on lusitanian/oauth's provider file name and save it under oauth/providers folder. For example if your lusitanian/oauth provider file is named Vimeo.php, you provider source file will be named VimeoOAuthProviderSource.php.

This file contains basic informations like the web service's console url, its name, and a simple getAccount() method, and when you'll add it to the oauth/providers folder, your provider will start showing up in Craft CP / Plugin / OAuth.

When you're ready with your OAuth provider source, perform a pull request on Craft OAuth plugin on GitHub and your provider will be added to the next release.

Checking that OAuth plugin is installed

In your plugin, you will have to check that OAuth is installed before using it in order to prevent errors.

In your templates you can check that OAuth plugin is installed this way:

{% if craft.oauth is not defined %}

    {# OAuth plugin not installed #}

{% else %}

    {# OAuth installed #}

{% endif %}

In your php files, you could do it that way:

$plugin = craft()->plugins->getPlugin('oauth');

if(!$plugin)
{
    // oauth is not installed
}
else
{
    // oauth is installed
}

I'm working on a step by step guide on how to implement OAuth in Craft plugin. It's not ready yet but I'll probably start to share the draft in August.


Error when creating a Matrix or installing a Plugin

Check to make sure you have a db.php file here:

craft/app/etc/config/defaults/db.php

If not, download a fresh copy of the version of Craft you're using and replace your craft/app folder with the one you just downloaded.

This file was missing in my Git repository because the .gitignore file was set to ignore all db.php files on the site, not just the one they wanted to ignore in the craft/config folder.

If that's the case, make sure your .gitignore file uses a line like this:

craft/config/db.php

Instead of just this:

db.php


There is no inherent order or hierarchy between entries in a Channel section.  You could do some janky things to simulate it, but you're probably much better off converting that workArticles section to a Structured section, which does give its entries both order and hierarchy.

In your case, you might not care about the hierarchy, so you can just keep everything on the first level.

Very similar to https://craftcms.com/support/structure-nav#top-level-only

Then, if you need to, you can take advantage of all of the structure specific properties of EntryModel, (level, parent, nextSibling, etc.)


craft.fields.getFieldByHandle() has been deprecated

You should replace craft.fields.getFieldbyHandle() with craft.app.fields.getFieldByHandle() (note the additional .app in there).