class IconPackManager
Defines an icon pack plugin manager to deal with icons.
An extension can define an icon pack in an EXTENSION_NAME.icons.yml file contained in the extension's base directory. Each icon pack must have an `extractor` and `template` property. An optional `config` property can be required based on the value of the `extractor` property.
example_pack:
extractor: (string) Plugin ID of the IconExtractor. Provided extractors are
`path`, `svg`, and `svg_sprite`. Contributed modules can provide more
extractors for different use cases. These extractors will use the value
of `config: sources` below to discover the icons and build the icon list
for this icon pack.
template: (string) Twig template to render the icon and the icon values
available in the template:
- `icon_id`: Icon ID based on filename or {icon_id} pattern.
- `source`: Icon path or URL relative to the extension or to the Drupal
root.
- all specific values from the extractor plugin, extractor `svg` will
provide a `content` variable with the SVG icon code without <svg>
wrapper.
- all `settings` values if set below based on keys.
config:
sources: (array) Mandatory for extractors: `path`, `svg`, `svg_sprite`.
This list of paths or URLs is used to generate the list of icons for
this pack. Extractors `path` and `svg_sprite` allow remote files, while
`svg` does not allow remote files for security concerns, as the extractor
generates a variable with the content of the SVG file. Examples of
values:
- path/to/relative/*.svg
- path/to/relative/{icon_id}-suffix.svg # Extract icon id.
- /path/relative/drupal/root/*.svg # For icons in /libraries.
- http://www.my_domain.com/my_icon.png
- ...
# ... Other keys for specific or contributed extractor plugins.
# Recommended values:
label: (string) The name of the icon pack for display
# Optional values:
description: (string) The description of the icon pack for display.
license:
name: (string) A System Package Data Exchange (SPDX) license identifier
such as "GPL-2.0-or-later" (see https://spdx.org/licenses/), or if
not applicable, the human-readable name of the license.
url: (string) The URL of the license information for the version
of the library used.
gpl-compatible: A Boolean for whether this library is GPL compatible.
links: (array)
- (string) The URL of a Documentation page.
- ...
version: (string) The version of the icon pack.
enabled: (boolean) Set FALSE to disable the icon pack discovering process.
Definition will not be populated with icons. Defaults to TRUE.
preview: (string) Optional Twig template for previewing icons in the admin
backend when the standard template does not support proper icon display.
This is particularly useful for font-based icon packs that use a format
like <i class="..">. By default, the admin preview relies on the
<img src=""> template. This feature aids contrib module implementations
that integrate icons with the Field API or CKEditor when a preview is
necessary.
library: (string) Drupal library machine name to include.
# Optional values for the template; they must follow JSON Schema and can
# only be non-scalar primitives.
# A specific class \Drupal\Core\Theme\Icon\IconExtractorSettingsForm
# will transform these settings into Drupal Form API to be available as a
# form for contributed modules implementing icons for FormElement, Field
# API, Menu, CKEditor, or other Drupal APIs.
# Constraints in the form are indicative and will not apply to the values
# passed to the template; the implementation, for example, a FormElement
# must enforce the constraints.
settings: (array)
FORM_KEY: (string) Name of the setting in the template.
title : (string) Title of the setting.
description : (string) Optional description of the setting.
type : (string) Primitive type: string, number, integer, boolean.
default: (mixed) Form default value, will not be used as default
value in the template, template must use |default() twig filter.
[...] Specific JSON Schema values like multipleOf, minimum, maximum...
For example:
my_icon_pack:
label: "My icons"
description: "My UI Icons pack to use everywhere."
license:
name: GPL3-or-later
url: https://www.gnu.org/licenses/gpl-3.0.html
gpl-compatible: true
links:
- https://homepage.com
- https://homepage.com#usage
version: 1.0.0
enabled: true
extractor: svg
config:
sources:
- icons/{icon_id}.svg
- icons_grouped/{group}/{icon_id}.svg
settings:
size:
title: "Size"
type: "integer"
minimum: 24
default: 32
template: >
<img src={{ source }} width="{{ size|default(32) }}" height="{{ size|default(32) }}"/>
library: "my_theme/my_lib"
@internal The icon API is experimental and is not meant for production use. See https://www.drupal.org/core/experimental for more information.
Hierarchy
- class \Drupal\Component\Plugin\PluginManagerBase implements \Drupal\Component\Plugin\PluginManagerInterface uses \Drupal\Component\Plugin\Discovery\DiscoveryTrait
- class \Drupal\Core\Plugin\DefaultPluginManager extends \Drupal\Component\Plugin\PluginManagerBase implements \Drupal\Component\Plugin\PluginManagerInterface, \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface, \Drupal\Core\Cache\CacheableDependencyInterface uses \Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait, \Drupal\Core\Cache\UseCacheBackendTrait
- class \Drupal\Core\Theme\Icon\Plugin\IconPackManager extends \Drupal\Core\Plugin\DefaultPluginManager implements \Drupal\Core\Theme\Icon\Plugin\IconPackManagerInterface
- class \Drupal\Core\Plugin\DefaultPluginManager extends \Drupal\Component\Plugin\PluginManagerBase implements \Drupal\Component\Plugin\PluginManagerInterface, \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface, \Drupal\Core\Cache\CacheableDependencyInterface uses \Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait, \Drupal\Core\Cache\UseCacheBackendTrait
Expanded class hierarchy of IconPackManager
See also
\Drupal\Core\Theme\Icon\IconExtractorInterface
\Drupal\Core\Theme\Icon\IconExtractorWithFinderInterface
\Drupal\Core\Theme\Icon\IconExtractorSettingsForm
1 file declares its use of IconPackManager
- IconPackManagerKernelTest.php in core/
tests/ Drupal/ KernelTests/ Core/ Theme/ Icon/ IconPackManagerKernelTest.php
File
-
core/
lib/ Drupal/ Core/ Theme/ Icon/ Plugin/ IconPackManager.php, line 146
Namespace
Drupal\Core\Theme\Icon\PluginView source
class IconPackManager extends DefaultPluginManager implements IconPackManagerInterface {
private const SCHEMA_VALIDATE = 'core/assets/schemas/v1/icon_pack.schema.json';
/**
* The schema validator.
*
* This property will only be set if the validator library is available.
*
* @var \JsonSchema\Validator|null
*/
private ?Validator $validator = NULL;
/**
* Constructs the IconPackPluginManager object.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $themeHandler
* The theme handler.
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
* The cache backend.
* @param \Drupal\Core\Theme\Icon\IconExtractorPluginManager $iconPackExtractorManager
* The icon plugin extractor service.
* @param \Drupal\Core\Theme\Icon\IconCollector $iconCollector
* The icon cache collector service.
* @param string $appRoot
* The application root.
*/
public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $themeHandler, CacheBackendInterface $cacheBackend, IconExtractorPluginManager $iconPackExtractorManager, IconCollector $iconCollector, string $appRoot) {
$this->moduleHandler = $module_handler;
$this->factory = new ContainerFactory($this);
$this->alterInfo('icon_pack');
$this->setCacheBackend($cacheBackend, 'icon_pack', [
'icon_pack_plugin',
'icon_pack_collector',
]);
}
/**
* Sets the validator service if available.
*
* @param \JsonSchema\Validator|null $validator
* The JSON Validator class.
*/
public function setValidator(?Validator $validator = NULL) : void {
if (NULL !== $validator) {
$this->validator = $validator;
return;
}
if (class_exists(Validator::class)) {
$this->validator = new Validator();
}
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) : void {
if (preg_match('@[^a-z0-9_]@', $plugin_id)) {
throw new IconPackConfigErrorException(sprintf('Invalid icon pack id in: %s, name: %s must contain only lowercase letters, numbers, and underscores.', $definition['provider'], $plugin_id));
}
$this->validateDefinition($definition);
// Do not include disabled definition with `enabled: false`.
if (!($definition['enabled'] ?? TRUE)) {
return;
}
if (!isset($definition['provider'])) {
return;
}
// Provide path information for extractors.
$relative_path = $this->moduleHandler
->moduleExists($definition['provider']) ? $this->moduleHandler
->getModule($definition['provider'])
->getPath() : $this->themeHandler
->getTheme($definition['provider'])
->getPath();
$definition['relative_path'] = $relative_path;
// To avoid the need for appRoot in extractors.
$definition['absolute_path'] = sprintf('%s/%s', $this->appRoot, $relative_path);
// Load all discovered icon ids in the definition so they are cached.
$definition['icons'] = $this->getIconsFromDefinition($definition);
}
/**
* {@inheritdoc}
*/
public function getIcons(array $allowed_icon_pack = []) : array {
$definitions = $this->getDefinitions();
if (NULL === $definitions) {
return [];
}
$icons = [];
foreach ($definitions as $definition) {
if ($allowed_icon_pack && !in_array($definition['id'], $allowed_icon_pack, TRUE)) {
continue;
}
$icons = array_merge($icons, $definition['icons'] ?? []);
}
return $icons;
}
/**
* {@inheritdoc}
*/
public function getIcon(string $icon_full_id) : ?IconDefinitionInterface {
return $this->iconCollector
->get($icon_full_id, $this->getDefinitions());
}
/**
* {@inheritdoc}
*/
public function getExtractorFormDefaults(string $pack_id) : array {
$icon_pack_definitions = $this->getDefinitions();
if (!isset($icon_pack_definitions[$pack_id]) || !isset($icon_pack_definitions[$pack_id]['settings'])) {
return [];
}
$default = [];
foreach ($icon_pack_definitions[$pack_id]['settings'] as $name => $definition) {
if (isset($definition['default'])) {
$default[$name] = $definition['default'];
}
}
return $default;
}
/**
* {@inheritdoc}
*/
public function getExtractorPluginForms(array &$form, FormStateInterface $form_state, array $default_settings = [], array $allowed_icon_pack = [], bool $wrap_details = FALSE) : void {
$icon_pack_definitions = $this->getDefinitions();
if (NULL === $icon_pack_definitions) {
return;
}
if (!empty($allowed_icon_pack)) {
$icon_pack_definitions = array_intersect_key($icon_pack_definitions, $allowed_icon_pack);
}
$extractor_forms = $this->iconPackExtractorManager
->getExtractorForms($icon_pack_definitions);
if (empty($extractor_forms)) {
return;
}
foreach ($icon_pack_definitions as $pack_id => $definition) {
// Simply skip if no settings declared in definition.
if (count($definition['settings'] ?? []) === 0) {
continue;
}
// Create the container for each extractor settings used to have the
// extractor form.
$form[$pack_id] = [
'#type' => $wrap_details ? 'details' : 'container',
'#title' => $definition['label'] ?? $pack_id,
];
// Create the extractor form and set settings so we can build with values.
$subform_state = SubformState::createForSubform($form[$pack_id], $form, $form_state);
$subform_state->getCompleteFormState()
->setValue('saved_values', $default_settings[$pack_id] ?? []);
if (is_a($extractor_forms[$pack_id], '\\Drupal\\Core\\Plugin\\PluginFormInterface')) {
$form[$pack_id] += $extractor_forms[$pack_id]->buildConfigurationForm($form[$pack_id], $subform_state);
}
}
}
/**
* {@inheritdoc}
*/
public function listIconPackOptions(bool $include_description = FALSE) : array {
$icon_pack_definitions = $this->getDefinitions();
if (NULL === $icon_pack_definitions) {
return [];
}
$options = [];
foreach ($icon_pack_definitions as $definition) {
if (empty($definition['icons'])) {
continue;
}
$label = $definition['label'] ?? $definition['id'];
if ($include_description && isset($definition['description'])) {
$label = sprintf('%s - %s', $label, $definition['description']);
}
$options[$definition['id']] = sprintf('%s (%u)', $label, count($definition['icons']));
}
natsort($options);
return $options;
}
/**
* {@inheritdoc}
*/
protected function getDiscovery() : DiscoveryInterface {
if (!$this->discovery) {
$this->discovery = new YamlDiscovery('icons', $this->moduleHandler
->getModuleDirectories() + $this->themeHandler
->getThemeDirectories());
$this->discovery
->addTranslatableProperty('label')
->addTranslatableProperty('description');
$this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
}
return $this->discovery;
}
/**
* {@inheritdoc}
*/
protected function providerExists(mixed $provider) : bool {
return $this->moduleHandler
->moduleExists($provider) || $this->themeHandler
->themeExists($provider);
}
/**
* Discover list of icons from definition extractor.
*
* @param array $definition
* The definition.
*
* @return array
* Discovered icons.
*/
private function getIconsFromDefinition(array $definition) : array {
if (!isset($definition['extractor'])) {
return [];
}
/** @var \Drupal\Core\Theme\Icon\IconExtractorInterface $extractor */
$extractor = $this->iconPackExtractorManager
->createInstance($definition['extractor'], $definition);
return $extractor->discoverIcons();
}
/**
* Validates a definition against the JSON schema specification.
*
* @param array $definition
* The definition to alter.
*
* @return bool
* FALSE if the response failed validation, otherwise TRUE.
*
* @throws \Drupal\Core\Theme\Icon\Exception\IconPackConfigErrorException
* Thrown when the definition is not valid.
*/
private function validateDefinition(array $definition) : bool {
// If the validator isn't set, then the validation library is not installed.
if (!$this->validator) {
return TRUE;
}
$schema_ref = sprintf('file://%s/%s', $this->appRoot, self::SCHEMA_VALIDATE);
$schema = (object) [
'$ref' => $schema_ref,
];
$definition_object = Validator::arrayToObjectRecursive($definition);
$this->validator
->validate($definition_object, $schema, Constraint::CHECK_MODE_COERCE_TYPES);
if ($this->validator
->isValid()) {
return TRUE;
}
$message_parts = array_map(static fn(array $error): string => sprintf("[%s] %s", $error['property'], $error['message']), $this->validator
->getErrors());
$message = implode(", ", $message_parts);
throw new IconPackConfigErrorException(sprintf('%s:%s Error in definition `%s`:%s', $definition['provider'], $definition['id'], $definition_object->id, $message));
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
DefaultPluginManager::$additionalAnnotationNamespaces | protected | property | Additional annotation namespaces. | ||
DefaultPluginManager::$alterHook | protected | property | Name of the alter hook if one should be invoked. | ||
DefaultPluginManager::$cacheKey | protected | property | The cache key. | ||
DefaultPluginManager::$cacheTags | protected | property | An array of cache tags to use for the cached definitions. | ||
DefaultPluginManager::$defaults | protected | property | A set of defaults to be referenced by $this->processDefinition(). | 12 | |
DefaultPluginManager::$moduleExtensionList | protected | property | The module extension list. | ||
DefaultPluginManager::$moduleHandler | protected | property | The module handler to invoke the alter hook. | 1 | |
DefaultPluginManager::$namespaces | protected | property | An object of root paths that are traversable. | ||
DefaultPluginManager::$pluginDefinitionAnnotationName | protected | property | The name of the annotation that contains the plugin definition. | ||
DefaultPluginManager::$pluginDefinitionAttributeName | protected | property | The name of the attribute that contains the plugin definition. | ||
DefaultPluginManager::$pluginInterface | protected | property | The interface each plugin should implement. | 1 | |
DefaultPluginManager::$subdir | protected | property | The subdirectory within a namespace to look for plugins. | ||
DefaultPluginManager::alterDefinitions | protected | function | Invokes the hook to alter the definitions if the alter hook is set. | 5 | |
DefaultPluginManager::alterInfo | protected | function | Sets the alter hook name. | ||
DefaultPluginManager::clearCachedDefinitions | public | function | Clears static and persistent plugin definition caches. | Overrides CachedDiscoveryInterface::clearCachedDefinitions | 11 |
DefaultPluginManager::extractProviderFromDefinition | protected | function | Extracts the provider from a plugin definition. | ||
DefaultPluginManager::findDefinitions | protected | function | Finds plugin definitions. | 7 | |
DefaultPluginManager::getCacheContexts | public | function | The cache contexts associated with this object. | Overrides CacheableDependencyInterface::getCacheContexts | |
DefaultPluginManager::getCachedDefinitions | protected | function | Returns the cached plugin definitions of the decorated discovery class. | ||
DefaultPluginManager::getCacheMaxAge | public | function | The maximum age for which this object may be cached. | Overrides CacheableDependencyInterface::getCacheMaxAge | |
DefaultPluginManager::getCacheTags | public | function | The cache tags associated with this object. | Overrides CacheableDependencyInterface::getCacheTags | |
DefaultPluginManager::getDefinitions | public | function | Gets the definition of all plugins for this type. | Overrides DiscoveryTrait::getDefinitions | 1 |
DefaultPluginManager::getFactory | protected | function | Gets the plugin factory. | Overrides PluginManagerBase::getFactory | |
DefaultPluginManager::setCacheBackend | public | function | Initialize the cache backend. | ||
DefaultPluginManager::setCachedDefinitions | protected | function | Sets a cache of plugin definitions for the decorated discovery class. | ||
DefaultPluginManager::useCaches | public | function | Disable the use of caches. | Overrides CachedDiscoveryInterface::useCaches | 1 |
DiscoveryCachedTrait::$definitions | protected | property | Cached definitions array. | 1 | |
DiscoveryCachedTrait::getDefinition | public | function | Overrides DiscoveryTrait::getDefinition | 3 | |
DiscoveryTrait::doGetDefinition | protected | function | Gets a specific plugin definition. | ||
DiscoveryTrait::hasDefinition | public | function | |||
IconPackManager::$validator | private | property | The schema validator. | ||
IconPackManager::getDiscovery | protected | function | Gets the plugin discovery. | Overrides DefaultPluginManager::getDiscovery | |
IconPackManager::getExtractorFormDefaults | public | function | Retrieve extractor default options. | Overrides IconPackManagerInterface::getExtractorFormDefaults | |
IconPackManager::getExtractorPluginForms | public | function | Retrieve extractor forms based on the provided icon set limit. | Overrides IconPackManagerInterface::getExtractorPluginForms | |
IconPackManager::getIcon | public | function | Get definition of a specific icon. | Overrides IconPackManagerInterface::getIcon | |
IconPackManager::getIcons | public | function | Get a list of all the icons within definitions. | Overrides IconPackManagerInterface::getIcons | |
IconPackManager::getIconsFromDefinition | private | function | Discover list of icons from definition extractor. | ||
IconPackManager::listIconPackOptions | public | function | Populates a key-value pair of available icon pack. | Overrides IconPackManagerInterface::listIconPackOptions | |
IconPackManager::processDefinition | public | function | Performs extra processing on plugin definitions. | Overrides DefaultPluginManager::processDefinition | |
IconPackManager::providerExists | protected | function | Determines if the provider of a definition exists. | Overrides DefaultPluginManager::providerExists | |
IconPackManager::SCHEMA_VALIDATE | private | constant | |||
IconPackManager::setValidator | public | function | Sets the validator service if available. | ||
IconPackManager::validateDefinition | private | function | Validates a definition against the JSON schema specification. | ||
IconPackManager::__construct | public | function | Constructs the IconPackPluginManager object. | Overrides DefaultPluginManager::__construct | |
PluginManagerBase::$discovery | protected | property | The object that discovers plugins managed by this manager. | ||
PluginManagerBase::$factory | protected | property | The object that instantiates plugins managed by this manager. | ||
PluginManagerBase::$mapper | protected | property | The object that returns the preconfigured plugin instance appropriate for a particular runtime condition. | ||
PluginManagerBase::createInstance | public | function | 15 | ||
PluginManagerBase::getFallbackPluginId | protected | function | 6 | ||
PluginManagerBase::getInstance | public | function | 6 | ||
PluginManagerBase::handlePluginNotFound | protected | function | Allows plugin managers to specify custom behavior if a plugin is not found. | 1 | |
UseCacheBackendTrait::$cacheBackend | protected | property | Cache backend instance. | ||
UseCacheBackendTrait::$useCaches | protected | property | Flag whether caches should be used or skipped. | ||
UseCacheBackendTrait::cacheGet | protected | function | Fetches from the cache backend, respecting the use caches flag. | ||
UseCacheBackendTrait::cacheSet | protected | function | Stores data in the persistent cache, respecting the use caches flag. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.