PluginProbe
Elementor Website Builder – more than just a page builder / 3.16.0-dev1
Elementor Website Builder – more than just a page builder v3.16.0-dev1
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
elementor / modules / ai / module.php

module.php in Elementor Website Builder – more than just a page builder 3.16.0-dev1, at modules/ai/module.php

606 lines 15.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Modules\Ai;
3
4 use Elementor\Core\Base\Module as BaseModule;
5 use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
6 use Elementor\Modules\Ai\Connect\Ai;
7 use Elementor\Plugin;
8 use Elementor\User;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13 class Module extends BaseModule {
14
15 public function get_name() {
16 return 'ai';
17 }
18
19 public function __construct() {
20 parent::__construct();
21
22 add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) {
23 $connect_module->register_app( 'ai', Ai::get_class_name() );
24 } );
25
26 add_action( 'elementor/ajax/register_actions', function( $ajax ) {
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_get_image_to_image_remove_background', [ $this, 'ajax_ai_get_image_to_image_remove_background' ] );
41 $ajax->register_ajax_action( 'ai_get_image_to_image_replace_background', [ $this, 'ajax_ai_get_image_to_image_replace_background' ] );
42 $ajax->register_ajax_action( 'ai_upload_image', [ $this, 'ajax_ai_upload_image' ] );
43 } );
44
45 add_action( 'elementor/editor/before_enqueue_scripts', function() {
46 wp_enqueue_script(
47 'elementor-ai',
48 $this->get_js_assets_url( 'ai' ),
49 [
50 'elementor-common',
51 'elementor-editor-modules',
52 'elementor-editor-document',
53 'elementor-v2-ui',
54 'elementor-v2-icons',
55 ],
56 ELEMENTOR_VERSION,
57 true
58 );
59
60 wp_localize_script(
61 'elementor-ai',
62 'ElementorAiConfig',
63 [
64 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
65 'connect_url' => $this->get_ai_connect_url(),
66 ]
67 );
68
69 wp_set_script_translations( 'elementor-ai', 'elementor' );
70 } );
71
72 add_action( 'elementor/editor/after_enqueue_styles', function() {
73 wp_enqueue_style(
74 'elementor-ai',
75 $this->get_css_assets_url( 'modules/ai/editor' ),
76 [],
77 ELEMENTOR_VERSION
78 );
79 } );
80 }
81
82 private function get_ai_connect_url() {
83 $app = $this->get_ai_app();
84
85 return $app->get_admin_url( 'authorize', [
86 'utm_source' => 'ai-popup',
87 'utm_campaign' => 'connect-account',
88 'utm_medium' => 'wp-dash',
89 'source' => 'generic',
90 ] );
91 }
92
93 public function ajax_ai_get_user_information( $data ) {
94 $app = $this->get_ai_app();
95
96 if ( ! $app->is_connected() ) {
97 return [
98 'is_connected' => false,
99 'connect_url' => $this->get_ai_connect_url(),
100 ];
101 }
102
103 $user_usage = wp_parse_args( $app->get_usage(), [
104 'hasAiSubscription' => false,
105 'usedQuota' => 0,
106 'quota' => 100,
107 ] );
108
109 return [
110 'is_connected' => true,
111 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
112 'usage' => $user_usage,
113 ];
114 }
115
116 private function verify_permissions( $editor_post_id ) {
117 $document = Plugin::$instance->documents->get( $editor_post_id );
118
119 if ( ! $document ) {
120 throw new \Exception( 'Document not found' );
121 }
122
123 if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
124 throw new \Exception( 'Access denied' );
125 }
126 }
127
128 public function ajax_ai_get_image_prompt_enhancer( $data ) {
129 $this->verify_permissions( $data['editor_post_id'] );
130
131 $app = $this->get_ai_app();
132
133 if ( empty( $data['prompt'] ) ) {
134 throw new \Exception( 'Missing prompt' );
135 }
136
137 if ( ! $app->is_connected() ) {
138 throw new \Exception( 'not_connected' );
139 }
140
141 $result = $app->get_image_prompt_enhanced( $data['prompt'] );
142 if ( is_wp_error( $result ) ) {
143 throw new \Exception( $result->get_error_message() );
144 }
145
146 return [
147 'text' => $result['text'],
148 'response_id' => $result['responseId'],
149 'usage' => $result['usage'],
150 ];
151 }
152
153 public function ajax_ai_get_completion_text( $data ) {
154 $this->verify_permissions( $data['editor_post_id'] );
155
156 $app = $this->get_ai_app();
157
158 if ( empty( $data['prompt'] ) ) {
159 throw new \Exception( 'Missing prompt' );
160 }
161
162 if ( ! $app->is_connected() ) {
163 throw new \Exception( 'not_connected' );
164 }
165
166 $context = $this->get_request_context( $data );
167
168 $result = $app->get_completion_text( $data['prompt'], $context );
169 if ( is_wp_error( $result ) ) {
170 throw new \Exception( $result->get_error_message() );
171 }
172
173 return [
174 'text' => $result['text'],
175 'response_id' => $result['responseId'],
176 'usage' => $result['usage'],
177 ];
178 }
179
180 private function get_ai_app() : Ai {
181 return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' );
182 }
183
184 private function get_request_context( $data ) {
185 if ( empty( $data['context'] ) ) {
186 return [];
187 }
188
189 return $data['context'];
190 }
191
192 public function ajax_ai_get_edit_text( $data ) {
193 $this->verify_permissions( $data['editor_post_id'] );
194
195 $app = $this->get_ai_app();
196
197 if ( empty( $data['input'] ) ) {
198 throw new \Exception( 'Missing input' );
199 }
200
201 if ( empty( $data['instruction'] ) ) {
202 throw new \Exception( 'Missing instruction' );
203 }
204
205 if ( ! $app->is_connected() ) {
206 throw new \Exception( 'not_connected' );
207 }
208
209 $context = $this->get_request_context( $data );
210
211 $result = $app->get_edit_text( $data['input'], $data['instruction'], $context );
212 if ( is_wp_error( $result ) ) {
213 throw new \Exception( $result->get_error_message() );
214 }
215
216 return [
217 'text' => $result['text'],
218 'response_id' => $result['responseId'],
219 'usage' => $result['usage'],
220 ];
221 }
222
223 public function ajax_ai_get_custom_code( $data ) {
224 $app = $this->get_ai_app();
225
226 if ( empty( $data['prompt'] ) ) {
227 throw new \Exception( 'Missing prompt' );
228 }
229
230 if ( empty( $data['language'] ) ) {
231 throw new \Exception( 'Missing language' );
232 }
233
234 if ( ! $app->is_connected() ) {
235 throw new \Exception( 'not_connected' );
236 }
237
238 $context = $this->get_request_context( $data );
239
240 $result = $app->get_custom_code( $data['prompt'], $data['language'], $context );
241 if ( is_wp_error( $result ) ) {
242 throw new \Exception( $result->get_error_message() );
243 }
244
245 return [
246 'text' => $result['text'],
247 'response_id' => $result['responseId'],
248 'usage' => $result['usage'],
249 ];
250 }
251
252 public function ajax_ai_get_custom_css( $data ) {
253 $this->verify_permissions( $data['editor_post_id'] );
254
255 $app = $this->get_ai_app();
256
257 if ( empty( $data['prompt'] ) ) {
258 throw new \Exception( 'Missing prompt' );
259 }
260
261 if ( empty( $data['html_markup'] ) ) {
262 $data['html_markup'] = '';
263 }
264
265 if ( empty( $data['element_id'] ) ) {
266 throw new \Exception( 'Missing element_id' );
267 }
268
269 if ( ! $app->is_connected() ) {
270 throw new \Exception( 'not_connected' );
271 }
272
273 $context = $this->get_request_context( $data );
274
275 $result = $app->get_custom_css( $data['prompt'], $data['html_markup'], $data['element_id'], $context );
276 if ( is_wp_error( $result ) ) {
277 throw new \Exception( $result->get_error_message() );
278 }
279
280 return [
281 'text' => $result['text'],
282 'response_id' => $result['responseId'],
283 'usage' => $result['usage'],
284 ];
285 }
286
287 public function ajax_ai_set_get_started( $data ) {
288 $app = $this->get_ai_app();
289
290 User::set_introduction_viewed( [
291 'introductionKey' => 'ai_get_started',
292 ] );
293
294 return $app->set_get_started();
295 }
296
297 public function ajax_ai_set_status_feedback( $data ) {
298 if ( empty( $data['response_id'] ) ) {
299 throw new \Exception( 'Missing response_id' );
300 }
301
302 $app = $this->get_ai_app();
303
304 if ( ! $app->is_connected() ) {
305 throw new \Exception( 'not_connected' );
306 }
307
308 $app->set_status_feedback( $data['response_id'] );
309
310 return [];
311 }
312
313 public function ajax_ai_get_text_to_image( $data ) {
314 $this->verify_permissions( $data['editor_post_id'] );
315
316 if ( empty( $data['prompt'] ) ) {
317 throw new \Exception( 'Missing prompt' );
318 }
319
320 $app = $this->get_ai_app();
321
322 if ( ! $app->is_connected() ) {
323 throw new \Exception( 'not_connected' );
324 }
325
326 $context = $this->get_request_context( $data );
327
328 $result = $app->get_text_to_image( $data['prompt'], $data['promptSettings'], $context );
329
330 if ( is_wp_error( $result ) ) {
331 throw new \Exception( $result->get_error_message() );
332 }
333
334 return [
335 'images' => $result['images'],
336 'response_id' => $result['responseId'],
337 'usage' => $result['usage'],
338 ];
339 }
340
341 public function ajax_ai_get_image_to_image( $data ) {
342 $this->verify_permissions( $data['editor_post_id'] );
343
344 $app = $this->get_ai_app();
345
346 if ( empty( $data['prompt'] ) ) {
347 throw new \Exception( 'Missing prompt' );
348 }
349
350 if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
351 throw new \Exception( 'Missing Image' );
352 }
353
354 if ( empty( $data['promptSettings'] ) ) {
355 throw new \Exception( 'Missing prompt settings' );
356 }
357
358 if ( ! $app->is_connected() ) {
359 throw new \Exception( 'not_connected' );
360 }
361
362 $context = $this->get_request_context( $data );
363
364 $result = $app->get_image_to_image( [
365 'prompt' => $data['prompt'],
366 'promptSettings' => $data['promptSettings'],
367 'attachment_id' => $data['image']['id'],
368 ], $context );
369
370 if ( is_wp_error( $result ) ) {
371 throw new \Exception( $result->get_error_message() );
372 }
373
374 return [
375 'images' => $result['images'],
376 'response_id' => $result['responseId'],
377 'usage' => $result['usage'],
378 ];
379 }
380
381 public function ajax_ai_get_image_to_image_upscale( $data ) {
382 $this->verify_permissions( $data['editor_post_id'] );
383
384 $app = $this->get_ai_app();
385
386 if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
387 throw new \Exception( 'Missing Image' );
388 }
389
390 if ( empty( $data['promptSettings'] ) ) {
391 throw new \Exception( 'Missing prompt settings' );
392 }
393
394 if ( ! $app->is_connected() ) {
395 throw new \Exception( 'not_connected' );
396 }
397
398 $context = $this->get_request_context( $data );
399
400 $result = $app->get_image_to_image_upscale( [
401 'promptSettings' => $data['promptSettings'],
402 'attachment_id' => $data['image']['id'],
403 ], $context );
404
405 if ( is_wp_error( $result ) ) {
406 throw new \Exception( $result->get_error_message() );
407 }
408
409 return [
410 'images' => $result['images'],
411 'response_id' => $result['responseId'],
412 'usage' => $result['usage'],
413 ];
414 }
415
416 public function ajax_ai_get_image_to_image_replace_background( $data ) {
417 $this->verify_permissions( $data['editor_post_id'] );
418
419 $app = $this->get_ai_app();
420
421 if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
422 throw new \Exception( 'Missing Image' );
423 }
424
425 if ( empty( $data['prompt'] ) ) {
426 throw new \Exception( 'Prompt Missing' );
427 }
428
429 if ( ! $app->is_connected() ) {
430 throw new \Exception( 'not_connected' );
431 }
432
433 $context = $this->get_request_context( $data );
434
435 $result = $app->get_image_to_image_replace_background( [
436 'attachment_id' => $data['image']['id'],
437 'prompt' => $data['prompt'],
438 ], $context );
439
440 if ( is_wp_error( $result ) ) {
441 throw new \Exception( $result->get_error_message() );
442 }
443
444 return [
445 'images' => $result['images'],
446 'response_id' => $result['responseId'],
447 'usage' => $result['usage'],
448 ];
449 }
450
451 public function ajax_ai_get_image_to_image_remove_background( $data ) {
452 $this->verify_permissions( $data['editor_post_id'] );
453
454 $app = $this->get_ai_app();
455
456 if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
457 throw new \Exception( 'Missing Image' );
458 }
459
460 if ( ! $app->is_connected() ) {
461 throw new \Exception( 'not_connected' );
462 }
463
464 $context = $this->get_request_context( $data );
465
466 $result = $app->get_image_to_image_remove_background( [
467 'attachment_id' => $data['image']['id'],
468 ], $context );
469
470 if ( is_wp_error( $result ) ) {
471 throw new \Exception( $result->get_error_message() );
472 }
473
474 return [
475 'images' => $result['images'],
476 'response_id' => $result['responseId'],
477 'usage' => $result['usage'],
478 ];
479 }
480
481 public function ajax_ai_get_image_to_image_mask( $data ) {
482 $this->verify_permissions( $data['editor_post_id'] );
483
484 $app = $this->get_ai_app();
485
486 if ( empty( $data['prompt'] ) ) {
487 throw new \Exception( 'Missing prompt' );
488 }
489
490 if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
491 throw new \Exception( 'Missing Image' );
492 }
493
494 if ( empty( $data['promptSettings'] ) ) {
495 throw new \Exception( 'Missing prompt settings' );
496 }
497
498 if ( ! $app->is_connected() ) {
499 throw new \Exception( 'not_connected' );
500 }
501
502 if ( empty( $data['mask'] ) ) {
503 throw new \Exception( 'Missing Mask' );
504 }
505
506 $context = $this->get_request_context( $data );
507
508 $result = $app->get_image_to_image_mask( [
509 'prompt' => $data['prompt'],
510 'promptSettings' => $data['promptSettings'],
511 'attachment_id' => $data['image']['id'],
512 'mask' => $data['mask'],
513 ], $context );
514
515 if ( is_wp_error( $result ) ) {
516 throw new \Exception( $result->get_error_message() );
517 }
518
519 return [
520 'images' => $result['images'],
521 'response_id' => $result['responseId'],
522 'usage' => $result['usage'],
523 ];
524 }
525 public function ajax_ai_get_image_to_image_outpainting( $data ) {
526 $this->verify_permissions( $data['editor_post_id'] );
527
528 $app = $this->get_ai_app();
529
530 if ( empty( $data['prompt'] ) ) {
531 throw new \Exception( 'Missing prompt' );
532 }
533
534 if ( ! $app->is_connected() ) {
535 throw new \Exception( 'not_connected' );
536 }
537
538 if ( empty( $data['mask'] ) ) {
539 throw new \Exception( 'Missing Expended Image' );
540 }
541
542 $context = $this->get_request_context( $data );
543
544 $result = $app->get_image_to_image_out_painting( [
545 'prompt' => $data['prompt'],
546 'mask' => $data['mask'],
547 ], $context );
548
549 if ( is_wp_error( $result ) ) {
550 throw new \Exception( $result->get_error_message() );
551 }
552
553 return [
554 'images' => $result['images'],
555 'response_id' => $result['responseId'],
556 'usage' => $result['usage'],
557 ];
558 }
559
560 public function ajax_ai_upload_image( $data ) {
561 if ( empty( $data['image'] ) ) {
562 throw new \Exception( 'Missing image data' );
563 }
564
565 $image = $data['image'];
566
567 if ( empty( $image['image_url'] ) ) {
568 throw new \Exception( 'Missing image_url' );
569 }
570
571 $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] );
572
573 if ( is_wp_error( $image_data ) ) {
574 throw new \Exception( $image_data->get_error_message() );
575 }
576
577 if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) {
578 $app = $this->get_ai_app();
579 $app->set_used_gallery_image( $image['id'] );
580 }
581
582 return [
583 'image' => array_merge( $image_data, $data ),
584 ];
585 }
586
587 private function upload_image( $image_url, $image_title, $parent_post_id = 0 ) {
588 if ( ! current_user_can( 'upload_files' ) ) {
589 throw new \Exception( 'Not Allowed to Upload images' );
590 }
591
592 $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' );
593
594 if ( ! empty( $attachment_id['error'] ) ) {
595 return new \WP_Error( 'upload_error', $attachment_id['error'] );
596 }
597
598 return [
599 'id' => $attachment_id,
600 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ),
601 'alt' => $image_title,
602 'source' => 'library',
603 ];
604 }
605 }
606