Order Entries in a Section by Category Titles I currently have entries from a section output simply using a variable like:

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

All of the beer entries have brands assigned to them using the category field.  How can I display the beer entries by brand? I use that beer variable throughout my templates for entry counts, filters, etc. I'd like to try and keep that in tact if possible.

I tried to use this but it seems to do more than what I need: http://craftcookbook.net/recipes/330.  I also tried the groupBy plugin but had no success in getting that to output anything.

After some further research this http://craftcookbook.net/recipes/279 was a better fit for my needs.  The code I ended up using based on the link is below.

{% set beer = [] %}
{% set beerBrands = craft.categories.group('brandCategory').level(1).find() %}
    {% for category in beerBrands %}
         {% set beerRelatedCategories = craft.entries.section('beers').relatedTo(category).find() %}
         {% set beer = beer|merge(beerRelatedCategories) %}
    {% endfor %}


Craft Commerce: discount taxation Is there a way in Craft Commerce to apply discounts to the net order total (eg the subtotal/before tax is applied to the order)? Out of the box this seems to discount from the basket/order total.

Got to bottom of this - is down to how the Tax Rules are set-up.
Taxable Subject was set to Line Item but should have been set to Order Total.


Saving a variant with new stock from plugin Has anyone had any luck trying to save a variant via a plugin?

I'm trying to integrate with a 3rd party inventory system and want to grab the current stock information and update my variants. Currently I have

$criteria = craft()->elements->getCriteria('Commerce_Variant');
$criteria->sku = $sku;
$variant = $criteria->first();

Which works just fine to grab the variant i need, but...

$variant->stock = 99;
craft()->commerce_variants->saveVariant($variant);

This gives me no error, but does not update the variant in the database. What is the method I should be using to achieve this?

I haven't done this, but as a rule I try an mimic what Commerce actually does itself.  In this area I would be inclined to follow the code Commerce uses in it's own completed order handling, found in public function orderCompleteHandler in commerce/services/Commerce_VariantsService.php around line 290:

        $purchasable = $lineItem->getPurchasable();

        // Only reduce variant stock if the variant exists in db
        if (!$purchasable)
        {
            continue;
        }

        $clearCacheOfElementIds = [];
        if ($purchasable instanceof Commerce_VariantModel && !$purchasable->unlimitedStock)
        {

            // Update the qty in the db
            craft()->db->createCommand()->update('commerce_variants',
                ['stock' => new \CDbExpression('stock - :qty', [':qty' => $lineItem->qty])],
                'id = :variantId',
                [':variantId' => $purchasable->id]);

            // Update the stock
            $purchasable->stock = craft()->db->createCommand()
                ->select('stock')
                ->from('commerce_variants')
                ->where('id = :variantId', [':variantId' => $purchasable->id])
                ->queryScalar();

            // Clear the cache since the stock changed
            $clearCacheOfElementIds[] = $purchasable->id;
            $clearCacheOfElementIds[] = $purchasable->product->id;
        }

        $clearCacheOfElementIds = array_unique($clearCacheOfElementIds);
        craft()->templateCache->deleteCachesByElementId($clearCacheOfElementIds);


How can I retrieve number of times an entry has been updated since postDate For a news site I would like to retrieve the number of times an entry has been updated since postDate. Is that possible without using a plugin? Im using craft pro and have versioning enabled for that section.

Update: This came up in another question and I think that I may have found a simple way to do this without the use of a plugin.

{% set versions = craft.entryRevisions.getVersionsByEntryId(entry.id, entry.locale) %}
{{ versions|length }}


Headless CraftCMS as mobile app backend, requiring login Currently building a mobile app with Phonegap, I'm able to use the Element API for many features, but when comes the time to use Craft as a loggedin user, I run out of ideas.

I read a lot of things about headless Craft, but I found nothing about the proper way to authenticate outside the Craft installation itself.

First, I achieved something semi-decent with a JWT system and a custom controller to perform a login validation, which works great, and allow me to read from Craft on my controller (with $allowAnonymous = true;, unfortunately)

(For curious eyes, it's far from perfect: https://gist.github.com/rtrudel/49b96ebc93bfd6774ebbe841426e4b6c )

But I do not want to turn off CSRF globally, and I need to use users/save-user to save user profile in-app). The CSRF token value returned by Craft::$app->getRequest(); seems not right for the next ajax calls, which return Bad request, unable to verify your data submission.

So, this brings me out of ideas... do I have another way to login my user from my mobile app and to retrieve the CSRF token correctly in order to do that, and for all the ajax calls my app will do once logged in?

My JWT approach is maybe not as good as expected in the use-case, perhaps I have to think different? I'm open to any idea to make it work as it should.

My main concern is security and efficiency.

Thank you!

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

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

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


Best approach for Craft Commerce 3 - Variants & Custom Text I'm working on a Craft Commerce 3 project and for something I initially thought would be easy to do with variants seems to have completely thrown me off course so any feedback would be great.
I have a site we're building that has one single product - a car registration plate maker. The user needs to input their reg and select from a few options such as styles, colours, badges etc. Some of these options change the price and some don't.
I've built many sites in Craft CMS but only one previously with Craft Commerce 1. For this Craft Commerce site we built a plug-in that updates the product price and worked great so I'm thinking - do I need to go down the plug-in route or am I missing something with variants?
Variants - when I added variants, ideally I wanted a separate field for each variant option (text style, then one for size and then one for colours and so on) - not just one select field with the possible combinations.
Single Purchase Element - I've also been thinking just build the whole thing completely separate from Commerce so I'd in effect just have JS calculating the final price based on options selected. Then when added to cart, a form would just add the price, all the options selected and the custom text (car reg) to the line item.
Any pointers on this would be really appreciated. I've tried many things but nothing seems to be a solid direction yet.

I built something similar for a client recently (in Craft, but not Craft Commerce) that required two variations (size and colour) where not all variations were compatible (ie. some sizes were only available in some colours, and vice versa).
The approach I took was to mimic Shopify’s approach. Each product has a table with three columns (size, colour, product code). I didn’t include price because it is always the same regardless of variations, but you could add that easily.
On the page Craft outputs a single select input in case JavaScript fails for any reason. Something like:
{% if entry.options|length %}

  <form class="#" action="#" method="post">

      <fieldset class="form__group">

          Options
          <select class="input--select" id="opt__opt" name="options">
            {% for row in entry.options %}
            <option class="sel__option" data-colour="{{ row.colour }}" data-size="{{ row.size }}" data-code="{{ row.code }}" value="{{row.code}} - {{ row.size }} - {{ row.colour }}">Size: {{ row.size }} | Colour: {{ row.colour }}</option>
            {% endfor %}
          </select>

      </fieldset>

      <fieldset class="form__group form__group--submit">
        <button class="button button--submit" type="submit" name="submit" id="opt__btn">Get Quick Quote</button>
      </fieldset>

  </form>

{% endif %}

Then, JavaScript lifts the data out of the select, and creates two new selects and a hidden input (the latter being for the product code). When the selects’ value changes the values in the other input are compared to make sure they are a valid combination (if not that option is disabled):
// holds the option data
let option_data = [];

// holds the inputs so can be manipulated
let size_input,
    colour_input,
    code_input;

// creates the wrapper for the select
function createSelectWrapper(parent)
{
  let sw = document.createElement("div");
  sw.setAttribute("class", "select__wrapper");
  parent.appendChild(sw);
  return sw;
}

// creates the label for the select
function createSelectLabel(parent, target, label)
{
  // create wrapper
  let lw = document.createElement("div");
      lw.setAttribute("class", "label__wrapper");

  // create label
  let lbl = document.createElement("label");
      lbl.setAttribute("class", "label");
      lbl.setAttribute("for", target);
      lbl.innerHTML = label;

  lw.appendChild(lbl);

  parent.appendChild(lw);

}

// creates a select element
function createSelect(parent, id, name, prompt, array)
{
  // create select
  let sel = document.createElement("select");

  // set attributes
  sel.setAttribute("id", id);
  sel.setAttribute("name", name);
  sel.setAttribute("class", "input--select");

  // create default
  let def = document.createElement("option");
  def.innerHTML = prompt;
  def.setAttribute("disabled", "disabled");
  def.setAttribute("selected", "selected");
  sel.appendChild(def);

  let l = array.length;
  for(let i = 0; i < l; i++)
  {
    // create option
    let opt = document.createElement("option");

    // set attributes
    let val = array[i];
    opt.innerHTML = val;
    opt.setAttribute("value", val);
    opt.setAttribute("class", "option");

    // append option
    sel.appendChild(opt);
  }

  // insert select
  parent.appendChild(sel);

  //return reference
  return sel;

}

// creates a hidden field to hold product code
function createHidden(parent, id, name)
{

  // create hidden input
  let inp = document.createElement("input");

  // set attributes
  inp.setAttribute("type", "hidden");
  inp.setAttribute("id", id);
  inp.setAttribute("name", name);

  // add to page
  parent.appendChild(inp);

  // return reference
  return inp;

}

// checks if value is unique
function getValueUnique(arr, val)
{
  let i = arr.length,
      isFound = false;
  while(i--)
  {
    if(arr[i] == val)
    {
      isFound = true;
      break;
    }
  }

  return !isFound;
}

// toggles the enabled/disabled of the submit button
function toggleSubmitBtn(willEnable)
{
  let submitBtn = document.getElementById("opt__btn");

  if(willEnable)
  {
    submitBtn.removeAttribute("disabled");
  }
  else
  {
    submitBtn.setAttribute("disabled", "disabled");
  }
}

