← All changes
|
addons/automator/includes/actions/delay-actions.php
+240
-240
8.7.9
→
8.8.2
View file →
| @@ -1,240 +1,240 @@ | ||
| 1 | -<?php | |
| 2 | -/** | |
| 3 | - * Delay / Wait Action | |
| 4 | - * | |
| 5 | - * Pauses workflow execution for a configurable duration using WordPress Cron | |
| 6 | - * (wp_schedule_single_event). The remaining nodes and trigger data are | |
| 7 | - * persisted to the wpbot_automator_scheduled_tasks DB table so they can be | |
| 8 | - * resumed accurately after the delay fires. | |
| 9 | - * | |
| 10 | - * @package WPbot_Automator | |
| 11 | - */ | |
| 12 | - | |
| 13 | -namespace WPbot_Automator\Actions; | |
| 14 | - | |
| 15 | -use WPbot_Automator\Core\Database; | |
| 16 | -use WPbot_Automator\Engine\Workflow_Runner; | |
| 17 | - | |
| 18 | -if ( ! defined( 'ABSPATH' ) ) { | |
| 19 | - exit; | |
| 20 | -} | |
| 21 | - | |
| 22 | -/** | |
| 23 | - * Class Delay_Actions | |
| 24 | - */ | |
| 25 | -class Delay_Actions extends Action { | |
| 26 | - | |
| 27 | - /** | |
| 28 | - * WP Cron hook name used to resume deferred workflows. | |
| 29 | - */ | |
| 30 | - const CRON_HOOK = 'wpbot_automator_resume_delayed_workflow'; | |
| 31 | - | |
| 32 | - /** | |
| 33 | - * Constructor | |
| 34 | - */ | |
| 35 | - public function __construct() { | |
| 36 | - $this->id = 'delay'; | |
| 37 | - $this->group = __( 'Flow Control', 'wpbot-automator' ); | |
| 38 | - | |
| 39 | - // Register the cron callback once per request. | |
| 40 | - add_action( self::CRON_HOOK, array( $this, 'resume_workflow' ), 10, 1 ); | |
| 41 | - } | |
| 42 | - | |
| 43 | - /** | |
| 44 | - * Execute the action. | |
| 45 | - * | |
| 46 | - * When the workflow runner hits this node it will call execute() with | |
| 47 | - * extra context that the runner passes via $action_data['__context']. | |
| 48 | - * | |
| 49 | - * Because the runner doesn't natively support "pausing", we intercept | |
| 50 | - * execution here by: | |
| 51 | - * 1. Resolving the delay in seconds. | |
| 52 | - * 2. Collecting the remaining nodes (everything downstream of this node). | |
| 53 | - * 3. Persisting the checkpoint to the DB. | |
| 54 | - * 4. Scheduling a single WP Cron event. | |
| 55 | - * 5. Returning a special '__halt' flag so the runner stops this branch. | |
| 56 | - * | |
| 57 | - * @param array $action_data Configuration for this action from the workflow. | |
| 58 | - * @param array $trigger_data Data passed from the trigger. | |
| 59 | - * @return array | |
| 60 | - */ | |
| 61 | - public function execute( $action_data, $trigger_data ) { | |
| 62 | - $action_id = isset( $action_data['actionId'] ) ? $action_data['actionId'] : ''; | |
| 63 | - | |
| 64 | - if ( 'delay_execution' !== $action_id ) { | |
| 65 | - return array( 'success' => false, 'message' => 'Unknown delay action.' ); | |
| 66 | - } | |
| 67 | - | |
| 68 | - $cfg = isset( $action_data['config'] ) ? $action_data['config'] : array(); | |
| 69 | - $delay_amount = isset( $cfg['delay_amount'] ) ? absint( $cfg['delay_amount'] ) : 0; | |
| 70 | - $delay_unit = isset( $cfg['delay_unit'] ) ? sanitize_key( $cfg['delay_unit'] ) : 'minutes'; | |
| 71 | - | |
| 72 | - // Convert to seconds. | |
| 73 | - $multiplier = array( | |
| 74 | - 'minutes' => MINUTE_IN_SECONDS, | |
| 75 | - 'hours' => HOUR_IN_SECONDS, | |
| 76 | - 'days' => DAY_IN_SECONDS, | |
| 77 | - ); | |
| 78 | - $delay_seconds = $delay_amount * ( isset( $multiplier[ $delay_unit ] ) ? $multiplier[ $delay_unit ] : MINUTE_IN_SECONDS ); | |
| 79 | - | |
| 80 | - if ( $delay_seconds <= 0 ) { | |
| 81 | - return array( 'success' => false, 'message' => 'Delay: invalid delay amount or unit.' ); | |
| 82 | - } | |
| 83 | - | |
| 84 | - // Pull the workflow context the runner embeds when it calls us. | |
| 85 | - // This is set via $action_data['__workflow_context'] by the modified runner. | |
| 86 | - $context = isset( $action_data['__workflow_context'] ) ? $action_data['__workflow_context'] : array(); | |
| 87 | - | |
| 88 | - $workflow_id = isset( $context['workflow_id'] ) ? absint( $context['workflow_id'] ) : 0; | |
| 89 | - $trigger_id = isset( $context['trigger_id'] ) ? $context['trigger_id'] : ''; | |
| 90 | - $remaining_nodes = isset( $context['remaining_nodes'] ) ? $context['remaining_nodes'] : array(); | |
| 91 | - $connections = isset( $context['connections'] ) ? $context['connections'] : array(); | |
| 92 | - | |
| 93 | - if ( empty( $remaining_nodes ) || ! $workflow_id ) { | |
| 94 | - // Nothing to schedule — just return success (no downstream nodes). | |
| 95 | - return array( | |
| 96 | - 'success' => true, | |
| 97 | - 'message' => sprintf( | |
| 98 | - 'Delay: %d %s queued but no downstream nodes found; nothing to schedule.', | |
| 99 | - $delay_amount, | |
| 100 | - $delay_unit | |
| 101 | - ), | |
| 102 | - ); | |
| 103 | - } | |
| 104 | - | |
| 105 | - // Persist the checkpoint. | |
| 106 | - $scheduled_at = gmdate( 'Y-m-d H:i:s', time() + $delay_seconds ); | |
| 107 | - $task_id = Database::add_scheduled_task( | |
| 108 | - array( | |
| 109 | - 'workflow_id' => $workflow_id, | |
| 110 | - 'trigger_id' => (string) $trigger_id, | |
| 111 | - 'trigger_data' => $trigger_data, | |
| 112 | - 'remaining_nodes' => $remaining_nodes, | |
| 113 | - 'connections' => $connections, | |
| 114 | - 'scheduled_at' => $scheduled_at, | |
| 115 | - ) | |
| 116 | - ); | |
| 117 | - | |
| 118 | - if ( ! $task_id ) { | |
| 119 | - return array( 'success' => false, 'message' => 'Delay: failed to persist scheduled task to DB.' ); | |
| 120 | - } | |
| 121 | - | |
| 122 | - // Schedule the WP Cron event. | |
| 123 | - $scheduled = wp_schedule_single_event( | |
| 124 | - time() + $delay_seconds, | |
| 125 | - self::CRON_HOOK, | |
| 126 | - array( $task_id ) | |
| 127 | - ); | |
| 128 | - | |
| 129 | - if ( false === $scheduled ) { | |
| 130 | - // Cron scheduling failed — clean up and bail. | |
| 131 | - Database::delete_scheduled_task( $task_id ); | |
| 132 | - return array( 'success' => false, 'message' => 'Delay: wp_schedule_single_event() failed.' ); | |
| 133 | - } | |
| 134 | - | |
| 135 | - $human_delay = $delay_amount . ' ' . $delay_unit; | |
| 136 | - | |
| 137 | - // error_log( sprintf( | |
| 138 | - // 'WPbot Automator - Delay: Workflow %d paused for %s. Task ID: %d. Resumes at %s.', | |
| 139 | - // $workflow_id, | |
| 140 | - // $human_delay, | |
| 141 | - // $task_id, | |
| 142 | - // $scheduled_at | |
| 143 | - // ) ); | |
| 144 | - | |
| 145 | - // Signal the workflow runner to halt further synchronous execution on | |
| 146 | - // this branch by returning the special __halt key. | |
| 147 | - return array( | |
| 148 | - 'success' => true, | |
| 149 | - '__halt' => true, | |
| 150 | - 'task_id' => $task_id, | |
| 151 | - 'message' => sprintf( 'Delay: workflow paused for %s. Will resume at %s.', $human_delay, $scheduled_at ), | |
| 152 | - ); | |
| 153 | - } | |
| 154 | - | |
| 155 | - /** | |
| 156 | - * WP Cron callback: resume the deferred workflow. | |
| 157 | - * | |
| 158 | - * Called by WordPress Cron after the delay has elapsed. | |
| 159 | - * | |
| 160 | - * @param int $task_id Scheduled task row ID. | |
| 161 | - */ | |
| 162 | - public function resume_workflow( $task_id ) { | |
| 163 | - $task = Database::get_scheduled_task( absint( $task_id ) ); | |
| 164 | - | |
| 165 | - if ( ! $task ) { | |
| 166 | - //error_log( sprintf( 'WPbot Automator - Delay: resume_workflow called with unknown task ID %d.', $task_id ) ); | |
| 167 | - return; | |
| 168 | - } | |
| 169 | - | |
| 170 | - if ( 'pending' !== $task['status'] ) { | |
| 171 | - // Already processed (guard against duplicate cron fires). | |
| 172 | - return; | |
| 173 | - } | |
| 174 | - | |
| 175 | - // Mark as completed immediately to prevent double execution. | |
| 176 | - Database::complete_scheduled_task( $task_id ); | |
| 177 | - | |
| 178 | - $workflow_id = (int) $task['workflow_id']; | |
| 179 | - $trigger_id = $task['trigger_id']; | |
| 180 | - $trigger_data = is_array( $task['trigger_data'] ) ? $task['trigger_data'] : array(); | |
| 181 | - $remaining_nodes = is_array( $task['remaining_nodes'] ) ? $task['remaining_nodes'] : array(); | |
| 182 | - $connections = is_array( $task['connections'] ) ? $task['connections'] : array(); | |
| 183 | - | |
| 184 | - // error_log( sprintf( | |
| 185 | - // 'WPbot Automator - Delay: Resuming workflow %d from task %d. Remaining nodes: %d.', | |
| 186 | - // $workflow_id, | |
| 187 | - // $task_id, | |
| 188 | - // count( $remaining_nodes ) | |
| 189 | - // ) ); | |
| 190 | - | |
| 191 | - if ( empty( $remaining_nodes ) ) { | |
| 192 | - return; | |
| 193 | - } | |
| 194 | - | |
| 195 | - // Re-use the runner to execute remaining nodes directly. | |
| 196 | - // We grab the first remaining node id and pass the rest as the full set. | |
| 197 | - $first_node_id = $remaining_nodes[0]; | |
| 198 | - $executed_nodes = array(); // Fresh slate — all remaining nodes are eligible. | |
| 199 | - | |
| 200 | - // The runner's run_next_nodes is private, so we build a minimal fake | |
| 201 | - // workflow structure and call execute_workflow with only the remaining nodes. | |
| 202 | - $fake_workflow = array( | |
| 203 | - 'id' => $workflow_id, | |
| 204 | - 'workflow_data' => wp_json_encode( | |
| 205 | - array( | |
| 206 | - 'nodes' => $remaining_nodes, | |
| 207 | - 'connections' => $connections, | |
| 208 | - ) | |
| 209 | - ), | |
| 210 | - ); | |
| 211 | - | |
| 212 | - // The first remaining_nodes entry is the node immediately after the | |
| 213 | - // delay node. We mark it as a "trigger" type with a wildcard so the | |
| 214 | - // runner's trigger-matching logic accepts it as the starting point. | |
| 215 | - $fake_workflow['workflow_data'] = wp_json_encode( | |
| 216 | - array( | |
| 217 | - 'nodes' => array_map( | |
| 218 | - function( $node, $idx ) { | |
| 219 | - if ( 0 === $idx ) { | |
| 220 | - // Re-type as trigger so execute_workflow starts here. | |
| 221 | - $node['type'] = 'trigger'; | |
| 222 | - } | |
| 223 | - return $node; | |
| 224 | - }, | |
| 225 | - $remaining_nodes, | |
| 226 | - array_keys( $remaining_nodes ) | |
| 227 | - ), | |
| 228 | - 'connections' => $connections, | |
| 229 | - ) | |
| 230 | - ); | |
| 231 | - | |
| 232 | - Workflow_Runner::execute_workflow( $fake_workflow, $trigger_data, $first_node_id ); | |
| 233 | - | |
| 234 | - // error_log( sprintf( | |
| 235 | - // 'WPbot Automator - Delay: Workflow %d resumed and completed from task %d.', | |
| 236 | - // $workflow_id, | |
| 237 | - // $task_id | |
| 238 | - // ) ); | |
| 239 | - } | |
| 240 | -} | |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * Delay / Wait Action | |
| 4 | + * | |
| 5 | + * Pauses workflow execution for a configurable duration using WordPress Cron | |
| 6 | + * (wp_schedule_single_event). The remaining nodes and trigger data are | |
| 7 | + * persisted to the wpbot_automator_scheduled_tasks DB table so they can be | |
| 8 | + * resumed accurately after the delay fires. | |
| 9 | + * | |
| 10 | + * @package WPbot_Automator | |
| 11 | + */ | |
| 12 | + | |
| 13 | +namespace WPbot_Automator\Actions; | |
| 14 | + | |
| 15 | +use WPbot_Automator\Core\Database; | |
| 16 | +use WPbot_Automator\Engine\Workflow_Runner; | |
| 17 | + | |
| 18 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 19 | + exit; | |
| 20 | +} | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * Class Delay_Actions | |
| 24 | + */ | |
| 25 | +class Delay_Actions extends Action { | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * WP Cron hook name used to resume deferred workflows. | |
| 29 | + */ | |
| 30 | + const CRON_HOOK = 'wpbot_automator_resume_delayed_workflow'; | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * Constructor | |
| 34 | + */ | |
| 35 | + public function __construct() { | |
| 36 | + $this->id = 'delay'; | |
| 37 | + $this->group = __( 'Flow Control', 'wpbot-automator' ); | |
| 38 | + | |
| 39 | + // Register the cron callback once per request. | |
| 40 | + add_action( self::CRON_HOOK, array( $this, 'resume_workflow' ), 10, 1 ); | |
| 41 | + } | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * Execute the action. | |
| 45 | + * | |
| 46 | + * When the workflow runner hits this node it will call execute() with | |
| 47 | + * extra context that the runner passes via $action_data['__context']. | |
| 48 | + * | |
| 49 | + * Because the runner doesn't natively support "pausing", we intercept | |
| 50 | + * execution here by: | |
| 51 | + * 1. Resolving the delay in seconds. | |
| 52 | + * 2. Collecting the remaining nodes (everything downstream of this node). | |
| 53 | + * 3. Persisting the checkpoint to the DB. | |
| 54 | + * 4. Scheduling a single WP Cron event. | |
| 55 | + * 5. Returning a special '__halt' flag so the runner stops this branch. | |
| 56 | + * | |
| 57 | + * @param array $action_data Configuration for this action from the workflow. | |
| 58 | + * @param array $trigger_data Data passed from the trigger. | |
| 59 | + * @return array | |
| 60 | + */ | |
| 61 | + public function execute( $action_data, $trigger_data ) { | |
| 62 | + $action_id = isset( $action_data['actionId'] ) ? $action_data['actionId'] : ''; | |
| 63 | + | |
| 64 | + if ( 'delay_execution' !== $action_id ) { | |
| 65 | + return array( 'success' => false, 'message' => 'Unknown delay action.' ); | |
| 66 | + } | |
| 67 | + | |
| 68 | + $cfg = isset( $action_data['config'] ) ? $action_data['config'] : array(); | |
| 69 | + $delay_amount = isset( $cfg['delay_amount'] ) ? absint( $cfg['delay_amount'] ) : 0; | |
| 70 | + $delay_unit = isset( $cfg['delay_unit'] ) ? sanitize_key( $cfg['delay_unit'] ) : 'minutes'; | |
| 71 | + | |
| 72 | + // Convert to seconds. | |
| 73 | + $multiplier = array( | |
| 74 | + 'minutes' => MINUTE_IN_SECONDS, | |
| 75 | + 'hours' => HOUR_IN_SECONDS, | |
| 76 | + 'days' => DAY_IN_SECONDS, | |
| 77 | + ); | |
| 78 | + $delay_seconds = $delay_amount * ( isset( $multiplier[ $delay_unit ] ) ? $multiplier[ $delay_unit ] : MINUTE_IN_SECONDS ); | |
| 79 | + | |
| 80 | + if ( $delay_seconds <= 0 ) { | |
| 81 | + return array( 'success' => false, 'message' => 'Delay: invalid delay amount or unit.' ); | |
| 82 | + } | |
| 83 | + | |
| 84 | + // Pull the workflow context the runner embeds when it calls us. | |
| 85 | + // This is set via $action_data['__workflow_context'] by the modified runner. | |
| 86 | + $context = isset( $action_data['__workflow_context'] ) ? $action_data['__workflow_context'] : array(); | |
| 87 | + | |
| 88 | + $workflow_id = isset( $context['workflow_id'] ) ? absint( $context['workflow_id'] ) : 0; | |
| 89 | + $trigger_id = isset( $context['trigger_id'] ) ? $context['trigger_id'] : ''; | |
| 90 | + $remaining_nodes = isset( $context['remaining_nodes'] ) ? $context['remaining_nodes'] : array(); | |
| 91 | + $connections = isset( $context['connections'] ) ? $context['connections'] : array(); | |
| 92 | + | |
| 93 | + if ( empty( $remaining_nodes ) || ! $workflow_id ) { | |
| 94 | + // Nothing to schedule — just return success (no downstream nodes). | |
| 95 | + return array( | |
| 96 | + 'success' => true, | |
| 97 | + 'message' => sprintf( | |
| 98 | + 'Delay: %d %s queued but no downstream nodes found; nothing to schedule.', | |
| 99 | + $delay_amount, | |
| 100 | + $delay_unit | |
| 101 | + ), | |
| 102 | + ); | |
| 103 | + } | |
| 104 | + | |
| 105 | + // Persist the checkpoint. | |
| 106 | + $scheduled_at = gmdate( 'Y-m-d H:i:s', time() + $delay_seconds ); | |
| 107 | + $task_id = Database::add_scheduled_task( | |
| 108 | + array( | |
| 109 | + 'workflow_id' => $workflow_id, | |
| 110 | + 'trigger_id' => (string) $trigger_id, | |
| 111 | + 'trigger_data' => $trigger_data, | |
| 112 | + 'remaining_nodes' => $remaining_nodes, | |
| 113 | + 'connections' => $connections, | |
| 114 | + 'scheduled_at' => $scheduled_at, | |
| 115 | + ) | |
| 116 | + ); | |
| 117 | + | |
| 118 | + if ( ! $task_id ) { | |
| 119 | + return array( 'success' => false, 'message' => 'Delay: failed to persist scheduled task to DB.' ); | |
| 120 | + } | |
| 121 | + | |
| 122 | + // Schedule the WP Cron event. | |
| 123 | + $scheduled = wp_schedule_single_event( | |
| 124 | + time() + $delay_seconds, | |
| 125 | + self::CRON_HOOK, | |
| 126 | + array( $task_id ) | |
| 127 | + ); | |
| 128 | + | |
| 129 | + if ( false === $scheduled ) { | |
| 130 | + // Cron scheduling failed — clean up and bail. | |
| 131 | + Database::delete_scheduled_task( $task_id ); | |
| 132 | + return array( 'success' => false, 'message' => 'Delay: wp_schedule_single_event() failed.' ); | |
| 133 | + } | |
| 134 | + | |
| 135 | + $human_delay = $delay_amount . ' ' . $delay_unit; | |
| 136 | + | |
| 137 | + // error_log( sprintf( | |
| 138 | + // 'WPbot Automator - Delay: Workflow %d paused for %s. Task ID: %d. Resumes at %s.', | |
| 139 | + // $workflow_id, | |
| 140 | + // $human_delay, | |
| 141 | + // $task_id, | |
| 142 | + // $scheduled_at | |
| 143 | + // ) ); | |
| 144 | + | |
| 145 | + // Signal the workflow runner to halt further synchronous execution on | |
| 146 | + // this branch by returning the special __halt key. | |
| 147 | + return array( | |
| 148 | + 'success' => true, | |
| 149 | + '__halt' => true, | |
| 150 | + 'task_id' => $task_id, | |
| 151 | + 'message' => sprintf( 'Delay: workflow paused for %s. Will resume at %s.', $human_delay, $scheduled_at ), | |
| 152 | + ); | |
| 153 | + } | |
| 154 | + | |
| 155 | + /** | |
| 156 | + * WP Cron callback: resume the deferred workflow. | |
| 157 | + * | |
| 158 | + * Called by WordPress Cron after the delay has elapsed. | |
| 159 | + * | |
| 160 | + * @param int $task_id Scheduled task row ID. | |
| 161 | + */ | |
| 162 | + public function resume_workflow( $task_id ) { | |
| 163 | + $task = Database::get_scheduled_task( absint( $task_id ) ); | |
| 164 | + | |
| 165 | + if ( ! $task ) { | |
| 166 | + //error_log( sprintf( 'WPbot Automator - Delay: resume_workflow called with unknown task ID %d.', $task_id ) ); | |
| 167 | + return; | |
| 168 | + } | |
| 169 | + | |
| 170 | + if ( 'pending' !== $task['status'] ) { | |
| 171 | + // Already processed (guard against duplicate cron fires). | |
| 172 | + return; | |
| 173 | + } | |
| 174 | + | |
| 175 | + // Mark as completed immediately to prevent double execution. | |
| 176 | + Database::complete_scheduled_task( $task_id ); | |
| 177 | + | |
| 178 | + $workflow_id = (int) $task['workflow_id']; | |
| 179 | + $trigger_id = $task['trigger_id']; | |
| 180 | + $trigger_data = is_array( $task['trigger_data'] ) ? $task['trigger_data'] : array(); | |
| 181 | + $remaining_nodes = is_array( $task['remaining_nodes'] ) ? $task['remaining_nodes'] : array(); | |
| 182 | + $connections = is_array( $task['connections'] ) ? $task['connections'] : array(); | |
| 183 | + | |
| 184 | + // error_log( sprintf( | |
| 185 | + // 'WPbot Automator - Delay: Resuming workflow %d from task %d. Remaining nodes: %d.', | |
| 186 | + // $workflow_id, | |
| 187 | + // $task_id, | |
| 188 | + // count( $remaining_nodes ) | |
| 189 | + // ) ); | |
| 190 | + | |
| 191 | + if ( empty( $remaining_nodes ) ) { | |
| 192 | + return; | |
| 193 | + } | |
| 194 | + | |
| 195 | + // Re-use the runner to execute remaining nodes directly. | |
| 196 | + // We grab the first remaining node id and pass the rest as the full set. | |
| 197 | + $first_node_id = $remaining_nodes[0]; | |
| 198 | + $executed_nodes = array(); // Fresh slate — all remaining nodes are eligible. | |
| 199 | + | |
| 200 | + // The runner's run_next_nodes is private, so we build a minimal fake | |
| 201 | + // workflow structure and call execute_workflow with only the remaining nodes. | |
| 202 | + $fake_workflow = array( | |
| 203 | + 'id' => $workflow_id, | |
| 204 | + 'workflow_data' => wp_json_encode( | |
| 205 | + array( | |
| 206 | + 'nodes' => $remaining_nodes, | |
| 207 | + 'connections' => $connections, | |
| 208 | + ) | |
| 209 | + ), | |
| 210 | + ); | |
| 211 | + | |
| 212 | + // The first remaining_nodes entry is the node immediately after the | |
| 213 | + // delay node. We mark it as a "trigger" type with a wildcard so the | |
| 214 | + // runner's trigger-matching logic accepts it as the starting point. | |
| 215 | + $fake_workflow['workflow_data'] = wp_json_encode( | |
| 216 | + array( | |
| 217 | + 'nodes' => array_map( | |
| 218 | + function( $node, $idx ) { | |
| 219 | + if ( 0 === $idx ) { | |
| 220 | + // Re-type as trigger so execute_workflow starts here. | |
| 221 | + $node['type'] = 'trigger'; | |
| 222 | + } | |
| 223 | + return $node; | |
| 224 | + }, | |
| 225 | + $remaining_nodes, | |
| 226 | + array_keys( $remaining_nodes ) | |
| 227 | + ), | |
| 228 | + 'connections' => $connections, | |
| 229 | + ) | |
| 230 | + ); | |
| 231 | + | |
| 232 | + Workflow_Runner::execute_workflow( $fake_workflow, $trigger_data, $first_node_id ); | |
| 233 | + | |
| 234 | + // error_log( sprintf( | |
| 235 | + // 'WPbot Automator - Delay: Workflow %d resumed and completed from task %d.', | |
| 236 | + // $workflow_id, | |
| 237 | + // $task_id | |
| 238 | + // ) ); | |
| 239 | + } | |
| 240 | +} | |