Chris@0: '', Chris@0: 'names' => [], Chris@0: 'weight' => 20, Chris@0: 'class' => '\Drupal\config_translation\ConfigNamesMapper', Chris@0: ]; Chris@0: Chris@0: /** Chris@0: * Constructs a ConfigMapperManager. Chris@0: * Chris@0: * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend Chris@0: * The cache backend. Chris@0: * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager Chris@0: * The language manager. Chris@0: * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler Chris@0: * The module handler. Chris@0: * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager Chris@0: * The typed config manager. Chris@0: * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler Chris@0: * The theme handler. Chris@0: */ Chris@0: public function __construct(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManagerInterface $typed_config_manager, ThemeHandlerInterface $theme_handler) { Chris@0: $this->typedConfigManager = $typed_config_manager; Chris@0: Chris@0: $this->factory = new ContainerFactory($this, '\Drupal\config_translation\ConfigMapperInterface'); Chris@0: Chris@0: // Let others alter definitions with hook_config_translation_info_alter(). Chris@0: $this->moduleHandler = $module_handler; Chris@0: $this->themeHandler = $theme_handler; Chris@0: Chris@0: $this->alterInfo('config_translation_info'); Chris@0: // Config translation only uses an info hook discovery, cache by language. Chris@0: $cache_key = 'config_translation_info_plugins' . ':' . $language_manager->getCurrentLanguage()->getId(); Chris@0: $this->setCacheBackend($cache_backend, $cache_key, ['config_translation_info_plugins']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function getDiscovery() { Chris@0: if (!isset($this->discovery)) { Chris@0: // Look at all themes and modules. Chris@0: // @todo If the list of installed modules and themes is changed, new Chris@0: // definitions are not picked up immediately and obsolete definitions Chris@0: // are not removed, because the list of search directories is only Chris@0: // compiled once in this constructor. The current code only works due to Chris@0: // coincidence: The request that installs (for instance, a new theme) Chris@0: // does not instantiate this plugin manager at the beginning of the Chris@0: // request; when routes are being rebuilt at the end of the request, Chris@0: // this service only happens to get instantiated with the updated list Chris@0: // of installed themes. Chris@0: $directories = []; Chris@0: foreach ($this->moduleHandler->getModuleList() as $name => $module) { Chris@0: $directories[$name] = $module->getPath(); Chris@0: } Chris@0: foreach ($this->themeHandler->listInfo() as $theme) { Chris@0: $directories[$theme->getName()] = $theme->getPath(); Chris@0: } Chris@0: Chris@0: // Check for files named MODULE.config_translation.yml and Chris@0: // THEME.config_translation.yml in module/theme roots. Chris@0: $this->discovery = new YamlDiscovery('config_translation', $directories); Chris@0: $this->discovery = new InfoHookDecorator($this->discovery, 'config_translation_info'); Chris@0: $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery); Chris@0: } Chris@0: return $this->discovery; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getMappers(RouteCollection $collection = NULL) { Chris@0: $mappers = []; Chris@0: foreach ($this->getDefinitions() as $id => $definition) { Chris@0: $mappers[$id] = $this->createInstance($id); Chris@0: if ($collection) { Chris@0: $mappers[$id]->setRouteCollection($collection); Chris@0: } Chris@0: } Chris@0: Chris@0: return $mappers; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function processDefinition(&$definition, $plugin_id) { Chris@0: parent::processDefinition($definition, $plugin_id); Chris@0: Chris@0: if (!isset($definition['base_route_name'])) { Chris@0: throw new InvalidPluginDefinitionException($plugin_id, "The plugin definition of the mapper '$plugin_id' does not contain a base_route_name."); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function buildDataDefinition(array $definition, $value = NULL, $name = NULL, $parent = NULL) { Chris@0: return $this->typedConfigManager->buildDataDefinition($definition, $value, $name, $parent); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function findDefinitions() { Chris@0: $definitions = $this->getDiscovery()->getDefinitions(); Chris@0: foreach ($definitions as $plugin_id => &$definition) { Chris@0: $this->processDefinition($definition, $plugin_id); Chris@0: } Chris@0: if ($this->alterHook) { Chris@0: $this->moduleHandler->alter($this->alterHook, $definitions); Chris@0: } Chris@0: Chris@0: // If this plugin was provided by a module that does not exist, remove the Chris@0: // plugin definition. Chris@0: foreach ($definitions as $plugin_id => $plugin_definition) { Chris@0: 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: unset($definitions[$plugin_id]); Chris@0: } Chris@0: } Chris@0: return $definitions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function hasTranslatable($name) { Chris@0: return $this->findTranslatable($this->typedConfigManager->get($name)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns TRUE if at least one translatable element is found. Chris@0: * Chris@0: * @param \Drupal\Core\TypedData\TypedDataInterface $element Chris@0: * Configuration schema element. Chris@0: * Chris@0: * @return bool Chris@0: * A boolean indicating if there is at least one translatable element. Chris@0: */ Chris@0: protected function findTranslatable(TypedDataInterface $element) { Chris@0: // In case this is a sequence or a mapping check whether any child element Chris@0: // is translatable. Chris@0: if ($element instanceof TraversableTypedDataInterface) { Chris@0: foreach ($element as $child_element) { Chris@0: if ($this->findTranslatable($child_element)) { Chris@0: return TRUE; Chris@0: } Chris@0: } Chris@0: // If none of the child elements are translatable, return FALSE. Chris@0: return FALSE; Chris@0: } Chris@0: else { Chris@0: $definition = $element->getDataDefinition(); Chris@0: return isset($definition['translatable']) && $definition['translatable']; Chris@0: } Chris@0: } Chris@0: Chris@0: }