PluginProbe
Elementor Website Builder – more than just a page builder / 3.17.3
Elementor Website Builder – more than just a page builder v3.17.3
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.17.3, at modules/ai/connect/ai.php

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