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

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