first commit
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Compatibility;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Import;
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base_Adapter {
|
||||
|
||||
/**
|
||||
* @param array $manifest_data
|
||||
* @param array $meta
|
||||
* @return false
|
||||
*/
|
||||
public static function is_compatibility_needed( array $manifest_data, array $meta ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function adapt_manifest( array $manifest_data ) {
|
||||
return $manifest_data;
|
||||
}
|
||||
|
||||
public function adapt_site_settings( array $site_settings, array $manifest_data, $path ) {
|
||||
return $site_settings;
|
||||
}
|
||||
|
||||
public function adapt_template( array $template_data, array $template_settings ) {
|
||||
return $template_data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Compatibility;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Envato extends Base_Adapter {
|
||||
public static function is_compatibility_needed( array $manifest_data, array $meta ) {
|
||||
return ! empty( $manifest_data['manifest_version'] );
|
||||
}
|
||||
|
||||
public function adapt_manifest( array $manifest_data ) {
|
||||
$templates = $manifest_data['templates'];
|
||||
|
||||
$manifest_data['templates'] = [];
|
||||
|
||||
foreach ( $templates as $template ) {
|
||||
// Envato store their global kit styles as a 'global.json' template file.
|
||||
// We need to be able to know the path to this specifc 'global.json' since it functions as the site-settings.json
|
||||
$is_global = ! empty( $template['metadata']['template_type'] ) && 'global-styles' === $template['metadata']['template_type'];
|
||||
if ( $is_global ) {
|
||||
// Adding the path of the 'global.json' template to the manifest which will be used in the future.
|
||||
$manifest_data['path-to-envto-site-settings'] = $template['source'];
|
||||
|
||||
// Getting the site-settings because Envato stores them in one of the posts.
|
||||
$kit = Plugin::$instance->kits_manager->get_active_kit();
|
||||
$kit_tabs = $kit->get_tabs();
|
||||
unset( $kit_tabs['settings-site-identity'] );
|
||||
$manifest_data['site-settings'] = array_keys( $kit_tabs );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Evanto uses "type" instead of "doc_type"
|
||||
$template['doc_type'] = $template['type'];
|
||||
|
||||
// Evanto uses for "name" instead of "title"
|
||||
$template['title'] = $template['name'];
|
||||
|
||||
// Envato specifying an exact path to the template rather than using its "ID" as an index.
|
||||
// This extracts the "file name" part out of our exact source list and we treat that as an ID.
|
||||
$file_name_without_extension = str_replace( '.json', '', basename( $template['source'] ) );
|
||||
|
||||
// Append the template to the global list:
|
||||
$manifest_data['templates'][ $file_name_without_extension ] = $template;
|
||||
}
|
||||
|
||||
$manifest_data['name'] = $manifest_data['title'];
|
||||
|
||||
return $manifest_data;
|
||||
}
|
||||
|
||||
public function adapt_site_settings( array $site_settings, array $manifest_data, $path ) {
|
||||
if ( empty( $manifest_data['path-to-envto-site-settings'] ) ) {
|
||||
return $site_settings;
|
||||
}
|
||||
|
||||
$global_file_path = $path . $manifest_data['path-to-envto-site-settings'];
|
||||
$global_file_data = ImportExportUtils::read_json_file( $global_file_path );
|
||||
|
||||
return [
|
||||
'settings' => $global_file_data['page_settings'],
|
||||
];
|
||||
}
|
||||
|
||||
public function adapt_template( array $template_data, array $template_settings ) {
|
||||
if ( ! empty( $template_data['metadata']['elementor_pro_conditions'] ) ) {
|
||||
foreach ( $template_data['metadata']['elementor_pro_conditions'] as $condition ) {
|
||||
list ( $type, $name, $sub_name, $sub_id ) = array_pad( explode( '/', $condition ), 4, '' );
|
||||
|
||||
$template_data['import_settings']['conditions'][] = compact( 'type', 'name', 'sub_name', 'sub_id' );
|
||||
}
|
||||
}
|
||||
|
||||
return $template_data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Compatibility;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Kit_Library extends Base_Adapter {
|
||||
public static function is_compatibility_needed( array $manifest_data, array $meta ) {
|
||||
return ! empty( $meta['referrer'] ) && 'kit-library' === $meta['referrer'];
|
||||
}
|
||||
|
||||
public function adapt_manifest( array $manifest_data ) {
|
||||
if ( ! empty( $manifest_data['content']['page'] ) ) {
|
||||
foreach ( $manifest_data['content']['page'] as & $page ) {
|
||||
$page['thumbnail'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $manifest_data['templates'] ) ) {
|
||||
foreach ( $manifest_data['templates'] as & $template ) {
|
||||
$template['thumbnail'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $manifest_data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,909 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\ImportExport;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Processes\Export;
|
||||
use Elementor\App\Modules\ImportExport\Processes\Import;
|
||||
use Elementor\App\Modules\ImportExport\Processes\Revert;
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\Files\Uploads_Manager;
|
||||
use Elementor\Modules\System_Info\Reporters\Server;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tools;
|
||||
use Elementor\Utils as ElementorUtils;
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Import Export Module
|
||||
*
|
||||
* Responsible for initializing Elementor App functionality
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
const FORMAT_VERSION = '2.0';
|
||||
|
||||
const EXPORT_TRIGGER_KEY = 'elementor_export_kit';
|
||||
|
||||
const UPLOAD_TRIGGER_KEY = 'elementor_upload_kit';
|
||||
|
||||
const IMPORT_TRIGGER_KEY = 'elementor_import_kit';
|
||||
|
||||
const IMPORT_RUNNER_TRIGGER_KEY = 'elementor_import_kit__runner';
|
||||
|
||||
const REFERRER_KIT_LIBRARY = 'kit-library';
|
||||
|
||||
const REFERRER_LOCAL = 'local';
|
||||
|
||||
const PLUGIN_PERMISSIONS_ERROR_KEY = 'plugin-installation-permissions-error';
|
||||
|
||||
const KIT_LIBRARY_ERROR_KEY = 'invalid-kit-library-zip-error';
|
||||
|
||||
const NO_WRITE_PERMISSIONS_KEY = 'no-write-permissions';
|
||||
|
||||
const THIRD_PARTY_ERROR = 'third-party-error';
|
||||
|
||||
const DOMDOCUMENT_MISSING = 'domdocument-missing';
|
||||
|
||||
const OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS = 'elementor_import_sessions';
|
||||
|
||||
const OPTION_KEY_ELEMENTOR_REVERT_SESSIONS = 'elementor_revert_sessions';
|
||||
|
||||
const META_KEY_ELEMENTOR_IMPORT_SESSION_ID = '_elementor_import_session_id';
|
||||
|
||||
const META_KEY_ELEMENTOR_EDIT_MODE = '_elementor_edit_mode';
|
||||
const IMPORT_PLUGINS_ACTION = 'import-plugins';
|
||||
|
||||
/**
|
||||
* Assigning the export process to a property, so we can use the process from outside the class.
|
||||
*
|
||||
* @var Export
|
||||
*/
|
||||
public $export;
|
||||
|
||||
/**
|
||||
* Assigning the import process to a property, so we can use the process from outside the class.
|
||||
*
|
||||
* @var Import
|
||||
*/
|
||||
public $import;
|
||||
|
||||
/**
|
||||
* Assigning the revert process to a property, so we can use the process from outside the class.
|
||||
*
|
||||
* @var Revert
|
||||
*/
|
||||
public $revert;
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'import-export';
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
$this->register_actions();
|
||||
|
||||
if ( ElementorUtils::is_wp_cli() ) {
|
||||
\WP_CLI::add_command( 'elementor kit', WP_CLI::class );
|
||||
}
|
||||
|
||||
( new Usage() )->register();
|
||||
|
||||
$this->revert = new Revert();
|
||||
}
|
||||
|
||||
public function get_init_settings() {
|
||||
if ( ! Plugin::$instance->app->is_current() ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->get_config_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the import/export tab in elementor tools.
|
||||
*/
|
||||
public function register_settings_tab( Tools $tools ) {
|
||||
$tools->add_tab( 'import-export-kit', [
|
||||
'label' => esc_html__( 'Import / Export Kit', 'elementor' ),
|
||||
'sections' => [
|
||||
'intro' => [
|
||||
'label' => esc_html__( 'Template Kits', 'elementor' ),
|
||||
'callback' => function() {
|
||||
$this->render_import_export_tab_content();
|
||||
},
|
||||
'fields' => [],
|
||||
],
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the import/export tab content.
|
||||
*/
|
||||
private function render_import_export_tab_content() {
|
||||
$intro_text_link = sprintf( '<a href="https://go.elementor.com/wp-dash-import-export-general/" target="_blank">%s</a>', esc_html__( 'Learn more', 'elementor' ) );
|
||||
|
||||
$intro_text = sprintf(
|
||||
/* translators: 1: New line break, 2: Learn more link. */
|
||||
__( 'Design sites faster with a template kit that contains some or all components of a complete site, like templates, content & site settings.%1$sYou can import a kit and apply it to your site, or export the elements from this site to be used anywhere else. %2$s', 'elementor' ),
|
||||
'<br>',
|
||||
$intro_text_link
|
||||
);
|
||||
|
||||
$content_data = [
|
||||
'export' => [
|
||||
'title' => esc_html__( 'Export a Template Kit', 'elementor' ),
|
||||
'button' => [
|
||||
'url' => Plugin::$instance->app->get_base_url() . '#/export',
|
||||
'text' => esc_html__( 'Start Export', 'elementor' ),
|
||||
],
|
||||
'description' => esc_html__( 'Bundle your whole site - or just some of its elements - to be used for another website.', 'elementor' ),
|
||||
'link' => [
|
||||
'url' => 'https://go.elementor.com/wp-dash-import-export-export-flow/',
|
||||
'text' => esc_html__( 'Learn More', 'elementor' ),
|
||||
],
|
||||
],
|
||||
'import' => [
|
||||
'title' => esc_html__( 'Import a Template Kit', 'elementor' ),
|
||||
'button' => [
|
||||
'url' => Plugin::$instance->app->get_base_url() . '#/import',
|
||||
'text' => esc_html__( 'Start Import', 'elementor' ),
|
||||
],
|
||||
'description' => esc_html__( 'Apply the design and settings of another site to this one.', 'elementor' ),
|
||||
'link' => [
|
||||
'url' => 'https://go.elementor.com/wp-dash-import-export-import-flow/',
|
||||
'text' => esc_html__( 'Learn More', 'elementor' ),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$last_imported_kit = $this->revert->get_last_import_session();
|
||||
$penultimate_imported_kit = $this->revert->get_penultimate_import_session();
|
||||
|
||||
$user_date_format = get_option( 'date_format' );
|
||||
$user_time_format = get_option( 'time_format' );
|
||||
$date_format = $user_date_format . ' ' . $user_time_format;
|
||||
|
||||
$should_show_revert_section = $this->should_show_revert_section( $last_imported_kit );
|
||||
|
||||
if ( $should_show_revert_section ) {
|
||||
if ( ! empty( $penultimate_imported_kit ) ) {
|
||||
$revert_text = sprintf(
|
||||
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' ),
|
||||
! empty( $last_imported_kit['kit_title'] ) ? $last_imported_kit['kit_title'] : esc_html__( 'imported kit', 'elementor' ),
|
||||
gmdate( $date_format, $last_imported_kit['start_timestamp'] ),
|
||||
'<br>',
|
||||
! empty( $penultimate_imported_kit['kit_title'] ) ? $penultimate_imported_kit['kit_title'] : esc_html__( 'imported kit', 'elementor' ),
|
||||
gmdate( $date_format, $penultimate_imported_kit['start_timestamp'] )
|
||||
);
|
||||
} else {
|
||||
$revert_text = sprintf(
|
||||
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' ),
|
||||
! empty( $last_imported_kit['kit_title'] ) ? $last_imported_kit['kit_title'] : esc_html__( 'imported kit', 'elementor' ),
|
||||
gmdate( $date_format, $last_imported_kit['start_timestamp'] ),
|
||||
'<br>'
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="tab-import-export-kit__content">
|
||||
<p class="tab-import-export-kit__info"><?php ElementorUtils::print_unescaped_internal_string( $intro_text ); ?></p>
|
||||
|
||||
<div class="tab-import-export-kit__wrapper">
|
||||
<?php foreach ( $content_data as $data ) { ?>
|
||||
<div class="tab-import-export-kit__container">
|
||||
<div class="tab-import-export-kit__box">
|
||||
<h2><?php ElementorUtils::print_unescaped_internal_string( $data['title'] ); ?></h2>
|
||||
<a href="<?php ElementorUtils::print_unescaped_internal_string( $data['button']['url'] ); ?>" class="elementor-button e-primary">
|
||||
<?php ElementorUtils::print_unescaped_internal_string( $data['button']['text'] ); ?>
|
||||
</a>
|
||||
</div>
|
||||
<p><?php ElementorUtils::print_unescaped_internal_string( $data['description'] ); ?></p>
|
||||
<a href="<?php ElementorUtils::print_unescaped_internal_string( $data['link']['url'] ); ?>" target="_blank"><?php ElementorUtils::print_unescaped_internal_string( $data['link']['text'] ); ?></a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if ( $should_show_revert_section ) {
|
||||
|
||||
$link_attributes = [
|
||||
'href' => $this->get_revert_href(),
|
||||
'id' => 'elementor-import-export__revert_kit',
|
||||
'class' => 'button',
|
||||
];
|
||||
?>
|
||||
<div class="tab-import-export-kit__revert">
|
||||
<h2>
|
||||
<?php ElementorUtils::print_unescaped_internal_string( esc_html__( 'Remove the most recent Kit', 'elementor' ) ); ?>
|
||||
</h2>
|
||||
<p class="tab-import-export-kit__info">
|
||||
<?php ElementorUtils::print_unescaped_internal_string( $revert_text ); ?>
|
||||
</p>
|
||||
<?php $this->render_last_kit_thumbnail( $last_imported_kit ); ?>
|
||||
<a <?php ElementorUtils::print_html_attributes( $link_attributes ); ?> >
|
||||
<?php ElementorUtils::print_unescaped_internal_string( esc_html__( 'Remove Kit', 'elementor' ) ); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function get_revert_href(): string {
|
||||
$admin_post_url = admin_url( 'admin-post.php?action=elementor_revert_kit' );
|
||||
$nonced_admin_post_url = wp_nonce_url( $admin_post_url, 'elementor_revert_kit' );
|
||||
return $this->maybe_add_referrer_param( $nonced_admin_post_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if referred by a kit and adds the referrer ID to the href
|
||||
*
|
||||
* @param string $href
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function maybe_add_referrer_param( string $href ): string {
|
||||
$param_name = 'referrer_kit';
|
||||
|
||||
if ( empty( $_GET[ $param_name ] ) ) {
|
||||
return $href;
|
||||
}
|
||||
|
||||
return add_query_arg( $param_name, sanitize_key( $_GET[ $param_name ] ), $href );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the last kit thumbnail if exists
|
||||
*
|
||||
* @param $last_imported_kit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_last_kit_thumbnail( $last_imported_kit ) {
|
||||
if ( empty( $last_imported_kit['kit_thumbnail'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="tab-import-export-kit__kit-item-row">
|
||||
<article class="tab-import-export-kit__kit-item">
|
||||
<header>
|
||||
<h3>
|
||||
<?php echo esc_html( $last_imported_kit['kit_title'] ); ?>
|
||||
</h3>
|
||||
</header>
|
||||
<img
|
||||
src="<?php echo esc_url( $last_imported_kit['kit_thumbnail'] ); ?>"
|
||||
alt="<?php echo esc_attr( $last_imported_kit['kit_title'] ); ?>"
|
||||
loading="lazy"
|
||||
>
|
||||
</article>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a kit zip file and get the kit data.
|
||||
*
|
||||
* Assigning the Import process to the 'import' property,
|
||||
* so it will be available to use in different places such as: WP_Cli, Pro, etc.
|
||||
*
|
||||
* @param string $file Path to the file.
|
||||
* @param string $referrer Referrer of the file 'local' or 'kit-library'.
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function upload_kit( $file, $referrer ) {
|
||||
$this->ensure_writing_permissions();
|
||||
|
||||
$this->import = new Import( $file, [ 'referrer' => $referrer ] );
|
||||
|
||||
return [
|
||||
'session' => $this->import->get_session_id(),
|
||||
'manifest' => $this->import->get_manifest(),
|
||||
'conflicts' => $this->import->get_settings_conflicts(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a kit by session_id.
|
||||
* Upload and import a kit by kit zip file.
|
||||
*
|
||||
* If the split_to_chunks flag is true, the process won't start
|
||||
* It will initialize the import process and return the session_id and the runners.
|
||||
*
|
||||
* Assigning the Import process to the 'import' property,
|
||||
* so it will be available to use in different places such as: WP_Cli, Pro, etc.
|
||||
*
|
||||
* @param string $path Path to the file or session_id.
|
||||
* @param array $settings Settings the import use to determine which content to import.
|
||||
* (e.g: include, selected_plugins, selected_cpt, selected_override_conditions, etc.)
|
||||
* @param bool $split_to_chunks Determine if the import process should be split into chunks.
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function import_kit( string $path, array $settings, bool $split_to_chunks = false ): array {
|
||||
$this->ensure_writing_permissions();
|
||||
$this->ensure_DOMDocument_exists();
|
||||
|
||||
$this->import = new Import( $path, $settings );
|
||||
$this->import->register_default_runners();
|
||||
|
||||
remove_filter( 'elementor/document/save/data', [ Plugin::$instance->modules_manager->get_modules( 'content-sanitizer' ), 'sanitize_content' ] );
|
||||
do_action( 'elementor/import-export/import-kit', $this->import );
|
||||
|
||||
if ( $split_to_chunks ) {
|
||||
$this->import->init_import_session( true );
|
||||
|
||||
return [
|
||||
'session' => $this->import->get_session_id(),
|
||||
'runners' => $this->import->get_runners_name(),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->import->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resuming import process by re-creating the import instance and running the specific runner.
|
||||
*
|
||||
* @param string $session_id The id off the import session.
|
||||
* @param string $runner_name The specific runner that we want to run.
|
||||
*
|
||||
* @return array Two types of response.
|
||||
* 1. The status and the runner name.
|
||||
* 2. The imported data. (Only if the runner is the last one in the import process)
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function import_kit_by_runner( string $session_id, string $runner_name ): array {
|
||||
// Check session_id
|
||||
$this->import = Import::from_session( $session_id );
|
||||
$runners = $this->import->get_runners_name();
|
||||
|
||||
$run = $this->import->run_runner( $runner_name );
|
||||
|
||||
if ( end( $runners ) === $run['runner'] ) {
|
||||
return $this->import->get_imported_data();
|
||||
}
|
||||
|
||||
return $run;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a kit.
|
||||
*
|
||||
* Assigning the Export process to the 'export' property,
|
||||
* so it will be available to use in different places such as: WP_Cli, Pro, etc.
|
||||
*
|
||||
* @param array $settings Settings the export use to determine which content to export.
|
||||
* (e.g: include, kit_info, selected_plugins, selected_cpt, etc.)
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function export_kit( array $settings ) {
|
||||
$this->ensure_writing_permissions();
|
||||
|
||||
$this->export = new Export( $settings );
|
||||
$this->export->register_default_runners();
|
||||
|
||||
do_action( 'elementor/import-export/export-kit', $this->export );
|
||||
|
||||
return $this->export->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle revert kit ajax request.
|
||||
*/
|
||||
public function revert_last_imported_kit() {
|
||||
$this->revert = new Revert();
|
||||
$this->revert->register_default_runners();
|
||||
|
||||
do_action( 'elementor/import-export/revert-kit', $this->revert );
|
||||
|
||||
$this->revert->run();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle revert last imported kit ajax request.
|
||||
*/
|
||||
public function handle_revert_last_imported_kit() {
|
||||
check_admin_referer( 'elementor_revert_kit' );
|
||||
|
||||
$this->revert_last_imported_kit();
|
||||
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=' . Tools::PAGE_ID . '#tab-import-export-kit' ) );
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register appropriate actions.
|
||||
*/
|
||||
private function register_actions() {
|
||||
add_action( 'admin_init', function() {
|
||||
if ( wp_doing_ajax() &&
|
||||
isset( $_POST['action'] ) &&
|
||||
wp_verify_nonce( ElementorUtils::get_super_global_value( $_POST, '_nonce' ), Ajax::NONCE_KEY ) &&
|
||||
current_user_can( 'manage_options' )
|
||||
) {
|
||||
$this->maybe_handle_ajax();
|
||||
}
|
||||
} );
|
||||
|
||||
add_action( 'admin_post_elementor_revert_kit', [ $this, 'handle_revert_last_imported_kit' ] );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
|
||||
|
||||
$page_id = Tools::PAGE_ID;
|
||||
|
||||
add_action( "elementor/admin/after_create_settings/{$page_id}", [ $this, 'register_settings_tab' ] );
|
||||
|
||||
// TODO 18/04/2023 : This needs to be moved to the runner itself after https://elementor.atlassian.net/browse/HTS-434 is done.
|
||||
if ( self::IMPORT_PLUGINS_ACTION === ElementorUtils::get_super_global_value( $_SERVER, 'HTTP_X_ELEMENTOR_ACTION' ) ) {
|
||||
add_filter( 'woocommerce_create_pages', [ $this, 'empty_pages' ], 10, 0 );
|
||||
}
|
||||
// TODO ^^^
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the creation of the default WooCommerce pages (Cart, Checkout, etc.)
|
||||
*
|
||||
* TODO 18/04/2023 : This needs to be moved to the runner itself after https://elementor.atlassian.net/browse/HTS-434 is done.
|
||||
* @return array
|
||||
*/
|
||||
public function empty_pages(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
private function ensure_writing_permissions() {
|
||||
$server = new Server();
|
||||
|
||||
$paths_to_check = [
|
||||
Server::KEY_PATH_WP_CONTENT_DIR => $server->get_system_path( Server::KEY_PATH_WP_CONTENT_DIR ),
|
||||
Server::KEY_PATH_UPLOADS_DIR => $server->get_system_path( Server::KEY_PATH_UPLOADS_DIR ),
|
||||
Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR => $server->get_system_path( Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR ),
|
||||
];
|
||||
|
||||
$permissions = $server->get_paths_permissions( $paths_to_check );
|
||||
|
||||
// WP Content dir has to be exists and writable.
|
||||
if ( ! $permissions[ Server::KEY_PATH_WP_CONTENT_DIR ]['write'] ) {
|
||||
throw new \Error( self::NO_WRITE_PERMISSIONS_KEY );
|
||||
}
|
||||
|
||||
// WP Uploads dir has to be exists and writable.
|
||||
if ( ! $permissions[ Server::KEY_PATH_UPLOADS_DIR ]['write'] ) {
|
||||
throw new \Error( self::NO_WRITE_PERMISSIONS_KEY );
|
||||
}
|
||||
|
||||
// Elementor uploads dir permissions is divided to 2 cases:
|
||||
// 1. If the dir exists, it has to be writable.
|
||||
// 2. If the dir doesn't exist, the parent dir has to be writable (wp uploads dir), so we can create it.
|
||||
if ( $permissions[ Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['exists'] && ! $permissions[ Server::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['write'] ) {
|
||||
throw new \Error( self::NO_WRITE_PERMISSIONS_KEY );
|
||||
}
|
||||
}
|
||||
|
||||
private function ensure_DOMDocument_exists() {
|
||||
if ( ! class_exists( 'DOMDocument' ) ) {
|
||||
throw new \Error( self::DOMDOCUMENT_MISSING );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_script(
|
||||
'elementor-import-export-admin',
|
||||
$this->get_js_assets_url( 'import-export-admin' ),
|
||||
[ 'elementor-common' ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'elementor-import-export-admin',
|
||||
'elementorImportExport',
|
||||
[
|
||||
'lastImportedSession' => $this->revert->get_last_import_session(),
|
||||
'appUrl' => Plugin::$instance->app->get_base_url() . '#/kit-library',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign each ajax action to a method.
|
||||
*/
|
||||
private function maybe_handle_ajax() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$action = ElementorUtils::get_super_global_value( $_POST, 'action' );
|
||||
|
||||
try {
|
||||
switch ( $action ) {
|
||||
case static::EXPORT_TRIGGER_KEY:
|
||||
$this->handle_export_kit();
|
||||
break;
|
||||
|
||||
case static::UPLOAD_TRIGGER_KEY:
|
||||
$this->handle_upload_kit();
|
||||
break;
|
||||
|
||||
case static::IMPORT_TRIGGER_KEY:
|
||||
$this->handle_import_kit();
|
||||
break;
|
||||
|
||||
case static::IMPORT_RUNNER_TRIGGER_KEY:
|
||||
$this->handle_import_kit__runner();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch ( \Error $e ) {
|
||||
if ( isset( $this->import ) ) {
|
||||
$this->import->finalize_import_session_option();
|
||||
}
|
||||
|
||||
Plugin::$instance->logger->get_logger()->error( $e->getMessage(), [
|
||||
'meta' => [
|
||||
'trace' => $e->getTraceAsString(),
|
||||
],
|
||||
] );
|
||||
|
||||
if ( isset( $this->import ) && $this->is_third_party_class( $e->getTrace()[0]['class'] ) ) {
|
||||
wp_send_json_error( self::THIRD_PARTY_ERROR, 500 );
|
||||
}
|
||||
|
||||
wp_send_json_error( $e->getMessage(), 500 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle upload kit ajax request.
|
||||
*/
|
||||
private function handle_upload_kit() {
|
||||
// PHPCS - A URL that should contain special chars (auth headers information).
|
||||
$file_url = isset( $_POST['e_import_file'] )
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
? wp_unslash( $_POST['e_import_file'] )
|
||||
: '';
|
||||
|
||||
// Import from kit library
|
||||
if ( ! empty( $file_url ) ) {
|
||||
if (
|
||||
! wp_verify_nonce( ElementorUtils::get_super_global_value( $_POST, 'e_kit_library_nonce' ), 'kit-library-import' )
|
||||
) {
|
||||
throw new \Error( 'Invalid kit library nonce.' );
|
||||
}
|
||||
|
||||
if ( ! filter_var( $file_url, FILTER_VALIDATE_URL ) || 0 !== strpos( $file_url, 'http' ) ) {
|
||||
throw new \Error( static::KIT_LIBRARY_ERROR_KEY );
|
||||
}
|
||||
|
||||
$remote_zip_request = wp_remote_get( $file_url );
|
||||
|
||||
if ( is_wp_error( $remote_zip_request ) ) {
|
||||
Plugin::$instance->logger->get_logger()->error( $remote_zip_request->get_error_message() );
|
||||
throw new \Error( static::KIT_LIBRARY_ERROR_KEY );
|
||||
}
|
||||
|
||||
if ( 200 !== $remote_zip_request['response']['code'] ) {
|
||||
Plugin::$instance->logger->get_logger()->error( $remote_zip_request['response']['message'] );
|
||||
throw new \Error( static::KIT_LIBRARY_ERROR_KEY );
|
||||
}
|
||||
|
||||
$file_name = Plugin::$instance->uploads_manager->create_temp_file( $remote_zip_request['body'], 'kit.zip' );
|
||||
$referrer = static::REFERRER_KIT_LIBRARY;
|
||||
} else {
|
||||
// PHPCS - Already validated in caller function.
|
||||
$file_name = ElementorUtils::get_super_global_value( $_FILES, 'e_import_file' )['tmp_name'];
|
||||
$referrer = static::REFERRER_LOCAL;
|
||||
}
|
||||
|
||||
Plugin::$instance->logger->get_logger()->info( 'Uploading Kit: ', [
|
||||
'meta' => [
|
||||
'kit_id' => ElementorUtils::get_super_global_value( $_POST, 'kit_id' ),
|
||||
'referrer' => $referrer,
|
||||
],
|
||||
] );
|
||||
|
||||
$uploaded_kit = $this->upload_kit( $file_name, $referrer );
|
||||
|
||||
$session_dir = $uploaded_kit['session'];
|
||||
$manifest = $uploaded_kit['manifest'];
|
||||
$conflicts = $uploaded_kit['conflicts'];
|
||||
|
||||
if ( ! empty( $file_url ) ) {
|
||||
Plugin::$instance->uploads_manager->remove_file_or_dir( dirname( $file_name ) );
|
||||
}
|
||||
|
||||
if ( isset( $manifest['plugins'] ) && ! current_user_can( 'install_plugins' ) ) {
|
||||
throw new \Error( static::PLUGIN_PERMISSIONS_ERROR_KEY );
|
||||
}
|
||||
|
||||
$result = [
|
||||
'session' => $session_dir,
|
||||
'manifest' => $manifest,
|
||||
];
|
||||
|
||||
if ( ! empty( $conflicts ) ) {
|
||||
$result['conflicts'] = $conflicts;
|
||||
} else {
|
||||
// Moved into the IE process \Elementor\App\Modules\ImportExport\Processes\Import::get_default_settings_conflicts
|
||||
// TODO: remove in 3.10.0
|
||||
$result = apply_filters( 'elementor/import/stage_1/result', $result );
|
||||
}
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle import kit ajax request.
|
||||
*/
|
||||
private function handle_import_kit() {
|
||||
// PHPCS - Already validated in caller function
|
||||
$settings = json_decode( ElementorUtils::get_super_global_value( $_POST, 'data' ), true ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$tmp_folder_id = $settings['session'];
|
||||
|
||||
$import = $this->import_kit( $tmp_folder_id, $settings, true );
|
||||
|
||||
// get_settings_config() added manually because the frontend Ajax request doesn't trigger the get_init_settings().
|
||||
$import['configData'] = $this->get_config_data();
|
||||
|
||||
Plugin::$instance->logger->get_logger()->info(
|
||||
sprintf( 'Selected import runners: %1$s',
|
||||
implode( ', ', $import['runners'] )
|
||||
)
|
||||
);
|
||||
|
||||
wp_send_json_success( $import );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ajax request for running specific runner in the import kit process.
|
||||
*/
|
||||
private function handle_import_kit__runner() {
|
||||
// PHPCS - Already validated in caller function
|
||||
$settings = json_decode( ElementorUtils::get_super_global_value( $_POST, 'data' ), true ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$session_id = $settings['session'];
|
||||
$runner = $settings['runner'];
|
||||
|
||||
$import = $this->import_kit_by_runner( $session_id, $runner );
|
||||
|
||||
// get_settings_config() added manually because the frontend Ajax request doesn't trigger the get_init_settings().
|
||||
$import['configData'] = $this->get_config_data();
|
||||
|
||||
if ( ! empty( $import['status'] ) ) {
|
||||
Plugin::$instance->logger->get_logger()->info(
|
||||
sprintf( 'Import runner completed: %1$s %2$s',
|
||||
$import['runner'],
|
||||
( 'success' === $import['status'] ? '✓' : '✗' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success( $import );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle export kit ajax request.
|
||||
*/
|
||||
private function handle_export_kit() {
|
||||
// PHPCS - Already validated in caller function
|
||||
$settings = json_decode( ElementorUtils::get_super_global_value( $_POST, 'data' ), true ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$export = $this->export_kit( $settings );
|
||||
|
||||
$file_name = $export['file_name'];
|
||||
$file = ElementorUtils::file_get_contents( $file_name );
|
||||
|
||||
if ( ! $file ) {
|
||||
throw new \Error( 'Could not read the exported file.' );
|
||||
}
|
||||
|
||||
Plugin::$instance->uploads_manager->remove_file_or_dir( dirname( $file_name ) );
|
||||
|
||||
$result = [
|
||||
'manifest' => $export['manifest'],
|
||||
'file' => base64_encode( $file ),
|
||||
];
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config data that will be exposed to the frontend.
|
||||
*/
|
||||
private function get_config_data() {
|
||||
$export_nonce = wp_create_nonce( 'elementor_export' );
|
||||
$export_url = add_query_arg( [ '_nonce' => $export_nonce ], Plugin::$instance->app->get_base_url() );
|
||||
|
||||
return [
|
||||
'exportURL' => $export_url,
|
||||
'summaryTitles' => $this->get_summary_titles(),
|
||||
'builtinWpPostTypes' => ImportExportUtils::get_builtin_wp_post_types(),
|
||||
'elementorPostTypes' => ImportExportUtils::get_elementor_post_types(),
|
||||
'isUnfilteredFilesEnabled' => Uploads_Manager::are_unfiltered_uploads_enabled(),
|
||||
'elementorHomePageUrl' => $this->get_elementor_home_page_url(),
|
||||
'recentlyEditedElementorPageUrl' => $this->get_recently_edited_elementor_page_url(),
|
||||
'tools_url' => Tools::get_url(),
|
||||
'importSessions' => Revert::get_import_sessions(),
|
||||
'lastImportedSession' => $this->revert->get_last_import_session(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get labels of Elementor document types, Elementor Post types, WordPress Post types and Custom Post types.
|
||||
*/
|
||||
private function get_summary_titles() {
|
||||
$summary_titles = [];
|
||||
|
||||
$document_types = Plugin::$instance->documents->get_document_types();
|
||||
|
||||
foreach ( $document_types as $name => $document_type ) {
|
||||
$summary_titles['templates'][ $name ] = [
|
||||
'single' => $document_type::get_title(),
|
||||
'plural' => $document_type::get_plural_title(),
|
||||
];
|
||||
}
|
||||
|
||||
$elementor_post_types = ImportExportUtils::get_elementor_post_types();
|
||||
$wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
$post_types = array_merge( $elementor_post_types, $wp_builtin_post_types );
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
$summary_titles['content'][ $post_type ] = [
|
||||
'single' => $post_type_object->labels->singular_name ?? '',
|
||||
'plural' => $post_type_object->label ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
$custom_post_types = ImportExportUtils::get_registered_cpt_names();
|
||||
if ( ! empty( $custom_post_types ) ) {
|
||||
foreach ( $custom_post_types as $custom_post_type ) {
|
||||
|
||||
$custom_post_types_object = get_post_type_object( $custom_post_type );
|
||||
// CPT data appears in two arrays:
|
||||
// 1. content object: in order to show the export summary when completed in getLabel function
|
||||
$summary_titles['content'][ $custom_post_type ] = [
|
||||
'single' => $custom_post_types_object->labels->singular_name ?? '',
|
||||
'plural' => $custom_post_types_object->label ?? '',
|
||||
];
|
||||
|
||||
// 2. customPostTypes object: in order to actually export the data
|
||||
$summary_titles['content']['customPostTypes'][ $custom_post_type ] = [
|
||||
'single' => $custom_post_types_object->labels->singular_name ?? '',
|
||||
'plural' => $custom_post_types_object->label ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$active_kit = Plugin::$instance->kits_manager->get_active_kit();
|
||||
|
||||
foreach ( $active_kit->get_tabs() as $key => $tab ) {
|
||||
$summary_titles['site-settings'][ $key ] = $tab->get_title();
|
||||
}
|
||||
|
||||
return $summary_titles;
|
||||
}
|
||||
|
||||
public function should_show_revert_section( $last_imported_kit ) {
|
||||
if ( empty( $last_imported_kit ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: BC - remove in the future
|
||||
// The 'templates' runner was in core and moved to the Pro plugin. (Part of it still exits in the Core for BC)
|
||||
// The runner that is in the core version is missing the revert functionality,
|
||||
// therefore we shouldn't display the revert section if the import process done with the core version.
|
||||
$is_import_templates_ran = isset( $last_imported_kit['runners']['templates'] );
|
||||
if ( $this->has_pro() && $is_import_templates_ran ) {
|
||||
$has_imported_templates = ! empty( $last_imported_kit['runners']['templates'] );
|
||||
|
||||
return $has_imported_templates;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function has_pro(): bool {
|
||||
return ElementorUtils::has_pro();
|
||||
}
|
||||
|
||||
private function get_elementor_editor_home_page_url() {
|
||||
if ( 'page' !== get_option( 'show_on_front' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$frontpage_id = get_option( 'page_on_front' );
|
||||
|
||||
return $this->get_elementor_editor_page_url( $frontpage_id );
|
||||
}
|
||||
|
||||
private function get_elementor_home_page_url() {
|
||||
if ( 'page' !== get_option( 'show_on_front' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$frontpage_id = get_option( 'page_on_front' );
|
||||
|
||||
return $this->get_elementor_page_url( $frontpage_id );
|
||||
}
|
||||
|
||||
private function get_recently_edited_elementor_page_url() {
|
||||
$query = ElementorUtils::get_recently_edited_posts_query( [ 'posts_per_page' => 1 ] );
|
||||
|
||||
if ( ! isset( $query->post ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->get_elementor_page_url( $query->post->ID );
|
||||
}
|
||||
|
||||
private function get_recently_edited_elementor_editor_page_url() {
|
||||
$query = ElementorUtils::get_recently_edited_posts_query( [ 'posts_per_page' => 1 ] );
|
||||
|
||||
if ( ! isset( $query->post ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->get_elementor_editor_page_url( $query->post->ID );
|
||||
}
|
||||
|
||||
private function get_elementor_document( $page_id ) {
|
||||
$document = Plugin::$instance->documents->get( $page_id );
|
||||
|
||||
if ( ! $document || ! $document->is_built_with_elementor() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
private function get_elementor_page_url( $page_id ) {
|
||||
$document = $this->get_elementor_document( $page_id );
|
||||
|
||||
return $document ? $document->get_preview_url() : '';
|
||||
}
|
||||
|
||||
private function get_elementor_editor_page_url( $page_id ) {
|
||||
$document = $this->get_elementor_document( $page_id );
|
||||
|
||||
return $document ? $document->get_edit_url() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_third_party_class( $class ) {
|
||||
$allowed_classes = [
|
||||
'Elementor\\',
|
||||
'ElementorPro\\',
|
||||
'WP_',
|
||||
'wp_',
|
||||
];
|
||||
|
||||
foreach ( $allowed_classes as $allowed_class ) {
|
||||
if ( str_starts_with( $class, $allowed_class ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Processes;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Module;
|
||||
use Elementor\App\Modules\ImportExport\Utils;
|
||||
use Elementor\Core\Utils\Str;
|
||||
use Elementor\Plugin;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Elementor_Content;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Export_Runner_Base;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Plugins;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Site_Settings;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Taxonomies;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Templates;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Export\Wp_Content;
|
||||
|
||||
class Export {
|
||||
const ZIP_ARCHIVE_MODULE_MISSING = 'zip-archive-module-is-missing';
|
||||
|
||||
/**
|
||||
* @var Export_Runner_Base[]
|
||||
*/
|
||||
protected $runners = [];
|
||||
|
||||
/**
|
||||
* Selected content types to export.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_include;
|
||||
|
||||
/**
|
||||
* The kit information. (e.g: title, description)
|
||||
*
|
||||
* @var array $export_data
|
||||
*/
|
||||
private $settings_kit_info;
|
||||
|
||||
/**
|
||||
* Selected plugins to export.
|
||||
* Contains the plugins essential data for export. (e.g: name, path, version, etc.)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_selected_plugins;
|
||||
|
||||
/**
|
||||
* Selected custom post types to export.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_selected_custom_post_types;
|
||||
|
||||
/**
|
||||
* The output data of the export process.
|
||||
* Will be written into the manifest.json file.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $manifest_data;
|
||||
|
||||
/**
|
||||
* The zip archive object.
|
||||
*
|
||||
* @var \ZipArchive
|
||||
*/
|
||||
private $zip;
|
||||
|
||||
public function __construct( $settings = [] ) {
|
||||
$this->settings_include = ! empty( $settings['include'] ) ? $settings['include'] : null;
|
||||
$this->settings_kit_info = ! empty( $settings['kitInfo'] ) ? $settings['kitInfo'] : null;
|
||||
$this->settings_selected_plugins = isset( $settings['plugins'] ) ? $settings['plugins'] : null;
|
||||
$this->settings_selected_custom_post_types = isset( $settings['selectedCustomPostTypes'] ) ? $settings['selectedCustomPostTypes'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a runner.
|
||||
*
|
||||
* @param Export_Runner_Base $runner_instance
|
||||
*/
|
||||
public function register( Export_Runner_Base $runner_instance ) {
|
||||
$this->runners[ $runner_instance::get_name() ] = $runner_instance;
|
||||
}
|
||||
|
||||
public function register_default_runners() {
|
||||
$this->register( new Site_Settings() );
|
||||
$this->register( new Plugins() );
|
||||
$this->register( new Templates() );
|
||||
$this->register( new Taxonomies() );
|
||||
$this->register( new Elementor_Content() );
|
||||
$this->register( new Wp_Content() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the export process.
|
||||
*
|
||||
* @return array The export data output.
|
||||
*
|
||||
* @throws \Exception If no export runners have been specified.
|
||||
*/
|
||||
public function run() {
|
||||
if ( empty( $this->runners ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the export process because no export runners have been specified. Try again by specifying export runners.' );
|
||||
}
|
||||
|
||||
$this->set_default_settings();
|
||||
|
||||
$this->init_zip_archive();
|
||||
$this->init_manifest_data();
|
||||
|
||||
$data = [
|
||||
'include' => $this->settings_include,
|
||||
'selected_plugins' => $this->settings_selected_plugins,
|
||||
'selected_custom_post_types' => $this->settings_selected_custom_post_types,
|
||||
];
|
||||
|
||||
foreach ( $this->runners as $runner ) {
|
||||
if ( $runner->should_export( $data ) ) {
|
||||
$export_result = $runner->export( $data );
|
||||
$this->handle_export_result( $export_result );
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_json_file( 'manifest', $this->manifest_data );
|
||||
|
||||
$zip_file_name = $this->zip->filename;
|
||||
$this->zip->close();
|
||||
|
||||
return [
|
||||
'manifest' => $this->manifest_data,
|
||||
'file_name' => $zip_file_name,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default settings for the export.
|
||||
*/
|
||||
private function set_default_settings() {
|
||||
if ( ! is_array( $this->get_settings_include() ) ) {
|
||||
$this->settings_include( $this->get_default_settings_include() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_kit_info() ) ) {
|
||||
$this->settings_kit_info( $this->get_default_settings_kit_info() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_selected_custom_post_types() ) && in_array( 'content', $this->settings_include, true ) ) {
|
||||
$this->settings_selected_custom_post_types( $this->get_default_settings_custom_post_types() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_selected_plugins() ) && in_array( 'plugins', $this->settings_include, true ) ) {
|
||||
$this->settings_selected_plugins( $this->get_default_settings_selected_plugins() );
|
||||
}
|
||||
}
|
||||
|
||||
public function settings_include( $include ) {
|
||||
$this->settings_include = $include;
|
||||
}
|
||||
|
||||
public function get_settings_include() {
|
||||
return $this->settings_include;
|
||||
}
|
||||
|
||||
private function settings_kit_info( $kit_info ) {
|
||||
$this->settings_kit_info = $kit_info;
|
||||
}
|
||||
|
||||
private function get_settings_kit_info() {
|
||||
return $this->settings_kit_info;
|
||||
}
|
||||
|
||||
public function settings_selected_custom_post_types( $selected_custom_post_types ) {
|
||||
$this->settings_selected_custom_post_types = $selected_custom_post_types;
|
||||
}
|
||||
|
||||
public function get_settings_selected_custom_post_types() {
|
||||
return $this->settings_selected_custom_post_types;
|
||||
}
|
||||
|
||||
public function settings_selected_plugins( $plugins ) {
|
||||
$this->settings_selected_plugins = $plugins;
|
||||
}
|
||||
|
||||
public function get_settings_selected_plugins() {
|
||||
return $this->settings_selected_plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of which content types should be exported.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_include() {
|
||||
return [ 'templates', 'content', 'settings', 'plugins' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of the kit info.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_kit_info() {
|
||||
return [
|
||||
'title' => 'kit',
|
||||
'description' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of the plugins that should be exported.
|
||||
*
|
||||
* @return array{name: string, plugin:string, pluginUri: string, version: string}
|
||||
*/
|
||||
private function get_default_settings_selected_plugins() {
|
||||
$installed_plugins = Plugin::$instance->wp->get_plugins();
|
||||
|
||||
return $installed_plugins->map( function ( $item, $key ) {
|
||||
return [
|
||||
'name' => $item['Name'],
|
||||
'plugin' => $key,
|
||||
'pluginUri' => $item['PluginURI'],
|
||||
'version' => $item['Version'],
|
||||
];
|
||||
} )->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of all the custom post types that should be exported.
|
||||
* Should be all the custom post types that are not built in to WordPress and not part of Elementor.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_custom_post_types() {
|
||||
return Utils::get_registered_cpt_names();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the zip archive.
|
||||
*/
|
||||
private function init_zip_archive() {
|
||||
if ( ! class_exists( '\ZipArchive' ) ) {
|
||||
throw new \Error( static::ZIP_ARCHIVE_MODULE_MISSING );
|
||||
}
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
$temp_dir = Plugin::$instance->uploads_manager->create_unique_dir();
|
||||
|
||||
$zip_file_name = $temp_dir . sanitize_title( $this->settings_kit_info['title'] ) . '.zip';
|
||||
|
||||
$zip->open( $zip_file_name, \ZipArchive::CREATE | \ZipArchive::OVERWRITE );
|
||||
|
||||
$this->zip = $zip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the manifest data and add some basic info to it.
|
||||
*/
|
||||
private function init_manifest_data() {
|
||||
$kit_post = Plugin::$instance->kits_manager->get_active_kit()->get_post();
|
||||
|
||||
$manifest_data = [
|
||||
'name' => sanitize_title( $this->settings_kit_info['title'] ),
|
||||
'title' => $this->settings_kit_info['title'],
|
||||
'description' => $this->settings_kit_info['description'],
|
||||
'author' => get_the_author_meta( 'display_name', $kit_post->post_author ),
|
||||
'version' => Module::FORMAT_VERSION,
|
||||
'elementor_version' => ELEMENTOR_VERSION,
|
||||
'created' => gmdate( 'Y-m-d H:i:s' ),
|
||||
'thumbnail' => get_the_post_thumbnail_url( $kit_post ),
|
||||
'site' => get_site_url(),
|
||||
];
|
||||
|
||||
$this->manifest_data = $manifest_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the export process output.
|
||||
* Add the manifest data from the runner to the manifest.json file.
|
||||
* Create files according to the files array that should be exported by the runner.
|
||||
*
|
||||
* @param array $export_result
|
||||
*/
|
||||
private function handle_export_result( $export_result ) {
|
||||
foreach ( $export_result['manifest'] as $data ) {
|
||||
$this->manifest_data += $data;
|
||||
}
|
||||
|
||||
if ( isset( $export_result['files']['path'] ) ) {
|
||||
$export_result['files'] = [ $export_result['files'] ];
|
||||
}
|
||||
|
||||
foreach ( $export_result['files'] as $file ) {
|
||||
$file_extension = pathinfo( $file['path'], PATHINFO_EXTENSION );
|
||||
if ( empty( $file_extension ) ) {
|
||||
$this->add_json_file(
|
||||
$file['path'],
|
||||
$file['data']
|
||||
);
|
||||
} else {
|
||||
$this->add_file(
|
||||
$file['path'],
|
||||
$file['data']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add json file to the zip archive.
|
||||
*
|
||||
* @param string $path The relative path to the file.
|
||||
* @param array $content The content of the file.
|
||||
* @param int $json_flags
|
||||
*/
|
||||
private function add_json_file( $path, array $content, $json_flags = 0 ) {
|
||||
if ( ! Str::ends_with( $path, '.json' ) ) {
|
||||
$path .= '.json';
|
||||
}
|
||||
|
||||
$this->add_file( $path, wp_json_encode( $content, $json_flags ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add file to the zip archive.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $content The content of the file.
|
||||
*/
|
||||
private function add_file( $file, $content ) {
|
||||
$this->zip->addFromString( $file, $content );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,807 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Processes;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Compatibility\Base_Adapter;
|
||||
use Elementor\App\Modules\ImportExport\Compatibility\Envato;
|
||||
use Elementor\App\Modules\ImportExport\Compatibility\Kit_Library;
|
||||
use Elementor\App\Modules\ImportExport\Utils;
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Core\Kits\Documents\Kit;
|
||||
use Elementor\Plugin;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Elementor_Content;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Import_Runner_Base;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Plugins;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Site_Settings;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Taxonomies;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Templates;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Import\Wp_Content;
|
||||
use Elementor\App\Modules\ImportExport\Module;
|
||||
use Elementor\App\Modules\KitLibrary\Connect\Kit_Library as Kit_Library_Api;
|
||||
|
||||
class Import {
|
||||
const MANIFEST_ERROR_KEY = 'manifest-error';
|
||||
|
||||
const ZIP_FILE_ERROR_KEY = 'invalid-zip-file';
|
||||
|
||||
const ZIP_ARCHIVE_ERROR_KEY = 'zip-archive-module-missing';
|
||||
|
||||
/**
|
||||
* @var Import_Runner_Base[]
|
||||
*/
|
||||
protected $runners = [];
|
||||
|
||||
/**
|
||||
* The session ID of the import process.
|
||||
* This ID is uniquely generated for each import process (by the temp folder which contains the extracted kit files).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $session_id;
|
||||
|
||||
/**
|
||||
* The Kit ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $kit_id;
|
||||
|
||||
/**
|
||||
* Adapter for the kit compatibility.
|
||||
*
|
||||
* @var Base_Adapter[]
|
||||
*/
|
||||
private $adapters;
|
||||
|
||||
/**
|
||||
* Document's data (elements and settings) that was imported during the process.
|
||||
*
|
||||
* @var array { [document_id] => { "elements": array , "settings": array } }
|
||||
*/
|
||||
private $documents_data = [];
|
||||
|
||||
/**
|
||||
* Path to the extracted kit files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $extracted_directory_path;
|
||||
|
||||
/**
|
||||
* Imported kit manifest.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $manifest;
|
||||
|
||||
/**
|
||||
* Imported kit site settings. (e.g: custom_colors, custom_typography, etc.)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $site_settings;
|
||||
|
||||
/**
|
||||
* Selected content types to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_include;
|
||||
|
||||
/**
|
||||
* Referer of the import. (e.g: kit-library, local, etc.)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $settings_referrer;
|
||||
|
||||
/**
|
||||
* All the conflict between the exited templates and the kit templates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_conflicts;
|
||||
|
||||
/**
|
||||
* Selected elementor templates conditions to override.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_selected_override_conditions;
|
||||
|
||||
/**
|
||||
* Selected custom post types to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_selected_custom_post_types;
|
||||
|
||||
/**
|
||||
* Selected plugins to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings_selected_plugins;
|
||||
|
||||
/**
|
||||
* The imported data output.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $imported_data = [];
|
||||
|
||||
/**
|
||||
* The metadata output of the import runners.
|
||||
* Will be saved in the import_session and will be used to revert the import process.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $runners_import_metadata = [];
|
||||
|
||||
/**
|
||||
* @param string $path session_id | zip_file_path
|
||||
* @param array $settings Use to determine which content to import.
|
||||
* (e.g: include, selected_plugins, selected_cpt, selected_override_conditions, etc.)
|
||||
* @param array|null $old_instance An array of old instance parameters that will be used for creating new instance.
|
||||
* We are using it for quick creation of the instance when the import process is being split into chunks.
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct( string $path, array $settings = [], array $old_instance = null ) {
|
||||
if ( ! empty( $old_instance ) ) {
|
||||
$this->set_import_object( $old_instance );
|
||||
} else {
|
||||
if ( is_file( $path ) ) {
|
||||
$this->extracted_directory_path = $this->extract_zip( $path );
|
||||
} else {
|
||||
$elementor_tmp_directory = Plugin::$instance->uploads_manager->get_temp_dir();
|
||||
$path = $elementor_tmp_directory . basename( $path );
|
||||
|
||||
if ( ! is_dir( $path ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the import process because the import session does not exist.' );
|
||||
}
|
||||
|
||||
$this->extracted_directory_path = $path . '/';
|
||||
}
|
||||
|
||||
$this->session_id = basename( $this->extracted_directory_path );
|
||||
$this->kit_id = $settings['id'] ?? '';
|
||||
$this->settings_referrer = ! empty( $settings['referrer'] ) ? $settings['referrer'] : 'local';
|
||||
$this->settings_include = ! empty( $settings['include'] ) ? $settings['include'] : null;
|
||||
|
||||
// Using isset and not empty is important since empty array is valid option.
|
||||
$this->settings_selected_override_conditions = $settings['overrideConditions'] ?? null;
|
||||
$this->settings_selected_custom_post_types = $settings['selectedCustomPostTypes'] ?? null;
|
||||
$this->settings_selected_plugins = $settings['plugins'] ?? null;
|
||||
|
||||
$this->manifest = $this->read_manifest_json();
|
||||
$this->site_settings = $this->read_site_settings_json();
|
||||
|
||||
$this->set_default_settings();
|
||||
}
|
||||
|
||||
add_filter( 'wp_php_error_args', function ( $args, $error ) {
|
||||
return $this->filter_php_error_args( $args, $error );
|
||||
}, 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the import object parameters.
|
||||
*
|
||||
* @param array $instance
|
||||
* @return void
|
||||
*/
|
||||
private function set_import_object( array $instance ) {
|
||||
$this->session_id = $instance['session_id'];
|
||||
|
||||
$instance_data = $instance['instance_data'];
|
||||
|
||||
$this->extracted_directory_path = $instance_data['extracted_directory_path'];
|
||||
$this->runners = $instance_data['runners'];
|
||||
$this->adapters = $instance_data['adapters'];
|
||||
|
||||
$this->manifest = $instance_data['manifest'];
|
||||
$this->site_settings = $instance_data['site_settings'];
|
||||
|
||||
$this->settings_include = $instance_data['settings_include'];
|
||||
$this->settings_referrer = $instance_data['settings_referrer'];
|
||||
$this->settings_conflicts = $instance_data['settings_conflicts'];
|
||||
$this->settings_selected_override_conditions = $instance_data['settings_selected_override_conditions'];
|
||||
$this->settings_selected_custom_post_types = $instance_data['settings_selected_custom_post_types'];
|
||||
$this->settings_selected_plugins = $instance_data['settings_selected_plugins'];
|
||||
|
||||
$this->documents_data = $instance_data['documents_data'];
|
||||
$this->imported_data = $instance_data['imported_data'];
|
||||
$this->runners_import_metadata = $instance_data['runners_import_metadata'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating a new instance of the import process by the id of the old import session.
|
||||
*
|
||||
* @param string $session_id
|
||||
*
|
||||
* @return Import
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function from_session( string $session_id ): Import {
|
||||
$import_sessions = Utils::get_import_sessions();
|
||||
|
||||
if ( ! $import_sessions || ! isset( $import_sessions[ $session_id ] ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the import process because the import session does not exist.' );
|
||||
}
|
||||
|
||||
$import_session = $import_sessions[ $session_id ];
|
||||
|
||||
return new self( $session_id, [], $import_session );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a runner.
|
||||
* Be aware that the runner will be executed in the order of registration, the order is crucial for the import process.
|
||||
*
|
||||
* @param Import_Runner_Base $runner_instance
|
||||
*/
|
||||
public function register( Import_Runner_Base $runner_instance ) {
|
||||
$this->runners[ $runner_instance::get_name() ] = $runner_instance;
|
||||
}
|
||||
|
||||
public function register_default_runners() {
|
||||
$this->register( new Site_Settings() );
|
||||
$this->register( new Plugins() );
|
||||
$this->register( new Templates() );
|
||||
$this->register( new Taxonomies() );
|
||||
$this->register( new Elementor_Content() );
|
||||
$this->register( new Wp_Content() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default settings for the import.
|
||||
*/
|
||||
private function set_default_settings() {
|
||||
if ( ! is_array( $this->get_settings_include() ) ) {
|
||||
$this->settings_include( $this->get_default_settings_include() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_conflicts() ) ) {
|
||||
$this->settings_conflicts( $this->get_default_settings_conflicts() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_selected_override_conditions() ) ) {
|
||||
$this->settings_selected_override_conditions( $this->get_default_settings_override_conditions() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_selected_custom_post_types() ) ) {
|
||||
$this->settings_selected_custom_post_types( $this->get_default_settings_custom_post_types() );
|
||||
}
|
||||
|
||||
if ( ! is_array( $this->get_settings_selected_plugins() ) ) {
|
||||
$this->settings_selected_plugins( $this->get_default_settings_plugins() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the import process.
|
||||
*
|
||||
* @return array The imported data output.
|
||||
*
|
||||
* @throws \Exception If no import runners have been specified.
|
||||
*/
|
||||
public function run() {
|
||||
if ( empty( $this->runners ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the import process because no import runners have been specified. Try again by specifying import runners.' );
|
||||
}
|
||||
|
||||
$data = [
|
||||
'session_id' => $this->session_id,
|
||||
'include' => $this->settings_include,
|
||||
'manifest' => $this->manifest,
|
||||
'site_settings' => $this->site_settings,
|
||||
'selected_plugins' => $this->settings_selected_plugins,
|
||||
'extracted_directory_path' => $this->extracted_directory_path,
|
||||
'selected_custom_post_types' => $this->settings_selected_custom_post_types,
|
||||
];
|
||||
|
||||
$this->init_import_session();
|
||||
|
||||
remove_filter( 'elementor/document/save/data', [ Plugin::$instance->modules_manager->get_modules( 'content-sanitizer' ), 'sanitize_content' ] );
|
||||
add_filter( 'elementor/document/save/data', [ $this, 'prevent_saving_elements_on_post_creation' ], 10, 2 );
|
||||
|
||||
// Set the Request's state as an Elementor upload request, in order to support unfiltered file uploads.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
|
||||
|
||||
foreach ( $this->runners as $runner ) {
|
||||
if ( $runner->should_import( $data ) ) {
|
||||
$import = $runner->import( $data, $this->imported_data );
|
||||
$this->imported_data = array_merge_recursive( $this->imported_data, $import );
|
||||
|
||||
$this->runners_import_metadata[ $runner::get_name() ] = $runner->get_import_session_metadata();
|
||||
}
|
||||
}
|
||||
|
||||
// After the upload complete, set the elementor upload state back to false.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( false );
|
||||
|
||||
remove_filter( 'elementor/document/save/data', [ $this, 'prevent_saving_elements_on_post_creation' ], 10 );
|
||||
|
||||
$this->finalize_import_session_option();
|
||||
|
||||
$this->save_elements_of_imported_posts();
|
||||
|
||||
Plugin::$instance->uploads_manager->remove_file_or_dir( $this->extracted_directory_path );
|
||||
return $this->imported_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run specific runner by runner_name
|
||||
*
|
||||
* @param string $runner_name
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception If no export runners have been specified.
|
||||
*/
|
||||
public function run_runner( string $runner_name ): array {
|
||||
if ( empty( $this->runners ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the import process because no import runners have been specified. Try again by specifying import runners.' );
|
||||
}
|
||||
|
||||
$data = [
|
||||
'session_id' => $this->session_id,
|
||||
'include' => $this->settings_include,
|
||||
'manifest' => $this->manifest,
|
||||
'site_settings' => $this->site_settings,
|
||||
'selected_plugins' => $this->settings_selected_plugins,
|
||||
'extracted_directory_path' => $this->extracted_directory_path,
|
||||
'selected_custom_post_types' => $this->settings_selected_custom_post_types,
|
||||
];
|
||||
|
||||
add_filter( 'elementor/document/save/data', [ $this, 'prevent_saving_elements_on_post_creation' ], 10, 2 );
|
||||
|
||||
// Set the Request's state as an Elementor upload request, in order to support unfiltered file uploads.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
|
||||
|
||||
$runner = $this->runners[ $runner_name ];
|
||||
|
||||
if ( empty( $runner ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the import process because the import runner was not found. Try again by specifying an import runner.' );
|
||||
}
|
||||
|
||||
if ( $runner->should_import( $data ) ) {
|
||||
$import = $runner->import( $data, $this->imported_data );
|
||||
$this->imported_data = array_merge_recursive( $this->imported_data, $import );
|
||||
|
||||
$this->runners_import_metadata[ $runner::get_name() ] = $runner->get_import_session_metadata();
|
||||
}
|
||||
|
||||
// After the upload complete, set the elementor upload state back to false.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( false );
|
||||
|
||||
remove_filter( 'elementor/document/save/data', [ $this, 'prevent_saving_elements_on_post_creation' ], 10 );
|
||||
|
||||
$is_last_runner = key( array_slice( $this->runners, -1, 1, true ) ) === $runner_name;
|
||||
if ( $is_last_runner ) {
|
||||
$this->finalize_import_session_option();
|
||||
$this->save_elements_of_imported_posts();
|
||||
} else {
|
||||
$this->update_instance_data_in_import_session_option();
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'runner' => $runner_name,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and save all the instance data to the import sessions option.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_import_session( $save_instance_data = false ) {
|
||||
$import_sessions = Utils::get_import_sessions( true );
|
||||
|
||||
$import_sessions[ $this->session_id ] = [
|
||||
'session_id' => $this->session_id,
|
||||
'kit_title' => $this->manifest['title'] ?? '',
|
||||
'kit_name' => $this->manifest['name'] ?? '',
|
||||
'kit_thumbnail' => $this->get_kit_thumbnail(),
|
||||
'kit_source' => $this->settings_referrer,
|
||||
'user_id' => get_current_user_id(),
|
||||
'start_timestamp' => current_time( 'timestamp' ),
|
||||
];
|
||||
|
||||
if ( $save_instance_data ) {
|
||||
$import_sessions[ $this->session_id ]['instance_data'] = [
|
||||
'extracted_directory_path' => $this->extracted_directory_path,
|
||||
'runners' => $this->runners,
|
||||
'adapters' => $this->adapters,
|
||||
|
||||
'manifest' => $this->manifest,
|
||||
'site_settings' => $this->site_settings,
|
||||
|
||||
'settings_include' => $this->settings_include,
|
||||
'settings_referrer' => $this->settings_referrer,
|
||||
'settings_conflicts' => $this->settings_conflicts,
|
||||
'settings_selected_override_conditions' => $this->settings_selected_override_conditions,
|
||||
'settings_selected_custom_post_types' => $this->settings_selected_custom_post_types,
|
||||
'settings_selected_plugins' => $this->settings_selected_plugins,
|
||||
|
||||
'documents_data' => $this->documents_data,
|
||||
'imported_data' => $this->imported_data,
|
||||
'runners_import_metadata' => $this->runners_import_metadata,
|
||||
];
|
||||
}
|
||||
|
||||
update_option( Module::OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS, $import_sessions, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Kit thumbnail, goes to the home page thumbnail if main doesn't exist
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_kit_thumbnail(): string {
|
||||
if ( ! empty( $this->manifest['thumbnail'] ) ) {
|
||||
return $this->manifest['thumbnail'];
|
||||
}
|
||||
|
||||
if ( empty( $this->kit_id ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$api = new Kit_Library_Api();
|
||||
$kit = $api->get_by_id( $this->kit_id );
|
||||
|
||||
if ( is_wp_error( $kit ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $kit->thumbnail;
|
||||
}
|
||||
|
||||
public function get_runners_name(): array {
|
||||
return array_keys( $this->runners );
|
||||
}
|
||||
|
||||
public function get_manifest() {
|
||||
return $this->manifest;
|
||||
}
|
||||
|
||||
public function get_extracted_directory_path() {
|
||||
return $this->extracted_directory_path;
|
||||
}
|
||||
|
||||
public function get_session_id() {
|
||||
return $this->session_id;
|
||||
}
|
||||
|
||||
public function get_adapters() {
|
||||
return $this->adapters;
|
||||
}
|
||||
|
||||
public function get_imported_data() {
|
||||
return $this->imported_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings by key.
|
||||
* Used for backward compatibility.
|
||||
*
|
||||
* @param string $key The key of the setting.
|
||||
*/
|
||||
public function get_settings( $key ) {
|
||||
switch ( $key ) {
|
||||
case 'include':
|
||||
return $this->get_settings_include();
|
||||
|
||||
case 'overrideConditions':
|
||||
return $this->get_settings_selected_override_conditions();
|
||||
|
||||
case 'selectedCustomPostTypes':
|
||||
return $this->get_settings_selected_custom_post_types();
|
||||
|
||||
case 'plugins':
|
||||
return $this->get_settings_selected_plugins();
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function settings_include( array $settings_include ) {
|
||||
$this->settings_include = $settings_include;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_settings_include() {
|
||||
return $this->settings_include;
|
||||
}
|
||||
|
||||
public function settings_referrer( $settings_referrer ) {
|
||||
$this->settings_referrer = $settings_referrer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_settings_referrer() {
|
||||
return $this->settings_referrer;
|
||||
}
|
||||
|
||||
public function settings_conflicts( array $settings_conflicts ) {
|
||||
$this->settings_conflicts = $settings_conflicts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_settings_conflicts() {
|
||||
return $this->settings_conflicts;
|
||||
}
|
||||
|
||||
public function settings_selected_override_conditions( array $settings_selected_override_conditions ) {
|
||||
$this->settings_selected_override_conditions = $settings_selected_override_conditions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_settings_selected_override_conditions() {
|
||||
return $this->settings_selected_override_conditions;
|
||||
}
|
||||
|
||||
public function settings_selected_custom_post_types( array $settings_selected_custom_post_types ) {
|
||||
$this->settings_selected_custom_post_types = $settings_selected_custom_post_types;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_settings_selected_custom_post_types() {
|
||||
return $this->settings_selected_custom_post_types;
|
||||
}
|
||||
|
||||
public function settings_selected_plugins( array $settings_selected_plugins ) {
|
||||
$this->settings_selected_plugins = $settings_selected_plugins;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_settings_selected_plugins() {
|
||||
return $this->settings_selected_plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent saving elements on elementor post creation.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Document $document
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function prevent_saving_elements_on_post_creation( array $data, Document $document ) {
|
||||
if ( isset( $data['elements'] ) ) {
|
||||
$this->documents_data[ $document->get_main_id() ] = [ 'elements' => $data['elements'] ];
|
||||
|
||||
$data['elements'] = [];
|
||||
}
|
||||
|
||||
if ( isset( $data['settings'] ) ) {
|
||||
$this->documents_data[ $document->get_main_id() ]['settings'] = $data['settings'];
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the zip file.
|
||||
*
|
||||
* @param string $zip_path The path to the zip file.
|
||||
* @return string The extracted directory path.
|
||||
*/
|
||||
private function extract_zip( $zip_path ) {
|
||||
$extraction_result = Plugin::$instance->uploads_manager->extract_and_validate_zip( $zip_path, [ 'json', 'xml' ] );
|
||||
|
||||
if ( is_wp_error( $extraction_result ) ) {
|
||||
if ( isset( $extraction_result->errors['zip_error'] ) ) {
|
||||
throw new \Error( static::ZIP_ARCHIVE_ERROR_KEY );
|
||||
}
|
||||
|
||||
throw new \Error( static::ZIP_FILE_ERROR_KEY );
|
||||
}
|
||||
|
||||
return $extraction_result['extraction_directory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the manifest file from the extracted directory and adapt it if needed.
|
||||
*
|
||||
* @return string The manifest file content.
|
||||
*/
|
||||
private function read_manifest_json() {
|
||||
$manifest = Utils::read_json_file( $this->extracted_directory_path . 'manifest' );
|
||||
|
||||
if ( ! $manifest ) {
|
||||
Plugin::$instance->logger->get_logger()->error( static::MANIFEST_ERROR_KEY );
|
||||
throw new \Error( static::ZIP_FILE_ERROR_KEY );
|
||||
}
|
||||
|
||||
$this->init_adapters( $manifest );
|
||||
|
||||
foreach ( $this->adapters as $adapter ) {
|
||||
$manifest = $adapter->adapt_manifest( $manifest );
|
||||
}
|
||||
|
||||
return $manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the adapters and determine which ones to use.
|
||||
*
|
||||
* @param array $manifest_data The manifest file content.
|
||||
*/
|
||||
private function init_adapters( array $manifest_data ) {
|
||||
$this->adapters = [];
|
||||
|
||||
/** @var Base_Adapter[] $adapter_types */
|
||||
$adapter_types = [ Envato::class, Kit_Library::class ];
|
||||
|
||||
foreach ( $adapter_types as $adapter_type ) {
|
||||
if ( $adapter_type::is_compatibility_needed( $manifest_data, [ 'referrer' => $this->get_settings_referrer() ] ) ) {
|
||||
$this->adapters[] = new $adapter_type( $this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site settings file from the extracted directory and adapt it if needed.
|
||||
*
|
||||
* @return string The site settings file content.
|
||||
*/
|
||||
private function read_site_settings_json() {
|
||||
$site_settings = Utils::read_json_file( $this->extracted_directory_path . 'site-settings' );
|
||||
|
||||
foreach ( $this->adapters as $adapter ) {
|
||||
$site_settings = $adapter->adapt_site_settings( $site_settings, $this->manifest, $this->extracted_directory_path );
|
||||
}
|
||||
|
||||
return $site_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the custom post types in the kit.
|
||||
*
|
||||
* @return array Custom post types names.
|
||||
*/
|
||||
private function get_default_settings_custom_post_types() {
|
||||
if ( empty( $this->manifest['custom-post-type-title'] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$manifest_post_types = array_keys( $this->manifest['custom-post-type-title'] );
|
||||
|
||||
return array_diff( $manifest_post_types, Utils::get_builtin_wp_post_types() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of elementor templates conditions to override.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_conflicts() {
|
||||
if ( empty( $this->manifest['templates'] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return apply_filters( 'elementor/import/get_default_settings_conflicts', [], $this->manifest['templates'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of elementor templates conditions to override.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_override_conditions() {
|
||||
if ( empty( $this->settings_conflicts ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_keys( $this->settings_conflicts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of the plugins that should be imported.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_plugins() {
|
||||
return ! empty( $this->manifest['plugins'] ) ? $this->manifest['plugins'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default settings of which content types should be imported.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_settings_include() {
|
||||
return [ 'templates', 'plugins', 'content', 'settings' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data that requires updating/replacement when imported.
|
||||
*
|
||||
* @return array{post_ids: array, term_ids: array}
|
||||
*/
|
||||
private function get_imported_data_replacements() : array {
|
||||
return [
|
||||
'post_ids' => Utils::map_old_new_post_ids( $this->imported_data ),
|
||||
'term_ids' => Utils::map_old_new_term_ids( $this->imported_data ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the prevented elements on elementor post creation elements.
|
||||
* Handle the replacement of all the dynamic content of the elements that probably have been changed during the import.
|
||||
*/
|
||||
private function save_elements_of_imported_posts() {
|
||||
$imported_data_replacements = $this->get_imported_data_replacements();
|
||||
|
||||
foreach ( $this->documents_data as $new_id => $data ) {
|
||||
$document = Plugin::$instance->documents->get( $new_id );
|
||||
|
||||
if ( isset( $data['elements'] ) ) {
|
||||
$data['elements'] = $document->on_import_update_dynamic_content( $data['elements'], $imported_data_replacements );
|
||||
}
|
||||
|
||||
if ( isset( $data['settings'] ) ) {
|
||||
|
||||
if ( $document instanceof Kit ) {
|
||||
// Without post_status certain tabs in the Kit will not save properly.
|
||||
$data['settings']['post_status'] = get_post_status( $new_id );
|
||||
}
|
||||
|
||||
$data['settings'] = $document->on_import_update_settings( $data['settings'], $imported_data_replacements );
|
||||
}
|
||||
|
||||
$document->save( $data );
|
||||
}
|
||||
}
|
||||
|
||||
private function update_instance_data_in_import_session_option() {
|
||||
$import_sessions = Utils::get_import_sessions();
|
||||
|
||||
$import_sessions[ $this->session_id ]['instance_data']['documents_data'] = $this->documents_data;
|
||||
$import_sessions[ $this->session_id ]['instance_data']['imported_data'] = $this->imported_data;
|
||||
$import_sessions[ $this->session_id ]['instance_data']['runners_import_metadata'] = $this->runners_import_metadata;
|
||||
|
||||
update_option( Module::OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS, $import_sessions, false );
|
||||
}
|
||||
|
||||
public function finalize_import_session_option() {
|
||||
$import_sessions = Utils::get_import_sessions();
|
||||
|
||||
if ( ! isset( $import_sessions[ $this->session_id ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset( $import_sessions[ $this->session_id ]['instance_data'] );
|
||||
|
||||
$import_sessions[ $this->session_id ]['end_timestamp'] = current_time( 'timestamp' );
|
||||
$import_sessions[ $this->session_id ]['runners'] = $this->runners_import_metadata;
|
||||
|
||||
update_option( Module::OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS, $import_sessions, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the php error args and return 408 status code if the error is a timeout.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array $error
|
||||
* @return array
|
||||
*/
|
||||
private function filter_php_error_args( $args, $error ) {
|
||||
if ( strpos( $error['message'], 'Maximum execution time' ) !== false ) {
|
||||
$args['response'] = 408;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\ImportExport\Processes;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Module;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Elementor_Content;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Revert_Runner_Base;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Plugins;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Site_Settings;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Taxonomies;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Templates;
|
||||
use Elementor\App\Modules\ImportExport\Runners\Revert\Wp_Content;
|
||||
use Elementor\App\Modules\ImportExport\Utils;
|
||||
|
||||
class Revert {
|
||||
|
||||
/**
|
||||
* @var Revert_Runner_Base[]
|
||||
*/
|
||||
protected $runners = [];
|
||||
|
||||
private $import_sessions;
|
||||
|
||||
private $revert_sessions;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->import_sessions = self::get_import_sessions();
|
||||
$this->revert_sessions = self::get_revert_sessions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a runner.
|
||||
*
|
||||
* @param Revert_Runner_Base $runner_instance
|
||||
*/
|
||||
public function register( Revert_Runner_Base $runner_instance ) {
|
||||
$this->runners[ $runner_instance::get_name() ] = $runner_instance;
|
||||
}
|
||||
|
||||
public function register_default_runners() {
|
||||
$this->register( new Site_Settings() );
|
||||
$this->register( new Plugins() );
|
||||
$this->register( new Templates() );
|
||||
$this->register( new Taxonomies() );
|
||||
$this->register( new Elementor_Content() );
|
||||
$this->register( new Wp_Content() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the revert process.
|
||||
*
|
||||
* @throws \Exception If no revert runners have been specified.
|
||||
*/
|
||||
public function run() {
|
||||
if ( empty( $this->runners ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the revert process because no revert runners have been specified. Try again by specifying revert runners.' );
|
||||
}
|
||||
|
||||
$import_session = $this->get_last_import_session();
|
||||
|
||||
if ( empty( $import_session ) ) {
|
||||
throw new \Exception( 'Couldn’t execute the revert process because there are no import sessions to revert.' );
|
||||
}
|
||||
|
||||
// fallback if the import session failed and doesn't have the runners metadata
|
||||
if ( ! isset( $import_session['runners'] ) && isset( $import_session['instance_data'] ) ) {
|
||||
$import_session['runners'] = $import_session['instance_data']['runners_import_metadata'] ?? [];
|
||||
}
|
||||
|
||||
foreach ( $this->runners as $runner ) {
|
||||
if ( $runner->should_revert( $import_session ) ) {
|
||||
$runner->revert( $import_session );
|
||||
}
|
||||
}
|
||||
|
||||
$this->revert_attachments( $import_session );
|
||||
|
||||
$this->delete_last_import_data();
|
||||
}
|
||||
|
||||
public static function get_import_sessions() {
|
||||
$import_sessions = Utils::get_import_sessions();
|
||||
|
||||
if ( ! $import_sessions ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
usort( $import_sessions, function( $a, $b ) {
|
||||
return strcmp( $a['start_timestamp'], $b['start_timestamp'] );
|
||||
} );
|
||||
|
||||
return $import_sessions;
|
||||
}
|
||||
|
||||
public static function get_revert_sessions() {
|
||||
$revert_sessions = get_option( Module::OPTION_KEY_ELEMENTOR_REVERT_SESSIONS );
|
||||
|
||||
if ( ! $revert_sessions ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $revert_sessions;
|
||||
}
|
||||
|
||||
public function get_last_import_session() {
|
||||
$import_sessions = $this->import_sessions;
|
||||
|
||||
if ( empty( $import_sessions ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return end( $import_sessions );
|
||||
}
|
||||
|
||||
public function get_penultimate_import_session() {
|
||||
$sessions_data = $this->import_sessions;
|
||||
$penultimate_element_value = [];
|
||||
|
||||
if ( empty( $sessions_data ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
end( $sessions_data );
|
||||
|
||||
prev( $sessions_data );
|
||||
|
||||
if ( ! is_null( key( $sessions_data ) ) ) {
|
||||
$penultimate_element_value = current( $sessions_data );
|
||||
}
|
||||
|
||||
return $penultimate_element_value;
|
||||
}
|
||||
|
||||
private function delete_last_import_data() {
|
||||
$import_sessions = $this->import_sessions;
|
||||
$revert_sessions = $this->revert_sessions;
|
||||
|
||||
$reverted_session = array_pop( $import_sessions );
|
||||
|
||||
$revert_sessions[] = [
|
||||
'session_id' => $reverted_session['session_id'],
|
||||
'kit_title' => $reverted_session['kit_title'],
|
||||
'kit_name' => $reverted_session['kit_name'],
|
||||
'kit_thumbnail' => $reverted_session['kit_thumbnail'],
|
||||
'source' => $reverted_session['kit_source'],
|
||||
'user_id' => get_current_user_id(),
|
||||
'import_timestamp' => $reverted_session['start_timestamp'],
|
||||
'revert_timestamp' => current_time( 'timestamp' ),
|
||||
];
|
||||
|
||||
update_option( Module::OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS, $import_sessions, false );
|
||||
update_option( Module::OPTION_KEY_ELEMENTOR_REVERT_SESSIONS, $revert_sessions, false );
|
||||
|
||||
$this->import_sessions = $import_sessions;
|
||||
$this->revert_sessions = $revert_sessions;
|
||||
}
|
||||
|
||||
private function revert_attachments( $data ) {
|
||||
$query_args = [
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'any',
|
||||
'posts_per_page' => -1,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => Module::META_KEY_ELEMENTOR_IMPORT_SESSION_ID,
|
||||
'value' => $data['session_id'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$query = new \WP_Query( $query_args );
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
wp_delete_attachment( $post->ID, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Elementor_Content extends Export_Runner_Base {
|
||||
private $page_on_front_id;
|
||||
|
||||
public function __construct() {
|
||||
$this->init_page_on_front_data();
|
||||
}
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'elementor-content';
|
||||
}
|
||||
|
||||
public function should_export( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'content', $data['include'], true )
|
||||
);
|
||||
}
|
||||
|
||||
public function export( array $data ) {
|
||||
$elementor_post_types = ImportExportUtils::get_elementor_post_types();
|
||||
|
||||
$files = [];
|
||||
$manifest = [];
|
||||
|
||||
foreach ( $elementor_post_types as $post_type ) {
|
||||
$export = $this->export_elementor_post_type( $post_type );
|
||||
$files = array_merge( $files, $export['files'] );
|
||||
|
||||
$manifest[ $post_type ] = $export['manifest_data'];
|
||||
}
|
||||
|
||||
$manifest_data['content'] = $manifest;
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function export_elementor_post_type( $post_type ) {
|
||||
$query_args = [
|
||||
'post_type' => $post_type,
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_EDIT_MODE,
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => '_elementor_data',
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => '_elementor_data',
|
||||
'compare' => '!=',
|
||||
'value' => '[]',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$query = new \WP_Query( $query_args );
|
||||
|
||||
if ( empty( $query ) ) {
|
||||
return [
|
||||
'files' => [],
|
||||
'manifest_data' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$post_type_taxonomies = $this->get_post_type_taxonomies( $post_type );
|
||||
|
||||
$manifest_data = [];
|
||||
$files = [];
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
$document = Plugin::$instance->documents->get( $post->ID );
|
||||
|
||||
$terms = ! empty( $post_type_taxonomies ) ? $this->get_post_terms( $post->ID, $post_type_taxonomies ) : [];
|
||||
|
||||
$post_manifest_data = [
|
||||
'title' => $post->post_title,
|
||||
'excerpt' => $post->post_excerpt,
|
||||
'doc_type' => $document->get_name(),
|
||||
'thumbnail' => get_the_post_thumbnail_url( $post ),
|
||||
'url' => get_permalink( $post ),
|
||||
'terms' => $terms,
|
||||
];
|
||||
|
||||
if ( $post->ID === $this->page_on_front_id ) {
|
||||
$post_manifest_data['show_on_front'] = true;
|
||||
}
|
||||
|
||||
$manifest_data[ $post->ID ] = $post_manifest_data;
|
||||
|
||||
$files[] = [
|
||||
'path' => 'content/' . $post_type . '/' . $post->ID,
|
||||
'data' => $document->get_export_data(),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'manifest_data' => $manifest_data,
|
||||
];
|
||||
}
|
||||
|
||||
private function get_post_type_taxonomies( $post_type ) {
|
||||
return get_object_taxonomies( $post_type );
|
||||
}
|
||||
|
||||
private function get_post_terms( $post_id, array $taxonomies ) {
|
||||
$terms = wp_get_object_terms( $post_id, $taxonomies );
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$result[] = [
|
||||
'term_id' => $term->term_id,
|
||||
'taxonomy' => $term->taxonomy,
|
||||
'slug' => $term->slug,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function init_page_on_front_data() {
|
||||
$show_page_on_front = 'page' === get_option( 'show_on_front' );
|
||||
|
||||
if ( $show_page_on_front ) {
|
||||
$this->page_on_front_id = (int) get_option( 'page_on_front' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Runners\Runner_Interface;
|
||||
|
||||
abstract class Export_Runner_Base implements Runner_Interface {
|
||||
|
||||
/**
|
||||
* By the passed data we should decide if we want to run the export function of the runner or not.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function should_export( array $data );
|
||||
|
||||
/**
|
||||
* Main function of the runner export process.
|
||||
*
|
||||
* @param array $data Necessary data for the export process.
|
||||
*
|
||||
* @return array{files: array, manifest: array}
|
||||
* The files that should be part of the kit and the relevant manifest data.
|
||||
*/
|
||||
abstract public function export( array $data );
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
class Plugins extends Export_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'plugins';
|
||||
}
|
||||
|
||||
public function should_export( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'plugins', $data['include'], true ) &&
|
||||
is_array( $data['selected_plugins'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function export( array $data ) {
|
||||
$manifest_data['plugins'] = $data['selected_plugins'];
|
||||
|
||||
return [
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
'files' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Site_Settings extends Export_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'site-settings';
|
||||
}
|
||||
|
||||
public function should_export( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'settings', $data['include'], true )
|
||||
);
|
||||
}
|
||||
|
||||
public function export( array $data ) {
|
||||
$kit = Plugin::$instance->kits_manager->get_active_kit();
|
||||
$kit_data = $kit->get_export_data();
|
||||
$kit_tabs = $kit->get_tabs();
|
||||
|
||||
$excluded_kit_settings_keys = [
|
||||
'site_name',
|
||||
'site_description',
|
||||
'site_logo',
|
||||
'site_favicon',
|
||||
];
|
||||
|
||||
foreach ( $excluded_kit_settings_keys as $setting_key ) {
|
||||
unset( $kit_data['settings'][ $setting_key ] );
|
||||
}
|
||||
|
||||
unset( $kit_tabs['settings-site-identity'] );
|
||||
|
||||
$kit_tabs = array_keys( $kit_tabs );
|
||||
$manifest_data['site-settings'] = $kit_tabs;
|
||||
|
||||
return [
|
||||
'files' => [
|
||||
'path' => 'site-settings',
|
||||
'data' => $kit_data,
|
||||
],
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
|
||||
class Taxonomies extends Export_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'taxonomies';
|
||||
}
|
||||
|
||||
public function should_export( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'content', $data['include'], true )
|
||||
);
|
||||
}
|
||||
|
||||
public function export( array $data ) {
|
||||
$wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
$selected_custom_post_types = isset( $data['selected_custom_post_types'] ) ? $data['selected_custom_post_types'] : [];
|
||||
$post_types = array_merge( $wp_builtin_post_types, $selected_custom_post_types );
|
||||
|
||||
$export = $this->export_taxonomies( $post_types );
|
||||
|
||||
$manifest_data['taxonomies'] = $export['manifest'];
|
||||
|
||||
return [
|
||||
'files' => $export['files'],
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function export_taxonomies( array $post_types ) {
|
||||
$files = [];
|
||||
$manifest = [];
|
||||
|
||||
$taxonomies = get_taxonomies();
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$taxonomy_post_types = get_taxonomy( $taxonomy )->object_type;
|
||||
$intersected_post_types = array_intersect( $taxonomy_post_types, $post_types );
|
||||
|
||||
if ( empty( $intersected_post_types ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $this->export_terms( $taxonomy );
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $intersected_post_types as $post_type ) {
|
||||
$manifest[ $post_type ][] = $taxonomy;
|
||||
}
|
||||
|
||||
$files[] = [
|
||||
'path' => 'taxonomies/' . $taxonomy,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'manifest' => $manifest,
|
||||
];
|
||||
}
|
||||
|
||||
private function export_terms( $taxonomy ) {
|
||||
$terms = get_terms( [
|
||||
'taxonomy' => (array) $taxonomy,
|
||||
'hide_empty' => true,
|
||||
'get' => 'all',
|
||||
] );
|
||||
|
||||
$ordered_terms = $this->order_terms( $terms );
|
||||
|
||||
if ( empty( $ordered_terms ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ( $ordered_terms as $term ) {
|
||||
$data[] = [
|
||||
'term_id' => $term->term_id,
|
||||
'name' => $term->name,
|
||||
'slug' => $term->slug,
|
||||
'taxonomy' => $term->taxonomy,
|
||||
'description' => $term->description,
|
||||
'parent' => $term->parent,
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Put terms in order with no child going before its parent.
|
||||
private function order_terms( array $terms ) {
|
||||
$ordered_terms = [];
|
||||
|
||||
while ( $term = array_shift( $terms ) ) {
|
||||
$is_top_level = 0 === $term->parent;
|
||||
$is_parent_exits = isset( $ordered_terms[ $term->parent ] );
|
||||
|
||||
if ( $is_top_level || $is_parent_exits ) {
|
||||
$ordered_terms[ $term->term_id ] = $term;
|
||||
} else {
|
||||
$terms[] = $term;
|
||||
}
|
||||
}
|
||||
|
||||
return $ordered_terms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Utils;
|
||||
|
||||
class Templates extends Export_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'templates';
|
||||
}
|
||||
|
||||
public function should_export( array $data ) {
|
||||
return (
|
||||
Utils::has_pro() &&
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'templates', $data['include'], true )
|
||||
);
|
||||
}
|
||||
|
||||
public function export( array $data ) {
|
||||
$template_types = array_values( Source_Local::get_template_types() );
|
||||
|
||||
$query_args = [
|
||||
'post_type' => Source_Local::CPT,
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => Document::TYPE_META_KEY,
|
||||
'value' => $template_types,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$templates_query = new \WP_Query( $query_args );
|
||||
|
||||
$templates_manifest_data = [];
|
||||
$files = [];
|
||||
|
||||
foreach ( $templates_query->posts as $template_post ) {
|
||||
$template_id = $template_post->ID;
|
||||
|
||||
$template_document = Plugin::$instance->documents->get( $template_id );
|
||||
|
||||
$templates_manifest_data[ $template_id ] = $template_document->get_export_summary();
|
||||
|
||||
$files[] = [
|
||||
'path' => 'templates/' . $template_id,
|
||||
'data' => $template_document->get_export_data(),
|
||||
];
|
||||
}
|
||||
|
||||
$manifest_data['templates'] = $templates_manifest_data;
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Export;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Core\Utils\ImportExport\WP_Exporter;
|
||||
|
||||
class Wp_Content extends Export_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'wp-content';
|
||||
}
|
||||
|
||||
public function should_export( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'content', $data['include'], true )
|
||||
);
|
||||
}
|
||||
|
||||
public function export( array $data ) {
|
||||
$post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
$custom_post_types = isset( $data['selected_custom_post_types'] ) ? $data['selected_custom_post_types'] : [];
|
||||
|
||||
$files = [];
|
||||
$manifest_data = [];
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$export = $this->export_wp_post_type( $post_type );
|
||||
$files[] = $export['file'];
|
||||
$manifest_data['wp-content'][ $post_type ] = $export['manifest_data'];
|
||||
}
|
||||
|
||||
foreach ( $custom_post_types as $post_type ) {
|
||||
$export = $this->export_wp_post_type( $post_type );
|
||||
$files[] = $export['file'];
|
||||
$manifest_data['wp-content'][ $post_type ] = $export['manifest_data'];
|
||||
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
$manifest_data['custom-post-type-title'][ $post_type ] = [
|
||||
'name' => $post_type_object->name,
|
||||
'label' => $post_type_object->label,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function export_wp_post_type( $post_type ) {
|
||||
$wp_exporter = new WP_Exporter( [
|
||||
'content' => $post_type,
|
||||
'status' => 'publish',
|
||||
'limit' => 20,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_EDIT_MODE,
|
||||
'compare' => 'NOT EXISTS',
|
||||
],
|
||||
],
|
||||
'include_post_featured_image_as_attachment' => true,
|
||||
] );
|
||||
|
||||
$export_result = $wp_exporter->run();
|
||||
|
||||
return [
|
||||
'file' => [
|
||||
'path' => 'wp-content/' . $post_type . '/' . $post_type . '.xml',
|
||||
'data' => $export_result['xml'],
|
||||
],
|
||||
'manifest_data' => $export_result['ids'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Elementor_Content extends Import_Runner_Base {
|
||||
|
||||
private $show_page_on_front;
|
||||
|
||||
private $page_on_front_id;
|
||||
|
||||
private $import_session_id;
|
||||
|
||||
public function __construct() {
|
||||
$this->init_page_on_front_data();
|
||||
}
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'elementor-content';
|
||||
}
|
||||
|
||||
public function should_import( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'content', $data['include'], true ) &&
|
||||
! empty( $data['manifest']['content'] ) &&
|
||||
! empty( $data['extracted_directory_path'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function import( array $data, array $imported_data ) {
|
||||
$result['content'] = [];
|
||||
$this->import_session_id = $data['session_id'];
|
||||
|
||||
$elementor_post_types = ImportExportUtils::get_elementor_post_types();
|
||||
|
||||
foreach ( $elementor_post_types as $post_type ) {
|
||||
if ( empty( $data['manifest']['content'][ $post_type ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$posts_settings = $data['manifest']['content'][ $post_type ];
|
||||
$path = $data['extracted_directory_path'] . 'content/' . $post_type . '/';
|
||||
$imported_terms = ! empty( $imported_data['taxonomies'] )
|
||||
? ImportExportUtils::map_old_new_term_ids( $imported_data )
|
||||
: [];
|
||||
|
||||
$result['content'][ $post_type ] = $this->import_elementor_post_type( $posts_settings, $path, $post_type, $imported_terms );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function import_elementor_post_type( array $posts_settings, $path, $post_type, array $imported_terms ) {
|
||||
$result = [
|
||||
'succeed' => [],
|
||||
'failed' => [],
|
||||
];
|
||||
|
||||
foreach ( $posts_settings as $id => $post_settings ) {
|
||||
try {
|
||||
$post_data = ImportExportUtils::read_json_file( $path . $id );
|
||||
$import = $this->import_post( $post_settings, $post_data, $post_type, $imported_terms );
|
||||
|
||||
if ( is_wp_error( $import ) ) {
|
||||
$result['failed'][ $id ] = $import->get_error_message();
|
||||
continue;
|
||||
}
|
||||
|
||||
$result['succeed'][ $id ] = $import;
|
||||
} catch ( \Exception $error ) {
|
||||
$result['failed'][ $id ] = $error->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function import_post( array $post_settings, array $post_data, $post_type, array $imported_terms ) {
|
||||
$post_attributes = [
|
||||
'post_title' => $post_settings['title'],
|
||||
'post_type' => $post_type,
|
||||
'post_status' => 'publish',
|
||||
];
|
||||
|
||||
if ( ! empty( $post_settings['excerpt'] ) ) {
|
||||
$post_attributes['post_excerpt'] = $post_settings['excerpt'];
|
||||
}
|
||||
|
||||
$new_document = Plugin::$instance->documents->create(
|
||||
$post_settings['doc_type'],
|
||||
$post_attributes
|
||||
);
|
||||
|
||||
if ( is_wp_error( $new_document ) ) {
|
||||
throw new \Exception( $new_document->get_error_message() );
|
||||
}
|
||||
|
||||
$post_data['import_settings'] = $post_settings;
|
||||
|
||||
$new_attachment_callback = function( $attachment_id ) {
|
||||
$this->set_session_post_meta( $attachment_id, $this->import_session_id );
|
||||
};
|
||||
|
||||
add_filter( 'elementor/template_library/import_images/new_attachment', $new_attachment_callback );
|
||||
|
||||
$new_document->import( $post_data );
|
||||
|
||||
remove_filter( 'elementor/template_library/import_images/new_attachment', $new_attachment_callback );
|
||||
|
||||
$new_post_id = $new_document->get_main_id();
|
||||
|
||||
if ( ! empty( $post_settings['terms'] ) ) {
|
||||
$this->set_post_terms( $new_post_id, $post_settings['terms'], $imported_terms );
|
||||
}
|
||||
|
||||
if ( ! empty( $post_settings['show_on_front'] ) ) {
|
||||
$this->set_page_on_front( $new_post_id );
|
||||
}
|
||||
|
||||
$this->set_session_post_meta( $new_post_id, $this->import_session_id );
|
||||
|
||||
return $new_post_id;
|
||||
}
|
||||
|
||||
private function set_post_terms( $post_id, array $terms, array $imported_terms ) {
|
||||
foreach ( $terms as $term ) {
|
||||
if ( ! isset( $imported_terms[ $term['term_id'] ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_set_post_terms( $post_id, [ $imported_terms[ $term['term_id'] ] ], $term['taxonomy'], false );
|
||||
}
|
||||
}
|
||||
|
||||
private function init_page_on_front_data() {
|
||||
$this->show_page_on_front = 'page' === get_option( 'show_on_front' );
|
||||
|
||||
if ( $this->show_page_on_front ) {
|
||||
$this->page_on_front_id = (int) get_option( 'page_on_front' );
|
||||
}
|
||||
}
|
||||
|
||||
private function set_page_on_front( $page_id ) {
|
||||
update_option( 'page_on_front', $page_id );
|
||||
|
||||
if ( ! $this->show_page_on_front ) {
|
||||
update_option( 'show_on_front', 'page' );
|
||||
}
|
||||
}
|
||||
|
||||
public function get_import_session_metadata() : array {
|
||||
return [
|
||||
'page_on_front' => $this->page_on_front_id ?? 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Runners\Runner_Interface;
|
||||
|
||||
abstract class Import_Runner_Base implements Runner_Interface {
|
||||
|
||||
/**
|
||||
* By the passed data we should decide if we want to run the import function of the runner or not.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function should_import( array $data );
|
||||
|
||||
/**
|
||||
* Main function of the runner import process.
|
||||
*
|
||||
* @param array $data Necessary data for the import process.
|
||||
* @param array $imported_data Data that already imported by previously runners.
|
||||
*
|
||||
* @return array The result of the import process
|
||||
*/
|
||||
abstract public function import( array $data, array $imported_data );
|
||||
|
||||
public function get_import_session_metadata() : array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function set_session_post_meta( $post_id, $meta_value ) {
|
||||
update_post_meta( $post_id, static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID, $meta_value );
|
||||
}
|
||||
|
||||
public function set_session_term_meta( $term_id, $meta_value ) {
|
||||
update_term_meta( $term_id, static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID, $meta_value );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Core\Utils\Plugins_Manager;
|
||||
use Elementor\Core\Utils\Str;
|
||||
|
||||
class Plugins extends Import_Runner_Base {
|
||||
|
||||
/**
|
||||
* @var Plugins_Manager
|
||||
*/
|
||||
private $plugins_manager;
|
||||
|
||||
public function __construct( $plugins_manager = null ) {
|
||||
if ( $plugins_manager ) {
|
||||
$this->plugins_manager = $plugins_manager;
|
||||
} else {
|
||||
$this->plugins_manager = new Plugins_Manager();
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'plugins';
|
||||
}
|
||||
|
||||
public function should_import( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'plugins', $data['include'], true ) &&
|
||||
! empty( $data['manifest']['plugins'] ) &&
|
||||
! empty( $data['selected_plugins'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function import( array $data, array $imported_data ) {
|
||||
$plugins = $data['selected_plugins'];
|
||||
|
||||
$plugins_collection = ( new Collection( $plugins ) )
|
||||
->map( function ( $item ) {
|
||||
if ( ! Str::ends_with( $item['plugin'], '.php' ) ) {
|
||||
$item['plugin'] .= '.php';
|
||||
}
|
||||
return $item;
|
||||
} );
|
||||
|
||||
$slugs = $plugins_collection
|
||||
->map( function ( $item ) {
|
||||
return $item['plugin'];
|
||||
} )
|
||||
->all();
|
||||
|
||||
$installed = $this->plugins_manager->install( $slugs );
|
||||
$activated = $this->plugins_manager->activate( $installed['succeeded'] );
|
||||
|
||||
$ordered_activated_plugins = $plugins_collection
|
||||
->filter( function ( $item ) use ( $activated ) {
|
||||
return in_array( $item['plugin'], $activated['succeeded'], true );
|
||||
} )
|
||||
->map( function ( $item ) {
|
||||
return $item['name'];
|
||||
} )
|
||||
->all();
|
||||
|
||||
$result['plugins'] = $ordered_activated_plugins;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Settings\Page\Manager as PageManager;
|
||||
use Elementor\App\Modules\ImportExport\Utils;
|
||||
|
||||
class Site_Settings extends Import_Runner_Base {
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $previous_kit_id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $active_kit_id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $imported_kit_id;
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'site-settings';
|
||||
}
|
||||
|
||||
public function should_import( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'settings', $data['include'], true ) &&
|
||||
! empty( $data['site_settings']['settings'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function import( array $data, array $imported_data ) {
|
||||
$new_site_settings = $data['site_settings']['settings'];
|
||||
$title = $data['manifest']['title'] ?? 'Imported Kit';
|
||||
|
||||
$active_kit = Plugin::$instance->kits_manager->get_active_kit();
|
||||
|
||||
$this->active_kit_id = (int) $active_kit->get_id();
|
||||
$this->previous_kit_id = (int) Plugin::$instance->kits_manager->get_previous_id();
|
||||
|
||||
$result = [];
|
||||
|
||||
$old_settings = $active_kit->get_meta( PageManager::META_KEY );
|
||||
|
||||
if ( ! $old_settings ) {
|
||||
$old_settings = [];
|
||||
}
|
||||
|
||||
if ( ! empty( $old_settings['custom_colors'] ) ) {
|
||||
$new_site_settings['custom_colors'] = array_merge( $old_settings['custom_colors'], $new_site_settings['custom_colors'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $old_settings['custom_typography'] ) ) {
|
||||
$new_site_settings['custom_typography'] = array_merge( $old_settings['custom_typography'], $new_site_settings['custom_typography'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $new_site_settings['space_between_widgets'] ) ) {
|
||||
$new_site_settings['space_between_widgets'] = Utils::update_space_between_widgets_values( $new_site_settings['space_between_widgets'] );
|
||||
}
|
||||
|
||||
$new_site_settings = array_replace_recursive( $old_settings, $new_site_settings );
|
||||
|
||||
$new_kit = Plugin::$instance->kits_manager->create_new_kit( $title, $new_site_settings );
|
||||
|
||||
$this->imported_kit_id = (int) $new_kit;
|
||||
|
||||
$result['site-settings'] = (bool) $new_kit;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function get_import_session_metadata() : array {
|
||||
return [
|
||||
'previous_kit_id' => $this->previous_kit_id,
|
||||
'active_kit_id' => $this->active_kit_id,
|
||||
'imported_kit_id' => $this->imported_kit_id,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
|
||||
class Taxonomies extends Import_Runner_Base {
|
||||
|
||||
private $import_session_id;
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'taxonomies';
|
||||
}
|
||||
|
||||
public function should_import( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'content', $data['include'], true ) &&
|
||||
! empty( $data['extracted_directory_path'] ) &&
|
||||
! empty( $data['manifest']['taxonomies'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function import( array $data, array $imported_data ) {
|
||||
$path = $data['extracted_directory_path'] . 'taxonomies/';
|
||||
$this->import_session_id = $data['session_id'];
|
||||
|
||||
$wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
$selected_custom_post_types = isset( $data['selected_custom_post_types'] ) ? $data['selected_custom_post_types'] : [];
|
||||
$post_types = array_merge( $wp_builtin_post_types, $selected_custom_post_types );
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
if ( empty( $data['manifest']['taxonomies'][ $post_type ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result['taxonomies'][ $post_type ] = $this->import_taxonomies( $data['manifest']['taxonomies'][ $post_type ], $path );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function import_taxonomies( array $taxonomies, $path ) {
|
||||
$result = [];
|
||||
$imported_taxonomies = [];
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
if ( ! taxonomy_exists( $taxonomy ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $imported_taxonomies[ $taxonomy ] ) ) {
|
||||
$result[ $taxonomy ] = $imported_taxonomies[ $taxonomy ];
|
||||
continue;
|
||||
}
|
||||
|
||||
$taxonomy_data = ImportExportUtils::read_json_file( $path . $taxonomy );
|
||||
if ( empty( $taxonomy_data ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$import = $this->import_taxonomy( $taxonomy_data );
|
||||
$result[ $taxonomy ] = $import;
|
||||
$imported_taxonomies[ $taxonomy ] = $import;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function import_taxonomy( array $taxonomy_data ) {
|
||||
$terms = [];
|
||||
|
||||
foreach ( $taxonomy_data as $term ) {
|
||||
$old_slug = $term['slug'];
|
||||
|
||||
$existing_term = term_exists( $term['slug'], $term['taxonomy'] );
|
||||
if ( $existing_term ) {
|
||||
if ( 'nav_menu' === $term['taxonomy'] ) {
|
||||
$term = $this->handle_duplicated_nav_menu_term( $term );
|
||||
} else {
|
||||
$terms[] = [
|
||||
'old_id' => (int) $term['term_id'],
|
||||
'new_id' => (int) $existing_term['term_id'],
|
||||
'old_slug' => $old_slug,
|
||||
'new_slug' => $term['slug'],
|
||||
];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$parent = $this->get_term_parent( $term, $terms );
|
||||
|
||||
$args = [
|
||||
'slug' => $term['slug'],
|
||||
'description' => wp_slash( $term['description'] ),
|
||||
'parent' => (int) $parent,
|
||||
];
|
||||
|
||||
$new_term = wp_insert_term( wp_slash( $term['name'] ), $term['taxonomy'], $args );
|
||||
if ( ! is_wp_error( $new_term ) ) {
|
||||
$this->set_session_term_meta( (int) $new_term['term_id'], $this->import_session_id );
|
||||
|
||||
$terms[] = [
|
||||
'old_id' => $term['term_id'],
|
||||
'new_id' => (int) $new_term['term_id'],
|
||||
'old_slug' => $old_slug,
|
||||
'new_slug' => $term['slug'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $terms;
|
||||
}
|
||||
|
||||
private function handle_duplicated_nav_menu_term( $term ) {
|
||||
do {
|
||||
$term['slug'] = $term['slug'] . '-duplicate';
|
||||
$term['name'] = $term['name'] . ' duplicate';
|
||||
} while ( term_exists( $term['slug'], 'nav_menu' ) );
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
private function get_term_parent( $term, array $imported_terms ) {
|
||||
$parent = $term['parent'];
|
||||
if ( 0 !== $parent && ! empty( $imported_terms ) ) {
|
||||
foreach ( $imported_terms as $imported_term ) {
|
||||
if ( $parent === $imported_term['old_id'] ) {
|
||||
$parent_term = term_exists( $imported_term['new_id'], $term['taxonomy'] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $parent_term['term_id'] ) ) {
|
||||
return $parent_term['term_id'];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Utils;
|
||||
|
||||
class Templates extends Import_Runner_Base {
|
||||
private $import_session_id;
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'templates';
|
||||
}
|
||||
|
||||
public function should_import( array $data ) {
|
||||
return (
|
||||
Utils::has_pro() &&
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'templates', $data['include'], true ) &&
|
||||
! empty( $data['extracted_directory_path'] ) &&
|
||||
! empty( $data['manifest']['templates'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function import( array $data, array $imported_data ) {
|
||||
$this->import_session_id = $data['session_id'];
|
||||
|
||||
$path = $data['extracted_directory_path'] . 'templates/';
|
||||
$templates = $data['manifest']['templates'];
|
||||
|
||||
$result['templates'] = [
|
||||
'succeed' => [],
|
||||
'failed' => [],
|
||||
];
|
||||
|
||||
foreach ( $templates as $id => $template_settings ) {
|
||||
try {
|
||||
$template_data = ImportExportUtils::read_json_file( $path . $id );
|
||||
$import = $this->import_template( $id, $template_settings, $template_data );
|
||||
|
||||
$result['templates']['succeed'][ $id ] = $import;
|
||||
} catch ( \Exception $error ) {
|
||||
$result['templates']['failed'][ $id ] = $error->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function import_template( $id, array $template_settings, array $template_data ) {
|
||||
$doc_type = $template_settings['doc_type'];
|
||||
|
||||
$new_document = Plugin::$instance->documents->create(
|
||||
$doc_type,
|
||||
[
|
||||
'post_title' => $template_settings['title'],
|
||||
'post_type' => Source_Local::CPT,
|
||||
'post_status' => 'publish',
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $new_document ) ) {
|
||||
throw new \Exception( $new_document->get_error_message() );
|
||||
}
|
||||
|
||||
$template_data['import_settings'] = $template_settings;
|
||||
$template_data['id'] = $id;
|
||||
|
||||
$new_attachment_callback = function( $attachment_id ) {
|
||||
$this->set_session_post_meta( $attachment_id, $this->import_session_id );
|
||||
};
|
||||
|
||||
add_filter( 'elementor/template_library/import_images/new_attachment', $new_attachment_callback );
|
||||
|
||||
$new_document->import( $template_data );
|
||||
|
||||
remove_filter( 'elementor/template_library/import_images/new_attachment', $new_attachment_callback );
|
||||
|
||||
$document_id = $new_document->get_main_id();
|
||||
|
||||
$this->set_session_post_meta( $document_id, $this->import_session_id );
|
||||
|
||||
return $document_id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Import;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Core\Utils\ImportExport\WP_Import;
|
||||
|
||||
class Wp_Content extends Import_Runner_Base {
|
||||
|
||||
private $import_session_id;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $selected_custom_post_types = [];
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'wp-content';
|
||||
}
|
||||
|
||||
public function should_import( array $data ) {
|
||||
return (
|
||||
isset( $data['include'] ) &&
|
||||
in_array( 'content', $data['include'], true ) &&
|
||||
! empty( $data['extracted_directory_path'] ) &&
|
||||
! empty( $data['manifest']['wp-content'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function import( array $data, array $imported_data ) {
|
||||
$this->import_session_id = $data['session_id'];
|
||||
|
||||
$path = $data['extracted_directory_path'] . 'wp-content/';
|
||||
|
||||
$post_types = $this->filter_post_types( $data['selected_custom_post_types'] );
|
||||
|
||||
$taxonomies = $imported_data['taxonomies'] ?? [];
|
||||
$imported_terms = ImportExportUtils::map_old_new_term_ids( $imported_data );
|
||||
|
||||
$result['wp-content'] = [];
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$import = $this->import_wp_post_type(
|
||||
$path,
|
||||
$post_type,
|
||||
$imported_data,
|
||||
$taxonomies,
|
||||
$imported_terms
|
||||
);
|
||||
|
||||
if ( empty( $import ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result['wp-content'][ $post_type ] = $import;
|
||||
$imported_data = array_merge( $imported_data, $result );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function import_wp_post_type( $path, $post_type, array $imported_data, array $taxonomies, array $imported_terms ) {
|
||||
$args = [
|
||||
'fetch_attachments' => true,
|
||||
'posts' => ImportExportUtils::map_old_new_post_ids( $imported_data ),
|
||||
'terms' => $imported_terms,
|
||||
'taxonomies' => ! empty( $taxonomies[ $post_type ] ) ? $taxonomies[ $post_type ] : [],
|
||||
'posts_meta' => [
|
||||
static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID => $this->import_session_id,
|
||||
],
|
||||
'terms_meta' => [
|
||||
static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID => $this->import_session_id,
|
||||
],
|
||||
];
|
||||
|
||||
$file = $path . $post_type . '/' . $post_type . '.xml';
|
||||
|
||||
if ( ! file_exists( $file ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$wp_importer = new WP_Import( $file, $args );
|
||||
$result = $wp_importer->run();
|
||||
|
||||
return $result['summary']['posts'];
|
||||
}
|
||||
|
||||
private function filter_post_types( $selected_custom_post_types = [] ) {
|
||||
$wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
|
||||
foreach ( $selected_custom_post_types as $custom_post_type ) {
|
||||
if ( post_type_exists( $custom_post_type ) ) {
|
||||
$this->selected_custom_post_types[] = $custom_post_type;
|
||||
}
|
||||
}
|
||||
|
||||
$post_types = array_merge( $wp_builtin_post_types, $this->selected_custom_post_types );
|
||||
$post_types = $this->force_element_to_be_last_by_value( $post_types, 'nav_menu_item' );
|
||||
|
||||
return $post_types;
|
||||
}
|
||||
|
||||
public function get_import_session_metadata() : array {
|
||||
return [
|
||||
'custom_post_types' => $this->selected_custom_post_types,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array array The array we want to relocate his element.
|
||||
* @param $element mixed The value of the element in the array we want to shift to end of the array.
|
||||
* @return mixed
|
||||
*/
|
||||
private function force_element_to_be_last_by_value( array $array, $element ) {
|
||||
$index = array_search( $element, $array, true );
|
||||
|
||||
if ( false !== $index ) {
|
||||
unset( $array[ $index ] );
|
||||
$array[] = $element;
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Elementor_Content extends Revert_Runner_Base {
|
||||
private $show_page_on_front;
|
||||
|
||||
private $page_on_front_id;
|
||||
|
||||
public function __construct() {
|
||||
$this->init_page_on_front_data();
|
||||
}
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'elementor-content';
|
||||
}
|
||||
|
||||
public function should_revert( array $data ) : bool {
|
||||
return (
|
||||
isset( $data['runners'] ) &&
|
||||
array_key_exists( static::get_name(), $data['runners'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function revert( array $data ) {
|
||||
$elementor_post_types = ImportExportUtils::get_elementor_post_types();
|
||||
|
||||
$query_args = [
|
||||
'post_type' => $elementor_post_types,
|
||||
'post_status' => 'any',
|
||||
'posts_per_page' => -1,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_EDIT_MODE,
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID,
|
||||
'value' => $data['session_id'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$query = new \WP_Query( $query_args );
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
$post_type_document = Plugin::$instance->documents->get( $post->ID );
|
||||
$post_type_document->delete();
|
||||
|
||||
// Deleting the post will reset the show_on_front option. We need to set it to false,
|
||||
// so we can set it back to what it was.
|
||||
if ( $post->ID === $this->page_on_front_id ) {
|
||||
$this->show_page_on_front = false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->restore_page_on_front( $data );
|
||||
}
|
||||
|
||||
private function init_page_on_front_data() {
|
||||
$this->show_page_on_front = 'page' === get_option( 'show_on_front' );
|
||||
|
||||
if ( $this->show_page_on_front ) {
|
||||
$this->page_on_front_id = (int) get_option( 'page_on_front' );
|
||||
}
|
||||
}
|
||||
|
||||
private function restore_page_on_front( $data ) {
|
||||
if ( empty( $data['runners'][ static::get_name() ]['page_on_front'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page_on_front = $data['runners'][ static::get_name() ]['page_on_front'];
|
||||
|
||||
$document = Plugin::$instance->documents->get( $page_on_front );
|
||||
|
||||
if ( ! $document ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_page_on_front( $document->get_main_id() );
|
||||
}
|
||||
|
||||
private function set_page_on_front( $page_id ) {
|
||||
update_option( 'page_on_front', $page_id );
|
||||
|
||||
if ( ! $this->show_page_on_front ) {
|
||||
update_option( 'show_on_front', 'page' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
class Plugins extends Revert_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'plugins';
|
||||
}
|
||||
|
||||
public function should_revert( array $data ) : bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function revert( array $data ) {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Runners\Runner_Interface;
|
||||
|
||||
abstract class Revert_Runner_Base implements Runner_Interface {
|
||||
|
||||
/**
|
||||
* By the passed data we should decide if we want to run the revert function of the runner or not.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function should_revert( array $data ) : bool;
|
||||
|
||||
/**
|
||||
* Main function of the runner revert process.
|
||||
*
|
||||
* @param array $data Necessary data for the revert process.
|
||||
*/
|
||||
abstract public function revert( array $data );
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Site_Settings extends Revert_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'site-settings';
|
||||
}
|
||||
|
||||
public function should_revert( array $data ) : bool {
|
||||
return (
|
||||
isset( $data['runners'] ) &&
|
||||
array_key_exists( static::get_name(), $data['runners'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function revert( array $data ) {
|
||||
Plugin::$instance->kits_manager->revert(
|
||||
$data['runners'][ static::get_name() ]['imported_kit_id'],
|
||||
$data['runners'][ static::get_name() ]['active_kit_id'],
|
||||
$data['runners'][ static::get_name() ]['previous_kit_id']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
class Taxonomies extends Revert_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'taxonomies';
|
||||
}
|
||||
|
||||
public function should_revert( array $data ) : bool {
|
||||
return (
|
||||
isset( $data['runners'] ) &&
|
||||
array_key_exists( static::get_name(), $data['runners'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function revert( array $data ) {
|
||||
$taxonomies = get_taxonomies();
|
||||
|
||||
$terms = get_terms( [
|
||||
'taxonomy' => $taxonomies,
|
||||
'hide_empty' => false,
|
||||
'get' => 'all',
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID,
|
||||
'value' => $data['session_id'],
|
||||
],
|
||||
],
|
||||
] );
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
wp_delete_term( $term->term_id, $term->taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
class Templates extends Revert_Runner_Base {
|
||||
/*
|
||||
* The implement of this runner is part of the Pro plugin.
|
||||
*/
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'templates';
|
||||
}
|
||||
|
||||
public function should_revert( array $data ) : bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function revert( array $data ) { }
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners\Revert;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Utils as ImportExportUtils;
|
||||
|
||||
class Wp_Content extends Revert_Runner_Base {
|
||||
|
||||
public static function get_name() : string {
|
||||
return 'wp-content';
|
||||
}
|
||||
|
||||
public function should_revert( array $data ) : bool {
|
||||
return (
|
||||
isset( $data['runners'] ) &&
|
||||
array_key_exists( static::get_name(), $data['runners'] )
|
||||
);
|
||||
}
|
||||
|
||||
public function revert( array $data ) {
|
||||
$builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
$custom_post_types = $data['runners']['wp-content']['custom_post_types'] ?? [];
|
||||
|
||||
$post_types = array_merge( $builtin_post_types, $custom_post_types );
|
||||
|
||||
$query_args = [
|
||||
'post_type' => $post_types,
|
||||
'post_status' => 'any',
|
||||
'posts_per_page' => -1,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_EDIT_MODE,
|
||||
'compare' => 'NOT EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID,
|
||||
'value' => $data['session_id'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$query = new \WP_Query( $query_args );
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
wp_delete_post( $post->ID, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the nav menu terms.
|
||||
* BC: The nav menu in new kits will be imported as part of the taxonomies, but old kits
|
||||
* importing the nav menu terms as part from the wp-content import.
|
||||
*/
|
||||
$this->revert_nav_menus( $data );
|
||||
}
|
||||
|
||||
private function revert_nav_menus( array $data ) {
|
||||
$terms = get_terms( [
|
||||
'taxonomy' => 'nav_menu',
|
||||
'hide_empty' => false,
|
||||
'get' => 'all',
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => static::META_KEY_ELEMENTOR_IMPORT_SESSION_ID,
|
||||
'value' => $data['session_id'],
|
||||
],
|
||||
],
|
||||
] );
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
wp_delete_term( $term->term_id, $term->taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport\Runners;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Module;
|
||||
|
||||
interface Runner_Interface {
|
||||
|
||||
const META_KEY_ELEMENTOR_IMPORT_SESSION_ID = Module::META_KEY_ELEMENTOR_IMPORT_SESSION_ID;
|
||||
|
||||
const META_KEY_ELEMENTOR_EDIT_MODE = Module::META_KEY_ELEMENTOR_EDIT_MODE;
|
||||
|
||||
/**
|
||||
* Get the name of the runners, used to identify the runner.
|
||||
* The name should be unique, unless you want to run over existing runner.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() : string;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Processes\Revert;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Usage {
|
||||
|
||||
/**
|
||||
* Register hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
add_filter( 'elementor/tracker/send_tracking_data_params', function ( array $params ) {
|
||||
$params['usages']['import_export']['revert'] = $this->get_revert_usage_data();
|
||||
|
||||
return $params;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Revert usage data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_revert_usage_data() {
|
||||
$revert_sessions = ( new Revert() )->get_revert_sessions();
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ( $revert_sessions as $revert_session ) {
|
||||
$data[] = [
|
||||
'kit_name' => $revert_session['kit_name'],
|
||||
'source' => $revert_session['source'],
|
||||
'revert_timestamp' => (int) $revert_session['revert_timestamp'],
|
||||
'total_time' => ( (int) $revert_session['revert_timestamp'] - (int) $revert_session['import_timestamp'] ),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
140
wp-content/plugins/elementor/app/modules/import-export/utils.php
Normal file
140
wp-content/plugins/elementor/app/modules/import-export/utils.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport;
|
||||
|
||||
use Elementor\Core\Utils\Str;
|
||||
use Elementor\Modules\LandingPages\Module as Landing_Pages_Module;
|
||||
use \Elementor\Modules\FloatingButtons\Module as Floating_Buttons_Module;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Utils as ElementorUtils;
|
||||
|
||||
class Utils {
|
||||
|
||||
public static function read_json_file( $path ) {
|
||||
if ( ! Str::ends_with( $path, '.json' ) ) {
|
||||
$path .= '.json';
|
||||
}
|
||||
|
||||
$file_content = ElementorUtils::file_get_contents( $path, true );
|
||||
|
||||
return $file_content ? json_decode( $file_content, true ) : [];
|
||||
}
|
||||
|
||||
public static function map_old_new_post_ids( array $imported_data ) {
|
||||
$result = [];
|
||||
|
||||
$result += $imported_data['templates']['succeed'] ?? [];
|
||||
|
||||
if ( isset( $imported_data['content'] ) ) {
|
||||
foreach ( $imported_data['content'] as $post_type ) {
|
||||
$result += $post_type['succeed'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $imported_data['wp-content'] ) ) {
|
||||
foreach ( $imported_data['wp-content'] as $post_type ) {
|
||||
$result += $post_type['succeed'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function map_old_new_term_ids( array $imported_data ) {
|
||||
$result = [];
|
||||
|
||||
if ( ! isset( $imported_data['taxonomies'] ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ( $imported_data['taxonomies'] as $post_type_taxonomies ) {
|
||||
foreach ( $post_type_taxonomies as $taxonomy ) {
|
||||
foreach ( $taxonomy as $term ) {
|
||||
$result[ $term['old_id'] ] = $term['new_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function get_elementor_post_types() {
|
||||
$elementor_post_types = get_post_types_by_support( 'elementor' );
|
||||
|
||||
return array_filter( $elementor_post_types, function ( $value ) {
|
||||
// Templates are handled in a separate process.
|
||||
return 'elementor_library' !== $value;
|
||||
} );
|
||||
}
|
||||
|
||||
public static function get_builtin_wp_post_types() {
|
||||
return [ 'post', 'page', 'nav_menu_item' ];
|
||||
}
|
||||
|
||||
public static function get_registered_cpt_names() {
|
||||
$post_types = get_post_types( [
|
||||
'public' => true,
|
||||
'can_export' => true,
|
||||
'_builtin' => false,
|
||||
] );
|
||||
|
||||
unset(
|
||||
$post_types[ Landing_Pages_Module::CPT ],
|
||||
$post_types[ Source_Local::CPT ],
|
||||
$post_types[ Floating_Buttons_Module::CPT_FLOATING_BUTTONS ]
|
||||
);
|
||||
|
||||
return array_keys( $post_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a string name to title format.
|
||||
*
|
||||
* @param $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function transform_name_to_title( $name ): string {
|
||||
if ( empty( $name ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$title = str_replace( [ '-', '_' ], ' ', $name );
|
||||
|
||||
return ucwords( $title );
|
||||
}
|
||||
|
||||
public static function get_import_sessions( $should_run_cleanup = false ) {
|
||||
$import_sessions = get_option( Module::OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS, [] );
|
||||
|
||||
if ( $should_run_cleanup ) {
|
||||
foreach ( $import_sessions as $session_id => $import_session ) {
|
||||
if ( ! isset( $import_session['runners'] ) && isset( $import_session['instance_data'] ) ) {
|
||||
$import_sessions[ $session_id ]['runners'] = $import_session['instance_data']['runners_import_metadata'] ?? [];
|
||||
|
||||
unset( $import_sessions[ $session_id ]['instance_data'] );
|
||||
}
|
||||
}
|
||||
|
||||
update_option( Module::OPTION_KEY_ELEMENTOR_IMPORT_SESSIONS, $import_sessions );
|
||||
}
|
||||
|
||||
return $import_sessions;
|
||||
}
|
||||
|
||||
public static function update_space_between_widgets_values( $space_between_widgets ) {
|
||||
$setting_exist = isset( $space_between_widgets['size'] );
|
||||
$already_processed = isset( $space_between_widgets['column'] );
|
||||
|
||||
if ( ! $setting_exist || $already_processed ) {
|
||||
return $space_between_widgets;
|
||||
}
|
||||
|
||||
$size = strval( $space_between_widgets['size'] );
|
||||
$space_between_widgets['column'] = $size;
|
||||
$space_between_widgets['row'] = $size;
|
||||
$space_between_widgets['isLinked'] = true;
|
||||
|
||||
return $space_between_widgets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\App\Modules\ImportExport;
|
||||
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Core\Utils\Plugins_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\App\Modules\KitLibrary\Connect\Kit_Library;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Wp_Cli extends \WP_CLI_Command {
|
||||
|
||||
const AVAILABLE_SETTINGS = [ 'include', 'overrideConditions', 'selectedCustomPostTypes', 'plugins' ];
|
||||
|
||||
/**
|
||||
* Export a Kit
|
||||
*
|
||||
* [--include]
|
||||
* Which type of content to include. Possible values are 'content', 'templates', 'site-settings'.
|
||||
* if this parameter won't be specified, All data types will be included.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* 1. wp elementor kit export path/to/export-file-name.zip
|
||||
* - This will export all site data to the specified file name.
|
||||
*
|
||||
* 2. wp elementor kit export path/to/export-file-name.zip --include=kit-settings,content
|
||||
* - This will export only site settings and content.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array $assoc_args
|
||||
*/
|
||||
public function export( $args, $assoc_args ) {
|
||||
if ( empty( $args[0] ) ) {
|
||||
\WP_CLI::error( 'Please specify a file name' );
|
||||
}
|
||||
|
||||
\WP_CLI::line( 'Kit export started.' );
|
||||
|
||||
$export_settings = [];
|
||||
foreach ( $assoc_args as $key => $value ) {
|
||||
if ( ! in_array( $key, static::AVAILABLE_SETTINGS, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$export_settings[ $key ] = explode( ',', $value );
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Running the export process through the import-export module so the export property in the module will be available to use.
|
||||
*
|
||||
* @type Module $import_export_module
|
||||
*/
|
||||
$import_export_module = Plugin::$instance->app->get_component( 'import-export' );
|
||||
$result = $import_export_module->export_kit( $export_settings );
|
||||
|
||||
rename( $result['file_name'], $args[0] );
|
||||
} catch ( \Error $error ) {
|
||||
\WP_CLI::error( $error->getMessage() );
|
||||
}
|
||||
|
||||
\WP_CLI::success( 'Kit exported successfully.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Kit
|
||||
*
|
||||
* [--include]
|
||||
* Which type of content to include. Possible values are 'content', 'templates', 'site-settings'.
|
||||
* if this parameter won't be specified, All data types will be included.
|
||||
*
|
||||
* [--overrideConditions]
|
||||
* Templates ids to override conditions for.
|
||||
*
|
||||
* [--sourceType]
|
||||
* Which source type is used in the current session. Available values are 'local', 'remote', 'library'.
|
||||
* The default value is 'local'
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* 1. wp elementor kit import path/to/elementor-kit.zip
|
||||
* - This will import the whole kit file content.
|
||||
*
|
||||
* 2. wp elementor kit import path/to/elementor-kit.zip --include=site-settings,content
|
||||
* - This will import only site settings and content.
|
||||
*
|
||||
* 3. wp elementor kit import path/to/elementor-kit.zip --overrideConditions=3478,4520
|
||||
* - This will import all content and will override conditions for the given template ids.
|
||||
*
|
||||
* 4. wp elementor kit import path/to/elementor-kit.zip --unfilteredFilesUpload=enable
|
||||
* - This will allow the import process to import unfiltered files.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array $assoc_args
|
||||
*/
|
||||
public function import( array $args, array $assoc_args ) {
|
||||
if ( ! current_user_can( 'administrator' ) ) {
|
||||
\WP_CLI::error( 'You must run this command as an admin user' );
|
||||
}
|
||||
|
||||
if ( empty( $args[0] ) ) {
|
||||
\WP_CLI::error( 'Please specify a file to import' );
|
||||
}
|
||||
|
||||
\WP_CLI::line( 'Kit import started' );
|
||||
|
||||
$assoc_args = wp_parse_args( $assoc_args, [
|
||||
'sourceType' => 'local',
|
||||
] );
|
||||
|
||||
$url = null;
|
||||
$file_path = $args[0];
|
||||
$import_settings = [];
|
||||
$import_settings['referrer'] = Module::REFERRER_LOCAL;
|
||||
|
||||
switch ( $assoc_args['sourceType'] ) {
|
||||
case 'library':
|
||||
$url = $this->get_url_from_library( $file_path );
|
||||
$zip_path = $this->create_temp_file_from_url( $url );
|
||||
$import_settings['referrer'] = Module::REFERRER_KIT_LIBRARY;
|
||||
break;
|
||||
|
||||
case 'remote':
|
||||
$zip_path = $this->create_temp_file_from_url( $file_path );
|
||||
break;
|
||||
|
||||
case 'local':
|
||||
$zip_path = $file_path;
|
||||
break;
|
||||
|
||||
default:
|
||||
\WP_CLI::error( 'Unknown source type.' );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( 'enable' === $assoc_args['unfilteredFilesUpload'] ) {
|
||||
Plugin::$instance->uploads_manager->enable_unfiltered_files_upload();
|
||||
}
|
||||
|
||||
foreach ( $assoc_args as $key => $value ) {
|
||||
if ( ! in_array( $key, static::AVAILABLE_SETTINGS, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$import_settings[ $key ] = explode( ',', $value );
|
||||
}
|
||||
|
||||
try {
|
||||
\WP_CLI::line( 'Importing data...' );
|
||||
|
||||
/**
|
||||
* Running the import process through the import-export module so the import property in the module will be available to use.
|
||||
*
|
||||
* @type Module $import_export_module
|
||||
*/
|
||||
$import_export_module = Plugin::$instance->app->get_component( 'import-export' );
|
||||
|
||||
if ( ! $import_export_module ) {
|
||||
\WP_CLI::error( 'Import Export module is not available.' );
|
||||
}
|
||||
|
||||
$import = $import_export_module->import_kit( $zip_path, $import_settings );
|
||||
|
||||
$manifest_data = $import_export_module->import->get_manifest();
|
||||
|
||||
/**
|
||||
* Import Export Manifest Data
|
||||
*
|
||||
* Allows 3rd parties to read and edit the kit's manifest before it is used.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param array $manifest_data The Kit's Manifest data
|
||||
*/
|
||||
$manifest_data = apply_filters( 'elementor/import-export/wp-cli/manifest_data', $manifest_data );
|
||||
|
||||
\WP_CLI::line( 'Removing temp files...' );
|
||||
|
||||
// The file was created from remote or library request, it also should be removed.
|
||||
if ( $url ) {
|
||||
Plugin::$instance->uploads_manager->remove_file_or_dir( dirname( $zip_path ) );
|
||||
}
|
||||
|
||||
\WP_CLI::success( 'Kit imported successfully' );
|
||||
} catch ( \Error $error ) {
|
||||
Plugin::$instance->logger->get_logger()->error( $error->getMessage(), [
|
||||
'meta' => [
|
||||
'trace' => $error->getTraceAsString(),
|
||||
],
|
||||
] );
|
||||
|
||||
if ( $url ) {
|
||||
Plugin::$instance->uploads_manager->remove_file_or_dir( dirname( $zip_path ) );
|
||||
}
|
||||
|
||||
\WP_CLI::error( $error->getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert last imported kit.
|
||||
*/
|
||||
public function revert() {
|
||||
\WP_CLI::line( 'Kit revert started.' );
|
||||
|
||||
try {
|
||||
/**
|
||||
* Running the revert process through the import-export module so the revert property in the module will be available to use.
|
||||
*
|
||||
* @type Module $import_export_module
|
||||
*/
|
||||
$import_export_module = Plugin::$instance->app->get_component( 'import-export' );
|
||||
$import_export_module->revert_last_imported_kit();
|
||||
|
||||
} catch ( \Error $error ) {
|
||||
\WP_CLI::error( $error->getMessage() );
|
||||
}
|
||||
|
||||
\WP_CLI::success( 'Kit reverted successfully.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get kit url by the kit id
|
||||
* TODO: Maybe extract it.
|
||||
*
|
||||
* @param $kit_id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_url_from_library( $kit_id ) {
|
||||
/** @var Kit_Library $app */
|
||||
$app = Plugin::$instance->common->get_component( 'connect' )->get_app( 'kit-library' );
|
||||
|
||||
if ( ! $app ) {
|
||||
\WP_CLI::error( 'Kit library app not found' );
|
||||
}
|
||||
|
||||
$response = $app->download_link( $kit_id );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
\WP_CLI::error( "Library Response: {$response->get_error_message()}" );
|
||||
}
|
||||
|
||||
return $response->download_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get kit zip file path by the kit url
|
||||
* TODO: Maybe extract it.
|
||||
*
|
||||
* @param $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function create_temp_file_from_url( $url ) {
|
||||
\WP_CLI::line( 'Extracting zip archive...' );
|
||||
$response = wp_remote_get( $url );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
\WP_CLI::error( "Download file url: {$response->get_error_message()}" );
|
||||
}
|
||||
|
||||
if ( 200 !== $response['response']['code'] ) {
|
||||
\WP_CLI::error( "Download file url: {$response['response']['message']}" );
|
||||
}
|
||||
|
||||
// Set the Request's state as an Elementor upload request, in order to support unfiltered file uploads.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
|
||||
|
||||
$file = Plugin::$instance->uploads_manager->create_temp_file( $response['body'], 'kit.zip' );
|
||||
|
||||
// After the upload complete, set the elementor upload state back to false.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( false );
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Connect;
|
||||
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Kit_Library extends Library {
|
||||
const DEFAULT_BASE_ENDPOINT = 'https://my.elementor.com/api/v1/kits-library';
|
||||
const FALLBACK_BASE_ENDPOINT = 'https://ms-8874.elementor.com/api/v1/kits-library';
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Kit Library', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_all( $args = [] ) {
|
||||
return $this->http_request( 'GET', 'kits/plugin-version/' . ELEMENTOR_VERSION, $args );
|
||||
}
|
||||
|
||||
public function get_by_id( $id ) {
|
||||
return $this->http_request( 'GET', 'kits/' . $id );
|
||||
}
|
||||
|
||||
public function get_taxonomies() {
|
||||
return $this->http_request( 'GET', 'taxonomies' );
|
||||
}
|
||||
|
||||
public function get_manifest( $id ) {
|
||||
return $this->http_request( 'GET', "kits/{$id}/manifest" );
|
||||
}
|
||||
|
||||
public function download_link( $id ) {
|
||||
return $this->http_request( 'GET', "kits/{$id}/download-link" );
|
||||
}
|
||||
|
||||
protected function get_api_url() {
|
||||
return [
|
||||
static::DEFAULT_BASE_ENDPOINT,
|
||||
static::FALLBACK_BASE_ENDPOINT,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the connect information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_connect_info() {
|
||||
$connect_info = $this->get_base_connect_info();
|
||||
|
||||
$additional_info = [];
|
||||
|
||||
// BC Support.
|
||||
$old_kit_library = new \Elementor\Core\App\Modules\KitLibrary\Connect\Kit_Library();
|
||||
|
||||
/**
|
||||
* Additional connect info.
|
||||
*
|
||||
* Filters the connection information when connecting to Elementor servers.
|
||||
* This hook can be used to add more information or add more data.
|
||||
*
|
||||
* @param array $additional_info Additional connecting information array.
|
||||
* @param Base_App $this The base app instance.
|
||||
*/
|
||||
$additional_info = apply_filters( 'elementor/connect/additional-connect-info', $additional_info, $old_kit_library );
|
||||
|
||||
return array_merge( $connect_info, $additional_info );
|
||||
}
|
||||
|
||||
protected function init() {
|
||||
// Remove parent init actions.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Data;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Data\V2\Base\Controller;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Modules\Library\User_Favorites;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Base_Controller extends Controller {
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @return Repository
|
||||
*/
|
||||
public function get_repository() {
|
||||
if ( ! $this->repository ) {
|
||||
/** @var \Elementor\Core\Common\Modules\Connect\Module $connect */
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
|
||||
$subscription_plans = ( new Collection( $connect->get_subscription_plans() ) )
|
||||
->map( function ( $value ) {
|
||||
return $value['label'];
|
||||
} );
|
||||
|
||||
$this->repository = new Repository(
|
||||
$connect->get_app( 'kit-library' ),
|
||||
new User_Favorites( get_current_user_id() ),
|
||||
$subscription_plans
|
||||
);
|
||||
}
|
||||
|
||||
return $this->repository;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Data\Kits;
|
||||
|
||||
use Elementor\App\Modules\KitLibrary\Data\Base_Controller;
|
||||
use Elementor\Data\V2\Base\Exceptions\Error_404;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Controller extends Base_Controller {
|
||||
|
||||
public function get_name() {
|
||||
return 'kits';
|
||||
}
|
||||
|
||||
public function get_items( $request ) {
|
||||
$data = $this->get_repository()->get_all( $request->get_param( 'force' ) );
|
||||
|
||||
return [
|
||||
'data' => $data->values(),
|
||||
];
|
||||
}
|
||||
|
||||
public function get_item( $request ) {
|
||||
$data = $this->get_repository()->find( $request->get_param( 'id' ) );
|
||||
|
||||
if ( ! $data ) {
|
||||
return new Error_404( esc_html__( 'Kit not exists.', 'elementor' ), 'kit_not_exists' );
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_collection_params() {
|
||||
return [
|
||||
'force' => [
|
||||
'description' => 'Force an API request and skip the cache.',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'type' => 'boolean',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function register_endpoints() {
|
||||
$this->index_endpoint->register_item_route( \WP_REST_Server::READABLE, [
|
||||
'id' => [
|
||||
'description' => 'Unique identifier for the object.',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
],
|
||||
'id_arg_type_regex' => '[\w]+',
|
||||
] );
|
||||
|
||||
$this->register_endpoint( new Endpoints\Download_Link( $this ) );
|
||||
$this->register_endpoint( new Endpoints\Favorites( $this ) );
|
||||
}
|
||||
|
||||
public function get_permission_callback( $request ) {
|
||||
return current_user_can( 'administrator' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Data\Kits\Endpoints;
|
||||
|
||||
use Elementor\Data\V2\Base\Endpoint;
|
||||
use Elementor\App\Modules\KitLibrary\Data\Kits\Controller;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* @property Controller $controller
|
||||
*/
|
||||
class Download_Link extends Endpoint {
|
||||
public function get_name() {
|
||||
return 'download-link';
|
||||
}
|
||||
|
||||
public function get_format() {
|
||||
return 'kits/download-link/{id}';
|
||||
}
|
||||
|
||||
protected function register() {
|
||||
$this->register_item_route( \WP_REST_Server::READABLE, [
|
||||
'id_arg_type_regex' => '[\w]+',
|
||||
] );
|
||||
}
|
||||
|
||||
public function get_item( $id, $request ) {
|
||||
$repository = $this->controller->get_repository();
|
||||
$data = $repository->get_download_link( $id );
|
||||
|
||||
return [
|
||||
'data' => $data,
|
||||
'meta' => [
|
||||
'nonce' => wp_create_nonce( 'kit-library-import' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Data\Kits\Endpoints;
|
||||
|
||||
use Elementor\App\Modules\KitLibrary\Data\Kits\Controller;
|
||||
use Elementor\Data\V2\Base\Endpoint;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* @property Controller $controller
|
||||
*/
|
||||
class Favorites extends Endpoint {
|
||||
public function get_name() {
|
||||
return 'favorites';
|
||||
}
|
||||
|
||||
public function get_format() {
|
||||
return 'kits/favorites/{id}';
|
||||
}
|
||||
|
||||
protected function register() {
|
||||
$args = [
|
||||
'id_arg_type_regex' => '[\w]+',
|
||||
];
|
||||
|
||||
$this->register_item_route( \WP_REST_Server::CREATABLE, $args );
|
||||
$this->register_item_route( \WP_REST_Server::DELETABLE, $args );
|
||||
}
|
||||
|
||||
public function create_item( $id, $request ) {
|
||||
$repository = $this->controller->get_repository();
|
||||
$kit = $repository->add_to_favorites( $id );
|
||||
|
||||
return [
|
||||
'data' => $kit,
|
||||
];
|
||||
}
|
||||
|
||||
public function delete_item( $id, $request ) {
|
||||
$repository = $this->controller->get_repository();
|
||||
|
||||
$kit = $repository->remove_from_favorites( $id );
|
||||
|
||||
return [
|
||||
'data' => $kit,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Data;
|
||||
|
||||
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Data\V2\Base\Exceptions\Error_404;
|
||||
use Elementor\Data\V2\Base\Exceptions\WP_Error_Exception;
|
||||
use Elementor\Modules\Library\User_Favorites;
|
||||
use Elementor\App\Modules\KitLibrary\Connect\Kit_Library;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Repository {
|
||||
/**
|
||||
* There is no label for subscription plan with access_level=0 + it should not
|
||||
* be translated.
|
||||
*/
|
||||
const SUBSCRIPTION_PLAN_FREE_TAG = 'Free';
|
||||
|
||||
const TAXONOMIES_KEYS = [ 'tags', 'categories', 'main_category', 'third_category', 'features', 'types' ];
|
||||
|
||||
const KITS_CACHE_KEY = 'elementor_remote_kits';
|
||||
const KITS_TAXONOMIES_CACHE_KEY = 'elementor_remote_kits_taxonomies';
|
||||
|
||||
const KITS_CACHE_TTL_HOURS = 12;
|
||||
const KITS_TAXONOMIES_CACHE_TTL_HOURS = 12;
|
||||
|
||||
/**
|
||||
* @var Kit_Library
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var User_Favorites
|
||||
*/
|
||||
protected $user_favorites;
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*/
|
||||
protected $subscription_plans;
|
||||
|
||||
/**
|
||||
* Get all kits.
|
||||
*
|
||||
* @param false $force_api_request
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function get_all( $force_api_request = false ) {
|
||||
return $this->get_kits_data( $force_api_request )
|
||||
->map( function ( $kit ) {
|
||||
return $this->transform_kit_api_response( $kit );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific kit.
|
||||
*
|
||||
* @param $id
|
||||
* @param array $options
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function find( $id, $options = [] ) {
|
||||
$options = wp_parse_args( $options, [
|
||||
'manifest_included' => true,
|
||||
] );
|
||||
|
||||
$item = $this->get_kits_data()
|
||||
->find( function ( $kit ) use ( $id ) {
|
||||
return $kit->_id === $id;
|
||||
} );
|
||||
|
||||
if ( ! $item ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$manifest = null;
|
||||
|
||||
if ( $options['manifest_included'] ) {
|
||||
$manifest = $this->api->get_manifest( $id );
|
||||
|
||||
if ( is_wp_error( $manifest ) ) {
|
||||
throw new WP_Error_Exception( $manifest );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->transform_kit_api_response( $item, $manifest );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param false $force_api_request
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function get_taxonomies( $force_api_request = false ) {
|
||||
return $this->get_taxonomies_data( $force_api_request )
|
||||
->only( static::TAXONOMIES_KEYS )
|
||||
->reduce( function ( Collection $carry, $taxonomies, $type ) {
|
||||
return $carry->merge( array_map( function ( $taxonomy ) use ( $type ) {
|
||||
return [
|
||||
'text' => $taxonomy->name,
|
||||
'type' => $type,
|
||||
];
|
||||
}, $taxonomies ) );
|
||||
}, new Collection( [] ) )
|
||||
->merge(
|
||||
$this->subscription_plans->map( function ( $label ) {
|
||||
return [
|
||||
'text' => $label ? $label : self::SUBSCRIPTION_PLAN_FREE_TAG,
|
||||
'type' => 'subscription_plans',
|
||||
];
|
||||
} )
|
||||
)
|
||||
->unique( [ 'text', 'type' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_download_link( $id ) {
|
||||
$response = $this->api->download_link( $id );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
throw new WP_Error_Exception( $response );
|
||||
}
|
||||
|
||||
return [ 'download_link' => $response->download_link ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function add_to_favorites( $id ) {
|
||||
$kit = $this->find( $id, [ 'manifest_included' => false ] );
|
||||
|
||||
if ( ! $kit ) {
|
||||
throw new Error_404( esc_html__( 'Kit not found', 'elementor' ), 'kit_not_found' );
|
||||
}
|
||||
|
||||
$this->user_favorites->add( 'elementor', 'kits', $kit['id'] );
|
||||
|
||||
$kit['is_favorite'] = true;
|
||||
|
||||
return $kit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function remove_from_favorites( $id ) {
|
||||
$kit = $this->find( $id, [ 'manifest_included' => false ] );
|
||||
|
||||
if ( ! $kit ) {
|
||||
throw new Error_404( esc_html__( 'Kit not found', 'elementor' ), 'kit_not_found' );
|
||||
}
|
||||
|
||||
$this->user_favorites->remove( 'elementor', 'kits', $kit['id'] );
|
||||
|
||||
$kit['is_favorite'] = false;
|
||||
|
||||
return $kit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force_api_request
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function get_kits_data( $force_api_request = false ) {
|
||||
$data = get_transient( static::KITS_CACHE_KEY );
|
||||
|
||||
$experiments_manager = Plugin::$instance->experiments;
|
||||
$kits_editor_layout_type = $experiments_manager->is_feature_active( 'container' ) ? 'container_flexbox' : '';
|
||||
|
||||
if ( ! $data || $force_api_request ) {
|
||||
$args = [
|
||||
'body' => [
|
||||
'editor_layout_type' => $kits_editor_layout_type,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Filters arguments for the request to the Kits API.
|
||||
*
|
||||
* @since 3.11.0
|
||||
*
|
||||
* @param array[] $args Array of http arguments.
|
||||
*/
|
||||
$args = apply_filters( 'elementor/kit-library/get-kits-data/args', $args );
|
||||
|
||||
$data = $this->api->get_all( $args );
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
throw new WP_Error_Exception( $data );
|
||||
}
|
||||
|
||||
set_transient( static::KITS_CACHE_KEY, $data, static::KITS_CACHE_TTL_HOURS * HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
return new Collection( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force_api_request
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function get_taxonomies_data( $force_api_request = false ) {
|
||||
$data = get_transient( static::KITS_TAXONOMIES_CACHE_KEY );
|
||||
|
||||
if ( ! $data || $force_api_request ) {
|
||||
$data = $this->api->get_taxonomies();
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
throw new WP_Error_Exception( $data );
|
||||
}
|
||||
|
||||
set_transient( static::KITS_TAXONOMIES_CACHE_KEY, $data, static::KITS_TAXONOMIES_CACHE_TTL_HOURS * HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
return new Collection( (array) $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $kit
|
||||
* @param null $manifest
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function transform_kit_api_response( $kit, $manifest = null ) {
|
||||
// BC: Support legacy APIs that don't have access tiers.
|
||||
if ( isset( $kit->access_tier ) ) {
|
||||
$access_tier = $kit->access_tier;
|
||||
} else {
|
||||
$access_tier = 0 === $kit->access_level
|
||||
? ConnectModule::ACCESS_TIER_FREE
|
||||
: ConnectModule::ACCESS_TIER_ESSENTIAL;
|
||||
}
|
||||
|
||||
$subscription_plan_tag = $this->subscription_plans->get( $access_tier );
|
||||
|
||||
$taxonomies = ( new Collection( ( (array) $kit )['taxonomies'] ) )
|
||||
->filter( function ( $taxonomy ) {
|
||||
return in_array( $taxonomy->type, self::TAXONOMIES_KEYS );
|
||||
} )
|
||||
->flatten()
|
||||
->pluck( 'name' )
|
||||
->push( $subscription_plan_tag ? $subscription_plan_tag : self::SUBSCRIPTION_PLAN_FREE_TAG );
|
||||
|
||||
return array_merge(
|
||||
[
|
||||
'id' => $kit->_id,
|
||||
'title' => $kit->title,
|
||||
'thumbnail_url' => $kit->thumbnail,
|
||||
'access_level' => $kit->access_level,
|
||||
'access_tier' => $access_tier,
|
||||
'keywords' => $kit->keywords,
|
||||
'taxonomies' => $taxonomies->values(),
|
||||
'is_favorite' => $this->user_favorites->exists( 'elementor', 'kits', $kit->_id ),
|
||||
// TODO: Remove all the isset when the API stable.
|
||||
'trend_index' => isset( $kit->trend_index ) ? $kit->trend_index : 0,
|
||||
'featured_index' => isset( $kit->featured_index ) ? $kit->featured_index : 0,
|
||||
'popularity_index' => isset( $kit->popularity_index ) ? $kit->popularity_index : 0,
|
||||
'created_at' => isset( $kit->created_at ) ? $kit->created_at : null,
|
||||
'updated_at' => isset( $kit->updated_at ) ? $kit->updated_at : null,
|
||||
//
|
||||
],
|
||||
$manifest ? $this->transform_manifest_api_response( $manifest ) : []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $manifest
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function transform_manifest_api_response( $manifest ) {
|
||||
$manifest_content = ( new Collection( (array) $manifest->content ) )
|
||||
->reduce( function ( $carry, $content, $type ) {
|
||||
$mapped_documents = array_map( function ( $document ) use ( $type ) {
|
||||
// TODO: Fix it!
|
||||
// Hack to override a bug when a document with type of 'wp-page' is declared as 'wp-post'.
|
||||
if ( 'page' === $type ) {
|
||||
$document->doc_type = 'wp-page';
|
||||
}
|
||||
|
||||
return $document;
|
||||
}, (array) $content );
|
||||
|
||||
return $carry + $mapped_documents;
|
||||
}, [] );
|
||||
|
||||
$content = ( new Collection( (array) $manifest->templates ) )
|
||||
->union( $manifest_content )
|
||||
->map( function ( $manifest_item, $key ) {
|
||||
return [
|
||||
'id' => isset( $manifest_item->id ) ? $manifest_item->id : $key,
|
||||
'title' => $manifest_item->title,
|
||||
'doc_type' => $manifest_item->doc_type,
|
||||
'thumbnail_url' => $manifest_item->thumbnail,
|
||||
'preview_url' => isset( $manifest_item->url ) ? $manifest_item->url : null,
|
||||
];
|
||||
} );
|
||||
|
||||
return [
|
||||
'description' => $manifest->description,
|
||||
'preview_url' => isset( $manifest->site ) ? $manifest->site : '',
|
||||
'documents' => $content->values(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Kit_Library $kit_library
|
||||
* @param User_Favorites $user_favorites
|
||||
* @param Collection $subscription_plans
|
||||
*/
|
||||
public function __construct( Kit_Library $kit_library, User_Favorites $user_favorites, Collection $subscription_plans ) {
|
||||
$this->api = $kit_library;
|
||||
$this->user_favorites = $user_favorites;
|
||||
$this->subscription_plans = $subscription_plans;
|
||||
}
|
||||
|
||||
public static function clear_cache() {
|
||||
delete_transient( static::KITS_CACHE_KEY );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary\Data\Taxonomies;
|
||||
|
||||
use Elementor\App\Modules\KitLibrary\Data\Base_Controller;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Controller extends Base_Controller {
|
||||
|
||||
public function get_name() {
|
||||
return 'kit-taxonomies';
|
||||
}
|
||||
|
||||
public function get_collection_params() {
|
||||
return [
|
||||
'force' => [
|
||||
'description' => 'Force an API request and skip the cache.',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'type' => 'boolean',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function get_items( $request ) {
|
||||
$data = $this->get_repository()->get_taxonomies( $request->get_param( 'force' ) );
|
||||
|
||||
return [
|
||||
'data' => $data->values(),
|
||||
];
|
||||
}
|
||||
|
||||
public function get_permission_callback( $request ) {
|
||||
return current_user_can( 'administrator' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Kit_Library_Menu_Item implements Admin_Menu_Item {
|
||||
|
||||
public function is_visible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_parent_slug() {
|
||||
return Source_Local::ADMIN_MENU_SLUG;
|
||||
}
|
||||
|
||||
public function get_label() {
|
||||
return esc_html__( 'Kit Library', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_capability() {
|
||||
return 'manage_options';
|
||||
}
|
||||
}
|
||||
117
wp-content/plugins/elementor/app/modules/kit-library/module.php
Normal file
117
wp-content/plugins/elementor/app/modules/kit-library/module.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\KitLibrary;
|
||||
|
||||
use Elementor\App\Modules\KitLibrary\Data\Repository;
|
||||
use Elementor\Core\Admin\Menu\Admin_Menu_Manager;
|
||||
use Elementor\Core\Admin\Menu\Main as MainMenu;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\App\Modules\KitLibrary\Connect\Kit_Library;
|
||||
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
|
||||
use Elementor\App\Modules\KitLibrary\Data\Kits\Controller as Kits_Controller;
|
||||
use Elementor\App\Modules\KitLibrary\Data\Taxonomies\Controller as Taxonomies_Controller;
|
||||
use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Module extends BaseModule {
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'kit-library';
|
||||
}
|
||||
|
||||
private function register_admin_menu( MainMenu $menu ) {
|
||||
$menu->add_submenu( [
|
||||
'page_title' => esc_html__( 'Kit Library', 'elementor' ),
|
||||
'menu_title' => '<span id="e-admin-menu__kit-library">' . esc_html__( 'Kit Library', 'elementor' ) . '</span>',
|
||||
'menu_slug' => Plugin::$instance->app->get_base_url() . '#/kit-library',
|
||||
'index' => 40,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the admin menu the old way.
|
||||
*/
|
||||
private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) {
|
||||
$admin_menu->register(
|
||||
Plugin::$instance->app->get_base_url() . '#/kit-library',
|
||||
new Kit_Library_Menu_Item()
|
||||
);
|
||||
}
|
||||
|
||||
private function set_kit_library_settings() {
|
||||
if ( ! Plugin::$instance->common ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var ConnectModule $connect */
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
|
||||
/** @var Kit_Library $kit_library */
|
||||
$kit_library = $connect->get_app( 'kit-library' );
|
||||
|
||||
Plugin::$instance->app->set_settings( 'kit-library', [
|
||||
'has_access_to_module' => current_user_can( 'administrator' ),
|
||||
'subscription_plans' => $this->apply_filter_subscription_plans( $connect->get_subscription_plans( 'kit-library' ) ),
|
||||
'is_pro' => false,
|
||||
'is_library_connected' => $kit_library->is_connected(),
|
||||
'library_connect_url' => $kit_library->get_admin_url( 'authorize', [
|
||||
'utm_source' => 'kit-library',
|
||||
'utm_medium' => 'wp-dash',
|
||||
'utm_campaign' => 'library-connect',
|
||||
'utm_term' => '%%page%%', // Will be replaced in the frontend.
|
||||
] ),
|
||||
'access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
'app_url' => Plugin::$instance->app->get_base_url() . '#/' . $this->get_name(),
|
||||
] );
|
||||
}
|
||||
|
||||
private function apply_filter_subscription_plans( array $subscription_plans ): array {
|
||||
foreach ( $subscription_plans as $key => $plan ) {
|
||||
if ( null === $plan['promotion_url'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$subscription_plans[ $key ] = Filtered_Promotions_Manager::get_filtered_promotion_data(
|
||||
$plan,
|
||||
'elementor/kit_library/' . $key . '/promotion',
|
||||
'promotion_url'
|
||||
);
|
||||
}
|
||||
|
||||
return $subscription_plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
Plugin::$instance->data_manager_v2->register_controller( new Kits_Controller() );
|
||||
Plugin::$instance->data_manager_v2->register_controller( new Taxonomies_Controller() );
|
||||
|
||||
// Assigning this action here since the repository is being loaded by demand.
|
||||
add_action( 'elementor/experiments/feature-state-change/container', [ Repository::class, 'clear_cache' ], 10, 0 );
|
||||
|
||||
add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) {
|
||||
$this->register_admin_menu_legacy( $admin_menu );
|
||||
}, Source_Local::ADMIN_MENU_PRIORITY + 30 );
|
||||
|
||||
add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
|
||||
$connect_module->register_app( 'kit-library', Kit_Library::get_class_name() );
|
||||
} );
|
||||
|
||||
add_action( 'elementor/init', function () {
|
||||
$this->set_kit_library_settings();
|
||||
}, 12 /** after the initiation of the connect kit library */ );
|
||||
}
|
||||
}
|
||||
487
wp-content/plugins/elementor/app/modules/onboarding/module.php
Normal file
487
wp-content/plugins/elementor/app/modules/onboarding/module.php
Normal file
@@ -0,0 +1,487 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\Onboarding;
|
||||
|
||||
use Automatic_Upgrader_Skin;
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Core\Files\Uploads_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
use Elementor\Utils;
|
||||
use Plugin_Upgrader;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Onboarding Module
|
||||
*
|
||||
* Responsible for initializing Elementor App functionality
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
const VERSION = '1.0.0';
|
||||
const ONBOARDING_OPTION = 'elementor_onboarded';
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @since 3.6.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'onboarding';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Onboarding Settings
|
||||
*
|
||||
* Creates an array of module settings that is localized into the JS App config.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
private function set_onboarding_settings() {
|
||||
if ( ! Plugin::$instance->common ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the published pages and posts
|
||||
$pages_and_posts = new \WP_Query( [
|
||||
'post_type' => [ 'page', 'post' ],
|
||||
'post_status' => 'publish',
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'no_found_rows' => true,
|
||||
] );
|
||||
|
||||
$custom_site_logo_id = get_theme_mod( 'custom_logo' );
|
||||
$custom_logo_src = wp_get_attachment_image_src( $custom_site_logo_id, 'full' );
|
||||
$site_name = get_option( 'blogname', '' );
|
||||
|
||||
$hello_theme = wp_get_theme( 'hello-elementor' );
|
||||
$hello_theme_errors = is_object( $hello_theme->errors() ) ? $hello_theme->errors()->errors : [];
|
||||
|
||||
/** @var Library $library */
|
||||
$library = Plugin::$instance->common->get_component( 'connect' )->get_app( 'library' );
|
||||
|
||||
Plugin::$instance->app->set_settings( 'onboarding', [
|
||||
'eventPlacement' => 'Onboarding wizard',
|
||||
'onboardingAlreadyRan' => get_option( self::ONBOARDING_OPTION ),
|
||||
'onboardingVersion' => self::VERSION,
|
||||
'isLibraryConnected' => $library->is_connected(),
|
||||
// Used to check if the Hello Elementor theme is installed but not activated.
|
||||
'helloInstalled' => empty( $hello_theme_errors['theme_not_found'] ),
|
||||
'helloActivated' => 'hello-elementor' === get_option( 'template' ),
|
||||
// The "Use Hello theme on my site" checkbox should be checked by default only if this condition is met.
|
||||
'helloOptOut' => count( $pages_and_posts->posts ) < 5,
|
||||
'siteName' => esc_html( $site_name ),
|
||||
'isUnfilteredFilesEnabled' => Uploads_Manager::are_unfiltered_uploads_enabled(),
|
||||
'urls' => [
|
||||
'kitLibrary' => Plugin::$instance->app->get_base_url() . '#/kit-library?order[direction]=desc&order[by]=featuredIndex',
|
||||
'createNewPage' => Plugin::$instance->documents->get_create_new_post_url(),
|
||||
'connect' => $library->get_admin_url( 'authorize', [
|
||||
'utm_source' => 'onboarding-wizard',
|
||||
'utm_campaign' => 'connect-account',
|
||||
'utm_medium' => 'wp-dash',
|
||||
'utm_term' => self::VERSION,
|
||||
'source' => 'generic',
|
||||
] ),
|
||||
'upgrade' => 'https://go.elementor.com/go-pro-onboarding-wizard-upgrade/',
|
||||
'signUp' => $library->get_admin_url( 'authorize', [
|
||||
'utm_source' => 'onboarding-wizard',
|
||||
'utm_campaign' => 'connect-account',
|
||||
'utm_medium' => 'wp-dash',
|
||||
'utm_term' => self::VERSION,
|
||||
'source' => 'generic',
|
||||
'screen_hint' => 'signup',
|
||||
] ),
|
||||
'uploadPro' => Plugin::$instance->app->get_base_url() . '#/onboarding/uploadAndInstallPro?mode=popup',
|
||||
],
|
||||
'siteLogo' => [
|
||||
'id' => $custom_site_logo_id,
|
||||
'url' => $custom_logo_src ? $custom_logo_src[0] : '',
|
||||
],
|
||||
'utms' => [
|
||||
'connectTopBar' => '&utm_content=top-bar',
|
||||
'connectCta' => '&utm_content=cta-button',
|
||||
'connectCtaLink' => '&utm_content=cta-link',
|
||||
'downloadPro' => '?utm_source=onboarding-wizard&utm_campaign=my-account-subscriptions&utm_medium=wp-dash&utm_content=import-pro-plugin&utm_term=' . self::VERSION,
|
||||
],
|
||||
'nonce' => wp_create_nonce( 'onboarding' ),
|
||||
'experiment' => Plugin::$instance->experiments->is_feature_active( 'e_onboarding' ),
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Permission Error Response
|
||||
*
|
||||
* Returns the response that is returned when the user's capabilities are not sufficient for performing an action.
|
||||
*
|
||||
* @since 3.6.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_permission_error_response() {
|
||||
return [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => esc_html__( 'You do not have permission to perform this action.', 'elementor' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe Update Site Logo
|
||||
*
|
||||
* If a new name is provided, it will be updated as the Site Name.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function maybe_update_site_name() {
|
||||
$problem_error = [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => esc_html__( 'There was a problem setting your site name.', 'elementor' ),
|
||||
],
|
||||
];
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
if ( empty( $_POST['data'] ) ) {
|
||||
return $problem_error;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$data = json_decode( Utils::get_super_global_value( $_POST, 'data' ), true );
|
||||
|
||||
if ( ! isset( $data['siteName'] ) ) {
|
||||
return $problem_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Onboarding Site Name
|
||||
*
|
||||
* Filters the new site name passed by the user to update in Elementor's onboarding process.
|
||||
* Elementor runs `esc_html()` on the Site Name passed by the user for security reasons. If a user wants to
|
||||
* include special characters in their site name, they can use this filter to override it.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string Escaped new site name
|
||||
*/
|
||||
$new_site_name = apply_filters( 'elementor/onboarding/site-name', $data['siteName'] );
|
||||
|
||||
// The site name is sanitized in `update_options()`
|
||||
update_option( 'blogname', $new_site_name );
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'payload' => [
|
||||
'siteNameUpdated' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe Update Site Logo
|
||||
*
|
||||
* If an image attachment ID is provided, it will be updated as the Site Logo Theme Mod.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function maybe_update_site_logo() {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return $this->get_permission_error_response();
|
||||
}
|
||||
|
||||
$data_error = [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => esc_html__( 'There was a problem setting your site logo.', 'elementor' ),
|
||||
],
|
||||
];
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
if ( empty( $_POST['data'] ) ) {
|
||||
return $data_error;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$data = json_decode( Utils::get_super_global_value( $_POST, 'data' ), true );
|
||||
|
||||
// If there is no attachment ID passed or it is not a valid ID, exit here.
|
||||
if ( empty( $data['attachmentId'] ) ) {
|
||||
return $data_error;
|
||||
}
|
||||
|
||||
$absint_attachment_id = absint( $data['attachmentId'] );
|
||||
|
||||
if ( 0 === $absint_attachment_id ) {
|
||||
return $data_error;
|
||||
}
|
||||
|
||||
$attachment_url = wp_get_attachment_url( $data['attachmentId'] );
|
||||
|
||||
// Check if the attachment exists. If it does not, exit here.
|
||||
if ( ! $attachment_url ) {
|
||||
return $data_error;
|
||||
}
|
||||
|
||||
set_theme_mod( 'custom_logo', $absint_attachment_id );
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'payload' => [
|
||||
'siteLogoUpdated' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe Upload Logo Image
|
||||
*
|
||||
* If an image file upload is provided, and it passes validation, it will be uploaded to the site's Media Library.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function maybe_upload_logo_image() {
|
||||
$error_message = esc_html__( 'There was a problem uploading your file.', 'elementor' );
|
||||
|
||||
$file = Utils::get_super_global_value( $_FILES, 'fileToUpload' );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
if ( ! is_array( $file ) || empty( $file['type'] ) ) {
|
||||
return [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => $error_message,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// If the user has allowed it, set the Request's state as an "Elementor Upload" request, in order to add
|
||||
// support for non-standard file uploads.
|
||||
if ( 'image/svg+xml' === $file['type'] ) {
|
||||
if ( Uploads_Manager::are_unfiltered_uploads_enabled() ) {
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
|
||||
} else {
|
||||
wp_send_json_error( 'To upload SVG files, you must allow uploading unfiltered files.' );
|
||||
}
|
||||
}
|
||||
|
||||
// If the image is an SVG file, sanitation is performed during the import (upload) process.
|
||||
$image_attachment = Plugin::$instance->templates_manager->get_import_images_instance()->import( $file );
|
||||
|
||||
if ( 'image/svg+xml' === $file['type'] && Uploads_Manager::are_unfiltered_uploads_enabled() ) {
|
||||
// Reset Upload state.
|
||||
Plugin::$instance->uploads_manager->set_elementor_upload_state( false );
|
||||
}
|
||||
|
||||
if ( $image_attachment && ! is_wp_error( $image_attachment ) ) {
|
||||
$result = [
|
||||
'status' => 'success',
|
||||
'payload' => [
|
||||
'imageAttachment' => $image_attachment,
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$result = [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => $error_message,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate Hello Theme
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function maybe_activate_hello_theme() {
|
||||
if ( ! current_user_can( 'switch_themes' ) ) {
|
||||
return $this->get_permission_error_response();
|
||||
}
|
||||
|
||||
switch_theme( 'hello-elementor' );
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'payload' => [
|
||||
'helloThemeActivated' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload and Install Elementor Pro
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function upload_and_install_pro() {
|
||||
if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) {
|
||||
return $this->get_permission_error_response();
|
||||
}
|
||||
|
||||
$error_message = esc_html__( 'There was a problem uploading your file.', 'elementor' );
|
||||
|
||||
$file = Utils::get_super_global_value( $_FILES, 'fileToUpload' ) ?? [];
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
if ( ! is_array( $file ) || empty( $file['type'] ) ) {
|
||||
return [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => $error_message,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
if ( ! class_exists( 'Automatic_Upgrader_Skin' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
||||
}
|
||||
|
||||
$skin = new Automatic_Upgrader_Skin();
|
||||
$upgrader = new Plugin_Upgrader( $skin );
|
||||
$upload_result = $upgrader->install( $file['tmp_name'], [ 'overwrite_package' => false ] );
|
||||
|
||||
if ( ! $upload_result || is_wp_error( $upload_result ) ) {
|
||||
$result = [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => $error_message,
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$activated = activate_plugin( WP_PLUGIN_DIR . '/elementor-pro/elementor-pro.php', false, false, true );
|
||||
|
||||
if ( ! is_wp_error( $activated ) ) {
|
||||
$result = [
|
||||
'status' => 'success',
|
||||
'payload' => [
|
||||
'elementorProInstalled' => true,
|
||||
],
|
||||
];
|
||||
} else {
|
||||
$result = [
|
||||
'status' => 'error',
|
||||
'payload' => [
|
||||
'error_message' => $error_message,
|
||||
'elementorProInstalled' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function maybe_update_onboarding_db_option() {
|
||||
$db_option = get_option( self::ONBOARDING_OPTION );
|
||||
|
||||
if ( ! $db_option ) {
|
||||
update_option( self::ONBOARDING_OPTION, true );
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'payload' => 'onboarding DB',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe Handle Ajax
|
||||
*
|
||||
* This method checks if there are any AJAX actions being
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function maybe_handle_ajax() {
|
||||
$result = [];
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
switch ( Utils::get_super_global_value( $_POST, 'action' ) ) {
|
||||
case 'elementor_update_site_name':
|
||||
// If no value is passed for any reason, no need ot update the site name.
|
||||
$result = $this->maybe_update_site_name();
|
||||
break;
|
||||
case 'elementor_update_site_logo':
|
||||
$result = $this->maybe_update_site_logo();
|
||||
break;
|
||||
case 'elementor_upload_site_logo':
|
||||
$result = $this->maybe_upload_logo_image();
|
||||
break;
|
||||
case 'elementor_activate_hello_theme':
|
||||
$result = $this->maybe_activate_hello_theme();
|
||||
break;
|
||||
case 'elementor_upload_and_install_pro':
|
||||
$result = $this->upload_and_install_pro();
|
||||
break;
|
||||
case 'elementor_update_onboarding_option':
|
||||
$result = $this->maybe_update_onboarding_db_option();
|
||||
}
|
||||
|
||||
if ( ! empty( $result ) ) {
|
||||
if ( 'success' === $result['status'] ) {
|
||||
wp_send_json_success( $result['payload'] );
|
||||
} else {
|
||||
wp_send_json_error( $result['payload'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'elementor/init', function() {
|
||||
// Only load when viewing the onboarding app.
|
||||
if ( Plugin::$instance->app->is_current() ) {
|
||||
$this->set_onboarding_settings();
|
||||
// Needed for installing the Hello Elementor theme.
|
||||
wp_enqueue_script( 'updates' );
|
||||
// Needed for uploading Logo from WP Media Library.
|
||||
wp_enqueue_media();
|
||||
}
|
||||
}, 12 );
|
||||
|
||||
// Needed for uploading Logo from WP Media Library. The 'admin_menu' hook is used because it runs before
|
||||
// 'admin_init', and the App triggers printing footer scripts on 'admin_init' at priority 0.
|
||||
add_action( 'admin_menu', function () {
|
||||
add_action( 'wp_print_footer_scripts', function () {
|
||||
if ( function_exists( 'wp_print_media_templates' ) ) {
|
||||
wp_print_media_templates();
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
add_action( 'admin_init', function() {
|
||||
if ( wp_doing_ajax() &&
|
||||
isset( $_POST['action'] ) &&
|
||||
isset( $_POST['_nonce'] ) &&
|
||||
wp_verify_nonce( Utils::get_super_global_value( $_POST, '_nonce' ), Ajax::NONCE_KEY ) &&
|
||||
current_user_can( 'manage_options' )
|
||||
) {
|
||||
$this->maybe_handle_ajax();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\SiteEditor;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Site Editor Module
|
||||
*
|
||||
* Responsible for initializing Elementor App functionality
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'site-editor';
|
||||
}
|
||||
|
||||
public function add_menu_in_admin_bar( $admin_bar_config ) {
|
||||
$admin_bar_config['elementor_edit_page']['children'][] = [
|
||||
'id' => 'elementor_app_site_editor',
|
||||
'title' => esc_html__( 'Theme Builder', 'elementor' ),
|
||||
'sub_title' => esc_html__( 'Site', 'elementor' ),
|
||||
'href' => Plugin::$instance->app->get_settings( 'menu_url' ),
|
||||
'class' => 'elementor-app-link',
|
||||
'parent_class' => 'elementor-second-section',
|
||||
];
|
||||
|
||||
return $admin_bar_config;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
add_filter( 'elementor/frontend/admin_bar/settings', [ $this, 'add_menu_in_admin_bar' ] ); // After kit (Site settings)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user