PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.8.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.8.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
← All changes | classes/controllers/FrmFormsController.php +398 -215 6.36.8.1 View file →
@@ -4,8 +4,18 @@
4 4 }
5 5
6 6 class FrmFormsController {
7 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 +
8 18 public static function menu() {
9 19 $menu_label = __( 'Forms', 'formidable' );
10 20 if ( ! FrmAppHelper::pro_is_installed() ) {
11 21 $menu_label .= ' (Lite)';
@@ -146,9 +156,21 @@
146 156 }
147 157
148 158 public static function update_settings() {
149 159 FrmAppHelper::permission_check( 'frm_edit_forms' );
160 + $process_form = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' );
150 161
162 + if ( ! wp_verify_nonce( $process_form, 'process_form_nonce' ) ) {
163 + $frm_settings = FrmAppHelper::get_settings();
164 + $error_args = array(
165 + 'title' => __( 'Verification failed', 'formidable' ),
166 + 'body' => $frm_settings->admin_permission,
167 + 'cancel_text' => __( 'Cancel', 'formidable' ),
168 + );
169 + FrmAppController::show_error_modal( $error_args );
170 + return;
171 + }
172 +
151 173 $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
152 174
153 175 $errors = FrmForm::validate( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
154 176 $warnings = FrmFormsHelper::check_for_warnings( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
@@ -202,8 +224,10 @@
202 224
203 225 if ( count( $errors ) > 0 ) {
204 226 return self::get_edit_vars( $id, $errors );
205 227 } else {
228 + self::maybe_remove_draft_option_from_fields( $id );
229 +
206 230 FrmForm::update( $id, $values );
207 231 $message = __( 'Form was successfully updated.', 'formidable' );
208 232
209 233 if ( self::is_too_long( $values ) ) {
@@ -219,9 +243,37 @@
219 243 wp_die( FrmAppHelper::kses( $message, array( 'a' ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
220 244 }
221 245
222 246 return self::get_edit_vars( $id, array(), $message );
247 + }//end if
248 + }
249 +
250 + /**
251 + * Remove the draft flag from any new fields from this current session.
252 + *
253 + * @since 6.8
254 + *
255 + * @param int $form_id
256 + * @return void
257 + */
258 + private static function maybe_remove_draft_option_from_fields( $form_id ) {
259 + $draft_field_ids_csv = FrmAppHelper::get_post_param( 'draft_fields', '', 'sanitize_text_field' );
260 + if ( ! $draft_field_ids_csv ) {
261 + // If the draft_fields input is empty there are no new fields in the session.
262 + return;
223 263 }
264 +
265 + $draft_field_ids = array_filter( explode( ',', $draft_field_ids_csv ), 'is_numeric' );
266 + if ( ! $draft_field_ids ) {
267 + // Exit early if the draft fields input is invalid. It should be a CSV of integer values.
268 + return;
269 + }
270 +
271 + $draft_field_rows = FrmFieldsHelper::get_draft_field_results( $form_id, $draft_field_ids );
272 + foreach ( $draft_field_rows as $row ) {
273 + $row->field_options['draft'] = 0;
274 + FrmField::update( $row->id, array( 'field_options' => $row->field_options ) );
275 + }
224 276 }
225 277
226 278 /**
227 279 * Check if the value at the end of the form was included.
@@ -272,9 +324,10 @@
272 324 $url = admin_url( 'admin.php?page=formidable' );
273 325 $message = 'form_duplicate_error';
274 326
275 327 if ( $form ) {
276 - $url = admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . absint( $form ) );
328 + $new_template = FrmAppHelper::simple_get( 'new_template' ) ? '&new_template=true' : '';
329 + $url = admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . absint( $form ) . $new_template );
277 330 $message = 'form_duplicated';
278 331 }
279 332
280 333 $url .= '&message=' . $message;
@@ -299,8 +352,10 @@
299 352 }
300 353
301 354 /**
302 355 * @since 3.0
356 + *
357 + * @return void
303 358 */
304 359 public static function show_page_preview() {
305 360 echo self::page_preview(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
306 361 }
@@ -350,8 +405,13 @@
350 405 return;
351 406 }
352 407
353 408 $random_page = reset( $random_page );
409 + if ( ! is_a( $random_page, 'WP_Post' ) ) {
410 + // The return type can also be int.
411 + return;
412 + }
413 +
354 414 query_posts(
355 415 array(
356 416 'post_type' => 'page',
357 417 'page_id' => $random_page->ID,
@@ -367,9 +427,9 @@
367 427 * Set the WP $post global object. Used for in-theme preview when defining a page.
368 428 *
369 429 * @since 5.5.2
370 430 *
371 - * @param WP_Post $post
431 + * @param WP_Post $page The page object.
372 432 * @return void
373 433 */
374 434 private static function set_post_global( $page ) {
375 435 global $post;
@@ -387,13 +447,29 @@
387 447 add_filter( 'is_active_sidebar', '__return_false' );
388 448 FrmStylesController::enqueue_css( 'enqueue', true );
389 449
390 450 if ( false === get_template_part( 'page' ) ) {
451 + if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
452 + add_filter( 'body_class', 'FrmFormsController::preview_block_theme_body_classnames' );
453 + }
391 454 self::fallback_when_page_template_part_is_not_supported_by_theme();
392 455 }
393 456 }
394 457
395 458 /**
459 + * Add padding to the body for block themes.
460 + *
461 + * @since 6.5.2
462 + *
463 + * @param array $classes The body classes list.
464 + * @return array
465 + */
466 + public static function preview_block_theme_body_classnames( $classes ) {
467 + $classes[] = 'has-global-padding';
468 + return $classes;
469 + }
470 +
471 + /**
396 472 * Not every theme supports get_template_part( 'page' ).
397 473 * When this is not supported, false is returned, and we can handle a fallback.
398 474 */
399 475 private static function fallback_when_page_template_part_is_not_supported_by_theme() {
@@ -398,9 +474,10 @@
398 474 */
399 475 private static function fallback_when_page_template_part_is_not_supported_by_theme() {
400 476 if ( have_posts() ) {
401 477 the_post();
402 - get_header( '' );
478 + self::get_template( 'header' );
479 +
403 480 // add some generic class names to the container to add some natural padding to the content.
404 481 // .entry-content catches the WordPress TwentyTwenty theme.
405 482 // .container catches Customizr content.
406 483 echo '<div class="container entry-content">';
@@ -405,13 +482,54 @@
405 482 // .container catches Customizr content.
406 483 echo '<div class="container entry-content">';
407 484 the_content();
408 485 echo '</div>';
409 - get_footer();
486 +
487 + self::get_template( 'footer' );
410 488 }
411 489 }
412 490
413 491 /**
492 + * Calls core function to get a template part if it doesn't cause deprecation warnings. Otherwise skips the deprecation function call
493 + * and renders required html fragements calling required functions.
494 + *
495 + * @since 6.7.1
496 + * @param string $template
497 + * @return void
498 + */
499 + private static function get_template( $template ) {
500 + if ( self::should_try_getting_template( $template ) ) {
501 + call_user_func( 'get_' . $template );
502 + return;
503 + }
504 +
505 + if ( 'header' === $template ) {
506 + include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/preview/header.php';
507 + return;
508 + }
509 +
510 + if ( 'footer' === $template ) {
511 + include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/preview/footer.php';
512 + }
513 + }
514 +
515 + /**
516 + * Returns true if calling a template function doesn't trigger deprecation warnings.
517 + *
518 + * @since 6.7.1
519 + * @param string $template
520 + * @return bool
521 + */
522 + private static function should_try_getting_template( $template ) {
523 + $stylesheet_path = get_stylesheet_directory();
524 + $template_path = get_template_directory();
525 + $is_child_theme = $stylesheet_path !== $template_path;
526 + $template_name = $template . '.php';
527 +
528 + return file_exists( $stylesheet_path . '/' . $template_name ) || $is_child_theme && file_exists( $template_path . '/' . $template_name );
529 + }
530 +
531 + /**
414 532 * Set the page title for the theme preview page
415 533 *
416 534 * @since 3.0
417 535 */
@@ -432,15 +550,20 @@
432 550 return __( 'Form Preview', 'formidable' );
433 551 }
434 552
435 553 /**
436 - * Set the page content for the theme preview page
554 + * Set the page content for the theme preview page.
437 555 *
438 556 * @since 3.0
557 + *
558 + * @param string $content
559 + * @return string
439 560 */
440 561 public static function preview_content( $content ) {
441 562 if ( in_the_loop() ) {
442 - $content = self::show_page_preview();
563 + self::show_page_preview();
564 + // Clear the content for the page we're using.
565 + $content = '';
443 566 }
444 567
445 568 return $content;
446 569 }
@@ -527,9 +650,9 @@
527 650 FrmAppHelper::permission_check( $available_status[ $status ]['permission'] );
528 651
529 652 $params = FrmForm::list_page_params();
530 653
531 - //check nonce url
654 + // Check nonce url.
532 655 check_admin_referer( $status . '_form_' . $params['id'] );
533 656
534 657 $count = 0;
535 658 if ( FrmForm::set_status( $params['id'], $available_status[ $status ]['new_status'] ) ) {
@@ -610,15 +733,20 @@
610 733 $count ++;
611 734 }
612 735 }
613 736
614 - /* translators: %1$s: Number of forms */
615 - $message = sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
737 + if ( $count ) {
738 + /* translators: %1$s: Number of forms */
739 + return sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
740 + }
616 741
617 - return $message;
742 + return '';
618 743 }
619 744
620 - private static function delete_all() {
745 + /**
746 + * @since 6.7.1 Function went from private to public.
747 + */
748 + public static function delete_all() {
621 749 // Check nonce url.
622 750 $permission_error = FrmAppHelper::permission_nonce_error( 'frm_delete_forms', '_wpnonce', 'bulk-toplevel_page_formidable' );
623 751 if ( $permission_error !== false ) {
624 752 self::display_forms_list( array(), '', array( $permission_error ) );
@@ -625,14 +753,15 @@
625 753
626 754 return;
627 755 }
628 756
629 - $count = FrmForm::scheduled_delete( time() );
757 + $count = FrmForm::scheduled_delete( time() );
758 + $url = remove_query_arg( array( 'delete_all' ) );
630 759
631 - /* translators: %1$s: Number of forms */
632 - $message = sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
760 + $url .= '&message=forms_permanently_deleted&forms_deleted=' . $count;
633 761
634 - self::display_forms_list( array(), $message );
762 + wp_safe_redirect( $url );
763 + die();
635 764 }
636 765
637 766 /**
638 767 * Create a new form from the modal.
@@ -670,9 +799,9 @@
670 799 self::create_default_on_submit_action( $form_id );
671 800 self::create_default_email_action( $form_id );
672 801
673 802 $response = array(
674 - 'redirect' => FrmForm::get_edit_link( $form_id ),
803 + 'redirect' => FrmForm::get_edit_link( $form_id ) . '&new_template=true',
675 804 );
676 805
677 806 echo wp_json_encode( $response );
678 807 wp_die();
@@ -678,46 +807,13 @@
678 807 wp_die();
679 808 }
680 809
681 810 /**
682 - * Create a custom template from a form
683 - *
684 - * @since 3.06
685 - */
686 - public static function build_template() {
687 - global $wpdb;
688 -
689 - FrmAppHelper::permission_check( 'frm_edit_forms' );
690 - check_ajax_referer( 'frm_ajax', 'nonce' );
691 -
692 - $form_id = FrmAppHelper::get_param( 'xml', '', 'post', 'absint' );
693 - $new_form_id = FrmForm::duplicate( $form_id, 1, true );
694 - if ( ! $new_form_id ) {
695 - $response = array(
696 - 'message' => __( 'There was an error creating a template.', 'formidable' ),
697 - );
698 - } else {
699 - $new_values = self::get_modal_values();
700 - $query_results = $wpdb->update( $wpdb->prefix . 'frm_forms', $new_values, array( 'id' => $new_form_id ) );
701 - if ( $query_results ) {
702 - FrmForm::clear_form_cache();
703 - }
704 -
705 - $response = array(
706 - 'redirect' => admin_url( 'admin.php?page=formidable&frm_action=duplicate&id=' . $new_form_id ) . '&_wpnonce=' . wp_create_nonce(),
707 - );
708 - }
709 -
710 - echo wp_json_encode( $response );
711 - wp_die();
712 - }
713 -
714 - /**
715 811 * Before creating a new form, get the name and description from the modal.
716 812 *
717 813 * @since 4.0
718 814 */
719 - private static function get_modal_values() {
815 + public static function get_modal_values() {
720 816 $name = FrmAppHelper::get_param( 'name', '', 'post', 'sanitize_text_field' );
721 817 $desc = FrmAppHelper::get_param( 'desc', '', 'post', 'sanitize_textarea_field' );
722 818
723 819 return array(
@@ -879,14 +975,14 @@
879 975 * @return array<string,string>
880 976 */
881 977 public static function get_columns( $columns ) {
882 978 $columns['cb'] = '<input type="checkbox" />';
883 - $columns['name'] = __( 'Form Title', 'formidable' );
884 - $columns['entries'] = __( 'Entries', 'formidable' );
979 + $columns['name'] = esc_html__( 'Form Title', 'formidable' );
980 + $columns['entries'] = esc_html__( 'Entries', 'formidable' );
885 981 $columns['id'] = 'ID';
886 - $columns['form_key'] = __( 'Key', 'formidable' );
887 - $columns['shortcode'] = __( 'Actions', 'formidable' );
888 - $columns['created_at'] = __( 'Date', 'formidable' );
982 + $columns['form_key'] = esc_html__( 'Key', 'formidable' );
983 + $columns['shortcode'] = esc_html__( 'Actions', 'formidable' );
984 + $columns['created_at'] = esc_html__( 'Date', 'formidable' );
889 985
890 986 add_screen_option(
891 987 'per_page',
892 988 array(
@@ -940,147 +1036,43 @@
940 1036
941 1037 return $save;
942 1038 }
943 1039
944 - /**
945 - * @return bool
946 - */
947 - public static function expired() {
948 - global $frm_expired;
949 - return $frm_expired;
950 - }
1040 + private static function get_edit_vars( $id, $errors = array(), $message = '', $create_link = false ) {
1041 + global $frm_vars;
951 1042
952 - /**
953 - * Get data from api before rendering it so that we can flag the modal as expired
954 - *
955 - * @return void
956 - */
957 - public static function before_list_templates() {
958 - global $frm_templates;
959 - global $frm_expired;
960 - global $frm_license_type;
961 -
962 - $api = new FrmFormTemplateApi();
963 - $frm_templates = $api->get_api_info();
964 - $expired = false;
965 - $license_type = '';
966 - if ( isset( $frm_templates['error'] ) ) {
967 - $error = $frm_templates['error']['message'];
968 - $error = str_replace( 'utm_medium=addons', 'utm_medium=form-templates', $error );
969 - $expired = 'expired' === $frm_templates['error']['code'];
970 - $license_type = isset( $frm_templates['error']['type'] ) ? $frm_templates['error']['type'] : '';
971 - unset( $frm_templates['error'] );
972 - }
973 -
974 - $frm_expired = $expired;
975 - $frm_license_type = $license_type;
976 - }
977 -
978 - /**
979 - * @return void
980 - */
981 - public static function list_templates() {
982 - global $frm_templates;
983 - global $frm_license_type;
984 -
985 - $templates = $frm_templates;
986 - $custom_templates = array();
987 - $templates_by_category = array();
988 -
989 - self::add_user_templates( $custom_templates );
990 -
991 - foreach ( $templates as $template ) {
992 - if ( ! isset( $template['categories'] ) ) {
993 - continue;
994 - }
995 -
996 - foreach ( $template['categories'] as $category ) {
997 - if ( ! isset( $templates_by_category[ $category ] ) ) {
998 - $templates_by_category[ $category ] = array();
999 - }
1000 -
1001 - $templates_by_category[ $category ][] = $template;
1002 - }
1003 - }
1004 - unset( $template );
1005 -
1006 - // Subcategories that are included elsewhere.
1007 - $redundant_cats = array( 'PayPal', 'Stripe', 'Twilio' );
1008 -
1009 - $categories = array_keys( $templates_by_category );
1010 - $categories = array_diff( $categories, FrmFormsHelper::ignore_template_categories() );
1011 - $categories = array_diff( $categories, $redundant_cats );
1012 - sort( $categories );
1013 -
1014 - array_walk(
1015 - $custom_templates,
1016 - function( &$template ) {
1017 - $template['custom'] = true;
1018 - }
1043 + $form = FrmForm::getOne( $id );
1044 + $error_args = array(
1045 + 'title' => __( 'You can\'t edit the form', 'formidable' ),
1046 + 'body' => __( 'You are trying to edit a form that does not exist', 'formidable' ),
1047 + 'cancel_url' => admin_url( 'admin.php?page=formidable' ),
1048 + 'continue_url' => add_query_arg(
1049 + array(
1050 + 'page' => 'formidable',
1051 + )
1052 + ),
1019 1053 );
1020 -
1021 - $my_templates_translation = __( 'My Templates', 'formidable' );
1022 - $categories = array_merge( array( $my_templates_translation ), $categories );
1023 - $pricing = FrmAppHelper::admin_upgrade_link( 'form-templates' );
1024 - $license_type = $frm_license_type;
1025 - $args = compact( 'pricing', 'license_type' );
1026 - $where = apply_filters( 'frm_forms_dropdown', array(), '' );
1027 - $forms = FrmForm::get_published_forms( $where );
1028 - $view_path = FrmAppHelper::plugin_path() . '/classes/views/frm-forms/';
1029 -
1030 - $templates_by_category[ $my_templates_translation ] = $custom_templates;
1031 -
1032 - unset( $pricing, $license_type, $where );
1033 - wp_enqueue_script( 'accordion' ); // register accordion for template groups
1034 - require $view_path . 'list-templates.php';
1035 - }
1036 -
1037 - /**
1038 - * @since 4.03.01
1039 - */
1040 - private static function get_template_categories( $templates ) {
1041 - $categories = array();
1042 - foreach ( $templates as $template ) {
1043 - if ( isset( $template['categories'] ) ) {
1044 - $categories = array_merge( $categories, $template['categories'] );
1045 - }
1054 + if ( ! $form ) {
1055 + FrmAppController::show_error_modal( $error_args );
1056 + return;
1046 1057 }
1047 - $exclude_cats = FrmFormsHelper::ignore_template_categories();
1048 - $categories = array_unique( $categories );
1049 - $categories = array_diff( $categories, $exclude_cats );
1050 - sort( $categories );
1051 - return $categories;
1052 - }
1053 1058
1054 - private static function add_user_templates( &$templates ) {
1055 - $user_templates = array(
1056 - 'is_template' => 1,
1057 - 'default_template' => 0,
1058 - );
1059 - $user_templates = FrmForm::getAll( $user_templates, 'name' );
1060 - foreach ( $user_templates as $template ) {
1061 - $template = array(
1062 - 'id' => $template->id,
1063 - 'name' => $template->name,
1064 - 'key' => $template->form_key,
1065 - 'description' => $template->description,
1066 - 'url' => wp_nonce_url( admin_url( 'admin.php?page=formidable&frm_action=duplicate&id=' . absint( $template->id ) ) ),
1067 - 'released' => $template->created_at,
1068 - 'installed' => 1,
1059 + if ( 'trash' === $form->status ) {
1060 + $error_args['body'] = __( 'The form you\'re trying to edit is in trash. You must restore it first before you can make changes', 'formidable' );
1061 + $error_args['continue_url'] = add_query_arg(
1062 + array(
1063 + 'page' => 'formidable',
1064 + '_wpnonce' => wp_create_nonce( 'untrash_form_' . $id ),
1065 + 'form_type' => 'trash',
1066 + 'frm_action' => 'untrash',
1067 + 'id' => $id,
1068 + )
1069 1069 );
1070 - array_unshift( $templates, $template );
1071 - unset( $template );
1070 + $error_args['continue_text'] = __( 'Restore form', 'formidable' );
1071 + FrmAppController::show_error_modal( $error_args );
1072 + return;
1072 1073 }
1073 - }
1074 1074
1075 - private static function get_edit_vars( $id, $errors = array(), $message = '', $create_link = false ) {
1076 - global $frm_vars;
1077 -
1078 - $form = FrmForm::getOne( $id );
1079 - if ( ! $form ) {
1080 - wp_die( esc_html__( 'You are trying to edit a form that does not exist.', 'formidable' ) );
1081 - }
1082 -
1083 1075 if ( $form->parent_form_id ) {
1084 1076 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
1085 1077 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>' ) );
1086 1078 }
@@ -1123,11 +1115,11 @@
1123 1115 $has_fields = isset( $values['fields'] ) && ! empty( $values['fields'] );
1124 1116
1125 1117 if ( defined( 'DOING_AJAX' ) ) {
1126 1118 wp_die();
1127 - } else {
1128 - require( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/edit.php' );
1129 1119 }
1120 +
1121 + require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/edit.php';
1130 1122 }
1131 1123
1132 1124 public static function update_form_builder_fields( $fields, $form ) {
1133 1125 foreach ( $fields as $field ) {
@@ -1259,8 +1251,21 @@
1259 1251 'screenshot' => 'chat.png',
1260 1252 )
1261 1253 ),
1262 1254 ),
1255 + 'abandonment' => array(
1256 + 'name' => __( 'Form Abandonment', 'formidable' ),
1257 + 'icon' => 'frm_icon_font frm_abandoned_icon',
1258 + 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1259 + 'data' => FrmAppHelper::get_upgrade_data_params(
1260 + 'abandonment',
1261 + array(
1262 + 'upgrade' => __( 'Form abandonment settings', 'formidable' ),
1263 + 'message' => __( 'Unlock the power of data capture to boost lead generation and master the art of form optimization.', 'formidable' ),
1264 + 'screenshot' => 'abandonment.png',
1265 + )
1266 + ),
1267 + ),
1263 1268 'html' => array(
1264 1269 'name' => __( 'Customize HTML', 'formidable' ),
1265 1270 'class' => __CLASS__,
1266 1271 'function' => 'html_settings',
@@ -1267,9 +1272,9 @@
1267 1272 'icon' => 'frm_icon_font frm_code_icon',
1268 1273 ),
1269 1274 );
1270 1275
1271 - foreach ( array( 'landing', 'chat' ) as $feature ) {
1276 + foreach ( array( 'landing', 'chat', 'abandonment' ) as $feature ) {
1272 1277 if ( ! FrmAppHelper::show_new_feature( $feature ) ) {
1273 1278 unset( $sections[ $feature ] );
1274 1279 }
1275 1280 }
@@ -1303,9 +1308,9 @@
1303 1308 $section['id'] = $section['anchor'];
1304 1309 }
1305 1310
1306 1311 $sections[ $key ] = $section;
1307 - }
1312 + }//end foreach
1308 1313
1309 1314 return $sections;
1310 1315 }
1311 1316
@@ -1605,13 +1610,17 @@
1605 1610 * Replace any [form_name] shortcodes in a string.
1606 1611 *
1607 1612 * @since 5.5
1608 1613 *
1609 - * @param string $string
1614 + * @param string|array $string
1610 1615 * @param stdClass|string|int $form
1611 - * @return string
1616 + * @return string|array
1612 1617 */
1613 1618 public static function replace_form_name_shortcodes( $string, $form ) {
1619 + if ( ! is_string( $string ) ) {
1620 + return $string;
1621 + }
1622 +
1614 1623 if ( false === strpos( $string, '[form_name]' ) ) {
1615 1624 return $string;
1616 1625 }
1617 1626
@@ -1725,9 +1734,9 @@
1725 1734 if ( isset( $_REQUEST['delete_all'] ) ) {
1726 1735 // Override the action for this page.
1727 1736 $action = 'delete_all';
1728 1737 }
1729 - }
1738 + }//end if
1730 1739
1731 1740 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1732 1741 FrmAppHelper::trigger_hook_load( 'form' );
1733 1742
@@ -1739,9 +1748,8 @@
1739 1748 case 'update':
1740 1749 case 'trash':
1741 1750 case 'untrash':
1742 1751 case 'destroy':
1743 - case 'delete_all':
1744 1752 case 'settings':
1745 1753 case 'update_settings':
1746 1754 return self::$action( $vars );
1747 1755 case 'lite-reports':
@@ -1771,14 +1779,47 @@
1771 1779 self::display_forms_list( array(), '', array( __( 'There was a problem duplicating the form', 'formidable' ) ) );
1772 1780 return;
1773 1781 }
1774 1782
1783 + if ( 'forms_permanently_deleted' === $message ) {
1784 + $count = FrmAppHelper::get_param( 'forms_deleted', 0, 'get', 'absint' );
1785 + /* translators: %1$s: Number of forms */
1786 + $message = sprintf( _n( '%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'formidable' ), $count );
1787 + self::display_forms_list( array(), $message, '' );
1788 + return;
1789 + }
1790 +
1775 1791 self::display_forms_list();
1776 1792
1777 1793 return;
1778 - }
1794 + }//end switch
1779 1795 }
1780 1796
1797 + /**
1798 + * Rename a form.
1799 + *
1800 + * Handles the AJAX request for renaming a form.
1801 + *
1802 + * @since 6.7
1803 + *
1804 + * @return void
1805 + */
1806 + public static function rename_form() {
1807 + // Check permission and nonce
1808 + FrmAppHelper::permission_check( 'frm_edit_forms' );
1809 + check_ajax_referer( 'frm_ajax', 'nonce' );
1810 +
1811 + // Get posted data
1812 + $form_id = FrmAppHelper::get_post_param( 'form_id', '', 'absint' );
1813 + $name = FrmAppHelper::get_post_param( 'form_name', '', 'sanitize_text_field' );
1814 +
1815 + // Update the form name and form key.
1816 + $form_key = FrmAppHelper::get_unique_key( sanitize_title( $name ), 'frm_forms', 'form_key' );
1817 + FrmForm::update( $form_id, compact( 'name', 'form_key' ) );
1818 +
1819 + wp_send_json_success( compact( 'form_key' ) );
1820 + }
1821 +
1781 1822 public static function json_error( $errors ) {
1782 1823 $errors['json'] = __( 'Abnormal HTML characters prevented your form from saving correctly', 'formidable' );
1783 1824
1784 1825 return $errors;
@@ -1817,9 +1858,11 @@
1817 1858
1818 1859 include FrmAppHelper::plugin_path() . '/classes/views/shared/reports-info.php';
1819 1860 }
1820 1861
1821 - /* FRONT-END FORMS */
1862 + /**
1863 + * FRONT-END FORMS.
1864 + */
1822 1865 public static function admin_bar_css() {
1823 1866 if ( is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
1824 1867 return;
1825 1868 }
@@ -2007,9 +2050,10 @@
2007 2050 */
2008 2051 private static function maybe_get_form_to_show( $id ) {
2009 2052 $form = false;
2010 2053
2011 - if ( ! empty( $id ) ) { // form id or key is set
2054 + if ( ! empty( $id ) ) {
2055 + // Form id or key is set.
2012 2056 $form = FrmForm::getOne( $id );
2013 2057 if ( ! $form || $form->parent_form_id || $form->status === 'trash' ) {
2014 2058 $form = false;
2015 2059 }
@@ -2081,9 +2125,9 @@
2081 2125 'form' => $form,
2082 2126 )
2083 2127 );
2084 2128 }
2085 - }
2129 + }//end if
2086 2130 }
2087 2131
2088 2132 /**
2089 2133 * If the form was processed earlier (init), get the generated errors
@@ -2261,8 +2305,13 @@
2261 2305 if ( ! FrmOnSubmitHelper::form_has_migrated( $args['form'] ) ) {
2262 2306 return array();
2263 2307 }
2264 2308
2309 + // If a redirect action has already opened the URL in a new tab, we show the default message in the currect tab.
2310 + if ( ! empty( self::$redirected_in_new_tab[ $args['form']->id ] ) ) {
2311 + return array( FrmOnSubmitHelper::get_fallback_action_after_open_in_new_tab( $event ) );
2312 + }
2313 +
2265 2314 $entry = FrmEntry::getOne( $args['entry_id'], true );
2266 2315 $actions = FrmOnSubmitHelper::get_actions( $args['form']->id );
2267 2316 $met_actions = array();
2268 2317 $has_redirect = false;
@@ -2278,9 +2327,10 @@
2278 2327
2279 2328 $action_type = FrmOnSubmitHelper::get_action_type( $action );
2280 2329
2281 2330 if ( 'redirect' === $action_type ) {
2282 - if ( $has_redirect ) { // Do not process because we run the first redirect action only.
2331 + if ( $has_redirect ) {
2332 + // Do not process because we run the first redirect action only.
2283 2333 continue;
2284 2334 }
2285 2335 }
2286 2336
@@ -2293,9 +2343,9 @@
2293 2343 }
2294 2344
2295 2345 $met_actions[] = $action;
2296 2346 unset( $action );
2297 - }
2347 + }//end foreach
2298 2348
2299 2349 $args['event'] = $event;
2300 2350
2301 2351 /**
@@ -2484,9 +2534,9 @@
2484 2534 add_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
2485 2535 }
2486 2536
2487 2537 $post = $old_post;
2488 - }
2538 + }//end if
2489 2539 }
2490 2540
2491 2541 /**
2492 2542 * @since 3.0
@@ -2507,16 +2557,25 @@
2507 2557 $success_url = apply_filters( 'frm_redirect_url', $success_url, $args['form'], $args );
2508 2558
2509 2559 $doing_ajax = FrmAppHelper::doing_ajax();
2510 2560
2511 - if ( ! empty( $args['ajax'] ) && $doing_ajax && empty( $args['force_delay_redirect'] ) ) { // Is AJAX submit and there is just one Redirect action runs.
2512 - echo json_encode( array( 'redirect' => $success_url ) );
2561 + if ( ! empty( $args['ajax'] ) && $doing_ajax && empty( $args['force_delay_redirect'] ) ) {
2562 + // Is AJAX submit and there is just one Redirect action runs.
2563 + echo json_encode( self::get_ajax_redirect_response_data( $args + compact( 'success_url' ) ) );
2513 2564 wp_die();
2514 2565 }
2515 2566
2516 - if ( ! headers_sent() && empty( $args['force_delay_redirect'] ) ) { // Not AJAX submit, no headers sent, and there is just one Redirect action runs.
2567 + if ( ! headers_sent() && empty( $args['force_delay_redirect'] ) ) {
2568 + // Not AJAX submit, no headers sent, and there is just one Redirect action runs.
2569 + if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2570 + self::print_open_in_new_tab_js_with_fallback_handler( $success_url, $args );
2571 + self::$redirected_in_new_tab[ $args['form']->id ] = 1;
2572 + return;
2573 + }
2574 +
2517 2575 wp_redirect( esc_url_raw( $success_url ) );
2518 - die(); // do not use wp_die or redirect fails
2576 + // Do not use wp_die or redirect fails.
2577 + die();
2519 2578 }
2520 2579
2521 2580 // Redirect with a delay.
2522 2581 self::redirect_after_submit_using_js( $args + compact( 'success_url', 'doing_ajax' ) );
@@ -2522,8 +2581,63 @@
2522 2581 self::redirect_after_submit_using_js( $args + compact( 'success_url', 'doing_ajax' ) );
2523 2582 }
2524 2583
2525 2584 /**
2585 + * Prints open in new tab js with fallback handler.
2586 + *
2587 + * @since 6.3.1
2588 + *
2589 + * @param string $success_url Success URL.
2590 + * @param array $args See {@see FrmFormsController::redirect_after_submit()}.
2591 + */
2592 + private static function print_open_in_new_tab_js_with_fallback_handler( $success_url, $args ) {
2593 + echo '<script>var newTab = window.open("' . esc_url_raw( $success_url ) . '", "_blank");';
2594 + echo 'if ( ! newTab ) {';
2595 +
2596 + $data = array(
2597 + 'formId' => intval( $args['form']->id ),
2598 + 'message' => self::get_redirect_fallback_message( $success_url, $args ),
2599 + );
2600 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2601 + echo 'frmShowNewTabFallback = ' . FrmAppHelper::maybe_json_encode( $data ) . ';';
2602 + echo '}</script>';
2603 + }
2604 +
2605 + /**
2606 + * Gets response data for redirect action when AJAX submitting.
2607 + *
2608 + * @since 6.3.1
2609 + *
2610 + * @param array $args See {@see FrmFormsController::run_success_action()}.
2611 + * @return array
2612 + */
2613 + private static function get_ajax_redirect_response_data( $args ) {
2614 + $response_data = array( 'redirect' => $args['success_url'] );
2615 +
2616 + if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2617 + $response_data['openInNewTab'] = 1;
2618 +
2619 + $args['message'] = FrmOnSubmitHelper::get_default_new_tab_msg();
2620 +
2621 + $args['form']->options['success_msg'] = $args['message'];
2622 + $args['form']->options['edit_msg'] = $args['message'];
2623 + if ( ! isset( $args['fields'] ) ) {
2624 + $args['fields'] = FrmField::get_all_for_form( $args['form']->id );
2625 + }
2626 +
2627 + $args['message'] = self::prepare_submit_message( $args['form'], $args['entry_id'], $args );
2628 +
2629 + ob_start();
2630 + self::show_lone_success_messsage( $args );
2631 + $response_data['content'] = ob_get_clean();
2632 +
2633 + $response_data['fallbackMsg'] = self::get_redirect_fallback_message( $args['success_url'], $args );
2634 + }
2635 +
2636 + return $response_data;
2637 + }
2638 +
2639 + /**
2526 2640 * Redirects after submitting using JS. This is used when showing message before redirecting.
2527 2641 *
2528 2642 * @since 6.0
2529 2643 *
@@ -2531,8 +2645,9 @@
2531 2645 */
2532 2646 private static function redirect_after_submit_using_js( $args ) {
2533 2647 $success_msg = FrmOnSubmitHelper::get_default_redirect_msg();
2534 2648 $redirect_msg = self::get_redirect_message( $args['success_url'], $success_msg, $args );
2649 + $success_url = esc_url_raw( $args['success_url'] );
2535 2650
2536 2651 /**
2537 2652 * Filters the delay time before redirecting when On Submit Redirect action is delayed.
2538 2653 *
@@ -2541,16 +2656,23 @@
2541 2656 * @param int $delay_time Delay time in miliseconds.
2542 2657 */
2543 2658 $delay_time = apply_filters( 'frm_redirect_delay_time', 8000 );
2544 2659
2660 + if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2661 + $redirect_js = 'window.open("' . $success_url . '", "_blank")';
2662 + } else {
2663 + $redirect_js = 'window.location="' . $success_url . '";';
2664 + }
2665 +
2545 2666 add_filter( 'frm_use_wpautop', '__return_true' );
2546 2667
2547 2668 echo FrmAppHelper::maybe_kses( $redirect_msg ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2548 2669 echo '<script>';
2549 - if ( empty( $args['doing_ajax'] ) ) { // Not AJAX submit, delay JS until window.load.
2670 + if ( empty( $args['doing_ajax'] ) ) {
2671 + // Not AJAX submit, delay JS until window.load.
2550 2672 echo 'window.onload=function(){';
2551 2673 }
2552 - echo 'setTimeout(function(){window.location="' . esc_url_raw( $args['success_url'] ) . '";}, ' . intval( $delay_time ) . ');';
2674 + echo 'setTimeout(function(){' . $redirect_js . '}, ' . intval( $delay_time ) . ');'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2553 2675 if ( empty( $args['doing_ajax'] ) ) {
2554 2676 echo '};';
2555 2677 }
2556 2678 echo '</script>';
@@ -2560,14 +2682,13 @@
2560 2682 * @since 3.0
2561 2683 *
2562 2684 * @param string $success_url
2563 2685 * @param string $success_msg
2564 - * @param array $args
2686 + * @param array $args
2565 2687 */
2566 2688 private static function get_redirect_message( $success_url, $success_msg, $args ) {
2567 2689 $redirect_msg = '<div class="' . esc_attr( FrmFormsHelper::get_form_style_class( $args['form'] ) ) . '"><div class="frm-redirect-msg" role="status">' . $success_msg . '<br/>' .
2568 - /* translators: %1$s: Start link HTML, %2$s: End link HTML */
2569 - sprintf( __( '%1$sClick here%2$s if you are not automatically redirected.', 'formidable' ), '<a href="' . esc_url( $success_url ) . '">', '</a>' ) .
2690 + self::get_redirect_fallback_message( $success_url, $args ) .
2570 2691 '</div></div>';
2571 2692
2572 2693 $redirect_args = array(
2573 2694 'entry_id' => $args['entry_id'],
@@ -2578,8 +2699,31 @@
2578 2699 return apply_filters( 'frm_redirect_msg', $redirect_msg, $redirect_args );
2579 2700 }
2580 2701
2581 2702 /**
2703 + * Gets fallback message when redirecting failed.
2704 + *
2705 + * @since 6.3.1
2706 + *
2707 + * @param string $success_url Redirect URL.
2708 + * @param array $args Contains `form` object.
2709 + * @return string
2710 + */
2711 + private static function get_redirect_fallback_message( $success_url, $args ) {
2712 + $target = '';
2713 + if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2714 + $target = ' target="_blank"';
2715 + }
2716 +
2717 + return sprintf(
2718 + /* translators: %1$s: Start link HTML, %2$s: End link HTML */
2719 + __( '%1$sClick here%2$s if you are not automatically redirected.', 'formidable' ),
2720 + '<a href="' . esc_url( $success_url ) . '"' . $target . '>',
2721 + '</a>'
2722 + );
2723 + }
2724 +
2725 + /**
2582 2726 * Prepare to show the success message and empty form after submit
2583 2727 *
2584 2728 * @since 2.05
2585 2729 */
@@ -2877,9 +3021,9 @@
2877 3021 * Create a page with an embedded formidable Gutenberg block.
2878 3022 *
2879 3023 * @since 5.2
2880 3024 *
2881 - * @return never
3025 + * @return void
2882 3026 */
2883 3027 public static function create_page_with_shortcode() {
2884 3028 if ( ! current_user_can( 'publish_posts' ) ) {
2885 3029 die( 0 );
@@ -2924,10 +3068,9 @@
2924 3068
2925 3069 /**
2926 3070 * @since 5.3
2927 3071 *
2928 - * @param string $content
2929 - * @param int $form_id
3072 + * @param int $form_id
2930 3073 * @return string
2931 3074 */
2932 3075 private static function get_page_shortcode_content_for_form( $form_id ) {
2933 3076 $shortcode = '[formidable id="' . $form_id . '"]';
@@ -2938,9 +3081,9 @@
2938 3081
2939 3082 /**
2940 3083 * Get page dropdown for AJAX request for embedding form in an existing page.
2941 3084 *
2942 - * @return never
3085 + * @return void
2943 3086 */
2944 3087 public static function get_page_dropdown() {
2945 3088 if ( ! current_user_can( 'publish_posts' ) ) {
2946 3089 die( 0 );
@@ -3012,6 +3155,46 @@
3012 3155 * @since 3.06
3013 3156 */
3014 3157 public static function add_new() {
3015 3158 _deprecated_function( __FUNCTION__, '4.08' );
3159 + }
3160 +
3161 + /**
3162 + * Create a custom template from a form
3163 + *
3164 + * @since 3.06
3165 + * @deprecated 6.7
3166 + */
3167 + public static function build_template() {
3168 + _deprecated_function( __METHOD__, '6.7' );
3169 + }
3170 +
3171 + /**
3172 + * @deprecated 6.7
3173 + *
3174 + * @return bool
3175 + */
3176 + public static function expired() {
3177 + _deprecated_function( __METHOD__, '6.7' );
3178 + return FrmAddonsController::is_license_expired();
3179 + }
3180 +
3181 + /**
3182 + * Get data from api before rendering it so that we can flag the modal as expired
3183 + *
3184 + * @deprecated 6.7
3185 + *
3186 + * @return void
3187 + */
3188 + public static function before_list_templates() {
3189 + _deprecated_function( __METHOD__, '6.7' );
3190 + }
3191 +
3192 + /**
3193 + * @deprecated 6.7
3194 + *
3195 + * @return void
3196 + */
3197 + public static function list_templates() {
3198 + _deprecated_function( __METHOD__, '6.7' );
3016 3199 }
3017 3200 }