diff --git a/core/core.services.yml b/core/core.services.yml index 5399bc7..2b894f7 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -191,9 +191,6 @@ services: http_kernel: class: Drupal\Core\HttpKernel arguments: ['@event_dispatcher', '@service_container', '@controller_resolver'] - language_manager: - class: Drupal\Core\Language\LanguageManager - arguments: ['@state'] string_translator.custom_strings: class: Drupal\Core\StringTranslation\Translator\CustomStrings tags: diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 5472321..d85e31d 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2428,65 +2428,7 @@ function language_multilingual() { * weight ascending and name ascending. */ function language_list($flags = Language::STATE_CONFIGURABLE) { - - $languages = &drupal_static(__FUNCTION__); - - // Initialize master language list. - if (!isset($languages)) { - // Initialize local language list cache. - $languages = array(); - - // Fill in master language list based on current configuration. - $default = language_default(); - if (language_multilingual() || module_exists('language')) { - // Use language module configuration if available. - $language_entities = config_get_storage_names_with_prefix('language.entity'); - - // Initialize default property so callers have an easy reference and can - // save the same object without data loss. - foreach ($language_entities as $langcode_config_name) { - $langcode = substr($langcode_config_name, strlen('language.entity.')); - $info = Drupal::config($langcode_config_name)->get(); - $languages[$langcode] = new Language(array( - 'default' => ($info['id'] == $default->id), - 'name' => $info['label'], - 'id' => $info['id'], - 'direction' => $info['direction'], - 'locked' => $info['locked'], - 'weight' => $info['weight'], - )); - } - Language::sort($languages); - } - else { - // No language module, so use the default language only. - $languages = array($default->id => $default); - // Add the special languages, they will be filtered later if needed. - $languages += language_default_locked_languages($default->weight); - } - } - - // Filter the full list of languages based on the value of the $all flag. By - // default we remove the locked languages, but the caller may request for - // those languages to be added as well. - $filtered_languages = array(); - - // Add the site's default language if flagged as allowed value. - if ($flags & Language::STATE_SITE_DEFAULT) { - $default = isset($default) ? $default : language_default(); - // Rename the default language. - $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name)); - $filtered_languages['site_default'] = $default; - } - - foreach ($languages as $langcode => $language) { - if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) { - continue; - } - $filtered_languages[$langcode] = $language; - } - - return $filtered_languages; + return Drupal::languageManager()->getLanguageList($flags); } /** @@ -2500,24 +2442,7 @@ function language_list($flags = Language::STATE_CONFIGURABLE) { * An array of language objects. */ function language_default_locked_languages($weight = 0) { - $locked_language = array( - 'default' => FALSE, - 'locked' => TRUE, - 'enabled' => TRUE, - ); - - $languages = array(); - $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array( - 'id' => Language::LANGCODE_NOT_SPECIFIED, - 'name' => t('Not specified'), - 'weight' => ++$weight, - ) + $locked_language); - $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array( - 'id' => Language::LANGCODE_NOT_APPLICABLE, - 'name' => t('Not applicable'), - 'weight' => ++$weight, - ) + $locked_language); - return $languages; + return Drupal::languageManager()->getDefaultLockedLanguages($weight); } /** @@ -2578,15 +2503,8 @@ function language_is_locked($langcode) { * A language object. */ function language_default() { - $info = variable_get('language_default', array( - 'id' => 'en', - 'name' => 'English', - 'direction' => 0, - 'weight' => 0, - 'locked' => 0, - )); - $info['default'] = TRUE; - return new Language($info); + $default_info = variable_get('language_default', Language::$defaultValues); + return new Language($default_info + array('default' => TRUE)); } /** diff --git a/core/includes/common.inc b/core/includes/common.inc index eb7362c..ae19dbf 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4783,6 +4783,14 @@ function drupal_flush_all_caches() { } /** + * Rebuild the container to store updated language negotiation settings. + */ +function drupal_rebuild_language_negotiation_settings() { + Drupal::service('plugin.manager.language_negotiation_method')->clearCachedDefinitions(); + PhpStorageFactory::get('service_container')->deleteAll(); +} + +/** * Changes the dummy query string added to all CSS and JavaScript files. * * Changing the dummy query string appended to CSS and JavaScript files forces diff --git a/core/includes/file.inc b/core/includes/file.inc index 9319755..20576f3 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -7,6 +7,7 @@ use Drupal\Core\StreamWrapper\LocalStream; use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage; +use Drupal\Core\StreamWrapper\PublicStream; /** * Stream wrapper bit flags that are the basis for composite types. @@ -385,7 +386,7 @@ function file_stream_wrapper_get_instance_by_uri($uri) { * @param $scheme * If the stream was "public://target", "public" would be the scheme. * - * @return + * @return Drupal\Core\StreamWrapper\StreamWrapperInterface * Returns a new stream wrapper object appropriate for the given $scheme. * For example, for the public scheme a stream wrapper object * (Drupal\Core\StreamWrapper\PublicStream). @@ -1614,7 +1615,7 @@ function file_directory_temp() { if (empty($temporary_directory)) { // If no directory has been found default to 'files/tmp'. - $temporary_directory = variable_get('file_public_path', conf_path() . '/files') . '/tmp'; + $temporary_directory = PublicStream::basePath() . '/tmp'; // Windows accepts paths with either slash (/) or backslash (\), but will // not accept a path which contains both a slash and a backslash. Since diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 8e4b66e..a5dcfca 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -9,6 +9,7 @@ use Drupal\Core\Database\Install\TaskException; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser; use Drupal\Core\StringTranslation\Translator\FileTranslation; use Drupal\Core\DependencyInjection\ContainerBuilder; @@ -370,6 +371,14 @@ function install_begin_request(&$install_state) { else { // @todo Move into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder. $container = new ContainerBuilder(); + // Register Core's namespaces just for the installer. + $namespaces = array( + 'Drupal\Core' => DRUPAL_ROOT . '/core/lib', + 'Drupal\Component' => DRUPAL_ROOT . '/core/lib', + ); + $container->register('container.namespaces', 'ArrayObject') + ->addArgument('%container.namespaces%'); + $container->setParameter('container.namespaces', $namespaces); $container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher'); @@ -386,9 +395,12 @@ function install_begin_request(&$install_state) { ->addArgument(new Reference('config.context')); // Register the 'language_manager' service. - $container - ->register('language_manager', 'Drupal\Core\Language\LanguageManager') - ->addArgument(NULL); + $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager') + ->addArgument(new Reference('container.namespaces')) + ->addArgument(new Reference('cache.cache')); + $container->register('language_manager', 'Drupal\Core\Language\InstallLanguageManager') + ->addArgument(array()) + ->addArgument(new Reference('plugin.manager.language_negotiation_method')); // Register the translation services. install_register_translation_service($container); @@ -1574,7 +1586,8 @@ function install_select_language_form($form, &$form_state, $files = array()) { } } - $browser_langcode = language_from_browser($browser_options); + $method = new LanguageNegotiationBrowser(array('browser' => array('mappings' => array()))); + $browser_langcode = $method->negotiateLanguage($browser_options, Request::createFromGlobals()); $form['langcode'] = array( '#type' => 'select', '#title' => t('Choose language'), diff --git a/core/includes/language.inc b/core/includes/language.inc index 44e91c1..7ca90bc 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -113,42 +113,6 @@ */ /** - * Chooses a language based on language negotiation method settings. - * - * @param $type - * The language type key to find the language for. - * - * @param $request - * The HttpReqeust object representing the current request. - * - * @return - * The negotiated language object. - */ -function language_types_initialize($type, $request = NULL) { - // Execute the language negotiation methods in the order they were set up and - // return the first valid language found. - $negotiation = variable_get("language_negotiation_$type", array()); - - foreach ($negotiation as $method_id => $method) { - // Skip negotiation methods not appropriate for this type. - if (isset($method['types']) && !in_array($type, $method['types'])) { - continue; - } - $language = language_negotiation_method_invoke($method_id, $method, $request); - if ($language) { - // Remember the method ID used to detect the language. - $language->method_id = $method_id; - return $language; - } - } - - // If no other language was found use the default one. - $language = language_default(); - $language->method_id = LANGUAGE_NEGOTIATION_SELECTED; - return $language; -} - -/** * Returns information about all defined language types. * * @return @@ -436,53 +400,6 @@ function language_negotiation_info() { return $negotiation_info; } -/** - * Invokes a language negotiation method and caches the results. - * - * @param $method_id - * The language negotiation method's identifier. - * @param $method - * (optional) An associative array of information about the method to be - * invoked (see hook_language_negotiation_info() for details). If not passed - * in, it will be loaded through language_negotiation_info(). - * - * @param $request - * (optional) The HttpRequest object representing the current request. - * - * @return - * A language object representing the language chosen by the method. - */ -function language_negotiation_method_invoke($method_id, $method = NULL, $request = NULL) { - $results = &drupal_static(__FUNCTION__); - - if (!isset($results[$method_id])) { - global $user; - - $languages = language_list(); - - if (!isset($method)) { - $negotiation_info = language_negotiation_info(); - $method = $negotiation_info[$method_id]; - } - - if (isset($method['file'])) { - require_once DRUPAL_ROOT . '/' . $method['file']; - } - // If the language negotiation method has no cache preference or this is - // satisfied we can execute the callback. - $cache = !isset($method['cache']) || $user->isAuthenticated() || $method['cache'] == variable_get('cache', 0); - $callback = isset($method['callbacks']['negotiation']) ? $method['callbacks']['negotiation'] : FALSE; - $langcode = $cache && function_exists($callback) ? $callback($languages, $request) : FALSE; - $results[$method_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE; - } - - // Since objects are resources, 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. - return !empty($results[$method_id]) ? clone($results[$method_id]) : $results[$method_id]; -} - /** * Identifies language from configuration. * @@ -502,39 +419,6 @@ function language_from_selected($languages) { } /** - * Splits the given path into prefix and actual path. - * - * Parse the given path and return the language object identified by the prefix - * and the actual path. - * - * @param $path - * The path to split. - * @param $languages - * An array of valid languages. - * - * @return - * An array composed of: - * - A language object corresponding to the identified prefix on success, - * FALSE otherwise. - * - The path without the prefix on success, the given path otherwise. - */ -function language_url_split_prefix($path, $languages) { - $args = empty($path) ? array() : explode('/', $path); - $prefix = array_shift($args); - - // Search prefix within enabled languages. - $prefixes = language_negotiation_url_prefixes(); - foreach ($languages as $language) { - if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) { - // Rebuild $path with the language removed. - return array($language, implode('/', $args)); - } - } - - return array(FALSE, $path); -} - -/** * Returns the possible fallback languages ordered by language weight. * * @param diff --git a/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php b/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php index c82cfcd..15a7eb2 100644 --- a/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php +++ b/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php @@ -6,6 +6,7 @@ */ namespace Drupal\Component\PhpStorage; +use Drupal\Core\StreamWrapper\PublicStream; /** * Creates a php storage object @@ -49,8 +50,7 @@ static function get($name) { $configuration['bin'] = $name; } if (!isset($configuration['directory'])) { - $path = isset($conf['file_public_path']) ? $conf['file_public_path'] : conf_path() . '/files'; - $configuration['directory'] = DRUPAL_ROOT . "/$path/php"; + $configuration['directory'] = DRUPAL_ROOT . '/' . PublicStream::basePath() . '/php'; } return new $class($configuration); } diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index a6cb957..7fbd029 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -13,6 +13,7 @@ use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterLanguageNegotiationPass; use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass; @@ -50,6 +51,7 @@ public function register(ContainerBuilder $container) { $container->addScope(new Scope('request')); $this->registerTwig($container); $this->registerModuleHandler($container); + $this->registerLanguage($container); $container->addCompilerPass(new RegisterRouteFiltersPass()); // Add a compiler pass for registering event subscribers. @@ -133,4 +135,24 @@ public static function registerTwig(ContainerBuilder $container) { ->addMethodCall('addExtension', array(new Definition('Twig_Extension_Debug'))); } + /** + * Registers language related services. + */ + protected function registerLanguage(ContainerBuilder $container) { + // @todo Refactor this when all language negotiation settings are converted + // a single configuration object. + $storage = $container->get('kernel.config.storage'); + $config = $storage->read('language.negotiation') ?: array(); + $config += array('browser' => array('mappings' => $storage->read('language.mappings'))); + + // Register the 'language_manager' service. + $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager') + ->addArgument(new Reference('container.namespaces')) + ->addArgument(new Reference('cache.cache')); + $container->register('language_manager', 'Drupal\Core\Language\LanguageManager') + ->addArgument($config) + ->addArgument(new Reference('plugin.manager.language_negotiation_method')) + ->addArgument(new Reference('state')); + } + } diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index af1fe52..bf61aad 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -524,6 +524,8 @@ protected function buildContainer() { $container->register('class_loader', 'Symfony\Component\ClassLoader\ClassLoader')->setSynthetic(TRUE); $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE); $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE); + // Register the kernel-level config storage. + $container->set('kernel.config.storage', $this->configStorage); $yaml_loader = new YamlFileLoader($container); foreach ($this->serviceYamls as $filename) { $yaml_loader->load($filename); diff --git a/core/lib/Drupal/Core/Language/InstallLanguageManager.php b/core/lib/Drupal/Core/Language/InstallLanguageManager.php new file mode 100644 index 0000000..b70ee3f --- /dev/null +++ b/core/lib/Drupal/Core/Language/InstallLanguageManager.php @@ -0,0 +1,40 @@ +config = $config; + $this->negotiatorManager = $negotiator_manager; + } + + /** + * {@inheritdoc} + */ + public function isMultilingual() { + // No state service in install time. + return FALSE; + } + +} diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php index 7181b32..ceea20f 100644 --- a/core/lib/Drupal/Core/Language/Language.php +++ b/core/lib/Drupal/Core/Language/Language.php @@ -17,6 +17,23 @@ * @see language_default() */ class Language { + + /** + * The values to use to instantiate the default language. + * + * @todo Remove once converted to config. + * + * @var array + */ + public static $defaultValues = array( + 'id' => 'en', + 'name' => 'English', + 'direction' => 0, + 'weight' => 0, + 'locked' => 0, + 'default' => TRUE, + ); + // Properties within the Language are set up as the default language. public $name = ''; public $id = ''; diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php index 7bc716d..a4273f3 100644 --- a/core/lib/Drupal/Core/Language/LanguageManager.php +++ b/core/lib/Drupal/Core/Language/LanguageManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Language; +use Drupal\Component\Plugin\PluginManagerInterface; use Symfony\Component\HttpFoundation\Request; use Drupal\Core\KeyValueStore\KeyValueStoreInterface; @@ -16,18 +17,16 @@ class LanguageManager { /** - * A request object. - * - * @var \Symfony\Component\HttpFoundation\Request + * The language negotiation method id for the language manager. */ - protected $request; + const METHOD_ID = 'language-default'; /** * The Key/Value Store to use for state. * * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface */ - protected $state = NULL; + protected $state; /** * An array of language objects keyed by language type. @@ -37,6 +36,34 @@ class LanguageManager { protected $languages; /** + * An array of all the available languages keyed by language code. + * + * @var array + */ + protected $languageList; + + /** + * An array of configuration. + * + * @var array + */ + protected $config; + + /** + * The language negotiation method plugin manager. + * + * @var \Drupal\Component\Plugin\PluginManagerInterface + */ + protected $negotiatorManager; + + /** + * A request object. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; + + /** * Whether or not the language manager has been initialized. * * @var bool @@ -46,20 +73,23 @@ class LanguageManager { /** * Whether already in the process of language initialization. * - * @todo This is only needed due to the circular dependency between language - * and config. See http://drupal.org/node/1862202 for the plan to fix this. - * * @var bool */ protected $initializing = FALSE; /** - * Constructs an LanguageManager object. + * Constructs a new LanguageManager object. * - * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state - * The state keyvalue store. + * @param array $config + * An array of configuration. + * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager + * The language negotiation methods plugin manager + * @param \Drupal\Core\KeyValueStoreInterface $state + * The state key value store. */ - public function __construct(KeyValueStoreInterface $state = NULL) { + public function __construct(array $config, PluginManagerInterface $negotiator_manager, KeyValueStoreInterface $state) { + $this->config = $config; + $this->negotiatorManager = $negotiator_manager; $this->state = $state; } @@ -71,9 +101,11 @@ public function init() { return; } if ($this->isMultilingual()) { - foreach ($this->getLanguageTypes() as $type) { - $this->getLanguage($type); - } + // This is still assumed by various functions to be loaded. + include_once DRUPAL_ROOT . '/core/includes/language.inc'; + } + foreach ($this->getLanguageTypes() as $type) { + $this->getLanguage($type); } $this->initialized = TRUE; } @@ -105,31 +137,109 @@ public function getLanguage($type = Language::TYPE_INTERFACE) { return $this->languages[$type]; } + // Ensure we have a valid value for this language type. + $this->languages[$type] = $this->getLanguageDefault(); + if ($this->isMultilingual() && $this->request) { if (!$this->initializing) { $this->initializing = TRUE; - // @todo Objectify the language system so that we don't have to load an - // include file and call out to procedural code. See - // http://drupal.org/node/1862202 - include_once DRUPAL_ROOT . '/core/includes/language.inc'; - $this->languages[$type] = language_types_initialize($type, $this->request); + $this->languages[$type] = $this->initializeType($type); $this->initializing = FALSE; } - else { - // Config has called getLanguage() during initialization of a language - // type. Simply return the default language without setting it on the - // $this->languages property. See the TODO in the docblock for the - // $initializing property. - return $this->getLanguageDefault(); + // If the current interface language needs to be retrieved during + // initialization we return the system language. This way t() calls + // happening during initialization will return the original strings which + // can be translated by calling t() again afterwards. This can happen for + // instance while parsing negotiation method definitions. + elseif ($type == Language::TYPE_INTERFACE) { + return new Language(array('id' => Language::LANGCODE_SYSTEM)); } } - else { - $this->languages[$type] = $this->getLanguageDefault(); - } + return $this->languages[$type]; } /** + * Initializes the specified language type. + * + * @param string $type + * The language type to be initialized. + */ + public function initializeType($type) { + // Execute the language negotiation methods in the order they were set up + // and return the first valid language found. + foreach ($this->getNegotiationForType($type) as $method_id) { + if (!isset($this->negotiated[$method_id])) { + $this->negotiated[$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->negotiated[$method_id]) ? clone($this->negotiated[$method_id]) : FALSE; + + if ($language) { + // Remember the method ID used to detect the language. + $language->method_id = $method_id; + return $language; + } + } + + // If no other language was found use the default one. + $language = $this->getLanguageDefault(); + $language->method_id = LanguageManager::METHOD_ID; + return $language; + } + + /** + * Returns the negotiation settings for the specified language type. + * + * @param string $type + * The type of the language to retireve the negotiation settings for. + * + * @returns array + * An array of language negotiation method identifiers ordered by method + * weight. + */ + protected function getNegotiationForType($type) { + // @todo convert to CMI https://drupal.org/node/1827038 + return array_keys(variable_get("language_negotiation_$type", array())); + } + + /** + * 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\Language|FALSE + * Negotiated language object for given type and method, FALSE otherwise. + */ + protected function negotiateLanguage($type, $method_id) { + global $user; + $langcode = FALSE; + $languages = $this->getLanguageList(); + $method = $this->negotiatorManager->getDefinition($method_id); + + if (!isset($method['types']) || in_array($type, $method['types'])) { + // If the language negotiation method has no cache preference or this is + // satisfied we can execute the callback. + $cache = !isset($method['cache']) || $user->isAuthenticated() || $method['cache'] == \Drupal::config('system.performance')->get('cache.page.use_internal'); + if ($cache) { + $negotiator = $this->negotiatorManager->createInstance($method_id, $this->config); + $negotiator->setLanguageManager($this); + $langcode = $negotiator->negotiateLanguage($languages, $this->request); + } + } + + return isset($languages[$langcode]) ? $languages[$langcode] : FALSE; + } + + /** * Resets the given language type or all types if none specified. * * @param string|null $type @@ -141,6 +251,7 @@ public function reset($type = NULL) { if (!isset($type)) { $this->languages = array(); $this->initialized = FALSE; + $this->languageList = NULL; } elseif (isset($this->languages[$type])) { unset($this->languages[$type]); @@ -154,20 +265,17 @@ public function reset($type = NULL) { * TRUE if more than one language is enabled, FALSE otherwise. */ public function isMultilingual() { - if (!isset($this->state)) { - // No state service in install time. - return FALSE; - } return ($this->state->get('language_count') ?: 1) > 1; } /** * Returns an array of the available language types. * - * @return array() + * @return array * An array of all language types. */ protected function getLanguageTypes() { + // @todo convert to CMI https://drupal.org/node/1827038 return language_types_get_all(); } @@ -177,16 +285,90 @@ protected function getLanguageTypes() { * @return \Drupal\Core\Language\Language * A language object. */ - protected function getLanguageDefault() { - $default_info = variable_get('language_default', array( - 'id' => 'en', - 'name' => 'English', - 'direction' => 0, - 'weight' => 0, - 'locked' => 0, - )); - $default_info['default'] = TRUE; - return new Language($default_info); + public function getLanguageDefault() { + // @todo convert to CMI https://drupal.org/node/1827038 + $default_info = variable_get('language_default', Language::$defaultValues); + return new Language($default_info + array('default' => TRUE)); + } + + /** + * Returns a list of languages set up on the site. + * + * @param $flags + * (optional) Specifies the state of the languages that have to be returned. + * It can be: Language::STATE_CONFIGURABLE, Language::STATE_LOCKED, Language::STATE_ALL. + * + * @return array + * An associative array of languages, keyed by the language code, ordered by + * weight ascending and name ascending. + */ + public function getLanguageList($flags = Language::STATE_CONFIGURABLE) { + // Initialize master language list. + if (!isset($this->languageList)) { + // Initialize local language list cache. + $this->languageList = array(); + // Fill in master language list based on current configuration. + $default = $this->getLanguageDefault(); + // No language module, so use the default language only. + $this->languageList = array($default->id => $default); + // Add the special languages, they will be filtered later if needed. + $this->languageList += $this->getDefaultLockedLanguages($default->weight); + } + + // Filter the full list of languages based on the value of the $all flag. By + // default we remove the locked languages, but the caller may request for + // those languages to be added as well. + $filtered_languages = array(); + + // Add the site's default language if flagged as allowed value. + if ($flags & Language::STATE_SITE_DEFAULT) { + $default = isset($default) ? $default : $this->getLanguageDefault(); + // Rename the default language. + $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name)); + $filtered_languages['site_default'] = $default; + } + + foreach ($this->languageList as $id => $language) { + if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) { + continue; + } + $filtered_languages[$id] = $language; + } + + return $filtered_languages; + } + + /** + * Returns a list of the default locked languages. + * + * @param int $weight + * (optional) An integer value that is used as the start value for the + * weights of the locked languages. + * + * @return array + * An array of language objects. + */ + public function getDefaultLockedLanguages($weight = 0) { + $languages = array(); + + $locked_language = array( + 'default' => FALSE, + 'locked' => TRUE, + 'enabled' => TRUE, + ); + $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array( + 'id' => Language::LANGCODE_NOT_SPECIFIED, + 'name' => t('Not specified'), + 'weight' => ++$weight, + ) + $locked_language); + + $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array( + 'id' => Language::LANGCODE_NOT_APPLICABLE, + 'name' => t('Not applicable'), + 'weight' => ++$weight, + ) + $locked_language); + + return $languages; } /** diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php b/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php new file mode 100644 index 0000000..9b1c666 --- /dev/null +++ b/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php @@ -0,0 +1,37 @@ +config = $config; + } + + /** + * Implements \Drupal\Core\Language\LanguageNegotiationInterface::setLanguageManager(). + */ + public function setLanguageManager(LanguageManager $languageManager) { + $this->languageManager = $languageManager; + } + +} diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php new file mode 100644 index 0000000..0410d26 --- /dev/null +++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php @@ -0,0 +1,39 @@ +cacheBackend = $cache_backend; + $this->cacheKeyPrefix = 'language_negotiation_plugins'; + $this->cacheKey = 'language_negotiation_plugins'; + } + + /** + * {@inheritdoc} + */ + public function clearCachedDefinitions() { + $this->cacheBackend->delete($this->cacheKey); + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php new file mode 100644 index 0000000..8e8ce12 --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php @@ -0,0 +1,150 @@ +server->get('HTTP_ACCEPT_LANGUAGE')) { + return FALSE; + } + + // The Accept-Language header contains information about the language + // preferences configured in the user's browser / operating system. RFC 2616 + // (section 14.4) defines the Accept-Language header as follows: + // Accept-Language = "Accept-Language" ":" + // 1#( language-range [ ";" "q" "=" qvalue ] ) + // language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" + $browser_langcodes = array(); + if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($request->server->get('HTTP_ACCEPT_LANGUAGE')), $matches, PREG_SET_ORDER)) { + // Load custom mappings to support browsers that are sending non standard + // language codes. + $mappings = $this->config['browser']['mappings']; + + foreach ($matches as $match) { + if ($mappings) { + $langcode = strtolower($match[1]); + foreach ($mappings as $browser_langcode => $drupal_langcode) { + if ($langcode == $browser_langcode) { + $match[1] = $drupal_langcode; + } + } + } + // We can safely use strtolower() here, tags are ASCII. + // RFC2616 mandates that the decimal part is no more than three digits, + // so we multiply the qvalue by 1000 to avoid floating point + // comparisons. + $langcode = strtolower($match[1]); + $qvalue = isset($match[2]) ? (float) $match[2] : 1; + $browser_langcodes[$langcode] = (int) ($qvalue * 1000); + } + } + + // We should take pristine values from the HTTP headers, but Internet + // Explorer from version 7 sends only specific language tags (eg. fr-CA) + // without the corresponding generic tag (fr) unless explicitly configured. + // In that case, we assume that the lowest value of the specific tags is the + // value of the generic language to be as close to the HTTP 1.1 spec as + // possible. + // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and + // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx + asort($browser_langcodes); + foreach ($browser_langcodes as $langcode => $qvalue) { + // For Chinese languages the generic tag is either zh-hans or zh-hant, so + // we need to handle this separately, we can not split $langcode on the + // first occurence of '-' otherwise we get a non-existing language zh. + // All other languages use a langcode without a '-', so we can safely + // split on the first occurence of it. + $generic_tag = ''; + if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) { + $generic_tag = substr($langcode, 0, 7); + } + else { + $generic_tag = strtok($langcode, '-'); + } + if (!empty($generic_tag) && !isset($browser_langcodes[$generic_tag])) { + // Add the generic langcode, but make sure it has a lower qvalue as the + // more specific one, so the more specific one gets selected if it's + // defined by both the browser and Drupal. + $browser_langcodes[$generic_tag] = $qvalue - 0.1; + } + } + + // Find the enabled language with the greatest qvalue, following the rules + // of RFC 2616 (section 14.4). If several languages have the same qvalue, + // prefer the one with the greatest weight. + $best_match_langcode = FALSE; + $max_qvalue = 0; + foreach ($languages as $langcode => $language) { + // Language tags are case insensitive (RFC2616, sec 3.10). + $langcode = strtolower($langcode); + + // If nothing matches below, the default qvalue is the one of the wildcard + // language, if set, or is 0 (which will never match). + $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0; + + // Find the longest possible prefix of the browser-supplied language ('the + // language-range') that matches this site language ('the language tag'). + $prefix = $langcode; + do { + if (isset($browser_langcodes[$prefix])) { + $qvalue = $browser_langcodes[$prefix]; + break; + } + } + while ($prefix = substr($prefix, 0, strrpos($prefix, '-'))); + + // Find the best match. + if ($qvalue > $max_qvalue) { + $best_match_langcode = $language->id; + $max_qvalue = $qvalue; + } + } + + return $best_match_langcode; + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php new file mode 100644 index 0000000..8dce46d --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php @@ -0,0 +1,42 @@ +config['selected_langcode']) ? $this->config['selected_langcode'] : FALSE; + return $langcode != 'site_default' ? $langcode : $this->languageManager->getLanguageDefault()->id; + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php new file mode 100644 index 0000000..07fbb6a --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php @@ -0,0 +1,58 @@ +config['session']['parameter']; + + // Request parameter: we need to update the session parameter only if we + // have an authenticated user. + $langcode = $request->query->get($param); + if ($langcode) { + global $user; + $languages = $this->languageManager->getLanguageList(); + if ($user->isAuthenticated() && isset($languages[$langcode])) { + $_SESSION[$param] = $langcode; + } + } + + // Session parameter. + if (isset($_SESSION[$param])) { + return $_SESSION[$param]; + } + + return FALSE; + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php new file mode 100644 index 0000000..7cba9bf --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php @@ -0,0 +1,41 @@ +languageManager->getLanguage()->id; + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php new file mode 100644 index 0000000..02147bc --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php @@ -0,0 +1,100 @@ +config['url']['source']) { + case LanguageNegotiationUrl::CONFIG_PATH_PREFIX: + $request_path = urldecode(trim($request->getPathInfo(), '/')); + $path_args = explode('/', $request_path); + $prefix = array_shift($path_args); + + // Search prefix within enabled languages. + $prefixes = $this->config['url']['prefixes']; + $negotiated_language = FALSE; + foreach ($languages as $language) { + if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) { + $negotiated_language = $language; + break; + } + } + + if ($negotiated_language !== FALSE) { + $langcode = $negotiated_language->id; + } + break; + + case LanguageNegotiationUrl::CONFIG_DOMAIN: + // Get only the host, not the port. + $http_host = $request->server->get('HTTP_HOST'); + if (strpos($http_host, ':') !== FALSE) { + $http_host_tmp = explode(':', $http_host); + $http_host = current($http_host_tmp); + } + $domains = $this->config['url']['domains']; + foreach ($languages as $language) { + // Skip the check if the language doesn't have a domain. + if (!empty($domains[$language->id])) { + // Ensure that there is exactly one protocol in the URL when + // checking the hostname. + $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->id]); + $host = parse_url($host, PHP_URL_HOST); + if ($http_host == $host) { + $langcode = $language->id; + break; + } + } + } + break; + } + + return $langcode; + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php new file mode 100644 index 0000000..637fa4e --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php @@ -0,0 +1,73 @@ +languageManager->getLanguageDefault(); + $prefix = ($this->config['url']['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX); + + // If the default language is not configured to convey language information, + // a missing URL language information indicates that URL language should be + // the default one, otherwise we fall back to an already detected language. + $domains = $this->config['url']['domains']; + $prefixes = $this->config['url']['prefixes']; + if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) { + return $default->id; + } + else { + return $this->languageManager->getLanguage()->id; + } + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php new file mode 100644 index 0000000..85d2da2 --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php @@ -0,0 +1,47 @@ +attributes->get('_account'); + + if ($user->isAuthenticated() && isset($languages[$user->getPreferredLangcode()])) { + return $user->getPreferredLangcode(); + } + + // No language preference from the user. + return FALSE; + } + +} diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php new file mode 100644 index 0000000..6a9ff9f --- /dev/null +++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php @@ -0,0 +1,51 @@ +attributes->get('_account'); + + // @todo Avoid calling _current_path() and path_is_admin() directly. + $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path(); + if ($user->isAuthenticated() && isset($languages[$user->getPreferredAdminLangcode()]) && path_is_admin($request_path)) { + return $user->getPreferredAdminLangcode(); + } + + // No language preference from the user or not on an admin path. + return FALSE; + } + +} diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 207b77a..19803bf 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -16,20 +16,35 @@ class PublicStream extends LocalStream { /** - * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath() + * {@inheritdoc} */ public function getDirectoryPath() { - return variable_get('file_public_path', conf_path() . '/files'); + return static::basePath(); } /** - * Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl(). - * - * @return string - * Returns the HTML URI of a public file. + * {@inheritdoc} */ - function getExternalUrl() { + public function getExternalUrl() { $path = str_replace('\\', '/', $this->getTarget()); return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path); } + + /** + * Returns the base path for public:// + * + * @return string + * the base path for public:// typically sites/default/files. + */ + static public function basePath() { + $base_path = settings()->get('file_public_path', conf_path() . '/files'); + if ($test_prefix = drupal_valid_test_ua()) { + // Append the testing suffix unless already given. + // @see Drupal\simpletest\WebTestBase::setUp() + if (strpos($base_path, '/simpletest/' . substr($test_prefix, 10)) === FALSE) { + return $base_path . '/simpletest/' . substr($test_prefix, 10); + } + } + return $base_path; + } } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php index a078821..3d1f8f5 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php @@ -74,10 +74,9 @@ public function testLanguageBlockVisibility() { $this->drupalpost('admin/config/regional/settings', $edit, t('Save configuration')); // Reset the static cache of the language list. - drupal_static_reset('language_list'); - + $this->container->get('language_manager')->reset(); // Check that a page has a block. - $this->drupalget('', array('language' => language_load('en'))); + $this->drupalGet('', array('language' => language_load('en'))); $this->assertText('Powered by Drupal', 'The body of the custom block appears on the page.'); // Check that a page doesn't has a block for the current language anymore. diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php index 84a080e..3ac4a24 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php @@ -81,7 +81,6 @@ function setUp() { * Test that comment language is properly set. */ function testCommentLanguage() { - drupal_static_reset('language_list'); // Create two nodes, one for english and one for french, and comment each // node using both english and french as content language by changing URL diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php index 548eb8e..bea3e82 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php @@ -7,6 +7,7 @@ namespace Drupal\filter\Tests; +use Drupal\Core\StreamWrapper\PublicStream; use Drupal\simpletest\WebTestBase; /** @@ -75,7 +76,7 @@ function setUp() { function testImageSource() { global $base_url; - $public_files_path = variable_get('file_public_path', conf_path() . '/files'); + $public_files_path = PublicStream::basePath(); $http_base_url = preg_replace('/^https?/', 'http', $base_url); $https_base_url = preg_replace('/^https?/', 'https', $base_url); diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 03b8c95..fa03dcd 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -227,25 +227,6 @@ function image_permission() { } /** - * Implements hook_form_FORM_ID_alter(). - */ -function image_form_system_file_system_settings_alter(&$form, &$form_state) { - $form['#submit'][] = 'image_system_file_system_settings_submit'; -} - -/** - * Form submission handler for system_file_system_settings(). - * - * Adds a menu rebuild after the public file path has been changed, so that the - * menu router item depending on that file path will be regenerated. - */ -function image_system_file_system_settings_submit($form, &$form_state) { - if ($form['file_public_path']['#default_value'] !== $form_state['values']['file_public_path']) { - Drupal::state()->set('menu_rebuild_needed', TRUE); - } -} - -/** * Implements hook_file_download(). * * Control the access to files underneath the styles directory. diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc index 979419e..67f9e5d 100644 --- a/core/modules/language/language.admin.inc +++ b/core/modules/language/language.admin.inc @@ -177,6 +177,7 @@ function language_admin_add_form_submit($form, &$form_state) { } // Save the language and inform the user that it happened. $language = language_save($language); + drupal_rebuild_language_negotiation_settings(); drupal_set_message(t('The language %language has been created and can now be used.', array('%language' => $language->name))); // Tell the user they have the option to add a language switcher block @@ -526,6 +527,9 @@ function language_negotiation_configure_form_submit($form, &$form_state) { Drupal::service('plugin.manager.block')->clearCachedDefinitions(); } + // Rebuild the container to update the submitted settings. + drupal_rebuild_language_negotiation_settings(); + $form_state['redirect'] = 'admin/config/regional/language/detection'; drupal_set_message(t('Language negotiation configuration saved.')); } @@ -708,6 +712,8 @@ function language_negotiation_configure_browser_form_submit($form, &$form_state) if (!empty($mappings)) { language_set_browser_drupal_langcode_mappings($mappings); } + // Rebuild the container to update the submitted settings. + drupal_rebuild_language_negotiation_settings(); $form_state['redirect'] = 'admin/config/regional/language/detection'; } diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 4d2688c..152d8fc 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -522,8 +522,8 @@ function language_save($language) { variable_set('language_default', (array) $language); } - // Kill the static cache in language_list(). - drupal_static_reset('language_list'); + // Reset the language information. + Drupal::languageManager()->reset(); // Update language count based on unlocked language count. language_update_count(); @@ -583,6 +583,8 @@ function language_delete($langcode) { // Update weight of locked system languages. language_update_locked_weights(); + Drupal::languageManager()->reset(); + $t_args = array('%language' => $language->name, '%langcode' => $language->id); watchdog('language', 'The %language (%langcode) language has been removed.', $t_args); return TRUE; @@ -879,4 +881,5 @@ function language_system_regional_settings_form_submit($form, &$form_state) { $language = $languages[$form_state['values']['site_default_language']]; $language->default = TRUE; language_save($language); + drupal_rebuild_language_negotiation_settings(); } diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc index 8834812..116dd94 100644 --- a/core/modules/language/language.negotiation.inc +++ b/core/modules/language/language.negotiation.inc @@ -50,332 +50,6 @@ const LANGUAGE_NEGOTIATION_URL_DOMAIN = 'domain'; /** - * Identifies the language from the current interface language. - * - * @return - * The current interface language code. - */ -function language_from_interface() { - return language(Language::TYPE_INTERFACE)->id; -} - -/** - * Identify language from the Accept-language HTTP header we got. - * - * The algorithm works as follows: - * - map browser language codes to Drupal language codes. - * - order all browser language codes by qvalue from high to low. - * - add generic browser language codes if they aren't already specified - * but with a slightly lower qvalue. - * - find the most specific Drupal language code with the highest qvalue. - * - if 2 or more languages are having the same qvalue, respect the order of - * them inside the $languages array. - * - * We perform browser accept-language parsing only if page cache is disabled, - * otherwise we would cache a user-specific preference. - * - * @param $languages - * An array of language objects for enabled languages ordered by weight. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_browser($languages) { - if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - return FALSE; - } - - // The Accept-Language header contains information about the language - // preferences configured in the user's browser / operating system. RFC 2616 - // (section 14.4) defines the Accept-Language header as follows: - // Accept-Language = "Accept-Language" ":" - // 1#( language-range [ ";" "q" "=" qvalue ] ) - // language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) - // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" - $browser_langcodes = array(); - if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) { - // Load custom mappings to support browsers that are sending non standard - // language codes. - $mappings = language_get_browser_drupal_langcode_mappings(); - foreach ($matches as $match) { - if ($mappings) { - $langcode = strtolower($match[1]); - foreach ($mappings as $browser_langcode => $drupal_langcode) { - if ($langcode == $browser_langcode) { - $match[1] = $drupal_langcode; - } - } - } - // We can safely use strtolower() here, tags are ASCII. - // RFC2616 mandates that the decimal part is no more than three digits, - // so we multiply the qvalue by 1000 to avoid floating point comparisons. - $langcode = strtolower($match[1]); - $qvalue = isset($match[2]) ? (float) $match[2] : 1; - $browser_langcodes[$langcode] = (int) ($qvalue * 1000); - } - } - - // We should take pristine values from the HTTP headers, but Internet Explorer - // from version 7 sends only specific language tags (eg. fr-CA) without the - // corresponding generic tag (fr) unless explicitly configured. In that case, - // we assume that the lowest value of the specific tags is the value of the - // generic language to be as close to the HTTP 1.1 spec as possible. - // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and - // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx - asort($browser_langcodes); - foreach ($browser_langcodes as $langcode => $qvalue) { - // For Chinese languages the generic tag is either zh-hans or zh-hant, so we - // need to handle this separately, we can not split $langcode on the - // first occurence of '-' otherwise we get a non-existing language zh. - // All other languages use a langcode without a '-', so we can safely split - // on the first occurence of it. - $generic_tag = ''; - if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) { - $generic_tag = substr($langcode, 0, 7); - } - else { - $generic_tag = strtok($langcode, '-'); - } - if (!empty($generic_tag) && !isset($browser_langcodes[$generic_tag])) { - // Add the generic langcode, but make sure it has a lower qvalue as the - // more specific one, so the more specific one gets selected if it's - // defined by both the browser and Drupal. - $browser_langcodes[$generic_tag] = $qvalue - 0.1; - } - } - - // Find the enabled language with the greatest qvalue, following the rules of - // RFC 2616 (section 14.4). If several languages have the same qvalue, prefer - // the one with the greatest weight. - $best_match_langcode = FALSE; - $max_qvalue = 0; - foreach ($languages as $langcode => $language) { - // Language tags are case insensitive (RFC2616, sec 3.10). - $langcode = strtolower($langcode); - - // If nothing matches below, the default qvalue is the one of the wildcard - // language, if set, or is 0 (which will never match). - $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0; - - // Find the longest possible prefix of the browser-supplied language ('the - // language-range') that matches this site language ('the language tag'). - $prefix = $langcode; - do { - if (isset($browser_langcodes[$prefix])) { - $qvalue = $browser_langcodes[$prefix]; - break; - } - } - while ($prefix = substr($prefix, 0, strrpos($prefix, '-'))); - - // Find the best match. - if ($qvalue > $max_qvalue) { - $best_match_langcode = $language->id; - $max_qvalue = $qvalue; - } - } - - return $best_match_langcode; -} - -/** - * Identify language from the user preferences. - * - * @param $languages - * An array of valid language objects. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_user($languages) { - // User preference (only for authenticated users). - global $user; - - if ($user->id()) { - $langcode = $user->getPreferredLangcode(); - $default_langcode = language_default()->id; - if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode])) { - return $langcode; - } - } - - // No language preference from the user. - return FALSE; -} - -/** - * Identifies admin language from the user preferences. - * - * @param $languages - * An array of valid language objects. - * - * @param \Symfony\Component\HttpFoundation\Request|null $request - * (optional) The HttpRequest object representing the current request. - * Defaults to NULL. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_user_admin(array $languages, Request $request = NULL) { - // User preference (only for authenticated users). - global $user; - - if ($user->id()) { - $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path(); - $langcode = $user->getPreferredAdminLangcode(); - $default_langcode = language_default()->id; - if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode]) && path_is_admin($request_path)) { - return $langcode; - } - } - - // No language preference from the user or not on an admin path. - return FALSE; -} - -/** - * Identify language from a request/session parameter. - * - * @param $languages - * An array of valid language objects. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_session($languages) { - $param = Drupal::config('language.negotiation')->get('session.parameter'); - - // Request parameter: we need to update the session parameter only if we have - // an authenticated user. - if (isset($_GET[$param]) && isset($languages[$langcode = $_GET[$param]])) { - global $user; - if ($user->id()) { - $_SESSION[$param] = $langcode; - } - return $langcode; - } - - // Session parameter. - if (isset($_SESSION[$param])) { - return $_SESSION[$param]; - } - - return FALSE; -} - -/** - * Identify language via URL prefix or domain. - * - * @param $languages - * An array of valid language objects. - * - * @param \Symfony\Component\HttpFoundation\Request|null $request - * (optional) The HttpRequest object representing the current request. - * Defaults to NULL. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_url($languages, Request $request = NULL) { - $language_url = FALSE; - - if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL) || !$request) { - return $language_url; - } - - switch (Drupal::config('language.negotiation')->get('url.source')) { - case LANGUAGE_NEGOTIATION_URL_PREFIX: - - $request_path = urldecode(trim($request->getPathInfo(), '/')); - list($language, $path) = language_url_split_prefix($request_path, $languages); - - if ($language !== FALSE) { - $language_url = $language->id; - } - break; - - case LANGUAGE_NEGOTIATION_URL_DOMAIN: - // Get only the host, not the port. - $http_host= $_SERVER['HTTP_HOST']; - if (strpos($http_host, ':') !== FALSE) { - $http_host_tmp = explode(':', $http_host); - $http_host = current($http_host_tmp); - } - $domains = language_negotiation_url_domains(); - foreach ($languages as $language) { - // Skip the check if the language doesn't have a domain. - if (!empty($domains[$language->id])) { - // Ensure that there is exactly one protocol in the URL when checking - // the hostname. - $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->id]); - $host = parse_url($host, PHP_URL_HOST); - if ($http_host == $host) { - $language_url = $language->id; - break; - } - } - } - break; - } - - return $language_url; -} - -/** - * Determines the language to be assigned to URLs when none is detected. - * - * The language negotiation process has a fallback chain that ends with the - * default language negotiation method. Each built-in language type has a - * separate initialization: - * - Interface language, which is the only configurable one, always gets a valid - * value. If no request-specific language is detected, the default language - * will be used. - * - Content language merely inherits the interface language by default. - * - URL language is detected from the requested URL and will be used to rewrite - * URLs appearing in the page being rendered. If no language can be detected, - * there are two possibilities: - * - If the default language has no configured path prefix or domain, then the - * default language is used. This guarantees that (missing) URL prefixes are - * preserved when navigating through the site. - * - If the default language has a configured path prefix or domain, a - * requested URL having an empty prefix or domain is an anomaly that must be - * fixed. This is done by introducing a prefix or domain in the rendered - * page matching the detected interface language. - * - * @param $languages - * (optional) An array of valid language objects. This is passed by - * language_negotiation_method_invoke() to every language method callback, - * but it is not actually needed here. Defaults to NULL. - * - * @param $request - * (optional) The HttpRequest object representing the current request. - * - * @param $language_type - * (optional) The language type to fall back to. Defaults to the interface - * language. - * - * @return - * A valid language code. - */ -function language_url_fallback($language = NULL, $request = NULL, $language_type = Language::TYPE_INTERFACE) { - $default = language_default(); - $prefix = (Drupal::config('language.negotiation')->get('url.source') == LANGUAGE_NEGOTIATION_URL_PREFIX); - - // If the default language is not configured to convey language information, - // a missing URL language information indicates that URL language should be - // the default one, otherwise we fall back to an already detected language. - $domains = language_negotiation_url_domains(); - $prefixes = language_negotiation_url_prefixes(); - if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) { - return $default->id; - } - else { - $langcode = language($language_type)->id; - return $langcode; - } -} - -/** * Return links for the URL language switcher block. * * Translation links may be provided by other modules. diff --git a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php index aa3cc2b..c9593f0 100644 --- a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php +++ b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php @@ -42,51 +42,43 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa */ protected $languageManager; - /** - * An array of enabled languages. - * - * @var array - */ - protected $languages; - /** * Constructs a PathProcessorLanguage object. * * @param \Drupal\Core\Config\ConfigFactory $config * A config factory object for retrieving configuration settings. - * - * @param array $languages - * An array of languages, keyed by language code, representing the languages - * currently enabled on the site. + * @param LanguageManager $language_manager + * A LanguageManager object. */ - public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager, array $languages = array()) { + public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager) { $this->config = $config; $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE); $this->languageManager = $language_manager; - if (empty($languages)) { - $languages = language_list(); - } - $this->languages = $languages; } /** * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound(). */ public function processInbound($path, Request $request) { - if (!empty($path)) { - $args = explode('/', $path); - $prefix = array_shift($args); + if (empty($path)) { + return $path; + } - // Search prefix within enabled languages. - $prefixes = $this->config->get('language.negotiation')->get('url.prefixes'); - foreach ($this->languages as $language) { - if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) { - // Rebuild $path with the language removed. - return implode('/', $args); - } - } + $languages = $this->languageManager->getLanguageList(); + $prefixes = $this->config->get('language.negotiation')->get('url.prefixes'); + $parts = explode('/', $path); + $prefix = array_shift($parts); + + // Search prefix within enabled languages. + foreach ($languages as $language) { + if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) { + // Rebuild $path with the language removed. + $path = implode('/', $parts); + break; + } } + return $path; } @@ -103,7 +95,7 @@ public function processOutbound($path, &$options = array(), Request $request = N $url_scheme = $request->getScheme(); $port = $request->getPort(); } - $languages = array_flip(array_keys($this->languages)); + $languages = array_flip(array_keys($this->languageManager->getLanguageList())); // Language can be passed as an option, or we go for current URL language. if (!isset($options['language'])) { $language_url = $this->languageManager->getLanguage(Language::TYPE_URL); diff --git a/core/modules/language/lib/Drupal/language/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php index 8abeb8b..bc32308 100644 --- a/core/modules/language/lib/Drupal/language/LanguageListController.php +++ b/core/modules/language/lib/Drupal/language/LanguageListController.php @@ -140,8 +140,7 @@ public function submitForm(array &$form, array &$form_state) { } } - // Kill the static cache in language_list(). - drupal_static_reset('language_list'); + drupal_rebuild_language_negotiation_settings(); // Update weight of locked system languages. language_update_locked_weights(); diff --git a/core/modules/language/lib/Drupal/language/LanguageManager.php b/core/modules/language/lib/Drupal/language/LanguageManager.php new file mode 100644 index 0000000..6c744a2 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageManager.php @@ -0,0 +1,77 @@ +configStorage = $config_storage; + } + + /** + * {@inheritdoc} + */ + public function getLanguageList($flags = Language::STATE_CONFIGURABLE) { + if (!isset($this->languageList)) { + // Fill in master language list based on current configuration. + $default = $this->getLanguageDefault(); + + // Use language module configuration if available. + $language_ids = $this->configStorage->listAll('language.entity'); + foreach (\Drupal::service('config.factory')->loadMultiple($language_ids) as $language_config) { + $langcode = $language_config->get('id'); + $info = $language_config->get(); + $info['default'] = ($langcode == $default->id); + $this->languageList[$langcode] = new Language($info); + } + } + + return parent::getLanguageList($flags); + } + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php new file mode 100644 index 0000000..40e7670 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php @@ -0,0 +1,34 @@ +getDefinition('language_manager'); + $definition->setClass('Drupal\language\LanguageManager') + ->addArgument(new Reference('config.storage')); + } + +} diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php index 07ea3d5..4f0b2c0 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php @@ -9,6 +9,8 @@ use Drupal\simpletest\WebTestBase; use Drupal\Core\Language\Language; +use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser; +use Symfony\Component\HttpFoundation\Request; /** * Test browser language detection. @@ -153,9 +155,11 @@ function testLanguageFromBrowser() { 'zh-cht' => 'zh-hant', ); + $mappings = $this->container->get('config.factory')->get('language.mappings')->get(); + $language_neg = new LanguageNegotiationBrowser(array('browser' => array('mappings' => $mappings))); foreach ($test_cases as $accept_language => $expected_result) { - $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $accept_language; - $result = language_from_browser($languages); + $request = Request::create('', 'GET', array(), array(), array(), array('HTTP_ACCEPT_LANGUAGE' => $accept_language)); + $result = $language_neg->negotiateLanguage($languages, $request); $this->assertIdentical($result, $expected_result, format_string("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none'))); } } diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php index fcec194..3970899 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php @@ -85,6 +85,7 @@ public function testDefaultLangcode() { $langcode = language_get_default_langcode('custom_type', 'custom_bundle'); $language_interface = language(Language::TYPE_INTERFACE); $this->assertEqual($langcode, $language_interface->id); + drupal_rebuild_language_negotiation_settings(); // Site's default. $old_default = language_default(); diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php index f970bdd..b9f93da 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php @@ -156,7 +156,7 @@ function testLanguageConfigurationWeight() { */ protected function checkConfigurableLanguageWeight($state = 'by default') { // Reset language list. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $max_configurable_language_weight = $this->getHighestConfigurableLanguageWeight(); $replacements = array('@event' => $state); foreach (language_list(Language::STATE_LOCKED) as $locked_language) { diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php index 4870f57..783648c 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php @@ -116,15 +116,15 @@ function testLanguageList() { // Verify that language is no longer found. $this->drupalGet('admin/config/regional/language/delete/' . $langcode); $this->assertResponse(404, 'Language no longer found.'); - // Make sure the "language_count" state has been updated correctly. - drupal_static_reset('language_list'); + // Make sure the "language_count" variable has been updated correctly. + $this->container->get('language_manager')->reset(); $languages = language_list(); $language_count = $this->container->get('state')->get('language_count') ?: 1; $this->assertEqual($language_count, count($languages), 'Language count is correct.'); // Delete French. $this->drupalPost('admin/config/regional/language/delete/fr', array(), t('Delete')); // Get the count of languages. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $languages = language_list(); // We need raw here because %language and %langcode will add HTML. $t_args = array('%language' => 'French', '%langcode' => 'fr'); diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php index e039341..17e08ae 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php @@ -106,7 +106,7 @@ function testUILanguageNegotiation() { // into database when seen by t(). Without doing this, our target string // is for some reason not found when doing translate search. This might // be some bug. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $languages = language_list(); variable_set('language_default', (array) $languages['vi']); // First visit this page to make sure our target string is searchable. diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php index 5266fd1..0cfed4c 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php @@ -45,9 +45,6 @@ function setUp() { // Enable URL language detection and selection. $edit = array('language_interface[enabled][language-url]' => 1); $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings')); - - // Reset static caching. - drupal_static_reset('language_list'); } /** @@ -119,7 +116,7 @@ function testDomainNameNegotiationPort() { ->save(); // Reset static caching. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); // In case index.php is part of the URLs, we need to adapt the asserted // URLs as well. diff --git a/core/modules/language/tests/language_elements_test/language_elements_test.info.yml b/core/modules/language/tests/language_elements_test/language_elements_test.info.yml index 254c5f6..9fc44c1 100644 --- a/core/modules/language/tests/language_elements_test/language_elements_test.info.yml +++ b/core/modules/language/tests/language_elements_test/language_elements_test.info.yml @@ -4,4 +4,4 @@ description: 'Support module for the language form elements tests.' core: 8.x package: Testing version: VERSION -hidden: true +#hidden: true diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module index c3386cf..87af948 100644 --- a/core/modules/language/tests/language_test/language_test.module +++ b/core/modules/language/tests/language_test/language_test.module @@ -102,10 +102,3 @@ function language_test_store_language_negotiation() { } Drupal::state()->set('language_test.language_negotiation_last', $last); } - -/** - * Provides a test language negotiation method. - */ -function language_test_language_negotiation_method($languages) { - return 'it'; -} diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php deleted file mode 100644 index 991755e..0000000 --- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php +++ /dev/null @@ -1,28 +0,0 @@ -get('language_test.domain')) { - $_SERVER['HTTP_HOST'] = $test_domain; - } - return parent::init(); - } - -} diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php deleted file mode 100644 index c8b1633..0000000 --- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php +++ /dev/null @@ -1,36 +0,0 @@ -getDefinition('language_manager'); - $definition->setClass('Drupal\language_test\LanguageTestManager'); - } - -} - diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php new file mode 100644 index 0000000..0f60de1 --- /dev/null +++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php @@ -0,0 +1,41 @@ +drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); $edit = array( 'site_default_language' => 'ar', @@ -86,7 +85,6 @@ function testContentTypeLanguageConfiguration() { 'direction' => '0', ); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); - drupal_static_reset('language_list'); // Set the content type to use multilingual support. $this->drupalGet("admin/structure/types/manage/{$type2->type}"); @@ -160,7 +158,6 @@ function testContentTypeDirLang() { $edit = array(); $edit['predefined_langcode'] = 'es'; $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); // Set the content type to use multilingual support. $this->drupalGet("admin/structure/types/manage/{$type->type}"); @@ -224,7 +221,6 @@ function testNodeAdminLanguageFilter() { // Enable multiple languages. $this->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language')); $this->drupalPost('admin/config/regional/language/add', array('predefined_langcode' => 'zh-hant'), t('Add language')); - drupal_static_reset('language_list'); // Create two nodes: English and Chinese. $node_en = $this->drupalCreateNode(array('langcode' => 'en')); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php index e391022..ffe961f 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php @@ -230,7 +230,7 @@ function testJavaScriptTranslation() { 'direction' => '0', ); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); // Build the JavaScript translation file. diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php index 46d082e..df69c5e 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php @@ -7,6 +7,7 @@ namespace Drupal\locale\Tests; +use Drupal\Core\StreamWrapper\PublicStream; use Drupal\simpletest\WebTestBase; use Drupal\Component\Utility\String; @@ -81,7 +82,7 @@ protected function setTranslationsDirectory($path) { protected function addLanguage($langcode) { $edit = array('predefined_langcode' => $langcode); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $this->assertTrue(language_load($langcode), String::format('Language %langcode added.', array('%langcode' => $langcode))); } @@ -172,7 +173,7 @@ protected function setTranslationFiles() { \Drupal::state()->set('locale.test_projects_alter', TRUE); // Setup the environment. - $public_path = variable_get('file_public_path', conf_path() . '/files'); + $public_path = PublicStream::basePath(); $this->setTranslationsDirectory($public_path . '/local'); $config->set('translation.default_filename', '%project-%version.%language._po')->save(); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 02731a4..4aa5197 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -19,7 +19,7 @@ * @ingroup forms */ function locale_translate_import_form($form, &$form_state) { - drupal_static_reset('language_list'); + Drupal::languageManager()->reset(); $languages = language_list(); // Initialize a language list to the ones available, including English if we diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc index 83777b6..73b869f 100644 --- a/core/modules/locale/locale.pages.inc +++ b/core/modules/locale/locale.pages.inc @@ -99,7 +99,7 @@ function locale_translate_filters() { $filters = array(); // Get all languages, except English. - drupal_static_reset('language_list'); + Drupal::languageManager()->reset(); $languages = language_list(); $language_options = array(); foreach ($languages as $langcode => $language) { @@ -255,7 +255,7 @@ function locale_translate_edit_form($form, &$form_state) { $filter_values = locale_translate_filter_values(); $langcode = $filter_values['langcode']; - drupal_static_reset('language_list'); + Drupal::languageManager()->reset(); $languages = language_list(); $langname = isset($langcode) ? $languages[$langcode]->name : "- None -"; diff --git a/core/modules/locale/tests/modules/locale_test/locale_test.module b/core/modules/locale/tests/modules/locale_test/locale_test.module index 1ba430c..973a27b 100644 --- a/core/modules/locale/tests/modules/locale_test/locale_test.module +++ b/core/modules/locale/tests/modules/locale_test/locale_test.module @@ -4,6 +4,7 @@ * @file * Simulate a custom module with a local po file. */ +use Drupal\Core\StreamWrapper\PublicStream; /** * Implements hook_system_info_alter(). @@ -42,7 +43,7 @@ function locale_test_locale_translation_projects_alter(&$projects) { // Instead of the default ftp.drupal.org we use the file system of the test // instance to simulate a remote file location. $url = url(NULL, array('absolute' => TRUE)); - $remote_url = $url . variable_get('file_public_path', conf_path() . '/files') . '/remote/'; + $remote_url = $url . PublicStream::basePath() . '/remote/'; // Completely replace the project data with a set of test projects. $base_url = url(); diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php index 1724a4a..8553e39 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php @@ -99,7 +99,10 @@ function testAliasTranslation() { $this->drupalGet('fr/' . $edit['path[alias]']); $this->assertText($french_node->label(), 'Alias for French translation works.'); - // Confirm that the alias is returned by url(). + // Confirm that the alias is returned by url(). Languages are cached on + // many levels, and we need to clear those caches. + $this->container->get('language_manager')->reset(); + $languages = language_list(); $url = $this->container->get('url_generator')->generateFromPath('node/' . $french_node->id(), array('language' => $languages[$french_node->language()->id])); $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.'); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 04e98f1..0805bc1 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -17,6 +17,7 @@ use Drupal\Core\Config\StorageInterface; use Drupal\Core\DrupalKernel; use Drupal\Core\Language\Language; +use Drupal\Core\StreamWrapper\PublicStream; use ReflectionMethod; use ReflectionObject; @@ -708,7 +709,7 @@ public function run(array $methods = array()) { if ($simpletest_config->get('verbose')) { // Initialize verbose debugging. $this->verbose = TRUE; - $this->verboseDirectory = variable_get('file_public_path', conf_path() . '/files') . '/simpletest/verbose'; + $this->verboseDirectory = PublicStream::basePath() . '/simpletest/verbose'; $this->verboseDirectoryUrl = file_create_url($this->verboseDirectory); if (file_prepare_directory($this->verboseDirectory, FILE_CREATE_DIRECTORY) && !file_exists($this->verboseDirectory . '/.htaccess')) { file_put_contents($this->verboseDirectory . '/.htaccess', "\nExpiresActive Off\n\n"); @@ -888,7 +889,9 @@ protected function prepareEnvironment() { $this->originalTheme = isset($GLOBALS['theme']) ? $GLOBALS['theme'] : NULL; // Save further contextual information. - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); + // Use the original files directory to avoid nesting it within an existing + // simpletest directory if a test is executed within a test. + $this->originalFileDirectory = settings()->get('file_public_path', conf_path() . '/files'); $this->originalProfile = drupal_get_profile(); $this->originalUser = isset($user) ? clone $user : NULL; diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php index c990234..dd725ed 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php @@ -56,7 +56,7 @@ protected function setUp() { $conf = array(); drupal_static_reset(); - $conf['file_public_path'] = $this->public_files_directory; + $this->settingsSet('file_public_path', $this->public_files_directory); // Change the database prefix. // All static variables need to be reset before the database prefix is diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 4aec435..9e1ecb8 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -16,6 +16,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\UserSession; +use Drupal\Core\StreamWrapper\PublicStream; use PDO; use stdClass; use DOMDocument; @@ -416,7 +417,7 @@ protected function drupalGetTestFiles($type, $size = NULL) { $original = drupal_get_path('module', 'simpletest') . '/files'; $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/'); foreach ($files as $file) { - file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files')); + file_unmanaged_copy($file->uri, PublicStream::basePath()); } $this->generatedTestFiles = TRUE; @@ -772,7 +773,7 @@ protected function setUp() { NestedArray::setValue($GLOBALS['conf'], array_merge(array($config_base), explode('.', $name)), $value); } } - $GLOBALS['conf']['file_public_path'] = $this->public_files_directory; + $this->settingsSet('file_public_path', $this->public_files_directory); // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; $this->settingsSet('cache', array('default' => 'cache.backend.memory')); @@ -820,7 +821,6 @@ protected function setUp() { } $config->save(); } - variable_set('file_public_path', $this->public_files_directory); // Use the test mail class instead of the default mail handler class. \Drupal::config('system.mail')->set('interface.default', 'Drupal\Core\Mail\VariableLog')->save(); diff --git a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php index 3a4aa18..83cb9e7 100644 --- a/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php +++ b/core/modules/system/lib/Drupal/system/Form/FileSystemForm.php @@ -7,6 +7,7 @@ namespace Drupal\system\Form; +use Drupal\Core\StreamWrapper\PublicStream; use Drupal\system\SystemConfigFormBase; /** @@ -27,12 +28,11 @@ public function getFormID() { public function buildForm(array $form, array &$form_state) { $config = $this->configFactory->get('system.file'); $form['file_public_path'] = array( - '#type' => 'textfield', + '#type' => 'item', '#title' => t('Public file system path'), - '#default_value' => variable_get('file_public_path', conf_path() . '/files'), - '#maxlength' => 255, - '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web.'), - '#after_build' => array('system_check_directory'), + '#default_value' => PublicStream::basePath(), + '#markup' => PublicStream::basePath(), + '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web. This must be changed in settings.php'), ); $form['file_private_path'] = array( @@ -78,7 +78,6 @@ public function submitForm(array &$form, array &$form_state) { $config = $this->configFactory->get('system.file') ->set('path.private', $form_state['values']['file_private_path']) ->set('path.temporary', $form_state['values']['file_temporary_path']); - variable_set('file_public_path', $form_state['values']['file_public_path']); if (isset($form_state['values']['file_default_scheme'])) { $config->set('default_scheme', $form_state['values']['file_default_scheme']); diff --git a/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php b/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php index 216d0d1..c77b391 100644 --- a/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php +++ b/core/modules/system/lib/Drupal/system/Form/ThemeSettingsForm.php @@ -7,6 +7,7 @@ namespace Drupal\system\Form; +use Drupal\Core\StreamWrapper\PublicStream; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Drupal\Core\Cache\Cache; @@ -240,7 +241,7 @@ public function buildForm(array $form, array &$form_state, $theme_name = '') { // Prepare local file path for description. if ($original_path && isset($friendly_path)) { - $local_file = strtr($original_path, array('public:/' => variable_get('file_public_path', conf_path() . '/files'))); + $local_file = strtr($original_path, array('public:/' => PublicStream::basePath())); } elseif ($theme_name) { $local_file = drupal_get_path('theme', $theme_name) . '/' . $default; diff --git a/core/modules/system/lib/Drupal/system/Tests/File/ConfigTest.php b/core/modules/system/lib/Drupal/system/Tests/File/ConfigTest.php index 7314365..36de9d9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/ConfigTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/ConfigTest.php @@ -37,7 +37,6 @@ function testFileConfigurationPage() { // upon form submission. $file_path = $this->public_files_directory; $fields = array( - 'file_public_path' => $file_path . '/file_config_page_test/public', 'file_private_path' => $file_path . '/file_config_page_test/private', 'file_temporary_path' => $file_path . '/file_config_page_test/temporary', 'file_default_scheme' => 'private', diff --git a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php index 1b5c429..16bc592 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\File; +use Drupal\Core\StreamWrapper\PublicStream; use Drupal\simpletest\WebTestBase; /** @@ -82,7 +83,7 @@ function testUriFunctions() { // Test file_build_uri() and // Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath(). $this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', 'Expected scheme was added.'); - $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(), variable_get('file_public_path'), 'Expected default directory path was returned.'); + $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(), PublicStream::basePath(), 'Expected default directory path was returned.'); $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(), $config->get('path.temporary'), 'Expected temporary directory path was returned.'); $config->set('default_scheme', 'private')->save(); $this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', 'Got a valid URI from foo/bar.txt.'); diff --git a/core/modules/system/lib/Drupal/system/Tests/FileTransfer/FileTransferTest.php b/core/modules/system/lib/Drupal/system/Tests/FileTransfer/FileTransferTest.php index 080e8fd..f57d608 100644 --- a/core/modules/system/lib/Drupal/system/Tests/FileTransfer/FileTransferTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/FileTransfer/FileTransferTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\FileTransfer; use Drupal\Core\FileTransfer\FileTransferException; +use Drupal\Core\StreamWrapper\PublicStream; use Drupal\simpletest\WebTestBase; /** @@ -91,7 +92,7 @@ function testJail() { $gotit = TRUE; try { - $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. variable_get('file_public_path', conf_path() . '/files')); + $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. PublicStream::basePath()); } catch (FileTransferException $e) { $gotit = FALSE; diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php index 1afeca5..e91eb19 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php @@ -69,6 +69,7 @@ public function setUp() { $custom_strings[$definition['label']] = $langcode . ' ' . $definition['label']; } variable_set('locale_custom_strings_' . $langcode, array('' => $custom_strings)); + $this->rebuildContainer(); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php index 4358f10..4d5aec1 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\System; +use Drupal\Core\StreamWrapper\PublicStream; use Drupal\simpletest\WebTestBase; /** @@ -45,7 +46,7 @@ function setUp() { function testThemeSettings() { // Specify a filesystem path to be used for the logo. $file = current($this->drupalGetTestFiles('image')); - $file_relative = strtr($file->uri, array('public:/' => variable_get('file_public_path', conf_path() . '/files'))); + $file_relative = strtr($file->uri, array('public:/' => PublicStream::basePath())); $default_theme_path = 'core/themes/stark'; $supported_paths = array( @@ -97,7 +98,7 @@ function testThemeSettings() { if (file_uri_scheme($input) == 'public') { $implicit_public_file = file_uri_target($input); $explicit_file = $input; - $local_file = strtr($input, array('public:/' => variable_get('file_public_path', conf_path() . '/files'))); + $local_file = strtr($input, array('public:/' => PublicStream::basePath())); } // Adjust for fully qualified stream wrapper URI elsewhere. elseif (file_uri_scheme($input) !== FALSE) { @@ -107,7 +108,7 @@ function testThemeSettings() { elseif ($input == file_uri_target($file->uri)) { $implicit_public_file = $input; $explicit_file = 'public://' . $input; - $local_file = variable_get('file_public_path', conf_path() . '/files') . '/' . $input; + $local_file = PublicStream::basePath() . '/' . $input; } $this->assertEqual((string) $elements[0], $implicit_public_file); $this->assertEqual((string) $elements[1], $explicit_file); @@ -134,9 +135,9 @@ function testThemeSettings() { // Relative path within the public filesystem to non-existing file. 'whatever.png', // Relative path to non-existing file in public filesystem. - variable_get('file_public_path', conf_path() . '/files') . '/whatever.png', + PublicStream::basePath() . '/whatever.png', // Semi-absolute path to non-existing file in public filesystem. - '/' . variable_get('file_public_path', conf_path() . '/files') . '/whatever.png', + '/' . PublicStream::basePath() . '/whatever.png', // Relative path to arbitrary non-existing file. 'core/misc/whatever.png', // Semi-absolute path to arbitrary non-existing file. diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php index 6d87db5..2c76edc 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php @@ -251,6 +251,7 @@ protected function installLanguages() { drupal_unlink($filename); } } + $this->container->get('language_manager')->reset(); } /** diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 3f0fac0..a4e7926 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -9,6 +9,7 @@ use Drupal\Component\Uuid\Uuid; use Drupal\Core\Database\Database; use Drupal\Core\Language\Language; +use Drupal\Core\StreamWrapper\PublicStream; /** * Implements hook_requirements(). @@ -302,7 +303,7 @@ function system_requirements($phase) { if ($phase != 'install') { $filesystem_config = Drupal::config('system.file'); $directories = array( - variable_get('file_public_path', conf_path() . '/files'), + PublicStream::basePath(), // By default no private files directory is configured. For private files // to be secure the admin needs to provide a path outside the webroot. $filesystem_config->get('path.private'), @@ -315,8 +316,8 @@ function system_requirements($phase) { if ($phase == 'install') { global $conf; $directories = array(); - if (!empty($conf['file_public_path'])) { - $directories[] = $conf['file_public_path']; + if ($file_public_path = settings()->get('file_public_path')) { + $directories[] = $file_public_path; } else { // If we are installing Drupal, the settings.php file might not exist yet @@ -2242,6 +2243,15 @@ function system_update_8059() { } /** + * Move the file_public_path variable to settings. + */ +function system_upgrade_8060() { + if ($path = update_variable_get('file_public_path')) { + drupal_rewrite_settings(array('settings' => array('file_public_path' => $path))); + } +} + +/** * @} End of "defgroup updates-7.x-to-8.x". * The next series of updates should start at 9000. */ diff --git a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php index f7d0fe1..60ace1b 100644 --- a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php +++ b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php @@ -108,6 +108,7 @@ public function testTourFunctionality() { // Enable Italian language and navigate to it/tour-test1 and verify italian // version of tip is found. language_save(new Language(array('id' => 'it'))); + drupal_rebuild_language_negotiation_settings(); $this->drupalGet('it/tour-test-1'); $elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array( diff --git a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php index 434464e..0da1475 100644 --- a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php +++ b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php @@ -286,7 +286,6 @@ function testTranslateOwnContentRole() { * Resets static caches to make the test code match the client-side behavior. */ function resetCaches() { - drupal_static_reset('language_list'); $this->rebuildContainer(); } @@ -320,7 +319,7 @@ function addLanguage($langcode) { $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); // Make sure we are not using a stale list. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $languages = language_list(); $this->assertTrue(array_key_exists($langcode, $languages), 'Language was installed successfully.'); diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 091af6c..31a9c89 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -3,6 +3,7 @@ * @file * This script runs Drupal tests from command line. */ +use Drupal\Core\StreamWrapper\PublicStream; const SIMPLETEST_SCRIPT_COLOR_PASS = 32; // Green. const SIMPLETEST_SCRIPT_COLOR_FAIL = 31; // Red. @@ -399,7 +400,7 @@ function simpletest_script_execute_batch($test_classes) { echo 'FATAL ' . $child['class'] . ': test runner returned a non-zero error code (' . $status['exitcode'] . ').' . "\n"; if ($args['die-on-fail']) { list($db_prefix, ) = simpletest_last_test_get($child['test_id']); - $public_files = variable_get('file_public_path', conf_path() . '/files'); + $public_files = PublicStream::basePath(); $test_directory = $public_files . '/simpletest/' . substr($db_prefix, 10); echo 'Simpletest database and files kept and test exited immediately on fail so should be reproducible if you change settings.php to use the database prefix '. $db_prefix . ' and config directories in '. $test_directory . "\n"; $args['keep-results'] = TRUE; @@ -568,7 +569,7 @@ function simpletest_script_cleanup($test_id, $test_class, $exitcode) { // Check whether a test file directory was setup already. // @see prepareEnvironment() - $public_files = variable_get('file_public_path', conf_path() . '/files'); + $public_files = PublicStream::basePath(); $test_directory = $public_files . '/simpletest/' . substr($db_prefix, 10); if (is_dir($test_directory)) { // Output the error_log. diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php index f49184f..b29aa18 100644 --- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php +++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php @@ -46,10 +46,15 @@ public function setUp() { $this->languages = $languages; // Create a language manager stub. - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') + ->disableOriginalConstructor() + ->getMock(); $language_manager->expects($this->any()) ->method('getLanguage') ->will($this->returnValue($languages['en'])); + $language_manager->expects($this->any()) + ->method('getLanguageList') + ->will($this->returnValue($this->languages)); $this->languageManager = $language_manager; } @@ -95,7 +100,7 @@ function testProcessInbound() { $alias_processor = new PathProcessorAlias($alias_manager); $decode_processor = new PathProcessorDecode(); $front_processor = new PathProcessorFront($config_factory_stub); - $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $this->languages); + $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager); // First, test the processor manager with the processors in the incorrect // order. The alias processor will run before the language processor, meaning diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php index bd6ba26..f13ef95 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php @@ -113,7 +113,9 @@ public function testDefaultPluginManagerWithEmptyCache() { ->with($cid . ':en', $this->expectedDefinitions); $language = new Language(array('id' => 'en')); - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') + ->disableOriginalConstructor() + ->getMock(); $language_manager->expects($this->once()) ->method('getLanguage') ->with(Language::TYPE_INTERFACE) @@ -144,7 +146,9 @@ public function testDefaultPluginManagerWithFilledCache() { ->method('set'); $language = new Language(array('id' => 'en')); - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') + ->disableOriginalConstructor() + ->getMock(); $language_manager->expects($this->once()) ->method('getLanguage') ->with(Language::TYPE_INTERFACE) diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index fe16cfc..a11647d 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -455,6 +455,15 @@ # $settings['mixed_mode_sessions'] = TRUE; /** + * Public file path: + * + * A local file system path where public files will be stored. This directory + * must exist and be writable by Drupal. This directory must be relative to + * the Drupal installation directory and be accessible over the web. + */ +# $settings['file_public_path'] = 'sites/default/files'; + +/** * Session write interval: * * Set the minimum interval between each session write to database.