PluginProbe
Elementor Website Builder – more than just a page builder / 3.16.2
Elementor Website Builder – more than just a page builder v3.16.2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
← All changes | modules/ai/module.php +143 -1135 4.0.83.16.2 View file →
@@ -1,36 +1,17 @@
1 1 <?php
2 2 namespace Elementor\Modules\Ai;
3 3
4 -use Elementor\Controls_Manager;
5 4 use Elementor\Core\Base\Module as BaseModule;
6 5 use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
7 -use Elementor\Element_Base;
8 -use Elementor\Modules\Ai\Feature_Intro\Product_Image_Unification_Intro;
6 +use Elementor\Modules\Ai\Connect\Ai;
9 7 use Elementor\Plugin;
10 -use Elementor\Core\Utils\Collection;
11 -use Elementor\Modules\Ai\Connect\Ai;
12 8 use Elementor\User;
13 -use Elementor\Utils;
14 9
15 10 if ( ! defined( 'ABSPATH' ) ) {
16 11 exit; // Exit if accessed directly.
17 12 }
18 -
19 13 class Module extends BaseModule {
20 - const HISTORY_TYPE_ALL = 'all';
21 - const HISTORY_TYPE_TEXT = 'text';
22 - const HISTORY_TYPE_CODE = 'code';
23 - const HISTORY_TYPE_IMAGE = 'images';
24 - const HISTORY_TYPE_BLOCK = 'blocks';
25 - const VALID_HISTORY_TYPES = [
26 - self::HISTORY_TYPE_ALL,
27 - self::HISTORY_TYPE_TEXT,
28 - self::HISTORY_TYPE_CODE,
29 - self::HISTORY_TYPE_IMAGE,
30 - self::HISTORY_TYPE_BLOCK,
31 - ];
32 - const MIN_PAGES_FOR_CREATE_WITH_AI_BANNER = 10;
33 14
34 15 public function get_name() {
35 16 return 'ai';
36 17 }
@@ -37,120 +18,48 @@
37 18
38 19 public function __construct() {
39 20 parent::__construct();
40 21
41 - ( new SitePlannerConnect\Module() );
42 -
43 - if ( is_admin() ) {
44 - ( new Preferences() )->register();
45 - add_action( 'elementor/import-export/import-kit/runner/after-run', [ $this, 'handle_kit_install' ] );
46 - }
47 -
48 - if ( ! $this->is_ai_enabled() ) {
49 - return;
50 - }
51 -
52 - add_filter( 'elementor/core/admin/homescreen', [ $this, 'add_create_with_ai_banner_to_homescreen' ] );
53 -
54 22 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
55 23 $connect_module->register_app( 'ai', Ai::get_class_name() );
56 24 } );
57 25
58 26 add_action( 'elementor/ajax/register_actions', function( $ajax ) {
59 - $handlers = [
60 - 'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ],
61 - 'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ],
62 - 'ai_get_remote_frontend_config' => [ $this, 'ajax_ai_get_remote_frontend_config' ],
63 - 'ai_get_completion_text' => [ $this, 'ajax_ai_get_completion_text' ],
64 - 'ai_get_excerpt' => [ $this, 'ajax_ai_get_excerpt' ],
65 - 'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ],
66 - 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
67 - 'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ],
68 - 'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ],
69 - 'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ],
70 - 'ai_set_status_feedback' => [ $this, 'ajax_ai_set_status_feedback' ],
71 - 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
72 - 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
73 - 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
74 - 'ai_get_image_to_image_mask' => [ $this, 'ajax_ai_get_image_to_image_mask' ],
75 - 'ai_get_image_to_image_mask_cleanup' => [ $this, 'ajax_ai_get_image_to_image_mask_cleanup' ],
76 - 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
77 - 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
78 - 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
79 - 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
80 - 'ai_upload_image' => [ $this, 'ajax_ai_upload_image' ],
81 - 'ai_generate_layout' => [ $this, 'ajax_ai_generate_layout' ],
82 - 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
83 - 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
84 - 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
85 - 'ai_toggle_favorite_history_item' => [ $this, 'ajax_ai_toggle_favorite_history_item' ],
86 - 'ai_get_product_image_unification' => [ $this, 'ajax_ai_get_product_image_unification' ],
87 - 'ai_get_animation' => [ $this, 'ajax_ai_get_animation' ],
88 - 'ai_get_image_to_image_isolate_objects' => [ $this, 'ajax_ai_get_product_image_unification' ],
89 - ];
90 -
91 - foreach ( $handlers as $tag => $callback ) {
92 - $ajax->register_ajax_action( $tag, $callback );
93 - }
27 + $ajax->register_ajax_action( 'ai_get_user_information', [ $this, 'ajax_ai_get_user_information' ] );
28 + $ajax->register_ajax_action( 'ai_get_completion_text', [ $this, 'ajax_ai_get_completion_text' ] );
29 + $ajax->register_ajax_action( 'ai_get_edit_text', [ $this, 'ajax_ai_get_edit_text' ] );
30 + $ajax->register_ajax_action( 'ai_get_custom_code', [ $this, 'ajax_ai_get_custom_code' ] );
31 + $ajax->register_ajax_action( 'ai_get_custom_css', [ $this, 'ajax_ai_get_custom_css' ] );
32 + $ajax->register_ajax_action( 'ai_set_get_started', [ $this, 'ajax_ai_set_get_started' ] );
33 + $ajax->register_ajax_action( 'ai_set_status_feedback', [ $this, 'ajax_ai_set_status_feedback' ] );
34 + $ajax->register_ajax_action( 'ai_get_image_prompt_enhancer', [ $this, 'ajax_ai_get_image_prompt_enhancer' ] );
35 + $ajax->register_ajax_action( 'ai_get_text_to_image', [ $this, 'ajax_ai_get_text_to_image' ] );
36 + $ajax->register_ajax_action( 'ai_get_image_to_image', [ $this, 'ajax_ai_get_image_to_image' ] );
37 + $ajax->register_ajax_action( 'ai_get_image_to_image_mask', [ $this, 'ajax_ai_get_image_to_image_mask' ] );
38 + $ajax->register_ajax_action( 'ai_get_image_to_image_outpainting', [ $this, 'ajax_ai_get_image_to_image_outpainting' ] );
39 + $ajax->register_ajax_action( 'ai_get_image_to_image_upscale', [ $this, 'ajax_ai_get_image_to_image_upscale' ] );
40 + $ajax->register_ajax_action( 'ai_get_image_to_image_remove_background', [ $this, 'ajax_ai_get_image_to_image_remove_background' ] );
41 + $ajax->register_ajax_action( 'ai_get_image_to_image_replace_background', [ $this, 'ajax_ai_get_image_to_image_replace_background' ] );
42 + $ajax->register_ajax_action( 'ai_upload_image', [ $this, 'ajax_ai_upload_image' ] );
94 43 } );
95 44
96 45 add_action( 'elementor/editor/before_enqueue_scripts', function() {
97 - $this->enqueue_main_script();
98 - $this->enqueue_layout_script();
99 - } );
100 -
101 - add_action( 'elementor/editor/after_enqueue_styles', function() {
102 - wp_enqueue_style(
103 - 'elementor-ai-editor',
104 - $this->get_css_assets_url( 'modules/ai/editor' ),
105 - [],
106 - ELEMENTOR_VERSION
107 - );
108 - } );
109 -
110 - add_action( 'elementor/preview/enqueue_styles', function() {
111 - wp_enqueue_style(
112 - 'elementor-ai-layout-preview',
113 - $this->get_css_assets_url( 'modules/ai/layout-preview' ),
114 - [],
115 - ELEMENTOR_VERSION
116 - );
117 - } );
118 -
119 - if ( is_admin() ) {
120 - add_action( 'wp_enqueue_media', [ $this, 'enqueue_ai_media_library' ] );
121 - add_action( 'admin_head', [ $this, 'enqueue_ai_media_library_upload_screen' ] );
122 -
123 - if ( current_user_can( 'edit_products' ) || current_user_can( 'publish_products' ) ) {
124 - add_action( 'admin_init', [ $this, 'enqueue_ai_products_page_scripts' ] );
125 - add_action( 'current_screen', [ $this, 'enqueue_ai_single_product_page_scripts' ] );
126 - add_action( 'wp_ajax_elementor-ai-get-product-images', [ $this, 'get_product_images_ajax' ] );
127 - add_action( 'wp_ajax_elementor-ai-set-product-images', [ $this, 'set_product_images_ajax' ] );
128 - Product_Image_Unification_Intro::add_hooks();
129 - }
130 - }
131 -
132 - add_action( 'enqueue_block_editor_assets', function() {
133 - wp_enqueue_script( 'elementor-ai-gutenberg',
134 - $this->get_js_assets_url( 'ai-gutenberg' ),
46 + wp_enqueue_script(
47 + 'elementor-ai',
48 + $this->get_js_assets_url( 'ai' ),
135 49 [
136 - 'jquery',
50 + 'elementor-common',
51 + 'elementor-editor-modules',
52 + 'elementor-editor-document',
137 53 'elementor-v2-ui',
138 54 'elementor-v2-icons',
139 - 'wp-blocks',
140 - 'wp-element',
141 - 'wp-editor',
142 - 'wp-data',
143 - 'wp-components',
144 - 'wp-compose',
145 - 'wp-i18n',
146 - 'wp-hooks',
147 - 'elementor-ai-media-library',
148 55 ],
149 - ELEMENTOR_VERSION, true );
56 + ELEMENTOR_VERSION,
57 + true
58 + );
150 59
151 60 wp_localize_script(
152 - 'elementor-ai-gutenberg',
61 + 'elementor-ai',
153 62 'ElementorAiConfig',
154 63 [
155 64 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
156 65 'connect_url' => $this->get_ai_connect_url(),
@@ -156,374 +65,19 @@
156 65 'connect_url' => $this->get_ai_connect_url(),
157 66 ]
158 67 );
159 68
160 - wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
161 - });
162 -
163 - add_filter( 'elementor/document/save/data', function ( $data ) {
164 - return $this->remove_temporary_containers( $data );
69 + wp_set_script_translations( 'elementor-ai', 'elementor' );
165 70 } );
166 71
167 - add_action( 'elementor/element/common/section_effects/after_section_start', [ $this, 'register_ai_motion_effect_control' ], 10, 1 );
168 - add_action( 'elementor/element/container/section_effects/after_section_start', [ $this, 'register_ai_motion_effect_control' ], 10, 1 );
169 - add_action( 'elementor/element/common/_section_transform/after_section_end', [ $this, 'register_ai_hover_effect_control' ], 10, 1 );
170 - add_action( 'elementor/element/container/_section_transform/after_section_end', [ $this, 'register_ai_hover_effect_control' ], 10, 1 );
171 - }
172 -
173 - public function is_ai_enabled() {
174 - if ( ! Plugin::$instance->experiments->is_feature_active( 'container' ) ) {
175 - return false;
176 - }
177 -
178 - return Preferences::is_ai_enabled( get_current_user_id() );
179 - }
180 -
181 - public function handle_kit_install( $imported_data ) {
182 - if ( ! $this->is_ai_enabled() ) {
183 - return;
184 - }
185 -
186 - if ( ! isset( $imported_data['status'] ) || 'success' !== $imported_data['status'] ) {
187 - return;
188 - }
189 -
190 - if ( ! isset( $imported_data['runner'] ) || 'site-settings' !== $imported_data['runner'] ) {
191 - return;
192 - }
193 -
194 - if ( ! isset( $imported_data['configData']['lastImportedSession']['instance_data']['site_settings']['settings']['ai'] ) ) {
195 - return;
196 - }
197 -
198 - $is_connected = $this->get_ai_app()->is_connected() && User::get_introduction_meta( 'ai_get_started' );
199 -
200 - if ( ! $is_connected ) {
201 - return;
202 - }
203 -
204 - $last_imported_session = $imported_data['configData']['lastImportedSession'];
205 - $imported_ai_data = $last_imported_session['instance_data']['site_settings']['settings']['ai'];
206 -
207 - $this->get_ai_app()->send_event( [
208 - 'name' => 'kit_installed',
209 - 'data' => $imported_ai_data,
210 - 'client' => [
211 - 'name' => 'elementor',
212 - 'version' => ELEMENTOR_VERSION,
213 - 'session_id' => $last_imported_session['session_id'],
214 - ],
215 - ] );
216 - }
217 -
218 - public function register_ai_hover_effect_control( Element_Base $element ) {
219 - if ( ! $element->get_controls( 'ai_hover_animation' ) ) {
220 - $element->add_control(
221 - 'ai_hover_animation',
222 - [
223 - 'tabs_wrapper' => '_tabs_positioning',
224 - 'inner_tab' => '_tab_positioning_hover',
225 - 'label' => esc_html__( 'Animate With AI', 'elementor' ),
226 - 'type' => Controls_Manager::RAW_HTML,
227 - 'raw' => '
228 -<style>
229 - .elementor-control-ai_hover_animation .elementor-control-content {
230 - display: flex;
231 - flex-direction: row;
232 - justify-content: space-between;
233 - align-items: center;
234 - }
235 - .elementor-control-ai_hover_animation .elementor-control-raw-html {
236 - display: none;
237 - }
238 -</style>',
239 - 'render_type' => 'none',
240 - 'ai' => [
241 - 'active' => true,
242 - 'type' => 'hover_animation',
243 - ],
244 - ],
245 - [
246 - 'position' => [
247 - 'of' => '_transform_rotate_popover_hover',
248 - 'type' => 'control',
249 - 'at' => 'before',
250 - ],
251 - ]
72 + add_action( 'elementor/editor/after_enqueue_styles', function() {
73 + wp_enqueue_style(
74 + 'elementor-ai',
75 + $this->get_css_assets_url( 'modules/ai/editor' ),
76 + [],
77 + ELEMENTOR_VERSION
252 78 );
253 - }
254 - }
255 - public function register_ai_motion_effect_control( $element ) {
256 - if ( Utils::has_pro() && ! $element->get_controls( 'ai_animation' ) ) {
257 - $element->add_control(
258 - 'ai_animation',
259 - [
260 - 'label' => esc_html__( 'Animate With AI', 'elementor' ),
261 - 'type' => Controls_Manager::RAW_HTML,
262 - 'raw' => '
263 - <style>
264 - .elementor-control-ai_animation .elementor-control-content {
265 - display: flex;
266 - flex-direction: row;
267 - justify-content: space-between;
268 - align-items: center;
269 - }
270 - .elementor-control-ai_animation .elementor-control-raw-html {
271 - display: none;
272 - }
273 - </style>',
274 - 'render_type' => 'none',
275 - 'ai' => [
276 - 'active' => true,
277 - 'type' => 'animation',
278 - ],
279 - ]
280 - );
281 - }
282 - }
283 -
284 - private function get_current_screen() {
285 - $is_wc = class_exists( 'WooCommerce' ) && post_type_exists( 'product' );
286 - if ( ! $is_wc ) {
287 - return 'other';
288 - }
289 -
290 - $is_products_page = isset( $_GET['post_type'] ) && 'product' === $_GET['post_type'];
291 - if ( $is_products_page ) {
292 - return 'wc-products';
293 - }
294 -
295 - $screen = get_current_screen();
296 - $is_single_product_page = isset( $screen->post_type ) && ( 'product' === $screen->post_type && 'post' === $screen->base );
297 - if ( $is_single_product_page ) {
298 - return 'wc-single-product';
299 - }
300 -
301 - return 'other';
302 - }
303 -
304 - public function enqueue_ai_products_page_scripts() {
305 - if ( 'wc-products' !== $this->get_current_screen() ) {
306 - return;
307 - }
308 -
309 - $this->add_wc_scripts();
310 - }
311 -
312 - public function enqueue_ai_single_product_page_scripts() {
313 - if ( 'wc-single-product' !== $this->get_current_screen() ) {
314 - return;
315 - }
316 -
317 - $this->add_wc_scripts();
318 - }
319 -
320 - private function add_products_bulk_action( $bulk_actions ) {
321 - $bulk_actions['elementor-ai-unify-product-images'] = __( 'Unify with Elementor AI', 'elementor' );
322 - return $bulk_actions;
323 - }
324 -
325 - public function get_product_images_ajax() {
326 - check_ajax_referer( 'elementor-ai-unify-product-images_nonce', 'nonce' );
327 -
328 - $post_ids = isset( $_POST['post_ids'] ) ? array_map( 'intval', $_POST['post_ids'] ) : [];
329 - $is_galley_only = isset( $_POST['is_galley_only'] ) && sanitize_text_field( wp_unslash( $_POST['is_galley_only'] ) );
330 -
331 - $image_ids = [];
332 -
333 - foreach ( $post_ids as $post_id ) {
334 - if ( $is_galley_only ) {
335 - $product = wc_get_product( $post_id );
336 - $gallery_image_ids = $product->get_gallery_image_ids();
337 - foreach ( $gallery_image_ids as $image_id ) {
338 - $image_ids[] = [
339 - 'productId' => $post_id,
340 - 'id' => $image_id,
341 - 'image_url' => wp_get_attachment_url( $image_id ),
342 - ];
343 - }
344 - continue;
345 - }
346 -
347 - $image_id = get_post_thumbnail_id( $post_id );
348 -
349 - if ( ! $image_id ) {
350 - $product = wc_get_product( $post_id );
351 - $gallery_image_ids = $product->get_gallery_image_ids();
352 - if ( ! empty( $gallery_image_ids ) ) {
353 - $image_id = $gallery_image_ids[0];
354 - }
355 - }
356 -
357 - $image_ids[] = [
358 - 'productId' => $post_id,
359 - 'id' => $image_id ? $image_id : 'No Image',
360 - 'image_url' => $image_id ? wp_get_attachment_url( $image_id ) : 'No Image',
361 - ];
362 - }
363 -
364 - wp_send_json_success( [ 'product_images' => array_slice( $image_ids, 0, 10 ) ] );
365 -
366 - wp_die();
367 - }
368 -
369 - private function get_attachment_id_by_url( $url ) {
370 - $attachments = get_posts( [
371 - 'post_type' => 'attachment',
372 - 'meta_query' => [
373 - [
374 - 'key' => '_wp_attached_file',
375 - 'value' => basename( $url ),
376 - 'compare' => 'LIKE',
377 - ],
378 - ],
379 - 'fields' => 'ids',
380 - 'numberposts' => 1,
381 - ] );
382 -
383 - return ! empty( $attachments ) ? $attachments[0] : null;
384 - }
385 -
386 - public function set_product_images_ajax() {
387 - check_ajax_referer( 'elementor-ai-unify-product-images_nonce', 'nonce' );
388 -
389 - $product_id = isset( $_POST['productId'] ) ? sanitize_text_field( wp_unslash( $_POST['productId'] ) ) : '';
390 - $image_url = isset( $_POST['image_url'] ) ? sanitize_text_field( wp_unslash( $_POST['image_url'] ) ) : '';
391 - $image_to_add = isset( $_POST['image_to_add'] ) ? intval( wp_unslash( $_POST['image_to_add'] ) ) : null;
392 - $image_to_remove = isset( $_POST['image_to_remove'] ) ? intval( wp_unslash( $_POST['image_to_remove'] ) ) : null;
393 - $is_product_gallery = isset( $_POST['is_product_gallery'] ) && sanitize_text_field( wp_unslash( $_POST['is_product_gallery'] ) ) === 'true';
394 -
395 - if ( ! $product_id || ! $image_url ) {
396 - throw new \Exception( 'Product ID and Image URL are required' );
397 - }
398 -
399 - $product = wc_get_product( $product_id );
400 - if ( ! $product ) {
401 - throw new \Exception( 'Product not found' );
402 - }
403 -
404 - $attachment_id = $this->get_attachment_id_by_url( $image_url );
405 - if ( is_wp_error( $attachment_id ) ) {
406 - throw new \Exception( 'Image upload failed' );
407 - }
408 -
409 - if ( $is_product_gallery ) {
410 - $this->update_product_gallery( $product, $image_to_remove, $image_to_add );
411 - } else {
412 - $product->set_image_id( $attachment_id );
413 - $product->save();
414 - }
415 -
416 - wp_send_json_success( [
417 - 'message' => __( 'Image added successfully', 'elementor' ),
418 - ] );
419 - }
420 -
421 - public function enqueue_ai_media_library_upload_screen() {
422 - $screen = get_current_screen();
423 - if ( ! $screen || 'upload' !== $screen->id ) {
424 - return;
425 - }
426 -
427 - $this->enqueue_ai_media_library();
428 - }
429 -
430 - public function enqueue_ai_media_library() {
431 - wp_enqueue_script( 'elementor-ai-media-library',
432 - $this->get_js_assets_url( 'ai-media-library' ),
433 - [
434 - 'jquery',
435 - 'elementor-v2-ui',
436 - 'elementor-v2-icons',
437 - 'media-grid',
438 - ],
439 - ELEMENTOR_VERSION,
440 - true
441 - );
442 -
443 - wp_localize_script(
444 - 'elementor-ai-media-library',
445 - 'ElementorAiConfig',
446 - [
447 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
448 - 'connect_url' => $this->get_ai_connect_url(),
449 - ]
450 - );
451 -
452 - wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
453 - }
454 -
455 - private function enqueue_main_script() {
456 - wp_enqueue_script(
457 - 'elementor-ai',
458 - $this->get_js_assets_url( 'ai' ),
459 - [
460 - 'react',
461 - 'react-dom',
462 - 'backbone-marionette',
463 - 'elementor-web-cli',
464 - 'wp-date',
465 - 'elementor-common',
466 - 'elementor-editor-modules',
467 - 'elementor-editor-document',
468 - 'elementor-v2-ui',
469 - 'elementor-v2-icons',
470 - ],
471 - ELEMENTOR_VERSION,
472 - true
473 - );
474 -
475 - $config = [
476 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
477 - 'connect_url' => $this->get_ai_connect_url(),
478 - ];
479 -
480 - wp_localize_script(
481 - 'elementor-ai',
482 - 'ElementorAiConfig',
483 - $config
484 - );
485 -
486 - wp_set_script_translations( 'elementor-ai', 'elementor' );
487 - }
488 -
489 - private function enqueue_layout_script() {
490 - wp_enqueue_script(
491 - 'elementor-ai-layout',
492 - $this->get_js_assets_url( 'ai-layout' ),
493 - [
494 - 'react',
495 - 'react-dom',
496 - 'backbone-marionette',
497 - 'elementor-common',
498 - 'elementor-web-cli',
499 - 'elementor-editor-modules',
500 - 'elementor-ai',
501 - 'elementor-v2-ui',
502 - 'elementor-v2-icons',
503 - ],
504 - ELEMENTOR_VERSION,
505 - true
506 - );
507 -
508 - wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
509 - }
510 -
511 - private function remove_temporary_containers( $data ) {
512 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
513 - return $data;
514 - }
515 -
516 - // If for some reason the document has been saved during an AI Layout session,
517 - // ensure that the temporary containers are removed from the data.
518 - $data['elements'] = array_filter( $data['elements'], function( $element ) {
519 - $is_preview_container = strpos( $element['id'], 'e-ai-preview-container' ) === 0;
520 - $is_screenshot_container = strpos( $element['id'], 'e-ai-screenshot-container' ) === 0;
521 -
522 - return ! $is_preview_container && ! $is_screenshot_container;
523 79 } );
524 -
525 - return $data;
526 80 }
527 81
528 82 private function get_ai_connect_url() {
529 83 $app = $this->get_ai_app();
@@ -558,35 +112,8 @@
558 112 'usage' => $user_usage,
559 113 ];
560 114 }
561 115
562 - public function ajax_ai_get_remote_config() {
563 - $app = $this->get_ai_app();
564 -
565 - if ( ! $app->is_connected() ) {
566 - return [];
567 - }
568 -
569 - return $app->get_remote_config();
570 - }
571 -
572 - public function ajax_ai_get_remote_frontend_config( $data ) {
573 - $callback = function () use ( $data ) {
574 - return $this->get_ai_app()->get_remote_frontend_config( $data );
575 - };
576 -
577 - return Utils::get_cached_callback( $callback, 'ai_remote_frontend_config-' . get_current_user_id(), HOUR_IN_SECONDS );
578 - }
579 -
580 - public function verify_upload_permissions( $data ) {
581 - $referer = wp_get_referer();
582 -
583 - if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) {
584 - return;
585 - }
586 - $this->verify_permissions( $data['editor_post_id'] );
587 - }
588 -
589 116 private function verify_permissions( $editor_post_id ) {
590 117 $document = Plugin::$instance->documents->get( $editor_post_id );
591 118
592 119 if ( ! $document ) {
@@ -592,19 +119,15 @@
592 119 if ( ! $document ) {
593 120 throw new \Exception( 'Document not found' );
594 121 }
595 122
596 - if ( $document->is_built_with_elementor() ) {
597 - if ( ! $document->is_editable_by_current_user() ) {
598 - throw new \Exception( 'Access denied' );
599 - }
600 - } elseif ( ! current_user_can( 'edit_post', $editor_post_id ) ) {
601 - throw new \Exception( 'Access denied' );
123 + if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
124 + throw new \Exception( 'Access denied' );
602 125 }
603 126 }
604 127
605 128 public function ajax_ai_get_image_prompt_enhancer( $data ) {
606 - $this->verify_upload_permissions( $data );
129 + $this->verify_permissions( $data['editor_post_id'] );
607 130
608 131 $app = $this->get_ai_app();
609 132
610 133 if ( empty( $data['prompt'] ) ) {
@@ -613,12 +136,13 @@
613 136
614 137 if ( ! $app->is_connected() ) {
615 138 throw new \Exception( 'not_connected' );
616 139 }
617 - $request_ids = $this->get_request_ids( $data['payload'] );
618 140
619 - $result = $app->get_image_prompt_enhanced( $data['prompt'], [], $request_ids );
620 - $this->throw_on_error( $result );
141 + $result = $app->get_image_prompt_enhanced( $data['prompt'] );
142 + if ( is_wp_error( $result ) ) {
143 + throw new \Exception( $result->get_error_message() );
144 + }
621 145
622 146 return [
623 147 'text' => $result['text'],
624 148 'response_id' => $result['responseId'],
@@ -630,9 +154,9 @@
630 154 $this->verify_permissions( $data['editor_post_id'] );
631 155
632 156 $app = $this->get_ai_app();
633 157
634 - if ( empty( $data['payload']['prompt'] ) ) {
158 + if ( empty( $data['prompt'] ) ) {
635 159 throw new \Exception( 'Missing prompt' );
636 160 }
637 161
638 162 if ( ! $app->is_connected() ) {
@@ -640,39 +164,13 @@
640 164 }
641 165
642 166 $context = $this->get_request_context( $data );
643 167
644 - $request_ids = $this->get_request_ids( $data['payload'] );
645 -
646 - $result = $app->get_completion_text( $data['payload']['prompt'], $context, $request_ids );
647 - $this->throw_on_error( $result );
648 -
649 - return [
650 - 'text' => $result['text'],
651 - 'response_id' => $result['responseId'],
652 - 'usage' => $result['usage'],
653 - ];
654 - }
655 -
656 -
657 - public function ajax_ai_get_excerpt( $data ): array {
658 - $app = $this->get_ai_app();
659 -
660 - if ( empty( $data['payload']['content'] ) ) {
661 - throw new \Exception( 'Missing content' );
168 + $result = $app->get_completion_text( $data['prompt'], $context );
169 + if ( is_wp_error( $result ) ) {
170 + throw new \Exception( $result->get_error_message() );
662 171 }
663 172
664 - if ( ! $app->is_connected() ) {
665 - throw new \Exception( 'Not connected' );
666 - }
667 -
668 - $context = $this->get_request_context( $data );
669 -
670 - $request_ids = $this->get_request_ids( $data['payload'] );
671 -
672 - $result = $app->get_excerpt( $data['payload']['content'], $context, $request_ids );
673 - $this->throw_on_error( $result );
674 -
675 173 return [
676 174 'text' => $result['text'],
677 175 'response_id' => $result['responseId'],
678 176 'usage' => $result['usage'],
@@ -678,36 +176,9 @@
678 176 'usage' => $result['usage'],
679 177 ];
680 178 }
681 179
682 - public function ajax_ai_get_featured_image( $data ): array {
683 - $this->verify_upload_permissions( $data );
684 -
685 - if ( empty( $data['payload']['prompt'] ) ) {
686 - throw new \Exception( 'Missing prompt' );
687 - }
688 -
689 - $app = $this->get_ai_app();
690 -
691 - if ( ! $app->is_connected() ) {
692 - throw new \Exception( 'not_connected' );
693 - }
694 -
695 - $context = $this->get_request_context( $data );
696 - $request_ids = $this->get_request_ids( $data['payload'] );
697 -
698 - $result = $app->get_featured_image( $data, $context, $request_ids );
699 -
700 - $this->throw_on_error( $result );
701 -
702 - return [
703 - 'images' => $result['images'],
704 - 'response_id' => $result['responseId'],
705 - 'usage' => $result['usage'],
706 - ];
707 - }
708 -
709 - private function get_ai_app(): Ai {
180 + private function get_ai_app() : Ai {
710 181 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
711 182 }
712 183
713 184 private function get_request_context( $data ) {
@@ -717,26 +188,18 @@
717 188
718 189 return $data['context'];
719 190 }
720 191
721 - private function get_request_ids( $data ) {
722 - if ( empty( $data['requestIds'] ) ) {
723 - return new \stdClass();
724 - }
725 -
726 - return $data['requestIds'];
727 - }
728 -
729 192 public function ajax_ai_get_edit_text( $data ) {
730 193 $this->verify_permissions( $data['editor_post_id'] );
731 194
732 195 $app = $this->get_ai_app();
733 196
734 - if ( empty( $data['payload']['input'] ) ) {
197 + if ( empty( $data['input'] ) ) {
735 198 throw new \Exception( 'Missing input' );
736 199 }
737 200
738 - if ( empty( $data['payload']['instruction'] ) ) {
201 + if ( empty( $data['instruction'] ) ) {
739 202 throw new \Exception( 'Missing instruction' );
740 203 }
741 204
742 205 if ( ! $app->is_connected() ) {
@@ -744,13 +207,13 @@
744 207 }
745 208
746 209 $context = $this->get_request_context( $data );
747 210
748 - $request_ids = $this->get_request_ids( $data['payload'] );
211 + $result = $app->get_edit_text( $data['input'], $data['instruction'], $context );
212 + if ( is_wp_error( $result ) ) {
213 + throw new \Exception( $result->get_error_message() );
214 + }
749 215
750 - $result = $app->get_edit_text( $data, $context, $request_ids );
751 - $this->throw_on_error( $result );
752 -
753 216 return [
754 217 'text' => $result['text'],
755 218 'response_id' => $result['responseId'],
756 219 'usage' => $result['usage'],
@@ -759,13 +222,13 @@
759 222
760 223 public function ajax_ai_get_custom_code( $data ) {
761 224 $app = $this->get_ai_app();
762 225
763 - if ( empty( $data['payload']['prompt'] ) ) {
226 + if ( empty( $data['prompt'] ) ) {
764 227 throw new \Exception( 'Missing prompt' );
765 228 }
766 229
767 - if ( empty( $data['payload']['language'] ) ) {
230 + if ( empty( $data['language'] ) ) {
768 231 throw new \Exception( 'Missing language' );
769 232 }
770 233
771 234 if ( ! $app->is_connected() ) {
@@ -773,13 +236,13 @@
773 236 }
774 237
775 238 $context = $this->get_request_context( $data );
776 239
777 - $request_ids = $this->get_request_ids( $data['payload'] );
240 + $result = $app->get_custom_code( $data['prompt'], $data['language'], $context );
241 + if ( is_wp_error( $result ) ) {
242 + throw new \Exception( $result->get_error_message() );
243 + }
778 244
779 - $result = $app->get_custom_code( $data, $context, $request_ids );
780 - $this->throw_on_error( $result );
781 -
782 245 return [
783 246 'text' => $result['text'],
784 247 'response_id' => $result['responseId'],
785 248 'usage' => $result['usage'],
@@ -790,17 +253,17 @@
790 253 $this->verify_permissions( $data['editor_post_id'] );
791 254
792 255 $app = $this->get_ai_app();
793 256
794 - if ( empty( $data['payload']['prompt'] ) ) {
257 + if ( empty( $data['prompt'] ) ) {
795 258 throw new \Exception( 'Missing prompt' );
796 259 }
797 260
798 - if ( empty( $data['payload']['html_markup'] ) ) {
261 + if ( empty( $data['html_markup'] ) ) {
799 262 $data['html_markup'] = '';
800 263 }
801 264
802 - if ( empty( $data['payload']['element_id'] ) ) {
265 + if ( empty( $data['element_id'] ) ) {
803 266 throw new \Exception( 'Missing element_id' );
804 267 }
805 268
806 269 if ( ! $app->is_connected() ) {
@@ -807,12 +270,13 @@
807 270 throw new \Exception( 'not_connected' );
808 271 }
809 272
810 273 $context = $this->get_request_context( $data );
811 - $request_ids = $this->get_request_ids( $data['payload'] );
812 274
813 - $result = $app->get_custom_css( $data, $context, $request_ids );
814 - $this->throw_on_error( $result );
275 + $result = $app->get_custom_css( $data['prompt'], $data['html_markup'], $data['element_id'], $context );
276 + if ( is_wp_error( $result ) ) {
277 + throw new \Exception( $result->get_error_message() );
278 + }
815 279
816 280 return [
817 281 'text' => $result['text'],
818 282 'response_id' => $result['responseId'],
@@ -846,11 +310,11 @@
846 310 return [];
847 311 }
848 312
849 313 public function ajax_ai_get_text_to_image( $data ) {
850 - $this->verify_upload_permissions( $data );
314 + $this->verify_permissions( $data['editor_post_id'] );
851 315
852 - if ( empty( $data['payload']['prompt'] ) ) {
316 + if ( empty( $data['prompt'] ) ) {
853 317 throw new \Exception( 'Missing prompt' );
854 318 }
855 319
856 320 $app = $this->get_ai_app();
@@ -859,13 +323,14 @@
859 323 throw new \Exception( 'not_connected' );
860 324 }
861 325
862 326 $context = $this->get_request_context( $data );
863 - $request_ids = $this->get_request_ids( $data['payload'] );
864 327
865 - $result = $app->get_text_to_image( $data, $context, $request_ids );
328 + $result = $app->get_text_to_image( $data['prompt'], $data['promptSettings'], $context );
866 329
867 - $this->throw_on_error( $result );
330 + if ( is_wp_error( $result ) ) {
331 + throw new \Exception( $result->get_error_message() );
332 + }
868 333
869 334 return [
870 335 'images' => $result['images'],
871 336 'response_id' => $result['responseId'],
@@ -873,17 +338,21 @@
873 338 ];
874 339 }
875 340
876 341 public function ajax_ai_get_image_to_image( $data ) {
877 - $this->verify_upload_permissions( $data );
342 + $this->verify_permissions( $data['editor_post_id'] );
878 343
879 344 $app = $this->get_ai_app();
880 345
881 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
346 + if ( empty( $data['prompt'] ) ) {
347 + throw new \Exception( 'Missing prompt' );
348 + }
349 +
350 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
882 351 throw new \Exception( 'Missing Image' );
883 352 }
884 353
885 - if ( empty( $data['payload']['settings'] ) ) {
354 + if ( empty( $data['promptSettings'] ) ) {
886 355 throw new \Exception( 'Missing prompt settings' );
887 356 }
888 357
889 358 if ( ! $app->is_connected() ) {
@@ -890,17 +359,18 @@
890 359 throw new \Exception( 'not_connected' );
891 360 }
892 361
893 362 $context = $this->get_request_context( $data );
894 - $request_ids = $this->get_request_ids( $data['payload'] );
895 363
896 364 $result = $app->get_image_to_image( [
897 - 'prompt' => $data['payload']['prompt'],
898 - 'promptSettings' => $data['payload']['settings'],
899 - 'attachment_id' => $data['payload']['image']['id'],
900 - ], $context, $request_ids );
365 + 'prompt' => $data['prompt'],
366 + 'promptSettings' => $data['promptSettings'],
367 + 'attachment_id' => $data['image']['id'],
368 + ], $context );
901 369
902 - $this->throw_on_error( $result );
370 + if ( is_wp_error( $result ) ) {
371 + throw new \Exception( $result->get_error_message() );
372 + }
903 373
904 374 return [
905 375 'images' => $result['images'],
906 376 'response_id' => $result['responseId'],
@@ -908,17 +378,17 @@
908 378 ];
909 379 }
910 380
911 381 public function ajax_ai_get_image_to_image_upscale( $data ) {
912 - $this->verify_upload_permissions( $data );
382 + $this->verify_permissions( $data['editor_post_id'] );
913 383
914 384 $app = $this->get_ai_app();
915 385
916 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
386 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
917 387 throw new \Exception( 'Missing Image' );
918 388 }
919 389
920 - if ( empty( $data['payload']['promptSettings'] ) ) {
390 + if ( empty( $data['promptSettings'] ) ) {
921 391 throw new \Exception( 'Missing prompt settings' );
922 392 }
923 393
924 394 if ( ! $app->is_connected() ) {
@@ -925,16 +395,17 @@
925 395 throw new \Exception( 'not_connected' );
926 396 }
927 397
928 398 $context = $this->get_request_context( $data );
929 - $request_ids = $this->get_request_ids( $data['payload'] );
930 399
931 400 $result = $app->get_image_to_image_upscale( [
932 - 'promptSettings' => $data['payload']['promptSettings'],
933 - 'attachment_id' => $data['payload']['image']['id'],
934 - ], $context, $request_ids );
401 + 'promptSettings' => $data['promptSettings'],
402 + 'attachment_id' => $data['image']['id'],
403 + ], $context );
935 404
936 - $this->throw_on_error( $result );
405 + if ( is_wp_error( $result ) ) {
406 + throw new \Exception( $result->get_error_message() );
407 + }
937 408
938 409 return [
939 410 'images' => $result['images'],
940 411 'response_id' => $result['responseId'],
@@ -942,17 +413,17 @@
942 413 ];
943 414 }
944 415
945 416 public function ajax_ai_get_image_to_image_replace_background( $data ) {
946 - $this->verify_upload_permissions( $data );
417 + $this->verify_permissions( $data['editor_post_id'] );
947 418
948 419 $app = $this->get_ai_app();
949 420
950 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
421 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
951 422 throw new \Exception( 'Missing Image' );
952 423 }
953 424
954 - if ( empty( $data['payload']['prompt'] ) ) {
425 + if ( empty( $data['prompt'] ) ) {
955 426 throw new \Exception( 'Prompt Missing' );
956 427 }
957 428
958 429 if ( ! $app->is_connected() ) {
@@ -959,16 +430,17 @@
959 430 throw new \Exception( 'not_connected' );
960 431 }
961 432
962 433 $context = $this->get_request_context( $data );
963 - $request_ids = $this->get_request_ids( $data['payload'] );
964 434
965 435 $result = $app->get_image_to_image_replace_background( [
966 - 'attachment_id' => $data['payload']['image']['id'],
967 - 'prompt' => $data['payload']['prompt'],
968 - ], $context, $request_ids );
436 + 'attachment_id' => $data['image']['id'],
437 + 'prompt' => $data['prompt'],
438 + ], $context );
969 439
970 - $this->throw_on_error( $result );
440 + if ( is_wp_error( $result ) ) {
441 + throw new \Exception( $result->get_error_message() );
442 + }
971 443
972 444 return [
973 445 'images' => $result['images'],
974 446 'response_id' => $result['responseId'],
@@ -976,13 +448,13 @@
976 448 ];
977 449 }
978 450
979 451 public function ajax_ai_get_image_to_image_remove_background( $data ) {
980 - $this->verify_upload_permissions( $data );
452 + $this->verify_permissions( $data['editor_post_id'] );
981 453
982 454 $app = $this->get_ai_app();
983 455
984 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
456 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
985 457 throw new \Exception( 'Missing Image' );
986 458 }
987 459
988 460 if ( ! $app->is_connected() ) {
@@ -989,14 +461,16 @@
989 461 throw new \Exception( 'not_connected' );
990 462 }
991 463
992 464 $context = $this->get_request_context( $data );
993 - $request_ids = $this->get_request_ids( $data['payload'] );
465 +
994 466 $result = $app->get_image_to_image_remove_background( [
995 - 'attachment_id' => $data['payload']['image']['id'],
996 - ], $context, $request_ids );
467 + 'attachment_id' => $data['image']['id'],
468 + ], $context );
997 469
998 - $this->throw_on_error( $result );
470 + if ( is_wp_error( $result ) ) {
471 + throw new \Exception( $result->get_error_message() );
472 + }
999 473
1000 474 return [
1001 475 'images' => $result['images'],
1002 476 'response_id' => $result['responseId'],
@@ -1004,75 +478,44 @@
1004 478 ];
1005 479 }
1006 480
1007 481 public function ajax_ai_get_image_to_image_mask( $data ) {
1008 - $this->verify_upload_permissions( $data );
482 + $this->verify_permissions( $data['editor_post_id'] );
1009 483
1010 484 $app = $this->get_ai_app();
1011 485
1012 - if ( empty( $data['payload']['prompt'] ) ) {
486 + if ( empty( $data['prompt'] ) ) {
1013 487 throw new \Exception( 'Missing prompt' );
1014 488 }
1015 489
1016 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
490 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
1017 491 throw new \Exception( 'Missing Image' );
1018 492 }
1019 493
1020 - if ( empty( $data['payload']['settings'] ) ) {
494 + if ( empty( $data['promptSettings'] ) ) {
1021 495 throw new \Exception( 'Missing prompt settings' );
1022 496 }
1023 497
1024 - if ( empty( $data['payload']['mask'] ) ) {
1025 - throw new \Exception( 'Missing Mask' );
1026 - }
1027 -
1028 - $context = $this->get_request_context( $data );
1029 - $request_ids = $this->get_request_ids( $data['payload'] );
1030 -
1031 - $result = $app->get_image_to_image_mask( [
1032 - 'prompt' => $data['payload']['prompt'],
1033 - 'attachment_id' => $data['payload']['image']['id'],
1034 - 'mask' => $data['payload']['mask'],
1035 - ], $context, $request_ids );
1036 -
1037 - $this->throw_on_error( $result );
1038 -
1039 - return [
1040 - 'images' => $result['images'],
1041 - 'response_id' => $result['responseId'],
1042 - 'usage' => $result['usage'],
1043 - ];
1044 - }
1045 - public function ajax_ai_get_image_to_image_mask_cleanup( $data ) {
1046 - $this->verify_upload_permissions( $data );
1047 -
1048 - $app = $this->get_ai_app();
1049 -
1050 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
1051 - throw new \Exception( 'Missing Image' );
1052 - }
1053 -
1054 - if ( empty( $data['payload']['settings'] ) ) {
1055 - throw new \Exception( 'Missing prompt settings' );
1056 - }
1057 -
1058 498 if ( ! $app->is_connected() ) {
1059 499 throw new \Exception( 'not_connected' );
1060 500 }
1061 501
1062 - if ( empty( $data['payload']['mask'] ) ) {
502 + if ( empty( $data['mask'] ) ) {
1063 503 throw new \Exception( 'Missing Mask' );
1064 504 }
1065 505
1066 506 $context = $this->get_request_context( $data );
1067 - $request_ids = $this->get_request_ids( $data['payload'] );
1068 507
1069 - $result = $app->get_image_to_image_mask_cleanup( [
1070 - 'attachment_id' => $data['payload']['image']['id'],
1071 - 'mask' => $data['payload']['mask'],
1072 - ], $context, $request_ids );
508 + $result = $app->get_image_to_image_mask( [
509 + 'prompt' => $data['prompt'],
510 + 'promptSettings' => $data['promptSettings'],
511 + 'attachment_id' => $data['image']['id'],
512 + 'mask' => $data['mask'],
513 + ], $context );
1073 514
1074 - $this->throw_on_error( $result );
515 + if ( is_wp_error( $result ) ) {
516 + throw new \Exception( $result->get_error_message() );
517 + }
1075 518
1076 519 return [
1077 520 'images' => $result['images'],
1078 521 'response_id' => $result['responseId'],
@@ -1078,32 +521,35 @@
1078 521 'response_id' => $result['responseId'],
1079 522 'usage' => $result['usage'],
1080 523 ];
1081 524 }
1082 -
1083 525 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1084 - $this->verify_upload_permissions( $data );
526 + $this->verify_permissions( $data['editor_post_id'] );
1085 527
1086 528 $app = $this->get_ai_app();
1087 529
530 + if ( empty( $data['prompt'] ) ) {
531 + throw new \Exception( 'Missing prompt' );
532 + }
533 +
1088 534 if ( ! $app->is_connected() ) {
1089 535 throw new \Exception( 'not_connected' );
1090 536 }
1091 537
1092 - if ( empty( $data['payload']['mask'] ) ) {
538 + if ( empty( $data['mask'] ) ) {
1093 539 throw new \Exception( 'Missing Expended Image' );
1094 540 }
1095 541
1096 542 $context = $this->get_request_context( $data );
1097 - $request_ids = $this->get_request_ids( $data['payload'] );
543 +
1098 544 $result = $app->get_image_to_image_out_painting( [
1099 - 'size' => $data['payload']['size'],
1100 - 'position' => $data['payload']['position'],
1101 - 'mask' => $data['payload']['mask'],
1102 - 'image_base64' => $data['payload']['image_base64'],
1103 - ], $context, $request_ids );
545 + 'prompt' => $data['prompt'],
546 + 'mask' => $data['mask'],
547 + ], $context );
1104 548
1105 - $this->throw_on_error( $result );
549 + if ( is_wp_error( $result ) ) {
550 + throw new \Exception( $result->get_error_message() );
551 + }
1106 552
1107 553 return [
1108 554 'images' => $result['images'],
1109 555 'response_id' => $result['responseId'],
@@ -1111,10 +557,8 @@
1111 557 ];
1112 558 }
1113 559
1114 560 public function ajax_ai_upload_image( $data ) {
1115 - $this->verify_upload_permissions( $data );
1116 -
1117 561 if ( empty( $data['image'] ) ) {
1118 562 throw new \Exception( 'Missing image data' );
1119 563 }
1120 564
@@ -1126,14 +570,14 @@
1126 570
1127 571 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1128 572
1129 573 if ( is_wp_error( $image_data ) ) {
1130 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
574 + throw new \Exception( $image_data->get_error_message() );
1131 575 }
1132 576
1133 577 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1134 - $app = $this->get_ai_app();
1135 - $app->set_used_gallery_image( $image['id'] );
578 + $app = $this->get_ai_app();
579 + $app->set_used_gallery_image( $image['id'] );
1136 580 }
1137 581
1138 582 return [
1139 583 'image' => array_merge( $image_data, $data ),
@@ -1139,197 +583,15 @@
1139 583 'image' => array_merge( $image_data, $data ),
1140 584 ];
1141 585 }
1142 586
1143 - public function ajax_ai_generate_layout( $data ) {
1144 - $this->verify_permissions( $data['editor_post_id'] );
1145 -
1146 - $app = $this->get_ai_app();
1147 -
1148 - if ( empty( $data['prompt'] ) && empty( $data['attachments'] ) ) {
1149 - throw new \Exception( 'Missing prompt / attachments' );
1150 - }
1151 -
1152 - if ( ! $app->is_connected() ) {
1153 - throw new \Exception( 'not_connected' );
1154 - }
1155 -
1156 - $result = $app->generate_layout(
1157 - $data,
1158 - $this->prepare_generate_layout_context( $data )
1159 - );
1160 -
1161 - if ( is_wp_error( $result ) ) {
1162 - $message = $result->get_error_message();
1163 -
1164 - if ( is_array( $message ) ) {
1165 - $message = implode( ', ', $message );
1166 - throw new \Exception( esc_html( $message ) );
1167 - }
1168 -
1169 - $this->throw_on_error( $result );
1170 - }
1171 -
1172 - $elements = $result['text']['elements'] ?? [];
1173 - $base_template_id = $result['baseTemplateId'] ?? null;
1174 - $template_type = $result['templateType'] ?? null;
1175 -
1176 - if ( empty( $elements ) || ! is_array( $elements ) ) {
1177 - throw new \Exception( 'unknown_error' );
1178 - }
1179 -
1180 - if ( 1 === count( $elements ) ) {
1181 - $template = $elements[0];
1182 - } else {
1183 - $template = [
1184 - 'elType' => 'container',
1185 - 'elements' => $elements,
1186 - 'settings' => [
1187 - 'content_width' => 'full',
1188 - 'flex_gap' => [
1189 - 'column' => '0',
1190 - 'row' => '0',
1191 - 'unit' => 'px',
1192 - ],
1193 - 'padding' => [
1194 - 'unit' => 'px',
1195 - 'top' => '0',
1196 - 'right' => '0',
1197 - 'bottom' => '0',
1198 - 'left' => '0',
1199 - 'isLinked' => true,
1200 - ],
1201 - ],
1202 - ];
1203 - }
1204 -
1205 - return [
1206 - 'all' => [],
1207 - 'text' => $template,
1208 - 'response_id' => $result['responseId'],
1209 - 'usage' => $result['usage'],
1210 - 'base_template_id' => $base_template_id,
1211 - 'template_type' => $template_type,
1212 - ];
1213 - }
1214 -
1215 - public function ajax_ai_get_layout_prompt_enhancer( $data ) {
1216 - $this->verify_permissions( $data['editor_post_id'] );
1217 -
1218 - $app = $this->get_ai_app();
1219 -
1220 - if ( empty( $data['prompt'] ) ) {
1221 - throw new \Exception( 'Missing prompt' );
1222 - }
1223 -
1224 - if ( ! $app->is_connected() ) {
1225 - throw new \Exception( 'not_connected' );
1226 - }
1227 -
1228 - $result = $app->get_layout_prompt_enhanced(
1229 - $data['prompt'],
1230 - $data['enhance_type'],
1231 - $this->prepare_generate_layout_context( $data )
1232 - );
1233 -
1234 - $this->throw_on_error( $result );
1235 -
1236 - return [
1237 - 'text' => $result['text'] ?? $data['prompt'],
1238 - 'response_id' => $result['responseId'] ?? '',
1239 - 'usage' => $result['usage'] ?? '',
1240 - ];
1241 - }
1242 -
1243 - private function prepare_generate_layout_context( $data ) {
1244 - $request_context = $this->get_request_context( $data );
1245 - $kit = Plugin::$instance->kits_manager->get_active_kit();
1246 -
1247 - if ( ! $kit ) {
1248 - return $request_context;
1249 - }
1250 -
1251 - $kits_data = Collection::make( $kit->get_data()['settings'] ?? [] );
1252 -
1253 - $colors = $kits_data
1254 - ->filter( function ( $_, $key ) {
1255 - return in_array( $key, [ 'system_colors', 'custom_colors' ], true );
1256 - } )
1257 - ->flatten()
1258 - ->filter( function ( $val ) {
1259 - return ! empty( $val['_id'] );
1260 - } )
1261 - ->map( function ( $val ) {
1262 - return [
1263 - 'id' => $val['_id'],
1264 - 'label' => $val['title'] ?? null,
1265 - 'value' => $val['color'] ?? null,
1266 - ];
1267 - } );
1268 -
1269 - $typography = $kits_data
1270 - ->filter( function ( $_, $key ) {
1271 - return in_array( $key, [ 'system_typography', 'custom_typography' ], true );
1272 - } )
1273 - ->flatten()
1274 - ->filter( function ( $val ) {
1275 - return ! empty( $val['_id'] );
1276 - } )
1277 - ->map( function ( $val ) {
1278 - $font_size = null;
1279 -
1280 - if ( isset(
1281 - $val['typography_font_size']['unit'],
1282 - $val['typography_font_size']['size']
1283 - ) ) {
1284 - $prop = $val['typography_font_size'];
1285 -
1286 - $font_size = 'custom' === $prop['unit']
1287 - ? $prop['size']
1288 - : $prop['size'] . $prop['unit'];
1289 - }
1290 -
1291 - return [
1292 - 'id' => $val['_id'],
1293 - 'label' => $val['title'] ?? null,
1294 - 'value' => [
1295 - 'family' => $val['typography_font_family'] ?? null,
1296 - 'weight' => $val['typography_font_weight'] ?? null,
1297 - 'style' => $val['typography_font_style'] ?? null,
1298 - 'size' => $font_size,
1299 - ],
1300 - ];
1301 - } );
1302 -
1303 - $request_context['globals'] = [
1304 - 'colors' => $colors->all(),
1305 - 'typography' => $typography->all(),
1306 - ];
1307 -
1308 - return $request_context;
1309 - }
1310 -
1311 587 private function upload_image( $image_url, $image_title, $parent_post_id = 0 ) {
1312 588 if ( ! current_user_can( 'upload_files' ) ) {
1313 589 throw new \Exception( 'Not Allowed to Upload images' );
1314 590 }
1315 591
1316 - $uploads_manager = new \Elementor\Core\Files\Uploads_Manager();
1317 - if ( $uploads_manager::are_unfiltered_uploads_enabled() ) {
1318 - Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
1319 - add_filter( 'wp_handle_sideload_prefilter', [ Plugin::$instance->uploads_manager, 'handle_elementor_upload' ] );
1320 - add_filter( 'image_sideload_extensions', function( $extensions ) {
1321 - $extensions[] = 'svg';
1322 - return $extensions;
1323 - });
1324 - }
1325 -
1326 592 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1327 593
1328 - if ( is_wp_error( $attachment_id ) ) {
1329 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1330 - }
1331 -
1332 594 if ( ! empty( $attachment_id['error'] ) ) {
1333 595 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1334 596 }
1335 597
@@ -1334,264 +596,10 @@
1334 596 }
1335 597
1336 598 return [
1337 599 'id' => $attachment_id,
1338 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1339 - 'alt' => esc_attr( $image_title ),
600 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
601 + 'alt' => $image_title,
1340 602 'source' => 'library',
1341 603 ];
1342 - }
1343 -
1344 - public function ajax_ai_get_history( $data ): array {
1345 - $type = $data['type'] ?? self::HISTORY_TYPE_ALL;
1346 -
1347 - if ( ! in_array( $type, self::VALID_HISTORY_TYPES, true ) ) {
1348 - throw new \Exception( 'Invalid history type' );
1349 - }
1350 -
1351 - $page = sanitize_text_field( $data['page'] ?? 1 );
1352 - $limit = sanitize_text_field( $data['limit'] ?? 10 );
1353 -
1354 - $app = $this->get_ai_app();
1355 -
1356 - if ( ! $app->is_connected() ) {
1357 - throw new \Exception( 'not_connected' );
1358 - }
1359 -
1360 - $context = $this->get_request_context( $data );
1361 -
1362 - $result = $app->get_history_by_type( $type, $page, $limit, $context );
1363 -
1364 - if ( is_wp_error( $result ) ) {
1365 - throw new \Exception( esc_html( $result->get_error_message() ) );
1366 - }
1367 -
1368 - return $result;
1369 - }
1370 -
1371 - public function ajax_ai_delete_history_item( $data ): array {
1372 - if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) {
1373 - throw new \Exception( 'Missing id parameter' );
1374 - }
1375 -
1376 - $app = $this->get_ai_app();
1377 -
1378 - if ( ! $app->is_connected() ) {
1379 - throw new \Exception( 'not_connected' );
1380 - }
1381 -
1382 - $context = $this->get_request_context( $data );
1383 -
1384 - $result = $app->delete_history_item( $data['id'], $context );
1385 -
1386 - if ( is_wp_error( $result ) ) {
1387 - throw new \Exception( esc_html( $result->get_error_message() ) );
1388 - }
1389 -
1390 - return [];
1391 - }
1392 -
1393 - public function ajax_ai_toggle_favorite_history_item( $data ): array {
1394 - if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) {
1395 - throw new \Exception( 'Missing id parameter' );
1396 - }
1397 -
1398 - $app = $this->get_ai_app();
1399 -
1400 - if ( ! $app->is_connected() ) {
1401 - throw new \Exception( 'not_connected' );
1402 - }
1403 -
1404 - $context = $this->get_request_context( $data );
1405 -
1406 - $result = $app->toggle_favorite_history_item( $data['id'], $context );
1407 -
1408 - if ( is_wp_error( $result ) ) {
1409 - throw new \Exception( esc_html( $result->get_error_message() ) );
1410 - }
1411 -
1412 - return [];
1413 - }
1414 -
1415 - public function ajax_ai_get_product_image_unification( $data ): array {
1416 - if ( ! empty( $data['payload']['postId'] ) ) {
1417 - $data['editor_post_id'] = $data['payload']['postId'];
1418 - }
1419 - $this->verify_upload_permissions( $data );
1420 -
1421 - $app = $this->get_ai_app();
1422 -
1423 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
1424 - throw new \Exception( 'Missing Image' );
1425 - }
1426 -
1427 - if ( empty( $data['payload']['settings'] ) ) {
1428 - throw new \Exception( 'Missing prompt settings' );
1429 - }
1430 -
1431 - if ( ! $app->is_connected() ) {
1432 - throw new \Exception( 'not_connected' );
1433 - }
1434 -
1435 - $context = $this->get_request_context( $data );
1436 - $request_ids = $this->get_request_ids( $data['payload'] );
1437 -
1438 - $result = $app->get_unify_product_images( [
1439 - 'promptSettings' => $data['payload']['settings'],
1440 - 'attachment_id' => $data['payload']['image']['id'],
1441 - 'featureIdentifier' => $data['payload']['featureIdentifier'] ?? '',
1442 - ], $context, $request_ids );
1443 -
1444 - $this->throw_on_error( $result );
1445 -
1446 - return [
1447 - 'images' => $result['images'],
1448 - 'response_id' => $result['responseId'],
1449 - 'usage' => $result['usage'],
1450 - ];
1451 - }
1452 -
1453 - public function ajax_ai_get_animation( $data ): array {
1454 - $this->verify_upload_permissions( $data );
1455 -
1456 - $app = $this->get_ai_app();
1457 -
1458 - if ( empty( $data['payload']['prompt'] ) ) {
1459 - throw new \Exception( 'Missing prompt' );
1460 - }
1461 -
1462 - if ( empty( $data['payload']['motionEffectType'] ) ) {
1463 - throw new \Exception( 'Missing animation type' );
1464 - }
1465 -
1466 - if ( ! $app->is_connected() ) {
1467 - throw new \Exception( 'not_connected' );
1468 - }
1469 -
1470 - $context = $this->get_request_context( $data );
1471 -
1472 - $request_ids = $this->get_request_ids( $data['payload'] );
1473 -
1474 - $result = $app->get_animation( $data, $context, $request_ids );
1475 - $this->throw_on_error( $result );
1476 -
1477 - return [
1478 - 'text' => $result['text'],
1479 - 'response_id' => $result['responseId'],
1480 - 'usage' => $result['usage'],
1481 - ];
1482 - }
1483 -
1484 - /**
1485 - * @param mixed $result
1486 - */
1487 - private function throw_on_error( $result ): void {
1488 - if ( is_wp_error( $result ) ) {
1489 - wp_send_json_error( [
1490 - 'message' => esc_html( $result->get_error_message() ),
1491 - 'extra_data' => $result->get_error_data(),
1492 - ] );
1493 - }
1494 - }
1495 -
1496 - /**
1497 - * @return void
1498 - */
1499 - public function add_wc_scripts(): void {
1500 - wp_enqueue_script( 'elementor-ai-unify-product-images',
1501 - $this->get_js_assets_url( 'ai-unify-product-images' ),
1502 - [
1503 - 'jquery',
1504 - 'elementor-v2-ui',
1505 - 'elementor-v2-icons',
1506 - 'wp-components',
1507 - 'elementor-common',
1508 - ],
1509 - ELEMENTOR_VERSION,
1510 - true
1511 - );
1512 -
1513 - wp_localize_script(
1514 - 'elementor-ai-unify-product-images',
1515 - 'UnifyProductImagesConfig',
1516 - [
1517 - 'get_product_images_url' => admin_url( 'admin-ajax.php' ),
1518 - 'set_product_images_url' => admin_url( 'admin-ajax.php' ),
1519 - 'nonce' => wp_create_nonce( 'elementor-ai-unify-product-images_nonce' ),
1520 - 'placeholder' => ELEMENTOR_ASSETS_URL . 'images/app/ai/product-image-unification-example.gif?' . ELEMENTOR_VERSION,
1521 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
1522 - 'connect_url' => $this->get_ai_connect_url(),
1523 - ]
1524 - );
1525 -
1526 - add_filter( 'bulk_actions-edit-product', function ( $data ) {
1527 - return $this->add_products_bulk_action( $data );
1528 - });
1529 -
1530 - wp_set_script_translations( 'elementor-ai-unify-product-images', 'elementor' );
1531 - }
1532 -
1533 - /**
1534 - * @param $product
1535 - * @param int|null $image_to_remove
1536 - * @param int|null $image_to_add
1537 - * @return void
1538 - */
1539 - private function update_product_gallery( $product, ?int $image_to_remove, ?int $image_to_add ): void {
1540 - $gallery_image_ids = $product->get_gallery_image_ids();
1541 -
1542 - $index = array_search( $image_to_remove, $gallery_image_ids, true );
1543 - if ( false !== $index ) {
1544 - unset( $gallery_image_ids[ $index ] );
1545 - }
1546 -
1547 - if ( ! in_array( $image_to_add, $gallery_image_ids, true ) ) {
1548 - $gallery_image_ids[] = $image_to_add;
1549 - }
1550 -
1551 - $product->set_gallery_image_ids( $gallery_image_ids );
1552 - $product->save();
1553 - }
1554 -
1555 - private function should_display_create_with_ai_banner() {
1556 - $elementor_pages = new \WP_Query( [
1557 - 'post_type' => 'page',
1558 - 'post_status' => 'publish',
1559 - 'fields' => 'ids',
1560 - 'posts_per_page' => self::MIN_PAGES_FOR_CREATE_WITH_AI_BANNER + 1,
1561 - ] );
1562 -
1563 - if ( $elementor_pages->post_count > self::MIN_PAGES_FOR_CREATE_WITH_AI_BANNER ) {
1564 - return false;
1565 - }
1566 -
1567 - if ( Utils::is_custom_kit_applied() ) {
1568 - return false;
1569 - }
1570 -
1571 - return true;
1572 - }
1573 -
1574 - private function get_create_with_ai_banner_data() {
1575 - return [
1576 - 'title' => 'Create and launch your site faster with AI',
1577 - 'description' => 'Share your vision with our AI Chat and watch as it becomes a brief, sitemap, and wireframes in minutes:',
1578 - 'input_placeholder' => 'Start describing the site you want to create...',
1579 - 'button_title' => 'Create with AI',
1580 - 'button_cta_url' => 'http://planner.elementor.com/chat.html',
1581 - 'background_image' => ELEMENTOR_ASSETS_URL . 'images/app/ai/ai-site-creator-homepage-bg.svg',
1582 - 'utm_source' => 'editor-home',
1583 - 'utm_medium' => 'wp-dash',
1584 - 'utm_campaign' => 'generate-with-ai',
1585 - ];
1586 - }
1587 -
1588 - public function add_create_with_ai_banner_to_homescreen( $home_screen_data ) {
1589 - if ( $this->should_display_create_with_ai_banner() ) {
1590 - $home_screen_data['create_with_ai'] = $this->get_create_with_ai_banner_data();
1591 - } else {
1592 - $home_screen_data['create_with_ai'] = null;
1593 - }
1594 -
1595 - return $home_screen_data;
1596 604 }
1597 605 }