<?php /** * Plugin Name: Batch operations * Description: My version Drupal Batch API for WordPress. * Version: 0.1.0 * Author: Igor V Belousov * Author URI: http://belousovv.ru/ */ // Add backend page without menu item add_action( 'admin_menu', 'batch_operations_add_page' ); // Add JSON query for run operation add_action( 'wp_ajax_batch_operations', 'batch_operations_process' ); // Add translations add_action( 'init', 'batch_operations_load_translation_file'); global $batch_operations_version; $batch_operations_version = '0.1.0'; function batch_operations_load_translation_file() { load_plugin_textdomain( 'batch-operations', false, '/batch_operations/languages' ); } /** * Add backend page without menu item */ function batch_operations_add_page() { add_submenu_page( null, 'Batch operations', 'Batch operations', 'edit_posts', 'batch-operations', 'batch_operations_page_view' ); } /** * View batch operations page */ function batch_operations_page_view() { global $batch_operations_version; wp_enqueue_script( 'jquery' ); wp_enqueue_script( 'batch_operations_script', plugin_dir_url('') . 'batch_operations/js/batch.min.js', array(), $batch_operations_version ); wp_enqueue_style( 'batch_operations_script', plugin_dir_url('') . 'batch_operations/css/batch.css', array(), $batch_operations_version ); $id = ( empty( $_REQUEST["id"] ) )? 0 : $_REQUEST["id"]; if ( ! preg_match( '/^[\d,A-F]*$/', $id ) || ( strlen( $id ) != 39 ) ) { $id = 0; } if ( false === ( $current_array = get_transient( 'batch_' . $id ) ) ) { $id = 0; } $title = __( 'Processing', 'batch-operations' ); $init_message = ''; if ( ! empty( $current_array ) ) { $title = ( empty ( $current_array['title'] ) ) ? $title : $current_array['title'] ; $init_message = ( empty ( $current_array['init_message'] ) ) ? __( 'Initializing.', 'batch-operations' ) : $current_array['init_message'] ; } ?> <script type="text/javascript"> var batch_id='<?php print $id; ?>',successful_page='<?php echo $current_array['successful_page']; ?>'; </script> <div class="wrap"> <h2><?php echo $title ?></h2> <div class="batch-progress"> <span style="width:0%;"></span> </div> <div class="batch-progress-message"><?php echo $init_message; ?></div><div class="batch-percent"></div> <div class="batch-message"></div> </div> <?php } function batch_operations_process () { $id = ( empty( $_REQUEST["id"] ) )? 0 : $_REQUEST["id"]; if ( ! preg_match( '/^[\d,A-F]*$/', $id ) || ( strlen( $id ) != 39 ) ) { wp_send_json( array( 'do' => 'finish' ) ); } if ( false === ( $current_array = get_transient( 'batch_' . $id ) ) ) { wp_send_json( array( 'do' => 'finish' ) ); } $result['do'] = ''; $start = time() + 1; $flag = true; while ($flag) { //make array of parameters for function $parameters_array = array(); if ( isset( $current_array['operations'][0][1] ) ) { $parameters_array = $current_array['operations'][0][1]; } $parameters_array[] = &$current_array['context']; //run function call_user_func_array( $current_array['operations'][0][0], $parameters_array ); if ( true == $current_array['context']['finished'] ) { $current_array['context']['sandbox'] = array(); array_splice( $current_array['operations'], 0, 1 ); $current_array['current']++; } if ( time() > $start || 0 == count( $current_array['operations'] ) ) { $flag=false; } } if ( 0 == count( $current_array['operations'] ) ) { $result['do']='finish'; } $result['percent'] = round( $current_array['current'] / ($current_array['count'] / 100 ) ); $result['progress_message'] = str_replace( array( '%current%', '%total%' ), array( $current_array['current'], $current_array['count'] ), __( $current_array['progress_message'], 'batch-operations') ); $result['message'] = $current_array['context']['message']; if ( '' == $result['do'] ) { set_transient( 'batch_' . $id, $current_array , WEEK_IN_SECONDS ); } else { delete_transient( 'batch_' . $id ); } wp_send_json( $result ); } /** * Start batch operations * * <pre> * $batch = array( * 'title' => t('Exporting'), * 'operations' => array( * array('my_function_1', array(123, 'qwe')), * array('my_function_2', array()), * ), * 'finished' => 'my_finished_callback', * ); * * batch_operations_start($batch); * </pre> * * <ul> * <li> operations: (required) Array of operations to be performed, where each item is an array consisting of the name of an implementation of callback_batch_operation() and an array of parameter. Example: * <li> title: A safe, translated string to use as the title for the progress page. Defaults to __('Processing'). * <li> init_message: Message displayed while the processing is initialized. Defaults to __('Initializing.'). * <li> progress_message: Message displayed while processing the batch. Available placeholders are %current% and %total%. * <li> error_message: Message displayed if an error occurred while processing the batch. Defaults to __('An error has occurred.'). * <li> finished: Name of an implementation of callback_batch_finished(). This is 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. * * Sample callback_batch_operation(): * * <pre> * function my_function_1($id, $text, &$context) { * $context['results'][] = $text . $id; * $context['message'] = 'Text + id ='.$text . $id; * } * * The $context array gathers batch context information about the execution (read), * as well as 'return values' for the current operation (write) * The following keys are provided : * 'results' (read / write): The array of results gathered so far by * the batch processing, for the current operation to append its own. * 'message' (write): A text message displayed in the progress page. * The following keys allow for multi-step operations : * 'sandbox' (read / write): An array that can be freely used to * store persistent data between iterations. It is recommended to * use this instead of $_SESSION, which is unsafe if the user * continues browsing in a separate window while the batch is processing. * 'finished' (write): A float number between 0 and 1 informing * the processing engine of the completion level for the operation. * 1 (or no value explicitly set) means the operation is finished * and the batch processing can continue to the next operation. * </pre> * * @param array $batch_arr array operations and more * @param string $redirect Url to redirect to when the batch has finished processing */ function batch_operations_start( $batch_arr, $redirect = NULL ) { $id = rand( 100, 999 ) . strtoupper( md5( date( 'YMDBs' ) ) ) . rand( 1000, 9999 ); $batch_arr['context'] = array( 'message' => '', 'sandbox' => array(), 'finished' => true, 'results' => array() ); $batch_arr['count'] = count( $batch_arr['operations'] ); $batch_arr['current'] = 0; if ( empty( $redirect ) ) { $batch_arr['successful_page'] = get_admin_url(); } else { $batch_arr['successful_page'] = $redirect; } if ( empty( $batch_arr['progress_message'] ) ) { $batch_arr['progress_message'] = __( 'Completed %current% of %total%.' ); } set_transient( 'batch_' . $id, $batch_arr , WEEK_IN_SECONDS ); $location = get_admin_url( null, 'tools.php' ) . "?page=batch-operations&id=" . $id; if ( ! headers_sent() ) { wp_redirect( $location ); } else { // if header is set then runs this hack echo '<script type="text/javascript">'; echo 'document.location.href="' . $location .'";'; echo '</script>'; echo '<noscript>'; echo '<meta http-equiv="refresh" content="0;url=' . $location . '" />'; echo '</noscript>'; } exit(0); }