Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Apr 23 2021
Apr 23

Aten loves libraries. We’ve built a range of software solutions for libraries all over the country, including the John D. Rockefeller Jr. Library in Williamsburg, VA, the Denver Public Library in our own Denver county, the Richland Library in South Carolina, Nashville Public Library in Tennessee and the Reaching Across Illinois Library System (“RAILS”). It’s remarkable just how many commonalities libraries share when it comes to the features, tools, and considerations their websites need to better serve their users.

Some of those similarities are a no-contest justification for building common, configurable library solutions — solutions like Intercept: a reimagined, generalized approach to library event management, room and equipment reservation, and customer tracking. We co-developed Intercept for Drupal with Richland Library, reused it with L2 / RAILS and actively maintain it in the hopes that it goes on serving libraries for years to come. But for all of the similarities we see between libraries and their digital needs, there are some key differences, too.

Complex permissions needs

Library Directory and Learning Calendar (“L2”) — a project of RAILS — had unique permissions needs. Their staff needed custom view, edit, and delete permissions to website content managed at different organizational levels of the library system. Those organizational levels, structured hierarchically from Illinois State Library, to Regional Library System, through Catalog Consortium, Agency and finally Location, needed associated cascading permissions — i.e., permissions granted at a higher organizational level should cascade to associated content in lower organizational levels. An administrator for an Illinois State Library piece of content, for example, should be able to exercise their administrative permissions on Regional, Consortium, Agency, and Location content associated with that State Library content. An administrator for an Agency would have administrative permissions for that Agency’s Locations.

Granular role based Drupal permissions wouldn’t cut it. That’s because with standard Drupal permissions, each user role can be assigned view, edit, and delete permissions to each content type (say any Regional Library System Page), but we needed to assign those permissions to the appropriate instances of a content type — like the specific Regional Library System Page that belongs to the west-central region, for example.

There are plenty of contributed modules that start down the right path towards (for lack of a better term) cascading permissions by content affiliation, but they wouldn’t have gotten us all the way there. Both the Group and Permissions by term modules, for example, can be incredibly useful in similar situations. In this case, given the features and functionality contributed modules would introduce that we don’t need, plus the level of modification necessary to achieve our goals, we decided on a lightweight, custom solution.

Role and affiliation based custom permissions for L2

Permissions for L2 staff are established using a custom affiliation entity, which stores data about the role a particular user has in relation to a specific piece of content. The custom affiliation entity references a user, a role (a taxonomy term like Admin, Manager, or Staff, for example), and a specific piece of content (a node). A variety of other fields are established in the same affiliation entity in order to store additional metadata about the relationship like contact information, job title, job description, or other details.

Custom affiliation entity field configuration screen, Drupal 8 Affiliation, Role / Access Group, and User fields establish a permission relationship. Metadata fields (blurred here) can provide some arbitrary data about the relationship.

The custom affiliation entities are organized into their own bundles, one for each of the hierarchically structured organizational levels previously described: Illinois State Library, Regional Library System, Catalog Consortium, Agency, and finally Location. This way each individual type of affiliation can contain the metadata fields appropriate for its specific organizational level. Finally, there is an arbitrary Group affiliation which affiliates a user with a piece of content without granting the cascading permissions that accompany standard affiliations.

Custom entity affiliation entity types, Drupal 8 Each organizational level is represented by its own custom affiliation entity type.

The content items (read nodes) whose permissions we’re controlling are organized along the same organizational levels: Illinois State Library, Regional Library System, etc. Each of these content types uses a unique entity reference field to establish a parent / child relationship along the organizational levels. Locations are associated with Agencies, Catalog Consortia, Regional Library Systems or directly with the Illinois State Library; Agencies are associated with other Agencies and / or Regional Library Systems; Catalog Consortia are associated with Regional Library Systems; and Regional Library Systems with the single parent Illinois State Library. It’s a complicated web!

