PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.12.0
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.12.0
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / inc / api.php

api.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 3.12.0, at inc/api.php

562 lines 15.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The class defines way of connecting this user to the Optimole Dashboard.
5 *
6 * @codeCoverageIgnore
7 * @package \Optimole\Inc
8 * @author Optimole <friends@optimole.com>
9 */
10 final class Optml_Api {
11
12 /**
13 * Optimole root api url.
14 *
15 * @var string Api root.
16 */
17 private $api_root = 'https://dashboard.optimole.com/api/';
18 /**
19 * Optimole upload api root url.
20 *
21 * @var string Api root.
22 */
23 private $upload_api_root = 'https://generateurls-prod.i.optimole.com/upload';
24 /**
25 * Optimole onboard api root url.
26 *
27 * @var string Api root.
28 */
29 private $onboard_api_root = 'https://onboard.i.optimole.com/onboard_api/';
30 /**
31 * Optimole offload conflicts api root url.
32 *
33 * @var string Api root.
34 */
35 private $upload_conflicts_api = 'https://conflicts.i.optimole.com/offload_api/';
36 /**
37 * Hold the user api key.
38 *
39 * @var string Api key.
40 */
41 private $api_key;
42 /**
43 * Optml_Api constructor.
44 */
45 public function __construct() {
46 $settings = new Optml_Settings();
47 $this->api_key = $settings->get( 'api_key' );
48 if ( defined( 'OPTIML_API_ROOT' ) && constant( 'OPTIML_API_ROOT' ) ) {
49 $this->api_root = constant( 'OPTIML_API_ROOT' );
50 }
51 if ( defined( 'OPTIML_UPLOAD_API_ROOT' ) && constant( 'OPTIML_UPLOAD_API_ROOT' ) ) {
52 $this->upload_api_root = constant( 'OPTIML_UPLOAD_API_ROOT' );
53 }
54 if ( defined( 'OPTIML_ONBOARD_API_ROOT' ) && constant( 'OPTIML_ONBOARD_API_ROOT' ) ) {
55 $this->onboard_api_root = constant( 'OPTIML_ONBOARD_API_ROOT' );
56 }
57 if ( defined( 'OPTIML_UPLOAD_CONFLICTS_API_ROOT' ) && constant( 'OPTIML_UPLOAD_CONFLICTS_API_ROOT' ) ) {
58 $this->upload_conflicts_api = constant( 'OPTIML_UPLOAD_CONFLICTS_API_ROOT' );
59 }
60 }
61
62 /**
63 * Connect to the service.
64 *
65 * @param string $api_key Api key.
66 *
67 * @return array|bool|WP_Error
68 */
69 public function connect( $api_key = '' ) {
70 if ( ! empty( $api_key ) ) {
71 $this->api_key = $api_key;
72 }
73
74 return $this->request( '/optml/v2/account/connect', 'POST', [ 'sample_image' => $this->get_sample_image() ] );
75 }
76
77 /**
78 * Get sample image.
79 *
80 * @return string
81 */
82 private function get_sample_image() {
83 $accepted_mimes = [ 'image/jpeg', 'image/png', 'image/webp' ];
84 $args = [
85 'post_type' => 'attachment',
86 'post_status' => 'any',
87 'number' => '1',
88 'no_found_rows' => true,
89 'fields' => 'ids',
90 'post_mime_type' => $accepted_mimes,
91 'post_parent__not_in' => [ 0 ],
92 ];
93 $image_result = new WP_Query( $args );
94 $original_image_url = 'none';
95 if ( ! empty( $image_result->posts ) ) {
96 $original_image_url = wp_get_attachment_image_url( $image_result->posts[0], 'full' );
97 }
98
99 return $original_image_url;
100 }
101 /**
102 * Get user data from service.
103 *
104 * @return array|string|bool|WP_Error User data.
105 */
106 public function get_user_data( $api_key = '', $application = '' ) {
107 if ( ! empty( $api_key ) ) {
108 $this->api_key = $api_key;
109 }
110
111 return $this->request( '/optml/v2/account/details', 'POST', [ 'application' => $application ] );
112 }
113
114 /**
115 * Toggle the extra visits.
116 *
117 * @param string $api_key Api key.
118 * @param string $status Status of the visits toggle.
119 *
120 * @return array|bool|string
121 */
122 public function update_extra_visits( $api_key = '', $status = 'enabled', $application = '' ) {
123 if ( ! empty( $api_key ) ) {
124 $this->api_key = $api_key;
125 }
126
127 return $this->request( '/optml/v2/account/extra_visits', 'POST', [ 'extra_visits' => $status, 'application' => $application ] );
128 }
129
130 /**
131 * Get cache token from service.
132 *
133 * @return array|bool|WP_Error User data.
134 */
135 public function get_cache_token( $token = '', $type = '', $api_key = '' ) {
136 if ( ! empty( $api_key ) ) {
137 $this->api_key = $api_key;
138 }
139 $lock = get_transient( 'optml_cache_lock' );
140 if ( ! empty( $type ) && $type === 'assets' ) {
141 $lock = get_transient( 'optml_cache_lock_assets' );
142 }
143
144 if ( $lock === 'yes' ) {
145 return new WP_Error( 'cache_throttle', __( 'You can clear cache only once per 5 minutes.', 'optimole-wp' ) );
146 }
147 return $this->request( '/optml/v1/cache/tokens', 'POST', [ 'token' => $token, 'type' => $type ] );
148 }
149
150 /**
151 * Request constructor.
152 *
153 * @param string $path The request url.
154 * @param string $method The request method type.
155 * @param array|string $params The request method type.
156 *
157 * @return array|string|boolean|WP_Error Api data.
158 */
159 private function request( $path, $method = 'GET', $params = [], $extra_headers = [] ) {
160
161 $headers = [
162 'Optml-Site' => get_home_url(),
163 ];
164 if ( ! empty( $this->api_key ) ) {
165 $headers['Authorization'] = 'Bearer ' . $this->api_key;
166 }
167 if ( is_array( $headers ) ) {
168 $headers = array_merge( $headers, $extra_headers );
169 }
170 $url = trailingslashit( $this->api_root ) . ltrim( $path, '/' );
171 // If there is a extra, add that as a url var.
172 if ( 'GET' === $method && ! empty( $params ) ) {
173 foreach ( $params as $key => $val ) {
174 $url = add_query_arg( [ $key => $val ], $url );
175 }
176 }
177 $args = $this->build_args( $method, $url, $headers, $params );
178
179 $response = wp_remote_request( $url, $args );
180
181 if ( is_wp_error( $response ) ) {
182 return $response;
183 }
184
185 $response = wp_remote_retrieve_body( $response );
186
187 if ( empty( $response ) ) {
188 return false;
189 }
190 $response = json_decode( $response, true );
191
192 if ( isset( $response['id'] ) && is_numeric( $response['id'] ) ) {
193 return true;
194 }
195 if ( ! isset( $response['code'] ) ) {
196 return false;
197 }
198
199 if ( intval( $response['code'] ) !== 200 ) {
200 if ( isset( $response['error'] ) && $response['error'] === 'domain_not_accessible' ) {
201 return new WP_Error( 'domain_not_accessible', sprintf( __( 'It seems Optimole is having trouble reaching your website. This issue often occurs if your website is private, local, or protected by a firewall. But don\'t stress – it\'s an easy fix! Ensure your website is live and accessible to the public. If a firewall is in place, just tweak the settings to allow the %1$sOptimole(1.0)%2$s user agent access to your website. %3$sLearn More%4$s', 'optimole-wp' ), '<i>', '</i>', '<a href="https://docs.optimole.com/article/1976-resolving-optimole-access-to-your-website" target="_blank">', '</a>' ) );
202 }
203 if ( $path === 'optml/v2/account/complete_register_remote' && isset( $response['error'] ) ) {
204 if ( strpos( $response['error'], 'This email address is already registered.' ) !== false ) {
205 return 'email_registered';
206 }
207
208 if ( $response['error'] === 'ERROR: Site already whitelisted.' ) {
209 return 'site_exists';
210 }
211 }
212
213 if ( $path === '/optml/v2/account/details'
214 && isset( $response['code'] ) && $response['code'] === 'not_allowed' ) {
215 return 'disconnect';
216 }
217 return false;
218 }
219
220 return $response['data'];
221
222 }
223
224 /**
225 * Builds Request arguments array.
226 *
227 * @param string $method Request method (GET | POST | PUT | UPDATE | DELETE).
228 * @param string $url Request URL.
229 * @param array $headers Headers Array.
230 * @param array|string $params Additional params for the Request.
231 *
232 * @return array
233 */
234 private function build_args( $method, $url, $headers, $params ) {
235 $args = [
236 'method' => $method,
237 'timeout' => 45,
238 'user-agent' => 'Optimle WP (v' . OPTML_VERSION . ') ',
239 'sslverify' => false,
240 'headers' => $headers,
241 ];
242 if ( $method !== 'GET' ) {
243 $args['body'] = $params;
244 }
245
246 return $args;
247 }
248
249 /**
250 * Upload image to our servers using the generated signed url.
251 *
252 * @param string $upload_url The signed to url to upload the image to.
253 * @param string $content_type Image mime type, it must match the actual mime type of the image.
254 * @param string $image Image data from file_get_contents.
255 * @return mixed
256 */
257 public function upload_image( $upload_url, $content_type, $image ) {
258 $args = $this->build_args( 'PUT', '', ['content-type' => $content_type], $image );
259 return wp_remote_request( $upload_url, $args );
260 }
261
262 /**
263 * Check if the optimized url is available.
264 *
265 * @param string $url The optimized url to check.
266 * @return bool Whether or not the url is valid.
267 */
268 public function check_optimized_url( $url ) {
269 $response = wp_remote_get( $url, ['timeout' => 30] );
270
271 if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 || ! empty( wp_remote_retrieve_header( $response, 'x-not-found-o' ) ) ) {
272 $this->log_offload_error( $response );
273 return false;
274 }
275 return true;
276 }
277
278 /**
279 * Get options for the signed urls api call.
280 *
281 * @param string $original_url Image original url.
282 * @param string $delete Whether to delete a bucket object or not(ie. generate signed upload url).
283 * @param string $table_id Remote id used on our servers.
284 * @param string $update_table False or success.
285 * @param string $get_url Whether to return a get url or not.
286 * @param string $width Original image width.
287 * @param string $height Original image height.
288 * @param int $file_size Original file size.
289 * @return array|WP_Error
290 */
291 public function call_upload_api( $original_url = '', $delete = 'false', $table_id = '', $update_table = 'false', $get_url = 'false', $width = 'auto', $height = 'auto', $file_size = 0 ) {
292 $body = [
293 'secret' => Optml_Config::$secret,
294 'userKey' => Optml_Config::$key,
295 'originalUrl' => $original_url,
296 'deleteUrl' => $delete,
297 'id' => $table_id,
298 'updateDynamo' => $update_table,
299 'getUrl' => $get_url,
300 'width' => $width,
301 'height' => $height,
302 'originalFileSize' => $file_size,
303 ];
304 $body = wp_json_encode( $body );
305
306 $options = [
307 'body' => $body,
308 'headers' => [
309 'Content-Type' => 'application/json',
310 ],
311 'timeout' => 60,
312 'blocking' => true,
313 'sslverify' => false,
314 'data_format' => 'body',
315 ];
316 return wp_remote_post( $this->upload_api_root, $options );
317 }
318
319 /**
320 * Send a list of images to upload.
321 *
322 * @param array $images List of Images.
323 * @return array
324 */
325 public function call_onboard_api( $images = [] ) {
326 $settings = new Optml_Settings();
327 $token_images = $settings->get( 'cache_buster_images' );
328
329 $body = [
330 'secret' => Optml_Config::$secret,
331 'userKey' => Optml_Config::$key,
332 'images' => $images,
333 'cache_buster' => $token_images,
334 ];
335 $body = wp_json_encode( $body );
336
337 $options = [
338 'body' => $body,
339 'headers' => [
340 'Content-Type' => 'application/json',
341 ],
342 'timeout' => 60,
343 'blocking' => true,
344 'sslverify' => false,
345 'data_format' => 'body',
346 ];
347 return wp_remote_post( $this->onboard_api_root, $options );
348 }
349
350
351 /**
352 * Send a list of images with alt/title values to update.
353 *
354 * @param array $images List of images.
355 * @return array
356 */
357 public function call_data_enrich_api( $images = [] ) {
358 return $this->request( 'optml/v2/media/add_data', 'POST', ['images' => $images, 'key' => Optml_Config::$key] );
359 }
360 /**
361 * Register user remotely on optimole.com.
362 *
363 * @param string $email User email.
364 *
365 * @return array|bool|string|WP_Error Api response.
366 */
367 public function create_account( $email ) {
368 return $this->request(
369 'optml/v2/account/complete_register_remote',
370 'POST',
371 [
372 'email' => $email,
373 'version' => OPTML_VERSION,
374 'sample_image' => $this->get_sample_image(),
375 'site' => get_home_url(),
376 ]
377 );
378 }
379
380 /**
381 * Get the optimized images from API.
382 *
383 * @param string $api_key the api key.
384 *
385 * @return array|bool|WP_Error
386 */
387 public function get_optimized_images( $api_key = '' ) {
388 if ( ! empty( $api_key ) ) {
389 $this->api_key = $api_key;
390 }
391 $app_key = '';
392 $settings = new Optml_Settings();
393 $service_data = $settings->get( 'service_data' );
394 if ( isset( $service_data['cdn_key'] ) ) {
395 $app_key = $service_data['cdn_key'];
396 }
397 return $this->request( '/optml/v1/stats/images', 'GET', [], ['application' => $app_key] );
398 }
399
400 /**
401 * Get the watermarks from API.
402 *
403 * @param string $api_key The API key.
404 *
405 * @return array|bool|WP_Error
406 */
407 public function get_watermarks( $api_key = '' ) {
408 if ( ! empty( $api_key ) ) {
409 $this->api_key = $api_key;
410 }
411
412 return $this->request( '/optml/v1/settings/watermark' );
413 }
414
415 /**
416 * Remove the watermark from the API.
417 *
418 * @param integer $post_id The watermark post ID.
419 * @param string $api_key The API key.
420 *
421 * @return array|bool|WP_Error
422 */
423 public function remove_watermark( $post_id, $api_key = '' ) {
424 if ( ! empty( $api_key ) ) {
425 $this->api_key = $api_key;
426 }
427
428 return $this->request( '/optml/v1/settings/watermark', 'DELETE', [ 'watermark' => $post_id ] );
429 }
430
431 /**
432 * Add watermark.
433 *
434 * @param array $file The file to be uploaded.
435 *
436 * @return array|bool|mixed|object
437 */
438 public function add_watermark( $file ) {
439
440 $headers = [
441 'Content-Disposition' => 'attachment; filename=' . $file['file']['name'],
442 ];
443
444 $response = $this->request( 'wp/v2/media', 'POST', file_get_contents( $file['file']['tmp_name'] ), $headers );
445
446 if ( $response === false ) {
447 return false;
448 }
449
450 return $response;
451 }
452
453 /**
454 * Call the images endpoint.
455 *
456 * @param integer $page Page used to advance the search.
457 * @param array $domains Domains to filter by.
458 * @param string $search The string to search inside the originURL.
459 * @return mixed The decoded json response from the api.
460 */
461 public function get_cloud_images( $page = 0, $domains = [], $search = '' ) {
462
463 $params = ['key' => Optml_Config::$key ];
464 $params['page'] = $page;
465 $params['size'] = 40;
466 if ( $search !== '' ) {
467 $params['search'] = $search;
468 }
469
470 if ( ! empty( $domains ) ) {
471 $params['domains'] = implode( ',', $domains );
472 }
473 return $this->request( 'optml/v2/media/browser', 'GET', $params );
474 }
475 /**
476 * Get offload conflicts.
477 *
478 * @return array The decoded conflicts list.
479 */
480 public function get_offload_conflicts() {
481 $conflicts_list = wp_remote_retrieve_body( wp_remote_get( $this->upload_conflicts_api ) );
482 return json_decode( $conflicts_list, true );
483 }
484 /**
485 * Get offload conflicts.
486 *
487 * @param array $error_response The error to send as a string.
488 */
489 public function log_offload_error( $error_response ) {
490
491 $headers = wp_remote_retrieve_headers( $error_response );
492 $body = wp_remote_retrieve_body( $error_response );
493
494 $headers_to_log = 'no_headers_returned';
495 if ( ! empty( $headers ) ) {
496 $headers_to_log = wp_json_encode( $headers->getAll() );
497 }
498 wp_remote_post(
499 $this->upload_conflicts_api,
500 [
501 'headers' => [ 'Content-Type' => 'application/json' ],
502 'timeout' => 15,
503 'blocking' => true,
504 'sslverify' => false,
505 'data_format' => 'body',
506 'body' => [
507 'error_body' => wp_json_encode( $body ),
508 'error_headers' => $headers_to_log,
509 'error_site' => wp_json_encode( get_home_url() ),
510 ],
511 ]
512 );
513 }
514
515 /**
516 * Send Offloading Logs
517 *
518 * @param string $type Type of log (offload/rollback).
519 * @param string $message Log message.
520 *
521 * @return mixed
522 */
523 public function send_log( $type, $message ) {
524 return $this->request(
525 'optml/v2/logs',
526 'POST',
527 [
528 'type' => $type,
529 'message' => $message,
530 'site' => get_home_url(),
531 ]
532 );
533 }
534
535 /**
536 * Throw error on object clone
537 *
538 * The whole idea of the singleton design pattern is that there is a single
539 * object therefore, we don't want the object to be cloned.
540 *
541 * @access public
542 * @return void
543 * @since 1.0.0
544 */
545 public function __clone() {
546 // Cloning instances of the class is forbidden.
547 _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'optimole-wp' ), '1.0.0' );
548 }
549
550 /**
551 * Disable unserializing of the class
552 *
553 * @access public
554 * @return void
555 * @since 1.0.0
556 */
557 public function __wakeup() {
558 // Unserializing instances of the class is forbidden.
559 _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'optimole-wp' ), '1.0.0' );
560 }
561 }
562