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

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