first commit

This commit is contained in:
2026-03-24 00:31:47 +01:00
commit 2506f6f9c7
3328 changed files with 1172155 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
<?php
namespace Elementor\Modules\CloudKitLibrary\Connect;
use Elementor\Core\Common\Modules\Connect\Apps\Library;
use Elementor\Core\Utils\Exceptions;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Cloud_Kits extends Library {
const THRESHOLD_UNLIMITED = -1;
const FAILED_TO_FETCH_QUOTA_KEY = 'failed-to-fetch-quota';
const INSUFFICIENT_QUOTA_KEY = 'insufficient-quota';
public function get_title() {
return esc_html__( 'Cloud Kits', 'elementor' );
}
protected function get_api_url(): string {
return 'https://cloud-library.prod.builder.elementor.red/api/v1/cloud-library';
}
/**
* @return array|\WP_Error
*/
public function get_all( $args = [] ) {
return $this->http_request( 'GET', 'kits', [], [
'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
] );
}
/**
* @return array|\WP_Error
*/
public function get_quota() {
return $this->http_request( 'GET', 'quota/kits', [], [
'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
] );
}
public function validate_quota() {
$quota = $this->get_quota();
if ( is_wp_error( $quota ) ) {
throw new \Error( static::FAILED_TO_FETCH_QUOTA_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
$is_unlimited = self::THRESHOLD_UNLIMITED === $quota['threshold'];
$has_quota = $quota['currentUsage'] < $quota['threshold'];
if ( ! $is_unlimited && ! $has_quota ) {
throw new \Error( static::INSUFFICIENT_QUOTA_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
}
public function check_eligibility() {
$quota = $this->get_quota();
if ( is_wp_error( $quota ) ) {
return [
'is_eligible' => false,
'subscription_id' => '',
];
}
return [
'is_eligible' => isset( $quota['threshold'] ) && 0 !== $quota['threshold'],
'subscription_id' => ! empty( $quota['subscriptionId'] ) ? $quota['subscriptionId'] : '',
];
}
public function create_kit( $title, $description, $content_file_data, $preview_file_data, array $includes ) {
$this->validate_quota();
$endpoint = 'kits';
$boundary = wp_generate_password( 24, false );
$headers = [
'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
];
$body = $this->create_multipart_body(
[
'title' => $title,
'description' => $description,
'includes' => wp_json_encode( $includes ),
],
[
'previewFile' => [
'filename' => 'preview.png',
'content' => $preview_file_data,
'content_type' => 'image/png',
],
],
$boundary
);
$payload = [
'headers' => $headers,
'body' => $body,
'timeout' => 120,
];
$response = $this->http_request( 'POST', $endpoint, $payload, [
'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
] );
if ( empty( $response['id'] ) ) {
$error_message = esc_html__( 'Failed to create kit: Invalid response', 'elementor' );
throw new \Exception( $error_message, Exceptions::INTERNAL_SERVER_ERROR ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
if ( empty( $response['uploadUrl'] ) ) {
$this->delete_kit( $response['id'] );
$error_message = esc_html__( 'Failed to create kit: No upload URL provided', 'elementor' );
throw new \Exception( $error_message, Exceptions::INTERNAL_SERVER_ERROR ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
$upload_success = $this->upload_content_file( $response['uploadUrl'], $content_file_data );
if ( ! $upload_success ) {
$this->delete_kit( $response['id'] );
$error_message = esc_html__( 'Failed to create kit: Content upload failed', 'elementor' );
throw new \Exception( $error_message, Exceptions::INTERNAL_SERVER_ERROR ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
return $response;
}
private function upload_content_file( $upload_url, $content_file_data ) {
$upload_response = wp_remote_request( $upload_url, [
'method' => 'PUT',
'body' => $content_file_data,
'headers' => [
'Content-Type' => 'application/zip',
'Content-Length' => strlen( $content_file_data ),
],
'timeout' => 120,
] );
if ( is_wp_error( $upload_response ) ) {
return false;
}
$response_code = wp_remote_retrieve_response_code( $upload_response );
return $response_code >= 200 && $response_code < 300;
}
public function get_kit( array $args ) {
$args = array_merge_recursive( $args, [
'timeout' => 60, // just in case if zip is big
] );
return $this->http_request( 'GET', 'kits/' . $args['id'], $args, [
'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
] );
}
public function delete_kit( int $id ) {
return $this->http_request( 'DELETE', 'kits/' . $id, [], [
'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
] );
}
private function create_multipart_body( $fields, $files, $boundary ): string {
$eol = "\r\n";
$body = '';
foreach ( $fields as $name => $value ) {
$body .= "--{$boundary}{$eol}";
$body .= "Content-Disposition: form-data; name=\"{$name}\"{$eol}{$eol}";
$body .= "{$value}{$eol}";
}
foreach ( $files as $name => $file ) {
$filename = basename( $file['filename'] );
$content_type = $file['content_type'];
$content = $file['content'];
$body .= "--{$boundary}{$eol}";
$body .= "Content-Disposition: form-data; name=\"{$name}\"; filename=\"{$filename}\"{$eol}";
$body .= "Content-Type: {$content_type}{$eol}{$eol}";
$body .= $content . $eol;
}
$body .= "--{$boundary}--{$eol}";
return $body;
}
protected function init() {}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Elementor\Modules\CloudKitLibrary\Data;
use Elementor\Modules\CloudKitLibrary\Connect\Cloud_Kits;
use Elementor\Modules\CloudKitLibrary\Module as CloudKitLibrary;
use Elementor\App\Modules\KitLibrary\Data\Base_Controller;
use Elementor\Core\Utils\Collection;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Controller extends Base_Controller {
public function get_name() {
return 'cloud-kits';
}
public function get_items( $request ) {
$data = $this->get_app()->get_all();
if ( is_wp_error( $data ) ) {
return [
'data' => [],
];
}
$kits = ( new Collection( $data ) )->map( function ( $kit ) {
return [
'id' => $kit['id'],
'title' => $kit['title'],
'thumbnail_url' => $kit['thumbnailUrl'],
'created_at' => $kit['createdAt'],
'updated_at' => $kit['updatedAt'],
];
} );
return [
'data' => $kits->values(),
];
}
public function delete_item( $request ) {
return [
'data' => $this->get_app()->delete_kit( $request->get_param( 'id' ) ),
];
}
public function get_item( $request ) {
return [
'data' => $this->get_app()->get_kit( [ 'id' => $request->get_param( 'id' ) ] ),
];
}
public function register_endpoints() {
$this->index_endpoint->register_item_route( \WP_REST_Server::DELETABLE, [
'id' => [
'description' => 'Unique identifier for the object.',
'type' => 'integer',
'required' => true,
],
] );
$this->register_endpoint( new Endpoints\Eligibility( $this ) );
}
public function get_permission_callback( $request ) {
return current_user_can( 'manage_options' );
}
protected function get_app(): Cloud_Kits {
return CloudKitLibrary::get_app();
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Elementor\Modules\CloudKitLibrary\Data\Endpoints;
use Elementor\Modules\CloudKitLibrary\Data\Controller;
use Elementor\Modules\CloudKitLibrary\Module as CloudKitLibrary;
use Elementor\Data\V2\Base\Endpoint;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* @property Controller $controller
*/
class Eligibility extends Endpoint {
public function get_name() {
return 'eligibility';
}
public function get_format() {
return 'cloud-kits/eligibility';
}
public function get_items( $request ) {
return CloudKitLibrary::get_app()->check_eligibility();
}
}

View File

@@ -0,0 +1,149 @@
<?php
namespace Elementor\Modules\CloudKitLibrary;
use Elementor\Modules\CloudKitLibrary\Data\Controller as Cloud_Kits_Controller;
use Elementor\Core\Utils\Exceptions;
use Elementor\Plugin;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Modules\CloudKitLibrary\Connect\Cloud_Kits;
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
use Elementor\App\Modules\ImportExport\Module as ImportExport_Module;
use Elementor\App\Modules\KitLibrary\Connect\Kit_Library as Kit_Library_Api;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Module extends BaseModule {
public function get_name(): string {
return 'cloud-kit-library';
}
public function __construct() {
parent::__construct();
add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
$connect_module->register_app( 'cloud-kits', Cloud_Kits::get_class_name() );
} );
add_filter( 'elementor/export/kit/export-result', [ $this, 'handle_export_kit_result' ], 10, 5 );
add_filter( 'elementor/import/kit/result/cloud', [ $this, 'handle_import_kit_from_cloud' ], 10, 1 );
add_filter( 'elementor/import/kit_thumbnail', [ $this, 'handle_import_kit_thumbnail' ], 10, 3 );
add_action( 'elementor/kit_library/registered', function () {
Plugin::$instance->data_manager_v2->register_controller( new Cloud_Kits_Controller() );
} );
}
public function handle_import_kit_thumbnail( $thumbnail, $kit_id, $referrer ) {
if ( ImportExport_Module::REFERRER_KIT_LIBRARY === $referrer ) {
if ( empty( $kit_id ) ) {
return '';
}
$api = new Kit_Library_Api();
$kit = $api->get_by_id( $kit_id );
if ( is_wp_error( $kit ) ) {
return '';
}
return $kit->thumbnail;
}
if ( ImportExport_Module::REFERRER_CLOUD === $referrer ) {
if ( empty( $kit_id ) ) {
return '';
}
$kit = self::get_app()->get_kit( [ 'id' => $kit_id ] );
if ( is_wp_error( $kit ) ) {
return '';
}
return $kit['thumbnailUrl'] ?? '';
}
return $thumbnail;
}
public function handle_export_kit_result( $result, $source, $export, $settings, $file ) {
if ( ImportExport_Module::EXPORT_SOURCE_CLOUD !== $source ) {
return $result;
}
unset( $result['file'] );
$raw_screen_shot = base64_decode( substr( $settings['screenShotBlob'], strlen( 'data:image/png;base64,' ) ) );
$title = $export['manifest']['title'];
$description = $export['manifest']['description'];
$kit = self::get_app()->create_kit(
$title,
$description,
$file,
$raw_screen_shot,
$settings['include'],
);
if ( is_wp_error( $kit ) ) {
return $kit;
}
$result['kit'] = $kit;
return $result;
}
public function handle_import_kit_from_cloud( $args ) {
$kit = self::get_app()->get_kit( [
'id' => $args['kit_id'],
] );
if ( is_wp_error( $kit ) ) {
return $kit;
}
if ( empty( $kit['downloadUrl'] ) ) {
throw new \Error( ImportExport_Module::KIT_LIBRARY_ERROR_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
return [
'file_name' => self::get_remote_kit_zip( $kit['downloadUrl'] ),
'referrer' => ImportExport_Module::REFERRER_CLOUD,
'file_url' => $kit['downloadUrl'],
'kit' => $kit,
];
}
public static function get_remote_kit_zip( $url ) {
$remote_zip_request = wp_safe_remote_get( $url );
if ( is_wp_error( $remote_zip_request ) ) {
Plugin::$instance->logger->get_logger()->error( $remote_zip_request->get_error_message() );
throw new \Error( ImportExport_Module::KIT_LIBRARY_ERROR_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
if ( 200 !== $remote_zip_request['response']['code'] ) {
Plugin::$instance->logger->get_logger()->error( $remote_zip_request['response']['message'] );
throw new \Error( ImportExport_Module::KIT_LIBRARY_ERROR_KEY ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
return Plugin::$instance->uploads_manager->create_temp_file( $remote_zip_request['body'], 'kit.zip' );
}
public static function get_app(): Cloud_Kits {
$cloud_kits_app = Plugin::$instance->common->get_component( 'connect' )->get_app( 'cloud-kits' );
if ( ! $cloud_kits_app ) {
$error_message = esc_html__( 'Cloud-Kits is not instantiated.', 'elementor' );
throw new \Exception( $error_message, Exceptions::FORBIDDEN ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
return $cloud_kits_app;
}
}