PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / trunk
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization vtrunk
4.2.14 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 All 135 releases
← All changes | inc/rest.php +707 -413 3.1.0 → trunk View file →
@@ -6,8 +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 +
10 14 /**
11 15 * Class Optml_Rest
12 16 *
13 17 * @codeCoverageIgnore
@@ -12,8 +16,9 @@
12 16 *
13 17 * @codeCoverageIgnore
14 18 */
15 19 class Optml_Rest {
20 +
16 21 /**
17 22 * Rest api namespace.
18 23 *
19 24 * @var string Namespace.
@@ -20,351 +25,340 @@
20 25 */
21 26 private $namespace;
22 27
23 28 /**
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 + /**
24 165 * Optml_Rest constructor.
166 + *
167 + * @return void
25 168 */
26 169 public function __construct() {
27 170 $this->namespace = OPTML_NAMESPACE . '/v1';
171 +
28 172 add_action( 'rest_api_init', [ $this, 'register' ] );
29 173 }
30 174
31 175 /**
32 - * Register rest routes.
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
33 188 */
34 - public function register() {
189 + private function register_route( $route, $method = 'GET', $args = [], $permission_callback = 'manage_options' ) {
190 + if ( empty( $route ) ) {
191 + return;
192 + }
35 193
36 - $this->register_service_routes();
194 + $method_map = [
195 + 'GET' => \WP_REST_Server::READABLE,
196 + 'POST' => \WP_REST_Server::CREATABLE,
197 + 'POST_PUT_PATCH' => \WP_REST_Server::EDITABLE,
198 + ];
37 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 +
38 228 register_rest_route(
39 229 $this->namespace,
40 - '/update_option',
41 - [
42 - [
43 - 'methods' => \WP_REST_Server::CREATABLE,
44 - 'permission_callback' => function () {
45 - return current_user_can( 'manage_options' );
46 - },
47 - 'callback' => [ $this, 'update_option' ],
48 - ],
49 - ]
230 + '/' . $route,
231 + [ $params ]
50 232 );
233 + }
51 234
52 - register_rest_route(
53 - $this->namespace,
54 - '/request_update',
55 - [
56 - [
57 - 'methods' => \WP_REST_Server::READABLE,
58 - 'permission_callback' => function () {
59 - return current_user_can( 'manage_options' );
60 - },
61 - 'callback' => [ $this, 'request_update' ],
62 - ],
63 - ]
64 - );
65 - register_rest_route(
66 - $this->namespace,
67 - '/check_redirects',
68 - [
69 - [
70 - 'methods' => \WP_REST_Server::EDITABLE,
71 - 'permission_callback' => function () {
72 - return current_user_can( 'manage_options' );
73 - },
74 - 'callback' => [ $this, 'check_redirects' ],
75 - ],
76 - ]
77 - );
235 + /**
236 + * Register rest routes.
237 + *
238 + * @return void
239 + */
240 + public function register() {
78 241
242 + $this->register_service_routes();
243 +
79 244 $this->register_image_routes();
80 - $this->register_watermark_routes();
81 245 $this->register_conflict_routes();
82 246 $this->register_cache_routes();
83 247 $this->register_media_offload_routes();
248 + $this->register_dam_routes();
249 + $this->register_notification_routes();
250 + $this->register_optimization_routes();
84 251 }
85 252
86 253 /**
87 254 * Method to register service specific routes.
255 + *
256 + * @return void
88 257 */
89 258 public function register_service_routes() {
90 - register_rest_route(
91 - $this->namespace,
92 - '/connect',
93 - [
94 - [
95 - 'methods' => \WP_REST_Server::CREATABLE,
96 - 'permission_callback' => function () {
97 - return current_user_can( 'manage_options' );
98 - },
99 - 'callback' => [ $this, 'connect' ],
100 - 'args' => [
101 - 'api_key' => [
102 - 'type' => 'string',
103 - 'required' => true,
104 - ],
105 - ],
106 - ],
107 - ]
108 - );
109 - register_rest_route(
110 - $this->namespace,
111 - '/select',
112 - [
113 - [
114 - 'methods' => \WP_REST_Server::CREATABLE,
115 - 'permission_callback' => function () {
116 - return current_user_can( 'manage_options' );
117 - },
118 - 'callback' => [ $this, 'select_application' ],
119 - 'args' => [
120 - 'api_key' => [
121 - 'type' => 'string',
122 - 'required' => true,
123 - ],
124 - 'application' => [
125 - 'type' => 'string',
126 - 'required' => true,
127 - ],
128 - ],
129 - ],
130 - ]
131 - );
132 - register_rest_route(
133 - $this->namespace,
134 - '/register',
135 - [
136 - [
137 - 'methods' => \WP_REST_Server::CREATABLE,
138 - 'permission_callback' => function () {
139 - return current_user_can( 'manage_options' );
140 - },
141 - 'callback' => [ $this, 'register_service' ],
142 - 'args' => [
143 - 'email' => [
144 - 'type' => 'string',
145 - 'required' => true,
146 - ],
147 - ],
148 - ],
149 - ]
150 - );
151 - register_rest_route(
152 - $this->namespace,
153 - '/disconnect',
154 - [
155 - [
156 - 'methods' => \WP_REST_Server::READABLE,
157 - 'permission_callback' => function () {
158 - return current_user_can( 'manage_options' );
159 - },
160 - 'callback' => [ $this, 'disconnect' ],
161 - ],
162 - ]
163 - );
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 + }
164 266 }
165 267
166 268 /**
167 269 * Method to register image specific routes.
270 + *
271 + * @return void
168 272 */
169 273 public function register_image_routes() {
170 - register_rest_route(
171 - $this->namespace,
172 - '/poll_optimized_images',
173 - [
174 - [
175 - 'methods' => \WP_REST_Server::READABLE,
176 - 'permission_callback' => function () {
177 - return current_user_can( 'manage_options' );
178 - },
179 - 'callback' => [ $this, 'poll_optimized_images' ],
180 - ],
181 - ]
182 - );
183 - register_rest_route(
184 - $this->namespace,
185 - '/images-sample-rate',
186 - [
187 - [
188 - 'methods' => \WP_REST_Server::CREATABLE,
189 - 'permission_callback' => function () {
190 - return current_user_can( 'manage_options' );
191 - },
192 - 'callback' => [ $this, 'get_sample_rate' ],
193 - ],
194 - ]
195 - );
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 + }
196 281 }
197 282 /**
198 283 * Method to register media offload specific routes.
284 + *
285 + * @return void
199 286 */
200 287 public function register_media_offload_routes() {
201 - register_rest_route(
202 - $this->namespace,
203 - '/offload_images',
204 - [
205 - [
206 - 'methods' => \WP_REST_Server::CREATABLE,
207 - 'permission_callback' => function () {
208 - return current_user_can( 'manage_options' );
209 - },
210 - 'callback' => [ $this, 'offload_images' ],
211 - ],
212 - ]
213 - );
214 - register_rest_route(
215 - $this->namespace,
216 - '/rollback_images',
217 - [
218 - [
219 - 'methods' => \WP_REST_Server::CREATABLE,
220 - 'permission_callback' => function () {
221 - return current_user_can( 'manage_options' );
222 - },
223 - 'callback' => [ $this, 'rollback_images' ],
224 - ],
225 - ]
226 - );
227 - register_rest_route(
228 - $this->namespace,
229 - '/number_of_library_images',
230 - [
231 - [
232 - 'methods' => \WP_REST_Server::CREATABLE,
233 - 'permission_callback' => function () {
234 - return current_user_can( 'manage_options' );
235 - },
236 - 'callback' => [ $this, 'number_of_library_images' ],
237 - ],
238 - ]
239 - );
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 + }
240 294 }
241 295
242 - /**
243 - * Method to register watermark specific routes.
244 - */
245 - public function register_watermark_routes() {
246 - register_rest_route(
247 - $this->namespace,
248 - '/poll_watermarks',
249 - [
250 - [
251 - 'methods' => \WP_REST_Server::READABLE,
252 - 'permission_callback' => function () {
253 - return current_user_can( 'manage_options' );
254 - },
255 - 'callback' => [ $this, 'poll_watermarks' ],
256 - ],
257 - ]
258 - );
259 - register_rest_route(
260 - $this->namespace,
261 - '/add_watermark',
262 - [
263 - [
264 - 'methods' => \WP_REST_Server::CREATABLE,
265 - 'permission_callback' => function () {
266 - return current_user_can( 'manage_options' );
267 - },
268 - 'callback' => [ $this, 'add_watermark' ],
269 - ],
270 - ]
271 - );
272 - register_rest_route(
273 - $this->namespace,
274 - '/remove_watermark',
275 - [
276 - [
277 - 'methods' => \WP_REST_Server::CREATABLE,
278 - 'permission_callback' => function () {
279 - return current_user_can( 'manage_options' );
280 - },
281 - 'callback' => [ $this, 'remove_watermark' ],
282 - ],
283 - ]
284 - );
285 - }
286 296
287 297 /**
288 298 * Method to register conflicts specific routes.
299 + *
300 + * @return void
289 301 */
290 302 public function register_conflict_routes() {
291 - register_rest_route(
292 - $this->namespace,
293 - '/poll_conflicts',
294 - [
295 - [
296 - 'methods' => \WP_REST_Server::READABLE,
297 - 'permission_callback' => function () {
298 - return current_user_can( 'manage_options' );
299 - },
300 - 'callback' => [ $this, 'poll_conflicts' ],
301 - ],
302 - ]
303 - );
304 - register_rest_route(
305 - $this->namespace,
306 - '/dismiss_conflict',
307 - [
308 - [
309 - 'methods' => \WP_REST_Server::CREATABLE,
310 - 'permission_callback' => function () {
311 - return current_user_can( 'manage_options' );
312 - },
313 - 'callback' => [ $this, 'dismiss_conflict' ],
314 - ],
315 - ]
316 - );
303 + foreach ( self::$rest_routes['conflict_routes'] as $route => $details ) {
304 + $this->register_route( $route, $details );
305 + }
317 306 }
318 307
319 308 /**
320 309 * Method to register cache specific routes.
310 + *
311 + * @return void
321 312 */
322 313 public function register_cache_routes() {
323 - register_rest_route(
324 - $this->namespace,
325 - '/clear_cache',
326 - [
327 - [
328 - 'methods' => \WP_REST_Server::CREATABLE,
329 - 'permission_callback' => function () {
330 - return current_user_can( 'manage_options' );
331 - },
332 - 'callback' => [ $this, 'clear_cache_request' ],
333 - ],
334 - ]
335 - );
314 + foreach ( self::$rest_routes['cache_routes'] as $route => $details ) {
315 + $this->register_route( $route, $details );
316 + }
336 317 }
337 318
338 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 + /**
339 344 * Clear Cache request.
340 345 *
341 346 * @param WP_REST_Request $request clear cache rest request.
347 + * @phpstan-param WP_REST_Request<array{type?: string}> $request
342 348 *
343 349 * @return WP_Error|WP_REST_Response
344 350 */
345 351 public function clear_cache_request( WP_REST_Request $request ) {
346 352 $settings = new Optml_Settings();
347 - $token = $settings->get( 'cache_buster' );
348 - $request = new Optml_Api();
349 - $data = $request->get_cache_token( $token );
350 - if ( $data === false || is_wp_error( $data ) || empty( $data ) || ! isset( $data['token'] ) ) {
351 - $extra = '';
352 - if ( is_wp_error( $data ) ) {
353 - /**
354 - * Error from api.
355 - *
356 - * @var WP_Error $data Error object.
357 - */
358 - $extra = sprintf( __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
359 - }
360 - wp_send_json_error( __( 'Can not get new token from Optimole service', 'optimole-wp' ) . $extra );
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() );
361 358 }
362 359
363 - set_transient( 'optml_cache_lock', 'yes', 5 * MINUTE_IN_SECONDS );
364 - $settings->update( 'cache_buster', $data['token'] );
365 -
366 - return $this->response( $data['token'], '200' );
360 + return $this->response( $response, '200' );
367 361 }
368 362
369 363 /**
370 364 * Connect to optimole service.
@@ -369,8 +363,9 @@
369 363 /**
370 364 * Connect to optimole service.
371 365 *
372 366 * @param WP_REST_Request $request connect rest request.
367 + * @phpstan-param WP_REST_Request<array{api_key: string, application?: string}> $request
373 368 *
374 369 * @return WP_Error|WP_REST_Response
375 370 */
376 371 public function connect( WP_REST_Request $request ) {
@@ -377,22 +372,32 @@
377 372 $api_key = $request->get_param( 'api_key' );
378 373 $original_request = $request;
379 374 $request = new Optml_Api();
380 375 $data = $request->connect( $api_key );
376 +
381 377 if ( $data === false || is_wp_error( $data ) ) {
378 +
382 379 $extra = '';
383 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 384 /**
385 385 * Error from api.
386 386 *
387 387 * @var WP_Error $data Error object.
388 388 */
389 - $extra = sprintf( __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
389 + $extra = sprintf( /* translators: Error details */ __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
390 390 }
391 - wp_send_json_error( __( 'Can not connect to Optimole service', 'optimole-wp' ) . $extra );
391 + return $this->response( __( 'Can not connect to Optimole service', 'optimole-wp' ) . $extra, 400 );
392 392 }
393 393 $settings = new Optml_Settings();
394 394 $settings->update( 'api_key', $api_key );
395 +
396 + if ( isset( $data['extra_visits'] ) ) {
397 + $settings->update_frontend_banner_from_remote( $data['extra_visits'] );
398 + }
399 +
395 400 if ( $data['app_count'] === 1 ) {
396 401 return $this->select_application( $original_request );
397 402 }
398 403 return $this->response( $data );
@@ -401,8 +406,9 @@
401 406 /**
402 407 * Select application.
403 408 *
404 409 * @param WP_REST_Request $request Rest request.
410 + * @phpstan-param WP_REST_Request<array{api_key: string, application?: string}> $request
405 411 *
406 412 * @return WP_REST_Response
407 413 */
408 414 public function select_application( WP_REST_Request $request ) {
@@ -417,9 +423,9 @@
417 423 * Error from api.
418 424 *
419 425 * @var WP_Error $data Error object.
420 426 */
421 - $extra = sprintf( __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
427 + $extra = sprintf( /* translators: Error details */ __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
422 428 }
423 429 wp_send_json_error( __( 'Can not connect to Optimole service', 'optimole-wp' ) . $extra );
424 430 }
425 431 $settings = new Optml_Settings();
@@ -429,9 +435,10 @@
429 435
430 436 /**
431 437 * Wrapper for api response.
432 438 *
433 - * @param mixed $data data from api.
439 + * @param mixed $data data from api.
440 + * @param string|int $code Response code.
434 441 *
435 442 * @return WP_REST_Response
436 443 */
437 444 private function response( $data, $code = 'success' ) {
@@ -441,8 +448,9 @@
441 448 /**
442 449 * Connect to optimole service.
443 450 *
444 451 * @param WP_REST_Request $request connect rest request.
452 + * @phpstan-param WP_REST_Request<array{email: string, auto_connect?: string}> $request
445 453 *
446 454 * @return WP_Error|WP_REST_Response
447 455 */
448 456 public function register_service( WP_REST_Request $request ) {
@@ -447,22 +455,74 @@
447 455 */
448 456 public function register_service( WP_REST_Request $request ) {
449 457 $email = $request->get_param( 'email' );
450 458 $api = new Optml_Api();
459 +
451 460 $user = $api->create_account( $email );
452 - if ( $user === false ) {
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 );
466 + }
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 +
453 479 return new WP_REST_Response(
454 480 [
455 481 'data' => null,
456 - 'message' => __( 'Error creating account.', 'optimole-wp' ),
482 + 'message' => __( 'Error creating account.', 'optimole-wp' ) . ' ' . $user->get_error_message(),
457 483 'code' => 'error',
458 484 ],
459 485 200
460 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 + );
461 497
462 498 }
463 499
464 - return $this->response( $user );
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 );
465 525 }
466 526
467 527 /**
468 528 * Return image samples.
@@ -467,8 +527,9 @@
467 527 /**
468 528 * Return image samples.
469 529 *
470 530 * @param WP_REST_Request $request Rest request.
531 + * @phpstan-param WP_REST_Request<array{quality?: int, force?: string}> $request
471 532 *
472 533 * @return WP_REST_Response Image urls.
473 534 */
474 535 public function get_sample_rate( WP_REST_Request $request ) {
@@ -513,11 +574,71 @@
513 574 return $this->response( $image );
514 575 }
515 576
516 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 + /**
517 638 * Return sample image data.
518 639 *
519 - * @return array Image data.
640 + * @return array{url: string, width: string|int, height: string|int, id: int} Image data.
520 641 */
521 642 private function fetch_sample_image() {
522 643 $accepted_mimes = [ 'image/jpeg' ];
523 644 $args = [
@@ -565,10 +686,11 @@
565 686
566 687 /**
567 688 * Disconnect from optimole service.
568 689 *
569 - * @SuppressWarnings(PHPMD.UnusedFormalParameter)
570 690 * @param WP_REST_Request $request disconnect rest request.
691 + *
692 + * @return void
571 693 */
572 694 public function disconnect( WP_REST_Request $request ) {
573 695 $settings = new Optml_Settings();
574 696 $settings->reset();
@@ -578,8 +700,9 @@
578 700 /**
579 701 * Get optimized images from API.
580 702 *
581 703 * @param WP_REST_Request $request rest request.
704 + * @phpstan-param WP_REST_Request<array{api_key?: string}> $request
582 705 *
583 706 * @return WP_REST_Response
584 707 */
585 708 public function poll_optimized_images( WP_REST_Request $request ) {
@@ -585,17 +708,26 @@
585 708 public function poll_optimized_images( WP_REST_Request $request ) {
586 709 $api_key = $request->get_param( 'api_key' );
587 710 $request = new Optml_Api();
588 711 $images = $request->get_optimized_images( $api_key );
589 - if ( ! isset( $images['list'] ) || empty( $images['list'] ) ) {
712 + if ( is_wp_error( $images ) || ! is_array( $images ) || empty( $images['list'] ) ) {
590 713 return $this->response( [] );
591 714 }
592 715
593 716 $final_images = array_splice( $images['list'], 0, 10 );
594 - $media = new Optml_Media_Offload();
595 717
596 718 foreach ( $final_images as $index => $value ) {
597 - $final_images[ $index ]['url'] = $media->get_media_optimized_url( $value['url'], $value['key'] );
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 + );
598 730 unset( $final_images[ $index ]['key'] );
599 731 }
600 732
601 733 return $this->response( $final_images );
@@ -600,61 +732,10 @@
600 732
601 733 return $this->response( $final_images );
602 734 }
603 735
604 - /**
605 - * Get watermarks from API.
606 - *
607 - * @param WP_REST_Request $request rest request.
608 - *
609 - * @return WP_REST_Response
610 - */
611 - public function poll_watermarks( WP_REST_Request $request ) {
612 - $api_key = $request->get_param( 'api_key' );
613 - $request = new Optml_Api();
614 - $watermarks = $request->get_watermarks( $api_key );
615 - if ( ! isset( $watermarks['watermarks'] ) || empty( $watermarks['watermarks'] ) ) {
616 - return $this->response( [] );
617 - }
618 - $final_images = array_splice( $watermarks['watermarks'], 0, 10 );
619 736
620 - return $this->response( $final_images );
621 - }
622 -
623 737 /**
624 - * Add watermark.
625 - *
626 - * @param WP_REST_Request $request rest request.
627 - *
628 - * @return WP_REST_Response
629 - */
630 - public function add_watermark( WP_REST_Request $request ) {
631 - $file = $request->get_file_params();
632 - $request = new Optml_Api();
633 - $response = $request->add_watermark( $file );
634 - if ( $response === false ) {
635 - return $this->response( __( 'Error uploading image. Please try again.', 'optimole-wp' ), 'error' );
636 - }
637 -
638 - return $this->response( __( 'Watermark image uploaded succesfully ! ', 'optimole-wp' ) );
639 - }
640 -
641 - /**
642 - * Remove watermark.
643 - *
644 - * @param WP_REST_Request $request rest request.
645 - *
646 - * @return WP_REST_Response
647 - */
648 - public function remove_watermark( WP_REST_Request $request ) {
649 - $post_id = $request->get_param( 'postID' );
650 - $api_key = $request->get_param( 'api_key' );
651 - $request = new Optml_Api();
652 -
653 - return $this->response( $request->remove_watermark( $post_id, $api_key ) );
654 - }
655 -
656 - /**
657 738 * Get conflicts from API.
658 739 *
659 740 * @param WP_REST_Request $request rest request.
660 741 *
@@ -675,8 +756,9 @@
675 756 /**
676 757 * Dismiss conflict.
677 758 *
678 759 * @param WP_REST_Request $request rest request.
760 + * @phpstan-param WP_REST_Request<array{conflictID?: string}> $request
679 761 *
680 762 * @return WP_REST_Response
681 763 */
682 764 public function dismiss_conflict( WP_REST_Request $request ) {
@@ -702,10 +784,13 @@
702 784 */
703 785 public function request_update( WP_REST_Request $request ) {
704 786 do_action( 'optml_daily_sync' );
705 787 $settings = new Optml_Settings();
706 -
707 - return $this->response( $settings->get( 'service_data' ) );
788 + $service_data = $settings->get( 'service_data' );
789 + if ( empty( $service_data ) ) {
790 + return $this->response( '', 'disconnected' );
791 + }
792 + return $this->response( $service_data );
708 793 }
709 794
710 795 /**
711 796 * Update options method.
@@ -710,8 +795,9 @@
710 795 /**
711 796 * Update options method.
712 797 *
713 798 * @param WP_REST_Request $request option update rest request.
799 + * @phpstan-param WP_REST_Request<array{settings?: array<string, mixed>}> $request
714 800 *
715 801 * @return WP_REST_Response
716 802 */
717 803 public function update_option( WP_REST_Request $request ) {
@@ -727,108 +813,316 @@
727 813 return $this->response( $sanitized );
728 814 }
729 815
730 816 /**
731 - * Update options method.
817 + * Get total number of images.
732 818 *
733 - * @param WP_REST_Request $request option update rest request.
819 + * @param WP_REST_Request $request rest request object.
820 + * @phpstan-param WP_REST_Request<array{action?: string, refresh?: bool}> $request
734 821 *
735 822 * @return WP_REST_Response
736 823 */
737 - public function check_redirects( WP_REST_Request $request ) {
738 - if ( empty( $request->get_param( 'images' ) ) ) {
739 - return $this->response( __( 'No images available on the current page.' ), 'noImagesFound' );
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' );
740 830 }
741 - // 'ok' if no issues found, 'log' is there are issues we need to notify, 'deactivated' if the user's account is disabled
742 - $status = 'ok';
743 - $result = '';
744 - foreach ( $request->get_param( 'images' ) as $domain => $value ) {
745 - $args = [
746 - 'method' => 'GET',
747 - 'redirection' => 0,
748 - ];
749 - $processed_images = 0;
750 - if ( isset( $value['src'] ) ) {
751 - $processed_images = count( $value['src'] );
752 - }
753 - if ( isset( $value['ignoredUrls'] ) && $value['ignoredUrls'] > $processed_images ) {
754 - $result .= '<li>❌ ' . sprintf( __( 'The images from: %1$s are not optimized by Optimole. If you would like to do so, you can follow this: %2$sWhy Optimole does not optimize all the images from my site?%3$s.', 'optimole-wp' ), $domain, '<a target="_blank" href="https://docs.optimole.com/article/1290-how-to-optimize-images-using-optimole-from-my-domain">', '</a>' ) . '</li>';
755 - $status = 'log';
756 - continue;
757 - }
758 831
759 - if ( $processed_images > 0 ) {
760 - $response = wp_remote_get( $value['src'][ rand( 0, $processed_images - 1 ) ], $args );
761 - if ( is_array( $response ) && ! is_wp_error( $response ) ) {
762 - $headers = $response['headers']; // array of http header lines
763 - $status_code = $response['response']['code'];
764 - if ( $status_code === 301 ) {
765 - $status = 'deactivated';
766 - $result = '<li>❌ ' . sprintf( __( 'Your account is currently disabled due to exceeding quota and Optimole is no longer able to optimize the images. In order to fix this you will need to %1$supgrade%2$s.', 'optimole-wp' ), '<a target="_blank" href="https://optimole.com/pricing">', '</a>' ) . '</li>';
767 - break;
832 + if ( ! empty( $request->get_param( 'refresh' ) ) ) {
833 + $refresh = $request->get_param( 'refresh' );
834 + }
835 +
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();
853 + }
854 +
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'];
768 875 }
769 - if ( $status_code === 302 ) {
770 - if ( isset( $headers['x-redirect-o'] ) ) {
771 - $optimole_code = (int) $headers['x-redirect-o'];
772 - if ( $optimole_code === 1 ) {
773 - $status = 'log';
774 - $result .= '<li>❌ ' . sprintf( __( 'The domain: %1$s is not allowed to optimize images using your Optimole account. You can add this to the allowed list %2$shere%3$s.', 'optimole-wp' ), '<b>' . $domain . '</b>', '<a target="_blank" href="https://dashboard.optimole.com/whitelist">', '</a>' ) . '</li>';
775 - }
776 - if ( $optimole_code === 4 ) {
777 - $status = 'log';
778 - $result .= '<li>❌ ' . sprintf( __( 'We are not able to download the images from %1$s. Please check %2$sthis%3$s document for a more advanced guide on how to solve this. ', 'optimole-wp' ), '<b>' . $domain . '</b>', '<a target="_blank" href="https://docs.optimole.com/article/1291-why-optimole-is-not-able-to-download-the-images-from-my-site">', '</a>' ) . '<br />' . '</li>';
779 - }
780 - }
781 - }
782 876 }
783 877 }
784 878 }
785 - if ( $result === '' ) {
786 - $result = __( 'No issues detected, everything is running smoothly.', 'optimole-wp' );
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' ] );
787 896 }
788 897
789 - return $this->response( '<ul>' . $result . '</ul>', $status );
898 + $insert = Optml_Main::instance()->dam->insert_attachments( $images );
899 +
900 + return $this->response( $insert );
790 901 }
791 902
792 903 /**
793 - * Push existing image our servers.
904 + * Dismiss a notification (set the notification key to 'yes').
794 905 *
795 - * @param WP_REST_Request $request rest request object.
906 + * @param WP_REST_Request $request the incoming request.
907 + * @phpstan-param WP_REST_Request<array{key?: string}> $request
796 908 *
797 909 * @return WP_REST_Response
798 910 */
799 - public function offload_images( WP_REST_Request $request ) {
800 - $batch = 300;
801 - if ( ! empty( $request->get_param( 'batch' ) ) ) {
802 - $batch = $request->get_param( 'batch' );
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' );
803 916 }
804 - return $this->response( Optml_Media_Offload::upload_images( $batch ) );
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' ] );
805 925 }
926 +
806 927 /**
807 - * Rollback images to media library.
928 + * Store optimization data.
808 929 *
809 - * @param WP_REST_Request $request rest request object.
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
810 932 *
811 933 * @return WP_REST_Response
812 934 */
813 - public function rollback_images( WP_REST_Request $request ) {
814 - $batch = 300;
815 - if ( ! empty( $request->get_param( 'batch' ) ) ) {
816 - $batch = $request->get_param( 'batch' );
935 + public function optimizations( WP_REST_Request $request ) {
936 + $settings = new Optml_Settings();
937 + if ( ! Optml_Manager::should_load_profiler() ) {
938 + return $this->response( 'Optimization is not enabled', 'error' );
817 939 }
818 - return $this->response( Optml_Media_Offload::rollback_images( $batch ) );
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 +
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' );
990 + }
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 + }
999 + }
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' );
819 1057 }
1058 +
820 1059 /**
821 - * Get total number of images.
1060 + * Method to register above fold data routes.
822 1061 *
823 - * @param WP_REST_Request $request rest request object.
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.
824 1072 *
1073 + * @param WP_REST_Request $request Rest request.
1074 + * @phpstan-param WP_REST_Request<array{id: int, action: string, status?: string}> $request
1075 + *
825 1076 * @return WP_REST_Response
826 1077 */
827 - public function number_of_library_images( WP_REST_Request $request ) {
828 - $action = 'offload_images';
829 - if ( ! empty( $request->get_param( 'action' ) ) ) {
830 - $action = $request->get_param( 'action' );
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 + }
831 1088 }
832 - return $this->response( Optml_Media_Offload::number_of_library_images( $action ) );
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;
833 1127 }
834 1128 }