PluginProbe
Elementor Website Builder – more than just a page builder / 4.2.4
Elementor Website Builder – more than just a page builder v4.2.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 / import-export-customization / module.php

module.php in Elementor Website Builder – more than just a page builder 4.2.4, at app/modules/import-export-customization/module.php

808 lines 26.3 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\ImportExportCustomization;
3
4 use Elementor\App\Modules\ImportExportCustomization\Processes\Export;
5 use Elementor\App\Modules\ImportExportCustomization\Processes\Import;
6 use Elementor\App\Modules\ImportExportCustomization\Processes\Revert;
7 use Elementor\Core\Base\Module as BaseModule;
8 use Elementor\Core\Files\Uploads_Manager;
9 use Elementor\Modules\CloudKitLibrary\Module as CloudKitLibrary;
10 use Elementor\Modules\GlobalClasses\Global_Classes_REST_API;
11 use Elementor\Modules\System_Info\Reporters\Server;
12 use Elementor\Modules\Variables\Storage\Constants;
13 use Elementor\Modules\Variables\Storage\Variables_Collection;
14 use Elementor\Plugin;
15 use Elementor\Tools;
16 use Elementor\Utils as ElementorUtils;
17 use Elementor\App\Modules\ImportExportCustomization\Utils as ImportExportUtils;
18 use Elementor\App\Modules\ImportExportCustomization\Data\Controller;
19 use Elementor\Core\Settings\Manager as SettingsManager;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit; // Exit if accessed directly.
23 }
24
25 /**
26 * Import Export Module
27 *
28 * Responsible for initializing Elementor App functionality
29 */
30 class Module extends BaseModule {
31 const FORMAT_VERSION = '3.0';
32
33 const REFERRER_KIT_LIBRARY = 'kit-library';
34
35 const REFERRER_LOCAL = 'local';
36
37 const REFERRER_CLOUD = 'cloud';
38
39 const PLUGIN_PERMISSIONS_ERROR_KEY = 'plugin-installation-permissions-error';
40
41 const KIT_LIBRARY_ERROR_KEY = 'invalid-kit-library-zip-error';
42
43 const CLOUD_KIT_LIBRARY_ERROR_LOADING_RESOURCE = 'error-loading-resource';
44
45 const NO_WRITE_PERMISSIONS_KEY = 'no-write-permissions';
46
47 const THIRD_PARTY_ERROR = 'third-party-error';
48
49 const DOMDOCUMENT_MISSING = 'domdocument-missing';
50
51 const MEDIA_PROCESSING_ERROR = 'media-processing-error';
52
53 const OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS = 'elementor_import_sessions';
54
55 const OPTION_KEY_ELEMENTOR_REVERT_SESSIONS = 'elementor_revert_sessions';
56
57 const META_KEY_ELEMENTOR_IMPORT_SESSION_ID = '_elementor_import_session_id';
58
59 const META_KEY_ELEMENTOR_EDIT_MODE = '_elementor_edit_mode';
60 const IMPORT_PLUGINS_ACTION = 'import-plugins';
61 const EXPORT_SOURCE_CLOUD = 'cloud';
62 const EXPORT_SOURCE_FILE = 'file';
63
64 /**
65 * Assigning the export process to a property, so we can use the process from outside the class.
66 *
67 * @var Export
68 */
69 public $export;
70
71 /**
72 * Assigning the import process to a property, so we can use the process from outside the class.
73 *
74 * @var Import
75 */
76 public $import;
77
78 /**
79 * Assigning the revert process to a property, so we can use the process from outside the class.
80 *
81 * @var Revert
82 */
83 public $revert;
84
85 /**
86 * Get name.
87 *
88 * @access public
89 *
90 * @return string
91 */
92 public function get_name() {
93 return 'import-export-customization';
94 }
95
96 public function __construct() {
97 $this->register_actions();
98
99 Controller::register_hooks();
100
101 if ( ElementorUtils::is_wp_cli() ) {
102 \WP_CLI::add_command( 'elementor kit', WP_CLI::class );
103 }
104
105 ( new Usage() )->register();
106
107 $this->revert = new Revert();
108 }
109
110 public function get_init_settings() {
111 if ( ! Plugin::$instance->app->is_current() ) {
112 return [];
113 }
114
115 return $this->get_config_data();
116 }
117
118 /**
119 * Register the import/export tab in elementor tools.
120 */
121 public function register_settings_tab( Tools $tools ) {
122 $tools->add_tab( 'import-export-kit', [
123 'label' => esc_html__( 'Website Templates', 'elementor' ),
124 'sections' => [
125 'intro' => [
126 'label' => esc_html__( 'Website Templates', 'elementor' ),
127 'callback' => function() {
128 $this->render_import_export_tab_content();
129 },
130 'fields' => [],
131 ],
132 ],
133 ] );
134 }
135
136 /**
137 * Render the import/export tab content.
138 */
139 private function render_import_export_tab_content() {
140 $is_cloud_kits_available = CloudKitLibrary::get_app()->check_eligibility()['is_eligible'];
141
142 $content_data = [
143 'export' => [
144 'title' => esc_html__( 'Export this website', 'elementor' ),
145 'button' => [
146 'url' => Plugin::$instance->app->get_base_url() . '#/export-customization',
147 'text' => esc_html__( 'Export', 'elementor' ),
148 'id' => 'elementor-import-export__export',
149 ],
150 'description' => esc_html__( 'You can download this website as a .zip file, or upload it to the library.', 'elementor' ),
151 ],
152 'import' => [
153 'title' => esc_html__( 'Apply a Website Template', 'elementor' ),
154 'button' => [
155 'url' => Plugin::$instance->app->get_base_url() . '#/import-customization',
156 'text' => $is_cloud_kits_available ? esc_html__( 'Upload .zip file', 'elementor' ) : esc_html__( 'Import', 'elementor' ),
157 'id' => 'elementor-import-export__import',
158 ],
159 'description' => esc_html__( 'You can import design and settings from a .zip file or choose from the library.', 'elementor' ),
160 ],
161 ];
162
163 if ( $is_cloud_kits_available ) {
164 $return_to_url = Tools::get_url() . '#tab-import-export-kit';
165 $kit_library_url = add_query_arg(
166 [ 'return_to' => rawurlencode( $return_to_url ) ],
167 Plugin::$instance->app->get_base_url() . '#/kit-library/cloud'
168 );
169 $content_data['import']['button_secondary'] = [
170 'url' => $kit_library_url,
171 'text' => esc_html__( 'Import from library', 'elementor' ),
172 'id' => 'elementor-import-export__import_from_library',
173 ];
174 }
175
176 $last_imported_kit = $this->revert->get_last_import_session();
177 $penultimate_imported_kit = $this->revert->get_penultimate_import_session();
178
179 $user_date_format = get_option( 'date_format' );
180 $user_time_format = get_option( 'time_format' );
181 $date_format = $user_date_format . ' ' . $user_time_format;
182
183 $should_show_revert_section = ! empty( $last_imported_kit );
184
185 if ( $should_show_revert_section ) {
186 if ( ! empty( $penultimate_imported_kit ) ) {
187 $revert_text = sprintf(
188 /* translators: 1: kit title, 2: date, 3: line break, 4: kit title, 5: date. */
189 esc_html__( 'Remove all the content and site settings that came with "%1$s" on %2$s %3$s and revert to the site setting that came with "%4$s" on %5$s.', 'elementor' ),
190 ! empty( $last_imported_kit['kit_title'] ) ? $last_imported_kit['kit_title'] : esc_html__( 'imported kit', 'elementor' ),
191 gmdate( $date_format, $last_imported_kit['start_timestamp'] ),
192 '<br>',
193 ! empty( $penultimate_imported_kit['kit_title'] ) ? $penultimate_imported_kit['kit_title'] : esc_html__( 'imported kit', 'elementor' ),
194 gmdate( $date_format, $penultimate_imported_kit['start_timestamp'] )
195 );
196 } else {
197 $revert_text = sprintf(
198 /* translators: 1: kit title, 2: date, 3: line break */
199 esc_html__( 'Remove all the content and site settings that came with "%1$s" on %2$s.%3$s Your original site settings will be restored.', 'elementor' ),
200 ! empty( $last_imported_kit['kit_title'] ) ? $last_imported_kit['kit_title'] : esc_html__( 'imported kit', 'elementor' ),
201 gmdate( $date_format, $last_imported_kit['start_timestamp'] ),
202 '<br>'
203 );
204 }
205 }
206 ?>
207
208 <div class="tab-import-export-kit__content">
209 <p class="tab-import-export-kit__info">
210 <?php
211 printf(
212 '%1$s <a href="https://go.elementor.com/wp-dash-import-export-general/" target="_blank">%2$s</a>',
213 esc_html__( 'Here’s where you can export this website as a .zip file, upload it to the cloud, or start the process of applying an existing template to your site.', 'elementor' ),
214 esc_html__( 'Learn more', 'elementor' ),
215 );
216 ?>
217 </p>
218
219 <div class="tab-import-export-kit__wrapper">
220 <?php foreach ( $content_data as $data ) {
221 $this->print_item_content( $data );
222 } ?>
223 </div>
224
225 <?php
226 if ( $should_show_revert_section ) {
227
228 $link_attributes = [
229 'href' => $this->get_revert_href(),
230 'id' => 'elementor-import-export__revert_kit',
231 'class' => 'button',
232 ];
233 ?>
234 <div class="tab-import-export-kit__revert">
235 <h2>
236 <?php echo esc_html__( 'Remove the most recent Website Template', 'elementor' ); ?>
237 </h2>
238 <p class="tab-import-export-kit__info">
239 <?php ElementorUtils::print_unescaped_internal_string( $revert_text ); ?>
240 </p>
241 <?php $this->render_last_kit_thumbnail( $last_imported_kit ); ?>
242 <a <?php ElementorUtils::print_html_attributes( $link_attributes ); ?> >
243 <?php echo esc_html__( 'Remove Website Template', 'elementor' ); ?>
244 </a>
245 </div>
246 <?php } ?>
247 </div>
248 <?php
249 }
250
251 private function print_item_content( $data ) {
252 $container_classes = 'tab-import-export-kit__container e-editor-one';
253 ?>
254 <div class="<?php echo esc_attr( $container_classes ); ?>">
255 <div class="tab-import-export-kit__box">
256 <h2><?php ElementorUtils::print_unescaped_internal_string( $data['title'] ); ?></h2>
257 </div>
258 <p class="description"><?php ElementorUtils::print_unescaped_internal_string( $data['description'] ); ?></p>
259
260 <?php if ( ! empty( $data['link'] ) ) : ?>
261 <a href="<?php ElementorUtils::print_unescaped_internal_string( $data['link']['url'] ); ?>" target="_blank"><?php ElementorUtils::print_unescaped_internal_string( $data['link']['text'] ); ?></a>
262 <?php endif; ?>
263 <div class="tab-import-export-kit__box action-buttons">
264 <?php if ( ! empty( $data['button_secondary'] ) ) : ?>
265 <a id="<?php ElementorUtils::print_unescaped_internal_string( $data['button_secondary']['id'] ); ?>" href="<?php ElementorUtils::print_unescaped_internal_string( $data['button_secondary']['url'] ); ?>" class="elementor-button e-btn-txt e-btn-txt-border">
266 <?php ElementorUtils::print_unescaped_internal_string( $data['button_secondary']['text'] ); ?>
267 </a>
268 <?php endif; ?>
269 <a <?php ElementorUtils::print_html_attributes( [ 'id' => $data['button']['id'] ] ); ?> href="<?php ElementorUtils::print_unescaped_internal_string( $data['button']['url'] ); ?>" class="elementor-button e-primary">
270 <?php ElementorUtils::print_unescaped_internal_string( $data['button']['text'] ); ?>
271 </a>
272 </div>
273 </div>
274 <?php
275 }
276
277 private function get_revert_href(): string {
278 $current_url = add_query_arg( null, null );
279 return $this->maybe_add_referrer_param( $current_url );
280 }
281
282 /**
283 * Checks if referred by a kit and adds the referrer ID to the href
284 *
285 * @param string $href
286 *
287 * @return string
288 */
289 private function maybe_add_referrer_param( string $href ): string {
290 $param_name = 'referrer_kit';
291
292 if ( empty( $_GET[ $param_name ] ) ) {
293 return $href;
294 }
295
296 return add_query_arg( $param_name, sanitize_key( $_GET[ $param_name ] ), $href );
297 }
298
299 /**
300 * Get referrer kit ID from current request
301 *
302 * @return string
303 */
304 private function get_referrer_kit_id_from_request(): string {
305 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Called via REST API with its own authentication
306 return sanitize_key( $_GET['referrer_kit'] ?? '' );
307 }
308
309 /**
310 * Render the last kit thumbnail if exists
311 *
312 * @param $last_imported_kit
313 *
314 * @return void
315 */
316 private function render_last_kit_thumbnail( $last_imported_kit ) {
317 if ( empty( $last_imported_kit['kit_thumbnail'] ) ) {
318 return;
319 }
320
321 ?>
322 <div class="tab-import-export-kit__kit-item-row">
323 <article class="tab-import-export-kit__kit-item">
324 <header>
325 <h3>
326 <?php echo esc_html( $last_imported_kit['kit_title'] ); ?>
327 </h3>
328 </header>
329 <img
330 src="<?php echo esc_url( $last_imported_kit['kit_thumbnail'] ); ?>"
331 alt="<?php echo esc_attr( $last_imported_kit['kit_title'] ); ?>"
332 loading="lazy"
333 >
334 </article>
335 </div>
336 <?php
337 }
338
339 /**
340 * Upload a kit zip file and get the kit data.
341 *
342 * Assigning the Import process to the 'import' property,
343 * so it will be available to use in different places such as: WP_Cli, Pro, etc.
344 *
345 * @param string $file Path to the file.
346 * @param string $referrer Referrer of the file 'local' or 'kit-library'.
347 * @param string $kit_id
348 * @return array
349 * @throws \Exception If customization validation fails or processing errors occur.
350 */
351 public function upload_kit( $file, $referrer, $kit_id = null ) {
352 $this->ensure_writing_permissions();
353
354 $this->import = new Import( $file, [
355 'referrer' => $referrer,
356 'id' => $kit_id,
357 ] );
358
359 $this->save_upload_session_data();
360
361 return [
362 'session' => $this->import->get_session_id(),
363 'manifest' => $this->import->get_manifest(),
364 'conflicts' => $this->import->get_settings_conflicts(),
365 ];
366 }
367
368 /**
369 * Import a kit by session_id.
370 * Upload and import a kit by kit zip file.
371 *
372 * If the split_to_chunks flag is true, the process won't start
373 * It will initialize the import process and return the session_id and the runners.
374 *
375 * Assigning the Import process to the 'import' property,
376 * so it will be available to use in different places such as: WP_Cli, Pro, etc.
377 *
378 * @param string $path Path to the file or session_id.
379 * @param array $settings Settings the import use to determine which content to import.
380 * (e.g: include, selected_plugins, selected_cpt, selected_override_conditions, etc.)
381 * @param bool $split_to_chunks Determine if the import process should be split into chunks.
382 * @return array
383 * @throws \Exception If export configuration is invalid or processing fails.
384 */
385 public function import_kit( string $path, array $settings, bool $split_to_chunks = false ): array {
386 $this->ensure_writing_permissions();
387 $this->ensure_DOMDocument_exists();
388
389 $this->import = new Import( $path, $settings );
390 $this->import->register_default_runners();
391
392 remove_filter( 'elementor/document/save/data', [ Plugin::$instance->modules_manager->get_modules( 'content-sanitizer' ), 'sanitize_content' ] );
393 do_action( 'elementor/import-export-customization/import-kit', $this->import );
394
395 if ( $split_to_chunks ) {
396 $this->import->init_import_session( true );
397
398 return [
399 'session' => $this->import->get_session_id(),
400 'runners' => $this->import->get_runners_name(),
401 ];
402 }
403
404 return $this->import->run();
405 }
406
407 private function save_upload_session_data(): void {
408 $this->import->init_import_session();
409 }
410
411 /**
412 * Resuming import process by re-creating the import instance and running the specific runner.
413 *
414 * @param string $session_id The id off the import session.
415 * @param string $runner_name The specific runner that we want to run.
416 *
417 * @return array Two types of response.
418 * 1. The status and the runner name.
419 * 2. The imported data. (Only if the runner is the last one in the import process)
420 * @throws \Exception If export configuration is invalid or processing fails.
421 */
422 public function import_kit_by_runner( string $session_id, string $runner_name ): array {
423 // Check session_id
424 $this->import = Import::from_session( $session_id );
425 $runners = $this->import->get_runners_name();
426
427 $run = $this->import->run_runner( $runner_name );
428
429 if ( end( $runners ) === $run['runner'] ) {
430 return $this->import->get_imported_data();
431 }
432
433 return $run;
434 }
435
436 /**
437 * Export a kit.
438 *
439 * Assigning the Export process to the 'export' property,
440 * so it will be available to use in different places such as: WP_Cli, Pro, etc.
441 *
442 * @param array $settings Settings the export use to determine which content to export.
443 * (e.g: include, kit_info, selected_plugins, selected_cpt, etc.)
444 * @return array
445 * @throws \Exception If export configuration is invalid or processing fails.
446 */
447 public function export_kit( array $settings ) {
448 $this->ensure_writing_permissions();
449
450 $this->export = new Export( $settings );
451 $this->export->register_default_runners();
452
453 do_action( 'elementor/import-export-customization/export-kit', $this->export );
454
455 return $this->export->run();
456 }
457
458 /**
459 * Handle revert kit request.
460 */
461 public function revert_last_imported_kit(): array {
462 $this->revert = new Revert();
463 $this->revert->register_default_runners();
464
465 $import_sessions = Revert::get_import_sessions();
466
467 if ( empty( $import_sessions ) ) {
468 return [
469 'revert_completed' => false,
470 'message' => __( 'No import sessions available to revert.', 'elementor' ),
471 'referrer_kit_id' => $this->get_referrer_kit_id_from_request(),
472 'show_referrer_dialog' => false,
473 ];
474 }
475
476 do_action( 'elementor/import-export-customization/revert-kit', $this->revert );
477
478 $this->revert->run();
479
480 $referrer_kit_id = $this->get_referrer_kit_id_from_request();
481
482 return [
483 'revert_completed' => true,
484 'referrer_kit_id' => $referrer_kit_id,
485 'show_referrer_dialog' => ! empty( $referrer_kit_id ),
486 ];
487 }
488
489 /**
490 * Register appropriate actions.
491 */
492 private function register_actions() {
493 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
494
495 $page_id = Tools::PAGE_ID;
496
497 add_action( "elementor/admin/after_create_settings/{$page_id}", [ $this, 'register_settings_tab' ] );
498
499 // TODO 18/04/2023 : This needs to be moved to the runner itself after https://elementor.atlassian.net/browse/HTS-434 is done.
500 if ( self::IMPORT_PLUGINS_ACTION === ElementorUtils::get_super_global_value( $_SERVER, 'HTTP_X_ELEMENTOR_ACTION' ) ) {
501 add_filter( 'woocommerce_create_pages', [ $this, 'empty_pages' ], 10, 0 );
502 }
503 // TODO ^^^
504
505 add_filter( 'elementor/import/kit/result', function( $result ) {
506 if ( ! empty( $result['file_url'] ) ) {
507 return [
508 'file_name' => $this->get_remote_kit_zip( $result['file_url'] ),
509 'referrer' => static::REFERRER_KIT_LIBRARY,
510 'file_url' => $result['file_url'],
511 ];
512 }
513
514 return $result;
515 } );
516 }
517
518 /**
519 * Prevent the creation of the default WooCommerce pages (Cart, Checkout, etc.)
520 *
521 * TODO 18/04/2023 : This needs to be moved to the runner itself after https://elementor.atlassian.net/browse/HTS-434 is done.
522 *
523 * @return array
524 */
525 public function empty_pages(): array {
526 return [];
527 }
528
529 private function ensure_writing_permissions() {
530 $server = new Server();
531
532 $paths_to_check = [
533 Server::KEY_PATH_WP_CONTENT_DIR => $server->get_system_path( Server::KEY_PATH_WP_CONTENT_DIR ),
534 Server::KEY_PATH_UPLOADS_DIR => $server->get_system_path( Server::KEY_PATH_UPLOADS_DIR ),
535 Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR => $server->get_system_path( Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR ),
536 ];
537
538 $permissions = $server->get_paths_permissions( $paths_to_check );
539
540 // WP Content dir has to be exists and writable.
541 if ( ! $permissions[ Server::KEY_PATH_WP_CONTENT_DIR ]['write'] ) {
542 throw new \Error( self::NO_WRITE_PERMISSIONS_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
543 }
544
545 // WP Uploads dir has to be exists and writable.
546 if ( ! $permissions[ Server::KEY_PATH_UPLOADS_DIR ]['write'] ) {
547 throw new \Error( self::NO_WRITE_PERMISSIONS_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
548 }
549
550 // Elementor uploads dir permissions is divided to 2 cases:
551 // 1. If the dir exists, it has to be writable.
552 // 2. If the dir doesn't exist, the parent dir has to be writable (wp uploads dir), so we can create it.
553 if ( $permissions[ Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['exists'] && ! $permissions[ Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['write'] ) {
554 throw new \Error( self::NO_WRITE_PERMISSIONS_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
555 }
556 }
557
558 private function ensure_DOMDocument_exists() {
559 if ( ! class_exists( 'DOMDocument' ) ) {
560 throw new \Error( self::DOMDOCUMENT_MISSING ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
561 }
562 }
563
564 /**
565 * Enqueue admin scripts
566 */
567 public function enqueue_scripts() {
568 wp_enqueue_script(
569 'elementor-import-export-admin',
570 $this->get_js_assets_url( 'import-export-admin' ),
571 [ 'elementor-common' ],
572 ELEMENTOR_VERSION,
573 true
574 );
575
576 wp_localize_script(
577 'elementor-import-export-admin',
578 'elementorImportExport',
579 [
580 'lastImportedSession' => $this->revert->get_last_import_session(),
581 'appUrl' => Plugin::$instance->app->get_base_url() . '#/kit-library',
582 ]
583 );
584
585 wp_enqueue_script(
586 'import-export-customization-admin',
587 $this->get_js_assets_url( 'import-export-customization-admin' ),
588 [ 'elementor-common', 'wp-api-fetch' ],
589 ELEMENTOR_VERSION,
590 true
591 );
592 }
593
594 protected function get_remote_kit_zip( $url ) {
595 $remote_zip_request = wp_safe_remote_get( $url );
596
597 if ( is_wp_error( $remote_zip_request ) ) {
598 Plugin::$instance->logger->get_logger()->error( $remote_zip_request->get_error_message() );
599 throw new \Error( static::KIT_LIBRARY_ERROR_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
600 }
601
602 if ( 200 !== $remote_zip_request['response']['code'] ) {
603 Plugin::$instance->logger->get_logger()->error( $remote_zip_request['response']['message'] );
604 throw new \Error( static::KIT_LIBRARY_ERROR_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
605 }
606
607 return Plugin::$instance->uploads_manager->create_temp_file( $remote_zip_request['body'], 'kit.zip' );
608 }
609
610 /**
611 * Get config data that will be exposed to the frontend.
612 */
613 private function get_config_data() {
614 $export_nonce = wp_create_nonce( 'elementor_export' );
615 $export_url = add_query_arg( [ '_nonce' => $export_nonce ], Plugin::$instance->app->get_base_url() );
616
617 return [
618 'exportURL' => $export_url,
619 'summaryTitles' => $this->get_summary_titles(),
620 'builtinWpPostTypes' => ImportExportUtils::get_builtin_wp_post_types(),
621 'elementorPostTypes' => ImportExportUtils::get_elementor_post_types(),
622 'isUnfilteredFilesEnabled' => Uploads_Manager::are_unfiltered_uploads_enabled(),
623 'elementorHomePageUrl' => $this->get_elementor_home_page_url(),
624 'recentlyEditedElementorPageUrl' => $this->get_recently_edited_elementor_page_url(),
625 'tools_url' => Tools::get_url(),
626 'importSessions' => Revert::get_import_sessions(),
627 'lastImportedSession' => $this->revert->get_last_import_session(),
628 'kitPreviewNonce' => wp_create_nonce( 'kit_thumbnail' ),
629 'restApiBaseUrl' => Controller::get_base_url(),
630 'restNonce' => wp_create_nonce( 'wp_rest' ),
631 'restUrl' => rest_url(),
632 'uiTheme' => $this->get_elementor_ui_theme_preference(),
633 'exportGroups' => $this->get_export_groups(),
634 'manifestVersion' => self::FORMAT_VERSION,
635 'elementorVersion' => ELEMENTOR_VERSION,
636 'upgradeVersionUrl' => admin_url( 'plugins.php' ),
637 'limits' => $this->get_limits(),
638 ];
639 }
640
641 private function get_elementor_ui_theme_preference() {
642 $editor_preferences = SettingsManager::get_settings_managers( 'editorPreferences' );
643
644 return $editor_preferences->get_model()->get_settings( 'ui_theme' );
645 }
646
647 private function get_export_groups() {
648 $export_groups = [];
649 $document_types = Plugin::$instance->documents->get_document_types();
650
651 foreach ( $document_types as $name => $document_type ) {
652 $export_groups[ $name ] = defined( $document_type . '::EXPORT_GROUP' ) ? $document_type::EXPORT_GROUP : '';
653 }
654
655 return $export_groups;
656 }
657
658 private function get_limits() {
659 $classes_limit = class_exists( Global_Classes_REST_API::class )
660 ? Global_Classes_REST_API::MAX_ITEMS
661 : 100;
662
663 $variables_limit = class_exists( Constants::class )
664 ? Constants::TOTAL_VARIABLES_COUNT
665 : 100;
666
667 return [
668 'classes' => $classes_limit,
669 'variables' => $variables_limit,
670 ];
671 }
672
673 /**
674 * Get labels of Elementor document types, Elementor Post types, WordPress Post types and Custom Post types.
675 */
676 private function get_summary_titles() {
677 $summary_titles = [];
678
679 $document_types = Plugin::$instance->documents->get_document_types();
680
681 foreach ( $document_types as $name => $document_type ) {
682 $summary_titles['templates'][ $name ] = [
683 'single' => $document_type::get_title(),
684 'plural' => $document_type::get_plural_title(),
685 ];
686 }
687
688 $elementor_post_types = ImportExportUtils::get_elementor_post_types();
689 $wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
690 $post_types = array_merge( $elementor_post_types, $wp_builtin_post_types );
691
692 foreach ( $post_types as $post_type ) {
693 $post_type_object = get_post_type_object( $post_type );
694
695 $summary_titles['content'][ $post_type ] = [
696 'single' => $post_type_object->labels->singular_name ?? '',
697 'plural' => $post_type_object->label ?? '',
698 ];
699 }
700
701 $custom_post_types = ImportExportUtils::get_registered_cpt_names();
702 if ( ! empty( $custom_post_types ) ) {
703 foreach ( $custom_post_types as $custom_post_type ) {
704
705 $custom_post_types_object = get_post_type_object( $custom_post_type );
706 // CPT data appears in two arrays:
707 // 1. content object: in order to show the export summary when completed in getLabel function
708 $summary_titles['content'][ $custom_post_type ] = [
709 'single' => $custom_post_types_object->labels->singular_name ?? '',
710 'plural' => $custom_post_types_object->label ?? '',
711 ];
712
713 // 2. customPostTypes object: in order to actually export the data
714 $summary_titles['content']['customPostTypes'][ $custom_post_type ] = [
715 'single' => $custom_post_types_object->labels->singular_name ?? '',
716 'plural' => $custom_post_types_object->label ?? '',
717 ];
718 }
719 }
720
721 return $summary_titles;
722 }
723
724 private function get_elementor_editor_home_page_url() {
725 if ( 'page' !== get_option( 'show_on_front' ) ) {
726 return '';
727 }
728
729 $frontpage_id = get_option( 'page_on_front' );
730
731 return $this->get_elementor_editor_page_url( $frontpage_id );
732 }
733
734 private function get_elementor_home_page_url() {
735 if ( 'page' !== get_option( 'show_on_front' ) ) {
736 return '';
737 }
738
739 $frontpage_id = get_option( 'page_on_front' );
740
741 return $this->get_elementor_page_url( $frontpage_id );
742 }
743
744 private function get_recently_edited_elementor_page_url() {
745 $query = ElementorUtils::get_recently_edited_posts_query( [ 'posts_per_page' => 1 ] );
746
747 if ( ! isset( $query->post ) ) {
748 return '';
749 }
750
751 return $this->get_elementor_page_url( $query->post->ID );
752 }
753
754 private function get_recently_edited_elementor_editor_page_url() {
755 $query = ElementorUtils::get_recently_edited_posts_query( [ 'posts_per_page' => 1 ] );
756
757 if ( ! isset( $query->post ) ) {
758 return '';
759 }
760
761 return $this->get_elementor_editor_page_url( $query->post->ID );
762 }
763
764 private function get_elementor_document( $page_id ) {
765 $document = Plugin::$instance->documents->get( $page_id );
766
767 if ( ! $document || ! $document->is_built_with_elementor() ) {
768 return false;
769 }
770
771 return $document;
772 }
773
774 private function get_elementor_page_url( $page_id ) {
775 $document = $this->get_elementor_document( $page_id );
776
777 return $document ? $document->get_preview_url() : '';
778 }
779
780 private function get_elementor_editor_page_url( $page_id ) {
781 $document = $this->get_elementor_document( $page_id );
782
783 return $document ? $document->get_edit_url() : '';
784 }
785
786 /**
787 * @param string $class_name
788 *
789 * @return bool
790 */
791 public function is_third_party_class( $class_name ) {
792 $allowed_classes = [
793 'Elementor\\',
794 'ElementorPro\\',
795 'WP_',
796 'wp_',
797 ];
798
799 foreach ( $allowed_classes as $allowed_class ) {
800 if ( str_starts_with( $class_name, $allowed_class ) ) {
801 return false;
802 }
803 }
804
805 return true;
806 }
807 }
808