Ansel and Assets Sitting In a Tree

I’m pleased to announce that as of today, with the release of Assets 3, Ansel now works with Assets on both ExpressionEngine 2 and ExpressionEngine 3. In the past, many users have asked if Ansel works with or supports Assets. Until today the answer had been no. The reason is Ansel came into being after ExpressionEngine 3 had been released and Assets was not compatible with ExpressionEngine 3. And, while I love Pixel & Tonic and the team over there, the future of their ExpressionEngine add-ons was uncertain. It seemed like a bad use of time to make Ansel compatible with a file-management add-on that may not have a future beyond EE 2. While Ansel has always been compatible with EE 2, EE 3 is the future.

However, with the fine folks over at EEHarbor having acquired Assets and having offered me a pre-release of Assets 3, the time seemed right to work on Ansel's compatibility with Assets.

Starting with Ansel 1.4.0, if Assets is installed, you can choose any of Asset's sources as an upload and/or save destination for an Ansel field type.

As always, if you would like a copy of Ansel to evaluate, don't hesitate to get in touch!

Speaking at ExpressionEngine Conference 2016

I am excited (and slightly nervous) to tell you that one of the presentations I submitted to ExpressionEngine Conference 2016 has been accepted. I will be speaking on Git best practices and how best to leverage Git when using ExpressionEngine. This is a topic I am passionate and excited about. Git is one of the best things that happened to me as a developer and I’ve put a lot of time and effort into learning how best to leverage Git. I also believe in sharing knowledge so I’m very happy to bring the knowledge that I have to a community I truly enjoy being part of.

I also hope that my presentation will bring something for both old and new hands at Git. We can all use some brushing up from time to time, and we can all be reminded of things or encouraged to do things we know we should be doing but aren’t. Even writing this presentation reminded me of some best practices I had let slide.

I really hope you will come to the conference. And not just to hear me, either. There will be a lot of great presentations that I am quite looking forward to. And I’m looking forward to catching up with friends I interact with online a lot but rarely see.

And if you register before September 1st, you’ll get the early bird admission special price!

One other thing: If you’re at the conference, come up and say hi. I make a lot of noise online, but when I don’t know someone, I tend to be standoffish. It’s something about my personality of which I am aware and working on. All the same, it’s good for me to push myself, and good for you to push me.

And as a special conference promotion, if you come up and say hi to me, and you’re interested, I’ll be glad to give you a copy of the Ansel + Treasury bundle. You give me your devot:ee account email and a license with ability to download the bundle will magically appear in your devot:ee account.

Treasury for ExpressionEngine

Today, I am pleased to announce Treasury for ExpressionEngine 3. What is Treasury? Well, I’m glad you asked! Treasury is a file manager for ExpressionEngine. But not just a file manager — after all, ExpressionEngine already has the capability to store and manage files on the local server. Treasury allows you to store files on Amazon S3, SFTP, and/or FTP locations. And yes, for completeness sake, it does have a local mode as well.

Treasury has tags that allow you to retrieve and display any and all files stored in Treasury’s locations, and Treasury also has a simple file field for selecting files on Channel Entries. The field is also compatible with Grid, Bloqs, and Low Variables (of course!).

But that's not the real power of Treasury. Because in addition to the simple file field, Treasury has a developer API so any ExpressionEngine developer can make any applicable add-on Treasury compatible.

I went first in making an existing add-on Treasury compatible (you know, obviously). Starting right now with Ansel 1.3.0, Ansel is Treasury compatible. This means you can combine the power of Ansel’s image manipulation with full CDN storage. This has been one of the most requested features for Ansel. But it was beyond the scope of Ansel. Ansel is not a file manager, it is a field type that manipulates images. But now, Treasury provides the storage layer should you want to use Amazon S3, or other scenario for your file storage.

I’m providing two ways for you to purchase Treasury. The first is as a stand-alone add-on. The second is as part of a bundle with Ansel. The bundle provides a $49.00 savings over buying them both separately!

And, as always, you can get in touch if you want to test drive any commercial add-ons. When you test drive a BuzzingPixel add-on, you’ll get the full version. Once you have test driven the add-on(s), all you’ll need to do is purchase a site license and enter the license key and you’ll be good to go.

So let’s get started building awesome things!

Marksmin

Marksmin has been updated for ExpressionEngine 3. And it is still compatbile with ExpressionEngine 2. Because of the nature of the add-on and what it does, there are not two versions of the add-on — one for EE 2 and one for EE 3. Marksmin uses one ExpressionEngine extension hook available in both EE 2 and EE 3 and minify’s template HTML output. So making it work with both was very easy.

As you may or may not be aware, Marksmin was not my add-on up until this point — but I used to be a senior developer at the company where it originated and having minified HTML output is fairly critical to my workflow because of the consistency it brings to using display: inline-block; in CSS, and the benefit of less data transfered to the end user.

