f5f0d55004ad7abef7e333693ee81afe3998ca8b
[yaffs-website] / web / core / modules / config_translation / src / ConfigNamesMapper.php
1 <?php
2
3 namespace Drupal\config_translation;
4
5 use Drupal\config_translation\Event\ConfigMapperPopulateEvent;
6 use Drupal\config_translation\Event\ConfigTranslationEvents;
7 use Drupal\config_translation\Exception\ConfigMapperLanguageException;
8 use Drupal\Core\Config\ConfigFactoryInterface;
9 use Drupal\Core\Config\TypedConfigManagerInterface;
10 use Drupal\Core\Language\LanguageInterface;
11 use Drupal\Core\Language\LanguageManagerInterface;
12 use Drupal\Core\Plugin\PluginBase;
13 use Drupal\Core\Routing\RouteMatchInterface;
14 use Drupal\Core\Routing\RouteProviderInterface;
15 use Drupal\Core\StringTranslation\TranslationInterface;
16 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
17 use Drupal\Core\Url;
18 use Drupal\locale\LocaleConfigManager;
19 use Symfony\Component\DependencyInjection\ContainerInterface;
20 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21 use Symfony\Component\Routing\Route;
22 use Symfony\Component\Routing\RouteCollection;
23
24 /**
25  * Configuration mapper base implementation.
26  */
27 class ConfigNamesMapper extends PluginBase implements ConfigMapperInterface, ContainerFactoryPluginInterface {
28
29   /**
30    * The configuration factory.
31    *
32    * @var \Drupal\Core\Config\ConfigFactoryInterface
33    */
34   protected $configFactory;
35
36   /**
37    * The typed config manager.
38    *
39    * @var \Drupal\Core\Config\TypedConfigManagerInterface
40    */
41   protected $typedConfigManager;
42
43   /**
44    * The typed configuration manager.
45    *
46    * @var \Drupal\locale\LocaleConfigManager
47    */
48   protected $localeConfigManager;
49
50   /**
51    * The mapper plugin discovery service.
52    *
53    * @var \Drupal\config_translation\ConfigMapperManagerInterface
54    */
55   protected $configMapperManager;
56
57   /**
58    * The route provider.
59    *
60    * @var \Drupal\Core\Routing\RouteProviderInterface
61    */
62   protected $routeProvider;
63
64   /**
65    * The base route object that the mapper is attached to.
66    *
67    * @var \Symfony\Component\Routing\Route
68    */
69   protected $baseRoute;
70
71   /**
72    * The available routes.
73    *
74    * @var \Symfony\Component\Routing\RouteCollection
75    */
76   protected $routeCollection;
77
78   /**
79    * The language code of the language this mapper, if any.
80    *
81    * @var string|null
82    */
83   protected $langcode = NULL;
84
85   /**
86    * The language manager.
87    *
88    * @var \Drupal\Core\Language\LanguageManagerInterface
89    */
90   protected $languageManager;
91
92   /**
93    * The event dispatcher.
94    *
95    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
96    */
97   protected $eventDispatcher;
98
99   /**
100    * Constructs a ConfigNamesMapper.
101    *
102    * @param $plugin_id
103    *   The config mapper plugin ID.
104    * @param mixed $plugin_definition
105    *   An array of plugin information with the following keys:
106    *   - title: The title of the mapper, used for generating page titles.
107    *   - base_route_name: The route name of the base route this mapper is
108    *     attached to.
109    *   - names: (optional) An array of configuration names.
110    *   - weight: (optional) The weight of this mapper, used in mapper listings.
111    *     Defaults to 20.
112    *   - list_controller: (optional) Class name for list controller used to
113    *     generate lists of this type of configuration.
114    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
115    *   The configuration factory.
116    * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
117    *   The typed configuration manager.
118    * @param \Drupal\locale\LocaleConfigManager $locale_config_manager
119    *   The locale configuration manager.
120    * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
121    *   The mapper plugin discovery service.
122    * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
123    *   The route provider.
124    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
125    *   The string translation manager.
126    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
127    *   The language manager.
128    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
129    *   (optional) The event dispatcher.
130    *
131    * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
132    *   Throws an exception if the route specified by the 'base_route_name' in
133    *   the plugin definition could not be found by the route provider.
134    */
135   public function __construct($plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, LocaleConfigManager $locale_config_manager, ConfigMapperManagerInterface $config_mapper_manager, RouteProviderInterface $route_provider, TranslationInterface $string_translation, LanguageManagerInterface $language_manager, EventDispatcherInterface $event_dispatcher = NULL) {
136     $this->pluginId = $plugin_id;
137     $this->pluginDefinition = $plugin_definition;
138     $this->routeProvider = $route_provider;
139
140     $this->configFactory = $config_factory;
141     $this->typedConfigManager = $typed_config;
142     $this->localeConfigManager = $locale_config_manager;
143     $this->configMapperManager = $config_mapper_manager;
144
145     $this->stringTranslation = $string_translation;
146     $this->languageManager = $language_manager;
147     $this->eventDispatcher = $event_dispatcher ?: \Drupal::service('event_dispatcher');
148   }
149
150   /**
151    * {@inheritdoc}
152    */
153   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
154     // Note that we ignore the plugin $configuration because mappers have
155     // nothing to configure in themselves.
156     return new static (
157       $plugin_id,
158       $plugin_definition,
159       $container->get('config.factory'),
160       $container->get('config.typed'),
161       $container->get('locale.config_manager'),
162       $container->get('plugin.manager.config_translation.mapper'),
163       $container->get('router.route_provider'),
164       $container->get('string_translation'),
165       $container->get('language_manager'),
166       $container->get('event_dispatcher')
167     );
168   }
169
170   /**
171    * {@inheritdoc}
172    */
173   public function setRouteCollection(RouteCollection $collection) {
174     $this->routeCollection = $collection;
175   }
176
177   /**
178    * {@inheritdoc}
179    */
180   public function getTitle() {
181     // A title from a *.config_translation.yml. Should be translated for
182     // display in the current page language.
183     return $this->t($this->pluginDefinition['title']);
184   }
185
186   /**
187    * {@inheritdoc}
188    */
189   public function getBaseRouteName() {
190     return $this->pluginDefinition['base_route_name'];
191   }
192
193   /**
194    * {@inheritdoc}
195    */
196   public function getBaseRouteParameters() {
197     return [];
198   }
199
200   /**
201    * {@inheritdoc}
202    */
203   public function getBaseRoute() {
204     if ($this->routeCollection) {
205       return $this->routeCollection->get($this->getBaseRouteName());
206     }
207     else {
208       return $this->routeProvider->getRouteByName($this->getBaseRouteName());
209     }
210   }
211
212   /**
213    * Allows to process all config translation routes.
214    *
215    * @param \Symfony\Component\Routing\Route $route
216    *   The route object to process.
217    */
218   protected function processRoute(Route $route) {
219   }
220
221   /**
222    * {@inheritdoc}
223    */
224   public function getBasePath() {
225     return Url::fromRoute($this->getBaseRouteName(), $this->getBaseRouteParameters())->getInternalPath();
226   }
227
228   /**
229    * {@inheritdoc}
230    */
231   public function getOverviewRouteName() {
232     return 'config_translation.item.overview.' . $this->getBaseRouteName();
233   }
234
235   /**
236    * {@inheritdoc}
237    */
238   public function getOverviewRouteParameters() {
239     return $this->getBaseRouteParameters();
240   }
241
242   /**
243    * {@inheritdoc}
244    */
245   public function getOverviewRoute() {
246     $route = new Route(
247       $this->getBaseRoute()->getPath() . '/translate',
248       [
249         '_controller' => '\Drupal\config_translation\Controller\ConfigTranslationController::itemPage',
250         'plugin_id' => $this->getPluginId(),
251       ],
252       ['_config_translation_overview_access' => 'TRUE']
253     );
254     $this->processRoute($route);
255     return $route;
256   }
257
258   /**
259    * {@inheritdoc}
260    */
261   public function getOverviewPath() {
262     return Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters())->getInternalPath();
263   }
264
265   /**
266    * {@inheritdoc}
267    */
268   public function getAddRouteName() {
269     return 'config_translation.item.add.' . $this->getBaseRouteName();
270   }
271
272   /**
273    * {@inheritdoc}
274    */
275   public function getAddRouteParameters() {
276     // If sub-classes provide route parameters in getBaseRouteParameters(), they
277     // probably also want to provide those for the add, edit, and delete forms.
278     $parameters = $this->getBaseRouteParameters();
279     $parameters['langcode'] = $this->langcode;
280     return $parameters;
281   }
282
283   /**
284    * {@inheritdoc}
285    */
286   public function getAddRoute() {
287     $route = new Route(
288       $this->getBaseRoute()->getPath() . '/translate/{langcode}/add',
289       [
290         '_form' => '\Drupal\config_translation\Form\ConfigTranslationAddForm',
291         'plugin_id' => $this->getPluginId(),
292       ],
293       ['_config_translation_form_access' => 'TRUE']
294     );
295     $this->processRoute($route);
296     return $route;
297   }
298
299   /**
300    * {@inheritdoc}
301    */
302   public function getEditRouteName() {
303     return 'config_translation.item.edit.' . $this->getBaseRouteName();
304   }
305
306   /**
307    * {@inheritdoc}
308    */
309   public function getEditRouteParameters() {
310     return $this->getAddRouteParameters();
311   }
312
313   /**
314    * {@inheritdoc}
315    */
316   public function getEditRoute() {
317     $route = new Route(
318       $this->getBaseRoute()->getPath() . '/translate/{langcode}/edit',
319       [
320         '_form' => '\Drupal\config_translation\Form\ConfigTranslationEditForm',
321         'plugin_id' => $this->getPluginId(),
322       ],
323       ['_config_translation_form_access' => 'TRUE']
324     );
325     $this->processRoute($route);
326     return $route;
327   }
328
329   /**
330    * {@inheritdoc}
331    */
332   public function getDeleteRouteName() {
333     return 'config_translation.item.delete.' . $this->getBaseRouteName();
334   }
335
336   /**
337    * {@inheritdoc}
338    */
339   public function getDeleteRouteParameters() {
340     return $this->getAddRouteParameters();
341   }
342
343   /**
344    * {@inheritdoc}
345    */
346   public function getDeleteRoute() {
347     $route = new Route(
348       $this->getBaseRoute()->getPath() . '/translate/{langcode}/delete',
349       [
350         '_form' => '\Drupal\config_translation\Form\ConfigTranslationDeleteForm',
351         'plugin_id' => $this->getPluginId(),
352       ],
353       ['_config_translation_form_access' => 'TRUE']
354     );
355     $this->processRoute($route);
356     return $route;
357   }
358
359   /**
360    * {@inheritdoc}
361    */
362   public function getConfigNames() {
363     return $this->pluginDefinition['names'];
364   }
365
366   /**
367    * {@inheritdoc}
368    */
369   public function addConfigName($name) {
370     $this->pluginDefinition['names'][] = $name;
371   }
372
373   /**
374    * {@inheritdoc}
375    */
376   public function getWeight() {
377     return $this->pluginDefinition['weight'];
378   }
379
380   /**
381    * {@inheritdoc}
382    */
383   public function populateFromRouteMatch(RouteMatchInterface $route_match) {
384     $this->langcode = $route_match->getParameter('langcode');
385
386     $event = new ConfigMapperPopulateEvent($this, $route_match);
387     $this->eventDispatcher->dispatch(ConfigTranslationEvents::POPULATE_MAPPER, $event);
388   }
389
390   /**
391    * {@inheritdoc}
392    */
393   public function getTypeLabel() {
394     return $this->getTitle();
395   }
396
397   /**
398    * {@inheritdoc}
399    */
400   public function getLangcode() {
401     $langcodes = array_map([$this, 'getLangcodeFromConfig'], $this->getConfigNames());
402
403     if (count(array_unique($langcodes)) > 1) {
404       throw new ConfigMapperLanguageException('A config mapper can only contain configuration for a single language.');
405     }
406
407     return reset($langcodes);
408   }
409
410   /**
411    * {@inheritdoc}
412    */
413   public function getLangcodeFromConfig($config_name) {
414     // Default to English if no language code was provided in the file.
415     // Although it is a best practice to include a language code, if the
416     // developer did not think about a multilingual use case, we fall back
417     // on assuming the file is English.
418     return $this->configFactory->get($config_name)->get('langcode') ?: 'en';
419   }
420
421   /**
422    * {@inheritdoc}
423    */
424   public function setLangcode($langcode) {
425     $this->langcode = $langcode;
426     return $this;
427   }
428
429   /**
430    * {@inheritdoc}
431    */
432   public function getConfigData() {
433     $config_data = [];
434     foreach ($this->getConfigNames() as $name) {
435       $config_data[$name] = $this->configFactory->getEditable($name)->get();
436     }
437     return $config_data;
438   }
439
440   /**
441    * {@inheritdoc}
442    */
443   public function hasSchema() {
444     foreach ($this->getConfigNames() as $name) {
445       if (!$this->typedConfigManager->hasConfigSchema($name)) {
446         return FALSE;
447       }
448     }
449     return TRUE;
450   }
451
452   /**
453    * {@inheritdoc}
454    */
455   public function hasTranslatable() {
456     foreach ($this->getConfigNames() as $name) {
457       if ($this->configMapperManager->hasTranslatable($name)) {
458         return TRUE;
459       }
460     }
461     return FALSE;
462   }
463
464   /**
465    * {@inheritdoc}
466    */
467   public function hasTranslation(LanguageInterface $language) {
468     foreach ($this->getConfigNames() as $name) {
469       if ($this->localeConfigManager->hasTranslation($name, $language->getId())) {
470         return TRUE;
471       }
472     }
473     return FALSE;
474   }
475
476   /**
477    * {@inheritdoc}
478    */
479   public function getTypeName() {
480     return $this->t('Settings');
481   }
482
483   /**
484    * {@inheritdoc}
485    */
486   public function getOperations() {
487     return [
488       'translate' => [
489         'title' => $this->t('Translate'),
490         'url' => Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters()),
491       ],
492     ];
493   }
494
495   /**
496    * {@inheritdoc}
497    */
498   public function getContextualLinkGroup() {
499     return NULL;
500   }
501
502 }