PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.29
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.29
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.29, 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 return;
770 }
771
772 $error = __( 'You do not have permission to view this form', 'formidable' );
773 wp_die(
774 '<h1>' . esc_html( $error ) . '</h1>',
775 '<p>' . esc_html( $error ) . '</p>',
776 403
777 );
778 }
779
780 /**
781 * Some themes have a null $src value.
782 * This function adds a filter to ensure that $src is not null.
783 * WP will call str_starts_with with the null value triggering a deprecated message otherwise.
784 *
785 * @since 6.10
786 *
787 * @return void
788 */
789 private static function fix_deprecated_null_param_warning() {
790 add_filter(
791 'script_loader_src',
792 /**
793 * @param string|null $src
794 *
795 * @return string
796 */
797 function ( $src ) {
798 return is_null( $src ) ? '' : $src;
799 }
800 );
801 }
802
803 public static function untrash() {
804 self::change_form_status( 'untrash' );
805 }
806
807 /**
808 * @param array $ids
809 *
810 * @return string
811 */
812 public static function bulk_untrash( $ids ) {
813 FrmAppHelper::permission_check( 'frm_edit_forms' );
814
815 $count = FrmForm::set_status( $ids, 'published' );
816
817 if ( ! $count ) {
818 // Don't show "0 forms restored" on refresh.
819 return '';
820 }
821
822 /* translators: %1$s: Number of forms */
823 return sprintf( _n( '%1$s form restored from the Trash.', '%1$s forms restored from the Trash.', $count, 'formidable' ), $count );
824 }
825
826 /**
827 * @since 3.06
828 */
829 public static function ajax_trash() {
830 FrmAppHelper::permission_check( 'frm_delete_forms' );
831 check_ajax_referer( 'frm_ajax', 'nonce' );
832 $form_id = FrmAppHelper::get_param( 'id', '', 'post', 'absint' );
833 FrmForm::set_status( $form_id, 'trash' );
834 wp_die();
835 }
836
837 public static function trash() {
838 self::change_form_status( 'trash' );
839 }
840
841 /**
842 * @param string $status
843 *
844 * @return void
845 */
846 public static function change_form_status( $status ) {
847 $available_status = array(
848 'untrash' => array(
849 'permission' => 'frm_edit_forms',
850 'new_status' => 'published',
851 ),
852 'trash' => array(
853 'permission' => 'frm_delete_forms',
854 'new_status' => 'trash',
855 ),
856 );
857
858 if ( ! isset( $available_status[ $status ] ) ) {
859 return;
860 }
861
862 FrmAppHelper::permission_check( $available_status[ $status ]['permission'] );
863
864 $params = FrmForm::list_page_params();
865
866 // Check nonce url.
867 check_admin_referer( $status . '_form_' . $params['id'] );
868
869 $count = 0;
870
871 if ( FrmForm::set_status( $params['id'], $available_status[ $status ]['new_status'] ) ) {
872 ++$count;
873 }
874
875 $form_type = FrmAppHelper::get_simple_request(
876 array(
877 'param' => 'form_type',
878 'type' => 'request',
879 )
880 );
881
882 /* translators: %1$s: Number of forms */
883 $available_status['untrash']['message'] = sprintf( _n( '%1$s form restored from the Trash.', '%1$s forms restored from the Trash.', $count, 'formidable' ), $count );
884
885 /* translators: %1$s: Number of forms, %2$s: Start link HTML, %3$s: End link HTML */
886 $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
887
888 $message = $available_status[ $status ]['message'];
889
890 self::display_forms_list( $params, $message );
891 }
892
893 /**
894 * @param array $ids
895 *
896 * @return string
897 */
898 public static function bulk_trash( $ids ) {
899 FrmAppHelper::permission_check( 'frm_delete_forms' );
900
901 $count = 0;
902
903 foreach ( $ids as $id ) {
904 if ( FrmForm::trash( $id ) ) {
905 ++$count;
906 }
907 }
908
909 if ( ! $count ) {
910 return '';
911 }
912
913 $current_page = FrmAppHelper::get_simple_request(
914 array(
915 'param' => 'form_type',
916 'type' => 'request',
917 )
918 );
919
920 return sprintf(
921 /* translators: %1$s: Number of forms, %2$s: Start link HTML, %3$s: End link HTML */
922 _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' ),
923 $count,
924 '<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
925 '</a>'
926 );
927 }
928
929 public static function destroy() {
930 FrmAppHelper::permission_check( 'frm_delete_forms' );
931
932 $params = FrmForm::list_page_params();
933
934 // Check nonce url.
935 check_admin_referer( 'destroy_form_' . $params['id'] );
936
937 $count = 0;
938
939 if ( FrmForm::destroy( $params['id'] ) ) {
940 ++$count;
941 }
942
943 /* translators: %1$s: Number of forms */
944 $message = sprintf( _n( '%1$s Form Permanently Deleted', '%1$s Forms Permanently Deleted', $count, 'formidable' ), $count );
945
946 self::display_forms_list( $params, $message );
947 }
948
949 /**
950 * @param array $ids
951 *
952 * @return string
953 */
954 public static function bulk_destroy( $ids ) {
955 FrmAppHelper::permission_check( 'frm_delete_forms' );
956
957 $count = 0;
958
959 foreach ( $ids as $id ) {
960 $d = FrmForm::destroy( $id );
961
962 if ( $d ) {
963 ++$count;
964 }
965 }
966
967 if ( $count ) {
968 /* translators: %1$s: Number of forms */
969 return sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
970 }
971
972 return '';
973 }
974
975 /**
976 * @since 6.7.1 Function went from private to public.
977 */
978 public static function delete_all() {
979 // Check nonce url.
980 $permission_error = FrmAppHelper::permission_nonce_error( 'frm_delete_forms', '_wpnonce', 'bulk-toplevel_page_formidable' );
981
982 if ( $permission_error !== false ) {
983 self::display_forms_list( array(), '', array( $permission_error ) );
984 return;
985 }
986
987 $count = FrmForm::scheduled_delete( time() );
988 $url = remove_query_arg( array( 'delete_all' ) );
989 $url .= '&message=forms_permanently_deleted&forms_deleted=' . $count;
990
991 wp_safe_redirect( $url );
992 die();
993 }
994
995 /**
996 * Create a new form from the modal.
997 *
998 * @since 4.0
999 */
1000 public static function build_new_form() {
1001 FrmAppHelper::permission_check( 'frm_edit_forms' );
1002 check_ajax_referer( 'frm_ajax', 'nonce' );
1003
1004 $new_values = self::get_modal_values();
1005 $new_values['form_key'] = $new_values['name'];
1006 $new_values['options'] = array(
1007 'antispam' => 1,
1008 'on_submit_migrated' => 1,
1009 );
1010
1011 /**
1012 * Allows changing form values before creating from the modal.
1013 *
1014 * @since 5.4
1015 *
1016 * @param array $values Form values.
1017 */
1018 $new_values = apply_filters( 'frm_new_form_values', $new_values );
1019
1020 $form_id = FrmForm::create( $new_values );
1021 /**
1022 * @since 5.3
1023 *
1024 * @param int $form_id
1025 */
1026 do_action( 'frm_build_new_form', $form_id );
1027
1028 self::create_submit_button_field( $form_id );
1029 self::create_default_on_submit_action( $form_id );
1030 self::create_default_email_action( $form_id );
1031
1032 $response = array(
1033 'redirect' => FrmForm::get_edit_link( $form_id ) . '&new_template=true',
1034 );
1035
1036 echo wp_json_encode( $response );
1037 wp_die();
1038 }
1039
1040 /**
1041 * Before creating a new form, get the name and description from the modal.
1042 *
1043 * @since 4.0
1044 *
1045 * @return array<string,string>
1046 */
1047 public static function get_modal_values() {
1048 $name = FrmAppHelper::get_param( 'name', '', 'post', 'sanitize_text_field' );
1049 $desc = FrmAppHelper::get_param( 'desc', '', 'post', 'sanitize_textarea_field' );
1050
1051 return array(
1052 'name' => $name,
1053 'description' => $desc,
1054 );
1055 }
1056
1057 /**
1058 * Inserts Formidable button
1059 * Hook exists since 2.5.0
1060 *
1061 * @since 2.0.15
1062 *
1063 * @return void
1064 */
1065 public static function insert_form_button() {
1066 if ( ! current_user_can( 'frm_view_forms' ) ) {
1067 return;
1068 }
1069
1070 // Store the result in memory and re-use it when this function is called multiple times.
1071 // This helps speed up the form builder when there are a lot of HTML fields, where this
1072 // Button is inserted once per HTML field.
1073 // In a form with 66 HTML fields, this saves 0.5 seconds on page load time, tested locally.
1074 if ( ! isset( self::$formidable_tinymce_button ) ) {
1075 FrmAppHelper::load_admin_wide_js();
1076 $menu_name = FrmAppHelper::get_menu_name();
1077 $icon = apply_filters( 'frm_media_icon', FrmAppHelper::svg_logo() );
1078 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
1079 }
1080 echo self::$formidable_tinymce_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1081 }
1082
1083 /**
1084 * @return void
1085 */
1086 public static function insert_form_popup() {
1087 if ( ! self::should_insert_form_popup() ) {
1088 return;
1089 }
1090
1091 FrmAppHelper::load_admin_wide_js();
1092
1093 $shortcodes = array(
1094 'formidable' => array(
1095 'name' => __( 'Form', 'formidable' ),
1096 'label' => __( 'Insert a Form', 'formidable' ),
1097 ),
1098 );
1099 $shortcodes = apply_filters( 'frm_popup_shortcodes', $shortcodes );
1100
1101 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/insert_form_popup.php';
1102
1103 if ( ! FrmAppHelper::is_form_builder_page() || class_exists( '_WP_Editors', false ) ) {
1104 return;
1105 }
1106
1107 // Initialize a wysiwyg so we have usable settings defined in tinyMCEPreInit.mceInit
1108 require ABSPATH . WPINC . '/class-wp-editor.php';
1109 // phpcs:disable Generic.WhiteSpace.ScopeIndent
1110 ?>
1111 <div class="frm_hidden">
1112 <?php wp_editor( '', 'frm_description_placeholder', array() ); ?>
1113 </div>
1114 <?php
1115 // phpcs:enable Generic.WhiteSpace.ScopeIndent
1116 }
1117
1118 /**
1119 * Check the page being loaded, determine if this is a page that should include the form popup.
1120 *
1121 * @since 5.0.14
1122 *
1123 * @return bool
1124 */
1125 private static function should_insert_form_popup() {
1126 if ( FrmAppHelper::is_form_builder_page() ) {
1127 return true;
1128 }
1129
1130 $page = basename( FrmAppHelper::get_server_value( 'PHP_SELF' ) );
1131 return in_array( $page, array( 'post.php', 'page.php', 'page-new.php', 'post-new.php' ), true );
1132 }
1133
1134 public static function get_shortcode_opts() {
1135 FrmAppHelper::permission_check( 'frm_view_forms' );
1136 check_ajax_referer( 'frm_ajax', 'nonce' );
1137
1138 $shortcode = FrmAppHelper::get_post_param( 'shortcode', '', 'sanitize_text_field' );
1139
1140 if ( ! $shortcode ) {
1141 wp_die();
1142 }
1143
1144 echo '<div id="sc-opts-' . esc_attr( $shortcode ) . '" class="frm_shortcode_option">';
1145 echo '<input type="radio" name="frmsc" value="' . esc_attr( $shortcode ) . '" id="sc-' . esc_attr( $shortcode ) . '" class="frm_hidden" />';
1146
1147 $form_id = '';
1148 $opts = array();
1149 switch ( $shortcode ) {
1150 case 'formidable':
1151 $opts = array(
1152 'form_id' => 'id',
1153 'title' => array(
1154 'val' => 1,
1155 'label' => __( 'Display form title', 'formidable' ),
1156 ),
1157 'description' => array(
1158 'val' => 1,
1159 'label' => __( 'Display form description', 'formidable' ),
1160 ),
1161 'minimize' => array(
1162 'val' => 1,
1163 'label' => __( 'Minimize form HTML', 'formidable' ),
1164 ),
1165 );
1166 }
1167
1168 $opts = apply_filters( 'frm_sc_popup_opts', $opts, $shortcode );
1169
1170 if ( isset( $opts['form_id'] ) && is_string( $opts['form_id'] ) ) {
1171 // Allow other shortcodes to use the required form id option
1172 $form_id = $opts['form_id'];
1173 unset( $opts['form_id'] );
1174 }
1175
1176 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/shortcode_opts.php';
1177
1178 echo '</div>';
1179
1180 wp_die();
1181 }
1182
1183 /**
1184 * Display list of forms in a table.
1185 *
1186 * @param array $params
1187 * @param string $message
1188 * @param array $errors
1189 *
1190 * @return void
1191 */
1192 public static function display_forms_list( $params = array(), $message = '', $errors = array() ) {
1193 FrmAppHelper::permission_check( 'frm_view_forms' );
1194
1195 global $wpdb, $frm_vars;
1196
1197 if ( ! $params ) {
1198 $params = FrmForm::list_page_params();
1199 }
1200
1201 /**
1202 * @since 5.3.1
1203 *
1204 * @param string $table_class Class name for List Helper.
1205 */
1206 $table_class = apply_filters( 'frm_forms_list_class', 'FrmFormsListHelper' );
1207 $wp_list_table = new $table_class( compact( 'params' ) );
1208 $pagenum = $wp_list_table->get_pagenum();
1209
1210 $wp_list_table->prepare_items();
1211
1212 $total_pages = $wp_list_table->get_pagination_arg( 'total_pages' );
1213
1214 if ( $pagenum > $total_pages && $total_pages > 0 ) {
1215 wp_safe_redirect( esc_url_raw( add_query_arg( 'paged', $total_pages ) ) );
1216 die();
1217 }
1218
1219 $error = FrmInbox::check_for_error();
1220
1221 if ( $error ) {
1222 $show_messages = array( $error['subject'] . '. ' . $error['message'] );
1223 }
1224
1225 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/list.php';
1226 }
1227
1228 /**
1229 * @param array<string,string> $columns
1230 *
1231 * @return array<string,string>
1232 */
1233 public static function get_columns( $columns ) {
1234 $columns['cb'] = '<input type="checkbox" />';
1235 $columns['name'] = esc_html__( 'Form Title', 'formidable' );
1236 $columns['entries'] = esc_html__( 'Entries', 'formidable' );
1237 $columns['id'] = 'ID';
1238 $columns['form_key'] = esc_html__( 'Key', 'formidable' );
1239
1240 if ( 'trash' !== FrmAppHelper::simple_get( 'form_type' ) ) {
1241 $columns['shortcode'] = esc_html__( 'Actions', 'formidable' );
1242 }
1243
1244 $columns['created_at'] = esc_html__( 'Date', 'formidable' );
1245
1246 add_screen_option(
1247 'per_page',
1248 array(
1249 'label' => __( 'Forms', 'formidable' ),
1250 'default' => 20,
1251 'option' => 'formidable_page_formidable_per_page',
1252 )
1253 );
1254
1255 return $columns;
1256 }
1257
1258 /**
1259 * @return array<string,string>
1260 */
1261 public static function get_sortable_columns() {
1262 return array(
1263 'id' => 'id',
1264 'name' => 'name',
1265 'description' => 'description',
1266 'form_key' => 'form_key',
1267 'created_at' => 'created_at',
1268 );
1269 }
1270
1271 /**
1272 * @param mixed $hidden_columns
1273 *
1274 * @return array
1275 */
1276 public static function hidden_columns( $hidden_columns ) {
1277 if ( ! is_array( $hidden_columns ) ) {
1278 $hidden_columns = array();
1279 }
1280
1281 $type = FrmAppHelper::get_simple_request(
1282 array(
1283 'param' => 'form_type',
1284 'type' => 'request',
1285 )
1286 );
1287
1288 if ( $type === 'template' ) {
1289 $hidden_columns[] = 'id';
1290 $hidden_columns[] = 'form_key';
1291 }
1292
1293 return $hidden_columns;
1294 }
1295
1296 /**
1297 * @param mixed $save
1298 * @param string $option
1299 * @param int|string $value
1300 *
1301 * @return mixed
1302 */
1303 public static function save_per_page( $save, $option, $value ) {
1304 if ( $option === 'formidable_page_formidable_per_page' ) {
1305 return (int) $value;
1306 }
1307
1308 return $save;
1309 }
1310
1311 /**
1312 * @param int|string $id
1313 * @param array $errors
1314 * @param string $message
1315 * @param bool $create_link
1316 *
1317 * @return void
1318 */
1319 private static function get_edit_vars( $id, $errors = array(), $message = '', $create_link = false ) {
1320 global $frm_vars;
1321
1322 $form = FrmForm::getOne( $id );
1323 $error_args = array(
1324 'title' => __( 'You can\'t edit the form', 'formidable' ),
1325 'body' => __( 'You are trying to edit a form that does not exist', 'formidable' ),
1326 'cancel_url' => admin_url( 'admin.php?page=formidable' ),
1327 'continue_url' => add_query_arg(
1328 array(
1329 'page' => 'formidable',
1330 )
1331 ),
1332 );
1333
1334 if ( ! $form ) {
1335 FrmAppController::show_error_modal( $error_args );
1336 return;
1337 }
1338
1339 if ( 'trash' === $form->status ) {
1340 $error_args['body'] = __( 'The form you\'re trying to edit is in trash. You must restore it first before you can make changes', 'formidable' );
1341 $error_args['continue_url'] = add_query_arg(
1342 array(
1343 'page' => 'formidable',
1344 '_wpnonce' => wp_create_nonce( 'untrash_form_' . $id ),
1345 'form_type' => 'trash',
1346 'frm_action' => 'untrash',
1347 'id' => $id,
1348 )
1349 );
1350 $error_args['continue_text'] = __( 'Restore form', 'formidable' );
1351 FrmAppController::show_error_modal( $error_args );
1352 return;
1353 }
1354
1355 if ( $form->parent_form_id ) {
1356 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
1357 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
1358 }
1359
1360 $frm_field_selection = FrmField::field_selection();
1361 $fields = FrmField::get_all_for_form( $form->id );
1362
1363 // Automatically add end section fields if they don't exist (2.0 migration).
1364 $reset_fields = false;
1365 FrmFormsHelper::auto_add_end_section_fields( $form, $fields, $reset_fields );
1366 FrmSubmitHelper::maybe_create_submit_field( $form, $fields, $reset_fields );
1367
1368 if ( $reset_fields ) {
1369 $fields = FrmField::get_all_for_form( $form->id, '', 'exclude' );
1370 }
1371
1372 unset( $reset_fields );
1373
1374 $args = array( 'parent_form_id' => $form->id );
1375 $values = FrmAppHelper::setup_edit_vars( $form, 'forms', '', true, array(), $args );
1376
1377 /**
1378 * Allows modifying the list of fields in the form builder.
1379 *
1380 * @since 5.0.04
1381 *
1382 * @param object[] $fields Array of fields.
1383 * @param array $args The arguments. Contains `form`.
1384 */
1385 $values['fields'] = apply_filters( 'frm_fields_in_form_builder', $fields, compact( 'form' ) );
1386
1387 $edit_message = __( 'Form was successfully updated.', 'formidable' );
1388
1389 if ( $form->is_template && $message === $edit_message ) {
1390 $message = __( 'Template was successfully updated.', 'formidable' );
1391 }
1392
1393 self::maybe_update_form_builder_message( $message );
1394
1395 $has_fields = ! empty( $values['fields'] ) && ! FrmSubmitHelper::only_contains_submit_field( $values['fields'] );
1396
1397 if ( defined( 'DOING_AJAX' ) ) {
1398 wp_die();
1399 }
1400
1401 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/edit.php';
1402 }
1403
1404 /**
1405 * @param array $fields
1406 *
1407 * @return array
1408 */
1409 public static function update_form_builder_fields( $fields ) {
1410 foreach ( $fields as $field ) {
1411 $field->do_not_include_icons = true;
1412 }
1413 return $fields;
1414 }
1415
1416 /**
1417 * @param string $message
1418 *
1419 * @return void
1420 */
1421 public static function maybe_update_form_builder_message( &$message ) {
1422 if ( 'form_duplicated' === FrmAppHelper::simple_get( 'message' ) ) {
1423 $message = __( 'Form was Successfully Copied', 'formidable' );
1424 }
1425 }
1426
1427 /**
1428 * @param int|string $id
1429 * @param array $errors
1430 * @param array|string $args
1431 *
1432 * @return void
1433 */
1434 public static function get_settings_vars( $id, $errors = array(), $args = array() ) {
1435 FrmAppHelper::permission_check( 'frm_edit_forms' );
1436
1437 global $frm_vars;
1438
1439 self::maybe_print_media_templates();
1440
1441 if ( ! is_array( $args ) ) {
1442 // For reverse compatibility.
1443 $args = array(
1444 'message' => $args,
1445 );
1446 }
1447
1448 $defaults = array(
1449 'message' => '',
1450 'warnings' => array(),
1451 );
1452 $args = array_merge( $defaults, $args );
1453 $message = $args['message'];
1454 $warnings = $args['warnings'];
1455 $form = FrmForm::getOne( $id );
1456 $fields = FrmField::get_all_for_form( $id );
1457 $values = FrmAppHelper::setup_edit_vars( $form, 'forms', $fields, true );
1458
1459 /**
1460 * Allows changing fields in the form settings.
1461 *
1462 * @since 5.0.04
1463 *
1464 * @param array $fields Array of fields.
1465 * @param array $args The arguments. Contains `form`.
1466 */
1467 $values['fields'] = apply_filters( 'frm_fields_in_settings', $values['fields'], compact( 'form' ) );
1468
1469 self::clean_submit_html( $values );
1470
1471 $sections = self::get_settings_tabs( $values );
1472 $current = FrmAppHelper::simple_get( 't', 'sanitize_title', 'advanced_settings' );
1473
1474 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings.php';
1475 }
1476
1477 /**
1478 * 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.
1479 *
1480 * @since 6.10
1481 *
1482 * @return void
1483 */
1484 private static function maybe_print_media_templates() {
1485 if ( FrmAppHelper::pro_is_included() ) {
1486 // This issue does not exist when Pro is active so exit early.
1487 return;
1488 }
1489
1490 add_action(
1491 'wp_enqueue_editor',
1492 function () {
1493 wp_print_media_templates();
1494 }
1495 );
1496 }
1497
1498 /**
1499 * @since 4.0
1500 *
1501 * @param array $atts
1502 *
1503 * @return void
1504 */
1505 public static function form_publish_button( $atts ) {
1506 $values = $atts['values'];
1507 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/_publish_box.php';
1508 }
1509
1510 /**
1511 * Get a list of all the settings tabs for the form settings page.
1512 *
1513 * @since 4.0
1514 *
1515 * @param array $values
1516 *
1517 * @return array
1518 */
1519 private static function get_settings_tabs( $values ) {
1520 $sections = array(
1521 'advanced' => array(
1522 'name' => __( 'General', 'formidable' ),
1523 'title' => __( 'General Form Settings', 'formidable' ),
1524 'function' => array( self::class, 'advanced_settings' ),
1525 'icon' => 'frmfont frm_settings_icon',
1526 ),
1527 'email' => array(
1528 'name' => __( 'Actions & Notifications', 'formidable' ),
1529 'function' => array( 'FrmFormActionsController', 'email_settings' ),
1530 'id' => 'frm_notification_settings',
1531 'icon' => 'frmfont frm_mail_bulk_icon',
1532 ),
1533 'permissions' => array(
1534 'name' => __( 'Form Permissions', 'formidable' ),
1535 'icon' => 'frmfont frm_lock_closed_icon',
1536 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1537 'data' => array(
1538 'medium' => 'permissions',
1539 'upgrade' => __( 'Form Permissions', 'formidable' ),
1540 'message' => __( 'Allow editing, protect forms and files, limit entries, and save drafts. Upgrade to get form and entry permissions.', 'formidable' ),
1541 'screenshot' => 'permissions.png',
1542 'learn-more' => FrmAppHelper::get_doc_url( 'general-form-settings/#kb-form-permissions', 'form-permissions-settings' ),
1543 ),
1544 ),
1545 'scheduling' => array(
1546 'name' => __( 'Form Scheduling', 'formidable' ),
1547 'icon' => 'frmfont frm_calendar_icon',
1548 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1549 'data' => array(
1550 'medium' => 'scheduling',
1551 'upgrade' => __( 'Form scheduling settings', 'formidable' ),
1552 'screenshot' => 'scheduling.png',
1553 'learn-more' => FrmAppHelper::get_doc_url( 'general-form-settings/#kb-form-scheduling', 'form-scheduling-settings' ),
1554 ),
1555 ),
1556 'buttons' => array(
1557 'name' => __( 'Buttons', 'formidable' ),
1558 'class' => self::class,
1559 'function' => 'buttons_settings',
1560 'icon' => 'frmfont frm_button_icon',
1561 ),
1562 'landing' => array(
1563 'name' => __( 'Form Landing Page', 'formidable' ),
1564 'icon' => 'frmfont frm_file_text_icon',
1565 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1566 'data' => FrmAppHelper::get_landing_page_upgrade_data_params(),
1567 ),
1568 'chat' => array(
1569 'name' => __( 'Conversational Forms', 'formidable' ),
1570 'icon' => 'frmfont frm_chat_forms_icon',
1571 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1572 'data' => FrmAppHelper::get_upgrade_data_params(
1573 'chat',
1574 array(
1575 'upgrade' => __( 'Conversational Forms', 'formidable' ),
1576 'message' => __( 'Ask one question at a time for automated conversations.', 'formidable' ),
1577 'screenshot' => 'chat.png',
1578 'learn-more' => FrmAppHelper::get_doc_url( 'conversational-forms', 'conversational-forms-settings', false ),
1579 )
1580 ),
1581 ),
1582 'abandonment' => array(
1583 'name' => __( 'Form Abandonment', 'formidable' ),
1584 'icon' => 'frmfont frm_abandoned_icon',
1585 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1586 'data' => FrmAppHelper::get_upgrade_data_params(
1587 'abandonment',
1588 array(
1589 'upgrade' => __( 'Form abandonment settings', 'formidable' ),
1590 'message' => __( 'Unlock the power of data capture to boost lead generation and master the art of form optimization.', 'formidable' ),
1591 'screenshot' => 'abandonment.png',
1592 'learn-more' => FrmAppHelper::get_doc_url( 'features/form-abandonment-wordpress-plugin', 'form-abandonment-settings', false ),
1593 )
1594 ),
1595 ),
1596 'html' => array(
1597 'name' => __( 'Customize HTML', 'formidable' ),
1598 'class' => self::class,
1599 'function' => 'html_settings',
1600 'icon' => 'frmfont frm_code_icon',
1601 ),
1602 );
1603
1604 if ( ! has_action( 'frm_add_form_style_tab_options' ) && ! has_action( 'frm_add_form_button_options' ) ) {
1605 unset( $sections['buttons'] );
1606 }
1607
1608 $sections = apply_filters( 'frm_add_form_settings_section', $sections, $values );
1609
1610 foreach ( $sections as $key => $section ) {
1611 $defaults = array(
1612 'html_class' => '',
1613 'name' => ucfirst( $key ),
1614 'icon' => 'frmfont frm_settings_icon',
1615 );
1616
1617 $section = array_merge( $defaults, $section );
1618
1619 if ( ! isset( $section['anchor'] ) ) {
1620 $section['anchor'] = $key;
1621 }
1622
1623 $section['anchor'] .= '_settings';
1624
1625 if ( ! isset( $section['title'] ) ) {
1626 $section['title'] = $section['name'];
1627 }
1628
1629 if ( ! isset( $section['id'] ) ) {
1630 $section['id'] = $section['anchor'];
1631 }
1632
1633 $sections[ $key ] = $section;
1634 }//end foreach
1635
1636 return $sections;
1637 }
1638
1639 /**
1640 * @since 4.0
1641 *
1642 * @param array $values
1643 *
1644 * @return void
1645 */
1646 public static function advanced_settings( $values ) {
1647 $first_h3 = 'frm_first_h3';
1648 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-advanced.php';
1649 }
1650
1651 /**
1652 * @param array $values
1653 *
1654 * @return void
1655 */
1656 public static function render_spam_settings( $values ) {
1657 if ( function_exists( 'akismet_http_post' ) ) {
1658 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/akismet.php';
1659 }
1660 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/stopforumspam.php';
1661 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/antispam.php';
1662 }
1663
1664 /**
1665 * @since 4.0
1666 *
1667 * @param array $values
1668 *
1669 * @return void
1670 */
1671 public static function buttons_settings( $values ) {
1672 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-buttons.php';
1673 }
1674
1675 /**
1676 * @since 4.0
1677 *
1678 * @param array $values
1679 *
1680 * @return void
1681 */
1682 public static function html_settings( $values ) {
1683 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-html.php';
1684 }
1685
1686 /**
1687 * Replace old Submit Button href with new href to avoid errors in Chrome
1688 *
1689 * @since 2.03.08
1690 *
1691 * @param array|bool $values
1692 *
1693 * @return void
1694 */
1695 private static function clean_submit_html( &$values ) {
1696 if ( is_array( $values ) && isset( $values['submit_html'] ) ) {
1697 $values['submit_html'] = str_replace( 'javascript:void(0)', '#', $values['submit_html'] );
1698 }
1699 }
1700
1701 /**
1702 * Updates classes used in submit and prev buttons to avoid conflict with twenty twenty-one theme.
1703 *
1704 * @param array $classes
1705 *
1706 * @return array
1707 */
1708 public static function update_button_classes( $classes ) {
1709 if ( function_exists( 'twenty_twenty_one_setup' ) ) {
1710 $classes[] = 'has-text-color has-background';
1711 }
1712
1713 return $classes;
1714 }
1715
1716 /**
1717 * @since 6.27 Added $template_path parameter.
1718 *
1719 * @param int|string $form_id
1720 * @param string $class
1721 * @param string $template_path The path to a template file to use instead of the default.
1722 *
1723 * @return void
1724 */
1725 public static function mb_tags_box( $form_id, $class = '', $template_path = 'default' ) {
1726 $fields = FrmField::get_all_for_form( $form_id, '', 'include' );
1727
1728 /**
1729 * Allows modifying the list of fields in the tags box.
1730 *
1731 * @since 5.0.04
1732 *
1733 * @param array $fields The list of fields.
1734 * @param array $args The arguments. Contains `form_id`.
1735 */
1736 $fields = apply_filters( 'frm_fields_in_tags_box', $fields, compact( 'form_id' ) );
1737 $linked_forms = array();
1738 $col = 'one';
1739 $settings_tab = FrmAppHelper::is_admin_page( 'formidable' );
1740 $cond_shortcodes = apply_filters( 'frm_conditional_shortcodes', array() );
1741 $entry_shortcodes = self::get_shortcode_helpers( $settings_tab );
1742 $advanced_helpers = self::advanced_helpers( compact( 'fields', 'form_id' ) );
1743
1744 if ( 'default' === $template_path || ! file_exists( $template_path ) ) {
1745 include FrmAppHelper::plugin_path() . '/classes/views/shared/mb_adv_info.php';
1746 return;
1747 }
1748
1749 include $template_path;
1750 }
1751
1752 /**
1753 * @since 3.04.01
1754 *
1755 * @param array $atts
1756 *
1757 * @return array
1758 */
1759 private static function advanced_helpers( $atts ) {
1760 $advanced_helpers = array(
1761 'default' => array(
1762 'heading' => __( 'Customize field values with the following parameters.', 'formidable' ),
1763 'codes' => self::get_advanced_shortcodes(),
1764 ),
1765 );
1766
1767 $user_fields = self::user_shortcodes();
1768
1769 if ( $user_fields ) {
1770 $user_helpers = array();
1771
1772 foreach ( $user_fields as $uk => $uf ) {
1773 $user_helpers[ '|user_id| show="' . $uk . '"' ] = $uf;
1774 unset( $uk, $uf );
1775 }
1776
1777 $advanced_helpers['user_id'] = array(
1778 'codes' => $user_helpers,
1779 );
1780 }
1781
1782 /**
1783 * Add extra helper shortcodes on the Advanced tab in form settings and views
1784 *
1785 * @since 3.04.01
1786 *
1787 * @param array $advanced_helpers
1788 * @param array $atts - Includes fields and form_id
1789 */
1790 return apply_filters( 'frm_advanced_helpers', $advanced_helpers, $atts );
1791 }
1792
1793 /**
1794 * Get an array of the options to display in the advanced tab
1795 * of the customization panel
1796 *
1797 * @since 2.0.6
1798 *
1799 * @return array
1800 */
1801 private static function get_advanced_shortcodes() {
1802 $adv_shortcodes = array(
1803 'x sep=", "' => array(
1804 'label' => __( 'Separator', 'formidable' ),
1805 'title' => __( 'Use a different separator for checkbox fields', 'formidable' ),
1806 ),
1807 'x format="d-m-Y"' => array(
1808 'label' => __( 'Date Format', 'formidable' ),
1809 ),
1810 'x show="field_label"' => array(
1811 'label' => __( 'Field Label', 'formidable' ),
1812 ),
1813 'x wpautop=0' => array(
1814 'label' => __( 'No Auto P', 'formidable' ),
1815 'title' => __( 'Do not automatically add any paragraphs or line breaks', 'formidable' ),
1816 ),
1817 );
1818
1819 // __( 'Leave blank instead of defaulting to User Login', 'formidable' ) : blank=1
1820
1821 return apply_filters( 'frm_advanced_shortcodes', $adv_shortcodes );
1822 }
1823
1824 /**
1825 * @since 3.04.01
1826 *
1827 * @return array
1828 */
1829 private static function user_shortcodes() {
1830 $options = array(
1831 'ID' => __( 'User ID', 'formidable' ),
1832 'first_name' => __( 'First Name', 'formidable' ),
1833 'last_name' => __( 'Last Name', 'formidable' ),
1834 'display_name' => __( 'Display Name', 'formidable' ),
1835 'user_login' => __( 'User Login', 'formidable' ),
1836 'user_email' => __( 'Email', 'formidable' ),
1837 'avatar' => __( 'Avatar', 'formidable' ),
1838 'author_link' => __( 'Author Link', 'formidable' ),
1839 );
1840
1841 return apply_filters( 'frm_user_shortcodes', $options );
1842 }
1843
1844 /**
1845 * Get an array of the helper shortcodes to display in the customization panel
1846 *
1847 * @since 2.0.6
1848 *
1849 * @param bool $settings_tab
1850 *
1851 * @return array
1852 */
1853 private static function get_shortcode_helpers( $settings_tab ) {
1854 $entry_shortcodes = array(
1855 'id' => __( 'Entry ID', 'formidable' ),
1856 'key' => __( 'Entry Key', 'formidable' ),
1857 'post_id' => __( 'Post ID', 'formidable' ),
1858 'ip' => __( 'User IP', 'formidable' ),
1859 'created-at' => __( 'Entry created', 'formidable' ),
1860 'updated-at' => __( 'Entry updated', 'formidable' ),
1861 '' => '',
1862 'siteurl' => __( 'Site URL', 'formidable' ),
1863 'sitename' => __( 'Site Name', 'formidable' ),
1864 'form_name' => __( 'Form Name', 'formidable' ),
1865 );
1866
1867 $entry_shortcodes = array_merge( FrmShortcodeHelper::get_contextual_shortcode_values(), $entry_shortcodes );
1868
1869 if ( ! FrmAppHelper::pro_is_installed() ) {
1870 unset( $entry_shortcodes['post_id'] );
1871 }
1872
1873 /**
1874 * Use this hook to add or remove buttons in the helpers section
1875 * in the customization panel
1876 *
1877 * @since 2.0.6
1878 *
1879 * @param array $entry_shortcodes
1880 * @param bool $settings_tab
1881 */
1882 return apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab );
1883 }
1884
1885 /**
1886 * Insert the form class setting into the form.
1887 *
1888 * @param stdClass $form
1889 *
1890 * @return void
1891 */
1892 public static function form_classes( $form ) {
1893 if ( isset( $form->options['form_class'] ) ) {
1894 echo esc_attr( sanitize_text_field( $form->options['form_class'] ) );
1895 }
1896
1897 if ( ! empty( $form->options['js_validate'] ) ) {
1898 echo ' frm_js_validate ';
1899 self::add_js_validate_form_to_global_vars( $form );
1900 }
1901
1902 if ( FrmForm::is_ajax_on( $form ) ) {
1903 echo ' frm_ajax_submit ';
1904 }
1905 }
1906
1907 /**
1908 * @since 5.0.03
1909 *
1910 * @param stdClass $form
1911 */
1912 public static function add_js_validate_form_to_global_vars( $form ) {
1913 global $frm_vars;
1914
1915 if ( ! isset( $frm_vars['js_validate_forms'] ) ) {
1916 $frm_vars['js_validate_forms'] = array();
1917 }
1918 $frm_vars['js_validate_forms'][ $form->id ] = $form;
1919 }
1920
1921 public static function get_email_html() {
1922 FrmAppHelper::permission_check( 'frm_view_forms' );
1923 check_ajax_referer( 'frm_ajax', 'nonce' );
1924
1925 echo FrmEntriesController::show_entry_shortcode( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1926 array(
1927 'form_id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1928 'default_email' => true,
1929 'plain_text' => FrmAppHelper::get_post_param( 'plain_text', '', 'absint' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1930 )
1931 );
1932 wp_die();
1933 }
1934
1935 /**
1936 * @param string $content
1937 * @param int|stdClass|string $form
1938 * @param false|int|stdClass|string $entry
1939 *
1940 * @return string
1941 */
1942 public static function filter_content( $content, $form, $entry = false ) {
1943 $content = self::replace_form_name_shortcodes( $content, $form );
1944
1945 self::get_entry_by_param( $entry );
1946
1947 if ( ! $entry ) {
1948 return $content;
1949 }
1950
1951 if ( is_object( $form ) ) {
1952 $form = $form->id;
1953 }
1954
1955 /*
1956 * Repeater actions adds `parent_entry` to `$entry` store the parent entry data. If `parent_entry` is not empty,
1957 * use the parent form ID instead of repeater form ID to fix the parent form field shortcodes doesn't work.
1958 */
1959 if ( ! empty( $entry->parent_entry ) ) {
1960 $form = $entry->parent_entry->form_id;
1961 }
1962
1963 $shortcodes = FrmFieldsHelper::get_shortcodes( $content, $form );
1964
1965 return apply_filters( 'frm_replace_content_shortcodes', $content, $entry, $shortcodes );
1966 }
1967
1968 /**
1969 * Replace any [form_name] shortcodes in a string.
1970 *
1971 * @since 5.5
1972 *
1973 * @param array|string $string
1974 * @param int|stdClass|string $form
1975 *
1976 * @return array|string
1977 */
1978 public static function replace_form_name_shortcodes( $string, $form ) {
1979 if ( ! is_string( $string ) ) {
1980 return $string;
1981 }
1982
1983 if ( ! str_contains( $string, '[form_name]' ) ) {
1984 return $string;
1985 }
1986
1987 if ( ! is_object( $form ) ) {
1988 $form = FrmForm::getOne( $form );
1989 }
1990
1991 $form_name = is_object( $form ) ? $form->name : '';
1992 return str_replace( '[form_name]', $form_name, $string );
1993 }
1994
1995 /**
1996 * @param false|int|stdClass|string $entry
1997 *
1998 * @return void
1999 */
2000 private static function get_entry_by_param( &$entry ) {
2001 if ( $entry && is_object( $entry ) ) {
2002 return;
2003 }
2004
2005 if ( ! $entry || ! is_numeric( $entry ) ) {
2006 $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
2007 }
2008
2009 FrmEntry::maybe_get_entry( $entry );
2010 }
2011
2012 /**
2013 * @param string $content
2014 * @param false|object $entry
2015 * @param array $shortcodes
2016 *
2017 * @return string
2018 */
2019 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
2020 return FrmFieldsHelper::replace_content_shortcodes( $content, $entry, $shortcodes );
2021 }
2022
2023 /**
2024 * @param array $errors
2025 *
2026 * @return array
2027 */
2028 public static function process_bulk_form_actions( $errors ) {
2029 if ( ! $_REQUEST ) {
2030 return $errors;
2031 }
2032
2033 $bulkaction = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
2034
2035 // phpcs:ignore Universal.Operators.StrictComparisons
2036 if ( $bulkaction == - 1 ) {
2037 $bulkaction = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
2038 }
2039
2040 if ( $bulkaction && str_starts_with( $bulkaction, 'bulk_' ) ) {
2041 FrmAppHelper::remove_get_action();
2042 $bulkaction = str_replace( 'bulk_', '', $bulkaction );
2043 }
2044
2045 $ids = FrmAppHelper::get_param( 'item-action', '', 'get', 'sanitize_text_field' );
2046
2047 if ( ! $ids ) {
2048 $errors[] = __( 'No forms were specified', 'formidable' );
2049 return $errors;
2050 }
2051
2052 $permission_error = FrmAppHelper::permission_nonce_error( '', '_wpnonce', 'bulk-toplevel_page_formidable' );
2053
2054 if ( $permission_error !== false ) {
2055 $errors[] = $permission_error;
2056 return $errors;
2057 }
2058
2059 if ( ! is_array( $ids ) ) {
2060 $ids = explode( ',', $ids );
2061 }
2062
2063 switch ( $bulkaction ) {
2064 case 'delete':
2065 $message = self::bulk_destroy( $ids );
2066 break;
2067 case 'trash':
2068 $message = self::bulk_trash( $ids );
2069 break;
2070 case 'untrash':
2071 $message = self::bulk_untrash( $ids );
2072 }
2073
2074 if ( ! empty( $message ) ) {
2075 $errors['message'] = $message;
2076 }
2077
2078 return $errors;
2079 }
2080
2081 /**
2082 * Includes html that shows a message when the device is too small to use Formidable Forms admin pages.
2083 *
2084 * @since 6.20
2085 *
2086 * @return void
2087 */
2088 public static function include_device_too_small_message() {
2089 if ( ! FrmAppHelper::is_formidable_admin() ) {
2090 return;
2091 }
2092
2093 include FrmAppHelper::plugin_path() . '/classes/views/shared/small-device-message.php';
2094 }
2095
2096 public static function route() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
2097 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
2098 $vars = array();
2099 FrmAppHelper::include_svg();
2100
2101 if ( isset( $_POST['frm_compact_fields'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
2102 FrmAppHelper::permission_check( 'frm_edit_forms' );
2103
2104 // Javascript needs to be allowed in some field settings.
2105 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
2106 $json_vars = htmlspecialchars_decode( nl2br( str_replace( '&quot;', '"', wp_unslash( $_POST['frm_compact_fields'] ) ) ) );
2107 $json_vars = json_decode( $json_vars, true );
2108
2109 if ( $json_vars ) {
2110 $vars = FrmAppHelper::json_to_array( $json_vars );
2111 $action = $vars[ $action ];
2112 unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2113 $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2114 $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2115 } else {
2116 // Json decoding failed so we should return an error message.
2117 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
2118
2119 if ( 'edit' === $action ) {
2120 $action = 'update';
2121 }
2122
2123 add_filter( 'frm_validate_form', 'FrmFormsController::json_error' );
2124 }
2125 } else {
2126 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
2127
2128 if ( isset( $_REQUEST['delete_all'] ) ) {
2129 // Override the action for this page.
2130 $action = 'delete_all';
2131 }
2132 }//end if
2133
2134 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
2135 FrmAppHelper::trigger_hook_load( 'form' );
2136
2137 switch ( $action ) {
2138 case 'create':
2139 case 'edit':
2140 case 'update':
2141 case 'trash':
2142 case 'untrash':
2143 case 'destroy':
2144 case 'settings':
2145 case 'update_settings':
2146 return self::$action( $vars );
2147 case 'lite-reports':
2148 self::no_reports( $vars );
2149 return null;
2150 case 'views':
2151 self::no_views( $vars );
2152 return null;
2153 default:
2154 do_action( 'frm_form_action_' . $action );
2155
2156 if ( apply_filters( 'frm_form_stop_action_' . $action, false ) ) {
2157 return null;
2158 }
2159
2160 $action = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
2161
2162 // phpcs:ignore Universal.Operators.StrictComparisons
2163 if ( $action == - 1 ) {
2164 $action = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
2165 }
2166
2167 if ( str_starts_with( $action, 'bulk_' ) ) {
2168 FrmAppHelper::remove_get_action();
2169
2170 self::list_form();
2171 return null;
2172 }
2173
2174 $message = FrmAppHelper::get_param( 'message' );
2175
2176 if ( 'form_duplicate_error' === $message ) {
2177 self::display_forms_list( array(), '', array( __( 'There was a problem duplicating the form', 'formidable' ) ) );
2178 return null;
2179 }
2180
2181 if ( 'forms_permanently_deleted' === $message ) {
2182 $count = FrmAppHelper::get_param( 'forms_deleted', 0, 'get', 'absint' );
2183 /* translators: %1$s: Number of forms */
2184 $message = sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
2185 self::display_forms_list( array(), $message, '' );
2186 return null;
2187 }
2188
2189 self::display_forms_list();
2190
2191 return null;
2192 }//end switch
2193 }
2194
2195 /**
2196 * Rename a form.
2197 *
2198 * Handles the AJAX request for renaming a form.
2199 *
2200 * @since 6.7
2201 *
2202 * @return void
2203 */
2204 public static function rename_form() {
2205 // Check permission and nonce
2206 FrmAppHelper::permission_check( 'frm_edit_forms' );
2207 check_ajax_referer( 'frm_ajax', 'nonce' );
2208
2209 // Get posted data
2210 $form_id = FrmAppHelper::get_post_param( 'form_id', '', 'absint' );
2211 $name = FrmAppHelper::get_post_param( 'form_name', '', 'sanitize_text_field' );
2212 $form = FrmForm::getOne( $form_id );
2213
2214 if ( ! $form ) {
2215 wp_send_json_error( __( 'Form not found', 'formidable' ) );
2216 }
2217
2218 if ( $name === $form->name ) {
2219 // Nothing to change so exit early.
2220 wp_send_json_success();
2221 }
2222
2223 $to_update = array(
2224 'name' => $name,
2225 );
2226
2227 if ( '' === $name ) {
2228 $form_key = $form->form_key;
2229 } else {
2230 // Only update form_key if name is not empty.
2231 $form_key = FrmAppHelper::get_unique_key( sanitize_title( $name ), 'frm_forms', 'form_key' );
2232 $to_update['form_key'] = $form_key;
2233 }
2234
2235 FrmForm::update( $form_id, $to_update );
2236
2237 wp_send_json_success( compact( 'form_key' ) );
2238 }
2239
2240 /**
2241 * @param array $errors
2242 *
2243 * @return array
2244 */
2245 public static function json_error( $errors ) {
2246 $errors['json'] = __( 'Abnormal HTML characters prevented your form from saving correctly', 'formidable' );
2247 return $errors;
2248 }
2249
2250 /**
2251 * Add education about views.
2252 *
2253 * @since 4.07
2254 *
2255 * @param array $values
2256 *
2257 * @return void
2258 */
2259 public static function no_views( $values = array() ) {
2260 FrmAppHelper::include_svg();
2261 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
2262 $form = $id ? FrmForm::getOne( $id ) : false;
2263
2264 include FrmAppHelper::plugin_path() . '/classes/views/shared/views-info.php';
2265 }
2266
2267 /**
2268 * Add education about reports.
2269 *
2270 * @since 4.07
2271 *
2272 * @param array $values
2273 *
2274 * @return void
2275 */
2276 public static function no_reports( $values = array() ) {
2277 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
2278 $form = $id ? FrmForm::getOne( $id ) : false;
2279
2280 include FrmAppHelper::plugin_path() . '/classes/views/shared/reports-info.php';
2281 }
2282
2283 /**
2284 * FRONT-END FORMS.
2285 */
2286 public static function admin_bar_css() {
2287 if ( is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
2288 return;
2289 }
2290
2291 self::move_menu_to_footer();
2292
2293 add_action( 'wp_before_admin_bar_render', 'FrmFormsController::admin_bar_configure' );
2294 FrmAppHelper::load_font_style();
2295 }
2296
2297 /**
2298 * @since 4.05.02
2299 */
2300 private static function move_menu_to_footer() {
2301 $settings = FrmAppHelper::get_settings();
2302
2303 if ( empty( $settings->admin_bar ) ) {
2304 remove_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
2305 }
2306 }
2307
2308 public static function admin_bar_configure() {
2309 global $frm_vars;
2310
2311 if ( empty( $frm_vars['forms_loaded'] ) ) {
2312 return;
2313 }
2314
2315 $actions = array();
2316
2317 foreach ( $frm_vars['forms_loaded'] as $form ) {
2318 if ( is_object( $form ) ) {
2319 $actions[ $form->id ] = $form->name;
2320 }
2321 unset( $form );
2322 }
2323
2324 if ( ! $actions ) {
2325 return;
2326 }
2327
2328 self::add_menu_to_admin_bar();
2329 self::add_forms_to_admin_bar( $actions );
2330 }
2331
2332 /**
2333 * @since 2.05.07
2334 */
2335 public static function add_menu_to_admin_bar() {
2336 global $wp_admin_bar;
2337
2338 $wp_admin_bar->add_node(
2339 array(
2340 'id' => 'frm-forms',
2341 'title' => '<span class="ab-icon"></span><span class="ab-label">' . FrmAppHelper::get_menu_name() . '</span>',
2342 'href' => admin_url( 'admin.php?page=formidable' ),
2343 'meta' => array(
2344 'title' => FrmAppHelper::get_menu_name(),
2345 ),
2346 )
2347 );
2348 }
2349
2350 /**
2351 * @since 2.05.07
2352 *
2353 * @param array $actions
2354 *
2355 * @return void
2356 */
2357 private static function add_forms_to_admin_bar( $actions ) {
2358 global $wp_admin_bar;
2359
2360 asort( $actions );
2361
2362 foreach ( $actions as $form_id => $name ) {
2363 $wp_admin_bar->add_node(
2364 array(
2365 'parent' => 'frm-forms',
2366 'id' => 'edit_form_' . $form_id,
2367 'title' => $name ? $name : FrmFormsHelper::get_no_title_text(),
2368 'href' => FrmForm::get_edit_link( $form_id ),
2369 )
2370 );
2371 }
2372 }
2373
2374 /**
2375 * The formidable shortcode
2376 *
2377 * @param array $atts The params from the shortcode.
2378 *
2379 * @return string
2380 */
2381 public static function get_form_shortcode( $atts ) {
2382 global $frm_vars;
2383
2384 if ( ! empty( $frm_vars['skip_shortcode'] ) ) {
2385 $sc = '[formidable';
2386 $sc .= FrmAppHelper::array_to_html_params( $atts );
2387 return $sc . ']';
2388 }
2389
2390 $shortcode_atts = shortcode_atts(
2391 array(
2392 'id' => '',
2393 'key' => '',
2394 'title' => 'auto',
2395 'description' => 'auto',
2396 'readonly' => false,
2397 'entry_id' => false,
2398 'fields' => array(),
2399 'exclude_fields' => array(),
2400 'minimize' => false,
2401 ),
2402 $atts
2403 );
2404 do_action( 'formidable_shortcode_atts', $shortcode_atts, $atts );
2405
2406 return self::show_form( $shortcode_atts['id'], $shortcode_atts['key'], $shortcode_atts['title'], $shortcode_atts['description'], $atts );
2407 }
2408
2409 /**
2410 * @since 5.2.01
2411 *
2412 * @param false|int|string $id
2413 * @param false|string $key
2414 *
2415 * @return false|stdClass
2416 */
2417 private static function maybe_get_form_by_id_or_key( $id, $key ) {
2418 if ( ! $id ) {
2419 $id = $key;
2420 }
2421 return self::maybe_get_form_to_show( $id );
2422 }
2423
2424 /**
2425 * @param false|int|string $id
2426 * @param false|string $key
2427 * @param bool|int|string $title may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
2428 * @param bool|int|string $description may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
2429 * @param array $atts
2430 *
2431 * @return string
2432 */
2433 public static function show_form( $id = '', $key = '', $title = false, $description = false, $atts = array() ) {
2434 $form = self::maybe_get_form_by_id_or_key( $id, $key );
2435
2436 if ( ! $form ) {
2437 return __( 'Please select a valid form', 'formidable' );
2438 }
2439
2440 if ( 'auto' === $title ) {
2441 $title = ! empty( $form->options['show_title'] );
2442 }
2443
2444 if ( 'auto' === $description ) {
2445 $description = ! empty( $form->options['show_description'] );
2446 }
2447
2448 FrmAppController::maybe_update_styles();
2449
2450 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
2451 FrmAppHelper::trigger_hook_load( 'form', $form );
2452
2453 $form = apply_filters( 'frm_pre_display_form', $form );
2454 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
2455
2456 if ( self::is_viewable_draft_form( $form ) ) {
2457 // Don't show a draft form on a page
2458 $form = __( 'Please select a valid form', 'formidable' );
2459 } elseif ( ! FrmForm::is_visible_to_user( $form ) ) {
2460 $form = do_shortcode( $frm_settings->login_msg );
2461 } else {
2462 do_action( 'frm_pre_get_form', $form );
2463 $form = self::get_form( $form, $title, $description, $atts );
2464
2465 /**
2466 * Use this shortcode to check for external shortcodes that may span
2467 * across multiple fields in the customizable HTML
2468 *
2469 * @since 2.0.8
2470 */
2471 $form = apply_filters( 'frm_filter_final_form', $form );
2472 }
2473
2474 return $form;
2475 }
2476
2477 /**
2478 * @param false|int|string $id
2479 *
2480 * @return false|stdClass
2481 */
2482 private static function maybe_get_form_to_show( $id ) {
2483 $form = false;
2484
2485 if ( $id ) {
2486 // Form id or key is set.
2487 $form = FrmForm::getOne( $id );
2488
2489 if ( ! $form || $form->parent_form_id || $form->status === 'trash' ) {
2490 $form = false;
2491 }
2492 }
2493
2494 return $form;
2495 }
2496
2497 /**
2498 * @param object $form
2499 *
2500 * @return bool
2501 */
2502 private static function is_viewable_draft_form( $form ) {
2503 return $form->status === 'draft' && current_user_can( 'frm_edit_forms' ) && ! FrmAppHelper::is_preview_page();
2504 }
2505
2506 /**
2507 * @param object $form
2508 * @param bool $title
2509 * @param bool $description
2510 * @param array $atts
2511 *
2512 * @return string
2513 */
2514 public static function get_form( $form, $title, $description, $atts = array() ) {
2515 ob_start();
2516
2517 do_action( 'frm_before_get_form', $atts );
2518
2519 self::get_form_contents( $form, $title, $description, $atts );
2520 self::enqueue_scripts( FrmForm::get_params( $form ) );
2521
2522 $contents = ob_get_clean();
2523
2524 self::maybe_minimize_form( $atts, $contents );
2525
2526 return $contents;
2527 }
2528
2529 /**
2530 * @param array $params
2531 *
2532 * @return void
2533 */
2534 public static function enqueue_scripts( $params ) {
2535 do_action( 'frm_enqueue_form_scripts', $params );
2536 }
2537
2538 /**
2539 * @param object $form
2540 * @param bool $title
2541 * @param bool $description
2542 * @param array $atts
2543 *
2544 * @return void
2545 */
2546 public static function get_form_contents( $form, $title, $description, $atts ) {
2547 $params = FrmForm::get_params( $form );
2548 $errors = self::get_saved_errors( $form, $params );
2549 $fields = FrmFieldsHelper::get_form_fields( $form->id, $errors );
2550 $reset = false;
2551 $pass_args = compact( 'form', 'fields', 'errors', 'title', 'description', 'reset' );
2552
2553 $pass_args['action'] = $params['action'];
2554 // phpcs:ignore WordPress.Security.NonceVerification.Missing, Universal.Operators.StrictComparisons
2555 $handle_process_here = $params['action'] === 'create' && $params['posted_form_id'] == $form->id && $_POST;
2556
2557 if ( ! $handle_process_here ) {
2558 FrmFormState::set_initial_value( 'title', $title );
2559 FrmFormState::set_initial_value( 'description', $description );
2560
2561 do_action( 'frm_display_form_action', $params, $fields, $form, $title, $description );
2562
2563 if ( apply_filters( 'frm_continue_to_new', true, $form->id, $params['action'] ) ) {
2564 self::show_form_after_submit( $pass_args );
2565 }
2566 } elseif ( $errors ) {
2567 self::show_form_after_submit( $pass_args );
2568 } else {
2569 do_action( 'frm_validate_form_creation', $params, $fields, $form, $title, $description );
2570
2571 if ( apply_filters( 'frm_continue_to_create', true, $form->id ) ) {
2572 $entry_id = self::just_created_entry( $form->id );
2573 $pass_args['entry_id'] = $entry_id;
2574 $pass_args['reset'] = true;
2575
2576 self::run_on_submit_actions( $pass_args );
2577
2578 do_action(
2579 'frm_after_entry_processed',
2580 array(
2581 'entry_id' => $entry_id,
2582 'form' => $form,
2583 )
2584 );
2585 }
2586 }//end if
2587 }
2588
2589 /**
2590 * If the form was processed earlier (init), get the generated errors
2591 *
2592 * @since 2.05
2593 *
2594 * @param object $form
2595 * @param array $params
2596 *
2597 * @return array
2598 */
2599 private static function get_saved_errors( $form, $params ) {
2600 global $frm_vars;
2601
2602 // phpcs:ignore WordPress.Security.NonceVerification.Missing, Universal.Operators.StrictComparisons
2603 if ( $params['posted_form_id'] == $form->id && $_POST && isset( $frm_vars['created_entries'][ $form->id ] ) ) {
2604 $errors = $frm_vars['created_entries'][ $form->id ]['errors'];
2605 } else {
2606 $errors = array();
2607 }
2608
2609 /**
2610 * Allows modifying the generated errors if the form was processed earlier.
2611 *
2612 * @since 5.2.03
2613 *
2614 * @param array $errors Errors data. Is empty array if no errors found.
2615 * @param array $params Form params. See {@see FrmForm::get_params()}.
2616 */
2617 return apply_filters( 'frm_saved_errors', $errors, $params );
2618 }
2619
2620 /**
2621 * @since 2.2.7
2622 *
2623 * @param int|string $form_id
2624 *
2625 * @return int
2626 */
2627 public static function just_created_entry( $form_id ) {
2628 global $frm_vars;
2629 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
2630 }
2631
2632 /**
2633 * Gets confirmation method.
2634 *
2635 * @since 3.0
2636 * @since 6.0 This method can return an array of met On Submit actions.
2637 *
2638 * @param array $atts {
2639 * Atts.
2640 *
2641 * @type object $form Form object.
2642 * @type int $entry_id Entry ID.
2643 * }
2644 *
2645 * @return array|string
2646 */
2647 private static function get_confirmation_method( $atts ) {
2648 $action = FrmOnSubmitHelper::current_event( $atts );
2649 $opt = 'update' === $action ? 'edit_action' : 'success_action';
2650 $method = ! empty( $atts['form']->options[ $opt ] ) ? $atts['form']->options[ $opt ] : 'message';
2651
2652 if ( ! empty( $atts['entry_id'] ) ) {
2653 $met_actions = self::get_met_on_submit_actions( $atts, $action );
2654
2655 if ( $met_actions ) {
2656 $method = $met_actions;
2657 }
2658 }
2659
2660 $method = apply_filters( 'frm_success_filter', $method, $atts['form'], $action );
2661
2662 if ( $method !== 'message' && ( ! $atts['entry_id'] || ! is_numeric( $atts['entry_id'] ) ) ) {
2663 return 'message';
2664 }
2665
2666 return $method;
2667 }
2668
2669 /**
2670 * @param object $form
2671 * @param array $params
2672 * @param array $args
2673 *
2674 * @return void
2675 */
2676 public static function maybe_trigger_redirect( $form, $params, $args ) {
2677 if ( ! isset( $params['id'] ) ) {
2678 global $frm_vars;
2679 $params['id'] = $frm_vars['created_entries'][ $form->id ]['entry_id'];
2680 }
2681
2682 $conf_method = self::get_confirmation_method(
2683 array(
2684 'form' => $form,
2685 'entry_id' => $params['id'],
2686 'action' => FrmOnSubmitHelper::current_event( $params ),
2687 )
2688 );
2689
2690 self::maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args );
2691 }
2692
2693 /**
2694 * Maybe trigger redirect with the new Confirmation action.
2695 *
2696 * @since 6.1.1
2697 *
2698 * @param array|string $conf_method Array of confirmation actions or the action type string.
2699 * @param object $form Form object.
2700 * @param array $params See {@see FrmFormsController::maybe_trigger_redirect()}.
2701 * @param array $args See {@see FrmFormsController::maybe_trigger_redirect()}.
2702 */
2703 public static function maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args ) {
2704 if ( is_array( $conf_method ) && 1 === count( $conf_method ) && 'redirect' === FrmOnSubmitHelper::get_action_type( $conf_method[0] ) && empty( $conf_method[0]->post_content['redirect_delay'] ) ) { // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
2705 $event = FrmOnSubmitHelper::current_event( $params );
2706 FrmOnSubmitHelper::populate_on_submit_data( $form->options, $conf_method[0], $event );
2707 $conf_method = 'redirect';
2708 }
2709
2710 if ( 'redirect' === $conf_method ) {
2711 self::trigger_redirect( $form, $params, $args );
2712 }
2713 }
2714
2715 /**
2716 * @param object $form
2717 * @param array $params
2718 * @param array $args
2719 *
2720 * @return void
2721 */
2722 public static function trigger_redirect( $form, $params, $args ) {
2723 $success_args = array(
2724 'action' => $params['action'],
2725 'conf_method' => 'redirect',
2726 'form' => $form,
2727 'entry_id' => $params['id'],
2728 );
2729
2730 if ( isset( $args['ajax'] ) ) {
2731 $success_args['ajax'] = $args['ajax'];
2732 }
2733
2734 self::run_success_action( $success_args );
2735 }
2736
2737 /**
2738 * Used when the success action is not 'message'
2739 *
2740 * @since 2.05
2741 * @since 6.0 `$args['force_delay_redirect']` is added.
2742 *
2743 * @param array $args {
2744 * The args.
2745 *
2746 * @type string $conf_method The method.
2747 * @type object $form Form object.
2748 * @type int $entry_id Entry ID.
2749 * @type string $action The action event. Accepts `create` or `update`.
2750 * @type array $fields The array of fields.
2751 * @type int $force_delay_redirect Force to show the message before redirecting in case redirect method runs.
2752 * }
2753 */
2754 public static function run_success_action( $args ) {
2755 global $frm_vars;
2756 $extra_args = $args;
2757 unset( $extra_args['form'] );
2758
2759 do_action( 'frm_success_action', $args['conf_method'], $args['form'], $args['form']->options, $args['entry_id'], $extra_args );
2760
2761 $opt = ! isset( $args['action'] ) || $args['action'] === 'create' ? 'success' : 'edit';
2762
2763 $args['success_opt'] = $opt;
2764 $args['ajax'] = ! empty( $frm_vars['ajax'] );
2765
2766 if ( $args['conf_method'] === 'page' && is_numeric( $args['form']->options[ $opt . '_page_id' ] ) ) {
2767 self::load_page_after_submit( $args );
2768 } elseif ( $args['conf_method'] === 'redirect' ) {
2769 self::redirect_after_submit( $args );
2770 } else {
2771 self::show_message_after_save( $args );
2772 }
2773 }
2774
2775 /**
2776 * Gets met On Submit actions.
2777 *
2778 * @since 6.0
2779 *
2780 * @param array $args See {@see FrmFormsController::run_success_action()}.
2781 * @param string $event Form event. Default is `create`.
2782 *
2783 * @return array Array of actions that meet the conditional logics.
2784 */
2785 public static function get_met_on_submit_actions( $args, $event = 'create' ) {
2786 if ( ! FrmOnSubmitHelper::form_has_migrated( $args['form'] ) ) {
2787 return array();
2788 }
2789
2790 // If a redirect action has already opened the URL in a new tab, we show the default message in the current tab.
2791 if ( ! empty( self::$redirected_in_new_tab[ $args['form']->id ] ) ) {
2792 return array( FrmOnSubmitHelper::get_fallback_action_after_open_in_new_tab( $event ) );
2793 }
2794
2795 $entry = FrmEntry::getOne( $args['entry_id'], true );
2796 $actions = FrmOnSubmitHelper::get_actions( $args['form']->id );
2797 $met_actions = array();
2798 $has_redirect = false;
2799
2800 foreach ( $actions as $action ) {
2801 if ( ! in_array( $event, $action->post_content['event'], true ) ) {
2802 continue;
2803 }
2804
2805 if ( FrmFormAction::action_conditions_met( $action, $entry ) ) {
2806 continue;
2807 }
2808
2809 $action_type = FrmOnSubmitHelper::get_action_type( $action );
2810
2811 if ( 'redirect' === $action_type && $has_redirect ) {
2812 // Do not process because we run the first redirect action only.
2813 continue;
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