PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.21
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.21
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.21, at classes/controllers/FrmFormActionsController.php

716 lines 20.3 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 if ( ! is_object( $form ) ) {
553 return;
554 }
555
556 $link_settings = self::get_form_actions( $type );
557 if ( 'all' !== $type ) {
558 $link_settings = array( $type => $link_settings );
559 }
560
561 $stored_actions = array();
562 $action_priority = array();
563
564 if ( in_array( $event, array( 'create', 'update' ), true ) && defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
565 $this_event = 'import';
566 } else {
567 $this_event = $event;
568 }
569
570 foreach ( $form_actions as $action ) {
571
572 $skip_this_action = ! in_array( $this_event, $action->post_content['event'], true ) || FrmOnSubmitAction::$slug === $action->post_excerpt;
573 $skip_this_action = apply_filters( 'frm_skip_form_action', $skip_this_action, compact( 'action', 'entry', 'form', 'event' ) );
574 if ( $skip_this_action ) {
575 continue;
576 }
577
578 if ( ! is_object( $entry ) ) {
579 $entry = FrmEntry::getOne( $entry, true );
580 }
581
582 if ( empty( $entry ) || ( FrmEntriesHelper::DRAFT_ENTRY_STATUS === (int) $entry->is_draft && 'draft' !== $event ) ) {
583 continue;
584 }
585
586 $child_entry = ( is_numeric( $form->parent_form_id ) && $form->parent_form_id ) || ( $entry && ( $entry->form_id != $form->id || $entry->parent_item_id ) ) || ! empty( $args['is_child'] );
587
588 if ( $child_entry ) {
589 // maybe trigger actions for sub forms
590 $trigger_children = apply_filters( 'frm_use_embedded_form_actions', false, compact( 'form', 'entry' ) );
591 if ( ! $trigger_children ) {
592 continue;
593 }
594 }
595
596 // Check conditional logic.
597 $stop = FrmFormAction::action_conditions_met( $action, $entry );
598 if ( $stop ) {
599 continue;
600 }
601
602 // Store actions so they can be triggered with the correct priority.
603 $stored_actions[ $action->ID ] = $action;
604 $action_priority[ $action->ID ] = $link_settings[ $action->post_excerpt ]->action_options['priority'];
605
606 unset( $action );
607 }//end foreach
608
609 if ( ! empty( $stored_actions ) ) {
610 asort( $action_priority );
611
612 // Make sure hooks are loaded.
613 new FrmNotification();
614
615 foreach ( $action_priority as $action_id => $priority ) {
616 $action = $stored_actions[ $action_id ];
617
618 /**
619 * Allows custom form action trigger.
620 *
621 * @since 6.10
622 *
623 * @param bool $skip Skip default trigger.
624 * @param object $action Action object.
625 * @param object $entry Entry object.
626 * @param object $form Form object.
627 * @param string $event Event ('create' or 'update').
628 */
629 if ( false === apply_filters( 'frm_custom_trigger_action', false, $action, $entry, $form, $event ) ) {
630 do_action( 'frm_trigger_' . $action->post_excerpt . '_action', $action, $entry, $form, $event );
631 do_action( 'frm_trigger_' . $action->post_excerpt . '_' . $event . '_action', $action, $entry, $form );
632 }
633
634 // If post is created, get updated $entry object.
635 if ( $action->post_excerpt === 'wppost' && $event === 'create' ) {
636 $entry = FrmEntry::getOne( $entry->id, true );
637 }
638 }//end foreach
639 }//end if
640 }
641
642 public static function duplicate_form_actions( $form_id, $values, $args = array() ) {
643 if ( empty( $args['old_id'] ) ) {
644 // Continue if we know which actions to copy.
645 return;
646 }
647
648 /**
649 * @var array
650 */
651 $action_controls = self::get_form_actions();
652
653 foreach ( $action_controls as $action_control ) {
654 $action_control->duplicate_form_actions( $form_id, $args['old_id'] );
655 unset( $action_control );
656 }
657 }
658
659 public static function limit_by_type( $where ) {
660 global $frm_vars, $wpdb;
661
662 if ( ! isset( $frm_vars['action_type'] ) ) {
663 return $where;
664 }
665
666 $where .= $wpdb->prepare( ' AND post_excerpt = %s ', $frm_vars['action_type'] );
667
668 return $where;
669 }
670
671 /**
672 * Prevent WPML from filtering form actions based on the active language.
673 *
674 * @since 6.20
675 *
676 * @param bool|null $null
677 * @param string $post_type
678 * @return bool|null
679 */
680 public static function prevent_wpml_translations( $null, $post_type ) {
681 if ( self::$action_post_type === $post_type ) {
682 return false;
683 }
684 return $null;
685 }
686 }
687
688 class Frm_Form_Action_Factory {
689 public $actions = array();
690
691 public function __construct() {
692 add_action( 'frm_form_actions_init', array( $this, '_register_actions' ), 100 );
693 }
694
695 public function register( $action_class ) {
696 $this->actions[ $action_class ] = new $action_class();
697 }
698
699 public function unregister( $action_class ) {
700 if ( isset( $this->actions[ $action_class ] ) ) {
701 unset( $this->actions[ $action_class ] );
702 }
703 }
704
705 public function _register_actions() {
706 $keys = array_keys( $this->actions );
707
708 foreach ( $keys as $key ) {
709 // don't register new action if old action with the same id is already registered
710 if ( ! isset( $this->actions[ $key ] ) ) {
711 $this->actions[ $key ]->_register();
712 }
713 }
714 }
715 }
716