PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.13
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.13
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 / FrmAppController.php

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

1,325 lines 36.9 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 FrmAppController {
7
8 /**
9 * @return void
10 */
11 public static function menu() {
12 FrmAppHelper::maybe_add_permissions();
13 if ( ! current_user_can( 'frm_view_forms' ) ) {
14 return;
15 }
16
17 $menu_name = FrmAppHelper::get_menu_name();
18 add_menu_page( 'Formidable', $menu_name, 'frm_view_forms', 'formidable', 'FrmFormsController::route', self::menu_icon(), self::get_menu_position() );
19 }
20
21 /**
22 * @return int
23 */
24 private static function get_menu_position() {
25 return (int) apply_filters( 'frm_menu_position', 29 );
26 }
27
28 /**
29 * @since 3.05
30 */
31 private static function menu_icon() {
32 $icon = FrmAppHelper::svg_logo(
33 array(
34 'fill' => '#a0a5aa',
35 'orange' => '#a0a5aa',
36 )
37 );
38 $icon = 'data:image/svg+xml;base64,' . base64_encode( $icon );
39
40 return apply_filters( 'frm_icon', $icon );
41 }
42
43 /**
44 * @since 3.0
45 */
46 public static function add_admin_class( $classes ) {
47 if ( self::is_white_page() ) {
48 $classes .= ' frm-white-body ';
49 $classes .= self::get_os();
50
51 $page = str_replace( 'formidable-', '', FrmAppHelper::simple_get( 'page', 'sanitize_title' ) );
52 if ( empty( $page ) || $page === 'formidable' ) {
53 $action = FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' );
54 if ( in_array( $action, array( 'settings', 'edit', 'list' ) ) ) {
55 $page .= $action;
56 } else {
57 $page = $action;
58 }
59 }
60 if ( ! empty( $page ) ) {
61 $classes .= ' frm-admin-page-' . $page;
62 }
63 }
64
65 if ( self::is_grey_page() ) {
66 $classes .= ' frm-grey-body ';
67 }
68
69 if ( FrmAppHelper::is_full_screen() ) {
70 $full_screen_on = self::get_full_screen_setting();
71 $add_class = '';
72 if ( $full_screen_on ) {
73 $add_class = ' frm-full-screen is-fullscreen-mode';
74
75 // Load the CSS for .is-fullscreen-mode.
76 wp_enqueue_style( 'wp-edit-post' );
77 }
78 $classes .= apply_filters( 'frm_admin_full_screen_class', $add_class );
79 }
80
81 if ( ! FrmAppHelper::pro_is_installed() ) {
82 $classes .= ' frm-lite ';
83 }
84
85 if ( get_user_setting( 'unfold' ) && 'f' !== get_user_setting( 'mfold' ) ) {
86 $classes .= ' frm-unfold ';
87 }
88
89 return $classes;
90 }
91
92 /**
93 * Get the full screen mode setting from the block editor.
94 *
95 * @since 6.1
96 *
97 * @return bool
98 */
99 private static function get_full_screen_setting() {
100 global $wpdb;
101 $meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences';
102
103 $prefs = get_user_meta( get_current_user_id(), $meta_key, true );
104 if ( $prefs && isset( $prefs['core/edit-post']['fullscreenMode'] ) ) {
105 return $prefs['core/edit-post']['fullscreenMode'];
106 }
107
108 return true;
109 }
110
111 /**
112 * @since 4.0
113 *
114 * @return string
115 */
116 private static function get_os() {
117 $agent = strtolower( FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' ) );
118 $os = '';
119 if ( strpos( $agent, 'mac' ) !== false ) {
120 $os = ' osx';
121 } elseif ( strpos( $agent, 'linux' ) !== false ) {
122 $os = ' linux';
123 } elseif ( strpos( $agent, 'windows' ) !== false ) {
124 $os = ' windows';
125 }
126 return $os;
127 }
128
129 /**
130 * @since 3.0
131 */
132 private static function is_white_page() {
133 $white_pages = array(
134 'formidable',
135 'formidable-entries',
136 'formidable-views',
137 'formidable-views-editor',
138 'formidable-pro-upgrade',
139 'formidable-addons',
140 'formidable-import',
141 'formidable-settings',
142 'formidable-styles',
143 'formidable-styles2',
144 'formidable-inbox',
145 FrmFormTemplatesController::PAGE_SLUG,
146 FrmOnboardingWizardController::PAGE_SLUG,
147 );
148
149 if ( ! class_exists( 'FrmTransHooksController', false ) && ! FrmTransLiteAppHelper::should_fallback_to_paypal() ) {
150 // Only consider the payments page as a "white page" when the Payments submodule is off.
151 // Otherwise this causes a lot of styling issues when the Stripe add-on (or Authorize.Net) is active.
152
153 // Add an extra check to avoid white page styling on the PayPal "edit" action.
154 // We fallback to the PayPal add on for the "edit" action since Stripe Lite does not have an edit view.
155 if ( ! in_array( FrmAppHelper::simple_get( 'action' ), array( 'edit', 'new' ), true ) || ! is_callable( 'FrmPaymentsController::route' ) ) {
156 $white_pages[] = 'formidable-payments';
157 }
158 }
159
160 $is_white_page = self::is_page_in_list( $white_pages ) || self::is_grey_page() || FrmAppHelper::is_view_builder_page();
161
162 /**
163 * Allow another add on to style a page as a Formidable "white page", which adds a white background color.
164 *
165 * @since 5.3
166 *
167 * @param bool $is_white_page
168 */
169 $is_white_page = apply_filters( 'frm_is_white_page', $is_white_page );
170
171 return $is_white_page;
172 }
173
174 /**
175 * Add a grey bg instead of white.
176 *
177 * @since 6.8
178 *
179 * @return bool
180 */
181 private static function is_grey_page() {
182 $grey_pages = array(
183 'formidable-applications',
184 'formidable-dashboard',
185 );
186
187 $is_grey_page = self::is_page_in_list( $grey_pages );
188
189 /**
190 * Filter to change FF wrapper background to grey.
191 *
192 * @since 6.8
193 *
194 * @param bool $is_grey_page
195 * @return bool
196 */
197 return apply_filters( 'frm_is_grey_page', $is_grey_page );
198 }
199
200 /**
201 * @since 6.8
202 *
203 * @param array $pages A list of page names to check.
204 * @return bool
205 */
206 private static function is_page_in_list( $pages ) {
207 $get_page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
208 return in_array( $get_page, $pages, true );
209 }
210
211 /**
212 * @return void
213 */
214 public static function load_wp_admin_style() {
215 FrmAppHelper::load_font_style();
216 }
217
218 /**
219 * @param int|object $form
220 * @param bool $show_nav
221 * @param string $title
222 *
223 * @psalm-param 'hide'|'show' $title
224 *
225 * @return void
226 */
227 public static function get_form_nav( $form, $show_nav = false, $title = 'show' ) {
228 $show_nav = FrmAppHelper::get_param( 'show_nav', $show_nav, 'get', 'absint' );
229 if ( empty( $show_nav ) || ! $form ) {
230 return;
231 }
232
233 FrmForm::maybe_get_form( $form );
234 if ( ! is_object( $form ) ) {
235 return;
236 }
237
238 $id = $form->id;
239 $current_page = self::get_current_page();
240 $nav_items = self::get_form_nav_items( $form );
241
242 include FrmAppHelper::plugin_path() . '/classes/views/shared/form-nav.php';
243 }
244
245 /**
246 * @return string
247 */
248 private static function get_current_page() {
249 $page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
250 $post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title', 'None' );
251 $current_page = isset( $_GET['page'] ) ? $page : $post_type;
252
253 if ( FrmAppHelper::is_view_builder_page() ) {
254 $current_page = 'frm_display';
255 }
256
257 return $current_page;
258 }
259
260 /**
261 * @param object $form
262 * @return array
263 */
264 private static function get_form_nav_items( $form ) {
265 $id = $form->parent_form_id ? $form->parent_form_id : $form->id;
266
267 $nav_items = array(
268 array(
269 'link' => FrmForm::get_edit_link( $id ),
270 'label' => __( 'Build', 'formidable' ),
271 'current' => array( 'edit', 'new', 'duplicate' ),
272 'page' => 'formidable',
273 'permission' => 'frm_edit_forms',
274 ),
275 array(
276 'link' => FrmStylesHelper::get_list_url( $id ),
277 'label' => __( 'Style', 'formidable' ),
278 'current' => array(),
279 'page' => 'formidable-styles',
280 'permission' => 'frm_edit_forms',
281 ),
282 array(
283 'link' => admin_url( 'admin.php?page=formidable&frm_action=settings&id=' . absint( $id ) ),
284 'label' => __( 'Settings', 'formidable' ),
285 'current' => array( 'settings' ),
286 'page' => 'formidable',
287 'permission' => 'frm_edit_forms',
288 ),
289 array(
290 'link' => admin_url( 'admin.php?page=formidable-entries&frm_action=list&form=' . absint( $id ) ),
291 'label' => __( 'Entries', 'formidable' ),
292 'current' => array(),
293 'page' => 'formidable-entries',
294 'permission' => 'frm_view_entries',
295 ),
296 );
297
298 $views_installed = is_callable( 'FrmProAppHelper::views_is_installed' ) && FrmProAppHelper::views_is_installed();
299
300 if ( ! $views_installed ) {
301 $nav_items[] = array(
302 'link' => admin_url( 'admin.php?page=formidable-views&form=' . absint( $id ) ),
303 'label' => __( 'Views', 'formidable' ),
304 'current' => array(),
305 'page' => 'formidable-views',
306 'permission' => 'frm_view_entries',
307 'atts' => array(
308 'class' => 'frm_noallow',
309 ),
310 );
311 }
312
313 // Let people know reports and views exist.
314 if ( ! FrmAppHelper::pro_is_installed() ) {
315 $nav_items[] = array(
316 'link' => admin_url( 'admin.php?page=formidable&frm_action=lite-reports&form=' . absint( $id ) ),
317 'label' => __( 'Reports', 'formidable' ),
318 'current' => array( 'reports' ),
319 'page' => 'formidable',
320 'permission' => 'frm_view_entries',
321 'atts' => array(
322 'class' => 'frm_noallow',
323 ),
324 );
325 }
326
327 $nav_args = array(
328 'form_id' => $id,
329 'form' => $form,
330 );
331
332 return (array) apply_filters( 'frm_form_nav_list', $nav_items, $nav_args );
333 }
334
335 // Adds a settings link to the plugins page
336 /**
337 * @return array
338 */
339 public static function settings_link( $links ) {
340 $settings = array();
341
342 if ( ! FrmAppHelper::pro_is_installed() ) {
343 $label = FrmAddonsController::is_license_expired() ? __( 'Renew', 'formidable' ) : __( 'Upgrade to Pro', 'formidable' );
344 $settings[] = '<a href="' . esc_url( FrmAppHelper::admin_upgrade_link( 'plugin-row' ) ) . '" target="_blank" rel="noopener"><b style="color:#1da867;font-weight:700;">' . esc_html( $label ) . '</b></a>';
345 }
346
347 $settings[] = '<a href="' . esc_url( admin_url( 'admin.php?page=formidable' ) ) . '">' . __( 'Build a Form', 'formidable' ) . '</a>';
348
349 return array_merge( $settings, $links );
350 }
351
352 /**
353 * @return void
354 */
355 public static function pro_get_started_headline() {
356 self::review_request();
357 FrmAppHelper::min_pro_version_notice( '6.0' );
358 }
359
360 /**
361 * Add admin notices as needed for reviews
362 *
363 * @since 3.04.03
364 *
365 * @return void
366 */
367 private static function review_request() {
368 $reviews = new FrmReviews();
369 $reviews->review_request();
370 }
371
372 /**
373 * Save the request to hide the review
374 *
375 * @since 3.04.03
376 *
377 * @return void
378 */
379 public static function dismiss_review() {
380 FrmAppHelper::permission_check( 'frm_change_settings' );
381 check_ajax_referer( 'frm_ajax', 'nonce' );
382
383 $reviews = new FrmReviews();
384 $reviews->dismiss_review();
385 }
386
387 /**
388 * @since 3.04.02
389 *
390 * @return void
391 */
392 public static function include_upgrade_overlay() {
393 self::enqueue_dialog_assets();
394 add_action( 'admin_footer', 'FrmAppController::upgrade_overlay_html' );
395 }
396
397 /**
398 * Enqueue scripts and styles required for modals.
399 *
400 * @since 5.3
401 *
402 * @return void
403 */
404 public static function enqueue_dialog_assets() {
405 wp_enqueue_script( 'jquery-ui-dialog' );
406 wp_enqueue_style( 'jquery-ui-dialog' );
407 }
408
409 /**
410 * @since 3.06.03
411 *
412 * @return void
413 */
414 public static function upgrade_overlay_html() {
415 $is_pro = FrmAppHelper::pro_is_installed();
416 $upgrade_link = array(
417 'medium' => 'builder',
418 'content' => 'upgrade',
419 );
420 $default_link = FrmAppHelper::admin_upgrade_link( $upgrade_link );
421 $plugin_path = FrmAppHelper::plugin_path();
422 $shared_path = $plugin_path . '/classes/views/shared/';
423
424 include $shared_path . 'upgrade_overlay.php';
425 include $shared_path . 'confirm-overlay.php';
426 }
427
428 /**
429 * Create a basic form with an email field.
430 *
431 * @param string $form_key
432 * @param string $title
433 * @param string $description
434 * @return void
435 */
436 public static function api_email_form( $form_key, $title = '', $description = '' ) {
437 $user = wp_get_current_user();
438 $args = array(
439 'api_url' => 'https://sandbox.formidableforms.com/api/wp-json/frm/v2/forms/' . $form_key . '?return=html&exclude_script=jquery&exclude_style=formidable-css',
440 'title' => $title,
441 'description' => $description,
442 );
443 require FrmAppHelper::plugin_path() . '/classes/views/form-templates/modals/leave-email-modal.php';
444 }
445
446 /**
447 * @return void
448 */
449 public static function include_info_overlay() {
450 self::enqueue_dialog_assets();
451 add_action( 'admin_footer', 'FrmAppController::info_overlay_html' );
452 }
453
454 /**
455 * @return void
456 */
457 public static function info_overlay_html() {
458 include FrmAppHelper::plugin_path() . '/classes/views/shared/info-overlay.php';
459 }
460
461 /**
462 * @since 3.04.02
463 *
464 * @return void
465 */
466 public static function remove_upsells() {
467 remove_action( 'frm_before_settings', 'FrmSettingsController::license_box' );
468 remove_action( 'frm_after_settings', 'FrmSettingsController::settings_cta' );
469 remove_action( 'frm_add_form_style_tab_options', 'FrmFormsController::add_form_style_tab_options' );
470 remove_action( 'frm_after_field_options', 'FrmFormsController::logic_tip' );
471 }
472
473 /**
474 * If there are CURL problems on this server, wp_remote_post won't work for installing
475 * Use a javascript fallback instead.
476 *
477 * @since 2.0.3
478 *
479 * @return void
480 */
481 public static function install_js_fallback() {
482 FrmAppHelper::load_admin_wide_js();
483 ?>
484 <div id="frm_install_message"></div>
485 <script>jQuery(document).ready( frm_install_now );</script>
486 <?php
487 }
488
489 /**
490 * Check if the database is outdated
491 *
492 * @since 2.0.1
493 * @return bool
494 */
495 public static function needs_update() {
496 $needs_upgrade = self::compare_for_update(
497 array(
498 'option' => 'frm_db_version',
499 'new_db_version' => FrmAppHelper::$db_version,
500 'new_plugin_version' => FrmAppHelper::plugin_version(),
501 )
502 );
503
504 if ( ! $needs_upgrade ) {
505 $needs_upgrade = apply_filters( 'frm_db_needs_upgrade', $needs_upgrade );
506 }
507
508 return $needs_upgrade;
509 }
510
511 /**
512 * Check both version number and DB number for changes
513 *
514 * @since 3.0.04
515 *
516 * @param array $atts
517 * @return bool
518 */
519 public static function compare_for_update( $atts ) {
520 $db_version = get_option( $atts['option'] );
521
522 if ( strpos( $db_version, '-' ) === false ) {
523 $needs_upgrade = true;
524 } else {
525 $last_upgrade = explode( '-', $db_version );
526 $needs_db_upgrade = (int) $last_upgrade[1] < (int) $atts['new_db_version'];
527 $new_version = version_compare( $last_upgrade[0], $atts['new_plugin_version'], '<' );
528 $needs_upgrade = $needs_db_upgrade || $new_version;
529 }
530
531 return $needs_upgrade;
532 }
533
534 /**
535 * Check for database update and trigger js loading
536 *
537 * @since 2.0.1
538 *
539 * @return void
540 */
541 public static function admin_init() {
542 if ( FrmAppHelper::get_param( 'delete_all' ) && FrmAppHelper::is_admin_page( 'formidable' ) && 'trash' === FrmAppHelper::get_param( 'form_type' ) ) {
543 FrmFormsController::delete_all();
544 }
545
546 if ( FrmAppHelper::is_admin_page( 'formidable' ) && 'duplicate' === FrmAppHelper::get_param( 'frm_action' ) ) {
547 FrmFormsController::duplicate();
548 }
549
550 if ( FrmAppHelper::is_style_editor_page() && 'save' === FrmAppHelper::get_param( 'frm_action' ) ) {
551 // Hook in earlier than FrmStylesController::route so we can redirect before the headers have been sent.
552 FrmStylesController::save_style();
553 }
554
555 // Register personal data hooks.
556 new FrmPersonalData();
557
558 if ( ! FrmAppHelper::doing_ajax() && self::needs_update() ) {
559 self::network_upgrade_site();
560 }
561
562 if ( ! FrmAppHelper::doing_ajax() ) {
563 // don't continue during ajax calls
564 self::admin_js();
565 }
566
567 self::trigger_page_load_hooks();
568
569 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
570 // Redirect to the "Form Templates" page if the 'frm_action' parameter matches specific actions.
571 // This provides backward compatibility for old addons that use legacy modal templates.
572 $action = FrmAppHelper::get_param( 'frm_action' );
573 $trigger_name_modal = FrmAppHelper::get_param( 'triggerNewFormModal' );
574 if ( $trigger_name_modal || in_array( $action, array( 'add_new', 'list_templates' ), true ) ) {
575 $application_id = FrmAppHelper::simple_get( 'applicationId', 'absint' );
576 $url_param = $application_id ? '&applicationId=' . $application_id : '';
577
578 wp_safe_redirect( admin_url( 'admin.php?page=' . FrmFormTemplatesController::PAGE_SLUG . $url_param ) );
579 exit;
580 }
581
582 FrmInbox::maybe_disable_screen_options();
583 }
584
585 self::maybe_add_ip_warning();
586 }
587
588 /**
589 * Get the current page and check for a possible function to trigger.
590 * If a class name matches the page name, and the class has a load_page() method, trigger it.
591 *
592 * @since 6.8
593 *
594 * @return void
595 */
596 private static function trigger_page_load_hooks() {
597 $page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
598 if ( strpos( $page, 'formidable-' ) !== 0 ) {
599 // Only trigger hooks on Formidable pages.
600 return;
601 }
602
603 $page = str_replace( array( 'formidable-', '-' ), array( '', ' ' ), $page );
604 $page = str_replace( ' ', '', ucwords( $page ) );
605 $class = 'Frm' . $page . 'Controller';
606 if ( class_exists( $class ) && method_exists( $class, 'load_page' ) ) {
607 call_user_func( array( $class, 'load_page' ) );
608 }
609 }
610
611 /**
612 * Show a warning for the IP address setting if it hasn't been set.
613 *
614 * @since 6.1
615 *
616 * @return void
617 */
618 private static function maybe_add_ip_warning() {
619 $settings = FrmAppHelper::get_settings();
620 if ( false !== $settings->custom_header_ip ) {
621 // The setting has been changed from the false default (to either 1 or 0), so stop showing the message.
622 return;
623 }
624
625 if ( ! self::is_behind_proxy() ) {
626 // This message is only applicable when using a reverse proxy.
627 return;
628 }
629
630 if ( FrmAppHelper::get_post_param( 'frm_action', '', 'sanitize_text_field' ) ) {
631 // Avoid the message on a POST action. We don't want to show the message if we're saving global settings.
632 return;
633 }
634
635 $global_settings_link = admin_url( 'admin.php?page=formidable-settings' ) . '#frm_custom_header_ip';
636 $message = sprintf(
637 // Translators: 1: Global Settings Link
638 __( 'IP addresses in form submissions may no longer be accurate! If you are experiencing issues, we recommend going to %1$s and enabling the "Use custom headers when retrieving IPs with form submissions." setting.', 'formidable' ),
639 '<a href="' . esc_url( $global_settings_link ) . '">' . __( 'Global Settings', 'formidable' ) . '</a>'
640 );
641 $option_name = 'frm_dismiss_ip_address_notice';
642 FrmAppHelper::add_dismissable_warning_message( $message, $option_name );
643 }
644
645 /**
646 * Check if any reverse proxy headers are set.
647 *
648 * @since 6.1
649 *
650 * @return bool
651 */
652 private static function is_behind_proxy() {
653 $custom_headers = FrmAppHelper::get_custom_header_keys_for_ip();
654 foreach ( $custom_headers as $header ) {
655 if ( 'REMOTE_ADDR' === $header ) {
656 // We want to check every key but REMOTE_ADDR. REMOTE_ATTR is not unique to reverse proxy servers.
657 continue;
658 }
659
660 $ip = trim( FrmAppHelper::get_server_value( $header ) );
661 // Return true for anything that isn't empty but ignoring values like ::1.
662 if ( $ip && 0 !== strpos( $ip, '::' ) ) {
663 return true;
664 }
665 }
666
667 return false;
668 }
669
670 /**
671 * @return void
672 */
673 public static function admin_js() {
674 $plugin_url = FrmAppHelper::plugin_url();
675 $version = FrmAppHelper::plugin_version();
676
677 FrmAppHelper::load_admin_wide_js();
678
679 // Register component assets early to ensure they can be enqueued later in controllers.
680 wp_register_style( 'formidable-animations', $plugin_url . '/css/admin/animations.css', array(), $version );
681
682 if ( class_exists( 'FrmOverlayController' ) ) {
683 // This should always exist.
684 // But it may not have loaded properly when updating the plugin.
685 FrmOverlayController::register_assets();
686 }
687
688 wp_register_style( 'formidable_admin_global', $plugin_url . '/css/admin/frm_admin_global.css', array(), $version );
689 wp_enqueue_style( 'formidable_admin_global' );
690
691 wp_register_style( 'formidable-admin', $plugin_url . '/css/frm_admin.css', array(), $version );
692 wp_register_style( 'formidable-grids', $plugin_url . '/css/frm_grids.css', array(), $version );
693
694 wp_register_script( 'formidable_dom', $plugin_url . '/js/admin/dom.js', array( 'jquery', 'jquery-ui-dialog', 'wp-i18n' ), $version, true );
695 wp_register_script( 'formidable_embed', $plugin_url . '/js/admin/embed.js', array( 'formidable_dom', 'jquery-ui-autocomplete' ), $version, true );
696 self::register_popper1();
697 wp_register_script( 'bootstrap_tooltip', $plugin_url . '/js/bootstrap.min.js', array( 'jquery', 'popper' ), '4.6.1', true );
698 wp_register_script( 'formidable_settings', $plugin_url . '/js/admin/settings.js', array(), $version, true );
699
700 if ( self::should_show_floating_links() ) {
701 self::enqueue_floating_links( $plugin_url, $version );
702 }
703
704 $dependencies = array(
705 'formidable_admin_global',
706 'jquery',
707 'jquery-ui-core',
708 'jquery-ui-draggable',
709 'jquery-ui-sortable',
710 'bootstrap_tooltip',
711 'bootstrap-multiselect',
712 'wp-i18n',
713 // Required in WP versions older than 5.7
714 'wp-hooks',
715 'formidable_dom',
716 'formidable_embed',
717 );
718
719 if ( FrmAppHelper::is_style_editor_page( 'edit' ) ) {
720 // We only need to load the color picker when editing styles.
721 $dependencies[] = 'wp-color-picker';
722 }
723
724 wp_register_script( 'formidable_admin', $plugin_url . '/js/formidable_admin.js', $dependencies, $version, true );
725
726 if ( FrmAppHelper::on_form_listing_page() ) {
727 // For the existing page dropdown in the Form embed modal.
728 wp_enqueue_script( 'jquery-ui-autocomplete' );
729 }
730
731 wp_register_script( 'bootstrap-multiselect', $plugin_url . '/js/bootstrap-multiselect.js', array( 'jquery', 'bootstrap_tooltip', 'popper' ), '1.1.1', true );
732
733 if ( ! class_exists( 'FrmTransHooksController', false ) ) {
734 /**
735 * Gateway fields are included for add-on compatibility but we do not want it to be visible.
736 * They do however need to be visible when the payments submodule is active.
737 */
738 wp_add_inline_style(
739 'formidable-admin',
740 '#frm_builder_page li[data-ftype="gateway"] { display: none; }'
741 );
742 }
743
744 $page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
745 $post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title' );
746
747 global $pagenow;
748 if ( strpos( $page, 'formidable' ) === 0 || ( $pagenow === 'edit.php' && $post_type === 'frm_display' ) ) {
749
750 wp_enqueue_script( 'admin-widgets' );
751 wp_enqueue_style( 'widgets' );
752 self::maybe_deregister_popper2();
753 wp_enqueue_script( 'formidable_admin' );
754 wp_enqueue_script( 'formidable_embed' );
755 FrmAppHelper::localize_script( 'admin' );
756
757 wp_enqueue_style( 'formidable-animations' );
758 wp_enqueue_style( 'formidable-admin' );
759 if ( 'formidable-styles' !== $page && 'formidable-styles2' !== $page ) {
760 wp_enqueue_style( 'formidable-grids' );
761 self::maybe_enqueue_dropzone_css( $page );
762 } else {
763 wp_enqueue_style( 'formidable-grids' );
764 }
765
766 if ( 'formidable-entries' === $page ) {
767 // Load front end js for entries.
768 wp_enqueue_script( 'formidable' );
769
770 // Registers and enqueues the entries page scripts.
771 wp_register_script( 'formidable_entries', $plugin_url . '/js/admin/entries.js', array( 'formidable_admin', 'wp-dom-ready' ), $version, true );
772 wp_enqueue_script( 'formidable_entries' );
773 }
774
775 do_action( 'frm_enqueue_builder_scripts' );
776 self::include_upgrade_overlay();
777 self::include_info_overlay();
778 } elseif ( FrmAppHelper::is_view_builder_page() ) {
779 if ( isset( $_REQUEST['post_type'] ) ) {
780 $post_type = sanitize_title( wp_unslash( $_REQUEST['post_type'] ) );
781 } elseif ( isset( $_REQUEST['post'] ) && absint( $_REQUEST['post'] ) ) {
782 $post = get_post( absint( wp_unslash( $_REQUEST['post'] ) ) );
783 if ( ! $post ) {
784 return;
785 }
786 $post_type = $post->post_type;
787 } else {
788 return;
789 }
790
791 if ( $post_type === 'frm_display' ) {
792 self::enqueue_legacy_views_assets();
793 }
794 }//end if
795
796 if ( 'formidable-addons' === $page ) {
797 wp_register_script( 'formidable_addons', $plugin_url . '/js/admin/addons.js', array( 'formidable_admin', 'wp-dom-ready' ), $version, true );
798 wp_enqueue_script( 'formidable_addons' );
799 }
800 }
801
802 /**
803 * Avoid loading dropzone CSS on the form list page. It isn't required there.
804 *
805 * @since 6.11
806 *
807 * @param string $page
808 * @return void
809 */
810 private static function maybe_enqueue_dropzone_css( $page ) {
811 if ( ! FrmAppHelper::pro_is_installed() ) {
812 return;
813 }
814
815 $should_avoid_loading_dropzone = 'formidable' === $page && ! FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' );
816 if ( ! $should_avoid_loading_dropzone ) {
817 wp_enqueue_style( 'formidable-dropzone' );
818 }
819 }
820
821 /**
822 * The floating links are not shown on every page.
823 * They are also not shown if white labeling is being used.
824 *
825 * @since 6.8.4
826 *
827 * @return bool
828 */
829 private static function should_show_floating_links() {
830 if ( ! FrmAppHelper::is_formidable_branding() ) {
831 return false;
832 }
833
834 return FrmAppHelper::is_formidable_admin() &&
835 ! FrmAppHelper::is_style_editor_page() &&
836 ! FrmAppHelper::is_admin_page( 'formidable-views-editor' ) &&
837 ! FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' );
838 }
839
840 /**
841 * @since 6.3.2
842 *
843 * @return void
844 */
845 public static function admin_enqueue_scripts() {
846 self::load_wp_admin_style();
847 self::maybe_force_formidable_block_on_gutenberg_page();
848 }
849
850 /**
851 * Enqueue required assets for the Legacy Views editor (Views v4.x).
852 *
853 * @since 6.1.2
854 *
855 * @return void
856 */
857 private static function enqueue_legacy_views_assets() {
858 wp_enqueue_style( 'formidable-grids' );
859 wp_enqueue_script( 'jquery-ui-draggable' );
860 self::maybe_deregister_popper2();
861 wp_enqueue_script( 'formidable_admin' );
862 wp_add_inline_style(
863 'formidable-admin',
864 '
865 .frm-white-body.post-type-frm_display .columns-2 {
866 display: block;
867 overflow: visible;
868 }
869
870 .frm-white-body.post-type-frm_display .columns-2 > div {
871 overflow-y: hidden;
872 }
873
874 .frm-white-body.post-type-frm_display #titlediv #title-prompt-text {
875 padding: 3px 0 0 10px;
876 }
877
878 .post-type-frm_display #field-search-input,
879 .post-type-frm_display #advanced-search-input {
880 flex: 1;
881 }
882 '
883 );
884 wp_enqueue_style( 'formidable-admin' );
885 FrmAppHelper::localize_script( 'admin' );
886 self::include_info_overlay();
887 }
888
889 /**
890 * Fix a Duplicator Pro conflict because it uses Popper 2. See issue #3459.
891 *
892 * @since 5.2.02.01
893 *
894 * @return void
895 */
896 private static function maybe_deregister_popper2() {
897 global $wp_scripts;
898
899 if ( ! array_key_exists( 'popper', $wp_scripts->registered ) ) {
900 return;
901 }
902
903 $popper = $wp_scripts->registered['popper'];
904 if ( version_compare( $popper->ver, '2.0', '>=' ) ) {
905 wp_deregister_script( 'popper' );
906 self::register_popper1();
907 }
908 }
909
910 /**
911 * Register Popper required for Bootstrap 4.
912 *
913 * @since 5.2.02.01
914 *
915 * @return void
916 */
917 private static function register_popper1() {
918 if ( ! self::should_register_popper() ) {
919 return;
920 }
921 wp_register_script( 'popper', FrmAppHelper::plugin_url() . '/js/popper.min.js', array( 'jquery' ), '1.16.0', true );
922 }
923
924 /**
925 * Only register popper on Formidable pages.
926 * This helps to avoid popper conflicts on other plugin pages, including the WP Bakery page editor.
927 *
928 * @since 6.11.1
929 *
930 * @return bool
931 */
932 private static function should_register_popper() {
933 global $pagenow;
934
935 $post_id = FrmAppHelper::simple_get( 'post', 'absint' );
936 if ( 'post.php' === $pagenow && $post_id && 'frm_display' === get_post_type( $post_id ) ) {
937 return true;
938 }
939
940 $post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title' );
941 $is_views_post_type = 'frm_display' === $post_type;
942 if ( in_array( $pagenow, array( 'post-new.php', 'edit.php' ), true ) && $is_views_post_type ) {
943 return true;
944 }
945
946 $page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
947 if ( strpos( $page, 'formidable' ) === 0 ) {
948 return true;
949 }
950
951 return in_array( $pagenow, array( 'term.php', 'edit-tags.php' ), true ) && FrmAppHelper::simple_get( 'taxonomy' ) === 'frm_application';
952 }
953
954 /**
955 * Automatically insert a Formidable block when loading Gutenberg when $_GET['frmForm' is set.
956 *
957 * @since 5.2
958 *
959 * @return void
960 */
961 private static function maybe_force_formidable_block_on_gutenberg_page() {
962 global $pagenow;
963 if ( 'post.php' !== $pagenow ) {
964 return;
965 }
966
967 $form_id = FrmAppHelper::simple_get( 'frmForm', 'absint' );
968 if ( ! $form_id ) {
969 return;
970 }
971
972 self::add_js_to_inject_gutenberg_block( 'formidable/simple-form', 'formId', $form_id );
973 }
974
975 /**
976 * @since 5.3
977 *
978 * @param string $block_name
979 * @param string $object_key
980 * @param array|string $object_id
981 *
982 * @return void
983 */
984 public static function add_js_to_inject_gutenberg_block( $block_name, $object_key, $object_id ) {
985 require FrmAppHelper::plugin_path() . '/classes/views/shared/edit-page-js.php';
986 }
987
988 /**
989 * @return void
990 */
991 public static function load_lang() {
992 load_plugin_textdomain( 'formidable', false, FrmAppHelper::plugin_folder() . '/languages/' );
993 }
994
995 /**
996 * Check if the styles are updated when a form is loaded on the front-end
997 *
998 * @since 3.0.1
999 *
1000 * @return void
1001 */
1002 public static function maybe_update_styles() {
1003 if ( self::needs_update() ) {
1004 self::network_upgrade_site();
1005 }
1006 }
1007
1008 /**
1009 * @since 3.0
1010 *
1011 * @return void
1012 */
1013 public static function create_rest_routes() {
1014 $args = array(
1015 'methods' => 'GET',
1016 'callback' => 'FrmAppController::api_install',
1017 'permission_callback' => __CLASS__ . '::can_update_db',
1018 );
1019
1020 register_rest_route( 'frm-admin/v1', '/install', $args );
1021
1022 $args = array(
1023 'methods' => 'GET',
1024 'callback' => 'FrmAddonsController::install_addon_api',
1025 'permission_callback' => 'FrmAddonsController::can_install_addon_api',
1026 );
1027
1028 register_rest_route( 'frm-admin/v1', '/install-addon', $args );
1029 }
1030
1031 /**
1032 * Make sure the install is only being run when we tell it to.
1033 * We don't want to run manually by people calling the API.
1034 *
1035 * @since 4.06.02
1036 */
1037 public static function can_update_db() {
1038 return get_transient( 'frm_updating_api' );
1039 }
1040
1041 /**
1042 * Run silent upgrade on each site in the network during a network upgrade.
1043 * Update database settings for all sites in a network during network upgrade process.
1044 *
1045 * @since 2.0.1
1046 *
1047 * @param int $blog_id Blog ID.
1048 *
1049 * @return void
1050 */
1051 public static function network_upgrade_site( $blog_id = 0 ) {
1052 // Flag to check if install is happening as intended.
1053 set_transient( 'frm_updating_api', true, MINUTE_IN_SECONDS );
1054 $request = new WP_REST_Request( 'GET', '/frm-admin/v1/install' );
1055
1056 self::maybe_add_wp_site_health();
1057
1058 if ( $blog_id ) {
1059 switch_to_blog( $blog_id );
1060 $response = rest_do_request( $request );
1061 restore_current_blog();
1062 } else {
1063 $response = rest_do_request( $request );
1064 }
1065
1066 if ( $response->is_error() ) {
1067 // if the remove post fails, use javascript instead
1068 add_action( 'admin_notices', 'FrmAppController::install_js_fallback' );
1069 }
1070 }
1071
1072 /**
1073 * Make sure WP_Site_Health has been included because it is required when calling rest_do_request.
1074 * Check first that the file exists because WP_Site_Health was only introduced in WordPress 5.2.
1075 *
1076 * @return void
1077 */
1078 private static function maybe_add_wp_site_health() {
1079 if ( ! class_exists( 'WP_Site_Health' ) ) {
1080 $wp_site_health_path = ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
1081 if ( file_exists( $wp_site_health_path ) ) {
1082 require_once $wp_site_health_path;
1083 }
1084 }
1085 }
1086
1087 /**
1088 * @since 3.0
1089 *
1090 * @return true
1091 */
1092 public static function api_install() {
1093 delete_transient( 'frm_updating_api' );
1094 if ( self::needs_update() ) {
1095 $running = get_option( 'frm_install_running' );
1096 if ( false === $running || $running < strtotime( '-5 minutes' ) ) {
1097 update_option( 'frm_install_running', time(), 'no' );
1098 self::install();
1099 delete_option( 'frm_install_running' );
1100 }
1101 }
1102
1103 return true;
1104 }
1105
1106 /**
1107 * Silent database upgrade (no redirect).
1108 * Called via ajax request during network upgrade process.
1109 *
1110 * @since 2.0.1
1111 *
1112 * @return void
1113 */
1114 public static function ajax_install() {
1115 FrmAppHelper::permission_check( 'frm_change_settings' );
1116 check_ajax_referer( 'frm_ajax', 'nonce' );
1117 self::api_install();
1118 wp_die();
1119 }
1120
1121 /**
1122 * @return void
1123 */
1124 public static function install() {
1125 $frmdb = new FrmMigrate();
1126 $frmdb->upgrade();
1127 }
1128
1129 /**
1130 * @return void
1131 */
1132 public static function uninstall() {
1133 FrmAppHelper::permission_check( 'administrator' );
1134 check_ajax_referer( 'frm_ajax', 'nonce' );
1135
1136 $frmdb = new FrmMigrate();
1137 $frmdb->uninstall();
1138
1139 // Disable the plugin and redirect after uninstall so the tables don't get added right back.
1140 $plugins = array( FrmAppHelper::plugin_folder() . '/formidable.php', 'formidable-pro/formidable-pro.php' );
1141 deactivate_plugins( $plugins, false, false );
1142 echo esc_url_raw( admin_url( 'plugins.php?deactivate=true' ) );
1143
1144 wp_die();
1145 }
1146
1147 public static function drop_tables( $tables ) {
1148 global $wpdb;
1149 $tables[] = $wpdb->prefix . 'frm_fields';
1150 $tables[] = $wpdb->prefix . 'frm_forms';
1151 $tables[] = $wpdb->prefix . 'frm_items';
1152 $tables[] = $wpdb->prefix . 'frm_item_metas';
1153
1154 return $tables;
1155 }
1156
1157 /**
1158 * @return void
1159 */
1160 public static function deauthorize() {
1161 FrmAppHelper::permission_check( 'frm_change_settings' );
1162 check_ajax_referer( 'frm_ajax', 'nonce' );
1163
1164 delete_option( 'frmpro-credentials' );
1165 delete_option( 'frmpro-authorized' );
1166 delete_site_option( 'frmpro-credentials' );
1167 delete_site_option( 'frmpro-authorized' );
1168 wp_die();
1169 }
1170
1171 /**
1172 * This is triggered when Formidable is activated.
1173 *
1174 * @return void
1175 */
1176 public static function handle_activation() {
1177 self::maybe_activate_payment_cron();
1178 }
1179
1180 /**
1181 * The payment cron is unscheduled when Formidable is deactivated.
1182 * We need to add it back again on activation if Stripe is configured.
1183 *
1184 * @since 6.5
1185 *
1186 * @return void
1187 */
1188 private static function maybe_activate_payment_cron() {
1189 if ( ! FrmStrpLiteConnectHelper::stripe_connect_is_setup() ) {
1190 return;
1191 }
1192
1193 FrmTransLiteAppController::maybe_schedule_cron();
1194 }
1195
1196 public static function set_footer_text( $text ) {
1197 if ( FrmAppHelper::is_formidable_admin() ) {
1198 $text = '';
1199 }
1200
1201 return $text;
1202 }
1203
1204 /**
1205 * Add admin footer links.
1206 *
1207 * @since 6.4.1
1208 *
1209 * @return void
1210 */
1211 public static function add_admin_footer_links() {
1212 if ( self::should_show_footer_links() ) {
1213 include FrmAppHelper::plugin_path() . '/classes/views/shared/admin-footer-links.php';
1214 }
1215 }
1216
1217 /**
1218 * Check if the footer links should be shown.
1219 *
1220 * @since 6.7
1221 *
1222 * @return bool
1223 */
1224 private static function should_show_footer_links() {
1225 $post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title' );
1226 $is_formidable_page = FrmAppHelper::is_formidable_admin() || 'frm_logs' === $post_type;
1227 $show_footer_links = $is_formidable_page;
1228 if ( FrmAppHelper::is_full_screen() || ! FrmAppHelper::is_formidable_branding() ) {
1229 $show_footer_links = false;
1230 }
1231
1232 /**
1233 * Filter whether to show the Formidable footer links.
1234 *
1235 * @since 6.7
1236 *
1237 * @param bool $show_footer_links
1238 * @return bool
1239 */
1240 return apply_filters( 'frm_show_footer_links', $show_footer_links );
1241 }
1242
1243 /**
1244 * Show an error modal and terminate the script execution.
1245 *
1246 * @since 6.7
1247 *
1248 * @param array $error_args Arguments that control the behavior of the error modal.
1249 *
1250 * @return void
1251 */
1252 public static function show_error_modal( $error_args ) {
1253 $defaults = array(
1254 'title' => '',
1255 'body' => '',
1256 'cancel_url' => '',
1257 'cancel_classes' => '',
1258 'continue_url' => '',
1259 'continue_classes' => '',
1260 'icon' => 'frm_lock_simple',
1261 );
1262
1263 $error_args = wp_parse_args( $error_args, $defaults );
1264 if ( ! isset( $error_args['cancel_text'] ) && ! empty( $error_args['cancel_url'] ) ) {
1265 $error_args['cancel_text'] = __( 'Cancel', 'formidable' );
1266 }
1267
1268 if ( ! isset( $error_args['continue_text'] ) && ! empty( $error_args['continue_url'] ) ) {
1269 $error_args['continue_text'] = __( 'Continue', 'formidable' );
1270 }
1271
1272 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/error-modal.php';
1273 }
1274
1275 /**
1276 * Handles Floating Links' scripts and styles enqueueing.
1277 *
1278 * @since 6.4
1279 *
1280 * @param string $plugin_url URL of the plugin.
1281 * @param string $version Current version of the plugin.
1282 * @return void
1283 */
1284 private static function enqueue_floating_links( $plugin_url, $version ) {
1285 if ( ! $plugin_url || ! $version ) {
1286 // If any required parameters are missing, exit early.
1287 return;
1288 }
1289
1290 // Enqueue the Floating Links styles.
1291 wp_enqueue_style( 's11-floating-links', $plugin_url . '/css/packages/s11-floating-links.css', array(), $version );
1292
1293 // Enqueue the Floating Links script.
1294 wp_enqueue_script( 's11-floating-links', $plugin_url . '/js/packages/floating-links/s11-floating-links.js', array(), $version, true );
1295
1296 // Enqueue the config script.
1297 wp_enqueue_script( 's11-floating-links-config', $plugin_url . '/js/packages/floating-links/config.js', array( 'wp-i18n' ), $version, true );
1298
1299 if ( function_exists( 'wp_set_script_translations' ) ) {
1300 wp_set_script_translations( 's11-floating-links-config', 's11-' );
1301 }
1302
1303 $floating_links_data = array(
1304 'proIsInstalled' => FrmAppHelper::pro_is_installed(),
1305 );
1306 wp_localize_script( 's11-floating-links-config', 's11FloatingLinksData', $floating_links_data );
1307
1308 /**
1309 * Prompt Pro to load additional floating links scripts.
1310 * This is used to include images in the Inbox SlideIn when Pro is active.
1311 *
1312 * @since 6.8.4
1313 */
1314 do_action( 'frm_enqueue_floating_links' );
1315 }
1316
1317 /**
1318 * @deprecated 3.0 This is still referenced in https://formidableforms.com/knowledgebase/php-examples/ as of May 8, 2024.
1319 * @codeCoverageIgnore
1320 */
1321 public static function page_route( $content ) {
1322 return FrmDeprecated::page_route( $content );
1323 }
1324 }
1325