annotate core/modules/config_translation/src/ConfigMapperManager.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\config_translation;
Chris@0 4
Chris@0 5 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
Chris@0 6 use Drupal\Core\Cache\CacheBackendInterface;
Chris@0 7 use Drupal\Core\Config\TypedConfigManagerInterface;
Chris@0 8 use Drupal\Core\Extension\ModuleHandlerInterface;
Chris@0 9 use Drupal\Core\Extension\ThemeHandlerInterface;
Chris@0 10 use Drupal\Core\Language\LanguageManagerInterface;
Chris@0 11 use Drupal\Core\Plugin\DefaultPluginManager;
Chris@0 12 use Drupal\Core\Plugin\Discovery\InfoHookDecorator;
Chris@0 13 use Drupal\Core\Plugin\Discovery\YamlDiscovery;
Chris@0 14 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
Chris@0 15 use Drupal\Core\Plugin\Factory\ContainerFactory;
Chris@0 16 use Drupal\Core\TypedData\TraversableTypedDataInterface;
Chris@0 17 use Drupal\Core\TypedData\TypedDataInterface;
Chris@0 18 use Symfony\Component\Routing\RouteCollection;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * Manages plugins for configuration translation mappers.
Chris@0 22 */
Chris@0 23 class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperManagerInterface {
Chris@0 24
Chris@0 25 /**
Chris@0 26 * The typed config manager.
Chris@0 27 *
Chris@0 28 * @var \Drupal\Core\Config\TypedConfigManagerInterface
Chris@0 29 */
Chris@0 30 protected $typedConfigManager;
Chris@0 31
Chris@0 32 /**
Chris@0 33 * The theme handler.
Chris@0 34 *
Chris@0 35 * @var \Drupal\Core\Extension\ThemeHandlerInterface
Chris@0 36 */
Chris@0 37 protected $themeHandler;
Chris@0 38
Chris@0 39 /**
Chris@0 40 * {@inheritdoc}
Chris@0 41 */
Chris@0 42 protected $defaults = [
Chris@0 43 'title' => '',
Chris@0 44 'names' => [],
Chris@0 45 'weight' => 20,
Chris@0 46 'class' => '\Drupal\config_translation\ConfigNamesMapper',
Chris@0 47 ];
Chris@0 48
Chris@0 49 /**
Chris@0 50 * Constructs a ConfigMapperManager.
Chris@0 51 *
Chris@0 52 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
Chris@0 53 * The cache backend.
Chris@0 54 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
Chris@0 55 * The language manager.
Chris@0 56 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
Chris@0 57 * The module handler.
Chris@0 58 * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
Chris@0 59 * The typed config manager.
Chris@0 60 * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
Chris@0 61 * The theme handler.
Chris@0 62 */
Chris@0 63 public function __construct(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManagerInterface $typed_config_manager, ThemeHandlerInterface $theme_handler) {
Chris@0 64 $this->typedConfigManager = $typed_config_manager;
Chris@0 65
Chris@0 66 $this->factory = new ContainerFactory($this, '\Drupal\config_translation\ConfigMapperInterface');
Chris@0 67
Chris@0 68 // Let others alter definitions with hook_config_translation_info_alter().
Chris@0 69 $this->moduleHandler = $module_handler;
Chris@0 70 $this->themeHandler = $theme_handler;
Chris@0 71
Chris@0 72 $this->alterInfo('config_translation_info');
Chris@0 73 // Config translation only uses an info hook discovery, cache by language.
Chris@0 74 $cache_key = 'config_translation_info_plugins' . ':' . $language_manager->getCurrentLanguage()->getId();
Chris@0 75 $this->setCacheBackend($cache_backend, $cache_key, ['config_translation_info_plugins']);
Chris@0 76 }
Chris@0 77
Chris@0 78 /**
Chris@0 79 * {@inheritdoc}
Chris@0 80 */
Chris@0 81 protected function getDiscovery() {
Chris@0 82 if (!isset($this->discovery)) {
Chris@0 83 // Look at all themes and modules.
Chris@0 84 // @todo If the list of installed modules and themes is changed, new
Chris@0 85 // definitions are not picked up immediately and obsolete definitions
Chris@0 86 // are not removed, because the list of search directories is only
Chris@0 87 // compiled once in this constructor. The current code only works due to
Chris@0 88 // coincidence: The request that installs (for instance, a new theme)
Chris@0 89 // does not instantiate this plugin manager at the beginning of the
Chris@0 90 // request; when routes are being rebuilt at the end of the request,
Chris@0 91 // this service only happens to get instantiated with the updated list
Chris@0 92 // of installed themes.
Chris@0 93 $directories = [];
Chris@0 94 foreach ($this->moduleHandler->getModuleList() as $name => $module) {
Chris@0 95 $directories[$name] = $module->getPath();
Chris@0 96 }
Chris@0 97 foreach ($this->themeHandler->listInfo() as $theme) {
Chris@0 98 $directories[$theme->getName()] = $theme->getPath();
Chris@0 99 }
Chris@0 100
Chris@0 101 // Check for files named MODULE.config_translation.yml and
Chris@0 102 // THEME.config_translation.yml in module/theme roots.
Chris@0 103 $this->discovery = new YamlDiscovery('config_translation', $directories);
Chris@0 104 $this->discovery = new InfoHookDecorator($this->discovery, 'config_translation_info');
Chris@0 105 $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
Chris@0 106 }
Chris@0 107 return $this->discovery;
Chris@0 108 }
Chris@0 109
Chris@0 110 /**
Chris@0 111 * {@inheritdoc}
Chris@0 112 */
Chris@0 113 public function getMappers(RouteCollection $collection = NULL) {
Chris@0 114 $mappers = [];
Chris@0 115 foreach ($this->getDefinitions() as $id => $definition) {
Chris@0 116 $mappers[$id] = $this->createInstance($id);
Chris@0 117 if ($collection) {
Chris@0 118 $mappers[$id]->setRouteCollection($collection);
Chris@0 119 }
Chris@0 120 }
Chris@0 121
Chris@0 122 return $mappers;
Chris@0 123 }
Chris@0 124
Chris@0 125 /**
Chris@0 126 * {@inheritdoc}
Chris@0 127 */
Chris@0 128 public function processDefinition(&$definition, $plugin_id) {
Chris@0 129 parent::processDefinition($definition, $plugin_id);
Chris@0 130
Chris@0 131 if (!isset($definition['base_route_name'])) {
Chris@0 132 throw new InvalidPluginDefinitionException($plugin_id, "The plugin definition of the mapper '$plugin_id' does not contain a base_route_name.");
Chris@0 133 }
Chris@0 134 }
Chris@0 135
Chris@0 136 /**
Chris@0 137 * {@inheritdoc}
Chris@0 138 */
Chris@0 139 public function buildDataDefinition(array $definition, $value = NULL, $name = NULL, $parent = NULL) {
Chris@0 140 return $this->typedConfigManager->buildDataDefinition($definition, $value, $name, $parent);
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * {@inheritdoc}
Chris@0 145 */
Chris@0 146 protected function findDefinitions() {
Chris@0 147 $definitions = $this->getDiscovery()->getDefinitions();
Chris@0 148 foreach ($definitions as $plugin_id => &$definition) {
Chris@0 149 $this->processDefinition($definition, $plugin_id);
Chris@0 150 }
Chris@0 151 if ($this->alterHook) {
Chris@0 152 $this->moduleHandler->alter($this->alterHook, $definitions);
Chris@0 153 }
Chris@0 154
Chris@0 155 // If this plugin was provided by a module that does not exist, remove the
Chris@0 156 // plugin definition.
Chris@0 157 foreach ($definitions as $plugin_id => $plugin_definition) {
Chris@0 158 if (isset($plugin_definition['provider']) && !in_array($plugin_definition['provider'], ['core', 'component']) && (!$this->moduleHandler->moduleExists($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array_keys($this->themeHandler->listInfo())))) {
Chris@0 159 unset($definitions[$plugin_id]);
Chris@0 160 }
Chris@0 161 }
Chris@0 162 return $definitions;
Chris@0 163 }
Chris@0 164
Chris@0 165 /**
Chris@0 166 * {@inheritdoc}
Chris@0 167 */
Chris@0 168 public function hasTranslatable($name) {
Chris@0 169 return $this->findTranslatable($this->typedConfigManager->get($name));
Chris@0 170 }
Chris@0 171
Chris@0 172 /**
Chris@0 173 * Returns TRUE if at least one translatable element is found.
Chris@0 174 *
Chris@0 175 * @param \Drupal\Core\TypedData\TypedDataInterface $element
Chris@0 176 * Configuration schema element.
Chris@0 177 *
Chris@0 178 * @return bool
Chris@0 179 * A boolean indicating if there is at least one translatable element.
Chris@0 180 */
Chris@0 181 protected function findTranslatable(TypedDataInterface $element) {
Chris@0 182 // In case this is a sequence or a mapping check whether any child element
Chris@0 183 // is translatable.
Chris@0 184 if ($element instanceof TraversableTypedDataInterface) {
Chris@0 185 foreach ($element as $child_element) {
Chris@0 186 if ($this->findTranslatable($child_element)) {
Chris@0 187 return TRUE;
Chris@0 188 }
Chris@0 189 }
Chris@0 190 // If none of the child elements are translatable, return FALSE.
Chris@0 191 return FALSE;
Chris@0 192 }
Chris@0 193 else {
Chris@0 194 $definition = $element->getDataDefinition();
Chris@0 195 return isset($definition['translatable']) && $definition['translatable'];
Chris@0 196 }
Chris@0 197 }
Chris@0 198
Chris@0 199 }