Cascading permissions diagram Permissions granted on content at any one organizational level cascade to associated content in the lower levels.

Once the content for L2 was developed and properly structured with the appropriate parent / child relationships, granting a user specific view, edit, and delete permissions for a particular region of the structured content tree was simple. Simply create an affiliation that assigns the user a role in relation to content at a specific level of the organization, and voila! — the user gains access to that content and all of its children at each of the lower levels.

The permission grid: Tying it all together

One last element ties our whole permissions system together: a robust permissions map that associates view, edit, and delete permissions per custom defined Role / Access Group in relation to various entities. Unlike the unwieldy Drupal permissions grid that assigns roles to broadly defined permissions with the help of about a million radio buttons, our definitions can be static (think code, not GUI) and only have to deal with view, edit, and delete permissions for entities or entity fields. Each Role / Access Group has its view, edit and delete permissions defined per bundle or per specific bundle / field combination, resulting in very cleancut — and extremely granular — permission control.

[
  {
    "entity": "node",                         // Entity type we're granting access to
    "field": "",                              // Field for this entity, left blank we're defining with entity level access
    "affiliation bundle": "location",         // Affiliation entity that controls access, in this case the location affiliation type
    "target bundle": "location",              // Entity bundle we're granting access to, in this case a location node
    "role_access_group": "location_manager",  // Custom defined Role / Access Group that grants this permission
    "edit": 1,                                // Edit permission
    "view": "",                               // View permission
    "delete": ""                              // Delete permission
  }
]

Our “permissions grid” is made up of about 500 similar declarations in a single JSON file, which range through a variety of Roles / Access Groups, a couple of entity types, and tons of bundle / field combinations for some of the more complex field level permissions.

Individual permissions grants are then handled through hook_ENTITY_TYPE_access() and hook_entity_field_access(), which use a a custom Service to load all of the requesting user’s affiliation entities, determine their role in relation to the content (node) in question, then find that role’s particular permissions using our custom JSON permissions map. Here’s an example for node access.

/**
 * Determines if the operation is allowed for a node.
 *
 * @param object $relationship
 *   The relationship object.
 * @param string $operation
 *   The operation being attempted.
 * @param Drupal\node\Entity\Node $node
 *   The node on which the operation is being attempted.
 *
 * @return bool
 *   True if the operation is allowed.
 */
public function nodePermission(object $relationship, $operation, Node $node) {
  // Create an array of data from l2_access_permissions.json, each element
  // of which is an array with elements matching $relationship object
  // properties.
  $module_path = drupal_get_path('module', 'l2_access');
  $matrix = json_decode(file_get_contents($module_path . '/l2_access_permissions.json'),
    TRUE);
 
  // Create an array matching the structure of $matrix elements to see if
  // it matches any $matrix elements (which would mean that there might be
  // a permission that allows this $operation.)
  $relationship_array = [
    'entity' => 'node',
    'field' => '',
    'affiliation bundle' => $relationship->affiliation_bundle,
    'target bundle' => $relationship->target_bundle,
    'role_access_group' => $relationship->role_access_group,
  ];
 
  // Set the $relationship_array's 'edit' and 'view' elements based on the
  // $operation's value.
  $operation = ($operation == 'update') ? 'edit' : $operation;
  $operations = ['view', 'edit', 'delete'];
  foreach ($operations as $op) {
    $relationship_array[$op] = ($op == $operation) ? 1 : "";
  }
 
  // Handy here that array_search() can test whether an array is an element
  // in another array. Here: is $relationship_array an element of $matrix?
  $match = array_search($relationship_array, $matrix);
  if (!$match) {
    return FALSE;
  }
 
  // Found a match. Does it allow access?
  switch ($operation) {
    case 'edit':
      if ($matrix[$match]['edit'] == 1) {
        return TRUE;
      }
      break;
 
    case 'update':
      if ($matrix[$match]['edit'] == 1) {
        return TRUE;
      }
      break;
 
    case 'view':
      if ($matrix[$match]['view'] == 1) {
        return TRUE;
      }
      break;
  }
 
  // If we're here, this $relationship doesn't provide $operation access.
  return FALSE;
}

