You are here

zenophile.module in Zenophile 6

Same filename and directory in other branches
  1. 6.2 zenophile.module
  2. 7 zenophile.module

File

zenophile.module
View source
<?php

/**
 * Implementation of hook_menu().
 */
function zenophile_menu() {
  return array(
    'admin/build/themes/zenophile' => array(
      'title' => 'Create Zen subtheme',
      'description' => 'Quickly create a Zen subtheme for theming.',
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'zenophile_create',
      ),
      'access arguments' => array(
        'create zen theme with zenophile',
      ),
      'type' => MENU_LOCAL_TASK,
    ),
  );
}

/**
 * Implementation of hook_perm().
 */
function zenophile_perm() {
  return array(
    'create zen theme with zenophile',
  );
}

/**
 * Form to create the subtheme. drupal_get_form() callback.
 */
function zenophile_create() {

  // Check for Zen
  if (drupal_get_path('theme', 'STARTERKIT') === '') {
    drupal_set_message(t('The STARTERKIT theme could not be found. Please check that the <a href="!zen">Zen theme</a> is properly installed.'), array(
      '!zen' => 'http://drupal.org/project/zen',
    ), 'error');
  }
  else {

    // Create a list of Zen-based themes. This is… good enough.
    $zen_based = array();
    foreach (list_themes(TRUE) as $theme) {

      // Check for zen.css in the theme's stylesheet list
      if (in_array('zen.css', array_keys($theme->info['stylesheets']['all']))) {
        $zen_based[$theme->name] = t('@tname (@tsname)', array(
          '@tname' => $theme->info['name'],
          '@tsname' => $theme->name,
        ));
      }
    }
    return array(
      'parent' => array(
        '#title' => t('Starter theme'),
        '#type' => 'select',
        '#options' => $zen_based,
        '#default_value' => 'STARTERKIT',
        '#required' => TRUE,
      ),
      'sysname' => array(
        '#title' => t('System name'),
        '#description' => t('The machine-compatible name of your subtheme. This name should consist of only lowercase letters plus the underscore character.'),
        '#type' => 'textfield',
        '#required' => TRUE,
      ),
      'friendly' => array(
        '#title' => t('Human name'),
        '#description' => t('A human-friendly name for your subtheme. This name may contain uppercase letters, spaces, punctuation, etc. If left blank, the system name will also be used here.'),
        '#type' => 'textfield',
      ),
      'description' => array(
        '#title' => t('Description'),
        '#description' => t('A short description of this theme.'),
        '#type' => 'textfield',
        '#required' => TRUE,
      ),
      'layout' => array(
        '#title' => t('Layout type'),
        '#description' => t('A fixed layout will stay the same width, regardless of the user&rsquo;s browser window width. Any space beyond the width of the layout will be filled with blank space on either side of the layout, so that the layout is always centered in the browser window. A liquid layout will adjust its width depending on the width of the user&rsquo;s browser window width, so that the edges of the layout are always the same distance from the edges of the browser window (though this breaks when the browser window is made to be extremely narrow). If in doubt, you probably want to use a fixed layout.'),
        '#type' => 'radios',
        '#options' => array(
          'fixed' => t('Fixed'),
          'liquid' => t('Liquid'),
        ),
        '#default_value' => 'fixed',
        '#required' => TRUE,
      ),
      'site' => array(
        '#title' => t('Site directory'),
        '#description' => t('Which site directory would you like your new subtheme to be placed in? If in doubt, select <em>all</em>.'),
        '#type' => 'select',
        '#options' => _zenophile_find_sites(),
        '#default_value' => array(
          'all',
        ),
        '#required' => TRUE,
      ),
      'fresh' => array(
        '#title' => t('Create fresh CSS file'),
        '#description' => t('If checked, Zenophile will create a new empty CSS file and add it to the theme via its .info file. Some themers may prefer to start with a fresh empty CSS file rather than adapting the pre-created CSS file which will be copied over from the STARTERKIT directory.'),
        '#type' => 'checkbox',
        '#default_value' => TRUE,
      ),
      'submit' => array(
        '#type' => 'submit',
        '#value' => t('Submit'),
      ),
    );
  }
}

/**
 * Validator for zenophile_create().
 */
function zenophile_create_validate($form, &$form_state) {

  // Check that the system name of the theme is valid
  if (preg_match('/[^a-z_]/', $form_state['values']['sysname'])) {
    form_set_error('sysname', t('The <em>System name</em> may only consist of lowercase letters and the underscore character.'));
  }
  if (drupal_get_path('theme', $form_state['values']['sysname'])) {
    form_set_error('sysname', t('A theme with this <em>System name</em> already exists. Cowardly refusing to create another one.'));
  }
}

/**
 * Submitor (?) for zenophile_create().
 */
