popsu-d7/sites/all/modules/skinr/skinr.module
Bachir Soussi Chiadmi 1bc61b12ad first import
2015-04-08 11:40:19 +02:00

1471 lines
44 KiB
Plaintext

<?php
/**
* @file
* Handles core Skinr functionality.
*/
/**
* The Skinr API version.
*/
define('SKINR_VERSION', 2);
/**
* Show this rule on every page except the listed pages.
*/
define('SKINR_RULE_VISIBILITY_NOTLISTED', 0);
/**
* Show this rule on only the listed pages.
*/
define('SKINR_RULE_VISIBILITY_LISTED', 1);
/**
* Show this rule if the associated PHP code returns TRUE.
*/
define('SKINR_RULE_VISIBILITY_PHP', 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',
));
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();
}