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

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