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

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