Twig default filter or Null coalescing operator?

So things like the null coalescing operator make it easier to chain things, so if you have a number things to fall back on, it's much neater to write:

{% set woof = bark ?? howl ?? growl ?? null %}

...then it is to try to chain that all together with | default filters.

Something that may also make your life easier is the Empty Coalesce plugin, so you can write:

{% set woof = bark ??? howl ??? growl ??? null %}

What's the difference? The null coalescing operator only checks for null, so if bark = "" then it'll choose that, which often isn't what you want for strings.

There's a writeup of all of this in Handling Errors Gracefully in Craft CMS article.

Now, what is going to be more performant? Unless you're in a massive inner loop (which we should avoid in a templating language anyway), I doubt it'll matter. PHP code is transpiled to a bytecode that often gets cached, it should be fine.

But in theory, ?? and ??? are probably marginally faster.


How to use Sprig to filter categories using a dropdown

You need to apply the category filter to the entry query.
{% set categoryId = categoryId ?? '' %}

{% set entryQuery = craft.entries ({
    search: search,
    section: 'articles',
    limit: 2,
    relatedTo: categoryId,
}) %}

And also update the select field to match.
    <select sprig name="categoryId" id="category" value="{{ categoryId }}" class="form-control">
      <option value="">Any</option>
        {% for category in categories %}
          <option value="{{ category.id }}">{{ category }}</option>
        {% endfor %}
    </select>

See the Sprig filters video for an explanation of how to output and use categories as filters:
https://putyourlightson.com/sprig/videos#sprig-filters


Updating a Lightswitch value not working via front-end

After taking a look at this it would seem like the lightswitch is always expecting a value to determine the whether the state should be on or off. When you don’t check the input and submit the form, the field doesn’t get sent to the controller, the value isn’t replaced (as there technically is no value) and nothing updates.

You can work around this by providing a hidden input above the checkbox with a constant value of 0, so when the form submits and there is no check in the checkbox a 0 will get sent and will override the 1 if already set. If you check the input then the hidden field will be overwritten when the form submits and will update the field value of 0 to a 1 and will light the switch.

I hope that makes sense.

Here is the complete form I have used with success:

<form method="post" accept-charset="UTF-8">
 {{ getCsrfInput() }}
 <input type="hidden" name="action" value="users/saveUser">
 <input type="hidden" name="redirect" value="/">
 <input type="hidden" name="userId" value="{{ currentUser.id }}">

 <label>Some Option</label>
 <label>
  <input type="hidden" name="fields[membersAvailableForCollaborativeProjects]" value="0">
  <input type="checkbox" name="fields[membersAvailableForCollaborativeProjects]" value="1" {% if user.membersAvailableForCollaborativeProjects %}checked{% endif %} /> Yes
 </label>
 <input type="submit" value="Save user">
</form>


For anyone experiencing this issue, what solved it for me was deleting the craft application files and replacing them with the ones from the same release version.

It seems one of them got corrupted at some point.


Twig's is not null should work for this.  Assuming you have a Number field called number and you're looping through a list of entries, something like this should work:

{% for entry in entries if entry.number is not null %}
    {{ entry.number }}
{% endfor %}


Output assets within a matrix block using Imager and Craft 3

Why do you try to fetch the image multiple times?

{% for image in block.logos %}

Will fetch the images and loop them {% set logo = block.logos.first() %} -> will fetch the first image again and again.

{% for image in block.logos %}
    {% set transformedImage = craft.imager.transformImage(image, { width: 312, height: 200, }) %}
    <img src="{{ transformedImage.url }}" alt="{{ logo.title }}">
{% endfor %}

Should work


Is it possible to duplicate an entry as not enabled/live from the listing page?

After talking with Brad in Craft support we came up with this to put in a plugin. So now when duplicate is called in the admin (list of entries) it is set to disabled. Normal Craft behaviour is to keep the same state as the entry being copied.
//if duplicate sets entry to not live / disabled
Event::on(
    Entry::class,
    Entry::EVENT_BEFORE_SAVE,
    function(ModelEvent $event) {
        $entry = $event->sender;

        // If the Entry is being duplicated.
        if ($entry->duplicateOf) {

            $entry->enabled = false;//set to disabled

        }
});


Is there an accessible callback when Craft.EntryTypeSwitcher is run?

It looks like the only easy way to hook into that event is to listen for the resize event on the window object. On line 55, it is triggering resize(). Looking at the code, I am not seeing any other way to listen for these events. The Craft.EntryTypeSwitcher class is getting instantiated but not getting assigned to any variable in the DOM, so there is no way for you to grab that instance (from what I can tell anyway).


How do I get started with Element API plugin?

There was a mistake in the ajax request. I was using POST instead of GET.

1) After installing the plugin :

Add elementapi.php to your /craft/config/ directory

<?php
namespace Craft;

return [
    'endpoints' => [
        'news.json' => [
            'elementType' => 'Entry',
            'criteria' => ['section' => 'news'],
            'transformer' => function (EntryModel $entry) {
                return [
                    'title' => $entry->title,
                    'url' => $entry->url,
                    'jsonUrl' => UrlHelper::getUrl("news/{$entry->id}.json")
                ];
            },
        ]
    ]
];

2) Enabling CSRF Protection
add 'enableCsrfProtection' => true to your /craft/config/general.php file.

3) getting entries with ajax :

_layout.html

<head>
...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js">  </script>
<script>
    $(function () {
        var data = {
            'CSRF': $('input[name="CRAFT_CSRF_TOKEN"]').val(),
        };

        $.ajax({
            'type': 'get',
            'contentType': 'application/x-www-form-urlencoded; charset=UTF-8',
            'cache': false,
            'data': data,
            'url': 'news.json',
            'dataType': 'json',
            'timeout': 50000
        }).done(function (response) {
            console.log(response);
        }).fail(function (error) {
                // Total fail.
            });
        });
    </script>
</head>

<body>
<div id="container">
    {{ getCsrfInput() }}
...


Is there a master list of user/group permissions in Craft?

You can go straight to the source and get the list from UserPermissionsService->getAllPermissions().


You should be able to foreach the $block->product to include the selected values (or labels)

return [
    'endpoints' => [
        'fullmembers.json' => [
            'elementType' => 'User',
            'criteria' => ['group' => 'fullMembership'],
            'transformer' => function(UserModel $member) {
                $businessCategories = [];
                foreach ($member->businessTypeMarketSectorsProducts as $block) {
                    switch ($block->type->handle) {
                        case 'businessCategory':
                            $productValues = [];
                            foreach ($block->product as $product) {
                                if ($product->selected) {
                                    $productValues[] = $product->value;
                                }
                            }
                            $businessCategories[] = [
                                'businessType' => $block->businessType->value,
                                'marketSector' => $block->marketSector->value,
                                'product' => $productValues
                            ];
                            break;
                    }
                }

                return [
                    'id' => $member->id,
                    'companyName' => $member->membersCompanyName,
                    'addressLine3' => $member->membersAddressLine3,
                    'lat' => $member->memberAddress->lat,
                    'lng' => $member->memberAddress->lng,
                    'email' => $member->email,
                    // 'profile' => '/directory/company/' . $member->id . '/' . $member->username,
                    'profile' => UrlHelper::getUrl("directory/company/{$member->id}"),
                    'businessCategories' => $businessCategories
                ];
            },
        ]
    ]
];


Reverse Related entries with Matrix Field

Have a look at the 'Going Through Matrix' section at http://buildwithcraft.com/docs/relations#the-relatedTo-param.

{% set articles = craft.entries.section('articles').relatedTo({
    targetElement: entry,
    field: 'myMatrixField.fieldHandle'
}) %}


Related Projects (Structure + Channel Matrix)

I'm not sure I totally understand your issue, but give this a try in your Matrix code:

{% for entry in block.projectsEntry %}
    {{ entry[0].title }}
{% endfor %}

Are you getting any errors in devMode?


Read the article about ElementCriteriaModel in the official Craft docs on how to query elements in general and the craft.entries article about the parameter available to modify the object returned.

Example code for your events list:

