diff --git a/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php b/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php index 41857d0..7f720c6 100644 --- a/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php +++ b/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php @@ -31,4 +31,14 @@ public function status() { return \Drupal::formBuilder()->getForm('locale_translation_status_form'); } + /** + * Wraps locale_translate_export_form(). + * + * @todo Remove locale_translate_export_form(). + */ + public function export() { + module_load_include('bulk.inc', 'locale'); + return drupal_get_form('locale_translate_export_form'); + } + } diff --git a/core/modules/locale/lib/Drupal/locale/Form/TranslationStatusForm.php b/core/modules/locale/lib/Drupal/locale/Form/TranslationStatusForm.php new file mode 100644 index 0000000..2736f16 --- /dev/null +++ b/core/modules/locale/lib/Drupal/locale/Form/TranslationStatusForm.php @@ -0,0 +1,299 @@ +get('module_handler'), + $container->get('state') + ); + } + + /** + * Constructs a TranslationStatusForm object. + * + * @param ModuleHandlerInterface $module_handler + * A module handler. + * @param \Drupal\Core\State\StateInterface $state + * The state service. + */ + public function __construct(ModuleHandlerInterface $module_handler, StateInterface $state) { + $this->moduleHandler = $module_handler; + $this->state = $state; + } + + /** + * {@inheritdoc} + */ + public function getFormID() { + return 'locale_translation_status_form'; + } + + /** + * Form builder for displaying the current translation status. + * + * @ingroup forms + */ + public function buildForm(array $form, array &$form_state) { + $this->moduleHandler->loadInclude('locale', 'translation.inc'); + $this->moduleHandler->loadInclude('locale', 'compare.inc'); + + $languages = locale_translatable_language_list(); + $status = $this->state->get('locale.translation_status'); + $options = array(); + $languages_update = array(); + $languages_not_found = array(); + $projects_update = array(); + // Prepare information about projects which have available translation + // updates. + if ($languages && $status) { + $updates = $this->prepareUpdateData($status); + + // Build data options for the select table. + foreach ($updates as $langcode => $update) { + $title = String::checkPlain($languages[$langcode]->name); + $locale_translation_update_info = array('#theme' => 'locale_translation_update_info'); + foreach (array('updates', 'not_found') as $update_status) { + if (isset($update[$update_status])) { + $locale_translation_update_info['#' . $update_status] = $update[$update_status]; + } + } + $options[$langcode] = array( + 'title' => array( + 'class' => array('label'), + 'data' => array( + '#title' => $title, + '#markup' => $title + ), + ), + 'status' => array( + 'class' => array('description', 'expand', 'priority-low'), + 'data' => drupal_render($locale_translation_update_info), + ), + ); + if (!empty($update['not_found'])) { + $languages_not_found[$langcode] = $langcode; + } + else if (!empty($update['updates'])) { + $languages_update[$langcode] = $langcode; + } + } + // Sort the table data on language name. + uasort($options, function ($a, $b) { + return strcasecmp($a['title']['data']['#title'], $b['title']['data']['#title']); + }); + $languages_not_found = array_diff($languages_not_found, $languages_update); + } + + $last_checked = $this->state->get('locale.translation_last_checked'); + $form['last_checked'] = array( + '#theme' => 'locale_translation_last_check', + '#last' => $last_checked, + ); + + $header = array( + 'title' => array( + 'data' => $this->t('Language'), + 'class' => array('title'), + ), + 'status' => array( + 'data' => $this->t('Status'), + 'class' => array('status', 'priority-low'), + ), + ); + + if (!$languages) { + $empty = $this->t('No translatable languages available. Add a language first.', array('@add_language' => $this->urlGenerator()->generateFromPath('admin/config/regional/language'))); + } + elseif ($status) { + $empty = $this->t('All translations up to date.'); + } + else { + $empty = $this->t('No translation status available. Check manually.', array('@check' => url('admin/reports/translations/check'))); + } + + // The projects which require an update. Used by the _submit callback. + $form['projects_update'] = array( + '#type' => 'value', + '#value' => $projects_update, + ); + + $form['langcodes'] = array( + '#type' => 'tableselect', + '#header' => $header, + '#options' => $options, + '#default_value' => $languages_update, + '#empty' => $empty, + '#js_select' => TRUE, + '#multiple' => TRUE, + '#required' => TRUE, + '#not_found' => $languages_not_found, + '#after_build' => array('locale_translation_language_table'), + ); + + $form['#attached']['library'][] = 'locale/drupal.locale.admin'; + $form['#attached']['css'] = array(drupal_get_path('module', 'locale') . '/css/locale.admin.css'); + + $form['actions'] = array('#type' => 'actions'); + if ($languages_update) { + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Update translations'), + ); + } + + return $form; + } + + /** + * Prepare information about projects with available translation updates. + * + * @param array $status + * Translation update status as an array keyed by Project ID and langcode. + * + * @return array + * Translation update status as an array keyed by language code and + * translation update status. + */ + protected function prepareUpdateData(array $status) { + $updates = array(); + + // @todo Calling locale_translation_build_projects() is an expensive way to + // get a module name. In follow-up issue http://drupal.org/node/1842362 + // the project name will be stored to display use, like here. + $project_data = locale_translation_build_projects(); + + foreach ($status as $project_id => $project) { + foreach ($project as $langcode => $project_info) { + // No translation file found for this project-language combination. + if (empty($project_info->type)) { + $updates[$langcode]['not_found'][] = array( + 'name' => $project_info->name == 'drupal' ? $this->t('Drupal core') : $project_data[$project_info->name]->info['name'], + 'version' => $project_info->version, + 'info' => $this->createInfoString($project_info), + ); + } + // Translation update found for this project-language combination. + elseif ($project_info->type == LOCALE_TRANSLATION_LOCAL || $project_info->type == LOCALE_TRANSLATION_REMOTE ) { + $local = isset($project_info->files[LOCALE_TRANSLATION_LOCAL]) ? $project_info->files[LOCALE_TRANSLATION_LOCAL] : NULL; + $remote = isset($project_info->files[LOCALE_TRANSLATION_REMOTE]) ? $project_info->files[LOCALE_TRANSLATION_REMOTE] : NULL; + $recent = _locale_translation_source_compare($local, $remote) == LOCALE_TRANSLATION_SOURCE_COMPARE_LT ? $remote : $local; + $updates[$langcode]['updates'][] = array( + 'name' => $project_data[$project_info->name]->info['name'], + 'version' => $project_info->version, + 'timestamp' => $recent->timestamp, + ); + } + } + } + return $updates; + } + + /** + * Provides debug info for projects in case translation files are not found. + * + * Translations files are being fetched either from Drupal translation server + * and local files or only from the local filesystem depending on the + * "Translation source" setting at admin/config/regional/translate/settings. + * This method will produce debug information including the respective path(s) + * based on this setting. + * + * Translations for development versions are never fetched, so the debug info + * for that is a fixed message. + * + * @param array $project_info + * An array which is the project information of the source. + * + * @return string + * The string which contains debug information. + */ + protected function createInfoString($project_info) { + $remote_path = isset($project_info->files['remote']->uri) ? $project_info->files['remote']->uri : FALSE; + $local_path = isset($project_info->files['local']->uri) ? $project_info->files['local']->uri : FALSE; + + if (strpos($project_info->version, 'dev') !== FALSE) { + return $this->t('No translation files are provided for development releases.'); + } + if (locale_translation_use_remote_source() && $remote_path && $local_path) { + return $this->t('File not found at %remote_path nor at %local_path', array( + '%remote_path' => $remote_path, + '%local_path' => $local_path, + )); + } + elseif ($local_path) { + return $this->t('File not found at %local_path', array('%local_path' => $local_path)); + } + return $this->t('Translation file location could not be determined.'); + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + // Check if a language has been selected. 'tableselect' doesn't. + if (!array_filter($form_state['values']['langcodes'])) { + form_set_error('', $this->t('Select a language to update.')); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + $this->moduleHandler->loadInclude('locale', 'fetch.inc'); + $langcodes = array_filter($form_state['values']['langcodes']); + $projects = array_filter($form_state['values']['projects_update']); + + // Set the translation import options. This determines if existing + // translations will be overwritten by imported strings. + $options = _locale_translation_default_update_options(); + + // If the status was updated recently we can immediately start fetching the + // translation updates. If the status is expired we clear it an run a batch to + // update the status and then fetch the translation updates. + $last_checked = $this->state->get('locale.translation_last_checked'); + if ($last_checked < REQUEST_TIME - LOCALE_TRANSLATION_STATUS_TTL) { + locale_translation_clear_status(); + $batch = locale_translation_batch_update_build(array(), $langcodes, $options); + batch_set($batch); + } + else { + $batch = locale_translation_batch_fetch_build($projects, $langcodes, $options); + batch_set($batch); + } + } + +} diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index afb4047..368aba8 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -1366,3 +1366,19 @@ function _locale_rebuild_js($langcode = NULL) { return TRUE; } } +/** + * Form element callback: After build changes to the language update table. + * + * Adds labels to the languages and removes checkboxes from languages from which + * translation files could not be found. + */ +function locale_translation_language_table($form_element) { + // Remove checkboxes of languages without updates. + if ($form_element['#not_found']) { + foreach ($form_element['#not_found'] as $langcode) { + $form_element[$langcode] = array(); + } + } + return $form_element; +} + diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc index c35e33a..b07a0e3 100644 --- a/core/modules/locale/locale.pages.inc +++ b/core/modules/locale/locale.pages.inc @@ -38,234 +38,6 @@ function locale_translation_manual_status() { } /** - * Page callback: Display the current translation status. - * - * @see locale_menu() - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Use \Drupal\locale\Form\LocaleForm::status(). - */ -function locale_translation_status_form($form, &$form_state) { - module_load_include('translation.inc', 'locale'); - module_load_include('compare.inc', 'locale'); - $updates = $options = array(); - $languages_update = $languages_not_found = array(); - $projects_update = array(); - - // @todo Calling locale_translation_build_projects() is an expensive way to - // get a module name. In follow-up issue http://drupal.org/node/1842362 - // the project name will be stored to display use, like here. - $project_data = locale_translation_build_projects(); - $languages = locale_translatable_language_list(); - $status = locale_translation_get_status(); - - // Prepare information about projects which have available translation - // updates. - if ($languages && $status) { - foreach ($status as $project) { - foreach ($project as $langcode => $project_info) { - // No translation file found for this project-language combination. - if (empty($project_info->type)) { - $updates[$langcode]['not_found'][] = array( - 'name' => $project_info->name == 'drupal' ? t('Drupal core') : $project_data[$project_info->name]->info['name'], - 'version' => $project_info->version, - 'info' => _locale_translation_status_debug_info($project_info), - ); - $languages_not_found[$langcode] = $langcode; - } - // Translation update found for this project-language combination. - elseif ($project_info->type == LOCALE_TRANSLATION_LOCAL || $project_info->type == LOCALE_TRANSLATION_REMOTE ) { - $local = isset($project_info->files[LOCALE_TRANSLATION_LOCAL]) ? $project_info->files[LOCALE_TRANSLATION_LOCAL] : NULL; - $remote = isset($project_info->files[LOCALE_TRANSLATION_REMOTE]) ? $project_info->files[LOCALE_TRANSLATION_REMOTE] : NULL; - $recent = _locale_translation_source_compare($local, $remote) == LOCALE_TRANSLATION_SOURCE_COMPARE_LT ? $remote : $local; - $updates[$langcode]['updates'][] = array( - 'name' => $project_data[$project_info->name]->info['name'], - 'version' => $project_info->version, - 'timestamp' => $recent->timestamp, - ); - $languages_update[$langcode] = $langcode; - $projects_update[$project_info->name] = $project_info->name; - } - } - } - $languages_not_found = array_diff($languages_not_found, $languages_update); - - // Build data options for the select table. - foreach($updates as $langcode => $update) { - $title = String::checkPlain($languages[$langcode]->name); - $locale_translation_update_info = array('#theme' => 'locale_translation_update_info'); - foreach (array('updates', 'not_found') as $update_status) { - if (isset($update[$update_status])) { - $locale_translation_update_info['#' . $update_status] = $update[$update_status]; - } - } - $options[$langcode] = array( - 'title' => array( - 'class' => array('label'), - 'data' => array( - '#title' => $title, - '#markup' => $title - ), - ), - 'status' => array('class' => array('description', 'expand', 'priority-low'), 'data' => drupal_render($locale_translation_update_info)), - ); - } - // Sort the table data on language name. - uasort($options, function ($a, $b) { - return strcasecmp($a['title']['data']['#title'], $b['title']['data']['#title']); - }); - } - - $last_checked = \Drupal::state()->get('locale.translation_last_checked'); - $form['last_checked'] = array( - '#theme' => 'locale_translation_last_check', - '#last' => $last_checked, - ); - - $header = array( - 'title' => array( - 'data' => t('Language'), - 'class' => array('title'), - ), - 'status' => array( - 'data' => t('Status'), - 'class' => array('status', 'priority-low'), - ), - ); - - if (!$languages) { - $empty = t('No translatable languages available. Add a language first.', array('@add_language' => url('admin/config/regional/language'))); - } - elseif ($status) { - $empty = t('All translations up to date.'); - } - else { - $empty = t('No translation status available. Check manually.', array('@check' => url('admin/reports/translations/check'))); - } - - // The projects which require an update. Used by the _submit callback. - $form['projects_update'] = array( - '#type' => 'value', - '#value' => $projects_update, - ); - - $form['langcodes'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#default_value' => $languages_update, - '#empty' => $empty, - '#js_select' => TRUE, - '#multiple' => TRUE, - '#required' => TRUE, - '#not_found' => $languages_not_found, - '#after_build' => array('locale_translation_language_table'), - ); - - $form['#attached']['library'][] = 'locale/drupal.locale.admin'; - - $form['actions'] = array('#type' => 'actions'); - if ($languages_update) { - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Update translations'), - ); - } - - return $form; -} - -/** - * Form validation handler for locale_translation_status_form(). - */ -function locale_translation_status_form_validate($form, &$form_state) { - // Check if a language has been selected. 'tableselect' doesn't. - if (!array_filter($form_state['values']['langcodes'])) { - form_set_error('', $form_state, t('Select a language to update.')); - } -} - -/** - * Form submission handler for locale_translation_status_form(). - */ -function locale_translation_status_form_submit($form, &$form_state) { - module_load_include('fetch.inc', 'locale'); - $langcodes = array_filter($form_state['values']['langcodes']); - $projects = array_filter($form_state['values']['projects_update']); - - // Set the translation import options. This determines if existing - // translations will be overwritten by imported strings. - $options = _locale_translation_default_update_options(); - - // If the status was updated recently we can immediately start fetching the - // translation updates. If the status is expired we clear it an run a batch to - // update the status and then fetch the translation updates. - $last_checked = \Drupal::state()->get('locale.translation_last_checked'); - if ($last_checked < REQUEST_TIME - LOCALE_TRANSLATION_STATUS_TTL) { - locale_translation_clear_status(); - $batch = locale_translation_batch_update_build(array(), $langcodes, $options); - batch_set($batch); - } - else { - $batch = locale_translation_batch_fetch_build($projects, $langcodes, $options); - batch_set($batch); - } -} - -/** - * Form element callback: After build changes to the language update table. - * - * Adds labels to the languages and removes checkboxes from languages from which - * translation files could not be found. - */ -function locale_translation_language_table($form_element) { - // Remove checkboxes of languages without updates. - if ($form_element['#not_found']) { - foreach ($form_element['#not_found'] as $langcode) { - $form_element[$langcode] = array(); - } - } - return $form_element; -} - -/** - * Provides debug info for projects in case translation files are not found. - * - * Translations files are being fetched either from Drupal translation server - * and local files or only from the local filesystem depending on the - * "Translation source" setting at admin/config/regional/translate/settings. - * This method will produce debug information including the respective path(s) - * based on this setting. - * - * Translations for development versions are never fetched, so the debug info - * for that is a fixed message. - * - * @param array $source - * An array which is the project information of the source. - * - * @return string - * The string which contains debug information. - */ -function _locale_translation_status_debug_info($source) { - $remote_path = isset($source->files['remote']->uri) ? $source->files['remote']->uri : ''; - $local_path = isset($source->files['local']->uri) ? $source->files['local']->uri : ''; - - if (strpos($source->version, 'dev') !== FALSE) { - return t('No translation files are provided for development releases.'); - } - if (locale_translation_use_remote_source() && $remote_path && $local_path) { - return t('File not found at %remote_path nor at %local_path', array( - '%remote_path' => $remote_path, - '%local_path' => $local_path, - )); - } - elseif ($local_path) { - return t('File not found at %local_path', array('%local_path' => $local_path)); - } - return t('Translation file location could not be determined.'); -} - -/** * Returns HTML for translation edit form. * * @param array $variables diff --git a/core/modules/locale/locale.routing.yml b/core/modules/locale/locale.routing.yml index 6658dad..0869917 100644 --- a/core/modules/locale/locale.routing.yml +++ b/core/modules/locale/locale.routing.yml @@ -40,7 +40,7 @@ locale.translate_export: locale.translate_status: path: '/admin/reports/translations' defaults: - _content: '\Drupal\locale\Form\LocaleForm::status' + _form: '\Drupal\locale\Form\TranslationStatusForm' _title: 'Available translation updates' requirements: _permission: 'translate interface'