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

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