PluginProbe ʕ •ᴥ•ʔ
Robin Image Optimizer – Unlimited Image Optimization, WebP & AVIF / trunk
Robin Image Optimizer – Unlimited Image Optimization, WebP & AVIF vtrunk
2.0.6 2.0.5 trunk 1.3.7 1.4.0 1.4.1 1.4.2 1.4.6 1.5.0 1.5.3 1.5.6 1.5.8 1.6.5 1.6.6 1.6.9 1.7.0 1.7.4 1.8.1 1.8.2 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4
robin-image-optimizer / includes / classes / class-rio-bulk-optimization.php
robin-image-optimizer / includes / classes Last commit date
models 4 weeks ago processing 5 months ago processors 5 months ago class-rio-attachment.php 5 months ago class-rio-backup.php 7 months ago class-rio-bulk-optimization.php 4 weeks ago class-rio-cron.php 7 months ago class-rio-image-query.php 5 months ago class-rio-image-statistic.php 5 months ago class-rio-media-library.php 4 weeks ago class-rio-multisite.php 7 months ago class-rio-optimization-orchestrator.php 5 months ago class-rio-optimization-tools.php 7 months ago class-rio-views.php 5 months ago class-wrio-license.php 7 months ago class-wrio-premium-provider.php 7 months ago class-wrio-support.php 7 months ago index.php 7 months ago
class-rio-bulk-optimization.php
998 lines
1 <?php
2
3 use WBCR\Factory_Processing_759\WP_Background_Process;
4
5 // Exit if accessed directly
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 /**
11 * Class WRIO_Bulk_Optimization
12 *
13 * Handles bulk optimization operations and processes related to media attachments,
14 * thumbnails, and other optimization tasks within the WordPress environment.
15 */
16 class WRIO_Bulk_Optimization {
17
18 public $processing;
19
20 public function __construct() {
21 $image_optimization_type = WRIO_Plugin::app()->getOption( 'image_optimization_type', '' );
22 if ( wrio_is_license_activate() && $image_optimization_type === 'background' ) {
23 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
24 $this->processing = $scope ? wrio_get_processing_class( $scope ) : $scope;
25
26 add_action( 'wp_ajax_wrio-cron-start', [ $this, 'processing_start' ] );
27 add_action( 'wp_ajax_wrio-cron-stop', [ $this, 'processing_stop' ] );
28
29 add_action( 'wp_ajax_wrio-webp-cron-start', [ $this, 'webp_processing_start' ] );
30 add_action( 'wp_ajax_wrio-webp-cron-stop', [ $this, 'webp_processing_stop' ] );
31 add_action( 'wp_ajax_wrio-avif-cron-start', [ $this, 'avif_processing_start' ] );
32 add_action( 'wp_ajax_wrio-avif-cron-stop', [ $this, 'avif_processing_stop' ] );
33 } else {
34 add_action( 'wp_ajax_wrio-cron-start', [ $this, 'cron_start' ] );
35 add_action( 'wp_ajax_wrio-cron-stop', [ $this, 'cron_stop' ] );
36
37 add_action( 'wp_ajax_wrio-webp-cron-start', [ $this, 'webp_cron_start' ] );
38 add_action( 'wp_ajax_wrio-webp-cron-stop', [ $this, 'webp_cron_stop' ] );
39 add_action( 'wp_ajax_wrio-avif-cron-start', [ $this, 'avif_cron_start' ] );
40 add_action( 'wp_ajax_wrio-avif-cron-stop', [ $this, 'avif_cron_stop' ] );
41 }
42
43 add_action( 'wp_ajax_wrio-bulk-optimization-process', [ $this, 'bulk_optimization_process' ] );
44 add_action( 'wp_ajax_wrio-bulk-conversion-process', [ $this, 'bulk_conversion_process' ] );
45 add_action( 'wp_ajax_wio_reoptimize_image', [ $this, 'reoptimize_image' ] );
46 add_action( 'wp_ajax_wio_convert_image', [ $this, 'convert_image' ] );
47 add_action( 'wp_ajax_wio_restore_image', [ $this, 'restore_image' ] );
48
49 add_action( 'wp_ajax_wbcr-rio-check-servers-status', [ $this, 'check_servers_status' ] );
50 add_action( 'wp_ajax_wbcr-rio-check-user-balance', [ $this, 'check_user_balance' ] );
51
52 // add_action( 'wp_ajax_wbcr-rio-calculate-total-images', [ $this, 'calculate_total_images' ] );
53 add_action( 'wp_ajax_wbcr-rio-calculate-total-images', [ $this, 'calculate_total_images' ] );
54 add_action( 'wp_ajax_wbcr-rio-calculate-total-attachments', [ $this, 'calculate_total_attachments' ] );
55 add_action( 'wp_ajax_wbcr-rio-calculate-total-thumbs', [ $this, 'calculate_total_thumbs' ] );
56 }
57
58 /**
59 * Calculates the total number of attachments that meet specified criteria.
60 * Retrieves the total count of attachment posts with allowed formats, excluding duplicates
61 * if WPML is active, and sends the result as a JSON response.
62 * Includes nonce verification and checks user permissions.
63 *
64 * @return void Outputs JSON response with the total count of attachments.
65 */
66 public function calculate_total_attachments() {
67 check_ajax_referer( 'bulk_optimization' );
68
69 if ( ! current_user_can( 'manage_options' ) ) {
70 wp_die( - 1 );
71 }
72
73 WRIO_Plugin::app()->deletePopulateOption( 'wrio_partial_total_count' );
74
75 $total_attachments = WRIO_Image_Query::get_instance()->count_total_attachments();
76
77 wp_send_json_success(
78 [
79 'found_attachments' => (int) $total_attachments,
80 ]
81 );
82 }
83
84 /**
85 * Calculates the total number of attachment thumbnails that match specified criteria.
86 * Processes a batch of attachments, counting the allowed thumbnail sizes, and updates
87 * the total count in the database. Handles AJAX requests, including user permissions
88 * and nonce verification.
89 *
90 * @return void Outputs JSON response with the total count of thumbnails and processing status.
91 */
92 public function calculate_total_thumbs() {
93 check_ajax_referer( 'bulk_optimization' );
94
95 if ( ! current_user_can( 'manage_options' ) ) {
96 wp_die( - 1 );
97 }
98
99 global $wpdb;
100
101 $offset = (int) WRIO_Plugin::app()->request->post( 'offset', 0 );
102 $limit = (int) WRIO_Plugin::app()->request->post( 'limit', 200 );
103
104 $allowed_formats_sql = wrio_get_allowed_formats( true );
105 $allowed_sizes = explode( ',', WRIO_Plugin::app()->getPopulateOption( 'allowed_sizes_thumbnail', '' ) );
106
107 $query = $wpdb->prepare(
108 "
109 SELECT posts.ID
110 FROM {$wpdb->posts} as posts
111 WHERE post_type = 'attachment'
112 AND post_status = 'inherit'
113 AND post_mime_type IN ({$allowed_formats_sql})
114 LIMIT %d OFFSET %d
115 ",
116 $limit,
117 $offset
118 );
119
120 // Учитываем WPML (исключение дубликатов)
121 if ( defined( 'WPML_PLUGIN_FILE' ) ) {
122 $query = str_replace(
123 'WHERE post_type =',
124 "WHERE NOT EXISTS (
125 SELECT icl.element_id
126 FROM {$wpdb->prefix}icl_translations as icl
127 WHERE icl.element_id = posts.ID
128 AND icl.element_type = 'post_attachment'
129 AND source_language_code IS NOT NULL
130 ) AND post_type =",
131 $query
132 );
133 }
134
135 $attachments = $wpdb->get_results( $query );
136 $total_count = (int) WRIO_Plugin::app()->getPopulateOption( 'wrio_partial_total_count', 0 );
137
138 $current_batch_count = 0;
139
140 foreach ( $attachments as $attachment ) {
141 $meta = wp_get_attachment_metadata( $attachment->ID );
142 if ( $meta && isset( $meta['sizes'] ) ) {
143 foreach ( $meta['sizes'] as $size_key => $size_value ) {
144 if ( in_array( $size_key, $allowed_sizes ) ) {
145 ++$current_batch_count;
146 }
147 }
148 }
149 }
150
151 $total_count += $current_batch_count;
152 WRIO_Plugin::app()->updatePopulateOption( 'wrio_partial_total_count', $total_count );
153
154 // Если больше данны�
155 для обработки нет — завершаем и со�
156 раняем в кеш
157 if ( count( $attachments ) < $limit ) {
158 WRIO_Plugin::app()->deletePopulateOption( 'wrio_partial_total_count' );
159
160 wp_send_json_success(
161 [
162 'found_thumbs' => $total_count,
163 'done' => true,
164 ]
165 );
166 }
167
168 wp_send_json_success(
169 [
170 'found_thumbs' => $total_count,
171 'done' => false,
172 'next_offset' => $offset + $limit,
173 ]
174 );
175 }
176
177 public function cron_start() {
178 check_ajax_referer( 'bulk_optimization' );
179
180 if ( ! current_user_can( 'manage_options' ) ) {
181 wp_die( - 1 );
182 }
183
184 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
185
186 if ( empty( $scope ) ) {
187 wp_die( - 1 );
188 }
189
190 // where was runned cron
191 $cron_running_place = WRIO_Plugin::app()->getPopulateOption( 'cron_running', false );
192
193 if ( $scope == $cron_running_place ) {
194 wp_send_json_success();
195 }
196
197 WRIO_Plugin::app()->updatePopulateOption( 'cron_running', $scope );
198 WRIO_Cron::start();
199
200 wp_send_json_success();
201 }
202
203 public function cron_stop() {
204 check_ajax_referer( 'bulk_optimization' );
205
206 if ( ! current_user_can( 'manage_options' ) ) {
207 wp_die( - 1 );
208 }
209
210 WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false );
211 WRIO_Cron::stop();
212
213 wp_send_json_success();
214 }
215
216 public function webp_cron_start() {
217 check_ajax_referer( 'bulk_conversion' );
218
219 if ( ! current_user_can( 'manage_options' ) ) {
220 wp_die( - 1 );
221 }
222
223 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
224
225 if ( empty( $scope ) ) {
226 wp_die( - 1 );
227 }
228
229 $type = 'conversion';
230
231 // where was runned cron
232 $cron_running_place = WRIO_Plugin::app()->getPopulateOption( "{$type}_cron_running", false );
233
234 if ( $scope == $cron_running_place ) {
235 wp_send_json_success();
236 }
237
238 WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", $scope );
239 WRIO_Cron::start( $type );
240
241 wp_send_json_success();
242 }
243
244 public function webp_cron_stop() {
245 check_ajax_referer( 'bulk_conversion' );
246
247 if ( ! current_user_can( 'manage_options' ) ) {
248 wp_die( - 1 );
249 }
250
251 $type = 'conversion';
252
253 WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", false );
254 WRIO_Cron::stop( $type );
255
256 wp_send_json_success();
257 }
258
259 public function processing_start() {
260 check_ajax_referer( 'bulk_optimization' );
261
262 if ( ! current_user_can( 'manage_options' ) ) {
263 wp_die( - 1 );
264 }
265
266 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
267
268 if ( empty( $scope ) ) {
269 wp_die( - 1 );
270 }
271
272 // where was runned
273 $process_running_place = WRIO_Plugin::app()->getPopulateOption( 'process_running', false );
274
275 if ( $scope == $process_running_place ) {
276 wp_send_json_success();
277 }
278
279 WRIO_Plugin::app()->updatePopulateOption( 'process_running', $scope );
280
281 $processing = wrio_get_processing_class( $scope );
282 if ( ! $processing ) {
283 WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
284 wp_send_json_error( [ 'message' => 'Processing class not found for scope: ' . $scope ] );
285 }
286
287 if ( $processing->push_items() ) {
288 $processing->save()->dispatch();
289 } else {
290 WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
291 wp_send_json_success(
292 [
293 'stop' => true,
294 ]
295 );
296 }
297
298 wp_send_json_success();
299 }
300
301 public function processing_stop() {
302 check_ajax_referer( 'bulk_optimization' );
303
304 if ( ! current_user_can( 'manage_options' ) ) {
305 wp_die( - 1 );
306 }
307
308 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
309 if ( empty( $scope ) ) {
310 wp_die( - 1 );
311 }
312
313 WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
314 $processing = wrio_get_processing_class( $scope );
315 if ( $processing ) {
316 $processing->cancel_process();
317 }
318
319 wp_send_json_success();
320 }
321
322 public function webp_processing_start() {
323 check_ajax_referer( 'bulk_conversion' );
324
325 if ( ! current_user_can( 'manage_options' ) ) {
326 wp_die( - 1 );
327 }
328
329 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
330
331 if ( empty( $scope ) ) {
332 wp_die( - 1 );
333 }
334
335 $scope = $scope . '_webp';
336
337 // where was runned
338 $process_running_place = WRIO_Plugin::app()->getPopulateOption( "{$scope}_process_running", false );
339
340 if ( $scope == $process_running_place ) {
341 wp_send_json_success();
342 }
343
344 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", $scope );
345
346 $processing = wrio_get_processing_class( $scope );
347 if ( ! $processing ) {
348 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
349 wp_send_json_error( [ 'message' => 'Processing class not found for scope: ' . $scope ] );
350 }
351
352 if ( $processing->push_items() ) {
353 $processing->save()->dispatch();
354 } else {
355 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
356 wp_send_json_success(
357 [
358 'stop' => true,
359 ]
360 );
361 }
362
363 wp_send_json_success();
364 }
365
366 public function webp_processing_stop() {
367 check_ajax_referer( 'bulk_conversion' );
368
369 if ( ! current_user_can( 'manage_options' ) ) {
370 wp_die( - 1 );
371 }
372
373 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
374 if ( empty( $scope ) ) {
375 wp_die( - 1 );
376 }
377
378 $scope = $scope . '_webp';
379
380 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
381 $processing = wrio_get_processing_class( $scope );
382 if ( $processing ) {
383 $processing->cancel_process();
384 }
385
386 wp_send_json_success();
387 }
388
389 /**
390 * Start AVIF conversion cron job.
391 *
392 * @return void
393 */
394 public function avif_cron_start() {
395 check_ajax_referer( 'bulk_conversion' );
396
397 if ( ! current_user_can( 'manage_options' ) ) {
398 wp_die( - 1 );
399 }
400
401 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
402
403 if ( empty( $scope ) ) {
404 wp_die( - 1 );
405 }
406
407 $type = 'avif_conversion';
408
409 // where was runned cron
410 $cron_running_place = WRIO_Plugin::app()->getPopulateOption( "{$type}_cron_running", false );
411
412 if ( $scope == $cron_running_place ) {
413 wp_send_json_success();
414 }
415
416 WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", $scope );
417 WRIO_Cron::start( $type );
418
419 wp_send_json_success();
420 }
421
422 /**
423 * Stop AVIF conversion cron job.
424 *
425 * @return void
426 */
427 public function avif_cron_stop() {
428 check_ajax_referer( 'bulk_conversion' );
429
430 if ( ! current_user_can( 'manage_options' ) ) {
431 wp_die( - 1 );
432 }
433
434 $type = 'avif_conversion';
435
436 WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", false );
437 WRIO_Cron::stop( $type );
438
439 wp_send_json_success();
440 }
441
442 /**
443 * Start AVIF conversion background processing.
444 *
445 * @return void
446 */
447 public function avif_processing_start() {
448 check_ajax_referer( 'bulk_conversion' );
449
450 if ( ! current_user_can( 'manage_options' ) ) {
451 wp_die( - 1 );
452 }
453
454 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
455
456 if ( empty( $scope ) ) {
457 wp_die( - 1 );
458 }
459
460 $scope = $scope . '_avif';
461
462 // where was runned
463 $process_running_place = WRIO_Plugin::app()->getPopulateOption( "{$scope}_process_running", false );
464
465 if ( $scope == $process_running_place ) {
466 wp_send_json_success();
467 }
468
469 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", $scope );
470
471 $processing = wrio_get_processing_class( $scope );
472 if ( ! $processing ) {
473 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
474 wp_send_json_error( [ 'message' => 'Processing class not found for scope: ' . $scope ] );
475 }
476
477 if ( $processing->push_items() ) {
478 $processing->save()->dispatch();
479 } else {
480 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
481 wp_send_json_success(
482 [
483 'stop' => true,
484 ]
485 );
486 }
487
488 wp_send_json_success();
489 }
490
491 /**
492 * Stop AVIF conversion background processing.
493 *
494 * @return void
495 */
496 public function avif_processing_stop() {
497 check_ajax_referer( 'bulk_conversion' );
498
499 if ( ! current_user_can( 'manage_options' ) ) {
500 wp_die( - 1 );
501 }
502
503 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
504 if ( empty( $scope ) ) {
505 wp_die( - 1 );
506 }
507
508 $scope = $scope . '_avif';
509
510 WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
511 $processing = wrio_get_processing_class( $scope );
512 if ( $processing ) {
513 $processing->cancel_process();
514 }
515
516 wp_send_json_success();
517 }
518
519 public function bulk_optimization_process() {
520 check_admin_referer( 'bulk_optimization' );
521
522 if ( ! current_user_can( 'manage_options' ) ) {
523 wp_die( - 1 );
524 }
525
526 $reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
527 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
528
529 WRIO_Plugin::app()->logger->info( sprintf( 'Start bulk optimization process! Scope: %s', $scope ) );
530
531 if ( empty( $scope ) ) {
532 wp_die( - 1 );
533 }
534
535 // Use orchestrator for media-library scope
536 if ( 'media-library' === $scope ) {
537 if ( $reset_current_error ) {
538 $media_library = WRIO_Media_Library::get_instance();
539 $media_library->resetCurrentErrors();
540 }
541
542 $orchestrator = WRIO_Optimization_Orchestrator::get_instance();
543 $result = $orchestrator->execute_next_action( 1 );
544
545 if ( isset( $result['error'] ) ) {
546 $error_massage = $result['error'];
547
548 if ( empty( $error_massage ) ) {
549 $error_massage = __( "Unknown error. Enable error log on the plugin's settings page, then check the error report on the Error Log page. You can export the error report and send it to the support service of the plugin.", 'robin-image-optimizer' );
550 }
551
552 WRIO_Plugin::app()->logger->error( sprintf( 'Bulk optimization error: %s.', $error_massage ) );
553
554 wp_send_json_error( [ 'error_message' => $error_massage ] );
555 }
556
557 WRIO_Plugin::app()->logger->info( sprintf( 'End bulk optimization process! Scope: %s. Remain: %d', $scope, $result['remain'] ) );
558
559 wp_send_json_success( $result );
560 }
561
562 // Fall back to old behavior for custom-folders, nextgen, etc.
563 // Context class name. If plugin expands with add-ons
564 $class_name = 'WRIO_' . wrio_dashes_to_camel_case( $scope, true );
565
566 if ( ! class_exists( $class_name ) ) {
567 WRIO_Plugin::app()->logger->error( sprintf( 'Bulk optimization error: Context class (%s) not found.', $class_name ) );
568
569 // todo: Temporary bug fix.
570 if ( 'custom-folders' === $scope ) {
571 $class_name = 'WRIO_Custom_Folders';
572 } elseif ( 'nextgen-gallery' == $scope ) {
573 $class_name = 'WRIO_Nextgen_Gallery';
574 }
575
576 if ( ! class_exists( $class_name ) ) {
577 wp_send_json_error( [ 'error_message' => 'Context class not found.' ] );
578 }
579 }
580
581 /**
582 * Create an instance of the class depending on the context in which scope user
583 * has runned optimization.
584 *
585 * @see WRIO_Custom_Folders
586 * @see WRIO_Nextgen_Gallery
587 * @var WRIO_Media_Library $optimizer
588 */
589 $optimizer = new $class_name();
590
591 if ( $reset_current_error ) {
592 $optimizer->resetCurrentErrors();
593 }
594
595 $result = $optimizer->processUnoptimizedImages( 1 );
596
597 if ( is_wp_error( $result ) ) {
598 $error_massage = $result->get_error_message();
599
600 if ( empty( $error_massage ) ) {
601 $error_massage = __( "Unknown error. Enable error log on the plugin's settings page, then check the error report on the Error Log page. You can export the error report and send it to the support service of the plugin.", 'robin-image-optimizer' );
602 }
603
604 WRIO_Plugin::app()->logger->error( sprintf( 'Bulk optimization error: %s.', $result->get_error_message() ) );
605
606 wp_send_json_error( [ 'error_message' => $error_massage ] );
607 }
608
609 // If all images are processed, send completion command
610 if ( $result['remain'] <= 0 ) {
611 $result['end'] = true;
612 }
613
614 WRIO_Plugin::app()->logger->info( sprintf( 'End bulk optimization process! Scope: %s. Remain: %d', $scope, $result['remain'] ) );
615
616 wp_send_json_success( $result );
617 }
618
619 public function bulk_conversion_process() {
620 check_admin_referer( 'bulk_conversion' );
621
622 if ( ! current_user_can( 'manage_options' ) ) {
623 wp_die( - 1 );
624 }
625
626 $reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
627 $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
628 $format = WRIO_Plugin::app()->request->request( 'format', 'webp', true );
629
630 // Validate format
631 if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
632 $format = 'webp';
633 }
634
635 WRIO_Plugin::app()->logger->info( sprintf( 'Start bulk conversion process! Scope: %s, Format: %s', $scope, $format ) );
636
637 if ( empty( $scope ) ) {
638 wp_die( - 1 );
639 }
640
641 // Context class name. If plugin expands with add-ons
642 $class_name = 'WRIO_' . wrio_dashes_to_camel_case( $scope, true );
643
644 if ( ! class_exists( $class_name ) ) {
645 WRIO_Plugin::app()->logger->error( sprintf( 'Bulk conversion error: Context class (%s) not found.', $class_name ) );
646
647 // todo: Temporary bug fix.
648 if ( 'media-library' === $scope ) {
649 $class_name = 'WRIO_Media_Library';
650 } elseif ( 'custom-folders' === $scope ) {
651 $class_name = 'WRIO_Custom_Folders';
652 } elseif ( 'nextgen-gallery' == $scope ) {
653 $class_name = 'WRIO_Nextgen_Gallery';
654 }
655
656 if ( ! class_exists( $class_name ) ) {
657 wp_send_json_error( [ 'error_message' => 'Context class not found.' ] );
658 }
659 }
660
661 /**
662 * Create an instance of the class depending on the context in which scope user
663 * has runned optimization.
664 *
665 * @see WRIO_Media_Library
666 * @see WRIO_Custom_Folders
667 * @see WRIO_Nextgen_Gallery
668 * @var WRIO_Media_Library $optimizer
669 */
670 $optimizer = new $class_name();
671
672 if ( $reset_current_error ) {
673 $optimizer->resetCurrentErrors(); // сбрасываем текущие ошибки оптимизации
674 }
675
676 $result = $optimizer->webpUnoptimizedImages( 1, $format );
677
678 if ( is_wp_error( $result ) ) {
679 $error_massage = $result->get_error_message();
680
681 if ( empty( $error_massage ) ) {
682 $error_massage = __( "Unknown error. Enable error log on the plugin's settings page, then check the error report on the Error Log page. You can export the error report and send it to the support service of the plugin.", 'robin-image-optimizer' );
683 }
684
685 WRIO_Plugin::app()->logger->error( sprintf( 'Bulk conversion error: %s.', $result->get_error_message() ) );
686
687 wp_send_json_error( [ 'error_message' => $error_massage ] );
688 }
689
690 // если изображения закончились - посылаем команду завершения
691 if ( $result['remain'] <= 0 ) {
692 $result['end'] = true;
693 }
694
695 WRIO_Plugin::app()->logger->info( sprintf( 'End bulk conversion process! Scope: %s, Format: %s. Remain: %d', $scope, $format, $result['remain'] ) );
696
697 wp_send_json_success( $result );
698 }
699
700 public function reoptimize_image() {
701 check_admin_referer( 'reoptimize' );
702
703 if ( ! current_user_can( 'manage_options' ) ) {
704 wp_die( - 1 );
705 }
706
707 $default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
708
709 $attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
710 $level = WRIO_Plugin::app()->request->post( 'level', $default_level, true );
711
712 $backup = WIO_Backup::get_instance();
713 $media_library = WRIO_Media_Library::get_instance();
714 $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
715
716 if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
717 echo $media_library->getMediaColumnContent( $attachment_id );
718 die();
719 }
720
721 $optimized_data = $media_library->optimizeAttachment( $attachment_id, $level );
722
723 if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
724 echo 'processing';
725 die();
726 }
727
728 echo $media_library->getMediaColumnContent( $attachment_id );
729 die();
730 }
731
732 public function convert_image() {
733 check_admin_referer( 'convert' );
734
735 if ( ! current_user_can( 'manage_options' ) ) {
736 wp_die( - 1 );
737 }
738
739 $attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
740 $format = WRIO_Plugin::app()->request->post( 'format', 'webp', true );
741 $media_library = WRIO_Media_Library::get_instance();
742
743 $media_library->webpConvertAttachment( $attachment_id, $format );
744
745 echo $media_library->getMediaColumnContent( $attachment_id );
746 die();
747 }
748
749 public function restore_image() {
750 check_admin_referer( 'restore' );
751
752 if ( ! current_user_can( 'manage_options' ) ) {
753 wp_die( - 1 );
754 }
755
756 $attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
757
758 $media_library = WRIO_Media_Library::get_instance();
759 $wio_attachment = $media_library->getAttachment( $attachment_id );
760
761 if ( $wio_attachment->isOptimized() ) {
762 $media_library->restoreAttachment( $attachment_id );
763 }
764
765 echo $media_library->getMediaColumnContent( $attachment_id );
766 die();
767 }
768
769 public function check_servers_status() {
770 check_ajax_referer( 'bulk_optimization' );
771
772 if ( ! current_user_can( 'manage_options' ) ) {
773 wp_die( - 1 );
774 }
775
776 // Auto-detect server based on license status
777 $is_premium = wrio_is_license_activate();
778 $license_source = wrio_get_license_source();
779 $server_name = $is_premium ? 'server_5' : 'server_2';
780 $return_data = [ 'server_name' => $server_name ];
781
782 $headers = [
783 'User-Agent' => '',
784 ];
785
786 // For SDK (ThemeIsle) licenses, the license was already validated during activation
787 // via ThemeIsle's API, so we can skip the Robin API license check.
788 if ( $is_premium && 'sdk' === $license_source ) {
789 wp_send_json_success( $return_data );
790 }
791
792 if ( $is_premium ) {
793 $api_url = 'https://dashboard.robinoptimizer.com/v1/license/check';
794 $headers['Authorization'] = 'Bearer ' . base64_encode( wrio_get_license_key() );
795 $headers['PluginId'] = wrio_get_freemius_plugin_id();
796 $headers['X-License-Source'] = $license_source;
797 $headers['X-Site-Url'] = home_url();
798 } else {
799 $api_url = 'https://dashboard.robinoptimizer.com/v1/free/license/check';
800 $host = get_option( 'siteurl' );
801 $headers['Authorization'] = 'Bearer ' . base64_encode( $host );
802 $headers['X-Site-Url'] = home_url();
803 }
804
805 $request = wp_remote_request(
806 $api_url,
807 [
808 'method' => 'GET',
809 'headers' => $headers,
810 ]
811 );
812
813 if ( is_wp_error( $request ) ) {
814 $er_msg = $request->get_error_message();
815
816 $return_data['error'] = $er_msg;
817 wp_send_json_error( $return_data );
818 }
819
820 $response_code = wp_remote_retrieve_response_code( $request );
821
822 if ( $response_code != 200 ) {
823 $return_data['error'] = 'Server response ' . $response_code;
824 wp_send_json_error( $return_data );
825 }
826
827 $data = json_decode( wp_remote_retrieve_body( $request ) );
828
829 if ( isset( $data->response->server_load ) ) {
830 $return_data = [ 'server_load' => $data->response->server_load ];
831 }
832
833 wp_send_json_success( $return_data );
834 }
835
836 public function check_user_balance() {
837 check_ajax_referer( 'bulk_optimization' );
838
839 if ( ! current_user_can( 'manage_options' ) ) {
840 wp_die( - 1 );
841 }
842
843 // Auto-detect server based on license status
844 $is_premium = wrio_is_license_activate();
845 $license_source = wrio_get_license_source();
846 $processor = WIO_OptimizationTools::getImageProcessor();
847
848 if ( ! $processor->has_quota_limit() ) {
849 wp_send_json_error( [ 'error' => __( 'The server has no quota restrictions!', 'robin-image-optimizer' ) ] );
850 }
851
852 // For SDK (ThemeIsle) licenses, we can't check quota via Robin API yet.
853 // Return unlimited quota - actual limits will be enforced server-side during optimization.
854 if ( $is_premium && 'sdk' === $license_source ) {
855 wp_send_json_success(
856 [
857 'balance' => -1, // -1 indicates unlimited/unknown
858 'reset_at' => '',
859 ]
860 );
861 }
862
863 $headers = [];
864
865 if ( $is_premium ) {
866 $api_url = 'https://dashboard.robinoptimizer.com/v1/license/remaining';
867 $headers['Authorization'] = 'Bearer ' . base64_encode( wrio_get_license_key() );
868 $headers['PluginId'] = wrio_get_freemius_plugin_id();
869 $headers['X-License-Source'] = $license_source;
870 $headers['X-Site-Url'] = home_url();
871 } else {
872 $api_url = 'https://dashboard.robinoptimizer.com/v1/free/license/remaining';
873 $host = get_option( 'siteurl' );
874 $headers['Authorization'] = 'Bearer ' . base64_encode( $host );
875 $headers['X-Site-Url'] = home_url();
876 }
877
878 $request = wp_remote_request(
879 $api_url,
880 [
881 'method' => 'GET',
882 'headers' => $headers,
883 ]
884 );
885
886 if ( is_wp_error( $request ) ) {
887 $error_msg = $request->get_error_message();
888
889 $return_data['error'] = $error_msg;
890 wp_send_json_error( $return_data );
891 }
892
893 $response_code = wp_remote_retrieve_response_code( $request );
894 $response_body = wp_remote_retrieve_body( $request );
895
896 if ( $response_code != 200 ) {
897 $return_data['error'] = 'Server response ' . $response_code;
898 if ( $response_code === 401 ) {
899 $error_data = @json_decode( $response_body );
900 $return_data['error'] = $error_data->message;
901 }
902 wp_send_json_error( $return_data );
903 }
904
905 if ( empty( $response_body ) ) {
906 $return_data['error'] = 'Server responded an empty request body!';
907 wp_send_json_error( $return_data );
908 }
909
910 $data = @json_decode( $response_body );
911
912 if ( ! isset( $data->status ) || $data->status != 'ok' ) {
913 $return_data['error'] = 'Server responded an fail status';
914 wp_send_json_error( $return_data );
915 }
916
917 $current_quota = (int) $data->response->quota;
918 $processor->set_quota_limit( $current_quota );
919
920 $output = [ 'balance' => $current_quota ];
921
922 $reset_at = (int) $data->response->reset_at;
923 $reset_at += (int) get_option( 'gmt_offset', 0 );
924 $output['reset_at'] = gmdate( 'd-m-Y H:i', $reset_at );
925
926 wp_send_json_success( $output );
927 }
928
929 /*
930 public function calculate_total_images() {
931 check_ajax_referer( 'bulk_optimization' );
932
933 if ( ! current_user_can( 'manage_options' ) ) {
934 wp_die( - 1 );
935 }
936
937 global $wpdb;
938 $db_table = RIO_Process_Queue::table_name();
939 $sql = $wpdb->prepare( "SELECT * FROM {$db_table}
940 WHERE item_type = 'attachment' AND result_status IN (%s, %s)
941 ORDER BY id DESC;", RIO_Process_Queue::STATUS_SUCCESS, RIO_Process_Queue::STATUS_ERROR );
942 $optimized_images = $wpdb->get_results( $sql, ARRAY_A );
943
944 $count = 0;
945 if ( ! empty( $optimized_images ) ) {
946 foreach ( $optimized_images as $row ) {
947 $item = new RIO_Process_Queue( $row );
948 $count = $count + 1 + (int) $item->get_extra_data()->get_thumbnails_count();
949 }
950 }
951
952 $allowed_formats_sql = wrio_get_allowed_formats( true );
953
954 $sql = "SELECT posts.ID
955 FROM {$wpdb->posts} as posts
956 WHERE post_type = 'attachment'
957 AND post_status = 'inherit'
958 AND post_mime_type IN ( {$allowed_formats_sql} )";
959
960 // If you use a WPML plugin, you need to exclude duplicate images
961 if ( defined( 'WPML_PLUGIN_FILE' ) ) {
962 $sql .= " AND NOT EXISTS
963 (SELECT trnsl.element_id FROM {$wpdb->prefix}icl_translations as trnsl
964 WHERE trnsl.element_id=posts.ID
965 AND trnsl.element_type='post_attachment'
966 AND source_language_code IS NOT NULL
967 )";
968 }
969
970 $attachments = $wpdb->get_results( $sql );
971
972 $allowed_sizes = explode( ',', WRIO_Plugin::app()->getPopulateOption( 'allowed_sizes_thumbnail', '' ) );
973 $total_images = 0;
974 $upload = wp_upload_dir();
975 $upload = $upload['basedir'];
976 foreach ( $attachments as $attachment ) {
977 $meta = wp_get_attachment_metadata( $attachment->ID );
978 if ( $meta ) {
979 if ( isset( $meta['file'] ) && file_exists( "{$upload}/{$meta['file']}" ) ) {
980 $total_images ++;
981 }
982
983 foreach ( $meta['sizes'] as $k => $value ) {
984 if ( in_array( $k, $allowed_sizes ) ) {
985 $total_images ++;
986 }
987 }
988 }
989 }
990
991 $result_total = $total_images - $count;
992
993 wp_send_json_success( [
994 'total' => $result_total >= 0 ? $result_total : 0,
995 ] );
996 }*/
997 }
998