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

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