// inits the options fields in the form
function initOptions()
{

  // grab the current input
  let orig_options = document.getElementById("opt__opt");
  let orig_options_parent = orig_options.parentNode;
  let orig_options_grandparent = orig_options_parent.parentNode;
  let orig_options_children = orig_options.getElementsByClassName("sel__option");

  // create colour Array
  let colour_arr = [];

  // create size Array
  let size_arr = [];

  // get the current data
  let l = orig_options_children.length;
  for(let i=0; i < l; i++)
  {
    const t = orig_options_children[i].dataset;
    var json = JSON.stringify({
      colour: t.colour,
      size: t.size,
      code: t.code
    })
    option_data.push(json);

    // check if it needs to add to arrays
    if(getValueUnique(colour_arr, t.colour)) { colour_arr.push(t.colour); }
    if(getValueUnique(size_arr, t.size)) { size_arr.push(t.size); }

  }

  //sort arrays (just in case)
  colour_arr.sort();
  size_arr.sort(function(a, b) {return a - b});

  // remove the old dropdown
  orig_options_grandparent.removeChild(orig_options_parent);

  // create select wrapper
  let size_wrapper = createSelectWrapper(orig_options_grandparent);
  let colour_wrapper = createSelectWrapper(orig_options_grandparent);

  // add labels
  createSelectLabel(size_wrapper, "opt__size", "Size", true);
  createSelectLabel(colour_wrapper, "opt__colour", "Colour");

  // create new selects
  size_input = createSelect(size_wrapper, "opt__size", "size", "Choose Size", size_arr);
  colour_input = createSelect(colour_wrapper, "opt__colour", "colour", "Choose Colour", colour_arr);

  // create hidden field to hold product code
  code_input = createHidden(orig_options_grandparent, "opt__code", "product_code");

  // listen for changes on the options
  size_input.addEventListener("change", onOptionChange);
  colour_input.addEventListener("change", onOptionChange);

}

// handles the disabling of an option
function disableOption(option, isSize)
{
  option.setAttribute("disabled", "disabled");
  option.innerHTML = option.getAttribute("value") + " - not available in your chosen " + (isSize ? "colour" : "size");
}

// handles the enabling of an option
function enableOption(option)
{
  option.removeAttribute("disabled");
  option.innerHTML = option.getAttribute("value");
}

// event triggered by one of the option dropdowns changing
function onOptionChange(event)
{

  // identify selects
  let triggerSelect = event.target,
      targetSelect = triggerSelect == size_input ? colour_input : size_input;

  // first set the targetSelect to all be not available
  let targetOptions = targetSelect.getElementsByClassName("option");
  let l = targetOptions.length;
  for(let i = 0; i < l; i++)
  {
    disableOption(targetOptions[i], targetSelect == size_input);
  }

  // next loop through the data to find the ones that should be turned back on
  let triggerProp = triggerSelect == size_input ? "size" : "colour";
  let targetProp = triggerProp == "size" ? "colour" : "size";

  let triggerVal = event.target.value;
  let availVals = [];

  l = option_data.length;
  for(i=0; i < l; i++)
  {
    let jsonData = JSON.parse(option_data[i]);

    if(triggerVal == jsonData[triggerProp])
    {
      availVals.push(jsonData[targetProp]);
    }

  }

  // turn the target select options back on
  l = availVals.length;
  for(i = 0; i < l; i++)
  {
    let m = targetOptions.length;
    for(let j = 0; j < m; j++)
    {
      if(targetOptions[j].getAttribute("value") == availVals[i])
      {
        // found a match
        enableOption(targetOptions[j]);
      }
    }
  }

  // see if there’s a product code
  // (triggerVal is already set)
  targetVal = targetSelect.value;

  l = option_data.length;
  for(i = 0; i < l; i++)
  {
    let jsonData = JSON.parse(option_data[i]);
    if(jsonData[triggerProp] == triggerVal && jsonData[targetProp] == targetVal)
    {
      code_input.value = jsonData.code; // set product code in hidden input
      toggleSubmitBtn(true); // enable submit button
      break;
    }
  }

}

// inits page
function init()
{
  // disable button
  toggleSubmitBtn(false);

  // init options dropdown
  initOptions();

}

// check document is ready
document.addEventListener('DOMContentLoaded', function(){
  init();
})

(Some of that might look verbose, I’ve copied and pasted from a large file and tried to strip out anything irrelevant.)
It’s not live yet, but it’s testing fine.
I think for multi-variant products it’s the simplest approach for the client, because they can update the product in a simple table and it’s reasonably idiot-proof.


Query for Lightswitch in GraphQL I have two entry types that I want to show in a sorted list (by postDate, with paginating).

{
  entries(
    type: ["entryType1", "entryType2"],
    orderBy: "postDate desc",
    limit: 5,
    offset: 0
  ) {
    id
    title
    postDate
    typeHandle
  }
}

This already works. Now the second post type entryType2 should have an optional Lightswitch showInList, where the editor can decide if the entry should appear in the list.

{
  entries(
    type: ["entryType1", "entryType2"],
    orderBy: "postDate desc",
    showInList: 1,
    limit: 5,
    offset: 0
  ) {
    id
    title
    postDate
    typeHandle
  }
}

Unfortunately with the adapted querying I only get the entries of the type with the Lightswitch field declared. When setting showInList: null I get all entries of both entry types, but its ignoring if showInList is true/false for any entry of the second post type.
I tested different QueryArguments but its seems they all have no effect.

Every QueryArgument is handled as if true

Tested:

showInList: 1 → shows only entries of entryType2 with
showInList set to true, ignores entryType2
showInList: ['not 1'] → I thought this would give all entries of entryType2 with showInList set to false but it behaves the same as showInList: 1
showInList: null → shows entries of both types but ignores the showInList field

is there something like showInList: ['1 or undefined'] or any other QueryArgument that would give me the expected result?

Tested in CraftCMS 3.4.17.1 with full schema

Just saw this resolved issue on GitHub: https://github.com/craftcms/cms/issues/5930
So Lightswitch querying should be fixed in the upcoming release CraftCMS 3.5

Edit: there is a workaround until 3.5 is released. see comment by @harrymee123 and my example query at https://github.com/craftcms/cms/issues/5930#issuecomment-657417173


In my element type index view, why is the first column always empty? I am creating a simple Element Type to hold form submissions, but I have hit a wall with this one issue I am having. For some reason I cannot get anything to output in the first column of my element type's index view. I have attached a screenshot. It doesn't matter what column comes first it is always blank. If I inspect the contents of the empty cells there is an empty anchor tag that links correctly to the edit/view entry template. I am not sure what I am doing wrong.

The first column is reserved for whatever your element model's __toString() function returns. By default that is the element's title, if your element type has editable titles. Otherwise you will need to override that method and return whatever makes sense.


How can I add fields in the Sprout design your form? I am using Craft CMS 2.7.3 and sprout forms 2.6.0 version.

I have created a dropdown field in the craft cms -> fields. How can I fetch this field or render this field in the Sprout form design?

Even if I create a tab and save it. It doesn't get saved.

To start off, you will probably want to update to the most recent version of Sprout Forms on Craft 2. As of this message, it looks like v2.6.4 is the most recent version:
https://github.com/barrelstrength/craft-sprout-forms/releases/tag/v2.6.4

Sprout Forms manages Fields independently of Craft so you will not see any of your Craft Fields when editing a Form. Fields you create for a particular form will only be available to that specific form.

Once you create a Form, you should see a New Field button below the initial empty Tab. You will need to add a Field to the Tab before you save in order for the Tab to be saved.


Trying to capture categories field type data in user profile save/update I am trying to capture the data that is selected when a user selects categories within a categories field type (handle: groups) within the Control Panel profile tab.

I was looking thru the Fields, Categories and Content services documentation in the craft docs section and I am still unsure.

I noticed when I did a var_dump of $user:

$user = UserModel::populateModel(UserRecord::model()->findById($id));

It displays the field 'groups' within the _attributeConfigs, but does not show up in private '_attributes' (Craft\BaseModel)

The other fields do show up, for example, occupation shows up within both the _attributeConfigs (its a string) and shows up in private '_attributes' (Craft\BaseModel) with a value of 'Teacher'.

I am unsure how to get the data the user selected when selecting the categories while updating their profile within the control panel.

Usually, its very easy to pull data from a relational fieldtype (Categories, Entries, Assets etc). E.g. if your Categories field handle is memberGroups, you can simply do this:

$categories = $user->memberGroups;

However, your Categories field handle is groups, which conflicts with the built-in UserModel attribute groups, used to pull any user groups the user is a member of – this means that calling $user->groups will give you an array of UserGroupModel instances, ignoring your Categories field.

Also, the $user->getContent()->getAttribute('groups'); method doesn't appear to work for relational fields.

The easy answer: I'd suggest you simply change the field handle for your Categories field to something else – for example memberGroups. Don't worry, you won't lose any data if you do. Then you can just do $categories = $user->memberGroups;.

If you can't or wont change the field handle, here's a workaround:

$criteria = craft()->elements->getCriteria(ElementType::Category);
$criteria->relatedTo = array(
    'sourceElement' => $user,
    'field' => 'groups'
);
$categories = $criteria->find();


Is there any downside to using localized characters in urls? I'm working on a site with both english and spanish locales. Crafts localization features have been awesome so far, and I've been embracing Craft's ability to easily localize URLs.

For example, if I have a section on conservation, the english url would be:

www.mysite.com/conservation

And the spanish one would be:

www.mysite.com/es/conservación

Am I going to run into any technical issues by allowing accented characters in my urls?

There is technically nothing wrong with using them in a URI since they are valid URI characters.  Even multi-byte characters (Chinese, Korean, Japanese, etc.) are valid in the URI (i.e. domain.com/漢字漢字).

However, some people have requested that they not be allowed in the slug, so for the upcoming 2.2 release, we've added a config setting where you can disable the behavior and multi-byte characters are stripped and high-ASCII characters like ó are swapped with their low-ASCII counterparts (o).

I can't speak to any other non-technical reasons why they might not want to be used (SEO, etc.).


What criteria can the Element API plugin accept? I have a structure called "locations" that contains two entry types:

State
Location

I'd like to access specific states at the URL locations/ny.json, for example, and get a JSON object with all the location entries nested under that state.

The entries are already properly nested for HTML output, but I'm trying to get the JSON to match.

This is my elementsapi.php file right now:

<?php
namespace Craft;

return [
  'endpoints' => [
    'locations.json' => [
      'elementType' => 'Entry',
      'criteria' => [
        'section' => 'locations',
        'type' => 'location'
      ],
      'transformer' => function(EntryModel $entry) {
        return [
          'title' => $entry->title,
          'url' => $entry->url,
          'latitude' => $entry->latitude,
          'longitude' => $entry->longitude,
          'jsonUrl' => UrlHelper::getUrl("locations/{$entry->id}.json")
        ];
      },
    ],
    'locations/<slug:{slug}>.json' => function($slug) {
      return [
        'elementType' => 'Entry',
        'criteria' => [
          'section' => 'locations',
          'type' => 'state',
          'slug' => $slug
        ],
        'transformer' => function(EntryModel $entry) {
          return [
            'title' => $entry->title,
            'url' => $entry->url,
            'latitude' => $entry->latitude,
            'longitude' => $entry->longitude,
            'jsonUrl' => UrlHelper::getUrl("locations/{$entry->id}.json")
          ];
        },
      ];
    },
  ]
];

