help page for full documentation.', array('@skinr-help' => url('admin/advanced_help/skinr'))); } else { return t('Please download and enable the Advanced Help 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', )); 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 ($hook == 'maintenance_page') { return; } $current_theme = skinr_current_theme(); $skin_info = skinr_get_skin_info(); $theme_registry = theme_get_registry(); $original_hook = (isset($theme_registry[$hook]['original hook']) ? $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'); foreach ($array_elements as $module => $elements) { if (empty($elements)) { // We can receive empty arrays; if that happens, there's no point // in continuing. continue; } // Get a list of skin configuration IDs to pass to // skinr_skin_load_multiple(). $params = array( 'theme' => $current_theme, 'module' => $module, 'element' => $elements, 'status' => 1, ); $sids = skinr_skin_get_sids($params); if (empty($sids)) { // Noting to apply. continue; } $applied_skins = array(); foreach (skinr_skin_load_multiple($sids) as $skin) { $applied_skins = array($skin->skin => $skin->options) + $applied_skins; } // Invoke hook_skinr_preprocess_alter() in all modules. // @todo Review whether this alter hook is useful or not, and if it's in // the right place or not. $context = array( 'hook' => $hook, 'variables' => &$variables, 'theme' => $current_theme, 'module' => $module, 'elements' => $elements, 'options' => $applied_skins, ); drupal_alter('skinr_preprocess', $context); // 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($skin_info[$skin_name]['attached'])) { $elements['#attached'] = $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($skin_info[$skin_name]['options'][$skin_option]['attached'])) { $elements['#attached'] = $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); } // ------------------------------------------------------------------ // Rule functions. /** * Validate a rule object. * * @param $rule * A rule object. * * @return * TRUE on success, FALSE on failure. */ function skinr_rule_validate(&$rule) { if (empty($rule->title) || empty($rule->rule_type)) { return FALSE; } if (!isset($rule->node_types)) { $rule->node_types = array(); } if (!isset($rule->roles)) { $rule->roles = array(); } if (!isset($rule->visibility)) { $rule->visibility = 0; } if (!isset($rule->pages)) { $rule->pages = ''; } if (!is_array($rule->node_types) || !is_array($rule->roles)) { return FALSE; } if ($rule->visibility !== 0 && $rule->visibility !== 1 && $rule->visibility !== 2) { return FALSE; } return TRUE; } /** * Save a skinr rule object. * * @param $rule * A rule object. * * @return * The rule ID. */ function skinr_rule_save($rule) { // Make sure we're getting valid data. if (!skinr_rule_validate($rule)) { return FALSE; } $status = drupal_write_record('skinr_rules', $rule, !empty($rule->rid) ? array('rid') : array()); return $status; } /** * Load a skinr rule object. * * @param $rid * (optional) The rule ID. * * @return * A rule object. If no $rid is specified an array of all rules will be * returned. */ function skinr_rule_load($rid = NULL) { $rids = (isset($rid) ? array($rid) : array()); $rules = skinr_rule_load_multiple($rids); return $rules ? reset($rules) : FALSE; } /** * Loads multiple skinr rule objects. * * @param $rids * An array of rule IDs. Optional. * @param $conditions * An array of conditions on the {skinr_rules} table in the form 'field' => * $value. * * @return * An array of rule objects indexed by rid. If $rids is not provided, all * rules are returned. */ function skinr_rule_load_multiple($rids = array(), $conditions = array()) { $rules = array(); $select = db_select('skinr_rules')->fields('skinr_rules'); if (!empty($rids)) { $select->condition('rid', $rids); } foreach ($conditions as $field => $condition) { $select->condition($field, $condition); } foreach ($select->execute() as $rule) { $rule->node_types = unserialize($rule->node_types); $rule->roles = unserialize($rule->roles); $rules[$rule->rid] = $rule; } return $rules; } /** * Delete a skinr rule object. * * @param $rid * The rule ID. */ function skinr_rule_delete($rid) { if ($rule = skinr_rule_load($rid)) { db_delete('skinr_rules') ->condition('rid', $rule->rid) ->execute(); db_delete('skinr_skins') ->condition('module', 'page') ->condition('element', $rule->rid) ->execute(); } } /** * Determines if the rule should be visible for a given path. * * @param $rid * The rule ID. * @param $path * (optional) The path to check. Defaults to the path of the current page. * @param $account * (optional) The account to check. Defaults to currently logged in user. * * @return * TRUE if the rule should be visible, FALSE otherwise. */ function skinr_rule_is_visible($rid, $path = NULL, $account = NULL) { global $user; if (!isset($account)) { $account = $user; } if ($rule = skinr_rule_load($rid)) { if (!isset($path)) { $path = $_GET['q']; } // Check role visibility. if (!empty($rule->roles) && ($account->uid != 1) && !count(array_intersect(array_keys($account->roles), $rule->roles))) { return FALSE; } // Check content type visibility. // If a rule has no node types associated, it is displayed for every type. // For rules with node types associated, if the node type does not match // the settings from this rule, return FALSE. if (!empty($rule->node_types)) { $node = menu_get_object('node', 1, $path); $node_types = node_type_get_types(); if (arg(0, $path) == 'node' && arg(1, $path) == 'add' && arg(2, $path)) { $node_add_arg = strtr(arg(2, $path), '-', '_'); } if (!empty($node)) { // This is a node or node edit page. if (empty($rule->node_types[$node->type])) { // This rule should not be displayed for this node type. return FALSE; } } elseif (isset($node_add_arg) && isset($node_types[$node_add_arg])) { // This is a node creation page. if (!isset($rule->node_types[$node_add_arg]) || !$rule->node_types[$node_add_arg]) { // This rule should not be displayed for this node type. return FALSE; } } else { // This is not a node page, remove the rule. return FALSE; } } // Match path if necessary. if ($rule->pages) { // Convert path to lowercase. This allows comparison of the same path // with different case. Ex: /Page, /page, /PAGE. $pages = drupal_strtolower($rule->pages); if ($rule->visibility < SKINR_RULE_VISIBILITY_PHP) { // Convert the Drupal path to lowercase $path = drupal_strtolower(drupal_get_path_alias($path)); // Compare the lowercase internal and lowercase path alias (if any). $page_match = drupal_match_path($path, $pages); if ($path != $_GET['q']) { $page_match = $page_match || drupal_match_path($path, $pages); } // When $rule->visibility has a value of 0 (SKINR_RULE_VISIBILITY_NOTLISTED), // the rule is displayed on all pages except those listed in $rule->pages. // When set to 1 (SKINR_RULE_VISIBILITY_LISTED), it is displayed only on those // pages listed in $rule->pages. $page_match = !($rule->visibility xor $page_match); } elseif (module_exists('php')) { $page_match = php_eval($rule->pages); } else { $page_match = FALSE; } } else { $page_match = TRUE; } return $page_match; } return FALSE; } /** * 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() { $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; } } // 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 { // 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'); 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; } /** * 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. $queried_skins = db_select('skinr_skins', 's') ->fields('s') ->condition('sid', $sids) ->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. $skin = db_query("SELECT * 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; } /** * 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. * * The global variable $theme_key doesn't work for our purposes when an admin * theme is enabled. * * @param $exclude_admin_theme * Optional. Set to TRUE to exclude the admin theme from possible themes to * return. * * @return * The current theme name. */ function skinr_current_theme($exclude_admin_theme = FALSE) { global $user, $custom_theme; if (!empty($user->theme) && drupal_theme_access($user->theme)) { $current_theme = $user->theme; } elseif (!empty($custom_theme) && drupal_theme_access($custom_theme) && !($exclude_admin_theme && $custom_theme == variable_get('admin_theme', '0'))) { // Don't return the admin theme if we're editing skinr settings. $current_theme = $custom_theme; } else { $current_theme = variable_get('theme_default', 'bartik'); } 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 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://') * the path does not get prepended. * * @param $files * A an array of filenames that need the path prepended. * @todo Adjust docs to account for arrays instead of filenames. * @param $path * The path to prepend. */ function _skinr_add_path_to_files(&$files, $path) { foreach ($files as $key => $file) { if (is_array($file)) { if (strpos($file[0], 'http://') === 0 || strpos($file[0], 'https://') === 0 ) { continue; } $files[$key][0] = $path . '/' . $file[0]; } else { if (strpos($file, 'http://') === 0 || strpos($file, 'https://') === 0) { continue; } $files[$key] = $path . '/' . $file; } } } /** * 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]); // 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']); } // Add path to scripts. if (isset($skin_infos[$skin_name]['attached']['js'])) { _skinr_add_path_to_files($skin_infos[$skin_name]['attached']['js'], $source['path']); } foreach ($skin_infos[$skin_name]['options'] as $option_name => $option) { // Add path to stylesheets. if (isset($option['attached']['css'])) { _skinr_add_path_to_files($skin_infos[$skin_name]['options'][$option_name]['attached']['css'], $source['path']); } // Add path to scripts. if (isset($option['attached']['js'])) { _skinr_add_path_to_files($skin_infos[$skin_name]['options'][$option_name]['attached']['js'], $source['path']); } // Validate class by running it through drupal_html_class(). if (!is_array($skin_infos[$skin_name]['options'][$option_name]['class'])) { $skin_infos[$skin_name]['options'][$option_name]['class'] = array($skin_infos[$skin_name]['options'][$option_name]['class']); } 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) { $hooks["{$name}_skinr_skin_{$plugin}_info"] = array( // The source path for a plugin is the plugin directory. 'path' => $dir . '/' . basename(dirname($file->uri)), 'filename' => $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) { $hooks["{$name}_skinr_group_{$plugin}_info"] = array( // The source path for a plugin is the plugin directory. 'path' => $dir . '/' . basename(dirname($file->uri)), 'filename' => $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() { skinr_cache_reset(); } /** * 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 views_skinr_api_2() { return skinr_skinr_api_modules(); }