LJ Archive

Tips for Writing Interoperable Drupal Distributions

Nedjo Rogers

Issue #888, April 2068

Want a crash course on how to write distributions for the Drupal platform? Get tips and expert advice from a longtime Drupal contributor and distribution developer. This article is packed with examples of problems and solutions for writing plugins that integrate cleanly with other distributions and make the best use of the Drupal toolkit.

First off, what is a distribution and why would you write one? Even more than many other content management system (CMS) frameworks, Drupal is up for pretty much any task you set it. You can think of Drupal like a potter's workshop. Want a vase or plate or mug or Halloween mask? You can find different kinds of clay on the shelf and the potter's wheel is right over there—go for it!

No problem—as long as you're a skilled potter. But, if it's your first time spinning clay, your plate's likely to come out wonky or crack when it hits the kiln.

What you want is a mold—something that'll give your plate just the right thickness and structure and shape. From there, sure, you can add your own details and dab on whatever glaze you like.

So if Drupal is like a potter's workshop, a distribution is the plaster mold—something you can use quickly to make an expertly designed product that fits a particular purpose.

There's a growing set of Drupal distributions available. Need to build a Web site for a conference, newspaper, government agency, on-line store or nonprofit organization? Rather than starting from scratch, you'll get a huge leg up if you start with the Conference Organizing Distribution (COD), OpenPublic, OpenPublish, Commerce Kickstart or Open Outreach.

Why Write a Distribution?

Maybe the first question should be, “what are some top reasons not to write a distribution?” Fame and fortune, for one—writing and maintaining a distro is hard work. It eventually may create some income in the form of contracts to extend existing functionality, but in the short term, you've got a ton of designing and coding to get through. As for fame, well, despite their merits, most distributions are notably modest in terms of both community profile and the number of actual installs.

Another good reason not to write a distribution is there already may be one (or more) in existence that fits the bill. True, those existing distributions may not do everything you want them to do exactly the way you would have done it. But, rather than doing it your own way, try pitching in and improving the existing projects. That's what open source is all about.

With the “why nots” out of the way, here are some key reasons to consider writing a distribution:

  • You're working with or for a large institution or network that will need many sites built along a common model.

  • You've got a sizable contract to build a Web site that fits a need not already covered by an existing distribution; you expect to be doing similar sites in the future, and you've got room in the project to generalize.

  • You're tired of doing the same site-building work over and over and want to focus on refinements.

  • You have a burning interest in and commitment to a particular sector or need and want to dig in over the long term.

  • You're committed to lowering the financial and technical barriers to technology access.

  • You're up for sharing your work, even though doing so will feel like pinning a badge to your chest labeled “complaints department”.

  • You're an open-source evangelist and deeply believe that everyone is best served when technology is freely available to all.

Key Concepts in Distribution Authoring

Before I go into details, here's a quick terminology overview:

  • A module is a plugin that extends Drupal's functionality. The core Drupal product, Drupal core, ships with a number of modules, and community contributors have written thousands more.

  • An install profile is a special kind of Drupal module that contains code to be used only when the site is first installed. Each distribution needs its own install profile.

  • A feature module or feature is a Drupal module produced with the Features module and containing exported configuration. A distribution typically is made up of multiple feature modules, each providing a distinct set of functionality.

  • The features approach is built on exportables—APIs that allow configuration from a Drupal site to be exported into code and reused on other sites.

  • An exportable configuration object, often called a feature component, is uniquely identified by a machine-readable name or (less frequently) a universally unique identifier (UUID).

  • One or more features and their supporting dependencies can be packaged together into an app that then can be installed with the Apps module. Many but not all Drupal distributions are being built using the apps model.

  • A base distribution is a distribution that's specifically written to provide commonly needed functionality for use in other distributions.

Distributions and Interoperability

Interoperability is the ability of different systems to work together. When we talk about interoperability and Drupal distributions we mean: how can we make sure the building blocks of different distributions fit together in consistent and understandable ways?

The Origin of Features