{# Get events from `events` section in desired sort order #}
{% set allEvents = craft.entries.section('events').order('eventsDate desc') %}

{# Loop through `allEvents` and print the list #}
{% for event in allEvents %}
    <li>{{ event.eventsHeadline }} - {{ event.eventsDate|date('F j, Y') }}</li>
{% endfor %}


Best practices to name Matrix fields handles

They only have to be unique within a block. So your initial idea would be best.
Have a galleryMatrix field with an image block and fields with handles imgName and imgAsset.

I don't think you can use name as a handle, because it is a reserved word. Asset may be possible.


Smart Map - Static map displays fine, dynamic map does not

It's possible that some other JavaScript (unrelated to Smart Map) is throwing an error, and Smart Map never gets a chance to load the map. Loading a dynamic map is intentionally done late in the process, so that it doesn't interfere with other scripts you may have running.

The static map is simply loaded as a remote image, so no JavaScript is required.


You'll have to resubmit all the previous comments through hidden fields aswell. You can achieve this by looping through them in the form, and put their values into hidden inputs.


It sounds like you just want a cron job to trigger an action on your plugin.

You don't need to register any Control Panel routes for that, so you can remove your registerCpRoutes method.

If your plugin only has one controller, the URL your cron job would want to target would be:

yoursite.com/actions/pluginHandle/pluginAction

You can read more about it in the plugin controller docs.


As aberkie pointed out, Craft does use them internally, but they should not be left blank or you'll potentially run into some issues now and you'll miss out on some upcoming features.

You can programmatically create one with StringHelper::UUID or use a site like guidgen.com to get a valid one.


Complex logic on a ElementCriteriaModel parameter?

For most cases you can find a workaround to combine such conditions by making use of entry IDs.

In this example query for each time range condition separately and use the ids() method to "collect" your entries. All matching IDs are then passed to the id parameter of a final ElementCriteriaModel, that you can also use for Pagination etc.:

{% set dateParamCw32 = 'and, >= 2014-08-04 00:00:00, < 2014-08-10 23:59:59' %}
{% set dateParamCw34 = 'and, >= 2014-08-18 00:00:00, < 2014-08-24 23:59:59' %}

{% set entriesCw32Ids = craft.entries.section('events').startDate(dateParamCw32).ids() %}
{% set entriesCw34Ids = craft.entries.section('events').startDate(dateParamCw34).ids() %}

{% set idParam = [] %}
{% set idParam = idParam|merge(entriesCw32Ids) %}
{% set idParam = idParam|merge(entriesCw34Ids) %}

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


Let's start by saying what you don't want to do...

Don't use Matrix blocks to solve this problem. As you mentioned, the Matrix field would get pretty crazy, and you couldn't prevent people from using the wrong blocks in the wrong places.
Don't use Globals to solve this problem. Global fields get loaded automatically on every page load, so you're bound to add a lot of extra page weight that doesn't need to be there.

To me, it sounds like entry types are the perfect solution. You can have multiple entry types which all share the same fields, but then add extra fields to the entry types which specifically need them. If I'm understanding your problem correctly, it's exactly what entry types were created to solve.

Lastly, you mention needing a Channel or Structure to use entry types. This is 100% true, but it makes me wonder... Are you currently just using Singles? If you're currently using Singles, then this should be a non-issue... each Single is basically a unique entry type anyway.

Hope that helps, let me know if I missed something.


Looping through a Global with a Matrix field

{% for block in contactDetails.myMatrixField %}
     {{ block.location }}
     {{ block.address }}
{% endfor %}


How to prevent non-image files being uploaded through frontend form?

There is a checkbox when creating/editing the field labeled "Restrict allowed file types?" Checking it will reveal file type options.

Source: https://craftcms.com/docs/assets-fields


How can I use a where statement with grouped results?

This should work:

{% for date, attempt in attempts|group('attemptDate') %}

      {{ date|date('M j, Y') }}
      {% for entry in attempt %}
        {% if entry.type == 'My Type' %}
          {{ entry.title }}
        {% endif %}
      {% endfor %}

  {% endfor %}

I don't know what you want to do with the 'type' of the entries?
But if you want to limit the amount of entries according to type, you could fill "attempts" with all entries of a certain type first and then loop through them. Than you could remove the if statement which would be cleaner and faster I think.

So something like:

{% set attempts = craft.entries.section("something").type("My Type").find() %}


Is it possible to load different "_layouts" with ajax for certain pages?

Although you couldn't necessarily change the _layout template with the method given in that tutorial. What you could do is include the header
the templates rather than in the _layout.html for each template and change it out with another header in the other specific URLs. Or hide it, or whatever you like.


Craft Commerce 1 - Stripe payment intents SCA ready

The Stripe for Commerce 2 plugin has been updated for SCA, but I'm not aware of any solution for Commerce 1.

https://github.com/craftcms/commerce-stripe


How to return a hierarchical list of a site's page structure using the ElementAPI

It's not the most economical way to do it but if you don't have hundreds and hundreds of entries, this should do it for you:

<?php
use craft\elements\Entry;

function getChildren($entries){
    foreach ($entries as $entry) {
        $items[] = [
            'title' => $entry->title,
            'slug' => $entry->slug,
            'level' => $entry->level,
            'children' => $entry->hasDescendants ? getChildren($entry->children) : false
        ];
    }
    return $items;
}

return [
    'endpoints' => [
        'structure.json' => function() {
            return [
                'elementType' => Entry::class,
                'criteria' => [
                    'section' => 'structure',
                    'orderBy' => 'lft asc',
                    'level' => 1,
                    'limit' => null
                ],
                'transformer' => function(Entry $entry) {
                    return [
                        'title' => $entry->title,
                        'slug' => $entry->slug,
                        'level' => $entry->level,
                        'children' => $entry->hasDescendants ? getChildren($entry->children) : false
                    ];
                },
            ];
        }
    ]
];


How can I iterate more than 100 blocks in a matrix field for loop?

The default limit for fetching elements is 100. This can be overridden as follows:

{% for block in entry.myMatrixField.limit(null) %}

Passing in null will grab all of them, or you can pass in the specific number you want:

{% for block in entry.myMatrixField.limit(400) %}


Setting Variable to Specific Category for Loop

You can't just pass a string (category name) to relatedTo, you need to pass either the category ID or a complete CategoryModel (you can also pass in an array of IDs or models, if there are several categories).

This means that unless you already have the category IDs or models, you'll need to pull them using the craft.categories service:

{% set management = craft.categories.title('Management').first() %}

{% set staff = craft.entries.section('staffList').relatedTo(management) %}

If you have multiple category groups, and there's a possibility you might have multiple categories across those groups with the same name, you should specify which category group the category you're after belongs to:

{% set management = craft.categories.group('categoryGroupHandle').title('Management').first() %}

If you want to pull staff entries related to multiple categories, here's how that could look:

{% set categories = craft.categories.title(['Management', 'Staff', 'Other']) %}

{% set staff = craft.entries.section('staffList').relatedTo(categories) %}

Finally, keep in mind that if you're rendering a category template (i.e. you've requested the URL for a particular category and Craft has rendered the template specified in the relevant category group's template path), Craft will actually populate the template with a category variable, containing the CategoryModel for the category belonging to that URL. If this is the case, then you simply need to do this:

{% set staff = craft.entries.section('staffList').relatedTo(category) %}


Multi-lingual site with a splash (language select screen) at the root

Well that was easy enough.  I had forgotten about the .htaccess in the subfolders.  Everything works like planned now.

public/.htaccess (redirects to craft.php if file doesn't exist)
public/index.html (splash page)
public/craft.php (craft loader)

public/fr/.htaccess (redirects to index.php if file doesn't exist)
public/fr/index.php (craft loader for french)

public/en/.htaccess (redirects to index.php if file doesn't exist)
public/en/index.php (craft loader for english)


Automatically link to entries in rich text field

How about just using the replace filter?

{% set text = "A small boat surrounds a large island" %}

{% set linkedText = text|replace({'boat':'boat'}) %}

{{ linkedText|raw }}

That works.

In theory there should be a way to do it with parseRefs too, but my initial test isn't working yet:

{% set refText = text|replace({'boat':'boat'}) %}

{% set parsedText = refText|parseRefs %}

{{ parsedText|raw }}

...which leaves an unparsed ref tag so far.  I'm just heading out of the office but maybe a simple replace as above is enough to get you started?


Hiding Craft's admin folder from search engines

So if you follow best practices, the craft/ directory should just never be in the public webserver root to begin with. And if it's not, search engines are never going to be able to index it.

The directories should look like this:

craft/
public/

In other words, they are side by site, and Craft is never in the public web server directory.

Craft's admin/login pages already send the x-robots-tag: none header, which tells Google et al not to index those pages. Learn More

I've created many Craft sites over the years, all of them still have /admin as the AdminCP trigger, none of them have been indexed, and I've had zero security breaches/brute force attacks.

Doesn't mean it can't/won't happen, but security through obscurity only gets you so far anyway. Put the craft/ directory out of the public root, follow the advice in Hardening Craft CMS Permissions and sleep well at night.


Can I accept file uploads using the Craft Guest Entries plugin?

I don't see why you couldn't. That is unless the plugin needs to handle file uploads as opposed to the Entry Model.

Take a look at Uploading Files from a Form. You need to make sure your form tag has enctype="multipart/form-data" and your file input has the name fields[fieldHandle]. If you are uploading multiple files, be sure to add and extra set of square braces on the end (fields[fieldHandle][]).


Should I use cache on data pulled from within files?

The reason the documentation says to not use it on static text, is because the cached data will be saved to the database, resulting in at least one database query (two if it needs to update the data). Database operations are fairly expensive compared to reading data from files, or running twig code, so in many cases this will be slower than just showing the text.

There's no way to know for sure if using cache tags will be a benefit in your case. It really depends on what exactly you're doing in your template, and where the bottlenecks in your system is. The only way to know, is to test it. If you turn devMode on, reload your page with and without the cache tag enabled, you could compare the response time shown in the console output under "Profiling Summary Report". This is the time it takes for the page to be generated by craft, which is what you'd seek to minimize using the cache tag. Remember to reload the page several times for each case and do an average, since the result will vary somewhat depending on what else is going on in your system.


Filter users: in group AND not in other group

I'm not sure you can do that in one query only but I might be wrong.
Using two queries, you could do the following:
{# Fetch users from the group you don't want to display #}
{% set usersToExclude = craft
    .users()
    .group('groupToExclude')
    .ids() %}

{# Built an array of ids to exclude adding 'not' to it #}
{% set exclusionList = ['not'] | merge(usersToExclude) %}

{# Fetch your users excluding the one found above #}
{% set vnp_users = craft
    .users()
    .group('groupYouWant')
    .id(exclusionList).all() %}


You could put them in a supertable which supports nested matrix etc.
All the info on supertable here: https://github.com/engram-design/SuperTable


Welcome to craft

There is a example for a archive on the official page: Creating an “Archive” Page for Entries

Maybe this fit's your use case.


Craft site moved to HTTPS - now having issues

So, removing the cache settings for CSS and JS in my nginx config fixed this.

# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1M;
access_log off;
}

Also, once I got into the admin area after adjusting this, resaving certain entries flushed the twig template cache.


Sounds like it might be a form submission issue rather than anything related to password /profile updates. I ran into a similar issue a while ago and resolved it by disabling Suhosin on my server. May want to give that a try.


Set User's entry field from PHP in Craft 3

By checking vendor/craftcms/cms/src/controllers/UsersController.php and vendor/craftcms/cms/src/base/Element.php, I did it like

$user->setFieldValue('products', $userProductsIds);
return Craft::$app->getElements()->saveElement($user, false);


Can Craft be used with conjunction with other framework (Laravel)?

Craft is frequently used in conjunction with other applications/frameworks. How one would use Craft in coordination with a Laravel application, and whether that would be a good idea, depends on the specific use case.

Most frequently, Craft is used to serve a content API that other applications can consume. Additionally, it's common to se Laravel apps used as an intermediary to broker integrations between the CMS and external services.

In any case, there's no specific technical limitation that would prevent Craft and Laravel from being used in conjunction with each other.


Trouble updating Craft when allowAdminChanges is false

I think P&T changed the behavior in Craft 3.1.0 and newer. Before 3.1.0 it was necessary to have 'allowAdminChanges' => true with updates in/to project config.

To be 100% safe you can always enable admin changes when you're on the command line by using this config line in production environments:

'allowAdminChanges' => (php_sapi_name() === 'cli'),

In most installations we even go step further and have a second domain available, which also enables admin changes when this domain is used (for emergencies), where www.yourdomain.com would be the normal domain and admin.yourdomain.com could be the one with admin changes enabled:

'allowAdminChanges' => (php_sapi_name() === 'cli' || strpos($_SERVER['HTTP_HOST'], 'admin.') !== false),


Collecting total charge value where custom field is X

Can you not do:

{% set total = 0 %}

{% for charge in charges %}
    {% set total = total + charge.amount %}
{% endfor %}

{{ total|number_format(2, '.', ',') }}

?


You get all available options with the options attribute and you can check if it is selected in the current entry with option.selected

{% for opt in entry.checkboxFieldHandle.options %}
    <label>
        <input name="fields[checkboxFieldHandle][]" type="checkbox" value="{{ opt.value }}">
        {{ opt.label }}
        {% if opt.selected %}
            I'm selected
        {% endif %}
    </label>
{% endfor %}

But to be honest I don't really understand your second part

Edit
As for your field, you can display all the selected values with

{% set selectedUserGroupIds = entry.userGroupFieldHandle %}
{% for userGroupId in selectedUserGroupIds %}
    {% set usergroup = craft.userGroups.getGroupById(userGroupId) %}
    {{ usergroup.name }}
{% endfor %}


Controllers in Craft 3/Commerce 2 use kebab-case (update-cart) instead of camelCase (updateCate), so adjusting that may correct your issue.

The common convention for forms processed by Craft controllers is to leave the action attribute blank and include a hidden input with the action URL value.

In your example, you can do that by adding an action param to your form_data variable

var form_data = $(this).serializeArray();
// Add the action URL to the submitted data
form_data['action'] = 'commerce/cart/update-cart'

Then you'd leave the url param on your Ajax request blank. The updated code would look something like this if you add the action in your script.

$('.add-to-cart').on('submit', function(e) {
    console.log('add to cart clicked');
    e.preventDefault();
    var form_data = $(this).serializeArray();
    // Add the action URL to the submitted data
    form_data['action'] = 'commerce/cart/update-cart'
    console.log(form_data);
    $.ajax({
        type: 'post',
        url: '',
        data: form_data,
        dataType: 'json',
        success: function(response) {
            if (response.success) {
                console.log('success');
                console.log(response.success);
                $(this).hide();
                $(this).siblings('.success').show();
                $(this).parents('.hover-cart').fadeOut();
            }
            else {
                console.log('error');
                console.log(response.success);
                alert("We're sorry, but the item couldn't be added to your cart. Please try again. If the issue persists, please send an email to webmaster@lwf.org");
            }
        }
    });
});

or the snippet below if you want to add the action to your Twig template

<form method="POST" class="add-to-cart">
    {{ csrfInput() }}
    {# Include the controller used to process the form  #}
    <input type="hidden" name="action" value="commerce/cart/update-cart">
    <input type="hidden" name="purchasableId" value="{{ variant.id }}">
    <input type="number" name="qty" value="1">
    <button type="submit"><i class="fas fa-shopping-cart"></i></button>
</form>

If you use the Twig code above, make sure you update the url property of your Ajax request to remove the existing URL.


Recommendations for Form Integrations with Salesforce?

Sprout Forms it's a good option for your requirements, using the Payload Forwarding you'll be able to send the data to an endpoint on salesforce, you can also use the Hidden Field or the Invisible Field from the Sprout Fields plugin to setup any pre-filled API-keys if required.


Entries from previous year 2016 not displaying

Sounds like your search indexes are corrupted. I’d try to run the “Rebuild Search Indexes” tool in CP > Settings.


I used to have the same issue then I started using Eager Loading Element. It allows you to speed up your craft cms templates by fetching matrix from the database more efficiently.

You can refer to this link which might help you for further understanding of Eager Loading.


Recommended way to include API keys in a multi-environment configuration?

You can insert whatever settings you need either in your config/general.php file, config/pluginhandle.php, or the plugin/config.php file.

You can have different value for your setting in each of the environments you have defined. If you call your setting myplugin_stripe_key, you'd put something like this in your config file:

return array(
    '*' => array(
      ...
    ),

    'local' => array(
        'myplugin_stripe_key' => 'sk_test_xxxxxxxxxxxxxxxxxx',
        ...
    ),

    'stage' => array(
        'myplugin_stripe_key' => 'sk_stage_xxxxxxxxxxxxxxxxxx',
        ...
    ),

    'prod' => array(
        'myplugin_stripe_key' => 'sk_prod_xxxxxxxxxxxxxxxxxx',
        ...
    )
);

In your plugin:

private function _saveStripeCustomer($account, $token = NULL)
{
  \Stripe::setApiKey(craft()->config->get('myplugin_stripe_key'));
  // ...
}

If you use this several places in your service, I'd fetch all settings in the constructor and save them in an array:

class MypluginService extends BaseApplicationComponent
{
    var $settings = array();

    public function __construct($urlRecord = null)
    {
        $this->settings = $this->_initSettings();
    }

    private function _initSettings()
    {
        $settings = array();
        $settings['myplugin_stripe_key'] = craft()->config->get('myplugin_stripe_key');
        return $settings;
    }
}

Then you can access them later like this:

\Stripe::setApiKey($this->settings['myplugin_stripe_key']);

If you need this key in javascript, you could create a craft variable that gets the config setting and output it to a javascript variable somewhere in your twig templates. You'd basically use the same way to get the config variable inside the craft variable method.


It depends to some extent whether the form is a login/registration form (to a user account) or just an information form, that you are saving in the database.

If the resources page is restricted and requires login, then you should place {% requireLogin %} at the top of your resources template. If anyone requests this page they will automatically be redirected to the login page, and after a successful login, will be routed back to the original request, which was the resources page. The login page can have a link to 'create account' with the additional fields you need, if they don't already have one.

If it is just an information form and not for login then you can simply create a form with the required fields. Submit the form using action/saveEntry along with a hidden field called redirect as explained here.

<form method="post" action="" accept-charset="UTF-8">
    <input type="hidden" name="action" value="entries/saveEntry">
    <input type="hidden" name="redirect" value="path/to/resources">
    ...
</form>

This is not secure however. If you need to make certain that they just don't go straight to the resources page without filling out the form, then you can test for the existence of an entry.id on the resources page (which is what is returned from a successful entries/saveEntry), and redirect them back to the form again if not defined. However, this would make the resources page viewable only once (unless you set a cookie or store session data, etc). And they would have to fill out the form every time they wanted to view it, which doesn't sound like what you want. In which case go with the login route above.

There are example templates for login, user registration, and others (more links at bottom left of those pages).


Custom Asset Source Types - not possible

Custom Asset sources are not possible in Craft 2 without some core hacking.

However totally doable in Craft 3 (even in the dev preview).  There is an example of how to pull it off here.


Amazon S3 Images not showing up in Assets

I'd recommend updating the asset indexes via Utilities → Asset Indexes (or Settings → Update Asset Indexes if you're on Craft 2) in your Control Panel.


If you can access the users table, change the admin email to yours, then just use the forgot password link


Getting to grips with errors and flash messages

It's hard to be more specific without some more context for each of the snippets you post, but:

{% if errors is defined %}

        {% for error in errors %}

{{ error }}

        {% endfor %}

{% endif %}

That's checking if a variable named errors has been passed into the template, and if so it assumes it's an array and loops through the array to display each error in an unordered list.

{% if errorMessage is defined %}

{{ errorMessage }}

{% endif %}

That's checking is a variable named errorMessage has been passed into the template and if so it assumes it's a string and just displays the single error message.

{% if model and model.getErrors('firstName') %}
   {{ model.getErrors('firstName')|join }}
{% endif %}

That's checking if a variable named model has been passed into the template and assumes it's an instance of BaseModel.  Then it calls the getErrors method of the class passing in firstName as a parameter to specifically check if any errors have been set on its firstName attribute.  If so, it will take that array of errors and display them using Twig's join filter.


I have built something similar so hopefully this will help get you rolling.

You are going to need to (or at least I would) create a plugin that you can call in your twig template. Pay special attention about creating a variable and services.

Let's say your plugin is called theater.
Let's also pretend you have a url "mysite.com/showtimes"

In your showtimes template, you would make a call to your theater plugin to get showtimes.

showtimes.twig

{# Get all upcoming showtimes from the api. #}
{% set showtimes = craft.theater.showtimes %}

I won't get into the specifics of building an entire plugin, but here are the main players you'll need.

First you'll need a file called TheaterVariable.php that has this:

TheaterVariable.php

<?php
namespace Craft;

class TheaterVariable
{
    /**
     * Get all upcoming showtimes from the api.
     *
     * @return mixed
     */
    public function showtimes()
    {
        $showtimes = craft()->theater->getShowTimes();

        return $results['data'];
    }
...

}

Now you'll need a file called TheaterService.php to do the real work of getting your showtimes.

TheaterService.php

<?php
namespace Craft;

class TheaterService extends BaseApplicationComponent
{
    /**
     * Get all upcoming showtimes from the api.
     *
     * @return \Guzzle\Http\EntityBodyInterface|string
     */
    public function getShowTimes()
    {
        try {
            error_log((craft()->config->get('devMode')) ? 'Getting all upcoming showtimes.' . '' : '');

            $client = new \Guzzle\Http\Client('http://your-api-domain.com/);
            $uri = 'api/api-endpoint';

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

            $request->addHeader('Accept', 'application/json');
            $request->addHeader('Authorization', 'YOUR CREDS');

            $response = $request->send();

            return $response->json();
        } catch
        (\Exception $e) {
            if (craft()->config->get('devMode')) {
                error_log('Error: Could not get upcoming showtimes.');
                error_log($e->getResponse()->getBody());
            }
        }

    ...

    }
}

Finally, back to your showtimes template:

showtimes.twig

{% if showtimes|length %}
    {% for showtime in showtimes %}
        ...
    {% endfor %}
{% else %}
    {# Could not find any upcoming showtimes for some reason. #}
{% endif %}

I am using oAuth to handle my authorization, so that part might be a bit different for you. But in the big picture this should be pretty close.

Hope this helps!


Craft Search looking for keywords contained in the query

From the docs:

{% set results = craft.entries.search('*' ~ query ~ '*').order('score') %}


Craft 3 Update

This is currently possible in Craft 3.  Install the AWS S3 plugin from the Plugin Store. Make sure you're running Craft Pro.  Create a new Asset Volume using S3.  Go to Settings->Users->Settings in the Control Panel and set the "User Photo Location" setting to your S3 volume.

Craft 2

Currently no, although this is likely to change in the upcoming Craft 3.


User registration form add user to User Group?

See the docs here:

https://craftcms.com/docs/users#public-registration

  Public Registration

  Craft Pro has the option of allowing public user registration. It is not enabled by default, though. To enable it, you must go to Settings → Users → Settings, and check the “Allow public registration?” setting. With that checked, you will also be given the ability to choose a default user group that publicly-registered users are assigned to.


What's the correct directory setup for Craft with Vanilla Forums?

Daryl,

I've setup a few sites with Craft and Vanilla Forums, I've always named the folder "Vanilla" to either "Community" or "Forum" so the url being "vanilla" isn't a problem. Also you should know you can do a javascript include into another craft template so the URL can be anything you want really. I've never had a .htaccess conflict though.

I've always run the forums under /forums or /community or something of the sort when not using the javascript iframe embed method at which point the url can be anything. I've never attempted using a sub-domain, just did not see the point.


The error shows up because Craft can’t find the zip php extension.
Try to install it by running pecl install zip in your terminal.


How can I show a list of product options as checkboxes in the cart?

{% if item.options.value is defined and item.options.value == 'on' %}


Is it possible to search an entry for specific table content

I don't believe this is possible using the native search functionality.

You can search for a specific value within a table field using:

{% set results = craft.entries.search('tableField:"ID"') %}

That should return all entries that contain "ID" in tableField, but it doesn't restrict the search to a particular column in the table.

How likely is it that the ID will occur in other columns of the table?


Craft is just passing on the error message it gets back when trying to connect with whatever settings it was given.

And we're using Mailgun ourselves (port 587) for sending emails on https://craftcms.com, which is a Craft install.

Make sure you're using smtp.mailgun.org as the host and that you've got the transport type set to TLS as well.


How to create a custom query inside a plugin in Craft?

Easy enough:

$query = craft()->elements->getCriteria(ElementType::Entry);
$query->section = 'mySection';
$results = $query->find();

You can obviously refine the query before calling find():

$query->limit   = null;
$query->myCustomField = 'value';


I think you're going to have to go up the chain to where the request starts - i.e. the server config. Rewrite all urls matching api?action=... to somewhere else. If you're using nginx it'd be something like

rewrite /api/$arg_action last;


Obtaining SEO metadata for a specific entry under Sprout SEO 3

I didn't understand Ben's answer, and I just needed to output an entry's SEO meta title with a fallback to the entry title in a for loop. Maybe this will help someone in a similar situation:

{% for entry in entries %}
    {% if entry.sproutSeoElementMetadata.title %}
        {% set title = entry.sproutSeoElementMetadata.title %}
    {% else %}
        {% set title = entry.title %}
    {% endif %}

{{ title }}

{% endfor %}

Just like entry.sproutSeoElementMetadata.title will grab the meta title of that entry, you can do the same with description: entry.sproutSeoElementMetadata.description

The meta image is a bit more complicated though:

{# get meta image's asset id #}
{% set imageId = entry.sproutSeoElementMetadata.ogImage %}

{# get the actual asset image by its id #}
{% set image = craft.assets.id(imageId).first %}

{# output the image's url #}
{{ image.url }}


How can I parse environment variables in a string?


includeJsFile links directly to the file. It doesn't try anything fancy, it just finds the file (exactly as you've referred to it) and creates a normal <script> tag for it.

includeJsResource looks for that file in a few different places. It could be a plugin resource, it could be a native Craft resource, etc.

Whenever possible, stick to includeJsFile.


Find one section in a for loop based on url

Just use {{ entry.title }} on your dynamic _entry template to output that specific title. You were looping through all the news entries again on the dynamic _entry template and listing each one. Any other custom fields on that page/section can be accessed simply by {{ entry.myCustomFieldName }}.


Removing index.php from URL even after .htaccess updates?

There's nothing unique or special for doing this with Craft CMS - you should be able to use this method for just about any web app running in a LAMP stack. See this post on removing index.php from the URL with Apache/mod_rewrite/.htaccess.

TLDR: add this bit to the end of your .htaccess file before the closing  tag.

If Craft's index.php is at the domain root, such as example.com/index.php, this should do it:

# Redirect to same url without index.php
RewriteCond %{THE_REQUEST} ^GET./index\.php(/.*)?$ [NC]
RewriteCond %{REQUEST_URI} !^/index\.php/admin\b [NC]
RewriteRule ^index\.php(/(.*))?$ /$2 [R=302,NE,L]

If it lives in a subfolder, do this instead:

# Redirect to same url without index.php
RewriteCond %{THE_REQUEST} ^GET./subfolder/index\.php(/.*)?$ [NC]
RewriteCond %{REQUEST_URI} !^/subfolder/index\.php/admin\b [NC]
RewriteRule ^subfolder/index\.php(/(.*))?$ /$2 [R=302,NE,L]

(In both examples, replace admin on the 2nd line with whatever your site’s CP trigger is set to, if you’ve changed it.)


Yes.

{{ 'this is a string'|replace('/this is a (string)/', 'This is another $1') }}

will output

This is another string

When you pass something that looks like a regex into the |replace filter,  preg_replace() is called behind the scenes, so |replace can do anything preg_replace() can do.


This doesn't work with Blubrry specifically, but you can use it to get the data into Google Analytics for things that aren't normally trackable: Instant Analytics

Check out the Tracking RSS Feeds section.


We used to use PhraseApp to manage Craft translations, but as of Craft 3, we don't anymore (everything is now on Github).

When we were using them, we wrote a quick PHP script that would take the PHP based translation file format Craft uses, parse them and convert them to a format that PhraseApp could use.  Then there was another quick script that would reverse the process when we pulled them down.


Logging in Every Minute - Craft Admin Sessions (on Heroku)

My own answer... for anyone interested, the Memcached extension in my composer.json file wasn't being picked up by Heroku on deploy. So I updated my composer.json file like this:

{
  "require": {
    "ext-mbstring": "*",
    "ext-imagick": "*",
    "ext-memcached": "*"
  }
}

Then ran composer update to update the composer.lock file.

When deploying to Heroku, the composer.lock file is very similar to the Gemfile.lock in Rails - telling Heroku exactly what to install with the deployment. Now the Memcached extension is being installed by Heroku and my session problem has been solved.


Instead of wrestling with artifacts and that whole approach I simply created a private repo on github, then set up a local satis that pulls in from that repo and then serves it to the installs as a composer repository when running composer update. Works like a charm.


Both of these work:

{% set sectionNamePages = craft.entries.section('sectionName').type('entryTypeA').level(1) %}
{% for page in sectionNamePages %}

{{ page.getLink() }}

{% endfor %}

{% set sectionNamePages = craft.entries.section('sectionName').level(1) %}
{% for page in sectionNamePages.type('entryTypeA') %}

{{ page.getLink() }}

{% endfor %}


How do I update the state of lightswitches reliably using checkboxes on a frontend user profile form?

While both lightswitches and checkboxes represent an on/off state, they are quite different. Standard browser behavior is designed to only post checkbox data if the checkbox is checked, otherwise it is ignored. In PHP, when processing the form, you would want to check whether the post-data is present or not; if it's present then the value is checked; otherwise it's unchecked.

Although I haven't reviewed how craft processes checkbox form submissions I would assume that it also performs this check behind the scenes, and converts the value to a 1 or 0 to store the data. However, being a lightswitch, craft probably does not perform this check, so if the field is missing from the post-data (which it would be if unchecked), then craft simply ignores it, as not being part of the form to begin with.

There may be a simpler method built in that I'm not aware of, but one solution would be to create a hidden field with your lightswitch handle, and then set that field using javascript when the user checks or unchecks the checkbox, much the way that craft does.

Here is the standard lightswitch html for example so you can see how craft sets this up:

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

You would want something like this:

        <input type="checkbox" class="js-checkbox" {% if currentUser.memberShowEmailLink %}checked="checked"{% endif %}>
        Show a link to contact me via email in the member directory?

    <input type="hidden" class="js-lightbox" name="fields[memberShowEmailLink]" value="{% if currentUser.memberShowEmailLink %}1{% endif %}">

Then in javascript:

$('.js-checkbox').on('change', function(event) {
    if($(this).prop('checked')) {
        $('.js-lightbox').val(1);
    } else {
        $('.js-lightbox').val(0);
    }
});

Note: if you leave off the 'name' attribute the dummy checkbox should not get submitted.


Late answer but this is currently possible (Aug 2018).  Make sure you do have your Stripe payment method set to 'Authorize Only (Manually Capture)'.

Go to your order, and you'll find a Capture button in the Transactions box at the bottom.  Just hit that button anytime to charge the card.


Customize activation and other system emails

Actually Brad, I was able to figure out a way to do this with some help from @carlcs in the slack channel.

I defined an "HTML Template" in the main email settings screen, then in that template I extend a basic HTML email wrapper, then inside my content block I did this:

{% if emailKey == 'account_activation' %}
    {% include 'mail/activation' %}
{% endif %}
{% if emailKey == 'forgot_password' %}
    {% include 'mail/forgot-password' %}
{% endif %}
{% if emailKey == 'test_email' %}
    {% include 'mail/test-email' %}
{% endif %}
{% if emailKey == 'verify_new_email' %}
    {% include 'mail/verify-new-email' %}
{% endif %}

Then I just created HTML versions of each email within the context of my wrapper.

It works perfectly!


Get related entries in plugin via Entries field type

First, you should get all the Merchant Entries with the merchantId of 123

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section('merchants');
$criteria->merchantId = '123';

$merchants = $criteria->find();

Next, find all Gift Card entries that are related to the found merchants.

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = 'giftCards';
$criteria->relatedTo = array(
    'targetElement' => $merchants,
    'field' => 'YOUR-ENTRIES-FIELD'
);

$gift_cards = $criteria->find();


Plugin: Fill Record with data (auto generated form)

I believe what you're asking is if there is any built-in scaffolding support in Craft.

If that's the case, then no... you'd have to manually build out the forms and CRUD support.

Yii does ship with a component called gii that enables scaffolding, but it's currently not compatible with Craft and we don't support it.


How do I add a second section name to this logic?

A second limiter is added to the conditional like this:

craft.entries.section('and, not homepage, not clientPage')


It is (currently) not possible to set an Entry Template without selecting "Entries in this section have their own URLs" in the settings.
So to make Live Preview available on your "slides" entries, you have to register the URLs.
But if you use hidden / pseudo URLs (i.e. hideme/{slug}) that you don't link to and nobody knows of, this shouldn't be that bad of a workaround:

You can check if the page is shown with Live Preview using craft.request.isLivePreview.
Use this to redirect normal page views (search engines / accidential URL discoveries) to 404
and show your slider for Live Preview requests.
Set the Entry Template to the same file you use for your homepage or a new template file where you include the slider partial.

{# Conditionally show slider or redirect to 404 #}
{% if craft.request.isLivePreview %}
    {% include '_includes/slider' %}
{% else %}
    {% redirect 404 %}
{% endif %}

Live Preview doesn't only work for the Entry variable any more (→ Craft 2.3.2616 release notes). So you can make Live Preview to
update on changes detected on any entries referenced from the template.

EDIT: Unsaved entries are not yet included in such craft.entries calls (→ Brandon's comment to my question on g+). As a workaround you could do something like this:

_includes/slider.html:

{# Show unsaved "slider" entry #}
{% if not entry.id %}
    <li>{{ entry.title }}</li>
{% endif %}

{# Go through "slider" entries #}
{% for slide in craft.entries.section('slides') %}
    {{ slide.title }}
{% endfor %}

A dedicated field to set the template (or even multiple of them) used for Live Preview would make a nice feature request though! → done


This should do it:

craft.app.getPlugins().getPlugin('pluginHandle').getSettings()


Questions about Craft 4 release and licenses

1) Is there anything to say about a release date for Craft 4? And about around what time Pixel and Tonic consider it a stable release to be used in production?

Nothing official on either.

  2) What will happen to the support and updates for Craft 3 once Craft 4 is out? Is there any roadmap showing support/update-cycles for Pixel and Tonic products?

No official dates yet, but you can probably use past support timelines (https://craftcms.com/guides/supported-versions) as a guide to what Craft 3 support would be once Craft 4 is out.

  3) Will Craft 3 work with the announced Craft Cloud or will the cloud only work from version 4 up?

Nothing officially announced.

  4) Is there anything to say about how difficult it will likely be to migrate from v3 to v4 and what that would be about?

We'll try to make the 3 => 4 migration as seamless as possible.

  5) What about existing paid licenses when a customer upgrades from version 3 to 4? Is it under the same license after the upgrade? Or are licenses bound to a major version and need a customer buy a new license for version 4?

The licensing model should be exactly how it is for Craft 3.


Is caching by cart.id sufficient?

I would not cache the cart, it could always be changing. You would need to create a cache ID based on every aspect of the cart that could change (line items, coupons, addresses, sales, discounts, tax etc). Bottom line, do not cache dynamic things like the cart.

  Is there any potential risk?

See above.

  What is {% set cart = craft.commerce.cart %} actually setting when cart.id = null ?

You are getting an order model returned, it just does not get an ID until the first item is added to the cart and the order/cart is saved to the database.


It's actually quite easy and you don't really need json or a plugin to do it. If you really need it as a plugin, then that's a whole different thing that would need a separate answer.

In your search template file:

<form id="find-contact-form" data-url="/contacts">
    Zip Code
    <input type="text" name="zip">

    <input type="submit" value="Find Contacts"/>
</form>

In your search template javascript file (make sure to include jquery):

$(function() {
    $('#find-contact-form').submit(function(event) {
        event.preventDefault();

        var data = $(this).serialize();
        var url = $(this).attr('data-url');

        $.post(url, data, function(result) {
            $('#contacts').html(result);
        }, 'html')
        .fail(function() {
            //console.log("error");
        });
    });
}

In your contacts.html template:

{% if craft.request.isAjax %}

    {% set zip = craft.request.getParam('zip') %}

    {% if zip %}
        {% set contacts = craft.entries.section('contacts').zip(zip) %}
        {% if contacts|length %}
            {% for contact in contacts %}

                {{ contact.firstName }} {{ contact.lastName}}

                T {{ contact.telephone}}

            {% endfor %}
        {% else %}
            No contacts found with the zip {{ zip }}.
        {% endif %}
    {% endif %}

{% endif %}

You can also use that same template for your normal contacts page. Just use the clause {% if not craft.request.isAjax %}Put my full page content here.{% endif %}. In fact, the search page can just submit to itself using this technique.


What is the best way to handle Events that get triggered during a migration?

In general it’s best to avoid calling service methods from migration files in the first place. The reason for that is, your services should be able to assume that they’re always working with the latest schema design.

Imagine if a month from now you decide you need to add a new column to your formentries table (or whatever it is). The associated record, model, and service all get updated to work with this new column’s data. And you write a migration that adds the column:

$this->addColumn('formentries', 'newColumnName', ColumnType::Varchar);

You test and everything appears to work great, and then release it.

Then someone who was running an old version of your plugin tries to update. Before Craft runs the new migration that adds the new formentries column, it still needs to run your old migration that calls your saveFormEntry() service method. Except that saveFormEntry is now assuming that the new column already exists, and depending on your code, either they run into a validation error (so no form entries get saved) or a MySQL error (because your record is trying to save on a column that doesn’t exist yet).

So, in the long run you’ll be better off if you keep all DB-related changes contained to your migration file.

That said, Craft does cheat a little here - this is exactly why we have the concept of “breakpoint updates” at our disposal. When we mark a release as a breakpoint, it prevents anyone from updating to a version after it, until they have updated to it. So if we do need to call service methods in a migration, then we just need to remember to set a breakpoint at some point before we start making more DB schema changes.

Plugins will also be able to set breakpoints when Craft 3 comes along. Because of that, this is still a relevant question. I’m thinking that the safest thing to do here is to simply disable event dispatching altogether during updates. A plugin should be in full control over what code gets run when it’s updating itself, with no chance of another plugin intervening.

I don’t think we have a way to disable Yii’s event system altogether (and probably shouldn’t even if we could), but we could at the very least prevent craft()->on() from doing its thing during update requests. Maybe even add a new argument to it that can be set to true if the event should get fired even if it’s an update request, in case there are any use cases that I’m overlooking.


Installation error - Craft.uppercaseFirst is not a function

It sounds like some files were not uploaded correctly to your server, or are corrupt/missing from your Craft download.

Download a fresh copy of Craft from http://buildwithcraft.com and swap out your existing craft/app folder with the new, freshly downloaded craft/app folder.


Edit a table field type from the front end

Each input name needs to have the following format:

fields[FieldHandle][RowId][ColId]

It doesn’t matter what you set RowId to, as long as it’s the same for all of the columns in that row. (Table fields in the CP just get incrementing row IDs - 0, 1, 2, etc.)

ColId, on the other hand, needs to be based on your actual column IDs. The only way to get those would be to inspect a Table field’s HTML on your Edit Entry page:

Each column will have an ID in the format of colN. “N” won’t necessarily be nth column in the table though; it will be the nth column you’ve created in the table since the field was first created, regardless of how they are ordered or whether any columns had ever been deleted. Which is why it’s best to just inspect the table HTML rather than trying to guess what the column ID will be.

So in your case, that  line should look something like this:

<input type="text" id="accreditingInstitution" name="fields[providerCredentials][0][col1]" value="{{ row.accreditingInstitution }}">


Problem solved! (head slap)

The <form> requires the following parameters:
enctype="multipart/form-data"

An odd error that gets thrown if omitted.


How to link to an entry in a Markdown field in the CMS

I just figured out how to do this. It was vexing me!

Anyway, Craft is awesome and has provided support for Reference Tags.

Write your Markdown like this:

[link text]({entry:20})

Here's the Twig:

{{ entry.markdown|parseRefs|markdown }}

Reference Tags support more than just entries, but this was the bit that I needed.


Orders can not be updated after they are completed. The only way around this at the moment is if a plugin used it's own controller action to update the order.

Please submit a feature request here: https://github.com/craftcms/commerce/issues


You can use Twig's loop variable (docs).
{% for asset in relatedEntries %}

    {{ loop.index }}  {{ asset.title }}

{% endfor %}


How to add a custom panel to the debug bar?

I'm not sure this is the proper way to do it but I got this working.
I used the Yii\Application::EVENT_BEFORE_REQUEST event in my plugin's
init() method:
<?php

namespace my\namespace;

use craft\base\Plugin;
use craft\web\Application;

class MyPlugin extends Plugin
{
    public function init()
    {
        Event::on(
            Application::class,
            Application::EVENT_BEFORE_REQUEST,
            function() {
                /** @var \yii\debug\Module */
                $debugModule = Craft::$app->getModule('debug');

                $debugModule->panels['mycustompanel'] = new DebugPanel(['module' => $debugModule]);
            }
        );
    }
}

At first, I thought about using the yii\base\Controller::EVENT_BEFORE_ACTION
event, but in order to work, the Yii2 component needed to add some routes  and
the EVENT_BEFORE_ACTION event occurs after routing has been done.
Any other event I could think of occurred too early, before Craft had called
the craft\web\Application::debugBootstrap() method.
So far it works but if anyone has a suggestion on the proper way to do it, I'm all hears.

Edit 10/05/2020: it looks like I was almost there. I was just missing the id property in the Panel's constructor parameter. Without it, clicking your panel name in the collapsed debug bar won't open straight away on your panel's tab.
It's also important to check that the Debug module is loaded before adding your panel to it. Otherwise users having the debug bar disabled would get errors.
I found out by looking at the code in Ben Croker's Element Panel plugin.
Event::on(
    Application::class,
    Application::EVENT_BEFORE_REQUEST,
    function () {
        /** @var \yii\debug\Module */
        $debugModule = Craft::$app->getModule('debug');

        if ($debugModule) {
            $debugModule->panels['mycustompanel'] = new DebugPanel([
                'id'     => 'mycustompanel',
                'module' => $debugModule,
            ]);
        }
    }
);


adding a block of matrix field from a front end entry form

With the help of CreateSean comment I have managed to fix the problems in the form.
Here it is for future reference for everyone:
<form method="post" accept-charset="UTF-8" enctype="multipart/form-data" class="edit-store">
    {{ csrfInput() }}
    {{ actionInput('entries/save-entry') }}
    {{ redirectInput('members-zone/{slug}') }}
    {{ hiddenInput('movementCoursesEntries', '2') }}
    {{ hiddenInput('enabled', '1') }}
    <input type="hidden" name="entryId" value="{{ entry.id }}">

    {# various includes here #}
    <input type="hidden" name="fields[blocks]" />

    {% for block in entry.videos %}
        {% if block.type == 'video_box' %}
            <div class="matrixblock ">
                <input type="hidden" name="fields[videos][{{ block.id }}][type]" value="video_box">
                <input type="hidden" name="fields[videos][{{ block.id }}][enabled]" value="1">
                <input type="hidden" name="fields[videos][{{ block.id }}][fields][videoTitle]" value="{{ block.videoTitle }}">
                <input type="hidden" name="fields[videos][{{ block.id }}][fields][videoUrl]" value="{{ block.videoUrl }}">
            </div>
        {% endif %}
    {% endfor %}
    {# hidden field with block type that is being updated #}
    <input type="hidden" name="fields[videos][new_0][type]" value="video_box">
    {# is this block enabled #}
    <input type="hidden" name="fields[videos][new_0][enabled]" value="1">
    <input type="text" name="fields[videos][new_0][fields][videoTitle]" value="" placeholder="Video Title">
    <input type="text" name="fields[videos][new_0][fields][videoUrl]" value=""  placeholder="Videl URL">

    <input type="submit" value="Save" class="btn btn-primary">
</form>


You need to include your module handle to the route

module-handle/site/say

You also should use the correct namespace instead of app/controllers I suggest you to read the documentation

You can as well define a route if you like


Step 01.
Open your php.ini file.
File path for php 5.6: /etc/php/5.6/apache2/php.ini
File path for php 7.1: /etc/php/7.1/apache2/php.ini

Step 02:
Then increase max_exection_time from 30 to 300 or other as you like.

Step 03:
Restart your server sudo service apache2 restart

Step 04:
Go to your web's root directory then run this command composer update

Now browse your site.


How to directly get value of text field in a given section of type structure?

Try this:

{% set value = craft.entries.section('mySectionHandle').type('entryTypeHandle').first().myTextFieldHandle %}

{{ value }}

Just with your field handle at the end


Using PayPal Standard with Craft Commerce

Craft Commerce uses Omnipay for its payment processing library.  Here's a list of the Omnipay officially supported 1st party gateways: https://github.com/thephpleague/omnipay#payment-gateways

Omnipay: Paypal says it supports these Paypal services:

PayPal_Express (PayPal Express Checkout)
PayPal_ExpressInContext (PayPal Express In-Context Checkout)
PayPal_Pro (PayPal Website Payments Pro)
PayPal_Rest (Paypal Rest API)

Didn't have much Googling luck trying to find a 3rd party Omnipay plugins that support Paypal Standard, but there's always the option of writing an Omnipay compatible one that wraps the Paypal Standard API, then a Craft plugin to bring that into Commerce.


When your plugin has a camel cased name you need to make sure you camel case your references to it, such as when you try and visit an action url.

I assume this is due to when craft tries to resolve the url it sees pluginname and pluginName as two different things, much like php variables.

I would also assume this is due to the way Craft autoloads classes.

So using webMentionReciever would resolve since it would be a "exact" match to your plugin name.

This isn't limited to just plugin names, the same goes for any service, controller etc references too.


Encoding special characters even with the raw filter due to include

As mentioned in the Discord you are probably looking for the source function.

The reason the include doesn't work is covered in the old PR for (what eventually became) source. It's probably best I link all that discussion than try to put it in my own words here!

Initial discussion with your problem
First PR
Further PR after discussion (the source function as it is now)


Can I use a Block inside an Include file?

I believe it is possible @jack. Take a look at this working demo - http://twigfiddle.com/9rwjeo

Update:

On closer inspection the original example did not work. You can not have a block inside an include how you are describing.

I have update the twig fiddle with a work around though


Getting error "Your session has ended. Enter your password to log back in." every 30 seconds or so

Thanks for the troubleshooting help. It appears as though the issue was the cache and how Craft handles sessions.

For those who use a cache/CDN and are having issues with control panel session timeout:

Even after ignoring /admin on our cache, the cached pages on the public-facing portion of the site were conflicting somehow with how Craft was handling the sessions in the admin panel.

Our solution was to create a cookie we called isAuth that was created on login and destroyed on logout. We did this by creating a plugin and using userSession.onLogin and userSession.onLogout.

Then we set our Akamai cache to no-cache, no-store if isAuth cookie is present. This fixed all timeout issues.


you can do all of that with Craft CMS, the thing you're searching for are "related entries" where you can relate news to a certain movie ... director ... basically you can relate everything to everything.

You can find more information about relationships here:
https://craftcms.com/docs/relations


"The license located at config/license.key belongs to gridserver.com"

You need to log into id.craftcms.com and re-assign the license to the appropriate domain e.g. centraldshhomes.com.

Craft automatically gets the top level domain when you grab a license, as part of our workflow we just assign it closer to go live / deployment.


What variables are available to what template types?

It looks like you're coming from some other system? When I see "single-whatever" I think of WordPress' specific template structure... which may or may not have any meaning in Craft.

Unlike something like WordPress where there's a very specific structure to how you get information and what template determines how it behaves, Craft is a bit more open-ended (and DIY) in that regard and that can lead to some confusion, especially if you try to setup something like a search page and then try to use entry as variable (which won't be defined, unless you define it yourself).

1. Is there a simple list of the types of pages Craft outputs?

Unlike other content management systems, Craft really doesn't have a concept of "page types". One of the great things about Craft (and probably slightly confusing when you first start) is how you reference content throughout the system in the same, simple way.

You probably know that Craft holds your "page" content in what are called Entries. Think of Entries like Pages since they can map to a URL but they don't necessarily have to...

2. What are all the variables available...

Templates more or less work the same throughout. Craft's Globals are available no matter which template you're in, but Craft really allows you to fetch any piece of content (user, asset, tag, entry, etc.) anywhere you need it.

But what I think you're kind of asking about is when you view an entry or category specifically...

If you setup your sections/categories to have a URL/slug that Craft knows about, Craft automatically will populate the entry or category variable for you so you can access things like title, slug, and any custom fields you might also have on that entry or category.

It's basically a shortcut so you don't have to get it yourself and determine what kind of "page" you're on when you're in a template. Plugins can also inject their own variables into the system.

Craft's documentation on routing is a good place to reference on this as well, especially if you're used to another system where a certain file/folder structure is implied. Everything is up to you so your templates can be as messy or clean as you want them to be.


This issue turned out to be caused by a config setting that had been added to general.php

'cacheMethod' => 'apc'

The server had APC installed but my local environment did not.  There were a couple options to fix this:

Install Alternative PHP Cache (APC)
Setup a multi-environment config and override the cacheMethod setting with another method that is supported in your local environment, such as 'cacheMethod' => 'file'
Remove this line as it may not be needed anyway. apc is PHP byte caching and if your running PHP 5.5, that's enabled by default and apc is unnecessary


Seems like a simple striptags filter would do nicely in this case:

{{ entry.titleField|striptags('
')|raw }}

If you're having issues with linebreaks, you might want to add the nl2br filter as well:

{{ entry.titleField|striptags('
')|raw|nl2br }}


How to get core shipping methods in Commerce 2? How to save an order?

Commerce::getInstance()->getShippingMethods()->getAllShippingMethods(); would be the functional equivilant of Commerce 1'scommerce_shippingMethods->getAllCoreShippingMethods() since it returns the the shipping methods defined in the database.

Commerce::getInstance()->getShippingMethods()->getAvailableShippingMethods(); will return the shipping methods in the database as well as any 3rd party defined shipping methods.


We've looked into it before as I agree, it'd be awesome. But as of February 2020, the answer is no because SQLite is missing some key features that Craft currently relies on: https://www.sqlite.org/omitted.html

Specifically the missing joins and alter table commands.


Check which pagination page to display code outside of pagination loop

UPDATED/EDIT ANSWER:

If you only want to show the entries on the first page you should make an if statement to check if you are on the first page for your Featured Items by requesting the page number.

Option 1:

{% if craft.request.getPageNum() == 1 %}

// Featured content here

{% endif %}

Option 2:

{% if craft.request.getLastSegment() == 'p1' %}

// Featured content here

{% endif %}

The first option is the best / cleanest solution even though they give the same outcome. Option 2 is a nice work around but might be more usefull in other ways then this one.

More about craft requests can be found in the docs:
https://craftcms.com/docs/templating/craft.request


Is it possible for an assigned entry (via field type Entries field) to know what it has been assigned to?

You could use the relatedTo param (docs).

{% set relatedPipelines = craft.entries()
    .section('pipeline')
    .relatedTo(yourSeriesEntry)
    .all() %}

Would return pipelines (if any) related to your series1.


These two items are unrelated.

First, there's no need to worry about the deprecation errors.  We actively maintain our plugins and will have them resolved before they should have any effect on general use of our plugins. We've made a feature request to Craft to consider to reduce the prominence of the deprecation message, as it generally just causes unneeded alarm.

Second, the releases message is probably due to us currently having an SNI SSL certificate installed and some older server configurations choke because they don't know how to deal with them. The error probably indicates that you may be running fairly old software on your server.

If you're curious, I was told by Craft support:

  Technically there are 3 things a server needs for it to be able to
  recognize SNI SSL certs.

  1) Using a version of cURL that supports it, at least 7.18.1 (8 years
  old: https://curl.haxx.se/changes.html)

  2) Using a version of cURL compiled against a library that supports
  SNI, e.g. OpenSSL 0.9.8j (depending on the compilation options some
  older versions. 7 years old:
  https://www.openssl.org/news/changelog.html)

  3) Using TLS 1.0 at least (not SSLv3)


How can I output the value of a superTable field within a loop?

The correct markup is:

{{ block[field.handle] }}

The final looks like this:

<input type="text" name="fields[myField][fields][{{ field.handle }}]" value="{{ block[field.handle] }}">


This is how you're getting your "Forside" articles:

{% for entry in entry.artikkelbolk1.limit(14) %}
    <!--print out article content-->
{% endfor %}

this let me assume you're using an entries field to "collect" articles from your "artikkelbolk1" section.
The Twig syntax you're using is actually a shortcut for this:

{% for entry in craft.entries({
    relatedTo: { sourceElement: entry, field: 'artikkelbolk1' },
    order:     'sortOrder',
    limit:     14
}) %}
    <!--print out article content-->
{% endfor %}

So it get's you all entries related to your current entry, the preset entry model of your "forside" section.

To get all articles not listed on "Forside" you'd do this. First, grab those "forside" entries again, but you're only interested in the IDs
of the entry models, that's why you now append the ids() method. Then join the IDs into a string with the following syntax (use Twig's join filter for that!):

'and, not 42, not 82, not 20'

So it basically lists all entry IDs prefixed with 'not'; the 'and' at the beginning just makes Craft aware that you're listing multiple values. With this string you can now go ahead and exclude your entries from an ElementCriteriaModel. To do that simply pass the string into the id parameter.

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

{% set forsideEntriesIds = craft.entries({
    relatedTo: { sourceElement: forside, field: 'artikkelbolk1' },
    order:     'dateCreated desc',
    limit:     14
}).ids() %}

{% set idParam = 'and, not ' ~ forsideEntriesIds|join(', not ') %}

{% for entry in craft.entries.section('artikkel').id(idParam).order('dateCreated desc') %}
    <!--print out article content-->
{% endfor %}


Try this:

{% do view.registerCssFile(siteUrl ~ 'css/print.css', {'media' : 'print'}) %}


Dynamic placeholder text in 'Plain text' field type

When editing the field, I have tried setting the 'Placeholder Text' as {title}, but this does not work - it displays "{title}"!

Yeah, those placeholder text fields aren't dynamically parsed as Twig templates like others (Entry URL Format, etc.).

  I don't want to create a new custom type of field as it seems a little heavy handed for this use case.

Unfortunately that's all that is coming to mind for me for this use-case.


Adding additional custom fields to Contact Form Plugin from Pixel & Tonic

I'd start by reading the "Adding Additional Fields" part of the docs. ;)


Use the web inspector to look at the console log and see if it can find the images
Visit that image's location in the browser. If the location is correct, but it's not rendering, you probably need to check the image's file permissions. If it's set to anything lower than 644, you won't be able to see it. Set the image's permissions to 644
Make sure your assets folder and any subfolders are set to 777 so it can generate the image transforms


Craft unable to determine if an update is available?

There's a 99% chance you're running old version of MAMP that needs to be upgraded.

The host that https://elliott.craftcms.com lives on recently dropped support for TLS 1.0, which what your stack is trying to connect with.


Edit: I needed to do a little digging.

Internally, Craft can actually have multiple instances of Twig_Environment running (a Twig_Environment instance is what you get back by calling craft()->templates->getTwig()). Craft caches each instance in a private class variable, with the template loader classname and a hash of the environment's options making up the cache key. If a Twig_Environment instance doesn't exist for the loader classname and options array you pass to getTwig() (null and null, in your case), Craft will actually create a new Twig_Environment instance and return it.

This means that you're probably not getting back the same Twig_Environment instance that Craft is using to actually render your template – which explains why the call to addGlobal() works, but the variable is still undefined in your template.

The solution is to make sure to pass in the same loader classname and options as Craft does, to make the TemplateService return the same instance. The default here appears to be null for the loader classname, and ['safe_mode' => false] for the options.

In other words, this should work:

public function init()
{
    $var = 'something';
    $twig = craft()->templates->getTwig(null, ['safe_mode' => false]);
    $twig->addGlobal('var', $var);
}

Unfortunately, I don't know if Craft can ever be expected to use different options (or even a different loader) in certain circumstances – e.g. if safe_mode would ever be true. In any case, it is false by default in the TemplatesService render() method. Hopefully, someone might clear this up further.

Anyway – for a less hacky way to add globals to the Twig environment, here's my original answer:

You'll need to create a custom Twig extension. In your extension class, add a getGlobals() method, e.g.

<?php namespace Craft;

use Twig_Extension;
use Twig_Filter_Method;

class AwesomeTwigExtension extends \Twig_Extension
{
    public function getName()
    {
        return 'Awesome';
    }
    public function getGlobals()
    {
        $var = 'something';
        return [
            'var' => $var,
        ];
    }
}

Don't forget to actually load your custom extension – in your plugin's primary class:

public function addTwigExtension()
{
    Craft::import('plugins.pluginhandle.twigextensions.AwesomeTwigExtension');
    return new AwesomeTwigExtension();
}

Of course, adding the globals in a custom extension instead of just using your plugin's primary class' init() method is a bit more verbose, but remember that you could easily add a method to your plugin's primary class returning the globals and just call that from your extension, e.g.

// In your plugin's primary class
public function getGlobals()
{
    $var = ...
    return [
        'var' => $var,
        ...
    ];
}

// In your custom Twig extension
public function getGlobals()
{
    return craft()->plugins->getPlugin('pluginHandle')->getGlobals();
}

(You could of course also return the globals from a plugin service, which would be cleaner).

Hopefully somebody else will have an explanation for why the addGlobal() method doesn't work – and perhaps an even easier solution.


It turns out Smartdown should do the trick. It's a Craft plugin that adds MarkdownExtra and SmartyPants (the latter of which matters here) support to Twig.

SmartyPants is a rather ancient blogging plug-in from John Gruber for intelligently handling smart punctuation. Michel Fortin ran with it in the form of PHP SmartyPants, and most recently this Craft plugin by Experience.

Note: my first answer linked to a SmartyPants Craft filter by Experience that's apparently not finished. That one won't get you very far. :)


Using image transforms sometimes results in bloated file sizes.

Is Imagick installed on your server?

If not, Craft uses GD as a "fallback". Chances are that transforms with GD lead to poorer image quality and longer calculation times. I'm not sure about ICC profiles, but I think they only get included using Imagick (photos can look really bad if profile is missing unexpectedly!).


Try using _count -- there are some examples here: https://github.com/craftcms/cms/issues/4847


Smart Map plugin, HAVING SQL and GROUP BY elements.id

The HAVING clause comes from this piece of code:
// Modify query
$query
    ->addSelect($haversine.' AS distance')
    ->having('distance <= '.$filter->range)
;

The variables going into this query modification are:

$haversine - A complex formula which determines the relative distance of each address from the center point.
$filter->range - The maximum search distance. (defaults to 25)

This code exists so that your proximity search results can be sorted by closest locations. It will be triggered any time you chain this into your Twig query:
.myFieldHandle(params)

You can see the full details of how this all ties together in the documentation here:

https://plugins.doublesecretagency.com/smart-map/sorting-entries-by-closest-locations/

There should be no conflict with any existing GROUP BY clause... Smart Map does not set or modify that aspect of the query. And there is no inherent conflict between HAVING and GROUP BY, as demonstrated by this w3schools example, which utilizes both in combination.


Entry Pagination - Group by Current Category and Order by Post Date

Using the pagination helper might be a cleaner way to achieve your desired results. It would go something like this:

{% paginate craft.entries.section('news').order('postDate').relatedTo({targetElement:categoryId}).limit(5) as entries %}
    {% for item in entries %}
        {# your item code goes here #}
    {% endfor %}
    {% if paginate.prevUrl %}
        Previous
    {% endif %}
    {% if paginate.nextUrl %}
        Next
    {% endif %}
{% endpaginate %}

By using the relatedTo({targetElement:categoryId}) param you will stay in the same category as you paginate entries. This pagination helper handles the urls.  So we are just accessing the paginate.prevUrl & paginate.nextUrl variables provided by the helper.  All you need to do is make sure the categoryId is set to the category you need and set the limit to the number of items to paginate by.

--

For your particular setup I would use two templates.  One for the News landing page and one for the category landing. The news landing page will paginate all News entries.

Once a category is clicked and now uses the template specified in the category setup page, you will limit paginated entries to the current category.  The variable category is reserved for those templates so you can access anything variable associated with the current category you are on. You will only notice slight difference between the two. The Category Template adds the relatedTo({targetElement:category.id}) criteria in addition to setting the active category class.

Here are both templates.

News Landing Template

{% set title = entry.title %}
{% set description = entry.body | striptags %}

{% extends "_layout" %}

{% block content %}

{{ newsHeader.header }}

All

            {% for topic in craft.categories.group('newsCategories') %}

                {{ topic.title }}

            {% endfor %}

            All

All

                {% for topic in craft.categories.group('newsCategories') %}

                    {{ topic.title }}

                {% endfor %}

    {% paginate craft.entries.section('news').order('postDate').limit(5) as entries %}

        {% for item in entries %}

{{ entry.title }}

{{ entry.postDate|date("F j, Y") }} &nbsp;|&nbsp;

                  {% if entryCategories | length %}
                    {% for topic in entryCategories %}
                      {{ topic.title }}
                    {% endfor %}
                  {% endif %}

{{ entry.body }}

        {% endfor %}

        {% if paginate.prevUrl %}
            Previous
        {% endif %}
        {% if paginate.nextUrl %}
            Next
        {% endif %}

    {% endpaginate %}

{% endblock %}

Category Template

{% set title = entry.title %}
{% set description = entry.body | striptags %}

{% extends "_layout" %}

{% block content %}

{{ newsHeader.header }}

All

            {% for topic in craft.categories.group('newsCategories') %}

                {{ topic.title }}

            {% endfor %}

            All

All

                {% for topic in craft.categories.group('newsCategories') %}

                    {{ topic.title }}

                {% endfor %}

    {% paginate craft.entries.section('news').order('postDate').relatedTo({targetElement:category.id}).limit(5) as entries %}

        {% for item in entries %}

{{ entry.title }}

{{ entry.postDate|date("F j, Y") }} &nbsp;|&nbsp;

                  {% if entryCategories | length %}
                    {% for topic in entryCategories %}
                      {{ topic.title }}
                    {% endfor %}
                  {% endif %}

{{ entry.body }}

        {% endfor %}

        {% if paginate.prevUrl %}
            Previous
        {% endif %}
        {% if paginate.nextUrl %}
            Next
        {% endif %}

    {% endpaginate %}

{% endblock %}


No – whatever you've currently set as the sorting order (Structure, Post Date, Expiry Date, etc) in the element index in the control panel has absolutely no effect on how your entries are output in a template, or how they're ordered.

For Structure sections, the default sorting order in a template is Structure, i.e. hierarchical:

{% for entry in craft.entries.section('myStructureSection') %}
    {# Will output entries in hierarchical, chronological order #}
{% endfor %}

Keep in mind that if your Structure has multiple levels, i.e. child entries, you'll have to jump through a few hoops – using the {% nav %} tag is recommended as its able to output child entries recursively, e.g:

{% nav entry in entries %}

        {{ entry.title }}
        {% ifchildren %}

                {% children %}

        {% endifchildren %}

{% endnav %}

Finally, if you want to output the entries in a non-hierarchical order, you can use the order parameter:

{% for entry in craft.entries.section('myStructureSection').order('postDate desc') %}
    {# Will output newest first #}
{% endfor %}


Error: Invalid negative value for  attribute

Craft charts are taking the width and height of the container's
 as a basis for drawing the chart. So your JS is probably working but you need to define a width and height for the chart's div to make it work.

A basic implementation of the Area chart would look like this:

{% extends "_layouts/cp" %}
{% set title = "Area Chart"|t %}

{% includecss %}
    #chart {
        width: 400px;
        height: 300px;
    }
{% endincludecss %}

{% includejs %}

    var $chart = $('#area'),
        chart = new Craft.charts.Area($chart);

    var dataTable = new Craft.charts.DataTable({
        columns: [
            { label: "Date", type: 'date' },
            { label: "New Users", type: 'number' },
        ],
        rows: [
            ['2016-01-23', 5],
            ['2016-01-24', 15],
            ['2016-01-25', 10],
            ['2016-01-26', 13],
        ]
    });

    chart.draw(dataTable);

{% endincludejs %}

{% block content %}

{% endblock %}


exclude list of entries from craft.entries tag

As @Youniteus stated in a comment to this post the answer was related to this question

I had to tweak the join statements a bit but it's working now.

complete code here:

{# convert array to string  so we can remove these ids #}
{% set excludedJobs = excludeRelatedJobs|join(', not ') %}

{# set careers to all jobs created by author#}
{% if not excludedJobs|length%}
    {% set careers = craft.entries.section('careers').authorId(user.id) %}

{# filter out jobs already added to store#}
{% else %}
    {% set careers = craft.entries.section('careers').authorId(user.id).id('and, not ' ~ excludedJobs) %}
{% endif %}

    {% for job in careers %}

{{ job.title }}

    {% endfor %}


Database Request with entry variable as argument

hmm, wat do you mean with sub_entries? As a general pointer I would do it like this:

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

and then loop over the books.

To get back to your question, I would suggest moving all the different sections into 1 channel with multiple types in it. For example:

Channel: Media

Types:

Books
Games
Movies

Then you can query them like this:

{% set books = craft.entries.section('media').type(['books']) %}

{% for entry in books %}
    <h2>{{entry.title}}</h2>
{% endfor %}

More info on types here


Here’s a simple plugin with two tabs and two fields, one field per tab. This plugin was generated on https://pluginfactory.io/. It’s a great starting point to get the plugin scaffolding set up. After that I added the code that I’ve included below to get it to work.

It should give you something like the following screenshot for the first tab:

and for the second tab it should look similar to this:

Folder Structure

[-] craft
   [-] plugins
      [-] myplugin
         [-] controllers
            |-- MyPlugin_TabOneController.php
            |-- MyPlugin_TabTwoController.php
         [+] resources
         [+] services
         [-] templates
            [-] _layouts
               |-- cp.twig
            [-] tabOne
               |-- _index.twig
            [-] tabTwo
               |-- _index.twig
         [+] translations
         [+] variables
         |-- MyPluginPlugin.php

myplugin/MyPluginPlugin.php

I'm only highlighting the functions that were necessary for the plugins to show up. Add or adjust them as needed.

...

public function registerCpRoutes()
{
    return array(
        'myplugin' => ['action' => "myPlugin/tabOne/index"],
        'myplugin/additional-settings' => ['action' => "myPlugin/tabTwo/index"],
        /**
         * Each 'action' relates to a controller in your plugin.
         * The names of your controller and these action must match.
         */
    );
}

protected function defineSettings()
{
    return array(
        'someSetting' => array(AttributeType::String, 'label' => 'Some Setting', 'default' => ''),
        'additionalSetting' => array(AttributeType::String, 'label' => 'Some Additional Setting', 'default' => ''),
        /*...more settings here as needed...*/
    );
}

public function getSettingsHtml()
{
   return craft()->templates->render('myplugin/MyPlugin_Settings', array(
       'settings' => $this->getSettings()
   ));
}

public function getSettingsUrl()
{
    return 'myplugin';
    /* This must be the same as the first segment in your cpRoutes. */
}

...

myplugin/controllers/MyPlugin_TabOneController.php

namespace Craft;

class MyPlugin_TabOneController extends BaseController
{

    /**
     * @var    bool|array Allows anonymous access to this controller's actions.
     * @access protected
     */
    protected $allowAnonymous = array('actionIndex',
        );

    /**
     */
    public function actionIndex()
    {
        $settings = craft()->plugins->getPlugin('myPlugin')->getSettings();
        $this->renderTemplate('myplugin/tabOne/_index', array(
            'settings' => $settings,
        ));
        /**
         * The path above is to your templates folder in your plugin.
         */
    }
}

myplugin/controllers/MyPlugin_TabTwoController.php

namespace Craft;

class MyPlugin_TabTwoController extends BaseController
{

    /**
     * @var    bool|array Allows anonymous access to this controller's actions.
     * @access protected
     */
    protected $allowAnonymous = array('actionIndex',
        );

    /**
     */
    public function actionIndex()
    {
        $settings = craft()->plugins->getPlugin('myPlugin')->getSettings();
        $this->renderTemplate('myplugin/tabTwo/_index', array(
            'settings' => $settings,
        ));
        /**
         * The path above is to your templates folder in your plugin.
         */
    }
}

myplugin/templates/_layouts/cp.twig

It’s not necessary to break out the templates like I have here but it does help to keep from repeating your self in the templates, especially if you have several tabs.

{% extends "_layouts/cp" %}
{% import "_includes/forms" as forms %}

{% includeCssResource "myplugin/css/Myplugin_Style.css" %}
{% includeJsResource "myplugin/js/Myplugin_Script.js" %}

{% set title = 'My Plugin' %}

{% set tabs = {
    settings: { label: "Settings"|t, url: url('myplugin') },
    additionalSettings: { label: "Additional Settings"|t, url: url('myplugin/additional-settings') },
} %}

myplugin/templates/tabOne/_index.twig

The data-saveshortcut attribute on the form will allow you to use the keyboard shortcut to save your plugin’s settings by using ⌘ + S on a Mac. I’m assuming that Ctrl + S works on Windows although I haven’t tried it.

{% extends "myplugin/_layouts/cp" %}
{% import "_includes/forms" as forms %}

{% set selectedTab = 'settings' %}

{% set content %}

Settings

    <form method="post" action="" accept-charset="UTF-8" data-saveshortcut data-confirm-unload>

        {{ getCsrfInput() }}

        <input type="hidden" name="action" value="plugins/savePluginSettings">
        <input type="hidden" name="pluginClass" value="MyPlugin">
        <input type="hidden" name="redirect" value="myplugin">

        {{ forms.textField({
            label: 'Some Setting',
            instructions: 'Enter some setting here.',
            id: 'settings-someSetting',
            name: 'settings[someSetting]',
            value: settings['someSetting']})
        }}

            <input class="btn submit" type="submit" value="{{ 'Save'|t }}">

    </form>

{% endset %}

myplugin/templates/tabOne/_index.twig

Notice that you’ll need to set the selectedTab in each template for the tabs to work correctly. This should match one of your keys that was set in the tabs variable in the myplugin/templates/_layouts/cp.twig. Also the hidden redirect input in the form should be set to wherever you want to it end up. I noticed that it felt the best when it went back to the original page and it showed the saved values.

{% extends "myplugin/_layouts/cp" %}
{% import "_includes/forms" as forms %}

{% set selectedTab = 'additionalSettings' %}

{% set content %}

Additional Settings

    <form method="post" action="" accept-charset="UTF-8" data-saveshortcut data-confirm-unload>

        {{ getCsrfInput() }}

        <input type="hidden" name="action" value="plugins/savePluginSettings">
        <input type="hidden" name="pluginClass" value="MyPlugin">
        <input type="hidden" name="redirect" value="myplugin/additional-settings">

        {{ forms.textField({
            label: 'Some Additional Setting',
            instructions: 'Enter some setting here.',
            id: 'settings-additionalSetting',
            name: 'settings[additionalSetting]',
            value: settings['additionalSetting']})
        }}

            <input class="btn submit" type="submit" value="{{ 'Save'|t }}">

    </form>

{% endset %}

That should do it!


How do I output a matrix block type only once when looping through entries?

You can output the first matrix block of it's type using .type(blockHandle):

{% for entry in craft.entries.section('mySection') %}
    {% set photoBlock = entry.tactics.type('photo').first %}

    {% if photoBlock %}

            {% set photo = photoBlock.photo.first() %}

            {% if photo %}

            {% endif %}

            {{ entry.title }}

    {% endif %}
{% endfor %}


Checking if a field contains information

If you want a Dropdown field with the option to not select a value (or to clear out the current selection), you can just add an option (typically the first) with an empty value column. If the empty value/option is selected, the following conditional will evaluate to false, and if any other, non-empty value is selected it will evaluate to true:

{% if entry.dropdownFieldHandle %}
    I have a value: {{ entry.dropdownFieldHandle }}
{% else %}
    I have no value in the dropdown
{% endif %}

Note that for Dropdown fields its also possible to leave the label column empty, or to use something like "Pick a value..." as a placeholder.

If you use null instead of an empty value, the above conditional will always evaluate to true, because Twig will treat the value null as a string (i.e. "null"), not an actual NULL value.

As for the |length filter, since your question brings it up – the only thing |length does is to return the length of whatever variable its suffixed to. For an iterable object (like an array or a relational field like Assets or Entries) the length will be the amount of elements in that object (for instance, the number of Assets selected in an Assets field); for strings and numbers it will be the number of characters (meaning "foo"|length and 123|length will both return 3).

The reason |length is often used in conditionals, is that 0 is considered a "falsey" value, and any length larger than 0 is considered a "truthy" value. In other words, a conditional like {% if ""|length %} will be evaluated to false, because "" is an empty string and hence will return 0 characters, while a conditional like {% if "foo"|length %} will evaluate to true because the length filter will return 3 characters and 3 is a "truthy" value.

That being said, the reason why the |length filter isn't neccessary in the above example conditional is simply because Twig will automatically evaluate conditionals testing empty strings to false. So while {% if entry.dropdownFieldHandle|length %} would also work (i.e. evaluate to false for empty values), it isn't actually neccessary.

Finally, is not (or simply not) is simply Twig-speak for the != operator, i.e. is not equal to. So if you wanted to reverse the conditional above, here's how that would look:

{% if not entry.dropdownFieldHandle %}
    I have no value in the dropdown
{% else %}
    I have a value: {{ entry.dropdownFieldHandle }}
{% endif %}


Craft license and develop a website locally?

Sebastian, you can always work locally "for free", using "craft.dev" as host name.

Check the FAQs here at the bottom of the pricing page https://buildwithcraft.com/pricing

MAMP PRO makes it really easy to setup custom host names, btw.


What's the difference? SQL_MODE error in one site and not the other

Answering my own question:
We ended up configuring MySQL as Routh suggests here:
Getting this SQL Error: GROUP BY incompatible with sql_mode=only_full_group_by

Problem solved.