altcolor-1.0.0-beta1/src/Plugin/AltColorPluginManager.php
src/Plugin/AltColorPluginManager.php
<?php
namespace Drupal\altcolor\Plugin;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;
use Drupal\Core\Theme\ThemeInitializationInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
/**
* Provides an alternative color manager.
*/
class AltColorPluginManager extends DefaultPluginManager implements AltColorPluginManagerInterface {
/**
* Provides default values for ThemeColors plugins.
*
* @var array
*/
protected $defaults = [
'colors' => [],
'schemes' => [],
'class' => 'Drupal\altcolor\Plugin\ThemeColors',
];
/**
* {@inheritdoc}
*/
protected $pluginInterface = 'Drupal\altcolor\Plugin\ThemeColorsInterface';
/**
* Static cache of color definitions by theme.
*
* @var array
*/
protected $colorDefinitionsByTheme;
/**
* Constructs a new AlternativeColorManager instance.
*/
public function __construct(
protected ThemeHandlerInterface $themeHandler,
protected ThemeManagerInterface $themeManager,
protected ThemeInitializationInterface $themeInitialization,
CacheBackendInterface $cache_backend,
) {
$this->factory = new ContainerFactory($this, $this->pluginInterface);
$this->setCacheBackend($cache_backend, 'altcolor');
}
/**
* {@inheritdoc}
*/
protected function getDiscovery() {
if (!isset($this->discovery)) {
$this->discovery = new YamlDiscovery('colors', $this->themeHandler->getThemeDirectories());
$this->discovery->addTranslatableProperty('label', 'label_context');
$this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
}
return $this->discovery;
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id): void {
parent::processDefinition($definition, $plugin_id);
// Check if the *.colors.yml file contains at least one configurable color.
if (empty($definition['colors'])) {
throw new PluginException('At least one configurable color is required.');
}
// Check if all scheme colors have the corresponding configurable colors.
foreach ($definition['schemes'] as $scheme => $content) {
if (!empty(array_diff_key($definition['colors'], $content['colors']))) {
throw new PluginException(sprintf('The colors in scheme (%s) do not match with the configurable colors.', $scheme));
}
// Check if all scheme colors have a correct color format.
foreach ($content['colors'] as $color_field => $color_value) {
if (!preg_match('/^#[a-fA-F0-9]{6}$/', $color_value)) {
throw new PluginException(sprintf('The colors in scheme (%s) must be 7-character string specifying a color hexadecimal format.', $scheme));
}
}
}
}
/**
* {@inheritdoc}
*/
protected function providerExists($provider): bool {
return $this->themeHandler->themeExists($provider);
}
/**
* {@inheritdoc}
*/
public function getColorDefinitionsForActiveTheme(): ThemeColorsInterface|NULL {
$active_theme = $this->themeManager->getActiveTheme();
return $this->getColorDefinitionsByTheme($active_theme->getName());
}
/**
* {@inheritdoc}
*/
public function getColorDefinitionsByTheme($theme): ThemeColorsInterface|NULL {
if (!isset($this->colorDefinitionsByTheme[$theme])) {
foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
// Store the color definitions per theme.
$this->colorDefinitionsByTheme[$plugin_definition['provider']] = $plugin_definition;
}
// Initialize an active theme by name.
$active_theme = $this->themeInitialization->getActiveThemeByName($theme);
// Resolve all the parent themes and get the color definitions in reversed
// order. This ensures that the color definitions from the top-most base
// theme are processed first, and each subtheme can override its parent
// theme.
$theme_chain = array_reverse(array_keys($active_theme->getBaseThemeExtensions()));
$theme_chain[] = $active_theme->getName();
// Use an empty definition and override it with the first theme in the
// chain that provides a color definition. Keep updating the override for
// every encountered theme with a color definition. Every extending theme
// without its own definition will inherit the definition from the parent.
$definition = NULL;
foreach ($theme_chain as $extension) {
$color_definition = $this->colorDefinitionsByTheme[$extension] ?? NULL;
if (!empty($color_definition)) {
$definition = $color_definition;
}
$this->colorDefinitionsByTheme[$extension] = $definition;
}
}
// Make sure to use the provider that is defined in the color definition. An
// extending theme might not define a plugin but use its parent plugin
// instead.
return !empty($this->colorDefinitionsByTheme[$theme])
? $this->createInstance($this->colorDefinitionsByTheme[$theme]['provider'], $this->colorDefinitionsByTheme[$theme])
: NULL;
}
/**
* {@inheritdoc}
*/
public function clearCachedDefinitions() {
parent::clearCachedDefinitions();
$this->colorDefinitionsByTheme = [];
}
}
