In our last installment (A New Paradigm for Overriding Drupal Features) I presented a new sandbox module that allowed you to override Field Settings from existing Features. As promised, I have now generalized that approach and have created a new sandbox module called Features Override 2 that applies this method to any feature.
The Problem (revisited)
As a quick review, the problem we are trying to solve is dealing with site-specific changes to existing Features. The Drupal Features module allows you to capture configuration data, such as entities, fields, variables, views, permissions, etc into code modules. These code modules can then be placed into version control, enabled and disabled, or used on a separate Drupal installation to provide the same feature.
In many cases, a Feature is developed for a generic site (or Distribution, such as OpenPublish) but needs to be slightly changed for the specific site you are developing. If you change the base distribution feature, it will be hard to upgrade to a newer version of that distribution feature. What you really want to do is just capture your changes to a site-specific "Override" feature and leave the original distribution untouched.
The New Solution
As we previously discussed, the existing Features Override module attempted to solve this problem but had many drawbacks. I have now taken the approach used to override Field Settings in the previous blog entry and applied that to all features types. As a new maintainer of the Features Override module, I intend this new sandbox to eventually become the 7-2.x release of Features Override.
Currently, the new Features Override 2 sandbox module requires a patch to the Features module: #1317054: Provide a universal hook for altering default components (although by the time you read this it may already be committed to the Features 1.x dev branch). The README.txt file included with the module documents the basic usage, but I'll review the highlights here.
(Install and enable sandbox module. Apply patch to Features as needed)
Unlike the old Features Override module, overrides are handled just like any normal feature in the new sandbox. First install (or create) some "base" features for your site. For example, "base_article" containing the fields, content-type, or whatever you want. Next, make some changes to that component using the normal Drupal UI (for example, change the display settings of a field, or change the name of a view).
Once you have made changes, the Features page will show your base feature as being "overridden". To capture these changes into a new feature, just click Create Feature and select the new "Feature Override" exportable from the Components drop-down list. A list of overridden components will be shown. Check the box next to the ones you want to save, then Download Feature to create the feature. Install this new Override feature module on your site and enable it. Now the original feature will be shown in it's "Default" state, but the changes you made will be applied by the new Override feature.
Even if you never use this module to create any overrides it adds a very useful new tab for inspecting changes. In the past, the "Review Overrides" tab could be used to look at the "diff" between the live database and the stored feature code. But it couldn't really show the full context of the change. For example, if the change was to a display in a view, it was difficult to determine what view was being changed.
The new "Overrides" tab added by the sandbox module shows the line-level detail of the current changes.
When you select a specific Override feature and click the new Overrides tab, a line-by-line list of the changes being overridden are displayed, along with a list of any new changes (not yet saved to a feature). Using the checkboxes you can completely control the line-level detail of which changes are saved to the Override feature and which are not.
Merging Features and Overrides
But wait, there's more! As detailed in the README file, you can also easily capture changes to an Override module, then apply those changes to another feature to "merge" the two features together. You can use this to make changes to the base feature without disturbing any override features. Or you can use this to update existing override features. The module provides a lot of flexibility for handling overridden features in a simple and intuitive way.
The magic behind the scenes that allows feature overrides to finally work is a new hook that allows external modules to modify the code being generated by Features exportables. The Features Override 2 sandbox uses this new hook to alter the code being written, adding it's own "alter hook" to the exportable code. This alter hook is used to apply the changes from the override feature to the base feature. In the past, no such hook was available to modify the Features exportable code at this level. The old Features Override module tried to work around this limitation, but it just wasn't possible. Some features (such as Views) require careful handling of the changes so they are made to the correct part of the exported array structure. For example, changes to "current display" are ignored in favor of changes to the "displays[display-name]" array.
The core "diff engine" used in the Features Override module was maintained (slightly modified) to determine the set of additions and deletions needed for the override. But these changes are now selected "on the fly" in the Create Feature screen rather than needed to write them to an intermediate database table and module file. The new sandbox doesn't use any additional database tables or files.
As was the case in the previous blog article, the key was applying the alter hooks to the actual exportable code and then treating overrides as any other normal feature. It's a simple concept that gets a bit tricky in the implementation. And the nested level of hooks and alters can be confusing the first time you look at it. But in the end the goal of providing easy-to-use overrides of existing features was met.
I encourage you to grab the sandbox module and Features patch and start playing with this. Post any problems in the sandbox issue queue. Once a few other people have tested this and we get any major issues resolved, then I'll go ahead and rename this to Features Override and post it as the 7-2.x release.
Thanks to the Drupal community members: nedjo, tim.plunkett, hefox, febbraro, e2thex, and jec006 for their help and support with this work.