PluginProbe
Elementor Website Builder – more than just a page builder / 3.25.4
Elementor Website Builder – more than just a page builder v3.25.4
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 +27 -489 4.1.0-dev33.25.4 View file →
@@ -1,12 +1,9 @@
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;
9 6 use Elementor\Plugin;
10 7 use Elementor\Core\Utils\Collection;
11 8 use Elementor\Modules\Ai\Connect\Ai;
12 9 use Elementor\User;
@@ -28,9 +25,8 @@
28 25 self::HISTORY_TYPE_CODE,
29 26 self::HISTORY_TYPE_IMAGE,
30 27 self::HISTORY_TYPE_BLOCK,
31 28 ];
32 - const MIN_PAGES_FOR_CREATE_WITH_AI_BANNER = 10;
33 29
34 30 public function get_name() {
35 31 return 'ai';
36 32 }
@@ -37,20 +33,19 @@
37 33
38 34 public function __construct() {
39 35 parent::__construct();
40 36
41 - ( new SitePlannerConnect\Module() );
42 -
43 37 if ( is_admin() ) {
44 38 ( new Preferences() )->register();
45 - add_action( 'elementor/import-export/import-kit/runner/after-run', [ $this, 'handle_kit_install' ] );
46 39 }
47 40
48 - if ( ! $this->is_ai_enabled() ) {
41 + if ( ! Plugin::$instance->experiments->is_feature_active( 'container' ) ) {
49 42 return;
50 43 }
51 44
52 - add_filter( 'elementor/core/admin/homescreen', [ $this, 'add_create_with_ai_banner_to_homescreen' ] );
45 + if ( ! Preferences::is_ai_enabled( get_current_user_id() ) ) {
46 + return;
47 + }
53 48
54 49 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
55 50 $connect_module->register_app( 'ai', Ai::get_class_name() );
56 51 } );
@@ -82,11 +77,8 @@
82 77 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
83 78 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
84 79 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
85 80 '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 81 ];
90 82
91 83 foreach ( $handlers as $tag => $callback ) {
92 84 $ajax->register_ajax_action( $tag, $callback );
@@ -117,17 +109,8 @@
117 109 } );
118 110
119 111 if ( is_admin() ) {
120 112 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 113 }
131 114
132 115 add_action( 'enqueue_block_editor_assets', function() {
133 116 wp_enqueue_script( 'elementor-ai-gutenberg',
@@ -162,272 +145,10 @@
162 145
163 146 add_filter( 'elementor/document/save/data', function ( $data ) {
164 147 return $this->remove_temporary_containers( $data );
165 148 } );
166 -
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 149 }
172 150
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 - ]
252 - );
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 151 public function enqueue_ai_media_library() {
431 152 wp_enqueue_script( 'elementor-ai-media-library',
432 153 $this->get_js_assets_url( 'ai-media-library' ),
433 154 [
@@ -476,8 +197,13 @@
476 197 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
477 198 'connect_url' => $this->get_ai_connect_url(),
478 199 ];
479 200
201 + if ( $this->get_ai_app()->is_connected() ) {
202 + // Use a cached version, don't call the API on every editor load.
203 + $config['usage'] = $this->get_ai_app()->get_cached_usage();
204 + }
205 +
480 206 wp_localize_script(
481 207 'elementor-ai',
482 208 'ElementorAiConfig',
483 209 $config
@@ -508,9 +234,9 @@
508 234 wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
509 235 }
510 236
511 237 private function remove_temporary_containers( $data ) {
512 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
238 + if ( empty( $data['elements'] ) ) {
513 239 return $data;
514 240 }
515 241
516 242 // If for some reason the document has been saved during an AI Layout session,
@@ -584,9 +310,8 @@
584 310 return;
585 311 }
586 312 $this->verify_permissions( $data['editor_post_id'] );
587 313 }
588 -
589 314 private function verify_permissions( $editor_post_id ) {
590 315 $document = Plugin::$instance->documents->get( $editor_post_id );
591 316
592 317 if ( ! $document ) {
@@ -592,14 +317,10 @@
592 317 if ( ! $document ) {
593 318 throw new \Exception( 'Document not found' );
594 319 }
595 320
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' );
321 + if ( ! $document->is_editable_by_current_user() ) {
322 + throw new \Exception( 'Access denied' );
602 323 }
603 324 }
604 325
605 326 public function ajax_ai_get_image_prompt_enhancer( $data ) {
@@ -705,9 +426,9 @@
705 426 'usage' => $result['usage'],
706 427 ];
707 428 }
708 429
709 - private function get_ai_app(): Ai {
430 + private function get_ai_app() : Ai {
710 431 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
711 432 }
712 433
713 434 private function get_request_context( $data ) {
@@ -1020,8 +741,12 @@
1020 741 if ( empty( $data['payload']['settings'] ) ) {
1021 742 throw new \Exception( 'Missing prompt settings' );
1022 743 }
1023 744
745 + if ( ! $app->is_connected() ) {
746 + throw new \Exception( 'not_connected' );
747 + }
748 +
1024 749 if ( empty( $data['payload']['mask'] ) ) {
1025 750 throw new \Exception( 'Missing Mask' );
1026 751 }
1027 752
@@ -1111,10 +836,8 @@
1111 836 ];
1112 837 }
1113 838
1114 839 public function ajax_ai_upload_image( $data ) {
1115 - $this->verify_upload_permissions( $data );
1116 -
1117 840 if ( empty( $data['image'] ) ) {
1118 841 throw new \Exception( 'Missing image data' );
1119 842 }
1120 843
@@ -1126,14 +849,14 @@
1126 849
1127 850 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1128 851
1129 852 if ( is_wp_error( $image_data ) ) {
1130 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
853 + throw new \Exception( $image_data->get_error_message() );
1131 854 }
1132 855
1133 856 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1134 - $app = $this->get_ai_app();
1135 - $app->set_used_gallery_image( $image['id'] );
857 + $app = $this->get_ai_app();
858 + $app->set_used_gallery_image( $image['id'] );
1136 859 }
1137 860
1138 861 return [
1139 862 'image' => array_merge( $image_data, $data ),
@@ -1162,9 +885,9 @@
1162 885 $message = $result->get_error_message();
1163 886
1164 887 if ( is_array( $message ) ) {
1165 888 $message = implode( ', ', $message );
1166 - throw new \Exception( esc_html( $message ) );
889 + throw new \Exception( $message );
1167 890 }
1168 891
1169 892 $this->throw_on_error( $result );
1170 893 }
@@ -1312,24 +1035,10 @@
1312 1035 if ( ! current_user_can( 'upload_files' ) ) {
1313 1036 throw new \Exception( 'Not Allowed to Upload images' );
1314 1037 }
1315 1038
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 1039 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1327 1040
1328 - if ( is_wp_error( $attachment_id ) ) {
1329 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1330 - }
1331 -
1332 1041 if ( ! empty( $attachment_id['error'] ) ) {
1333 1042 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1334 1043 }
1335 1044
@@ -1334,10 +1043,10 @@
1334 1043 }
1335 1044
1336 1045 return [
1337 1046 'id' => $attachment_id,
1338 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1339 - 'alt' => esc_attr( $image_title ),
1047 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
1048 + 'alt' => $image_title,
1340 1049 'source' => 'library',
1341 1050 ];
1342 1051 }
1343 1052
@@ -1361,9 +1070,9 @@
1361 1070
1362 1071 $result = $app->get_history_by_type( $type, $page, $limit, $context );
1363 1072
1364 1073 if ( is_wp_error( $result ) ) {
1365 - throw new \Exception( esc_html( $result->get_error_message() ) );
1074 + throw new \Exception( $result->get_error_message() );
1366 1075 }
1367 1076
1368 1077 return $result;
1369 1078 }
@@ -1383,9 +1092,9 @@
1383 1092
1384 1093 $result = $app->delete_history_item( $data['id'], $context );
1385 1094
1386 1095 if ( is_wp_error( $result ) ) {
1387 - throw new \Exception( esc_html( $result->get_error_message() ) );
1096 + throw new \Exception( $result->get_error_message() );
1388 1097 }
1389 1098
1390 1099 return [];
1391 1100 }
@@ -1405,83 +1114,14 @@
1405 1114
1406 1115 $result = $app->toggle_favorite_history_item( $data['id'], $context );
1407 1116
1408 1117 if ( is_wp_error( $result ) ) {
1409 - throw new \Exception( esc_html( $result->get_error_message() ) );
1118 + throw new \Exception( $result->get_error_message() );
1410 1119 }
1411 1120
1412 1121 return [];
1413 1122 }
1414 1123
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 1124 /**
1485 1125 * @param mixed $result
1486 1126 */
1487 1127 private function throw_on_error( $result ): void {
@@ -1486,112 +1126,10 @@
1486 1126 */
1487 1127 private function throw_on_error( $result ): void {
1488 1128 if ( is_wp_error( $result ) ) {
1489 1129 wp_send_json_error( [
1490 - 'message' => esc_html( $result->get_error_message() ),
1130 + 'message' => $result->get_error_message(),
1491 1131 'extra_data' => $result->get_error_data(),
1492 1132 ] );
1493 1133 }
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 1134 }
1597 1135 }