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

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

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