Click. Connect. Learn.

Building Features for Install Profiles

When we originally set out to build a set of Features to support the install profile of our Knight Drupal Initiative work, we figured we would use Context to drive the creation of these features.

Contexts within the site

And this almost worked. Almost.

The Challenge

The challenge we faced in working with features involved sorting out the way Features manages dependencies. When you're setting up a feature, and especially when you're setting up a feature based on a context, the dependencies are automatically brought in and generated for you. So, most of the things that the context relies upon are automatically made a part of your feature (and Strongarm can generally grab the rest). This is great if every single one of your contexts is a completely standalone item. But, if anything in your context contains (for example) a UI element that connects over and exposes functionality that is contained within another context, then you will have two features with overlapping dependencies, and this creates conflicts.

The Goals

At the outset, we had four main goals in setting up our install profile, and the related features used within the profile.

  • Create a site that someone could install and start using;
  • For site maintainers/non-developer admins, make features easy and intuitive to use by creating a logical set of dependencies; aka, features should contain modular sets of functionality, and be as small and as lightweight as possible;
  • Retain the ability to use features to track changes in site config over time. One of the huge benefits of Features is the ability to track changes that you make in config; using the Diff module brings more of the awesomesauce, and here at FunnyMonkey we are serious about the awesomesauce;
  • Make features that are as reusable as possible.

Some Other Things That Didn't Work

Briefly, we contemplated shipping an install profile with one enormous feature. Technically, this works, and the install would have been simple, but the maintainability of this arrangement could potentially get complicated over time. Additionally, one feature that holds everything is not particularly reusable, and it doesn't reveal a clean site architecture through a set of clearly defined and managed dependencies. So, while this would have worked, and is a viable approach in some use cases, it didn't align with our goals.

The next option we considered was to use Features to define functionality, and document how to use the Context module to control block visibility and create a coherent UI to connect the various corners of the site. This would have achieved our goal of making reusable features, but it would have required non-technical users to interact with the Context UI in order to get the most from the site. Given that one of the goals of the entire Knight Drupal Initiative is lowering the barrier to entry for newer or novice users, this approach didn't seem viable either.

Hey! You Put Your Feature In My Module!

One of the many cool things about features is that it pushes config to code; to put it another way, it creates a module from config options. Like modules, features can declare dependencies. So, with this in mind, we exported some initial features that contained the overlapping dependencies as defined above. Then, we edited the exported features so that they contained just the components we wanted them to have - this process included removing the overlaps, and maintaining dependencies on both modules and other features. This allowed us to create some base features that contain the central keys to delivering the functionality - things like content types, imagecache settings, fields, etc. Then, we created some extras features that contain - for example - various views, flags, and other mechanisms used to organize information on the site. Finally, we created UI features that contain the contexts and the reactions that display specific blocks on specific pages.

As we worked on the initial site build - and the subsequent revisions of that build prior to building out features - we also paid careful attention to tags, and to maintaining some consistency with how we tagged and organized our views and contexts. This meta-organization of the building blocks within the site helped us as we got down to organizing the build into features and an install profile.

To get a sense of how this comes together, look at the screenshot below.

The UI feature

This page is pulled together via multiple different features, and they are tied together with a final UI feature.

Using this approach, we are able to meet all of our goals as defined above. Additionally, by separating the functionality from the UI that exposes that functionality, we provide more flexibility for people to use whatever means they want to display content. On our site, we use Context to control block visibility and other display settings. However, someone else could just as easily leave our UI features turned off and use Panels, and/or Drupal's core block visibility settings.

For another example, the home page of the site ships with a slide show, and content displayed within vertical tabs (the base theme we are using for this site is Hexagon; there is some real loveliness in there, but that's a topic for another post). This homepage is generated via a UI feature; if someone wants this UI, they can turn it on. Or, people can build out swappable home page UI features that build on the underlying components, and manage these changes via the features UI.

Homepage slideshow, delivered via a UI feature

The down side of this approach, of course, is that you can't use the Features UI to build your feature, and the process of defining dependencies manually requires some quality time with Strongarm and the variables table.

variables table exportables

Ideally, we could control or manually override dependencies using the Features UI, but patching Features to provide manual overrides of dependencies is no small task, and would likely come at the expense of usability.

By treating features like the modules they are, and by setting up dependencies between them, we can create small, reusable building blocks that retain the maintainability of features created via the standard Features UI. We are getting ready to release our install profile that incorporates this method of building and maintaining features; we still have some testing to do to make sure that we haven't missed or overlooked anything. And with that said, there are likely other ways of solving this, and we would love to hear about them.

Comments

Good writeup Bill, thanks.

Submitted on August 19th, 2010 by Lev

Good writeup Bill, thanks. We've been struggling with some Features/Context dependency conflicts as well and will definitely consider your approach. Haven't really found anything better, other than essentially duplicating Contexts, which isn't really viable.

Duplicating contexts

Submitted on August 19th, 2010 by Bill

We looked at duplicating contexts as well, and ruled it out for a couple reasons -

First, it muddied the site architecture.

Second, weighting blocks across contexts within the same region can be tricky, and is more difficult to track over time across deployments with re-used features.

At the end of the day, I don't think that what we have laid out is the end-all, be-all solution. But it works reasonably well, and retains much of the strength and flexibility of features, and creates some options for building atop it in the future.

It also retains some simplicity, and relates directly to a site's information architecture, which is definitely not true of the "one feature for everything" approach.

install profile

Submitted on August 21st, 2010 by MB

This is really great!
Do you have an estimate for when the voicebox install profile will be released? Is there any pre-release code that I could look at?

Doing final testing

Submitted on August 21st, 2010 by Bill

We're doing the final rounds of testing on the features build, and getting the details in place for the profile.

We'd like to get this out in the next couple weeks - and barring any horrid discoveries in testing, that should happen.

Fantastic. Thank you!

Submitted on August 21st, 2010 by MB

Fantastic. Thank you!

Great article. Thanks.

Submitted on August 23rd, 2010 by Drupal Themes Showcase

Great article. Thanks.

Any update ? A beta to test

Submitted on September 7th, 2010 by Jb Ingold

Any update ? A beta to test ?

It's coming

Submitted on September 10th, 2010 by Bill

We're looking to cut an alpha ASAP.

We're testing the install profile now.

Would you share how you work

Submitted on September 7th, 2010 by Jb Ingold

Would you share how you work on defining dependencies manually with Strongarm and the variables table.
http://funnymonkey.com/files/variables.jpg is much too difficult to read :)

Read through the variables table

Submitted on September 10th, 2010 by Bill

We literally read though the variables table to see what was stored there. Then, we enabled these specific variables within the feature. Basically, we used the Features UI to create the code and structure of the feature, then we edited the feature to remove the extraneous pieces/dependencies we didn't want or need.

Working without Features UI

Submitted on July 21st, 2011 by Boris Gordon

Are you using drush to run updates after manually adding/removing exportables from the info file or some other method?

I know this is an old post...

Submitted on July 15th, 2011 by Jon Betts

...and I'm not a developer so I could be missing a lot of not so subtle details, but there is a module available which may help to alleviate some of the issues with variables, conflicts and Strongarm.

http://drupal.org/project/features_plumber

Not sure if it was around when this article was posted.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd><b><quote><blockquote>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.