PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.3
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.5.3, at vendor/team-updraft/lib-onboarding-wizard/Wizard/Onboarding/Onboarding.php

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