Pull info from a Related field within Matrix > within Super Table

I didn't test it yet but it should like this :

{% set entries = craft.entries().section('schedules').all() %}
{% for entry in entries %}
{% for block in entry.stageSchedule : %}
    {% if block.type == 'yourblocktype' : %}
         {% for item in block.schedule %}
            {{ item.timeOn }}
         {% endfor %}
    {% endif %}
{% endfor %}
{% endfor %}

I assume there is only one entry in schedules sections.
Please be remind block type is different with block name.
Hope this help.


Add a column to entries list using defineAdditionalEntryTableAttributes?

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


I believe you're looking for this instead.
Event::on(
    Entry::class,
    Element::EVENT_AFTER_SAVE,
    static function (ModelEvent $event) {
        $entry = $event->sender;
        if ($entry->status === 'disabled'
            && $entry->propagating
            && $entry->resaving
            && ElementHelper::isDraftOrRevision($entry)) {
            return; // do nothing if disabled/draft/resaving...
        }
        if ($entry->section->handle === 'my-handle') { // check section handle
            // call your preferred msg client: slack, email, mailgun...
        }
    }
);


The Craft.MatrixInput class that manages each Matrix field doesn’t actually get assigned to a variable, so there’s no way to reference it right now. We probably won’t change that, since there wouldn’t be much point to it in the first place. The class doesn’t provide any sort of object-oriented API for managing the blocks; there’s no need for it in the current implementation.

If you want to get a reference to all the blocks’ containers, you can just use a jQuery selector:

var $blocks = $('#fields-myMatrixField > .blocks > div');

$blocks.each(function() {
    var $block = $(this),
        id = $block.data('id');
    // ...
});


The statement is only executed once.

You are essentially creating an elementCriteriaModel, returning the first element using the .first() parameter (which in this case is an Entry Model), and storing it in a variable; which you then can use to access your field values.

If you want to further increase performance you can wrap the whole block in a cache tag, which will speed up subsequent requests.


Live Preview not loading - https / http conflict

I had the same issue!

In my case, it was because I had defined http://example.com as my siteUrl for the production environment, in my general.php config file. Simply changing this to https://example.com fixed it!

Sample code: (Change 'production' to match the env you are targeting of course)

'production' => [
    // Base site URL (if multisite)
    'siteUrl' => [
        'mysite!' => 'https://example.ca',
    ],
    //OR Base site URL (if single site)
    'siteUrl' => 'https://example.ca'
],

More on the general.php config file: https://docs.craftcms.com/v3/config/config-settings.html


Check if cart contains valid products for discount code

Try something like this:

{% set currentDiscount = craft.commerce.getDiscountByCode(cart.couponCode) %}

{% if currentDiscount %}

{% set cartProductIds = [] %}

{% for item in cart.lineItems %}
  {% set cartProductIds = cartProductIds|merge([item.purchasable.product.id]) %}
{% endfor %}

{% for product in currentDiscount.products %}
  {% if not product.id in cartProductIds %}{{ product.title }} not in cart.{% endif %}
{% endfor %}

{% endif %}


Help! Craft issues after changing PHP version

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.


How can I set the "activateAccountSuccessPath" for users with Control Panel access?

You can write a plugin that listens to the users.onActivateUser event, and manually redirects the browser to your desired page.

Just add this code to your plugin’s primary class:

public function init()
{
    craft()->on('users.activateUser', array($this, 'onActivateUser'));
}

public function onActivateUser(Event $event)
{
    /* @var UserModel $user */
    $user = $event->params['user'];

    // Are they in the default group?
    // (Indicates this was probably public registration)
    if ($user->isInGroup('myDefaultGroup'))
    {
        // Redirect them to the activateAccountSuccessPath
        $activateAccountSuccessPath = craft()->config->getLocalized('activateAccountSuccessPath');
        $url = UrlHelper::getSiteUrl($activateAccountSuccessPath);
        craft()->request->redirect($url);
    }
}


Check in header file if lightswitch field is on

Try:

if craft.entries.section('aboutus').lightswitch_field('1')|length

If you want to check if it is "off" use:

if craft.entries.section('aboutus').lightswitch_field('not 1')|length


Restrict entry editing to category / topic

