Learn

Share Your Knowledge!

This fast-growing section of our site is the new one-stop shop for educational materials for ExpressionEngine with rich and expert content, from both EllisLab and the community. Submit your article, tip, or solution today!

  • MySQL 8 and ExpressionEngine: Tips for the Trailblazers

    in: Tips

    NOTE: This article was written October 2, 2018 when MySQL 8 was very new. This article may quickly become stale as PHP and MySQL progress.

    MySQL 8 is cutting edge. And developers who want to try out the new features can expect a smooth ride in ExpressionEngine—with minor tweaks to their MySQL settings.

    The biggest incompatible change to MySQL 8 is in how it authenticates your password. The default is a new plugin called caching_sha2_password. This results in an error in all sorts of applications, including desktop MySQL browsers:

    The server requested authentication method unknown to the client [caching_sha2_password] …

    Since most versions of PHP do not recognize this new plugin†, you’ll need to change MySQL to authenticate the old way. Use a my.cnf configuration file to restore the old way. In your my.conf in the [mysqld] section add:

    default_authentication_plugin=mysql_native_password

    Restart the server after editing the option file. Run the query SHOW VARIABLES; and look for the default_authentication_plugin value to verify that your change took.

    If the value is correct and you still get that pesky error, then the MySQL user probably needs to be modified as well. To change the user’s authentication plugin, use the ALTER USER command:

    ALTER USER 'jeffrey'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

    (replacing password with your actual password, of course). And that should take care of it!

    † From the PHP manual:

    PHP version before 7.1.16, or PHP 7.2 before 7.2.4, set MySQL 8 Server’s default password plugin to mysql_native_password or else you will see errors similar to The server requested authentication method unknown to the client [caching_sha2_password] even when caching_sha2_password is not used.

    | Read in 2 minutes
  • Downloadable iCalendar Files

    in: Tips, Tutorials, Templating

    This tutorial will show you how to create downloadable iCalendar files. With ExpressionEngine, it’s easy-peasy because you can use your well-structured content however you need. You can use the same entries you use for your web site to create shareable .ics iCalendar format files. Here’s how in three easy steps.

    1. Install the “Download Content” plugin.
    2. Create your iCalendar format template.
    3. Build a link to your iCalendar template.

    Install the free Download Content plugin

    In order to make your ExpressionEngine-generated .ics files be treated like a downloadable file, grab and install the free Download Content plugin.

    • Download the plugin from GitHub.
    • Unzip and upload the enclosed download_content folder to /system/user/addons.
    • Visit the Add-on Manager in your control panel, and click Install for the “Download Content” plugin.

    Create your download template

    Now we need to create an endpoint that we will link to that allows our visitors to download our .ics file. In this example, I’m creating an iCalendar file for a free concert the local brewpub is putting on. I have created a template named add-to-my-calendar inside my concerts template group.

    It uses the entry date and expiration date of the concert, the title, and a custom field called meta_description. Side note: meta_description is part of my standard set of SEO fields that I add to every channel. Repurposing it for the description of the calendar event makes sense. I’m also going to take advantage of a few iCalendar fields to setup a default alarm two hours before the event.

    {exp:channel:entries channel='concerts' limit='1' show_future_entries='yes' require_entry='yes'}
    	{if no_results}
    		{redirect='404'}
    	{/if}
    
    {exp:download_content filename='{url_title}.ics'}
    BEGIN:VCALENDAR
    VERSION:2.0
    BEGIN:VEVENT
    UID:downloadable-icalendar-files-{entry_date format='%Y%m%d'}@{site_name:url_slug}
    DTSTART;TZID={entry_date format='%e:%Y%m%dT%H%i%s'}
    DTEND;TZID={expiration_date format='%e:%Y%m%dT%H%i%s'}
    SUMMARY:{title}
    X-APPLE-TRAVEL-ADVISORY-BEHAVIOR:AUTOMATIC
    LOCATION:The BrewPub\n62950 NE 18th St\nBend\, OR 97701
    BEGIN:VALARM
    X-WR-ALARMUID:downloadable-icalendar-files-{entry_date format='%Y%m%d'}@{site_name:url_slug}
    TRIGGER:-PT120M
    DESCRIPTION:{meta_description}
    ACTION:DISPLAY
    END:VALARM
    END:VEVENT
    END:VCALENDAR
    {/exp:download_content}
    
    {/exp:channel:entries}

    Let’s break down the tags:

    {exp:channel:entries channel='concerts' show_future_entries='yes' limit='1' require_entry='yes'}
    
    • show_future_entries= lets us use the Entry Date as the start time of the event. Since we want people to see events that haven’t happened yet, we tell the Channel Entries tag to show future events.
    • limit='1' require_entry='yes' is a common design pattern for single-entry templates. This makes sure we only show one entry, and we don’t want it to display anything if the URL title is invalid. We combine it with:
    {if no_results}
        {redirect='404'}
    {/if}
    

    If there are no results—an invalid URL, an entry that’s already expired, a closed status, etc.—we will have proper 404 behavior.

    The next tag is the Download Content plugin we installed earlier.

    {exp:download_content filename='{url_title}.ics'}
    

    Using the entry’s URL title for the filename isn’t a requirement, but it will help the visitor identify the file’s purpose after downloading. It needs an .ics extension so desktop and mobile calendar apps know that the file is for them. This plugin makes sure the output only contains what’s inside the plugin’s tag, and gets treated as a download instead of displaying it on the screen.

    iCalendar files are picky about whitespace. This is why the code is not indented like we would normally encourage.

    The rest of the contents are made from standard iCalendar file format attributes. You can read more about that file format and its attributes here: iCalendar file format. Here are some notes to help dissect our template decisions:

    • UID: Providing a UID allows the calendar app to change the existing event if it is updated. Otherwise adding a new version will create a new event. The URL title, date, and site name should give us a unique ID. The :url_slug variable modifier replaces spaces with slashes and so forth so we can use the site’s name here.
    • DTSTART/DTEND: The format parameter used here (%e:%Y%m%dT%H%i%s) will output: America/Los_Angeles:20180827T105300
    • We use the entry_date for the start date and the expiration_date for the end date.
    • SUMMARY uses the entry’s title for the event’s name
    • Some calendar apps may not use the DESCRIPTION, but we add it for those that do. Likewise, we add DTSTAMP because Outlook demands it.
    • TRIGGER:-PT120M sets an alarm for 120 minutes (2 hours) before the event
    • X-APPLE-TRAVEL-ADVISORY-BEHAVIOR:AUTOMATIC sets an alarm for Apple clients when the person needs to leave their current location to make it to the event on time.

    Create a download link

    Now all we need to do is link to our .ics file and we’re good to go! From my concert listing page, I’ll add:

    <a href="{url_title_path='concerts/add-to-my-calendar'}">Add this concert to my calendar!</a>
    

    Now when I click the link, it downloads a properly formatted .ics file that I can add to my calendar!

    With this knowledge you can create events, todos, journal entries, and any other kind of iCalendar file you can imagine. Use these same principles to output your content in other file formats, too; the sky’s the limit! With ExpressionEngine, you never have to work hard to make your content work hard for you.

    | Read in 5 minutes
  • Getting Started: A 10-Minute Primer

    in: Tutorials, Templating

    Get started using ExpressionEngine FAST. This primer will introduce you to the basic concepts needed to master ExpressionEngine, and build a fully functional blog in just 10 minutes!

    Learn the basics of building a beautiful web site with ExpressionEngine and make a fully functional blog from scratch in just 10 minutes!

    Subscribe to future videos here: http://bit.ly/ExpressionEngineTV

    Music: https://www.bensound.com

    Transcript

    ExpressionEngine is one of the web’s most flexible content management systems.You can create, really, any kind of website with it. But we wanted to make a quick primer for those new to ExpressionEngine to show how all the pieces work together; how you put in content, how you display that content.

    And so, we’re just going to make a simple blog for now, it’s a type of site that we’re all familiar with so it should make things easy to understand. So, I’ve got a fresh install ready here, a blank canvas to create our site, and I’ve logged into the control panel.

    The first thing I like to do is name my site, and there’s this button up here that’s kind of calling out to me to do that, so we’ll click through and we’ll just call it, “My Blog.” And we’ll update the short name as well, and we’ll click Save. And you can see the new name shows up right here in the top left of our control panel.

    Now from here, we need to set up a way to enter our blog’s content, and to do that in ExpressionEngine, we need to setup a Channel. Now, Channels are basically the different types of content your site has, and your content lives in these Channels. Maybe think of them as the buckets for your different types of content.

    So, if your site has events, you might have an events Channel, or if your site has recipes, you might have a recipes Channel. But the important thing with Channels is that allow you to break free of the idea of content living on a certain page on your site, we don’t really have to think about that now, we just want our content to be able to stand on its own, and then ExpressionEngine will allow us to display it on any part of the site that we want and within any design that we want.

    So for our blog, we just need to make one simple Channel. And to do that, we’ll go the Developer menu and click Channels, and then Add New. And we’ll just call it, “Blog.”

    Next we want to add the fields, or the different parts of our content that we want to capture separately. So again, if you had an events Channel, you might use some Date fields to capture some dates, or your recipes Channel might use a Grid field to capture all the steps and ingredients that appear in a recipe.

    But for our blog, we at least want to capture the blog entry text itself, and for that, I just want to use a simple Textarea, we’ll call it, “Body.” And I want to choose Markdown as the formatting type because I want to keep my content as clean and as portable as I can, and you can install this formatting type via the Add-ons page if the option isn’t showing up here for you. And with that, we’ll click Save, and you can see it shows up here.

    But there’s a couple more fields I think I might want, one is an excerpt field, in case I want more control over the excerpt that appears on my blog homepage. So we’ll create that the same, maybe make it a little smaller.

    And I also think I want a featured image be a part of my blog posts. and for that I want to use a File field so I can upload a file directly from the publish form. We’ll call it, “Featured Image.” And to use the file field you do need to have set up an Upload Destination in ExpressionEngine which I must confess I did set up prior to this so I didn’t have to break the flow, but it’s a simple thing to do. And with that, we’ll save our Channel.

    And you’ll see it now appears under the Create menu, and if we click through, you’ll see all the fields that we just created. So this is ready for me to post my first blog entry. I’ll just paste in some placeholder content here.

    So we have our first entry, but how do we display this on our website? For that, we need to write some templates. So we’ll go to the Template Manager, and first need to make a Template Group for our templates to live in. So we’ll just call it blog, and we’ll keep it the default group, and we’ll click Save. And when we make a Template Group, it automatically creates an index template for us, and since this Template Group is our default Template Group, this index template is actually our site’s homepage.

    Now ExpressionEngine lets us edit the template within the control panel here, but when you create a template, it also creates a file that you can edit with a text editor, and many folks prefer to just work with that file directly. So I’m going to do that. So here’s our installation, I’m going to go to system, user, templates and here is that same site short name that we set up earlier, so our templates must be in here. And here is that same index template that we just created under our blog Template Group.

    Now one of the things that makes ExpressionEngine stand out is its easy, clean, but powerful template language. If you know HTML, it should look pretty familiar and be easy to understand. For example, say you wanted to display the site name that we set up earlier. Instead of angle brackets like in HTML, ExpressionEngine’s template tags and variables use curly braces. And of course you can write any HTML you like around these variables to make them look how you want.

    So we’ll save this, and now let’s take a look at our homepage. And there is the site name that we set up earlier surrounded by those H1 tags. But let’s start writing the code that shows us our blog entries. I’m going to start out with an opening curly brace, and type exp:channel, because our blog content is in a channel, and we want the entries from that channel.

    Now here’s where the HTML knowledge is going to come in handy because we’re going to add some attributes, or parameters, to this tag. First we need to tell it which channel we want to pull from because we can have all kinds of channels, we want to pull from the “blog” channel. And since this is our homepage, we want to show the newest entries first so we want to order by the entry date and we want to sort descending. And let’s say we only want to show the most recent 10 entries. And again like in HTML, we’re going to write a closing tag.

    Now in between these two tags is where we’re going specify how we want our content displayed. And whatever is inside the tags here is going to repeat for each blog entry. So let’s say we want to show the entry title inside some H2 tags here. And then right below that, we want to show our excerpt. So we’ll save that and let’s go see how it looks.

    And there’s our blog entries. Here’s the one I added earlier, and then a couple more that I went back and added just to fill out the listing some more. But, it looks like this one is missing an excerpt. Maybe I forgot or just didn’t feel the need to make a special excerpt for that entry. But I still want to show a little preview of the entry so I’m going to go back to my template and add a conditional.

    And if you’re at all familiar with programming, this should make some sense. We’re basically going to say {if excerpt}, basically if the excerpt exists, show the excerpt, otherwise, take the body of the blog article and limit it to, let’s say, 200 characters. And we’ll close the conditional. And we’ll refresh.

    Ah, that’s looks better. Now we’re missing one crucial thing and that’s to be able to read the full article. So, to do this, we need to make a new template. And I actually don’t need to go to the control panel to make this template, I can just make a new file inside our Template Group. So we’ll just call it, “article.” And in that template, we want to put in a Channel Entries tag like on our homepage except this Channel Entries tag is going to let the URL tell it which entry to show, and we want to make sure it shows nothing else, so we want to put this require_entry= parameter on there.

    And we’ll change this to show the article body. And next, we’ll go back to our homepage template, and add a link to view the full article. We’ll just make a link out of the title here, and we can use this handy url_title_path variable, and we’ll point it to our article template that we just set up in our blog Template Group. And what this variable will do is automatically generate a URL based on the entry’s title.

    Ok, so let’s see if this works. We have links, and we’ll click through, and hey, there’s our full article. And if we take a look at the URL up here, you can see it’s loading the article template, and it’s put a nice, readable title for this entry so that the article template knows which entry to show. And with that, our blog is pretty much finished.

    But there’s one glaring thing we haven’t addressed and it’s that this doesn’t look very good, we’re just using the default browser styles here. And that’s because, if we look at the source, ExpressionEngine doesn’t inject its own markup or CSS, you are 100% in control of that. So to fix this, I’ve found this free template that I kind of like from a template site, and it’s just the HTML, it wasn’t even built with ExpressionEngine in mind, but that’s ok, I can just take these templates, the homepage and article template here, and paste them into the templates we thet we just made, and then we just replace the static, hard-coded content with our dynamic ExpressionEngine tags. So, let’s do that.

    And that should do it. You might notice there’s a lot of duplicate code across the templates and you’ll typically want put that stuff into a Template Layout, but that’s for another video. So, let’s take a look.

    And there you go, there’s my blog entries, and you can even see I got to use the featured images that I added to the entries, I just took the custom field variable and put it in the image tag source, and it just worked!

    So hopefully by now you get the gist of how ExpressionEngine works, and you have a good jumping off point for being able to publish your universe. Now there’s a lot more to cover from Template Layouts as I mentioned, to Fluid content, relationships, custom add-on development, so much more. It’s all in our documentation, but also subscribe to this channel and we’ll try to break down these things together.

    | Read in 11 minutes
  • Smarter Breadcrumbs with Layout Lists

    in: Front-end Development, Tips, Templating

    Layout List variables make it easy to reuse content in a variety of ways.

    Define the breadcrumb name, URL and position on your content templates:

    {layout:set:append name='breadcrumb_urls'}{path='second'}{/layout:set:append}
    {layout:set:append name='breadcrumb_titles'}Second crumb{/layout:set:append}
    {layout:set:append name='breadcrumb_jsonld_positions'}2{/layout:set:append}
    
    {layout:set:append name='breadcrumb_urls'}{path='active'}{/layout:set:append}
    {layout:set:append name='breadcrumb_titles'}Active crumb{/layout:set:append}
    {layout:set:append name='breadcrumb_jsonld_positions'}3{/layout:set:append}

    Output it in whatever format you need on your layouts.

    Bootstrap’s Modern Business template’s HTML breadcrumbs:

    {layout:breadcrumb_urls}
        {if count == 1}
            <ol class="breadcrumb">
                <li class="breadcrumb-item">
                    <a href="index">Home</a>
                </li>
                <li class="breadcrumb-item">
                    <a href="{value}">{layout:breadcrumb_titles index='{index}'}</a>
                </li>
        {if:elseif count < total_results}
                <li class="breadcrumb-item">
                  <a href="{value}">{layout:breadcrumb_titles index='{index}'}</a>
                </li>
        {if:else}
                <li class="breadcrumb-item active">
                    {layout:breadcrumb_titles index='{index}'}
                </li>
            </ol>
        {/if}
    {/layout:breadcrumb_urls}

    A structured markup BreadcrumbList:

    <script type="application/ld+json">
        {
            "@context": "http://schema.org",
            "@type": "BreadcrumbList",
            "itemListElement": [
            {
                "@type": "ListItem",
                "position": 1,
                "item": {
                    "@id": "https://example.com/index",
                    "name": "Home"
                }
            }
            {layout:breadcrumb_urls}
            ,{
                "@type": "ListItem",
                "position": {layout:breadcrumb_jsonld_positions index='{index}'},
                "item": {
                    "@id": "{value}",
                    "name": "{layout:breadcrumb_titles index='{index}'}"
                }
            }
            {/layout:breadcrumb_urls}
        ]}
    </script>

    DRY, flexible, easy to maintain and update. Your final result will look like:

    <ol class="breadcrumb">
        <li class="breadcrumb-item">
            <a href="index">Home</a>
        </li>
        <li class="breadcrumb-item">
            <a href="second">Second crumb</a>
        </li>
        <li class="breadcrumb-item active">
            Active crumb
        </li>
    </ol>
    
    <script type="application/ld+json">
        {
            "@context": "http://schema.org",
            "@type": "BreadcrumbList",
            "itemListElement": [
            {
                "@type": "ListItem",
                "position": 1,
                "item": {
                    "@id": "https://example.com/index",
                    "name": "Home"
                }
            },
            {
                "@type": "ListItem",
                "position": 2,
                "item": {
                    "@id": "https://example.com/second",
                    "name": "Second crumb"
                }
            },
            {
                "@type": "ListItem",
                "position": 3,
                "item": {
                    "@id": "https://example.com/active",
                    "name": "Active crumb"
                }
            }]
        }
    </script>
    | Read in 3 minutes
  • Blog Channel Set

    in: Resources, Channel Sets

    Quickly create a Blog Channel for ExpressionEngine with this Channel Set.

    The Blog Channel Set comes with custom fields, statuses, and categories to get you up and running fast. Further customizations can be made to tailor your blog, but here’s what’s inside:

    Custom Fields

    • {blog_content}: a Textarea for the blog post’s content
    • {blog_image}: a File field for a featured image for the blog post

    Statuses

    • Open: published
    • Closed: not published
    • Featured: to call attention to a blog post, typically on the homepage

    Categories

    • News
    • Personal
    • Photos
    • Videos
    • Music

    Sample Tags

    {exp:channel:entries channel='blog' limit='1' require_entry='yes'}
    	{if no_results}
    		{redirect='404'}
    	{/if}
    
    	<h1>Blog Channel Set</h1>
    
    	{if has_categories}
    		<div>Categories:
    			<ul>
    				{categories}
    					<li><a href="{path='blog/index'}">{category_name}</a></li>
    				{/categories}
    			</ul>
    		</div>
    	{/if}
    
    	{if blog_image}
    		{blog_image}
    			<figure>
    				<img src="{blog_image:image}" alt="{blog_image:caption}">
    				<figcaption>{blog_image:caption}</figcaption>
    			</figure>
    		{/blog_image}
    	{/if}
    
    	{blog_content}
    {/exp:channel:entries}
    | Read in 1 minute
.(JavaScript must be enabled to view this email address)

ExpressionEngine News!

#eecms, #events, #releases