Last updated February 27, 2014. Created on November 19, 2008.
Edited by Christopher James Francis Rodgers, KrisBulman, xjm, roderik. Log in to edit this page.

Zenophile Module

Zenophile is a tiny module which allows themers to very easily create Zen subthemes without all the tedious file copying and find-and-replacing required when creating subthemes by hand. With this module, subthemes can be created in a fraction of the time just by entering information into a single-page form and clicking “Submit.”

Drush

zen.drush.inc for zen-6.x-2.x

With Drush 7.x-4.4 this script needs named as zen.drush.inc to be placed in your ~/.drush/ folder.

Run the command from the themes folder that zen resides in, example:
/sites/all/themes/

It supports the following commands:

drush zen "My theme name" my_theme

zen.drush.inc

<?php
/**
 * @file
 * Contains functions only needed for drush integration.
 */

/**
 * Implementation of hook_drush_command().
 */
function zen_drush_command() {
  $items = array();

  $items['zen'] = array(
    'description' => 'Create a theme using zen.',
    'arguments' => array(
      'name'         => 'A name for your theme.',
      'machine_name' => '[optional] A machine-readable name for your theme.',
      'description'  => 'A description of your theme.',
    ),
    'options' => array(
      'name'         => 'A name for your theme.',
      'machine-name' => '[a-z, 0-9] A machine-readable name for your theme.',
      'description'  => 'A description of your theme.',
      'without-rtl'  => 'Remove all RTL stylesheets.',
      // @TODO: Add these options:
      // 'layout'       => '[fixed,fluid,960gs] Choose the page layout method.',
    ),
    'examples' => array(
      'drush zen "My theme name"' => 'Create a sub-theme, using the default options.',
      'drush zen "My theme name" my_theme' => 'Create a sub-theme with a specific machine name.',
    ),
  );

  return $items;
}

/**
 * Create a zen sub-theme using the starter kit.
 */
function drush_zen($name = NULL, $machine_name = NULL) {
  // Determine the theme name.
  if (!isset($name)) {
    $name = drush_get_option('name');
  }

  // Determine the machine name.
  if (!isset($machine_name)) {
    $machine_name = drush_get_option('machine-name');
  }
  if (!$machine_name) {
    $machine_name = $name;
  }
  $machine_name = str_replace(' ', '_', strtolower($machine_name));
  $search = array(
    '/[^a-z0-9_]/', // Remove characters not valid in function names.
    '/^[^a-z]+/',   // Functions must begin with an alpha character.
  );
  $machine_name = preg_replace($search, '', $machine_name);

  // Determine the path to the new subtheme by finding the path to zen.
  $zen_path = drush_locate_root() . '/' . drupal_get_path('theme', 'zen');
  $subtheme_path = explode('/', $zen_path);
  array_pop($subtheme_path);
  $subtheme_path = implode('/', $subtheme_path) . '/' . str_replace('_', '-', $machine_name);

  // Make a fresh copy of the original starter kit.
  drush_op('zen_copy', $zen_path . '/STARTERKIT', $subtheme_path);

  // Replace all occurrences of 'STARTERKIT' with the machine name of our sub theme.
  drush_op('zen_file_str_replace', $subtheme_path . '/STARTERKIT.info.txt', 'STARTERKIT', $machine_name);

  // Rename the .info file.
  $subtheme_info_file = $subtheme_path . '/' . $machine_name . '.info';
  drush_op('rename', $subtheme_path . '/STARTERKIT.info.txt', $subtheme_info_file);

  // Alter the contents of the .info file based on the command options.
  $alterations = array(
    '= Zen Sub-theme Starter Kit' => '= ' . $name,
  );
  if ($description = drush_get_option('description')) {
    $alterations['Read the <a href="http://drupal.org/node/629510">online docs</a> or the included README.txt on how to create a Zen sub-theme.'] = $description;
  }
  drush_op('zen_file_str_replace', $subtheme_info_file, array_keys($alterations), $alterations);

  // Replace all occurrences of 'STARTERKIT' with the machine name of our sub theme.
  drush_op('zen_file_str_replace', $subtheme_path . '/theme-settings.php', 'STARTERKIT', $machine_name);
  drush_op('zen_file_str_replace', $subtheme_path . '/template.php', 'STARTERKIT', $machine_name);

  // Remove all RTL stylesheets.
  if ($without_rtl = drush_get_option('without-rtl')) {
    foreach (array('forms', 'html-reset', 'layout-fixed', 'layout-liquid', 'navigation', 'pages', 'tabs') as $file) {
      // Remove the RTL stylesheet.
      drush_op('unlink', $subtheme_path . '/css/' . $file . '-rtl.css');
      drush_op('zen_file_str_replace', $subtheme_path . '/css/' . $file . '.css', ' /* LTR */', '');
      // Remove the RTL sass file.
      drush_op('unlink', $subtheme_path . '/sass/' . $file . '-rtl.scss');
      drush_op('zen_file_str_replace', $subtheme_path . '/sass/' . $file . '.scss', ' // LTR', '');
    }
  }

  // Notify user of the newly created theme.
  drush_print(dt('Starter kit for "!name" created in: !path', array(
    '!name' => $name,
    '!path' => $subtheme_path,
  )));
}