The end result is powerful and flexible. Our JSON permissions map tells us which Roles / Access Groups have which permissions per entity or entity / field combination. The custom affiliation entities grant users a specific Role / Access Group in relation to a specific piece of content, and that content’s entity references to other entities allow the permissions to cascade to entities in lower organizational levels.

That may sound like a lot, but it’s surprisingly simple. The solution boils down to a handful of entity access hook implementations that use a custom service to lookup the current user’s permissions by affiliation via a JSON permissions map. The total footprint sits at around 1000 lines of code — not counting the permissions map itself — and flexibly manages thousands of users’ permissions across thousands of complex, hierarchical content relationships down to the field level. Pretty neat.

Apr 15 2021
Apr 15

Content authoring on the web has evolved. Compelling web content uses a variety of multimedia elements to engage users, tell stories, build brands, and share new ideas. Images, video clips, slideshows, static or parallax backgrounds, block quotes and text pullouts — more than ever, content creators need a tool that embraces an evolving medium and keeps pace with the author’s creativity. We believe Layout Paragraphs is that tool. But first, let’s all agree that the body field is dead.

What is the body field

Web editors and content authors who have been around over the last decade of digital media are intimately familiar with the body field. Many popular content management systems (looking at you, Drupal and Wordpress) feature a standard “post” or “content” type — like page, article, or blog post — right out of the box. These content types often have a couple of fields to fill out before you can publish your post, things like title, tags, friendly URL / slug, and of course body.

The body field, traditionally, is where all your content goes. In the early days of blogging and web publishing that was likely to be all or mostly text, but with time came images, videos, and eventually an array of other multimedia elements. And the body field adapted. Tokens became standard for plenty of web editors — cryptic chunks of text like [[nid:376 align:right]] that would be auto-magically replaced with other elements once you click Publish. WYSIWYG editors (What You See Is What You Get) started shipping with Insert image and Insert video buttons, and began including a variety of tools for encapsulating, aligning, and positioning lengths of text or various media elements. And while these accommodations started to connect content creators with the boundless possibilities of multimedia authoring, they were (and are) clumsy and unpredictable.

Structured content: Reduce, reuse, recycle

One major problem with all of these innovations is that once you click Publish, all that complex content still ends up in the body field — that is, saved into your database as one giant clump of complicated text, tokens, style codes, etc. If you’d like to publish another page with a similar look & feel, get ready to re-inject all of your tokens or go through the same WYSIWYG click-a-thon to reestablish your styles and layout. Thinking of producing a list of all the images used in your posts? Good luck! With all content mushed together into a single field, your site doesn’t “know” the difference between a paragraph of text, an image, or a video.

A structured approach to content organizes each individual element — a pane of text, an image, slideshow, video, or a “donate now” banner — as its own self-contained entity which, ideally, could then be placed within flexibly configured regions on a page. Creating other, similar pages then becomes as simple as swapping out individual elements or shifting them around between regions of a page. And libraries of reusable content elements become the norm — so that you’re now picking your images, videos, donate banners and more from lists of existing content or existing content styles, instead of trying to hunt down how you did something similar the last time.

