PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.28
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.28
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 / FrmFormsController.php

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

3,709 lines 102.2 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 FrmFormsController {
7
8 /**
9 * Track the form that opened the redirect URL in a new tab. This is used to check if we should show the default
10 * message in the current tab.
11 *
12 * @since 6.2
13 *
14 * @var array Keys are form IDs and values are 1.
15 */
16 private static $redirected_in_new_tab = array();
17
18 /**
19 * The HTML for the Formdiable TinyMCE button (That triggers a popup to insert shortcodes)
20 * is stored here and re-used as an optimization.
21 *
22 * @since 6.11
23 *
24 * @var string|null
25 */
26 private static $formidable_tinymce_button;
27
28 public static function menu() {
29 $menu_label = __( 'Forms', 'formidable' );
30
31 if ( ! FrmAppHelper::pro_is_installed() ) {
32 $menu_label .= ' (Lite)';
33 }
34
35 add_submenu_page( 'formidable', 'Formidable | ' . $menu_label, $menu_label, 'frm_view_forms', 'formidable', 'FrmFormsController::route' );
36
37 self::maybe_load_listing_hooks();
38 }
39
40 public static function maybe_load_listing_hooks() {
41 if ( ! FrmAppHelper::on_form_listing_page() ) {
42 return;
43 }
44
45 add_filter( 'get_user_option_managetoplevel_page_formidablecolumnshidden', 'FrmFormsController::hidden_columns' );
46
47 add_filter( 'manage_toplevel_page_formidable_columns', 'FrmFormsController::get_columns', 0 );
48 add_filter( 'manage_toplevel_page_formidable_sortable_columns', 'FrmFormsController::get_sortable_columns' );
49 }
50
51 public static function head() {
52 if ( wp_is_mobile() ) {
53 wp_enqueue_script( 'jquery-touch-punch' );
54 }
55 }
56
57 public static function register_widgets() {
58 require_once FrmAppHelper::plugin_path() . '/classes/widgets/FrmShowForm.php';
59 register_widget( 'FrmShowForm' );
60 }
61
62 /**
63 * Show a message about conditional logic
64 *
65 * @since 4.06.03
66 */
67 public static function logic_tip() {
68 $images_url = FrmAppHelper::plugin_url() . '/images/';
69 $data_message = __( 'Only show the fields you need and create branching forms. Upgrade to get conditional logic and question branching.', 'formidable' );
70 $data_message .= ' <img src="' . esc_url( $images_url ) . '/survey-logic.png" srcset="' . esc_url( $images_url ) . 'survey-logic@2x.png 2x" alt="' . esc_attr__( 'Conditional Logic options', 'formidable' ) . '"/>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
71 echo '<a href="javascript:void(0)" class="frm_noallow frm_show_upgrade frm_add_logic_link frm-collapsed frm-flex-justify" data-upgrade="' . esc_attr__( 'Conditional Logic options', 'formidable' ) . '" data-message="' . esc_attr( $data_message ) . '" data-medium="builder" data-content="logic">'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
72 esc_html_e( 'Conditional Logic', 'formidable' );
73 FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown8_icon', array( 'aria-hidden' => 'true' ) );
74 echo '</a>';
75 }
76
77 /**
78 * By default, Divi processes form shortcodes on the edit post page.
79 * Now that won't do.
80 *
81 * @since 3.01
82 *
83 * @param array $shortcodes List of shortcodes to process.
84 *
85 * @return array
86 */
87 public static function prevent_divi_conflict( $shortcodes ) {
88 $shortcodes[] = 'formidable';
89 return $shortcodes;
90 }
91
92 /**
93 * @return void
94 */
95 public static function list_form() {
96 FrmAppHelper::permission_check( 'frm_view_forms' );
97
98 $message = '';
99 $params = FrmForm::list_page_params();
100 $errors = self::process_bulk_form_actions( array() );
101
102 if ( isset( $errors['message'] ) ) {
103 $message = $errors['message'];
104 unset( $errors['message'] );
105 }
106
107 $errors = apply_filters( 'frm_admin_list_form_action', $errors );
108
109 self::display_forms_list( $params, $message, $errors );
110 }
111
112 /**
113 * Create the default email action
114 *
115 * @since 2.02.11
116 *
117 * @param int|object $form
118 *
119 * @return void
120 */
121 private static function create_default_email_action( $form ) {
122 FrmForm::maybe_get_form( $form );
123
124 if ( ! is_object( $form ) ) {
125 return;
126 }
127
128 $create_email = apply_filters( 'frm_create_default_email_action', true, $form );
129
130 if ( ! $create_email ) {
131 return;
132 }
133
134 /**
135 * @var FrmFormAction
136 */
137 $action_control = FrmFormActionsController::get_form_actions( 'email' );
138 $action_control->create( $form->id );
139 }
140
141 /**
142 * Create the default On submit action
143 *
144 * @since 6.0.0
145 *
146 * @param int|object $form Form object or ID.
147 *
148 * @return void
149 */
150 private static function create_default_on_submit_action( $form ) {
151 FrmForm::maybe_get_form( $form );
152
153 if ( ! is_object( $form ) ) {
154 return;
155 }
156
157 /**
158 * Enable or disable the default On Submit action.
159 *
160 * @since 6.0.0
161 *
162 * @param bool $create Set to `false` if you want to disable.
163 * @param object $form Form object.
164 */
165 $create = apply_filters( 'frm_create_default_on_submit_action', true, $form );
166
167 if ( ! $create ) {
168 return;
169 }
170
171 /**
172 * @var FrmFormAction
173 */
174 $action_control = FrmFormActionsController::get_form_actions( FrmOnSubmitAction::$slug );
175 $action_control->create( $form->id );
176 }
177
178 /**
179 * Creates submit button field.
180 *
181 * @since 6.9
182 *
183 * @param int|object $form Form ID or object.
184 *
185 * @return void
186 */
187 private static function create_submit_button_field( $form ) {
188 FrmForm::maybe_get_form( $form );
189
190 if ( ! is_object( $form ) ) {
191 return;
192 }
193
194 if ( FrmSubmitHelper::get_submit_field( $form->id ) ) {
195 // Do not create submit button field if it exists.
196 return;
197 }
198
199 FrmField::create(
200 array(
201 'type' => FrmSubmitHelper::FIELD_TYPE,
202 'name' => __( 'Submit', 'formidable' ),
203 'field_order' => FrmSubmitHelper::DEFAULT_ORDER,
204 'form_id' => $form->id,
205 'field_options' => FrmFieldsHelper::get_default_field_options( FrmSubmitHelper::FIELD_TYPE ),
206 'description' => '',
207 'default_value' => '',
208 'options' => array(),
209 )
210 );
211 }
212
213 /**
214 * @param array|false $values
215 *
216 * @return void
217 */
218 public static function edit( $values = false ) {
219 FrmAppHelper::permission_check( 'frm_edit_forms' );
220
221 $id = isset( $values['id'] ) ? absint( $values['id'] ) : FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
222
223 self::get_edit_vars( $id );
224 }
225
226 /**
227 * @param mixed $id
228 * @param string $message
229 *
230 * @return void
231 */
232 public static function settings( $id = false, $message = '' ) {
233 FrmAppHelper::permission_check( 'frm_edit_forms' );
234
235 if ( ! $id || ! is_numeric( $id ) ) {
236 $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
237 }
238
239 FrmOnSubmitHelper::maybe_migrate_submit_settings_to_action( $id );
240
241 self::get_settings_vars( $id, array(), $message );
242 }
243
244 public static function update_settings() {
245 FrmAppHelper::permission_check( 'frm_edit_forms' );
246 $process_form = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' );
247
248 if ( ! wp_verify_nonce( $process_form, 'process_form_nonce' ) ) {
249 $frm_settings = FrmAppHelper::get_settings();
250 $error_args = array(
251 'title' => __( 'Verification failed', 'formidable' ),
252 'body' => $frm_settings->admin_permission,
253 'cancel_text' => __( 'Cancel', 'formidable' ),
254 );
255 FrmAppController::show_error_modal( $error_args );
256 return;
257 }
258
259 $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
260 $errors = FrmForm::validate( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
261 $warnings = FrmFormsHelper::check_for_warnings( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
262
263 if ( count( $errors ) > 0 ) {
264 self::get_settings_vars( $id, $errors, compact( 'warnings' ) );
265 return;
266 }
267
268 do_action( 'frm_before_update_form_settings', $id );
269
270 $antispam_was_on = self::antispam_was_on( $id );
271
272 FrmForm::update( $id, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
273
274 $antispam_is_on = ! empty( $_POST['options']['antispam'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
275
276 if ( $antispam_is_on !== $antispam_was_on ) {
277 FrmAntiSpam::clear_caches();
278 }
279
280 $message = __( 'Settings Successfully Updated', 'formidable' );
281
282 self::get_settings_vars( $id, array(), compact( 'message', 'warnings' ) );
283 }
284
285 /**
286 * @param int $form_id
287 *
288 * @return bool
289 */
290 private static function antispam_was_on( $form_id ) {
291 $form = FrmForm::getOne( $form_id );
292 return ! empty( $form->options['antispam'] );
293 }
294
295 /**
296 * @param array $values
297 *
298 * @return void
299 */
300 public static function update( $values = array() ) {
301 if ( ! $values ) {
302 $values = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
303 }
304
305 // Set radio button and checkbox meta equal to "other" value.
306 if ( FrmAppHelper::pro_is_installed() ) {
307 $values = FrmProEntry::mod_other_vals( $values, 'back' );
308 }
309
310 $errors = FrmForm::validate( $values );
311 $permission_error = FrmAppHelper::permission_nonce_error( 'frm_edit_forms', 'frm_save_form', 'frm_save_form_nonce' );
312
313 if ( $permission_error !== false ) {
314 $errors['form'] = $permission_error;
315 }
316
317 $id = isset( $values['id'] ) ? absint( $values['id'] ) : FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
318
319 if ( count( $errors ) > 0 ) {
320 self::get_edit_vars( $id, $errors );
321 return;
322 }
323
324 self::maybe_remove_draft_option_from_fields( $id );
325
326 FrmForm::update( $id, $values );
327 $message = __( 'Form was successfully updated.', 'formidable' );
328
329 if ( self::is_too_long( $values ) ) {
330 $message .= '<br/> ' . sprintf(
331 /* translators: %1$s: Start link HTML, %2$s: end link HTML */
332 __( 'However, your form is very long and may be %1$sreaching server limits%2$s.', 'formidable' ),
333 '<a href="' . esc_url( self::get_form_too_long_docs_url() ) . '" target="_blank" rel="noopener">',
334 '</a>'
335 );
336 }
337
338 if ( defined( 'DOING_AJAX' ) ) {
339 wp_die( FrmAppHelper::kses( $message, array( 'a' ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
340 }
341
342 self::get_edit_vars( $id, array(), $message );
343 }
344
345 /**
346 * @since 6.25.1
347 *
348 * @return string
349 */
350 private static function get_form_too_long_docs_url() {
351 $utm = array(
352 'campaign' => 'builder',
353 'content' => 'form-too-long',
354 );
355 return FrmAppHelper::admin_upgrade_link( $utm, 'knowledgebase/i-have-a-long-form-why-did-the-options-at-the-end-of-the-form-stop-saving/' );
356 }
357
358 /**
359 * Remove the draft flag from any new fields from this current session.
360 *
361 * @since 6.8
362 *
363 * @param int $form_id
364 *
365 * @return void
366 */
367 private static function maybe_remove_draft_option_from_fields( $form_id ) {
368 $draft_field_ids_csv = FrmAppHelper::get_post_param( 'draft_fields', '', 'sanitize_text_field' );
369
370 if ( ! $draft_field_ids_csv ) {
371 // If the draft_fields input is empty there are no new fields in the session.
372 return;
373 }
374
375 $draft_field_ids = array_filter( explode( ',', $draft_field_ids_csv ), 'is_numeric' );
376
377 if ( ! $draft_field_ids ) {
378 // Exit early if the draft fields input is invalid. It should be a CSV of integer values.
379 return;
380 }
381
382 $draft_field_rows = FrmFieldsHelper::get_draft_field_results( $form_id, $draft_field_ids );
383
384 foreach ( $draft_field_rows as $row ) {
385 $row->field_options['draft'] = 0;
386 FrmField::update( $row->id, array( 'field_options' => $row->field_options ) );
387 }
388 }
389
390 /**
391 * Check if the value at the end of the form was included.
392 * If it's missing, it means other values at the end of the form
393 * were likely not saved either.
394 *
395 * @since 3.06.01
396 *
397 * @param array $values
398 *
399 * @return bool
400 */
401 private static function is_too_long( $values ) {
402 return empty( $values['frm_end'] );
403 }
404
405 public static function duplicate() {
406 FrmAppHelper::permission_check( 'frm_edit_forms' );
407 $nonce = FrmAppHelper::simple_get( '_wpnonce' );
408
409 if ( ! wp_verify_nonce( $nonce ) ) {
410 $frm_settings = FrmAppHelper::get_settings();
411 wp_die( esc_html( $frm_settings->admin_permission ) );
412 }
413
414 $params = FrmForm::list_page_params();
415 $form = FrmForm::duplicate( $params['id'], $params['template'], true );
416 $url = admin_url( 'admin.php?page=formidable' );
417 $message = 'form_duplicate_error';
418
419 if ( $form ) {
420 $new_template = FrmAppHelper::simple_get( 'new_template' ) ? '&new_template=true' : '';
421 $url = admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . absint( $form ) . $new_template );
422 $message = 'form_duplicated';
423 }
424
425 $url .= '&message=' . $message;
426
427 wp_safe_redirect( $url );
428 exit();
429 }
430
431 /**
432 * @return string|null
433 */
434 public static function page_preview() {
435 $params = FrmForm::list_page_params();
436
437 if ( ! $params['form'] || is_numeric( $params['form'] ) ) {
438 return null;
439 }
440
441 self::maybe_block_preview( $params['form'] );
442
443 $form = FrmForm::getOne( $params['form'] );
444
445 if ( ! $form ) {
446 return null;
447 }
448
449 return self::show_form( $form->id, '', 'auto', 'auto' );
450 }
451
452 /**
453 * @since 3.0
454 *
455 * @return void
456 */
457 public static function show_page_preview() {
458 $preview = self::page_preview();
459
460 if ( is_null( $preview ) ) {
461 wp_die(
462 '<h1>' . esc_html__( 'Form key is invalid', 'formidable' ) . '</h1>',
463 '<p>' . esc_html__( 'Form key is invalid', 'formidable' ) . '</p>',
464 404
465 );
466 }
467
468 echo $preview; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
469 }
470
471 /**
472 * @return void
473 */
474 public static function preview() {
475 do_action( 'frm_wp' );
476 FrmAppHelper::set_current_screen_and_hook_suffix();
477
478 global $frm_vars;
479 $frm_vars['preview'] = true;
480
481 // print_emoji_styles is deprecated.
482 remove_action( 'wp_print_styles', 'print_emoji_styles' );
483
484 if ( FrmTestModeController::should_add_test_mode_container() ) {
485 do_action( 'frm_test_mode_init' );
486 add_action( 'wp_enqueue_scripts', 'FrmTestModeController::register_and_enqueue_required_scripts' );
487 add_filter( 'frm_filter_final_form', 'FrmTestModeController::maybe_add_test_mode_container', 99 );
488 }
489
490 $include_theme = FrmAppHelper::get_param( 'theme', '', 'get', 'absint' );
491
492 if ( $include_theme ) {
493 self::set_preview_query();
494 self::load_theme_preview();
495 } else {
496 self::load_direct_preview();
497 }
498
499 wp_die();
500 }
501
502 /**
503 * Set the page global to any available page (except for the Blog Page).
504 *
505 * @return void
506 */
507 private static function set_preview_query() {
508 $page_query = array(
509 'numberposts' => 1,
510 'orderby' => 'date',
511 'order' => 'ASC',
512 'post_type' => 'page',
513 );
514
515 $page_for_posts = get_option( 'page_for_posts' );
516
517 if ( is_numeric( $page_for_posts ) ) {
518 // Avoid querying for the "Posts Page" or "Blog Page" so we don't display 10 forms.
519 $page_query['post__not_in'] = array( $page_for_posts );
520 }
521
522 $random_page = get_posts( $page_query );
523
524 if ( ! $random_page ) {
525 return;
526 }
527
528 $random_page = reset( $random_page );
529
530 if ( ! is_a( $random_page, 'WP_Post' ) ) {
531 // The return type can also be int.
532 return;
533 }
534
535 query_posts(
536 array(
537 'post_type' => 'page',
538 'page_id' => $random_page->ID,
539 )
540 );
541
542 // Fixes Pro issue #3004. Prevent an undefined $post object.
543 // Otherwise WordPress themes will trigger a warning "Attempt to read property "comment_count" on null".
544 self::set_post_global( $random_page );
545 }
546
547 /**
548 * Set the WP $post global object. Used for in-theme preview when defining a page.
549 *
550 * @since 5.5.2
551 *
552 * @param WP_Post $page The page object.
553 *
554 * @return void
555 */
556 private static function set_post_global( $page ) {
557 global $post;
558 $post = $page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
559 }
560
561 /**
562 * @since 3.0
563 *
564 * @return void
565 */
566 private static function load_theme_preview() {
567 add_filter( 'wp_title', 'FrmFormsController::preview_title', 9999 );
568 add_filter( 'the_title', 'FrmFormsController::preview_page_title', 9999 );
569 add_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
570 add_action( 'loop_no_results', 'FrmFormsController::show_page_preview' );
571 add_filter( 'is_active_sidebar', '__return_false' );
572 FrmStylesController::enqueue_css( 'enqueue', true );
573
574 if ( false !== get_template_part( 'page' ) ) {
575 return;
576 }
577
578 if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
579 add_filter( 'body_class', 'FrmFormsController::preview_block_theme_body_classnames' );
580 }
581 self::fallback_when_page_template_part_is_not_supported_by_theme();
582 }
583
584 /**
585 * Add padding to the body for block themes.
586 *
587 * @since 6.5.2
588 *
589 * @param array $classes The body classes list.
590 *
591 * @return array
592 */
593 public static function preview_block_theme_body_classnames( $classes ) {
594 $classes[] = 'has-global-padding';
595 return $classes;
596 }
597
598 /**
599 * Not every theme supports get_template_part( 'page' ).
600 * When this is not supported, false is returned, and we can handle a fallback.
601 *
602 * @return void
603 */
604 private static function fallback_when_page_template_part_is_not_supported_by_theme() {
605 if ( ! have_posts() ) {
606 return;
607 }
608
609 the_post();
610 self::get_template( 'header' );
611
612 // Add some generic class names to the container to add some natural padding to the content.
613 // .entry-content catches the WordPress TwentyTwenty theme.
614 // .container catches Customizr content.
615 echo '<div class="container entry-content">';
616 the_content();
617 echo '</div>';
618
619 // Prevent extra unexpected forms in the footer.
620 // For some reason this happens in the Twenty Twenty Five theme.
621 add_filter( 'frm_filter_final_form', '__return_empty_string' );
622
623 self::get_template( 'footer' );
624 }
625
626 /**
627 * Calls core function to get a template part if it doesn't cause deprecation warnings. Otherwise skips the deprecation function call
628 * and renders required html fragments calling required functions.
629 *
630 * @since 6.7.1
631 *
632 * @param string $template
633 *
634 * @return void
635 */
636 private static function get_template( $template ) {
637 if ( self::should_try_getting_template( $template ) ) {
638 call_user_func( 'get_' . $template );
639 return;
640 }
641
642 if ( 'header' === $template ) {
643 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/preview/header.php';
644 return;
645 }
646
647 if ( 'footer' === $template ) {
648 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/preview/footer.php';
649 }
650 }
651
652 /**
653 * Returns true if calling a template function doesn't trigger deprecation warnings.
654 *
655 * @since 6.7.1
656 *
657 * @param string $template
658 *
659 * @return bool
660 */
661 private static function should_try_getting_template( $template ) {
662 $stylesheet_path = get_stylesheet_directory();
663 $template_path = get_template_directory();
664 $is_child_theme = $stylesheet_path !== $template_path;
665 $template_name = $template . '.php';
666
667 return file_exists( $stylesheet_path . '/' . $template_name ) || $is_child_theme && file_exists( $template_path . '/' . $template_name );
668 }
669
670 /**
671 * Set the page title for the theme preview page
672 *
673 * @since 3.0
674 *
675 * @param string $title
676 *
677 * @return string
678 */
679 public static function preview_page_title( $title ) {
680 return in_the_loop() ? self::preview_title( $title ) : $title;
681 }
682
683 /**
684 * Set the page title for the theme preview page.
685 *
686 * @since 3.0
687 *
688 * @param string $title
689 *
690 * @return string
691 */
692 public static function preview_title( $title ) {
693 return __( 'Form Preview', 'formidable' );
694 }
695
696 /**
697 * Set the page content for the theme preview page.
698 *
699 * @since 3.0
700 *
701 * @param string $content
702 *
703 * @return string
704 */
705 public static function preview_content( $content ) {
706 if ( in_the_loop() ) {
707 self::show_page_preview();
708 // Clear the content for the page we're using.
709 $content = '';
710 }
711
712 return $content;
713 }
714
715 /**
716 * @since 3.0
717 */
718 private static function load_direct_preview() {
719 $key = FrmAppHelper::simple_get( 'form', 'sanitize_title' );
720
721 // phpcs:ignore Universal.Operators.StrictComparisons
722 if ( $key == '' ) {
723 $key = FrmAppHelper::get_post_param( 'form', '', 'sanitize_title' );
724 }
725
726 if ( ! $key ) {
727 $error = __( 'Form key is missing', 'formidable' );
728 wp_die(
729 '<h1>' . esc_html( $error ) . '</h1>',
730 '<p>' . esc_html( $error ) . '</p>',
731 404
732 );
733 }
734
735 self::maybe_block_preview( $key );
736
737 $form = FrmForm::getAll( array( 'form_key' => $key ), '', 1 );
738
739 if ( ! $form ) {
740 $error = __( 'Form does not exist', 'formidable' );
741 wp_die(
742 '<h1>' . esc_html( $error ) . '</h1>',
743 '<p>' . esc_html( $error ) . '</p>',
744 404
745 );
746 }
747
748 header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
749
750 self::fix_deprecated_null_param_warning();
751
752 require FrmAppHelper::plugin_path() . '/classes/views/frm-entries/direct.php';
753 }
754
755 /**
756 * Block the form preview for the contact form by default if the user cannot view forms.
757 * This is to prevent the contact form being exploited.
758 * Since this form is added by default and every site uses the same key, it is too easy
759 * to guess this form.
760 *
761 * @since 6.20
762 *
763 * @param string $form_key Form key.
764 *
765 * @return void
766 */
767 public static function maybe_block_preview( $form_key ) {
768 if ( FrmFormsHelper::should_block_preview( $form_key ) ) {
769 $error = __( 'You do not have permission to view this form', 'formidable' );
770 wp_die(
771 '<h1>' . esc_html( $error ) . '</h1>',
772 '<p>' . esc_html( $error ) . '</p>',
773 403
774 );
775 }
776 }
777
778 /**
779 * Some themes have a null $src value.
780 * This function adds a filter to ensure that $src is not null.
781 * WP will call str_starts_with with the null value triggering a deprecated message otherwise.
782 *
783 * @since 6.10
784 *
785 * @return void
786 */
787 private static function fix_deprecated_null_param_warning() {
788 add_filter(
789 'script_loader_src',
790 /**
791 * @param string|null $src
792 *
793 * @return string
794 */
795 function ( $src ) {
796 return is_null( $src ) ? '' : $src;
797 }
798 );
799 }
800
801 public static function untrash() {
802 self::change_form_status( 'untrash' );
803 }
804
805 /**
806 * @param array $ids
807 *
808 * @return string
809 */
810 public static function bulk_untrash( $ids ) {
811 FrmAppHelper::permission_check( 'frm_edit_forms' );
812
813 $count = FrmForm::set_status( $ids, 'published' );
814
815 if ( ! $count ) {
816 // Don't show "0 forms restored" on refresh.
817 return '';
818 }
819
820 /* translators: %1$s: Number of forms */
821 return sprintf( _n( '%1$s form restored from the Trash.', '%1$s forms restored from the Trash.', $count, 'formidable' ), $count );
822 }
823
824 /**
825 * @since 3.06
826 */
827 public static function ajax_trash() {
828 FrmAppHelper::permission_check( 'frm_delete_forms' );
829 check_ajax_referer( 'frm_ajax', 'nonce' );
830 $form_id = FrmAppHelper::get_param( 'id', '', 'post', 'absint' );
831 FrmForm::set_status( $form_id, 'trash' );
832 wp_die();
833 }
834
835 public static function trash() {
836 self::change_form_status( 'trash' );
837 }
838
839 /**
840 * @param string $status
841 *
842 * @return void
843 */
844 public static function change_form_status( $status ) {
845 $available_status = array(
846 'untrash' => array(
847 'permission' => 'frm_edit_forms',
848 'new_status' => 'published',
849 ),
850 'trash' => array(
851 'permission' => 'frm_delete_forms',
852 'new_status' => 'trash',
853 ),
854 );
855
856 if ( ! isset( $available_status[ $status ] ) ) {
857 return;
858 }
859
860 FrmAppHelper::permission_check( $available_status[ $status ]['permission'] );
861
862 $params = FrmForm::list_page_params();
863
864 // Check nonce url.
865 check_admin_referer( $status . '_form_' . $params['id'] );
866
867 $count = 0;
868
869 if ( FrmForm::set_status( $params['id'], $available_status[ $status ]['new_status'] ) ) {
870 ++$count;
871 }
872
873 $form_type = FrmAppHelper::get_simple_request(
874 array(
875 'param' => 'form_type',
876 'type' => 'request',
877 )
878 );
879
880 /* translators: %1$s: Number of forms */
881 $available_status['untrash']['message'] = sprintf( _n( '%1$s form restored from the Trash.', '%1$s forms restored from the Trash.', $count, 'formidable' ), $count );
882
883 /* translators: %1$s: Number of forms, %2$s: Start link HTML, %3$s: End link HTML */
884 $available_status['trash']['message'] = sprintf( _n( '%1$s form moved to the Trash. %2$sUndo%3$s', '%1$s forms moved to the Trash. %2$sUndo%3$s', $count, 'formidable' ), $count, '<a href="' . esc_url( wp_nonce_url( '?page=formidable&frm_action=untrash&form_type=' . $form_type . '&id=' . $params['id'], 'untrash_form_' . $params['id'] ) ) . '">', '</a>' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
885
886 $message = $available_status[ $status ]['message'];
887
888 self::display_forms_list( $params, $message );
889 }
890
891 /**
892 * @param array $ids
893 *
894 * @return string
895 */
896 public static function bulk_trash( $ids ) {
897 FrmAppHelper::permission_check( 'frm_delete_forms' );
898
899 $count = 0;
900
901 foreach ( $ids as $id ) {
902 if ( FrmForm::trash( $id ) ) {
903 ++$count;
904 }
905 }
906
907 if ( ! $count ) {
908 return '';
909 }
910
911 $current_page = FrmAppHelper::get_simple_request(
912 array(
913 'param' => 'form_type',
914 'type' => 'request',
915 )
916 );
917
918 return sprintf(
919 /* translators: %1$s: Number of forms, %2$s: Start link HTML, %3$s: End link HTML */
920 _n( '%1$s form moved to the Trash. %2$sUndo%3$s', '%1$s forms moved to the Trash. %2$sUndo%3$s', $count, 'formidable' ),
921 $count,
922 '<a href="' . esc_url( wp_nonce_url( '?page=formidable&frm_action=list&action=bulk_untrash&form_type=' . $current_page . '&item-action=' . implode( ',', $ids ), 'bulk-toplevel_page_formidable' ) ) . '">', // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
923 '</a>'
924 );
925 }
926
927 public static function destroy() {
928 FrmAppHelper::permission_check( 'frm_delete_forms' );
929
930 $params = FrmForm::list_page_params();
931
932 // Check nonce url.
933 check_admin_referer( 'destroy_form_' . $params['id'] );
934
935 $count = 0;
936
937 if ( FrmForm::destroy( $params['id'] ) ) {
938 ++$count;
939 }
940
941 /* translators: %1$s: Number of forms */
942 $message = sprintf( _n( '%1$s Form Permanently Deleted', '%1$s Forms Permanently Deleted', $count, 'formidable' ), $count );
943
944 self::display_forms_list( $params, $message );
945 }
946
947 /**
948 * @param array $ids
949 *
950 * @return string
951 */
952 public static function bulk_destroy( $ids ) {
953 FrmAppHelper::permission_check( 'frm_delete_forms' );
954
955 $count = 0;
956
957 foreach ( $ids as $id ) {
958 $d = FrmForm::destroy( $id );
959
960 if ( $d ) {
961 ++$count;
962 }
963 }
964
965 if ( $count ) {
966 /* translators: %1$s: Number of forms */
967 return sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
968 }
969
970 return '';
971 }
972
973 /**
974 * @since 6.7.1 Function went from private to public.
975 */
976 public static function delete_all() {
977 // Check nonce url.
978 $permission_error = FrmAppHelper::permission_nonce_error( 'frm_delete_forms', '_wpnonce', 'bulk-toplevel_page_formidable' );
979
980 if ( $permission_error !== false ) {
981 self::display_forms_list( array(), '', array( $permission_error ) );
982 return;
983 }
984
985 $count = FrmForm::scheduled_delete( time() );
986 $url = remove_query_arg( array( 'delete_all' ) );
987 $url .= '&message=forms_permanently_deleted&forms_deleted=' . $count;
988
989 wp_safe_redirect( $url );
990 die();
991 }
992
993 /**
994 * Create a new form from the modal.
995 *
996 * @since 4.0
997 */
998 public static function build_new_form() {
999 FrmAppHelper::permission_check( 'frm_edit_forms' );
1000 check_ajax_referer( 'frm_ajax', 'nonce' );
1001
1002 $new_values = self::get_modal_values();
1003 $new_values['form_key'] = $new_values['name'];
1004 $new_values['options'] = array(
1005 'antispam' => 1,
1006 'on_submit_migrated' => 1,
1007 );
1008
1009 /**
1010 * Allows changing form values before creating from the modal.
1011 *
1012 * @since 5.4
1013 *
1014 * @param array $values Form values.
1015 */
1016 $new_values = apply_filters( 'frm_new_form_values', $new_values );
1017
1018 $form_id = FrmForm::create( $new_values );
1019 /**
1020 * @since 5.3
1021 *
1022 * @param int $form_id
1023 */
1024 do_action( 'frm_build_new_form', $form_id );
1025
1026 self::create_submit_button_field( $form_id );
1027 self::create_default_on_submit_action( $form_id );
1028 self::create_default_email_action( $form_id );
1029
1030 $response = array(
1031 'redirect' => FrmForm::get_edit_link( $form_id ) . '&new_template=true',
1032 );
1033
1034 echo wp_json_encode( $response );
1035 wp_die();
1036 }
1037
1038 /**
1039 * Before creating a new form, get the name and description from the modal.
1040 *
1041 * @since 4.0
1042 *
1043 * @return array<string,string>
1044 */
1045 public static function get_modal_values() {
1046 $name = FrmAppHelper::get_param( 'name', '', 'post', 'sanitize_text_field' );
1047 $desc = FrmAppHelper::get_param( 'desc', '', 'post', 'sanitize_textarea_field' );
1048
1049 return array(
1050 'name' => $name,
1051 'description' => $desc,
1052 );
1053 }
1054
1055 /**
1056 * Inserts Formidable button
1057 * Hook exists since 2.5.0
1058 *
1059 * @since 2.0.15
1060 *
1061 * @return void
1062 */
1063 public static function insert_form_button() {
1064 if ( ! current_user_can( 'frm_view_forms' ) ) {
1065 return;
1066 }
1067
1068 // Store the result in memory and re-use it when this function is called multiple times.
1069 // This helps speed up the form builder when there are a lot of HTML fields, where this
1070 // Button is inserted once per HTML field.
1071 // In a form with 66 HTML fields, this saves 0.5 seconds on page load time, tested locally.
1072 if ( ! isset( self::$formidable_tinymce_button ) ) {
1073 FrmAppHelper::load_admin_wide_js();
1074 $menu_name = FrmAppHelper::get_menu_name();
1075 $icon = apply_filters( 'frm_media_icon', FrmAppHelper::svg_logo() );
1076 self::$formidable_tinymce_button = '<a href="#TB_inline?width=50&height=50&inlineId=frm_insert_form" class="thickbox button add_media frm_insert_form" title="' . esc_attr__( 'Add forms and content', 'formidable' ) . '">' . FrmAppHelper::kses( $icon, 'all' ) . ' ' . esc_html( $menu_name ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
1077 }
1078 echo self::$formidable_tinymce_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1079 }
1080
1081 /**
1082 * @return void
1083 */
1084 public static function insert_form_popup() {
1085 if ( ! self::should_insert_form_popup() ) {
1086 return;
1087 }
1088
1089 FrmAppHelper::load_admin_wide_js();
1090
1091 $shortcodes = array(
1092 'formidable' => array(
1093 'name' => __( 'Form', 'formidable' ),
1094 'label' => __( 'Insert a Form', 'formidable' ),
1095 ),
1096 );
1097 $shortcodes = apply_filters( 'frm_popup_shortcodes', $shortcodes );
1098
1099 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/insert_form_popup.php';
1100
1101 if ( ! FrmAppHelper::is_form_builder_page() || class_exists( '_WP_Editors', false ) ) {
1102 return;
1103 }
1104
1105 // Initialize a wysiwyg so we have usable settings defined in tinyMCEPreInit.mceInit
1106 require ABSPATH . WPINC . '/class-wp-editor.php';
1107 // phpcs:disable Generic.WhiteSpace.ScopeIndent
1108 ?>
1109 <div class="frm_hidden">
1110 <?php wp_editor( '', 'frm_description_placeholder', array() ); ?>
1111 </div>
1112 <?php
1113 // phpcs:enable Generic.WhiteSpace.ScopeIndent
1114 }
1115
1116 /**
1117 * Check the page being loaded, determine if this is a page that should include the form popup.
1118 *
1119 * @since 5.0.14
1120 *
1121 * @return bool
1122 */
1123 private static function should_insert_form_popup() {
1124 if ( FrmAppHelper::is_form_builder_page() ) {
1125 return true;
1126 }
1127
1128 $page = basename( FrmAppHelper::get_server_value( 'PHP_SELF' ) );
1129 return in_array( $page, array( 'post.php', 'page.php', 'page-new.php', 'post-new.php' ), true );
1130 }
1131
1132 public static function get_shortcode_opts() {
1133 FrmAppHelper::permission_check( 'frm_view_forms' );
1134 check_ajax_referer( 'frm_ajax', 'nonce' );
1135
1136 $shortcode = FrmAppHelper::get_post_param( 'shortcode', '', 'sanitize_text_field' );
1137
1138 if ( ! $shortcode ) {
1139 wp_die();
1140 }
1141
1142 echo '<div id="sc-opts-' . esc_attr( $shortcode ) . '" class="frm_shortcode_option">';
1143 echo '<input type="radio" name="frmsc" value="' . esc_attr( $shortcode ) . '" id="sc-' . esc_attr( $shortcode ) . '" class="frm_hidden" />';
1144
1145 $form_id = '';
1146 $opts = array();
1147 switch ( $shortcode ) {
1148 case 'formidable':
1149 $opts = array(
1150 'form_id' => 'id',
1151 'title' => array(
1152 'val' => 1,
1153 'label' => __( 'Display form title', 'formidable' ),
1154 ),
1155 'description' => array(
1156 'val' => 1,
1157 'label' => __( 'Display form description', 'formidable' ),
1158 ),
1159 'minimize' => array(
1160 'val' => 1,
1161 'label' => __( 'Minimize form HTML', 'formidable' ),
1162 ),
1163 );
1164 }
1165
1166 $opts = apply_filters( 'frm_sc_popup_opts', $opts, $shortcode );
1167
1168 if ( isset( $opts['form_id'] ) && is_string( $opts['form_id'] ) ) {
1169 // Allow other shortcodes to use the required form id option
1170 $form_id = $opts['form_id'];
1171 unset( $opts['form_id'] );
1172 }
1173
1174 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/shortcode_opts.php';
1175
1176 echo '</div>';
1177
1178 wp_die();
1179 }
1180
1181 /**
1182 * Display list of forms in a table.
1183 *
1184 * @param array $params
1185 * @param string $message
1186 * @param array $errors
1187 *
1188 * @return void
1189 */
1190 public static function display_forms_list( $params = array(), $message = '', $errors = array() ) {
1191 FrmAppHelper::permission_check( 'frm_view_forms' );
1192
1193 global $wpdb, $frm_vars;
1194
1195 if ( ! $params ) {
1196 $params = FrmForm::list_page_params();
1197 }
1198
1199 /**
1200 * @since 5.3.1
1201 *
1202 * @param string $table_class Class name for List Helper.
1203 */
1204 $table_class = apply_filters( 'frm_forms_list_class', 'FrmFormsListHelper' );
1205 $wp_list_table = new $table_class( compact( 'params' ) );
1206 $pagenum = $wp_list_table->get_pagenum();
1207
1208 $wp_list_table->prepare_items();
1209
1210 $total_pages = $wp_list_table->get_pagination_arg( 'total_pages' );
1211
1212 if ( $pagenum > $total_pages && $total_pages > 0 ) {
1213 wp_safe_redirect( esc_url_raw( add_query_arg( 'paged', $total_pages ) ) );
1214 die();
1215 }
1216
1217 $error = FrmInbox::check_for_error();
1218
1219 if ( $error ) {
1220 $show_messages = array( $error['subject'] . '. ' . $error['message'] );
1221 }
1222
1223 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/list.php';
1224 }
1225
1226 /**
1227 * @param array<string,string> $columns
1228 *
1229 * @return array<string,string>
1230 */
1231 public static function get_columns( $columns ) {
1232 $columns['cb'] = '<input type="checkbox" />';
1233 $columns['name'] = esc_html__( 'Form Title', 'formidable' );
1234 $columns['entries'] = esc_html__( 'Entries', 'formidable' );
1235 $columns['id'] = 'ID';
1236 $columns['form_key'] = esc_html__( 'Key', 'formidable' );
1237
1238 if ( 'trash' !== FrmAppHelper::simple_get( 'form_type' ) ) {
1239 $columns['shortcode'] = esc_html__( 'Actions', 'formidable' );
1240 }
1241
1242 $columns['created_at'] = esc_html__( 'Date', 'formidable' );
1243
1244 add_screen_option(
1245 'per_page',
1246 array(
1247 'label' => __( 'Forms', 'formidable' ),
1248 'default' => 20,
1249 'option' => 'formidable_page_formidable_per_page',
1250 )
1251 );
1252
1253 return $columns;
1254 }
1255
1256 /**
1257 * @return array<string,string>
1258 */
1259 public static function get_sortable_columns() {
1260 return array(
1261 'id' => 'id',
1262 'name' => 'name',
1263 'description' => 'description',
1264 'form_key' => 'form_key',
1265 'created_at' => 'created_at',
1266 );
1267 }
1268
1269 /**
1270 * @param mixed $hidden_columns
1271 *
1272 * @return array
1273 */
1274 public static function hidden_columns( $hidden_columns ) {
1275 if ( ! is_array( $hidden_columns ) ) {
1276 $hidden_columns = array();
1277 }
1278
1279 $type = FrmAppHelper::get_simple_request(
1280 array(
1281 'param' => 'form_type',
1282 'type' => 'request',
1283 )
1284 );
1285
1286 if ( $type === 'template' ) {
1287 $hidden_columns[] = 'id';
1288 $hidden_columns[] = 'form_key';
1289 }
1290
1291 return $hidden_columns;
1292 }
1293
1294 /**
1295 * @param mixed $save
1296 * @param string $option
1297 * @param int|string $value
1298 *
1299 * @return mixed
1300 */
1301 public static function save_per_page( $save, $option, $value ) {
1302 if ( $option === 'formidable_page_formidable_per_page' ) {
1303 return (int) $value;
1304 }
1305
1306 return $save;
1307 }
1308
1309 /**
1310 * @param int|string $id
1311 * @param array $errors
1312 * @param string $message
1313 * @param bool $create_link
1314 *
1315 * @return void
1316 */
1317 private static function get_edit_vars( $id, $errors = array(), $message = '', $create_link = false ) {
1318 global $frm_vars;
1319
1320 $form = FrmForm::getOne( $id );
1321 $error_args = array(
1322 'title' => __( 'You can\'t edit the form', 'formidable' ),
1323 'body' => __( 'You are trying to edit a form that does not exist', 'formidable' ),
1324 'cancel_url' => admin_url( 'admin.php?page=formidable' ),
1325 'continue_url' => add_query_arg(
1326 array(
1327 'page' => 'formidable',
1328 )
1329 ),
1330 );
1331
1332 if ( ! $form ) {
1333 FrmAppController::show_error_modal( $error_args );
1334 return;
1335 }
1336
1337 if ( 'trash' === $form->status ) {
1338 $error_args['body'] = __( 'The form you\'re trying to edit is in trash. You must restore it first before you can make changes', 'formidable' );
1339 $error_args['continue_url'] = add_query_arg(
1340 array(
1341 'page' => 'formidable',
1342 '_wpnonce' => wp_create_nonce( 'untrash_form_' . $id ),
1343 'form_type' => 'trash',
1344 'frm_action' => 'untrash',
1345 'id' => $id,
1346 )
1347 );
1348 $error_args['continue_text'] = __( 'Restore form', 'formidable' );
1349 FrmAppController::show_error_modal( $error_args );
1350 return;
1351 }
1352
1353 if ( $form->parent_form_id ) {
1354 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
1355 wp_die( sprintf( esc_html__( 'You are trying to edit a child form. Please edit from %1$shere%2$s', 'formidable' ), '<a href="' . esc_url( FrmForm::get_edit_link( $form->parent_form_id ) ) . '">', '</a>' ) ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
1356 }
1357
1358 $frm_field_selection = FrmField::field_selection();
1359 $fields = FrmField::get_all_for_form( $form->id );
1360
1361 // Automatically add end section fields if they don't exist (2.0 migration).
1362 $reset_fields = false;
1363 FrmFormsHelper::auto_add_end_section_fields( $form, $fields, $reset_fields );
1364 FrmSubmitHelper::maybe_create_submit_field( $form, $fields, $reset_fields );
1365
1366 if ( $reset_fields ) {
1367 $fields = FrmField::get_all_for_form( $form->id, '', 'exclude' );
1368 }
1369
1370 unset( $reset_fields );
1371
1372 $args = array( 'parent_form_id' => $form->id );
1373 $values = FrmAppHelper::setup_edit_vars( $form, 'forms', '', true, array(), $args );
1374
1375 /**
1376 * Allows modifying the list of fields in the form builder.
1377 *
1378 * @since 5.0.04
1379 *
1380 * @param object[] $fields Array of fields.
1381 * @param array $args The arguments. Contains `form`.
1382 */
1383 $values['fields'] = apply_filters( 'frm_fields_in_form_builder', $fields, compact( 'form' ) );
1384
1385 $edit_message = __( 'Form was successfully updated.', 'formidable' );
1386
1387 if ( $form->is_template && $message === $edit_message ) {
1388 $message = __( 'Template was successfully updated.', 'formidable' );
1389 }
1390
1391 self::maybe_update_form_builder_message( $message );
1392
1393 $has_fields = ! empty( $values['fields'] ) && ! FrmSubmitHelper::only_contains_submit_field( $values['fields'] );
1394
1395 if ( defined( 'DOING_AJAX' ) ) {
1396 wp_die();
1397 }
1398
1399 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/edit.php';
1400 }
1401
1402 /**
1403 * @param array $fields
1404 *
1405 * @return array
1406 */
1407 public static function update_form_builder_fields( $fields ) {
1408 foreach ( $fields as $field ) {
1409 $field->do_not_include_icons = true;
1410 }
1411 return $fields;
1412 }
1413
1414 /**
1415 * @param string $message
1416 *
1417 * @return void
1418 */
1419 public static function maybe_update_form_builder_message( &$message ) {
1420 if ( 'form_duplicated' === FrmAppHelper::simple_get( 'message' ) ) {
1421 $message = __( 'Form was Successfully Copied', 'formidable' );
1422 }
1423 }
1424
1425 /**
1426 * @param int|string $id
1427 * @param array $errors
1428 * @param array|string $args
1429 *
1430 * @return void
1431 */
1432 public static function get_settings_vars( $id, $errors = array(), $args = array() ) {
1433 FrmAppHelper::permission_check( 'frm_edit_forms' );
1434
1435 global $frm_vars;
1436
1437 self::maybe_print_media_templates();
1438
1439 if ( ! is_array( $args ) ) {
1440 // For reverse compatibility.
1441 $args = array(
1442 'message' => $args,
1443 );
1444 }
1445
1446 $defaults = array(
1447 'message' => '',
1448 'warnings' => array(),
1449 );
1450 $args = array_merge( $defaults, $args );
1451 $message = $args['message'];
1452 $warnings = $args['warnings'];
1453 $form = FrmForm::getOne( $id );
1454 $fields = FrmField::get_all_for_form( $id );
1455 $values = FrmAppHelper::setup_edit_vars( $form, 'forms', $fields, true );
1456
1457 /**
1458 * Allows changing fields in the form settings.
1459 *
1460 * @since 5.0.04
1461 *
1462 * @param array $fields Array of fields.
1463 * @param array $args The arguments. Contains `form`.
1464 */
1465 $values['fields'] = apply_filters( 'frm_fields_in_settings', $values['fields'], compact( 'form' ) );
1466
1467 self::clean_submit_html( $values );
1468
1469 $sections = self::get_settings_tabs( $values );
1470 $current = FrmAppHelper::simple_get( 't', 'sanitize_title', 'advanced_settings' );
1471
1472 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings.php';
1473 }
1474
1475 /**
1476 * Print WordPress media templates email actions does not trigger a "Uncaught Error: Template not found: #tmpl-media-selection" error when the media button is clicked.
1477 *
1478 * @since 6.10
1479 *
1480 * @return void
1481 */
1482 private static function maybe_print_media_templates() {
1483 if ( FrmAppHelper::pro_is_included() ) {
1484 // This issue does not exist when Pro is active so exit early.
1485 return;
1486 }
1487
1488 add_action(
1489 'wp_enqueue_editor',
1490 function () {
1491 wp_print_media_templates();
1492 }
1493 );
1494 }
1495
1496 /**
1497 * @since 4.0
1498 *
1499 * @param array $atts
1500 *
1501 * @return void
1502 */
1503 public static function form_publish_button( $atts ) {
1504 $values = $atts['values'];
1505 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/_publish_box.php';
1506 }
1507
1508 /**
1509 * Get a list of all the settings tabs for the form settings page.
1510 *
1511 * @since 4.0
1512 *
1513 * @param array $values
1514 *
1515 * @return array
1516 */
1517 private static function get_settings_tabs( $values ) {
1518 $sections = array(
1519 'advanced' => array(
1520 'name' => __( 'General', 'formidable' ),
1521 'title' => __( 'General Form Settings', 'formidable' ),
1522 'function' => array( self::class, 'advanced_settings' ),
1523 'icon' => 'frmfont frm_settings_icon',
1524 ),
1525 'email' => array(
1526 'name' => __( 'Actions & Notifications', 'formidable' ),
1527 'function' => array( 'FrmFormActionsController', 'email_settings' ),
1528 'id' => 'frm_notification_settings',
1529 'icon' => 'frmfont frm_mail_bulk_icon',
1530 ),
1531 'permissions' => array(
1532 'name' => __( 'Form Permissions', 'formidable' ),
1533 'icon' => 'frmfont frm_lock_closed_icon',
1534 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1535 'data' => array(
1536 'medium' => 'permissions',
1537 'upgrade' => __( 'Form Permissions', 'formidable' ),
1538 'message' => __( 'Allow editing, protect forms and files, limit entries, and save drafts. Upgrade to get form and entry permissions.', 'formidable' ),
1539 'screenshot' => 'permissions.png',
1540 'learn-more' => FrmAppHelper::get_doc_url( 'general-form-settings/#kb-form-permissions', 'form-permissions-settings' ),
1541 ),
1542 ),
1543 'scheduling' => array(
1544 'name' => __( 'Form Scheduling', 'formidable' ),
1545 'icon' => 'frmfont frm_calendar_icon',
1546 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1547 'data' => array(
1548 'medium' => 'scheduling',
1549 'upgrade' => __( 'Form scheduling settings', 'formidable' ),
1550 'screenshot' => 'scheduling.png',
1551 'learn-more' => FrmAppHelper::get_doc_url( 'general-form-settings/#kb-form-scheduling', 'form-scheduling-settings' ),
1552 ),
1553 ),
1554 'buttons' => array(
1555 'name' => __( 'Buttons', 'formidable' ),
1556 'class' => self::class,
1557 'function' => 'buttons_settings',
1558 'icon' => 'frmfont frm_button_icon',
1559 ),
1560 'landing' => array(
1561 'name' => __( 'Form Landing Page', 'formidable' ),
1562 'icon' => 'frmfont frm_file_text_icon',
1563 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1564 'data' => FrmAppHelper::get_landing_page_upgrade_data_params(),
1565 ),
1566 'chat' => array(
1567 'name' => __( 'Conversational Forms', 'formidable' ),
1568 'icon' => 'frmfont frm_chat_forms_icon',
1569 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1570 'data' => FrmAppHelper::get_upgrade_data_params(
1571 'chat',
1572 array(
1573 'upgrade' => __( 'Conversational Forms', 'formidable' ),
1574 'message' => __( 'Ask one question at a time for automated conversations.', 'formidable' ),
1575 'screenshot' => 'chat.png',
1576 'learn-more' => FrmAppHelper::get_doc_url( 'conversational-forms', 'conversational-forms-settings', false ),
1577 )
1578 ),
1579 ),
1580 'abandonment' => array(
1581 'name' => __( 'Form Abandonment', 'formidable' ),
1582 'icon' => 'frmfont frm_abandoned_icon',
1583 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1584 'data' => FrmAppHelper::get_upgrade_data_params(
1585 'abandonment',
1586 array(
1587 'upgrade' => __( 'Form abandonment settings', 'formidable' ),
1588 'message' => __( 'Unlock the power of data capture to boost lead generation and master the art of form optimization.', 'formidable' ),
1589 'screenshot' => 'abandonment.png',
1590 'learn-more' => FrmAppHelper::get_doc_url( 'features/form-abandonment-wordpress-plugin', 'form-abandonment-settings', false ),
1591 )
1592 ),
1593 ),
1594 'html' => array(
1595 'name' => __( 'Customize HTML', 'formidable' ),
1596 'class' => self::class,
1597 'function' => 'html_settings',
1598 'icon' => 'frmfont frm_code_icon',
1599 ),
1600 );
1601
1602 if ( ! has_action( 'frm_add_form_style_tab_options' ) && ! has_action( 'frm_add_form_button_options' ) ) {
1603 unset( $sections['buttons'] );
1604 }
1605
1606 $sections = apply_filters( 'frm_add_form_settings_section', $sections, $values );
1607
1608 foreach ( $sections as $key => $section ) {
1609 $defaults = array(
1610 'html_class' => '',
1611 'name' => ucfirst( $key ),
1612 'icon' => 'frmfont frm_settings_icon',
1613 );
1614
1615 $section = array_merge( $defaults, $section );
1616
1617 if ( ! isset( $section['anchor'] ) ) {
1618 $section['anchor'] = $key;
1619 }
1620
1621 $section['anchor'] .= '_settings';
1622
1623 if ( ! isset( $section['title'] ) ) {
1624 $section['title'] = $section['name'];
1625 }
1626
1627 if ( ! isset( $section['id'] ) ) {
1628 $section['id'] = $section['anchor'];
1629 }
1630
1631 $sections[ $key ] = $section;
1632 }//end foreach
1633
1634 return $sections;
1635 }
1636
1637 /**
1638 * @since 4.0
1639 *
1640 * @param array $values
1641 *
1642 * @return void
1643 */
1644 public static function advanced_settings( $values ) {
1645 $first_h3 = 'frm_first_h3';
1646 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-advanced.php';
1647 }
1648
1649 /**
1650 * @param array $values
1651 *
1652 * @return void
1653 */
1654 public static function render_spam_settings( $values ) {
1655 if ( function_exists( 'akismet_http_post' ) ) {
1656 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/akismet.php';
1657 }
1658 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/stopforumspam.php';
1659 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/antispam.php';
1660 }
1661
1662 /**
1663 * @since 4.0
1664 *
1665 * @param array $values
1666 *
1667 * @return void
1668 */
1669 public static function buttons_settings( $values ) {
1670 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-buttons.php';
1671 }
1672
1673 /**
1674 * @since 4.0
1675 *
1676 * @param array $values
1677 *
1678 * @return void
1679 */
1680 public static function html_settings( $values ) {
1681 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-html.php';
1682 }
1683
1684 /**
1685 * Replace old Submit Button href with new href to avoid errors in Chrome
1686 *
1687 * @since 2.03.08
1688 *
1689 * @param array|bool $values
1690 *
1691 * @return void
1692 */
1693 private static function clean_submit_html( &$values ) {
1694 if ( is_array( $values ) && isset( $values['submit_html'] ) ) {
1695 $values['submit_html'] = str_replace( 'javascript:void(0)', '#', $values['submit_html'] );
1696 }
1697 }
1698
1699 /**
1700 * Updates classes used in submit and prev buttons to avoid conflict with twenty twenty-one theme.
1701 *
1702 * @param array $classes
1703 *
1704 * @return array
1705 */
1706 public static function update_button_classes( $classes ) {
1707 if ( function_exists( 'twenty_twenty_one_setup' ) ) {
1708 $classes[] = 'has-text-color has-background';
1709 }
1710
1711 return $classes;
1712 }
1713
1714 /**
1715 * @since 6.27 Added $template_path parameter.
1716 *
1717 * @param int|string $form_id
1718 * @param string $class
1719 * @param string $template_path The path to a template file to use instead of the default.
1720 *
1721 * @return void
1722 */
1723 public static function mb_tags_box( $form_id, $class = '', $template_path = 'default' ) {
1724 $fields = FrmField::get_all_for_form( $form_id, '', 'include' );
1725
1726 /**
1727 * Allows modifying the list of fields in the tags box.
1728 *
1729 * @since 5.0.04
1730 *
1731 * @param array $fields The list of fields.
1732 * @param array $args The arguments. Contains `form_id`.
1733 */
1734 $fields = apply_filters( 'frm_fields_in_tags_box', $fields, compact( 'form_id' ) );
1735 $linked_forms = array();
1736 $col = 'one';
1737 $settings_tab = FrmAppHelper::is_admin_page( 'formidable' );
1738 $cond_shortcodes = apply_filters( 'frm_conditional_shortcodes', array() );
1739 $entry_shortcodes = self::get_shortcode_helpers( $settings_tab );
1740 $advanced_helpers = self::advanced_helpers( compact( 'fields', 'form_id' ) );
1741
1742 if ( 'default' === $template_path || ! file_exists( $template_path ) ) {
1743 include FrmAppHelper::plugin_path() . '/classes/views/shared/mb_adv_info.php';
1744 return;
1745 }
1746
1747 include $template_path;
1748 }
1749
1750 /**
1751 * @since 3.04.01
1752 *
1753 * @param array $atts
1754 *
1755 * @return array
1756 */
1757 private static function advanced_helpers( $atts ) {
1758 $advanced_helpers = array(
1759 'default' => array(
1760 'heading' => __( 'Customize field values with the following parameters.', 'formidable' ),
1761 'codes' => self::get_advanced_shortcodes(),
1762 ),
1763 );
1764
1765 $user_fields = self::user_shortcodes();
1766
1767 if ( $user_fields ) {
1768 $user_helpers = array();
1769
1770 foreach ( $user_fields as $uk => $uf ) {
1771 $user_helpers[ '|user_id| show="' . $uk . '"' ] = $uf;
1772 unset( $uk, $uf );
1773 }
1774
1775 $advanced_helpers['user_id'] = array(
1776 'codes' => $user_helpers,
1777 );
1778 }
1779
1780 /**
1781 * Add extra helper shortcodes on the Advanced tab in form settings and views
1782 *
1783 * @since 3.04.01
1784 *
1785 * @param array $advanced_helpers
1786 * @param array $atts - Includes fields and form_id
1787 */
1788 return apply_filters( 'frm_advanced_helpers', $advanced_helpers, $atts );
1789 }
1790
1791 /**
1792 * Get an array of the options to display in the advanced tab
1793 * of the customization panel
1794 *
1795 * @since 2.0.6
1796 *
1797 * @return array
1798 */
1799 private static function get_advanced_shortcodes() {
1800 $adv_shortcodes = array(
1801 'x sep=", "' => array(
1802 'label' => __( 'Separator', 'formidable' ),
1803 'title' => __( 'Use a different separator for checkbox fields', 'formidable' ),
1804 ),
1805 'x format="d-m-Y"' => array(
1806 'label' => __( 'Date Format', 'formidable' ),
1807 ),
1808 'x show="field_label"' => array(
1809 'label' => __( 'Field Label', 'formidable' ),
1810 ),
1811 'x wpautop=0' => array(
1812 'label' => __( 'No Auto P', 'formidable' ),
1813 'title' => __( 'Do not automatically add any paragraphs or line breaks', 'formidable' ),
1814 ),
1815 );
1816
1817 // __( 'Leave blank instead of defaulting to User Login', 'formidable' ) : blank=1
1818
1819 return apply_filters( 'frm_advanced_shortcodes', $adv_shortcodes );
1820 }
1821
1822 /**
1823 * @since 3.04.01
1824 *
1825 * @return array
1826 */
1827 private static function user_shortcodes() {
1828 $options = array(
1829 'ID' => __( 'User ID', 'formidable' ),
1830 'first_name' => __( 'First Name', 'formidable' ),
1831 'last_name' => __( 'Last Name', 'formidable' ),
1832 'display_name' => __( 'Display Name', 'formidable' ),
1833 'user_login' => __( 'User Login', 'formidable' ),
1834 'user_email' => __( 'Email', 'formidable' ),
1835 'avatar' => __( 'Avatar', 'formidable' ),
1836 'author_link' => __( 'Author Link', 'formidable' ),
1837 );
1838
1839 return apply_filters( 'frm_user_shortcodes', $options );
1840 }
1841
1842 /**
1843 * Get an array of the helper shortcodes to display in the customization panel
1844 *
1845 * @since 2.0.6
1846 *
1847 * @param bool $settings_tab
1848 *
1849 * @return array
1850 */
1851 private static function get_shortcode_helpers( $settings_tab ) {
1852 $entry_shortcodes = array(
1853 'id' => __( 'Entry ID', 'formidable' ),
1854 'key' => __( 'Entry Key', 'formidable' ),
1855 'post_id' => __( 'Post ID', 'formidable' ),
1856 'ip' => __( 'User IP', 'formidable' ),
1857 'created-at' => __( 'Entry created', 'formidable' ),
1858 'updated-at' => __( 'Entry updated', 'formidable' ),
1859 '' => '',
1860 'siteurl' => __( 'Site URL', 'formidable' ),
1861 'sitename' => __( 'Site Name', 'formidable' ),
1862 'form_name' => __( 'Form Name', 'formidable' ),
1863 );
1864
1865 $entry_shortcodes = array_merge( FrmShortcodeHelper::get_contextual_shortcode_values(), $entry_shortcodes );
1866
1867 if ( ! FrmAppHelper::pro_is_installed() ) {
1868 unset( $entry_shortcodes['post_id'] );
1869 }
1870
1871 /**
1872 * Use this hook to add or remove buttons in the helpers section
1873 * in the customization panel
1874 *
1875 * @since 2.0.6
1876 *
1877 * @param array $entry_shortcodes
1878 * @param bool $settings_tab
1879 */
1880 return apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab );
1881 }
1882
1883 /**
1884 * Insert the form class setting into the form.
1885 *
1886 * @param stdClass $form
1887 *
1888 * @return void
1889 */
1890 public static function form_classes( $form ) {
1891 if ( isset( $form->options['form_class'] ) ) {
1892 echo esc_attr( sanitize_text_field( $form->options['form_class'] ) );
1893 }
1894
1895 if ( ! empty( $form->options['js_validate'] ) ) {
1896 echo ' frm_js_validate ';
1897 self::add_js_validate_form_to_global_vars( $form );
1898 }
1899
1900 if ( FrmForm::is_ajax_on( $form ) ) {
1901 echo ' frm_ajax_submit ';
1902 }
1903 }
1904
1905 /**
1906 * @since 5.0.03
1907 *
1908 * @param stdClass $form
1909 */
1910 public static function add_js_validate_form_to_global_vars( $form ) {
1911 global $frm_vars;
1912
1913 if ( ! isset( $frm_vars['js_validate_forms'] ) ) {
1914 $frm_vars['js_validate_forms'] = array();
1915 }
1916 $frm_vars['js_validate_forms'][ $form->id ] = $form;
1917 }
1918
1919 public static function get_email_html() {
1920 FrmAppHelper::permission_check( 'frm_view_forms' );
1921 check_ajax_referer( 'frm_ajax', 'nonce' );
1922
1923 echo FrmEntriesController::show_entry_shortcode( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1924 array(
1925 'form_id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1926 'default_email' => true,
1927 'plain_text' => FrmAppHelper::get_post_param( 'plain_text', '', 'absint' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1928 )
1929 );
1930 wp_die();
1931 }
1932
1933 /**
1934 * @param string $content
1935 * @param int|stdClass|string $form
1936 * @param false|int|stdClass|string $entry
1937 *
1938 * @return string
1939 */
1940 public static function filter_content( $content, $form, $entry = false ) {
1941 $content = self::replace_form_name_shortcodes( $content, $form );
1942
1943 self::get_entry_by_param( $entry );
1944
1945 if ( ! $entry ) {
1946 return $content;
1947 }
1948
1949 if ( is_object( $form ) ) {
1950 $form = $form->id;
1951 }
1952
1953 /*
1954 * Repeater actions adds `parent_entry` to `$entry` store the parent entry data. If `parent_entry` is not empty,
1955 * use the parent form ID instead of repeater form ID to fix the parent form field shortcodes doesn't work.
1956 */
1957 if ( ! empty( $entry->parent_entry ) ) {
1958 $form = $entry->parent_entry->form_id;
1959 }
1960
1961 $shortcodes = FrmFieldsHelper::get_shortcodes( $content, $form );
1962
1963 return apply_filters( 'frm_replace_content_shortcodes', $content, $entry, $shortcodes );
1964 }
1965
1966 /**
1967 * Replace any [form_name] shortcodes in a string.
1968 *
1969 * @since 5.5
1970 *
1971 * @param array|string $string
1972 * @param int|stdClass|string $form
1973 *
1974 * @return array|string
1975 */
1976 public static function replace_form_name_shortcodes( $string, $form ) {
1977 if ( ! is_string( $string ) ) {
1978 return $string;
1979 }
1980
1981 if ( ! str_contains( $string, '[form_name]' ) ) {
1982 return $string;
1983 }
1984
1985 if ( ! is_object( $form ) ) {
1986 $form = FrmForm::getOne( $form );
1987 }
1988
1989 $form_name = is_object( $form ) ? $form->name : '';
1990 return str_replace( '[form_name]', $form_name, $string );
1991 }
1992
1993 /**
1994 * @param false|int|stdClass|string $entry
1995 *
1996 * @return void
1997 */
1998 private static function get_entry_by_param( &$entry ) {
1999 if ( ! $entry || ! is_object( $entry ) ) {
2000 if ( ! $entry || ! is_numeric( $entry ) ) {
2001 $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
2002 }
2003
2004 FrmEntry::maybe_get_entry( $entry );
2005 }
2006 }
2007
2008 /**
2009 * @param string $content
2010 * @param false|object $entry
2011 * @param array $shortcodes
2012 *
2013 * @return string
2014 */
2015 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
2016 return FrmFieldsHelper::replace_content_shortcodes( $content, $entry, $shortcodes );
2017 }
2018
2019 /**
2020 * @param array $errors
2021 *
2022 * @return array
2023 */
2024 public static function process_bulk_form_actions( $errors ) {
2025 if ( ! $_REQUEST ) {
2026 return $errors;
2027 }
2028
2029 $bulkaction = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
2030
2031 // phpcs:ignore Universal.Operators.StrictComparisons
2032 if ( $bulkaction == - 1 ) {
2033 $bulkaction = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
2034 }
2035
2036 if ( $bulkaction && str_starts_with( $bulkaction, 'bulk_' ) ) {
2037 FrmAppHelper::remove_get_action();
2038 $bulkaction = str_replace( 'bulk_', '', $bulkaction );
2039 }
2040
2041 $ids = FrmAppHelper::get_param( 'item-action', '', 'get', 'sanitize_text_field' );
2042
2043 if ( ! $ids ) {
2044 $errors[] = __( 'No forms were specified', 'formidable' );
2045 return $errors;
2046 }
2047
2048 $permission_error = FrmAppHelper::permission_nonce_error( '', '_wpnonce', 'bulk-toplevel_page_formidable' );
2049
2050 if ( $permission_error !== false ) {
2051 $errors[] = $permission_error;
2052 return $errors;
2053 }
2054
2055 if ( ! is_array( $ids ) ) {
2056 $ids = explode( ',', $ids );
2057 }
2058
2059 switch ( $bulkaction ) {
2060 case 'delete':
2061 $message = self::bulk_destroy( $ids );
2062 break;
2063 case 'trash':
2064 $message = self::bulk_trash( $ids );
2065 break;
2066 case 'untrash':
2067 $message = self::bulk_untrash( $ids );
2068 }
2069
2070 if ( ! empty( $message ) ) {
2071 $errors['message'] = $message;
2072 }
2073
2074 return $errors;
2075 }
2076
2077 /**
2078 * Includes html that shows a message when the device is too small to use Formidable Forms admin pages.
2079 *
2080 * @since 6.20
2081 *
2082 * @return void
2083 */
2084 public static function include_device_too_small_message() {
2085 if ( ! FrmAppHelper::is_formidable_admin() ) {
2086 return;
2087 }
2088
2089 include FrmAppHelper::plugin_path() . '/classes/views/shared/small-device-message.php';
2090 }
2091
2092 public static function route() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
2093 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
2094 $vars = array();
2095 FrmAppHelper::include_svg();
2096
2097 if ( isset( $_POST['frm_compact_fields'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
2098 FrmAppHelper::permission_check( 'frm_edit_forms' );
2099
2100 // Javascript needs to be allowed in some field settings.
2101 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
2102 $json_vars = htmlspecialchars_decode( nl2br( str_replace( '&quot;', '"', wp_unslash( $_POST['frm_compact_fields'] ) ) ) );
2103 $json_vars = json_decode( $json_vars, true );
2104
2105 if ( ! $json_vars ) {
2106 // Json decoding failed so we should return an error message.
2107 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
2108
2109 if ( 'edit' === $action ) {
2110 $action = 'update';
2111 }
2112
2113 add_filter( 'frm_validate_form', 'FrmFormsController::json_error' );
2114 } else {
2115 $vars = FrmAppHelper::json_to_array( $json_vars );
2116 $action = $vars[ $action ];
2117 unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2118 $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2119 $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2120 }
2121 } else {
2122 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
2123
2124 if ( isset( $_REQUEST['delete_all'] ) ) {
2125 // Override the action for this page.
2126 $action = 'delete_all';
2127 }
2128 }//end if
2129
2130 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
2131 FrmAppHelper::trigger_hook_load( 'form' );
2132
2133 switch ( $action ) {
2134 case 'create':
2135 case 'edit':
2136 case 'update':
2137 case 'trash':
2138 case 'untrash':
2139 case 'destroy':
2140 case 'settings':
2141 case 'update_settings':
2142 return self::$action( $vars );
2143 case 'lite-reports':
2144 self::no_reports( $vars );
2145 return null;
2146 case 'views':
2147 self::no_views( $vars );
2148 return null;
2149 default:
2150 do_action( 'frm_form_action_' . $action );
2151
2152 if ( apply_filters( 'frm_form_stop_action_' . $action, false ) ) {
2153 return null;
2154 }
2155
2156 $action = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
2157
2158 // phpcs:ignore Universal.Operators.StrictComparisons
2159 if ( $action == - 1 ) {
2160 $action = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
2161 }
2162
2163 if ( str_starts_with( $action, 'bulk_' ) ) {
2164 FrmAppHelper::remove_get_action();
2165
2166 self::list_form();
2167 return null;
2168 }
2169
2170 $message = FrmAppHelper::get_param( 'message' );
2171
2172 if ( 'form_duplicate_error' === $message ) {
2173 self::display_forms_list( array(), '', array( __( 'There was a problem duplicating the form', 'formidable' ) ) );
2174 return null;
2175 }
2176
2177 if ( 'forms_permanently_deleted' === $message ) {
2178 $count = FrmAppHelper::get_param( 'forms_deleted', 0, 'get', 'absint' );
2179 /* translators: %1$s: Number of forms */
2180 $message = sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
2181 self::display_forms_list( array(), $message, '' );
2182 return null;
2183 }
2184
2185 self::display_forms_list();
2186
2187 return null;
2188 }//end switch
2189 }
2190
2191 /**
2192 * Rename a form.
2193 *
2194 * Handles the AJAX request for renaming a form.
2195 *
2196 * @since 6.7
2197 *
2198 * @return void
2199 */
2200 public static function rename_form() {
2201 // Check permission and nonce
2202 FrmAppHelper::permission_check( 'frm_edit_forms' );
2203 check_ajax_referer( 'frm_ajax', 'nonce' );
2204
2205 // Get posted data
2206 $form_id = FrmAppHelper::get_post_param( 'form_id', '', 'absint' );
2207 $name = FrmAppHelper::get_post_param( 'form_name', '', 'sanitize_text_field' );
2208 $form = FrmForm::getOne( $form_id );
2209
2210 if ( ! $form ) {
2211 wp_send_json_error( __( 'Form not found', 'formidable' ) );
2212 }
2213
2214 if ( $name === $form->name ) {
2215 // Nothing to change so exit early.
2216 wp_send_json_success();
2217 }
2218
2219 $to_update = array(
2220 'name' => $name,
2221 );
2222
2223 if ( '' !== $name ) {
2224 // Only update form_key if name is not empty.
2225 $form_key = FrmAppHelper::get_unique_key( sanitize_title( $name ), 'frm_forms', 'form_key' );
2226 $to_update['form_key'] = $form_key;
2227 } else {
2228 $form_key = $form->form_key;
2229 }
2230
2231 FrmForm::update( $form_id, $to_update );
2232
2233 wp_send_json_success( compact( 'form_key' ) );
2234 }
2235
2236 /**
2237 * @param array $errors
2238 *
2239 * @return array
2240 */
2241 public static function json_error( $errors ) {
2242 $errors['json'] = __( 'Abnormal HTML characters prevented your form from saving correctly', 'formidable' );
2243 return $errors;
2244 }
2245
2246 /**
2247 * Add education about views.
2248 *
2249 * @since 4.07
2250 *
2251 * @param array $values
2252 *
2253 * @return void
2254 */
2255 public static function no_views( $values = array() ) {
2256 FrmAppHelper::include_svg();
2257 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
2258 $form = $id ? FrmForm::getOne( $id ) : false;
2259
2260 include FrmAppHelper::plugin_path() . '/classes/views/shared/views-info.php';
2261 }
2262
2263 /**
2264 * Add education about reports.
2265 *
2266 * @since 4.07
2267 *
2268 * @param array $values
2269 *
2270 * @return void
2271 */
2272 public static function no_reports( $values = array() ) {
2273 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
2274 $form = $id ? FrmForm::getOne( $id ) : false;
2275
2276 include FrmAppHelper::plugin_path() . '/classes/views/shared/reports-info.php';
2277 }
2278
2279 /**
2280 * FRONT-END FORMS.
2281 */
2282 public static function admin_bar_css() {
2283 if ( is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
2284 return;
2285 }
2286
2287 self::move_menu_to_footer();
2288
2289 add_action( 'wp_before_admin_bar_render', 'FrmFormsController::admin_bar_configure' );
2290 FrmAppHelper::load_font_style();
2291 }
2292
2293 /**
2294 * @since 4.05.02
2295 */
2296 private static function move_menu_to_footer() {
2297 $settings = FrmAppHelper::get_settings();
2298
2299 if ( empty( $settings->admin_bar ) ) {
2300 remove_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
2301 }
2302 }
2303
2304 public static function admin_bar_configure() {
2305 global $frm_vars;
2306
2307 if ( empty( $frm_vars['forms_loaded'] ) ) {
2308 return;
2309 }
2310
2311 $actions = array();
2312
2313 foreach ( $frm_vars['forms_loaded'] as $form ) {
2314 if ( is_object( $form ) ) {
2315 $actions[ $form->id ] = $form->name;
2316 }
2317 unset( $form );
2318 }
2319
2320 if ( ! $actions ) {
2321 return;
2322 }
2323
2324 self::add_menu_to_admin_bar();
2325 self::add_forms_to_admin_bar( $actions );
2326 }
2327
2328 /**
2329 * @since 2.05.07
2330 */
2331 public static function add_menu_to_admin_bar() {
2332 global $wp_admin_bar;
2333
2334 $wp_admin_bar->add_node(
2335 array(
2336 'id' => 'frm-forms',
2337 'title' => '<span class="ab-icon"></span><span class="ab-label">' . FrmAppHelper::get_menu_name() . '</span>',
2338 'href' => admin_url( 'admin.php?page=formidable' ),
2339 'meta' => array(
2340 'title' => FrmAppHelper::get_menu_name(),
2341 ),
2342 )
2343 );
2344 }
2345
2346 /**
2347 * @since 2.05.07
2348 *
2349 * @param array $actions
2350 *
2351 * @return void
2352 */
2353 private static function add_forms_to_admin_bar( $actions ) {
2354 global $wp_admin_bar;
2355
2356 asort( $actions );
2357
2358 foreach ( $actions as $form_id => $name ) {
2359 $wp_admin_bar->add_node(
2360 array(
2361 'parent' => 'frm-forms',
2362 'id' => 'edit_form_' . $form_id,
2363 'title' => $name ? $name : FrmFormsHelper::get_no_title_text(),
2364 'href' => FrmForm::get_edit_link( $form_id ),
2365 )
2366 );
2367 }
2368 }
2369
2370 /**
2371 * The formidable shortcode
2372 *
2373 * @param array $atts The params from the shortcode.
2374 *
2375 * @return string
2376 */
2377 public static function get_form_shortcode( $atts ) {
2378 global $frm_vars;
2379
2380 if ( ! empty( $frm_vars['skip_shortcode'] ) ) {
2381 $sc = '[formidable';
2382 $sc .= FrmAppHelper::array_to_html_params( $atts );
2383 return $sc . ']';
2384 }
2385
2386 $shortcode_atts = shortcode_atts(
2387 array(
2388 'id' => '',
2389 'key' => '',
2390 'title' => 'auto',
2391 'description' => 'auto',
2392 'readonly' => false,
2393 'entry_id' => false,
2394 'fields' => array(),
2395 'exclude_fields' => array(),
2396 'minimize' => false,
2397 ),
2398 $atts
2399 );
2400 do_action( 'formidable_shortcode_atts', $shortcode_atts, $atts );
2401
2402 return self::show_form( $shortcode_atts['id'], $shortcode_atts['key'], $shortcode_atts['title'], $shortcode_atts['description'], $atts );
2403 }
2404
2405 /**
2406 * @since 5.2.01
2407 *
2408 * @param false|int|string $id
2409 * @param false|string $key
2410 *
2411 * @return false|stdClass
2412 */
2413 private static function maybe_get_form_by_id_or_key( $id, $key ) {
2414 if ( ! $id ) {
2415 $id = $key;
2416 }
2417 return self::maybe_get_form_to_show( $id );
2418 }
2419
2420 /**
2421 * @param false|int|string $id
2422 * @param false|string $key
2423 * @param bool|int|string $title may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
2424 * @param bool|int|string $description may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
2425 * @param array $atts
2426 *
2427 * @return string
2428 */
2429 public static function show_form( $id = '', $key = '', $title = false, $description = false, $atts = array() ) {
2430 $form = self::maybe_get_form_by_id_or_key( $id, $key );
2431
2432 if ( ! $form ) {
2433 return __( 'Please select a valid form', 'formidable' );
2434 }
2435
2436 if ( 'auto' === $title ) {
2437 $title = ! empty( $form->options['show_title'] );
2438 }
2439
2440 if ( 'auto' === $description ) {
2441 $description = ! empty( $form->options['show_description'] );
2442 }
2443
2444 FrmAppController::maybe_update_styles();
2445
2446 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
2447 FrmAppHelper::trigger_hook_load( 'form', $form );
2448
2449 $form = apply_filters( 'frm_pre_display_form', $form );
2450 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
2451
2452 if ( self::is_viewable_draft_form( $form ) ) {
2453 // Don't show a draft form on a page
2454 $form = __( 'Please select a valid form', 'formidable' );
2455 } elseif ( ! FrmForm::is_visible_to_user( $form ) ) {
2456 $form = do_shortcode( $frm_settings->login_msg );
2457 } else {
2458 do_action( 'frm_pre_get_form', $form );
2459 $form = self::get_form( $form, $title, $description, $atts );
2460
2461 /**
2462 * Use this shortcode to check for external shortcodes that may span
2463 * across multiple fields in the customizable HTML
2464 *
2465 * @since 2.0.8
2466 */
2467 $form = apply_filters( 'frm_filter_final_form', $form );
2468 }
2469
2470 return $form;
2471 }
2472
2473 /**
2474 * @param false|int|string $id
2475 *
2476 * @return false|stdClass
2477 */
2478 private static function maybe_get_form_to_show( $id ) {
2479 $form = false;
2480
2481 if ( $id ) {
2482 // Form id or key is set.
2483 $form = FrmForm::getOne( $id );
2484
2485 if ( ! $form || $form->parent_form_id || $form->status === 'trash' ) {
2486 $form = false;
2487 }
2488 }
2489
2490 return $form;
2491 }
2492
2493 /**
2494 * @param object $form
2495 *
2496 * @return bool
2497 */
2498 private static function is_viewable_draft_form( $form ) {
2499 return $form->status === 'draft' && current_user_can( 'frm_edit_forms' ) && ! FrmAppHelper::is_preview_page();
2500 }
2501
2502 /**
2503 * @param object $form
2504 * @param bool $title
2505 * @param bool $description
2506 * @param array $atts
2507 *
2508 * @return string
2509 */
2510 public static function get_form( $form, $title, $description, $atts = array() ) {
2511 ob_start();
2512
2513 do_action( 'frm_before_get_form', $atts );
2514
2515 self::get_form_contents( $form, $title, $description, $atts );
2516 self::enqueue_scripts( FrmForm::get_params( $form ) );
2517
2518 $contents = ob_get_clean();
2519
2520 self::maybe_minimize_form( $atts, $contents );
2521
2522 return $contents;
2523 }
2524
2525 /**
2526 * @param array $params
2527 *
2528 * @return void
2529 */
2530 public static function enqueue_scripts( $params ) {
2531 do_action( 'frm_enqueue_form_scripts', $params );
2532 }
2533
2534 /**
2535 * @param object $form
2536 * @param bool $title
2537 * @param bool $description
2538 * @param array $atts
2539 *
2540 * @return void
2541 */
2542 public static function get_form_contents( $form, $title, $description, $atts ) {
2543 $params = FrmForm::get_params( $form );
2544 $errors = self::get_saved_errors( $form, $params );
2545 $fields = FrmFieldsHelper::get_form_fields( $form->id, $errors );
2546 $reset = false;
2547 $pass_args = compact( 'form', 'fields', 'errors', 'title', 'description', 'reset' );
2548
2549 $pass_args['action'] = $params['action'];
2550 // phpcs:ignore WordPress.Security.NonceVerification.Missing, Universal.Operators.StrictComparisons
2551 $handle_process_here = $params['action'] === 'create' && $params['posted_form_id'] == $form->id && $_POST;
2552
2553 if ( ! $handle_process_here ) {
2554 FrmFormState::set_initial_value( 'title', $title );
2555 FrmFormState::set_initial_value( 'description', $description );
2556
2557 do_action( 'frm_display_form_action', $params, $fields, $form, $title, $description );
2558
2559 if ( apply_filters( 'frm_continue_to_new', true, $form->id, $params['action'] ) ) {
2560 self::show_form_after_submit( $pass_args );
2561 }
2562 } elseif ( $errors ) {
2563 self::show_form_after_submit( $pass_args );
2564 } else {
2565 do_action( 'frm_validate_form_creation', $params, $fields, $form, $title, $description );
2566
2567 if ( apply_filters( 'frm_continue_to_create', true, $form->id ) ) {
2568 $entry_id = self::just_created_entry( $form->id );
2569 $pass_args['entry_id'] = $entry_id;
2570 $pass_args['reset'] = true;
2571
2572 self::run_on_submit_actions( $pass_args );
2573
2574 do_action(
2575 'frm_after_entry_processed',
2576 array(
2577 'entry_id' => $entry_id,
2578 'form' => $form,
2579 )
2580 );
2581 }
2582 }//end if
2583 }
2584
2585 /**
2586 * If the form was processed earlier (init), get the generated errors
2587 *
2588 * @since 2.05
2589 *
2590 * @param object $form
2591 * @param array $params
2592 *
2593 * @return array
2594 */
2595 private static function get_saved_errors( $form, $params ) {
2596 global $frm_vars;
2597
2598 // phpcs:ignore WordPress.Security.NonceVerification.Missing, Universal.Operators.StrictComparisons
2599 if ( $params['posted_form_id'] == $form->id && $_POST && isset( $frm_vars['created_entries'][ $form->id ] ) ) {
2600 $errors = $frm_vars['created_entries'][ $form->id ]['errors'];
2601 } else {
2602 $errors = array();
2603 }
2604
2605 /**
2606 * Allows modifying the generated errors if the form was processed earlier.
2607 *
2608 * @since 5.2.03
2609 *
2610 * @param array $errors Errors data. Is empty array if no errors found.
2611 * @param array $params Form params. See {@see FrmForm::get_params()}.
2612 */
2613 return apply_filters( 'frm_saved_errors', $errors, $params );
2614 }
2615
2616 /**
2617 * @since 2.2.7
2618 *
2619 * @param int|string $form_id
2620 *
2621 * @return int
2622 */
2623 public static function just_created_entry( $form_id ) {
2624 global $frm_vars;
2625 return isset( $frm_vars['created_entries'] ) && isset( $frm_vars['created_entries'][ $form_id ] ) && isset( $frm_vars['created_entries'][ $form_id ]['entry_id'] ) ? $frm_vars['created_entries'][ $form_id ]['entry_id'] : 0; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
2626 }
2627
2628 /**
2629 * Gets confirmation method.
2630 *
2631 * @since 3.0
2632 * @since 6.0 This method can return an array of met On Submit actions.
2633 *
2634 * @param array $atts {
2635 * Atts.
2636 *
2637 * @type object $form Form object.
2638 * @type int $entry_id Entry ID.
2639 * }
2640 *
2641 * @return array|string
2642 */
2643 private static function get_confirmation_method( $atts ) {
2644 $action = FrmOnSubmitHelper::current_event( $atts );
2645 $opt = 'update' === $action ? 'edit_action' : 'success_action';
2646 $method = ! empty( $atts['form']->options[ $opt ] ) ? $atts['form']->options[ $opt ] : 'message';
2647
2648 if ( ! empty( $atts['entry_id'] ) ) {
2649 $met_actions = self::get_met_on_submit_actions( $atts, $action );
2650
2651 if ( $met_actions ) {
2652 $method = $met_actions;
2653 }
2654 }
2655
2656 $method = apply_filters( 'frm_success_filter', $method, $atts['form'], $action );
2657
2658 if ( $method !== 'message' && ( ! $atts['entry_id'] || ! is_numeric( $atts['entry_id'] ) ) ) {
2659 return 'message';
2660 }
2661
2662 return $method;
2663 }
2664
2665 /**
2666 * @param object $form
2667 * @param array $params
2668 * @param array $args
2669 *
2670 * @return void
2671 */
2672 public static function maybe_trigger_redirect( $form, $params, $args ) {
2673 if ( ! isset( $params['id'] ) ) {
2674 global $frm_vars;
2675 $params['id'] = $frm_vars['created_entries'][ $form->id ]['entry_id'];
2676 }
2677
2678 $conf_method = self::get_confirmation_method(
2679 array(
2680 'form' => $form,
2681 'entry_id' => $params['id'],
2682 'action' => FrmOnSubmitHelper::current_event( $params ),
2683 )
2684 );
2685
2686 self::maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args );
2687 }
2688
2689 /**
2690 * Maybe trigger redirect with the new Confirmation action.
2691 *
2692 * @since 6.1.1
2693 *
2694 * @param array|string $conf_method Array of confirmation actions or the action type string.
2695 * @param object $form Form object.
2696 * @param array $params See {@see FrmFormsController::maybe_trigger_redirect()}.
2697 * @param array $args See {@see FrmFormsController::maybe_trigger_redirect()}.
2698 */
2699 public static function maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args ) {
2700 if ( is_array( $conf_method ) && 1 === count( $conf_method ) ) {
2701 if ( 'redirect' === FrmOnSubmitHelper::get_action_type( $conf_method[0] ) && empty( $conf_method[0]->post_content['redirect_delay'] ) ) {
2702 $event = FrmOnSubmitHelper::current_event( $params );
2703 FrmOnSubmitHelper::populate_on_submit_data( $form->options, $conf_method[0], $event );
2704 $conf_method = 'redirect';
2705 }
2706 }
2707
2708 if ( 'redirect' === $conf_method ) {
2709 self::trigger_redirect( $form, $params, $args );
2710 }
2711 }
2712
2713 /**
2714 * @param object $form
2715 * @param array $params
2716 * @param array $args
2717 *
2718 * @return void
2719 */
2720 public static function trigger_redirect( $form, $params, $args ) {
2721 $success_args = array(
2722 'action' => $params['action'],
2723 'conf_method' => 'redirect',
2724 'form' => $form,
2725 'entry_id' => $params['id'],
2726 );
2727
2728 if ( isset( $args['ajax'] ) ) {
2729 $success_args['ajax'] = $args['ajax'];
2730 }
2731
2732 self::run_success_action( $success_args );
2733 }
2734
2735 /**
2736 * Used when the success action is not 'message'
2737 *
2738 * @since 2.05
2739 * @since 6.0 `$args['force_delay_redirect']` is added.
2740 *
2741 * @param array $args {
2742 * The args.
2743 *
2744 * @type string $conf_method The method.
2745 * @type object $form Form object.
2746 * @type int $entry_id Entry ID.
2747 * @type string $action The action event. Accepts `create` or `update`.
2748 * @type array $fields The array of fields.
2749 * @type int $force_delay_redirect Force to show the message before redirecting in case redirect method runs.
2750 * }
2751 */
2752 public static function run_success_action( $args ) {
2753 global $frm_vars;
2754 $extra_args = $args;
2755 unset( $extra_args['form'] );
2756
2757 do_action( 'frm_success_action', $args['conf_method'], $args['form'], $args['form']->options, $args['entry_id'], $extra_args );
2758
2759 $opt = ! isset( $args['action'] ) || $args['action'] === 'create' ? 'success' : 'edit';
2760
2761 $args['success_opt'] = $opt;
2762 $args['ajax'] = ! empty( $frm_vars['ajax'] );
2763
2764 if ( $args['conf_method'] === 'page' && is_numeric( $args['form']->options[ $opt . '_page_id' ] ) ) {
2765 self::load_page_after_submit( $args );
2766 } elseif ( $args['conf_method'] === 'redirect' ) {
2767 self::redirect_after_submit( $args );
2768 } else {
2769 self::show_message_after_save( $args );
2770 }
2771 }
2772
2773 /**
2774 * Gets met On Submit actions.
2775 *
2776 * @since 6.0
2777 *
2778 * @param array $args See {@see FrmFormsController::run_success_action()}.
2779 * @param string $event Form event. Default is `create`.
2780 *
2781 * @return array Array of actions that meet the conditional logics.
2782 */
2783 public static function get_met_on_submit_actions( $args, $event = 'create' ) {
2784 if ( ! FrmOnSubmitHelper::form_has_migrated( $args['form'] ) ) {
2785 return array();
2786 }
2787
2788 // If a redirect action has already opened the URL in a new tab, we show the default message in the current tab.
2789 if ( ! empty( self::$redirected_in_new_tab[ $args['form']->id ] ) ) {
2790 return array( FrmOnSubmitHelper::get_fallback_action_after_open_in_new_tab( $event ) );
2791 }
2792
2793 $entry = FrmEntry::getOne( $args['entry_id'], true );
2794 $actions = FrmOnSubmitHelper::get_actions( $args['form']->id );
2795 $met_actions = array();
2796 $has_redirect = false;
2797
2798 foreach ( $actions as $action ) {
2799 if ( ! in_array( $event, $action->post_content['event'], true ) ) {
2800 continue;
2801 }
2802
2803 if ( FrmFormAction::action_conditions_met( $action, $entry ) ) {
2804 continue;
2805 }
2806
2807 $action_type = FrmOnSubmitHelper::get_action_type( $action );
2808
2809 if ( 'redirect' === $action_type ) {
2810 if ( $has_redirect ) {
2811 // Do not process because we run the first redirect action only.
2812 continue;
2813 }
2814 }
2815
2816 if ( ! self::is_valid_on_submit_action( $action, $args, $event ) ) {
2817 continue;
2818 }
2819
2820 if ( 'redirect' === $action_type ) {
2821 $has_redirect = true;
2822 }
2823
2824 $met_actions[] = $action;
2825 unset( $action );
2826 }//end foreach
2827
2828 $args['event'] = $event;
2829
2830 /**
2831 * Filters the On Submit actions that meet the conditional logics.
2832 *
2833 * @since 6.0
2834 *
2835 * @param array $met_actions Actions that meet the conditional logics.
2836 * @param array $args See {@see FrmFormsController::run_success_action()}. `$args['event']` is also added.
2837 */
2838 $met_actions = apply_filters( 'frm_get_met_on_submit_actions', $met_actions, $args );
2839
2840 if ( ! $met_actions ) {
2841 return array( FrmOnSubmitHelper::get_fallback_action( $event ) );
2842 }
2843
2844 return $met_actions;
2845 }
2846
2847 /**
2848 * Checks if a Confirmation action has the valid data.
2849 *
2850 * @since 6.1.2
2851 *
2852 * @param object $action Form action object.
2853 * @param array $args See {@see FrmFormsController::run_success_action()}.
2854 * @param string $event Form event. Default is `create`.
2855 *
2856 * @return bool
2857 */
2858 private static function is_valid_on_submit_action( $action, $args, $event = 'create' ) {
2859 $action_type = FrmOnSubmitHelper::get_action_type( $action );
2860
2861 if ( 'redirect' === $action_type ) {
2862 // Run through frm_redirect_url filter. This is used for the valid action check.
2863 $action->post_content['success_url'] = apply_filters(
2864 'frm_redirect_url',
2865 $action->post_content['success_url'],
2866 $args['form'],
2867 $args + array( 'action' => $event )
2868 );
2869
2870 return ! empty( $action->post_content['success_url'] );
2871 }
2872
2873 if ( 'page' === $action_type ) {
2874 if ( empty( $action->post_content['success_page_id'] ) ) {
2875 return false;
2876 }
2877
2878 $page = get_post( $action->post_content['success_page_id'] );
2879
2880 if ( ! $page || 'trash' === $page->post_status ) {
2881 return false;
2882 }
2883 }
2884
2885 return true;
2886 }
2887
2888 /**
2889 * Runs On Submit actions.
2890 *
2891 * @since 6.0
2892 *
2893 * @param array $args See inside {@see FrmFormsController::get_form_contents()} method.
2894 */
2895 public static function run_on_submit_actions( $args ) {
2896 $args['conf_method'] = self::get_confirmation_method(
2897 array(
2898 'form' => $args['form'],
2899 'entry_id' => $args['entry_id'],
2900 'action' => FrmOnSubmitHelper::current_event( $args ),
2901 )
2902 );
2903
2904 if ( ! is_array( $args['conf_method'] ) ) {
2905 self::run_success_action( $args );
2906 return;
2907 }
2908
2909 // If conf_method is an array, run On Submit actions.
2910 if ( ! $args['conf_method'] ) {
2911 // Use default message.
2912 FrmOnSubmitHelper::populate_on_submit_data( $args['form']->options );
2913 self::run_success_action( $args );
2914 } elseif ( 1 === count( $args['conf_method'] ) ) {
2915 FrmOnSubmitHelper::populate_on_submit_data( $args['form']->options, reset( $args['conf_method'] ) );
2916 $args['conf_method'] = $args['form']->options['success_action'];
2917 self::run_success_action( $args );
2918 } else {
2919 self::run_multi_on_submit_actions( $args );
2920 }
2921 }
2922
2923 /**
2924 * Runs multiple success actions.
2925 *
2926 * @since 6.0
2927 *
2928 * @param array $args See {@see FrmFormsController::run_success_action()}.
2929 */
2930 public static function run_multi_on_submit_actions( $args ) {
2931 $redirect_action = null;
2932
2933 foreach ( $args['conf_method'] as $action ) {
2934 if ( 'redirect' === FrmOnSubmitHelper::get_action_type( $action ) ) {
2935 // We catch the redirect action to run it last.
2936 $redirect_action = $action;
2937 continue;
2938 }
2939
2940 self::run_single_on_submit_action( $args, $action );
2941
2942 unset( $action );
2943 }
2944
2945 if ( ! $redirect_action ) {
2946 return;
2947 }
2948
2949 // Show script to delay the redirection.
2950 $args['force_delay_redirect'] = true;
2951 self::run_single_on_submit_action( $args, $redirect_action );
2952 }
2953
2954 /**
2955 * Runs single On Submit action.
2956 *
2957 * @since 6.0
2958 *
2959 * @param array $args See {@see FrmFormsController::run_success_action()}.
2960 * @param object $action On Submit action object.
2961 */
2962 public static function run_single_on_submit_action( $args, $action ) {
2963 $new_args = self::get_run_success_action_args( $args, $action );
2964 self::run_success_action( $new_args );
2965 }
2966
2967 /**
2968 * Gets run_success_action() args from the On Submit action.
2969 *
2970 * @since 6.0
2971 *
2972 * @param array $args See {@see FrmFormsController::run_success_action()}.
2973 * @param object $action On Submit action object.
2974 *
2975 * @return array
2976 */
2977 private static function get_run_success_action_args( $args, $action ) {
2978 $new_args = $args;
2979
2980 FrmOnSubmitHelper::populate_on_submit_data( $new_args['form']->options, $action, $args['action'] );
2981
2982 $opt = 'update' === $args['action'] ? 'edit_' : 'success_';
2983 $new_args['conf_method'] = $new_args['form']->options[ $opt . 'action' ];
2984
2985 /**
2986 * Filters the run success action args.
2987 *
2988 * @since 6.0
2989 *
2990 * @param array $new_args The new args.
2991 * @param array $args The old args. See {@see FrmFormsController::run_success_action()}.
2992 * @param object $action On Submit action object.
2993 */
2994 return apply_filters( 'frm_get_run_success_action_args', $new_args, $args, $action );
2995 }
2996
2997 /**
2998 * @since 3.0
2999 *
3000 * @param array $args
3001 *
3002 * @return void
3003 */
3004 private static function load_page_after_submit( $args ) {
3005 global $post;
3006 $opt = $args['success_opt'];
3007
3008 // phpcs:ignore Universal.Operators.StrictComparisons
3009 if ( ! $post || $args['form']->options[ $opt . '_page_id' ] != $post->ID ) {
3010 $page = get_post( $args['form']->options[ $opt . '_page_id' ] );
3011 $old_post = $post;
3012 $post = $page;
3013 $content = apply_filters( 'frm_content', $page->post_content, $args['form'], $args['entry_id'] );
3014
3015 // Fix the On Submit page content doesn't show when previewing In theme.
3016 $has_preview_filter = has_filter( 'the_content', 'FrmFormsController::preview_content' );
3017
3018 if ( $has_preview_filter ) {
3019 remove_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
3020 }
3021
3022 echo apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3023
3024 if ( $has_preview_filter ) {
3025 add_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
3026 }
3027
3028 $post = $old_post;
3029 }//end if
3030 }
3031
3032 /**
3033 * @since 3.0
3034 *
3035 * @param array $args See {@see FrmFormsController::run_success_action()}.
3036 */
3037 private static function redirect_after_submit( $args ) {
3038 add_filter( 'frm_use_wpautop', '__return_false' );
3039
3040 $opt = $args['success_opt'];
3041 $success_url = trim( $args['form']->options[ $opt . '_url' ] );
3042 $success_url = apply_filters( 'frm_content', $success_url, $args['form'], $args['entry_id'] );
3043 $success_url = do_shortcode( $success_url );
3044
3045 $args['id'] = $args['entry_id'];
3046 FrmEntriesController::delete_entry_before_redirect( $success_url, $args['form'], $args );
3047
3048 add_filter( 'frm_redirect_url', 'FrmEntriesController::prepare_redirect_url' );
3049 $success_url = apply_filters( 'frm_redirect_url', $success_url, $args['form'], $args );
3050 $doing_ajax = FrmAppHelper::doing_ajax();
3051
3052 if ( ! empty( $args['ajax'] ) && $doing_ajax && empty( $args['force_delay_redirect'] ) ) {
3053 // Is AJAX submit and there is just one Redirect action runs.
3054 echo json_encode( self::get_ajax_redirect_response_data( $args + compact( 'success_url' ) ) );
3055 wp_die();
3056 }
3057
3058 if ( ! headers_sent() && empty( $args['force_delay_redirect'] ) && empty( $args['form']->options['redirect_delay'] ) ) {
3059 // Not AJAX submit, no headers sent, and there is just one Redirect action runs.
3060 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
3061 self::print_open_in_new_tab_js_with_fallback_handler( $success_url, $args );
3062 self::$redirected_in_new_tab[ $args['form']->id ] = 1;
3063 return;
3064 }
3065
3066 wp_redirect( esc_url_raw( $success_url ) );
3067 // Do not use wp_die or redirect fails.
3068 die();
3069 }
3070
3071 // Redirect with a delay.
3072 self::redirect_after_submit_using_js( $args + compact( 'success_url', 'doing_ajax' ) );
3073 }
3074
3075 /**
3076 * Prints open in new tab js with fallback handler.
3077 *
3078 * @since 6.3.1
3079 *
3080 * @param string $success_url Success URL.
3081 * @param array $args See {@see FrmFormsController::redirect_after_submit()}.
3082 */
3083 private static function print_open_in_new_tab_js_with_fallback_handler( $success_url, $args ) {
3084 echo '<script>var newTab = window.open("' . esc_url_raw( $success_url ) . '", "_blank");';
3085 echo 'if ( ! newTab ) {';
3086
3087 $data = array(
3088 'formId' => intval( $args['form']->id ),
3089 'message' => self::get_redirect_fallback_message( $success_url, $args ),
3090 );
3091 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3092 echo 'frmShowNewTabFallback = ' . FrmAppHelper::maybe_json_encode( $data ) . ';';
3093 echo '}</script>';
3094 }
3095
3096 /**
3097 * Gets response data for redirect action when AJAX submitting.
3098 *
3099 * @since 6.3.1
3100 *
3101 * @param array $args See {@see FrmFormsController::run_success_action()}.
3102 *
3103 * @return array
3104 */
3105 private static function get_ajax_redirect_response_data( $args ) {
3106 $response_data = array(
3107 'redirect' => $args['success_url'],
3108 'content' => '',
3109 );
3110 $unset_content = true;
3111
3112 if ( ! empty( $args['form']->options['redirect_delay'] ) ) {
3113 $response_data['delay'] = $args['form']->options['redirect_delay_time'];
3114 $response_data['content'] .= self::get_redirect_message( $args['success_url'], $args['form']->options['redirect_delay_msg'], $args );
3115 $unset_content = false;
3116 }
3117
3118 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
3119 $response_data['openInNewTab'] = 1;
3120
3121 // Only show open in new tab text if there is no delay.
3122 if ( empty( $response_data['content'] ) ) {
3123 $args['message'] = FrmOnSubmitHelper::get_default_new_tab_msg();
3124
3125 $args['form']->options['success_msg'] = $args['message'];
3126 $args['form']->options['edit_msg'] = $args['message'];
3127
3128 if ( ! isset( $args['fields'] ) ) {
3129 $args['fields'] = FrmField::get_all_for_form( $args['form']->id );
3130 }
3131
3132 $args['message'] = self::prepare_submit_message( $args['form'], $args['entry_id'], $args );
3133
3134 ob_start();
3135 self::show_lone_success_message( $args );
3136 $response_data['content'] .= ob_get_clean();
3137 $unset_content = false;
3138 }//end if
3139
3140 $response_data['fallbackMsg'] = self::get_redirect_fallback_message( $args['success_url'], $args );
3141 }//end if
3142
3143 if ( $unset_content && '' === $response_data['content'] ) {
3144 unset( $response_data['content'] );
3145 }
3146
3147 return $response_data;
3148 }
3149
3150 /**
3151 * Redirects after submitting using JS. This is used when showing message before redirecting.
3152 *
3153 * @since 6.0
3154 *
3155 * @param array $args See {@see FrmFormsController::run_success_action()}.
3156 */
3157 private static function redirect_after_submit_using_js( $args ) {
3158 $success_msg = $args['form']->options['redirect_delay_msg'];
3159 $redirect_msg = self::get_redirect_message( $args['success_url'], $success_msg, $args );
3160 $success_url = esc_url_raw( $args['success_url'] );
3161
3162 /**
3163 * Filters the delay time before redirecting when On Submit Redirect action is delayed.
3164 *
3165 * @since 6.0
3166 *
3167 * @param int $delay_time Delay time in milliseconds.
3168 */
3169 $delay_time = apply_filters( 'frm_redirect_delay_time', 1000 * $args['form']->options['redirect_delay_time'] );
3170
3171 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
3172 $redirect_js = 'window.open("' . $success_url . '", "_blank")';
3173 } else {
3174 $redirect_js = 'window.location="' . $success_url . '";';
3175 }
3176
3177 add_filter( 'frm_use_wpautop', '__return_true' );
3178
3179 echo FrmAppHelper::maybe_kses( $redirect_msg ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3180 echo '<script>';
3181
3182 if ( empty( $args['doing_ajax'] ) ) {
3183 // Not AJAX submit, delay JS until window.load.
3184 echo 'window.onload=function(){';
3185 }
3186 echo 'setTimeout(function(){' . $redirect_js . '}, ' . intval( $delay_time ) . ');'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3187
3188 if ( empty( $args['doing_ajax'] ) ) {
3189 echo '};';
3190 }
3191 echo '</script>';
3192 }
3193
3194 /**
3195 * @since 3.0
3196 *
3197 * @param string $success_url
3198 * @param string $success_msg
3199 * @param array $args
3200 */
3201 private static function get_redirect_message( $success_url, $success_msg, $args ) {
3202 $success_msg = apply_filters( 'frm_content', $success_msg, $args['form'], $args['entry_id'] );
3203 $success_msg = do_shortcode( FrmAppHelper::use_wpautop( $success_msg ) );
3204
3205 // phpcs:disable Generic.WhiteSpace.ScopeIndent
3206 $redirect_msg = '<div class="' . esc_attr( FrmFormsHelper::get_form_style_class( $args['form'] ) ) . '"><div class="frm-redirect-msg" role="status">' . $success_msg . '<br/>' . // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3207 self::get_redirect_fallback_message( $success_url, $args ) .
3208 '</div></div>';
3209 // phpcs:enable Generic.WhiteSpace.ScopeIndent
3210
3211 $redirect_args = array(
3212 'entry_id' => $args['entry_id'],
3213 'form_id' => $args['form']->id,
3214 'form' => $args['form'],
3215 );
3216
3217 return apply_filters( 'frm_redirect_msg', $redirect_msg, $redirect_args );
3218 }
3219
3220 /**
3221 * Gets fallback message when redirecting failed.
3222 *
3223 * @since 6.3.1
3224 *
3225 * @param string $success_url Redirect URL.
3226 * @param array $args Contains `form` object.
3227 *
3228 * @return string
3229 */
3230 private static function get_redirect_fallback_message( $success_url, $args ) {
3231 $target = '';
3232
3233 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
3234 $target = ' target="_blank"';
3235 }
3236
3237 return sprintf(
3238 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
3239 __( '%1$sClick here%2$s if you are not automatically redirected.', 'formidable' ),
3240 '<a href="' . esc_url( $success_url ) . '"' . $target . '>',
3241 '</a>'
3242 );
3243 }
3244
3245 /**
3246 * Prepare to show the success message and empty form after submit
3247 *
3248 * @since 2.05
3249 *
3250 * @param array $atts
3251 *
3252 * @return void
3253 */
3254 public static function show_message_after_save( $atts ) {
3255 $atts['message'] = self::prepare_submit_message( $atts['form'], $atts['entry_id'], $atts );
3256
3257 if ( ! isset( $atts['form']->options['show_form'] ) || $atts['form']->options['show_form'] ) {
3258 if ( isset( $atts['action'] ) && 'update' === $atts['action'] && is_callable( array( 'FrmProEntriesController', 'show_front_end_form_with_entry' ) ) ) {
3259 $entry = FrmEntry::getOne( $atts['entry_id'] );
3260
3261 if ( $entry ) {
3262 // This is copied from the Pro plugin.
3263 $atts['conf_message'] = FrmProEntriesController::confirmation( 'message', $atts['form'], $atts['form']->options, $entry->id, $atts );
3264 $atts['show_form'] = FrmProEntriesController::is_form_displayed_after_edit( $atts['form'] );
3265 FrmProEntriesController::show_front_end_form_with_entry( $entry, $atts );
3266 }
3267 } else {
3268 self::show_form_after_submit( $atts );
3269 }
3270 } else {
3271 self::show_lone_success_message( $atts );
3272 }
3273 }
3274
3275 /**
3276 * Show an empty form
3277 *
3278 * @since 2.05
3279 *
3280 * @param array $args
3281 *
3282 * @return void
3283 */
3284 private static function show_form_after_submit( $args ) {
3285 self::fill_atts_for_form_display( $args );
3286
3287 $errors = $args['errors'];
3288 $message = $args['message'];
3289 $form = $args['form'];
3290 $title = $args['title'];
3291 $description = $args['description'];
3292
3293 if ( empty( $args['fields'] ) ) {
3294 $values = array(
3295 'custom_style' => FrmAppHelper::custom_style_value( array() ),
3296 );
3297 } else {
3298 $values = FrmEntriesHelper::setup_new_vars( $args['fields'], $form, $args['reset'] );
3299 }
3300 unset( $args );
3301
3302 $include_form_tag = apply_filters( 'frm_include_form_tag', true, $form );
3303 $frm_settings = FrmAppHelper::get_settings();
3304 $submit = $form->options['submit_value'] ?? $frm_settings->submit_value;
3305
3306 global $frm_vars;
3307 self::maybe_load_css( $form, $values['custom_style'], $frm_vars['load_css'] );
3308
3309 $message_placement = self::message_placement( $form, $message );
3310
3311 include FrmAppHelper::plugin_path() . '/classes/views/frm-entries/new.php';
3312 }
3313
3314 /**
3315 * Gets message placement.
3316 *
3317 * @since 4.05.02
3318 * @since 6.26 This method changed from `private` to `public`.
3319 *
3320 * @param object $form Form object.
3321 * @param string $message The message.
3322 *
3323 * @return string Accepts 'before', 'after', or 'submit'.
3324 */
3325 public static function message_placement( $form, $message ) {
3326 $place = 'before';
3327
3328 if ( $message && isset( $form->options['form_class'] ) ) {
3329 if ( str_contains( $form->options['form_class'], 'frm_below_success' ) ) {
3330 $place = 'after';
3331 } elseif ( str_contains( $form->options['form_class'], 'frm_inline_success' ) ) {
3332 $place = 'submit';
3333 }
3334 }
3335
3336 /**
3337 * Filter the message placement.
3338 *
3339 * @since 4.05.02
3340 *
3341 * @param string $place Accepts 'before', 'after', or 'submit'.
3342 * @param array $args Args.
3343 */
3344 return apply_filters( 'frm_message_placement', $place, compact( 'form', 'message' ) );
3345 }
3346
3347 /**
3348 * Get all the values needed on the new.php entry page
3349 *
3350 * @since 2.05
3351 *
3352 * @param array $args
3353 *
3354 * @return void
3355 */
3356 private static function fill_atts_for_form_display( &$args ) {
3357 if ( ! isset( $args['title'] ) && isset( $args['show_title'] ) ) {
3358 $args['title'] = $args['show_title'];
3359 }
3360
3361 if ( ! isset( $args['description'] ) && isset( $args['show_description'] ) ) {
3362 $args['description'] = $args['show_description'];
3363 }
3364
3365 $defaults = array(
3366 'errors' => array(),
3367 'message' => '',
3368 'fields' => array(),
3369 'form' => array(),
3370 'title' => true,
3371 'description' => false,
3372 'reset' => false,
3373 );
3374 $args = wp_parse_args( $args, $defaults );
3375 }
3376
3377 /**
3378 * Show the success message without the form
3379 *
3380 * @since 2.05
3381 *
3382 * @param array $atts
3383 *
3384 * @return void
3385 */
3386 private static function show_lone_success_message( $atts ) {
3387 global $frm_vars;
3388 $values = FrmEntriesHelper::setup_new_vars( $atts['fields'], $atts['form'], true );
3389 self::maybe_load_css( $atts['form'], $values['custom_style'], $frm_vars['load_css'] );
3390
3391 $include_extra_container = 'frm_forms' . FrmFormsHelper::get_form_style_class( $values );
3392 $errors = array();
3393 $form = $atts['form'];
3394 $message = $atts['message'];
3395
3396 include FrmAppHelper::plugin_path() . '/classes/views/frm-entries/errors.php';
3397 }
3398
3399 /**
3400 * Prepare the success message before it's shown
3401 *
3402 * @since 2.05
3403 * @since 6.0.x Added the third parameter.
3404 *
3405 * @param object $form Form object.
3406 * @param int|string $entry_id Entry ID.
3407 * @param array $args See {@see FrmFormsController::run_success_action()}.
3408 *
3409 * @return string
3410 */
3411 private static function prepare_submit_message( $form, $entry_id, $args = array() ) {
3412 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
3413 $opt = $args['success_opt'] ?? 'success';
3414
3415 if ( $entry_id && is_numeric( $entry_id ) ) {
3416 $message = $form->options[ $opt . '_msg' ] ?? $frm_settings->success_msg;
3417 $class = 'frm_message';
3418 } else {
3419 $message = $frm_settings->failed_msg;
3420 $class = FrmFormsHelper::form_error_class();
3421 }
3422
3423 $message = FrmFormsHelper::get_success_message( compact( 'message', 'form', 'entry_id', 'class' ) );
3424
3425 return apply_filters( 'frm_main_feedback', $message, $form, $entry_id );
3426 }
3427
3428 /**
3429 * @return void
3430 */
3431 public static function front_head() {
3432 $version = FrmAppHelper::plugin_version();
3433 $suffix = FrmAppHelper::js_suffix();
3434
3435 if ( $suffix && self::has_combo_js_file() ) {
3436 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . '/js/frm.min.js', array( 'jquery' ), $version, true );
3437 } else {
3438 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . "/js/formidable{$suffix}.js", array( 'jquery' ), $version, true );
3439 }
3440
3441 add_filter( 'script_loader_tag', 'FrmFormsController::defer_script_loading', 10, 2 );
3442
3443 if ( FrmAppHelper::is_admin() ) {
3444 // Don't load this in back-end
3445 return;
3446 }
3447
3448 FrmAppHelper::localize_script( 'front' );
3449 FrmStylesController::enqueue_css( 'register' );
3450 }
3451
3452 /**
3453 * @since 3.0
3454 */
3455 public static function has_combo_js_file() {
3456 return is_readable( FrmAppHelper::plugin_path() . '/js/frm.min.js' );
3457 }
3458
3459 /**
3460 * @param object $form
3461 * @param int|string $this_load
3462 * @param bool $global_load
3463 *
3464 * @return void
3465 */
3466 public static function maybe_load_css( $form, $this_load, $global_load ) {
3467 $load_css = FrmForm::is_form_loaded( $form, $this_load, $global_load );
3468
3469 if ( ! $load_css ) {
3470 return;
3471 }
3472
3473 global $frm_vars;
3474 self::footer_js( 'header' );
3475 $frm_vars['css_loaded'] = true;
3476
3477 self::load_late_css();
3478 }
3479
3480 /**
3481 * If css is loaded only on applicable pages, include it before the form loads
3482 * to prevent a flash of unstyled form.
3483 *
3484 * @since 4.01
3485 *
3486 * @return void
3487 */
3488 private static function load_late_css() {
3489 $frm_settings = FrmAppHelper::get_settings();
3490 $late_css = $frm_settings->load_style === 'dynamic';
3491
3492 if ( ! $late_css || ! self::should_load_late() ) {
3493 return;
3494 }
3495
3496 global $wp_styles;
3497
3498 if ( is_array( $wp_styles->queue ) && in_array( 'formidable', $wp_styles->queue, true ) ) {
3499 wp_print_styles( 'formidable' );
3500 }
3501 }
3502
3503 /**
3504 * Avoid late load if All in One SEO is active because it prevents CSS from loading entirely.
3505 *
3506 * @since 5.2.03
3507 *
3508 * @return bool
3509 */
3510 private static function should_load_late() {
3511 return ! function_exists( 'aioseo' );
3512 }
3513
3514 /**
3515 * @param string $tag
3516 * @param string $handle
3517 *
3518 * @return string
3519 */
3520 public static function defer_script_loading( $tag, $handle ) {
3521 if ( 'captcha-api' === $handle && ! str_contains( $tag, 'defer' ) ) {
3522 return str_replace( ' src', ' defer="defer" async="async" src', $tag );
3523 }
3524
3525 return $tag;
3526 }
3527
3528 /**
3529 * @param string $location
3530 *
3531 * @return void
3532 */
3533 public static function footer_js( $location = 'footer' ) {
3534 global $frm_vars;
3535
3536 FrmStylesController::enqueue_css();
3537
3538 if ( FrmAppHelper::is_admin() || $location === 'header' || empty( $frm_vars['forms_loaded'] ) ) {
3539 return;
3540 }
3541
3542 // Load formidable js
3543 wp_enqueue_script( 'formidable' );
3544
3545 FrmHoneypot::maybe_print_honeypot_js();
3546 }
3547
3548 /**
3549 * @since 2.0.8
3550 *
3551 * @param array $atts
3552 * @param string $content
3553 *
3554 * @return void
3555 */
3556 private static function maybe_minimize_form( $atts, &$content ) {
3557 // Check if minimizing is turned on
3558 if ( self::is_minification_on( $atts ) ) {
3559 $content = str_replace( array( "\r\n", "\r", "\n", "\t", ' ' ), '', $content );
3560 }
3561 }
3562
3563 /**
3564 * @since 2.0.8
3565 *
3566 * @param array $atts
3567 *
3568 * @return bool
3569 */
3570 private static function is_minification_on( $atts ) {
3571 return ! empty( $atts['minimize'] );
3572 }
3573
3574 /**
3575 * @since 5.0.16
3576 *
3577 * @return void
3578 */
3579 public static function landing_page_preview_option() {
3580 $dir = apply_filters( 'frm_landing_page_preview_option', false );
3581
3582 if ( false === $dir || ! file_exists( $dir . 'landing-page-preview-option.php' ) ) {
3583 $dir = self::get_form_views_path();
3584 }
3585
3586 include $dir . 'landing-page-preview-option.php';
3587 }
3588
3589 /**
3590 * @since 5.0.16
3591 *
3592 * @return string
3593 */
3594 private static function get_form_views_path() {
3595 return FrmAppHelper::plugin_path() . '/classes/views/frm-forms/';
3596 }
3597
3598 /**
3599 * Create a page with an embedded formidable Gutenberg block.
3600 *
3601 * @since 5.2
3602 *
3603 * @return void
3604 */
3605 public static function create_page_with_shortcode() {
3606 if ( ! current_user_can( 'publish_posts' ) ) {
3607 die( 0 );
3608 }
3609
3610 check_ajax_referer( 'frm_ajax', 'nonce' );
3611
3612 $type = FrmAppHelper::get_post_param( 'type', '', 'sanitize_text_field' );
3613
3614 if ( ! $type || ! in_array( $type, array( 'form', 'view' ), true ) ) {
3615 die( 0 );
3616 }
3617
3618 $object_id = FrmAppHelper::get_post_param( 'object_id', '', 'absint' );
3619
3620 if ( ! $object_id ) {
3621 die( 0 );
3622 }
3623
3624 $postarr = array( 'post_type' => 'page' );
3625
3626 if ( 'form' === $type ) {
3627 $postarr['post_content'] = self::get_page_shortcode_content_for_form( $object_id );
3628 } else {
3629 $postarr['post_content'] = apply_filters( 'frm_create_page_with_' . $type . '_shortcode_content', '', $object_id );
3630 }
3631
3632 $name = FrmAppHelper::get_post_param( 'name', '', 'sanitize_text_field' );
3633
3634 if ( $name ) {
3635 $postarr['post_title'] = $name;
3636 }
3637
3638 $success = wp_insert_post( $postarr );
3639
3640 if ( ! is_numeric( $success ) || ! $success ) {
3641 die( 0 );
3642 }
3643
3644 wp_send_json(
3645 array(
3646 'redirect' => get_edit_post_link( $success, 'redirect' ),
3647 )
3648 );
3649 }
3650
3651 /**
3652 * @since 5.3
3653 *
3654 * @param int $form_id
3655 *
3656 * @return string
3657 */
3658 private static function get_page_shortcode_content_for_form( $form_id ) {
3659 $shortcode = '[formidable id="' . $form_id . '"]';
3660 $html_comment_start = '<!-- wp:formidable/simple-form {"formId":"' . $form_id . '"} -->';
3661 $html_comment_end = '<!-- /wp:formidable/simple-form -->';
3662 return $html_comment_start . '<div>' . $shortcode . '</div>' . $html_comment_end;
3663 }
3664
3665 /**
3666 * Get page dropdown for AJAX request for embedding form in an existing page.
3667 *
3668 * @return void
3669 */
3670 public static function get_page_dropdown() {
3671 if ( ! current_user_can( 'publish_posts' ) ) {
3672 die( 0 );
3673 }
3674
3675 check_ajax_referer( 'frm_ajax', 'nonce' );
3676
3677 $html = FrmAppHelper::clip(
3678 function () {
3679 FrmAppHelper::maybe_autocomplete_pages_options(
3680 array(
3681 'field_name' => 'frm_page_dropdown',
3682 'page_id' => '',
3683 'placeholder' => __( 'Select a Page', 'formidable' ),
3684 )
3685 );
3686 }
3687 );
3688 $post_type_object = get_post_type_object( 'page' );
3689 wp_send_json(
3690 array(
3691 'html' => $html,
3692 'edit_page_url' => admin_url( sprintf( $post_type_object->_edit_link . '&action=edit', 0 ) ),
3693 )
3694 );
3695 }
3696
3697 /**
3698 * @deprecated 4.0
3699 *
3700 * @param array $values
3701 *
3702 * @return void
3703 */
3704 public static function create( $values = array() ) {
3705 _deprecated_function( __METHOD__, '4.0', 'FrmFormsController::update' );
3706 self::update( $values );
3707 }
3708 }
3709