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