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

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