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

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

725 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Onboarding Wizard Controller class.
4 *
5 * @package Formidable
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 die( 'You are not allowed to call this page directly.' );
10 }
11
12 /**
13 * Handles the Onboarding Wizard page in the admin area.
14 *
15 * @since 6.9
16 */
17 class FrmOnboardingWizardController {
18
19 /**
20 * The slug of the Onboarding Wizard page.
21 *
22 * @var string
23 */
24 const PAGE_SLUG = 'formidable-onboarding-wizard';
25
26 /**
27 * The script handle.
28 *
29 * @var string
30 */
31 const SCRIPT_HANDLE = 'frm-onboarding-wizard';
32
33 /**
34 * The required user capability to view the Onboarding Wizard page.
35 *
36 * @var string
37 */
38 const REQUIRED_CAPABILITY = 'frm_view_forms';
39
40 /**
41 * Transient name used for managing redirection to the Onboarding Wizard page.
42 *
43 * @var string
44 */
45 const TRANSIENT_NAME = 'frm_activation_redirect';
46
47 /**
48 * Transient value associated with the redirection to the Onboarding Wizard page.
49 * Used when activating a single plugin.
50 *
51 * @var string
52 */
53 const TRANSIENT_VALUE = 'formidable-welcome';
54
55 /**
56 * Transient value associated with the redirection to the Onboarding Wizard page.
57 * Used when activating multiple plugins at once.
58 *
59 * @var string
60 */
61 const TRANSIENT_MULTI_VALUE = 'formidable-welcome-multi';
62
63 /**
64 * Option name for storing the redirect status for the Onboarding Wizard page.
65 *
66 * @var string
67 */
68 const REDIRECT_STATUS_OPTION = 'frm_welcome_redirect';
69
70 /**
71 * Option name for tracking if the onboarding wizard was skipped.
72 *
73 * @var string
74 */
75 const ONBOARDING_SKIPPED_OPTION = 'frm_onboarding_skipped';
76
77 /**
78 * Defines the initial step for redirection within the application flow.
79 *
80 * @var string
81 */
82 const INITIAL_STEP = 'consent-tracking';
83
84 /**
85 * Option name to store usage data.
86 *
87 * @var string
88 */
89 const USAGE_DATA_OPTION = 'frm_onboarding_usage_data';
90
91 /**
92 * Holds the URL to access the Onboarding Wizard's page.
93 *
94 * @var string
95 */
96 private static $page_url = '';
97
98 /**
99 * Holds a list of add-ons available for installation.
100 *
101 * @var array
102 */
103 private static $available_addons = array();
104
105 /**
106 * Path to views.
107 *
108 * @var string
109 */
110 private static $view_path = '';
111
112 /**
113 * Upgrade URL.
114 *
115 * @var string
116 */
117 private static $upgrade_link = '';
118
119 /**
120 * Initialize hooks for template page only.
121 *
122 * @since 6.9
123 *
124 * @return void
125 */
126 public static function load_admin_hooks() {
127 self::set_page_url();
128 add_action( 'admin_init', self::class . '::do_admin_redirects' );
129
130 if ( self::has_onboarding_been_skipped() ) {
131 add_filter( 'option_frm_inbox', self::class . '::add_wizard_to_floating_links' );
132 }
133
134 // Load page if admin page is Onboarding Wizard.
135 self::maybe_load_page();
136 }
137
138 /**
139 * Performs a safe redirect to the welcome screen when the plugin is activated.
140 * On single activation, we will redirect immediately.
141 * When activating multiple plugins, the redirect is delayed until a Formidable page is loaded.
142 *
143 * @return void
144 */
145 public static function do_admin_redirects() {
146 $current_page = FrmAppHelper::simple_get( 'page', 'sanitize_title' );
147
148 // Prevent endless loop.
149 if ( $current_page === self::PAGE_SLUG ) {
150 return;
151 }
152
153 // Only do this for single site installs.
154 if ( is_network_admin() ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
155 self::mark_onboarding_as_skipped();
156 return;
157 }
158
159 if ( self::has_onboarding_been_skipped() || FrmAppHelper::pro_is_connected() ) {
160 return;
161 }
162
163 $transient_value = get_transient( self::TRANSIENT_NAME );
164
165 if ( ! in_array( $transient_value, array( self::TRANSIENT_VALUE, self::TRANSIENT_MULTI_VALUE ), true ) ) {
166 return;
167 }
168
169 if ( isset( $_GET['activate-multi'] ) ) {
170 /**
171 * $_GET['activate-multi'] is set after activating multiple plugins.
172 * In this case, change the transient value so we know for future checks.
173 */
174 set_transient( self::TRANSIENT_NAME, self::TRANSIENT_MULTI_VALUE, 60 );
175 return;
176 }
177
178 if ( self::TRANSIENT_MULTI_VALUE === $transient_value && ! FrmAppHelper::is_formidable_admin() ) {
179 // For multi-activations we want to only redirect when a user loads a Formidable page.
180 return;
181 }
182
183 set_transient( self::TRANSIENT_NAME, 'no', 60 );
184
185 // Prevent redirect with every activation.
186 if ( self::has_already_redirected() ) {
187 return;
188 }
189
190 // Redirect to the onboarding wizard's initial step.
191 $page_url = add_query_arg( 'step', self::INITIAL_STEP, self::$page_url );
192
193 if ( wp_safe_redirect( esc_url_raw( $page_url ) ) ) {
194 exit;
195 }
196 }
197
198 /**
199 * Initializes the Onboarding Wizard setup if on its designated admin page.
200 *
201 * @since 6.9
202 *
203 * @return void
204 */
205 public static function maybe_load_page() {
206 if ( ! self::is_onboarding_wizard_page() ) {
207 return;
208 }
209
210 // Dismiss the onboarding wizard message so it stops appearing after it is clicked.
211 $message = new FrmInbox();
212 $message->dismiss( 'onboarding_wizard' );
213
214 add_action( 'admin_menu', self::class . '::menu', 99 );
215 add_action( 'admin_init', self::class . '::assign_properties' );
216 add_action( 'admin_enqueue_scripts', self::class . '::enqueue_assets', 15 );
217 add_action( 'admin_head', self::class . '::remove_menu' );
218
219 add_filter( 'admin_body_class', self::class . '::add_admin_body_classes', 999 );
220 add_filter( 'frm_show_footer_links', '__return_false' );
221 }
222
223 /**
224 * Initializes class properties with essential values for operation.
225 *
226 * @since 6.9
227 *
228 * @return void
229 */
230 public static function assign_properties() {
231 self::$view_path = FrmAppHelper::plugin_path() . '/classes/views/onboarding-wizard/';
232
233 self::$upgrade_link = FrmAppHelper::admin_upgrade_link(
234 array(
235 'medium' => 'onboarding-wizard',
236 'content' => 'upgrade',
237 )
238 );
239
240 self::set_available_addons();
241 }
242
243 /**
244 * Add Onboarding Wizard menu item to sidebar and define index page.
245 *
246 * @since 6.9
247 *
248 * @return void
249 */
250 public static function menu() {
251 if ( ! current_user_can( 'activate_plugins' ) ) {
252 return;
253 }
254
255 $label = __( 'Onboarding Wizard', 'formidable' );
256
257 add_submenu_page(
258 'formidable',
259 'Formidable | ' . $label,
260 $label,
261 self::REQUIRED_CAPABILITY,
262 self::PAGE_SLUG,
263 array( self::class, 'render' )
264 );
265 }
266
267 /**
268 * Renders the Onboarding Wizard page in the WordPress admin area.
269 *
270 * @since 6.9
271 *
272 * @return void
273 */
274 public static function render() {
275 if ( self::has_onboarding_been_skipped() ) {
276 delete_option( self::ONBOARDING_SKIPPED_OPTION );
277 self::has_already_redirected();
278 }
279
280 // Include SVG images for icons.
281 FrmAppHelper::include_svg();
282
283 $view_path = self::get_view_path();
284 $available_addons = self::get_available_addons();
285 $upgrade_link = self::get_upgrade_link();
286 $addons_count = FrmAddonsController::get_addons_count();
287 $license_key = base64_decode( rawurldecode( FrmAppHelper::get_param( 'key', '', 'request', 'sanitize_text_field' ) ) );
288 $pro_is_installed = FrmAppHelper::pro_is_installed();
289
290 // Note: Add step parts in order.
291 $step_parts = array(
292 'consent-tracking' => 'steps/consent-tracking-step.php',
293 'install-addons' => 'steps/install-addons-step.php',
294 'success' => 'steps/success-step.php',
295 'unsuccessful' => 'steps/unsuccessful-step.php',
296 );
297
298 include $view_path . 'index.php';
299 }
300
301 /**
302 * Handle AJAX request to setup the "Never miss an important update" step.
303 *
304 * @since 6.9
305 *
306 * @return void
307 */
308 public static function ajax_consent_tracking() {
309 // Check permission and nonce.
310 FrmAppHelper::permission_check( self::REQUIRED_CAPABILITY );
311 check_ajax_referer( 'frm_ajax', 'nonce' );
312
313 // Update Settings.
314 $frm_settings = FrmAppHelper::get_settings();
315 $frm_settings->update_setting( 'tracking', true, 'rest_sanitize_boolean' );
316
317 // Remove the 'FrmProSettingsController::store' action to avoid PHP errors during AJAX call.
318 remove_action( 'frm_store_settings', 'FrmProSettingsController::store' );
319 $frm_settings->store();
320
321 FrmEmailCollectionHelper::subscribe_to_active_campaign();
322
323 // Send response.
324 wp_send_json_success();
325 }
326
327 /**
328 * Handle AJAX request to set up usage data for the Onboarding Wizard.
329 *
330 * @since 6.9
331 *
332 * @return void
333 */
334 public static function setup_usage_data() {
335 // Check permission and nonce.
336 FrmAppHelper::permission_check( self::REQUIRED_CAPABILITY );
337 check_ajax_referer( 'frm_ajax', 'nonce' );
338
339 // Retrieve the current usage data.
340 $usage_data = self::get_usage_data();
341
342 $fields_to_update = array(
343 'allows_tracking' => 'rest_sanitize_boolean',
344 'installed_addons' => 'sanitize_text_field',
345 'processed_steps' => 'sanitize_text_field',
346 'completed_steps' => 'rest_sanitize_boolean',
347 );
348
349 foreach ( $fields_to_update as $field => $sanitize_callback ) {
350 if ( isset( $_POST[ $field ] ) ) {
351 $usage_data[ $field ] = FrmAppHelper::get_post_param( $field, '', $sanitize_callback );
352 }
353 }
354
355 update_option( self::USAGE_DATA_OPTION, $usage_data );
356 wp_send_json_success();
357 }
358
359 /**
360 * Enqueues the Onboarding Wizard page scripts and styles.
361 *
362 * @since 6.9
363 *
364 * @return void
365 */
366 public static function enqueue_assets() {
367 $plugin_url = FrmAppHelper::plugin_url();
368 $version = FrmAppHelper::plugin_version();
369 $js_dependencies = array(
370 'wp-i18n',
371 // This prevents a console error "wp.hooks is undefined" in WP versions older than 5.7.
372 'wp-hooks',
373 'formidable_dom',
374 );
375
376 // Enqueue styles that needed.
377 wp_enqueue_style( 'formidable-admin' );
378 wp_enqueue_style( 'formidable-grids' );
379
380 // Register and enqueue Onboarding Wizard style.
381 wp_register_style( self::SCRIPT_HANDLE, $plugin_url . '/css/admin/onboarding-wizard.css', array(), $version );
382 wp_enqueue_style( self::SCRIPT_HANDLE );
383
384 // Register and enqueue Onboarding Wizard script.
385 wp_register_script( self::SCRIPT_HANDLE, $plugin_url . '/js/onboarding-wizard.js', $js_dependencies, $version, true );
386 wp_localize_script( self::SCRIPT_HANDLE, 'frmOnboardingWizardVars', self::get_js_variables() );
387 wp_enqueue_script( self::SCRIPT_HANDLE );
388
389 /**
390 * Fires after the Onboarding Wizard enqueue assets.
391 *
392 * @since 6.9
393 */
394 do_action( 'frm_onboarding_wizard_enqueue_assets' );
395
396 FrmAppHelper::dequeue_extra_global_scripts();
397 }
398
399 /**
400 * Get the Onboarding Wizard JS variables as an array.
401 *
402 * @since 6.9
403 *
404 * @return array
405 */
406 private static function get_js_variables() {
407 return array(
408 'INITIAL_STEP' => self::INITIAL_STEP,
409 );
410 }
411
412 /**
413 * Remove the Onboarding Wizard submenu page from the formidable parent menu
414 * since it is not necessary to show that link there.
415 *
416 * @since 6.9
417 *
418 * @return void
419 */
420 public static function remove_menu() {
421 remove_submenu_page( 'formidable', self::PAGE_SLUG );
422 }
423
424 /**
425 * Adds custom classes to the existing string of admin body classes.
426 *
427 * The function appends a custom class to the existing admin body classes, enabling full-screen mode for the admin interface.
428 *
429 * @since 6.9
430 *
431 * @param string $classes Existing body classes.
432 *
433 * @return string Updated list of body classes, including the newly added classes.
434 */
435 public static function add_admin_body_classes( $classes ) {
436 return $classes . ' frm-admin-full-screen';
437 }
438
439 /**
440 * Checks if the Onboarding Wizard was skipped during the plugin's installation.
441 *
442 * @since 6.9
443 *
444 * @return bool True if the Onboarding Wizard was skipped, false otherwise.
445 */
446 public static function has_onboarding_been_skipped() {
447 return get_option( self::ONBOARDING_SKIPPED_OPTION, false );
448 }
449
450 /**
451 * Marks the Onboarding Wizard as skipped to prevent automatic redirects to the wizard.
452 *
453 * @since 6.9
454 *
455 * @return void
456 */
457 public static function mark_onboarding_as_skipped() {
458 update_option( self::ONBOARDING_SKIPPED_OPTION, true, false );
459 }
460
461 /**
462 * Adds an Onboarding Wizard welcome message to the floating notifications.
463 *
464 * @since 6.9
465 *
466 * @param mixed $inbox_messages The inbox option data. An array of existing inbox messages if there is valid data set in the option.
467 *
468 * @return array Configuration for the onboarding wizard slide-in notification.
469 */
470 public static function add_wizard_to_floating_links( $inbox_messages ) {
471 $message = __( 'Welcome to Formidable Forms! Click here to run the Onboarding Wizard and it will guide you through the basic settings and get you started in 2 minutes.', 'formidable' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
472
473 if ( ! is_array( $inbox_messages ) ) {
474 $inbox_messages = array();
475 }
476
477 $inbox_messages['onboarding_wizard'] = array(
478 'subject' => esc_html__( 'Begin With Ease!', 'formidable' ),
479 'message' => esc_html( $message ),
480 'slidein' => esc_html( $message ),
481 'cta' => '<a href="' . esc_url( self::$page_url ) . '" class="button-primary frm-button-primary" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Begin Setup', 'formidable' ) . '</a>', // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
482 'created' => time(),
483 'key' => 'onboarding_wizard',
484 );
485
486 return $inbox_messages;
487 }
488
489 /**
490 * Check if the current page is the Onboarding Wizard page.
491 *
492 * @since 6.9
493 *
494 * @return bool True if the current page is the Onboarding Wizard page, false otherwise.
495 */
496 public static function is_onboarding_wizard_page() {
497 return FrmAppHelper::is_admin_page( self::PAGE_SLUG );
498 }
499
500 /**
501 * Checks if the plugin has already performed a redirect to avoid repeated redirections.
502 *
503 * @return bool Returns true if already redirected, otherwise false.
504 */
505 private static function has_already_redirected() {
506 if ( get_option( self::REDIRECT_STATUS_OPTION ) ) {
507 return true;
508 }
509
510 update_option( self::REDIRECT_STATUS_OPTION, FrmAppHelper::plugin_version(), false );
511 return false;
512 }
513
514 /**
515 * Get the path to the Onboarding Wizard views.
516 *
517 * @since 6.9
518 *
519 * @return string Path to views.
520 */
521 public static function get_page_url() {
522 return self::$page_url;
523 }
524
525 /**
526 * Set the URL to access the Onboarding Wizard's page.
527 *
528 * @return void
529 */
530 private static function set_page_url() {
531 self::$page_url = admin_url( 'admin.php?page=' . self::PAGE_SLUG );
532 }
533
534 /**
535 * Get the list of add-ons available for installation.
536 *
537 * @since 6.9
538 *
539 * @return array A list of add-ons.
540 */
541 public static function get_available_addons() {
542 return self::$available_addons;
543 }
544
545 /**
546 * Set the list of add-ons available for installation.
547 *
548 * @since 6.9
549 *
550 * @return void
551 */
552 private static function set_available_addons() {
553 $pro_is_installed = FrmAppHelper::pro_is_installed();
554 $plugins = get_plugins();
555
556 // Base add-ons always included.
557 self::$available_addons['spam-protection'] = array(
558 'title' => esc_html__( 'Spam Protection', 'formidable' ),
559 'is-checked' => true,
560 'is-disabled' => true,
561 'help-text' => esc_html__( 'Get anti-spam options like reCAPTCHA, hCaptcha, Akismet, Turnstile and the blocklist.', 'formidable' ),
562 );
563 self::$available_addons['stripe-payments'] = array(
564 'title' => esc_html__( 'Stripe Payments', 'formidable' ),
565 'is-checked' => true,
566 'is-disabled' => true,
567 'help-text' => esc_html__( 'Collect donations and payments with your forms. Offer physical products, digital goods, services, and more.', 'formidable' ),
568 );
569
570 // Add-ons included when Pro is not installed.
571 if ( ! $pro_is_installed ) {
572 self::$available_addons['visual-styler'] = array(
573 'title' => esc_html__( 'Visual Styler', 'formidable' ),
574 'is-checked' => true,
575 'is-disabled' => true,
576 'help-text' => esc_html__( 'Customize form appearance with an intuitive styling interface.', 'formidable' ),
577 );
578 self::$available_addons['save-entries'] = array(
579 'title' => esc_html__( 'Save Entries', 'formidable' ),
580 'is-checked' => true,
581 'is-disabled' => true,
582 'help-text' => esc_html__( 'Save form submissions to your database for future reference and analysis.', 'formidable' ),
583 );
584 }
585
586 // SMTP add-on if wp_mail_smtp is not installed.
587 if ( ! function_exists( 'wp_mail_smtp' ) ) {
588 $wp_mail_smtp_plugin = 'wp-mail-smtp/wp_mail_smtp.php';
589 $is_installed_wp_mail_smtp = array_key_exists( $wp_mail_smtp_plugin, $plugins );
590
591 self::$available_addons['wp-mail-smtp'] = array(
592 'title' => esc_html__( 'SMTP', 'formidable' ),
593 'rel' => $is_installed_wp_mail_smtp ? $wp_mail_smtp_plugin : 'wp-mail-smtp',
594 'is-checked' => false,
595 'is-vendor' => true,
596 'is-installed' => $is_installed_wp_mail_smtp,
597 'help-text' => esc_html__( 'Improve email deliverability by routing WordPress emails through SMTP.', 'formidable' ),
598 );
599 }
600
601 // Add-ons available when Pro is installed.
602 if ( $pro_is_installed ) {
603 $available_pro_addons = array(
604 'formidable-views' => array(
605 'addon_key' => 'views',
606 'title' => __( 'Views', 'formidable' ),
607 'plugin_file' => 'formidable-views/formidable-views.php',
608 ),
609 'formidable-mailchimp' => array(
610 'addon_key' => 'mailchimp',
611 'title' => __( 'Mailchimp', 'formidable' ),
612 'plugin_file' => 'formidable-mailchimp/formidable-mailchimp.php',
613 ),
614 'formidable-registration' => array(
615 'addon_key' => 'registration',
616 'title' => __( 'User Registration', 'formidable' ),
617 'plugin_file' => 'formidable-registration/formidable-registration.php',
618 ),
619 'formidable-api' => array(
620 'addon_key' => 'api',
621 'title' => __( 'Form Rest API', 'formidable' ),
622 'plugin_file' => 'formidable-api/formidable-api.php',
623 ),
624 'formidable-signature' => array(
625 'addon_key' => 'signature',
626 'title' => __( 'Signature Forms', 'formidable' ),
627 'plugin_file' => 'formidable-signature/signature.php',
628 ),
629 );
630
631 // Include ACF Forms add-on if ACF is installed.
632 if ( class_exists( 'ACF' ) ) {
633 $available_pro_addons['formidable-acf'] = array(
634 'addon_key' => 'acf',
635 'title' => __( 'ACF Forms', 'formidable' ),
636 'plugin_file' => 'formidable-acf/formidable-acf.php',
637 );
638 }
639
640 foreach ( $available_pro_addons as $key => $data ) {
641 $addon = FrmAddonsController::get_addon( $data['addon_key'] );
642 $plugin_file = $data['plugin_file'];
643
644 if ( is_plugin_active( $plugin_file ) || ! isset( $addon['url'] ) ) {
645 continue;
646 }
647
648 $is_installed = array_key_exists( $plugin_file, $plugins );
649
650 self::$available_addons[ $key ] = array(
651 'title' => $data['title'],
652 'rel' => $is_installed ? $plugin_file : $addon['url'],
653 'is-checked' => false,
654 'is-installed' => $is_installed,
655 'help-text' => $addon['excerpt'],
656 );
657 }
658 }//end if
659
660 // Gravity Forms Migrator add-on.
661 $gravity_forms_plugin = 'formidable-gravity-forms-importer/formidable-gravity-forms-importer.php';
662
663 if ( ! class_exists( 'GFForms' ) || is_plugin_active( $gravity_forms_plugin ) ) {
664 return;
665 }
666
667 $is_installed_gravity_forms = array_key_exists( $gravity_forms_plugin, $plugins );
668
669 self::$available_addons['formidable-gravity-forms-importer'] = array(
670 'title' => esc_html__( 'Gravity Forms Migrator', 'formidable' ),
671 'rel' => $is_installed_gravity_forms ? $gravity_forms_plugin : 'formidable-gravity-forms-importer',
672 'is-checked' => false,
673 'is-vendor' => true,
674 'is-installed' => $is_installed_gravity_forms,
675 'help-text' => esc_html__( 'Easily migrate your forms from Gravity Forms to Formidable.', 'formidable' ),
676 );
677 }
678
679 /**
680 * Get the path to the Onboarding Wizard views.
681 *
682 * @since 6.9
683 *
684 * @return string Path to views.
685 */
686 public static function get_view_path() {
687 return self::$view_path;
688 }
689
690 /**
691 * Get the upgrade link.
692 *
693 * @since 6.9
694 *
695 * @return string URL for upgrading accounts.
696 */
697 public static function get_upgrade_link() {
698 return self::$upgrade_link;
699 }
700
701 /**
702 * Retrieves the current Onboarding Wizard usage data, returning an empty array if none exists.
703 *
704 * @since 6.9
705 *
706 * @return array Current usage data.
707 */
708 public static function get_usage_data() {
709 return get_option( self::USAGE_DATA_OPTION, array() );
710 }
711
712 /**
713 * Validates if the Onboarding Wizard page is being displayed.
714 *
715 * @since 6.9
716 * @deprecated 6.16
717 *
718 * @return bool True if the Onboarding Wizard page is displayed, false otherwise.
719 */
720 public static function is_onboarding_wizard_displayed() {
721 _deprecated_function( __METHOD__, '6.16' );
722 return get_transient( self::TRANSIENT_NAME ) === self::TRANSIENT_VALUE;
723 }
724 }
725