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

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

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