2015-06-07 14:43:06 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Plugin Name: Batch operations
|
|
|
|
* Description: My version Drupal Batch API for WordPress.
|
2015-06-08 13:29:01 +00:00
|
|
|
* Version: 0.1.0
|
2015-06-07 14:43:06 +00:00
|
|
|
* Author: Igor V Belousov
|
|
|
|
* Author URI: http://belousovv.ru/
|
2015-08-16 01:58:20 +00:00
|
|
|
* Plugin URI: https://github.com/IgorVBelousov/batch_operations/wiki
|
|
|
|
* License: GPLv2 or later
|
2015-06-07 14:43:06 +00:00
|
|
|
*/
|
|
|
|
|
2015-08-16 01:58:20 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2015 Igor V Belousov
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2015-06-07 14:43:06 +00:00
|
|
|
// 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' );
|
|
|
|
|
2015-06-09 12:18:27 +00:00
|
|
|
// Add translations
|
2015-08-07 19:47:07 +00:00
|
|
|
add_action( 'init', 'batch_operations_load_translation_file' );
|
|
|
|
|
|
|
|
// Add huck for view messages
|
|
|
|
add_action( 'admin_notices', 'batch_operations_notice_view' );
|
2015-06-09 12:18:27 +00:00
|
|
|
|
2015-06-07 14:43:06 +00:00
|
|
|
global $batch_operations_version;
|
2015-06-09 12:18:27 +00:00
|
|
|
$batch_operations_version = '0.1.0';
|
2015-06-07 14:43:06 +00:00
|
|
|
|
2015-08-07 19:47:07 +00:00
|
|
|
/**
|
|
|
|
* Load translation file
|
|
|
|
*/
|
2015-06-09 12:18:27 +00:00
|
|
|
function batch_operations_load_translation_file() {
|
|
|
|
load_plugin_textdomain( 'batch-operations', false, '/batch_operations/languages' );
|
|
|
|
}
|
|
|
|
|
2015-06-07 14:43:06 +00:00
|
|
|
/**
|
|
|
|
* Add backend page without menu item
|
|
|
|
*/
|
|
|
|
function batch_operations_add_page() {
|
2015-06-10 09:51:19 +00:00
|
|
|
add_submenu_page( null, 'Batch operations', 'Batch operations', 'edit_posts', 'batch-operations', 'batch_operations_page_view' );
|
2015-06-07 14:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* View batch operations page
|
|
|
|
*/
|
|
|
|
function batch_operations_page_view() {
|
2015-06-09 13:11:49 +00:00
|
|
|
global $batch_operations_version;
|
2015-06-18 09:24:30 +00:00
|
|
|
|
2015-06-07 14:43:06 +00:00
|
|
|
wp_enqueue_script( 'jquery' );
|
2015-06-09 13:11:49 +00:00
|
|
|
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 );
|
2015-06-09 12:18:27 +00:00
|
|
|
|
2015-06-18 09:24:30 +00:00
|
|
|
$id = ( empty( $_REQUEST["id"] ) )? 0 : $_REQUEST["id"];
|
|
|
|
if ( ! preg_match( '/^[\d,A-F]*$/', $id ) || ( strlen( $id ) != 39 ) ) {
|
|
|
|
$id = 0;
|
|
|
|
}
|
2015-06-09 12:18:27 +00:00
|
|
|
|
2015-06-18 09:24:30 +00:00
|
|
|
if ( false === ( $current_array = get_transient( 'batch_' . $id ) ) ) {
|
|
|
|
$id = 0;
|
|
|
|
}
|
2015-06-09 12:18:27 +00:00
|
|
|
|
|
|
|
$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'] ;
|
|
|
|
}
|
|
|
|
|
2015-06-07 14:43:06 +00:00
|
|
|
?>
|
|
|
|
<script type="text/javascript">
|
2015-08-07 11:07:08 +00:00
|
|
|
var batch_id='<?php print $id; ?>',successful_page='<?php echo $current_array['successful_page']; ?>';
|
2015-06-07 14:43:06 +00:00
|
|
|
</script>
|
|
|
|
<div class="wrap">
|
2015-06-09 12:18:27 +00:00
|
|
|
<h2><?php echo $title ?></h2>
|
2015-06-07 14:43:06 +00:00
|
|
|
<div class="batch-progress">
|
|
|
|
<span style="width:0%;"></span>
|
|
|
|
</div>
|
2015-06-10 09:51:19 +00:00
|
|
|
<div class="batch-progress-message"><?php echo $init_message; ?></div><div class="batch-percent"></div>
|
|
|
|
<div class="batch-message"></div>
|
2015-06-07 14:43:06 +00:00
|
|
|
|
|
|
|
</div>
|
|
|
|
<?php
|
|
|
|
}
|
|
|
|
|
2015-08-07 11:10:05 +00:00
|
|
|
/**
|
|
|
|
* Execution operations from $batch['operations']
|
|
|
|
*/
|
2015-06-07 14:43:06 +00:00
|
|
|
function batch_operations_process () {
|
2015-06-18 09:24:30 +00:00
|
|
|
$id = ( empty( $_REQUEST["id"] ) )? 0 : $_REQUEST["id"];
|
|
|
|
if ( ! preg_match( '/^[\d,A-F]*$/', $id ) || ( strlen( $id ) != 39 ) ) {
|
2015-06-07 14:43:06 +00:00
|
|
|
wp_send_json( array( 'do' => 'finish' ) );
|
|
|
|
}
|
|
|
|
|
2015-06-18 09:24:30 +00:00
|
|
|
if ( false === ( $current_array = get_transient( 'batch_' . $id ) ) ) {
|
2015-06-07 14:43:06 +00:00
|
|
|
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'];
|
2015-08-07 22:10:01 +00:00
|
|
|
|
|
|
|
$current_array['message'] = '';
|
|
|
|
|
|
|
|
global $batch_operations_error_array;
|
|
|
|
set_error_handler( 'batch_operations_error_handler' , E_NOTICE | E_WARNING );
|
2015-06-07 14:43:06 +00:00
|
|
|
//run function
|
|
|
|
call_user_func_array( $current_array['operations'][0][0], $parameters_array );
|
2015-08-07 22:10:01 +00:00
|
|
|
//check error
|
|
|
|
if ( ! empty($batch_operations_error_array) ) {
|
|
|
|
$current_array['success'] = false;
|
|
|
|
$current_array['error_operations'][] = array(
|
|
|
|
'operation' => $current_array['operations'][0][0],
|
|
|
|
'parameters' => $parameters_array,
|
|
|
|
'error_array' => $batch_operations_error_array
|
|
|
|
);
|
|
|
|
$batch_operations_error_array = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
restore_error_handler();
|
2015-06-07 14:43:06 +00:00
|
|
|
|
|
|
|
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 ) );
|
2015-06-10 09:51:19 +00:00
|
|
|
$result['progress_message'] = str_replace(
|
|
|
|
array(
|
|
|
|
'%current%',
|
|
|
|
'%total%'
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$current_array['current'],
|
|
|
|
$current_array['count']
|
|
|
|
),
|
|
|
|
__( $current_array['progress_message'], 'batch-operations')
|
|
|
|
);
|
2015-06-07 14:43:06 +00:00
|
|
|
$result['message'] = $current_array['context']['message'];
|
|
|
|
|
|
|
|
if ( '' == $result['do'] ) {
|
2015-06-18 09:24:30 +00:00
|
|
|
set_transient( 'batch_' . $id, $current_array , WEEK_IN_SECONDS );
|
2015-06-07 14:43:06 +00:00
|
|
|
} else {
|
2015-06-18 09:24:30 +00:00
|
|
|
delete_transient( 'batch_' . $id );
|
2015-08-07 22:10:01 +00:00
|
|
|
if ( ! empty( $current_array['finished'] ) ) {
|
|
|
|
call_user_func_array( $current_array['finished'], array( $current_array['success'], $current_array['context']['results'], $current_array['error_operations'] ) );
|
|
|
|
}
|
2015-06-07 14:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wp_send_json( $result );
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:10:01 +00:00
|
|
|
global $batch_operations_error_array;
|
|
|
|
|
|
|
|
function batch_operations_error_handler ($errno, $errmsg, $filename, $linenum, $vars) {
|
|
|
|
global $batch_operations_error_array;
|
|
|
|
$batch_operations_error_array=array($errno, $errmsg, $filename, $linenum, $vars);
|
|
|
|
|
|
|
|
}
|
2015-06-07 14:43:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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>
|
2015-08-07 19:47:07 +00:00
|
|
|
* <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:
|
2015-06-10 09:51:19 +00:00
|
|
|
* <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.').
|
2015-06-07 14:43:06 +00:00
|
|
|
* <li> progress_message: Message displayed while processing the batch. Available placeholders are %current% and %total%.
|
2015-06-10 09:51:19 +00:00
|
|
|
* <li> error_message: Message displayed if an error occurred while processing the batch. Defaults to __('An error has occurred.').
|
2015-06-07 14:43:06 +00:00
|
|
|
* <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.
|
2015-08-07 19:47:07 +00:00
|
|
|
* </ul>
|
2015-06-07 14:43:06 +00:00
|
|
|
*
|
|
|
|
* Sample callback_batch_operation():
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* function my_function_1($id, $text, &$context) {
|
|
|
|
* $context['results'][] = $text . $id;
|
|
|
|
* $context['message'] = 'Text + id ='.$text . $id;
|
|
|
|
* }
|
2015-08-07 19:47:07 +00:00
|
|
|
* </pre>
|
2015-06-07 14:43:06 +00:00
|
|
|
*
|
|
|
|
* 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 :
|
2015-08-07 19:47:07 +00:00
|
|
|
*
|
2015-06-07 14:43:06 +00:00
|
|
|
* 'results' (read / write): The array of results gathered so far by
|
|
|
|
* the batch processing, for the current operation to append its own.
|
2015-08-07 19:47:07 +00:00
|
|
|
*
|
2015-06-07 14:43:06 +00:00
|
|
|
* 'message' (write): A text message displayed in the progress page.
|
|
|
|
* The following keys allow for multi-step operations :
|
2015-08-07 19:47:07 +00:00
|
|
|
*
|
2015-06-07 14:43:06 +00:00
|
|
|
* '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.
|
2015-08-07 19:47:07 +00:00
|
|
|
*
|
2015-06-07 14:43:06 +00:00
|
|
|
* '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.
|
|
|
|
*
|
|
|
|
* @param array $batch_arr array operations and more
|
2015-08-07 11:07:08 +00:00
|
|
|
* @param string $redirect Url to redirect to when the batch has finished processing
|
2015-06-07 14:43:06 +00:00
|
|
|
*/
|
2015-08-07 11:07:08 +00:00
|
|
|
function batch_operations_start( $batch_arr, $redirect = NULL )
|
2015-06-07 14:43:06 +00:00
|
|
|
{
|
2015-06-18 09:24:30 +00:00
|
|
|
$id = rand( 100, 999 ) . strtoupper( md5( date( 'YMDBs' ) ) ) . rand( 1000, 9999 );
|
2015-06-07 14:43:06 +00:00
|
|
|
|
|
|
|
$batch_arr['context'] = array(
|
|
|
|
'message' => '',
|
|
|
|
'sandbox' => array(),
|
|
|
|
'finished' => true,
|
|
|
|
'results' => array()
|
|
|
|
);
|
|
|
|
$batch_arr['count'] = count( $batch_arr['operations'] );
|
|
|
|
$batch_arr['current'] = 0;
|
|
|
|
|
2015-08-07 11:07:08 +00:00
|
|
|
if ( empty( $redirect ) ) {
|
|
|
|
$batch_arr['successful_page'] = get_admin_url();
|
|
|
|
} else {
|
|
|
|
$batch_arr['successful_page'] = $redirect;
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:10:01 +00:00
|
|
|
$batch_arr['success'] = true;
|
|
|
|
$batch_arr['error_operations'] = array();
|
|
|
|
|
2015-06-10 09:51:19 +00:00
|
|
|
if ( empty( $batch_arr['progress_message'] ) ) {
|
2015-06-18 09:24:30 +00:00
|
|
|
$batch_arr['progress_message'] = __( 'Completed %current% of %total%.' );
|
2015-06-10 09:51:19 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 09:24:30 +00:00
|
|
|
set_transient( 'batch_' . $id, $batch_arr , WEEK_IN_SECONDS );
|
|
|
|
$location = get_admin_url( null, 'tools.php' ) . "?page=batch-operations&id=" . $id;
|
2015-06-07 14:43:06 +00:00
|
|
|
|
|
|
|
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);
|
2015-08-07 19:47:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set message for next view for current user
|
|
|
|
*
|
|
|
|
* @param string $message message text
|
|
|
|
* @param string $type type of message: info, success, warning, error. default - info
|
|
|
|
*/
|
|
|
|
function batch_operations_notice( $message, $type = 'info' ) {
|
|
|
|
|
|
|
|
if ( false === ( $messages = get_transient( 'batch_operations_notice' ) ) ) {
|
|
|
|
$messages = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$messages[ get_current_user_id() ][] = array(
|
|
|
|
'message' => $message,
|
|
|
|
'type' => $type
|
|
|
|
);
|
|
|
|
|
|
|
|
set_transient( 'batch_operations_notice', $messages );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* View message
|
|
|
|
*/
|
|
|
|
function batch_operations_notice_view() {
|
|
|
|
if ( $messages = get_transient( 'batch_operations_notice' ) ) {
|
|
|
|
$current_uid = get_current_user_id();
|
|
|
|
if ( ! empty( $messages[ $current_uid ] ) ) {
|
|
|
|
// if version < 4.1.1 then add css
|
|
|
|
global $wp_version;
|
|
|
|
|
|
|
|
$version = explode( '.', $wp_version );
|
|
|
|
|
|
|
|
if ( 4 > $version[0] || ( ( 4 == $version[0] && 1 >= $version[1] ) && 2 == count( $version ) ) ) {
|
|
|
|
global $batch_operations_version;
|
|
|
|
wp_enqueue_style( 'batch_operations_script', plugin_dir_url('') . 'batch_operations/css/notice.css', array(), $batch_operations_version );
|
|
|
|
}
|
|
|
|
|
|
|
|
// print message
|
|
|
|
foreach ( $messages[ $current_uid ] as $key => $value ) {
|
|
|
|
echo '<div class="notice notice-' . $value['type'] . '"><p>' . $value['message'] . '</p></div>';
|
|
|
|
}
|
|
|
|
|
|
|
|
//delete messages
|
|
|
|
unset( $messages[ $current_uid ] );
|
|
|
|
|
|
|
|
if ( empty( $messages ) ) {
|
|
|
|
delete_transient( 'batch_operations_notice' );
|
|
|
|
} else {
|
|
|
|
set_transient( 'batch_operations_notice', $messages );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-05 19:26:11 +00:00
|
|
|
}
|