skinr.module in Skinr 8.2
Same filename and directory in other branches
Handles core Skinr functionality.
File
skinr.moduleView source
<?php
/**
* @file
* Handles core Skinr functionality.
*/
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Component\Utility\Html;
use Drupal\skinr\Entity\Skin;
/**
* The Skinr API version.
*/
const SKINR_VERSION = 3;
/**
* A flag used to let us know if an object is in the database.
*/
const SKINR_STORAGE_IN_DATABASE = 0;
const SKINR_STORAGE_IN_CODE = 1;
const SKINR_STORAGE_IN_CODE_OVERRIDDEN = 2;
/**
* Implements hook_help().
*/
function skinr_help($path, $arg) {
switch ($path) {
case 'help.page.skinr':
return t('Visit the <a href="http://skinr.org/" target="_blank">Skinr.org</a> for full documentation.');
break;
}
}
/**
* Implements hook_hook_info().
*/
function skinr_hook_info() {
$hooks = array(
'skinr_api_' . SKINR_VERSION,
'skinr_elements',
'skinr_group_info',
'skinr_group_info_alter',
'skinr_skin_info',
'skinr_skin_info_alter',
'skinr_theme_hooks',
'skinr_theme_hooks_alter',
);
$hooks = array_fill_keys($hooks, array(
'group' => 'skinr',
));
$hooks['skinr_skin_defaults'] = array(
'group' => 'skinr_default',
);
return $hooks;
}
/**
* Clears cached Skinr information.
*/
function skinr_cache_reset() {
// @todo Use tags instead?
// skinr_context_group_defaults, skinr_skinable_hooks
\Drupal::cache()
->deleteMultiple(array(
'skinr_implements_api',
'skinr_skin_defaults',
'skinr_skin_info',
'skinr_group_info',
'skinr_config_info',
));
}
/**
* Implements hook_library_info_build().
*/
function skinr_library_info_build() {
$skin_infos = skinr_get_skin_info();
$libraries = [];
foreach ($skin_infos as $skin_name => $skin_info) {
$path = base_path() . $skin_info['source']['path'] . '/';
if (!empty($skin_info['attached'])) {
$libraries['skinr.' . $skin_name] = _skinr_attached_to_library($skin_info['attached'], $path);
}
foreach ($skin_info['options'] as $option => &$data) {
if (!empty($data['attached'])) {
$libraries['skinr.' . $skin_name . '.' . $option] = _skinr_attached_to_library($data['attached'], $path);
}
}
}
return $libraries;
}
/**
* Helper function to turn skin plugins attached arrays into a library.
*
* @see skinr_library_info_build()
*/
function _skinr_attached_to_library($attached, $path) {
// CSS.
if (isset($attached['css'])) {
foreach ($attached['css'] as &$category) {
_skinr_fix_path($category, $path);
}
}
// Javascript.
if (isset($attached['js'])) {
_skinr_fix_path($attached['js'], $path);
}
return $attached;
}
/**
* Helper function to fix library paths for skin plugins.
*
* @see skinr_library_info_build()
*/
function _skinr_fix_path(&$data, $path) {
$is_local_uri = function ($uri) {
$scheme = \Drupal::service('file_system')
->uriScheme($uri);
return $scheme === FALSE || \Drupal::service('stream_wrapper_manager')
->getViaScheme($scheme) instanceof \Drupal\Core\StreamWrapper\LocalStream;
};
$new = [];
foreach ($data as $source => $options) {
if ($is_local_uri($source)) {
$new[$path . $source] = $options;
}
else {
$new[$source] = $options;
}
}
$data = $new;
}
/**
* Implements hook_preprocess().
*
* @todo Optimize this function by removing dependencies on
* skinr_get_skin_info() and similar resource heavy functions.
* @todo Account for Drupal's caching being enabled and make it work.
*/
function skinr_preprocess(&$variables, $hook) {
// Fix for update script.
if (defined('MAINTENANCE_MODE')) {
return;
}
$data =& drupal_static(__FUNCTION__);
// Caching the data returned from the following functions is reported to
// improve performance.
if (!isset($data)) {
$data['current_theme'] = skinr_current_theme();
$data['skin_info'] = skinr_get_skin_info();
$data['theme_registry'] = \Drupal::service('theme.registry')
->getRuntime();
$data['skip_cache'] = FALSE;
$implementations = \Drupal::moduleHandler()
->getImplementations('skinr_preprocess_alter');
// skinr_panels is special case that only runs when skinr_context is enabled.
if (count($implementations) > 0 && $implementations[0] !== 'skinr_panels') {
// Skip caching whenever a module implements skinr_preprocess_alter.
$data['skip_cache'] = TRUE;
}
}
// @todo Use $variables['theme_hook_original'] ?
$original_hook = $hook;
// dpm($hook . '|' . $variables['theme_hook_original']);
// An array of $elements based on $module and $original_hook, derived from $variables.
$implementations = \Drupal::cache('bootstrap')
->get('skinr_elements');
$array_elements = skinr_invoke_all('skinr_elements', $variables, $original_hook, 'preprocess');
foreach ($array_elements as $element_type => $elements) {
$applied_skins = array();
foreach ($elements as $element) {
$cid = 'skinr_preprocess:' . $element_type . ':' . $element . ':' . $data['current_theme'];
if (!$data['skip_cache'] && ($cached = \Drupal::cache()
->get($cid))) {
// This type of caching is incompatible with skinr_context.
$applied_skins = $cached->data;
}
else {
$properties = array(
'theme' => $data['current_theme'],
'element_type' => $element_type,
'element' => $element,
'status' => 1,
);
$skins = entity_load_multiple_by_properties('skin', $properties);
// Invoke hook_skinr_preprocess_alter() in all modules.
$context = array(
'hook' => $hook,
'variables' => &$variables,
'theme' => $data['current_theme'],
'element_type' => $element_type,
'elements' => $elements,
);
\Drupal::moduleHandler()
->alter('skinr_preprocess', $skins, $context);
$applied_skins = array();
foreach ($skins as $skin) {
$applied_skins = array(
$skin->skin => $skin
->getOptions(),
) + $applied_skins;
}
// Cache data.
if (!$data['skip_cache']) {
\Drupal::cache('data')
->set($cid, $applied_skins, CacheBackendInterface::CACHE_PERMANENT, array(
'skinr:' . $element_type . ':' . $element,
));
}
}
}
// Attach JS and CSS, and add classes.
if (!empty($applied_skins)) {
foreach ($applied_skins as $skin_name => $skin_options) {
// Special case for _additional.
if ($skin_name == '_additional') {
continue;
}
$skin_info = $data['skin_info'][$skin_name];
// Make sure this skin is enabled for the current theme.
$status = skinr_skin_info_status_get($skin_info);
if (empty($status[$data['current_theme']])) {
continue;
}
if (isset($skin_info['attached'])) {
$variables['#attached']['library'][] = 'skinr/skinr.' . $skin_name;
}
if (!is_array($skin_options)) {
$skin_options = array(
$skin_options,
);
}
foreach ($skin_options as $skin_option) {
if (isset($data['skin_info'][$skin_name]['options'][$skin_option]['attached'])) {
$variables['#attached']['library'][] = 'skinr/skinr.' . $skin_name . '.' . $skin_option;
}
}
}
$flattened_skins_array = skinr_flatten_skins_array($applied_skins);
if (isset($variables['attributes']['class'])) {
$variables['attributes']['class'] = array_merge($variables['attributes']['class'], $flattened_skins_array);
}
else {
$variables['attributes']['class'] = $flattened_skins_array;
}
}
}
}
/**
* 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 (!is_array($options)) {
$options = array(
$options,
);
}
if ($skin_name == '_additional') {
$classes = array_merge($classes, $options);
}
else {
foreach ($options as $option) {
if (!empty($skin_info[$skin_name]['options'][$option]['class'])) {
$classes = array_merge($classes, $skin_info[$skin_name]['options'][$option]['class']);
}
}
}
}
return array_unique($classes);
}
/**
* Returns a list of extensions that implement this API version of Skinr.
*
* @return
* An associative array whose keys are system names of extensions and whose
* values are again associative arrays containing:
* - type: Either 'module' or 'theme'.
* - name: The system name of the extension.
* - path: The path to the extension.
* - directory: (optional) The sub-directory holding Skinr plugin files.
* - ...: Any other properties defined by the module or theme.
*/
function skinr_implements_api() {
// All themes are disabled while running update script so theme skins are not
// cached. Don't cache to prevent this.
if (defined('MAINTENANCE_MODE')) {
return array();
}
$cache =& drupal_static(__FUNCTION__);
if (!isset($cache)) {
if ($cached = \Drupal::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 (\Drupal::moduleHandler()
->getImplementations('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.
// ModuleHandler::getImplementations() only auto-loads $module.skinr.inc
// in a module's root folder.
if (isset($cache[$module]['path'])) {
$file = $cache[$module]['path'] . '/' . $module . '.skinr.inc';
if (file_exists(DRUPAL_ROOT . '/' . $file)) {
$cache[$module]['include file'] = $file;
}
}
else {
// If there is a $module.skinr.inc in the module's root, it gets
// auto-loaded for any hooks. But if a skin defined thering contains a
// custom 'form callback' function, we'll need to load it manually. So
// store the file's info.
$file = drupal_get_path('module', $module) . '/' . $module . '.skinr.inc';
if (file_exists(DRUPAL_ROOT . '/' . $file)) {
$cache[$module]['include file'] = $file;
}
}
// Populate defaults.
$cache[$module] += array(
'path' => drupal_get_path('module', $module),
'directory' => NULL,
);
}
// Collect the equivalent of hook_skinr_api_VERSION() implementations in
// themes. The theme system only initializes one theme (and optionally its
// base themes) for the current request, and the phptemplate engine only
// loads template.php during theme initialization. Furthermore, template.php
// is a custom concept of the phptemplate engine and does not exist for
// other theme engines. Since we are interested in all existing
// implementations of all enabled themes, the equivalent of the module hook
// is a theme .info file property 'skinr' that has the sub-keys 'api' and
// optionally 'directory' defined.
// Account for all enabled themes and (any recursive) base themes of them,
// regardless of whether base themes are enabled.
$theme_handler = \Drupal::service('theme_handler');
$all_themes = $theme_handler
->listInfo();
$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
// \Drupal::service('theme_handler')->listInfo().
$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()
// @see \Drupal::theme()->getActiveTheme()
$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;
}
}
}
\Drupal::cache()
->set('skinr_implements_api', $cache);
}
return $cache;
}
/**
* Determine whether a module implements a hook.
*
* Replacement for ModuleHandler::implementsHook() 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. ModuleHandler::implementsHook()
// only auto-loads $module.skinr.inc in a module's root folder.
skinr_load_include($extension['include file']);
if (function_exists($function)) {
return TRUE;
}
}
else {
// Run through ModuleHandler::implementsHook() to auto-load
// $module.skinr.inc from a non-custom path.
if (\Drupal::moduleHandler()
->implementsHook($module, $hook)) {
return TRUE;
}
}
}
return FALSE;
}
/**
* Determine which modules are implementing a hook.
*
* Replacement for ModuleHandler::getImplementations() 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)) {
// @todo Fix cache bin.
$implementations = \Drupal::cache('bootstrap')
->get('skinr_implements');
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.
// ModuleHandler::getImplementations() and
// ModuleHandler::implementsHook() 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 ModuleHandler::implementsHook() to auto-load
// $module.skinr.inc from a non-custom path.
if (\Drupal::moduleHandler()
->implementsHook($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::moduleHandler()
->alter('skinr_implements', $implementations[$hook], $hook);
}
}
else {
foreach ($implementations[$hook] as $module => $file) {
if ($file) {
skinr_load_include($file);
}
else {
\Drupal::moduleHandler()
->implementsHook($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 ModuleHandler::implementsHook() 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()
* @todo hook_exit() no longer exists.
*/
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']);
// @todo Fix cache bin.
\Drupal::cache('bootstrap')
->set('skinr_implements', $implementations);
}
}
/**
* Invoke a hook in all enabled modules and themes that implement it.
*
* Replacement for ModuleHandler::invokeAll() 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;
}
// -----------------------------------------------------------------------
// 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 &$skin) {
// Make sure we're getting valid data.
if (!skinr_skin_validate($skin)) {
return FALSE;
}
return $skin
->save();
}
/**
* Get skin configuration IDs.
*/
function skinr_skin_uuid_to_sid($uuid) {
$sids =& drupal_static(__FUNCTION__, array());
if (!isset($sids[$uuid])) {
$sids[$uuid] = db_query("SELECT sid FROM {skinr_skins} WHERE uuid = :uuid", array(
':uuid' => $uuid,
))
->fetchField();
}
return $sids[$uuid];
}
/**
* Get skin configuration IDs.
*/
function skinr_skin_sid_to_uuid($sid) {
$uuids =& drupal_static(__FUNCTION__, array());
if (!isset($uuids[$sid])) {
$uuids[$sid] = db_query("SELECT uuid FROM {skinr_skins} WHERE sid = :sid", array(
':sid' => $sid,
))
->fetchField();
}
return $uuids[$sid];
}
/**
* Load a skin configuration object from the database using UUID.
*
* @param $uuid
* The UUID of the skin configuration to load.
*
* @return
* A fully-populated skin configuration object.
*/
function skinr_skin_load_by_uuid($uuid) {
if (!($sid = skinr_skin_uuid_to_sid($uuid))) {
return FALSE;
}
// Run this through skinr_skin_load_multiple() to preserve caching.
$skin = entity_load_multiple('skin', array(
$sid,
));
return $skin ? reset($skin) : FALSE;
}
/**
* Returns all default skin configuration objects.
*/
function _skinr_skin_get_defaults() {
$default_skins =& drupal_static(__FUNCTION__);
if (!isset($default_skins)) {
if ($cached = \Drupal::cache()
->get('skinr_skin_defaults')) {
$default_skins = $cached->data;
return $default_skins;
}
// Don't use ModuleHandler::invokeAll() to prevent oddly merged defaults.
$default_skins = array();
foreach (\Drupal::moduleHandler()
->getImplementations('skinr_skin_defaults') as $module) {
$function = $module . '_skinr_skin_defaults';
if (function_exists($function)) {
$result = call_user_func_array($function, array());
if (isset($result) && is_array($result)) {
$default_skins = array_merge($default_skins, $result);
}
elseif (isset($result)) {
$default_skins[] = $result;
}
}
}
// Let modules modify the skin configuration.
\Drupal::moduleHandler()
->alter('skinr_skin_defaults', $default_skins);
\Drupal::cache()
->set('skinr_skin_defaults', $default_skins);
}
return $default_skins;
}
/**
* Imports default skin confiuration objects from code into database.
*
* @param $module_name
* Limit importing of defaults to a single module.
* @param $force
* If FALSE (default) the default skin will only be imported if the skin
* configuration doesn't exist, or if storage is default in code. If TRUE, the
* skin configuration in code will always be overwritten.
*
* @return
* If an import failed, returns FALSE. If all imports succeeded, returns TRUE.
*/
function skinr_skin_defaults_import($module_name = NULL, $force = FALSE) {
if (isset($module_name)) {
if (!($default_skins = \Drupal::moduleHandler()
->invoke($module_name, 'skinr_skin_defaults'))) {
$default_skins = array();
}
\Drupal::moduleHandler()
->alter('skinr_skin_defaults', $default_skins);
}
else {
$default_skins = _skinr_skin_get_defaults();
}
$status = TRUE;
foreach ($default_skins as $skin) {
$status = skinr_skin_import($skin, $force) && $status;
}
return $status;
}
/**
* Revert a skin configuration object that's stored in code to its default state.
*
* @param $sid
* A skin configuration ID.
*
* @return
* If revert failed, returns FALSE. If it succeeded, returns TRUE.
*/
function skinr_skin_revert($sid) {
if (!($uuid = skinr_skin_sid_to_uuid($sid))) {
return FALSE;
}
$default_skins = _skinr_skin_get_defaults();
if (!isset($default_skins[$uuid])) {
return FALSE;
}
if ($status = skinr_skin_import($default_skins[$uuid], TRUE)) {
drupal_static_reset('skinr_skin_uuid_to_sid');
drupal_static_reset('skinr_skin_sid_to_uuid');
}
return $status;
}
/**
* Import a skin configuration object as defined in skinr_skin_defaults().
*
* @param $default_skin
* A skin configuration object.
* @param $force
* If FALSE (default) the default skin will only be imported if the skin
* configuration doesn't exist, or if storage is default in code. If TRUE, the
* skin configuration in code will always be overwritten.
*
* @return
* If import failed, returns FALSE. If it succeeded, returns TRUE.
*/
function skinr_skin_import($default_skin, $force = FALSE) {
// Make sure we don't modify the cached default skin array.
$skin = clone $default_skin;
// Functionality is abstracted for use in skinr_skin_storage().
_skinr_skin_import($skin);
if (!$force && !empty($skin->sid)) {
// Load existing skin configuration from DB.
$db_skin = entity_load('skin', $skin->sid, TRUE);
// Sync status.
$skin->status = $db_skin->status;
if ($skin != $db_skin) {
// Skin exists and is overridden, so cancel import.
\Drupal::logger('skinr')
->warning('Canceled import of skin with UUID %uuid and SID %sid. It is overridden.', array(
'%uuid' => $skin->uuid,
'%sid' => $skin->sid,
));
return FALSE;
}
}
if ($status = skinr_skin_save($skin)) {
drupal_static_reset('skinr_skin_uuid_to_sid');
drupal_static_reset('skinr_skin_sid_to_uuid');
\Drupal::logger('skinr')
->notice('Imported skin with UUID %uuid and SID %sid.', array(
'%uuid' => $skin->uuid,
'%sid' => $skin->sid,
));
}
else {
\Drupal::logger('skinr')
->warning('Failed to import skin with UUID %uuid and SID %sid.', array(
'%uuid' => $skin->uuid,
'%sid' => isset($skin->sid) ? $skin->sid : t('unknown'),
));
}
return $status;
}
function _skinr_skin_import(&$skin) {
if ($sid = skinr_skin_uuid_to_sid($skin->uuid)) {
$skin->sid = $sid;
}
// Let modules modify the skin configuration.
\Drupal::moduleHandler()
->alter('skinr_skin_import', $skin);
}
/**
* Export var function.
*
* @see features_var_export()
*/
function skinr_var_export($var, $prefix = '', $init = TRUE) {
if (is_object($var)) {
$output = method_exists($var, 'export') ? $var
->export() : skinr_var_export((array) $var, '', FALSE);
}
else {
if (is_array($var)) {
if (empty($var)) {
$output = 'array()';
}
else {
$output = "array(\n";
foreach ($var as $key => $value) {
// Using normal var_export on the key to ensure correct quoting.
$output .= " " . var_export($key, TRUE) . " => " . skinr_var_export($value, ' ', FALSE) . ",\n";
}
$output .= ')';
}
}
else {
if (is_bool($var)) {
$output = $var ? 'TRUE' : 'FALSE';
}
else {
if (is_int($var)) {
$output = intval($var);
}
else {
if (is_numeric($var)) {
$output = floatval($var);
}
else {
if (is_string($var) && strpos($var, "\n") !== FALSE) {
// Replace line breaks in strings with a token for replacement
// at the very end. This protects whitespace in strings from
// unintentional indentation.
$var = str_replace("\n", "***BREAK***", $var);
$output = var_export($var, TRUE);
}
else {
$output = var_export($var, TRUE);
}
}
}
}
}
}
if ($prefix) {
$output = str_replace("\n", "\n{$prefix}", $output);
}
if ($init) {
$output = str_replace("***BREAK***", "\n", $output);
}
return $output;
}
/**
* Export object function.
*
* @see ctools_object_export()
*/
function skinr_object_export($object, $identifier, $prefix = '') {
$output = $prefix . '$' . $identifier . ' = new ' . get_class($object) . "();\n";
$output .= $prefix . '$' . $identifier . '->status = ';
if ($object->status) {
$output .= 'TRUE; /* Edit this to false to make a default ' . $identifier . ' disabled initially */' . "\n";
}
else {
$output .= 'FALSE; /* Edit this to true to make a default ' . $identifier . ' enabled initially */' . "\n";
}
$output .= $prefix . '$' . $identifier . '->api_version = ' . SKINR_VERSION . ";\n";
foreach ($object as $field => $value) {
if ($field == 'status') {
continue;
}
$output .= $prefix . '$' . $identifier . '->' . $field . ' = ' . skinr_var_export($value, $prefix) . ";\n";
}
return $output;
}
/**
* Output a skin configuration object as code suitable for skinr_skin_defaults().
*
* @param $skin
* A skin configuration object.
* @param $prefix
* A string to prefix the code with, used to indent the resulting code.
*
* @return
* A string.
*/
function skinr_skin_export($skin, $prefix = '') {
// Make sure we don't modify the cached skin object.
$skin = clone $skin;
// Let modules modify the skin configuration.
\Drupal::moduleHandler()
->alter('skinr_skin_export', $skin, $prefix);
// Remove site specific $sid.
unset($skin->sid);
$output = skinr_object_export($skin, 'skin', $prefix);
$output .= $prefix . "\$skins['{$skin->uuid}'] = \$skin;\n";
return $output;
}
/**
* Returns a skin configuration object's storage method.
*
* @param $skin
* A skin configuration object.
*
* @return
* SKINR_STORAGE_IN_DATABASE if stored in the database,
* SKINR_STORAGE_IN_CODE if stored in code,
* SKINR_STORAGE_IN_CODE_OVERRIDDEN if stored in code and overridden in db.
*/
function skinr_skin_storage($skin) {
$default_skins = _skinr_skin_get_defaults();
$storage = SKINR_STORAGE_IN_DATABASE;
if (isset($default_skins[$skin->uuid])) {
$default_skin = clone $default_skins[$skin->uuid];
// Make sure skin has same processing as import.
_skinr_skin_import($default_skin);
// API version is only used for export.
unset($default_skin->api_version);
// Status shouldn't influence overridden.
$default_skin->status = $skin->status;
$storage = SKINR_STORAGE_IN_CODE;
if ($default_skin != $skin) {
// Default was overridden.
$storage = SKINR_STORAGE_IN_CODE_OVERRIDDEN;
}
}
return $storage;
}
/**
* Get skin configuration IDs.
*
* @param $filter_by
* An associative array whose keys are:
* - theme: (optional) The theme.
* - module: (optional) The module.
* - element: (optional) The element ID.
* - skin: (optional) The skin name.
* - status: (optional) Boolean indicating whether or not this skin
* configuration is enabled.
*
* @return
* An array of skin configuration IDs.
*/
function skinr_skin_get_sids($filter_by = array()) {
$query = \Drupal::entityQuery('skin');
if (isset($filter_by['theme'])) {
$query
->condition('theme', $filter_by['theme']);
}
if (isset($filter_by['element_type'])) {
$query
->condition('element_type', $filter_by['element_type']);
}
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();
}
/**
* Helper function to remove empty skins from an array.
*
* @param $array
* A single or multi-dimensional array to strip of empty values.
*
* @return
* An array stripped of empty values.
*/
function _skinr_array_strip_empty($array) {
$new_array = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$value = _skinr_array_strip_empty($value);
}
if (!empty($value)) {
$new_array[$key] = $value;
}
}
return $new_array;
}
/**
* Helper function to retrieve the current theme.
*
* @param $exclude_admin_theme
* Optional. Set to TRUE to exclude the admin theme from possible themes to
* return.
*
* @return
* The current theme name.
*
* @see drupal_theme_initialize()
*/
function skinr_current_theme($exclude_admin_theme = FALSE) {
global $user;
// Drupal core, and modules such as themkey and og_theme, set the theme
// through hook_custom_theme() or hook_menu() using 'theme callback', which
// are all picked up by menu_get_custom_theme().
$theme = \Drupal::theme()
->getActiveTheme()
->getName();
if (!$theme) {
dpm('Empty theme!');
}
return $theme;
if ($exclude_admin_theme) {
// @todo See https://api.drupal.org/api/drupal/core%21includes%21theme.inc/function/drupal_theme_initialize/8
$theme_handler = \Drupal::service('theme_handler');
$themes = $theme_handler
->listInfo();
// Determine the active theme for the theme negotiator service. This includes
// the default theme as well as really specific ones like the ajax base theme.
$route_match = \Drupal::routeMatch();
$theme = \Drupal::service('theme.negotiator')
->determineActiveTheme($route_match);
// If no theme could be negotiated, or if the negotiated theme is not within
// the list of enabled themes, fall back to the default theme output of core
// and modules (similar to Stark, but without a theme extension at all). This
// is possible, because _drupal_theme_initialize() always loads the Twig theme
// engine.
if (!$theme || !isset($themes[$theme])) {
$theme = 'core';
}
}
return $theme;
}
/**
* 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) {
$status = \Drupal::config('skinr.status')
->get($skin_info['name']);
// We don't get a default returned for dynamically created config.
if (is_null($status)) {
$status = $skin_info['status'];
}
return $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) {
// @todo Clear if default?
\Drupal::configFactory()
->getEditable('skinr.status')
->set($skin_info['name'], $status)
->save();
}
/**
* Retrieves all skins registered by modules and themes.
*
* @return
* An array of skins.
*/
function skinr_get_skin_info() {
$skin_info =& drupal_static(__FUNCTION__);
if ($skin_info === NULL) {
if ($cached = \Drupal::cache()
->get('skinr_skin_info')) {
$skin_info = $cached->data;
return $skin_info;
}
$skin_info = [];
$plugins = \Drupal::service('plugin.manager.skin')
->getDefinitions();
foreach ($plugins as $plugin) {
if (empty($plugin['skins'])) {
continue;
}
$skin_info = $plugin['skins'];
}
// Allow modules to alter registered skin information.
\Drupal::moduleHandler()
->alter('skinr_skin_info', $skin_info);
\Drupal::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 ($group_info === NULL) {
if ($cached = \Drupal::cache()
->get('skinr_group_info')) {
$group_info = $cached->data;
return $group_info;
}
$group_info = [];
$plugins = \Drupal::service('plugin.manager.skin')
->getDefinitions();
foreach ($plugins as $plugin) {
if (empty($plugin['groups'])) {
continue;
}
foreach ($plugin['groups'] as $group_name => $group_data) {
$group_info[$group_name] = $group_data + [
'title' => '',
'description' => '',
'weight' => 0,
];
}
}
// Allow modules to alter groups through hook_skinr_group_info_alter().
\Drupal::moduleHandler()
->alter('skinr_group_info', $group_info);
\Drupal::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 = \Drupal::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::moduleHandler()
->alter('skinr_config_info', $config_info);
\Drupal::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::moduleHandler()
->alter('skinr_theme_hooks', $theme_hooks[$module][$element], $module, $element);
}
return $theme_hooks[$module][$element];
}
/**
* Implements hook_modules_enabled().
*/
function skinr_modules_enabled($modules) {
skinr_cache_reset();
foreach ($modules as $module) {
skinr_skin_defaults_import($module);
}
}
/**
* Implements hook_modules_disabled().
*/
function skinr_modules_disabled() {
skinr_cache_reset();
}
/**
* Implements hook_themes_enabled().
*/
function skinr_themes_enabled() {
skinr_cache_reset();
}
/**
* Implements hook_themes_disabled().
*/
function skinr_themes_disabled() {
skinr_cache_reset();
}
/**
* Helper function for built-in integration code.
*/
function skinr_skinr_api_modules() {
return array(
'path' => drupal_get_path('module', 'skinr') . '/modules',
);
}
function block_skinr_api_3() {
return skinr_skinr_api_modules();
}
function comment_skinr_api_3() {
return skinr_skinr_api_modules();
}
function node_skinr_api_3() {
return skinr_skinr_api_modules();
}
function system_skinr_api_3() {
return skinr_skinr_api_modules();
}
function views_skinr_api_3() {
return skinr_skinr_api_modules();
}
Functions
Name | Description |
---|---|
block_skinr_api_3 | |
comment_skinr_api_3 | |
node_skinr_api_3 | |
skinr_cache_reset | Clears cached Skinr information. |
skinr_current_theme | Helper function to retrieve the current theme. |
skinr_exit | Implements hook_exit(). |
skinr_flatten_skins_array | Returns an array of classes. |
skinr_get_config_info | Fetch Skinr configuration data from functionality plugins. |
skinr_get_group_info | Retrieves all skin groups registered by modules and themes. |
skinr_get_skin_info | Retrieves all skins registered by modules and themes. |
skinr_help | Implements hook_help(). |
skinr_hook | Determine whether a module implements a hook. |
skinr_hook_info | Implements hook_hook_info(). |
skinr_implements | Determine which modules are implementing a hook. |
skinr_implements_api | Returns a list of extensions that implement this API version of Skinr. |
skinr_invoke_all | Invoke a hook in all enabled modules and themes that implement it. |
skinr_library_info_build | Implements hook_library_info_build(). |
skinr_load_include | Loads a $module.skinr.inc include file. |
skinr_modules_disabled | Implements hook_modules_disabled(). |
skinr_modules_enabled | Implements hook_modules_enabled(). |
skinr_object_export | Export object function. |
skinr_preprocess | Implements hook_preprocess(). |
skinr_skinr_api_modules | Helper function for built-in integration code. |
skinr_skin_defaults_import | Imports default skin confiuration objects from code into database. |
skinr_skin_export | Output a skin configuration object as code suitable for skinr_skin_defaults(). |
skinr_skin_get_sids | Get skin configuration IDs. |
skinr_skin_import | Import a skin configuration object as defined in skinr_skin_defaults(). |
skinr_skin_info_status_get | Retrieve the overridden status of a skin. |
skinr_skin_info_status_set | Set the status of a skin. Overrides the skin plugin settings. |
skinr_skin_load_by_uuid | Load a skin configuration object from the database using UUID. |
skinr_skin_revert | Revert a skin configuration object that's stored in code to its default state. |
skinr_skin_save | Save a skin object. |
skinr_skin_sid_to_uuid | Get skin configuration IDs. |
skinr_skin_storage | Returns a skin configuration object's storage method. |
skinr_skin_uuid_to_sid | Get skin configuration IDs. |
skinr_skin_validate | Validate a skinr object. |
skinr_themes_disabled | Implements hook_themes_disabled(). |
skinr_themes_enabled | Implements hook_themes_enabled(). |
skinr_theme_hooks | Provide a list of all available theme hooks for a given element. |
skinr_var_export | Export var function. |
system_skinr_api_3 | |
views_skinr_api_3 | |
_skinr_array_strip_empty | Helper function to remove empty skins from an array. |
_skinr_attached_to_library | Helper function to turn skin plugins attached arrays into a library. |
_skinr_fix_path | Helper function to fix library paths for skin plugins. |
_skinr_skin_get_defaults | Returns all default skin configuration objects. |
_skinr_skin_import |
Constants
Name | Description |
---|---|
SKINR_STORAGE_IN_CODE | |
SKINR_STORAGE_IN_CODE_OVERRIDDEN | |
SKINR_STORAGE_IN_DATABASE | A flag used to let us know if an object is in the database. |
SKINR_VERSION | The Skinr API version. |