/**
 * Copy a directory recursively.
 */
function zen_copy($source_dir, $target_dir, $ignore = '/^(\.(\.)?|CVS|\.svn|\.git|\.DS_Store)$/') {
  if (!is_dir($source_dir)) {
    drush_die(dt('The directory "!directory" was not found.', array('!directory' => $source_dir)));
  }
  $dir = opendir($source_dir);
  @mkdir($target_dir);
  while($file = readdir($dir)) {
    if (!preg_match($ignore, $file)) {
      if (is_dir($source_dir . '/' . $file)) {
        zen_copy($source_dir . '/' . $file, $target_dir . '/' . $file, $ignore);
      }
      else {
        copy($source_dir . '/' . $file, $target_dir . '/' . $file);
      }
    }
  }
  closedir($dir);
}

/**
 * Replace strings in a file.
 */
function zen_file_str_replace($file_path, $find, $replace) {
  $file_contents = file_get_contents($file_path);
  $file_contents = str_replace($find, $replace, $file_contents);
  file_put_contents($file_path, $file_contents);
}

UNIX Shell script

This script prompts for a human-readable name for a new subtheme name. The human readable name is placed in the theme's .info file. A machine-readable name is created by converting the human-readable name to all lowercase letters (dashes and spaces become underscores); for example, Joe Cool's "Better" Theme becomes joe_cools_better_theme. The script then copies the STARTERKIT to the new subtheme directory and renames internal references as necessary.

Script for zen-6.x-1.x

#!/bin/bash
# This script creates a new subtheme for zen following the instructions on drupal.org/node/226507
# Place this file in your themes directory, alongside the zen folder, as a file named "subtheme"
# Usage: open a shell, navigate to the themes directory and run ./subtheme
# (Run chmod 700 on this file to make it executable)
# Based on script submitted here: drupal.org/node/276120

echo "Enter a human-readable subtheme name:"
read -e INPUTNAME

NAME=`echo $INPUTNAME | tr '[:upper:]' '[:lower:]' | sed -e 's/[ -]/_/g' -e 's/[^a-z0-9_]//g'`
SEDNAME=`echo $INPUTNAME | sed -e 's/"/\\"/g'`

echo -n "Choose a page layout type [f]ixed or [l]iquid for subtheme $NAME: "
read -e LAYOUT

if [ $LAYOUT = "f" ]; then
  CSS="layout-fixed.css"
elif [ $LAYOUT = "l" ]; then
  CSS="layout-liquid.css"
else
  echo Invalid layout type.
  exit
fi

cp -r zen/STARTERKIT $NAME

#
# Change the theme name (the line 'name = ...') to the new subtheme name
#
sed 's/STARTERKIT/'$NAME'/g' $NAME/STARTERKIT.info.txt | sed 's/^\(name *= *\).*/\1'"$SEDNAME"'/g' > $NAME/$NAME.info
  rm $NAME/STARTERKIT.info.txt

