// Get visitor's IP if (!function_exists('get_user_ip')) { function get_user_ip() { return apply_filters('user_ip', ''); } } add_filter('user_ip', 'if_menu_user_ip'); function if_menu_user_ip($ip = '') { if (empty($ip)) { foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) { if (array_key_exists($key, $_SERVER) === true) { foreach (array_map('trim', explode(',', $_SERVER[$key])) as $ip) { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) { return $ip; } } } } } return $ip; } // Get visitor's Country Code, ex: US, ES, etc / XX or empty = Unknown if (!function_exists('get_user_country_code')) { function get_user_country_code() { return strtoupper(apply_filters('user_country_code', '')); } } add_filter('user_country_code', 'if_menu_user_country_code_woocommerce'); add_filter('user_country_code', 'if_menu_user_country_code_headers'); add_filter('user_country_code', 'if_menu_user_country_code_metaapis'); function if_menu_user_country_code_woocommerce($countryCode = '') { if (!$countryCode && class_exists('WC_Geolocation')) { $location = WC_Geolocation::geolocate_ip(); if ($location['country'] && !in_array($location['country'], array('A1', 'A2', 'EU', 'AP'))) { $countryCode = $location['country']; } } return $countryCode; } function if_menu_user_country_code_headers($countryCode = '') { if (empty($countryCode)) { foreach (array('HTTP_CF_IPCOUNTRY', 'X-AppEngine-country', 'CloudFront-Viewer-Country', 'GEOIP_COUNTRY_CODE', 'HTTP_X_COUNTRY_CODE', 'HTTP_X_GEO_COUNTRY') as $key) { if (isset($_SERVER[$key]) && $_SERVER[$key] && !in_array($_SERVER[$key], array('XX', 'ZZ', 'A1', 'A2', 'EU', 'AP'))) { return $_SERVER[$key]; } } } return $countryCode; } function if_menu_user_country_code_metaapis($countryCode = '') { if (!$countryCode) { $ip = get_user_ip(); if (false === ($countryCode = get_transient('ip-country-code-' . sanitize_key($ip)))) { $response = wp_remote_get('https://web-api.com/ip-info/' . $ip . '?key=api_01j9rbgkf8vz3rxa5d6p9h8j20'); $data = json_decode(wp_remote_retrieve_body($response) ?: '[]'); if (isset($data->country) && $data->country) { $countryCode = $data->country; set_transient('ip-country-code-' . sanitize_key($ip), $countryCode, WEEK_IN_SECONDS); } else { $countryCode = ''; } } } return $countryCode; } // Exit if accessed directly if ( !defined( 'ABSPATH' ) ) exit; // BEGIN ENQUEUE PARENT ACTION // AUTO GENERATED - Do not modify or remove comment markers above or below: if ( !function_exists( 'chld_thm_cfg_locale_css' ) ): function chld_thm_cfg_locale_css( $uri ){ if ( empty( $uri ) && is_rtl() && file_exists( get_template_directory() . '/rtl.css' ) ) $uri = get_template_directory_uri() . '/rtl.css'; return $uri; } endif; add_filter( 'locale_stylesheet_uri', 'chld_thm_cfg_locale_css' ); if ( !function_exists( 'chld_thm_cfg_parent_css' ) ): function chld_thm_cfg_parent_css() { wp_enqueue_style( 'chld_thm_cfg_parent', trailingslashit( get_template_directory_uri() ) . 'style.css', array( ) ); } endif; add_action( 'wp_enqueue_scripts', 'chld_thm_cfg_parent_css', 10 ); // END ENQUEUE PARENT ACTION namespace Elementor\TemplateLibrary; use Elementor\Api; use Elementor\Core\Common\Modules\Connect\Module as ConnectModule; use Elementor\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor template library remote source. * * Elementor template library remote source handler class is responsible for * handling remote templates from Elementor.com servers. * * @since 1.0.0 */ class Source_Remote extends Source_Base { const API_TEMPLATES_URL = 'https://my.elementor.com/api/connect/v1/library/templates'; const TEMPLATES_DATA_TRANSIENT_KEY_PREFIX = 'elementor_remote_templates_data_'; /** * Get remote template ID. * * Retrieve the remote template ID. * * @since 1.0.0 * @access public * * @return string The remote template ID. */ public function get_id() { return 'remote'; } /** * Get remote template title. * * Retrieve the remote template title. * * @since 1.0.0 * @access public * * @return string The remote template title. */ public function get_title() { return esc_html__( 'Remote', 'elementor' ); } /** * Register remote template data. * * Used to register custom template data like a post type, a taxonomy or any * other data. * * @since 1.0.0 * @access public */ public function register_data() {} /** * Get remote templates. * * Retrieve remote templates from Elementor.com servers. * * @since 1.0.0 * @access public * * @param array $args Optional. Not used in remote source. * * @return array Remote templates. */ public function get_items( $args = [] ) { $force_update = ! empty( $args['force_update'] ) && is_bool( $args['force_update'] ); $templates_data = $this->get_templates_data( $force_update ); $templates = []; foreach ( $templates_data as $template_data ) { $templates[] = $this->prepare_template( $template_data ); } return $templates; } /** * Get remote template. * * Retrieve a single remote template from Elementor.com servers. * * @since 1.0.0 * @access public * * @param int $template_id The template ID. * * @return array Remote template. */ public function get_item( $template_id ) { $templates = $this->get_items(); return $templates[ $template_id ]; } /** * Save remote template. * * Remote template from Elementor.com servers cannot be saved on the * database as they are retrieved from remote servers. * * @since 1.0.0 * @access public * * @param array $template_data Remote template data. * * @return \WP_Error */ public function save_item( $template_data ) { return new \WP_Error( 'invalid_request', 'Cannot save template to a remote source' ); } /** * Update remote template. * * Remote template from Elementor.com servers cannot be updated on the * database as they are retrieved from remote servers. * * @since 1.0.0 * @access public * * @param array $new_data New template data. * * @return \WP_Error */ public function update_item( $new_data ) { return new \WP_Error( 'invalid_request', 'Cannot update template to a remote source' ); } /** * Delete remote template. * * Remote template from Elementor.com servers cannot be deleted from the * database as they are retrieved from remote servers. * * @since 1.0.0 * @access public * * @param int $template_id The template ID. * * @return \WP_Error */ public function delete_template( $template_id ) { return new \WP_Error( 'invalid_request', 'Cannot delete template from a remote source' ); } /** * Export remote template. * * Remote template from Elementor.com servers cannot be exported from the * database as they are retrieved from remote servers. * * @since 1.0.0 * @access public * * @param int $template_id The template ID. * * @return \WP_Error */ public function export_template( $template_id ) { return new \WP_Error( 'invalid_request', 'Cannot export template from a remote source' ); } /** * Get remote template data. * * Retrieve the data of a single remote template from Elementor.com servers. * * @since 1.5.0 * @access public * * @param array $args Custom template arguments. * @param string $context Optional. The context. Default is `display`. * * @return array|\WP_Error Remote Template data. */ public function get_data( array $args, $context = 'display' ) { $data = Api::get_template_content( $args['template_id'] ); if ( is_wp_error( $data ) ) { return $data; } // 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 ); // BC. $data = (array) $data; $data['content'] = $this->replace_elements_ids( $data['content'] ); $data['content'] = $this->process_export_import_content( $data['content'], 'on_import' ); $post_id = $args['editor_post_id']; $document = Plugin::$instance->documents->get( $post_id ); if ( $document ) { $data['content'] = $document->get_elements_raw_data( $data['content'], true ); } // After the upload complete, set the elementor upload state back to false Plugin::$instance->uploads_manager->set_elementor_upload_state( false ); return $data; } /** * Get templates data from a transient or from a remote request. * In any of the following 2 conditions, the remote request will be triggered: * 1. Force update - "$force_update = true" parameter was passed. * 2. The data saved in the transient is empty or not exist. * * @param bool $force_update * @return array */ protected function get_templates_data( bool $force_update ): array { $experiments_manager = Plugin::$instance->experiments; $editor_layout_type = $experiments_manager->is_feature_active( 'container' ) ? 'container_flexbox' : ''; return $this->get_templates( $editor_layout_type ); } /** * Get the templates from a remote server and set a transient. * * @param string $editor_layout_type * @return array */ protected function get_templates( string $editor_layout_type ): array { $templates_data = $this->get_templates_remotely( $editor_layout_type ); return empty( $templates_data ) ? [] : $templates_data; } /** * Fetch templates from the remote server. * * @param string $editor_layout_type * @return array|false */ protected function get_templates_remotely( string $editor_layout_type ) { $response = wp_remote_get( static::API_TEMPLATES_URL, [ 'body' => $this->get_templates_body_args( $editor_layout_type ), ] ); if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { return false; } $templates_data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $templates_data ) || ! is_array( $templates_data ) ) { return []; } return $templates_data; } /** * Prepare the body arguments for the remote request. * * @param string $editor_layout_type * * @return array */ protected function get_templates_body_args( string $editor_layout_type ): array { return [ 'plugin_version' => ELEMENTOR_VERSION, 'editor_layout_type' => $editor_layout_type, ]; } /** * @since 2.2.0 * @access private */ protected function prepare_template( array $template_data ) { $favorite_templates = $this->get_user_meta( 'favorites' ); // BC: Support legacy APIs that don't have access tiers. if ( isset( $template_data['access_tier'] ) ) { $access_tier = $template_data['access_tier']; } else { $access_tier = 0 === $template_data['access_level'] ? ConnectModule::ACCESS_TIER_FREE : ConnectModule::ACCESS_TIER_ESSENTIAL; } return [ 'template_id' => $template_data['id'], 'source' => $this->get_id(), 'type' => $template_data['type'], 'subtype' => $template_data['subtype'], 'title' => $template_data['title'], 'thumbnail' => $template_data['thumbnail'], 'date' => $template_data['tmpl_created'], 'author' => $template_data['author'], 'tags' => json_decode( $template_data['tags'] ), 'isPro' => ( '1' === $template_data['is_pro'] ), 'accessLevel' => $template_data['access_level'], 'accessTier' => $access_tier, 'popularityIndex' => (int) $template_data['popularity_index'], 'trendIndex' => (int) $template_data['trend_index'], 'hasPageSettings' => ( '1' === $template_data['has_page_settings'] ), 'url' => $template_data['url'], 'favorite' => ! empty( $favorite_templates[ $template_data['id'] ] ), ]; } public function clear_cache() { delete_transient( static::TEMPLATES_DATA_TRANSIENT_KEY_PREFIX . ELEMENTOR_VERSION ); } } namespace Elementor\TemplateLibrary; use Elementor\Core\Base\Document; use Elementor\Core\Utils\Exceptions; use Elementor\Modules\CloudLibrary\Connect\Cloud_Library; use Elementor\Modules\CloudLibrary\Documents\Cloud_Template_Preview; use Elementor\Plugin; use Elementor\DB; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Source_Cloud extends Source_Base { const FOLDER_RESOURCE_TYPE = 'FOLDER'; const TEMPLATE_RESOURCE_TYPE = 'TEMPLATE'; protected function get_app(): Cloud_Library { $cloud_library_app = Plugin::$instance->common->get_component( 'connect' )->get_app( 'cloud-library' ); if ( ! $cloud_library_app ) { $error_message = esc_html__( 'Cloud-Library is not instantiated.', 'elementor' ); throw new \Exception( $error_message, Exceptions::FORBIDDEN ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped } return $cloud_library_app; } public function get_id(): string { return 'cloud'; } public function get_title(): string { return esc_html__( 'Cloud Library', 'elementor' ); } public function register_data() {} public function supports_quota(): bool { return true; } public function get_items( $args = [] ) { return $this->get_app()->get_resources( $args ); } public function get_item_children( array $args = [] ) { return $this->get_app()->get_resources( [ 'parentId' => $args['template_id'] ] ); } public function get_item( $id ) { return $this->get_app()->get_resource( [ 'id' => $id ] ); } public function get_data( array $args ) { $data = $this->get_app()->get_resource( [ 'id' => $args['template_id'] ] ); if ( is_wp_error( $data ) || empty( $data['content'] ) ) { return $data; } $decoded_data = json_decode( $data['content'], true ); $data['content'] = $decoded_data['content']; Plugin::$instance->uploads_manager->set_elementor_upload_state( true ); $data['content'] = $this->replace_elements_ids( $data['content'] ); $data['content'] = $this->process_export_import_content( $data['content'], 'on_import' ); if ( ! empty( $args['editor_post_id'] ) ) { $post_id = $args['editor_post_id']; $document = Plugin::$instance->documents->get( $post_id ); if ( $document ) { $data['content'] = $document->get_elements_raw_data( $data['content'], true ); } } if ( ! empty( $args['with_page_settings'] ) ) { $data['page_settings'] = $decoded_data['page_settings']; } // After the upload complete, set the elementor upload state back to false Plugin::$instance->uploads_manager->set_elementor_upload_state( false ); return $data; } public function delete_template( $template_id ) { return $this->get_app()->delete_resource( $template_id ); } public function save_item( $template_data ): int { $app = $this->get_app(); $resource_data = $this->format_resource_item_for_create( $template_data ); $response = $app->post_resource( $resource_data ); return (int) $response['id']; } private function format_resource_item_for_create( $template_data ) { return [ 'title' => $template_data['title'] ?? esc_html__( '(no title)', 'elementor' ), 'type' => self::TEMPLATE_RESOURCE_TYPE, 'templateType' => $template_data['type'], 'parentId' => ! empty( $template_data['parentId'] ) ? (int) $template_data['parentId'] : null, 'content' => wp_json_encode( [ 'content' => $template_data['content'], 'page_settings' => $template_data['page_settings'], ] ), 'hasPageSettings' => ! empty( $template_data['page_settings'] ), ]; } public function save_folder( array $folder_data = [] ) { $app = $this->get_app(); $resource_data = [ 'title' => $folder_data['title'] ?? esc_html__( 'New Folder', 'elementor' ), 'type' => self::FOLDER_RESOURCE_TYPE, 'templateType' => 'folder', 'parentId' => null, ]; $response = $app->post_resource( $resource_data ); return (int) $response['id']; } public function update_item( $template_data ) { return $this->get_app()->update_resource( $template_data ); } public function search_templates( array $args = [] ) { return $this->get_app()->get_resources( $args ); } public function export_template( $id ) { $data = $this->get_app()->get_resource( [ 'id' => $id ] ); if ( is_wp_error( $data ) ) { return new \WP_Error( 'export_template_error', 'An error has occured' ); } if ( static::TEMPLATE_RESOURCE_TYPE === $data['type'] ) { $this->handle_export_file( $data ); } if ( static::FOLDER_RESOURCE_TYPE === $data['type'] ) { $this->handle_export_folder( $id ); } } protected function handle_export_file( array $data ): void { $file_data = $this->prepare_template_export( $data ); if ( is_wp_error( $file_data ) ) { return; } $this->send_file_headers( $file_data['name'], strlen( $file_data['content'] ) ); $this->serve_file( $file_data['content'] ); } protected function handle_export_folder( int $folder_id ) { $data = $this->get_item_children( [ 'template_id' => $folder_id ] ); if ( empty( $data['templates'] ) ) { throw new \Exception( 'Folder does not have any templates to export' ); } $template_ids = array_map( fn( $template ) => $template['template_id'], $data['templates'] ); $this->export_multiple_templates( $template_ids ); } private function prepare_template_export( $data ) { if ( empty( $data['content'] ) ) { throw new \Exception( 'Template data not found' ); } $data['content'] = json_decode( $data['content'], true ); if ( empty( $data['content']['content'] ) ) { throw new \Exception( 'The template is empty' ); } $export_data = [ 'content' => $data['content']['content'], 'page_settings' => $data['content']['page_settings'] ?? [], 'version' => DB::DB_VERSION, 'title' => $data['title'], 'type' => $data['templateType'], ]; return [ 'name' => 'elementor-' . $data['id'] . '-' . gmdate( 'Y-m-d' ) . '.json', 'content' => wp_json_encode( $export_data ), ]; } public function export_multiple_templates( array $template_ids ) { $files = []; $temp_path = Plugin::$instance->uploads_manager->create_unique_dir(); foreach ( $template_ids as $template_id ) { $files[] = $this->get_file_item( $template_id, $temp_path ); } if ( empty( $files ) ) { throw new \Exception( 'There are no files to export (probably all the requested templates are empty).' ); } list( $zip_archive_filename, $zip_complete_path ) = $this->handle_zip_file( $temp_path, $files ); $this->send_file_headers( $zip_archive_filename, $this->filesize( $zip_complete_path ) ); $this->serve_zip( $zip_complete_path ); Plugin::$instance->uploads_manager->remove_file_or_dir( $temp_path ); } protected function handle_zip_file( string $temp_path, array $files ): array { if ( ! class_exists( 'ZipArchive' ) ) { throw new \Error( 'ZipArchive module missing' ); } $zip_archive_filename = 'elementor-templates-' . gmdate( 'Y-m-d' ) . '.zip'; $zip_archive = new \ZipArchive(); $zip_complete_path = $temp_path . '/' . $zip_archive_filename; $zip_archive->open( $zip_complete_path, \ZipArchive::CREATE ); foreach ( $files as $file ) { $zip_archive->addFile( $file['path'], $file['name'] ); } $zip_archive->close(); return [ $zip_archive_filename, $zip_complete_path ]; } private function get_file_item( $template_id, string $temp_path ) { $data = $this->get_app()->get_resource( [ 'id' => $template_id ] ); $file_data = $this->prepare_template_export( $data ); if ( is_wp_error( $file_data ) ) { return; } $complete_path = $temp_path . $file_data['name']; $put_contents = file_put_contents( $complete_path, $file_data['content'] ); if ( ! $put_contents ) { throw new \Exception( sprintf( 'Cannot create file "%s".', esc_html( $file_data['name'] ) ) ); } return [ 'path' => $complete_path, 'name' => $file_data['name'], ]; } public function move_template_to_folder( array $args = [] ) { $move_args = [ 'title' => $args['title'], 'id' => $args['from_template_id'], 'parentId' => ! empty( $args['parentId'] ) ? (int) $args['parentId'] : '', ]; return $this->update_item( $move_args ); } public function move_bulk_templates_to_folder( array $args = [] ) { $move_args = [ 'ids' => $args['from_template_id'], 'parentId' => ! empty( $args['parentId'] ) ? (int) $args['parentId'] : null, ]; return $this->get_app()->bulk_move_templates( $move_args ); } public function save_item_preview( $template_id, $data ) { return $this->get_app()->update_resource_preview( $template_id, $data ); } public function mark_preview_as_failed( $template_id, $data ) { return $this->get_app()->mark_preview_as_failed( $template_id, $data ); } /** * @param int $template_id * @return Document|\WP_Error * @throws \Exception If the user has no permission or the post is not found. */ public function create_document_for_preview( int $template_id ) { if ( ! current_user_can( 'edit_posts' ) ) { return new \WP_Error( Exceptions::FORBIDDEN, esc_html__( 'You do not have permission to create preview documents.', 'elementor' ) ); } $cloud_library_app = $this->get_app(); $template = $cloud_library_app->get_resource( [ 'id' => $template_id ] ); if ( is_wp_error( $template ) ) { $error_message = $template->get_error_message(); throw new \Exception( esc_html( $error_message ), Exceptions::FORBIDDEN ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped } $decoded_content = json_decode( $template['content'], true ); return $this->save_document_for_preview( [ 'content' => $decoded_content['content'], 'page_settings' => $decoded_content['page_settings'], ] ); } protected function save_document_for_preview( $template_content ) { $template_data = [ 'title' => esc_html__( '(no title)', 'elementor' ), 'page_settings' => $template_content['page_settings'] ?? [], 'status' => 'draft', 'type' => 'container', ]; $document = Plugin::$instance->documents->create( Cloud_Template_Preview::TYPE, [ 'post_title' => $template_data['title'], 'post_status' => $template_data['status'], ] ); if ( is_wp_error( $document ) ) { wp_die(); } $template_data['content'] = $this->replace_elements_ids( $template_content['content'] ); $document->save( [ 'elements' => $template_data['content'], 'settings' => $template_data['page_settings'], ] ); do_action( 'elementor/template-library/after_save_template', $document->get_main_id(), $template_data ); do_action( 'elementor/template-library/after_update_template', $document->get_main_id(), $template_data ); return $document; } public function bulk_delete_items( array $template_ids ) { return $this->get_app()->bulk_delete_resources( $template_ids ); } public function bulk_undo_delete_items( array $template_ids ) { return $this->get_app()->bulk_undo_delete_resources( $template_ids ); } public function save_bulk_items( array $data = [] ) { $items = []; foreach ( $data as $template_data ) { $items[] = $this->format_resource_item_for_create( $template_data ); } return $this->get_app()->post_bulk_resources( $items ); } public function get_bulk_items( array $args = [] ) { return $this->get_app()->get_bulk_resources_with_content( $args ); } public function get_quota() { return $this->get_app()->get_quota(); } public function import_template( $name, $path ) { if ( empty( $path ) ) { return new \WP_Error( 'file_error', 'Please upload a file to import' ); } Plugin::$instance->uploads_manager->set_elementor_upload_state( true ); $items = []; $quota = $this->get_quota(); if ( is_wp_error( $quota ) ) { return $quota; } if ( $quota['currentUsage'] >= $quota['threshold'] ) { return new \WP_Error( 'quota_error', 'The upload failed because you’ve saved the maximum templates already.' ); } if ( 'zip' === pathinfo( $name, PATHINFO_EXTENSION ) ) { $extracted_files = Plugin::$instance->uploads_manager->extract_and_validate_zip( $path, [ 'json' ] ); if ( is_wp_error( $extracted_files ) ) { Plugin::$instance->uploads_manager->remove_file_or_dir( $extracted_files['extraction_directory'] ); return $extracted_files; } $items_to_save = []; foreach ( $extracted_files['files'] as $file_path ) { // Skip macOS metadata files and folders if ( false !== strpos( $file_path, '__MACOSX' ) || '.' === basename( $file_path )[0] ) { continue; } $prepared = $this->prepare_import_template_data( $file_path ); if ( is_wp_error( $prepared ) ) { // Skip failed templates continue; } $items_to_save[] = $this->format_resource_item_for_create( $prepared ); } $is_quota_valid = $this->validate_quota( $items_to_save ); if ( is_wp_error( $is_quota_valid ) ) { return $is_quota_valid; } if ( ! $is_quota_valid ) { return new \WP_Error( 'quota_error', 'The upload failed because it will pass the maximum templates you can save.' ); } $items = $this->get_app()->post_bulk_resources( $items_to_save ); Plugin::$instance->uploads_manager->remove_file_or_dir( $extracted_files['extraction_directory'] ); } else { $prepared = $this->prepare_import_template_data( $path ); if ( is_wp_error( $prepared ) ) { return $prepared; } $is_quota_valid = $this->validate_quota( [ $prepared ] ); if ( is_wp_error( $is_quota_valid ) ) { return $is_quota_valid; } if ( ! $is_quota_valid ) { return new \WP_Error( 'quota_error', 'The upload failed because it will pass the maximum templates you can save.' ); } $item = $this->get_app()->post_resource( $this->format_resource_item_for_create( $prepared ) ); if ( is_wp_error( $item ) ) { return $item; } $items[] = $item; } return $items; } public function validate_quota( $items ) { $quota = $this->get_quota(); if ( is_wp_error( $quota ) ) { return $quota; } return $quota['currentUsage'] + count( $items ) <= $quota['threshold']; } }