PluginProbe
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI / 6.0.3
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI v6.0.3
6.0.3 6.0.2 6.0.1 6.0.0 5.8.6 5.8.5 5.8.4 5.8.3 5.8.2 5.8.1 5.8.0 5.7.9.2 5.7.9.1 5.7.8 5.7.9 5.7.6 5.7.7 5.7.5 5.7.4 5.7.3 5.7.2 5.7.1 trunk 5.6.0 5.6.1 All 34 releases
← All changes | includes/automations.php +1119 -0 5.7.4 → 6.0.3 View file →
@@ -1,0 +1,1119 @@
1 +<?php
2 +/**
3 + * Automations
4 + *
5 + * @package AutomatorWP\Automations
6 + * @author AutomatorWP <[email protected]>, Ruben Garcia <[email protected]>
7 + * @since 1.0.0
8 + */
9 +// Exit if accessed directly
10 +if( !defined( 'ABSPATH' ) ) exit;
11 +
12 +/**
13 + * Get automation registered statuses
14 + *
15 + * @since 1.0.0
16 + *
17 + * @return array
18 + */
19 +function automatorwp_get_automation_statuses() {
20 +
21 + return apply_filters( 'automatorwp_automation_statuses', array(
22 + 'active' => __( 'Active', 'automatorwp' ),
23 + 'in-progress' => __( 'In Progress', 'automatorwp' ),
24 + 'inactive' => __( 'Inactive', 'automatorwp' ),
25 + ) );
26 +
27 +}
28 +
29 +/**
30 + * Get automation registered types
31 + *
32 + * @since 1.3.0
33 + *
34 + * @return array
35 + */
36 +function automatorwp_get_automation_types() {
37 +
38 + return apply_filters( 'automatorwp_automation_types', array(
39 + 'user' => array(
40 + 'image' => AUTOMATORWP_URL . 'assets/img/automatorwp-logo.svg',
41 + 'label' => __( 'Logged-in', 'automatorwp' ),
42 + 'desc' => __( 'Run actions on the user who completes the triggers.', 'automatorwp' ),
43 +
44 + ),
45 + 'anonymous' => array(
46 + 'image' => AUTOMATORWP_URL . 'assets/img/automatorwp-anonymous-logo.svg',
47 + 'label' => __( 'Anonymous', 'automatorwp' ),
48 + 'desc' => __( 'Run actions based on not logged-in users interactions.', 'automatorwp' ),
49 + ),
50 + ) );
51 +
52 +}
53 +
54 +/**
55 + * Get automation registered types labels
56 + *
57 + * @since 1.3.0
58 + *
59 + * @return array
60 + */
61 +function automatorwp_get_automation_types_labels() {
62 +
63 + $types = automatorwp_get_automation_types();
64 + $labels = array();
65 +
66 + foreach( $types as $type => $args ) {
67 + $labels[$type] = $args['label'];
68 + }
69 +
70 + return $labels;
71 +
72 +}
73 +
74 +/**
75 + * Get the automation types that will make a look
76 + *
77 + * @since 1.3.0
78 + *
79 + * @return array
80 + */
81 +function automatorwp_get_automation_loop_types() {
82 +
83 + return apply_filters( 'automatorwp_automation_loop_types', array() );
84 +
85 +}
86 +
87 +/**
88 + * Get the automation object data
89 + *
90 + * @param int $automation_id The automation ID
91 + * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
92 + * a object, an associative array, or a numeric array, respectively. Default OBJECT.
93 + *
94 + * @return array|stdClass|null
95 + */
96 +function automatorwp_get_automation_object( $automation_id, $output = OBJECT ) {
97 +
98 + ct_setup_table( 'automatorwp_automations' );
99 +
100 + $automation = ct_get_object( $automation_id );
101 +
102 + ct_reset_setup_table();
103 +
104 + return $automation;
105 +
106 +}
107 +
108 +/**
109 + * Get the automation metadata
110 + *
111 + * @param int $automation_id The automation ID
112 + * @param string $meta_key Optional. The meta key to retrieve. By default, returns
113 + * data for all keys. Default empty.
114 + * @param bool $single Optional. Whether to return a single value. Default false.
115 + *
116 + * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
117 + */
118 +function automatorwp_get_automation_meta( $automation_id, $meta_key = '', $single = false ) {
119 +
120 + ct_setup_table( 'automatorwp_automations' );
121 + $meta_value = ct_get_object_meta( $automation_id, $meta_key, $single );
122 + ct_reset_setup_table();
123 +
124 + return $meta_value;
125 +
126 +}
127 +
128 +/**
129 + * Update automation metadata
130 + *
131 + * @param int $automation_id The automation ID.
132 + * @param string $meta_key Metadata key.
133 + * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
134 + * @param mixed $prev_value Optional. Previous value to check before removing. Default empty.
135 + *
136 + * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
137 + */
138 +function automatorwp_update_automation_meta( $automation_id, $meta_key, $meta_value, $prev_value = '' ) {
139 +
140 + ct_setup_table( 'automatorwp_automations' );
141 + $meta_id = ct_update_object_meta( $automation_id, $meta_key, $meta_value, $prev_value );
142 + ct_reset_setup_table();
143 +
144 + return $meta_id;
145 +
146 +}
147 +
148 +/**
149 + * Get automation triggers
150 + *
151 + * @since 1.0.0
152 + *
153 + * @param int $automation_id The automation ID
154 + * @param string $output The required return type (OBJECT|ARRAY_A|ARRAY_N)
155 + *
156 + * @return stdClass|array The automation triggers
157 + */
158 +function automatorwp_get_automation_triggers( $automation_id, $output = OBJECT ) {
159 +
160 + $cache = automatorwp_get_cache( 'automation_triggers', array(), false );
161 +
162 + if( isset( $cache[$automation_id] ) ) {
163 +
164 + // Use triggers already cached
165 + $triggers = $cache[$automation_id];
166 +
167 + } else {
168 +
169 + // Query the triggers for the first time
170 + ct_setup_table( 'automatorwp_triggers' );
171 +
172 + $ct_query = new CT_Query( array(
173 + 'automation_id' => $automation_id,
174 + 'orderby' => 'position',
175 + 'order' => 'ASC',
176 + 'items_per_page' => -1,
177 + ) );
178 +
179 + $triggers = $ct_query->get_results();
180 +
181 + ct_reset_setup_table();
182 +
183 + // Cache triggers
184 + $cache[$automation_id] = $triggers;
185 +
186 + automatorwp_set_cache( 'automation_triggers', $cache );
187 +
188 + }
189 +
190 + if( $output === ARRAY_N || $output === ARRAY_A ) {
191 +
192 + // Turn array of objects into an array of arrays
193 + foreach( $triggers as $i => $trigger ) {
194 + $triggers[$i] = (array) $trigger;
195 + }
196 +
197 + }
198 +
199 + return $triggers;
200 +
201 +}
202 +
203 +/**
204 + * Get automation trigger by type
205 + *
206 + * @since 1.0.0
207 + *
208 + * @param int $automation_id The automation ID
209 + * @param string $type The trigger type
210 + * @param string $output The required return type (OBJECT|ARRAY_A|ARRAY_N)
211 + *
212 + * @return stdClass|array The automation trigger
213 + */
214 +function automatorwp_get_automation_trigger_by_type( $automation_id, $type, $output = OBJECT ) {
215 +
216 + $cache = automatorwp_get_cache( 'automation_trigger_by_type', array(), false );
217 +
218 + if( isset( $cache[$automation_id] ) && isset( $cache[$automation_id][$type] ) ) {
219 +
220 + // Use the trigger already cached
221 + $trigger = $cache[$automation_id][$type];
222 +
223 + } else {
224 +
225 + $triggers = automatorwp_get_automation_triggers( $automation_id, $output );
226 + $trigger = null;
227 +
228 + foreach( $triggers as $item ) {
229 +
230 + if( $output === ARRAY_N || $output === ARRAY_A ) {
231 + // Array
232 + $item_type = $item['type'];
233 + } else {
234 + // stdClass
235 + $item_type = $item->type;
236 + }
237 +
238 + if( $item_type === $type ) {
239 + $trigger = $item;
240 + break;
241 + }
242 +
243 + }
244 +
245 + if( ! isset( $cache[$automation_id] ) ){
246 + $cache[$automation_id] = array();
247 + }
248 +
249 + // Cache triggers
250 + $cache[$automation_id][$type] = $trigger;
251 +
252 + automatorwp_set_cache( 'automation_trigger_by_type', $cache );
253 +
254 + }
255 +
256 + return $trigger;
257 +
258 +}
259 +
260 +/**
261 + * Get automation actions
262 + *
263 + * @since 1.0.0
264 + *
265 + * @param int $automation_id The automation ID
266 + * @param string $output The required return type (OBJECT|ARRAY_A|ARRAY_N)
267 + *
268 + * @return array Array of automation actions
269 + */
270 +function automatorwp_get_automation_actions( $automation_id, $output = OBJECT ) {
271 +
272 + $cache = automatorwp_get_cache( 'automation_actions', array(), false );
273 +
274 + if( isset( $cache[$automation_id] ) ) {
275 +
276 + // Use triggers already cached
277 + $actions = $cache[$automation_id];
278 +
279 + } else {
280 +
281 + // Query the triggers for the first time
282 +
283 + ct_setup_table( 'automatorwp_actions' );
284 +
285 + $ct_query = new CT_Query( array(
286 + 'automation_id' => $automation_id,
287 + 'orderby' => 'position',
288 + 'order' => 'ASC',
289 + 'items_per_page' => -1,
290 + ) );
291 +
292 + $actions = $ct_query->get_results();
293 +
294 + ct_reset_setup_table();
295 +
296 + // Cache actions
297 + $cache[$automation_id] = $actions;
298 +
299 + automatorwp_set_cache( 'automation_actions', $cache );
300 +
301 + }
302 +
303 + if( $output === ARRAY_N || $output === ARRAY_A ) {
304 +
305 + // Turn array of objects into an array of arrays
306 + foreach( $actions as $i => $action ) {
307 + $actions[$i] = (array) $action;
308 + }
309 +
310 + }
311 +
312 + return $actions;
313 +
314 +}
315 +
316 +/**
317 + * Get the automation run details
318 + *
319 + * @since 2.2.2
320 + *
321 + * @param stdClass $automation The automation object
322 + *
323 + * @return array|false
324 + */
325 +function automatorwp_get_automation_run_details( $automation ) {
326 +
327 + global $automatorwp_run_automation_error;
328 +
329 + if( ! is_object( $automation ) ) {
330 + $automatorwp_run_automation_error = __( 'Invalid automation.', 'automatorwp' );
331 + return false;
332 + }
333 +
334 + // Used for automation loop
335 + $items_per_loop = absint( automatorwp_get_automation_meta( $automation->id, 'items_per_loop', true ) );
336 +
337 + $count = apply_filters( 'automatorwp_get_automation_run_details_count', 0, $automation );
338 +
339 + $loop = absint( automatorwp_get_automation_meta( $automation->id, 'current_loop', true ) );
340 + $processed = ( $loop * $items_per_loop ) + $items_per_loop;
341 + $processed = min( $processed, $count );
342 + $percentage = ( $count > 0 ? ( $processed / $count ) * 100 : 100 );
343 +
344 + $run_details = array(
345 + 'loop' => $loop,
346 + 'items_per_loop' => $items_per_loop,
347 + 'count' => $count,
348 + 'processed' => $processed,
349 + 'percentage' => $percentage,
350 + 'run_again' => ( $processed < $count ),
351 + );
352 +
353 + return apply_filters( 'automatorwp_get_automation_run_details', $run_details, $automation );
354 +
355 +}
356 +
357 +/**
358 + * Calculate the next run date for an all users automation
359 + *
360 + * @since 1.0.0
361 + *
362 + * @param int $automation_id The automation ID
363 + * @param bool $is_saving Flag to meet if the automation is being saved from the edit screen to handle the values from $_POST
364 + */
365 +function automatorwp_update_automation_next_run_date( $automation_id, $is_saving = false ) {
366 +
367 + // Setup vars
368 + $args = automatorwp_setup_automation_next_run_date_args( $automation_id, $is_saving );
369 + $current_time = current_time( 'timestamp' );
370 + $date_format = automatorwp_get_date_format( array( 'Y-m-d', 'm/d/Y' ) );
371 + $time_format = automatorwp_get_time_format();
372 + $datetime_format = $date_format . ' ' . $time_format;
373 + $timestamp = false;
374 +
375 + if( $args['schedule_run'] === true ) {
376 + // Schedule run
377 +
378 + $schedule_run_datetime = $args['schedule_run_datetime'];
379 +
380 + // Check if schedule run datetime is correctly setup (this only when saving)
381 + if( is_array( $schedule_run_datetime ) && isset( $schedule_run_datetime['date'] ) && isset( $schedule_run_datetime['time'] ) ) {
382 +
383 + $date = sanitize_text_field( $schedule_run_datetime['date'] );
384 + $time = sanitize_text_field( $schedule_run_datetime['time'] );
385 +
386 + $timestamp = automatorwp_get_timestamp_from_value( $date . ' ' . $time, $date_format );
387 + } else if( ! empty( $schedule_run_datetime ) ) {
388 + // Schedule run datetime is stored as a timestamp
389 + $timestamp = $schedule_run_datetime;
390 + }
391 + } else if( $args['recurring_run'] === true ) {
392 + // Recurring run
393 +
394 + $day = $args['recurring_run_day'];
395 + $period = $args['recurring_run_period'];
396 + $time = $args['recurring_run_time'];
397 +
398 + switch( $period ) {
399 + case 'day':
400 + $date = date( 'Y-m-d', strtotime(' +1 day', $current_time ) );
401 + break;
402 + case 'week':
403 + $days_of_the_week = array( 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' );
404 + $week_day = $day - 1;
405 +
406 + // Get the current week timestamp
407 + $week_timestamp = strtotime( $days_of_the_week[$week_day], strtotime( 'this week', $current_time ) );
408 +
409 + // If lower than today, get the next week
410 + if( $week_timestamp <= $current_time ) {
411 + $week_timestamp = strtotime( $days_of_the_week[$week_day], strtotime( 'next week', $current_time ) );
412 + }
413 +
414 + $date = date( 'Y-m-d', $week_timestamp );
415 + break;
416 + case 'month':
417 + // Get the current month timestamp
418 + $month = absint( date('m', $current_time) );
419 +
420 + // Check the max day of this month
421 + $max_day = absint( date( 't', $current_time ) );
422 + $max_day = min( $day, $max_day );
423 +
424 + $month_timestamp = strtotime( date( "{$max_day}-{$month}-Y", $current_time ) );
425 +
426 + // If lower than today, get the next month
427 + if( $month_timestamp <= $current_time ) {
428 + $next_month = strtotime( 'next month', $current_time );
429 + $month = absint( date('m', $next_month) );
430 +
431 + // Check the max day of next month
432 + $max_day = absint( date( 't', $next_month ) );
433 + $max_day = min( $day, $max_day );
434 +
435 + $month_timestamp = strtotime( date( "{$max_day}-{$month}-Y", $current_time ) );
436 + }
437 +
438 + $date = date( 'Y-m-d', $month_timestamp );
439 + break;
440 + case 'year':
441 + $year = absint( date('Y', $current_time) );
442 + $year_day = $day - 1;
443 +
444 + // Check if is a leap year
445 + if( $day === 365 && checkdate( 2, 29, $year ) ) {
446 + $year_day = 365;
447 + }
448 +
449 + // Get the current year timestamp
450 +
451 + $date = DateTime::createFromFormat( 'Y z' , $year . ' ' . $year_day);
452 + $year_timestamp = $date->getTimestamp();
453 +
454 + // If lower than today, get the next year
455 + if( $year_timestamp <= $current_time ) {
456 + // Increase year and restore the day to check if next year is leap
457 + $year += 1;
458 + $year_day = $day - 1;
459 +
460 + // Check if is a leap year
461 + if( $day === 365 && checkdate( 2, 29, $year ) ) {
462 + $year_day = 365;
463 + }
464 +
465 + $date = DateTime::createFromFormat( 'Y z' , $year . ' ' . $year_day);
466 +
467 + $year_timestamp = $date->getTimestamp();
468 + }
469 +
470 + $date = date( 'Y-m-d', $year_timestamp );
471 + break;
472 + }
473 +
474 + $datetime = $date . ' ' . $time;
475 + $timestamp = automatorwp_get_timestamp_from_value( $datetime, $datetime_format );
476 +
477 + }
478 +
479 + // Update the next run date meta
480 + if( $timestamp && $timestamp > $current_time ) {
481 + automatorwp_update_automation_meta( $automation_id, 'next_run_date', date( 'Y-m-d H:i:s', $timestamp ) );
482 + } else {
483 + automatorwp_update_automation_meta( $automation_id, 'next_run_date', '' );
484 + }
485 +
486 +}
487 +
488 +/**
489 + * Setup args for the next run date calculation
490 + *
491 + * @since 1.0.0
492 + *
493 + * @param int $automation_id The automation ID
494 + * @param bool $is_saving Flag to meet if the automation is being saved from the edit screen to handle the values from $_POST
495 + */
496 +function automatorwp_setup_automation_next_run_date_args( $automation_id, $is_saving = false ) {
497 +
498 + $args = array();
499 +
500 + // Schedule run
501 + if( $is_saving ) {
502 + $args['schedule_run'] = ( isset( $_POST['schedule_run'] ) ? true : false );
503 + } else {
504 + $args['schedule_run'] = (bool) automatorwp_get_automation_meta( $automation_id, 'schedule_run', true );
505 + }
506 +
507 + if( $args['schedule_run'] ) {
508 +
509 + // Schedule run datetime
510 + if( $is_saving ) {
511 + $args['schedule_run_datetime'] = ( isset( $_POST['schedule_run_datetime'] ) ? $_POST['schedule_run_datetime'] : array() );
512 + } else {
513 + $args['schedule_run_datetime'] = automatorwp_get_automation_meta( $automation_id, 'schedule_run_datetime', true );
514 + }
515 +
516 + }
517 +
518 + // Recurring run
519 + if( $is_saving ) {
520 + $args['recurring_run'] = ( isset( $_POST['recurring_run'] ) ? true : false );
521 + } else {
522 + $args['recurring_run'] = (bool) automatorwp_get_automation_meta( $automation_id, 'recurring_run', true );
523 + }
524 +
525 + if( $args['recurring_run'] ) {
526 +
527 + // Recurring run day
528 + if( $is_saving ) {
529 + $args['recurring_run_day'] = absint( ( isset( $_POST['recurring_run_day'] ) ? $_POST['recurring_run_day'] : '1' ) );
530 + } else {
531 + $args['recurring_run_day'] = absint( automatorwp_get_automation_meta( $automation_id, 'recurring_run_day', true ) );
532 + }
533 +
534 + // Recurring run period
535 + if( $is_saving ) {
536 + $args['recurring_run_period'] = ( isset( $_POST['recurring_run_period'] ) ? sanitize_text_field( $_POST['recurring_run_period'] ) : 'day' );
537 + } else {
538 + $args['recurring_run_period'] = automatorwp_get_automation_meta( $automation_id, 'recurring_run_period', true );
539 + }
540 +
541 + // Recurring run time
542 + if( $is_saving ) {
543 + $args['recurring_run_time'] = ( isset( $_POST['recurring_run_time'] ) ? sanitize_text_field( $_POST['recurring_run_time'] ) : 'day' );
544 + } else {
545 + $args['recurring_run_time'] = automatorwp_get_automation_meta( $automation_id, 'recurring_run_time', true );
546 + }
547 +
548 + }
549 +
550 + return $args;
551 +
552 +}
553 +
554 +/**
555 + * Clone automation
556 + *
557 + * @since 1.0.0
558 + *
559 + * @param int $automation_id The automation ID
560 + * @param int $user_id The user ID to assign to the automation
561 + *
562 + * @return int|bool Clone result
563 + */
564 +function automatorwp_clone_automation( $automation_id, $user_id = 0 ) {
565 +
566 + if( $user_id === 0 ) {
567 + $user_id = get_current_user_id();
568 + }
569 +
570 + ct_setup_table( 'automatorwp_automations' );
571 +
572 + $automation = ct_get_object( $automation_id );
573 +
574 + // Bail if automation does not exists
575 + if( ! $automation ) {
576 + ct_reset_setup_table();
577 + return false;
578 + }
579 +
580 + $automation = ( array ) $automation;
581 +
582 + // Set up the new automation info
583 + unset( $automation['id'] );
584 + $automation['title'] .= ( ! empty( $automation['title'] ) ? ' ' : '' ) . __( '(Cloned)', 'automatorwp' );
585 + $automation['user_id'] = $user_id;
586 + $automation['status'] = 'inactive';
587 + $automation['date'] = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
588 +
589 + // Insert the new automation
590 + $new_automation_id = ct_insert_object( $automation );
591 +
592 + if( ! $new_automation_id ) {
593 + ct_reset_setup_table();
594 + return false;
595 + }
596 +
597 + automatorwp_clone_automation_items( $automation_id, $new_automation_id );
598 +
599 + ct_reset_setup_table();
600 + return $new_automation_id;
601 +
602 +}
603 +
604 +/**
605 + * Clone all automation items to a new one
606 + *
607 + * @since 1.0.0
608 + *
609 + * @param int $automation_id The automation ID
610 + * @param int $new_automation_id The automation ID to clone all the items
611 + */
612 +function automatorwp_clone_automation_items( $automation_id, $new_automation_id ) {
613 +
614 + global $wpdb;
615 +
616 + $item_types = array( 'trigger', 'action' );
617 + $ids = array();
618 + $replacements = array();
619 +
620 + // Migrate all items to the new automation and collect the old and new IDs
621 + foreach( $item_types as $item_type ) {
622 +
623 + if( $item_type === 'trigger' ) {
624 + $items = automatorwp_get_automation_triggers( $automation_id );
625 + } else {
626 + $items = automatorwp_get_automation_actions( $automation_id );
627 + }
628 +
629 + ct_setup_table( "automatorwp_{$item_type}s" );
630 +
631 + foreach( $items as $item ) {
632 +
633 + $new_item = ( array ) $item;
634 +
635 + unset( $new_item['id'] );
636 + $new_item['automation_id'] = $new_automation_id;
637 + $new_item['date'] = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
638 +
639 + $new_item_id = ct_insert_object( $new_item );
640 +
641 + // Update old ids and replacements to be used on metas
642 + if( $new_item_id ) {
643 + $ids[$item->id] = $new_item_id;
644 +
645 + if ( $item_type === 'trigger' ) {
646 + $replacements['{' . $item->id . ':'] = '{' . $new_item_id . ':';
647 + $replacements['{t:' . $item->id . ':'] = '{t:' . $new_item_id . ':';
648 + }
649 +
650 + if ( $item_type === 'action' ) {
651 + $replacements['{a:' . $item->id . ':'] = '{a:' . $new_item_id . ':';
652 + }
653 +
654 + }
655 +
656 + }
657 +
658 + ct_reset_setup_table();
659 +
660 + }
661 +
662 + $tags = array_keys( $replacements );
663 +
664 + // Loop again the items to update their metas
665 + foreach( $item_types as $item_type ) {
666 +
667 + // Do not worry about performance, AutomatorWP already caches this functions
668 + if( $item_type === 'trigger' ) {
669 + $items = automatorwp_get_automation_triggers( $automation_id );
670 + } else {
671 + $items = automatorwp_get_automation_actions( $automation_id );
672 + }
673 +
674 + $ct_table = ct_setup_table( "automatorwp_{$item_type}s" );
675 + $metas = array();
676 +
677 + foreach( $items as $item ) {
678 +
679 + // Skip if item not has been cloned
680 + if( ! isset( $ids[$item->id] ) ) {
681 + continue;
682 + }
683 +
684 + // Get all item metas
685 + $item_metas = $wpdb->get_results( "SELECT meta_key, meta_value FROM {$ct_table->meta->db->table_name} WHERE id = {$item->id}", ARRAY_A );
686 +
687 + foreach( $item_metas as $i => $item_meta ) {
688 +
689 + $meta_key = $item_metas[$i]['meta_key'];
690 +
691 + /**
692 + * Filter to exclude a meta on clone this item
693 + * $item_type: trigger | action
694 + * $item->type: The trigger or action type
695 + * $meta_key: The meta key
696 + *
697 + * @since 1.0.0
698 + *
699 + * @param bool $exclude
700 + *
701 + * @return bool
702 + */
703 + $exclude = apply_filters( "automatorwp_clone_{$item_type}_{$item->type}_meta_{$meta_key}_excluded", false );
704 +
705 + // Skip if meta gets excluded on clone
706 + if( $exclude ) {
707 + continue;
708 + }
709 +
710 + // Replace metas with old IDs with the new ones
711 + $meta_value = str_replace( $tags, $replacements, $item_metas[$i]['meta_value'] );
712 +
713 + // Prepare for the upcoming insert
714 + $metas[] = $wpdb->prepare( '%d, %s, %s', array( $ids[$item->id], $meta_key, $meta_value ) );
715 + }
716 +
717 + $item_title = str_replace( $tags, $replacements, $item->title );
718 +
719 + /**
720 + * Filter to parse the item title
721 + * $item_type: trigger | action
722 + * $item->type: The trigger or action type
723 + *
724 + * @since 1.0.0
725 + *
726 + * @param string $item_title The item title
727 + * @param int $old_item_id The old item ID
728 + * @param int $new_item_id The new item ID
729 + *
730 + * @return string
731 + */
732 + $item_title = apply_filters( "automatorwp_clone_{$item_type}_{$item->type}_title", $item_title, $item->id, $ids[$item->id] );
733 +
734 + // Update the new item title
735 + ct_update_object( array(
736 + 'id' => $ids[$item->id],
737 + 'title' => $item_title,
738 + ) );
739 +
740 + }
741 +
742 + if( count( $metas ) ) {
743 + $metas = implode( '), (', $metas );
744 +
745 + // Run a single query to insert all metas instead of insert them one-by-one
746 + $wpdb->query( "INSERT INTO {$ct_table->meta->db->table_name} (id, meta_key, meta_value) VALUES ({$metas})" );
747 + }
748 +
749 + ct_reset_setup_table();
750 +
751 + }
752 +
753 +}
754 +
755 +/**
756 + * Turn an automation into an exportable URL
757 + *
758 + * @since 1.0.0
759 + *
760 + * @param int $automation_id The automation ID
761 + *
762 + * @return string
763 + */
764 +function automatorwp_get_automation_export_url( $automation_id ) {
765 +
766 + $automation = automatorwp_get_automation_object( $automation_id );
767 +
768 + if( ! $automation ) {
769 + return '';
770 + }
771 +
772 + $url = 'wp-admin/admin.php?page=automatorwp_import_automation';
773 +
774 + // Setup the automation args
775 + // Only include those parameters if they are different from their default value to reduce the URL length
776 + if( ! empty( $automation->title ) ) {
777 + $url = add_query_arg( 'ti', $automation->title, $url );
778 + }
779 +
780 + if( $automation->type !== 'user' ) {
781 + $url = add_query_arg( 'ty', $automation->type, $url );
782 + }
783 +
784 + if( absint( $automation->sequential ) !== 0 ) {
785 + $url = add_query_arg( 's', $automation->sequential, $url );
786 + }
787 +
788 + if( absint( $automation->times_per_user ) !== 1 ) {
789 + $url = add_query_arg( 'tu', $automation->times_per_user, $url );
790 + }
791 +
792 + if( absint( $automation->times ) !== 0 ) {
793 + $url = add_query_arg( 'tim', $automation->times, $url );
794 + }
795 +
796 + $url .= automatorwp_get_automation_items_export_url( $automation_id );
797 +
798 + return $url;
799 +
800 +}
801 +
802 +/**
803 + * Turn automation items into an exportable URL
804 + *
805 + * @since 1.0.0
806 + *
807 + * @param int $automation_id The automation ID
808 + *
809 + * @return string
810 + */
811 +function automatorwp_get_automation_items_export_url( $automation_id ) {
812 +
813 + $item_types = array( 'trigger', 'action' );
814 + $false_url = 'a.php?b=c';
815 + $url = $false_url;
816 +
817 + // Loop all automation items
818 + foreach( $item_types as $item_type ) {
819 +
820 + // Get the items
821 + if( $item_type === 'trigger' ) {
822 + $items = automatorwp_get_automation_triggers( $automation_id );
823 + } else {
824 + $items = automatorwp_get_automation_actions( $automation_id );
825 + }
826 +
827 + $url_items = array();
828 +
829 + ct_setup_table( "automatorwp_{$item_type}s" );
830 +
831 + foreach( $items as $item ) {
832 +
833 + // Get the type args
834 + if( $item_type === 'trigger' ) {
835 + $type_args = automatorwp_get_trigger( $item->type );
836 + } else {
837 + $type_args = automatorwp_get_action( $item->type );
838 + }
839 +
840 + if( ! $type_args ) {
841 + continue;
842 + }
843 +
844 + // Setup the item options
845 + $options = array();
846 +
847 + // Special check for filters
848 + if( $item->type === 'filter' ) {
849 +
850 + $filter = ct_get_object_meta( $item->id, 'filter', true );
851 +
852 + $filter_args = automatorwp_get_filter( $filter );
853 +
854 + // If filter args found, append the filter options to the type options
855 + if( $filter_args ) {
856 + $type_args['options'] = array_merge( $type_args['options'], $filter_args['options'] );
857 + }
858 +
859 + }
860 +
861 + foreach( $type_args['options'] as $option => $option_args ) {
862 +
863 + // Skip option if not has fields
864 + if( ! isset( $option_args['fields'] ) ) {
865 + continue;
866 + }
867 +
868 + foreach( $option_args['fields'] as $field_id => $field_args ) {
869 +
870 + /**
871 + * Filter to exclude a meta on export this item through URL
872 + * $item_type: trigger | action
873 + * $item->type: The trigger or action type
874 + * $field_id: The meta key
875 + *
876 + * @since 1.0.0
877 + *
878 + * @param bool $exclude
879 + *
880 + * @return bool
881 + */
882 + $exclude = apply_filters( "automatorwp_export_url_{$item_type}_{$item->type}_meta_{$field_id}_excluded", false );
883 +
884 + // Skip if meta gets excluded on export through URL
885 + if( $exclude ) {
886 + continue;
887 + }
888 +
889 + $field_value = ct_get_object_meta( $item->id, $field_id, true );
890 +
891 + // Skip options with
892 + if( isset( $field_args['default'] ) && $field_args['default'] == $field_value ) {
893 + continue;
894 + }
895 +
896 + // Skip if no value entered
897 + if( empty( $field_value ) ) {
898 + continue;
899 + }
900 +
901 + $options[$field_id] = urlencode( maybe_serialize( $field_value ) );
902 +
903 + }
904 +
905 + }
906 +
907 + $url_items[] = array(
908 + 'i' => $item->id,
909 + 't' => $item->type,
910 + 'o' => $options,
911 + );
912 +
913 + }
914 +
915 + ct_reset_setup_table();
916 +
917 + // Add the items to the URL
918 + if( $item_type === 'trigger' ) {
919 + $prefix = 't';
920 + } else {
921 + $prefix = 'a';
922 + }
923 +
924 + // Pull all url items to reduce the URL length
925 + $url = automatorwp_pull_array_for_export_url( $url_items, $url, $prefix );
926 +
927 + }
928 +
929 + // Remove the false URL part
930 + $url = str_replace( $false_url, '', $url );
931 +
932 + return $url;
933 +
934 +}
935 +
936 +/**
937 + * Helper function to pull array elements to an exportable URL
938 + *
939 + * @since 1.0.0
940 + *
941 + * @param array $array
942 + * @param string $url
943 + * @param string $prefix
944 + *
945 + * @return string
946 + */
947 +function automatorwp_pull_array_for_export_url( $array, $url, $prefix = '' ) {
948 +
949 + foreach( $array as $key => $value ) {
950 +
951 + if( is_array( $value ) ) {
952 +
953 + $url = automatorwp_pull_array_for_export_url( $value, $url, "{$prefix}-{$key}" );
954 +
955 + } else {
956 + $url = add_query_arg( "{$prefix}-{$key}", $value, $url );
957 + }
958 +
959 + }
960 +
961 + return $url;
962 +
963 +}
964 +
965 +/**
966 + * Helper function to get array elements from an exportable URL
967 + *
968 + * @since 1.0.0
969 + *
970 + * @param array $request
971 + * @param string $prefix
972 + *
973 + * @return array
974 + */
975 +function automatorwp_get_array_from_export_url( $request, $prefix = '' ) {
976 +
977 + $params = array();
978 + $params[$prefix] = array();
979 +
980 + foreach( $request as $key => $value ) {
981 +
982 + // Skip if not starts with the required prefix
983 + if( ! automatorwp_starts_with( $key, "{$prefix}-" ) ) {
984 + continue;
985 + }
986 +
987 + $keys = explode( '-', $key );
988 + $length = count( $keys );
989 +
990 + $array = &$params[$prefix];
991 +
992 + foreach ( $keys as $i => $sub_key ) {
993 +
994 + // Skip the first sub key
995 + if( $i === 0 ) {
996 + continue;
997 + }
998 +
999 + // Create the sub key
1000 + if( ! isset( $array[$sub_key] ) ) {
1001 + $array[$sub_key] = array();
1002 + }
1003 +
1004 + // If is the last sub key, assign the original value
1005 + if( $i === ( $length - 1 ) ) {
1006 + $array[$sub_key] = $value;
1007 + }
1008 +
1009 + $array = &$array[$sub_key];
1010 +
1011 + }
1012 +
1013 + }
1014 +
1015 + return $params[$prefix];
1016 +
1017 +}
1018 +
1019 +/**
1020 + * Helper function to meet if is a scheduled automation
1021 + *
1022 + * @param stdClass $automation
1023 + *
1024 + * @return bool
1025 + */
1026 +function automatorwp_is_automation_scheduled( $automation ) {
1027 +
1028 + $now = current_time( 'timestamp' );
1029 + $date = strtotime( $automation->date );
1030 +
1031 + return ( $date > $now );
1032 +
1033 +}
1034 +
1035 +/**
1036 + * Helper function to meet if is an expired automation
1037 + *
1038 + * @param stdClass $automation
1039 + *
1040 + * @return bool
1041 + */
1042 +function automatorwp_is_automation_expired( $automation ) {
1043 +
1044 + // Ensure that automation object is up-to-date
1045 + if( ! property_exists( $automation, 'expiration' ) ) return false;
1046 + // Bail if automation is not active
1047 + if( $automation->status !== 'active' ) return false;
1048 +
1049 + $now = current_time( 'timestamp' );
1050 +
1051 + $expiration = strtotime( $automation->expiration );
1052 + $date = strtotime( $automation->date );
1053 +
1054 + // Only check this if automation has been correctly setup
1055 + if( $expiration > 0 && $expiration > $date ) {
1056 + return ( $expiration < $now );
1057 + }
1058 +
1059 + return false;
1060 +
1061 +}
1062 +
1063 +/**
1064 + * Helper function to activate an automation
1065 + *
1066 + * @param int $automation_id
1067 + *
1068 + * @return bool
1069 + */
1070 +function automatorwp_activate_automation( $automation_id ) {
1071 +
1072 + ct_setup_table( 'automatorwp_automations' );
1073 +
1074 + $automation = ct_get_object( $automation_id );
1075 +
1076 + // Bail if automation does not exists
1077 + if( ! $automation ) {
1078 + ct_reset_setup_table();
1079 + return false;
1080 + }
1081 +
1082 + $automation = ( array ) $automation;
1083 + $automation['status'] = 'active';
1084 +
1085 + $result = ct_update_object( $automation );
1086 +
1087 + ct_reset_setup_table();
1088 +
1089 + return true;
1090 +}
1091 +
1092 +/**
1093 + * Helper function to activate an automation
1094 + *
1095 + * @param int $automation_id
1096 + *
1097 + * @return bool
1098 + */
1099 +function automatorwp_deactivate_automation( $automation_id ) {
1100 +
1101 + ct_setup_table( 'automatorwp_automations' );
1102 +
1103 + $automation = ct_get_object( $automation_id );
1104 +
1105 + // Bail if automation does not exists
1106 + if( ! $automation ) {
1107 + ct_reset_setup_table();
1108 + return false;
1109 + }
1110 +
1111 + $automation = ( array ) $automation;
1112 + $automation['status'] = 'inactive';
1113 +
1114 + $result = ct_update_object( $automation );
1115 +
1116 + ct_reset_setup_table();
1117 +
1118 + return true;
1119 +}