The second endpoint is currently returning a single state entry, but I would like it to return all locations that are a descendant of that state.

Is this possible using a criteria param such as descendantOf?
What criteria params are available?

The $entry variable that is passed to your endpoint’s transformer function will be an EntryModel object - the same type of object you use when interacting with entries in your templates. So everything you can do in your templates you can also do from this function.

The easiest way to get a Structure section entry’s children is with the getChildren() function, which will actually give you a new ElementCriteriaModel object with the descendantOf and descendantDist params pre-set for you. So here’s what your transformer function might look like:

'transformer' => function(EntryModel $state) {
  $locations = $state->getChildren()->type('location')->find();
  $locationsInfo = [];

  foreach ($locations as $location) {
    $locationsInfo[] = [
      'title' => $location->title,
      'url' => $location->url,
      // ...
    ];
  }

  return [
    'title' => $state->title,
    'url' => $state->url,
    'latitude' => $state->latitude,
    'longitude' => $state->longitude,
    'jsonUrl' => UrlHelper::getUrl("locations/{$state->id}.json"),
    'locations' => $locationsInfo
  ];
},

(Since you’re dealing with multiple entries within your transformer function, I also went ahead and renamed $entry to $state, just so it didn’t get confusing.)


Freelance work available I am looking for some support as the developer who built my site (www.lexiconthai.com) is no longer available.

I need a few small changes doing right now (adding meta tag plug in, changing fonts), but will need small jobs doing on a regular basis.

Any idea where I can find someone to support me?

Thanks in advance,

David

This isn't the appropriate place for such a request. You can however register for the Craft CMS Slack where there is a #jobs channel.


Struggling with language switcher for Craft 3 I am using SEOmatic helper class to get localizedUrls which is making life much easier! I would like to only display the localizedurl if the current entry is enable on that site. Below is what I have, has anyone successful done this for craft 3?

{% set currentSite = craft.app.sites.currentSite %}

{% set currentSiteLanguage = currentSite['language'] %}

{% set localizedUrlsArray = seomatic.helper.getLocalizedUrls() %}

{% for localizedUrls in localizedUrlsArray %}

{% if currentSiteLanguage != localizedUrls['language'] %}

    {% if entry is defined %}

        {% if entry.enabledForSite %}

             {{ localizedUrls['language'] }}

        {% endif %}

    {% else %}

         {{ localizedUrls['language'] }}

    {% endif %}

{% endif %}

{% endfor %}

I think I might finally have it working. If someone has a more elegant way to handle the index, tag and category I would love to see it!

        {% if entry is defined %}

            {% for siteForEntry in entry.getSupportedSites %}

                {% set site = craft.app.getSites.getSiteById(siteForEntry.siteId) %}

                {% if site.id != entry.siteId %}

                    {% set entryForOtherSite = craft.entries.id(entry.id).site(site).one() %}

                    {% if entryForOtherSite is not null %}

{{ site.name }}

                    {% endif %}

                {% endif %}

            {% endfor %}

        {% elseif category is defined %}

            {% set currentSite = craft.app.sites.currentSite %}

            {% set sites = craft.app.sites.getAllSites() %}

            {% for site in sites %}

                {% if site.id != currentSite.id %}

{{ site.name }}

                {% endif %}

            {% endfor %}

        {% else %}

            {% set currentSite = craft.app.sites.currentSite %}

            {% set sites = craft.app.sites.getAllSites() %}

            {% for site in sites %}

                {% if site.id != currentSite.id %}

{{ site.name }}

                {% endif %}

            {% endfor %}

        {% endif %}


Image Transform to convert animated GIF to static? I'd like to be able to convert an animated GIF to static for displaying in my mobile view (to reduce bandwidth etc)

currently it seems I can only really set the output to JPG for this (PNG seemed to break the image and if i set to GIF it stays animated)

does anyone have any suggestions for this please? is it possible to extend the Image Transforms?

I guess I'd need to create some kind of plugin? at least that way I could extend it to define a frame number to use etc within my twig template.

thanks,
J.

Is this a one-off image or do you need to use the transform for a bunch of GIFs?

There's no way automated way to tap into transforms currently. (Craft 3 is going to be much better in this respect.)

The other hard part is you're probably going to want to specify what frame of the GIF to use. (Fun fact: years before browsers supported animated gifs, they would simply show either the first frame or the last frame.)

If it were me, I'd set the static frame manually using whatever method you want (Photoshop, Imagemagick/gd on the command line, etc.)

Then use a custom field on the asset to relate that static gif back to its animated counterpart.  Unlike in other CMS, this is where Craft really becomes powerful.


Getting relation through entry field by author I currently have a structure called 'Divisions' that includes 'Team' pages as children. On the 'Team', there is an entries field 'Head Coach', which is an entry created on a 'Coaches' channel.

The coach has an account and has created the entry, so he is the author.

My question is, assuming the coach is the 'currentUser' is there a way to find the relation between that 'Head Coach' entry and the 'Team' if they are related by the author?

I have this code so far:

{% set coachingTeam = craft.entries.section('divisions').type('team').relatedTo({
    targetElement: currentUser,
    field: 'headCoach'
}).find() %}

The only way I've found to do this was to loop through all the teams, and if the currentUser and the author of the 'Head Coach' entry matched, then set the team.

It seems expensive, but I can't figure out how to do this with a relations call.

{% set allTeams = craft.entries.section('divisions').type('team').find() %}
{% set coachingTeam = '' %}
{% for team in allTeams %}
    {% if (team.headCoach|length and team.headCoach.first.author.id == currentUser.id) %}
        {% set coachingTeam = team %}
    {% endif %}
{% endfor %}

This is untested code.

{% set coachingTeam = craft.entries({
  section: "divisions",
  type: "team",
  relatedTo:[
    'and',
    { targetElement: currentUser.id },
    { targetElement: headCoach[0].id }
  ]
}) %}


Correct code for a conditional field? What's the correct way to create a conditional so that a field won't display if it's empty? Is it:

{% if entry.myentry | length %}
    ...
{% endif %}

or

{% if entry.myentry %}
    ...
{% endif %}

I tried both and both seem to work, but I want to make sure they're both correct.

It depends. Technically to determine if a variable is not empty (keep in mind '0' or 'false' are not considered empty), you can use Twig's 'is not empty':

{% if entry.myField is not empty %}...{% endif %}

But in most situations where '0' and/or 'false' are not expected values, then  anything that evaluates to '0', 'false' or 'null' will also work. So,

{% if entry.myField %}...{% endif %}

or

{% if entry.myField|length %}...{% endif %}

If the field is not defined in the field layout however, these may also return an error. In which case you might want to use Twig's 'is defined' first, before checking if the field is empty.

`{% if entry.myField is defined %}...{% endif %}`

In most cases, I just use {% if entry.myField %}...{% endif %}.


how to create a new table in database I followed the document to create a migration, add a schema version, install migration and add schema codes into Install.php. Last I run the migrate up command, but no table is created. Anyone could tell me what is going wrong please?

Not sure I'm following 100%, but in Craft 3, you can add the code necessary to create the new table in your plugin's Install migration.

That will run every time someone does a fresh installation of your plugin.

For the people that already have your plugin installed, you need to create a migration that also creates the table.  The code for the table creation will be very similar in both places.

For the latter, in order for Craft to know a plugin has a new migration is wants to run, you'll need to bump your plugin's schema version number.


General speed/performance on large Craft build Looking for hints and tips on speeding up and streamlining performance of a Craft site as we prepare for launch.

The database is approaching 100MB and whilst it is a fairly large and complex build (multiple locales with fairly extensive use of Matrix) this seems fairly excesive and page load times can be slow. Have looked at the Patrick Pohler post but any general, system level wins would be great as site is crawling a fair bit.

Am getting some performance increase through use of {% cache %} tags but then hitting DeleteStaleTemplateCaches issue qiute regularly which is stopping tasks running.

Any help appreciated!

We are constantly seeing the deleting stale template cache timeout problem on our servers - try increasing the time PHP can execute for to something very long. It seems like clearing the caches counts as one PHP request and not a bunch of tasks, so they can time out.

Good use of cache tags helps, and caching globally can also help - but other than that all I've found to make complex Craft builds faster is using something like Varnish or nginx. Neither of which are simple and both of which I've avoided so far.

PHP7 will help a lot, but I'm not aware of any distro that has PHP7 available yet.


Not able to catch exception sending SMS with Twilio in plugin Not sure if this is the best place to post, but since it's a Craft 3 plugin I'll give it a shot.

foreach ($numbers as $number){
    $number = $twilio->lookups->v1->phoneNumbers($user->mobil)->fetch(['countryCode' => 'NO'])->phoneNumber;

    try {
        // Send SMS
        $twilio->messages->create(
            $number,
            [
                'from' => $settings->twilioNumber,
                'body' => $request->post('body')
            ]
        );
    } catch (\Exception $e) {
        continue; // Continue (loop) to next number as this one failed/is invalid.
    }
}

In short, whenever a $number is invalid, execution stops giving Twilio\Exceptions\RestException [HTTP 404] Unable to fetch record: The requested resource /PhoneNumbers/"the-invalid-number" was not found.

It seems that the catch is never reached.

Note that I have tried replacing \Exception with the following to no avail.

use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\DeserializeException;
use Twilio\Exceptions\EnvironmentException;
use Twilio\Exceptions\HttpException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwimlException;

Regardless, I thought \Exception would catch any exception?

Edit: Here's the solution.

try {
    // Validate the number
    $number = $twilio->lookups->v1->phoneNumbers($user->mobil)->fetch(['countryCode' => 'NO'])->phoneNumber;

    // Send SMS
    $twilio->messages->create(
        $number,
        [
            'from' => $settings->twilioNumber,
            'body' => $request->post('body')
        ]
    );
} catch (\Exception $e) {
    // Number is invalid; continue to next number
    continue;
}

The exception is thrown before you enter your catch block during this line

 $number = $twilio->lookups->v1->phoneNumbers($user->mobil)->fetch(['countryCode' => 'NO'])->phoneNumber;

That's why it can't be cached. Move it into the same block and handle every single possible exception that can be thrown accordingly or via generic \Exception class


