Is it safe to have PHP files in the public directory of a craft cms website? I just ‘discovered’ that the developer on my client’s craft website has been migrating his old landing pages to the new setup by cramming the older static pages into the public directory instead of using the CMS.

There are a lot of PHP files there but they are mostly using a PHP mailer setup - not any database queries.

My question is: how dangerous or safe is it to have PHP files in the public directory?

As long as the PHP files themselves don't introduce any security holes, having them in your web root is perfectly fine. That's not uncommon at all. How they were written makes all the difference, of course. If you aren't sure whether the developer followed best practices for the security of the mailer scripts, that might be of some concern.


Showing specific category content as well as ALL I have a photography website with just two categories, 'Lifestyle' and 'Food'.

I have two problems:

How to display the content

I have a category group called 'photoCategories' to which I have assigned two categories 'Lifestyle' and 'Food'.  I also have a Category field 'imageCategory'.

At the moment I have two Entries, one for Lifestyle and one for Food and I have linked to the appropriate category in the category field.

I have a 'category' template but I can't figure how to pull the appropriate content based on the URI and how to pull All content (within) the category group for when the user selects All?

My navigation needs to look as follows:

Homepage (Single)
All Images ('images' Channel)
Lifestyle Photos ('images' Channel)
Food Photos ('images' Channel)
About Us (Single)
Contact Us (Single)

Any advice is very much appreciated.

Thanks

Martin

It sounds like you're pretty close. I'll work off of the assumption that you want the URLs to be structured like this:

/images

/lifestyle

/food

…and an individual image will be at /images/{image slug}.

Like you've done, I'd create a category group called photoGalleries. I'd set the URL format simple to /{slug} and a max level of 1. The two categories can then be added to that.

The images channel would have a URL format of /images/{slug}.

By setting up categories and the channel like this, Craft will automatically create variables when it matches those paths. You can then use these variables to do practical things.

To show images by category, you'll need to use to .relatedTo() method. When Craft matches a category route, it will create a category variable which can be passed into this method. In the template for a category, you'll need code like this:

{% set images = craft.entries.section('images').relatedTo(category).find() %}
{% for image in images %}

{% endfor %}

This asks Craft to find any image which has been categorised by (related to) a specific category.

You can then create a simple images listing via a template for /images. If you wanted to get really fancy, you could output what categories an image is under. To do that you'd need code like this.

{% for image in craft.entries.images.find() %}

    {% for category in craft.categories.relatedTo(image).find() %}

{{ category }}

    {% endfor %}

{% endfor %}

Hope that gets you going.


What is the Pricing Structure for CraftCMS? Is the pricing listed on the site for the Pro & Commerce versions of CraftCMS a one time, per-site charge or a reoccurring charge (i.e. monthly, yearly)?

One license per site, not a renewal (one time purchase)


Fetch Single asset without for loop? I've got a Assets field which is limited to 1 asset inside a Matrix field. Is there a better way to fetch the image without using a forloop?

At the minute I've got:

{% for image in block.backgroundImage %}
    {{ image.getUrl() }}
{% endfor %}

But it just doesn't seem right using a forloop when I know there is only one image.

Thanks

You can call the first() function to just get the first image:

{% set image = block.backgroundImage.first() %}
{{ image.getUrl() }}

But this will fail if there is no image. So to be safe, you need:

{% set image = block.backgroundImage.first() %}
{% if image %}
    {{ image.getUrl() }}
{% endif %}


How to connect to a Remote DB using CraftCMS 3.1+ I tried using Andrew Welch's craft-connect plugin, but the plugin was throwing an error on the REMOTE_DB_SERVER variable. I commented the variable out in the .env file, but I still could not connect. I also tried just using the IP address of the remote server in the DB_SERVER variable.

What I hope to accomplish is a shared development DB which exists on a remote server. I can then connect both of my local environments to that development DB. When I know that the development DB is good to go, I will synch it with my production DB. But, I cannot sort out how to accomplish a remote DB connection with CraftCMS.

I can connect to my development DB using the SSH method in SequelPro (requires SSH host IP, SSH user and SSH password). I've tried replacing the DB server variable with the IP address of my server in my .env file, but that doesn't work. If I'm tunneling into the server using SSH first in SequelPro, wouldn't I also need to do the same with my connection in Craft?

Probably the database doesn't listen for connections from the outside world. So yeah you'd need some kind of an ssh tunnel to connect to it. I think you may find, though, that doing development via a remote db can be pretty slow; check out Database & Asset Syncing Between Environments in Craft CMS


Passing an array on Template Render I am trying to pass an array on template render like this:

public function actionConfirm()
{
   $variables['confirm'] = array(
      'name'   => 'Silvanus',
      'office' => 'OOTS',
    );
    $this->renderTemplate('confirm', $variables);
}

And output it in layout like this:

{%for variable in variables%}
  {{variable.name}}
  {{variable.office}}

This is just an example, mainly after client pays using paypal I get a payment Object that I want to pass it to the confirmation layout as per explanation above. I have tried it doesn't work.

Anyone has an idea on how to achieve this?

Thanks

Cheers.

Yes, you do it with the setRouteVariables mathod on UrlManager, like this:

craft()->urlManager->setRouteVariables(array(
    'variable' => array(
      'name' => 'silvanus',
      'office' => 'OOTS
    )
));

$this->renderTemplate('confirm');

Now you can access your variables like this in your template:

{{ variable.name }}
{{ variable.office }}

Check out the documentation on Controllers, there's an example.

EDIT:
The above method is how I've done it in the past, but I see now that the renderTemplate method also can be used. I think the problem is just how you access it in your templates, I think you should just do:

{{ confirm.name }}
{{ confirm.office }}


How would I detect whether an entry's asset field has been updated on save? We need to send a notification email when an entry's asset field, Custom Image, has been updated on entry save. We're able to do this consistently for text fields but I'm having trouble doing the same for an asset field.
// Use statements etc

...

// Register the event handler
Event::on(Entry::class, Entry::EVENT_AFTER_SAVE, [$this, 'handleAfterSave']);

...

public function handleAfterSave(ModelEvent $event): void
{
    $entry = $event->sender;
    $revisions = Entry::find()->revisionOf($entry)->status(null)->orderBy('revisionId DESC')->all();
    $lastRevision = Craft::$app->getElements()->getElementById($revisions[1]->id);

    $fields = Craft::$app->getFields();

    // text fields
    $notificationHandles = [
        'textField1',
        'textField2',
    ];
    $updatedFields = array_map(
        fn ($h) => [
            'fieldName' => $fields->getFieldByHandle($h)->name,
            'oldValue' => $lastRevision[$h],
            'newValue' => $entry[$h],
        ],
        // Only need to show field if different from last revision
        array_filter($notificationHandles, fn ($h) => $lastRevision[$h] !== $entry[$h])
    );

    if (count($updatedFields) > 0) {
        self::$notifications->notifyUpdated($entry, $updatedFields);
    }
}

The above works for text fields. However, when adapting for an asset field, it appears that $lastRevision[$h] is not immediately updated.
If we have an entry with an empty Custom Image asset field:

Assign an image to Custom Image
Save
Email is sent
Return to the entry
Remove the asset from Custom Image
Save
No email is sent

To put it another way, if I have an entry with an empty Custom Image field:

Assign an image to Custom Image
Save
Email is sent
Return to the entry
Remove the asset from Custom Image
Save, but dump $lastRevision using EVENT_AFTER_SAVE
We can see that customImage is still null, where I would expect it to show the previously attached image (an AssetQuery etc)
No email is sent because both $lastRevision['customImage'] and $entry['customImage'] are null.

Thanks!

Have you tried the $element->isFieldDirty() method, or checking through the $element->dirtyFields array for your asset field, rather than checking revision dates?
There may be a good reason why you're not using those, but I thought I'd suggest it in case you hadn't considered it.


Assets, Multi Locale and 'Target Locale' I've created all my Assets fields with 'Same as Source' selected in 'Target Locale'. When I create an entry in language A, all field content is copied over apart from the assets selections into language B/C/D.

