You are here

skinr.module in Skinr 7.2

Same filename and directory in other branches
  1. 8.2 skinr.module
  2. 6.2 skinr.module
  3. 6 skinr.module

Handles core Skinr functionality.

File

skinr.module
View source
<?php

/**
 * @file
 * Handles core Skinr functionality.
 */

/**
 * The Skinr API version.
 */
define('SKINR_VERSION', 2);

/**
 * A flag used to let us know if an object is in the database.
 */
define('SKINR_STORAGE_IN_DATABASE', 0);
define('SKINR_STORAGE_IN_CODE', 1);
define('SKINR_STORAGE_IN_CODE_OVERRIDDEN', 2);

/**
 * Implements hook_help().
 */
function skinr_help($path, $arg) {
  switch ($path) {
    case 'admin/help#skinr':
      if (module_exists('advanced_help')) {
        return t('Visit the <a href="@skinr-help">help page</a> for full documentation.', array(
          '@skinr-help' => url('admin/advanced_help/skinr'),
        ));
      }
      else {
        return t('Please download and enable the <a href="http://drupal.org/project/advanced_help">Advanced Help</a> module for full Skinr documentation.');
      }
      break;
  }
}

/**
 * Implements hook_hook_info().
 */
function skinr_hook_info() {
  $hooks = array(
    'skinr_api_2',
    'skinr_elements',
    'skinr_group_info',
    'skinr_group_info_alter',
    'skinr_skin_info',
    'skinr_skin_info_alter',
    'skinr_theme_hooks',
    'skinr_theme_hooks_alter',
  );
  $hooks = array_fill_keys($hooks, array(
    'group' => 'skinr',
  ));
  $hooks['skinr_skin_defaults'] = array(
    'group' => 'skinr_default',
  );
  return $hooks;
}

/**
 * Clears cached Skinr information.
 */
function skinr_cache_reset() {
  cache_clear_all('skinr_', 'cache', TRUE);
}

/**
 * Implements hook_preprocess().
 *
 * @todo Optimize this function by removing dependencies on
 *   skinr_get_skin_info() and similar resource heavy functions.
 * @todo Account for Drupal's caching being enabled and make it work.
 */
function skinr_preprocess(&$variables, $hook) {

  // Fix for update script.
  if (defined('MAINTENANCE_MODE')) {
    return;
  }
  $data =& drupal_static(__FUNCTION__);

  // Caching the data returned from the following functions is reported to
  // improve performance.
  if (!isset($data)) {
    $data['current_theme'] = skinr_current_theme();
    $data['skin_info'] = skinr_get_skin_info();
    $data['theme_registry'] = theme_get_registry();
    $data['skip_cache'] = FALSE;
    $implementations = module_implements('skinr_preprocess_alter');

    // skinr_panels is special case that only runs when skinr_context is enabled.
    if (count($implementations) > 0 && $implementations[0] !== 'skinr_panels') {

      // Skip caching whenever a module implements skinr_preprocess_alter.
      $data['skip_cache'] = TRUE;
    }
  }
  $original_hook = isset($data['theme_registry'][$hook]['original hook']) ? $data['theme_registry'][$hook]['original hook'] : $hook;

  // An array of $elements based on $module and $original_hook, derived from $variables.
  $array_elements = skinr_invoke_all('skinr_elements', $variables, $original_hook, 'preprocess');
  $timeit = FALSE;
  if (is_array($array_elements) && isset($array_elements[$original_hook]) && in_array('system__navigation', $array_elements[$original_hook])) {
    $timeit = TRUE;
  }
  if ($timeit) {
    timer_start(__FUNCTION__ . '__loop');
  }
  foreach ($array_elements as $module => $elements) {
    $applied_skins = array();
    foreach ($elements as $element) {
      if ($timeit) {
        timer_start(__FUNCTION__ . '__cache');
      }
      $cid = 'skinr_preprocess:' . $module . ':' . $element . ':' . $data['current_theme'];
      if (!$data['skip_cache'] && ($cached = cache_get($cid))) {

        // This type of caching is incompatible with skinr_context.
        $applied_skins = $cached->data;
      }
      else {

        // Get a list of skin configuration IDs to pass to
        // skinr_skin_load_multiple().
        $params = array(
          'theme' => $data['current_theme'],
          'module' => $module,
          'element' => $element,
          'status' => 1,
        );
        $sids = skinr_skin_get_sids($params);
        $skins = !empty($sids) ? skinr_skin_load_multiple($sids) : array();

        // Invoke hook_skinr_preprocess_alter() in all modules.
        $context = array(
          'hook' => $hook,
          'variables' => &$variables,
          'theme' => $data['current_theme'],
          'module' => $module,
          'elements' => $elements,
        );
        drupal_alter('skinr_preprocess', $skins, $context);
        $applied_skins = array();
        foreach ($skins as $skin) {
          $applied_skins = array(
            $skin->skin => $skin->options,
          ) + $applied_skins;
        }

        // Cache data.
        if (!$data['skip_cache']) {
          cache_set($cid, $applied_skins);
        }
      }
    }

    // Use drupal_process_attached() to add attachements such as JS and CSS.
    if (!empty($applied_skins)) {
      foreach ($applied_skins as $skin_name => $skin_options) {

        // Special case for _additional.
        if ($skin_name == '_additional') {
          continue;
        }

        // Make sure this skin is enabled for the current theme.
        if (isset($data['skin_info'][$skin_name]['attached'])) {
          $elements = array(
            '#attached' => $data['skin_info'][$skin_name]['attached'],
          );
          drupal_process_attached($elements);
        }
        if (!is_array($skin_options)) {
          $skin_options = array(
            $skin_options,
          );
        }
        foreach ($skin_options as $skin_option) {
          if (isset($data['skin_info'][$skin_name]['options'][$skin_option]['attached'])) {
            $elements = array(
              '#attached' => $data['skin_info'][$skin_name]['options'][$skin_option]['attached'],
            );
            drupal_process_attached($elements);
          }
        }
      }
      $variables['classes_array'] = array_merge($variables['classes_array'], skinr_flatten_skins_array($applied_skins));
    }
  }
}

/**
 * Returns an array of classes.
 *
 * @param $skin_options
 *   An array of skin options keyed by their skin name. The key '_additional'
 *   is reserved for additional classes entered by the user.
 *
 * @todo Optimize this function by removing dependencies on the resource heavy
 *   skinr_get_skin_info() function.
 * @todo Rename function to reflect new functionality.
 */
function skinr_flatten_skins_array($skin_options) {
  $skin_info = skinr_get_skin_info();
  $classes = array();
  foreach ($skin_options as $skin_name => $options) {
    if ($skin_name == '_additional') {
      $classes = array_merge($classes, $options);
    }
    else {
      foreach ($options as $option) {
        if (!empty($skin_info[$skin_name]['options'][$option]['class'])) {
          $classes = array_merge($classes, $skin_info[$skin_name]['options'][$option]['class']);
        }
      }
    }
  }
  return array_unique($classes);
}

