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

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