As to why I’m taking over development, the conversation when something like this, "Hey TJ, you want to take over dev on any of these add-ons you use on a regular basis?" And I said, "sure." Updating Marksmin for EE 3 was pretty easy so here it is. Enjoy!

Ansel for ExpressionEngine

I am really and truly excited to release Ansel for ExpressionEngine. Ansel is something I’ve been working on for quite a while. It was first conceived of over two years ago while I was working at Caddis. I asked my friend and Caddis co-owner, Michael, if surely there was not a better way to manage image sizes and constraints in a CMS. Using existing resizing tools was a very frustrating experience because they were all blind cropping tools. What I mean is, sure, you could have a file field to upload an image to, but if you resized it, either with ExpressionEngine manipulations, or with tools for resizing images on the fly in templates, those tools would blindly crop and resize. What we wanted was a way to let the user choose what part of the image to see and stay within defined constraints.

Michael wrote the precursor to what is now Ansel — and I want to be sure to give him credit because without his work on that precursor add-on, Ansel would simply not be possible.

I believed that with polish and carefully considered development, Ansel could be commercially viable. It fills a big need in the content management world. With his blessing, I wrote Ansel from the ground up, but based on many of the original concepts from his original add-on.

And I am very excited to bring Ansel to you today. The concept behind Ansel is simple enough: define constraints on the image, minimum and maximum width and/or height and crop ratio. Then present the content editor with a lasso that lets them select the portion of the image they want within those constraints. As you may know if you’ve ever done any image manipulation, it can be extremely complex. But I’m proud to say that Ansel takes this complexity and makes it work as simply as it sounds.

It is, in many ways, my biggest add-on release yet — and it is the one I am most proud of to date. And because I am proud of it, and confident in the need the it fills and the problem it solves, I want to make you an offer. If you want to test drive Ansel I will be happy to provide you a copy. Just get in touch with me and ask me for a copy to test drive and I’ll get it to you right away.

Of course, as always, to use it on a live site you need to purchase a site license, but I am confident that once you’ve used it, you will want to do just that!

How To Use Construct

Whether you just want to create user-friendly, drag and drop menus, or you would like to create menus with associated Channel Entries, Construct is a great tool to have in your tool belt. In this tutorial, I’d like to show you how to use some of the features of Construct.

Let's first take a look at how to create menus with Construct.

Menus in the Control Panel

If you have a fresh installation of Construct, you will need to create one or more trees for your nodes (navigation items) to live in. If you have more than one menu on your site — say very different menus for the header and footer — you might want to create two trees such as Header and Footer.

When creating trees, you can assign which member groups will have access to those trees. Non-Super Admins will only see the trees you give them access to and will not have access to any other settings. This makes Construct a great tool for separating responsibilities among content editors and site managers as needed.

Once you have created some trees, you are ready to add nodes to that tree. When using Construct for only menu creation (meaning you have routing turned off in settings), the Node options are very simple.

Create Node

After creating a node you can drag and drop nodes to nest them as needed (within the neseting limits you set when creating the tree). Construct uses the node's slug to build out the full URL path to that node. So for instance, if you have the node our-ceo nested under our-company, which in turn is nested under about, which is at the root level, the our-ceo node’s full URL path would be about/our-company/our-ceo.

Nodes

This makes it extremely easy for site managers to manage the menu structure without having to type out paths with the added possibility of making mistakes.

But not all sites are the same; they don't all conform to a specific structure and may need to link to external URLs in the menu, or a different location in the hierarchy. Construct 2 accounts for that with the external URL field. You can link to anything you need to from any node.

Menu Output in Templates

Now that we have a tree or two with nodes in them, we need to output those nodes for the menus. Fortunately Construct makes this pretty easy. Here is an example of simple menu output:


<nav class="site-nav">
    {exp:construct:nodes tree_id="2"}
        {if construct:level_count == 1}
        <ul class="site-nav__list">
        {/if}
            <li class="site-nav__item">
                <a href="{construct:node_link}" class="site-nav__link">
                    {construct:node_name}
                </a>
                {construct:children}
            </li>
        {if construct:level_count == construct:level_total_results}
        </ul>
        {/if}
    {/exp:construct:nodes}
</nav>

So let’s deconstruct what's going on there. It’s all pretty easy to grasp if you are used to working with EE templates.

All the nodes are going to use the same markup from our tag pair. If it is the first item for the current level ({if construct:level_count == 1}), we're going to start a new unordered list. If it is the last item for the current level({if construct:level_count == construct:level_total_results}), we're going to end the unordered list.

We're using the {construct:node_link} tag which will output a link to the current node hierarchy, or the external link if present. And, of course, the node name.

