PluginProbe
Elementor Website Builder – more than just a page builder / 3.33.0-dev1
Elementor Website Builder – more than just a page builder v3.33.0-dev1
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / app / modules / onboarding / module.php

module.php in Elementor Website Builder – more than just a page builder 3.33.0-dev1, at app/modules/onboarding/module.php

551 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\App\Modules\Onboarding;
3
4 use Automatic_Upgrader_Skin;
5 use Elementor\Core\Base\Module as BaseModule;
6 use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
7 use Elementor\Core\Common\Modules\Connect\Apps\Library;
8 use Elementor\Core\Files\Uploads_Manager;
9 use Elementor\Includes\EditorAssetsAPI;
10 use Elementor\Plugin;
11 use Elementor\Utils;
12 use Plugin_Upgrader;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit; // Exit if accessed directly.
16 }
17
18 /**
19 * Onboarding Module
20 *
21 * Responsible for initializing Elementor App functionality
22 *
23 * @since 3.6.0
24 */
25 class Module extends BaseModule {
26
27 const VERSION = '1.0.0';
28 const ONBOARDING_OPTION = 'elementor_onboarded';
29
30 private ?API $editor_assets_api = null;
31
32 /**
33 * Get name.
34 *
35 * @since 3.6.0
36 * @access public
37 *
38 * @return string
39 */
40 public function get_name() {
41 return 'onboarding';
42 }
43
44 private function is_theme_selection_experiment_enabled() {
45 $editor_assets_api = $this->get_editor_assets_api();
46
47 if ( null === $editor_assets_api ) {
48 return false;
49 }
50
51 return $editor_assets_api->is_theme_selection_experiment_enabled();
52 }
53
54 private function is_good_to_go_experiment_enabled() {
55 $editor_assets_api = $this->get_editor_assets_api();
56
57 if ( null === $editor_assets_api ) {
58 return false;
59 }
60
61 return $editor_assets_api->is_good_to_go_experiment_enabled();
62 }
63
64 private function get_editor_assets_api(): ?API {
65 if ( null !== $this->editor_assets_api ) {
66 return $this->editor_assets_api;
67 }
68
69 $editor_assets_api_instance = new EditorAssetsAPI( $this->get_editor_assets_api_config() );
70 $this->editor_assets_api = new API( $editor_assets_api_instance );
71
72 return $this->editor_assets_api;
73 }
74
75 private function get_editor_assets_api_config(): array {
76 return [
77 EditorAssetsAPI::ASSETS_DATA_URL => 'https://assets.elementor.com/ab-testing/v1/ab-testing.json',
78 EditorAssetsAPI::ASSETS_DATA_TRANSIENT_KEY => '_elementor_ab_testing_data',
79 EditorAssetsAPI::ASSETS_DATA_KEY => 'ab-testing',
80 ];
81 }
82
83 /**
84 * Set Onboarding Settings
85 *
86 * Creates an array of module settings that is localized into the JS App config.
87 *
88 * @since 3.6.0
89 */
90 private function set_onboarding_settings() {
91 if ( ! Plugin::$instance->common ) {
92 return;
93 }
94
95 // Get the published pages and posts
96 $pages_and_posts = new \WP_Query( [
97 'post_type' => [ 'page', 'post' ],
98 'post_status' => 'publish',
99 'update_post_meta_cache' => false,
100 'update_post_term_cache' => false,
101 'no_found_rows' => true,
102 ] );
103
104 $custom_site_logo_id = get_theme_mod( 'custom_logo' );
105 $custom_logo_src = wp_get_attachment_image_src( $custom_site_logo_id, 'full' );
106 $site_name = get_option( 'blogname', '' );
107
108 $hello_theme = wp_get_theme( 'hello-elementor' );
109 $hello_theme_errors = is_object( $hello_theme->errors() ) ? $hello_theme->errors()->errors : [];
110
111 /** @var Library $library */
112 $library = Plugin::$instance->common->get_component( 'connect' )->get_app( 'library' );
113
114 Plugin::$instance->app->set_settings( 'onboarding', [
115 'eventPlacement' => 'Onboarding wizard',
116 'onboardingAlreadyRan' => get_option( self::ONBOARDING_OPTION ),
117 'onboardingVersion' => self::VERSION,
118 'isLibraryConnected' => $library->is_connected(),
119 // Used to check if the Hello Elementor theme is installed but not activated.
120 'helloInstalled' => empty( $hello_theme_errors['theme_not_found'] ),
121 'helloActivated' => 'hello-elementor' === get_option( 'template' ),
122 // The "Use Hello theme on my site" checkbox should be checked by default only if this condition is met.
123 'helloOptOut' => count( $pages_and_posts->posts ) < 5,
124 'siteName' => esc_html( $site_name ),
125 'isUnfilteredFilesEnabled' => Uploads_Manager::are_unfiltered_uploads_enabled(),
126 'urls' => [
127 'kitLibrary' => Plugin::$instance->app->get_base_url() . '&source=onboarding#/kit-library?order[direction]=desc&order[by]=featuredIndex',
128 'sitePlanner' => add_query_arg( [
129 'type' => 'editor',
130 'siteUrl' => esc_url( home_url() ),
131 'siteName' => esc_html( $site_name ),
132 'siteDescription' => esc_html( get_bloginfo( 'description' ) ),
133 'siteLanguage' => get_locale(),
134 ], 'https://planner.elementor.com/onboarding.html' ),
135 'createNewPage' => Plugin::$instance->documents->get_create_new_post_url(),
136 'connect' => $library->get_admin_url( 'authorize', [
137 'utm_source' => 'onboarding-wizard',
138 'utm_campaign' => 'connect-account',
139 'utm_medium' => 'wp-dash',
140 'utm_term' => self::VERSION,
141 'source' => 'generic',
142 ] ),
143 'upgrade' => 'https://go.elementor.com/go-pro-onboarding-wizard-upgrade/',
144 'signUp' => $library->get_admin_url( 'authorize', [
145 'utm_source' => 'onboarding-wizard',
146 'utm_campaign' => 'connect-account',
147 'utm_medium' => 'wp-dash',
148 'utm_term' => self::VERSION,
149 'source' => 'generic',
150 'screen_hint' => 'signup',
151 ] ),
152 'uploadPro' => Plugin::$instance->app->get_base_url() . '#/onboarding/uploadAndInstallPro?mode=popup',
153 ],
154 'siteLogo' => [
155 'id' => $custom_site_logo_id,
156 'url' => $custom_logo_src ? $custom_logo_src[0] : '',
157 ],
158 'utms' => [
159 'connectTopBar' => '&utm_content=top-bar',
160 'connectCta' => '&utm_content=cta-button',
161 'connectCtaLink' => '&utm_content=cta-link',
162 'downloadPro' => '?utm_source=onboarding-wizard&utm_campaign=my-account-subscriptions&utm_medium=wp-dash&utm_content=import-pro-plugin&utm_term=' . self::VERSION,
163 ],
164 'nonce' => wp_create_nonce( 'onboarding' ),
165 'experiment' => true,
166 'themeSelectionExperimentEnabled' => $this->is_theme_selection_experiment_enabled(),
167 'goodToGoExperimentEnabled' => $this->is_good_to_go_experiment_enabled(),
168 ] );
169 }
170
171 /**
172 * Get Permission Error Response
173 *
174 * Returns the response that is returned when the user's capabilities are not sufficient for performing an action.
175 *
176 * @since 3.6.4
177 *
178 * @return array
179 */
180 private function get_permission_error_response() {
181 return [
182 'status' => 'error',
183 'payload' => [
184 'error_message' => esc_html__( 'You do not have permission to perform this action.', 'elementor' ),
185 ],
186 ];
187 }
188
189 /**
190 * Maybe Update Site Logo
191 *
192 * If a new name is provided, it will be updated as the Site Name.
193 *
194 * @since 3.6.0
195 *
196 * @return array
197 */
198 private function maybe_update_site_name() {
199 $problem_error = [
200 'status' => 'error',
201 'payload' => [
202 'error_message' => esc_html__( 'There was a problem setting your site name.', 'elementor' ),
203 ],
204 ];
205
206 // phpcs:ignore WordPress.Security.NonceVerification.Missing
207 if ( empty( $_POST['data'] ) ) {
208 return $problem_error;
209 }
210
211 // phpcs:ignore WordPress.Security.NonceVerification.Missing
212 $data = json_decode( Utils::get_super_global_value( $_POST, 'data' ), true );
213
214 if ( ! isset( $data['siteName'] ) ) {
215 return $problem_error;
216 }
217
218 /**
219 * Onboarding Site Name
220 *
221 * Filters the new site name passed by the user to update in Elementor's onboarding process.
222 * Elementor runs `esc_html()` on the Site Name passed by the user for security reasons. If a user wants to
223 * include special characters in their site name, they can use this filter to override it.
224 *
225 * @since 3.6.0
226 *
227 * @param string Escaped new site name
228 */
229 $new_site_name = apply_filters( 'elementor/onboarding/site-name', $data['siteName'] );
230
231 // The site name is sanitized in `update_options()`
232 update_option( 'blogname', $new_site_name );
233
234 return [
235 'status' => 'success',
236 'payload' => [
237 'siteNameUpdated' => true,
238 ],
239 ];
240 }
241
242 /**
243 * Maybe Update Site Logo
244 *
245 * If an image attachment ID is provided, it will be updated as the Site Logo Theme Mod.
246 *
247 * @since 3.6.0
248 *
249 * @return array
250 */
251 private function maybe_update_site_logo() {
252 if ( ! current_user_can( 'edit_theme_options' ) ) {
253 return $this->get_permission_error_response();
254 }
255
256 $data_error = [
257 'status' => 'error',
258 'payload' => [
259 'error_message' => esc_html__( 'There was a problem setting your site logo.', 'elementor' ),
260 ],
261 ];
262
263 // phpcs:ignore WordPress.Security.NonceVerification.Missing
264 if ( empty( $_POST['data'] ) ) {
265 return $data_error;
266 }
267
268 // phpcs:ignore WordPress.Security.NonceVerification.Missing
269 $data = json_decode( Utils::get_super_global_value( $_POST, 'data' ), true );
270
271 // If there is no attachment ID passed or it is not a valid ID, exit here.
272 if ( empty( $data['attachmentId'] ) ) {
273 return $data_error;
274 }
275
276 $absint_attachment_id = absint( $data['attachmentId'] );
277
278 if ( 0 === $absint_attachment_id ) {
279 return $data_error;
280 }
281
282 $attachment_url = wp_get_attachment_url( $data['attachmentId'] );
283
284 // Check if the attachment exists. If it does not, exit here.
285 if ( ! $attachment_url ) {
286 return $data_error;
287 }
288
289 set_theme_mod( 'custom_logo', $absint_attachment_id );
290
291 return [
292 'status' => 'success',
293 'payload' => [
294 'siteLogoUpdated' => true,
295 ],
296 ];
297 }
298
299 /**
300 * Maybe Upload Logo Image
301 *
302 * If an image file upload is provided, and it passes validation, it will be uploaded to the site's Media Library.
303 *
304 * @since 3.6.0
305 *
306 * @return array
307 */
308 private function maybe_upload_logo_image() {
309 $error_message = esc_html__( 'There was a problem uploading your file.', 'elementor' );
310
311 $file = Utils::get_super_global_value( $_FILES, 'fileToUpload' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
312
313 // phpcs:ignore WordPress.Security.NonceVerification.Missing
314 if ( ! is_array( $file ) || empty( $file['type'] ) ) {
315 return [
316 'status' => 'error',
317 'payload' => [
318 'error_message' => $error_message,
319 ],
320 ];
321 }
322
323 // If the user has allowed it, set the Request's state as an "Elementor Upload" request, in order to add
324 // support for non-standard file uploads.
325 if ( 'image/svg+xml' === $file['type'] ) {
326 if ( Uploads_Manager::are_unfiltered_uploads_enabled() ) {
327 Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
328 } else {
329 wp_send_json_error( 'To upload SVG files, you must allow uploading unfiltered files.' );
330 }
331 }
332
333 // If the image is an SVG file, sanitation is performed during the import (upload) process.
334 $image_attachment = Plugin::$instance->templates_manager->get_import_images_instance()->import( $file );
335
336 if ( 'image/svg+xml' === $file['type'] && Uploads_Manager::are_unfiltered_uploads_enabled() ) {
337 // Reset Upload state.
338 Plugin::$instance->uploads_manager->set_elementor_upload_state( false );
339 }
340
341 if ( $image_attachment && ! is_wp_error( $image_attachment ) ) {
342 $result = [
343 'status' => 'success',
344 'payload' => [
345 'imageAttachment' => $image_attachment,
346 ],
347 ];
348 } else {
349 $result = [
350 'status' => 'error',
351 'payload' => [
352 'error_message' => $error_message,
353 ],
354 ];
355 }
356
357 return $result;
358 }
359
360 /**
361 * Activate Hello Theme
362 *
363 * @since 3.6.0
364 *
365 * @return array
366 */
367 private function maybe_activate_hello_theme() {
368 if ( ! current_user_can( 'switch_themes' ) ) {
369 return $this->get_permission_error_response();
370 }
371
372 $theme_slug = Utils::get_super_global_value( $_POST, 'theme_slug' ) ?? 'hello-biz'; // phpcs:ignore WordPress.Security.NonceVerification.Missing
373 $allowed_themes = [ 'hello-elementor', 'hello-biz' ];
374 if ( ! in_array( $theme_slug, $allowed_themes, true ) ) {
375 $theme_slug = 'hello-biz';
376 }
377
378 switch_theme( $theme_slug );
379
380 return [
381 'status' => 'success',
382 'payload' => [
383 'helloThemeActivated' => true,
384 ],
385 ];
386 }
387
388 /**
389 * Upload and Install Elementor Pro
390 *
391 * @since 3.6.0
392 *
393 * @return array
394 */
395 private function upload_and_install_pro() {
396 if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
397 return $this->get_permission_error_response();
398 }
399
400 $error_message = esc_html__( 'There was a problem uploading your file.', 'elementor' );
401
402 $file = Utils::get_super_global_value( $_FILES, 'fileToUpload' ) ?? []; // phpcs:ignore WordPress.Security.NonceVerification.Missing
403
404 // phpcs:ignore WordPress.Security.NonceVerification.Missing
405 if ( ! is_array( $file ) || empty( $file['type'] ) ) {
406 return [
407 'status' => 'error',
408 'payload' => [
409 'error_message' => $error_message,
410 ],
411 ];
412 }
413
414 $result = [];
415
416 if ( ! class_exists( 'Automatic_Upgrader_Skin' ) ) {
417 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
418 }
419
420 $skin = new Automatic_Upgrader_Skin();
421 $upgrader = new Plugin_Upgrader( $skin );
422 $upload_result = $upgrader->install( $file['tmp_name'], [ 'overwrite_package' => false ] );
423
424 if ( ! $upload_result || is_wp_error( $upload_result ) ) {
425 $result = [
426 'status' => 'error',
427 'payload' => [
428 'error_message' => $error_message,
429 ],
430 ];
431 } else {
432 $activated = activate_plugin( WP_PLUGIN_DIR . '/elementor-pro/elementor-pro.php', false, false, true );
433
434 if ( ! is_wp_error( $activated ) ) {
435 $result = [
436 'status' => 'success',
437 'payload' => [
438 'elementorProInstalled' => true,
439 ],
440 ];
441 } else {
442 $result = [
443 'status' => 'error',
444 'payload' => [
445 'error_message' => $error_message,
446 'elementorProInstalled' => false,
447 ],
448 ];
449 }
450 }
451
452 return $result;
453 }
454
455 private function maybe_update_onboarding_db_option() {
456 $db_option = get_option( self::ONBOARDING_OPTION );
457
458 if ( ! $db_option ) {
459 update_option( self::ONBOARDING_OPTION, true );
460 }
461
462 return [
463 'status' => 'success',
464 'payload' => 'onboarding DB',
465 ];
466 }
467
468 /**
469 * Maybe Handle Ajax
470 *
471 * This method checks if there are any AJAX actions being
472 *
473 * @since 3.6.0
474 */
475 private function maybe_handle_ajax() {
476 $result = [];
477
478 // phpcs:ignore WordPress.Security.NonceVerification.Missing
479 switch ( Utils::get_super_global_value( $_POST, 'action' ) ) {
480 case 'elementor_update_site_name':
481 // If no value is passed for any reason, no need to update the site name.
482 $result = $this->maybe_update_site_name();
483 break;
484 case 'elementor_update_site_logo':
485 $result = $this->maybe_update_site_logo();
486 break;
487 case 'elementor_upload_site_logo':
488 $result = $this->maybe_upload_logo_image();
489 break;
490 case 'elementor_activate_hello_theme':
491 $result = $this->maybe_activate_hello_theme();
492 break;
493 case 'elementor_upload_and_install_pro':
494 $result = $this->upload_and_install_pro();
495 break;
496 case 'elementor_update_onboarding_option':
497 $result = $this->maybe_update_onboarding_db_option();
498 break;
499 case 'elementor_save_onboarding_features':
500 // phpcs:ignore WordPress.Security.NonceVerification.Missing
501 $result = $this->get_component( 'features_usage' )->save_onboarding_features( Utils::get_super_global_value( $_POST, 'data' ) ?? [] );
502 }
503
504 if ( ! empty( $result ) ) {
505 if ( 'success' === $result['status'] ) {
506 wp_send_json_success( $result['payload'] );
507 } else {
508 wp_send_json_error( $result['payload'] );
509 }
510 }
511 }
512
513 public function __construct() {
514 $this->add_component( 'features_usage', new Features_Usage() );
515
516 add_action( 'elementor/init', function() {
517 // Only load when viewing the onboarding app.
518 if ( Plugin::$instance->app->is_current() ) {
519 $this->set_onboarding_settings();
520 // Needed for installing the Hello Elementor theme.
521 wp_enqueue_script( 'updates' );
522 // Needed for uploading Logo from WP Media Library.
523 wp_enqueue_media();
524 }
525 }, 12 );
526
527 // Needed for uploading Logo from WP Media Library. The 'admin_menu' hook is used because it runs before
528 // 'admin_init', and the App triggers printing footer scripts on 'admin_init' at priority 0.
529 add_action( 'admin_menu', function () {
530 add_action( 'wp_print_footer_scripts', function () {
531 if ( function_exists( 'wp_print_media_templates' ) ) {
532 wp_print_media_templates();
533 }
534 } );
535 } );
536
537 add_action( 'admin_init', function() {
538 if ( wp_doing_ajax() &&
539 isset( $_POST['action'] ) &&
540 isset( $_POST['_nonce'] ) &&
541 wp_verify_nonce( Utils::get_super_global_value( $_POST, '_nonce' ), Ajax::NONCE_KEY ) &&
542 current_user_can( 'manage_options' )
543 ) {
544 $this->maybe_handle_ajax();
545 }
546 } );
547
548 $this->get_component( 'features_usage' )->register();
549 }
550 }
551