styleswitcher.module in Style Switcher 8.2
Same filename and directory in other branches
Module's hooks implementations and helper functions.
File
styleswitcher.moduleView source
<?php
/**
* @file
* Module's hooks implementations and helper functions.
*/
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\styleswitcher\StyleswitcherElementInfoAlter;
/**
* Indicates that the cookie must live 365 days more.
*/
const STYLESWITCHER_COOKIE_EXPIRE = 31536000;
/**
* Implements hook_page_attachments().
*/
function styleswitcher_page_attachments(array &$attachments) {
// Add the dynamic CSS to every page. Use a library instead of straight
// html_head_link attachment because we need our alternative stylesheets to
// overwrite the most other css on the page and in standard html.html.twig
// css goes after head.
$attachments['#attached']['library'][] = 'styleswitcher/dynamic-css';
}
/**
* Implements hook_css_alter().
*/
function styleswitcher_css_alter(&$css, AttachedAssetsInterface $assets) {
$path = drupal_get_path('module', 'styleswitcher') . '/styleswitcher.active.css';
// The dynamic-css library is not always attached. For example, maintenance
// pages don't have it. So check for its presence.
if (isset($css[$path])) {
$asset = $css[$path];
unset($css[$path]);
// Use the latest standard group to be after the most of other css. Some
// themes (like Omega) use even latter groups to set their grid layouts
// hoping they wouldn't be overridden.
$asset['group'] = CSS_AGGREGATE_THEME;
$asset['weight'] = PHP_INT_MAX;
$theme = Drupal::theme()
->getActiveTheme()
->getName();
// Construct absolute URL explicitly to work out disabled clean URLs.
$path = Url::fromRoute('styleswitcher.css', [
'theme' => $theme,
], [
'absolute' => TRUE,
])
->toString();
$css[$path] = $asset;
$css[$path]['data'] = $path;
}
}
/**
* Implements hook_element_info_alter().
*/
function styleswitcher_element_info_alter(&$types) {
if (floatval(\Drupal::VERSION) >= 8.800000000000001) {
$callback = [
StyleswitcherElementInfoAlter::class,
'preRenderHtmlTag',
];
}
else {
$callback = 'styleswitcher_pre_render_html_tag';
}
// This pre-render callback must run before element's #markup is created in
// HtmlTag::preRenderHtmlTag() which is a pre-render callback too.
array_unshift($types['html_tag']['#pre_render'], $callback);
}
/**
* Render API callback: Adds HTML id for the Style Switcher style link.
*
* This function is assigned as a #pre_render callback in
* styleswitcher_element_info_alter().
*/
function styleswitcher_pre_render_html_tag(array $element) {
if (!empty($element['#attributes']['media']) && $element['#attributes']['media'] == 'styleswitcher') {
// Set 'media' back to its default value.
$element['#attributes']['media'] = 'all';
// Add an ID.
$element['#attributes']['id'] = 'styleswitcher-css';
}
return $element;
}
/**
* Loads a style array by its machine name and a theme name.
*
* @param string $name
* Machine name of the style to load.
* @param string $theme
* Machine name of the theme to get the style for.
* @param string $type
* (optional) Style type: 'theme' or 'custom'. If the type is specified then
* it will be prefixed with a slash to the $name argument.
*
* @return array|null
* Style array on success or NULL otherwise. Style is an associative array
* containing:
* - name: Machine name.
* - label: Human-readable label.
* - path: System or external path to the CSS file.
* - weight: Weight of style link in the switch list.
* - status: Indicates whether the style is enabled or not.
* - is_default: Indicates that this style is the default one. This element
* may not always show the truth. It is recommended to use
* styleswitcher_default_style_key() to get the default style key.
* - _i: Index number of the style. It is used for sorting of styles with
* equal weights.
* - theme: Name of the theme the style is loaded for.
*
* @see styleswitcher_default_style_key()
*/
function styleswitcher_style_load($name, $theme, $type = '') {
if ($type) {
$name = $type . '/' . $name;
// The next conversion is only rationale for auto-loader wildcards in paths
// and there are $type arguments always set in menu items so if this
// function is called with only one argument this conversion is redundant.
$name = strtr($name, '-', '_');
}
$styles = styleswitcher_style_load_multiple($theme, [
'name' => $name,
]);
if ($styles) {
return reset($styles);
}
}
/**
* Returns a list of styles.
*
* @param string $theme
* Name of the theme to get styles for.
* @param array $filter
* Style properties to filter by. Each key is a property name, and value is
* a corresponding filter value.
*
* @return array
* Array of styles - all or just filtered by conditions from the $filter
* argument. Keys are machine names and each element is a corresponding style
* array, which structure is the same as returned from
* styleswitcher_style_load().
*
* @see styleswitcher_style_load()
*/
function styleswitcher_style_load_multiple($theme, array $filter = []) {
$styles =& drupal_static(__FUNCTION__, []);
if (!isset($styles[$theme])) {
$theme_styles = styleswitcher_custom_styles() + styleswitcher_theme_styles($theme);
$settings = styleswitcher_styles_settings($theme);
foreach (array_keys($theme_styles) as $i => $key) {
if (isset($settings[$key])) {
$theme_styles[$key] += $settings[$key];
}
// Default settings.
$theme_styles[$key] += [
'is_default' => FALSE,
'status' => TRUE,
'weight' => 0,
'_i' => $i,
'theme' => $theme,
];
}
$styles[$theme] = $theme_styles;
}
if (empty($filter)) {
return $styles[$theme];
}
$return = [];
foreach ($styles[$theme] as $key => $style) {
// Check the requested conditions.
foreach ($filter as $property => $value) {
if ($style[$property] != $value) {
continue 2;
}
}
$return[$key] = $style;
}
return $return;
}
/**
* Returns a list of styles with theme-specific settings.
*
* @param string $theme
* Name of the theme to get styles settings for.
*
* @return array
* Array which keys are styles machine names and each element is a
* corresponding array with settings: weight, status and is_default. All
* settings are optional. See styleswitcher_style_load() for their
* descriptions.
*
* @see styleswitcher_style_load()
*/
function styleswitcher_styles_settings($theme) {
$settings = Drupal::config('styleswitcher.styles_settings')
->get('settings') ?? [];
if (empty($settings[$theme])) {
$settings[$theme] = [];
styleswitcher_theme_styles($theme);
// Disable the blank style if theme has its own default one.
if (styleswitcher_theme_default_style_key($theme)) {
$settings[$theme]['custom/default']['status'] = FALSE;
}
}
return $settings[$theme];
}
/**
* Returns a list of custom styles.
*
* @return array
* Array which keys are styles machine names and each element is a
* corresponding array with properties: name, label and path. All properties
* are mandatory. See styleswitcher_style_load() for their descriptions.
*
* @see styleswitcher_style_load()
*/
function styleswitcher_custom_styles() {
$defaults['custom/default'] = [
'name' => 'custom/default',
'label' => 'Default',
'path' => NULL,
];
$styles = Drupal::config('styleswitcher.custom_styles')
->get('styles');
return $styles ? $styles : $defaults;
}
/**
* Returns a list of styles provided by a theme.
*
* @param string $theme
* Name of the theme to retrieve styles of.
* @param string|null $original_theme
* (optional) Name of the theme which the function was originally called with.
* While iterating over base themes the $theme argument changes but
* $original_theme keeps the same value.
*
* @return array
* Array of styles. Keys are machine names and each element is a corresponding
* array of style properties: name, label and path. All properties are
* mandatory. See styleswitcher_style_load() for their descriptions.
*
* @see styleswitcher_style_load()
*/
function styleswitcher_theme_styles($theme, $original_theme = NULL) {
$styles =& drupal_static(__FUNCTION__, []);
// Theme can be an empty string if custom style is loading.
if (!$theme) {
return [];
}
if (isset($styles[$theme])) {
return $styles[$theme];
}
$theme_styles = [];
$theme_path = drupal_get_path('theme', $theme);
$themes = Drupal::service('theme_handler')
->listInfo();
$theme_info = isset($themes[$theme]) ? $themes[$theme]->info : [];
if (!isset($original_theme)) {
$original_theme = $theme;
}
// Search base themes for styleswitcher info.
if (isset($theme_info['base theme'])) {
$theme_styles = styleswitcher_theme_styles($theme_info['base theme'], $original_theme);
}
if (!empty($theme_info['styleswitcher'])) {
$info = $theme_info['styleswitcher'];
// Array of alternatives.
if (!empty($info['css'])) {
foreach ($info['css'] as $label => $path) {
$name = 'theme/' . _styleswitcher_style_name($label);
$theme_styles[$name] = [
'name' => $name,
'label' => $label,
'path' => UrlHelper::isExternal($path) ? $path : $theme_path . '/' . $path,
];
}
}
// Default style.
if (isset($info['default'])) {
$default = $info['default'];
}
elseif (isset($info['css']['default'])) {
$default = $info['css']['default'];
unset($theme_styles['theme/default']);
}
if (isset($default)) {
$default_in_existing = FALSE;
// Check if Default points to one of existing alternatives.
foreach ($theme_styles as $name => $style) {
if ($default == $style['label'] || $default == $style['path'] || $theme_path . '/' . $default == $style['path']) {
styleswitcher_theme_default_style_key($original_theme, $name);
$default_in_existing = TRUE;
break;
}
}
// Default is a path not mentioned in css array.
if (!$default_in_existing) {
$defaults['theme/default'] = [
'name' => 'theme/default',
'label' => 'Default',
'path' => UrlHelper::isExternal($default) ? $default : $theme_path . '/' . $default,
];
// Place default style above others.
$theme_styles = $defaults + $theme_styles;
styleswitcher_theme_default_style_key($original_theme, 'theme/default');
}
}
}
// Do not inflate the memory.
if ($theme == $original_theme) {
$styles[$theme] = $theme_styles;
}
return $theme_styles;
}
/**
* Transliterates a human-readable name to a machine name.
*
* @param string $display_name
* Style label.
*
* @return string
* Transliterated name.
*
* @see Drupal.behaviors.machineName.transliterate()
* @see \Drupal\Component\Utility\Html::cleanCssIdentifier()
* @see \Drupal\Component\Utility\Html::getId()
*/
function _styleswitcher_style_name($display_name) {
$name = mb_strtolower($display_name);
$name = strtr($name, [
' ' => '_',
'-' => '_',
]);
$name = preg_replace('/[^a-z0-9_]/', '', $name);
return $name;
}
/**
* Saves/returns the key of default style provided by a theme.
*
* When theme's .info file is scanned for styles to switch this function is
* called with argument to statically save a style key for further use. And
* after, when this function is called it returns the key saved earlier.
*
* @param string $theme
* Name of the theme which default style is being saved/requested.
* @param string|null $key
* The key of theme's default style.
*
* @return string|null
* The key of theme's default style.
*/
function styleswitcher_theme_default_style_key($theme, $key = NULL) {
$default_key =& drupal_static(__FUNCTION__, []);
if (isset($key)) {
$default_key[$theme] = $key;
}
return isset($default_key[$theme]) ? $default_key[$theme] : NULL;
}
/**
* Finds the default style and returns its key.
*
* @param string $theme
* Name of the theme to find the default style for.
*
* @return string
* The key of the default style.
*/
function styleswitcher_default_style_key($theme) {
$default_key =& drupal_static(__FUNCTION__, []);
// Search the default style explicitly set by admin.
if (!isset($default_key[$theme])) {
$styles = styleswitcher_style_load_multiple($theme, [
'is_default' => TRUE,
]);
$default_key[$theme] = key($styles);
}
// Plan B. If default style is not set in styles configuration form by admin
// then find out initial default style defined by theme.
if (!isset($default_key[$theme])) {
styleswitcher_theme_styles($theme);
$default_key[$theme] = styleswitcher_theme_default_style_key($theme);
}
// Fallback to the blank style.
if (!isset($default_key[$theme])) {
$styles = styleswitcher_style_load_multiple($theme, [
'path' => NULL,
]);
$default_key[$theme] = key($styles);
}
return $default_key[$theme];
}
/**
* Implements hook_theme().
*/
function styleswitcher_theme() {
return [
'styleswitcher_admin_styles_table' => [
'render element' => 'form',
'function' => 'theme_styleswitcher_admin_styles_table',
],
'styleswitcher_admin_style_overview' => [
'variables' => [
'style' => NULL,
],
'function' => 'theme_styleswitcher_admin_style_overview',
],
];
}
/**
* Returns HTML for the styles settings overview form element.
*
* @param array $variables
* An associative array containing:
* - form: An element representing the form.
*
* @ingroup themeable
*/
function theme_styleswitcher_admin_styles_table(array $variables) {
$form = $variables['form'];
$header = [
t('Style'),
t('Enabled'),
t('Default'),
t('Weight'),
];
$rows = [];
if (!empty($form['weight'])) {
foreach (Element::children($form['weight']) as $key) {
if ($key == $form['default']['#default_value']) {
$form['enabled'][$key]['#attributes']['disabled'] = 'disabled';
}
$label = $form['label'][$key]['#markup'];
$form['enabled'][$key]['#title'] = t('Enable @title', [
'@title' => $label,
]);
$form['enabled'][$key]['#title_display'] = 'invisible';
$form['default'][$key]['#title'] = t('Set @title as default', [
'@title' => $label,
]);
$form['default'][$key]['#title_display'] = 'invisible';
// Build the table row.
$row = [
[
'data' => $form['name'][$key],
],
[
'data' => $form['enabled'][$key],
],
[
'data' => $form['default'][$key],
],
[
'data' => $form['weight'][$key],
],
];
$rows[] = [
'data' => $row,
'class' => [
'draggable',
],
];
}
}
$table = [
'#type' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => t('No styles to switch.'),
'#attributes' => [
'id' => 'styleswitcher-styles-table',
],
'#tabledrag' => [
0 => [
'action' => 'order',
'relationship' => 'sibling',
'group' => 'styleswitcher-style-weight',
],
],
];
return Drupal::service('renderer')
->render($table);
}
/**
* Returns HTML for a style description on the styles overview page.
*
* @param array $variables
* An associative array containing:
* - style: A style array as returned from styleswitcher_style_load().
*
* @see styleswitcher_style_load()
*
* @ingroup themeable
*/
function theme_styleswitcher_admin_style_overview(array $variables) {
$style = $variables['style'];
$output = Html::escape($style['label']);
$output .= ' <small>' . t('(Machine name: @name)', [
'@name' => $style['name'],
]) . '</small>';
if (isset($style['path'])) {
$description = $style['path'];
}
else {
$description = t('Blank style which just removes effect of others.');
}
$output .= '<div class="description">' . $description . '</div>';
return $output;
}
/**
* Sorts styles by weight and index.
*
* This function first compares style weights, and then - if weights are equal -
* style index numbers.
*
* The compared items (function parameters) should be associative arrays that
* include a 'weight' and an '_i' elements.
*
* Callback for uasort() within
* Drupal\styleswitcher\Plugin\Block\Styleswitcher::build() and
* Drupal\styleswitcher\Form\StyleswitcherConfigTheme::buildForm().
*
* @see \Drupal\styleswitcher\Plugin\Block\Styleswitcher::build()
* @see \Drupal\styleswitcher\Form\StyleswitcherConfigTheme::buildForm()
*/
function styleswitcher_sort(array $a, array $b) {
$property = $a['weight'] != $b['weight'] ? 'weight' : '_i';
return $a[$property] - $b[$property];
}
/**
* Implements hook_themes_uninstalled().
*/
function styleswitcher_themes_uninstalled(array $theme_list) {
// Delete styleswitcher settings of disabled themes.
$config = Drupal::configFactory()
->getEditable('styleswitcher.styles_settings');
$settings = $config
->get('settings') ?? [];
$settings = array_diff_key($settings, array_flip($theme_list));
$config
->set('settings', $settings)
->save();
}
Functions
Name | Description |
---|---|
styleswitcher_css_alter | Implements hook_css_alter(). |
styleswitcher_custom_styles | Returns a list of custom styles. |
styleswitcher_default_style_key | Finds the default style and returns its key. |
styleswitcher_element_info_alter | Implements hook_element_info_alter(). |
styleswitcher_page_attachments | Implements hook_page_attachments(). |
styleswitcher_pre_render_html_tag | Render API callback: Adds HTML id for the Style Switcher style link. |
styleswitcher_sort | Sorts styles by weight and index. |
styleswitcher_styles_settings | Returns a list of styles with theme-specific settings. |
styleswitcher_style_load | Loads a style array by its machine name and a theme name. |
styleswitcher_style_load_multiple | Returns a list of styles. |
styleswitcher_theme | Implements hook_theme(). |
styleswitcher_themes_uninstalled | Implements hook_themes_uninstalled(). |
styleswitcher_theme_default_style_key | Saves/returns the key of default style provided by a theme. |
styleswitcher_theme_styles | Returns a list of styles provided by a theme. |
theme_styleswitcher_admin_styles_table | Returns HTML for the styles settings overview form element. |
theme_styleswitcher_admin_style_overview | Returns HTML for a style description on the styles overview page. |
_styleswitcher_style_name | Transliterates a human-readable name to a machine name. |
Constants
Name | Description |
---|---|
STYLESWITCHER_COOKIE_EXPIRE | Indicates that the cookie must live 365 days more. |