cp zen/zen/$CSS $NAME/layout.css
cp zen/zen/print.css $NAME/print.css
cp zen/zen/html-elements.css $NAME/html-elements.css
cp zen/zen/zen.css $NAME/$NAME.css
sed 's/STARTERKIT/'$NAME'/g' zen/STARTERKIT/template.php > $NAME/template.php
sed 's/STARTERKIT/'$NAME'/g' zen/STARTERKIT/theme-settings.php > $NAME/theme-settings.php

exit 0

Script for zen-6.x-2.x

#!/bin/bash
# This script creates a new subtheme for zen-6.x-2.x-dev
# Place this file in your theme directory, alongside the zen folder, as a file name "subtheme-creator"
# Usage: open a shell, navigate to the zen directory and run ./subtheme-creator
# (Run chmod 700 on this file to make it executable)
# Based on script submitted here: drupal.org/node/276120

#~ Chain of events:
#~ * copy STARTERKIT/ from zen/ and rename to NEWNAME (lowercase/underscores)
#~ * rename STARTERKIT.info.txt to NEWNAME.info
#~ * edit NEWNAME.info name and description field
#~ * remove unneeded layout-{!chosen layout}.css file and reference in NEWNAME.info
#~ * replace all occurances of STARTERKIT in template.php & theme-settings.php with NEWNAME


echo -n "Enter a name for your theme: "
read -e NAME

HUMAN=`echo $NAME | sed -e 's/[^a-zA-Z0-9\ ]//g'`
NAME=`echo $NAME | tr [:upper:] [:lower:] | sed -e 's/[\-\ ]/_/g' -e 's/[^a-z0-9_]//g'`

if [ -d $NAME ]; then
    echo "Theme folder already exists with that name."
    echo "Do you want to remove the existing theme?"
    select OVERWRITE in Yes No
    do
        if [ $OVERWRITE = "Yes" ]; then
            rm -r $NAME /tmp/$NAME;
            break
        else
            echo "Exiting..."
            exit
        fi
    done
fi

echo "Choose a page layout"
select LAYOUT in Fixed Liquid
do
    if [ $LAYOUT = "Liquid" ]; then
        DELCSS="layout-fixed"
        CSS="layout-liquid"
        break
    else
        DELCSS="layout-liquid"
        CSS="layout-fixed"
        break
    fi
done

# Copy the STARTERKIT folder
cp -r zen/STARTERKIT $NAME

# Create .info file
sed -e 's/STARTERKIT/'$NAME'/g' -e 's/Zen Sub-theme Starter Kit/'"$HUMAN"'/g' -e 's/^\(description = \).*$/\1A Zen sub-theme/g' $NAME/STARTERKIT.info.txt > $NAME/$NAME.info
rm $NAME/STARTERKIT.info*

# Remove layout css file and references not required
rm $NAME/css/$DELCSS*
sed -e 's/'$DELCSS'/'$CSS'/g' $NAME/$NAME.info > /tmp/zen.info
mv /tmp/zen.info $NAME/$NAME.info

# Replace all occurances of STARTERKIT with theme name in template and theme-settings files
sed 's/STARTERKIT/'$NAME'/g' $NAME/template.php > /tmp/zen-template.php
mv /tmp/zen-template.php $NAME/template.php
sed 's/STARTERKIT/'$NAME'/g' $NAME/theme-settings.php > /tmp/zen-theme-settings.php
mv /tmp/zen-theme-settings.php $NAME/theme-settings.php

echo New theme folder $NAME created in `pwd`

exit 0

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

Nick Robillard’s picture

I added this to handle an svn committed STARTERKIT dir.

# Handle svn - export if .svn dir exists.
DIRECTORY="zen/STARTERKIT"
if [ -d "$DIRECTORY""/.svn" ]; then
  svn export $DIRECTORY $NAME
else
  cp -r $DIRECTORY $NAME
fi
sanguis’s picture

the 6.2 versions work for 7.3 as well.
thank you.