From 9896745775e1ca5d942cb4238cbafead324e01c9 Mon Sep 17 00:00:00 2001 From: Philip Downer Date: Thu, 15 Nov 2018 14:25:58 -0600 Subject: [PATCH] BB-3277: Fixes bug where multiple batch jobs would only trigger first job. When multiple batch jobs exist, the AJAX controller was only configured to target the very first batch job. We now pass the requested batch name into each AJAX request ensuring that the correct job is targeted and run. --- README.md | 49 ++++++++++++++--------------- composer.json | 2 +- js/src/ramsey-batch.js | 20 +++++++----- src/BatchJob.php | 3 -- src/Controllers/BatchController.php | 31 +++++++++++++++++- wp-ramsey-batch.php | 7 ++++- 6 files changed, 73 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 8d2e81b..0dc36b4 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,6 @@ if( is_admin() && class_exists('RamseySolutions\RamseyBatch\Controllers\BatchCon ### Create a batch job class and register the job Begin by creating a class for your batch job with a basic `__construct` method. Your class _must_ extend the `\RamseySolutions\RamseyBatch\BatchJob` class. -The filter `ramsey-batch-jobs` allows you to hook a class method to register your batch job into the UI. - ```php 'My Batch Job Name', - 'description' => 'Description of what the batch job does.', - 'lastRunDate' => $this->getLastRunDate() - ]; - - return $jobs; + //Add any logic specific to the class after calling the parent constructor } } ``` -There are a few methods that your class must provide. The general flow of plugin is: - -1. **run()** method is called - This should update the last run date and compile your list of items to be batch processed one-by-one. It should output a JSON array of items if successful. -1. **runItem()** method is called for each item in your collection. Each item from the JSON array is passed to the method individually for processing. If the item is processed successfully, it should [output a successful response](https://codex.wordpress.org/Function_Reference/wp_send_json_success) via JSON. - +The general flow of plugin is: +1. The user clicks the "Run Job" button in the UI. +1. The `run()` method of your class is called. This should update the last run date and compile your list of items to be batch processed one-by-one. It should output a JSON array of items if successful. +1. Once all of the batch items have been collected, the `runItem()` method of your class is called for each item in the batch. Each item from the JSON array is passed to the method individually for processing. If the item is processed successfully, it should [output a successful response](https://codex.wordpress.org/Function_Reference/wp_send_json_success) via JSON. -The required methods are stubbed out below: +Because `\RamseySolutions\RamseyBatch\BatchJob` is an abstract class, there are a few methods that your extending class must define. These required methods are stubbed out below: ```php +/** + * Register the batch job + * @param array $jobs Array of batch job information + * @return array + */ +public function registerBatchJob(array $jobs) { + $jobs[__CLASS__] = [ + 'name' => 'My Batch Job Name', + 'description' => 'Description of what the batch job does.', + 'lastRunDate' => $this->getLastRunDate() + ]; + + return $jobs; +} + /** * Set the batch job items * @param array $items Array of values to process over. These values will be passed into your processing function one-by-one via AJAX. Use the 'run' method to compile @@ -155,6 +152,8 @@ public function runItem() { $postId = $_REQUEST['item']; //Setup an empty array to hold our response. As batch jobs should only be run in the WP admin by authenticated users, you can use this response to output information in the browser console. + //You can pass any number of elements in the response array, however, the response array expects at least a 'reason' key to provide some browser output. + //You can add a 'type' key with a value of 'warn' to output the message in the browser using console.warn() rather than console.log() $response = []; //We're only expecting post ID's, so let's do a quick sanity check. If this fails in real life, you should probably log it out for further analysis. @@ -204,8 +203,6 @@ class MyBatchJob extends \RamseySolutions\RamseyBatch\BatchJob { */ public function __construct() { parent::__construct(); - - add_filter('ramsey-batch-jobs', [$this, 'registerBatchJob']); } /** diff --git a/composer.json b/composer.json index 0140c61..6d525e8 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Perform batch jobs inside of WordPress.", "type": "wordpress-plugin", "license": "GPL v3", - "version": "1.0.1", + "version": "1.1.0", "authors": [ { "name": "Philip Downer", diff --git a/js/src/ramsey-batch.js b/js/src/ramsey-batch.js index 840c8ad..42e8941 100644 --- a/js/src/ramsey-batch.js +++ b/js/src/ramsey-batch.js @@ -1,6 +1,7 @@ jQuery( document ).ready( ( $ ) => { const ramseyBatch = {}; ramseyBatch.items = []; + ramseyBatch.batchName; ramseyBatch.totalItems = ramseyBatch.items.length; ramseyBatch.currentItem = 0; ramseyBatch.itemsComplete = 0; @@ -61,18 +62,23 @@ jQuery( document ).ready( ( $ ) => { method: 'POST', data: { action: 'ramsey-batch-item', - item + item, + batchName: ramseyBatch.batchName }, success( response ) { if ( ! response.success ) { - console.log( response.data.reason, response ); + console.error( response.data.reason, response ); return reject(); } ramseyBatch.itemsComplete++; - console.log( response.data.reason, response ); + if ( 'warn' == response.data.type ) { + console.warn( response.data.reason, response ); + } else { + console.log( response.data.reason, response ); + } return resolve(); } @@ -95,8 +101,8 @@ jQuery( document ).ready( ( $ ) => { function startBatch( trigger ) { return new Promise( ( resolve, reject ) => { currentButton = $( trigger ); - const batchName = currentButton.data( 'batchName' ); - const batchNameClean = batchName.replace( new RegExp( '\\\\', 'g' ), '' ); + ramseyBatch.batchName = currentButton.data( 'batchName' ); + const batchNameClean = ramseyBatch.batchName.replace( new RegExp( '\\\\', 'g' ), '' ); progressMeter = $( `tr.progressMeter[data-batch-name="${batchNameClean}"]` ); progressBar = progressMeter.find( '.meter' ); @@ -110,14 +116,14 @@ jQuery( document ).ready( ( $ ) => { method: 'POST', data: { action: 'ramsey-batch', - batchName + batchName: ramseyBatch.batchName }, success( response ) { if ( ! response.success ) { return reject( new Error( response.data.reason ) ); } - console.log( 'Starting batch!', response.data.items ); + console.log( 'Starting batch!', batchNameClean, response.data.items ); updateItems( response.data.items ); diff --git a/src/BatchJob.php b/src/BatchJob.php index 2bab9d1..87d9a73 100644 --- a/src/BatchJob.php +++ b/src/BatchJob.php @@ -38,9 +38,6 @@ abstract public function registerBatchJob(array $jobs); public function __construct() { $this->items = []; $this->lastRunTimes = get_option(RB_PLUGIN_SLUG . '_batch-run-timestamps', []); - - add_action('wp_ajax_' . RB_PLUGIN_SLUG, [$this, 'run'] ); - add_action('wp_ajax_' . RB_PLUGIN_SLUG . '-item', [$this, 'runItem']); } /** diff --git a/src/Controllers/BatchController.php b/src/Controllers/BatchController.php index 4d4566c..62d4288 100644 --- a/src/Controllers/BatchController.php +++ b/src/Controllers/BatchController.php @@ -19,6 +19,31 @@ public function __construct() { $this->jobs = apply_filters(RB_PLUGIN_SLUG . '-jobs', [], $this); } + public static function runJob() { + if( !wp_doing_ajax() || $_REQUEST['action'] != RB_PLUGIN_SLUG ) return; + + $currentBatchName = stripslashes($_REQUEST['batchName']); + $batchObj = $GLOBALS[RB_PLUGIN_SLUG][$currentBatchName]; + + if( !$batchObj ) { + wp_send_json_error(['reason' => "Attempted to run job but global object not found - {$currentBatchName}."]); + } + + $batchObj->run(); + } + + public static function runJobItem() { + if( !wp_doing_ajax() || $_REQUEST['action'] != 'ramsey-batch-item' ) return; + + $currentBatchName = stripslashes($_REQUEST['batchName']); + $batchObj = $GLOBALS[RB_PLUGIN_SLUG][$currentBatchName]; + if( !$batchObj ) { + wp_send_json_error(['reason' => "Attempted to run job item but global object not found - {$currentBatchName}."]); + } + + $batchObj->runItem(); + } + /** * Enqueue JS scripts * @return void @@ -34,7 +59,11 @@ public static function enqueueScripts() { public static function register(string $name) { if( !class_exists($name) ) return; - new $name; + + $obj = new $name; + add_filter('ramsey-batch-jobs', [$obj, 'registerBatchJob']); + + $GLOBALS[RB_PLUGIN_SLUG][$name] = $obj; } public function getJobs() { diff --git a/wp-ramsey-batch.php b/wp-ramsey-batch.php index 2512f36..77b97f1 100644 --- a/wp-ramsey-batch.php +++ b/wp-ramsey-batch.php @@ -3,7 +3,7 @@ Plugin Name: Ramsey Batch Plugin URI: https://www.daveramsey.com Description: Provides a framework and UI for running batch jobs inside of WordPress. -Version: 1.0.1 +Version: 1.1.0 Author: Philip Downer Author URI: https://philipdowner.com License: GPLv3 @@ -36,6 +36,11 @@ function ramseyBatchDisplayAdminPage() { $page->display(); } +add_action('admin_init', function() { + add_action('wp_ajax_' . RB_PLUGIN_SLUG, 'RamseySolutions\RamseyBatch\Controllers\BatchController::runJob' ); + add_action('wp_ajax_' . RB_PLUGIN_SLUG . '-item', 'RamseySolutions\RamseyBatch\Controllers\BatchController::runJobItem'); +}); + add_action('admin_enqueue_scripts', function() { BatchController::enqueueScripts(); }); \ No newline at end of file