PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.19
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.19
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / controllers / FrmFormActionsController.php

FrmFormActionsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.19, at classes/controllers/FrmFormActionsController.php

697 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmFormActionsController {
7 public static $action_post_type = 'frm_form_actions';
8
9 /**
10 * @var array|null
11 */
12 public static $registered_actions;
13
14 /**
15 * Variables saved in the post:
16 * post_content: json settings
17 * menu_order: form id
18 * post_excerpt: action type
19 */
20 public static function register_post_types() {
21 register_post_type(
22 self::$action_post_type,
23 array(
24 'label' => __( 'Form Actions', 'formidable' ),
25 'description' => '',
26 'public' => false,
27 'show_ui' => false,
28 'exclude_from_search' => true,
29 'show_in_nav_menus' => false,
30 'show_in_menu' => true,
31 'capability_type' => 'page',
32 'supports' => array( 'title', 'editor', 'excerpt', 'custom-fields', 'page-attributes' ),
33 'has_archive' => false,
34 )
35 );
36
37 self::actions_init();
38 }
39
40 public static function actions_init() {
41 self::$registered_actions = new Frm_Form_Action_Factory();
42 self::register_actions();
43 do_action( 'frm_form_actions_init' );
44 }
45
46 /**
47 * @return void
48 */
49 public static function register_actions() {
50 $action_classes = array(
51 'on_submit' => 'FrmOnSubmitAction',
52 'email' => 'FrmEmailAction',
53 'wppost' => 'FrmDefPostAction',
54 'register' => 'FrmDefRegAction',
55 'paypal' => 'FrmDefPayPalAction',
56 'payment' => 'FrmTransLiteAction',
57 'quiz' => 'FrmDefQuizAction',
58 'quiz_outcome' => 'FrmDefQuizOutcomeAction',
59 'mailchimp' => 'FrmDefMlcmpAction',
60 'api' => 'FrmDefApiAction',
61 'salesforce' => 'FrmDefSalesforceAction',
62 'activecampaign' => 'FrmDefActiveCampaignAction',
63 'constantcontact' => 'FrmDefConstContactAction',
64 'getresponse' => 'FrmDefGetResponseAction',
65 'hubspot' => 'FrmDefHubspotAction',
66 'zapier' => 'FrmDefZapierAction',
67 'twilio' => 'FrmDefTwilioAction',
68 'highrise' => 'FrmDefHighriseAction',
69 'mailpoet' => 'FrmDefMailpoetAction',
70 'aweber' => 'FrmDefAweberAction',
71 'convertkit' => 'FrmDefConvertKitAction',
72 'googlespreadsheet' => 'FrmDefGoogleSpreadsheetAction',
73 );
74
75 $action_classes = apply_filters( 'frm_registered_form_actions', $action_classes );
76
77 include_once FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/email_action.php';
78 include_once FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/default_actions.php';
79
80 foreach ( $action_classes as $action_class ) {
81 self::$registered_actions->register( $action_class );
82 }
83 }
84
85 /**
86 * @since 4.0
87 *
88 * @param array $values
89 */
90 public static function email_settings( $values ) {
91 $form = FrmForm::getOne( $values['id'] );
92 $groups = self::form_action_groups();
93
94 $action_controls = self::get_form_actions();
95 self::maybe_add_action_to_group( $action_controls, $groups );
96
97 $allowed = self::active_actions( $action_controls );
98
99 include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/settings.php';
100 }
101
102 /**
103 * Add unknown actions to a group.
104 *
105 * @since 4.0
106 */
107 private static function maybe_add_action_to_group( $action_controls, &$groups ) {
108 $grouped = array();
109 foreach ( $groups as $group ) {
110 if ( isset( $group['actions'] ) ) {
111 $grouped = array_merge( $grouped, $group['actions'] );
112 }
113 }
114
115 foreach ( $action_controls as $action ) {
116 if ( isset( $groups[ $action->id_base ] ) || in_array( $action->id_base, $grouped ) ) {
117 continue;
118 }
119
120 $this_group = $action->action_options['group'];
121 if ( ! isset( $groups[ $this_group ] ) ) {
122 $this_group = 'misc';
123 }
124
125 if ( ! isset( $groups[ $this_group ]['actions'] ) ) {
126 $groups[ $this_group ]['actions'] = array();
127 }
128 $groups[ $this_group ]['actions'][] = $action->id_base;
129
130 unset( $action );
131 }
132 }
133
134 /**
135 * @since 4.0
136 *
137 * @return array
138 */
139 public static function form_action_groups() {
140 $groups = array(
141 'misc' => array(
142 'name' => '',
143 'icon' => 'frm_icon_font frm_shuffle_icon',
144 'actions' => array(
145 'email',
146 'wppost',
147 'register',
148 'quiz',
149 'quiz_outcome',
150 'twilio',
151 ),
152 ),
153 'payment' => array(
154 'name' => __( 'eCommerce', 'formidable' ),
155 'icon' => 'frm_icon_font frm_credit_card_alt_icon',
156 'actions' => array(
157 'paypal',
158 'payment',
159 ),
160 ),
161 'marketing' => array(
162 'name' => __( 'Email Marketing', 'formidable' ),
163 'icon' => 'frm_icon_font frm_mail_bulk_icon',
164 'actions' => array(
165 'mailchimp',
166 'activecampaign',
167 'constantcontact',
168 'getresponse',
169 'aweber',
170 'mailpoet',
171 ),
172 ),
173 'crm' => array(
174 'name' => __( 'CRM', 'formidable' ),
175 'icon' => 'frm_icon_font frm_address_card_icon',
176 'actions' => array(
177 'salesforce',
178 'hubspot',
179 'highrise',
180 ),
181 ),
182 );
183
184 return apply_filters( 'frm_action_groups', $groups );
185 }
186
187 /**
188 * Get the number of currently active form actions.
189 *
190 * @since 4.0
191 *
192 * @return array
193 */
194 private static function active_actions( $action_controls ) {
195 $allowed = array();
196 foreach ( $action_controls as $action_control ) {
197 if ( isset( $action_control->action_options['active'] ) && $action_control->action_options['active'] ) {
198 $allowed[] = $action_control->id_base;
199 }
200 }
201 return $allowed;
202 }
203
204 /**
205 * For each add-on, add an li, class, and javascript function. If active, add an additional class.
206 *
207 * @since 4.0
208 * @param object $action_control
209 * @param array $allowed
210 */
211 public static function show_action_icon_link( $action_control, $allowed ) {
212 $data = array();
213 $classes = ' frm_' . $action_control->id_base . '_action frm_single_action';
214
215 $group_class = ' frm-group-' . $action_control->action_options['group'];
216
217 /* translators: %s: Name of form action */
218 $upgrade_label = sprintf( esc_html__( '%s form actions', 'formidable' ), $action_control->action_options['tooltip'] );
219
220 $default_shown = array( 'wppost', 'register', 'payment', 'quiz', 'hubspot' );
221 $default_shown = array_values( array_diff( $default_shown, $allowed ) );
222 $default_position = array_search( $action_control->id_base, $default_shown );
223 $allowed_count = count( $allowed );
224
225 if ( isset( $action_control->action_options['active'] ) && $action_control->action_options['active'] ) {
226 $classes .= ' frm_active_action';
227 } else {
228 $classes .= ' frm_inactive_action';
229 if ( $default_position !== false && ( $allowed_count + $default_position ) < 6 ) {
230 $group_class .= ' frm-default-show';
231 }
232
233 $data['data-upgrade'] = $upgrade_label;
234 $data['data-medium'] = 'settings-' . $action_control->id_base;
235
236 $upgrading = FrmAddonsController::install_link( $action_control->action_options['plugin'] );
237 if ( isset( $upgrading['url'] ) ) {
238 $data['data-oneclick'] = json_encode( $upgrading );
239 }
240
241 if ( isset( $action_control->action_options['message'] ) ) {
242 $data['data-message'] = $action_control->action_options['message'];
243 }
244
245 $requires = FrmFormsHelper::get_plan_required( $upgrading );
246 if ( $requires && 'free' !== $requires ) {
247 $data['data-requires'] = $requires;
248 }
249 }//end if
250
251 // HTML to include on the icon.
252 $icon_atts = array();
253 if ( $action_control->action_options['color'] !== 'var(--primary-700)' ) {
254 $icon_atts = array(
255 'style' => '--primary-700:' . $action_control->action_options['color'],
256 );
257 }
258
259 include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_action_icon.php';
260 }
261
262 /**
263 * @param string $action
264 * @return array|FrmFormAction A single form action is returned when a specific $action value is requested.
265 */
266 public static function get_form_actions( $action = 'all' ) {
267 $temp_actions = self::$registered_actions;
268 if ( empty( $temp_actions ) ) {
269 self::actions_init();
270 $temp_actions = self::$registered_actions->actions;
271 } else {
272 $temp_actions = $temp_actions->actions;
273 }
274
275 $actions = array();
276
277 foreach ( $temp_actions as $a ) {
278 if ( 'all' !== $action && $a->id_base == $action ) {
279 return $a;
280 }
281
282 $actions[ $a->id_base ] = $a;
283 }
284
285 return $actions;
286 }
287
288 /**
289 * @since 2.0
290 */
291 public static function list_actions( $form, $values ) {
292 if ( empty( $form ) ) {
293 return;
294 }
295
296 /**
297 * Use this hook to migrate old settings into a new action
298 *
299 * @since 2.0
300 */
301 do_action( 'frm_before_list_actions', $form );
302
303 $filters = array(
304 'post_status' => 'all',
305 );
306 $form_actions = FrmFormAction::get_action_for_form( $form->id, 'all', $filters );
307
308 /**
309 * @var array
310 */
311 $action_controls = self::get_form_actions();
312
313 $action_map = array();
314
315 foreach ( $action_controls as $key => $control ) {
316 $action_map[ $control->id_base ] = $key;
317 }
318
319 self::maybe_show_limit_warning( $form->id, $form_actions );
320
321 foreach ( $form_actions as $action ) {
322 if ( ! isset( $action_map[ $action->post_excerpt ] ) ) {
323 // don't try and show settings if action no longer exists
324 continue;
325 }
326
327 self::action_control( $action, $form, $action->ID, $action_controls[ $action_map[ $action->post_excerpt ] ], $values );
328 }
329 }
330
331 /**
332 * Show a warning before the form actions list if there are 99 actions, and the limit is set to 99.
333 * If it is filtered, the warning is still shown when applicable, just using the new limit.
334 *
335 * @since 6.17
336 *
337 * @param int|string $form_id
338 * @param array $form_actions
339 * @return void
340 */
341 private static function maybe_show_limit_warning( $form_id, $form_actions ) {
342 $count = count( $form_actions );
343 if ( $count < 99 ) {
344 return;
345 }
346
347 $limit = FrmFormAction::get_action_limit( $form_id );
348 if ( $limit < 99 || $count < $limit ) {
349 return;
350 }
351
352 $documentation_url = 'https://formidableforms.com/knowledgebase/frm_form_action_limit/#kb-increase-limit-of-form-actions';
353
354 echo '<div class="frm_warning_style">';
355 FrmAppHelper::icon_by_class( 'frm_icon_font frm_alert_icon' );
356 echo '&nbsp;';
357 printf(
358 // translators: %s: URL to documentation
359 esc_html__( 'You have reached your form action limit. To increase this limit, you will require additional code. Visit our documentation at %s.', 'formidable' ),
360 '<a href="' . esc_url( $documentation_url ) . '" target="_blank">' . esc_html( $documentation_url ) . '</a>'
361 );
362 echo '</div>';
363 }
364
365 public static function action_control( $form_action, $form, $action_key, $action_control, $values ) {
366 $action_control->_set( $action_key );
367
368 $use_logging = self::should_show_log_message( $form_action->post_excerpt );
369
370 include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/form_action.php';
371 }
372
373 public static function add_form_action() {
374 FrmAppHelper::permission_check( 'frm_edit_forms' );
375 check_ajax_referer( 'frm_ajax', 'nonce' );
376
377 global $frm_vars;
378
379 $action_key = FrmAppHelper::get_param( 'list_id', '', 'post', 'absint' );
380 $action_type = FrmAppHelper::get_param( 'type', '', 'post', 'sanitize_text_field' );
381
382 /**
383 * @var FrmFormAction
384 */
385 $action_control = self::get_form_actions( $action_type );
386 $action_control->_set( $action_key );
387
388 $form_id = FrmAppHelper::get_param( 'form_id', '', 'post', 'absint' );
389
390 $form_action = $action_control->prepare_new( $form_id );
391 $use_logging = self::should_show_log_message( $action_type );
392
393 $values = array();
394 $form = self::fields_to_values( $form_id, $values );
395
396 include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/form_action.php';
397 wp_die();
398 }
399
400 public static function fill_action() {
401 FrmAppHelper::permission_check( 'frm_edit_forms' );
402 check_ajax_referer( 'frm_ajax', 'nonce' );
403
404 $action_key = FrmAppHelper::get_param( 'action_id', '', 'post', 'absint' );
405 $action_type = FrmAppHelper::get_param( 'action_type', '', 'post', 'sanitize_text_field' );
406
407 $action_control = self::get_form_actions( $action_type );
408 if ( empty( $action_control ) ) {
409 wp_die();
410 }
411
412 $form_action = $action_control->get_single_action( $action_key );
413
414 $values = array();
415 $form = self::fields_to_values( $form_action->menu_order, $values );
416
417 $use_logging = self::should_show_log_message( $action_type );
418
419 include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_action_inside.php';
420 wp_die();
421 }
422
423 /**
424 * @since 3.06.04
425 * @return bool
426 */
427 private static function should_show_log_message( $action_type ) {
428 $logging = array( 'api', 'salesforce', 'constantcontact', 'activecampaign' );
429 return in_array( $action_type, $logging, true ) && ! function_exists( 'frm_log_autoloader' );
430 }
431
432 private static function fields_to_values( $form_id, array &$values ) {
433 $form = FrmForm::getOne( $form_id );
434
435 $values = array(
436 'fields' => array(),
437 'id' => $form->id,
438 );
439
440 $fields = FrmField::get_all_for_form( $form->id );
441 foreach ( $fields as $k => $f ) {
442 $f = (array) $f;
443 $opts = (array) $f['field_options'];
444 $f = array_merge( $opts, $f );
445 if ( ! isset( $f['post_field'] ) ) {
446 $f['post_field'] = '';
447 }
448 $values['fields'][] = $f;
449 unset( $k, $f );
450 }
451
452 return $form;
453 }
454
455 /**
456 * @param int $form_id
457 * @return void
458 */
459 public static function update_settings( $form_id ) {
460 FrmAppHelper::permission_check( 'frm_edit_forms' );
461 $process_form = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' );
462 if ( ! wp_verify_nonce( $process_form, 'process_form_nonce' ) ) {
463 $frm_settings = FrmAppHelper::get_settings();
464 $error_args = array(
465 'title' => __( 'Verification failed', 'formidable' ),
466 'body' => $frm_settings->admin_permission,
467 'cancel_url' => add_query_arg(
468 array(
469 'page' => 'formidable',
470 'frm_action' => 'settings',
471 'id' => $form_id,
472 ),
473 admin_url( 'admin.php?' )
474 ),
475 );
476 FrmAppController::show_error_modal( $error_args );
477 return;
478 }
479
480 global $wpdb;
481
482 $registered_actions = self::$registered_actions->actions;
483
484 $old_actions = FrmDb::get_col(
485 $wpdb->posts,
486 array(
487 'post_type' => self::$action_post_type,
488 'menu_order' => $form_id,
489 ),
490 'ID'
491 );
492 $new_actions = array();
493
494 foreach ( $registered_actions as $registered_action ) {
495 $action_ids = $registered_action->update_callback( $form_id );
496 if ( ! empty( $action_ids ) ) {
497 $new_actions[] = $action_ids;
498 }
499 }
500
501 // Only use array_merge if there are new actions.
502 if ( ! empty( $new_actions ) ) {
503 $new_actions = call_user_func_array( 'array_merge', $new_actions );
504 }
505 $old_actions = array_diff( $old_actions, $new_actions );
506
507 self::delete_missing_actions( $old_actions );
508
509 FrmOnSubmitHelper::save_on_submit_settings( $form_id );
510 }
511
512 public static function delete_missing_actions( $old_actions ) {
513 if ( ! empty( $old_actions ) ) {
514 foreach ( $old_actions as $old_id ) {
515 wp_delete_post( $old_id );
516 }
517 FrmDb::cache_delete_group( 'frm_actions' );
518 }
519 }
520
521 public static function trigger_create_actions( $entry_id, $form_id, $args = array() ) {
522 $filter_args = $args;
523 $filter_args['entry_id'] = $entry_id;
524 $filter_args['form_id'] = $form_id;
525
526 /**
527 * @since 2.0.23
528 * @since 6.11.2 $filter_args is now passed instead of $args. It includes additional ID data.
529 *
530 * @param string $event 'create' by default. Pro may filter this value to 'draft' instead.
531 * @param array $filter_args
532 */
533 $event = apply_filters( 'frm_trigger_create_action', 'create', $filter_args );
534
535 self::trigger_actions( $event, $form_id, $entry_id, 'all', $args );
536 }
537
538 /**
539 * @param string $event
540 */
541 public static function trigger_actions( $event, $form, $entry, $type = 'all', $args = array() ) {
542 $action_status = array(
543 'post_status' => 'publish',
544 );
545 $form_actions = FrmFormAction::get_action_for_form( ( is_object( $form ) ? $form->id : $form ), $type, $action_status );
546
547 if ( empty( $form_actions ) ) {
548 return;
549 }
550
551 FrmForm::maybe_get_form( $form );
552
553 $link_settings = self::get_form_actions( $type );
554 if ( 'all' !== $type ) {
555 $link_settings = array( $type => $link_settings );
556 }
557
558 $stored_actions = array();
559 $action_priority = array();
560
561 if ( in_array( $event, array( 'create', 'update' ), true ) && defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
562 $this_event = 'import';
563 } else {
564 $this_event = $event;
565 }
566
567 foreach ( $form_actions as $action ) {
568
569 $skip_this_action = ! in_array( $this_event, $action->post_content['event'], true ) || FrmOnSubmitAction::$slug === $action->post_excerpt;
570 $skip_this_action = apply_filters( 'frm_skip_form_action', $skip_this_action, compact( 'action', 'entry', 'form', 'event' ) );
571 if ( $skip_this_action ) {
572 continue;
573 }
574
575 if ( ! is_object( $entry ) ) {
576 $entry = FrmEntry::getOne( $entry, true );
577 }
578
579 if ( empty( $entry ) || ( FrmEntriesHelper::DRAFT_ENTRY_STATUS === (int) $entry->is_draft && 'draft' !== $event ) ) {
580 continue;
581 }
582
583 $child_entry = ( ( is_object( $form ) && is_numeric( $form->parent_form_id ) && $form->parent_form_id ) || ( $entry && ( $entry->form_id != $form->id || $entry->parent_item_id ) ) || ( isset( $args['is_child'] ) && $args['is_child'] ) );
584
585 if ( $child_entry ) {
586 // maybe trigger actions for sub forms
587 $trigger_children = apply_filters( 'frm_use_embedded_form_actions', false, compact( 'form', 'entry' ) );
588 if ( ! $trigger_children ) {
589 continue;
590 }
591 }
592
593 // Check conditional logic.
594 $stop = FrmFormAction::action_conditions_met( $action, $entry );
595 if ( $stop ) {
596 continue;
597 }
598
599 // Store actions so they can be triggered with the correct priority.
600 $stored_actions[ $action->ID ] = $action;
601 $action_priority[ $action->ID ] = $link_settings[ $action->post_excerpt ]->action_options['priority'];
602
603 unset( $action );
604 }//end foreach
605
606 if ( ! empty( $stored_actions ) ) {
607 asort( $action_priority );
608
609 // Make sure hooks are loaded.
610 new FrmNotification();
611
612 foreach ( $action_priority as $action_id => $priority ) {
613 $action = $stored_actions[ $action_id ];
614
615 /**
616 * Allows custom form action trigger.
617 *
618 * @since 6.10
619 *
620 * @param bool $skip Skip default trigger.
621 * @param object $action Action object.
622 * @param object $entry Entry object.
623 * @param object $form Form object.
624 * @param string $event Event ('create' or 'update').
625 */
626 if ( false === apply_filters( 'frm_custom_trigger_action', false, $action, $entry, $form, $event ) ) {
627 do_action( 'frm_trigger_' . $action->post_excerpt . '_action', $action, $entry, $form, $event );
628 do_action( 'frm_trigger_' . $action->post_excerpt . '_' . $event . '_action', $action, $entry, $form );
629 }
630
631 // If post is created, get updated $entry object.
632 if ( $action->post_excerpt === 'wppost' && $event === 'create' ) {
633 $entry = FrmEntry::getOne( $entry->id, true );
634 }
635 }//end foreach
636 }//end if
637 }
638
639 public static function duplicate_form_actions( $form_id, $values, $args = array() ) {
640 if ( empty( $args['old_id'] ) ) {
641 // Continue if we know which actions to copy.
642 return;
643 }
644
645 /**
646 * @var array
647 */
648 $action_controls = self::get_form_actions();
649
650 foreach ( $action_controls as $action_control ) {
651 $action_control->duplicate_form_actions( $form_id, $args['old_id'] );
652 unset( $action_control );
653 }
654 }
655
656 public static function limit_by_type( $where ) {
657 global $frm_vars, $wpdb;
658
659 if ( ! isset( $frm_vars['action_type'] ) ) {
660 return $where;
661 }
662
663 $where .= $wpdb->prepare( ' AND post_excerpt = %s ', $frm_vars['action_type'] );
664
665 return $where;
666 }
667 }
668
669 class Frm_Form_Action_Factory {
670 public $actions = array();
671
672 public function __construct() {
673 add_action( 'frm_form_actions_init', array( $this, '_register_actions' ), 100 );
674 }
675
676 public function register( $action_class ) {
677 $this->actions[ $action_class ] = new $action_class();
678 }
679
680 public function unregister( $action_class ) {
681 if ( isset( $this->actions[ $action_class ] ) ) {
682 unset( $this->actions[ $action_class ] );
683 }
684 }
685
686 public function _register_actions() {
687 $keys = array_keys( $this->actions );
688
689 foreach ( $keys as $key ) {
690 // don't register new action if old action with the same id is already registered
691 if ( ! isset( $this->actions[ $key ] ) ) {
692 $this->actions[ $key ]->_register();
693 }
694 }
695 }
696 }
697