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

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