ShortPixel Image Optimizer - Version 5.1.0

Version Description

Release date October 20th, 2022 * New: added SmartCropping, especially useful for eCommerce sites; * New: if the WebP/AVIF files are larger than the JPG/PNG/GIF version, they are no longer generated to ensure that the smallest file is always delivered; * Fix: various DB-related settings were adjusted for files with very long names and to keep AVIF/WebP optimization data correct; * Fix: bulk processing history was lost when deleting the plugin; * Fix: the file name in the bulk preview was added back; * Fix: various situations and edge cases with WPML are now fixed; * Fix: when a Custom Media item was excluded, there was no clear message next to it; * Fix: added a check to prevent re-optimization when using bulk actions in the Media Library; * Fix: the deactivation pop-up is also displayed in a multisite environment; * Fix: minor wording and CSS fixes in the plugin settings and notifications; * Fix: if there are still images to optimize and/or generate, all of them are counted and displayed correctly in the Media Library; * Tweak: added check of necessary GD library functions to use PNG to JPG conversion; * Compat: in some very special cases an error was triggered when the YITH Watermark Premium plugin was enabled; * Language: 20 new strings added, 2 updated, 1 fuzzed, and 2 deprecated.

Download this release

Release Info

Developer petredobrescu
Plugin Icon 128x128 ShortPixel Image Optimizer
Version 5.1.0
Comparing to
See all releases

Code changes from version 5.0.9 to 5.1.0

Files changed (39) hide show
  1. build/shortpixel/log/src/DebugItem.php +14 -2
  2. build/shortpixel/notices/src/NoticeModel.php +3 -3
  3. class/Controller/AdminController.php +13 -1
  4. class/Controller/AdminNoticesController.php +25 -3
  5. class/Controller/AjaxController.php +2 -6
  6. class/Controller/ApiController.php +142 -21
  7. class/Controller/BulkController.php +1 -0
  8. class/Controller/FileSystemController.php +18 -9
  9. class/Controller/OptimizeController.php +32 -12
  10. class/Controller/Queue/MediaLibraryQueue.php +0 -1
  11. class/Controller/Queue/Queue.php +153 -30
  12. class/Controller/ResponseController.php +1 -1
  13. class/Controller/SettingsController.php +8 -2
  14. class/Controller/View/BulkViewController.php +5 -0
  15. class/Controller/View/EditMediaViewController.php +27 -26
  16. class/Controller/View/ListMediaViewController.php +15 -3
  17. class/Helper/InstallHelper.php +4 -2
  18. class/Helper/UiHelper.php +51 -33
  19. class/Model/File/DirectoryModel.php +1 -2
  20. class/Model/File/FileModel.php +1 -1
  21. class/Model/Image/CustomImageModel.php +97 -26
  22. class/Model/Image/ImageModel.php +372 -161
  23. class/Model/Image/MediaLibraryModel.php +403 -510
  24. class/Model/Image/MediaLibraryThumbnailModel.php +25 -19
  25. class/external/nextgen/nextGenController.php +24 -3
  26. class/external/nextgen/nextGenViewController.php +2 -0
  27. class/shortpixel-png2jpg.php +15 -2
  28. class/view/settings/part-advanced.php +2 -2
  29. class/view/settings/part-general.php +22 -0
  30. class/view/settings/part-wso.php +28 -0
  31. class/view/shortpixel-feedback.php +2 -0
  32. class/view/view-settings.php +1 -0
  33. class/wp-shortpixel-settings.php +6 -2
  34. readme.txt +26 -2
  35. res/css/shortpixel-bulk.css +4 -2
  36. res/css/shortpixel-bulk.css.map +1 -1
  37. res/js/screens/screen-custom.js +1 -1
  38. res/scss/shortpixel-bulk.scss +5 -2
  39. wp-shortpixel.php +2 -2
build/shortpixel/log/src/DebugItem.php CHANGED
@@ -41,13 +41,25 @@ class DebugItem
41
  {
42
  $this->data[] = print_r($data, true);
43
  }
44
- if ($dataType == 2) //array
45
  {
 
 
 
 
 
 
 
 
 
 
 
 
46
  foreach($data as $index => $item)
47
  {
48
  if (is_object($item) || is_array($item))
49
  {
50
- $this->data[] = print_r($item, true);
51
  }
52
  }
53
  }
41
  {
42
  $this->data[] = print_r($data, true);
43
  }
44
+ if ($dataType == 2) //array or object.
45
  {
46
+ $count = false;
47
+ if (gettype($data) == 'array')
48
+ $count = count($data);
49
+ elseif(gettype($data) == 'object')
50
+ $count = count(get_object_vars($data));
51
+
52
+ $firstLine = ucfirst(gettype($data)) . ':';
53
+ if ($count !== false)
54
+ $firstLine .= ' (' . $count . ')';
55
+
56
+ $this->data[] = $firstLine;
57
+
58
  foreach($data as $index => $item)
59
  {
60
  if (is_object($item) || is_array($item))
61
  {
62
+ $this->data[] = print_r($index, true) . ' ( ' . ucfirst(gettype($item)) . ') => ' . print_r($item, true);
63
  }
64
  }
65
  }
build/shortpixel/notices/src/NoticeModel.php CHANGED
@@ -234,9 +234,9 @@ class NoticeModel //extends ShortPixelModel
234
  document.getElementById('button-$id').onclick = function()
235
  {
236
  var el = document.getElementById('$id');
237
- $(el).fadeTo(100,0,function() {
238
- $(el).slideUp(100, 0, function () {
239
- $(el).remove();
240
  })
241
  });
242
  } </script>";
234
  document.getElementById('button-$id').onclick = function()
235
  {
236
  var el = document.getElementById('$id');
237
+ jQuery(el).fadeTo(100,0,function() {
238
+ jQuery(el).slideUp(100, 0, function () {
239
+ jQuery(el).remove();
240
  })
241
  });
242
  } </script>";
class/Controller/AdminController.php CHANGED
@@ -44,7 +44,16 @@ class AdminController extends \ShortPixel\Controller
44
  return $meta;
45
  }
46
 
47
- $mediaItem = \wpSPIO()->filesystem()->getImage($id, 'media');
 
 
 
 
 
 
 
 
 
48
 
49
  if ($mediaItem->getExtension() == 'pdf')
50
  {
@@ -61,6 +70,9 @@ class AdminController extends \ShortPixel\Controller
61
  $control = new OptimizeController();
62
  $control->addItemToQueue($mediaItem);
63
  }
 
 
 
64
  return $meta; // It's a filter, otherwise no thumbs
65
  }
66
 
44
  return $meta;
45
  }
46
 
47
+ // todo add check here for mediaitem
48
+ $fs = \wpSPIO()->filesystem();
49
+ $fs->flushImageCache(); // it's possible file just changed by external plugin.
50
+ $mediaItem = $fs->getImage($id, 'media');
51
+
52
+ if ($mediaItem === false)
53
+ {
54
+ Log::addError('Handle Image Upload Hook triggered, by error in image :' . $id );
55
+ return $meta;
56
+ }
57
 
58
  if ($mediaItem->getExtension() == 'pdf')
59
  {
70
  $control = new OptimizeController();
71
  $control->addItemToQueue($mediaItem);
72
  }
73
+ else {
74
+ Log::addWarn('Passed mediaItem is not processable', $mediaItem);
75
+ }
76
  return $meta; // It's a filter, otherwise no thumbs
77
  }
78
 
class/Controller/AdminNoticesController.php CHANGED
@@ -36,6 +36,7 @@ class AdminNoticesController extends \ShortPixel\Controller
36
  const MSG_NO_APIKEY_REPEAT_LONG = 'ApiNotice302'; // Last Repeat.
37
 
38
  const MSG_INTEGRATION_NGGALLERY = 'IntNotice400';
 
39
 
40
  const MSG_CONVERT_LEGACY = 'LegNotice100';
41
 
@@ -193,6 +194,8 @@ class AdminNoticesController extends \ShortPixel\Controller
193
  protected function doIntegrationNotices()
194
  {
195
  $settings= \wpSPIO()->settings();
 
 
196
  if (! \wpSPIO()->settings()->verifiedKey)
197
  {
198
  return; // no key, no integrations.
@@ -206,6 +209,26 @@ class AdminNoticesController extends \ShortPixel\Controller
206
  Notices::makePersistent($notice, self::MSG_INTEGRATION_NGGALLERY, YEAR_IN_SECONDS);
207
  }
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
 
211
  /** Load the various messages about the lack of API-keys in the plugin */
@@ -463,7 +486,7 @@ class AdminNoticesController extends \ShortPixel\Controller
463
  {
464
  // @todo The notice is user-dependent but the notice is dismissed installation-wide.
465
 
466
- $message = __('You can see ShortPixel Image Optimiser actions and data only via the list view. Switch to the list view to use the plugin via the media library', 'shortpixel-image-optimiser');
467
  $new_notice = Notices::addNormal($message);
468
  Notices::makePersistent($new_notice, self::MSG_LISTVIEW_ACTIVE, YEAR_IN_SECONDS);
469
  }
@@ -508,8 +531,7 @@ class AdminNoticesController extends \ShortPixel\Controller
508
  $message = "<p>" . __('In order to start the optimization process, you need to validate your API Key in the '
509
  . '<a href="options-general.php?page=wp-shortpixel-settings">ShortPixel Settings</a> page in your WordPress Admin.','shortpixel-image-optimiser') . "
510
  </p>
511
- <p>" . __('If you don’t have an API Key, you can get one delivered to your inbox, for free.','shortpixel-image-optimiser') . "</p>
512
- <p>" . __('Please <a href="https://shortpixel.com/wp-apikey" target="_blank">sign up to get your API key.</a>','shortpixel-image-optimiser') . "</p>";
513
 
514
  return $message;
515
  }
36
  const MSG_NO_APIKEY_REPEAT_LONG = 'ApiNotice302'; // Last Repeat.
37
 
38
  const MSG_INTEGRATION_NGGALLERY = 'IntNotice400';
39
+ const MSG_FEATURE_SMARTCROP = 'FeaNotice100';
40
 
41
  const MSG_CONVERT_LEGACY = 'LegNotice100';
42
 
194
  protected function doIntegrationNotices()
195
  {
196
  $settings= \wpSPIO()->settings();
197
+ $noticeController = Notices::getInstance();
198
+
199
  if (! \wpSPIO()->settings()->verifiedKey)
200
  {
201
  return; // no key, no integrations.
209
  Notices::makePersistent($notice, self::MSG_INTEGRATION_NGGALLERY, YEAR_IN_SECONDS);
210
  }
211
 
212
+ $smartcropNotice = $noticeController->getNoticeByID(self::MSG_FEATURE_SMARTCROP);
213
+ if ($smartcropNotice === false || $smartcropNotice->isDismissed() === false)
214
+ {
215
+ $link = 'https://shortpixel.com/knowledge-base/article/182-what-is-smart-cropping';
216
+ $link2 = 'https://shortpixel.com/blog/how-to-smart-crop-wordpress-images/#how-to-crop-wordpress-images-automatically-smart-solution';
217
+ $link3 = esc_url(admin_url('options-general.php?page=wp-shortpixel-settings'));
218
+
219
+ $message = sprintf(__('%s With ShortPixel you can now %ssmartly crop%s the thumbnails on your website. This is especially useful for eCommerce websites %s(read more)%s. %s %s Activate the option in the %sShortPixel Settings%s page. %s', 'shortpixel-image-optimiser'),
220
+ '<p>' ,
221
+ '<a href="' . $link . '" target="_blank">', '</a>',
222
+ '<a href="' . $link2 . '" target="_blank">', '</a>',
223
+ '</p>', '<p>',
224
+ '<a href="' . $link3 . '" >', '</a>',
225
+ '</p>',
226
+ );
227
+ $notice = Notices::addNormal($message);
228
+ Notices::makePersistent($notice, self::MSG_FEATURE_SMARTCROP, YEAR_IN_SECONDS);
229
+ }
230
+
231
+
232
  }
233
 
234
  /** Load the various messages about the lack of API-keys in the plugin */
486
  {
487
  // @todo The notice is user-dependent but the notice is dismissed installation-wide.
488
 
489
+ $message = sprintf(__('You can see ShortPixel Image Optimiser actions and data only via the list view. Switch to the list view to use the plugin via the media library. Click to %s switch to the list view %s now. ', 'shortpixel-image-optimiser'), '<a href="' . admin_url('upload.php?mode=list') . '">','</a>');
490
  $new_notice = Notices::addNormal($message);
491
  Notices::makePersistent($new_notice, self::MSG_LISTVIEW_ACTIVE, YEAR_IN_SECONDS);
492
  }
531
  $message = "<p>" . __('In order to start the optimization process, you need to validate your API Key in the '
532
  . '<a href="options-general.php?page=wp-shortpixel-settings">ShortPixel Settings</a> page in your WordPress Admin.','shortpixel-image-optimiser') . "
533
  </p>
534
+ <p>" . __('If you don’t have an API Key, just fill out the form and a key will be created.','shortpixel-image-optimiser') . "</p>";
 
535
 
536
  return $message;
537
  }
class/Controller/AjaxController.php CHANGED
@@ -40,7 +40,7 @@ class AjaxController
40
  $bulkSecret = $cacheControl->getItem('bulk-secret');
41
 
42
  $secretKey = $bulkSecret->getValue();
43
- if (is_null($secretKey) || strlen($secretKey) == 0)
44
  {
45
  $secretKey = false;
46
  }
@@ -150,13 +150,11 @@ class AjaxController
150
  $this->send($json);
151
  }
152
 
153
-
154
  public function ajax_processQueue()
155
  {
156
  $this->checkNonce('processing');
157
  $this->checkProcessorKey();
158
 
159
-
160
  // Notice that POST variables are always string, so 'true', not true.
161
  // phpcs:ignore -- Nonce is checked
162
  $isBulk = (isset($_POST['isBulk']) && $_POST['isBulk'] === 'true') ? true : false;
@@ -172,8 +170,6 @@ class AjaxController
172
  $this->send($result);
173
  }
174
 
175
-
176
-
177
  public function ajaxRequest()
178
  {
179
  $this->checkNonce('ajax_request');
@@ -265,7 +261,6 @@ class AjaxController
265
  public function getMediaItem($id, $type)
266
  {
267
  $fs = \wpSPIO()->filesystem();
268
-
269
  return $fs->getImage($id, $type);
270
 
271
  }
@@ -330,6 +325,7 @@ class AjaxController
330
 
331
  $json->$type = $control->restoreItem($mediaItem);
332
 
 
333
  return $json;
334
  }
335
 
40
  $bulkSecret = $cacheControl->getItem('bulk-secret');
41
 
42
  $secretKey = $bulkSecret->getValue();
43
+ if (is_null($secretKey) || strlen($secretKey) == 0 || $secretKey === 'null')
44
  {
45
  $secretKey = false;
46
  }
150
  $this->send($json);
151
  }
152
 
 
153
  public function ajax_processQueue()
154
  {
155
  $this->checkNonce('processing');
156
  $this->checkProcessorKey();
157
 
 
158
  // Notice that POST variables are always string, so 'true', not true.
159
  // phpcs:ignore -- Nonce is checked
160
  $isBulk = (isset($_POST['isBulk']) && $_POST['isBulk'] === 'true') ? true : false;
170
  $this->send($result);
171
  }
172
 
 
 
173
  public function ajaxRequest()
174
  {
175
  $this->checkNonce('ajax_request');
261
  public function getMediaItem($id, $type)
262
  {
263
  $fs = \wpSPIO()->filesystem();
 
264
  return $fs->getImage($id, $type);
265
 
266
  }
325
 
326
  $json->$type = $control->restoreItem($mediaItem);
327
 
328
+
329
  return $json;
330
  }
331
 
class/Controller/ApiController.php CHANGED
@@ -15,8 +15,11 @@ class ApiController
15
  const STATUS_SKIP = -4;
16
  const STATUS_NOT_FOUND = -5;
17
  const STATUS_NO_KEY = -6;
18
- const STATUS_RETRY = -7;
19
- const STATUS_SEARCHING = -8; // when the Queue is looping over images, but in batch none were found.
 
 
 
20
  const STATUS_QUEUE_FULL = -404;
21
  const STATUS_MAINTENANCE = -500;
22
  const STATUS_CONNECTION_ERROR = -503; // Not official, error connection in WP.
@@ -66,7 +69,12 @@ class ApiController
66
  */
67
  public function processMediaItem($item, $imageObj)
