PluginProbe
Elementor Website Builder – more than just a page builder / 3.31.4
Elementor Website Builder – more than just a page builder v3.31.4
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.31.4, at app/modules/onboarding/module.php

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