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

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