WordPress bulk actions in plugins, starterkit example
Here is simple starterkit example of making bulk actions in wordpress via ajax
<?php
/*
Plugin Name: Bulk
Plugin URI: http://mac-blog.org.ua/
Description: Test for bulk actions via ajax.
Author: Marchenko Alexandr
Version: None
Author URI: http://mac-blog.org.ua/
*/
//add example admin page, and register hook to enqueue needed scripts and styles
function bulk_admin_menu() {
$page = add_menu_page('Bulk Page Title', 'Bulk Menu Title', 'manage_options', 'bulk_page_slug', 'bulk_page_handler');
add_action('admin_print_scripts-' . $page, 'bulk_admin_print_scripts');
}
add_action('admin_menu', 'bulk_admin_menu');
//register and enqueue needed scripts and styles here
function bulk_admin_print_scripts() {
wp_enqueue_script('jquery');
}
//page handler is simple function that renders page
function bulk_page_handler() {
//here is our nonce, we will use it in bulk_process function to check that request is maded from this page
$_ajax_nonce = wp_create_nonce(basename(__FILE__));
//here is page actualy, u can do somthing like: include_once 'bulk_page.php'; and move all this code to bulk_page.php file
//include_once 'bulk_page.php';
?>
<div class="wrap">
<!--
Here is our simple form, it just contains one input to configure number of actions
but form may be as big as u want
-->
<div id="bulk_form">
<input type="number" name="bulk_count" id="bulk_count" placeholder="number of iterations..." required>
<input id="bulk_submit" type="submit" value="Submit">
</div>
<!--
#bulk_process will be used to show process metrics, just for example
-->
<div id="bulk_process" style="display:none">
<table cellspacing="0" cellpadding="5" border="1">
<tr>
<th>success</th>
<th>total</th>
<th>success</th>
<th>fail</th>
<th>current</th>
<th>remainded</th>
<th>percentage</th>
<th>start</th>
<th>elapsed</th>
<th>remainded</th>
<th>per item</th>
<th>message</th>
</tr>
</table>
</div>
</div>
<script>
jQuery(document).ready(function($) {
//some process metrics variables, might be usefull for creating nice interface
var bulk_actions_total = 0;
var bulk_actions_success = 0;
var bulk_actions_fail = 0;
var bulk_actions_current = 0;
var bulk_actions_remainded = 0;
var bulk_actions_complete_percentage = 0;
var bulk_actions_time_start = 0;
var bulk_actions_time_elapsed = 0;
var bulk_actions_time_remainded = 0;
var bulk_actions_time_per_item = 0;
//This function called as many times as you input
//it just call php bulk_process() function via ajax
//recursively until all actions will be done
function bulk_process() {
//recalculate most important metric values
bulk_actions_current = bulk_actions_current + 1;
bulk_actions_remainded = bulk_actions_remainded - 1;
//check if we are done
if(bulk_actions_remainded < 0) return bulk_finish();
//make another request
$.ajax({
type: 'POST',
dataType: 'json',
url: ajaxurl, //this is Wordpress variable
data: {
action: 'bulk_process', //action name, add_action('wp_ajax_[bulk_process]', 'bulk_process');
_ajax_nonce: '<?php echo $_ajax_nonce?>', //our nonce, defined in bulk_page_handler function
bulk_nonce: $('bulk_nonce').val(), //and there can be as much data as u want, it will be accessible in php via $_POST
index: bulk_actions_current,
//...
bulk_count: $('bulk_count').val()
},
success: function(response) {
//in both cases of success and failure we call bulk_update function
//to notify user about changes, also we are going to recursion
bulk_update(response, response.success); //notice, response.success coming from php, and can be false
bulk_process();
},
error: function(response) {
bulk_update(response, false); //there is some network error or something else
bulk_process();
}
});
}
//this function call after every action is done
function bulk_update(response, success) {
calculateMetrics(success);
$('#bulk_process table').append('<tr><td>' + success + '</td><td>' + bulk_actions_total + '</td><td>' + bulk_actions_success + '</td><td>' + bulk_actions_fail + '</td><td>' + bulk_actions_current + '</td><td>' + bulk_actions_remainded + '</td><td>' + bulk_actions_complete_percentage + '%</td><td>' + bulk_actions_time_start + '</td><td>' + humanReadableTime(bulk_actions_time_elapsed) + '</td><td>' + humanReadableTime(bulk_actions_time_remainded) + '</td><td>' + humanReadableTime(bulk_actions_time_per_item) + '</td><td>' + response.message + '</td></tr>');
console.log(
response,
success,
bulk_actions_total,
bulk_actions_success,
bulk_actions_fail,
bulk_actions_current,
bulk_actions_remainded,
bulk_actions_complete_percentage,
bulk_actions_time_start,
bulk_actions_time_elapsed,
bulk_actions_time_remainded,
bulk_actions_time_per_item
);
}
//we will call this function at the end of our bulk actions
function bulk_finish() {
console.log('FINISH');
}
//here we are attaching to submit button click event and starting bulk process
$('#bulk_submit').click(function(event){
event.preventDefault();
//get and validate number of actions
var count = parseInt($('#bulk_count').val());
if(!count) {
alert('Please enter number');
return false;
}
//define start metric values
bulk_actions_total = count;
bulk_actions_remainded = count;
bulk_actions_time_start = new Date();
//start bull process
bulk_process();
$('#bulk_form').hide();
$('#bulk_process').show();
return false;
});
//helper function, must be called every time to get apropriate metrics
function calculateMetrics(success) {
if(success) {
bulk_actions_success = bulk_actions_success + 1;
} else {
bulk_actions_fail = bulk_actions_fail + 1;
}
bulk_actions_complete_percentage = Math.round(bulk_actions_current / (bulk_actions_total / 100));
bulk_actions_time_elapsed = (new Date()).getTime() - bulk_actions_time_start.getTime();
bulk_actions_time_elapsed = bulk_actions_time_elapsed / 1000;
bulk_actions_time_per_item = bulk_actions_time_elapsed / bulk_actions_current;
bulk_actions_time_remainded = bulk_actions_remainded * bulk_actions_time_per_item;
}
//helper function to display nice time text
function humanReadableTime(seconds) {
// 1, 2, 5 - period label
var periods = [
['секунда', 'секурнды', 'секунд'], //1 second, 2 seconds, 5 seconds
['минута', 'минуты', 'минут'], //min
['час', 'часа', 'часов'], //hour
['день', 'дня', 'дней'], //day
['неделя', 'недели', 'недель'], //week
['месяц', 'месяца', 'месяцев'], //month
['год', 'года', 'лет'], //year
];
var lengths = [60, 60, 24, 7, 4.35, 12];
for(var i = 0; seconds >= lengths[i]; i++) {
seconds = seconds / lengths[i];
}
seconds = Math.round(seconds);
var cases = [2, 0, 1, 1, 1, 2];
var text = periods[i][(seconds%100>4 && seconds%100<20)? 2: cases[Math.min(seconds%10, 5)]];
return seconds + ' ' + text;
}
});
</script>
<?php
}
// our ajax handler, all data accessible via $_POST
function bulk_process() {
//check correct nonce (setted in bulk_page_handler function)
check_ajax_referer(basename(__FILE__));
//try to turn off error reporting
@error_reporting(0);
//we are going to retrieve json response
header('Content-type: application/json');
try {
//TODO: do something heavy weight here
//throw exceptions on error
if($_POST['index'] == 2) throw new Exception('Wrong id');
//fill $response variable at the end
sleep(rand(1, 3));
$response = array(
'success' => true,
'message' => $_POST['index'] . ' - done'
);
} catch(Exception $ex) {
$response = array(
'success' => false,
'message' => $ex->getMessage()
);
}
//echoing response
echo json_encode($response);
//do not forget about this die()
die();
}
//notice how we registering our handler
//wp_ajax_[bulk_process] - where [bulk_process] will be action name in javascript
add_action('wp_ajax_bulk_process', 'bulk_process');
Some links:
http://codex.wordpress.org/Function_Reference/add_menu_page
http://codex.wordpress.org/AJAX_in_Plugins
http://codex.wordpress.org/Plugin_API/Action_Reference/wp_ajax_(action)
http://codex.wordpress.org/Function_Reference/wp_enqueue_script