/**
 * Returns a list of extensions that implement this API version of Skinr.
 *
 * @return
 *   An associative array whose keys are system names of extensions and whose
 *   values are again associative arrays containing:
 *   - type: Either 'module' or 'theme'.
 *   - name: The system name of the extension.
 *   - path: The path to the extension.
 *   - directory: (optional) The sub-directory holding Skinr plugin files.
 *   - ...: Any other properties defined by the module or theme.
 */
function skinr_implements_api() {

  // All themes are disabled while running update script so theme skins are not
  // cached. Don't cache to prevent this.
  if (defined('MAINTENANCE_MODE')) {
    return array();
  }
  $cache =& drupal_static(__FUNCTION__);
  if (!isset($cache)) {
    if ($cached = cache_get('skinr_implements_api')) {
      $cache = $cached->data;
      return $cache;
    }
    $cache = array();

    // Collect hook_skinr_api_VERSION() module implementations. This will also
    // auto-load $module.skinr.inc files, which may contain skin/group hook
    // implementations (when not using the plugin system).
    $module_info = system_get_info('module');
    foreach (module_implements('skinr_api_' . SKINR_VERSION) as $module) {

      // Ensure that $module and the extension type is registered.
      $cache[$module] = array(
        'type' => 'module',
        'name' => $module,
        'version' => isset($module_info[$module]['version']) ? $module_info[$module]['version'] : NULL,
      );

      // Check whether the hook returns any information.
      $function = $module . '_skinr_api_' . SKINR_VERSION;
      $info = $function();
      if (isset($info) && is_array($info)) {
        $cache[$module] += $info;
      }

      // If the module specified a custom path, check whether it contains a
      // $module.skinr.inc file and auto-load it. module_implements() only
      // auto-loads $module.skinr.inc in a module's root folder.
      if (isset($cache[$module]['path'])) {
        $file = $cache[$module]['path'] . '/' . $module . '.skinr.inc';
        if (file_exists(DRUPAL_ROOT . '/' . $file)) {
          $cache[$module]['include file'] = $file;
        }
      }
      else {

        // If there is a $module.skinr.inc in the module's root, it gets
        // auto-loaded for any hooks. But if a skin defined thering contains a
        // custom 'form callback' function, we'll need to load it manually. So
        // store the file's info.
        $file = drupal_get_path('module', $module) . '/' . $module . '.skinr.inc';
        if (file_exists(DRUPAL_ROOT . '/' . $file)) {
          $cache[$module]['include file'] = $file;
        }
      }

      // Populate defaults.
      $cache[$module] += array(
        'path' => drupal_get_path('module', $module),
        'directory' => NULL,
      );
    }

    // Collect the equivalent of hook_skinr_api_VERSION() implementations in
    // themes. The theme system only initializes one theme (and optionally its
    // base themes) for the current request, and the phptemplate engine only
    // loads template.php during theme initialization. Furthermore, template.php
    // is a custom concept of the phptemplate engine and does not exist for
    // other theme engines. Since we are interested in all existing
    // implementations of all enabled themes, the equivalent of the module hook
    // is a theme .info file property 'skinr' that has the sub-keys 'api' and
    // optionally 'directory' defined.
    // Account for all enabled themes and (any recursive) base themes of them,
    // regardless of whether base themes are enabled.
    $all_themes = list_themes();
    $themes = array();

    // Additionally record the base themes and sub themes of each theme, in
    // order to apply inheritance rules elsewhere. Do not assign these variables
    // as properties on the theme objects themselves, since all objects are
    // pointers (much like references) in PHP 5, so our properties would be
    // visible for everyone else who calls list_themes().
    $base_themes = array();
    $sub_themes = array();
    foreach ($all_themes as $name => $theme) {

      // If the theme is enabled, add it to the stack.
      if (!empty($theme->status)) {
        $themes[$name] = $theme;

        // Find and add all base themes of the enabled theme to the stack.
        // @see drupal_theme_initialize()
        $sub_theme_name = $name;
        while ($name && isset($all_themes[$name]->base_theme)) {

          // Record the sub theme for the base theme.
          $sub_themes[$all_themes[$name]->base_theme][$name] = $name;

          // Add the base theme to the stack.
          $name = $all_themes[$name]->base_theme;
          $themes[$name] = $all_themes[$name];

          // Record the base theme for the original sub theme.
          $base_themes[$sub_theme_name][$name] = $name;
        }
      }
    }
    foreach ($themes as $name => $theme) {
      if (isset($theme->info['skinr']['api']) && $theme->info['skinr']['api'] == SKINR_VERSION) {

        // Ensure that the theme name and the extension type is registered.
        $cache[$name] = array(
          'type' => 'theme',
          'name' => $name,
          'version' => isset($theme->info['version']) ? $theme->info['version'] : NULL,
          'base themes' => isset($base_themes[$name]) ? $base_themes[$name] : array(),
          'sub themes' => isset($sub_themes[$name]) ? $sub_themes[$name] : array(),
        );

        // Add any additional information that has been registered.
        $cache[$name] += $theme->info['skinr'];

        // Populate defaults.
        $cache[$name] += array(
          'path' => drupal_get_path('theme', $name),
          // Since themes cannot do anything else than registering skins and
          // groups, we default to the sub-directory 'skins'.
          'directory' => 'skins',
        );

        // Lastly, for API consistency with modules, check whether the theme
        // contains a $theme.skinr.inc file and auto-load it, if any.
        $file = $cache[$name]['path'] . '/' . $name . '.skinr.inc';
        if (file_exists(DRUPAL_ROOT . '/' . $file)) {
          $cache[$name]['include file'] = $file;
        }
      }
    }
    cache_set('skinr_implements_api', $cache);
  }
  return $cache;
}

/**
 * Determine whether a module implements a hook.
 *
 * Replacement for module_hook() that only invokes modules that implement
 * the current version of Skinr API. It also supports $module.skinr.inc files
 * in themes and custom paths.
 *
 * @param $module
 *   The name of the module (without the .module extension).
 * @param $hook
 *   The name of the hook (e.g. "skinr_skin_info" or "skinr_theme_hooks").
 *
 * @return
 *   TRUE if the module is both installed and enabled, and the hook is
 *   implemented in that module.
 */
