ShortPixel Image Optimizer - Version 4.12.0

Version Description

Release date: 31st October 2018

  • Generate WebP <picture> tags - use the output buffer instead of the_content which is not triggered by some themes on all content.
  • compatibility of the WebP <picture> tag with lazy loading plugins (that support <picture>)
  • Compatibility with Polylang.
  • hooks to be used by thumbnail regeneration plugins: 'shortpixel-thumbnails-before-regenerate' and 'shortpixel-thumbnails-regenerated'
  • Proper error message when the custom tables cannot be created.
  • exclude the PNGs from conversion to JPEG when they match the exclude patterns.
  • properly warn when cURL is not enabled that Cloudflare integration won't work.
  • send only one url for metadata thumbnails which correspond to the same physical file.
  • JavaScript delayed init for cases when some plugins deffer the load of javascript files.
  • fix identifying filenames with basename length =
Download this release

Release Info

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

Code changes from version 4.11.3 to 4.12.0

class/db/shortpixel-custom-meta-dao.php CHANGED
@@ -216,7 +216,13 @@ class ShortPixelCustomMetaDao {
216
  }
217
  $folderMsg = $this->saveFolder($folder);
218
  if(!$folder->getId()) {
219
- throw new Exception(__('Inserted folder doesn\'t have an ID!','shortpixel-image-optimiser'));
 
 
 
 
 
 
220
  }
221
  //die(var_dump($folder));
222
  if(!$folderMsg) {
216
  }
217
  $folderMsg = $this->saveFolder($folder);
218
  if(!$folder->getId()) {
219
+ //try again creating the tables first.
220
+ $this->createUpdateShortPixelTables();
221
+ $folderMsg = $this->saveFolder($folder);
222
+ //still no luck - complain... :)
223
+ if(!$folder->getId()) {
224
+ return __('The folder could not be saved to the database. Please check that the plugin can create its database tables.', 'shortpixel-image-optimiser') . $folderMsg;
225
+ }
226
  }
227
  //die(var_dump($folder));
