PluginProbe
Elementor Website Builder – more than just a page builder / 3.23.0-dev3
Elementor Website Builder – more than just a page builder v3.23.0-dev3
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
← All changes | modules/ai/module.php +111 -563 4.3.0-beta23.23.0-dev3 View file →
@@ -1,14 +1,13 @@
1 1 <?php
2 2 namespace Elementor\Modules\Ai;
3 3
4 -use Elementor\Controls_Manager;
5 4 use Elementor\Core\Base\Module as BaseModule;
6 5 use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
7 -use Elementor\Element_Base;
8 -use Elementor\Plugin;
6 +use Elementor\Core\Experiments\Manager as Experiments_Manager;
9 7 use Elementor\Core\Utils\Collection;
10 8 use Elementor\Modules\Ai\Connect\Ai;
9 +use Elementor\Plugin;
11 10 use Elementor\User;
12 11 use Elementor\Utils;
13 12
14 13 if ( ! defined( 'ABSPATH' ) ) {
@@ -27,10 +26,11 @@
27 26 self::HISTORY_TYPE_CODE,
28 27 self::HISTORY_TYPE_IMAGE,
29 28 self::HISTORY_TYPE_BLOCK,
30 29 ];
31 - const MIN_PAGES_FOR_CREATE_WITH_AI_BANNER = 10;
32 30
31 + const LAYOUT_EXPERIMENT = 'ai-layout';
32 +
33 33 public function get_name() {
34 34 return 'ai';
35 35 }
36 36
@@ -36,21 +36,10 @@
36 36
37 37 public function __construct() {
38 38 parent::__construct();
39 39
40 - ( new SitePlannerConnect\Module() );
40 + $this->register_layout_experiment();
41 41
42 - if ( is_admin() ) {
43 - ( new Preferences() )->register();
44 - add_action( 'elementor/import-export/import-kit/runner/after-run', [ $this, 'handle_kit_install' ] );
45 - }
46 -
47 - if ( ! $this->is_ai_enabled() ) {
48 - return;
49 - }
50 -
51 - add_filter( 'elementor/core/admin/homescreen', [ $this, 'add_create_with_ai_banner_to_homescreen' ] );
52 -
53 42 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
54 43 $connect_module->register_app( 'ai', Ai::get_class_name() );
55 44 } );
56 45
@@ -57,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',
@@ -133,27 +114,31 @@
133 114 [
134 115 'jquery',
135 116 'elementor-v2-ui',
136 117 'elementor-v2-icons',
137 - 'wp-blocks',
138 - 'wp-element',
139 - 'wp-editor',
140 - 'wp-data',
141 - 'wp-components',
142 - 'wp-compose',
143 - 'wp-i18n',
144 - 'wp-hooks',
145 - 'elementor-ai-media-library',
146 118 ],
147 119 ELEMENTOR_VERSION, true );
148 120
121 + $session_id = 'elementor-editor-session-' . Utils::generate_random_string();
122 +
123 + $config = [
124 + 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
125 + 'connect_url' => $this->get_ai_connect_url(),
126 + 'client_session_id' => $session_id,
127 + ];
128 +
129 + if ( $this->get_ai_app()->is_connected() ) {
130 + $usage = $this->get_ai_app()->get_usage( 'gutenberg-loader', $session_id );
131 +
132 + if ( ! is_wp_error( $usage ) ) {
133 + $config['usage'] = $usage;
134 + }
135 + }
136 +
149 137 wp_localize_script(
150 138 'elementor-ai-gutenberg',
151 139 'ElementorAiConfig',
152 - [
153 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
154 - 'connect_url' => $this->get_ai_connect_url(),
155 - ]
140 + $config
156 141 );
157 142
158 143 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
159 144 });
@@ -158,274 +143,29 @@
158 143 wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
159 144 });
160 145
161 146 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;
147 + if ( $this->is_layout_active() ) {
148 + return $this->remove_temporary_containers( $data );
343 149 }
344 150
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();
151 + return $data;
152 + } );
365 153 }
366 154
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 - ],
155 + private function register_layout_experiment() {
156 + Plugin::$instance->experiments->add_feature( [
157 + 'name' => static::LAYOUT_EXPERIMENT,
158 + 'title' => esc_html__( 'Build with AI', 'elementor' ),
159 + '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' ),
160 + 'default' => Experiments_Manager::STATE_ACTIVE,
161 + 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE,
162 + 'dependencies' => [
163 + 'container',
376 164 ],
377 - 'fields' => 'ids',
378 - 'numberposts' => 1,
379 165 ] );
380 -
381 - return ! empty( $attachments ) ? $attachments[0] : null;
382 166 }
383 167
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 168 public function enqueue_ai_media_library() {
429 169 wp_enqueue_script( 'elementor-ai-media-library',
430 170 $this->get_js_assets_url( 'ai-media-library' ),
431 171 [
@@ -437,15 +177,20 @@
437 177 ELEMENTOR_VERSION,
438 178 true
439 179 );
440 180
181 + $session_id = 'wp-media-library-session-' . Utils::generate_random_string();
182 +
183 + $config = [
184 + 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
185 + 'connect_url' => $this->get_ai_connect_url(),
186 + 'client_session_id' => $session_id,
187 + ];
188 +
441 189 wp_localize_script(
442 190 'elementor-ai-media-library',
443 191 'ElementorAiConfig',
444 - [
445 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
446 - 'connect_url' => $this->get_ai_connect_url(),
447 - ]
192 + $config
448 193 );
449 194
450 195 wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
451 196 }
@@ -469,13 +214,24 @@
469 214 ELEMENTOR_VERSION,
470 215 true
471 216 );
472 217
218 + $session_id = 'elementor-editor-session-' . Utils::generate_random_string();
219 +
473 220 $config = [
474 221 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
475 222 'connect_url' => $this->get_ai_connect_url(),
223 + 'client_session_id' => $session_id,
476 224 ];
477 225
226 + if ( $this->get_ai_app()->is_connected() ) {
227 + $usage = $this->get_ai_app()->get_usage( 'elementor-loader', $session_id );
228 +
229 + if ( ! is_wp_error( $usage ) ) {
230 + $config['usage'] = $usage;
231 + }
232 + }
233 +
478 234 wp_localize_script(
479 235 'elementor-ai',
480 236 'ElementorAiConfig',
481 237 $config
@@ -481,8 +237,31 @@
481 237 $config
482 238 );
483 239
484 240 wp_set_script_translations( 'elementor-ai', 'elementor' );
241 +
242 + if ( $this->get_ai_app()->is_connected() && ! empty( $config['is_get_started'] ) ) {
243 + $remote_config = Utils::get_cached_callback( [ $this->get_ai_app(), 'get_remote_config' ], 'ai_remote_config-' . get_current_user_id(), HOUR_IN_SECONDS );
244 +
245 + if ( ! is_wp_error( $remote_config ) && ! empty( $remote_config['config']['remoteIntegrationUrl'] ) ) {
246 + wp_enqueue_script(
247 + 'elementor-ai-integration',
248 + $remote_config['config']['remoteIntegrationUrl'],
249 + [
250 + 'elementor-ai',
251 + ],
252 + ELEMENTOR_VERSION,
253 + true
254 + );
255 + }
256 +
257 + add_filter( 'script_loader_tag', function( $tag, $handle ) {
258 + if ( 'elementor-ai-integration' === $handle ) {
259 + return str_replace( ' src', ' type="module" src', $tag );
260 + }
261 + return $tag;
262 + }, 10, 2 );
263 + }
485 264 }
486 265
487 266 private function enqueue_layout_script() {
488 267 wp_enqueue_script(
@@ -505,10 +284,14 @@
505 284
506 285 wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
507 286 }
508 287
288 + private function is_layout_active() {
289 + return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT );
290 + }
291 +
509 292 private function remove_temporary_containers( $data ) {
510 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
293 + if ( empty( $data['elements'] ) ) {
511 294 return $data;
512 295 }
513 296
514 297 // If for some reason the document has been saved during an AI Layout session,
@@ -543,9 +326,9 @@
543 326 'connect_url' => $this->get_ai_connect_url(),
544 327 ];
545 328 }
546 329
547 - $user_usage = wp_parse_args( $app->get_usage(), [
330 + $user_usage = wp_parse_args( $app->get_usage( 'elementor-editor', $data['editor_session_id'] ), [
548 331 'hasAiSubscription' => false,
549 332 'usedQuota' => 0,
550 333 'quota' => 100,
551 334 ] );
@@ -565,17 +348,8 @@
565 348 }
566 349
567 350 return $app->get_remote_config();
568 351 }
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 352 public function verify_upload_permissions( $data ) {
579 353 $referer = wp_get_referer();
580 354
581 355 if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) {
@@ -582,9 +356,8 @@
582 356 return;
583 357 }
584 358 $this->verify_permissions( $data['editor_post_id'] );
585 359 }
586 -
587 360 private function verify_permissions( $editor_post_id ) {
588 361 $document = Plugin::$instance->documents->get( $editor_post_id );
589 362
590 363 if ( ! $document ) {
@@ -590,14 +363,10 @@
590 363 if ( ! $document ) {
591 364 throw new \Exception( 'Document not found' );
592 365 }
593 366
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' );
367 + if ( ! $document->is_editable_by_current_user() ) {
368 + throw new \Exception( 'Access denied' );
600 369 }
601 370 }
602 371
603 372 public function ajax_ai_get_image_prompt_enhancer( $data ) {
@@ -703,9 +472,9 @@
703 472 'usage' => $result['usage'],
704 473 ];
705 474 }
706 475
707 - private function get_ai_app(): Ai {
476 + private function get_ai_app() : Ai {
708 477 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
709 478 }
710 479
711 480 private function get_request_context( $data ) {
@@ -1018,42 +787,8 @@
1018 787 if ( empty( $data['payload']['settings'] ) ) {
1019 788 throw new \Exception( 'Missing prompt settings' );
1020 789 }
1021 790
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 791 if ( ! $app->is_connected() ) {
1057 792 throw new \Exception( 'not_connected' );
1058 793 }
1059 794
@@ -1063,9 +798,10 @@
1063 798
1064 799 $context = $this->get_request_context( $data );
1065 800 $request_ids = $this->get_request_ids( $data['payload'] );
1066 801
1067 - $result = $app->get_image_to_image_mask_cleanup( [
802 + $result = $app->get_image_to_image_mask( [
803 + 'prompt' => $data['payload']['prompt'],
1068 804 'attachment_id' => $data['payload']['image']['id'],
1069 805 'mask' => $data['payload']['mask'],
1070 806 ], $context, $request_ids );
1071 807
@@ -1076,9 +812,8 @@
1076 812 'response_id' => $result['responseId'],
1077 813 'usage' => $result['usage'],
1078 814 ];
1079 815 }
1080 -
1081 816 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1082 817 $this->verify_upload_permissions( $data );
1083 818
1084 819 $app = $this->get_ai_app();
@@ -1109,10 +844,8 @@
1109 844 ];
1110 845 }
1111 846
1112 847 public function ajax_ai_upload_image( $data ) {
1113 - $this->verify_upload_permissions( $data );
1114 -
1115 848 if ( empty( $data['image'] ) ) {
1116 849 throw new \Exception( 'Missing image data' );
1117 850 }
1118 851
@@ -1124,14 +857,14 @@
1124 857
1125 858 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1126 859
1127 860 if ( is_wp_error( $image_data ) ) {
1128 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
861 + throw new \Exception( $image_data->get_error_message() );
1129 862 }
1130 863
1131 864 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1132 - $app = $this->get_ai_app();
1133 - $app->set_used_gallery_image( $image['id'] );
865 + $app = $this->get_ai_app();
866 + $app->set_used_gallery_image( $image['id'] );
1134 867 }
1135 868
1136 869 return [
1137 870 'image' => array_merge( $image_data, $data ),
@@ -1160,9 +893,9 @@
1160 893 $message = $result->get_error_message();
1161 894
1162 895 if ( is_array( $message ) ) {
1163 896 $message = implode( ', ', $message );
1164 - throw new \Exception( esc_html( $message ) );
897 + throw new \Exception( $message );
1165 898 }
1166 899
1167 900 $this->throw_on_error( $result );
1168 901 }
@@ -1310,24 +1043,10 @@
1310 1043 if ( ! current_user_can( 'upload_files' ) ) {
1311 1044 throw new \Exception( 'Not Allowed to Upload images' );
1312 1045 }
1313 1046
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 1047 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1325 1048
1326 - if ( is_wp_error( $attachment_id ) ) {
1327 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1328 - }
1329 -
1330 1049 if ( ! empty( $attachment_id['error'] ) ) {
1331 1050 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1332 1051 }
1333 1052
@@ -1332,10 +1051,10 @@
1332 1051 }
1333 1052
1334 1053 return [
1335 1054 'id' => $attachment_id,
1336 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1337 - 'alt' => esc_attr( $image_title ),
1055 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
1056 + 'alt' => $image_title,
1338 1057 'source' => 'library',
1339 1058 ];
1340 1059 }
1341 1060
@@ -1359,9 +1078,9 @@
1359 1078
1360 1079 $result = $app->get_history_by_type( $type, $page, $limit, $context );
1361 1080
1362 1081 if ( is_wp_error( $result ) ) {
1363 - throw new \Exception( esc_html( $result->get_error_message() ) );
1082 + throw new \Exception( $result->get_error_message() );
1364 1083 }
1365 1084
1366 1085 return $result;
1367 1086 }
@@ -1381,9 +1100,9 @@
1381 1100
1382 1101 $result = $app->delete_history_item( $data['id'], $context );
1383 1102
1384 1103 if ( is_wp_error( $result ) ) {
1385 - throw new \Exception( esc_html( $result->get_error_message() ) );
1104 + throw new \Exception( $result->get_error_message() );
1386 1105 }
1387 1106
1388 1107 return [];
1389 1108 }
@@ -1403,83 +1122,14 @@
1403 1122
1404 1123 $result = $app->toggle_favorite_history_item( $data['id'], $context );
1405 1124
1406 1125 if ( is_wp_error( $result ) ) {
1407 - throw new \Exception( esc_html( $result->get_error_message() ) );
1126 + throw new \Exception( $result->get_error_message() );
1408 1127 }
1409 1128
1410 1129 return [];
1411 1130 }
1412 1131
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 1132 /**
1483 1133 * @param mixed $result
1484 1134 */
1485 1135 private function throw_on_error( $result ): void {
@@ -1484,112 +1134,10 @@
1484 1134 */
1485 1135 private function throw_on_error( $result ): void {
1486 1136 if ( is_wp_error( $result ) ) {
1487 1137 wp_send_json_error( [
1488 - 'message' => esc_html( $result->get_error_message() ),
1138 + 'message' => $result->get_error_message(),
1489 1139 'extra_data' => $result->get_error_data(),
1490 1140 ] );
1491 1141 }
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 1142 }
1595 1143 }