PluginProbe
Elementor Website Builder – more than just a page builder / 3.34.3
Elementor Website Builder – more than just a page builder v3.34.3
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 3.34.3, at app/modules/import-export-customization/module.php

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