Structuring your content into individual, reusable elements drives a lot of collateral benefits:

  • Ease of content creation. Authoring with a consistent set of components lets you focus more on content, hierarchy, and intent — instead of trying to remember how you got a video in there at all the last time.
  • Styling and restyling. Using discrete elements of content means that your HTML markup and CSS remains consistent across all of your various pages. When the time comes to update the look & feel of your website, changes made on one article page will “just work” on the rest of your articles and all of their elements.
  • Content migration. The day might arrive when you consider migrating your content to a newer platform or different software. Structured content makes that a snap: Each image, video, slideshow, or paragraph of text gets individually ported to its new home on the new platform. On the contrary, migrating a mess of markup, tokens, and style codes stored in a single body field means writing complex, custom code to recognize those items and deal with them appropriately — not a simple task.
  • Syndication. Want to feature your content in a collection via RSS or deploy it to an app with XML? With structured content apps and other websites can consume just the elements they want of your content (maybe the first paragraph, a title, an image, or a link) then display those elements appropriately according to their own styles — rather than just grabbing a few hundred lines of a single, messy body field and making do.
  • Libraries, lists, and cross-promotion. Want to see all the slideshows that appear in articles with a specific tag? Maybe a list of pull-quotes from your most recent blog posts? Structured content creates a world of opportunity around libraries, lists and cross-promotions.

Structuring content facilitates reuse, and it positions your content to go on living in a variety of platforms or a variety of presentations — long after you first click the Publish button.

The two faces of web content

Structuring content is an essential part of beautiful, intuitive digital authoring, but it’s only one side of the coin. The other side is putting an end to the two faces of your web content. Excepting some of the advances of modern WYSIWYG editors, web content has always had two very different faces: View and Edit.

The way content is edited and the way it’s presented are often strikingly different. Especially if you’re still holding on to “one giant body field” innovations like custom tokens, BBCode, Markdown, or short codes, it can take a handful of Preview to Edit to Preview roundtrips to get your content exactly how you want it. Even if you’ve made the move to structured content, a majority of web-based editors are marked by a dramatic difference between the “back end” and the “front end” — just one more digital convention getting between inspired authors and publishing beautiful content easily.

The king is dead, long live the king

The body field is dead. It’s taken center stage in publishing digital content for long enough. Plenty of web products (Medium and Notion, to name a couple) have driven nails in that coffin for publishers creating content on those specific platforms. But what about content creators and web editors working within their organizations’ websites, in custom web applications where content authoring is just one of several important features? What about you, and your organization’s website? What’s next for your web application?

For Drupal websites, we think Layout Paragraphs is what’s next. It was first released to the Drupal community nearly a year ago, and has been the beneficiary of ambitious development ever since.

The Layout Paragraphs module makes it dead simple for authors to create robust, multimedia content from a library of available components. It offers intuitive drag-and-drop controls for managing content flow and layout, and with appropriate styling can bring the two faces of web content — View and Edit — closer than ever before. We built Layout Paragraphs to embrace the future of multimedia content authoring and to solve the problems we watch clients work through every day.

You can watch a short, two-minute demo of Layout Paragraphs here, or follow the instructions in that post to see it in action for yourself.

Mar 11 2021
Mar 11

Upgrading from Drupal 7 to Drupal 8 is a major project (a full rebuild in most cases) that can rival or even top the original cost of application development. Upgrading from Drupal 8 to 9 and beyond, in comparison, requires just a tiny fraction of that effort. Here’s a look at why — and why you should take the leap from Drupal 7 to Drupal 8 and 9.

The upgrade from Drupal 7 to Drupal 8 was a major theme for plenty of our clients over the last few years. Drupal 8 — and versions beyond — boast a wide range of benefits that stem from a more modern architecture, but one of the biggest wins in my mind is the advent of semantic versioning (for Drupal) and a renewed commitment to making Drupal upgrades easy forever. Moving to Drupal 8 could be your last major upgrade.

The ghost of Drupal past

The move from Drupal 7 to Drupal 8 isn’t easy. While it’s often still touted as an “upgrade process” the reality is that it boils down to a complete rewrite of the codebase and a content migration. We’ve worked on more than a handful of Drupal 7 to Drupal 8 upgrades in the last years, and the majority constitute a “six months or more” sized project.