68
  {
69
- if (! $imageObj->isProcessable() || $imageObj->isOptimizePrevented() == true)
 
 
 
 
 
70
  {
71
  if ($imageObj->isOptimized())
72
  {
@@ -93,6 +101,9 @@ class ApiController
93
  $requestArgs['refresh'] = (property_exists($item, 'refresh') && $item->refresh) ? true : false;
94
  $requestArgs['flags'] = (property_exists($item, 'flags')) ? $item->flags : array();
95
 
 
 
 
96
  $request = $this->getRequest($requestArgs);
97
  $item = $this->doRequest($item, $request);
98
 
@@ -146,6 +157,8 @@ class ApiController
146
 
147
  $defaults = array(
148
  'urls' => null,
 
 
149
  'compressionType' => $settings->compressionType,
150
  'blocking' => true,
151
  'item_id' => null,
@@ -169,6 +182,16 @@ class ApiController
169
  'urllist' => $args['urls'],
170
  );
171
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  if(/*false &&*/ $settings->downloadArchive == self::DOWNLOAD_ARCHIVE && class_exists('PharData')) {
174
  $requestParameters['group'] = $args['item_id'];
@@ -179,6 +202,7 @@ class ApiController
179
 
180
  $requestParameters = apply_filters('shortpixel/api/request', $requestParameters, $args['item_id']);
181
 
 
182
  $arguments = array(
183
  'method' => 'POST',
184
  'timeout' => 15,
@@ -206,10 +230,13 @@ class ApiController
206
  */
207
  protected function doRequest($item, $requestParameters )
208
  {
 
 
209
  $response = wp_remote_post($this->apiEndPoint, $requestParameters );
210
  Log::addDebug('ShortPixel API Request sent', $requestParameters['body']);
211
 
212
 
 
213
  //only if $Blocking is true analyze the response
214
  if ( $requestParameters['blocking'] )
215
  {
@@ -255,6 +282,7 @@ class ApiController
255
  private function parseResponse($response)
256
  {
257
  $data = $response['body'];
 
258
  $data = json_decode($data);
259
  return (array)$data;
260
  }
@@ -278,6 +306,42 @@ class ApiController
278
  {
279
  $status = $APIresponse[0]->Status;
280
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
  // This is only set if something is up, otherwise, ApiResponse returns array
283
  if (is_object($status))
@@ -368,7 +432,7 @@ class ApiController
368
  case self::STATUS_SUCCESS:
369
 
370
  //handle image has been processed
371
- return $this->handleSuccess($item, $APIresponse);
372
  default:
373
 
374
  // Theoretically this should not be needed.
@@ -420,9 +484,10 @@ class ApiController
420
  * @param Object $response The API Response with opt. info.
421
  * @return ObjectArray $results The Result of the optimization
422
  */
423
- private function handleSuccess($item, $response)
424
  {
425
  Log::addDebug('ShortPixel API : Handling Success!', $response);
 
426
  $settings = \wpSPIO()->settings();
427
  $fs = \wpSPIO()->fileSystem();
428
 
@@ -437,20 +502,42 @@ class ApiController
437
  $fileSize = "LosslessSize";
438
  }
439
  $webpType = "WebP" . $fileType;
 
 
440
  $avifType = "AVIF" . $fileType;
 
 
 
 
 
441
 
 
 
 
 
 
442
 
443
  $tempFiles = $responseFiles = $results = array();
444
 
 
445
  //download each file from array and process it
446
- foreach ($response as $fileData )
447
  {
 
 
 
 
 
 
 
448
  if(!isset($fileData->Status)) continue; //if optimized images archive is activated, last entry of APIResponse if the Archive data.
449
 
450
  //file was processed OK
451
  if ($fileData->Status->Code == self::STATUS_SUCCESS )
452
  {
453
- $downloadResult = $this->handleDownload($fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize
 
 
454
  );
455
  $archive = false;
456
 
@@ -458,11 +545,20 @@ class ApiController
458
  * @todo Write Unit Test for Status_unchanged
459
  * But it should still be regarded as File Done. This can happen on very small file ( 6pxX6px ) which will not optimize.
460
  */
461
- if ($downloadResult->apiStatus == self::STATUS_SUCCESS || $downloadResult->apiStatus == self::STATUS_UNCHANGED )
462
  {
463
  // Removes any query ?strings and returns just filename of originalURL
464
  $originalURL = $fileData->OriginalURL;
465
 
 
 
 
 
 
 
 
 
 
466
  if (strpos($fileData->OriginalURL, '?') !== false)
467
  {
468
  $originalURL = substr($fileData->OriginalURL, 0, (strpos($fileData->OriginalURL, '?')) ); // Strip Query String from URL. If it's there!
@@ -473,38 +569,50 @@ class ApiController
473
 
474
  // Put it in Results.
475
  $originalName = $originalFile->getFileName();
476
- $results[$originalName] = $downloadResult;
 
 
477
 
478
  // Handle Stats
479
- $savedSpace += $fileData->OriginalSize - $fileData->$fileSize;
480
- $originalSpace += $fileData->OriginalSize;
481
  $optimizedSpace += $fileData->$fileSize;
482
  $fileCount++;
483
 
484
  // ** Download Webp files if they are returned **/
485
  if (isset($fileData->$webpType) && $fileData->$webpType != 'NA')
486
  {
487
- $webpName = $originalFile->getFileBase() . '.webp'; //basename(parse_url($fileData->$webpType, PHP_URL_PATH));
488
-
489
- if($archive) { // swallow pride here, or fix this.
 
 
 
 
 
490
  $webpDownloadResult = $this->fromArchive($archive['Path'], $fileData->$webpType, false,false);
491
  } else {
492
  $webpDownloadResult = $this->handleDownload($fileData->$webpType, false, false);
493
  }
494
 
495
- if ( $webpDownloadResult->apiStatus == self::STATUS_SUCCESS)
496
  {
497
  Log::addDebug('Downloaded Webp : ' . $fileData->$webpType);
498
- $results[$webpName] = $webpDownloadResult;
 
499
  }
500
  }
501
 
502
  // ** Download Webp files if they are returned **/
503
  if (isset($fileData->$avifType) && $fileData->$avifType !== 'NA')
504
  {
505
- $avifName = $originalFile->getFileBase() . '.avif'; ;
506
 
507
- if($archive) { // swallow pride here, or fix this.
 
 
 
 
508
  $avifDownloadResult = $this->fromArchive($archive['Path'], $fileData->$avifType, false,false);
509
  } else {
510
  $avifDownloadResult = $this->handleDownload($fileData->$avifType, false, false);
@@ -513,7 +621,9 @@ class ApiController
513
  if ( $avifDownloadResult->apiStatus == self::STATUS_SUCCESS)
514
  {
515
  Log::addDebug('Downloaded Avif : ' . $fileData->$avifType);
516
- $results[$avifName] = $avifDownloadResult;
 
 
517
  }
518
  }
519
 
@@ -548,10 +658,16 @@ class ApiController
548
  Log::addDebug("Adding $fileCount files to stats, $originalSpace went to $optimizedSpace ($savedSpace)");
549
 
550
  // *******************************
 
 
 
 
 
551
 
552
- return $this->returnSuccess($results, self::STATUS_SUCCESS, false);
553
  }
554
 
 
555
  /**
556
  * handles the download of an optimized image from ShortPixel API
557
  * @param string $optimizedUrl
@@ -567,10 +683,15 @@ class ApiController
567
  //if there is no improvement in size then we do not download this file, except (sigh) when the fileType is heic since it converts.
568
  if (($optimizedSize !== false && $originalSize !== false) && $originalSize == $optimizedSize && strpos($optimizedUrl, 'heic') === false )
569
  {
570
-
571
  Log::addDebug('Optimize and Original size seems the same');
572
  return $this->returnRetry(self::STATUS_UNCHANGED, __("File wasn't optimized so we do not download it.", 'shortpixel-image-optimiser'));
573
  }
 
 
 
 
 
 
574
  $correctFileSize = $optimizedSize;
575
  $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl));
576
 
15
  const STATUS_SKIP = -4;
16
  const STATUS_NOT_FOUND = -5;
17
  const STATUS_NO_KEY = -6;
18
+ // const STATUS_RETRY = -7;
19
+ // const STATUS_SEARCHING = -8; // when the Queue is looping over images, but in batch none were found.
20
+ const STATUS_OPTIMIZED_BIGGER = -9;
21
+
22
+
23
  const STATUS_QUEUE_FULL = -404;
24
  const STATUS_MAINTENANCE = -500;
25
  const STATUS_CONNECTION_ERROR = -503; // Not official, error connection in WP.
69
  */
70
  public function processMediaItem($item, $imageObj)
71
  {
72
+ if (! is_object($imageObj))
73
+ {
74
+ $item->result = $this->returnFailure(self::STATUS_FAIL, __('Item seems invalid, removed or corrupted.', 'shortpixel-image-optimiser'));
75
+ return $item;
76
+ }
77
+ elseif (! $imageObj->isProcessable() || $imageObj->isOptimizePrevented() == true)
78
  {
79
  if ($imageObj->isOptimized())
80
  {
101
  $requestArgs['refresh'] = (property_exists($item, 'refresh') && $item->refresh) ? true : false;
102
  $requestArgs['flags'] = (property_exists($item, 'flags')) ? $item->flags : array();
103
 
104
+ $requestArgs['paramlist'] = property_exists($item, 'paramlist') ? $item->paramlist : null;
105
+ $requestArgs['returndatalist'] = property_exists($item, 'returndatalist') ? $item->returndatalist : null;
106
+
107
  $request = $this->getRequest($requestArgs);
108
  $item = $this->doRequest($item, $request);
109
 
157
 
158
  $defaults = array(
159
  'urls' => null,
160
+ 'paramlist' => null,
161
+ 'returndatalist' => null,
162
  'compressionType' => $settings->compressionType,
163
  'blocking' => true,
164
  'item_id' => null,
182
  'urllist' => $args['urls'],
183
  );
184
 
185
+ if (! is_null($args['paramlist']))
186
+ {
187
+ $requestParameters['paramlist'] = $args['paramlist'];
188
+ }
189
+
190
+ if (! is_null($args['returndatalist']))
191
+ {
192
+ $requestParameters['returndatalist'] = $args['returndatalist'];
193
+ }
194
+
195
 
196
  if(/*false &&*/ $settings->downloadArchive == self::DOWNLOAD_ARCHIVE && class_exists('PharData')) {
197
  $requestParameters['group'] = $args['item_id'];
202
 
203
  $requestParameters = apply_filters('shortpixel/api/request', $requestParameters, $args['item_id']);
204
 
205
+
206
  $arguments = array(
207
  'method' => 'POST',
208
  'timeout' => 15,
230
  */
231
  protected function doRequest($item, $requestParameters )
232
  {
233
+
234
+
235
  $response = wp_remote_post($this->apiEndPoint, $requestParameters );
236
  Log::addDebug('ShortPixel API Request sent', $requestParameters['body']);
237
 
238
 
239
+
240
  //only if $Blocking is true analyze the response
241
  if ( $requestParameters['blocking'] )
242
  {
282
  private function parseResponse($response)
283
  {
284
  $data = $response['body'];
285
+
286
  $data = json_decode($data);
287
  return (array)$data;
288
  }
306
  {
307
  $status = $APIresponse[0]->Status;
308
  }
309
+ elseif ( is_array($APIresponse)) // This is a workaround for some obscure PHP 5.6 bug. @todo Remove when dropping support PHP < 7.
310
+ {
311
+ foreach($APIresponse as $key => $data)
312
+ {
313
+ // Running the whole array, because handleSuccess enums on key index as well :/
314
+ if (property_exists($data, 'Status'))
315
+ {
316
+ if ($status === false)
317
+ {
318
+ $status = $data->Status;
319
+ }
320
+ $APIresponse[$key] = $data; // reset it, so it can read the index. This should be 0.
321
+ }
322
+ }
323
+ }
324
+
325
+ if (isset($APIresponse['returndatalist']))
326
+ {
327
+ $returnDataList = (array) $APIresponse['returndatalist'];
328
+ if (isset($returnDataList['sizes']) && is_object($returnDataList['sizes']))
329
+ $returnDataList['sizes'] = (array) $returnDataList['sizes'];
330
+
331
+ if (isset($returnDataList['doubles']) && is_object($returnDataList['doubles']))
332
+ $returnDataList['doubles'] = (array) $returnDataList['doubles'];
333
+
334
+ if (isset($returnDataList['duplicates']) && is_object($returnDataList['duplicates']))
335
+ $returnDataList['duplicates'] = (array) $returnDataList['duplicates'];
336
+
337
+ if (isset($returnDataList['fileSizes']) && is_object($returnDataList['fileSizes']))
338
+ $returnDataList['fileSizes'] = (array) $returnDataList['fileSizes'];
339
+
340
+ unset($APIresponse['returndatalist']);
341
+ }
342
+ else {
343
+ $returnDataList = array();
344
+ }
345
 
346
  // This is only set if something is up, otherwise, ApiResponse returns array
347
  if (is_object($status))
432
  case self::STATUS_SUCCESS:
433
 
434
  //handle image has been processed
435
+ return $this->handleSuccess($item, $APIresponse, $returnDataList);
436
  default:
437
 
438
  // Theoretically this should not be needed.
484
  * @param Object $response The API Response with opt. info.
485
  * @return ObjectArray $results The Result of the optimization
486
  */
487
+ private function handleSuccess($item, $response, $returnDataList)
488
  {
489
  Log::addDebug('ShortPixel API : Handling Success!', $response);
490
+
491
  $settings = \wpSPIO()->settings();
492
  $fs = \wpSPIO()->fileSystem();
493
 
502
  $fileSize = "LosslessSize";
503
  }
504
  $webpType = "WebP" . $fileType;
505
+ $webpTypeSize = 'WebP' . $fileSize;
506
+
507
  $avifType = "AVIF" . $fileType;
508
+ $avifTypeSize = "AVIF" . $fileSize;
509
+
510
+ $dataList = $returnDataList['sizes']; // sizes have all images that should be downloaded
511
+ $dataListKeys = array_keys($dataList); // The imageThumbnail Name
512
+ $dataListValues = array_values($dataList); // The FileName.
513
 
514
+ $dataListFileSizes = array();
515
+ if (isset($returnDataList['fileSizes']))
516
+ {
517
+ $dataListFileSizes = $returnDataList['fileSizes'];
518
+ }
519
 
520
  $tempFiles = $responseFiles = $results = array();
521
 
522
+
523
  //download each file from array and process it
524
+ for ($i = 0; $i < count($response); $i++ )
525
  {
526
+ $fileData = $response[$i];
527
+ $imageName = $dataListKeys[$i];
528
+ $fileName = $dataListValues[$i];
529
+
530
+ $returnFileSize = isset($dataListFileSizes[$imageName]) ? $dataListFileSizes[$imageName] : null;
531
+
532
+
533
  if(!isset($fileData->Status)) continue; //if optimized images archive is activated, last entry of APIResponse if the Archive data.
534
 
535
  //file was processed OK
536
  if ($fileData->Status->Code == self::STATUS_SUCCESS )
537
  {
538
+
539
+ $OriginalFileSize = (! is_null($returnFileSize)) ? $returnFileSize : $fileData->OriginalSize;
540
+ $downloadResult = $this->handleDownload($fileData->$fileType, $fileData->$fileSize, $OriginalFileSize
541
  );
542
  $archive = false;
543
 
545
  * @todo Write Unit Test for Status_unchanged
546
  * But it should still be regarded as File Done. This can happen on very small file ( 6pxX6px ) which will not optimize.
547
  */
548
+ if ($downloadResult->apiStatus == self::STATUS_SUCCESS || $downloadResult->apiStatus == self::STATUS_UNCHANGED || $downloadResult->apiStatus == self::STATUS_OPTIMIZED_BIGGER )
549
  {
550
  // Removes any query ?strings and returns just filename of originalURL
551
  $originalURL = $fileData->OriginalURL;
552
 
553
+ // If the optimized result is bigger, it won'ty be replaced. Check the webp / avif files against original FileSize.
554
+ if ( $downloadResult->apiStatus === self::STATUS_OPTIMIZED_BIGGER)
555
+ {
556
+ $checkFileSize = $OriginalFileSize;
557
+ }
558
+ else {
559
+ $checkFileSize = $fileData->$fileSize;
560
+ }
561
+
562
  if (strpos($fileData->OriginalURL, '?') !== false)
563
  {
564
  $originalURL = substr($fileData->OriginalURL, 0, (strpos($fileData->OriginalURL, '?')) ); // Strip Query String from URL. If it's there!
569
 
570
  // Put it in Results.
571
  $originalName = $originalFile->getFileName();
572
+ $results[$imageName] = array('img' => $downloadResult,
573
+ 'debug-fileName' => $fileName); // This fileName is only for debugging purposes, should not be used.
574
+ // $results[$originalName] = $downloadResult;
575
 
576
  // Handle Stats
577
+ $savedSpace += $OriginalFileSize - $fileData->$fileSize;
578
+ $originalSpace += $OriginalFileSize;
579
  $optimizedSpace += $fileData->$fileSize;
580
  $fileCount++;
581
 
582
  // ** Download Webp files if they are returned **/
583
  if (isset($fileData->$webpType) && $fileData->$webpType != 'NA')
584
  {
585
+ $webpName = $originalFile->getFileBase() . '.webp';
586
+ $webpDownloadResult = false;
587
+
588
+ if ($fileData->$webpTypeSize > $checkFileSize) // if file is bigger.
589
+ {
590
+ $results[$imageName]['webp'] = $this->returnOk(self::STATUS_OPTIMIZED_BIGGER, __('Special file type bigger than core file','shortpixel-image-optimiser'));
591
+ }
592
+ elseif($archive) { // swallow pride here, or fix this.
593
  $webpDownloadResult = $this->fromArchive($archive['Path'], $fileData->$webpType, false,false);
594
  } else {
595
  $webpDownloadResult = $this->handleDownload($fileData->$webpType, false, false);
596
  }
597
 
598
+ if ($webpDownloadResult && $webpDownloadResult->apiStatus == self::STATUS_SUCCESS)
599
  {
600
  Log::addDebug('Downloaded Webp : ' . $fileData->$webpType);
601
+ $results[$imageName]['webp'] = $webpDownloadResult;
602
+ // $results[$webpName] = $webpDownloadResult;
603
  }
604
  }
605
 
606
  // ** Download Webp files if they are returned **/
607
  if (isset($fileData->$avifType) && $fileData->$avifType !== 'NA')
608
  {
609
+ $avifName = $originalFile->getFileBase() . '.avif';
610
 
611
+ if ($fileData->$avifTypeSize > $checkFileSize) // if file is bigger.
612
+ {
613
+ $results[$imageName]['avif'] = $this->returnOk(self::STATUS_OPTIMIZED_BIGGER, __('Special file type bigger than core file','shortpixel-image-optimiser'));
614
+ }
615
+ elseif($archive) { // swallow pride here, or fix this.
616
  $avifDownloadResult = $this->fromArchive($archive['Path'], $fileData->$avifType, false,false);
617
  } else {
618
  $avifDownloadResult = $this->handleDownload($fileData->$avifType, false, false);
621
  if ( $avifDownloadResult->apiStatus == self::STATUS_SUCCESS)
622
  {
623
  Log::addDebug('Downloaded Avif : ' . $fileData->$avifType);
624
+
625
+ //$results[$avifName] = $avifDownloadResult;
626
+ $results[$imageName]['avif'] = $avifDownloadResult;
627
  }
628
  }
629
 
658
  Log::addDebug("Adding $fileCount files to stats, $originalSpace went to $optimizedSpace ($savedSpace)");
659
 
660
  // *******************************
661
+ $returndata = (isset($response['returndatalist'])) ? $response['returndatalist'] : array();
662
+ $return = array(
663
+ 'files' => $results,
664
+ 'data' => $returnDataList,
665
+ );
666
 
667
+ return $this->returnSuccess($return, self::STATUS_SUCCESS, false);
668
  }
669
 
670
+
671
  /**
672
  * handles the download of an optimized image from ShortPixel API
673
  * @param string $optimizedUrl
683
  //if there is no improvement in size then we do not download this file, except (sigh) when the fileType is heic since it converts.
684
  if (($optimizedSize !== false && $originalSize !== false) && $originalSize == $optimizedSize && strpos($optimizedUrl, 'heic') === false )
685
  {
 
686
  Log::addDebug('Optimize and Original size seems the same');
687
  return $this->returnRetry(self::STATUS_UNCHANGED, __("File wasn't optimized so we do not download it.", 'shortpixel-image-optimiser'));
688
  }
689
+ /*elseif (($optimizedSize !== false && $originalSize !== false) && $optimizedSize > $originalSize )
690
+ {
691
+ Log::addDebug('Optimized size is bigger than original : Original: ' . $originalSize . ' Optimized: ' . $optimizedSize . ' ( ' . $optimizedUrl . ')' );
692
+ return $this->returnRetry(self::STATUS_OPTIMIZED_BIGGER, __("Result was bigger so we do not download it.", 'shortpixel-image-optimiser'));
693
+ } */
694
+
695
  $correctFileSize = $optimizedSize;
696
  $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl));
697
 
class/Controller/BulkController.php CHANGED
@@ -240,6 +240,7 @@ class BulkController
240
  delete_option(self::$logName);
241
  }
242
 
 
243
  public static function uninstallPlugin()
244
  {
245
  delete_option(self::$logName);
240
  delete_option(self::$logName);
241
  }
242
 
243
+ // Removes Bulk Log .
244
  public static function uninstallPlugin()
245
  {
246
  delete_option(self::$logName);
class/Controller/FileSystemController.php CHANGED
@@ -39,9 +39,9 @@ Class FileSystemController extends \ShortPixel\Controller
39
  /** Get MediaLibraryModel for a Post_id
40
  * @param int $id
41
  */
42
- public function getMediaImage($id)
43
  {
44
- if (isset(self::$mediaItems[$id]))
45
  {
46
  return self::$mediaItems[$id];
47
  }
@@ -65,9 +65,9 @@ Class FileSystemController extends \ShortPixel\Controller
65
  /**
66
  * @param int $id
67
  */
68
- public function getCustomImage($id)
69
  {
70
- if (isset(self::$customItems[$id]))
71
  {
72
  return self::$customItems[$id];
73
  }
@@ -82,6 +82,14 @@ Class FileSystemController extends \ShortPixel\Controller
82
  return $imageObj;
83
  }
84
 
 
 
 
 
 
 
 
 
85
  /** Gets a custom Image Model without being in the database. This is used to check if path is a proper customModel path ( not mediaLibrary ) and see if the file should be included per excusion rules */
86
  public function getCustomStub( $path, $load = true)
87
  {
@@ -94,15 +102,15 @@ Class FileSystemController extends \ShortPixel\Controller
94
  * int $id
95
  * string $type
96
  */
97
- public function getImage( $id, $type)
98
  {
99
  // False, OptimizeController does a hard check for false.
100
  $imageObj = false;
101
 
102
  if ($type == 'media')
103
- $imageObj = $this->getMediaImage($id);
104
  elseif($type == 'custom')
105
- $imageObj = $this->getCustomImage($id);
106
  else
107
  Log::addError('FileSystemController GetImage - no correct type given: ' . $type);
108
 
@@ -152,7 +160,8 @@ Class FileSystemController extends \ShortPixel\Controller
152
  $filepath = apply_filters('shortpixel/file/virtual/translate', $filepath, $file);
153
  }
154
 
155
- if ($filepath !== $file->getFullPath())
 
156
  {
157
  $file = $this->getFile($filepath);
158
  }
@@ -393,7 +402,7 @@ Class FileSystemController extends \ShortPixel\Controller
393
  public function downloadFile($url, $destinationPath)
394
  {
395
  $downloadTimeout = max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15);
396
- $fs = \wpSPIO()->filesystem();
397
  // $fs = \wpSPIO()->fileSystem();
398
  $destinationFile = $fs->getFile($destinationPath);
399
 
39
  /** Get MediaLibraryModel for a Post_id
40
  * @param int $id
41
  */
42
+ public function getMediaImage($id, $useCache = true)
43
  {
44
+ if ($useCache === true && isset(self::$mediaItems[$id]))
45
  {
46
  return self::$mediaItems[$id];
47
  }
65
  /**
66
  * @param int $id
67
  */
68
+ public function getCustomImage($id, $useCache = true)
69
  {
70
+ if ($useCache === true && isset(self::$customItems[$id]))
71
  {
72
  return self::$customItems[$id];
73
  }
82
  return $imageObj;
83
  }
84
 
85
+ // Use sporadically, every time an angel o performance dies.
86
+ // Required for files that change i.e. enable media replace or other filesystem changing operation.
87
+ public function flushImageCache()
88
+ {
89
+ self::$mediaItems = array();
90
+ self::$customItems = array();
91
+ }
92
+
93
  /** Gets a custom Image Model without being in the database. This is used to check if path is a proper customModel path ( not mediaLibrary ) and see if the file should be included per excusion rules */
94
  public function getCustomStub( $path, $load = true)
95
  {
102
  * int $id
103
  * string $type
104
  */
105
+ public function getImage( $id, $type, $useCache = true)
106
  {
107
  // False, OptimizeController does a hard check for false.
108
  $imageObj = false;
109
 
110
  if ($type == 'media')
111
+ $imageObj = $this->getMediaImage($id, $useCache);
112
  elseif($type == 'custom')
113
+ $imageObj = $this->getCustomImage($id, $useCache);
114
  else
115
  Log::addError('FileSystemController GetImage - no correct type given: ' . $type);
116
 
160
  $filepath = apply_filters('shortpixel/file/virtual/translate', $filepath, $file);
161
  }
162
 
163
+ // translate can return false if not properly offloaded / not found there.
164
+ if ($filepath !== $file->getFullPath() && $filepath !== false)
165
  {
166
  $file = $this->getFile($filepath);
167
  }
402
  public function downloadFile($url, $destinationPath)
403
  {
404
  $downloadTimeout = max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15);
405
+ $fs = \wpSPIO()->filesystem(); // @todo change this all to $this
406
  // $fs = \wpSPIO()->fileSystem();
407
  $destinationFile = $fs->getFile($destinationPath);
408
 
class/Controller/OptimizeController.php CHANGED
@@ -126,6 +126,13 @@ class OptimizeController
126
  $json->result->is_done = true;
127
  $json->result->fileStatus = ImageModel::FILE_STATUS_ERROR;
128
  }
 
 
 
 
 
 
 
129
  else
130
  {
131
  $result = $queue->addSingleItem($mediaItem); // 1 if ok, 0 if not found, false is not processable
@@ -272,8 +279,11 @@ class OptimizeController
272
 
273
  if ($json->status == 1) // successfull restore.
274
  {
 
 
 
275
  // Hard reload since metadata probably removed / changed but still loaded, which might enqueue wrong files.
276
- $mediaItem = \wpSPIO()->filesystem()->getImage($mediaItem->get('id'), $mediaItem->get('type'));
277
 
278
  $mediaItem->setMeta('compressionType', $compressionType);
279
  $json = $this->addItemToQueue($mediaItem);
@@ -287,7 +297,6 @@ class OptimizeController
287
  /** Returns the state of the queue so the startup JS can decide if something is going on and what. **/
288
  public function getStartupData()
289
  {
290
-
291
  $mediaQ = $this->getQueue('media');
292
  $customQ = $this->getQueue('custom');
293
 
@@ -303,7 +312,6 @@ class OptimizeController
303
  $data = $this->numberFormatStats($data);
304
 
305
  return $data;
306
-
307
  }
308
 
309
 
@@ -424,7 +432,11 @@ class OptimizeController
424
  $qtype = strtolower($qtype);
425
 
426
  $imageObj = $fs->getImage($item->item_id, $qtype);
 
 
 
427
 
 
428
  // @todo Figure out why this isn't just on action regime as well.
429
  if (property_exists($item, 'png2jpg'))
430
  {
@@ -444,7 +456,8 @@ class OptimizeController
444
  switch($item->action)
445
  {
446
  case 'restore';
447
- $imageObj->restore();
 
448
  break;
449
  case 'migrate':
450
  $imageObj->migrate(); // hard migrate in bulk, to check if all is there / resync on problems.
@@ -490,6 +503,10 @@ class OptimizeController
490
 
491
  // Regardless if it worked or not, requeue the item otherwise it will keep trying to convert due to the flag.
492
  $imageObj = $fs->getMediaImage($item->item_id);
 
 
 
 
493
  $this->addItemToQueue($imageObj);
494
 
495
  return $item;
@@ -564,19 +581,14 @@ class OptimizeController
564
  );
565
  ResponseController::addData($item->item_id, $response);
566
 
567
-
568
  if ($result->is_done )
569
  {
570
  $q->itemFailed($item, true);
571
  $this->HandleItemError($item, $qtype);
572
 
573
  ResponseController::addData($item->item_id, 'is_done', true);
574
-
575
  }
576
- else
577
- {
578
 
579
- }
580
  }
581
  elseif ($result->is_done)
582
  {
@@ -590,8 +602,6 @@ class OptimizeController
590
  $imageItem->setMeta('compressionType', $item->compressionType);
591
  }
592
 
593
- Log::addDebug('*** HandleAPIResult: Handle Optimized ** ', array_keys($result->files) );
594
-
595
  if (count($result->files) > 0 )
596
  {
597
  // Dump Stats, Dump Quota. Refresh
@@ -601,6 +611,9 @@ class OptimizeController
601
  $optimizeResult = $imageItem->handleOptimized($result->files); // returns boolean or null
602
  $item->result->improvements = $imageItem->getImprovements();
603
 
 
 
 
604
  if ($optimizeResult)
605
  {
606
  $item->result->apiStatus = ApiController::STATUS_SUCCESS;
@@ -672,7 +685,7 @@ class OptimizeController
672
  {
673
  if ($imageItem->isProcessable() && $result->apiStatus !== ApiController::STATUS_NOT_API)
674
  {
675
- Log::addDebug('Item with ID' . $imageItem->item_id . ' still has processables (with dump)');
676
  $api = $this->getAPI();
677
  $newItem = new \stdClass;
678
  $newItem->urls = $imageItem->getOptimizeUrls();
@@ -732,6 +745,13 @@ class OptimizeController
732
  }
733
  }
734
 
 
 
 
 
 
 
 
735
  // Cleaning up the debugger.
736
  $debugItem = clone $item;
737
  unset($debugItem->_queueItem);
126
  $json->result->is_done = true;
127
  $json->result->fileStatus = ImageModel::FILE_STATUS_ERROR;
128
  }
129
+ elseif($queue->isDuplicateActive($mediaItem))
130
+ {
131
+ $json->result->fileStatus = ImageModel::FILE_STATUS_UNPROCESSED;
132
+ $json->result->is_error = false;
133
+ $json->result->is_done = true;
134
+ $json->result->message = __('A duplicate of this item is already active in queue. ', 'shortpixel-image-optimiser');
135
+ }
136
  else
137
  {
138
  $result = $queue->addSingleItem($mediaItem); // 1 if ok, 0 if not found, false is not processable
279
 
280
  if ($json->status == 1) // successfull restore.
281
  {
282
+ $fs = \wpSPIO()->filesystem();
283
+ $fs->flushImageCache();
284
+
285
  // Hard reload since metadata probably removed / changed but still loaded, which might enqueue wrong files.
286
+ $mediaItem = $fs->getImage($mediaItem->get('id'), $mediaItem->get('type'));
287
 
288
  $mediaItem->setMeta('compressionType', $compressionType);
289
  $json = $this->addItemToQueue($mediaItem);
297
  /** Returns the state of the queue so the startup JS can decide if something is going on and what. **/
298
  public function getStartupData()
299
  {
 
300
  $mediaQ = $this->getQueue('media');
301
  $customQ = $this->getQueue('custom');
302
 
312
  $data = $this->numberFormatStats($data);
313
 
314
  return $data;
 
315
  }
316
 
317
 
432
  $qtype = strtolower($qtype);
433
 
434
  $imageObj = $fs->getImage($item->item_id, $qtype);
435
+ if (is_object($imageObj))
436
+ {
437
+ ResponseController::addData($item->item_id, 'fileName', $imageObj->getFileName());
438
 
439
+ }
440
  // @todo Figure out why this isn't just on action regime as well.
441
  if (property_exists($item, 'png2jpg'))
442
  {
456
  switch($item->action)
457
  {
458
  case 'restore';
459
+ // Log::addError('Restore tick is off in sendToProcessing!');
460
+ $imageObj->restore(array('keep_in_queue' => true));
461
  break;
462
  case 'migrate':
463
  $imageObj->migrate(); // hard migrate in bulk, to check if all is there / resync on problems.
503
 
504
  // Regardless if it worked or not, requeue the item otherwise it will keep trying to convert due to the flag.
505
  $imageObj = $fs->getMediaImage($item->item_id);
506
+
507
+ // Keep compressiontype from object, set in queue, imageModelToQueue
508
+ $imageObj->setMeta('compressionType', $item->compressionType);
509
+
510
  $this->addItemToQueue($imageObj);
511
 
512
  return $item;
581
  );
582
  ResponseController::addData($item->item_id, $response);
583
 
 
584
  if ($result->is_done )
585
  {
586
  $q->itemFailed($item, true);
587
  $this->HandleItemError($item, $qtype);
588
 
589
  ResponseController::addData($item->item_id, 'is_done', true);
 
590
  }
 
 
591
 
 
592
  }
593
  elseif ($result->is_done)
594
  {
602
  $imageItem->setMeta('compressionType', $item->compressionType);
603
  }
604
 
 
 
605
  if (count($result->files) > 0 )
606
  {
607
  // Dump Stats, Dump Quota. Refresh
611
  $optimizeResult = $imageItem->handleOptimized($result->files); // returns boolean or null
612
  $item->result->improvements = $imageItem->getImprovements();
613
 
614
+
615
+
616
+
617
  if ($optimizeResult)
618
  {
619
  $item->result->apiStatus = ApiController::STATUS_SUCCESS;
685
  {
686
  if ($imageItem->isProcessable() && $result->apiStatus !== ApiController::STATUS_NOT_API)
687
  {
688
+ Log::addDebug('Item with ID' . $imageItem->item_id . ' still has processables (with dump)', $imageItem->getOptimizeUrls());
689
  $api = $this->getAPI();
690
  $newItem = new \stdClass;
691
  $newItem->urls = $imageItem->getOptimizeUrls();
745
  }
746
  }
747
 
748
+ // Not relevant for further returning.
749
+ if (property_exists($item, 'paramlist'))
750
+ unset($item->paramlist);
751
+
752
+ if (property_exists($item, 'returndatalist'))
753
+ unset($item->returndatalist);
754
+
755
  // Cleaning up the debugger.
756
  $debugItem = clone $item;
757
  unset($debugItem->_queueItem);
class/Controller/Queue/MediaLibraryQueue.php CHANGED
@@ -68,7 +68,6 @@ class MediaLibraryQueue extends Queue
68
  $prepare[] = $limit;
69
 
70
  $sqlmeta = $wpdb->prepare($sqlmeta, $prepare);
71
- Log::addDebug('Media Library, Queue meta SQL' . $sqlmeta);
72
  $results = $wpdb->get_col($sqlmeta);
73
 
74
  $fs = \wpSPIO()->filesystem();
68
  $prepare[] = $limit;
69
 
70
  $sqlmeta = $wpdb->prepare($sqlmeta, $prepare);
 
71
  $results = $wpdb->get_col($sqlmeta);
72
 
73
  $fs = \wpSPIO()->filesystem();
class/Controller/Queue/Queue.php CHANGED
@@ -72,18 +72,23 @@ abstract class Queue
72
  */
73
  public function addSingleItem(ImageModel $imageModel)
74
  {
75
-
76
- // $preparing = $this->getStatus('preparing');
77
-
78
  $qItem = $this->imageModelToQueue($imageModel);
79
  $counts = $qItem->counts;
80
 
81
- $item = array('id' => $imageModel->get('id'), 'value' => $qItem, 'item_count' => $counts->creditCount);
 
 
 
 
 
 
 
 
 
82
  $this->q->addItems(array($item), false);
83
  $numitems = $this->q->withRemoveDuplicates()->enqueue(); // enqueue returns numitems
84
 
85
  // $this->q->setStatus('preparing', $preparing, true); // add single should not influence preparing status.
86
- $result = new \stdClass;
87
  $result = $this->getQStatus($result, $numitems);
88
  $result->numitems = $numitems;
89
 
@@ -203,18 +208,30 @@ abstract class Queue
203
  {
204
  continue;
205
  }
 
 
 
 
 
 
206
  $qObject = $this->imageModelToQueue($mediaItem);
207
 
208
  $counts = $qObject->counts;
209
 
210
- $queue[] = array('id' => $mediaItem->get('id'), 'value' => $qObject, 'item_count' => $counts->creditCount);
 
 
 
 
 
 
211
 
212
  $imageCount += $counts->creditCount;
213
  $webpCount += $counts->webpCount;
214
  $avifCount += $counts->avifCount;
215
  $baseCount += $counts->baseCount; // base images (all minus webp/avif)
216
 
217
- do_action('shortpixel_start_image_optimisation', $mediaItem->get('id'), $mediaItem);
218
 
219
  }
220
  else
@@ -330,6 +347,8 @@ abstract class Queue
330
 
331
  $customData = $this->getStatus('custom_data');
332
 
 
 
333
  $stats->total = $stats->in_queue + $stats->fatal_errors + $stats->errors + $stats->done + $stats->in_process;
334
  if ($stats->total > 0)
335
  {
@@ -366,9 +385,9 @@ abstract class Queue
366
  $count->images_avif = 0;
367
  if (is_object($customData))
368
  {
369
- $count->images_webp = $customData->webpCount;
370
- $count->images_avif = $customData->avifCount;
371
- $count->images_basecount = $customData->baseCount;
372
  }
373
 
374
  return $count;
@@ -467,32 +486,82 @@ abstract class Queue
467
  $item = new \stdClass;
468
  $item->compressionType = \wpSPIO()->settings()->compressionType;
469
 
470
- $urls = $imageModel->getOptimizeUrls();
471
- $imagePreview = UIHelper::findBestPreview($imageModel, 800, true);
472
- $imagePreviewURL = (is_object($imagePreview)) ? $imagePreview->getURL() : false;
 
 
 
 
 
 
 
 
473
 
474
  $counts = new \stdClass;
475
- $counts->creditCount = 0; // count the used credits for this item.
476
- $counts->baseCount = 0; // count the base images.
477
- $counts->avifCount = 0;
478
- $counts->webpCount = 0;
479
  //$creditCount = 0;
480
 
481
- $webps = ($imageModel->isProcessableFileType('webp')) ? $imageModel->getOptimizeFileType('webp') : null;
482
- $avifs = ($imageModel->isProcessableFileType('avif')) ? $imageModel->getOptimizeFileType('avif') : null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
 
484
- $hasUrls = (count($urls) > 0) ? true : false;
 
485
  $hasWebps = (! is_null($webps) && count($webps) > 0) ? true : false;
486
  $hasAvifs = (! is_null($avifs) && count($avifs) > 0) ? true : false;
487
  $flags = array();
488
  $items = array();
489
 
490
- $webpLeft = $avifLeft = false;
491
 
492
- if (is_null($webps) && is_null($avifs))
493
  {
494
  // nothing.
495
- // $items[] = $item;
496
  $counts->creditCount += count($urls);
497
  $counts->baseCount += count($urls);
498
 
@@ -569,11 +638,9 @@ abstract class Queue
569
  }
570
  }
571
 
572
- }
573
 
574
- // $paths = $imageModel->getOptimizePaths();
575
- //Log::addDebug('AvifL on ' . $imageModel->get('id') . ' ', array($avifLeft, $urls));
576
- if ($imageModel->get('do_png2jpg') && $hasUrls) // Flag is set in Is_Processable in mediaLibraryModel, when settings are on, image is png.
577
  {
578
  $item->png2jpg = $imageModel->get('do_png2jpg');
579
  }
@@ -583,17 +650,32 @@ abstract class Queue
583
  {
584
  $item->compressionType = $imageModel->getMeta('compressionType');
585
  }
586
- $item->flags = $flags;
587
 
588
  // Former securi function, add timestamp to all URLS, for cache busting.
589
- $urls = $this->timestampURLS($urls, $imageModel->get('id'));
 
590
  $item->urls = apply_filters('shortpixel_image_urls', $urls, $imageModel->get('id'));
591
- $item->preview = $imagePreviewURL;
 
 
 
 
 
 
 
 
 
592
  $item->counts = $counts;
593
 
594
  return $item;
595
  }
596
 
 
 
 
 
 
 
597
  protected function timestampURLS($urls, $id)
598
  {
599
  // https://developer.wordpress.org/reference/functions/get_post_modified_time/
@@ -635,6 +717,47 @@ abstract class Queue
635
  $this->q->updateItemValue($qItem);
636
  }
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  public function itemDone ($item)
639
  {
640
  $qItem = $this->mediaItemToQueue($item); // convert again
72
  */
73
  public function addSingleItem(ImageModel $imageModel)
74
  {
 
 
 
75
  $qItem = $this->imageModelToQueue($imageModel);
76
  $counts = $qItem->counts;
77
 
78
+ $media_id = $imageModel->get('id');
79
+ // Check if this is a duplicate existing.
80
+ if ($imageModel->getParent() !== false)
81
+ {
82
+ $media_id = $imageModel->getParent();
83
+ }
84
+
85
+ $result = new \stdClass;
86
+
87
+ $item = array('id' => $media_id, 'value' => $qItem, 'item_count' => $counts->creditCount);
88
  $this->q->addItems(array($item), false);
89
  $numitems = $this->q->withRemoveDuplicates()->enqueue(); // enqueue returns numitems
90
 
91
  // $this->q->setStatus('preparing', $preparing, true); // add single should not influence preparing status.
 
92
  $result = $this->getQStatus($result, $numitems);
93
  $result->numitems = $numitems;
94
 
208
  {
209
  continue;
210
  }
211
+
212
+ if ($this->isDuplicateActive($mediaItem, $queue))
213
+ {
214
+ continue;
215
+ }
216
+
217
  $qObject = $this->imageModelToQueue($mediaItem);
218
 
219
  $counts = $qObject->counts;
220
 
221
+ $media_id = $mediaItem->get('id');
222
+ if ($mediaItem->getParent() !== false)
223
+ {
224
+ $media_id = $mediaItem->getParent();
225
+ }
226
+
227
+ $queue[] = array('id' => $media_id, 'value' => $qObject, 'item_count' => $counts->creditCount);
228
 
229
  $imageCount += $counts->creditCount;
230
  $webpCount += $counts->webpCount;
231
  $avifCount += $counts->avifCount;
232
  $baseCount += $counts->baseCount; // base images (all minus webp/avif)
233
 
234
+ do_action('shortpixel_start_image_optimisation', $media_id, $mediaItem);
235
 
236
  }
237
  else
347
 
348
  $customData = $this->getStatus('custom_data');
349
 
350
+
351
+
352
  $stats->total = $stats->in_queue + $stats->fatal_errors + $stats->errors + $stats->done + $stats->in_process;
353
  if ($stats->total > 0)
354
  {
385
  $count->images_avif = 0;
386
  if (is_object($customData))
387
  {
388
+ $count->images_webp = (int) $customData->webpCount;
389
+ $count->images_avif = (int) $customData->avifCount;
390
+ $count->images_basecount = (int) $customData->baseCount;
391
  }
392
 
393
  return $count;
486
  $item = new \stdClass;
487
  $item->compressionType = \wpSPIO()->settings()->compressionType;
488
 
489
+ // $urls = $imageModel->getOptimizeUrls();
490
+ $data = $imageModel->getOptimizeData();
491
+ $urls = $data['urls'];
492
+ $params = $data['params'];
493
+
494
+ // $imagePreview = UIHelper::findBestPreview($imageModel, 800, true);
495
+ // $imagePreviewURL = (is_object($imagePreview)) ? $imagePreview->getURL() : false;
496
+
497
+ list($u, $baseCount) = $imageModel->getCountOptimizeData('thumbnails');
498
+ list($u, $webpCount) = $imageModel->getCountOptimizeData('webp');
499
+ list($u, $avifCount) = $imageModel->getCountOptimizeData('avif');
500
 
501
  $counts = new \stdClass;
502
+ $counts->creditCount = $baseCount + $webpCount + $avifCount; // count the used credits for this item.
503
+ $counts->baseCount = $baseCount; // count the base images.
504
+ $counts->avifCount = $avifCount;
505
+ $counts->webpCount = $webpCount;
506
  //$creditCount = 0;
507
 
508
+ // $webps = ($imageModel->isProcessableFileType('webp')) ? $imageModel->getOptimizeFileType('webp') : null;
509
+ // $avifs = ($imageModel->isProcessableFileType('avif')) ? $imageModel->getOptimizeFileType('avif') : null;
510
+
511
+ $removeKeys = array('image', 'webp', 'avif'); // keys not native to API / need to be removed.
512
+
513
+ // Is UI info, not for processing.
514
+ if (isset($data['params']['paths']))
515
+ {
516
+ unset($data['params']['paths']);
517
+ }
518
+
519
+ foreach($data['params'] as $sizeName => $param)
520
+ {
521
+ $plus = false;
522
+ $convertTo = array();
523
+ if ($param['image'] === true)
524
+ {
525
+ $plus = true;
526
+ }
527
+ if ($param['webp'] === true)
528
+ {
529
+ $convertTo[] = ($plus === true) ? '+webp' : 'webp';
530
+ }
531
+ if ($param['avif'] === true)
532
+ {
533
+ $convertTo[] = ($plus === true) ? '+avif' : 'avif';
534
+ }
535
+
536
+ foreach($removeKeys as $key)
537
+ {
538
+ if (isset($param[$key]))
539
+ {
540
+ unset($data['params'][$sizeName][$key]);
541
+ }
542
+ }
543
+
544
+ if (count($convertTo) > 0)
545
+ {
546
+ $convertTo = implode('|', $convertTo);
547
+ $data['params'][$sizeName]['convertto'] = $convertTo;
548
+ }
549
+ }
550
+
551
+
552
 
553
+
554
+ /* $hasUrls = (count($urls) > 0) ? true : false;
555
  $hasWebps = (! is_null($webps) && count($webps) > 0) ? true : false;
556
  $hasAvifs = (! is_null($avifs) && count($avifs) > 0) ? true : false;
557
  $flags = array();
558
  $items = array();
559
 
560
+ $webpLeft = $avifLeft = false; */
561
 
562
+ /*if (is_null($webps) && is_null($avifs))
563
  {
564
  // nothing.
 
565
  $counts->creditCount += count($urls);
566
  $counts->baseCount += count($urls);
567
 
638
  }
639
  }
640
 
641
+ } */
642
 
643
+ if ($imageModel->get('do_png2jpg') && $baseCount > 0) // Flag is set in Is_Processable in mediaLibraryModel, when settings are on, image is png.
 
 
644
  {
645
  $item->png2jpg = $imageModel->get('do_png2jpg');
646
  }
650
  {
651
  $item->compressionType = $imageModel->getMeta('compressionType');
652
  }
 
653
 
654
  // Former securi function, add timestamp to all URLS, for cache busting.
655
+ $urls = $this->timestampURLS( array_values($urls), $imageModel->get('id'));
656
+
657
  $item->urls = apply_filters('shortpixel_image_urls', $urls, $imageModel->get('id'));
658
+ if (count($data['params']) > 0)
659
+ {
660
+ $item->paramlist= array_values($data['params']);
661
+ }
662
+
663
+ if (count($data['returnParams']) > 0)
664
+ {
665
+ $item->returndatalist = $data['returnParams'];
666
+ }
667
+ // $item->preview = $imagePreviewURL;
668
  $item->counts = $counts;
669
 
670
  return $item;
671
  }
672
 
673
+ // @internal
674
+ public function _debug_imageModelToQueue($imageModel)
675
+ {
676
+ return $this->imageModelToQueue($imageModel);
677
+ }
678
+
679
  protected function timestampURLS($urls, $id)
680
  {
681
  // https://developer.wordpress.org/reference/functions/get_post_modified_time/
717
  $this->q->updateItemValue($qItem);
718
  }
719
 
720
+ public function isDuplicateActive($mediaItem, $queue = array() )
721
+ {
722
+ if ($mediaItem->get('type') === 'custom')
723
+ return false;
724
+
725
+ $WPMLduplicates = $mediaItem->getWPMLDuplicates();
726
+ $qitems = array();
727
+ if (count($queue) > 0)
728
+ {
729
+ foreach($queue as $qitem)
730
+ {
731
+ $qitems[] = $qitem['id'];
732
+ }
733
+ }
734
+
735
+ if (is_array($WPMLduplicates) && count($WPMLduplicates) > 0)
736
+ {
737
+ $duplicateActive = false;
738
+ foreach($WPMLduplicates as $duplicate_id)
739
+ {
740
+ if (in_array($duplicate_id, $qitems))
741
+ {
742
+ Log::addDebug('Duplicate Item is in queue already, skipping (ar). Duplicate:' . $duplicate_id);
743
+ $duplicateActive = true;
744
+ break;
745
+ }
746
+ elseif ($this->isItemInQueue($duplicate_id))
747
+ {
748
+ Log::addDebug('Duplicate Item is in queue already, skipping (db). Duplicate:' . $duplicate_id);
749
+ $duplicateActive = true;
750
+ break;
751
+ }
752
+ }
753
+ if (true === $duplicateActive)
754
+ {
755
+ return $duplicateActive;
756
+ }
757
+ }
758
+ return false;
759
+ }
760
+
761
  public function itemDone ($item)
762
  {
763
  $qItem = $this->mediaItemToQueue($item); // convert again
class/Controller/ResponseController.php CHANGED
@@ -95,7 +95,6 @@ class ResponseController
95
  $data = $name;
96
  }
97
 
98
-
99
  $item_type = (array_key_exists('item_type', $data)) ? $data['item_type'] : false;
100
  // If no queue / queue type is set, set it if item type is passed to ResponseController. For items outside the queue system.
101
  if ($item_type && is_null(self::$queueType))
@@ -109,6 +108,7 @@ class ResponseController
109
  {
110
  if (property_exists($resp, $prop))
111
  {
 
112
  $resp->$prop = $val;
113
  }
114
  else {
95
  $data = $name;
96
  }
97
 
 
98
  $item_type = (array_key_exists('item_type', $data)) ? $data['item_type'] : false;
99
  // If no queue / queue type is set, set it if item type is passed to ResponseController. For items outside the queue system.
100
  if ($item_type && is_null(self::$queueType))
108
  {
109
  if (property_exists($resp, $prop))
110
  {
111
+
112
  $resp->$prop = $val;
113
  }
114
  else {
class/Controller/SettingsController.php CHANGED
@@ -162,7 +162,7 @@ class SettingsController extends \ShortPixel\ViewController
162
  }
163
  else
164
  {
165
- Notice::addError( __('Unexpected error obtain your ShortPixel key. Please contact support about this', 'shortpixel-image-optimiser') . ' ' . json_encode($body) );
166
  }
167
 
168
  $this->doRedirect();
@@ -291,6 +291,7 @@ class SettingsController extends \ShortPixel\ViewController
291
  $message = sprintf(__('All items in the %s queue have been removed and the process is stopped', 'shortpixel-image-optimiser'), $queue);
292
  }
293
 
 
294
  Notice::addSuccess($message);
295
  }
296
 
@@ -356,7 +357,12 @@ class SettingsController extends \ShortPixel\ViewController
356
  if ($this->do_redirect)
357
  $this->doRedirect('bulk');
358
  else {
359
- Notice::addSuccess(__('Settings Saved', 'shortpixel-image-optimiser'));
 
 
 
 
 
360
  $this->doRedirect();
361
  }
362
  }
162
  }
163
  else
164
  {
165
+ Notice::addError( __('Unexpected error obtaining the ShortPixel key. Please contact support about this:', 'shortpixel-image-optimiser') . ' ' . json_encode($body) );
166
  }
167
 
168
  $this->doRedirect();
291
  $message = sprintf(__('All items in the %s queue have been removed and the process is stopped', 'shortpixel-image-optimiser'), $queue);
292
  }
293
 
294
+
295
  Notice::addSuccess($message);
296
  }
297
 
357
  if ($this->do_redirect)
358
  $this->doRedirect('bulk');
359
  else {
360
+
361
+ $noticeController = Notice::getInstance();
362
+ $notice = Notice::addSuccess(__('Settings Saved', 'shortpixel-image-optimiser'));
363
+ $notice->is_removable = false;
364
+ $noticeController->update();
365
+
366
  $this->doRedirect();
367
  }
368
  }
class/Controller/View/BulkViewController.php CHANGED
@@ -111,6 +111,11 @@ class BulkViewController extends \ShortPixel\ViewController
111
  if (is_numeric($value))
112
  $approx->media->$item = max($value, 0);
113
  }
 
 
 
 
 
114
  return $approx;
115
 
116
  }
111
  if (is_numeric($value))
112
  $approx->media->$item = max($value, 0);
113
  }
114
+ foreach($approx->total as $item => $value)
115
+ {
116
+ if (is_numeric($value))
117
+ $approx->total->$item = max($value, 0);
118
+ }
119
  return $approx;
120
 
121
  }
class/Controller/View/EditMediaViewController.php CHANGED
@@ -3,6 +3,7 @@ namespace ShortPixel\Controller\View;
3
  use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
 
5
  use ShortPixel\Helper\UiHelper as UiHelper;
 
6
 
7
 
8
  //use ShortPixel\Model\ImageModel as ImageModel;
@@ -134,6 +135,12 @@ class EditMediaViewController extends \ShortPixel\ViewController
134
  if ($tsOptimized !== null)
135
  $stats[] = array(__("Optimized on :", 'shortpixel-image-optimiser') . "<br /> ", UiHelper::formatTS($tsOptimized) );
136
 
 
 
 
 
 
 
137
  return $stats;
138
  }
139
 
@@ -152,36 +159,18 @@ class EditMediaViewController extends \ShortPixel\ViewController
152
 
153
  if ($imageObj->isProcessable())
154
  {
155
- $urls = $imageObj->getOptimizeUrls();
156
-
157
- if ($imageObj->isProcessableFileType('webp'))
158
- {
159
- $diff = array_diff($imageObj->getOptimizeFileType('webp'), $urls); // diff from mains.
160
- foreach($diff as $i => $v)
161
- {
162
- $diff[$i] = " [webp] " . $v;
163
- }
164
- $urls = array_merge($urls, $diff);
165
- }
166
- if ($imageObj->isProcessableFileType('avif'))
167
- {
168
- $diff = array_diff($imageObj->getOptimizeFileType('avif'), $urls); // diff from mains.
169
- foreach($diff as $i => $v)
170
- {
171
- $diff[$i] = " [avif] " . $v;
172
- }
173
- $urls = array_merge($urls, $diff);
174
- }
175
  }
176
 
177
  $thumbnails = $imageObj->get('thumbnails');
178
  $processable = ($imageObj->isProcessable()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> (' . $imageObj->getReason('processable') . ')';
 
179
  $restorable = ($imageObj->isRestorable()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> (' . $imageObj->getReason('restorable') . ')';
180
 
181
  $hasrecord = ($imageObj->hasDBRecord()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> ';
182
- // $sizes = isset($this->data['sizes']) ? $this->data['sizes'] : array();
183
-
184
- //$debugMeta = $imageObj->debugGetImageMeta();
185
 
186
  $debugInfo = array();
187
  $debugInfo[] = array(__('URL (get attachment URL)', 'shortpixel_image_optiser'), wp_get_attachment_url($this->post_id));
@@ -195,23 +184,35 @@ class EditMediaViewController extends \ShortPixel\ViewController
195
  $debugInfo[] = array(__('Size and Mime (ImageObj)'), $imageObj->get('width') . 'x' . $imageObj->get('height'). ' (' . $imageObj->get('mime') . ')');
196
  $debugInfo[] = array(__('Status (ShortPixel)'), $imageObj->getMeta('status') . ' ' );
197
 
198
-
199
-
200
  $debugInfo[] = array(__('Processable'), $processable);
 
201
  $debugInfo[] = array(__('Restorable'), $restorable);
202
  $debugInfo[] = array(__('Record'), $hasrecord);
203
 
204
  $debugInfo[] = array(__('WPML Duplicates'), json_encode($imageObj->getWPMLDuplicates()) );
205
 
 
 
 
 
 
206
  if (isset($urls))
207
  {
208
  $debugInfo[] = array(__('To Optimize URLS'), $urls);
209
  }
 
 
 
 
 
 
 
 
 
210
 
211
  $debugInfo['imagemetadata'] = array(__('ImageModel Metadata (ShortPixel)'), $imageObj);
212
  $debugInfo[] = array('', '<hr>');
213
 
214
- //$debugInfo['shortpixeldata'] = array(__('Data'), $this->data);
215
  $debugInfo['wpmetadata'] = array(__('WordPress Get Attachment Metadata'), $meta );
216
  $debugInfo[] = array('', '<hr>');
217
 
3
  use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
 
5
  use ShortPixel\Helper\UiHelper as UiHelper;
6
+ use ShortPixel\Controller\OptimizeController as OptimizeController;
7
 
8
 
9
  //use ShortPixel\Model\ImageModel as ImageModel;
135
  if ($tsOptimized !== null)
136
  $stats[] = array(__("Optimized on :", 'shortpixel-image-optimiser') . "<br /> ", UiHelper::formatTS($tsOptimized) );
137
 
138
+ if ($imageObj->isOptimized())
139
+ {
140
+ $stats[] = array( sprintf(__('%s %s Read more about theses stats %s ', 'shortpixel-image-optimiser'), '
141
+ <p><img alt=' . esc_html('Info Icon', 'shortpixel-image-optimiser') . ' src=' . esc_url( wpSPIO()->plugin_url('res/img/info-icon.png' )) . ' style="margin-bottom: -4px;"/>', '<a href="https://shortpixel.com/knowledge-base/article/553-the-stats-from-the-shortpixel-column-in-the-media-library-explained" target="_blank">', '</a></p>'), '');
142
+ }
143
+
144
  return $stats;
145
  }
146
 
159
 
160
  if ($imageObj->isProcessable())
161
  {
162
+ //$urls = $imageObj->getOptimizeUrls();
163
+ $optimizeData = $imageObj->getOptimizeData();
164
+ $urls = $optimizeData['urls'];
165
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
 
168
  $thumbnails = $imageObj->get('thumbnails');
169
  $processable = ($imageObj->isProcessable()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> (' . $imageObj->getReason('processable') . ')';
170
+ $anyFileType = ($imageObj->isProcessableAnyFileType()) ? '<span class="green">Yes</span>' : '<span class="red">No</span>';
171
  $restorable = ($imageObj->isRestorable()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> (' . $imageObj->getReason('restorable') . ')';
172
 
173
  $hasrecord = ($imageObj->hasDBRecord()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> ';
 
 
 
174
 
175
  $debugInfo = array();
176
  $debugInfo[] = array(__('URL (get attachment URL)', 'shortpixel_image_optiser'), wp_get_attachment_url($this->post_id));
184
  $debugInfo[] = array(__('Size and Mime (ImageObj)'), $imageObj->get('width') . 'x' . $imageObj->get('height'). ' (' . $imageObj->get('mime') . ')');
185
  $debugInfo[] = array(__('Status (ShortPixel)'), $imageObj->getMeta('status') . ' ' );
186
 
 
 
187
  $debugInfo[] = array(__('Processable'), $processable);
188
+ $debugInfo[] = array(__('Avif/Webp needed'), $anyFileType);
189
  $debugInfo[] = array(__('Restorable'), $restorable);
190
  $debugInfo[] = array(__('Record'), $hasrecord);
191
 
192
  $debugInfo[] = array(__('WPML Duplicates'), json_encode($imageObj->getWPMLDuplicates()) );
193
 
194
+ if ($imageObj->getParent() !== false)
195
+ {
196
+ $debugInfo[] = array(__('WPML duplicate - Parent: '), $imageObj->getParent());
197
+ }
198
+
199
  if (isset($urls))
200
  {
201
  $debugInfo[] = array(__('To Optimize URLS'), $urls);
202
  }
203
+ if (isset($optimizeData))
204
+ {
205
+ $debugInfo[] = array(__('Optimize Data'), $optimizeData);
206
+
207
+ $optControl = new optimizeController();
208
+ $q = $optControl->getQueue($imageObj->get('type'));
209
+
210
+ $debugInfo[] = array(__('Image to Queue'), $q->_debug_imageModelToQueue($imageObj) );
211
+ }
212
 
213
  $debugInfo['imagemetadata'] = array(__('ImageModel Metadata (ShortPixel)'), $imageObj);
214
  $debugInfo[] = array('', '<hr>');
215
 
 
216
  $debugInfo['wpmetadata'] = array(__('WordPress Get Attachment Metadata'), $meta );
217
  $debugInfo[] = array('', '<hr>');
218
 
class/Controller/View/ListMediaViewController.php CHANGED
@@ -78,18 +78,30 @@ class ListMediaViewController extends \ShortPixel\ViewController
78
  foreach($items as $item_id)
79
  {
80
  $mediaItem = $fs->getMediaImage($item_id);
 
81
  switch($plugin_action)
82
  {
83
  case 'optimize':
84
- $res = $optimizeController->addItemToQueue($mediaItem);
 
85
  break;
86
  case 'glossy':
87
  case 'lossy':
88
  case 'lossless':
89
- $res = $optimizeController->reOptimizeItem($mediaItem, $targetCompressionType);
 
 
 
 
 
 
 
 
 
90
  break;
91
  case 'restore';
92
- $res = $optimizeController->restoreItem($mediaItem);
 
93
  break;
94
  }
95
 
78
  foreach($items as $item_id)
79
  {
80
  $mediaItem = $fs->getMediaImage($item_id);
81
+
82
  switch($plugin_action)
83
  {
84
  case 'optimize':
85
+ if ($mediaItem->isProcessable())
86
+ $res = $optimizeController->addItemToQueue($mediaItem);
87
  break;
88
  case 'glossy':
89
  case 'lossy':
90
  case 'lossless':
91
+ if ($mediaItem->isOptimized() && $mediaItem->getMeta('compressionType') == $targetCompressionType )
92
+ {
93
+ // do nothing if already done w/ this compression.
94
+ }
95
+ elseif(! $mediaItem->isOptimized())
96
+ {
97
+ $res = $optimizeController->addItemToQueue($mediaItem);
98
+ }
99
+ else
100
+ $res = $optimizeController->reOptimizeItem($mediaItem, $targetCompressionType);
101
  break;
102
  case 'restore';
103
+ if ($mediaItem->isOptimized())
104
+ $res = $optimizeController->restoreItem($mediaItem);
105
  break;
106
  }
107
 
class/Helper/InstallHelper.php CHANGED
@@ -67,7 +67,6 @@ class InstallHelper
67
  // $env = \wpSPIO()->env();
68
 
69
  OptimizeController::uninstallPlugin();
70
- BulkController::uninstallPlugin();
71
  ApiKeyController::uninstallPlugin();
72
  }
73
 
@@ -86,6 +85,8 @@ class InstallHelper
86
  self::deactivatePlugin(); // deactivate
87
  self::uninstallPlugin(); // uninstall
88
 
 
 
89
 
90
  $settings::resetOptions();
91
 
@@ -269,6 +270,7 @@ class InstallHelper
269
  message varchar(255),
270
  ts_added timestamp,
271
  ts_optimized timestamp,
 
272
  PRIMARY KEY sp_id (id)
273
  ) $charsetCollate;";
274
 
@@ -285,7 +287,7 @@ class InstallHelper
285
  attach_id bigint unsigned NOT NULL,
286
  parent bigint unsigned NOT NULL,
287
  image_type tinyint default 0,
288
- size varchar(150),
289
  status tinyint default 0,
290
  compression_type tinyint,
291
  compressed_size int,
67
  // $env = \wpSPIO()->env();
68
 
69
  OptimizeController::uninstallPlugin();
 
70
  ApiKeyController::uninstallPlugin();
71
  }
72
 
85
  self::deactivatePlugin(); // deactivate
86
  self::uninstallPlugin(); // uninstall
87
 
88
+ // Bulk Log
89
+ BulkController::uninstallPlugin();
90
 
91
  $settings::resetOptions();
92
 
270
  message varchar(255),
271
  ts_added timestamp,
272
  ts_optimized timestamp,
273
+ extra_info LONGTEXT,
274
  PRIMARY KEY sp_id (id)
275
  ) $charsetCollate;";
276
 
287
  attach_id bigint unsigned NOT NULL,
288
  parent bigint unsigned NOT NULL,
289
  image_type tinyint default 0,
290
+ size varchar(200),
291
  status tinyint default 0,
292
  compression_type tinyint,
293
  compressed_size int,
class/Helper/UiHelper.php CHANGED
@@ -135,8 +135,9 @@ class UiHelper
135
  </div>";
136
 
137
  }
138
- $output .= "</div></div> <!-- thumb optimized -->";
139
  }
 
140
  }
141
 
142
  if ($retinasDone > 0)
@@ -153,16 +154,18 @@ class UiHelper
153
  }
154
  if ($imageObj->isOptimized() && $imageObj->isProcessable())
155
  {
156
- $optimizable = $imageObj->getOptimizeURLS(true);
 
 
157
  // Todo check if Webp / Acif is active, check for unoptimized items
158
- $processWebp = ($imageObj->isProcessableFileType('webp')) ? true : false;
159
- $processAvif = ($imageObj->isProcessableFileType('avif')) ? true : false;
160
 
161
- if (count($optimizable) > 0)
162
  {
163
- $output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d to optimize', 'shortpixel-image-optimiser'), count($optimizable)) . '</h4>';
164
  $output .= "<span>";
165
- foreach($optimizable as $optObj)
166
  {
167
  $output .= substr($optObj, strrpos($optObj, '/')+1) . '<br>';
168
  }
@@ -170,24 +173,23 @@ class UiHelper
170
  $output .= '</div>';
171
  }
172
 
173
- if ($processWebp && count($optimizable) == 0)
174
  {
175
- $webps = $imageObj->getOptimizeFileType('webp');
176
- $output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d Webp files to create', 'shortpixel-image-optimiser'), count($webps)) . '</h4>';
177
  $output .= "<span>";
178
- foreach($webps as $optObj)
179
  {
180
  $output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'webp') . '<br>';
181
  }
182
  $output .= "</span>";
183
  $output .= '</div>';
184
  }
185
- if ($processAvif && count($optimizable) == 0)
186
  {
187
- $avifs = $imageObj->getOptimizeFileType('avif');
188
- $output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d Avif files to create', 'shortpixel-image-optimiser'), count($avifs)) . '</h4>';
189
  $output .= "<span>";
190
- foreach($avifs as $optObj)
191
  {
192
  $output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'avif') . '<br>';
193
  }
@@ -196,8 +198,6 @@ class UiHelper
196
  }
197
  }
198
 
199
-
200
-
201
  return $output;
202
 
203
  }
@@ -240,20 +240,25 @@ class UiHelper
240
 
241
  if ($mediaItem->isOptimized() )
242
  {
243
- $optimizable = $mediaItem->getOptimizeURLS(true);
 
 
244
 
245
  if ($mediaItem->isProcessable() && ! $mediaItem->isOptimizePrevented())
246
  {
247
  $action = self::getAction('optimizethumbs', $id);
248
- if (count($optimizable) > 0)
249
  {
250
- $action['text'] = sprintf(__('Optimize %s thumbnails','shortpixel-image-optimiser'),count($optimizable));
 
 
 
 
 
 
251
  }
252
  else
253
  {
254
- $optimizableWebp = $mediaItem->isProcessableFileType('webp') ? count($mediaItem->getOptimizeFileType('webp', true)) : 0;
255
- $optimizableAvif = $mediaItem->isProcessableFileType('avif') ? count($mediaItem->getOptimizeFileType('avif', true)) : 0;
256
-
257
  if ($optimizableWebp > 0 && $optimizableAvif > 0)
258
  $text = sprintf(__('Optimize %s webps and %s avif','shortpixel-image-optimiser'),$optimizableWebp, $optimizableAvif);
259
  elseif ($optimizableWebp > 0)
@@ -383,7 +388,14 @@ class UiHelper
383
  // This basically happens when a NextGen gallery is not added to Custom Media.
384
  elseif ($mediaItem->get('id') === 0)
385
  {
386
- $text = __('This image was not found in our database. Refresh folders, or add this gallery', 'shortpixel-image-optimiser');
 
 
 
 
 
 
 
387
  }
388
  elseif ($mediaItem->isOptimized())
389
  {
@@ -414,15 +426,19 @@ class UiHelper
414
 
415
 
416
  $redo_legacy = false;
417
- $was_converted = get_post_meta($mediaItem->get('id'), '_shortpixel_was_converted', true);
418
- $updateTs = 1656892800; // July 4th 2022 - 00:00 GMT
419
 
420
- if ($was_converted < $updateTs)
421
  {
422
- $meta = $mediaItem->getWPMetaData();
423
- if (is_array($meta) && isset($meta['ShortPixel']))
 
 
424
  {
425
- $redo_legacy = self::getAction('redo_legacy', $mediaItem->get('id'));
 
 
 
 
426
  }
427
  }
428
 
@@ -609,10 +625,12 @@ class UiHelper
609
  $decimalpoint = isset($wp_locale->number_format['decimal_point']) ? $wp_locale->number_format['decimal_point'] : false;
610
  $number = number_format_i18n( (float) $number, $precision);
611
 
 
 
612
  // Don't show trailing zeroes if number is a whole unbroken number. -> string comparison because number_format_i18n returns string.
613
- if ($decimalpoint !== false && substr($number, strpos($number, $decimalpoint) + 1) === '00')
614
  {
615
- $number = substr($number, 0, strpos($number, $decimalpoint));
616
  }
617
  // Some locale's have no-breaking-space as thousands separator. This doesn't work well in JS / Cron Shell so replace with space.
618
  $number = str_replace('&nbsp;', ' ', $number);
@@ -636,7 +654,7 @@ class UiHelper
636
  }
637
  else
638
  {
639
- return substr($name, 0, strpos($name, '.')) . '.' . $type;
640
  }
641
 
642
  }
135
  </div>";
136
 
137
  }
138
+ $output .= "</div> <!-- /thumb-wrapper -->";
139
  }
140
+ $output .= "</div> <!-- /thumb optimized -->";
141
  }
142
 
143
  if ($retinasDone > 0)
154
  }
155
  if ($imageObj->isOptimized() && $imageObj->isProcessable())
156
  {
157
+ list($urls, $optimizable) = $imageObj->getCountOptimizeData('thumbnails');
158
+ list($webpUrls, $webpCount) = $imageObj->getCountOptimizeData('webp');
159
+ list($avifUrls, $avifCount) = $imageObj->getCountOptimizeData('avif');
160
  // Todo check if Webp / Acif is active, check for unoptimized items
161
+ // $processWebp = ($imageObj->isProcessableFileType('webp')) ? true : false;
162
+ // $processAvif = ($imageObj->isProcessableFileType('avif')) ? true : false;
163
 
164
+ if ($optimizable > 0)
165
  {
166
+ $output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d to optimize', 'shortpixel-image-optimiser'), $optimizable) . '</h4>';
167
  $output .= "<span>";
168
+ foreach($urls as $optObj)
169
  {
170
  $output .= substr($optObj, strrpos($optObj, '/')+1) . '<br>';
171
  }
173
  $output .= '</div>';
174
  }
175
 
176
+ if ($webpCount > 0 )
177
  {
178
+
179
+ $output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d Webp files to create', 'shortpixel-image-optimiser'), $webpCount) . '</h4>';
180
  $output .= "<span>";
181
+ foreach($webpUrls as $optObj)
182
  {
183
  $output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'webp') . '<br>';
184
  }
185
  $output .= "</span>";
186
  $output .= '</div>';
187
  }
188
+ if ($avifCount > 0)
189
  {
190
+ $output .= '<div class="thumbs-todo"><h4>' . sprintf(__('%d Avif files to create', 'shortpixel-image-optimiser'), $avifCount) . '</h4>';
 
191
  $output .= "<span>";
192
+ foreach($avifUrls as $optObj)
193
  {
194
  $output .= self::convertImageTypeName(substr($optObj, strrpos($optObj, '/')+1), 'avif') . '<br>';
195
  }
198
  }
199
  }
200
 
 
 
201
  return $output;
202
 
203
  }
240
 
241
  if ($mediaItem->isOptimized() )
242
  {
243
+ list($u, $optimizable) = $mediaItem->getCountOptimizeData('thumbnails');
244
+ list($u, $optimizableWebp) = $mediaItem->getCountOptimizeData('webp');
245
+ list($u, $optimizableAvif) = $mediaItem->getCountOptimizeData('avif');
246
 
247
  if ($mediaItem->isProcessable() && ! $mediaItem->isOptimizePrevented())
248
  {
249
  $action = self::getAction('optimizethumbs', $id);
250
+ if ($optimizable > 0)
251
  {
252
+ $total = $optimizable + $optimizableWebp + $optimizableAvif;
253
+ if ($optimizableWebp > 0 || $optimizableAvif > 0)
254
+ $itemText = __('items', 'shortpixel-image-optimiser');
255
+ else {
256
+ $itemText = __('thumbnails', 'shortpixel-image-optimiser');
257
+ }
258
+ $action['text'] = sprintf(__('Optimize %s %s','shortpixel-image-optimiser'),$total, $itemText);
259
  }
260
  else
261
  {
 
 
 
262
  if ($optimizableWebp > 0 && $optimizableAvif > 0)
263
  $text = sprintf(__('Optimize %s webps and %s avif','shortpixel-image-optimiser'),$optimizableWebp, $optimizableAvif);
264
  elseif ($optimizableWebp > 0)
388
  // This basically happens when a NextGen gallery is not added to Custom Media.
389
  elseif ($mediaItem->get('id') === 0)
390
  {
391
+ if ($mediaItem->isProcessable(true) === false)
392
+ {
393
+ $text = __('Not Processable: ','shortpixel_image_optimiser');
394
+ $text .= $mediaItem->getProcessableReason();
395
+ }
396
+ else {
397
+ $text = __('This image was not found in our database. Refresh folders, or add this gallery', 'shortpixel-image-optimiser');
398
+ }
399
  }
400
  elseif ($mediaItem->isOptimized())
401
  {
426
 
427
 
428
  $redo_legacy = false;
 
 
429
 
430
+ if ($mediaItem->get('type') == 'media')
431
  {
432
+ $was_converted = get_post_meta($mediaItem->get('id'), '_shortpixel_was_converted', true);
433
+ $updateTs = 1656892800; // July 4th 2022 - 00:00 GMT
434
+
435
+ if ($was_converted < $updateTs)
436
  {
437
+ $meta = $mediaItem->getWPMetaData();
438
+ if (is_array($meta) && isset($meta['ShortPixel']))
439
+ {
440
+ $redo_legacy = self::getAction('redo_legacy', $mediaItem->get('id'));
441
+ }
442
  }
443
  }
444
 
625
  $decimalpoint = isset($wp_locale->number_format['decimal_point']) ? $wp_locale->number_format['decimal_point'] : false;
626
  $number = number_format_i18n( (float) $number, $precision);
627
 
628
+ $hasDecimal = (strpos($number, $decimalpoint) === false) ? false : true;
629
+
630
  // Don't show trailing zeroes if number is a whole unbroken number. -> string comparison because number_format_i18n returns string.
631
+ if ($decimalpoint !== false && $hasDecimal && substr($number, strpos($number, $decimalpoint) + 1) === '00')
632
  {
633
+ $number = substr($number, 0, strpos($number, $decimalpoint));
634
  }
635
  // Some locale's have no-breaking-space as thousands separator. This doesn't work well in JS / Cron Shell so replace with space.
636
  $number = str_replace('&nbsp;', ' ', $number);
654
  }
655
  else
656
  {
657
+ return substr($name, 0, strrpos($name, '.')) . '.' . $type;
658
  }
659
 
660
  }
class/Model/File/DirectoryModel.php CHANGED
@@ -155,7 +155,7 @@ class DirectoryModel extends \ShortPixel\Model
155
  */
156
  public function getRelativePath()
157
  {
158
- // not used anywhere in directory.
159
  // $upload_dir = wp_upload_dir(null, false);
160
 
161
  $install_dir = get_home_path();
@@ -399,7 +399,6 @@ class DirectoryModel extends \ShortPixel\Model
399
  */
400
  public function getSubDirectories()
401
  {
402
- $fs = \wpSPIO()->fileSystem();
403
 
404
  if (! $this->exists() || ! $this->is_readable())
405
  {
155
  */
156
  public function getRelativePath()
157
  {
158
+ // not used anywhere in directory.
159
  // $upload_dir = wp_upload_dir(null, false);
160
 
161
  $install_dir = get_home_path();
399
  */
400
  public function getSubDirectories()
401
  {
 
402
 
403
  if (! $this->exists() || ! $this->is_readable())
404
  {
class/Model/File/FileModel.php CHANGED
@@ -541,7 +541,7 @@ class FileModel extends \ShortPixel\Model
541
 
542
  $this->is_virtual = true;
543
 
544
- // This filter checks if some supplier will be able to handle the file when needed.
545
  $path = apply_filters('shortpixel/image/urltopath', false, $url);
546
 
547
  if ($path !== false)
541
 
542
  $this->is_virtual = true;
543
 
544
+ // This filter checks if some supplier will be able to handle the file when needed.
545
  $path = apply_filters('shortpixel/image/urltopath', false, $url);
546
 
547
  if ($path !== false)
class/Model/Image/CustomImageModel.php CHANGED
@@ -3,6 +3,8 @@ namespace ShortPixel\Model\Image;
3
  use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
  use ShortPixel\Controller\OptimizeController as OptimizeController;
5
 
 
 
6
 
7
  // @todo Custom Model for adding files, instead of meta DAO.
8
  class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
@@ -55,33 +57,40 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
55
  }
56
 
57
 
58
- public function getOptimizePaths()
59
  {
60
- if (! $this->isProcessable())
61
- return array();
62
-
63
- $paths = array();
64
 
65
- if (! $this->image_meta->status == self::FILE_STATUS_SUCCESS)
66
- $paths = array($this->getFullPath());
67
 
68
- return $paths;
69
  }
70
 
71
- public function getOptimizeUrls()
72
- {
 
 
 
 
 
73
 
74
- $fs = \wpSPIO()->filesystem();
75
  if ($this->is_virtual())
76
  $url = $this->getFullPath();
77
  else
78
  $url = $this->getURL();
79
 
80
- if ($this->isProcessable(true))
81
- return array($url);
 
 
 
 
 
 
 
 
 
82
 
83
- return array();
84
- }
85
 
86
  public function getURL()
87
  {
@@ -116,6 +125,12 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
116
  {
117
  $bool = parent::isProcessable();
118
 
 
 
 
 
 
 
119
  if ($bool === false && $strict === false)
120
  {
121
  // Todo check if Webp / Acif is active, check for unoptimized items
@@ -223,27 +238,33 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
223
  $return = false;
224
  }
225
 
 
226
  do_action('shortpixel/image/after_restore', $this, $this->id, $bool);
227
 
228
  return $return;
229
  }
230
 
231
- // Placeholder function. I think this functionality was not available before
232
- public function isSizeExcluded()
233
- {
234
- return false;
235
- }
236
-
237
- public function handleOptimized($downloadResults)
238
  {
239
  $bool = true;
240
 
 
 
 
 
 
 
 
 
 
 
 
241
  if (! $this->isOptimized() ) // main file might not be contained in results
242
  {
243
- $bool = parent::handleOptimized($downloadResults);
244
  }
245
 
246
- $this->handleOptimizedFileType($downloadResults);
247
 
248
  if ($bool)
249
  {
@@ -251,6 +272,8 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
251
  $this->saveMeta();
252
  }
253
 
 
 
254
  return $bool;
255
  }
256
 
@@ -312,9 +335,32 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
312
  $optimizedDate = \ShortPixelTools::DBtoTimestamp($imagerow->ts_optimized);
313
  $metaObj->tsOptimized = $optimizedDate;
314
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  $this->image_meta = $metaObj;
316
  }
317
 
 
 
 
 
 
318
  /** Load a CustomImageModel as Stub ( to be added ) . Checks if the image is already added as well
319
  *
320
  * @param String $path
@@ -403,6 +449,24 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
403
  $added = new \DateTime();
404
  $added->setTimeStamp($metaObj->tsAdded);
405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  $data = array(
407
  'folder_id' => $this->folder_id,
408
  'compressed_size' => $metaObj->compressedSize,
@@ -421,6 +485,7 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
421
  'path' => $this->getFullPath(),
422
  'name' => $this->getFileName(),
423
  'path_md5' => md5($this->getFullPath()), // this is legacy
 
424
  );
425
  // The keys are just for readability.
426
  $format = array(
@@ -441,6 +506,7 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
441
  'path' => '%s',
442
  'name' => '%s',
443
  'path_md5' => '%s' , // this is legacy
 
444
  );
445
 
446
 
@@ -518,8 +584,13 @@ class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
518
  $improvements['main'] = array($perc, $size);
519
  $count++;
520
  } */
521
- $improvements['main'] = array($this->getImprovement(), 0);
522
- $improvements['totalpercentage'] = round($this->getImprovement()); // the same.
 
 
 
 
 
523
 
524
  return $improvements;
525
 
3
  use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
  use ShortPixel\Controller\OptimizeController as OptimizeController;
5
 
6
+ use ShortPixel\Controller\ApiController as API;
7
+
8
 
9
  // @todo Custom Model for adding files, instead of meta DAO.
10
  class CustomImageModel extends \ShortPixel\Model\Image\ImageModel
57
  }
58
 
59
 
60
+ public function getOptimizeUrls()
61
  {
 
 
 
 
62
 
63
+ $data = $this->getOptimizeData();
64
+ return array_values($data['urls']);
65
 
 
66
  }
67
 
68
+ public function getOptimizeData()
69
+ {
70
+ $parameters = array(
71
+ 'urls' => array(),
72
+ 'params' => array(),
73
+ 'returnParams' => array(),
74
+ );
75
 
76
+ $fs = \wpSPIO()->filesystem();
77
  if ($this->is_virtual())
78
  $url = $this->getFullPath();
79
  else
80
  $url = $this->getURL();
81
 
82
+ if ($this->isProcessable(true) || $this->isProcessableAnyFileType())
83
+ {
84
+ $parameters['urls'][0] = $url;
85
+ $parameters['paths'][0] = $this->getFullPath();
86
+ $parameters['params'][0] = $this->createParamList();
87
+ $parameters['returnParams']['sizes'][0] = $this->getFileName();
88
+ }
89
+
90
+
91
+ return $parameters;
92
+ }
93
 
 
 
94
 
95
  public function getURL()
96
  {
125
  {
126
  $bool = parent::isProcessable();
127
 
128
+ // The exclude size on the image - via regex - if fails, prevents the whole thing from optimization.
129
+ if ($this->processable_status == ImageModel::P_EXCLUDE_SIZE || $this->processable_status == ImageModel::P_EXCLUDE_PATH)
130
+ {
131
+ return $bool;
132
+ }
133
+
134
  if ($bool === false && $strict === false)
135
  {
136
  // Todo check if Webp / Acif is active, check for unoptimized items
238
  $return = false;
239
  }
240
 
241
+ $this->dropFromQueue();
242
  do_action('shortpixel/image/after_restore', $this, $this->id, $bool);
243
 
244
  return $return;
245
  }
246
 
247
+ public function handleOptimized($optimizeData)
 
 
 
 
 
 
248
  {
249
  $bool = true;
250
 
251
+ if (isset($optimizeData['files']) && isset($optimizeData['data']))
252
+ {
253
+ $files = $optimizeData['files'];
254
+ $data = $optimizeData['data'];
255
+ }
256
+ else {
257
+ Log::addError('Something went wrong with handleOptimized', $optimizeData);
258
+ }
259
+
260
+
261
+
262
  if (! $this->isOptimized() ) // main file might not be contained in results
263
  {
264
+ $bool = parent::handleOptimized($files[0]);
265
  }
266
 
267
+ $this->handleOptimizedFileType($files[0]);
268
 
269
  if ($bool)
270
  {
272
  $this->saveMeta();
273
  }
274
 
275
+ $this->deleteTempFiles($files);
276
+
277
  return $bool;
278
  }
279
 
335
  $optimizedDate = \ShortPixelTools::DBtoTimestamp($imagerow->ts_optimized);
336
  $metaObj->tsOptimized = $optimizedDate;
337
 
338
+ $extraInfo = property_exists($imagerow, 'extra_info') ? $imagerow->extra_info : null;
339
+
340
+ if (! is_null($extraInfo))
341
+ {
342
+ $data = json_decode($extraInfo, true);
343
+
344
+ if (isset($data['webpStatus']))
345
+ {
346
+ $this->setMeta('webp', $data['webpStatus']);
347
+ }
348
+ if (isset($data['avifStatus']))
349
+ {
350
+ $this->setMeta('avif', $data['avifStatus']);
351
+ }
352
+
353
+
354
+ }
355
+
356
  $this->image_meta = $metaObj;
357
  }
358
 
359
+ public function getParent()
360
+ {
361
+ return false; // no parents here
362
+ }
363
+
364
  /** Load a CustomImageModel as Stub ( to be added ) . Checks if the image is already added as well
365
  *
366
  * @param String $path
449
  $added = new \DateTime();
450
  $added->setTimeStamp($metaObj->tsAdded);
451
 
452
+ $extra_info = array();
453
+ if ($this->getMeta('webp') === self::FILETYPE_BIGGER)
454
+ {
455
+ $extra_info['webpStatus'] = self::FILETYPE_BIGGER;
456
+ }
457
+ if ($this->getMeta('avif') === self::FILETYPE_BIGGER)
458
+ {
459
+ $extra_info['avifStatus'] = self::FILETYPE_BIGGER;
460
+ }
461
+
462
+ if (count($extra_info) > 0)
463
+ {
464
+ $extra_info = json_encode($extra_info);
465
+ }
466
+ else {
467
+ $extra_info = null;
468
+ }
469
+
470
  $data = array(
471
  'folder_id' => $this->folder_id,
472
  'compressed_size' => $metaObj->compressedSize,
485
  'path' => $this->getFullPath(),
486
  'name' => $this->getFileName(),
487
  'path_md5' => md5($this->getFullPath()), // this is legacy
488
+ 'extra_info' => $extra_info,
489
  );
490
  // The keys are just for readability.
491
  $format = array(
506
  'path' => '%s',
507
  'name' => '%s',
508
  'path_md5' => '%s' , // this is legacy
509
+ 'extra_info' => '%s',
510
  );
511
 
512
 
584
  $improvements['main'] = array($perc, $size);
585
  $count++;
586
  } */
587
+ $improvement = $this->getImprovement();
588
+ if (is_null($improvement)) // getImprovement can return null.
589
+ {
590
+ $improvement = 0;
591
+ }
592
+ $improvements['main'] = array($improvement, 0);
593
+ $improvements['totalpercentage'] = round($improvement); // the same.
594
 
595
  return $improvements;
596
 
class/Model/Image/ImageModel.php CHANGED
@@ -51,6 +51,14 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
51
  const P_BACKUP_NOT_EXISTS = 110;
52
  const P_NOT_OPTIMIZED = 111;
53
 
 
 
 
 
 
 
 
 
54
  protected $image_meta; // metadata Object of the image.
55
  protected $recordChanged = false;
56
 
@@ -62,6 +70,7 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
62
  protected $error_message;
63
 
64
  protected $id;
 
65
 
66
  protected $processable_status = 0;
67
  protected $restorable_status = 0;
@@ -72,15 +81,14 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
72
  //protected $is_optimized = false;
73
  // protected $is_image = false;
74
 
75
- abstract public function getOptimizePaths();
76
  abstract public function getOptimizeUrls();
77
 
 
78
  abstract protected function saveMeta();
79
  abstract protected function loadMeta();
80
- abstract protected function isSizeExcluded();
81
 
82
  abstract protected function getImprovements();
83
- abstract protected function getOptimizeFileType();
84
 
85
  // Function to prevent image from doing anything automatically - after fatal error.
86
  abstract protected function preventNextTry($reason = '');
@@ -135,21 +143,38 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
135
  {
136
  $settings = \WPSPIO()->settings();
137
 
 
 
 
 
138
  if ($type == 'webp' && ! $settings->createWebp)
139
  return false;
140
 
141
  if ($type == 'avif' && ! $settings->createAvif)
142
  return false;
143
 
144
- // true, will only return paths ( = lighter )
145
- $files = $this->getOptimizeFileType($type, true);
146
 
147
- if (count($files) > 0)
 
148
  return true;
149
  else
150
  return false;
151
  }
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  public function exists()
155
  {
@@ -273,10 +298,6 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
273
  return null;
274
  }
275
 
276
- public function getLastErrorMessage()
277
- {
278
- return 'Deprecated - Get message via ResponseController'; // $this->error_message;
279
- }
280
 
281
  public function __get($name)
282
  {
@@ -299,10 +320,61 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
299
  return $this->image_meta->$name;
300
  }
301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
  protected function getImageType($type = 'webp')
304
  {
305
  $fs = \wpSPIO()->filesystem();
 
 
306
 
307
  if (! is_null($this->getMeta($type)))
308
  {
@@ -442,163 +514,154 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
442
  *
443
  * @param Array TemporaryFiles . Files from API optimizer with KEY of filename and FileModel Temporary File
444
  */
445
- public function handleOptimized($downloadResults)
446
  {
447
  $settings = \wpSPIO()->settings();
448
  $fs = \wpSPIO()->filesystem();
449
 
450
- foreach($downloadResults as $urlName => $resultObj)
451
- {
452
-
453
- if ($urlName != $this->getFileName())
454
- {
455
- continue;
456
- }
457
 
458
- if ($settings->backupImages)
 
 
 
 
 
 
 
 
 
 
 
 
459
  {
460
- // If conversion to jpg is done, this function also does the backup.
461
- if ($this->getMeta('did_png2jpg') === true)
462
- {
463
- $backupok = true;
464
- }
465
- else
466
- {
467
- $backupok = $this->createBackup();
468
- }
469
-
470
- if (! $backupok)
471
- {
472
- Log::addError('Backup Not OK - ' . $urlName);
473
-
474
- $response = array(
475
- 'is_error' => true,
476
- 'issue_type' => ResponseController::ISSUE_BACKUP_CREATE,
477
- 'message' => __('Could not create backup. Please check file permissions', 'shortpixel-image-optimiser'),
478
- 'fileName' => $this->getFileName(),
479
- );
480
-
481
- ResponseController::addData($this->get('id'), $response);
482
-
483
- $this->preventNextTry(__('Could not create backup'));
484
- return false;
485
- }
486
- }
487
 
488
- $originalSize = $this->getFileSize();
 
 
 
 
 
489
 
490
- if ($resultObj->apiStatus == API::STATUS_UNCHANGED)
491
- {
492
- $copyok = true;
493
- $optimizedSize = $this->getFileSize();
494
- $tempFile = null;
495
  }
496
- else
497
- {
498
- $tempFile = $resultObj->file;
499
 
 
500
 
501
- // assume that if this happens, the conversion to jpg was done.
502
- if ($this->getExtension() == 'heic')
503
- {
504
- $heicPath = $this->getFullPath();
 
 
 
 
 
505
 
506
- $this->fullpath = (string) $this->getFileDir() . $this->getFileBase() . '.jpg';
507
- $this->resetStatus();
508
- $this->setFileInfo();
509
- $wasHeic = true;
510
 
511
- }
512
- if ($this->is_virtual())
513
- {
514
- $filepath = apply_filters('shortpixel/file/virtual/translate', $this->getFullPath(), $this);
515
 
516
- $virtualFile = $fs->getFile($filepath);
517
- $copyok = $tempFile->copy($virtualFile);
518
- }
519
- else
520
- $copyok = $tempFile->copy($this);
521
 
522
- $optimizedSize = $tempFile->getFileSize();
523
- $this->setImageSize();
524
- } // else
 
525
 
526
- if ($copyok)
527
- {
528
- $this->setMeta('status', self::FILE_STATUS_SUCCESS);
529
- $this->setMeta('tsOptimized', time());
530
- $this->setMeta('compressedSize', $optimizedSize);
531
- $this->setMeta('originalSize', $originalSize);
532
- // $this->setMeta('improvement', $originalSize - $optimizedSize);
533
- if ($this->hasMeta('did_keepExif'))
534
- $this->setMeta('did_keepExif', $settings->keepExif);
535
- if ($this->hasMeta('did_cmyk2rgb'))
536
- $this->setMeta('did_cmyk2rgb', $settings->CMYKtoRGBconversion);
537
-
538
- // Not set before in this case.
539
- if (is_null($this->getMeta('compressionType')) || $this->getMeta('compressionType') === false)
540
- {
541
- $this->setMeta('compressionType', $settings->compressionType);
542
- }
543
-
544
- if ($settings->resizeImages)
545
- {
546
-
547
- $resizeWidth = $settings->resizeWidth;
548
- $resizeHeight = $settings->resizeHeight;
549
-
550
- $originalWidth = $this->getMeta('originalWidth');
551
- $originalHeight = $this->getMeta('originalHeight');
552
-
553
- $width = $this->get('width'); // image width
554
- $height = $this->get('height');
555
-
556
- if ( ($resizeWidth == $width && $width != $originalWidth) || ($resizeHeight == $height && $height != $originalHeight ) ) // resized.
557
- {
558
- $this->setMeta('resizeWidth', $this->get('width') );
559
- $this->setMeta('resizeHeight', $this->get('height') );
560
- $this->setMeta('resize', true);
561
- $resizeType = ($settings->resizeType == 1) ? __('Cover', 'shortpixel-image-optimiser') : __('Contain', 'shortpixel-image-optimiser');
562
- $this->setMeta('resizeType', $resizeType);
563
- }
564
- else
565
- $this->setMeta('resize', false);
566
- }
567
-
568
-
569
- if ($tempFile)
570
- $tempFile->delete();
571
-
572
- if (isset($wasHeic) && $wasHeic == true)
573
- {
574
- $heicFile = $fs->getFile($heicPath);
575
- if ($heicFile->exists())
576
- {
577
- $heicFile->delete(); // the original heic -file should not linger in uploads.
578
- }
579
- }
580
 
 
 
 
581
 
582
- }
583
- else
584
- {
585
- Log::addError('Copy failed for ' . $this->getFullPath() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586
 
587
- $response = array(
588
- 'is_error' => true,
589
- 'issue_type' => ResponseController::ISSUE_BACKUP_CREATE,
590
- 'message' => __('Could not copy optimized image from temporary files. Check file permissions', 'shortpixel-image-optimiser'),
591
- 'fileName' => $this->getFileName(),
592
- );
593
 
594
- ResponseController::addData($this->get('id'), $response);;
 
 
 
595
 
596
- return false;
597
- }
598
- return true;
599
- break;
 
 
600
 
601
- }
 
 
 
 
602
 
603
  Log::addWarn('Could not find images of this item in tempfile -' . $this->id . '(' . $this->getFullPath() . ')', array_keys($downloadResults) );
604
 
@@ -615,38 +678,48 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
615
  return null;
616
  }
617
 
618
- public function handleOptimizedFileType($downloadResults)
619
  {
620
 
621
- $filebase = $this->getFileBase();
622
-
623
- $webpFile = $filebase . '.webp';
624
-
625
- if (isset($downloadResults[$webpFile]) && isset($downloadResults[$webpFile]->file)) // check if there is webp with same filename
626
  {
627
- $webpResult = $this->handleWebp($downloadResults[$webpFile]->file);
628
  if ($webpResult === false)
629
- Log::addWarn('Webps available, but copy failed ' . $downloadResults[$webpFile]->file->getFullPath());
630
  else
631
  $this->setMeta('webp', $webpResult->getFileName());
632
  }
 
 
 
 
 
 
 
633
 
634
- $avifFile = $filebase . '.avif';
635
-
636
- if (isset($downloadResults[$avifFile]) && isset($downloadResults[$avifFile]->file)) // check if there is webp with same filename
637
  {
638
- $avifResult = $this->handleAvif($downloadResults[$avifFile]->file);
639
  if ($avifResult === false)
640
- Log::addWarn('Avif available, but copy failed ' . $downloadResults[$avifFile]->file->getFullPath());
641
  else
642
  $this->setMeta('avif', $avifResult->getFileName());
643
  }
 
 
 
 
 
 
 
 
644
  }
645
 
646
  public function isRestorable()
647
  {
648
 
649
- if (! $this->isOptimized())
 
650
  {
651
  $this->restorable_status = self::P_NOT_OPTIMIZED;
652
  return false; // not optimized, done.
@@ -816,8 +889,6 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
816
  $target = $fs->getFile((string) $this->getFileDir() . $this->getFileName() . '.webp'); // double extension, if exists.
817
  }
818
 
819
-
820
-
821
  $result = false;
822
 
823
  if (! $target->exists()) // don't copy if exists.
@@ -937,6 +1008,59 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
937
  return false;
938
  }
939
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940
  /** Convert Image Meta to A Class */
941
  protected function toClass()
942
  {
@@ -1036,4 +1160,91 @@ abstract class ImageModel extends \ShortPixel\Model\File\FileModel
1036
  return \wpSPIO()->filesystem();
1037
  }
1038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1039
  } // model
51
  const P_BACKUP_NOT_EXISTS = 110;
52
  const P_NOT_OPTIMIZED = 111;
53
 
54
+ const IMAGE_TYPE_MAIN = 0;
55
+ const IMAGE_TYPE_THUMB = 1;
56
+ const IMAGE_TYPE_ORIGINAL = 2;
57
+ const IMAGE_TYPE_RETINA = 3;
58
+ const IMAGE_TYPE_DUPLICATE = 4;
59
+
60
+ const FILETYPE_BIGGER = -10;
61
+
62
  protected $image_meta; // metadata Object of the image.
63
  protected $recordChanged = false;
64
 
70
  protected $error_message;
71
 
72
  protected $id;
73
+ protected $imageType;
74
 
75
  protected $processable_status = 0;
76
  protected $restorable_status = 0;
81
  //protected $is_optimized = false;
82
  // protected $is_image = false;
83
 
 
84
  abstract public function getOptimizeUrls();
85
 
86
+
87
  abstract protected function saveMeta();
88
  abstract protected function loadMeta();
 
89
 
90
  abstract protected function getImprovements();
91
+ // abstract protected function getOptimizeFileType();
92
 
93
  // Function to prevent image from doing anything automatically - after fatal error.
94
  abstract protected function preventNextTry($reason = '');
143
  {
144
  $settings = \WPSPIO()->settings();
145
 
146
+ // Pdf, no special files.
147
+ if ($this->getExtension() == 'pdf')
148
+ return false;
149
+
150
  if ($type == 'webp' && ! $settings->createWebp)
151
  return false;
152
 
153
  if ($type == 'avif' && ! $settings->createAvif)
154
  return false;
155
 
156
+ $imgObj = $this->getImageType($type);
 
157
 
158
+ // if this image doesn't have webp / avif, it can be processed.
159
+ if ($imgObj === false && $this->getMeta($type) !== self::FILETYPE_BIGGER)
160
  return true;
161
  else
162
  return false;
163
  }
164
 
165
+ public function isProcessableAnyFileType()
166
+ {
167
+ $webp = $this->isProcessableFileType('webp');
168
+ $avif = $this->isProcessableFileType('avif');
169
+
170
+ if ($webp === false && $avif === false)
171
+ return false;
172
+ else {
173
+ return true;
174
+ }
175
+ }
176
+
177
+
178
 
179
  public function exists()
180
  {
298
  return null;
299
  }
300
 
 
 
 
 
301
 
302
  public function __get($name)
303
  {
320
  return $this->image_meta->$name;
321
  }
322
 
323
+ /* Get counts of what needs to be optimized still
324
+ * @param String What to count: thumbnails, webp, avif.
325
+ */
326
+ public function getCountOptimizeData($param = 'thumbnails')
327
+ {
328
+ $optimizeData = $this->getOptimizeData();
329
+
330
+ if (! isset($optimizeData['params']) || ! isset($optimizeData['urls']))
331
+ {
332
+ array(array(), 0);
333
+ }
334
+
335
+ $count = 0;
336
+ $urls = array();
337
+ $i = 0;
338
+ foreach($optimizeData['params'] as $sizeName => $data)
339
+ {
340
+
341
+ switch($param)
342
+ {
343
+ case 'thumbnails';
344
+ if (isset($data['image']) && $data['image'] === true)
345
+ {
346
+ $count++;
347
+ $urls[] = $optimizeData['paths'][$sizeName];
348
+ }
349
+ break;
350
+ case 'webp';
351
+ if (isset($data['webp']) && $data['webp'] === true)
352
+ {
353
+ $count++;
354
+ $urls[] = $optimizeData['paths'][$sizeName];
355
+ }
356
+ break;
357
+ case 'avif';
358
+ if (isset($data['avif']) && $data['avif'] === true)
359
+ {
360
+ $count++;
361
+ $urls[] = $optimizeData['paths'][$sizeName];
362
+ }
363
+ break;
364
+
365
+ $i++;
366
+ }
367
+ }
368
+
369
+
370
+ return array($urls, $count);
371
+ }
372
 
373
  protected function getImageType($type = 'webp')
374
  {
375
  $fs = \wpSPIO()->filesystem();
376
+ if ($this->getMeta($type) === self::FILETYPE_BIGGER)
377
+ return false;
378
 
379
  if (! is_null($this->getMeta($type)))
380
  {
514
  *
515
  * @param Array TemporaryFiles . Files from API optimizer with KEY of filename and FileModel Temporary File
516
  */
517
+ public function handleOptimized($results)
518
  {
519
  $settings = \wpSPIO()->settings();
520
  $fs = \wpSPIO()->filesystem();
521
 
522
+ $resultObj = $results['img'];
 
 
 
 
 
 
523
 
524
+ if ($settings->backupImages)
525
+ {
526
+ // If conversion to jpg is done, this function also does the backup.
527
+ if ($this->getMeta('did_png2jpg') === true)
528
+ {
529
+ $backupok = true;
530
+ }
531
+ else
532
+ {
533
+ $backupok = $this->createBackup();
534
+ }
535
+
536
+ if (! $backupok)
537
  {
538
+ Log::addError('Backup Not OK - ' . $this->getFileName());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
+ $response = array(
541
+ 'is_error' => true,
542
+ 'issue_type' => ResponseController::ISSUE_BACKUP_CREATE,
543
+ 'message' => __('Could not create backup. Please check file permissions', 'shortpixel-image-optimiser'),
544
+ 'fileName' => $this->getFileName(),
545
+ );
546
 
547
+ ResponseController::addData($this->get('id'), $response);
548
+
549
+ $this->preventNextTry(__('Could not create backup'));
550
+ return false;
 
551
  }
552
+ }
 
 
553
 
554
+ $originalSize = $this->getFileSize();
555
 
556
+ if ($resultObj->apiStatus == API::STATUS_UNCHANGED || $resultObj->apiStatus == API::STATUS_OPTIMIZED_BIGGER)
557
+ {
558
+ $copyok = true;
559
+ $optimizedSize = $this->getFileSize();
560
+ $tempFile = null;
561
+ }
562
+ else
563
+ {
564
+ $tempFile = $resultObj->file;
565
 
 
 
 
 
566
 
567
+ // assume that if this happens, the conversion to jpg was done.
568
+ if ($this->getExtension() == 'heic')
569
+ {
570
+ $heicPath = $this->getFullPath();
571
 
572
+ $this->fullpath = (string) $this->getFileDir() . $this->getFileBase() . '.jpg';
573
+ $this->resetStatus();
574
+ $this->setFileInfo();
575
+ $wasHeic = true;
 
576
 
577
+ }
578
+ if ($this->is_virtual())
579
+ {
580
+ $filepath = apply_filters('shortpixel/file/virtual/translate', $this->getFullPath(), $this);
581
 
582
+ $virtualFile = $fs->getFile($filepath);
583
+ $copyok = $tempFile->copy($virtualFile);
584
+ }
585
+ else
586
+ $copyok = $tempFile->copy($this);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
 
588
+ $optimizedSize = $tempFile->getFileSize();
589
+ $this->setImageSize();
590
+ } // else
591
 
592
+ if ($copyok)
593
+ {
594
+ $this->setMeta('status', self::FILE_STATUS_SUCCESS);
595
+ $this->setMeta('tsOptimized', time());
596
+ $this->setMeta('compressedSize', $optimizedSize);
597
+ $this->setMeta('originalSize', $originalSize);
598
+ // $this->setMeta('improvement', $originalSize - $optimizedSize);
599
+ if ($this->hasMeta('did_keepExif'))
600
+ $this->setMeta('did_keepExif', $settings->keepExif);
601
+ if ($this->hasMeta('did_cmyk2rgb'))
602
+ $this->setMeta('did_cmyk2rgb', $settings->CMYKtoRGBconversion);
603
+
604
+ // Not set before in this case.
605
+ if (is_null($this->getMeta('compressionType')) || $this->getMeta('compressionType') === false)
606
+ {
607
+ $this->setMeta('compressionType', $settings->compressionType);
608
+ }
609
+
610
+ if ($settings->resizeImages)
611
+ {
612
+
613
+ $resizeWidth = $settings->resizeWidth;
614
+ $resizeHeight = $settings->resizeHeight;
615
+
616
+ $originalWidth = $this->getMeta('originalWidth');
617
+ $originalHeight = $this->getMeta('originalHeight');
618
+
619
+ $width = $this->get('width'); // image width
620
+ $height = $this->get('height');
621
+
622
+ if ( ($resizeWidth == $width && $width != $originalWidth) || ($resizeHeight == $height && $height != $originalHeight ) ) // resized.
623
+ {
624
+ $this->setMeta('resizeWidth', $this->get('width') );
625
+ $this->setMeta('resizeHeight', $this->get('height') );
626
+ $this->setMeta('resize', true);
627
+ $resizeType = ($settings->resizeType == 1) ? __('Cover', 'shortpixel-image-optimiser') : __('Contain', 'shortpixel-image-optimiser');
628
+ $this->setMeta('resizeType', $resizeType);
629
+ }
630
+ else
631
+ $this->setMeta('resize', false);
632
+ }
633
+
634
+
635
+ /*if ($tempFile)
636
+ $tempFile->delete();
637
+ */
638
+ if (isset($wasHeic) && $wasHeic == true)
639
+ {
640
+ $heicFile = $fs->getFile($heicPath);
641
+ if ($heicFile->exists())
642
+ {
643
+ $heicFile->delete(); // the original heic -file should not linger in uploads.
644
+ }
645
+ }
646
 
 
 
 
 
 
 
647
 
648
+ }
649
+ else
650
+ {
651
+ Log::addError('Copy failed for ' . $this->getFullPath() );
652
 
653
+ $response = array(
654
+ 'is_error' => true,
655
+ 'issue_type' => ResponseController::ISSUE_BACKUP_CREATE,
656
+ 'message' => __('Could not copy optimized image from temporary files. Check file permissions', 'shortpixel-image-optimiser'),
657
+ 'fileName' => $this->getFileName(),
658
+ );
659
 
660
+ ResponseController::addData($this->get('id'), $response);;
661
+
662
+ return false;
663
+ }
664
+ return true;
665
 
666
  Log::addWarn('Could not find images of this item in tempfile -' . $this->id . '(' . $this->getFullPath() . ')', array_keys($downloadResults) );
667
 
678
  return null;
679
  }
680
 
681
+ public function handleOptimizedFileType($downloadResult)
682
  {
683
 
684
+ if (isset($downloadResult['webp']) && property_exists($downloadResult['webp'],'file')) // check if there is webp with same filename
 
 
 
 
685
  {
686
+ $webpResult = $this->handleWebp($downloadResult['webp']->file);
687
  if ($webpResult === false)
688
+ Log::addWarn('Webps available, but copy failed ' . $downloadResults['webp']->file->getFullPath());
689
  else
690
  $this->setMeta('webp', $webpResult->getFileName());
691
  }
692
+ elseif(isset($downloadResult['webp']) && property_exists($downloadResult['webp'], 'apiStatus'))
693
+ {
694
+ if ($downloadResult['webp']->apiStatus == API::STATUS_OPTIMIZED_BIGGER)
695
+ {
696
+ $this->setMeta('webp', self::FILETYPE_BIGGER);
697
+ }
698
+ }
699
 
700
+ if (isset($downloadResult['avif']) && property_exists($downloadResult['avif'], 'file')) // check if there is webp with same filename
 
 
701
  {
702
+ $avifResult = $this->handleAvif($downloadResult['avif']->file);
703
  if ($avifResult === false)
704
+ Log::addWarn('Avif available, but copy failed ' . $downloadResult['avif']->file->getFullPath());
705
  else
706
  $this->setMeta('avif', $avifResult->getFileName());
707
  }
708
+ elseif(isset($downloadResult['avif']) && property_exists($downloadResult['avif'], 'apiStatus'))
709
+ {
710
+
711
+ if ($downloadResult['avif']->apiStatus == API::STATUS_OPTIMIZED_BIGGER)
712
+ {
713
+ $this->setMeta('avif', self::FILETYPE_BIGGER);
714
+ }
715
+ }
716
  }
717
 
718
  public function isRestorable()
719
  {
720
 
721
+ // Check for both optimized and hasBackup, because even if status for some reason is not optimized, but backup is there, restore anyhow.
722
+ if (! $this->isOptimized() && ! $this->hasBackup())
723
  {
724
  $this->restorable_status = self::P_NOT_OPTIMIZED;
725
  return false; // not optimized, done.
889
  $target = $fs->getFile((string) $this->getFileDir() . $this->getFileName() . '.webp'); // double extension, if exists.
890
  }
891
 
 
 
892
  $result = false;
893
 
894
  if (! $target->exists()) // don't copy if exists.
1008
  return false;
1009
  }
1010
 
1011
+ protected function isSizeExcluded()
1012
+ {
1013
+ $excludePatterns = \wpSPIO()->settings()->excludePatterns;
1014
+
1015
+ if (! $excludePatterns || ! is_array($excludePatterns) ) // no patterns, nothing excluded
1016
+ return false;
1017
+
1018
+ $bool = false;
1019
+
1020
+ foreach($excludePatterns as $item) {
1021
+ $type = trim($item["type"]);
1022
+ if($type == "size") {
1023
+ //$meta = $meta? $meta : wp_get_attachment_metadata($ID);
1024
+ $width = $this->get('width');
1025
+ $height = $this->get('height');
1026
+
1027
+ if( $width && $height
1028
+ && $this->isProcessableSize($width, $height, $item["value"]) === false){
1029
+ $this->processable_status = self::P_EXCLUDE_SIZE;
1030
+ return true; // exit directly because we have our exclusion
1031
+ }
1032
+ else
1033
+ $bool = false; // continue and check all patterns, there might be multiple.
1034
+ }
1035
+ }
1036
+
1037
+ return $bool;
1038
+ }
1039
+
1040
+ private function isProcessableSize($width, $height, $excludePattern)
1041
+ {
1042
+
1043
+ $ranges = preg_split("/(x|×|X)/",$excludePattern);
1044
+ $widthBounds = explode("-", $ranges[0]);
1045
+ $minWidth = intval($widthBounds[0]);
1046
+ $maxWidth = (!isset($widthBounds[1])) ? intval($widthBounds[0]) : intval($widthBounds[1]);
1047
+
1048
+ $heightBounds = isset($ranges[1]) ? explode("-", $ranges[1]) : false;
1049
+ $minHeight = $maxHeight = 0;
1050
+ if ($heightBounds)
1051
+ {
1052
+ $minHeight = intval($heightBounds[0]);
1053
+ $maxHeight = (!isset($heightBounds[1])) ? intval($heightBounds[0]) : intval($heightBounds[1]);
1054
+ }
1055
+
1056
+ if( $width >= $minWidth && $width <= $maxWidth
1057
+ && ( $heightBounds === false
1058
+ || ($height >= $minHeight && $height <= $maxHeight) )) {
1059
+ return false;
1060
+ }
1061
+ return true;
1062
+ }
1063
+
1064
  /** Convert Image Meta to A Class */
1065
  protected function toClass()
1066
  {
1160
  return \wpSPIO()->filesystem();
1161
  }
1162
 
1163
+ protected function createParamList()
1164
+ {
1165
+ $settings = \wpSPIO()->settings();
1166
+
1167
+ $resize = false;
1168
+ $hasResizeSizes = (intval($settings->resizeImages) > 0) ? true : false;
1169
+ $result = array();
1170
+
1171
+ $useSmartcrop = false;
1172
+
1173
+ if ($settings->useSmartcrop == true && $this->getExtension() !== 'pdf')
1174
+ {
1175
+ $resize = 4 ;
1176
+ $useSmartcrop = true;
1177
+ }
1178
+ elseif ( $hasResizeSizes)
1179
+ {
1180
+ $resize = $settings->resizeImages ? 1 + 2 * ($settings->resizeType == 'inner' ? 1 : 0) : 0;
1181
+ }
1182
+
1183
+ if ($resize > 0)
1184
+ {
1185
+ $resize_width = $resize_height = 0; // can be not set.
1186
+ $width = $this->get('width');
1187
+ $height = $this->get('height');
1188
+
1189
+ if ($hasResizeSizes)
1190
+ {
1191
+
1192
+
1193
+ $resize_width = intval($settings->resizeWidth);
1194
+ $resize_height = intval($settings->resizeHeight);
1195
+ // If retina, allowed resize sizes is doubled, otherwise big image / big retina would end up same sizes.
1196
+ if ($this->get('imageType') == self::IMAGE_TYPE_RETINA)
1197
+ {
1198
+ $resize_width = $resize_width * 2;
1199
+ $resize_height = $resize_height * 2;
1200
+ }
1201
+ }
1202
+
1203
+ $width = ( $this->get('width') <= $resize_width || $resize_width === 0) ? $width : $resize_width;
1204
+ $height = ($this->get('height') <= $resize_height || $resize_height === 0) ? $height : $resize_height;
1205
+
1206
+ $result = array('resize' => $resize, 'resize_width' => $width, 'resize_height' => $height);
1207
+ }
1208
+
1209
+ // Check if the image is not excluded
1210
+ $imageOk = ($this->isProcessable(true) || $this->isOptimized()) ? true : false ;
1211
+
1212
+ $result['image'] = $this->isProcessable(true);
1213
+ $result['webp'] = ($imageOk && $this->isProcessableFileType('webp')) ? true : false;
1214
+ $result['avif'] = ($imageOk && $this->isProcessableFileType('avif')) ? true : false;
1215
+
1216
+ return $result;
1217
+
1218
+ }
1219
+
1220
+ protected function deleteTempFiles($files)
1221
+ {
1222
+ foreach($files as $name => $data)
1223
+ {
1224
+ if (isset($data['img']) && property_exists($data['img'], 'file'))
1225
+ {
1226
+ if ($data['img']->file->exists())
1227
+ {
1228
+ $data['img']->file->delete();
1229
+ }
1230
+ }
1231
+ if (isset($data['webp']) && property_exists($data['webp'], 'file'))
1232
+ {
1233
+ if ($data['webp']->file->exists())
1234
+ {
1235
+ $data['webp']->file->delete();
1236
+ }
1237
+ }
1238
+ if (isset($data['avif']) && property_exists($data['avif'], 'file'))
1239
+ {
1240
+ if ($data['avif']->file->exists())
1241
+ {
1242
+ $data['avif']->file->delete();
1243
+ }
1244
+ }
1245
+
1246
+ }
1247
+
1248
+ }
1249
+
1250
  } // model
class/Model/Image/MediaLibraryModel.php CHANGED
@@ -21,6 +21,7 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
21
  protected $do_png2jpg = false; // option to flag this one should be checked / converted to jpg.
22
 
23
  protected $wp_metadata;
 
24
 
25
  protected $type = 'media';
26
  protected $is_main_file = true; // for checking
@@ -30,15 +31,13 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
30
  protected $optimizePrevented; // cache if there is any reason to prevent optimizing
31
  private $justConverted = false; // check if conversion happened on same run, to prevent double runs.
32
 
33
- const IMAGE_TYPE_MAIN = 0;
34
- const IMAGE_TYPE_THUMB = 1;
35
- const IMAGE_TYPE_ORIGINAL = 2;
36
- const IMAGE_TYPE_RETINA = 3;
37
- const IMAGE_TYPE_DUPLICATE = 4;
38
 
39
  public function __construct($post_id, $path)
40
  {
41
  $this->id = $post_id;
 
42
 
43
  parent::__construct($path, $post_id, null);
44
 
@@ -55,166 +54,169 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
55
  }
56
 
57
 
58
- public function getOptimizePaths()
59
- {
60
- $paths = array();
61
-
62
- if ($this->isProcessable(true))
63
- $paths = array($this->getFullPath());
64
-
65
- foreach($this->thumbnails as $thumbObj)
66
- {
67
- if ($thumbObj->isProcessable() )
68
- $paths = array_merge($paths, $thumbObj->getOptimizePaths());
69
- }
70
-
71
- $paths = array_values(array_unique($paths));
72
- return $paths;
73
- }
74
-
75
 
76
  // Path will only return the filepath. For reasons, see getOptimizeFileType
77
- public function getOptimizeUrls($get_path = false)
78
  {
79
- $settings = \wpSPIO()->settings();
 
 
 
80
 
81
- if (true === $get_path)
82
- {
 
 
 
 
83
 
84
- $url = $this->getFullPath();
85
- }
86
- else{
87
- $url = $this->getURL();
88
- }
89
 
90
  if (! $url) // If the whole image URL can't be found
91
  {
92
- return array();
93
  }
94
 
95
- $urls = array();
96
- if ($this->isProcessable(true))
97
- {
98
- $urls = array($url);
99
- }
100
 
101
- if ($this->isScaled())
102
- {
103
- $urls = array_merge($urls, $this->original_file->getOptimizeUrls($get_path));
104
- }
 
105
 
106
- foreach($this->thumbnails as $thumbObj)
107
- {
108
- if($thumbObj->isThumbnailProcessable())
109
- $urls = array_merge($urls, $thumbObj->getOptimizeUrls($get_path));
110
- }
 
 
111
 
112
- // @todo Check Retina's
 
 
 
113
 
114
- if ($settings->optimizeRetina && ! is_null($this->retinas))
115
- {
116
- foreach($this->retinas as $retinaObj)
117
- {
118
- if ($retinaObj->isThumbnailProcessable())
119
- $urls = array_merge($urls, $retinaObj->getOptimizeUrls($get_path));
120
- }
121
 
122
- }
 
123
 
 
 
 
 
124
 
125
- $urls = array_values(array_unique($urls));
126
- return $urls;
127
- }
 
128
 
129
- // Try to get the URL via WordPress
130
- // This is now officially a heavy function. Take times, other plugins (like s3) might really delay it
131
- public function getURL()
132
- {
133
- $url = $this->fs()->checkURL(wp_get_attachment_url($this->id));
134
- return $url;
135
- }
136
 
137
- /** Get FileTypes that might be optimized. Checking for setting should go via isProcessableFileType!
138
- * Get path will return the filepath of said files. This is not useful, except for -checking- if it's processable via isProcessableFileType . GetURL can potentially hit performance, and we don't need it in that case.
139
- */
140
- public function getOptimizeFileType($type = 'webp', $get_path = false)
141
- {
142
- if ($type == 'webp')
143
- {
144
- $types = $this->getWebps();
145
- }
146
- elseif ($type == 'avif')
147
- {
148
- $types = $this->getAvifs();
149
- }
150
-
151
- $toOptimize = array();
152
 
153
- // main file.
154
- if (! isset($types[0]))
155
- {
156
- // The isProcessable(true) is very important, since non-strict calls of this function check this function as well ( resulting in loop )
157
- if ($this->isProcessable(true) || $this->isOptimized())
158
- {
159
- if (parent::getOptimizeFileType($type))
160
- {
161
- if (true === $get_path) {
162
- $toOptimize[] = $this->getFullPath();
163
- } else {
164
- $toOptimize[] = $this->getURL(); // $fs->pathToUrl($this);
165
- }
166
- }
167
- }
168
- }
169
- if ($this->isScaled() ) // scaled image
170
- {
171
- if ($this->original_file->getOptimizeFileType($type) )
172
- {
173
- if (true === $get_path)
174
  {
175
- $toOptimize[] = $this->original_file->getFullPath();
176
  }
177
  else {
178
- $toOptimize[] = $this->original_file->getURL();
179
  }
180
- }
181
- }
182
 
183
- foreach($this->thumbnails as $thumbName => $thumbObj)
184
- {
185
- if ($thumbObj->getOptimizeFileType($type))
186
- {
187
- if (true === $get_path)
188
  {
189
- $toOptimize[] = $thumbObj->getFullPath();
190
- }
191
- else{
192
- $toOptimize[] = $thumbObj->getURL(); //$fs->pathToUrl($thumbObj);
193
  }
194
- }
195
- }
196
-
197
- if (! is_null($this->retinas))
198
- {
199
- foreach($this->retinas as $retinaName => $retinaObj)
200
- {
201
- if ($retinaObj->getOptimizeFileType($type))
202
  {
203
- if (true === $get_path)
 
 
204
  {
205
- $toOptimize[] = $retinaObj->getFullPath();
 
 
 
 
206
  }
207
- else{
208
- $toOptimize[] = $retinaObj->getURL(); //$fs->pathToUrl($thumbObj);
 
 
209
  }
210
  }
211
- }
212
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
- return array_values(array_unique($toOptimize));
215
- //foreach($types as $index => $)
 
 
 
 
 
 
 
 
 
216
  }
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  public function getWPMetaData()
219
  {
220
  if (is_null($this->wp_metadata))
@@ -232,29 +234,6 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
232
  return $this->is_scaled;
233
  }
234
 
235
- /** Check and find if there is an error message around
236
- *
237
- * This is usually requested after some error status has been detected.
238
- * Note that the error might not be in the main image, but can also be in a thumbnail!
239
- *
240
- */
241
- public function getLastErrorMessage()
242
- {
243
- $message = $this->error_message;
244
- if (is_null($message) || strlen($message) == 0)
245
- {
246
-
247
- foreach ($this->thumbnails as $thumbnail)
248
- {
249
- $message = $thumbnail->getLastErrorMessage();
250
- if (! is_null($message) && strlen($message) > 0)
251
- return $message;
252
- }
253
- }
254
-
255
- return $message;
256
- }
257
-
258
 
259
  /** Loads an array of Thumbnailmodels based on sizes available in WordPress metadata
260
  ** @return Array consisting ofMediaLibraryThumbnailModel
@@ -359,7 +338,7 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
359
  {
360
  $retinaObj = $thumbObj->getRetina();
361
  if ($retinaObj)
362
- $this->retinas[$thumbname] = $retinaObj;
363
  }
364
  }
365
 
@@ -457,19 +436,35 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
457
 
458
  }
459
 
460
- public function handleOptimized($tempFiles)
461
  {
462
  $return = true;
463
  $wpmeta = wp_get_attachment_metadata($this->get('id'));
 
 
 
 
 
 
 
 
 
 
 
464
 
465
- if (! $this->isOptimized() && isset($tempFiles[$this->getFileName()]) ) // main file might not be contained in results
 
 
 
 
 
466
  {
467
  if ($this->getExtension() == 'heic')
468
  {
469
  $isHeic = true;
470
  }
471
 
472
- $result = parent::handleOptimized($tempFiles);
473
  if (! $result)
474
  {
475
  return false;
@@ -489,24 +484,43 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
489
 
490
  }
491
 
492
- $this->handleOptimizedFileType($tempFiles);
493
 
494
- $optimized = array();
495
 
496
  $compressionType = $this->getMeta('compressionType'); // CompressionType not set on subimages etc.
497
 
498
  // If thumbnails should not be optimized, they should not be in result Array.
499
  // #### THUMBNAILS ####
500
- foreach($this->thumbnails as $thumbnail)
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  {
502
  // Check if thumbnail is in the tempfiles return set. This might not always be the case
503
- if (! isset($tempFiles[$thumbnail->getFileName()]) )
504
  {
505
  continue;
506
  }
507
 
508
- $thumbnail->setMeta('compressionType', $compressionType);
509
- $thumbnail->handleOptimizedFileType($tempFiles); // check for webps /etc
 
 
 
 
 
 
 
510
 
511
  if ($thumbnail->isOptimized())
512
  {
@@ -516,23 +530,14 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
516
  {
517
  continue; // when excluded.
518
  }
519
- $filebase = $thumbnail->getFileBase();
520
  $result = false;
521
 
522
- if (isset($optimized[$filebase])) // double sizes.
523
- {
524
- $databaseID = $thumbnail->getMeta('databaseID');
525
- $thumbnail->setMetaObj($optimized[$filebase]);
526
- $thumbnail->setMeta('databaseID', $databaseID); // keep dbase id the same, otherwise it won't write this thumb to DB due to same ID.
527
- $result = false;
528
- }
529
- else
530
- {
531
- $result = $thumbnail->handleOptimized($tempFiles);
532
- }
533
 
534
  // Always update the WP meta - except for unlisted files.
535
- if ($thumbnail->getMeta('file') === null)
536
  {
537
 
538
  $size = $thumbnail->get('size');
@@ -545,105 +550,54 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
545
  $wpmeta['sizes'][$size]['filesize'] = $thumbnail->getFileSize();
546
  }
547
 
548
- if ($result)
549
- {
550
- $optimized[$filebase] = $thumbnail->getMetaObj();
551
- }
552
- elseif ($thumbnail->get('prevent_next_try') !== false) // in case of fatal issues.
553
  {
554
  $this->preventNextTry($thumbnail->get('prevent_next_try'));
555
  $return = false; //failed
556
  }
557
  }
558
 
559
- /** #### RETINAS ***/
560
- if (is_array($this->retinas))
561
  {
562
- foreach($this->retinas as $name => $retinaObj)
563
- {
564
- if (! isset($tempFiles[$retinaObj->getFileName()]) )
565
- {
566
- continue;
567
- }
568
-
569
- $retinaObj->setMeta('compressionType', $compressionType);
570
- $retinaObj->handleOptimizedFileType($tempFiles); // check for webps /etc
571
-
572
- if ($retinaObj->isOptimized())
573
- {
574
- continue;
575
- }
576
- if (!$retinaObj->isProcessable())
577
- {
578
- continue; // when excluded.
579
- }
580
- $filebase = $retinaObj->getFileBase();
581
- $result = false;
582
 
583
- if (isset($optimized[$filebase])) // double sizes.
584
- {
585
- $databaseID = $retinaObj->getMeta('databaseID');
586
- $retinaObj->setMetaObj($optimized[$filebase]);
587
- $retinaObj->setMeta('databaseID', $databaseID); // keep dbase id the same, otherwise it won't write this thumb to DB due to same ID.
588
- $result = false;
589
- }
590
- else
591
- {
592
- $result = $retinaObj->handleOptimized($tempFiles);
593
- }
594
 
595
- if ($result)
596
- {
597
- $optimized[$filebase] = $retinaObj->getMetaObj();
598
- }
599
- elseif ($retinaObj->get('prevent_next_try') !== false) // in case of fatal issues.
600
- {
601
- $this->preventNextTry($retinaObj->get('prevent_next_try'));
602
- $return = false; //failed
603
- }
604
- } // retinas loop
605
  }
606
 
607
- if ($this->isScaled() )
608
- {
609
- $original_file = $this->getOriginalFile();
610
- $original_file->handleOptimizedFileType($tempFiles); // @todo Find out why this is before handleOptimized
611
- $original_file->setMeta('compressionType', $compressionType);
612
-
613
- if (! $original_file->isOptimized())
614
- {
615
-
616
- $result = $original_file->handleOptimized($tempFiles);
617
-
618
- if (! $result && $original_file->get('prevent_next_try') !== false)
619
- {
620
- $this->preventNextTry($original_file->get('prevent_next_try'));
621
- $return = false; // failed.
622
- }
623
-
624
- }
625
- $this->original_file = $original_file;
626
- }
627
 
628
  $this->saveMeta();
629
  update_post_meta($this->get('id'), '_wp_attachment_metadata', $wpmeta);
630
 
631
-
632
- $duplicates = $this->getWPMLDuplicates();
633
- if (is_array($duplicates) && count($duplicates) > 0)
634
  {
635
  // Run the WPML duplicates
636
- foreach($duplicates as $duplicate_id)
637
  {
638
- // Save the exact same data under another post.
639
- $this->createDuplicateRecord($duplicate_id);
640
 
 
 
 
 
 
641
  $duplicate_meta = wp_get_attachment_metadata($duplicate_id);
642
  $duplicate_meta = array_merge($duplicate_meta, $wpmeta);
643
 
644
  update_post_meta($duplicate_id, '_wp_attachment_metadata', $duplicate_meta);
645
  }
646
-
647
  }
648
 
649
  return $return;
@@ -773,6 +727,7 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
773
  //$meta->fromClass($thumbMeta); // Load Thumbnail data from our saved Meta in model
774
  $thumbObj->setMetaObj($newMeta);
775
  $thumbObj->setName($name);
 
776
  if ($thumbObj->exists()) // if we exist.
777
  {
778
  $thumbnails[$name] = $thumbObj;
@@ -795,6 +750,7 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
795
  $retMeta->fromClass($retinaMeta);
796
  $retinaObj->setMetaObj($retMeta);
797
  $retinaObj->setName($name);
 
798
  $retinaObj->is_retina = true;
799
 
800
  $this->retinas[$name] = $retinaObj;
@@ -802,18 +758,22 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
802
  }
803
  }
804
 
805
- if (property_exists($metadata, 'original_file') && is_object($metadata->original_file))
 
 
 
806
  {
807
- $orFile = $this->getOriginalFile();
808
- if ($orFile)
809
- {
810
- $orMeta = new ImageThumbnailMeta();
811
- $orMeta->fromClass($metadata->original_file);
812
- $orFile->setMetaObj($orMeta);
813
- $this->original_file = $orFile;
814
- }
815
  }
816
 
 
817
  }
818
 
819
  // settings defaults
@@ -843,9 +803,10 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
843
  }
844
  elseif (count($meta) == 1 && $meta[0]->image_type == self::IMAGE_TYPE_DUPLICATE)
845
  {
846
- $duplicate_id = $meta[0]->parent;
847
  $sqlPrep = $wpdb->prepare($sqlQuery, $duplicate_id);
848
  $meta = $wpdb->get_results($sqlPrep);
 
849
 
850
  }
851
  elseif (count($meta) == 0) // no records, no object.
@@ -952,36 +913,27 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
952
  */
953
  // @todo Test with retinas, they probably won't work because named after thumbname or 0
954
 
955
- protected function saveDBMeta($metadata)
956
  {
957
  //global $wpdb;
958
 
959
- $records = array();
960
- $records[] = $this->createRecord($metadata->image_meta, self::IMAGE_TYPE_MAIN);
961
 
962
- if (property_exists($metadata, 'thumbnails'))
963
- {
964
- foreach($metadata->thumbnails as $name => $thumbData)
965
- {
966
- $records[] = $this->createRecord($thumbData, self::IMAGE_TYPE_THUMB, $name);
967
- }
968
- }
969
 
970
- if (property_exists($metadata, 'retinas'))
971
- {
972
- foreach($metadata->retinas as $name => $retData)
973
- {
974
- $records[] = $this->createRecord($retData, self::IMAGE_TYPE_RETINA, $name);
975
- }
976
- }
977
 
978
- if ($this->isScaled() && property_exists($metadata, 'original_file'))
979
- {
980
- $orData = $metadata->original_file;
981
- $records[] = $this->createRecord($orData, self::IMAGE_TYPE_ORIGINAL);
982
- }
983
- // @todo II -> figure out how to store thumbnails. Probably either per thumbnails check if it exists in the database and then update or delete.
984
- // This should include a check to see if there are thumbnail sizes no longer in the thumbnails array, probably they should be removed.
 
 
985
 
986
  $this->cleanupDatabase($records);
987
 
@@ -1094,18 +1046,10 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1094
  {
1095
  $data = new \StdClass;
1096
 
1097
- $data->parent = ($parent == null) ? $this->id : $parent;
1098
  $data->attach_id = $duplicate_id;
1099
  $imageType = self::IMAGE_TYPE_DUPLICATE;
1100
 
1101
- /*
1102
- $data->status = $this->getMeta('status');
1103
- $data->tsOptimized = $this->getMeta('tsOptimized');
1104
- $data->tsAdded = $this->getMeta('tsAdded');
1105
- $data->compressionType = $this->getMeta('compressionType');
1106
- $data->originalSize = $this->getMeta('originalSize');
1107
- $data->compressedSize = $this->getMeta('compressedSize');
1108
- */
1109
  $data->status = null;
1110
  $data->tsOptimized = null;
1111
  $data->tsAdded = null;
@@ -1113,8 +1057,9 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1113
  $data->originalSize = null;
1114
  $data->compressedSize = null;
1115
 
 
1116
 
1117
- $this->createRecord($data, $imageType);
1118
  }
1119
 
1120
  private function cleanupDatabase($records)
@@ -1139,77 +1084,23 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1139
  $wpdb->query($sql);
1140
  }
1141
 
1142
- private function createSave()
1143
- {
1144
- $metadata = new \stdClass; // $this->image_meta->toClass();
1145
- $metadata->image_meta = $this->image_meta->toClass();
1146
- $thumbnails = array();
1147
- $retinas = array();
1148
- // $webps = array();
1149
 
1150
- foreach($this->thumbnails as $thumbName => $thumbObj)
1151
- {
1152
- if ($thumbObj->getMeta('status') > 0)
1153
- {
1154
- $thumbnails[$thumbName] = $thumbObj->toClass();
1155
- }
1156
- }
1157
-
1158
- if (is_array($this->retinas))
1159
- {
1160
- foreach($this->retinas as $index => $retinaObj)
1161
- {
1162
- if ($retinaObj->getMeta('status') > 0)
1163
- {
1164
- $retinas[$index] = $retinaObj->toClass();
1165
- }
1166
- }
1167
- }
1168
-
1169
- if (count($thumbnails) > 0)
1170
- $metadata->thumbnails = $thumbnails;
1171
- if (count($retinas) > 0)
1172
- $metadata->retinas = $retinas;
1173
-
1174
- if ($this->isScaled())
1175
- {
1176
- if ($this->original_file->getMeta('status') > 0 )
1177
- {
1178
- $metadata->original_file = $this->original_file->toClass();
1179
- }
1180
- }
1181
-
1182
- return $metadata;
1183
- }
1184
 
1185
  public function saveMeta()
1186
  {
1187
  global $wpdb;
1188
 
1189
- $metadata = $this->createSave();
1190
- $this->saveDBMeta($metadata);
1191
- // There is no point checking for errors since false is returned on both failure and no field changed.
1192
- //update_post_meta($this->id, '_shortpixel_meta', $metadata);
1193
 
1194
- /* if ($this->isOptimized())
1195
- {
1196
- update_post_meta($this->id, '_shortpixel_optimized', $this->getImprovement() );
1197
- update_post_meta($this->id, '_shortpixel_optdate', $this->getMeta('tsOptimized'));
1198
- } */
1199
  }
1200
 
1201
  /** Delete the ShortPixel Meta. */
1202
  public function deleteMeta()
1203
  {
1204
  global $wpdb;
1205
- // $bool = delete_post_meta($this->id, '_shortpixel_meta');
1206
- //if (! $bool)
1207
- // Log::addWarn('Delete Post Meta failed');
1208
 
1209
- //delete_post_meta($this->id, '_shortpixel_optimized');
1210
  $this->resetPrevent();
1211
- //delete_post_meta($this->id, '_shortpixel_was_converted');
1212
- //delete_post_meta($this->id, '_shortpixel_optdate');
1213
 
1214
  $sql = 'DELETE FROM ' . $wpdb->prefix . 'shortpixel_postmeta WHERE attach_id = %s';
1215
  $sql = $wpdb->prepare($sql, $this->id);
@@ -1223,9 +1114,9 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1223
  // FileDelete param for subclass compat.
1224
  public function onDelete($fileDelete = false)
1225
  {
1226
- $duplicates = $this->getWPMLDuplicates();
1227
 
1228
- $fileDelete = (count($duplicates) == 0) ? true : false;
1229
 
1230
  if ($fileDelete === true)
1231
  parent::onDelete();
@@ -1258,6 +1149,19 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1258
  $this->removeLegacy();
1259
  $this->deleteMeta();
1260
  $this->dropFromQueue();
 
 
 
 
 
 
 
 
 
 
 
 
 
1261
  }
1262
 
1263
  public function dropFromQueue()
@@ -1284,7 +1188,7 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1284
  return false;
1285
  }
1286
 
1287
- /* Check if an image in theory could be processed. Check exclusions, thumbnails etc */
1288
  /* @param Strict Boolean Check only the main image, don't check thumbnails */
1289
  public function isProcessable($strict = false)
1290
  {
@@ -1312,7 +1216,9 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1312
  if (! $bool) // if parent is not processable, check if thumbnails are, can still have a work to do.
1313
  {
1314
 
1315
- foreach($this->thumbnails as $thumbnail)
 
 
1316
  {
1317
 
1318
  $bool = $thumbnail->isThumbnailProcessable();
@@ -1321,35 +1227,12 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1321
  return true;
1322
  }
1323
 
1324
- // check if original image is optimized.
1325
- if ($this->isScaled())
1326
- {
1327
- $bool = $this->getOriginalFile()->isThumbnailProcessable();
1328
- if ($bool === true)
1329
- return true;
1330
- }
1331
  }
1332
 
1333
- if (! $bool && ! is_null($this->retinas) && count($this->retinas) > 0)
1334
- {
1335
- foreach($this->retinas as $name => $retinaObj)
1336
- {
1337
- if ($retinaObj->isThumbnailProcessable())
1338
- {
1339
- $bool = true;
1340
- return true;
1341
- }
1342
- }
1343
- }
1344
-
1345
- // Todo check if Webp / Avisf is active, check for unoptimized items
1346
- if ($this->isProcessableFileType('webp'))
1347
- {
1348
- $bool = true;
1349
- }
1350
- if ($this->isProcessableFileType('avif'))
1351
  {
1352
- $bool = true;
1353
  }
1354
 
1355
  return $bool;
@@ -1448,7 +1331,6 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1448
  {
1449
  $file = $fs->getFile($thumbObj->getFileDir() . $thumbObj->getFileBase() . '.jpg');
1450
  $thumbObj->setMeta('did_png2jpg', true);
1451
- $thumbObj->setMeta('status', ImageModel::FILE_STATUS_PENDING);
1452
 
1453
  if ($file->exists()) // if new exists, remove old
1454
  {
@@ -1461,19 +1343,16 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1461
 
1462
  if ($this->isScaled())
1463
  {
1464
-
1465
- $originalFile = $this->getOriginalFile();
1466
- $file = $fs->getFile($originalFile->getFileDir() . $originalFile->getFileBase() . '.jpg');
1467
- $originalFile->setMeta('did_png2jpg', true);
1468
- $originalFile->setMeta('status', ImageModel::FILE_STATUS_PENDING);
1469
-
1470
- if ($file->exists()) // if new exists, remove old
1471
- {
1472
- $originalFile->delete(); // remove the old file.
1473
- $originalFile->fullpath = $file->getFullPath();
1474
- $originalFile->resetStatus();
1475
- $originalFile->setFileInfo();
1476
- }
1477
  }
1478
 
1479
  // Update
@@ -1483,10 +1362,9 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1483
 
1484
  if ($settings->backupImages == 1)
1485
  {
1486
-
1487
  // When failed, delete the backups. This can't be done via restore since image is not optimized.
1488
  $backupFile = $this->getBackupFile();
1489
- if ($backupFile->exists())
1490
  {
1491
  $backupFile->delete();
1492
  }
@@ -1501,14 +1379,12 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1501
  if ($this->isScaled())
1502
  {
1503
  $backupFile = $this->getOriginalFile()->getBackupFile();
1504
- if ($backupFile->exists())
1505
  $backupFile->delete();
1506
 
1507
  }
1508
  }
1509
-
1510
  // Prevent from retrying next time, since stuff will be requeued.
1511
-
1512
  }
1513
 
1514
  $this->setMeta('tried_png2jpg', true);
@@ -1517,58 +1393,6 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1517
  return $bool;
1518
  } // convertPNG
1519
 
1520
- protected function isSizeExcluded()
1521
- {
1522
- $excludePatterns = \wpSPIO()->settings()->excludePatterns;
1523
-
1524
- if (! $excludePatterns || ! is_array($excludePatterns) ) // no patterns, nothing excluded
1525
- return false;
1526
-
1527
- $bool = false;
1528
-
1529
- foreach($excludePatterns as $item) {
1530
- $type = trim($item["type"]);
1531
- if($type == "size") {
1532
- //$meta = $meta? $meta : wp_get_attachment_metadata($ID);
1533
- $width = $this->get('width');
1534
- $height = $this->get('height');
1535
-
1536
- if( $width && $height
1537
- && $this->isProcessableSize($width, $height, $item["value"]) === false){
1538
- $this->processable_status = self::P_EXCLUDE_SIZE;
1539
- return true; // exit directly because we have our exclusion
1540
- }
1541
- else
1542
- $bool = false; // continue and check all patterns, there might be multiple.
1543
- }
1544
- }
1545
-
1546
- return $bool;
1547
-
1548
- }
1549
-
1550
- private function isProcessableSize($width, $height, $excludePattern) {
1551
-
1552
- $ranges = preg_split("/(x|×|X)/",$excludePattern);
1553
- $widthBounds = explode("-", $ranges[0]);
1554
- $minWidth = intval($widthBounds[0]);
1555
- $maxWidth = (!isset($widthBounds[1])) ? intval($widthBounds[0]) : intval($widthBounds[1]);
1556
-
1557
- $heightBounds = isset($ranges[1]) ? explode("-", $ranges[1]) : false;
1558
- $minHeight = $maxHeight = 0;
1559
- if ($heightBounds)
1560
- {
1561
- $minHeight = intval($heightBounds[0]);
1562
- $maxHeight = (!isset($heightBounds[1])) ? intval($heightBounds[0]) : intval($heightBounds[1]);
1563
- }
1564
-
1565
- if( $width >= $minWidth && $width <= $maxWidth
1566
- && ( $heightBounds === false
1567
- || ($height >= $minHeight && $height <= $maxHeight) )) {
1568
- return false;
1569
- }
1570
- return true;
1571
- }
1572
 
1573
  // Perhaps treat this as thumbnail? And remove function from FileSystemController?
1574
  protected function setOriginalFile()
@@ -1579,6 +1403,8 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1579
  return false;
1580
 
1581
  $originalFile = $fs->getOriginalImage($this->id);
 
 
1582
 
1583
  if ($originalFile->exists() && $originalFile->getFullPath() !== $this->getfullPath() )
1584
  {
@@ -1604,6 +1430,24 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1604
  return false;
1605
  }
1606
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1607
  /** Try to find language duplicates in WPML and add the same status to it.
1608
  ** @integration WPML
1609
  *
@@ -1704,8 +1548,13 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1704
 
1705
  /** Removed the current attachment, with hopefully removing everything we set.
1706
  */
1707
- public function restore()
1708
  {
 
 
 
 
 
1709
 
1710
  $fs = \wpSPIO()->filesystem();
1711
 
@@ -1715,6 +1564,10 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1715
  $cleanRestore = true;
1716
  $wpmeta = wp_get_attachment_metadata($this->get('id'));
1717
 
 
 
 
 
1718
  $did_png2jpg = $this->getMeta('did_png2jpg');
1719
  $is_resized = $this->getMeta('resize');
1720
 
@@ -1853,18 +1706,22 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1853
  $this->saveMeta(); // Save if something is not restored.
1854
  }
1855
 
 
 
 
 
 
1856
  update_post_meta($this->get('id'), '_wp_attachment_metadata', $wpmeta);
1857
 
1858
 
1859
  do_action('shortpixel_after_restore_image', $this->id, $cleanRestore); // legacy
1860
  do_action('shortpixel/image/after_restore', $this, $this->id, $cleanRestore);
1861
 
1862
- $duplicates = $this->getWPMLDuplicates();
1863
- if (is_array($duplicates) && count($duplicates) > 0 )
1864
  {
1865
  $current_id = $this->id;
1866
 
1867
- foreach($duplicates as $duplicate_id)
1868
  {
1869
  $this->id = $duplicate_id;
1870
  $this->removeLegacy();
@@ -1953,29 +1810,32 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1953
  return false;
1954
  }
1955
 
 
1956
 
1957
- foreach($this->thumbnails as $thumbObj)
 
1958
  {
1959
  if ($thumbObj->hasBackup())
1960
  {
1961
  $backupFile = $thumbObj->getBackupFile();
1962
 
1963
- $backupFile->delete();
1964
-
1965
- $backupFileJPG = $fs->getFile($backupFile->getFileDir() . $backupFile->getFileBase() . '.jpg');
1966
- if ($backupFileJPG->exists())
1967
  {
1968
- $backupFileJPG->delete();
 
 
 
 
 
 
1969
  }
1970
  }
1971
 
1972
- // $thumbObj->delete(); // delete the jpg
1973
- $toRemove[] = $thumbObj;
1974
- // $thumbObj->resetStatus();
1975
- // $thumbObj->image_meta = new ImageThumbnailMeta();
1976
  }
1977
 
1978
- if ($this->isScaled())
1979
  {
1980
 
1981
  $originalFile = $this->getOriginalFile();
@@ -1993,10 +1853,8 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
1993
  }
1994
 
1995
  $toRemove[] = $originalFile;
1996
- // $originalFile->delete(); // should be .jpg file.
1997
- // $originalFile->resetStatus();
1998
- // $originalFile->image_meta = new ImageThumbnailMeta();
1999
- }
2000
 
2001
  // Fullpath now will still be .jpg
2002
  // PNGconvert is first, because some plugins check for _attached_file metadata and prevent deleting files if still connected to media library. Exmaple: polylang.
@@ -2031,10 +1889,8 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
2031
  $res = \wp_create_image_subsizes($fullpath, $this->id);
2032
 
2033
  remove_filter('as3cf_wait_for_generate_attachment_metadata', array($this, 'returnTrue'));
2034
-
2035
  }
2036
 
2037
-
2038
  public function returnTrue()
2039
  {
2040
  return true;
@@ -2052,6 +1908,29 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
2052
  }
2053
  }
2054
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2055
  // Load items that might be processable but not in WP metadata or other meta's .
2056
  // used for IsProcessable ( check if we have something ) and handleOptimized ( to check against the optimized stuff )
2057
  private function loadLooseItems()
@@ -2347,7 +2226,10 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
2347
  }
2348
 
2349
  $originalFile->image_meta->tsAdded = $tsAdded;
2350
- $originalFile->image_meta->tsOptimized = $tsOptimized;
 
 
 
2351
  $originalFile->has_backup = $originalFile->hasBackup();
2352
 
2353
  $originalFile->image_meta->webp = $this->checkLegacyFileTypeFileName($originalFile, 'webp');
@@ -2372,42 +2254,47 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
2372
  $retinasOpt = $data['retinasOpt'];
2373
  $retinas = $this->getRetinas();
2374
 
2375
- foreach($retinas as $index => $retinaObj) // Thumbnail Model
2376
- {
2377
- if ($retinaObj->hasDBRecord() === true)
2378
- {
2379
- continue;
2380
- }
2381
-
2382
- // Check if thumbnail ('parent') is Optimized, if so, then retina probably should be optimized as well.
2383
- if ( (isset($this->thumbnails[$index]) &&
2384
- is_object($this->thumbnails[$index]) &&
2385
- $this->thumbnails[$index]->isOptimized) || $retinaObj->hasBackup() )
2386
- {
2387
- $retinaObj->image_meta->status = $status;
2388
- $retinaObj->image_meta->compressionType = $type;
2389
- if ($status == self::FILE_STATUS_SUCCESS)
2390
- $retinaObj->image_meta->compressedSize = $retinaObj->getFileSize();
2391
- else
2392
- $retinaObj->image_meta->originalSize = $retinaObj->getFileSize();
2393
- // $retinaObj->image_meta->improvement = -1; // n/a
2394
- $retinaObj->image_meta->tsAdded = $tsAdded;
2395
- $retinaObj->image_meta->tsOptimized = $tsOptimized;
2396
- $retinaObj->image_meta->did_jpg2png = $did_jpg2png;
2397
- if ($retinaObj->hasBackup())
2398
- {
2399
- $retinaObj->has_backup = true;
2400
- if ($status == self::FILE_STATUS_SUCCESS)
2401
- $retinaObj->image_meta->originalSize = $retinaObj->getBackupFile()->getFileSize();
2402
- }
2403
-
2404
- $retinaObj->recordChanged(true);
2405
- $retinas[$index] = $retinaObj;
2406
- $addedCounter++;
2407
- }
2408
- } // foreach
2409
-
2410
- $this->retinas = $retinas;
 
 
 
 
 
2411
  if ($count !== $addedCounter)
2412
  {
2413
  Log::addWarning("Conversion: $count retinas expected in legacy, " . $addedCounter . 'found. This can be due to overlapping image sizes.');
@@ -2542,10 +2429,16 @@ class MediaLibraryModel extends \ShortPixel\Model\Image\MediaLibraryThumbnailMod
2542
  'retinas' => $this->retinas,
2543
  'original_file' => $this->original_file,
2544
  'is_scaled' => $this->is_scaled,
 
2545
  );
2546
 
2547
  }
2548
 
 
 
 
 
 
2549
  /** Adds Unlisted Image to the Media Library Item
2550
  * This function is called in IsProcessable
2551
  */
21
  protected $do_png2jpg = false; // option to flag this one should be checked / converted to jpg.
22
 
23
  protected $wp_metadata;
24
+ private $parent; // In case of WPML Duplicates
25
 
26
  protected $type = 'media';
27
  protected $is_main_file = true; // for checking
31
  protected $optimizePrevented; // cache if there is any reason to prevent optimizing
32
  private $justConverted = false; // check if conversion happened on same run, to prevent double runs.
33
 
34
+ private $optimizeData; // cache to prevent running this more than once per run.
35
+
 
 
 
36
 
37
  public function __construct($post_id, $path)
38
  {
39
  $this->id = $post_id;
40
+ $this->imageType = self::IMAGE_TYPE_MAIN;
41
 
42
  parent::__construct($path, $post_id, null);
43
 
54
  }
55
 
56
 
57
+ public function getOptimizeUrls()
58
+ {
59
+ $data = $this->getOptimizeData();
60
+ return array_values($data['urls']);
61
+ }
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  // Path will only return the filepath. For reasons, see getOptimizeFileType
64
+ public function getOptimizeData()
65
  {
66
+ if (! is_null($this->optimizeData))
67
+ {
68
+ return $this->optimizeData;
69
+ }
70
 
71
+ $parameters = array(
72
+ 'urls' => array(),
73
+ 'params' => array(),
74
+ // doubles are items that will have same result, but is diffent file. duplicates are same files, same result - only meta update.
75
+ 'returnParams' => array('sizes' => array(), 'doubles' => array(), 'duplicates' => array() ),
76
+ );
77
 
78
+ $settings = \wpSPIO()->settings();
79
+
80
+ $url = $this->getURL();
 
 
81
 
82
  if (! $url) // If the whole image URL can't be found
83
  {
84
+ return $parameters;
85
  }
86
 
87
+ $isSmartCrop = ($settings->useSmartcrop == true && $this->getExtension() !== 'pdf') ? true : false;
88
+ $doubles = array(); // check via hash if same command / result is there.
 
 
 
89
 
90
+ // Use URL of biggest image.
91
+ /*if ($isSmartCrop === true && $this->isScaled())
92
+ {
93
+ $url = $this->getOriginalFile()->getURL();
94
+ } */
95
 
96
+ if ($this->isProcessable(true) || ($this->isProcessableAnyFileType() && $this->isOptimized()) )
97
+ {
98
+ $paramList = $this->createParamList();
99
+ $parameters['urls'][0] = $url;
100
+ $parameters['paths'][0] = $this->getFullPath();
101
+ $parameters['params'][0] = $paramList;
102
+ $parameters['returnParams']['sizes'][0] = $this->getFileName();
103
 
104
+ if ($isSmartCrop)
105
+ {
106
+ $parameters['returnParams']['fileSizes'][0] = $this->getFileSize();
107
+ }
108
 
109
+ $hash = md5( serialize($paramList) . $url);
110
+ $doubles[$hash] = 0;
111
+ }
 
 
 
 
112
 
113
+ $thumbObjs = $this->getThumbObjects();
114
+ $unProcessable = array();
115
 
116
+ foreach($thumbObjs as $name => $thumbObj)
117
+ {
118
+ if ($thumbObj->isThumbnailProcessable() || ($thumbObj->isProcessableAnyFileType() && $thumbObj->isOptimized()) )
119
+ {
120
 
121
+ if (! $isSmartCrop)
122
+ {
123
+ $url = $thumbObj->getURL();
124
+ }
125
 
126
+ $paramList = $thumbObj->createParamList();
127
+ $hash = md5( serialize($paramList) . $url); // Hash the paramlist + url to find identical results.
 
 
 
 
 
128
 
129
+ if (isset($doubles[$hash]))
130
+ {
131
+ $doubleName = $doubles[$hash];
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ if (is_numeric($doubleName) && intval($doubleName) === 0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  {
135
+ $compareObj = $this;
136
  }
137
  else {
138
+ $compareObj = $thumbObjs[$doubleName];
139
  }
 
 
140
 
141
+ if ($thumbObj->getFileName() == $compareObj->getFileName())
 
 
 
 
142
  {
143
+ $parameters['returnParams']['duplicates'][$name] = $doubleName;
 
 
 
144
  }
145
+ else
 
 
 
 
 
 
 
146
  {
147
+ // Check if in a duplicate item is in doubles, so we don't double-double it.
148
+ $aDuplicate = false;
149
+ foreach($parameters['returnParams']['doubles'] as $doubleNameInDoubles => $unneeded)
150
  {
151
+ if ($doubleNameInDoubles !== 0 && $doubleNameInDoubles !== 1 && $thumbObjs[$doubleNameInDoubles]->getFileName() == $thumbObj->getFileName())
152
+ {
153
+ $aDuplicate = true;
154
+ $parameters['returnParams']['duplicates'][$name] = $doubleNameInDoubles;
155
+ }
156
  }
157
+
158
+ if (false === $aDuplicate)
159
+ {
160
+ $parameters['returnParams']['doubles'][$name] = $doubleName;
161
  }
162
  }
163
+ }
164
+ else {
165
+ $parameters['urls'][$name] = $url;
166
+ $parameters['paths'][$name] = $thumbObj->getFullPath();
167
+ $parameters['params'][$name] = $paramList;
168
+ $parameters['returnParams']['sizes'][$name] = $thumbObj->getFileName();
169
+ if ($isSmartCrop)
170
+ {
171
+ $parameters['returnParams']['fileSizes'][$name] = $thumbObj->getFileSize();
172
+ }
173
+ $doubles[$hash] = $name;
174
+ }
175
+
176
+ }
177
+ else {
178
+ // Save rejected thumbs, because they might be a duplicate of something that goes on the processing.
179
+ $unProcessable[] = $thumbObj;
180
+ }
181
+ }
182
+
183
+ // If one or more thumbnails were not processable, still check them against the process list in case identical sizes are being processed and it should be marked as a duplicate.
184
+ if (isset($parameters['paths']) && count($unProcessable) > 0)
185
+ {
186
+ $pathVal = array_values($parameters['paths']);
187
+ $pathLookup = array_flip($parameters['paths']); // lookup fullpath -> size.
188
 
189
+ foreach($unProcessable as $thumbObj)
190
+ {
191
+ if (in_array($thumbObj->getFullPath(), $pathVal) === true)
192
+ {
193
+ $parameters['returnParams']['duplicates'][$thumbObj->get('name')] = $pathLookup[$thumbObj->getFullPath()];
194
+ }
195
+ }
196
+ }
197
+
198
+ $this->optimizeData = $parameters;
199
+ return $parameters;
200
  }
201
 
202
+ public function flushOptimizeData()
203
+ {
204
+ $this->optimizeData = null;
205
+ }
206
+
207
+
208
+
209
+
210
+
211
+ // Try to get the URL via WordPress
212
+ // This is now officially a heavy function. Take times, other plugins (like s3) might really delay it
213
+ public function getURL()
214
+ {
215
+ $url = $this->fs()->checkURL(wp_get_attachment_url($this->id));
216
+ return $url;
217
+ }
218
+
219
+
220
  public function getWPMetaData()
221
  {
222
  if (is_null($this->wp_metadata))
234
  return $this->is_scaled;
235
  }
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
  /** Loads an array of Thumbnailmodels based on sizes available in WordPress metadata
239
  ** @return Array consisting ofMediaLibraryThumbnailModel
338
  {
339
  $retinaObj = $thumbObj->getRetina();
340
  if ($retinaObj)
341
+ $this->retinas[$retinaObj->get('name')] = $retinaObj;
342
  }
343
  }
344
 
436
 
437
  }
438
 
439
+ public function handleOptimized($optimizeData)
440
  {
441
  $return = true;
442
  $wpmeta = wp_get_attachment_metadata($this->get('id'));
443
+ $WPMLduplicates = $this->getWPMLDuplicates();
444
+ $fs = \wpSPIO()->filesystem();
445
+
446
+ if (isset($optimizeData['files']) && isset($optimizeData['data']))
447
+ {
448
+ $files = $optimizeData['files'];
449
+ $data = $optimizeData['data'];
450
+ }
451
+ else {
452
+ Log::addError('Something went wrong with handleOptimized', $optimizeData);
453
+ }
454
 
455
+ $optimized = array();
456
+
457
+ // Main file has a 0 index.
458
+ $mainFile = (isset($files) && isset($files[0])) ? $files[0] : false;
459
+
460
+ if (! $this->isOptimized() && isset($mainFile['img']) ) // main file might not be contained in results
461
  {
462
  if ($this->getExtension() == 'heic')
463
  {
464
  $isHeic = true;
465
  }
466
 
467
+ $result = parent::handleOptimized($mainFile);
468
  if (! $result)
469
  {
470
  return false;
484
 
485
  }
486
 
487
+ $this->handleOptimizedFileType($mainFile);
488
 
 
489
 
490
  $compressionType = $this->getMeta('compressionType'); // CompressionType not set on subimages etc.
491
 
492
  // If thumbnails should not be optimized, they should not be in result Array.
493
  // #### THUMBNAILS ####
494
+ $thumbObjs = $this->getThumbObjects();
495
+
496
+ // Add doubles to the processing list. Doubles are sizes with the same result, but should be copied to it's respective thumbnail file + backup.
497
+ if (isset($data['doubles']))
498
+ {
499
+ foreach($data['doubles'] as $doubleName => $doubleRef)
500
+ {
501
+ $files[$doubleName] = $files[$doubleRef];
502
+ $doubleObj = $thumbObjs[$doubleName];
503
+ $data['sizes'][$doubleName] = $doubleObj->getFileName();
504
+ }
505
+ }
506
+
507
+ foreach($data['sizes'] as $sizeName => $fileName)
508
  {
509
  // Check if thumbnail is in the tempfiles return set. This might not always be the case
510
+ if (! isset($files[$sizeName]) )
511
  {
512
  continue;
513
  }
514
 
515
+ if (is_numeric($sizeName) && intval($sizeName) === 0)
516
+ {
517
+ continue;
518
+ }
519
+
520
+ $resultObj = $files[$sizeName];
521
+ $thumbnail = $thumbObjs[$sizeName];
522
+
523
+ $thumbnail->handleOptimizedFileType($resultObj); // check for webps /etc
524
 
525
  if ($thumbnail->isOptimized())
526
  {
530
  {
531
  continue; // when excluded.
532
  }
533
+
534
  $result = false;
535
 
536
+ $thumbnail->setMeta('compressionType', $compressionType);
537
+ $result = $thumbnail->handleOptimized($resultObj);
 
 
 
 
 
 
 
 
 
538
 
539
  // Always update the WP meta - except for unlisted files.
540
+ if ($thumbnail->get('imageType') == self::IMAGE_TYPE_THUMB && $thumbnail->getMeta('file') === null)
541
  {
542
 
543
  $size = $thumbnail->get('size');
550
  $wpmeta['sizes'][$size]['filesize'] = $thumbnail->getFileSize();
551
  }
552
 
553
+
554
+ if ($thumbnail->get('prevent_next_try') !== false) // in case of fatal issues.
 
 
 
555
  {
556
  $this->preventNextTry($thumbnail->get('prevent_next_try'));
557
  $return = false; //failed
558
  }
559
  }
560
 
561
+ // Add duplicates. Duplicates are metadata sizes that have a same file ( identical ) defined pointing.
562
+ if (isset($data['duplicates']))
563
  {
564
+ foreach($data['duplicates'] as $duplicateName => $duplicateRef)
565
+ {
566
+ $thumbnail = $thumbObjs[$duplicateName];
567
+ $duplicateObj = $thumbObjs[$duplicateRef];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
 
569
+ $databaseID = $thumbnail->getMeta('databaseID');
570
+ $thumbnail->setMetaObj($duplicateObj->getMetaObj());
571
+ $thumbnail->setMeta('databaseID', $databaseID); // keep dbase id the same, otherwise it won't write this thumb to DB due to same ID.
 
 
 
 
 
 
 
 
572
 
573
+ }
 
 
 
 
 
 
 
 
 
574
  }
575
 
576
+ // Remove Temp Files
577
+ $this->deleteTempFiles($files);
578
+ $this->flushOptimizeData();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
579
 
580
  $this->saveMeta();
581
  update_post_meta($this->get('id'), '_wp_attachment_metadata', $wpmeta);
582
 
583
+ if (is_array($WPMLduplicates) && count($WPMLduplicates) > 0)
 
 
584
  {
585
  // Run the WPML duplicates
586
+ foreach($WPMLduplicates as $duplicate_id)
587
  {
588
+ // Get this Object cacheless, because it can create records when loading.
589
+ $duplicateObj = $fs->getImage($duplicate_id, 'media', false);
590
 
591
+ // Save the exact same data under another post. Don't duplicate it, when already there.
592
+ if ($duplicateObj->getParent() === false)
593
+ {
594
+ $this->createDuplicateRecord($duplicate_id);
595
+ }
596
  $duplicate_meta = wp_get_attachment_metadata($duplicate_id);
597
  $duplicate_meta = array_merge($duplicate_meta, $wpmeta);
598
 
599
  update_post_meta($duplicate_id, '_wp_attachment_metadata', $duplicate_meta);
600
  }
 
601
  }
602
 
603
  return $return;
727
  //$meta->fromClass($thumbMeta); // Load Thumbnail data from our saved Meta in model
728
  $thumbObj->setMetaObj($newMeta);
729
  $thumbObj->setName($name);
730
+
731
  if ($thumbObj->exists()) // if we exist.
732
  {
733
  $thumbnails[$name] = $thumbObj;
750
  $retMeta->fromClass($retinaMeta);
751
  $retinaObj->setMetaObj($retMeta);
752
  $retinaObj->setName($name);
753
+ $retinaObj->setImageType(self::IMAGE_TYPE_RETINA);
754
  $retinaObj->is_retina = true;
755
 
756
  $this->retinas[$name] = $retinaObj;
758
  }
759
  }
760
 
761
+
762
+ $orFile = $this->getOriginalFile();
763
+
764
+ if ($orFile)
765
  {
766
+ $orMeta = new ImageThumbnailMeta();
767
+ if (property_exists($metadata, 'original_file') && is_object($metadata->original_file))
768
+ {
769
+ $orMeta->fromClass($metadata->original_file);
770
+ }
771
+ $orFile->setMetaObj($orMeta);
772
+ $orFile->setName(1); // 1 is name for original file / image.
773
+ $this->original_file = $orFile;
774
  }
775
 
776
+
777
  }
778
 
779
  // settings defaults
803
  }
804
  elseif (count($meta) == 1 && $meta[0]->image_type == self::IMAGE_TYPE_DUPLICATE)
805
  {
806
+ $duplicate_id = (int) $meta[0]->parent;
807
  $sqlPrep = $wpdb->prepare($sqlQuery, $duplicate_id);
808
  $meta = $wpdb->get_results($sqlPrep);
809
+ $this->parent = $duplicate_id;
810
 
811
  }
812
  elseif (count($meta) == 0) // no records, no object.
913
  */
914
  // @todo Test with retinas, they probably won't work because named after thumbname or 0
915
 
916
+ protected function saveDBMeta()
917
  {
918
  //global $wpdb;
919
 
920
+ $records = array();
921
+ $records[] = $this->createRecord($this->toClass(), self::IMAGE_TYPE_MAIN);
922
 
 
 
 
 
 
 
 
923
 
924
+ $thumbObjs = $this->getThumbObjects();
925
+ foreach($thumbObjs as $thumbObj)
926
+ {
 
 
 
 
927
 
928
+ $name = $thumbObj->get('name');
929
+ $type = $thumbObj->get('imageType');
930
+
931
+ if ($type == self::IMAGE_TYPE_ORIGINAL)
932
+ $name = null;
933
+
934
+ $records[] = $this->createRecord($thumbObj->toClass(), $type, $name);
935
+
936
+ }
937
 
938
  $this->cleanupDatabase($records);
939
 
1046
  {
1047
  $data = new \StdClass;
1048
 
1049
+ $data->parent = ($parent === null) ? $this->id : $parent;
1050
  $data->attach_id = $duplicate_id;
1051
  $imageType = self::IMAGE_TYPE_DUPLICATE;
1052
 
 
 
 
 
 
 
 
 
1053
  $data->status = null;
1054
  $data->tsOptimized = null;
1055
  $data->tsAdded = null;
1057
  $data->originalSize = null;
1058
  $data->compressedSize = null;
1059
 
1060
+ $this->parent = $data->parent;
1061
 
1062
+ $this->createRecord($data, $imageType);
1063
  }
1064
 
1065
  private function cleanupDatabase($records)
1084
  $wpdb->query($sql);
1085
  }
1086
 
 
 
 
 
 
 
 
1087
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1088
 
1089
  public function saveMeta()
1090
  {
1091
  global $wpdb;
1092
 
1093
+ $this->saveDBMeta();
 
 
 
1094
 
 
 
 
 
 
1095
  }
1096
 
1097
  /** Delete the ShortPixel Meta. */
1098
  public function deleteMeta()
1099
  {
1100
  global $wpdb;
 
 
 
1101
 
 
1102
  $this->resetPrevent();
1103
+
 
1104
 
1105
  $sql = 'DELETE FROM ' . $wpdb->prefix . 'shortpixel_postmeta WHERE attach_id = %s';
1106
  $sql = $wpdb->prepare($sql, $this->id);
1114
  // FileDelete param for subclass compat.
1115
  public function onDelete($fileDelete = false)
1116
  {
1117
+ $WPMLduplicates = $this->getWPMLDuplicates();
1118
 
1119
+ $fileDelete = (count($WPMLduplicates) == 0) ? true : false;
1120
 
1121
  if ($fileDelete === true)
1122
  parent::onDelete();
1149
  $this->removeLegacy();
1150
  $this->deleteMeta();
1151
  $this->dropFromQueue();
1152
+
1153
+ $current_id = $this->id;
1154
+
1155
+ /* perhaps not needed if (is_array($WPMLduplicates) && count($WPMLduplicates) > 0)
1156
+ {
1157
+ foreach($WPMLduplicates as $duplicate_id)
1158
+ {
1159
+ $this->id = $duplicate_id;
1160
+ $this->removeLegacy();
1161
+ $this->deleteMeta();
1162
+ $this->dropFromQueue();
1163
+ }
1164
+ } */
1165
  }
1166
 
1167
  public function dropFromQueue()
1188
  return false;
1189
  }
1190
 
1191
+ /* Check if an image in theory could be processed. Check exclusions, thumbnails etc. */
1192
  /* @param Strict Boolean Check only the main image, don't check thumbnails */
1193
  public function isProcessable($strict = false)
1194
  {
1216
  if (! $bool) // if parent is not processable, check if thumbnails are, can still have a work to do.
1217
  {
1218
 
1219
+ $thumbObjs = $this->getThumbObjects();
1220
+
1221
+ foreach($thumbObjs as $thumbnail)
1222
  {
1223
 
1224
  $bool = $thumbnail->isThumbnailProcessable();
1227
  return true;
1228
  }
1229
 
 
 
 
 
 
 
 
1230
  }
1231
 
1232
+ // First test if this file isn't unprocessable for any other reason, then check.
1233
+ if (($this->isProcessable(true) || $this->isOptimized() ) && $this->isProcessableAnyFileType() === true)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1234
  {
1235
+ $bool = true;
1236
  }
1237
 
1238
  return $bool;
1331
  {
1332
  $file = $fs->getFile($thumbObj->getFileDir() . $thumbObj->getFileBase() . '.jpg');
1333
  $thumbObj->setMeta('did_png2jpg', true);
 
1334
 
1335
  if ($file->exists()) // if new exists, remove old
1336
  {
1343
 
1344
  if ($this->isScaled())
1345
  {
1346
+ $file = $fs->getFile($originalFile->getFileDir() . $originalFile->getFileBase() . '.jpg');
1347
+ $originalFile->setMeta('did_png2jpg', true);
1348
+
1349
+ if ($file->exists()) // if new exists, remove old
1350
+ {
1351
+ $originalFile->delete(); // remove the old file.
1352
+ $originalFile->fullpath = $file->getFullPath();
1353
+ $originalFile->resetStatus();
1354
+ $originalFile->setFileInfo();
1355
+ }
 
 
 
1356
  }
1357
 
1358
  // Update
1362
 
1363
  if ($settings->backupImages == 1)
1364
  {
 
1365
  // When failed, delete the backups. This can't be done via restore since image is not optimized.
1366
  $backupFile = $this->getBackupFile();
1367
+ if (is_object($backupFile) && $backupFile->exists())
1368
  {
1369
  $backupFile->delete();
1370
  }
1379
  if ($this->isScaled())
1380
  {
1381
  $backupFile = $this->getOriginalFile()->getBackupFile();
1382
+ if (is_object($backupFile) && $backupFile->exists())
1383
  $backupFile->delete();
1384
 
1385
  }
1386
  }
 
1387
  // Prevent from retrying next time, since stuff will be requeued.
 
1388
  }
1389
 
1390
  $this->setMeta('tried_png2jpg', true);
1393
  return $bool;
1394
  } // convertPNG
1395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1396
 
1397
  // Perhaps treat this as thumbnail? And remove function from FileSystemController?
1398
  protected function setOriginalFile()
1403
  return false;
1404
 
1405
  $originalFile = $fs->getOriginalImage($this->id);
1406
+ $originalFile->setName(1); // required for named API requests et al.
1407
+ $originalFile->setImageType(self::IMAGE_TYPE_ORIGINAL);
1408
 
1409
  if ($originalFile->exists() && $originalFile->getFullPath() !== $this->getfullPath() )
1410
  {
1430
  return false;
1431
  }
1432
 
1433
+
1434
+ // Check if this Image has a Parent indicating it's a WPML Duplicate.
1435
+ public function getParent()
1436
+ {
1437
+ if (is_null($this->parent))
1438
+ {
1439
+ return false;
1440
+ }
1441
+ if (is_numeric($this->parent))
1442
+ {
1443
+ return $this->parent;
1444
+ }
1445
+
1446
+ // Dunno.
1447
+ return false;
1448
+ }
1449
+
1450
+
1451
  /** Try to find language duplicates in WPML and add the same status to it.
1452
  ** @integration WPML
1453
  *
1548
 
1549
  /** Removed the current attachment, with hopefully removing everything we set.
1550
  */
1551
+ public function restore($args = array())
1552
  {
1553
+ $defaults = array(
1554
+ 'keep_in_queue' => false, // used for bulk restore.
1555
+ );
1556
+
1557
+ $args = wp_parse_args($args, $defaults);
1558
 
1559
  $fs = \wpSPIO()->filesystem();
1560
 
1564
  $cleanRestore = true;
1565
  $wpmeta = wp_get_attachment_metadata($this->get('id'));
1566
 
1567
+ // Get them early in case the filename changes ( ie png to jpg ) because it will stop getting it.
1568
+ $WPMLduplicates = $this->getWPMLDuplicates();
1569
+
1570
+
1571
  $did_png2jpg = $this->getMeta('did_png2jpg');
1572
  $is_resized = $this->getMeta('resize');
1573
 
1706
  $this->saveMeta(); // Save if something is not restored.
1707
  }
1708
 
1709
+ if ($args['keep_in_queue'] === false)
1710
+ {
1711
+ $this->dropFromQueue();
1712
+ }
1713
+
1714
  update_post_meta($this->get('id'), '_wp_attachment_metadata', $wpmeta);
1715
 
1716
 
1717
  do_action('shortpixel_after_restore_image', $this->id, $cleanRestore); // legacy
1718
  do_action('shortpixel/image/after_restore', $this, $this->id, $cleanRestore);
1719
 
1720
+ if (is_array($WPMLduplicates) && count($WPMLduplicates) > 0 )
 
1721
  {
1722
  $current_id = $this->id;
1723
 
1724
+ foreach($WPMLduplicates as $duplicate_id)
1725
  {
1726
  $this->id = $duplicate_id;
1727
  $this->removeLegacy();
1810
  return false;
1811
  }
1812
 
1813
+ $thumbObjs = $this->getThumbObjects();
1814
 
1815
+
1816
+ foreach($thumbObjs as $thumbObj)
1817
  {
1818
  if ($thumbObj->hasBackup())
1819
  {
1820
  $backupFile = $thumbObj->getBackupFile();
1821
 
1822
+ if (is_object($backupFile))
 
 
 
1823
  {
1824
+ $backupFile->delete();
1825
+
1826
+ $backupFileJPG = $fs->getFile($backupFile->getFileDir() . $backupFile->getFileBase() . '.jpg');
1827
+ if (is_object($backupFileJPG) && $backupFileJPG->exists())
1828
+ {
1829
+ $backupFileJPG->delete();
1830
+ }
1831
  }
1832
  }
1833
 
1834
+ $toRemove[] = $thumbObj;
1835
+
 
 
1836
  }
1837
 
1838
+ /* if ($this->isScaled())
1839
  {
1840
 
1841
  $originalFile = $this->getOriginalFile();
1853
  }
1854
 
1855
  $toRemove[] = $originalFile;
1856
+
1857
+ } */
 
 
1858
 
1859
  // Fullpath now will still be .jpg
1860
  // PNGconvert is first, because some plugins check for _attached_file metadata and prevent deleting files if still connected to media library. Exmaple: polylang.
1889
  $res = \wp_create_image_subsizes($fullpath, $this->id);
1890
 
1891
  remove_filter('as3cf_wait_for_generate_attachment_metadata', array($this, 'returnTrue'));
 
1892
  }
1893
 
 
1894
  public function returnTrue()
1895
  {
1896
  return true;
1908
  }
1909
  }
1910
 
1911
+ /** Utility function to create a loopable object of thumbnails, retinas and scaled (if so) and other object with thumbnailModel . Goal is to prevent several functions having to do the same operation on array with different names ( optimized, getOptimizeUrl, etc )
1912
+ * @return Object Iterator
1913
+ */
1914
+ private function getThumbObjects()
1915
+ {
1916
+ $objects = $this->thumbnails;
1917
+
1918
+ if (! is_null($this->retinas) && is_array($this->retinas))
1919
+ {
1920
+ foreach($this->retinas as $retinaObj)
1921
+ {
1922
+ $objects['retina_' . $retinaObj->get('name')] = $retinaObj;
1923
+ }
1924
+ // $objects = array_merge($objects, $this->retinas);
1925
+ }
1926
+ if ($this->isScaled())
1927
+ {
1928
+ $objects[1] = $this->getOriginalFile();
1929
+ }
1930
+
1931
+ return $objects;
1932
+ }
1933
+
1934
  // Load items that might be processable but not in WP metadata or other meta's .
1935
  // used for IsProcessable ( check if we have something ) and handleOptimized ( to check against the optimized stuff )
1936
  private function loadLooseItems()
2226
  }
2227
 
2228
  $originalFile->image_meta->tsAdded = $tsAdded;
2229
+ if (isset($tsOptimized))
2230
+ {
2231
+ $originalFile->image_meta->tsOptimized = $tsOptimized;
2232
+ }
2233
  $originalFile->has_backup = $originalFile->hasBackup();
2234
 
2235
  $originalFile->image_meta->webp = $this->checkLegacyFileTypeFileName($originalFile, 'webp');
2254
  $retinasOpt = $data['retinasOpt'];
2255
  $retinas = $this->getRetinas();
2256
 
2257
+ if ( intval($retinasOpt) > 0 && is_array($retinas))
2258
+ {
2259
+ foreach($retinas as $index => $retinaObj) // Thumbnail Model
2260
+ {
2261
+ if ($retinaObj->hasDBRecord() === true)
2262
+ {
2263
+ continue;
2264
+ }
2265
+
2266
+ // Check if thumbnail ('parent') is Optimized, if so, then retina probably should be optimized as well.
2267
+ if ( (isset($this->thumbnails[$index]) &&
2268
+ is_object($this->thumbnails[$index]) &&
2269
+ $this->thumbnails[$index]->isOptimized) || $retinaObj->hasBackup() )
2270
+ {
2271
+ $retinaObj->image_meta->status = $status;
2272
+ $retinaObj->image_meta->compressionType = $type;
2273
+ if ($status == self::FILE_STATUS_SUCCESS)
2274
+ $retinaObj->image_meta->compressedSize = $retinaObj->getFileSize();
2275
+ else
2276
+ $retinaObj->image_meta->originalSize = $retinaObj->getFileSize();
2277
+ // $retinaObj->image_meta->improvement = -1; // n/a
2278
+ $retinaObj->image_meta->tsAdded = $tsAdded;
2279
+ if (isset($tsOptimized))
2280
+ {
2281
+ $retinaObj->image_meta->tsOptimized = $tsOptimized;
2282
+ }
2283
+ $retinaObj->image_meta->did_jpg2png = $did_jpg2png;
2284
+ if ($retinaObj->hasBackup())
2285
+ {
2286
+ $retinaObj->has_backup = true;
2287
+ if ($status == self::FILE_STATUS_SUCCESS)
2288
+ $retinaObj->image_meta->originalSize = $retinaObj->getBackupFile()->getFileSize();
2289
+ }
2290
+
2291
+ $retinaObj->recordChanged(true);
2292
+ $retinas[$index] = $retinaObj;
2293
+ $addedCounter++;
2294
+ }
2295
+ } // foreach
2296
+ $this->retinas = $retinas;
2297
+ } // is array.
2298
  if ($count !== $addedCounter)
2299
  {
2300
  Log::addWarning("Conversion: $count retinas expected in legacy, " . $addedCounter . 'found. This can be due to overlapping image sizes.');
2429
  'retinas' => $this->retinas,
2430
  'original_file' => $this->original_file,
2431
  'is_scaled' => $this->is_scaled,
2432
+ 'imageType' => $this->imageType,
2433
  );
2434
 
2435
  }
2436
 
2437
+ private function getAllThumbnails()
2438
+ {
2439
+
2440
+ }
2441
+
2442
  /** Adds Unlisted Image to the Media Library Item
2443
  * This function is called in IsProcessable
2444
  */
class/Model/Image/MediaLibraryThumbnailModel.php CHANGED
@@ -27,6 +27,7 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
27
  parent::__construct($path);
28
  $this->image_meta = new ImageThumbnailMeta();
29
  $this->id = $id;
 
30
  $this->size = $size;
31
  $this->setWebp();
32
  $this->setAvif();
@@ -60,6 +61,11 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
60
  $this->name = $name;
61
  }
62
 
 
 
 
 
 
63
  public function getRetina()
64
  {
65
  $filebase = $this->getFileBase();
@@ -68,6 +74,8 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
68
 
69
  $retina = new MediaLibraryThumbnailModel($filepath . $filebase . '@2x.' . $extension, $this->id, $this->size); // mind the dot in after 2x
70
  $retina->setName($this->size);
 
 
71
  $retina->is_retina = true;
72
 
73
  if ($retina->exists())
@@ -78,7 +86,7 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
78
 
79
 
80
 
81
- public function getOptimizeFileType($type = 'webp')
82
  {
83
  // pdf extension can be optimized, but don't come with these filetypes
84
  if ($this->getExtension() == 'pdf')
@@ -97,6 +105,7 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
97
  return false;
98
  }
99
 
 
100
  // @param FileDelete can be false. I.e. multilang duplicates might need removal of metadata, but not images.
101
  public function onDelete($fileDelete = true)
102
  {
@@ -123,34 +132,23 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
123
  return $this->image_meta;
124
  }
125
 
126
- public function getOptimizePaths()
127
- {
128
- if (! $this->isProcessable() )
129
- return array();
130
 
131
- return array($this->getFullPath());
132
- }
133
 
134
  // get_path param see MediaLibraryModel
135
- public function getOptimizeUrls($get_path = false)
 
136
  {
137
  if (! $this->isProcessable() )
138
- return array();
139
 
140
- if (true === $get_path)
141
- {
142
- $url = $this->getFullPath();
143
- }
144
- else {
145
- $url = $this->getURL();
146
- }
147
 
148
  if (! $url)
149
  {
150
- return array(); //nothing
151
  }
152
 
153
- return array($url);
154
  }
155
 
156
  public function getURL()
@@ -193,7 +191,6 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
193
 
194
  }
195
 
196
-
197
  return $this->fs()->checkURL($url);
198
  }
199
 
@@ -263,6 +260,15 @@ class MediaLibraryThumbnailModel extends \ShortPixel\Model\Image\ImageModel
263
  return false;
264
  }
265
 
 
 
 
 
 
 
 
 
 
266
  protected function excludeThumbnails()
267
  {
268
  return (! \wpSPIO()->settings()->processThumbnails);
27
  parent::__construct($path);
28
  $this->image_meta = new ImageThumbnailMeta();
29
  $this->id = $id;
30
+ $this->imageType = self::IMAGE_TYPE_THUMB;
31
  $this->size = $size;
32
  $this->setWebp();
33
  $this->setAvif();
61
  $this->name = $name;
62
  }
63
 
64
+ public function setImageType($type)
65
+ {
66
+ $this->imageType = $type;
67
+ }
68
+
69
  public function getRetina()
70
  {
71
  $filebase = $this->getFileBase();
74
 
75
  $retina = new MediaLibraryThumbnailModel($filepath . $filebase . '@2x.' . $extension, $this->id, $this->size); // mind the dot in after 2x
76
  $retina->setName($this->size);
77
+ $retina->setImageType(self::IMAGE_TYPE_RETINA);
78
+
79
  $retina->is_retina = true;
80
 
81
  if ($retina->exists())
86
 
87
 
88
 
89
+ public function isFileTypeNeeded($type = 'webp')
90
  {
91
  // pdf extension can be optimized, but don't come with these filetypes
92
  if ($this->getExtension() == 'pdf')
105
  return false;
106
  }
107
 
108
+
109
  // @param FileDelete can be false. I.e. multilang duplicates might need removal of metadata, but not images.
110
  public function onDelete($fileDelete = true)
111
  {
132
  return $this->image_meta;
133
  }
134
 
 
 
 
 
135
 
 
 
136
 
137
  // get_path param see MediaLibraryModel
138
+ // This should be unused at the moment!
139
+ public function getOptimizeUrls()
140
  {
141
  if (! $this->isProcessable() )
142
+ return false;
143
 
144
+ $url = $this->getURL();
 
 
 
 
 
 
145
 
146
  if (! $url)
147
  {
148
+ return false; //nothing
149
  }
150
 
151
+ return $url;
152
  }
153
 
154
  public function getURL()
191
 
192
  }
193
 
 
194
  return $this->fs()->checkURL($url);
195
  }
196
 
260
  return false;
261
  }
262
 
263
+ public function isProcessableFileType($type = 'webp')
264
+ {
265
+ // Prevent webp / avif processing for thumbnails if this is off. Exclude main file
266
+ if ($this->excludeThumbnails() === true && $this->is_main_file === false )
267
+ return false;
268
+
269
+ return parent::isProcessableFileType($type);
270
+ }
271
+
272
  protected function excludeThumbnails()
273
  {
274
  return (! \wpSPIO()->settings()->processThumbnails);
class/external/nextgen/nextGenController.php CHANGED
@@ -45,6 +45,7 @@ class NextGenController
45
  add_filter( 'ngg_manage_images_number_of_columns', array( $this->view, 'nggCountColumns' ) );
46
  add_filter( 'ngg_manage_images_column_7_header', array( $this->view, 'nggColumnHeader' ) );
47
  add_filter( 'ngg_manage_images_column_7_content', array( $this, 'loadNextGenItem' ), 10,2 );
 
48
 
49
  }
50
 
@@ -128,6 +129,19 @@ class NextGenController
128
  $viewController = new NextGenViewController();
129
  $viewController->loadItem($picture);
130
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  /** Enables nextGen, add galleries to custom folders
132
  * @param boolean $silent Throw a notice or not. This seems to be based if nextgen was already activated previously or not.
133
  */
@@ -188,12 +202,19 @@ class NextGenController
188
  }
189
 
190
  /* @return DirectoryModel */
191
- public function getGalleries()
192
  {
193
  global $wpdb;
194
  $fs = \wpSPIO()->filesystem();
195
  $homepath = $fs->getWPFileBase();
196
- $result = $wpdb->get_results("SELECT path FROM {$wpdb->prefix}ngg_gallery");
 
 
 
 
 
 
 
197
 
198
  $galleries = array();
199
 
@@ -269,7 +290,7 @@ class NextGenController
269
 
270
  if ($this->optimizeNextGen() === true) {
271
  $imageFsPath = $this->getImageAbspath($image);
272
- $otherMedia->addImage($imageFsPath);
273
  }
274
  }
275
 
45
  add_filter( 'ngg_manage_images_number_of_columns', array( $this->view, 'nggCountColumns' ) );
46
  add_filter( 'ngg_manage_images_column_7_header', array( $this->view, 'nggColumnHeader' ) );
47
  add_filter( 'ngg_manage_images_column_7_content', array( $this, 'loadNextGenItem' ), 10,2 );
48
+ add_filter('ngg_manage_gallery_fields', array($this, 'refreshFolderOnLoad'), 10, 2);
49
 
50
  }
51
 
129
  $viewController = new NextGenViewController();
130
  $viewController->loadItem($picture);
131
  }
132
+
133
+ public function refreshFolderOnLoad($array, $gallery)
134
+ {
135
+ $galleries = $this->getGalleries($gallery->gid);
136
+ if (isset($galleries[0]))
137
+ {
138
+ $otherMedia = OtherMediaController::getInstance();
139
+ $galleryFolder = $galleries[0];
140
+ $folder = $otherMedia->getFolderByPath($galleryFolder->getPath());
141
+ $folder->refreshFolder(true);
142
+ }
143
+ return $array;
144
+ }
145
  /** Enables nextGen, add galleries to custom folders
146
  * @param boolean $silent Throw a notice or not. This seems to be based if nextgen was already activated previously or not.
147
  */
202
  }
203
 
204
  /* @return DirectoryModel */
205
+ public function getGalleries($id = null)
206
  {
207
  global $wpdb;
208
  $fs = \wpSPIO()->filesystem();
209
  $homepath = $fs->getWPFileBase();
210
+
211
+ $sql = "SELECT path FROM {$wpdb->prefix}ngg_gallery";
212
+ if (! is_null($id))
213
+ {
214
+ $sql .= ' WHERE gid = %d';
215
+ $sql = $wpdb->prepare($sql, $id);
216
+ }
217
+ $result = $wpdb->get_results($sql);
218
 
219
  $galleries = array();
220
 
290
 
291
  if ($this->optimizeNextGen() === true) {
292
  $imageFsPath = $this->getImageAbspath($image);
293
+ $otherMedia->addImage($imageFsPath, array('is_nextgen' => true));
294
  }
295
  }
296
 
class/external/nextgen/nextGenViewController.php CHANGED
@@ -35,6 +35,8 @@ class NextGenViewController extends \ShortPixel\ViewController
35
 
36
  wp_enqueue_style('dashicons');
37
  $this->loadView('snippets/part-comparer');
 
 
38
  return __('ShortPixel Compression','shortpixel-image-optimiser');
39
  }
40
 
35
 
36
  wp_enqueue_style('dashicons');
37
  $this->loadView('snippets/part-comparer');
38
+
39
+
40
  return __('ShortPixel Compression','shortpixel-image-optimiser');
41
  }
42
 
class/shortpixel-png2jpg.php CHANGED
@@ -98,8 +98,6 @@ class ShortPixelPng2Jpg {
98
  return true;
99
  } // success
100
 
101
-
102
-
103
  } // result.
104
  else
105
  {
@@ -406,6 +404,8 @@ class ShortPixelPng2Jpg {
406
  $newFile = $params['file'];
407
  $attach_id = $imageObj->get('id');
408
 
 
 
409
  // This action prevents images from being regenerated on the thumbnail hook.
410
  do_action('shortpixel-thumbnails-before-regenerate', $attach_id );
411
 
@@ -447,6 +447,19 @@ class ShortPixelPng2Jpg {
447
  // wp_update_post(array('ID' => $attach_id, 'post_mime_type' => 'image/jpeg' ));
448
  $bool = wp_update_attachment_metadata($attach_id, $new_metadata);
449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  return $new_metadata;
451
 
452
  }
98
  return true;
99
  } // success
100
 
 
 
101
  } // result.
102
  else
103
  {
404
  $newFile = $params['file'];
405
  $attach_id = $imageObj->get('id');
406
 
407
+ $WPMLduplicates = $imageObj->getWPMLDuplicates();
408
+
409
  // This action prevents images from being regenerated on the thumbnail hook.
410
  do_action('shortpixel-thumbnails-before-regenerate', $attach_id );
411
 
447
  // wp_update_post(array('ID' => $attach_id, 'post_mime_type' => 'image/jpeg' ));
448
  $bool = wp_update_attachment_metadata($attach_id, $new_metadata);
449
 
450
+
451
+ if (is_array($WPMLduplicates) && count($WPMLduplicates) > 0)
452
+ {
453
+ foreach ($WPMLduplicates as $duplicate_id)
454
+ {
455
+ update_attached_file($duplicate_id, $newFile->getFullPath() );
456
+ wp_update_attachment_metadata($duplicate_id, $new_metadata);
457
+
458
+ $post_ar["ID"] = $duplicate_id;
459
+ wp_update_post($post_ar);
460
+ }
461
+ }
462
+
463
  return $new_metadata;
464
 
465
  }
class/view/settings/part-advanced.php CHANGED
@@ -258,7 +258,7 @@ use \ShortPixel\Helper\UiHelper as UiHelper;
258
 
259
  <?php if(!$this->is_gd_installed):
260
  ?>
261
- <div style="color:red;"><?php esc_html__('You need PHP GD with support for JPEG and PNG files for this feature. Please ask your hosting provider to install it.','shortpixel-image-optimiser'); ?>
262
  </div>
263
  <?php endif; ?>
264
 
@@ -410,7 +410,7 @@ use \ShortPixel\Helper\UiHelper as UiHelper;
410
  $folder_id = $dirObj->get('id');
411
 
412
 
413
- $type_display = ($dirObj->get('is_nextgen') ) ? __('Nextgen', 'shortpixel-image-optimiser') . "<br>" : "";
414
  $stat = $dirObj->getStats();
415
 
416
  $fullstatus = esc_html__("Optimized",'shortpixel-image-optimiser') . ": " . $stat->Optimized . ", "
258
 
259
  <?php if(!$this->is_gd_installed):
260
  ?>
261
+ <div style="color:red;"><?php esc_html_e('You need PHP GD with support for JPEG and PNG files for this feature. Please ask your hosting provider to install it.','shortpixel-image-optimiser'); ?>
262
  </div>
263
  <?php endif; ?>
264
 
410
  $folder_id = $dirObj->get('id');
411
 
412
 
413
+ $type_display = ($dirObj->get('is_nextgen') ) ? __('Nextgen', 'shortpixel-image-optimiser') . ":" : "";
414
  $stat = $dirObj->getStats();
415
 
416
  $fullstatus = esc_html__("Optimized",'shortpixel-image-optimiser') . ": " . $stat->Optimized . ", "
class/view/settings/part-general.php CHANGED
@@ -149,6 +149,28 @@ namespace ShortPixel;
149
  </td>
150
  </tr>
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  <tr>
153
  <th scope="row"><?php esc_html_e('Backup','shortpixel-image-optimiser');?></th>
154
  <td>
149
  </td>
150
  </tr>
151
 
152
+ <tr>
153
+ <th scope="row"><?php esc_html_e('Enable SmartCrop:','shortpixel-image-optimiser');?></th>
154
+ <td>
155
+
156
+ <div class='switch_button'>
157
+ <div class="spio-inline-help"><span class="dashicons dashicons-editor-help" title="Click for more info" data-link="https://shortpixel.com/knowledge-base/article/182-what-is-smart-cropping"></span></div>
158
+ <label>
159
+ <input type="checkbox" class="switch" name="useSmartcrop" value="1" <?php checked($view->data->useSmartcrop, '1');?>>
160
+ <div class="the_switch">&nbsp; </div>
161
+ <?php printf(esc_html__('Enable %s Smartcropping %s option.','shortpixel-image-optimiser'), '<strong>', '</strong>'); ?>
162
+ </label>
163
+ </div>
164
+
165
+ <p class="settings-info">
166
+ <?php printf(esc_html__('Generate subject-centered thumbnails using ShortPixel\'s AI engine (%sexample%s). The new thumbnails look sharper (and can be slightly bigger) than the ones created by WordPress. Ideal for e-commerce websites and blogs where the images sell the products/content.','shortpixel-image-optimiser'), '<a href="https://shortpixel.com/knowledge-base/article/182-what-is-smart-cropping" target="_blank">', '</a>'); ?>
167
+ </p>
168
+
169
+ </td>
170
+ </tr>
171
+
172
+
173
+
174
  <tr>
175
  <th scope="row"><?php esc_html_e('Backup','shortpixel-image-optimiser');?></th>
176
  <td>
class/view/settings/part-wso.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ $bool = apply_filters('shortpixel/settings/no_banner', true);
5
+ if (! $bool )
6
+ return;
7
+
8
+ if ( defined('SHORTPIXEL_NO_BANNER') && SHORTPIXEL_NO_BANNER == true)
9
+ return;
10
+
11
+ ?>
12
+
13
+ <section class='wso banner'>
14
+ <span class="image">
15
+ <img src="<?php echo \wpSPIO()->plugin_url() ?>res/img/robo-winky.png" />
16
+ </span>
17
+ <span class="line"><h3>
18
+ <?php printf(__('ARE YOU CONCERNED WITH YOUR %s %s SITE SPEED? %s', 'shortpixel-image-optimiser'),'<br>', '<span class="red">','</span>'); ?>
19
+ </h3>
20
+ </span>
21
+ <span class="line"><h3>
22
+ <?php printf(__('ALLOW ShortPixel SPECIALISTS TO %s FIND THE SOLUTION FOR YOU.', 'shortpixel-image-optimiser'), '<br>'); ?>
23
+ </h3>
24
+ </span>
25
+ <span class="button-wrap">
26
+ <a href="https://wso.shortpixel.com/?utm_source=SPIO" target="_blank" class='button' ><?php _e('Find out more', 'shortpixel-image-optimiser'); ?></a>
27
+ </span>
28
+ </section>
class/view/shortpixel-feedback.php CHANGED
@@ -25,6 +25,7 @@ class ShortPixelFeedback {
25
 
26
  // Deactivation
27
  add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'filterActionLinks') );
 
28
  add_action( 'admin_footer-plugins.php', array( $this, 'goodbyeAjax') );
29
  add_action( 'wp_ajax_shortpixel_deactivate_plugin', array( $this, 'deactivatePluginCallback') );
30
 
@@ -37,6 +38,7 @@ class ShortPixelFeedback {
37
  public function filterActionLinks( $links ) {
38
 
39
  if( isset( $links['deactivate'] ) ) {
 
40
  $deactivation_link = $links['deactivate'];
41
  // Insert an onClick action to allow form before deactivating
42
  $deactivation_link = str_replace( '<a ',
25
 
26
  // Deactivation
27
  add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'filterActionLinks') );
28
+ add_filter('network_admin_plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'filterActionLinks'));
29
  add_action( 'admin_footer-plugins.php', array( $this, 'goodbyeAjax') );
30
  add_action( 'wp_ajax_shortpixel_deactivate_plugin', array( $this, 'deactivatePluginCallback') );
31
 
38
  public function filterActionLinks( $links ) {
39
 
40
  if( isset( $links['deactivate'] ) ) {
41
+
42
  $deactivation_link = $links['deactivate'];
43
  // Insert an onClick action to allow form before deactivating
44
  $deactivation_link = str_replace( '<a ',
class/view/view-settings.php CHANGED
@@ -78,6 +78,7 @@ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
78
  ?>
79
 
80
  </article>
 
81
 
82
  <?php $this->loadView('snippets/part-inline-help'); ?>
83
  <?php $this->loadView('snippets/part-inline-modal'); ?>
78
  ?>
79
 
80
  </article>
81
+ <?php $this->loadView('settings/part-wso'); ?>
82
 
83
  <?php $this->loadView('snippets/part-inline-help'); ?>
84
  <?php $this->loadView('snippets/part-inline-modal'); ?>
class/wp-shortpixel-settings.php CHANGED
@@ -26,6 +26,7 @@ class WPShortPixelSettings extends \ShortPixel\Model {
26
  'verifiedKey' => array('key' => 'wp-short-pixel-verifiedKey', 'default' => false, 'group' => 'options'),
27
  'compressionType' => array('key' => 'wp-short-pixel-compression', 'default' => 1, 'group' => 'options'),
28
  'processThumbnails' => array('key' => 'wp-short-process_thumbnails', 'default' => 1, 'group' => 'options'),
 
29
  'keepExif' => array('key' => 'wp-short-pixel-keep-exif', 'default' => 0, 'group' => 'options'),
30
  'CMYKtoRGBconversion' => array('key' => 'wp-short-pixel_cmyk2rgb', 'default' => 1, 'group' => 'options'),
31
  'createWebp' => array('key' => 'wp-short-create-webp', 'default' => null, 'group' => 'options'),
@@ -121,6 +122,7 @@ class WPShortPixelSettings extends \ShortPixel\Model {
121
  'resizeWidth' => array('s' => 'int'), // int
122
  'resizeHeight' => array('s' => 'int'), // int
123
  'processThumbnails' => array('s' => 'boolean'), // checkbox
 
124
  'backupImages' => array('s' => 'boolean'), // checkbox
125
  'keepExif' => array('s' => 'int'), // checkbox
126
  'resizeImages' => array('s' => 'boolean'),
@@ -193,8 +195,10 @@ class WPShortPixelSettings extends \ShortPixel\Model {
193
  delete_option( 'wp-short-pixel-current-total-files');
194
  delete_option('wp-short-pixel-remove-settings-on-delete-plugin');
195
 
196
- delete_option(self::$_optionsMap['removeSettingsOnDeletePlugin']['key']);
197
-
 
 
198
  // Dismissed now via Notices Controller.
199
  /* $dismissed = get_option('wp-short-pixel-dismissed-notices', array());
200
  if(isset($dismissed['compat'])) {
26
  'verifiedKey' => array('key' => 'wp-short-pixel-verifiedKey', 'default' => false, 'group' => 'options'),
27
  'compressionType' => array('key' => 'wp-short-pixel-compression', 'default' => 1, 'group' => 'options'),
28
  'processThumbnails' => array('key' => 'wp-short-process_thumbnails', 'default' => 1, 'group' => 'options'),
29
+ 'useSmartcrop' => array('key' => 'wpspio-usesmartcrop', 'default' => 0, 'group' => 'options'),
30
  'keepExif' => array('key' => 'wp-short-pixel-keep-exif', 'default' => 0, 'group' => 'options'),
31
  'CMYKtoRGBconversion' => array('key' => 'wp-short-pixel_cmyk2rgb', 'default' => 1, 'group' => 'options'),
32
  'createWebp' => array('key' => 'wp-short-create-webp', 'default' => null, 'group' => 'options'),
122
  'resizeWidth' => array('s' => 'int'), // int
123
  'resizeHeight' => array('s' => 'int'), // int
124
  'processThumbnails' => array('s' => 'boolean'), // checkbox
125
+ 'useSmartcrop' => array('s' => 'boolean'),
126
  'backupImages' => array('s' => 'boolean'), // checkbox
127
  'keepExif' => array('s' => 'int'), // checkbox
128
  'resizeImages' => array('s' => 'boolean'),
195
  delete_option( 'wp-short-pixel-current-total-files');
196
  delete_option('wp-short-pixel-remove-settings-on-delete-plugin');
197
 
198
+ if (isset(self::$_optionsMap['removeSettingsOnDeletePlugin']) && isset(self::$_optionsMap['removeSettingsOnDeletePlugin']['key']))
199
+ {
200
+ delete_option(self::$_optionsMap['removeSettingsOnDeletePlugin']['key']);
201
+ }
202
  // Dismissed now via Notices Controller.
203
  /* $dismissed = get_option('wp-short-pixel-dismissed-notices', array());
204
  if(isset($dismissed['compat'])) {
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: ShortPixel
3
  Tags: convert webp, optimize images, image optimization, resize, compressor, image, avif, compression, optimize, image optimiser, image compression, compress pdf, compress jpg, compress png, performance, photography, smush, scale, pictures
4
  Requires at least: 4.8.0
5
- Tested up to: 6.0
6
  Requires PHP: 5.6
7
- Stable tag: 5.0.9
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -28,6 +28,12 @@ Optimized images mean better user experience, better PageSpeed Insights or GTmet
28
 
29
  Make an instant <a href="https://shortpixel.com/image-compression-test" target="_blank">image compression test</a> of your site or <a href="https://shortpixel.com/online-image-compression" target="_blank">compress some images</a> to test our optimization algorithms.
30
 
 
 
 
 
 
 
31
  **Why is ShortPixel the best choice when it comes to image optimization or PDF compression?**
32
 
33
  * popular plugin with over 300,000 active installations - according to WordPress
@@ -35,6 +41,7 @@ Make an instant <a href="https://shortpixel.com/image-compression-test" target="
35
  * option to convert any JPEG, PNG or GIF (even animated ones!) to **WebP** and **AVIF** for more Google love. <a href="https://shortpixel.com/blog/how-webp-images-can-speed-up-your-site/" target="_blank">How to enable WebP?</a>. <a href="https://shortpixel.com/blog/what-is-avif-and-why-is-it-good/" target="_blank">What is AVIF and why is it good?</a>.
36
  * **New!:** full <a href="https://shortpixel.com/knowledge-base/article/537-wp-cli-support-for-shortpixel-image-optimizer" target="_blank">WP-CLI support</a> for background processing, useful especially for websites with a very large Media Library
37
  * **New!:** Easily add <a href="https://shortpixel.com/knowledge-base/article/543-how-to-schedule-a-cron-event-to-run-shortpixel-image-optimizer" target="_blank">recurrent cron jobs</a> for background optimization. Useful if you have users uploading images via the front end of your website
 
38
  * option to automatically convert PNG to JPG if that will result in smaller images. Ideal for large images in PNG format
39
  * option to include the next generation images (WebP and AVIF) into the front-end pages by using the `<picture>` tag instead of `<img>`, independent from generating them through the plugin
40
  * compatible with WP Retina 2x - all **retina images** are automatically compressed. <a href="https://shortpixel.com/blog/how-to-use-optimized-retina-images-on-your-wordpress-site-for-best-user-experience-on-apple-devices/" target="_blank">How to benefit from Retina displays?</a>
@@ -341,6 +348,23 @@ Add HTTP basic authentication credentials by defining these constants in wp-conf
341
 
342
  == Changelog ==
343
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  = 5.0.9 =
345
  Release date August 29th, 2022
346
  * Fix: one of the processing queues was flooded in certain situations, on sites with many concurrent editors;
2
  Contributors: ShortPixel
3
  Tags: convert webp, optimize images, image optimization, resize, compressor, image, avif, compression, optimize, image optimiser, image compression, compress pdf, compress jpg, compress png, performance, photography, smush, scale, pictures
4
  Requires at least: 4.8.0
5
+ Tested up to: 6.1
6
  Requires PHP: 5.6
7
+ Stable tag: 5.1.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
28
 
29
  Make an instant <a href="https://shortpixel.com/image-compression-test" target="_blank">image compression test</a> of your site or <a href="https://shortpixel.com/online-image-compression" target="_blank">compress some images</a> to test our optimization algorithms.
30
 
31
+ **New! Smart Cropping**
32
+
33
+ With this new feature, all thumbnails used on your website are not only optimized, but also regenerated to fully display the subject of the image.
34
+ All thumbnails fully display the subject and look consistent as well. The new thumbnails look sharper (and can be slightly bigger) than the ones created by WordPress. Ideal for e-commerce and other websites where the images are paramount to selling the products or to support the content. See <a href="https://shortpixel.com/knowledge-base/article/182-what-is-smart-cropping" target="_blank">an example</a>.
35
+
36
+
37
  **Why is ShortPixel the best choice when it comes to image optimization or PDF compression?**
38
 
39
  * popular plugin with over 300,000 active installations - according to WordPress
41
  * option to convert any JPEG, PNG or GIF (even animated ones!) to **WebP** and **AVIF** for more Google love. <a href="https://shortpixel.com/blog/how-webp-images-can-speed-up-your-site/" target="_blank">How to enable WebP?</a>. <a href="https://shortpixel.com/blog/what-is-avif-and-why-is-it-good/" target="_blank">What is AVIF and why is it good?</a>.
42
  * **New!:** full <a href="https://shortpixel.com/knowledge-base/article/537-wp-cli-support-for-shortpixel-image-optimizer" target="_blank">WP-CLI support</a> for background processing, useful especially for websites with a very large Media Library
43
  * **New!:** Easily add <a href="https://shortpixel.com/knowledge-base/article/543-how-to-schedule-a-cron-event-to-run-shortpixel-image-optimizer" target="_blank">recurrent cron jobs</a> for background optimization. Useful if you have users uploading images via the front end of your website
44
+ * **New!:** Smart Cropping. Generate subject-centered thumbnails using ShortPixel's AI engine
45
  * option to automatically convert PNG to JPG if that will result in smaller images. Ideal for large images in PNG format
46
  * option to include the next generation images (WebP and AVIF) into the front-end pages by using the `<picture>` tag instead of `<img>`, independent from generating them through the plugin
47
  * compatible with WP Retina 2x - all **retina images** are automatically compressed. <a href="https://shortpixel.com/blog/how-to-use-optimized-retina-images-on-your-wordpress-site-for-best-user-experience-on-apple-devices/" target="_blank">How to benefit from Retina displays?</a>
348
 
349
  == Changelog ==
350
 
351
+ = 5.1.0 =
352
+ Release date October 20th, 2022
353
+ * New: added SmartCropping, especially useful for eCommerce sites;
354
+ * New: if the WebP/AVIF files are larger than the JPG/PNG/GIF version, they are no longer generated to ensure that the smallest file is always delivered;
355
+ * Fix: various DB-related settings were adjusted for files with very long names and to keep AVIF/WebP optimization data correct;
356
+ * Fix: bulk processing history was lost when deleting the plugin;
357
+ * Fix: the file name in the bulk preview was added back;
358
+ * Fix: various situations and edge cases with WPML are now fixed;
359
+ * Fix: when a Custom Media item was excluded, there was no clear message next to it;
360
+ * Fix: added a check to prevent re-optimization when using bulk actions in the Media Library;
361
+ * Fix: the deactivation pop-up is also displayed in a multisite environment;
362
+ * Fix: minor wording and CSS fixes in the plugin settings and notifications;
363
+ * Fix: if there are still images to optimize and/or generate, all of them are counted and displayed correctly in the Media Library;
364
+ * Tweak: added check of necessary GD library functions to use PNG to JPG conversion;
365
+ * Compat: in some very special cases an error was triggered when the YITH Watermark Premium plugin was enabled;
366
+ * Language: 20 new strings added, 2 updated, 1 fuzzed, and 2 deprecated.
367
+
368
  = 5.0.9 =
369
  Release date August 29th, 2022
370
  * Fix: one of the processing queues was flooded in certain situations, on sites with many concurrent editors;
res/css/shortpixel-bulk.css CHANGED
@@ -820,19 +820,21 @@
820
  }
821
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line, .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line {
822
  display: flex;
823
- width: 90%;
824
  justify-content: space-between;
825
  align-items: center;
826
- margin: 15px auto;
827
  }
828
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line [data-result=filename], .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line [data-result=filename] {
829
  overflow: hidden;
830
  font-weight: 700;
 
831
  }
832
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line .opt-circle-image, .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line .opt-circle-image {
833
  width: 55px;
834
  height: 55px;
835
  float: right;
 
 
836
  }
837
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line .opt-circle-image .trail, .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line .opt-circle-image .trail {
838
  stroke: #ddd;
820
  }
821
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line, .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line {
822
  display: flex;
823
+ width: 100%;
824
  justify-content: space-between;
825
  align-items: center;
 
826
  }
827
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line [data-result=filename], .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line [data-result=filename] {
828
  overflow: hidden;
829
  font-weight: 700;
830
+ margin-top: -12px;
831
  }
832
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line .opt-circle-image, .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line .opt-circle-image {
833
  width: 55px;
834
  height: 55px;
835
  float: right;
836
+ margin-top: 12px;
837
+ margin-right: 5%;
838
  }
839
  .shortpixel-bulk-wrapper section.panel.process .image-preview-section .image-preview-line .opt-circle-image .trail, .shortpixel-bulk-wrapper section.panel.finished .image-preview-section .image-preview-line .opt-circle-image .trail {
840
  stroke: #ddd;
res/css/shortpixel-bulk.css.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["../scss/shortpixel-bulk.scss","../scss/elements/_colors.scss","../scss/elements/_animation.scss"],"names":[],"mappings":"AAAA;AAKE;AAqWF;AAoLI;AA2GF;;AAnoBE;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;;AAGN;EACE;;AAGF;EAEG;EAGA;;AAGL;EACC;EACA;EACA;EACA;EAEA;EACA;EACA,YC3CiB;ED4CjB,OC/CU;EDgDV;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEC,YCzDS;ED0DT;EACA;;AAGD;EACC,cC/DS;EDgET,YChES;;ADkEV;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAID;EAEE,YCpFU;EDqFV;EACA;EACA;EACA;;AAGJ;EACE;EAED;EACA;;AACA;EAEE;;AAIH;EACG;;AAGD;EACE;;AAGJ;EACG;EACA;;AAGJ;EACG;IACG;;EAEH;IACG;;;AAIN;EAEE;EACA;;AAID;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACC;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEH;EAEE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;EACA;EACA;;AACA;EACC;;AACA;EAAW;;AACR;EAAS;EAA+B;;AAE3C;EAAQ;;AACR;EACE;EACD;;AAGF;EAAI;EAAuB;;AAK5B;EAEI;EACA;EACA;EACA;;AAGA;EAGG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACN;EACA;;AAGG;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EAAU;;AACV;EAEN;EACA;;AAMH;EAEG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEJ;;AAGI;EAEI;EACA;EACA;EACA;;AAGJ;EAEG;EAED;EACA;;AAKE;EAAK;;AACL;EACE;EACA;;AACA;EAAS;;AAGf;EACE;EACA;EAEA;EACL;EACK;EACA;EACA;EACA;EACA;EACL;EACA;EAEK;EACL;EACA;EACA;;AACA;EAAS;;AAMT;EAGE;EAGH;EACG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EAEE;;AACA;EAEG;EACA;EACA;;AAGH;EAAiB;;AAIvB;EAEE;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AACA;EACA;EACA;EACA;;AAEA;EAEC;EACA;EACC;;AASA;EAAa;;AAIb;EAEK;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;EAAS;;AAGX;EAEG;EAEA;EACA;EACA;EACA;EACP;EAEA;EACA;;AAEO;EAGP,YCnZQ;EDqZP;EACA;EAES;;AAKH;EACE;EACA;;AAEF;EAEN;;AAMF;EAEG;EACA;EACA;;AACA;EAAK;EAAwB;;AAC7B;EACE;;AACA;EAAS;EAAqB;;AAI9B;EACE;EACA;;AAKF;EACE;EACA;EAGA;EACA;;AAHA;EAAgB;;AAChB;EAAgB;;AAGhB;EACG;EACA;EACA;;AAYb;EAEI;;AACA;EAEE;EACA;EACA;EACA;;AAEA;EAEG;EACA;EACA;EACA;EACA;;AAGH;EAEE;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACN;;AAEI;EAEE;EACA;EACA;EACA;EACN;;AAoBG;EATH;;AAUG;EAAe;;AACf;EAAQ;;AAEX;EAEG;EACJ;EACI;;AAKA;EACE;EACL;EACA;;AAKE;EAEG;EACL;;AAKG;EAEG;EACA;EACA;EACA;EACA;EACA;;AACN;EACC;;AACA;EAAI;;AAEC;EACE;;AAEF;EE9kBV;EACI;EACA;EACA;EACA;;AAEL;EACC;IAAM;IAA2B;;;AAGlC;EACC;IAAM;IAA8B;;;AAGrC;EACC;IAAM;IAA+B;;;AAGtC;EACC;IAAM;IAAmC;;;AAG1C;EACC;IAAM;IAAgC;;;AF6jBrC;EAEE;EACA;;AACA;EACE;EACD;;AAED;EAAI;;AAWA;EACG;;AAEH;EACG;EACA;EACA;EACA;;AAEH;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAMZ;EAEE;;AAYI;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EA1HH;;AA2HG;EAAe;;AACf;EAAQ;;AAEX;EAEI;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAKD;EACI;EACA;EACA;;AACA;EAEG;EACA;;AAIN;EAEG;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAEG;EACA;EACA;;AACA;EAEG;;AAIR;EAEG;EACA;EACA;EAEA;;AAOd;EAEE;EACA;EACA;EACA;;AAEA;EAAU;;AAGZ;EACI;EAEA;EAEL;;AACK;EAAM;;AACN;EACG;EACA;EACA;EACN;;AACM;EAAgB;EAAkB;;AACxC;EAAkB;;AAClB;EAAkB;;AAClB;EAAU;EAAc;;AAGrB;EACE;EACA;;AAIN;EAEG;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;EACA;;AACA;EAAO;;AAET;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEI;EACA;EACA;;AASD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EA/QH;;AAgRG;EAAe;;AACf;EAAQ;;AAGX;EAEE;EACA;EACA;EACA;EACA;EACJ;EACA;;AAEI;EACE;EACA;EACL;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGG;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE,QC/0BD;EDg1BC;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAEE;EACA;EACA;;AACA;EAAQ;;AACR;EACI;EACA;EACN;;AAEE;EACG;EACA;;AAEH;EAEE;EACA;EACA;EACA;EACA;EACL;;AAGK;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIN;EAEE;;AAMJ;EACC;;AAIC;EACG;EACA;EACA;EACA;EACJ;;AAEA;EACG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAIC;EAEG;EACA;EACA;EACA;EACA;;AAGN;EAEC;EACA;;AAGK;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAIH;EACA;EAGK;;AAEL;EAIC;EACA;EACA;;AAID;EAEC;EACC;EACD;EACA;EACA;EACA;EACA;;AAED;EAEE;;AAIF;EAGC;;AASI;EAEG;EACA;EACA;EACA;;AAEA;EACE;EACR;EACA;EASA;EACA;;AATA;EAEC;;AAED;EAEC;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGK;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEG;;AAEH;EAEE;;AAKP;EACI;EACA;;AAYD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EApjBH;;AAqjBG;EAAe;;AACf;EAAQ;;AAGX;EACI;;AAEF;EAEI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAOV;EAEG;EACA;EACA;EACA;EACA;;AAKL;EAEE;EAEA;;AACA;EAAK;EACH;EACA;;AAEA;EAEE;EACA;;AAIJ;EAAW;;AAEX;EAEC;EACA;;AAEK;EACG;;AAEH;EACG;EACA;EACA;EACA;EACN;;AAEG;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAGX;EAEE;EACA","file":"shortpixel-bulk.css"}
1
+ {"version":3,"sourceRoot":"","sources":["../scss/shortpixel-bulk.scss","../scss/elements/_colors.scss","../scss/elements/_animation.scss"],"names":[],"mappings":"AAAA;AAKE;AAqWF;AAoLI;AA2GF;;AAnoBE;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;;AAGN;EACE;;AAGF;EAEG;EAGA;;AAGL;EACC;EACA;EACA;EACA;EAEA;EACA;EACA,YC3CiB;ED4CjB,OC/CU;EDgDV;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEC,YCzDS;ED0DT;EACA;;AAGD;EACC,cC/DS;EDgET,YChES;;ADkEV;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAID;EAEE,YCpFU;EDqFV;EACA;EACA;EACA;;AAGJ;EACE;EAED;EACA;;AACA;EAEE;;AAIH;EACG;;AAGD;EACE;;AAGJ;EACG;EACA;;AAGJ;EACG;IACG;;EAEH;IACG;;;AAIN;EAEE;EACA;;AAID;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACC;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEH;EAEE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;EACA;EACA;;AACA;EACC;;AACA;EAAW;;AACR;EAAS;EAA+B;;AAE3C;EAAQ;;AACR;EACE;EACD;;AAGF;EAAI;EAAuB;;AAK5B;EAEI;EACA;EACA;EACA;;AAGA;EAGG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACN;EACA;;AAGG;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EAAU;;AACV;EAEN;EACA;;AAMH;EAEG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEJ;;AAGI;EAEI;EACA;EACA;EACA;;AAGJ;EAEG;EAED;EACA;;AAKE;EAAK;;AACL;EACE;EACA;;AACA;EAAS;;AAGf;EACE;EACA;EAEA;EACL;EACK;EACA;EACA;EACA;EACA;EACL;EACA;EAEK;EACL;EACA;EACA;;AACA;EAAS;;AAMT;EAGE;EAGH;EACG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EAEE;;AACA;EAEG;EACA;EACA;;AAGH;EAAiB;;AAIvB;EAEE;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AACA;EACA;EACA;EACA;;AAEA;EAEC;EACA;EACC;;AASA;EAAa;;AAIb;EAEK;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;EAAS;;AAGX;EAEG;EAEA;EACA;EACA;EACA;EACP;EAEA;EACA;;AAEO;EAGP,YCnZQ;EDqZP;EACA;EAES;;AAKH;EACE;EACA;;AAEF;EAEN;;AAMF;EAEG;EACA;EACA;;AACA;EAAK;EAAwB;;AAC7B;EACE;;AACA;EAAS;EAAqB;;AAI9B;EACE;EACA;;AAKF;EACE;EACA;EAGA;EACA;;AAHA;EAAgB;;AAChB;EAAgB;;AAGhB;EACG;EACA;EACA;;AAYb;EAEI;;AACA;EAEE;EACA;EACA;EACA;;AAEA;EAEG;EACA;EACA;EACA;EACA;;AAGH;EAEE;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACN;;AAEI;EAEE;EACA;EACA;EACA;EACN;;AAoBG;EATH;;AAUG;EAAe;;AACf;EAAQ;;AAEX;EAEG;EACJ;EACI;;AAKA;EACE;EACL;EACA;;AAKE;EAEG;EACL;;AAKG;EAEG;EACA;EACA;EACA;EACA;EACA;;AACN;EACC;;AACA;EAAI;;AAEC;EACE;;AAEF;EE9kBV;EACI;EACA;EACA;EACA;;AAEL;EACC;IAAM;IAA2B;;;AAGlC;EACC;IAAM;IAA8B;;;AAGrC;EACC;IAAM;IAA+B;;;AAGtC;EACC;IAAM;IAAmC;;;AAG1C;EACC;IAAM;IAAgC;;;AF6jBrC;EAEE;EACA;;AACA;EACE;EACD;;AAED;EAAI;;AAWA;EACG;;AAEH;EACG;EACA;EACA;EACA;;AAEH;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAMZ;EAEE;;AAYI;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EA1HH;;AA2HG;EAAe;;AACf;EAAQ;;AAEX;EAEI;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAKD;EACI;EACA;EACA;;AACA;EAEG;EACA;;AAIN;EAEG;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAEG;EACA;EACA;;AACA;EAEG;;AAIR;EAEG;EACA;EACA;EAEA;;AAOd;EAEE;EACA;EACA;EACA;;AAEA;EAAU;;AAGZ;EACI;EAEA;EAEL;;AACK;EAAM;;AACN;EACG;EACA;EACA;EACN;;AACM;EAAgB;EAAkB;;AACxC;EAAkB;;AAClB;EAAkB;;AAClB;EAAU;EAAc;;AAGrB;EACE;EACA;;AAIN;EAEG;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;EACA;;AACA;EAAO;;AAET;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEI;EACA;EACA;;AASD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EA/QH;;AAgRG;EAAe;;AACf;EAAQ;;AAGX;EAEE;EACA;EACA;EACA;EACA;EACJ;EACA;;AAEI;EACE;EACA;EACL;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGG;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE,QC/0BD;EDg1BC;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAEE;EACA;EACA;;AACA;EAAQ;;AACR;EACI;EACA;EACN;;AAEE;EACG;EACA;;AAEH;EAEE;EACA;EACA;EACA;EACA;EACL;;AAGK;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIN;EAEE;;AAMJ;EACC;;AAIC;EACG;EACA;EACA;EACA;EACJ;;AAEA;EACG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAIC;EAEG;EACA;EACA;EACA;;AAIN;EAEC;EACA;EACA;;AAGK;EACI;EACA;EACA;EACP;EACA;;AAEO;EACE;;AAEF;EACE;EACA;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAIH;EACA;EAGK;;AAEL;EAIC;EACA;EACA;;AAID;EAEC;EACC;EACD;EACA;EACA;EACA;EACA;;AAED;EAEE;;AAIF;EAGC;;AASI;EAEG;EACA;EACA;EACA;;AAEA;EACE;EACR;EACA;EASA;EACA;;AATA;EAEC;;AAED;EAEC;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGK;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEG;;AAEH;EAEE;;AAKP;EACI;EACA;;AAYD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAvjBH;;AAwjBG;EAAe;;AACf;EAAQ;;AAGX;EACI;;AAEF;EAEI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAOV;EAEG;EACA;EACA;EACA;EACA;;AAKL;EAEE;EAEA;;AACA;EAAK;EACH;EACA;;AAEA;EAEE;EACA;;AAIJ;EAAW;;AAEX;EAEC;EACA;;AAEK;EACG;;AAEH;EACG;EACA;EACA;EACA;EACN;;AAEG;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAGX;EAEE;EACA","file":"shortpixel-bulk.css"}
res/js/screens/screen-custom.js CHANGED
@@ -208,7 +208,7 @@ var ShortPixelScreen = function (MainScreen, processor)
208
  element.outerHTML = data.custom.itemView;
209
 
210
  }
211
- return true;
212
  }
213
 
214
  this.RestoreItem = function(id)
208
  element.outerHTML = data.custom.itemView;
209
 
210
  }
211
+ return false;
212
  }
213
 
214
  this.RestoreItem = function(id)
res/scss/shortpixel-bulk.scss CHANGED
@@ -946,22 +946,25 @@
946
  .image-preview-line
947
  {
948
  display: flex;
949
- width: 90%;
950
  justify-content: space-between;
951
  align-items: center;
952
- margin: 15px auto;
953
  // max-width: 300px;
954
 
955
  [data-result="filename"]
956
  {
957
  overflow: hidden;
958
  font-weight: 700;
 
959
  }
960
 
961
  .opt-circle-image {
962
  width: 55px;
963
  height: 55px;
964
  float: right;
 
 
965
  //margin-right: 35px;
966
  .trail {
967
  stroke: #ddd;
946
  .image-preview-line
947
  {
948
  display: flex;
949
+ width: 100%;
950
  justify-content: space-between;
951
  align-items: center;
952
+ // margin: 15px auto;
953
  // max-width: 300px;
954
 
955
  [data-result="filename"]
956
  {
957
  overflow: hidden;
958
  font-weight: 700;
959
+ margin-top: -12px;
960
  }
961
 
962
  .opt-circle-image {
963
  width: 55px;
964
  height: 55px;
965
  float: right;
966
+ margin-top: 12px;
967
+ margin-right: 5%;
968
  //margin-right: 35px;
969
  .trail {
970
  stroke: #ddd;
wp-shortpixel.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: ShortPixel Image Optimizer
4
  * Plugin URI: https://shortpixel.com/
5
  * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="/wp-admin/options-general.php?page=wp-shortpixel-settings" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
- * Version: 5.0.9
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * GitHub Plugin URI: https://github.com/short-pixel-optimizer/shortpixel-image-optimiser
@@ -31,7 +31,7 @@ if (! defined('SHORTPIXEL_RESET_ON_ACTIVATE'))
31
  define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
32
  define('SHORTPIXEL_PLUGIN_DIR', __DIR__);
33
 
34
- define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "5.0.9");
35
 
36
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
37
  define('SHORTPIXEL_MAX_FAIL_RETRIES', 3);
3
  * Plugin Name: ShortPixel Image Optimizer
4
  * Plugin URI: https://shortpixel.com/
5
  * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="/wp-admin/options-general.php?page=wp-shortpixel-settings" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
+ * Version: 5.1.0
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * GitHub Plugin URI: https://github.com/short-pixel-optimizer/shortpixel-image-optimiser
31
  define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
32
  define('SHORTPIXEL_PLUGIN_DIR', __DIR__);
33
 
34
+ define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "5.1.0");
35
 
36
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
37
  define('SHORTPIXEL_MAX_FAIL_RETRIES', 3);