| Server IP : 209.209.40.120 / Your IP : 216.73.217.112 Web Server : Microsoft-IIS/10.0 System : Windows NT NEWWWW 10.0 build 17763 (Windows Server 2019) i586 User : NEWWWW$ ( 0) PHP Version : 8.3.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/HostingSpaces/admin/tickwaves.com/wwwroot/wp-content/plugins/solace-extra/admin/ |
Upload File : |
<?php
defined( 'ABSPATH' ) || exit;
/**
* Backend submenu Import
*
* @link https://solacewp.com
* @since 1.0.0
*
* @package Solace_Extra
* @subpackage Solace_Extra/admin
*/
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress importer uses core/legacy hook names for compatibility.
/**
* The admin-specific functionality of the plugin (import).
*
* @package Solace_Extra
* @subpackage Solace_Extra/import
* @author Solace <solacewp@gmail.com>
*/
require_once plugin_dir_path(__FILE__) . 'wp-background-processing.php';
/** Display verbose errors */
if ( ! defined( 'IMPORT_DEBUG' ) ) {
define( 'IMPORT_DEBUG', WP_DEBUG );
}
if ( ! function_exists( 'post_exists' ) ) {
require_once ABSPATH . 'wp-admin/includes/post.php';
}
/** WordPress Import Administration API */
require_once ABSPATH . 'wp-admin/includes/import.php';
if ( ! class_exists( 'WP_Importer' ) ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WordPress Importer API compatibility
$class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
if ( file_exists( $class_wp_importer ) ) {
require $class_wp_importer;
}
}
/** Functions missing in older WordPress versions. */
require_once __DIR__ . '/export-import/compat.php';
/** Solace_Extra_WXR_Parser class */
require_once __DIR__ . '/export-import/parsers/class-wxr-parser.php';
/** Solace_Extra_WXR_Parser_SimpleXML class */
require_once __DIR__ . '/export-import/parsers/class-wxr-parser-simplexml.php';
/** Solace_Extra_WXR_Parser_XML class */
require_once __DIR__ . '/export-import/parsers/class-wxr-parser-xml.php';
/** Solace_Extra_WXR_Parser_Regex class */
require_once __DIR__ . '/export-import/parsers/class-wxr-parser-regex.php';
/** Solace_Extra_WP_Import class */
require_once __DIR__ . '/export-import/class-wp-import.php';
class Solace_Import_Elementor_Process extends XWP_Background_Process {
protected $action = 'import_posts_process';
private $demo_name;
protected function task($item) {
if (isset($item['action']) && $item['action'] === 'demo_name' && isset($item['demo_name'])) {
$this->import_elementor_kit($item['demo_name']);
}
return false;
}
protected function import_elementor_kit($demo_name) {
// Set elementor settings
$demo = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $demo_name . '/wp-json/solace/v1/elementor';
$this->demo_name = $demo_name;
// Remote get data
$response = wp_remote_get($demo);
$datas = wp_remote_retrieve_body($response);
$data = json_decode($datas);
update_option( 'elementor_disable_color_schemes', 'yes' );
update_option( 'elementor_disable_typography_schemes', 'yes' );
update_option( 'elementor_experiment-nested-elements', sanitize_key( $data->nested_elements ) );
$zip_url = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . "wp-content/uploads/demolist/" . $demo_name . "/" . $demo_name . ".zip";
\Elementor\Plugin::$instance->uploads_manager->enable_unfiltered_files_upload();
\Elementor\Plugin::$instance->uploads_manager->set_elementor_upload_state(true);
$upload_dir = wp_upload_dir();
$response = wp_remote_get($zip_url);
$zip_data = wp_upload_bits(basename($zip_url), null, wp_remote_retrieve_body(wp_remote_get($zip_url)));
if (!$zip_data['error']) {
if (class_exists('Elementor\Plugin')) {
$zip_file_path = $zip_data['file'];
$zip = new ZipArchive();
if ($zip->open($zip_file_path, ZipArchive::CREATE) === true) {
\Elementor\Plugin::$instance->uploads_manager->set_elementor_upload_state(true);
\Elementor\Plugin::$instance->uploads_manager->enable_unfiltered_files_upload();
add_filter('elementor/template_library/import_images/new_attachment', array($this, 'handle_images_import'));
add_action('wp_import_insert_post', array($this, 'handle_import_post'), 10, 4);
add_action('wp_import_set_post_terms', array($this, 'handle_import_post_terms'), 10, 5);
add_filter('elementor/document/save/data', array($this, 'handle_pages_import'), 10, 2);
$import_export = \Elementor\Plugin::$instance->app->get_component('import-export');
// In non-admin contexts (WP-Cron / REST) Elementor skips registering
// the 'import-export' component — App::__construct() gates it behind
// current_user_can('manage_options'). When that happens, build the
// module directly so the kit still imports.
if (!$import_export && class_exists('\Elementor\App\Modules\ImportExport\Module')) {
$import_export = new \Elementor\App\Modules\ImportExport\Module();
}
if (!$import_export) {
throw new \RuntimeException('Elementor import-export component is unavailable; cannot import kit.');
}
$import_export->import_kit($zip_file_path, array('include', 'selected_plugins', 'selected_cpt', 'selected_override_conditions'));
$zip->close();
// $this->set_latest_cart_page_as_default();
$this->set_latest_page_as_default('CART');
$this->set_latest_page_as_default('CHECKOUT');
$this->set_latest_page_as_default('MY ACCOUNT');
} else {
esc_html_e('Failed opening file ZIP.', 'solace-extra');
}
} else {
esc_html_e('Plugin Elementor Not Activated.', 'solace-extra');
}
} else {
esc_html_e('Failed downloading file ZIP.', 'solace-extra');
}
if ( class_exists( 'Elementor\Plugin' ) ) {
$elementor_active_kit = get_option( 'elementor_active_kit' );
update_post_meta( $elementor_active_kit, 'solace_import_elementor_kit', true );
}
}
protected function complete() {
parent::complete();
update_option('solace_extra_import_zip_complete', true);
set_theme_mod('solace_extra_import_zip_complete', true);
}
/**
* Run a demo's Elementor kit import synchronously, in the current PHP process.
*
* The REST/automation bridge (Solace_Extra_REST_Demo) uses this instead of the
* async loopback dispatch, which fails under Application-Password auth: the
* loopback request carries no login cookie, so admin-ajax.php sees it as an
* unauthenticated user and check_ajax_referer() rejects the nonce — the queue
* is never processed. Running the import inline (from a WP-Cron event that does
* NOT require the admin nonce/cookie) sidesteps that entirely.
*
* @param string $demo_name The demo slug to import.
* @return void
*/
public function run_import_now($demo_name) {
$this->import_elementor_kit($demo_name);
}
/**
* Drain the queued batch in this PHP process instead of via the async loopback.
*
* import_zip() ends with save()->dispatch(). Under Application-Password auth the
* dispatch is a dead end (cookieless loopback -> rejected nonce), so nothing ever
* processes the batch. Calling handle() here runs the queued task, then complete()
* flips solace_extra_import_zip_complete — exactly what the admin flow ends up doing.
*
* @return void
*/
public function run_queue_now() {
$this->handle();
}
public function set_latest_page_as_default($page_title) {
$pages = get_posts([
'post_type' => 'page',
'post_status' => 'publish',
'title' => $page_title,
'numberposts' => -1,
'fields' => 'ids',
]);
if (!empty($pages)) {
$latest_page_id = max($pages);
if ($page_title === 'CART') {
update_option('woocommerce_cart_page_id', $latest_page_id);
} elseif ($page_title === 'CHECKOUT') {
update_option('woocommerce_checkout_page_id', $latest_page_id);
} elseif ($page_title === 'MY ACCOUNT') {
update_option('woocommerce_myaccount_page_id', $latest_page_id);
}
}
}
/**
* Handles the import of pages.
*
* @param mixed $data The data being processed during import.
* @param WP_Document $document The document being imported.
*
* @return mixed The modified or unmodified data after handling the pages import.
*/
public function handle_pages_import($data, $document)
{
// Get the post object from the document
$post_object = $document->get_post();
// Ensure the post object is an instance of WP_Post
if ($post_object instanceof WP_Post) {
// Check if the post type is 'page'
if ($post_object->post_type === 'page') {
// Get the post ID
$post_id = $post_object->ID;
// Ensure the post ID is available before updating the meta
if ($post_id) {
// Get demo_name.
$imported_demo_name = 'solace_extra_' . $this->demo_name;
// Update the 'solace_import_pages' meta to true to mark the page as imported
update_post_meta($post_id, 'solace_import_pages', true);
update_post_meta($post_id, $imported_demo_name, true);
}
}
// Check if the post type is 'solace-sitebuilder'
if ($post_object->post_type === 'solace-sitebuilder') {
// Get the post ID
$post_id = $post_object->ID;
// Ensure the post ID is available before updating the meta
if ($post_id) {
// Get demo_name.
$imported_demo_name = 'solace_extra_' . $this->demo_name;
// Update the 'solace_import_site_builder' meta to true to mark the page as imported
update_post_meta($post_id, 'solace_import_site_builder', true);
update_post_meta($post_id, $imported_demo_name, true);
}
}
}
// Return the unmodified data
return $data;
}
/**
* Handles the import of posts and updates the post meta 'solace_import_posts'.
*
* @param int $post_id The ID of the imported post.
* @param int $original_post_id The ID of the original post.
* @param object $postdata The post data.
* @param object $post The post object.
*/
public function handle_import_post($post_id, $original_post_id, $postdata, $post)
{
// Get demo_name.
$imported_demo_name = 'solace_extra_' . $this->demo_name;
update_post_meta($post_id, $imported_demo_name, true);
update_post_meta($post_id, 'solace_import_posts', true);
if ($post['post_type'] === 'product') {
update_post_meta($post_id, 'solace_import_products', true);
}
}
/**
* Handles the import of images and updates the post meta 'solace_import_images'.
*
* @param int $post_id The ID of the post associated with the imported image.
*/
public function handle_images_import($post_id)
{
// Update the post meta 'solace_import_images' with the value 'true'
update_post_meta($post_id, 'solace_import_images', true);
return $post_id;
}
/**
* Handle the import of terms for a post.
*
* This function processes the imported terms for a post, updating term metadata
* for categories and post tags with the 'solace_imported_term' flag.
*
* @param array $tt_ids Term taxonomy IDs.
* @param array $ids Term IDs.
* @param string $tax Taxonomy name.
* @param int $post_id Post ID.
* @param WP_Post $post The post object.
*
* @return void
*/
public function handle_import_post_terms($tt_ids, $ids, $tax, $post_id, $post)
{
foreach ($post['terms'] as $data) {
if ($data['domain'] === 'category') {
$category = get_term_by('slug', $data['slug'], 'category');
if ($category) {
if ($data['slug'] !== 'uncategorized') {
$category_id = $category->term_id;
update_term_meta($category_id, 'solace_imported_term_category', true);
}
}
}
if ($data['domain'] === 'post_tag') {
$tag = get_term_by('slug', $data['slug'], 'post_tag');
if ($tag) {
$tag_id = $tag->term_id;
update_term_meta($tag_id, 'solace_imported_term_post_tag', true);
}
}
// Products cat
if ($data['domain'] === 'product_cat') {
$product_cat = get_term_by('slug', $data['slug'], 'product_cat');
if ($product_cat) {
if ($data['slug'] !== 'uncategorized') {
$product_cat_id = $product_cat->term_id;
update_term_meta($product_cat_id, 'solace_imported_term_product_cat', true);
}
}
}
// Product tag
if ($data['domain'] === 'product_tag') {
$tag = get_term_by('slug', $data['slug'], 'product_tag');
if ($tag) {
$tag_id = $tag->term_id;
update_term_meta($tag_id, 'solace_imported_term_product_tag', true);
}
}
}
}
}
class Thumbnail_Generator extends XWP_Background_Process {
protected $action = 'generate_thumbnails';
/**
* Proses setiap item
*
* @param mixed $item Data attachment ID.
* @return mixed
*/
protected function task($item) {
if (!wp_attachment_is_image($item)) {
return false;
}
$file_path = get_attached_file($item);
if (!$file_path || !file_exists($file_path)) {
return false;
}
$sizes = get_intermediate_image_sizes();
$metadata = wp_generate_attachment_metadata($item, $file_path);
foreach ($sizes as $size) {
if (!isset($metadata['sizes'][$size])) {
$editor = wp_get_image_editor($file_path);
if (!is_wp_error($editor)) {
$editor->resize(
get_option("{$size}_size_w"),
get_option("{$size}_size_h"),
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- WordPress core image size crop filter
apply_filters("{$size}_crop", false)
);
$editor->save();
}
}
}
wp_update_attachment_metadata($item, $metadata);
return false;
}
/**
* Selesai proses
*
* @return void
*/
protected function complete() {
parent::complete();
}
}
class Solace_Extra_Import {
/**
* The ID of this plugin.
*
* @since 1.0.0
* @access private
* @var string $plugin_name The ID of this plugin.
*/
private $plugin_name;
private $import_process;
/**
* The demo_name of this plugin.
*
* @since 1.0.0
* @access private
* @var string $demo_name The current demo_name of this plugin.
*/
private $demo_name;
/**
* The version of this plugin.
*
* @since 1.0.0
* @access private
* @var string $version The current version of this plugin.
*/
private $version;
/**
* Initialize the class and set its properties.
*
* @since 1.0.0
* @param string $plugin_name The name of this plugin.
* @param string $version The version of this plugin.
*/
public function __construct($plugin_name, $version)
{
$this->plugin_name = $plugin_name;
$this->version = $version;
$this->import_process = new Solace_Import_Elementor_Process();
}
/**
* Checks to see whether a string is an image url or not.
*
* @since 0.1
* @access private
* @param string $string The string to check.
* @return bool Whether the string is an image url or not.
*/
static private function _is_image_url($string = '')
{
if (is_string($string)) {
if (preg_match('/\.(jpg|jpeg|png|gif)/i', $string)) {
return true;
}
}
return false;
}
/**
* Handles AJAX request for importing the site builder.
*
* This method verifies the nonce and checks if the current user has the
* 'manage_options' capability. If either check fails, it returns an
* appropriate JSON error response with a relevant HTTP status code.
*
* @return void
*/
public function call_ajax_import_site_builder() {
// Verify nonce.
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ajax-nonce' ) ) {
wp_send_json_error( array( 'error' => 'Invalid nonce!' ), 403 ); // 403 Forbidden
}
// Check current user capability.
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'error' => 'Unauthorized' ), 401 ); // 401 Unauthorized
}
// Get and sanitize the demo name from POST data
$get_demo_name = ! empty( $_POST['getDemo'] ) ? sanitize_title( wp_unslash( $_POST['getDemo'] ) ) : '';
// If the demo name is empty after sanitization, reject the request
if ( empty( $get_demo_name ) ) {
wp_send_json_error( array( 'error' => 'Demo name is required.' ), 400 ); // Bad Request
}
// Construct the remote URL to the XML file
$demo_url = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . "wp-content/uploads/demolist/" . $get_demo_name . "/" . $get_demo_name . ".xml";
// Use wp_remote_head() to check if the remote file exists
$response = wp_remote_head( $demo_url );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
wp_send_json_error( array( 'error' => 'Demo XML not found.' ), 404 ); // Not Found
}
// Instantiate your custom importer and run the import using the remote URL
$importer = new Solace_Extra_WP_Import();
$importer->fetch_attachments = true;
$file = $demo_url;
$importer->import( $file );
wp_send_json_success(
array(
'message' => 'Import completed successfully.',
'file' => $file,
)
);
wp_die();
}
/**
* Solace Ajax Import
*/
public function call_ajax_import_customizer()
{
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'ajax-nonce' )) {
wp_send_json_error( array( 'error' => 'Invalid nonce!' ) );
}
// Check current user
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'error' => 'Unauthorized' ) );
}
// Delete import page site builder.
$this->delete_imported_site_builder();
// Get menus data from the API
$get_demo_name = ! empty( $_POST['getDemo'] ) ? sanitize_title( wp_unslash( $_POST['getDemo'] ) ) : '';
$demo = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $get_demo_name . "/wp-json/solace/v1/customizer-setting";
// Remote and local API URLs
$url_customizer = $demo;
$url_local_customizer = plugin_dir_url(__FILE__) . 'demo/customizer/casanova/casanova.json';
// Make remote request using wp_remote_get
$response_customizer = wp_remote_get($url_customizer);
// Check for errors
if (is_wp_error($response_customizer)) {
echo esc_html('Error: ' . $response_customizer->get_error_message());
wp_die();
}
// Get the HTTP response code
$http_code_customizer = wp_remote_retrieve_response_code($response_customizer);
// Check for errors based on HTTP response code
if ($http_code_customizer >= 400) {
// The response code is 400 or greater, switch to the backup URL
$response_customizer = wp_remote_get($url_local_customizer);
}
// Decode the response body
$data = json_decode(wp_remote_retrieve_body($response_customizer), true);
// Data checks.
if (!is_array($data)) {
esc_html_e('An error occurred while importing the customizer! The imported data is not valid.', 'solace-extra');
wp_die();
}
// If wp_css is set then import it.
if (function_exists('wp_update_custom_css_post') && isset($data['wp_css']) && '' !== $data['wp_css']) {
wp_update_custom_css_post($data['wp_css']);
}
// Loop through the mods.
foreach ($data as $key => $val) {
if ($key === 'solace_menu_locations') {
continue;
}
// Save the mod.
set_theme_mod($key, $val);
}
// Set menu locations
if (isset($data['solace_menu_locations'])) {
foreach ($data['solace_menu_locations'] as $menuLocation) {
if ($menuLocation['location'] && $menuLocation['menu_slug']) {
$get_slug_menu = $menuLocation['menu_slug'];
$menu = wp_get_nav_menu_object($get_slug_menu);
if ($menu && $menu->slug == $get_slug_menu) {
$locations = get_nav_menu_locations();
$locations[$menuLocation['location']] = $menu->term_id;
set_theme_mod('nav_menu_locations', $locations);
}
}
}
}
$api_url = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $get_demo_name . '/wp-json/solace/v1/customizer-setting?timestamp=' . time();
$response = wp_remote_get($api_url);
if (!is_wp_error($response)) {
$data = json_decode(wp_remote_retrieve_body($response), true);
if ($data) {
$remote_logo_header_url = $data['logourl'];
$this->download_and_update_logo_logo_from_live_url($remote_logo_header_url);
$remote_logo_footer_url = $data['logofooterurl'];
$this->download_and_update_logo_footer_logo_from_live_url($remote_logo_footer_url);
}
}
// Define the remote URL to the XML file.
$demo_url = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . "wp-content/uploads/demolist/" . $get_demo_name . "/" . $get_demo_name . ".xml";
// Make a GET request to download the file.
$response = wp_remote_get( $demo_url );
// Check if the request was successful and the response code is 200 (OK).
if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) == 200 ) {
// Get the body/content of the response.
$body = wp_remote_retrieve_body( $response );
// Get the upload directory path.
$upload_dir = wp_upload_dir();
// Set the local file path to save the downloaded XML.
$local_path = trailingslashit( $upload_dir['path'] ) . $get_demo_name . '.xml';
// Save the file to the server.
file_put_contents( $local_path, $body );
// Instantiate your custom importer and import from the local file.
$importer = new Solace_Extra_WP_Import();
$importer->fetch_attachments = true;
$importer->import( $local_path );
}
// Remote and local API URLs
$url_setting_options = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $get_demo_name . '/wp-json/solace/v1/setting-options';
// Make remote request using wp_remote_get
$response_setting_options = wp_remote_get($url_setting_options);
// Check for errors
if ( is_wp_error( $response_setting_options ) ) {
echo esc_html( 'Error setting options: ' . $response_setting_options->get_error_message() );
wp_die();
}
// Decode the response body
$data_setting_options = json_decode( wp_remote_retrieve_body( $response_setting_options ), true );
// Data checks.
if ( ! is_array( $data_setting_options ) ) {
esc_html_e( 'An error occurred while importing the setting options!', 'solace-extra' );
wp_die();
}
// Fix products sale shortcode.
if ( class_exists( 'WooCommerce' ) ) {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_posts'");
// looping products
foreach ( $post_ids as $id ) {
$product = wc_get_product( $id );
if ( $product && !is_wp_error( $product ) ) {
$sale_price = $product->get_sale_price();
if ( $sale_price ) {
$new_regular_price = $product->get_regular_price();
$product->set_regular_price( $new_regular_price + 1 );
$product->save();
$product->set_regular_price( $new_regular_price - 1 );
$product->save();
}
}
}
}
// Ensure the 'plugin.php' file is loaded to use WordPress plugin functions.
require_once ABSPATH . 'wp-admin/includes/plugin.php';
// Define the plugin path for LiteSpeed Cache.
$plugin = 'litespeed-cache/litespeed-cache.php';
$status_plugin_litespeed = get_option( 'solace_extra_deactivate_litespeed', false );
// Check status plugin litespeed.
if ( $status_plugin_litespeed ) {
// Check if the plugin is already active.
if ( ! is_plugin_active( $plugin ) ) {
// Attempt to activate the plugin.
$result = activate_plugin( $plugin );
// Check if the activation was successful or if an error occurred.
if ( is_wp_error( $result ) ) {
// Output the error message if activation failed.
// esc_html_e( 'Failed to activate the plugin: ', 'solace-extra' ) . $result->get_error_message();
} else {
// Output a success message if activation was successful.
// esc_html_e( 'LiteSpeed Cache plugin has been successfully activated.', 'solace-extra' );
}
} else {
// Output a message if the plugin is already active.
// esc_html_e( 'LiteSpeed Cache plugin is already active.', 'solace-extra' );
}
}
esc_html_e('Success! Import Customizer...', 'solace-extra');
wp_die();
}
public function download_and_update_logo_logo_from_live_url($remote_url) {
$remote_url = esc_url_raw($remote_url);
if (!filter_var($remote_url, FILTER_VALIDATE_URL)) {
esc_html_e('Invalid URL.', 'solace-extra');
return;
}
$allowed_domains = ['solacewp.com', 'www.solacewp.com'];
$parsed_url = wp_parse_url($remote_url);
if (empty($parsed_url['host']) || !in_array($parsed_url['host'], $allowed_domains, true)) {
esc_html_e('Domain not allowed.', 'solace-extra');
return;
}
$response = wp_remote_get($remote_url, ['timeout' => 10]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
esc_html_e('Failed to download file.', 'solace-extra');
return;
}
$file_content = wp_remote_retrieve_body($response);
$upload_dir = wp_upload_dir();
$file_name = sanitize_file_name(basename($remote_url));
$file_path = $upload_dir['path'] . '/' . $file_name;
if (!function_exists('wp_filesystem')) {
esc_html_e('Failed to initialize WP_Filesystem.', 'solace-extra');
return;
}
global $wp_filesystem;
WP_Filesystem();
$file_saved = $wp_filesystem->put_contents($file_path, $file_content, FS_CHMOD_FILE);
if ($file_saved === false) {
esc_html_e('Failed to save file.', 'solace-extra');
return;
}
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$file_type = wp_check_filetype($file_name);
if (!in_array($file_type['type'], $allowed_types, true)) {
esc_html_e('File type not allowed.', 'solace-extra');
wp_delete_file($file_path);
return;
}
$attachment = [
'post_title' => pathinfo($file_name, PATHINFO_FILENAME),
'post_mime_type' => $file_type['type'],
'post_status' => 'inherit',
];
$attachment_id = wp_insert_attachment($attachment, $file_path);
if (!$attachment_id) {
wp_delete_file($file_path);
esc_html_e('Failed to save attachment.', 'solace-extra');
return;
}
require_once ABSPATH . 'wp-admin/includes/image.php';
$attachment_data = wp_generate_attachment_metadata($attachment_id, $file_path);
wp_update_attachment_metadata($attachment_id, $attachment_data);
set_theme_mod('logo_logo', json_encode(['light' => $attachment_id, 'dark' => $attachment_id, 'same' => true]));
echo esc_html__('Media successfully downloaded and saved.', 'solace-extra');
}
public function download_and_update_logo_footer_logo_from_live_url($remote_url) {
$remote_url = esc_url_raw($remote_url);
if (!filter_var($remote_url, FILTER_VALIDATE_URL)) {
esc_html_e('Invalid URL.', 'solace-extra');
return;
}
$allowed_domains = ['solacewp.com', 'www.solacewp.com'];
$parsed_url = wp_parse_url($remote_url);
if (empty($parsed_url['host']) || !in_array($parsed_url['host'], $allowed_domains, true)) {
esc_html_e('Domain not allowed.', 'solace-extra');
return;
}
$response = wp_remote_get($remote_url, ['timeout' => 10]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
esc_html_e('Failed to download file.', 'solace-extra');
return;
}
$file_content = wp_remote_retrieve_body($response);
$upload_dir = wp_upload_dir();
$file_name = sanitize_file_name(basename($remote_url));
$file_path = $upload_dir['path'] . '/' . $file_name;
if (!function_exists('wp_filesystem')) {
esc_html_e('Failed to initialize WP_Filesystem.', 'solace-extra');
return;
}
global $wp_filesystem;
WP_Filesystem();
$file_saved = $wp_filesystem->put_contents($file_path, $file_content, FS_CHMOD_FILE);
if ($file_saved === false) {
esc_html_e('Failed to save file.', 'solace-extra');
return;
}
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$file_type = wp_check_filetype($file_name);
if (!in_array($file_type['type'], $allowed_types, true)) {
esc_html_e('File type not allowed.', 'solace-extra');
wp_delete_file($file_path);
return;
}
$attachment = [
'post_title' => pathinfo($file_name, PATHINFO_FILENAME),
'post_mime_type' => $file_type['type'],
'post_status' => 'inherit',
];
$attachment_id = wp_insert_attachment($attachment, $file_path);
if (!$attachment_id) {
wp_delete_file($file_path);
esc_html_e('Failed to save attachment.', 'solace-extra');
return;
}
require_once ABSPATH . 'wp-admin/includes/image.php';
$attachment_data = wp_generate_attachment_metadata($attachment_id, $file_path);
wp_update_attachment_metadata($attachment_id, $attachment_data);
$logo_logo_data = json_encode(['light' => $attachment_id, 'dark' => $attachment_id, 'same' => true]);
if ($attachment_id) {
update_post_meta($attachment_id, 'solace_import_images', true);
}
set_theme_mod('logo-footer_logo', $logo_logo_data);
echo esc_html__('Media successfully downloaded and saved.', 'solace-extra');
}
/**
* Available widgets
*
* Gather site's widgets into array with ID base, name, etc.
* Used by export and import functions.
*
* @since 0.4
* @global array $wp_registered_widget_updates
* @return array Widget information
*/
public function available_widgets()
{
global $wp_registered_widget_controls;
$widget_controls = $wp_registered_widget_controls;
$available_widgets = array();
foreach ($widget_controls as $widget) {
// No duplicates.
if (!empty($widget['id_base']) && !isset($available_widgets[$widget['id_base']])) {
$available_widgets[$widget['id_base']]['id_base'] = $widget['id_base'];
$available_widgets[$widget['id_base']]['name'] = $widget['name'];
}
}
return apply_filters('available_widgets', $available_widgets);
}
/**
* Solace Ajax Import Widget
*/
public function call_ajax_import_widget()
{
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'ajax-nonce' )) {
$response = array('error' => 'Invalid nonce!');
echo wp_json_encode($response);
wp_die();
}
// Check current user
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'error' => 'Unauthorized' ) );
}
// Get menus data from the API
$get_demo_name = ! empty( $_POST['getDemo'] ) ? sanitize_title( wp_unslash( $_POST['getDemo'] ) ) : '';
$demo = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $get_demo_name . "/wp-json/solace/v1/widgets";
// Remote API URL
$url_widget = $demo;
// Make remote request using wp_remote_get
$response_widget = wp_remote_get($url_widget);
// Check for errors
if (is_wp_error($response_widget)) {
echo esc_html( 'Error: ' . $response_widget->get_error_message() );
wp_die();
}
// Get the HTTP response code
$http_code_widget = wp_remote_retrieve_response_code($response_widget);
// Check for errors based on HTTP response code
if ($http_code_widget >= 400) {
// The response code is 400 or greater, handle accordingly
// For example, switch to a backup URL
// $url_local_widget = plugin_dir_url(__FILE__) . 'demo/customizer/casanova/casanova.json';
// $response_widget = wp_remote_get($url_local_widget);
}
// Decode the response body
$data = json_decode(wp_remote_retrieve_body($response_widget), true);
$data = (object) $data;
// Filter Widget menu
foreach ($data as $widget_area => $widgets) {
foreach ($widgets as $widget_id => $item) {
// Check if the 'nav_menu_slug' index exists in the $item array
if (isset($item['nav_menu_slug'])) {
// Get the menu slug from the data
$menu_slug = $item['nav_menu_slug'];
// Get the menu object based on the menu slug
$menu = get_term_by('slug', $menu_slug, 'nav_menu');
// Check if the menu is found before updating the values
if ($menu) {
// Update 'nav_menu' and 'nav_menu_id' with the term_id of the menu
$data->$widget_area[$widget_id]['nav_menu'] = $menu->term_id;
$data->$widget_area[$widget_id]['nav_menu_id'] = $menu->term_id;
}
}
}
}
$this->import_widgets($data);
esc_html_e('Success! Import Widgets...', 'solace-extra');
wp_die();
}
/**
* Import widget JSON data
*
* @since 0.4
* @global array $wp_registered_sidebars
* @param object $data JSON widget data from .wie file.
* @return array Results array
*/
function import_widgets($data)
{
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'ajax-nonce' )) {
$response = array('error' => 'Invalid nonce!');
echo wp_json_encode($response);
wp_die();
}
global $wp_registered_sidebars;
// Have valid data?
// If no data or could not decode.
if (empty($data) || !is_object($data)) {
wp_die(
esc_html__('Import data is invalid.', 'solace-extra'),
'',
array(
'back_link' => true,
)
);
}
// Hook before import.
do_action('before_import');
$data = apply_filters('import_widgets', $data);
// Get all available widgets site supports.
$available_widgets = $this->available_widgets();
// Get all existing widget instances.
$widget_instances = array();
foreach ($available_widgets as $widget_data) {
$widget_instances[$widget_data['id_base']] = get_option('widget_' . $widget_data['id_base']);
}
// Begin results.
$results = array();
// Loop import data's sidebars.
foreach ($data as $sidebar_id => $widgets) {
// Skip inactive widgets (should not be in export file).
if ('wp_inactive_widgets' === $sidebar_id) {
continue;
}
// Check if sidebar is available on this site.
// Otherwise add widgets to inactive, and say so.
if (isset($wp_registered_sidebars[$sidebar_id])) {
$sidebar_available = true;
$use_sidebar_id = $sidebar_id;
$sidebar_message_type = 'success';
$sidebar_message = '';
} else {
$sidebar_available = false;
$use_sidebar_id = 'wp_inactive_widgets'; // Add to inactive if sidebar does not exist in theme.
$sidebar_message_type = 'error';
$sidebar_message = esc_html__('Widget area does not exist in theme (using Inactive)', 'solace-extra');
}
// Result for sidebar
// Sidebar name if theme supports it; otherwise ID.
$results[$sidebar_id]['name'] = !empty($wp_registered_sidebars[$sidebar_id]['name']) ? $wp_registered_sidebars[$sidebar_id]['name'] : $sidebar_id;
$results[$sidebar_id]['message_type'] = $sidebar_message_type;
$results[$sidebar_id]['message'] = $sidebar_message;
$results[$sidebar_id]['widgets'] = array();
// Loop widgets.
foreach ($widgets as $widget_instance_id => $widget) {
$fail = false;
// Get id_base (remove -# from end) and instance ID number.
$id_base = preg_replace('/-[0-9]+$/', '', $widget_instance_id);
$instance_id_number = str_replace($id_base . '-', '', $widget_instance_id);
// Does site support this widget?
if (!$fail && !isset($available_widgets[$id_base])) {
$fail = true;
$widget_message_type = 'error';
$widget_message = esc_html__('Site does not support widget', 'solace-extra'); // Explain why widget not imported.
}
// Filter to modify settings object before conversion to array and import
// Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below)
// Ideally the newer wie_widget_settings_array below will be used instead of this.
$widget = apply_filters('wie_widget_settings', $widget);
// Convert multidimensional objects to multidimensional arrays
// Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays
// Without this, they are imported as objects and cause fatal error on Widgets page
// If this creates problems for plugins that do actually intend settings in objects then may need to consider other approach: https://wordpress.org/support/topic/problem-with-array-of-arrays
// It is probably much more likely that arrays are used than objects, however.
$widget = json_decode(wp_json_encode($widget), true);
// Filter to modify settings array
// This is preferred over the older wie_widget_settings filter above
// Do before identical check because changes may make it identical to end result (such as URL replacements).
$widget = apply_filters('wie_widget_settings_array', $widget);
// Does widget with identical settings already exist in same sidebar?
if (!$fail && isset($widget_instances[$id_base])) {
// Get existing widgets in this sidebar.
$sidebars_widgets = get_option('sidebars_widgets');
$sidebar_widgets = isset($sidebars_widgets[$use_sidebar_id]) ? $sidebars_widgets[$use_sidebar_id] : array(); // Check Inactive if that's where will go.
// Loop widgets with ID base.
$single_widget_instances = !empty($widget_instances[$id_base]) ? $widget_instances[$id_base] : array();
foreach ($single_widget_instances as $check_id => $check_widget) {
// Is widget in same sidebar and has identical settings?
if (in_array("$id_base-$check_id", $sidebar_widgets, true) && (array) $widget === $check_widget) {
$fail = true;
$widget_message_type = 'warning';
// Explain why widget not imported.
$widget_message = esc_html__('Widget already exists', 'solace-extra');
break;
}
}
}
// No failure.
if (!$fail) {
// Add widget instance
$single_widget_instances = get_option('widget_' . $id_base); // All instances for that widget ID base, get fresh every time.
$single_widget_instances = !empty($single_widget_instances) ? $single_widget_instances : array(
'_multiwidget' => 1, // Start fresh if have to.
);
$single_widget_instances[] = $widget; // Add it.
// Get the key it was given.
end($single_widget_instances);
$new_instance_id_number = key($single_widget_instances);
// If key is 0, make it 1
// When 0, an issue can occur where adding a widget causes data from other widget to load,
// and the widget doesn't stick (reload wipes it).
if ('0' === strval($new_instance_id_number)) {
$new_instance_id_number = 1;
$single_widget_instances[$new_instance_id_number] = $single_widget_instances[0];
unset($single_widget_instances[0]);
}
// Move _multiwidget to end of array for uniformity.
if (isset($single_widget_instances['_multiwidget'])) {
$multiwidget = $single_widget_instances['_multiwidget'];
unset($single_widget_instances['_multiwidget']);
$single_widget_instances['_multiwidget'] = $multiwidget;
}
// Update option with new widget.
update_option('widget_' . $id_base, $single_widget_instances);
// Assign widget instance to sidebar.
// Which sidebars have which widgets, get fresh every time.
$sidebars_widgets = get_option('sidebars_widgets');
// Avoid rarely fatal error when the option is an empty string
// https://github.com/churchthemes/widget-importer-exporter/pull/11.
if (!$sidebars_widgets) {
$sidebars_widgets = array();
}
// Use ID number from new widget instance.
$new_instance_id = $id_base . '-' . $new_instance_id_number;
// Add new instance to sidebar.
$sidebars_widgets[$use_sidebar_id][] = $new_instance_id;
// Save the amended data.
update_option('sidebars_widgets', $sidebars_widgets);
// After widget import action.
$after_widget_import = array(
'sidebar' => $use_sidebar_id,
'sidebar_old' => $sidebar_id,
'widget' => $widget,
'widget_type' => $id_base,
'widget_id' => $new_instance_id,
'widget_id_old' => $widget_instance_id,
'widget_id_num' => $new_instance_id_number,
'widget_id_num_old' => $instance_id_number,
);
do_action('after_widget_import', $after_widget_import);
// Success message.
if ($sidebar_available) {
$widget_message_type = 'success';
$widget_message = esc_html__('Imported', 'solace-extra');
} else {
$widget_message_type = 'warning';
$widget_message = esc_html__('Imported to Inactive', 'solace-extra');
}
}
// Result for widget instance
$results[$sidebar_id]['widgets'][$widget_instance_id]['name'] = isset($available_widgets[$id_base]['name']) ? $available_widgets[$id_base]['name'] : $id_base; // Widget name or ID if name not available (not supported by site).
$results[$sidebar_id]['widgets'][$widget_instance_id]['title'] = !empty($widget['title']) ? $widget['title'] : esc_html__('No Title', 'solace-extra'); // Show "No Title" if widget instance is untitled.
$results[$sidebar_id]['widgets'][$widget_instance_id]['message_type'] = $widget_message_type;
$results[$sidebar_id]['widgets'][$widget_instance_id]['message'] = $widget_message;
}
}
// Hook after import.
do_action('after_import');
}
/**
* Deletes attachments imported during the Solace import process.
*/
public function delete_sidebars_widgets()
{
update_option('sidebars_widgets', array());
}
/**
* Deletes customizers imported during the Solace import process.
*/
public function delete_customizers()
{
// Remove all them mods
remove_theme_mods();
}
/**
* Deletes attachments imported during the Solace import process.
*/
public function delete_imported_attachments()
{
global $wpdb;
$meta_key = 'solace_import_images';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_results(
$wpdb->prepare(
"SELECT *
FROM {$wpdb->prefix}postmeta
WHERE CONVERT(meta_key USING utf8) LIKE %s",
'%' . $wpdb->esc_like($meta_key) . '%'
)
);
// Remove attachment
if (!empty($post_ids)) {
foreach ($post_ids as $id) {
wp_delete_attachment($id->post_id, true);
}
}
}
/**
* Deletes imported terms from the 'category' taxonomy based on a specific term meta key.
*/
public function delete_imported_terms_category()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$term_ids = $wpdb->get_col("SELECT term_id FROM {$wpdb->termmeta} WHERE meta_key='solace_imported_term_category'");
foreach ($term_ids as $id) {
wp_delete_term($id, 'category');
}
}
/**
* Deletes imported terms from the 'product_cat' taxonomy based on a specific term meta key.
*/
public function delete_imported_terms_product_cat()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$term_ids = $wpdb->get_col("SELECT term_id FROM {$wpdb->termmeta} WHERE meta_key='solace_imported_term_product_cat'");
foreach ($term_ids as $id) {
wp_delete_term($id, 'product_cat');
}
}
/**
* Deletes imported terms from the 'product_tag' taxonomy based on a specific term meta key.
*/
public function delete_imported_terms_product_tag()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$term_ids = $wpdb->get_col("SELECT term_id FROM {$wpdb->termmeta} WHERE meta_key='solace_imported_term_product_tag'");
foreach ($term_ids as $id) {
wp_delete_term($id, 'product_tag');
}
}
/**
* Deletes imported terms from the 'post_tag' taxonomy based on a specific term meta key.
*/
public function delete_imported_terms_post_tag()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$term_ids = $wpdb->get_col("SELECT term_id FROM {$wpdb->termmeta} WHERE meta_key='solace_imported_term_post_tag'");
foreach ($term_ids as $id) {
wp_delete_term($id, 'post_tag');
}
}
/**
* Deletes posts imported during the Solace import process.
*/
public function delete_imported_posts()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_posts'");
// Remove posts
foreach ($post_ids as $id) {
$thumbnail_id = get_post_thumbnail_id($id);
wp_delete_attachment($thumbnail_id, true);
wp_delete_post($id, true);
}
}
/**
* Deletes menu items imported during the Solace import process.
*/
public function delete_imported_menu_items()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_menu_item'");
// Remove menu items.
foreach ($post_ids as $id) {
wp_delete_post($id, true);
}
}
/**
* Deletes products imported during the Solace import process.
*/
public function delete_imported_products()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_products'");
// Remove products
foreach ($post_ids as $id) {
$thumbnail_id = get_post_thumbnail_id($id);
wp_delete_attachment($thumbnail_id, true);
wp_delete_post($id, true);
}
}
/**
* Deletes pages imported during the Solace import process.
*/
public function delete_imported_pages()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_pages'");
// Remove pages
foreach ($post_ids as $id) {
$thumbnail_id = get_post_thumbnail_id($id);
wp_delete_attachment($thumbnail_id, true);
wp_delete_post($id, true);
}
}
/**
* Deletes pages imported during the Solace import process.
*/
public function delete_imported_site_builder()
{
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_site_builder'");
// Remove pages
foreach ($post_ids as $id) {
$thumbnail_id = get_post_thumbnail_id($id);
wp_delete_attachment($thumbnail_id, true);
wp_delete_post($id, true);
}
}
/**
* Delete all posts of the custom post type 'solace-sitebuilder'
* that have the meta key 'solace_wp_importer_site_builder'.
*
* This function uses WordPress functions instead of direct SQL queries
* to maintain compatibility with coding standards and caching.
*
* @return void
*/
function delete_wp_importer_post_type_sitebuilder() {
// Query posts using get_posts() with meta_query
$posts = get_posts(array(
'post_type' => 'solace-sitebuilder',
'posts_per_page' => -1, // Get all matching posts
'fields' => 'ids', // Return only IDs
'post_status' => 'any',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
array(
'key' => 'solace_wp_importer_site_builder',
'compare' => 'EXISTS',
),
),
));
// Delete each post permanently.
foreach ($posts as $post_id) {
wp_delete_post($post_id, true); // true = force delete (skip Trash).
}
}
/**
* Deletes all Elementor templates.
*
* This function retrieves all Elementor template IDs from the WordPress database
* and deletes each template using the wp_delete_post function.
*
* @return void
*/
function delete_all_elementor_templates() {
global $wpdb;
remove_all_actions( 'wp_trash_post' );
remove_all_actions( 'before_delete_post' );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all elementor templates.
$template_ids = $wpdb->get_col("
SELECT {$wpdb->posts}.ID
FROM {$wpdb->posts}
INNER JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id
WHERE {$wpdb->posts}.post_type = 'elementor_library'
AND {$wpdb->postmeta}.meta_key = 'solace_import_elementor_kit'
");
// $template_ids = $wpdb->get_col("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='solace_import_elementor_kit'");
// Loop through each template ID and delete the template.
foreach ( $template_ids as $id ) {
wp_delete_post( $id, true );
}
}
/**
* Deletes previously imported attachments and posts as part of the cleanup process.
*/
public function delete_previously_imported()
{
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'error' => 'Unauthorized!' ), 403 );
}
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce(
sanitize_text_field( wp_unslash( $_POST['nonce'] ) ),
'ajax-nonce'
) ) {
wp_send_json_error( array( 'error' => 'Invalid nonce!' ), 403 );
}
$this->delete_wp_importer_post_type_sitebuilder();
$this->delete_imported_terms_category();
$this->delete_imported_terms_post_tag();
$this->delete_imported_attachments();
$this->delete_imported_posts();
$this->delete_imported_pages();
$this->delete_imported_terms_product_cat();
$this->delete_imported_terms_product_tag();
$this->delete_imported_products();
$this->delete_elementor_current_setting();
wp_die();
}
/**
* Run the "delete previously imported" cleanup and return, without any
* AJAX/nonce/wp_die plumbing.
*
* delete_previously_imported() above is an admin-ajax handler: it does its
* own capability + 'ajax-nonce' check and ends with wp_die(), so it can't be
* reused from other contexts. This variant performs the exact same deletion
* sequence but is safe to call from the REST bridge, which authorizes the
* request itself (manage_options) and needs a normal return value.
*
* Only content tagged with the importer's own meta keys is removed
* (solace_import_pages / _posts / _products / _images, solace_imported_term_*,
* and the solace-sitebuilder CPT) — hand-authored content is left untouched.
*
* @return void
*/
public function delete_previously_imported_data() {
$this->delete_wp_importer_post_type_sitebuilder();
$this->delete_imported_terms_category();
$this->delete_imported_terms_post_tag();
$this->delete_imported_attachments();
$this->delete_imported_posts();
$this->delete_imported_pages();
$this->delete_imported_terms_product_cat();
$this->delete_imported_terms_product_tag();
$this->delete_imported_products();
$this->delete_elementor_current_setting();
}
function delete_elementor_current_setting(){
$custom_typography = [
[
'_id' => 'primary',
'title' => 'Smaller',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'secondary',
'title' => 'Logo Title / Subtitle',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'text',
'title' => 'Solace Base',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'accent',
'title' => 'Button',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_body_font_family',
'title' => 'Solace Base',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_h1_font_family_general',
'title' => 'Solace H1',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_h2_font_family_general',
'title' => 'Solace H2',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_h3_font_family_general',
'title' => 'Solace H3',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_h4_font_family_general',
'title' => 'Solace H4',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_h5_font_family_general',
'title' => 'Solace H5',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => 'px',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_h6_font_family_general',
'title' => 'Solace H6',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_smaller_font_family',
'title' => 'Smaller',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_logotitle_font_family',
'title' => 'Logo Title / Subtitle',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
],
[
'_id' => 'solace_button_font_family',
'title' => 'Button',
'typography_typography' => 'custom',
'typography_font_family' => '',
'typography_font_size' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_weight' => '',
'typography_text_transform' => '',
'typography_line_height' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_font_size_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_line_height_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_tablet' => [
'unit' => '',
'size' => '',
'sizes' => []
],
'typography_letter_spacing_mobile' => [
'unit' => '',
'size' => '',
'sizes' => []
]
]
];
if (class_exists('Elementor\Plugin')) {
$elementor_active_kit = get_option( 'elementor_active_kit' );
// Retrieve the Elementor page settings meta data.
$meta = get_post_meta( $elementor_active_kit, '_elementor_page_settings', true );
if ( $meta ) {
\Elementor\Plugin::$instance->kits_manager->update_kit_settings_based_on_option( 'system_typography', $custom_typography );
}
}
}
function install_and_activate_theme() {
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'ajax-nonce' )) {
$response = array('error' => 'Invalid nonce!');
echo wp_json_encode($response);
wp_die();
}
// Capability check — only administrators may install/activate themes.
if ( ! current_user_can( 'manage_options' ) ) {
$response = array( 'error' => 'Forbidden' );
echo wp_json_encode( $response );
wp_die();
}
solace_disable_image_processing();
// Set import zip complete option to false
update_option('solace_extra_import_zip_complete', false);
set_theme_mod('solace_extra_import_zip_complete', false);
$theme_slug = 'solace';
if (!(get_option('template') === $theme_slug || get_option('stylesheet') === $theme_slug)) {
if (!(file_exists(trailingslashit(WP_CONTENT_DIR) . 'themes/' . $theme_slug))) {
$api = themes_api('theme_information', array('slug' => $theme_slug));
if (is_wp_error($api)) {
esc_html_e('Failed to retrieve theme information from WordPress.org.', 'solace-extra');
return;
}
$theme_zip = download_url($api->download_link);
if (!is_wp_error($theme_zip)) {
$theme_dir = trailingslashit(WP_CONTENT_DIR) . 'themes';
$zip = new ZipArchive;
if ($zip->open($theme_zip) === true) {
$zip->extractTo($theme_dir);
$zip->close();
wp_delete_file($theme_zip);
} else {
esc_html_e('Failed to open ZIP file.', 'solace-extra');
}
}
}
switch_theme($theme_slug);
if (!(get_option('template') === $theme_slug || get_option('stylesheet') === $theme_slug)) {
esc_html_e('Theme Installed and Activated', 'solace-extra');
} else {
esc_html_e('Failed to activate theme.', 'solace-extra');
}
} else {
esc_html_e('Theme is already installed and active.', 'solace-extra');
}
}
function install_and_activate_plugins()
{
// Verify user permissions and nonce
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'ajax-nonce' ) ) {
wp_send_json_error( [ 'error' => 'Invalid nonce!' ] );
}
if ( ! current_user_can( 'install_plugins' ) ) {
wp_send_json_error( [ 'error' => 'Unauthorized action!' ] );
}
update_option( 'elementor_onboarded', true );
$demo_name = ! empty( $_POST['getDemo'] ) ? sanitize_text_field( wp_unslash( $_POST['getDemo'] ) ) : '';
// Remote and local API URLs
$url = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL . $demo_name ) . 'wp-json/solace/v1/required-plugin';
// Make remote request using wp_remote_get
$response = wp_remote_get($url);
if ( is_wp_error( $response ) ) {
wp_send_json_error( [ 'error' => 'Failed to fetch plugins data.' ] );
}
$decoded_data = json_decode( wp_remote_retrieve_body( $response ), true );
$plugins_to_install = null;
if ( $decoded_data['page_builder'] && $decoded_data['ecommerce'] ) {
$plugins_to_install = array(
'elementor',
'woocommerce',
);
} else if ( $decoded_data['page_builder'] && ! $decoded_data['ecommerce'] ) {
$plugins_to_install = array(
'elementor',
);
} else {
$plugins_to_install = array(
'elementor',
);
}
if ( ! function_exists( 'plugins_api' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
}
if ( ! class_exists( 'WP_Upgrader' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
}
foreach ( $plugins_to_install as $plugin_slug ) {
if ( ! is_plugin_active( "$plugin_slug/$plugin_slug.php" ) && ! file_exists( WP_PLUGIN_DIR . "/$plugin_slug/$plugin_slug.php" ) ) {
$api = plugins_api( 'plugin_information', [ 'slug' => $plugin_slug ] );
if ( isset( $api->download_link ) ) {
$plugin_zip = download_url( $api->download_link );
if ( ! is_wp_error( $plugin_zip ) ) {
$zip = new ZipArchive;
if ( $zip->open( $plugin_zip ) === true ) {
$zip->extractTo( WP_PLUGIN_DIR );
$zip->close();
wp_delete_file( $plugin_zip );
}
}
}
}
}
foreach ( $plugins_to_install as $plugin_slug ) {
activate_plugin( "$plugin_slug/$plugin_slug.php" );
}
update_option( 'permalink_structure', '/%postname%/' );
flush_rewrite_rules();
$url = admin_url( 'admin.php?page=elementor-app#onboarding' );
$response = wp_remote_get( $url );
if ( is_wp_error( $response ) ) {
esc_html_e( 'Onboarding True', 'solace-extra');
} else {
esc_html_e( 'Onboarding False', 'solace-extra');
}
// Check if the LiteSpeed Cache plugin is active by looking in the 'active_plugins' option.
$active_plugins = get_option( 'active_plugins' );
if ( in_array( 'litespeed-cache/litespeed-cache.php', $active_plugins ) ) {
update_option( 'solace_extra_deactivate_litespeed', true );
// Ensure the 'plugin.php' file is loaded to use WordPress plugin functions.
require_once ABSPATH . 'wp-admin/includes/plugin.php';
// Define the plugin path for LiteSpeed Cache.
$plugin = 'litespeed-cache/litespeed-cache.php';
// Check if the plugin is active.
if ( is_plugin_active( $plugin ) ) {
// Deactivate the plugin.
deactivate_plugins( $plugin );
// Check if the plugin was successfully deactivated.
if ( ! is_plugin_active( $plugin ) ) {
// Output a success message.
// esc_html_e( 'LiteSpeed Cache plugin has been successfully deactivated.', 'solace-extra');
} else {
// Output an error message if the plugin is still active.
// esc_html_e( 'Failed to deactivate the LiteSpeed Cache plugin.', 'solace-extra');
}
} else {
// Output a message if the plugin is not active.
// esc_html_e( 'LiteSpeed Cache plugin is already inactive.', 'solace-extra');
}
} else {
update_option( 'solace_extra_deactivate_litespeed', false );
}
}
public function import_zip()
{
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'ajax-nonce' )) {
$response = array('error' => 'Invalid nonce!');
echo wp_json_encode($response);
wp_die();
}
// Capability check — only administrators may import/destructive-reset.
if ( ! current_user_can( 'manage_options' ) ) {
$response = array( 'error' => 'Forbidden' );
echo wp_json_encode( $response );
wp_die();
}
set_theme_mod('solace_extra_import_zip_complete', false); // Menyimpan status impor sebagai false
// Delete menu items.
$this->delete_imported_menu_items();
// Delete menu previously.
$prevDemo = ! empty( $_POST['prevDemo'] ) ? sanitize_text_field( wp_unslash ( $_POST['prevDemo'] ) ) : '';
if (!empty($prevDemo) && $prevDemo !== 'blank') {
// Split the string into an array using commas as separators
$arrayPrevDemo = explode(',', $prevDemo);
// Remove leading and trailing whitespaces from each element in the array
$arrayPrevDemo = array_map('trim', $arrayPrevDemo);
// Remove duplicate values from the array, keeping only unique values
$arrayPrevDemo = array_unique($arrayPrevDemo);
foreach ($arrayPrevDemo as $get_demo) {
$demo = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $get_demo . "/wp-json/solace/v1/menus";
$response = wp_remote_get($demo);
$menus_data = wp_remote_retrieve_body($response);
$menus = json_decode($menus_data);
if (empty($get_demo)) {
esc_html_e('Erorr remove menu', 'solace-extra');
die;
}
// Loop through each menu
if (!empty($menus)) {
$menus_local = wp_get_nav_menus();
foreach ($menus as $menu) {
wp_delete_nav_menu($menu->slug);
// Loop through each menu to be deleted
foreach ($menus_local as $menu_local) {
// Check if there is a duplicate by appending 'duplicate' at the end of slug
if (strpos($menu_local->slug, $menu->slug . '-duplicate') !== false) {
// If found, delete the menu
wp_delete_nav_menu($menu_local->slug);
}
}
}
}
}
}
if ( class_exists( 'Elementor\Plugin' ) ) {
// Delete Elementor templates kit.
$this->delete_all_elementor_templates();
// Delete widgets.
$this->delete_sidebars_widgets();
// Delete customizers.
$this->delete_customizers();
}
// $this->install_and_activate_elementor();
$demo_name = isset($_POST['getDemoName']) ? sanitize_text_field(wp_unslash($_POST['getDemoName'])) : '';
$this->import_process->push_to_queue(array(
'action' => 'demo_name',
'demo_name' => $demo_name
));
$this->import_process->save()->dispatch();
}
/**
* Get unique posts by title or slug, filtering by a specific post meta.
*
* @param string $title The title or slug to search for.
* @return array|null An array of posts with the specified title/slug and meta, or null if none found.
*/
public function get_unique_posts_by_title( $title, $demo_name ) {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here.
$post_ids = $wpdb->get_col(
$wpdb->prepare(
"
SELECT p.ID
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_title = %s
",
$demo_name,
$title
)
);
// Check if the array has more than one element.
if (count($post_ids) > 1) {
// Get the last element of the array.
$last_post_id = end($post_ids);
} else {
// If there's only one element or the array is empty
$last_post_id = !empty($post_ids) ? $post_ids[0] : null;
}
return $last_post_id;
}
// Function to update the WordPress menu
public function call_ajax_import_menu()
{
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce( sanitize_text_field( wp_unslash ( $_POST['nonce'] ) ), 'ajax-nonce' )) {
$response = array('error' => 'Invalid nonce!');
echo wp_json_encode($response);
wp_die();
}
// Check current user
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'error' => 'Unauthorized' ) );
}
// Get menus data from the API
$get_demo_url = ! empty( $_POST['getDemo'] ) ? sanitize_title( wp_unslash( $_POST['getDemo'] ) ) : '';
$demo_name = ! empty( $_POST['getDemo'] ) ? sanitize_title( 'solace_extra_' . wp_unslash( $_POST['getDemo'] ) ) : '';
$demo = trailingslashit( SOLACE_EXTRA_DEMO_IMPORT_URL ) . $get_demo_url . "/wp-json/solace/v1/menus";
$response = wp_remote_get($demo);
$menus_data = wp_remote_retrieve_body($response);
$menus = json_decode($menus_data);
if (empty($get_demo_url)) {
esc_html_e('Error menu demo url', 'solace-extra');
die;
}
// Get the list of menus
$menu_local = wp_get_nav_menus();
// Check if there are any menus available
if ( ! empty( $menu_local ) ) {
// Loop through the menus and check if any slug contains "-duplicate"
foreach ( $menu_local as $menu ) {
// Check if the slug contains '-duplicate'
if ( strpos( $menu->slug, '-duplicate' ) !== false ) {
// Delete the menu that has '-duplicate' in its slug
wp_delete_nav_menu( $menu->slug );
}
}
}
// Set Shop page.
update_option( 'woocommerce_shop_page_id', null );
// Loop through each menu
if (!empty($menus)) {
foreach ($menus as $menu) {
// Skip if the menu name is not 'test_mymenu'
// if ('test_mymenu' !== $menu->name) {
// continue;
// }
// Get the internal menu by slug
$internal_menu = get_term_by('slug', $menu->slug, 'nav_menu');
// Return if internal menu is not found
if ($internal_menu === false) {
return;
}
// Remove existing menu items
$menu_items = wp_get_nav_menu_items($internal_menu->term_id);
foreach ($menu_items as $menu_item) {
wp_delete_post($menu_item->ID, true);
}
// if ( isset( $internal_menu->term_id ) ) {
// $new_slug = 'solace-extra-' . $internal_menu->slug;
// wp_update_term( $internal_menu->term_id, 'nav_menu', array(
// 'slug' => $new_slug,
// ));
// }
// Build arguments for new menu items
$args = [];
$index = 0;
$index_menu_item = 0;
if (!empty($menu->terms)) {
foreach ($menu->terms as $menu_item) {
// Skip if the post status is 'draft'
if ('draft' === $menu_item->post_status) {
continue;
}
// Process menu item based on its type
$args_menu_item = $this->processMenuItem($menu_item, $demo_name, $index_menu_item);
// Update nav menu item and get the updated ID
$nav_menu_item_update = wp_update_nav_menu_item($internal_menu->term_id, 0, $args_menu_item);
update_post_meta($nav_menu_item_update, 'solace_import_menu_item', true);
// Update the object ID in the arguments
$args_menu_item['menu-item-object-id'] = $nav_menu_item_update;
// Output for debugging
// echo '<pre style="background: yellow;">';
// $this->outputDebuggingInfo($args_menu_item);
// echo "</pre>";
$args[$index++] = $args_menu_item;
$index_menu_item++;
}
}
// Update parent ID for menu items
$this->updateParentID($internal_menu->term_id, $args);
// Output for debugging
// echo '<pre style="background: lightgreen;">';
// outputDebuggingInfo($args);
// echo "</pre>";
}
}
wp_die();
}
/**
* Normalize a menu item URL from remote demo domain to local site.
*
* For custom-link menu items imported from a remote demo, the URL contains
* the remote domain and a demo-slug prefix (/clawnest/...). This method
* replaces the remote domain with the local site URL and strips the
* demo-slug prefix so the link works on the user's site.
*
* The allowed domain is extracted from SOLACE_EXTRA_DEMO_IMPORT_URL,
* so it adapts automatically when the demo server URL changes.
*
* @since 1.6.0
* @param string $url The original menu item URL from the demo data.
* @return string Normalized URL pointing to the local site.
*/
private function normalize_menu_url($url)
{
// Keep fragment-only or empty links as-is.
if (empty($url) || '#' === $url || 0 === strpos($url, '#')) {
return $url;
}
$parsed = wp_parse_url($url);
// Relative path — keep as-is, WP will resolve it.
if (empty($parsed['host'])) {
return $url;
}
// Build allowed domains from SOLACE_EXTRA_DEMO_IMPORT_URL.
$demo_host = wp_parse_url(SOLACE_EXTRA_DEMO_IMPORT_URL, PHP_URL_HOST);
$allowed = array($demo_host);
if ($demo_host && 'www.' !== substr($demo_host, 0, 4)) {
$allowed[] = 'www.' . $demo_host;
}
if (!in_array($parsed['host'], $allowed, true)) {
return $url;
}
$path = isset($parsed['path']) ? $parsed['path'] : '/';
// Strip the first path segment (the demo slug, e.g. /clawnest/… → /…).
$segments = explode('/', trim($path, '/'));
if (count($segments) > 1) {
array_shift($segments); // remove demo slug
$path = '/' . implode('/', $segments);
} elseif (count($segments) === 1) {
// URL is just the demo slug root — point to site root.
$path = '/';
}
// Preserve query string if present.
$query = isset($parsed['query']) ? '?' . $parsed['query'] : '';
return home_url($path . $query);
}
// Function to process menu item based on its type
// Function to process menu item based on its type
public function processMenuItem($menu_item, $demo_name, $index_menu_item)
{
$args = array();
// Common properties for all types (default assume original data).
$common_args = array(
'menu-item-title' => $menu_item->title,
'menu-item-object' => $menu_item->object,
'menu-item-object-id' => 0,
'menu-item-position' => $menu_item->menu_order,
'menu-item-type' => $menu_item->type,
'menu-item-url' => $this->normalize_menu_url($menu_item->url),
'menu-item-description' => $menu_item->description,
'menu-item-attr-title' => $menu_item->attr_title,
'menu-item-target' => $menu_item->target,
'menu-item-xfn' => $menu_item->xfn,
'menu-item-status' => $menu_item->post_status,
'menu-item-parent-id' => 0,
'menu-item-parent-title' => $menu_item->menu_item_parent_title,
'menu-item-parent-type' => $menu_item->menu_item_parent_type,
);
// Helper: fallback to custom link when target object is not found.
$fallback_to_custom = function () use ( $menu_item, $common_args ) {
return array_merge(
$common_args,
array(
'menu-item-type' => 'custom',
'menu-item-object' => 'custom',
'menu-item-object-id' => 0,
'menu-item-url' => $this->normalize_menu_url($menu_item->url),
)
);
};
if ($menu_item->type === 'taxonomy') {
// Process taxonomy type menu item.
$url = $menu_item->url;
$path = wp_parse_url($url, PHP_URL_PATH);
$slug = basename($path);
// Use the original taxonomy from the menu item when possible.
$taxonomy = ! empty( $menu_item->object ) && taxonomy_exists( $menu_item->object )
? $menu_item->object
: 'category';
$term = get_term_by('slug', $slug, $taxonomy);
if ($term && is_object($term)) {
$args = array_merge($common_args, array(
'menu-item-object-id' => $term->term_id,
));
} else {
// If the term does not exist locally, store as a custom link
// so the item is not left in "Pending" state.
$args = $fallback_to_custom();
}
} elseif ($menu_item->type === 'post_type') {
// Process post_type menu item.
$menu_item_url = $menu_item->url;
$path = wp_parse_url($menu_item_url, PHP_URL_PATH);
$slug = basename($path);
// ==== SPECIAL CASE: SHOP MENU ITEM ====
$is_shop_title = ( stripos( $menu_item->title, 'shop' ) !== false );
$is_shop_slug = ( strtolower( $slug ) === 'shop' );
if ( $is_shop_title && $is_shop_slug ) {
$local_shop_id = 0;
// 1) Coba cari page lokal dengan slug 'shop' terlebih dahulu.
if ( ! $local_shop_id ) {
$shop_page = get_page_by_path( 'shop', OBJECT, 'page' );
if ( $shop_page && ! is_wp_error( $shop_page ) && $shop_page->ID ) {
// Pastikan memang slug-nya 'shop', bukan page lain seperti 'hello-world'.
if ( isset( $shop_page->post_name ) && strtolower( $shop_page->post_name ) === 'shop' ) {
$local_shop_id = (int) $shop_page->ID;
}
}
}
// 2) Jika belum ada, coba dari WooCommerce shop page setting
// tapi validasi bahwa judul/slug memang berhubungan dengan "shop".
if ( ! $local_shop_id && function_exists( 'wc_get_page_id' ) ) {
$candidate_id = absint( wc_get_page_id( 'shop' ) );
if ( $candidate_id ) {
$candidate_post = get_post( $candidate_id );
if ( $candidate_post && ! is_wp_error( $candidate_post ) ) {
$title_ok = ( stripos( $candidate_post->post_title, 'shop' ) !== false );
$slug_ok = ( isset( $candidate_post->post_name ) && strtolower( $candidate_post->post_name ) === 'shop' );
if ( $title_ok || $slug_ok ) {
$local_shop_id = $candidate_id;
}
}
}
}
// 3) Kalau ada page lokal, pakai sebagai post_type page.
if ( $local_shop_id ) {
$local_url = get_permalink( $local_shop_id );
if ( ! $local_url || is_wp_error( $local_url ) ) {
$local_url = $menu_item->url; // fallback ke URL dari JSON.
}
$shop_args = $common_args;
$shop_args['menu-item-object'] = 'page';
$shop_args['menu-item-type'] = 'post_type';
$shop_args['menu-item-object-id'] = $local_shop_id;
$shop_args['menu-item-url'] = $local_url;
return $shop_args;
}
return $fallback_to_custom();
}
// ==== END SPECIAL CASE SHOP ====
// ==== SPECIAL CASE: HOME / FRONT PAGE MENU ITEM ====
$home_path_normalized = trim( (string) $path, '/' );
$home_demo_slug = str_replace( 'solace_extra_', '', $demo_name );
$home_path_parts = ( '' === $home_path_normalized )
? array()
: explode( '/', $home_path_normalized );
// Strip the demo slug prefix (e.g. "/nuvion/").
if ( ! empty( $home_path_parts ) && $home_path_parts[0] === $home_demo_slug ) {
array_shift( $home_path_parts );
}
$is_home_url = empty( $home_path_parts );
$is_page_like = ! empty( $menu_item->object ) && 'page' === $menu_item->object;
if ( $is_home_url && $is_page_like ) {
$local_home_id = 0;
// 1) Prefer a page imported from this demo that matches the
// menu item title (e.g. "Home").
$local_home_id = absint( $this->get_unique_posts_by_title( $menu_item->title, $demo_name ) );
// 2) Fallback to the local page with slug "home".
if ( ! $local_home_id ) {
$home_page = get_page_by_path( 'home', OBJECT, 'page' );
if ( $home_page && ! is_wp_error( $home_page ) && isset( $home_page->ID ) ) {
$local_home_id = (int) $home_page->ID;
}
}
// 3) Fallback to WordPress' configured front page.
if ( ! $local_home_id ) {
$front_id = absint( get_option( 'page_on_front', 0 ) );
if ( $front_id ) {
$local_home_id = $front_id;
}
}
if ( $local_home_id ) {
$local_url = get_permalink( $local_home_id );
if ( ! $local_url || is_wp_error( $local_url ) ) {
$local_url = home_url( '/' );
}
$home_args = $common_args;
$home_args['menu-item-object'] = 'page';
$home_args['menu-item-type'] = 'post_type';
$home_args['menu-item-object-id'] = $local_home_id;
$home_args['menu-item-url'] = $local_url;
return $home_args;
}
}
// ==== END SPECIAL CASE HOME ====
// Determine the post type from menu item object
$post_type = ! empty( $menu_item->object ) && post_type_exists( $menu_item->object )
? $menu_item->object
: 'post';
// Define arguments for querying a post based on the slug.
$query_args = array(
'name' => $slug,
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => -1,
);
// Retrieve the post based on the arguments.
$post = get_posts($query_args);
// Track resolved object ID.
$resolved_object_id = 0;
// Check if a post with the specified slug exists.
if ($post && $post[0]->post_title) {
$resolved_object_id = (int) $post[0]->ID;
} else {
// Second approach if the first one doesn't yield results.
// Check if a post with the menu item title exists.
$post_exists = post_exists($menu_item->title);
// Parse the URL and get the path
$path = wp_parse_url($menu_item->url, PHP_URL_PATH);
// Get the last segment from the path
$path_segments = explode('/', trim($path, '/'));
$last_segment = end($path_segments);
// Remove dashes and slashes, convert to desired format
$clean_slug = str_replace('-', ' ', $last_segment);
// Remove the prefix 'solace_extra_'
$get_demo_name = str_replace( 'solace_extra_', '', $demo_name );
// Set default variable $unique_posts (post ID or false).
$unique_posts = false;
if ( $get_demo_name === $clean_slug ) {
// Check property demo name.
$unique_posts = absint( $this->get_unique_posts_by_title( $menu_item->title, $demo_name ) );
} else {
// Check property demo name.
$unique_posts = absint( $this->get_unique_posts_by_title( $clean_slug, $demo_name ) );
}
// Check if the word "shop" exists in the text
$page_shop = stripos( $menu_item->title, 'shop' ) !== false;
if ( $page_shop ) {
// Check dan ganti "pages" dengan "page"
$clean_slug = str_replace( 'pages', 'page', $clean_slug );
$unique_posts = absint( $this->get_unique_posts_by_title( $clean_slug, $demo_name ) );
}
// Fix page blog.
if ( 'Blog' === $menu_item->title || 'blog' === $menu_item->title ) {
if ( 'post_type' === $menu_item->type && $menu_item->is_posts_page ) {
$post = get_post( $unique_posts );
if ( ! $post ) {
$new_page = array(
'post_title' => 'Blog',
'post_status' => 'publish',
'post_type' => 'page'
);
// Insert new page.
$page_id = wp_insert_post( $new_page );
$unique_posts = $page_id;
}
}
}
// Check if any unique posts were found and display their details.
if ($unique_posts) {
$post_exists = $unique_posts;
if ( $menu_item->is_posts_page ) {
$page_for_posts = $post_exists;
update_option( 'page_for_posts', $page_for_posts );
}
if ( $menu_item->is_shop_page && $menu_item->woocommerce_shop_page_id ) {
// Set Shop page.
update_option( 'woocommerce_shop_page_id', $post_exists );
}
}
if ($post_exists) {
$resolved_object_id = (int) $post_exists;
} else {
// Try to find by post type from menu item object first
$found_by_type = false;
if ( ! empty( $menu_item->object ) && post_type_exists( $menu_item->object ) ) {
$post_by_type = get_page_by_path($slug, OBJECT, $menu_item->object);
if ($post_by_type && $post_by_type->ID) {
$resolved_object_id = (int) $post_by_type->ID;
$found_by_type = true;
}
}
// Fallback to common post types if not found by specific type
if ( ! $found_by_type ) {
$page = get_page_by_path($slug, OBJECT, 'page');
$post = get_page_by_path($slug, OBJECT, 'post');
$singular = get_page_by_path($slug, OBJECT, 'any');
if ($page && $page->ID) {
$resolved_object_id = (int) $page->ID;
} elseif ($post && $post->ID) {
$resolved_object_id = (int) $post->ID;
} elseif ($singular && $singular->ID) {
$resolved_object_id = (int) $singular->ID;
}
}
}
}
// Extra handling for Shop page:
// In some cases the imported Shop page may have a different slug or title,
// so we try a more forgiving lookup when nothing has been resolved yet.
if ( ! $resolved_object_id && stripos( $menu_item->title, 'shop' ) !== false ) {
// 1) Use WooCommerce shop page if already configured.
$wc_shop_id = 0;
if ( function_exists( 'wc_get_page_id' ) ) {
$wc_shop_id = absint( wc_get_page_id( 'shop' ) );
} else {
$wc_shop_id = absint( get_option( 'woocommerce_shop_page_id', 0 ) );
}
if ( $wc_shop_id ) {
$resolved_object_id = $wc_shop_id;
}
}
// If still not resolved and looks like "Shop", try to find it by path / title.
if ( ! $resolved_object_id && stripos( $menu_item->title, 'shop' ) !== false ) {
// 2) Try by canonical path "shop".
$shop_page = get_page_by_path( 'shop', OBJECT, 'page' );
if ( $shop_page && ! is_wp_error( $shop_page ) && $shop_page->ID ) {
$resolved_object_id = (int) $shop_page->ID;
}
}
if ( ! $resolved_object_id && stripos( $menu_item->title, 'shop' ) !== false ) {
// 3) Fallback: search any published page that matches the title loosely.
$shop_candidates = get_posts(
array(
'post_type' => 'page',
'post_status' => 'publish',
's' => $menu_item->title,
'posts_per_page' => 1,
'fields' => 'ids',
)
);
if ( ! empty( $shop_candidates ) ) {
$resolved_object_id = (int) $shop_candidates[0];
}
}
if ( $resolved_object_id ) {
// Target content found secara umum (bukan kasus khusus Shop).
$args = array_merge(
$common_args,
array(
'menu-item-object-id' => $resolved_object_id,
)
);
} else {
// Nothing resolved -> turn into custom link to avoid "Pending" state.
$args = $fallback_to_custom();
}
} else {
// Process other types of menu item
$args = array_merge($common_args, array(
'menu-item-object-id' => $menu_item->object_id,
));
}
return $args;
}
// Function to update parent ID for menu items
public function updateParentID($menu_id, &$args)
{
$self_items = wp_get_nav_menu_items($menu_id);
$index = 0;
foreach ($args as &$args_menu_item) {
foreach ($self_items as $self_menu_item) {
// Update parent ID if the titles match
if (array_key_exists('menu-item-parent-title', $args_menu_item)) {
if ($args_menu_item['menu-item-parent-title'] === $self_menu_item->title) {
$args_menu_item['menu-item-parent-id'] = $self_menu_item->ID;
// wp_update_nav_menu_item($menu_id, $args_menu_item['menu-item-object-id'], $args_menu_item);
update_post_meta($args_menu_item['menu-item-object-id'], '_menu_item_menu_item_parent', $self_menu_item->ID);
}
}
}
$index++;
}
}
// Function to output debugging information
public function outputDebuggingInfo($data)
{
// Output debugging information
// print_r($data);
}
}