So you could definitely use the onBeforeBuildElementQuery event to keep them from showing up in the CP (though you'd have to detect if the query is coming from the CP).

I'd also recommend your plugin uses the onBeforeSaveElement event. From there you could check the element's related categories to see if the user has them on their user account, and then allow/deny them to be able to save the element from there. While hiding it from the CP would likely keep them from finding it, putting in more validation will make sure that no one emails an edit link to someone who isn't supposed to be able to edit it.


The original answer is good, but as noted, somewhat incorrect, at least concerning a chainable .with() method.

Here's my attempt to explain things: Speed up your Craft CMS Templates with Eager Loading


How do I call the getInputHtml() method of a custom fieldtype plugin from another plugin?

The problem as I see it is that the call is happening on the front-end. Because of this, the template renderer is not looking for the template at plugin1/templates/_field/input as it normally would, but at plugin1/_field/input, which explains the error message.

If Plugin 1 is your own plugin then you should be able to include the templates part of the path in the render call, for example:

return craft()->templates->render('templates/_field/input');

I haven't tested this but I think it should work.

Alternatively, and I would recommend this, since this is a front-end call, you could create your own function getFrontEndInputHtml() to return the html appropriately.


Struggling to get Entry index number so I can output "X of Total Entries"

I don't know if this is the best solution or if it will even work, but this would be my first try.

{% for spotlight in craft.entries().section('spotlights').all() %}
  {# Check to see if the title of the "spotlight" matches the current entry's title #}
  {% if spotlight.title == entry.title %}

{{ loop.index }} of {{ total }}

  {% endif %}
{% endfor %}


Pagination base URL - any way to have pagination links go to URLs other than current one?

Not tested, but I would assume that you could simply get the total entries, divide by the pagination limit and then generate your dynamic nav using a range loop.

{% set totalEntries = craft.entries.section('news').limit(null).total() %}
{% set pageLimit = 10 %}
{% set totalPages = (totalEntries / pageLimit)|round(0, 'ceil') %}

    {% for i in range(1, totalPages) %}

    {% endfor %}


Display X minute / hours / days in entries

Check out the DateTimeHelper method timeAgoInWords. It will do what you are after.

https://craftcms.com/classreference/helpers/DateTimeHelper#timeAgoInWords-detail

There is even a handy twig filter available for use in your templates.

{{ entry.dateCreated|timeAgoInWords }}


Basically you want to retrieve all entries where a certain field is not empty?

Return entries where field is "not empty"


After upgrading to PHP 7 I started getting "storage folder isn't writable by PHP" errors

Try resetting the permissions of the storage folder:

https://craftcms.com/docs/installing#step-2-set-the-permissions

(It's possible that PHP was running with higher-than-default permissions in the previous setup, in which case everything may have worked even if you hadn't explicitly made the storage folder writable.)


Is there a way to add custom aspect ratios in the image editor?

This will be possible using a new imageEditorRatios config setting that is coming in Craft 3.5 (due to release in early August 2020):
...
'imageEditorRatios' => [
    'Unconstrained' => 'none',
    'Original' => 'original',
    'Square' => 1,
    '16:9' => 1.78,
    '10:8' => 1.25,
    '7:5' => 1.4,
    '4:3' => 1.33,
    '5:3' => 1.67,
    '3:2' => 1.5,
    'Some custom ratio' => 1.9,
];

Source (GitHub issue)


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

Update:

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

Update 2:

Officially fixed in 2.2 build 2598.


slugs in asset picker not working until page is published

This is expected behaviour. By dynamically creating asset folders based off the entry slug, the entry itself has to first be saved. This is so when assets are uploaded, Craft can look at the slug of your entry, create an asset folder with the same name and put the assets there.

By uploading assets to an unsaved entry, the slug doesn't really exist yet, so Craft won't know where to put your files.

As you say, the only way round this is to save your entry first, so Craft knows what to name your folders, and then on the second save add in your assets.

Correct me if I am wrong but that is my understanding of it all.


I think something like this should work

{# First get all the stats #}
{% set stats = craft.entries.section('statistics') %}

{# iterate over those stat in a random order %}
{% for stat in stats.order('RAND()') %}

    {% if stat.image | length %}

        {% set image = stat.image.first %}

    {% else %}

        {# If there's no image, query for a random stat, excluding those without images #}
        {% set randomStat = stats.image(':notempty:').limit(1).order('RAND()') %}
        {% set image = randomStat.first.image.first %}

    {% endif %}

{% endfor %}


doublesecretagency business logic module template: 404 on example form action

Did you remember to add the action as a hidden input (<input type="hidden" name="action" value="business-logic/example/example-form-submit">, rather than an action attribute on the form? And also use method="post" on the form.


It turns out that I was running a dev release of PHP 5.6 from a PPA and not the official Ubuntu release. With Brad Bell's help (thank you so much!) I got on the right track to looking into PHP and realized that that was the problem. After purging the PPA and reverting to PHP 5.5.9 on the official Ubuntu repo, the problem seems to have gone away. Hopefully never to return again.

Thanks again, Brad!

EDIT: As I said below, the problem appeared again. It seems to have been solved when I disabled Craft's devMode in my environment variables. I'm not sure what would cause that to be an issue, but devMode is something I can live without for now since the site is near going live. Brad, if you have any insight as to why devMode would be causing the issues, I'd love to hear it :)

Thanks!


Redirect user based on group after Craft login

This is an alternate solution to what you're trying to do, and only applies if the login is coming and being directed to front end templates, but you could do something similar directly in your twig template, rather than having to use a plugin.

You could put something like...

{% if currentUser and currentUser.isInGroup('sales') %}
{% redirect "new.url" %}
{% endif %}

...somewhere on the page that logged in users will land on by default.


Possible to change view of related entries field?

Easy task: you can create your own relations field and use that instead.
Take a look at the default Entries field here is the php code for it click

All you have to do is changing the template that should be rendered so you'll end up with something like

class MyCustomRelationField extends BaseRelationField
{
    // Static
    // =========================================================================
    /**
     * @inheritdoc
     */
    public static function displayName(): string
    {
        return Craft::t('app', 'My awesome field');
    }
    /**
     * @inheritdoc
     */
    protected static function elementType(): string
    {
        return Entry::class;
    }
    /**
     * @inheritdoc
     */
    public static function defaultSelectionLabel(): string
    {
        return Craft::t('app', 'Add an entry');
    }

    /**
     * @var string Template to use for field rendering
     */
    protected $inputTemplate = 'path/to/your/custom/template';
}

the $inputTemplate is the template that gets rendered in the cp so you'll take the default one here as your base and change the parts you need

<div class="elements">
    {% for element in elements %}
        {% include "_elements/element" with {
            context: 'field',
            size: (viewMode == 'large' ? 'large' : 'small')
        } %}
    {% endfor %}
</div>

to something you like. Because the default content in this include is generated here. Take that as a reference what you have to change/include in your own template (or hook) together with your icon/thumbnail


Update entry Enabled value based on field value when saving

Looking into this more and understanding how Craft works, a post date needed to be set, it will not do this automatically for you.

On saving the entry, it will check to see if the entry is enabled and has a post date - https://github.com/craftcms/cms/blob/d72c2b0bbddfeced0920f11bfaf4d9891fe62a5a/src/elements/Entry.php#L936

To achieve this I copied how Craft manages the date in the beforeSave function https://github.com/craftcms/cms/blob/develop/src/elements/Entry.php#L1234-L1236

On testing this allowed the entry to be viewed in the frontend and allowed other plugins and modules to do what they needed to with the entry.


Use-case: custom plugin or extended use of entries/categories/tags

I just started using Craft, so I still don't understand 100% the core, that's why I cannot decide which would be the best approach.

Welcome to the community! You're asking a lot here!

Someone more knowledgeable about plugin-development will want to chime in here but in my experience, Craft is quite flexible and can handle a lot of things out of the box, even without writing a plugin. It's awesome when you have to relate content to and from each other, like you're wanting to do here.

I don't quite know all the requirements but if I were spec'ing this job, it sounds like you could do a lot of this by using most of Craft's entry types.

Jobs (section)
Companies (section)
Skills (Category / Source )
Industry (Category / Source)
...

I would deal with ATS import as a separate plugin.

You'd create entries in the Jobs section. Create fields that relate the job back to an entry in the company section as well as categorize it using those categories. Like entries, categories can have custom fields so you can put assets field on a skills category and upload logos so every job that has, say, jQuery in it, could have that logo appear.

Depending on your requirements, your Jobs section could have multiple entry types. So if you have specific fields that need to appear on certain jobs categories and what not, you can do that.

I see Matrix being an MVP here, too. When a regular set of fields doesn't cover what you need (you mentioned "custom settings that apply to each job"), you can mix and match a ton of different combinations with Matrix.

Bottom line: I would play more with Craft before you get into plugin development. If you're new to Craft, how many sites have you built with it before you started plugin development? Is there something that led you to know you actually need to use a plugin or is it just a programmer scratching an itch? (We've all been there!)

When it comes to programming and web design in general, to paraphrase The Matrix, "there is a difference between knowing when to reinvent the wheel and not." That only comes with experience.


How to use createFromString() in Craft 3

You're looking for the toDateTime() method in the DateTimeHelper class:

DateTimeHelper::toDateTime($pubDate, true);


After upgrading Craft CMS, is there a receipt to print out?

You should receive an email after purchasing a Craft license. Maybe double check your email details and/or your spam folder.

Anyway, if you email the guys at support@buildwithcraft.com they will sort out a receipt for you.


Issue trying to use function from DateTimeHelper class: "timeAgoInWords() is not defined"

Generally speaking, only Variable classes are exposed to templates in Craft and there isn't a Variable wrapper for DateTimeHelper.

However, that would be a super-simple plugin to write that exposes timeAgoInWords as a Twig filter so you could do something like

entry.postDate|timeAgoInWords


Correct way of showing errors when editing profile via front-end form?

Not sure what validation error you're expecting from the template code.

Both firstName and lastName are non-required fields that will basically accept any input.

photoUrl could return a validation error if, for example, you tried to upload a .txt file to but you're not calling the errorList() macro on that field.

Not sure if userDescription is required or not, but the errorList() macro isn't being called there, either.

Finally, the variable that Craft returns back to the template when there are validation errors is called account.

So you'd want to do something similar to what the User Registration Form example doc does:

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


How can I access the file extension of an asset?

Yes, you can access a number of properties on assets. You can get the "file group" (whether it's a document, image, video etc) using:

{{ asset[0].kind }}

More specifically, you can access the file extension using:

{{ asset[0].extension }}

Full list in the documentation at: http://buildwithcraft.com/docs/templating/assetfilemodel


With Element API, can I combine two entry types into a JSON object, keyed by type?

I'm using Craft as a headless CMS, so I wanted full control over the JSON it was returning. I didn't figure out how to do this with Element API, but it's very simple to write your own plugin that turns entries into JSON. Craft already includes the code to look up entries, so it's easy.

A basic controller might look something like:

class MyCustomPlugin_SomeResourceController {

    public function actionWidgets() {
        $criteria = craft()->elements->getCriteria(ElementType::Entry);
        $widgetEntries = $criteria->section("resources")->type("widget")->find();

        // map entries to PHP objects
        $widgetObjects = array_map(function($entry) {
            return [
                "name" => $entry->title
            ];
        }, $widgetEntries);

        $this->returnJson([
            "widgets" => $widgetObjects,
            "anotherThing" => "somethingElse"
        ]);
    }

}

In the actual app, I'm combining several different types of entries into one endpoint, so I extracted the reading and formatting into services, which I combine in various controller actions.

Presumably, Element API has some other features that I'm missing out on, but I haven't noticed.


I'll let Brandon and Brad answer regarding what's planned. :)

But, couldn't you solve this by adding fields to your assets? You can make fields of whatever type and add them to the field layout of asset sources. To edit them, you just double-click on the asset in the matrix. This works both for existing assets and newly uploaded assets as far as I can tell.


Multi-locale pages 404 on subdirectories

I was running into the same issue, struggling with it and it turns out that the official documentation is a bit misleading (no offense) when it comes to the .htaccess file, because it just mentions to copy it:

You can not just copy the .htaccess but you have to change the RewriteBase parameter according to your folder structure in the /public folder.

In your case the .htaccess file in the /public folder should start like this:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /

    (...)

Within your /public/es folder you should have the following in the .htaccess:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /es/

    (...)

Hope that helps!

TL;DR —> The RewriteBase in the .htaccess has to be changed according to the folder setup of the locales


Craft Pro 3.1.33 - Download PDF in admin user profile

Well, the only i way i found to solve this was to made my custom plugin for craft 3 CP.

I build my template scafold here:

https://pluginfactory.io/

Then installed it via composer and started coding to achieve what i want.

Job done.


The routes.php config file doesn't support multi-environment setups in the manner that general.php or db.php does – in other words, your example code using a multidimensional array would throw an error.

As a workaround, you can write some PHP in your routes.php file. For simple stuff, a ternary operator may suffice:

return array(
    'test' => strpos(CRAFT_ENVIRONMENT, '.loc') !== false ? '_test/index' : 'index',
);

For more complex stuff, you could set up different arrays of routes for different enviroments, and maybe do some merging:

$routes = array(
    'foo' => '_foo',
    'bar' => '_bar',
);

$devRoutes = array(
    'baz' => '_baz',
);

$stagingRoutes = array(
    'baz' => '_staging/baz',
);

if (strpos(CRAFT_ENVIRONMENT, '.loc') !== false) {
    $routes = array_merge($routes, $devRoutes);
} else if (strpos(CRAFT_ENVIRONMENT, '.staging') !== false) {
    $routes = array_merge($routes, $stagingRoutes);
}

return $routes;

If you're wondering about the CRAFT_ENVIRONMENT constant, that'll default to your hostname (e.g. myawesomesite.loc) unless you explicitly set it to something else before bootstrapping Craft (e.g. in your index.php file). The simple strpos(CRAFT_ENVIRONMENT, $env) test is how Craft merges multi-enviroment configs (e.g. from general.php) under the hood.


If you want to enable fuzzy searching globally (across the entire Craft install - CP, front-end, ElementAPI, etc.), you can use the defaultSearchTerms config setting.

You can also just do it programmatically from the ElementAPI plugin with something like:

Suffix wildcard:

'criteria' => ['title' => $search ~ '*']

Prefix wildcard:

'criteria' => ['title' => '*' ~ $search]

Both sides:

'criteria' => ['title' => '*' ~ $search ~ '*']

Or you can use any of the other search syntaxes listed on the search docs page.


How to get corresponding Post in Craft request?

That makes sense, because getPost always gets the last post (I think).

If you've got multiple form inputs with the same name, then the last one in is probably the value that will get sent over POST to the controller.

You could make sure your themeId name inputs are unique by appending a unique string or some such to them, then parsing through all of the submitted values on the PHP side, perhaps?


block.image.one() shows more than one image

Yes you're correct, one() will get the first matching element. However, in your code you're not asking for the first image block, but rather the first image from each image block (because each image field can have multiple images attached to it).

The structure is basically something like this:

entry          <- 1. The entry
  mainPost     <- 2. The matrix field
    postImage  <- 3. The matrix field's block
      image    <- 4. The field inside the matrix field's block

We want to get the first result from #3 but you're currently only asking for the first result from #4. (It is probably a good idea to also ask for the first result from #4 as well btw.)

I think something like the following would work in your case:

{% for block in entry.mainPost.type("postImage").limit(1).all() %}
  {% set image = block.image.one() %}
  {% if image %}
    <img src="{{ image.getUrl('summaryImage')}}" alt="">
  {% endif %}
{% endfor %}

UPDATE: looks like you actually need to use limit() instead of one() when querying for the block.

UPDATE 2: tacked on the all() to fix the deprecation!


Does "preprod" count as a dev sounding sub-domain?

No, it doesn't match anything on the list:

UPDATE
After filing this request, the documentation has been improved to more clearly state that this is, in fact, an exhaustive list.
I have update the screenshot above accordingly.


We use rsync. This can either be run manually with your CLI or you could set up a cron to run it every so often. It's significantly faster than FTP.


List only Orders containing specific Products

This is now possible as of Craft Commerce 1.1.1211 using the new hasPurchasables criteria attribute:

https://craftcommerce.com/docs/craft-commerce-orders#haspurchasables


If your “event” Entry's Section settings define a URI and a template path, Craft will take care of defining entry in the template. No need to look it up by segment!

Generally speaking, this will “just work” for Live Preview, as well.

 I'd recommend setting the Event Section's Template to _events/event, and creating templates/_events/event.twig in your project folder. Then, entry will be automatically available when the template is rendered, either from a front-end request, or via Live Preview.

I can't speak exactly to the internals of Live Preview—but I can say that it side-steps the normal routing by laterally POSTing the updated content to a Craft Controller that independently resolves and renders the template. That said, once it starts rendering the template, it's almost exactly the same context you'll get when the Entry is live, and requested via its normal URL.

   Using an underscore in the template's path will make sure the template is not directly accessible, and is only rendered during a request that Craft has determined matches an Element (in your case, the event Entry), or via the Live Preview controller. Read more about how Craft routes requests, here.


try:

{% set params = params | merge({
    postDate:['and', '>= ' ~ start.date, '< ' ~ end.date]
}) %}


Web folder content moved up a level - Craft Path settings

To replicate what you did in craft 2, you would change your web/index.php to:

define('CRAFT_BASE_PATH', dirname(__DIR__,1) . '/nameofthefolderwherecraftlives');

The ,1 tells craft to go up 1 level and change /nameofthefolderwherecraftlives to well... The name of the folder where craft lives :)

So for what I show you above, your structure will be:

web

index.php

nameofthefolderwherecraftlives

config
modules
storage
templates
vendor

It's here in the docs.

Hope that helps!


Issues with Twig template includes when rendering from a service called from a CP-triggered event

Not 100% sure what is going wrong here, but here is what I would do to debug this on my end:

Simply output the current path the template parser is using in the controller and event handler to see the difference in the values

var_dump(craft()->path->getTemplatesPath());
exit();

If the values are different, you can override the value to do your parsing as your need it like so:

// Get the current path and save it
$oldPath = craft()->path->getTemplatesPath();

// Set your desired template path
craft()->path->setTemplatesPath('your/new/path');

// Render your template and pass some data to it that will be parsed
craft()->templates->render('your/template', $data = array());

// Set the old path so Craft can resume as normal and you don't break stuff
craft()->path->setTemplatesPath($oldPath);

One other option is to try to include your macro like so:

{% import 'myplugin/_macros/mymacro' as mymacro % }

I basically do the exact same thing you are doing with Postmaster for Craft. I include my own macros, parse templates and strings during event handlers, it all works perfectly for me using the technique I outlined above. Feel free to take a look at my code if you want to see some more advanced examples of this.

On a side note, I bet Postmaster will do exactly what you want to do in regards to sending an email for new entries. You should give it a shot, it has loads of additional features I bet you could use. No less, I do help this solve your problem either way. Sorry this wasn't really an official answer or a guaranteed fix, but I gave it my best shot without seeing all of your code.


In one of your plugin services, you can render a template that handles the markdown filter and return the results as an HTML string. You can then send that HTML to wherever you want to preview.

In your controller:

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

    $content = craft()->request->getPost('textarea_content');

    $html = craft()->pluginName_serviceName->renderMarkdown($content);

    $return = array('html' => $html);

    $this->returnJson( $return );
}

In your service:

public function renderMarkdown($content)
{
    $rendered = craft()->templates->render('path/to/template', array('content' => $content));
    return $rendered;
}

All you would need is a small template that renders your content. If you have this template in your plugin folder, be sure to adjust the template path as described here.

Template:

{{content|markdown}}

If you need more help with the writing services, let me know!


I'm not sure this is possible in Twig alone, but it should be easy enough in PHP.

To do that, you could write a Craft plugin that adds a custom Twig extension that has a ScormIt filter that uses PHP's DateInterval class to output it in the format that you're looking for.

That way, you could do something like:

{{ videoObject.duration|ScormIt }}


Using Composer to Manage Site-Level Dependencies

I think what you really want is something like Bower:

http://bower.io

It has Bootstrap in its manifest, amongst many others.


How would you recommend integrating craft with Algolia

Disclaimer : I'm the dev of the SearchPlus plugin

The SearchPlus plugin by SquareBit (currently in private beta) integrates directly with Algolia and handles all the index population and updates.

There's also a few helpers for integrating on the front-end.

All told - you should be able to just drop the plugin onto the site, configure your indexes and start a population action, drop a bit of js on the front-end and you're done.

Drop me a line and I'll get you on the beta.


Integrating Yii 2 modules and frontend widgets with Craft CMS 3

If you go down the route of creating your own modules for craft/yii2:

You can register access from twig to PHP services in your modules init() function

Event::on(CraftVariable::class, CraftVariable::EVENT_INIT, function(Event $e) {

        $e->sender->set('phpaccess',services\yourService::class);

    });

Access functions within this service in twig with

{% craft.phpaccess.yourfunctionname() %}

You could also return a twig template from your modules controller with some additional variables

$view = $this->getView();
$html = $view->renderTemplate(
    '/_templatename.twig',
    [
        'additionalvar' => $somephpvar
    ]
);

return $html;

Also if you havn't already seen the pluginfactory utility, it does save a lot of time!


In answer to your second question:

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

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

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

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

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

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


Creating the field/block types manually is only part of the equation - you would also need to create the new content table for your Matrix field.

All you need to do is assign all of your block types and their sub-fields’ settings to $field->settings before calling saveField():

$field = new FieldModel();
$field->groupId      = 1;
$field->name         = Craft::t('Field Name');
$field->handle       = 'handleName';
$field->translatable = false;
$field->type         = 'Matrix';

$field->settings = array(
    'blockTypes' => array(
        'new1' => array(
            'name' => 'My Block Type 1',
            'handle' => 'myBlockType1',
            'fields' => array(
                'new1' => array(
                    'name' => 'My Sub-Field 1',
                    'handle' => 'mySubField1',
                    'required' => true,
                    'translatable' => false,
                    'type' => 'PlainText',
                    'typesettings' => array(
                        'multiline' => false,
                    )
                ),
                'new2' => array(
                    'name' => 'My Sub-Field 2',
                    'handle' => 'mySubField2',
                    'required' => false,
                    'translatable' => false,
                    'type' => 'PlainText',
                    'typesettings' => array(
                        'multiline' => true,
                    )
                ),
            )
        )
    )
);

craft()->fields->saveField($field);


I asked the same question ( Oct 24 2019 ) to the Craft team, so i'll just copy & paste the answer here from Brandon Kelly (Pixel & Tonic):

  Hey Christopher,

  We do have plans for a field type like that, but more likely based on
  editor.js rather than Imperavi's Article Editor.

  I have heard rumblings that someone else is working on a plugin based
  on that though.

  -Brandon


Add active class to nav created from globals

Depends on your URL structure ultimately and whether you have sub pages, but I think the easiest way to do it would be something like:

{% if mainNav.navitems|length %}
 {% for row in mainNav.navitems %}

     {{ row.name }}

 {% endfor %}
{% endif %}

You don't have to use the segment(1) method if it doesn't suit, take a look at the request docs to find something that suits your URLs


Usually when selling goods that come in different size and colors, a different SKU would exist for any size/color combination that you sell, thus a unique variant.

So, when that item becomes low in stock (Stock Keeping Unit), you can order more in.

In Craft Commerce we do not automatically generate every combination of variant based on the custom fields attached to a variant because most stores never sell every combination, and also, not every custom field is a unique aspect of that variant.

If more options are added to a product, there is nothing forcing you to make every combination as a variant, you could just use an option/custom field to mark a single variant as special (i.e Gold Edition).

If you are selling something that has a lot of options that customers are required to choose from each option, there is nothing stopping you from building a UI on the front end that makes selecting a variant possible based on all the options of all the custom fields. In that case your front end UI is building a query for a variant (craft.commerce.variants).

If you're not really selling a unique single SKU, but rather wanting a 'build a product' type product based on smaller SKUs, like building a bike, then you would need to build that yourself on the front end and/or with a plugin. That plugin would track what is in the cart, and which items still need to be added to complete the bike.

An alternative to that is building a custom purchasable with a plugin. This would allow you to expose the building of the purchasable element to the customer (with any options or custom logic you wish), and once the purchasable is saved by the customer, they can add it to the cart.

Lastly, if you don't want to go down the custom purchasable route, you can always just use generic options params when adding a product to the cart to allow any extra customer choices for the product. If the price is the same for the product regardless of the option chosen, this might be the easiest path if you have a huge amount of options and don't want to have a variant for each. The only thing you would want to do is validate the options for the current product.

The line item options feature mentioned above can also be combined with a small plugin that modifies the price of the line item based on the options choosen with the onPopulateLineItem event. The plugin would use the event something like this:

craft()->on('commerce_lineItems.onPopulateLineItem', function($event){

$purchasable= $event->params['purchasable'];
$lineItem = $event->params['lineItem'];

  if(isset($lineItem->options['giftWrapped']) && $lineItem->options['giftWrapped'] == 1){
    $lineItem->price = $lineItem->price + 1;
    $lineItem->salePrice = $lineItem->price;
    $lineItem->saleAmount = 0;
  }
}

Remembering that using the options method above will not be compatible with any stock tracking or inventory features now or in the future.

Let me know if you have any follow up questions and I can improve the answer above.


How do I render specific child entries of a structure based on url segment

Probably the simplest way is to split the code up like this in your template.

{% switch entry.level %}
    {% case 1 %}
        {# all the food #}
    {% case 2 %}
        {# course level #}
    {% case 3 %}
        {# one dish #}
{% endswitch %}

This give you a lot of fine control over each level of your structure.

Or even simpler:

{% set entries = craft.entries.section('food').descendantOf( entry ) %}

and run the same loop regardless of which level you are on.

You can also have a look at this page to see how to create a navigation for a structured section.

And if you want elaborately styled menus check out this link


Twig function to load templates into variable?

You can't do it exactly like that in Twig. But there're different approaches that'd do the same thing depending on exactly what you're trying to achieve.

For instance, you could parse the template into a variable, and then pass the variable to your include:

{% set nav %}
    {% include 'component/nav' %}
{% endset %}

{% include 'component/footer' with { nav: nav } %}

Or, you could just pass in the template name as a string, and parse it inside the component/footer template. In you base template:

{% include 'component/footer' with { navTemplateName: 'component/nav' } %}

Inside component/footer:

{% include navTemplateName %}

What approach suits your use case depends on whether or not the scope that component/nav is parsed in, matters.


Access a function in a plugin from another plugin

If the method you want to access is public and in another service file, you could access it via the service layer:

craft()->theOtherPluginService->method();

The method you are using above, the Scope Resolution Operator should work fine without declaring the \Craft namespace, as the plugins should already be loaded into the Craft namespace.  It's used for accessing static and constant methods:

ClassName::method()

In cases where you may be loading a library that is not in the Craft namespace, the syntax you're using above would be needed, where you declare the full namespace:

\SomeOtherNameSpace\ClassName::method()


Detect if a language is translated or not

You could try adding something like the following to the end of your template. This is assuming you only have 2 locales.

{# test for the current entry locale (i.e. spanish) #}
{% if entry.locale = 'es' %}

    {# retrieve the same entry in the original locale #}
    {% set localizedEntry = craft.entries.id(entry.id).locale('en') %}

    {# compare the body content, and append additional content #}
    {% if localizedEntry[0].body == entry.body %}
        Sorry, not translated yet.
    {% endif %}

{% endif %}


I think you would probably also need to check the default variant of the product, e.g.:

{% if product.defaultVariant.onSale %}
  {% set price = product.defaultVariant.salePrice %}
{% else %}
  {% set price = product.defaultVariant.price %}
{% endif %}

Product Price - {{ product.price }}


Problem with Site Running Both HTTP & HTTPs

What web server are you running? I ask because IIS will define the $_SERVER['https'] variable even if the site is being accessed via HTTP (it's value will be 'off') and Ben's answer is only checking if the $_SERVER['https'] variable is present.

One solution is to add a further check that $_SERVER['https'] == 'on'

Try updating this:

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

to this:

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


How do I sort a collection of entries from multiple sections by date?

Rather than fetching the entries at first, just grab their IDs using ids() instead of find():

{% set featuredBlogId = craft.entries.section('blogs').limit(1).ids() %}
{% set featuredArticleId = craft.entries.section('articles').limit(1).ids() %}
{% set featuredSpecialReportId = craft.entries.section('specialReports').limit(1).ids() %}
{% set featuredInfographicId = craft.entries.section('infographics').limit(1).ids() %}

Then merge all of those arrays together:

{% set featuredIds = featuredBlogId
    |merge(featuredArticleId)
    |merge(featuredSpecialReportId)
    |merge(featuredInfographicId) %}

Once you’ve got that array of all the latest featured entries’ IDs, you can fetch their full entry models all at once:

{% set featuredEntries = craft.entries.id(featuredIds) %}

{% for entry in featuredEntries %}
    {{ entry.title }}<br>
{% endfor %}


Order entries by content of their fields

You need to join the Matrix table and the Matrix content table in that case to order by this specific field. LeftJoin will allow you to only join the first block. If you have several different block ids you need to leftJoin only blocks with your block type ID

{% set field = craft.app.getFields().getFieldByHandle('yourFieldHandle') %}
{% set entries = craft
    .entries()
    .section('entries')
    .leftJoin('{{%matrixblocks}} matrixblocks', '[[matrixblocks.ownerId]] = [[elements.id]]')
    .leftJoin(
        field.contentTable . ' myContentTable',
        '[[myContentTable.elementId]] = [[matrixblocks.id]]'
     )
    .orderBy({'myContentTable.fieldprefix_fieldHandle': SORT_ASC})
    .all()
%}

change myContentTable.fieldprefix_fieldHandle to your exact DB column or fetch it dynamically.


How does the negative (not) conditional work in Twig?

Twig != is a comparison operator.

  The following comparison operators are supported in any expression: ==, !=, <, >, >=, and <=

You can use != to compare any two things that php lets you compare, and what you get back is a boolean.

Twig not is a logic operator. (so are and and or)

  not: Negates a statement.


CSRF in plugin dashboard widget 400 error

as jQuery docs says you have to pass data object or string via data.
so you should change

var data = [];

to

 var data = {};

in your code


For custom fields that you add to users under settings>users>fields, you can control whether the field is required by clicking on the small gear icon and selecting make required. With built-in fields such as firstName and lastName, make required cannot be controlled. Within the CP, at least, you would need to look for a plug-in solution.

On the front-end, I would suggest a writting javascript solution that simply validates that the fields are not empty before submitting the form (or before enabling the submit button). Have a look at jquery's .on('click') or .on('change') functionality for more info.


Check if submitted email address belongs to a user account

I assume you have basic knowledge about plugins and routing, if not I'll update my post in a few hours.. otherwise Your form could be

{% if userExists is defined %}
    {% if userExists == true %}

        hello {{ user.name }}

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

Username or email

            <input id="loginName" type="text" name="loginName"
                   value="{{ craft.session.rememberedUsername }}">

Password

            <input id="password" type="password" name="password">

                <input type="checkbox" name="rememberMe" value="1">
                Remember me

            <input type="submit" value="Login">

            {% if errorMessage is defined %}

{{ errorMessage }}

            {% endif %}
        </form>

Forget your password?

    {% else %}

    {% endif %}

{% else %}

    <form method="post">
        <input type="hidden" name="action" value="test/checkEmail">
        <input type="text" name="email">
        <input type="submit">
    </form>
{% endif %}

And your controller

public function actionCheckEmail(){
    $email = craft()->request->getParam('email');
    if($user = craft()->users->getUserByEmail($email)){
        // user registered
        $userExists = true;
    }else{
        $userExists = false;
    }
    craft()->urlManager->setRouteVariables([
        'userExists'    => $userExists,
        'user'          => $user
    ]);
}


How to remove something from the slug/url

In Craft 3 under your section config change the URL config

https://docs.craftcms.com/v3/sections-and-entries.html#sections

SEE: Entry URI Formats

You can select what template it should render and what URL config you want

Failing this you can use something more advanced:

https://docs.craftcms.com/v3/routing.html#advanced-routing-with-url-rules


Best way to create an entry (bio) view for a User

Not to diminish from Damon's excellent answer — I have certainly used this technique in the past when needed. However, just to provide another option, keep in mind that you don't need to create a separate section/channel for profile information (and thus a plugin to keep them in sync, etc). You can also simply link to a user profile template by creating a route, using username or id (or any other unique identifiable user field or field combination) to identify the user, and then pull data from the custom fields you added to 'users'.

You will first want to create a route in settings to your user profile template. Notice the use of the 'slug' token, which will be available to the template as simply {{ slug }}:

If the URL looks like this:
about/profiles/<slug>   <-- where '<slug>' is the slug token

Load this template:
about/user-profile.twig

In your 'about' page template (i.e. the user's index page):

{% set users = craft.users.group('myGroup').limit(null) %}

    {% for user in users %}

{{ user.firstName }} {{ user.lastName }}

    {% endfor %}

Then, in your 'about/user-profile' template:

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

{{ user.firstName }} {{ user.LastName }}

{{ user.biography }}

    etc...
{% else %}
    {% redirect '404' %}
{% endif %}


Right now the CP Live Preview implementation is very heavily tied to the control panel.  I can't see a easy or clean way to get it to work on a front-end request.

Feel free to make a feature request for it, though.


I have tried the methods answered in this similar question:

Getting this SQL Error: GROUP BY incompatible with sql_mode=only_full_group_by

To fix my issue all I did was edit this file:

nano /etc/my.cnf

with the following line:

sql_mode="NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES"

The website was back up and running instantly.


I would move that logic out of your model class and into a service method.

Your model could still have a thin wrapper for it:

public function getVideos()
{
    return craft()->myPluginVideoService->getVideosByChannelId($this->id);
}

Then your plugin's video service would have:

public function getVideosByChannelId($channelId)
{
    // Can memoize by channelId if this will be called frequently

    $result = craft()->db->createCommand()
        ->select('*')
        ->from('myVideoTable')
        ->where('channelId = :channelId', array(':channelId' => $channelId))
        ->queryAll();

    if ($result)
    {
        return MyPlugin_VideoModel::populateModels($result);
    }
}

The benefits are:

Less cruft in your models, which should (as much as possible) be used as data transport objects
You're not leaking records out of your service layer (like your original model was doing)
You're building a consistent API at your service layer that any other classes (other models, other template variables, other services, other plugins, etc.) can go through.

But essentially you're correct. You'll need to write methods to expose the relationships you're looking for to the templates.

  what is the reason you use the direct database API, in stead of the Record::model()->findAllByPk($pksArray) method?

Habit. Active records, while convenient for simple use-cases, have performance overhead associated with them, so usually I just default to DbCommand.


Disable modification of assets title and name

I guess this feature request will solve my problem... in the future:
http://feedback.buildwithcraft.com/forums/285221-feature-requests/suggestions/7197199-ability-to-hide-or-disable-fields-based-on-a-user

But in the meantime I did it with a small css hacking (no choice):

protected function includeResources()

switch ( $segments[ 0 ] ) {
    case 'entries' : case 'categories' : case 'assets' :
        craft()->templates->includeCssResource( 'plugin/styles.css' );
        break;
}

style.css

// remove title and name fields from modal editor in CP assets page
    .hud {
        .body.elementeditor {
            .field[id$="filename-field"], .field[id$="-title-field"] {
                display: none;
            }
            .field {
                margin-top: 0px;
            }
        }
    }

Works fine :


Override "sameAddress" option when updating cart

It is possible to override sameAddress

The trick to doing this is as follows:

Checkout Step 1: Set your shipping address and use sameAddress1

<form method="POST">
    <input type="hidden" name="action" value="commerce/cart/updateCart">
    <input type="hidden" name="redirect" value="/checkout/shipping">
    <input type="hidden" name="shippingAddressId" value="new">
    <input type="hidden" name="sameAddress" value="1">

      shipping address fields here

</form>

Checkout Step 2: Set shipping method as normal

Checkout Step 3: Set the billing address, including the shipping address as hidden fields, set sameAddress to 0.

<form method="POST" id="paymentMethod">
    <input type="hidden" name="action" value="commerce/cart/updateCart">
    <input type="hidden" name="redirect" value="/checkout/payment">

    <!-- billing fields -->
    <input type="text" name="billingAddress[firstName]" value="{{ cart.billingAddress ? cart.billingAddress.firstName }}">
    <input type="text" name="billingAddress[lastName]" value="{{ cart.billingAddress ? cart.billingAddress.lastName }}">

    etc.
    <!-- tell Craft this is a new billing address -->
    <input type="hidden" name="billingAddressId" value="new">

    <!-- hidden shipping address fields -->
    <input type="hidden" name="shippingAddress[firstName]" value="{{ cart.shippingAddress.firstName }}">
    <input type="hidden" name="shippingAddress[lastName]" value="{{ cart.shippingAddress.lastName }}">

    etc.
</form>


Render generic message/error template from plugin

I'm not familiar with EE's show_error, but a plugin's error template logic is identical to the logic that Craft uses to determine what to display.

From your plugin, if you:

throw new HttpException($statusCode, $optionalMessage)

Where $statusCode is 404 or 400, or any proper http status code, Craft will load the corresponding 404.html, 400.html, etc. from the craft/app/templates folder displaying the optional message, or the more generic message supplied in the template if no optional one is provided.

However, if you have supplied your own 404, 400, etc. from your own craft/templates folder, Craft will display that one instead of the one it provides so you can customize these errors for your site.

If you:

throw new Exception($optionalMessage)

while devMode is enabled, then Craft will load exception.html which will display the full error long with stack trace to help in debugging.  If devMode is not enabled, it will display the more generic error.html.

Craft specifically checks for database connection errors and will display the error.html template while setting a 500 status code.


How can I force a slug to update on save

You could write a simple plugin that listened to the onSaveEntry and perform whatever custom business logic you require for slugs.

Don't re-save that entry going through craft()->entries->saveEntry(), though, or else you'll create an infinite loop by firing the onSaveEntry event again.

You could either call craft()->elements->saveElement() directly or use use QueryBuilder to directly update the database with the slug.


Front End Entry Form Select Dropdown Error

On your dropdown field's name, you need a [] on the end for it to submit properly:

name="fields[demographic][]"

As mentioned in this answer, the dropdown field is basically an array of options, so you need that empty array on the end for them to be stored in.


When does Craft change or delete template caches?

Craft can delete caches in a few different ways:

When somebody manually clears the cache via Settings->Clear Caches
When Elements within {% cache %} tag are saved or deleted
When a new Element is created

Regarding #2 and #3, when Craft can identify which Elements you are caching, it stores them in the craft_templatecacheelements table.  If you save or delete an Element that matches one of the Element IDs that Craft has stored, Craft will clear the related template caches immediately.  In the case where you are creating a new Element, it takes Craft a little bit longer to determine if that Element could be part of a related cached query and once Craft determines that is the case, will clear the cache.

Craft knows which Element IDs exist if your queries are taking place between the {% cache %} tags.

If you'd like to explore a bit more, a few places to explore would be the craft/services/TemplateCacheService.php and craft/app/etc/templating/BaseTemplate.php.


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

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

Hello Craft


Plugins: Is there any events and/or calendar plugin in the making?

Until the Craft plugin store becomes an "official things", the best, most curated list of plugins available for Craft is over on Straight Up Craft

The closest thing I can find on there to what you're looking for is the "Recurring Dates" plugin.


I moved my craft site to a subdirectory, and things are broken now, help!

So I figured it out! my htaccess did not have the "." in front of them. So my htaccess in my subdirectory was not being read. Once I renamed it to .htaccess, it worked fine!


How to redirect site root to preferred language in multi-language site?

I would put a .htaccess file in the public HTML folder and let Apache deal with it, that way you don't have the overhead of PHP/Craft having to deal with that request.


Check if a field a included in a Matrix block

In certain cases Twig is unable to recognize the correct object oriented syntax with magic properties so {% if object.property is defined %} will still produce an error.

You can use {% if block['backgroundColor'] is defined %} it seems strange and would usually considered as wrong syntax (PHP wise) but it works in Twig


Order entries based on last string of title

Another option is to use the Preparse plugin to automatically parse out and store last names in a hidden field.

Install the Preparse plugin
Create a new “Last Name” field using the Preparse field type
Set the field’s Twig template setting to this:

{{ entry.title|split(' ')|last }}

Edit the People section’s entry type settings, and add the Last Name field to its field layout.
Edit the People section’s main settings - just re-save them, so that all of your existing entries get updated.

Now when your People entries are saved, the last word in their Title will be copied over to a hidden “Last Name” field, and you can sort by that in your template:

{% set entries = craft.entries.section('people').order('lastName asc').limit(0) %}


Insert entry into article field (elementselect?) on a user, with the "onBeforeSaveEntry" hook

Sweet, solved it.

Changed the line

array_push($focusList, $entry);

to

array_push($focusList, $entry->id);

and it worked! :D

EDIT: NOPE, it did not fully work. This hook overwrites all the other entries, leaving only the most recent inside the list.

This code does work, without overwriting:

parent::init();
craft()->on('entries.onSaveEntry', function(Event $event) {
    $entry = $event->params['entry'];
    $isNew = $event->params['isNewEntry'];

    if ($isNew) {
        $user = craft()->users->getUserById($entry['authorId']);
        $focusList = $user->getFieldValue('focusList');
        $values = [];

        foreach ($focusList as $focusEntry) {
            array_push($values, $focusEntry->id);
        }

        array_push($values, $entry->id);

        $user->setContentFromPost(array(
            'focusList' => $values
         ));
        craft()->users->saveUser($user);
    }
});


getSectionByHandle doesn't return an array, but a single SectionModel - so you need to remove the for loop:

{% set enroll = craft.sections.getSectionByHandle('workshopEnrollments') %}

{{ enroll.id }}


Stripe: Craft Commerce_OrderModel and its behaviors do not have a method or closure ....

Are you sure all environments are identical? (Same templates, versions, plugins, configs, etc.)

Best way is to check your craft.log file and turn on devMode so you can get a proper dump as to what's causing your problem.


You can have placeholders and default values, but I guess that won't be enough for you.

You could create a plugin that injects some javascript into the new entry page that executes an AJAX call and fills these fields in.


You could use a conditional to check if your "search term" parameter has a value.

Search Results

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

{% if query|length  %}

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

    {% if entries|length %}

{{ entries|length }} results:

            {% for entry in entries %}

{{ entry.title }}

            {% endfor %}

    {% else %}

Your search for “{{ query }}” didn’t return any results.

    {% endif %}

{% else %}

You forgot to enter a search term.

{% endif %}

(This code is modified from the examples in the Craft docs for "Search Form")


Get entries based on custom field lightswitch in custom plugin in Craft 2

You need to set the value of your Lightswitch field directly instead of using the relatedTo parameter

$criteria->lightswitchFieldHandle = '1';


craft.request.getQuery returns empty on multi lingual site

Turns out the problem was with the Nginx config as Brad suggested. There was a question mark behind the p=$1. When we removed the ? and restarted PHP it worked.

location / {
    try_files $uri $uri/ @rewrites;
  }

location /en/ {
   try_files $uri $uri/ @enrewrites;
  }

location @enrewrites {
    rewrite ^/en/(.*)$ /en/index.php?p=$1 last;
  }

location @rewrites {
rewrite ^(.*) /index.php?p=$1 last;
}


Sounds like you just need to initially setup the client account with your colleague's email/password/name.

Have them do their testing and when you're ready to give it to your client, either just update the email/password/name or delete the client account and re-create it for your client.


Joining sections (Channel) to query based on a related item attribute

Not sure about doing this in a single query. Referencing this doc page section, https://docs.craftcms.com/v3/relations.html#templating, you can make use of relatedTo() by passing in IDs.

Assuming you know the ID of the category already and that ID is 1:

{% set showIds = craft.entries.section('shows').relatedTo(1).ids() %}

{% set entries = craft.entries.section('events').relatedTo(showIds).all() %}

The relatedTo() method doesn't care if the IDs are from Entries or Categories, or even Users and Tags. They're all Elements stored in the elements table so they have unique IDs.


Getting entries related to current entries categories

This solution should also remove your "featured" category entries:

{% set categories = craft.categories.relatedTo(entry) %}
{% set featuredCategory = craft.categories.slug('featured').first() %}

{% set allIds = [] %}
{% for id in categories.ids() if id != featuredCategory.id %}
    {% set allIds = allIds|merge([id]) %}
{% endfor %}

{% set entries = craft.entries.relatedTo(allIds) %}


How to get my craft projectConfig settings inside JS on the front end

{% js %}
    let admin_email = "{{ admin_email }}";
    console.log(admin_email);
    alert(admin_email);
{% endjs %}

Missed the "" around the assignment. :)


What is the future for the Craft rich text editor?

Eventually, if a Craft CKEditor plugin doesn't come to life, then we might consider developing a 1st party (Pixel & Tonic) CKEditor plugin, although it would likely not replace the native Redactor Rich Tech Field implementation.

And of course, 3rd party plugins can implement whatever WYSIWYG they want to use.


Importing tables and Tablepress like plugin functionality

How do you plan to manage it in Craft?

If you want each row as a separate entry, create a section to hold the data, setup the fields you want to use to match each column, then use Bob Olde Hampsink's import plugin to bring in the data. The plugin allows you to map the data to the fields in Craft.

Craft supports tables natively but you need a way to import them. You can use Bob's import plugin but you'll need to use the plugin's hooks to get the data where you want it. Tables in Craft are stored as JSON so you have to do some data wresting.


confused by inconsistent behavior of matrix

Since you have 3 block types in your Matrix, you need to check for the block type before trying to call an inner field.

{% block content %}

{{ entry.title }}

TEST EDIT 8

        {% for block in entry.blogPost %}
            {% if block.type == 'imageCaption' %}

heading - {{ block.heading }}

sub heading - {{ block.subHeading }}

            {% elseif block.type == 'WhateverTypeAccoladeIsIn' %}

accolade- {{ block.accolade }}

            {% endif %}
        {% endfor %}

Posted on {{ entry.postDate.format('F d, Y') }}

        {{ entry.body }}

{% endblock %}

You should add that check before block.accolade as well for whichever block type accolade is in.


Firstly the Craft install on your beta site, does it share the same database?

Craft's multi-site setup is meant to use a single database.

  Secondly I'm not exactly sure what 'step 7' is asking me to do, could you please explain?

Step 7: Configure Your Web Server and DNS

1.  Configure your web server so the domain (e.g. beta.craftcms.com) points at the web directory. Craft will automatically detect which site the browser is requesting.
2.  Update your DNS records so the domain points at the web server.

It will be difficult to answer without knowing your specific environment and where your domain name is registered or if you're just doing this locally with a development domain.


Install MySQL stored procedure to database on plugin install in Craft 3

After further investigation Yii handles the DELIMITER ;; in the createCommand() method. I just had to remove the opening and closing DELIMITER syntax and execute the Craft::$app->getDb()->createCommand("CREATE PROCEDURE ( ... )")->execute() for it to install.


Order/sort a structure section by date too?

This should work

{% set entries = craft.entries({
    section: 'news',
    sortOrder: 'postDate desc, lft asc',
}) %}


Including plugin javascript on the client side

Think you need:

UrlHelper::getResourceUrl('path/to/file.ext')

There is more info over at http://buildwithcraft.com/docs/plugins/resources

Edit

ok, scrap that answer, I didn't read the rest of the help page :)

I think you are nearly there though. I'll have a test on my local machine and get back to you.

Edit 2

I believe I'm running into the same problem you have described as well. I did however get it working by doing this:

<script src="{{ resourceUrl('eforms/js/public.js') }}"></script>


Find a single user by his/her affiliateId (int).

$criteria = craft()->elements->getCriteria(ElementType::User);
$criteria->affiliateId = 1;
$users = $criteria->find();

  Query all users within the Affiliate group, order by studentCount asc and return the top record. (I need a user that has the fewest number of students).

$criteria = craft()->elements->getCriteria(ElementType::User);
$criteria->affiliateGroup = 'myGroup';
$criteria->order = 'studentCount ASC';
$criteria->limit = 1;
$users = $criteria->find();


The answer depends on the field type.

Assets, Categories, Checkboxes, Dropdown, Entries, Matrix, Multi-select, Radio Buttons, Tags, and Users fields each will each appear to be non-empty, even if they don’t have a user-submitted value, because in each case, entry.myFieldHandle will still return something:

an ElementCriteriaModel object for Assets, Categories, Entries, Matrix, Tags, and Users fields
a MultiOptionsFieldData object for Checkboxes and Multi-select fields
a SingleOptionFieldData object for Dropdown and Radio Buttons fields

So for each of those field types, you will need to use the |length filter to determine if there is an actual value.

{% if not entry.myCheckboxesField|length %}

Also worth noting for Assets, Categories, Entries, Matrix, Tags, and Users fields: ElementCriteriaModel objects also have a total() function which can be used to speed up your template if all you need to know is whether any elements were selected, and you won’t be actually looping through them further down in the template. (|length actually fetches the elements first, and then returns the count of the results, but stores those elements internally in case they are going to be looped through later on. .total() runs a separate, much less expensive DB query, but the results won’t be cached internally for later use.)

{% if not entry.myCategoriesField.total() %}

Color, Date/Time, Lightswitch, Number, Plain Text, Rich Text, and Table fields will each return an empty value (null, an empty string/array, etc.) when there is no value, so each of those field types are safe to use with the is empty test:

{% if entry.myTextField is empty %}

This would also work:

{% if not entry.myTextField %}

In all cases, the |length filter will work as expected, so if you don’t want to remember this stuff, just use that.


You can do that with Behaviors. There is a good Working example made by Pixel and Tonic team within their Git for the DotAll workshop

namespace ontherocks\behaviors;

use craft\elements\db\EntryQuery;
use craft\elements\Entry;
use craft\elements\User;
use yii\base\Behavior;

/**
 * Class UserBehavior
 *
 * @property User $owner
 * @property EntryQuery $recipes
 */
class UserBehavior extends Behavior
{
    public function getRecipes(): EntryQuery
    {
        if (!$this->owner->id) {
            // just return a query that is doomed to fail
            return Entry::find()
                ->id(false);
        }
        return Entry::find()
            ->section('recipes')
            ->authorId($this->owner->id);
    }
}

See how to attach behaviors

// define user behavior
Event::on(User::class, Element::EVENT_DEFINE_BEHAVIORS, function(DefineBehaviorsEvent $event) {
    $event->behaviors[$this->id] = UserBehavior::class;
});

You can attach your custom behavior to the user class as well exactly like the code above and do

{% set user = currentUser %}
{% set subscriptions = user.getActiveSubscriptions() %}

Or you attach the behavior to the SubscriptionQuery and do

{% set subscriptions = craft.subcriptions.activeSubscriptions(currentUser).all() %}


How to get the original entered password value on registration form error?

You could always use the Craft request object to repopulate the fields, however I don't think it's common practice to repopulate password fields for security reasons.

Another thing that is considered bad practice is differentiating behaviour for partially correct credentials - the response from the web server should always be the same whether one part or multiple parts are missing from a correct authorization submission to prevent information disclosure to a potential attacker.


You'd have to do the in multiple queries at this point.

Consider voting for this feature request, which is asking for what you're looking for.


Phantom routes load index template, no 404 and no errors

Thanks to P&T via a support ticket, this issue has been resolved.

Basically I just had a structure which, some point, I had used as my navigation and was causing a conflict. Something which I just completely overlooked.

From P&T via a support ticket:

  The issue seems to be in your Navigation structure. You have "Entries in this section have their own URLs" turned on, but you do not set a template for the section. So, when a user requests 'search', which is an entry in your Navigation structure, Craft defaults to show the index.twig template.


Technically it's a combination of the element's slug plus URI that is required to be unique (on a per-locale basis).

You can see the logic used and where it's enforced at the code level in ElementHelper::setUniqueURI()


A compatibility issue with Craft 3.4.1 and maybe earlier. Running PostgreSQL12+ (PgSQL12+) couldn't finish Craft's installation setup.

I have no idea what change in PgSQL12+ could cause this behavior but downgrading PgSQL12 to lower versions like PostgreSQL11 worked as expected.

However Craft's docs states that PostgreSQL 9.5+ should meet Craft’s server requirements.

I can't take this for answer to my question because if this is an incompatibility issue than downgrading doesn't answer my question. I believe you guys get to it and crack it as always.

Anyway here are the logs when tried to run Craft with PgSQL12+.

phperrors.log
web.log

Update

Running Craft 3.4.6 along with PostgreSQL 12.2 seems to remove the problem. I checked the CHANGELOG-v3 and I couldn't find anything related to PgSQL database.


If you're using a for loop to output the entries you can make use of loop.index which will give you the current iteration of the loop (starting from 1) and loop.length which is the total number of items in the for loop.

So something along the lines of:

{% for entry in craft.entries.section('projects') %}
   {{ entry.title }} - {{ loop.index }} / {{ loop.length }}
{% endfor %}

Or, if you're on a single entry you can use this answer by Marion Newlevant.


Templating - printing entry.url throws js error when trying to embed Disqus?

Try wrapping your entry.url in quotes so JS reads it as a string.

this.page.url = '{{entry.url}}


It looks like you're not returning the user model back to the template.

You need something like this in your controller.

craft()->urlManager->setRouteVariables(array(
    'account' => $user
));


How can I get an entry's child category, and not the parent category?

Your Categories field (entry.chooseLocation) is an Element Criteria Model. It's exactly the same as if you'd done something like this...

craft.categories.group('myGroup')

The Element Criteria Model (ECM) is an important concept in Craft, because it's the driver for most content handling. Once you've got a bearing on what the ECM is, you can use it fairly consistently.

For example, just like craft.entries, you can apply .find, .first, and other methods to retrieve a specific subset of elements. And depending on the element type, you can chain other filtering methods in between...

craft.categories.group('myGroup').limit(3).find()

Returning to your question, entry.chooseLocation will be an ECM for Categories. So it's just a matter of filtering it appropriately.

{% for category in entry.chooseLocation.level(2).find() %}

Here is the full list of filtering options you can use on a Categories ECM.


There is an overview of how routing works in Craft here: https://craftcms.com/docs/routing and that guide works for front-end routing as well as Craft's own control panel routing when deciding which (if any) templates to render.

The call to p=admin/actions/elementIndex/getElements is a control panel AJAX action request (#3 is the routing list) that's going to the craft/app/controllers/ElementIndexController.php's actionGetElements method and returning data.


Is it possible to skip entries with children?

So after some rest, I did find the solution as follow:

'criteria' => ['section' => ['pages', 'home'], 'level'=>array(null,1)]

This gives me the desire result.


Loop through all created fields and get the field type

Making sure you include use craft\db\Query; at the top of your Class, you can do something like this:

$fields = (new Query())
    ->select(['id','handle','name','instructions','type'])
    ->from(['{{%fields}}'])
    ->all();

foreach ($fields as $field) {
    $output[(int) $field['id']] = array(
        'id'            => (int) $field['id'],
        'handle'        => $field['handle'],
        'name'          => $field['name'],
        'instructions'  => $field['instructions'],
        'type'          => $field['type']
    );
}


How can I restrict tags that are able to be used?

That’s not possible, as tags are meant to be more of a “folksonomy” rather than a strict taxonomy.

Sounds to me like categories would be a better fit for your needs. With those, the options are defined in advance, and chosen from the fields, rather than created from the fields.

Update:

We just updated the Categories and Tags feature page with a new video that may do a better job showing the difference between the two.


There is a pull request on github for this. You can see what to do here if you want to fix locally until an update is released.


Thanks for that tip, Lindsey, but here's what happened: I changed calendar37 to calendar37_Occurrences and then all three tables wouldn't uninstall.  It turns out that the issue is having a capital letter after the underscore.  So it seems like an "undocumented feature" that you can make plugin tables persist after uninstall by including a capital letter after the underscore.

return 'calendar37_Subsets';
return 'calendar37_Views';

needed to become:

return 'calendar37_subsets';
return 'calendar37_views';


Home page loop once, other pages loop through all

You could do it this way:

{% set blockLimit = entry.section.isHomepage() ? 1 : null %}

{% for block in memberships.memberships.limit(blockLimit) %}

    {% for asset in block.logo %}

    {% endfor %}

{% endfor %}

entry.section returns the SectionModel for the entry, and isHomepage() tells you whether that section is the homepage. Then you set your limit accordingly.


Twig filter to convert special characters to URL friendly characters

My first idea would be to use the url_encode filter than would turn your special characters into a URL friendly format. I imagine these would also be Javascript friendly.

If you'd like to convert the characters to something like "ueber-uns" maybe take a look at the convert_encoding filter in Twig. It uses a function called mb-convert-encoding.

I tried to find what encoding you'd need to use to convert Ü to something javascript friendly but was only able print even more obscure characters.


Is there a way to merge multiple Plain Text fields into a Matrix or Table field?

You can obviously perform your import directly from the CSV files (or remote database), reformat the data and then import; or import the data into temporary craft text fields and then in a separate operation, move the data in craft. Personally I would opt for the first solution to avoid the extra work of creating temp fields (and allow me to import again later if needed).

This example is for importing into a table field (importing into a matrix field would be quite different and slightly more complicated).

// loop through each row in your CSV file or results on external database query
foreach($result as $row)
{
    $entry = new EntryModel();
    $entry->sectionId   = 1;
    $entry->typeId      = 1;
    $entry->authorId    = 1;
    $entry->enabled     = true;

    // create temp array to store feature data
    $myTableFieldData = [];
    array_push($myTableFieldData, array("col1" => $row['feature1']));
    array_push($myTableFieldData, array("col1" => $row['feature2']));
    array_push($myTableFieldData, array("col1" => $row['feature3']));
    // ...

    $entry->getContent()->setAttributes(array(
        'myTableFieldHandle' => $myTableFieldData,
    ));

    // if you want to check the data first before importing
    // uncomment this and comment out saveEntry below
    //var_dump($entry);

    $success = craft()->entries->saveEntry($entry);

    if (!$success)
    {
        $errors = $entry->getErrors();
        foreach ($errors as $error) {
            MyImportPlugin::log('Error:'.$error[0], LogLevel::Error);
        }
    }
    else
    {
        MyImportPlugin::log('Successfully saved entry "'.$entry->id.'"', LogLevel::Info);
    };

}


{% Exit 404 %} doesn't work right with routes

When a route is accessed, entry isn't automatically defined.

The actual value for * is available in the any variable in your template—see this example:

Route: projects/type/(*)
Template: _test

{# templates/_test.twig #}
Substituted route param: {{ any }}

Do note that entry is not automatically set by Craft for any custom routes! It's your responsibility to take the incoming value (in your case, any) and look up the Entry, within the template, or bail if nothing is found:

{# We can totally skip the test for `any`, because this template is only loaded when there's a non-zero-length value in the route: #}
{% set entry = craft.entries({ title: any }).one() %}

{% if not entry %}
  {% exit 404 %}
{% endif %}

{# At this point, you've set `entry`, and can use it however you like! #}
{{ entry.myFieldHandle }}

  I'd recommend reviewing the requirements for your route, and consider using a more specific token in your route (i.e. (slug)), and matching based on a more stable value—titles can change, but IDs should stick around, and slugs are less prone to editing!


Which eWay are you implementing?

We have used eWAY Rapid Direct with no issues for over a year now with Commerce.

They key most people seem to miss is that you must implement the CSE - Client Side Encryption part of the eWay process before the gateway submission to eWay will wok.  This is documented by eWay here:

https://eway.io/api-v3/#how-it-works

In essense you just need to include some JS on your payment form page.  On that page you accept the card number etc pretty much as normal, but before you pass it to eway is must be encrypted on the client machine.  Here's some scrappy JS to show what I mean first include the eWay JS:

                    {# Include EWAY CSE JS #}
                {% if craft.config.devMode %}
                    <script src="https://secure.ewaypayments.com/scripts/eCrypt.debug.js"></script>
                {% else %}
                    <script src="https://secure.ewaypayments.com/scripts/eCrypt.js"></script>
                {% endif %}

...and in your payment form have something like this as you will need it later (I store my key in the commerce config file so I can set a dev vs live one based on environment, but you could jstu hardcode it below or whatever):

              {% set paymentMethods = craft.commerce.paymentMethods %}
                  <input type="hidden" name="CSEKey" value="{{ paymentMethods[1].settings.CSEKey }}">

Then, you need to deal with the actual encryption side of things...something like this should do it for you:

        var method = $('input[name=paymentMethodId]:checked', '#submitOrder').val();

    if (method == 1) {
        // EWAY Client Side Encryption - See https://eway.io/api-v3/#encrypt-function

        // EWAY CSE - encrypt the card number and the CCV on the client's machine so it's never seen by us or anyone else.
        // Pull the values from the test input fields (fake*)
        // and inject the encrypted version into the real fields
        var $fakeCardNumber = $("#fakeNumber");
        var $fakeCcv = $("#fakeCvv");
        var $cardNumber = $("input[name=encryptedCardNumber]");
        var $ccv = $("input[name=encryptedCardCvv]");

        // get the unencrypted values and remove all spaces from the numbers
        var cardNumber = $fakeCardNumber.val().replace(/\s/g, "");
        var ccv = $fakeCcv.val().replace(/\s/g, "");

        // and now re-set them to the encrypted values if these are actually filled in
        if (cardNumber) {
            $cardNumber.val(eCrypt.encryptValue(cardNumber, CSEKey));
        }
        if (ccv) {
            $ccv.val(eCrypt.encryptValue(ccv, CSEKey));
        }
    }

That's not the most beautiful code, but I think once you've implemented that (which you need tom to be compliant with eWay) - you will find things drop into place easily enough from there.


Sprout Forms Confirmation Page Field Not Defined

When you set the lastEntry variable on your page. You are either setting lastEntry to be the SproutForms_FormEntryModel or null. In both cases you are defining the variable.

{# Sets lastEntry to SproutForms_FormEntryModel or null #}
{% set lastEntry = craft.sproutForms.lastEntry() %}

If you then want to test the lastEntry variable, you'll need to use something different that is defined.

I believe the following will work:

{% set lastEntry = craft.sproutForms.lastEntry() %}
{% set qualified = true %}
{% if lastEntry and lastEntry.fieldName == 'no' %}
    {% set qualified = false %}
{% endif %}

Testing for if lastEntry will work just like using if lastEntry is not null. The secondary part of your conditional should only be evaluated if if lastEntry returns true, in which case your SproutForms_FormEntryModel would exist and the fieldName field would be defined, with or without a value.


Assuming your example code, something along these lines will help...

{% if entry.yourTable | length %}

    {% for row in entry.yourTable %}

       {{ row.column1 }}	{{ row.column2 }}	{{ row.column3 }}

    {% endfor %}

{% endif %}

Here's the relevant section in the docs: Table fields


Selling products with a digital variant using Craft Commerce

You could definitely use standard products/variants to sell something that is digitally delivered. The Digital Products plugin just provides a nicer way to verify the ownership of a purchase, but you can see if someone has purchased a particular variant with something like this:

{% set orders = craft.commerce.orders.user(currentUser).hasPurchasables(product.defaultVariant).find() %}
{% if orders|length %}
 You already purchased this item in  Order #{{ orders[0].shortNumber }}
{% endif %}


Show all discounts at checkout with Craft Commerce

Hmm, something else is at play here.

I use:

{% if cart.totalDiscount != 0 or cart.couponCode %}
  - {{ cart.totalDiscount | currency(cart.currency) }}

...and it works.

Are you sure the promotion is actually being applied?  I guess it is if you can calculate the discount.


If CSRF protection is enabled (and it is by default for the latest versions of Craft), Stripe was getting a server error when using the Charge webhook url. The solution is to disable CSRF for the webhook URI - you can read more about it here

or simply add this to your config/general.php file:

'enableCsrfProtection' => (!isset($_SERVER['REQUEST_URI']) || $_SERVER['REQUEST_URI'] != '/actions/charge/webhook/callback'),


GraphQL - Get related entries based on tags field

I might be missing something here but wouldn't passing the current entry's tag IDs rather than the entry ID work for you?

query QueryName {
  entries(
      section: "article",
      limit: 2,
      relatedTo: [123,456] // Current article's tag IDs?
  ) {
      id,
      title
  }
}


My headings were created alphabetically but Z-A not A-Z, I solved by modifying the dom elements and then clicking save.

var source = document.querySelector('.cs-sidebar .sources');
var divs = [...document.querySelectorAll('.cs-sidebar .sources .customize-sources-item')];
divs = divs.slice(0).reverse();
source.innerHTML = '';
for(let i = 0; i < divs.length; i++){
  source.appendChild(divs[i]);
}


How to compare just the date part of a dateTime field?

I'm not 100% sure I'm reading your question correctly, but here goes:

I'm assuming you're getting the date as a string as a route param (maybe you shouldn't call it date though, as that is a function in Twig), I'll call that dateTime here:

{% set dayAfter = date(datetime) | date_modify('+1 day') | date('Y-m-d') %}
{% set entries = craft.entries.section('events').startDate(['and', '>= '~datetime, '< '~dayAfter]) %}

What's happening is you get a date, then you add one day to that date, then find all entries that have startDate greater than or equal to the date you're looking for, and less than the day after, thus getting all entries for that particular date.

This might be over engineered though, and there might be a way simpler way to do this...


Installing the Charge plugin breaks plugin page

So the answer at the bottom of the page here ended up solving my issue.

The plugin was using php short array syntax " [ ] " which is available in 5.4 + and the server was set at 5.3.29.  When I changed that it worked perfectly.


How do I create console commands for my module or plugin?

What's the equivalent for registering console commands?

You don't need to listen to an event to create a console command. Assuming your namespacing and autoloading is set up correctly, they'll "just work".

I'd suggest going over to https://pluginfactory.io/ and select with Craft 3 plugin or module and tick the "Console Commands" checkbox to see a working example of how the files and structure is set up.


Returning specific fields with elementCriteriaModel

Not 100% certain, but I suspect that with a standard ElementCriteriaModel it's all or nothing, as craft generally returns the full userModel including all custom fields. If you're worried about memory limitations, then you might want to either process the users in batches and/or create your own custom query. Something like this perhaps:

// define query
$criteria = craft()->elements->getCriteria(ElementType::User);

$query = craft()->elements->buildElementsQuery($criteria);
$query->select(array('elements.id AS id', 'field_extid AS extid', 'field_lastupdatetime AS lastupdatetime'));
$query->limit(null);
$results = $query->queryAll();

foreach($results as $result)
{
    // compare craft values to json

    // var_dump($result['extid']) // uncomment to test
    // var_dump($result['lastupdatetime']) // uncomment to test
    // set $userExists equal to true or false if needed

    // create/retrieve user
    if ($userExists) {
        $user = craft()->users->getUserById($result['id']);
    } else {
        $user = new UserModel();
    }

    // update, save, etc
    // ...
}

You can also use var_dump($results) to test whether you are getting the expected results.


How to output an Entries Field value within a Matrix Field block

You get this error because {{ block.slideLink }} returns an array of EntryModels (even if you set your Entries field up to only allow 1 entry). So you need to add the first() method to get the first (and only) EntryModel that you are interested in:

{% set entry = block.slideLink.first() %}

Access the url property to return the URL of that entry:

{% if entry %}
    {{ entry.url }}
{% endif %}


Changing an entries data in onBeforeSaveEntry event depending on data set in saved entry

You need to set an array of category IDs to your categories field.

$featuredEntry->setContentFromPost(array(
    'category' => array(42)
));

Query for it with an craft.categories criteria model or if you're hardcoding it anyways you could just look it up in the CP:

  example.com/admin/categories/myCategoryGroup/42-myCategoryHandle


Sprout Forms: Localized version of form with translated labels?

Currently, Sprout Forms (v1.1.0) is translatable, but we have not yet added support for Craft's localization feature.

You can find a template of a translation file on our Github Account.

We want to support localization in the long run. The main reason we have not yet is mostly just one of priority. In comparison to other advanced features, we have had very few requests for it (feel free to send us a note and advocate for how this needs to work for you) and development for it isn't documented anywhere (feel free to send Pixel and Tonic a request for documenting it better too!). At the time of this posting, I posted a question here about the topic 7 months ago [February 2015] and the question has received one upvote and 41 views:

How do you add support to manage content in multiple languages in a plugin with a custom Element Type?


Have you tried this:

{% set currentMemberNews = craft.entries.section('news').relatedTo({
    targetElement: currentMember,
    field: 'yourFieldHandle',
}) %}

To specifically define the correct field and the fact that the member is on the target side of this relation?


Just recently worked a similar project so I'll share what we've uncovered.

Your needs are basically 2-fold: take payment and provide a platform where people can log into and consume content.

  Is this possible with CraftCMS or would it need Commerce?

Craft has you covered on content, however taking payment is where having Commerce built-in can be beneficial.

If you're able to build the billing component yourself, you don't need Commerce, however having it built-in, can save a lot of development time and having it integrated in the same system makes sense, especially if someone might have access to tiers of content (this class goes here, this one is not included, etc.) Using Craft's user groups can be pretty beneficial here.

  We're thinking the Stripe payment plugin - any advice/alternatives?

Does your client already have other systems where their customers already "subscribe" (eg MindBody, ZenPlanner, Pike13, etc)?

If that's the case, you could interface the two. Upon login (or periodically via cron) ping the 3rd party billing system and see if there's an active subscription. If there is, let them continue; if not, throw up an error message.
If you're going to offer subscriptions, Stripe is currently the only first-party gateway to do so. Payment Gateways are definitely a weak spot for Commerce at the moment.

A lot of offline business will have their own merchant accounts so using a gateway like Authorize.Net is pretty common, however the 3rd party plugin doesn't support subscriptions yet.

As an aside, payment processors hate risk. And the risk becomes higher with digital products, subscriptions, etc. Payment gateways like Stripe are notorious for withholding funds especially if your client scales too quickly, too fast.

When it comes to recurring billing and high ticket amounts, these are payment processor red flags. Having a solid payment processor history is helpful when it comes to disputes and chargebacks. It's a huge bummer to have a client have a super successful launch and then they get an email from their processor saying their account is on hold or that they're withholding funds for 2 weeks. Having a "backup" option of taking payment even if it's manual can be helpful.

  Can we stream off S3 or is it better to use Vimeo CDN, is there a plugin to assist?

Unless you like giving more money to Bezos than necessary, bandwidth on S3 is expensive. And it doesn't really handle video all that well IMO. If someone is on a smartphone with a mediocre internet connection, you don't want to send them down 4K video when medium quality SD or HD would suffice.

Vimeo and Wistia are better options in this respect. These services can tailor the streaming experience and you can easily lock down content. Wistia excels when it comes to more analytics (who watched what, how long and more). Their API is pretty easy to work with as well.

  How to ensure videos are only available to logged in users?

Both Wistia and Vimeo have options to embed only on domains but unlike YouTube and Vimeo, Wistia doesn't let Google index its content by default so it makes it a slightly better choice.

Unlike YouTube where there's not really a good way to prevent users from going back to watch the video on the site (where users can then go and bookmark it), you can customize the player and branding so it's really "embedded" on the site.

  What happens when somebody unsubscribes - how do we automatically remove/pause their access?

On every page load of a restricted page, typically you want to {% requireLogin %} and then check to see if the user has an active subscription.

The Commerce docs and included templates are good examples of a starting place.

Depending on how you setup your site and what the user has access to,  you can relate the Commerce subscription with the class/video module in question to make sure the user is only allowed to see what they currently have purchased or have access to.


Cache Creation

The primary files to look at are Cache_Node.php in app/etc/templating/twigextensions and TemplateCacheService.php in app/services.

The Cache Node does some basic checking before deciding what content to output, and what to do with that content. It builds a cache key (either automatically, or uses the one defined), and then looks for an existing cached template. If a cache is found for that key, it is outputted and the template process moves on. If no cache is found for that key, the caching process begins.

If the cache node is not in a situation where caching is being ignored (either Live Preview or a config setting), then it starts caching with the Template Cache Service. That service has a method called startTemplateCache. This method creates an array of element queries and element IDs that are stored under the cache key.

The template within the cache node is then compiled with Twig's subcompile method. While this is happening, the element queries and resulting element IDs are stored in the previously created arrays. The compiled result is then placed in a variable and then given (along with the other cache parameters) to the endTemplateCache method.

The endTemplateCache method stores the cache in the templatecaches table. It also loops over the element criteria and element id arrays to tag the caches using the templatecachecriteria and templatecacheelements respectively.

Finally, the cache tag outputs the body (cached or otherwise).

Cache Clearing

When you make changes to elements in the control panel, the Template Cache Service does one or two things to clear caches:

If the element was modified or deleted, it will check the templatecacheelements table to immediately clear any caches that it knows the element was involved in.
If the element was added or modified, it queues up a new “Delete Stale Template Caches” task, which cycles through the templatecachecriteria table to see if there are any cached element queries which (if not cached) would have started showing the just-saved element. If the query returns the element, its corresponding cache will be deleted.


Using variable references to fields in a template

Figured it out:

{% for n in [1, 2, 3, 4, 5, 6] %}
    {% if attribute(matrixBlock, 'quote' ~ n)|length %}

        {{ attribute(matrixBlock, 'quote' ~ n) }}
        {{ attribute(matrixBlock, 'attribution' ~ n) }}

    {% endif %}
{% endfor %}


Direct relation between templates and PHP

A very similar question has been asked before How can I follow relations in ElementCriteriaModels from plugin code. You would want something like this:

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = 'products';
$criteria->relatedTo = array(
    'targetElement' => $e,
    'field'         => $field
);
$elements = craft()->elements->findElements($criteria);

Where $e is testVariant, and $field is productVariants.productSizes, which can be retrieved using craft()->elements->getElementById(id) and craft()->fields->getFieldById(id) or getFieldByHandle(handle) respectively.

To create a controller see the documentation on Controllers.


Firstly, you'll need to change:

{% for row in superTableBlock %} to {% for row in block.superTableBlock %}

So you can reference the Super Table field within your out Matrix field.

You also don't need to loop through {% for block in row.superTableBlock %}

I can't tell if you're looping through the inner Matrix field. Have you tried:

{% for block in entry.matrixFieldParent %}
    {% if block.type == "sectionBuilder" %}
        {% for row in block.sectionBlock %}
            {% for sectionLayout in row.sectionLayout %}
                {% switch sectionLayout.type %}
                    {% case "contentBlock" %}
                       {{ sectionLayout.sectionGrid }}
                    {% case "articlesFeedBlock" %}
                       {{ sectionLayout.sectionBackground }}
                {% endswitch %}
            {% endfor %}
        {% endfor %}
    {% endif %}
{% endfor %}