Much of the toolset for building distributions began with Open Atrium, a Drupal distribution that provides an advanced, team-based project management solution. Open Atrium devs took the time to build out a framework useful not just for their own product but for any distribution. The result was the Features module and a host of supporting add-ons.

Features quickly caught on with Drupal distribution developers. But in using it, they hit a number of snags.

Written on a one-off basis, each distribution had its own particular logic and structure. If you wanted to extend Open Atrium, you had to wade in and learn a lot about how that distribution was put together. And if you loved a particular piece of Open Atrium (say, its event calendar) and wanted to pull just that feature into your own site—well, good luck. Each feature was highly dependent on a whole set of assumptions and dependencies specific to the distribution.

Disentangling the Web looked like way more work than it was worth.

Kit

The Open Atrium devs quickly recognized these issues and dug into addressing them. The result was Kit, a specification for feature development. The Kit Feature Specification sets out guidelines that feature developers can follow to help ensure that different features integrate seamlessly together, no matter what distribution or purpose they were built for.

What's in Kit? The good news is mainly some pretty sensible and easy-to-follow naming conventions. Kit answers questions like: how should you name the paths to your main feature landing pages? By following Kit, you'll help ensure that, if your work is combined with other Kit-compliant features, the result won't be a mishmash of disparate configuration.

But, there's a lot that didn't make it into Kit. Here are some practical steps beyond Kit to help ensure that your work integrates seamlessly with other distributions.

Practical Steps in Building Interoperable Distributions

So far, I've been describing distribution building in pretty general terms. Now it's time to jump into some practical problems and real-world examples. If you're a developer relatively new to Drupal and some of this doesn't immediately make a lot of intrinsic sense, don't worry—it'll all be a lot clearer after you roll up your sleeves and dig in a bit. And if code blocks make your eyes swim, just pretend they aren't there—you can do a lot without touching a line of code.

Study Up on Existing Distributions

A first good step is to get up close and comfortable with some of the existing distributions. You might choose to build your features on the same model as those of an existing distribution or feature set, or even directly add to an existing feature set.

For example, the Debut feature set includes a Debut Feature Specification that extends and updates Kit, providing guidelines for a higher level of integration. The Debut project page on drupal.org also links to developer documentation on building new Debut features.

Other distributions like OpenPublish include detailed documentation on their Web sites about how to write features or apps that extend the distribution. Read up!

Consider Building Off a Base Distribution

Panopoly is the first distribution written specifically to be a base for other more-specific distributions. Panopoly tackles some key administrative, usability and design challenges that any distros will face. Building a distribution off Panopoly will save you having to solve all those problems yourself. It also will facilitate interoperability—at least with other Panopoly-based distros. The Panopoly project page on drupal.org includes documentation on how to base a distribution off of Panopoly.

Spark is a newer initiative that's also tackling usability and design challenges. It's not yet fully primed to be a base for building other distros, but discussions are underway, and it's a project to keep an eye on.

Consider Packaging Your Work as Apps

Not everyone is thrilled with the Apps model. Apps makes respected Drupal developer and evangelist Angie Byron (webchick) wince—judging by her comments in a recent thread on drupal.org. Her concerns, to paraphrase, were that the apps model clouds waters already muddied by features and modules and whatnot and also detracts from getting usability fixes into Drupal core and drupal.org where they can help everybody.

That said, well, Apps is being adopted as a tool in many Drupal 7 distributions and can help solve both development and usability issues.

For developers, Apps makes it easier to incorporate features from other distros into their own. For example, Apps is what facilitates using Panopoly as the basis of your own distribution.

For site builders, Apps gives a one-click solution for installing a feature—complete with all of its dependencies—on a site that wasn't based on a distribution.

Don't Re-invent the Feature

Even beyond building off a base distribution, you may be able just to take and use a lot of what you need from existing features. Before writing yet another event or blog or mapping or social-media feature, take a good look at what's already out there.

Seriously ask yourself: what would I need to do to use the existing feature directly in my distribution? Incorporating other people's work can free you up to focus on what you're really interested in. Think of it this way: the alternative is having to build and maintain everything yourself. Ouch!

And, what better path to compatibility could there be than actually using the same code?

