- <?php
- * @file
- * Provides the Backdrop API for placing icons.
- *
- * For comprehensive documentation on the icon API, see
- * https://docs.backdropcms.org/documentation/icon-api
- */
-
- define ('CORE_ICON_PATH', 'core/misc/icons');
-
- * Return the markup for outputting an icon by a machine name.
- *
- * This will first check if an icon is provided by the active theme (in the
- * theme's "icons" directory). If so, that icon will be used. Next, the
- * icon will be pulled from any module that provided the icon through
- * hook_icon_info(). If the icon is not provided by a theme or module, a
- * matching icon will be returned from the /core/misc/icons directory.
- *
- * If the "immutable" option is set to TRUE, the selection order will be
- * reversed, that is the original provider of an icon will return the icon
- * rather than allowing any overriding by a theme or module.
- *
- * @param string $icon_name
- * The icon name such as "home", "info", etc. Do not include the file
- * extension.
- * @param array $options
- * An array of options to pass to the icon. Keys include:
- * - alt: An alternative text to be used for screen readers. If not specified
- * the icon will be marked as decorative with aria-hidden="true". Note that
- * in the case of SVG icons, this will rendered as a <title> attribute
- * within the <svg> tag.
- * - attributes: Any additional attributes to add to the icon, including
- * classes (as an array), name, id, etc.
- * - immutable: Boolean value if this icon is not allowed to be overridden
- * by themes or modules. If an icon is provided by core, it cannot be
- * overridden, but an icon can still be added.
- *
- * @return string
- *
- * @see https://docs.backdropcms.org/documentation/icon-api
- *
- * @since 1.28.0 Function added.
- */
- function icon($icon_name, array $options = array()) {
-
- $options += array(
- 'alt' => NULL,
- 'attributes' => array(),
- 'immutable' => FALSE,
- );
-
- $immutable = !empty($options['immutable']);
- $icon_path = icon_get_path($icon_name, $immutable);
-
- if ($icon_path) {
- unset($options['immutable']);
- return theme('icon', array(
- 'name' => $icon_name,
- 'path' => $icon_path,
- 'alt' => $options['alt'],
- 'attributes' => $options['attributes'],
- ));
- }
-
- return '<!-- ' . t('Icon @name not found.', array('@name' => $icon_name)) . ' -->';
- }
-
- * Locates an icon and returns its path.
- *
- * Icons can be provided by themes, modules, and core. Normally, icons are
- * provided by core (from the core/misc/icons directory), but modules can
- * provide additional icons or override core ones. Modules must implement
- * hook_icon_info() to provide icons. Themes can also provide icons or override
- * existing ones by placing icons in their "icons" subdirectory.
- *
- * @param string $icon_name
- * The name of an icon without a file extension. Most icon names can be
- * determined by looking in the core/misc/icons directory.
- * @param boolean $immutable
- * Immutable icons cannot be modified by themes or modules. Instead of
- * allowing overrides, the first system that defines an icon name defines
- * its path. This allows certain icons to lock to the icon provided by core
- * or a module without allowing a theme to override it. Useful in situations
- * where an icon is used across multiple themes (like in the admin bar).
- *
- * @return string|NULL
- * The path to an icon, relative to the Backdrop installation root, with the
- * file extension (usually .svg). Or NULL if an icon is not found.
- *
- * @see hook_icon_info()
- * @see hook_icon_info_alter()
- *
- * @since 1.28.0 Function added.
- */
- function icon_get_path($icon_name, $immutable = FALSE) {
-
- $icon_paths = &backdrop_static(__FUNCTION__, array(
- 'normal' => array(),
- 'immutable' => array(),
- ));
-
- $immutable_type = $immutable ? 'immutable' : 'normal';
- if (isset($icon_paths[$immutable_type][$icon_name])) {
- return $icon_paths[$immutable_type][$icon_name];
- }
-
-
- $search_functions = array(
- '_icon_from_theme',
- '_icon_from_module',
- '_icon_from_core',
- );
-
- if ($immutable) {
- $search_functions = array_reverse($search_functions);
- }
-
- $icon_path = NULL;
- foreach ($search_functions as $function) {
- if ($icon_path = $function($icon_name)) {
- break;
- }
- }
-
-
- $icon_paths[$immutable_type][$icon_name] = $icon_path;
- return $icon_path;
- }
-
- * Checks if the current theme provides an icon.
- *
- * Do not call this function directly. Use icon() instead.
- *
- * @param string $icon_name
- * The icon name to be located.
- *
- * @return string|void
- * The path to the icon if found.
- *
- * @see icon()
- * @private
- */
- function _icon_from_theme($icon_name, $theme = NULL) {
- $theme = isset($theme) ? $theme : $GLOBALS['theme_key'];
-
-
- $theme_icon_directory = theme_get_setting('icon_directory', $theme);
-
-
- if (!$theme_icon_directory) {
- $theme_icon_directory = 'icons';
- }
-
-
- $theme_icon_path = backdrop_get_path('theme', $theme) . '/' . $theme_icon_directory . '/' . $icon_name . '.svg';
-
-
- if (file_exists($theme_icon_path)) {
- return $theme_icon_path;
- }
-
-
-
- $themes = list_themes();
- $theme_info = $themes[$theme];
- if (isset($theme_info->info['base theme'])) {
- return _icon_from_theme($icon_name, $theme_info->info['base theme']);
- }
- }
-
- * Checks if a theme provides an icon.
- *
- * Do not call this function directly. Use icon() instead.
- *
- * @param string $icon_name
- * The icon name to be located.
- *
- * @return string|void
- * The path to the icon if found.
- *
- * @see icon()
- * @private
- */
- function _icon_from_module($icon_name) {
- $icon_info = icon_get_info($icon_name);
- if ($icon_info) {
- if (isset($icon_info['directory'])) {
- $directory = $icon_info['directory'];
- }
- else {
- $directory = backdrop_get_path('module', $icon_info['module']) . '/icons';
- }
- if (isset($icon_info['name'])) {
- $filename = $icon_info['name'] . '.svg';
- }
- else {
- $filename = $icon_name . '.svg';
- }
- $module_icon_path = $directory . '/' . $filename;
- if (file_exists($module_icon_path)) {
- return $module_icon_path;
- }
- }
- }
-
- * Checks if core provides an icon.
- *
- * Do not call this function directly. Use icon() instead.
- *
- * @param string $icon_name
- * The icon name to be located.
- *
- * @return string|void
- * The path to the icon if found.
- *
- * @see icon()
- * @private
- */
- function _icon_from_core($icon_name) {
-
- $misc_icon_path = 'core/misc/icons/' . $icon_name . '.svg';
- if (file_exists($misc_icon_path)) {
- return $misc_icon_path;
- }
- }
-
- * Get info about a module-provided icons.
- *
- * Note this will not return information about core icons or theme-provided
- * icons.
- *
- * @param string|null $icon_name
- * An icon name, with no extension, such as "home" or "info". If omitted,
- * all icon info will be returned.
- *
- * @return array
- *
- * @since 1.28.0 Function added.
- */
- function icon_get_info($icon_name = NULL) {
- $icon_info = &backdrop_static(__FUNCTION__);
-
- if (!isset($icon_info)) {
- $icon_info = array();
- foreach (module_implements('icon_info') as $module) {
- $module_icon_info = module_invoke($module, 'icon_info');
-
- foreach (array_keys($module_icon_info) as $module_icon_name) {
- $module_icon_info[$module_icon_name]['module'] = $module;
- }
- $icon_info = array_merge($icon_info, $module_icon_info);
- }
- backdrop_alter('icon_info', $icon_info);
- }
- if ($icon_name) {
- return isset($icon_info[$icon_name]) ? $icon_info[$icon_name] : NULL;
- }
- else {
- return $icon_info;
- }
- }
-
- * Returns HTML for an inline-icon.
- *
- * This effectively returns the contents of an SVG file. But it could
- * potentially be overridden to replace inlined SVGs with other mechanisms, like
- * an icon font.
- *
- * @param array $variables
- * An associative array containing:
- * - name: The machine name for the icon being displayed.
- * - path: The full path to the icon file, relative to installation root.
- * - alt: Alternative text for this icon. Default icons are SVGs and this
- * value is added as a <title> tag within the SVG. If not specified, the
- * icon will automatically be assigned the aria-hidden="true" attribute.
- * - attributes: Attributes to be added to the icon itself.
- *
- * @return string
- * The HTML output. An empty string if the icon file is not svg.
- *
- * @since 1.28.0 Function added.
- * @since 1.28.1 The <ellipse>, <line>, <polygon> and <polyline> SVG elements
- * are allowed.
- */
- function theme_icon(array $variables) {
-
- if (image_is_svg($variables['path'])) {
-
- $svg_contents = file_get_contents($variables['path']);
- if (strpos($svg_contents, '<svg') === 0) {
-
-
- $allowed_svg_basic_shapes = array(
- 'circle',
- 'ellipse',
- 'line',
- 'polygon',
- 'polyline',
- 'rect',
- );
-
-
-
- $allowed_svg_other = array(
- 'defs',
- 'desc',
- 'linearGradient',
- 'path',
- 'stop',
- 'svg',
- 'title',
- 'use',
- );
-
- $allowed_svg_elements = array_merge($allowed_svg_basic_shapes, $allowed_svg_other);
-
-
- $svg_contents = filter_xss($svg_contents, $allowed_svg_elements);
-
-
- if ($variables['alt']) {
- $variables['attributes']['alt'] = $variables['alt'];
- }
- else {
- $variables['attributes']['aria-hidden'] = 'true';
- }
-
-
- $variables['attributes']['class'] = array_merge(
- array('icon', 'icon--' . $variables['name']),
- isset($variables['attributes']['class']) ? $variables['attributes']['class'] : array()
- );
-
- return image_add_svg_attributes($svg_contents, $variables['attributes']);
- }
- }
- return '';
- }
-
- * Helper function to load icon details.
- *
- * @param string $icon_name
- * The icon name to load details for. If empty, load all icons.
- * @return array
- * An array of icon details, keyed by icon name:
- * - All icons, if no $icon_name was provided
- * - The single result if an $icon_name was provided, or
- * - An empty array if no icon was found for a provided $icon_name.
- */
- function icon_get_all_details($icon_name = '') {
-
-
- if (!empty($icon_name) && _icon_from_core($icon_name)) {
- return array(
- 'project_type' => 'core',
- 'project_description' => t('Backdrop core'),
- 'project_icon_key' => $icon_name,
- 'name' => $icon_name,
- 'directory' => CORE_ICON_PATH,
- ) + icon_get_style($icon_name);
- }
-
-
-
- if (!empty($icon_name) && !icon_get_path($icon_name)) {
- return array();
- }
-
- $icons = &backdrop_static(__FUNCTION__, array());
-
- if (empty($icons)) {
-
- $core_icons = scandir(CORE_ICON_PATH, SCANDIR_SORT_NONE);
- foreach ($core_icons as $icon) {
- if (substr($icon, -4, 4) == '.svg') {
- $icon_key = substr($icon, 0, -4);
- $icons[$icon_key] = array(
- 'project_type' => 'core',
- 'project_description' => t('Backdrop core'),
- 'project_icon_key' => $icon_key,
- 'name' => $icon_key,
- 'directory' => CORE_ICON_PATH,
- ) + icon_get_style($icon_key);
- }
- }
-
- $module_icons = icon_get_info();
- foreach ($module_icons as $name => $icon) {
- $icon_key = isset($icon['name']) ? $icon['name'] : $name;
- $directory = isset($icon['directory']) ? $icon['directory'] : backdrop_get_path('module', $icon['module']) . '/icons';;
- $icons[$name] = array(
- 'project_type' => 'module',
- 'project_description' => t('@module (module)', ['@module' => $icon['module']]),
- 'project_icon_key' => $name,
- 'name' => $icon_key,
- 'directory' => $directory,
- ) + icon_get_style($icon_key);
- }
-
-
- $themes = list_themes();
- $config = config('system.core');
- $active_theme = $GLOBALS['theme_key'];
- $current_themes = array($active_theme);
- if (isset($themes->$active_theme['base_themes'])) {
- $current_themes += array_keys($themes->$active_theme['base_themes']);
- }
-
-
- if ($active_theme == $config->get('admin_theme')) {
- $default_theme = $config->get('theme_default');
- $current_themes[] = $default_theme;
- if (isset($themes->$default_theme['base_themes'])) {
- $current_themes += array_keys($themes->$default_theme['base_themes']);
- }
- }
-
- foreach ($current_themes as $theme) {
- $theme_error = ($theme != $active_theme) ? $theme : '';
-
-
- $theme_icon_directory = theme_get_setting('icon_directory', $theme);
-
-
- if (!$theme_icon_directory) {
- $theme_icon_directory = 'icons';
- }
- $theme_directory = backdrop_get_path('theme', $theme) . '/' . $theme_icon_directory;
- if (is_dir($theme_directory)) {
- $theme_icons = scandir($theme_directory);
- if (!empty($theme_icons)) {
- foreach ($theme_icons as $key => $icon) {
- if (substr($icon, -4, 4) == '.svg') {
- $icon_key = substr($icon, 0, -4);
- $icons[$icon_key] = array(
- 'project_type' => 'theme',
- 'project_description' => t('@theme (theme*)', ['@theme' => $theme]),
- 'project_icon_key' => $icon_key,
- 'name' => $icon_key,
- 'directory' => $theme_directory,
- 'theme_error' => $theme_error,
- ) + icon_get_style($icon_key);
- }
- }
- }
- }
- }
- }
-
- backdrop_alter('icon_get_all_details', $icons);
-
- backdrop_sort($icons, array('project_icon_key' => SORT_STRING));
-
- if (empty($icon_name)) {
- $result = $icons;
- }
- elseif (isset($icons[$icon_name])) {
- $result = $icons[$icon_name];
- }
- else {
- $result = array();
- }
-
- return $result;
- }
-
- * Helper function to get an icon style based on the last segment of the icon
- * name. We have some pre-approved suffixes that correspond to style options.
- *
- * @param string $icon_name
- * The icon name to get the style for.
- *
- * @return array
- * An array keyed with:
- * - 'style': A string containing the style of the icon, or empty for default.
- * - 'is_brand': A boolean indicating whether this is a brand icon.
- */
- function icon_get_style($icon_name) {
- $icon_data = array(
- 'style' => '',
- 'is_brand' => FALSE,
- );
-
- static $icon_allowed_styles;
- if (!isset($icon_allowed_styles)) {
- $icon_allowed_styles = icon_allowed_styles();
- }
-
-
- $icon_name = explode('-', $icon_name);
- $icon_style = array_pop($icon_name);
- $brand_check = array_pop($icon_name);
-
- if (in_array($icon_style, array_keys($icon_allowed_styles))) {
- $icon_data['style'] = $icon_style;
- }
- else {
-
- $icon_data['style'] = 'outline';
- }
- if ($icon_style == 'logo' || $brand_check == 'logo') {
- $icon_data['is_brand'] = TRUE;
- }
-
- return $icon_data;
- }
-
- * Helper function to get list of allowed/known styles.
- *
- * @return array
- * An array of approved styles, keyed by the style machine name.
- */
- function icon_allowed_styles() {
- $allowed_styles = array(
- 'fill' => 'Fill',
- 'outline' => 'Outline (or other)',
- 'thin' => 'Thin',
- 'light' => 'Light',
- 'bold' => 'Bold',
- 'duotone' => 'Duotone'
- );
-
- backdrop_alter('icon_allowed_styles', $allowed_styles);
-
- return $allowed_styles;
- }