introduction to drupal 7 installation profiles

26 Jan 2014

Last weekend I worked on a Drupal site and it was not fun =( mostly due to incomplete and inaccurate documentation. My goal was to create a distributable bundle for Spanish Ubuntu local teams. I already had a base theme and didn’t though it could be that hard, however what started as a simple task ended as a very long weekend. On this post I’ll try to document what issues I found and how they were worked out.

For the record, I must say I’m not a web developer, I find all the technologies related quite difficult to understand, I also didn’t have any previous experience with drupal internals.

Since the beginning I though any work should be automatized as much as possible, ideally an administrator would be able to download the bundle, throw it away on the file system, run the web installer and start creating content. The result happily is quite similar:

The resulting site would be translated to spanish, will enable a bunch of modules (l10n_update, admin_menu, smtp, ckeditor), an Ubuntu theme, search permissions and menu links.

For this to work, I used:

Installation profiles

Installation profiles are special modules who allow you to declare which modules, languages and themes to load by default, they can also be used to modify the installer to add or remove steps.

They must have a .info, .profile and .install files at:

I named my module ‘umx’

.info

This file contains name, description, drupal version and dependencies (modules enabled by default):

declaring only core modules as dependencies

The content of my /profiles/umx/umx.info file was:

name        = Ubuntu-mx
description = Install Ubuntu-mx modules, language (es) and configuration
version     = 0.1
core        = 7.x

dependencies[] = block
dependencies[] = color
dependencies[] = comment
dependencies[] = contextual
dependencies[] = dashboard
dependencies[] = help
dependencies[] = image
dependencies[] = list
dependencies[] = number
dependencies[] = options
dependencies[] = path
dependencies[] = taxonomy
dependencies[] = dblog
dependencies[] = shortcut
dependencies[] = overlay
dependencies[] = field_ui
dependencies[] = file
dependencies[] = rdf

dependencies[] = umx_conf

I found much more comfortable to declare only core modules as dependencies and carry on the rest of the configuration in a separate module (generated by the features module). This way I can keep using the same unmodified profile and alter the resulting site by regenerating the umx_conf module when necessary.

.profile

On the .profile and .install files can be written functions to override/define hooks, I used /profile/umx/umx.profile to configure the default language to spanish (as a result the language dialogue is skipped):

function umx_profile_details() {
    $details['language'] = "es";
    return $details;
}

The default language can also be configured in a feature module (on this example, umx_conf) however if doing so, the installation process itself will run in English, and if it’s double declared (here and in a feature module), the installation will return a sql duplication key error.

It’s not optimal, but it’s the best I could do. I located the es.po file at:

The localize.drupal.org server has po files with a drupal version prefix, if downloaded from there, you should rename them. Eg

    drupal-7.26.es.po -> es.po

.install

Here you can override install, update and uninstall functions. Since the feature modules doesn’t support theme settings (something who could be really useful) I declared the default theme on this file (/profile/umx/umx.install):

function umx_install() {
    db_update('system')
        ->fields(array('status' => 1))
        ->condition('type', 'theme')
        ->condition('name', 'umxtheme')
        ->execute();
    variable_set('theme_default', 'umxtheme');
}

According to the documentation, theme settings should be declared in profile_themes_enabled() with the theme_enable() function, however I was unable to make to work any of them.

Snippets which did NOT work (drupal 7.26):

function umx_themes_enabled() {
    //any code;
}
function umx_install_finished() {
    //any code;
}
function umx_update_N() {
    //any code;
}
function umx_install() {
    variable_set('theme_default','umxtheme');
}
function umx_install() {
    $list_themes = list_themes(TRUE);
    $major_version = (int)VERSION;

    $theme_default = isset($list_themes['umxtheme']) ? 'umxtheme' : 'garland';
    $admin_theme   = isset($list_themes['seven']) ? 'seven' : 'garland';

    variable_set('theme_default', $theme_default);
    theme_enable($theme_default);
    theme_disable(array('bartik'));

    if($affect_admin_theme){
        variable_set('admin_theme', $admin_theme);
    }

    if (module_exists('switchtheme')) {
        if (empty($_GET['theme']) || $_GET['theme'] !== $theme_default) {
            $query = array(
                'theme' => $theme_default
            ); 
            if($major_version < 7){
                $options = $query;
            }
            else{
                $options = array('query' => $query);
            }

            drupal_goto($_GET['q'], $options);
        }
    }
}
function umx_install() {
    $enable = array(
        'theme_default' => 'umxtheme',
        'admin_theme'   => 'seven',
    );
    theme_enable($enable);

    foreach ($enable as $var => $theme) {
        if (!is_numeric($var)) {
            variable_set($var, $theme);
        }
    }

    theme_disable(array('bartik'));
}

Drupal themes must be placed at:

One lesson I learned was that themes should be named clearly, at the beginning I declared the theme as ‘umx’ (for ubuntu-mx theme) and put it in /sites/all/themes/umx-theme/, later on while trying to configure the bundle with a default theme I got confused because the profile installation was named equal and I was not sure which one I was referring to. Name your themes with a unique string, (I finally decided to rename ‘umx’ to ‘umxtheme’).

Ignore the name field written in the .info file, and prefix your hook functions with the selected drupal theme (e,g, umxtheme_footer_text())

Feature modules

Feature modules are good for bundling configurations, permissions and dependencies on non default modules. I created umx_conf to iterate faster. Using feature modules you can modify drupal settings through a browser and then export the result to code from the feature menu.

Autogenerated features modules can contain:

Very few times you would need to modify them directly. In my experience (a weekend) the autogenerated files will sometimes contain mistakes, therefore I modified them slightly to feet my needs:

.info

On this file, dependencies are declared, the generator makes a quite clever job adding recursively all the dependencies your configurations depend on.

name        = umx conf
description = Common settings for the Ubuntu-mx local portal.
core        = 7.x
package     = Features
version     = 7.x-0.1
project     = umx_conf

dependencies[] = admin_menu
dependencies[] = admin_menu_toolbar
dependencies[] = ckeditor
dependencies[] = features
dependencies[] = l10n_update
dependencies[] = locale
dependencies[] = menu
dependencies[] = search
dependencies[] = smtp

features[ckeditor_profile][] = Advanced
features[ckeditor_profile][] = CKEditor Global Profile
features[ckeditor_profile][] = Full

features[features_api][]     = api:2
features[user_permission][]  = search content
features[user_permission][]  = use advanced search

features[menu_custom][] = main-menu
features[menu_links][]  = main-menu_portada:<front>
features[menu_links][]  = main-menu_foros:http://google.com
features[menu_links][]  = main-menu_preguntas:http://ubuntu.shapado.com
features[menu_links][]  = main-menu_wiki:https://wiki.ubuntu.com/UbuntuMxTeam
features[menu_links][]  = main-menu_chat:http://google.com
features[menu_links][]  = main-menu_descargar-ubuntu:http://google.com

.module

On this file, you can override hook functions, I leave it blank

.feature.submodule.inc

On these files per module configurations are saved, in my case, I had to modify /sites/all/modules/umx_conf/umx_conf.features.menu_links.inc because some links weren’t exported.

function umx_conf_menu_default_menu_links() {
  $menu_links = array();
  $menu_links['main-menu_portada:<front>'] = array(
    'menu_name' => 'main-menu',
    'link_path' => '<front>',
    'router_path' => '',
    'link_title' => 'Portada',
    'options' => array(
      'attributes' => array(
        'title' => '',
      ),
      'identifier' => 'main-menu_portada:<front>',
    ),
    'module' => 'menu',
    'hidden' => 0,
    'external' => 1,
    'has_children' => 0,
    'expanded' => 0,
    'weight' => 0,
    'customized' => 1,
  );

  ...

I had to adjust mostly the weight parameter and add missing links. Not difficult if you follow the syntax (even if you don’t know php).

Extra

If you had problems with the above description, a probably better and more up-to-date approach to profile Drupal installation is described at:

That’s it, I hope this information can save some time to someone, have fun!