228
  if(!$folderMsg) {
class/db/shortpixel-meta-facade.php CHANGED
@@ -329,7 +329,7 @@ class ShortPixelMetaFacade {
329
  throw new Exception("Post metadata is corrupt (No attachment URL)", ShortPixelAPI::ERR_POSTMETA_CORRUPT);
330
  }
331
  if ( !parse_url($attURL, PHP_URL_SCHEME) ) {//no absolute URLs used -> we implement a hack
332
- return self::getHomeUrl() . $attURL;//get the file URL
333
  }
334
  else {
335
  return $attURL;//get the file URL
@@ -408,6 +408,7 @@ class ShortPixelMetaFacade {
408
  }
409
  if (file_exists($tPath)) {
410
  $tUrl = str_replace(ShortPixelAPI::MB_basename($url), $thumbnailInfo['file'], $url);
 
411
  $urlList[] = $tUrl;
412
  $filePaths[] = $tPath;
413
  if($addRetina) {
@@ -453,7 +454,7 @@ class ShortPixelMetaFacade {
453
 
454
  public static function isRetina($path) {
455
  $baseName = pathinfo(ShortPixelAPI::MB_basename($path), PATHINFO_FILENAME);
456
- return strpos($baseName, "@2x") == strlen($baseName) - 3;
457
  }
458
 
459
  public static function getWPMLDuplicates( $id ) {
@@ -468,7 +469,16 @@ class ShortPixelMetaFacade {
468
  SELECT pm.post_id FROM {$wpdb->postmeta} pm
469
  WHERE pm.meta_value = %s AND pm.meta_key = '_icl_lang_duplicate_of'
470
  ", $id ) );
471
-
 
 
 
 
 
 
 
 
 
472
  if(!in_array($id, $duplicates)) $duplicates[] = $id;
473
 
474
  $transTable = $wpdb->get_results("SELECT COUNT(1) hasTransTable FROM information_schema.tables WHERE table_schema='{$wpdb->dbname}' AND table_name='{$wpdb->prefix}icl_translations'");
329
  throw new Exception("Post metadata is corrupt (No attachment URL)", ShortPixelAPI::ERR_POSTMETA_CORRUPT);
330
  }
331
  if ( !parse_url($attURL, PHP_URL_SCHEME) ) {//no absolute URLs used -> we implement a hack
332
+ return self::getHomeUrl() . ltrim($attURL,'/');//get the file URL
333
  }
334
  else {
335
  return $attURL;//get the file URL
408
  }
409
  if (file_exists($tPath)) {
410
  $tUrl = str_replace(ShortPixelAPI::MB_basename($url), $thumbnailInfo['file'], $url);
411
+ if(in_array($tUrl, $urlList)) continue;
412
  $urlList[] = $tUrl;
413
  $filePaths[] = $tPath;
414
  if($addRetina) {
454
 
455
  public static function isRetina($path) {
456
  $baseName = pathinfo(ShortPixelAPI::MB_basename($path), PATHINFO_FILENAME);
457
+ return (substr($baseName, -3) === '@2x');
458
  }
459
 
460
  public static function getWPMLDuplicates( $id ) {
469
  SELECT pm.post_id FROM {$wpdb->postmeta} pm
470
  WHERE pm.meta_value = %s AND pm.meta_key = '_icl_lang_duplicate_of'
471
  ", $id ) );
472
+
473
+ //Polylang
474
+ $moreDuplicates = $wpdb->get_col( $wpdb->prepare( "
475
+ SELECT p.ID FROM {$wpdb->posts} p
476
+ INNER JOIN {$wpdb->posts} pbase ON p.guid = pbase.guid
477
+ WHERE pbase.ID = %s
478
+ ", $id ) );
479
+
480
+ $duplicates = array_unique(array_merge($duplicates, $moreDuplicates));
481
+
482
  if(!in_array($id, $duplicates)) $duplicates[] = $id;
483
 
484
  $transTable = $wpdb->get_results("SELECT COUNT(1) hasTransTable FROM information_schema.tables WHERE table_schema='{$wpdb->dbname}' AND table_name='{$wpdb->prefix}icl_translations'");
class/db/wp-shortpixel-media-library-adapter.php CHANGED
@@ -86,7 +86,7 @@ class WpShortPixelMediaLbraryAdapter {
86
  {
87
  $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($filePath);
88
  $foundCount = count($foundThumbs);
89
- //echo(" <br>&gt; $counter CHECKING FILE THUMBS: FOUND $foundCount "
90
  // . ($foundCount > $sizesCount ? " DIFFERENT ($sizesCount)!" : ""));
91
  if(count($foundThumbs) > $sizesCount) {
92
  $unlisted = array();
@@ -248,6 +248,7 @@ class WpShortPixelMediaLbraryAdapter {
248
  foreach($sizes as $key => $val) {
249
  if (strpos($key, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) continue;
250
  if (isset($val['mime-type']) && $val['mime-type'] == "image/webp") continue;
 
251
  if (in_array($key, $exclude)) continue;
252
  $uniq[$val['file']] = $key;
253
  }
@@ -262,6 +263,7 @@ class WpShortPixelMediaLbraryAdapter {
262
  foreach($sizesAll as $key => $size) {
263
  if(strpos($key, ShortPixelMeta::FOUND_THUMB_PREFIX) === 0) continue;
264
  if(in_array($size['file'], $files)) continue;
 
265
  $sizes[$key] = $size;
266
  $files[] = $size['file'];
267
  }
86
  {
87
  $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($filePath);
88
  $foundCount = count($foundThumbs);
89
+ //echo(" <br>&gt; $counter CHECKING FILE THUMBS: FOUND $foundCount "
90
  // . ($foundCount > $sizesCount ? " DIFFERENT ($sizesCount)!" : ""));
91
  if(count($foundThumbs) > $sizesCount) {
92
  $unlisted = array();
248
  foreach($sizes as $key => $val) {
249
  if (strpos($key, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) continue;
250
  if (isset($val['mime-type']) && $val['mime-type'] == "image/webp") continue;
251
+ if(!isset($val['file'])) continue;
252
  if (in_array($key, $exclude)) continue;
253
  $uniq[$val['file']] = $key;
254
  }
263
  foreach($sizesAll as $key => $size) {
264
  if(strpos($key, ShortPixelMeta::FOUND_THUMB_PREFIX) === 0) continue;
265
  if(in_array($size['file'], $files)) continue;
266
+ if(!isset($size['file'])) continue;
267
  $sizes[$key] = $size;
268
  $files[] = $size['file'];
269
  }
class/front/img-to-picture-webp.php CHANGED
@@ -5,84 +5,113 @@
5
  */
6
 
7
  class ShortPixelImgToPictureWebp {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  public static function convert($content) {
10
  // Don't do anything with the RSS feed.
11
  if ( is_feed() || is_admin() ) { return $content; }
12
 
13
- $thisClass = __CLASS__; // hack for PHP 5.3 which doesn't accept self:: in closures
14
- return preg_replace_callback('/<img[^>]*>/', function ($match) use ($thisClass) {
15
- // Do nothing with images that have the 'sp-no-webp' class.
16
- if ( strpos($match[0], 'sp-no-webp') ) { return $match[0]; }
17
-
18
- $img = $thisClass::get_attributes($match[0]);
19
-
20
- $src = (isset($img['src'])) ? $img['src'] : false;
21
- $srcset = (isset($img['srcset'])) ? $img['srcset'] : false;
22
- $sizes = (isset($img['sizes'])) ? $img['sizes'] : false;
23
-
24
- //check if there are webps
25
- /*$id = $thisClass::url_to_attachment_id( $src );
26
- if(!$id) {
27
- return $match[0];
28
- }
29
- $imageBase = dirname(get_attached_file($id)) . '/';
30
- */
31
- $updir = wp_upload_dir();
32
- $proto = explode("://", $src);
 
 
 
 
 
 
 
 
 
 
 
 
33
  $proto = $proto[0];
34
  if(strpos($updir['baseurl'], $proto."://") === false) {
35
  $base = explode("://", $updir['baseurl']);
36
- $updir['baseurl'] = $proto . "://" . $base[1];
37
- }
38
- $imageBase = str_replace($updir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $src);
39
- if($imageBase == $src) {
40
- return $match[0];
41
- }
42
- $imageBase = dirname($imageBase) . '/';
43
-
44
- // We don't wanna have an src attribute on the <img>
45
- unset($img['src']);
46
- //unset($img['srcset']);
47
- //unset($img['sizes']);
48
-
49
- $srcsetWebP = '';
50
- if($srcset) {
51
- $defs = explode(",", $srcset);
52
- foreach($defs as $item) {
53
- $parts = preg_split('/\s+/', trim($item));
54
-
55
- //echo(" file: " . $parts[0] . " ext: " . pathinfo($parts[0], PATHINFO_EXTENSION) . " basename: " . wp_basename($parts[0], '.' . pathinfo($parts[0], PATHINFO_EXTENSION)));
56
-
57
- $fileWebP = $imageBase . wp_basename($parts[0], '.' . pathinfo($parts[0], PATHINFO_EXTENSION)) . '.webp';
58
- if(file_exists($fileWebP)) {
59
- $srcsetWebP .= (strlen($srcsetWebP) ? ',': '')
60
- . preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $parts[0])
61
- . (isset($parts[1]) ? ' ' . $parts[1] : '');
62
- }
63
  }
64
- //$srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+\s+/', '.webp ', $srcset);
65
- } else {
66
- $srcset = trim($src);
67
-
68
- // die(var_dump($match));
69
-
70
- $fileWebP = $imageBase . wp_basename($srcset, '.' . pathinfo($srcset, PATHINFO_EXTENSION)) . '.webp';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  if(file_exists($fileWebP)) {
72
- $srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $srcset);
 
 
73
  }
74
  }
75
- if(!strlen($srcsetWebP)) { return $match[0]; }
76
-
77
- //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
78
- $img['class'] = (isset($img['class']) ? $img['class'] . " " : "") . "sp-no-webp";
79
-
80
- return '<picture ' . $thisClass::create_attributes($img) . '>'
81
- .'<source srcset="' . $srcsetWebP . '"' . ($sizes ? ' sizes="' . $sizes . '"' : '') . ' type="image/webp">'
82
- .'<source srcset="' . $srcset . '"' . ($sizes ? ' sizes="' . $sizes . '"' : '') . '>'
83
- .'<img src="' . $src . '" ' . $thisClass::create_attributes($img) . '>'
84
- .'</picture>';
85
- }, $content);
 
 
 
 
 
 
 
 
 
 
86
  }
87
 
88
  public static function get_attributes( $image_node )
5
  */
6
 
7
  class ShortPixelImgToPictureWebp {
8
+
9
+ public static function lazyGet($img, $type) {
10
+ return array(
11
+ 'value' =>
12
+ (isset($img[$type]) && strlen($img[$type])) ?
13
+ $img[$type]
14
+ : (isset($img['data-' . $type]) && strlen($img['data-' . $type]) ?
15
+ $img['data-' . $type]
16
+ : (isset($img['data-lazy-' . $type]) && strlen($img['data-lazy-' . $type]) ? $img['data-lazy-' . $type] : false)),
17
+ 'prefix' =>
18
+ (isset($img[$type]) && strlen($img[$type])) ? ''
19
+ : (isset($img['data-' . $type]) && strlen($img['data-' . $type]) ? 'data-'
20
+ : (isset($img['data-lazy-' . $type]) && strlen($img['data-lazy-' . $type]) ? 'data-lazy-' : false))
21
+ );
22
+ }
23
 
24
  public static function convert($content) {
25
  // Don't do anything with the RSS feed.
26
  if ( is_feed() || is_admin() ) { return $content; }
27
 
28
+ return preg_replace_callback('/<img[^>]*>/', array('ShortPixelImgToPictureWebp', 'convertImage'), $content);
29
+ }
30
+
31
+ public static function convertImage($match) {
32
+ // Do nothing with images that have the 'sp-no-webp' class.
33
+ if ( strpos($match[0], 'sp-no-webp') ) { return $match[0]; }
34
+
35
+ $img = self::get_attributes($match[0]);
36
+
37
+ $srcInfo = self::lazyGet($img, 'src');
38
+ $src = $srcInfo['value'];
39
+ $srcPrefix = $srcInfo['prefix'];
40
+
41
+ $srcsetInfo = self::lazyGet($img, 'srcset');
42
+ $srcset = $srcsetInfo['value'];
43
+ $srcsetPrefix = $srcsetInfo['prefix'];
44
+
45
+ $sizesInfo = self::lazyGet($img, 'sizes');
46
+ $sizes = $sizesInfo['value'];
47
+ $sizesPrefix = $sizesInfo['prefix'];
48
+
49
+ //check if there are webps
50
+ /*$id = $thisClass::url_to_attachment_id( $src );
51
+ if(!$id) {
52
+ return $match[0];
53
+ }
54
+ $imageBase = dirname(get_attached_file($id)) . '/';
55
+ */
56
+ $updir = wp_upload_dir();
57
+ $proto = explode("://", $src);
58
+ if(count($proto) > 1) {
59
+ //check that baseurl uses the same http/https proto and if not, change
60
  $proto = $proto[0];
61
  if(strpos($updir['baseurl'], $proto."://") === false) {
62
  $base = explode("://", $updir['baseurl']);
63
+ if(count($base) > 1) {
64
+ $updir['baseurl'] = $proto . "://" . $base[1];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  }
66
+ }
67
+ }
68
+ $imageBase = str_replace($updir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $src);
69
+ if($imageBase == $src) {
70
+ return $match[0];
71
+ }
72
+ $imageBase = dirname($imageBase) . '/';
73
+
74
+ // We don't wanna have an src attribute on the <img>
75
+ unset($img['src']);
76
+ //unset($img['srcset']);
77
+ //unset($img['sizes']);
78
+
79
+ $srcsetWebP = '';
80
+ if($srcset) {
81
+ $defs = explode(",", $srcset);
82
+ foreach($defs as $item) {
83
+ $parts = preg_split('/\s+/', trim($item));
84
+
85
+ //echo(" file: " . $parts[0] . " ext: " . pathinfo($parts[0], PATHINFO_EXTENSION) . " basename: " . wp_basename($parts[0], '.' . pathinfo($parts[0], PATHINFO_EXTENSION)));
86
+
87
+ $fileWebP = $imageBase . wp_basename($parts[0], '.' . pathinfo($parts[0], PATHINFO_EXTENSION)) . '.webp';
88
  if(file_exists($fileWebP)) {
89
+ $srcsetWebP .= (strlen($srcsetWebP) ? ',': '')
90
+ . preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $parts[0])
91
+ . (isset($parts[1]) ? ' ' . $parts[1] : '');
92
  }
93
  }
94
+ //$srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+\s+/', '.webp ', $srcset);
95
+ } else {
96
+ $srcset = trim($src);
97
+
98
+ // die(var_dump($match));
99
+
100
+ $fileWebP = $imageBase . wp_basename($srcset, '.' . pathinfo($srcset, PATHINFO_EXTENSION)) . '.webp';
101
+ if(file_exists($fileWebP)) {
102
+ $srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $srcset);
103
+ }
104
+ }
105
+ if(!strlen($srcsetWebP)) { return $match[0]; }
106
+
107
+ //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
108
+ $img['class'] = (isset($img['class']) ? $img['class'] . " " : "") . "sp-no-webp";
109
+
110
+ return '<picture ' . self::create_attributes($img) . '>'
111
+ .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
112
+ .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
113
+ .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . '>'
114
+ .'</picture>';
115
  }
116
 
117
  public static function get_attributes( $image_node )
class/model/shortpixel-folder.php CHANGED
@@ -31,11 +31,29 @@ class ShortPixelFolder extends ShortPixelEntity{
31
  }
32
  return false;
33
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  /**
36
  * returns the first from parents that is a parent folder of $folder
37
- * @param type $folder
38
- * @param type $parents
39
  * @return parent if found, false otherwise
40
  */
41
  public static function checkFolderIsSubfolder($folder, $parents) {
@@ -52,9 +70,9 @@ class ShortPixelFolder extends ShortPixelEntity{
52
 
53
  /**
54
  * finds the first from the subfolders that has the folder as parent
55
- * @param type $folder
56
- * @param type $subfolders
57
- * @return subfolder if found, false otherwise
58
  */
59
  public static function checkFolderIsParent($folder, $subfolders) {
60
  if(!is_array($subfolders)) {
@@ -133,7 +151,7 @@ class ShortPixelFolder extends ShortPixelEntity{
133
  if(in_array($t, $ignore)) continue;
134
  $tpath = trailingslashit($path) . $t;
135
  if (is_dir($tpath)) {
136
- self::checkFolderContentsRecursive($tpath, $callback);
137
  } elseif( WPShortPixel::_isProcessablePath($tpath, array(), WPShortPixelSettings::getOpt('excludePatterns'))
138
  && !(in_array($tpath, $reference) && $reference[$tpath]->compressedSize == filesize($tpath))) {
139
  $changed[] = $tpath;
@@ -142,7 +160,7 @@ class ShortPixelFolder extends ShortPixelEntity{
142
  }
143
 
144
  public function getFolderContentsChangeDate() {
145
- return self::getFolderContentsChangeDateRecursive($this->getPath(), 0, time($this->getTsUpdated()));
146
  }
147
 
148
  protected static function getFolderContentsChangeDateRecursive($path, $mtime, $refMtime) {
@@ -221,7 +239,7 @@ class ShortPixelFolder extends ShortPixelEntity{
221
 
222
  /**
223
  * needed as callback
224
- * @param type $item
225
  */
226
  public static function path($item) {
227
  return $item->getPath();
31
  }
32
  return false;
33
  }
34
+
35
+ public static function deleteFolder($dirname) {
36
+ if (is_dir($dirname))
37
+ $dir_handle = opendir($dirname);
38
+ if (!$dir_handle)
39
+ return false;
40
+ while($file = @readdir($dir_handle)) {
41
+ if ($file != "." && $file != "..") {
42
+ if (!is_dir($dirname."/".$file))
43
+ @unlink($dirname."/".$file);
44
+ else
45
+ self::deleteFolder($dirname.'/'.$file);
46
+ }
47
+ }
48
+ closedir($dir_handle);
49
+ @rmdir($dirname);
50
+ return true;
51
+ }
52
 
53
  /**
54
  * returns the first from parents that is a parent folder of $folder
55
+ * @param string $folder
56
+ * @param array $parents
57
  * @return parent if found, false otherwise
58
  */
59
  public static function checkFolderIsSubfolder($folder, $parents) {
70
 
71
  /**
72
  * finds the first from the subfolders that has the folder as parent
73
+ * @param string $folder
74
+ * @param array $subfolders
75
+ * @return string subfolder if found, false otherwise
76
  */
77
  public static function checkFolderIsParent($folder, $subfolders) {
78
  if(!is_array($subfolders)) {
151
  if(in_array($t, $ignore)) continue;
152
  $tpath = trailingslashit($path) . $t;
153
  if (is_dir($tpath)) {
154
+ self::checkFolderContentsRecursive($tpath, $changed, $callback);
155
  } elseif( WPShortPixel::_isProcessablePath($tpath, array(), WPShortPixelSettings::getOpt('excludePatterns'))
156
  && !(in_array($tpath, $reference) && $reference[$tpath]->compressedSize == filesize($tpath))) {
157
  $changed[] = $tpath;
160
  }
161
 
162
  public function getFolderContentsChangeDate() {
163
+ return self::getFolderContentsChangeDateRecursive($this->getPath(), 0, strtotime($this->getTsUpdated()));
164
  }
165
 
166
  protected static function getFolderContentsChangeDateRecursive($path, $mtime, $refMtime) {
239
 
240
  /**
241
  * needed as callback
242
+ * @param ShortPixelFolder $item
243
  */
244
  public static function path($item) {
245
  return $item->getPath();
class/shortpixel-png2jpg.php CHANGED
@@ -109,6 +109,7 @@ class ShortPixelPng2Jpg {
109
  $origSize = filesize($image);
110
  if($newSize > $origSize * 0.95) {
111
  //if the image is not 5% smaller, don't bother.
 
112
  unlink($newPath);
113
  return (object)array("params" => $params, "unlink" => false);
114
  }
@@ -127,6 +128,7 @@ class ShortPixelPng2Jpg {
127
  $image = $imageForBk;
128
  $ret = ShortPixelAPI::backupImage($image, array($image));
129
  if($ret['Status'] !== ShortPixelAPI::STATUS_SUCCESS) {
 
130
  unlink($newPath);
131
  return (object)array("params" => $params, "unlink" => false);
132
  }
@@ -143,6 +145,21 @@ class ShortPixelPng2Jpg {
143
  return (object)array("params" => $params, "unlink" => $image);
144
  }
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  /**
147
  * Convert an uploaded image from PNG to JPG
148
  * @param type $params
@@ -154,6 +171,7 @@ class ShortPixelPng2Jpg {
154
  if(!$this->_settings->png2jpg || strtolower(substr($params['file'], -4)) !== '.png') {
155
  return $params;
156
  }
 
157
 
158
  $image = $params['file'];
159
  WPShortPixel::log("Convert Media PNG to JPG on upload: {$image}");
@@ -199,6 +217,7 @@ class ShortPixelPng2Jpg {
199
  if(!$this->_settings->png2jpg || !isset($meta['file']) || strtolower(substr($meta['file'], -4)) !== '.png') {
200
  return ;
201
  }
 
202
 
203
  WPShortPixel::log("Send to processing: Convert Media PNG to JPG #{$ID} META: " . json_encode($meta));
204
 
@@ -264,10 +283,21 @@ class ShortPixelPng2Jpg {
264
  WPShortPixel::log(" WPML duplicates: " . json_encode($duplicates));
265
 
266
  $originalSizes = isset($meta['sizes']) ? $meta['sizes'] : array();
 
267
  foreach($meta['sizes'] as $size => $info) {
268
- $retThumb = $this->doConvertPng2Jpg(array('file' => $basePath . $baseRelPath . $info['file'], 'url' => false, 'type' => 'image/png'),
269
- $this->_settings->backupImages, "[0-9]+x[0-9]+");
270
- $rett = $retThumb->params;
 
 
 
 
 
 
 
 
 
 
271
 
272
  WPShortPixel::log("PNG2JPG doConvert thumb RETURNED " . json_encode($rett));
273
  if ($rett['type'] == 'image/jpeg') {
@@ -280,7 +310,10 @@ class ShortPixelPng2Jpg {
280
  WPShortPixel::log("PNG2JPG thumb original: " . $originalSizes[$size]['file']);
281
  $toReplace[$baseUrl . $baseRelPath . $info['file']] = $baseUrl . $baseRelPath . wp_basename($rett['file']);
282
 
 
283
  $this->updateThumbAlsoInWPMLDuplicates($ID, $meta, $duplicates, $size, wp_basename($rett['file']));
 
 
284
  }
285
  }
286
  $meta['ShortPixelPng2Jpg'] = array('originalFile' => $imagePath, 'originalSizes' => $originalSizes, 'originalSizes2' => $originalSizes,
@@ -288,6 +321,7 @@ class ShortPixelPng2Jpg {
288
  'optimizationPercent' => round(100.0 * (1.00 - $jpgSize / $pngSize)));
289
  //wp_update_attachment_metadata($ID, $meta);
290
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
 
291
  }
292
 
293
  self::png2JpgUpdateUrls(array(), $toReplace);
@@ -405,7 +439,7 @@ class ShortPixelPng2Jpg {
405
  }
406
 
407
  public static function removeUrlProtocol($url) {
408
- return preg_replace("/^http[s]{0,1}:\/\//", "//", $url);
409
  }
410
 
411
  /**
109
  $origSize = filesize($image);
110
  if($newSize > $origSize * 0.95) {
111
  //if the image is not 5% smaller, don't bother.
112
+ WPShortPixel::log("PNG2JPG converted image is larger ($newSize vs. $origSize), keeping the PNG");
113
  unlink($newPath);
114
  return (object)array("params" => $params, "unlink" => false);
115
  }
128
  $image = $imageForBk;
129
  $ret = ShortPixelAPI::backupImage($image, array($image));
130
  if($ret['Status'] !== ShortPixelAPI::STATUS_SUCCESS) {
131
+ WPShortPixel::log("PNG2JPG couldn't backup, keeping the PNG");
132
  unlink($newPath);
133
  return (object)array("params" => $params, "unlink" => false);
134
  }
145
  return (object)array("params" => $params, "unlink" => $image);
146
  }
147
 
148
+ protected function isExcluded($params) {
149
+ if(is_array($this->_settings->excludePatterns)) {
150
+ foreach($this->_settings->excludePatterns as $item) {
151
+ $type = trim($item["type"]);
152
+ if(in_array($type, array('name', 'path')) && WpShortPixel::matchExcludePattern($params['file'], $item['value'])) {
153
+ return true; //excluded by name pattern
154
+ }
155
+ if(isset($params['width']) && isset($params['height']) && 'size' == $type && WPShortPixel::isProcessableSize($params['width'], $params['height'], $item['value'])){
156
+ return true;
157
+ }
158
+ }
159
+ }
160
+ return false;
161
+ }
162
+
163
  /**
164
  * Convert an uploaded image from PNG to JPG
165
  * @param type $params
171
  if(!$this->_settings->png2jpg || strtolower(substr($params['file'], -4)) !== '.png') {
172
  return $params;
173
  }
174
+ if($this->isExcluded($params)) { return $params; }
175
 
176
  $image = $params['file'];
177
  WPShortPixel::log("Convert Media PNG to JPG on upload: {$image}");
217
  if(!$this->_settings->png2jpg || !isset($meta['file']) || strtolower(substr($meta['file'], -4)) !== '.png') {
218
  return ;
219
  }
220
+ if($this->isExcluded($meta)) { return; }
221
 
222
  WPShortPixel::log("Send to processing: Convert Media PNG to JPG #{$ID} META: " . json_encode($meta));
223
 
283
  WPShortPixel::log(" WPML duplicates: " . json_encode($duplicates));
284
 
285
  $originalSizes = isset($meta['sizes']) ? $meta['sizes'] : array();
286
+ $filesConverted = array();
287
  foreach($meta['sizes'] as $size => $info) {
288
+ if(isset($filesConverted[$info['file']])) {
289
+ WPShortPixel::log("PNG2JPG DUPLICATED THUMB: " . $size);
290
+ if($filesConverted[$info['file']] === false) {
291
+ WPShortPixel::log("PNG2JPG DUPLICATED THUMB not converted");
292
+ continue;
293
+ }
294
+ WPShortPixel::log("PNG2JPG DUPLICATED THUMB already converted");
295
+ $rett = $filesConverted[$info['file']];
296
+ } else {
297
+ $retThumb = $this->doConvertPng2Jpg(array('file' => $basePath . $baseRelPath . $info['file'], 'url' => false, 'type' => 'image/png'),
298
+ $this->_settings->backupImages, "[0-9]+x[0-9]+");
299
+ $rett = $retThumb->params;
300
+ }
301
 
302
  WPShortPixel::log("PNG2JPG doConvert thumb RETURNED " . json_encode($rett));
303
  if ($rett['type'] == 'image/jpeg') {
310
  WPShortPixel::log("PNG2JPG thumb original: " . $originalSizes[$size]['file']);
311
  $toReplace[$baseUrl . $baseRelPath . $info['file']] = $baseUrl . $baseRelPath . wp_basename($rett['file']);
312
 
313
+ $filesConverted[$info['file']] = $rett;
314
  $this->updateThumbAlsoInWPMLDuplicates($ID, $meta, $duplicates, $size, wp_basename($rett['file']));
315
+ } else {
316
+ $filesConverted[$info['file']] = false;
317
  }
318
  }
319
  $meta['ShortPixelPng2Jpg'] = array('originalFile' => $imagePath, 'originalSizes' => $originalSizes, 'originalSizes2' => $originalSizes,
321
  'optimizationPercent' => round(100.0 * (1.00 - $jpgSize / $pngSize)));
322
  //wp_update_attachment_metadata($ID, $meta);
323
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
324
+ WPShortPixel::log("Updated meta: " . json_encode($meta));
325
  }
326
 
327
  self::png2JpgUpdateUrls(array(), $toReplace);
439
  }
440
 
441
  public static function removeUrlProtocol($url) {
442
+ return preg_replace("/^http[s]{0,1}:\/\//", "", $url);
443
  }
444
 
445
  /**
class/view/shortpixel-feedback.php CHANGED
@@ -62,7 +62,7 @@ class ShortPixelFeedback {
62
  . esc_html__( 'Remove the ShortPixel settings on plugin delete.', $this->plugin_name ) . '</label></span><br>';
63
  $html .= '<p><strong>' . esc_html( $form['body'] ) . '</strong></p><p>';
64
  foreach( $form['options'] as $key => $option ) {
65
- $html .= '<input type="radio" name="shortpixel-deactivate-reason"'.('features' == $key ? ' checked="checked"' : '').' id="' . esc_attr( $key ) . '" value="' . esc_attr( $key ) . '"> <label for="' . esc_attr( $key ) . '">' . esc_attr( $option ) . '</label><br>';
66
  }
67
  $html .= '</p><label id="shortpixel-deactivate-details-label" for="shortpixel-deactivate-reasons"><strong>' . esc_html( $form['details'] ) .'</strong></label><textarea name="shortpixel-deactivate-details" id="shortpixel-deactivate-details" rows="2" style="width:100%"></textarea>';
68
  $html .= '<label for="anonymous" title="'
@@ -72,7 +72,7 @@ class ShortPixelFeedback {
72
  }
73
  $html .= '</div><!-- .shortpixel-deactivate-form-body -->';
74
  $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', $this->plugin_name ) . '</p>';
75
- $html .= '<div class="shortpixel-deactivate-form-footer"><p><a id="shortpixel-deactivate-plugin" href="#">' . __( 'Just Deactivate', $this->plugin_name ) . '</a><a id="shortpixel-deactivate-submit-form" class="button button-primary" href="#">' . __( 'Submit and Deactivate', $this->plugin_name ) . '</a></p></div>'
76
  ?>
77
  <div class="shortpixel-deactivate-form-bg"></div>
78
  <style type="text/css">
@@ -150,9 +150,10 @@ class ShortPixelFeedback {
150
  jQuery(document).ready(function($){
151
  var deactivateURL = $("#shortpixel-deactivate-link-<?php echo esc_attr( $this->plugin_name ); ?>"),
152
  formContainer = $('#shortpixel-deactivate-form-<?php echo esc_attr( $this->plugin_name ); ?>'),
 
153
  detailsStrings = {
154
  'setup' : '<?php echo __( 'What was the dificult part ?', $this->plugin_name ) ?>',
155
- 'documentation' : '<?php echo __( 'What can we describe more ?', $this->plugin_name ) ?>',
156
  'features' : '<?php echo __( 'How could we improve ?', $this->plugin_name ) ?>',
157
  'better-plugin' : '<?php echo __( 'Can you mention it ?', $this->plugin_name ) ?>',
158
  'incompatibility' : '<?php echo __( 'With what plugin or theme is incompatible ?', $this->plugin_name ) ?>',
@@ -176,35 +177,40 @@ class ShortPixelFeedback {
176
  var detailsLabel = formContainer.find( '#shortpixel-deactivate-details-label strong' );
177
  var value = formContainer.find( 'input[name="shortpixel-deactivate-reason"]:checked' ).val();
178
  detailsLabel.text( detailsStrings[ value ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  });
180
 
181
  formContainer.on('click', '#shortpixel-deactivate-submit-form', function(e){
182
- debugger;
183
- var data = {
184
- 'action': 'shortpixel_deactivate_plugin',
185
- 'security': "<?php echo wp_create_nonce ( 'shortpixel_deactivate_plugin' ); ?>",
186
- 'dataType': "json"
187
- };
188
  e.preventDefault();
189
- // As soon as we click, the body of the form should disappear
190
- formContainer.addClass( 'process-response' );
191
- // Fade in spinner
192
- formContainer.find(".deactivating-spinner").fadeIn();
193
-
194
- data['reason'] = formContainer.find( 'input[name="shortpixel-deactivate-reason"]:checked' ).val();
195
- data['details'] = formContainer.find('#shortpixel-deactivate-details').val();
196
- data['anonymous'] = formContainer.find( '#anonymous:checked' ).length;
197
- data['remove-settings'] = formContainer.find( '#shortpixel-remove-settings:checked').length;
198
-
199
- $.post(
200
- ajaxurl,
201
- data,
202
- function(response){
203
- // Redirect to original deactivation URL
204
- debugger;
205
- window.location.href = url;
206
- }
207
- );
208
  });
209
 
210
  formContainer.on('click', '#shortpixel-deactivate-plugin', function(e){
@@ -233,7 +239,7 @@ class ShortPixelFeedback {
233
  $form['body'] = __( 'Before you deactivate the plugin, would you quickly give us your reason for doing so?', $this->plugin_name );
234
  $form['options'] = array(
235
  'setup' => __( 'Set up is too difficult', $this->plugin_name ),
236
- 'documentation' => __( 'Lack of documentation', $this->plugin_name ),
237
  'features' => __( 'Not the features I wanted', $this->plugin_name ),
238
  'better-plugin' => __( 'Found a better plugin', $this->plugin_name ),
239
  'incompatibility' => __( 'Incompatible with theme or plugin', $this->plugin_name ),
62
  . esc_html__( 'Remove the ShortPixel settings on plugin delete.', $this->plugin_name ) . '</label></span><br>';
63
  $html .= '<p><strong>' . esc_html( $form['body'] ) . '</strong></p><p>';
64
  foreach( $form['options'] as $key => $option ) {
65
+ $html .= '<input type="radio" name="shortpixel-deactivate-reason" id="' . esc_attr( $key ) . '" value="' . esc_attr( $key ) . '"> <label for="' . esc_attr( $key ) . '">' . esc_attr( $option ) . '</label><br>';
66
  }
67
  $html .= '</p><label id="shortpixel-deactivate-details-label" for="shortpixel-deactivate-reasons"><strong>' . esc_html( $form['details'] ) .'</strong></label><textarea name="shortpixel-deactivate-details" id="shortpixel-deactivate-details" rows="2" style="width:100%"></textarea>';
68
  $html .= '<label for="anonymous" title="'
72
  }
73
  $html .= '</div><!-- .shortpixel-deactivate-form-body -->';
74
  $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', $this->plugin_name ) . '</p>';
75
+ $html .= '<div class="shortpixel-deactivate-form-footer"><p><a id="shortpixel-deactivate-plugin" href="#">' . __( 'Just Deactivate', $this->plugin_name ) . '</a><a id="shortpixel-deactivate-submit-form" class="button button-primary" href="#" disabled>' . __( 'Submit and Deactivate', $this->plugin_name ) . '</a></p></div>'
76
  ?>
77
  <div class="shortpixel-deactivate-form-bg"></div>
78
  <style type="text/css">
150
  jQuery(document).ready(function($){
151
  var deactivateURL = $("#shortpixel-deactivate-link-<?php echo esc_attr( $this->plugin_name ); ?>"),
152
  formContainer = $('#shortpixel-deactivate-form-<?php echo esc_attr( $this->plugin_name ); ?>'),
153
+ deactivated = true,
154
  detailsStrings = {
155
  'setup' : '<?php echo __( 'What was the dificult part ?', $this->plugin_name ) ?>',
156
+ 'docs' : '<?php echo __( 'What can we describe more ?', $this->plugin_name ) ?>',
157
  'features' : '<?php echo __( 'How could we improve ?', $this->plugin_name ) ?>',
158
  'better-plugin' : '<?php echo __( 'Can you mention it ?', $this->plugin_name ) ?>',
159
  'incompatibility' : '<?php echo __( 'With what plugin or theme is incompatible ?', $this->plugin_name ) ?>',
177
  var detailsLabel = formContainer.find( '#shortpixel-deactivate-details-label strong' );
178
  var value = formContainer.find( 'input[name="shortpixel-deactivate-reason"]:checked' ).val();
179
  detailsLabel.text( detailsStrings[ value ] );
180
+ if(deactivated) {
181
+ deactivated = false;
182
+ $('#shortpixel-deactivate-submit-form').removeAttr("disabled");
183
+ formContainer.on('click', '#shortpixel-deactivate-submit-form', function(e){
184
+ var data = {
185
+ 'action': 'shortpixel_deactivate_plugin',
186
+ 'security': "<?php echo wp_create_nonce ( 'shortpixel_deactivate_plugin' ); ?>",
187
+ 'dataType': "json"
188
+ };
189
+ e.preventDefault();
190
+ // As soon as we click, the body of the form should disappear
191
+ formContainer.addClass( 'process-response' );
192
+ // Fade in spinner
193
+ formContainer.find(".deactivating-spinner").fadeIn();
194
+
195
+ data['reason'] = formContainer.find( 'input[name="shortpixel-deactivate-reason"]:checked' ).val();
196
+ data['details'] = formContainer.find('#shortpixel-deactivate-details').val();
197
+ data['anonymous'] = formContainer.find( '#anonymous:checked' ).length;
198
+ data['remove-settings'] = formContainer.find( '#shortpixel-remove-settings:checked').length;
199
+
200
+ $.post(
201
+ ajaxurl,
202
+ data,
203
+ function(response){
204
+ // Redirect to original deactivation URL
205
+ window.location.href = url;
206
+ }
207
+ );
208
+ });
209
+ }
210
  });
211
 
212
  formContainer.on('click', '#shortpixel-deactivate-submit-form', function(e){
 
 
 
 
 
 
213
  e.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  });
215
 
216
  formContainer.on('click', '#shortpixel-deactivate-plugin', function(e){
239
  $form['body'] = __( 'Before you deactivate the plugin, would you quickly give us your reason for doing so?', $this->plugin_name );
240
  $form['options'] = array(
241
  'setup' => __( 'Set up is too difficult', $this->plugin_name ),
242
+ 'docs' => __( 'Lack of documentation', $this->plugin_name ),
243
  'features' => __( 'Not the features I wanted', $this->plugin_name ),
244
  'better-plugin' => __( 'Found a better plugin', $this->plugin_name ),
245
  'incompatibility' => __( 'Incompatible with theme or plugin', $this->plugin_name ),
class/view/shortpixel_view.php CHANGED
@@ -393,7 +393,7 @@ class ShortPixelView {
393
  <?php
394
  $failed = $this->ctrl->getPrioQ()->getFailed();
395
  if(count($failed)) { ?>
396
- <div class="bulk-progress" style="margin-bottom: 15px">
397
  <p>
398
  <?php _e('The following images could not be processed because of their limited write rights. This usually happens if you have changed your hosting provider. Please restart the optimization process after you granted write rights to all the files below.','shortpixel-image-optimiser');?>
399
  </p>
@@ -421,23 +421,36 @@ class ShortPixelView {
421
  _e('','shortpixel-image-optimiser');
422
  if (count($quotaData['filesWithErrors'])) {
423
  echo('&nbsp;');
424
- _e('Some have errors:','shortpixel-image-optimiser'); echo(' ');
425
- $first = true;
426
  foreach($quotaData['filesWithErrors'] as $id => $data) {
427
- if(!$first) {
428
- echo(",&nbsp;");
429
- }
430
- $first = false;
431
  if(ShortPixelMetaFacade::isCustomQueuedId($id)) {
432
- echo('<a href="'. ShortPixelMetaFacade::getHomeUrl() . ShortPixelMetaFacade::filenameToRootRelative($data['Path']).'" title="'.$data['Message'].'" target="_blank">'.$data['Name'].'</a>');
433
  } else {
434
- echo('<a href="post.php?post='.$data['Id'].'&action=edit" title="'.$data['Message'].'">'.$data['Name'].'</a>');
435
  }
436
  }
437
  if(isset($quotaData['moreFilesWithErrors']) && $quotaData['moreFilesWithErrors']) {
438
- echo('&nbsp;');printf(__("(%s more)",'shortpixel-image-optimiser'), $quotaData['moreFilesWithErrors']);
439
  }
440
- } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  </p>
442
  <?php }
443
  $settings = $this->ctrl->getSettings();
@@ -680,7 +693,7 @@ class ShortPixelView {
680
 
681
  public function displayFailed($failed) {
682
  ?>
683
- <div class="bulk-progress bulk-stats">
684
  <?php foreach($failed as $fail) {
685
  if($fail->type == ShortPixelMetaFacade::CUSTOM_TYPE) {
686
  $meta = $fail->meta;
@@ -1145,7 +1158,7 @@ class ShortPixelView {
1145
  if(!$gdInstalled) {echo("&nbsp;<span style='color:red;'>" . __('You need PHP GD for this. Please ask your hosting to install it.','shortpixel-image-optimiser') . "</span>");}
1146
  ?>
1147
  <p class="settings-info">
1148
- <?php _e('Converts all PNGs that don\'t have transparent pixels to JPEG. This can dramatically reduce the file size, especially if you have camera pictures that are saved in PNG format. <strong>PNGs with transparency will not be converted.</strong> The plugin will also search for references of the image in posts and will replace them.','shortpixel-image-optimiser');?>
1149
  </p>
1150
  </td>
1151
  </tr>
@@ -1289,6 +1302,10 @@ class ShortPixelView {
1289
  */
1290
  function display_cloudflare_settings_form()
1291
  {
 
 
 
 
1292
  ?>
1293
  <p><?php _e("If you're using Cloudflare on your site then we advise you to fill in the details below. This will allow ShortPixel to work seamlessly with Cloudflare so that any image optimized/restored by ShortPixel will be automatically updated on Cloudflare as well.",'shortpixel-image-optimiser');?></p>
1294
  <form name='wp_shortpixel_cloudflareAPI' action='options-general.php?page=wp-shortpixel&noheader=true'
@@ -1300,7 +1317,7 @@ class ShortPixelView {
1300
  <label for="cloudflare-email"><?php _e('Cloudflare E-mail:', 'shortpixel-image-optimiser'); ?></label>
1301
  </th>
1302
  <td>
1303
- <input name="cloudflare-email" type="text" id="cloudflare-email"
1304
  value="<?php echo($this->ctrl->fetch_cloudflare_api_email()); ?>" class="regular-text">
1305
  <p class="settings-info">
1306
  <?php _e('The e-mail address you use to login to CloudFlare.','shortpixel-image-optimiser');?>
@@ -1312,7 +1329,7 @@ class ShortPixelView {
1312
  for="cloudflare-auth-key"><?php _e('Global API Key:', 'shortpixel-image-optimiser'); ?></label>
1313
  </th>
1314
  <td>
1315
- <input name="cloudflare-auth-key" type="text" id="cloudflare-auth-key"
1316
  value="<?php echo($this->ctrl->fetch_cloudflare_api_key()); ?>" class="regular-text">
1317
  <p class="settings-info">
1318
  <?php _e("This can be found when you're logged into your account, on the My Profile page:",'shortpixel-image-optimiser');?> <a href='https://www.cloudflare.com/a/profile' target='_blank'>https://www.cloudflare.com/a/profile</a>
@@ -1324,7 +1341,7 @@ class ShortPixelView {
1324
  for="cloudflare-zone-id"><?php _e('Zone ID:', 'shortpixel-image-optimiser'); ?></label>
1325
  </th>
1326
  <td>
1327
- <input name="cloudflare-zone-id" type="text" id="cloudflare-zone-id"
1328
  value="<?php echo($this->ctrl->fetch_cloudflare_api_zoneid()); ?>" class="regular-text">
1329
  <p class="settings-info">
1330
  <?php _e('This can be found in your Cloudflare account in the "Overview" section for your domain.','shortpixel-image-optimiser');?>
393
  <?php
394
  $failed = $this->ctrl->getPrioQ()->getFailed();
395
  if(count($failed)) { ?>
396
+ <div class="bulk-progress sp-notice sp-notice-warning sp-floating-block sp-double-width" style="margin-bottom: 15px">
397
  <p>
398
  <?php _e('The following images could not be processed because of their limited write rights. This usually happens if you have changed your hosting provider. Please restart the optimization process after you granted write rights to all the files below.','shortpixel-image-optimiser');?>
399
  </p>
421
  _e('','shortpixel-image-optimiser');
422
  if (count($quotaData['filesWithErrors'])) {
423
  echo('&nbsp;');
424
+ echo('<strong class="bulk-error-show closed">');_e('Some have errors','shortpixel-image-optimiser'); echo(' <a <span class="dashicons dashicons-arrow-down"></span></strong>');
425
+ ?><div class="bulk-error-list"><ul><?php
426
  foreach($quotaData['filesWithErrors'] as $id => $data) {
 
 
 
 
427
  if(ShortPixelMetaFacade::isCustomQueuedId($id)) {
428
+ echo('<li><a href="'. ShortPixelMetaFacade::getHomeUrl() . ShortPixelMetaFacade::filenameToRootRelative($data['Path']).'" target="_blank">'.$data['Name'].'</a> - '.$data['Message'].'</li>');
429
  } else {
430
+ echo('<li><a href="post.php?post='.$data['Id'].'&action=edit">'.$data['Name'].'</a> - '.$data['Message'].'</li>');
431
  }
432
  }
433
  if(isset($quotaData['moreFilesWithErrors']) && $quotaData['moreFilesWithErrors']) {
434
+ echo('<li>');printf(__("(%s more)",'shortpixel-image-optimiser'), $quotaData['moreFilesWithErrors']); echo('</li>');
435
  }
436
+ ?></ul></div>
437
+ <script>
438
+ jQuery(function(){
439
+ jQuery(".sp-notice .bulk-error-show").click(function(e){
440
+ var elm = e.target;
441
+ if(jQuery(elm).hasClass("closed")){
442
+ jQuery(".sp-notice .bulk-error-list").show(400);
443
+ jQuery(elm).removeClass("closed").addClass("open");
444
+ jQuery("a.dashicons", elm).removeClass("dashicons-arrow-down").addClass("dashicons-arrow-up");
445
+ } else {
446
+ jQuery(".sp-notice .bulk-error-list").hide(400);
447
+ jQuery(elm).removeClass("open").addClass("closed");
448
+ jQuery("a.dashicons", elm).removeClass("dashicons-arrow-up").addClass("dashicons-arrow-down");
449
+ }
450
+ });
451
+ });
452
+ </script>
453
+ <?php } ?>
454
  </p>
455
  <?php }
456
  $settings = $this->ctrl->getSettings();
693
 
694
  public function displayFailed($failed) {
695
  ?>
696
+ <div class="bulk-progress bulk-stats" style="padding-top:5px;">
697
  <?php foreach($failed as $fail) {
698
  if($fail->type == ShortPixelMetaFacade::CUSTOM_TYPE) {
699
  $meta = $fail->meta;
1158
  if(!$gdInstalled) {echo("&nbsp;<span style='color:red;'>" . __('You need PHP GD for this. Please ask your hosting to install it.','shortpixel-image-optimiser') . "</span>");}
1159
  ?>
1160
  <p class="settings-info">
1161
+ <?php _e('Converts all PNGs that don\'t have transparent pixels to JPEG. This can dramatically reduce the file size, especially if you have camera pictures that are saved in PNG format. The plugin will also search for references of the image in posts and will replace them.','shortpixel-image-optimiser');?>
1162
  </p>
1163
  </td>
1164
  </tr>
1302
  */
1303
  function display_cloudflare_settings_form()
1304
  {
1305
+ $noCurl = !function_exists('curl_init');
1306
+ if($noCurl) {
1307
+ echo('<p style="font-weight:bold;color:red">' . __("Please enable PHP cURL extension for the Cloudflare integration to work.", 'shortpixel-image-optimiser') . '</p>' );
1308
+ }
1309
  ?>
1310
  <p><?php _e("If you're using Cloudflare on your site then we advise you to fill in the details below. This will allow ShortPixel to work seamlessly with Cloudflare so that any image optimized/restored by ShortPixel will be automatically updated on Cloudflare as well.",'shortpixel-image-optimiser');?></p>
1311
  <form name='wp_shortpixel_cloudflareAPI' action='options-general.php?page=wp-shortpixel&noheader=true'
1317
  <label for="cloudflare-email"><?php _e('Cloudflare E-mail:', 'shortpixel-image-optimiser'); ?></label>
1318
  </th>
1319
  <td>
1320
+ <input name="cloudflare-email" type="text" id="cloudflare-email" <?php echo($noCurl ? 'disabled' : '');?>
1321
  value="<?php echo($this->ctrl->fetch_cloudflare_api_email()); ?>" class="regular-text">
1322
  <p class="settings-info">
1323
  <?php _e('The e-mail address you use to login to CloudFlare.','shortpixel-image-optimiser');?>
1329
  for="cloudflare-auth-key"><?php _e('Global API Key:', 'shortpixel-image-optimiser'); ?></label>
1330
  </th>
1331
  <td>
1332
+ <input name="cloudflare-auth-key" type="text" id="cloudflare-auth-key" <?php echo($noCurl ? 'disabled' : '');?>
1333
  value="<?php echo($this->ctrl->fetch_cloudflare_api_key()); ?>" class="regular-text">
1334
  <p class="settings-info">
1335
  <?php _e("This can be found when you're logged into your account, on the My Profile page:",'shortpixel-image-optimiser');?> <a href='https://www.cloudflare.com/a/profile' target='_blank'>https://www.cloudflare.com/a/profile</a>
1341
  for="cloudflare-zone-id"><?php _e('Zone ID:', 'shortpixel-image-optimiser'); ?></label>
1342
  </th>
1343
  <td>
1344
+ <input name="cloudflare-zone-id" type="text" id="cloudflare-zone-id" <?php echo($noCurl ? 'disabled' : '');?>
1345
  value="<?php echo($this->ctrl->fetch_cloudflare_api_zoneid()); ?>" class="regular-text">
1346
  <p class="settings-info">
1347
  <?php _e('This can be found in your Cloudflare account in the "Overview" section for your domain.','shortpixel-image-optimiser');?>
class/wp-short-pixel.php CHANGED
@@ -39,7 +39,7 @@ class WPShortPixel {
39
 
40
  define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
41
 
42
- if(is_plugin_active('envira-gallery/envira-gallery.php')) {
43
  define('SHORTPIXEL_CUSTOM_THUMB_SUFFIX', '_c');
44
  }
45
 
@@ -78,7 +78,9 @@ class WPShortPixel {
78
 
79
  //custom hook
80
  add_action( 'shortpixel-optimize-now', array( &$this, 'optimizeNowHook' ), 10, 1);
81
- add_action( 'shortpixel-thumbnails-regenerated', array( &$this, 'thumbnailsRegeneratedHook' ), 10, 3);
 
 
82
 
83
  if($isAdminUser) {
84
  //add settings page
@@ -109,7 +111,7 @@ class WPShortPixel {
109
  //only if the key is not yet valid or the user hasn't bought any credits.
110
  $stats = $this->_settings->currentStats;
111
  $totalCredits = isset($stats["APICallsQuotaNumeric"]) ? $stats['APICallsQuotaNumeric'] + $stats['APICallsQuotaOneTimeNumeric'] : 0;
112
- if(!$this->_settings->verifiedKey || $totalCredits < 4000) {
113
  require_once 'view/shortpixel-feedback.php';
114
  new ShortPixelFeedback( SHORTPIXEL_PLUGIN_FILE, 'shortpixel-image-optimiser', $this->_settings->apiKey, $this);
115
  }
@@ -369,7 +371,7 @@ class WPShortPixel {
369
  array("__SP_FIRST_TYPE__", "__SP_SECOND_TYPE__"), "__SP_CELL_MESSAGE__", 'sp-column-actions-template');
370
  }
371
  }
372
- ?>
373
  <script type="text/javascript" >
374
  var ShortPixelConstants = {
375
  STATUS_SUCCESS: <?php echo ShortPixelAPI::STATUS_SUCCESS; ?>,
@@ -392,9 +394,14 @@ class WPShortPixel {
392
  AFFILIATE: '<?php echo(self::getAffiliateSufix());?>'
393
  };
394
  //check after 10 seconds if ShortPixel initialized OK, if not, force the init (could happen if a JS error somewhere else stopped the JS execution).
395
- setTimeout(function($) {
396
- ShortPixel.init();
397
- }, 10000);
 
 
 
 
 
398
  </script> <?php
399
  wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
400
 
@@ -556,7 +563,7 @@ class WPShortPixel {
556
  }
557
 
558
  /**
559
- * this is hooked onto the MediaLibrary image upload
560
  * @param array $meta - the wordpress postmeta structure
561
  * @param type $ID - the Media Library ID
562
  * @return the meta structure updated with ShortPixel info if case
@@ -574,6 +581,11 @@ class WPShortPixel {
574
  if(isset($dbMeta['ShortPixelImprovement'])) {
575
  return $meta;
576
  }
 
 
 
 
 
577
 
578
  self::log("Handle Media Library Image Upload #{$ID}");
579
  //self::log("STACK: " . json_encode(debug_backtrace()));
@@ -872,10 +884,21 @@ class WPShortPixel {
872
  $meta = $item->getMeta();//wp_get_attachment_metadata($crtStartQueryID);
873
 
874
  if($meta->getStatus() != 2) {
875
- $itemList[] = $item;
876
- $idList[] = $crtStartQueryID;
877
- if(count($itemList) > SHORTPIXEL_PRESEND_ITEMS) break;
878
- }
 
 
 
 
 
 
 
 
 
 
 
879
  elseif($meta->getCompressionType() !== null && $meta->getCompressionType() != $this->_settings->compressionType) {//a different type of compression was chosen in settings
880
  if($this->doRestore($crtStartQueryID)) {
881
  $itemList[] = $item = new ShortPixelMetaFacade($crtStartQueryID); //force reload after restore
@@ -888,12 +911,14 @@ class WPShortPixel {
888
  elseif( $this->_settings->processThumbnails && $meta->getThumbsOpt() !== null
889
  && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0
890
  || $meta->getThumbsOpt() < WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes) && is_array($meta->getThumbsOptList()))) { //thumbs were chosen in settings
891
- //if($crtStartQueryID == 44 || $crtStartQueryID == 49) {echo("No THuMBS?");die(var_dump($meta));}
892
- $meta->setThumbsTodo(true);
893
- $item->updateMeta($meta);//wp_update_attachment_metadata($crtStartQueryID, $meta);
894
- $itemList[] = $item;
895
- $idList[] = $crtStartQueryID;
896
- if(count($itemList) > SHORTPIXEL_PRESEND_ITEMS) break;
 
 
897
  }
898
  elseif($itemMetaData->meta_key == '_wp_attachment_metadata') { //count skipped
899
  $skippedAlreadyProcessed++;
@@ -1012,7 +1037,7 @@ class WPShortPixel {
1012
 
1013
  //self::log("HIP: 0 Bulk ran: " . $this->prioQ->bulkRan());
1014
  $customIds = false;
1015
- if(count($ids) < 3 && $this->prioQ->bulkRan() && $this->_settings->hasCustomFolders
1016
  && (!$this->_settings->cancelPointer || $this->_settings->skipToCustom)
1017
  && !$this->_settings->customBulkPaused)
1018
  { //take from custom images if any left to optimize - only if bulk was ever started
@@ -1032,8 +1057,8 @@ class WPShortPixel {
1032
  //self::log("HIP: 1 Ids: ".json_encode($ids));
1033
  if(count($ids)) {$idl='';foreach($ids as $i){$idl.=$i->getId().' ';} self::log("HIP: 1 Selected IDs: $idl");}
1034
 
1035
- //2: Send up to 3 files to the server for processing
1036
- for($i = 0, $itemHandler = false; $ids !== false && $i < min(3, count($ids)); $i++) {
1037
  $crtItemHandler = $ids[$i];
1038
  $tmpMeta = $crtItemHandler->getMeta();
1039
  $compType = ($tmpMeta->getCompressionType() !== null ? $tmpMeta->getCompressionType() : $this->_settings->compressionType);
@@ -1047,7 +1072,9 @@ class WPShortPixel {
1047
  $firstUrlAndPaths = $URLsAndPATHs;
1048
  }
1049
  } catch(Exception $e) { // Exception("Post metadata is corrupt (No attachment URL)") or Exception("Image files are missing.")
1050
- $crtItemHandler->incrementRetries(1, ($e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::ERR_FILE_NOT_FOUND), $e->getMessage());
 
 
1051
  if(! $this->prioQ->remove($crtItemHandler->getQueuedId()) ){
1052
  $this->advanceBulk($crtItemHandler->getId());
1053
  $res['searching'] = true;
@@ -1145,10 +1172,15 @@ class WPShortPixel {
1145
  $bkThumb = $backupUrl . $urlBkPath . $thumb;
1146
  }
1147
  if(strlen($thumb)) {
1148
- $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
1149
- $urlPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
1150
- //$urlPath = implode("/", array_slice($filePath, 0, count($filePath) - 1));
1151
- $thumb = $uploadsUrl . $urlPath . $thumb;
 
 
 
 
 
1152
  }
1153
  }
1154
 
@@ -1267,7 +1299,7 @@ class WPShortPixel {
1267
 
1268
  private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false) {
1269
 
1270
- //for the moment deactivate the conversion for existing images
1271
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) { //currently only for ML
1272
  $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
1273
 
@@ -1295,7 +1327,9 @@ class WPShortPixel {
1295
  if(pathinfo($mainFile, PATHINFO_EXTENSION) !== pathinfo($size['file'], PATHINFO_EXTENSION)){
1296
  continue;
1297
  }
1298
- $mimeType = $size['mime-type'];
 
 
1299
  if($size['file'] === ShortPixelAPI::MB_basename($found)) {
1300
  $foundThumbs[$id] = false;
1301
  }
@@ -1407,6 +1441,17 @@ class WPShortPixel {
1407
  die(json_encode($ret));
1408
  }
1409
 
 
 
 
 
 
 
 
 
 
 
 
1410
  /**
1411
  * to be called by thumbnail regeneration plugins when regenerating the thumbnails for an image
1412
  * @param $postId - the postId of the image
@@ -1439,6 +1484,12 @@ class WPShortPixel {
1439
  }
1440
  //wp_update_attachment_metadata($postId, $meta);
1441
  update_post_meta($postId, '_wp_attachment_metadata', $meta);
 
 
 
 
 
 
1442
  if(!$bulk) {
1443
  $this->prioQ->push($postId);
1444
  }
@@ -1565,7 +1616,9 @@ class WPShortPixel {
1565
  $baseRelPath = trailingslashit(dirname($image));
1566
  $toReplace[ShortPixelPng2Jpg::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($png2jpgMain);
1567
  foreach($sizes as $key => $size) {
1568
- $toReplace[$baseUrl . $baseRelPath . $size['file']] = $baseUrl . $baseRelPath . wp_basename($png2jpgSizes[$key]['file']);
 
 
1569
  }
1570
  $file = $png2jpgMain;
1571
  $sizes = $png2jpgSizes;
@@ -1612,7 +1665,7 @@ class WPShortPixel {
1612
  $this->throwNotice('generic-err', __("No backup files found. Restore not performed.",'shortpixel-image-optimiser'));
1613
  return false;
1614
  }
1615
- //either backups exist, or it was error so it's normal no backup is present
1616
  try {
1617
  $width = false;
1618
  if($bkCount) { // backups, if exist
@@ -1651,7 +1704,7 @@ class WPShortPixel {
1651
  if($png2jpgMain) {
1652
  $crtMeta['file'] = trailingslashit(dirname($crtMeta['file'])) . ShortPixelAPI::MB_basename($file);
1653
  update_attached_file($ID, $crtMeta['file']);
1654
- if($png2jpgSizes) {
1655
  $crtMeta['sizes'] = $png2jpgSizes;
1656
  } else {
1657
  //this was an image converted on upload, regenerate the thumbs using the PNG main image BUT deactivate temporarily the filter!!
@@ -1662,10 +1715,10 @@ class WPShortPixel {
1662
  }
1663
  //wp_update_attachment_metadata($ID, $crtMeta);
1664
  update_post_meta($ID, '_wp_attachment_metadata', $crtMeta);
 
 
 
1665
  }
1666
- unset($rawMeta["ShortPixelImprovement"]);
1667
- unset($rawMeta['ShortPixel']);
1668
- unset($rawMeta['ShortPixelPng2Jpg']);
1669
 
1670
  if($png2jpgMain) {
1671
  $spPng2Jpg = new ShortPixelPng2Jpg($this->_settings);
@@ -1816,11 +1869,13 @@ class WPShortPixel {
1816
  $this->sendToProcessing(new ShortPixelMetaFacade($ID), false, true);
1817
  $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "message" => "");
1818
  } catch(Exception $e) { // Exception("Post metadata is corrupt (No attachment URL)") or Exception("Image files are missing.")
1819
- $meta['ShortPixelImprovement'] = $e->getMessage();
1820
- $meta['ShortPixel']['ErrCode'] = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::STATUS_FAIL;
1821
- unset($meta['ShortPixel']['WaitingProcessing']);
1822
- //wp_update_attachment_metadata($ID, $meta);
1823
- update_post_meta($ID, '_wp_attachment_metadata', $meta);
 
 
1824
  $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
1825
  }
1826
  } else {
@@ -2407,6 +2462,10 @@ class WPShortPixel {
2407
  }
2408
  $_POST['key'] = SHORTPIXEL_API_KEY;
2409
  }
 
 
 
 
2410
 
2411
  //check all custom folders and update meta table if files appeared
2412
  $customFolders = $this->refreshCustomFolders($notice, isset($_POST['removeFolder']) ? $_POST['removeFolder'] : null);
@@ -2841,7 +2900,7 @@ class WPShortPixel {
2841
  if(!is_array($data)) {
2842
  $data = unserialize($data);
2843
  }
2844
- //if($extended) {var_dump(wp_get_attachment_url($id)); var_dump($data);}
2845
  $fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
2846
  $invalidKey = !$this->_settings->verifiedKey;
2847
  $quotaExceeded = $this->_settings->quotaExceeded;
@@ -3186,8 +3245,7 @@ class WPShortPixel {
3186
  if(in_array($type, array("name", "path"))) {
3187
  $pattern = trim($item["value"]);
3188
  $target = $type == "name" ? ShortPixelAPI::MB_basename($path) : $path;
3189
- if( $pattern[0] == '/' && @preg_match($pattern, false) !== false && preg_match($pattern, $target) //search as regex pattern if starts with a / and regex is valid
3190
- || $pattern[0] != '/' && strpos($target, $pattern) !== false) { //search as a substring if not
3191
  return false;
3192
  }
3193
  }
@@ -3197,7 +3255,7 @@ class WPShortPixel {
3197
  return false;
3198
  }
3199
  }
3200
-
3201
  static public function isProcessableSize($width, $height, $excludePattern) {
3202
  $ranges = preg_split("/(x|×)/",$excludePattern);
3203
  $widthBounds = explode("-", $ranges[0]);
@@ -3212,6 +3270,11 @@ class WPShortPixel {
3212
  return true;
3213
  }
3214
 
 
 
 
 
 
3215
 
3216
  //return an array with URL(s) and PATH(s) for this file
3217
  public function getURLsAndPATHs($itemHandler, $meta = NULL, $onlyThumbs = false) {
@@ -3458,6 +3521,7 @@ class WPShortPixel {
3458
  return $this->_settings->resizeHeight;
3459
  }
3460
  public static function getAffiliateSufix() {
 
3461
  // return isset($_COOKIE["AffiliateShortPixel"])
3462
  // ? "/affiliate/" . $_COOKIE["AffiliateShortPixel"]
3463
  // : (defined("SHORTPIXEL_AFFILIATE_CODE") && strlen(SHORTPIXEL_AFFILIATE_CODE) ? "/affiliate/" . SHORTPIXEL_AFFILIATE_CODE : "");
39
 
40
  define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
41
 
42
+ if(is_plugin_active('envira-gallery/envira-gallery.php') || is_plugin_active('soliloquy-lite/soliloquy-lite.php')) {
43
  define('SHORTPIXEL_CUSTOM_THUMB_SUFFIX', '_c');
44
  }
45
 
78
 
79
  //custom hook
80
  add_action( 'shortpixel-optimize-now', array( &$this, 'optimizeNowHook' ), 10, 1);
81
+
82
+ add_action( 'shortpixel-thumbnails-before-regenerate', array( &$this, 'thumbnailsBeforeRegenerateHook' ), 10, 1);
83
+ add_action( 'shortpixel-thumbnails-regenerated', array( &$this, 'thumbnailsRegeneratedHook' ), 10, 4);
84
 
85
  if($isAdminUser) {
86
  //add settings page
111
  //only if the key is not yet valid or the user hasn't bought any credits.
112
  $stats = $this->_settings->currentStats;
113
  $totalCredits = isset($stats["APICallsQuotaNumeric"]) ? $stats['APICallsQuotaNumeric'] + $stats['APICallsQuotaOneTimeNumeric'] : 0;
114
+ if(true || !$this->_settings->verifiedKey || $totalCredits < 4000) {
115
  require_once 'view/shortpixel-feedback.php';
116
  new ShortPixelFeedback( SHORTPIXEL_PLUGIN_FILE, 'shortpixel-image-optimiser', $this->_settings->apiKey, $this);
117
  }
371
  array("__SP_FIRST_TYPE__", "__SP_SECOND_TYPE__"), "__SP_CELL_MESSAGE__", 'sp-column-actions-template');
372
  }
373
  }
374
+ ?><!--SP ARCHIVE is <?php echo($this->_settings->downloadArchive . " CRC: " . crc32(get_site_url())%10); ?> -->
375
  <script type="text/javascript" >
376
  var ShortPixelConstants = {
377
  STATUS_SUCCESS: <?php echo ShortPixelAPI::STATUS_SUCCESS; ?>,
394
  AFFILIATE: '<?php echo(self::getAffiliateSufix());?>'
395
  };
396
  //check after 10 seconds if ShortPixel initialized OK, if not, force the init (could happen if a JS error somewhere else stopped the JS execution).
397
+ function delayedInit() {
398
+ if(typeof ShortPixel !== "undefined") {
399
+ ShortPixel.init();
400
+ } else {
401
+ setTimeout(delayedInit, 10000);
402
+ }
403
+ }
404
+ setTimeout(delayedInit, 10000);
405
  </script> <?php
406
  wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
407
 
563
  }
564
 
565
  /**
566
+ * Optimize a new image - usually uploaded into the Media Library
567
  * @param array $meta - the wordpress postmeta structure
568
  * @param type $ID - the Media Library ID
569
  * @return the meta structure updated with ShortPixel info if case
581
  if(isset($dbMeta['ShortPixelImprovement'])) {
582
  return $meta;
583
  }
584
+
585
+ $t = get_transient("wp-short-pixel-regenerating");
586
+ if(is_array($t) && isset($t[$ID])) {
587
+ return;
588
+ }
589
 
590
  self::log("Handle Media Library Image Upload #{$ID}");
591
  //self::log("STACK: " . json_encode(debug_backtrace()));
884
  $meta = $item->getMeta();//wp_get_attachment_metadata($crtStartQueryID);
885
 
886
  if($meta->getStatus() != 2) {
887
+ $addIt = (strpos($meta->getMessage(), __('Image files are missing.', 'shortpixel-image-optimiser')) === false);
888
+
889
+ if(!$addIt) {
890
+ //in case the message is "Image files are missing", we only add it if we could do a restore.
891
+ if ($this->doRestore($crtStartQueryID)) {
892
+ $addIt = true;
893
+ $item = new ShortPixelMetaFacade($crtStartQueryID);
894
+ }
895
+ }
896
+ if($addIt) {
897
+ $itemList[] = $item;
898
+ $idList[] = $crtStartQueryID;
899
+ if(count($itemList) > SHORTPIXEL_PRESEND_ITEMS) break;
900
+ }
901
+ }
902
  elseif($meta->getCompressionType() !== null && $meta->getCompressionType() != $this->_settings->compressionType) {//a different type of compression was chosen in settings
903
  if($this->doRestore($crtStartQueryID)) {
904
  $itemList[] = $item = new ShortPixelMetaFacade($crtStartQueryID); //force reload after restore
911
  elseif( $this->_settings->processThumbnails && $meta->getThumbsOpt() !== null
912
  && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0
913
  || $meta->getThumbsOpt() < WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes) && is_array($meta->getThumbsOptList()))) { //thumbs were chosen in settings
914
+ $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
915
+ if(count($URLsAndPATHs["URLs"])) {
916
+ $meta->setThumbsTodo(true);
917
+ $item->updateMeta($meta);//wp_update_attachment_metadata($crtStartQueryID, $meta);
918
+ $itemList[] = $item;
919
+ $idList[] = $crtStartQueryID;
920
+ if(count($itemList) > SHORTPIXEL_PRESEND_ITEMS) break;
921
+ }
922
  }
923
  elseif($itemMetaData->meta_key == '_wp_attachment_metadata') { //count skipped
924
  $skippedAlreadyProcessed++;
1037
 
1038
  //self::log("HIP: 0 Bulk ran: " . $this->prioQ->bulkRan());
1039
  $customIds = false;
1040
+ if(count($ids) < SHORTPIXEL_PRESEND_ITEMS && $this->prioQ->bulkRan() && $this->_settings->hasCustomFolders
1041
  && (!$this->_settings->cancelPointer || $this->_settings->skipToCustom)
1042
  && !$this->_settings->customBulkPaused)
1043
  { //take from custom images if any left to optimize - only if bulk was ever started
1057
  //self::log("HIP: 1 Ids: ".json_encode($ids));
1058
  if(count($ids)) {$idl='';foreach($ids as $i){$idl.=$i->getId().' ';} self::log("HIP: 1 Selected IDs: $idl");}
1059
 
1060
+ //2: Send up to SHORTPIXEL_PRESEND_ITEMS files to the server for processing
1061
+ for($i = 0, $itemHandler = false; $ids !== false && $i < min(SHORTPIXEL_PRESEND_ITEMS, count($ids)); $i++) {
1062
  $crtItemHandler = $ids[$i];
1063
  $tmpMeta = $crtItemHandler->getMeta();
1064
  $compType = ($tmpMeta->getCompressionType() !== null ? $tmpMeta->getCompressionType() : $this->_settings->compressionType);
1072
  $firstUrlAndPaths = $URLsAndPATHs;
1073
  }
1074
  } catch(Exception $e) { // Exception("Post metadata is corrupt (No attachment URL)") or Exception("Image files are missing.")
1075
+ if($tmpMeta->getStatus() != 2) {
1076
+ $crtItemHandler->incrementRetries(1, ($e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::ERR_FILE_NOT_FOUND), $e->getMessage());
1077
+ }
1078
  if(! $this->prioQ->remove($crtItemHandler->getQueuedId()) ){
1079
  $this->advanceBulk($crtItemHandler->getId());
1080
  $res['searching'] = true;
1172
  $bkThumb = $backupUrl . $urlBkPath . $thumb;
1173
  }
1174
  if(strlen($thumb)) {
1175
+ if($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
1176
+ $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
1177
+ $urlPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
1178
+ //$urlPath = implode("/", array_slice($filePath, 0, count($filePath) - 1));
1179
+ $thumb = $uploadsUrl . $urlPath . $thumb;
1180
+ } else {
1181
+ $mainUrl = ShortPixelMetaFacade::safeGetAttachmentUrl($itemHandler->getId());
1182
+ $thumb = dirname($mainUrl) . '/' . $thumb;
1183
+ }
1184
  }
1185
  }
1186
 
1299
 
1300
  private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false) {
1301
 
1302
+ //conversion of PNG 2 JPG for existing images
1303
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) { //currently only for ML
1304
  $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
1305
 
1327
  if(pathinfo($mainFile, PATHINFO_EXTENSION) !== pathinfo($size['file'], PATHINFO_EXTENSION)){
1328
  continue;
1329
  }
1330
+ if(isset($size['mime-type'])) { //situation from support case #9351 Ramesh Mehay
1331
+ $mimeType = $size['mime-type'];
1332
+ }
1333
  if($size['file'] === ShortPixelAPI::MB_basename($found)) {
1334
  $foundThumbs[$id] = false;
1335
  }
1441
  die(json_encode($ret));
1442
  }
1443
 
1444
+ /**
1445
+ * To be called by thumbnail regeneration plugins before regenerating thumbnails for an image.
1446
+ * @param $postId
1447
+ */
1448
+ public function thumbnailsBeforeRegenerateHook($postId) {
1449
+ $t = get_transient("wp-short-pixel-regenerating");
1450
+ if($t === false) $t = array();
1451
+ $t[$postId] = true;
1452
+ set_transient("wp-short-pixel-regenerating" . $t, true, 30);
1453
+ }
1454
+
1455
  /**
1456
  * to be called by thumbnail regeneration plugins when regenerating the thumbnails for an image
1457
  * @param $postId - the postId of the image
1484
  }
1485
  //wp_update_attachment_metadata($postId, $meta);
1486
  update_post_meta($postId, '_wp_attachment_metadata', $meta);
1487
+ $t = get_transient("wp-short-pixel-regenerating");
1488
+ if(is_array($t) && isset($t[$postId])) {
1489
+ unset($t[$postId]);
1490
+ set_transient("wp-short-pixel-regenerating" . $t, true, 30);
1491
+ }
1492
+
1493
  if(!$bulk) {
1494
  $this->prioQ->push($postId);
1495
  }
1616
  $baseRelPath = trailingslashit(dirname($image));
1617
  $toReplace[ShortPixelPng2Jpg::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($png2jpgMain);
1618
  foreach($sizes as $key => $size) {
1619
+ if(isset($png2jpgSizes[$key])) {
1620
+ $toReplace[$baseUrl . $baseRelPath . $size['file']] = $baseUrl . $baseRelPath . wp_basename($png2jpgSizes[$key]['file']);
1621
+ }
1622
  }
1623
  $file = $png2jpgMain;
1624
  $sizes = $png2jpgSizes;
1665
  $this->throwNotice('generic-err', __("No backup files found. Restore not performed.",'shortpixel-image-optimiser'));
1666
  return false;
1667
  }
1668
+ //either backups exist, or there was an error when trying to optimize, so it's normal no backup is present
1669
  try {
1670
  $width = false;
1671
  if($bkCount) { // backups, if exist
1704
  if($png2jpgMain) {
1705
  $crtMeta['file'] = trailingslashit(dirname($crtMeta['file'])) . ShortPixelAPI::MB_basename($file);
1706
  update_attached_file($ID, $crtMeta['file']);
1707
+ if($png2jpgSizes && count($png2jpgSizes)) {
1708
  $crtMeta['sizes'] = $png2jpgSizes;
1709
  } else {
1710
  //this was an image converted on upload, regenerate the thumbs using the PNG main image BUT deactivate temporarily the filter!!
1715
  }
1716
  //wp_update_attachment_metadata($ID, $crtMeta);
1717
  update_post_meta($ID, '_wp_attachment_metadata', $crtMeta);
1718
+ if($attachmentID == $ID) { //copy back the metadata which will be returned.
1719
+ $rawMeta = $crtMeta;
1720
+ }
1721
  }
 
 
 
1722
 
1723
  if($png2jpgMain) {
1724
  $spPng2Jpg = new ShortPixelPng2Jpg($this->_settings);
1869
  $this->sendToProcessing(new ShortPixelMetaFacade($ID), false, true);
1870
  $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "message" => "");
1871
  } catch(Exception $e) { // Exception("Post metadata is corrupt (No attachment URL)") or Exception("Image files are missing.")
1872
+ if(!isset($meta['ShortPixelImprovement']) || !is_numeric($meta['ShortPixelImprovement'])) {
1873
+ $meta['ShortPixelImprovement'] = $e->getMessage();
1874
+ $meta['ShortPixel']['ErrCode'] = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::STATUS_FAIL;
1875
+ unset($meta['ShortPixel']['WaitingProcessing']);
1876
+ //wp_update_attachment_metadata($ID, $meta);
1877
+ update_post_meta($ID, '_wp_attachment_metadata', $meta);
1878
+ }
1879
  $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
1880
  }
1881
  } else {
2462
  }
2463
  $_POST['key'] = SHORTPIXEL_API_KEY;
2464
  }
2465
+
2466
+ if(isset($_GET['setsparchive'])) {
2467
+ $this->_settings->downloadArchive = intval($_GET['setsparchive']);
2468
+ }
2469
 
2470
  //check all custom folders and update meta table if files appeared
2471
  $customFolders = $this->refreshCustomFolders($notice, isset($_POST['removeFolder']) ? $_POST['removeFolder'] : null);
2900
  if(!is_array($data)) {
2901
  $data = unserialize($data);
2902
  }
2903
+ //if($extended) {var_dump(wp_get_attachment_url($id)); echo(json_encode($data));}
2904
  $fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
2905
  $invalidKey = !$this->_settings->verifiedKey;
2906
  $quotaExceeded = $this->_settings->quotaExceeded;
3245
  if(in_array($type, array("name", "path"))) {
3246
  $pattern = trim($item["value"]);
3247
  $target = $type == "name" ? ShortPixelAPI::MB_basename($path) : $path;
3248
+ if( self::matchExcludePattern($target, $pattern) ) { //search as a substring if not
 
3249
  return false;
3250
  }
3251
  }
3255
  return false;
3256
  }
3257
  }
3258
+
3259
  static public function isProcessableSize($width, $height, $excludePattern) {
3260
  $ranges = preg_split("/(x|×)/",$excludePattern);
3261
  $widthBounds = explode("-", $ranges[0]);
3270
  return true;
3271
  }
3272
 
3273
+ static public function matchExcludePattern($target, $pattern) {
3274
+ return (
3275
+ $pattern[0] == '/' && @preg_match($pattern, false) !== false && preg_match($pattern, $target) //search as regex pattern if starts with a / and regex is valid
3276
+ || $pattern[0] != '/' && strpos($target, $pattern) !== false ); //search as a substring if not
3277
+ }
3278
 
3279
  //return an array with URL(s) and PATH(s) for this file
3280
  public function getURLsAndPATHs($itemHandler, $meta = NULL, $onlyThumbs = false) {
3521
  return $this->_settings->resizeHeight;
3522
  }
3523
  public static function getAffiliateSufix() {
3524
+ // not allowed anymore by WP as of Sept.27 2018
3525
  // return isset($_COOKIE["AffiliateShortPixel"])
3526
  // ? "/affiliate/" . $_COOKIE["AffiliateShortPixel"]
3527
  // : (defined("SHORTPIXEL_AFFILIATE_CODE") && strlen(SHORTPIXEL_AFFILIATE_CODE) ? "/affiliate/" . SHORTPIXEL_AFFILIATE_CODE : "");
class/wp-shortpixel-cloudflare-api.php CHANGED
@@ -142,6 +142,8 @@ class ShortPixelCloudFlareApi {
142
  * @return array|mixed|object - Request response as decoded JSON
143
  */
144
  private function delete_url_cache_request_action( $request_url = '', $parameters_as_json = '', $request_headers = array() ) {
 
 
145
  $curl_connection = curl_init();
146
  curl_setopt( $curl_connection, CURLOPT_URL, $request_url );
147
  curl_setopt( $curl_connection, CURLOPT_CUSTOMREQUEST, "DELETE" );
142
  * @return array|mixed|object - Request response as decoded JSON
143
  */
144
  private function delete_url_cache_request_action( $request_url = '', $parameters_as_json = '', $request_headers = array() ) {
145
+ if(!function_exists('curl_init')) return false;
146
+
147
  $curl_connection = curl_init();
148
  curl_setopt( $curl_connection, CURLOPT_URL, $request_url );
149
  curl_setopt( $curl_connection, CURLOPT_CUSTOMREQUEST, "DELETE" );
class/wp-shortpixel-settings.php CHANGED
@@ -68,6 +68,8 @@ class WPShortPixelSettings {
68
  'quotaExceeded' => array('key' => 'wp-short-pixel-quota-exceeded', 'default' => 0, 'group' => 'state'),
69
  'httpProto' => array('key' => 'wp-short-pixel-protocol', 'default' => 'https', 'group' => 'state'),
70
  'downloadProto' => array('key' => 'wp-short-pixel-download-protocol', 'default' => null, 'group' => 'state'),
 
 
71
  'mediaAlert' => array('key' => 'wp-short-pixel-media-alert', 'default' => null, 'group' => 'state'),
72
  'dismissedNotices' => array('key' => 'wp-short-pixel-dismissed-notices', 'default' => array(), 'group' => 'state'),
73
  'activationDate' => array('key' => 'wp-short-pixel-activation-date', 'default' => null, 'group' => 'state'),
@@ -121,6 +123,10 @@ class WPShortPixelSettings {
121
  foreach(self::$_optionsMap as $opt) {
122
  self::getOpt($opt['key'], $opt['default']);
123
  }
 
 
 
 
124
  }
125
 
126
  public static function debugResetOptions() {
68
  'quotaExceeded' => array('key' => 'wp-short-pixel-quota-exceeded', 'default' => 0, 'group' => 'state'),
69
  'httpProto' => array('key' => 'wp-short-pixel-protocol', 'default' => 'https', 'group' => 'state'),
70
  'downloadProto' => array('key' => 'wp-short-pixel-download-protocol', 'default' => null, 'group' => 'state'),
71
+ //TODO downloadArchive initial sa fie 10% - hash pe numele de domeniu
72
+ 'downloadArchive' => array('key' => 'wp-short-pixel-download-archive', 'default' => -1, 'group' => 'state'),
73
  'mediaAlert' => array('key' => 'wp-short-pixel-media-alert', 'default' => null, 'group' => 'state'),
74
  'dismissedNotices' => array('key' => 'wp-short-pixel-dismissed-notices', 'default' => array(), 'group' => 'state'),
75
  'activationDate' => array('key' => 'wp-short-pixel-activation-date', 'default' => null, 'group' => 'state'),
123
  foreach(self::$_optionsMap as $opt) {
124
  self::getOpt($opt['key'], $opt['default']);
125
  }
126
+
127
+ if(self::getOpt("downloadArchive") == -1) {
128
+ self::setOpt(self::$_optionsMap["downloadArchive"]['key'], crc32(get_site_url())%10);
129
+ }
130
  }
131
 
132
  public static function debugResetOptions() {
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: ShortPixel
3
  Tags: compressor, image, compression, optimize, image optimizer, image optimiser, image compression, resize, compress pdf, compress jpg, compress png, image compression
4
  Requires at least: 3.2.0
5
- Tested up to: 4.9
6
  Requires PHP: 5.2
7
- Stable tag: 4.11.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -63,7 +63,7 @@ Make an instant <a href="http://shortpixel.com/image-compression-test" target="_
63
  * **free optimization credits for non-profits**, <a href="https://shortpixel.com/contact" target="_blank">contact us</a> for details
64
 
65
  **How much does it cost?**
66
- ShortPixel comes with 100 free credits/month and additional credits can be bought with as little as $4.99 for 5,000 image credits.
67
  Check out <a href="https://shortpixel.com/pricing" target="_blank">our prices</a>.
68
 
69
  > **Testimonials:**
@@ -241,6 +241,22 @@ The ShortPixel Image Optimiser plugin calls the following actions and filters:
241
 
242
  == Changelog ==
243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  = 4.11.3 =
245
 
246
  Release date: 27th September 2018
@@ -250,7 +266,7 @@ Release date: 27th September 2018
250
 
251
  = 4.11.2 =
252
 
253
- Release date: 20th August 2018
254
 
255
  * Fix "Image files are missing" warning when thumbails optimization is activated but all the thumbnails are excepted from optimization and the bulk is ran a second time.
256
  * Fix not saving properly the metadata on some situations
2
  Contributors: ShortPixel
3
  Tags: compressor, image, compression, optimize, image optimizer, image optimiser, image compression, resize, compress pdf, compress jpg, compress png, image compression
4
  Requires at least: 3.2.0
5
+ Tested up to: 5.0
6
  Requires PHP: 5.2
7
+ Stable tag: 4.12.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
63
  * **free optimization credits for non-profits**, <a href="https://shortpixel.com/contact" target="_blank">contact us</a> for details
64
 
65
  **How much does it cost?**
66
+ ShortPixel comes with 100 free credits/month and additional credits can be bought for as little as $4.99 for 5,000 image credits.
67
  Check out <a href="https://shortpixel.com/pricing" target="_blank">our prices</a>.
68
 
69
  > **Testimonials:**
241
 
242
  == Changelog ==
243
 
244
+ = 4.12.0 =
245
+
246
+ Release date: 31st October 2018
247
+
248
+ * Generate WebP &lt;picture&gt; tags - use the output buffer instead of the_content which is not triggered by some themes on all content.
249
+ * compatibility of the WebP &lt;picture&gt; tag with lazy loading plugins (that support &lt;picture&gt;)
250
+ * Compatibility with Polylang.
251
+ * hooks to be used by thumbnail regeneration plugins: 'shortpixel-thumbnails-before-regenerate' and 'shortpixel-thumbnails-regenerated'
252
+ * Proper error message when the custom tables cannot be created.
253
+ * exclude the PNGs from conversion to JPEG when they match the exclude patterns.
254
+ * properly warn when cURL is not enabled that Cloudflare integration won't work.
255
+ * send only one url for metadata thumbnails which correspond to the same physical file.
256
+ * JavaScript delayed init for cases when some plugins deffer the load of javascript files.
257
+ * fix identifying filenames with basename length == 3 as retina
258
+ * display improvements for the bulk errors list
259
+
260
  = 4.11.3 =
261
 
262
  Release date: 27th September 2018
266
 
267
  = 4.11.2 =
268
 
269
+ Release date: 30th August 2018
270
 
271
  * Fix "Image files are missing" warning when thumbails optimization is activated but all the thumbnails are excepted from optimization and the bulk is ran a second time.
272
  * Fix not saving properly the metadata on some situations
res/css/short-pixel.css CHANGED
@@ -81,6 +81,9 @@ div.fb-like {
81
  .sp-notice-success {
82
  border-left-color: #46b450;
83
  }
 
 
 
84
  li.sp-conflict-plugins-list {
85
  line-height: 28px;
86
  list-style: disc;
@@ -161,6 +164,24 @@ div.sp-bulk-summary {
161
  float:right;
162
  margin:8px 5px 3px 20px;
163
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  input.dial {
165
  box-shadow: none;
166
  }
@@ -442,13 +463,14 @@ th.sorted.column-wp-shortPixel a {
442
  width: 80px;
443
  text-align: right;
444
  }
445
- .progress {
446
  background-color: #ecedee;
447
  height: 30px;
448
  position: relative;
449
  width: 60%;
450
  display: inline-block;
451
  margin-right: 28px;
 
452
  }
453
  .progress .progress-img {
454
  position: absolute;
81
  .sp-notice-success {
82
  border-left-color: #46b450;
83
  }
84
+ .sp-notice-warning {
85
+ border-left-color: #f1e02a;
86
+ }
87
  li.sp-conflict-plugins-list {
88
  line-height: 28px;
89
  list-style: disc;
164
  float:right;
165
  margin:8px 5px 3px 20px;
166
  }
167
+ .sp-notice .bulk-error-show {
168
+ cursor: pointer;
169
+ }
170
+ .sp-notice div.bulk-error-list {
171
+ background-color: #f1f1f1;
172
+ padding: 0 10px;
173
+ display: none;
174
+ max-height: 200px;
175
+ overflow-y: scroll;
176
+ }
177
+ .sp-notice div.bulk-error-list ul {
178
+ padding: 3px 0 0;
179
+ margin-top: 5px;
180
+ }
181
+ .sp-notice div.bulk-error-list ul > li:not(:last-child) {
182
+ border-bottom: 1px solid white;
183
+ padding-bottom: 4px;
184
+ }
185
  input.dial {
186
  box-shadow: none;
187
  }
463
  width: 80px;
464
  text-align: right;
465
  }
466
+ .short-pixel-bulk-page .progress {
467
  background-color: #ecedee;
468
  height: 30px;
469
  position: relative;
470
  width: 60%;
471
  display: inline-block;
472
  margin-right: 28px;
473
+ overflow: visible;
474
  }
475
  .progress .progress-img {
476
  position: absolute;
res/css/short-pixel.min.css CHANGED
@@ -1 +1 @@
1
- .sp-dropbtn.button{padding:1px 24px 20px 5px;font-size:20px;cursor:pointer}.sp-dropdown{position:relative;display:inline-block}.sp-dropdown-content{display:none;right:0;position:absolute;background-color:#f9f9f9;min-width:190px;box-shadow:0 8px 16px 0 rgba(0,0,0,0.2);z-index:1}.sp-dropdown-content a{color:black;padding:12px 16px;text-decoration:none;display:block}.sp-dropdown-content a:hover{background-color:#f1f1f1}.sp-dropdown.sp-show .sp-dropdown-content{display:block}div.fb-like{transform:scale(1.3);-ms-transform:scale(1.3);-webkit-transform:scale(1.3);-o-transform:scale(1.3);-moz-transform:scale(1.3);transform-origin:bottom left;-ms-transform-origin:bottom left;-webkit-transform-origin:bottom left;-moz-transform-origin:bottom left;-webkit-transform-origin:bottom left}.wp-core-ui .button.button-alert,.wp-core-ui .button.button-alert:hover{background:#f79797}.wp-core-ui .button.remove-folder-button{min-width:120px}.sp-notice{background:#fff;border-left:4px solid #fff;-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);box-shadow:0 1px 1px 0 rgba(0,0,0,.1);padding:1px 12px}@media(max-width:1249px){.sp-notice{margin:5px 15px 2px}}.sp-notice-info{border-left-color:#00a0d2}.sp-notice-success{border-left-color:#46b450}li.sp-conflict-plugins-list{line-height:28px;list-style:disc;margin-left:80px}li.sp-conflict-plugins-list a.button{margin-left:10px}div.short-pixel-bulk-page input.dial{font-size:16px !important}div.short-pixel-bulk-page h1{margin-bottom:20px}div.bulk-progress div.sp-h2{margin-top:0;margin-bottom:10px;font-size:23px;font-weight:400;padding:9px 15px 4px 0;line-height:29px}div.bulk-progress-partners{margin-top:20px}div.bulk-progress.bulk-progress-partners a div{display:inline-block;vertical-align:top;line-height:50px;margin-left:30px;font-size:1.2em}div.bulk-progress .bulk-progress-indicator,div.sp-quota-exceeded-alert .bulk-progress-indicator{display:inline-block;text-align:center;padding:0 10px;margin-left:10px;float:left;height:90px;overflow:hidden;border:1px solid #1caecb}div.wrap.short-pixel-bulk-page .bulk-notice-container{margin-top:15px;position:absolute;width:500px}div.wrap.short-pixel-bulk-page .bulk-notice-container .bulk-notice-msg{text-align:center;margin:10px 0 0 32px;overflow:hidden;border:1px solid #1caecb;background-color:#9ddbe0;border-radius:5px;padding:7px 10px 10px;display:none;max-width:600px;margin-right:20px}div.wrap.short-pixel-bulk-page .bulk-notice-container .bulk-notice-msg.bulk-error{border:1px solid #b5914d;background-color:#ffe996;margin-right:20px;position:relative;z-index:10}div.wrap.short-pixel-bulk-page .bulk-notice-container .bulk-notice-msg.bulk-error.bulk-error-fatal{border:1px solid #c32525;background-color:#ff969d}div.wrap.short-pixel-bulk-page .bulk-notice-msg img{float:left;margin-top:3px;margin-right:5px}div.sp-bulk-summary{float:right;margin:8px 5px 3px 20px}input.dial{box-shadow:none}.shortpixel-table .column-filename{max-width:32em;width:40%}.shortpixel-table .column-folder{max-width:20em;width:20%}.shortpixel-table .column-media_type{max-width:8em;width:10%}.shortpixel-table .column-status{max-width:16em;width:15%}.shortpixel-table .column-options{max-width:16em;width:15%}.form-table table.shortpixel-folders-list tr{background-color:#eee}.form-table table.shortpixel-folders-list td{padding:5px 10px}div.shortpixel-rate-us{display:inline-block;margin-left:10px;vertical-align:top;font-weight:bold}div.shortpixel-rate-us>a{vertical-align:middle;padding:1px 5px 0;text-align:center;display:inline-block}div.shortpixel-rate-us>a>span{display:inline-block;vertical-align:top;margin-top:5px}div.shortpixel-rate-us>a>img{padding-top:7px}div.shortpixel-rate-us>a:active,div.shortpixel-rate-us>a:hover,div.shortpixel-rate-us>a:focus{outline:0;border-style:none}.sp-loading-small{margin-top:2px;float:left;margin-right:5px}li.shortpixel-toolbar-processing>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div{height:33px;margin-top:-1px;padding:0 3px}li.shortpixel-toolbar-processing>a.ab-item>div>img,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>img{margin-right:2px;margin-top:6px}li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert{display:none}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert{display:inline;font-size:26px;color:red;font-weight:bold;vertical-align:top}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div{background-image:none}.sp-quota-exceeded-alert{background-color:#fff;border-left:4px solid red;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);padding:1px 12px}.shortpixel-clearfix{width:100%;float:left}div.sp-modal-shade{display:none;position:fixed;z-index:10;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#000;background-color:rgba(0,0,0,0.4)}div.shortpixel-modal{background-color:#fefefe;margin:8% auto;padding:20px;border:1px solid #888;width:30%;min-width:300px}div.shortpixel-modal .sp-close-button,div.shortpixel-modal .sp-close-upgrade-button{float:right;margin-top:0;background:transparent;border:0;font-size:22px;line-height:10px;cursor:pointer}.twentytwenty-horizontal .twentytwenty-before-label:before,.twentytwenty-horizontal .twentytwenty-after-label:before{font-family:inherit;font-size:16px}div.sp-modal-title{font-size:22px}div.sp-modal-body{margin-top:20px}.short-pixel-bulk-page p{margin:.6em 0}div.shortpixel-modal .sptw-modal-spinner{background-image:url("../img/spinner2.gif");background-repeat:no-repeat;background-position:center}.short-pixel-bulk-page form.start{display:table;content:" ";width:98%;background-color:white;padding:10px 10px 0;position:relative}.bulk-stats-container{display:inline-block;min-width:450px;width:45%;float:left;padding-right:50px;font-size:1.1em;line-height:1.5em}.bulk-text-container{display:inline-block;min-width:440px;width:45%;float:left;padding-right:50px}.bulk-text-container h3{border-bottom:1px solid #a8a8a8;margin-bottom:.5em;padding-bottom:.5em}.bulk-wide{display:inline-block;width:90%;float:left;margin-top:25px}.bulk-stats-container .bulk-label{width:220px;display:inline-block}.bulk-stats-container .bulk-val{width:50px;display:inline-block;text-align:right}.bulk-stats-container .bulk-total{font-weight:bold;margin-top:10px;margin-bottom:10px}.wp-core-ui .bulk-play{display:inline;width:310px;float:left;margin-bottom:20px}.wp-core-ui .bulk-play.bulk-nothing-optimize{font-weight:bold;color:#0080b2;border:1px solid;border-radius:5px;margin-top:60px;padding:5px 12px}.wp-core-ui .bulk-play a.button{height:60px;margin-top:27px;width:290px;overflow:hidden}.wp-core-ui .column-wp-shortPixel .sp-column-actions{max-width:140px;float:right;text-align:right}.wp-core-ui .column-wp-shortPixel .sp-column-actions .button.button-smaller{margin-right:0}.wp-core-ui .column-wp-shortPixel .button.button-smaller{font-size:13px;padding:0 5px;margin-bottom:4px;height:20px;line-height:16px;float:right}th.sortable.column-wp-shortPixel a,th.sorted.column-wp-shortPixel a{display:inline-block}.column-wp-shortPixel .sorting-indicator{display:inline-block}.wp-core-ui .bulk-play a.button .bulk-btn-img{float:left;display:inline-block;padding-top:6px}.wp-core-ui .bulk-play a.button .bulk-btn-txt{float:left;display:inline-block;text-align:right;line-height:1.3em;margin:11px 10px}.wp-core-ui .bulk-play a.button .bulk-btn-txt span.label{font-size:1.6em}.wp-core-ui .bulk-play a.button .bulk-btn-txt span.total{font-size:1.4em}.short-pixel-notice-icon{float:left;margin:10px 10px 10px 0}.bulk-progress{padding:20px 32px 17px;background-color:#fff}.bulk-progress.bulk-stats>div{display:inline-block}.bulk-progress.bulk-stats>div.label{width:320px}.bulk-progress.bulk-stats>div.stat-value{width:80px;text-align:right}.progress{background-color:#ecedee;height:30px;position:relative;width:60%;display:inline-block;margin-right:28px}.progress .progress-img{position:absolute;top:-10px;z-index:2;margin-left:-35px;line-height:48px;font-size:22px;font-weight:bold}.progress .progress-img span{vertical-align:top;margin-left:-7px}.progress .progress-left{background-color:#1cbecb;bottom:0;left:0;position:absolute;top:0;z-index:1;font-size:22px;font-weight:bold;line-height:28px;text-align:center;color:#fff}.bulk-estimate{font-size:20px;line-height:30px;vertical-align:top;display:inline-block}.wp-core-ui .button-primary.bulk-cancel{float:right;height:30px}.short-pixel-block-title{font-size:22px;font-weight:bold;margin-bottom:15px;text-align:center;margin-bottom:30px}.sp-floating-block.bulk-slider-container{display:none}.sp-floating-block.sp-notice.bulk-notices-parent{padding:0;margin:0;float:right;margin-right:500px !important}.bulk-slider-container{margin-top:20px;min-height:300px;overflow:hidden}.bulk-slider-container h2{margin-bottom:15px}.bulk-slider-container span.filename{font-weight:normal}.bulk-slider{display:table;margin:0 auto}.bulk-slider .bulk-slide{margin:0 auto;padding-left:120px;display:inline-block;font-weight:bold}.bulk-slider .img-original,.bulk-slider .img-optimized{display:inline-block;margin-right:20px;text-align:center}.bulk-slider .img-original div,.bulk-slider .img-optimized div{max-height:450px;overflow:hidden}.bulk-slider .img-original img,.bulk-slider .img-optimized img{max-width:300px}.bulk-slider .img-info{display:inline-block;vertical-align:top;font-size:48px;max-width:150px;padding:10px 0 0 20px}.bulk-slide-images{display:inline-block;border:1px solid #1caecb;padding:15px 0 0 20px}p.settings-info{padding-top:0;color:#818181;font-size:13px !important}p.settings-info.shortpixel-settings-error{color:#c32525}.shortpixel-key-valid{font-weight:bold}.shortpixel-key-valid .dashicons-yes:before{font-size:2em;line-height:25px;color:#3485ba;margin-left:-20px}.shortpixel-compression .shortpixel-compression-options{color:#999}.shortpixel-compression strong{line-height:22px}.shortpixel-compression .shortpixel-compression-options{display:inline-block}.shortpixel-compression label{width:158px;margin:0 -2px;background-color:#e2faff;font-weight:bold;display:inline-block}.shortpixel-compression label span{text-align:center;font-size:18px;padding:8px 0;display:block}.shortpixel-compression label input{display:none}.shortpixel-compression input:checked+span{background-color:#0085ba;color:#f7f7f7}.shortpixel-compression .shortpixel-radio-info{min-height:60px}article.sp-tabs{position:relative;display:block;width:100%;height:1100px;margin:2em auto}article.sp-tabs section{position:absolute;display:block;top:1.8em;left:0;height:1100px;width:94%;padding:10px 20px;background-color:#ddd;box-shadow:0 3px 3px rgba(0,0,0,0.1);z-index:0}article.sp-tabs section:first-child{z-index:1}article.sp-tabs section h2{position:absolute;font-size:1.3em;font-weight:normal;width:180px;height:1.8em;top:-1.8em;left:10px;padding:0;margin:0;color:#999;background-color:#ddd}article.sp-tabs section:nth-child(2) h2{left:192px}article.sp-tabs section:nth-child(3) h2{left:374px}article.sp-tabs section:nth-child(4) h2{left:556px}article.sp-tabs section:nth-child(5) h2{left:738px}article.sp-tabs section h2 a{display:block;width:100%;line-height:1.8em;text-align:center;text-decoration:none;color:#23282d;outline:0 none}article.sp-tabs section h2 a:focus,article.sp-tabs section#tab-resources a:focus{box-shadow:none;outline:0}article.sp-tabs section.sel-tab,article.sp-tabs section.sel-tab h2{color:#333;background-color:#fff;z-index:2}.shortpixel-help-link span.dashicons{text-decoration:none;margin-top:-1px}@media(min-width:1000px){section#tab-resources .col-md-6{display:inline-block;width:45%}}@media(max-width:999px){section#tab-resources .col-sm-12{display:inline-block;width:100%}}section#tab-resources .text-center{text-align:center}section#tab-resources p{font-size:16px}.wrap.short-pixel-bulk-page{margin-right:0}.sp-container{overflow:hidden;display:block;width:100%}.sp-floating-block{overflow:hidden;display:inline-block;float:left;margin-right:1.1% !important}.sp-full-width{width:98.8%;box-sizing:border-box}.sp-double-width{width:65.52%;box-sizing:border-box}.sp-single-width{width:32.23%;box-sizing:border-box}@media(max-width:1759px){.sp-floating-block{margin-right:1.3% !important}.sp-double-width,.sp-full-width{width:98.65%}.sp-single-width{width:48.7%}}@media(max-width:1249px){.sp-floating-block{margin-right:2% !important}.sp-double-width,.sp-full-width,.sp-single-width{width:97%}}.sp-tabs h2:before{content:none}#wpadminbar .shortpixel-toolbar-processing .cssload-container{width:100%;height:24px;text-align:center;position:absolute;top:0;left:-1px}#wpadminbar .shortpixel-toolbar-processing.shortpixel-quota-exceeded .cssload-container,#wpadminbar .shortpixel-toolbar-processing.shortpixel-alert .cssload-container{display:none}#wpadminbar .shortpixel-toolbar-processing .cssload-speeding-wheel{width:24px;height:24px;opacity:.7;margin:0 auto;border:4px solid #1cbfcb;border-radius:50%;border-left-color:transparent;animation:cssload-spin 2000ms infinite linear;-o-animation:cssload-spin 2000ms infinite linear;-ms-animation:cssload-spin 2000ms infinite linear;-webkit-animation:cssload-spin 2000ms infinite linear;-moz-animation:cssload-spin 2000ms infinite linear}@keyframes cssload-spin{100%{transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes cssload-spin{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes cssload-spin{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes cssload-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes cssload-spin{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}
1
+ .sp-dropbtn.button{padding:1px 24px 20px 5px;font-size:20px;cursor:pointer}.sp-dropdown{position:relative;display:inline-block}.sp-dropdown-content{display:none;right:0;position:absolute;background-color:#f9f9f9;min-width:190px;box-shadow:0 8px 16px 0 rgba(0,0,0,0.2);z-index:1}.sp-dropdown-content a{color:black;padding:12px 16px;text-decoration:none;display:block}.sp-dropdown-content a:hover{background-color:#f1f1f1}.sp-dropdown.sp-show .sp-dropdown-content{display:block}div.fb-like{transform:scale(1.3);-ms-transform:scale(1.3);-webkit-transform:scale(1.3);-o-transform:scale(1.3);-moz-transform:scale(1.3);transform-origin:bottom left;-ms-transform-origin:bottom left;-webkit-transform-origin:bottom left;-moz-transform-origin:bottom left;-webkit-transform-origin:bottom left}.wp-core-ui .button.button-alert,.wp-core-ui .button.button-alert:hover{background:#f79797}.wp-core-ui .button.remove-folder-button{min-width:120px}.sp-notice{background:#fff;border-left:4px solid #fff;-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);box-shadow:0 1px 1px 0 rgba(0,0,0,.1);padding:1px 12px}@media(max-width:1249px){.sp-notice{margin:5px 15px 2px}}.sp-notice-info{border-left-color:#00a0d2}.sp-notice-success{border-left-color:#46b450}.sp-notice-warning{border-left-color:#f1e02a}li.sp-conflict-plugins-list{line-height:28px;list-style:disc;margin-left:80px}li.sp-conflict-plugins-list a.button{margin-left:10px}div.short-pixel-bulk-page input.dial{font-size:16px !important}div.short-pixel-bulk-page h1{margin-bottom:20px}div.bulk-progress div.sp-h2{margin-top:0;margin-bottom:10px;font-size:23px;font-weight:400;padding:9px 15px 4px 0;line-height:29px}div.bulk-progress-partners{margin-top:20px}div.bulk-progress.bulk-progress-partners a div{display:inline-block;vertical-align:top;line-height:50px;margin-left:30px;font-size:1.2em}div.bulk-progress .bulk-progress-indicator,div.sp-quota-exceeded-alert .bulk-progress-indicator{display:inline-block;text-align:center;padding:0 10px;margin-left:10px;float:left;height:90px;overflow:hidden;border:1px solid #1caecb}div.wrap.short-pixel-bulk-page .bulk-notice-container{margin-top:15px;position:absolute;width:500px}div.wrap.short-pixel-bulk-page .bulk-notice-container .bulk-notice-msg{text-align:center;margin:10px 0 0 32px;overflow:hidden;border:1px solid #1caecb;background-color:#9ddbe0;border-radius:5px;padding:7px 10px 10px;display:none;max-width:600px;margin-right:20px}div.wrap.short-pixel-bulk-page .bulk-notice-container .bulk-notice-msg.bulk-error{border:1px solid #b5914d;background-color:#ffe996;margin-right:20px;position:relative;z-index:10}div.wrap.short-pixel-bulk-page .bulk-notice-container .bulk-notice-msg.bulk-error.bulk-error-fatal{border:1px solid #c32525;background-color:#ff969d}div.wrap.short-pixel-bulk-page .bulk-notice-msg img{float:left;margin-top:3px;margin-right:5px}div.sp-bulk-summary{float:right;margin:8px 5px 3px 20px}.sp-notice .bulk-error-show{cursor:pointer}.sp-notice div.bulk-error-list{background-color:#f1f1f1;padding:0 10px;display:none;max-height:200px;overflow-y:scroll}.sp-notice div.bulk-error-list ul{padding:3px 0 0;margin-top:5px}.sp-notice div.bulk-error-list ul>li:not(:last-child){border-bottom:1px solid white;padding-bottom:4px}input.dial{box-shadow:none}.shortpixel-table .column-filename{max-width:32em;width:40%}.shortpixel-table .column-folder{max-width:20em;width:20%}.shortpixel-table .column-media_type{max-width:8em;width:10%}.shortpixel-table .column-status{max-width:16em;width:15%}.shortpixel-table .column-options{max-width:16em;width:15%}.form-table table.shortpixel-folders-list tr{background-color:#eee}.form-table table.shortpixel-folders-list td{padding:5px 10px}div.shortpixel-rate-us{display:inline-block;margin-left:10px;vertical-align:top;font-weight:bold}div.shortpixel-rate-us>a{vertical-align:middle;padding:1px 5px 0;text-align:center;display:inline-block}div.shortpixel-rate-us>a>span{display:inline-block;vertical-align:top;margin-top:5px}div.shortpixel-rate-us>a>img{padding-top:7px}div.shortpixel-rate-us>a:active,div.shortpixel-rate-us>a:hover,div.shortpixel-rate-us>a:focus{outline:0;border-style:none}.sp-loading-small{margin-top:2px;float:left;margin-right:5px}li.shortpixel-toolbar-processing>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div{height:33px;margin-top:-1px;padding:0 3px}li.shortpixel-toolbar-processing>a.ab-item>div>img,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>img{margin-right:2px;margin-top:6px}li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert{display:none}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert{display:inline;font-size:26px;color:red;font-weight:bold;vertical-align:top}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div{background-image:none}.sp-quota-exceeded-alert{background-color:#fff;border-left:4px solid red;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);padding:1px 12px}.shortpixel-clearfix{width:100%;float:left}div.sp-modal-shade{display:none;position:fixed;z-index:10;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#000;background-color:rgba(0,0,0,0.4)}div.shortpixel-modal{background-color:#fefefe;margin:8% auto;padding:20px;border:1px solid #888;width:30%;min-width:300px}div.shortpixel-modal .sp-close-button,div.shortpixel-modal .sp-close-upgrade-button{float:right;margin-top:0;background:transparent;border:0;font-size:22px;line-height:10px;cursor:pointer}.twentytwenty-horizontal .twentytwenty-before-label:before,.twentytwenty-horizontal .twentytwenty-after-label:before{font-family:inherit;font-size:16px}div.sp-modal-title{font-size:22px}div.sp-modal-body{margin-top:20px}.short-pixel-bulk-page p{margin:.6em 0}div.shortpixel-modal .sptw-modal-spinner{background-image:url("../img/spinner2.gif");background-repeat:no-repeat;background-position:center}.short-pixel-bulk-page form.start{display:table;content:" ";width:98%;background-color:white;padding:10px 10px 0;position:relative}.bulk-stats-container{display:inline-block;min-width:450px;width:45%;float:left;padding-right:50px;font-size:1.1em;line-height:1.5em}.bulk-text-container{display:inline-block;min-width:440px;width:45%;float:left;padding-right:50px}.bulk-text-container h3{border-bottom:1px solid #a8a8a8;margin-bottom:.5em;padding-bottom:.5em}.bulk-wide{display:inline-block;width:90%;float:left;margin-top:25px}.bulk-stats-container .bulk-label{width:220px;display:inline-block}.bulk-stats-container .bulk-val{width:50px;display:inline-block;text-align:right}.bulk-stats-container .bulk-total{font-weight:bold;margin-top:10px;margin-bottom:10px}.wp-core-ui .bulk-play{display:inline;width:310px;float:left;margin-bottom:20px}.wp-core-ui .bulk-play.bulk-nothing-optimize{font-weight:bold;color:#0080b2;border:1px solid;border-radius:5px;margin-top:60px;padding:5px 12px}.wp-core-ui .bulk-play a.button{height:60px;margin-top:27px;width:290px;overflow:hidden}.wp-core-ui .column-wp-shortPixel .sp-column-actions{max-width:140px;float:right;text-align:right}.wp-core-ui .column-wp-shortPixel .sp-column-actions .button.button-smaller{margin-right:0}.wp-core-ui .column-wp-shortPixel .button.button-smaller{font-size:13px;padding:0 5px;margin-bottom:4px;height:20px;line-height:16px;float:right}th.sortable.column-wp-shortPixel a,th.sorted.column-wp-shortPixel a{display:inline-block}.column-wp-shortPixel .sorting-indicator{display:inline-block}.wp-core-ui .bulk-play a.button .bulk-btn-img{float:left;display:inline-block;padding-top:6px}.wp-core-ui .bulk-play a.button .bulk-btn-txt{float:left;display:inline-block;text-align:right;line-height:1.3em;margin:11px 10px}.wp-core-ui .bulk-play a.button .bulk-btn-txt span.label{font-size:1.6em}.wp-core-ui .bulk-play a.button .bulk-btn-txt span.total{font-size:1.4em}.short-pixel-notice-icon{float:left;margin:10px 10px 10px 0}.bulk-progress{padding:20px 32px 17px;background-color:#fff}.bulk-progress.bulk-stats>div{display:inline-block}.bulk-progress.bulk-stats>div.label{width:320px}.bulk-progress.bulk-stats>div.stat-value{width:80px;text-align:right}.short-pixel-bulk-page .progress{background-color:#ecedee;height:30px;position:relative;width:60%;display:inline-block;margin-right:28px;overflow:visible}.progress .progress-img{position:absolute;top:-10px;z-index:2;margin-left:-35px;line-height:48px;font-size:22px;font-weight:bold}.progress .progress-img span{vertical-align:top;margin-left:-7px}.progress .progress-left{background-color:#1cbecb;bottom:0;left:0;position:absolute;top:0;z-index:1;font-size:22px;font-weight:bold;line-height:28px;text-align:center;color:#fff}.bulk-estimate{font-size:20px;line-height:30px;vertical-align:top;display:inline-block}.wp-core-ui .button-primary.bulk-cancel{float:right;height:30px}.short-pixel-block-title{font-size:22px;font-weight:bold;margin-bottom:15px;text-align:center;margin-bottom:30px}.sp-floating-block.bulk-slider-container{display:none}.sp-floating-block.sp-notice.bulk-notices-parent{padding:0;margin:0;float:right;margin-right:500px !important}.bulk-slider-container{margin-top:20px;min-height:300px;overflow:hidden}.bulk-slider-container h2{margin-bottom:15px}.bulk-slider-container span.filename{font-weight:normal}.bulk-slider{display:table;margin:0 auto}.bulk-slider .bulk-slide{margin:0 auto;padding-left:120px;display:inline-block;font-weight:bold}.bulk-slider .img-original,.bulk-slider .img-optimized{display:inline-block;margin-right:20px;text-align:center}.bulk-slider .img-original div,.bulk-slider .img-optimized div{max-height:450px;overflow:hidden}.bulk-slider .img-original img,.bulk-slider .img-optimized img{max-width:300px}.bulk-slider .img-info{display:inline-block;vertical-align:top;font-size:48px;max-width:150px;padding:10px 0 0 20px}.bulk-slide-images{display:inline-block;border:1px solid #1caecb;padding:15px 0 0 20px}p.settings-info{padding-top:0;color:#818181;font-size:13px !important}p.settings-info.shortpixel-settings-error{color:#c32525}.shortpixel-key-valid{font-weight:bold}.shortpixel-key-valid .dashicons-yes:before{font-size:2em;line-height:25px;color:#3485ba;margin-left:-20px}.shortpixel-compression .shortpixel-compression-options{color:#999}.shortpixel-compression strong{line-height:22px}.shortpixel-compression .shortpixel-compression-options{display:inline-block}.shortpixel-compression label{width:158px;margin:0 -2px;background-color:#e2faff;font-weight:bold;display:inline-block}.shortpixel-compression label span{text-align:center;font-size:18px;padding:8px 0;display:block}.shortpixel-compression label input{display:none}.shortpixel-compression input:checked+span{background-color:#0085ba;color:#f7f7f7}.shortpixel-compression .shortpixel-radio-info{min-height:60px}article.sp-tabs{position:relative;display:block;width:100%;height:1100px;margin:2em auto}article.sp-tabs section{position:absolute;display:block;top:1.8em;left:0;height:1100px;width:94%;padding:10px 20px;background-color:#ddd;box-shadow:0 3px 3px rgba(0,0,0,0.1);z-index:0}article.sp-tabs section:first-child{z-index:1}article.sp-tabs section h2{position:absolute;font-size:1.3em;font-weight:normal;width:180px;height:1.8em;top:-1.8em;left:10px;padding:0;margin:0;color:#999;background-color:#ddd}article.sp-tabs section:nth-child(2) h2{left:192px}article.sp-tabs section:nth-child(3) h2{left:374px}article.sp-tabs section:nth-child(4) h2{left:556px}article.sp-tabs section:nth-child(5) h2{left:738px}article.sp-tabs section h2 a{display:block;width:100%;line-height:1.8em;text-align:center;text-decoration:none;color:#23282d;outline:0 none}article.sp-tabs section h2 a:focus,article.sp-tabs section#tab-resources a:focus{box-shadow:none;outline:0}article.sp-tabs section.sel-tab,article.sp-tabs section.sel-tab h2{color:#333;background-color:#fff;z-index:2}.shortpixel-help-link span.dashicons{text-decoration:none;margin-top:-1px}@media(min-width:1000px){section#tab-resources .col-md-6{display:inline-block;width:45%}}@media(max-width:999px){section#tab-resources .col-sm-12{display:inline-block;width:100%}}section#tab-resources .text-center{text-align:center}section#tab-resources p{font-size:16px}.wrap.short-pixel-bulk-page{margin-right:0}.sp-container{overflow:hidden;display:block;width:100%}.sp-floating-block{overflow:hidden;display:inline-block;float:left;margin-right:1.1% !important}.sp-full-width{width:98.8%;box-sizing:border-box}.sp-double-width{width:65.52%;box-sizing:border-box}.sp-single-width{width:32.23%;box-sizing:border-box}@media(max-width:1759px){.sp-floating-block{margin-right:1.3% !important}.sp-double-width,.sp-full-width{width:98.65%}.sp-single-width{width:48.7%}}@media(max-width:1249px){.sp-floating-block{margin-right:2% !important}.sp-double-width,.sp-full-width,.sp-single-width{width:97%}}.sp-tabs h2:before{content:none}#wpadminbar .shortpixel-toolbar-processing .cssload-container{width:100%;height:24px;text-align:center;position:absolute;top:0;left:-1px}#wpadminbar .shortpixel-toolbar-processing.shortpixel-quota-exceeded .cssload-container,#wpadminbar .shortpixel-toolbar-processing.shortpixel-alert .cssload-container{display:none}#wpadminbar .shortpixel-toolbar-processing .cssload-speeding-wheel{width:24px;height:24px;opacity:.7;margin:0 auto;border:4px solid #1cbfcb;border-radius:50%;border-left-color:transparent;animation:cssload-spin 2000ms infinite linear;-o-animation:cssload-spin 2000ms infinite linear;-ms-animation:cssload-spin 2000ms infinite linear;-webkit-animation:cssload-spin 2000ms infinite linear;-moz-animation:cssload-spin 2000ms infinite linear}@keyframes cssload-spin{100%{transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes cssload-spin{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes cssload-spin{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes cssload-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes cssload-spin{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}
shortpixel_api.php CHANGED
@@ -28,7 +28,6 @@ class ShortPixelAPI {
28
  const ERR_UNKNOWN = -999;
29
 
30
  private $_settings;
31
- private $_maxAttempts = 10;
32
  private $_apiEndPoint;
33
  private $_apiDumpEndPoint;
34
 
@@ -76,23 +75,26 @@ class ShortPixelAPI {
76
  * @param array $URLs - list of urls to send to API
77
  * @param Boolean $Blocking - true means it will wait for an answer
78
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
79
- * @param int $compressionType 1 - lossy, 2 - glossy, 0 - lossless
80
- * @return response from wp_remote_post or error
 
 
81
  */
82
  public function doRequests($URLs, $Blocking, $itemHandler, $compressionType = false, $refresh = false) {
83
 
84
  if(!count($URLs)) {
85
  $meta = $itemHandler->getMeta();
86
- $files = " (";
87
  if(count($meta->getThumbsMissing())) {
 
88
  foreach ($meta->getThumbsMissing() as $miss) {
89
  $files .= $miss . ", ";
90
  }
91
  if(strrpos($files, ', ')) {
92
  $files = substr_replace($files , ')', strrpos($files , ', '));
93
  }
 
94
  }
95
- throw new Exception(__('Image files are missing.' . (strlen($files) > 1 ? $files : '') ,'shortpixel-image-optimiser'));
96
  }
97
 
98
  //WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
@@ -107,9 +109,11 @@ class ShortPixelAPI {
107
  'resize' => $this->_settings->resizeImages ? 1 + 2 * ($this->_settings->resizeType == 'inner' ? 1 : 0) : 0,
108
  'resize_width' => $this->_settings->resizeWidth,
109
  'resize_height' => $this->_settings->resizeHeight,
110
- 'group_id' => $itemHandler->getId(),
111
  'urllist' => $URLs
112
  );
 
 
 
113
  if($refresh) {
114
  $requestParameters['refresh'] = 1;
115
  }
@@ -125,7 +129,7 @@ class ShortPixelAPI {
125
  {
126
  //WpShortPixel::log("API response : " . json_encode($response));
127
 
128
- //die(var_dump(array('URL: ' => $this->_apiEndPoint, '<br><br>REQUEST:' => $this->prepareRequest($requestParameters), '<br><br>RESPONSE: ' => $response )));
129
  //there was an error, save this error inside file's SP optimization field
130
  if ( is_object($response) && get_class($response) == 'WP_Error' )
131
  {
@@ -153,7 +157,7 @@ class ShortPixelAPI {
153
  /**
154
  * parse the JSON response
155
  * @param $response
156
- * @return parsed array
157
  */
158
  public function parseResponse($response) {
159
  $data = $response['body'];
@@ -166,7 +170,7 @@ class ShortPixelAPI {
166
  * @param array $URLs - list of urls to send to API
167
  * @param array $PATHs - list of local paths for the images
168
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
169
- * @return status/message array
170
  */
171
  public function processImage($URLs, $PATHs, $itemHandler = null) {
172
  return $this->processImageRecursive($URLs, $PATHs, $itemHandler, 0);
@@ -177,8 +181,8 @@ class ShortPixelAPI {
177
  * @param array $URLs - list of urls to send to API
178
  * @param array $PATHs - list of local paths for the images
179
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
180
- * @param type $startTime - time of the first call
181
- * @return status/message array
182
  */
183
  private function processImageRecursive($URLs, $PATHs, $itemHandler = null, $startTime = 0)
184
  {
@@ -227,7 +231,7 @@ class ShortPixelAPI {
227
  $compressionType = $meta->getCompressionType() !== null ? $meta->getCompressionType() : $this->_settings->compressionType;
228
  $response = $this->doRequests($URLs, true, $itemHandler, $compressionType);//send requests to API
229
 
230
- //die(var_dump($response));
231
 
232
  if($response['response']['code'] != 200) {//response <> 200 -> there was an error apparently?
233
  return array("Status" => self::STATUS_FAIL, "Message" => __('There was an error and your request was not processed.', 'shortpixel-image-optimiser')
@@ -235,16 +239,16 @@ class ShortPixelAPI {
235
  }
236
 
237
  $APIresponse = $this->parseResponse($response);//get the actual response from API, its an array
238
-
239
  if ( isset($APIresponse[0]) ) //API returned image details
240
  {
241
  foreach ( $APIresponse as $imageObject ) {//this part makes sure that all the sizes were processed and ready to be downloaded
242
- if ( $imageObject->Status->Code == 0 || $imageObject->Status->Code == 1 ) {
243
  sleep(1);
244
  return $this->processImageRecursive($URLs, $PATHs, $itemHandler, $startTime);
245
  }
246
  }
247
-
248
  $firstImage = $APIresponse[0];//extract as object first image
249
  switch($firstImage->Status->Code)
250
  {
@@ -317,7 +321,7 @@ class ShortPixelAPI {
317
  * If http works then it's http, otherwise sets https
318
  * @param string $url
319
  * @param bool $reset - forces recheck even if preferred protocol is already set
320
- * @return url with the preferred protocol
321
  */
322
  public function setPreferredProtocol($url, $reset = false) {
323
  //switch protocol based on the formerly detected working protocol
@@ -334,53 +338,71 @@ class ShortPixelAPI {
334
 
335
  }
336
 
337
- function downloadAll($target) {
338
- //TODO DOCS
339
- //http://php.net/manual/en/phardata.buildfromiterator.php
340
- //http://php.net/manual/en/phardata.extractto.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  }
342
 
343
  /**
344
  * handles the download of an optimized image from ShortPixel API
345
- * @param type $fileData - info about the file
346
- * @param int $compressionType - 1 - lossy, 2 - glossy, 0 - lossless
347
- * @return status/message array
 
 
348
  */
349
- private function handleDownload($fileData, $compressionType){
350
- //var_dump($fileData);
351
- if($compressionType)
352
- {
353
- $fileType = "LossyURL";
354
- $fileSize = "LossySize";
355
- $webpType = "WebPLossyURL";
356
- }
357
- else
358
- {
359
- $fileType = "LosslessURL";
360
- $fileSize = "LoselessSize";
361
- $webpType = "WebPLosslessURL";
362
- }
363
-
364
- $downloadTimeout = max(ini_get('max_execution_time') - 10, 15);
365
 
366
  $webpTempFile = "NA";
367
- if(isset($fileData->$webpType) && $fileData->$webpType !== "NA") {
368
- $webpURL = $this->setPreferredProtocol(urldecode($fileData->$webpType));
369
  $webpTempFile = download_url($webpURL, $downloadTimeout);
370
  $webpTempFile = is_wp_error( $webpTempFile ) ? "NA" : $webpTempFile;
371
  }
372
 
373
  //if there is no improvement in size then we do not download this file
374
- if ( $fileData->OriginalSize == $fileData->$fileSize )
375
  return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
376
 
377
- $correctFileSize = $fileData->$fileSize;
378
- $fileURL = $this->setPreferredProtocol(urldecode($fileData->$fileType));
379
 
380
  $tempFile = download_url($fileURL, $downloadTimeout);
381
  if(is_wp_error( $tempFile ))
382
  { //try to switch the default protocol
383
- $fileURL = $this->setPreferredProtocol(urldecode($fileData->$fileType), true); //force recheck of the protocol
384
  $tempFile = download_url($fileURL, $downloadTimeout);
385
  }
386
 
@@ -389,10 +411,11 @@ class ShortPixelAPI {
389
 
390
  if ( is_wp_error( $tempFile ) ) {
391
  @unlink($tempFile);
 
392
  $returnMessage = array(
393
  "Status" => self::STATUS_ERROR,
394
  "Code" => self::ERR_DOWNLOAD,
395
- "Message" => __('Error downloading file','shortpixel-image-optimiser') . " ({$fileData->$fileType}) " . $tempFile->get_error_message());
396
  }
397
  //check response so that download is OK
398
  elseif (!file_exists($tempFile)) {
@@ -403,6 +426,7 @@ class ShortPixelAPI {
403
  elseif( filesize($tempFile) != $correctFileSize) {
404
  $size = filesize($tempFile);
405
  @unlink($tempFile);
 
406
  $returnMessage = array(
407
  "Status" => self::STATUS_ERROR,
408
  "Code" => self::ERR_INCORRECT_FILE_SIZE,
@@ -451,23 +475,105 @@ class ShortPixelAPI {
451
  }
452
  }
453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  /**
455
  * handles a successful optimization, setting metadata and handling download for each file in the set
456
- * @param type $APIresponse - the response from the API - contains the optimized images URLs to download
457
- * @param type $PATHs - list of local paths for the files
458
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
459
  * @param int $compressionType - 1 - lossy, 2 - glossy, 0 - lossless
460
- * @return status/message array
461
  */
462
  private function handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType) {
463
  $counter = $savedSpace = $originalSpace = $optimizedSpace = $averageCompression = 0;
464
  $NoBackup = true;
465
 
466
- $fileType = ( $compressionType ) ? "LossySize" : "LoselessSize";
467
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  //download each file from array and process it
469
  foreach ( $APIresponse as $fileData )
470
  {
 
 
471
  if ( $fileData->Status->Code == 2 ) //file was processed OK
472
  {
473
  if ( $counter == 0 ) { //save percent improvement for main file
@@ -475,19 +581,25 @@ class ShortPixelAPI {
475
  } else { //count thumbnails only
476
  $this->_settings->thumbsCount = $this->_settings->thumbsCount + 1;
477
  }
478
- $downloadResult = $this->handleDownload($fileData,$compressionType);
479
-
 
 
 
 
 
 
480
  if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
481
- $tempFiles[$counter] = $downloadResult;
482
  }
483
  //when the status is STATUS_UNCHANGED we just skip the array line for that one
484
  elseif( $downloadResult['Status'] == self::STATUS_UNCHANGED ) {
485
  //this image is unchanged so won't be copied below, only the optimization stats need to be computed
486
  $originalSpace += $fileData->OriginalSize;
487
- $optimizedSpace += $fileData->$fileType;
488
- $tempFiles[$counter] = $downloadResult;
489
  }
490
- else {
 
491
  return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
492
  }
493
 
@@ -497,7 +609,7 @@ class ShortPixelAPI {
497
  }
498
  $counter++;
499
  }
500
-
501
  //figure out in what SubDir files should land
502
  $mainPath = $itemHandler->getMeta()->getPath();
503
 
@@ -507,7 +619,8 @@ class ShortPixelAPI {
507
  $backupStatus = self::backupImage($mainPath, $PATHs);
508
  if($backupStatus == self::STATUS_FAIL) {
509
  $itemHandler->incrementRetries(1, self::ERR_SAVE_BKP, $backupStatus["Message"]);
510
- return $backupStatus;
 
511
  }
512
  $NoBackup = false;
513
  }//end backup section
@@ -518,8 +631,7 @@ class ShortPixelAPI {
518
  $retinas = 0;
519
  $thumbsOpt = 0;
520
  $thumbsOptList = array();
521
- $webpSizes = array();
522
-
523
  if ( !empty($tempFiles) )
524
  {
525
  //overwrite the original files with the optimized ones
@@ -529,7 +641,7 @@ class ShortPixelAPI {
529
 
530
  $targetFile = $PATHs[$tempFileID];
531
  $isRetina = ShortPixelMetaFacade::isRetina($targetFile);
532
-
533
  if( ($tempFile['Status'] == self::STATUS_UNCHANGED || $tempFile['Status'] == self::STATUS_SUCCESS) && !$isRetina
534
  && $targetFile !== $mainPath) {
535
  $thumbsOpt++;
@@ -538,7 +650,7 @@ class ShortPixelAPI {
538
 
539
  if($tempFile['Status'] == self::STATUS_SUCCESS) { //if it's unchanged it will still be in the array but only for WebP (handled below)
540
  $tempFilePATH = $tempFile["Message"];
541
- if ( file_exists($tempFilePATH) && file_exists($PATHs[$tempFileID]) && is_writable($PATHs[$tempFileID]) ) {
542
  copy($tempFilePATH, $targetFile);
543
  if(ShortPixelMetaFacade::isRetina($targetFile)) {
544
  $retinas ++;
@@ -550,18 +662,27 @@ class ShortPixelAPI {
550
  }
551
  //Calculate the saved space
552
  $fileData = $APIresponse[$tempFileID];
553
- $savedSpace += $fileData->OriginalSize - $fileData->$fileType;
554
  $originalSpace += $fileData->OriginalSize;
555
- $optimizedSpace += $fileData->$fileType;
556
  $averageCompression += $fileData->PercentImprovement;
557
- WPShortPixel::log("HANDLE SUCCESS: Image " . $PATHs[$tempFileID] . " original size: ".$fileData->OriginalSize . " optimized: " . $fileData->$fileType);
558
 
559
  //add the number of files with < 5% optimization
560
- if ( ( ( 1 - $APIresponse[$tempFileID]->$fileType/$APIresponse[$tempFileID]->OriginalSize ) * 100 ) < 5 ) {
561
  $this->_settings->under5Percent++;
562
  }
563
  }
564
  else {
 
 
 
 
 
 
 
 
 
565
  $writeFailed++;
566
  }
567
  @unlink($tempFilePATH);
@@ -574,9 +695,16 @@ class ShortPixelAPI {
574
  @unlink($tempWebpFilePATH);
575
  }
576
  }
577
-
 
578
  if ( $writeFailed > 0 )//there was an error
579
  {
 
 
 
 
 
 
580
  $msg = sprintf(__('Optimized version of %s file(s) couldn\'t be updated.','shortpixel-image-optimiser'),$writeFailed);
581
  $itemHandler->incrementRetries(1, self::ERR_SAVE, $msg);
582
  $this->_settings->bulkProcessingStatus = "error";
@@ -628,7 +756,8 @@ class ShortPixelAPI {
628
 
629
  $itemHandler->updateMeta($meta);
630
  $itemHandler->optimizationSucceeded();
631
-
 
632
  if(!$originalSpace) { //das kann nicht sein, alles klar?!
633
  throw new Exception("OriginalSpace = 0. APIResponse" . json_encode($APIresponse));
634
  }
@@ -641,7 +770,24 @@ class ShortPixelAPI {
641
  ? number_format(100.0 * (1.0 - (1.0 - $png2jpg / 100.0) * $optimizedSpace / $originalSpace), 2)
642
  : "Couldn't compute thumbs optimization percent. Main image: " . $percentImprovement);
643
  }//end handleSuccess
644
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  /**
646
  * a basename alternative that deals OK with multibyte charsets (e.g. Arabic)
647
  * @param string $Path
@@ -666,7 +812,7 @@ class ShortPixelAPI {
666
 
667
  /**
668
  * sometimes, the paths to the files as defined in metadata are wrong, we try to automatically correct them
669
- * @param type $PATHs
670
  * @return boolean|string
671
  */
672
  static public function CheckAndFixImagePaths($PATHs){
28
  const ERR_UNKNOWN = -999;
29
 
30
  private $_settings;
 
31
  private $_apiEndPoint;
32
  private $_apiDumpEndPoint;
33
 
75
  * @param array $URLs - list of urls to send to API
76
  * @param Boolean $Blocking - true means it will wait for an answer
77
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
78
+ * @param bool|int $compressionType 1 - lossy, 2 - glossy, 0 - lossless
79
+ * @param bool $refresh
80
+ * @return Array response from wp_remote_post or error
81
+ * @throws Exception
82
  */
83
  public function doRequests($URLs, $Blocking, $itemHandler, $compressionType = false, $refresh = false) {
84
 
85
  if(!count($URLs)) {
86
  $meta = $itemHandler->getMeta();
 
87
  if(count($meta->getThumbsMissing())) {
88
+ $files = " (";
89
  foreach ($meta->getThumbsMissing() as $miss) {
90
  $files .= $miss . ", ";
91
  }
92
  if(strrpos($files, ', ')) {
93
  $files = substr_replace($files , ')', strrpos($files , ', '));
94
  }
95
+ throw new Exception(__('Image files are missing.', 'shortpixel-image-optimiser') . (strlen($files) > 1 ? $files : ''));
96
  }
97
+ else throw new Exception(__('Image files are missing.', 'shortpixel-image-optimiser'));
98
  }
99
 
100
  //WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
109
  'resize' => $this->_settings->resizeImages ? 1 + 2 * ($this->_settings->resizeType == 'inner' ? 1 : 0) : 0,
110
  'resize_width' => $this->_settings->resizeWidth,
111
  'resize_height' => $this->_settings->resizeHeight,
 
112
  'urllist' => $URLs
113
  );
114
+ if(/*false &&*/ $this->_settings->downloadArchive == 7 && class_exists('PharData')) {
115
+ $requestParameters['group'] = $itemHandler->getId();
116
+ }
117
  if($refresh) {
118
  $requestParameters['refresh'] = 1;
119
  }
129
  {
130
  //WpShortPixel::log("API response : " . json_encode($response));
131
 
132
+ //die(var_dump(array('URL: ' => $this->_apiEndPoint, '<br><br>REQUEST:' => $requestParameters, '<br><br>RESPONSE: ' => $response, '<br><br>BODY: ' => isset($response['body']) ? $response['body'] : '' )));
133
  //there was an error, save this error inside file's SP optimization field
134
  if ( is_object($response) && get_class($response) == 'WP_Error' )
135
  {
157
  /**
158
  * parse the JSON response
159
  * @param $response
160
+ * @return array parsed
161
  */
162
  public function parseResponse($response) {
163
  $data = $response['body'];
170
  * @param array $URLs - list of urls to send to API
171
  * @param array $PATHs - list of local paths for the images
172
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
173
+ * @return array status/message array
174
  */
175
  public function processImage($URLs, $PATHs, $itemHandler = null) {
176
  return $this->processImageRecursive($URLs, $PATHs, $itemHandler, 0);
181
  * @param array $URLs - list of urls to send to API
182
  * @param array $PATHs - list of local paths for the images
183
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
184
+ * @param int $startTime - time of the first call
185
+ * @return array status/message array
186
  */
187
  private function processImageRecursive($URLs, $PATHs, $itemHandler = null, $startTime = 0)
188
  {
231
  $compressionType = $meta->getCompressionType() !== null ? $meta->getCompressionType() : $this->_settings->compressionType;
232
  $response = $this->doRequests($URLs, true, $itemHandler, $compressionType);//send requests to API
233
 
234
+ //die($response['body']);
235
 
236
  if($response['response']['code'] != 200) {//response <> 200 -> there was an error apparently?
237
  return array("Status" => self::STATUS_FAIL, "Message" => __('There was an error and your request was not processed.', 'shortpixel-image-optimiser')
239
  }
240
 
241
  $APIresponse = $this->parseResponse($response);//get the actual response from API, its an array
242
+
243
  if ( isset($APIresponse[0]) ) //API returned image details
244
  {
245
  foreach ( $APIresponse as $imageObject ) {//this part makes sure that all the sizes were processed and ready to be downloaded
246
+ if ( isset($imageObject->Status) && ( $imageObject->Status->Code == 0 || $imageObject->Status->Code == 1 ) ) {
247
  sleep(1);
248
  return $this->processImageRecursive($URLs, $PATHs, $itemHandler, $startTime);
249
  }
250
  }
251
+
252
  $firstImage = $APIresponse[0];//extract as object first image
253
  switch($firstImage->Status->Code)
254
  {
321
  * If http works then it's http, otherwise sets https
322
  * @param string $url
323
  * @param bool $reset - forces recheck even if preferred protocol is already set
324
+ * @return string url with the preferred protocol
325
  */
326
  public function setPreferredProtocol($url, $reset = false) {
327
  //switch protocol based on the formerly detected working protocol
338
 
339
  }
340
 
341
+ function fromArchive($path, $optimizedUrl, $optimizedSize, $originalSize, $webpUrl) {
342
+ $webpTempFile = "NA";
343
+ if($webpUrl !== "NA") {
344
+ $webpTempFile = $path . '/' . wp_basename($webpUrl);
345
+ $webpTempFile = file_exists($webpTempFile) ? $webpTempFile : 'NA';
346
+ }
347
+
348
+ //if there is no improvement in size then we do not download this file
349
+ if ( $originalSize == $optimizedSize )
350
+ return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
351
+
352
+ $correctFileSize = $optimizedSize;
353
+ $tempFile = $path . '/' . wp_basename($optimizedUrl);
354
+
355
+ if(file_exists($tempFile)) {
356
+ //on success we return this
357
+ if( filesize($tempFile) != $correctFileSize) {
358
+ $size = filesize($tempFile);
359
+ @unlink($tempFile);
360
+ @unlink($webpTempFile);
361
+ $returnMessage = array(
362
+ "Status" => self::STATUS_ERROR,
363
+ "Code" => self::ERR_INCORRECT_FILE_SIZE,
364
+ "Message" => sprintf(__('Error in archive - incorrect file size (downloaded: %s, correct: %s )','shortpixel-image-optimiser'),$size, $correctFileSize));
365
+ } else {
366
+ $returnMessage = array("Status" => self::STATUS_SUCCESS, "Message" => $tempFile, "WebP" => $webpTempFile);
367
+ }
368
+ } else {
369
+ $returnMessage = array("Status" => self::STATUS_ERROR,
370
+ "Code" => self::ERR_FILE_NOT_FOUND,
371
+ "Message" => __('Unable to locate downloaded file','shortpixel-image-optimiser') . " " . $tempFile);
372
+ }
373
+
374
+ return $returnMessage;
375
  }
376
 
377
  /**
378
  * handles the download of an optimized image from ShortPixel API
379
+ * @param string $optimizedUrl
380
+ * @param int $optimizedSize
381
+ * @param int $originalSize
382
+ * @param string $webpUrl
383
+ * @return array status /message array
384
  */
385
+ private function handleDownload($optimizedUrl, $optimizedSize, $originalSize, $webpUrl){
386
+ $downloadTimeout = max(ini_get('max_execution_time') - 10, 15);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
  $webpTempFile = "NA";
389
+ if($webpUrl !== "NA") {
390
+ $webpURL = $this->setPreferredProtocol(urldecode($webpUrl));
391
  $webpTempFile = download_url($webpURL, $downloadTimeout);
392
  $webpTempFile = is_wp_error( $webpTempFile ) ? "NA" : $webpTempFile;
393
  }
394
 
395
  //if there is no improvement in size then we do not download this file
396
+ if ( $originalSize == $optimizedSize )
397
  return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
398
 
399
+ $correctFileSize = $optimizedSize;
400
+ $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl));
401
 
402
  $tempFile = download_url($fileURL, $downloadTimeout);
403
  if(is_wp_error( $tempFile ))
404
  { //try to switch the default protocol
405
+ $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl), true); //force recheck of the protocol
406
  $tempFile = download_url($fileURL, $downloadTimeout);
407
  }
408
 
411
 
412
  if ( is_wp_error( $tempFile ) ) {
413
  @unlink($tempFile);
414
+ @unlink($webpTempFile);
415
  $returnMessage = array(
416
  "Status" => self::STATUS_ERROR,
417
  "Code" => self::ERR_DOWNLOAD,
418
+ "Message" => __('Error downloading file','shortpixel-image-optimiser') . " ({$optimizedUrl}) " . $tempFile->get_error_message());
419
  }
420
  //check response so that download is OK
421
  elseif (!file_exists($tempFile)) {
426
  elseif( filesize($tempFile) != $correctFileSize) {
427
  $size = filesize($tempFile);
428
  @unlink($tempFile);
429
+ @unlink($webpTempFile);
430
  $returnMessage = array(
431
  "Status" => self::STATUS_ERROR,
432
  "Code" => self::ERR_INCORRECT_FILE_SIZE,
475
  }
476
  }
477
 
478
+ private function createArchiveTempFolder($archiveBasename) {
479
+ $archiveTempDir = get_temp_dir() . '/' . $archiveBasename;
480
+ if(file_exists($archiveTempDir) && is_dir($archiveTempDir) && (time() - filemtime($archiveTempDir) < max(30, SHORTPIXEL_MAX_EXECUTION_TIME) + 10)) {
481
+ WPShortPixel::log("CONFLICT. Folder already exists and is modified in the last minute. Current IP:" . $_SERVER['REMOTE_ADDR']);
482
+ return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
483
+ }
484
+ if( !file_exists($archiveTempDir) && !@mkdir($archiveTempDir) ) {
485
+ return array("Status" => self::STATUS_ERROR, "Code" => self::ERR_SAVE, "Message" => "Could not create temporary folder.");
486
+ }
487
+ return array("Status" => self::STATUS_SUCCESS, "Dir" => $archiveTempDir);
488
+ }
489
+
490
+ private function downloadArchive($archive, $compressionType, $first = true) {
491
+ if($archive->ArchiveStatus->Code == 1 || $archive->ArchiveStatus->Code == 0) {
492
+ return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
493
+ } elseif($archive->ArchiveStatus->Code == 2) {
494
+
495
+ $suffix = ($compressionType == 0 ? "-lossless" : "");
496
+ $archiveURL = "Archive" . ($compressionType == 0 ? "Lossless" : "") . "URL";
497
+ $archiveSize = "Archive" . ($compressionType == 0 ? "Lossless" : "") . "Size";
498
+
499
+ $archiveTemp = $this->createArchiveTempFolder(wp_basename($archive->$archiveURL, '.tar'));
500
+ if($archiveTemp["Status"] == self::STATUS_SUCCESS) { $archiveTempDir = $archiveTemp["Dir"]; }
501
+ else { return $archiveTemp; }
502
+
503
+ $downloadResult = $this->handleDownload($archive->$archiveURL, $archive->$archiveSize, 0, 'NA');
504
+
505
+ if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
506
+ $archiveFile = $downloadResult['Message'];
507
+ if(filesize($archiveFile) !== $archive->$archiveSize) {
508
+ @unlink($archiveFile);
509
+ ShortpixelFolder::deleteFolder($archiveTempDir);
510
+ return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
511
+ }
512
+ $pharData = new PharData($archiveFile);
513
+ try {
514
+ if (SHORTPIXEL_DEBUG === true) {
515
+ $info = "Current IP:" . $_SERVER['REMOTE_ADDR'] . "ARCHIVE CONTENTS: COUNT " . $pharData->count() . ", ";
516
+ foreach($pharData as $file) {
517
+ $info .= $file . ", ";
518
+ }
519
+ WPShortPixel::log($info);
520
+ }
521
+ $pharData->extractTo($archiveTempDir, null, true);
522
+ WPShortPixel::log("ARCHIVE EXTRACTED " . json_encode(scandir($archiveTempDir)));
523
+ @unlink($archiveFile);
524
+ } catch (Exception $ex) {
525
+ @unlink($archiveFile);
526
+ ShortpixelFolder::deleteFolder($archiveTempDir);
527
+ return array("Status" => self::STATUS_ERROR, "Code" => $ex->getCode(), "Message" => $ex->getMessage());
528
+ }
529
+ return array("Status" => self::STATUS_SUCCESS, "Code" => 2, "Message" => "Success", "Path" => $archiveTempDir);
530
+
531
+ } else {
532
+ WPShortPixel::log("ARCHIVE ERROR (" . $archive->$archiveURL . "): " . json_encode($downloadResult));
533
+ if($first && $downloadResult['Code'] == self::ERR_INCORRECT_FILE_SIZE) {
534
+ WPShortPixel::log("RETRYING AFTER ARCHIVE ERROR");
535
+ return $this->downloadArchive($archive, $compressionType, false); // try again, maybe the archive was flushing...
536
+ }
537
+ @rmdir($archiveTempDir); //in the case it was just created and it's empty...
538
+ return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
539
+ }
540
+ }
541
+ return false;
542
+ }
543
+
544
  /**
545
  * handles a successful optimization, setting metadata and handling download for each file in the set
546
+ * @param array $APIresponse - the response from the API - contains the optimized images URLs to download
547
+ * @param array $PATHs - list of local paths for the files
548
  * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
549
  * @param int $compressionType - 1 - lossy, 2 - glossy, 0 - lossless
550
+ * @return array status/message
551
  */
552
  private function handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType) {
553
  $counter = $savedSpace = $originalSpace = $optimizedSpace = $averageCompression = 0;
554
  $NoBackup = true;
555
 
556
+ if($compressionType) {
557
+ $fileType = "LossyURL";
558
+ $fileSize = "LossySize";
559
+ } else {
560
+ $fileType = "LosslessURL";
561
+ $fileSize = "LoselessSize";
562
+ }
563
+ $webpType = "WebP" . $fileType;
564
+
565
+ $archive = /*false &&*/
566
+ ($this->_settings->downloadArchive == 7 && class_exists('PharData') && isset($APIresponse[count($APIresponse) - 1]->ArchiveStatus))
567
+ ? $this->downloadArchive($APIresponse[count($APIresponse) - 1], $compressionType) : false;
568
+ if($archive !== false && $archive['Status'] !== self::STATUS_SUCCESS) {
569
+ return $archive;
570
+ }
571
+
572
  //download each file from array and process it
573
  foreach ( $APIresponse as $fileData )
574
  {
575
+ if(!isset($fileData->Status)) continue; //if optimized images archive is activated, last entry of APIResponse if the Archive data.
576
+
577
  if ( $fileData->Status->Code == 2 ) //file was processed OK
578
  {
579
  if ( $counter == 0 ) { //save percent improvement for main file
581
  } else { //count thumbnails only
582
  $this->_settings->thumbsCount = $this->_settings->thumbsCount + 1;
583
  }
584
+ //TODO la sfarsit sa faca fallback la handleDownload
585
+ if($archive) {
586
+ $downloadResult = $this->fromArchive($archive['Path'], $fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize, isset($fileData->$webpType) ? $fileData->$webpType : 'NA');
587
+ } else {
588
+ $downloadResult = $this->handleDownload($fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize, isset($fileData->$webpType) ? $fileData->$webpType : 'NA');
589
+ }
590
+
591
+ $tempFiles[$counter] = $downloadResult;
592
  if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
593
+ //nothing to do
594
  }
595
  //when the status is STATUS_UNCHANGED we just skip the array line for that one
596
  elseif( $downloadResult['Status'] == self::STATUS_UNCHANGED ) {
597
  //this image is unchanged so won't be copied below, only the optimization stats need to be computed
598
  $originalSpace += $fileData->OriginalSize;
599
+ $optimizedSpace += $fileData->$fileSize;
 
600
  }
601
+ else {
602
+ self::cleanupTemporaryFiles($archive, $tempFiles);
603
  return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
604
  }
605
 
609
  }
610
  $counter++;
611
  }
612
+
613
  //figure out in what SubDir files should land
614
  $mainPath = $itemHandler->getMeta()->getPath();
615
 
619
  $backupStatus = self::backupImage($mainPath, $PATHs);
620
  if($backupStatus == self::STATUS_FAIL) {
621
  $itemHandler->incrementRetries(1, self::ERR_SAVE_BKP, $backupStatus["Message"]);
622
+ self::cleanupTemporaryFiles($archive, empty($tempFiles) ? array() : $tempFiles);
623
+ return array("Status" => self::STATUS_FAIL, "Code" =>"backup-fail", "Message" => "Failed to back the image up.");
624
  }
625
  $NoBackup = false;
626
  }//end backup section
631
  $retinas = 0;
632
  $thumbsOpt = 0;
633
  $thumbsOptList = array();
634
+
 
635
  if ( !empty($tempFiles) )
636
  {
637
  //overwrite the original files with the optimized ones
641
 
642
  $targetFile = $PATHs[$tempFileID];
643
  $isRetina = ShortPixelMetaFacade::isRetina($targetFile);
644
+
645
  if( ($tempFile['Status'] == self::STATUS_UNCHANGED || $tempFile['Status'] == self::STATUS_SUCCESS) && !$isRetina
646
  && $targetFile !== $mainPath) {
647
  $thumbsOpt++;
650
 
651
  if($tempFile['Status'] == self::STATUS_SUCCESS) { //if it's unchanged it will still be in the array but only for WebP (handled below)
652
  $tempFilePATH = $tempFile["Message"];
653
+ if ( file_exists($tempFilePATH) && file_exists($targetFile) && is_writable($targetFile) ) {
654
  copy($tempFilePATH, $targetFile);
655
  if(ShortPixelMetaFacade::isRetina($targetFile)) {
656
  $retinas ++;
662
  }
663
  //Calculate the saved space
664
  $fileData = $APIresponse[$tempFileID];
665
+ $savedSpace += $fileData->OriginalSize - $fileData->$fileSize;
666
  $originalSpace += $fileData->OriginalSize;
667
+ $optimizedSpace += $fileData->$fileSize;
668
  $averageCompression += $fileData->PercentImprovement;
669
+ WPShortPixel::log("HANDLE SUCCESS: Image " . $PATHs[$tempFileID] . " original size: ".$fileData->OriginalSize . " optimized: " . $fileData->$fileSize);
670
 
671
  //add the number of files with < 5% optimization
672
+ if ( ( ( 1 - $APIresponse[$tempFileID]->$fileSize/$APIresponse[$tempFileID]->OriginalSize ) * 100 ) < 5 ) {
673
  $this->_settings->under5Percent++;
674
  }
675
  }
676
  else {
677
+ if($archive && SHORTPIXEL_DEBUG === true) {
678
+ if(!file_exists($tempFilePATH)) {
679
+ WPShortPixel::log("MISSING FROM ARCHIVE. tempFilePath: $tempFilePATH with ID: $tempFileID");
680
+ } elseif(!file_exists($targetFile)){
681
+ WPShortPixel::log("MISSING TARGET: $targetFile");
682
+ } elseif(!is_writable($targetFile)){
683
+ WPShortPixel::log("TARGET NOT WRITABLE: $targetFile");
684
+ }
685
+ }
686
  $writeFailed++;
687
  }
688
  @unlink($tempFilePATH);
695
  @unlink($tempWebpFilePATH);
696
  }
697
  }
698
+ self::cleanupTemporaryFiles($archive, $tempFiles);
699
+
700
  if ( $writeFailed > 0 )//there was an error
701
  {
702
+ if($archive && SHORTPIXEL_DEBUG === true) {
703
+ WPShortPixel::log("ARCHIVE HAS MISSING FILES. EXPECTED: " . json_encode($PATHs)
704
+ . " AND: " . json_encode($APIresponse)
705
+ . " GOT ARCHIVE: " . $APIresponse[count($APIresponse) - 1]->ArchiveURL . " LOSSLESS: " . $APIresponse[count($APIresponse) - 1]->ArchiveLosslessURL
706
+ . " CONTAINING: " . json_encode(scandir($archive['Path'])));
707
+ }
708
  $msg = sprintf(__('Optimized version of %s file(s) couldn\'t be updated.','shortpixel-image-optimiser'),$writeFailed);
709
  $itemHandler->incrementRetries(1, self::ERR_SAVE, $msg);
710
  $this->_settings->bulkProcessingStatus = "error";
756
 
757
  $itemHandler->updateMeta($meta);
758
  $itemHandler->optimizationSucceeded();
759
+ WPShortPixel::log("HANDLE SUCCESS: Metadata saved.");
760
+
761
  if(!$originalSpace) { //das kann nicht sein, alles klar?!
762
  throw new Exception("OriginalSpace = 0. APIResponse" . json_encode($APIresponse));
763
  }
770
  ? number_format(100.0 * (1.0 - (1.0 - $png2jpg / 100.0) * $optimizedSpace / $originalSpace), 2)
771
  : "Couldn't compute thumbs optimization percent. Main image: " . $percentImprovement);
772
  }//end handleSuccess
773
+
774
+ /**
775
+ * @param $archive
776
+ * @param $tempFiles
777
+ */
778
+ protected static function cleanupTemporaryFiles($archive, $tempFiles)
779
+ {
780
+ if ($archive) {
781
+ ShortpixelFolder::deleteFolder($archive['Path']);
782
+ } else {
783
+ if (!empty($tempFiles) && is_array($tempFiles)) {
784
+ foreach ($tempFiles as $tmpFile) {
785
+ @unlink($tmpFile["Message"]);
786
+ }
787
+ }
788
+ }
789
+ }
790
+
791
  /**
792
  * a basename alternative that deals OK with multibyte charsets (e.g. Arabic)
793
  * @param string $Path
812
 
813
  /**
814
  * sometimes, the paths to the files as defined in metadata are wrong, we try to automatically correct them
815
+ * @param array $PATHs
816
  * @return boolean|string
817
  */
818
  static public function CheckAndFixImagePaths($PATHs){
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="options-general.php?page=wp-shortpixel" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
- * Version: 4.11.3
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
@@ -18,7 +18,7 @@ define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
18
 
19
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
20
 
21
- define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.11.2");
22
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
23
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
24
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
@@ -80,8 +80,14 @@ function shortpixelInit() {
80
  require_once('wp-shortpixel-req.php');
81
  $shortPixelPluginInstance = new WPShortPixel;
82
  }
83
- }
84
 
 
 
 
 
 
 
85
  function shortPixelHandleImageUploadHook($meta, $ID = null) {
86
  global $shortPixelPluginInstance;
87
  if(!isset($shortPixelPluginInstance)) {
@@ -174,8 +180,9 @@ function shortPixelGravityForms( $value, $lead, $field, $form ) {
174
  }
175
 
176
  if ( get_option('wp-short-pixel-create-webp-markup')) {
177
- add_filter( 'the_content', 'shortPixelConvertImgToPictureAddWebp', 10000 ); // priority big, so it will be executed last
178
- add_filter( 'post_thumbnail_html', 'shortPixelConvertImgToPictureAddWebp');
 
179
  add_action( 'wp_head', 'shortPixelAddPictureJs');
180
  // add_action( 'wp_enqueue_scripts', 'spAddPicturefillJs' );
181
  }
@@ -185,12 +192,12 @@ if ( !function_exists( 'vc_action' ) || vc_action() !== 'vc_inline' ) { //handle
185
  add_action('ngg_added_new_image', 'shortPixelNggAdd');
186
 
187
  $autoPng2Jpg = get_option('wp-short-pixel-png2jpg');
188
- if($autoPng2Jpg) {
 
189
  add_action( 'wp_handle_upload', 'shortPixelPng2JpgHook');
190
  add_action( 'mpp_handle_upload', 'shortPixelPng2JpgHook');
191
  }
192
  add_action('wp_handle_replace', 'shortPixelReplaceHook');
193
- $autoMediaLibrary = get_option('wp-short-pixel-auto-media-library');
194
  if($autoMediaLibrary) {
195
  add_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
196
  add_filter( 'mpp_generate_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
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="options-general.php?page=wp-shortpixel" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
+ * Version: 4.12.0
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
18
 
19
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
20
 
21
+ define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.12.0");
22
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
23
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
24
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
80
  require_once('wp-shortpixel-req.php');
81
  $shortPixelPluginInstance = new WPShortPixel;
82
  }
83
+ }
84
 
85
+ /**
86
+ * this is hooked into wp_generate_attachment_metadata
87
+ * @param $meta
88
+ * @param null $ID
89
+ * @return WPShortPixel the instance
90
+ */
91
  function shortPixelHandleImageUploadHook($meta, $ID = null) {
92
  global $shortPixelPluginInstance;
93
  if(!isset($shortPixelPluginInstance)) {
180
  }
181
 
182
  if ( get_option('wp-short-pixel-create-webp-markup')) {
183
+ //add_filter( 'the_content', 'shortPixelConvertImgToPictureAddWebp', 10000 ); // priority big, so it will be executed last
184
+ //add_filter( 'post_thumbnail_html', 'shortPixelConvertImgToPictureAddWebp');
185
+ ob_start( 'shortPixelConvertImgToPictureAddWebp');
186
  add_action( 'wp_head', 'shortPixelAddPictureJs');
187
  // add_action( 'wp_enqueue_scripts', 'spAddPicturefillJs' );
188
  }
192
  add_action('ngg_added_new_image', 'shortPixelNggAdd');
193
 
194
  $autoPng2Jpg = get_option('wp-short-pixel-png2jpg');
195
+ $autoMediaLibrary = get_option('wp-short-pixel-auto-media-library');
196
+ if($autoPng2Jpg && $autoMediaLibrary) {
197
  add_action( 'wp_handle_upload', 'shortPixelPng2JpgHook');
198
  add_action( 'mpp_handle_upload', 'shortPixelPng2JpgHook');
199
  }
200
  add_action('wp_handle_replace', 'shortPixelReplaceHook');
 
201
  if($autoMediaLibrary) {
202
  add_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
203
  add_filter( 'mpp_generate_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );