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

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

3,127 lines 90.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmFormsController {
7
8 /**
9 * Track the form that opened the redirect URL in a new tab. This is used to check if we should show the default
10 * message in the currect tab.
11 *
12 * @since 6.2
13 *
14 * @var array Keys are form IDs and values are 1.
15 */
16 private static $redirected_in_new_tab = array();
17
18 public static function menu() {
19 $menu_label = __( 'Forms', 'formidable' );
20 if ( ! FrmAppHelper::pro_is_installed() ) {
21 $menu_label .= ' (Lite)';
22 }
23 add_submenu_page( 'formidable', 'Formidable | ' . $menu_label, $menu_label, 'frm_view_forms', 'formidable', 'FrmFormsController::route' );
24
25 self::maybe_load_listing_hooks();
26 }
27
28 public static function maybe_load_listing_hooks() {
29 if ( ! FrmAppHelper::on_form_listing_page() ) {
30 return;
31 }
32
33 add_filter( 'get_user_option_managetoplevel_page_formidablecolumnshidden', 'FrmFormsController::hidden_columns' );
34
35 add_filter( 'manage_toplevel_page_formidable_columns', 'FrmFormsController::get_columns', 0 );
36 add_filter( 'manage_toplevel_page_formidable_sortable_columns', 'FrmFormsController::get_sortable_columns' );
37 }
38
39 public static function head() {
40 if ( wp_is_mobile() ) {
41 wp_enqueue_script( 'jquery-touch-punch' );
42 }
43 }
44
45 public static function register_widgets() {
46 require_once FrmAppHelper::plugin_path() . '/classes/widgets/FrmShowForm.php';
47 register_widget( 'FrmShowForm' );
48 }
49
50 /**
51 * Show a message about conditional logic
52 *
53 * @since 4.06.03
54 */
55 public static function logic_tip() {
56 $images_url = FrmAppHelper::plugin_url() . '/images/';
57 $data_message = __( 'Only show the fields you need and create branching forms. Upgrade to get conditional logic and question branching.', 'formidable' );
58 $data_message .= ' <img src="' . esc_attr( $images_url ) . '/survey-logic.png" srcset="' . esc_attr( $images_url ) . 'survey-logic@2x.png 2x" alt="' . esc_attr__( 'Conditional Logic options', 'formidable' ) . '"/>';
59 echo '<a href="javascript:void(0)" class="frm_noallow frm_show_upgrade frm_add_logic_link frm-collapsed frm-flex-justify" data-upgrade="' . esc_attr__( 'Conditional Logic options', 'formidable' ) . '" data-message="' . esc_attr( $data_message ) . '" data-medium="builder" data-content="logic">';
60 esc_html_e( 'Conditional Logic', 'formidable' );
61 FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown6_icon', array( 'aria-hidden' => 'true' ) );
62 echo '</a>';
63 }
64
65 /**
66 * By default, Divi processes form shortcodes on the edit post page.
67 * Now that won't do.
68 *
69 * @since 3.01
70 */
71 public static function prevent_divi_conflict( $shortcodes ) {
72 $shortcodes[] = 'formidable';
73
74 return $shortcodes;
75 }
76
77 /**
78 * @return void
79 */
80 public static function list_form() {
81 FrmAppHelper::permission_check( 'frm_view_forms' );
82
83 $message = '';
84 $params = FrmForm::list_page_params();
85 $errors = self::process_bulk_form_actions( array() );
86 if ( isset( $errors['message'] ) ) {
87 $message = $errors['message'];
88 unset( $errors['message'] );
89 }
90 $errors = apply_filters( 'frm_admin_list_form_action', $errors );
91
92 self::display_forms_list( $params, $message, $errors );
93 }
94
95 /**
96 * Create the default email action
97 *
98 * @since 2.02.11
99 *
100 * @param object|int $form
101 */
102 private static function create_default_email_action( $form ) {
103 FrmForm::maybe_get_form( $form );
104 $create_email = apply_filters( 'frm_create_default_email_action', true, $form );
105
106 if ( $create_email ) {
107 $action_control = FrmFormActionsController::get_form_actions( 'email' );
108 $action_control->create( $form->id );
109 }
110 }
111
112 /**
113 * Create the default On submit action
114 *
115 * @since 6.0.0
116 *
117 * @param object|int $form Form object or ID.
118 */
119 private static function create_default_on_submit_action( $form ) {
120 FrmForm::maybe_get_form( $form );
121
122 /**
123 * Enable or disable the default On Submit action.
124 *
125 * @since 6.0.0
126 *
127 * @param bool $create Set to `false` if you want to disable.
128 * @param object $form Form object.
129 */
130 $create = apply_filters( 'frm_create_default_on_submit_action', true, $form );
131
132 if ( $create ) {
133 $action_control = FrmFormActionsController::get_form_actions( FrmOnSubmitAction::$slug );
134 $action_control->create( $form->id );
135 }
136 }
137
138 public static function edit( $values = false ) {
139 FrmAppHelper::permission_check( 'frm_edit_forms' );
140
141 $id = isset( $values['id'] ) ? absint( $values['id'] ) : FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
142
143 return self::get_edit_vars( $id );
144 }
145
146 public static function settings( $id = false, $message = '' ) {
147 FrmAppHelper::permission_check( 'frm_edit_forms' );
148
149 if ( ! $id || ! is_numeric( $id ) ) {
150 $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
151 }
152
153 FrmOnSubmitHelper::maybe_migrate_submit_settings_to_action( $id );
154
155 return self::get_settings_vars( $id, array(), $message );
156 }
157
158 public static function update_settings() {
159 FrmAppHelper::permission_check( 'frm_edit_forms' );
160
161 $id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
162
163 $errors = FrmForm::validate( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
164 $warnings = FrmFormsHelper::check_for_warnings( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
165
166 if ( count( $errors ) > 0 ) {
167 return self::get_settings_vars( $id, $errors, compact( 'warnings' ) );
168 }
169
170 do_action( 'frm_before_update_form_settings', $id );
171
172 $antispam_was_on = self::antispam_was_on( $id );
173
174 FrmForm::update( $id, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
175
176 $antispam_is_on = ! empty( $_POST['options']['antispam'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
177 if ( $antispam_is_on !== $antispam_was_on ) {
178 FrmAntiSpam::clear_caches();
179 }
180
181 $message = __( 'Settings Successfully Updated', 'formidable' );
182
183 return self::get_settings_vars( $id, array(), compact( 'message', 'warnings' ) );
184 }
185
186 /**
187 * @param int $form_id
188 * @return bool
189 */
190 private static function antispam_was_on( $form_id ) {
191 $form = FrmForm::getOne( $form_id );
192 return ! empty( $form->options['antispam'] );
193 }
194
195 public static function update( $values = array() ) {
196 if ( empty( $values ) ) {
197 $values = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
198 }
199
200 // Set radio button and checkbox meta equal to "other" value.
201 if ( FrmAppHelper::pro_is_installed() ) {
202 $values = FrmProEntry::mod_other_vals( $values, 'back' );
203 }
204
205 $errors = FrmForm::validate( $values );
206 $permission_error = FrmAppHelper::permission_nonce_error( 'frm_edit_forms', 'frm_save_form', 'frm_save_form_nonce' );
207 if ( $permission_error !== false ) {
208 $errors['form'] = $permission_error;
209 }
210
211 $id = isset( $values['id'] ) ? absint( $values['id'] ) : FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
212
213 if ( count( $errors ) > 0 ) {
214 return self::get_edit_vars( $id, $errors );
215 } else {
216 FrmForm::update( $id, $values );
217 $message = __( 'Form was successfully updated.', 'formidable' );
218
219 if ( self::is_too_long( $values ) ) {
220 $message .= '<br/> ' . sprintf(
221 /* translators: %1$s: Start link HTML, %2$s: end link HTML */
222 __( 'However, your form is very long and may be %1$sreaching server limits%2$s.', 'formidable' ),
223 '<a href="https://formidableforms.com/knowledgebase/i-have-a-long-form-why-did-the-options-at-the-end-of-the-form-stop-saving/?utm_source=WordPress&utm_medium=builder&utm_campaign=liteplugin" target="_blank" rel="noopener">',
224 '</a>'
225 );
226 }
227
228 if ( defined( 'DOING_AJAX' ) ) {
229 wp_die( FrmAppHelper::kses( $message, array( 'a' ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
230 }
231
232 return self::get_edit_vars( $id, array(), $message );
233 }
234 }
235
236 /**
237 * Check if the value at the end of the form was included.
238 * If it's missing, it means other values at the end of the form
239 * were likely not saved either.
240 *
241 * @since 3.06.01
242 */
243 private static function is_too_long( $values ) {
244 return ( ! isset( $values['frm_end'] ) ) || empty( $values['frm_end'] );
245 }
246
247 /**
248 * Redirect to the url for creating from a template
249 * Also delete the current form
250 *
251 * @since 2.0
252 * @deprecated 3.06
253 */
254 public static function _create_from_template() {
255 _deprecated_function( __FUNCTION__, '3.06' );
256
257 FrmAppHelper::permission_check( 'frm_edit_forms' );
258 check_ajax_referer( 'frm_ajax', 'nonce' );
259
260 $current_form = FrmAppHelper::get_param( 'this_form', '', 'get', 'absint' );
261 $template_id = FrmAppHelper::get_param( 'id', '', 'get', 'absint' );
262
263 if ( $current_form ) {
264 FrmForm::destroy( $current_form );
265 }
266
267 echo esc_url_raw( admin_url( 'admin.php?page=formidable&frm_action=duplicate&id=' . absint( $template_id ) ) );
268 wp_die();
269 }
270
271 public static function duplicate() {
272 FrmAppHelper::permission_check( 'frm_edit_forms' );
273 $nonce = FrmAppHelper::simple_get( '_wpnonce' );
274
275 if ( ! wp_verify_nonce( $nonce ) ) {
276 $frm_settings = FrmAppHelper::get_settings();
277 wp_die( esc_html( $frm_settings->admin_permission ) );
278 }
279
280 $params = FrmForm::list_page_params();
281 $form = FrmForm::duplicate( $params['id'], $params['template'], true );
282 $url = admin_url( 'admin.php?page=formidable' );
283 $message = 'form_duplicate_error';
284
285 if ( $form ) {
286 $url = admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . absint( $form ) );
287 $message = 'form_duplicated';
288 }
289
290 $url .= '&message=' . $message;
291
292 wp_safe_redirect( $url );
293 exit();
294 }
295
296 /**
297 * @return string
298 */
299 public static function page_preview() {
300 $params = FrmForm::list_page_params();
301 if ( ! $params['form'] ) {
302 return;
303 }
304
305 $form = FrmForm::getOne( $params['form'] );
306 if ( $form ) {
307 return self::show_form( $form->id, '', 'auto', 'auto' );
308 }
309 }
310
311 /**
312 * @since 3.0
313 */
314 public static function show_page_preview() {
315 echo self::page_preview(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
316 }
317
318 /**
319 * @return void
320 */
321 public static function preview() {
322 do_action( 'frm_wp' );
323 FrmAppHelper::set_current_screen_and_hook_suffix();
324
325 global $frm_vars;
326 $frm_vars['preview'] = true;
327
328 $include_theme = FrmAppHelper::get_param( 'theme', '', 'get', 'absint' );
329 if ( $include_theme ) {
330 self::set_preview_query();
331 self::load_theme_preview();
332 } else {
333 self::load_direct_preview();
334 }
335
336 wp_die();
337 }
338
339 /**
340 * Set the page global to any available page (except for the Blog Page).
341 *
342 * @return void
343 */
344 private static function set_preview_query() {
345 $page_query = array(
346 'numberposts' => 1,
347 'orderby' => 'date',
348 'order' => 'ASC',
349 'post_type' => 'page',
350 );
351
352 $page_for_posts = get_option( 'page_for_posts' );
353 if ( is_numeric( $page_for_posts ) ) {
354 // Avoid querying for the "Posts Page" or "Blog Page" so we don't display 10 forms.
355 $page_query['post__not_in'] = array( $page_for_posts );
356 }
357
358 $random_page = get_posts( $page_query );
359 if ( ! $random_page ) {
360 return;
361 }
362
363 $random_page = reset( $random_page );
364 query_posts(
365 array(
366 'post_type' => 'page',
367 'page_id' => $random_page->ID,
368 )
369 );
370
371 // Fixes Pro issue #3004. Prevent an undefined $post object.
372 // Otherwise WordPress themes will trigger a warning "Attempt to read property "comment_count" on null".
373 self::set_post_global( $random_page );
374 }
375
376 /**
377 * Set the WP $post global object. Used for in-theme preview when defining a page.
378 *
379 * @since 5.5.2
380 *
381 * @param WP_Post $post
382 * @return void
383 */
384 private static function set_post_global( $page ) {
385 global $post;
386 $post = $page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
387 }
388
389 /**
390 * @since 3.0
391 */
392 private static function load_theme_preview() {
393 add_filter( 'wp_title', 'FrmFormsController::preview_title', 9999 );
394 add_filter( 'the_title', 'FrmFormsController::preview_page_title', 9999 );
395 add_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
396 add_action( 'loop_no_results', 'FrmFormsController::show_page_preview' );
397 add_filter( 'is_active_sidebar', '__return_false' );
398 FrmStylesController::enqueue_css( 'enqueue', true );
399
400 if ( false === get_template_part( 'page' ) ) {
401 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'] = __( 'Form Title', 'formidable' );
894 $columns['entries'] = __( 'Entries', 'formidable' );
895 $columns['id'] = 'ID';
896 $columns['form_key'] = __( 'Key', 'formidable' );
897 $columns['shortcode'] = __( 'Actions', 'formidable' );
898 $columns['created_at'] = __( '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 'html' => array(
1274 'name' => __( 'Customize HTML', 'formidable' ),
1275 'class' => __CLASS__,
1276 'function' => 'html_settings',
1277 'icon' => 'frm_icon_font frm_code_icon',
1278 ),
1279 );
1280
1281 foreach ( array( 'landing', 'chat' ) as $feature ) {
1282 if ( ! FrmAppHelper::show_new_feature( $feature ) ) {
1283 unset( $sections[ $feature ] );
1284 }
1285 }
1286
1287 $sections = apply_filters( 'frm_add_form_settings_section', $sections, $values );
1288
1289 if ( FrmAppHelper::pro_is_installed() && ! FrmAppHelper::meets_min_pro_version( '4.0' ) ) {
1290 // Prevent settings from showing in 2 spots.
1291 unset( $sections['permissions'], $sections['scheduling'] );
1292 }
1293
1294 foreach ( $sections as $key => $section ) {
1295 $defaults = array(
1296 'html_class' => '',
1297 'name' => ucfirst( $key ),
1298 'icon' => 'frm_icon_font frm_settings_icon',
1299 );
1300
1301 $section = array_merge( $defaults, $section );
1302
1303 if ( ! isset( $section['anchor'] ) ) {
1304 $section['anchor'] = $key;
1305 }
1306 $section['anchor'] .= '_settings';
1307
1308 if ( ! isset( $section['title'] ) ) {
1309 $section['title'] = $section['name'];
1310 }
1311
1312 if ( ! isset( $section['id'] ) ) {
1313 $section['id'] = $section['anchor'];
1314 }
1315
1316 $sections[ $key ] = $section;
1317 }
1318
1319 return $sections;
1320 }
1321
1322 /**
1323 * @since 4.0
1324 *
1325 * @param array $values
1326 */
1327 public static function advanced_settings( $values ) {
1328 $first_h3 = 'frm_first_h3';
1329
1330 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-advanced.php';
1331 }
1332
1333 /**
1334 * @param array $values
1335 */
1336 public static function render_spam_settings( $values ) {
1337 if ( function_exists( 'akismet_http_post' ) ) {
1338 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/akismet.php';
1339 }
1340 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/honeypot.php';
1341 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/antispam.php';
1342 }
1343
1344 /**
1345 * @since 4.0
1346 *
1347 * @param array $values
1348 */
1349 public static function buttons_settings( $values ) {
1350 $styles = apply_filters( 'frm_get_style_opts', array() );
1351
1352 $frm_settings = FrmAppHelper::get_settings();
1353 $no_global_style = $frm_settings->load_style === 'none';
1354
1355 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-buttons.php' );
1356 }
1357
1358 /**
1359 * @since 4.0
1360 *
1361 * @param array $values
1362 */
1363 public static function html_settings( $values ) {
1364 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-html.php' );
1365 }
1366
1367 /**
1368 * Replace old Submit Button href with new href to avoid errors in Chrome
1369 *
1370 * @since 2.03.08
1371 *
1372 * @param array|boolean $values
1373 */
1374 private static function clean_submit_html( &$values ) {
1375 if ( is_array( $values ) && isset( $values['submit_html'] ) ) {
1376 $values['submit_html'] = str_replace( 'javascript:void(0)', '#', $values['submit_html'] );
1377 }
1378 }
1379
1380 /**
1381 * Updates classes used in submit and prev buttons to avoid conflict with twenty twenty-one theme.
1382 *
1383 * @param array $classes
1384 *
1385 * @return array
1386 */
1387 public static function update_button_classes( $classes ) {
1388 if ( function_exists( 'twenty_twenty_one_setup' ) ) {
1389 $classes[] = 'has-text-color has-background';
1390 }
1391
1392 return $classes;
1393 }
1394
1395 public static function mb_tags_box( $form_id, $class = '' ) {
1396 $fields = FrmField::get_all_for_form( $form_id, '', 'include' );
1397
1398 /**
1399 * Allows modifying the list of fields in the tags box.
1400 *
1401 * @since 5.0.04
1402 *
1403 * @param array $fields The list of fields.
1404 * @param array $args The arguments. Contains `form_id`.
1405 */
1406 $fields = apply_filters( 'frm_fields_in_tags_box', $fields, compact( 'form_id' ) );
1407 $linked_forms = array();
1408 $col = 'one';
1409 $settings_tab = FrmAppHelper::is_admin_page( 'formidable' ) ? true : false;
1410
1411 $cond_shortcodes = apply_filters( 'frm_conditional_shortcodes', array() );
1412 $entry_shortcodes = self::get_shortcode_helpers( $settings_tab );
1413
1414 $advanced_helpers = self::advanced_helpers( compact( 'fields', 'form_id' ) );
1415
1416 include( FrmAppHelper::plugin_path() . '/classes/views/shared/mb_adv_info.php' );
1417 }
1418
1419 /**
1420 * @since 3.04.01
1421 */
1422 private static function advanced_helpers( $atts ) {
1423 $advanced_helpers = array(
1424 'default' => array(
1425 'heading' => __( 'Customize field values with the following parameters.', 'formidable' ),
1426 'codes' => self::get_advanced_shortcodes(),
1427 ),
1428 );
1429
1430 $user_fields = self::user_shortcodes();
1431 if ( ! empty( $user_fields ) ) {
1432 $user_helpers = array();
1433 foreach ( $user_fields as $uk => $uf ) {
1434 $user_helpers[ '|user_id| show="' . $uk . '"' ] = $uf;
1435 unset( $uk, $uf );
1436 }
1437
1438 $advanced_helpers['user_id'] = array(
1439 'codes' => $user_helpers,
1440 );
1441 }
1442
1443 /**
1444 * Add extra helper shortcodes on the Advanced tab in form settings and views
1445 *
1446 * @since 3.04.01
1447 *
1448 * @param array $advanced_helpers
1449 * @param array $atts - Includes fields and form_id
1450 */
1451 return apply_filters( 'frm_advanced_helpers', $advanced_helpers, $atts );
1452 }
1453
1454 /**
1455 * Get an array of the options to display in the advanced tab
1456 * of the customization panel
1457 *
1458 * @since 2.0.6
1459 */
1460 private static function get_advanced_shortcodes() {
1461 $adv_shortcodes = array(
1462 'x sep=", "' => array(
1463 'label' => __( 'Separator', 'formidable' ),
1464 'title' => __( 'Use a different separator for checkbox fields', 'formidable' ),
1465 ),
1466 'x format="d-m-Y"' => array(
1467 'label' => __( 'Date Format', 'formidable' ),
1468 ),
1469 'x show="field_label"' => array(
1470 'label' => __( 'Field Label', 'formidable' ),
1471 ),
1472 'x wpautop=0' => array(
1473 'label' => __( 'No Auto P', 'formidable' ),
1474 'title' => __( 'Do not automatically add any paragraphs or line breaks', 'formidable' ),
1475 ),
1476 );
1477 $adv_shortcodes = apply_filters( 'frm_advanced_shortcodes', $adv_shortcodes );
1478
1479 // __( 'Leave blank instead of defaulting to User Login', 'formidable' ) : blank=1
1480
1481 return $adv_shortcodes;
1482 }
1483
1484 /**
1485 * @since 3.04.01
1486 */
1487 private static function user_shortcodes() {
1488 $options = array(
1489 'ID' => __( 'User ID', 'formidable' ),
1490 'first_name' => __( 'First Name', 'formidable' ),
1491 'last_name' => __( 'Last Name', 'formidable' ),
1492 'display_name' => __( 'Display Name', 'formidable' ),
1493 'user_login' => __( 'User Login', 'formidable' ),
1494 'user_email' => __( 'Email', 'formidable' ),
1495 'avatar' => __( 'Avatar', 'formidable' ),
1496 'author_link' => __( 'Author Link', 'formidable' ),
1497 );
1498
1499 return apply_filters( 'frm_user_shortcodes', $options );
1500 }
1501
1502 /**
1503 * Get an array of the helper shortcodes to display in the customization panel
1504 *
1505 * @since 2.0.6
1506 */
1507 private static function get_shortcode_helpers( $settings_tab ) {
1508 $entry_shortcodes = array(
1509 'id' => __( 'Entry ID', 'formidable' ),
1510 'key' => __( 'Entry Key', 'formidable' ),
1511 'post_id' => __( 'Post ID', 'formidable' ),
1512 'ip' => __( 'User IP', 'formidable' ),
1513 'created-at' => __( 'Entry created', 'formidable' ),
1514 'updated-at' => __( 'Entry updated', 'formidable' ),
1515 '' => '',
1516 'siteurl' => __( 'Site URL', 'formidable' ),
1517 'sitename' => __( 'Site Name', 'formidable' ),
1518 );
1519
1520 if ( ! FrmAppHelper::pro_is_installed() ) {
1521 unset( $entry_shortcodes['post_id'] );
1522 }
1523
1524 if ( $settings_tab ) {
1525 $entry_shortcodes['default-message'] = __( 'Default Msg', 'formidable' );
1526 $entry_shortcodes['default-html'] = __( 'Default HTML', 'formidable' );
1527 $entry_shortcodes['default-plain'] = __( 'Default Plain', 'formidable' );
1528 $entry_shortcodes['form_name'] = __( 'Form Name', 'formidable' );
1529 }
1530
1531 /**
1532 * Use this hook to add or remove buttons in the helpers section
1533 * in the customization panel
1534 *
1535 * @since 2.0.6
1536 */
1537 $entry_shortcodes = apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab );
1538
1539 return $entry_shortcodes;
1540 }
1541
1542 /**
1543 * Insert the form class setting into the form.
1544 *
1545 * @param stdClass $form
1546 * @return void
1547 */
1548 public static function form_classes( $form ) {
1549 if ( isset( $form->options['form_class'] ) ) {
1550 echo esc_attr( sanitize_text_field( $form->options['form_class'] ) );
1551 }
1552
1553 if ( ! empty( $form->options['js_validate'] ) ) {
1554 echo ' frm_js_validate ';
1555 self::add_js_validate_form_to_global_vars( $form );
1556 }
1557
1558 if ( ! FrmFormsHelper::should_use_pro_for_ajax_submit() && FrmForm::is_ajax_on( $form ) ) {
1559 echo ' frm_ajax_submit ';
1560 }
1561 }
1562
1563 /**
1564 * @since 5.0.03
1565 *
1566 * @param stdClass $form
1567 */
1568 public static function add_js_validate_form_to_global_vars( $form ) {
1569 global $frm_vars;
1570 if ( ! isset( $frm_vars['js_validate_forms'] ) ) {
1571 $frm_vars['js_validate_forms'] = array();
1572 }
1573 $frm_vars['js_validate_forms'][ $form->id ] = $form;
1574 }
1575
1576 public static function get_email_html() {
1577 FrmAppHelper::permission_check( 'frm_view_forms' );
1578 check_ajax_referer( 'frm_ajax', 'nonce' );
1579
1580 echo FrmEntriesController::show_entry_shortcode( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1581 array(
1582 'form_id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ),
1583 'default_email' => true,
1584 'plain_text' => FrmAppHelper::get_post_param( 'plain_text', '', 'absint' ),
1585 )
1586 );
1587 wp_die();
1588 }
1589
1590 /**
1591 * @param string $content
1592 * @param stdClass|string|int $form
1593 * @param stdClass|string|int|false $entry
1594 * @return string
1595 */
1596 public static function filter_content( $content, $form, $entry = false ) {
1597 $content = self::replace_form_name_shortcodes( $content, $form );
1598
1599 self::get_entry_by_param( $entry );
1600 if ( ! $entry ) {
1601 return $content;
1602 }
1603
1604 if ( is_object( $form ) ) {
1605 $form = $form->id;
1606 }
1607
1608 $shortcodes = FrmFieldsHelper::get_shortcodes( $content, $form );
1609 $content = apply_filters( 'frm_replace_content_shortcodes', $content, $entry, $shortcodes );
1610
1611 return $content;
1612 }
1613
1614 /**
1615 * Replace any [form_name] shortcodes in a string.
1616 *
1617 * @since 5.5
1618 *
1619 * @param string|array $string
1620 * @param stdClass|string|int $form
1621 * @return string|array
1622 */
1623 public static function replace_form_name_shortcodes( $string, $form ) {
1624 if ( ! is_string( $string ) ) {
1625 return $string;
1626 }
1627
1628 if ( false === strpos( $string, '[form_name]' ) ) {
1629 return $string;
1630 }
1631
1632 if ( ! is_object( $form ) ) {
1633 $form = FrmForm::getOne( $form );
1634 }
1635
1636 $form_name = is_object( $form ) ? $form->name : '';
1637 return str_replace( '[form_name]', $form_name, $string );
1638 }
1639
1640 /**
1641 * @param stdClass|string|int|false $entry
1642 * @return void
1643 */
1644 private static function get_entry_by_param( &$entry ) {
1645 if ( ! $entry || ! is_object( $entry ) ) {
1646 if ( ! $entry || ! is_numeric( $entry ) ) {
1647 $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
1648 }
1649
1650 FrmEntry::maybe_get_entry( $entry );
1651 }
1652 }
1653
1654 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
1655 return FrmFieldsHelper::replace_content_shortcodes( $content, $entry, $shortcodes );
1656 }
1657
1658 public static function process_bulk_form_actions( $errors ) {
1659 if ( ! $_REQUEST ) {
1660 return $errors;
1661 }
1662
1663 $bulkaction = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
1664 if ( $bulkaction == - 1 ) {
1665 $bulkaction = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
1666 }
1667
1668 if ( ! empty( $bulkaction ) && strpos( $bulkaction, 'bulk_' ) === 0 ) {
1669 FrmAppHelper::remove_get_action();
1670
1671 $bulkaction = str_replace( 'bulk_', '', $bulkaction );
1672 }
1673
1674 $ids = FrmAppHelper::get_param( 'item-action', '', 'get', 'sanitize_text_field' );
1675 if ( empty( $ids ) ) {
1676 $errors[] = __( 'No forms were specified', 'formidable' );
1677
1678 return $errors;
1679 }
1680
1681 $permission_error = FrmAppHelper::permission_nonce_error( '', '_wpnonce', 'bulk-toplevel_page_formidable' );
1682 if ( $permission_error !== false ) {
1683 $errors[] = $permission_error;
1684
1685 return $errors;
1686 }
1687
1688 if ( ! is_array( $ids ) ) {
1689 $ids = explode( ',', $ids );
1690 }
1691
1692 switch ( $bulkaction ) {
1693 case 'delete':
1694 $message = self::bulk_destroy( $ids );
1695 break;
1696 case 'trash':
1697 $message = self::bulk_trash( $ids );
1698 break;
1699 case 'untrash':
1700 $message = self::bulk_untrash( $ids );
1701 }
1702
1703 if ( isset( $message ) && ! empty( $message ) ) {
1704 $errors['message'] = $message;
1705 }
1706
1707 return $errors;
1708 }
1709
1710 public static function route() {
1711 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
1712 $vars = array();
1713 FrmAppHelper::include_svg();
1714
1715 if ( isset( $_POST['frm_compact_fields'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1716 FrmAppHelper::permission_check( 'frm_edit_forms' );
1717
1718 // Javascript needs to be allowed in some field settings.
1719 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
1720 $json_vars = htmlspecialchars_decode( nl2br( str_replace( '&quot;', '"', wp_unslash( $_POST['frm_compact_fields'] ) ) ) );
1721 $json_vars = json_decode( $json_vars, true );
1722 if ( empty( $json_vars ) ) {
1723 // json decoding failed so we should return an error message.
1724 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
1725 if ( 'edit' == $action ) {
1726 $action = 'update';
1727 }
1728
1729 add_filter( 'frm_validate_form', 'FrmFormsController::json_error' );
1730 } else {
1731 $vars = FrmAppHelper::json_to_array( $json_vars );
1732 $action = $vars[ $action ];
1733 unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1734 $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1735 $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1736 }
1737 } else {
1738 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
1739 if ( isset( $_REQUEST['delete_all'] ) ) {
1740 // Override the action for this page.
1741 $action = 'delete_all';
1742 }
1743 }
1744
1745 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1746 FrmAppHelper::trigger_hook_load( 'form' );
1747
1748 switch ( $action ) {
1749 case 'new':
1750 return self::new_form( $vars );
1751 case 'create':
1752 case 'edit':
1753 case 'update':
1754 case 'trash':
1755 case 'untrash':
1756 case 'destroy':
1757 case 'delete_all':
1758 case 'settings':
1759 case 'update_settings':
1760 return self::$action( $vars );
1761 case 'lite-reports':
1762 return self::no_reports( $vars );
1763 case 'views':
1764 return self::no_views( $vars );
1765 default:
1766 do_action( 'frm_form_action_' . $action );
1767 if ( apply_filters( 'frm_form_stop_action_' . $action, false ) ) {
1768 return;
1769 }
1770
1771 $action = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
1772 if ( $action == - 1 ) {
1773 $action = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
1774 }
1775
1776 if ( strpos( $action, 'bulk_' ) === 0 ) {
1777 FrmAppHelper::remove_get_action();
1778
1779 self::list_form();
1780 return;
1781 }
1782
1783 $message = FrmAppHelper::get_param( 'message' );
1784 if ( 'form_duplicate_error' === $message ) {
1785 self::display_forms_list( array(), '', array( __( 'There was a problem duplicating the form', 'formidable' ) ) );
1786 return;
1787 }
1788
1789 self::display_forms_list();
1790
1791 return;
1792 }
1793 }
1794
1795 public static function json_error( $errors ) {
1796 $errors['json'] = __( 'Abnormal HTML characters prevented your form from saving correctly', 'formidable' );
1797
1798 return $errors;
1799 }
1800
1801 /**
1802 * Education for premium features.
1803 *
1804 * @since 4.05
1805 */
1806 public static function add_form_style_tab_options() {
1807 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/add_form_style_options.php' );
1808 }
1809
1810 /**
1811 * Add education about views.
1812 *
1813 * @since 4.07
1814 */
1815 public static function no_views( $values = array() ) {
1816 FrmAppHelper::include_svg();
1817 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1818 $form = $id ? FrmForm::getOne( $id ) : false;
1819
1820 include FrmAppHelper::plugin_path() . '/classes/views/shared/views-info.php';
1821 }
1822
1823 /**
1824 * Add education about reports.
1825 *
1826 * @since 4.07
1827 */
1828 public static function no_reports( $values = array() ) {
1829 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1830 $form = $id ? FrmForm::getOne( $id ) : false;
1831
1832 include FrmAppHelper::plugin_path() . '/classes/views/shared/reports-info.php';
1833 }
1834
1835 /* FRONT-END FORMS */
1836 public static function admin_bar_css() {
1837 if ( is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
1838 return;
1839 }
1840
1841 self::move_menu_to_footer();
1842
1843 add_action( 'wp_before_admin_bar_render', 'FrmFormsController::admin_bar_configure' );
1844 FrmAppHelper::load_font_style();
1845 }
1846
1847 /**
1848 * @since 4.05.02
1849 */
1850 private static function move_menu_to_footer() {
1851 $settings = FrmAppHelper::get_settings();
1852 if ( empty( $settings->admin_bar ) ) {
1853 remove_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
1854 }
1855 }
1856
1857 public static function admin_bar_configure() {
1858 global $frm_vars;
1859 if ( empty( $frm_vars['forms_loaded'] ) ) {
1860 return;
1861 }
1862
1863 $actions = array();
1864 foreach ( $frm_vars['forms_loaded'] as $form ) {
1865 if ( is_object( $form ) ) {
1866 $actions[ $form->id ] = $form->name;
1867 }
1868 unset( $form );
1869 }
1870
1871 if ( empty( $actions ) ) {
1872 return;
1873 }
1874
1875 self::add_menu_to_admin_bar();
1876 self::add_forms_to_admin_bar( $actions );
1877 }
1878
1879 /**
1880 * @since 2.05.07
1881 */
1882 public static function add_menu_to_admin_bar() {
1883 global $wp_admin_bar;
1884
1885 $wp_admin_bar->add_node(
1886 array(
1887 'id' => 'frm-forms',
1888 'title' => '<span class="ab-icon"></span><span class="ab-label">' . FrmAppHelper::get_menu_name() . '</span>',
1889 'href' => admin_url( 'admin.php?page=formidable' ),
1890 'meta' => array(
1891 'title' => FrmAppHelper::get_menu_name(),
1892 ),
1893 )
1894 );
1895 }
1896
1897 /**
1898 * @since 2.05.07
1899 */
1900 private static function add_forms_to_admin_bar( $actions ) {
1901 global $wp_admin_bar;
1902
1903 asort( $actions );
1904
1905 foreach ( $actions as $form_id => $name ) {
1906
1907 $wp_admin_bar->add_node(
1908 array(
1909 'parent' => 'frm-forms',
1910 'id' => 'edit_form_' . $form_id,
1911 'title' => empty( $name ) ? __( '(no title)', 'formidable' ) : $name,
1912 'href' => FrmForm::get_edit_link( $form_id ),
1913 )
1914 );
1915 }
1916 }
1917
1918 /**
1919 * The formidable shortcode
1920 *
1921 * @param array $atts The params from the shortcode.
1922 * @return string
1923 */
1924 public static function get_form_shortcode( $atts ) {
1925 global $frm_vars;
1926 if ( isset( $frm_vars['skip_shortcode'] ) && $frm_vars['skip_shortcode'] ) {
1927 $sc = '[formidable';
1928 $sc .= FrmAppHelper::array_to_html_params( $atts );
1929 return $sc . ']';
1930 }
1931
1932 $shortcode_atts = shortcode_atts(
1933 array(
1934 'id' => '',
1935 'key' => '',
1936 'title' => 'auto',
1937 'description' => 'auto',
1938 'readonly' => false,
1939 'entry_id' => false,
1940 'fields' => array(),
1941 'exclude_fields' => array(),
1942 'minimize' => false,
1943 ),
1944 $atts
1945 );
1946 do_action( 'formidable_shortcode_atts', $shortcode_atts, $atts );
1947
1948 return self::show_form( $shortcode_atts['id'], $shortcode_atts['key'], $shortcode_atts['title'], $shortcode_atts['description'], $atts );
1949 }
1950
1951 /**
1952 * @since 5.2.01
1953 *
1954 * @param string|int|false $id
1955 * @param string|false $key
1956 * @return stdClass|false
1957 */
1958 private static function maybe_get_form_by_id_or_key( $id, $key ) {
1959 if ( ! $id ) {
1960 $id = $key;
1961 }
1962 return self::maybe_get_form_to_show( $id );
1963 }
1964
1965 /**
1966 * @param string|int|false $id
1967 * @param string|false $key
1968 * @param string|int|bool $title may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
1969 * @param string|int|bool $description may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
1970 * @param array $atts
1971 * @return string
1972 */
1973 public static function show_form( $id = '', $key = '', $title = false, $description = false, $atts = array() ) {
1974 $form = self::maybe_get_form_by_id_or_key( $id, $key );
1975
1976 if ( ! $form ) {
1977 return __( 'Please select a valid form', 'formidable' );
1978 }
1979
1980 if ( 'auto' === $title ) {
1981 $title = ! empty( $form->options['show_title'] );
1982 }
1983
1984 if ( 'auto' === $description ) {
1985 $description = ! empty( $form->options['show_description'] );
1986 }
1987
1988 FrmAppController::maybe_update_styles();
1989
1990 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1991 FrmAppHelper::trigger_hook_load( 'form', $form );
1992
1993 $form = apply_filters( 'frm_pre_display_form', $form );
1994
1995 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
1996
1997 if ( self::is_viewable_draft_form( $form ) ) {
1998 // don't show a draft form on a page
1999 $form = __( 'Please select a valid form', 'formidable' );
2000 } elseif ( ! FrmForm::is_visible_to_user( $form ) ) {
2001 $form = do_shortcode( $frm_settings->login_msg );
2002 } else {
2003 do_action( 'frm_pre_get_form', $form );
2004 $form = self::get_form( $form, $title, $description, $atts );
2005
2006 /**
2007 * Use this shortcode to check for external shortcodes that may span
2008 * across multiple fields in the customizable HTML
2009 *
2010 * @since 2.0.8
2011 */
2012 $form = apply_filters( 'frm_filter_final_form', $form );
2013 }
2014
2015 return $form;
2016 }
2017
2018 /**
2019 * @param string|int|false $id
2020 * @return stdClass|false
2021 */
2022 private static function maybe_get_form_to_show( $id ) {
2023 $form = false;
2024
2025 if ( ! empty( $id ) ) { // form id or key is set
2026 $form = FrmForm::getOne( $id );
2027 if ( ! $form || $form->parent_form_id || $form->status === 'trash' ) {
2028 $form = false;
2029 }
2030 }
2031
2032 return $form;
2033 }
2034
2035 private static function is_viewable_draft_form( $form ) {
2036 return $form->status === 'draft' && current_user_can( 'frm_edit_forms' ) && ! FrmAppHelper::is_preview_page();
2037 }
2038
2039 public static function get_form( $form, $title, $description, $atts = array() ) {
2040 ob_start();
2041
2042 do_action( 'frm_before_get_form', $atts );
2043
2044 self::get_form_contents( $form, $title, $description, $atts );
2045 self::enqueue_scripts( FrmForm::get_params( $form ) );
2046
2047 $contents = ob_get_contents();
2048 ob_end_clean();
2049
2050 self::maybe_minimize_form( $atts, $contents );
2051
2052 return $contents;
2053 }
2054
2055 public static function enqueue_scripts( $params ) {
2056 do_action( 'frm_enqueue_form_scripts', $params );
2057 }
2058
2059 public static function get_form_contents( $form, $title, $description, $atts ) {
2060 $params = FrmForm::get_params( $form );
2061 $errors = self::get_saved_errors( $form, $params );
2062 $fields = FrmFieldsHelper::get_form_fields( $form->id, $errors );
2063 $reset = false;
2064 $pass_args = compact( 'form', 'fields', 'errors', 'title', 'description', 'reset' );
2065
2066 $pass_args['action'] = $params['action'];
2067 $handle_process_here = $params['action'] === 'create' && $params['posted_form_id'] == $form->id && $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
2068
2069 if ( ! $handle_process_here ) {
2070 FrmFormState::set_initial_value( 'title', $title );
2071 FrmFormState::set_initial_value( 'description', $description );
2072
2073 do_action( 'frm_display_form_action', $params, $fields, $form, $title, $description );
2074 if ( apply_filters( 'frm_continue_to_new', true, $form->id, $params['action'] ) ) {
2075 self::show_form_after_submit( $pass_args );
2076 }
2077 } elseif ( ! empty( $errors ) ) {
2078 self::show_form_after_submit( $pass_args );
2079
2080 } else {
2081
2082 do_action( 'frm_validate_form_creation', $params, $fields, $form, $title, $description );
2083
2084 if ( apply_filters( 'frm_continue_to_create', true, $form->id ) ) {
2085 $entry_id = self::just_created_entry( $form->id );
2086 $pass_args['entry_id'] = $entry_id;
2087 $pass_args['reset'] = true;
2088
2089 self::run_on_submit_actions( $pass_args );
2090
2091 do_action(
2092 'frm_after_entry_processed',
2093 array(
2094 'entry_id' => $entry_id,
2095 'form' => $form,
2096 )
2097 );
2098 }
2099 }
2100 }
2101
2102 /**
2103 * If the form was processed earlier (init), get the generated errors
2104 *
2105 * @since 2.05
2106 */
2107 private static function get_saved_errors( $form, $params ) {
2108 global $frm_vars;
2109
2110 if ( $params['posted_form_id'] == $form->id && $_POST && isset( $frm_vars['created_entries'][ $form->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
2111 $errors = $frm_vars['created_entries'][ $form->id ]['errors'];
2112 } else {
2113 $errors = array();
2114 }
2115
2116 /**
2117 * Allows modifying the generated errors if the form was processed earlier.
2118 *
2119 * @since 5.2.03
2120 *
2121 * @param array $errors Errors data. Is empty array if no errors found.
2122 * @param array $params Form params. See {@see FrmForm::get_params()}.
2123 */
2124 return apply_filters( 'frm_saved_errors', $errors, $params );
2125 }
2126
2127 /**
2128 * @since 2.2.7
2129 */
2130 public static function just_created_entry( $form_id ) {
2131 global $frm_vars;
2132
2133 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;
2134 }
2135
2136 /**
2137 * Gets confirmation method.
2138 *
2139 * @since 3.0
2140 * @since 6.0 This method can return an array of met On Submit actions.
2141 *
2142 * @param array $atts {
2143 * Atts.
2144 *
2145 * @type object $form Form object.
2146 * @type int $entry_id Entry ID.
2147 * }
2148 * @return string|array
2149 */
2150 private static function get_confirmation_method( $atts ) {
2151 $action = FrmOnSubmitHelper::current_event( $atts );
2152 $opt = 'update' === $action ? 'edit_action' : 'success_action';
2153 $method = ( isset( $atts['form']->options[ $opt ] ) && ! empty( $atts['form']->options[ $opt ] ) ) ? $atts['form']->options[ $opt ] : 'message';
2154
2155 if ( ! empty( $atts['entry_id'] ) ) {
2156 $met_actions = self::get_met_on_submit_actions( $atts, $action );
2157 if ( $met_actions ) {
2158 $method = $met_actions;
2159 }
2160 }
2161
2162 $method = apply_filters( 'frm_success_filter', $method, $atts['form'], $action );
2163
2164 if ( $method != 'message' && ( ! $atts['entry_id'] || ! is_numeric( $atts['entry_id'] ) ) ) {
2165 $method = 'message';
2166 }
2167
2168 return $method;
2169 }
2170
2171 public static function maybe_trigger_redirect( $form, $params, $args ) {
2172 if ( ! isset( $params['id'] ) ) {
2173 global $frm_vars;
2174 $params['id'] = $frm_vars['created_entries'][ $form->id ]['entry_id'];
2175 }
2176
2177 $conf_method = self::get_confirmation_method(
2178 array(
2179 'form' => $form,
2180 'entry_id' => $params['id'],
2181 'action' => FrmOnSubmitHelper::current_event( $params ),
2182 )
2183 );
2184
2185 self::maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args );
2186 }
2187
2188 /**
2189 * Maybe trigger redirect with the new Confirmation action.
2190 *
2191 * @since 6.1.1
2192 *
2193 * @param array|string $conf_method Array of confirmation actions or the action type string.
2194 * @param object $form Form object.
2195 * @param array $params See {@see FrmFormsController::maybe_trigger_redirect()}.
2196 * @param array $args See {@see FrmFormsController::maybe_trigger_redirect()}.
2197 */
2198 public static function maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args ) {
2199 if ( is_array( $conf_method ) && 1 === count( $conf_method ) ) {
2200 if ( 'redirect' === FrmOnSubmitHelper::get_action_type( $conf_method[0] ) ) {
2201 $event = FrmOnSubmitHelper::current_event( $params );
2202 FrmOnSubmitHelper::populate_on_submit_data( $form->options, $conf_method[0], $event );
2203 $conf_method = 'redirect';
2204 }
2205 }
2206
2207 if ( 'redirect' === $conf_method ) {
2208 self::trigger_redirect( $form, $params, $args );
2209 }
2210 }
2211
2212 public static function trigger_redirect( $form, $params, $args ) {
2213 $success_args = array(
2214 'action' => $params['action'],
2215 'conf_method' => 'redirect',
2216 'form' => $form,
2217 'entry_id' => $params['id'],
2218 );
2219
2220 if ( isset( $args['ajax'] ) ) {
2221 $success_args['ajax'] = $args['ajax'];
2222 }
2223
2224 self::run_success_action( $success_args );
2225 }
2226
2227 /**
2228 * Used when the success action is not 'message'
2229 *
2230 * @since 2.05
2231 * @since 6.0 `$args['force_delay_redirect']` is added.
2232 *
2233 * @param array $args {
2234 * The args.
2235 *
2236 * @type string $conf_method The method.
2237 * @type object $form Form object.
2238 * @type int $entry_id Entry ID.
2239 * @type string $action The action event. Accepts `create` or `update`.
2240 * @type array $fields The array of fields.
2241 * @type int $force_delay_redirect Force to show the message before redirecting in case redirect method runs.
2242 * }
2243 */
2244 public static function run_success_action( $args ) {
2245 global $frm_vars;
2246 $extra_args = $args;
2247 unset( $extra_args['form'] );
2248
2249 do_action( 'frm_success_action', $args['conf_method'], $args['form'], $args['form']->options, $args['entry_id'], $extra_args );
2250
2251 $opt = ( ! isset( $args['action'] ) || $args['action'] === 'create' ) ? 'success' : 'edit';
2252
2253 $args['success_opt'] = $opt;
2254 $args['ajax'] = ! empty( $frm_vars['ajax'] );
2255
2256 if ( $args['conf_method'] === 'page' && is_numeric( $args['form']->options[ $opt . '_page_id' ] ) ) {
2257 self::load_page_after_submit( $args );
2258 } elseif ( $args['conf_method'] === 'redirect' ) {
2259 self::redirect_after_submit( $args );
2260 } else {
2261 self::show_message_after_save( $args );
2262 }
2263 }
2264
2265 /**
2266 * Gets met On Submit actions.
2267 *
2268 * @since 6.0
2269 *
2270 * @param array $args See {@see FrmFormsController::run_success_action()}.
2271 * @param string $event Form event. Default is `create`.
2272 * @return array Array of actions that meet the conditional logics.
2273 */
2274 public static function get_met_on_submit_actions( $args, $event = 'create' ) {
2275 if ( ! FrmOnSubmitHelper::form_has_migrated( $args['form'] ) ) {
2276 return array();
2277 }
2278
2279 // If a redirect action has already opened the URL in a new tab, we show the default message in the currect tab.
2280 if ( ! empty( self::$redirected_in_new_tab[ $args['form']->id ] ) ) {
2281 return array( FrmOnSubmitHelper::get_fallback_action_after_open_in_new_tab( $event ) );
2282 }
2283
2284 $entry = FrmEntry::getOne( $args['entry_id'], true );
2285 $actions = FrmOnSubmitHelper::get_actions( $args['form']->id );
2286 $met_actions = array();
2287 $has_redirect = false;
2288
2289 foreach ( $actions as $action ) {
2290 if ( ! in_array( $event, $action->post_content['event'], true ) ) {
2291 continue;
2292 }
2293
2294 if ( FrmFormAction::action_conditions_met( $action, $entry ) ) {
2295 continue;
2296 }
2297
2298 $action_type = FrmOnSubmitHelper::get_action_type( $action );
2299
2300 if ( 'redirect' === $action_type ) {
2301 if ( $has_redirect ) { // Do not process because we run the first redirect action only.
2302 continue;
2303 }
2304 }
2305
2306 if ( ! self::is_valid_on_submit_action( $action, $args, $event ) ) {
2307 continue;
2308 }
2309
2310 if ( 'redirect' === $action_type ) {
2311 $has_redirect = true;
2312 }
2313
2314 $met_actions[] = $action;
2315 unset( $action );
2316 }
2317
2318 $args['event'] = $event;
2319
2320 /**
2321 * Filters the On Submit actions that meet the conditional logics.
2322 *
2323 * @since 6.0
2324 *
2325 * @param array $met_actions Actions that meet the conditional logics.
2326 * @param array $args See {@see FrmFormsController::run_success_action()}. `$args['event']` is also added.
2327 */
2328 $met_actions = apply_filters( 'frm_get_met_on_submit_actions', $met_actions, $args );
2329
2330 if ( empty( $met_actions ) ) {
2331 $met_actions = array( FrmOnSubmitHelper::get_fallback_action( $event ) );
2332 }
2333
2334 return $met_actions;
2335 }
2336
2337 /**
2338 * Checks if a Confirmation action has the valid data.
2339 *
2340 * @since 6.1.2
2341 *
2342 * @param object $action Form action object.
2343 * @param array $args See {@see FrmFormsController::run_success_action()}.
2344 * @param string $event Form event. Default is `create`.
2345 * @return bool
2346 */
2347 private static function is_valid_on_submit_action( $action, $args, $event = 'create' ) {
2348 $action_type = FrmOnSubmitHelper::get_action_type( $action );
2349
2350 if ( 'redirect' === $action_type ) {
2351 // Run through frm_redirect_url filter. This is used for the valid action check.
2352 $action->post_content['success_url'] = apply_filters(
2353 'frm_redirect_url',
2354 $action->post_content['success_url'],
2355 $args['form'],
2356 $args + array( 'action' => $event )
2357 );
2358
2359 return ! empty( $action->post_content['success_url'] );
2360 }
2361
2362 if ( 'page' === $action_type ) {
2363 if ( empty( $action->post_content['success_page_id'] ) ) {
2364 return false;
2365 }
2366
2367 $page = get_post( $action->post_content['success_page_id'] );
2368 if ( ! $page || 'trash' === $page->post_status ) {
2369 return false;
2370 }
2371 }
2372
2373 return true;
2374 }
2375
2376 /**
2377 * Runs On Submit actions.
2378 *
2379 * @since 6.0
2380 *
2381 * @param array $args See inside {@see FrmFormsController::get_form_contents()} method.
2382 */
2383 public static function run_on_submit_actions( $args ) {
2384 $args['conf_method'] = self::get_confirmation_method(
2385 array(
2386 'form' => $args['form'],
2387 'entry_id' => $args['entry_id'],
2388 'action' => FrmOnSubmitHelper::current_event( $args ),
2389 )
2390 );
2391 if ( ! is_array( $args['conf_method'] ) ) {
2392 self::run_success_action( $args );
2393 return;
2394 }
2395
2396 // If conf_method is an array, run On Submit actions.
2397 if ( ! $args['conf_method'] ) {
2398 // Use default message.
2399 FrmOnSubmitHelper::populate_on_submit_data( $args['form']->options );
2400 self::run_success_action( $args );
2401 } elseif ( 1 === count( $args['conf_method'] ) ) {
2402 FrmOnSubmitHelper::populate_on_submit_data( $args['form']->options, reset( $args['conf_method'] ) );
2403 $args['conf_method'] = $args['form']->options['success_action'];
2404 self::run_success_action( $args );
2405 } else {
2406 self::run_multi_on_submit_actions( $args );
2407 }
2408 }
2409
2410 /**
2411 * Runs multiple success actions.
2412 *
2413 * @since 6.0
2414 *
2415 * @param array $args See {@see FrmFormsController::run_success_action()}.
2416 */
2417 public static function run_multi_on_submit_actions( $args ) {
2418 $redirect_action = null;
2419 foreach ( $args['conf_method'] as $action ) {
2420 if ( 'redirect' === FrmOnSubmitHelper::get_action_type( $action ) ) {
2421 // We catch the redirect action to run it last.
2422 $redirect_action = $action;
2423 continue;
2424 }
2425
2426 self::run_single_on_submit_action( $args, $action );
2427
2428 unset( $action );
2429 }
2430
2431 if ( $redirect_action ) {
2432 // Show script to delay the redirection.
2433 $args['force_delay_redirect'] = true;
2434 self::run_single_on_submit_action( $args, $redirect_action );
2435 }
2436 }
2437
2438 /**
2439 * Runs single On Submit action.
2440 *
2441 * @since 6.0
2442 *
2443 * @param array $args See {@see FrmFormsController::run_success_action()}.
2444 * @param object $action On Submit action object.
2445 */
2446 public static function run_single_on_submit_action( $args, $action ) {
2447 $new_args = self::get_run_success_action_args( $args, $action );
2448 self::run_success_action( $new_args );
2449 }
2450
2451 /**
2452 * Gets run_success_action() args from the 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 * @return array
2459 */
2460 private static function get_run_success_action_args( $args, $action ) {
2461 $new_args = $args;
2462
2463 FrmOnSubmitHelper::populate_on_submit_data( $new_args['form']->options, $action, $args['action'] );
2464
2465 $opt = 'update' === $args['action'] ? 'edit_' : 'success_';
2466
2467 $new_args['conf_method'] = $new_args['form']->options[ $opt . 'action' ];
2468
2469 /**
2470 * Filters the run success action args.
2471 *
2472 * @since 6.0
2473 *
2474 * @param array $new_args The new args.
2475 * @param array $args The old args. See {@see FrmFormsController::run_success_action()}.
2476 * @param object $action On Submit action object.
2477 */
2478 return apply_filters( 'frm_get_run_success_action_args', $new_args, $args, $action );
2479 }
2480
2481 /**
2482 * @since 3.0
2483 */
2484 private static function load_page_after_submit( $args ) {
2485 global $post;
2486 $opt = $args['success_opt'];
2487 if ( ! $post || $args['form']->options[ $opt . '_page_id' ] != $post->ID ) {
2488 $page = get_post( $args['form']->options[ $opt . '_page_id' ] );
2489 $old_post = $post;
2490 $post = $page;
2491 $content = apply_filters( 'frm_content', $page->post_content, $args['form'], $args['entry_id'] );
2492
2493 // Fix the On Submit page content doesn't show when previewing In theme.
2494 $has_preview_filter = has_filter( 'the_content', 'FrmFormsController::preview_content' );
2495
2496 if ( $has_preview_filter ) {
2497 remove_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
2498 }
2499
2500 echo apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2501
2502 if ( $has_preview_filter ) {
2503 add_filter( 'the_content', 'FrmFormsController::preview_content', 9999 );
2504 }
2505
2506 $post = $old_post;
2507 }
2508 }
2509
2510 /**
2511 * @since 3.0
2512 * @param array $args See {@see FrmFormsController::run_success_action()}.
2513 */
2514 private static function redirect_after_submit( $args ) {
2515 add_filter( 'frm_use_wpautop', '__return_false' );
2516
2517 $opt = $args['success_opt'];
2518 $success_url = trim( $args['form']->options[ $opt . '_url' ] );
2519 $success_url = apply_filters( 'frm_content', $success_url, $args['form'], $args['entry_id'] );
2520 $success_url = do_shortcode( $success_url );
2521
2522 $args['id'] = $args['entry_id'];
2523 FrmEntriesController::delete_entry_before_redirect( $success_url, $args['form'], $args );
2524
2525 add_filter( 'frm_redirect_url', 'FrmEntriesController::prepare_redirect_url' );
2526 $success_url = apply_filters( 'frm_redirect_url', $success_url, $args['form'], $args );
2527
2528 $doing_ajax = FrmAppHelper::doing_ajax();
2529
2530 if ( ! empty( $args['ajax'] ) && $doing_ajax && empty( $args['force_delay_redirect'] ) ) { // Is AJAX submit and there is just one Redirect action runs.
2531 echo json_encode( self::get_ajax_redirect_response_data( $args + compact( 'success_url' ) ) );
2532 wp_die();
2533 }
2534
2535 if ( ! headers_sent() && empty( $args['force_delay_redirect'] ) ) { // Not AJAX submit, no headers sent, and there is just one Redirect action runs.
2536 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2537 self::print_open_in_new_tab_js_with_fallback_handler( $success_url, $args );
2538 self::$redirected_in_new_tab[ $args['form']->id ] = 1;
2539 return;
2540 }
2541
2542 wp_redirect( esc_url_raw( $success_url ) );
2543 die(); // do not use wp_die or redirect fails
2544 }
2545
2546 // Redirect with a delay.
2547 self::redirect_after_submit_using_js( $args + compact( 'success_url', 'doing_ajax' ) );
2548 }
2549
2550 /**
2551 * Prints open in new tab js with fallback handler.
2552 *
2553 * @since 6.3.1
2554 *
2555 * @param string $success_url Success URL.
2556 * @param array $args See {@see FrmFormsController::redirect_after_submit()}.
2557 */
2558 private static function print_open_in_new_tab_js_with_fallback_handler( $success_url, $args ) {
2559 echo '<script>var newTab = window.open("' . esc_url_raw( $success_url ) . '", "_blank");';
2560 echo 'if ( ! newTab ) {';
2561
2562 $data = array(
2563 'formId' => intval( $args['form']->id ),
2564 'message' => self::get_redirect_fallback_message( $success_url, $args ),
2565 );
2566 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2567 echo 'frmShowNewTabFallback = ' . FrmAppHelper::maybe_json_encode( $data ) . ';';
2568 echo '}</script>';
2569 }
2570
2571 /**
2572 * Gets response data for redirect action when AJAX submitting.
2573 *
2574 * @since 6.3.1
2575 *
2576 * @param array $args See {@see FrmFormsController::run_success_action()}.
2577 * @return array
2578 */
2579 private static function get_ajax_redirect_response_data( $args ) {
2580 $response_data = array( 'redirect' => $args['success_url'] );
2581
2582 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2583 $response_data['openInNewTab'] = 1;
2584
2585 $args['message'] = FrmOnSubmitHelper::get_default_new_tab_msg();
2586
2587 $args['form']->options['success_msg'] = $args['message'];
2588 $args['form']->options['edit_msg'] = $args['message'];
2589 if ( ! isset( $args['fields'] ) ) {
2590 $args['fields'] = FrmField::get_all_for_form( $args['form']->id );
2591 }
2592
2593 $args['message'] = self::prepare_submit_message( $args['form'], $args['entry_id'], $args );
2594
2595 ob_start();
2596 self::show_lone_success_messsage( $args );
2597 $response_data['content'] = ob_get_clean();
2598
2599 $response_data['fallbackMsg'] = self::get_redirect_fallback_message( $args['success_url'], $args );
2600 }
2601
2602 return $response_data;
2603 }
2604
2605 /**
2606 * Redirects after submitting using JS. This is used when showing message before redirecting.
2607 *
2608 * @since 6.0
2609 *
2610 * @param array $args See {@see FrmFormsController::run_success_action()}.
2611 */
2612 private static function redirect_after_submit_using_js( $args ) {
2613 $success_msg = FrmOnSubmitHelper::get_default_redirect_msg();
2614 $redirect_msg = self::get_redirect_message( $args['success_url'], $success_msg, $args );
2615 $success_url = esc_url_raw( $args['success_url'] );
2616
2617 /**
2618 * Filters the delay time before redirecting when On Submit Redirect action is delayed.
2619 *
2620 * @since 6.0
2621 *
2622 * @param int $delay_time Delay time in miliseconds.
2623 */
2624 $delay_time = apply_filters( 'frm_redirect_delay_time', 8000 );
2625
2626 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2627 $redirect_js = 'window.open("' . $success_url . '", "_blank")';
2628 } else {
2629 $redirect_js = 'window.location="' . $success_url . '";';
2630 }
2631
2632 add_filter( 'frm_use_wpautop', '__return_true' );
2633
2634 echo FrmAppHelper::maybe_kses( $redirect_msg ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2635 echo '<script>';
2636 if ( empty( $args['doing_ajax'] ) ) { // Not AJAX submit, delay JS until window.load.
2637 echo 'window.onload=function(){';
2638 }
2639 echo 'setTimeout(function(){' . $redirect_js . '}, ' . intval( $delay_time ) . ');'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2640 if ( empty( $args['doing_ajax'] ) ) {
2641 echo '};';
2642 }
2643 echo '</script>';
2644 }
2645
2646 /**
2647 * @since 3.0
2648 *
2649 * @param string $success_url
2650 * @param string $success_msg
2651 * @param array $args
2652 */
2653 private static function get_redirect_message( $success_url, $success_msg, $args ) {
2654 $redirect_msg = '<div class="' . esc_attr( FrmFormsHelper::get_form_style_class( $args['form'] ) ) . '"><div class="frm-redirect-msg" role="status">' . $success_msg . '<br/>' .
2655 self::get_redirect_fallback_message( $success_url, $args ) .
2656 '</div></div>';
2657
2658 $redirect_args = array(
2659 'entry_id' => $args['entry_id'],
2660 'form_id' => $args['form']->id,
2661 'form' => $args['form'],
2662 );
2663
2664 return apply_filters( 'frm_redirect_msg', $redirect_msg, $redirect_args );
2665 }
2666
2667 /**
2668 * Gets fallback message when redirecting failed.
2669 *
2670 * @since 6.3.1
2671 *
2672 * @param string $success_url Redirect URL.
2673 * @param array $args Contains `form` object.
2674 * @return string
2675 */
2676 private static function get_redirect_fallback_message( $success_url, $args ) {
2677 $target = '';
2678 if ( ! empty( $args['form']->options['open_in_new_tab'] ) ) {
2679 $target = ' target="_blank"';
2680 }
2681
2682 return sprintf(
2683 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
2684 __( '%1$sClick here%2$s if you are not automatically redirected.', 'formidable' ),
2685 '<a href="' . esc_url( $success_url ) . '"' . $target . '>',
2686 '</a>'
2687 );
2688 }
2689
2690 /**
2691 * Prepare to show the success message and empty form after submit
2692 *
2693 * @since 2.05
2694 */
2695 public static function show_message_after_save( $atts ) {
2696 $atts['message'] = self::prepare_submit_message( $atts['form'], $atts['entry_id'], $atts );
2697
2698 if ( ! isset( $atts['form']->options['show_form'] ) || $atts['form']->options['show_form'] ) {
2699 if ( isset( $atts['action'] ) && 'update' === $atts['action'] && is_callable( array( 'FrmProEntriesController', 'show_front_end_form_with_entry' ) ) ) {
2700 $entry = FrmEntry::getOne( $atts['entry_id'] );
2701 if ( $entry ) {
2702 // This is copied from the Pro plugin.
2703 $atts['conf_message'] = FrmProEntriesController::confirmation( 'message', $atts['form'], $atts['form']->options, $entry->id, $atts );
2704 $atts['show_form'] = FrmProEntriesController::is_form_displayed_after_edit( $atts['form'] );
2705 FrmProEntriesController::show_front_end_form_with_entry( $entry, $atts );
2706 }
2707 } else {
2708 self::show_form_after_submit( $atts );
2709 }
2710 } else {
2711 self::show_lone_success_messsage( $atts );
2712 }
2713 }
2714
2715 /**
2716 * Show an empty form
2717 *
2718 * @since 2.05
2719 */
2720 private static function show_form_after_submit( $args ) {
2721 self::fill_atts_for_form_display( $args );
2722
2723 $errors = $args['errors'];
2724 $message = $args['message'];
2725 $form = $args['form'];
2726 $title = $args['title'];
2727 $description = $args['description'];
2728
2729 if ( empty( $args['fields'] ) ) {
2730 $values = array(
2731 'custom_style' => FrmAppHelper::custom_style_value( array() ),
2732 );
2733 } else {
2734 $values = FrmEntriesHelper::setup_new_vars( $args['fields'], $form, $args['reset'] );
2735 }
2736 unset( $args );
2737
2738 $include_form_tag = apply_filters( 'frm_include_form_tag', true, $form );
2739
2740 $frm_settings = FrmAppHelper::get_settings();
2741 $submit = isset( $form->options['submit_value'] ) ? $form->options['submit_value'] : $frm_settings->submit_value;
2742
2743 global $frm_vars;
2744 self::maybe_load_css( $form, $values['custom_style'], $frm_vars['load_css'] );
2745
2746 $message_placement = self::message_placement( $form, $message );
2747
2748 include FrmAppHelper::plugin_path() . '/classes/views/frm-entries/new.php';
2749 }
2750
2751 /**
2752 * @return string - 'before', 'after', or 'submit'
2753 *
2754 * @since 4.05.02
2755 */
2756 private static function message_placement( $form, $message ) {
2757 $place = 'before';
2758
2759 if ( $message && isset( $form->options['form_class'] ) ) {
2760 if ( strpos( $form->options['form_class'], 'frm_below_success' ) !== false ) {
2761 $place = 'after';
2762 } elseif ( strpos( $form->options['form_class'], 'frm_inline_success' ) !== false ) {
2763 $place = 'submit';
2764 }
2765 }
2766
2767 /**
2768 * @return string - 'before' or 'after'
2769 *
2770 * @since 4.05.02
2771 */
2772 return apply_filters( 'frm_message_placement', $place, compact( 'form', 'message' ) );
2773 }
2774
2775 /**
2776 * Get all the values needed on the new.php entry page
2777 *
2778 * @since 2.05
2779 */
2780 private static function fill_atts_for_form_display( &$args ) {
2781 if ( ! isset( $args['title'] ) && isset( $args['show_title'] ) ) {
2782 $args['title'] = $args['show_title'];
2783 }
2784
2785 if ( ! isset( $args['description'] ) && isset( $args['show_description'] ) ) {
2786 $args['description'] = $args['show_description'];
2787 }
2788
2789 $defaults = array(
2790 'errors' => array(),
2791 'message' => '',
2792 'fields' => array(),
2793 'form' => array(),
2794 'title' => true,
2795 'description' => false,
2796 'reset' => false,
2797 );
2798 $args = wp_parse_args( $args, $defaults );
2799 }
2800
2801 /**
2802 * Show the success message without the form
2803 *
2804 * @since 2.05
2805 */
2806 private static function show_lone_success_messsage( $atts ) {
2807 global $frm_vars;
2808 $values = FrmEntriesHelper::setup_new_vars( $atts['fields'], $atts['form'], true );
2809 self::maybe_load_css( $atts['form'], $values['custom_style'], $frm_vars['load_css'] );
2810
2811 $include_extra_container = 'frm_forms' . FrmFormsHelper::get_form_style_class( $values );
2812
2813 $errors = array();
2814 $form = $atts['form'];
2815 $message = $atts['message'];
2816
2817 include( FrmAppHelper::plugin_path() . '/classes/views/frm-entries/errors.php' );
2818 }
2819
2820 /**
2821 * Prepare the success message before it's shown
2822 *
2823 * @since 2.05
2824 * @since 6.0.x Added the third parameter.
2825 *
2826 * @param object $form Form object.
2827 * @param int $entry_id Entry ID.
2828 * @param array $args See {@see FrmFormsController::run_success_action()}.
2829 * @return string
2830 */
2831 private static function prepare_submit_message( $form, $entry_id, $args = array() ) {
2832 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
2833 $opt = isset( $args['success_opt'] ) ? $args['success_opt'] : 'success';
2834
2835 if ( $entry_id && is_numeric( $entry_id ) ) {
2836 $message = isset( $form->options[ $opt . '_msg' ] ) ? $form->options[ $opt . '_msg' ] : $frm_settings->success_msg;
2837 $class = 'frm_message';
2838 } else {
2839 $message = $frm_settings->failed_msg;
2840 $class = FrmFormsHelper::form_error_class();
2841 }
2842
2843 $message = FrmFormsHelper::get_success_message( compact( 'message', 'form', 'entry_id', 'class' ) );
2844
2845 return apply_filters( 'frm_main_feedback', $message, $form, $entry_id );
2846 }
2847
2848 /**
2849 * @return void
2850 */
2851 public static function front_head() {
2852 $version = FrmAppHelper::plugin_version();
2853 $suffix = FrmAppHelper::js_suffix();
2854
2855 if ( ! empty( $suffix ) && self::has_combo_js_file() ) {
2856 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . '/js/frm.min.js', array( 'jquery' ), $version, true );
2857 } else {
2858 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . "/js/formidable{$suffix}.js", array( 'jquery' ), $version, true );
2859 }
2860
2861 add_filter( 'script_loader_tag', 'FrmFormsController::defer_script_loading', 10, 2 );
2862
2863 if ( FrmAppHelper::is_admin() ) {
2864 // don't load this in back-end
2865 return;
2866 }
2867
2868 FrmAppHelper::localize_script( 'front' );
2869 FrmStylesController::enqueue_css( 'register' );
2870 }
2871
2872 /**
2873 * @since 3.0
2874 */
2875 public static function has_combo_js_file() {
2876 return is_readable( FrmAppHelper::plugin_path() . '/js/frm.min.js' );
2877 }
2878
2879 public static function maybe_load_css( $form, $this_load, $global_load ) {
2880 $load_css = FrmForm::is_form_loaded( $form, $this_load, $global_load );
2881
2882 if ( ! $load_css ) {
2883 return;
2884 }
2885
2886 global $frm_vars;
2887 self::footer_js( 'header' );
2888 $frm_vars['css_loaded'] = true;
2889
2890 self::load_late_css();
2891 }
2892
2893 /**
2894 * If css is loaded only on applicable pages, include it before the form loads
2895 * to prevent a flash of unstyled form.
2896 *
2897 * @since 4.01
2898 *
2899 * @return void
2900 */
2901 private static function load_late_css() {
2902 $frm_settings = FrmAppHelper::get_settings();
2903 $late_css = $frm_settings->load_style === 'dynamic';
2904
2905 if ( ! $late_css || ! self::should_load_late() ) {
2906 return;
2907 }
2908
2909 global $wp_styles;
2910 if ( is_array( $wp_styles->queue ) && in_array( 'formidable', $wp_styles->queue, true ) ) {
2911 wp_print_styles( 'formidable' );
2912 }
2913 }
2914
2915 /**
2916 * Avoid late load if All in One SEO is active because it prevents CSS from loading entirely.
2917 *
2918 * @since 5.2.03
2919 *
2920 * @return bool
2921 */
2922 private static function should_load_late() {
2923 return ! function_exists( 'aioseo' );
2924 }
2925
2926 public static function defer_script_loading( $tag, $handle ) {
2927 if ( 'captcha-api' == $handle && ! strpos( $tag, 'defer' ) ) {
2928 $tag = str_replace( ' src', ' defer="defer" async="async" src', $tag );
2929 }
2930
2931 return $tag;
2932 }
2933
2934 public static function footer_js( $location = 'footer' ) {
2935 global $frm_vars;
2936
2937 FrmStylesController::enqueue_css();
2938
2939 if ( ! FrmAppHelper::is_admin() && $location != 'header' && ! empty( $frm_vars['forms_loaded'] ) ) {
2940 // load formidable js
2941 wp_enqueue_script( 'formidable' );
2942 }
2943 }
2944
2945 /**
2946 * @since 2.0.8
2947 */
2948 private static function maybe_minimize_form( $atts, &$content ) {
2949 // check if minimizing is turned on
2950 if ( self::is_minification_on( $atts ) ) {
2951 $content = str_replace( array( "\r\n", "\r", "\n", "\t", ' ' ), '', $content );
2952 }
2953 }
2954
2955 /**
2956 * @since 2.0.8
2957 * @return boolean
2958 */
2959 private static function is_minification_on( $atts ) {
2960 return isset( $atts['minimize'] ) && ! empty( $atts['minimize'] );
2961 }
2962
2963 /**
2964 * @since 5.0.16
2965 *
2966 * @return void
2967 */
2968 public static function landing_page_preview_option() {
2969 $dir = apply_filters( 'frm_landing_page_preview_option', false );
2970 if ( false === $dir || ! file_exists( $dir . 'landing-page-preview-option.php' ) ) {
2971 $dir = self::get_form_views_path();
2972 }
2973 include $dir . 'landing-page-preview-option.php';
2974 }
2975
2976 /**
2977 * @since 5.0.16
2978 *
2979 * @return string
2980 */
2981 private static function get_form_views_path() {
2982 return FrmAppHelper::plugin_path() . '/classes/views/frm-forms/';
2983 }
2984
2985 /**
2986 * Create a page with an embedded formidable Gutenberg block.
2987 *
2988 * @since 5.2
2989 *
2990 * @return never
2991 */
2992 public static function create_page_with_shortcode() {
2993 if ( ! current_user_can( 'publish_posts' ) ) {
2994 die( 0 );
2995 }
2996
2997 check_ajax_referer( 'frm_ajax', 'nonce' );
2998
2999 $type = FrmAppHelper::get_post_param( 'type', '', 'sanitize_text_field' );
3000 if ( ! $type || ! in_array( $type, array( 'form', 'view' ), true ) ) {
3001 die( 0 );
3002 }
3003
3004 $object_id = FrmAppHelper::get_post_param( 'object_id', '', 'absint' );
3005 if ( ! $object_id ) {
3006 die( 0 );
3007 }
3008
3009 $postarr = array( 'post_type' => 'page' );
3010
3011 if ( 'form' === $type ) {
3012 $postarr['post_content'] = self::get_page_shortcode_content_for_form( $object_id );
3013 } else {
3014 $postarr['post_content'] = apply_filters( 'frm_create_page_with_' . $type . '_shortcode_content', '', $object_id );
3015 }
3016
3017 $name = FrmAppHelper::get_post_param( 'name', '', 'sanitize_text_field' );
3018 if ( $name ) {
3019 $postarr['post_title'] = $name;
3020 }
3021
3022 $success = wp_insert_post( $postarr );
3023 if ( ! is_numeric( $success ) || ! $success ) {
3024 die( 0 );
3025 }
3026
3027 wp_send_json(
3028 array(
3029 'redirect' => get_edit_post_link( $success, 'redirect' ),
3030 )
3031 );
3032 }
3033
3034 /**
3035 * @since 5.3
3036 *
3037 * @param string $content
3038 * @param int $form_id
3039 * @return string
3040 */
3041 private static function get_page_shortcode_content_for_form( $form_id ) {
3042 $shortcode = '[formidable id="' . $form_id . '"]';
3043 $html_comment_start = '<!-- wp:formidable/simple-form {"formId":"' . $form_id . '"} -->';
3044 $html_comment_end = '<!-- /wp:formidable/simple-form -->';
3045 return $html_comment_start . '<div>' . $shortcode . '</div>' . $html_comment_end;
3046 }
3047
3048 /**
3049 * Get page dropdown for AJAX request for embedding form in an existing page.
3050 *
3051 * @return never
3052 */
3053 public static function get_page_dropdown() {
3054 if ( ! current_user_can( 'publish_posts' ) ) {
3055 die( 0 );
3056 }
3057
3058 check_ajax_referer( 'frm_ajax', 'nonce' );
3059
3060 $html = FrmAppHelper::clip(
3061 function() {
3062 FrmAppHelper::maybe_autocomplete_pages_options(
3063 array(
3064 'field_name' => 'frm_page_dropdown',
3065 'page_id' => '',
3066 'placeholder' => __( 'Select a Page', 'formidable' ),
3067 )
3068 );
3069 }
3070 );
3071 $post_type_object = get_post_type_object( 'page' );
3072 wp_send_json(
3073 array(
3074 'html' => $html,
3075 'edit_page_url' => admin_url( sprintf( $post_type_object->_edit_link . '&action=edit', 0 ) ),
3076 )
3077 );
3078 }
3079
3080 /**
3081 * @deprecated 4.0
3082 */
3083 public static function new_form( $values = array() ) {
3084 FrmDeprecated::new_form( $values );
3085 }
3086
3087 /**
3088 * @deprecated 4.0
3089 */
3090 public static function create( $values = array() ) {
3091 _deprecated_function( __METHOD__, '4.0', 'FrmFormsController::update' );
3092 self::update( $values );
3093 }
3094
3095 /**
3096 * @deprecated 3.0
3097 * @codeCoverageIgnore
3098 */
3099 public static function bulk_create_template( $ids ) {
3100 return FrmDeprecated::bulk_create_template( $ids );
3101 }
3102
3103 /**
3104 * @deprecated 3.0
3105 * @codeCoverageIgnore
3106 */
3107 public static function edit_key() {
3108 FrmDeprecated::edit_key();
3109 }
3110
3111 /**
3112 * @deprecated 3.0
3113 * @codeCoverageIgnore
3114 */
3115 public static function edit_description() {
3116 FrmDeprecated::edit_description();
3117 }
3118
3119 /**
3120 * @deprecated 4.08
3121 * @since 3.06
3122 */
3123 public static function add_new() {
3124 _deprecated_function( __FUNCTION__, '4.08' );
3125 }
3126 }
3127