diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 5f1adad..0bbc549 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -693,6 +693,12 @@ function install_tasks($install_state) {
'type' => 'batch',
'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
),
+ 'install_update_configuration_translations' => array(
+ 'display_name' => st('Translate configuration'),
+ 'display' => $needs_translations,
+ 'type' => 'batch',
+ 'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+ ),
'install_finished' => array(
'display_name' => st('Finished'),
),
@@ -1666,7 +1672,21 @@ function install_configure_form($form, &$form_state, &$install_state) {
*/
function install_import_translations_remaining(&$install_state) {
include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
- return locale_translate_batch_import_update(array('langcode' => $install_state['parameters']['langcode']));
+ return locale_translate_batch_import_files(array('langcode' => $install_state['parameters']['langcode']));
+}
+
+/**
+ * Creates configuration translations.
+ *
+ * @param $install_state
+ * An array of information about the current installation state.
+ *
+ * @return
+ * The batch definition, if there are configuration objects to update.
+ */
+function install_update_configuration_translations(&$install_state) {
+ include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
+ return locale_config_batch_update_components(array('langcode' => $install_state['parameters']['langcode']));
}
/**
diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php
index f232f63..efd7ce6 100644
--- a/core/lib/Drupal/Core/Config/InstallStorage.php
+++ b/core/lib/Drupal/Core/Config/InstallStorage.php
@@ -15,6 +15,13 @@
class InstallStorage extends FileStorage {
/**
+ * Folder map indexed by configuration name
+ *
+ * @var array
+ */
+ protected $folders;
+
+ /**
* Overrides Drupal\Core\Config\FileStorage::__construct().
*/
public function __construct() {
@@ -38,17 +45,9 @@ public function __construct() {
* afterwards check for a corresponding module or theme.
*/
public function getFilePath($name) {
- // Extract the owner.
- $owner = strtok($name, '.');
- // Determine the path to the owner.
- $path = FALSE;
- foreach (array('profile', 'module', 'theme') as $type) {
- if ($path = drupal_get_path($type, $owner)) {
- $file = $path . '/config/' . $name . '.' . self::getFileExtension();
- if (file_exists($file)) {
- return $file;
- }
- }
+ $folders = $this->getAllFolders();
+ if (isset($folders[$name])) {
+ return $folders[$name] . '/' . $name . '.' . $this->getFileExtension();
}
// If any code in the early installer requests a configuration object that
// does not exist anywhere as default config, then that must be mistake.
@@ -90,6 +89,74 @@ public function rename($name, $new_name) {
* @throws Drupal\Core\Config\StorageException
*/
public function listAll($prefix = '') {
- throw new StorageException('List operation is not allowed during install.');
+ $names = array_keys($this->getAllFolders());
+ if (!$prefix) {
+ return $names;
+ }
+ else {
+ foreach ($names as $index => $name) {
+ if (strpos($name, $prefix) !== 0 ) {
+ unset($names[$index]);
+ }
+ }
+ return $names;
+ }
+ }
+
+ /**
+ * Returns a map of all metadata names and the module they belong to.
+ *
+ * @return array
+ * An array mapping metadata names with directories.
+ */
+ protected function getAllFolders() {
+ if (!isset($this->folders)) {
+ $this->folders = $this->getComponentNames('profile', array(drupal_get_profile()));
+ $this->folders += $this->getComponentNames('module', module_list());
+ $this->folders += $this->getComponentNames('theme', array_keys(list_themes()));
+ }
+ return $this->folders;
+ }
+
+ /**
+ * 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
+ * Folders indexed by configuration name.
+ */
+ public function getComponentNames($type, $list) {
+ $extension = '.' . $this->getFileExtension();
+ $folders = array();
+ foreach ($list as $name) {
+ $directory = $this->getComponentFolder($type, $name);
+ if (file_exists($directory)) {
+ $files = glob($directory . '/*' . $extension);
+ foreach ($files as $filename) {
+ $name = basename($filename, $extension);
+ $folders[$name] = $directory;
+ }
+ }
+ }
+ return $folders;
+ }
+
+ /**
+ * Get folder inside each component that contains the files.
+ *
+ * @param string $type
+ * Component type: 'module' | 'theme' | 'profile'
+ * @param string $name
+ * Component name.
+ *
+ * @return string
+ * The configuration folder name for this component.
+ */
+ protected function getComponentFolder($type, $name) {
+ return drupal_get_path($type, $name) . '/config';
}
}
diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
index 0776f18..5f16f41 100644
--- a/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
+++ b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
@@ -7,84 +7,25 @@
namespace Drupal\Core\Config\Metadata;
-use Drupal\Core\Config\FileStorage;
+use Drupal\Core\Config\InstallStorage;
/**
* Defines the file storage controller for metadata files.
*/
-class MetadataStorage extends FileStorage {
-
- /**
- * Metadata map indexing configuration names and modules.
- *
- * @var array
- */
- protected $metaMap;
-
- /**
- * Overrides Drupal\Core\Config\FileStorage::__construct().
- */
- public function __construct() {
- // Do nothing.
- }
+class MetadataStorage extends InstallStorage {
/**
* Implements Drupal\Core\Config\StorageInterface::exists().
*/
public function exists($name) {
- return array_key_exists($name, $this->getMetadataMap());
- }
-
- /**
- * Overrides Drupal\Core\Config\FileStorage::getFilePath().
- */
- public function getFilePath($name) {
- $metamap = $this->getMetadataMap();
- $module = $metamap[$name];
- // We are looking for $module/meta/$name.yml
- $directory = drupal_get_path('module', $module) . '/meta';
- return $directory . '/' . $name . '.yml';
- }
-
- /**
- * Implements Drupal\Core\Config\StorageInterface::listAll().
- */
- public function listAll($prefix = '') {
- $names = array_keys($this->getMetadataMap());
- if (!$prefix) {
- return $names;
- }
- else {
- foreach ($names as $index => $name) {
- if (strpos($name, $prefix) !== 0 ) {
- unset($names[$index]);
- }
- }
- return $names;
- }
+ return array_key_exists($name, $this->getAllFolders());
}
/**
- * Returns a map of all metadata names and the module they belong to.
- *
- * @return array
- * An array mapping metadata names with module names.
+ * Overrides Drupal\Core\Config\InstallStorage::getComponentFolder().
*/
- protected function getMetadataMap() {
- if (!isset($this->metaMap)) {
- $extension = '.' . self::getFileExtension();
- foreach (module_list() as $module) {
- $directory = drupal_get_path('module', $module) . '/meta';
- if (file_exists($directory)) {
- $files = glob($directory . '/*' . $extension);
- foreach ($files as $filename) {
- $name = basename($filename, $extension);
- $this->metaMap[$name] = $module;
- }
- }
- }
- }
- return $this->metaMap;
+ protected function getComponentFolder($type, $name) {
+ return drupal_get_path($type, $name) . '/meta';
}
}
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 3bab052..8e01943 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -11,9 +11,8 @@
use Drupal\locale\Gettext;
use Drupal\locale\PoDatabaseReader;
use Drupal\Core\Language\Language;
-use Drupal\Core\Config\Config;
-use Drupal\Core\Config\FileStorage;
-use Drupal\Core\Config\NullStorage;
+use Drupal\Core\Config\InstallStorage;
+use Drupal\Core\Config\StorageException;
/**
@@ -292,7 +291,11 @@ function locale_translate_add_language_set_batch($options) {
);
// See if we have language files to import for the newly added language,
// collect and import them.
- if ($batch = locale_translate_batch_import_update($options)) {
+ if ($batch = locale_translate_batch_import_files($options)) {
+ batch_set($batch);
+ }
+ // Create or update all configuration translations for this language.
+ if ($batch = locale_config_batch_update_components($options)) {
batch_set($batch);
}
}
@@ -357,33 +360,6 @@ function locale_translate_batch_import_files($options, $force = FALSE) {
}
/**
- * Prepare a batch to import translations and update configuration.
- *
- * @param array $options
- * An array with options that can have the following elements:
- * - 'components', Array with lists of components to update indexed by type.
- * Defaults to all modules and themes.
- * - All the options in locale_translate_batch_import_files().
- * @param $force
- * (optional) Import all available files, even if they were imported before.
- */
-function locale_translate_batch_import_update($options, $force = FALSE) {
- $options += array('components' => array());
- $batch = locale_translate_batch_import_files($options, $force);
- $langcodes = !empty($options['langcode']) ? array($options['langcode']) : array();
- if ($update_batch = locale_config_batch_update_components($options['components'], $langcodes)) {
- if ($batch) {
- // Merge both batches so we can see a result page for both.
- $batch['operations'] = array_merge($batch['operations'], $update_batch['operations']);
- }
- else {
- $batch = $update_batch;
- }
- }
- return $batch;
-}
-
-/**
* Get an array of available interface translation file.
*
* @param $langcode
@@ -621,14 +597,9 @@ function locale_translate_batch_finished($success, $results) {
$skipped_files[] = $filepath;
}
}
- $configuration = isset($results['stats']['configuration']) ? $results['stats']['configuration'] : 0;
}
drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)));
watchdog('locale', 'The translation was succesfully imported. %number new strings added, %update updated and %delete removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes));
- if ($configuration) {
- drupal_set_message(t('The configuration was successfully updated. There are %number configuration objects updated.', array('%number' => $configuration)));
- watchdog('locale', 'The configuration was successfully updated. %number configuration objects updated.', array('%number' => $configuration));
- }
if ($skips) {
if (module_exists('dblog')) {
$skip_message = format_plural($skips, 'A translation string was skipped because of disallowed or malformed HTML. See the log for details.', '@count translation strings were skipped because of disallowed or malformed HTML. See the log for details.', array('@url' => url('admin/reports/dblog')));
@@ -639,6 +610,10 @@ function locale_translate_batch_finished($success, $results) {
drupal_set_message($skip_message, 'error');
watchdog('locale', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING);
}
+ // Merge feedback about configuration updates too.
+ if (isset($results['stats']['configuration'])) {
+ locale_config_batch_finished($success, $results);
+ }
}
}
@@ -711,22 +686,37 @@ function locale_translate_delete_translation_files($langcode) {
/**
* Build a locale batch to refresh configuration.
*
+ * @param array $options
+ * An array with options that can have the following elements:
+ * - 'langcode': The language code. Optional, defaults to NULL, which means
+ * that all translatable languages will be refreshed.
+ * - 'finish_feedback': Whether or not to give feedback to the user when the
+ * batch is finished. Optional, defaults to TRUE.
* @param array $components
- * Array of component lists indexed by type.
- * @param array $langcodes
- * (optional) Array of language codes to refresh. Defaults to all languages.
+ * (optional) Array of component lists indexed by type. If not present or it
+ * is an empty array, it will update all components.
*/
-function locale_config_batch_update_components($components, $langcodes = array()) {
- $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
+function locale_config_batch_update_components($options, $components = array()) {
+ $langcodes = isset($options['langcode']) ? array($options['langcode']) : array_keys(locale_translatable_language_list());
if ($langcodes && $names = _locale_config_component_names($components)) {
- return locale_config_batch_build($names, $langcodes);
+ return locale_config_batch_build($names, $langcodes, $options);
}
}
/**
- * Build a locale batch to refresh specific configuration.
+ * Creates a locale batch to refresh specific configuration.
+ *
+ * @param array $names
+ * List of configuration object names to update.
+ * @param array $langcodes
+ * List of language codes to refresh.
+ * @param array $options
+ * An array with options that can have the following elements:
+ * - 'finish_feedback': Whether or not to give feedback to the user when the
+ * batch is finished. Optional, defaults to TRUE.
*/
-function locale_config_batch_build($names, $langcodes) {
+function locale_config_batch_build($names, $langcodes, $options = array()) {
+ $options += array('finish_feedback' => TRUE);
$t = get_t();
foreach ($names as $name) {
$operations[] = array('locale_config_batch_refresh_name', array($name, $langcodes));
@@ -738,6 +728,9 @@ function locale_config_batch_build($names, $langcodes) {
'error_message' => $t('Error updating configuration translations'),
'file' => drupal_get_path('module', 'locale') . '/locale.bulk.inc',
);
+ if (!empty($options['finish_feedback'])) {
+ $batch['completed'] = 'locale_config_batch_finished';
+ }
return $batch;
}
@@ -758,6 +751,23 @@ function locale_config_batch_refresh_name($name, $langcodes, &$context) {
}
/**
+ * Finished callback of system page locale import batch.
+ */
+function locale_config_batch_finished($success, $results) {
+ if ($success) {
+ $configuration = isset($results['stats']['configuration']) ? $results['stats']['configuration'] : 0;
+ if ($configuration) {
+ drupal_set_message(t('The configuration was successfully updated. There are %number configuration objects updated.', array('%number' => $configuration)));
+ watchdog('locale', 'The configuration was successfully updated. %number configuration objects updated.', array('%number' => $configuration));
+ }
+ else {
+ drupal_set_message(t('No configuration objects have been updated.'));
+ watchdog('locale', 'No configuration objects have been updated.', array(), WATCHDOG_WARNING);
+ }
+ }
+}
+
+/**
* Delete configuration for language.
*
* @param $langcode
@@ -782,69 +792,36 @@ function locale_config_delete_language($langcode) {
* Number of configuration objects retranslated.
*/
function locale_config_update_multiple($names, $langcodes = array()) {
+ $default_storage = new InstallStorage();
+ $config_storage = drupal_container()->get('config.storage');
$langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
$count = 0;
foreach ($names as $name) {
- if ($data = locale_config_default_data($name)) {
- $wrapper = new LocaleConfigWrapper($name, $data, locale_storage());
- foreach ($langcodes as $lang) {
- $translation = $wrapper->getTranslation($lang, TRUE)->getData();
- locale_config_update_storage($name, $lang, $translation);
- $count++;
+ $wrapper = NULL;
+ try {
+ if ($data = $default_storage->read($name)) {
+ $updated = $config_storage->read($name);
+ if ($data = _locale_config_data_compare($data, $updated)) {
+ $wrapper = new LocaleConfigWrapper($name, $data, locale_storage());
+ }
}
- }
- }
- return $count;
-}
-
-/**
- * Update translated configuration.
- *
- * @param string $name
- * Configuration name.
- * @param string $langcode
- * Language code.
- * @param array $data
- * Array with configuration data.
- */
-function locale_config_update_storage($name, $langcode, $data) {
- $locale_name = 'locale.config.' . $langcode . '.' . $name;
- $storage = drupal_container()->get('config.storage');
- // Keep existing translations and merge new ones on top of them.
- $current = $storage->read($locale_name);
- $updated = $current ? NestedArray::mergeDeep($current, $data) : $data;
- if ($updated) {
- $storage->write($locale_name, $updated);
- }
- else {
- $storage->delete($locale_name);
- }
- return $updated;
-}
-
-/**
- * Get default data for configuration object.
- *
- * @param string $name
- * Configuration name.
- */
-function locale_config_default_data($name) {
- $data = &drupal_static(__FUNCTION__);
- if (!isset($data[$name])) {
- if ($directory = locale_config_default_folder($name)) {
- $storage = new FileStorage($directory);
- $data[$name] = $storage->read($name);
- // Get default configuration and compare it with current one.
- if ($data[$name]) {
- $updated = drupal_container()->get('config.storage')->read($name);
- $data[$name] = _locale_config_data_compare($data[$name], $updated);
+ foreach ($langcodes as $langcode) {
+ $locale_name = 'locale.config.' . $langcode . '.' . $name;
+ $translation = $wrapper ? $wrapper->getTranslation($langcode, TRUE)->getData() : NULL;
+ if ($translation) {
+ $config_storage->write($locale_name, $translation);
+ $count++;
+ }
+ else {
+ $config_storage->delete($locale_name);
+ }
}
}
- else {
- $data[$name] = array();
+ catch (StorageException $e) {
+ // The configuration name does not exist, just continue.
}
}
- return $data[$name];
+ return $count;
}
/**
@@ -877,30 +854,6 @@ function _locale_config_data_compare($default, $updated) {
}
/**
- * Get all default configuration names and the path on which they are.
- *
- * @param string $name
- * (optional) Configuration name to get the directory where it is.
- *
- * @return array|string
- * All directories indexed by configuration name or only the one
- * where the configuration name is if $name present.
- */
-function locale_config_default_folder($name = NULL) {
- $folders = &drupal_static(__FUNCTION__);
- if (!$folders) {
- $folders = _locale_config_default_folder('module', module_list());
- $folders += _locale_config_default_folder('theme', array_keys(list_themes()));
- }
- if ($name) {
- return isset($folders[$name]) ? $folders[$name] : NULL;
- }
- else {
- return $folders;
- }
-}
-
-/**
* Delete configuration for uninstalled components.
*/
function locale_config_delete_components($components) {
@@ -916,32 +869,6 @@ function locale_config_delete_components($components) {
}
}
-
-/**
- * Get all configuration names and folders for a list of modules or themes.
- *
- * @param string $type
- * Type of components: 'module' | 'theme'
- * @param array $list
- * Array of theme or module names.
- *
- * @return array
- * Folders indexed by configuration name.
- */
-function _locale_config_default_folder($type, $list) {
- $folders = array();
- foreach ($list as $name) {
- $directory = drupal_get_path($type, $name) . '/config';
- if (file_exists($directory)) {
- $storage = new FileStorage($directory);
- foreach ($storage->listAll() as $config_name) {
- $folders[$config_name] = $directory;
- }
- }
- }
- return $folders;
-}
-
/**
* Get configuration names associated with components.
*
@@ -952,15 +879,16 @@ function _locale_config_default_folder($type, $list) {
* Array of configuration object names.
*/
function _locale_config_component_names($components) {
+ $storage = new InstallStorage();
if ($components) {
$names = array();
foreach ($components as $type => $list) {
- $names = array_merge($names, array_keys(_locale_config_default_folder($type, $list)));
+ $names = array_merge($names, $storage->getComponentNames($type, $list));
}
return $names;
}
else {
- return array_keys(locale_config_default_folder());
+ return $storage->listAll();
}
}
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 01ef845..b4771a0 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -466,8 +466,10 @@ function locale_system_update($components) {
// built-in support for translation imports in the installer.
if (!drupal_installation_attempted()) {
include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
- $options['components'] = $components;
- if ($batch = locale_translate_batch_import_update($options, TRUE)) {
+ if ($batch = locale_translate_batch_import_files(array(), TRUE)) {
+ batch_set($batch);
+ }
+ if ($batch = locale_config_batch_update_components(array(), $components)) {
batch_set($batch);
}
}
@@ -793,28 +795,40 @@ function locale_string_is_safe($string) {
* Language codes for updated translations.
* @param array $lids
* List of string identifiers that have been updated / created.
- * @param boolean $refresh_config
- * (optional)Set to TRUE to refresh configuration translations too.
*/
-function _locale_refresh_translations($langcodes, $lids, $refresh_config = FALSE) {
+function _locale_refresh_translations($langcodes, $lids) {
if ($lids && $langcodes) {
// Update javascript translations if any of the strings has a javascript location.
if ($strings = locale_storage()->getStrings(array('lid' => $lids, 'type' => 'javascript'))) {
array_map('_locale_invalidate_js', $langcodes);
}
- // Update configuration if any of the strings has a configuration location.
- if ($refresh_config) {
- include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
- if ($names = _locale_config_string_names($lids)) {
- locale_config_update_multiple($names, $langcodes);
- }
- }
}
// Clear locale cache.
cache()->invalidateTags(array('locale' => TRUE));
}
/**
+ * Refresh configuration after string translations have been updated.
+ *
+ * The information that will be refreshed includes:
+ * - JavaScript translations.
+ * - Locale cache.
+ *
+ * @param array $langcodes
+ * Language codes for updated translations.
+ * @param array $lids
+ * List of string identifiers that have been updated / created.
+ */
+function _locale_refresh_configuration($langcodes, $lids) {
+ if ($lids && $langcodes) {
+ include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
+ if ($names = _locale_config_string_names($lids)) {
+ locale_config_update_multiple($names, $langcodes);
+ }
+ }
+}
+
+/**
* Parses a JavaScript file, extracts strings wrapped in Drupal.t() and
* Drupal.formatPlural() and inserts them into the database.
*
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index e6f2d4c..7ca6e17 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -438,8 +438,9 @@ function locale_translate_edit_form_submit($form, &$form_state) {
}
if ($updated) {
- // Clear cache and force refresh of JavaScript translations.
- _locale_refresh_translations(array($langcode), $updated, TRUE);
+ // Clear cache and refresh configuration and JavaScript translations.
+ _locale_refresh_configuration(array($langcode), $updated);
+ _locale_refresh_translations(array($langcode), $updated);
}
}