PluginProbe
Elementor Website Builder – more than just a page builder / 3.20.0-dev3
Elementor Website Builder – more than just a page builder v3.20.0-dev3
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 -716 4.2.23.20.0-dev3 View file →
@@ -1,15 +1,13 @@
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 -use Elementor\Plugin;
6 +use Elementor\Core\Experiments\Manager as Experiments_Manager;
10 7 use Elementor\Core\Utils\Collection;
11 8 use Elementor\Modules\Ai\Connect\Ai;
9 +use Elementor\Plugin;
12 10 use Elementor\User;
13 11 use Elementor\Utils;
14 12
15 13 if ( ! defined( 'ABSPATH' ) ) {
@@ -28,10 +26,11 @@
28 26 self::HISTORY_TYPE_CODE,
29 27 self::HISTORY_TYPE_IMAGE,
30 28 self::HISTORY_TYPE_BLOCK,
31 29 ];
32 - const MIN_PAGES_FOR_CREATE_WITH_AI_BANNER = 10;
33 30
31 + const LAYOUT_EXPERIMENT = 'ai-layout';
32 +
34 33 public function get_name() {
35 34 return 'ai';
36 35 }
37 36
@@ -37,21 +36,10 @@
37 36
38 37 public function __construct() {
39 38 parent::__construct();
40 39
41 - ( new SitePlannerConnect\Module() );
40 + $this->register_layout_experiment();
42 41
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 42 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
55 43 $connect_module->register_app( 'ai', Ai::get_class_name() );
56 44 } );
57 45
@@ -58,12 +46,9 @@
58 46 add_action( 'elementor/ajax/register_actions', function( $ajax ) {
59 47 $handlers = [
60 48 'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ],
61 49 'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ],
62 - 'ai_get_remote_frontend_config' => [ $this, 'ajax_ai_get_remote_frontend_config' ],
63 50 '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 51 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
67 52 'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ],
68 53 'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ],
69 54 'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ],
@@ -71,9 +56,8 @@
71 56 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
72 57 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
73 58 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
74 59 '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 60 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
77 61 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
78 62 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
79 63 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
@@ -82,11 +66,8 @@
82 66 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
83 67 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
84 68 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
85 69 '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 70 ];
90 71
91 72 foreach ( $handlers as $tag => $callback ) {
92 73 $ajax->register_ajax_action( $tag, $callback );
@@ -94,9 +75,12 @@
94 75 } );
95 76
96 77 add_action( 'elementor/editor/before_enqueue_scripts', function() {
97 78 $this->enqueue_main_script();
98 - $this->enqueue_layout_script();
79 +
80 + if ( $this->is_layout_active() ) {
81 + $this->enqueue_layout_script();
82 + }
99 83 } );
100 84
101 85 add_action( 'elementor/editor/after_enqueue_styles', function() {
102 86 wp_enqueue_style(
@@ -107,352 +91,40 @@
107 91 );
108 92 } );
109 93
110 94 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 - );
95 + if ( $this->is_layout_active() ) {
96 + wp_enqueue_style(
97 + 'elementor-ai-layout-preview',
98 + $this->get_css_assets_url( 'modules/ai/layout-preview' ),
99 + [],
100 + ELEMENTOR_VERSION
101 + );
102 + }
117 103 } );
118 104
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();
105 + add_filter( 'elementor/document/save/data', function ( $data ) {
106 + if ( $this->is_layout_active() ) {
107 + return $this->remove_temporary_containers( $data );
129 108 }
130 - }
131 109
132 - add_action( 'enqueue_block_editor_assets', function() {
133 - wp_enqueue_script( 'elementor-ai-gutenberg',
134 - $this->get_js_assets_url( 'ai-gutenberg' ),
135 - [
136 - 'jquery',
137 - 'elementor-v2-ui',
138 - '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 - ],
149 - ELEMENTOR_VERSION, true );
150 -
151 - wp_localize_script(
152 - 'elementor-ai-gutenberg',
153 - 'ElementorAiConfig',
154 - [
155 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
156 - 'connect_url' => $this->get_ai_connect_url(),
157 - ]
158 - );
159 -
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 );
110 + return $data;
165 111 } );
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 112 }
172 113
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'],
114 + private function register_layout_experiment() {
115 + Plugin::$instance->experiments->add_feature( [
116 + 'name' => static::LAYOUT_EXPERIMENT,
117 + 'title' => esc_html__( 'Build with AI', 'elementor' ),
118 + 'description' => esc_html__( 'Tap into the potential of AI to easily create and customize containers to your specifications, right within Elementor. This feature comes packed with handy AI tools, including generation, variations, and URL references.', 'elementor' ),
119 + 'default' => Experiments_Manager::STATE_ACTIVE,
120 + 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE,
121 + 'dependencies' => [
122 + 'container',
214 123 ],
215 124 ] );
216 125 }
217 126
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 - 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 127 private function enqueue_main_script() {
456 128 wp_enqueue_script(
457 129 'elementor-ai',
458 130 $this->get_js_assets_url( 'ai' ),
@@ -476,8 +148,13 @@
476 148 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
477 149 'connect_url' => $this->get_ai_connect_url(),
478 150 ];
479 151
152 + if ( $this->get_ai_app()->is_connected() ) {
153 + // Use a cached version, don't call the API on every editor load.
154 + $config['usage'] = $this->get_ai_app()->get_cached_usage();
155 + }
156 +
480 157 wp_localize_script(
481 158 'elementor-ai',
482 159 'ElementorAiConfig',
483 160 $config
@@ -483,8 +160,31 @@
483 160 $config
484 161 );
485 162
486 163 wp_set_script_translations( 'elementor-ai', 'elementor' );
164 +
165 + if ( $this->get_ai_app()->is_connected() && ! empty( $config['is_get_started'] ) ) {
166 + $remote_config = Utils::get_cached_callback( [ $this->get_ai_app(), 'get_remote_config' ], 'ai_remote_config-' . get_current_user_id(), HOUR_IN_SECONDS );
167 +
168 + if ( ! is_wp_error( $remote_config ) && ! empty( $remote_config['config']['remoteIntegrationUrl'] ) ) {
169 + wp_enqueue_script(
170 + 'elementor-ai-integration',
171 + $remote_config['config']['remoteIntegrationUrl'],
172 + [
173 + 'elementor-ai',
174 + ],
175 + ELEMENTOR_VERSION,
176 + true
177 + );
178 + }
179 +
180 + add_filter( 'script_loader_tag', function( $tag, $handle ) {
181 + if ( 'elementor-ai-integration' === $handle ) {
182 + return str_replace( ' src', ' type="module" src', $tag );
183 + }
184 + return $tag;
185 + }, 10, 2 );
186 + }
487 187 }
488 188
489 189 private function enqueue_layout_script() {
490 190 wp_enqueue_script(
@@ -507,10 +207,14 @@
507 207
508 208 wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
509 209 }
510 210
211 + private function is_layout_active() {
212 + return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT );
213 + }
214 +
511 215 private function remove_temporary_containers( $data ) {
512 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
216 + if ( empty( $data['elements'] ) ) {
513 217 return $data;
514 218 }
515 219
516 220 // If for some reason the document has been saved during an AI Layout session,
@@ -568,25 +272,8 @@
568 272
569 273 return $app->get_remote_config();
570 274 }
571 275
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 276 private function verify_permissions( $editor_post_id ) {
590 277 $document = Plugin::$instance->documents->get( $editor_post_id );
591 278
592 279 if ( ! $document ) {
@@ -592,19 +279,15 @@
592 279 if ( ! $document ) {
593 280 throw new \Exception( 'Document not found' );
594 281 }
595 282
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' );
283 + if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
284 + throw new \Exception( 'Access denied' );
602 285 }
603 286 }
604 287
605 288 public function ajax_ai_get_image_prompt_enhancer( $data ) {
606 - $this->verify_upload_permissions( $data );
289 + $this->verify_permissions( $data['editor_post_id'] );
607 290
608 291 $app = $this->get_ai_app();
609 292
610 293 if ( empty( $data['prompt'] ) ) {
@@ -613,12 +296,13 @@
613 296
614 297 if ( ! $app->is_connected() ) {
615 298 throw new \Exception( 'not_connected' );
616 299 }
617 - $request_ids = $this->get_request_ids( $data['payload'] );
618 300
619 - $result = $app->get_image_prompt_enhanced( $data['prompt'], [], $request_ids );
620 - $this->throw_on_error( $result );
301 + $result = $app->get_image_prompt_enhanced( $data['prompt'] );
302 + if ( is_wp_error( $result ) ) {
303 + throw new \Exception( $result->get_error_message() );
304 + }
621 305
622 306 return [
623 307 'text' => $result['text'],
624 308 'response_id' => $result['responseId'],
@@ -643,36 +327,12 @@
643 327
644 328 $request_ids = $this->get_request_ids( $data['payload'] );
645 329
646 330 $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' );
331 + if ( is_wp_error( $result ) ) {
332 + throw new \Exception( $result->get_error_message() );
662 333 }
663 334
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 335 return [
676 336 'text' => $result['text'],
677 337 'response_id' => $result['responseId'],
678 338 'usage' => $result['usage'],
@@ -678,36 +338,9 @@
678 338 'usage' => $result['usage'],
679 339 ];
680 340 }
681 341
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 {
342 + private function get_ai_app() : Ai {
710 343 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
711 344 }
712 345
713 346 private function get_request_context( $data ) {
@@ -747,9 +380,11 @@
747 380
748 381 $request_ids = $this->get_request_ids( $data['payload'] );
749 382
750 383 $result = $app->get_edit_text( $data, $context, $request_ids );
751 - $this->throw_on_error( $result );
384 + if ( is_wp_error( $result ) ) {
385 + throw new \Exception( $result->get_error_message() );
386 + }
752 387
753 388 return [
754 389 'text' => $result['text'],
755 390 'response_id' => $result['responseId'],
@@ -776,9 +411,11 @@
776 411
777 412 $request_ids = $this->get_request_ids( $data['payload'] );
778 413
779 414 $result = $app->get_custom_code( $data, $context, $request_ids );
780 - $this->throw_on_error( $result );
415 + if ( is_wp_error( $result ) ) {
416 + throw new \Exception( $result->get_error_message() );
417 + }
781 418
782 419 return [
783 420 'text' => $result['text'],
784 421 'response_id' => $result['responseId'],
@@ -810,9 +447,11 @@
810 447 $context = $this->get_request_context( $data );
811 448 $request_ids = $this->get_request_ids( $data['payload'] );
812 449
813 450 $result = $app->get_custom_css( $data, $context, $request_ids );
814 - $this->throw_on_error( $result );
451 + if ( is_wp_error( $result ) ) {
452 + throw new \Exception( $result->get_error_message() );
453 + }
815 454
816 455 return [
817 456 'text' => $result['text'],
818 457 'response_id' => $result['responseId'],
@@ -846,9 +485,9 @@
846 485 return [];
847 486 }
848 487
849 488 public function ajax_ai_get_text_to_image( $data ) {
850 - $this->verify_upload_permissions( $data );
489 + $this->verify_permissions( $data['editor_post_id'] );
851 490
852 491 if ( empty( $data['payload']['prompt'] ) ) {
853 492 throw new \Exception( 'Missing prompt' );
854 493 }
@@ -863,9 +502,11 @@
863 502 $request_ids = $this->get_request_ids( $data['payload'] );
864 503
865 504 $result = $app->get_text_to_image( $data, $context, $request_ids );
866 505
867 - $this->throw_on_error( $result );
506 + if ( is_wp_error( $result ) ) {
507 + throw new \Exception( $result->get_error_message() );
508 + }
868 509
869 510 return [
870 511 'images' => $result['images'],
871 512 'response_id' => $result['responseId'],
@@ -873,12 +514,16 @@
873 514 ];
874 515 }
875 516
876 517 public function ajax_ai_get_image_to_image( $data ) {
877 - $this->verify_upload_permissions( $data );
518 + $this->verify_permissions( $data['editor_post_id'] );
878 519
879 520 $app = $this->get_ai_app();
880 521
522 + if ( empty( $data['payload']['prompt'] ) ) {
523 + throw new \Exception( 'Missing prompt' );
524 + }
525 +
881 526 if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
882 527 throw new \Exception( 'Missing Image' );
883 528 }
884 529
@@ -898,9 +543,11 @@
898 543 'promptSettings' => $data['payload']['settings'],
899 544 'attachment_id' => $data['payload']['image']['id'],
900 545 ], $context, $request_ids );
901 546
902 - $this->throw_on_error( $result );
547 + if ( is_wp_error( $result ) ) {
548 + throw new \Exception( $result->get_error_message() );
549 + }
903 550
904 551 return [
905 552 'images' => $result['images'],
906 553 'response_id' => $result['responseId'],
@@ -908,9 +555,9 @@
908 555 ];
909 556 }
910 557
911 558 public function ajax_ai_get_image_to_image_upscale( $data ) {
912 - $this->verify_upload_permissions( $data );
559 + $this->verify_permissions( $data['editor_post_id'] );
913 560
914 561 $app = $this->get_ai_app();
915 562
916 563 if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
@@ -932,9 +579,11 @@
932 579 'promptSettings' => $data['payload']['promptSettings'],
933 580 'attachment_id' => $data['payload']['image']['id'],
934 581 ], $context, $request_ids );
935 582
936 - $this->throw_on_error( $result );
583 + if ( is_wp_error( $result ) ) {
584 + throw new \Exception( $result->get_error_message() );
585 + }
937 586
938 587 return [
939 588 'images' => $result['images'],
940 589 'response_id' => $result['responseId'],
@@ -942,9 +591,9 @@
942 591 ];
943 592 }
944 593
945 594 public function ajax_ai_get_image_to_image_replace_background( $data ) {
946 - $this->verify_upload_permissions( $data );
595 + $this->verify_permissions( $data['editor_post_id'] );
947 596
948 597 $app = $this->get_ai_app();
949 598
950 599 if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
@@ -966,9 +615,11 @@
966 615 'attachment_id' => $data['payload']['image']['id'],
967 616 'prompt' => $data['payload']['prompt'],
968 617 ], $context, $request_ids );
969 618
970 - $this->throw_on_error( $result );
619 + if ( is_wp_error( $result ) ) {
620 + throw new \Exception( $result->get_error_message() );
621 + }
971 622
972 623 return [
973 624 'images' => $result['images'],
974 625 'response_id' => $result['responseId'],
@@ -976,9 +627,9 @@
976 627 ];
977 628 }
978 629
979 630 public function ajax_ai_get_image_to_image_remove_background( $data ) {
980 - $this->verify_upload_permissions( $data );
631 + $this->verify_permissions( $data['editor_post_id'] );
981 632
982 633 $app = $this->get_ai_app();
983 634
984 635 if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
@@ -994,9 +645,11 @@
994 645 $result = $app->get_image_to_image_remove_background( [
995 646 'attachment_id' => $data['payload']['image']['id'],
996 647 ], $context, $request_ids );
997 648
998 - $this->throw_on_error( $result );
649 + if ( is_wp_error( $result ) ) {
650 + throw new \Exception( $result->get_error_message() );
651 + }
999 652
1000 653 return [
1001 654 'images' => $result['images'],
1002 655 'response_id' => $result['responseId'],
@@ -1004,9 +657,9 @@
1004 657 ];
1005 658 }
1006 659
1007 660 public function ajax_ai_get_image_to_image_mask( $data ) {
1008 - $this->verify_upload_permissions( $data );
661 + $this->verify_permissions( $data['editor_post_id'] );
1009 662
1010 663 $app = $this->get_ai_app();
1011 664
1012 665 if ( empty( $data['payload']['prompt'] ) ) {
@@ -1020,42 +673,8 @@
1020 673 if ( empty( $data['payload']['settings'] ) ) {
1021 674 throw new \Exception( 'Missing prompt settings' );
1022 675 }
1023 676
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 677 if ( ! $app->is_connected() ) {
1059 678 throw new \Exception( 'not_connected' );
1060 679 }
1061 680
@@ -1065,14 +684,18 @@
1065 684
1066 685 $context = $this->get_request_context( $data );
1067 686 $request_ids = $this->get_request_ids( $data['payload'] );
1068 687
1069 - $result = $app->get_image_to_image_mask_cleanup( [
688 + $result = $app->get_image_to_image_mask( [
689 + 'prompt' => $data['payload']['prompt'],
690 + 'promptSettings' => $data['payload']['settings'],
1070 691 'attachment_id' => $data['payload']['image']['id'],
1071 692 'mask' => $data['payload']['mask'],
1072 693 ], $context, $request_ids );
1073 694
1074 - $this->throw_on_error( $result );
695 + if ( is_wp_error( $result ) ) {
696 + throw new \Exception( $result->get_error_message() );
697 + }
1075 698
1076 699 return [
1077 700 'images' => $result['images'],
1078 701 'response_id' => $result['responseId'],
@@ -1078,14 +701,17 @@
1078 701 'response_id' => $result['responseId'],
1079 702 'usage' => $result['usage'],
1080 703 ];
1081 704 }
1082 -
1083 705 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1084 - $this->verify_upload_permissions( $data );
706 + $this->verify_permissions( $data['editor_post_id'] );
1085 707
1086 708 $app = $this->get_ai_app();
1087 709
710 + if ( empty( $data['payload']['prompt'] ) ) {
711 + throw new \Exception( 'Missing prompt' );
712 + }
713 +
1088 714 if ( ! $app->is_connected() ) {
1089 715 throw new \Exception( 'not_connected' );
1090 716 }
1091 717
@@ -1095,15 +721,15 @@
1095 721
1096 722 $context = $this->get_request_context( $data );
1097 723 $request_ids = $this->get_request_ids( $data['payload'] );
1098 724 $result = $app->get_image_to_image_out_painting( [
1099 - 'size' => $data['payload']['size'],
1100 - 'position' => $data['payload']['position'],
725 + 'prompt' => $data['payload']['prompt'],
1101 726 'mask' => $data['payload']['mask'],
1102 - 'image_base64' => $data['payload']['image_base64'],
1103 727 ], $context, $request_ids );
1104 728
1105 - $this->throw_on_error( $result );
729 + if ( is_wp_error( $result ) ) {
730 + throw new \Exception( $result->get_error_message() );
731 + }
1106 732
1107 733 return [
1108 734 'images' => $result['images'],
1109 735 'response_id' => $result['responseId'],
@@ -1111,10 +737,8 @@
1111 737 ];
1112 738 }
1113 739
1114 740 public function ajax_ai_upload_image( $data ) {
1115 - $this->verify_upload_permissions( $data );
1116 -
1117 741 if ( empty( $data['image'] ) ) {
1118 742 throw new \Exception( 'Missing image data' );
1119 743 }
1120 744
@@ -1126,14 +750,14 @@
1126 750
1127 751 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1128 752
1129 753 if ( is_wp_error( $image_data ) ) {
1130 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
754 + throw new \Exception( $image_data->get_error_message() );
1131 755 }
1132 756
1133 757 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1134 - $app = $this->get_ai_app();
1135 - $app->set_used_gallery_image( $image['id'] );
758 + $app = $this->get_ai_app();
759 + $app->set_used_gallery_image( $image['id'] );
1136 760 }
1137 761
1138 762 return [
1139 763 'image' => array_merge( $image_data, $data ),
@@ -1154,9 +778,9 @@
1154 778 }
1155 779
1156 780 $result = $app->generate_layout(
1157 781 $data,
1158 - $this->prepare_generate_layout_context( $data )
782 + $this->prepare_generate_layout_context()
1159 783 );
1160 784
1161 785 if ( is_wp_error( $result ) ) {
1162 786 $message = $result->get_error_message();
@@ -1162,12 +786,11 @@
1162 786 $message = $result->get_error_message();
1163 787
1164 788 if ( is_array( $message ) ) {
1165 789 $message = implode( ', ', $message );
1166 - throw new \Exception( esc_html( $message ) );
1167 790 }
1168 791
1169 - $this->throw_on_error( $result );
792 + throw new \Exception( $message );
1170 793 }
1171 794
1172 795 $elements = $result['text']['elements'] ?? [];
1173 796 $base_template_id = $result['baseTemplateId'] ?? null;
@@ -1227,12 +850,14 @@
1227 850
1228 851 $result = $app->get_layout_prompt_enhanced(
1229 852 $data['prompt'],
1230 853 $data['enhance_type'],
1231 - $this->prepare_generate_layout_context( $data )
854 + $this->prepare_generate_layout_context()
1232 855 );
1233 856
1234 - $this->throw_on_error( $result );
857 + if ( is_wp_error( $result ) ) {
858 + throw new \Exception( $result->get_error_message() );
859 + }
1235 860
1236 861 return [
1237 862 'text' => $result['text'] ?? $data['prompt'],
1238 863 'response_id' => $result['responseId'] ?? '',
@@ -1239,14 +864,13 @@
1239 864 'usage' => $result['usage'] ?? '',
1240 865 ];
1241 866 }
1242 867
1243 - private function prepare_generate_layout_context( $data ) {
1244 - $request_context = $this->get_request_context( $data );
868 + private function prepare_generate_layout_context() {
1245 869 $kit = Plugin::$instance->kits_manager->get_active_kit();
1246 870
1247 871 if ( ! $kit ) {
1248 - return $request_context;
872 + return [];
1249 873 }
1250 874
1251 875 $kits_data = Collection::make( $kit->get_data()['settings'] ?? [] );
1252 876
@@ -1299,14 +923,14 @@
1299 923 ],
1300 924 ];
1301 925 } );
1302 926
1303 - $request_context['globals'] = [
1304 - 'colors' => $colors->all(),
1305 - 'typography' => $typography->all(),
927 + return [
928 + 'globals' => [
929 + 'colors' => $colors->all(),
930 + 'typography' => $typography->all(),
931 + ],
1306 932 ];
1307 -
1308 - return $request_context;
1309 933 }
1310 934
1311 935 private function upload_image( $image_url, $image_title, $parent_post_id = 0 ) {
1312 936 if ( ! current_user_can( 'upload_files' ) ) {
@@ -1312,24 +936,10 @@
1312 936 if ( ! current_user_can( 'upload_files' ) ) {
1313 937 throw new \Exception( 'Not Allowed to Upload images' );
1314 938 }
1315 939
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 940 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1327 941
1328 - if ( is_wp_error( $attachment_id ) ) {
1329 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1330 - }
1331 -
1332 942 if ( ! empty( $attachment_id['error'] ) ) {
1333 943 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1334 944 }
1335 945
@@ -1334,10 +944,10 @@
1334 944 }
1335 945
1336 946 return [
1337 947 'id' => $attachment_id,
1338 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1339 - 'alt' => esc_attr( $image_title ),
948 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
949 + 'alt' => $image_title,
1340 950 'source' => 'library',
1341 951 ];
1342 952 }
1343 953
@@ -1361,9 +971,9 @@
1361 971
1362 972 $result = $app->get_history_by_type( $type, $page, $limit, $context );
1363 973
1364 974 if ( is_wp_error( $result ) ) {
1365 - throw new \Exception( esc_html( $result->get_error_message() ) );
975 + throw new \Exception( $result->get_error_message() );
1366 976 }
1367 977
1368 978 return $result;
1369 979 }
@@ -1383,9 +993,9 @@
1383 993
1384 994 $result = $app->delete_history_item( $data['id'], $context );
1385 995
1386 996 if ( is_wp_error( $result ) ) {
1387 - throw new \Exception( esc_html( $result->get_error_message() ) );
997 + throw new \Exception( $result->get_error_message() );
1388 998 }
1389 999
1390 1000 return [];
1391 1001 }
@@ -1405,193 +1015,10 @@
1405 1015
1406 1016 $result = $app->toggle_favorite_history_item( $data['id'], $context );
1407 1017
1408 1018 if ( is_wp_error( $result ) ) {
1409 - throw new \Exception( esc_html( $result->get_error_message() ) );
1019 + throw new \Exception( $result->get_error_message() );
1410 1020 }
1411 1021
1412 1022 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 1023 }
1597 1024 }