diff --git a/core/core.services.yml b/core/core.services.yml index 53a2725..dcb195e5 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -260,8 +260,6 @@ services: config.storage.schema: class: Drupal\Core\Config\ExtensionInstallStorage arguments: ['@config.storage', 'config/schema'] - config.storage.installer: - class: Drupal\Core\Config\InstallStorage config.typed: class: Drupal\Core\Config\TypedConfigManager arguments: ['@config.storage', '@config.storage.schema', '@cache.discovery', '@module_handler'] diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php index 3d5adad..c180847 100644 --- a/core/lib/Drupal/Core/Language/LanguageManager.php +++ b/core/lib/Drupal/Core/Language/LanguageManager.php @@ -233,27 +233,16 @@ public function getLanguageSwitchLinks($type, Url $url) { } /** - * Some common languages with their English and native names. - * - * Language codes are defined by the W3C language tags document for - * interoperability. Language codes typically have a language and, optionally, - * a script or regional variant name. See: - * http://www.w3.org/International/articles/language-tags/ for more - * information. - * - * This list is based on languages available from localize.drupal.org. See - * http://localize.drupal.org/issues for information on how to add languages - * there. - * - * The "Left-to-right marker" comments and the enclosed UTF-8 markers are to - * make otherwise strange looking PHP syntax natural (to not be displayed in - * right to left). See http://drupal.org/node/128866#comment-528929. - * - * @return array - * An array of language code to language name information. - * Language name information itself is an array of English and native names. + * @inheritdoc */ public static function getStandardLanguageList() { + // This list is based on languages available from localize.drupal.org. See + // http://localize.drupal.org/issues for information on how to add languages + // there. + // + // The "Left-to-right marker" comments and the enclosed UTF-8 markers are to + // make otherwise strange looking PHP syntax natural (to not be displayed in + // right to left). See http://drupal.org/node/128866#comment-528929. return array( 'af' => array('Afrikaans', 'Afrikaans'), 'am' => array('Amharic', 'አማርኛ'), diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php index c29dd33..b536a00 100644 --- a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php +++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php @@ -208,4 +208,19 @@ public function setConfigOverrideLanguage(LanguageInterface $language = NULL); */ public function getConfigOverrideLanguage(); + /** + * Some common languages with their English and native names. + * + * Language codes are defined by the W3C language tags document for + * interoperability. Language codes typically have a language and, optionally, + * a script or regional variant name. See: + * http://www.w3.org/International/articles/language-tags/ for more + * information. + * + * @return array + * An array of language code to language name information. Language name + * information itself is an array of English and native names. + */ + public static function getStandardLanguageList(); + } diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 356f68b..58a913a 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -28,13 +28,13 @@ function book_help($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.book': $output = '

' . t('About') . '

'; - $output .= '

' . t('The Book module is used for creating structured, multi-page content, such as site resource guides, manuals, and wikis. It allows you to create content that has chapters, sections, subsections, or any similarly-tiered structure. For more information, see the online documentation for the Book module.', array('!book' => 'https://drupal.org/documentation/modules/book')) . '

'; + $output .= '

' . t('The Book module is used for creating structured, multi-page content, such as site resource guides, manuals, and wikis. It allows you to create content that has chapters, sections, subsections, or any similarly-tiered structure. For more information, see the online documentation for the Book module.', ['!book' => 'https://drupal.org/documentation/modules/book']) . '

'; $output .= '

' . t('Uses') . '

'; $output .= '
'; $output .= '
' . t('Adding and managing book content') . '
'; - $output .= '
' . t('You can assign separate permissions for creating new books as well as creating, editing and deleting book content. Users with the Administer book outlines permission can add any type of content to a book by selecting the appropriate book outline while editing the content. They can also view a list of all books, and edit and rearrange section titles on the Book administration page.', array('!admin-book' => \Drupal::url('book.admin'))) . '
'; + $output .= '
' . t('You can assign separate permissions for creating new books as well as creating, editing and deleting book content. Users with the Administer book outlines permission can add any type of content to a book by selecting the appropriate book outline while editing the content. They can also view a list of all books, and edit and rearrange section titles on the Book administration page.', ['!admin-book' => \Drupal::url('book.admin')]) . '
'; $output .= '
' . t('Book navigation') . '
'; - $output .= '
' . t("Book pages have a default book-specific navigation block. This navigation block contains links that lead to the previous and next pages in the book, and to the level above the current page in the book's structure. This block can be enabled on the Blocks administration page. For book pages to show up in the book navigation, they must be added to a book outline.", array('!admin-block' => \Drupal::url('block.admin_display'))) . '
'; + $output .= '
' . t("Book pages have a default book-specific navigation block. This navigation block contains links that lead to the previous and next pages in the book, and to the level above the current page in the book's structure. This block can be enabled on the Blocks administration page. For book pages to show up in the book navigation, they must be added to a book outline.", ['!admin-block' => \Drupal::url('block.admin_display')]) . '
'; $output .= '
' . t('Collaboration') . '
'; $output .= '
' . t('Books can be created collaboratively, as they allow users with appropriate permissions to add pages into existing books, and add those pages to a custom table of contents.') . '
'; $output .= '
' . t('Printing books') . '
'; @@ -46,7 +46,7 @@ function book_help($route_name, RouteMatchInterface $route_match) { return '

' . t('The book module offers a means to organize a collection of related content pages, collectively known as a book. When viewed, this content automatically displays links to adjacent book pages, providing a simple navigation system for creating and reviewing structured content.') . '

'; case 'entity.node.book_outline_form': - return '

' . t('The outline feature allows you to include pages in the Book hierarchy, as well as move them within the hierarchy or to reorder an entire book.', array('!book' => \Drupal::url('book.render'), '!book-admin' => \Drupal::url('book.admin'))) . '

'; + return '

' . t('The outline feature allows you to include pages in the Book hierarchy, as well as move them within the hierarchy or to reorder an entire book.', ['!book' => \Drupal::url('book.render'), '!book-admin' => \Drupal::url('book.admin')]) . '

'; } } @@ -54,27 +54,27 @@ function book_help($route_name, RouteMatchInterface $route_match) { * Implements hook_theme(). */ function book_theme() { - return array( - 'book_navigation' => array( - 'variables' => array('book_link' => NULL), - ), - 'book_tree' => array( + return [ + 'book_navigation' => [ + 'variables' => ['book_link' => NULL], + ], + 'book_tree' => [ 'render element' => 'tree', - ), - 'book_link' => array( + ], + 'book_link' => [ 'render element' => 'element', 'function' => 'theme_book_link', - ), - 'book_export_html' => array( - 'variables' => array('title' => NULL, 'contents' => NULL, 'depth' => NULL), - ), - 'book_all_books_block' => array( + ], + 'book_export_html' => [ + 'variables' => ['title' => NULL, 'contents' => NULL, 'depth' => NULL], + ], + 'book_all_books_block' => [ 'render element' => 'book_menus', - ), - 'book_node_export_html' => array( - 'variables' => array('node' => NULL, 'children' => NULL), - ), - ); + ], + 'book_node_export_html' => [ + 'variables' => ['node' => NULL, 'children' => NULL] + ], + ]; } /** @@ -100,31 +100,31 @@ function book_node_links_alter(array &$node_links, NodeInterface $node, array &$ $child_type = \Drupal::config('book.settings')->get('child_type'); $access_control_handler = \Drupal::entityManager()->getAccessControlHandler('node'); if (($account->hasPermission('add content to books') || $account->hasPermission('administer book outlines')) && $access_control_handler->createAccess($child_type) && $node->isPublished() && $node->book['depth'] < BookManager::BOOK_MAX_DEPTH) { - $links['book_add_child'] = array( + $links['book_add_child'] = [ 'title' => t('Add child page'), 'url' => Url::fromRoute('node.add', ['node_type' => $child_type], ['query' => ['parent' => $node->id()]]), - ); + ]; } if ($account->hasPermission('access printer-friendly version')) { - $links['book_printer'] = array( + $links['book_printer'] = [ 'title' => t('Printer-friendly version'), 'url' => Url::fromRoute('book.export', [ 'type' => 'html', 'node' => $node->id(), ]), - 'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.')) - ); + 'attributes' => ['title' => t('Show a printer-friendly version of this book page and its sub-pages.')] + ]; } } } if (!empty($links)) { - $node_links['book'] = array( + $node_links['book'] = [ '#theme' => 'links__node__book', '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); + '#attributes' => ['class' => ['links', 'inline']], + ]; } } } @@ -151,17 +151,17 @@ function book_form_node_form_alter(&$form, FormStateInterface $form_state, $form $collapsed = !($node->isNew() && !empty($node->book['pid'])); $form = \Drupal::service('book.manager')->addFormElements($form, $form_state, $node, $account, $collapsed); // The "js-hide" class hides submit button when Javascript is enabled. - $form['book']['pick-book'] = array( + $form['book']['pick-book'] = [ '#type' => 'submit', '#value' => t('Change book (update list of parents)'), - '#submit' => array('book_pick_book_nojs_submit'), + '#submit' => ['book_pick_book_nojs_submit'], '#weight' => 20, - '#attributes' => array( - 'class' => array( + '#attributes' => [ + 'class' => [ 'js-hide', - ), - ), - ); + ], + ], + ]; $form['#entity_builders'][] = 'book_node_builder'; } } @@ -235,8 +235,8 @@ function book_node_view(array &$build, EntityInterface $node, EntityViewDisplayI if (!$book_node->access()) { return; } - $book_navigation = array( '#theme' => 'book_navigation', '#book_link' => $node->book); - $build['book_navigation'] = array( + $book_navigation = [ '#theme' => 'book_navigation', '#book_link' => $node->book]; + $build['book_navigation'] = [ '#markup' => drupal_render($book_navigation), '#weight' => 100, '#attached' => [ @@ -249,7 +249,7 @@ function book_node_view(array &$build, EntityInterface $node, EntityViewDisplayI '#cache' => [ 'tags' => $node->getEntityType()->getListCacheTags(), ], - ); + ]; } } } @@ -341,10 +341,10 @@ function book_form_node_delete_confirm_alter(&$form, FormStateInterface $form_st $node = Node::load($form['nid']['#value']); if (isset($node->book) && $node->book['has_children']) { - $form['book_warning'] = array( - '#markup' => '

' . t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', array('%title' => $node->label())) . '

