PluginProbe
Elementor Website Builder – more than just a page builder / 3.18.2
Elementor Website Builder – more than just a page builder v3.18.2
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 / connect / ai.php

ai.php in Elementor Website Builder – more than just a page builder 3.18.2, at modules/ai/connect/ai.php

636 lines 15.2 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\Connect;
3
4 use Elementor\Core\Common\Modules\Connect\Apps\Library;
5 use Elementor\Modules\Ai\Module;
6 use Elementor\Utils as ElementorUtils;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly
10 }
11
12 class Ai extends Library {
13 const API_URL = 'https://my.elementor.com/api/v2/ai/';
14
15 const STYLE_PRESET = 'style_preset';
16 const IMAGE_TYPE = 'image_type';
17 const IMAGE_STRENGTH = 'image_strength';
18 const ASPECT_RATIO = 'ratio';
19 const IMAGE_RESOLUTION = 'image_resolution';
20
21 const PROMPT = 'prompt';
22
23 public function get_title() {
24 return esc_html__( 'AI', 'elementor' );
25 }
26
27 protected function get_api_url() {
28 return static::API_URL . '/';
29 }
30
31 public function get_usage() {
32 return $this->ai_request(
33 'POST',
34 'status/check',
35 [
36 'api_version' => ELEMENTOR_VERSION,
37 'site_lang' => get_bloginfo( 'language' ),
38 ]
39 );
40 }
41
42 public function get_cached_usage() {
43 $cache_key = 'elementor_ai_usage';
44 $cache_time = 24 * HOUR_IN_SECONDS;
45 $usage = get_site_transient( $cache_key );
46
47 if ( ! $usage ) {
48 $usage = $this->get_usage();
49 set_site_transient( $cache_key, $usage, $cache_time );
50 }
51
52 return $usage;
53 }
54
55 public function get_remote_config() {
56 return $this->ai_request(
57 'GET',
58 'remote-config/config',
59 [
60 'api_version' => ELEMENTOR_VERSION,
61 'site_lang' => get_bloginfo( 'language' ),
62 ]
63 );
64 }
65
66 /**
67 * get_file_payload
68 * @param $filename
69 * @param $file_type
70 * @param $file_path
71 * @param $boundary
72 *
73 * @return string
74 */
75 private function get_file_payload( $filename, $file_type, $file_path, $boundary ) {
76 $name = $filename ?? basename( $file_path );
77 $mine_type = 'image' === $file_type ? image_type_to_mime_type( exif_imagetype( $file_path ) ) : $file_type;
78 $payload = '';
79 // Upload the file
80 $payload .= '--' . $boundary;
81 $payload .= "\r\n";
82 $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"; filename="' . esc_attr( $name ) . '"' . "\r\n";
83 $payload .= 'Content-Type: ' . $mine_type . "\r\n";
84 $payload .= "\r\n";
85 $payload .= file_get_contents( $file_path );
86 $payload .= "\r\n";
87
88 return $payload;
89 }
90
91 private function get_upload_request_body( $body, $file, $boundary, $file_name = '' ) {
92 $payload = '';
93 // add all body fields as standard POST fields:
94 foreach ( $body as $name => $value ) {
95 $payload .= '--' . $boundary;
96 $payload .= "\r\n";
97 $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"' . "\r\n\r\n";
98 $payload .= $value;
99 $payload .= "\r\n";
100 }
101
102 if ( is_array( $file ) ) {
103 foreach ( $file as $key => $file_data ) {
104 $payload .= $this->get_file_payload( $file_data['name'], $file_data['type'], $file_data['path'], $boundary );
105 }
106 } else {
107 $image_mime = image_type_to_mime_type( exif_imagetype( $file ) );
108 // @todo: add validation for supported image types
109
110 if ( empty( $file_name ) ) {
111 $file_name = basename( $file );
112 }
113 $payload .= $this->get_file_payload( $file_name, $image_mime, $file, $boundary );
114 }
115
116 $payload .= '--' . $boundary . '--';
117
118 return $payload;
119 }
120
121 private function ai_request( $method, $endpoint, $body, $file = false, $file_name = '', $format = 'default' ) {
122 $headers = [
123 'x-elementor-ai-version' => '2',
124 ];
125
126 if ( $file ) {
127 $boundary = wp_generate_password( 24, false );
128 $body = $this->get_upload_request_body( $body, $file, $boundary, $file_name );
129 // add content type header
130 $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
131 } elseif ( 'json' === $format ) {
132 $headers['Content-Type'] = 'application/json';
133 $body = wp_json_encode( $body );
134 }
135
136 return $this->http_request(
137 $method,
138 $endpoint,
139 [
140 'timeout' => 100,
141 'headers' => $headers,
142 'body' => $body,
143
144 ],
145 [
146 'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
147 ]
148 );
149 }
150
151 public function set_get_started() {
152 return $this->ai_request(
153 'POST',
154 'status/get-started',
155 [
156 'api_version' => ELEMENTOR_VERSION,
157 'site_lang' => get_bloginfo( 'language' ),
158 ]
159 );
160 }
161
162 public function set_status_feedback( $response_id ) {
163 return $this->ai_request(
164 'POST',
165 'status/feedback/' . $response_id,
166 [
167 'api_version' => ELEMENTOR_VERSION,
168 'site_lang' => get_bloginfo( 'language' ),
169 ]
170 );
171 }
172
173 public function set_used_gallery_image( $image_id ) {
174 return $this->ai_request(
175 'POST',
176 'status/used-gallery-image/' . $image_id,
177 [
178 'api_version' => ELEMENTOR_VERSION,
179 'site_lang' => get_bloginfo( 'language' ),
180 ]
181 );
182 }
183
184 public function get_completion_text( $prompt, $context = [] ) {
185 return $this->ai_request(
186 'POST',
187 'text/completion',
188 [
189 'prompt' => $prompt,
190 'context' => wp_json_encode( $context ),
191 'api_version' => ELEMENTOR_VERSION,
192 'site_lang' => get_bloginfo( 'language' ),
193 ]
194 );
195 }
196
197 /**
198 * get_image_prompt_enhanced
199 * @param $prompt
200 *
201 * @return mixed|\WP_Error
202 */
203 public function get_image_prompt_enhanced( $prompt, $context = [] ) {
204 return $this->ai_request(
205 'POST',
206 'text/enhance-image-prompt',
207 [
208 'prompt' => $prompt,
209 'context' => wp_json_encode( $context ),
210 'api_version' => ELEMENTOR_VERSION,
211 'site_lang' => get_bloginfo( 'language' ),
212 ]
213 );
214 }
215
216 public function get_edit_text( $input, $instruction, $context = [] ) {
217 return $this->ai_request(
218 'POST',
219 'text/edit',
220 [
221 'input' => $input,
222 'instruction' => $instruction,
223 'context' => wp_json_encode( $context ),
224 'api_version' => ELEMENTOR_VERSION,
225 'site_lang' => get_bloginfo( 'language' ),
226 ]
227 );
228 }
229
230 public function get_custom_code( $prompt, $language, $context = [] ) {
231 return $this->ai_request(
232 'POST',
233 'text/custom-code',
234 [
235 'prompt' => $prompt,
236 'language' => $language,
237 'context' => wp_json_encode( $context ),
238 'api_version' => ELEMENTOR_VERSION,
239 'site_lang' => get_bloginfo( 'language' ),
240 ]
241 );
242 }
243
244 public function get_custom_css( $prompt, $html_markup, $element_id, $context = [] ) {
245 return $this->ai_request(
246 'POST',
247 'text/custom-css',
248 [
249 'prompt' => $prompt,
250 'html_markup' => $html_markup,
251 'element_id' => $element_id,
252 'context' => wp_json_encode( $context ),
253 'api_version' => ELEMENTOR_VERSION,
254 'site_lang' => get_bloginfo( 'language' ),
255 ]
256 );
257 }
258
259 /**
260 * get_text_to_image
261 * @param $prompt
262 * @param $prompt_settings
263 *
264 * @return mixed|\WP_Error
265 */
266 public function get_text_to_image( $prompt, $prompt_settings, $context = [] ) {
267 return $this->ai_request(
268 'POST',
269 'image/text-to-image',
270 [
271 self::PROMPT => $prompt,
272 self::IMAGE_TYPE => $prompt_settings[ self::IMAGE_TYPE ] . '/' . $prompt_settings[ self::STYLE_PRESET ],
273 self::ASPECT_RATIO => $prompt_settings[ self::ASPECT_RATIO ],
274 'context' => wp_json_encode( $context ),
275 'api_version' => ELEMENTOR_VERSION,
276 'site_lang' => get_bloginfo( 'language' ),
277 ]
278 );
279 }
280
281 /**
282 * get_image_to_image
283 * @param $image_data
284 *
285 * @return mixed|\WP_Error
286 * @throws \Exception
287 */
288 public function get_image_to_image( $image_data, $context = [] ) {
289 $image_file = get_attached_file( $image_data['attachment_id'] );
290
291 if ( ! $image_file ) {
292 throw new \Exception( 'Image file not found' );
293 }
294
295 $result = $this->ai_request(
296 'POST',
297 'image/image-to-image',
298 [
299 self::PROMPT => $image_data[ self::PROMPT ],
300 self::IMAGE_TYPE => $image_data['promptSettings'][ self::IMAGE_TYPE ] . '/' . $image_data['promptSettings'][ self::STYLE_PRESET ],
301 self::IMAGE_STRENGTH => $image_data['promptSettings'][ self::IMAGE_STRENGTH ],
302 self::ASPECT_RATIO => $image_data['promptSettings'][ self::ASPECT_RATIO ],
303 'context' => wp_json_encode( $context ),
304 'api_version' => ELEMENTOR_VERSION,
305 'site_lang' => get_bloginfo( 'language' ),
306 ],
307 $image_file,
308 'image'
309 );
310
311 return $result;
312 }
313
314 /**
315 * get_image_to_image_upscale
316 * @param $image_data
317 *
318 * @return mixed|\WP_Error
319 * @throws \Exception
320 */
321 public function get_image_to_image_upscale( $image_data, $context = [] ) {
322 $image_file = get_attached_file( $image_data['attachment_id'] );
323
324 if ( ! $image_file ) {
325 throw new \Exception( 'Image file not found' );
326 }
327
328 $result = $this->ai_request(
329 'POST',
330 'image/image-to-image/upscale',
331 [
332 self::IMAGE_RESOLUTION => $image_data['promptSettings']['upscale_to'],
333 'context' => wp_json_encode( $context ),
334 'api_version' => ELEMENTOR_VERSION,
335 'site_lang' => get_bloginfo( 'language' ),
336 ],
337 $image_file,
338 'image'
339 );
340
341 return $result;
342 }
343
344 /**
345 * get_image_to_image_remove_background
346 * @param $image_data
347 *
348 * @return mixed|\WP_Error
349 * @throws \Exception
350 */
351 public function get_image_to_image_remove_background( $image_data, $context = [] ) {
352 $image_file = get_attached_file( $image_data['attachment_id'] );
353
354 if ( ! $image_file ) {
355 throw new \Exception( 'Image file not found' );
356 }
357
358 $result = $this->ai_request(
359 'POST',
360 'image/image-to-image/remove-background',
361 [
362 'context' => wp_json_encode( $context ),
363 'api_version' => ELEMENTOR_VERSION,
364 'site_lang' => get_bloginfo( 'language' ),
365 ],
366 $image_file,
367 'image'
368 );
369
370 return $result;
371 }
372
373 /**
374 * get_image_to_image_remove_text
375 * @param $image_data
376 *
377 * @return mixed|\WP_Error
378 * @throws \Exception
379 */
380 public function get_image_to_image_replace_background( $image_data, $context = [] ) {
381 $image_file = get_attached_file( $image_data['attachment_id'] );
382
383 if ( ! $image_file ) {
384 throw new \Exception( 'Image file not found' );
385 }
386
387 $result = $this->ai_request(
388 'POST',
389 'image/image-to-image/replace-background',
390 [
391 self::PROMPT => $image_data[ self::PROMPT ],
392 'context' => wp_json_encode( $context ),
393 'api_version' => ELEMENTOR_VERSION,
394 'site_lang' => get_bloginfo( 'language' ),
395 ],
396 $image_file,
397 'image'
398 );
399
400 return $result;
401 }
402
403 /**
404 * store_temp_file
405 * used to store a temp file for the AI request and deletes it once the request is done
406 * @param $file_content
407 * @param $file_ext
408 *
409 * @return string
410 */
411 private function store_temp_file( $file_content, $file_ext = '' ) {
412 $temp_file = str_replace( '.tmp', '', wp_tempnam() . $file_ext );
413 file_put_contents( $temp_file, $file_content );
414 // make sure the temp file is deleted on shutdown
415 register_shutdown_function( function () use ( $temp_file ) {
416 if ( file_exists( $temp_file ) ) {
417 unlink( $temp_file );
418 }
419 } );
420 return $temp_file;
421 }
422
423 /**
424 * get_image_to_image_out_painting
425 * @param $image_data
426 *
427 * @return mixed|\WP_Error
428 * @throws \Exception
429 */
430 public function get_image_to_image_out_painting( $image_data, $context = [] ) {
431 $img_content = str_replace( ' ', '+', $image_data['mask'] );
432 $img_content = substr( $img_content, strpos( $img_content, ',' ) + 1 );
433 $img_content = base64_decode( $img_content );
434 $mask_file = $this->store_temp_file( $img_content, '.png' );
435
436 if ( ! $mask_file ) {
437 throw new \Exception( 'Expended Image file not found' );
438 }
439
440 $result = $this->ai_request(
441 'POST',
442 'image/image-to-image/outpainting',
443 [
444 self::PROMPT => $image_data[ self::PROMPT ],
445 self::IMAGE_TYPE => '',
446 'context' => wp_json_encode( $context ),
447 'api_version' => ELEMENTOR_VERSION,
448 'site_lang' => get_bloginfo( 'language' ),
449 ],
450 [
451 [
452 'name' => 'image',
453 'type' => 'image',
454 'path' => $mask_file,
455 ],
456 ]
457 );
458
459 return $result;
460 }
461
462 /**
463 * get_image_to_image_mask
464 * @param $image_data
465 *
466 * @return mixed|\WP_Error
467 * @throws \Exception
468 */
469 public function get_image_to_image_mask( $image_data, $context = [] ) {
470 $image_file = get_attached_file( $image_data['attachment_id'] );
471 $mask_file = $this->store_temp_file( $image_data['mask'], '.svg' );
472
473 if ( ! $image_file ) {
474 throw new \Exception( 'Image file not found' );
475 }
476
477 if ( ! $mask_file ) {
478 throw new \Exception( 'Mask file not found' );
479 }
480
481 $result = $this->ai_request(
482 'POST',
483 'image/image-to-image/inpainting',
484 [
485 self::PROMPT => $image_data[ self::PROMPT ],
486 self::IMAGE_TYPE => $image_data['promptSettings'][ self::IMAGE_TYPE ] . '/' . $image_data['promptSettings'][ self::STYLE_PRESET ],
487 self::IMAGE_STRENGTH => $image_data['promptSettings'][ self::IMAGE_STRENGTH ],
488 'context' => wp_json_encode( $context ),
489 'api_version' => ELEMENTOR_VERSION,
490 'site_lang' => get_bloginfo( 'language' ),
491 ],
492 [
493 [
494 'name' => 'image',
495 'type' => 'image',
496 'path' => $image_file,
497 ],
498 [
499 'name' => 'mask_image',
500 'type' => 'image/svg+xml',
501 'path' => $mask_file,
502 ],
503 ]
504 );
505
506 return $result;
507 }
508
509 public function generate_layout( $data, $context ) {
510 $endpoint = 'generate/layout';
511
512 $body = [
513 'prompt' => $data['prompt'],
514 'variationType' => (int) $data['variationType'],
515 'ids' => $data['ids'],
516 ];
517
518 if ( ! empty( $data['prevGeneratedIds'] ) ) {
519 $body['generatedBaseTemplatesIds'] = $data['prevGeneratedIds'];
520 }
521
522 if ( ! empty( $data['attachments'] ) ) {
523 $attachment = $data['attachments'][0];
524
525 switch ( $attachment['type'] ) {
526 case 'json':
527 $endpoint = 'generate/generate-json-variation';
528
529 $body['json'] = [
530 'type' => 'elementor',
531 'elements' => [ $attachment['content'] ],
532 ];
533 break;
534 case 'url':
535 $endpoint = 'generate/html-to-elementor';
536
537 $html = wp_json_encode( $attachment['content'] );
538
539 $body['html'] = $html;
540
541 break;
542 }
543 }
544
545 $context['currentContext'] = $data['currentContext'];
546
547 if ( ElementorUtils::has_pro() ) {
548 $context['features'] = [
549 'subscriptions' => [ 'Pro' ],
550 ];
551 }
552
553 $metadata = [
554 'context' => $context,
555 'api_version' => ELEMENTOR_VERSION,
556 'site_lang' => get_bloginfo( 'language' ),
557 'config' => [
558 'generate' => [
559 'all' => true,
560 ],
561 ],
562 ];
563
564 $body = array_merge( $body, $metadata );
565
566 // Temp hack for platforms that filters the http_request_args, and it breaks JSON requests.
567 remove_all_filters( 'http_request_args' );
568
569 return $this->ai_request(
570 'POST',
571 $endpoint,
572 $body,
573 false,
574 '',
575 'json'
576 );
577 }
578
579 public function get_layout_prompt_enhanced( $prompt, $enhance_type, $context ) {
580 return $this->ai_request(
581 'POST',
582 'generate/enhance-prompt',
583 [
584 'prompt' => $prompt,
585 'enhance_type' => $enhance_type,
586 'context' => wp_json_encode( $context ),
587 'api_version' => ELEMENTOR_VERSION,
588 'site_lang' => get_bloginfo( 'language' ),
589 ]
590 );
591 }
592
593 public function get_history_by_type( $type, $page, $limit, $context = [] ) {
594 $endpoint = Module::HISTORY_TYPE_ALL === $type
595 ? 'history'
596 : add_query_arg( [
597 'page' => $page,
598 'limit' => $limit,
599 ], "history/{$type}" );
600
601 return $this->ai_request(
602 'POST',
603 $endpoint,
604 [
605 'context' => wp_json_encode( $context ),
606 'api_version' => ELEMENTOR_VERSION,
607 'site_lang' => get_bloginfo( 'language' ),
608 ]
609 );
610 }
611
612 public function delete_history_item( $id, $context = [] ) {
613 return $this->ai_request(
614 'DELETE', 'history/' . $id,
615 [
616 'context' => wp_json_encode( $context ),
617 'api_version' => ELEMENTOR_VERSION,
618 'site_lang' => get_bloginfo( 'language' ),
619 ]
620 );
621 }
622
623 public function toggle_favorite_history_item( $id, $context = [] ) {
624 return $this->ai_request(
625 'POST', sprintf( 'history/%s/favorite', $id ),
626 [
627 'context' => wp_json_encode( $context ),
628 'api_version' => ELEMENTOR_VERSION,
629 'site_lang' => get_bloginfo( 'language' ),
630 ]
631 );
632 }
633
634 protected function init() {}
635 }
636