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.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 4.1.0-beta2 4.1.0-dev2 All 455 releases
← All changes | modules/ai/module.php +117 -559 4.3.0-beta1 → 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,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,9 +46,8 @@
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 51 'ai_get_excerpt' => [ $this, 'ajax_ai_get_excerpt' ],
64 52 'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ],
65 53 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
@@ -70,9 +58,8 @@
70 58 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
71 59 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
72 60 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
73 61 '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 62 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
76 63 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
77 64 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
78 65 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
@@ -81,11 +68,8 @@
81 68 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
82 69 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
83 70 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
84 71 '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 72 ];
89 73
90 74 foreach ( $handlers as $tag => $callback ) {
91 75 $ajax->register_ajax_action( $tag, $callback );
@@ -93,9 +77,12 @@
93 77 } );
94 78
95 79 add_action( 'elementor/editor/before_enqueue_scripts', function() {
96 80 $this->enqueue_main_script();
97 - $this->enqueue_layout_script();
81 +
82 + if ( $this->is_layout_active() ) {
83 + $this->enqueue_layout_script();
84 + }
98 85 } );
99 86
100 87 add_action( 'elementor/editor/after_enqueue_styles', function() {
101 88 wp_enqueue_style(
@@ -106,26 +93,20 @@
106 93 );
107 94 } );
108 95
109 96 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 - );
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 + }
116 105 } );
117 106
118 107 if ( is_admin() ) {
119 108 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' ] );
127 - }
128 109 }
129 110
130 111 add_action( 'enqueue_block_editor_assets', function() {
131 112 wp_enqueue_script( 'elementor-ai-gutenberg',
@@ -137,23 +118,31 @@
137 118 'wp-blocks',
138 119 'wp-element',
139 120 'wp-editor',
140 121 'wp-data',
141 - 'wp-components',
142 - 'wp-compose',
143 - 'wp-i18n',
144 - 'wp-hooks',
145 - 'elementor-ai-media-library',
146 122 ],
147 123 ELEMENTOR_VERSION, true );
148 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 +
149 141 wp_localize_script(
150 142 'elementor-ai-gutenberg',
151 143 'ElementorAiConfig',
152 - [
153 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
154 - 'connect_url' => $this->get_ai_connect_url(),
155 - ]
144 + $config
156 145 );
157 146
158 147 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
159 148 });
@@ -158,274 +147,29 @@
158 147 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
159 148 });
160 149
161 150 add_filter( 'elementor/document/save/data', function ( $data ) {
162 - return $this->remove_temporary_containers( $data );
163 - } );
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 - }
170 -
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'],
212 - ],
213 - ] );
214 - }
215 -
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;
151 + if ( $this->is_layout_active() ) {
152 + return $this->remove_temporary_containers( $data );
343 153 }
344 154
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();
155 + return $data;
156 + } );
365 157 }
366 158
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 - ],
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',
376 168 ],
377 - 'fields' => 'ids',
378 - 'numberposts' => 1,
379 169 ] );
380 -
381 - return ! empty( $attachments ) ? $attachments[0] : null;
382 170 }
383 171
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 172 public function enqueue_ai_media_library() {
429 173 wp_enqueue_script( 'elementor-ai-media-library',
430 174 $this->get_js_assets_url( 'ai-media-library' ),
431 175 [
@@ -437,15 +181,26 @@
437 181 ELEMENTOR_VERSION,
438 182 true
439 183 );
440 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 +
441 199 wp_localize_script(
442 200 'elementor-ai-media-library',
443 201 'ElementorAiConfig',
444 - [
445 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
446 - 'connect_url' => $this->get_ai_connect_url(),
447 - ]
202 + $config
448 203 );
449 204
450 205 wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
451 206 }
@@ -469,13 +224,24 @@
469 224 ELEMENTOR_VERSION,
470 225 true
471 226 );
472 227
228 + $session_id = 'elementor-editor-session-' . Utils::generate_random_string();
229 +
473 230 $config = [
474 231 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
475 232 'connect_url' => $this->get_ai_connect_url(),
233 + 'client_session_id' => $session_id,
476 234 ];
477 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 +
478 244 wp_localize_script(
479 245 'elementor-ai',
480 246 'ElementorAiConfig',
481 247 $config
@@ -481,8 +247,31 @@
481 247 $config
482 248 );
483 249
484 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 + }
485 274 }
486 275
487 276 private function enqueue_layout_script() {
488 277 wp_enqueue_script(
@@ -505,10 +294,14 @@
505 294
506 295 wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
507 296 }
508 297
298 + private function is_layout_active() {
299 + return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT );
300 + }
301 +
509 302 private function remove_temporary_containers( $data ) {
510 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
303 + if ( empty( $data['elements'] ) ) {
511 304 return $data;
512 305 }
513 306
514 307 // If for some reason the document has been saved during an AI Layout session,
@@ -543,9 +336,9 @@
543 336 'connect_url' => $this->get_ai_connect_url(),
544 337 ];
545 338 }
546 339
547 - $user_usage = wp_parse_args( $app->get_usage(), [
340 + $user_usage = wp_parse_args( $app->get_usage( 'elementor-editor', $data['editor_session_id'] ), [
548 341 'hasAiSubscription' => false,
549 342 'usedQuota' => 0,
550 343 'quota' => 100,
551 344 ] );
@@ -565,17 +358,8 @@
565 358 }
566 359
567 360 return $app->get_remote_config();
568 361 }
569 -
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 362 public function verify_upload_permissions( $data ) {
579 363 $referer = wp_get_referer();
580 364
581 365 if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) {
@@ -582,9 +366,8 @@
582 366 return;
583 367 }
584 368 $this->verify_permissions( $data['editor_post_id'] );
585 369 }
586 -
587 370 private function verify_permissions( $editor_post_id ) {
588 371 $document = Plugin::$instance->documents->get( $editor_post_id );
589 372
590 373 if ( ! $document ) {
@@ -590,14 +373,10 @@
590 373 if ( ! $document ) {
591 374 throw new \Exception( 'Document not found' );
592 375 }
593 376
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' );
377 + if ( ! $document->is_editable_by_current_user() ) {
378 + throw new \Exception( 'Access denied' );
600 379 }
601 380 }
602 381
603 382 public function ajax_ai_get_image_prompt_enhancer( $data ) {
@@ -703,9 +482,9 @@
703 482 'usage' => $result['usage'],
704 483 ];
705 484 }
706 485
707 - private function get_ai_app(): Ai {
486 + private function get_ai_app() : Ai {
708 487 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
709 488 }
710 489
711 490 private function get_request_context( $data ) {
@@ -1018,42 +797,8 @@
1018 797 if ( empty( $data['payload']['settings'] ) ) {
1019 798 throw new \Exception( 'Missing prompt settings' );
1020 799 }
1021 800
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 801 if ( ! $app->is_connected() ) {
1057 802 throw new \Exception( 'not_connected' );
1058 803 }
1059 804
@@ -1063,9 +808,10 @@
1063 808
1064 809 $context = $this->get_request_context( $data );
1065 810 $request_ids = $this->get_request_ids( $data['payload'] );
1066 811
1067 - $result = $app->get_image_to_image_mask_cleanup( [
812 + $result = $app->get_image_to_image_mask( [
813 + 'prompt' => $data['payload']['prompt'],
1068 814 'attachment_id' => $data['payload']['image']['id'],
1069 815 'mask' => $data['payload']['mask'],
1070 816 ], $context, $request_ids );
1071 817
@@ -1076,9 +822,8 @@
1076 822 'response_id' => $result['responseId'],
1077 823 'usage' => $result['usage'],
1078 824 ];
1079 825 }
1080 -
1081 826 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1082 827 $this->verify_upload_permissions( $data );
1083 828
1084 829 $app = $this->get_ai_app();
@@ -1109,10 +854,8 @@
1109 854 ];
1110 855 }
1111 856
1112 857 public function ajax_ai_upload_image( $data ) {
1113 - $this->verify_upload_permissions( $data );
1114 -
1115 858 if ( empty( $data['image'] ) ) {
1116 859 throw new \Exception( 'Missing image data' );
1117 860 }
1118 861
@@ -1124,14 +867,14 @@
1124 867
1125 868 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1126 869
1127 870 if ( is_wp_error( $image_data ) ) {
1128 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
871 + throw new \Exception( $image_data->get_error_message() );
1129 872 }
1130 873
1131 874 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1132 - $app = $this->get_ai_app();
1133 - $app->set_used_gallery_image( $image['id'] );
875 + $app = $this->get_ai_app();
876 + $app->set_used_gallery_image( $image['id'] );
1134 877 }
1135 878
1136 879 return [
1137 880 'image' => array_merge( $image_data, $data ),
@@ -1160,9 +903,9 @@
1160 903 $message = $result->get_error_message();
1161 904
1162 905 if ( is_array( $message ) ) {
1163 906 $message = implode( ', ', $message );
1164 - throw new \Exception( esc_html( $message ) );
907 + throw new \Exception( $message );
1165 908 }
1166 909
1167 910 $this->throw_on_error( $result );
1168 911 }
@@ -1310,24 +1053,10 @@
1310 1053 if ( ! current_user_can( 'upload_files' ) ) {
1311 1054 throw new \Exception( 'Not Allowed to Upload images' );
1312 1055 }
1313 1056
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 1057 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1325 1058
1326 - if ( is_wp_error( $attachment_id ) ) {
1327 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1328 - }
1329 -
1330 1059 if ( ! empty( $attachment_id['error'] ) ) {
1331 1060 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1332 1061 }
1333 1062
@@ -1332,10 +1061,10 @@
1332 1061 }
1333 1062
1334 1063 return [
1335 1064 'id' => $attachment_id,
1336 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1337 - 'alt' => esc_attr( $image_title ),
1065 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
1066 + 'alt' => $image_title,
1338 1067 'source' => 'library',
1339 1068 ];
1340 1069 }
1341 1070
@@ -1359,9 +1088,9 @@
1359 1088
1360 1089 $result = $app->get_history_by_type( $type, $page, $limit, $context );
1361 1090
1362 1091 if ( is_wp_error( $result ) ) {
1363 - throw new \Exception( esc_html( $result->get_error_message() ) );
1092 + throw new \Exception( $result->get_error_message() );
1364 1093 }
1365 1094
1366 1095 return $result;
1367 1096 }
@@ -1381,9 +1110,9 @@
1381 1110
1382 1111 $result = $app->delete_history_item( $data['id'], $context );
1383 1112
1384 1113 if ( is_wp_error( $result ) ) {
1385 - throw new \Exception( esc_html( $result->get_error_message() ) );
1114 + throw new \Exception( $result->get_error_message() );
1386 1115 }
1387 1116
1388 1117 return [];
1389 1118 }
@@ -1403,83 +1132,14 @@
1403 1132
1404 1133 $result = $app->toggle_favorite_history_item( $data['id'], $context );
1405 1134
1406 1135 if ( is_wp_error( $result ) ) {
1407 - throw new \Exception( esc_html( $result->get_error_message() ) );
1136 + throw new \Exception( $result->get_error_message() );
1408 1137 }
1409 1138
1410 1139 return [];
1411 1140 }
1412 1141
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 1142 /**
1483 1143 * @param mixed $result
1484 1144 */
1485 1145 private function throw_on_error( $result ): void {
@@ -1484,112 +1144,10 @@
1484 1144 */
1485 1145 private function throw_on_error( $result ): void {
1486 1146 if ( is_wp_error( $result ) ) {
1487 1147 wp_send_json_error( [
1488 - 'message' => esc_html( $result->get_error_message() ),
1148 + 'message' => $result->get_error_message(),
1489 1149 'extra_data' => $result->get_error_data(),
1490 1150 ] );
1491 1151 }
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 1152 }
1595 1153 }