Whenever working in a Drupal project you have to deal with a bunch of modules - core, contrib, custom and features. In the lifetime of a project you enable new ones and disable and uninstall modules you don't need anymore or replaced with a better one. But how do you track that, and how do you make sure that those modules that should not be active really are not?

I did not find any existing solution in the Drupal contrib modules that deals with this issue, so I wrote a small helper module: Master.

Master Yoda icon designed by Artua

Drupal module whitelists with master

You really should know and should define what modules have to be enabled to fulfill the project's functionality. Those modules that are meant to be active should be defined to be active in a way. With master you got a tool to whitelist those modules that have to be active. We call those modules master modules.

Configuration in settings.php

This is done by simply defining $conf['master_modules'] in your settings.php :

$conf['master_modules'] = array( 'myworld_news', 'myworld_contact', );

Module dependencies

Modules that are not in this whitelist may also be required by those modules. Therefore master also takes care of module dependencies. So if your module myworld_news needs node , features , strongarm and views enabled, you do not have to write those modules in your $conf['master_modules'] , you simply place those as dependencies in your myworld_news.info .

name = "MyWorld News" description = "News section for my world" core = "7.x" dependencies[] = features dependencies[] = node dependencies[] = strongarm dependencies[] = views

As you might know, Views has a dependency to the Chaos Tools Suite - it is not listed in myworld_news.info as the dependency is already declared in the Views module. Master is aware of that. Modules that are a direct or indirect dependency of a master module are required modules.

Redundant modules

Modules that are no master modules and have no dependency to one of those master modules are meant to be redundant. They shall not be active in the given site. If you want a redundant module to be meant to be active, you have to define it as master module (add it to settings.php configuration) or add it as a direct or indirect dependency of one of the master modules.

Using master

So how can you use that? After you defined your modules in your settings.php , you can use drush commands to check the status of the module whitelist and to ensure the intended module status.

Status

drush master-status [--status=…] [--pipe] [--sort] [--scope=…] [--skip-scope-validation]

With the master-status command you will get an output of the current state of your master modules and their dependencies. So you can make sure certain modules are active or disabled.

The command lists all modules in specific categories:

Master modules: those defined in $conf['master_modules']

Required modules: dependencies of the master modules

Missing modules: modules that are either master or required, but are not enabled

Redundant modules: modules that are not master and not required, but are enabled

Uninstall modules: modules that are disabled, not master and not required, but not completely uninstalled

By default the list is printed to the shell in a human readable table with additional information. You can limit the output to specific groups of modules by using --status : e.g. drush master-status --status="master,required" for only master modules and their dependencies. You can use the --pipe option to only return a whitespace delimited list of module names or use the --sort option to sort the modules by name.

The --scope option is for using different sets for different environments. This functionality is described in detail below. By default a scope has to be given, unless you use the --skip-scope-validation option.

Ensure modules

drush master-ensure-modules [--no-disable] [--no-uninstall] [--scope=…] [--skip-scope-validation]

This is the heart of Master. With drush master-ensure-modules you can make sure all modules are enabled, disabled and uninstalled as they are meant to be - in one single command. The command retrieves the information from your master modules configuration. It enables all master and required modules. Additionally it disables and uninstalls all redundant modules.

Especially when using Master the first time, you first should check the status of your modules using drush master-status , because maybe you forgot to put some modules in your configuration or module dependencies. Without any additional option, Master would disable and uninstall all modules that are not defined to be active.

Master will make sure of the order modules are enabled, disabled and uninstalled. Dependency will be enabled first and disabled last. When modules are disabled they are uninstalled before the next module will be disabled (and uninstalled). This way we make sure dependent modules may still be active - just for a reason it might be needed.

Master also makes sure already disabled modules are really uninstalled: in most cases, uninstalling a module means removing tables and variables from the database - this is managed by hook_uninstall() and/or hook_schema() .

By using the options --no-disable and --no-uninstall you can avoid disabling and uninstalling modules when using the master-ensure-modules command.

--scope and --skip-scope-validation fulfill the same purpose as in drush master-status .

Different modules on different environments

When you work with different staging environments or even different production environments you may have the need to enable an additional set of modules on these sites. For example you might have a language specific feature for the German website that is not part of the international one, or you simply want to make sure your developers have fieldui_ and devel enabled on the local development environment.

For that use case you can define scope specific additions to the global master module configuration. You will also do that by defining a variable in your settings.php . The variable is called $conf['master_modules:SCOPE'] , where SCOPE is the name of the scope (e.g. local or live-de ).

$conf['master_modules:local'] = array( 'devel', 'field_ui', 'stage_file_proxy', 'views_ui', ); $conf['master_modules:live-de'] = array( 'myworld_de', );

The listed modules will be merged with the modules defined in $conf['master_modules'] and therefore build a bigger set of master modules.

Note: Instead of listing additional separate modules it might be better to add a "dev-Feature", so you can provide additional configuration too (e.g. for devel).

When using the drush commands, you simply append a --scope=local to the command and the given set will be used:

drush master-status --scope=local drush master-ensure-modules --scope=local

Scope validation

As any accidental deactivation or uninstallation of a module might solve data loss, I implemented "scope validation" for any command. This means that a scope has to be defined for the command being executed.

You define a scope by simple setting an array (at least an empty one) for the scope specific module list:

$conf['master_modules:live'] = array();

When you do enter a scope that is not defined that way or enter no scope, the command will fail with an error The given scope "" is not valid! . This might happen if you accidentally type in drush master-ensure-modules --scope=locla or drush master-status . As said, the error is also triggered, when you enter no scope, so you do not accidently forget to enter your scope name.

You can disable scope-validation by using the --skip-scope-validation option in your drush commands. That way we make sure the user decides to do so.

Do not uninstall

In some cases it might be necessary to not completely uninstall the modules that shall be disabled. That might be the case when you only want to temporarily disable a module and leave content intact for the moment - e.g. you disabled the forum or just want to disable the migrate module for now but keep the migrate mapping tables.

For that case, you can fill your master_uninstall_blacklist in the settings.php with modules that shall not be uninstalled by Master when they are about to be.

$conf['master_uninstall_blacklist'] = array( 'myworld_module', ); $conf['master_uninstall_blacklist:live] = array( 'migrate', );

As you see, there is also a scope specific setting, using master_uninstall_blacklist:SCOPE .

Note: This functionality is no replacement for doing a database backups before changing your code. Be sure to make a backup before you use Master!

How we use it.

We at undpaul simply put the drush commands in one of our update.sh files:

040_modules.sh

################################################################################ # Enable our modules and features. ################################################################################ # We use the master module for controlling the module status. # @see http://drupal.org/project/master drush $DRUSH_PARAMS --yes pm-enable master # And finally execute master. drush $DRUSH_PARAMS --yes master-ensure-modules --scope=$STAGE_INDICATOR

This is located right before reverting features, so we do not revert old features.

What it (currently) does not do.

Currently Master does not have any UI for the admin backend. Everyone is welcome to contribute to a master_ui, but in our workflow with automated drush deployment scripts, we simply did not need it by now.

Master does not manage any dependencies by downloading missing components from drupal.org or any third party site. I guess this would be some part drush make might be good at.

Additionally I had in mind to provide a command that will delete all of the module projects, that are not in use in the current site, so we do not clutter up our modules directory - especially when we use a VCS.

I'm curious about your comments and maybe some contributions. The project is available on d.o. You're invited to p