PluginProbe
Elementor Website Builder – more than just a page builder / 3.14.0-beta4
Elementor Website Builder – more than just a page builder v3.14.0-beta4
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 +135 -1180 4.3.4 → 3.14.0-beta4 View file →
@@ -1,34 +1,18 @@
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;
6 +use Elementor\Modules\Ai\Connect\Ai;
8 7 use Elementor\Plugin;
9 -use Elementor\Core\Utils\Collection;
10 -use Elementor\Modules\Ai\Connect\Ai;
11 8 use Elementor\User;
12 -use Elementor\Utils;
13 9
14 10 if ( ! defined( 'ABSPATH' ) ) {
15 11 exit; // Exit if accessed directly.
16 12 }
13 +class Module extends BaseModule {
17 14
18 -class Module extends BaseModule {
19 - const HISTORY_TYPE_ALL = 'all';
20 - const HISTORY_TYPE_TEXT = 'text';
21 - const HISTORY_TYPE_CODE = 'code';
22 - const HISTORY_TYPE_IMAGE = 'images';
23 - const HISTORY_TYPE_BLOCK = 'blocks';
24 - const VALID_HISTORY_TYPES = [
25 - self::HISTORY_TYPE_ALL,
26 - self::HISTORY_TYPE_TEXT,
27 - self::HISTORY_TYPE_CODE,
28 - self::HISTORY_TYPE_IMAGE,
29 - self::HISTORY_TYPE_BLOCK,
30 - ];
31 15 public function get_name() {
32 16 return 'ai';
33 17 }
34 18
@@ -34,503 +18,63 @@
34 18
35 19 public function __construct() {
36 20 parent::__construct();
37 21
38 - ( new SitePlannerConnect\Module() );
39 -
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 22 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
50 23 $connect_module->register_app( 'ai', Ai::get_class_name() );
51 24 } );
52 25
53 26 add_action( 'elementor/ajax/register_actions', function( $ajax ) {
54 - $handlers = [
55 - 'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ],
56 - 'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ],
57 - 'ai_get_remote_frontend_config' => [ $this, 'ajax_ai_get_remote_frontend_config' ],
58 - 'ai_get_completion_text' => [ $this, 'ajax_ai_get_completion_text' ],
59 - 'ai_get_excerpt' => [ $this, 'ajax_ai_get_excerpt' ],
60 - 'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ],
61 - 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ],
62 - 'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ],
63 - 'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ],
64 - 'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ],
65 - 'ai_set_status_feedback' => [ $this, 'ajax_ai_set_status_feedback' ],
66 - 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ],
67 - 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ],
68 - 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ],
69 - '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 - 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ],
72 - 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ],
73 - 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ],
74 - 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ],
75 - 'ai_upload_image' => [ $this, 'ajax_ai_upload_image' ],
76 - 'ai_generate_layout' => [ $this, 'ajax_ai_generate_layout' ],
77 - 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ],
78 - 'ai_get_history' => [ $this, 'ajax_ai_get_history' ],
79 - 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ],
80 - '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 - ];
85 -
86 - foreach ( $handlers as $tag => $callback ) {
87 - $ajax->register_ajax_action( $tag, $callback );
88 - }
27 + $ajax->register_ajax_action( 'ai_get_user_information', [ $this, 'ajax_ai_get_user_information' ] );
28 + $ajax->register_ajax_action( 'ai_get_completion_text', [ $this, 'ajax_ai_get_completion_text' ] );
29 + $ajax->register_ajax_action( 'ai_get_edit_text', [ $this, 'ajax_ai_get_edit_text' ] );
30 + $ajax->register_ajax_action( 'ai_get_custom_code', [ $this, 'ajax_ai_get_custom_code' ] );
31 + $ajax->register_ajax_action( 'ai_get_custom_css', [ $this, 'ajax_ai_get_custom_css' ] );
32 + $ajax->register_ajax_action( 'ai_set_get_started', [ $this, 'ajax_ai_set_get_started' ] );
33 + $ajax->register_ajax_action( 'ai_set_status_feedback', [ $this, 'ajax_ai_set_status_feedback' ] );
34 + $ajax->register_ajax_action( 'ai_get_image_prompt_enhancer', [ $this, 'ajax_ai_get_image_prompt_enhancer' ] );
35 + $ajax->register_ajax_action( 'ai_get_text_to_image', [ $this, 'ajax_ai_get_text_to_image' ] );
36 + $ajax->register_ajax_action( 'ai_get_image_to_image', [ $this, 'ajax_ai_get_image_to_image' ] );
37 + $ajax->register_ajax_action( 'ai_get_image_to_image_mask', [ $this, 'ajax_ai_get_image_to_image_mask' ] );
38 + $ajax->register_ajax_action( 'ai_get_image_to_image_outpainting', [ $this, 'ajax_ai_get_image_to_image_outpainting' ] );
39 + $ajax->register_ajax_action( 'ai_get_image_to_image_upscale', [ $this, 'ajax_ai_get_image_to_image_upscale' ] );
40 + $ajax->register_ajax_action( 'ai_upload_image', [ $this, 'ajax_ai_upload_image' ] );
89 41 } );
90 42
91 43 add_action( 'elementor/editor/before_enqueue_scripts', function() {
92 - $this->enqueue_main_script();
93 - $this->enqueue_layout_script();
94 - } );
95 -
96 - add_action( 'elementor/editor/after_enqueue_styles', function() {
97 - wp_enqueue_style(
98 - 'elementor-ai-editor',
99 - $this->get_css_assets_url( 'modules/ai/editor' ),
100 - [],
101 - ELEMENTOR_VERSION
102 - );
103 - } );
104 -
105 - 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 - );
112 - } );
113 -
114 - if ( is_admin() ) {
115 - 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 - }
125 -
126 - add_action( 'enqueue_block_editor_assets', function() {
127 - wp_enqueue_script( 'elementor-ai-gutenberg',
128 - $this->get_js_assets_url( 'ai-gutenberg' ),
44 + wp_enqueue_script(
45 + 'elementor-ai',
46 + $this->get_js_assets_url( 'ai' ),
129 47 [
130 - 'jquery',
131 - 'elementor-v2-ui',
132 - 'elementor-v2-icons',
133 - 'wp-blocks',
134 - 'wp-element',
135 - 'wp-editor',
136 - 'wp-data',
137 - 'wp-components',
138 - 'wp-compose',
139 - 'wp-i18n',
140 - 'wp-hooks',
141 - 'elementor-ai-media-library',
48 + 'elementor-common',
49 + 'elementor-editor-modules',
50 + 'elementor-editor-document',
51 + 'elementor-packages-ui',
52 + 'elementor-packages-icons',
142 53 ],
143 - ELEMENTOR_VERSION, true );
54 + ELEMENTOR_VERSION,
55 + true
56 + );
144 57
145 58 wp_localize_script(
146 - 'elementor-ai-gutenberg',
59 + 'elementor-ai',
147 60 'ElementorAiConfig',
148 61 [
149 62 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
150 - 'connect_url' => $this->get_ai_connect_url(),
151 63 ]
152 64 );
153 -
154 - wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' );
155 - });
156 -
157 - add_filter( 'elementor/document/save/data', function ( $data ) {
158 - return $this->remove_temporary_containers( $data );
159 65 } );
160 66
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 - ]
67 + add_action( 'elementor/editor/after_enqueue_styles', function() {
68 + wp_enqueue_style(
69 + 'elementor-ai',
70 + $this->get_css_assets_url( 'modules/ai/editor' ),
71 + [],
72 + ELEMENTOR_VERSION
246 73 );
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;
339 - }
340 -
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();
361 - }
362 -
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 - ],
372 - ],
373 - 'fields' => 'ids',
374 - 'numberposts' => 1,
375 - ] );
376 -
377 - return ! empty( $attachments ) ? $attachments[0] : null;
378 - }
379 -
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 - public function enqueue_ai_media_library() {
425 - wp_enqueue_script( 'elementor-ai-media-library',
426 - $this->get_js_assets_url( 'ai-media-library' ),
427 - [
428 - 'jquery',
429 - 'elementor-v2-ui',
430 - 'elementor-v2-icons',
431 - 'media-grid',
432 - ],
433 - ELEMENTOR_VERSION,
434 - true
435 - );
436 -
437 - wp_localize_script(
438 - 'elementor-ai-media-library',
439 - 'ElementorAiConfig',
440 - [
441 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
442 - 'connect_url' => $this->get_ai_connect_url(),
443 - ]
444 - );
445 -
446 - wp_set_script_translations( 'elementor-ai-media-library', 'elementor' );
447 - }
448 -
449 - private function enqueue_main_script() {
450 - wp_enqueue_script(
451 - 'elementor-ai',
452 - $this->get_js_assets_url( 'ai' ),
453 - [
454 - 'react',
455 - 'react-dom',
456 - 'backbone-marionette',
457 - 'elementor-web-cli',
458 - 'wp-date',
459 - 'elementor-common',
460 - 'elementor-editor-modules',
461 - 'elementor-editor-document',
462 - 'elementor-v2-ui',
463 - 'elementor-v2-icons',
464 - ],
465 - ELEMENTOR_VERSION,
466 - true
467 - );
468 -
469 - $config = [
470 - 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
471 - 'connect_url' => $this->get_ai_connect_url(),
472 - ];
473 -
474 - wp_localize_script(
475 - 'elementor-ai',
476 - 'ElementorAiConfig',
477 - $config
478 - );
479 -
480 - wp_set_script_translations( 'elementor-ai', 'elementor' );
481 - }
482 -
483 - private function enqueue_layout_script() {
484 - wp_enqueue_script(
485 - 'elementor-ai-layout',
486 - $this->get_js_assets_url( 'ai-layout' ),
487 - [
488 - 'react',
489 - 'react-dom',
490 - 'backbone-marionette',
491 - 'elementor-common',
492 - 'elementor-web-cli',
493 - 'elementor-editor-modules',
494 - 'elementor-ai',
495 - 'elementor-v2-ui',
496 - 'elementor-v2-icons',
497 - ],
498 - ELEMENTOR_VERSION,
499 - true
500 - );
501 -
502 - wp_set_script_translations( 'elementor-ai-layout', 'elementor' );
503 - }
504 -
505 - private function remove_temporary_containers( $data ) {
506 - if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
507 - return $data;
508 - }
509 -
510 - // If for some reason the document has been saved during an AI Layout session,
511 - // ensure that the temporary containers are removed from the data.
512 - $data['elements'] = array_filter( $data['elements'], function( $element ) {
513 - $is_preview_container = strpos( $element['id'], 'e-ai-preview-container' ) === 0;
514 - $is_screenshot_container = strpos( $element['id'], 'e-ai-screenshot-container' ) === 0;
515 -
516 - return ! $is_preview_container && ! $is_screenshot_container;
517 74 } );
518 -
519 - return $data;
520 75 }
521 76
522 - private function get_ai_connect_url() {
523 - $app = $this->get_ai_app();
524 -
525 - return $app->get_admin_url( 'authorize', [
526 - 'utm_source' => 'ai-popup',
527 - 'utm_campaign' => 'connect-account',
528 - 'utm_medium' => 'wp-dash',
529 - 'source' => 'generic',
530 - ] );
531 - }
532 -
533 77 public function ajax_ai_get_user_information( $data ) {
534 78 $app = $this->get_ai_app();
535 79
536 80 if ( ! $app->is_connected() ) {
@@ -535,9 +79,14 @@
535 79
536 80 if ( ! $app->is_connected() ) {
537 81 return [
538 82 'is_connected' => false,
539 - 'connect_url' => $this->get_ai_connect_url(),
83 + 'connect_url' => $app->get_admin_url( 'authorize', [
84 + 'utm_source' => 'ai-popup',
85 + 'utm_campaign' => 'connect-account',
86 + 'utm_medium' => 'wp-dash',
87 + 'source' => 'generic',
88 + ] ),
540 89 ];
541 90 }
542 91
543 92 $user_usage = wp_parse_args( $app->get_usage(), [
@@ -552,35 +101,8 @@
552 101 'usage' => $user_usage,
553 102 ];
554 103 }
555 104
556 - public function ajax_ai_get_remote_config() {
557 - $app = $this->get_ai_app();
558 -
559 - if ( ! $app->is_connected() ) {
560 - return [];
561 - }
562 -
563 - return $app->get_remote_config();
564 - }
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 - public function verify_upload_permissions( $data ) {
575 - $referer = wp_get_referer();
576 -
577 - if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) {
578 - return;
579 - }
580 - $this->verify_permissions( $data['editor_post_id'] );
581 - }
582 -
583 105 private function verify_permissions( $editor_post_id ) {
584 106 $document = Plugin::$instance->documents->get( $editor_post_id );
585 107
586 108 if ( ! $document ) {
@@ -586,19 +108,15 @@
586 108 if ( ! $document ) {
587 109 throw new \Exception( 'Document not found' );
588 110 }
589 111
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' );
112 + if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
113 + throw new \Exception( 'Access denied' );
596 114 }
597 115 }
598 116
599 117 public function ajax_ai_get_image_prompt_enhancer( $data ) {
600 - $this->verify_upload_permissions( $data );
118 + $this->verify_permissions( $data['editor_post_id'] );
601 119
602 120 $app = $this->get_ai_app();
603 121
604 122 if ( empty( $data['prompt'] ) ) {
@@ -607,12 +125,13 @@
607 125
608 126 if ( ! $app->is_connected() ) {
609 127 throw new \Exception( 'not_connected' );
610 128 }
611 - $request_ids = $this->get_request_ids( $data['payload'] );
612 129
613 - $result = $app->get_image_prompt_enhanced( $data['prompt'], [], $request_ids );
614 - $this->throw_on_error( $result );
130 + $result = $app->get_image_prompt_enhanced( $data['prompt'] );
131 + if ( is_wp_error( $result ) ) {
132 + throw new \Exception( $result->get_error_message() );
133 + }
615 134
616 135 return [
617 136 'text' => $result['text'],
618 137 'response_id' => $result['responseId'],
@@ -624,9 +143,9 @@
624 143 $this->verify_permissions( $data['editor_post_id'] );
625 144
626 145 $app = $this->get_ai_app();
627 146
628 - if ( empty( $data['payload']['prompt'] ) ) {
147 + if ( empty( $data['prompt'] ) ) {
629 148 throw new \Exception( 'Missing prompt' );
630 149 }
631 150
632 151 if ( ! $app->is_connected() ) {
@@ -632,41 +151,13 @@
632 151 if ( ! $app->is_connected() ) {
633 152 throw new \Exception( 'not_connected' );
634 153 }
635 154
636 - $context = $this->get_request_context( $data );
637 -
638 - $request_ids = $this->get_request_ids( $data['payload'] );
639 -
640 - $result = $app->get_completion_text( $data['payload']['prompt'], $context, $request_ids );
641 - $this->throw_on_error( $result );
642 -
643 - return [
644 - 'text' => $result['text'],
645 - 'response_id' => $result['responseId'],
646 - 'usage' => $result['usage'],
647 - ];
648 - }
649 -
650 -
651 - public function ajax_ai_get_excerpt( $data ): array {
652 - $app = $this->get_ai_app();
653 -
654 - if ( empty( $data['payload']['content'] ) ) {
655 - throw new \Exception( 'Missing content' );
155 + $result = $app->get_completion_text( $data['prompt'] );
156 + if ( is_wp_error( $result ) ) {
157 + throw new \Exception( $result->get_error_message() );
656 158 }
657 159
658 - if ( ! $app->is_connected() ) {
659 - throw new \Exception( 'Not connected' );
660 - }
661 -
662 - $context = $this->get_request_context( $data );
663 -
664 - $request_ids = $this->get_request_ids( $data['payload'] );
665 -
666 - $result = $app->get_excerpt( $data['payload']['content'], $context, $request_ids );
667 - $this->throw_on_error( $result );
668 -
669 160 return [
670 161 'text' => $result['text'],
671 162 'response_id' => $result['responseId'],
672 163 'usage' => $result['usage'],
@@ -672,65 +163,22 @@
672 163 'usage' => $result['usage'],
673 164 ];
674 165 }
675 166
676 - public function ajax_ai_get_featured_image( $data ): array {
677 - $this->verify_upload_permissions( $data );
678 -
679 - if ( empty( $data['payload']['prompt'] ) ) {
680 - throw new \Exception( 'Missing prompt' );
681 - }
682 -
683 - $app = $this->get_ai_app();
684 -
685 - if ( ! $app->is_connected() ) {
686 - throw new \Exception( 'not_connected' );
687 - }
688 -
689 - $context = $this->get_request_context( $data );
690 - $request_ids = $this->get_request_ids( $data['payload'] );
691 -
692 - $result = $app->get_featured_image( $data, $context, $request_ids );
693 -
694 - $this->throw_on_error( $result );
695 -
696 - return [
697 - 'images' => $result['images'],
698 - 'response_id' => $result['responseId'],
699 - 'usage' => $result['usage'],
700 - ];
701 - }
702 -
703 - private function get_ai_app(): Ai {
167 + private function get_ai_app() : Ai {
704 168 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
705 169 }
706 170
707 - private function get_request_context( $data ) {
708 - if ( empty( $data['context'] ) ) {
709 - return [];
710 - }
711 -
712 - return $data['context'];
713 - }
714 -
715 - private function get_request_ids( $data ) {
716 - if ( empty( $data['requestIds'] ) ) {
717 - return new \stdClass();
718 - }
719 -
720 - return $data['requestIds'];
721 - }
722 -
723 171 public function ajax_ai_get_edit_text( $data ) {
724 172 $this->verify_permissions( $data['editor_post_id'] );
725 173
726 174 $app = $this->get_ai_app();
727 175
728 - if ( empty( $data['payload']['input'] ) ) {
176 + if ( empty( $data['input'] ) ) {
729 177 throw new \Exception( 'Missing input' );
730 178 }
731 179
732 - if ( empty( $data['payload']['instruction'] ) ) {
180 + if ( empty( $data['instruction'] ) ) {
733 181 throw new \Exception( 'Missing instruction' );
734 182 }
735 183
736 184 if ( ! $app->is_connected() ) {
@@ -736,15 +184,13 @@
736 184 if ( ! $app->is_connected() ) {
737 185 throw new \Exception( 'not_connected' );
738 186 }
739 187
740 - $context = $this->get_request_context( $data );
188 + $result = $app->get_edit_text( $data['input'], $data['instruction'] );
189 + if ( is_wp_error( $result ) ) {
190 + throw new \Exception( $result->get_error_message() );
191 + }
741 192
742 - $request_ids = $this->get_request_ids( $data['payload'] );
743 -
744 - $result = $app->get_edit_text( $data, $context, $request_ids );
745 - $this->throw_on_error( $result );
746 -
747 193 return [
748 194 'text' => $result['text'],
749 195 'response_id' => $result['responseId'],
750 196 'usage' => $result['usage'],
@@ -753,13 +199,13 @@
753 199
754 200 public function ajax_ai_get_custom_code( $data ) {
755 201 $app = $this->get_ai_app();
756 202
757 - if ( empty( $data['payload']['prompt'] ) ) {
203 + if ( empty( $data['prompt'] ) ) {
758 204 throw new \Exception( 'Missing prompt' );
759 205 }
760 206
761 - if ( empty( $data['payload']['language'] ) ) {
207 + if ( empty( $data['language'] ) ) {
762 208 throw new \Exception( 'Missing language' );
763 209 }
764 210
765 211 if ( ! $app->is_connected() ) {
@@ -765,15 +211,13 @@
765 211 if ( ! $app->is_connected() ) {
766 212 throw new \Exception( 'not_connected' );
767 213 }
768 214
769 - $context = $this->get_request_context( $data );
215 + $result = $app->get_custom_code( $data['prompt'], $data['language'] );
216 + if ( is_wp_error( $result ) ) {
217 + throw new \Exception( $result->get_error_message() );
218 + }
770 219
771 - $request_ids = $this->get_request_ids( $data['payload'] );
772 -
773 - $result = $app->get_custom_code( $data, $context, $request_ids );
774 - $this->throw_on_error( $result );
775 -
776 220 return [
777 221 'text' => $result['text'],
778 222 'response_id' => $result['responseId'],
779 223 'usage' => $result['usage'],
@@ -784,17 +228,17 @@
784 228 $this->verify_permissions( $data['editor_post_id'] );
785 229
786 230 $app = $this->get_ai_app();
787 231
788 - if ( empty( $data['payload']['prompt'] ) ) {
232 + if ( empty( $data['prompt'] ) ) {
789 233 throw new \Exception( 'Missing prompt' );
790 234 }
791 235
792 - if ( empty( $data['payload']['html_markup'] ) ) {
236 + if ( empty( $data['html_markup'] ) ) {
793 237 $data['html_markup'] = '';
794 238 }
795 239
796 - if ( empty( $data['payload']['element_id'] ) ) {
240 + if ( empty( $data['element_id'] ) ) {
797 241 throw new \Exception( 'Missing element_id' );
798 242 }
799 243
800 244 if ( ! $app->is_connected() ) {
@@ -800,14 +244,13 @@
800 244 if ( ! $app->is_connected() ) {
801 245 throw new \Exception( 'not_connected' );
802 246 }
803 247
804 - $context = $this->get_request_context( $data );
805 - $request_ids = $this->get_request_ids( $data['payload'] );
248 + $result = $app->get_custom_css( $data['prompt'], $data['html_markup'], $data['element_id'] );
249 + if ( is_wp_error( $result ) ) {
250 + throw new \Exception( $result->get_error_message() );
251 + }
806 252
807 - $result = $app->get_custom_css( $data, $context, $request_ids );
808 - $this->throw_on_error( $result );
809 -
810 253 return [
811 254 'text' => $result['text'],
812 255 'response_id' => $result['responseId'],
813 256 'usage' => $result['usage'],
@@ -840,27 +283,26 @@
840 283 return [];
841 284 }
842 285
843 286 public function ajax_ai_get_text_to_image( $data ) {
844 - $this->verify_upload_permissions( $data );
287 + $this->verify_permissions( $data['editor_post_id'] );
845 288
846 - if ( empty( $data['payload']['prompt'] ) ) {
289 + $app = $this->get_ai_app();
290 +
291 + if ( empty( $data['prompt'] ) ) {
847 292 throw new \Exception( 'Missing prompt' );
848 293 }
849 294
850 - $app = $this->get_ai_app();
851 -
852 295 if ( ! $app->is_connected() ) {
853 296 throw new \Exception( 'not_connected' );
854 297 }
855 298
856 - $context = $this->get_request_context( $data );
857 - $request_ids = $this->get_request_ids( $data['payload'] );
299 + $result = $app->get_text_to_image( $data['prompt'], $data['promptSettings'] );
858 300
859 - $result = $app->get_text_to_image( $data, $context, $request_ids );
301 + if ( is_wp_error( $result ) ) {
302 + throw new \Exception( $result->get_error_message() );
303 + }
860 304
861 - $this->throw_on_error( $result );
862 -
863 305 return [
864 306 'images' => $result['images'],
865 307 'response_id' => $result['responseId'],
866 308 'usage' => $result['usage'],
@@ -867,17 +309,21 @@
867 309 ];
868 310 }
869 311
870 312 public function ajax_ai_get_image_to_image( $data ) {
871 - $this->verify_upload_permissions( $data );
313 + $this->verify_permissions( $data['editor_post_id'] );
872 314
873 315 $app = $this->get_ai_app();
874 316
875 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
317 + if ( empty( $data['prompt'] ) ) {
318 + throw new \Exception( 'Missing prompt' );
319 + }
320 +
321 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
876 322 throw new \Exception( 'Missing Image' );
877 323 }
878 324
879 - if ( empty( $data['payload']['settings'] ) ) {
325 + if ( empty( $data['promptSettings'] ) ) {
880 326 throw new \Exception( 'Missing prompt settings' );
881 327 }
882 328
883 329 if ( ! $app->is_connected() ) {
@@ -883,18 +329,17 @@
883 329 if ( ! $app->is_connected() ) {
884 330 throw new \Exception( 'not_connected' );
885 331 }
886 332
887 - $context = $this->get_request_context( $data );
888 - $request_ids = $this->get_request_ids( $data['payload'] );
889 -
890 333 $result = $app->get_image_to_image( [
891 - 'prompt' => $data['payload']['prompt'],
892 - 'promptSettings' => $data['payload']['settings'],
893 - 'attachment_id' => $data['payload']['image']['id'],
894 - ], $context, $request_ids );
334 + 'prompt' => $data['prompt'],
335 + 'promptSettings' => $data['promptSettings'],
336 + 'attachment_id' => $data['image']['id'],
337 + ] );
895 338
896 - $this->throw_on_error( $result );
339 + if ( is_wp_error( $result ) ) {
340 + throw new \Exception( $result->get_error_message() );
341 + }
897 342
898 343 return [
899 344 'images' => $result['images'],
900 345 'response_id' => $result['responseId'],
@@ -902,17 +347,17 @@
902 347 ];
903 348 }
904 349
905 350 public function ajax_ai_get_image_to_image_upscale( $data ) {
906 - $this->verify_upload_permissions( $data );
351 + $this->verify_permissions( $data['editor_post_id'] );
907 352
908 353 $app = $this->get_ai_app();
909 354
910 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
355 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
911 356 throw new \Exception( 'Missing Image' );
912 357 }
913 358
914 - if ( empty( $data['payload']['promptSettings'] ) ) {
359 + if ( empty( $data['promptSettings'] ) ) {
915 360 throw new \Exception( 'Missing prompt settings' );
916 361 }
917 362
918 363 if ( ! $app->is_connected() ) {
@@ -918,52 +363,17 @@
918 363 if ( ! $app->is_connected() ) {
919 364 throw new \Exception( 'not_connected' );
920 365 }
921 366
922 - $context = $this->get_request_context( $data );
923 - $request_ids = $this->get_request_ids( $data['payload'] );
924 -
925 367 $result = $app->get_image_to_image_upscale( [
926 - 'promptSettings' => $data['payload']['promptSettings'],
927 - 'attachment_id' => $data['payload']['image']['id'],
928 - ], $context, $request_ids );
368 + 'promptSettings' => $data['promptSettings'],
369 + 'attachment_id' => $data['image']['id'],
370 + ] );
929 371
930 - $this->throw_on_error( $result );
931 -
932 - return [
933 - 'images' => $result['images'],
934 - 'response_id' => $result['responseId'],
935 - 'usage' => $result['usage'],
936 - ];
937 - }
938 -
939 - public function ajax_ai_get_image_to_image_replace_background( $data ) {
940 - $this->verify_upload_permissions( $data );
941 -
942 - $app = $this->get_ai_app();
943 -
944 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
945 - throw new \Exception( 'Missing Image' );
372 + if ( is_wp_error( $result ) ) {
373 + throw new \Exception( $result->get_error_message() );
946 374 }
947 375
948 - if ( empty( $data['payload']['prompt'] ) ) {
949 - throw new \Exception( 'Prompt Missing' );
950 - }
951 -
952 - if ( ! $app->is_connected() ) {
953 - throw new \Exception( 'not_connected' );
954 - }
955 -
956 - $context = $this->get_request_context( $data );
957 - $request_ids = $this->get_request_ids( $data['payload'] );
958 -
959 - $result = $app->get_image_to_image_replace_background( [
960 - 'attachment_id' => $data['payload']['image']['id'],
961 - 'prompt' => $data['payload']['prompt'],
962 - ], $context, $request_ids );
963 -
964 - $this->throw_on_error( $result );
965 -
966 376 return [
967 377 'images' => $result['images'],
968 378 'response_id' => $result['responseId'],
969 379 'usage' => $result['usage'],
@@ -969,105 +379,44 @@
969 379 'usage' => $result['usage'],
970 380 ];
971 381 }
972 382
973 - public function ajax_ai_get_image_to_image_remove_background( $data ) {
974 - $this->verify_upload_permissions( $data );
975 -
976 - $app = $this->get_ai_app();
977 -
978 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
979 - throw new \Exception( 'Missing Image' );
980 - }
981 -
982 - if ( ! $app->is_connected() ) {
983 - throw new \Exception( 'not_connected' );
984 - }
985 -
986 - $context = $this->get_request_context( $data );
987 - $request_ids = $this->get_request_ids( $data['payload'] );
988 - $result = $app->get_image_to_image_remove_background( [
989 - 'attachment_id' => $data['payload']['image']['id'],
990 - ], $context, $request_ids );
991 -
992 - $this->throw_on_error( $result );
993 -
994 - return [
995 - 'images' => $result['images'],
996 - 'response_id' => $result['responseId'],
997 - 'usage' => $result['usage'],
998 - ];
999 - }
1000 -
1001 383 public function ajax_ai_get_image_to_image_mask( $data ) {
1002 - $this->verify_upload_permissions( $data );
384 + $this->verify_permissions( $data['editor_post_id'] );
1003 385
1004 386 $app = $this->get_ai_app();
1005 387
1006 - if ( empty( $data['payload']['prompt'] ) ) {
388 + if ( empty( $data['prompt'] ) ) {
1007 389 throw new \Exception( 'Missing prompt' );
1008 390 }
1009 391
1010 - if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) {
392 + if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
1011 393 throw new \Exception( 'Missing Image' );
1012 394 }
1013 395
1014 - if ( empty( $data['payload']['settings'] ) ) {
396 + if ( empty( $data['promptSettings'] ) ) {
1015 397 throw new \Exception( 'Missing prompt settings' );
1016 398 }
1017 399
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 400 if ( ! $app->is_connected() ) {
1053 401 throw new \Exception( 'not_connected' );
1054 402 }
1055 403
1056 - if ( empty( $data['payload']['mask'] ) ) {
404 + if ( empty( $data['mask'] ) ) {
1057 405 throw new \Exception( 'Missing Mask' );
1058 406 }
1059 407
1060 - $context = $this->get_request_context( $data );
1061 - $request_ids = $this->get_request_ids( $data['payload'] );
408 + $result = $app->get_image_to_image_mask( [
409 + 'prompt' => $data['prompt'],
410 + 'promptSettings' => $data['promptSettings'],
411 + 'attachment_id' => $data['image']['id'],
412 + 'mask' => $data['mask'],
413 + ] );
1062 414
1063 - $result = $app->get_image_to_image_mask_cleanup( [
1064 - 'attachment_id' => $data['payload']['image']['id'],
1065 - 'mask' => $data['payload']['mask'],
1066 - ], $context, $request_ids );
415 + if ( is_wp_error( $result ) ) {
416 + throw new \Exception( $result->get_error_message() );
417 + }
1067 418
1068 - $this->throw_on_error( $result );
1069 -
1070 419 return [
1071 420 'images' => $result['images'],
1072 421 'response_id' => $result['responseId'],
1073 422 'usage' => $result['usage'],
@@ -1072,32 +421,33 @@
1072 421 'response_id' => $result['responseId'],
1073 422 'usage' => $result['usage'],
1074 423 ];
1075 424 }
1076 -
1077 425 public function ajax_ai_get_image_to_image_outpainting( $data ) {
1078 - $this->verify_upload_permissions( $data );
426 + $this->verify_permissions( $data['editor_post_id'] );
1079 427
1080 428 $app = $this->get_ai_app();
1081 429
430 + if ( empty( $data['prompt'] ) ) {
431 + throw new \Exception( 'Missing prompt' );
432 + }
433 +
1082 434 if ( ! $app->is_connected() ) {
1083 435 throw new \Exception( 'not_connected' );
1084 436 }
1085 437
1086 - if ( empty( $data['payload']['mask'] ) ) {
438 + if ( empty( $data['mask'] ) ) {
1087 439 throw new \Exception( 'Missing Expended Image' );
1088 440 }
1089 441
1090 - $context = $this->get_request_context( $data );
1091 - $request_ids = $this->get_request_ids( $data['payload'] );
1092 442 $result = $app->get_image_to_image_out_painting( [
1093 - 'size' => $data['payload']['size'],
1094 - 'position' => $data['payload']['position'],
1095 - 'mask' => $data['payload']['mask'],
1096 - 'image_base64' => $data['payload']['image_base64'],
1097 - ], $context, $request_ids );
443 + 'prompt' => $data['prompt'],
444 + 'mask' => $data['mask'],
445 + ] );
1098 446
1099 - $this->throw_on_error( $result );
447 + if ( is_wp_error( $result ) ) {
448 + throw new \Exception( $result->get_error_message() );
449 + }
1100 450
1101 451 return [
1102 452 'images' => $result['images'],
1103 453 'response_id' => $result['responseId'],
@@ -1105,14 +455,11 @@
1105 455 ];
1106 456 }
1107 457
1108 458 public function ajax_ai_upload_image( $data ) {
1109 - $this->verify_upload_permissions( $data );
1110 -
1111 459 if ( empty( $data['image'] ) ) {
1112 460 throw new \Exception( 'Missing image data' );
1113 461 }
1114 -
1115 462 $image = $data['image'];
1116 463
1117 464 if ( empty( $image['image_url'] ) ) {
1118 465 throw new \Exception( 'Missing image_url' );
@@ -1120,15 +467,16 @@
1120 467
1121 468 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
1122 469
1123 470 if ( is_wp_error( $image_data ) ) {
1124 - throw new \Exception( esc_html( $image_data->get_error_message() ) );
471 + throw new \Exception( $image_data->get_error_message() );
1125 472 }
1126 473
1127 - if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
1128 - $app = $this->get_ai_app();
1129 - $app->set_used_gallery_image( $image['id'] );
1130 - }
474 + /*if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
475 + // todo: uncomment once endpoint is ready send checkpoint
476 + // $app = $this->get_ai_app();
477 + // $app->set_used_gallery_image( $image['id'] );
478 + }*/
1131 479
1132 480 return [
1133 481 'image' => array_merge( $image_data, $data ),
1134 482 ];
@@ -1133,197 +481,15 @@
1133 481 'image' => array_merge( $image_data, $data ),
1134 482 ];
1135 483 }
1136 484
1137 - public function ajax_ai_generate_layout( $data ) {
1138 - $this->verify_permissions( $data['editor_post_id'] );
1139 -
1140 - $app = $this->get_ai_app();
1141 -
1142 - if ( empty( $data['prompt'] ) && empty( $data['attachments'] ) ) {
1143 - throw new \Exception( 'Missing prompt / attachments' );
1144 - }
1145 -
1146 - if ( ! $app->is_connected() ) {
1147 - throw new \Exception( 'not_connected' );
1148 - }
1149 -
1150 - $result = $app->generate_layout(
1151 - $data,
1152 - $this->prepare_generate_layout_context( $data )
1153 - );
1154 -
1155 - if ( is_wp_error( $result ) ) {
1156 - $message = $result->get_error_message();
1157 -
1158 - if ( is_array( $message ) ) {
1159 - $message = implode( ', ', $message );
1160 - throw new \Exception( esc_html( $message ) );
1161 - }
1162 -
1163 - $this->throw_on_error( $result );
1164 - }
1165 -
1166 - $elements = $result['text']['elements'] ?? [];
1167 - $base_template_id = $result['baseTemplateId'] ?? null;
1168 - $template_type = $result['templateType'] ?? null;
1169 -
1170 - if ( empty( $elements ) || ! is_array( $elements ) ) {
1171 - throw new \Exception( 'unknown_error' );
1172 - }
1173 -
1174 - if ( 1 === count( $elements ) ) {
1175 - $template = $elements[0];
1176 - } else {
1177 - $template = [
1178 - 'elType' => 'container',
1179 - 'elements' => $elements,
1180 - 'settings' => [
1181 - 'content_width' => 'full',
1182 - 'flex_gap' => [
1183 - 'column' => '0',
1184 - 'row' => '0',
1185 - 'unit' => 'px',
1186 - ],
1187 - 'padding' => [
1188 - 'unit' => 'px',
1189 - 'top' => '0',
1190 - 'right' => '0',
1191 - 'bottom' => '0',
1192 - 'left' => '0',
1193 - 'isLinked' => true,
1194 - ],
1195 - ],
1196 - ];
1197 - }
1198 -
1199 - return [
1200 - 'all' => [],
1201 - 'text' => $template,
1202 - 'response_id' => $result['responseId'],
1203 - 'usage' => $result['usage'],
1204 - 'base_template_id' => $base_template_id,
1205 - 'template_type' => $template_type,
1206 - ];
1207 - }
1208 -
1209 - public function ajax_ai_get_layout_prompt_enhancer( $data ) {
1210 - $this->verify_permissions( $data['editor_post_id'] );
1211 -
1212 - $app = $this->get_ai_app();
1213 -
1214 - if ( empty( $data['prompt'] ) ) {
1215 - throw new \Exception( 'Missing prompt' );
1216 - }
1217 -
1218 - if ( ! $app->is_connected() ) {
1219 - throw new \Exception( 'not_connected' );
1220 - }
1221 -
1222 - $result = $app->get_layout_prompt_enhanced(
1223 - $data['prompt'],
1224 - $data['enhance_type'],
1225 - $this->prepare_generate_layout_context( $data )
1226 - );
1227 -
1228 - $this->throw_on_error( $result );
1229 -
1230 - return [
1231 - 'text' => $result['text'] ?? $data['prompt'],
1232 - 'response_id' => $result['responseId'] ?? '',
1233 - 'usage' => $result['usage'] ?? '',
1234 - ];
1235 - }
1236 -
1237 - private function prepare_generate_layout_context( $data ) {
1238 - $request_context = $this->get_request_context( $data );
1239 - $kit = Plugin::$instance->kits_manager->get_active_kit();
1240 -
1241 - if ( ! $kit ) {
1242 - return $request_context;
1243 - }
1244 -
1245 - $kits_data = Collection::make( $kit->get_data()['settings'] ?? [] );
1246 -
1247 - $colors = $kits_data
1248 - ->filter( function ( $_, $key ) {
1249 - return in_array( $key, [ 'system_colors', 'custom_colors' ], true );
1250 - } )
1251 - ->flatten()
1252 - ->filter( function ( $val ) {
1253 - return ! empty( $val['_id'] );
1254 - } )
1255 - ->map( function ( $val ) {
1256 - return [
1257 - 'id' => $val['_id'],
1258 - 'label' => $val['title'] ?? null,
1259 - 'value' => $val['color'] ?? null,
1260 - ];
1261 - } );
1262 -
1263 - $typography = $kits_data
1264 - ->filter( function ( $_, $key ) {
1265 - return in_array( $key, [ 'system_typography', 'custom_typography' ], true );
1266 - } )
1267 - ->flatten()
1268 - ->filter( function ( $val ) {
1269 - return ! empty( $val['_id'] );
1270 - } )
1271 - ->map( function ( $val ) {
1272 - $font_size = null;
1273 -
1274 - if ( isset(
1275 - $val['typography_font_size']['unit'],
1276 - $val['typography_font_size']['size']
1277 - ) ) {
1278 - $prop = $val['typography_font_size'];
1279 -
1280 - $font_size = 'custom' === $prop['unit']
1281 - ? $prop['size']
1282 - : $prop['size'] . $prop['unit'];
1283 - }
1284 -
1285 - return [
1286 - 'id' => $val['_id'],
1287 - 'label' => $val['title'] ?? null,
1288 - 'value' => [
1289 - 'family' => $val['typography_font_family'] ?? null,
1290 - 'weight' => $val['typography_font_weight'] ?? null,
1291 - 'style' => $val['typography_font_style'] ?? null,
1292 - 'size' => $font_size,
1293 - ],
1294 - ];
1295 - } );
1296 -
1297 - $request_context['globals'] = [
1298 - 'colors' => $colors->all(),
1299 - 'typography' => $typography->all(),
1300 - ];
1301 -
1302 - return $request_context;
1303 - }
1304 -
1305 485 private function upload_image( $image_url, $image_title, $parent_post_id = 0 ) {
1306 486 if ( ! current_user_can( 'upload_files' ) ) {
1307 487 throw new \Exception( 'Not Allowed to Upload images' );
1308 488 }
1309 489
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 490 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
1321 491
1322 - if ( is_wp_error( $attachment_id ) ) {
1323 - return new \WP_Error( 'upload_error', $attachment_id->get_error_message() );
1324 - }
1325 -
1326 492 if ( ! empty( $attachment_id['error'] ) ) {
1327 493 return new \WP_Error( 'upload_error', $attachment_id['error'] );
1328 494 }
1329 495
@@ -1328,221 +494,10 @@
1328 494 }
1329 495
1330 496 return [
1331 497 'id' => $attachment_id,
1332 - 'url' => esc_url( wp_get_attachment_image_url( $attachment_id, 'full' ) ),
1333 - 'alt' => esc_attr( $image_title ),
498 + 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
499 + 'alt' => $image_title,
1334 500 'source' => 'library',
1335 501 ];
1336 - }
1337 -
1338 - public function ajax_ai_get_history( $data ): array {
1339 - $type = $data['type'] ?? self::HISTORY_TYPE_ALL;
1340 -
1341 - if ( ! in_array( $type, self::VALID_HISTORY_TYPES, true ) ) {
1342 - throw new \Exception( 'Invalid history type' );
1343 - }
1344 -
1345 - $page = sanitize_text_field( $data['page'] ?? 1 );
1346 - $limit = sanitize_text_field( $data['limit'] ?? 10 );
1347 -
1348 - $app = $this->get_ai_app();
1349 -
1350 - if ( ! $app->is_connected() ) {
1351 - throw new \Exception( 'not_connected' );
1352 - }
1353 -
1354 - $context = $this->get_request_context( $data );
1355 -
1356 - $result = $app->get_history_by_type( $type, $page, $limit, $context );
1357 -
1358 - if ( is_wp_error( $result ) ) {
1359 - throw new \Exception( esc_html( $result->get_error_message() ) );
1360 - }
1361 -
1362 - return $result;
1363 - }
1364 -
1365 - public function ajax_ai_delete_history_item( $data ): array {
1366 - if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) {
1367 - throw new \Exception( 'Missing id parameter' );
1368 - }
1369 -
1370 - $app = $this->get_ai_app();
1371 -
1372 - if ( ! $app->is_connected() ) {
1373 - throw new \Exception( 'not_connected' );
1374 - }
1375 -
1376 - $context = $this->get_request_context( $data );
1377 -
1378 - $result = $app->delete_history_item( $data['id'], $context );
1379 -
1380 - if ( is_wp_error( $result ) ) {
1381 - throw new \Exception( esc_html( $result->get_error_message() ) );
1382 - }
1383 -
1384 - return [];
1385 - }
1386 -
1387 - public function ajax_ai_toggle_favorite_history_item( $data ): array {
1388 - if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) {
1389 - throw new \Exception( 'Missing id parameter' );
1390 - }
1391 -
1392 - $app = $this->get_ai_app();
1393 -
1394 - if ( ! $app->is_connected() ) {
1395 - throw new \Exception( 'not_connected' );
1396 - }
1397 -
1398 - $context = $this->get_request_context( $data );
1399 -
1400 - $result = $app->toggle_favorite_history_item( $data['id'], $context );
1401 -
1402 - if ( is_wp_error( $result ) ) {
1403 - throw new \Exception( esc_html( $result->get_error_message() ) );
1404 - }
1405 -
1406 - return [];
1407 - }
1408 -
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 - /**
1479 - * @param mixed $result
1480 - */
1481 - private function throw_on_error( $result ): void {
1482 - if ( is_wp_error( $result ) ) {
1483 - wp_send_json_error( [
1484 - 'message' => esc_html( $result->get_error_message() ),
1485 - 'extra_data' => $result->get_error_data(),
1486 - ] );
1487 - }
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 502 }
1548 503 }