One reason for that is Drupal’s new Symfony reliant architecture that leans far closer to the tenets of object oriented programming (OOP) versus the more procedural approach used in previous Drupal versions — a significant technical change which in most cases means Drupal 7 code simply won’t work in Drupal 8. Add to that a completely revamped templating system and you get a similar obstacle on the front-end: Drupal 7 templates & templating systems won’t work in Drupal 8. Those major changes along with a potentially long list of complications with Drupal 8 data migrations is likely to land your Drupal 7 to Drupal 8 upgrade squarely in “rebuild the application” territory.

The good news is that it’s the last time.

Drupal 8 and beyond: Your last major upgrade

Drupal 8, 9 and beyond promise a continued investment in modern architecture, incremental major feature releases, greater stability and sustainability, and a much larger network of invested developers — among lots of other perks. Perhaps the best news to come along with the new Drupal architecture is that large-scale, overhaul-style major version upgrades will be a thing of the past. And we’re beginning to see that now as we begin moving Drupal 8 sites to Drupal 9.

Clearing the road ahead: A commitment to easy upgrade paths

With Drupal 8 comes the commitment to semantic versioning. Semantic versioning isn’t just a naming convention for software versions, it’s a descriptive norm that guides how new versions of code should be written and establishes strict backwards compatibility requirements. The upshot for end users is significant but manageable incremental changes between major versions (say 8.x and 9.x) which aim towards seamless major version upgrades. Major versions can no longer make dramatic jumps (like Drupal 8’s move to OOP or the Twig templating system) which necessitate major code rewrites, but instead make small, reasonable changes between minor versions.

With the new approach to versioning developers get a variety of automated tools that ease the shift between minor and major versions. Deprecation checking means developers get notices in their code editors when parts of the existing codebase have been marked as deprecated, i.e., queued for change or removal in an upcoming major version. Automated tools like Upgrade Status or Drupal Rector and its Drupal front-end Upgrade Rector can provide detailed upgrade status information and even automatically update code between major versions. All in all, upgrade paths are looking easier than ever.

A wider foundation: Sustainability through community (and Symfony)

Building Drupal on top of Symfony means even further specialization on the Drupal end. The new Drupal versions use Symfony for a host of standard web application tasks — things like form validation, data serialization, data storage and retrieval, content translation, templating, easy and human-readable configuration management with YAML — the list goes on. Building on top of Symfony lets Drupal developers build closer to the consumer level, i.e., build the media libraries, authoring experiences, layout managers, etc that really matter to users.

Drupal’s reliance on Symfony — a robust and mature web application framework with more than 600,000 registered developers — also translates to greater sustainability through a larger active community. Besides adding Symfony’s 600K developers to Drupal’s roughly 1 million, the move to a modern architecture and more collaborative versioning system makes Drupal more attractive to developers on the leading edge of their disciplines. That fact alone stands to benefit the Drupal community through new, top-tier talent driving further innovation.

Regular new stuff: Incremental feature releases

With Drupal’s new versioning cycle comes another benefit: major new features in minor version core releases. As Drupal 8 churns through minor releases (the second place in the three place version number, e.g. “y” in x.y.z) it continues to add brand new features to core. Unlike previous major Drupal versions, these aren’t just bug fixes and security upgrades. Game changing modules like JSON API, Media, and Layout Builder were all added to Drupal 8 in minor version releases — and more are in the works.

What does this mean for end users? A greater commitment to standardized features that meet primary needs (like media and layout management, for example) and a bigger pool of developers making sure those features are updated, stable, and compatible.

I just finished one of my first Drupal 8/9 upgrades yesterday for a small searchable database of computer science tips & tricks run by a college out of Claremont, California. Admittedly it was a pretty simple site, but the upgrade took me just two hours. Wow.

Aten will be working on a variety of Drupal 8 to Drupal 9 upgrades in the coming months, but the landscape is almost unrecognizable compared to our Drupal 7 to Drupal 8 efforts. For us, for the rest of the Drupal world, and for our clients, that is real good news.

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web