Index: skinr.handlers.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skinr/skinr.handlers.inc,v
retrieving revision 1.9
diff -u -p -r1.9 skinr.handlers.inc
--- skinr.handlers.inc	20 Dec 2010 21:42:29 -0000	1.9
+++ skinr.handlers.inc	10 Jan 2011 22:15:55 -0000
@@ -140,26 +140,37 @@ function skinr_submit_handler(&$form, $f
   }
 }
 
-// Declare API compatibility on behalf of core modules:
+/**
+ * Implements hook_skinr_api_VERSION().
+ */
+function skinr_skinr_api_2() {
+}
 
 /**
- * Implements hook_skinr_api().
- *
- * This one is used as the base to reduce errors when updating.
+ * Helper function for built-in integration code.
  */
-function skinr_skinr_api() {
+function skinr_skinr_api_modules() {
   return array(
-    'api' => 1,
     'path' => drupal_get_path('module', 'skinr') . '/modules',
   );
 }
 
-function block_skinr_api() { return skinr_skinr_api(); }
+function block_skinr_api_2() {
+  return skinr_skinr_api_modules();
+}
 
-function comment_skinr_api() { return skinr_skinr_api(); }
+function comment_skinr_api_2() {
+  return skinr_skinr_api_modules();
+}
 
-function node_skinr_api() { return skinr_skinr_api(); }
+function node_skinr_api_2() {
+  return skinr_skinr_api_modules();
+}
 
-function panels_skinr_api() { return skinr_skinr_api(); }
+function panels_skinr_api_2() {
+  return skinr_skinr_api_modules();
+}
 
-function views_skinr_api() { return skinr_skinr_api(); }
+function views_skinr_api_2() {
+  return skinr_skinr_api_modules();
+}
Index: skinr.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skinr/skinr.module,v
retrieving revision 1.37
diff -u -p -r1.37 skinr.module
--- skinr.module	20 Dec 2010 21:42:29 -0000	1.37
+++ skinr.module	10 Jan 2011 22:48:46 -0000
@@ -7,6 +7,11 @@
  */
 
 /**
+ * The Skinr API version.
+ */
+define('SKINR_VERSION', 2);
+
+/**
  * Implements hook_help().
  */
 function skinr_help($path, $arg) {
@@ -23,6 +28,19 @@ function skinr_help($path, $arg) {
 }
 
 /**
+ * Implements hook_hook_info().
+ */
+function skinr_hook_info() {
+  $hooks = array(
+    'skinr_api_2',
+  );
+  $hooks = array_fill_keys($hooks, array(
+    'group' => 'skinr',
+  ));
+  return $hooks;
+}
+
+/**
  * Implements hook_module_implements_alter().
  */
 function skinr_module_implements_alter(&$implementations, $hook) {
@@ -36,10 +54,12 @@ function skinr_module_implements_alter(&
 
 /**
  * Implements hook_init().
+ *
+ * @todo Kill me. Entirely.
  */
 function skinr_init() {
   module_load_include('inc', 'skinr', 'skinr.handlers');
-  skinr_module_load_all_includes();
+  skinr_load_includes();
 }
 
 /**
@@ -64,8 +84,10 @@ function skinr_preprocess(&$variables, $
       $preprocess_settings = $module_settings['preprocess'][$original_hook];
       $sids = skinr_handler('preprocess_index_handler', 'preprocess', $preprocess_settings['index_handler'], $variables);
 
+      // @todo Kill skinr_skin_extract(), and only store what has been
+      //   configured, enabled, and should be actively applied, in order to use
+      //   drupal_process_attached().
       if ($extracted = skinr_skin_extract($module, $sids, $current_theme)) {
-        // @todo How can this be done better with drupal_process_attached()?
         foreach ($extracted['css'] as $file) {
           if ($file['enabled']) {
             drupal_add_css($file['path'], $file['file_options']);
@@ -417,37 +439,84 @@ function skinr_rule_visible($rid) {
   return FALSE;
 }
 
-// ------------------------------------------------------------------
-// Include file helpers.
-
-/**
- * Includes $module.skinr.inc files.
- */
-function skinr_module_load_all_includes() {
-  foreach (skinr_get_module_apis() as $module => $info) {
-    $file = DRUPAL_ROOT . "/$info[path]/$module.skinr.inc";
-    if (is_file($file)) {
-      require_once $file;
-    }
-  }
-}
-
 /**
- * Get a list of modules that support skinr.
+ * 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_get_module_apis() {
+function skinr_implements() {
   $cache = &drupal_static(__FUNCTION__);
 
   if (!isset($cache)) {
     $cache = array();
-    foreach (module_implements('skinr_api') as $module) {
-      $function = $module . '_skinr_api';
+    // 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).
+    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,
+      );
+      // Check whether the hook returns any information.
+      $function = $module . '_skinr_api_' . SKINR_VERSION;
       $info = $function();
-      if (isset($info['api']) && $info['api'] == 1.000) {
-        if (!isset($info['path'])) {
-          $info['path'] = drupal_get_path('module', $module);
+      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() does not
+      // work for Skinr's implementations on behalf of core modules.
+      if (isset($cache[$module]['path'])) {
+        $file = DRUPAL_ROOT . '/' . $cache[$module]['path'] . '/' . $module . '.skinr.inc';
+        if (file_exists($file)) {
+          require_once $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.
+    foreach (list_themes() as $name => $theme) {
+      if (!empty($theme->status) && 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,
+        );
+        // 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 = DRUPAL_ROOT . '/' . $cache[$name]['path'] . '/' . $name . '.skinr.inc';
+        if (file_exists($file)) {
+          require_once $file;
         }
-        $cache[$module] = $info;
       }
     }
   }
@@ -455,6 +524,58 @@ function skinr_get_module_apis() {
   return $cache;
 }
 
+/**
+ * Includes $extension.skinr.inc files of extensions compatible with this version of Skinr.
+ *
+ * @todo Shoot me. Twice.
+ */
+function skinr_load_includes() {
+  foreach (skinr_implements() as $extension) {
+    $file = DRUPAL_ROOT . '/' . $extension['path'] . '/' . $extension['name'] . '.skinr.inc';
+    if (file_exists($file)) {
+      require_once $file;
+    }
+  }
+}
+
+/**
+ * Includes Skinr plugin files for an extension, if any.
+ *
+ * @param $extension
+ *   The API information for an extension, as returned by skinr_implements().
+ */
+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' => FALSE,
+      '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.
 
@@ -702,24 +823,12 @@ function skinr_current_theme($exclude_ad
 }
 
 /**
- * Prepare defaults for groups.
- *
- * @return
- *   An array of default group settings.
- */
-function skinr_group_info_default() {
-  return array(
-    'title' => '',
-    'description' => '',
-    'weight' => NULL,
-  );
-}
-
-/**
  * Prepare defaults for skins.
  *
  * @return
  *   An array of default skin settings.
+ *
+ * @todo Merge into skinr_get_skin_info() and remove this function.
  */
 function skinr_skin_info_default() {
   return array(
@@ -758,24 +867,6 @@ function skinr_skin_info_status_default(
 }
 
 /**
- * Get a list of plugin hooks.
- *
- * @return
- *   An array of hook details keyed by hook name. The hook details contain the
- *   following keys:
- *   - 'type': Available options are 'module' or 'theme'.
- *   - 'name': The name of the owner module or theme of this plugin.
- *   - 'plugin': The name of the skins plugin.
- *   - 'path': This plugin's path.
- */
-function skinr_plugin_hooks() {
-  // @todo Load the include files and get an array of hooks; we need to
-  //   know which module and which plugin comprises the hook.
-  $hooks = array();
-  return $hooks;
-}
-
-/**
  * Helper function to prepend a path to an array of stylesheet or script filenames.
  *
  * If the url is absolute (e.g. the url start with 'http://' or 'https://')
@@ -813,11 +904,10 @@ function _skinr_add_path_to_files(&$file
  * @param $skin_infos
  *   A skins array as returned from a skins plugin.
  * @param $source
- *   An array with the following keys:
- *   - 'type': Available options are 'module' or 'theme'.
- *   - 'name': The name of the owner module or theme of this plugin.
- *   - 'plugin': The name of the skins plugin.
- *   - 'path': This plugin's path.
+ *   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) {
@@ -833,9 +923,6 @@ function skinr_skin_info_process(&$skin_
     // Merge in default statuses for all themes.
     $skin_infos[$skin_name]['status'] = array_merge(skinr_skin_info_status_default($skin_infos[$skin_name]['default_status']), $skin_infos[$skin_name]['status']);
 
-    // @todo Apply overridden statuses. This will need to be loaded from
-    //   database.
-
     // Add path to stylesheets.
     if (isset($skin_infos[$skin_name]['attached']['css'])) {
       _skinr_add_path_to_files($skin_infos[$skin_name]['attached']['css'], $source['path']);
@@ -867,82 +954,111 @@ function skinr_skin_info_process(&$skin_
 }
 
 /**
- * Retrieves a list of all available skins.
+ * Retrieves all skins registered by modules and themes.
  *
  * @return
- *    An array of skins.
+ *   An array of skins.
  */
 function skinr_get_skin_info() {
-  $skin_infos = &drupal_static(__FUNCTION__);
+  $skin_info = &drupal_static(__FUNCTION__);
 
-  if (!isset($skin_infos)) {
-    // Set skins to an array to avoid errors down the line if no skins
-    // are found.
-    $skin_infos = array();
-
-    // Load skins from hook_skinr_skins().
-    $hooks = skinr_plugin_hooks();
-    foreach ($hooks as $hook => $source) {
-      $function = $hook . '_skinr_skin_info';
-      $result = $function();
-      if (isset($result)) {
-        if (!is_array($result)) {
-          $result = array($result);
+  if (!isset($skin_info)) {
+    if ($cached = cache_get('skinr_skin_info')) {
+      $skin_info = $cached->data;
+      return $skin_info;
+    }
+    $skin_info = array();
+
+    foreach (skinr_implements() as $name => $extension) {
+      $hooks = array(
+        "{$name}_skinr_skin_info" => $extension,
+      );
+      // Load the extension's plugins, if any.
+      if ($files = skinr_load_plugins($extension)) {
+        // The source path for a plugin is the directory it is contained in.
+        $dir = $extension['path'] . '/' . $extension['directory'];
+        foreach ($files as $plugin => $file) {
+          $hooks["{$name}_skinr_skin_{$plugin}_info"] = array(
+            'path' => $dir,
+          ) + $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);
 
-        // Make any programatic changes.
-        skinr_skin_info_process($result, $source);
-
-        $skin_infos = array_merge_recursive($skin_infos, $result);
+            $skin_info += $extension_info;
+          }
+        }
       }
     }
 
-    // Allow modules to alter skin infos through hook_skinr_skin_info_alter().
-    drupal_alter('skinr_skin_info', $skin_infos);
+    // Allow modules to alter registered skin information.
+    drupal_alter('skinr_skin_info', $skin_info);
+
+    cache_set('skinr_skin_info', $skin_info);
   }
 
-  return $skin_infos;
+  return $skin_info;
 }
 
 /**
- * Retrieves a list of all available groups.
+ * Retrieves all skin groups registered by modules and themes.
  *
  * @return
- *    An array of groups.
+ *   An array of groups.
  */
 function skinr_get_group_info() {
-  $group_infos = &drupal_static(__FUNCTION__);
+  $group_info = &drupal_static(__FUNCTION__);
 
-  if (!isset($group_infos)) {
-    // Set groups to an array to avoid errors down the line if no groups
-    // are found.
-    $group_infos = array();
-
-    // Load skins from hook_skinr_groups().
-    $hooks = skinr_plugin_hooks();
-    foreach ($hooks as $hook => $source) {
-      $function = $hook . '_skinr_group_info';
-      $result = $function();
-      if (isset($result)) {
-        if (!is_array($result)) {
-          $result = array($result);
+  if (!isset($group_info)) {
+    if ($cached = cache_get('skinr_group_info')) {
+      $group_info = $cached->data;
+      return $group_info;
+    }
+    $group_info = array();
+
+    foreach (skinr_implements() as $name => $extension) {
+      $hooks = array(
+        "{$name}_skinr_group_info" => $extension,
+      );
+      // Load the extension's plugins, if any.
+      if ($files = skinr_load_plugins($extension)) {
+        // The source path for a plugin is the directory it is contained in.
+        $dir = $extension['path'] . '/' . $extension['directory'];
+        foreach ($files as $plugin => $file) {
+          $hooks["{$name}_skinr_group_{$plugin}_info"] = array(
+            'path' => $dir,
+          ) + $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.
+            $extension_info += array(
+              'title' => '',
+              'description' => '',
+              'weight' => 0,
+            );
 
-        // Make any programatic changes.
-        // Merge in defaults.
-        foreach ($result as $group_name => $group_info) {
-          $result[$group_name] = array_merge(skinr_group_info_default(), $group_info);
+            $group_info += $extension_info;
+          }
         }
-
-        $group_infos = array_merge_recursive($group_infos, $result);
       }
     }
 
     // Allow modules to alter groups through hook_skinr_group_info_alter().
-    drupal_alter('skinr_group_info', $group_infos);
+    drupal_alter('skinr_group_info', $group_info);
+
+    cache_set('skinr_group_info', $group_info);
   }
 
-  return $group_infos;
+  return $group_info;
 }
 
 /**
@@ -952,14 +1068,32 @@ function skinr_get_group_info() {
  *   An array of all configuration data.
  */
 function skinr_get_config_info() {
-  $config = &drupal_static(__FUNCTION__);
+  $config_info = &drupal_static(__FUNCTION__);
 
   if (!isset($config)) {
-    $config = module_invoke_all('skinr_config_info');
-    drupal_alter('skinr_config_info', $config);
+    if ($cached = cache_get('skinr_config_info')) {
+      $config_info = $cached->data;
+      return $config_info;
+    }
+    $config_info = array();
+
+    foreach (skinr_implements() as $name => $extension) {
+      $function = "{$name}_skinr_config_info";
+      if (function_exists($function)) {
+        $extension_info = $function();
+        if (isset($extension_info) && is_array($extension_info)) {
+          $config_info = array_merge_recursive($config_info, $extension_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;
+  return $config_info;
 }
 
 /**
Index: skinr.skinr.inc
===================================================================
RCS file: skinr.skinr.inc
diff -N skinr.skinr.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ skinr.skinr.inc	10 Jan 2011 22:20:09 -0000
@@ -0,0 +1,176 @@
+<?php
+// $Id: skinr.skinr.inc,v 1.9 2010/11/27 23:22:27 jgirlygirl Exp $
+
+/**
+ * @file
+ * Provide skinr handling for page level rules.
+ */
+
+/**
+ * @defgroup skinr page rule handlers
+ * @{
+ */
+
+/**
+ * Implementation of hook_skinr_config_info().
+ */
+function skinr_skinr_config_info() {
+  $data['rules']['form']['skinr_rule_edit'] = array(
+    'index_handler' => 'rules_skinr_form_index_handler',
+    'preprocess_hook_callback' => 'rules_skinr_preprocess_hook_callback',
+    'title' => t('rule settings'),
+    'skinr_weight' => 0,
+    'collapsed' => FALSE,
+  );
+  $data['rules']['form']['skinr_ui_form'] = array(
+    'preprocess_hook_callback' => 'rules_skinr_preprocess_hook_callback',
+    'title' => t('rule settings'),
+    'collapsed' => FALSE,
+  );
+  $data['rules']['preprocess']['html'] = array(
+    'index_handler' => 'rules_skinr_preprocess_index_handler',
+  );
+  $data['rules']['preprocess']['region'] = array(
+    'index_handler' => 'rules_skinr_preprocess_index_handler',
+  );
+  $data['rules']['contextual_links']['html'] = array(
+    'contextual_links_handler' => 'rules_skinr_contextual_links',
+  );
+  $data['rules']['contextual_links']['region'] = array(
+    'contextual_links_handler' => 'rules_skinr_contextual_links',
+  );
+
+  return $data;
+}
+
+/**
+ * Skinr form index handler.
+ *
+ * @param $op
+ *   What kind of action is being performed. Possible values:
+ *   - 'form': the form elements for Skinr are being inserted in a form.
+ *   - 'submit': the form has been submitted.
+ * @param &$form
+ *   - For 'form', passes in the $form parameter from hook_form_alter().
+ *   - For 'submit', passes in the $form parameter from hook_form_submit().
+ * @param $form_state
+ *   - For 'form', passes in the $form_state parameter from hook_form_alter().
+ *   - For 'submit', passes in the $form_state parameter from hook_form_submit().
+ * @return
+ *   The index where we can find our values in Skinr's data structure.
+ */
+function rules_skinr_form_index_handler($op, &$form, $form_state) {
+  switch ($op) {
+    case 'form':
+      if (!empty($form['rule']['rid']['#value'])) {
+        return $form['rule']['rid']['#value'];
+      }
+      else {
+        return 0;
+      }
+
+    case 'submit':
+      return $form_state['values']['rid'];
+  }
+}
+
+/**
+ * Skinr preprocess_hook_callback.
+ *
+ * @param &$form
+ *   Passes in the $form parameter from hook_form_alter().
+ * @param $form_state
+ *   Passes in the $form_state parameter from hook_form_alter().
+ * @return
+ *   The preprocess_hook we wish to use.
+ */
+function rules_skinr_preprocess_hook_callback(&$form, $form_state) {
+  $preprocess_hooks = array();
+
+  if (!empty($form['rule'])) {
+    $hooks = explode('__', $form['rule']['rule_type']['#value']);
+  }
+  else {
+    $rule = skinr_rule_load($form['skinr']['sid']['#value']);
+    $hooks = explode('__', $rule->rule_type);
+  }
+  while (count($hooks)) {
+    $preprocess_hooks[] = implode('__', $hooks);
+    array_pop($hooks);
+  }
+
+  return $preprocess_hooks;
+}
+
+/**
+ * Skinr preprocess index handler.
+ *
+ * @param &$variables
+ *   Passes in the $variables parameter from module_preprocess().
+ * @return
+ *   The index where we can find our values in Skinr's data structure. If an
+ *   array is returned, it will loop through each index in Skinr's data
+ *   structure and merge the returned classes.
+ */
+function rules_skinr_preprocess_index_handler(&$variables) {
+  if (!empty($variables['region'])) {
+    $rule_type = 'region__' . $variables['region'];
+  }
+  else {
+    $rule_type = 'page';
+  }
+  $rules = skinr_rule_load_multiple(array(), array('rule_type' => $rule_type));
+
+  // Find any page level skinr options and return an array of them.
+  $indices = array();
+  foreach ($rules as $rule) {
+    if (skinr_rule_visible($rule->rid)) {
+      $indices[] = $rule->rid;
+    }
+  }
+  return $indices;
+}
+
+/**
+ * Skinr contextual links handler.
+ *
+ * @param &$variables
+ *   Passes in the $variables parameter from skinr_preprocess().
+ * @return
+ *   An associative array. Each value is an array that forms the function
+ *   arguments for menu_contextual_links(). For example:
+ *   @code
+ *    $links = array(
+ *      'skinr-modulename' => array(
+ *        'admin/appearance/skinr/edit', array('system', 'navigation')),
+ *      ),
+ *      'skinr-modulename-1' => array(
+ *        'admin/appearance/skinr/edit', array('system', 'something-else')),
+ *      ),
+ *    );
+ *   @endcode
+ */
+function rules_skinr_contextual_links(&$variables) {
+  if (!empty($variables['region'])) {
+    $rule_type = 'region__' . $variables['region'];
+  }
+  else {
+    $rule_type = 'page';
+  }
+  $rules = skinr_rule_load_multiple(array(), array('rule_type' => 'page'));
+  $links = array();
+  $counter = 1;
+
+  foreach ($rules as $rule) {
+    if (skinr_rule_visible($rule->rid)) {
+      $links['skinr-rule-' . $counter++] = array(
+        'admin/config/skinr/edit/nojs', array('page', $rule->rid),
+      );
+    }
+  }
+  return $links;
+}
+
+/**
+ * @}
+ */
Index: modules/skinr.skinr.inc
===================================================================
RCS file: modules/skinr.skinr.inc
diff -N modules/skinr.skinr.inc
--- modules/skinr.skinr.inc	27 Nov 2010 23:22:27 -0000	1.9
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,176 +0,0 @@
-<?php
-// $Id: skinr.skinr.inc,v 1.9 2010/11/27 23:22:27 jgirlygirl Exp $
-/**
- * @file
- * Provide skinr handling for page level rules.
- */
-
-/**
- * @defgroup skinr page rule handlers
- *
- * @{
- */
-
-/**
- * Implementation of hook_skinr_config_info().
- */
-function skinr_skinr_config_info() {
-  $data['rules']['form']['skinr_rule_edit'] = array(
-    'index_handler' => 'rules_skinr_form_index_handler',
-    'preprocess_hook_callback' => 'rules_skinr_preprocess_hook_callback',
-    'title' => t('rule settings'),
-    'skinr_weight' => 0,
-    'collapsed' => FALSE,
-  );
-  $data['rules']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'rules_skinr_preprocess_hook_callback',
-    'title' => t('rule settings'),
-    'collapsed' => FALSE,
-  );
-  $data['rules']['preprocess']['html'] = array(
-    'index_handler' => 'rules_skinr_preprocess_index_handler',
-  );
-  $data['rules']['preprocess']['region'] = array(
-    'index_handler' => 'rules_skinr_preprocess_index_handler',
-  );
-  $data['rules']['contextual_links']['html'] = array(
-    'contextual_links_handler' => 'rules_skinr_contextual_links',
-  );
-  $data['rules']['contextual_links']['region'] = array(
-    'contextual_links_handler' => 'rules_skinr_contextual_links',
-  );
-
-  return $data;
-}
-
-/**
- * Skinr form index handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - 'form': the form elements for Skinr are being inserted in a form.
- *   - 'submit': the form has been submitted.
- * @param &$form
- *   - For 'form', passes in the $form parameter from hook_form_alter().
- *   - For 'submit', passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   - For 'form', passes in the $form_state parameter from hook_form_alter().
- *   - For 'submit', passes in the $form_state parameter from hook_form_submit().
- * @return
- *   The index where we can find our values in Skinr's data structure.
- */
-function rules_skinr_form_index_handler($op, &$form, $form_state) {
-  switch ($op) {
-    case 'form':
-      if (!empty($form['rule']['rid']['#value'])) {
-        return $form['rule']['rid']['#value'];
-      }
-      else {
-        return 0;
-      }
-
-    case 'submit':
-      return $form_state['values']['rid'];
-  }
-}
-
-/**
- * Skinr preprocess_hook_callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- * @return
- *   The preprocess_hook we wish to use.
- */
-function rules_skinr_preprocess_hook_callback(&$form, $form_state) {
-  $preprocess_hooks = array();
-
-  if (!empty($form['rule'])) {
-    $hooks = explode('__', $form['rule']['rule_type']['#value']);
-  }
-  else {
-    $rule = skinr_rule_load($form['skinr']['sid']['#value']);
-    $hooks = explode('__', $rule->rule_type);
-  }
-  while (count($hooks)) {
-    $preprocess_hooks[] = implode('__', $hooks);
-    array_pop($hooks);
-  }
-
-  return $preprocess_hooks;
-}
-
-/**
- * Skinr preprocess index handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from module_preprocess().
- * @return
- *   The index where we can find our values in Skinr's data structure. If an
- *   array is returned, it will loop through each index in Skinr's data
- *   structure and merge the returned classes.
- */
-function rules_skinr_preprocess_index_handler(&$variables) {
-  if (!empty($variables['region'])) {
-    $rule_type = 'region__' . $variables['region'];
-  }
-  else {
-    $rule_type = 'page';
-  }
-  $rules = skinr_rule_load_multiple(array(), array('rule_type' => $rule_type));
-
-  // Find any page level skinr options and return an array of them.
-  $indices = array();
-  foreach ($rules as $rule) {
-    if (skinr_rule_visible($rule->rid)) {
-      $indices[] = $rule->rid;
-    }
-  }
-  return $indices;
-}
-
-/**
- * Skinr contextual links handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from skinr_preprocess().
- * @return
- *   An associative array. Each value is an array that forms the function
- *   arguments for menu_contextual_links(). For example:
- *   @code
- *    $links = array(
- *      'skinr-modulename' => array(
- *        'admin/appearance/skinr/edit', array('system', 'navigation')),
- *      ),
- *      'skinr-modulename-1' => array(
- *        'admin/appearance/skinr/edit', array('system', 'something-else')),
- *      ),
- *    );
- *   @endcode
- */
-function rules_skinr_contextual_links(&$variables) {
-  if (!empty($variables['region'])) {
-    $rule_type = 'region__' . $variables['region'];
-  }
-  else {
-    $rule_type = 'page';
-  }
-  $rules = skinr_rule_load_multiple(array(), array('rule_type' => 'page'));
-  $links = array();
-  $counter = 1;
-
-  foreach ($rules as $rule) {
-    if (skinr_rule_visible($rule->rid)) {
-      $links['skinr-rule-' . $counter++] = array(
-        'admin/config/skinr/edit/nojs', array('page', $rule->rid),
-      );
-    }
-  }
-  return $links;
-}
-
-/**
- * @}
- */
Index: tests/skinr.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skinr/tests/skinr.test,v
retrieving revision 1.4
diff -u -p -r1.4 skinr.test
--- tests/skinr.test	20 Dec 2010 21:42:30 -0000	1.4
+++ tests/skinr.test	10 Jan 2011 22:46:41 -0000
@@ -24,6 +24,9 @@ class SkinrInstallationTestCase extends 
     parent::setUp();
   }
 
+  /**
+   * Tests installation and uninstallation of Skinr modules.
+   */
   function testInstallation() {
     $this->admin_user = $this->drupalCreateUser(array(
       'access administration pages',
@@ -78,3 +81,115 @@ class SkinrInstallationTestCase extends 
     $this->assertEqual($count, 0, t('No variables found.'));
   }
 }
+
+/**
+ * Tests API functionality.
+ */
+class SkinrApiTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'API',
+      'description' => 'Tests Skinr API functionality.',
+      'group' => 'Skinr',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('skinr', 'skinr_test', 'skinr_test_incompatible'));
+  }
+
+  /**
+   * Tests skinr_implements().
+   */
+  function testSkinrImplements() {
+    // Verify that skinr_implements() only returns extensions that are
+    // compatible with this version of Skinr.
+    $extensions = skinr_implements();
+
+    // The expected extensions and their specific properties, if any.
+    $all_expected = array(
+      // Skinr is always expected.
+      'skinr' => array(),
+      // Node is a required core module, so always expected.
+      'node' => array(
+        'path' => drupal_get_path('module', 'skinr') . '/modules',
+      ),
+      // skinr_test has been installed.
+      'skinr_test' => array(
+        'directory' => 'skins',
+      ),
+    );
+    foreach ($all_expected as $name => $expected) {
+      // Populate defaults.
+      $expected += array(
+        'type' => 'module',
+        'name' => $name,
+      );
+      $expected += array(
+        'path' => drupal_get_path($expected['type'], $name),
+        'directory' => NULL,
+      );
+      $this->assertEqual($extensions[$name], $expected, t('%extension implementation found.', array(
+        '%extension' => $name,
+      )));
+      unset($extensions[$name]);
+    }
+    // Ensure that skinr_test_incompatible is not contained.
+    $this->assertTrue(!isset($extensions['skinr_test_incompatible']), 'Incompatible extension not found.');
+    // After asserting all expected, the list of extensions should be empty.
+    $this->assertTrue(empty($extensions), 'No unexpected extensions found.');
+  }
+
+  /**
+   * Tests hook_skinr_skin_info().
+   */
+  function testSkinrSkinInfo() {
+    // Verify that skinr_get_skin_info() finds and returns all registered skins
+    // in $module.skinr.inc files as well as Skinr plugin files, but does not
+    // return skins that are incompatible with the current Skinr API version.
+    $skin_info = skinr_get_skin_info();
+
+    // skinr_test_font is registered via hook_skinr_skin_info() in
+    // skinr_test.skinr.inc.
+    $this->assertTrue(isset($skin_info['skinr_test_font']), 'Skin registered in $module.skinr.inc found.');
+    unset($skin_info['skinr_test_font']);
+
+    // skinr_test_example is registered via hook_skinr_skin_PLUGIN_info() in
+    // skins/example.inc.
+    $this->assertTrue(isset($skin_info['skinr_test_example']), 'Skin registered in plugin file found.');
+    unset($skin_info['skinr_test_example']);
+
+    // Ensure that skinr_test_incompatible is not contained.
+    $this->assertTrue(!isset($skin_info['skinr_test_incompatible']), 'Incompatible skin not found.');
+    // After asserting all expected, the list of skins should be empty.
+    $this->assertTrue(empty($skin_info), 'No unexpected skins found.');
+  }
+
+
+  /**
+   * Tests hook_skinr_config_info().
+   */
+  function testSkinrConfigInfo() {
+    // Verify that skinr_get_config_info() finds all existing and compatible
+    // hook_skinr_config_info() implementations.
+    $config = skinr_get_config_info();
+    debug($config);
+
+    // Skinr's own implementation in skinr.skinr.inc should always be found.
+    $this->assertTrue(isset($config['rules']), 'hook_skinr_config_info() in $module.skinr.inc found.');
+    unset($config['rules']);
+
+    // Skinr's implementation on behalf of Node module in modules/node.skinr.inc
+    // should be found.
+    $this->assertTrue(isset($config['node']), 'hook_skinr_config_info() in a custom path found.');
+    unset($config['node']);
+
+    // Ensure that skinr_test_incompatible is not contained.
+    $this->assertTrue(!isset($config['skinr_test_incompatible']), 'Incompatible hook_skinr_config_info() not found.');
+    // After asserting all expected, the list of skins should be empty.
+    $this->assertTrue(empty($config), 'No unexpected skins found.');
+    debug($config);
+  }
+}
Index: tests/skinr_test.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skinr/tests/skinr_test.info,v
retrieving revision 1.1
diff -u -p -r1.1 skinr_test.info
--- tests/skinr_test.info	9 Dec 2010 00:08:47 -0000	1.1
+++ tests/skinr_test.info	10 Jan 2011 09:47:14 -0000
@@ -1,5 +1,6 @@
-name = Skinr Test Skin
-description = A test module used for testing skins with Simpletest.
+; $Id$
+name = Skinr Testing
+description = A test module used for testing Skinr.
 package = Testing
 core = 7.x
 hidden = TRUE
Index: tests/skinr_test.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skinr/tests/skinr_test.module,v
retrieving revision 1.1
diff -u -p -r1.1 skinr_test.module
--- tests/skinr_test.module	9 Dec 2010 00:08:47 -0000	1.1
+++ tests/skinr_test.module	10 Jan 2011 08:40:04 -0000
@@ -2,34 +2,10 @@
 // $Id: skinr_test.module,v 1.1 2010/12/09 00:08:47 jgirlygirl Exp $
 
 /**
- * Implements hook_skinr_api().
+ * @file
+ * Skinr testing module.
+ *
+ * Other modules should be able to place their Skinr support/integration code
+ * into a conditionally loaded $module.skinr.inc file, so this .module file
+ * only exists, because Drupal requires a .module file to exist.
  */
-function skinr_test_skinr_api() {
-  return array('api' => 2.0);
-}
-
-/**
- * Implements hook_skinr_skin_info().
- */
-function skinr_test_skinr_skin_info() {
-  $skins['skinr_test_font'] = array(
-    'title' => t('Font family'),
-    'type' => 'select',
-    'group' => 'typography',
-    'theme hooks' => array('block', 'region'),
-    'attached' => array(
-      'css' => array('skinr_test.css'),
-    ),
-    'options' => array(
-      'font_1' => array(
-        'title' => 'Arial, Helvetica, Nimbus Sans L, Liberation Sans, FreeSans',
-        'class' => array('font-1'),
-      ),
-      'font_2' => array(
-        'title' => 'Lucida Grande, Lucida Sans Unicode, DejaVu Sans, Tahoma',
-        'class' => array('font-2'),
-      ),
-    ),
-  );
-  return $skins;
-}
Index: tests/skinr_test.skinr.inc
===================================================================
RCS file: tests/skinr_test.skinr.inc
diff -N tests/skinr_test.skinr.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/skinr_test.skinr.inc	10 Jan 2011 09:20:01 -0000
@@ -0,0 +1,37 @@
+<?php
+// $Id$
+
+/**
+ * Implements hook_skinr_api_VERSION().
+ */
+function skinr_test_skinr_api_2() {
+  return array(
+    'directory' => 'skins',
+  );
+}
+
+/**
+ * Implements hook_skinr_skin_info().
+ */
+function skinr_test_skinr_skin_info() {
+  $skins['skinr_test_font'] = array(
+    'title' => t('Font family'),
+    'type' => 'select',
+    'group' => 'typography',
+    'theme hooks' => array('block', 'region'),
+    'attached' => array(
+      'css' => array('skinr_test.css'),
+    ),
+    'options' => array(
+      'font_1' => array(
+        'title' => 'Arial, Helvetica, Nimbus Sans L, Liberation Sans, FreeSans',
+        'class' => array('font-1'),
+      ),
+      'font_2' => array(
+        'title' => 'Lucida Grande, Lucida Sans Unicode, DejaVu Sans, Tahoma',
+        'class' => array('font-2'),
+      ),
+    ),
+  );
+  return $skins;
+}
Index: tests/skinr_test_incompatible.info
===================================================================
RCS file: tests/skinr_test_incompatible.info
diff -N tests/skinr_test_incompatible.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/skinr_test_incompatible.info	10 Jan 2011 09:47:38 -0000
@@ -0,0 +1,7 @@
+; $Id$
+name = Skinr Incompatible Testing
+description = A test module used for testing incompatible Skinr API implementations.
+package = Testing
+core = 7.x
+hidden = TRUE
+dependencies[] = skinr
Index: tests/skinr_test_incompatible.module
===================================================================
RCS file: tests/skinr_test_incompatible.module
diff -N tests/skinr_test_incompatible.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/skinr_test_incompatible.module	10 Jan 2011 22:40:43 -0000
@@ -0,0 +1,38 @@
+<?php
+// $Id$
+
+/**
+ * Implements hook_skinr_api(). (bogus)
+ */
+function skinr_test_incompatible_skinr_api() {
+}
+
+/**
+ * Implements hook_skinr_api_VERSION(). (incompatible)
+ */
+function skinr_test_incompatible_skinr_api_1() {
+}
+
+/**
+ * Implements hook_skinr_skin_info().
+ *
+ * This hook implementation purposively does not live in $module.skinr.inc, so
+ * tests can verify that this implementation is not invoked, even though it is
+ * loaded.
+ */
+function skinr_test_incompatible_skinr_skin_info() {
+  $skins['skinr_test_incompatible'] = array(
+    'title' => 'Incompatible',
+  );
+  return $skins;
+}
+
+/**
+ * Implements hook_skinr_config_info().
+ */
+function skinr_test_incompatible_skinr_config_info() {
+  $config['skinr_test_incompatible']['preprocess']['html'] = array(
+    'index_handler' => 'rules_skinr_preprocess_index_handler',
+  );
+  return $config;
+}
Index: tests/skins/example.inc
===================================================================
RCS file: tests/skins/example.inc
diff -N tests/skins/example.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/skins/example.inc	10 Jan 2011 09:21:10 -0000
@@ -0,0 +1,12 @@
+<?php
+// $Id$
+
+/**
+ * Implements hook_skinr_skin_PLUGIN_info().
+ */
+function skinr_test_skinr_skin_example_info() {
+  $skins['skinr_test_example'] = array(
+    'title' => t('Example skin plugin'),
+  );
+  return $skins;
+}
