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

2,663 lines 76.0 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 /**
892 * @param mixed $hidden_columns
893 * @return array
894 */
895 public static function hidden_columns( $hidden_columns ) {
896 if ( ! is_array( $hidden_columns ) ) {
897 $hidden_columns = array();
898 }
899
900 $type = FrmAppHelper::get_simple_request(
901 array(
902 'param' => 'form_type',
903 'type' => 'request',
904 )
905 );
906
907 if ( $type === 'template' ) {
908 $hidden_columns[] = 'id';
909 $hidden_columns[] = 'form_key';
910 }
911
912 return $hidden_columns;
913 }
914
915 public static function save_per_page( $save, $option, $value ) {
916 if ( $option == 'formidable_page_formidable_per_page' ) {
917 $save = (int) $value;
918 }
919
920 return $save;
921 }
922
923 /**
924 * @return bool
925 */
926 public static function expired() {
927 global $frm_expired;
928 return $frm_expired;
929 }
930
931 /**
932 * Get data from api before rendering it so that we can flag the modal as expired
933 *
934 * @return void
935 */
936 public static function before_list_templates() {
937 global $frm_templates;
938 global $frm_expired;
939 global $frm_license_type;
940
941 $api = new FrmFormTemplateApi();
942 $frm_templates = $api->get_api_info();
943 $expired = false;
944 $license_type = '';
945 if ( isset( $frm_templates['error'] ) ) {
946 $error = $frm_templates['error']['message'];
947 $error = str_replace( 'utm_medium=addons', 'utm_medium=form-templates', $error );
948 $expired = 'expired' === $frm_templates['error']['code'];
949 $license_type = isset( $frm_templates['error']['type'] ) ? $frm_templates['error']['type'] : '';
950 unset( $frm_templates['error'] );
951 }
952
953 $frm_expired = $expired;
954 $frm_license_type = $license_type;
955 }
956
957 /**
958 * @return void
959 */
960 public static function list_templates() {
961 global $frm_templates;
962 global $frm_license_type;
963
964 $templates = $frm_templates;
965 $custom_templates = array();
966 $templates_by_category = array();
967
968 self::add_user_templates( $custom_templates );
969
970 foreach ( $templates as $template ) {
971 if ( ! isset( $template['categories'] ) ) {
972 continue;
973 }
974
975 foreach ( $template['categories'] as $category ) {
976 if ( ! isset( $templates_by_category[ $category ] ) ) {
977 $templates_by_category[ $category ] = array();
978 }
979
980 $templates_by_category[ $category ][] = $template;
981 }
982 }
983 unset( $template );
984
985 // Subcategories that are included elsewhere.
986 $redundant_cats = array( 'PayPal', 'Stripe', 'Twilio' );
987
988 $categories = array_keys( $templates_by_category );
989 $categories = array_diff( $categories, FrmFormsHelper::ignore_template_categories() );
990 $categories = array_diff( $categories, $redundant_cats );
991 sort( $categories );
992
993 array_walk(
994 $custom_templates,
995 function( &$template ) {
996 $template['custom'] = true;
997 }
998 );
999
1000 $my_templates_translation = __( 'My Templates', 'formidable' );
1001 $categories = array_merge( array( $my_templates_translation ), $categories );
1002 $pricing = FrmAppHelper::admin_upgrade_link( 'form-templates' );
1003 $license_type = $frm_license_type;
1004 $args = compact( 'pricing', 'license_type' );
1005 $where = apply_filters( 'frm_forms_dropdown', array(), '' );
1006 $forms = FrmForm::get_published_forms( $where );
1007 $view_path = FrmAppHelper::plugin_path() . '/classes/views/frm-forms/';
1008
1009 $templates_by_category[ $my_templates_translation ] = $custom_templates;
1010
1011 unset( $pricing, $license_type, $where );
1012 wp_enqueue_script( 'accordion' ); // register accordion for template groups
1013 require $view_path . 'list-templates.php';
1014 }
1015
1016 /**
1017 * @since 4.03.01
1018 */
1019 private static function get_template_categories( $templates ) {
1020 $categories = array();
1021 foreach ( $templates as $template ) {
1022 if ( isset( $template['categories'] ) ) {
1023 $categories = array_merge( $categories, $template['categories'] );
1024 }
1025 }
1026 $exclude_cats = FrmFormsHelper::ignore_template_categories();
1027 $categories = array_unique( $categories );
1028 $categories = array_diff( $categories, $exclude_cats );
1029 sort( $categories );
1030 return $categories;
1031 }
1032
1033 private static function add_user_templates( &$templates ) {
1034 $user_templates = array(
1035 'is_template' => 1,
1036 'default_template' => 0,
1037 );
1038 $user_templates = FrmForm::getAll( $user_templates, 'name' );
1039 foreach ( $user_templates as $template ) {
1040 $template = array(
1041 'id' => $template->id,
1042 'name' => $template->name,
1043 'key' => $template->form_key,
1044 'description' => $template->description,
1045 'url' => wp_nonce_url( admin_url( 'admin.php?page=formidable&frm_action=duplicate&id=' . absint( $template->id ) ) ),
1046 'released' => $template->created_at,
1047 'installed' => 1,
1048 );
1049 array_unshift( $templates, $template );
1050 unset( $template );
1051 }
1052 }
1053
1054 private static function get_edit_vars( $id, $errors = array(), $message = '', $create_link = false ) {
1055 global $frm_vars;
1056
1057 $form = FrmForm::getOne( $id );
1058 if ( ! $form ) {
1059 wp_die( esc_html__( 'You are trying to edit a form that does not exist.', 'formidable' ) );
1060 }
1061
1062 if ( $form->parent_form_id ) {
1063 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
1064 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>' ) );
1065 }
1066
1067 $frm_field_selection = FrmField::field_selection();
1068
1069 $fields = FrmField::get_all_for_form( $form->id );
1070
1071 // Automatically add end section fields if they don't exist (2.0 migration).
1072 $reset_fields = false;
1073 FrmFormsHelper::auto_add_end_section_fields( $form, $fields, $reset_fields );
1074
1075 if ( $reset_fields ) {
1076 $fields = FrmField::get_all_for_form( $form->id, '', 'exclude' );
1077 }
1078
1079 unset( $reset_fields );
1080
1081 $args = array( 'parent_form_id' => $form->id );
1082 $values = FrmAppHelper::setup_edit_vars( $form, 'forms', '', true, array(), $args );
1083
1084 /**
1085 * Allows modifying the list of fields in the form builder.
1086 *
1087 * @since 5.0.04
1088 *
1089 * @param object[] $fields Array of fields.
1090 * @param array $args The arguments. Contains `form`.
1091 */
1092 $values['fields'] = apply_filters( 'frm_fields_in_form_builder', $fields, compact( 'form' ) );
1093
1094 $edit_message = __( 'Form was successfully updated.', 'formidable' );
1095 if ( $form->is_template && $message == $edit_message ) {
1096 $message = __( 'Template was successfully updated.', 'formidable' );
1097 }
1098
1099 self::maybe_update_form_builder_message( $message );
1100
1101 $all_templates = FrmForm::getAll( array( 'is_template' => 1 ), 'name' );
1102 $has_fields = isset( $values['fields'] ) && ! empty( $values['fields'] );
1103
1104 if ( defined( 'DOING_AJAX' ) ) {
1105 wp_die();
1106 } else {
1107 require( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/edit.php' );
1108 }
1109 }
1110
1111 public static function update_form_builder_fields( $fields, $form ) {
1112 foreach ( $fields as $field ) {
1113 $field->do_not_include_icons = true;
1114 }
1115 return $fields;
1116 }
1117
1118 public static function maybe_update_form_builder_message( &$message ) {
1119 if ( 'form_duplicated' === FrmAppHelper::simple_get( 'message' ) ) {
1120 $message = __( 'Form was Successfully Copied', 'formidable' );
1121 }
1122 }
1123
1124 public static function get_settings_vars( $id, $errors = array(), $args = array() ) {
1125 FrmAppHelper::permission_check( 'frm_edit_forms' );
1126
1127 global $frm_vars;
1128
1129 if ( ! is_array( $args ) ) {
1130 // For reverse compatibility.
1131 $args = array(
1132 'message' => $args,
1133 );
1134 }
1135
1136 $defaults = array(
1137 'message' => '',
1138 'warnings' => array(),
1139 );
1140 $args = array_merge( $defaults, $args );
1141 $message = $args['message'];
1142 $warnings = $args['warnings'];
1143
1144 $form = FrmForm::getOne( $id );
1145 $fields = FrmField::get_all_for_form( $id );
1146 $values = FrmAppHelper::setup_edit_vars( $form, 'forms', $fields, true );
1147
1148 /**
1149 * Allows changing fields in the form settings.
1150 *
1151 * @since 5.0.04
1152 *
1153 * @param array $fields Array of fields.
1154 * @param array $args The arguments. Contains `form`.
1155 */
1156 $values['fields'] = apply_filters( 'frm_fields_in_settings', $values['fields'], compact( 'form' ) );
1157
1158 self::clean_submit_html( $values );
1159
1160 $sections = self::get_settings_tabs( $values );
1161 $current = FrmAppHelper::simple_get( 't', 'sanitize_title', 'advanced_settings' );
1162
1163 require( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings.php' );
1164 }
1165
1166 /**
1167 * @since 4.0
1168 */
1169 public static function form_publish_button( $atts ) {
1170 $values = $atts['values'];
1171 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/_publish_box.php' );
1172 }
1173
1174 /**
1175 * Get a list of all the settings tabs for the form settings page.
1176 *
1177 * @since 4.0
1178 *
1179 * @param array $values
1180 * @return array
1181 */
1182 private static function get_settings_tabs( $values ) {
1183 $sections = array(
1184 'advanced' => array(
1185 'name' => __( 'General', 'formidable' ),
1186 'title' => __( 'General Form Settings', 'formidable' ),
1187 'function' => array( __CLASS__, 'advanced_settings' ),
1188 'icon' => 'frm_icon_font frm_settings_icon',
1189 ),
1190 'email' => array(
1191 'name' => __( 'Actions & Notifications', 'formidable' ),
1192 'function' => array( 'FrmFormActionsController', 'email_settings' ),
1193 'id' => 'frm_notification_settings',
1194 'icon' => 'frm_icon_font frm_mail_bulk_icon',
1195 ),
1196 'permissions' => array(
1197 'name' => __( 'Form Permissions', 'formidable' ),
1198 'icon' => 'frm_icon_font frm_lock_icon',
1199 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1200 'data' => array(
1201 'medium' => 'permissions',
1202 'upgrade' => __( 'Form Permissions', 'formidable' ),
1203 'message' => __( 'Allow editing, protect forms and files, limit entries, and save drafts. Upgrade to get form and entry permissions.', 'formidable' ),
1204 'screenshot' => 'permissions.png',
1205 ),
1206 ),
1207 'scheduling' => array(
1208 'name' => __( 'Form Scheduling', 'formidable' ),
1209 'icon' => 'frm_icon_font frm_calendar_icon',
1210 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1211 'data' => array(
1212 'medium' => 'scheduling',
1213 'upgrade' => __( 'Form scheduling settings', 'formidable' ),
1214 'screenshot' => 'scheduling.png',
1215 ),
1216 ),
1217 'buttons' => array(
1218 'name' => __( 'Styling & Buttons', 'formidable' ),
1219 'class' => __CLASS__,
1220 'function' => 'buttons_settings',
1221 'icon' => 'frm_icon_font frm_pallet_icon',
1222 ),
1223 'landing' => array(
1224 'name' => __( 'Form Landing Page', 'formidable' ),
1225 'icon' => 'frm_icon_font frm_file_text_icon',
1226 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1227 'data' => FrmAppHelper::get_landing_page_upgrade_data_params(),
1228 ),
1229 'chat' => array(
1230 'name' => __( 'Conversational Forms', 'formidable' ),
1231 'icon' => 'frm_icon_font frm_chat_forms_icon',
1232 'html_class' => 'frm_show_upgrade_tab frm_noallow',
1233 'data' => FrmAppHelper::get_upgrade_data_params(
1234 'chat',
1235 array(
1236 'upgrade' => __( 'Conversational Forms', 'formidable' ),
1237 'message' => __( 'Ask one question at a time for automated conversations.', 'formidable' ),
1238 'screenshot' => 'chat.png',
1239 )
1240 ),
1241 ),
1242 'html' => array(
1243 'name' => __( 'Customize HTML', 'formidable' ),
1244 'class' => __CLASS__,
1245 'function' => 'html_settings',
1246 'icon' => 'frm_icon_font frm_code_icon',
1247 ),
1248 );
1249
1250 foreach ( array( 'landing', 'chat' ) as $feature ) {
1251 if ( ! FrmAppHelper::show_new_feature( $feature ) ) {
1252 unset( $sections[ $feature ] );
1253 }
1254 }
1255
1256 $sections = apply_filters( 'frm_add_form_settings_section', $sections, $values );
1257
1258 if ( FrmAppHelper::pro_is_installed() && ! FrmAppHelper::meets_min_pro_version( '4.0' ) ) {
1259 // Prevent settings from showing in 2 spots.
1260 unset( $sections['permissions'], $sections['scheduling'] );
1261 }
1262
1263 foreach ( $sections as $key => $section ) {
1264 $defaults = array(
1265 'html_class' => '',
1266 'name' => ucfirst( $key ),
1267 'icon' => 'frm_icon_font frm_settings_icon',
1268 );
1269
1270 $section = array_merge( $defaults, $section );
1271
1272 if ( ! isset( $section['anchor'] ) ) {
1273 $section['anchor'] = $key;
1274 }
1275 $section['anchor'] .= '_settings';
1276
1277 if ( ! isset( $section['title'] ) ) {
1278 $section['title'] = $section['name'];
1279 }
1280
1281 if ( ! isset( $section['id'] ) ) {
1282 $section['id'] = $section['anchor'];
1283 }
1284
1285 $sections[ $key ] = $section;
1286 }
1287
1288 return $sections;
1289 }
1290
1291 /**
1292 * @since 4.0
1293 *
1294 * @param array $values
1295 */
1296 public static function advanced_settings( $values ) {
1297 $first_h3 = 'frm_first_h3';
1298
1299 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-advanced.php';
1300 }
1301
1302 /**
1303 * @param array $values
1304 */
1305 private static function render_spam_settings( $values ) {
1306 if ( function_exists( 'akismet_http_post' ) ) {
1307 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/akismet.php';
1308 }
1309 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/honeypot.php';
1310 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/spam-settings/antispam.php';
1311 }
1312
1313 /**
1314 * @since 4.0
1315 *
1316 * @param array $values
1317 */
1318 public static function buttons_settings( $values ) {
1319 $styles = apply_filters( 'frm_get_style_opts', array() );
1320
1321 $frm_settings = FrmAppHelper::get_settings();
1322 $no_global_style = $frm_settings->load_style === 'none';
1323
1324 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-buttons.php' );
1325 }
1326
1327 /**
1328 * @since 4.0
1329 *
1330 * @param array $values
1331 */
1332 public static function html_settings( $values ) {
1333 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/settings-html.php' );
1334 }
1335
1336 /**
1337 * Replace old Submit Button href with new href to avoid errors in Chrome
1338 *
1339 * @since 2.03.08
1340 *
1341 * @param array|boolean $values
1342 */
1343 private static function clean_submit_html( &$values ) {
1344 if ( is_array( $values ) && isset( $values['submit_html'] ) ) {
1345 $values['submit_html'] = str_replace( 'javascript:void(0)', '#', $values['submit_html'] );
1346 }
1347 }
1348
1349 /**
1350 * Updates classes used in submit and prev buttons to avoid conflict with twenty twenty-one theme.
1351 *
1352 * @param array $classes
1353 *
1354 * @return array
1355 */
1356 public static function update_button_classes( $classes ) {
1357 if ( function_exists( 'twenty_twenty_one_setup' ) ) {
1358 $classes[] = 'has-text-color has-background';
1359 }
1360
1361 return $classes;
1362 }
1363
1364 public static function mb_tags_box( $form_id, $class = '' ) {
1365 $fields = FrmField::get_all_for_form( $form_id, '', 'include' );
1366
1367 /**
1368 * Allows modifying the list of fields in the tags box.
1369 *
1370 * @since 5.0.04
1371 *
1372 * @param array $fields The list of fields.
1373 * @param array $args The arguments. Contains `form_id`.
1374 */
1375 $fields = apply_filters( 'frm_fields_in_tags_box', $fields, compact( 'form_id' ) );
1376 $linked_forms = array();
1377 $col = 'one';
1378 $settings_tab = FrmAppHelper::is_admin_page( 'formidable' ) ? true : false;
1379
1380 $cond_shortcodes = apply_filters( 'frm_conditional_shortcodes', array() );
1381 $entry_shortcodes = self::get_shortcode_helpers( $settings_tab );
1382
1383 $advanced_helpers = self::advanced_helpers( compact( 'fields', 'form_id' ) );
1384
1385 include( FrmAppHelper::plugin_path() . '/classes/views/shared/mb_adv_info.php' );
1386 }
1387
1388 /**
1389 * @since 3.04.01
1390 */
1391 private static function advanced_helpers( $atts ) {
1392 $advanced_helpers = array(
1393 'default' => array(
1394 'heading' => __( 'Customize field values with the following parameters.', 'formidable' ),
1395 'codes' => self::get_advanced_shortcodes(),
1396 ),
1397 );
1398
1399 $user_fields = self::user_shortcodes();
1400 if ( ! empty( $user_fields ) ) {
1401 $user_helpers = array();
1402 foreach ( $user_fields as $uk => $uf ) {
1403 $user_helpers[ '|user_id| show="' . $uk . '"' ] = $uf;
1404 unset( $uk, $uf );
1405 }
1406
1407 $advanced_helpers['user_id'] = array(
1408 'codes' => $user_helpers,
1409 );
1410 }
1411
1412 /**
1413 * Add extra helper shortcodes on the Advanced tab in form settings and views
1414 *
1415 * @since 3.04.01
1416 *
1417 * @param array $advanced_helpers
1418 * @param array $atts - Includes fields and form_id
1419 */
1420 return apply_filters( 'frm_advanced_helpers', $advanced_helpers, $atts );
1421 }
1422
1423 /**
1424 * Get an array of the options to display in the advanced tab
1425 * of the customization panel
1426 *
1427 * @since 2.0.6
1428 */
1429 private static function get_advanced_shortcodes() {
1430 $adv_shortcodes = array(
1431 'x sep=", "' => array(
1432 'label' => __( 'Separator', 'formidable' ),
1433 'title' => __( 'Use a different separator for checkbox fields', 'formidable' ),
1434 ),
1435 'x format="d-m-Y"' => array(
1436 'label' => __( 'Date Format', 'formidable' ),
1437 ),
1438 'x show="field_label"' => array(
1439 'label' => __( 'Field Label', 'formidable' ),
1440 ),
1441 'x wpautop=0' => array(
1442 'label' => __( 'No Auto P', 'formidable' ),
1443 'title' => __( 'Do not automatically add any paragraphs or line breaks', 'formidable' ),
1444 ),
1445 );
1446 $adv_shortcodes = apply_filters( 'frm_advanced_shortcodes', $adv_shortcodes );
1447
1448 // __( 'Leave blank instead of defaulting to User Login', 'formidable' ) : blank=1
1449
1450 return $adv_shortcodes;
1451 }
1452
1453 /**
1454 * @since 3.04.01
1455 */
1456 private static function user_shortcodes() {
1457 $options = array(
1458 'ID' => __( 'User ID', 'formidable' ),
1459 'first_name' => __( 'First Name', 'formidable' ),
1460 'last_name' => __( 'Last Name', 'formidable' ),
1461 'display_name' => __( 'Display Name', 'formidable' ),
1462 'user_login' => __( 'User Login', 'formidable' ),
1463 'user_email' => __( 'Email', 'formidable' ),
1464 'avatar' => __( 'Avatar', 'formidable' ),
1465 'author_link' => __( 'Author Link', 'formidable' ),
1466 );
1467
1468 return apply_filters( 'frm_user_shortcodes', $options );
1469 }
1470
1471 /**
1472 * Get an array of the helper shortcodes to display in the customization panel
1473 *
1474 * @since 2.0.6
1475 */
1476 private static function get_shortcode_helpers( $settings_tab ) {
1477 $entry_shortcodes = array(
1478 'id' => __( 'Entry ID', 'formidable' ),
1479 'key' => __( 'Entry Key', 'formidable' ),
1480 'post_id' => __( 'Post ID', 'formidable' ),
1481 'ip' => __( 'User IP', 'formidable' ),
1482 'created-at' => __( 'Entry created', 'formidable' ),
1483 'updated-at' => __( 'Entry updated', 'formidable' ),
1484 '' => '',
1485 'siteurl' => __( 'Site URL', 'formidable' ),
1486 'sitename' => __( 'Site Name', 'formidable' ),
1487 );
1488
1489 if ( ! FrmAppHelper::pro_is_installed() ) {
1490 unset( $entry_shortcodes['post_id'] );
1491 }
1492
1493 if ( $settings_tab ) {
1494 $entry_shortcodes['default-message'] = __( 'Default Msg', 'formidable' );
1495 $entry_shortcodes['default-html'] = __( 'Default HTML', 'formidable' );
1496 $entry_shortcodes['default-plain'] = __( 'Default Plain', 'formidable' );
1497 $entry_shortcodes['form_name'] = __( 'Form Name', 'formidable' );
1498 }
1499
1500 /**
1501 * Use this hook to add or remove buttons in the helpers section
1502 * in the customization panel
1503 *
1504 * @since 2.0.6
1505 */
1506 $entry_shortcodes = apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab );
1507
1508 return $entry_shortcodes;
1509 }
1510
1511 /**
1512 * Insert the form class setting into the form
1513 */
1514 public static function form_classes( $form ) {
1515 if ( isset( $form->options['form_class'] ) ) {
1516 echo esc_attr( sanitize_text_field( $form->options['form_class'] ) );
1517 }
1518
1519 if ( ! empty( $form->options['js_validate'] ) ) {
1520 echo ' frm_js_validate ';
1521 self::add_js_validate_form_to_global_vars( $form );
1522 }
1523 }
1524
1525 /**
1526 * @since 5.0.03
1527 *
1528 * @param stdClass $form
1529 */
1530 public static function add_js_validate_form_to_global_vars( $form ) {
1531 global $frm_vars;
1532 if ( ! isset( $frm_vars['js_validate_forms'] ) ) {
1533 $frm_vars['js_validate_forms'] = array();
1534 }
1535 $frm_vars['js_validate_forms'][ $form->id ] = $form;
1536 }
1537
1538 public static function get_email_html() {
1539 FrmAppHelper::permission_check( 'frm_view_forms' );
1540 check_ajax_referer( 'frm_ajax', 'nonce' );
1541
1542 echo FrmEntriesController::show_entry_shortcode( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1543 array(
1544 'form_id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ),
1545 'default_email' => true,
1546 'plain_text' => FrmAppHelper::get_post_param( 'plain_text', '', 'absint' ),
1547 )
1548 );
1549 wp_die();
1550 }
1551
1552 /**
1553 * @param string $content
1554 * @param stdClass|string|int $form
1555 * @param stdClass|string|int|false $entry
1556 * @return string
1557 */
1558 public static function filter_content( $content, $form, $entry = false ) {
1559 $content = self::replace_form_name_shortcodes( $content, $form );
1560
1561 self::get_entry_by_param( $entry );
1562 if ( ! $entry ) {
1563 return $content;
1564 }
1565
1566 if ( is_object( $form ) ) {
1567 $form = $form->id;
1568 }
1569
1570 $shortcodes = FrmFieldsHelper::get_shortcodes( $content, $form );
1571 $content = apply_filters( 'frm_replace_content_shortcodes', $content, $entry, $shortcodes );
1572
1573 return $content;
1574 }
1575
1576 /**
1577 * Replace any [form_name] shortcodes in a string.
1578 *
1579 * @since 5.5
1580 *
1581 * @param string $string
1582 * @param stdClass|string|int $form
1583 * @return string
1584 */
1585 public static function replace_form_name_shortcodes( $string, $form ) {
1586 if ( false === strpos( $string, '[form_name]' ) ) {
1587 return $string;
1588 }
1589
1590 if ( ! is_object( $form ) ) {
1591 $form = FrmForm::getOne( $form );
1592 }
1593
1594 $form_name = is_object( $form ) ? $form->name : '';
1595 return str_replace( '[form_name]', $form_name, $string );
1596 }
1597
1598 /**
1599 * @param stdClass|string|int|false $entry
1600 * @return void
1601 */
1602 private static function get_entry_by_param( &$entry ) {
1603 if ( ! $entry || ! is_object( $entry ) ) {
1604 if ( ! $entry || ! is_numeric( $entry ) ) {
1605 $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
1606 }
1607
1608 FrmEntry::maybe_get_entry( $entry );
1609 }
1610 }
1611
1612 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
1613 return FrmFieldsHelper::replace_content_shortcodes( $content, $entry, $shortcodes );
1614 }
1615
1616 public static function process_bulk_form_actions( $errors ) {
1617 if ( ! $_REQUEST ) {
1618 return $errors;
1619 }
1620
1621 $bulkaction = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
1622 if ( $bulkaction == - 1 ) {
1623 $bulkaction = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
1624 }
1625
1626 if ( ! empty( $bulkaction ) && strpos( $bulkaction, 'bulk_' ) === 0 ) {
1627 FrmAppHelper::remove_get_action();
1628
1629 $bulkaction = str_replace( 'bulk_', '', $bulkaction );
1630 }
1631
1632 $ids = FrmAppHelper::get_param( 'item-action', '', 'get', 'sanitize_text_field' );
1633 if ( empty( $ids ) ) {
1634 $errors[] = __( 'No forms were specified', 'formidable' );
1635
1636 return $errors;
1637 }
1638
1639 $permission_error = FrmAppHelper::permission_nonce_error( '', '_wpnonce', 'bulk-toplevel_page_formidable' );
1640 if ( $permission_error !== false ) {
1641 $errors[] = $permission_error;
1642
1643 return $errors;
1644 }
1645
1646 if ( ! is_array( $ids ) ) {
1647 $ids = explode( ',', $ids );
1648 }
1649
1650 switch ( $bulkaction ) {
1651 case 'delete':
1652 $message = self::bulk_destroy( $ids );
1653 break;
1654 case 'trash':
1655 $message = self::bulk_trash( $ids );
1656 break;
1657 case 'untrash':
1658 $message = self::bulk_untrash( $ids );
1659 }
1660
1661 if ( isset( $message ) && ! empty( $message ) ) {
1662 $errors['message'] = $message;
1663 }
1664
1665 return $errors;
1666 }
1667
1668 public static function route() {
1669 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
1670 $vars = array();
1671 FrmAppHelper::include_svg();
1672
1673 if ( isset( $_POST['frm_compact_fields'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1674 FrmAppHelper::permission_check( 'frm_edit_forms' );
1675
1676 // Javascript needs to be allowed in some field settings.
1677 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
1678 $json_vars = htmlspecialchars_decode( nl2br( str_replace( '&quot;', '"', wp_unslash( $_POST['frm_compact_fields'] ) ) ) );
1679 $json_vars = json_decode( $json_vars, true );
1680 if ( empty( $json_vars ) ) {
1681 // json decoding failed so we should return an error message.
1682 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
1683 if ( 'edit' == $action ) {
1684 $action = 'update';
1685 }
1686
1687 add_filter( 'frm_validate_form', 'FrmFormsController::json_error' );
1688 } else {
1689 $vars = FrmAppHelper::json_to_array( $json_vars );
1690 $action = $vars[ $action ];
1691 unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1692 $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1693 $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1694 }
1695 } else {
1696 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
1697 if ( isset( $_REQUEST['delete_all'] ) ) {
1698 // Override the action for this page.
1699 $action = 'delete_all';
1700 }
1701 }
1702
1703 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1704 FrmAppHelper::trigger_hook_load( 'form' );
1705
1706 switch ( $action ) {
1707 case 'new':
1708 return self::new_form( $vars );
1709 case 'create':
1710 case 'edit':
1711 case 'update':
1712 case 'trash':
1713 case 'untrash':
1714 case 'destroy':
1715 case 'delete_all':
1716 case 'settings':
1717 case 'update_settings':
1718 return self::$action( $vars );
1719 case 'lite-reports':
1720 return self::no_reports( $vars );
1721 case 'views':
1722 return self::no_views( $vars );
1723 default:
1724 do_action( 'frm_form_action_' . $action );
1725 if ( apply_filters( 'frm_form_stop_action_' . $action, false ) ) {
1726 return;
1727 }
1728
1729 $action = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
1730 if ( $action == - 1 ) {
1731 $action = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
1732 }
1733
1734 if ( strpos( $action, 'bulk_' ) === 0 ) {
1735 FrmAppHelper::remove_get_action();
1736
1737 self::list_form();
1738 return;
1739 }
1740
1741 $message = FrmAppHelper::get_param( 'message' );
1742 if ( 'form_duplicate_error' === $message ) {
1743 self::display_forms_list( array(), '', array( __( 'There was a problem duplicating the form', 'formidable' ) ) );
1744 return;
1745 }
1746
1747 self::display_forms_list();
1748
1749 return;
1750 }
1751 }
1752
1753 public static function json_error( $errors ) {
1754 $errors['json'] = __( 'Abnormal HTML characters prevented your form from saving correctly', 'formidable' );
1755
1756 return $errors;
1757 }
1758
1759 /**
1760 * Education for premium features.
1761 *
1762 * @since 4.05
1763 */
1764 public static function add_form_style_tab_options() {
1765 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/add_form_style_options.php' );
1766 }
1767
1768 /**
1769 * Add education about views.
1770 *
1771 * @since 4.07
1772 */
1773 public static function no_views( $values = array() ) {
1774 FrmAppHelper::include_svg();
1775 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1776 $form = $id ? FrmForm::getOne( $id ) : false;
1777
1778 include FrmAppHelper::plugin_path() . '/classes/views/shared/views-info.php';
1779 }
1780
1781 /**
1782 * Add education about reports.
1783 *
1784 * @since 4.07
1785 */
1786 public static function no_reports( $values = array() ) {
1787 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1788 $form = $id ? FrmForm::getOne( $id ) : false;
1789
1790 include FrmAppHelper::plugin_path() . '/classes/views/shared/reports-info.php';
1791 }
1792
1793 /* FRONT-END FORMS */
1794 public static function admin_bar_css() {
1795 if ( is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
1796 return;
1797 }
1798
1799 self::move_menu_to_footer();
1800
1801 add_action( 'wp_before_admin_bar_render', 'FrmFormsController::admin_bar_configure' );
1802 FrmAppHelper::load_font_style();
1803 }
1804
1805 /**
1806 * @since 4.05.02
1807 */
1808 private static function move_menu_to_footer() {
1809 $settings = FrmAppHelper::get_settings();
1810 if ( empty( $settings->admin_bar ) ) {
1811 remove_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
1812 }
1813 }
1814
1815 public static function admin_bar_configure() {
1816 global $frm_vars;
1817 if ( empty( $frm_vars['forms_loaded'] ) ) {
1818 return;
1819 }
1820
1821 $actions = array();
1822 foreach ( $frm_vars['forms_loaded'] as $form ) {
1823 if ( is_object( $form ) ) {
1824 $actions[ $form->id ] = $form->name;
1825 }
1826 unset( $form );
1827 }
1828
1829 if ( empty( $actions ) ) {
1830 return;
1831 }
1832
1833 self::add_menu_to_admin_bar();
1834 self::add_forms_to_admin_bar( $actions );
1835 }
1836
1837 /**
1838 * @since 2.05.07
1839 */
1840 public static function add_menu_to_admin_bar() {
1841 global $wp_admin_bar;
1842
1843 $wp_admin_bar->add_node(
1844 array(
1845 'id' => 'frm-forms',
1846 'title' => '<span class="ab-icon"></span><span class="ab-label">' . FrmAppHelper::get_menu_name() . '</span>',
1847 'href' => admin_url( 'admin.php?page=formidable' ),
1848 'meta' => array(
1849 'title' => FrmAppHelper::get_menu_name(),
1850 ),
1851 )
1852 );
1853 }
1854
1855 /**
1856 * @since 2.05.07
1857 */
1858 private static function add_forms_to_admin_bar( $actions ) {
1859 global $wp_admin_bar;
1860
1861 asort( $actions );
1862
1863 foreach ( $actions as $form_id => $name ) {
1864
1865 $wp_admin_bar->add_node(
1866 array(
1867 'parent' => 'frm-forms',
1868 'id' => 'edit_form_' . $form_id,
1869 'title' => empty( $name ) ? __( '(no title)', 'formidable' ) : $name,
1870 'href' => FrmForm::get_edit_link( $form_id ),
1871 )
1872 );
1873 }
1874 }
1875
1876 /**
1877 * The formidable shortcode
1878 *
1879 * @param array $atts The params from the shortcode.
1880 * @return string
1881 */
1882 public static function get_form_shortcode( $atts ) {
1883 global $frm_vars;
1884 if ( isset( $frm_vars['skip_shortcode'] ) && $frm_vars['skip_shortcode'] ) {
1885 $sc = '[formidable';
1886 $sc .= FrmAppHelper::array_to_html_params( $atts );
1887 return $sc . ']';
1888 }
1889
1890 $shortcode_atts = shortcode_atts(
1891 array(
1892 'id' => '',
1893 'key' => '',
1894 'title' => 'auto',
1895 'description' => 'auto',
1896 'readonly' => false,
1897 'entry_id' => false,
1898 'fields' => array(),
1899 'exclude_fields' => array(),
1900 'minimize' => false,
1901 ),
1902 $atts
1903 );
1904 do_action( 'formidable_shortcode_atts', $shortcode_atts, $atts );
1905
1906 return self::show_form( $shortcode_atts['id'], $shortcode_atts['key'], $shortcode_atts['title'], $shortcode_atts['description'], $atts );
1907 }
1908
1909 /**
1910 * @since 5.2.01
1911 *
1912 * @param string|int|false $id
1913 * @param string|false $key
1914 * @return stdClass|false
1915 */
1916 private static function maybe_get_form_by_id_or_key( $id, $key ) {
1917 if ( ! $id ) {
1918 $id = $key;
1919 }
1920 return self::maybe_get_form_to_show( $id );
1921 }
1922
1923 /**
1924 * @param string|int|false $id
1925 * @param string|false $key
1926 * @param string|int|bool $title may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
1927 * @param string|int|bool $description may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
1928 * @param array $atts
1929 * @return string
1930 */
1931 public static function show_form( $id = '', $key = '', $title = false, $description = false, $atts = array() ) {
1932 $form = self::maybe_get_form_by_id_or_key( $id, $key );
1933
1934 if ( ! $form ) {
1935 return __( 'Please select a valid form', 'formidable' );
1936 }
1937
1938 if ( 'auto' === $title ) {
1939 $title = ! empty( $form->options['show_title'] );
1940 }
1941
1942 if ( 'auto' === $description ) {
1943 $description = ! empty( $form->options['show_description'] );
1944 }
1945
1946 FrmAppController::maybe_update_styles();
1947
1948 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1949 FrmAppHelper::trigger_hook_load( 'form', $form );
1950
1951 $form = apply_filters( 'frm_pre_display_form', $form );
1952
1953 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
1954
1955 if ( self::is_viewable_draft_form( $form ) ) {
1956 // don't show a draft form on a page
1957 $form = __( 'Please select a valid form', 'formidable' );
1958 } elseif ( ! FrmForm::is_visible_to_user( $form ) ) {
1959 $form = do_shortcode( $frm_settings->login_msg );
1960 } else {
1961 do_action( 'frm_pre_get_form', $form );
1962 $form = self::get_form( $form, $title, $description, $atts );
1963
1964 /**
1965 * Use this shortcode to check for external shortcodes that may span
1966 * across multiple fields in the customizable HTML
1967 *
1968 * @since 2.0.8
1969 */
1970 $form = apply_filters( 'frm_filter_final_form', $form );
1971 }
1972
1973 return $form;
1974 }
1975
1976 /**
1977 * @param string|int|false $id
1978 * @return stdClass|false
1979 */
1980 private static function maybe_get_form_to_show( $id ) {
1981 $form = false;
1982
1983 if ( ! empty( $id ) ) { // form id or key is set
1984 $form = FrmForm::getOne( $id );
1985 if ( ! $form || $form->parent_form_id || $form->status === 'trash' ) {
1986 $form = false;
1987 }
1988 }
1989
1990 return $form;
1991 }
1992
1993 private static function is_viewable_draft_form( $form ) {
1994 return $form->status === 'draft' && current_user_can( 'frm_edit_forms' ) && ! FrmAppHelper::is_preview_page();
1995 }
1996
1997 public static function get_form( $form, $title, $description, $atts = array() ) {
1998 ob_start();
1999
2000 do_action( 'frm_before_get_form', $atts );
2001
2002 self::get_form_contents( $form, $title, $description, $atts );
2003 self::enqueue_scripts( FrmForm::get_params( $form ) );
2004
2005 $contents = ob_get_contents();
2006 ob_end_clean();
2007
2008 self::maybe_minimize_form( $atts, $contents );
2009
2010 return $contents;
2011 }
2012
2013 public static function enqueue_scripts( $params ) {
2014 do_action( 'frm_enqueue_form_scripts', $params );
2015 }
2016
2017 public static function get_form_contents( $form, $title, $description, $atts ) {
2018 $params = FrmForm::get_params( $form );
2019 $errors = self::get_saved_errors( $form, $params );
2020 $fields = FrmFieldsHelper::get_form_fields( $form->id, $errors );
2021 $reset = false;
2022 $pass_args = compact( 'form', 'fields', 'errors', 'title', 'description', 'reset' );
2023
2024 $handle_process_here = $params['action'] === 'create' && $params['posted_form_id'] == $form->id && $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
2025
2026 if ( ! $handle_process_here ) {
2027 do_action( 'frm_display_form_action', $params, $fields, $form, $title, $description );
2028 if ( apply_filters( 'frm_continue_to_new', true, $form->id, $params['action'] ) ) {
2029 self::show_form_after_submit( $pass_args );
2030 }
2031 } elseif ( ! empty( $errors ) ) {
2032 self::show_form_after_submit( $pass_args );
2033
2034 } else {
2035
2036 do_action( 'frm_validate_form_creation', $params, $fields, $form, $title, $description );
2037
2038 if ( apply_filters( 'frm_continue_to_create', true, $form->id ) ) {
2039 $entry_id = self::just_created_entry( $form->id );
2040 $pass_args['entry_id'] = $entry_id;
2041 $pass_args['reset'] = true;
2042 $pass_args['conf_method'] = self::get_confirmation_method( compact( 'form', 'entry_id' ) );
2043
2044 self::run_success_action( $pass_args );
2045
2046 do_action(
2047 'frm_after_entry_processed',
2048 array(
2049 'entry_id' => $entry_id,
2050 'form' => $form,
2051 )
2052 );
2053 }
2054 }
2055 }
2056
2057 /**
2058 * If the form was processed earlier (init), get the generated errors
2059 *
2060 * @since 2.05
2061 */
2062 private static function get_saved_errors( $form, $params ) {
2063 global $frm_vars;
2064
2065 if ( $params['posted_form_id'] == $form->id && $_POST && isset( $frm_vars['created_entries'][ $form->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
2066 $errors = $frm_vars['created_entries'][ $form->id ]['errors'];
2067 } else {
2068 $errors = array();
2069 }
2070
2071 /**
2072 * Allows modifying the generated errors if the form was processed earlier.
2073 *
2074 * @since 5.2.03
2075 *
2076 * @param array $errors Errors data. Is empty array if no errors found.
2077 * @param array $params Form params. See {@see FrmForm::get_params()}.
2078 */
2079 return apply_filters( 'frm_saved_errors', $errors, $params );
2080 }
2081
2082 /**
2083 * @since 2.2.7
2084 */
2085 public static function just_created_entry( $form_id ) {
2086 global $frm_vars;
2087
2088 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;
2089 }
2090
2091 /**
2092 * @since 3.0
2093 */
2094 private static function get_confirmation_method( $atts ) {
2095 $opt = 'success_action';
2096 $method = ( isset( $atts['form']->options[ $opt ] ) && ! empty( $atts['form']->options[ $opt ] ) ) ? $atts['form']->options[ $opt ] : 'message';
2097 $method = apply_filters( 'frm_success_filter', $method, $atts['form'], 'create' );
2098
2099 if ( $method != 'message' && ( ! $atts['entry_id'] || ! is_numeric( $atts['entry_id'] ) ) ) {
2100 $method = 'message';
2101 }
2102
2103 return $method;
2104 }
2105
2106 public static function maybe_trigger_redirect( $form, $params, $args ) {
2107 if ( ! isset( $params['id'] ) ) {
2108 global $frm_vars;
2109 $params['id'] = $frm_vars['created_entries'][ $form->id ]['entry_id'];
2110 }
2111
2112 $conf_method = self::get_confirmation_method(
2113 array(
2114 'form' => $form,
2115 'entry_id' => $params['id'],
2116 )
2117 );
2118
2119 if ( 'redirect' === $conf_method ) {
2120 self::trigger_redirect( $form, $params, $args );
2121 }
2122 }
2123
2124 public static function trigger_redirect( $form, $params, $args ) {
2125 $success_args = array(
2126 'action' => $params['action'],
2127 'conf_method' => 'redirect',
2128 'form' => $form,
2129 'entry_id' => $params['id'],
2130 );
2131
2132 if ( isset( $args['ajax'] ) ) {
2133 $success_args['ajax'] = $args['ajax'];
2134 }
2135
2136 self::run_success_action( $success_args );
2137 }
2138
2139 /**
2140 * Used when the success action is not 'message'
2141 *
2142 * @since 2.05
2143 */
2144 public static function run_success_action( $args ) {
2145 $extra_args = $args;
2146 unset( $extra_args['form'] );
2147
2148 do_action( 'frm_success_action', $args['conf_method'], $args['form'], $args['form']->options, $args['entry_id'], $extra_args );
2149
2150 $opt = ( ! isset( $args['action'] ) || $args['action'] === 'create' ) ? 'success' : 'edit';
2151
2152 $args['success_opt'] = $opt;
2153 if ( $args['conf_method'] === 'page' && is_numeric( $args['form']->options[ $opt . '_page_id' ] ) ) {
2154 self::load_page_after_submit( $args );
2155 } elseif ( $args['conf_method'] === 'redirect' ) {
2156 self::redirect_after_submit( $args );
2157 } else {
2158 self::show_message_after_save( $args );
2159 }
2160 }
2161
2162 /**
2163 * @since 3.0
2164 */
2165 private static function load_page_after_submit( $args ) {
2166 global $post;
2167 $opt = $args['success_opt'];
2168 if ( ! $post || $args['form']->options[ $opt . '_page_id' ] != $post->ID ) {
2169 $page = get_post( $args['form']->options[ $opt . '_page_id' ] );
2170 $old_post = $post;
2171 $post = $page;
2172 $content = apply_filters( 'frm_content', $page->post_content, $args['form'], $args['entry_id'] );
2173 echo apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2174 $post = $old_post;
2175 }
2176 }
2177
2178 /**
2179 * @since 3.0
2180 */
2181 private static function redirect_after_submit( $args ) {
2182 global $frm_vars;
2183
2184 add_filter( 'frm_use_wpautop', '__return_false' );
2185
2186 $opt = $args['success_opt'];
2187 $success_url = trim( $args['form']->options[ $opt . '_url' ] );
2188 $success_url = apply_filters( 'frm_content', $success_url, $args['form'], $args['entry_id'] );
2189 $success_url = do_shortcode( $success_url );
2190
2191 $success_msg = isset( $args['form']->options[ $opt . '_msg' ] ) ? $args['form']->options[ $opt . '_msg' ] : __( 'Please wait while you are redirected.', 'formidable' );
2192
2193 $redirect_msg = self::get_redirect_message( $success_url, $success_msg, $args );
2194
2195 $args['id'] = $args['entry_id'];
2196 FrmEntriesController::delete_entry_before_redirect( $success_url, $args['form'], $args );
2197
2198 add_filter( 'frm_redirect_url', 'FrmEntriesController::prepare_redirect_url' );
2199 $success_url = apply_filters( 'frm_redirect_url', $success_url, $args['form'], $args );
2200
2201 $doing_ajax = FrmAppHelper::doing_ajax();
2202
2203 if ( isset( $args['ajax'] ) && $args['ajax'] && $doing_ajax ) {
2204 echo json_encode( array( 'redirect' => $success_url ) );
2205 wp_die();
2206 } elseif ( ! headers_sent() ) {
2207 wp_redirect( esc_url_raw( $success_url ) );
2208 die(); // do not use wp_die or redirect fails
2209 } else {
2210 add_filter( 'frm_use_wpautop', '__return_true' );
2211 echo FrmAppHelper::maybe_kses( $redirect_msg ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2212 echo "<script type='text/javascript'>window.onload = function(){setTimeout(window.location='" . esc_url_raw( $success_url ) . "', 8000);}</script>";
2213 }
2214 }
2215
2216 /**
2217 * @since 3.0
2218 *
2219 * @param string $success_url
2220 * @param string $success_msg
2221 * @param array $args
2222 */
2223 private static function get_redirect_message( $success_url, $success_msg, $args ) {
2224 $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/>' .
2225 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
2226 sprintf( __( '%1$sClick here%2$s if you are not automatically redirected.', 'formidable' ), '<a href="' . esc_url( $success_url ) . '">', '</a>' ) .
2227 '</div></div>';
2228
2229 $redirect_args = array(
2230 'entry_id' => $args['entry_id'],
2231 'form_id' => $args['form']->id,
2232 'form' => $args['form'],
2233 );
2234
2235 return apply_filters( 'frm_redirect_msg', $redirect_msg, $redirect_args );
2236 }
2237
2238 /**
2239 * Prepare to show the success message and empty form after submit
2240 *
2241 * @since 2.05
2242 */
2243 public static function show_message_after_save( $atts ) {
2244 $atts['message'] = self::prepare_submit_message( $atts['form'], $atts['entry_id'] );
2245
2246 if ( ! isset( $atts['form']->options['show_form'] ) || $atts['form']->options['show_form'] ) {
2247 self::show_form_after_submit( $atts );
2248 } else {
2249 self::show_lone_success_messsage( $atts );
2250 }
2251 }
2252
2253 /**
2254 * Show an empty form
2255 *
2256 * @since 2.05
2257 */
2258 private static function show_form_after_submit( $args ) {
2259 self::fill_atts_for_form_display( $args );
2260
2261 $errors = $args['errors'];
2262 $message = $args['message'];
2263 $form = $args['form'];
2264 $title = $args['title'];
2265 $description = $args['description'];
2266
2267 if ( empty( $args['fields'] ) ) {
2268 $values = array(
2269 'custom_style' => FrmAppHelper::custom_style_value( array() ),
2270 );
2271 } else {
2272 $values = FrmEntriesHelper::setup_new_vars( $args['fields'], $form, $args['reset'] );
2273 }
2274 unset( $args );
2275
2276 $include_form_tag = apply_filters( 'frm_include_form_tag', true, $form );
2277
2278 $frm_settings = FrmAppHelper::get_settings();
2279 $submit = isset( $form->options['submit_value'] ) ? $form->options['submit_value'] : $frm_settings->submit_value;
2280
2281 global $frm_vars;
2282 self::maybe_load_css( $form, $values['custom_style'], $frm_vars['load_css'] );
2283
2284 $message_placement = self::message_placement( $form, $message );
2285
2286 include FrmAppHelper::plugin_path() . '/classes/views/frm-entries/new.php';
2287 }
2288
2289 /**
2290 * @return string - 'before', 'after', or 'submit'
2291 *
2292 * @since 4.05.02
2293 */
2294 private static function message_placement( $form, $message ) {
2295 $place = 'before';
2296
2297 if ( $message && isset( $form->options['form_class'] ) ) {
2298 if ( strpos( $form->options['form_class'], 'frm_below_success' ) !== false ) {
2299 $place = 'after';
2300 } elseif ( strpos( $form->options['form_class'], 'frm_inline_success' ) !== false ) {
2301 $place = 'submit';
2302 }
2303 }
2304
2305 /**
2306 * @return string - 'before' or 'after'
2307 *
2308 * @since 4.05.02
2309 */
2310 return apply_filters( 'frm_message_placement', $place, compact( 'form', 'message' ) );
2311 }
2312
2313 /**
2314 * Get all the values needed on the new.php entry page
2315 *
2316 * @since 2.05
2317 */
2318 private static function fill_atts_for_form_display( &$args ) {
2319 $defaults = array(
2320 'errors' => array(),
2321 'message' => '',
2322 'fields' => array(),
2323 'form' => array(),
2324 'title' => true,
2325 'description' => false,
2326 'reset' => false,
2327 );
2328 $args = wp_parse_args( $args, $defaults );
2329 }
2330
2331 /**
2332 * Show the success message without the form
2333 *
2334 * @since 2.05
2335 */
2336 private static function show_lone_success_messsage( $atts ) {
2337 global $frm_vars;
2338 $values = FrmEntriesHelper::setup_new_vars( $atts['fields'], $atts['form'], true );
2339 self::maybe_load_css( $atts['form'], $values['custom_style'], $frm_vars['load_css'] );
2340
2341 $include_extra_container = 'frm_forms' . FrmFormsHelper::get_form_style_class( $values );
2342
2343 $errors = array();
2344 $form = $atts['form'];
2345 $message = $atts['message'];
2346
2347 include( FrmAppHelper::plugin_path() . '/classes/views/frm-entries/errors.php' );
2348 }
2349
2350 /**
2351 * Prepare the success message before it's shown
2352 *
2353 * @since 2.05
2354 */
2355 private static function prepare_submit_message( $form, $entry_id ) {
2356 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
2357
2358 if ( $entry_id && is_numeric( $entry_id ) ) {
2359 $message = isset( $form->options['success_msg'] ) ? $form->options['success_msg'] : $frm_settings->success_msg;
2360 $class = 'frm_message';
2361 } else {
2362 $message = $frm_settings->failed_msg;
2363 $class = FrmFormsHelper::form_error_class();
2364 }
2365
2366 $message = FrmFormsHelper::get_success_message( compact( 'message', 'form', 'entry_id', 'class' ) );
2367
2368 return apply_filters( 'frm_main_feedback', $message, $form, $entry_id );
2369 }
2370
2371 public static function front_head() {
2372 $version = FrmAppHelper::plugin_version();
2373 $suffix = FrmAppHelper::js_suffix();
2374
2375 if ( ! empty( $suffix ) && self::has_combo_js_file() ) {
2376 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . '/js/frm.min.js', array( 'jquery' ), $version, true );
2377 } else {
2378 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . "/js/formidable{$suffix}.js", array( 'jquery' ), $version, true );
2379 }
2380
2381 add_filter( 'script_loader_tag', 'FrmFormsController::defer_script_loading', 10, 2 );
2382
2383 if ( FrmAppHelper::is_admin() ) {
2384 // don't load this in back-end
2385 return;
2386 }
2387
2388 FrmAppHelper::localize_script( 'front' );
2389 FrmStylesController::enqueue_css( 'register' );
2390 }
2391
2392 /**
2393 * @since 3.0
2394 */
2395 public static function has_combo_js_file() {
2396 return is_readable( FrmAppHelper::plugin_path() . '/js/frm.min.js' );
2397 }
2398
2399 public static function maybe_load_css( $form, $this_load, $global_load ) {
2400 $load_css = FrmForm::is_form_loaded( $form, $this_load, $global_load );
2401
2402 if ( ! $load_css ) {
2403 return;
2404 }
2405
2406 global $frm_vars;
2407 self::footer_js( 'header' );
2408 $frm_vars['css_loaded'] = true;
2409
2410 self::load_late_css();
2411 }
2412
2413 /**
2414 * If css is loaded only on applicable pages, include it before the form loads
2415 * to prevent a flash of unstyled form.
2416 *
2417 * @since 4.01
2418 *
2419 * @return void
2420 */
2421 private static function load_late_css() {
2422 $frm_settings = FrmAppHelper::get_settings();
2423 $late_css = $frm_settings->load_style === 'dynamic';
2424
2425 if ( ! $late_css || ! self::should_load_late() ) {
2426 return;
2427 }
2428
2429 global $wp_styles;
2430 if ( is_array( $wp_styles->queue ) && in_array( 'formidable', $wp_styles->queue, true ) ) {
2431 wp_print_styles( 'formidable' );
2432 }
2433 }
2434
2435 /**
2436 * Avoid late load if All in One SEO is active because it prevents CSS from loading entirely.
2437 *
2438 * @since 5.2.03
2439 *
2440 * @return bool
2441 */
2442 private static function should_load_late() {
2443 return ! function_exists( 'aioseo' );
2444 }
2445
2446 public static function defer_script_loading( $tag, $handle ) {
2447 if ( 'captcha-api' == $handle && ! strpos( $tag, 'defer' ) ) {
2448 $tag = str_replace( ' src', ' defer="defer" async="async" src', $tag );
2449 }
2450
2451 return $tag;
2452 }
2453
2454 public static function footer_js( $location = 'footer' ) {
2455 global $frm_vars;
2456
2457 FrmStylesController::enqueue_css();
2458
2459 if ( ! FrmAppHelper::is_admin() && $location != 'header' && ! empty( $frm_vars['forms_loaded'] ) ) {
2460 // load formidable js
2461 wp_enqueue_script( 'formidable' );
2462 }
2463 }
2464
2465 /**
2466 * @since 2.0.8
2467 */
2468 private static function maybe_minimize_form( $atts, &$content ) {
2469 // check if minimizing is turned on
2470 if ( self::is_minification_on( $atts ) ) {
2471 $content = str_replace( array( "\r\n", "\r", "\n", "\t", ' ' ), '', $content );
2472 }
2473 }
2474
2475 /**
2476 * @since 2.0.8
2477 * @return boolean
2478 */
2479 private static function is_minification_on( $atts ) {
2480 return isset( $atts['minimize'] ) && ! empty( $atts['minimize'] );
2481 }
2482
2483 /**
2484 * @since 5.0.16
2485 *
2486 * @return void
2487 */
2488 public static function landing_page_preview_option() {
2489 $dir = apply_filters( 'frm_landing_page_preview_option', false );
2490 if ( false === $dir || ! file_exists( $dir . 'landing-page-preview-option.php' ) ) {
2491 $dir = self::get_form_views_path();
2492 }
2493 include $dir . 'landing-page-preview-option.php';
2494 }
2495
2496 /**
2497 * @since 5.0.16
2498 *
2499 * @return string
2500 */
2501 private static function get_form_views_path() {
2502 return FrmAppHelper::plugin_path() . '/classes/views/frm-forms/';
2503 }
2504
2505 /**
2506 * Create a page with an embedded formidable Gutenberg block.
2507 *
2508 * @since 5.2
2509 *
2510 * @return never
2511 */
2512 public static function create_page_with_shortcode() {
2513 if ( ! current_user_can( 'publish_posts' ) ) {
2514 die( 0 );
2515 }
2516
2517 check_ajax_referer( 'frm_ajax', 'nonce' );
2518
2519 $type = FrmAppHelper::get_post_param( 'type', '', 'sanitize_text_field' );
2520 if ( ! $type || ! in_array( $type, array( 'form', 'view' ), true ) ) {
2521 die( 0 );
2522 }
2523
2524 $object_id = FrmAppHelper::get_post_param( 'object_id', '', 'absint' );
2525 if ( ! $object_id ) {
2526 die( 0 );
2527 }
2528
2529 $postarr = array( 'post_type' => 'page' );
2530
2531 if ( 'form' === $type ) {
2532 $postarr['post_content'] = self::get_page_shortcode_content_for_form( $object_id );
2533 } else {
2534 $postarr['post_content'] = apply_filters( 'frm_create_page_with_' . $type . '_shortcode_content', '', $object_id );
2535 }
2536
2537 $name = FrmAppHelper::get_post_param( 'name', '', 'sanitize_text_field' );
2538 if ( $name ) {
2539 $postarr['post_title'] = $name;
2540 }
2541
2542 $success = wp_insert_post( $postarr );
2543 if ( ! is_numeric( $success ) || ! $success ) {
2544 die( 0 );
2545 }
2546
2547 wp_send_json(
2548 array(
2549 'redirect' => get_edit_post_link( $success, 'redirect' ),
2550 )
2551 );
2552 }
2553
2554 /**
2555 * @since 5.3
2556 *
2557 * @param string $content
2558 * @param int $form_id
2559 * @return string
2560 */
2561 private static function get_page_shortcode_content_for_form( $form_id ) {
2562 $shortcode = '[formidable id="' . $form_id . '"]';
2563 $html_comment_start = '<!-- wp:formidable/simple-form {"formId":"' . $form_id . '"} -->';
2564 $html_comment_end = '<!-- /wp:formidable/simple-form -->';
2565 return $html_comment_start . '<div>' . $shortcode . '</div>' . $html_comment_end;
2566 }
2567
2568 /**
2569 * Get page dropdown for AJAX request for embedding form in an existing page.
2570 *
2571 * @return never
2572 */
2573 public static function get_page_dropdown() {
2574 if ( ! current_user_can( 'publish_posts' ) ) {
2575 die( 0 );
2576 }
2577
2578 check_ajax_referer( 'frm_ajax', 'nonce' );
2579
2580 $html = FrmAppHelper::clip(
2581 function() {
2582 FrmAppHelper::maybe_autocomplete_pages_options(
2583 array(
2584 'field_name' => 'frm_page_dropdown',
2585 'page_id' => '',
2586 'placeholder' => __( 'Select a Page', 'formidable' ),
2587 )
2588 );
2589 }
2590 );
2591 $post_type_object = get_post_type_object( 'page' );
2592 wp_send_json(
2593 array(
2594 'html' => $html,
2595 'edit_page_url' => admin_url( sprintf( $post_type_object->_edit_link . '&action=edit', 0 ) ),
2596 )
2597 );
2598 }
2599
2600 /**
2601 * @deprecated 4.0
2602 */
2603 public static function new_form( $values = array() ) {
2604 FrmDeprecated::new_form( $values );
2605 }
2606
2607 /**
2608 * @deprecated 4.0
2609 */
2610 public static function create( $values = array() ) {
2611 _deprecated_function( __METHOD__, '4.0', 'FrmFormsController::update' );
2612 self::update( $values );
2613 }
2614
2615 /**
2616 * @deprecated 1.07.05
2617 * @codeCoverageIgnore
2618 */
2619 public static function add_default_templates( $path, $default = true, $template = true ) {
2620 FrmDeprecated::add_default_templates( $path, $default, $template );
2621 }
2622
2623 /**
2624 * @deprecated 3.0
2625 * @codeCoverageIgnore
2626 */
2627 public static function bulk_create_template( $ids ) {
2628 return FrmDeprecated::bulk_create_template( $ids );
2629 }
2630
2631 /**
2632 * @deprecated 2.03
2633 * @codeCoverageIgnore
2634 */
2635 public static function register_pro_scripts() {
2636 FrmDeprecated::register_pro_scripts();
2637 }
2638
2639 /**
2640 * @deprecated 3.0
2641 * @codeCoverageIgnore
2642 */
2643 public static function edit_key() {
2644 FrmDeprecated::edit_key();
2645 }
2646
2647 /**
2648 * @deprecated 3.0
2649 * @codeCoverageIgnore
2650 */
2651 public static function edit_description() {
2652 FrmDeprecated::edit_description();
2653 }
2654
2655 /**
2656 * @deprecated 4.08
2657 * @since 3.06
2658 */
2659 public static function add_new() {
2660 _deprecated_function( __FUNCTION__, '4.08' );
2661 }
2662 }
2663