Part 1 of “Getting to know Page Builder” discussed the conceptual approach that Page Builder takes to arranging content. In Part 2, below, I discuss how we built this in Drupal. This post is primarily useful for developers using the Drupal Kit who may want to do things like create their own Page Builder templates.
Page Builder is a custom Drupal module for assembling page content using an intuitive drag-and-drop interface. To first get an idea of the end-product in action, watch this video demo.
The following explains how it all works within Drupal.
The building blocks: pre-fab widgets.
The UT homepage redesign, designed and developed by Springbox, provided a variety of pre-themed page elements, such as hero images, Twitter feeds, contact info blocks, and social media links, as well as the basis for placing those items on a page. But to make this model work more broadly for UT developers, ITS needed to rewrite much of the code. Our first question was: how should these elements be defined in Drupal? As fields? Entities? Content types?
The answer: a combination of fields and entities. For page elements we felt would be re-used across multiple pages (Twitter feeds, contact info blocks) we defined custom entities (which are similar in function to content types, but allow you to move beyond node/CCK logic). As entities, Twitter feeds or contact blocks could be added, through an entity reference field, on any content type.
For elements that felt like unique page content (e.g., hero image, promo units, quick links), we created custom compound fields (which are similar to field collections but more customizable and efficient).
Example: take the “Promo Unit” custom compound field (see the code at
profiles/utexas/modules/custom/utexas_page_builder/utexas_promo_field). This single “field” actually consists of a headline, image, image display setting, copy text, and call-to-action link. Since Drupal thinks of it as a single field, the number of allowed instances can be set to any number in content type configuration. This means you can create a list of promo units (or any other custom compound field) on a single page, magnifying its use.
All in all, we built 14 unique page elements. Since they are a mix of entities and custom compound fields, from here on out, we’ll just refer to these building blocks as “widgets.” Each of these widgets has an individual tpl.php file that governs its theming.
Armed with widgets, we could use the Drupal site building interface to add these pieces to content types. The UT Drupal Kit comes with two content types, “Standard Page” and “Landing Page,” but the widgets are available to the entire system, so individual developers can assemble their own content types from our pre-fabbed pieces:
Many layouts are better than one.
Drupal’s theme layer is a matryoshka doll of tpl.php files. At its most simple, the active theme’s
html.tpl.php governs the outer layer, with
page.tpl.php nested within, and
node.tpl.php inside that. Drupal uses the principle of namespaced template inheritance: if you want to customize the layout of a given content type, you’ll create a
node--[content_type].tpl.php, which then takes precedence over the generic
But this is limiting: you get a single layout for all nodes of a content type.
Drupal hook_preprocess_HOOK to the rescue!
This function allows you to redefine which tpl.php should be used by manipulating the theme “suggestions.” Armed with this, we went about creating another custom entity: Templates.
Each Template entity has a name, description, .svg thumbnail, and a user-defined list of which widgets should be “available” on that template:
A tpl.php machine name is auto-generated from the template name (e.g., “Hero Image & Sidebars” becomes
page--hero-image-sidebars.tpl.php). By simply creating the appropriately named tpl.php file, a developer can now trigger Drupal to use that tpl.php file if the template has been selected by the user:
from function utexas_templates_preprocess_page()
// Use the loaded entity to attach a theme hook suggestion.
$template_name = str_replace('.tpl.php', '', str_replace('-', '_', $template[$term]->template_filename));
// Attach new theme hook suggestions.
$old_suggestions = $variables['theme_hook_suggestions'];
$new_suggestions = array($template_name);
$variables['theme_hook_suggestions'] = array_merge($new_suggestions, $old_suggestions);
We pre-fabbed 11 templates (8 for Standard Pages, 3 for Landing Pages) and corresponding tpl.php files. The only thing left was to allow a site builder to choose which template they wanted to use on a given page.
Since we had created templates as Drupal entities, this was a snap: add an entity reference field to Templates on the content type. A little lagniappe to convert the plain ol’ select list into a clickable image selector, and we had this:
The user’s choice on node add/edit form is stored, and when it comes time for Drupal to render the page, it retrieves the appropriate tpl.php file. This was a vast improvement: instead of only having one tpl.php file to display a page, users have many to choose from.
But a problem remained: layouts typically just output fields wherever they are defined to be outputted in the tpl.php file. Hero image is defined to show up in one
div, Twitter widget in another. How to contextually allow the site builder to manipulate widget positions?
What is optimally intuitive? Drag-and-drop.
Enter Drupal’s Context module (and its Context UI sub-module). The Context module stores conditional settings on a per-instance basis. We simply added a “context” field to the content types we wanted, which would store information about which page region each of the fields should display.
We then used the Field as Block module to define these fields as blocks, and let our context logic make any blocks (including Views blocks and Menu Blocks) available page elements.
Leveraging Context UI, along with yet another contributed module, Context Themes Reactions (which makes theme regions available to the system), we had an interface for the user to place widgets within any available region on the selected layout:
Putting it all together, Page Builder can be used on any new or existing content type with this simple recipe:
- Add predefined custom compound fields and entities via their own field widgets.
- Add predefined page layout templates (or make your own), via an entity reference field.
- Add the “context” field for storing widgets’ region settings on a per page basis.
utexas_page_builder module, and its sub-module,
utexas_templates, then takes care of making fields available on templates, and field regions configuable by context.
For full documentation, and a link to download the UT Drupal Kit, go to https://wikis.utexas.edu/display/UTDK/UT+Drupal+Kit