', + $form['book_warning'] = [ + '#markup' => '

' . t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $node->label()]) . '

', '#weight' => -10, - ); + ]; } } @@ -367,11 +367,11 @@ function template_preprocess_book_all_books_block(&$variables) { $elements = $variables['book_menus']; $variables['book_menus'] = array(); foreach (Element::children($elements) as $index) { - $variables['book_menus'][] = array( + $variables['book_menus'][] = [ 'id' => $index, 'menu' => $elements[$index], 'title' => $elements[$index]['#book_title'], - ); + ]; } } @@ -391,7 +391,7 @@ function template_preprocess_book_navigation(&$variables) { // Provide extra variables for themers. Not needed by default. $variables['book_id'] = $book_link['bid']; $variables['book_title'] = SafeMarkup::checkPlain($book_link['link_title']); - $variables['book_url'] = \Drupal::url('entity.node.canonical', array('node' => $book_link['bid'])); + $variables['book_url'] = \Drupal::url('entity.node.canonical', ['node' => $book_link['bid']]); $variables['current_depth'] = $book_link['depth']; $variables['tree'] = ''; @@ -404,11 +404,11 @@ function template_preprocess_book_navigation(&$variables) { $build = array(); if ($prev = $book_outline->prevLink($book_link)) { - $prev_href = \Drupal::url('entity.node.canonical', array('node' => $prev['nid'])); - $build['#attached']['html_head_link'][][] = array( + $prev_href = \Drupal::url('entity.node.canonical', ['node' => $prev['nid']]); + $build['#attached']['html_head_link'][][] = [ 'rel' => 'prev', 'href' => $prev_href, - ); + ]; $variables['prev_url'] = $prev_href; $variables['prev_title'] = SafeMarkup::checkPlain($prev['title']); } @@ -416,21 +416,21 @@ function template_preprocess_book_navigation(&$variables) { /** @var \Drupal\book\BookManagerInterface $book_manager */ $book_manager = \Drupal::service('book.manager'); if ($book_link['pid'] && $parent = $book_manager->loadBookLink($book_link['pid'])) { - $parent_href = \Drupal::url('entity.node.canonical', array('node' => $book_link['pid'])); - $build['#attached']['html_head_link'][][] = array( + $parent_href = \Drupal::url('entity.node.canonical', ['node' => $book_link['pid']]); + $build['#attached']['html_head_link'][][] = [ 'rel' => 'up', 'href' => $parent_href, - ); + ]; $variables['parent_url'] = $parent_href; $variables['parent_title'] = SafeMarkup::checkPlain($parent['title']); } if ($next = $book_outline->nextLink($book_link)) { - $next_href = \Drupal::url('entity.node.canonical', array('node' => $next['nid'])); - $build['#attached']['html_head_link'][][] = array( + $next_href = \Drupal::url('entity.node.canonical', ['node' => $next['nid']]); + $build['#attached']['html_head_link'][][] = [ 'rel' => 'next', 'href' => $next_href, - ); + ]; $variables['next_url'] = $next_href; $variables['next_title'] = SafeMarkup::checkPlain($next['title']); } @@ -442,7 +442,7 @@ function template_preprocess_book_navigation(&$variables) { $variables['has_links'] = FALSE; // Link variables to filter for values and set state of the flag variable. - $links = array('prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title'); + $links = ['prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title']; foreach ($links as $link) { if (isset($variables[$link])) { // Flag when there is a value. diff --git a/core/modules/config/src/Tests/ConfigInstallTest.php b/core/modules/config/src/Tests/ConfigInstallTest.php index fe9a6b6..00ec4fa 100644 --- a/core/modules/config/src/Tests/ConfigInstallTest.php +++ b/core/modules/config/src/Tests/ConfigInstallTest.php @@ -7,6 +7,7 @@ namespace Drupal\config\Tests; +use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\PreExistingConfigException; use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\UnmetDependenciesException; @@ -223,7 +224,8 @@ public function testDependencyChecking() { function testLanguage() { $this->installModules(['config_test_language']); // Test imported configuration with implicit language code. - $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english'); + $storage = new InstallStorage(); + $data = $storage->read('config_test.dynamic.dotted.english'); $this->assertTrue(!isset($data['langcode'])); $this->assertEqual( $this->config('config_test.dynamic.dotted.english')->get('langcode'), @@ -231,7 +233,7 @@ function testLanguage() { ); // Test imported configuration with explicit language code. - $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french'); + $data = $storage->read('config_test.dynamic.dotted.french'); $this->assertEqual($data['langcode'], 'fr'); $this->assertEqual( $this->config('config_test.dynamic.dotted.french')->get('langcode'), diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 237ef21..8329ce1 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -405,23 +405,30 @@ function locale_system_update(array $components) { // Skip running the translation imports if in the installer, // because it would break out of the installer flow. We have // built-in support for translation imports in the installer. - if (!drupal_installation_attempted() && locale_translatable_language_list() && \Drupal::config('locale.settings')->get('translation.import_enabled')) { - module_load_include('compare.inc', 'locale'); - - // Update the list of translatable projects and start the import batch. - // Only when new projects are added the update batch will be triggered. Not - // each enabled module will introduce a new project. E.g. sub modules. - $projects = array_keys(locale_translation_build_projects()); - if ($list = array_intersect($list, $projects)) { - module_load_include('fetch.inc', 'locale'); - // Get translation status of the projects, download and update - // translations. - $options = _locale_translation_default_update_options(); - $batch = locale_translation_batch_update_build($list, array(), $options); - batch_set($batch); + if (!drupal_installation_attempted() && locale_translatable_language_list()) { + if (\Drupal::config('locale.settings')->get('translation.import_enabled')) { + module_load_include('compare.inc', 'locale'); + + // Update the list of translatable projects and start the import batch. + // Only when new projects are added the update batch will be triggered. + // Not each enabled module will introduce a new project. E.g. sub modules. + $projects = array_keys(locale_translation_build_projects()); + if ($list = array_intersect($list, $projects)) { + module_load_include('fetch.inc', 'locale'); + // Get translation status of the projects, download and update + // translations. + $options = _locale_translation_default_update_options(); + $batch = locale_translation_batch_update_build($list, array(), $options); + batch_set($batch); + } } + + // Construct a batch to update configuration for all components. Installing + // this component may have installed configuration from any number of other + // components. Do this even if import is not enabled because parsing new + // configuration may expose new source strings. \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc'); - if ($batch = locale_config_batch_update_components(array(), array(), $components)) { + if ($batch = locale_config_batch_update_components(array())) { batch_set($batch); } } diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index 284878b..d9a2258 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -1,7 +1,11 @@ services: + locale.default.config.storage: + class: Drupal\locale\LocaleDefaultConfigStorage + arguments: ['@config.storage', '@language_manager'] + public: false locale.config_manager: class: Drupal\locale\LocaleConfigManager - arguments: ['@config.storage', '@config.storage.installer', '@locale.storage', '@config.factory', '@config.typed', '@language_manager'] + arguments: ['@config.storage', '@locale.storage', '@config.factory', '@config.typed', '@language_manager', '@locale.default.config.storage'] locale.storage: class: Drupal\locale\StringDatabaseStorage arguments: ['@database'] diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php index da21bca..d96c758 100644 --- a/core/modules/locale/src/LocaleConfigManager.php +++ b/core/modules/locale/src/LocaleConfigManager.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\StringTranslation\TranslationWrapper; @@ -45,13 +46,6 @@ class LocaleConfigManager { protected $configStorage; /** - * The storage instance for reading default configuration data. - * - * @var \Drupal\Core\Config\StorageInterface - */ - protected $installStorage; - - /** * The string storage for reading and writing translations. * * @var \Drupal\locale\StringStorageInterface; @@ -96,13 +90,17 @@ class LocaleConfigManager { protected $isUpdatingFromLocale = FALSE; /** + * The locale default config storage instance. + * + * @var \Drupal\locale\LocaleDefaultConfigStorage + */ + protected $defaultConfigStorage; + + /** * Creates a new typed configuration manager. * * @param \Drupal\Core\Config\StorageInterface $config_storage * The storage object to use for reading configuration data. - * @param \Drupal\Core\Config\StorageInterface $install_storage - * The storage object to use for reading default configuration - * data. * @param \Drupal\locale\StringStorageInterface $locale_storage * The locale storage to use for reading string translations. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory @@ -111,14 +109,16 @@ class LocaleConfigManager { * The typed configuration manager. * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager * The language manager. + * @param \Drupal\locale\LocaleDefaultConfigStorage $default_config_storage + * The locale default configuration storage. */ - public function __construct(StorageInterface $config_storage, StorageInterface $install_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager) { + public function __construct(StorageInterface $config_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager, LocaleDefaultConfigStorage $default_config_storage) { $this->configStorage = $config_storage; - $this->installStorage = $install_storage; $this->localeStorage = $locale_storage; $this->configFactory = $config_factory; $this->typedConfigManager = $typed_config; $this->languageManager = $language_manager; + $this->defaultConfigStorage = $default_config_storage; } /** @@ -133,7 +133,7 @@ public function __construct(StorageInterface $config_storage, StorageInterface $ public function getTranslatableDefaultConfig($name) { if ($this->isSupported($name)) { // Create typed configuration wrapper based on install storage data. - $data = $this->installStorageRead($name); + $data = $this->defaultConfigStorage->read($name); $type_definition = $this->typedConfigManager->getDefinition($name); $data_definition = $this->typedConfigManager->buildDataDefinition($type_definition, $data); $typed_config = $this->typedConfigManager->create($data_definition, $data); @@ -293,12 +293,12 @@ public function getComponentNames(array $components = array()) { foreach ($components as $type => $list) { // InstallStorage::getComponentNames returns a list of folders keyed by // config name. - $names = array_merge($names, $this->installStorageComponents($type, $list)); + $names = array_merge($names, $this->defaultConfigStorage->getComponentNames($type, $list)); } return $names; } else { - return $this->installStorageAll(); + return $this->defaultConfigStorage->listAll(); } } @@ -474,7 +474,7 @@ public function hasTranslation($name, $langcode) { * configuration exists. */ public function getDefaultConfigLangcode($name) { - $shipped = $this->installStorageRead($name); + $shipped = $this->defaultConfigStorage->read($name); if (!empty($shipped)) { return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en'; } @@ -635,81 +635,4 @@ protected function filterOverride(array $override_data, array $translatable) { return $filtered_data; } - /** - * Read a configuration from install storage or default languages. - * - * @param string $name - * Configuration object name. - * - * @return array - * Configuration data from install storage or default language. - */ - protected function installStorageRead($name) { - if ($this->installStorage->exists($name)) { - return $this->installStorage->read($name); - } - elseif (strpos($name, 'language.entity.') === 0) { - // Simulate default languages as if they were shipped as default - // configuration. - $langcode = str_replace('language.entity.', '', $name); - $predefined_languages = $this->languageManager->getStandardLanguageList(); - if (isset($predefined_languages[$langcode])) { - $data = $this->configStorage->read($name); - $data['label'] = $predefined_languages[$langcode][0]; - return $data; - } - } - } - - /** - * Return the list of configuration in install storage and current languages. - * - * @return array - * List of configuration in install storage and current languages. - */ - protected function installStorageAll() { - $languages = $this->predefinedConfiguredLanguages(); - return array_unique(array_merge($this->installStorage->listAll(), $languages)); - } - - /** - * Get all configuration names and folders for a list of modules or themes. - * - * @param string $type - * Type of components: 'module' | 'theme' | 'profile' - * @param array $list - * Array of theme or module names. - * - * @return array - * Configuration names provided by that component. In case of language - * module this list is extended with configured languages that have - * predefined names as well. - */ - protected function installStorageComponents($type, array $list) { - $names = array_keys($this->installStorage->getComponentNames($type, $list)); - if ($type == 'module' && in_array('language', $list)) { - $languages = $this->predefinedConfiguredLanguages(); - $names = array_unique(array_merge($names, $languages)); - } - return $names; - } - - /** - * Compute the list of configuration names that match predefined languages. - * - * @return array - * The list of configuration names that match predefined languages. - */ - protected function predefinedConfiguredLanguages() { - $names = $this->configStorage->listAll('language.entity.'); - $predefined_languages = $this->languageManager->getStandardLanguageList(); - foreach ($names as $id => $name) { - $langcode = str_replace('language.entity.', '', $name); - if (!isset($predefined_languages[$langcode])) { - unset($names[$id]); - } - } - return array_values($names); - } - } diff --git a/core/modules/locale/src/LocaleDefaultConfigStorage.php b/core/modules/locale/src/LocaleDefaultConfigStorage.php new file mode 100644 index 0000000..5828f61 --- /dev/null +++ b/core/modules/locale/src/LocaleDefaultConfigStorage.php @@ -0,0 +1,165 @@ +configStorage = $config_storage; + $this->languageManager = $language_manager; + + $this->requiredInstallStorage = new InstallStorage(); + $this->optionalInstallStorage = new InstallStorage(InstallStorage::CONFIG_OPTIONAL_DIRECTORY); + } + + /** + * Read a configuration from install storage or default languages. + * + * @param string $name + * Configuration object name. + * + * @return array + * Configuration data from install storage or default language. + */ + public function read($name) { + if ($this->requiredInstallStorage->exists($name)) { + return $this->requiredInstallStorage->read($name); + } + elseif ($this->optionalInstallStorage->exists($name)) { + return $this->optionalInstallStorage->read($name); + } + elseif (strpos($name, 'language.entity.') === 0) { + // Simulate default languages as if they were shipped as default + // configuration. + $langcode = str_replace('language.entity.', '', $name); + $predefined_languages = $this->languageManager->getStandardLanguageList(); + if (isset($predefined_languages[$langcode])) { + $data = $this->configStorage->read($name); + $data['label'] = $predefined_languages[$langcode][0]; + return $data; + } + } + } + + /** + * Return the list of configuration in install storage and current languages. + * + * @return array + * List of configuration in install storage and current languages. + */ + public function listAll() { + $languages = $this->predefinedConfiguredLanguages(); + return array_unique( + array_merge( + $this->requiredInstallStorage->listAll(), + $this->optionalInstallStorage->listAll(), + $languages + ) + ); + } + + /** + * Get all configuration names and folders for a list of modules or themes. + * + * @param string $type + * Type of components: 'module' | 'theme' | 'profile' + * @param array $list + * Array of theme or module names. + * + * @return array + * Configuration names provided by that component. In case of language + * module this list is extended with configured languages that have + * predefined names as well. + */ + public function getComponentNames($type, array $list) { + $names = array_unique( + array_merge( + array_keys($this->requiredInstallStorage->getComponentNames($type, $list)), + array_keys($this->optionalInstallStorage->getComponentNames($type, $list)) + ) + ); + if ($type == 'module' && in_array('language', $list)) { + $languages = $this->predefinedConfiguredLanguages(); + $names = array_unique(array_merge($names, $languages)); + } + return $names; + } + + /** + * Compute the list of configuration names that match predefined languages. + * + * @return array + * The list of configuration names that match predefined languages. + */ + protected function predefinedConfiguredLanguages() { + $names = $this->configStorage->listAll('language.entity.'); + $predefined_languages = $this->languageManager->getStandardLanguageList(); + foreach ($names as $id => $name) { + $langcode = str_replace('language.entity.', '', $name); + if (!isset($predefined_languages[$langcode])) { + unset($names[$id]); + } + } + return array_values($names); + } + +} + diff --git a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php index 24017fd..4564a5a 100644 --- a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php @@ -22,7 +22,7 @@ class LocaleConfigSubscriberTest extends KernelTestBase { /** * {@inheritdoc} */ - public static $modules = ['language', 'locale']; + public static $modules = ['language', 'locale', 'system']; /** * The configurable language manager used in this test. @@ -61,6 +61,7 @@ protected function setUp() { $this->setUpDefaultLanguage(); $this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']); + $this->installSchema('system', ['queue']); $this->setupLanguages(); diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php index 4535b9b..f0bf723 100644 --- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php @@ -19,6 +19,13 @@ class LocaleConfigTranslationTest extends WebTestBase { /** + * The language code used. + * + * @var string + */ + protected $langcode; + + /** * Modules to enable. * * @var array @@ -38,28 +45,28 @@ protected function setUp() { $this->config('locale.settings') ->set('translation.import_enabled', TRUE) ->save(); - } - /** - * Tests basic configuration translation. - */ - public function testConfigTranslation() { // Add custom language. - $langcode = 'xx'; + $this->langcode = 'xx'; $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface', 'administer modules', 'access site-wide contact form', 'administer contact forms', 'administer site configuration')); $this->drupalLogin($admin_user); $name = $this->randomMachineName(16); $edit = array( 'predefined_langcode' => 'custom', - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'label' => $name, 'direction' => LanguageInterface::DIRECTION_LTR, ); $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language')); // Set path prefix. - $edit = array("prefix[$langcode]" => $langcode); + $edit = array("prefix[$this->langcode]" => $this->langcode); $this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration')); + } + /** + * Tests basic configuration translation. + */ + public function testConfigTranslation() { // Check that the maintenance message exists and create translation for it. $source = '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'; $string = $this->storage->findString(array('source' => $source, 'context' => '', 'type' => 'configuration')); @@ -69,7 +76,7 @@ public function testConfigTranslation() { $message = $this->randomMachineName(20); $search = array( 'string' => $string->source, - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -82,7 +89,7 @@ public function testConfigTranslation() { $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); // Get translation and check we've only got the message. - $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'system.maintenance')->get(); + $translation = \Drupal::languageManager()->getLanguageConfigOverride($this->langcode, 'system.maintenance')->get(); $this->assertEqual(count($translation), 1, 'Got the right number of properties after translation.'); $this->assertEqual($translation['message'], $message); @@ -93,7 +100,7 @@ public function testConfigTranslation() { // Translate using the UI so configuration is refreshed. $search = array( 'string' => $string->source, - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -105,12 +112,12 @@ public function testConfigTranslation() { ); $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); - $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'core.date_format.medium')->get(); + $translation = \Drupal::languageManager()->getLanguageConfigOverride($this->langcode, 'core.date_format.medium')->get(); $this->assertEqual($translation['pattern'], 'D', 'Got the right date format pattern after translation.'); // Formatting the date 8 / 27 / 1985 @ 13:37 EST with pattern D should // display "Tue". - $formatted_date = format_date(494015820, $type = 'medium', NULL, NULL, $langcode); + $formatted_date = format_date(494015820, $type = 'medium', NULL, NULL, $this->langcode); $this->assertEqual($formatted_date, 'Tue', 'Got the right formatted date using the date format translation pattern.'); // Assert strings from image module config are not available. @@ -127,7 +134,7 @@ public function testConfigTranslation() { $this->assertTrue(isset($locations['configuration']) && isset($locations['configuration']['image.style.medium']), 'Configuration string has been created with the right location'); // Check the string is unique and has no translation yet. - $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); + $translations = $this->storage->getTranslations(array('language' => $this->langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); $this->assertEqual(count($translations), 1); $translation = reset($translations); $this->assertEqual($translation->source, $string->source); @@ -137,7 +144,7 @@ public function testConfigTranslation() { $image_style_label = $this->randomMachineName(20); $search = array( 'string' => $string->source, - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -149,12 +156,12 @@ public function testConfigTranslation() { $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); // Check the right single translation has been created. - $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); + $translations = $this->storage->getTranslations(array('language' => $this->langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); $translation = reset($translations); $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && $translation->translation == $image_style_label, 'Got only one translation for image configuration.'); // Try more complex configuration data. - $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'image.style.medium')->get(); + $translation = \Drupal::languageManager()->getLanguageConfigOverride($this->langcode, 'image.style.medium')->get(); $this->assertEqual($translation['label'], $image_style_label, 'Got the right translation for image style name after translation'); // Uninstall the module. @@ -169,7 +176,7 @@ public function testConfigTranslation() { $category_label = $this->randomMachineName(20); $search = array( 'string' => 'Website feedback', - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -183,7 +190,7 @@ public function testConfigTranslation() { // Check if this category displayed in this language will use the // translation. This test ensures the entity loaded from the request // upcasting will already work. - $this->drupalGet($langcode . '/contact/feedback'); + $this->drupalGet($this->langcode . '/contact/feedback'); $this->assertText($category_label); // Check if the UI does not show the translated String. @@ -191,4 +198,54 @@ public function testConfigTranslation() { $this->assertFieldById('edit-label', 'Website feedback', 'Translation is not loaded for Edit Form.'); } + /** + * Test translatability of optional configuration in locale. + */ + public function testOptionalConfiguration() { + $this->assertNodeConfig(FALSE, FALSE); + // Enable the node module. + $this->drupalPostForm('admin/modules', array('modules[Core][node][enable]' => "1"), t('Save configuration')); + $this->drupalPostForm(NULL, array(), t('Continue')); + $this->rebuildContainer(); + $this->assertNodeConfig(TRUE, FALSE); + // Enable the views module (which node provides some optional config for). + $this->drupalPostForm('admin/modules', array('modules[Core][views][enable]' => "1"), t('Save configuration')); + $this->rebuildContainer(); + $this->assertNodeConfig(TRUE, TRUE); + } + + /** + * Check that node configuration source strings are made available in locale. + * + * @param bool $required + * Whether to assume a sample of the required default configuration is + * present. + * @param bool $optional + * Whether to assume a sample of the optional default configuration is + * present. + */ + protected function assertNodeConfig($required, $optional) { + // Check the required default configuration in node module. + $string = $this->storage->findString(array('source' => 'Make content sticky', 'context' => '', 'type' => 'configuration')); + if ($required) { + $this->assertFalse($this->config('system.action.node_make_sticky_action')->isNew()); + $this->assertTrue($string, 'Node action text can be found with node module.'); + } + else { + $this->assertTrue($this->config('system.action.node_make_sticky_action')->isNew()); + $this->assertFalse($string, 'Node action text can not be found without node module.'); + } + + // Check the optional default configuration in node module. + $string = $this->storage->findString(array('source' => 'No front page content has been created yet.', 'context' => '', 'type' => 'configuration')); + if ($optional) { + $this->assertFalse($this->config('views.view.frontpage')->isNew()); + $this->assertTrue($string, 'Node view text can be found with node and views modules.'); + } + else { + $this->assertTrue($this->config('views.view.frontpage')->isNew()); + $this->assertFalse($string, 'Node view text can not be found without node and/or views modules.'); + } + } + }