If I change this and select a set Locale in 'Target Locale' what actually happens? (e.g. If I select language A, will it copy that asset into lang B/C/D's entry and can it be changed/updated by an admin who only has access to B?)

P.S. In this situation, all assets are remote on S3.

Thanks!

The "Target Locale" setting under the Asset field's settings is referring to which locale do you want to select assets in.  When it's set to "Same as source", then, for example, if you're viewing an entry in German, the Asset selector modal will only display Assets assigned to the German locale.  And if you were viewing the entry in Dutch, it would only show Assets assigned to the Dutch locale.

If you change the "Target Locale" from "Same as Source" to a specific local (i.e. German), then no matter what local you're viewing an entry in, only Assets assigned to the German locale would show.

When saving a new entry in locale 1, Craft "copies" content data (anything stored in the craft_content table in the database) over to the other locales for fields marked as translatable as a starting point for the other locales.  All relational fields (Assets, Entries, Users, etc.) aren't "content" and they are stored in the craft_relations table and Craft currently doesn't copy that information over.

Ironically, if you uncheck the "This field is translatable?" checkbox in the Asset field settings, it would show up on the other locales, because in that context the Assets aren't stored on a per-locale basis.


New Project: Wait for Craft 3 or start now with Craft 2? I am just about to start a new project and Craft seems the best fit as a CMS. The projects timescale is 3 months.

I understand that a beta for Craft 3 is due out at the end of this year (2016). In view of this, should i start development on Craft 3? Or run with Craft 2 for now?

The project will involved bespoke plugin development and use of the Commerce plugin. Is there any timeframe in place for updating the Commerce plugin to run on v3?

My recommendation would be to go with Craft 2:

  A bird in the hand is worth two in the bush

There is no doubt that Pixel & Tonic has turned their focus to Craft 3, but software projects are complex and often take longer than expected and Craft 3 currently only has speculative release dates. It has also had speculative release dates in the past.

If you've made the decision to go with Craft it's likely your additional costs based on the Craft 3 transition will either be to deal with uncertainty and moving targets while developing on Craft 3 for the next several months or to go with the stability of Craft 2 and deal with some extra costs of upgrading to Craft 3 when the transition is well documented and Craft 3 is stable.


How can I get/check the contents of a cart via javascript (cookie)? Within my application, I would like to only show a link to the the user's cart only if there are products in it.

I was going to use javascript to check the cookie I can see getting set. commerce_cookie

The problem I'm having is how to read that cookie. I realize its encrypted & I'm not accessing the cookie correctly. That's why I'm unable to get and check for any orders. Is there a better way to check the cart dynamically in this way?

I am trying to read it like this:

var cart = getCookie('commerce_cookie');

console.log(cart);  // false

// check if there are any products in the cart
if (cart) {
   // show the cart icon
} else {
    // hide the cart icon
}

function getCookie(cookie) {
    if ($.cookie(name)) {
        returnJSON.parse($.cookie(name));
    }
}

I have read through this thread about LJ Cookies, but I'm still not confident I'm looking for the cookie correctly:

{{ craft.lj_cookies.get( 'commerce_cookie' ) }}

SOLUTION

What I was tying to do was hide the cart icon and only show it (along with item quantity) only if there were items in the cart.

To do this, you'll need a plugin to listen for the onBeforeAddToCart event.

MyPlugin.php

// Listen for items getting added to the cart.
craft()->on('commerce_cart.onBeforeAddToCart', function (Event $event) {
    $cart = craft()->commerce_cart->getCart();
    // set expire & other params as needed
    $cookie = new HttpCookie('totalQty', $cart->totalQty);
    craft()->request->getCookies()->add($cookie->name, $cookie);
});

Then I've got an ajax request that will check if that cookie exists.

jquery

var data = {
    'CSRF': $('input[name="CSRF"]').val()
};

$.ajax({
    'type': 'post',
    'contentType': 'application/x-www-form-urlencoded; charset=UTF-8',
    'cache': false,
    'data': data,
    'url': '/actions/myplugin/checkCookie',
    'dataType': 'json',
    'timeout': 50000
}).done(function (response) {
    // handle response
}).fail(function (error) {
   // Total fail.
});

That routes to a controller method within my plugin:

MyPlugin.php

if (craft()->request->isAjaxRequest()) {
    return $this->returnJson(craft()->request->getCookie('totalQty'));
}

Here is how I am then handling/updating the UI with the results of that ajax request. I am summarizing my code here, there is more logic than I am listing - just trying to keep the size of this thread below the level of "book".

I am using velocity for my transitions.

jquery

 ...
 var qty = (result.value + 1);  // value is zero-based array so +1

  $('#icon').velocity('fadeIn', {
      'duration': 300,
      'display': 'inline-block',
      'complete': function () {
          $('<div/>', {
              'id': 'cartQty',
              'text': qty
          }).appendTo($('#icon'))
              .velocity('transition.bounceIn', {
                  'duration': 300,
                  'easing': 'easeInOutSine'
              });
          }
      });

I wasn't able to find an event that listened for the Empty Cart event, so I had to get creative here.

I listen for the "Empty Cart" form submit via jQuery. From there I make another ajax request to my plugin:

jQuery

$('#emptyCartForm').submit(function (event) {
     deleteCookie(); // calls an ajax method (below)
});

$.ajax({
    'type': 'post',
    'contentType': 'application/x-www-form-urlencoded; charset=UTF-8',
    'cache': false,
    'data': data,
    'url': '/actions/myplugin/deleteCookie',
    'dataType': 'json',
    'timeout': 50000
}).done(function (response) {
    // handle response
}).fail(function (error) {
   // Total fail.
});

That request makes a call to a controller method within my plugin:

MyPlugin.php

 if (craft()->request->isAjaxRequest()) {
     craft()->request->deleteCookie('totalQty');
     return $this->returnJson(['status' => 'success']);
}

I'm happy to edit or please feel free to edit if there is any way I can improve this technique.

The cookie that Craft Commerce uses for cart/session management is set using Craft's craft()->userSession->saveCookie, which hard-codes cookie's httpOnly flag, meaning it can't be accessed by JavaScript.

If you want to pull of what you're trying to do, you'll need to use something like the LJ Cookies or Cookies plugin to add and ready your own cookie according to your business logic.


Random Number from range What is the best way to get a random number within a certain range?

I made something but I guess there is a better way:

In this example I want to position some elements randomly. This approach works, but I have to define a variable for each value.

{% set randomizr = range(10, 90) %}

{% set randomHeight1 = shuffle(randomizr) | first %}
{% set randomWidth1 = shuffle(randomizr) | first %}

{% set randomHeight2 = shuffle(randomizr) | first %}
{% set randomWidth2 = shuffle(randomizr) | first %}

{% set randomHeight3 = shuffle(randomizr) | first %}
{% set randomWidth3 = shuffle(randomizr) | first %}

{% set randomHeight4 = shuffle(randomizr) | first %}
{% set randomWidth4 = shuffle(randomizr) | first %}

{% set randomHeight5 = shuffle(randomizr) | first %}
{% set randomWidth5 = shuffle(randomizr) | first %}

Is this somehow possible without writing each variable manually?

Building off of Brad's answer, you might consider using a for loop to render your markup, instead of setting all of those variables:

{% set range = range(10, 90) %}
{% set totalDivs = 5 %}

  {% for i in 1..totalDivs %}

  {% endfor %}


Create custom field to input canonical url in a page What is the best way of creating a custom field for each page whereby the user can input a URL into a field, which then gets input into a rel=canonical element like the example below.

<link rel="canonical" href="{{URL_FROM_CUSTOM_FIELD}}">

I already have an SEO tab like in the image below, however I am unsure of how to create a custom field inside this tab.

If someone could point me in the right direction, that would be great, as I am very new to the CMS.

If you truly want to this via a custom field:

Admin > Settings > Fields is where you create custom fields.
Settings > Sections > Edit Entry Type is where you assign custom fields to your section. You drag and drop the field under "Design Your Field Layout".
You can then output the custom field in your template using {{ entry.customFieldHandle }} for example.

A more common approach would be to code this tag in your templates instead of using a custom field. I can't give you a very specific approach to try with the code since I do not know your site structure and desired result.


Many sections use one template: How do I get current section? I have multiple sections (channels) that use the same index and _entry templates. In my index template, given that the section is dynamic, how do I define the current section and access it? Eg, I'd like to make the template's header dynamic: <h1>{{ section.name ~ " Blog" }}</h1>.

Here is my fragile, hacked solution that I hope I never have to use in production:

I start things off by grabbing a section of the current URL. This returns a string, eg: "southwest-mn", that I use to load the actual section.

{% set handle = craft.request.getSegment(2) %}

Next, I convert the string to one that reflects the section's handle format, eg: blog_southwest_mn (I changed it from blogSouthwestMn to make this string wizardry faster and easier)

{% set sectionHandle = handle|replace('-', '_') %}

Now we can patch together a dynamic section name based on our URL, so let's grab the entries

{% set blog = craft.entries.section('blog_' ~ sectionHandle) %}

This is the most embarrassing part of the code... I'm not sure how to get to the actual section model, so I used the following to display the correct header in the template

{% if handle == "southwest-mn" %}
    {% set blogTitle = "Southwest MN Blog" %}
{% endif %}

That's how I was able to at least get a prototype template working. But how can I load a dynamic section's model so I don't have to write such madness?

Thanks!

If I understand you right, you could just use the Entry Model which is automatically populated as entry if you call the Index. Entry Model objects have a method getSection() which returns the Section Model to which the Entry Model object belongs.

https://craftcms.com/docs/templating/entrymodel#getSection

Edit:
Okay I think I didn't understand you right. I think you get the error because no section is related to your index template and instead you call it directly. You actually don't want that, so rename your index file to _index in order to prevent direct call. If you call it directly, no entry object is populated and voilà: error.

You can probably achieve what you want by utilizing the way Craft's example website Happy Lager does it:

You have your channel sections with their handles a, b, c.... Entry Template of all is let's say whatever/_entry.
For every channel section you create a section of type single, the handles like aIndex, bIndex, cIndex.... All of these single sections point to whatever/_index as Entry Template but have different URIs, e.g. a for section aIndex.

When you hit craft.dev/a now, you get your index template whatever/_index populated with entry and entry.getSection() returns a section model of aIndex. Now you can get all entries of channel section a by using

{% set channel = entry.getSection().handle | replace("Index", "") %}
{% set entries = craft.entries.section(channel) %}

It is still kind of hacky because you have to name the section handles appropriately.


Best method to update Craft on a live site to avoid losing data What is the best way to update Craft on a live website? Preferably without losing any data?

There are a few ways to do this, and, can often be situational.
Essentially the main problem is potentially losing out on data that’s POSTed during the site being updated.

If a site is small: I often crawl the site before hand, making sure everything is cached with varnish, do a DB backup, then do the update locally (sync’d with the live DB), and then push & deploy the files. (Varnish holds the site up despite it being unusable behind the scenes). Even if the DB is a local, there is still downtime involved in updating it.

If a site is bigger, or the Craft versions differ dramatically: Though more time consuming, I would setup another directory on the same server (with an independent updated database), make sure it’s working on a test link, then switch the nginx config to point to the new directory for that site.

In any situation I think that all situations could be detrimental to a user. If a user has written out a long form which could potentially be submitted during an update, or, a client could be logged in to Craft, working on a big edit to a page and hit “save" (I know you can disable their access), but the same issue exists, when you use that feature too.

I think you're asking the IT equivalent of "If I go outside when it's storming, how can I not be hit by lightning?" There's always going to be that chance. When you run a public server, that's pretty similar.
I'm not a high availability expert but as someone's who ran servers on behalf for clients, what you're really asking is 2 questions:

How much downtime can we afford?
How do we engineer a solution to minimize that downtime?

As you alluded to, how you answer question 1) is going to inform 2). Properly answering question 2) is probably beyond the scope of this Q&A format, but since we're going the down the rabbit hole...
If your answer to question 1 is next to zero, then you need to evaluate how much time/effort/money will required to do this.  Does it matter if your mom and pop's brochure based site is down for half a minute while you update? Probably not.
If you're running a multiple server setup for a largish organization, then this is also a question for them and what they're willing to throw at IT to make that happen. No server is 100% available, 24/7 and responsive to users.  The companies who do this well are also compensated accordingly. When you add redundancy to the mix, you also double your potential for failure so it's a double edged sword.
You can minimize that chance of messing up a user's work by doing a few things to proactively protect against downtime and just as importantly, frame the user's expectations:
Communication
When do your users usually work on the site? If the site's users and authors are typically 9-5 corporate in nature, update the site off-hours when less people are going to be working or visiting on the site. And if you think downtime is going to more than a few minutes, inform them of the maintenance window.
At least in terms of Craft, updating takes seconds usually. I liken updates to a frog jumping from lily pad to lily pad. The lily pads are point updates.  The smaller jumps are going to be a lot easier to make then jumping from one side of the pond to the other.
You're also spreading out your uptime with multiple smaller jumps instead of huge leaps. If you put off updating, you're increasing the risk of something blowing up later on (schema updates, etc).
Updating software is not like updating hardware where you know the hardware is going to fail at some point, so you plan for it. Writing bug-free software is harder. Being agile here is important. You also run less of a risk of having unplanned downtime because you didn't jump and now there's a huge security update.
Engineering
The other important piece of uptime is if something does go wrong during the update, how do you recover? When Craft updates, unless you've turned off automatic database backup, it'll save a copy in case things go hairy and then revert back so you're not stuck in a weird state.
I would also question the need for a long form to begin with. Can we engineer the form in such a way that it saves the user's work periodically (either to their browser or the server) as they fill it out?  Or make each form part shorter?
If someone is working on a long form, it's  counter intuitively also less likely they're going to be hit by server downtime, because they're spending more time on it before it gets submitted.
Lindsey D points out that how you deploy can make a difference as well. Deployment services (eg: DeployBot) in coordination with some VPS providers offer atomic deployment. This guarantees that your site remains "untouched" until the new files are 100% uploaded. Using something like Capistrano can do this as well. AWS offers their own set of deploy and server management tools to help manage your uptime too. (Again, you pay the piper for all of this, which is why AWS gives you a year for free in their free tier.)
There's probably a few things you can do but those are just off the top of my head.


