PluginProbe
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI / 6.0.1
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI v6.0.1
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
automatorwp / includes / custom-tables / automations.php

automations.php in AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI 6.0.1, at includes/custom-tables/automations.php

1,100 lines 37.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Automations
4 *
5 * @package AutomatorWP\Custom_Tables\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 * Custom Table Labels
14 *
15 * @since 1.0.0
16 *
17 * @return array
18 */
19 function automatorwp_automations_labels() {
20
21 return array(
22 'singular' => __( 'Automation', 'automatorwp' ),
23 'plural' => __( 'Automations', 'automatorwp' ),
24 'labels' => array(
25 'list_menu_title' => __( 'Automations', 'automatorwp' ),
26 ),
27 );
28
29 }
30 add_filter( 'ct_automatorwp_automations_labels', 'automatorwp_automations_labels' );
31
32 /**
33 * Parse query args for automations
34 *
35 * @since 1.0.0
36 *
37 * @param string $where
38 * @param CT_Query $ct_query
39 *
40 * @return string
41 */
42 function automatorwp_automations_query_where( $where, $ct_query ) {
43
44 global $ct_table;
45
46 if( $ct_table->name !== 'automatorwp_automations' ) {
47 return $where;
48 }
49
50 $table_name = $ct_table->db->table_name;
51
52 // Shorthand
53 $qv = $ct_query->query_vars;
54
55 // Status
56 $where .= automatorwp_custom_table_where( $qv, 'status', 'status', 'string' );
57
58 // Type
59 $where .= automatorwp_custom_table_where( $qv, 'type', 'type', 'string' );
60
61 // User ID
62 $where .= automatorwp_custom_table_where( $qv, 'user_id', 'user_id', 'integer' );
63
64 return $where;
65 }
66 add_filter( 'ct_query_where', 'automatorwp_automations_query_where', 10, 2 );
67
68 /**
69 * Parse query args to be applied on JOIN clause
70 *
71 * @since 2.0.0
72 *
73 * @param string $join
74 * @param CT_Query $ct_query
75 *
76 * @return string
77 */
78 function automatorwp_automations_query_join( $join, $ct_query ) {
79
80 global $ct_table;
81
82 if( $ct_table->name !== 'automatorwp_automations' ) {
83 return $join;
84 }
85
86 $table_name = $ct_table->db->table_name;
87 $meta_table_name = $ct_table->meta->db->table_name;
88
89 // Shorthand
90 $qv = $ct_query->query_vars;
91
92 // Parent post type
93 if( isset( $qv['orderby'] ) && $qv['orderby'] === 'completions' ) {
94 $join .= " LEFT JOIN {$meta_table_name} am ON ( am.id = {$table_name}.id AND am.meta_key = 'completions' )";
95 }
96
97 return $join;
98
99 }
100 add_filter( 'ct_query_join', 'automatorwp_automations_query_join', 10, 2 );
101
102 /**
103 * Parse query args to be applied on ORDER BY clause
104 *
105 * @since 2.0.0
106 *
107 * @param string $orderby
108 * @param CT_Query $ct_query
109 *
110 * @return string
111 */
112 function automatorwp_automations_query_orderby( $orderby, $ct_query ) {
113
114 global $ct_table;
115
116 if( $ct_table->name !== 'automatorwp_automations' ) {
117 return $orderby;
118 }
119
120 // Shorthand
121 $qv = $ct_query->query_vars;
122
123 // Parent post type
124 if( isset( $qv['orderby'] ) && $qv['orderby'] === 'completions' ) {
125 $order = 'DESC';
126
127 if( isset( $qv['order'] ) && $qv['order'] === 'ASC' ) {
128 $order = 'ASC';
129 }
130
131 $orderby = "CAST( am.meta_value AS UNSIGNED ) " . $order;
132 }
133
134 return $orderby;
135
136 }
137 add_filter( 'ct_query_orderby', 'automatorwp_automations_query_orderby', 10, 2 );
138
139 /**
140 * Define the search fields for automations
141 *
142 * @since 1.0.0
143 *
144 * @param array $search_fields
145 *
146 * @return array
147 */
148 function automatorwp_automations_search_fields( $search_fields ) {
149
150 $search_fields[] = 'title';
151
152 return $search_fields;
153
154 }
155 add_filter( 'ct_query_automatorwp_automations_search_fields', 'automatorwp_automations_search_fields' );
156
157 /**
158 * Parse search query args for automations
159 *
160 * @since 1.3.9.7
161 *
162 * @param string $search
163 * @param CT_Query $ct_query
164 *
165 * @return string
166 */
167 function automatorwp_automations_query_search( $search, $ct_query ) {
168
169 global $ct_table, $wpdb;
170
171 if( $ct_table->name !== 'automatorwp_automations' ) {
172 return $search;
173 }
174
175 $table_name = $ct_table->db->table_name;
176
177 // Shorthand
178 $qv = $ct_query->query_vars;
179
180 // Check if is search and query is not filtered by an specific user
181 if( isset( $qv['s'] ) && ! empty( $qv['s'] ) && ! isset( $qv['user_id'] ) ) {
182
183 // Made a user sub-search to retrieve them
184 $users = get_users( array(
185 'search' => sprintf( '*%s*', $qv['s'] ),
186 'search_columns' => array(
187 'user_login',
188 'user_email',
189 'display_name',
190 ),
191 'fields' => 'ID',
192 ) );
193
194 if( ! empty( $users ) ) {
195 $search .= " AND ( {$table_name}.user_id IN (" . implode( ',', array_map( 'absint', $users ) ) . ") )";
196 }
197
198 }
199
200 return $search;
201
202 }
203 add_filter( 'ct_query_search', 'automatorwp_automations_query_search', 10, 2 );
204
205 /**
206 * Define the field for automations views
207 *
208 * @since 1.0.0
209 *
210 * @param string $field_id
211 *
212 * @return string
213 */
214 function automatorwp_automations_views_field( $field_id = '' ) {
215 return 'status';
216 }
217 add_filter( 'ct_list_automatorwp_automations_views_field', 'automatorwp_automations_views_field' );
218
219 /**
220 * Define the field labels for automations views
221 *
222 * @since 1.0.0
223 *
224 * @param array $field_labels
225 *
226 * @return array
227 */
228 function automatorwp_automations_views_field_labels( $field_labels = array() ) {
229 return automatorwp_get_automation_statuses();
230 }
231 add_filter( 'ct_list_automatorwp_automations_views_field_labels', 'automatorwp_automations_views_field_labels' );
232
233 /**
234 * Columns for automations list view
235 *
236 * @since 1.0.0
237 *
238 * @param array $columns
239 *
240 * @return array
241 */
242 function automatorwp_manage_automations_columns( $columns = array() ) {
243
244 $columns['title'] = __( 'Title', 'automatorwp' );
245 $columns['type'] = __( 'Type', 'automatorwp' );
246 $columns['triggers'] = __( 'Triggers', 'automatorwp' );
247 $columns['actions'] = __( 'Actions', 'automatorwp' );
248 $columns['user_id'] = __( 'Author', 'automatorwp' );
249 $columns['completions'] = __( 'Completions', 'automatorwp' );
250 $columns['status'] = __( 'Status', 'automatorwp' );
251 $columns['date'] = __( 'Date', 'automatorwp' );
252
253 return $columns;
254 }
255 add_filter( 'manage_automatorwp_automations_columns', 'automatorwp_manage_automations_columns' );
256
257 /**
258 * Sortable columns for automations list view
259 *
260 * @since 1.0.0
261 *
262 * @param array $sortable_columns
263 *
264 * @return array
265 */
266 function automatorwp_manage_automations_sortable_columns( $sortable_columns ) {
267
268 $sortable_columns['title'] = array( 'title', false );
269 $sortable_columns['type'] = array( 'type', false );
270 $sortable_columns['user_id'] = array( 'user_id', false );
271 $sortable_columns['completions'] = array( 'completions', true );
272 $sortable_columns['status'] = array( 'status', false );
273 $sortable_columns['date'] = array( 'date', true );
274
275 return $sortable_columns;
276
277 }
278 add_filter( 'manage_automatorwp_automations_sortable_columns', 'automatorwp_manage_automations_sortable_columns' );
279
280 /**
281 * Columns rendering for automations list view
282 *
283 * @since 1.0.0
284 *
285 * @param string $column_name
286 * @param integer $object_id
287 */
288 function automatorwp_manage_automations_custom_column( $column_name, $object_id ) {
289
290 // Setup vars
291 $automation = ct_get_object( $object_id );
292
293 if( automatorwp_is_automation_expired( $automation ) ) {
294 automatorwp_deactivate_automation( $automation->id );
295 $automation->status = 'inactive';
296 }
297
298 switch( $column_name ) {
299 case 'title':
300 $title = ! empty( $automation->title ) ? $automation->title : __( '(No title)', 'automatorwp' ); ?>
301 <strong><a href="<?php echo ct_get_edit_link( 'automatorwp_automations', $automation->id ); ?>"><?php echo esc_html( $title ); ?></a></strong>
302 <?php
303
304 break;
305 case 'type':
306 $types = automatorwp_get_automation_types();
307 $type = isset( $types[$automation->type] ) ? $types[$automation->type]['label'] : $automation->type;
308 ?>
309
310 <span class="automatorwp-automation-type automatorwp-automation-type-<?php echo esc_attr( $automation->type ); ?>">
311 <?php if ( isset( $types[$automation->type] ) ) : ?>
312 <div class="automatorwp-integration-icon cmb-tooltip">
313 <img src="<?php echo esc_attr( $types[$automation->type]['image'] ); ?>" alt="<?php echo esc_attr( $types[$automation->type]['label'] ); ?>">
314 <span class="cmb-tooltip-desc cmb-tooltip-top"><?php echo esc_html( $types[$automation->type]['label'] ); ?></span>
315 </div>
316 <?php else : ?>
317 <div class="automatorwp-integration-icon cmb-tooltip">
318 <img src="<?php echo esc_attr( AUTOMATORWP_URL . 'assets/img/integration-missing.svg' ); ?>" alt="<?php echo esc_attr( __( 'Missing plugin', 'automatorwp' ) ); ?>">
319 <span class="cmb-tooltip-desc cmb-tooltip-top"><?php echo esc_html( $automation->type ); ?></span>
320 </div>
321 <?php endif; ?>
322 <?php echo esc_html( $type ); ?>
323 </span>
324
325 <?php
326
327 break;
328 case 'triggers':
329 $triggers = automatorwp_get_automation_triggers( $automation->id );
330
331 foreach( $triggers as $trigger ) {
332
333 $type_args = automatorwp_get_trigger( $trigger->type );
334
335 if( $type_args ) {
336 $integration = automatorwp_get_integration( $type_args['integration'] );
337
338 $integration = apply_filters( 'automatorwp_automations_column_triggers_integration', $integration, $trigger, 'trigger', $automation );
339
340 if( $integration ) : ?>
341
342 <div class="automatorwp-integration-icon cmb-tooltip">
343 <img src="<?php echo esc_attr( $integration['icon'] ); ?>" alt="<?php echo esc_attr( $integration['label'] ); ?>">
344 <span class="cmb-tooltip-desc cmb-tooltip-top"><?php echo esc_html( $integration['label'] ); ?></span>
345 </div>
346
347 <?php endif;
348 } else { ?>
349
350 <div class="automatorwp-integration-icon cmb-tooltip">
351 <img src="<?php echo esc_attr( AUTOMATORWP_URL . 'assets/img/integration-missing.svg' ); ?>" alt="<?php echo esc_attr( __( 'Missing plugin', 'automatorwp' ) ); ?>">
352 <span class="cmb-tooltip-desc cmb-tooltip-top"><?php echo esc_html( $trigger->type ); ?></span>
353 </div>
354
355 <?php }
356
357 echo $trigger->title . '<br>';
358 }
359
360 break;
361 case 'actions':
362 $actions = automatorwp_get_automation_actions( $automation->id );
363
364 foreach( $actions as $action ) {
365
366 $type_args = automatorwp_get_action( $action->type );
367
368 if( $type_args ) {
369 $integration = automatorwp_get_integration( $type_args['integration'] );
370
371 if( $action->type === 'automatorwp_anonymous_user' ) {
372 $integration['icon'] = AUTOMATORWP_URL . 'assets/img/automatorwp-anonymous.svg';
373 }
374
375 if( $integration ) : ?>
376
377 <div class="automatorwp-integration-icon cmb-tooltip">
378 <img src="<?php echo esc_attr( $integration['icon'] ); ?>" alt="<?php echo esc_attr( $integration['label'] ); ?>">
379 <span class="cmb-tooltip-desc cmb-tooltip-top"><?php echo esc_html( $integration['label'] ); ?></span>
380 </div>
381
382 <?php endif;
383
384 } else { ?>
385
386 <div class="automatorwp-integration-icon cmb-tooltip">
387 <img src="<?php echo esc_attr( AUTOMATORWP_URL . 'assets/img/integration-missing.svg' ); ?>" alt="<?php echo esc_attr( __( 'Missing plugin', 'automatorwp' ) ); ?>">
388 <span class="cmb-tooltip-desc cmb-tooltip-top"><?php echo esc_html( $action->type ); ?></span>
389 </div>
390
391 <?php }
392
393 echo $action->title . '<br>';
394 }
395
396 break;
397 case 'user_id':
398 $user = get_userdata( $automation->user_id );
399
400 if( $user ) {
401
402 if( current_user_can( 'edit_users' ) ) {
403 ?>
404
405 <a href="<?php echo get_edit_user_link( $user->ID ); ?>"><?php echo $user->display_name . ' (' . $user->user_login . ')'; ?></a>
406
407 <?php
408 } else {
409 echo $user->display_name;
410 }
411
412 }
413 break;
414 case 'completions':
415 $completions = ct_get_object_meta( $automation->id, 'completions', true );
416
417 if( empty( $completions ) ) {
418 $completions = automatorwp_get_object_completion_times( $automation->id, 'automation' );
419 ct_update_object_meta( $automation->id, 'completions', $completions );
420 }
421
422 $completions = absint( $completions );
423 $times = absint( $automation->times );
424
425 if( $times === 0 ) : ?>
426 <span class="automatorwp-automation-completions"><?php echo $completions; ?></span>
427 <?php else : ?>
428 <span class="automatorwp-automation-completions <?php echo ( $completions >= $times ? 'automatorwp-automation-completions-completed' : '' ) ?>"><?php echo $completions . '/' . $times; ?></span>
429 <?php endif;
430 break;
431 case 'status':
432 $statuses = automatorwp_get_automation_statuses();
433 $status = isset( $statuses[$automation->status] ) ? $statuses[$automation->status] : esc_html( $automation->status );
434 ?>
435
436 <span class="automatorwp-automation-status automatorwp-automation-status-<?php echo esc_attr( $automation->status ); ?>"><?php echo $status; ?></span>
437
438 <?php
439
440 // Expiration
441 $now = current_time( 'timestamp' );
442 $expiration = strtotime( $automation->expiration );
443
444 if( $expiration > 0 ) {
445 $expires_text = $expiration > $now ? __( 'Expires on', 'automatorwp' ) : __( 'Expired on', 'automatorwp' );
446 ?>
447
448 <div class="automatorwp-automation-expiration">
449 <?php echo esc_html( $expires_text ); ?> <abbr title="<?php echo date( 'Y/m/d g:i:s a', $expiration ); ?>"><?php echo date( 'Y/m/d', $expiration ); ?></abbr>
450 </div>
451
452 <?php
453 }
454
455 break;
456 case 'date':
457 $date = strtotime( $automation->date ); ?>
458
459 <abbr title="<?php echo date( 'Y/m/d g:i:s a', $date ); ?>"><?php echo date( 'Y/m/d', $date ); ?></abbr>
460
461 <?php
462 break;
463 }
464 }
465 add_action( 'manage_automatorwp_automations_custom_column', 'automatorwp_manage_automations_custom_column', 10, 2 );
466
467 /**
468 * Filters the array of row action links on the item list table.
469 *
470 * @since 1.4.8
471 *
472 * @param array $actions An array of row action links. Defaults are 'Edit' and 'Delete Permanently'.
473 * @param stdClass $item The item object.
474 *
475 * @return array
476 */
477 function automatorwp_automations_custom_row_actions( $actions, $item ) {
478
479 global $ct_table;
480
481 $primary_key = $ct_table->db->primary_key;
482
483 // List link + object ID + action delete
484 $url = $ct_table->views->list->get_link();
485 $url = add_query_arg( array( $primary_key => $item->$primary_key ), $url );
486
487 $actions['clone'] = sprintf(
488 '<a href="%s" class="automatorwp-clone-automation" aria-label="%s">%s</a>',
489 add_query_arg( array( 'automatorwp-action' => 'clone_automation' ), $url ),
490 esc_attr( __( 'Clone', 'automatorwp' ) ),
491 esc_html__( 'Clone', 'automatorwp' )
492 );
493
494 $actions['url_export'] = sprintf(
495 '<a href="%s" class="automatorwp-url-export-automation" aria-label="%s">%s</a>',
496 add_query_arg( array( 'automatorwp-action' => 'url_export_automation' ), $url ),
497 esc_attr( __( 'Export', 'automatorwp' ) ),
498 esc_html__( 'Export', 'automatorwp' )
499 );
500
501 if( isset( $actions['delete'] ) ) {
502
503 // Remove the delete action to move it to the end
504 unset( $actions['delete'] );
505
506 // Override the delete action
507 $actions['delete'] = sprintf(
508 '<a href="%s" class="submitdelete" onclick="%s" aria-label="%s">%s</a>',
509 ct_get_delete_link( $ct_table->name, $item->$primary_key ),
510 "return confirm('" .
511 esc_attr( __( "Are you sure you want to delete this automation?\\n\\nClick \\'Cancel\\' to go back, \\'OK\\' to confirm the delete.", 'automatorwp' ) ) .
512 "');",
513 esc_attr( __( 'Delete', 'automatorwp' ) ),
514 esc_html__( 'Delete', 'automatorwp' )
515 );
516 }
517
518 return $actions;
519
520 }
521 add_filter( 'automatorwp_automations_row_actions', 'automatorwp_automations_custom_row_actions', 10, 2 );
522
523 /**
524 * Handler for the clone automation action.
525 *
526 * @since 1.4.8
527 *
528 * @param array $request Request parameters
529 */
530 function automatorwp_action_clone_automation( $request ) {
531
532
533 // Bail if automation ID not received
534 if( ! isset( $request['id'] ) ) {
535 return;
536 }
537
538 $automation_id = absint( $request['id'] );
539
540 // Bail if automation ID is not correct received
541 if( $automation_id === 0 ) {
542 return;
543 }
544
545 $ct_table = ct_setup_table( 'automatorwp_automations' );
546
547 $automation = ct_get_object( $automation_id );
548 $edit_capability = $ct_table->cap->edit_item;
549 $url = $ct_table->views->list->get_link();
550
551 ct_reset_setup_table();
552
553 // Check if automation exists and current user can edit it
554 if( ! $automation ) {
555 return;
556 }
557
558 // Check if automation exists and current user can edit it
559 if( ! current_user_can( $edit_capability, $automation->id ) ) {
560 return;
561 }
562
563 ct_reset_setup_table();
564
565 $new_automation_id = automatorwp_clone_automation( $automation_id, get_current_user_id() );
566
567 if( $new_automation_id ) {
568
569 $redirect = ( isset( $request['redirect'] ) ? $request['redirect'] : false );
570
571 if( $redirect ) {
572 $url = $ct_table->views->edit->get_link();
573 $url = add_query_arg( array( 'message' => 'automation_cloned' ), $url );
574 $url = add_query_arg( array( 'id' => $new_automation_id ), $url );
575
576 // Redirect to the new automation
577 wp_redirect( $url );
578 exit;
579 }
580
581 $url = add_query_arg( array( 'message' => 'automation_cloned' ), $url );
582 $url = add_query_arg( array( 'new_id' => $new_automation_id ), $url );
583 } else {
584 $url = add_query_arg( array( 'message' => 'automation_not_cloned' ), $url );
585 }
586
587 wp_redirect( $url );
588 exit;
589
590 }
591 add_action( 'automatorwp_action_clone_automation', 'automatorwp_action_clone_automation' );
592
593 function automatorwp_automations_admin_notices() {
594
595 $messages = array(
596 'automation_cloned' => __( 'Automation cloned successfully.', 'automatorwp' ),
597 'automation_not_cloned' => __( 'Automation could not be cloned.', 'automatorwp' ),
598 );
599
600 // If the new id exists, add a link to edit it directly
601 if( isset( $_GET['new_id'] ) && absint( $_GET['new_id'] ) !== 0 ) {
602 $messages['automation_cloned'] = sprintf(
603 __( 'Automation cloned successfully! <a href="%s">Edit the new automation</a>.', 'automatorwp' ),
604 ct_get_edit_link( 'automatorwp_automations', absint( $_GET['new_id'] ) )
605 );
606 }
607
608 /**
609 * Filters the automations table updated messages.
610 *
611 * @since 1.0.0
612 *
613 * @param array $messages Post updated messages. For defaults @see $messages declarations above.
614 */
615 $messages = apply_filters( 'automatorwp_automations_messages', $messages );
616
617 // Setup screen message
618 if ( isset( $_GET['message'] ) && isset( $messages[$_GET['message']] ) ) {
619 echo '<div id="message" class="updated notice is-dismissible"><p>' . esc_html( $messages[$_GET['message']] ) . '</p></div>';
620 }
621
622 }
623 add_action( 'admin_notices', 'automatorwp_automations_admin_notices' );
624
625 /**
626 * Default data when creating a new item (similar to WP auto draft) see ct_insert_object()
627 *
628 * @since 1.0.0
629 *
630 * @param array $default_data
631 *
632 * @return array
633 */
634 function automatorwp_automations_default_data( $default_data = array() ) {
635
636 // Handle the type during creation
637 $types = automatorwp_get_automation_types();
638 $type = ( isset( $_GET['type'] ) ? sanitize_text_field( $_GET['type'] ) : 'user' );
639
640 if( ! isset( $types[$type] ) ) {
641 $type = 'user';
642 }
643
644 $default_data['type'] = $type;
645 $default_data['user_id'] = get_current_user_id();
646 $default_data['sequential'] = 0;
647 $default_data['times_per_user'] = 0;
648 $default_data['times'] = 0;
649 $default_data['status'] = 'inactive';
650 $default_data['date'] = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
651 $default_data['expiration'] = '';
652 $default_data['title'] = '';
653
654 return $default_data;
655 }
656 add_filter( 'ct_automatorwp_automations_default_data', 'automatorwp_automations_default_data' );
657
658 /**
659 * Turns array of date and time into a valid mysql date on update object data
660 *
661 * @since 1.0.0
662 *
663 * @param array $object_data
664 * @param array $original_object_data
665 *
666 * @return array
667 */
668 function automatorwp_automations_insert_automation_data( $object_data, $original_object_data ) {
669
670 global $ct_table;
671
672 // If not is our custom table, return
673 if( $ct_table->name !== 'automatorwp_automations' ) {
674 return $object_data;
675 }
676
677 // Fix date format
678 if( isset( $object_data['date'] ) && ! empty( $object_data['date'] ) ) {
679 // Covers CMB2 format with array( 'date' => '', 'time' => '' )
680 if( is_array( $object_data['date'] ) ) {
681 $object_data['date'] = implode( ' ', $object_data['date'] );
682 }
683
684 $object_data['date'] = date( 'Y-m-d H:i:s', strtotime( $object_data['date'] ) );
685 }
686
687 // Fix expiration format
688 if( isset( $object_data['expiration'] ) ) {
689 // Covers CMB2 format with array( 'date' => '', 'time' => '' )
690 if( is_array( $object_data['expiration'] ) ) {
691 $object_data['expiration'] = trim( implode( ' ', $object_data['expiration'] ) );
692 }
693
694 if( ! empty( $object_data['expiration'] ) ) {
695 $now = current_time( 'timestamp' );
696 $expiration = strtotime( $object_data['expiration'] );
697
698 if( $object_data['status'] === 'active' && $expiration < $now ) {
699 // Prevent to have an older expiration (which will deactivate constantly the automation)
700 $object_data['expiration'] = '0000-00-00 00:00:00';
701 } else {
702 $object_data['expiration'] = date( 'Y-m-d H:i:s', $expiration );
703 }
704 } else {
705 // Never expires expiration
706 $object_data['expiration'] = '0000-00-00 00:00:00';
707 }
708
709 }
710
711 // Handle sequential saving
712 $object_data['sequential'] = ( isset( $_POST['sequential'] ) ? 1 : 0 );
713
714 return $object_data;
715
716 }
717 add_filter( 'ct_insert_object_data', 'automatorwp_automations_insert_automation_data', 10, 2 );
718
719 /**
720 * Register custom CMB2 meta boxes
721 *
722 * @since 1.0.0
723 */
724 function automatorwp_automations_meta_boxes( ) {
725
726 // Title
727 automatorwp_add_meta_box(
728 'automatorwp-automations-title',
729 __( 'Title', 'automatorwp' ),
730 'automatorwp_automations',
731 array(
732 'title' => array(
733 'name' => __( 'Title', 'automatorwp' ),
734 'type' => 'text',
735 'classes' => 'automatorwp-auto-save',
736 'attributes' => array(
737 'placeholder' => __( 'Enter title here', 'automatorwp' ) . '...',
738 )
739 ),
740 ),
741 array(
742 'priority' => 'core',
743 )
744 );
745
746 // Save Changes
747 automatorwp_add_meta_box(
748 'automatorwp-automations-save-changes',
749 __( 'Save Changes', 'automatorwp' ),
750 'automatorwp_automations',
751 array(
752 'type' => array(
753 'name' => __( 'Type', 'automatorwp' ),
754 'type' => 'select',
755 'options' => automatorwp_get_automation_types_labels(),
756 'js_controls' => automatorwp_js_controls_options( 'star-filled' ),
757 'before_row' => 'js_controls_before',
758 'after_row' => 'js_controls_after',
759 ),
760 'times_per_user' => array(
761 'name' => __( 'Times per user', 'automatorwp' ),
762 'desc' => __( 'Maximum number of times a user can complete this automation. Set it to 0 for unlimited.', 'automatorwp' ),
763 'type' => 'input',
764 'attributes' => array(
765 'type' => 'number',
766 'min' => '0',
767 ),
768 'classes' => 'automatorwp-has-tooltip',
769 'after_field' => 'automatorwp_tooltip_cb',
770 'js_controls' => automatorwp_js_controls_options( 'update' ),
771 'before_row' => 'js_controls_before',
772 'after_row' => 'js_controls_after',
773 ),
774 'times' => array(
775 'name' => __( 'Total times', 'automatorwp' ),
776 'desc' => __( 'Maximum number of times this automation can be completed. Set it to 0 for unlimited.', 'automatorwp' ),
777 'type' => 'input',
778 'attributes' => array(
779 'type' => 'number',
780 'min' => '0',
781 ),
782 'classes' => 'automatorwp-has-tooltip',
783 'after_field' => 'automatorwp_tooltip_cb',
784 'js_controls' => automatorwp_js_controls_options( 'admin-site' ),
785 'before_row' => 'js_controls_before',
786 'after_row' => 'js_controls_after',
787 ),
788 'user_id' => array(
789 'name' => __( 'Author', 'automatorwp' ),
790 'type' => 'select',
791 'classes' => 'automatorwp-user-selector',
792 'options_cb' => 'automatorwp_options_cb_users',
793 'display_cb' => 'automatorwp_display_cb_users',
794 'js_controls' => automatorwp_js_controls_options( 'admin-users' ),
795 'before_row' => 'js_controls_before',
796 'after_row' => 'js_controls_after',
797 ),
798 'status' => array(
799 'name' => __( 'Status', 'automatorwp' ),
800 'type' => 'select',
801 'options_cb' => 'automatorwp_options_cb_automation_statuses',
802 'js_controls' => array(
803 'icon' => 'dashicons-post-status',
804 'save_button' => __( 'Save', 'automatorwp' ),
805 'save_button_classes' => 'button button-compact button-primary',
806 'cancel_button_classes' => 'button button-compact automatorwp-button-danger',
807 ),
808 'before_row' => 'js_controls_before',
809 'after_row' => 'js_controls_after',
810 ),
811 'date' => array(
812 'name' => __( 'Created on', 'automatorwp' ),
813 'desc' => __( 'Automation will take effect based on this date. You can schedule <strong>future</strong> automations by setting this field with a future date.', 'automatorwp' )
814 . '<br>' . __( '<strong>Note:</strong> For future automations, status need to be <strong>active</strong> because setting a future date won\'t update the status automatically.', 'automatorwp' ),
815 'type' => 'text_datetime_timestamp',
816 'date_format' => automatorwp_get_date_format( array( 'm/d/Y', 'd/m/Y' ), 'm/d/Y' ),
817 'time_format' => 'H:i',
818 'escape_cb' => 'automatorwp_automation_date_escape_cb',
819 'display_cb' => 'automatorwp_automation_date_display_cb',
820 'attributes' => array(
821 'autocomplete' => 'off'
822 ),
823 'classes' => 'automatorwp-has-tooltip',
824 'after_field' => 'automatorwp_tooltip_cb',
825 'js_controls' => automatorwp_js_controls_options( 'calendar' ),
826 'before_row' => 'js_controls_before',
827 'after_row' => 'js_controls_after', // Handled on automatorwp_automations_publishing_actions()
828 ),
829 'expiration' => array(
830 'name' => __( 'Expires on', 'automatorwp' ),
831 'desc' => __( 'Automation will be automatically set to <strong>inactive</strong> after this date. You can schedule the automation deactivation by setting this field with a future date.', 'automatorwp' ),
832 'type' => 'text_datetime_timestamp',
833 'date_format' => automatorwp_get_date_format( array( 'm/d/Y', 'd/m/Y' ), 'm/d/Y' ),
834 'time_format' => 'H:i',
835 'escape_cb' => 'automatorwp_automation_date_escape_cb',
836 'display_cb' => 'automatorwp_automation_date_display_cb',
837 'attributes' => array(
838 'autocomplete' => 'off'
839 ),
840 'classes' => 'automatorwp-has-tooltip',
841 'after_field' => 'automatorwp_tooltip_cb',
842 'js_controls' => automatorwp_js_controls_options( 'clock' ),
843 'before_row' => 'js_controls_before',
844 //'after_row' => 'js_controls_after', // Handled on automatorwp_automations_publishing_actions()
845 'after_row' => 'automatorwp_automations_publishing_actions',
846
847 ),
848 ),
849 array(
850 'priority' => 'default',
851 'context' => 'side',
852 )
853 );
854
855 // Notes
856 automatorwp_add_meta_box(
857 'automatorwp-automations-notes',
858 __( 'Notes', 'automatorwp' ) . cmb_tooltip_get_html( __( 'Use this box to add internal notes about this automation.', 'automatorwp' ) ),
859 'automatorwp_automations',
860 array(
861 'notes' => array(
862 'name' => __( 'Notes', 'automatorwp' ),
863 'type' => 'textarea',
864 'classes' => 'automatorwp-auto-save',
865 'attributes' => array(
866 'rows' => 8,
867 'placeholder' => __( 'Internal notes here', 'automatorwp' ) . '...',
868 )
869 ),
870 ),
871 array(
872 'priority' => 'default',
873 'context' => 'side',
874 )
875 );
876
877 }
878 add_action( 'cmb2_admin_init', 'automatorwp_automations_meta_boxes' );
879
880 /**
881 * Helper function to reduce code
882 *
883 * @param string $icon
884 * @return array
885 */
886 function automatorwp_js_controls_options( $icon = 'automatorwp' ) {
887 return array(
888 'icon' => 'dashicons-' . $icon,
889 'save_button' => __( 'Save', 'automatorwp' ),
890 'save_button_classes' => 'button button-compact button-primary',
891 'cancel_button_classes' => 'button button-compact automatorwp-button-danger',
892 );
893 }
894
895 /**
896 * Expiration escape callback.
897 *
898 * @param string $value Field value.
899 * @param array $field_args Array of field arguments.
900 * @param CMB2_Field $field The field object
901 */
902 function automatorwp_automation_date_escape_cb( $value, $field_args, $field ) {
903
904 if ( $value === '0000-00-00 00:00:00' )
905 $escaped_value = '';
906 else
907 $escaped_value = date( automatorwp_get_date_format( array( 'm/d/Y', 'd/m/Y' ), 'm/d/Y' ) . ' H:i', strtotime( $value ) );
908
909 return $escaped_value;
910
911 }
912
913 /**
914 * Expiration display callback.
915 *
916 * @param array $field_args Array of field arguments.
917 * @param CMB2_Field $field The field object
918 */
919 function automatorwp_automation_date_display_cb( $field_args, $field ) {
920 $value = $field->escaped_value(); ?>
921 <div class="<?php echo $field->row_classes(); ?>" data-fieldtype="text_date_timestamp">
922 <?php echo $value === '' ? __( 'Never Expires', 'automatorwp' ) : $value; ?>
923 </div>
924 <?php
925 }
926
927 /**
928 * Publishing actions
929 *
930 * @since 1.0.0
931 */
932 function automatorwp_automations_publishing_actions( $field_args, $field ) {
933
934 global $ct_table;
935
936 if( ! $ct_table ) {
937 return;
938 }
939
940 if( $ct_table->name !== 'automatorwp_automations' ) {
941 return;
942 }
943
944 // The JS controls after of the date field
945 js_controls_after( $field_args, $field );
946
947 $primary_key = $ct_table->db->primary_key;
948 $object_id = isset( $_GET[$primary_key] ) ? absint( $_GET[$primary_key] ) : 0;
949
950 $automation = ct_get_object( $object_id );
951
952 // Bail if automation doesn't exists
953 if( ! $automation ) {
954 return;
955 }
956
957 $actions = array();
958
959 $url = $ct_table->views->edit->get_link();
960 $url = add_query_arg( array( $primary_key => $object_id ), $url );
961 $url = add_query_arg( array( 'redirect' => true ), $url );
962
963 $actions['clone'] = sprintf(
964 '<a href="%s" class="automatorwp-clone-automation" aria-label="%s">%s</a>',
965 add_query_arg( array( 'automatorwp-action' => 'clone_automation' ), $url ),
966 esc_attr( __( 'Clone', 'automatorwp' ) ),
967 automatorwp_dashicon( 'admin-page' ) . esc_html__( 'Clone', 'automatorwp' )
968 );
969
970 $actions['url_export'] = sprintf(
971 '<a href="%s" class="automatorwp-url-export-automation" aria-label="%s">%s</a>',
972 add_query_arg( array( 'automatorwp-action' => 'url_export_automation' ), $url ),
973 esc_attr( __( 'Export', 'automatorwp' ) ),
974 automatorwp_dashicon( 'cloud-upload' ) . esc_html__( 'Export', 'automatorwp' )
975 );
976
977 /**
978 * Filters the array of edit action links on the automation edit page.
979 *
980 * @since 1.4.8
981 *
982 * @param array $actions An array of row action links.
983 * @param stdClass $automation The automation object.
984 *
985 * @return array
986 */
987 $actions = apply_filters( 'automatorwp_automations_edit_actions', $actions, $automation )
988 ?>
989 <div id="major-publishing-actions">
990
991 <?php if( $automation->status === 'inactive' ) : ?>
992
993 <div class="automatorwp-save-and-activate">
994 <?php submit_button( __( 'Save &amp; Activate', 'automatorwp' ), ' primary large', 'automatorwp-save-and-activate', false ); ?>
995 </div>
996
997 <div id="publishing-action">
998 <?php submit_button( __( 'Save Changes' ), 'large', 'ct-save', false ); ?>
999 </div>
1000
1001 <div class="clear"></div>
1002
1003 <?php else : ?>
1004
1005 <div id="publishing-action">
1006 <?php submit_button( __( 'Save Changes' ), 'primary large', 'ct-save', false ); ?>
1007 </div>
1008
1009 <?php endif; ?>
1010
1011 <div id="automatorwp-automation-actions">
1012 <?php foreach( $actions as $action => $action_html ) : ?>
1013 <div id="automatorwp-automation-action-<?php echo esc_attr( $action ); ?>" class="automatorwp-automation-action"><?php echo $action_html; ?></div>
1014 <?php endforeach; ?>
1015 </div>
1016
1017 <div id="delete-action">
1018 <?php
1019 printf(
1020 '<a href="%s" class="submitdelete deletion" onclick="%s" aria-label="%s">%s</a>',
1021 ct_get_delete_link( $ct_table->name, $object_id ),
1022 "return confirm('" .
1023 esc_attr( __( "Are you sure you want to delete this automation?\\n\\nClick \\'Cancel\\' to go back, \\'OK\\' to confirm the delete.", 'automatorwp' ) ) .
1024 "');",
1025 esc_attr( __( 'Delete', 'automatorwp' ) ),
1026 automatorwp_dashicon( 'trash' ) . esc_html__( 'Delete', 'automatorwp' )
1027 );
1028 ?>
1029 </div>
1030
1031 <div class="clear"></div>
1032
1033 </div>
1034
1035 <?php
1036 }
1037
1038 /**
1039 * Remove meta boxes
1040 *
1041 * @since 1.0.0
1042 */
1043 function automatorwp_automations_remove_meta_boxes() {
1044 // Removes submitdiv box
1045 remove_meta_box( 'submitdiv', 'automatorwp_automations', 'side' );
1046 }
1047 add_action( 'add_meta_boxes', 'automatorwp_automations_remove_meta_boxes' );
1048
1049 /**
1050 * On delete an automation
1051 *
1052 * @since 1.0.0
1053 *
1054 * @param int $object_id
1055 */
1056 function automatorwp_automations_delete_object( $object_id ) {
1057
1058 global $wpdb, $ct_table;
1059
1060 if( ! ( $ct_table instanceof CT_Table ) ) {
1061 return;
1062 }
1063
1064 if( $ct_table->name !== 'automatorwp_automations' ) {
1065 return;
1066 }
1067
1068 $logs = AutomatorWP()->db->logs;
1069 $logs_meta = AutomatorWP()->db->logs_meta;
1070
1071 // Delete all logs assigned to this action
1072 $wpdb->query( "DELETE l FROM {$logs} AS l WHERE l.object_id = {$object_id} AND l.type = 'automation'" );
1073
1074 // Delete orphaned log metas
1075 $wpdb->query( "DELETE lm FROM {$logs_meta} lm LEFT JOIN {$logs} l ON l.id = lm.id WHERE l.id IS NULL" );
1076
1077 // Delete all triggers
1078 $triggers = automatorwp_get_automation_triggers( $object_id );
1079
1080 foreach( $triggers as $trigger ) {
1081 ct_setup_table( 'automatorwp_triggers' );
1082
1083 ct_delete_object( $trigger->id );
1084
1085 ct_reset_setup_table();
1086 }
1087
1088 // Delete all actions
1089 $actions = automatorwp_get_automation_actions( $object_id );
1090
1091 foreach( $actions as $action ) {
1092 ct_setup_table( 'automatorwp_actions' );
1093
1094 ct_delete_object( $action->id );
1095
1096 ct_reset_setup_table();
1097 }
1098
1099 }
1100 add_action( 'delete_object', 'automatorwp_automations_delete_object' );