Backup of db before drupal security update
[yaffs-website] / web / core / modules / locale / src / LocaleConfigSubscriber.php
1 <?php
2
3 namespace Drupal\locale;
4
5 use Drupal\Core\Config\ConfigCrudEvent;
6 use Drupal\Core\Config\ConfigEvents;
7 use Drupal\Core\Config\ConfigFactoryInterface;
8 use Drupal\Core\Config\StorableConfigBase;
9 use Drupal\language\Config\LanguageConfigOverrideCrudEvent;
10 use Drupal\language\Config\LanguageConfigOverrideEvents;
11 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12
13 /**
14  * Updates strings translation when configuration translations change.
15  *
16  * This reacts to the updates of translated active configuration and
17  * configuration language overrides. When those updates involve configuration
18  * which was available as default configuration, we need to feed back changes
19  * to any item which was originally part of that configuration to the interface
20  * translation storage. Those updated translations are saved as customized, so
21  * further community translation updates will not undo user changes.
22  *
23  * This subscriber does not respond to deleting active configuration or deleting
24  * configuration translations. The locale storage is additive and we cannot be
25  * sure that only a given configuration translation used a source string. So
26  * we should not remove the translations from locale storage in these cases. The
27  * configuration or override would itself be deleted either way.
28  *
29  * By design locale module only deals with sources in English.
30  *
31  * @see \Drupal\locale\LocaleConfigManager
32  */
33 class LocaleConfigSubscriber implements EventSubscriberInterface {
34
35   /**
36    * The configuration factory.
37    *
38    * @var \Drupal\Core\Config\ConfigFactoryInterface
39    */
40   protected $configFactory;
41
42   /**
43    * The typed configuration manager.
44    *
45    * @var \Drupal\locale\LocaleConfigManager
46    */
47   protected $localeConfigManager;
48
49   /**
50    * Constructs a LocaleConfigSubscriber.
51    *
52    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
53    *   The configuration factory.
54    * @param \Drupal\locale\LocaleConfigManager $locale_config_manager
55    *   The typed configuration manager.
56    */
57   public function __construct(ConfigFactoryInterface $config_factory, LocaleConfigManager $locale_config_manager) {
58     $this->configFactory = $config_factory;
59     $this->localeConfigManager = $locale_config_manager;
60   }
61
62   /**
63    * {@inheritdoc}
64    */
65   public static function getSubscribedEvents() {
66     $events[LanguageConfigOverrideEvents::SAVE_OVERRIDE] = 'onOverrideChange';
67     $events[LanguageConfigOverrideEvents::DELETE_OVERRIDE] = 'onOverrideChange';
68     $events[ConfigEvents::SAVE] = 'onConfigSave';
69     return $events;
70   }
71
72   /**
73    * Updates the locale strings when a translated active configuration is saved.
74    *
75    * @param \Drupal\Core\Config\ConfigCrudEvent $event
76    *   The configuration event.
77    */
78   public function onConfigSave(ConfigCrudEvent $event) {
79     // Only attempt to feed back configuration translation changes to locale if
80     // the update itself was not initiated by locale data changes.
81     if (!drupal_installation_attempted() && !$this->localeConfigManager->isUpdatingTranslationsFromLocale()) {
82       $config = $event->getConfig();
83       $langcode = $config->get('langcode') ?: 'en';
84       $this->updateLocaleStorage($config, $langcode);
85     }
86   }
87
88   /**
89    * Updates the locale strings when a configuration override is saved/deleted.
90    *
91    * @param \Drupal\language\Config\LanguageConfigOverrideCrudEvent $event
92    *   The language configuration event.
93    */
94   public function onOverrideChange(LanguageConfigOverrideCrudEvent $event) {
95     // Only attempt to feed back configuration override changes to locale if
96     // the update itself was not initiated by locale data changes.
97     if (!drupal_installation_attempted() && !$this->localeConfigManager->isUpdatingTranslationsFromLocale()) {
98       $translation_config = $event->getLanguageConfigOverride();
99       $langcode = $translation_config->getLangcode();
100       $reference_config = $this->configFactory->getEditable($translation_config->getName())->get();
101       $this->updateLocaleStorage($translation_config, $langcode, $reference_config);
102     }
103   }
104
105   /**
106    * Update locale storage based on configuration translations.
107    *
108    * @param \Drupal\Core\Config\StorableConfigBase $config
109    *   Active configuration or configuration translation override.
110    * @param string $langcode
111    *   The language code of $config.
112    * @param array $reference_config
113    *   (Optional) Reference configuration to check against if $config was an
114    *   override. This allows us to update locale keys for data not in the
115    *   override but still in the active configuration.
116    */
117   protected function updateLocaleStorage(StorableConfigBase $config, $langcode, array $reference_config = []) {
118     $name = $config->getName();
119     if ($this->localeConfigManager->isSupported($name) && locale_is_translatable($langcode)) {
120       $translatables = $this->localeConfigManager->getTranslatableDefaultConfig($name);
121       $this->processTranslatableData($name, $config->get(), $translatables, $langcode, $reference_config);
122     }
123   }
124
125   /**
126    * Process the translatable data array with a given language.
127    *
128    * @param string $name
129    *   The configuration name.
130    * @param array $config
131    *   The active configuration data or override data.
132    * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup[] $translatable
133    *   The translatable array structure.
134    *   @see \Drupal\locale\LocaleConfigManager::getTranslatableData()
135    * @param string $langcode
136    *   The language code to process the array with.
137    * @param array $reference_config
138    *   (Optional) Reference configuration to check against if $config was an
139    *   override. This allows us to update locale keys for data not in the
140    *   override but still in the active configuration.
141    */
142   protected function processTranslatableData($name, array $config, array $translatable, $langcode, array $reference_config = []) {
143     foreach ($translatable as $key => $item) {
144       if (!isset($config[$key])) {
145         if (isset($reference_config[$key])) {
146           $this->resetExistingTranslations($name, $translatable[$key], $reference_config[$key], $langcode);
147         }
148         continue;
149       }
150       if (is_array($item)) {
151         $reference_config = isset($reference_config[$key]) ? $reference_config[$key] : [];
152         $this->processTranslatableData($name, $config[$key], $item, $langcode, $reference_config);
153       }
154       else {
155         $this->saveCustomizedTranslation($name, $item->getUntranslatedString(), $item->getOption('context'), $config[$key], $langcode);
156       }
157     }
158   }
159
160   /**
161    * Reset existing locale translations to their source values.
162    *
163    * Goes through $translatable to reset any existing translations to the source
164    * string, so prior translations would not reappear in the configuration.
165    *
166    * @param string $name
167    *   The configuration name.
168    * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup $translatable
169    *   Either a possibly nested array with TranslatableMarkup objects at the
170    *   leaf items or a TranslatableMarkup object directly.
171    * @param array|string $reference_config
172    *   Either a possibly nested array with strings at the leaf items or a string
173    *   directly. Only those $translatable items that are also present in
174    *   $reference_config will get translations reset.
175    * @param string $langcode
176    *   The language code of the translation being processed.
177    */
178   protected function resetExistingTranslations($name, $translatable, $reference_config, $langcode) {
179     if (is_array($translatable)) {
180       foreach ($translatable as $key => $item) {
181         if (isset($reference_config[$key])) {
182           // Process further if the key still exists in the reference active
183           // configuration and the default translation but not the current
184           // configuration override.
185           $this->resetExistingTranslations($name, $item, $reference_config[$key], $langcode);
186         }
187       }
188     }
189     elseif (!is_array($reference_config)) {
190       $this->saveCustomizedTranslation($name, $translatable->getUntranslatedString(), $translatable->getOption('context'), $reference_config, $langcode);
191     }
192   }
193
194   /**
195    * Saves a translation string and marks it as customized.
196    *
197    * @param string $name
198    *   The configuration name.
199    * @param string $source
200    *   The source string value.
201    * @param string $context
202    *   The source string context.
203    * @param string $new_translation
204    *   The translation string.
205    * @param string $langcode
206    *   The language code of the translation.
207    */
208   protected function saveCustomizedTranslation($name, $source, $context, $new_translation, $langcode) {
209     $locale_translation = $this->localeConfigManager->getStringTranslation($name, $langcode, $source, $context);
210     if (!empty($locale_translation)) {
211       // Save this translation as custom if it was a new translation and not the
212       // same as the source. (The interface prefills translation values with the
213       // source). Or if there was an existing (non-empty) translation and the
214       // user changed it (even if it was changed back to the original value).
215       // Otherwise the translation file would be overwritten with the locale
216       // copy again later.
217       $existing_translation = $locale_translation->getString();
218       if (($locale_translation->isNew() && $source != $new_translation) ||
219         (!$locale_translation->isNew() && ((empty($existing_translation) && $source != $new_translation) || ((!empty($existing_translation) && $new_translation != $existing_translation))))) {
220         $locale_translation
221           ->setString($new_translation)
222           ->setCustomized(TRUE)
223           ->save();
224       }
225     }
226   }
227
228 }