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

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

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