RelatedTo Query Using Both "and" and "or" I'm attempting to build a filter that queries entries of a section based on two criteria: A selection of one or more categories (inclusive), and a single related entry (exclusive).

Specifically, the entries are from the "technologies" section. Each technology is related to one or more categories and a single "institution" entry.

The first step is to allow a user to, for example, select two categories (like "Information Technology" and "Medical Devices") and have the results be inclusive. Easily done with a relatedTo "or" parameter.

But next, I'd like the user to be able to select a single institution, and return only the previous results that are also associated with that institution (like "UW-Oshkosh"). This filter requires an additional relatedTo "and" parameter.

My attempt to add two different relatedTo parameters to the same query (detailed below) doesn't fly -- it will only honor the last relatedTo request:

{% set params = {
    section: 'technologies',
    limit: null
} %}

{% if technologyIds|length %}
    {% set paramTechnologyRelation = ['or'] %}
    {% for technologyId in technologyIds %}
        {% set paramTechnologyRelation = paramTechnologyRelation|merge([{ targetElement:technologyId }]) %}
    {% endfor %}
    {% set params = params|merge({relatedTo: paramTechnologyRelation}) %}
{% endif %}

{% if institutionId != "" %}
    {% set paramInstitutionRelation = ['and'] %}
    {% set paramInstitutionRelation = paramInstitutionRelation|merge([{ targetElement:institutionId }]) %}
    {% set params = params|merge({relatedTo: paramInstitutionRelation}) %}
{% endif %}

{% set technologyResults = craft.entries(params) %}

{% paginate technologyResults.limit(10) as technologies %}
    ...
{% endpaginate %}

I'm trying to think of other approaches -- Is is possible to run a second query on an array of entries? Any ideas on how to best approach this? Thanks for any insights.

Crisis averted, thanks largely in part to the versitile nature of the relatedTo targetElement processing. The relevation came that I could feed an array of category ids (inclusive) into that element, and use a single relatedTo "and" parameter overall to get my intended filtering:

{% set params = {
    section: 'technologies',
    limit: null
} %}

{% set paramRelation = ['and'] %}
{% if technologyIds|length %}
    {% set paramRelation = paramRelation|merge([{ targetElement:technologyIds }]) %}
{% endif %}
{% if institutionId != "" %}
    {% set paramRelation = paramRelation|merge([{ targetElement:institutionId }]) %}
{% endif %}
{% if paramRelation|length > 1 %}
    {% set params = params|merge({relatedTo: paramRelation}) %}
{% endif %}

{% set technologyResults = craft.entries(params) %}

{% paginate technologyResults.limit(10) as technologies %}
    ...
{% endpaginate %}

It's actually more simple this way as well, but I had initially underestimated how much heavy lifting the targetElement piece could do. Once again, Craft exceeds expectations. Thanks!


How to determine if a user's email has changed I'm trying to determine if a user has changed their email address via a front-end form. From what I've gleaned, the best way to do this would be by checking the existing record during users.beforeSaveUser, store the old email, then compare to the new email during users.onSaveUser.

However, when I get a user's record during users.beforeSaveUser, the newly-submitted email address has already overwritten their record.