Sprout Forms: creating additional text inputs in radio and checkbox labels In a customized Sprout Form field, is it possible to have radio buttons and checkboxes with a value of other have an additional text input in the label? This text input would presumably need to be tied to a separate text field in Sprout Forms which would not render with the other normal plaintext fields.

Is there some way to create a custom field that would make it possible to add the boolean radio/checkbox and input together with something like a matrix? We have multiple "other" + text input fields on the page we're trying to create. No set of options will ever have more than one other option.

The resulting markup might look something like this:

{% if type == 'radiobuttons' %} {# Or 'mycustomradiobuttontype' #}

    {%- for key, option in options %}
      {%- set label = (option.label is defined ? option.label : option) %}
      {%- set value = (option.value is defined ? option.value : key) %}
      {%- set checked = (value == selectedValue) %}

        <input type="radio"
          {%- if id %} id="{{ id }}-{{ loop.index }}"{% endif %}
          {%- if name %} name="{{ name }}"{% endif %}
          {%- if value %} value="{{ value }}"{% endif %}
          {%- if checked is defined and checked %} checked{% endif %}
          {{- dataAttributes -}}>

          {%- if label is defined %}{{ label|raw }}{% endif -%}
          {% if value == 'other' %}
            <input type="text" name="TODO" value="TODO">
          {% endif %}

    {% endfor %}

{% endif %}

The best approach for Radio Button and Checkbox Fields with a Select Other option would be to create a custom field (or fields) using Sprout Forms Front-end Field API.

You could use the existing Radio Button and Checkbox Fields as a starting point and adapt their UI's to present your users with the Select Other option.

You can review the front-end field classes in the Sprout Forms plugin here:

sproutforms/integrations/sproutforms/fields/SproutFormsCheckboxesField.php
sproutforms/integrations/sproutforms/fields/SproutFormsRadioButtonsField.php

It's on our list to add support for front-end fields with this behavior, but it will likely happen after Craft 3 is released. Please reach out if we can help with answering any questions regarding how to implement custom Front-end Fields or custom development support.


Advanced layout: Best way to create a tabbed page with variable content in each tab I'd like to create some tabs on a page, and the content inside each tab. The thing is, inside one tab you will have 3 columns which would have a title/body in little sections for each column. Then in another tab you'll have a masonry list so a title and then a bunch of spans repeated. Another tab might be a body field and an image.

So you can see there is repeating content inside each tab and you can't have a matrix in a matrix as far as I can see.

How would those more experienced approach it? Am I overthinking it? I'd like something that is easy for the customer (of course!).

Updated with diagram and what I had in mind.

This really is a case for nested Matrix fields. It’s something we definitely plan on adding at some point, but that won’t help you here.

Looks like you’re using nested Structure entries to accomplish it right now – that’s probably your best bet for the time being.


How to render a Twig template in the element-api plugin (v2.5) I want to render a Twig template in an API endpoint. Specifically I need HTML for the JSON Feed field "content_html".

My attempts until now (inside the transformer function):

$view = Craft::$app->getView();
$template = $view->resolveTemplate('_blog-entry');

// first attempt:
$html = $view->render($template, ['entry' => $blogpost]);

// second attempt:
$controller = Craft::$app->createController('templates/render')[0];

$html = $controller->render($template, ['entry' => $blogpost])

But in both ways it says "Template not found" even if the path after the "resolveTemplate" is correct.

Has anybody an idea how to do it? Thanks!

You didn’t tell where that Twig file is located, but I assume your issue has to do with being in the wrong “Template Mode”.

Here’s how you can switch mode:

Craft::$app->view->setTemplateMode()

Documentation: https://docs.craftcms.com/v3/extend/updating-plugins.html#rendering-plugin-templates-on-front-end-requests


MailChimp Subscribe Plugin A similar bug with this plugin has be brought up before.

The issue that I'm having is that the post handle mailchimpSubscribe/list/Subscribe doesn't seems to be working correctly. I'm wondering if this is due to any recent changes to Craft CMS? It has worked in the past but it doesn't seem to want to work now.

<form class="newsletter-form" action="#subscribe" method="POST">
<input type="hidden" name="action" value="mailchimpSubscribe/list/Subscribe">

{% if mailchimpSubscribe is defined %}
  {% if (not mailchimpSubscribe.success) and (mailchimpSubscribe.errorCode!='1000') %}

An error occured. Please try again.

  {% endif %}
{% endif %}

<input type="text" name="email" value="{% if (mailchimpSubscribe is defined) and (not mailchimpSubscribe.success) %}{{ mailchimpSubscribe.values.email }}{% endif %}"/>

After submitting the form I get

Unable to resolve the request "mailchimpSubscribe/list/Subscribe".

I thought maybe it was to do with an incorrect character that should be uppercase/lowercase but i've tried a few different combinations.

Has anyone come across the same issue recently?

This is an obvious one, but have you checked the plugin is installed correctly? If you're working from multiple development locations, or servers then perhaps you have a discrepancy between them?


How to render a state abbreviation in Craft Commerce? Folks, this has to be easier than I've made it. In a front-end template, how do you grab a state abbreviation from a customer's address in Craft Commerce?

{$ set addresses = craft.commerce.customer.addresses $}
{% for address in addresses %}
  <-- this is where I want to show the abbreviation -->
  {{ address.state.abbreviation }}
{% endfor %}

I received an answer on Slack from Stephen Callendar.

{% set addresses = craft.commerce.customer.addresses %}
{% for address in addresses %}
  {% if address.getState() %}
    {{ address.getState().abbreviation }}
  {% endif %}
{% endfor %}


Adding entries to an array, one at a time I'm trying to loop through some entries and if they fulfil a certain condition push them into another array of entries.

{% set todaysEvents = []
{# GET TODAY'S ONE-OFF EVENTS #}
{% set todaysOneOffEvents = craft.entries.section('events').eventDate('= '~today | date("Y-m-d")).find() %}

{# ADD TODAY'S ONE-OFF EVENTS TO TODAY'S EVENTS #}
{% set todaysEvents = todaysEvents|merge(todaysOneOffEvents) %}
{# WORKING BEAUTIFULLY #}

{# FOR MULTI-DAY EVENTS THE DATES ARE IN A MATRIX FIELD, SO WE CAN'T FILTER USING CRAFT.ENTRIES #}
{# INSTEAD, WE HAVE TO FILTER EVERY ENTRY IN EVENTS WHICH USE THE MULTI-DAY ENTRY TYPE #}
{% for event in craft.entries.section('events').type('eventWithMultipleDays').find() %}
    {# NOW FIND ONLY ENTRIES WITH A MATRIX BLOCK CONTAINING TODAY'S DATE #}
    {% for date in event.dates if date.multiDate == today | date("Y-m-d") %}
        {# CORRECTLY IDENTIFIES ENTRY AS HAVING TODAY'S DATE IN A MATRIX BLOCK #}
        {# SO NOW WE ADD IT TO TODAYSEVENTS ARRAY #}
        {% set todaysEvents = todaysEvents|merge(event) %}
        {# ERROR: THE MERGE FILTER ONLY WORKS WITH ARRAYS OR HASHES #}
    {% endfor %}
{% endfor %}

What go on? See the error in the last comment. How do I pass only these filtered entries into todaysEvents array?

The merge filter only works with 2 arrays (or 2 hashes), not with an array and an object. I think that the event you are trying to add is not an array, it is a single event (Entry Model). You might try adding the event using [ event ].

{% set todaysEvents = todaysEvents|merge([event]) %}


Enupal Snapshot plugin on local windows environment - can't save path Craft 3.3.20.1, Enupal Snapshot 1.2.7

Works on dev/live server but local can't find path to bins.

Downloaded MXE (MinGW-w64) from https://wkhtmltopdf.org/downloads.html and put in local working directory.

Tried D:\project\enupal-snapshot\wkhtmltox\bin\wkhtmltopdf.exe, without .exe and with slashes going the other way to be more unix-y.

Do I need to do something with the installer? Is the archive enough?

Have emailed Enupal support based on comment suggestion.


How can I loop through all entries that are enabled in at least one locale (not necessarily my current locale)? I am trying to build a sitemap and need to loop through all entries in Craft in order to do this.

However, I am building a multi language site which has entries that can be enabled in a single locale but not in others. Because my default language (English) doesn't have a language code in it's site url (i.e. mysite.com is the English index and mysite.com/ja is the Japanese index), the sitemap template I am using automatically assumes the current locale to be English and any entry that is not enabled in English (but is enabled in another locale) does not get included in the sitemap loop.

Is there any way I can still include entries that are not enabled in the current locale (English) but are enabled in an alternate locale? Or is there some better way I should be doing this?

Just add the locale criteria to your entries call and specify the locale(s) you need to query.

{% set items = craft.entries({
    section: 'sectionHandle',
    limit: null,
    locale:"es_us"
}) %}


Displaying image from related CraftCommerce entry Folks:

I'm using CraftCommerce. I have a main product and several accessories related to that product. Accessories have a title, description, price, and image.

Now, I have no problem pulling the title and description for the related accessories using this:

    {% for relatedProducts in product.relatedAccessories %}
       {{ relatedProducts.title }} - {{ relatedProducts.accessoryDescription }}
    {% endfor %}

Where I've got a problem is pulling the related image. If I use {{ asset.getUrl }} it pulls the first image from the main product entry rather than the image in the related entry. I have tried Get image from related entries asset field and How do I pull related entries and assets? without success.

Any assistance would be great.

Ok first thing take the s off relatedProduct because each loop is over one relatedProduct ;) - then you can do something like this:

{% for relatedProduct in product.relatedAccessories %}
   {{ relatedProduct.title }} - {{ relatedProduct.accessoryDescription }}
   Url: {{ relatedProduct.yourAssetHandle.first().url }}
{% endfor %}

You should probably protect that by first checking {% if relatedProduct.yourAssetHandle.first()|length %} unless you can be completely certain you will have an asset (required field for example).


Display news (from a channel) on the Homepage I have started my very first Craft project (coming from EE) - so, apologies if this is a to simple question.

On the homepage I want to display the latest news entry - and short excerpts from a few older ones. No other type of entries on the page.

Question: Should the section type of the homepage be a "channel" then, or a "single"? Or is there a something completely different way to do this?

Hi Magnus and welcome to Craft.

Your homepage should ideally be a Single. As the Craft site says these are one-off pages that have no special requirements or hierarchy.

You would then create a Channel called News (or Blog, whatever you require) and here you would have entries for each news article on your site.

Once you have everything set up, outputting the latest news article on your homepage is a very simple process:

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

{% if latestNews|length %}

    {% for article in latestNews %}
        <p>{{ article.title }}</p>
    {% endfor %}

{% endif %}

This code would sit in your index.twig template and grabs the 1st entry in the News section. Simple!


AJAX Front-end User Registration Form not displaying macro errorList I have a user registration form on my Front-end which is submitted with Ajax which works fine.

The only problem is that if I have any errors, like an already in use username they wont be displayed. The still function but just wont be displayed. Same goes for empty required fields.

Without using AJAX the errors are displayed as they should though.

Here is my Ajax:

$('#registrationForm').submit(function(ev) {
    ev.preventDefault();

    var data = $(this).serialize();

    $.post('/', data, function(response) {

    console.log(response);

    if (response.success) {
        console.log("success")
    } else {
        console.log(response.error);
    }
});

Here is my form:

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

    {% macro errorList(errors) %}
        {% if errors %}

                {% for error in errors %}

{{ error }}

                {% endfor %}

        {% endif %}
    {% endmacro %}

    {% from _self import errorList %}
    <input id="firstName" type="text" name="firstName" placeholder="Naam">
    <input id="username" class="hidden" type="text" name="username"
        {%- if account is defined %} value="{{ account.username }}"{% endif -%}>
    <input id="email" type="text" name="email" placeholder="E-mailadres"
        {%- if account is defined %} value="{{ account.email }}"{% endif %}>

    {% if account is defined %}
        {{ errorList(account.getErrors('email')) }}
    {% endif %}

    <input id="wachtwoord" type="password" name="password" placeholder="Wachtwoord">

    <input id="leeftijd" type="text" name="leeftijd" placeholder="Wat is je leeftijd?">

    {% if account is defined %}
        {{ errorList(account.getErrors('password')) }}
    {% endif %}
    <input id="userPhoto" class="button sec hover-btn blue-btn" type="file" name="userPhoto" value="{{ acc.uploadProfielButton }}">
    <input class="button hover-btn orange-btn" type="submit" value="{{ acc.aanmeldenButton }}">
</form>

No errors are displayed because you are using AJAX to post your data. The errorList function works only with "regular" POST flow. When ajax response is available you need to grab it and append errors yourself.

Try this code:

$('#registrationForm').submit(function(ev) {
    ev.preventDefault();

    var data = $(this).serialize();

    $.post('/', data, function(response) {

        if (response.success) {
            console.log("success")
        } else {
            console.log(response.errors);
            appendErrors(response.errors);
        }

    });
});

function appendErrors(errors) {
    $.each(errors, function(field, errors) {
        appendError(field, errors);
    });
}

function appendError(field, errors) {
    var $errorList = $('<ul>');

    $.each(errors, function(index, error) {
        $errorList.append($('<li>').html(error));
    });

    $("input[name=" + field + "]").after($errorList);
}


Why does my Twig for loop not get a Key? I'm looping through a Matrix field called Documents. The matrix has three fields:

Document (an asset field)
Document Title (plain text field)
Document Type (a category field)

I need to loop through the matrix and output all the rows, but grouped by Document Type. This seems like it should work:

{% set grouped = entry.documents | group('documentType.title') %}

{% for categoryTitle, groupedRows in grouped %}
    TITLE HERE: {{ categoryTitle }}

    {% for document in groupedRows %}
        <br/>{{ document.documentTitle }}
    {% endfor %}
{% endfor %}

The problem is that {{ categoryTitle }} is blank. Nothing in it. The code runs, but the key is blank. The grouped rows are populated.

What's wrong?

I'm guessing that you'd like {{ categoryTitle }} to be the current groups documentType.title?

If so, I would output document.documentType for the first document in each group using {% if loop.first %}.

Complete code suggestion would be:

{% set grouped = entry.documents|group('documentType.first().title') %}

{% for groupedRows in grouped %}
  {% for document in groupedRows %}
    {% if loop.first %}
      {{ document.documentType }}
    {% endif %}
    {{ document.documentTitle }}
  {% endfor %}
{% endfor %}

EDIT: updated to use carlcs group filter suggestion.


Default locale in console commands is always en_us rather than the site default The default (and only) locale for my site is en, however, when trying to query the ElementService from a console command without specifying the locale in the criteria, the locale used in the query is en_us. As such, the query returns no results. For example:

<?php
namespace Craft;

class ShoppingCommand extends BaseCommand {

    public function actionListOrders() {
        $c = craft()->elements->getCriteria(ElementType::Entry);
        $c->section = 'orders';
        $c->limit = 5;
        foreach($c as $o) {
            print $o->id . ": " . $o->title . "\n";
        }
    }
}

prints nothing, even though there are entries present for the default locale en.

Printing out the query object print_r(craft()->elements->buildElementsQuery($c)); shows the :locale parameter being passed to the sql query as en_us. But this locale doesn't even exist for the site (select * from craft_locales shows only en).

The only way I could find to work around this is by adding the site's default locale to the criteria manually with:

$c->locale = craft()->i18n->getPrimarySiteLocale();

However, since the query works find without this when run in a controller, I'm skeptical that it should be needed. Is this is a bug, or is there some extra configuration i'm missing? Having some parameter to specify the locale to use for commands would make sense in mutli-locale environments, but I would still expect the default to be the site's default rather than en_us.

Turns out this is actually a bug, too.

It's going to end up being more involved to fix than a build release probably warrants, though, so it'll have to wait for the upcoming 2.3 release to get a proper fix.

In the meantime, you'll be able to get by with your workaround of manually setting it.

$c->locale = craft()->i18n->getPrimarySiteLocale();


getPrev and getNext stop working after 100+ entries Prev and Next stop working after 100 entries..  There are no template errors.  I can access the pages for the individual entries - but Next and Previous are blank.

Here is my set up...

{% set criteria = {section: 'posts'} %}
{% set prev = entry.getPrev(criteria) %}
{% set next = entry.getNext(criteria) %}

{% if prev %}
my html ...{{ prev.url }} ...my html
{% endif %}

The same holds true for next.

Once there was a template error referencing a null value on prev.url - and referencing an entry ID that no longer appears to exists.  However, I discovered that I had had prev under if next.  That is the only time I was able to produce a template error.

In your criteria, you need to set the limit to null as by default Craft only returns up to 100 entries.

See: Default Craft entry limit


Multiple forms on same page.. Solspace Freeform When there's two forms on a single page. (E.g Newsletter Sign up and Book a Tour) The submit button obviously validates both forms.

How can I differentiate between them both and make them independent?

{{ craft.freeform.form("newsletter", {
    id: "form-newsletter"
    }
}).render() }}

The other form is loaded into the page through a template via a custom field.

Thanks for reporting this issue. We'll have a fix available for this in the next release of Freeform. :)

In the meantime, affected customers can contact us for a patched copy.


Is it possible to subscribe a user to Mailchimp during checkout in Craft Commerce? Our checkout is only for registered users.

Ideally you'd just provide a checkbox that says: Subscribe to Mailchimp?

Any ideas?

We offer this during checkout.

We use André Elvan's: https://github.com/aelvan/mailchimp-subscribe-craft

We implement it simply as an ajax call that happens in the background as the order is submitted for payment:

    if ($('#subscribeMC').prop('checked')) {
        // Fire this off asynchronously and with any luck it just works...
        $.ajax({
            type: 'POST',
            url: '/actions/mailchimpSubscribe/list/Subscribe',
            data: {
                CRAFT_CSRF_TOKEN: window.csrfTokenValue,
                email: email,
                mcvars: {
                    FNAME: firstName,
                    LNAME: lastName,
                }
            }
        });
    }

One thing I like about our implementation is that I added a 'check if subscribed' function to the above plugin and we only show the checkbox if they're not already on the list - it's one of my pet hates when I keep seeing a newsletter box for a list I know I am already on.

$.ajax({
    type: 'POST',
    url: '/actions/mailchimpSubscribe/list/checkIfSubscribed',
    data: {
        'CRAFT_CSRF_TOKEN': window.csrfTokenValue,
        'email': email
    },
    success: function(response) {
        //They're on the newsletter - hide the subscription form
        if (response.success) {
            $container.hide();
            $heading.hide();
        }
        //They're not on the list, bait the hook
        else {
            $container.fadeIn('fast');
            $heading.fadeIn('fast');
        }
    },
    //something went wrong, might as well show the form
    error: function(response) {
        $container.fadeIn('fast');
        $heading.fadeIn('fast');
    }
});

If you wanted to avoid JS/ajax, then I guess you could implement your own controller that you submit to at any point (e.g. intercept the commerce pay controller) - that first does your Mailchimp stuff and then calls the native controller using Craft's runController.

But the above JS works well and we get a bunch of newsletter subs with it every day...during subscribe checkout is by far the most effective way of increasing your mailing list we've found.  (And, please, whatever else you may do, do not implement a page covering newsletter signup popover!!)


Ordering entries by Entry Type I want to sort entries by Entry Type in the order they appear when you create/edit entry types in the CP (which can be reordered). I can't seem to find an argument for order() that works. Can this be done?

I don't think you can order by the types order, or get the types in your template (I might be wrong there though), but maybe you don't need to let this be completely dynamic? I guess the user won't be able to add new types or change the order of them after the site is made? If so, couldn't you just make an array of the types and do somthing like this:

{% set typesInOrder = ['handle1', 'handle2', 'handle3'] %}

{% for type in typesInOrder %}
    {% for entry in craft.entries.type(type) %}
         {{ entry.title }}
    {% endfor %}
{% endfor %}

There is of course some limitations, especially if you need to paginate the results. And it's bad for performance to do the craft.entries queries several times, especially if you have many entrytypes. Just thought I'd share this in case it's helpful for your usecase.


Plugin Controller actions for entering data and redisplaying form or redirecting I'm getting confused with how to submit forms to a plugin controller as shown briefly in the docs.

I'm making a multi-page form, which saves the data on each page to the session, sending it elsewhere at the end of the process. None of the data is related to things (Entries, etc) in Craft.

But I don't understand how to do the:

Display form
POST form
Either:

Validation passes, so save the data and redirect to the next page
Or, fail validation and display the same form again

Let's say the first page asks for firstname and surname, and the second for date of birth. I have a model per form page, for validation, so there's plugins/myform/models/myForm_NameModel.php:

<?php
namespace Craft;

class MyForm_NameModel extends BaseModel
{
    protected function defineAttributes()
    {
        return array(
            'firstname' => array(AttributeType::Name, 'maxLength' => 100),
            'surname' => array(AttributeType::Name, 'maxLength' => 100),
        );
    }
}

And I'll have a similar model for the form on the date of birth page.

Then I have plugins/myform/controllers/myForm_FormController.php

<?php
namespace Craft;

class MyForm_FormController extends BaseController
{
    protected $allowAnonymous = true;

    // Display name form.
    public function actionName()
    {
        $this->renderTemplate('myForm/forms/name');
    }

    public function actionSaveName()
    {
        $this->requirePostRequest();

        $post = craft()->request->getPost();
        $nameModel = MyForm_NameModel::populateModel($post);

        if ($nameModel->validate()) {
            // TODO: Store in session.
            $this->redirectToPostedUrl();
        }
        else
        {
            craft()->userSession->setError('Error!');
            craft()->urlManager->setRouteVariables(array(
                'nameModel' => $nameModel
            ));
        }
    }

    // Display date of birth form.
    public function actionDob()
    {
        $this->renderTemplate('myForm/forms/dob');
    }
}

And in plugins/myform/templates/forms/name.html:

<form method="post" action="" accept-charset="UTF-8">
    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="myForm/form/name">
    <input type="hidden" name="redirect" value="myForm/form/dob">

First name: <input type="text" name="firstname" value="{% if nameModel is defined %}{{ nameModel.firstname }}{% endif %}">

Surname: <input type="text" name="surname" value="{% if nameModel is defined %}{{ nameModel.surname }}{% endif %}">

    <input type="submit" value="Submit">
</form>

I'm missing something at the moment because all I get is the name form, and submitting it simply refreshes the page; no errors, no submitted values displayed. I think I'm confused by the action and redirect in the form template, and exactly what the controller actions should be doing.

I'm also not sure if using AttributeType::foo is correct when defining these models, seeing as they're not things that will be saved in the database?

I have something working, although I'm not sure it's the "correct" way of doing this.

I've got rid of the actionSaveName() method and my actionName() method in the Controller is now like this:

public function actionName()
{
    $templateVars = array();

    if (craft()->request->isPostRequest())
    {
        // The form was submitted.
        $post = craft()->request->getPost();
        $nameModel = MyForm_NameModel::populateModel($post);

        if ($nameModel->validate()) {
            // Successfully posted the name.
            // TODO: Store in session.
            $this->redirectToPostedUrl();
        }
        else
        {
            // Something was wrong with the submitted names.
            $templateVars['errors'] = $nameModel->getErrors();
            $templateVars['nameModel'] = $nameModel;
        }
    }

    // Either the first time viewing the form, or there were errors.
    $this->renderTemplate('myForm/forms/dob', $templateVars);
}

And then plugins/myform/templates/forms/name.html is like this:

<form method="post" action="" accept-charset="UTF-8">
    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="myForm/form/name">
    <input type="hidden" name="redirect" value="{{ actionUrl('myForm/form/dob') }}">

    {% if errors is defined %}
        {% if errors.firstname is defined %}
            {{ errors.firstname[0] }}
        {% endif %}
        {% if errors.surname is defined %}
            {{ errors.surname[0] }}
        {% endif %}
    {% endif %}

First name: <input type="text" name="firstname" value="{% if nameModel is defined %}{{ nameModel.firstname }}{% endif %}">

Surname: <input type="text" name="surname" value="{% if nameModel is defined %}{{ nameModel.surname }}{% endif %}">

    <input type="submit" value="Submit">
</form>

This seems to work fine but feels wrong in a few little ways:

The hidden action input is ignored; I can put anything in that and nothing changes; I really don't understand how that is supposed to be used.
If the submission is successful the URL redirected to is like /index.php?p=actions/myForm/form/dob rather than /actions/myForm/dob.
Mainly, that it feels quite different from the description in http://buildwithcraft.com/docs/plugins/controllers .


How to apply a "without" filter in a plugin I have and array of with allEntries and an array with alreadyUpdatedEntries. I would like to filter out all the alreadyUpdatedToEntries to end up with an array of notUpdatedEntries. Like how it is done in Twig.

{% set notUpdatedEntries = allEntries|without(alreadyUpdatedEntries) %}

Is there a similar Craft PHP method to do this in my plugin? Can I maybe include this in my elements criteria query?

You can use similar logic that Craft uses for the withoutFilter in CraftTwigExtension.

public function withoutFilter($arr, $exclude)
{
    $filteredArray = array();

    if (!is_array($exclude))
    {
        $exclude = array($exclude);
    }

    foreach ($arr as $key => $value)
    {
        if (!in_array($value, $exclude))
        {
            $filteredArray[$key] = $value;
        }
    }

    return $filteredArray;
}


Display custom product field in cart How would I access a custom product field in the cart. I did it with the snapshot attribute like so:

{# cart.html #}

{% for item in cart.lineItems %}
  {{ craft.commerce.products.id(item.snapshot.product.id).first().myCustomField }}
{% endfor %}

But is that the right way to do it?

You could set a variable to access the products model like so:

{% set product = item.purchasable.product %}

and then access the fields like normal {{ product.myCustomField }}

The snapshot holds the products fields as they were at the time the line item was added to the cart. So depending on how you're using the fields would dictate the method used.

You could also access the snapshot slightly cleaner using
{{ item.snapshot.product.myCustomField }}


Force an image transform to images inserted into Redactor I know this essentially does the trick, however I was hoping I could apply a custom transform to all image assets if one hasn't been defined by the user.

The problem I have is trying to convey the importance of defining image transforms to my "less-than-savvi" clients.

So, as a fallback I'd like to know if I can query any images and apply an image transform. I feel like this might be a step in the right direction, but not sure what to do next:

{{ include(template_from_string(entry.body)|raw) }}

Cheers.

I wrote a plugin called Retcon which is able to apply bulk image transforms to images in HTML/Redactor content:

{{ entry.body|retconTransform({ width : 1024 }) }}
{{ entry.body|retconTransform(myAwesomeTransform) }}

There's currently no way to tell Retcon to only transform images that hasn't already been transformed, though – so this might not be exactly what you're looking for.


Amazon S3 endpoint is wrong when accessing Asset info Amazon S3 is returning the following error for a file in a bucket that is in Ireland (s3-eu-west-1):

  [PermanentRedirect] The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.

The URL prefix is setup to be s3-eu-west-1.amazonaws.com, and using the S3.php class standalone with my key/secret, the bucket are listed as being in eu-west-1.

I tried logging out inside S3->getObjectInfo() what endpoint Craft/S3.php was using when accessing the file.

For some reason, it's s3-us-west-2.amazonaws.com. Isn't that weird?

The problem ended up being that I had completely missed the location setting in the database column of the Asset source.


Clean install - Service Unavailable message at setup Folks:

I installed CraftCMS. All files are uploaded, chmod done, and everything else. I visit the page http://example.com/admin to go through setup and I get the message "Service Unavailable. Our site is temporarily unavailable. Please try again later."

I checked .htaccess and the config file. All is well.
I changed isSystemOn from "null" to "true". Same message.
I contacted my host to see if Craft will run on the server. All
requirements are met.

Since this is a Craft message it seems pretty clear that the database is being connected to but nothing is happening. Any assistance would be great.

Had similar issues. Found the problem was in the .htaccess file.
Without code fixes, visit http://example.com/index.php/admin
instead of http://example.com/admin

Then fix your htaccess file later.


How do you set up live preview for headless Craft CMS installs connected to Gatsby? How do you set up live preview for headless Craft CMS installs connected to Gatsby?

When you enter live preview in the cp, it's an iframe to the URL you've set up for that section, including a token=xxxxxxxx, you need to pass this along to your GraphQL (assuming that's what you use), to let it know it's not supposed to fetch data from the database.

Andrew Welch has a bit more info about it here: https://nystudio107.com/blog/headless-preview-in-craft-cms

As well as another question here (based on vue, so you probably need to change som stuff for Gatsby): Live preview using Vue.js


Is there an issue with debug mode in 2.4? We've recently updated and now find that in debug mode we sometimes don't get the expected stack trace on error.

With a 500.twig file in place it displays that, otherwise we get the default Internal Server Error summary view.

Is this something that broke in the recent update?

There is a bug in the current release that's been fixed for the next one where PHP Errors/Warnings/Notices won't have the full stack trace shown even if the site is in devMode.


Add a column to entries list using defineAdditionalEntryTableAttributes? What I'm looking for is to add in a custom column that either has a checkmark or not based on if the entry's slug has a certain set of characters.

Example:

This would get a check: sluggyslug-SC_01234X

This would not: sluggyslug

I would be looking at the slug and matching via regex for the SC_XXXXX portion.

I see there is the hook defineAdditionalEntryTableAttributes and getEntryTableAttributeHtml but I get an error when I try to set them.

The code I tried in my plugin:

public function defineAdditionalEntryTableAttributes()
{
    return array(
        'something' => "Some Column",
    );
}

public function getEntryTableAttributeHtml(EntryModel $entry, $attribute)
{
    if ($attribute == 'something')
    {
        if (preg_match("/(?:SC_\w{5})$/", $entry->slug)) {
          return '
&#x2713;
';
        }
    }
}

If I try to add the column, I just get this via the getElements ajax call.

CException
Property "Craft\EntryModel.something" is not defined.

Is there anyway to add a column like this?

The error points to a missing corresponding field with handle 'something' in the entry model.


Why might the admin panel's font 404? The admin panel's icon font refuses to load, throwing a 404 along with the full body of the font file. The result looks like this:

I don't know nginx well, but we recently added a change to the config to categorically lock down cross-domain font usage where suddenvalleydevelopment.com is our primary domain and balboatowers.com is another one authorized to use fonts. Relevant lines:

map $http_origin $cors_permitted_domain {
    hostnames;
    default suddenvalleydevelopment.com;
    .suddenvalleydevelopment.com $http_origin;
    .balboatowers.com $http_origin;
}

location ~* \.(otf|ttf|eot|woff)$ {
    add_header "Access-Control-Allow-Origin" "$cors_permitted_domain";
    add_header 'Vary' 'Origin';
}

I'm pretty sure this created the problem you see above, but I don't understand how and therefore how to fix it. We know that Craft stores its own font files at craft/app/resources/fonts, and not in public/admin/resources/fonts like you'd expect from the request.

How does Craft get its icon font (Craft.woff, etc.) to the browser, and why would the CORS provision interfere with that?

The request for the craft font needs to be rewritten, since the actual file is (usually) outside of the public web root. Usually, this will be handled by the location block in your nginx configuration that rewrites requests to index.php. After you added the location block that adds the CORS header, though, all requests for font files are processed by this block only (see this thread for more on location priority). nginx only processes one location block pr request, it doesn't "add them up". The best solution would probably be to make the location block for adding the CORS headers more specific, so that the regexp doesn't match the Craft fonts.

Here's an example on what I did on a previous project where the fonts were inside my /assets folder:

location ~* ^/assets/.*\.(?:eot|ttf|woff)$ {
   add_header Access-Control-Allow-Origin *;
}


How pass an ElementModel to a Macro in Twig I'm trying to pass an AssetModel into a twig macro but all I'm getting is:

Object of class Craft\ElementCriteriaModel could not be converted to string

Is there any way to define my twig macro variable to be an object instead of a string?

This is my macro

{% macro item(thumbnail) %}
    {% if thumbnail %}
        <img src="{{ thumbnail }}"/>
    {% endif %}
{% endmacro %}

This is my template

{% import "macros/grid.html" as grid %}

{% for entry in craft.entries.section('handle') %}
    {{ grid.item(entry.image) }}
{% endfor %}

entry.image is an Asset field

I see a few problems. The first is that {{ grid.item(entry.image) }} is actually an ElementCriteriaModel (i.e. which behaves like an array of images) and not an ElementModel (i.e. a single image), even if only one image is defined. To get the first image you can use entry.image.first or entry.image[0].

The second problem is that in your macro,  is trying to set the url string using the whole ElementModel (or in your case the ElementCriteriaModel). Instead use: {{ thumbnail.url }}

{% macro item(thumbnail) %}
    {% if thumbnail %}

    {% endif %}
{% endmacro %}

{% from _self import item %}

{% for entry in craft.entries.section('handle') %}
    {{ item(entry.image.first) }}
{% endfor %}

Update: If on the other hand, for some reason you want to pass the ElementCriteriaModel then you just need to handle it appropriately in the macro:

{% macro item(thumbnail) %}
    {% if thumbnail.first %}

    {% endif %}
{% endmacro %}

{% from _self import item %}

{% for entry in craft.entries.section('handle') %}
    {{ item(entry.image) }}
{% endfor %}

As a side note — keep in mind that the macro can also exist in the same template and imported using _self.


Reusing logic to set string variables I would like to set some variables for every entry within a section ('courses'), and then use those variables in loops that are filtering by entry types and categories.

Some simplified markup:

Required Courses

  {% for entry in craft.entries.section('courses').type('safsCourse') %}

{{ courseName }}

  {% endfor %}

Elective Courses

At least one agricultural science course

      {% for entry in craft.entries.relatedTo(65) %}

{{ courseName }}

      {% endfor %}

At least one social science course (3cr)

      {% for entry in craft.entries.relatedTo(66) %}

a href="{{ msuLink }}">{{ courseName }}

      {% endfor %}

As you can see, I'm using the variables msulink and courseName. The code works as intended if I include the following within each {% for entry... %} block:

{% set courseName = entry.deptCode ~ ' ' ~ entry.courseNumber ~ ': ' ~ entry.title %}
{% set msuLink = 'https://reg.msu.edu/Courses/Request.aspx?SubjectCode=' ~
    entry.deptCode ~ '&CourseNumber=' ~ entry.courseNumber ~ '#Results' %}

But I don't want to set those variables three identical times.

How can I set those variables once and use them when I'm pulling various subsets of my Course section entries?

I would use macros. A Twig macro is basically a function, that can take arguments and render HTML.

Macros can be defined inline (i.e. in the same template where it's used) or in a separate file (better if you're writing a generic macro that'll possibly be used in several different templates).

Here's how your template could look, using macros:

{% macro courseName(entry) %}
    {{ entry.deptCode ~ ' ' ~ entry.courseNumber ~ ': ' ~ entry.title }}
{% endmacro %}

{% macro msuLink(entry) %}
    {{ 'https://reg.msu.edu/Courses/Request.aspx?SubjectCode=' ~
entry.deptCode ~ '&CourseNumber=' ~ entry.courseNumber ~ '#Results' }}
{% endmacro %}

{% import _self as helpers %}

Required Courses

    {% for entry in craft.entries.section('courses').type('safsCourse') %}

{{ helpers.courseName(entry) }}

    {% endfor %}

Elective Courses

At least one agricultural science course

    {% for entry in craft.entries.relatedTo(65) %}

{{ helpers.courseName(entry) }}

    {% endfor %}

...

If you're wondering about the {% import _self as helpers %} directive below the actual macros, the {% import %} tag is neccessary to actually expose your macros to your template (even if the macros are declared in the same file), and the "helpers" part is just a contextual variable that could be named anything you want (as semantic as possible is always a good thing).


Help! Craft issues after changing PHP version We have an install of the latest Craft on a Nexcess shared account. By default, the account runs PHP 5.4.x. We were working on and configuring the site for about a week - no issues of any kind until today. We used the Nexcess control panel to update the PHP version to 5.6.3 (for a plugin we were going to use). After doing so, we found that saving entries or certain configuration elements in settings would take 5+ minutes to complete. We downgraded back to 5.4.x and still the behaviour persists. Unfortunately, it does not seem to be 100% consistent. I would say it happens about 90% of the time and cannot find any common thread as to when it happens and when it does not.

Saving one entry is generating about 1.4 MB of Craft log files:

Craft Log 1

Craft Log 2

Beyond the Craft logs, I don't see a php error in the PHP logs but there are these Apache errors when saving the entry:

[Fri May 05 07:28:46 2017] [error] [client xx.xx.xx.xx] FastCGI: incomplete headers (0 bytes) received from server "/dev/shm/cadexdev-php.fcgi", referer: http://www.cadex-dev.ca/shop/admin/entries/multiUse/new

I don't know if this is host-related. I do have a ticket open with Nexcess but am not certain if this is something specific to Craft instead?

EDIT:
Also, a curious behaviour around this issue. If I save an entry and only wait, say, 20 seconds, quit my browser and then re-open and go to the entries page, the entry is there with the saved changes. No 5+ minute wait.

Had this happen. I believe the cause is twofold:
FastCGI's -idle-timeout is set too low.
Craft is still writing to the log but Apache cuts it off since it hasn't sent the actual data yet. That's what the incomplete headers means.
You'll might find a line like this in your config:
FastCgiServer /dev/shm/cadexdev-php.fcgi -idle-timeout 10

Some hosts might set it low, like 10 seconds. That could be fine for production to make sure scripts don't take a super long time, but as you've discovered devMode could take a little longer to run, depending on the case.
RunTasksAutomatically
The other piece is Craft does things in the background. Craft's documentation on runTasksAutomatically says:

This setting should be disabled for servers running Win32, or with Apache’s mod_deflate/mod_gzip installed, where PHP’s flush() method won’t work.

Some of those tasks clear caches and stuff. If they don't run, you'll get weird intermittent performance issues. Turn it off and run a cron job on your server periodically. We run php-fpm on both Apache and nginx and FWIW, we don't have this issue on nginx.
Increase PHP's memory
You'll also get a 500 / incomplete headers error if PHP dies because of lack of memory.


Grab and save image from url to Craft? I have a cool upcoming project which its content will heavily depend on external images. Although I'd like to get rid of WordPress for this project and would love to use Craft, WP has a great plugin called Grab & Save, that allows you to grab and save images from remote urls into your own WP media library.

I was wondering if Craft has a similar plugin or functionality, which it would be a huge time saver for these kind of projects.

-Edit

Without wanting to sound cheeky, would anyone with a bit of free time care to help me out a noobie and write it for me? I can't make head or tail of programming :/ heh.

Until the plugin store officially becomes a thing, the most up-to-date list of plugins available for Craft is here.

I'm not seeing anything that does exactly what you're looking for and Craft doesn't have that functionality natively, but it doesn't seem like too difficult of a plugin to write.


How to get Image/Asset URL in Matrix? I have got a problem with an asset field in a Matrix in an _entry template.
The Matrix is for a step-by-step feature. Each step has his own image.
So the asset field in my Matrix calls: stepImage.

Now in my _entry template I loop over the step Matrix like:

{% for row in entry.steps %}
    ...
{% endfor %}

In this loop I try to get the asset URL like:

  {% set image = row.stepImage.first() %}
  {% if image %}
      {{image.url}}
  {% endif %}

but it does not work. If i dump it, it returns bool(false).

Do you have any ideas?

I'd suggest giving the Matrix templating docs a once-over.
If steps is the name of your Matrix field, then {% for row in entry.steps %} is going to be looping over each of the Matrix blocks you've defined.
Then you'd want to check for the stepImage asset field in the correct block. i.e.
{% for block in entry.steps %}
    {% if block.type == 'myBlock' %}
         {% set image = block.stepImage.one() %}

         {% if image %}
             {{image.url}}
         {% endif %}
    {% endif %}
{% endfor %}


Use a plugin from another plugin in Craft 3 I would like to use another plugin from my own plugin. What is the best way to do this. Right now I am doing something like this, but there should be a better way than to explicitly call the service:

Craft::$app->getModule('mobile-detect')->mobileDetectService

What I am really looking for is the service container where I do not have to know anything just the plugin what should have been loaded...?

You can call the main plugin class directly

MobileDetect::$plugin->mobileDetectService

I don't know this plugin, maybe the class is wrong but that's the syntax you can use


How to deal with too many folders in asset source? I've got a channel with over 1,000 pages, and the entries for this channel have an asset field (for images). I set the asset field to use {slug} as the folder name (because otherwise the content author would have to wade through thousands of photos to find the ones for a certain entry), and so now I have thousands of folders in the asset source's folder in the filesystem. (Just using the server filesystem here, not S3/rackspace/etc).

Problem is, now when you try to expand that asset source in the asset manager sidebar, the page just crashes. Running latest version of Craft (2.6.something), PHP 7, on a beefy VPS server... client is most recent version of Chrome on a modern MacBook with plenty of RAM, etc... so this is not a problem of too few server or browser resources.

My question is: assuming there is some practical limit to the number of folders that the asset manager UI can deal with (which is what, by the way?)... what's a good strategy for me to handle this situation? Each of these thousand+entries will have at least one image, some up to a dozen (this is a fairly large product catalog site). Is there a better way to organize the folder structure, or perhaps a better way to dynamically generate the sub-folders (as opposed to {slug})?

As you have deduced, Assets have a fair amount of javascript attached to each folder and file to deal with all the dragging and dropping. To save resources, a folder gets initialized only when it becomes visible. So, if you're exposing a thousand of those folders at the same time, the JS engine will take quite a hit and consume huge amounts of resources.

In my experience, a good way of mitigating is dividing the subfolders by years and months. Since an entry might not have a post date yet when it's being edited and the post date might change over the course of an entry's lifetime, the path I've come to rely on is {dateCreated|date('Y')}/{dateCreated|date('m')}/{slug}.

Also note that if this is being used in a Matrix block, you'll have to prefix all those variables with owner.


Using ajax with routes is it right? So doing some entries loading via ajax from this question which I expanded on.
https://craftcms.stackexchange.com/a/9868/3579

I'm curious about setting up a route for the actual ajax file since it seems a route is needed otherwise it doesn't work, and will 404. In my example you can see I am setting the ajax url as

$.get( "/resource-center-ajax/p"+page, function( data ) {//do stuff}

so I setup a route with the following.

URI resource-center-ajax

Template /resource-center/_resource-center-ajax

Actual Template file _resource-center-ajax.html

If I go to example.com/resource-center-ajax it will show the results from my ajax file which is just a plain page since I am not including the layout file in that template file.

Is there a way for me to prevent people from accessing this url directly and make it only used for the ajax request? Maybe check if its an ajax request if not redirect to the homepage or something?

Sure, you can do something like

{% if not craft.request.isAjax() %}
  {% redirect siteUrl %}
{% endif %}


Can I move a local Asset to S3 and keep relations? I have a Assets folder (local) with 100 assets in it.

Now I have a new Assets folder connected to Amazon s3.

Can I just drag-n-drop my assets from my local folder over to my s3 folder and keep all relationships for those assets to my elements (entries)?

Yes you can. Just remember to add your new Assets folder as an assets source to your old Asset fields (fields that are "Asset" types) so users still can add/edit/change their assets.


Craft 2 - Adding custom CSS classes to Sprout Form fields on a global basis I'm reading the docs for the Sprout Form plugin and looking at how I can custom classes to the actual form elements i.e. <input>, <select> etc. I can see that there is the rendering options route, but I was hoping to achieve this on a global basis, rather than having to reference individual field handles due to multiple forms. I can add some custom classes through the custom template method, but it still doesn't give you full access to the actual form elements themselves.

I have form styling already, but it relies on CSS classes on the relative form elements, I can add CSS rules that also target the Sprout Forms HTML but I'd rather keep the class stuff consistent.

Would I need to write my own plugin for this? I've looked at the API and while it shows how to hook into the various field types. All seem to require using $renderingOptions. It looks like I could achieve this if I was outputting each field with displayField() manually, but I'm attempting to make this as user friendly as possible and trying to keep it in templates.

Because its Craft 2, I don't believe I can individually override any of the field types with my templates/_sproutforms/ folder, though in Craft 3 this does appear possible.

If I was just to write my own plugin that hooked into the various fields such as SproutFormsPlainTextField and added my own CSS class(es), would this be the best way? Testing this out, I modified the SproutFormsPlainTextField.php within the integrations folder at sproutforms\integrations\sproutforms\fields and added my own array for the class and it does work, but I'd imagine I'd need to do this as my own plugin, rather than modifying sproutforms directly. What's the best way to achieve this?

<?php
namespace Craft;

/**
 * Class SproutFormsPlainTextField
 *
 * @package Craft
 */
class SproutFormsPlainTextField extends SproutFormsBaseField
{
    /**
     * @return string
     */
    public function getType()
    {
        return 'PlainText';
    }

    /**
     * @param FieldModel $field
     * @param mixed      $value
     * @param array      $settings
     * @param array      $renderingOptions
     *
     * @return \Twig_Markup
     */
    public function getInputHtml($field, $value, $settings, array $renderingOptions = null)
    {
        // My own CSS classes required for CSS styling
        $renderingOptions = array(
            'class' => 'input text'
        );

        $this->beginRendering();

        $rendered = craft()->templates->render(
            'plaintext/input',
            array(
                'name'             => $field->handle,
                'value'            => $value,
                'field'            => $field,
                'settings'         => $settings,
                'renderingOptions' => $renderingOptions
            )
        );

        $this->endRendering();

        return TemplateHelper::getRaw($rendered);
    }

    /**
     * @return string
     */
    public function getTemplatesPath()
    {
        return craft()->path->getPluginsPath() . 'sproutforms/templates/_components/fields/';
    }
}

Would this be the best way?

You are correct. This is quite easy now in Sprout Forms for Craft 3 but is more of a challenge in Craft 2 because we can't easily give you access to the input html.

Your options in Craft 2 include:

Use Sprout Forms to override the field wrapper in the field.html file and then update your CSS to target input fields within the parent tag (i.e. div.my-class input instead of input.my-class. This will be the most maintainable option.
Target each individual field you wish to add a custom class to using rendering options. As you mentioned, the limitation here is that it's not very maintainable for multiple forms.
While I can't recommend it, if you are planning to upgrade to Craft 3 and choose to hack the core while you remain on Craft 2, the easiest place to do so may be right in the template files (instead of in the PHP classes). You can find the Sprout Forms input field template files in sproutforms/templates/_components/fields/*. You would need to update each individual template file with the classes you want. This option would be maintainable with the caveat that you'd need to remember that each time you update Sprout Forms you may overwrite your changes. If you go this route, it's probably best to manage your project in a git repository so you can recover any changes you might overwrite during an update.
If you did want to do this as your own plugin, you would not be able to modify the existing fields. You would need to create a new custom field to replace each existing custom field, and you would see both your custom fields and the default custom fields in the dropdown. This is maintainable in a general sense, but creates a lot of overhead for creating the duplicate fields with slightly different classes and will make the user interface feel like it has twice as many fields in the UI.
I expect you've considered this option, but updating to Craft 3 will have many benefits on top of greater control in Sprout Forms.


Trouble with "restricted" upload folders I'm having some difficulty with "Restrict uploads to a single folder?" option on an Assets entry field. I find that when I upload an asset into the field, the asset folder gets created as expected, but the asset does not end up in the folder.

For example, if the author is SomeAuthor, a folder called 'SomeAuthor' gets created in the correct location in the Asset Source using the following dynamic path, set in the field setting as mentioned above:

{% if owner is defined %}{owner.author.username}{% elseif author is defined %}{author.username}{% endif %}

I'm using this to allow me to re-use the field on Matrix and non-Matrix sections, but the point is that the folder is created successfully. What isn't so successful is that the file does not end up inside that folder, but in the root of my Asset Source. I've tried combinations of logging in as the specified author, removing all the assets from the site. Re-indexing etc. etc. all to no avail.

Perhaps I'm looking in the wrong place.

Does anyone have any experience trying to achieve what I'm attempting?

The general context is I have separate users on the system, each responsible for a "project" page, along with permissions to create 'articles' and 'events' in the respective sections/channels. I want to have one asset folder per user, so that no matter where they upload assets, the assets end up in the same folder per user, so that user with username 'SomeAuthor's assets will end up in 'SomeAuthor' folder within my single site source. I am attempting to do this using this snippet above, which in theory should work considering the author.username is not changing...

EDIT

Since posting this, I have discovered that I had another assets field with a simpler "Restrict uploads to a single folder?" setting: {author.username|lower} that was creating the field, and not the field I was testing with the setting above! Doh...

I supposed the real question is, therefore, how can I get a dynamic path, like the one above, to work?

Whenever you're not using the shortcut syntax with single braces you need to acces the element via object.

So add that var before owner etc. and you should be good.

One thing I noticed is that you can't use ternary syntax for conditionals within short syntax either but with Twig code you can:

{{ object.owner.author ? object.owner.author.friendlyName }}


Getting the UID of a user model I want to use the UID to get a user from a url and don't want to use the ID as it is guessable. I know I can do craft()->users->getUserByUid($uid); but after getting the uid, the uid is not on the user model which is kinda a pain.

For example what if i wanted the uid for the currently logged in user?

Currently, Craft doesn't expose the UID in any of its models.

Kind of a pain, but the only thing I can think of as as a workaround is to write a plugin that has a UserModel with the UID and mimics whatever methods from UsersService that you need.


Has anyone converted a Bootstrap based website to Craft? Is there any reason you wouldn't want to do this?

EDIT: More info

I have a website that was built in HTML/CSS and over the years, it's been modified, added to and hacked at to the point where the code is kind of a mess. I'd like to redesign the site using a structured CMS but I want the base HTML/CSS to be well coded and optimized. That's why I'm thinking of building a site using the Bootstrap framework and then rebuilding it in Craft. I realize this seems redundant but I'm not good enough with CSS to just build it from the scratch in Craft. I'm more of the cut and paste kind.

The reason I'm considering Craft is because I like the idea of defining fields and sections I can reuse. I built the site 90% through using ModX, it looked good but the back end and the way it presents Template Variables is not very friendly. I like Craft's approach a lot more.

I built Progress.org using Craft and Bootstrap—and it only took me about two months of part-time work. I’d say Craft and Bootstrap together are a match made in heaven. Here’s why:

1) Bootstrap is an out-of-the-box template that gives you great flexibility if you want to design your website how you want it to look. I found other frameworks like WordPress overly constraining.

2) Craft allows for near unlimited customizations as long as you know your front-end HTML/CSS.

If you know your HTML/CSS, then a Bootstrap/Craft combination is ideal, in my experience.


Fetch locale entries does not work in Live Preview I need to display booth local and localized entries in the same page.
So I fetch the different localized entries like this :

{% set localeEntry = craft.entries.id(myEntry.id).site('default').one() %} {# get the locale data #}
{% set enEntry = craft.entries.id(myEntry.id).site('en').one() %} {# get the english data #}

This works perfectly when the page is displaying in the browser, but the variables return empty when the page is displaying in the Craft CP's Live Preview panel.

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