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

2,648 lines 75.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmFormsController {
7
8 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 public static function mb_tags_box( $form_id, $class = '' ) {
1350 $fields = FrmField::get_all_for_form( $form_id, '', 'include' );
1351
1352 /**
1353 * Allows modifying the list of fields in the tags box.
1354 *
1355 * @since 5.0.04
1356 *
1357 * @param array $fields The list of fields.
1358 * @param array $args The arguments. Contains `form_id`.
1359 */
1360 $fields = apply_filters( 'frm_fields_in_tags_box', $fields, compact( 'form_id' ) );
1361 $linked_forms = array();
1362 $col = 'one';
1363 $settings_tab = FrmAppHelper::is_admin_page( 'formidable' ) ? true : false;
1364
1365 $cond_shortcodes = apply_filters( 'frm_conditional_shortcodes', array() );
1366 $entry_shortcodes = self::get_shortcode_helpers( $settings_tab );
1367
1368 $advanced_helpers = self::advanced_helpers( compact( 'fields', 'form_id' ) );
1369
1370 include( FrmAppHelper::plugin_path() . '/classes/views/shared/mb_adv_info.php' );
1371 }
1372
1373 /**
1374 * @since 3.04.01
1375 */
1376 private static function advanced_helpers( $atts ) {
1377 $advanced_helpers = array(
1378 'default' => array(
1379 'heading' => __( 'Customize field values with the following parameters.', 'formidable' ),
1380 'codes' => self::get_advanced_shortcodes(),
1381 ),
1382 );
1383
1384 $user_fields = self::user_shortcodes();
1385 if ( ! empty( $user_fields ) ) {
1386 $user_helpers = array();
1387 foreach ( $user_fields as $uk => $uf ) {
1388 $user_helpers[ '|user_id| show="' . $uk . '"' ] = $uf;
1389 unset( $uk, $uf );
1390 }
1391
1392 $advanced_helpers['user_id'] = array(
1393 'codes' => $user_helpers,
1394 );
1395 }
1396
1397 /**
1398 * Add extra helper shortcodes on the Advanced tab in form settings and views
1399 *
1400 * @since 3.04.01
1401 *
1402 * @param array $advanced_helpers
1403 * @param array $atts - Includes fields and form_id
1404 */
1405 return apply_filters( 'frm_advanced_helpers', $advanced_helpers, $atts );
1406 }
1407
1408 /**
1409 * Get an array of the options to display in the advanced tab
1410 * of the customization panel
1411 *
1412 * @since 2.0.6
1413 */
1414 private static function get_advanced_shortcodes() {
1415 $adv_shortcodes = array(
1416 'x sep=", "' => array(
1417 'label' => __( 'Separator', 'formidable' ),
1418 'title' => __( 'Use a different separator for checkbox fields', 'formidable' ),
1419 ),
1420 'x format="d-m-Y"' => array(
1421 'label' => __( 'Date Format', 'formidable' ),
1422 ),
1423 'x show="field_label"' => array(
1424 'label' => __( 'Field Label', 'formidable' ),
1425 ),
1426 'x wpautop=0' => array(
1427 'label' => __( 'No Auto P', 'formidable' ),
1428 'title' => __( 'Do not automatically add any paragraphs or line breaks', 'formidable' ),
1429 ),
1430 );
1431 $adv_shortcodes = apply_filters( 'frm_advanced_shortcodes', $adv_shortcodes );
1432
1433 // __( 'Leave blank instead of defaulting to User Login', 'formidable' ) : blank=1
1434
1435 return $adv_shortcodes;
1436 }
1437
1438 /**
1439 * @since 3.04.01
1440 */
1441 private static function user_shortcodes() {
1442 $options = array(
1443 'ID' => __( 'User ID', 'formidable' ),
1444 'first_name' => __( 'First Name', 'formidable' ),
1445 'last_name' => __( 'Last Name', 'formidable' ),
1446 'display_name' => __( 'Display Name', 'formidable' ),
1447 'user_login' => __( 'User Login', 'formidable' ),
1448 'user_email' => __( 'Email', 'formidable' ),
1449 'avatar' => __( 'Avatar', 'formidable' ),
1450 'author_link' => __( 'Author Link', 'formidable' ),
1451 );
1452
1453 return apply_filters( 'frm_user_shortcodes', $options );
1454 }
1455
1456 /**
1457 * Get an array of the helper shortcodes to display in the customization panel
1458 *
1459 * @since 2.0.6
1460 */
1461 private static function get_shortcode_helpers( $settings_tab ) {
1462 $entry_shortcodes = array(
1463 'id' => __( 'Entry ID', 'formidable' ),
1464 'key' => __( 'Entry Key', 'formidable' ),
1465 'post_id' => __( 'Post ID', 'formidable' ),
1466 'ip' => __( 'User IP', 'formidable' ),
1467 'created-at' => __( 'Entry created', 'formidable' ),
1468 'updated-at' => __( 'Entry updated', 'formidable' ),
1469 '' => '',
1470 'siteurl' => __( 'Site URL', 'formidable' ),
1471 'sitename' => __( 'Site Name', 'formidable' ),
1472 );
1473
1474 if ( ! FrmAppHelper::pro_is_installed() ) {
1475 unset( $entry_shortcodes['post_id'] );
1476 }
1477
1478 if ( $settings_tab ) {
1479 $entry_shortcodes['default-message'] = __( 'Default Msg', 'formidable' );
1480 $entry_shortcodes['default-html'] = __( 'Default HTML', 'formidable' );
1481 $entry_shortcodes['default-plain'] = __( 'Default Plain', 'formidable' );
1482 $entry_shortcodes['form_name'] = __( 'Form Name', 'formidable' );
1483 }
1484
1485 /**
1486 * Use this hook to add or remove buttons in the helpers section
1487 * in the customization panel
1488 *
1489 * @since 2.0.6
1490 */
1491 $entry_shortcodes = apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab );
1492
1493 return $entry_shortcodes;
1494 }
1495
1496 /**
1497 * Insert the form class setting into the form
1498 */
1499 public static function form_classes( $form ) {
1500 if ( isset( $form->options['form_class'] ) ) {
1501 echo esc_attr( sanitize_text_field( $form->options['form_class'] ) );
1502 }
1503
1504 if ( ! empty( $form->options['js_validate'] ) ) {
1505 echo ' frm_js_validate ';
1506 self::add_js_validate_form_to_global_vars( $form );
1507 }
1508 }
1509
1510 /**
1511 * @since 5.0.03
1512 *
1513 * @param stdClass $form
1514 */
1515 public static function add_js_validate_form_to_global_vars( $form ) {
1516 global $frm_vars;
1517 if ( ! isset( $frm_vars['js_validate_forms'] ) ) {
1518 $frm_vars['js_validate_forms'] = array();
1519 }
1520 $frm_vars['js_validate_forms'][ $form->id ] = $form;
1521 }
1522
1523 public static function get_email_html() {
1524 FrmAppHelper::permission_check( 'frm_view_forms' );
1525 check_ajax_referer( 'frm_ajax', 'nonce' );
1526
1527 echo FrmEntriesController::show_entry_shortcode( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1528 array(
1529 'form_id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ),
1530 'default_email' => true,
1531 'plain_text' => FrmAppHelper::get_post_param( 'plain_text', '', 'absint' ),
1532 )
1533 );
1534 wp_die();
1535 }
1536
1537 /**
1538 * @param string $content
1539 * @param stdClass|string|int $form
1540 * @param stdClass|string|int|false $entry
1541 * @return string
1542 */
1543 public static function filter_content( $content, $form, $entry = false ) {
1544 $content = self::replace_form_name_shortcodes( $content, $form );
1545
1546 self::get_entry_by_param( $entry );
1547 if ( ! $entry ) {
1548 return $content;
1549 }
1550
1551 if ( is_object( $form ) ) {
1552 $form = $form->id;
1553 }
1554
1555 $shortcodes = FrmFieldsHelper::get_shortcodes( $content, $form );
1556 $content = apply_filters( 'frm_replace_content_shortcodes', $content, $entry, $shortcodes );
1557
1558 return $content;
1559 }
1560
1561 /**
1562 * Replace any [form_name] shortcodes in a string.
1563 *
1564 * @since 5.5
1565 *
1566 * @param string $string
1567 * @param stdClass|string|int $form
1568 * @return string
1569 */
1570 public static function replace_form_name_shortcodes( $string, $form ) {
1571 if ( false === strpos( $string, '[form_name]' ) ) {
1572 return $string;
1573 }
1574
1575 if ( ! is_object( $form ) ) {
1576 $form = FrmForm::getOne( $form );
1577 }
1578
1579 $form_name = is_object( $form ) ? $form->name : '';
1580 return str_replace( '[form_name]', $form_name, $string );
1581 }
1582
1583 /**
1584 * @param stdClass|string|int|false $entry
1585 * @return void
1586 */
1587 private static function get_entry_by_param( &$entry ) {
1588 if ( ! $entry || ! is_object( $entry ) ) {
1589 if ( ! $entry || ! is_numeric( $entry ) ) {
1590 $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
1591 }
1592
1593 FrmEntry::maybe_get_entry( $entry );
1594 }
1595 }
1596
1597 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
1598 return FrmFieldsHelper::replace_content_shortcodes( $content, $entry, $shortcodes );
1599 }
1600
1601 public static function process_bulk_form_actions( $errors ) {
1602 if ( ! $_REQUEST ) {
1603 return $errors;
1604 }
1605
1606 $bulkaction = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
1607 if ( $bulkaction == - 1 ) {
1608 $bulkaction = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
1609 }
1610
1611 if ( ! empty( $bulkaction ) && strpos( $bulkaction, 'bulk_' ) === 0 ) {
1612 FrmAppHelper::remove_get_action();
1613
1614 $bulkaction = str_replace( 'bulk_', '', $bulkaction );
1615 }
1616
1617 $ids = FrmAppHelper::get_param( 'item-action', '', 'get', 'sanitize_text_field' );
1618 if ( empty( $ids ) ) {
1619 $errors[] = __( 'No forms were specified', 'formidable' );
1620
1621 return $errors;
1622 }
1623
1624 $permission_error = FrmAppHelper::permission_nonce_error( '', '_wpnonce', 'bulk-toplevel_page_formidable' );
1625 if ( $permission_error !== false ) {
1626 $errors[] = $permission_error;
1627
1628 return $errors;
1629 }
1630
1631 if ( ! is_array( $ids ) ) {
1632 $ids = explode( ',', $ids );
1633 }
1634
1635 switch ( $bulkaction ) {
1636 case 'delete':
1637 $message = self::bulk_destroy( $ids );
1638 break;
1639 case 'trash':
1640 $message = self::bulk_trash( $ids );
1641 break;
1642 case 'untrash':
1643 $message = self::bulk_untrash( $ids );
1644 }
1645
1646 if ( isset( $message ) && ! empty( $message ) ) {
1647 $errors['message'] = $message;
1648 }
1649
1650 return $errors;
1651 }
1652
1653 public static function route() {
1654 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
1655 $vars = array();
1656 FrmAppHelper::include_svg();
1657
1658 if ( isset( $_POST['frm_compact_fields'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1659 FrmAppHelper::permission_check( 'frm_edit_forms' );
1660
1661 // Javascript needs to be allowed in some field settings.
1662 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
1663 $json_vars = htmlspecialchars_decode( nl2br( str_replace( '&quot;', '"', wp_unslash( $_POST['frm_compact_fields'] ) ) ) );
1664 $json_vars = json_decode( $json_vars, true );
1665 if ( empty( $json_vars ) ) {
1666 // json decoding failed so we should return an error message.
1667 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
1668 if ( 'edit' == $action ) {
1669 $action = 'update';
1670 }
1671
1672 add_filter( 'frm_validate_form', 'FrmFormsController::json_error' );
1673 } else {
1674 $vars = FrmAppHelper::json_to_array( $json_vars );
1675 $action = $vars[ $action ];
1676 unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1677 $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1678 $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1679 }
1680 } else {
1681 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
1682 if ( isset( $_REQUEST['delete_all'] ) ) {
1683 // Override the action for this page.
1684 $action = 'delete_all';
1685 }
1686 }
1687
1688 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1689 FrmAppHelper::trigger_hook_load( 'form' );
1690
1691 switch ( $action ) {
1692 case 'new':
1693 return self::new_form( $vars );
1694 case 'create':
1695 case 'edit':
1696 case 'update':
1697 case 'trash':
1698 case 'untrash':
1699 case 'destroy':
1700 case 'delete_all':
1701 case 'settings':
1702 case 'update_settings':
1703 return self::$action( $vars );
1704 case 'lite-reports':
1705 return self::no_reports( $vars );
1706 case 'views':
1707 return self::no_views( $vars );
1708 default:
1709 do_action( 'frm_form_action_' . $action );
1710 if ( apply_filters( 'frm_form_stop_action_' . $action, false ) ) {
1711 return;
1712 }
1713
1714 $action = FrmAppHelper::get_param( 'action', '', 'get', 'sanitize_text_field' );
1715 if ( $action == - 1 ) {
1716 $action = FrmAppHelper::get_param( 'action2', '', 'get', 'sanitize_title' );
1717 }
1718
1719 if ( strpos( $action, 'bulk_' ) === 0 ) {
1720 FrmAppHelper::remove_get_action();
1721
1722 self::list_form();
1723 return;
1724 }
1725
1726 $message = FrmAppHelper::get_param( 'message' );
1727 if ( 'form_duplicate_error' === $message ) {
1728 self::display_forms_list( array(), '', array( __( 'There was a problem duplicating the form', 'formidable' ) ) );
1729 return;
1730 }
1731
1732 self::display_forms_list();
1733
1734 return;
1735 }
1736 }
1737
1738 public static function json_error( $errors ) {
1739 $errors['json'] = __( 'Abnormal HTML characters prevented your form from saving correctly', 'formidable' );
1740
1741 return $errors;
1742 }
1743
1744 /**
1745 * Education for premium features.
1746 *
1747 * @since 4.05
1748 */
1749 public static function add_form_style_tab_options() {
1750 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/add_form_style_options.php' );
1751 }
1752
1753 /**
1754 * Add education about views.
1755 *
1756 * @since 4.07
1757 */
1758 public static function no_views( $values = array() ) {
1759 FrmAppHelper::include_svg();
1760 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1761 $form = $id ? FrmForm::getOne( $id ) : false;
1762
1763 include FrmAppHelper::plugin_path() . '/classes/views/shared/views-info.php';
1764 }
1765
1766 /**
1767 * Add education about reports.
1768 *
1769 * @since 4.07
1770 */
1771 public static function no_reports( $values = array() ) {
1772 $id = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1773 $form = $id ? FrmForm::getOne( $id ) : false;
1774
1775 include FrmAppHelper::plugin_path() . '/classes/views/shared/reports-info.php';
1776 }
1777
1778 /* FRONT-END FORMS */
1779 public static function admin_bar_css() {
1780 if ( is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
1781 return;
1782 }
1783
1784 self::move_menu_to_footer();
1785
1786 add_action( 'wp_before_admin_bar_render', 'FrmFormsController::admin_bar_configure' );
1787 FrmAppHelper::load_font_style();
1788 }
1789
1790 /**
1791 * @since 4.05.02
1792 */
1793 private static function move_menu_to_footer() {
1794 $settings = FrmAppHelper::get_settings();
1795 if ( empty( $settings->admin_bar ) ) {
1796 remove_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
1797 }
1798 }
1799
1800 public static function admin_bar_configure() {
1801 global $frm_vars;
1802 if ( empty( $frm_vars['forms_loaded'] ) ) {
1803 return;
1804 }
1805
1806 $actions = array();
1807 foreach ( $frm_vars['forms_loaded'] as $form ) {
1808 if ( is_object( $form ) ) {
1809 $actions[ $form->id ] = $form->name;
1810 }
1811 unset( $form );
1812 }
1813
1814 if ( empty( $actions ) ) {
1815 return;
1816 }
1817
1818 self::add_menu_to_admin_bar();
1819 self::add_forms_to_admin_bar( $actions );
1820 }
1821
1822 /**
1823 * @since 2.05.07
1824 */
1825 public static function add_menu_to_admin_bar() {
1826 global $wp_admin_bar;
1827
1828 $wp_admin_bar->add_node(
1829 array(
1830 'id' => 'frm-forms',
1831 'title' => '<span class="ab-icon"></span><span class="ab-label">' . FrmAppHelper::get_menu_name() . '</span>',
1832 'href' => admin_url( 'admin.php?page=formidable' ),
1833 'meta' => array(
1834 'title' => FrmAppHelper::get_menu_name(),
1835 ),
1836 )
1837 );
1838 }
1839
1840 /**
1841 * @since 2.05.07
1842 */
1843 private static function add_forms_to_admin_bar( $actions ) {
1844 global $wp_admin_bar;
1845
1846 asort( $actions );
1847
1848 foreach ( $actions as $form_id => $name ) {
1849
1850 $wp_admin_bar->add_node(
1851 array(
1852 'parent' => 'frm-forms',
1853 'id' => 'edit_form_' . $form_id,
1854 'title' => empty( $name ) ? __( '(no title)', 'formidable' ) : $name,
1855 'href' => FrmForm::get_edit_link( $form_id ),
1856 )
1857 );
1858 }
1859 }
1860
1861 /**
1862 * The formidable shortcode
1863 *
1864 * @param array $atts The params from the shortcode.
1865 * @return string
1866 */
1867 public static function get_form_shortcode( $atts ) {
1868 global $frm_vars;
1869 if ( isset( $frm_vars['skip_shortcode'] ) && $frm_vars['skip_shortcode'] ) {
1870 $sc = '[formidable';
1871 $sc .= FrmAppHelper::array_to_html_params( $atts );
1872 return $sc . ']';
1873 }
1874
1875 $shortcode_atts = shortcode_atts(
1876 array(
1877 'id' => '',
1878 'key' => '',
1879 'title' => 'auto',
1880 'description' => 'auto',
1881 'readonly' => false,
1882 'entry_id' => false,
1883 'fields' => array(),
1884 'exclude_fields' => array(),
1885 'minimize' => false,
1886 ),
1887 $atts
1888 );
1889 do_action( 'formidable_shortcode_atts', $shortcode_atts, $atts );
1890
1891 return self::show_form( $shortcode_atts['id'], $shortcode_atts['key'], $shortcode_atts['title'], $shortcode_atts['description'], $atts );
1892 }
1893
1894 /**
1895 * @since 5.2.01
1896 *
1897 * @param string|int|false $id
1898 * @param string|false $key
1899 * @return stdClass|false
1900 */
1901 private static function maybe_get_form_by_id_or_key( $id, $key ) {
1902 if ( ! $id ) {
1903 $id = $key;
1904 }
1905 return self::maybe_get_form_to_show( $id );
1906 }
1907
1908 /**
1909 * @param string|int|false $id
1910 * @param string|false $key
1911 * @param string|int|bool $title may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
1912 * @param string|int|bool $description may be 'auto', true, false, 'true', 'false', 'yes', '1', 1, '0', 0.
1913 * @param array $atts
1914 * @return string
1915 */
1916 public static function show_form( $id = '', $key = '', $title = false, $description = false, $atts = array() ) {
1917 $form = self::maybe_get_form_by_id_or_key( $id, $key );
1918
1919 if ( ! $form ) {
1920 return __( 'Please select a valid form', 'formidable' );
1921 }
1922
1923 if ( 'auto' === $title ) {
1924 $title = ! empty( $form->options['show_title'] );
1925 }
1926
1927 if ( 'auto' === $description ) {
1928 $description = ! empty( $form->options['show_description'] );
1929 }
1930
1931 FrmAppController::maybe_update_styles();
1932
1933 add_action( 'frm_load_form_hooks', 'FrmHooksController::trigger_load_form_hooks' );
1934 FrmAppHelper::trigger_hook_load( 'form', $form );
1935
1936 $form = apply_filters( 'frm_pre_display_form', $form );
1937
1938 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
1939
1940 if ( self::is_viewable_draft_form( $form ) ) {
1941 // don't show a draft form on a page
1942 $form = __( 'Please select a valid form', 'formidable' );
1943 } elseif ( ! FrmForm::is_visible_to_user( $form ) ) {
1944 $form = do_shortcode( $frm_settings->login_msg );
1945 } else {
1946 do_action( 'frm_pre_get_form', $form );
1947 $form = self::get_form( $form, $title, $description, $atts );
1948
1949 /**
1950 * Use this shortcode to check for external shortcodes that may span
1951 * across multiple fields in the customizable HTML
1952 *
1953 * @since 2.0.8
1954 */
1955 $form = apply_filters( 'frm_filter_final_form', $form );
1956 }
1957
1958 return $form;
1959 }
1960
1961 /**
1962 * @param string|int|false $id
1963 * @return stdClass|false
1964 */
1965 private static function maybe_get_form_to_show( $id ) {
1966 $form = false;
1967
1968 if ( ! empty( $id ) ) { // form id or key is set
1969 $form = FrmForm::getOne( $id );
1970 if ( ! $form || $form->parent_form_id || $form->status === 'trash' ) {
1971 $form = false;
1972 }
1973 }
1974
1975 return $form;
1976 }
1977
1978 private static function is_viewable_draft_form( $form ) {
1979 return $form->status === 'draft' && current_user_can( 'frm_edit_forms' ) && ! FrmAppHelper::is_preview_page();
1980 }
1981
1982 public static function get_form( $form, $title, $description, $atts = array() ) {
1983 ob_start();
1984
1985 do_action( 'frm_before_get_form', $atts );
1986
1987 self::get_form_contents( $form, $title, $description, $atts );
1988 self::enqueue_scripts( FrmForm::get_params( $form ) );
1989
1990 $contents = ob_get_contents();
1991 ob_end_clean();
1992
1993 self::maybe_minimize_form( $atts, $contents );
1994
1995 return $contents;
1996 }
1997
1998 public static function enqueue_scripts( $params ) {
1999 do_action( 'frm_enqueue_form_scripts', $params );
2000 }
2001
2002 public static function get_form_contents( $form, $title, $description, $atts ) {
2003 $params = FrmForm::get_params( $form );
2004 $errors = self::get_saved_errors( $form, $params );
2005 $fields = FrmFieldsHelper::get_form_fields( $form->id, $errors );
2006 $reset = false;
2007 $pass_args = compact( 'form', 'fields', 'errors', 'title', 'description', 'reset' );
2008
2009 $handle_process_here = $params['action'] === 'create' && $params['posted_form_id'] == $form->id && $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
2010
2011 if ( ! $handle_process_here ) {
2012 do_action( 'frm_display_form_action', $params, $fields, $form, $title, $description );
2013 if ( apply_filters( 'frm_continue_to_new', true, $form->id, $params['action'] ) ) {
2014 self::show_form_after_submit( $pass_args );
2015 }
2016 } elseif ( ! empty( $errors ) ) {
2017 self::show_form_after_submit( $pass_args );
2018
2019 } else {
2020
2021 do_action( 'frm_validate_form_creation', $params, $fields, $form, $title, $description );
2022
2023 if ( apply_filters( 'frm_continue_to_create', true, $form->id ) ) {
2024 $entry_id = self::just_created_entry( $form->id );
2025 $pass_args['entry_id'] = $entry_id;
2026 $pass_args['reset'] = true;
2027 $pass_args['conf_method'] = self::get_confirmation_method( compact( 'form', 'entry_id' ) );
2028
2029 self::run_success_action( $pass_args );
2030
2031 do_action(
2032 'frm_after_entry_processed',
2033 array(
2034 'entry_id' => $entry_id,
2035 'form' => $form,
2036 )
2037 );
2038 }
2039 }
2040 }
2041
2042 /**
2043 * If the form was processed earlier (init), get the generated errors
2044 *
2045 * @since 2.05
2046 */
2047 private static function get_saved_errors( $form, $params ) {
2048 global $frm_vars;
2049
2050 if ( $params['posted_form_id'] == $form->id && $_POST && isset( $frm_vars['created_entries'][ $form->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
2051 $errors = $frm_vars['created_entries'][ $form->id ]['errors'];
2052 } else {
2053 $errors = array();
2054 }
2055
2056 /**
2057 * Allows modifying the generated errors if the form was processed earlier.
2058 *
2059 * @since 5.2.03
2060 *
2061 * @param array $errors Errors data. Is empty array if no errors found.
2062 * @param array $params Form params. See {@see FrmForm::get_params()}.
2063 */
2064 return apply_filters( 'frm_saved_errors', $errors, $params );
2065 }
2066
2067 /**
2068 * @since 2.2.7
2069 */
2070 public static function just_created_entry( $form_id ) {
2071 global $frm_vars;
2072
2073 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;
2074 }
2075
2076 /**
2077 * @since 3.0
2078 */
2079 private static function get_confirmation_method( $atts ) {
2080 $opt = 'success_action';
2081 $method = ( isset( $atts['form']->options[ $opt ] ) && ! empty( $atts['form']->options[ $opt ] ) ) ? $atts['form']->options[ $opt ] : 'message';
2082 $method = apply_filters( 'frm_success_filter', $method, $atts['form'], 'create' );
2083
2084 if ( $method != 'message' && ( ! $atts['entry_id'] || ! is_numeric( $atts['entry_id'] ) ) ) {
2085 $method = 'message';
2086 }
2087
2088 return $method;
2089 }
2090
2091 public static function maybe_trigger_redirect( $form, $params, $args ) {
2092 if ( ! isset( $params['id'] ) ) {
2093 global $frm_vars;
2094 $params['id'] = $frm_vars['created_entries'][ $form->id ]['entry_id'];
2095 }
2096
2097 $conf_method = self::get_confirmation_method(
2098 array(
2099 'form' => $form,
2100 'entry_id' => $params['id'],
2101 )
2102 );
2103
2104 if ( 'redirect' === $conf_method ) {
2105 self::trigger_redirect( $form, $params, $args );
2106 }
2107 }
2108
2109 public static function trigger_redirect( $form, $params, $args ) {
2110 $success_args = array(
2111 'action' => $params['action'],
2112 'conf_method' => 'redirect',
2113 'form' => $form,
2114 'entry_id' => $params['id'],
2115 );
2116
2117 if ( isset( $args['ajax'] ) ) {
2118 $success_args['ajax'] = $args['ajax'];
2119 }
2120
2121 self::run_success_action( $success_args );
2122 }
2123
2124 /**
2125 * Used when the success action is not 'message'
2126 *
2127 * @since 2.05
2128 */
2129 public static function run_success_action( $args ) {
2130 $extra_args = $args;
2131 unset( $extra_args['form'] );
2132
2133 do_action( 'frm_success_action', $args['conf_method'], $args['form'], $args['form']->options, $args['entry_id'], $extra_args );
2134
2135 $opt = ( ! isset( $args['action'] ) || $args['action'] === 'create' ) ? 'success' : 'edit';
2136
2137 $args['success_opt'] = $opt;
2138 if ( $args['conf_method'] === 'page' && is_numeric( $args['form']->options[ $opt . '_page_id' ] ) ) {
2139 self::load_page_after_submit( $args );
2140 } elseif ( $args['conf_method'] === 'redirect' ) {
2141 self::redirect_after_submit( $args );
2142 } else {
2143 self::show_message_after_save( $args );
2144 }
2145 }
2146
2147 /**
2148 * @since 3.0
2149 */
2150 private static function load_page_after_submit( $args ) {
2151 global $post;
2152 $opt = $args['success_opt'];
2153 if ( ! $post || $args['form']->options[ $opt . '_page_id' ] != $post->ID ) {
2154 $page = get_post( $args['form']->options[ $opt . '_page_id' ] );
2155 $old_post = $post;
2156 $post = $page;
2157 $content = apply_filters( 'frm_content', $page->post_content, $args['form'], $args['entry_id'] );
2158 echo apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2159 $post = $old_post;
2160 }
2161 }
2162
2163 /**
2164 * @since 3.0
2165 */
2166 private static function redirect_after_submit( $args ) {
2167 global $frm_vars;
2168
2169 add_filter( 'frm_use_wpautop', '__return_false' );
2170
2171 $opt = $args['success_opt'];
2172 $success_url = trim( $args['form']->options[ $opt . '_url' ] );
2173 $success_url = apply_filters( 'frm_content', $success_url, $args['form'], $args['entry_id'] );
2174 $success_url = do_shortcode( $success_url );
2175
2176 $success_msg = isset( $args['form']->options[ $opt . '_msg' ] ) ? $args['form']->options[ $opt . '_msg' ] : __( 'Please wait while you are redirected.', 'formidable' );
2177
2178 $redirect_msg = self::get_redirect_message( $success_url, $success_msg, $args );
2179
2180 $args['id'] = $args['entry_id'];
2181 FrmEntriesController::delete_entry_before_redirect( $success_url, $args['form'], $args );
2182
2183 add_filter( 'frm_redirect_url', 'FrmEntriesController::prepare_redirect_url' );
2184 $success_url = apply_filters( 'frm_redirect_url', $success_url, $args['form'], $args );
2185
2186 $doing_ajax = FrmAppHelper::doing_ajax();
2187
2188 if ( isset( $args['ajax'] ) && $args['ajax'] && $doing_ajax ) {
2189 echo json_encode( array( 'redirect' => $success_url ) );
2190 wp_die();
2191 } elseif ( ! headers_sent() ) {
2192 wp_redirect( esc_url_raw( $success_url ) );
2193 die(); // do not use wp_die or redirect fails
2194 } else {
2195 add_filter( 'frm_use_wpautop', '__return_true' );
2196 echo FrmAppHelper::maybe_kses( $redirect_msg ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2197 echo "<script type='text/javascript'>window.onload = function(){setTimeout(window.location='" . esc_url_raw( $success_url ) . "', 8000);}</script>";
2198 }
2199 }
2200
2201 /**
2202 * @since 3.0
2203 *
2204 * @param string $success_url
2205 * @param string $success_msg
2206 * @param array $args
2207 */
2208 private static function get_redirect_message( $success_url, $success_msg, $args ) {
2209 $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/>' .
2210 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
2211 sprintf( __( '%1$sClick here%2$s if you are not automatically redirected.', 'formidable' ), '<a href="' . esc_url( $success_url ) . '">', '</a>' ) .
2212 '</div></div>';
2213
2214 $redirect_args = array(
2215 'entry_id' => $args['entry_id'],
2216 'form_id' => $args['form']->id,
2217 'form' => $args['form'],
2218 );
2219
2220 return apply_filters( 'frm_redirect_msg', $redirect_msg, $redirect_args );
2221 }
2222
2223 /**
2224 * Prepare to show the success message and empty form after submit
2225 *
2226 * @since 2.05
2227 */
2228 public static function show_message_after_save( $atts ) {
2229 $atts['message'] = self::prepare_submit_message( $atts['form'], $atts['entry_id'] );
2230
2231 if ( ! isset( $atts['form']->options['show_form'] ) || $atts['form']->options['show_form'] ) {
2232 self::show_form_after_submit( $atts );
2233 } else {
2234 self::show_lone_success_messsage( $atts );
2235 }
2236 }
2237
2238 /**
2239 * Show an empty form
2240 *
2241 * @since 2.05
2242 */
2243 private static function show_form_after_submit( $args ) {
2244 self::fill_atts_for_form_display( $args );
2245
2246 $errors = $args['errors'];
2247 $message = $args['message'];
2248 $form = $args['form'];
2249 $title = $args['title'];
2250 $description = $args['description'];
2251
2252 if ( empty( $args['fields'] ) ) {
2253 $values = array(
2254 'custom_style' => FrmAppHelper::custom_style_value( array() ),
2255 );
2256 } else {
2257 $values = FrmEntriesHelper::setup_new_vars( $args['fields'], $form, $args['reset'] );
2258 }
2259 unset( $args );
2260
2261 $include_form_tag = apply_filters( 'frm_include_form_tag', true, $form );
2262
2263 $frm_settings = FrmAppHelper::get_settings();
2264 $submit = isset( $form->options['submit_value'] ) ? $form->options['submit_value'] : $frm_settings->submit_value;
2265
2266 global $frm_vars;
2267 self::maybe_load_css( $form, $values['custom_style'], $frm_vars['load_css'] );
2268
2269 $message_placement = self::message_placement( $form, $message );
2270
2271 include FrmAppHelper::plugin_path() . '/classes/views/frm-entries/new.php';
2272 }
2273
2274 /**
2275 * @return string - 'before', 'after', or 'submit'
2276 *
2277 * @since 4.05.02
2278 */
2279 private static function message_placement( $form, $message ) {
2280 $place = 'before';
2281
2282 if ( $message && isset( $form->options['form_class'] ) ) {
2283 if ( strpos( $form->options['form_class'], 'frm_below_success' ) !== false ) {
2284 $place = 'after';
2285 } elseif ( strpos( $form->options['form_class'], 'frm_inline_success' ) !== false ) {
2286 $place = 'submit';
2287 }
2288 }
2289
2290 /**
2291 * @return string - 'before' or 'after'
2292 *
2293 * @since 4.05.02
2294 */
2295 return apply_filters( 'frm_message_placement', $place, compact( 'form', 'message' ) );
2296 }
2297
2298 /**
2299 * Get all the values needed on the new.php entry page
2300 *
2301 * @since 2.05
2302 */
2303 private static function fill_atts_for_form_display( &$args ) {
2304 $defaults = array(
2305 'errors' => array(),
2306 'message' => '',
2307 'fields' => array(),
2308 'form' => array(),
2309 'title' => true,
2310 'description' => false,
2311 'reset' => false,
2312 );
2313 $args = wp_parse_args( $args, $defaults );
2314 }
2315
2316 /**
2317 * Show the success message without the form
2318 *
2319 * @since 2.05
2320 */
2321 private static function show_lone_success_messsage( $atts ) {
2322 global $frm_vars;
2323 $values = FrmEntriesHelper::setup_new_vars( $atts['fields'], $atts['form'], true );
2324 self::maybe_load_css( $atts['form'], $values['custom_style'], $frm_vars['load_css'] );
2325
2326 $include_extra_container = 'frm_forms' . FrmFormsHelper::get_form_style_class( $values );
2327
2328 $errors = array();
2329 $form = $atts['form'];
2330 $message = $atts['message'];
2331
2332 include( FrmAppHelper::plugin_path() . '/classes/views/frm-entries/errors.php' );
2333 }
2334
2335 /**
2336 * Prepare the success message before it's shown
2337 *
2338 * @since 2.05
2339 */
2340 private static function prepare_submit_message( $form, $entry_id ) {
2341 $frm_settings = FrmAppHelper::get_settings( array( 'current_form' => $form->id ) );
2342
2343 if ( $entry_id && is_numeric( $entry_id ) ) {
2344 $message = isset( $form->options['success_msg'] ) ? $form->options['success_msg'] : $frm_settings->success_msg;
2345 $class = 'frm_message';
2346 } else {
2347 $message = $frm_settings->failed_msg;
2348 $class = FrmFormsHelper::form_error_class();
2349 }
2350
2351 $message = FrmFormsHelper::get_success_message( compact( 'message', 'form', 'entry_id', 'class' ) );
2352
2353 return apply_filters( 'frm_main_feedback', $message, $form, $entry_id );
2354 }
2355
2356 public static function front_head() {
2357 $version = FrmAppHelper::plugin_version();
2358 $suffix = FrmAppHelper::js_suffix();
2359
2360 if ( ! empty( $suffix ) && self::has_combo_js_file() ) {
2361 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . '/js/frm.min.js', array( 'jquery' ), $version, true );
2362 } else {
2363 wp_register_script( 'formidable', FrmAppHelper::plugin_url() . "/js/formidable{$suffix}.js", array( 'jquery' ), $version, true );
2364 }
2365
2366 add_filter( 'script_loader_tag', 'FrmFormsController::defer_script_loading', 10, 2 );
2367
2368 if ( FrmAppHelper::is_admin() ) {
2369 // don't load this in back-end
2370 return;
2371 }
2372
2373 FrmAppHelper::localize_script( 'front' );
2374 FrmStylesController::enqueue_css( 'register' );
2375 }
2376
2377 /**
2378 * @since 3.0
2379 */
2380 public static function has_combo_js_file() {
2381 return is_readable( FrmAppHelper::plugin_path() . '/js/frm.min.js' );
2382 }
2383
2384 public static function maybe_load_css( $form, $this_load, $global_load ) {
2385 $load_css = FrmForm::is_form_loaded( $form, $this_load, $global_load );
2386
2387 if ( ! $load_css ) {
2388 return;
2389 }
2390
2391 global $frm_vars;
2392 self::footer_js( 'header' );
2393 $frm_vars['css_loaded'] = true;
2394
2395 self::load_late_css();
2396 }
2397
2398 /**
2399 * If css is loaded only on applicable pages, include it before the form loads
2400 * to prevent a flash of unstyled form.
2401 *
2402 * @since 4.01
2403 *
2404 * @return void
2405 */
2406 private static function load_late_css() {
2407 $frm_settings = FrmAppHelper::get_settings();
2408 $late_css = $frm_settings->load_style === 'dynamic';
2409
2410 if ( ! $late_css || ! self::should_load_late() ) {
2411 return;
2412 }
2413
2414 global $wp_styles;
2415 if ( is_array( $wp_styles->queue ) && in_array( 'formidable', $wp_styles->queue, true ) ) {
2416 wp_print_styles( 'formidable' );
2417 }
2418 }
2419
2420 /**
2421 * Avoid late load if All in One SEO is active because it prevents CSS from loading entirely.
2422 *
2423 * @since 5.2.03
2424 *
2425 * @return bool
2426 */
2427 private static function should_load_late() {
2428 return ! function_exists( 'aioseo' );
2429 }
2430
2431 public static function defer_script_loading( $tag, $handle ) {
2432 if ( 'captcha-api' == $handle && ! strpos( $tag, 'defer' ) ) {
2433 $tag = str_replace( ' src', ' defer="defer" async="async" src', $tag );
2434 }
2435
2436 return $tag;
2437 }
2438
2439 public static function footer_js( $location = 'footer' ) {
2440 global $frm_vars;
2441
2442 FrmStylesController::enqueue_css();
2443
2444 if ( ! FrmAppHelper::is_admin() && $location != 'header' && ! empty( $frm_vars['forms_loaded'] ) ) {
2445 // load formidable js
2446 wp_enqueue_script( 'formidable' );
2447 }
2448 }
2449
2450 /**
2451 * @since 2.0.8
2452 */
2453 private static function maybe_minimize_form( $atts, &$content ) {
2454 // check if minimizing is turned on
2455 if ( self::is_minification_on( $atts ) ) {
2456 $content = str_replace( array( "\r\n", "\r", "\n", "\t", ' ' ), '', $content );
2457 }
2458 }
2459
2460 /**
2461 * @since 2.0.8
2462 * @return boolean
2463 */
2464 private static function is_minification_on( $atts ) {
2465 return isset( $atts['minimize'] ) && ! empty( $atts['minimize'] );
2466 }
2467
2468 /**
2469 * @since 5.0.16
2470 *
2471 * @return void
2472 */
2473 public static function landing_page_preview_option() {
2474 $dir = apply_filters( 'frm_landing_page_preview_option', false );
2475 if ( false === $dir || ! file_exists( $dir . 'landing-page-preview-option.php' ) ) {
2476 $dir = self::get_form_views_path();
2477 }
2478 include $dir . 'landing-page-preview-option.php';
2479 }
2480
2481 /**
2482 * @since 5.0.16
2483 *
2484 * @return string
2485 */
2486 private static function get_form_views_path() {
2487 return FrmAppHelper::plugin_path() . '/classes/views/frm-forms/';
2488 }
2489
2490 /**
2491 * Create a page with an embedded formidable Gutenberg block.
2492 *
2493 * @since 5.2
2494 *
2495 * @return never
2496 */
2497 public static function create_page_with_shortcode() {
2498 if ( ! current_user_can( 'publish_posts' ) ) {
2499 die( 0 );
2500 }
2501
2502 check_ajax_referer( 'frm_ajax', 'nonce' );
2503
2504 $type = FrmAppHelper::get_post_param( 'type', '', 'sanitize_text_field' );
2505 if ( ! $type || ! in_array( $type, array( 'form', 'view' ), true ) ) {
2506 die( 0 );
2507 }
2508
2509 $object_id = FrmAppHelper::get_post_param( 'object_id', '', 'absint' );
2510 if ( ! $object_id ) {
2511 die( 0 );
2512 }
2513
2514 $postarr = array( 'post_type' => 'page' );
2515
2516 if ( 'form' === $type ) {
2517 $postarr['post_content'] = self::get_page_shortcode_content_for_form( $object_id );
2518 } else {
2519 $postarr['post_content'] = apply_filters( 'frm_create_page_with_' . $type . '_shortcode_content', '', $object_id );
2520 }
2521
2522 $name = FrmAppHelper::get_post_param( 'name', '', 'sanitize_text_field' );
2523 if ( $name ) {
2524 $postarr['post_title'] = $name;
2525 }
2526
2527 $success = wp_insert_post( $postarr );
2528 if ( ! is_numeric( $success ) || ! $success ) {
2529 die( 0 );
2530 }
2531
2532 wp_send_json(
2533 array(
2534 'redirect' => get_edit_post_link( $success, 'redirect' ),
2535 )
2536 );
2537 }
2538
2539 /**
2540 * @since 5.3
2541 *
2542 * @param string $content
2543 * @param int $form_id
2544 * @return string
2545 */
2546 private static function get_page_shortcode_content_for_form( $form_id ) {
2547 $shortcode = '[formidable id="' . $form_id . '"]';
2548 $html_comment_start = '<!-- wp:formidable/simple-form {"formId":"' . $form_id . '"} -->';
2549 $html_comment_end = '<!-- /wp:formidable/simple-form -->';
2550 return $html_comment_start . '<div>' . $shortcode . '</div>' . $html_comment_end;
2551 }
2552
2553 /**
2554 * Get page dropdown for AJAX request for embedding form in an existing page.
2555 *
2556 * @return never
2557 */
2558 public static function get_page_dropdown() {
2559 if ( ! current_user_can( 'publish_posts' ) ) {
2560 die( 0 );
2561 }
2562
2563 check_ajax_referer( 'frm_ajax', 'nonce' );
2564
2565 $html = FrmAppHelper::clip(
2566 function() {
2567 FrmAppHelper::maybe_autocomplete_pages_options(
2568 array(
2569 'field_name' => 'frm_page_dropdown',
2570 'page_id' => '',
2571 'placeholder' => __( 'Select a Page', 'formidable' ),
2572 )
2573 );
2574 }
2575 );
2576 $post_type_object = get_post_type_object( 'page' );
2577 wp_send_json(
2578 array(
2579 'html' => $html,
2580 'edit_page_url' => admin_url( sprintf( $post_type_object->_edit_link . '&action=edit', 0 ) ),
2581 )
2582 );
2583 }
2584
2585 /**
2586 * @deprecated 4.0
2587 */
2588 public static function new_form( $values = array() ) {
2589 FrmDeprecated::new_form( $values );
2590 }
2591
2592 /**
2593 * @deprecated 4.0
2594 */
2595 public static function create( $values = array() ) {
2596 _deprecated_function( __METHOD__, '4.0', 'FrmFormsController::update' );
2597 self::update( $values );
2598 }
2599
2600 /**
2601 * @deprecated 1.07.05
2602 * @codeCoverageIgnore
2603 */
2604 public static function add_default_templates( $path, $default = true, $template = true ) {
2605 FrmDeprecated::add_default_templates( $path, $default, $template );
2606 }
2607
2608 /**
2609 * @deprecated 3.0
2610 * @codeCoverageIgnore
2611 */
2612 public static function bulk_create_template( $ids ) {
2613 return FrmDeprecated::bulk_create_template( $ids );
2614 }
2615
2616 /**
2617 * @deprecated 2.03
2618 * @codeCoverageIgnore
2619 */
2620 public static function register_pro_scripts() {
2621 FrmDeprecated::register_pro_scripts();
2622 }
2623
2624 /**
2625 * @deprecated 3.0
2626 * @codeCoverageIgnore
2627 */
2628 public static function edit_key() {
2629 FrmDeprecated::edit_key();
2630 }
2631
2632 /**
2633 * @deprecated 3.0
2634 * @codeCoverageIgnore
2635 */
2636 public static function edit_description() {
2637 FrmDeprecated::edit_description();
2638 }
2639
2640 /**
2641 * @deprecated 4.08
2642 * @since 3.06
2643 */
2644 public static function add_new() {
2645 _deprecated_function( __FUNCTION__, '4.08' );
2646 }
2647 }
2648