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 }
|