PluginProbe
Elementor Website Builder – more than just a page builder / 3.22.2
Elementor Website Builder – more than just a page builder v3.22.2
4.3.4 4.3.3 4.3.2 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 All 457 releases
← All changes | modules/ai/module.php +105 -543 4.3.4 → 3.22.2 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,8 +26,11 @@
27 26 self::HISTORY_TYPE_CODE,
28 27 self::HISTORY_TYPE_IMAGE,
29 28 self::HISTORY_TYPE_BLOCK,
30 29 ];
30 +
31 + const LAYOUT_EXPERIMENT = 'ai-layout';
32 +
31 33 public function get_name() {
32 34 return 'ai';
33 35 }
34 36
@@ -34,19 +36,10 @@
34 36
35 37 public function __construct() {
36 38 parent::__construct();
37 39
38 - ( new SitePlannerConnect\Module() );
40 + $this->register_layout_experiment();
39 41
40 - if ( is_admin() ) {
41 - ( new Preferences() )->register();
42 - add_action( 'elementor/import-export/import-kit/runner/after-run', [ $this, 'handle_kit_install' ] );
43 - }
44 -
45 - if ( ! $this->is_ai_enabled() ) {
46 - return;
47 - }
48 -
49 42 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
50 43 $connect_module->register_app( 'ai', Ai::get_class_name() );
51 44 } );
52 45
@@ -53,12 +46,10 @@
53 46 add_action( 'elementor/ajax/register_actions', function( $ajax ) {
54 47 $handlers = [
55 48 'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ],
56 49 'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ],
57 - 'ai_get_remote_frontend_config' => [ $this, 'ajax_ai_get_remote_frontend_config' ],
58 50 'ai_get_completion_text' => [ $this, 'ajax_ai_get_completion_text' ],
59 51 'ai_get_excerpt' => [ $this, 'ajax_ai_get_excerpt' ],
60 - 'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ],
61 52 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
62 53 'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ],
63 54 'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ],
64 55 'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ],
@@ -66,9 +57,8 @@
66 57 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
67 58 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
68 59 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
69 60 'ai_get_image_to_image_mask' => [ $this, 'ajax_ai_get_image_to_image_mask' ],
70 - 'ai_get_image_to_image_mask_cleanup' => [ $this, 'ajax_ai_get_image_to_image_mask_cleanup' ],
71 61 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
72 62 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
73 63 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
74 64 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
@@ -77,11 +67,8 @@
77 67 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
78 68 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
79 69 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
80 70 'ai_toggle_favorite_history_item' => [ $this, 'ajax_ai_toggle_favorite_history_item' ],
81 - 'ai_get_product_image_unification' => [ $this, 'ajax_ai_get_product_image_unification' ],
82 - 'ai_get_animation' => [ $this, 'ajax_ai_get_animation' ],
83 - 'ai_get_image_to_image_isolate_objects' => [ $this, 'ajax_ai_get_product_image_unification' ],
84 71 ];
85 72
86 73 foreach ( $handlers as $tag => $callback ) {
87 74 $ajax->register_ajax_action( $tag, $callback );
@@ -89,9 +76,12 @@
89 76 } );
90 77
91 78 add_action( 'elementor/editor/before_enqueue_scripts', function() {
92 79 $this->enqueue_main_script();
93 - $this->enqueue_layout_script();
80 +
81 + if ( $this->is_layout_active() ) {
82 + $this->enqueue_layout_script();
83 + }
94 84 } );
95 85
96 86 add_action( 'elementor/editor/after_enqueue_styles', function() {
97 87 wp_enqueue_style(
@@ -102,26 +92,20 @@
102 92 );
103 93 } );
104 94
105 95 add_action( 'elementor/preview/enqueue_styles', function() {
106 - wp_enqueue_style(
107 - 'elementor-ai-layout-preview',
108 - $this->get_css_assets_url( 'modules/ai/layout-preview' ),
109 - [],
110 - ELEMENTOR_VERSION
111 - );
96 + if ( $this->is_layout_active() ) {
97 + wp_enqueue_style(
98 + 'elementor-ai-layout-preview',
99 + $this->get_css_assets_url( 'modules/ai/layout-preview' ),
100 + [],
101 + ELEMENTOR_VERSION
102 + );
103 + }
112 104 } );
113 105
114 106 if ( is_admin() ) {
115 107 add_action( 'wp_enqueue_media', [ $this, 'enqueue_ai_media_library' ] );
116 - add_action( 'admin_head', [ $this, 'enqueue_ai_media_library_upload_screen' ] );
117 -
118 - if ( current_user_can( 'edit_products' ) || current_user_can( 'publish_products' ) ) {
119 - add_action( 'admin_init', [ $this, 'enqueue_ai_products_page_scripts' ] );
120 - add_action( 'current_screen', [ $this, 'enqueue_ai_single_product_page_scripts' ] );
121 - add_action( 'wp_ajax_elementor-ai-get-product-images', [ $this, 'get_product_images_ajax' ] );
122 - add_action( 'wp_ajax_elementor-ai-set-product-images', [ $this, 'set_product_images_ajax' ] );
123 - }
124 108 }
125 109
126 110 add_action( 'enqueue_block_editor_assets', function() {
127 111 wp_enqueue_script( 'elementor-ai-gutenberg',
@@ -129,27 +113,31 @@
129 113 [
130 114 'jquery',
131 115 'elementor-v2-ui',
132 116 'elementor-v2-icons',
133 - 'wp-blocks',
134 - 'wp-element',
135 - 'wp-editor',
136 - 'wp-data',
137 - 'wp-components',
138 - 'wp-compose',
139 - 'wp-i18n',
140 - 'wp-hooks',
141 - 'elementor-ai-media-library',
142 117 ],
143 118 ELEMENTOR_VERSION, true );
144 119
120 + $session_id = 'elementor-editor-session-' . Utils::generate_random_string();
121 +
122 + $config = [
123 + 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
124 + 'connect_url' => $this->get_ai_connect_url(),
125 + 'client_session_id' => $session_id,
126 + ];
127 +
128 + if ( $this->get_ai_app()->is_connected() ) {
129 + $usage = $this->get_ai_app()->get_usage( 'gutenberg-loader', $session_id );
130 +
131 + if ( ! is_wp_error( $usage ) ) {
132 + $config['usage'] = $usage;
133 + }
134 + }
135 +
145 136 wp_localize_script(
146 137 'elementor-ai-gutenberg',
147 138 'ElementorAiConfig',
148 - [
149 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
150 - 'connect_url' => $this->get_ai_connect_url(),
151 - ]
139 + $config
152 140 );
153 141
154 142 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
155 143 });
@@ -154,274 +142,29 @@
154 142 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
155 143 });
156 144
157 145 add_filter( 'elementor/document/save/data', function ( $data ) {
158 - return $this->remove_temporary_containers( $data );
159 - } );
160 -
161 - add_action( 'elementor/element/common/section_effects/after_section_start', [ $this, 'register_ai_motion_effect_control' ], 10, 1 );
162 - add_action( 'elementor/element/container/section_effects/after_section_start', [ $this, 'register_ai_motion_effect_control' ], 10, 1 );
163 - add_action( 'elementor/element/common/_section_transform/after_section_end', [ $this, 'register_ai_hover_effect_control' ], 10, 1 );
164 - add_action( 'elementor/element/container/_section_transform/after_section_end', [ $this, 'register_ai_hover_effect_control' ], 10, 1 );
165 - }
166 -
167 - public function is_ai_enabled() {
168 - if ( ! Plugin::$instance->experiments->is_feature_active( 'container' ) ) {
169 - return false;
170 - }
171 -
172 - return Preferences::is_ai_enabled( get_current_user_id() );
173 - }
174 -
175 - public function handle_kit_install( $imported_data ) {
176 - if ( ! $this->is_ai_enabled() ) {
177 - return;
178 - }
179 -
180 - if ( ! isset( $imported_data['status'] ) || 'success' !== $imported_data['status'] ) {
181 - return;
182 - }
183 -
184 - if ( ! isset( $imported_data['runner'] ) || 'site-settings' !== $imported_data['runner'] ) {
185 - return;
186 - }
187 -
188 - if ( ! isset( $imported_data['configData']['lastImportedSession']['instance_data']['site_settings']['settings']['ai'] ) ) {
189 - return;
190 - }
191 -
192 - $is_connected = $this->get_ai_app()->is_connected() && User::get_introduction_meta( 'ai_get_started' );
193 -
194 - if ( ! $is_connected ) {
195 - return;
196 - }
197 -
198 - $last_imported_session = $imported_data['configData']['lastImportedSession'];
199 - $imported_ai_data = $last_imported_session['instance_data']['site_settings']['settings']['ai'];
200 -
201 - $this->get_ai_app()->send_event( [
202 - 'name' => 'kit_installed',
203 - 'data' => $imported_ai_data,
204 - 'client' => [
205 - 'name' => 'elementor',
206 - 'version' => ELEMENTOR_VERSION,
207 - 'session_id' => $last_imported_session['session_id'],
208 - ],
209 - ] );
210 - }
211 -
212 - public function register_ai_hover_effect_control( Element_Base $element ) {
213 - if ( ! $element->get_controls( 'ai_hover_animation' ) ) {
214 - $element->add_control(
215 - 'ai_hover_animation',
216 - [
217 - 'tabs_wrapper' => '_tabs_positioning',
218 - 'inner_tab' => '_tab_positioning_hover',
219 - 'label' => esc_html__( 'Animate With AI', 'elementor' ),
220 - 'type' => Controls_Manager::RAW_HTML,
221 - 'raw' => '
222 -<style>
223 - .elementor-control-ai_hover_animation .elementor-control-content {
224 - display: flex;
225 - flex-direction: row;
226 - justify-content: space-between;
227 - align-items: center;
228 - }
229 - .elementor-control-ai_hover_animation .elementor-control-raw-html {
230 - display: none;
231 - }
232 -</style>',
233 - 'render_type' => 'none',
234 - 'ai' => [
235 - 'active' => true,
236 - 'type' => 'hover_animation',
237 - ],
238 - ],
239 - [
240 - 'position' => [
241 - 'of' => '_transform_rotate_popover_hover',
242 - 'type' => 'control',
243 - 'at' => 'before',
244 - ],
245 - ]
246 - );
247 - }
248 - }
249 - public function register_ai_motion_effect_control( $element ) {
250 - if ( Utils::has_pro() && ! $element->get_controls( 'ai_animation' ) ) {
251 - $element->add_control(
252 - 'ai_animation',
253 - [
254 - 'label' => esc_html__( 'Animate With AI', 'elementor' ),
255 - 'type' => Controls_Manager::RAW_HTML,
256 - 'raw' => '
257 - <style>
258 - .elementor-control-ai_animation .elementor-control-content {
259 - display: flex;
260 - flex-direction: row;
261 - justify-content: space-between;
262 - align-items: center;
263 - }
264 - .elementor-control-ai_animation .elementor-control-raw-html {
265 - display: none;
266 - }
267 - </style>',
268 - 'render_type' => 'none',
269 - 'ai' => [
270 - 'active' => true,
271 - 'type' => 'animation',
272 - ],
273 - ]
274 - );
275 - }
276 - }
277 -
278 - private function get_current_screen() {
279 - $is_wc = class_exists( 'WooCommerce' ) && post_type_exists( 'product' );
280 - if ( ! $is_wc ) {
281 - return 'other';
282 - }
283 -
284 - $is_products_page = isset( $_GET['post_type'] ) && 'product' === $_GET['post_type'];
285 - if ( $is_products_page ) {
286 - return 'wc-products';
287 - }
288 -
289 - $screen = get_current_screen();
290 - $is_single_product_page = isset( $screen->post_type ) && ( 'product' === $screen->post_type && 'post' === $screen->base );
291 - if ( $is_single_product_page ) {
292 - return 'wc-single-product';
293 - }
294 -
295 - return 'other';
296 - }
297 -
298 - public function enqueue_ai_products_page_scripts() {
299 - if ( 'wc-products' !== $this->get_current_screen() ) {
300 - return;
301 - }
302 -
303 - $this->add_wc_scripts();
304 - }
305 -
306 - public function enqueue_ai_single_product_page_scripts() {
307 - if ( 'wc-single-product' !== $this->get_current_screen() ) {
308 - return;
309 - }
310 -
311 - $this->add_wc_scripts();
312 - }
313 -
314 - private function add_products_bulk_action( $bulk_actions ) {
315 - $bulk_actions['elementor-ai-unify-product-images'] = __( 'Unify with Elementor AI', 'elementor' );
316 - return $bulk_actions;
317 - }
318 -
319 - public function get_product_images_ajax() {
320 - check_ajax_referer( 'elementor-ai-unify-product-images_nonce', 'nonce' );
321 -
322 - $post_ids = isset( $_POST['post_ids'] ) ? array_map( 'intval', $_POST['post_ids'] ) : [];
323 - $is_galley_only = isset( $_POST['is_galley_only'] ) && sanitize_text_field( wp_unslash( $_POST['is_galley_only'] ) );
324 -
325 - $image_ids = [];
326 -
327 - foreach ( $post_ids as $post_id ) {
328 - if ( $is_galley_only ) {
329 - $product = wc_get_product( $post_id );
330 - $gallery_image_ids = $product->get_gallery_image_ids();
331 - foreach ( $gallery_image_ids as $image_id ) {
332 - $image_ids[] = [
333 - 'productId' => $post_id,
334 - 'id' => $image_id,
335 - 'image_url' => wp_get_attachment_url( $image_id ),
336 - ];
337 - }
338 - continue;
146 + if ( $this->is_layout_active() ) {
147 + return $this->remove_temporary_containers( $data );
339 148 }
340 149
341 - $image_id = get_post_thumbnail_id( $post_id );
342 -
343 - if ( ! $image_id ) {
344 - $product = wc_get_product( $post_id );
345 - $gallery_image_ids = $product->get_gallery_image_ids();
346 - if ( ! empty( $gallery_image_ids ) ) {
347 - $image_id = $gallery_image_ids[0];
348 - }
349 - }
350 -
351 - $image_ids[] = [
352 - 'productId' => $post_id,
353 - 'id' => $image_id ? $image_id : 'No Image',
354 - 'image_url' => $image_id ? wp_get_attachment_url( $image_id ) : 'No Image',
355 - ];
356 - }
357 -
358 - wp_send_json_success( [ 'product_images' => array_slice( $image_ids, 0, 10 ) ] );
359 -
360 - wp_die();
150 + return $data;
151 + } );
361 152 }
362 153
363 - private function get_attachment_id_by_url( $url ) {
364 - $attachments = get_posts( [
365 - 'post_type' => 'attachment',
366 - 'meta_query' => [
367 - [
368 - 'key' => '_wp_attached_file',
369 - 'value' => basename( $url ),
370 - 'compare' => 'LIKE',
371 - ],
154 + private function register_layout_experiment() {
155 + Plugin::$instance->experiments->add_feature( [
156 + 'name' => static::LAYOUT_EXPERIMENT,
157 + 'title' => esc_html__( 'Build with AI', 'elementor' ),
158 + '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' ),
159 + 'default' => Experiments_Manager::STATE_ACTIVE,
160 + 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE,
161 + 'dependencies' => [
162 + 'container',
372 163 ],
373 - 'fields' => 'ids',
374 - 'numberposts' => 1,
375 164 ] );
376 -
377 - return ! empty( $attachments ) ? $attachments[0] : null;
378 165 }
379 166
380 - public function set_product_images_ajax() {
381 - check_ajax_referer( 'elementor-ai-unify-product-images_nonce', 'nonce' );
382 -
383 - $product_id = isset( $_POST['productId'] ) ? sanitize_text_field( wp_unslash( $_POST['productId'] ) ) : '';
384 - $image_url = isset( $_POST['image_url'] ) ? sanitize_text_field( wp_unslash( $_POST['image_url'] ) ) : '';
385 - $image_to_add = isset( $_POST['image_to_add'] ) ? intval( wp_unslash( $_POST['image_to_add'] ) ) : null;
386 - $image_to_remove = isset( $_POST['image_to_remove'] ) ? intval( wp_unslash( $_POST['image_to_remove'] ) ) : null;
387 - $is_product_gallery = isset( $_POST['is_product_gallery'] ) && sanitize_text_field( wp_unslash( $_POST['is_product_gallery'] ) ) === 'true';
388 -
389 - if ( ! $product_id || ! $image_url ) {
390 - throw new \Exception( 'Product ID and Image URL are required' );
391 - }
392 -
393 - $product = wc_get_product( $product_id );
394 - if ( ! $product ) {
395 - throw new \Exception( 'Product not found' );
396 - }
397 -
398 - $attachment_id = $this->get_attachment_id_by_url( $image_url );
399 - if ( is_wp_error( $attachment_id ) ) {
400 - throw new \Exception( 'Image upload failed' );
401 - }
402 -
403 - if ( $is_product_gallery ) {
404 - $this->update_product_gallery( $product, $image_to_remove, $image_to_add );
405 - } else {
406 - $product->set_image_id( $attachment_id );
407 - $product->save();
408 - }
409 -
410 - wp_send_json_success( [
411 - 'message' => __( 'Image added successfully', 'elementor' ),
412 - ] );
413 - }
414 -
415 - public function enqueue_ai_media_library_upload_screen() {
416 - $screen = get_current_screen();
417 - if ( ! $screen || 'upload' !== $screen->id ) {
418 - return;
419 - }
420 -
421 - $this->enqueue_ai_media_library();
422 - }
423 -
424 167 public function enqueue_ai_media_library() {
425 168 wp_enqueue_script( 'elementor-ai-media-library',
426 169 $this->get_js_assets_url( 'ai-media-library' ),
427 170 [
@@ -433,15 +176,20 @@
433 176 ELEMENTOR_VERSION,
434 177 true
435 178 );
436 179
180 + $session_id = 'wp-media-library-session-' . Utils::generate_random_string();
181 +
182 + $config = [
183 + 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
184 + 'connect_url' => $this->get_ai_connect_url(),
185 + 'client_session_id' => $session_id,
186 + ];
187 +
437 188 wp_localize_script(
438 189 'elementor-ai-media-library',
439 190 'ElementorAiConfig',
440 - [
441 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
442 - 'connect_url' => $this->get_ai_connect_url(),
443 - ]
191 + $config
444 192 );
445 193
446 194 wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
447 195 }
@@ -470,8 +218,13 @@
470 218 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
471 219 'connect_url' => $this->get_ai_connect_url(),
472 220 ];
473 221
222 + if ( $this->get_ai_app()->is_connected() ) {
223 + // Use a cached version, don't call the API on every editor load.
224 + $config['usage'] = $this->get_ai_app()->get_cached_usage();
225 + }
226 +
474 227 wp_localize_script(
475 228 'elementor-ai',
476 229 'ElementorAiConfig',
477 230 $config
@@ -477,8 +230,31 @@
477 230 $config
478 231 );
479 232
480 233 wp_set_script_translations( 'elementor-ai', 'elementor' );
234 +
235 + if ( $this->get_ai_app()->is_connected() && ! empty( $config['is_get_started'] ) ) {
236 + $remote_config = Utils::get_cached_callback( [ $this->get_ai_app(), 'get_remote_config' ], 'ai_remote_config-' . get_current_user_id(), HOUR_IN_SECONDS );
237 +
238 + if ( ! is_wp_error( $remote_config ) && ! empty( $remote_config['config']['remoteIntegrationUrl'] ) ) {
239 + wp_enqueue_script(
240 + 'elementor-ai-integration',
241 + $remote_config['config']['remoteIntegrationUrl'],
242 + [
243 + 'elementor-ai',
244 + ],
245 + ELEMENTOR_VERSION,
246 + true
247 + );
248 + }
249 +
250 + add_filter( 'script_loader_tag', function( $tag, $handle ) {
251 + if ( 'elementor-ai-integration' === $handle ) {
252 + return str_replace( ' src', ' type="module" src', $tag );
253 + }
254 + return $tag;
255 + }, 10, 2 );
256 + }
481 257 }
482 258
483 259 private function enqueue_layout_script() {
484 260 wp_enqueue_script(
@@ -501,10 +277,14 @@
501 277
502 278 wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
503 279 }
504 280
281 + private function is_layout_active() {
282 + return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT );
283 + }
284 +
505 285 private function remove_temporary_containers( $data ) {
506 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
286 + if ( empty( $data['elements'] ) ) {
507 287 return $data;
508 288 }
509 289
510 290 // If for some reason the document has been saved during an AI Layout session,
@@ -561,17 +341,8 @@
561 341 }
562 342
563 343 return $app->get_remote_config();
564 344 }
565 -
566 - public function ajax_ai_get_remote_frontend_config( $data ) {
567 - $callback = function () use ( $data ) {
568 - return $this->get_ai_app()->get_remote_frontend_config( $data );
569 - };
570 -
571 - return Utils::get_cached_callback( $callback, 'ai_remote_frontend_config-' . get_current_user_id(), HOUR_IN_SECONDS );
572 - }
573 -
574 345 public function verify_upload_permissions( $data ) {
575 346 $referer = wp_get_referer();
576 347
577 348 if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) {
@@ -586,14 +357,10 @@
586 357 if ( ! $document ) {
587 358 throw new \Exception( 'Document not found' );
588 359 }
589 360
590 - if ( $document->is_built_with_elementor() ) {
591 - if ( ! $document->is_editable_by_current_user() ) {
592 - throw new \Exception( 'Access denied' );
593 - }
594 - } elseif ( ! current_user_can( 'edit_post', $editor_post_id ) ) {
595 - throw new \Exception( 'Access denied' );
361 + if ( ! $document->is_editable_by_current_user() ) {
362 + throw new \Exception( 'Access denied' );
596 363 }
597 364 }
598 365
599 366 public function ajax_ai_get_image_prompt_enhancer( $data ) {
@@ -672,36 +439,9 @@
672 439 'usage' => $result['usage'],
673 440 ];
674 441 }
675 442
676 - public function ajax_ai_get_featured_image( $data ): array {
677 - $this->verify_upload_permissions( $data );
678 -
679 - if ( empty( $data['payload']['prompt'] ) ) {
680 - throw new \Exception( 'Missing prompt' );
681 - }
682 -
683 - $app = $this->get_ai_app();
684 -
685 - if ( ! $app->is_connected() ) {
686 - throw new \Exception( 'not_connected' );
687 - }
688 -
689 - $context = $this->get_request_context( $data );
690 - $request_ids = $this->get_request_ids( $data['payload'] );
691 -
692 - $result = $app->get_featured_image( $data, $context, $request_ids );
693 -
694 - $this->throw_on_error( $result );
695 -
696 - return [
697 - 'images' => $result['images'],
698 - 'response_id' => $result['responseId'],
699 - 'usage' => $result['usage'],
700 - ];
701 - }
702 -
703 - private function get_ai_app(): Ai {
443 + private function get_ai_app() : Ai {
704 444 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
705 445 }
706 446
707 447 private function get_request_context( $data ) {
@@ -1014,42 +754,8 @@
1014 754 if ( empty( $data['payload']['settings'] ) ) {
1015 755 throw new \Exception( 'Missing prompt settings' );
1016 756 }
1017 757
1018 - if ( empty( $data['payload']['mask'] ) ) {
1019 - throw new \Exception( 'Missing Mask' );
1020 - }
1021 -
1022 - $context = $this->get_request_context( $data );
1023 - $request_ids = $this->get_request_ids( $data['payload'] );
1024 -
1025 - $result = $app->get_image_to_image_mask( [
1026 - 'prompt' => $data['payload']['prompt'],
1027 - 'attachment_id' => $data['payload']['image']['id'],
1028 - 'mask' => $data['payload']['mask'],
1029 - ], $context, $request_ids );
1030 -
1031 - $this->throw_on_error( $result );
1032 -
1033 - return [
1034 - 'images' => $result['images'],
1035 - 'response_id' => $result['responseId'],
1036 - 'usage' => $result['usage'],
1037 - ];
1038 - }
1039 - public function ajax_ai_get_image_to_image_mask_cleanup( $data ) {
1040 - $this->verify_upload_permissions( $data );
1041 -
1042 - $app = $this->get_ai_app();
1043 -
1044 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
1045 - throw new \Exception( 'Missing Image' );
1046 - }
1047 -
1048 - if ( empty( $data['payload']['settings'] ) ) {
1049 - throw new \Exception( 'Missing prompt settings' );
1050 - }
1051 -
1052 758 if ( ! $app->is_connected() ) {
1053 759 throw new \Exception( 'not_connected' );
1054 760 }
1055 761
@@ -1059,9 +765,10 @@
1059 765
1060 766 $context = $this->get_request_context( $data );
1061 767 $request_ids = $this->get_request_ids( $data['payload'] );
1062 768
1063 - $result = $app->get_image_to_image_mask_cleanup( [
769 + $result = $app->get_image_to_image_mask( [
770 + 'prompt' => $data['payload']['prompt'],
1064 771 'attachment_id' => $data['payload']['image']['id'],
1065 772 'mask' => $data['payload']['mask'],
1066 773 ], $context, $request_ids );
1067 774
@@ -1072,9 +779,8 @@
1072 779 'response_id' => $result['responseId'],
1073 780 'usage' => $result['usage'],
1074 781 ];
1075 782 }
1076 -
1077 783 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1078 784 $this->verify_upload_permissions( $data );
1079 785
1080 786 $app = $this->get_ai_app();
@@ -1105,10 +811,8 @@
1105 811 ];
1106 812 }
1107 813
1108 814 public function ajax_ai_upload_image( $data ) {
1109 - $this->verify_upload_permissions( $data );
1110 -
1111 815 if ( empty( $data['image'] ) ) {
1112 816 throw new \Exception( 'Missing image data' );
1113 817 }
1114 818
@@ -1120,14 +824,14 @@
1120 824
1121 825 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1122 826
1123 827 if ( is_wp_error( $image_data ) ) {
1124 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
828 + throw new \Exception( $image_data->get_error_message() );
1125 829 }
1126 830
1127 831 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1128 - $app = $this->get_ai_app();
1129 - $app->set_used_gallery_image( $image['id'] );
832 + $app = $this->get_ai_app();
833 + $app->set_used_gallery_image( $image['id'] );
1130 834 }
1131 835
1132 836 return [
1133 837 'image' => array_merge( $image_data, $data ),
@@ -1156,9 +860,9 @@
1156 860 $message = $result->get_error_message();
1157 861
1158 862 if ( is_array( $message ) ) {
1159 863 $message = implode( ', ', $message );
1160 - throw new \Exception( esc_html( $message ) );
864 + throw new \Exception( $message );
1161 865 }
1162 866
1163 867 $this->throw_on_error( $result );
1164 868 }
@@ -1306,24 +1010,10 @@
1306 1010 if ( ! current_user_can( 'upload_files' ) ) {
1307 1011 throw new \Exception( 'Not Allowed to Upload images' );
1308 1012 }
1309 1013
1310 - $uploads_manager = new \Elementor\Core\Files\Uploads_Manager();
1311 - if ( $uploads_manager::are_unfiltered_uploads_enabled() ) {
1312 - Plugin::$instance->uploads_manager->set_elementor_upload_state( true );
1313 - add_filter( 'wp_handle_sideload_prefilter', [ Plugin::$instance->uploads_manager, 'handle_elementor_upload' ] );
1314 - add_filter( 'image_sideload_extensions', function( $extensions ) {
1315 - $extensions[] = 'svg';
1316 - return $extensions;
1317 - });
1318 - }
1319 -
1320 1014 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1321 1015
1322 - if ( is_wp_error( $attachment_id ) ) {
1323 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1324 - }
1325 -
1326 1016 if ( ! empty( $attachment_id['error'] ) ) {
1327 1017 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1328 1018 }
1329 1019
@@ -1328,10 +1018,10 @@
1328 1018 }
1329 1019
1330 1020 return [
1331 1021 'id' => $attachment_id,
1332 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1333 - 'alt' => esc_attr( $image_title ),
1022 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
1023 + 'alt' => $image_title,
1334 1024 'source' => 'library',
1335 1025 ];
1336 1026 }
1337 1027
@@ -1355,9 +1045,9 @@
1355 1045
1356 1046 $result = $app->get_history_by_type( $type, $page, $limit, $context );
1357 1047
1358 1048 if ( is_wp_error( $result ) ) {
1359 - throw new \Exception( esc_html( $result->get_error_message() ) );
1049 + throw new \Exception( $result->get_error_message() );
1360 1050 }
1361 1051
1362 1052 return $result;
1363 1053 }
@@ -1377,9 +1067,9 @@
1377 1067
1378 1068 $result = $app->delete_history_item( $data['id'], $context );
1379 1069
1380 1070 if ( is_wp_error( $result ) ) {
1381 - throw new \Exception( esc_html( $result->get_error_message() ) );
1071 + throw new \Exception( $result->get_error_message() );
1382 1072 }
1383 1073
1384 1074 return [];
1385 1075 }
@@ -1399,83 +1089,14 @@
1399 1089
1400 1090 $result = $app->toggle_favorite_history_item( $data['id'], $context );
1401 1091
1402 1092 if ( is_wp_error( $result ) ) {
1403 - throw new \Exception( esc_html( $result->get_error_message() ) );
1093 + throw new \Exception( $result->get_error_message() );
1404 1094 }
1405 1095
1406 1096 return [];
1407 1097 }
1408 1098
1409 - public function ajax_ai_get_product_image_unification( $data ): array {
1410 - if ( ! empty( $data['payload']['postId'] ) ) {
1411 - $data['editor_post_id'] = $data['payload']['postId'];
1412 - }
1413 - $this->verify_upload_permissions( $data );
1414 -
1415 - $app = $this->get_ai_app();
1416 -
1417 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
1418 - throw new \Exception( 'Missing Image' );
1419 - }
1420 -
1421 - if ( empty( $data['payload']['settings'] ) ) {
1422 - throw new \Exception( 'Missing prompt settings' );
1423 - }
1424 -
1425 - if ( ! $app->is_connected() ) {
1426 - throw new \Exception( 'not_connected' );
1427 - }
1428 -
1429 - $context = $this->get_request_context( $data );
1430 - $request_ids = $this->get_request_ids( $data['payload'] );
1431 -
1432 - $result = $app->get_unify_product_images( [
1433 - 'promptSettings' => $data['payload']['settings'],
1434 - 'attachment_id' => $data['payload']['image']['id'],
1435 - 'featureIdentifier' => $data['payload']['featureIdentifier'] ?? '',
1436 - ], $context, $request_ids );
1437 -
1438 - $this->throw_on_error( $result );
1439 -
1440 - return [
1441 - 'images' => $result['images'],
1442 - 'response_id' => $result['responseId'],
1443 - 'usage' => $result['usage'],
1444 - ];
1445 - }
1446 -
1447 - public function ajax_ai_get_animation( $data ): array {
1448 - $this->verify_upload_permissions( $data );
1449 -
1450 - $app = $this->get_ai_app();
1451 -
1452 - if ( empty( $data['payload']['prompt'] ) ) {
1453 - throw new \Exception( 'Missing prompt' );
1454 - }
1455 -
1456 - if ( empty( $data['payload']['motionEffectType'] ) ) {
1457 - throw new \Exception( 'Missing animation type' );
1458 - }
1459 -
1460 - if ( ! $app->is_connected() ) {
1461 - throw new \Exception( 'not_connected' );
1462 - }
1463 -
1464 - $context = $this->get_request_context( $data );
1465 -
1466 - $request_ids = $this->get_request_ids( $data['payload'] );
1467 -
1468 - $result = $app->get_animation( $data, $context, $request_ids );
1469 - $this->throw_on_error( $result );
1470 -
1471 - return [
1472 - 'text' => $result['text'],
1473 - 'response_id' => $result['responseId'],
1474 - 'usage' => $result['usage'],
1475 - ];
1476 - }
1477 -
1478 1099 /**
1479 1100 * @param mixed $result
1480 1101 */
1481 1102 private function throw_on_error( $result ): void {
@@ -1480,69 +1101,10 @@
1480 1101 */
1481 1102 private function throw_on_error( $result ): void {
1482 1103 if ( is_wp_error( $result ) ) {
1483 1104 wp_send_json_error( [
1484 - 'message' => esc_html( $result->get_error_message() ),
1105 + 'message' => $result->get_error_message(),
1485 1106 'extra_data' => $result->get_error_data(),
1486 1107 ] );
1487 1108 }
1488 - }
1489 -
1490 - /**
1491 - * @return void
1492 - */
1493 - public function add_wc_scripts(): void {
1494 - wp_enqueue_script( 'elementor-ai-unify-product-images',
1495 - $this->get_js_assets_url( 'ai-unify-product-images' ),
1496 - [
1497 - 'jquery',
1498 - 'elementor-v2-ui',
1499 - 'elementor-v2-icons',
1500 - 'wp-components',
1501 - 'elementor-common',
1502 - ],
1503 - ELEMENTOR_VERSION,
1504 - true
1505 - );
1506 -
1507 - wp_localize_script(
1508 - 'elementor-ai-unify-product-images',
1509 - 'UnifyProductImagesConfig',
1510 - [
1511 - 'get_product_images_url' => admin_url( 'admin-ajax.php' ),
1512 - 'set_product_images_url' => admin_url( 'admin-ajax.php' ),
1513 - 'nonce' => wp_create_nonce( 'elementor-ai-unify-product-images_nonce' ),
1514 - 'placeholder' => ELEMENTOR_ASSETS_URL . 'images/app/ai/product-image-unification-example.gif?' . ELEMENTOR_VERSION,
1515 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
1516 - 'connect_url' => $this->get_ai_connect_url(),
1517 - ]
1518 - );
1519 -
1520 - add_filter( 'bulk_actions-edit-product', function ( $data ) {
1521 - return $this->add_products_bulk_action( $data );
1522 - });
1523 -
1524 - wp_set_script_translations( 'elementor-ai-unify-product-images', 'elementor' );
1525 - }
1526 -
1527 - /**
1528 - * @param $product
1529 - * @param int|null $image_to_remove
1530 - * @param int|null $image_to_add
1531 - * @return void
1532 - */
1533 - private function update_product_gallery( $product, ?int $image_to_remove, ?int $image_to_add ): void {
1534 - $gallery_image_ids = $product->get_gallery_image_ids();
1535 -
1536 - $index = array_search( $image_to_remove, $gallery_image_ids, true );
1537 - if ( false !== $index ) {
1538 - unset( $gallery_image_ids[ $index ] );
1539 - }
1540 -
1541 - if ( ! in_array( $image_to_add, $gallery_image_ids, true ) ) {
1542 - $gallery_image_ids[] = $image_to_add;
1543 - }
1544 -
1545 - $product->set_gallery_image_ids( $gallery_image_ids );
1546 - $product->save();
1547 1109 }
1548 1110 }