Avoid Dependencies of One Feature on Another

No, really, avoid them—even “soft” dependencies. Unless your new feature is strictly enhancing an existing feature—like, say, an “Event registration” feature that enhances an existing “Event” feature—having dependencies on other features is trouble.

The key problem with dependencies is that they quickly produce incompatibilities between one set of features and another. The Features module won't let you enable two different features that have one or more identically named components of the same type. You can avoid dependencies through careful planning of how you'll handle the components that are needed in more than one feature.

Keep Generic Components Out of Features Exportables

Take, for example, a user role like “administrator” or “editor”. These are generic components—they're going to be needed in many different features, so that each feature can assign appropriate permissions for tasks like creating a particular type of content.

Instead of exporting user roles to features, consider having each of your feature modules create the general-use roles it needs at install time using regular Drupal APIs. Here's an example of how you might do this in a module's install file using hook_install(), which Drupal core calls when a module is first installed:

/**
 * Implements hook_install().
 *
 * Create "administrator" and "editor" user roles
 * if they don't already exist.
 */
function example_install() {
  $role_names = array('administrator', 'editor');
  foreach ($role_names as $name) {
    // If there isn't an existing role by this
    // name, create one.
    if (!$role = user_role_load_by_name($name)) {
      $role = new StdClass();
      $role->name = $name;
      user_role_save($role);
    }
  }
}

And, even before you write your own custom install hooks, have a look at the Apps compatible module, which provides built-in support for handling shared components like user roles and taxonomy vocabularies.

Enhance Other Features without Requiring Them

Say you're writing a feature that adds social-networking links to different content types. Great—but which content types should get the links?

If you hard-code content types from specific features into your social linking feature, you've introduced either soft or hard dependencies—and precluded other feature authors from using the same approach. Instead, you can build in a flexible responder that enables other features to register their content types for social linking easily. Here's an example.

Say you're building a feature that uses the Service links module, which stores configuration in a variable called “service_links_node_types”. In your feature, include implementations of two Drupal code hooks that allow you to respond when modules are enabled.

Here's some pseudo code; see the Debut Social feature for a full working example:

/**
 * Implements hook_modules_enabled().
 *
 * When a module is enabled, check to see if it is
 * a feature module with a node type designated for
 * social linking.
 */
function example_modules_enabled($modules) {
  $social_types = array();

  // Load info from feature modules and filter to
  // get only the ones that were just installed.
  $features = features_get_features();
  $features = array_intersect_key(
    $features,
    drupal_map_assoc($modules)
  );

  // Iterate through the features.
  foreach ($features as $feature) {
    // Look at the $feature->info array for node
    // types and an 'example_social_types' key.
    // Add any hits to the $social_types array.
  }
  if (!empty($social_types)) {
    // Merge into the existing variable's value so
    // as not to overwrite existing settings, using
    // variable_get(), array_merge(), and then
    // variable_set().
  }
}

/**
 * Implements hook_enable().
 *
 * When this module is enabled, process any social
 * linking designations for all currently enabled
 * modules.
 */
function example_enable() {
  example_modules_installed(module_list());
}

Now, to get its content type recognized for social linking, a feature will need only a single line in its .info file:

example_social_types[example_node_type] = example_node_type

Distinguish between “Hard” and “Soft” Configuration

Features provide reliable and consistent methods for capturing, reproducing and updating configuration. That's exactly what you want with what's sometimes called “hard” configuration: the settings and objects that are core to your feature or distribution and shouldn't change in different environments—stuff like content types and fields.

But there's also “soft” configuration—the stuff you might want to set initially but that site administrators can and should change on each site. Soft configuration includes things like the default theme used on the site. You want to set an initial default theme, but any site should be free to replace that with its own theme choice.

To set soft configuration, you can skip Features and instead use the regular Drupal API functions like theme_enable() and variable_set().

Choose Your Layout Solution and Stick with It

One of the biggest divides between one distribution and another is layout—how are blocks of content arranged on the page? Here, there are two main options used in distros, each with their myriad advocates.

The Context module enables finely tuned control over block placement on any site page or section into any region that the site's theme provides.

