Commit 1e8ddda9 authored by Gábor Hojtsy's avatar Gábor Hojtsy

#169079 by yched: improve consistency of menu API and batch API by allowing...

#169079 by yched: improve consistency of menu API and batch API by allowing batch definition in non .module files, which opens the possibility of better performing batches (which are likely to reside in .admin.inc files generally)
 - apply this change in locale.module
 - improve batch API code documentation
parent 19322c37
......@@ -6,11 +6,12 @@
*/
/**
* State based dispatcher for batches.
* State-based dispatcher for the batch processing page.
*/
function _batch_page() {
$batch =& batch_get();
// Retrieve the current state of batch from db.
if (isset($_REQUEST['id']) && $data = db_result(db_query("SELECT batch FROM {batch} WHERE bid = %d AND token = '%s'", $_REQUEST['id'], drupal_get_token($_REQUEST['id'])))) {
$batch = unserialize($data);
}
......@@ -29,10 +30,12 @@ function _batch_page() {
break;
case 'do':
// JS-version AJAX callback.
_batch_do();
break;
case 'do_nojs':
// Non-JS progress page.
$output = _batch_progress_page_nojs();
break;
......@@ -65,8 +68,10 @@ function _batch_start() {
*/
function _batch_progress_page_js() {
$batch = batch_get();
$current_set = _batch_current_set();
// The first batch set gets to set the page title
// and the initialization and error messages.
$current_set = _batch_current_set();
drupal_set_title($current_set['title']);
drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE);
......@@ -86,7 +91,8 @@ function _batch_progress_page_js() {
}
/**
* Do one pass of execution and inform back the browser about progression.
* Do one pass of execution and inform back the browser about progression
* (used for JavaScript-mode only).
*/
function _batch_do() {
// HTTP POST required
......@@ -96,6 +102,7 @@ function _batch_do() {
return '';
}
// Perform actual processing.
list($percentage, $message) = _batch_process();
drupal_json(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message));
......@@ -113,7 +120,7 @@ function _batch_progress_page_nojs() {
$new_op = 'do_nojs';
if (!isset($batch['running'])) {
// This is the first page so return some output immediately.
// This is the first page so we return some output immediately.
$percentage = 0;
$message = $current_set['init_message'];
$batch['running'] = TRUE;
......@@ -135,12 +142,13 @@ function _batch_progress_page_nojs() {
list($fallback) = explode('<!--partial-->', $fallback);
print $fallback;
// Perform actual processing.
list($percentage, $message) = _batch_process($batch);
if ($percentage == 100) {
$new_op = 'finished';
}
// Processing successful; remove fallback.
// PHP did not die : remove the fallback output.
ob_end_clean();
}
......@@ -152,22 +160,32 @@ function _batch_progress_page_nojs() {
/**
* Advance batch processing for 1 second (or process the whole batch if it
* was not set for progressive execution).
* was not set for progressive execution - e.g forms submitted by drupal_execute).
*/
function _batch_process() {
$batch =& batch_get();
$current_set =& _batch_current_set();
$set_changed = TRUE;
if ($batch['progressive']) {
timer_start('batch_processing');
}
while (!$current_set['success']) {
// If this is the first time we iterate this batch set in the current
// request, we check if it requires an additional file for functions
// definitions.
if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) {
include_once($current_set['file']);
}
$finished = 1;
$task_message = '';
if ((list($function, $args) = reset($current_set['operations'])) && function_exists($function)) {
// Build the 'batch context' array, execute the function call, and retrieve the user message.
// Build the 'context' array, execute the function call,
// and retrieve the user message.
$batch_context = array('sandbox' => &$current_set['sandbox'], 'results' => &$current_set['results'], 'finished' => &$finished, 'message' => &$task_message);
// Process the current operation.
call_user_func_array($function, array_merge($args, array(&$batch_context)));
}
......@@ -180,9 +198,9 @@ function _batch_process() {
}
// If the batch set is completed, browse through the remaining sets,
// executing 'control sets' (stored submit handlers) along the way - this
// might in turn insert new batch sets. Stop when we find a set that
// actually has operations.
// executing 'control sets' (stored form submit handlers) along the way -
// this might in turn insert new batch sets.
// Stop when we find a set that actually has operations.
$set_changed = FALSE;
$old_set = $current_set;
while (empty($current_set['operations']) && ($current_set['success'] = TRUE) && _batch_next_set()) {
......@@ -192,7 +210,7 @@ function _batch_process() {
// At this point, either $current_set is a 'real' batch set (has operations),
// or all sets have been completed.
// Progressive mode : stop after 1 second.
// If we're in progressive mode, stop after 1 second.
if ($batch['progressive'] && timer_read('batch_processing') > 1000) {
break;
}
......@@ -203,7 +221,7 @@ function _batch_process() {
// Reporting 100% progress will cause the whole batch to be considered
// processed. If processing was paused right after moving to a new set,
// we have to use the info from the new one.
// we have to use the info from the new (unprocessed) one.
if ($set_changed && isset($current_set['operations'])) {
// Processing will continue with a fresh batch set.
$remaining = count($current_set['operations']);
......@@ -231,6 +249,7 @@ function _batch_process() {
return array($percentage, $message);
}
else {
// If we're not in progressive mode, the whole batch has been processed by now.
return _batch_finished();
}
......@@ -247,7 +266,7 @@ function &_batch_current_set() {
/**
* Move execution to the next batch set if any, executing the stored
* form _submit handlers along the way (thus possibly inserting
* additional batch sets)
* additional batch sets).
*/
function _batch_next_set() {
$batch =& batch_get();
......@@ -264,17 +283,23 @@ function _batch_next_set() {
}
/**
* End the batch :
* End the batch processing:
* Call the 'finished' callbacks to allow custom handling of results,
* and resolve page redirection.
*/
function _batch_finished() {
$batch =& batch_get();
// Execute the 'finished' callbacks.
foreach ($batch['sets'] as $key => $batch_set) {
if (isset($batch_set['finished']) && function_exists($batch_set['finished'])) {
$batch_set['finished']($batch_set['success'], $batch_set['results'], $batch_set['operations']);
// Execute the 'finished' callbacks for each batch set.
foreach($batch['sets'] as $key => $batch_set) {
if (isset($batch_set['finished'])) {
// Check if the set requires an additional file for functions definitions.
if (isset($batch_set['file']) && is_file($batch_set['file'])) {
include_once($batch_set['file']);
}
if (function_exists($batch_set['finished'])) {
$batch_set['finished']($batch_set['success'], $batch_set['results'], $batch_set['operations']);
}
}
}
......@@ -287,11 +312,13 @@ function _batch_finished() {
// Redirect if needed.
if ($_batch['progressive']) {
// Put back the 'destination' that was saved in batch_process().
if (isset($_batch['destination'])) {
$_REQUEST['destination'] = $_batch['destination'];
}
// Use $_batch['form_state']['redirect'], or $_batch['redirect'], or $_batch['source_page'].
// Use $_batch['form_state']['redirect'], or $_batch['redirect'],
// or $_batch['source_page'].
if (isset($_batch['form_state']['redirect'])) {
$redirect = $_batch['form_state']['redirect'];
}
......@@ -308,15 +335,17 @@ function _batch_finished() {
drupal_redirect_form($form, $redirect);
}
// We get here if $form['#redirect'] was FALSE, or if
// Save the final $form_state value, Redirect to the originating page.
// We get here if $form['#redirect'] was FALSE, or if the form is a
// multi-step form. We save the final $form_state value to be retrieved
// by drupal_get_form, and we redirect to the originating page.
$_SESSION['batch_form_state'] = $_batch['form_state'];
drupal_goto($_batch['source_page']);
}
}
/**
* Store the batch data for next request, or clear the table if the batch is finished.
* Shutdown function: store the batch data for next request,
* or clear the table if the batch is finished.
*/
function _batch_shutdown() {
if ($batch = batch_get()) {
......
......@@ -2129,11 +2129,17 @@ function form_clean_id($id = NULL) {
* 'progress_message': message displayed while processing the batch.
* Available placeholders are @current, @remaining, @total and @percent.
* Defaults to t('Remaining @remaining of @total.').
* 'error_message': message displayed if an error occurred while processing the batch.
* 'error_message': message displayed if an error occurred while processing
* the batch.
* Defaults to t('An error has occurred.').
* 'finished': the name of a function to be executed after the batch has completed.
* This should be used to perform any result massaging that may be needed,
* and possibly save data in $_SESSION for display after final page redirection.
* 'finished': the name of a function to be executed after the batch has
* completed. This should be used to perform any result massaging that
* may be needed, and possibly save data in $_SESSION for display after
* final page redirection.
* 'file': the path to the file containing the definitions of the
* 'operations' and 'finished' functions, for instance if they don't
* reside in the original '.module' file. The path should be relative to
* the base_path(), and thus should be built using drupal_get_path().
*
* Operations are added as new batch sets. Batch sets are used to ensure
* clean code independence, ensuring that several batches submitted by
......@@ -2191,7 +2197,7 @@ function batch_set($batch_definition) {
* Process the batch.
*
* Unless the batch has been marked with 'progressive' = FALSE, the function
* isses a drupal_goto and thus ends page execution.
* issues a drupal_goto and thus ends page execution.
*
* This function is not needed in form submit handlers; Form API takes care
* of batches that were set during form submission.
......
......@@ -2420,7 +2420,6 @@ function _locale_get_predefined_list() {
* A batch structure or FALSE if no files found.
*/
function locale_batch_by_language($langcode, $finished = '_locale_batch_installer_finished') {
// Collect all files to import for all enabled modules and themes.
$files = array();
$result = db_query("SELECT name, filename FROM {system} WHERE status = 1");
......@@ -2435,6 +2434,39 @@ function locale_batch_by_language($langcode, $finished = '_locale_batch_installe
return _locale_batch_build($files, $finished);
}
/**
* Prepare a batch to run when installing modules or enabling themes.
* This batch will import translations for the newly added components
* in all the languages already set up on the site.
*
* @param $components
* An array of component (theme and/or module) names to import
* translations for.
* @param $finished
* Optional finished callback for the batch.
*/
function locale_batch_by_component($components, $finished = '_locale_batch_system_finished') {
$files = array();
$languages = language_list('enabled');
unset($languages[1]['en']);
if (count($languages[1])) {
$language_list = join('|', array_keys($languages[1]));
// Collect all files to import for all $components.
$result = db_query("SELECT name, filename FROM {system} WHERE status = 1");
while ($component = db_fetch_object($result)) {
if (in_array($component->name, $components)) {
// Collect all files for this component in all enabled languages, named
// as $langcode.po or with names ending with $langcode.po. This allows
// for filenames like node-module.de.po to let translators use small
// files and be able to import in smaller chunks.
$files = array_merge($files, file_scan_directory(dirname($component->filename) .'/translations', '(^|\.)('. $language_list .')\.po$', array('.', '..', 'CVS'), 0, FALSE));
}
}
return _locale_batch_build($files, $finished);
}
return FALSE;
}
/**
* Build a locale batch from an array of files.
*
......@@ -2457,6 +2489,7 @@ function _locale_batch_build($files, $finished = NULL) {
'title' => $t('Importing interface translations'),
'init_message' => $t('Starting import'),
'error_message' => $t('Error importing interface translations'),
'file' => './includes/locale.inc',
);
if (isset($finished)) {
$batch['finished'] = $finished;
......@@ -2467,45 +2500,51 @@ function _locale_batch_build($files, $finished = NULL) {
}
/**
* Batch callback invoked when installer import processing finishes.
* Advance installer task to the finished screen.
* Perform interface translation import as a batch step.
*
* @param $filepath
* Path to a file to import.
* @param $results
* Contains a list of files imported.
*/
function _locale_batch_import($filepath, &$context) {
// The filename is either {langcode}.po or {prefix}.{langcode}.po, so
// we can extract the language code to use for the import from the end.
if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) {
$file = (object) array('filename' => basename($filepath), 'filepath' => $filepath);
_locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode[2]);
$context['results'][] = $filepath;
}
}
/**
* Finished callback of system page locale import batch.
* Inform the user of translation files imported.
*/
function _locale_batch_installer_finished($success, $results) {
variable_set('install_task', 'finished');
function _locale_batch_system_finished($success, $results) {
if ($success) {
drupal_set_message(format_plural(count($results), 'One translation file imported for the newly installed modules.', '@count translation files imported for the newly installed modules.'));
}
}
/**
* Prepare a batch to run when installing modules or enabling themes.
* This batch will import translations for the newly added components
* in all the languages already set up on the site.
*
* @param $components
* An array of component (theme and/or module) names to import
* translations for.
* @param $finished
* Optional finished callback for the batch.
* Finished callback of language addition locale import batch.
* Inform the user of translation files imported.
*/
function locale_batch_by_component($components, $finished = '_locale_batch_system_finished') {
$files = array();
$languages = language_list('enabled');
unset($languages[1]['en']);
if (count($languages[1])) {
$language_list = join('|', array_keys($languages[1]));
// Collect all files to import for all $components.
$result = db_query("SELECT name, filename FROM {system} WHERE status = 1");
while ($component = db_fetch_object($result)) {
if (in_array($component->name, $components)) {
// Collect all files for this component in all enabled languages, named
// as $langcode.po or with names ending with $langcode.po. This allows
// for filenames like node-module.de.po to let translators use small
// files and be able to import in smaller chunks.
$files = array_merge($files, file_scan_directory(dirname($component->filename) .'/translations', '(^|\.)('. $language_list .')\.po$', array('.', '..', 'CVS'), 0, FALSE));
}
}
return _locale_batch_build($files, $finished);
function _locale_batch_language_finished($success, $results) {
if ($success) {
drupal_set_message(format_plural(count($results), 'One translation file imported for the enabled modules.', '@count translation files imported for the enabled modules.'));
}
return FALSE;
}
/**
* Finished callback of installer locale import batch.
* Advance installer task to the finished screen.
*/
function _locale_batch_installer_finished($success, $results) {
variable_set('install_task', 'finished');
}
/**
* @} End of "locale-autoimport"
*/
......@@ -469,45 +469,6 @@ function locale_system_update($components) {
}
}
/**
* Finished callback of system page locale import batch.
* Inform the user of translation files imported.
*/
function _locale_batch_system_finished($success, $results) {
if ($success) {
drupal_set_message(format_plural(count($results), 'One translation file imported for the newly installed modules.', '@count translation files imported for the newly installed modules.'));
}
}
/**
* Finished callback of language addition locale import batch.
* Inform the user of translation files imported.
*/
function _locale_batch_language_finished($success, $results) {
if ($success) {
drupal_set_message(format_plural(count($results), 'One translation file imported for the enabled modules.', '@count translation files imported for the enabled modules.'));
}
}
/**
* Perform interface translation import as a batch step.
*
* @param $filepath
* Path to a file to import.
* @param $results
* Contains a list of files imported.
*/
function _locale_batch_import($filepath, &$context) {
include_once 'includes/locale.inc';
// The filename is either {langcode}.po or {prefix}.{langcode}.po, so
// we can extract the language code to use for the import from the end.
if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) {
$file = (object) array('filename' => basename($filepath), 'filepath' => $filepath);
_locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode[2]);
$context['results'][] = $filepath;
}
}
// ---------------------------------------------------------------------------------
// Language switcher block
......
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