add categories to sitemap plugin through the renderSitemap hook I'm trying to setup a sitemap using the craft-sitemap plugin, however it only covers sections by default and I need to add category groups into it, as half of the URLs are in categories.

I can see the Service Methods section in the docs that says that "There’s several service methods made available to add items to the sitemap."

Hooks
renderSitemap

Add a renderSitemap method to your plugin to add items via the various service methods listed below.

Here’s an example plugin hook method with comments:

addCategoryGroup(CategoryGroupModel $categoryGroup, [$changefreq, [$priority]])

Adds all categories in the group to the sitemap.

$group = craft()->categories->getGroupByHandle('news');
craft()->sitemap->addCategoryGroup($group);

But I'm not sure where to start. Where does the hook code go? As I'm assuming I'd have to create a new file, as it will need to be separate from the sitemap plugin as that will be overwritten if there are any updates.

I would appreciate some advice on this please?

You'll need to create a separate plugin, which adds the categories to the sitemap in response to the renderSitemap hook.

Here's what your main plugin file will look like. This should get you 90% of the way there; the rest is just organising a few files and folders, which is well-covered by the official documentation.

<?php

/**
 * SitemapCategories
 *
 * @package Craft
 */

namespace Craft;

/**
 * The main SitemapCategories plugin file.
 */
class SitemapCategoriesPlugin extends BasePlugin
{
    /**
     * Returns the plugin name.
     *
     * @return string
     */
    public function getName()
    {
        return Craft::t('Sitemap Categories');
    }

    /**
     * Returns the plugin description.
     *
     * @return string
     */
    public function getDescription()
    {
        return Craft::t('Adds categories to the sitemap.');
    }

    /**
     * Returns the plugin’s version number.
     *
     * @return string The plugin’s version number.
     */
    public function getVersion()
    {
        return '0.1.0';
    }

    /**
     * Returns the plugin developer’s name.
     *
     * @return string The plugin developer’s name.
     */
    public function getDeveloper()
    {
        return 'Your name here';
    }

    /**
     * Returns the plugin developer’s URL.
     *
     * @return string The plugin developer’s URL.
     */
    public function getDeveloperUrl()
    {
        return 'https://yourwebsite.com';
    }

    /**
     * Returns a faux schema version, so Craft doesn't attempt to run database
     * updates when the plugin version changes.
     *
     * @return string
     */
    public function getSchemaVersion()
    {
        return '0.0.0';
    }

    /**
     * Returns a boolean indicating whether the plugin has settings.
     *
     * @return bool
     */
    public function hasSettings()
    {
        return false;
    }

    /**
     * Returns a boolean indicating whether the plugin has it's own control
     * panel section.
     *
     * @return bool
     */
    public function hasCpSection()
    {
        return false;
    }

    /**
     * Adds all categories to the sitemap.
     */
    public function renderSitemap()
    {
        $groups = craft()->categories->getAllGroups();

        foreach ($groups as $group) {
            craft()->sitemap->addCategoryGroup($group);
        }
    }
}


How to send email to user when we change his group? I have some user groups with different permission and I need to notify them by email when i switch them to another group.

Is there a way to make it automatic by a plugin or via the code directly?

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

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


With Matrix blocks how can i get the column names From a craft matrix block how do i get the field names when no data has yet been entered?

i can write:

$o_matrix_block->getContent()->getAttributeConfigs();

this will give me an array:

 Array
                    (
                        [id] => Array
                            (
                                [maxLength] => 11
                                [min] => -2147483648
                                [max] => 2147483647
                                [decimals] => 0
                                [type] => number
                                [unsigned] =>
                                [length] => 10
                                [column] => integer
                            )

                        [elementId] => Array
                            (
                                [maxLength] => 11
                                [min] => -2147483648
                                [max] => 2147483647
                                [decimals] => 0
                                [type] => number
                                [unsigned] =>
                                [length] => 10
                                [column] => integer
                            )

                        [locale] => Array
                            (
                                [column] => locale
                                [default] => en_gb
                                [type] => locale
                            )

                        [title] => Array
                            (
                                [required] =>
                                [maxLength] => 255
                                [label] => Title
                                [type] => string
                            )

but I only get the array, when I have entered data.

with out getcontent() but the array elements are wrong

Pls help!

:)

My guess is the array is empty because there is no content.
Try $o_matrix_block->getType()->getFields();


Setting numerical key values in a hash I now know the answer to this but I’m posting it here as a note to myself and in case it helps people.
Let's say you are looping through some entries and storing values in a hash…
{% set myHash = {} %}

{% for entry in entries %}
    {% set value  = entry.intro %}
    {% set myHash = myHash|merge({
        (entry.id) : { intro : value }
    }) %}
{% endfor %}

The entry.id is a variable so it needs to be wrapped in brackets as a dynamic key.
But when you {{ dump(myHash) }} it turns out the numeric keys have just become index numbers – [0] in the truncated example below…
array(10) {
  [0]=>
  array(1) {
    ["intro"]=>
    string(13) "My intro text"
…

But there is a straightforward answer…

The answer is to prepend a letter to the numerical key…
{% for entry in entries %}
    {% set eId   = "e#{entry.id}" %} {# interpolation* #}
    {% set value = entry.intro %}

    {% set myHash = myHash|merge({
        (eId) : { intro : value }
    }) %}
{% endfor %}

Now the hash stores the custom key as a string…
array(10) {
  ["e1234"]=>
  array(1) {
    ["intro"]=>
    string(13) "My intro text"
…

…which you can clean up as needed…
{% for key, properties in hash %}
    {% set key = key|replace('e', '') %}
    …
{% endfor %}

…or factor into lookups…
{% set testId    = 'e1234' %}
{% set testIntro = myHash[testId].intro %}
{# `testIntro`  == 'My intro text' #}

Apparently, it's a PHP thing.
*Interpolation is often preferable to concatenation <-- see item #4.


How to find and replace an  tag? I have  in a Single-Line Text Type in a Table Field Type which is inside a matrix. Now I want to replace all of them through Find and replace option. But when I use  and Yes in Find and Replace Fields respectively then nowhere it is replaced. However a task runs saying Replacing “” with “Yes” but still nothing happens.

Could anyone please help.

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


Locale change based on geolocation I've gotten locales up and running on a site, but would like it to automatically route to domain.com/no for norwegian visitors and domain.com/en for all other visitors. For the life of me, I seem incapable of finding a way using the built in functions.

Do I need to write a plugin to handle this or is there some clever trick I haven't thought of? Can't find anything on geolocation at all.

The mod_geoip2 Apache module with a redirect in .htaccess file.

See: http://dev.maxmind.com/geoip/legacy/mod_geoip2/


Craft 3 - How do you programatically update a field type that is a Category? I have a Category element, locations, which I am trying to update programatically through a Craft 3 plugin that reads data from a CSV.

The locations category has a Field Layout: latitude (Number), longitude (Number), nearbyLocations (Categories).

I am having trouble updating a locations' nearbyLocations field. I am inserting an array of Categories into the nearbyLocations field on the setFieldValues() call.

A TypeError gets thrown deep in the bowels of Craft:

  craft\helpers\StringHelper::toLowerCase() must be of the type string,
  array given, called in
  /home/vagrant/code/vendor/craftcms/cms/src/helpers/Db.php on line 448

Below is my code:

$new_nearby_locations is an array of Category elements.

$new_nearby_locations = [];
foreach ($nearby_locations_slugs_from_csv as $nearby_location_slug) {
  $nearby_location = Category::find()
                    ->slug($nearby_location_slug)
                    ->one();

  if (!is_null($nearby_location)) {
      array_push($new_$nearby_locations, $nearby_location);
  }
}

Update $category's field values.

$category = Category::find()->slug($slug)->one();
$category->setFieldValues([
    'latitude' => $new_latitude, // previously defined
    'longitude' => $new_longitude, // previously defined
    'nearbyLocations' => $new_nearby_locations
]);

Save the changes

 Craft::$app->elements->saveElement($category);

I am suspicious that updating nearbyLocations by inserting an array of Category elements is wrong. If so, what is the right format?

Or am I doing something else wrong.

Thanks in advance!

You should insert an array of ids - not an array of elements - in order to make it work. That's basically it

$new_$nearby_locations[] = $nearby_location->id;

(that's usually better than array push too)

Edit

But your error message sounds more like your slug is an Array or something like this. Do you change any other fields somewhere?


What would it take to make Craft composer based? Since there is a free edition and the plugin architecture looks relatively simple, I'd like to know if anyone has an idea of how this could work. Are there any plans for it?

I'm not 100% sure what you're looking for, but Craft does use Composer to manage its own dependencies.

If you're referring to using Composer in your plugin, it's as simple as creating your composer.json file in your plugin's root folder and running composer update.


How to check if matrix block type is empty or not I have a matrix field with two different block types and I want create a conditional to check if one of the blocks is not empty show it and if it is show the other.  This is not working:

{% for block in entry.page.type('text, intro') %}
    {% if block.type.text != "" %}
        <h3>{{ block.text }}</h3>
    {% else %}
        {{ block.intro }}
    {% endif %}
{% endfor %}

Thanks!

You can fetch the number of entries for the first type, check if you receive an empty array if so fetch matrix blocks of type B

{% set blocksTypeOne = entry.matrixFieldHandle.type('blockTypeHandle1').find() %}
{% if blocksTypeOne|length %}
     // blocks with type A exists
{% else %}
    {% set blocksWithTwo = entry.matrixFieldHandle.type('blockTypeHandle2').find()
    // do something with them
{% endif %}


Search entries by location or name I have a page that lists all of the property developments of my client, and am able to show the lists of the categories in the dropdown fields, but I can't work out how to get them to actually change the results below.

Attached is a screenshot

So how do I get the results below the filters to change to only show the selected item in the dropdown? Am completely stuck on this.

What would be really helpful is a simple walkthrough of what I need to do to make the selected filter output results on the page, as is a complicated subject. And need it explaining in detail if possible, as am a beginner at this.

And here's my code:

    <form class="form-inline" action="{{ url('developments') }}" method="get">

        Location

          <select id="county1" class="form-control" name="county1" onchange="this.form.submit()">
            <option>All</option>
            {% for category in craft.categories.group('counties').order('title asc') %}
            <option value="{{ category.slug }}">{{ category.title }}</option>
            {% endfor %}
          </select>

      ........more dropdown code...
    </form>

      {% for category in craft.categories.group('developments').order('title asc') %}
      ......code for block...
      {% endfor %}

I managed to solve it finally, by starting from what was suggested in the comments. Basically to get the filter to work in Craft I've added action="{{ url('developments') }}" to the form class, which keeps the url on the same page, and then on the  tag I added a name="county1" and some javascript to get it to reload when it's selected using onchange="this.form.submit()"

Then in the results below I set a query variable using {% set query = craft.request.getParam('county1') %} and then created a second variable called queryFilter to search the correct category, based on the selected field {% set queryFilter = craft.categories.group('developments').search(query).order('title asc') %} which to print out the results is then referenced via a for loop {% for category in queryFilter %}

So there's quite a few steps to get it to work, so it's not something that's obvious from the Craft documentation, which doesn't have hardly anything on this subject that I've found.

Here's the code as whole so that it help makes sense to someone trying to do the same type of thing:

<form class="form-inline" action="{{ url('developments') }}" method="get">

    Location

      <select id="county1" class="form-control" name="county1" onchange="this.form.submit()">
        <option value="all">All</option>
        {% for category in craft.categories.group('counties').order('title asc') %}
        <option value="{{ category.slug }}">{{ category.title }}</option>
        {% endfor %}
        </select>

</form>

        {% set query = craft.request.getParam('county1') %}
        {% set queryFilter = craft.categories.group('developments').search(query).order('title asc') %}

        {% if queryFilter %}
        {% for category in queryFilter %}

        ....markup...

        {% endfor %}
        {% endif %}


Get the fields (fieldLayout) of the section. Not the entry Can I get all the fields of a section? For example, I have channel events, I want the fields in that section, but not related to any specific event.

I want to know all the fields (not the values), before actually executing the query.

You can't get the fields from a section since sections have no field layout. Sections have several entry types and those are responsible for the fields.

$entryType->getFieldLayout()->getFields();

You can get all types in a section with

$section->getEntryTypes();


Commerce: Is shippingZone available as a variable on the shipping method selection template? On the template for choosing shipping method, I need to be able to remove/hide some shipping methods from being shown, based on the shipping zone that has been set by Craft, which is based on the address entered, etc.

I'm using Postie for live Fedex rates, along with a couple of custom shipping methods, but Postie adds its live rates to the array of shipping methods regardless of the shipping zone, so I end up with my custom method, plus the Fedex methods, when I just want the custom one.

I'd like to evaluate shippingZone that is set for this cart, and if it is zone A or B, not how any Fedex methods, else, show them. I just need access to the shippingZone to do this.

Like so: https://d.pr/i/JCmTiT

Any way to get it?

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

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

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

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

To the variable class within the plugin add:

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

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

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

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

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

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

    return false;
}

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

This would allow you to do:

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

To determine if an address is within a zone.

Hope that helps.


Unexpected token "name" of value "name" ("end of statement block" expected) Internal Server Error
Unexpected token "name" of value "name" ("end of statement block" expected).

Getting this error on my commerce/products template.  What am I missing?

{% extends 'shop/_layouts/main' %}
{% block main %}

{% for product in craft.commerce.products.limit(5).find() %}

          {% include "shop/_images/product" with { class: 'u-max-full-width' } %}

{% if product.url %}{{ product.link }}{% else %}{{ product.title }}{% endif %}

            {% if product.variants|length %}
                <form method="POST">
                    <input type="hidden" name="action" value="commerce/cart/updateCart">
                    <input type="hidden" name="redirect" value="shop/cart">
                    <input type="hidden" name="qty" value="1">
                    {{ getCsrfInput() }}

                    <select name="purchasableId" class="purchasableId">
                        {%- for purchasable in product.variants -%}
                            <option {% if purchasable.stock <= 0 and purchasable.unlimitedStock == false %}disabled {% endif %}
                                    data-info="product-{{ product.id }}-purchasable-{{ purchasable.id }}"
                                    value="{{ purchasable.purchasableId }}">
                                    {{ purchasable.description }} {{ purchasable.salePrice|commerceCurrency(cart.currency) }}
                            </option>
                        {%- endfor -%}
                    </select>

                    {% if product.unlimitedStock or (product.unlimitedStock == false and product.totalStock > 0 ) %}
                  <button type="submit">{{ "Add to cart"|t }}</button>
                    {% else %}
                        {{ "Out of Stock"|t }}
                    {% endif %}
                </form>

                {% if currentUser %}
                {% set order = craft.commerce
                <select name="options[giftWrapped]">
                  <option value="no">No gift wrap.</option>
                  <option value="yes">Gift wrapped.</option>
                </select>
          .orders.user(currentUser).hasPurchasables(product.defaultVariant).find() %}
          {% if order|length %} I already own this product:  Order #{{ order[0].shortNumber }}
                {% endif %}
                {% endif %}

                {%- for purchasable in product.variants -%}

                        Regular Price: {{ purchasable.price|commerceCurrency(cart.currency) }}

                        Now: {{ purchasable.salePrice|commerceCurrency(cart.currency) }}

                        {% if purchasable.salesApplied %}Sales Applied:
{% endif %}

                            {% for sale in purchasable.salesApplied  %}

{{ sale.name }} {{ sale.description }}

                            {% endfor %}

                {%- endfor -%}

            {% endif %}

{% endfor %}
{% includejs %}
    $( ".purchasableId" ).change(function(e) {
        $(".purchasableInfo").hide();
        $("#"+($(this).find(":selected").data('info'))).toggle();
    });
{% endincludejs %}
{% endblock %}

This line is incomplete:

{% set order = craft.commerce


Images containing EXIF orientation tag are not rotated on upload The Craft CMS instance where I noticed this is running version 2.6, but I updated it last month from version 2.2.

While we did not notice any problem, according to the client, they were able to upload pictures taken with their smartphone and Craft would rotate the images correctly before the update.

As far as I can tell, Imagick is not available on this server (I checked PHPInfo) so GD has always been used. I also made sure that the "rotateImagesOnuploadByExifData" setting is set to true by adding it explicitly to the general config.

Any idea what could be causing the issue and would installing Imagick solve the problem?

After some mail exchanges with Brad and Andris from Craft CMS, it turned out that there is an edge case where the image rotation on upload will not work if the EXIF extension is not installed (which was our case).

There are two solutions to fix this problem:

Install EXIF extension
Install and use Imagick instead of GD.

As Imagick is the recommended image extension for Craft, we ended up installing Imagick on the server.

This is already working with Imagick on Craft 3, and will soon be working on Craft 2 with the next update (> v2.6.2954).


Update Commerce Cart using Ajax Is there a way to update the cart using ajax?

I've tried something like:

$('span.quick-cart-add>p>a').click(function(e){

    e.preventDefault();

    var data                    = {};
    data["purchasableId"]       = $(this).data("id");
    data["qty"]                 = $(this).data("qty");
    data[window.csrfTokenName]  = window.csrfTokenValue;

    $.post( "/actions/commerce/cart/updateCart", data, function(response) {

        alert( "success" );

    }).always(function(response) { console.log(data, response); });

});

I've tried passing "lineItemId" instead of "purchasableId", and also sending to "/actions/commerce/cart/updateLineItem", they all return response.success but the cart is never actually updated.

Am I not sending the right amount of fields/values, or is there something I'm missing when trying to accomplish this via ajax?

Yes, you can do all the normal commerce actions via ajax.

A basic recipe is this...

HTML:

        <form method="POST" id="addToCartForm">
        {{ getCsrfInput() }}
        <input type="hidden" name="action" value="commerce/cart/updateCart">
        <input type="hidden" name="redirect" value="/cart">
        <input type="hidden" name="purchasableId" value="{{ product.id }}">
        <input type="number" name="qty">
        </form>

JS:

        $form = ("#addToCartForm");
        var form_data = $form.serializeArray()
        form_data.push({
            name: window.csrfTokenName,
            value: window.csrfTokenValue
        })

        $.ajax({
            type: 'post',
            url: '/',
            data: form_data,
            success: function(response) {
                if (response.success) {
                    alert(response.cart)
                }
            ...etc

....response.cart will have the updated cart in it.


Twig variables wont work in an include js file? Im trying to put my  code in a included theme.js file. It wont recognize my variables anymore.

Working in .rain file within the html code:

<script>
  jQuery(document).ready(function(){
    var uspicon1 = "{{ theme.usp_1_icon }}";
    var uspicon2 = "{{ theme.usp_2_icon }}";
    var uspicon3 = "{{ theme.usp_3_icon }}";
    var uspicon4 = "{{ theme.usp_4_icon }}";
    jQuery('.icon1').addClass(uspicon1);
    jQuery('.icon2').addClass(uspicon2);
    jQuery('.icon3').addClass(uspicon3);
    jQuery('.icon4').addClass(uspicon4);
  });
</script>

Not working with an include .js file:

  jQuery(document).ready(function(){
    var uspicon1 = "{{ theme.usp_1_icon }}";
    var uspicon2 = "{{ theme.usp_2_icon }}";
    var uspicon3 = "{{ theme.usp_3_icon }}";
    var uspicon4 = "{{ theme.usp_4_icon }}";
    jQuery('.icon1').addClass(uspicon1);
    jQuery('.icon2').addClass(uspicon2);
    jQuery('.icon3').addClass(uspicon3);
    jQuery('.icon4').addClass(uspicon4);
  });

Practicly the same without the script tags. I tested a console.log() and it works fine. I'm guessing it wont recognize my {{ variables }} in .js file.

Anybody know what the syntax is?

There is no "syntax" for it since you don't parse these files with PHP.
You'll have to declare your variables inside a twig file

First Approach

Twig

Javascript

jQuery(document).ready(function(){
    var uspicon1 = myVariable.usp_1_icon;
    var uspicon2 = myVariable.usp_2_icon;
    var uspicon3 = myVariable.usp_3_icon;
    var uspicon4 = myVariable.usp_4_icon;
    jQuery('.icon1').addClass(uspicon1);
    jQuery('.icon2').addClass(uspicon2);
    jQuery('.icon3').addClass(uspicon3);
    jQuery('.icon4').addClass(uspicon4);
});

Second Approach

Or you move your Javascript inside a Twig file

{% js %}
jQuery(document).ready(function(){
    var uspicon1 = "{{ theme.usp_1_icon }}";
    var uspicon2 = "{{ theme.usp_2_icon }}";
    var uspicon3 = "{{ theme.usp_3_icon }}";
    var uspicon4 = "{{ theme.usp_4_icon }}";
    jQuery('.icon1').addClass(uspicon1);
    jQuery('.icon2').addClass(uspicon2);
    jQuery('.icon3').addClass(uspicon3);
    jQuery('.icon4').addClass(uspicon4);
});
{% endjs %}

You can as well attach the attributes in your html - that makes it much easier

Third Approach

Twig

Javascript

var element = jQuery('.icon4')
element.addClass(element.data('class'));


Display related entries on entry page using ElementAPI By following the ElementAPI plugin documentation I was able to set up pagination of entries from the ingredients section on an entry page. Entries from the ingredients section have a relation field called relatedEntries.

Sections structures involved - set as an example:

ingredients
drinks

Desired output would be to show entries related to currently loaded entry from drinks section.

So when I'm at site.com/drink-entry-alfa I'd like to display all related entries from section ingredients to that current entry.

Using Twig I'd request current entry.id and use the .relatedTo() parameter to get related entries.

Here is the example from ElementAPI's documentation:

<?php
namespace Craft;

return [
  'endpoints' => [
    'ingredients.json' => [
      'criteria' => ['section' => 'ingredients'],
      'elementsPerPage' => 10,
      'transformer' => function(EntryModel $entry) {
        return [
          'title' => $entry->title
        ];
      },
    ],
  ],
];

If you want to show all ingredients related to the current entry you'd use the Single Entry Endpoint to pass the entry into the relatedTo param.

'ingredients/<entryId:\d+>.json' => function($entryId) {
  return [
    'elementType' => 'Entry',
    'criteria' => [
        'section' => 'ingredients',
        'relatedTo' => $entryId
    ],
    'transformer' => function(EntryModel $entry) {
      return [
        'title' => $entry->title,
        'url' => $entry->url
      ];
    },
  ];
},


Where do I begin troubleshooting a Form in Craft that is redirecting to /login on iPad and iPhone? I'm not sure how to ask this question.  I have a form. It works just fine on desktops.  However when the form is submitted on iPhone or iPad it never makes it to my controller and resolves to the URL /login with a 404 (which is correct, because I don't have a /login template).

It seems this is true for custom controllers and for Craft forms like 'entries/saveEntry'.
I don't have any routes or other rules that target the 'login' url or any other URLs I'm using in the action parameter that I can determine. I've uninstalled all plugins.  Logs only report the 404 error message but they also seem to report that second page request to the 'login' URL.

I've traced the request through the WebApp.php file all the way to around line 254 where WebApp.php hands things off to the UrlManager:

parent::processRequest();

Any ideas what I should be looking for here?  I'm not even sure why this would be happening and feel I'm getting too deep as most routing in Craft is pretty straightforward.  Could there be any other, obvious settings or anything I might be overlooking?

Just an idea, but are you Allowing Anonymous Access to Actions? Maybe you are logged in on your Desktop, but not on your iPhone.

Example:

protected $allowAnonymous = true;


Use conditional for Element API results? Using the Element API is there a way to get entries by dateUpdated OR dateCreated within the last whatever time, say day?

Or a way to get the entry IDs for both of those criteria then do a query based on just the IDs from both results?

I want to get any entry that was created or updated within the last day using the API but can't quite get it.

If you want the latest of both, unless I'm missing something, you should just be able to query on dateUpdated.

When you create an entry the first time, its dateUpdated gets set to the same date as the dateCreated... So if you query your entries on dateUpdated only, you will get all entries created + updated in your time frame I guess.

As far as code goes, this should do it:

return [
    'endpoints' => [
        'entries.json' => function() {
            $timeframe = (new \DateTime('1 day ago'))->format(\DateTime::ATOM);
            return [
                'elementType' => Entry::class,
                'criteria' => [
                    'section' => 'entries',
                    'dateUpdated' => '>=' . $timeframe,
                ],
                'transformer' => function(Entry $entry) {
                    return [
                        'title' => $entry->title,
                        'url' => $entry->url,
                    ];
                },
            ];
        },
    ]
];


"Your session has ended" modal popping up constantly I know many other folks have posted this question over the years, but unfortunately none of the answers given so far are fixing this for me.

The problem: after logging-in on my staging server (this does not happen locally with MAMP), after a minute the "Your session has ended" modal comes up. Entering my password dismisses the modal for a second then it comes right back.

If I simply reload the page, I am still logged-in, but the modal will come back after another minute. Also, reloading causes my Craft identity cookie to keep its name but change its value.

If I look in /craft/storage/runtime/sessions, I can see that when the modal comes up, a new session file has been created, but is a 0 length file. All the session files have 600 permissions.

What I've tried/checked with no success:

session.auto_start = 0 is already set in the php.ini
session.save_path shows up correctly as
/home/<user>/craft/storage/runtime/sessions/
have tried both the overridePhpSessionLocation and requireUserAgentAndIpForSession config variables set to true and false
have deleted and recreated the sessions folder manually with 777 permissions
have deleted the sessions folder and let it be recreated by Craft (was created with 775 permissions)
I am not using any CDN/caching, or running this on Vagrant.

The server is running Apache/2.2.3 (Unix) and PHP 7.1.0.

Is there anything else I can try?

Thanks to Brad's help, we discovered that the server was misconfigured with an incorrect value for session.cookie_domain. Fixing that resolved the issue in this case.


Printmaker plugin cuts off HTML when rendering PDF link I’ve got a funny Printmaker issue here that I hope you can help me with!

I’m using the following code to generate a link to a PDF:

{% set vars = { title: supportGuides.title, body: supportGuides.body } %}

{% set settings = { orientation : 'portrait', filename:supportGuides.slug~'.pdf' } %}

{% set pdf = craft.printmaker.pdfFromTemplate('support-guides/download', vars, settings) %}

Download PDF Support Guide

In my HTML output, the beginning of the line is stripped out inexplicably:

https://mysite.dev/Printmaker/support-guide.pdf" target="_blank">Download PDF Support Guide

How can I prevent the opening tag from being cut out like that?

Depending on your server setup, DOMPDF might be resetting the output buffer when the PDF is generated. This is a fairly rare issue, but I have heard of it before.

Solution: Try moving the Printmaker PDF-generation code to the top of the template before any output.

For example, at the top of the template...

{% set pdfUrl = craft.printmaker.pdfFromTemplate('support-guides/download', vars, settings).url %}

...and then later, in your output content...

Download PDF Support Guide


Navigation drop-down problem Doing my first site with Craft and UiKit framework I encountered a small problem with dropdown menu. Here is my code:

  {% set entries = craft.entries.section('spb') %}
  {% nav entry in entries %}

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

        {% children %}

    {% endifchildren %}

  {% endnav %}

The point is that my child element looks exactly like parent because it takes its structure. But I don't need neither "class" nor "data-uk-dropdown" element...

...

Is there any workaround? Thanks.

You can use entry.level in a conditional:

{% set entries = craft.entries.section('spb') %}
{% nav entry in entries %}

    {% set liTagAttributes = entry.level == 1 ? 'class="uk-parent" data-uk-dropdown' : 'class="uk-child"' %}

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

{% endnav %}

I set a intermediate var to keep the template code clean. If you don't have many levels in your structure, you could also consider to not use the nav tag at all and nest your navigation levels using hasDescendents and getDescendents(1) (see the example code I posted here).


Handling dynamic content I would like to be able retrieve all entries based on a category selected in a drop down list. From a UX perspective, I would like this to be as seamless as possible and not have to do full page refresh. Is this something I should build using the ElementAPI or is there better way to accomplish this?

The proper way to do this would be to use ElementAPI, however you could create an alternative template with a JSON header coupled with a route.


Best way to provide navigation similar to wordpress What is the recommended way to setup navigation, in a similar way to Wordpress?
So that certain pages or channels can be added, and some may be excluded from the navigation.

Also, what if I want to add a sub-navigation menu on a page to show the child pages?

I would recommend going with a "Structure". This would be the build in way of doing it. You can setup the Structure to have it's own fields to control different "Pages" OR you can have it only use a related entries field and link to other entries anyway you like.

This topic has been discussed quite a lot and you have some great posts here:

Would you use a Structure as navigation over multiple sections (channel entries, indexes and singles)?
Multi-lingual site navigation made of singles, channels and structure
How do you handle complex navigation in Craft?

There's also "Plugin" solutions out there like https://github.com/am-impact/amnav.


How can I retain entry category filtering on entry pages' pagination I have a list of entries, with an option to filter them by categories.

If no filter is applied, clicking an entry should (and does) take you to the permalink URL for that entry, as defined in the section settings:

{{ entry.title }}

Results in:

Gin

However, if a filter is applied, I'd like to "remember" the category, and apply it to the entry's {{ entry.getNext() }}-style pagination.

In other words, imagine a "Consumables" section, with entries of "Gin", "Lasagne", "Noodles", & "Vodka" (with the obvious categories of "Beverages" and "Food"). You filter the entries on the listing page by the "Beverages" category, & click on the "Gin" entry therein. What I want is for the "Next Entry" link on Gin's page to go to "Vodka", not "Lasagne".

So far, the solution I came up with is to modify the URL for when entries are filtered so that the applied filter results in a URL of /consumables/category/beverages. I also have a route set up so that when a fourth segment is present, it tries to match it up with an entry, eg. /consumables/category/beverages/gin.

Now, I don't know if this is the best solution for this issue, but if it is, the remaining problem is figuring out how to modify the links to the entries on filtered pages so that they include the category. Something like {{ entry.url('consumables/category/' ~ entry.category) would be nice, but it doesn't seem to work like that.

So, the solution, as provided by @Maverick in their comment above, is just to pass the variable as a query param (?cat=foo), and check for that query param's value in the template.


Tabs in a Matrix Field In this video it looks like Courtney has tabs (Content | Design) in the individual fields in a Matrix. I can't work out how to do this, is it a plugin? It seems a great way of organising the fields for the editor

https://vimeo.com/294976443

Screengrab:

Thanks for any insight

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


Friendly order number - start sequence at specific number Folks:

I'm having trouble getting my head around friendly order numbers and sequencing. The client has asked for his sequence to start at 11 000.

I can set up a sequence that starts at 1 based on the docs. I can get this to show on the email receipt. What I'd like to know:

How can I get this to start at 11000 and be sure it rolls over to 12000 when orders hit that number?

I'm guessing that whatever the code winds up being, it's set in the "Order Reference Number Format" field, correct?

Once you have created the sequence (starting at 1) with your sequence key/id you can manually reset/update the number in the craft_sequences table in the next column. Obviously if you reset the number, you could get duplicate order reference numbers though if you are not careful.

Alternatively, you could just let the sequence start at 1 and use twig to add 11000 to the sequence number within the Order Reference Number Format.

Your Order Reference Number Format would look like this: 11000+seq('mySeqId')


Importing a database from backup Long story short, I migrated from Craft 2 to Craft 3 but had an issue with a plugin.

Database Exception: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'emailTemplateId' in 'where clause'
The SQL being executed was: UPDATE craft_sproutemail_notificationemailsSET emailTemplateId='barrelstrength\sproutforms\integrations\sproutemail\emailtemplates\basic\BasicSproutFormsNotification' WHERE emailTemplateId='sproutforms-basicsproutformsnotification'

I was told to reinstall Craft 2, update the plugin, and then migrate over to Craft 3.

I moved the Craft 3 files, reinstall Craft 2 and am trying to import the backup database. The problem is my backup database has existing tables that cause errors.

According to this post:

Best practice on importing Craft db backups?

They key is to get the database from the "backup database" function in
the control panel that includes

DROP TABLE IF EXISTS craft_analytics_settings; ...

before it creates all the tables so you shouldn't have to delete any
tables first, been working for us that way since the beginning.

I just don't know where to begin doing so. Is there any suggestions? At this point, I just want to get any version of the site back.

Thank you

Your problem is "only" database.

The first thing - create a another copy of your backup file.

Have you got phpMyAdmin?

You can create a new database and set env file in Craft.

or

You can drop all tables there and try load sql backup. If it is not working try create individual tables first after relations and data.


Nested objects containing array in the Elements API plugin I have an object look which contains an object categories from type of array. Currently I have to do the following to display the categories proper in the response.

'endpoints' => [
        'looks.json' => [
            'elementType' => 'Entry',
            'criteria' => ['section' => 'looks'],
            'transformer' => function(EntryModel $entry) {

                // Create an array of all the photo URLs
                $categories = [];
                foreach ($entry->categories as $category) {
                    $object = (object) ['id' => $category->id, 'title' => $category->title];
                    $categories[] = $object;
                }
                return [
                    'title' => $entry->title,
                    'id' => $entry->id,
                    'categories' => $categories
                ];
            },
        ]
    ]

Is there a more elegant way to doing this? Especially because I'll have a lot of more cases where I have to deal with arrays in objects and arrays in objects and so on.

A structure could looks something like this

[
    {
      name: "ads"
      categories: [
         {
            name: "asdasda"
            subCategories: [
                name: "asdasdsa"
           ]
          }
      ]
    }
]

Seems a bit unnecessary to cast it to an object.  This should work identically:

'transformer' => function(EntryModel $entry) {

    // Create an array of all the photo URLs
    $categories = [];

    foreach ($entry->categories as $category) {
        $categories[] = ['id' => $category->id, 'title' => $category->title];
    }

    return [
        'title' => $entry->title,
        'id' => $entry->id,
        'categories' => $categories
    ];
},


Creating sessions in Twig I am trying to create the PHP $_SESSION equivalent in Craft CMS using Twig.
I have searched very long and I can't find how you can create sessions in Twig.

In previous posts I have found that I should edit some PHP controllers for this to work but I have just started using Craft and Twig and have no idea what controllers to edit to let this work.

So if someone has an solution to this can you please explain it like you are explaining it to a 2 year old kid?

Thanks in advance!

Go to https://pluginfactory.io/ to generate a boilerplate Craft plugin.

API Version: Craft CMS version 2.5.x
PluginName: Twig Session
Description: Anything
Version: 1.0.0
Plugin Author: Anything
Author URL: Anything
Github Name: Blank
Uncheck: Tweet it out
Check: Variables
Click: Build Plugin

Download twigsession.zip and unzip it. Place the unzipped twigsession folder in your craft/plugins folder.  Open craft/plugins/twigsession/variables/TwigSessionVariable.php and change:

public function exampleVariable($optional = null)
{
    return "And away we go to the Twig template...";
}

To:

public function add($key, $value)
{
    craft()->httpSession->add($key, $value)
}

And add a new:

public function get($key)
{
    return craft()->httpSession->get($key)
}

Go to Settings->Plugins in the control panel. Install the "Twig Session" plugin.

From your front end templates, you can now use craft.twigSession.add('foo', 'bar') and craft.twigSession.get('foo').


In Craft 2, check if plugin is available for Craft 3? I'm currently running a Craft 2 site.

I thought there was a quick place to check my plugins to tell if they are ready for 3.0?

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


How to modify element's data during import? I'm trying to modify the element's data during the import. I'm using EVENT_STEP_BEFORE_ELEMENT_SAVE to modify the $contentData. I added some logging at each step and I can see that the $contentData is being changed to " - blah - " for each element in the feed but the original value for 'productDescription' is still being saved for the element.

Event::on(Process::class, Process::EVENT_STEP_BEFORE_ELEMENT_SAVE, function(FeedProcessEvent $event) {
 $event->contentData['productDescription'] = " - blah - ";
});

Here's the logging (irrelevant data omitted):

{before modifying ($contentData) ==== "productDescription":"Freestanding Spring Horses"}
{after modifying ($contentData) ::::: "productDescription":" - blah - "}
{Data ready to import ($contentData) -> "productDescription":" - blah - "}
{After saving ($contentData) ---- "productDescription":" - blah - "}

When I look at the productDescription field for the element that was just imported I see: "Freestanding Spring Horses"

So how can I modify the data that gets saved for each element?

The EVENT_STEP_BEFORE_ELEMENT_SAVE is actually fired after the content has been applied to element attributes and field content.

Instead, you'll want to modify the element's content directly. You can do this via:

$event->element->productDescription = " - blah - ";

As the plugin author, I may change this so it acts as you've suggested. Currently, modifying $contentData essentially does nothing. However, this will require a change to the event where it passes in attributeData and fieldData separately, as they need to be applied separately back on the element. I'll look at adding this in the next version.

Let me know if that doesn't work!


How to create ordered list of categories from multiple category groups I have got multiple category groups and I need to concatenate them into one list for a filter UI element.

At the moment I have the following:

{% set categories = craft.categories.group('colour').all() %}
{% set categories = categories|merge(craft.categories.group('suitability').all()) %}
{% set categories = categories|merge(craft.categories.group('finish').all()) %}
{% set categories = categories|merge(craft.categories.group('shape').all()) %}
{% set categories = categories|merge(craft.categories.group('material').all()) %}
{% set categories = categories|merge(craft.categories.group('edge').all()) %}
{% set categories = categories|merge(craft.categories.group('surface').all()) %}

So now I have a list of all the categories within these groups, I can loop over them as follows:

    {% for cat in categories %}

            <button class="button categories__btn">{{ cat.title }}</button>

    {% endfor %}

But the issue I'm having is that the array of categories is outputting them in the order they're being added. I tried this:

{% set categories = categories.orderBy('title asc').all() %}

But it returns "Impossible to invoke a method ("orderBy") on an array."

You can query on multiple category groups like so:

craft.categories.group('suitability, finish, shape, material, edge, surface').all()

It can also be done with an array:

craft.categories.group([
  'suitability',
  'finish',
  'shape',
  'material',
  'edge',
  'surface'
]).all()

Whenever you invoke .all() it returns an array. As those arrays were merged, they created a larger array. In that last bit of code, categories.orderBy() failed because categories was an array and arrays do not have a method named orderBy.

Hope that helps!


Craft 3 replacement for the Craft 2 fileCache component? I'm upgrading a plugin from Craft 2 to Craft 3 and it looks like the fileCache component is no more.

What has replaced it in Craft 3?

So, that will quite literally be the cache class then... sheesh, what a day this is.


Have auto selected categories with new section entry Is there a way to have auto selected a category when an editor creates a new section entry in the admin?
Kinda like a pre-filled field?

That functionality doesn't currently exist out-of-the-box (as of 2.5.2752), but it would be possible as a custom development: You'd essentially need to create a clone of the Categories fieldtype, add your own config/setting to select the default categories, and then use the prepValue() method of the fieldtype to pre-select those default categories for new entries.


CP Gear Icon missing? Created new Super Admin account Having a bit of an issue with user accounts. I created a new user account group (Super Admin) and assigned my user account to this group. Now that I've done that, I can no longer access the main settings area to edit file permissions, fields, sections, etc.

How can I reset my user account so I can access the settings? More importantly, how will I be able to add additional users with the same admin level as my user account?

'Admin' permission is set on the user level, whereas 'userGroup' permissions are set on the userGroup level. And as far as I know, 'admin' permission should trump any userGroup permissions. I'm not sure how you got locked out, unless you unchecked the 'Admin' checkbox on your user profile settings perhaps? Regardless, setting the 'admin' value to 1 in the craft_users table should be sufficient to restore privileges, without clearing out the other tables.

The only way to create other Admins is to check the 'Admin' checkbox on the user profile permissions tab. The next best thing is create a userGroup (called 'administrator' for example) with all of the permission boxes checked. Keep in mind however, that userGroup permissions need to be set explicitly, so if you create a new section, you may need to update the userGroup permissions to allow the new section.


Are there coding standards I can follow when writing my Craft plugin? I eventually figured out that I could use a code sniffer in Sublime Text, load up a CodeIgniter rule set (not a Craft project, obviously), and see in real time where my formatting, spacing and naming conventions could be improved for consistency.

Is there an equivalent standard for Craft/Yii? I'd like to start forming good habits, and the Sublime Text part is a bonus but not required—I'm most interested in whatever standards there are to reference.

I would strongly suggest that you follow PSR-2 since that is what most of the craft source code adheres to and it is a pretty good standard for general PHP development. Just be aware that craft itself won't pass PSR-2 linting because there are places where it deviates from the standard, two of which are namespacing (craft has a global namespace) and indentation (tabs over spaces) but that is totally fine in my opinion.

Writing clean, readable, intuitive, and testable code should be the end goal of any developer and if that means you have to violate a standards rule here or there, go for it: )


Custom translation file (Not using Locales/Multisite) Is it possible to make Craft 3 load a custom translation/language file? Even if not using multisite or locales?

My use case for this is just to change the way certain things are said. E.g. 'Invalid username or password.' => 'Invalid username or password.', when a user logs in. I wanted to be a bit more descriptive on this.

I can see the translation files in vendor/craftcms/cms/src/translations/[language code here] so I assume theres a way of adding bespoke ones to this, but outside of the vendor folder.

I just can't find it in the docs.

You are looking for Static Translations.

You can add a translation file for English to English (or whatever language you are using) to override default translations. In your case, you'll want to override the app translation category:

In your project folder:

website.com/
└── translations/
    └── en/
        └── app.php

And in app.php:

<?php

return [
    'Invalid username or password.' => 'Something new'
];


Craft CMS: Data too long for column 'cacheKey' at row 1 We have Craft CMS v3 using multi-sites.
We are using cache to cache whole pages based on the absolute URL as some URLs have query parameters which seem to be ignored when not using a key.  So the absoluteUrl is working. Our cache code is:
{% cache using key craft.app.request.absoluteUrl unless craft.app.request.getSegment(1) == 'members' or craft.app.request.getSegment(1) == 'data-summary' %}

One of the sites, Korean, has a few pages that result in an Internal Server Error as follows.
Next Twig\Error\RuntimeError: An exception has been thrown during the rendering of a template ("SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'cacheKey' at row 1

This leads me to believe the URL is longer than 255 characters as the database column templatecaches.cacheKey is varchar(255).
The SQL is:
The SQL being executed was: INSERT INTO `templatecaches` (`cacheKey`, `siteId`, `path`, `expiryDate`, `body`) VALUES ('https://www.mydomain.com/ko/%C3%AC%C2%86%C2%8C%C3%AA%C2%B0%C2%9C/%C3%AC%C2%9D%C2%B8%C3%AC%C2%A6%C2%9D-%C3%AB%C2%B0%C2%8F-%C3%AC%C2%88%C2%98%C3%AC%C2%83%C2%81-%C3%AB%C2%82%C2%B4%C3%AC%C2%97%C2%AD/%C3%AC%C2%B5%C2%9C%C3%AA%C2%B3%C2%A0-%C3%AA%C2%B8%C2%B0%C3%AC%C2%97%C2%85-%C3%AC%C2%88%C2%98%C3%AC%C2%83%C2%81?__geom=%E2%9C%AA', 26, 'site:ìê°/ì¸ì¦-ë°-ìì-ë´ì/ìµê³ -ê¸°ì-ìì', '2020-11-16 18:41:26', '[all the page content here]')"). in /home/mydomain/craftcms/templates/_layout.twig:84

The page URL in the browser has Korean symbols of course, so I guess its too many characters once converted to ascii.
I tried changing it to a Longtext as per another similar issue with a different column, but MySQL will not allow it due to the cacheKey being in the index.
Recommended solutions were to remove the column from the index but to be honest I dont want to start messing with things like this without guidance from Craft.
Is there a Craft recommended approach to this issue please?
Thanks

Is there a Craft recommended approach to this issue please?

The simplest solution would be to update to Craft 3.5+
Template cache data went from being stored in the database to the file system, so this won't be an issue for you anymore.


Manually override feed element in settings using the FeedMe plugin I'm fairly new to Craft so I could be missing out on some basics.
I need to import an events feed (xml) using the FeedMe plugin. The feed has some Link tags.
Ex.
<link rel="sales" type="text/html" href="[LINK TO TICKETSHOP]" />
<link rel="image" href="[LINK TO IMAGE]" />

I've got an Asset field but I can't properly set the Feed Element. It only shows <link/@href> or <link/@type>. If I select @href then it goes though all the elements with @href and generates a lot of errors because some aren't assets.
I've tried <link/@rel="image"> in the default value field but then it imports the same value for all the Events. So all the Entries end up with the same image.
Is there a way to manually define the Feed Element?

This is actually a bit of an issue with XML-based feeds. To Feed Me, both your node's are link nodes. They have different attributes for sure, but Feed Me doesn't identify nodes by their tag and attributes, so it'll look at all tags called link to get the image.

As you've stated, you'll be getting a bunch of errors due to this. Can you restructure your feed at all? The ideal method would be to rename one to <image> for instance.

Right now, there's no way to manually define field mapping, as its already complicated. You're more than welcome to submit a feature request.


(content) Propagation + website in multiple languages Currently we're developing a craft multi-site that has 3 languages (nl, fr, en). It's an upgrade (new style, removing junk, ..) from another cms so the content has to be inputted manually.

We've used a NEO block with different options for the layouting the content.
Now because 80% of the content is the same for the 3 languages, the propagation method for this block was set to "Save blocks to all sites the owner element is saved in".

Almost all of the content has been added, except for the last 20% which differs in some blocks not existing in certain languages.

Now we wanted to change the propagation method, but this deletes all the blocks in the others languages when we save an entry.

Is there a way to change the method without overwriting our data?

Thanks in advance!

Hopefully someone has a better answer than me, but I’ve been in the same situation recently and the way I eventually did it was to output the field for each page in Json using element api, then create a new field that does allow different blocks per locale/site and re-import the content back into that field per locale using feed me.

I left both fields on the control panel for a period of time so the client could check everything was correct, then moved the templates over to use the new field.


How do I assign a language to all datetime outputs I am building a german calendar and am struggling having one language throughout all datetime outputs. The website is set to lang = de but the outputs of the solspace calendar is still english.

Thanks to Kelsey Martens of solspace I found the key. The solspace demo calender uses the .format() method which doesn't translate to the sites language. Now I use the |date filter instead which translates also month and daynames.


Can a plugin add JS or CSS to the control panel? Say for example, I wanted to add a "Expand/Collapse All" button to Matrix fields.

Is there any way to do this, or is this something more appropriate for a feature request for P&T?

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

class MyFunkyPlugin extends BasePlugin
{

  function init() {

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

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

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

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

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

    }

  }

  // rest of the root plugin file
  ...

}

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

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


multi environment nginx, how to check $_SERVER['SERVER_NAME'] I'm trying to have Craft switch baseUrl in general.php based on the environment. The Craft docs tells me to specify the environment in index.php by using logic based on the $_SERVER['SERVER_NAME'] variable.
But this variable doesn't seem to exist in my server environment.

In Craft, neither craft/config/general.php and craft/public/index.php seem to have access to this variable. Where does the $_SERVER['SERVER_NAME'] for each project live and how do I see what it is? Is it supposed to be the same as the server_name block in my nginx config?

$_SERVER['SERVER_NAME'] is a global server variable (not a craft variable) and should be available. I don't use nginx, so I can't comment on where the $_SERVER['SERVER_NAME'] comes from exactly — but a quick google search seems to explain things:

  Blockquote
  nginx – $_SERVER['SERVER_NAME'] is the first entry in the server_name directive.

As far as multi-environment config — this is the method which I have adopted, which has proven to be reliable and flexible. (*The only issue that I've encountered is that it doesn't seem to like non-standard ports — if your using port :8888 in a virtual dev environment for example. Perhaps there is a way to add support for non-standard ports as well).

In your main index.php page (adapted from this craft cookbook recipe).

<?php

// Path to your craft/ folder
$craftPath = '../craft';

// Multi-environment Configuration
define('CRAFT_ENVIRONMENT', call_user_func_array(function($a,$b){if(array_key_exists('APP_ENV',$_SERVER)&&in_array($c=$_SERVER['APP_ENV'],array_keys($a))){return $c;}foreach($a as $c=>$d){$e=$_SERVER['SERVER_NAME'];if((is_array($d)&&in_array($e,$d))||$e==$d){return $c;}}return $b;}, array(

    // Environment Definitions
    array(
        'local' => array('myproject.craft.dev'),
        'dev' => array('myproject.mydevsite.com'),
        'stage' => array('stage.myproject.com'),
        'production' => array('myproject.com'),
    ),

    // Default Environment
    'production',

)));

// Do not edit below this line
$path = rtrim($craftPath, '/').'/app/index.php';

if (!is_file($path))
{
    exit('Could not find your craft/ folder. Please ensure that $craftPath is set correctly in '.__FILE__);
}

require_once $path;

And then in your config.php file (adapted from this stack exchange answer by Ben Parizek):

<?php

/**
 * General Configuration
 */

// Ensure our urls have the right scheme
define('URI_SCHEME', ( isset($_SERVER['HTTPS'] ) ) ? "https://" : "http://" );

// The site url (doesn't work well when port numbers are required, i.e. http://site.com:8888)
define('SITE_URL', URI_SCHEME . $_SERVER['SERVER_NAME'] . '/');

// The site basepath
define('BASE_PATH', realpath(CRAFT_BASE_PATH . '/../') . '/');

return array(

    // default config for all environments
    '*' => array(

        // environment variables
        'environmentVariables' => array(
            'baseUrl'  => SITE_URL,
            'basePath' => BASE_PATH . 'html/'
        ),

        // localization settings
        'siteUrl' => array(
            'en-us' => SITE_URL,
        ),

    ),

    // specific environment config

    'local' => array(

        'devMode'    => true,

    ),

    'dev' => array(

        'devMode'    => true,

    ),

    'stage' => array(

        // environment variables
        'environmentVariables' => array(
            'baseUrl'  => SITE_URL,
            'basePath' => BASE_PATH . 'public/'
        ),

    ),

    'production' => array(

        // environment variables
        'environmentVariables' => array(
            'baseUrl'  => SITE_URL,
            'basePath' => BASE_PATH . 'public/'
        ),

    ),

);


Search through Linklist in Richtext field not working? We launched a website last week based on Craft CMS.
One page contains mainly a list with links to sponsors (entered in a rich text field).

I make a fulltext search (via $critera->search) through the whole website, that works very fine, but it just didn't find the link texts inside a rich text field. (e.g. "Ascot", http://www.unidram.de/en/festival/partner-sponsors)

If I put the same text outside of the link, the search will find it. But that's not the solution, because the text should be the link.

I made a workaround and added Tags for every link to the page (except above given example). That did the job, but is additional work we won't normally do.

At first I clicked on "rebuild the search index".

The search on the website didn't find the page.
This is the template part of the search:

{% set query = craft.request.getPost('q') %}
{% set entries =
 craft.entries.search(query).section('seitenstruktur').order('score') %}

Then I searched for 'Ascot' in craft_searchindex in the DB.

Part of the content of the related field is this:

performance netzuniversitaetsgesellschaft potsdam sponsorenhochdreiascot bristolmedienpartnerpotsdamer neueste nachrichten

While the content looks like this:

So in fact the word 'Ascot' is saved as part of 'sponsorenhochdreiascot'.
As soon as I change it to 'sponsorenhochdrei ascot', the entry will be found by the search on the website.

The HTML Code of the part is:

Sponsoren

HochDrei

Ascot
Bristol

The search index seems to miss some space characters.

The reason that searching for Ascot doesn't match sponsorenhochdreiascot is because Craft doesn't do wildcard or fuzzy searching by default.

If you search for *ascot it would match.  You can all of the supported search syntaxes here: http://buildwithcraft.com/docs/searching

Update:
As of Craft 2.5, you can enable fuzzy searching by default.


What is the best practice in Craft 3 to sync schema between environments? How can sync the schema (fields + categories + tags + sections) from development to staging and live? Or should I do it manually?
Is there any support to make migration script instead of do it from the webpage

With the release of Craft 3.1 you can now keep fields (and other things) in sync between multiple environments.

Read more here: https://docs.craftcms.com/v3/project-config.html


How to modify and save a field in entry on EVENT_AFTER_SAVE in a plugin? I'm trying to build my first little plugin.
I'm stuck with a problem: I need to modify the value of the specific field reference, right after the saving of the entry because the new value is supposed to be based on the same entry id.

This works to get the Entry object and modify the reference:

Event::on(
            Element::class,
            Element::EVENT_AFTER_SAVE,
            function(Event $event) {
                if ($event->sender instanceof Entry) {
                    $idLast = Entry::find('id')
                        ->section('annunci')
                        ->orderBy('elements.dateUpdated desc')
                        ->one();
                    $newRef = 'AV' . $idLast['id'];
                    // assign new reference ? At this point the record is already saved!!!
                    $event->sender->setFieldValue('reference', $newRef);
                    // then what?
                }
        });

I can  manage to retrieve the last saved entry, grab its id, and then create my $newRef, but after that, how am I supposed to resave the entry and update the entry record with the new value?

Second question: I would like to intercept the event only on the first save (aka on creation), and avoiding recalculating the field value on every successive saving. Is this possible?

Thanks in advance for any help, much appreciated!

The accepted answer works but it's for elements in general, of which an entry is just one. Instead, you can skip checking if the element is an entry (if ($event->element instanceof Entry)) and just use the Entry class itself (it extends Element) like so:

use craft\elements\Entry;
use craft\events\ModelEvent;

Event::on(
    Entry::class,
    Entry::EVENT_AFTER_SAVE,
    function(ModelEvent $event) {
        $entry = $event->sender;
        $isNew = $event->isNew;

        if ($isNew) {
            // do your thing
        }
    }
);


How do I make a service only runable by the server? I've created an action that calls a service. The action will be called by a cron task. I want to set this up so that ONLY the cron task can call the action. It's important that it can not be run by any other means (via HTTP, CURL request etc.).

Any ideas on how to do that?

UPDATED (I think this will work):

public function actionMethod()
{
    $this->requireAjaxRequest();

    $key = craft()->request->getParam('key');

    if($key == 'random-string' && $_SERVER['REMOTE_ADDR'] == 'my-servers-ip-address') {
        craft()->plugin_name->actionMethod();
    }

}

Note, this is twinned with a cron tab that looks like:
curl -H "X-Requested-With: XMLHttpRequest" http://sitename.co.uk/actions/plugin_name/Method?key=random-string

Along with Brad's answer, one additional easy check in your code would be to see if the $_SERVER['REMOTE_ADDR'] is the server's IP and then bail if it's not.

That means you'd have to spoof the server's IP address AND guess the key. No better than a username/password combination but should be secure enough IMO.


Rounding down issue with commerceCurrency filter We have a site-wide sale on with 10% off products. On the category/detail page the commerceCurrency filter appears to round down, but when a customer adds an item to the basket, it's shown correctly as the rounded up price. Just had a complaint that it was 1p more than advertised (can you believe!).

Weirdly it rounds up correctly on my dev machine, which is running Ubuntu Desktop. Both running 18.04 LTS and PHP 7.3 with the same packages installed.

I have dug around the commerce/yii codebase and it's ultimately using the vendor/yiisoft/yii2/i18n/Formatter.php asCurrency() method.

I can write my own macro/filter to correct this, but seems like a bug to me.

e.g. Price £16.95, on Sale, £15.255.

{{ product.defaultVariant.salePrice|commerceCurrency(cart.currency) }}

Results in £15.25, but the value 15.26 is saved to commerce_lineitems and added to the cart.

It looks like there is currently a Github issue/feature request for this that is on the Commerce 3.0 milestone: https://github.com/craftcms/commerce/issues/695


Richtext Redactor Field - Standard Layout does not show image button When i use a "Default" Richtext field - the image button displays and works.
When i change Default to "Standard" (the out of the box one) the image button does not display.

All instructions suggest Craft should be handling this to map asset fields to the richtext field. But it is failing for these - Any ideas?

Tested and it works fine for me locally with the stock Standard.json config of

{
    buttons: ['html','formatting','bold','italic','unorderedlist','orderedlist','link','image','video'],
    plugins: ['fullscreen','video'],
    toolbarFixed: true
}

I'd make sure you don't have any other plugins installed that might be interfering and that you've actually got the 'image' value listed under your buttons: key.


Use default field type(s) within my plugin I am just experimenting with plugin development and would like to use the existing dateTime field type. For no other reason than to maintain consistency throughout my application.

Is there a way to call that field as a macro (for lack of better words) in my template or do I just view the generated source from one spot and copy/paste it into my templates?

Thank you!

All the default field types are exposed as template macros in /craft/app/templates/_includes/forms.html. Here's how you could easily create a DateTime input field:

{% import '_includes/forms' as forms %}

{{ forms.date( {
    id : 'myDateField',
    name : 'myDateField',
    value : myDate | default
} ) }}

Note that for the date field, value needs to be a valid DateTime object.

Other input types – such as a plain text field – are created in a similar manner:

{{ forms.text( {
    id: 'myTextField',
    name: 'myTextField',
    value : myValue | default,
    placeholder : 'My Placeholder',
} ) }}

Also note that as _includes/forms is outside the frontend templates folder, it will not be easily accessible outside the CP. If you want to include the form macros in a frontend template, see the following SE thread – I'm partial to Mike Pepper's answer, myself.


Can I auto-update to a specific version of Craft? Is it possible to auto-update to a specific version of Craft CMS – in particular, to 2.4.2726, the last version prior to 2.5?

I'd like to update some sites that haven't got the latest builds, but I'd prefer to wait for the confirmations of plugin developers regarding compatibility with the changes in 2.5. Can I easily move up to the latest 2.4.x release?

It's not possible to auto-update to a specific version of Craft, but it is possible to manually update to one.

You'd just need to download the version you want to update to, then perform a manual update.


Can I run one Craft install on multiple domains and/or subdomains? Is it possible to have one Craft install running multiple domains and/or subdomains?

For example having the same Craft install running a blog Channel at blog.example.com and also an about Single at example.com/about.

Update for Craft 3

Craft 3 includes native support for multi-site installation. Here's a link to the official docs and here's a link to another question with more resources.

Old answer for Craft 1 & 2

Credit to @LukeHolder for the original answer, but here's a bit more detailed and technical answer. Note, this might not be possible on all hosts.

Place the craft folder above both domains, add Craft's index.php file to both domain web roots and set the $craftPath variables in them to point to the same craft folder.

To make a template (say for a blog) only load on its own subdomain instead of whenever any of its slugs are requested on any domains you can run this quick check:

{% set domain = craft.request.getServerName() %}

{% if not domain starts with 'blog' %}
    {% redirect '404' %}
{% endif %}

This will make "blog.example.com/some-blog-post" load, but "example.com/some-blog-post" will redirect to the 404 page.

Links to posts can formatted like this to add the subdomain to the URL:

http://blog.example.com/{{ entry.uri }}

While this isn't an end all solution, it is a functional workaround, especially if it's only for page or two. Use Craft 3 if you can.


Working locally after cloning Craft 3 from Hyperlane I'm trying out the recommended Hyperlane hosting with Craft CMS. After installing on Hyperlane, I then clone the repo on to my local machine and run 'composer install'. That runs fine, and I create a .env file with my db connection details in it.

What happens next is I go to install on my machine by viewing the admin panel but I get the following:

Database Connection Error – craft\errors\DbConnectException
Craft CMS can’t connect to the database with the credentials in config/db.php.
↵
Caused by: Invalid Configuration – yii\base\InvalidConfigException
Connection::dsn cannot be empty.
in C:\Users\clive\OneDrive\Desktop\repos\projectname\vendor\yiisoft\yii2\db\Connection.php at line 602

If I add the database details to config/db.php myself, it works fine. It just seems to be a problem with fetching them from .env.

I'm using WAMP on Windows and have numerous Craft 3 projects already running. This same project is fine if I install locally from scratch. It's just when I start by cloning from Hyperlane.

Any ideas?

Solved. The config/db.php cloned from Hyperlane refers to different env variable names than those used within my .env file. Changing the names in config/db.php solved the issue.

Before:

return [
    'driver' => getenv('DB_DRIVER'),
    'server' => getenv('DB_HOST'),
    'user' => getenv('DB_USER'),
    'password' => getenv('DB_PASSWORD'),
    'database' => getenv('DB_NAME'),
    'schema' => getenv('DB_SCHEMA'),
    'tablePrefix' => getenv('DB_TABLE_PREFIX'),
    'port' => getenv('DB_PORT')
];

After:

return [
    'driver' => getenv('DB_DRIVER'),
    'server' => getenv('DB_SERVER'), // changed
    'user' => getenv('DB_USER'),
    'password' => getenv('DB_PASSWORD'),
    'database' => getenv('DB_DATABASE'), // changed
    'schema' => getenv('DB_SCHEMA'),
    'tablePrefix' => getenv('DB_TABLE_PREFIX'),
    'port' => getenv('DB_PORT')
];


Solspace Freeform - Change "submit" button to "next" I'm building out multi-page forms using composer. And having a submit button at the bottom of page 1 (of 5) is weird.

Can I change the text for the next/previous buttons on pages using the freeform render tag?

I'd prefer that only the last page of the form have the submit button, and all the other pages have buttons that say "Previous" and/or "Next."

If you click on the button that you are adding while making the form, it allows you to change the text to whatever you want it to say. Is that not what you are wanting to do?


Sprout Email Plugin and Multilanguage. How to translate SubjectLine? I use Sprout Email on a website to send transactional email (about 15 different types).

The website gets now translated to 3 languages (DE/FR/EN). It is all fine, but of course, also the transactional emails need to be in three languages.

As Sprout Email does not really support localization, it is still somehow possible by adding 3 "Email Body" fields to the email field layout:

emailBodyDe
emailBodyFr
emailBodyEn

In the mail template, I can then chose which body to use. All good so far.

{% set locale = object.locale %}
{% set emailBody = email.htmlEmailBodyDe %}
{% set emailSubject = email.subjectLine %}
{% switch locale %}
    {% case "fr" %}
        {% set emailBody = email.htmlEmailBodyDe %}
        {# ... set subject!? ... #}
    {% case "en" %}
        {% set emailBody = email.htmlEmailBodyEn %}
        {# ... set subject!? ... #}
{% endswitch %}

But I can't translate the subject line. There is just one fixed field in Sprout Email for that.

Any suggestions how I can also make the subject field multi-language?

First, we'll be adding native support for localization in Craft 3. This is a bit trickier in Craft 2 right now.

Two thoughts. You could set up 3 different emails (each managed in a different language) to be triggered off the same event. Not ideal, but an option.

Aside from that, Sprout Email does run the subjectLine field through renderObjectTemplate so you could use a field in whatever is triggering your event as a dynamic variable for the Subject Line.


How do I delete a Section? I created a section with the wrong slug. I am now trying to delete the section to start over, or supply a new slug, but I can't do neither. How can I delete this section, or supply a new slug?

To delete an Section, go to Settings -> Sections, then click the fain't, almost invisible icon to the right of the offending Section name.


Call Craft controller with cron job I am making a plugin which has a controller to be called every hour.
My server environment is using Anacron.

So I wrote this anacron job:
1@hourly 0 cron.hourly wget http://www.mydomain.com/actions/controlername

And I got 2 questions:

How to log my cron job into a file on the server? (so I can follow when it has been triggered)
What is the best way to log and follow my controller actions in Craft CP side?

In answer to your second question:

  What is the best way to log and follow my controller actions in Craft CP side?

You can log actions to a plugin-specific log file like this:

MyPluginNamePlugin::log('Controller action triggered', LogLevel::Info, true);

Setting the third parameter to true forces the logging to happen, even if you are not in devMode.

The log file can be found in the craft/storage/runtime/logs/ directory and will be called mypluginname.log.

To view logs in the CP visit /admin/utils/logs


What changes are needed if I change directory structure? I can't have a mess of craft stuff at the root of my server so I try to use the following directory structure, with craft files and folders gathered in a craft directory :

craft
-- config
-- modules
-- storage
-- vendor
-- etc...
www
-- index.php
-- web.config
-- etc.

This structure gives me a 500 error.
I assume I can change a path variable somewhere, but I just can't find where. Any ideas ?

You have to define the path to Craft in your www/index.php file.

define('CRAFT_BASE_PATH', __DIR__ . '/../craft/' ); // <- in your case something like this

The constant CRAFT_BASE_PATH must point to your /craft folder


Licence error and update issue cause of same plugin name / handle We developed a plugin with the name "poll" (composer.json: webhub/poll) for one single project. Now we get the licence error in the control panel, cause there is a plugin to buy which has got the same name (https://plugins.craftcms.com/poll).
First Problem: ./craft update all wants to update our plugin: poll 0.0.1 => 1.2.5 but we really do not have a version 1.2.5.
Second Problem: at the control panel plugins page, we have our plugin listed, but with an licence-code-input-field and the error message, that we don't have a licence.
The Reason for all of this is, that we picked the wrong name for our plugin, but renaming it is a pretty dirty task. Does anyone has a got a better idea?
The composer.json looks like this:
{
  "require": {
    "aws/aws-php-sns-message-validator": "^1.6",
    "carlcs/craft-redactorcustomstyles": "3.0.4",
    "cavellblood/craft-string-base64": "1.0.1",
    "codemonauts/craft-instagram-feed": "1.0.5",
    "craftcms/cms": "3.5.3",
    "craftcms/redactor": "2.7.0",
    "doublesecretagency/craft-matrixcolors": "2.0.1",
    "ether/seo": "3.6.6",
    "hybridinteractive/craft-position-fieldtype": "1.0.16",
    "meyfa/php-svg": "^0.9.0",
    "mildlygeeky/craft-redactor-script-buttons": "1.1.1",
    "mmikkel/cp-field-inspect": "1.2.0",
    "percipioglobal/craft-colour-swatches": "^1.2",
    "studioespresso/craft-dumper": "2.0.0",
    "topshelfcraft/environment-label": "3.2.0",
    "verbb/expanded-singles": "1.1.3",
    "verbb/super-table": "2.6.0.4",
    "vlucas/phpdotenv": "^2.4.0",
    "webhub/poll": "0.0.1",
    "webhub/super-sticker-generator": "^0.0.1"
  },
  "autoload": {
    "psr-4": {
      "modules\\laravelmixmodule\\": "modules/laravelmixmodule/src/"
    }
  },
  "config": {
    "sort-packages": true,
    "optimize-autoloader": true,
    "platform": {
      "php": "7.0"
    }
  },
  "scripts": {
    "post-root-package-install": [
      "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
    ]
  },
  "repositories": [
    {
      "type": "path",
      "url": "./plugins/*"
    }
  ],
  "require-dev": {
    "codeception/codeception": "^3.1"
  }
}

You're not gonna like this answer, but... rename your plugin. There's no way to reasonably circumvent all of the conflicts you're going to run into by keeping the same name as an existing public plugin.
If it's a private plugin that you're only using on a single site, consider converting it into a module instead.


"The license located at config/license.key belongs to gridserver.com" Getting this notification at the top of the control panel. This is a solo/free license version of Craft.

It happened after I updated my client's domain's DNS to point to my server.

When I originally set it up, I installed craft on my media temple grid server. The access domain for it at that time was: http://centraldsmhomes.com.s179491.gridserver.com. As soon as I updated the DNS for centraldshhomes.com to point to my mediatemple server, I got the license issue.

Can't seem to find any solutions to this problem.

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.


Editing a users custom fields from a plugin action Craft 3 Does anyone know if there is a block on updating users form an action?

Trying to update a users custom field in craft 3 via plugin in the admin. The admin template calls an ajax call to the action when a drop-down is changed.

This works ok in the base of the plugin, but not in an action.

    public function actionUpdateuser()
    {
        // Update user data
        $userId = 67852;
        $user = Craft::$app->users->getUserById($userId);
        $user->setFieldValue('userCompanyName', 'New Name 2');
        $result = Craft::$app->elements->saveElement($user);

        return $result;
    }

The action fires ok and can return other things just does not update the user.

You should just reference the field as an object property of the user instead of using setFieldValue. I also use the getElements function instead of using the property, but I don't think that makes much difference. The code below should work.
public function actionUpdateuser() {
    // Update user data
    $userId = 67852;
    $user = Craft::$app->users->getUserById($userId);
    $user->userCompanyName = 'New Name 2';
    $result = Craft::$app->getElements()->saveElement($user);

    return $result;
}

Also make sure to add the Craft Users class at the top of the page with:
use craft\elements\User;


autoLoginAfterAccountActivation and postLoginRedirect Can these things work together? Right now, after verification I am being redirected to the dashboard (/gistApp) instead of being routed through /actions/gistClient/loginControl where I have some user business logic. I know that /actions/gistClient/loginControl works because I can direct navigate to that URL.

Here is my general.php

return array(
 'omitScriptNameInUrls' => true,
  'loginPath' => '/gistapp/login',
  'setPasswordSuccessPath' => '/gistapp/login' ,
  'purgePendingUsersDuration' => 'P2M',
  'devMode' => true,
  'autoLoginAfterAccountActivation' => true,
  'postLoginRedirect' => '/actions/gistClient/loginControl',
  'enableCsrfProtection' => true,
  'csrfTokenName' => 'cranberryCsrfToken',
);

If the user being registered has access to the control panel, then you'll want to set the postCpLoginRedirect config setting as well.

Update:

Looks like this was a bug and the same problem as this one.  Should be fixed for the next release.

Update 2:

Officially fixed in 2.2 build 2598.


Matrix field freezes on localhost when I try to make a new entry I have a simple Matrix field that includes two sub-fields: a one line plain text field, and a multi-line plain text field. When I try to create an entry Craft freezes at the Create New Entry screen. When I run the same Craft configuration on a remote apache server no freezing occurs. I'm using a wamp server (on a PC of course). I have the same problem on my wamp-craft setup on PC at work and my PC at home. I've never had a problem with any wamp server before and have never had a problem with Craft. My guess is it's a wamp permissions problem that's specific to the Matrix field (this is my first attempt at using the Matrix field feature). Any suggestions?

I resolved this by upgrading my wampserver from ver 2.5 to 3.03.


How to auto-generate URLs in Craft? How to auto-generate URLs in Craft? For example when we upload images on Tumblr, an URL to the post is created:

http://twotimeselliott.tumblr.com/post/95032257420

And an URL for the image is created:

http://31.media.tumblr.com/2142197f5273865770be44723c2a5df0/tumblr_nagyr9INCE1r6ky2bo1_500.jpg

How can we do that in Craft?

I’m not sure if those Tumblr URLs are just an incrementing ID (so each new post is +1 greater than the last one), or if it’s a randomly-generated string of numbers.

If you just want yours to be the entry ID, all you’d have to do is go into your section’s settings, and set the following:

Do entries in this section have their own URLs? [check]
Entry URL Format: post/{id}

If you want it to be random, that’s a little more difficult. You would need to create a custom text field that stores the random number, attach it to your section, and start entering random numbers into it when creating new entries (or you could write a custom field type that handles this for you). Then your Entry URL Format would get set to post/{customFieldHandle} instead of post/{id}.

As for the image URL, if you have an Assets field that’s responsible for storing the entry’s main image, you can set it to restrict uploads to a single directory, and give it a subfolder based on the entry’s ID/random number custom field handle. (See http://buildwithcraft.com/docs/assets-fields#restricting-uploads-to-a-single-folder)


Is it a security risk to put Craft templates inside public root? Due to the way our build and deployment process works, it's convenient for us to have the Craft templates inside our /public/assets folder together with our css and javascript, instead of in /craft. Does this make the site less secure than having them outside of the public root?

If you’re running Apache, you can put a .htaccess file in your templates/ folder with this in it:

Deny from all

and any HTTP traffic to that folder will be given an Apache 403 error.

Even better, you can put this in there:

# Send the request to Craft
RewriteEngine On
RewriteRule (.*) /index.php?p=$1 [QSA,L]

and the request will be redirected to Craft’s index.php file behind the scenes, the same way that would-be 404s are sent there. So even if you go to /templates/index.html, or any other URL that points to an actual file within your templates/ folder, it will end up as /index.php?p=templates/index.html, leaving Craft to handle the request, which will most likely result in a Craft-based 404.


How can I get a restricted Asset Allowed File Type upload to accept RTF files I have an Asset field for front end users to upload asset files. I prefer to use the 'Restrict allowed file types?' option. However I do not see a choice for Rich Text Format (RTF). I have 'Word' checked, amongst others, but RTF is still rejected. Is there a way to amend this so users can submit RTF files? I am using the Sprout Forms plugin if it matters.

Does this help you? It'll let you add additional file extensions that are allowed to be uploaded.

http://buildwithcraft.com/docs/config-settings#allowedFileExtensions


Craft 3: how can I set an ordered list of all sites, and set a display name different from the site name? The Craft 3 multi-sites is very useful. However, I found the list dropdown while editing an entry confusing and awkward in my case.

I have several sites group, each containing a localized version of the site version:

- International Sales
    - English version
- Production
    - English version
    - French version
— Distribution
    - English version
    - French version

I could have set the sites group like this, to get the right display name when looping on each sites (using craft.app.sites.getAllSites()):

- International Sales
    - International Sales
- Production
    - Production
    - Production
— Distribution
    - French Distribution
    - Distribution France

But as you can see, there's almost nothing that inform the user which site version is using which locale, when editing the sites from the CP's dropdown.

So I had to change each name by something like this:

- International Sales
    - International Sales → English
- Production
    - Production → English
    - Production → French
— Distribution
    - Distribution France → English
    - Distribution France → French

Now I'm facing two problems:

How can I set an ordered list of all sites using Twig?
Because
craft.app.sites.getAllSites() loop through sites using an
alphabetical order, like in the CP. For example, I would like the
Distribution group displayed before the Production group.
How can I set a display name different from the site name? Because
International Sales → English, Production → English, etc. are ugly names, only for visual reference in the CP. I would like to
display their names like in the second version of the list:
International Sales, French Distribution, etc.

If you would use a custom plugin you can create this function in your (I know it's deprecated but I don't know a better name) PluginVariable

public function createSiteQuery(): Query
{
    return (new Query())
        ->select(['id', 'groupId', 'name', 'handle', 'language', 'primary', 'hasUrls', 'baseUrl', 'sortOrder'])
        ->from(['{{%sites}}']);
}

You can then use

{% set sites = craft.plugin-handle.createSiteQuery().orderby({'whatever': 'you want'}).all() %}

And attach as many parameters to the query as you like... If you would like to fetch only sites with a specific language you can do

{% set sites = craft.plugin-handle.createSiteQuery().where('language = "en"').all() %}

You have the full power of Yii2 Query class. You can group your sites as you like, change the order. You'll receive a single SiteRecord or multiple SiteRecords Class reference and you can display whatever information you like in the frontend.

You are free to change the site names in your frontend with a static array map in your php code as well. Create a map with siteHandle => "new display name" and do something like

{{ craft.plugin-handle.displayCustomSiteName(site.handle) }}


How to pass Calendar ID to include I am using Michael Rog's Calendar plug-in and am trying to pass the calendar ID to an include.

Parent template:

{% include '_includes/_calendar' with {'calendarId': '1'} %}

_includes/_calendar:

{% set params = {
    calendarId: '{{ id }}',
    dateRangeStart: 'today',
    dateRangeEnd: '+36 months'
} %}

{% for entry in craft.calendars.eventData(params).events %}

        <time>{{ entry.startDate  | date("D d M") }}</time>

{{ entry.element.title }}

        {{ entry.element.eventDescription }}

{% endfor %}

I am not getting any results for that.

If I change the params to

{% set params = {
    calendarId: '1',
    dateRangeStart: 'today',
    dateRangeEnd: '+36 months'
} %}

I get the results for the correct calendar.

I haven't used includes with variables like this before. Where am I going wrong?

Two things, the variable you send in is called calendarId, not id. Also, inside twig code (like the set function), you don't use twig tags, just the variables directly. Try to change the params object to:

{% set params = {
    calendarId: calendarId,
    dateRangeStart: 'today',
    dateRangeEnd: '+36 months'
} %}


Is it possible to create a cleaner replace? any help on this one appreciated.

I have an entry url that is automated to take several fields and make the the uri.

It pulls the jobTitle, jobCity and jobId fields and then... well... this:

jobs/{jobTitle|lower|url_encode|replace("%20", "-")|replace("%2C","")|replace("%80","")|replace("%E2","")|replace("%8B", "")}-{jobCity|lower|url_encode|replace("%20", "-")|replace("%27", "")}-{jobId}

As you can see... it is a tad... long.

The problem I have is the % stuff that creeps in. Some times the job titles have odd characters that the url encode converts into % symbols. These then break the url and stop craft from displaying the page.

My short term solution has been to add lots of replace's for each one, but is there way to make a replace that uses a wildcard - so if it sees % it replaces the next two characters as well?

Something like:

|replace("%wildcard","-") etc

The current replaces work, but it just feels unwieldy.

Many thanks for any advice.

I'm just gonna post my comment as an answer, because I think it's the way to go.

I'd use the kebab filter instead of url_encode, like this: {jobTitle|kebab}


Plugin settings are rendered wrong I'm trying to create a craft plugin but I have problems with rendering the settings.

Right now I used the same way like here so this is my plugin code

<?php
namespace Craft;

class testPlugin extends BasePlugin
{

    function getName()
    {
        return Craft::t('Test');
    }

    public function getVersion()
    {
        return '0.1';
    }

    public function getDeveloper()
    {
        return 'Secondred Newmedia GmbH';
    }

    public function getDeveloperUrl()
    {
        return 'http://www.secondred.de';
    }

    public function getSourceLanguage()
    {
        return 'de';
    }

    protected function defineSettings()
    {
        return array(
            'accountID' => array(AttributeType::Mixed)
        );
    }

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

    public function hasCpSection()
    {
        return false;
    }
}

And this is my config.twig

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

{% set page = 'Settings'|t %}
{% set title = 'Settings'|t %}

{% set content %}

Hello Craft

{% endset %}

But it always ends up like this

The menu is always rendered inside my plugin Settings and If I just use

public function getSettingsUrl()
{
    return 'test/tools/configuration';
}

the template is rendered correctly but I can't pass my variables anymore (of course in that example there are no, but I use some in my real plugin I just didn't want to show you 1000 lines of unneeded code) .

Could you please tell me what I'm doing wrong? My actual real plugin is nearly ready and this is the last error I have to fix but I don't know how. I need your help thank you very much.

Just remove the extends and set content. The settings twig-code is rendered in a container inside the CP anyways:

{% import "_includes/forms" as forms %}

Hello Craft


How to remove a user from a user group? Ok this should be pretty simple I feel! But I can't find the documentation for it.

I can set assign a user to a user group within a plugin with:

craft()->userGroups->assignUserToGroups($userid, $groupid);

How do I do the reverse and remove a user from a user group?

Cheers.

assignUserToGroups has $groupIds parameter (int / array) preset to null. From the docs:

  assignUserToGroups($userId, $groupIds = null)

So calling the function without that parameter set should do it:

craft()->userGroups->assignUserToGroups(1);


Just upgraded to Craft 3, environmentVariables changed or stopped working In my Craft 2 install, I had many links in Twig templates like this:

<link rel="stylesheet" href="{{ craft.config.environmentVariables.baseUrl }}assets/css/styles.css">

I recently upgraded to Craft 3, and on my local environment, these links no longer work. I get this error:

Neither the property "environmentVariables" nor one of the methods "environmentVariables()", "getenvironmentVariables()"/"isenvironmentVariables()"/"hasenvironmentVariables()" or "__call()" exist and have public access in class "craft\web\twig\variables\Config".

Is there a new way of using environment variables (or whatever they are now called) in Craft 3? I'm just trying to set links to the site URL, but I'd like to have that be different for my local development site and the production site. I found some documentation on how to use environment variables in Control Panel settings, but nothing about Twig templates.

Environment variables don't exist in Craft 3 in the same way that they existed in Craft 2. Instead, they have been replaced with system environment variables and aliases.

The Changes in Craft 3 docs explain what changes have been made and the Environmental Configuration explains how you can set these up and use them.