first commit

This commit is contained in:
Roman Pyrih
2026-03-10 09:50:10 +01:00
commit 64c4a90405
7289 changed files with 2645777 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
<?php
namespace Elementor\TemplateLibrary\Classes;
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
use Elementor\Core\Files\Uploads_Manager;
use Elementor\Plugin;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor template library import images.
*
* Elementor template library import images handler class is responsible for
* importing remote images used by the template library.
*
* @since 1.0.0
*/
class Import_Images {
/**
* Replaced images IDs.
*
* The IDs of all the new imported images. An array containing the old
* attachment ID and the new attachment ID generated after the import.
*
* @since 1.0.0
* @access private
*
* @var array
*/
private $_replace_image_ids = [];
/**
* Get image hash.
*
* Retrieve the sha1 hash of the image URL.
*
* @since 2.0.0
* @access private
*
* @param string $attachment_url The attachment URL.
*
* @return string Image hash.
*/
private function get_hash_image( $attachment_url ) {
return sha1( $attachment_url );
}
/**
* Get saved image.
*
* Retrieve new image ID, if the image has a new ID after the import.
*
* @since 2.0.0
* @access private
*
* @param array $attachment The attachment.
*
* @return false|array New image ID or false.
*/
private function get_saved_image( $attachment ) {
global $wpdb;
if ( isset( $this->_replace_image_ids[ $attachment['id'] ] ) ) {
return $this->_replace_image_ids[ $attachment['id'] ];
}
$post_id = $wpdb->get_var(
$wpdb->prepare(
'SELECT `post_id` FROM `' . $wpdb->postmeta . '`
WHERE `meta_key` = \'_elementor_source_image_hash\'
AND `meta_value` = %s
;',
$this->get_hash_image( $attachment['url'] )
)
);
if ( $post_id ) {
$new_attachment = [
'id' => $post_id,
'url' => wp_get_attachment_url( $post_id ),
];
$this->_replace_image_ids[ $attachment['id'] ] = $new_attachment;
return $new_attachment;
}
return false;
}
/**
* Import image.
*
* Import a single image from a remote server, upload the image WordPress
* uploads folder, create a new attachment in the database and updates the
* attachment metadata.
*
* @since 1.0.0
* @since 3.2.0 New `$parent_post_id` option added
* @access public
*
* @param array $attachment The attachment.
* @param int $parent_post_id Optional.
*
* @return false|array Imported image data, or false.
*/
public function import( $attachment, $parent_post_id = null ) {
if ( isset( $attachment['tmp_name'] ) ) {
// Used when called to import a directly-uploaded file.
$filename = $attachment['name'];
$file_content = false;
// security validation in case the tmp_name has been tampered with
if ( is_uploaded_file( $attachment['tmp_name'] ) ) {
$file_content = Utils::file_get_contents( $attachment['tmp_name'] );
}
} else {
// Used when attachment information is passed to this method.
if ( ! empty( $attachment['id'] ) ) {
$saved_image = $this->get_saved_image( $attachment );
if ( $saved_image ) {
return $saved_image;
}
}
// Extract the file name and extension from the url.
$filename = basename( $attachment['url'] );
$request = wp_safe_remote_get( $attachment['url'] );
// Make sure the request returns a valid result.
if ( is_wp_error( $request ) || ( ! empty( $request['response']['code'] ) && 200 !== (int) $request['response']['code'] ) ) {
return false;
}
$file_content = wp_remote_retrieve_body( $request );
}
if ( empty( $file_content ) ) {
return false;
}
$filetype = wp_check_filetype( $filename );
// If the file type is not recognized by WordPress, exit here to avoid creation of an empty attachment document.
if ( ! $filetype['ext'] ) {
return false;
}
if ( 'svg' === $filetype['ext'] ) {
// In case that unfiltered-files upload is not enabled, SVG images should not be imported.
if ( ! Uploads_Manager::are_unfiltered_uploads_enabled() ) {
return false;
}
$svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );
$file_content = $svg_handler->sanitizer( $file_content );
}
$upload = wp_upload_bits(
$filename,
null,
$file_content
);
$post = [
'post_title' => $filename,
'guid' => $upload['url'],
];
$info = wp_check_filetype( $upload['file'] );
if ( $info ) {
$post['post_mime_type'] = $info['type'];
} else {
// For now just return the origin attachment
return $attachment;
// return new \WP_Error( 'attachment_processing_error', esc_html__( 'Invalid file type.', 'elementor' ) );
}
$post_id = wp_insert_attachment( $post, $upload['file'], $parent_post_id );
apply_filters( 'elementor/template_library/import_images/new_attachment', $post_id );
// On REST requests.
if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
require_once ABSPATH . '/wp-admin/includes/image.php';
}
if ( ! function_exists( 'wp_read_video_metadata' ) ) {
require_once ABSPATH . '/wp-admin/includes/media.php';
}
wp_update_attachment_metadata(
$post_id,
wp_generate_attachment_metadata( $post_id, $upload['file'] )
);
update_post_meta( $post_id, '_elementor_source_image_hash', $this->get_hash_image( $attachment['url'] ) );
$new_attachment = [
'id' => $post_id,
'url' => $upload['url'],
];
if ( ! empty( $attachment['id'] ) ) {
$this->_replace_image_ids[ $attachment['id'] ] = $new_attachment;
}
return $new_attachment;
}
/**
* Import local file.
*
* Import a local file directly to WordPress media library.
* Used for importing files that are already downloaded locally (e.g., from extracted ZIP).
*
* @since 3.33.0
* @access public
*
* @param string $local_file_path The local file path.
* @param int $parent_post_id Optional. Parent post ID.
*
* @return false|array Imported image data, or false on failure.
*/
public function import_local_file( $local_file_path, $parent_post_id = null ) {
global $wp_filesystem;
if ( ! $wp_filesystem->exists( $local_file_path ) ) {
return false;
}
if ( ! function_exists( 'media_handle_sideload' ) ) {
require_once ABSPATH . 'wp-admin/includes/media.php';
}
if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
require_once ABSPATH . 'wp-admin/includes/image.php';
}
$file_array = [
'name' => basename( $local_file_path ),
'tmp_name' => $local_file_path,
];
$attachment_id = media_handle_sideload( $file_array, $parent_post_id );
if ( is_wp_error( $attachment_id ) ) {
return false;
}
apply_filters( 'elementor/template_library/import_images/new_attachment', $attachment_id );
return [
'id' => $attachment_id,
'url' => wp_get_attachment_url( $attachment_id ),
];
}
/**
* Template library import images constructor.
*
* Initializing the images import class used by the template library through
* the WordPress Filesystem API.
*
* @since 1.0.0
* @access public
*/
public function __construct() {
if ( ! function_exists( 'WP_Filesystem' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
WP_Filesystem();
}
}

View File

@@ -0,0 +1,242 @@
<?php
namespace Elementor\TemplateLibrary\Classes;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor media collector.
*
* Collects media URLs during export and creates a media ZIP file.
*/
class Media_Collector {
/**
* Filename for the media mapping JSON file within the ZIP.
*/
private const MAPPING_FILENAME = 'media-mapping.json';
/**
* Collected media URLs and their metadata.
*
* @var array
*/
private $collected_media = [];
/**
* Temporary directory for media files.
*
* @var string
*/
private $temp_dir = '';
public function __construct() {
add_action( 'elementor/templates/collect_media_url', [ $this, 'collect_media_url' ], 10, 2 );
}
/**
* Start media collection for export (Step 1: Collect URLs).
*/
public function start_collection() {
$this->collected_media = [];
}
/**
* Start media processing (Step 2: Download media files and create zip).
*/
public function start_processing() {
$this->temp_dir = \Elementor\Plugin::$instance->uploads_manager->create_unique_dir();
}
public function collect_media_url( string $url, array $media_data = [] ) {
if ( ! $this->is_media_url( $url ) || isset( $this->collected_media[ $url ] ) ) {
return;
}
$this->collected_media[ $url ] = true;
}
/**
* Process a single media URL (Step 2: Download and save).
*/
public function process_media_url( string $url ) {
if ( ! $this->is_media_url( $url ) ) {
return false;
}
$local_filename = $this->download_and_save_media( $url );
if ( $local_filename ) {
$this->collected_media[ $url ] = $local_filename;
return $local_filename;
}
return false;
}
private function download_and_save_media( string $url ) {
if ( $this->is_local_url( $url ) ) {
$local_file_path = $this->get_local_file_path( $url );
if ( $local_file_path && file_exists( $local_file_path ) ) {
return $this->copy_local_file( $local_file_path, $url );
}
}
return $this->download_via_http( $url );
}
private function is_local_url( string $url ): bool {
$site_url = get_site_url();
$home_url = get_home_url();
return strpos( $url, $site_url ) === 0 || strpos( $url, $home_url ) === 0;
}
private function get_local_file_path( string $url ) {
$site_url = get_site_url();
$home_url = get_home_url();
$relative_path = str_replace( [ $site_url, $home_url ], '', $url );
$relative_path = ltrim( $relative_path, '/' );
$upload_dir = wp_upload_dir();
$uploads_path = $upload_dir['basedir'];
$possible_paths = [
$uploads_path . '/' . $relative_path,
ABSPATH . $relative_path,
$uploads_path . '/' . basename( $url ),
];
foreach ( $possible_paths as $path ) {
if ( file_exists( $path ) ) {
return $path;
}
}
return false;
}
private function copy_local_file( string $source_path, string $original_url ) {
$original_filename = basename( $original_url );
$extension = pathinfo( $original_filename, PATHINFO_EXTENSION );
$name_without_extension = pathinfo( $original_filename, PATHINFO_FILENAME );
$unique_filename = sanitize_file_name( $name_without_extension . '_' . uniqid() . '.' . $extension );
$destination_path = $this->temp_dir . '/' . $unique_filename;
$copied = copy( $source_path, $destination_path );
return $copied ? $unique_filename : false;
}
private function download_via_http( string $url ) {
$response = wp_safe_remote_get( $url, [
'timeout' => 30,
'user-agent' => 'Elementor Template Exporter',
] );
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
return false;
}
$file_content = wp_remote_retrieve_body( $response );
if ( empty( $file_content ) ) {
return false;
}
$original_filename = basename( $url );
$extension = pathinfo( $original_filename, PATHINFO_EXTENSION );
$name_without_extension = pathinfo( $original_filename, PATHINFO_FILENAME );
$unique_filename = sanitize_file_name( $name_without_extension . '_' . uniqid() . '.' . $extension );
$file_path = $this->temp_dir . '/' . $unique_filename;
$saved = file_put_contents( $file_path, $file_content );
return $saved ? $unique_filename : false;
}
public function get_collected_urls(): array {
return array_keys( $this->collected_media );
}
public function process_media_collection( array $media_urls ) {
$this->start_processing();
foreach ( $media_urls as $url ) {
$this->process_media_url( $url );
}
return $this->create_media_zip();
}
public function create_media_zip() {
if ( empty( $this->collected_media ) ) {
return null;
}
if ( ! class_exists( '\ZipArchive' ) ) {
return null;
}
$zip = new \ZipArchive();
$zip_filename = 'media-' . uniqid() . '.zip';
$zip_path = $this->temp_dir . '/' . $zip_filename;
if ( $zip->open( $zip_path, \ZipArchive::CREATE ) !== true ) {
return null;
}
$mapping = [];
foreach ( $this->collected_media as $url => $filename ) {
if ( is_string( $filename ) ) {
$file_path = $this->temp_dir . '/' . $filename;
if ( file_exists( $file_path ) ) {
$zip->addFile( $file_path, $filename );
$mapping[ $url ] = $filename;
}
}
}
if ( empty( $mapping ) ) {
$zip->close();
return null;
}
$mapping_json = wp_json_encode( $mapping, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
$zip->addFromString( self::MAPPING_FILENAME, $mapping_json );
$zip->close();
return $zip_path;
}
public function cleanup() {
if ( $this->temp_dir ) {
\Elementor\Plugin::$instance->uploads_manager->remove_file_or_dir( $this->temp_dir );
}
}
private function is_media_url( $url ) {
if ( ! is_string( $url ) || empty( $url ) ) {
return false;
}
if ( strpos( $url, 'data:' ) === 0 ) {
return false;
}
$allowed_mime_types = get_allowed_mime_types();
$file_extension = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
foreach ( $allowed_mime_types as $pattern => $mime_type ) {
$pattern_regex = '/^(' . str_replace( '|', '|', $pattern ) . ')$/i';
if ( preg_match( $pattern_regex, $file_extension ) ) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Elementor\TemplateLibrary\Classes;
use Elementor\Plugin;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor media mapper.
*
* Maps media URLs to their corresponding filenames.
*/
class Media_Mapper {
const TRANSIENT_KEY = 'elementor_media_mapping';
public static function set_mapping( $mapping, $media_dir = '' ) {
$mapping = is_array( $mapping ) ? $mapping : [];
if ( ! empty( $mapping ) ) {
set_transient( self::TRANSIENT_KEY, [
'mapping' => $mapping,
'media_dir' => $media_dir,
], HOUR_IN_SECONDS );
}
}
public static function get_local_file_path( $original_url ) {
$stored_mapping = get_transient( self::TRANSIENT_KEY );
if ( ! $stored_mapping || ! is_array( $stored_mapping ) || empty( $original_url ) ) {
return $original_url;
}
$mapping = $stored_mapping['mapping'] ?? [];
$media_dir = $stored_mapping['media_dir'] ?? '';
if ( empty( $mapping ) ) {
return $original_url;
}
$filename = $mapping[ $original_url ] ?? null;
if ( $filename && $media_dir ) {
$file_path = $media_dir . '/' . $filename;
if ( file_exists( $file_path ) ) {
return $file_path;
}
}
return $original_url;
}
public static function clear_mapping() {
$stored_mapping = get_transient( self::TRANSIENT_KEY );
if ( $stored_mapping && is_array( $stored_mapping ) ) {
$media_dir = $stored_mapping['media_dir'] ?? '';
if ( $media_dir ) {
Plugin::$instance->uploads_manager->remove_file_or_dir( $media_dir );
}
}
delete_transient( self::TRANSIENT_KEY );
}
}