comparison core/modules/language/src/LanguageNegotiator.php @ 0:c75dbcec494b

Initial commit from drush-created site
author Chris Cannam
date Thu, 05 Jul 2018 14:24:15 +0000
parents
children 12f9dff5fda9
comparison
equal deleted inserted replaced
-1:000000000000 0:c75dbcec494b
1 <?php
2
3 namespace Drupal\language;
4
5 use Drupal\Component\Plugin\PluginManagerInterface;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Session\AccountInterface;
8 use Drupal\Core\Site\Settings;
9 use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
10 use Symfony\Component\HttpFoundation\RequestStack;
11
12 /**
13 * Class responsible for performing language negotiation.
14 */
15 class LanguageNegotiator implements LanguageNegotiatorInterface {
16
17 /**
18 * The language negotiation method plugin manager.
19 *
20 * @var \Drupal\Component\Plugin\PluginManagerInterface
21 */
22 protected $negotiatorManager;
23
24 /**
25 * The language manager.
26 *
27 * @var \Drupal\language\ConfigurableLanguageManagerInterface
28 */
29 protected $languageManager;
30
31 /**
32 * The configuration factory.
33 *
34 * @var \Drupal\Core\Config\ConfigFactoryInterface
35 */
36 protected $configFactory;
37
38 /**
39 * The settings instance.
40 *
41 * @var \Drupal\Core\Site\Settings
42 */
43 protected $settings;
44
45 /**
46 * The request stack object.
47 *
48 * @var \Symfony\Component\HttpFoundation\RequestStack
49 */
50 protected $requestStack;
51
52 /**
53 * The current active user.
54 *
55 * @var \Drupal\Core\Session\AccountInterface
56 */
57 protected $currentUser;
58
59 /**
60 * Local cache for language negotiation method instances.
61 *
62 * @var array
63 */
64 protected $methods;
65
66 /**
67 * An array of language objects keyed by method id.
68 *
69 * @var \Drupal\Core\Language\LanguageInterface[]
70 */
71 protected $negotiatedLanguages = [];
72
73 /**
74 * Constructs a new LanguageNegotiator object.
75 *
76 * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
77 * The language manager.
78 * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
79 * The language negotiation methods plugin manager
80 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
81 * The configuration factory.
82 * @param \Drupal\Core\Site\Settings $settings
83 * The settings instance.
84 */
85 public function __construct(ConfigurableLanguageManagerInterface $language_manager, PluginManagerInterface $negotiator_manager, ConfigFactoryInterface $config_factory, Settings $settings, RequestStack $requestStack) {
86 $this->languageManager = $language_manager;
87 $this->negotiatorManager = $negotiator_manager;
88 $this->configFactory = $config_factory;
89 $this->settings = $settings;
90 $this->requestStack = $requestStack;
91 }
92
93 /**
94 * Initializes the injected language manager with the negotiator.
95 *
96 * This should be called right after instantiating the negotiator to make it
97 * available to the language manager without introducing a circular
98 * dependency.
99 */
100 public function initLanguageManager() {
101 $this->languageManager->setNegotiator($this);
102 }
103
104 /**
105 * {@inheritdoc}
106 */
107 public function reset() {
108 $this->negotiatedLanguages = [];
109 $this->methods = [];
110 }
111
112 /**
113 * {@inheritdoc}
114 */
115 public function setCurrentUser(AccountInterface $current_user) {
116 $this->currentUser = $current_user;
117 $this->reset();
118 }
119
120 /**
121 * {@inheritdoc}
122 */
123 public function initializeType($type) {
124 $language = NULL;
125
126 if ($this->currentUser) {
127 // Execute the language negotiation methods in the order they were set up
128 // and return the first valid language found.
129 foreach ($this->getEnabledNegotiators($type) as $method_id => $info) {
130 if (!isset($this->negotiatedLanguages[$method_id])) {
131 $this->negotiatedLanguages[$method_id] = $this->negotiateLanguage($type, $method_id);
132 }
133
134 // Since objects are references, we need to return a clone to prevent
135 // the language negotiation method cache from being unintentionally
136 // altered. The same methods might be used with different language types
137 // based on configuration.
138 $language = !empty($this->negotiatedLanguages[$method_id]) ? clone($this->negotiatedLanguages[$method_id]) : NULL;
139
140 if ($language) {
141 $this->getNegotiationMethodInstance($method_id)->persist($language);
142 break;
143 }
144 }
145 }
146
147 if (!$language) {
148 // If no other language was found use the default one.
149 $language = $this->languageManager->getDefaultLanguage();
150 $method_id = static::METHOD_ID;
151 }
152
153 return [$method_id => $language];
154 }
155
156 /**
157 * Gets enabled detection methods for the provided language type.
158 *
159 * @param string $type
160 * The language type.
161 *
162 * @return array
163 * An array of enabled detection methods for the provided language type.
164 */
165 protected function getEnabledNegotiators($type) {
166 return $this->configFactory->get('language.types')->get('negotiation.' . $type . '.enabled') ?: [];
167 }
168
169 /**
170 * Performs language negotiation using the specified negotiation method.
171 *
172 * @param string $type
173 * The language type to be initialized.
174 * @param string $method_id
175 * The string identifier of the language negotiation method to use to detect
176 * language.
177 *
178 * @return \Drupal\Core\Language\LanguageInterface|null
179 * Negotiated language object for given type and method, FALSE otherwise.
180 */
181 protected function negotiateLanguage($type, $method_id) {
182 $langcode = NULL;
183 $method = $this->negotiatorManager->getDefinition($method_id);
184
185 if (!isset($method['types']) || in_array($type, $method['types'])) {
186 $langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->requestStack->getCurrentRequest());
187 }
188
189 $languages = $this->languageManager->getLanguages();
190 return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
191 }
192
193 /**
194 * {@inheritdoc}
195 */
196 public function getNegotiationMethods($type = NULL) {
197 $definitions = $this->negotiatorManager->getDefinitions();
198 if (isset($type)) {
199 $enabled_methods = $this->getEnabledNegotiators($type);
200 $definitions = array_intersect_key($definitions, $enabled_methods);
201 }
202 return $definitions;
203 }
204
205 /**
206 * {@inheritdoc}
207 */
208 public function getNegotiationMethodInstance($method_id) {
209 if (!isset($this->methods[$method_id])) {
210 $instance = $this->negotiatorManager->createInstance($method_id, []);
211 $instance->setLanguageManager($this->languageManager);
212 $instance->setConfig($this->configFactory);
213 $instance->setCurrentUser($this->currentUser);
214 $this->methods[$method_id] = $instance;
215 }
216 return $this->methods[$method_id];
217 }
218
219 /**
220 * {@inheritdoc}
221 */
222 public function getPrimaryNegotiationMethod($type) {
223 $enabled_methods = $this->getEnabledNegotiators($type);
224 return empty($enabled_methods) ? LanguageNegotiatorInterface::METHOD_ID : key($enabled_methods);
225 }
226
227 /**
228 * {@inheritdoc}
229 */
230 public function isNegotiationMethodEnabled($method_id, $type = NULL) {
231 $enabled = FALSE;
232 $language_types = !empty($type) ? [$type] : $this->languageManager->getLanguageTypes();
233
234 foreach ($language_types as $type) {
235 $enabled_methods = $this->getEnabledNegotiators($type);
236 if (isset($enabled_methods[$method_id])) {
237 $enabled = TRUE;
238 break;
239 }
240 }
241
242 return $enabled;
243 }
244
245 /**
246 * {@inheritdoc}
247 */
248 public function saveConfiguration($type, $enabled_methods) {
249 // As configurable language types might have changed, we reset the cache.
250 $this->languageManager->reset();
251 $definitions = $this->getNegotiationMethods();
252 $default_types = $this->languageManager->getLanguageTypes();
253
254 // Order the language negotiation method list by weight.
255 asort($enabled_methods);
256 foreach ($enabled_methods as $method_id => $weight) {
257 if (isset($definitions[$method_id])) {
258 $method = $definitions[$method_id];
259 // If the language negotiation method does not express any preference
260 // about types, make it available for any configurable type.
261 $types = array_flip(!empty($method['types']) ? $method['types'] : $default_types);
262 // Check whether the method is defined and has the right type.
263 if (!isset($types[$type])) {
264 unset($enabled_methods[$method_id]);
265 }
266 }
267 else {
268 unset($enabled_methods[$method_id]);
269 }
270 }
271 $this->configFactory->getEditable('language.types')->set('negotiation.' . $type . '.enabled', $enabled_methods)->save();
272 }
273
274 /**
275 * {@inheritdoc}
276 */
277 public function purgeConfiguration() {
278 // Ensure that we are getting the defined language negotiation information.
279 // An invocation of \Drupal\Core\Extension\ModuleInstaller::install() or
280 // \Drupal\Core\Extension\ModuleInstaller::uninstall() could invalidate the
281 // cached information.
282 $this->negotiatorManager->clearCachedDefinitions();
283 $this->languageManager->reset();
284 foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) {
285 $this->saveConfiguration($type, $this->getEnabledNegotiators($type));
286 }
287 }
288
289 /**
290 * {@inheritdoc}
291 */
292 public function updateConfiguration(array $types) {
293 // Ensure that we are getting the defined language negotiation information.
294 // An invocation of \Drupal\Core\Extension\ModuleInstaller::install() or
295 // \Drupal\Core\Extension\ModuleInstaller::uninstall() could invalidate the
296 // cached information.
297 $this->negotiatorManager->clearCachedDefinitions();
298 $this->languageManager->reset();
299
300 $language_types = [];
301 $language_types_info = $this->languageManager->getDefinedLanguageTypesInfo();
302 $method_definitions = $this->getNegotiationMethods();
303
304 foreach ($language_types_info as $type => $info) {
305 $configurable = in_array($type, $types);
306
307 // The default language negotiation settings, if available, are stored in
308 // $info['fixed'].
309 $has_default_settings = !empty($info['fixed']);
310 // Check whether the language type is unlocked. Only the status of
311 // unlocked language types can be toggled between configurable and
312 // non-configurable.
313 if (empty($info['locked'])) {
314 if (!$configurable && !$has_default_settings) {
315 // If we have an unlocked non-configurable language type without
316 // default language negotiation settings, we use the values
317 // negotiated for the interface language which, should always be
318 // available.
319 $method_weights = [LanguageNegotiationUI::METHOD_ID];
320 $method_weights = array_flip($method_weights);
321 $this->saveConfiguration($type, $method_weights);
322 }
323 }
324 else {
325 // The language type is locked. Locked language types with default
326 // settings are always considered non-configurable. In turn if default
327 // settings are missing, the language type is always considered
328 // configurable.
329
330 // If the language type is locked we can just store its default language
331 // negotiation settings if it has some, since it is not configurable.
332 if ($has_default_settings) {
333 $method_weights = [];
334 // Default settings are in $info['fixed'].
335
336 foreach ($info['fixed'] as $weight => $method_id) {
337 if (isset($method_definitions[$method_id])) {
338 $method_weights[$method_id] = $weight;
339 }
340 }
341 $this->saveConfiguration($type, $method_weights);
342 }
343 else {
344 // It was missing default settings, so force it to be configurable.
345 $configurable = TRUE;
346 }
347 }
348
349 // Accumulate information for each language type so it can be saved later.
350 $language_types[$type] = $configurable;
351 }
352
353 // Store the language type configuration.
354 $config = [
355 'configurable' => array_keys(array_filter($language_types)),
356 'all' => array_keys($language_types),
357 ];
358 $this->languageManager->saveLanguageTypesConfiguration($config);
359 }
360
361 }