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

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

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