PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 1.0.4
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v1.0.4
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
← All changes | inc/rest.php +136 -984 4.2.131.0.4 View file →
@@ -6,19 +6,12 @@
6 6 * @copyright Copyright (c) 2017, Marius Cristea
7 7 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8 8 */
9 9
10 -use Optimole\Sdk\Resource\ImageProperty\ResizeTypeProperty;
11 -use Optimole\Sdk\ValueObject\Position;
12 -use OptimoleWP\PageProfiler\Profile;
13 -
14 10 /**
15 11 * Class Optml_Rest
16 - *
17 - * @codeCoverageIgnore
18 12 */
19 13 class Optml_Rest {
20 -
21 14 /**
22 15 * Rest api namespace.
23 16 *
24 17 * @var string Namespace.
@@ -25,425 +18,130 @@
25 18 */
26 19 private $namespace;
27 20
28 21 /**
29 - * Upload conflicts api.
30 - *
31 - * @var array{
32 - * service_routes: RestRouteMap,
33 - * image_routes: RestRouteMap,
34 - * media_cloud_routes: RestRouteMap,
35 - * conflict_routes: SimpleRouteMap,
36 - * cache_routes: SimpleRouteMap,
37 - * dam_routes: RestRouteMap,
38 - * notification_dismiss_routes: RestRouteMap,
39 - * optimization_routes: RestRouteMap
40 - * }
41 - */
42 - public static $rest_routes = [
43 - 'service_routes' => [
44 - 'update_option' => 'POST',
45 - 'request_update' => 'GET',
46 - 'connect' => [
47 - 'POST',
48 - 'args' => [
49 - 'api_key' => [
50 - 'type' => 'string',
51 - 'required' => true,
52 - ],
53 - ],
54 - ],
55 - 'select_application' => [
56 - 'POST',
57 - 'args' => [
58 - 'api_key' => [
59 - 'type' => 'string',
60 - 'required' => true,
61 - ],
62 - 'application' => [
63 - 'type' => 'string',
64 - 'required' => true,
65 - ],
66 - ],
67 - ],
68 - 'register_service' => [
69 - 'POST',
70 - 'args' => [
71 - 'email' => [
72 - 'type' => 'string',
73 - 'required' => true,
74 - ],
75 - ],
76 - ],
77 - 'disconnect' => 'GET',
78 - ],
79 - 'image_routes' => [
80 - 'poll_optimized_images' => 'GET',
81 - 'get_sample_rate' => 'POST',
82 - 'upload_onboard_images' => [
83 - 'POST',
84 - 'args' => [
85 - 'offset' => [
86 - 'type' => 'number',
87 - 'required' => false,
88 - 'default' => 0,
89 - ],
90 - ],
91 - ],
92 - ],
93 - 'media_cloud_routes' => [
94 - 'number_of_images_and_pages' => 'POST',
95 - 'clear_offload_errors' => 'GET',
96 - 'get_offload_conflicts' => 'GET',
97 - 'move_image' => [
98 - 'POST',
99 - 'args' => [
100 - 'id' => [
101 - 'type' => 'number',
102 - 'required' => true,
103 - ],
104 - 'action' => [
105 - 'type' => 'string',
106 - 'required' => true,
107 - ],
108 - ],
109 - 'permission_callback' => [ __CLASS__, 'can_move_image' ],
110 - ],
111 - ],
112 - 'conflict_routes' => [
113 - 'poll_conflicts' => 'GET',
114 - 'dismiss_conflict' => 'POST',
115 - ],
116 - 'cache_routes' => [
117 - 'clear_cache_request' => 'POST',
118 - ],
119 - 'dam_routes' => [
120 - 'insert_images' => [
121 - 'POST',
122 - 'args' => [
123 - 'images' => [
124 - 'type' => 'array',
125 - 'required' => true,
126 - ],
127 - ],
128 - 'permission_callback' => 'upload_files',
129 - ],
130 - ],
131 - 'notification_dismiss_routes' => [
132 - 'dismiss_notice' => [
133 - 'POST',
134 - 'args' => [
135 - 'key' => [
136 - 'type' => 'string',
137 - 'required' => true,
138 - ],
139 - ],
140 - ],
141 - ],
142 - 'optimization_routes' => [
143 - 'optimizations' => [
144 - 'POST',
145 - 'args' => [
146 - 'd' => [
147 - 'type' => 'integer',
148 - 'required' => true,
149 - ],
150 - 'a' => [
151 - 'type' => 'array',
152 - 'required' => true,
153 - ],
154 - 'u' => [
155 - 'type' => 'string',
156 - 'required' => true,
157 - ],
158 - ],
159 - 'permission_callback' => '__return_true',
160 - ],
161 - ],
162 - ];
163 -
164 - /**
165 22 * Optml_Rest constructor.
166 - *
167 - * @return void
168 23 */
169 24 public function __construct() {
170 25 $this->namespace = OPTML_NAMESPACE . '/v1';
171 -
172 - add_action( 'rest_api_init', [ $this, 'register' ] );
26 + add_action( 'rest_api_init', array( $this, 'register' ) );
173 27 }
174 28
175 29 /**
176 - * Method to register a specific REST route.
177 - *
178 - * @param string $route The route name.
179 - * @param string $method The route access method: GET, POST, POST_PUT_PATCH.
180 - * @param array $args Optional arguments for route parameters.
181 - * @param string|callable $permission_callback Permission callback function or capability.
182 - *
183 - * @phpstan-param RestArgs $args Optional arguments for route parameters.
184 - *
185 - * @throws \InvalidArgumentException If method is invalid.
186 - *
187 - * @return void
30 + * Register rest routes.
188 31 */
189 - private function register_route( $route, $method = 'GET', $args = [], $permission_callback = 'manage_options' ) {
190 - if ( empty( $route ) ) {
191 - return;
192 - }
32 + public function register() {
193 33
194 - $method_map = [
195 - 'GET' => \WP_REST_Server::READABLE,
196 - 'POST' => \WP_REST_Server::CREATABLE,
197 - 'POST_PUT_PATCH' => \WP_REST_Server::EDITABLE,
198 - ];
199 -
200 - if ( ! isset( $method_map[ $method ] ) ) {
201 - _doing_it_wrong(
202 - __METHOD__,
203 - sprintf( 'Invalid REST method: %s', esc_html( $method ) ),
204 - '1.0.0'
205 - );
206 - return;
207 - }
208 -
209 - $permission = is_callable( $permission_callback )
210 - ? $permission_callback
211 - : function () use ( $permission_callback ) {
212 - if ( ! is_string( $permission_callback ) ) {
213 - return false;
214 - }
215 - return current_user_can( $permission_callback );
216 - };
217 -
218 - $params = [
219 - 'methods' => $method_map[ $method ],
220 - 'permission_callback' => $permission,
221 - 'callback' => [ $this, $route ],
222 - ];
223 -
224 - if ( ! empty( $args ) ) {
225 - $params['args'] = $args;
226 - }
227 -
228 34 register_rest_route(
229 - $this->namespace,
230 - '/' . $route,
231 - [ $params ]
35 + $this->namespace, '/connect', array(
36 + array(
37 + 'methods' => \WP_REST_Server::CREATABLE,
38 + 'permission_callback' => function () {
39 + return current_user_can( 'manage_options' );
40 + },
41 + 'callback' => array( $this, 'connect' ),
42 + 'args' => array(
43 + 'api_key' => array(
44 + 'type' => 'string',
45 + 'required' => true,
46 + ),
47 + ),
48 + ),
49 + )
232 50 );
51 + register_rest_route(
52 + $this->namespace, '/register', array(
53 + array(
54 + 'methods' => \WP_REST_Server::CREATABLE,
55 + 'permission_callback' => function () {
56 + return current_user_can( 'manage_options' );
57 + },
58 + 'callback' => array( $this, 'register_service' ),
59 + 'args' => array(
60 + 'email' => array(
61 + 'type' => 'string',
62 + 'required' => true,
63 + ),
64 + ),
65 + ),
66 + )
67 + );
68 + register_rest_route(
69 + $this->namespace, '/disconnect', array(
70 + array(
71 + 'methods' => \WP_REST_Server::READABLE,
72 + 'permission_callback' => function () {
73 + return current_user_can( 'manage_options' );
74 + },
75 + 'callback' => array( $this, 'disconnect' ),
76 + ),
77 + )
78 + );
79 + register_rest_route(
80 + $this->namespace, '/update_option', array(
81 + array(
82 + 'methods' => \WP_REST_Server::CREATABLE,
83 + 'permission_callback' => function () {
84 + return current_user_can( 'manage_options' );
85 + },
86 + 'callback' => array( $this, 'update_option' ),
87 + ),
88 + )
89 + );
90 + register_rest_route(
91 + $this->namespace, '/poll_optimized_images', array(
92 + array(
93 + 'methods' => \WP_REST_Server::READABLE,
94 + 'permission_callback' => function () {
95 + return current_user_can( 'manage_options' );
96 + },
97 + 'callback' => array( $this, 'poll_optimized_images' ),
98 + ),
99 + )
100 + );
101 + register_rest_route(
102 + $this->namespace, '/images-sample-rate', array(
103 + array(
104 + 'methods' => \WP_REST_Server::CREATABLE,
105 + 'permission_callback' => function () {
106 + return current_user_can( 'manage_options' );
107 + },
108 + 'callback' => array( $this, 'get_sample_rate' ),
109 + ),
110 + )
111 + );
233 112 }
234 113
235 114 /**
236 - * Register rest routes.
237 - *
238 - * @return void
239 - */
240 - public function register() {
241 -
242 - $this->register_service_routes();
243 -
244 - $this->register_image_routes();
245 - $this->register_conflict_routes();
246 - $this->register_cache_routes();
247 - $this->register_media_offload_routes();
248 - $this->register_dam_routes();
249 - $this->register_notification_routes();
250 - $this->register_optimization_routes();
251 - }
252 -
253 - /**
254 - * Method to register service specific routes.
255 - *
256 - * @return void
257 - */
258 - public function register_service_routes() {
259 - foreach ( self::$rest_routes['service_routes'] as $route => $details ) {
260 - if ( is_array( $details ) ) {
261 - $this->register_route( $route, $details[0], $details['args'] );
262 - } else {
263 - $this->register_route( $route, $details );
264 - }
265 - }
266 - }
267 -
268 - /**
269 - * Method to register image specific routes.
270 - *
271 - * @return void
272 - */
273 - public function register_image_routes() {
274 - foreach ( self::$rest_routes['image_routes'] as $route => $details ) {
275 - if ( is_array( $details ) ) {
276 - $this->register_route( $route, $details[0], $details['args'] );
277 - } else {
278 - $this->register_route( $route, $details );
279 - }
280 - }
281 - }
282 - /**
283 - * Method to register media offload specific routes.
284 - *
285 - * @return void
286 - */
287 - public function register_media_offload_routes() {
288 - foreach ( self::$rest_routes['media_cloud_routes'] as $route => $details ) {
289 -
290 - $permission = isset( $details['permission_callback'] ) ? $details['permission_callback'] : 'manage_options';
291 - $args = isset( $details['args'] ) ? $details['args'] : [];
292 - $this->register_route( $route, is_array( $details ) ? $details[0] : $details, $args, $permission );
293 - }
294 - }
295 -
296 -
297 - /**
298 - * Method to register conflicts specific routes.
299 - *
300 - * @return void
301 - */
302 - public function register_conflict_routes() {
303 - foreach ( self::$rest_routes['conflict_routes'] as $route => $details ) {
304 - $this->register_route( $route, $details );
305 - }
306 - }
307 -
308 - /**
309 - * Method to register cache specific routes.
310 - *
311 - * @return void
312 - */
313 - public function register_cache_routes() {
314 - foreach ( self::$rest_routes['cache_routes'] as $route => $details ) {
315 - $this->register_route( $route, $details );
316 - }
317 - }
318 -
319 - /**
320 - * Register DAM routes.
321 - *
322 - * @return void
323 - */
324 - public function register_dam_routes() {
325 - foreach ( self::$rest_routes['dam_routes'] as $route => $details ) {
326 - $permission = isset( $details['permission_callback'] ) ? $details['permission_callback'] : 'manage_options';
327 - $args = isset( $details['args'] ) ? $details['args'] : [];
328 - $this->register_route( $route, $details[0], $args, $permission );
329 - }
330 - }
331 -
332 - /**
333 - * Register notification dismiss routes.
334 - *
335 - * @return void
336 - */
337 - public function register_notification_routes() {
338 - foreach ( self::$rest_routes['notification_dismiss_routes'] as $route => $details ) {
339 - $this->register_route( $route, $details[0], isset( $details['args'] ) ? $details['args'] : [] );
340 - }
341 - }
342 -
343 - /**
344 - * Clear Cache request.
345 - *
346 - * @param WP_REST_Request $request clear cache rest request.
347 - * @phpstan-param WP_REST_Request<array{type?: string}> $request
348 - *
349 - * @return WP_Error|WP_REST_Response
350 - */
351 - public function clear_cache_request( WP_REST_Request $request ) {
352 - $settings = new Optml_Settings();
353 - $type = $request->get_param( 'type' );
354 - $response = $settings->clear_cache( $type );
355 -
356 - if ( is_wp_error( $response ) ) {
357 - wp_send_json_error( $response->get_error_message() );
358 - }
359 -
360 - return $this->response( $response, '200' );
361 - }
362 -
363 - /**
364 115 * Connect to optimole service.
365 116 *
366 117 * @param WP_REST_Request $request connect rest request.
367 - * @phpstan-param WP_REST_Request<array{api_key: string, application?: string}> $request
368 118 *
369 119 * @return WP_Error|WP_REST_Response
370 120 */
371 121 public function connect( WP_REST_Request $request ) {
372 122 $api_key = $request->get_param( 'api_key' );
373 - $original_request = $request;
374 123 $request = new Optml_Api();
375 - $data = $request->connect( $api_key );
376 -
377 - if ( $data === false || is_wp_error( $data ) ) {
378 -
379 - $extra = '';
380 - if ( is_wp_error( $data ) ) {
381 - if ( $data->get_error_code() === 'domain_not_accessible' ) {
382 - return $this->response( $data->get_error_message(), 400 );
383 - }
384 - /**
385 - * Error from api.
386 - *
387 - * @var WP_Error $data Error object.
388 - */
389 - $extra = sprintf( /* translators: Error details */ __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
390 - }
391 - return $this->response( __( 'Can not connect to Optimole service', 'optimole-wp' ) . $extra, 400 );
124 + $data = $request->get_user_data( $api_key );
125 + if ( $data === false ) {
126 + wp_send_json_error( __( 'Can not connect to optimole service', 'optimole-wp' ) );
392 127 }
393 128 $settings = new Optml_Settings();
129 + $settings->update( 'service_data', $data );
394 130 $settings->update( 'api_key', $api_key );
395 131
396 - if ( isset( $data['extra_visits'] ) ) {
397 - $settings->update_frontend_banner_from_remote( $data['extra_visits'] );
398 - }
399 -
400 - if ( $data['app_count'] === 1 ) {
401 - return $this->select_application( $original_request );
402 - }
403 132 return $this->response( $data );
404 133 }
405 134
406 135 /**
407 - * Select application.
408 - *
409 - * @param WP_REST_Request $request Rest request.
410 - * @phpstan-param WP_REST_Request<array{api_key: string, application?: string}> $request
411 - *
412 - * @return WP_REST_Response
413 - */
414 - public function select_application( WP_REST_Request $request ) {
415 - $api_key = $request->get_param( 'api_key' );
416 - $application = $request->get_param( 'application' );
417 - $request = new Optml_Api();
418 - $data = $request->get_user_data( $api_key, $application );
419 - if ( $data === false || is_wp_error( $data ) ) {
420 - $extra = '';
421 - if ( is_wp_error( $data ) ) {
422 - /**
423 - * Error from api.
424 - *
425 - * @var WP_Error $data Error object.
426 - */
427 - $extra = sprintf( /* translators: Error details */ __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
428 - }
429 - wp_send_json_error( __( 'Can not connect to Optimole service', 'optimole-wp' ) . $extra );
430 - }
431 - $settings = new Optml_Settings();
432 - $settings->update( 'service_data', $data );
433 - return $this->response( $data );
434 - }
435 -
436 - /**
437 136 * Wrapper for api response.
438 137 *
439 - * @param mixed $data data from api.
440 - * @param string|int $code Response code.
138 + * @param array $data data from api.
441 139 *
442 140 * @return WP_REST_Response
443 141 */
444 - private function response( $data, $code = 'success' ) {
445 - return new WP_REST_Response( [ 'data' => $data, 'code' => $code ], 200 );
142 + private function response( $data ) {
143 + return new WP_REST_Response( array( 'data' => $data, 'code' => 'success' ), 200 );
446 144 }
447 145
448 146 /**
449 147 * Connect to optimole service.
@@ -448,9 +146,8 @@
448 146 /**
449 147 * Connect to optimole service.
450 148 *
451 149 * @param WP_REST_Request $request connect rest request.
452 - * @phpstan-param WP_REST_Request<array{email: string, auto_connect?: string}> $request
453 150 *
454 151 * @return WP_Error|WP_REST_Response
455 152 */
456 153 public function register_service( WP_REST_Request $request ) {
@@ -455,74 +152,14 @@
455 152 */
456 153 public function register_service( WP_REST_Request $request ) {
457 154 $email = $request->get_param( 'email' );
458 155 $api = new Optml_Api();
459 -
460 156 $user = $api->create_account( $email );
461 -
462 - $auto_connect = $request->get_param( 'auto_connect' );
463 -
464 - if ( ! empty( $auto_connect ) && $auto_connect === 'true' ) {
465 - delete_option( Optml_Settings::OPTML_USER_EMAIL );
157 + if ( $user === false ) {
158 + return new WP_Error( 'error', 'Error creating account.' );
466 159 }
467 - if ( $user === false || is_wp_error( $user ) ) {
468 - if ( $user->get_error_code() === 'domain_not_accessible' ) {
469 - return new WP_REST_Response(
470 - [
471 - 'data' => null,
472 - 'message' => $user->get_error_message(),
473 - 'code' => 'domain_not_accessible',
474 - ],
475 - 200
476 - );
477 - }
478 160
479 - return new WP_REST_Response(
480 - [
481 - 'data' => null,
482 - 'message' => __( 'Error creating account.', 'optimole-wp' ) . ' ' . $user->get_error_message(),
483 - 'code' => 'error',
484 - ],
485 - 200
486 - );
487 - }
488 - if ( $user === 'email_registered' ) {
489 - return new WP_REST_Response(
490 - [
491 - 'data' => null,
492 - 'message' => __( 'Error: This email is already registered. Please choose another one.', 'optimole-wp' ),
493 - 'code' => 'email_registered',
494 - ],
495 - 200
496 - );
497 -
498 - }
499 -
500 - if ( $user === 'site_exists' ) {
501 - return new WP_REST_Response(
502 - [
503 - 'data' => null,
504 - 'message' => sprintf( /* translators: 1 start anchor tag to register page, 2 is ending anchor tag. */ __( 'Error: This site has been previously registered. You can login to your account from %1$shere%2$s', 'optimole-wp' ), '<a href="' . esc_url( tsdk_translate_link( 'https://dashboard.optimole.com/login', 'query' ) ) . '" target="_blank"> ', '</a>' ),
505 - 'code' => 'site_exists',
506 - ],
507 - 200
508 - );
509 - }
510 -
511 - $user_data = $user['res'];
512 -
513 - $settings = new Optml_Settings();
514 - $settings->update( 'api_key', $user_data['api_key'] );
515 -
516 - if ( $user_data['app_count'] === 1 ) {
517 - $settings->update( 'service_data', $user_data );
518 - }
519 -
520 - if ( isset( $user_data['extra_visits'] ) ) {
521 - $settings->update_frontend_banner_from_remote( $user_data['extra_visits'] );
522 - }
523 -
524 - return $this->response( $user_data );
161 + return $this->response( $user );
525 162 }
526 163
527 164 /**
528 165 * Return image samples.
@@ -527,9 +164,8 @@
527 164 /**
528 165 * Return image samples.
529 166 *
530 167 * @param WP_REST_Request $request Rest request.
531 - * @phpstan-param WP_REST_Request<array{quality?: int, force?: string}> $request
532 168 *
533 169 * @return WP_REST_Response Image urls.
534 170 */
535 171 public function get_sample_rate( WP_REST_Request $request ) {
@@ -534,155 +170,37 @@
534 170 */
535 171 public function get_sample_rate( WP_REST_Request $request ) {
536 172
537 173 add_filter( 'optml_dont_replace_url', '__return_true' );
538 - $image_sample = get_transient( 'optimole_sample_image' );
539 - if ( $image_sample === false || $request->get_param( 'force' ) === 'yes' ) {
540 - $image_sample = $this->fetch_sample_image();
541 - set_transient( 'optimole_sample_image', $image_sample );
542 - }
543 - $image = [ 'id' => $image_sample['id'] ];
544 174
545 - $image['original'] = $image_sample['url'];
546 -
547 - remove_filter( 'optml_dont_replace_url', '__return_true' );
548 -
549 - $image['optimized'] = apply_filters(
550 - 'optml_replace_image',
551 - $image['original'],
552 - [
553 - 'width' => $image_sample['width'],
554 - 'height' => $image_sample['height'],
555 - 'quality' => $request->get_param( 'quality' ),
556 - ]
175 + $accepted_mimes = array( 'image/jpeg', 'image/png' );
176 + $args = array(
177 + 'post_type' => 'attachment',
178 + 'post_status' => 'any',
179 + 'number' => '5',
180 + 'no_found_rows' => true,
181 + 'fields' => 'ids',
182 + 'post_mime_type' => $accepted_mimes,
557 183 );
558 -
559 - $optimized = wp_remote_get(
560 - $image['optimized'],
561 - [
562 - 'timeout' => 10,
563 - 'headers' => [
564 - 'Accept' => 'text/html,application/xhtml+xml,image/webp,image/apng ',
565 - ],
566 - ]
567 - );
568 -
569 - $original = wp_remote_get( $image['original'] );
570 -
571 - $image['optimized_size'] = (int) wp_remote_retrieve_header( $optimized, 'content-length' );
572 - $image['original_size'] = (int) wp_remote_retrieve_header( $original, 'content-length' );
573 -
574 - return $this->response( $image );
575 - }
576 -
577 - /**
578 - * Crawl & upload initial load.
579 - *
580 - * @param WP_REST_Request $request Rest request.
581 - * @phpstan-param WP_REST_Request<array{offset?: int}> $request
582 - *
583 - * @return WP_REST_Response If there are more posts left to receive.
584 - */
585 - public function upload_onboard_images( WP_REST_Request $request ) {
586 - $offset = absint( $request->get_param( 'offset' ) );
587 -
588 - // Arguments for get_posts function
589 - $args = [
590 - 'post_type' => 'attachment',
591 - 'post_mime_type' => 'image',
592 - 'post_status' => 'inherit',
593 - 'posts_per_page' => 100,
594 - 'offset' => $offset,
595 - 'fields' => 'ids',
596 - 'no_found_rows' => true,
597 - 'update_post_meta_cache' => false,
598 - 'update_post_term_cache' => false,
599 - 'orderby' => [
600 - 'parent' => 'DESC',
601 - ],
602 - ];
603 -
604 - // Initialize an array to store the image URLs
605 - $image_urls = [];
606 -
607 - add_filter( 'optml_dont_replace_url', '__return_true' );
608 -
609 - // If site has a logo, get the logo url if offset is 0, ie first request
610 - if ( $offset === 0 ) {
611 - $custom_logo_id = get_theme_mod( 'custom_logo' );
612 -
613 - if ( $custom_logo_id ) {
614 - $image_urls[] = wp_get_attachment_url( $custom_logo_id );
615 - }
616 - }
617 -
618 - $query = new \WP_Query( $args );
619 -
620 - if ( $query->have_posts() ) {
621 - $image_ids = $query->get_posts();
622 - $image_urls = array_map( 'wp_get_attachment_url', $image_ids );
623 - }
624 - remove_filter( 'optml_dont_replace_url', '__return_true' );
625 -
626 - if ( count( $image_urls ) === 0 ) {
627 - return $this->response( true );
628 - }
629 -
630 - $api = new Optml_Api();
631 - $api->call_onboard_api( $image_urls );
632 -
633 - // Check if image_url array has at least 100 items, if not, we have reached the end of the images
634 - return $this->response( count( $image_urls ) < 100 ? true : false );
635 - }
636 -
637 - /**
638 - * Return sample image data.
639 - *
640 - * @return array{url: string, width: string|int, height: string|int, id: int} Image data.
641 - */
642 - private function fetch_sample_image() {
643 - $accepted_mimes = [ 'image/jpeg' ];
644 - $args = [
645 - 'post_type' => 'attachment',
646 - 'post_status' => 'any',
647 - 'number' => '5',
648 - 'no_found_rows' => true,
649 - 'fields' => 'ids',
650 - 'post_mime_type' => $accepted_mimes,
651 - 'post_parent__not_in' => [ 0 ],
652 - ];
653 184 $image_result = new WP_Query( $args );
654 185 if ( empty( $image_result->posts ) ) {
655 - $rand_id = rand( 1, 3 );
656 - $original_image_url = OPTML_URL . 'assets/img/' . $rand_id . '.jpg';
657 -
658 - return [
659 - 'url' => $original_image_url,
660 - 'width' => '700',
661 - 'height' => '465',
662 - 'id' => - 1,
663 - ];
186 + return $this->response( array() );
664 187 }
665 - $attachment_id = $image_result->posts[ array_rand( $image_result->posts, 1 ) ];
666 188
667 - $original_image_url = wp_get_attachment_image_url( $attachment_id, 'full' );
189 + $image = array(
190 + 'id' => $image_result->posts [ array_rand( $image_result->posts, 1 ) ],
191 + );
192 + $image['original'] = wp_get_attachment_image_url( $image['id'], 'full' );
668 193
669 - $metadata = wp_get_attachment_metadata( $attachment_id );
194 + remove_filter( 'optml_dont_replace_url', '__return_true' );
670 195
671 - $width = 'auto';
672 - $height = 'auto';
673 - $size = 'full';
674 - if ( isset( $metadata['sizes'] ) && isset( $metadata['sizes'][ $size ] ) ) {
675 - $width = $metadata['sizes'][ $size ]['width'];
676 - $height = $metadata['sizes'][ $size ]['height'];
677 - }
196 + $image['optimized'] = apply_filters( 'optml_replace_image', $image['original'], array(
197 + 'width' => 'auto',
198 + 'height' => 'auto',
199 + 'quality' => $request->get_param( 'quality' )
200 + ) );
678 201
679 - return [
680 - 'url' => $original_image_url,
681 - 'id' => $attachment_id,
682 - 'width' => $width,
683 - 'height' => $height,
684 - ];
202 + return $this->response( $image );
685 203 }
686 204
687 205 /**
688 206 * Disconnect from optimole service.
@@ -687,10 +205,8 @@
687 205 /**
688 206 * Disconnect from optimole service.
689 207 *
690 208 * @param WP_REST_Request $request disconnect rest request.
691 - *
692 - * @return void
693 209 */
694 210 public function disconnect( WP_REST_Request $request ) {
695 211 $settings = new Optml_Settings();
696 212 $settings->reset();
@@ -700,9 +216,8 @@
700 216 /**
701 217 * Get optimized images from API.
702 218 *
703 219 * @param WP_REST_Request $request rest request.
704 - * @phpstan-param WP_REST_Request<array{api_key?: string}> $request
705 220 *
706 221 * @return WP_REST_Response
707 222 */
708 223 public function poll_optimized_images( WP_REST_Request $request ) {
@@ -708,421 +223,58 @@
708 223 public function poll_optimized_images( WP_REST_Request $request ) {
709 224 $api_key = $request->get_param( 'api_key' );
710 225 $request = new Optml_Api();
711 226 $images = $request->get_optimized_images( $api_key );
712 - if ( is_wp_error( $images ) || ! is_array( $images ) || empty( $images['list'] ) ) {
713 - return $this->response( [] );
227 + if ( ! isset ( $images['list'] ) || empty( $images['list'] ) ) {
228 + return $this->response( array() );
714 229 }
715 230
716 231 $final_images = array_splice( $images['list'], 0, 10 );
717 232
718 - foreach ( $final_images as $index => $value ) {
719 - $final_images[ $index ]['url'] = Optml_Media_Offload::instance()->get_media_optimized_url(
720 - $value['url'],
721 - strtolower( $value['key'] ),
722 - 140,
723 - 140,
724 - [
725 - 'type' => ResizeTypeProperty::FILL,
726 - 'enlarge' => false,
727 - 'gravity' => Position::CENTER,
728 - ]
729 - );
730 - unset( $final_images[ $index ]['key'] );
731 - }
732 -
733 233 return $this->response( $final_images );
734 234 }
735 235
736 -
737 236 /**
738 - * Get conflicts from API.
739 - *
740 - * @param WP_REST_Request $request rest request.
741 - *
742 - * @return WP_REST_Response
743 - */
744 - public function poll_conflicts( WP_REST_Request $request ) {
745 - $conflicts_to_register = apply_filters( 'optml_register_conflicts', [] );
746 - $manager = new Optml_Conflict_Manager( $conflicts_to_register );
747 -
748 - return $this->response(
749 - [
750 - 'count' => $manager->get_conflict_count(),
751 - 'conflicts' => $manager->get_conflict_list(),
752 - ]
753 - );
754 - }
755 -
756 - /**
757 - * Dismiss conflict.
758 - *
759 - * @param WP_REST_Request $request rest request.
760 - * @phpstan-param WP_REST_Request<array{conflictID?: string}> $request
761 - *
762 - * @return WP_REST_Response
763 - */
764 - public function dismiss_conflict( WP_REST_Request $request ) {
765 - $conflict_id = $request->get_param( 'conflictID' );
766 - $conflicts_to_register = apply_filters( 'optml_register_conflicts', [] );
767 - $manager = new Optml_Conflict_Manager( $conflicts_to_register );
768 - $manager->dismiss_conflict( $conflict_id );
769 -
770 - return $this->response(
771 - [
772 - 'count' => $manager->get_conflict_count(),
773 - 'conflicts' => $manager->get_conflict_list(),
774 - ]
775 - );
776 - }
777 -
778 - /**
779 - * Request stats update for app.
780 - *
781 - * @param WP_REST_Request $request rest request.
782 - *
783 - * @return WP_REST_Response
784 - */
785 - public function request_update( WP_REST_Request $request ) {
786 - do_action( 'optml_daily_sync' );
787 - $settings = new Optml_Settings();
788 - $service_data = $settings->get( 'service_data' );
789 - if ( empty( $service_data ) ) {
790 - return $this->response( '', 'disconnected' );
791 - }
792 - return $this->response( $service_data );
793 - }
794 -
795 - /**
796 237 * Update options method.
797 238 *
798 239 * @param WP_REST_Request $request option update rest request.
799 - * @phpstan-param WP_REST_Request<array{settings?: array<string, mixed>}> $request
800 - *
801 - * @return WP_REST_Response
802 240 */
803 241 public function update_option( WP_REST_Request $request ) {
804 - $new_settings = $request->get_param( 'settings' );
805 -
806 - if ( empty( $new_settings ) ) {
242 + $option_key = $request->get_param( 'option_key' );
243 + $option_type = $request->get_param( 'type' );
244 + $option_value = $request->get_param( 'option_value' );
245 + if ( empty( $option_key ) ) {
807 246 wp_send_json_error( 'No option key set.' );
808 247 }
809 -
810 - $settings = new Optml_Settings();
811 - $sanitized = $settings->parse_settings( $new_settings );
812 -
813 - return $this->response( $sanitized );
814 - }
815 -
816 - /**
817 - * Get total number of images.
818 - *
819 - * @param WP_REST_Request $request rest request object.
820 - * @phpstan-param WP_REST_Request<array{action?: string, refresh?: bool}> $request
821 - *
822 - * @return WP_REST_Response
823 - */
824 - public function number_of_images_and_pages( WP_REST_Request $request ) {
825 - $action = 'offload_images';
826 - $refresh = false;
827 -
828 - if ( ! empty( $request->get_param( 'action' ) ) ) {
829 - $action = $request->get_param( 'action' );
248 + if ( empty( $option_type ) ) {
249 + wp_send_json_error( 'No option type set.' );
830 250 }
831 251
832 - if ( ! empty( $request->get_param( 'refresh' ) ) ) {
833 - $refresh = $request->get_param( 'refresh' );
834 - }
252 + $accepted_types = array( 'toggle', 'enum' );
835 253
836 - return $this->response( Optml_Media_Offload::move_images( $action, $refresh ) );
837 - }
838 -
839 - /**
840 - * Clear the offload errors from previous offload attempts.
841 - *
842 - * @param WP_REST_Request $request Rest request object.
843 - *
844 - * @return WP_REST_Response
845 - */
846 - public function clear_offload_errors( WP_REST_Request $request ) {
847 - $action = $request->get_param( 'action' );
848 -
849 - if ( 'rollback_images' === $action ) {
850 - $delete_count = Optml_Media_Offload::clear_rollback_errors_meta();
851 - } else {
852 - $delete_count = Optml_Media_Offload::clear_offload_errors_meta();
254 + if ( ! in_array( $option_type, $accepted_types ) ) {
255 + wp_send_json_error( 'Invalid option type.' );
853 256 }
854 257
855 - return $this->response( [ 'success' => $delete_count ] );
856 - }
857 -
858 - /**
859 - * Get conflicts list.
860 - *
861 - * @param WP_REST_Request $request rest request object.
862 - *
863 - * @return WP_REST_Response
864 - */
865 - public function get_offload_conflicts( WP_REST_Request $request ) {
866 - $request = new Optml_Api();
867 - $decoded_list = $request->get_offload_conflicts();
868 - $active_conflicts = [];
869 - if ( isset( $decoded_list['plugins'] ) ) {
870 - foreach ( $decoded_list['plugins'] as $slug ) {
871 - if ( is_plugin_active( $slug ) ) {
872 - $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $slug );
873 - if ( ! empty( $plugin_data['Name'] ) ) {
874 - $active_conflicts[] = $plugin_data['Name'];
875 - }
876 - }
877 - }
878 - }
879 -
880 - return $this->response( $active_conflicts );
881 - }
882 -
883 - /**
884 - * Insert images request.
885 - *
886 - * @param WP_REST_Request $request insert images rest request.
887 - * @phpstan-param WP_REST_Request<array{images?: array<mixed>}> $request
888 - *
889 - * @return WP_REST_Response
890 - */
891 - public function insert_images( WP_REST_Request $request ) {
892 - $images = $request->get_param( 'images' );
893 -
894 - if ( ! is_array( $images ) || empty( $images ) ) {
895 - return $this->response( [ 'error' => 'No images found' ] );
896 - }
897 -
898 - $insert = Optml_Main::instance()->dam->insert_attachments( $images );
899 -
900 - return $this->response( $insert );
901 - }
902 -
903 - /**
904 - * Dismiss a notification (set the notification key to 'yes').
905 - *
906 - * @param WP_REST_Request $request the incoming request.
907 - * @phpstan-param WP_REST_Request<array{key?: string}> $request
908 - *
909 - * @return WP_REST_Response
910 - */
911 - public function dismiss_notice( WP_REST_Request $request ) {
912 - $key = $request->get_param( 'key' );
913 -
914 - if ( empty( $key ) ) {
915 - return $this->response( [ 'error' => 'Invalid key' ], 'error' );
916 - }
917 -
918 - $result = update_option( $key, 'yes' );
919 -
920 - if ( ! $result ) {
921 - return $this->response( [ 'error' => 'Could not dismiss notice' ], 'error' );
922 - }
923 -
924 - return $this->response( [ 'success' => 'Notice dismissed' ] );
925 - }
926 -
927 - /**
928 - * Store optimization data.
929 - *
930 - * @param WP_REST_Request $request Rest request.
931 - * @phpstan-param WP_REST_Request<array{d: int, a: array<mixed>, u: string, t?: int, h?: string, pu?: string, b?: array<string, array<string, list<string>>>, l?: array{i?: string, s?: string, u?: list<string>}, m?: array<int, array{w: int, h: int}>, s?: array<int, list<array{w: int, h: int, d: int, s: string, b: int}>>, c?: array<int, bool>}> $request
932 - *
933 - * @return WP_REST_Response
934 - */
935 - public function optimizations( WP_REST_Request $request ) {
936 258 $settings = new Optml_Settings();
937 - if ( ! Optml_Manager::should_load_profiler() ) {
938 - return $this->response( 'Optimization is not enabled', 'error' );
939 - }
940 - $time = $request->get_param( 't' );
941 - $hmac = $request->get_param( 'h' );
942 - $current_url = $request->get_param( 'pu' );
943 - if ( empty( $time ) || empty( $hmac ) ) {
944 - return $this->response( 'Missing required parameters', 'error' );
945 - }
946 259
947 - $device_type = (int) $request->get_param( 'd' );
948 - $above_fold_images = $request->get_param( 'a' );
949 - $url = $request->get_param( 'u' );
950 - if ( $time < time() - 300 ) {
951 - return $this->response( 'Invalid Signature.', 'error' );
952 - }
953 - if ( wp_hash( $url . $time . $current_url, 'nonce' ) !== $hmac ) {
954 - return $this->response( 'Invalid Signature.', 'error' );
955 - }
956 - $bg_selectors = $request->get_param( 'b' );
957 - $lcp_data = $request->get_param( 'l' );
958 - $crop_status = $request->get_param( 'c' );
959 - $origin = $request->get_header( 'origin' );
960 - if ( empty( $origin ) || ! is_allowed_http_origin( $origin ) ) {
961 - return $this->response( 'Invalid origin', 'error' );
962 - }
963 - if ( empty( $device_type ) || empty( $url ) || ! is_array( $above_fold_images ) ) {
964 - return $this->response( 'Missing required parameters', 'error' );
965 - }
966 - // save just the first 6 images, see https://github.com/Codeinwp/optimole-service/issues/1588#issuecomment-3357110865
967 - $above_fold_images = array_slice( $above_fold_images, 0, 6 );
968 - $above_fold_images = array_values( array_map( 'intval', array_filter( $above_fold_images, 'is_numeric' ) ) );
969 - if ( count( $bg_selectors ) > 100 ) {
970 - return $this->response( 'Background selectors limit exceeded', 'error' );
971 - }
972 - if ( $url === Profile::PLACEHOLDER ) {
973 - return $this->response( 'Missing profile parameters', 'error' );
974 - }
975 -
976 - $current_selectors = array_values( Optml_Lazyload_Replacer::get_background_lazyload_selectors() );
977 - $sanitized_selectors = [];
978 - foreach ( $bg_selectors as $selector => $above_fold_bg_selectors ) {
979 - if ( ! in_array( $selector, $current_selectors, true ) ) {
980 - return $this->response( 'Invalid background selector', 'error' );
981 - }
982 - if ( count( $above_fold_bg_selectors ) > 100 ) {
983 - return $this->response( 'Above fold background selectors limit exceeded', 'error' );
984 - }
985 - $selector = strip_tags( $selector );
986 - $sanitized_selectors[ $selector ] = [];
987 - foreach ( $above_fold_bg_selectors as $above_fold_bg_selector => $bg_urls ) {
988 - if ( count( $bg_urls ) > 3 ) {
989 - return $this->response( 'Background URLs limit exceeded', 'error' );
260 + switch ( $option_type ) {
261 + case 'toggle':
262 + $status = $settings->get( $option_key );
263 + if ( $status === 'enabled' ) {
264 + $settings->update( $option_key, 'disabled' );
265 + wp_send_json_success( $option_key . ' disabled.' );
266 + } else {
267 + $settings->update( $option_key, 'enabled' );
268 + wp_send_json_success( $option_key . ' enabled.' );
990 269 }
991 - $sanitized_selectors[ $selector ][ strip_tags( $above_fold_bg_selector ) ] = array_filter(
992 - array_map( 'sanitize_url', array_values( $bg_urls ) ),
993 - function ( $url ) {
994 - // we ignore urls that are not from our service
995 - return strpos( $url, Optml_Config::$service_url ) === 0;
996 - }
997 - );
998 - }
270 + break;
271 + case 'enum':
272 + $settings->update( $option_key, $option_value );
273 + wp_send_json_success( $option_key . ' saved to ' . $option_value );
274 + break;
275 + default:
276 + break;
999 277 }
1000 - $missing_dimensions = $request->get_param( 'm' );
1001 - $sanitized_missing_dimensions = [];
1002 - if ( ! empty( $missing_dimensions ) ) {
1003 - foreach ( $missing_dimensions as $id => $dimension ) {
1004 - $sanitized_missing_dimensions[ intval( $id ) ] = [ 'w' => intval( $dimension['w'] ), 'h' => intval( $dimension['h'] ) ];
1005 - }
1006 - }
1007 - $missing_srcsets = $request->get_param( 's' );
1008 - $sanitized_missing_srcsets = [];
1009 - if ( ! empty( $missing_srcsets ) ) {
1010 - foreach ( $missing_srcsets as $id => $srcset ) {
1011 - foreach ( $srcset as $size ) {
1012 - if ( ! isset( $size['w'], $size['h'], $size['d'], $size['s'], $size['b'] ) ) {
1013 - continue;
1014 - }
1015 - $sanitized_missing_srcsets[ intval( $id ) ][ intval( $size['w'] ) ] = [ 'h' => intval( $size['h'] ), 'w' => intval( $size['w'] ), 'd' => intval( $size['d'] ), 's' => sanitize_text_field( $size['s'] ), 'b' => intval( $size['b'] ) ];
1016 - }
1017 - }
1018 - }
1019 - $sanitized_crop_status = [];
1020 - if ( ! empty( $crop_status ) ) {
1021 - foreach ( $crop_status as $id => $crop ) {
1022 - $sanitized_crop_status[ intval( $id ) ] = (bool) $crop;
1023 - }
1024 - }
1025 - $sanitized_lcp_data = [];
1026 - if ( ! empty( $lcp_data ) ) {
1027 - $sanitized_lcp_data['imageId'] = sanitize_text_field( $lcp_data['i'] ?? '' );
1028 - $sanitized_lcp_data['bgSelector'] = sanitize_text_field( $lcp_data['s'] ?? '' );
1029 - if ( count( $lcp_data['u'] ?? [] ) > 3 ) {
1030 - return $this->response( 'LCP Background URLs limit exceeded', 'error' );
1031 - }
1032 - $sanitized_lcp_data['bgUrls'] = array_filter(
1033 - array_map( 'sanitize_url', array_values( $lcp_data['u'] ?? [] ) ),
1034 - function ( $url ) {
1035 - // we ignore urls that are not from our service
1036 - return strpos( $url, Optml_Config::$service_url ) === 0;
1037 - }
1038 - );
1039 - $sanitized_lcp_data['type'] = empty( $sanitized_lcp_data['imageId'] ) ? 'bg' : 'img';
1040 - }
1041 -
1042 - if ( OPTML_DEBUG ) {
1043 - do_action( 'optml_log', 'Storing profile data: ' . $url . ' - ' . $device_type . ' - ' . print_r( $above_fold_images, true ) . print_r( $sanitized_selectors, true ) . print_r( $sanitized_lcp_data, true ) . print_r( $sanitized_missing_dimensions, true ) . print_r( $sanitized_missing_srcsets, true ) . print_r( $sanitized_crop_status, true ) );
1044 - }
1045 - $profile = new Profile();
1046 - $profile->store( $url, $device_type, $above_fold_images, $sanitized_selectors, $sanitized_lcp_data, $sanitized_missing_dimensions, $sanitized_missing_srcsets, $sanitized_crop_status );
1047 -
1048 - if ( $profile->exists_all( $url ) ) {
1049 - /**
1050 - * Clear cache when storing profile data.
1051 - *
1052 - * @var string $url The url to clear the cache for.
1053 - */
1054 - do_action( 'optml_clear_cache', $current_url );
1055 - }
1056 - return $this->response( 'Optimization data stored successfully' );
1057 278 }
1058 279
1059 - /**
1060 - * Method to register above fold data routes.
1061 - *
1062 - * @return void
1063 - */
1064 - public function register_optimization_routes() {
1065 - foreach ( self::$rest_routes['optimization_routes'] as $route => $details ) {
1066 - $this->register_route( $route, $details[0], $details['args'], $details['permission_callback'] );
1067 - }
1068 - }
1069 -
1070 - /**
1071 - * Move image.
1072 - *
1073 - * @param WP_REST_Request $request Rest request.
1074 - * @phpstan-param WP_REST_Request<array{id: int, action: string, status?: string}> $request
1075 - *
1076 - * @return WP_REST_Response
1077 - */
1078 - public function move_image( WP_REST_Request $request ) {
1079 - $id = $request->get_param( 'id' );
1080 - $action = $request->get_param( 'action' );
1081 -
1082 - if ( $request->get_param( 'status' ) === 'start' ) {
1083 - try {
1084 - Optml_Media_Offload::instance()->move_single_image( $action === 'offload_image' ? 'offload_images' : 'rollback_images', $id );
1085 - } catch ( Exception $e ) {
1086 - return $this->response( strip_tags( $e->getMessage() ), 'error' );
1087 - }
1088 - }
1089 -
1090 - $meta = wp_get_attachment_metadata( $id );
1091 - $file = $meta['file'];
1092 - $result = 'not_moved';
1093 - if ( $action === 'offload_image' ) {
1094 - $result = Optml_Media_Offload::is_uploaded_image( $file ) ? 'moved' : 'not_moved';
1095 - }
1096 -
1097 - if ( $action === 'rollback_image' ) {
1098 - $result = ! Optml_Media_Offload::is_uploaded_image( $file ) ? 'moved' : 'not_moved';
1099 - }
1100 -
1101 - return $this->response( [ $id,$action ], $result );
1102 - }
1103 -
1104 - /**
1105 - * Check if user can move image to/from cloud.
1106 - *
1107 - * @param WP_REST_Request $request Rest request.
1108 - * @phpstan-param WP_REST_Request<array{id: int, action: string}> $request Rest request.
1109 - * @return bool True if user can move image, false otherwise.
1110 - */
1111 - public static function can_move_image( WP_REST_Request $request ) {
1112 -
1113 - if ( ! current_user_can( 'upload_files' ) ) {
1114 - return false;
1115 - }
1116 -
1117 - $id = $request->get_param( 'id' );
1118 -
1119 - if (
1120 - get_post_type( $id ) !== 'attachment' ||
1121 - ! current_user_can( 'edit_post', $id )
1122 - ) {
1123 - return false;
1124 - }
1125 -
1126 - return true;
1127 - }
1128 280 }