The Panels module enables even more options for selecting what content to place, but places content into its own custom layouts and regions instead of those provided by the site's theme.

If Context was the early champion, the balance may be shifting to Panels, which appears to be closer to the layout solutions that will go into Drupal 8 core. The Drupal Commons distro, for example, is switching from Context to Panels with its Drupal 7 upgrade.

One approach that's used is to pull layout and other user interface stuff into a separate feature. That way, site builders or distro authors can choose to use the core of a feature even if they choose a different layout solution. It's an interesting idea but one that adds some extra layers of complexity to the feature-building process.

Which to choose is going to be is up to you. But the layout model you decide on will have a big impact on how your feature or distro fits with the crowd.

Keep It Stable, Proven and Lean

There's a place for trying out the latest experimental alpha release of a cool module that someone posted last week, but it's probably not in your distro.

Stick wherever you can with the tried-and-true solutions that any skilled and experienced developer would choose for the purpose. Use recommended, stable releases rather than development versions. If you don't absolutely have to add a new dependency, don't. And avoid patches like the plague; they're bound to break if a site already has installed a non-patched version of the code.

Yes, there are exceptions to all of these rules. You might, on rare occasion, see no choice but to use a development version of a module or judiciously add a patch to address a critical bug. But every time you do, keep in mind you're cutting yourself off from other distros or features that might not have climbed out on the limb you're clinging to.

Save the Truly Specific for Your Install Profile

Here's an example from the Open Outreach distribution. Open Outreach is built for nonprofit organizations, but it's based on Debut features, which are written to be generic enough to be used on many different types of sites.

One such feature is for event handling. Debut Event includes an “Event type” vocabulary that allows content editors to classify events by category, with events of each category getting a different color on the event calendar.

But sticking terms that are specific to the nonprofit organization use case directly into Debut Event would break the whole idea of a generic feature. So instead we put just that bit of enhancement into the Open Outreach install profile, using one of the many code hooks that Drupal provides for responding to data transactions on the site. Here's code adapted from openoutreach.profile:


/**
 * Implements hook_modules_installed().
 *
 * Add custom taxonomy terms to the event_type
 * vocabulary if it is created.
 */
function example_entity_insert($entity, $type) {
  if ($type == 'taxonomy_vocabulary' &&
    $entity->machine_name == 'event_type') {
    $names = array(
      'Conference',
      'Meeting',
      'Workshop'
    );
    foreach ($names as $name) {
      $term = new StdClass();
      $term->name = $name;
      $term->vid = $entity->vid;
      $term->vocabulary_machine_name =
        $entity->machine_name;
      taxonomy_term_save($term);
    }
  }
}

When the event type vocabulary is created, Open Outreach responds by adding a few initial event types that nonprofit organizations typically might use. The Debut Event feature doesn't have to know or care.

Distributions and Interoperability in Drupal 8

Version 8 of Drupal core is being developed through a series of initiatives, each focused on a distinct area of development and headed up by leaders in the field.

Probably the most sweeping Drupal 8 changes for distributions are coming out of the configuration management initiative (CMI). This initiative has given Drupal native support for file-based configuration. A lot of the workarounds that are necessary to convince Drupal to recognize code-based configuration in Drupal 7 may soon be a thing of the past.

Big changes of course bring their share of challenges. There's a lot that still needs to settle out before it'll be clear just how features, apps and distributions will translate to Drupal 8. But with core Drupal devs solidly addressing configuration management in Drupal core, the future of Drupal distributions looks bright.

In the meantime, if you're venturing into the world of apps or features or distros, take the time to do it right. Draw on the best of what's out there. And, in turn, make your feature, app or distribution as interoperable as it can be. Doing so will help others make the most of your contribution and also help future-proof your work, whatever Drupal 8 brings.

Nedjo Rogers has been an active Drupal contributor since he posted his first module in 2003. He is the technical lead of the Open Outreach Drupal distribution for nonprofit organizations and a partner at Chocolate Lily Web Projects, chocolatelilyweb.ca. When not coding, he writes poetry and folk songs and tries to learn the accordion.

LJ Archive