craft()->on('users.beforeSaveUser', function(Event $event)
{
    if(!$event->params['isNewUser'])
    {
        $currentUser = craft()->users->getUserById($user->id);
        exit($currentUser->email);
    }
}

This code shows the new email, not the old one.

Is this expected behaviour? Any other way to accomplish what I need to?

Only thing I can think of off the top of my head would be to create a new, separate database table that stores user Ids against email addresses. Perhaps also with a timestamp of when the row was written.

You can then write the user's email address to that upon registration and use it to check against the "official" email address when you need to.

If you find a change, do whatever it is you need to do and update your table with the new email address.


Contact Form plugin - Error loading 404 Pages So I have a contact form in my layout (just using the contact form plugin from the Craft guys) and on 404 pages I'm getting the following:

  Impossible to invoke a method ("getErrors") on a string variable ("") in "_layout"

What's the best way around that and to still get the contact form to show up on an 404 page?

Many thanks in advance!

Template code is in the footer of _layout.html and looks like this:

    {% set asset = globalContact.contactBackground.first() %}

                {{ globalContact.contactPreHeader }}

                {{ globalContact.contactHeader }}

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

                        {% for error in errors %}

{{ error }}

                        {% endfor %}

                {% endif %}
            {% endmacro %}

            {% from _self import errorList %}
            <form method="post" action="" class="contact-form" accept-charset="UTF-8">
                {{ getCsrfInput() }}
                <input type="hidden" name="action" value="contactForm/sendMessage">
                <input type="hidden" name="redirect" value="thanks">

                    <input type="text" id="fromName" name="fromName" placeholder="Name" />
                    {% if message is defined %}{{ errorList(message.getErrors('fromName')) }}{% endif %}

                    <input type="text" id="fromEmail" name="fromEmail" placeholder="Email" />
                    {% if message is defined %}{{ errorList(message.getErrors('fromEmail')) }}{% endif %}

                    <input type="text" id="phone" name="message[Phone]" placeholder="Phone" />

                    <textarea rows="8" cols="40" id="message" name="message[body]" placeholder="Message">{% if message is defined %}{{ message.message }}{% endif %}</textarea>
                    {% if message is defined %}{{ errorList(message.getErrors('message')) }}{% endif %}

                <input id="shewbotsshew" name="shewbotsshew" type="text">

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

            </form>

Anywhere you've currently got something that looks like this:

{% if message is defined %}{{ errorList(message.getErrors('fromEmail')) }}{% endif %}

Try changing that to:

{{ message is defined and message ? errorList(message.getErrors('fromEmail')) }}

And you'll want to do that for the other attributes, too ('fromName', etc.).


Failed upgrade to 3.0.0 Just tried to run the auto-update to v3.0.0 and it failed catastrophically with the message:

  Error: file_put_contents(/var/www/html/craftcms/composer.lock): failed to open stream: Permission denied

Now my control panel page has a "Finish Up" button that doesn't seem to do anything (the styling on the page is completely broken).

It also looks like Craft didn't take a backup, as my most recent backup is from RC4, back in January.

I'm not sure how to recover from this, if it's even possible, as the docs don't seem to have any useful information.

Any help would be appreciated.

UPDATE

Following Brad's instructions below (thanks Brad!), I found that my composer.json file had the version set to 3.0.0-RC5 (I guess the RC4 backup is the most recent after all--I thought I was on a later version).

I tried running composer update but now I'm getting the following error:

Problem 1

- Installation request for craftcms/cms 3.0.0-RC5 -> satisfiable by craftcms/cms[3.0.0-RC5].
- Conclusion: remove roave/security-advisories dev-master
- craftcms/cms 3.0.0-RC5 requires yiisoft/yii2 ~2.0.13.0 -> satisfiable by yiisoft/yii2[2.0.13, 2.0.13.1, 2.0.13.2, 2.0.13.3].
- yiisoft/yii2 2.0.13 conflicts with roave/security-advisories[dev-master].
- yiisoft/yii2 2.0.13.1 conflicts with roave/security-advisories[dev-master].
- yiisoft/yii2 2.0.13.2 conflicts with roave/security-advisories[dev-master].
- yiisoft/yii2 2.0.13.3 conflicts with roave/security-advisories[dev-master].
- Installation request for roave/security-advisories dev-master -> satisfiable by roave/security-advisories[dev-master].

I also tried setting my version to RC4 in composer.json but got the same result.

Should I just remove roave/security-advisories from composer.json and try again?

Please excuse my ignorance--I have approximately zero experience with Composer.

Error: file_put_contents(/var/www/html/craftcms/composer.lock): failed to open stream: Permission denied

Whatever user:group PHP is running as on your server doesn't have the permissions to write to /car/www/html/craftcms

  It also looks like Craft didn't take a backup, as my most recent backup is from RC4, back in January.

Craft will only do a backup during updating if mysqldump or pg_dump is available to PHP (by default), or if your custom backupCommand config setting executes successfully.

If Craft can't make a backup during updating, it will halt the update process and prompt you asking what you'd like to do with one of the options being to continue without making a backup.

  I'm not sure how to recover from this,

To restore the Craft files to the previous version, you can open your project's composer.json file, find the craftcms/cms line, set the version to the pre-update attempt version, then run composer update from the command line.

If there were no database migrations that ran during the update attempt, that should be all you need to do.

If there were database migrations that ran, you'll probably get an message when you try to access the Control Panel saying that you can't backtrack to that version of Craft because now the database is out of sync with the files.

The only practical way around that is to restore from a pre-update attempt database backup.


Exclude multiple entries by their IDs I have a structure people that I need to grab entries from. However, I need to exclude certain entries as some are already displayed on the page in a different manner.

I know that you can exclude a single entry by doing something like this

{% for councilMember in craft.entries.section('people').id('not ' ~ entry.id)

But how do you exclude multiple entries by their IDs?

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

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

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

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

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

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

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

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

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

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

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

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

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


Nginx FastCGI caching NO PROXY We are implementing fast CGI cache (not not the proxy version) but it seems that our upstream cache hit is always MISS. If we hijack public/index.php by not running require_once($craftPath."/vendor/autoload.php"); and simply echo time the cache gets created and hit. We noticed that Craft adds cache control, pragma and expires headers by default. We tell FastCGI to ignore the headers with no luck.

Does Craft do anything to not allow FastCGI caching?

Thanks!

location ~ \.php$ {
    fastcgi_ignore_headers X-Accel-Expires Expires Cache-Control;
    fastcgi_cache cacher;
    fastcgi_cache_valid 200 60m;
    fastcgi_cache_methods GET HEAD;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}

Found this while searching for a solution. I fixed it by moving this line to the top of the vhost file:

    fastcgi_ignore_headers X-Accel-Expires Expires Cache-Control;

After this all my requests are properly cached and my site loads really fast! My entire vhost file:

fastcgi_cache_path /etc/nginx-cache levels=1:2 keys_zone=phpcache:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_ignore_headers X-Accel-Expires Expires Cache-Control;

server {
        listen   80;
        charset utf8;
        root /home/user/htdocs/public;
        index index.php;
        server_name domain.com;

        set $no_cache 0;

        # Don't cache the CMS admin area
        location /admin {
                set $no_cache 1;
        }

        gzip            on;
        gzip_min_length 10;
        gzip_proxied    expired no-cache no-store private auth;
        gzip_types      text/plain application/xml application/x-javascript text/css image/svg+xml;
        gzip_vary on;

        location ~ \.php$ {
                include fastcgi_params;
                fastcgi_pass unix:/var/run/domain-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

                fastcgi_cache phpcache; # The name of the cache key-zone to use
                fastcgi_cache_valid 200 30m; # What to cache: 'code 200' responses, for half an hour
                fastcgi_cache_methods GET HEAD; # What to cache: only GET and HEAD requests (ot POST)
                add_header X-Fastcgi-Cache $upstream_cache_status; # Allow us to see if the cache was HIT, MISS, or BYPASSED inside a browser's Inspector panel
                fastcgi_cache_bypass $no_cache; # Dont pull from the cache if true
                fastcgi_no_cache $no_cache; # Dont save to the cache if true
        }

         location ~ ^(.*)$ {
                 try_files $uri $uri/ /index.php?p=$uri&$args;
        }

        location ~* \.(js|css|png|jpg|jpeg|gif|ico|woff|ttf|svg)(\?ver=[0-9.]+)?$ { expires 1w; }

        location ~ /\.ht {
                deny all;
                return 404;
        }
}


Random quotes in matrix block type I have a Matrix block with 2 types: quotes and text. The first block contains a quote and a text. The second and following ones only contain quotes.

I want the type text to be limited to 1 and the type quotes limited to one at a time displayed randomly.
Can't figure out how?

{% for block in entry.intro %}

    {% if block.type == "quote" %}

{{ block.quote|raw }}

                {{ block.caption }}

    {% elseif block.type =="text" %}

{{ block.column1 }}

{{ block.column2 }}

    {% endif %}

{% endfor %}

You could query for your two blocks individually.

{% set quoteBlock = entry.intro.type('quote').first() %}
{% set textBlock = entry.intro.type('text').order('RAND()').first() %}

{{ quoteBlock.quote|raw }}

    {{ quoteBlock.caption }}

{{ textBlock.column1 }}

{{ textBlock.column2 }}


How to get submitted data in Sprout Forms custom email I've setup a custom email notification that is sent out after submitting a form. I want to have a set message but with a personalised greeting "Hi Bob". I have the email.html file and that works but I can't figure out how to display the persons name. In my example the field handle it yourName and the form handle is contactForm.

Is this possible?

Following Sprout Forms example templates (/admin/sproutemail/settings/examples): in your email.html file Hi {{ object['yourName'] }} or even Hi {{ object.yourName }}, should print Hi Bob.


How do you format a date for saving with users/saveUser on the front end? Currently using a date picker to select a date and submit it on user registration, formatted as m/d/Y, have played with various formats but can't one that works.

Any ideas?

Craft stores dates in the database in the following format:

Y-m-d H:i:s

So submitting the date in this format should work, for example:

2014-06-24 23:30:00

It should also be possible to split the date and time into individual fields as follows:

<input type="text" name="fields[customFieldName][date]" value="2014-06-24" />
<input type="text" name="fields[customFieldName][time]" value="23:30:00" />


Response: {"error":"Undefined index: fieldGroup"} I've recently had to handle a massive merge on a Craft project which resulted in many conflicts in the project.yaml file. I tried to clean up the merge conflicts as best as I can.

And now I have this error:

Question now is there a way to recover or re-generate the project.yaml?

Also, this my first experience working with Craft in a team. Any suggestions on best approach to keep project.yaml clean?

You can rebuild your project.yaml by running ./craft project-config/rebuild.

In terms of workflow here's a good article that discusses it.


Set Parent Template Defined Content to Output in Child Template I have a layout template, we'll call that Template A that is extended by another template, we'll call that Template B that includes another template, we'll call that Template C that includes another template lets call that Template D

So the visually it goes like so:

-Template A

--Template B

---Template C

----Template D

In Template C I define some content such as "Hello World" as well as a bunch of other content.

I do not want Template C to output the "Hello World" content but instead want Template D to decide where it is output as defined in Template C

I'm thinking that somewhere i might should use embed rather than include but i'm a bit hazy on exactly how to achieve this. Any help would be greatly appreciated.

NOTE: The content defined in Template C is defined after Template C includes Template D and also the content that is defined is not just a simple string as indicated above "hello world" but actually much more complex data.

UPDATE: Here is an example of the templates to help better understand the question.

Template C:

{% for block in entry.body.level(1) %}
  {% include 'Template D' %}
  {% if block.children is not empty %}
    {% block child %}
        {% for block in block.children %}
            {% include 'Template E' %}
        {% endfor %}
    {% endblock %}
  {% endif %}
{% endfor %}

Where Template E is the "Hello World" content mentioned above.

Template D:

 {% if block.slide|length %}

             {% for background in block.slide %}

             {% endfor %}

             {% block child %}{% endblock %}

 {% endif %}

And more code also after that endif. Where Block Child (in Template C) is the content to be defined and Block Child (in Template D) is where i want it output.

You could use include...with, as specified in the Twig Docs here

Example for your case:

{# in Template C #}
{% include 'template-d.twig' with {'foo':'Hello World'} %}

This should give Template D access to foo. Hope that helps!


Getting the first category of the deepest level for an entry I want to get the first category of the deepest level for an entry.

For example if the category structure looks like this

Ocean Life > Fish > Gold Fish

I want "Gold Fish"

Here is what I've come up with so far. Is there a more elegant solution that I'm missing?

            {% set primaryCategory = entry.categories.level(3).first %}
            {% if primaryCategory is empty %}
                {% set primaryCategory = entry.categories.level(2).first %}
            {% endif%}
            {% if primaryCategory is empty %}
                {% set primaryCategory = entry.categories.level(1).first %}
            {% endif%}

Assuming you are only assigning one category (and thus by default all of that categories ancestors), then getting the 'last' returned category should work.

{% set category = entry.categories.last() %}

Otherwise, you will need some logic of some kind. What you have works, or perhaps something where you don't need to know how many levels there are.

{% set level = 0 %}
{% for category in entry.categories %}
    {% set level = category.level > level ? category.level : level %}
{% endfor %}
{% set category = entry.categories.level(level).first %}

However, this will still not retrieve all the categories if there is more than one category set with same level.


Entries lightswitch field within matrix Is it possible to control if some content shows that's within a Matrix field if a lightswitch field on the entry is false.

I expect not as you're inside the matrix loop.

{% for block in entry.section3 %}
 ...
{% if block.applyButton %}
 ...
   {% if entry.applicationsClosed('not 1') %}
    {% for asset in entry.applyButtonDocument %}
     apply
    {% endfor %}
   {% endif %}
...
{% endif %}
....
{% endfor %}

I don't want to put the lightswitch within the Matrix as that would put it in an illogical place and would be needed to be switched multiple times.

It's being used as more of a global setting across the page to change various bits of content.

You can definitely access and use fields on the entry you're viewing within a matrix field loop. The only thing you need to watch out for is repeating the entry variable in your for loop.

You can simplify your if statement slightly by using {% if not entry.applicationsClosed %}


project.yaml synchronize error (not usable anymore) I had a table field and created a new super table field to try something.
After that I removed my table field and gave my super table field the same handle like the old table field had.

I did it locally and synced it via git.
After that I wanted to sync my project.yaml.
I tried ./craft project-config/sync and I got a error: error: Getting unknown property: craft\fields\Table::contentTable

I tried to remove the complete section on my production server but still no changes. At the moment I have no Idea what I could do…

Of course ./craft project-config/rebuild works but then I have my old project.yaml

Thanks for any help!

solved it with a rebuilded project file locally.


How to fetch only selected child category? I have some parent and their child categories which has more than one child.

In an entry I have a category field where I selected a single child category (Bourbon) which is visible like so:

Now I want to show the title and URL of the only selected single child category which is Bourbon.

I'm not really sure why craft is working that way and I'm not really sure if my way is the intended to solve it but it works at least for me. Make sure you select the one that fits best for you. Usually the best is the third one.

First solution
If you have a limitation in your field how many categories you can link the .last() function would suit since your child is always the last element and your parent is the first one.

{% set category = entry.fieldHandle.last() %}

But this does not work if you are going to relate multiple Cateogies

Second Solution
If your selected categories are always at level 2 and never at level 1 you can do the following. This works for multiple categories as well

{% set categories = entry.fieldHandle.level(2).all() %}
{% for category in categories %}
    {{ category.id }}
    {{ category.title }}<br>
{% endfor %}

Third solution
Change your matrix structure into two different channels (countries and continent) and relate your continent with your countries. In your relation field you restrict the selection to those two groups.
That way a user can select the country and/or a continent individually.


Getting all content, including Matrix fields from an EntryModel Initially I was doing $entry->getContent()->getAttributes(), but noticed my Matrix fields were all null.

What is the right way to do this?

I see that $entry->myMatrixField gives me the ElementCriteriaModel for my Matrix that I need, I thought that would be the same as using `getAttribute('myMatrixField'), but i guess not.

ContentModel will only have the values in the actual content table, and the values that are actually there still may not be what you’re after, because accessing those values directly won’t give the field types a chance to do any preprocessing on their data.

So the best way to reliably get an array of all of an element’s custom field content is to go through its Field Layout, via BaseElementModel::getFieldLayout():

$values = array();

foreach ($element->getFieldLayout()->getFields() as $fieldLayoutField)
{
    // $fieldLayoutField is a FieldLayoutFieldModel, *not* a FieldModel
    // http://buildwithcraft.com/classreference/models/FieldLayoutFieldModel
    // so get the actual FieldModel here
    $field = $fieldLayoutField->getField();

    // Now get the prepped field value
    $value = $element->getFieldValue($field->handle);

    $values[$field->handle] = $value;
}


Setting up load balancing for Craft on Laravel Forge I'm attempting to set up a load balancing set up on laravel forge for a craft site.
I have one server that is provisioned as a load balancer, two app servers both set up to server the same craft install (deployed from the same Git Repo) and one db server.

The two app servers are configured to use the db server in the env file. I have tested hitting both app servers by adjusting my host and can confirm that each loads the Craft site as expected. I've put up a firewall to only accept traffic on port 80 from the private IP of the load balancer.

My load balancing server is configured to network with the app servers. The load balancer is set up with SSL (let's encrypt).

The issue I'm seeing seems to be in the SSL redirect Laravel Forge sets up. After configuring SSL I get a bad gateway error when I load the site.
Here is my nginx config for the load balancer

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/mydomain.com/before/*;

# FORGE CONFIG (DO NOT REMOVE!)
include upstreams/mydomain.com;

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name mydomain.com;

    # FORGE SSL (DO NOT REMOVE!)
    ssl_certificate /etc/nginx/ssl/mydomain.com/774031/server.crt;
    ssl_certificate_key /etc/nginx/ssl/mydomain.com/774031/server.key;

    ssl_protocols TLSv1.2;

    charset utf-8;

    access_log off;
    error_log  /var/log/nginx/backtowork-ri.com-error.log error;

    # FORGE CONFIG (DO NOT REMOVE!)
    include forge-conf/backtowork-ri.com/server/*;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass https://1013240_app/;
        proxy_redirect off;

        # Handle Web Socket Connections
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

    }
}

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/mydomain.com/after/*;

if I adjust the config to listen on port 80 and 443 like so

server {
    listen 80;
    listen [::]:443 ssl;
    ...

I see a too many redirects error.

I'm really not sure what I'm doing wrong. Any one have any thoughts?

I figured out a solution, though I'm not totally clear why it works.

Adjusting the server statement below

server {
    listen 80;
    listen [::]:443 ssl;
    ...

to read

server {
    listen 80;
    listen 443 ssl;
    listen [::]:443 ssl;
    ...

Solved my problem and allowed my load balancer to direct to my application servers.


Craft\SproutForms_EntryModel and its behaviors do not have a method or closure named I'm working on a site that was partially built by another dev and am having some trouble with Sprout Forms.

It's partially working but when I add a new field (sailSeries) to the template (it exists in the sprout forms control panel) I get this error:

Craft\SproutForms_EntryModel and its behaviors do not have a method or closure named "sailSeries".

Expanding on what the original dev has I added the sails series inside this for loop at the bottom.

{% for tab in form.getFieldLayout().getTabs() %}
    {% set fields = tab.getFields() %}

    {% for field in fields %}

      {% set required = field.required %}
      {% set tabId = field.tabId %}

      {% set field = field.getField() %}

      {% if field.handle == 'contactType' %}
       {% set dropdownField = field %}
      {% endif %}

      various more fields

      {% if field.handle == 'sailSeries' %}
        {% set  sailSeries = field %}
      {% endif %}

    {% endfor %}
{% endfor %}

The the code later in the template to have the field display:

    {% set submittedDropdownValue = attribute(submittedEntry, sailSeries.handle) %}

      {{ sailSeries.name }}
        <select id="sailSeries" name="sailSeries">
        {% for option in sailSeries.settings.options %}
            {% set defaultValue = option.default ? option.value : null %}
            {% set selectedDropdownValue = submittedDropdownValue != '' ? submittedDropdownValue : defaultValue %}

            <option value="{{ option.value }}" {% if selectedDropdownValue == option.value %}selected="selected"{% endif %}>{{ option.label }}</option>
        {% endfor %}
        </select>

As soon as I add this in I get errors. Not sure why or how to troubleshoot. Please advise.

Revised code and now I'm getting this error:

  Variable "sailSeries" does not exist.

no clue why this is happening.

Make sure those "missing" fields aren't used in the title format (under the advanced tab) or in your notifications -  both in the control panel. Ran into something similar just now.


Users Order not Working I'm trying to order my users with craft.users.order('lastName'), which should be no problem, since I'm pulling that straight from the documentation; however, it's not working for some reason. Is this a current a bug in Craft?

Currently, I have users with these last names: Apple, Campbell, Jackson.

With order('lastName'), it's printing out: Campbell, Jackson, Apple. So no alphabetical order is being applied asc or desc.

Values in the documentation list possibilities, like: 'firstName' and 'lastName', which needs to be updated, since Craft only accepts lowercase values.

Changing 'firstName' to 'firstname' solves the issue.


Translated pages load a white empty page on localised site I'm running Craft CMS 2.6.2949 on PHP 7.0.10.

I have set up 3 locales: nl_be, en and fr.

I have set the following config into general.php

    '.dev' => array(
    // ...
    'siteUrl' => array(
        'nl_be' => 'http://xxx.dev/',
        'en' => 'http://xxx.dev/en/',
        'fr' => 'http://xxx.dev/fr/'
    ),

I have set the section to translateable and set some fields to be translateable.

I have translated some entries.

When I open the entry in the main language (nl_be) the website load with the desired template and styling. http://xxx.dev/nieuws/2016/we-installeerden-net-dit

When I open the translated version
http://xxx.dev/en/news/2016/we-just-installed-craft

I get a white, empty page. It seems like the template is not loaded.

Any ideas? Thanks a lot in advance.

This is the first time I'm trying Craft and I really love it. Great work Pixel & Tonic!

Bart

I found this:

I only copied the lines

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

// Tell Craft to serve the German content
define('CRAFT_LOCALE', 'de');

// Do not edit below this line
// ...

from the example here: https://craftcms.com/docs/localization-guide to the index.php file in the localised folder.

I did not copy the $path lines from the original index.php file in the webroot. This way I got the white empty pages.

Thanks a lot for the help on Twitter. I'm on my way to finish my first site!


Related entries in Matrix field I have three sections:

Discography
Performers
Works

Within "Discography", a Matrix field contains related entries from "Performers" and from "Works".

I am looking to display those related entries in Discography with the code below, but nothing happens (no error, no display):

{% for entry in craft.entries.section('Discography').order('title') %}

    {{ entry.title }}

    {% for block in entry.discoOeuvres %}

        {% set relatedWorks = craft.entries.section('Works').relatedTo(entry) %}
        {% set relatedPerformers = craft.entries.section('Performers').relatedTo(entry) %}

        {% for work in relatedWorks %}
            {{ work.title }}

            {% for performer in relatedPerformers %}
                {{ performer.title }}
            {% endfor %}
        {% endfor %}
    {% endfor %}
{% endfor %}

Also tried to follow this solution, with no success...

Where do I miss something?

Thanks for helping.

As Douglas mentioned in the comments, if the relation is defined within a Matrix block, then the Matrix block is the source of the relation; not the entry.

Soy you just need to change relatedWorks and relatedPerformers to:

{% set relatedWorks = craft.entries.section('Works').relatedTo(block) %}
{% set relatedPerformers = craft.entries.section('Performers').relatedTo(block) %}


Nearest entry using related entry Smart Map field I’m trying to build a query to get the geographically closest entries, based on an Smart Map field value within a related entry.

The following template tags were my first guess, but returned an empty result set.

{% set nearest = allUnitsCriteria.relatedPropertyField.smartMapField(params).order('distance') %}

What’s the best way to do this?

Being that the smartMapField attribute belongs to entries in the Property section, and not the entries in the Unit section, your current element criteria is a bit off (it basically assumes that the smartMapField property is found on the Unit entry models).

Basically, you'll need two element criterias to accomplish what you want.

The first criteria will pull Property entries based on their smartMapField value and the params object. The second one will pull Unit properties, by relation to the Property entries in the first criteria.

Finally, we'll need to use the group filter to output the unit entries in the correct order (i.e. those related to the nearest property entry first).

Important: It isn't 100% clear from your question, but I'm assuming that the relation between Units and Properties originates from a relatedPropertyField in the Units entries, rather than the other way around.

I haven't been able to test this, but I believe something like this should do the trick:

{% set nearest = craft.entries.section('units').relatedTo({
    targetElement : craft.entries.section('properties').smartMapField(params).order('distance').limit(null),
    field : 'relatedPropertyField'
}).limit(null).order('targets1.sortOrder')|group('relatedPropertyField.first().id') %}

Note the group filter applied to the nearest variable. This is needed to group the returned entries by their common Property relation. The group filter turns nearest into an array of grouped elements, which means that when you output the entries, you'll need a nested for loop:

{% for units in nearest %}
    {% for unit in units %}
        ...
    {% endfor %}
{% endfor %}


How can I get image locations in a list of entries using the Element API plugin? 'api/looks/list' => [
        'criteria' => ['section' => 'looks'],
        'transformer' => function(EntryModel $entry) {

                //print_r($entry->getAncestors());
                // $model = $entry->content->attributeConfigs["featuredImage"]["model"];
                // echo 'class type: ' . get_class($model);
                // print_r($model);

            return [
                'title' => $entry->title,
                'id' => $entry->id,
                'url' => $entry->url,
                'jsonUrl' => UrlHelper::getUrl("looks/{$entry->id}.json"),
                'kachelgroesse' => $entry->kachelgroesse,
                'category' => $entry->category,
                'image' => $entry->image
            ];
        },
    ],

This is my elementapi.php file and it returns:

{
  "data": [
    {
      "title": "SHINY AND RED LIPS",
      "id": "281",
      "url": "http://craft.dev:8888/looks/shiny-and-red-lips",
      "jsonUrl": "http://craft.dev:8888/looks/281.json",
      "kachelgroesse": {
        "0": {
          "label": "mittel",
          "value": "mittel",
          "selected": true
        }
      },
      "category": {
        "0": {
          "label": "Make-Up",
          "value": "Make-Up",
          "selected": true
        }
      },
      "image": {}
    }
  ],
}

There is no image returning.  I am requesting the list of all entries and their image/asset location from the API.

This should work for the images and you can easily adapt to do your categories & kachelgroesse if needed (not tested)...

'api/looks/list' => [
    'elementType' => 'Entry',
    'criteria' => ['section' => 'looks'],
    'transformer' => function(EntryModel $entry) {
        $images = [];
        foreach ($entry->featuredImage as $image) {
        $images[] = $image->url;
        }
        return [
        'title' => $entry->title,
        'id' => $entry->id,
        'url' => $entry->url,
        'jsonUrl' => UrlHelper::getUrl("looks/{$entry->id}.json"),
        'kachelgroesse' => $entry->kachelgroesse,
        'category' => $entry->category,
        'image' => $images
        ];
    },
],


How do I hide notifications about running tasks? I installed Import.  I tried to run a test import and it froze.   Now the pop-up status that appears in Craft's menu - just below Settings - will not disappear.

I even uninstalled the plugin.  But, the status notification is still in the menu.   It's driving me nuts.

How do I get rid of it?   At this point, I don't care about getting import to work or not.

(Oh, and there were no image transforms with the test import - it was only 21 lines of a simple css file.)

Those are running tasks that Craft (and plugins) use to perform long running maintenance operations and an Import task got stuck causing the ones to come after it not to run.

Since you don't care about the Import task anymore, you can open the craft_tasks table in the database and delete the one for the Import task (probably the top row).  The next time you load the control panel, the remaining tasks should complete.


Multisite system messages: change domain in {{ link }}, other than primary domain How can I change the domain in the generated {{ link }} in a system email, other than the primary domain?

I have a multi domain setup in craft3, and it works fine everywhere, except ... for the system messages.

For instance, when a user registers using a front-end entry form on a domain other than my primary domain, they receive the correct email, generated by the following template (Admin -> Email Settings -> System Messages):

Hey {{user.friendlyName}},

Thanks for creating an account with {{siteName}}! To activate your account, click the following link:

{{link}}

If you were not expecting this email, just ignore it.

The {{ siteName }} is rendered correctly in the email, but the {{ link }} still points to the primary domain, something like this: https://myprimaryandhtusthewrongdomain/actions/users/set-password?code=nicecode

I use the mailgun adapter to send the mails btw

This is currently expected behavior due to this bit of code, which explicitly passes in the primary site ID when generating those link URLs for a user:

https://github.com/craftcms/cms/blob/develop/src/services/Users.php#L1079-L1081

The reasoning is that even on a multi-site setup, users are all "global" (i.e. not defined per site).

If you feel this is a bug or produces undesired behavior, feel free to create an issue where it can be further discussed.


Sprout Email: "Unable to save recipient." Craft 2.5.2754, Sprout Email 2.01, fresh plugin install.

In attempting to use the Recipients feature, I created a new group (Contact Form Recipients) and hit the Add Recipient button. I filled out the email address, first, and last names, but it keeps saying "Unable to save recipient."

The error log says: Craft\SproutEmail_DefaultMailerRecipientModel->recipientLists failed validation: Recipient Lists cannot be blank.

I added a few more lists to be safe; no luck.

Is it possible I'm missing something, or is there a recommended fix or workaround for this?

Thanks for your help!

This was a bug and has been fixed in Sprout Email v2.0.2.


Accessing global set's field gives object that couldn't be converted to string I've been using globals many times but this is my first time encounter with such problem. Let's go from the bottom. I have two global sets with handles tax and company. The Tax set have only one plain text field taxRate which can be accessed like documentation says {{ tax.taxRate }}.

The Company set have more plain text fields and one table field.
Odd thing happens when I try to access any plain text field the same way. Example: {{ company.businessName }}.

I've tried to see what I get outputted using dump() function of that field and the output is quiet broader than the dump output of {{ tax.taxRate }}.

{{ dump(tax.taxRate) }}` -> string(2) "20"

{{ dump(company.businessName) }}` -> object(Craft\ElementCriteriaModel)#942 (17) {
["strictAttributes":protected]=&gt;
bool(false)
["_elementType":"Craft\ElementCriteriaModel":private]=&gt;
object(Craft\EntryElementType)#372 (7) {
["componentType":protected]=&gt;
string(11) "ElementType"
["_sourcesByContext":"Craft\BaseElementType":private]=&gt;
NULL
["_classHandle":"Craft\BaseComponentType":private]=&gt;
NULL ... and another dozens lines of it

I can access it using craft.globals.getSetByHandle('company') but I don't understand why I'm getting object returned in the set Company and string returned in the set Tax using the same fetch approach?

Well, when I went properly through the output of {{ dump(company.businessName) }} I found out right from the start that it is element criteria model of entry type.
That led me to check for the handle company that could exists twice in system.

I found section with the handle company. So two identical handles in system were causing my problem. But on the first place, how could Craft let me save the global set with the handle that already existed in system even if they are different type of element criteria model and Craft would throw that string conversion error anyway?


Best way to re-populate input field with Model data on front-end? I have a Plugin which saves data from a form the user enters on my front-end. When the input data is wrong and I display my validation errors using getErrors from the Model I want to re-populate all my form fields with the submitted data in my Twig template.

Right now I'm doing this like this:

<input type="text" name="fieldName" value="{% if model is defined %}{{ model.fieldName}}{% endif %}"/>

Is this the correct way of doing this or is there a better practice?

Seems like there's to many unnecessary if statements going on...

Thank you

The way the Craft docs do it is basically how you are already:

<label for="userLastName">Last Name *</label>
<input id="userLastName" type="text" name="fields[userLastName]" {%- if entry is defined %} value="{{ entry.userLastName }}"{% endif -%}>


How do you get all the categories in a category group in a controller? I want to get all the categories in a specific category group inside my controller. How do you do that?

I found the answer. This might help others:

$categories = craft()->elements->getCriteria(ElementType::Category)->group('handle');


Get 'purchasable' from ElementAPI The elementApi docs state you can use custom elementTypes via plugins.  I am using craft commerce which states it creates the custom elementType 'purchasable'

I've tried:

return [
'endpoints' => [
    'category.json' => [
        'elementType' => 'purchasable',
        'criteria' => [
            // 'category' => 'products', // randomly trying stuff here!
            // 'section' => 'products',
            // 'type' => 'products',
        ],
        'transformer' => function(EntryModel $entry) {
            return [
                'title' => $entry->title,
                'url' => $entry->url,
            ];
        },
    ]
]]

I've tried many variations on the word purchasable, as we as random guesses like 'product', 'item' etc etc

I should mention I am able to JSON back with all categories, Entries etc etc

I am sure this is possible, as craft, craftcommerce and elementApi are all made by the same guys!

Any help would be amazing

Try

return [
'endpoints' => [
    'products.json' => [
        'elementType' => 'Commerce_Product',
        'criteria' => [
            // 'category' => 'products', // randomly trying stuff here!
            // 'section' => 'products',
            // 'type' => 'products',
        ],
        'transformer' => function(Commerce_ProductModel $product) {
            return [
                'title' => $product->title,
                'url' => $product->url,
            ];
        },
    ]
]]


Is it safe to remove the craft/storage/runtime/sessions folder? I'm unable to transfer a site over to my development server because I keep getting problems with disk space. Space is actually fine, it's the number of inodes being used up. Using cPanel I get an error when using file manager to view the sessions folder, so I think that's the issue.

Using the following command, which I think is the one for counting the number of files in a directory:

ls | wc -l

It looks like there are over 300,000 files in there. How safe is it to delete the entire directory?

Docs

runtime/ – Pretty much everything in here is there for caching and logging purposes. Nothing that Craft couldn’t live without, if the folder happened to get deleted.

It is safe to delete it


Control Panel or admin dashboard not found I have installed craftcms 3 on my localhost. It got installed and the following screen appeared:

If I click on Control Panel I see the following screen

You need to set the web server to host from the /web directory. At present it looks like you are serving a folder that contains the Craft folder.


Craft's control panel search function returning inconsistent results When I search for titles of entries in the Craft control panel, I get odd results:

I have an entry called Fake Story about the Economy.

If I search for that exact title, I get no results.

If I search for Fake Story about, I get no results.

If I search for Fake Story, the entry Fake Story about the Economy is returned.

If I search for Fake Story the Economy, the entry Fake Story about the Economy is returned.

The results are similar for other entries in that channel. For example, I have an entry called Is it still business as usual?. The only way I can find it is if I leave out the word still when I type it (Is it business as usual?).

Other channels seem to be working fine.

This is of course affecting my ability to build a working search form on the front end as well.

Craft 3.3.19

Thanks for any help.

MySQL uses a list of stopwords when performing full-text searches. As you can see, about and still are in there... At the top of the page they say:

  False hits or misses might occur for stopword lookups...

I think this is the issue here. One way to get around it in your case is to add the below in your config/general.php (docs):

'defaultSearchTermOptions' => [
    'subLeft' => true,
],

If the above helps, another thing you can try is to change the storage engine your searchindex table from MyISAM to InnoDB. That would allow you to remove what you've added in your config/general.php but would require you to have at least MySQL v5.6+.


Can I eager load Super Table fields in a Matrix block? I have a Matrix with a Super Table in many of its blocks that I'm trying to eager load, but I can't figure out the syntax (or if its possible). The Super Table field's handle is columnSize and it contains two rows with dropdowns, screenSize and columnSize. My Matrix's handle is contentBlocks. The template code is below:

entry.contentBlocks.with(
    [
        'text:columnSize', // Super Table field
        'image:columnSize', // Super Table field
        ['image:image', {
            withTransforms: [{width: 1600, width: 1024, width: 768}]
        }], // Asset field
        ...
    ]
)

The syntax above works for every field I have except columnSize, which returns empty in the template when loaded using this method. Are there additional parameters needed for Super Table similar to image transforms?

This appears to have been added in the 0.4.7 release!

View the original issue here.

Note: I have not personally verified this functionality.


Caching and related Entries I'm currently working on integration some caching behaviour using {% cache %} tags. This works mostly just fine, but I have some issues when I work with related entries.

For example I have a list of categories with an indicator to show many entries they are assigned to.

New (3)
Used (2)
Broken (3)

If I change an entry and add a category, the cache refreshes and shows the correct amount. But if I remove a category from an entry I get the wrong number until I refresh the cache manually.

Is this the expected behaviour? Or do I miss something here?

Here is my code example:

{% cache unless not craft.config.cache %}
{% set categorySlug = productSection ~ 'Categories' %}

{% for category in craft.categories.group(categorySlug) %}
  {% set entryCount = craft.entries.relatedTo(category).total() %}
  {% if entryCount > 0 %}

      {{ category.title }} ( {{ entryCount }} )

  {% endif %}
{% endfor %}

{% endcache %}

This sounds like a bug, and I think I know why. Can you try changing the {% set entryCount ... line to this?

{% set entryCount = craft.entries.relatedTo(category.id).total() %}


Category slug is getting numbers appended to it I have a category Australia and have been experimenting with boboldehampsink import plugin.

After deleting categories and starting over the category Australia now has a slug called 'australia-6'. I just want 'australia' but i cant get rid of the  hypen and 6.

I notice the content table has lots of entries for australia but no slugs so I'm wondering about the db schema for craftcms and how come i have got a stuck slug so quickly?

When you save an entry/category that is supposed to have its own URL, Craft will assign it a unique URI, based on the section/category group’s URL Format setting.

For example, let’s say you have a section called News, and its “Entries in this section have their own URLs” checkbox is checked, and it has this URL Format:

news/{slug}

When you save a News entry, Craft will assign it a unique URI based on that URL Format. If the entry’s slug is “foo”, then the first URI Craft will try to assign it will be “news/foo”.

However, if that URI is already taken by another entry/category (or some third party element type), Craft will increment the slug and try again:

news/foo-1
news/foo-2
news/foo-3
etc.

(If incrementing the slug has no effect on the URI – because the URL Format doesn’t actually contain a {slug} token – then Craft will simply give up and add a validation error to the element.)

Once it has found a URI that is actually unique, it will update the element’s Slug with the incremented version.

So in your case, when you save a category with the slug “australia” and it becomes “australia-6“, that means that you have 6 other elements that had been saved with the slug “australia” before it, with the same URL Format that your current category has, and so “australia-6” was just the first slug Craft could find that resulted in your category having a unique URI.

To find out which elements your category’s URI is conflicting with, you can go to Entries > All Entries, and search for slug:australia, and click through each of your category groups in the Categories tab, searching for slug:australia in each of them.


From AttributeType::Enum in Model to a matching selectField? So I have a model with an Enum and I want a selectField for that Enum. Is there a DRY way to do that ? And if yes does it work with the translation-filter ?

The upcoming Craft 2.2 release added a getConstants() method to the BaseEnum class so you'd be able to do:

EnumClass::getConstants()

to get an array of all of the options in an enum.


Does a up to date boilerplate for Bootstrap 4 with live reload, sass and js build exist? I'm pretty new to Craft, and a noob when it comes to build tools. Coming from WordPress I've been using Roots/Sage which is a starter theme with liveReload(browserSync), Bootstrap 4, SASS and JS compiling.

I've been googling for a long time now, and I haven't been able to find a good boilerplate for me to start doing development on.

What build tool isn't really important to me(WebPack, Gulp, etc.).

What I'm looking for is:

liveReload, CSS-injection or simple browserSync. So when I'm updating my SASS styles and Craft templates the site automatically updates.
Bootstrap 4
JS concatenation and uglyfing

Is there anything out there that fits my(and I know a lot of other developers) needs?

Craft doesn't use themes, in the way WordPress does, so you won't find a direct equivalent to Roots / Sage.

That said, none of what you listed is CMS-specific, so there's nothing stopping you setting up a generic development / build workflow which matches your stated requirements.

Stack Overflow is a better place to ask about general tooling, though.


How to set dynamic criteria for endpoint with the Element API plugin? How can I set the criteria for an endpoint based on the current category view?

My elementapi.php looks like this where I can pass the slug of a category url to force a return of entries from the slug name.

$criteria = craft()->elements->getCriteria(ElementType::Category);
$criteria->slug = "albania";
$category = $criteria->first();

return [
                'endpoints' => [
                    'directory.json' => [
                        'elementType' => 'Entry',
                        'criteria' => [
                                'section' => 'directory',
                                'limit' => null,
                            ],
                        'paginate' => false,
                        'transformer' => function(EntryModel $entry) {
                            return [
                                'title' => $entry->title,
                                'mapinfo' => $entry->mapaddress,
                                'url' => $entry->slug
                            ];
                        },
                    ],
                    'directory-category.json' => [
                        'elementType' => 'Entry',
                        'criteria' => [
                            'section' => 'directory',
                            'relatedTo' => ['targetElement' => $category],
                            'limit' => null,
                        ],
                        'paginate' => false,
                        'transformer' => function(EntryModel $entry) {
                            return [
                                'title' => $entry->title,
                                'mapinfo' => $entry->mapaddress,
                                'url' => $entry->slug
                            ];
                        },
                    ],
                    'seafarers-directory/<entryId:\d+>.json' => function($entryId) {
                        return [
                            'elementType' => 'Entry',
                            'criteria' => ['id' => $entryId],
                            'first' => true,
                            'transformer' => function(EntryModel $entry) {
                                return [
                                    'title' => $entry->title,
                                    'url' => $entry->url,
                                    'body' => $entry->body,
                                    'mapinfo' => $entry->mapaddress
                                ];
                            },
                        ];
                    },
                ]
            ];

I tried setting the criteria category slug name with

craft()->urlManager->getMatchedElement();

but it always returned nothing.

Can a dynamic category be set for the criteria from the existing url like the dynamic entry is?

Finally figured this out.
By using the variable slug in the endpoint URL I was able to listen for the category in the currently viewed category slug and pull the data i needed for my google map. The trick is to typomake the end point a function - i was trying to set the criteria outside of the endpoint which is, in retrospect, silly.

'locations/<slug:{slug}>.json' => function($slug) {

                        $criteria = craft()->elements->getCriteria(ElementType::Category);
                        $criteria->slug = $slug;
                        $category = $criteria->first();
                        return [
                            'elementType' => 'Entry',
                            'criteria' => [
                                'section' => 'directory',
                                'relatedTo' => ['targetElement' => $category],
                                'limit' => null,
                            ],
                            'transformer' => function(EntryModel $entry) {
                                return [
                                    'title' => $entry->title,
                                    'mapinfo' => $entry->mapaddress,
                                    'url' => $entry->slug
                                ];
                            },
                        ];
                    },


How to clear a third-party cache when Craft updates its own template caches We're experimenting with using Fastly for full-site caching of a Craft site. Everything is working great so far, with the exception that Fastly obviously has no way to know when a URL's cache has been invalidated.

We were thinking that the easiest way to make this work would be to use Craft's built-in caching and create a simple plugin that listens for Craft to invalidate a URL's cache (such as when updating an Entry referenced by that cache) and call out to Fastly to clear its cache for the same URL.

Unfortunately, as far as I can tell, Craft doesn't provide an Event for when it updates its own caches. Am I missing something? Any tips on how to go about this?

Can't think of any clever workarounds, but this is on our 2.2 list to add events so that 3rd party services can be notified when template caches get invalidated.


Why is Redactor cutting off Headings after Heading 2? my client sees the attached screen in the editing window. it seems that Redactor is cutting off headings after Heading 2. They are using Chrome.
ideas?

The client was using a Mac and the trackpad scrollbars are set to only show when over scrollable content. This is embarrassing, but the issue is resolved.


List entries related to several categories This is the code used to display entries related to categories.

The issue: if an entry belongs to 2 categories, it displays all the categories to which it belongs on the page.

I need Craft to display only the category and entries the (lastSeg) of the slug (selected from a nav).

Can't find where to start?

{% set entries = craft.entries.section('contenuCompositions').relatedTo(category).order('dateYear','comp_opus','title').limit(0) %}
{% for category in craft.categories.group('compositions').relatedTo(entries).level(2).slug(lastSeg) %}

{{ category }}

{% for row in entries.relatedTo(category)|batch(3) %}

    {% for entry in row %}

{{ entry.title }}

    {% endfor %}

{% endfor %}

Not sure if this is still what you are after (I removed a lot of parameters), but basically this gets the one (!) category with the unique (!) slug you request from the URL and then lists the entries related to it:

{% set categorySlug = craft.request.getLastSegment() %}
{% set category = craft.categories.group('compositions').slug(categorySlug).first() %}

<h1>{{ category.title }}</h1>

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

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


Asset array by width? Is it possible to set a list of asset (images) based on their width? So i just want a list of images that are wider than 1000px for example... Without having to check in a for loop. So on the end of - craft.assets for example.

If you want to check all assets across all sources, it'd be this:

{% set assets = craft.assets.width('> ' ~ 1000).find() %}

If you want all assets from a particular source, it'd be:

{% set assets = craft.assets.source('sourceHandle').width('> ' ~ 1004).find() %}


Problem with accents in tags & URL I'm having a problem getting entries related to a tag with accentuated characters.
Here is what the URL looks like : fr.craft.dev/actualites/tag/Traçage%20électrique

The same page work fine with a tag without accents.

Is there anything special to do to fix this on my side ?
Or is-it a problem with craft ?
Here is my code :

{% set tag = craft.tags.name(tag).first() %}
{% set newsCriteria = craft.entries.section('news').relatedTo(tag).limit(5) %}

Thanks for your help.

Nicolas

There is a general config setting for it:

'limitAutoSlugsToAscii' => true,

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


Built in validate of title when using EntryModel? The example below will continue to save entries with the same title and slug.

Is there a built in validation when using craft()->entries->saveEntry?

$entry = new EntryModel();
$entry->sectionId = $sectionId;
$entry->getContent()->setAttributes(array(
            'title' => '123',
        ));
$re = craft()->entries->saveEntry($entry);

You could hook into the entries.beforeSaveEntry event like this:

craft()->on('entries.beforeSaveEntry', function (Event $e) {

    // Get the entry we're saving, and it's title
    $entry = $e->params['entry'];
    $title = $entry->getContent()->title;

    // Look for existing entries with the same title
    $existingEntry = craft()->elements->getCriteria(ElementType::Entry, [
      'title' => $title,
      'id' => $entry->id ? 'not '.$entry->id : null
    ])->first();

    // If an existing entry is found, prevent saving and pass an error message
    if ($existingEntry) {
      $entry->addErrors([
        'title' => Craft::t("An entry titled $title already exists"),
      ]);
      $e->performAction = false;
    }
});


Correct setup for Craft 3 and Git New to Craft and general novice full stop. I'm not sure about the correct way to use craft and git together. I currently use BitBucket for version control and have been using ExpressionEngine... so installing Craft and understanding the proper workflow is a bit confusing to get my head around.

I installed Composer locally and ran the command to install Craft 3, but then I just ended up putting all those files into BitBucket and then connecting that with my server, I created a database and then installed Craft on the server instead of locally. I have a feeling that's the wrong way to go about it, especially after reading that I shouldn't be putting the .env file in version control (which I can't understand?? if the .env file doesn't make its way to the server how does Craft know what database, etc. to connect to?)

Can anyone tell me or point me in the right direction to how I can learn the propper/correct flow and setup?

Thanks

This article might be of use to you: Setting up a New Craft CMS 3 Project

The DEPLOYMENT section talks about using Composer to install our dependencies, and not checking them into Git. That means a .gitignore that excludes vendor/

This isn't really a right or wrong situation, but rather that the advantages slightly outweigh the disadvantages, imo.

If you want to see this all live in action, you can see the devMode.fm website Github repo which is an OSS example Craft CMS website that powers the devMode.fm podcast.


How can I check the progress of a large Assets indexing job? I just uploaded 48,000 photos to Amazon S3, added the bucket to Craft, and am now trying to add them to the Assets library. Where can I see the logs for this?

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

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


Before saving Guest Entries, redirect to an entry review page? With guest entries, am I able to redirect before actually saving the entry to say a review page for the user to see the entry?

I figure I could use the guestEntries.beforeSave hook, but not sure how I'd show the information on the page... or pass to a template.

Any suggestions?

You can listen for the beforeSave event, but by redirecting or rendering another template, you're essentially hijacking the request and cancelling the initial save.

You could fork the plugin and add some custom logic using a flag you can tell whether it's an initial save and to redirect, or it's a subsequent save and to continue processing as normal.


Custom subdomain routing with a pluign I need to serve different home pages (structure entries) depending on the subdomain. For example:

foo.example.com -> this will render an entry with slug "foo"
bar.example.com -> this will render an entry with slug "bar"

So far I created a plugin and fetched the proper entry based on the subdomain:

class LocationsPlugin extends BasePlugin
{
    (...)
    public function init()
    {
        if (craft()->request->isSiteRequest()) {
            (...)
            // at this point $entry is the entry I want to render
            // the problem is the following doesn't work:
            craft()->templates->render("subdomains/_entry", ["entry" => $entry]);
        }
    }
}

I get the layout rednered but without the content. Twig's blocks aren't rendered at all and I suspect init() hook is called before some initializations required by render().

The question: How can I, having $entry in init(), just render it instead of the main page?

Thanks!
Jan

So I've been playing around with this and got stuck a few times, but I think I have a solution that could work for you.

I think the problem is actually because craft()->templates->render() simply just gives you the compiled template back, it doesn't try and render it in the browser. So your plugins init method is being hit, everything is working just fine, then it just goes past it and just keeps going until the end.

There is a controller method for rendering a template which is called like $this->renderTemplate which according to the docs:

  This is a shortcut for TemplatesService::render(), but rather than returning the rendered template, it will output it to the browser and end the request. It will also include any CSS and JS that is queued up via includeCssFile(), includeJsFile(), et al., and it will set the appropriate MIME type header based on the template file’s extension.

You can see the method from the Class reference here

Now I don't think you can or should try and call controller methods from within your plugin's init, so I think you need to do something inspired by this method i.e just echo the content and end the request.

So this is what I have used and it worked for me just fine, it might need some tweaking and more inspiration from the renderTemplate method, but it works as a start.

if (craft()->request->isSiteRequest()) {

  // Get your entry however you wish
  $entry = craft()->entries->getEntryById(264);

  // Render the template with $entry and set it to output
  $output = craft()->templates->render('arena', ["entry" => $entry]);

  // ** From renderTemplate **

  // Output it into a buffer, in case TasksService wants to close the connection prematurely
  ob_start();

  echo $output;

  // End the request
  craft()->end();
}

From this i got a rendered template in the browser, output the entries title using {{ entry.title }} and also all the {% extends %} {% block %} tags and everything else seemed to work.

If there is a better solution then i'm all ears, but this is what I could come up with from looking around the source/docs/stackexchange.

Hope that helps :)


Pasting into redactor with Arabic IS there a way of getting redactor to accept text pasted from arabic in a right to left flow? Should this be solved with custom style for editor based on locale? (Am already doing RTL direction in front end based on locale)

Update: temp fix
as per comments below i fixed with js plugin https://github.com/lindseydiloreto/craft-cpjs

Rough and ready hack to only run extra js when editing arabic entry.
On CpjsPlugin.php I edited the bottom:

 private function _renderJs()
 {
    $settings = $this->getSettings();
    if (trim($settings->jsFile)) {
        $filepath = craft()->config->parseEnvironmentString($settings->jsFile);
        if ($hash = @sha1_file($filepath)) {
            craft()->templates->includeJsFile($filepath.'?e='.$hash);
        } else {
            craft()->templates->includeJsFile($filepath);
        }
    }
    //joomkit hack for arabic redactor  fix
    $lang =  $this->_getCurrentLocale();
    //only add extra js if arabic is set in url
    if($lang == "ar"){
        craft()->templates->includeJs($settings->additionalJs);
    }
}

private function _getCurrentLocale() {

    $segments = craft()->request->segments;
    $lastSegment = end($segments);

    return $lastSegment;

}

The the extra js settings;

$(function()
{
$('.redactor-editor>*').css({
    direction: 'rtl',
    float: 'right'
});
$('.input #title').css({
    direction: 'rtl',
    float: 'right'
});

});

this will help arabic translators start translating hopefully