function skinr_hook($module, $hook) {
  $function = $module . '_' . $hook;
  if (function_exists($function)) {
    return TRUE;
  }

  // If the hook implementation does not exist, check whether it may live in an
  // include file in a custom path.
  $extensions = skinr_implements_api();
  if (isset($extensions[$module])) {
    $extension = $extensions[$module];
    if (isset($extension['include file'])) {

      // The module specified a custom path. module_hook() only auto-loads
      // $module.skinr.inc in a module's root folder.
      skinr_load_include($extension['include file']);
      if (function_exists($module . '_' . $hook)) {
        return TRUE;
      }
    }
    else {

      // Run through module_hook() to auto-load $module.skinr.inc from a
      // non-custom path.
      if (module_hook($module, $hook)) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

/**
 * Determine which modules are implementing a hook.
 *
 * Replacement for module_implements() that only invokes modules that implement
 * the current version of Skinr API. It also supports $module.skinr.inc files
 * in themes and custom paths.
 *
 * @param $hook
 *   The name of the hook (e.g. "skinr_skin_info" or "skinr_theme_hooks").
 *
 * @return
 *   An array with the names of the modules which are implementing this hook.
 *
 * @see skinr_exit()
 */
function skinr_implements($hook) {
  $implementations =& drupal_static(__FUNCTION__, array());

  // Fetch implementations from cache.
  if (empty($implementations)) {
    $implementations = cache_get('skinr_implements', 'cache_bootstrap');
    if ($implementations === FALSE) {
      $implementations = array();
    }
    else {
      $implementations = $implementations->data;
    }
  }
  if (!isset($implementations[$hook])) {
    $implementations['#write_cache'] = TRUE;
    $extensions = skinr_implements_api();
    $implementations[$hook] = array();
    foreach ($extensions as $module => $extension) {
      if (isset($extension['include file'])) {

        // The module specified a custom path. module_implements() and
        // module_hook() only auto-load $module.skinr.inc in a module's
        // root folder.
        $include_file = skinr_load_include($extension['include file']);
        if (function_exists($module . '_' . $hook)) {
          $implementations[$hook][$module] = $include_file ? $extension['include file'] : FALSE;
        }
      }
      else {

        // Run through module_hook() to auto-load $module.skinr.inc from a
        // non-custom path.
        if (module_hook($module, $hook)) {
          $implementations[$hook][$module] = FALSE;
        }
      }
    }

    // Allow modules to change the weight of specific implementations but avoid
    // an infinite loop.
    if ($hook != 'skinr_implements_alter') {
      drupal_alter('skinr_implements', $implementations[$hook], $hook);
    }
  }
  else {
    foreach ($implementations[$hook] as $module => $file) {
      if ($file) {
        skinr_load_include($file);
      }
      else {
        module_hook($module, $hook);
      }

      // It is possible that a module removed a hook implementation without the
      // implementations cache being rebuilt yet, so we check whether the
      // function exists on each request to avoid undefined function errors.
      // Since module_hook() may needlessly try to load the include file again,
      // function_exists() is used directly here.
      if (!function_exists($module . '_' . $hook)) {

        // Clear out the stale implementation from the cache and force a cache
        // refresh to forget about no longer existing hook implementations.
        unset($implementations[$hook][$module]);
        $implementations['#write_cache'] = TRUE;
      }
    }
  }
  return array_keys($implementations[$hook]);
}

/**
 * Implements hook_exit().
 *
 * @see module_implements_write_cache()
 */
function skinr_exit($destination = NULL) {
  $implementations =& drupal_static('skinr_implements');

  // Check whether we need to write the cache. We do not want to cache hooks
  // which are only invoked on HTTP POST requests since these do not need to be
  // optimized as tightly, and not doing so keeps the cache entry smaller.
  if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
    unset($implementations['#write_cache']);
    cache_set('skinr_implements', $implementations, 'cache_bootstrap');
  }
}

/**
 * Invoke a hook in all enabled modules and themes that implement it.
 *
 * Replacement for module_invoke_all() that only invokes modules that implement
 * the current version of Skinr API. It also supports $module.skinr.inc files
 * in themes and custom paths.
 *
 * @param $hook
 *   The name of the hook to invoke.
 * @param ...
 *   Arguments to pass to the hook.
 *
 * @return
 *   An array of return values of the hook implementations. If modules return
 *   arrays from their implementations, those are merged into one array.
 */
function skinr_invoke_all($hook) {
  $args = func_get_args();

  // Remove $hook from the arguments.
  unset($args[0]);
  $return = array();
  foreach (skinr_implements($hook) as $module) {
    $function = $module . '_' . $hook;
    if (function_exists($function)) {
      $result = call_user_func_array($function, $args);
      if (isset($result) && is_array($result)) {
        $return = array_merge_recursive($return, $result);
      }
      elseif (isset($result)) {
        $return[] = $result;
      }
    }
  }
  return $return;
}

/**
 * Loads a $module.skinr.inc include file.
 */
function skinr_load_include($file) {
  if (is_file($file)) {
    include_once $file;
    return $file;
  }
  return FALSE;
}

/**
 * Includes Skinr plugin files for an extension, if any.
 *
 * @param $extension
 *   The API information for an extension, as returned by skinr_implements_api().
 */
function skinr_load_plugins($extension) {
  static $loaded = array();

  // If plugins have already been loaded for this extension, return them.
  if (isset($loaded[$extension['name']])) {
    return $loaded[$extension['name']];
  }
  $loaded[$extension['name']] = array();

  // If the extension defines a plugin directory, scan its plugins.
  if (isset($extension['directory'])) {
    $dir = DRUPAL_ROOT . '/' . $extension['path'] . '/' . $extension['directory'];
    $mask = '@^' . DRUPAL_PHP_FUNCTION_PATTERN . '\\.inc$@';
    $loaded[$extension['name']] = file_scan_directory($dir, $mask, array(
      'key' => 'name',
      'recurse' => TRUE,
      'min_depth' => 1,
      'callback' => 'skinr_include_once',
    ));
  }
  return $loaded[$extension['name']];
}

/**
 * file_scan_directory() callback wrapper around include_once.
 *
 * include_once is a PHP construct, not a function, so it cannot be invoked
 * directly as 'callback' in file_scan_directory().
 */
function skinr_include_once($file) {
  include_once $file;
}

// -----------------------------------------------------------------------
// Skinr data handling functions.

/**
 * Validate a skinr object.
 *
 * @param $skin
 *   A skin object.
 *
 * @return
 *   TRUE on success, FALSE on failure.
 */
function skinr_skin_validate(&$skin) {
  if (empty($skin->theme) || empty($skin->module) || empty($skin->element) || empty($skin->skin) || empty($skin->options)) {
    return FALSE;
  }
  if (!is_array($skin->options)) {
    return FALSE;
  }

  // Strip empty skins.
  $skin->options = _skinr_array_strip_empty($skin->options);
  if (empty($skin->options)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Save a skin object.
 *
 * @param $skin
 *   A skin object.
 *
 * @return
 *   TRUE on success, FALSE on failure.
 */
function skinr_skin_save(&$skin) {

  // Make sure we're getting valid data.
  if (!skinr_skin_validate($skin)) {
    return FALSE;
  }

  // Load the stored skin configuration object, if any.
  if (!empty($skin->sid)) {
    if (!isset($skin->original)) {

      // Load an uncached version of the skin configuration object.
      $skin->original = skinr_skin_load_unchanged($skin->sid);
    }
  }

  // Let modules modify the node before it is saved to the database.
  module_invoke_all('skinr_skin_presave', $skin);
  if (!empty($skin->sid)) {

    // Record exists, so let's update.
    $status = drupal_write_record('skinr_skins', $skin, 'sid');
    module_invoke_all('skinr_skin_update', $skin);
  }
  else {

    // Assign it a UUID if none is set.
    if (empty($skin->uuid)) {
      if (!module_exists('uuid')) {
        module_load_include('inc', 'skinr', 'skinr.uuid');
      }
      $skin->uuid = uuid_generate();
    }

    // Insert a new record.
    $status = drupal_write_record('skinr_skins', $skin);
    module_invoke_all('skinr_skin_insert', $skin);
  }

  // Clear internal properties.
  unset($skin->original);

  // Clear the static loading cache.
  // @todo Once we have a more granular reset for skinr_skin_load_multiple(), we
  //   need to use it here.
  drupal_static_reset('skinr_skin_load_multiple');

  // Clear preprocess cache.
  cache_clear_all('skinr_preprocess:' . $skin->module . ':' . $skin->element . ':', 'cache', TRUE);
  return $status;
}

/**
 * Delete a skin object.
 *
 * @param $sid
 *   The skin configuration ID.
 */
function skinr_skin_delete($sid) {
  skinr_skin_delete_multiple(array(
    $sid,
  ));
}

/**
 * Delete multiple skin configuration objects.
 *
 * @param $sids
 *   An array of skin configuration IDs.
 */
function skinr_skin_delete_multiple($sids) {
  $transaction = db_transaction();
  if (!empty($sids)) {
    $skins = skinr_skin_load_multiple($sids);
    try {
      foreach ($skins as $sid => $skin) {
        module_invoke_all('skinr_skin_delete', $skin);
      }

      // Delete after calling hooks so that they can query node tables as needed.
      db_delete('skinr_skins')
        ->condition('sid', $sids, 'IN')
        ->execute();
    } catch (Exception $e) {
      $transaction
        ->rollback();
      watchdog_exception('skinr', $e);
      throw $e;
    }

    // Clear the skinr_skin_load_multiple cache.
    drupal_static_reset('skinr_skin_load_multiple');
  }
}

/**
 * Load a skin configuration object from the database.
 *
 * @param $sid
 *   The skin configuration ID.
 *
 * @return
 *   A fully-populated skin configuration object.
 */
function skinr_skin_load($sid = NULL) {
  $sids = isset($sid) ? array(
    $sid,
  ) : array();
  $skin = skinr_skin_load_multiple($sids);
  return $skin ? reset($skin) : FALSE;
}

/**
 * Get skin configuration IDs.
 */
function skinr_skin_uuid_to_sid($uuid) {
  $sids =& drupal_static(__FUNCTION__, array());
  if (!isset($sids[$uuid])) {
    $sids[$uuid] = db_query("SELECT sid FROM {skinr_skins} WHERE uuid = :uuid", array(
      ':uuid' => $uuid,
    ))
      ->fetchField();
  }
  return $sids[$uuid];
}

/**
 * Get skin configuration IDs.
 */
function skinr_skin_sid_to_uuid($sid) {
  $uuids =& drupal_static(__FUNCTION__, array());
  if (!isset($uuids[$sid])) {
    $uuids[$sid] = db_query("SELECT uuid FROM {skinr_skins} WHERE sid = :sid", array(
      ':sid' => $sid,
    ))
      ->fetchField();
  }
  return $uuids[$sid];
}

/**
 * Load a skin configuration object from the database using UUID.
 *
 * @param $uuid
 *   The UUID of the skin configuration to load.
 *
 * @return
 *   A fully-populated skin configuration object.
 */
function skinr_skin_load_by_uuid($uuid) {
  if (!($sid = skinr_skin_uuid_to_sid($uuid))) {
    return FALSE;
  }

  // Run this through skinr_skin_load_multiple() to preserve caching.
  $skin = skinr_skin_load_multiple(array(
    $sid,
  ));
  return $skin ? reset($skin) : FALSE;
}

/**
 * Load skin configuration objects from the database.
 *
 * This function should be used whenever you need to load more than one skin
 * configuration from the database. Skin configurations are loaded into memory
 * and will not require database access if loaded again during the same page
 * request.
 *
 * @see skinr_skin_get_sids()
 *
 * @param $sids
 *   An array of skin configuration IDs.
 *
 * @return
 *   An array of skin configuration objects indexed by sid.
 */
function skinr_skin_load_multiple($sids = array()) {

  // @todo Do we want to write a more granular cache reset?
  $skins =& drupal_static(__FUNCTION__, array());

  // Create a new variable which is either a prepared version of the $sids
  // array for later comparison with cached skin configuration objects, or FALSE
  // if no $sids were passed. The $sids array is reduced as items are loaded
  // from cache, and we need to know if it's empty for this reason to avoid
  // querying the database when all requested skin configuration objects are
  // loaded from cache.
  $passed_sids = !empty($sids) ? array_flip($sids) : FALSE;
  if ($passed_sids) {
    $sids = array_keys(array_diff_key($passed_sids, $skins));
  }

  // Load any remaining skin configurations from the database. This is the
  // case if $sids is set to FALSE (so we load all skins), or if there are any
  // sids left to load.
  if ($sids === FALSE || $sids) {

    // Build the query.
    $query = db_select('skinr_skins', 's')
      ->fields('s', array(
      'sid',
      'uuid',
      'theme',
      'module',
      'element',
      'skin',
      'options',
      'status',
    ));
    if ($sids !== FALSE) {
      $query
        ->condition('sid', $sids);
    }
    $queried_skins = $query
      ->execute()
      ->fetchAllAssoc('sid');
    foreach ($queried_skins as $sid => $skin) {

      // Unserialize options array.
      $queried_skins[$sid]->options = unserialize($skin->options);

      // Let modules modify the skin configurations.
      module_invoke_all('skinr_skin_load', $queried_skins[$sid]);
    }
    $skins += $queried_skins;
  }

  // Ensure that the returned array is ordered the same as the original
  // $sids array if this was passed in and remove any invalid sids.
  if ($passed_sids) {

    // Remove any invalid sids from the array.
    $passed_sids = array_intersect_key($passed_sids, $skins);
    $return = array();
    foreach ($passed_sids as $sid => $ignore) {
      $return[$sid] = $skins[$sid];
    }
  }
  else {
    $return = $skins;
  }
  return $return;
}

/**
 * Load an uncached version of a skin configuration object.
 *
 * @param $sid
 *   The skin configuration ID.
 *
 * @return
 *   A fully-populated skin configuration object.
 */
function skinr_skin_load_unchanged($sid) {

  // Load an uncached version of the skin configuration object. Specify order to
  // ensure consistent import/export.
  $skin = db_query("SELECT sid, uuid, theme, module, element, skin, options, status FROM {skinr_skins} WHERE sid = :sid", array(
    ':sid' => $sid,
  ))
    ->fetchObject();

  // Unserialize options array.
  $skin->options = unserialize($skin->options);

  // Let modules modify the skin configuration.
  module_invoke_all('skinr_skin_load', $skin);
  return $skin;
}

/**
 * Returns all default skin configuration objects.
 */
function _skinr_skin_get_defaults() {
  $default_skins =& drupal_static(__FUNCTION__);
  if (!isset($default_skins)) {
    if ($cached = cache_get('skinr_skin_defaults')) {
      $default_skins = $cached->data;
      return $default_skins;
    }

    // Don't use module_invoke_all() to prevent oddly merged defaults.
    $default_skins = array();
    foreach (module_implements('skinr_skin_defaults') as $module) {
      $function = $module . '_skinr_skin_defaults';
      if (function_exists($function)) {
        $result = call_user_func_array($function, array());
        if (isset($result) && is_array($result)) {
          $default_skins = array_merge($default_skins, $result);
        }
        elseif (isset($result)) {
          $default_skins[] = $result;
        }
      }
    }

    // Let modules modify the skin configuration.
    drupal_alter('skinr_skin_defaults', $default_skins);
    cache_set('skinr_skin_defaults', $default_skins);
  }
  return $default_skins;
}

/**
 * Imports default skin confiuration objects from code into database.
 *
 * @param $module_name
 *   Limit importing of defaults to a single module.
 * @param $force
 *   If FALSE (default) the default skin will only be imported if the skin
 *   configuration doesn't exist, or if storage is default in code. If TRUE, the
 *   skin configuration in code will always be overwritten.
 *
 * @return
 *  If an import failed, returns FALSE. If all imports succeeded, returns TRUE.
 */
function skinr_skin_defaults_import($module_name = NULL, $force = FALSE) {
  if (isset($module_name)) {
    if (!($default_skins = module_invoke($module_name, 'skinr_skin_defaults'))) {
      $default_skins = array();
    }
    drupal_alter('skinr_skin_defaults', $default_skins);
  }
  else {
    $default_skins = _skinr_skin_get_defaults();
  }
  $status = TRUE;
  foreach ($default_skins as $skin) {
    $status = skinr_skin_import($skin, $force) && $status;
  }
  return $status;
}

/**
 * Revert a skin configuration object that's stored in code to its default state.
 *
 * @param $sid
 *   A skin configuration ID.
 *
 * @return
 *  If revert failed, returns FALSE. If it succeeded, returns TRUE.
 */
function skinr_skin_revert($sid) {
  if (!($uuid = skinr_skin_sid_to_uuid($sid))) {
    return FALSE;
  }
  $default_skins = _skinr_skin_get_defaults();
  if (!isset($default_skins[$uuid])) {
    return FALSE;
  }
  if ($status = skinr_skin_import($default_skins[$uuid], TRUE)) {
    drupal_static_reset('skinr_skin_uuid_to_sid');
    drupal_static_reset('skinr_skin_sid_to_uuid');
  }
  return $status;
}

/**
 * Import a skin configuration object as defined in skinr_skin_defaults().
 *
 * @param $default_skin
 *   A skin configuration object.
 * @param $force
 *   If FALSE (default) the default skin will only be imported if the skin
 *   configuration doesn't exist, or if storage is default in code. If TRUE, the
 *   skin configuration in code will always be overwritten.
 *
 * @return
 *  If import failed, returns FALSE. If it succeeded, returns TRUE.
 */
function skinr_skin_import($default_skin, $force = FALSE) {

  // Make sure we don't modify the cached default skin array.
  $skin = clone $default_skin;

  // Functionality is abstracted for use in skinr_skin_storage().
  _skinr_skin_import($skin);
  if (!$force && !empty($skin->sid)) {

    // Load existing skin configuration from DB.
    $db_skin = skinr_skin_load_unchanged($skin->sid);

    // Sync status.
    $skin->status = $db_skin->status;
    if ($skin != $db_skin) {

      // Skin exists and is overridden, so cancel import.
      watchdog('skinr', 'Canceled import of skin with UUID %uuid and SID %sid. It is overridden.', array(
        '%uuid' => $skin->uuid,
        '%sid' => $skin->sid,
      ), WATCHDOG_WARNING);
      return FALSE;
    }
  }
  if ($status = skinr_skin_save($skin)) {
    drupal_static_reset('skinr_skin_uuid_to_sid');
    drupal_static_reset('skinr_skin_sid_to_uuid');
    watchdog('skinr', 'Imported skin with UUID %uuid and SID %sid.', array(
      '%uuid' => $skin->uuid,
      '%sid' => $skin->sid,
    ), WATCHDOG_NOTICE);
  }
  else {
    watchdog('skinr', 'Failed to import skin with UUID %uuid and SID %sid.', array(
      '%uuid' => $skin->uuid,
      '%sid' => isset($skin->sid) ? $skin->sid : t('unknown'),
    ), WATCHDOG_WARNING);
  }
  return $status;
}
function _skinr_skin_import(&$skin) {
  if ($sid = skinr_skin_uuid_to_sid($skin->uuid)) {
    $skin->sid = $sid;
  }

  // Let modules modify the skin configuration.
  drupal_alter('skinr_skin_import', $skin);
}

/**
 * Export var function.
 *
 * @see features_var_export()
 */
function skinr_var_export($var, $prefix = '', $init = TRUE) {
  if (is_object($var)) {
    $output = method_exists($var, 'export') ? $var
      ->export() : skinr_var_export((array) $var, '', FALSE);
  }
  else {
    if (is_array($var)) {
      if (empty($var)) {
        $output = 'array()';
      }
      else {
        $output = "array(\n";
        foreach ($var as $key => $value) {

          // Using normal var_export on the key to ensure correct quoting.
          $output .= "  " . var_export($key, TRUE) . " => " . skinr_var_export($value, '  ', FALSE) . ",\n";
        }
        $output .= ')';
      }
    }
    else {
      if (is_bool($var)) {
        $output = $var ? 'TRUE' : 'FALSE';
      }
      else {
        if (is_int($var)) {
          $output = intval($var);
        }
        else {
          if (is_numeric($var)) {
            $output = floatval($var);
          }
          else {
            if (is_string($var) && strpos($var, "\n") !== FALSE) {

              // Replace line breaks in strings with a token for replacement
              // at the very end. This protects whitespace in strings from
              // unintentional indentation.
              $var = str_replace("\n", "***BREAK***", $var);
              $output = var_export($var, TRUE);
            }
            else {
              $output = var_export($var, TRUE);
            }
          }
        }
      }
    }
  }
  if ($prefix) {
    $output = str_replace("\n", "\n{$prefix}", $output);
  }
  if ($init) {
    $output = str_replace("***BREAK***", "\n", $output);
  }
  return $output;
}

/**
 * Export object function.
 *
 * @see ctools_object_export()
 */
function skinr_object_export($object, $identifier, $prefix = '') {
  $output = $prefix . '$' . $identifier . ' = new ' . get_class($object) . "();\n";
  $output .= $prefix . '$' . $identifier . '->status = ';
  if ($object->status) {
    $output .= 'TRUE; /* Edit this to false to make a default ' . $identifier . ' disabled initially */' . "\n";
  }
  else {
    $output .= 'FALSE; /* Edit this to true to make a default ' . $identifier . ' enabled initially */' . "\n";
  }
  $output .= $prefix . '$' . $identifier . '->api_version = ' . SKINR_VERSION . ";\n";
  foreach ($object as $field => $value) {
    if ($field == 'status') {
      continue;
    }
    $output .= $prefix . '$' . $identifier . '->' . $field . ' = ' . skinr_var_export($value, $prefix) . ";\n";
  }
  return $output;
}

/**
 * Output a skin configuration object as code suitable for skinr_skin_defaults().
 *
 * @param $skin
 *   A skin configuration object.
 * @param $prefix
 *   A string to prefix the code with, used to indent the resulting code.
 *
 * @return
 *   A string.
 */
function skinr_skin_export($skin, $prefix = '') {

  // Make sure we don't modify the cached skin object.
  $skin = clone $skin;

  // Let modules modify the skin configuration.
  drupal_alter('skinr_skin_export', $skin, $prefix);

  // Remove site specific $sid.
  unset($skin->sid);
  $output = skinr_object_export($skin, 'skin', $prefix);
  $output .= $prefix . "\$skins['{$skin->uuid}'] = \$skin;\n";
  return $output;
}

/**
 * Returns a skin configuration object's storage method.
 *
 * @param $skin
 *   A skin configuration object.
 *
 * @return
 *   SKINR_STORAGE_IN_DATABASE if stored in the database,
 *   SKINR_STORAGE_IN_CODE if stored in code,
 *   SKINR_STORAGE_IN_CODE_OVERRIDDEN if stored in code and overridden in db.
 */
function skinr_skin_storage($skin) {
  $default_skins = _skinr_skin_get_defaults();
  $storage = SKINR_STORAGE_IN_DATABASE;
  if (isset($default_skins[$skin->uuid])) {
    $default_skin = clone $default_skins[$skin->uuid];

    // Make sure skin has same processing as import.
    _skinr_skin_import($default_skin);

    // API version is only used for export.
    unset($default_skin->api_version);

    // Status shouldn't influence overridden.
    $default_skin->status = $skin->status;
    $storage = SKINR_STORAGE_IN_CODE;
    if ($default_skin != $skin) {

      // Default was overridden.
      $storage = SKINR_STORAGE_IN_CODE_OVERRIDDEN;
    }
  }
  return $storage;
}

/**
 * Get skin configuration IDs.
 *
 * @param $filter_by
 *   An associative array whose keys are:
 *   - theme: (optional) The theme.
 *   - module: (optional) The module.
 *   - element: (optional) The element ID.
 *   - skin: (optional) The skin name.
 *   - status: (optional) Boolean indicating whether or not this skin
 *     configuration is enabled.
 *
 * @return
 *   An array of skin configuration IDs.
 */
function skinr_skin_get_sids($filter_by = array()) {
  $query = db_select('skinr_skins', 's')
    ->fields('s', array(
    'sid',
  ));
  if (isset($filter_by['theme'])) {
    $query
      ->condition('theme', $filter_by['theme']);
  }
  if (isset($filter_by['module'])) {
    $query
      ->condition('module', $filter_by['module']);
  }
  if (isset($filter_by['element'])) {
    $query
      ->condition('element', $filter_by['element']);
  }
  if (isset($filter_by['skin'])) {
    $query
      ->condition('skin', $filter_by['skin']);
  }
  if (isset($filter_by['status'])) {
    $query
      ->condition('status', $filter_by['status']);
  }
  return $query
    ->execute()
    ->fetchCol();
}

/**
 * Helper function to remove empty skins from an array.
 *
 * @param $array
 *   A single or multi-dimensional array to strip of empty values.
 *
 * @return
 *   An array stripped of empty values.
 */
function _skinr_array_strip_empty($array) {
  $new_array = array();
  foreach ($array as $key => $value) {
    if (is_array($value)) {
      $value = _skinr_array_strip_empty($value);
    }
    if (!empty($value)) {
      $new_array[$key] = $value;
    }
  }
  return $new_array;
}

/**
 * Helper function to retrieve the current theme.
 *
 * @param $exclude_admin_theme
 *   Optional. Set to TRUE to exclude the admin theme from possible themes to
 *   return.
 *
 * @return
 *   The current theme name.
 *
 * @see drupal_theme_initialize()
 */
function skinr_current_theme($exclude_admin_theme = FALSE) {
  global $user, $theme;

  // Drupal core, and modules such as themkey and og_theme, set the theme
  // through hook_custom_theme() or hook_menu() using 'theme callback', which
  // are all picked up by menu_get_custom_theme().
  $current_theme = $theme;
  if ($exclude_admin_theme) {

    // Only select the user selected theme if it is available in the
    // list of themes that can be accessed.
    $current_theme = !empty($user->theme) && drupal_theme_access($user->theme) ? $user->theme : variable_get('theme_default', 'bartik');

    // Allow modules to override the theme. Validation has already been performed
    // inside menu_get_custom_theme(), so we do not need to check it again here.
    $custom_theme = menu_get_custom_theme();
    if ($custom_theme != variable_get('admin_theme', '0')) {
      $current_theme = !empty($custom_theme) ? $custom_theme : $current_theme;
    }
  }
  return $current_theme;
}

/**
 * Prepare the default status for a skin.
 *
 * @param $skin_info
 *   Information about a registered skin.
 *
 * @return
 *   An array of default statuses for each enabled theme.
 */
function skinr_skin_info_status_default($skin_info) {
  $status = array();

  // Retrieve the explicit default status of the registering theme for itself.
  $base_theme_status = NULL;
  if (isset($skin_info['status'][$skin_info['source']['name']])) {
    $base_theme_status = $skin_info['status'][$skin_info['source']['name']];
  }

  // Retrieve the sub themes of the base theme that registered the skin.
  $sub_themes = array();
  if (isset($skin_info['source']['sub themes'])) {
    $sub_themes = $skin_info['source']['sub themes'];
  }
  $themes = list_themes();
  foreach ($themes as $name => $theme) {
    if (!$theme->status) {
      continue;
    }

    // If this theme is a sub theme of the theme that registered the skin, check
    // whether we need to inherit the status of the base theme to the sub theme.
    // This is the case when a skin of a base theme enables itself for the base
    // theme (not knowing about potential sub themes).
    if (isset($base_theme_status) && isset($sub_themes[$name])) {
      $status[$name] = $base_theme_status;
    }

    // Apply global default.
    $status += array(
      $name => $skin_info['default status'],
    );
  }

  // Lastly, apply all explicit defaults.
  $status = array_merge($status, $skin_info['status']);
  return $status;
}

/**
 * Retrieve the overridden status of a skin.
 *
 * @param $skin_info
 *   Information about a registered skin.
 *
 * @return
 *   An array of statuses for each enabled theme. If no overrides are found,
 *   the status defaults will be returned.
 */
function skinr_skin_info_status_get($skin_info) {
  return variable_get('skinr_skin_' . $skin_info['name'] . '_status', $skin_info['status']);
}

/**
 * Set the status of a skin. Overrides the skin plugin settings.
 *
 * @param $skin_info
 *   Information about a registered skin.
 * @param $status
 *   An array of statuses for each theme.
 */
function skinr_skin_info_status_set($skin_info, $status) {
  variable_set('skinr_skin_' . $skin_info['name'] . '_status', $status);
}

/**
 * Helper function to determine whether or not a given file is local or not.
 */
function _skinr_is_local_file($file) {
  if (strpos($file, 'http://') === 0 || strpos($file, 'https://') === 0 || strpos($file, '/') === 0) {
    return FALSE;
  }
  if (!file_exists($file)) {
    return FALSE;
  }
  return TRUE;
}
function _skinr_preprocess_attached(&$attached, $source) {
  foreach (array(
    'css',
    'js',
  ) as $type) {
    if (isset($attached[$type])) {
      $keys = array_keys($attached[$type]);
      foreach ($keys as $pos => $key) {
        $options = $attached[$type][$key];

        // If the value is not an array, it's a filename and passed as first
        // (and only) argument. Turn it into an array so we can insert our defaults.
        if (!is_array($options)) {
          $key = $options;
          $options = array();
          $attached[$type][$key] = $options;
          _skinr_array_splice($attached[$type], $pos, 1, array(
            $key => $options,
          ));
        }

        // Add path to relative stylesheet and script paths. If the url is
        // absolute (e.g. the url start with 'http://' or 'https://') or
        // relative to the site's root (e.g. the url starts with '/') the path
        // does not get prepended.
        if (is_numeric($key)) {

          // If $options is an array, but $key is not a filename, find $key in
          // the $options array.
          if (_skinr_is_local_file($source['path'] . '/' . $options['data'])) {
            $attached[$type][$key]['data'] = $source['path'] . '/' . $options['data'];
          }
        }
        elseif (_skinr_is_local_file($source['path'] . '/' . $key)) {
          $key = $source['path'] . '/' . $key;
          _skinr_array_splice($attached[$type], $pos, 1, array(
            $key => $options,
          ));
        }

        // Add in defaults.
        $attached[$type][$key] += array(
          'preprocess' => FALSE,
        );
      }
    }
  }
}

/**
 * Helper function to remove a portion of an array and replace it with something else.
 */
function _skinr_array_splice(array &$array, $offset, $length = 0, array $replacement = NULL) {
  if (is_null($replacement)) {
    $replacement = array();
  }
  $array = array_slice($array, 0, $offset, true) + $replacement + array_slice($array, $offset + $length, NULL, true);
}

/**
 * Parse a skin_infos array as returned from a skins plugin.
 *
 * This function inserts any missing defaults and updates the stylesheet and
 * script paths to be relative to Drupal's root.
 *
 * @param $skin_infos
 *   An array of skins as returned from skin plugins.
 * @param $source
 *   An associative array containing information about the source of the skin.
 *   See skinr_implements() for details.
 *
 * @todo Merge into skinr_get_skin_info() and remove this function.
 */
function skinr_skin_info_process(&$skin_infos, $source) {
  foreach ($skin_infos as $skin_name => $skin_info) {

    // Populate default properties.
    $skin_infos[$skin_name] += array(
      'name' => '',
      'title' => '',
      'type' => 'checkboxes',
      'description' => '',
      'group' => 'general',
      'theme hooks' => array(
        '*',
      ),
      'attached' => array(),
      'options' => array(),
      'weight' => NULL,
      'default status' => 0,
      'status' => array(),
    );

    // Merge in name.
    $skin_infos[$skin_name]['name'] = $skin_name;

    // Merge in source information.
    $skin_infos[$skin_name]['source'] = $source;

    // Merge in default status for all themes.
    $skin_infos[$skin_name]['status'] = skinr_skin_info_status_default($skin_infos[$skin_name]);
    _skinr_preprocess_attached($skin_infos[$skin_name]['attached'], $source);
    foreach ($skin_infos[$skin_name]['options'] as $option_name => $option) {
      if (isset($option['attached'])) {
        _skinr_preprocess_attached($skin_infos[$skin_name]['options'][$option_name]['attached'], $source);
      }

      // Validate class by running it through drupal_html_class().
      if (!is_array($skin_infos[$skin_name]['options'][$option_name]['class'])) {

        // Raise an error.
        watchdog('skinr', 'The class for option %option in skin %skin needs to be an array.', array(
          '%option' => $option_name,
          '%skin' => $skin_name,
        ), WATCHDOG_WARNING);

        // Reset to array to prevent errors.
        $skin_infos[$skin_name]['options'][$option_name]['class'] = array();
      }
      foreach ($skin_infos[$skin_name]['options'][$option_name]['class'] as $key => $class) {
        $skin_infos[$skin_name]['options'][$option_name]['class'][$key] = drupal_html_class($class);
      }
    }
  }
}

/**
 * Retrieves all skins registered by modules and themes.
 *
 * @return
 *   An array of skins.
 */
function skinr_get_skin_info() {
  $skin_info =& drupal_static(__FUNCTION__);
  if (!isset($skin_info)) {
    if ($cached = cache_get('skinr_skin_info')) {
      $skin_info = $cached->data;
      return $skin_info;
    }
    $skin_info = array();
    foreach (skinr_implements_api() as $name => $extension) {
      $hooks = array();

      // Run through skinr_hook to ensure the required include gets loaded.
      if (skinr_hook($name, 'skinr_skin_info')) {
        $hooks["{$name}_skinr_skin_info"] = $extension;
      }

      // Load the extension's plugins, if any.
      if ($files = skinr_load_plugins($extension)) {

        // The base path for plugins is the directory defined by the extension.
        $dir = $extension['path'] . '/' . $extension['directory'];
        foreach ($files as $plugin => $file) {
          $path = $dir . '/' . basename(dirname($file->uri));
          $hooks["{$name}_skinr_skin_{$plugin}_info"] = array(
            // The source path for a plugin is the plugin directory.
            'path' => $path,
            'include file' => $path . '/' . $file->filename,
          ) + $extension;
        }
      }
      foreach ($hooks as $function => $source) {
        if (function_exists($function)) {
          $extension_info = $function();
          if (isset($extension_info) && is_array($extension_info)) {

            // Prepare the skin information.
            skinr_skin_info_process($extension_info, $source);
            $skin_info += $extension_info;
          }
        }
      }
    }

    // Allow modules to alter registered skin information.
    drupal_alter('skinr_skin_info', $skin_info);
    cache_set('skinr_skin_info', $skin_info);
  }
  return $skin_info;
}

/**
 * Retrieves all skin groups registered by modules and themes.
 *
 * @return
 *   An array of groups.
 */
function skinr_get_group_info() {
  $group_info =& drupal_static(__FUNCTION__);
  if (!isset($group_info)) {
    if ($cached = cache_get('skinr_group_info')) {
      $group_info = $cached->data;
      return $group_info;
    }
    $group_info = array();
    foreach (skinr_implements_api() as $name => $extension) {
      $hooks = array();

      // Run through skinr_hook to ensure the required include gets loaded.
      if (skinr_hook($name, 'skinr_group_info')) {
        $hooks["{$name}_skinr_group_info"] = $extension;
      }

      // Load the extension's plugins, if any.
      if ($files = skinr_load_plugins($extension)) {

        // The base path for plugins is the directory defined by the extension.
        $dir = $extension['path'] . '/' . $extension['directory'];
        foreach ($files as $plugin => $file) {
          $path = $dir . '/' . basename(dirname($file->uri));
          $hooks["{$name}_skinr_group_{$plugin}_info"] = array(
            // The source path for a plugin is the plugin directory.
            'path' => $path,
            'include file' => $path . '/' . $file->filename,
          ) + $extension;
        }
      }
      foreach ($hooks as $function => $source) {
        if (function_exists($function)) {
          $extension_info = $function();
          if (isset($extension_info) && is_array($extension_info)) {

            // Prepare the skin group information.
            foreach ($extension_info as &$group) {
              $group += array(
                'title' => '',
                'description' => '',
                'weight' => 0,
              );
            }
            $group_info += $extension_info;
          }
        }
      }
    }

    // Allow modules to alter groups through hook_skinr_group_info_alter().
    drupal_alter('skinr_group_info', $group_info);
    cache_set('skinr_group_info', $group_info);
  }
  return $group_info;
}

/**
 * Fetch Skinr configuration data from functionality plugins.
 *
 * @return
 *   An array of all configuration data.
 */
function skinr_get_config_info() {
  $config_info =& drupal_static(__FUNCTION__);
  if (!isset($config_info)) {
    if ($cached = cache_get('skinr_config_info')) {
      $config_info = $cached->data;
      return $config_info;
    }
    $config_info = skinr_invoke_all('skinr_config_info');

    // Allow modules to alter config info via hook_skinr_config_info_alter().
    drupal_alter('skinr_config_info', $config_info);
    cache_set('skinr_config_info', $config_info);
  }
  return $config_info;
}

/**
 * Provide a list of all available theme hooks for a given element.
 *
 * @param $module
 *   The module implementing given element.
 * @param $element
 *   An element.
 *
 * @return
 *   An array of theme hooks.
 */
function skinr_theme_hooks($module, $element) {
  $theme_hooks =& drupal_static(__FUNCTION__, array());
  if (!isset($theme_hooks[$module][$element])) {

    // Invoke hook_skinr_theme_hooks() and hook_skinr_theme_hooks_alter().
    $theme_hooks[$module][$element] = skinr_invoke_all('skinr_theme_hooks', $module, $element);
    drupal_alter('skinr_theme_hooks', $theme_hooks[$module][$element], $module, $element);
  }
  return $theme_hooks[$module][$element];
}

/**
 * Implements hook_modules_enabled().
 */
function skinr_modules_enabled($modules) {
  skinr_cache_reset();
  foreach ($modules as $module) {
    skinr_skin_defaults_import($module);
  }
}

/**
 * Implements hook_modules_disabled().
 */
function skinr_modules_disabled() {
  skinr_cache_reset();
}

/**
 * Implements hook_themes_enabled().
 */
function skinr_themes_enabled() {
  skinr_cache_reset();
}

/**
 * Implements hook_themes_disabled().
 */
function skinr_themes_disabled() {
  skinr_cache_reset();
}

/**
 * Helper function for built-in integration code.
 */
function skinr_skinr_api_modules() {
  return array(
    'path' => drupal_get_path('module', 'skinr') . '/modules',
  );
}
function block_skinr_api_2() {
  return skinr_skinr_api_modules();
}
function comment_skinr_api_2() {
  return skinr_skinr_api_modules();
}
function node_skinr_api_2() {
  return skinr_skinr_api_modules();
}
function system_skinr_api_2() {
  return skinr_skinr_api_modules();
}
function views_skinr_api_2() {
  return skinr_skinr_api_modules();
}

Functions

Namesort descending Description
block_skinr_api_2
comment_skinr_api_2
node_skinr_api_2
skinr_cache_reset Clears cached Skinr information.
skinr_current_theme Helper function to retrieve the current theme.
skinr_exit Implements hook_exit().
skinr_flatten_skins_array Returns an array of classes.
skinr_get_config_info Fetch Skinr configuration data from functionality plugins.
skinr_get_group_info Retrieves all skin groups registered by modules and themes.
skinr_get_skin_info Retrieves all skins registered by modules and themes.
skinr_help Implements hook_help().
skinr_hook Determine whether a module implements a hook.
skinr_hook_info Implements hook_hook_info().
skinr_implements Determine which modules are implementing a hook.
skinr_implements_api Returns a list of extensions that implement this API version of Skinr.
skinr_include_once file_scan_directory() callback wrapper around include_once.
skinr_invoke_all Invoke a hook in all enabled modules and themes that implement it.
skinr_load_include Loads a $module.skinr.inc include file.
skinr_load_plugins Includes Skinr plugin files for an extension, if any.
skinr_modules_disabled Implements hook_modules_disabled().
skinr_modules_enabled Implements hook_modules_enabled().
skinr_object_export Export object function.
skinr_preprocess Implements hook_preprocess().
skinr_skinr_api_modules Helper function for built-in integration code.
skinr_skin_defaults_import Imports default skin confiuration objects from code into database.
skinr_skin_delete Delete a skin object.
skinr_skin_delete_multiple Delete multiple skin configuration objects.
skinr_skin_export Output a skin configuration object as code suitable for skinr_skin_defaults().
skinr_skin_get_sids Get skin configuration IDs.
skinr_skin_import Import a skin configuration object as defined in skinr_skin_defaults().
skinr_skin_info_process Parse a skin_infos array as returned from a skins plugin.
skinr_skin_info_status_default Prepare the default status for a skin.
skinr_skin_info_status_get Retrieve the overridden status of a skin.
skinr_skin_info_status_set Set the status of a skin. Overrides the skin plugin settings.
skinr_skin_load Load a skin configuration object from the database.
skinr_skin_load_by_uuid Load a skin configuration object from the database using UUID.
skinr_skin_load_multiple Load skin configuration objects from the database.
skinr_skin_load_unchanged Load an uncached version of a skin configuration object.
skinr_skin_revert Revert a skin configuration object that's stored in code to its default state.
skinr_skin_save Save a skin object.
skinr_skin_sid_to_uuid Get skin configuration IDs.
skinr_skin_storage Returns a skin configuration object's storage method.
skinr_skin_uuid_to_sid Get skin configuration IDs.
skinr_skin_validate Validate a skinr object.
skinr_themes_disabled Implements hook_themes_disabled().
skinr_themes_enabled Implements hook_themes_enabled().
skinr_theme_hooks Provide a list of all available theme hooks for a given element.
skinr_var_export Export var function.
system_skinr_api_2
views_skinr_api_2
_skinr_array_splice Helper function to remove a portion of an array and replace it with something else.
_skinr_array_strip_empty Helper function to remove empty skins from an array.
_skinr_is_local_file Helper function to determine whether or not a given file is local or not.
_skinr_preprocess_attached
_skinr_skin_get_defaults Returns all default skin configuration objects.
_skinr_skin_import

Constants

Namesort descending Description
SKINR_STORAGE_IN_CODE
SKINR_STORAGE_IN_CODE_OVERRIDDEN
SKINR_STORAGE_IN_DATABASE A flag used to let us know if an object is in the database.
SKINR_VERSION The Skinr API version.