PluginProbe
Elementor Website Builder – more than just a page builder / 3.23.0-beta5
Elementor Website Builder – more than just a page builder v3.23.0-beta5
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 +118 -513 4.3.4 → 3.23.0-beta5 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,9 +46,8 @@
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 52 'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ],
61 53 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
@@ -66,9 +58,8 @@
66 58 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
67 59 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
68 60 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
69 61 '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 62 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
72 63 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
73 64 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
74 65 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
@@ -77,11 +68,8 @@
77 68 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
78 69 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
79 70 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
80 71 '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 72 ];
85 73
86 74 foreach ( $handlers as $tag => $callback ) {
87 75 $ajax->register_ajax_action( $tag, $callback );
@@ -89,9 +77,12 @@
89 77 } );
90 78
91 79 add_action( 'elementor/editor/before_enqueue_scripts', function() {
92 80 $this->enqueue_main_script();
93 - $this->enqueue_layout_script();
81 +
82 + if ( $this->is_layout_active() ) {
83 + $this->enqueue_layout_script();
84 + }
94 85 } );
95 86
96 87 add_action( 'elementor/editor/after_enqueue_styles', function() {
97 88 wp_enqueue_style(
@@ -102,26 +93,20 @@
102 93 );
103 94 } );
104 95
105 96 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 - );
97 + if ( $this->is_layout_active() ) {
98 + wp_enqueue_style(
99 + 'elementor-ai-layout-preview',
100 + $this->get_css_assets_url( 'modules/ai/layout-preview' ),
101 + [],
102 + ELEMENTOR_VERSION
103 + );
104 + }
112 105 } );
113 106
114 107 if ( is_admin() ) {
115 108 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 109 }
125 110
126 111 add_action( 'enqueue_block_editor_assets', function() {
127 112 wp_enqueue_script( 'elementor-ai-gutenberg',
@@ -133,23 +118,31 @@
133 118 'wp-blocks',
134 119 'wp-element',
135 120 'wp-editor',
136 121 'wp-data',
137 - 'wp-components',
138 - 'wp-compose',
139 - 'wp-i18n',
140 - 'wp-hooks',
141 - 'elementor-ai-media-library',
142 122 ],
143 123 ELEMENTOR_VERSION, true );
144 124
125 + $session_id = 'wp-gutenberg-session-' . Utils::generate_random_string();
126 +
127 + $config = [
128 + 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
129 + 'connect_url' => $this->get_ai_connect_url(),
130 + 'client_session_id' => $session_id,
131 + ];
132 +
133 + if ( $this->get_ai_app()->is_connected() ) {
134 + $usage = $this->get_ai_app()->get_usage( 'gutenberg-loader', $session_id );
135 +
136 + if ( ! is_wp_error( $usage ) ) {
137 + $config['usage'] = $usage;
138 + }
139 + }
140 +
145 141 wp_localize_script(
146 142 'elementor-ai-gutenberg',
147 143 'ElementorAiConfig',
148 - [
149 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
150 - 'connect_url' => $this->get_ai_connect_url(),
151 - ]
144 + $config
152 145 );
153 146
154 147 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
155 148 });
@@ -154,274 +147,29 @@
154 147 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
155 148 });
156 149
157 150 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;
151 + if ( $this->is_layout_active() ) {
152 + return $this->remove_temporary_containers( $data );
339 153 }
340 154
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();
155 + return $data;
156 + } );
361 157 }
362 158
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 - ],
159 + private function register_layout_experiment() {
160 + Plugin::$instance->experiments->add_feature( [
161 + 'name' => static::LAYOUT_EXPERIMENT,
162 + 'title' => esc_html__( 'Build with AI', 'elementor' ),
163 + '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' ),
164 + 'default' => Experiments_Manager::STATE_ACTIVE,
165 + 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE,
166 + 'dependencies' => [
167 + 'container',
372 168 ],
373 - 'fields' => 'ids',
374 - 'numberposts' => 1,
375 169 ] );
376 -
377 - return ! empty( $attachments ) ? $attachments[0] : null;
378 170 }
379 171
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 172 public function enqueue_ai_media_library() {
425 173 wp_enqueue_script( 'elementor-ai-media-library',
426 174 $this->get_js_assets_url( 'ai-media-library' ),
427 175 [
@@ -433,15 +181,26 @@
433 181 ELEMENTOR_VERSION,
434 182 true
435 183 );
436 184
185 + if ( function_exists( 'get_current_screen' ) ) {
186 + if ( get_current_screen()->is_block_editor ) {
187 + return;
188 + }
189 + }
190 +
191 + $session_id = 'wp-media-library-session-' . Utils::generate_random_string();
192 +
193 + $config = [
194 + 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
195 + 'connect_url' => $this->get_ai_connect_url(),
196 + 'client_session_id' => $session_id,
197 + ];
198 +
437 199 wp_localize_script(
438 200 'elementor-ai-media-library',
439 201 'ElementorAiConfig',
440 - [
441 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
442 - 'connect_url' => $this->get_ai_connect_url(),
443 - ]
202 + $config
444 203 );
445 204
446 205 wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
447 206 }
@@ -465,13 +224,24 @@
465 224 ELEMENTOR_VERSION,
466 225 true
467 226 );
468 227
228 + $session_id = 'elementor-editor-session-' . Utils::generate_random_string();
229 +
469 230 $config = [
470 231 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
471 232 'connect_url' => $this->get_ai_connect_url(),
233 + 'client_session_id' => $session_id,
472 234 ];
473 235
236 + if ( $this->get_ai_app()->is_connected() ) {
237 + $usage = $this->get_ai_app()->get_usage( 'elementor-loader', $session_id );
238 +
239 + if ( ! is_wp_error( $usage ) ) {
240 + $config['usage'] = $usage;
241 + }
242 + }
243 +
474 244 wp_localize_script(
475 245 'elementor-ai',
476 246 'ElementorAiConfig',
477 247 $config
@@ -477,8 +247,31 @@
477 247 $config
478 248 );
479 249
480 250 wp_set_script_translations( 'elementor-ai', 'elementor' );
251 +
252 + if ( $this->get_ai_app()->is_connected() && ! empty( $config['is_get_started'] ) ) {
253 + $remote_config = Utils::get_cached_callback( [ $this->get_ai_app(), 'get_remote_config' ], 'ai_remote_config-' . get_current_user_id(), HOUR_IN_SECONDS );
254 +
255 + if ( ! is_wp_error( $remote_config ) && ! empty( $remote_config['config']['remoteIntegrationUrl'] ) ) {
256 + wp_enqueue_script(
257 + 'elementor-ai-integration',
258 + $remote_config['config']['remoteIntegrationUrl'],
259 + [
260 + 'elementor-ai',
261 + ],
262 + ELEMENTOR_VERSION,
263 + true
264 + );
265 + }
266 +
267 + add_filter( 'script_loader_tag', function( $tag, $handle ) {
268 + if ( 'elementor-ai-integration' === $handle ) {
269 + return str_replace( ' src', ' type="module" src', $tag );
270 + }
271 + return $tag;
272 + }, 10, 2 );
273 + }
481 274 }
482 275
483 276 private function enqueue_layout_script() {
484 277 wp_enqueue_script(
@@ -501,10 +294,14 @@
501 294
502 295 wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
503 296 }
504 297
298 + private function is_layout_active() {
299 + return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT );
300 + }
301 +
505 302 private function remove_temporary_containers( $data ) {
506 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
303 + if ( empty( $data['elements'] ) ) {
507 304 return $data;
508 305 }
509 306
510 307 // If for some reason the document has been saved during an AI Layout session,
@@ -539,9 +336,9 @@
539 336 'connect_url' => $this->get_ai_connect_url(),
540 337 ];
541 338 }
542 339
543 - $user_usage = wp_parse_args( $app->get_usage(), [
340 + $user_usage = wp_parse_args( $app->get_usage( 'elementor-editor', $data['editor_session_id'] ), [
544 341 'hasAiSubscription' => false,
545 342 'usedQuota' => 0,
546 343 'quota' => 100,
547 344 ] );
@@ -561,17 +358,8 @@
561 358 }
562 359
563 360 return $app->get_remote_config();
564 361 }
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 362 public function verify_upload_permissions( $data ) {
575 363 $referer = wp_get_referer();
576 364
577 365 if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) {
@@ -578,9 +366,8 @@
578 366 return;
579 367 }
580 368 $this->verify_permissions( $data['editor_post_id'] );
581 369 }
582 -
583 370 private function verify_permissions( $editor_post_id ) {
584 371 $document = Plugin::$instance->documents->get( $editor_post_id );
585 372
586 373 if ( ! $document ) {
@@ -586,14 +373,10 @@
586 373 if ( ! $document ) {
587 374 throw new \Exception( 'Document not found' );
588 375 }
589 376
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' );
377 + if ( ! $document->is_editable_by_current_user() ) {
378 + throw new \Exception( 'Access denied' );
596 379 }
597 380 }
598 381
599 382 public function ajax_ai_get_image_prompt_enhancer( $data ) {
@@ -699,9 +482,9 @@
699 482 'usage' => $result['usage'],
700 483 ];
701 484 }
702 485
703 - private function get_ai_app(): Ai {
486 + private function get_ai_app() : Ai {
704 487 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
705 488 }
706 489
707 490 private function get_request_context( $data ) {
@@ -1014,42 +797,8 @@
1014 797 if ( empty( $data['payload']['settings'] ) ) {
1015 798 throw new \Exception( 'Missing prompt settings' );
1016 799 }
1017 800
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 801 if ( ! $app->is_connected() ) {
1053 802 throw new \Exception( 'not_connected' );
1054 803 }
1055 804
@@ -1059,9 +808,10 @@
1059 808
1060 809 $context = $this->get_request_context( $data );
1061 810 $request_ids = $this->get_request_ids( $data['payload'] );
1062 811
1063 - $result = $app->get_image_to_image_mask_cleanup( [
812 + $result = $app->get_image_to_image_mask( [
813 + 'prompt' => $data['payload']['prompt'],
1064 814 'attachment_id' => $data['payload']['image']['id'],
1065 815 'mask' => $data['payload']['mask'],
1066 816 ], $context, $request_ids );
1067 817
@@ -1072,9 +822,8 @@
1072 822 'response_id' => $result['responseId'],
1073 823 'usage' => $result['usage'],
1074 824 ];
1075 825 }
1076 -
1077 826 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1078 827 $this->verify_upload_permissions( $data );
1079 828
1080 829 $app = $this->get_ai_app();
@@ -1105,10 +854,8 @@
1105 854 ];
1106 855 }
1107 856
1108 857 public function ajax_ai_upload_image( $data ) {
1109 - $this->verify_upload_permissions( $data );
1110 -
1111 858 if ( empty( $data['image'] ) ) {
1112 859 throw new \Exception( 'Missing image data' );
1113 860 }
1114 861
@@ -1120,14 +867,14 @@
1120 867
1121 868 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1122 869
1123 870 if ( is_wp_error( $image_data ) ) {
1124 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
871 + throw new \Exception( $image_data->get_error_message() );
1125 872 }
1126 873
1127 874 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1128 - $app = $this->get_ai_app();
1129 - $app->set_used_gallery_image( $image['id'] );
875 + $app = $this->get_ai_app();
876 + $app->set_used_gallery_image( $image['id'] );
1130 877 }
1131 878
1132 879 return [
1133 880 'image' => array_merge( $image_data, $data ),
@@ -1156,9 +903,9 @@
1156 903 $message = $result->get_error_message();
1157 904
1158 905 if ( is_array( $message ) ) {
1159 906 $message = implode( ', ', $message );
1160 - throw new \Exception( esc_html( $message ) );
907 + throw new \Exception( $message );
1161 908 }
1162 909
1163 910 $this->throw_on_error( $result );
1164 911 }
@@ -1306,24 +1053,10 @@
1306 1053 if ( ! current_user_can( 'upload_files' ) ) {
1307 1054 throw new \Exception( 'Not Allowed to Upload images' );
1308 1055 }
1309 1056
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 1057 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1321 1058
1322 - if ( is_wp_error( $attachment_id ) ) {
1323 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1324 - }
1325 -
1326 1059 if ( ! empty( $attachment_id['error'] ) ) {
1327 1060 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1328 1061 }
1329 1062
@@ -1328,10 +1061,10 @@
1328 1061 }
1329 1062
1330 1063 return [
1331 1064 'id' => $attachment_id,
1332 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1333 - 'alt' => esc_attr( $image_title ),
1065 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
1066 + 'alt' => $image_title,
1334 1067 'source' => 'library',
1335 1068 ];
1336 1069 }
1337 1070
@@ -1355,9 +1088,9 @@
1355 1088
1356 1089 $result = $app->get_history_by_type( $type, $page, $limit, $context );
1357 1090
1358 1091 if ( is_wp_error( $result ) ) {
1359 - throw new \Exception( esc_html( $result->get_error_message() ) );
1092 + throw new \Exception( $result->get_error_message() );
1360 1093 }
1361 1094
1362 1095 return $result;
1363 1096 }
@@ -1377,9 +1110,9 @@
1377 1110
1378 1111 $result = $app->delete_history_item( $data['id'], $context );
1379 1112
1380 1113 if ( is_wp_error( $result ) ) {
1381 - throw new \Exception( esc_html( $result->get_error_message() ) );
1114 + throw new \Exception( $result->get_error_message() );
1382 1115 }
1383 1116
1384 1117 return [];
1385 1118 }
@@ -1399,83 +1132,14 @@
1399 1132
1400 1133 $result = $app->toggle_favorite_history_item( $data['id'], $context );
1401 1134
1402 1135 if ( is_wp_error( $result ) ) {
1403 - throw new \Exception( esc_html( $result->get_error_message() ) );
1136 + throw new \Exception( $result->get_error_message() );
1404 1137 }
1405 1138
1406 1139 return [];
1407 1140 }
1408 1141
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 1142 /**
1479 1143 * @param mixed $result
1480 1144 */
1481 1145 private function throw_on_error( $result ): void {
@@ -1480,69 +1144,10 @@
1480 1144 */
1481 1145 private function throw_on_error( $result ): void {
1482 1146 if ( is_wp_error( $result ) ) {
1483 1147 wp_send_json_error( [
1484 - 'message' => esc_html( $result->get_error_message() ),
1148 + 'message' => $result->get_error_message(),
1485 1149 'extra_data' => $result->get_error_data(),
1486 1150 ] );
1487 1151 }
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 1152 }
1548 1153 }