PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.9.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.9.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / controllers / FrmFormsController.php

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

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