PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.6.1
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.6.1
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / team-updraft / lib-onboarding-wizard / Wizard / Onboarding / Onboarding.php

Onboarding.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.6.1, at vendor/team-updraft/lib-onboarding-wizard/Wizard/Onboarding/Onboarding.php

690 lines 24.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Updraftplus\Wp_Optimize\Wizard\Onboarding;
3
4 defined( 'ABSPATH' ) || die();
5
6 use Updraftplus\Wp_Optimize\Wizard\Installer\Installer;
7 use Updraftplus\Wp_Optimize\Wizard\RestResponse\RestResponse;
8
9 /**
10 * The onboarding class enqueues the react app and handles the REST API requests.
11 * A trait is used to add plugin specific functionality.
12 * The class itself is as much as possible independent of the plugin, so it can be used in other plugins with only little changes.
13 *
14 * There are three scenarios where the onboarding is active:
15 * - Free plugin: no license activation, and on the finish page, an upsell to premium is shown.
16 * - Pro plugin, first time onboarding: license activation is required, and on the finish page, some confirmation of the activation of additional features is shown.
17 * - Pro plugin, onboarding already completed in free: only license activation, possibly pro feature configuration, and no plugins installation, no email signup.
18 */
19 class Onboarding {
20
21 private $steps;
22 private $onboarding_path;
23 private $onboarding_url;
24 private $is_all_plugins_installed = false;
25 public $version;
26 public $prefix;
27 public $plugin_name = '';
28 public $privacy_statement_url;
29 public $privacy_url_label = '';
30 public $forgot_password_url = 'https://teamupdraft.com/my-account/lost-password/';
31 public $caller_slug;
32 public $capability;
33 public $support_url;
34 public $faqs_url = '';
35 public $documentation_url = '';
36 public $upgrade_url;
37 public $mailing_list;
38 public $mailing_list_endpoint;
39 public $page_prefix;
40 public $languages_dir;
41 public $text_domain;
42 public $logo_path;
43 public $is_pro = false;
44 public $reload_settings_page_on_finish = false;
45 public $udmupdater_nonce = 'udmupdater-ajax-nonce';
46 public $udmupdater_muid = 2;
47 public $udmupdater_slug = '';
48 public $udmupdater_mothership = 'https://teamupdraft.com/plugin-info/';
49 public $exit_wizard_text;
50
51 /**
52 * Initialize hooks and filters
53 */
54 public function init(): void {
55 if ( ! self::is_compatible() ) {
56 return;
57 }
58
59 $this->onboarding_path = __DIR__;
60 $this->onboarding_url = plugin_dir_url( __FILE__ );
61
62 if (empty($this->privacy_url_label)) {
63 $this->privacy_url_label = __( 'Privacy Statement', 'wp-optimize' );
64 }
65
66 $this->maybe_flush_rewrite_rules();
67
68 add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
69 add_action( 'wp_ajax_' . $this->prefix . '_onboarding_rest_api_fallback', [ $this, 'rest_api_fallback' ] );
70 add_action( 'admin_enqueue_scripts', [ $this, 'maybe_enqueue_onboarding' ] );
71 add_action( 'admin_footer', [ $this, 'add_root_html' ] );
72
73 }
74
75 /**
76 * Flush rewrite rules on first onboarding initialization
77 * As we're registering the rest route conditionally, we need to flush the rewrite rules once.
78 */
79 private function maybe_flush_rewrite_rules(): void {
80 $flushed_option = $this->prefix . '_onboarding_flushed_rewrite_rules';
81
82 if ( ! get_site_option( $flushed_option ) ) {
83 flush_rewrite_rules();
84 update_site_option( $flushed_option, time() );
85 }
86 }
87
88 /**
89 * Maybe load the plugin wizard, to load on every page of the caller plugin
90 *
91 * @return void
92 */
93 public function maybe_enqueue_onboarding(): void {
94 $screen = \get_current_screen();
95 if ( stripos( $screen->id, $this->page_prefix ) !== false) {
96 $this->enqueue_onboarding_scripts();
97 }
98 }
99
100 /**
101 * Add values and defaults to fields in steps
102 *
103 * @param array $steps array of onboarding steps.
104 * @return array<int, array{
105 * id: string,
106 * title: string,
107 * subtitle?: string,
108 * button?: array{id: string, label: string, icon?: string},
109 * fields?: array<int, array<string, mixed>>
110 * }>
111 */
112 private function add_fields_data_to_steps( array $steps ): array {
113 foreach ( $steps as $step_index => $step ) {
114 if ( isset( $step['fields'] ) && is_array( $step['fields'] ) ) {
115 foreach ( $step['fields'] as $field_index => $field ) {
116 // update values and defaults based on plugin specific functions.
117 // using prefixed hook.
118 // phpcs:ignore
119 $field = apply_filters( $this->prefix . '_onboarding_field', $field, $step['id'] );
120 if ( $field['type'] === 'email' ) {
121 $current_user = wp_get_current_user();
122 $current_user_email = $current_user->user_email;
123 $field['default'] = $current_user_email;
124 $field['value'] = $current_user_email;
125 }
126 if ( $field['id'] === 'plugins' ) {
127 $field['options'] = $this->get_recommended_plugins();
128 $field['value'] = $this->get_recommended_plugins( true );
129 if ($this->is_all_plugins_installed){
130 $field['label'] = '';
131 if (isset($step['title_conditional']) && !empty($step['title_conditional'])) {
132 if (isset($step['title_conditional']['all_installed']) && !empty($step['title_conditional']['all_installed'])) {
133 $steps[$step_index]['title'] = $steps[$step_index]['title_conditional']['all_installed'];
134 }
135 if (isset($step['subtitle_conditional']['all_installed']) && !empty($step['subtitle_conditional']['all_installed'])) {
136 $steps[$step_index]['subtitle'] = $steps[$step_index]['subtitle_conditional']['all_installed'];
137 }
138 }
139 $steps[ $step_index ]['button']['label'] = __('Continue', 'wp-optimize');
140 }
141 }
142 $steps[ $step_index ]['fields'][ $field_index ] = $field;
143 }
144 }
145 }
146 return $steps;
147 }
148
149 /**
150 * Conditionally drop steps
151 *
152 * @param array $steps array of onboarding steps.
153 * @return array<int, array{
154 * id: string,
155 * type: string,
156 * icon?: string,
157 * title: string,
158 * subtitle?: string,
159 * button?: array{id: string, label: string, icon?: string},
160 * fields?: array<int, array<string, mixed>>,
161 * solutions?: array<int, string>,
162 * bullets?: array<int, string>,
163 * intro_bullets?: array{title: string, desc: string, icon?: string},
164 * documentation?: string,
165 * }>
166 */
167 private function conditionally_drop_steps( array $steps ): array {
168 $is_pro_with_onboarding_free_completed = $this->is_pro_with_onboarding_free_completed();
169 foreach ( $steps as $step_index => $step ) {
170 // if this is the pro plugin onboarding, and user has completed the onboarding in the free plugin, we can skip first_run_only steps.
171 $first_run_only = isset( $step['first_run_only'] ) && (bool) $step['first_run_only'];
172 if ( $is_pro_with_onboarding_free_completed && $first_run_only ) {
173 unset( $steps[ $step_index ] );
174 continue;
175 }
176
177 if ( $step['id'] === 'license' ) {
178 // using prefixed hook.
179 // phpcs:ignore
180 $license_is_valid = (bool) apply_filters( $this->prefix . '_license_is_valid', false );
181 if ( $license_is_valid || ! $this->is_pro ) {
182 unset( $steps[ $step_index ] );
183 continue;
184 }
185 }
186
187 if ( $is_pro_with_onboarding_free_completed ) {
188 if ( isset( $step['title_upgrade'] ) ) {
189 $steps[ $step_index ]['title'] = $step['title_upgrade'];
190 }
191 if ( isset( $step['subtitle_upgrade'] ) ) {
192 $steps[ $step_index ]['subtitle'] = $step['subtitle_upgrade'];
193 }
194 }
195 }
196 // reset keys.
197 return array_values( $steps );
198 }
199
200 /**
201 * Extract the used fields from the onboarding steps, so react can filter the applicable fields.
202 *
203 * @param array $steps array of onboarding steps.
204 * @return array<int, array{
205 * id: string,
206 * title: string,
207 * subtitle?: string,
208 * button?: array{id: string, label: string, icon?: string},
209 * fields?: array<int, array<string, mixed>>
210 * }>
211 */
212 private function extract_fields_from_steps( array $steps ): array {
213 $fields = [];
214 foreach ( $steps as $step ) {
215 if ( isset( $step['fields'] ) && is_array( $step['fields'] ) ) {
216 foreach ( $step['fields'] as $index => $field ) {
217 if ( isset( $field['id'] ) ) {
218 $fields[] = $field;
219 }
220 }
221 }
222 }
223 return $fields;
224 }
225
226 /**
227 * Get the fields from a specific step.
228 *
229 * @param string $step The step ID to extract fields from.
230 * @return array<int, array<string, mixed>> List of fields (each field is an assoc array).
231 */
232 private function extract_fields_from_step( string $step ): array {
233 $step = $this->get_step_by_id( $this->sanitize_step_id( $step ) );
234 if ( empty( $step ) ) {
235 return [];
236 }
237 return ! empty( $step['fields'] ) ? $step['fields'] : [];
238 }
239
240 /**
241 * Sanitize the step ID to ensure it exists in the steps array.
242 */
243 private function sanitize_step_id( string $step_id ): string {
244 $steps = $this->get_steps();
245 foreach ( $steps as $step ) {
246 if ( isset( $step['id'] ) && $step['id'] === $step_id ) {
247 return $step_id;
248 }
249 }
250 return '';
251 }
252
253 /**
254 * Check if the current environment is compatible with the onboarding app.
255 */
256 private static function is_compatible(): bool {
257 if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
258 return false;
259 }
260 // check the WordPress version.
261 global $wp_version;
262 if ( version_compare( $wp_version, '6.2', '<' ) ) {
263 return false;
264 }
265 return true;
266 }
267
268 /**
269 * Check if the onboarding is active
270 */
271 public static function is_onboarding_active( string $prefix, string $caller_slug ): bool {
272 if ( ! self::is_compatible() ) {
273 return false;
274 }
275
276 $skipped = (bool) get_site_option( $prefix . '_skipped_onboarding' );
277 $started = (bool) get_site_option( $prefix . '_start_onboarding' );
278 $completed = (bool) get_site_option( $prefix . '_completed_onboarding' );
279
280 $current_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
281
282 // Don't Reopen the wizard if the user skipped the wizard.
283 if ($skipped || $completed) {
284 delete_site_option( $prefix . '_skipped_onboarding' );
285 delete_site_option( $prefix . '_start_onboarding' );
286 delete_site_option( $prefix . '_completed_onboarding' );
287 delete_site_option( $prefix . '_onboarding_flushed_rewrite_rules' );
288 return false;
289 }
290
291 // If onboarding is started or in progress or being called via REST
292 return $started
293 || strpos( $current_uri, $prefix . '/v1/onboarding/do_action/' ) !== false
294 || strpos( $current_uri, $prefix . '_onboarding_rest_api_fallback' ) !== false;
295 }
296
297 /**
298 * Add root HTML element for the onboarding app
299 */
300 public function add_root_html(): void {
301 echo '<div id="teamupdraft-onboarding"></div>';
302 }
303 /**
304 * Register REST API routes
305 */
306 public function register_rest_routes(): void {
307 register_rest_route(
308 $this->prefix . '/v1/onboarding',
309 'do_action/(?P<action>[a-z\_\-]+)',
310 [
311 'methods' => 'POST',
312 'callback' => [ $this, 'handle_rest_request' ],
313 'permission_callback' => [ $this, 'has_permission' ],
314 ]
315 );
316 }
317
318 /**
319 * Check if user has required capability
320 */
321 public function has_permission(): bool {
322 return current_user_can( $this->capability );
323 }
324
325 /**
326 * Handle REST API requests
327 */
328 public function handle_rest_request( \WP_REST_Request $request ): \WP_REST_Response {
329 if ( ! $this->has_permission() ) {
330 return $this->response( false, [], 'You do not have permission to do this.', 403 );
331 }
332 $action = sanitize_text_field( $request->get_param( 'action' ) );
333 $data = $request->get_json_params();
334 if ( ! wp_verify_nonce( $data['nonce'], $this->prefix . '_nonce' ) ) {
335 return $this->response( false, [], 'Nonce verification failed', 403 );
336 }
337 return $this->handle_onboarding_action( $action, $data );
338 }
339
340 /**
341 * Handle AJAX fallback requests, when the REST API is not available
342 */
343 public function rest_api_fallback(): void {
344 if ( ! $this->has_permission() ) {
345 wp_send_json_error( 'Unauthorized', 403 );
346 }
347 $data = json_decode( file_get_contents( 'php://input' ), true );
348 $data = $data['data'] ?? [];
349 if ( ! wp_verify_nonce( $data['nonce'], $this->prefix . '_nonce' ) ) {
350 $response = new RestResponse();
351 $response->message = 'Nonce verification failed';
352 wp_send_json( $response );
353 exit;
354 }
355
356 /**
357 * Determine action — prefer JSON body, fallback to GET
358 * Sanitized and unslashed
359 */
360 if ( isset( $data['path'] ) ) {
361 $action = sanitize_title( wp_unslash( $data['path'] ) );
362 } else {
363 $action = isset($_GET['rest_action']) ? sanitize_text_field( wp_unslash( $_GET['rest_action'] ) ) : '';
364 }
365 preg_match( '/do_action[\/|\-]([a-z\_\-]+)$/', $action, $matches );
366 if ( isset( $matches[1] ) ) {
367 $action = $matches[1];
368 }
369
370 $response = $this->handle_onboarding_action( $action, $data );
371 wp_send_json( $response );
372 exit;
373 }
374
375 /**
376 * Standardized response format
377 */
378 protected function response( bool $success = false, array $data = [], string $message = '', int $code = 200 ): \WP_REST_Response {
379 if ( ob_get_length() ) {
380 ob_clean();
381 }
382
383 return new \WP_REST_Response(
384 [
385 'success' => $success,
386 'message' => $message,
387 'data' => $data,
388 // can be used to check if the response in react actually contains this array.
389 'request_success' => true,
390 ],
391 $code
392 );
393 }
394
395 /**
396 * Get step by id
397 *
398 * @return ?array{
399 * id: string,
400 * title: string,
401 * subtitle?: string,
402 * button?: array{id: string, label: string, icon?: string},
403 * fields?: array<int, array<string, mixed>>
404 * }
405 */
406 private function get_step_by_id( string $id ): ?array {
407 $steps = $this->get_steps();
408 foreach ( $steps as $step ) {
409 if ( isset( $step['id'] ) && $step['id'] === $id ) {
410 return $step;
411 }
412 }
413 return null;
414 }
415
416 /**
417 * Handle onboarding actions
418 *
419 * @param string $action The onboarding action to handle.
420 * @param array $data The data associated with the action.
421 */
422 private function handle_onboarding_action( string $action, array $data ): \WP_REST_Response {
423 $response = $this->response( false );
424 switch ( $action ) {
425 case 'user_skipped_wizard':
426 update_site_option($this->prefix . '_skipped_onboarding', true );
427 $message = __('User skipped the wizard', 'wp-optimize');
428 $response = $this->response( true, [], $message);
429 break;
430 case 'user_completed_wizard':
431 update_site_option($this->prefix . '_completed_onboarding', true );
432 $message = __('User Completed the wizard', 'wp-optimize');
433 $response = $this->response( true, [], $message);
434 break;
435 case 'activate_license':
436 // using prefixed hook.
437 // phpcs:ignore
438 $license_data = apply_filters( $this->prefix . '_license_activation', [], $data );
439 $response = $this->response( $license_data['success'], [], $license_data['message'] );
440 break;
441 case 'update_settings':
442 // Get current step fields, so we only update these fields.
443 $step_fields = isset( $data['step'] ) ? $this->extract_fields_from_step( $data['step'] ) : [];
444 if ( ! empty( $step_fields ) ) {
445 // sanitized in save functions.
446 // using prefixed hook.
447 // phpcs:ignore
448 do_action( $this->prefix . '_onboarding_update_options', $data['settings'], $step_fields );
449 }
450 $response = $this->response( true );
451 break;
452 case 'download':
453 if ( isset( $data['plugin'] ) ) {
454 $installer = new Installer( $this->caller_slug, $data['plugin'] );
455 // Avoid re-downloading if already downloaded/installed or activated.
456 if ( $installer->plugin_is_activated( $data['plugin'] ) ) {
457 // Already active: nothing to do.
458 $response = $this->response( true, [ 'next_action' => 'installed' ] );
459 } elseif ( $installer->plugin_is_downloaded( $data['plugin'] ) ) {
460 // Already downloaded: next step is activation.
461 $response = $this->response( true, [ 'next_action' => 'activate' ] );
462 } else {
463 $success = $installer->download_plugin();
464 $next_action = $success ? 'activate' : 'installed';
465 $response = $this->response( (bool) $success, [ 'next_action' => $next_action ] );
466 }
467 }
468 break;
469 case 'activate':
470 if ( isset( $data['plugin'] ) ) {
471 $installer = new Installer( $this->caller_slug, $data['plugin'] );
472 // Avoid re-activating if already active.
473 if ( $installer->plugin_is_activated( $data['plugin'] ) ) {
474 $response = $this->response( true, [ 'next_action' => 'installed' ] );
475 } else {
476 $success = $installer->activate_plugin();
477 $response = $this->response( (bool) $success, [ 'next_action' => 'installed' ] );
478 }
479 }
480 break;
481
482 case 'update_email':
483 $step_fields = isset( $data['step'] ) ? $this->extract_fields_from_step( $data['step'] ) : [];
484 if ( isset( $data['email'] ) && is_email( $data['email'] ) ) {
485 $email = sanitize_email( $data['email'] );
486 if ( ! empty( $email ) ) {
487 $reporting_email_field_name = '';
488 $mailinglist_email_field_name = '';
489 foreach ( $step_fields as $field ) {
490 if ( isset( $field['type'] ) && $field['type'] === 'email' ) {
491 $reporting_email_field_name = $field['id'] ?? '';
492 }
493 if ( isset( $field['type'] ) && $field['type'] === 'checkbox' ) {
494 $mailinglist_email_field_name = $field['id'] ?? '';
495 }
496 }
497
498 if ( ! empty( $reporting_email_field_name ) ) {
499 // using prefixed hook.
500 // phpcs:ignore
501 do_action( $this->prefix . '_onboarding_update_single_option', $reporting_email_field_name, $email );
502 }
503 if ( ! empty( $mailinglist_email_field_name ) ) {
504 $include_tips = isset( $data['tips_tricks'] ) && (bool) $data['tips_tricks'];
505 // using prefixed hook.
506 // phpcs:ignore
507
508 do_action( $this->prefix . '_onboarding_update_single_option', 'tips_tricks_mailinglist', $email );
509
510 if ( $include_tips ) {
511 $this->signup_for_mailinglist( $email );
512 }
513 }
514 }
515 $response = $this->response( true );
516 }
517 break;
518 default:
519 $response = $this->response( false, [], 'Unknown action', 400 );
520 }
521
522 return $response;
523 }
524
525 /**
526 * Signup for a mailing list
527 */
528 private function signup_for_mailinglist( string $email ) {
529 $endpoint = $this->mailing_list_endpoint;
530
531 if (!empty($endpoint)) {
532 $api_params = [
533 'email' => sanitize_email($email),
534 'tags' => $this->mailing_list,
535 ];
536 wp_remote_post(
537 $endpoint,
538 [
539 'timeout' => 15,
540 'sslverify' => true,
541 'body' => $api_params
542 ]
543 );
544 }
545 }
546
547 /**
548 * Get onboarding steps
549 *
550 * @return array<int, array{
551 * id: string,
552 * type: string,
553 * icon?: string,
554 * title: string,
555 * subtitle?: string,
556 * button?: array{id: string, label: string, icon?: string},
557 * fields?: array<int, array<string, mixed>>,
558 * solutions?: array<int, string>,
559 * bullets?: array<int, string>,
560 * intro_bullets?: array{title: string, desc: string, icon?: string},
561 * documentation?: string,
562 * }> The onboarding steps array.
563 */
564 public function get_steps(): array {
565 if ( ! empty( $this->steps ) ) {
566 return $this->steps;
567 }
568
569 // phpcs:ignore
570 $steps = apply_filters( $this->prefix . '_onboarding_steps', [] );
571 // Hook name based on prefix.
572 // phpcs:ignore
573 $steps = apply_filters( $this->prefix . '_onboarding_steps', $steps );
574 $steps = $this->add_fields_data_to_steps( $steps );
575 $steps = $this->conditionally_drop_steps( $steps );
576 $this->steps = $steps;
577 return $this->steps;
578 }
579
580 /**
581 * Get recommended plugins for onboarding
582 *
583 * @return array<int, array{
584 * slug: string,
585 * file: string,
586 * constant_free: string,
587 * premium: array{
588 * type: string,
589 * value: string
590 * },
591 * wordpress_url: string,
592 * upgrade_url: string,
593 * title: string
594 * }>
595 */
596 private function get_recommended_plugins( bool $keys = false ): array {
597 $installer = new Installer( $this->caller_slug );
598 $plugins = $installer->get_plugins( false, 3 );
599 $this->is_all_plugins_installed = $installer->all_installed;
600 if ( $keys ) {
601 // just return the slugs as a value, value , value array.
602 return array_column( $plugins, 'slug' );
603 }
604 return $plugins;
605 }
606
607 /**
608 * Check if the user has completed the onboarding in the free version.
609 * At least an hour ago, so we don't drop steps for the curren premium installing user.
610 */
611 private function is_pro_with_onboarding_free_completed(): bool {
612 // if the pro plugin is active, and the free plugin has completed onboarding, we can skip some parts of the onboarding.
613 $free_completed_time = get_site_option( "{$this->prefix}_onboarding_free_completed" );
614 $now = time();
615 $free_completed_over_1_hour_ago = $free_completed_time && ( $now - $free_completed_time > HOUR_IN_SECONDS );
616 return $this->is_pro && $free_completed_over_1_hour_ago;
617 }
618
619 /**
620 * Enqueue onboarding scripts and styles
621 */
622 public function enqueue_onboarding_scripts(): void {
623 $steps = $this->get_steps();
624 $asset_file = include $this->onboarding_path . '/build/index.asset.php';
625
626 wp_set_script_translations( 'teamupdraft_onboarding', 'wp-optimize', $this->languages_dir );
627
628 wp_enqueue_script(
629 'teamupdraft_onboarding',
630 $this->onboarding_url . 'build/index.js',
631 $asset_file['dependencies'],
632 $asset_file['version'],
633 true
634 );
635
636 wp_enqueue_style(
637 $this->prefix . '_onboarding',
638 $this->onboarding_url . "build/Onboarding.css",
639 [],
640 $asset_file['version']
641 );
642
643 wp_enqueue_style(
644 'google-font-inter',
645 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
646 [],
647 null
648 );
649
650 wp_localize_script(
651 'teamupdraft_onboarding',
652 'teamupdraft_onboarding',
653 [
654 'logo' => esc_url($this->logo_path),
655 'prefix' => $this->prefix,
656 'plugin_name' => $this->plugin_name,
657 'version' => $this->version,
658 'steps' => $steps,
659 'nonce' => wp_create_nonce( $this->prefix . '_nonce' ),
660 'fields' => $this->extract_fields_from_steps( $this->get_steps() ),
661 'rest_url' => get_rest_url(),
662 'site_url' => get_site_url(),
663 'support' => esc_url( $this->support_url ),
664 'faqs' => esc_url( $this->faqs_url ),
665 'documentation' => esc_url( $this->documentation_url ),
666 'upgrade' => esc_url( $this->upgrade_url ),
667 'privacy_statement_url' => esc_url( $this->privacy_statement_url ),
668 'privacy_url_label' => $this->privacy_url_label,
669 'forgot_password_url' => esc_url( $this->forgot_password_url ),
670 'admin_ajax_url' => add_query_arg( [ 'action' => $this->prefix . '_onboarding_rest_api_fallback' ], admin_url( 'admin-ajax.php' ) ),
671 'is_pro' => $this->is_pro,
672 'network_link' => network_site_url( 'plugins.php' ),
673 'reload_on_finish' => $this->reload_settings_page_on_finish,
674 'text_domain' => $this->text_domain,
675 'udmupdater_nonce' => wp_create_nonce($this->udmupdater_nonce),
676 'udmupdater_muid' => $this->udmupdater_muid,
677 'udmupdater_slug' => $this->udmupdater_slug,
678 'udmupdater_mothership' => esc_url( $this->udmupdater_mothership ),
679 'is_all_plugins_installed' => $this->is_all_plugins_installed,
680 'exit_wizard_text' => !empty($this->exit_wizard_text) ? $this->exit_wizard_text : __('Exit setup', 'wp-optimize'),
681
682 ]
683 );
684 // remember if user has completed the onboarding in the free plugin.
685 if ( $this->is_pro ) {
686 update_site_option( "{$this->prefix}_onboarding_free_completed", time() );
687 }
688 }
689 }
690