Version 1
[yaffs-website] / web / core / modules / language / src / LanguageNegotiator.php
diff --git a/web/core/modules/language/src/LanguageNegotiator.php b/web/core/modules/language/src/LanguageNegotiator.php
new file mode 100644 (file)
index 0000000..db55899
--- /dev/null
@@ -0,0 +1,361 @@
+<?php
+
+namespace Drupal\language;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Site\Settings;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Class responsible for performing language negotiation.
+ */
+class LanguageNegotiator implements LanguageNegotiatorInterface {
+
+  /**
+   * The language negotiation method plugin manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $negotiatorManager;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The settings instance.
+   *
+   * @return \Drupal\Core\Site\Settings
+   */
+  protected $settings;
+
+  /**
+   * The request stack object.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * The current active user.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Local cache for language negotiation method instances.
+   *
+   * @var array
+   */
+  protected $methods;
+
+  /**
+   * An array of language objects keyed by method id.
+   *
+   * @var \Drupal\Core\Language\LanguageInterface[]
+   */
+  protected $negotiatedLanguages = [];
+
+  /**
+   * Constructs a new LanguageNegotiator object.
+   *
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
+   *    The language manager.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
+   * @param \Drupal\Core\Site\Settings $settings
+   *   The settings instance.
+   */
+  public function __construct(ConfigurableLanguageManagerInterface $language_manager, PluginManagerInterface $negotiator_manager, ConfigFactoryInterface $config_factory, Settings $settings, RequestStack $requestStack) {
+    $this->languageManager = $language_manager;
+    $this->negotiatorManager = $negotiator_manager;
+    $this->configFactory = $config_factory;
+    $this->settings = $settings;
+    $this->requestStack = $requestStack;
+  }
+
+  /**
+   * Initializes the injected language manager with the negotiator.
+   *
+   * This should be called right after instantiating the negotiator to make it
+   * available to the language manager without introducing a circular
+   * dependency.
+   */
+  public function initLanguageManager() {
+    $this->languageManager->setNegotiator($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    $this->negotiatedLanguages = [];
+    $this->methods = [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCurrentUser(AccountInterface $current_user) {
+    $this->currentUser = $current_user;
+    $this->reset();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initializeType($type) {
+    $language = NULL;
+
+    if ($this->currentUser) {
+      // Execute the language negotiation methods in the order they were set up
+      // and return the first valid language found.
+      foreach ($this->getEnabledNegotiators($type) as $method_id => $info) {
+        if (!isset($this->negotiatedLanguages[$method_id])) {
+          $this->negotiatedLanguages[$method_id] = $this->negotiateLanguage($type, $method_id);
+        }
+
+        // Since objects are references, we need to return a clone to prevent
+        // the language negotiation method cache from being unintentionally
+        // altered. The same methods might be used with different language types
+        // based on configuration.
+        $language = !empty($this->negotiatedLanguages[$method_id]) ? clone($this->negotiatedLanguages[$method_id]) : NULL;
+
+        if ($language) {
+          $this->getNegotiationMethodInstance($method_id)->persist($language);
+          break;
+        }
+      }
+    }
+
+    if (!$language) {
+      // If no other language was found use the default one.
+      $language = $this->languageManager->getDefaultLanguage();
+      $method_id = static::METHOD_ID;
+    }
+
+    return [$method_id => $language];
+  }
+
+  /**
+   * Gets enabled detection methods for the provided language type.
+   *
+   * @param string $type
+   *   The language type.
+   *
+   * @return array
+   *   An array of enabled detection methods for the provided language type.
+   */
+  protected function getEnabledNegotiators($type) {
+    return $this->configFactory->get('language.types')->get('negotiation.' . $type . '.enabled') ?: [];
+  }
+
+  /**
+   * Performs language negotiation using the specified negotiation method.
+   *
+   * @param string $type
+   *   The language type to be initialized.
+   * @param string $method_id
+   *   The string identifier of the language negotiation method to use to detect
+   *   language.
+   *
+   * @return \Drupal\Core\Language\LanguageInterface|null
+   *   Negotiated language object for given type and method, FALSE otherwise.
+   */
+  protected function negotiateLanguage($type, $method_id) {
+    $langcode = NULL;
+    $method = $this->negotiatorManager->getDefinition($method_id);
+
+    if (!isset($method['types']) || in_array($type, $method['types'])) {
+      $langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->requestStack->getCurrentRequest());
+    }
+
+    $languages = $this->languageManager->getLanguages();
+    return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNegotiationMethods($type = NULL) {
+    $definitions = $this->negotiatorManager->getDefinitions();
+    if (isset($type)) {
+      $enabled_methods = $this->getEnabledNegotiators($type);
+      $definitions = array_intersect_key($definitions, $enabled_methods);
+    }
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNegotiationMethodInstance($method_id) {
+    if (!isset($this->methods[$method_id])) {
+      $instance = $this->negotiatorManager->createInstance($method_id, []);
+      $instance->setLanguageManager($this->languageManager);
+      $instance->setConfig($this->configFactory);
+      $instance->setCurrentUser($this->currentUser);
+      $this->methods[$method_id] = $instance;
+    }
+    return $this->methods[$method_id];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPrimaryNegotiationMethod($type) {
+    $enabled_methods = $this->getEnabledNegotiators($type);
+    return empty($enabled_methods) ? LanguageNegotiatorInterface::METHOD_ID : key($enabled_methods);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isNegotiationMethodEnabled($method_id, $type = NULL) {
+    $enabled = FALSE;
+    $language_types = !empty($type) ? [$type] : $this->languageManager->getLanguageTypes();
+
+    foreach ($language_types as $type) {
+      $enabled_methods = $this->getEnabledNegotiators($type);
+      if (isset($enabled_methods[$method_id])) {
+        $enabled = TRUE;
+        break;
+      }
+    }
+
+    return $enabled;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function saveConfiguration($type, $enabled_methods) {
+    // As configurable language types might have changed, we reset the cache.
+    $this->languageManager->reset();
+    $definitions = $this->getNegotiationMethods();
+    $default_types = $this->languageManager->getLanguageTypes();
+
+    // Order the language negotiation method list by weight.
+    asort($enabled_methods);
+    foreach ($enabled_methods as $method_id => $weight) {
+      if (isset($definitions[$method_id])) {
+        $method = $definitions[$method_id];
+        // If the language negotiation method does not express any preference
+        // about types, make it available for any configurable type.
+        $types = array_flip(!empty($method['types']) ? $method['types'] : $default_types);
+        // Check whether the method is defined and has the right type.
+        if (!isset($types[$type])) {
+          unset($enabled_methods[$method_id]);
+        }
+      }
+      else {
+        unset($enabled_methods[$method_id]);
+      }
+    }
+    $this->configFactory->getEditable('language.types')->set('negotiation.' . $type . '.enabled', $enabled_methods)->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function purgeConfiguration() {
+    // Ensure that we are getting the defined language negotiation information.
+    // An invocation of \Drupal\Core\Extension\ModuleInstaller::install() or
+    // \Drupal\Core\Extension\ModuleInstaller::uninstall() could invalidate the
+    // cached information.
+    $this->negotiatorManager->clearCachedDefinitions();
+    $this->languageManager->reset();
+    foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) {
+      $this->saveConfiguration($type, $this->getEnabledNegotiators($type));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateConfiguration(array $types) {
+    // Ensure that we are getting the defined language negotiation information.
+    // An invocation of \Drupal\Core\Extension\ModuleInstaller::install() or
+    // \Drupal\Core\Extension\ModuleInstaller::uninstall() could invalidate the
+    // cached information.
+    $this->negotiatorManager->clearCachedDefinitions();
+    $this->languageManager->reset();
+
+    $language_types = [];
+    $language_types_info = $this->languageManager->getDefinedLanguageTypesInfo();
+    $method_definitions = $this->getNegotiationMethods();
+
+    foreach ($language_types_info as $type => $info) {
+      $configurable = in_array($type, $types);
+
+      // The default language negotiation settings, if available, are stored in
+      // $info['fixed'].
+      $has_default_settings = !empty($info['fixed']);
+      // Check whether the language type is unlocked. Only the status of
+      // unlocked language types can be toggled between configurable and
+      // non-configurable.
+      if (empty($info['locked'])) {
+        if (!$configurable && !$has_default_settings) {
+          // If we have an unlocked non-configurable language type without
+          // default language negotiation settings, we use the values
+          // negotiated for the interface language which, should always be
+          // available.
+          $method_weights = [LanguageNegotiationUI::METHOD_ID];
+          $method_weights = array_flip($method_weights);
+          $this->saveConfiguration($type, $method_weights);
+        }
+      }
+      else {
+        // The language type is locked. Locked language types with default
+        // settings are always considered non-configurable. In turn if default
+        // settings are missing, the language type is always considered
+        // configurable.
+
+        // If the language type is locked we can just store its default language
+        // negotiation settings if it has some, since it is not configurable.
+        if ($has_default_settings) {
+          $method_weights = [];
+          // Default settings are in $info['fixed'].
+
+          foreach ($info['fixed'] as $weight => $method_id) {
+            if (isset($method_definitions[$method_id])) {
+              $method_weights[$method_id] = $weight;
+            }
+          }
+          $this->saveConfiguration($type, $method_weights);
+        }
+        else {
+          // It was missing default settings, so force it to be configurable.
+          $configurable = TRUE;
+        }
+      }
+
+      // Accumulate information for each language type so it can be saved later.
+      $language_types[$type] = $configurable;
+    }
+
+    // Store the language type configuration.
+    $config = [
+      'configurable' => array_keys(array_filter($language_types)),
+      'all' => array_keys($language_types),
+    ];
+    $this->languageManager->saveLanguageTypesConfiguration($config);
+  }
+
+}