function zenophile_create_submit($form, &$form_state) {

  // Does the theme directory exist already for this site?
  $t_dir = "sites/{$form_state['values']['site']}/themes";
  if (!file_exists($t_dir) && !mkdir($t_dir, 0755)) {
    form_set_error(NULL, t('The <em>themes</em> directory for the %site site directory does not exist, and it could not be created automatically. Please create the directory %dir manually and try again.', array(
      '%site' => $site,
      '%dir' => $t_dir,
    )), 'error');
  }
  else {
    $dir = "{$t_dir}/{$form_state['values']['sysname']}";

    // Make the theme directory
    if (file_exists($dir)) {

      // This theoretically should have been caught by the validate function
      // above, but it's possible that there's a directory in this site's
      // themes directory which is not a proper theme… or it's a regular file.
      form_set_error(NULL, t('The subtheme directory %dir could not be created because a file or directory with that name already exists.', array(
        '%dir' => $dir,
      )));
    }
    else {
      if (mkdir($dir)) {

        // Copy over everyting in the STARTERKIT directory except STARTERKIT.info
        // (which we'll get to in just a bit), and template.php and
        // theme-settings.php (which we'll get to in step 6). This is essentially
        // Step 1 of the "by-hand" Zen subtheme creation directions.
        $parent_dir = drupal_get_path('theme', $form_state['values']['parent']);
        $zen_dir = drupal_get_path('theme', 'zen');
        $h = opendir($parent_dir);
        $parent_info = $form_state['values']['parent'] . '.info';
        while (($file = readdir($h)) !== FALSE) {
          $fpath = "{$parent_dir}/{$file}";
          if (is_file($fpath) && $file[0] !== '.' && $file !== $parent_info && $file !== 'template.php' && $file !== 'theme-settings.php') {
            copy($fpath, "{$dir}/{$file}");
          }
        }
        $path_part = "{$dir}/{$form_state['values']['sysname']}";

        // Now take care of STARTERKIT.info. Step 2.
        // Load the info file into a string.
        $info = file_get_contents("{$parent_dir}/{$parent_info}");

        // Reset the $Id$ string
        $info = preg_replace('/^; \\$Id.*\\$$/m', '; $Id$', $info, 1);

        // Build replacement arrays. We definitely want to replace the name and
        // description.
        $from = array(
          "/{$form_state['values']['parent']}/",
          '/^name\\s*=.*/m',
          '/^description\\s*=.*/m',
        );
        $to = array(
          $form_state['values']['sysname'],
          'name        = ' . ($form_state['values']['friendly'] === '' ? $form_state['values']['sysname'] : $form_state['values']['friendly']),
          'description = ' . $form_state['values']['description'],
        );

        // Do we also want to add the fresh stylesheet?
        if ($form_state['values']['fresh']) {
          $from[] = '/^stylesheets\\[all\\]\\[\\]\\s*=\\s*zen\\.css$/m';
          $to[] = "stylesheets[all][]   = zen.css\n\n  ; Specifying a nice clean stylesheet\nstylesheets[all][] = {$form_state['values']['sysname']}-fresh.css";

          // Make the blank stylesheet file
          touch($path_part . '-fresh.css');
        }

        // Do replacement and write the info file
        $info = preg_replace($from, $to, $info);
        file_put_contents($path_part . '.info', $info);

        // Copy the liquid or fixed stylesheet, the print stylesheet, and the
        // Zen stylesheet. Steps 3 through 5. Only do this if the parent is
        // STARTERKIT - otherwise these will have already been copied.
        if ($form_state['values']['parent'] === 'STARTERKIT') {
          copy("{$zen_dir}/layout-{$form_state['values']['layout']}.css", "{$dir}/layout.css");
          copy($zen_dir . '/print.css', $dir . '/print.css');

          // If there is a starter_theme.css file in the directory already,
          // rename it to this_theme.css. Otherwise, copy over zen.css and
          // rename it.
          $parent_css = "{$dir}/{$form_state['values']['parent']}.css";
          if (file_exists($parent_css)) {
            rename($parent_css, "{$dir}/{$form_state['values']['sysname']}.css");
          }
          else {
            copy($zen_dir . '/zen.css', "{$dir}/{$form_state['values']['sysname']}.css");
          }
        }

        // Copy template.php and theme-settings.php and replace STARTERKIT.
        // Kind of Step 1 plus Step 6 mixed together.
        $info = file_get_contents($parent_dir . '/template.php');
        $info = str_replace($form_state['values']['parent'], $form_state['values']['sysname'], $info);
        file_put_contents($dir . '/template.php', $info);
        $info = file_get_contents($parent_dir . '/theme-settings.php');
        $info = str_replace($form_state['values']['parent'], $form_state['values']['sysname'], $info);
        file_put_contents($dir . '/theme-settings.php', $info);
        drupal_set_message(t('A new subtheme was created in %dir.', array(
          '%dir' => $dir,
        )));

        // Flush the cached theme data so the new subtheme appears in the parent
        // theme list
        system_theme_data();
      }
      else {
        drupal_set_message(t('An error occurred while trying to create the subtheme directory %dir.', array(
          '%dir' => $dir,
        )));
      }
    }
  }
}

/**
 * List this site's sites as located in the sites directory.
 *
 * @return An array of directories in the sites directory.
 */
function _zenophile_find_sites() {
  $sites = array();
  if ($h = opendir('sites')) {
    while (($site = readdir($h)) !== FALSE) {

      // Don't allow dot files or links for security reasons
      if (is_dir('sites/' . $site) && !is_link('sites/' . $site) && $site[0] !== '.' && $site !== 'default') {
        $sites[] = $site;
      }
    }
    return drupal_map_assoc($sites);
  }
  else {
    drupal_set_message(t('The <em>sites</em> directory could not be read.'), 'error');
    return array();
  }
}

Functions

Namesort descending Description
zenophile_create Form to create the subtheme. drupal_get_form() callback.
zenophile_create_submit Submitor (?) for zenophile_create().
zenophile_create_validate Validator for zenophile_create().
zenophile_menu Implementation of hook_menu().
zenophile_perm Implementation of hook_perm().
_zenophile_find_sites List this site's sites as located in the sites directory.