Here is where things get more interesting. There's a strange looking tag in there: {construct:children}. Think of this tag as a marker. This is where any children of that node are placed. The children are rendered using the same markup, but instead of just being placed next after the parent node's output, they are placed where the marker tag is at. This allows for the possibility of nesting while using the same markup for all your nodes and keeping things really DRY.

But again, not all sites are the same and you may have a need to use different markup for a specific node level. Luckily for us, the ExpressionEngine template parser will let us do what we need:


<nav class="site-nav">
    {exp:construct:nodes tree_id="2" max_depth="2"}
        {if construct:node_level == 1}
            {if construct:level_count == 1}
            <ul class="site-nav__list">
            {/if}
                <li class="site-nav__item">
                    <a href="{construct:node_link}" class="site-nav__link">
                        {construct:node_name}
                    </a>
                    {construct:children}
                </li>
            {if construct:level_count == construct:level_total_results}
            <ul>
            {/if}
        {if:elseif construct:node_level == 2}
            <div class="site-nav__sub-nav-panel">
                <a href="{construct:node_link}" class="site-nav__sub-nav-link">
                    {construct:node_name}
                </a>
            </div>
        {/if}
    {/exp:construct:nodes}
</nav>

So in this case we're using entirely different markup for the second level nodes, which are being placed where the {construct:children} tag is at for their parent. This is just the simplest of examples, but I'm sure you can see just how powerful that is.

Creating Pages

Construct can also create pages for the menu items. To do this, you will need to create template preferences in the Construct Control panel. Each template preference has a few options: a name that you want the content editors to see when selecting the template, the EE template, channels for the template, and some listing options.

Standard Page Template

After you have created some templates, you also need to add them to any trees you want them to be available to. After you have done that, the template, and any entries from the channels you chose for that template, will be available to select for each node.

Creating a page

Once a template and entry are selected for a node, when you visit that Node's URI, Construct will serve that template and make certain variables available to the template so you can hook up the Channel Entries tag. Here is a very simple example:


{exp:channel:entries
    disable="categories|member_data|pagination"
    dynamic="no"
    entry_id="{construct_route:node_entry_id}"
    limit="1"
    status="open"
}
    <h1>{title}</h1>
    {body}
{/exp:channel:entries}

Entry Listing (such as a blog)

Construct can also make it easy to create entry listing and single entry pages. When creating template preferences, you can choose to make channels available to that template for entry listing, or you can define a template as a single entry template:

blog index template

blog entry template

You can see in the first screen shot that I have created a blog entry template, and in the second I have created a blog single entry template. Now when I choose the blog index as the template for a node, it will also give me the option to chose any of the listing channels for that template, and to choose a single entry template for those entries.

Here is what my node settings look like:

blog node

Now visiting that node’s URI (along with a pagination segment if present since I selected pagination for that node), Construct will serve the blog index template, and node/uri/some_segment will serve the blog entry template. Now let's look at the code that powers those two templates.

Blog Index Template


{exp:channel:entries
    channel="{construct_route:node_listing_channels}"
    {if construct_route:node_pagination}
    disable="categories|member_data"
    limit="{construct_route:node_pagination_amount}"
    {if:else}
    disable="categories|member_data|pagination"
    limit="6"
    {/if}
}
    {if no_results}{redirect="404"}{/if}
    <h2><a href="/{construct_route:node_full_route}/{url_title}">{title}</a></h2>
    {body}
    <hr>
    {if construct_route:node_pagination}
        {paginate}
            <p>Page {current_page} of {total_pages} pages {pagination_links}</p>
        {/paginate}
    {/if}
{/exp:channel:entries}

For the index template, I’m feeding the channel entries tag the {construct_route:node_listing_channels} variable, and I'm checking on whether pagination has been enabled to do various things.

Blog Entry Template


{exp:channel:entries
    channel="{construct_route:node_listing_channels}"
    disable="categories|member_data|pagination"
    limit="1"
    require_entry="yes"
    url_title="{last_segment}"
    dynamic="no"
}
    {if no_results}{redirect="404"}{/if}
    <h2>{title}</h2>
    {body}
{/exp:channel:entries}

There are very few surprises here. I’m setting require_entry="yes" so that invalid uri_titles will trigger the 404, I’m disabling dynamic url_title detection and feeding the slug manually so that no matter how much nesting may be happening, EE will not get confused. And that's pretty much all there is to it.

Simple and Powerful

So as you can see, Construct is easy to use and a powerful tool for creating menus, pages, listing content and more. If you would like to give Construct a try, I’d love to let you kick the tires! Just get in touch with me. And if you do find that it is a good fit (and I think you will, of course), then you can head on over to devot:ee and purchase a site license. Happy developing!