Commit 1bb21b5b authored by webchick's avatar webchick

Issue #1742894 by Sutharsan, webflo, attiks, Gábor Hojtsy: Added Get status of...

Issue #1742894 by Sutharsan, webflo, attiks, Gábor Hojtsy: Added Get status of local and remote translation files.
parent 2c994156
translation:
use_source: 'remote_and_local'
check_disabled_modules: false
default_filename: '%project-%version.%language.po'
default_server_pattern: 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po'
......@@ -28,7 +28,18 @@
* the module's folder.
* @code
* interface translation project = example_module
* interface translation server pattern = sites/example.com/modules/custom/example_module/%project-%version.%language.po
* interface translation server pattern = modules/custom/example_module/%project-%version.%language.po
* @endcode
*
* Streamwrappers can be used in the server pattern definition. The interface
* translations directory (Configuration > Media > File system) can be addressed
* using the "translations://" streamwrapper. But also other streamwrappers can
* be used.
* @code
* interface translation server pattern = translations://%project-%version.%language.po
* @endcode
* @code
* interface translation server pattern = public://translations/%project-%version.%language.po
* @endcode
*
* Multiple custom modules or themes sharing the same po file should have
......@@ -97,7 +108,7 @@
* @param array $projects
* Project data as returned by update_get_projects().
*
* @see locale_project_list().
* @see locale_translation_project_list().
*/
function hook_locale_translation_projects_alter(&$projects) {
// The translations are located at a custom translation sever.
......
<?php
/**
* @file
* Batch process to check the availability of remote or local po files.
*/
/**
* Build a batch to get the status of remote and local translation files.
*
* The batch process fetches the state of both remote and (if configured) local
* translation files. The data of the most recent translation is stored per
* per project and per language. This data is stored in a state variable
* 'locale_translation_status'. The timestamp it was last updated is stored
* in the state variable 'locale_translation_status_last_update'.
*
* @param array $sources
* Array of translation source objects for which to check the state of
* translation source files.
*/
function locale_translation_batch_status_build($sources) {
$operations = array();
// Set the batch processes for remote sources.
foreach ($sources as $source) {
$operations[] = array('locale_translation_batch_status_fetch_remote', array($source));
}
// Check for local sources, compare the results of local and remote and store
// the most recent.
$operations[] = array('locale_translation_batch_status_fetch_local', array($sources));
$operations[] = array('locale_translation_batch_status_compare', array());
$batch = array(
'operations' => $operations,
'title' => t('Checking available translations'),
'finished' => 'locale_translation_batch_status_finished',
'error_message' => t('Error checking available interface translation updates.'),
'file' => drupal_get_path('module', 'locale') . '/locale.batch.inc',
);
return $batch;
}
/**
* Batch operation callback: Check the availability of a remote po file.
*
* Checks the presence and creation time of one po file per batch process. The
* file URL and timestamp are stored.
*
* @param array $source
* A translation source object of the project for which to check the state of
* a remote po file.
* @param array $context
* The batch context array. The collected state is stored in the 'results'
* parameter of the context.
*
* @see locale_translation_batch_status_fetch_local()
* @see locale_translation_batch_status_compare()
*/
function locale_translation_batch_status_fetch_remote($source, &$context) {
// Check the translation file at the remote server and update the source
// data with the remote status.
if (isset($source->files['remote'])) {
$remote_file = $source->files['remote'];
$result = locale_translation_http_check($remote_file->url);
// Update the file object with the result data. In case of a redirect we
// store the resulting url.
if ($result && !empty($result->updated)) {
$remote_file->url = isset($result->redirect_url) ? $result->redirect_url : $remote_file->url;
$remote_file->timestamp = $result->updated;
$source->files['remote'] = $remote_file;
}
$context['results'][$source->name][$source->language] = $source;
}
}
/**
* Batch operation callback: Check the availability of local po files.
*
* Checks the presence and creation time of po files in the local file system.
* The file path and the timestamp are stored.
*
* @param array $sources
* Array of translation source objects of projects for which to check the
* state of local po files.
* @param array $context
* The batch context array. The collected state is stored in the 'results'
* parameter of the context.
*
* @see locale_translation_batch_status_fetch_remote()
* @see locale_translation_batch_status_compare()
*/
function locale_translation_batch_status_fetch_local($sources, &$context) {
module_load_include('compare.inc', 'locale');
// Get the status of local translation files and store the result data in the
// batch results for later processing.
foreach ($sources as $source) {
if (isset($source->files['local'])) {
locale_translation_source_check_file($source);
// If remote data was collected before, we merge it into the newly
// collected result.
if (isset($context['results'][$source->name][$source->language])) {
$source->files['remote'] = $context['results'][$source->name][$source->language]->files['remote'];
}
$context['results'][$source->name][$source->language] = $source;
}
}
}
/**
* Batch operation callback: Compare states and store the result.
*
* In the preceding batch processes data of remote and local translation sources
* is collected. Here we compare the collected results and update the source
* object with the data of the most recent translation file. The end result is
* stored in the 'locale_translation_status' state variable. Other
* processes can collect this data after the batch process is completed.
*
* @param array $context
* The batch context array. The 'results' element contains a structured array
* of project data with languages, local and remote source data.
*
* @see locale_translation_batch_status_fetch_remote()
* @see locale_translation_batch_status_fetch_local()
*/
function locale_translation_batch_status_compare(&$context) {
module_load_include('compare.inc', 'locale');
$results = array();
foreach ($context['results'] as $project => $langcodes) {
foreach ($langcodes as $langcode => $source) {
$local = isset($source->files['local']) ? $source->files['local'] : NULL;
$remote = isset($source->files['remote']) ? $source->files['remote'] : NULL;
// The available translation files are compare and data of the most recent
// file is used to update the source object.
$file = _locale_translation_source_compare($local, $remote) == LOCALE_TRANSLATION_SOURCE_COMPARE_LT ? $remote : $local;
if (isset($file->timestamp)) {
$source->type = $file->type;
$source->timestamp = $file->timestamp;
$results[$project][$langcode] = $source;
}
}
}
state()->set('locale_translation_status', $results);
state()->set('locale_translation_status_last_update', REQUEST_TIME);
}
/**
* Batch finished callback: Set result message.
*
* @param boolean $success
* TRUE if batch succesfully completed.
* @param array $results
* Batch results.
*/
function locale_translation_batch_status_finished($success, $results) {
$t = get_t();
if($success) {
if ($results) {
drupal_set_message(format_plural(
count($results),
'Checked available interface translation updates for one project.',
'Checked available interface translation updates for @count projects.'
));
}
}
else {
drupal_set_message($t('An error occurred trying to check available interface translation updates.'), 'error');
}
}
/**
* Check if remote file exists and when it was last updated.
*
* @param string $url
* URL of remote file.
* @param array $headers
* HTTP request headers.
* @return stdClass
* Result object containing the HTTP request headers, response code, headers,
* data, redirect status and updated timestamp.
*/
function locale_translation_http_check($url, $headers = array()) {
$result = drupal_http_request($url, array('headers' => $headers, 'method' => 'HEAD'));
if ($result && $result->code == '200') {
$result->updated = isset($result->headers['last-modified']) ? strtotime($result->headers['last-modified']) : 0;
}
return $result;
}
This diff is collapsed.
......@@ -68,6 +68,40 @@
*/
const LOCALE_CUSTOMIZED = 1;
/**
* Translation update mode: Use local files only.
*
* When checking for available translation updates, only local files will be
* used. Any remote translation file will be ignored. Also custom modules and
* themes which have set a "server pattern" to use a remote translation server
* will be ignored.
*/
const LOCALE_TRANSLATION_USE_SOURCE_LOCAL = 'local';
/**
* Translation update mode: Use both remote and local files.
*
* When checking for available translation updates, both local and remote files
* will be checked.
*/
const LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL = 'remote_and_local';
/**
* Default location of gettext file on the translation server.
*
* @see locale_translation_default_translation_server().
*/
const LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN = 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po';
/**
* Default file name of translation files stored in the local file system.
*
* The file name containing placeholders which are also used by the server
* pattern. See locale_translation_build_server_pattern() for supported
* placeholders.
*/
const LOCALE_TRANSLATION_DEFAULT_FILENAME = '%project-%version.%language.po';
/**
* Implements hook_help().
*/
......@@ -142,6 +176,20 @@ function locale_menu() {
'type' => MENU_LOCAL_TASK,
'file' => 'locale.bulk.inc',
);
$items['admin/reports/translations'] = array(
'title' => 'Available translation updates',
'description' => 'Get a status report about available interface translations for your installed modules and themes.',
'page callback' => 'locale_translation_status',
'access arguments' => array('translate interface'),
'file' => 'locale.pages.inc',
);
$items['admin/reports/translations/check'] = array(
'title' => 'Manual translation update check',
'page callback' => 'locale_translation_manual_status',
'access arguments' => array('translate interface'),
'type' => MENU_CALLBACK,
'file' => 'locale.pages.inc',
);
return $items;
}
......@@ -227,7 +275,20 @@ function locale_language_delete($language) {
cache()->delete('locale:' . $language->langcode);
}
// Locale core functionality
/**
* Returns list of translatable languages.
*
* @return array
* Array of installed languages keyed by language name. English is omitted
* unless it is marked as translatable.
*/
function locale_translatable_language_list() {
$languages = language_list();
if (!locale_translate_english()) {
unset($languages['en']);
}
return $languages;
}
/**
* Provides interface translation services.
......@@ -644,6 +705,20 @@ function locale_form_system_file_system_settings_alter(&$form, $form_state) {
if ($form['file_default_scheme']) {
$form['file_default_scheme']['#weight'] = 20;
}
$form['#submit'][] = 'locale_system_file_system_settings_submit';
}
/**
* Submit handler for the file system settings form.
*
* Clears the translation status when the Interface translations directory
* changes. Without a translations directory local po files in the directory
* should be ignored. The old translation status is no longer valid.
*/
function locale_system_file_system_settings_submit(&$form, $form_state) {
if ($form['locale_translate_file_directory']['#default_value'] != $form_state['values']['locale_translate_file_directory']) {
locale_translation_clear_status();
}
}
/**
......@@ -669,6 +744,14 @@ function locale_preprocess_node(&$variables) {
}
}
/**
* Clear the translation status cache.
*/
function locale_translation_clear_status() {
state()->delete('locale_translation_status');
state()->delete('locale_translation_status_last_update');
}
/**
* Check that a string is safe to be added or imported as a translation.
*
......@@ -819,10 +902,7 @@ function _locale_invalidate_js($langcode = NULL) {
if (empty($langcode)) {
// Invalidate all languages.
$languages = language_list();
if (!locale_translate_english()) {
unset($languages['en']);
}
$languages = locale_translatable_language_list();
foreach ($languages as $lcode => $data) {
$parsed['refresh:' . $lcode] = 'waiting';
}
......
......@@ -441,7 +441,44 @@ function locale_translate_edit_form_submit($form, &$form_state) {
}
/**
* Default theme function for translatione edit form.
* Page callback: Checks for translation updates and displays the translations status.
*
* Manually checks the translation status without the use of cron.
*
* @see locale_menu()
*/
function locale_translation_manual_status() {
module_load_include('compare.inc', 'locale');
locale_translation_flush_projects();
$projects = locale_translation_get_projects();
locale_translation_check_projects($projects);
// Execute a batch if required.
if (batch_get()) {
batch_process('admin/reports/translations');
}
drupal_goto('admin/reports/translations');
}
/**
* Page callback: Display the current translation status.
*
* @see locale_menu()
*/
function locale_translation_status() {
$languages = locale_translatable_language_list();
if (!$languages) {
drupal_set_message(t('No translatable languages available. <a href="@add_lanuage">Add language</a> first.', array('@add_lanuage' => url('admin/config/regional/language'))), 'warning');
}
// @todo Calculate and display the translation status here. See the follow-up
// issue for translation interface: http://drupal.org/node/1804702
return 'TODO: Show the translation status here';
}
/**
* Default theme function for translation edit form.
*/
function theme_locale_translate_edit_form_strings($variables) {
$output = '';
......
......@@ -7,4 +7,4 @@ hidden = TRUE
; Definitions for interface translations.
interface translation project = locale_test
interface translation server pattern = core/modules/locale/test/modules/locale_test/%project-%version.%language.po
interface translation server pattern = core/modules/locale/test/test.%language.po
......@@ -10,5 +10,6 @@
*/
function locale_test_uninstall() {
// Clear variables.
variable_del('locale_translation_test_system_info_alter');
state()->delete('locale_translation_test_system_info_alter');
state()->delete('locale_translation_test_projects');
}
......@@ -16,12 +16,130 @@ function locale_test_system_info_alter(&$info, $file, $type) {
// By default the locale_test modules are hidden and have a project specified.
// To test the module detection proces by locale_project_list() the
// test modules should mimic a custom module. I.e. be non-hidden.
if (!variable_get('locale_translation_test_system_info_alter', FALSE)) {
return;
if (state()->get('locale_translation_test_system_info_alter')) {
if ($file->name == 'locale_test' || $file->name == 'locale_test_disabled') {
// Don't hide the module.
$info['hidden'] = FALSE;
}
}
}
/**
* Implements hook_locale_translation_projects_alter().
*
* The translation status process by default checks the status of the installed
* projects. This function replaces the data of the installed modules by a
* predefined set of modules with fixed file names and release versions. Project
* names, versions, timestamps etc must be fixed because they must match the
* files created by the test script.
*
* The "locale_translation_test_projects" state variable must be set by the
* test script in order for this hook to take effect.
*/
function locale_test_locale_translation_projects_alter(&$projects) {
if (state()->get('locale_translation_test_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/';
if ($file->name == 'locale_test' || $file->name == 'locale_test_disabled') {
// Make the module appear as unhidden.
$info['hidden'] = FALSE;
// Completely replace the project data with a set of test projects.
$base_url = url();
$files_url = variable_get('file_public_path', conf_path() . '/files');
$projects = array (
'drupal' => array (
'name' => 'drupal',
'info' => array (
'name' => 'Drupal',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language.txt',
'package' => 'Core',
'version' => '8.0',
'project' => 'drupal',
'_info_file_ctime' => 1348824632,
'datestamp' => 0,
),
'datestamp' => 0,
'project_type' => 'core',
'project_status' => TRUE,
),
'contrib_module_one' => array (
'name' => 'contrib_module_one',
'info' => array (
'name' => 'Contributed module one',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language.txt',
'package' => 'Other',
'version' => '8.x-1.1',
'project' => 'contrib_module_one',
'datestamp' => '1344471537',
'_info_file_ctime' => 1348767306,
),
'datestamp' => '1344471537',
'project_type' => 'module',
'project_status' => TRUE,
),
'contrib_module_two' => array (
'name' => 'contrib_module_two',
'info' => array (
'name' => 'Contributed module two',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language.txt',
'package' => 'Other',
'version' => '8.x-2.0-beta4',
'project' => 'contrib_module_two',
'datestamp' => '1344471537',
'_info_file_ctime' => 1348767306,
),
'datestamp' => '1344471537',
'project_type' => 'module',
'project_status' => TRUE,
),
'contrib_module_three' => array (
'name' => 'contrib_module_three',
'info' => array (
'name' => 'Contributed module three',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language.txt',
'package' => 'Other',
'version' => '8.x-1.0',
'project' => 'contrib_module_three',
'datestamp' => '1344471537',
'_info_file_ctime' => 1348767306,
),
'datestamp' => '1344471537',
'project_type' => 'module',
'project_status' => TRUE,
),
'locale_test' => array (
'name' => 'locale_test',
'info' => array (
'name' => 'Locale test',
'interface translation project' => 'locale_test',
'interface translation server pattern' => 'core/modules/locale/tests/test.%language.po',
'package' => 'Other',
'version' => NULL,
'project' => 'locale_test',
'_info_file_ctime' => 1348767306,
'datestamp' => 0,
),
'datestamp' => 0,
'project_type' => 'module',
'project_status' => TRUE,
),
'custom_module_one' => array (
'name' => 'custom_module_one',
'info' => array (
'name' => 'Custom module one',
'interface translation project' => 'custom_module_one',
'interface translation server pattern' => 'translations://custom_module_one.%language.po',
'package' => 'Other',
'version' => NULL,
'project' => 'custom_module_one',
'_info_file_ctime' => 1348767306,
'datestamp' => 0,
),
'datestamp' => 0,
'project_type' => 'module',
'project_status' => TRUE,
),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment