ShortPixel Image Optimizer - Version 4.13.0

Version Description

Release date: 10th April 2019 * Bulk restore for the Other Media * make the filename extension be updated when manually optimizing a PNG from Media Library, if the convert to JPG is active, without refreshing the page * Integration with Regenerate Thumbnails Advanced new 2.0 beta version * Add the rules for WebP in the WP-CONTENT .htaccess * ShortPixel Other Media - display the time of optimization in the grid and offer option to sort by it * Keep sort order when optimizing / refreshing page on Other Media * offer the visual comparer for Other Media too * resolve the Settings inconsistency in Other Media (settings displayed were from when adding the folder not from when actually optimizing) * Make pressing Escape or clicking outside of any popup close it. * Fixed: Restoring an Other Media item and then Optimizing it again optimizes it Lossless * fix generating the WebP tags when the images are either on a subdomain or on a CDN domain having the same root domain as the main site.

Download this release

Release Info

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

Code changes from version 4.12.8 to 4.13.0

class/controller/bulk-restore-all.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+
5
+ class BulkRestoreAll extends ShortPixelController
6
+ {
7
+ protected static $slug = 'bulk-restore-all';
8
+ protected $template = 'view-restore-all';
9
+
10
+ protected $selected_folders = array();
11
+
12
+ public function __construct()
13
+ {
14
+ parent::__construct();
15
+
16
+ }
17
+
18
+ public function randomCheck()
19
+ {
20
+
21
+ $output = '';
22
+ for ($i=1; $i<= 10; $i++)
23
+ {
24
+ $output .= "<span><input type='radio' name='random_check[]' value='$i' onchange='ShortPixel.checkRandomAnswer(event)' /> $i </span>";
25
+ }
26
+
27
+ return $output;
28
+ }
29
+
30
+ public function randomAnswer()
31
+ {
32
+ $correct = rand(1,10);
33
+ $output = "<input type='hidden' name='random_answer' value='$correct' data-target='#bulkRestore' /> <span class='answer'>$correct</span> ";
34
+
35
+ return $output;
36
+ }
37
+
38
+ public function getCustomFolders()
39
+ {
40
+ //wpshortPixel::refreshCustomFolders();
41
+ $spMetaDao = $this->shortPixel->getSpMetaDao();
42
+ $customFolders = $spMetaDao->getFolders();
43
+
44
+ return $customFolders;
45
+
46
+ }
47
+
48
+ protected function processPostData($post)
49
+ {
50
+ if (isset($post['selected_folders']))
51
+ {
52
+ $folders = array_filter($post['selected_folders'], 'intval');
53
+ // var_dump($post['selected_folders']);
54
+ if (count($folders) > 0)
55
+ {
56
+ $this->selected_folders = $folders;
57
+ }
58
+ unset($post['selected_folders']);
59
+ }
60
+
61
+ parent::processPostData($post);
62
+
63
+ }
64
+
65
+ public function setupBulk()
66
+ {
67
+ // handle the custom folders if there are any.
68
+ if (count($this->selected_folders) > 0)
69
+ {
70
+ $spMetaDao = $this->shortPixel->getSpMetaDao();
71
+
72
+ foreach($this->selected_folders as $folder_id)
73
+ {
74
+ $spMetaDao->setBulkRestore($folder_id);
75
+ }
76
+ }
77
+ }
78
+
79
+
80
+ }
class/controller/controller.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace ShortPixel;
4
+
5
+ class ShortPixelController
6
+ {
7
+ protected static $controllers = array();
8
+
9
+ protected $data = array(); // data array for usage with databases data and such
10
+ protected $postData = array(); // data coming from form posts.
11
+ protected $layout; // object to use in the view.
12
+
13
+ protected $template = null; // template name to include when loading.
14
+
15
+ public static function init()
16
+ {
17
+ foreach (get_declared_classes() as $class) {
18
+ if (is_subclass_of($class, \ShortPixelTools::namespaceit('shortPixelController') ))
19
+ self::$controllers[] = $class;
20
+ }
21
+ }
22
+
23
+ public static function findControllerbySlug($name)
24
+ {
25
+ foreach(self::$controllers as $className)
26
+ {
27
+ if ($className::$slug == $name)
28
+ {
29
+ return $className; // found!
30
+ }
31
+ }
32
+ }
33
+
34
+ public function __construct()
35
+ {
36
+ $this->layout = new \stdClass;
37
+ if (isset($_POST) && count($_POST) > 0)
38
+ {
39
+ $this->processPostData($_POST);
40
+ }
41
+ }
42
+
43
+ /** Meant as a temporary glue method between all the shortpixel methods and the newer structures
44
+ *
45
+ * @param Object $pixel WPShortPixel instance.
46
+ */
47
+
48
+ public function setShortPixel($pixel)
49
+ {
50
+ $this->shortPixel = $pixel;
51
+ }
52
+
53
+ public function loadView()
54
+ {
55
+ if (is_null($this->template))
56
+ {
57
+ // error
58
+ return false;
59
+ }
60
+
61
+ $layout = $this->layout;
62
+ $controller = $this;
63
+
64
+ $template_path = \ShortPixelTools::getPluginPath() . 'class/view/' . $this->template . '.php';
65
+ if (file_exists($template_path))
66
+ include($template_path);
67
+
68
+ }
69
+
70
+ protected function processPostData($post)
71
+ {
72
+ // most likely to easy .
73
+ foreach($post as $name => $data )
74
+ {
75
+ $this->postData[sanitize_text_field($name)] = sanitize_text_field($data);
76
+ }
77
+ }
78
+
79
+
80
+
81
+
82
+
83
+ }
class/db/shortpixel-custom-meta-dao.php CHANGED
@@ -3,7 +3,7 @@
3
  class ShortPixelCustomMetaDao {
4
  const META_VERSION = 1;
5
  private $db, $excludePatterns;
6
-
7
  private static $fields = array(
8
  ShortPixelMeta::TABLE_SUFFIX => array(
9
  "folder_id" => "d",
@@ -20,6 +20,8 @@ class ShortPixelCustomMetaDao {
20
  "status" => "d",
21
  "retries" => "d",
22
  "message" => "s",
 
 
23
  "ext_meta_id" => "d" //this is nggPid for now
24
  ),
25
  ShortPixelFolder::TABLE_SUFFIX => array(
@@ -30,12 +32,12 @@ class ShortPixelCustomMetaDao {
30
  "ts_created" => "s",
31
  )
32
  );
33
-
34
  public function __construct($db, $excludePatterns = false) {
35
  $this->db = $db;
36
  $this->excludePatterns = $excludePatterns;
37
  }
38
-
39
  public static function getCreateFolderTableSQL($tablePrefix, $charsetCollate) {
40
  return "CREATE TABLE {$tablePrefix}shortpixel_folders (
41
  id mediumint(9) NOT NULL AUTO_INCREMENT,
@@ -50,7 +52,7 @@ class ShortPixelCustomMetaDao {
50
  ) $charsetCollate;";
51
  // UNIQUE INDEX spf_path_md5 (path_md5)
52
  }
53
-
54
  public static function getCreateMetaTableSQL($tablePrefix, $charsetCollate) {
55
  return "CREATE TABLE {$tablePrefix}shortpixel_meta (
56
  id mediumint(10) NOT NULL AUTO_INCREMENT,
@@ -77,7 +79,7 @@ class ShortPixelCustomMetaDao {
77
  //UNIQUE INDEX sp_path_md5 (path_md5),
78
  //FOREIGN KEY fk_shortpixel_meta_folder(folder_id) REFERENCES {$tablePrefix}shortpixel_folders(id)
79
  }
80
-
81
  private function addIfMissing($type, $table, $key, $field, $fkTable = null, $fkField = null) {
82
  $hasIndexSql = "select count(*) hasIndex from information_schema.statistics where table_name = '%s' and index_name = '%s' and table_schema = database()";
83
  $createIndexSql = "ALTER TABLE %s ADD UNIQUE INDEX %s (%s)";
@@ -93,7 +95,7 @@ class ShortPixelCustomMetaDao {
93
  }
94
  return false;
95
  }
96
-
97
  public function tablesExist() {
98
  $hasTablesSql = "SELECT COUNT(1) tableCount FROM information_schema.tables WHERE table_schema='".$this->db->getDbName()."' "
99
  . "AND (table_name='".$this->db->getPrefix()."shortpixel_meta' OR table_name='".$this->db->getPrefix()."shortpixel_folders')";
@@ -103,26 +105,26 @@ class ShortPixelCustomMetaDao {
103
  }
104
  return false;
105
  }
106
-
107
  public function dropTables() {
108
  if($this->tablesExist()) {
109
  $this->db->query("DROP TABLE ".$this->db->getPrefix()."shortpixel_meta");
110
  $this->db->query("DROP TABLE ".$this->db->getPrefix()."shortpixel_folders");
111
  }
112
  }
113
-
114
  public function createUpdateShortPixelTables() {
115
  $res = $this->db->createUpdateSchema(array(
116
  self::getCreateFolderTableSQL($this->db->getPrefix(), $this->db->getCharsetCollate()),
117
- self::getCreateMetaTableSQL($this->db->getPrefix(), $this->db->getCharsetCollate())
118
  ));
119
  // Set up indexes, not handled well by WP DBDelta
120
  $this->addIfMissing("UNIQUE INDEX", $this->db->getPrefix()."shortpixel_folders", "spf_path_md5", "path_md5");
121
  $this->addIfMissing("UNIQUE INDEX", $this->db->getPrefix()."shortpixel_meta", "sp_path_md5", "path_md5");
122
- $this->addIfMissing("FOREIGN KEY", $this->db->getPrefix()."shortpixel_meta", "fk_shortpixel_meta_folder", "folder_id",
123
  $this->db->getPrefix()."shortpixel_folders", "id");
124
  }
125
-
126
  public function getFolders($deleted = false) {
127
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_folders" . ($deleted ? "" : " WHERE status <> -1");
128
  $rows = $this->db->query($sql);
@@ -132,7 +134,7 @@ class ShortPixelCustomMetaDao {
132
  }
133
  return $folders;
134
  }
135
-
136
  public function getFolder($path, $deleted = false) {
137
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_folders" . ($deleted ? "" : " WHERE path = %s AND status <> -1");
138
  $rows = $this->db->query($sql, array($path));
@@ -142,21 +144,21 @@ class ShortPixelCustomMetaDao {
142
  }
143
  return false;
144
  }
145
-
146
  public function hasFoldersTable() {
147
  global $wpdb;
148
  $foldersTable = $wpdb->get_results("SELECT COUNT(1) hasFoldersTable FROM information_schema.tables WHERE table_schema='{$wpdb->dbname}' AND table_name='{$wpdb->prefix}shortpixel_folders'");
149
  if(isset($foldersTable[0]->hasFoldersTable) && $foldersTable[0]->hasFoldersTable > 0) {
150
  return true;
151
  }
152
- return false;
153
  }
154
-
155
  public function addFolder($folder, $fileCount = 0) {
156
  //$sql = "INSERT INTO {$this->db->getPrefix()}shortpixel_folders (path, file_count, ts_created) values (%s, %d, now())";
157
  //$this->db->query($sql, array($folder, $fileCount));
158
- return $this->db->insert($this->db->getPrefix().'shortpixel_folders',
159
- array("path" => $folder, "path_md5" => md5($folder), "file_count" => $fileCount, "ts_updated" => date("Y-m-d H:i:s")),
160
  array("path" => "%s", "path_md5" => "%s", "file_count" => "%d", "ts_updated" => "%s"));
161
  }
162
 
@@ -224,16 +226,16 @@ class ShortPixelCustomMetaDao {
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) {
229
  $fileList = $folder->getFileList();
230
  $this->batchInsertImages($fileList, $folder->getId());
231
  }
232
  return $folderMsg;
233
-
234
  }
235
  /**
236
- *
237
  * @param type $path
238
  * @return false if saved OK, error message otherwise.
239
  */
@@ -269,8 +271,8 @@ class ShortPixelCustomMetaDao {
269
  }
270
  } else {
271
  return __('Folder does not exist.','shortpixel-image-optimiser');
272
- }
273
- }
274
 
275
  protected function metaToParams($meta) {
276
  $params = $types = array();
@@ -292,14 +294,14 @@ class ShortPixelCustomMetaDao {
292
  $id = $this->db->insert($this->db->getPrefix().'shortpixel_meta', $p->params, $p->types);
293
  return $id;
294
  }
295
-
296
  public function batchInsertImages($pathsFile, $folderId) {
297
  $pathsFileHandle = fopen($pathsFile, 'r');
298
-
299
  //facem un delete pe cele care nu au shortpixel_folder, pentru curatenie - am mai intalnit situatii in care stergerea s-a agatat (stop monitoring)
300
  $sqlCleanup = "DELETE FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id NOT IN (SELECT id FROM {$this->db->getPrefix()}shortpixel_folders)";
301
  $this->db->query($sqlCleanup);
302
-
303
  $values = ''; $inserted = 0;
304
  $sql = "INSERT IGNORE INTO {$this->db->getPrefix()}shortpixel_meta(folder_id, path, name, path_md5, status) VALUES ";
305
  for ($i = 0; ($path = fgets($pathsFileHandle)) !== false; $i++) {
@@ -319,37 +321,45 @@ class ShortPixelCustomMetaDao {
319
  unlink($pathsFile);
320
  return $inserted;
321
  }
322
-
323
  public function resetFailed() {
324
  $sql = "UPDATE {$this->db->getPrefix()}shortpixel_meta SET status = 0, retries = 0 WHERE status < 0";
325
  $this->db->query($sql);
326
  }
327
-
 
 
 
 
 
 
 
 
 
328
  public function getPaginatedMetas($hasNextGen, $filters, $count, $page, $orderby = false, $order = false) {
 
329
  $sql = "SELECT sm.id, sm.name, sm.path folder, "
330
  . ($hasNextGen ? "CASE WHEN ng.gid IS NOT NULL THEN 'NextGen' ELSE 'Custom' END media_type, " : "'Custom' media_type, ")
331
- . "sm.status, sm.compression_type, sm.keep_exif, sm.cmyk2rgb, sm.resize, sm.resize_width, sm.resize_height, sm.message "
332
  . "FROM {$this->db->getPrefix()}shortpixel_meta sm "
333
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
334
  . ($hasNextGen ? "LEFT JOIN {$this->db->getPrefix()}ngg_gallery ng on sf.path = ng.path " : " ")
335
- . "WHERE sf.status <> -1 AND sm.status <> 3";
336
  foreach($filters as $field => $value) {
337
  $sql .= " AND sm.$field " . $value->operator . " ". $value->value . " ";
338
- }
339
  $sql .= ($orderby ? " ORDER BY $orderby $order " : "")
340
  . " LIMIT $count OFFSET " . ($page - 1) * $count;
341
-
342
- //die($sql);
343
  return $this->db->query($sql);
344
  }
345
-
346
  public function getPendingMetas($count) {
347
  return $this->db->query("SELECT sm.id from {$this->db->getPrefix()}shortpixel_meta sm "
348
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
349
  . "WHERE sf.status <> -1 AND sm.status <> 3 AND ( sm.status = 0 OR sm.status = 1 OR (sm.status < 0 AND sm.retries < 3)) "
350
  . "ORDER BY sm.id DESC LIMIT $count");
351
  }
352
-
353
  public function getFolderOptimizationStatus($folderId) {
354
  $res = $this->db->query("SELECT SUM(CASE WHEN sm.status = 2 THEN 1 ELSE 0 END) Optimized, SUM(CASE WHEN sm.status = 1 THEN 1 ELSE 0 END) Pending, "
355
  . "SUM(CASE WHEN sm.status = 0 THEN 1 ELSE 0 END) Waiting, SUM(CASE WHEN sm.status < 0 THEN 1 ELSE 0 END) Failed, count(*) Total "
@@ -358,26 +368,50 @@ class ShortPixelCustomMetaDao {
358
  . "WHERE sf.id = $folderId");
359
  return $res[0];
360
  }
361
-
362
  public function getPendingMetaCount() {
363
  $res = $this->db->query("SELECT COUNT(sm.id) recCount from {$this->db->getPrefix()}shortpixel_meta sm "
364
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
365
  . "WHERE sf.status <> -1 AND sm.status <> 3 AND ( sm.status = 0 OR sm.status = 1 OR (sm.status < 0 AND sm.retries < 3))");
366
  return isset($res[0]->recCount) ? $res[0]->recCount : null;
367
  }
368
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  public function getCustomMetaCount($filters = array()) {
 
370
  $sql = "SELECT COUNT(sm.id) recCount FROM {$this->db->getPrefix()}shortpixel_meta sm "
371
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
372
- . "WHERE sf.status <> -1 AND sm.status <> 3";
373
  foreach($filters as $field => $value) {
374
  $sql .= " AND sm.$field " . $value->operator . " ". $value->value . " ";
375
  }
376
-
377
  $res = $this->db->query($sql);
378
  return isset($res[0]->recCount) ? $res[0]->recCount : 0;
379
  }
380
-
381
  public function getMeta($id, $deleted = false) {
382
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_meta WHERE id = %d " . ($deleted ? "" : " AND status <> -1");
383
  $rows = $this->db->query($sql, array($id));
@@ -386,44 +420,74 @@ class ShortPixelCustomMetaDao {
386
  if($meta->getPath()) {
387
  $meta->setWebPath(ShortPixelMetaFacade::filenameToRootRelative($meta->getPath()));
388
  }
 
 
 
 
389
  //die(var_dump($meta)."ZA META");
390
  return $meta;
391
  }
392
- return null;
393
  }
394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  public function getMetaForPath($path, $deleted = false) {
396
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_meta WHERE path = %s " . ($deleted ? "" : " AND status <> -1");
397
  $rows = $this->db->query($sql, array($path));
398
  foreach($rows as $row) {
399
  return new ShortPixelMeta($row);
400
  }
401
- return null;
402
  }
403
-
404
  public function update($meta) {
405
  $metaClass = get_class($meta);
406
  $tableSuffix = "";
407
- eval( '$tableSuffix = ' . $metaClass . '::TABLE_SUFFIX;');
408
- $sql = "UPDATE {$this->db->getPrefix()}shortpixel_" . $tableSuffix . " SET ";
 
 
409
  foreach(self::$fields[$tableSuffix] as $field => $type) {
410
  $getter = "get" . ShortPixelTools::snakeToCamel($field);
411
  $val = $meta->$getter();
412
  if($meta->$getter() !== null) {
413
- $sql .= " {$field} = %{$type},"; $params[] = $val;
 
414
  }
415
  }
416
-
417
  if(substr($sql, -1) != ',') {
418
  return; //no fields added;
419
- }
420
-
421
  $sql = rtrim($sql, ",");
422
  $sql .= " WHERE id = %d";
423
  $params[] = $meta->getId();
424
  $this->db->query($sql, $params);
425
  }
426
-
427
  public function delete($meta) {
428
  $metaClass = get_class($meta);
429
  $tableSuffix = "";
@@ -431,7 +495,7 @@ class ShortPixelCustomMetaDao {
431
  $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_" . $tableSuffix . " WHERE id = %d";
432
  $this->db->query($sql, array($meta->getId()));
433
  }
434
-
435
  public function countAllProcessableFiles() {
436
  $sql = "SELECT count(*) totalFiles, sum(CASE WHEN status = 2 THEN 1 ELSE 0 END) totalProcessedFiles,"
437
  ." sum(CASE WHEN status = 2 AND compression_type = 1 THEN 1 ELSE 0 END) totalProcLossyFiles,"
@@ -439,7 +503,7 @@ class ShortPixelCustomMetaDao {
439
  ." sum(CASE WHEN status = 2 AND compression_type = 0 THEN 1 ELSE 0 END) totalProcLosslessFiles"
440
  ." FROM {$this->db->getPrefix()}shortpixel_meta WHERE status <> -1";
441
  $rows = $this->db->query($sql);
442
-
443
  $filesWithErrors = array();
444
  $sql = "SELECT id, name, path, message FROM {$this->db->getPrefix()}shortpixel_meta WHERE status < -1 AND retries >= 3 LIMIT 30";
445
  $failRows = $this->db->query($sql);
@@ -451,17 +515,17 @@ class ShortPixelCustomMetaDao {
451
  $moreFilesWithErrors++;
452
  }
453
  }
454
-
455
  if(!isset($rows[0])) {
456
  $rows[0] = (object)array('totalFiles' => 0, 'totalProcessedFiles' => 0, 'totalProcLossyFiles' => 0, 'totalProcGlossyFiles' => 0, 'totalProcLosslessFiles' => 0);
457
  }
458
-
459
- return array("totalFiles" => $rows[0]->totalFiles, "mainFiles" => $rows[0]->totalFiles,
460
  "totalProcessedFiles" => $rows[0]->totalProcessedFiles, "mainProcessedFiles" => $rows[0]->totalProcessedFiles,
461
  "totalProcLossyFiles" => $rows[0]->totalProcLossyFiles, "mainProcLossyFiles" => $rows[0]->totalProcLossyFiles,
462
  "totalProcGlossyFiles" => $rows[0]->totalProcGlossyFiles, "mainProcGlossyFiles" => $rows[0]->totalProcGlossyFiles,
463
  "totalProcLosslessFiles" => $rows[0]->totalProcLosslessFiles, "mainProcLosslessFiles" => $rows[0]->totalProcLosslessFiles,
464
- "totalCustomFiles" => $rows[0]->totalFiles, "mainCustomFiles" => $rows[0]->totalFiles,
465
  "totalProcessedCustomFiles" => $rows[0]->totalProcessedFiles, "mainProcessedCustomFiles" => $rows[0]->totalProcessedFiles,
466
  "totalProcLossyCustomFiles" => $rows[0]->totalProcLossyFiles, "mainProcLossyCustomFiles" => $rows[0]->totalProcLossyFiles,
467
  "totalProcGlossyCustomFiles" => $rows[0]->totalProcGlossyFiles, "mainProcGlossyCustomFiles" => $rows[0]->totalProcGlossyFiles,
@@ -469,6 +533,6 @@ class ShortPixelCustomMetaDao {
469
  "filesWithErrors" => $filesWithErrors,
470
  "moreFilesWithErrors" => $moreFilesWithErrors
471
  );
472
-
473
  }
474
  }
3
  class ShortPixelCustomMetaDao {
4
  const META_VERSION = 1;
5
  private $db, $excludePatterns;
6
+
7
  private static $fields = array(
8
  ShortPixelMeta::TABLE_SUFFIX => array(
9
  "folder_id" => "d",
20
  "status" => "d",
21
  "retries" => "d",
22
  "message" => "s",
23
+ "ts_added" => 's',
24
+ "ts_optimized" => 's',
25
  "ext_meta_id" => "d" //this is nggPid for now
26
  ),
27
  ShortPixelFolder::TABLE_SUFFIX => array(
32
  "ts_created" => "s",
33
  )
34
  );
35
+
36
  public function __construct($db, $excludePatterns = false) {
37
  $this->db = $db;
38
  $this->excludePatterns = $excludePatterns;
39
  }
40
+
41
  public static function getCreateFolderTableSQL($tablePrefix, $charsetCollate) {
42
  return "CREATE TABLE {$tablePrefix}shortpixel_folders (
43
  id mediumint(9) NOT NULL AUTO_INCREMENT,
52
  ) $charsetCollate;";
53
  // UNIQUE INDEX spf_path_md5 (path_md5)
54
  }
55
+
56
  public static function getCreateMetaTableSQL($tablePrefix, $charsetCollate) {
57
  return "CREATE TABLE {$tablePrefix}shortpixel_meta (
58
  id mediumint(10) NOT NULL AUTO_INCREMENT,
79
  //UNIQUE INDEX sp_path_md5 (path_md5),
80
  //FOREIGN KEY fk_shortpixel_meta_folder(folder_id) REFERENCES {$tablePrefix}shortpixel_folders(id)
81
  }
82
+
83
  private function addIfMissing($type, $table, $key, $field, $fkTable = null, $fkField = null) {
84
  $hasIndexSql = "select count(*) hasIndex from information_schema.statistics where table_name = '%s' and index_name = '%s' and table_schema = database()";
85
  $createIndexSql = "ALTER TABLE %s ADD UNIQUE INDEX %s (%s)";
95
  }
96
  return false;
97
  }
98
+
99
  public function tablesExist() {
100
  $hasTablesSql = "SELECT COUNT(1) tableCount FROM information_schema.tables WHERE table_schema='".$this->db->getDbName()."' "
101
  . "AND (table_name='".$this->db->getPrefix()."shortpixel_meta' OR table_name='".$this->db->getPrefix()."shortpixel_folders')";
105
  }
106
  return false;
107
  }
108
+
109
  public function dropTables() {
110
  if($this->tablesExist()) {
111
  $this->db->query("DROP TABLE ".$this->db->getPrefix()."shortpixel_meta");
112
  $this->db->query("DROP TABLE ".$this->db->getPrefix()."shortpixel_folders");
113
  }
114
  }
115
+
116
  public function createUpdateShortPixelTables() {
117
  $res = $this->db->createUpdateSchema(array(
118
  self::getCreateFolderTableSQL($this->db->getPrefix(), $this->db->getCharsetCollate()),
119
+ self::getCreateMetaTableSQL($this->db->getPrefix(), $this->db->getCharsetCollate())
120
  ));
121
  // Set up indexes, not handled well by WP DBDelta
122
  $this->addIfMissing("UNIQUE INDEX", $this->db->getPrefix()."shortpixel_folders", "spf_path_md5", "path_md5");
123
  $this->addIfMissing("UNIQUE INDEX", $this->db->getPrefix()."shortpixel_meta", "sp_path_md5", "path_md5");
124
+ $this->addIfMissing("FOREIGN KEY", $this->db->getPrefix()."shortpixel_meta", "fk_shortpixel_meta_folder", "folder_id",
125
  $this->db->getPrefix()."shortpixel_folders", "id");
126
  }
127
+
128
  public function getFolders($deleted = false) {
129
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_folders" . ($deleted ? "" : " WHERE status <> -1");
130
  $rows = $this->db->query($sql);
134
  }
135
  return $folders;
136
  }
137
+
138
  public function getFolder($path, $deleted = false) {
139
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_folders" . ($deleted ? "" : " WHERE path = %s AND status <> -1");
140
  $rows = $this->db->query($sql, array($path));
144
  }
145
  return false;
146
  }
147
+
148
  public function hasFoldersTable() {
149
  global $wpdb;
150
  $foldersTable = $wpdb->get_results("SELECT COUNT(1) hasFoldersTable FROM information_schema.tables WHERE table_schema='{$wpdb->dbname}' AND table_name='{$wpdb->prefix}shortpixel_folders'");
151
  if(isset($foldersTable[0]->hasFoldersTable) && $foldersTable[0]->hasFoldersTable > 0) {
152
  return true;
153
  }
154
+ return false;
155
  }
156
+
157
  public function addFolder($folder, $fileCount = 0) {
158
  //$sql = "INSERT INTO {$this->db->getPrefix()}shortpixel_folders (path, file_count, ts_created) values (%s, %d, now())";
159
  //$this->db->query($sql, array($folder, $fileCount));
160
+ return $this->db->insert($this->db->getPrefix().'shortpixel_folders',
161
+ array("path" => $folder, "path_md5" => md5($folder), "file_count" => $fileCount, "ts_updated" => date("Y-m-d H:i:s")),
162
  array("path" => "%s", "path_md5" => "%s", "file_count" => "%d", "ts_updated" => "%s"));
163
  }
164
 
226
  return __('The folder could not be saved to the database. Please check that the plugin can create its database tables.', 'shortpixel-image-optimiser') . $folderMsg;
227
  }
228
  }
229
+
230
  if(!$folderMsg) {
231
  $fileList = $folder->getFileList();
232
  $this->batchInsertImages($fileList, $folder->getId());
233
  }
234
  return $folderMsg;
235
+
236
  }
237
  /**
238
+ *
239
  * @param type $path
240
  * @return false if saved OK, error message otherwise.
241
  */
271
  }
272
  } else {
273
  return __('Folder does not exist.','shortpixel-image-optimiser');
274
+ }
275
+ }
276
 
277
  protected function metaToParams($meta) {
278
  $params = $types = array();
294
  $id = $this->db->insert($this->db->getPrefix().'shortpixel_meta', $p->params, $p->types);
295
  return $id;
296
  }
297
+
298
  public function batchInsertImages($pathsFile, $folderId) {
299
  $pathsFileHandle = fopen($pathsFile, 'r');
300
+
301
  //facem un delete pe cele care nu au shortpixel_folder, pentru curatenie - am mai intalnit situatii in care stergerea s-a agatat (stop monitoring)
302
  $sqlCleanup = "DELETE FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id NOT IN (SELECT id FROM {$this->db->getPrefix()}shortpixel_folders)";
303
  $this->db->query($sqlCleanup);
304
+
305
  $values = ''; $inserted = 0;
306
  $sql = "INSERT IGNORE INTO {$this->db->getPrefix()}shortpixel_meta(folder_id, path, name, path_md5, status) VALUES ";
307
  for ($i = 0; ($path = fgets($pathsFileHandle)) !== false; $i++) {
321
  unlink($pathsFile);
322
  return $inserted;
323
  }
324
+
325
  public function resetFailed() {
326
  $sql = "UPDATE {$this->db->getPrefix()}shortpixel_meta SET status = 0, retries = 0 WHERE status < 0";
327
  $this->db->query($sql);
328
  }
329
+
330
+ /** Reset Restored items
331
+ * On Bulk Optimize, Reset the restored status so it will process these images again.
332
+ *
333
+ **/
334
+ public function resetRestored() {
335
+ $sql = "UPDATE {$this->db->getPrefix()}shortpixel_meta SET status = 0, retries = 0 WHERE status = 3";
336
+ $this->db->query($sql);
337
+ }
338
+
339
  public function getPaginatedMetas($hasNextGen, $filters, $count, $page, $orderby = false, $order = false) {
340
+ // [BS] Remove exclusion for sm.status <> 3. Status 3 is 'restored, perform no action'
341
  $sql = "SELECT sm.id, sm.name, sm.path folder, "
342
  . ($hasNextGen ? "CASE WHEN ng.gid IS NOT NULL THEN 'NextGen' ELSE 'Custom' END media_type, " : "'Custom' media_type, ")
343
+ . "sm.status, sm.compression_type, sm.keep_exif, sm.cmyk2rgb, sm.resize, sm.resize_width, sm.resize_height, sm.message, sm.ts_added, sm.ts_optimized "
344
  . "FROM {$this->db->getPrefix()}shortpixel_meta sm "
345
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
346
  . ($hasNextGen ? "LEFT JOIN {$this->db->getPrefix()}ngg_gallery ng on sf.path = ng.path " : " ")
347
+ . "WHERE sf.status <> -1"; // AND sm.status <> 3
348
  foreach($filters as $field => $value) {
349
  $sql .= " AND sm.$field " . $value->operator . " ". $value->value . " ";
350
+ }
351
  $sql .= ($orderby ? " ORDER BY $orderby $order " : "")
352
  . " LIMIT $count OFFSET " . ($page - 1) * $count;
 
 
353
  return $this->db->query($sql);
354
  }
355
+
356
  public function getPendingMetas($count) {
357
  return $this->db->query("SELECT sm.id from {$this->db->getPrefix()}shortpixel_meta sm "
358
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
359
  . "WHERE sf.status <> -1 AND sm.status <> 3 AND ( sm.status = 0 OR sm.status = 1 OR (sm.status < 0 AND sm.retries < 3)) "
360
  . "ORDER BY sm.id DESC LIMIT $count");
361
  }
362
+
363
  public function getFolderOptimizationStatus($folderId) {
364
  $res = $this->db->query("SELECT SUM(CASE WHEN sm.status = 2 THEN 1 ELSE 0 END) Optimized, SUM(CASE WHEN sm.status = 1 THEN 1 ELSE 0 END) Pending, "
365
  . "SUM(CASE WHEN sm.status = 0 THEN 1 ELSE 0 END) Waiting, SUM(CASE WHEN sm.status < 0 THEN 1 ELSE 0 END) Failed, count(*) Total "
368
  . "WHERE sf.id = $folderId");
369
  return $res[0];
370
  }
371
+
372
  public function getPendingMetaCount() {
373
  $res = $this->db->query("SELECT COUNT(sm.id) recCount from {$this->db->getPrefix()}shortpixel_meta sm "
374
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
375
  . "WHERE sf.status <> -1 AND sm.status <> 3 AND ( sm.status = 0 OR sm.status = 1 OR (sm.status < 0 AND sm.retries < 3))");
376
  return isset($res[0]->recCount) ? $res[0]->recCount : null;
377
  }
378
+
379
+ /** Get all Custom Meta when status is other than 'restored' **/
380
+ public function getPendingBulkRestore($count)
381
+ {
382
+ return $this->db->query("SELECT sm.id from {$this->db->getPrefix()}shortpixel_meta sm "
383
+ . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
384
+ . "WHERE sf.status <> -1 AND sm.status = " . ShortPixelMeta::FILE_STATUS_TORESTORE
385
+ . " ORDER BY sm.id DESC LIMIT $count");
386
+ }
387
+
388
+ /** Sets files from folders that are selected for bulk restore to the status 'To Restore';
389
+ *
390
+ */
391
+ public function setBulkRestore($folder_id)
392
+ {
393
+ if (! is_numeric($folder_id) || $folder_id <= 0)
394
+ return false;
395
+
396
+ $table = $this->db->getPrefix() . 'shortpixel_meta';
397
+ //$sql = "UPDATE status on "; ShortPixelMeta::FILE_STATUS_TORESTORE
398
+ $this->db->update($table, array('status' => ShortPixelMeta::FILE_STATUS_TORESTORE), array('folder_id' => $folder_id), '%d', '%d' );
399
+ }
400
+
401
+
402
  public function getCustomMetaCount($filters = array()) {
403
+ // [BS] Remove exclusion for sm.status <> 3. Status 3 is 'restored, perform no action'
404
  $sql = "SELECT COUNT(sm.id) recCount FROM {$this->db->getPrefix()}shortpixel_meta sm "
405
  . "INNER JOIN {$this->db->getPrefix()}shortpixel_folders sf on sm.folder_id = sf.id "
406
+ . "WHERE sf.status <> -1"; // AND sm.status <> 3
407
  foreach($filters as $field => $value) {
408
  $sql .= " AND sm.$field " . $value->operator . " ". $value->value . " ";
409
  }
410
+
411
  $res = $this->db->query($sql);
412
  return isset($res[0]->recCount) ? $res[0]->recCount : 0;
413
  }
414
+
415
  public function getMeta($id, $deleted = false) {
416
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_meta WHERE id = %d " . ($deleted ? "" : " AND status <> -1");
417
  $rows = $this->db->query($sql, array($id));
420
  if($meta->getPath()) {
421
  $meta->setWebPath(ShortPixelMetaFacade::filenameToRootRelative($meta->getPath()));
422
  }
423
+ if ($meta->getStatus() == $meta::FILE_STATUS_UNPROCESSED)
424
+ {
425
+ $meta = $this->updateMetaWithSettings($meta);
426
+ }
427
  //die(var_dump($meta)."ZA META");
428
  return $meta;
429
  }
430
+ return null;
431
  }
432
 
433
+ /** If File is not yet processed, don't use empty default meta, but use the global settings
434
+ *
435
+ * @param $meta ShortPixelMeta object
436
+ * @return ShortPixelMetaObject - Replaced meta object data
437
+ * @author Bas Schuiling
438
+ */
439
+ protected function updateMetaWithSettings($meta)
440
+ {
441
+ $objSettings = new WPShortPixelSettings();
442
+
443
+ $meta->setKeepExif( $objSettings->keepExif );
444
+ $meta->setCmyk2rgb($objSettings->CMYKtoRGBconversion);
445
+ $meta->setResize($objSettings->resizeImages);
446
+ $meta->setResizeWidth($objSettings->resizeWidth);
447
+ $meta->setResizeHeight($objSettings->resizeHeight);
448
+ $meta->setCompressionType($objSettings->compressionType);
449
+ $meta->setBackup($objSettings->backupImages);
450
+ // update the record. If the image is pending the meta will be requested again.
451
+ $this->update($meta);
452
+ return $meta;
453
+ }
454
+
455
+
456
  public function getMetaForPath($path, $deleted = false) {
457
  $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_meta WHERE path = %s " . ($deleted ? "" : " AND status <> -1");
458
  $rows = $this->db->query($sql, array($path));
459
  foreach($rows as $row) {
460
  return new ShortPixelMeta($row);
461
  }
462
+ return null;
463
  }
464
+
465
  public function update($meta) {
466
  $metaClass = get_class($meta);
467
  $tableSuffix = "";
468
+ $tableSuffix = $metaClass::TABLE_SUFFIX;
469
+ $prefix = $this->db->getPrefix();
470
+ // eval( '$tableSuffix = ' . $metaClass . '::TABLE_SUFFIX;'); // horror!
471
+ $sql = "UPDATE " . $prefix . "shortpixel_" . $tableSuffix . " SET ";
472
  foreach(self::$fields[$tableSuffix] as $field => $type) {
473
  $getter = "get" . ShortPixelTools::snakeToCamel($field);
474
  $val = $meta->$getter();
475
  if($meta->$getter() !== null) {
476
+ $sql .= " {$field} = %{$type},";
477
+ $params[] = $val;
478
  }
479
  }
480
+
481
  if(substr($sql, -1) != ',') {
482
  return; //no fields added;
483
+ }
484
+
485
  $sql = rtrim($sql, ",");
486
  $sql .= " WHERE id = %d";
487
  $params[] = $meta->getId();
488
  $this->db->query($sql, $params);
489
  }
490
+
491
  public function delete($meta) {
492
  $metaClass = get_class($meta);
493
  $tableSuffix = "";
495
  $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_" . $tableSuffix . " WHERE id = %d";
496
  $this->db->query($sql, array($meta->getId()));
497
  }
498
+
499
  public function countAllProcessableFiles() {
500
  $sql = "SELECT count(*) totalFiles, sum(CASE WHEN status = 2 THEN 1 ELSE 0 END) totalProcessedFiles,"
501
  ." sum(CASE WHEN status = 2 AND compression_type = 1 THEN 1 ELSE 0 END) totalProcLossyFiles,"
503
  ." sum(CASE WHEN status = 2 AND compression_type = 0 THEN 1 ELSE 0 END) totalProcLosslessFiles"
504
  ." FROM {$this->db->getPrefix()}shortpixel_meta WHERE status <> -1";
505
  $rows = $this->db->query($sql);
506
+
507
  $filesWithErrors = array();
508
  $sql = "SELECT id, name, path, message FROM {$this->db->getPrefix()}shortpixel_meta WHERE status < -1 AND retries >= 3 LIMIT 30";
509
  $failRows = $this->db->query($sql);
515
  $moreFilesWithErrors++;
516
  }
517
  }
518
+
519
  if(!isset($rows[0])) {
520
  $rows[0] = (object)array('totalFiles' => 0, 'totalProcessedFiles' => 0, 'totalProcLossyFiles' => 0, 'totalProcGlossyFiles' => 0, 'totalProcLosslessFiles' => 0);
521
  }
522
+
523
+ return array("totalFiles" => $rows[0]->totalFiles, "mainFiles" => $rows[0]->totalFiles,
524
  "totalProcessedFiles" => $rows[0]->totalProcessedFiles, "mainProcessedFiles" => $rows[0]->totalProcessedFiles,
525
  "totalProcLossyFiles" => $rows[0]->totalProcLossyFiles, "mainProcLossyFiles" => $rows[0]->totalProcLossyFiles,
526
  "totalProcGlossyFiles" => $rows[0]->totalProcGlossyFiles, "mainProcGlossyFiles" => $rows[0]->totalProcGlossyFiles,
527
  "totalProcLosslessFiles" => $rows[0]->totalProcLosslessFiles, "mainProcLosslessFiles" => $rows[0]->totalProcLosslessFiles,
528
+ "totalCustomFiles" => $rows[0]->totalFiles, "mainCustomFiles" => $rows[0]->totalFiles,
529
  "totalProcessedCustomFiles" => $rows[0]->totalProcessedFiles, "mainProcessedCustomFiles" => $rows[0]->totalProcessedFiles,
530
  "totalProcLossyCustomFiles" => $rows[0]->totalProcLossyFiles, "mainProcLossyCustomFiles" => $rows[0]->totalProcLossyFiles,
531
  "totalProcGlossyCustomFiles" => $rows[0]->totalProcGlossyFiles, "mainProcGlossyCustomFiles" => $rows[0]->totalProcGlossyFiles,
533
  "filesWithErrors" => $filesWithErrors,
534
  "moreFilesWithErrors" => $moreFilesWithErrors
535
  );
536
+
537
  }
538
  }
class/db/shortpixel-meta-facade.php CHANGED
@@ -283,6 +283,9 @@ class ShortPixelMetaFacade {
283
  $this->updateMeta();
284
  } else {
285
  if($status) {
 
 
 
286
  $this->rawMeta['ShortPixel']['WaitingProcessing'] = true;
287
  unset($this->rawMeta['ShortPixel']['ErrCode']);
288
  } else {
@@ -703,9 +706,7 @@ class ShortPixelMetaFacade {
703
  }
704
  }
705
 
706
- public function optimizationStarted() {
707
- if($this->getType() == self::MEDIA_LIBRARY_TYPE) {
708
- do_action( 'shortpixel_start_image_optimisation', $this->getId() );
709
- }
710
  }
711
  }
283
  $this->updateMeta();
284
  } else {
285
  if($status) {
286
+ if(!isset($this->rawMeta['ShortPixel']['WaitingProcessing']) || !$this->rawMeta['ShortPixel']['WaitingProcessing']) {
287
+ self::optimizationStarted($this->getId());
288
+ }
289
  $this->rawMeta['ShortPixel']['WaitingProcessing'] = true;
290
  unset($this->rawMeta['ShortPixel']['ErrCode']);
291
  } else {
706
  }
707
  }
708
 
709
+ public static function optimizationStarted($id) {
710
+ do_action( 'shortpixel_start_image_optimisation', $id );
 
 
711
  }
712
  }
class/db/wp-shortpixel-db.php CHANGED
@@ -1,14 +1,14 @@
1
  <?php
2
 
3
  class WpShortPixelDb implements ShortPixelDb {
4
-
5
  protected $prefix;
6
  protected $defaultShowErrors;
7
-
8
  public function __construct($prefix = null) {
9
  $this->prefix = $prefix;
10
  }
11
-
12
  public static function createUpdateSchema($tableDefinitions) {
13
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
14
  $res = array();
@@ -17,7 +17,7 @@ class WpShortPixelDb implements ShortPixelDb {
17
  }
18
  return $res;
19
  }
20
-
21
  public static function checkCustomTables() {
22
  global $wpdb;
23
  if(function_exists("is_multisite") && is_multisite()) {
@@ -30,28 +30,28 @@ class WpShortPixelDb implements ShortPixelDb {
30
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb($prefix));
31
  $spMetaDao->createUpdateShortPixelTables();
32
  }
33
-
34
  } else {
35
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb());
36
  $spMetaDao->createUpdateShortPixelTables();
37
- }
38
  }
39
-
40
  public function getCharsetCollate() {
41
  global $wpdb;
42
  return $wpdb->get_charset_collate();
43
  }
44
-
45
  public function getPrefix() {
46
  global $wpdb;
47
  return $this->prefix ? $this->prefix : $wpdb->prefix;
48
  }
49
-
50
  public function getDbName() {
51
  global $wpdb;
52
  return $wpdb->dbname;
53
  }
54
-
55
  public function query($sql, $params = false) {
56
  global $wpdb;
57
  if($params) {
@@ -65,18 +65,26 @@ class WpShortPixelDb implements ShortPixelDb {
65
  $wpdb->insert($table, $params, $format);
66
  return $wpdb->insert_id;
67
  }
68
-
 
 
 
 
 
 
 
 
69
  public function prepare($query, $args) {
70
  global $wpdb;
71
  return $wpdb->prepare($query, $args);
72
  }
73
-
74
  public function hideErrors() {
75
  global $wpdb;
76
  $this->defaultShowErrors = $wpdb->show_errors;
77
  $wpdb->show_errors = false;
78
  }
79
-
80
  public function restoreErrors() {
81
  global $wpdb;
82
  $wpdb->show_errors = $this->defaultShowErrors;
1
  <?php
2
 
3
  class WpShortPixelDb implements ShortPixelDb {
4
+
5
  protected $prefix;
6
  protected $defaultShowErrors;
7
+
8
  public function __construct($prefix = null) {
9
  $this->prefix = $prefix;
10
  }
11
+
12
  public static function createUpdateSchema($tableDefinitions) {
13
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
14
  $res = array();
17
  }
18
  return $res;
19
  }
20
+
21
  public static function checkCustomTables() {
22
  global $wpdb;
23
  if(function_exists("is_multisite") && is_multisite()) {
30
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb($prefix));
31
  $spMetaDao->createUpdateShortPixelTables();
32
  }
33
+
34
  } else {
35
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb());
36
  $spMetaDao->createUpdateShortPixelTables();
37
+ }
38
  }
39
+
40
  public function getCharsetCollate() {
41
  global $wpdb;
42
  return $wpdb->get_charset_collate();
43
  }
44
+
45
  public function getPrefix() {
46
  global $wpdb;
47
  return $this->prefix ? $this->prefix : $wpdb->prefix;
48
  }
49
+
50
  public function getDbName() {
51
  global $wpdb;
52
  return $wpdb->dbname;
53
  }
54
+
55
  public function query($sql, $params = false) {
56
  global $wpdb;
57
  if($params) {
65
  $wpdb->insert($table, $params, $format);
66
  return $wpdb->insert_id;
67
  }
68
+
69
+ public function update($table, $params, $where, $format = null, $where_format = null)
70
+ {
71
+ global $wpdb;
72
+ $updated = $wpdb->update($table, $params, $where, $format, $where_format);
73
+ return $updated;
74
+
75
+ }
76
+
77
  public function prepare($query, $args) {
78
  global $wpdb;
79
  return $wpdb->prepare($query, $args);
80
  }
81
+
82
  public function hideErrors() {
83
  global $wpdb;
84
  $this->defaultShowErrors = $wpdb->show_errors;
85
  $wpdb->show_errors = false;
86
  }
87
+
88
  public function restoreErrors() {
89
  global $wpdb;
90
  $wpdb->show_errors = $this->defaultShowErrors;
class/db/wp-shortpixel-media-library-adapter.php CHANGED
@@ -287,11 +287,8 @@ class WpShortPixelMediaLbraryAdapter {
287
  $thumbs[]= $th;
288
  }
289
  }
290
- if( defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIX') || defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES') ){
291
  $suffixes = defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES') ? explode(',', SHORTPIXEL_CUSTOM_THUMB_SUFFIXES) : array();
292
- if( defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIX') ){
293
- $suffixes[] = SHORTPIXEL_CUSTOM_THUMB_SUFFIX;
294
- }
295
  foreach ($suffixes as $suffix){
296
  $pattern = '/' . preg_quote($base, '/') . '-\d+x\d+'. $suffix . '\.'. $ext .'/';
297
  foreach($thumbsCandidates as $th) {
287
  $thumbs[]= $th;
288
  }
289
  }
290
+ if( defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES') ){
291
  $suffixes = defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES') ? explode(',', SHORTPIXEL_CUSTOM_THUMB_SUFFIXES) : array();
 
 
 
292
  foreach ($suffixes as $suffix){
293
  $pattern = '/' . preg_quote($base, '/') . '-\d+x\d+'. $suffix . '\.'. $ext .'/';
294
  foreach($thumbsCandidates as $th) {
class/front/img-to-picture-webp.php CHANGED
@@ -26,23 +26,33 @@ class ShortPixelImgToPictureWebp
26
  {
27
  // Don't do anything with the RSS feed.
28
  if (is_feed() || is_admin()) {
29
- return $content;
30
  }
 
 
 
 
 
 
 
31
 
32
- return preg_replace_callback('/<img[^>]*>/', array('ShortPixelImgToPictureWebp', 'convertImage'), $content);
33
  }
34
 
35
  public static function convertImage($match)
36
  {
37
  // Do nothing with images that have the 'sp-no-webp' class.
38
  if (strpos($match[0], 'sp-no-webp')) {
39
- return $match[0];
40
  }
41
 
42
  $img = self::get_attributes($match[0]);
 
 
 
43
 
44
  $srcInfo = self::lazyGet($img, 'src');
45
  $src = $srcInfo['value'];
 
46
  $srcPrefix = $srcInfo['prefix'];
47
 
48
  $srcsetInfo = self::lazyGet($img, 'srcset');
@@ -62,7 +72,8 @@ class ShortPixelImgToPictureWebp
62
  }
63
  $imageBase = dirname(get_attached_file($id)) . '/';
64
  */
65
- $updir = wp_upload_dir();
 
66
  $proto = explode("://", $src);
67
  if (count($proto) > 1) {
68
  //check that baseurl uses the same http/https proto and if not, change
@@ -73,12 +84,31 @@ class ShortPixelImgToPictureWebp
73
  $updir['baseurl'] = $proto . "://" . $base[1];
74
  }
75
  }
76
- }
77
- $imageBase = str_replace($updir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $src);
78
- if ($imageBase == $src) {
79
- return $match[0];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
  $imageBase = dirname($imageBase) . '/';
 
 
 
 
 
82
 
83
  // We don't wanna have src-ish attributes on the <picture>
84
  unset($img['src']);
@@ -88,6 +118,7 @@ class ShortPixelImgToPictureWebp
88
  unset($img['sizes']);
89
  unset($img['alt']);
90
  $srcsetWebP = '';
 
91
  if ($srcset) {
92
  $defs = explode(",", $srcset);
93
  foreach ($defs as $item) {
@@ -112,7 +143,6 @@ class ShortPixelImgToPictureWebp
112
  } else {
113
  $srcset = trim($src);
114
 
115
- // die(var_dump($match));
116
 
117
  $fileWebPCompat = $imageBase . wp_basename($srcset, '.' . pathinfo($srcset, PATHINFO_EXTENSION)) . '.webp';
118
  $fileWebP = $imageBase . wp_basename($srcset) . '.webp';
@@ -126,7 +156,7 @@ class ShortPixelImgToPictureWebp
126
  }
127
  //return($match[0]. "<!-- srcsetTZF:".$srcsetWebP." -->");
128
  if (!strlen($srcsetWebP)) {
129
- return $match[0];
130
  }
131
 
132
  //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
@@ -140,11 +170,143 @@ class ShortPixelImgToPictureWebp
140
  .'</picture>';
141
  }
142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  public static function get_attributes($image_node)
144
  {
145
  if (function_exists("mb_convert_encoding")) {
146
  $image_node = mb_convert_encoding($image_node, 'HTML-ENTITIES', 'UTF-8');
147
  }
 
 
 
 
148
  $dom = new DOMDocument();
149
  @$dom->loadHTML($image_node);
150
  $image = $dom->getElementsByTagName('img')->item(0);
26
  {
27
  // Don't do anything with the RSS feed.
28
  if (is_feed() || is_admin()) {
29
+ return $content . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG convert is_feed or is_admin -->' : '');
30
  }
31
+ $content = preg_replace_callback('/<img[^>]*>/', array('self', 'convertImage'), $content);
32
+ //$content = preg_replace_callback('/background.*[^:](url\(.*\)[,;])/im', array('self', 'convertInlineStyle'), $content);
33
+
34
+ // [BS] No callback because we need preg_match_all
35
+ //$content = self::testInlineStyle($content);
36
+ // $content = preg_replace_callback('/background.*[^:]url\([\'|"](.*)[\'|"]\)[,;]/imU',array('self', 'convertInlineStyle'), $content);
37
+ return $content . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG WebP converted -->' : '');
38
 
 
39
  }
40
 
41
  public static function convertImage($match)
42
  {
43
  // Do nothing with images that have the 'sp-no-webp' class.
44
  if (strpos($match[0], 'sp-no-webp')) {
45
+ return $match[0] . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG convertImage sp-no-webp -->' : '');
46
  }
47
 
48
  $img = self::get_attributes($match[0]);
49
+ // [BS] Can return false in case of Module fail. Escape in that case with unmodified image
50
+ if ($img === false)
51
+ return $match[0];
52
 
53
  $srcInfo = self::lazyGet($img, 'src');
54
  $src = $srcInfo['value'];
55
+ $parsed_url = parse_url($src);
56
  $srcPrefix = $srcInfo['prefix'];
57
 
58
  $srcsetInfo = self::lazyGet($img, 'srcset');
72
  }
73
  $imageBase = dirname(get_attached_file($id)) . '/';
74
  */
75
+
76
+ /* [BS] $updir = wp_upload_dir();
77
  $proto = explode("://", $src);
78
  if (count($proto) > 1) {
79
  //check that baseurl uses the same http/https proto and if not, change
84
  $updir['baseurl'] = $proto . "://" . $base[1];
85
  }
86
  }
87
+ } */
88
+
89
+
90
+ /* [BS] $imageBase = str_replace($updir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $src);
91
+ if ($imageBase == $src) { //maybe the site uses a CDN or a subdomain?
92
+ $urlParsed = parse_url($src);
93
+ $srcHost = array_reverse(explode('.', $urlParsed['host']));
94
+ $baseParsed = parse_url($updir['baseurl']);
95
+ $baseurlHost = array_reverse(explode('.', $baseParsed['host']));
96
+ if ($srcHost[0] == $baseurlHost[0] && $srcHost[1] == $baseurlHost[1]
97
+ && (strlen($srcHost[1]) > 3 || isset($srcHost[2]) && isset($srcHost[2]) && $srcHost[2] == $baseurlHost[2])) {
98
+ $baseurl = str_replace($baseParsed['scheme'] . '://' . $baseParsed['host'], $urlParsed['scheme'] . '://' . $urlParsed['host'], $updir['baseurl']);
99
+ $imageBase = str_replace($baseurl, SHORTPIXEL_UPLOADS_BASE, $src);
100
+ }
101
+ if ($imageBase == $src) { //looks like it's an external URL though...
102
+ if(isset($_GET['SHORTPIXEL_DEBUG'])) WPShortPixel::log('SPDBG baseurl ' . $updir['baseurl'] . ' doesn\'t match ' . $src, true);
103
+ return $match[0] . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG baseurl ' . $updir['baseurl'] . ' doesn\'t match ' . $src . ' -->' : '');
104
+ }
105
  }
106
  $imageBase = dirname($imageBase) . '/';
107
+ */
108
+ $imageBase = static::getImageBase($src);
109
+ if($imageBase === false) {
110
+ return $match[0] . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG baseurl doesn\'t match ' . $src . ' -->' : '');
111
+ }
112
 
113
  // We don't wanna have src-ish attributes on the <picture>
114
  unset($img['src']);
118
  unset($img['sizes']);
119
  unset($img['alt']);
120
  $srcsetWebP = '';
121
+
122
  if ($srcset) {
123
  $defs = explode(",", $srcset);
124
  foreach ($defs as $item) {
143
  } else {
144
  $srcset = trim($src);
145
 
 
146
 
147
  $fileWebPCompat = $imageBase . wp_basename($srcset, '.' . pathinfo($srcset, PATHINFO_EXTENSION)) . '.webp';
148
  $fileWebP = $imageBase . wp_basename($srcset) . '.webp';
156
  }
157
  //return($match[0]. "<!-- srcsetTZF:".$srcsetWebP." -->");
158
  if (!strlen($srcsetWebP)) {
159
+ return $match[0] . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG no srcsetWebP found (' . $srcsetWebP . ') -->' : '');
160
  }
161
 
162
  //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
170
  .'</picture>';
171
  }
172
 
173
+ public static function testInlineStyle($content)
174
+ {
175
+ //preg_match_all('/background.*[^:](url\(.*\))[;]/isU', $content, $matches);
176
+ preg_match_all('/url\(.*\)/isU', $content, $matches);
177
+
178
+ if (count($matches) == 0)
179
+ return $content;
180
+
181
+ $content = self::convertInlineStyle($matches, $content);
182
+ return $content;
183
+ }
184
+
185
+ /** Function to convert inline CSS backgrounds to webp
186
+ * @param $match Regex match for inline style
187
+ * @return String Replaced (or not) content for webp.
188
+ * @author Bas Schuiling
189
+ */
190
+ public static function convertInlineStyle($matches, $content)
191
+ {
192
+ // ** matches[0] = url('xx') matches[1] the img URL.
193
+ // preg_match_all('/url\(\'(.*)\'\)/imU', $match, $matches);
194
+
195
+ // if (count($matches) == 0)
196
+ // return $match; // something wrong, escape.
197
+
198
+ //$content = $match;
199
+ $allowed_exts = array('jpg', 'jpeg', 'gif', 'png');
200
+ $converted = array();
201
+
202
+ for($i = 0; $i < count($matches[0]); $i++)
203
+ {
204
+ $item = $matches[0][$i];
205
+
206
+ preg_match('/url\(\'(.*)\'\)/imU', $item, $match);
207
+ if (! isset($match[1]))
208
+ continue;
209
+
210
+ $url = $match[1];
211
+ $parsed_url = parse_url($url);
212
+ $filename = basename($url);
213
+
214
+ $fileonly = pathinfo($url, PATHINFO_FILENAME);
215
+ $ext = pathinfo($url, PATHINFO_EXTENSION);
216
+
217
+ if (! in_array($ext, $allowed_exts))
218
+ continue;
219
+
220
+ $imageBaseURL = str_replace($filename, '', $url);
221
+
222
+ $imageBase = static::getImageBase($url);
223
+
224
+ if (! $imageBase) // returns false if URL is external, do nothing with that.
225
+ continue;
226
+
227
+ $checkedFile = false;
228
+ if (file_exists($imageBase . $fileonly . '.' . $ext . '.webp'))
229
+ {
230
+ $checkedFile = $imageBaseURL . $fileonly . '.' . $ext . '.webp';
231
+ }
232
+ elseif (file_exists($imageBase . $fileonly . '.webp'))
233
+ {
234
+ $checkedFile = $imageBaseURL . $fileonly . '.webp';
235
+ }
236
+
237
+ if ($checkedFile)
238
+ {
239
+ // if webp, then add another URL() def after the targeted one. (str_replace old full URL def, with new one on main match?
240
+ $target_urldef = $matches[0][$i];
241
+ if (! isset($converted[$target_urldef])) // if the same image is on multiple elements, this replace might go double. prevent.
242
+ {
243
+ $converted[] = $target_urldef;
244
+ $new_urldef = "url('" . $checkedFile . "'), " . $target_urldef;
245
+ $content = str_replace($target_urldef, $new_urldef, $content);
246
+ }
247
+ }
248
+
249
+ }
250
+
251
+ return $content;
252
+ }
253
+
254
+ /* ** Utility function to get ImageBase.
255
+ ** @param String $src Image Source
256
+ ** @returns String The Image Base
257
+ **/
258
+ public static function getImageBase($src)
259
+ {
260
+ $updir = wp_upload_dir();
261
+ $proto = explode("://", $src);
262
+ if (count($proto) > 1) {
263
+ //check that baseurl uses the same http/https proto and if not, change
264
+ $proto = $proto[0];
265
+ if (strpos($updir['baseurl'], $proto."://") === false) {
266
+ $base = explode("://", $updir['baseurl']);
267
+ if (count($base) > 1) {
268
+ $updir['baseurl'] = $proto . "://" . $base[1];
269
+ }
270
+ }
271
+ }
272
+
273
+ $imageBase = str_replace($updir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $src);
274
+ if ($imageBase == $src) { //for themes images or other non-uploads paths
275
+ $imageBase = str_replace(content_url(), WP_CONTENT_DIR, $src);
276
+ }
277
+
278
+ if ($imageBase == $src) { //maybe the site uses a CDN or a subdomain? - Or relative link
279
+ $urlParsed = parse_url($src);
280
+ $baseParsed = parse_url($updir['baseurl']);
281
+
282
+ $srcHost = array_reverse(explode('.', $urlParsed['host']));
283
+ $baseurlHost = array_reverse(explode('.', $baseParsed['host']));
284
+
285
+ if ($srcHost[0] == $baseurlHost[0] && $srcHost[1] == $baseurlHost[1]
286
+ && (strlen($srcHost[1]) > 3 || isset($srcHost[2]) && isset($srcHost[2]) && $srcHost[2] == $baseurlHost[2])) {
287
+
288
+ $baseurl = str_replace($baseParsed['scheme'] . '://' . $baseParsed['host'], $urlParsed['scheme'] . '://' . $urlParsed['host'], $updir['baseurl']);
289
+ $imageBase = str_replace($baseurl, SHORTPIXEL_UPLOADS_BASE, $src);
290
+ }
291
+ if ($imageBase == $src) { //looks like it's an external URL though...
292
+ return false;
293
+ }
294
+ }
295
+
296
+
297
+ $imageBase = trailingslashit(dirname($imageBase));
298
+ return $imageBase;
299
+ }
300
+
301
  public static function get_attributes($image_node)
302
  {
303
  if (function_exists("mb_convert_encoding")) {
304
  $image_node = mb_convert_encoding($image_node, 'HTML-ENTITIES', 'UTF-8');
305
  }
306
+ // [BS] Escape when DOM Module not installed
307
+ if (! class_exists('DOMDocument'))
308
+ return false;
309
+
310
  $dom = new DOMDocument();
311
  @$dom->loadHTML($image_node);
312
  $image = $dom->getElementsByTagName('img')->item(0);
class/model/shortpixel-entity.php CHANGED
@@ -11,6 +11,7 @@ class ShortPixelEntity{
11
  }
12
  foreach($dataArr as $key => $val) {
13
  $setter = 'set' . ShortPixelTools::snakeToCamel($key);
 
14
  if(method_exists($this, $setter)) {
15
  $this->$setter($val);
16
  }
11
  }
12
  foreach($dataArr as $key => $val) {
13
  $setter = 'set' . ShortPixelTools::snakeToCamel($key);
14
+
15
  if(method_exists($this, $setter)) {
16
  $this->$setter($val);
17
  }
class/model/shortpixel-meta.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class ShortPixelMeta extends ShortPixelEntity{
4
  const META_VERSION = 1;
5
-
6
  protected $id;
7
  protected $folderId;
8
  protected $extMetaId;
@@ -32,15 +32,23 @@ class ShortPixelMeta extends ShortPixelEntity{
32
  protected $tsAdded;
33
  protected $tsOptimized;
34
  protected $thumbs;
35
-
36
  const TABLE_SUFFIX = 'meta';
37
  const WEBP_THUMB_PREFIX = 'sp-webp-';
38
  const FOUND_THUMB_PREFIX = 'sp-found-';
39
-
 
 
 
 
 
 
 
 
40
  public function __construct($data = array()) {
41
  parent::__construct($data);
42
  }
43
-
44
  /**
45
  * @return meta string to be embedded into the image
46
  */
@@ -48,14 +56,14 @@ class ShortPixelMeta extends ShortPixelEntity{
48
  //To be implemented
49
  return base64_encode("Not now John.");
50
  }
51
-
52
  function getImprovementPercent() {
53
  if(is_numeric($this->message)) {
54
  return round($this->message,2);
55
  }
56
  return 0;
57
  }
58
-
59
  function getId() {
60
  return $this->id;
61
  }
@@ -114,7 +122,7 @@ class ShortPixelMeta extends ShortPixelEntity{
114
  function setPath($path) {
115
  $this->path = $path;
116
  }
117
-
118
  function getName() {
119
  return $this->name;
120
  }
@@ -290,11 +298,11 @@ class ShortPixelMeta extends ShortPixelEntity{
290
  function setMessage($message) {
291
  $this->message = $message;
292
  }
293
-
294
  function getType() {
295
  return (isset($this->folderId) ? ShortPixelMetaFacade::CUSTOM_TYPE : ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE);
296
  }
297
-
298
  function getThumbs() {
299
  return $this->thumbs;
300
  }
@@ -302,7 +310,7 @@ class ShortPixelMeta extends ShortPixelEntity{
302
  function setThumbs($thumbs) {
303
  $this->thumbs = $thumbs;
304
  }
305
-
306
  function addThumbs($thumbs) {
307
  $this->thumbs = array_merge($this->thumbs, $thumbs);
308
  }
2
 
3
  class ShortPixelMeta extends ShortPixelEntity{
4
  const META_VERSION = 1;
5
+
6
  protected $id;
7
  protected $folderId;
8
  protected $extMetaId;
32
  protected $tsAdded;
33
  protected $tsOptimized;
34
  protected $thumbs;
35
+
36
  const TABLE_SUFFIX = 'meta';
37
  const WEBP_THUMB_PREFIX = 'sp-webp-';
38
  const FOUND_THUMB_PREFIX = 'sp-found-';
39
+
40
+ // [BS] Attempt to shed some light on Meta Status on File.
41
+ // Anything lower than 0 is a processing error.
42
+ const FILE_STATUS_UNPROCESSED = 0;
43
+ const FILE_STATUS_PENDING = 1;
44
+ const FILE_STATUS_SUCCESS = 2;
45
+ const FILE_STATUS_RESTORED = 3;
46
+ const FILE_STATUS_TORESTORE = 4; // Used for Bulk Restore
47
+
48
  public function __construct($data = array()) {
49
  parent::__construct($data);
50
  }
51
+
52
  /**
53
  * @return meta string to be embedded into the image
54
  */
56
  //To be implemented
57
  return base64_encode("Not now John.");
58
  }
59
+
60
  function getImprovementPercent() {
61
  if(is_numeric($this->message)) {
62
  return round($this->message,2);
63
  }
64
  return 0;
65
  }
66
+
67
  function getId() {
68
  return $this->id;
69
  }
122
  function setPath($path) {
123
  $this->path = $path;
124
  }
125
+
126
  function getName() {
127
  return $this->name;
128
  }
298
  function setMessage($message) {
299
  $this->message = $message;
300
  }
301
+
302
  function getType() {
303
  return (isset($this->folderId) ? ShortPixelMetaFacade::CUSTOM_TYPE : ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE);
304
  }
305
+
306
  function getThumbs() {
307
  return $this->thumbs;
308
  }
310
  function setThumbs($thumbs) {
311
  $this->thumbs = $thumbs;
312
  }
313
+
314
  function addThumbs($thumbs) {
315
  $this->thumbs = array_merge($this->thumbs, $thumbs);
316
  }
class/shortpixel-tools.php CHANGED
@@ -11,11 +11,21 @@ class ShortPixelTools {
11
  }
12
  return $data;
13
  }*/
14
-
15
  public static function snakeToCamel($snake_case) {
16
  return str_replace(' ', '', ucwords(str_replace('_', ' ', $snake_case)));
17
  }
18
 
 
 
 
 
 
 
 
 
 
 
19
  public static function requestIsFrontendAjax()
20
  {
21
  $script_filename = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : '';
@@ -39,13 +49,38 @@ class ShortPixelTools {
39
  //If no checks triggered, we end up here - not an AJAX request.
40
  return false;
41
  }
42
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  public static function commonPrefix($str1, $str2) {
44
  $limit = min(strlen($str1), strlen($str2));
45
  for ($i = 0; $i < $limit && $str1[$i] === $str2[$i]; $i++);
46
  return substr($str1, 0, $i);
47
  }
48
-
49
  /**
50
  * This is a simplified wp_send_json made compatible with WP 3.2.x to 3.4.x
51
  * @param type $response
@@ -55,6 +90,8 @@ class ShortPixelTools {
55
  die(json_encode($response));
56
  }
57
 
 
 
58
  /**
59
  * finds if an array contains an item, comparing the property given as key
60
  * @param $item
@@ -69,9 +106,9 @@ class ShortPixelTools {
69
  }
70
  }
71
  return false;
72
- }
73
  }
74
 
75
  function ShortPixelVDD($v){
76
  return highlight_string("<?php\n\$data =\n" . var_export($v, true) . ";\n?>");
77
- }
11
  }
12
  return $data;
13
  }*/
14
+
15
  public static function snakeToCamel($snake_case) {
16
  return str_replace(' ', '', ucwords(str_replace('_', ' ', $snake_case)));
17
  }
18
 
19
+ public static function getPluginPath()
20
+ {
21
+ return plugin_dir_path(SHORTPIXEL_PLUGIN_FILE);
22
+ }
23
+
24
+ public static function namespaceit($name)
25
+ {
26
+ return '\ShortPixel\\' . $name;
27
+ }
28
+
29
  public static function requestIsFrontendAjax()
30
  {
31
  $script_filename = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : '';
49
  //If no checks triggered, we end up here - not an AJAX request.
50
  return false;
51
  }
52
+
53
+ /** Function to convert dateTime object to a date output
54
+ *
55
+ * Function checks if the date is recent and then uploads are friendlier message. Taken from media library list table date function
56
+ * @param DateTime $date DateTime object
57
+ **/
58
+ public static function format_nice_date( $date ) {
59
+
60
+ if ( '0000-00-00 00:00:00' === $date->format('Y-m-d ') ) {
61
+ $h_time = '';
62
+ } else {
63
+ $time = $date->format('U'); //get_post_time( 'G', true, $post, false );
64
+ if ( ( abs( $t_diff = time() - $time ) ) < DAY_IN_SECONDS ) {
65
+ if ( $t_diff < 0 ) {
66
+ $h_time = sprintf( __( '%s from now' ), human_time_diff( $time ) );
67
+ } else {
68
+ $h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) );
69
+ }
70
+ } else {
71
+ $h_time = $date->format( 'Y/m/d' );
72
+ }
73
+ }
74
+
75
+ return $h_time;
76
+ }
77
+
78
  public static function commonPrefix($str1, $str2) {
79
  $limit = min(strlen($str1), strlen($str2));
80
  for ($i = 0; $i < $limit && $str1[$i] === $str2[$i]; $i++);
81
  return substr($str1, 0, $i);
82
  }
83
+
84
  /**
85
  * This is a simplified wp_send_json made compatible with WP 3.2.x to 3.4.x
86
  * @param type $response
90
  die(json_encode($response));
91
  }
92
 
93
+
94
+
95
  /**
96
  * finds if an array contains an item, comparing the property given as key
97
  * @param $item
106
  }
107
  }
108
  return false;
109
+ }
110
  }
111
 
112
  function ShortPixelVDD($v){
113
  return highlight_string("<?php\n\$data =\n" . var_export($v, true) . ";\n?>");
114
+ }
class/view/shortpixel-list-table.php CHANGED
@@ -2,14 +2,14 @@
2
 
3
  if( ! class_exists( 'WP_List_Table' ) ) {
4
  require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
5
- }
6
 
7
  class ShortPixelListTable extends WP_List_Table {
8
 
9
  protected $ctrl;
10
  protected $spMetaDao;
11
  protected $hasNextGen;
12
-
13
  public function __construct($ctrl, $spMetaDao, $hasNextGen) {
14
  parent::__construct( array(
15
  'singular' => __('Image','shortpixel-image-optimiser'), //singular name of the listed records
@@ -29,6 +29,7 @@ class ShortPixelListTable extends WP_List_Table {
29
  $columns['name'] = __('Filename','shortpixel-image-optimiser');
30
  $columns['folder'] = __('Folder','shortpixel-image-optimiser');
31
  $columns['media_type'] = __('Type','shortpixel-image-optimiser');
 
32
  $columns['status'] = __('Status','shortpixel-image-optimiser');
33
  $columns['options'] = __('Options','shortpixel-image-optimiser');
34
  //$columns = apply_filters('shortpixel_list_columns', $columns);
@@ -39,45 +40,86 @@ class ShortPixelListTable extends WP_List_Table {
39
  function column_cb( $item ) {
40
  return sprintf('<input type="checkbox" name="bulk-optimize[]" value="%s" />', $item->id);
41
  }
42
-
43
  function column_default( $item, $column_name ) {
44
- switch( $column_name ) {
45
  case 'name':
46
  $title = '<a href="' . ShortPixelMetaFacade::pathToWebPath($item->folder) . '" title="'.$item->folder.'" target="_blank"><strong>' . $item->name . '</strong></a>';
47
 
48
  $url = ShortPixelMetaFacade::pathToWebPath($item->folder);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  $actions = array(
50
- 'optimize' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
51
- esc_attr( $_REQUEST['page'] ), 'optimize', absint( $item->id ), wp_create_nonce( 'sp_optimize_image' ),
52
- __('Optimize','shortpixel-image-optimiser')),
53
- 'retry' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
54
- esc_attr( $_REQUEST['page'] ), 'optimize', absint( $item->id ), wp_create_nonce( 'sp_optimize_image' ),
55
- __('Retry','shortpixel-image-optimiser')),
56
- 'restore' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
57
- esc_attr( $_REQUEST['page'] ), 'restore', absint( $item->id ), wp_create_nonce( 'sp_restore_image' ),
58
- __('Restore','shortpixel-image-optimiser')),
59
- 'redolossless' => sprintf( '<a href="?page=%s&action=%s&type=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
60
- esc_attr( $_REQUEST['page'] ), 'redo', 'lossless', absint( $item->id ), wp_create_nonce( 'sp_redo_image' ),
61
- __('Re-optimize lossless','shortpixel-image-optimiser')),
62
- 'redolossy' => sprintf( '<a href="?page=%s&action=%s&type=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
63
- esc_attr( $_REQUEST['page'] ), 'redo', 'lossy', absint( $item->id ), wp_create_nonce( 'sp_redo_image' ),
64
- __('Re-optimize lossy','shortpixel-image-optimiser')),
65
- 'redoglossy' => sprintf( '<a href="?page=%s&action=%s&type=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
66
- esc_attr( $_REQUEST['page'] ), 'redo', 'glossy', absint( $item->id ), wp_create_nonce( 'sp_redo_image' ),
67
- __('Re-optimize glossy','shortpixel-image-optimiser')),
68
- 'quota' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
69
- esc_attr( $_REQUEST['page'] ), 'quota', absint( $item->id ), wp_create_nonce( 'sp_check_quota' ),
70
- __('Check quota','shortpixel-image-optimiser')),
71
  'view' => sprintf( '<a href="%s" target="_blank">%s</a>', $url, __('View','shortpixel-image-optimiser'))
72
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  $settings = $this->ctrl->getSettings();
74
  $actionsEnabled = array();
75
  if($settings->quotaExceeded) {
76
  $actionsEnabled['quota'] = true;
77
- } elseif($item->status == 0 || $item->status == 1 || $item->status == 3 ) {
 
 
78
  $actionsEnabled['optimize'] = true;
79
- } elseif($item->status == 2) {
80
  $actionsEnabled['restore'] = true;
 
81
  switch($item->compression_type) {
82
  case 2:
83
  $actionsEnabled['redolossy'] = $actionsEnabled['redolossless'] = true;
@@ -89,7 +131,7 @@ class ShortPixelListTable extends WP_List_Table {
89
  $actionsEnabled['redolossy'] = $actionsEnabled['redoglossy'] = true;
90
  }
91
  //$actionsEnabled['redo'.($item->compression_type == 1 ? "lossless" : "lossy")] = true;
92
- } elseif($item->status == 3 || $item->status < 0) {
93
  $actionsEnabled['retry'] = true;
94
  }
95
  $actionsEnabled['view'] = true;
@@ -99,14 +141,19 @@ class ShortPixelListTable extends WP_List_Table {
99
  return ShortPixelMetaFacade::pathToRootRelative($item->folder);
100
  case 'status':
101
  switch($item->status) {
102
- case 3: $msg = __('Restored','shortpixel-image-optimiser');
103
- break;
104
- case 2: $msg = 0 + $item->message == 0
105
- ? __('Bonus processing','shortpixel-image-optimiser')
106
- : __('Reduced by','shortpixel-image-optimiser') . " <strong>" . $item->message . "%</strong>"
107
- . (0 + $item->message < 5 ? "<br>" . __('Bonus processing','shortpixel-image-optimiser') . "." : "");
 
 
 
 
 
108
  break;
109
- case 1: $msg = "<img src=\"" . plugins_url( 'shortpixel-image-optimiser/res/img/loading.gif') . "\" class='sp-loading-small'>&nbsp;"
110
  . __('Pending','shortpixel-image-optimiser');
111
  break;
112
  case 0: $msg = __('Waiting','shortpixel-image-optimiser');
@@ -121,20 +168,38 @@ class ShortPixelListTable extends WP_List_Table {
121
  return "<div id='sp-cust-msg-C-" . $item->id . "'>" . $msg . "</div>";
122
  break;
123
  case 'options':
124
- return __($item->compression_type == 2 ? 'Glossy' : ($item->compression_type == 1 ? 'Lossy' : 'Lossless'),'shortpixel-image-optimiser')
125
- . ($item->keep_exif == 1 ? "": ", " . __('Keep EXIF','shortpixel-image-optimiser'))
126
- . ($item->cmyk2rgb ? "": ", " . __('Preserve CMYK','shortpixel-image-optimiser'));
 
 
 
 
127
  case 'media_type':
128
  return $item->$column_name;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  default:
130
  return print_r( $item, true ) ; //Show the whole array for troubleshooting purposes
131
  }
132
  }
133
-
134
  public function no_items() {
135
  echo(__('No images avaliable. Go to <a href="options-general.php?page=wp-shortpixel#adv-settings">Advanced Settings</a> to configure additional folders to be optimized.','shortpixel-image-optimiser'));
136
  }
137
-
138
  /**
139
  * Columns to make sortable.
140
  *
@@ -144,19 +209,20 @@ class ShortPixelListTable extends WP_List_Table {
144
  $sortable_columns = array(
145
  'name' => array( 'name', true ),
146
  'folder' => array( 'folder', true ),
147
- 'status' => array( 'status', false )
 
148
  );
149
 
150
  return $sortable_columns;
151
  }
152
-
153
  /**
154
  * Handles data query and filter, sorting, and pagination.
155
  */
156
  public function prepare_items() {
157
 
158
  $this->_column_headers = $this->get_column_info();
159
-
160
  $this->_column_headers[0] = $this->get_columns();
161
 
162
  /** Process actions */
@@ -170,15 +236,17 @@ class ShortPixelListTable extends WP_List_Table {
170
  'total_items' => $total_items, //WE have to calculate the total number of items
171
  'per_page' => $perPage //WE have to determine how many items to show on a page
172
  ));
173
-
174
- $orderby = ( ! empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : 'ts_added';
 
175
  // If no order, default to asc
176
- $order = ( ! empty($_GET['order'] ) ) ? $_GET['order'] : 'desc';
177
-
 
178
  $this->items = $this->spMetaDao->getPaginatedMetas($this->hasNextGen, $this->getFilter(), $perPage, $currentPage, $orderby, $order);
179
  return $this->items;
180
- }
181
-
182
  protected function getFilter() {
183
  $filter = array();
184
  if(isset($_GET["s"]) && strlen($_GET["s"])) {
@@ -186,34 +254,36 @@ class ShortPixelListTable extends WP_List_Table {
186
  }
187
  return $filter;
188
  }
189
-
190
  public function record_count() {
191
  return $this->spMetaDao->getCustomMetaCount($this->getFilter());
192
  }
193
-
194
  public function action_optimize_image( $id ) {
195
  $this->ctrl->optimizeCustomImage($id);
196
  }
197
-
198
  public function action_restore_image( $id ) {
199
  $this->ctrl->doCustomRestore($id);
200
  }
201
-
202
  public function action_redo_image( $id, $type = false ) {
203
  $this->ctrl->redo('C-' . $id, $type);
204
  }
205
-
206
  public function process_actions() {
207
 
208
  //Detect when a bulk action is being triggered...
209
  $nonce = isset($_REQUEST['_wpnonce']) ? esc_attr($_REQUEST['_wpnonce']) : false;
 
 
210
  switch($this->current_action()) {
211
  case 'optimize':
212
  if (!wp_verify_nonce($nonce, 'sp_optimize_image')) {
213
  die('Error.');
214
  } else {
215
  $this->action_optimize_image(absint($_GET['image']));
216
- wp_redirect(esc_url(remove_query_arg(array('action', 'image', '_wpnonce'))));
217
  exit;
218
  }
219
  break;
@@ -222,7 +292,7 @@ class ShortPixelListTable extends WP_List_Table {
222
  die('Error.');
223
  } else {
224
  $this->action_restore_image(absint($_GET['image']));
225
- wp_redirect(esc_url(remove_query_arg(array('action', 'image', '_wpnonce'))));
226
  exit;
227
  }
228
  break;
@@ -230,8 +300,8 @@ class ShortPixelListTable extends WP_List_Table {
230
  if (!wp_verify_nonce($nonce, 'sp_redo_image')) {
231
  die('Error.');
232
  } else {
233
- $this->action_redo_image(absint($_GET['image']), $_GET['type']);
234
- wp_redirect(esc_url(remove_query_arg(array('action', 'image', '_wpnonce'))));
235
  exit;
236
  }
237
  break;
2
 
3
  if( ! class_exists( 'WP_List_Table' ) ) {
4
  require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
5
+ }
6
 
7
  class ShortPixelListTable extends WP_List_Table {
8
 
9
  protected $ctrl;
10
  protected $spMetaDao;
11
  protected $hasNextGen;
12
+
13
  public function __construct($ctrl, $spMetaDao, $hasNextGen) {
14
  parent::__construct( array(
15
  'singular' => __('Image','shortpixel-image-optimiser'), //singular name of the listed records
29
  $columns['name'] = __('Filename','shortpixel-image-optimiser');
30
  $columns['folder'] = __('Folder','shortpixel-image-optimiser');
31
  $columns['media_type'] = __('Type','shortpixel-image-optimiser');
32
+ $columns['date'] = __('Date', 'shortpixel-image-optimiser');
33
  $columns['status'] = __('Status','shortpixel-image-optimiser');
34
  $columns['options'] = __('Options','shortpixel-image-optimiser');
35
  //$columns = apply_filters('shortpixel_list_columns', $columns);
40
  function column_cb( $item ) {
41
  return sprintf('<input type="checkbox" name="bulk-optimize[]" value="%s" />', $item->id);
42
  }
43
+
44
  function column_default( $item, $column_name ) {
45
+ switch( $column_name ) {
46
  case 'name':
47
  $title = '<a href="' . ShortPixelMetaFacade::pathToWebPath($item->folder) . '" title="'.$item->folder.'" target="_blank"><strong>' . $item->name . '</strong></a>';
48
 
49
  $url = ShortPixelMetaFacade::pathToWebPath($item->folder);
50
+
51
+ $admin_url = admin_url('upload.php');
52
+ $admin_url = add_query_arg(array('page' => sanitize_text_field($_REQUEST['page']), 'image' => absint($item->id), 'noheader' => 'true'), $admin_url);
53
+
54
+ // add the order options if active
55
+ if (isset($_GET['orderby']))
56
+ {
57
+ $order = isset($_GET['order']) ? sanitize_text_field($_GET['order']) : 'desc';
58
+ $admin_url = add_query_arg(array('orderby' => sanitize_text_field($_GET['orderby']), 'order' => $order), $admin_url);
59
+ }
60
+ if (isset($_GET['paged']))
61
+ {
62
+ $admin_url = add_query_arg('paged', intval($_GET['paged']), $admin_url);
63
+ }
64
+
65
  $actions = array(
66
+ 'optimize' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'optimize', '_wpnonce' => wp_create_nonce( 'sp_optimize_image' ), ), $admin_url), __('Optimize','shortpixel-image-optimiser')),
67
+
68
+ 'retry' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'optimize', '_wpnonce' => wp_create_nonce( 'sp_optimize_image' ), ), $admin_url), __('Retry','shortpixel-image-optimiser')),
69
+
70
+ 'redolossless' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'redo', '_wpnonce' => wp_create_nonce( 'sp_redo_image'), 'type' => 'lossless'),$admin_url), __('Re-optimize lossless','shortpixel-image-optimiser')),
71
+
72
+ 'redolossy' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'redo', '_wpnonce' => wp_create_nonce( 'sp_redo_image'), 'type' => 'lossy'), $admin_url), __('Re-optimize lossy','shortpixel-image-optimiser')),
73
+
74
+ 'redoglossy' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'redo', '_wpnonce' => wp_create_nonce( 'sp_redo_image'), 'type' => 'glossy'), $admin_url), __('Re-optimize glossy','shortpixel-image-optimiser')),
75
+
76
+ 'quota' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'quota', '_wpnonce' => wp_create_nonce( 'sp_check_quota')), $admin_url), __('Check quota','shortpixel-image-optimiser')),
77
+
78
+ 'restore' => sprintf('<a href="%s">%s</a>', add_query_arg(array('action' => 'restore', '_wpnonce' => wp_create_nonce( 'sp_restore_image')), $admin_url), __('Restore','shortpixel-image-optimiser')),
79
+
80
+
81
+ 'compare' => sprintf( '<a href="javascript:ShortPixel.loadComparer(\'C-' . absint($item->id) . '\');">%s</a>"',
82
+ __('Compare', 'shortpixel-image-optimiser')),
 
 
 
 
83
  'view' => sprintf( '<a href="%s" target="_blank">%s</a>', $url, __('View','shortpixel-image-optimiser'))
84
  );
85
+
86
+
87
+ /*'optimize' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
88
+ esc_attr( $_REQUEST['page'] ), 'optimize', absint( $item->id ), wp_create_nonce( 'sp_optimize_image' ),
89
+ __('Optimize','shortpixel-image-optimiser')), */
90
+ /*'retry' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
91
+ esc_attr( $_REQUEST['page'] ), 'optimize', absint( $item->id ), wp_create_nonce( 'sp_optimize_image' ),
92
+ __('Retry','shortpixel-image-optimiser')), */
93
+
94
+ /* 'redolossless' => sprintf( '<a href="?page=%s&action=%s&type=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
95
+ esc_attr( $_REQUEST['page'] ), 'redo', 'lossless', absint( $item->id ), wp_create_nonce( 'sp_redo_image' ),
96
+ __('Re-optimize lossless','shortpixel-image-optimiser')), */
97
+ /* 'redolossy' => sprintf( '<a href="?page=%s&action=%s&type=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
98
+ esc_attr( $_REQUEST['page'] ), 'redo', 'lossy', absint( $item->id ), wp_create_nonce( 'sp_redo_image' ),
99
+ __('Re-optimize lossy','shortpixel-image-optimiser')), */
100
+ /*'redoglossy' => sprintf( '<a href="?page=%s&action=%s&type=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
101
+ esc_attr( $_REQUEST['page'] ), 'redo', 'glossy', absint( $item->id ), wp_create_nonce( 'sp_redo_image' ),
102
+ __('Re-optimize glossy','shortpixel-image-optimiser')), */
103
+ /*'quota' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
104
+ esc_attr( $_REQUEST['page'] ), 'quota', absint( $item->id ), wp_create_nonce( 'sp_check_quota' ),
105
+ __('Check quota','shortpixel-image-optimiser')), */
106
+ /*'restore' => sprintf( '<a href="?page=%s&action=%s&image=%s&_wpnonce=%s&noheader=true">%s</a>',
107
+ esc_attr( $_REQUEST['page'] ), 'restore', absint( $item->id ), wp_create_nonce( 'sp_restore_image' ),
108
+ __('Restore','shortpixel-image-optimiser')), */
109
+
110
+ $has_backup = $this->ctrl->getBackupFolderAny($item->folder, array());
111
+
112
  $settings = $this->ctrl->getSettings();
113
  $actionsEnabled = array();
114
  if($settings->quotaExceeded) {
115
  $actionsEnabled['quota'] = true;
116
+ } elseif($item->status == ShortPixelMeta::FILE_STATUS_UNPROCESSED ||
117
+ $item->status == ShortPixelMeta::FILE_STATUS_PENDING ||
118
+ $item->status == ShortPixelMeta::FILE_STATUS_RESTORED ) {
119
  $actionsEnabled['optimize'] = true;
120
+ } elseif($item->status == ShortPixelMeta::FILE_STATUS_SUCCESS && $has_backup) {
121
  $actionsEnabled['restore'] = true;
122
+ $actionsEnabled['compare'] = true;
123
  switch($item->compression_type) {
124
  case 2:
125
  $actionsEnabled['redolossy'] = $actionsEnabled['redolossless'] = true;
131
  $actionsEnabled['redolossy'] = $actionsEnabled['redoglossy'] = true;
132
  }
133
  //$actionsEnabled['redo'.($item->compression_type == 1 ? "lossless" : "lossy")] = true;
134
+ } elseif($item->status == ShortPixelMeta::FILE_STATUS_RESTORED || $item->status < ShortPixelMeta::FILE_STATUS_UNPROCESSED) {
135
  $actionsEnabled['retry'] = true;
136
  }
137
  $actionsEnabled['view'] = true;
141
  return ShortPixelMetaFacade::pathToRootRelative($item->folder);
142
  case 'status':
143
  switch($item->status) {
144
+ case ShortPixelMeta::FILE_STATUS_RESTORED:
145
+ $msg = __('Restored','shortpixel-image-optimiser');
146
+ break;
147
+ case ShortPixelMeta::FILE_STATUS_TORESTORE:
148
+ $msg = __('Restore Pending','shortpixel-image-optimiser');
149
+ break;
150
+ case ShortPixelMeta::FILE_STATUS_SUCCESS:
151
+ $msg = 0 + intval($item->message) == 0
152
+ ? __('Bonus processing','shortpixel-image-optimiser')
153
+ : __('Reduced by','shortpixel-image-optimiser') . " <strong>" . $item->message . "%</strong>"
154
+ . (0 + intval($item->message) < 5 ? "<br>" . __('Bonus processing','shortpixel-image-optimiser') . "." : "");
155
  break;
156
+ case 1: $msg = "<img src=\"" . plugins_url( 'shortpixel-image-optimiser/res/img/loading.gif') . "\" class='sp-loading-small'>&nbsp;"
157
  . __('Pending','shortpixel-image-optimiser');
158
  break;
159
  case 0: $msg = __('Waiting','shortpixel-image-optimiser');
168
  return "<div id='sp-cust-msg-C-" . $item->id . "'>" . $msg . "</div>";
169
  break;
170
  case 'options':
171
+ // [BS] Unprocessed items have no meta. Anything displayed here would be wrong.
172
+ if (ShortPixelMeta::FILE_STATUS_UNPROCESSED == $item->status || ShortPixelMeta::FILE_STATUS_RESTORED == $item->status)
173
+ return __('', 'shortpixel-image-optimiser');
174
+
175
+ return __($item->compression_type == 2 ? 'Glossy' : ($item->compression_type == 1 ? 'Lossy' : 'Lossless'),'shortpixel-image-optimiser')
176
+ . ($item->keep_exif == 0 ? "": ", " . __('Keep EXIF','shortpixel-image-optimiser'))
177
+ . ($item->cmyk2rgb == 1 ? "": ", " . __('Preserve CMYK','shortpixel-image-optimiser'));
178
  case 'media_type':
179
  return $item->$column_name;
180
+ case 'date':
181
+ //[BS] if not optimized, show the timestamp when the file was added
182
+
183
+ /*if ( '0000-00-00 00:00:00' === $item->ts_optimized )
184
+ $usetime = $item->ts_added;
185
+ else {
186
+ $usetime = $item->ts_optimized;
187
+ } */
188
+ if ( '0000-00-00 00:00:00' === $item->ts_optimized )
189
+ return "<span class='date-C-" . $item->id . "'></span>";
190
+
191
+ $date = new DateTime($item->ts_optimized);
192
+ return "<span class='date-C-" . $item->id . "'>" . ShortPixelTools::format_nice_date($date) . "</div>";
193
+ break;
194
  default:
195
  return print_r( $item, true ) ; //Show the whole array for troubleshooting purposes
196
  }
197
  }
198
+
199
  public function no_items() {
200
  echo(__('No images avaliable. Go to <a href="options-general.php?page=wp-shortpixel#adv-settings">Advanced Settings</a> to configure additional folders to be optimized.','shortpixel-image-optimiser'));
201
  }
202
+
203
  /**
204
  * Columns to make sortable.
205
  *
209
  $sortable_columns = array(
210
  'name' => array( 'name', true ),
211
  'folder' => array( 'folder', true ),
212
+ 'status' => array( 'status', false ),
213
+ 'date' => array('ts_optimized', true),
214
  );
215
 
216
  return $sortable_columns;
217
  }
218
+
219
  /**
220
  * Handles data query and filter, sorting, and pagination.
221
  */
222
  public function prepare_items() {
223
 
224
  $this->_column_headers = $this->get_column_info();
225
+
226
  $this->_column_headers[0] = $this->get_columns();
227
 
228
  /** Process actions */
236
  'total_items' => $total_items, //WE have to calculate the total number of items
237
  'per_page' => $perPage //WE have to determine how many items to show on a page
238
  ));
239
+
240
+ // [BS] Moving this from ts_added since often images get added at the same time, resulting in unpredictable sorting
241
+ $orderby = ( ! empty( $_GET['orderby'] ) ) ? sanitize_text_field($_GET['orderby']) : 'id';
242
  // If no order, default to asc
243
+ $order = ( ! empty($_GET['order'] ) ) ? sanitize_text_field($_GET['order']) : 'desc';
244
+ $currentPage = ( ! empty($_GET['paged'])) ? intval($_GET['paged']) : $currentPage;
245
+
246
  $this->items = $this->spMetaDao->getPaginatedMetas($this->hasNextGen, $this->getFilter(), $perPage, $currentPage, $orderby, $order);
247
  return $this->items;
248
+ }
249
+
250
  protected function getFilter() {
251
  $filter = array();
252
  if(isset($_GET["s"]) && strlen($_GET["s"])) {
254
  }
255
  return $filter;
256
  }
257
+
258
  public function record_count() {
259
  return $this->spMetaDao->getCustomMetaCount($this->getFilter());
260
  }
261
+
262
  public function action_optimize_image( $id ) {
263
  $this->ctrl->optimizeCustomImage($id);
264
  }
265
+
266
  public function action_restore_image( $id ) {
267
  $this->ctrl->doCustomRestore($id);
268
  }
269
+
270
  public function action_redo_image( $id, $type = false ) {
271
  $this->ctrl->redo('C-' . $id, $type);
272
  }
273
+
274
  public function process_actions() {
275
 
276
  //Detect when a bulk action is being triggered...
277
  $nonce = isset($_REQUEST['_wpnonce']) ? esc_attr($_REQUEST['_wpnonce']) : false;
278
+ $redirect_url = esc_url_raw(remove_query_arg(array('action', 'image', '_wpnonce')));
279
+
280
  switch($this->current_action()) {
281
  case 'optimize':
282
  if (!wp_verify_nonce($nonce, 'sp_optimize_image')) {
283
  die('Error.');
284
  } else {
285
  $this->action_optimize_image(absint($_GET['image']));
286
+ wp_redirect($redirect_url);
287
  exit;
288
  }
289
  break;
292
  die('Error.');
293
  } else {
294
  $this->action_restore_image(absint($_GET['image']));
295
+ wp_redirect($redirect_url);
296
  exit;
297
  }
298
  break;
300
  if (!wp_verify_nonce($nonce, 'sp_redo_image')) {
301
  die('Error.');
302
  } else {
303
+ $this->action_redo_image(absint($_GET['image']), sanitize_text_field($_GET['type']));
304
+ wp_redirect($redirect_url);
305
  exit;
306
  }
307
  break;
class/view/shortpixel_view.php CHANGED
@@ -1,20 +1,20 @@
1
  <?php
2
 
3
  class ShortPixelView {
4
-
5
  private $ctrl;
6
-
7
  public function __construct($controller) {
8
  $this->ctrl = $controller;
9
  }
10
-
11
  //handling older
12
  public function ShortPixelView($controller) {
13
  $this->__construct($controller);
14
  }
15
 
16
- public function displayQuotaExceededAlert($quotaData, $averageCompression = false, $recheck = false)
17
- { ?>
18
  <br/>
19
  <div class="wrap sp-quota-exceeded-alert" id="short-pixel-notice-exceed">
20
  <?php if($averageCompression) { ?>
@@ -30,42 +30,42 @@ class ShortPixelView {
30
  </div>
31
  </div>
32
  <?php } ?>
33
- <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/robo-scared.png'));?>"
34
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-scared.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-scared@2x.png' ));?> 2x'
35
  class='short-pixel-notice-icon'>
36
  <h3><?php /* translators: header of the alert box */ _e('Quota Exceeded','shortpixel-image-optimiser');?></h3>
37
- <p><?php /* translators: body of the alert box */
38
  if($recheck) {
39
  echo('<span style="color: red">' . __('You have no available image credits. If you just bought a package, please note that sometimes it takes a few minutes for the payment confirmation to be sent to us by the payment processor.','shortpixel-image-optimiser') . '</span><br>');
40
  }
41
- printf(__('The plugin has optimized <strong>%s images</strong> and stopped because it reached the available quota limit.','shortpixel-image-optimiser'),
42
- number_format(max(0, $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric'])));?>
43
  <?php if($quotaData['totalProcessedFiles'] < $quotaData['totalFiles']) { ?>
44
- <?php
45
  printf(__('<strong>%s images and %s thumbnails</strong> are not yet optimized by ShortPixel.','shortpixel-image-optimiser'),
46
- number_format(max(0, $quotaData['mainFiles'] - $quotaData['mainProcessedFiles'])),
47
  number_format(max(0, ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles'])))); ?>
48
  <?php } ?></p>
49
  <div> <!-- style='float:right;margin-top:20px;'> -->
50
  <button class="button button-primary" id="shortpixel-upgrade-advice" onclick="ShortPixel.proposeUpgrade()" style="margin-right:10px;"><strong>
51
  <?php _e('Show me the best available options', 'shortpixel_image_optimiser'); ?></strong></button>
52
- <a class='button button-primary' href='https://shortpixel.com/login/<?php echo($this->ctrl->getApiKey());?>'
53
  title='<?php _e('Go to my account and select a plan','shortpixel-image-optimiser');?>' target='_blank' style="margin-right:10px;">
54
  <strong><?php _e('Upgrade','shortpixel-image-optimiser');?></strong>
55
  </a>
56
  <input type='button' name='checkQuota' class='button' value='<?php _e('Confirm New Credits','shortpixel-image-optimiser');?>'
57
  onclick="ShortPixel.recheckQuota()">
58
  </div>
59
- <p><?php _e('Get more image credits by referring ShortPixel to your friends!','shortpixel-image-optimiser');?>
60
  <a href="https://shortpixel.com/login/<?php echo(defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey());?>/tell-a-friend" target="_blank">
61
  <?php _e('Check your account','shortpixel-image-optimiser');?>
62
  </a> <?php _e('for your unique referral link. For each user that joins, you will receive +100 additional image credits/month.','shortpixel-image-optimiser');?>
63
  </p>
64
-
65
  </div> <?php self::includeProposeUpgradePopup();
66
  }
67
-
68
- public static function displayApiKeyAlert()
69
  { ?>
70
  <p><?php _e('In order to start the optimization process, you need to validate your API Key in the '
71
  . '<a href="options-general.php?page=wp-shortpixel">ShortPixel Settings</a> page in your WordPress Admin.','shortpixel-image-optimiser');?>
@@ -75,7 +75,7 @@ class ShortPixelView {
75
  </p>
76
  <?php
77
  }
78
-
79
  public static function displayActivationNotice($when = 'activate', $extra = '') {
80
  $extraStyle = ($when == 'compat' || $when == 'fileperms' ? "background-color: #ff9999;margin: 5px 20px 15px 0;'" : '');
81
  $icon = false;
@@ -84,7 +84,7 @@ class ShortPixelView {
84
  case 'compat': $extraClass = 'notice-error below-h2';
85
  case 'fileperms': $icon = 'scared'; $extraClass = 'notice-error'; break;
86
  case 'unlisted': $icon = 'magnifier'; break;
87
- case 'upgmonth':
88
  case 'upgbulk': $icon = 'notes'; $extraClass = 'notice-success'; break;
89
  case 'spai':
90
  case 'generic-err': $extraClass = 'notice-error is-dismissible'; break;
@@ -94,11 +94,11 @@ class ShortPixelView {
94
  <div class='notice <?php echo($extraClass);?> notice-warning' id='short-pixel-notice-<?php echo($when);?>' <?php echo($extraStyle);?>>
95
  <?php if($when != 'activate') { ?>
96
  <div style="float:right;">
97
- <?php if($when == 'upgmonth' || $when == 'upgbulk'){ ?>
98
  <button class="button button-primary" id="shortpixel-upgrade-advice" onclick="ShortPixel.proposeUpgrade()" style="margin-top:10px;margin-left:10px;"><strong>
99
  <?php _e('Show me the best available options', 'shortpixel_image_optimiser'); ?></strong></button>
100
  <?php } ?>
101
- <?php if($when == 'unlisted'){ ?>
102
  <a href="javascript:ShortPixel.includeUnlisted()" class="button button-primary" style="margin-top:10px;margin-left:10px;">
103
  <strong><?php _e('Yes, include these thumbnails','shortpixel-image-optimiser');?></strong></a>
104
  <?php }
@@ -125,7 +125,7 @@ class ShortPixelView {
125
  if($when == 'upgmonth' || $when == 'upgbulk') { echo(' '); _e('advice','shortpixel-image-optimiser');}
126
  ?></h3> <?php
127
  switch($when) {
128
- case '2h' :
129
  _e("Action needed. Please <a href='https://shortpixel.com/wp-apikey' target='_blank'>get your API key</a> to activate your ShortPixel plugin.",'shortpixel-image-optimiser') . "<BR><BR>";
130
  break;
131
  case '3d':
@@ -157,18 +157,18 @@ class ShortPixelView {
157
  case 'upgbulk' : ?>
158
  <p> <?php
159
  if($when == 'upgmonth') {
160
- printf(__("You are adding an average of <strong>%d images and thumbnails every month</strong> to your Media Library and you have <strong>a plan of %d images/month</strong>."
161
  . " You might need to upgrade your plan in order to have all your images optimized.", 'shortpixel_image_optimiser'), $extra['monthAvg'], $extra['monthlyQuota']);
162
  } else {
163
  printf(__("You currently have <strong>%d images and thumbnails to optimize</strong> but you only have <strong>%d images</strong> available in your current plan."
164
  . " You might need to upgrade your plan in order to have all your images optimized.", 'shortpixel_image_optimiser'), $extra['filesTodo'], $extra['quotaAvailable']);
165
- }?></p><?php
166
  self::includeProposeUpgradePopup();
167
  break;
168
  case 'unlisted' :
169
  _e("<p>ShortPixel found thumbnails which are not registered in the metadata but present alongside the other thumbnails. These thumbnails could be created and needed by some plugin or by the theme. Let ShortPixel optimize them as well?</p>", 'shortpixel-image-optimiser');?>
170
  <p>
171
- <?php _e("For example, the image", 'shortpixel-image-optimiser');?>
172
  <a href='post.php?post=<?php echo($extra->id);?>&action=edit' target='_blank'>
173
  <?php echo($extra->name); ?>
174
  </a> has also these thumbs not listed in metadata:
@@ -185,10 +185,12 @@ class ShortPixelView {
185
  </div>
186
  <?php
187
  }
188
-
189
- protected static function includeProposeUpgradePopup() { ?>
 
 
190
  <div id="shortPixelProposeUpgradeShade" class="sp-modal-shade" style="display:none;">
191
- <div id="shortPixelProposeUpgrade" class="shortpixel-modal shortpixel-hide" style="min-width: 610px;">
192
  <div class="sp-modal-title">
193
  <button type="button" class="sp-close-upgrade-button" onclick="ShortPixel.closeProposeUpgrade()">&times;</button>
194
  <?php _e('Upgrade your ShortPixel account', 'shortpixel-image-optimiser');?>
@@ -196,10 +198,10 @@ class ShortPixelView {
196
  <div class="sp-modal-body sptw-modal-spinner" style="height:auto;min-height:400px;padding:0;">
197
  </div>
198
  </div>
199
- </div>
200
  <?php }
201
-
202
- public function displayBulkProcessingForm($quotaData, $thumbsProcessedCount, $under5PercentCount, $bulkRan,
203
  $averageCompression, $filesOptimized, $savedSpace, $percent, $customCount) {
204
  $settings = $this->ctrl->getSettings();
205
  $this->ctrl->outputHSBeacon();
@@ -220,7 +222,7 @@ class ShortPixelView {
220
  <div class="bulk-label"><?php _e('Smaller thumbnails','shortpixel-image-optimiser');?></div>
221
  <div class="bulk-val"><?php echo(number_format($quotaData['totalMlFiles'] - $quotaData['mainMlFiles']));?></div>
222
  <div style='width:165px; display:inline-block; padding-left: 5px'>
223
- <input type='checkbox' id='thumbnails' name='thumbnails' onclick='ShortPixel.checkThumbsUpdTotal(this)' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>>
224
  <?php _e('Include thumbnails','shortpixel-image-optimiser');?>
225
  </div><br>
226
  <?php if($quotaData["totalProcessedMlFiles"] > 0) { ?>
@@ -243,7 +245,7 @@ class ShortPixelView {
243
  <?php _e('Total to be optimized','shortpixel-image-optimiser');?>
244
  <div class="reset"><?php _e('(Originals and thumbnails)','shortpixel-image-optimiser');?></div>
245
  </div>
246
- <div class="bulk-val bulk-total" id='displayTotal'><?php echo(number_format($customCount));?></div>
247
  <?php } ?>
248
  </div>
249
  <?php if(max(0, $quotaData['totalMlFiles'] - $quotaData['totalProcessedMlFiles']) + $customCount > 0) { ?>
@@ -275,7 +277,7 @@ class ShortPixelView {
275
  <input type='submit' name='bulkCleanup' id='bulkCleanup' class='button' value='<?php _e('Bulk Delete SP Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('Cleanup', event)" style="width:100%">
276
  <input type='submit' name='bulkCleanupPending' id='bulkCleanupPending' class='button' value='<?php _e('Bulk Delete Pending Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('CleanupPending', event)" style="display:none">
277
  </div>
278
-
279
  <?php }
280
  } else {?>
281
  <div class="bulk-play bulk-nothing-optimize">
@@ -315,7 +317,7 @@ class ShortPixelView {
315
  <?php if($quotaData['totalProcessedFiles'] < $quotaData['totalFiles']) { ?>
316
  <p><?php printf(__('%s images and %s thumbnails are not yet optimized by ShortPixel.','shortpixel-image-optimiser'),
317
  number_format($quotaData['mainFiles'] - $quotaData['mainProcessedFiles']),
318
- number_format(($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles'])));?>
319
  </p>
320
  <?php } ?>
321
  <p><?php _e('You can continue optimizing your Media Gallery from where you left, by clicking the Resume processing button. Already optimized images will not be reprocessed.','shortpixel-image-optimiser');?></p>
@@ -356,15 +358,15 @@ class ShortPixelView {
356
  <div class="fb-like" data-href="https://www.facebook.com/ShortPixel" data-width="260" data-layout="button_count" data-action="like" data-show-faces="true" data-share="true"></div>
357
  </div>
358
  <div style="float:left;margin:-7px 0 0 10px">
359
- <a href="https://twitter.com/share" class="twitter-share-button" data-url="https://shortpixel.com"
360
- data-text="<?php
361
  if(0+$averageCompression>20) {
362
  _e("I just #optimized my site's images by ",'shortpixel-image-optimiser');
363
  } else {
364
  _e("I just #optimized my site's images ",'shortpixel-image-optimiser');
365
  }
366
  echo(round($averageCompression) ."%");
367
- echo(__("with @ShortPixel, a #WordPress image optimization plugin",'shortpixel-image-optimiser') . " #pagespeed #seo");?>"
368
  data-size='large'><?php _e('Tweet','shortpixel-image-optimiser');?></a>
369
  </div>
370
  <script>
@@ -387,7 +389,7 @@ class ShortPixelView {
387
  </script>
388
  </div>
389
  </div>
390
- <?php if(0+$averageCompression>30) {?>
391
  <div class='shortpixel-rate-us' style='float:left;padding-top:0'>
392
  <a href="https://wordpress.org/support/view/plugin-reviews/shortpixel-image-optimiser?rate=5#postform" target="_blank">
393
  <span>
@@ -399,13 +401,13 @@ class ShortPixelView {
399
  </div>
400
  <div id="sp-bulk-stats" style="display:none">
401
  <?php $this->displayBulkStats($quotaData['totalProcessedFiles'], $quotaData['mainProcessedFiles'], $under5PercentCount, $averageCompression, $savedSpace);?>
402
- </div>
403
  </div>
404
  <p><?php printf(__('Go to the ShortPixel <a href="%soptions-general.php?page=wp-shortpixel#stats">Stats</a>
405
  and see all your websites\' optimized stats. Download your detailed <a href="https://%s/v2/report.php?key=%s">Optimization Report</a>
406
  to check your image optimization statistics for the last 40 days.','shortpixel-image-optimiser'),
407
  get_admin_url(), SHORTPIXEL_API, (defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) );?></p>
408
- <?php
409
  $failed = $this->ctrl->getPrioQ()->getFailed();
410
  if(count($failed)) { ?>
411
  <div class="bulk-progress sp-notice sp-notice-warning sp-floating-block sp-double-width" style="margin-bottom: 15px">
@@ -418,20 +420,20 @@ class ShortPixelView {
418
  <div class="bulk-progress sp-notice sp-notice-info sp-floating-block sp-double-width">
419
  <?php
420
  $todo = $reopt = false;
421
- if($quotaData['totalProcessedFiles'] < $quotaData['totalFiles']) {
422
  $todo = true;
423
  $mainNotProcessed = max(0, $quotaData['mainFiles'] - $quotaData['mainProcessedFiles']);
424
  $thumbsNotProcessed = max(0, ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles']));
425
  ?>
426
  <p>
427
- <?php
428
  if($mainNotProcessed && $thumbsNotProcessed) {
429
- printf(__("%s images and %s thumbnails are not yet optimized by ShortPixel.",'shortpixel-image-optimiser'),
430
- number_format($mainNotProcessed), number_format($thumbsNotProcessed));
431
  } elseif($mainNotProcessed) {
432
- printf(__("%s images are not yet optimized by ShortPixel.",'shortpixel-image-optimiser'), number_format($mainNotProcessed));
433
  } elseif($thumbsNotProcessed) {
434
- printf(__("%s thumbnails are not yet optimized by ShortPixel.",'shortpixel-image-optimiser'), number_format($thumbsNotProcessed));
435
  }
436
  _e('','shortpixel-image-optimiser');
437
  if (count($quotaData['filesWithErrors'])) {
@@ -474,21 +476,21 @@ class ShortPixelView {
474
  $extraW = $extraO = '';
475
  if( !$this->ctrl->backupFolderIsEmpty()
476
  && ( ($quotaData['totalProcLossyFiles'] > 0 && $settings->compressionType != 1)
477
- || ($quotaData['totalProcGlossyFiles'] > 0 && $settings->compressionType != 2)
478
  || ($quotaData['totalProcLosslessFiles'] > 0 && $settings->compressionType != 0)))
479
- {
480
  $todo = $reopt = true;
481
  $statType = ucfirst($otherTypes[0]);
482
  $thumbsCount = $quotaData['totalProc'.$statType.'Files'] - $quotaData['mainProc'.$statType.'Files'];
483
-
484
  $statType2 = ucfirst($otherTypes[1]);
485
  $thumbsCount2 = $quotaData['totalProc'.$statType2.'Files'] - $quotaData['mainProc'.$statType2.'Files'];
486
  if($quotaData['totalProc'.$statType2.'Files'] > 0 ) {
487
  if($quotaData['totalProc'.$statType.'Files'] > 0) {
488
- $extraW = sprintf(__('%s images and %s thumbnails were optimized <strong>%s</strong>. ','shortpixel-image-optimiser'),
489
  number_format($quotaData['mainProc'.$statType2.'Files']),
490
  number_format($thumbsCount2), $otherTypes[1]);
491
- $extraO = sprintf(__('%s images were optimized <strong>%s</strong>. ','shortpixel-image-optimiser'),
492
  number_format($quotaData['mainProc'.$statType2.'Files']), $otherTypes[1]);
493
  } else {
494
  $extraW = $extraO = ''; $otherTypes[0] = $otherTypes[1]; $statType = $statType2;
@@ -497,13 +499,13 @@ class ShortPixelView {
497
  ?>
498
  <p id="with-thumbs" <?php echo(!$settings->processThumbnails ? 'style="display:none;"' : "");?>>
499
  <?php echo($extraW);
500
- printf(__('%s images and %s thumbnails were optimized <strong>%s</strong>. You can re-optimize <strong>%s</strong> the ones that have backup.','shortpixel-image-optimiser'),
501
  number_format($quotaData['mainProc'.$statType.'Files']),
502
  number_format($thumbsCount), $otherTypes[0], $optType);?>
503
  </p>
504
  <p id="without-thumbs" <?php echo($settings->processThumbnails ? 'style="display:none;"' : "");?>>
505
- <?php echo($extraO);
506
- printf(__('%s images were optimized <strong>%s</strong>. You can re-optimize <strong>%s</strong> the ones that have backup. ','shortpixel-image-optimiser'),
507
  number_format($quotaData['mainProc'.$statType.'Files']),
508
  $otherTypes[0], $optType);?>
509
  <?php echo($thumbsCount + $thumbsCount2 ? number_format($thumbsCount + $thumbsCount2) . __(' thumbnails will be restored to originals.','shortpixel-image-optimiser') : '');?>
@@ -518,18 +520,23 @@ class ShortPixelView {
518
  echo(' ');
519
  printf(__('Already <strong>%s</strong> optimized images will not be reprocessed.','shortpixel-image-optimiser'), $todo ? ($optType) : '');
520
  if($reopt) { ?>
521
- <br><?php _e('Please note that reoptimizing images as <strong>lossy/lossless</strong> may use additional credits.','shortpixel-image-optimiser')?>
522
  <a href="http://blog.shortpixel.com/the-all-new-re-optimization-functions-in-shortpixel/" target="_blank" class="shortpixel-help-link">
523
  <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
524
  </a>
525
  <?php } ?>
526
  </p>
527
  <form action='' method='POST' >
528
- <input type='checkbox' id='bulk-thumbnails' name='thumbnails' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>
529
  onchange="ShortPixel.onBulkThumbsCheck(this)"> <?php _e('Include thumbnails','shortpixel-image-optimiser');?><br><br>
 
 
 
530
  <input type='submit' name='bulkProcess' id='bulkProcess' class='button button-primary' value='<?php _e('Restart Optimizing','shortpixel-image-optimiser');?>'
531
  <?php echo($settings->quotaExceeded? "disabled title=\"" . __("Top-up your account to optimize more images.",'shortpixel-image-optimiser')."\"" : ""); ?>>
532
- <input type='submit' name='bulkRestore' id='bulkRestore' class='button' value='<?php _e('Bulk Restore Media Library','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('Restore',event)" style="float: right;">
 
 
533
  <input type='submit' name='bulkCleanup' id='bulkCleanup' class='button' value='<?php _e('Bulk Delete SP Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('Cleanup',event)" style="float: right;margin-right:10px;">
534
  <input type='submit' name='bulkCleanupPending' id='bulkCleanupPending' class='button' value='<?php _e('Bulk Delete Pending Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('CleanupPending', event)" style="display:none">
535
  </form>
@@ -608,7 +615,7 @@ class ShortPixelView {
608
  </div>
609
  <?php
610
  }
611
-
612
  public function displayBulkProgressBar($running, $percent, $message, $remainingQuota, $averageCompression, $type = 1, $customPending = false) {
613
  $percentBefore = $percentAfter = '';
614
  if($percent > 24) {
@@ -636,26 +643,26 @@ class ShortPixelView {
636
  </script>
637
  </div>
638
  </div>
639
- <?php if($running) {
640
  if($type > 0) { ?>
641
- <div class="sp-h2"><?php
642
  echo($type & 1 ? __('Media Library','shortpixel-image-optimiser') . " " : "");
643
  echo($type & 3 == 3 ? __('and','shortpixel-image-optimiser') . " " : "");
644
- echo($type & 2 ? __('Custom folders','shortpixel-image-optimiser') . " " : "");
645
  _e('optimization in progress ...','shortpixel-image-optimiser');?></div>
646
  <p style="margin: 0 0 18px;"><?php _e('Bulk optimization has started.','shortpixel-image-optimiser');?><br>
647
- <?php
648
  } elseif($type == 0) { // restore ?>
649
- <div class="sp-h2"><?php
650
  _e('Media Library restore in progress ...','shortpixel-image-optimiser');?></div>
651
- <p style="margin: 0 0 18px;"><?php _e('Bulk restore has started.','shortpixel-image-optimiser');?><br>
652
  <?php }
653
  elseif($type == -1) { // cleanup ?>
654
- <div class="sp-h2"><?php
655
  _e('Media Library cleanup in progress ...','shortpixel-image-optimiser');?></div>
656
- <p style="margin: 0 0 18px;"><?php _e('Bulk cleanup has started.','shortpixel-image-optimiser');?><br>
657
  <?php }
658
- printf(__('This process will take some time, depending on the number of images in your library. In the meantime, you can continue using
659
  the admin as usual, <a href="%s" target="_blank">in a different browser window or tab</a>.<br>
660
  However, <strong>if you close this window, the bulk processing will pause</strong> until you open the media gallery or the ShortPixel bulk page again.','shortpixel-image-optimiser'), get_admin_url());?>
661
  </p>
@@ -692,7 +699,7 @@ class ShortPixelView {
692
  </div>
693
  <?php
694
  }
695
-
696
  public function displayBulkStats($totalOptimized, $mainOptimized, $under5PercentCount, $averageCompression, $savedSpace) {?>
697
  <div class="bulk-progress bulk-stats">
698
  <div class="label"><?php _e('Processed Images and PDFs:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($mainOptimized));?></div><br>
@@ -706,11 +713,11 @@ class ShortPixelView {
706
  </div>
707
  <?php
708
  }
709
-
710
  public function displayFailed($failed) {
711
  ?>
712
  <div class="bulk-progress bulk-stats" style="padding-top:5px;">
713
- <?php foreach($failed as $fail) {
714
  if($fail->type == ShortPixelMetaFacade::CUSTOM_TYPE) {
715
  $meta = $fail->meta;
716
  ?> <div><a href="<?php echo(ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath());?>"><?php echo(substr($meta->getName(), 0, 80));?> - ID: C-<?php echo($fail->id);?></a></div><br/>
@@ -723,7 +730,7 @@ class ShortPixelView {
723
  <?php
724
  }
725
  function displaySettings($showApiKey, $editApiKey, $quotaData, $notice, $resources = null, $averageCompression = null, $savedSpace = null, $savedBandwidth = null,
726
- $remainingImages = null, $totalCallsMade = null, $fileCount = null, $backupFolderSize = null,
727
  $customFolders = null, $folderMsg = false, $addedFolder = false, $showAdvanced = false, $cloudflareAPI = false, $htaccessWriteable = false, $isNginx = false ) {
728
  //wp_enqueue_script('jquery.idTabs.js', plugins_url('/js/jquery.idTabs.js',__FILE__) );
729
  $this->ctrl->outputHSBeacon();
@@ -731,7 +738,7 @@ class ShortPixelView {
731
  <div class="wrap">
732
  <h1><?php _e('ShortPixel Plugin Settings','shortpixel-image-optimiser');?></h1>
733
  <p style="font-size:18px">
734
- <a href="https://shortpixel.com/<?php
735
  echo(($this->ctrl->getVerifiedKey() ? "login/".(defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) : "pricing") . WPShortPixel::getAffiliateSufix());
736
  ?>" target="_blank" style="font-size:18px">
737
  <?php _e('Upgrade now','shortpixel-image-optimiser');?>
@@ -767,7 +774,7 @@ class ShortPixelView {
767
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-settings"><?php _e('General','shortpixel-image-optimiser');?></a></h2>
768
  <?php }
769
  $this->displaySettingsForm($showApiKey, $editApiKey, $quotaData);?>
770
- </section>
771
  <?php if($this->ctrl->getVerifiedKey()) {?>
772
  <section <?php echo($showAdvanced ? "class='sel-tab'" : "");?> id="tab-adv-settings" class="clearfix">
773
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-adv-settings"><?php _e('Advanced','shortpixel-image-optimiser');?></a></h2>
@@ -789,9 +796,9 @@ class ShortPixelView {
789
  <section id="tab-stats">
790
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-stats"><?php _e('Statistics','shortpixel-image-optimiser');?></a></h2>
791
  <?php
792
- $this->displaySettingsStats($quotaData, $averageCompression, $savedSpace, $savedBandwidth,
793
  $remainingImages, $totalCallsMade, $fileCount, $backupFolderSize);?>
794
- </section>
795
  <?php }
796
  if( $resources !== null && $quotaData['APICallsQuotaOneTimeNumeric']<10000 && $quotaData['APICallsQuotaNumeric']<5000 ) {?>
797
  <section id="tab-resources">
@@ -807,15 +814,15 @@ class ShortPixelView {
807
  </script>
808
  </div>
809
  <?php
810
- }
811
-
812
  public function displaySettingsForm($showApiKey, $editApiKey, $quotaData) {
813
  $settings = $this->ctrl->getSettings();
814
  $checked = ($this->ctrl->processThumbnails() ? 'checked' : '');
815
  $checkedBackupImages = ($this->ctrl->backupImages() ? 'checked' : '');
816
  $removeExif = ($settings->keepExif ? '' : 'checked');
817
  $resize = ($this->ctrl->getResizeImages() ? 'checked' : '');
818
- $resizeDisabled = ($this->ctrl->getResizeImages() ? '' : 'disabled');
819
  $minSizes = $this->ctrl->getMaxIntermediateImageSize();
820
  $thumbnailsToProcess = isset($quotaData['totalFiles']) ? ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles']) : 0;
821
  $adminEmail = get_bloginfo('admin_email');
@@ -824,7 +831,7 @@ class ShortPixelView {
824
  <div class="wp-shortpixel-options wp-shortpixel-tab-content">
825
  <?php if($this->ctrl->getVerifiedKey()) { ?>
826
  <p><?php printf(__('New images uploaded to the Media Library will be optimized automatically.<br/>If you have existing images you would like to optimize, you can use the <a href="%supload.php?page=wp-short-pixel-bulk">Bulk Optimization Tool</a>.','shortpixel-image-optimiser'),get_admin_url());?></p>
827
- <?php } else {
828
  if($showApiKey) {?>
829
  <h3><?php _e('Request an API Key:','shortpixel-image-optimiser');?></h3>
830
  <p style='font-size: 14px'><?php _e('If you don\'t have an API Key, you can request one for free. Just press the "Request Key" button after checking that the e-mail is correct.','shortpixel-image-optimiser');?></p>
@@ -869,24 +876,24 @@ class ShortPixelView {
869
  <p style='font-size: 14px'>
870
  <?php _e('If you already have an API Key please input it below and press Validate.','shortpixel-image-optimiser');?>
871
  </p>
872
- <?php }
873
  }?>
874
  <table class="form-table">
875
  <tbody>
876
  <tr>
877
  <th scope="row"><label for="key"><?php _e('API Key:','shortpixel-image-optimiser');?></label></th>
878
  <td>
879
- <?php
880
  $canValidate = false;
881
  if($showApiKey) {
882
  $canValidate = true;?>
883
- <input name="key" type="text" id="key" value="<?php echo( $this->ctrl->getApiKey() );?>"
884
  class="regular-text" <?php echo($editApiKey ? "" : 'disabled') ?> <?php echo $this->ctrl->getVerifiedKey() ? 'onkeyup="ShortPixel.apiKeyChanged()"' : '' ?>>
885
- <?php } elseif(defined("SHORTPIXEL_API_KEY")) {
886
  $canValidate = true;?>
887
- <input name="key" type="text" id="key" disabled="true" placeholder="<?php
888
  if(defined("SHORTPIXEL_HIDE_API_KEY")) {
889
- echo("********************");
890
  } else {
891
  _e('Multisite API Key','shortpixel-image-optimiser');
892
  }
@@ -904,7 +911,7 @@ class ShortPixelView {
904
  <?php if($showApiKey && !$editApiKey) { ?>
905
  <p class="settings-info"><?php _e('Key defined in wp-config.php.','shortpixel-image-optimiser');?></p>
906
  <?php } ?>
907
-
908
  </td>
909
  </tr>
910
  <?php if (!$this->ctrl->getVerifiedKey()) { //if invalid key we display the link to the API Key ?>
@@ -977,7 +984,7 @@ class ShortPixelView {
977
  <td>
978
  <input name="removeExif" type="checkbox" id="removeExif" <?php echo( $removeExif );?>>
979
  <label for="removeExif"><?php _e('Remove the EXIF tag of the image (recommended).','shortpixel-image-optimiser');?></label>
980
- <p class="settings-info"> <?php _e('EXIF is a set of various pieces of information that are automatically embedded into the image upon creation. This can include GPS position, camera manufacturer, date and time, etc.
981
  Unless you really need that data to be preserved, we recommend removing it as it can lead to <a href="http://blog.shortpixel.com/how-much-smaller-can-be-images-without-exif-icc" target="_blank">better compression rates</a>.','shortpixel-image-optimiser');?></p>
982
  </td>
983
  </tr>
@@ -987,14 +994,14 @@ class ShortPixelView {
987
  <input name="resize" type="checkbox" id="resize" <?php echo( $resize );?>>
988
  <label for="resize"><?php _e('to maximum','shortpixel-image-optimiser');?></label>
989
  <input type="text" name="width" id="width" style="width:70px" class="resize-sizes"
990
- value="<?php echo( $this->ctrl->getResizeWidth() > 0 ? $this->ctrl->getResizeWidth() : min(924, $minSizes['width']) );?>" <?php echo( $resizeDisabled );?>/> <?php
991
  _e('pixels wide &times;','shortpixel-image-optimiser');?>
992
- <input type="text" name="height" id="height" class="resize-sizes" style="width:70px"
993
- value="<?php echo( $this->ctrl->getResizeHeight() > 0 ? $this->ctrl->getResizeHeight() : min(924, $minSizes['height']) );?>" <?php echo( $resizeDisabled );?>/> <?php
994
  _e('pixels high (original aspect ratio is preserved and image is not cropped)','shortpixel-image-optimiser');?>
995
  <input type="hidden" id="min-width" value="<?php echo($minSizes['width']);?>"/>
996
  <input type="hidden" id="min-height" value="<?php echo($minSizes['height']);?>"/>
997
- <p class="settings-info">
998
  <?php _e('Recommended for large photos, like the ones taken with your phone. Saved space can go up to 80% or more after resizing.','shortpixel-image-optimiser');?>
999
  <a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
1000
  <span class="dashicons dashicons-editor-help"></span><?php _e('Read more','shortpixel-image-optimiser');?>
@@ -1002,11 +1009,11 @@ class ShortPixelView {
1002
  </p>
1003
  <div style="margin-top: 10px;">
1004
  <input type="radio" name="resize_type" id="resize_type_outer" value="outer" <?php echo($settings->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
1005
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?>"
1006
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer@2x.png' ));?> 2x'
1007
  title="<?php _e('Sizes will be greater or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 1000x1500px while an image of 3000x2000px will be resized to 1800x1200px','shortpixel-image-optimiser');?>">
1008
  <input type="radio" name="resize_type" id="resize_type_inner" value="inner" <?php echo($settings->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
1009
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?>"
1010
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner@2x.png' ));?> 2x'
1011
  title="<?php _e('Sizes will be smaller or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 800x1200px while an image of 3000x2000px will be resized to 1000x667px','shortpixel-image-optimiser');?>">
1012
  <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
@@ -1024,14 +1031,14 @@ class ShortPixelView {
1024
  </div>
1025
  <script>
1026
  jQuery(document).ready(function () {
1027
- ShortPixel.setupGeneralTab(document.wp_shortpixel_options.compressionType,
1028
  Math.min(924, <?php echo($minSizes['width']);?>),
1029
  Math.min(924, <?php echo($minSizes['height']);?>));
1030
  });
1031
  </script>
1032
  <?php }
1033
  }
1034
-
1035
  public function displayAdvancedSettingsForm($customFolders = false, $addedFolder = false, $htaccessWriteable = false, $isNginx = false ) {
1036
  $settings = $this->ctrl->getSettings();
1037
  $minSizes = $this->ctrl->getMaxIntermediateImageSize();
@@ -1116,22 +1123,22 @@ class ShortPixelView {
1116
  <td></td>
1117
  </tr>
1118
  <?php foreach($customFolders as $folder) {
1119
- $typ = $folder->getType();
1120
  $typ = $typ ? $typ . "<br>" : "";
1121
  $stat = $this->ctrl->getSpMetaDao()->getFolderOptimizationStatus($folder->getId());
1122
  $cnt = $folder->getFileCount();
1123
- $st = ($cnt == 0
1124
  ? __("Empty",'shortpixel-image-optimiser')
1125
- : ($stat->Total == $stat->Optimized
1126
  ? __("Optimized",'shortpixel-image-optimiser')
1127
  : ($stat->Optimized + $stat->Pending > 0 ? __("Pending",'shortpixel-image-optimiser') : __("Waiting",'shortpixel-image-optimiser'))));
1128
-
1129
  $err = $stat->Failed > 0 && !$st == __("Empty",'shortpixel-image-optimiser') ? " ({$stat->Failed} failed)" : "";
1130
-
1131
  $action = ($st == __("Optimized",'shortpixel-image-optimiser') || $st == __("Empty",'shortpixel-image-optimiser') ? __("Stop monitoring",'shortpixel-image-optimiser') : __("Stop optimizing",'shortpixel-image-optimiser'));
1132
-
1133
- $fullStat = $st == __("Empty",'shortpixel-image-optimiser') ? "" : __("Optimized",'shortpixel-image-optimiser') . ": " . $stat->Optimized . ", "
1134
- . __("Pending",'shortpixel-image-optimiser') . ": " . $stat->Pending . ", " . __("Waiting",'shortpixel-image-optimiser') . ": " . $stat->Waiting . ", "
1135
  . __("Failed",'shortpixel-image-optimiser') . ": " . $stat->Failed;
1136
  ?>
1137
  <tr>
@@ -1153,42 +1160,49 @@ class ShortPixelView {
1153
  </td>
1154
  <td>
1155
  <input type="button" class="button remove-folder-button" data-value="<?php echo($folder->getPath()); ?>" title="<?php echo($action . " " . $folder->getPath()); ?>" value="<?php echo $action;?>">
1156
- <input type="button" style="display:none;" class="button button-alert recheck-folder-button" data-value="<?php echo($folder->getPath()); ?>"
1157
- title="<?php _e('Full folder refresh, check each file of the folder if it changed since it was optimized. Might take up to 1 min. for big folders.','shortpixel-image-optimiser');?>"
1158
  value="<?php _e('Refresh','shortpixel-image-optimiser');?>">
1159
  </td>
1160
  </tr>
1161
  <?php }?>
1162
  </table>
1163
  <?php } ?>
1164
- <input type="hidden" name="removeFolder" id="removeFolder"/>
1165
- <input type="hidden" name="recheckFolder" id="removeFolder"/>
1166
- <input type="text" name="addCustomFolderView" id="addCustomFolderView" class="regular-text" value="<?php echo($addedFolder);?>" disabled style="width: 50em;max-width: 70%;">&nbsp;
1167
- <input type="hidden" name="addCustomFolder" id="addCustomFolder" value="<?php echo($addedFolder);?>"/>
1168
- <input type="hidden" id="customFolderBase" value="<?php echo WPShortPixel::getCustomFolderBase(); ?>">
1169
- <a class="button button-primary select-folder-button" title="<?php _e('Select the images folder on your server.','shortpixel-image-optimiser');?>" href="javascript:void(0);">
1170
- <?php _e('Select ...','shortpixel-image-optimiser');?>
1171
- </a>
1172
- <input type="submit" name="saveAdv" id="saveAdvAddFolder" class="button button-primary" title="<?php _e('Add Folder','shortpixel-image-optimiser');?>" value="<?php _e('Add Folder','shortpixel-image-optimiser');?>">
 
 
 
 
 
1173
  <p class="settings-info">
1174
  <?php _e('Use the Select... button to select site folders. ShortPixel will optimize images and PDFs from the specified folders and their subfolders. The optimization status for each image or PDF in these folders can be seen in the <a href="upload.php?page=wp-short-pixel-custom">Other Media list</a>, under the Media menu.','shortpixel-image-optimiser');?>
1175
  <a href="https://blog.shortpixel.com/optimize-images-outside-media-library/" target="_blank" class="shortpixel-help-link">
1176
  <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
1177
  </a>
1178
  </p>
1179
- <div class="sp-modal-shade sp-folder-picker-shade">
1180
- <div class="shortpixel-modal">
 
1181
  <div class="sp-modal-title"><?php _e('Select the images folder','shortpixel-image-optimiser');?></div>
1182
  <div class="sp-folder-picker"></div>
1183
  <input type="button" class="button button-info select-folder-cancel" value="<?php _e('Cancel','shortpixel-image-optimiser');?>" style="margin-right: 30px;">
1184
  <input type="button" class="button button-primary select-folder" value="<?php _e('Select','shortpixel-image-optimiser');?>">
1185
  </div>
1186
- </div>
1187
  <script>
1188
  jQuery(document).ready(function () {
1189
  ShortPixel.initFolderSelector();
1190
  });
1191
  </script>
 
1192
  </td>
1193
  </tr>
1194
  <?php if($hasNextGen) { ?>
@@ -1323,10 +1337,10 @@ class ShortPixelView {
1323
  <tr>
1324
  <th scope="row"><label for="excludePatterns"><?php _e('Exclude patterns','shortpixel-image-optimiser');?></label></th>
1325
  <td>
1326
- <input name="excludePatterns" type="text" id="excludePatterns" value="<?php echo( $excludePatterns );?>" class="regular-text" placeholder="<?php
1327
- _e('name:keepbig, path:/ignore_regex/i, size:1000x2000','shortpixel-image-optimiser');?>">
1328
  <?php _e('Exclude certain images from being optimized, based on patterns.','shortpixel-image-optimiser');?>
1329
- <p class="settings-info">
1330
  <?php _e('Add patterns separated by comma. A pattern consist of a <strong>type:value</strong> pair; the accepted types are
1331
  <strong>"name"</strong>, <strong>"path"</strong> and <strong>"size"</strong>.
1332
  A file will be excluded if it matches any of the patterns.
@@ -1346,9 +1360,9 @@ class ShortPixelView {
1346
  <tr>
1347
  <th scope="row"><label for="authentication"><?php _e('HTTP AUTH credentials','shortpixel-image-optimiser');?></label></th>
1348
  <td>
1349
- <input name="siteAuthUser" type="text" id="siteAuthUser" value="<?php echo( $settings->siteAuthUser );?>" class="regular-text" placeholder="<?php _e('User','shortpixel-image-optimiser');?>"><br>
1350
- <input name="siteAuthPass" type="text" id="siteAuthPass" value="<?php echo( $settings->siteAuthPass );?>" class="regular-text" placeholder="<?php _e('Password','shortpixel-image-optimiser');?>">
1351
- <p class="settings-info">
1352
  <?php _e('Only fill in these fields if your site (front-end) is not publicly accessible and visitors need a user/pass to connect to it. If you don\'t know what is this then just <strong>leave the fields empty</strong>.','shortpixel-image-optimiser');?>
1353
  </p>
1354
  </td>
@@ -1399,7 +1413,7 @@ class ShortPixelView {
1399
  </script>
1400
  <?php }
1401
  }
1402
-
1403
  /**
1404
  * @desc This form is used in WP back-end to allow users that use CloudFlare to save their settings
1405
  *
@@ -1425,7 +1439,7 @@ class ShortPixelView {
1425
  </th>
1426
  <td>
1427
  <input name="cloudflare-email" type="text" id="cloudflare-email" <?php echo($noCurl ? 'disabled' : '');?>
1428
- value="<?php echo($this->ctrl->fetch_cloudflare_api_email()); ?>" class="regular-text">
1429
  <p class="settings-info">
1430
  <?php _e('The e-mail address you use to login to CloudFlare.','shortpixel-image-optimiser');?>
1431
  </p>
@@ -1437,7 +1451,7 @@ class ShortPixelView {
1437
  </th>
1438
  <td>
1439
  <input name="cloudflare-auth-key" type="text" id="cloudflare-auth-key" <?php echo($noCurl ? 'disabled' : '');?>
1440
- value="<?php echo($this->ctrl->fetch_cloudflare_api_key()); ?>" class="regular-text">
1441
  <p class="settings-info">
1442
  <?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>
1443
  </p>
@@ -1449,7 +1463,7 @@ class ShortPixelView {
1449
  </th>
1450
  <td>
1451
  <input name="cloudflare-zone-id" type="text" id="cloudflare-zone-id" <?php echo($noCurl ? 'disabled' : '');?>
1452
- value="<?php echo($this->ctrl->fetch_cloudflare_api_zoneid()); ?>" class="regular-text">
1453
  <p class="settings-info">
1454
  <?php _e('This can be found in your Cloudflare account in the "Overview" section for your domain.','shortpixel-image-optimiser');?>
1455
  </p>
@@ -1609,29 +1623,29 @@ class ShortPixelView {
1609
 
1610
  public function renderCustomColumn($id, $data, $extended = false){ ?>
1611
  <div id='sp-msg-<?php echo($id);?>' class='column-wp-shortPixel'>
1612
-
1613
  <?php switch($data['status']) {
1614
- case 'n/a': ?>
1615
  <?php _e('Optimization N/A','shortpixel-image-optimiser');?> <?php
1616
  break;
1617
- case 'notFound': ?>
1618
  <?php _e('Image does not exist.','shortpixel-image-optimiser');?> <?php
1619
  break;
1620
- case 'invalidKey':
1621
  if(defined("SHORTPIXEL_API_KEY")) { // multisite key - need to be validated on each site but it's not invalid
1622
  ?> <?php _e('Please <a href="options-general.php?page=wp-shortpixel">go to Settings</a> to validate the API Key.','shortpixel-image-optimiser');?> <?php
1623
  } else {
1624
  ?> <?php _e('Invalid API Key. <a href="options-general.php?page=wp-shortpixel">Check your Settings</a>','shortpixel-image-optimiser');?> <?php
1625
- }
1626
  break;
1627
- case 'quotaExceeded':
1628
  echo($this->getQuotaExceededHTML(isset($data['message']) ? $data['message'] : ''));
1629
  break;
1630
- case 'optimizeNow':
1631
- if($data['showActions']) { ?>
1632
  <a class='button button-smaller button-primary' href="javascript:manualOptimization('<?php echo($id)?>', false)">
1633
  <?php _e('Optimize now','shortpixel-image-optimiser');?>
1634
- </a>
1635
  <?php }
1636
  echo($data['message']);
1637
  if(isset($data['thumbsTotal']) && $data['thumbsTotal'] > 0) {
@@ -1643,7 +1657,7 @@ class ShortPixelView {
1643
  echo($data['message']);
1644
  if(isset($data['cleanup'])) {?> <a class='button button-smaller button-primary' href="javascript:manualOptimization('<?php echo($id)?>', true)">
1645
  <?php _e('Cleanup&Retry','shortpixel-image-optimiser');?>
1646
- </a> <?php
1647
  } else {
1648
  if($data['status'] == 'retry') { ?>
1649
  <div style="overflow:hidden">
@@ -1659,7 +1673,7 @@ class ShortPixelView {
1659
  <?php
1660
  }
1661
  break;
1662
- case 'pdfOptimized':
1663
  case 'imgOptimized':
1664
  $excluded = (isset($data['excludeSizes']) ? count($data['excludeSizes']) : 0);
1665
  $successText = $this->getSuccessText($data['percent'],$data['bonus'],$data['type'],$data['thumbsOpt'],$data['thumbsTotal'], $data['retinasOpt'], $data['excludeSizes']);
@@ -1680,15 +1694,14 @@ class ShortPixelView {
1680
  $missingThumbs .= '</span>';
1681
  }
1682
  $successText .= ($data['webpCount'] ? "<br>+" . $data['webpCount'] . __(" WebP images", 'shortpixel-image-optimiser') : "")
1683
- . "<br>EXIF: " . ($data['exifKept'] ? __('kept','shortpixel-image-optimiser') : __('removed','shortpixel-image-optimiser'))
1684
  . ($data['png2jpg'] ? '<br>' . __('Converted from PNG','shortpixel-image-optimiser'): '')
1685
  . "<br>" . __("Optimized on", 'shortpixel-image-optimiser') . ": " . $data['date']
1686
  . $excludeSizes . $missingThumbs;
1687
  }
1688
-
1689
  $this->renderListCell($id, $data['status'], $data['showActions'], $data['thumbsToOptimize'],
1690
  $data['backup'], $data['type'], $data['invType'], $successText);
1691
-
1692
  break;
1693
  }
1694
  //if($extended) {
@@ -1696,22 +1709,22 @@ class ShortPixelView {
1696
  //}
1697
  ?>
1698
  </div>
1699
- <?php
1700
  }
1701
-
1702
  public function getSuccessText($percent, $bonus, $type, $thumbsOpt = 0, $thumbsTotal = 0, $retinasOpt = 0, $excluded = 0) {
1703
  if($percent == 999) return __("Reduced by X%(unknown)");
1704
  return ($percent && $percent > 0 ? __('Reduced by','shortpixel-image-optimiser') . ' <strong>' . $percent . '%</strong> ' : '')
1705
  .(!$bonus ? ' ('.$type.')':'')
1706
- .($bonus && $percent ? '<br>' : '')
1707
- .($bonus ? __('Bonus processing','shortpixel-image-optimiser') : '')
1708
  .($bonus ? ' ('.$type.')':'') . '<br>'
1709
- .($thumbsOpt ? ( $thumbsTotal > $thumbsOpt
1710
- ? sprintf(__('+%s of %s thumbnails optimized','shortpixel-image-optimiser'),$thumbsOpt,$thumbsTotal)
1711
  : sprintf(__('+%s thumbnails optimized','shortpixel-image-optimiser'),$thumbsOpt)) : '')
1712
  .($retinasOpt ? '<br>' . sprintf(__('+%s Retina images optimized','shortpixel-image-optimiser') , $retinasOpt) : '' );
1713
  }
1714
-
1715
  public function renderListCell($id, $status, $showActions, $thumbsRemain, $backup, $type, $invType, $message, $extraClass = '') {
1716
  if($showActions && ($backup || $thumbsRemain)) { ?>
1717
  <div class='sp-column-actions <?php echo($extraClass);?>'>
@@ -1727,13 +1740,13 @@ class ShortPixelView {
1727
  </a>
1728
  <?php }
1729
  if($backup) {
1730
- if($type) {
1731
  //$invType = $type == 'lossy' ? 'lossless' : 'lossy'; ?>
1732
- <a class="sp-action-reoptimize1" href="javascript:reoptimize('<?php echo($id)?>', '<?php echo($invType[0])?>');"
1733
  title="<?php _e('Reoptimize from the backed-up image','shortpixel-image-optimiser');?>">
1734
  <?php _e('Re-optimize','shortpixel-image-optimiser');?> <?php echo($invType[0])?>
1735
  </a>
1736
- <a class="sp-action-reoptimize2" href="javascript:reoptimize('<?php echo($id)?>', '<?php echo($invType[1])?>');"
1737
  title="<?php _e('Reoptimize from the backed-up image','shortpixel-image-optimiser');?>">
1738
  <?php _e('Re-optimize','shortpixel-image-optimiser');?> <?php echo($invType[1])?>
1739
  </a><?php
@@ -1744,26 +1757,26 @@ class ShortPixelView {
1744
  <?php } ?>
1745
  </div>
1746
  </div>
1747
- </div>
1748
- <?php } ?>
1749
  <div class='sp-column-info'>
1750
  <?php echo($message);?>
1751
  </div> <?php
1752
  }
1753
-
1754
  public function getQuotaExceededHTML($message = '') {
1755
- return "<div class='sp-column-actions' style='width:110px;'>
1756
  <a class='button button-smaller button-primary' href='https://shortpixel.com/login/". (defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) . "' target='_blank'>"
1757
- . __('Extend Quota','shortpixel-image-optimiser') .
1758
  "</a>
1759
- <a class='button button-smaller' href='admin.php?action=shortpixel_check_quota'>"
1760
  . __('Check&nbsp;&nbsp;Quota','shortpixel-image-optimiser') .
1761
  "</a></div>
1762
  <div class='sp-column-info'>" . $message . " " . __('Quota Exceeded','shortpixel-image-optimiser') . "</div>";
1763
  }
1764
-
1765
  public function outputComparerHTML() {?>
1766
- <div class="sp-modal-shade">
1767
  <div id="spUploadCompare" class="shortpixel-modal shortpixel-hide">
1768
  <div class="sp-modal-title">
1769
  <button type="button" class="sp-close-button">&times;</button>
@@ -1796,7 +1809,7 @@ class ShortPixelView {
1796
  </div>
1797
  </div>
1798
  </div>
1799
- </div>
1800
  <?php
1801
  }
1802
  }
1
  <?php
2
 
3
  class ShortPixelView {
4
+
5
  private $ctrl;
6
+
7
  public function __construct($controller) {
8
  $this->ctrl = $controller;
9
  }
10
+
11
  //handling older
12
  public function ShortPixelView($controller) {
13
  $this->__construct($controller);
14
  }
15
 
16
+ public function displayQuotaExceededAlert($quotaData, $averageCompression = false, $recheck = false)
17
+ { ?>
18
  <br/>
19
  <div class="wrap sp-quota-exceeded-alert" id="short-pixel-notice-exceed">
20
  <?php if($averageCompression) { ?>
30
  </div>
31
  </div>
32
  <?php } ?>
33
+ <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/robo-scared.png'));?>"
34
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-scared.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-scared@2x.png' ));?> 2x'
35
  class='short-pixel-notice-icon'>
36
  <h3><?php /* translators: header of the alert box */ _e('Quota Exceeded','shortpixel-image-optimiser');?></h3>
37
+ <p><?php /* translators: body of the alert box */
38
  if($recheck) {
39
  echo('<span style="color: red">' . __('You have no available image credits. If you just bought a package, please note that sometimes it takes a few minutes for the payment confirmation to be sent to us by the payment processor.','shortpixel-image-optimiser') . '</span><br>');
40
  }
41
+ printf(__('The plugin has optimized <strong>%s images</strong> and stopped because it reached the available quota limit.','shortpixel-image-optimiser'),
42
+ number_format(max(0, $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric'])));?>
43
  <?php if($quotaData['totalProcessedFiles'] < $quotaData['totalFiles']) { ?>
44
+ <?php
45
  printf(__('<strong>%s images and %s thumbnails</strong> are not yet optimized by ShortPixel.','shortpixel-image-optimiser'),
46
+ number_format(max(0, $quotaData['mainFiles'] - $quotaData['mainProcessedFiles'])),
47
  number_format(max(0, ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles'])))); ?>
48
  <?php } ?></p>
49
  <div> <!-- style='float:right;margin-top:20px;'> -->
50
  <button class="button button-primary" id="shortpixel-upgrade-advice" onclick="ShortPixel.proposeUpgrade()" style="margin-right:10px;"><strong>
51
  <?php _e('Show me the best available options', 'shortpixel_image_optimiser'); ?></strong></button>
52
+ <a class='button button-primary' href='https://shortpixel.com/login/<?php echo($this->ctrl->getApiKey());?>'
53
  title='<?php _e('Go to my account and select a plan','shortpixel-image-optimiser');?>' target='_blank' style="margin-right:10px;">
54
  <strong><?php _e('Upgrade','shortpixel-image-optimiser');?></strong>
55
  </a>
56
  <input type='button' name='checkQuota' class='button' value='<?php _e('Confirm New Credits','shortpixel-image-optimiser');?>'
57
  onclick="ShortPixel.recheckQuota()">
58
  </div>
59
+ <p><?php _e('Get more image credits by referring ShortPixel to your friends!','shortpixel-image-optimiser');?>
60
  <a href="https://shortpixel.com/login/<?php echo(defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey());?>/tell-a-friend" target="_blank">
61
  <?php _e('Check your account','shortpixel-image-optimiser');?>
62
  </a> <?php _e('for your unique referral link. For each user that joins, you will receive +100 additional image credits/month.','shortpixel-image-optimiser');?>
63
  </p>
64
+
65
  </div> <?php self::includeProposeUpgradePopup();
66
  }
67
+
68
+ public static function displayApiKeyAlert()
69
  { ?>
70
  <p><?php _e('In order to start the optimization process, you need to validate your API Key in the '
71
  . '<a href="options-general.php?page=wp-shortpixel">ShortPixel Settings</a> page in your WordPress Admin.','shortpixel-image-optimiser');?>
75
  </p>
76
  <?php
77
  }
78
+
79
  public static function displayActivationNotice($when = 'activate', $extra = '') {
80
  $extraStyle = ($when == 'compat' || $when == 'fileperms' ? "background-color: #ff9999;margin: 5px 20px 15px 0;'" : '');
81
  $icon = false;
84
  case 'compat': $extraClass = 'notice-error below-h2';
85
  case 'fileperms': $icon = 'scared'; $extraClass = 'notice-error'; break;
86
  case 'unlisted': $icon = 'magnifier'; break;
87
+ case 'upgmonth':
88
  case 'upgbulk': $icon = 'notes'; $extraClass = 'notice-success'; break;
89
  case 'spai':
90
  case 'generic-err': $extraClass = 'notice-error is-dismissible'; break;
94
  <div class='notice <?php echo($extraClass);?> notice-warning' id='short-pixel-notice-<?php echo($when);?>' <?php echo($extraStyle);?>>
95
  <?php if($when != 'activate') { ?>
96
  <div style="float:right;">
97
+ <?php if($when == 'upgmonth' || $when == 'upgbulk'){ ?>
98
  <button class="button button-primary" id="shortpixel-upgrade-advice" onclick="ShortPixel.proposeUpgrade()" style="margin-top:10px;margin-left:10px;"><strong>
99
  <?php _e('Show me the best available options', 'shortpixel_image_optimiser'); ?></strong></button>
100
  <?php } ?>
101
+ <?php if($when == 'unlisted'){ ?>
102
  <a href="javascript:ShortPixel.includeUnlisted()" class="button button-primary" style="margin-top:10px;margin-left:10px;">
103
  <strong><?php _e('Yes, include these thumbnails','shortpixel-image-optimiser');?></strong></a>
104
  <?php }
125
  if($when == 'upgmonth' || $when == 'upgbulk') { echo(' '); _e('advice','shortpixel-image-optimiser');}
126
  ?></h3> <?php
127
  switch($when) {
128
+ case '2h' :
129
  _e("Action needed. Please <a href='https://shortpixel.com/wp-apikey' target='_blank'>get your API key</a> to activate your ShortPixel plugin.",'shortpixel-image-optimiser') . "<BR><BR>";
130
  break;
131
  case '3d':
157
  case 'upgbulk' : ?>
158
  <p> <?php
159
  if($when == 'upgmonth') {
160
+ printf(__("You are adding an average of <strong>%d images and thumbnails every month</strong> to your Media Library and you have <strong>a plan of %d images/month</strong>."
161
  . " You might need to upgrade your plan in order to have all your images optimized.", 'shortpixel_image_optimiser'), $extra['monthAvg'], $extra['monthlyQuota']);
162
  } else {
163
  printf(__("You currently have <strong>%d images and thumbnails to optimize</strong> but you only have <strong>%d images</strong> available in your current plan."
164
  . " You might need to upgrade your plan in order to have all your images optimized.", 'shortpixel_image_optimiser'), $extra['filesTodo'], $extra['quotaAvailable']);
165
+ }?></p><?php
166
  self::includeProposeUpgradePopup();
167
  break;
168
  case 'unlisted' :
169
  _e("<p>ShortPixel found thumbnails which are not registered in the metadata but present alongside the other thumbnails. These thumbnails could be created and needed by some plugin or by the theme. Let ShortPixel optimize them as well?</p>", 'shortpixel-image-optimiser');?>
170
  <p>
171
+ <?php _e("For example, the image", 'shortpixel-image-optimiser');?>
172
  <a href='post.php?post=<?php echo($extra->id);?>&action=edit' target='_blank'>
173
  <?php echo($extra->name); ?>
174
  </a> has also these thumbs not listed in metadata:
185
  </div>
186
  <?php
187
  }
188
+ protected static function includeProposeUpgradePopup() {
189
+ wp_enqueue_style('short-pixel-modal.min.css', plugins_url('/res/css/short-pixel-modal.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
190
+ ?>
191
+
192
  <div id="shortPixelProposeUpgradeShade" class="sp-modal-shade" style="display:none;">
193
+ <div id="shortPixelProposeUpgrade" class="shortpixel-modal shortpixel-hide" style="min-width:610px;margin-left:-305px;">
194
  <div class="sp-modal-title">
195
  <button type="button" class="sp-close-upgrade-button" onclick="ShortPixel.closeProposeUpgrade()">&times;</button>
196
  <?php _e('Upgrade your ShortPixel account', 'shortpixel-image-optimiser');?>
198
  <div class="sp-modal-body sptw-modal-spinner" style="height:auto;min-height:400px;padding:0;">
199
  </div>
200
  </div>
201
+ </div>
202
  <?php }
203
+
204
+ public function displayBulkProcessingForm($quotaData, $thumbsProcessedCount, $under5PercentCount, $bulkRan,
205
  $averageCompression, $filesOptimized, $savedSpace, $percent, $customCount) {
206
  $settings = $this->ctrl->getSettings();
207
  $this->ctrl->outputHSBeacon();
222
  <div class="bulk-label"><?php _e('Smaller thumbnails','shortpixel-image-optimiser');?></div>
223
  <div class="bulk-val"><?php echo(number_format($quotaData['totalMlFiles'] - $quotaData['mainMlFiles']));?></div>
224
  <div style='width:165px; display:inline-block; padding-left: 5px'>
225
+ <input type='checkbox' id='thumbnails' name='thumbnails' onclick='ShortPixel.checkThumbsUpdTotal(this)' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>>
226
  <?php _e('Include thumbnails','shortpixel-image-optimiser');?>
227
  </div><br>
228
  <?php if($quotaData["totalProcessedMlFiles"] > 0) { ?>
245
  <?php _e('Total to be optimized','shortpixel-image-optimiser');?>
246
  <div class="reset"><?php _e('(Originals and thumbnails)','shortpixel-image-optimiser');?></div>
247
  </div>
248
+ <div class="bulk-val bulk-total" id='displayTotal'><?php echo(number_format($customCount));?></div>
249
  <?php } ?>
250
  </div>
251
  <?php if(max(0, $quotaData['totalMlFiles'] - $quotaData['totalProcessedMlFiles']) + $customCount > 0) { ?>
277
  <input type='submit' name='bulkCleanup' id='bulkCleanup' class='button' value='<?php _e('Bulk Delete SP Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('Cleanup', event)" style="width:100%">
278
  <input type='submit' name='bulkCleanupPending' id='bulkCleanupPending' class='button' value='<?php _e('Bulk Delete Pending Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('CleanupPending', event)" style="display:none">
279
  </div>
280
+
281
  <?php }
282
  } else {?>
283
  <div class="bulk-play bulk-nothing-optimize">
317
  <?php if($quotaData['totalProcessedFiles'] < $quotaData['totalFiles']) { ?>
318
  <p><?php printf(__('%s images and %s thumbnails are not yet optimized by ShortPixel.','shortpixel-image-optimiser'),
319
  number_format($quotaData['mainFiles'] - $quotaData['mainProcessedFiles']),
320
+ number_format(($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles'])));?>
321
  </p>
322
  <?php } ?>
323
  <p><?php _e('You can continue optimizing your Media Gallery from where you left, by clicking the Resume processing button. Already optimized images will not be reprocessed.','shortpixel-image-optimiser');?></p>
358
  <div class="fb-like" data-href="https://www.facebook.com/ShortPixel" data-width="260" data-layout="button_count" data-action="like" data-show-faces="true" data-share="true"></div>
359
  </div>
360
  <div style="float:left;margin:-7px 0 0 10px">
361
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="https://shortpixel.com"
362
+ data-text="<?php
363
  if(0+$averageCompression>20) {
364
  _e("I just #optimized my site's images by ",'shortpixel-image-optimiser');
365
  } else {
366
  _e("I just #optimized my site's images ",'shortpixel-image-optimiser');
367
  }
368
  echo(round($averageCompression) ."%");
369
+ echo(__("with @ShortPixel, a #WordPress image optimization plugin",'shortpixel-image-optimiser') . " #pagespeed #seo");?>"
370
  data-size='large'><?php _e('Tweet','shortpixel-image-optimiser');?></a>
371
  </div>
372
  <script>
389
  </script>
390
  </div>
391
  </div>
392
+ <?php if(0+$averageCompression>30) {?>
393
  <div class='shortpixel-rate-us' style='float:left;padding-top:0'>
394
  <a href="https://wordpress.org/support/view/plugin-reviews/shortpixel-image-optimiser?rate=5#postform" target="_blank">
395
  <span>
401
  </div>
402
  <div id="sp-bulk-stats" style="display:none">
403
  <?php $this->displayBulkStats($quotaData['totalProcessedFiles'], $quotaData['mainProcessedFiles'], $under5PercentCount, $averageCompression, $savedSpace);?>
404
+ </div>
405
  </div>
406
  <p><?php printf(__('Go to the ShortPixel <a href="%soptions-general.php?page=wp-shortpixel#stats">Stats</a>
407
  and see all your websites\' optimized stats. Download your detailed <a href="https://%s/v2/report.php?key=%s">Optimization Report</a>
408
  to check your image optimization statistics for the last 40 days.','shortpixel-image-optimiser'),
409
  get_admin_url(), SHORTPIXEL_API, (defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) );?></p>
410
+ <?php
411
  $failed = $this->ctrl->getPrioQ()->getFailed();
412
  if(count($failed)) { ?>
413
  <div class="bulk-progress sp-notice sp-notice-warning sp-floating-block sp-double-width" style="margin-bottom: 15px">
420
  <div class="bulk-progress sp-notice sp-notice-info sp-floating-block sp-double-width">
421
  <?php
422
  $todo = $reopt = false;
423
+ if($quotaData['totalProcessedFiles'] < $quotaData['totalFiles']) {
424
  $todo = true;
425
  $mainNotProcessed = max(0, $quotaData['mainFiles'] - $quotaData['mainProcessedFiles']);
426
  $thumbsNotProcessed = max(0, ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles']));
427
  ?>
428
  <p>
429
+ <?php
430
  if($mainNotProcessed && $thumbsNotProcessed) {
431
+ printf(__("%s images and %s thumbnails are not yet optimized by ShortPixel.",'shortpixel-image-optimiser'),
432
+ number_format($mainNotProcessed), number_format($thumbsNotProcessed));
433
  } elseif($mainNotProcessed) {
434
+ printf(__("%s images are not yet optimized by ShortPixel.",'shortpixel-image-optimiser'), number_format($mainNotProcessed));
435
  } elseif($thumbsNotProcessed) {
436
+ printf(__("%s thumbnails are not yet optimized by ShortPixel.",'shortpixel-image-optimiser'), number_format($thumbsNotProcessed));
437
  }
438
  _e('','shortpixel-image-optimiser');
439
  if (count($quotaData['filesWithErrors'])) {
476
  $extraW = $extraO = '';
477
  if( !$this->ctrl->backupFolderIsEmpty()
478
  && ( ($quotaData['totalProcLossyFiles'] > 0 && $settings->compressionType != 1)
479
+ || ($quotaData['totalProcGlossyFiles'] > 0 && $settings->compressionType != 2)
480
  || ($quotaData['totalProcLosslessFiles'] > 0 && $settings->compressionType != 0)))
481
+ {
482
  $todo = $reopt = true;
483
  $statType = ucfirst($otherTypes[0]);
484
  $thumbsCount = $quotaData['totalProc'.$statType.'Files'] - $quotaData['mainProc'.$statType.'Files'];
485
+
486
  $statType2 = ucfirst($otherTypes[1]);
487
  $thumbsCount2 = $quotaData['totalProc'.$statType2.'Files'] - $quotaData['mainProc'.$statType2.'Files'];
488
  if($quotaData['totalProc'.$statType2.'Files'] > 0 ) {
489
  if($quotaData['totalProc'.$statType.'Files'] > 0) {
490
+ $extraW = sprintf(__('%s images and %s thumbnails were optimized <strong>%s</strong>. ','shortpixel-image-optimiser'),
491
  number_format($quotaData['mainProc'.$statType2.'Files']),
492
  number_format($thumbsCount2), $otherTypes[1]);
493
+ $extraO = sprintf(__('%s images were optimized <strong>%s</strong>. ','shortpixel-image-optimiser'),
494
  number_format($quotaData['mainProc'.$statType2.'Files']), $otherTypes[1]);
495
  } else {
496
  $extraW = $extraO = ''; $otherTypes[0] = $otherTypes[1]; $statType = $statType2;
499
  ?>
500
  <p id="with-thumbs" <?php echo(!$settings->processThumbnails ? 'style="display:none;"' : "");?>>
501
  <?php echo($extraW);
502
+ printf(__('%s images and %s thumbnails were optimized <strong>%s</strong>. You can re-optimize <strong>%s</strong> the ones that have backup.','shortpixel-image-optimiser'),
503
  number_format($quotaData['mainProc'.$statType.'Files']),
504
  number_format($thumbsCount), $otherTypes[0], $optType);?>
505
  </p>
506
  <p id="without-thumbs" <?php echo($settings->processThumbnails ? 'style="display:none;"' : "");?>>
507
+ <?php echo($extraO);
508
+ printf(__('%s images were optimized <strong>%s</strong>. You can re-optimize <strong>%s</strong> the ones that have backup. ','shortpixel-image-optimiser'),
509
  number_format($quotaData['mainProc'.$statType.'Files']),
510
  $otherTypes[0], $optType);?>
511
  <?php echo($thumbsCount + $thumbsCount2 ? number_format($thumbsCount + $thumbsCount2) . __(' thumbnails will be restored to originals.','shortpixel-image-optimiser') : '');?>
520
  echo(' ');
521
  printf(__('Already <strong>%s</strong> optimized images will not be reprocessed.','shortpixel-image-optimiser'), $todo ? ($optType) : '');
522
  if($reopt) { ?>
523
+ <br><?php _e('Please note that reoptimizing images as <strong>lossy/lossless</strong> may use additional credits.','shortpixel-image-optimiser')?>
524
  <a href="http://blog.shortpixel.com/the-all-new-re-optimization-functions-in-shortpixel/" target="_blank" class="shortpixel-help-link">
525
  <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
526
  </a>
527
  <?php } ?>
528
  </p>
529
  <form action='' method='POST' >
530
+ <input type='checkbox' id='bulk-thumbnails' name='thumbnails' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>
531
  onchange="ShortPixel.onBulkThumbsCheck(this)"> <?php _e('Include thumbnails','shortpixel-image-optimiser');?><br><br>
532
+
533
+ <a style="float: right;margin: 0 10px; line-height: 28px" href='<?php echo add_query_arg('part','bulk-restore-all'); ?> '><?php _e('Bulk Restore Images','shortpixel-image-optimiser'); ?></a>
534
+
535
  <input type='submit' name='bulkProcess' id='bulkProcess' class='button button-primary' value='<?php _e('Restart Optimizing','shortpixel-image-optimiser');?>'
536
  <?php echo($settings->quotaExceeded? "disabled title=\"" . __("Top-up your account to optimize more images.",'shortpixel-image-optimiser')."\"" : ""); ?>>
537
+ <!-- <input type='submit' name='bulkRestore' id='bulkRestore' class='button' value='<?php _e('Bulk Restore Media Library','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('Restore',event)" style="float: right;"> -->
538
+
539
+
540
  <input type='submit' name='bulkCleanup' id='bulkCleanup' class='button' value='<?php _e('Bulk Delete SP Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('Cleanup',event)" style="float: right;margin-right:10px;">
541
  <input type='submit' name='bulkCleanupPending' id='bulkCleanupPending' class='button' value='<?php _e('Bulk Delete Pending Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('CleanupPending', event)" style="display:none">
542
  </form>
615
  </div>
616
  <?php
617
  }
618
+
619
  public function displayBulkProgressBar($running, $percent, $message, $remainingQuota, $averageCompression, $type = 1, $customPending = false) {
620
  $percentBefore = $percentAfter = '';
621
  if($percent > 24) {
643
  </script>
644
  </div>
645
  </div>
646
+ <?php if($running) {
647
  if($type > 0) { ?>
648
+ <div class="sp-h2"><?php
649
  echo($type & 1 ? __('Media Library','shortpixel-image-optimiser') . " " : "");
650
  echo($type & 3 == 3 ? __('and','shortpixel-image-optimiser') . " " : "");
651
+ echo($type & 2 ? __('Custom folders','shortpixel-image-optimiser') . " " : "");
652
  _e('optimization in progress ...','shortpixel-image-optimiser');?></div>
653
  <p style="margin: 0 0 18px;"><?php _e('Bulk optimization has started.','shortpixel-image-optimiser');?><br>
654
+ <?php
655
  } elseif($type == 0) { // restore ?>
656
+ <div class="sp-h2"><?php
657
  _e('Media Library restore in progress ...','shortpixel-image-optimiser');?></div>
658
+ <p style="margin: 0 0 18px;"><?php _e('Bulk restore has started.','shortpixel-image-optimiser');?><br>
659
  <?php }
660
  elseif($type == -1) { // cleanup ?>
661
+ <div class="sp-h2"><?php
662
  _e('Media Library cleanup in progress ...','shortpixel-image-optimiser');?></div>
663
+ <p style="margin: 0 0 18px;"><?php _e('Bulk cleanup has started.','shortpixel-image-optimiser');?><br>
664
  <?php }
665
+ printf(__('This process will take some time, depending on the number of images in your library. In the meantime, you can continue using
666
  the admin as usual, <a href="%s" target="_blank">in a different browser window or tab</a>.<br>
667
  However, <strong>if you close this window, the bulk processing will pause</strong> until you open the media gallery or the ShortPixel bulk page again.','shortpixel-image-optimiser'), get_admin_url());?>
668
  </p>
699
  </div>
700
  <?php
701
  }
702
+
703
  public function displayBulkStats($totalOptimized, $mainOptimized, $under5PercentCount, $averageCompression, $savedSpace) {?>
704
  <div class="bulk-progress bulk-stats">
705
  <div class="label"><?php _e('Processed Images and PDFs:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($mainOptimized));?></div><br>
713
  </div>
714
  <?php
715
  }
716
+
717
  public function displayFailed($failed) {
718
  ?>
719
  <div class="bulk-progress bulk-stats" style="padding-top:5px;">
720
+ <?php foreach($failed as $fail) {
721
  if($fail->type == ShortPixelMetaFacade::CUSTOM_TYPE) {
722
  $meta = $fail->meta;
723
  ?> <div><a href="<?php echo(ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath());?>"><?php echo(substr($meta->getName(), 0, 80));?> - ID: C-<?php echo($fail->id);?></a></div><br/>
730
  <?php
731
  }
732
  function displaySettings($showApiKey, $editApiKey, $quotaData, $notice, $resources = null, $averageCompression = null, $savedSpace = null, $savedBandwidth = null,
733
+ $remainingImages = null, $totalCallsMade = null, $fileCount = null, $backupFolderSize = null,
734
  $customFolders = null, $folderMsg = false, $addedFolder = false, $showAdvanced = false, $cloudflareAPI = false, $htaccessWriteable = false, $isNginx = false ) {
735
  //wp_enqueue_script('jquery.idTabs.js', plugins_url('/js/jquery.idTabs.js',__FILE__) );
736
  $this->ctrl->outputHSBeacon();
738
  <div class="wrap">
739
  <h1><?php _e('ShortPixel Plugin Settings','shortpixel-image-optimiser');?></h1>
740
  <p style="font-size:18px">
741
+ <a href="https://shortpixel.com/<?php
742
  echo(($this->ctrl->getVerifiedKey() ? "login/".(defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) : "pricing") . WPShortPixel::getAffiliateSufix());
743
  ?>" target="_blank" style="font-size:18px">
744
  <?php _e('Upgrade now','shortpixel-image-optimiser');?>
774
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-settings"><?php _e('General','shortpixel-image-optimiser');?></a></h2>
775
  <?php }
776
  $this->displaySettingsForm($showApiKey, $editApiKey, $quotaData);?>
777
+ </section>
778
  <?php if($this->ctrl->getVerifiedKey()) {?>
779
  <section <?php echo($showAdvanced ? "class='sel-tab'" : "");?> id="tab-adv-settings" class="clearfix">
780
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-adv-settings"><?php _e('Advanced','shortpixel-image-optimiser');?></a></h2>
796
  <section id="tab-stats">
797
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-stats"><?php _e('Statistics','shortpixel-image-optimiser');?></a></h2>
798
  <?php
799
+ $this->displaySettingsStats($quotaData, $averageCompression, $savedSpace, $savedBandwidth,
800
  $remainingImages, $totalCallsMade, $fileCount, $backupFolderSize);?>
801
+ </section>
802
  <?php }
803
  if( $resources !== null && $quotaData['APICallsQuotaOneTimeNumeric']<10000 && $quotaData['APICallsQuotaNumeric']<5000 ) {?>
804
  <section id="tab-resources">
814
  </script>
815
  </div>
816
  <?php
817
+ }
818
+
819
  public function displaySettingsForm($showApiKey, $editApiKey, $quotaData) {
820
  $settings = $this->ctrl->getSettings();
821
  $checked = ($this->ctrl->processThumbnails() ? 'checked' : '');
822
  $checkedBackupImages = ($this->ctrl->backupImages() ? 'checked' : '');
823
  $removeExif = ($settings->keepExif ? '' : 'checked');
824
  $resize = ($this->ctrl->getResizeImages() ? 'checked' : '');
825
+ $resizeDisabled = ($this->ctrl->getResizeImages() ? '' : 'disabled');
826
  $minSizes = $this->ctrl->getMaxIntermediateImageSize();
827
  $thumbnailsToProcess = isset($quotaData['totalFiles']) ? ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles']) : 0;
828
  $adminEmail = get_bloginfo('admin_email');
831
  <div class="wp-shortpixel-options wp-shortpixel-tab-content">
832
  <?php if($this->ctrl->getVerifiedKey()) { ?>
833
  <p><?php printf(__('New images uploaded to the Media Library will be optimized automatically.<br/>If you have existing images you would like to optimize, you can use the <a href="%supload.php?page=wp-short-pixel-bulk">Bulk Optimization Tool</a>.','shortpixel-image-optimiser'),get_admin_url());?></p>
834
+ <?php } else {
835
  if($showApiKey) {?>
836
  <h3><?php _e('Request an API Key:','shortpixel-image-optimiser');?></h3>
837
  <p style='font-size: 14px'><?php _e('If you don\'t have an API Key, you can request one for free. Just press the "Request Key" button after checking that the e-mail is correct.','shortpixel-image-optimiser');?></p>
876
  <p style='font-size: 14px'>
877
  <?php _e('If you already have an API Key please input it below and press Validate.','shortpixel-image-optimiser');?>
878
  </p>
879
+ <?php }
880
  }?>
881
  <table class="form-table">
882
  <tbody>
883
  <tr>
884
  <th scope="row"><label for="key"><?php _e('API Key:','shortpixel-image-optimiser');?></label></th>
885
  <td>
886
+ <?php
887
  $canValidate = false;
888
  if($showApiKey) {
889
  $canValidate = true;?>
890
+ <input name="key" type="text" id="key" value="<?php echo( $this->ctrl->getApiKey() );?>"
891
  class="regular-text" <?php echo($editApiKey ? "" : 'disabled') ?> <?php echo $this->ctrl->getVerifiedKey() ? 'onkeyup="ShortPixel.apiKeyChanged()"' : '' ?>>
892
+ <?php } elseif(defined("SHORTPIXEL_API_KEY")) {
893
  $canValidate = true;?>
894
+ <input name="key" type="text" id="key" disabled="true" placeholder="<?php
895
  if(defined("SHORTPIXEL_HIDE_API_KEY")) {
896
+ echo("********************");
897
  } else {
898
  _e('Multisite API Key','shortpixel-image-optimiser');
899
  }
911
  <?php if($showApiKey && !$editApiKey) { ?>
912
  <p class="settings-info"><?php _e('Key defined in wp-config.php.','shortpixel-image-optimiser');?></p>
913
  <?php } ?>
914
+
915
  </td>
916
  </tr>
917
  <?php if (!$this->ctrl->getVerifiedKey()) { //if invalid key we display the link to the API Key ?>
984
  <td>
985
  <input name="removeExif" type="checkbox" id="removeExif" <?php echo( $removeExif );?>>
986
  <label for="removeExif"><?php _e('Remove the EXIF tag of the image (recommended).','shortpixel-image-optimiser');?></label>
987
+ <p class="settings-info"> <?php _e('EXIF is a set of various pieces of information that are automatically embedded into the image upon creation. This can include GPS position, camera manufacturer, date and time, etc.
988
  Unless you really need that data to be preserved, we recommend removing it as it can lead to <a href="http://blog.shortpixel.com/how-much-smaller-can-be-images-without-exif-icc" target="_blank">better compression rates</a>.','shortpixel-image-optimiser');?></p>
989
  </td>
990
  </tr>
994
  <input name="resize" type="checkbox" id="resize" <?php echo( $resize );?>>
995
  <label for="resize"><?php _e('to maximum','shortpixel-image-optimiser');?></label>
996
  <input type="text" name="width" id="width" style="width:70px" class="resize-sizes"
997
+ value="<?php echo( $this->ctrl->getResizeWidth() > 0 ? $this->ctrl->getResizeWidth() : min(924, $minSizes['width']) );?>" <?php echo( $resizeDisabled );?>/> <?php
998
  _e('pixels wide &times;','shortpixel-image-optimiser');?>
999
+ <input type="text" name="height" id="height" class="resize-sizes" style="width:70px"
1000
+ value="<?php echo( $this->ctrl->getResizeHeight() > 0 ? $this->ctrl->getResizeHeight() : min(924, $minSizes['height']) );?>" <?php echo( $resizeDisabled );?>/> <?php
1001
  _e('pixels high (original aspect ratio is preserved and image is not cropped)','shortpixel-image-optimiser');?>
1002
  <input type="hidden" id="min-width" value="<?php echo($minSizes['width']);?>"/>
1003
  <input type="hidden" id="min-height" value="<?php echo($minSizes['height']);?>"/>
1004
+ <p class="settings-info">
1005
  <?php _e('Recommended for large photos, like the ones taken with your phone. Saved space can go up to 80% or more after resizing.','shortpixel-image-optimiser');?>
1006
  <a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
1007
  <span class="dashicons dashicons-editor-help"></span><?php _e('Read more','shortpixel-image-optimiser');?>
1009
  </p>
1010
  <div style="margin-top: 10px;">
1011
  <input type="radio" name="resize_type" id="resize_type_outer" value="outer" <?php echo($settings->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
1012
+ <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?>"
1013
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer@2x.png' ));?> 2x'
1014
  title="<?php _e('Sizes will be greater or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 1000x1500px while an image of 3000x2000px will be resized to 1800x1200px','shortpixel-image-optimiser');?>">
1015
  <input type="radio" name="resize_type" id="resize_type_inner" value="inner" <?php echo($settings->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
1016
+ <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?>"
1017
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner@2x.png' ));?> 2x'
1018
  title="<?php _e('Sizes will be smaller or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 800x1200px while an image of 3000x2000px will be resized to 1000x667px','shortpixel-image-optimiser');?>">
1019
  <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
1031
  </div>
1032
  <script>
1033
  jQuery(document).ready(function () {
1034
+ ShortPixel.setupGeneralTab(document.wp_shortpixel_options.compressionType,
1035
  Math.min(924, <?php echo($minSizes['width']);?>),
1036
  Math.min(924, <?php echo($minSizes['height']);?>));
1037
  });
1038
  </script>
1039
  <?php }
1040
  }
1041
+
1042
  public function displayAdvancedSettingsForm($customFolders = false, $addedFolder = false, $htaccessWriteable = false, $isNginx = false ) {
1043
  $settings = $this->ctrl->getSettings();
1044
  $minSizes = $this->ctrl->getMaxIntermediateImageSize();
1123
  <td></td>
1124
  </tr>
1125
  <?php foreach($customFolders as $folder) {
1126
+ $typ = $folder->getType();
1127
  $typ = $typ ? $typ . "<br>" : "";
1128
  $stat = $this->ctrl->getSpMetaDao()->getFolderOptimizationStatus($folder->getId());
1129
  $cnt = $folder->getFileCount();
1130
+ $st = ($cnt == 0
1131
  ? __("Empty",'shortpixel-image-optimiser')
1132
+ : ($stat->Total == $stat->Optimized
1133
  ? __("Optimized",'shortpixel-image-optimiser')
1134
  : ($stat->Optimized + $stat->Pending > 0 ? __("Pending",'shortpixel-image-optimiser') : __("Waiting",'shortpixel-image-optimiser'))));
1135
+
1136
  $err = $stat->Failed > 0 && !$st == __("Empty",'shortpixel-image-optimiser') ? " ({$stat->Failed} failed)" : "";
1137
+
1138
  $action = ($st == __("Optimized",'shortpixel-image-optimiser') || $st == __("Empty",'shortpixel-image-optimiser') ? __("Stop monitoring",'shortpixel-image-optimiser') : __("Stop optimizing",'shortpixel-image-optimiser'));
1139
+
1140
+ $fullStat = $st == __("Empty",'shortpixel-image-optimiser') ? "" : __("Optimized",'shortpixel-image-optimiser') . ": " . $stat->Optimized . ", "
1141
+ . __("Pending",'shortpixel-image-optimiser') . ": " . $stat->Pending . ", " . __("Waiting",'shortpixel-image-optimiser') . ": " . $stat->Waiting . ", "
1142
  . __("Failed",'shortpixel-image-optimiser') . ": " . $stat->Failed;
1143
  ?>
1144
  <tr>
1160
  </td>
1161
  <td>
1162
  <input type="button" class="button remove-folder-button" data-value="<?php echo($folder->getPath()); ?>" title="<?php echo($action . " " . $folder->getPath()); ?>" value="<?php echo $action;?>">
1163
+ <input type="button" style="display:none;" class="button button-alert recheck-folder-button" data-value="<?php echo($folder->getPath()); ?>"
1164
+ title="<?php _e('Full folder refresh, check each file of the folder if it changed since it was optimized. Might take up to 1 min. for big folders.','shortpixel-image-optimiser');?>"
1165
  value="<?php _e('Refresh','shortpixel-image-optimiser');?>">
1166
  </td>
1167
  </tr>
1168
  <?php }?>
1169
  </table>
1170
  <?php } ?>
1171
+
1172
+ <div class='addCustomFolder'>
1173
+
1174
+ <input type="hidden" name="removeFolder" id="removeFolder"/>
1175
+ <input type="hidden" name="recheckFolder" id="removeFolder"/>
1176
+ <p class='add-folder-text'><strong><?php _e('Add a custom folder', 'shortpixel-image-optimiser'); ?></strong></p>
1177
+ <input type="text" name="addCustomFolderView" id="addCustomFolderView" class="regular-text" value="<?php echo($addedFolder);?>" disabled style="">&nbsp;
1178
+ <input type="hidden" name="addCustomFolder" id="addCustomFolder" value="<?php echo($addedFolder);?>"/>
1179
+ <input type="hidden" id="customFolderBase" value="<?php echo WPShortPixel::getCustomFolderBase(); ?>">
1180
+
1181
+ <a class="button select-folder-button" title="<?php _e('Select the images folder on your server.','shortpixel-image-optimiser');?>" href="javascript:void(0);">
1182
+ <?php _e('Select ...','shortpixel-image-optimiser');?>
1183
+ </a>
1184
+ <input type="submit" name="saveAdv" id="saveAdvAddFolder" class="button button-primary hidden" title="<?php _e('Add this Folder','shortpixel-image-optimiser');?>" value="<?php _e('Add this Folder','shortpixel-image-optimiser');?>">
1185
  <p class="settings-info">
1186
  <?php _e('Use the Select... button to select site folders. ShortPixel will optimize images and PDFs from the specified folders and their subfolders. The optimization status for each image or PDF in these folders can be seen in the <a href="upload.php?page=wp-short-pixel-custom">Other Media list</a>, under the Media menu.','shortpixel-image-optimiser');?>
1187
  <a href="https://blog.shortpixel.com/optimize-images-outside-media-library/" target="_blank" class="shortpixel-help-link">
1188
  <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
1189
  </a>
1190
  </p>
1191
+
1192
+ <div class="sp-modal-shade sp-folder-picker-shade"></div>
1193
+ <div class="shortpixel-modal modal-folder-picker shortpixel-hide">
1194
  <div class="sp-modal-title"><?php _e('Select the images folder','shortpixel-image-optimiser');?></div>
1195
  <div class="sp-folder-picker"></div>
1196
  <input type="button" class="button button-info select-folder-cancel" value="<?php _e('Cancel','shortpixel-image-optimiser');?>" style="margin-right: 30px;">
1197
  <input type="button" class="button button-primary select-folder" value="<?php _e('Select','shortpixel-image-optimiser');?>">
1198
  </div>
1199
+
1200
  <script>
1201
  jQuery(document).ready(function () {
1202
  ShortPixel.initFolderSelector();
1203
  });
1204
  </script>
1205
+ </div> <!-- end of AddCustomFolder -->
1206
  </td>
1207
  </tr>
1208
  <?php if($hasNextGen) { ?>
1337
  <tr>
1338
  <th scope="row"><label for="excludePatterns"><?php _e('Exclude patterns','shortpixel-image-optimiser');?></label></th>
1339
  <td>
1340
+ <input name="excludePatterns" type="text" id="excludePatterns" value="<?php echo( $excludePatterns );?>" class="regular-text" placeholder="<?php
1341
+ _e('name:keepbig, path:/ignore_regex/i, size:1000x2000','shortpixel-image-optimiser');?>">
1342
  <?php _e('Exclude certain images from being optimized, based on patterns.','shortpixel-image-optimiser');?>
1343
+ <p class="settings-info">
1344
  <?php _e('Add patterns separated by comma. A pattern consist of a <strong>type:value</strong> pair; the accepted types are
1345
  <strong>"name"</strong>, <strong>"path"</strong> and <strong>"size"</strong>.
1346
  A file will be excluded if it matches any of the patterns.
1360
  <tr>
1361
  <th scope="row"><label for="authentication"><?php _e('HTTP AUTH credentials','shortpixel-image-optimiser');?></label></th>
1362
  <td>
1363
+ <input name="siteAuthUser" type="text" id="siteAuthUser" value="<?php echo( esc_html($settings->siteAuthUser ));?>" class="regular-text" placeholder="<?php _e('User','shortpixel-image-optimiser');?>"><br>
1364
+ <input name="siteAuthPass" type="text" id="siteAuthPass" value="<?php echo( esc_html($settings->siteAuthPass ));?>" class="regular-text" placeholder="<?php _e('Password','shortpixel-image-optimiser');?>">
1365
+ <p class="settings-info">
1366
  <?php _e('Only fill in these fields if your site (front-end) is not publicly accessible and visitors need a user/pass to connect to it. If you don\'t know what is this then just <strong>leave the fields empty</strong>.','shortpixel-image-optimiser');?>
1367
  </p>
1368
  </td>
1413
  </script>
1414
  <?php }
1415
  }
1416
+
1417
  /**
1418
  * @desc This form is used in WP back-end to allow users that use CloudFlare to save their settings
1419
  *
1439
  </th>
1440
  <td>
1441
  <input name="cloudflare-email" type="text" id="cloudflare-email" <?php echo($noCurl ? 'disabled' : '');?>
1442
+ value="<?php echo(esc_html($this->ctrl->fetch_cloudflare_api_email())); ?>" class="regular-text">
1443
  <p class="settings-info">
1444
  <?php _e('The e-mail address you use to login to CloudFlare.','shortpixel-image-optimiser');?>
1445
  </p>
1451
  </th>
1452
  <td>
1453
  <input name="cloudflare-auth-key" type="text" id="cloudflare-auth-key" <?php echo($noCurl ? 'disabled' : '');?>
1454
+ value="<?php echo(esc_html($this->ctrl->fetch_cloudflare_api_key())); ?>" class="regular-text">
1455
  <p class="settings-info">
1456
  <?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>
1457
  </p>
1463
  </th>
1464
  <td>
1465
  <input name="cloudflare-zone-id" type="text" id="cloudflare-zone-id" <?php echo($noCurl ? 'disabled' : '');?>
1466
+ value="<?php echo(esc_html($this->ctrl->fetch_cloudflare_api_zoneid())); ?>" class="regular-text">
1467
  <p class="settings-info">
1468
  <?php _e('This can be found in your Cloudflare account in the "Overview" section for your domain.','shortpixel-image-optimiser');?>
1469
  </p>
1623
 
1624
  public function renderCustomColumn($id, $data, $extended = false){ ?>
1625
  <div id='sp-msg-<?php echo($id);?>' class='column-wp-shortPixel'>
1626
+
1627
  <?php switch($data['status']) {
1628
+ case 'n/a': ?>
1629
  <?php _e('Optimization N/A','shortpixel-image-optimiser');?> <?php
1630
  break;
1631
+ case 'notFound': ?>
1632
  <?php _e('Image does not exist.','shortpixel-image-optimiser');?> <?php
1633
  break;
1634
+ case 'invalidKey':
1635
  if(defined("SHORTPIXEL_API_KEY")) { // multisite key - need to be validated on each site but it's not invalid
1636
  ?> <?php _e('Please <a href="options-general.php?page=wp-shortpixel">go to Settings</a> to validate the API Key.','shortpixel-image-optimiser');?> <?php
1637
  } else {
1638
  ?> <?php _e('Invalid API Key. <a href="options-general.php?page=wp-shortpixel">Check your Settings</a>','shortpixel-image-optimiser');?> <?php
1639
+ }
1640
  break;
1641
+ case 'quotaExceeded':
1642
  echo($this->getQuotaExceededHTML(isset($data['message']) ? $data['message'] : ''));
1643
  break;
1644
+ case 'optimizeNow':
1645
+ if($data['showActions']) { ?>
1646
  <a class='button button-smaller button-primary' href="javascript:manualOptimization('<?php echo($id)?>', false)">
1647
  <?php _e('Optimize now','shortpixel-image-optimiser');?>
1648
+ </a>
1649
  <?php }
1650
  echo($data['message']);
1651
  if(isset($data['thumbsTotal']) && $data['thumbsTotal'] > 0) {
1657
  echo($data['message']);
1658
  if(isset($data['cleanup'])) {?> <a class='button button-smaller button-primary' href="javascript:manualOptimization('<?php echo($id)?>', true)">
1659
  <?php _e('Cleanup&Retry','shortpixel-image-optimiser');?>
1660
+ </a> <?php
1661
  } else {
1662
  if($data['status'] == 'retry') { ?>
1663
  <div style="overflow:hidden">
1673
  <?php
1674
  }
1675
  break;
1676
+ case 'pdfOptimized':
1677
  case 'imgOptimized':
1678
  $excluded = (isset($data['excludeSizes']) ? count($data['excludeSizes']) : 0);
1679
  $successText = $this->getSuccessText($data['percent'],$data['bonus'],$data['type'],$data['thumbsOpt'],$data['thumbsTotal'], $data['retinasOpt'], $data['excludeSizes']);
1694
  $missingThumbs .= '</span>';
1695
  }
1696
  $successText .= ($data['webpCount'] ? "<br>+" . $data['webpCount'] . __(" WebP images", 'shortpixel-image-optimiser') : "")
1697
+ . "<br>EXIF: " . ($data['exifKept'] ? __('kept','shortpixel-image-optimiser') : __('removed','shortpixel-image-optimiser'))
1698
  . ($data['png2jpg'] ? '<br>' . __('Converted from PNG','shortpixel-image-optimiser'): '')
1699
  . "<br>" . __("Optimized on", 'shortpixel-image-optimiser') . ": " . $data['date']
1700
  . $excludeSizes . $missingThumbs;
1701
  }
 
1702
  $this->renderListCell($id, $data['status'], $data['showActions'], $data['thumbsToOptimize'],
1703
  $data['backup'], $data['type'], $data['invType'], $successText);
1704
+
1705
  break;
1706
  }
1707
  //if($extended) {
1709
  //}
1710
  ?>
1711
  </div>
1712
+ <?php
1713
  }
1714
+
1715
  public function getSuccessText($percent, $bonus, $type, $thumbsOpt = 0, $thumbsTotal = 0, $retinasOpt = 0, $excluded = 0) {
1716
  if($percent == 999) return __("Reduced by X%(unknown)");
1717
  return ($percent && $percent > 0 ? __('Reduced by','shortpixel-image-optimiser') . ' <strong>' . $percent . '%</strong> ' : '')
1718
  .(!$bonus ? ' ('.$type.')':'')
1719
+ .($bonus && $percent ? '<br>' : '')
1720
+ .($bonus ? __('Bonus processing','shortpixel-image-optimiser') : '')
1721
  .($bonus ? ' ('.$type.')':'') . '<br>'
1722
+ .($thumbsOpt ? ( $thumbsTotal > $thumbsOpt
1723
+ ? sprintf(__('+%s of %s thumbnails optimized','shortpixel-image-optimiser'),$thumbsOpt,$thumbsTotal)
1724
  : sprintf(__('+%s thumbnails optimized','shortpixel-image-optimiser'),$thumbsOpt)) : '')
1725
  .($retinasOpt ? '<br>' . sprintf(__('+%s Retina images optimized','shortpixel-image-optimiser') , $retinasOpt) : '' );
1726
  }
1727
+
1728
  public function renderListCell($id, $status, $showActions, $thumbsRemain, $backup, $type, $invType, $message, $extraClass = '') {
1729
  if($showActions && ($backup || $thumbsRemain)) { ?>
1730
  <div class='sp-column-actions <?php echo($extraClass);?>'>
1740
  </a>
1741
  <?php }
1742
  if($backup) {
1743
+ if($type) {
1744
  //$invType = $type == 'lossy' ? 'lossless' : 'lossy'; ?>
1745
+ <a class="sp-action-reoptimize1" href="javascript:reoptimize('<?php echo($id)?>', '<?php echo($invType[0])?>');"
1746
  title="<?php _e('Reoptimize from the backed-up image','shortpixel-image-optimiser');?>">
1747
  <?php _e('Re-optimize','shortpixel-image-optimiser');?> <?php echo($invType[0])?>
1748
  </a>
1749
+ <a class="sp-action-reoptimize2" href="javascript:reoptimize('<?php echo($id)?>', '<?php echo($invType[1])?>');"
1750
  title="<?php _e('Reoptimize from the backed-up image','shortpixel-image-optimiser');?>">
1751
  <?php _e('Re-optimize','shortpixel-image-optimiser');?> <?php echo($invType[1])?>
1752
  </a><?php
1757
  <?php } ?>
1758
  </div>
1759
  </div>
1760
+ </div>
1761
+ <?php } ?>
1762
  <div class='sp-column-info'>
1763
  <?php echo($message);?>
1764
  </div> <?php
1765
  }
1766
+
1767
  public function getQuotaExceededHTML($message = '') {
1768
+ return "<div class='sp-column-actions' style='width:110px;'>
1769
  <a class='button button-smaller button-primary' href='https://shortpixel.com/login/". (defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) . "' target='_blank'>"
1770
+ . __('Extend Quota','shortpixel-image-optimiser') .
1771
  "</a>
1772
+ <a class='button button-smaller' href='admin.php?action=shortpixel_check_quota'>"
1773
  . __('Check&nbsp;&nbsp;Quota','shortpixel-image-optimiser') .
1774
  "</a></div>
1775
  <div class='sp-column-info'>" . $message . " " . __('Quota Exceeded','shortpixel-image-optimiser') . "</div>";
1776
  }
1777
+
1778
  public function outputComparerHTML() {?>
1779
+ <div class="sp-modal-shade"></div>
1780
  <div id="spUploadCompare" class="shortpixel-modal shortpixel-hide">
1781
  <div class="sp-modal-title">
1782
  <button type="button" class="sp-close-button">&times;</button>
1809
  </div>
1810
  </div>
1811
  </div>
1812
+
1813
  <?php
1814
  }
1815
  }
class/view/view-restore-all.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="wrap short-pixel-bulk-page bulk-restore-all">
3
+ <form action='<?php echo remove_query_arg('part'); ?>' method='POST' >
4
+ <h1><?php _e('Bulk Image Optimization by ShortPixel','shortpixel-image-optimiser');?></h1>
5
+
6
+ <div class="sp-notice sp-notice-info sp-floating-block sp-full-width">
7
+
8
+ <h3><?php _e( "Are you sure you want to restore from backup all the images optimized with ShortPixel?", 'shortpixel-image-optimiser' ); ?></h3>
9
+
10
+ <p><?php _e('Please read carefully. This function will: ', 'shortpixel-image-optimiser'); ?> </p>
11
+ <ol>
12
+ <li><?php _e('Remove all optimized images from media library', 'shortpixel-image-optimiser'); ?></li>
13
+ <li><?php _e( sprintf('Remove all optimized images from %s selected %s other media', '<strong>', '</strong>'), 'shortpixel-image-optimiser'); ?></li>
14
+ </ol>
15
+
16
+ <section class='select_folders'>
17
+ <h4><?php _e('Select which Custom Media Folders to restore', 'shortpixel-image-optimiser'); ?></h4>
18
+
19
+ <?php $folders = $controller->getCustomFolders();
20
+ foreach($folders as $folder):
21
+ $path = $folder->getPath();
22
+ $fileCount = $folder->getFileCount();
23
+ $folder_id = $folder->getId();
24
+ // $status = $folder->getStatus();
25
+ ?>
26
+
27
+
28
+ <label class='input'><input type='checkbox' name='selected_folders[]' value='<?php echo $folder_id ?>' checked /> <?php echo $path ?> <span class='filecount'> (<?php printf ('%s File(s)', $fileCount) ?>) </span></label>
29
+ <?php endforeach; ?>
30
+ </section>
31
+
32
+ <section class='random_check'>
33
+ <div><?php _e('To continue and agree with the warning, please check the correct value below', 'shortpixel-image-optimiser') ?>
34
+ <div class='random_answer'><?php echo $controller->randomAnswer(); ?></div>
35
+ </div>
36
+
37
+ <div class='inputs'><?php echo $controller->randomCheck(); ?></div>
38
+ </section>
39
+
40
+ <div class='form-controls'>
41
+ <a class='button' href="<?php echo remove_query_arg('part') ?>"><?php _e('Back', 'shortpixel-image-optimiser'); ?></a>
42
+ <button disabled aria-disabled="true" type='submit' class='button bulk restore disabled' name='bulkRestore' id='bulkRestore'><?php _e('Bulk Restore', 'shortpixel-image-optimiser'); ?></button>
43
+ </div>
44
+
45
+ </div> <!-- sp-notice -->
46
+ </form>
47
+ </div> <!-- wrap -->
class/wp-short-pixel.php CHANGED
@@ -1,21 +1,22 @@
1
  <?php
2
 
3
  class WPShortPixel {
4
-
5
  const BULK_EMPTY_QUEUE = 0;
6
-
7
  private $_apiInterface = null;
8
  private $_settings = null;
9
  private $prioQ = null;
10
  private $view = null;
11
-
 
12
  private $hasNextGen = false;
13
  private $spMetaDao = null;
14
-
15
  private $jsSuffix = '.min.js';
16
 
17
  private $timer;
18
-
19
  public static $PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'gif', 'png', 'pdf');
20
 
21
  public function __construct() {
@@ -26,9 +27,9 @@ class WPShortPixel {
26
  }
27
 
28
  load_plugin_textdomain('shortpixel-image-optimiser', false, plugin_basename(dirname( SHORTPIXEL_PLUGIN_FILE )).'/lang');
29
-
30
  $isAdminUser = current_user_can( 'manage_options' );
31
-
32
  $this->_settings = new WPShortPixelSettings();
33
  $this->_apiInterface = new ShortPixelAPI($this->_settings);
34
  $this->cloudflareApi = new ShortPixelCloudFlareApi($this->_settings->cloudflareEmail, $this->_settings->cloudflareAuthKey, $this->_settings->cloudflareZoneID);
@@ -36,12 +37,19 @@ class WPShortPixel {
36
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $this->_settings->excludePatterns);
37
  $this->prioQ = new ShortPixelQueue($this, $this->_settings);
38
  $this->view = new ShortPixelView($this);
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
- define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', '_tl,_tr,_br,_bl');
 
 
 
 
45
  }
46
 
47
  $this->setDefaultViewModeList();//set default mode as list. only @ first run
@@ -51,7 +59,7 @@ class WPShortPixel {
51
  add_filter( 'plugin_action_links_' . plugin_basename(SHORTPIXEL_PLUGIN_FILE), array(&$this, 'generatePluginLinks'));//for plugin settings page
52
 
53
  //add_action( 'admin_footer', array(&$this, 'handleImageProcessing'));
54
-
55
  //Media custom column
56
  add_filter( 'manage_media_columns', array( &$this, 'columns' ) );//add media library column header
57
  add_action( 'manage_media_custom_column', array( &$this, 'generateCustomColumn' ), 10, 2 );//generate the media library column
@@ -63,7 +71,7 @@ class WPShortPixel {
63
  add_action( 'add_meta_boxes', array( &$this, 'shortpixelInfoBox') );
64
  //for cleaning up the WebP images when an attachment is deleted
65
  add_action( 'delete_attachment', array( &$this, 'onDeleteImage') );
66
-
67
  //for NextGen
68
  if($this->_settings->hasCustomFolders) {
69
  add_filter( 'ngg_manage_images_columns', array( &$this, 'nggColumns' ) );
@@ -73,7 +81,7 @@ class WPShortPixel {
73
  // hook on the NextGen gallery list update
74
  add_action('ngg_update_addgallery_page', array( &$this, 'addNextGenGalleriesToCustom'));
75
  }
76
-
77
  // integration with WP/LR Sync plugin
78
  add_action( 'wplr_update_media', array( &$this, 'onWpLrUpdateMedia' ), 10, 2);
79
 
@@ -88,14 +96,14 @@ class WPShortPixel {
88
  //add settings page
89
  add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );//display SP in Settings menu
90
  add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
91
-
92
  add_action('wp_ajax_shortpixel_browse_content', array(&$this, 'browseContent'));
93
  add_action('wp_ajax_shortpixel_get_backup_size', array(&$this, 'getBackupSize'));
94
  add_action('wp_ajax_shortpixel_get_comparer_data', array(&$this, 'getComparerData'));
95
 
96
  add_action('wp_ajax_shortpixel_new_api_key', array(&$this, 'newApiKey'));
97
  add_action('wp_ajax_shortpixel_propose_upgrade', array(&$this, 'proposeUpgrade'));
98
-
99
  add_action( 'delete_attachment', array( &$this, 'handleDeleteAttachmentInBackup' ) );
100
  add_action( 'load-upload.php', array( &$this, 'handleCustomBulk'));
101
 
@@ -107,7 +115,7 @@ class WPShortPixel {
107
  add_action('wp_ajax_shortpixel_optimize_thumbs', array(&$this, 'handleOptimizeThumbs'));
108
 
109
  //toolbar notifications
110
- add_action( 'admin_bar_menu', array( &$this, 'toolbar_shortpixel_processing'), 999 );
111
  //deactivate plugin
112
  add_action( 'admin_post_shortpixel_deactivate_plugin', array(&$this, 'deactivatePlugin'));
113
  //only if the key is not yet valid or the user hasn't bought any credits.
@@ -118,7 +126,7 @@ class WPShortPixel {
118
  new ShortPixelFeedback( SHORTPIXEL_PLUGIN_FILE, 'shortpixel-image-optimiser', $this->_settings->apiKey, $this);
119
  }
120
  }
121
-
122
  //automatic optimization
123
  add_action( 'wp_ajax_shortpixel_image_processing', array( &$this, 'handleImageProcessing') );
124
  //manual optimization
@@ -132,10 +140,11 @@ class WPShortPixel {
132
  add_action('wp_ajax_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
133
  add_action('admin_action_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
134
  //This adds the constants used in PHP to be available also in JS
135
- add_action( 'admin_footer', array( &$this, 'shortPixelJS') );
136
- add_action( 'admin_head', array( &$this, 'headCSS') );
137
 
138
- if($this->_settings->frontBootstrap) {
 
139
  //also need to have it in the front footer then
140
  add_action( 'wp_footer', array( &$this, 'shortPixelJS') );
141
  //need to add the nopriv action for when items exist in the queue and no user is logged in
@@ -143,14 +152,16 @@ class WPShortPixel {
143
  }
144
  //register a method to display admin notices if necessary
145
  add_action('admin_notices', array( &$this, 'displayAdminNotices'));
146
-
147
  $this->migrateBackupFolder();
148
 
 
149
  if(!$this->_settings->redirectedSettings && !$this->_settings->verifiedKey && (!function_exists("is_multisite") || !is_multisite())) {
150
  $this->_settings->redirectedSettings = 1;
151
  wp_redirect(admin_url("options-general.php?page=wp-shortpixel"));
152
  exit();
153
  }
 
154
  }
155
 
156
  //handling older
@@ -170,13 +181,13 @@ class WPShortPixel {
170
  /*translators: title and menu name for the Bulk Processing page*/
171
  add_media_page( __('ShortPixel Bulk Process','shortpixel-image-optimiser'), __('Bulk ShortPixel','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcess' ) );
172
  }
173
-
174
  public static function shortPixelActivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
175
  {
176
  self::shortPixelDeactivatePlugin();
177
  if(SHORTPIXEL_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
178
  WPShortPixelSettings::debugResetOptions();
179
-
180
  $settings = new WPShortPixelSettings();
181
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $settings->excludePatterns);
182
  $spMetaDao->dropTables();
@@ -186,7 +197,7 @@ class WPShortPixel {
186
  }
187
  WPShortPixelSettings::onActivate();
188
  }
189
-
190
  public static function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
191
  {
192
  ShortPixelQueue::resetBulk();
@@ -319,7 +330,7 @@ class WPShortPixel {
319
  }
320
  return $found;
321
  }
322
-
323
  public function displayAdminNotices() {
324
  if(!ShortPixelQueue::testQ()) {
325
  ShortPixelView::displayActivationNotice('fileperms');
@@ -329,7 +340,7 @@ class WPShortPixel {
329
  }
330
  $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
331
  $this->_settings->dismissedNotices = $dismissed;
332
-
333
  if(!$this->_settings->verifiedKey) {
334
  $now = time();
335
  $act = $this->_settings->activationDate ? $this->_settings->activationDate : $now;
@@ -366,11 +377,11 @@ class WPShortPixel {
366
  $screen = get_current_screen();
367
  $stats = $this->countAllIfNeeded($this->_settings->currentStats, 86400);
368
  $quotaData = $stats;
369
-
370
  //this is for bulk page - alert on the total credits for total images
371
  if( !isset($dismissed['upgbulk']) && $screen && $screen->id == 'media_page_wp-short-pixel-bulk' && $this->bulkUpgradeNeeded($stats)) {
372
  //looks like the user hasn't got enough credits to bulk process all media library
373
- ShortPixelView::displayActivationNotice('upgbulk', array('filesTodo' => $stats['totalFiles'] - $stats['totalProcessedFiles'],
374
  'quotaAvailable' => max(0, $quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'])));
375
  }
376
  //consider the monthly plus 1/6 of the available one-time credits.
@@ -380,7 +391,7 @@ class WPShortPixel {
380
  }
381
  }
382
  }
383
-
384
  public function dismissAdminNotice() {
385
  $noticeId = preg_replace('|[^a-z0-9]|i', '', $_GET['notice_id']);
386
  $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
@@ -390,13 +401,13 @@ class WPShortPixel {
390
  $this->_settings->optimizeUnlisted = 1;
391
  }
392
  die(json_encode(array("Status" => 'success', "Message" => 'Notice ID: ' . $noticeId . ' dismissed')));
393
- }
394
 
395
  public function dismissMediaAlert() {
396
  $this->_settings->mediaAlert = 1;
397
  die(json_encode(array("Status" => 'success', "Message" => __('Media alert dismissed','shortpixel-image-optimiser'))));
398
- }
399
-
400
  protected function getMonthAvg($stats) {
401
  for($i = 4, $count = 0; $i>=1; $i--) {
402
  if($count == 0 && $stats['totalM' . $i] == 0) continue;
@@ -404,7 +415,7 @@ class WPShortPixel {
404
  }
405
  return ($stats['totalM1'] + $stats['totalM2'] + $stats['totalM3'] + $stats['totalM4']) / max(1,$count);
406
  }
407
-
408
  protected function monthlyUpgradeNeeded($quotaData) {
409
  return isset($quotaData['APICallsQuotaNumeric']) && $this->getMonthAvg($quotaData) > $quotaData['APICallsQuotaNumeric'] + ($quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'])/6 + 20;
410
  }
@@ -415,9 +426,9 @@ class WPShortPixel {
415
  }
416
 
417
  //set default move as "list". only set once, it won't try to set the default mode again.
418
- public function setDefaultViewModeList()
419
  {
420
- if($this->_settings->mediaLibraryViewMode === false)
421
  {
422
  $this->_settings->mediaLibraryViewMode = 1;
423
  $currentUserID = false;
@@ -427,21 +438,21 @@ class WPShortPixel {
427
  update_user_meta($currentUserID, "wp_media_library_mode", "list");
428
  }
429
  }
430
-
431
  }
432
 
433
- static function log($message) {
434
- if (SHORTPIXEL_DEBUG === true) {
435
  if (is_array($message) || is_object($message)) {
436
- self::doLog(print_r($message, true));
437
  } else {
438
- self::doLog($message);
439
  }
440
  }
441
  }
442
-
443
- static protected function doLog($message) {
444
- if(defined('SHORTPIXEL_DEBUG_TARGET')) {
445
  file_put_contents(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log", '[' . date('Y-m-d H:i:s') . "] $message\n", FILE_APPEND);
446
  } else {
447
  error_log($message);
@@ -451,13 +462,14 @@ class WPShortPixel {
451
  function headCSS() {
452
  echo('<style>.shortpixel-hide {display:none;}</style>');
453
  }
454
-
455
- function shortPixelJS() {
456
  //require_once(ABSPATH . 'wp-admin/includes/screen.php');
457
  if(function_exists('get_current_screen')) {
458
  $screen = get_current_screen();
459
- if(is_object($screen)) {
460
- if( in_array($screen->id, array('attachment', 'upload'))) {
 
461
  //output the comparer html
462
  $this->view->outputComparerHTML();
463
  //render a template of the list cell to be used by the JS
@@ -468,6 +480,11 @@ class WPShortPixel {
468
  wp_enqueue_style('short-pixel-bar.min.css', plugins_url('/res/css/short-pixel-bar.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
469
  if( in_array($screen->id, array('attachment', 'upload', 'settings_page_wp-shortpixel', 'media_page_wp-short-pixel-bulk', 'media_page_wp-short-pixel-custom'))) {
470
  wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
 
 
 
 
 
471
  }
472
  }
473
  }
@@ -541,14 +558,14 @@ class WPShortPixel {
541
  wp_localize_script( 'shortpixel' . $this->jsSuffix, '_spTr', $jsTranslation );
542
  wp_localize_script( 'shortpixel' . $this->jsSuffix, 'ShortPixelConstants', $ShortPixelConstants );
543
  wp_enqueue_script('shortpixel' . $this->jsSuffix);
544
-
545
  wp_enqueue_script('jquery.knob.min.js', plugins_url('/res/js/jquery.knob.min.js',SHORTPIXEL_PLUGIN_FILE) );
546
  wp_enqueue_script('jquery.tooltip.min.js', plugins_url('/res/js/jquery.tooltip.min.js',SHORTPIXEL_PLUGIN_FILE) );
547
  wp_enqueue_script('punycode.min.js', plugins_url('/res/js/punycode.min.js',SHORTPIXEL_PLUGIN_FILE) );
548
  }
549
 
550
  function toolbar_shortpixel_processing( $wp_admin_bar ) {
551
-
552
  $extraClasses = " shortpixel-hide";
553
  /*translators: toolbar icon tooltip*/
554
  $id = 'short-pixel-notice-toolbar';
@@ -579,7 +596,7 @@ class WPShortPixel {
579
 
580
  $args = array(
581
  'id' => 'shortpixel_processing',
582
- 'title' => '<div id="' . $id . '" title="' . $tooltip . '" ><img src="'
583
  . plugins_url( 'res/img/'.$icon, SHORTPIXEL_PLUGIN_FILE ) . '" success-url="' . $successLink . '"><span class="shp-alert">!</span>'
584
  .'<div class="cssload-container"><div class="cssload-speeding-wheel"></div></div></div>',
585
  'href' => $link,
@@ -623,7 +640,7 @@ class WPShortPixel {
623
  foreach( $mediaIds as $ID ) {
624
  $meta = wp_get_attachment_metadata($ID);
625
  if( ( !isset($meta['ShortPixel']) //never touched by ShortPixel
626
- || (isset($meta['ShortPixel']['WaitingProcessing']) && $meta['ShortPixel']['WaitingProcessing'] == true))
627
  && (!isset($meta['ShortPixelImprovement']) || $meta['ShortPixelImprovement'] == __('Optimization N/A','shortpixel-image-optimiser'))) {
628
  $this->prioQ->push($ID);
629
  if(!isset($meta['ShortPixel'])) {
@@ -632,6 +649,7 @@ class WPShortPixel {
632
  $meta['ShortPixel']['WaitingProcessing'] = true;
633
  //wp_update_attachment_metadata($ID, $meta);
634
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
 
635
  }
636
  }
637
  break;
@@ -687,11 +705,10 @@ class WPShortPixel {
687
  return $meta;
688
  }
689
 
690
- $t = get_transient("wp-short-pixel-regenerating");
691
- if(is_array($t) && isset($t[$ID])) {
692
  return $meta;
693
  }
694
-
695
  self::log("Handle Media Library Image Upload #{$ID}");
696
  //self::log("STACK: " . json_encode(debug_backtrace()));
697
 
@@ -711,7 +728,7 @@ class WPShortPixel {
711
  $meta['ShortPixelImprovement'] = __('Optimization N/A', 'shortpixel-image-optimiser');
712
  return $meta;
713
  }
714
- else
715
  {//the kind of file we can process. goody.
716
 
717
  $this->prioQ->push($ID);
@@ -734,7 +751,7 @@ class WPShortPixel {
734
  //self::log("IMG: sent: " . json_encode($URLsAndPATHs));
735
  }
736
  $meta['ShortPixel']['WaitingProcessing'] = true;
737
-
738
  // check if the image was converted from PNG upon uploading.
739
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {//for the moment
740
  $imagePath = $itemHandler->getMeta()->getPath();
@@ -743,27 +760,38 @@ class WPShortPixel {
743
  $params = $conv[$imagePath];
744
  unset($conv[$imagePath]);
745
  $this->_settings->convertedPng2Jpg == $conv;
746
- $meta['ShortPixelPng2Jpg'] = array('originalFile' => $params['pngFile'], 'originalSizes' => array(),
747
  'backup' => $params['backup'], 'optimizationPercent' => $params['optimizationPercent']);
748
  }
749
  }
750
-
751
  return $meta;
752
- }
753
  }//end handleMediaLibraryImageUpload
754
 
755
  /**
756
  * if the image was optimized in the last hour, send a request to delete from picQueue
757
  * @param $itemHandler
758
  * @param bool $urlsAndPaths
 
759
  */
760
  public function maybeDumpFromProcessedOnServer($itemHandler, $urlsAndPaths) {
761
  $meta = $itemHandler->getMeta();
762
 
763
- //die(var_dump($itemHandler->getURLsAndPATHs(true, false, true, array())));
 
 
 
 
 
 
 
 
 
764
 
765
- if(time() - strtotime($meta->getTsOptimized()) < 3600) {
766
- $this->_apiInterface->doDumpRequests($urlsAndPaths["URLs"]);
 
767
  }
768
  }
769
 
@@ -877,12 +905,30 @@ class WPShortPixel {
877
  }
878
  return $meta;
879
  }
880
-
881
  public function optimizeCustomImage($id) {
882
- $meta = $this->spMetaDao->getMeta($id);
883
- if($meta->getStatus() != 2) {
884
- $meta->setStatus(1);
 
 
 
 
 
 
 
 
 
885
  $meta->setRetries(0);
 
 
 
 
 
 
 
 
 
886
  $this->spMetaDao->update($meta);
887
  $this->prioQ->push('C-' . $id);
888
  }
@@ -890,34 +936,49 @@ class WPShortPixel {
890
 
891
  public function bulkRestore(){
892
  global $wpdb;
893
-
894
  $startQueryID = $crtStartQueryID = $this->prioQ->getStartBulkId();
895
- $endQueryID = $this->prioQ->getStopBulkId();
896
 
897
  if ( $startQueryID <= $endQueryID ) {
898
  return false;
899
  }
900
-
901
  $this->prioQ->resetPrio();
902
 
903
- $startTime = time();
904
  $maxTime = min(30, (is_numeric(SHORTPIXEL_MAX_EXECUTION_TIME) && SHORTPIXEL_MAX_EXECUTION_TIME > 10 ? SHORTPIXEL_MAX_EXECUTION_TIME - 5 : 25));
905
  $maxResults = SHORTPIXEL_MAX_RESULTS_QUERY * 2;
906
  if(in_array($this->prioQ->getBulkType(), array(ShortPixelQueue::BULK_TYPE_CLEANUP, ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING))) {
907
  $maxResults *= 20;
908
  }
909
  $restored = array();
910
-
911
  //$ind = 0;
912
  while( $crtStartQueryID >= $endQueryID && time() - $startTime < $maxTime) {
913
  //if($ind > 1) break;
914
  //$ind++;
 
 
 
915
  $resultsPostMeta = WpShortPixelMediaLbraryAdapter::getPostMetaSlice($crtStartQueryID, $endQueryID, $maxResults);
916
  if ( empty($resultsPostMeta) ) {
917
- $crtStartQueryID -= $maxResults;
918
- $startQueryID = $crtStartQueryID;
919
- $this->prioQ->setStartBulkId($startQueryID);
920
- continue;
 
 
 
 
 
 
 
 
 
 
 
 
921
  }
922
 
923
  foreach ( $resultsPostMeta as $itemMetaData ) {
@@ -928,7 +989,7 @@ class WPShortPixel {
928
  if($meta->getStatus() == 2 || $meta->getStatus() == 1) {
929
  if($meta->getStatus() == 2 && $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE) {
930
  $res = $this->doRestore($crtStartQueryID); //this is restore, the real
931
- } else {
932
  //this is only meta cleanup, no files are replaced (BACKUP REMAINS IN PLACE TOO)
933
  $item->cleanupMeta($this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING);
934
  $res = true;
@@ -938,33 +999,35 @@ class WPShortPixel {
938
  if($meta->getStatus() < 0) {//also cleanup errors either for restore or cleanup
939
  $item->cleanupMeta();
940
  }
941
- }
 
 
942
  }
943
- $this->advanceBulk($crtStartQueryID);
944
  return $restored;
945
  }
946
-
947
  //TODO muta in bulkProvider
948
  public function getBulkItemsFromDb(){
949
  global $wpdb;
950
-
951
  $startQueryID = $this->prioQ->getStartBulkId();
952
- $endQueryID = $this->prioQ->getStopBulkId();
953
  $skippedAlreadyProcessed = 0;
954
-
955
  if ( $startQueryID <= $endQueryID ) {
956
  return false;
957
  }
958
  $idList = array();
959
  $itemList = array();
960
- for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
961
  ($crtStartQueryID >= $endQueryID) && (count($itemList) < SHORTPIXEL_PRESEND_ITEMS) && ($sanityCheck < 150)
962
  && (SHORTPIXEL_MAX_EXECUTION_TIME < 10 || time() - $this->timer < SHORTPIXEL_MAX_EXECUTION_TIME - 5); $sanityCheck++) {
963
-
964
  self::log("GETDB: current StartID: " . $crtStartQueryID);
965
 
966
- /* $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta
967
- WHERE ( post_id <= $crtStartQueryID AND post_id >= $endQueryID )
968
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
969
  ORDER BY post_id DESC
970
  LIMIT " . SHORTPIXEL_MAX_RESULTS_QUERY;
@@ -987,7 +1050,7 @@ class WPShortPixel {
987
  if(!in_array($crtStartQueryID, $idList) && $this->isProcessable($crtStartQueryID, ($this->_settings->optimizePdfs ? array() : array('pdf')))) {
988
  $item = new ShortPixelMetaFacade($crtStartQueryID);
989
  $meta = $item->getMeta();//wp_get_attachment_metadata($crtStartQueryID);
990
-
991
  if($meta->getStatus() != 2) {
992
  $addIt = (strpos($meta->getMessage(), __('Image files are missing.', 'shortpixel-image-optimiser')) === false);
993
 
@@ -1013,7 +1076,7 @@ class WPShortPixel {
1013
  } else {
1014
  $skippedAlreadyProcessed++;
1015
  }
1016
- }
1017
  elseif( $this->_settings->processThumbnails && $meta->getThumbsOpt() !== null
1018
  && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0
1019
  || $meta->getThumbsOpt() < WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes) && is_array($meta->getThumbsOptList()))) { //thumbs were chosen in settings
@@ -1036,7 +1099,7 @@ class WPShortPixel {
1036
  $leapStart = $this->prioQ->getStartBulkId();
1037
  $crtStartQueryID = $startQueryID = $itemMetaData->post_id - 1; //decrement it so we don't select it again
1038
  $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->_settings, $leapStart, $crtStartQueryID);
1039
- $skippedAlreadyProcessed += $res["mainProcessedFiles"] - $res["mainProc".($this->getCompressionType() == 1 ? "Lossy" : "Lossless")."Files"];
1040
  self::log("GETDB: empty list. setStartBulkID to $startQueryID");
1041
  $this->prioQ->setStartBulkId($startQueryID);
1042
  } else {
@@ -1059,7 +1122,7 @@ class WPShortPixel {
1059
  }
1060
  return $items;
1061
  }
1062
-
1063
  private function checkKey($ID) {
1064
  if( $this->_settings->verifiedKey == false) {
1065
  if($ID == null){
@@ -1069,23 +1132,27 @@ class WPShortPixel {
1069
  $response = array("Status" => ShortPixelAPI::STATUS_NO_KEY, "ImageID" => $itemHandler ? $itemHandler->getId() : "-1", "Message" => __('Missing API Key','shortpixel-image-optimiser'));
1070
  $this->_settings->bulkLastStatus = $response;
1071
  die(json_encode($response));
1072
- }
1073
  }
1074
-
1075
  private function sendEmptyQueue() {
1076
  $avg = $this->getAverageCompression();
1077
  $fileCount = $this->_settings->fileCount;
1078
- $response = array("Status" => self::BULK_EMPTY_QUEUE,
1079
  /* translators: console message Empty queue 1234 -> 1234 */
1080
  "Message" => __('Empty queue ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
1081
- "BulkStatus" => ($this->prioQ->bulkRunning()
1082
  ? "1" : ($this->prioQ->bulkPaused() ? "2" : "0")),
1083
  "AverageCompression" => $avg,
1084
  "FileCount" => $fileCount,
1085
  "BulkPercent" => $this->prioQ->getBulkPercent());
1086
- die(json_encode($response));
1087
  }
1088
 
 
 
 
 
1089
  public function handleImageProcessing($ID = null) {
1090
  //if(rand(1,2) == 2) {
1091
  // header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
@@ -1093,18 +1160,18 @@ class WPShortPixel {
1093
  //}
1094
  //0: check key
1095
  $this->checkKey($ID);
1096
-
1097
  if($this->_settings->frontBootstrap && is_admin() && !ShortPixelTools::requestIsFrontendAjax()) {
1098
  //if in backend, and front-end is activated, mark processing from backend to shut off the front-end for 10 min.
1099
  $this->_settings->lastBackAction = time();
1100
  }
1101
-
1102
  $rawPrioQ = $this->prioQ->get();
1103
  if(count($rawPrioQ)) { self::log("HIP: 0 Priority Queue: ".json_encode($rawPrioQ)); }
1104
  self::log("HIP: 0 Bulk running? " . $this->prioQ->bulkRunning() . " START " . $this->_settings->startBulkId . " STOP " . $this->_settings->stopBulkId);
1105
-
1106
  //handle the bulk restore and cleanup first - these are fast operations taking precedece over optimization
1107
- if( $this->prioQ->bulkRunning()
1108
  && ( $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE
1109
  || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP
1110
  || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING)) {
@@ -1112,14 +1179,14 @@ class WPShortPixel {
1112
  if($res === false) {
1113
  $this->sendEmptyQueue();
1114
  } else {
1115
- die(json_encode(array("Status" => ShortPixelAPI::STATUS_RETRY,
1116
  "Message" => __('Restoring images... ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
1117
  "BulkPercent" => $this->prioQ->getBulkPercent(),
1118
  "Restored" => $res )));
1119
  }
1120
-
1121
  }
1122
-
1123
  //1: get 3 ids to process. Take them with priority from the queue
1124
  $ids = $this->getFromPrioAndCheck(SHORTPIXEL_PRESEND_ITEMS);
1125
  if(count($ids) < SHORTPIXEL_PRESEND_ITEMS ) { //take from bulk if bulk processing active
@@ -1145,21 +1212,22 @@ class WPShortPixel {
1145
  $customIds = false;
1146
  if(count($ids) < SHORTPIXEL_PRESEND_ITEMS && $this->prioQ->bulkRan() && $this->_settings->hasCustomFolders
1147
  && (!$this->_settings->cancelPointer || $this->_settings->skipToCustom)
1148
- && !$this->_settings->customBulkPaused)
1149
  { //take from custom images if any left to optimize - only if bulk was ever started
1150
  //but first refresh if it wasn't refreshed in the last hour
1151
  if(time() - $this->_settings->hasCustomFolders > 3600) {
1152
  $notice = null; $this->refreshCustomFolders($notice);
1153
  $this->_settings->hasCustomFolders = time();
1154
  }
1155
- $customIds = $this->spMetaDao->getPendingMetas( 3 - count($ids));
 
1156
  if(is_array($customIds)) {
1157
  $ids = array_merge($ids, array_map(array('ShortPixelMetaFacade', 'getNewFromRow'), $customIds));
1158
  }
1159
  }
1160
  //var_dump($ids);
1161
  //die("za stop 2");
1162
-
1163
  //self::log("HIP: 1 Ids: ".json_encode($ids));
1164
  if(count($ids)) {$idl='';foreach($ids as $i){$idl.=$i->getId().' ';} self::log("HIP: 1 Selected IDs: $idl");}
1165
 
@@ -1167,8 +1235,9 @@ class WPShortPixel {
1167
  for($i = 0, $itemHandler = false; $ids !== false && $i < min(SHORTPIXEL_PRESEND_ITEMS, count($ids)); $i++) {
1168
  $crtItemHandler = $ids[$i];
1169
  $tmpMeta = $crtItemHandler->getMeta();
 
1170
  $compType = ($tmpMeta->getCompressionType() !== null ? $tmpMeta->getCompressionType() : $this->_settings->compressionType);
1171
- try {
1172
  self::log("HIP: 1 sendToProcessing: ".$crtItemHandler->getId());
1173
  $URLsAndPATHs = $this->sendToProcessing($crtItemHandler, $compType, $tmpMeta->getThumbsTodo());
1174
  //self::log("HIP: 1 METADATA: ".json_encode($crtItemHandler->getRawMeta()));
@@ -1191,7 +1260,7 @@ class WPShortPixel {
1191
  if (!$itemHandler){
1192
  //if searching, than the script is searching for not processed items and found none yet, should be relaunced
1193
  if(isset($res['searching']) && $res['searching']) {
1194
- die(json_encode(array("Status" => ShortPixelAPI::STATUS_RETRY,
1195
  "Message" => __('Searching images to optimize... ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId() )));
1196
  }
1197
  //in this case the queue is really empty
@@ -1225,25 +1294,33 @@ class WPShortPixel {
1225
  $result["ThumbsCount"] = $meta->getThumbsOpt()
1226
  ? $meta->getThumbsOpt() //below is the fallback for old optimized images that don't have thumbsOpt
1227
  : ($this->_settings->processThumbnails ? $result["ThumbsTotal"] : 0);
1228
-
1229
  $result["RetinasCount"] = $meta->getRetinasOpt();
1230
  $result["BackupEnabled"] = ($this->getBackupFolderAny($meta->getPath(), $meta->getThumbs()) ? true : false);//$this->_settings->backupImages;
1231
-
 
 
 
 
 
 
 
 
1232
  if(!$prio && $itemId <= $this->prioQ->getStartBulkId()) {
1233
  $this->advanceBulk($itemId);
1234
  $this->setBulkInfo($itemId, $result);
1235
  }
1236
 
1237
  $result["AverageCompression"] = $this->getAverageCompression();
1238
-
1239
- if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
1240
-
1241
  $thumb = $bkThumb = "";
1242
  //$percent = 0;
1243
  $percent = $meta->getImprovementPercent();
1244
  if($percent){
1245
  $filePath = explode("/", $meta->getPath());
1246
-
1247
  //Get a suitable thumb
1248
  $sizes = $meta->getThumbs();
1249
  if('pdf' == strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))) {
@@ -1350,7 +1427,7 @@ class WPShortPixel {
1350
  $prio = $this->prioQ->addToFailed($itemHandler->getQueuedId());
1351
  }
1352
  self::log("HIP RES: skipping $itemId");
1353
- $this->advanceBulk($meta->getId());
1354
  if($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
1355
  $result["CustomImageLink"] = ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath();
1356
  }
@@ -1376,23 +1453,23 @@ class WPShortPixel {
1376
  elseif($result["Status"] == ShortPixelAPI::STATUS_RETRY && is_array($customIds)) {
1377
  $result["CustomImageLink"] = $thumb = ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath();
1378
  }
1379
-
1380
  if($result["Status"] !== ShortPixelAPI::STATUS_RETRY) {
1381
  $this->_settings->bulkLastStatus = $result;
1382
  }
1383
  die(json_encode($result));
1384
  }
1385
-
1386
-
1387
  private function advanceBulk($processedID) {
1388
  if($processedID <= $this->prioQ->getStartBulkId()) {
1389
  $this->prioQ->setStartBulkId($processedID - 1);
1390
  $this->prioQ->logBulkProgress();
1391
  }
1392
  }
1393
-
1394
  private function setBulkInfo($processedID, &$result) {
1395
- $deltaBulkPercent = $this->prioQ->getDeltaBulkPercent();
1396
  $minutesRemaining = $this->prioQ->getTimeRemaining();
1397
  $pendingMeta = $this->_settings->hasCustomFolders ? $this->spMetaDao->getPendingMetaCount() : 0;
1398
  $percent = $this->prioQ->getBulkPercent();
@@ -1406,18 +1483,18 @@ class WPShortPixel {
1406
  $result["BulkPercent"] = $percent;
1407
  $result["BulkMsg"] = $this->bulkProgressMessage($deltaBulkPercent, $minutesRemaining);
1408
  }
1409
-
1410
  private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false) {
1411
 
1412
  //conversion of PNG 2 JPG for existing images
1413
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) { //currently only for ML
1414
  $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
1415
-
1416
  if(isset($rawMeta['type']) && $rawMeta['type'] == 'image/jpeg') {
1417
  $itemHandler->getMeta(true);
1418
  }
1419
  }
1420
-
1421
  //WpShortPixelMediaLbraryAdapter::cleanupFoundThumbs($itemHandler);
1422
  $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
1423
 
@@ -1426,7 +1503,7 @@ class WPShortPixel {
1426
  if( $itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE
1427
  && $this->_settings->optimizeUnlisted) {
1428
  $mainFile = $meta->getPath();
1429
-
1430
  $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($mainFile);
1431
  //first identify which thumbs are not in the sizes
1432
  $sizes = $meta->getThumbs();
@@ -1460,29 +1537,29 @@ class WPShortPixel {
1460
  );
1461
  $ind++;
1462
  }
1463
- }
1464
  if($ind > $start) { // at least one thumbnail added, update
1465
  $meta->setThumbs($sizes);
1466
  $itemHandler->updateMeta($meta);
1467
  $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
1468
  }
1469
  }
1470
-
1471
  //find any missing thumbs files and mark them as such
1472
  $miss = $meta->getThumbsMissing();
1473
  /* TODO remove */if(is_numeric($miss)) $miss = array();
1474
- if( isset($URLsAndPATHs['sizesMissing']) && count($URLsAndPATHs['sizesMissing'])
1475
  && (null === $miss || count(array_diff_key($miss, array_merge($URLsAndPATHs['sizesMissing'], $miss))))) {
1476
  //fix missing thumbs in the metadata before sending to processing
1477
  $meta->setThumbsMissing($URLsAndPATHs['sizesMissing']);
1478
- $itemHandler->updateMeta();
1479
  }
1480
  //die(var_dump($itemHandler));
1481
  $refresh = $meta->getStatus() === ShortPixelAPI::ERR_INCORRECT_FILE_SIZE;
1482
  //echo("URLS: "); die(var_dump($URLsAndPATHs));
1483
- $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $itemHandler,
1484
- $compressionType === false ? $this->_settings->compressionType : $compressionType, $refresh);//send a request, do NOT wait for response
1485
  $itemHandler->setWaitingProcessing();
 
 
1486
  //$meta = wp_get_attachment_metadata($ID);
1487
  //$meta['ShortPixel']['WaitingProcessing'] = true;
1488
  //wp_update_attachment_metadata($ID, $meta);
@@ -1492,9 +1569,9 @@ class WPShortPixel {
1492
  public function handleManualOptimization() {
1493
  $imageId = $_GET['image_id'];
1494
  $cleanup = $_GET['cleanup'];
1495
-
1496
  self::log("Handle Manual Optimization #{$imageId}");
1497
-
1498
  switch(substr($imageId, 0, 2)) {
1499
  case "N-":
1500
  return "Add the gallery to the custom folders list in ShortPixel settings.";
@@ -1511,12 +1588,12 @@ class WPShortPixel {
1511
  break;
1512
  case "C-":
1513
  throw new Exception("HandleManualOptimization for custom images not implemented");
1514
- default:
1515
  $this->optimizeNowHook(intval($imageId), true);
1516
  break;
1517
  }
1518
  //do_action('shortpixel-optimize-now', $imageId);
1519
-
1520
  }
1521
 
1522
  public function checkStatus() {
@@ -1556,18 +1633,19 @@ class WPShortPixel {
1556
  * @param $postId
1557
  */
1558
  public function thumbnailsBeforeRegenerateHook($postId) {
1559
- $t = get_transient("wp-short-pixel-regenerating");
1560
- if($t === false) $t = array();
1561
- $t[$postId] = true;
1562
- set_transient("wp-short-pixel-regenerating" . $t, true, 30);
1563
  }
1564
 
 
1565
  /**
1566
  * to be called by thumbnail regeneration plugins when regenerating the thumbnails for an image
1567
  * @param $postId - the postId of the image
1568
  * @param $originalMeta - the metadata before the regeneration
1569
  * @param array $regeneratedSizes - the list of the regenerated thumbnails - if empty then all were regenerated.
1570
  * @param bool $bulk - true if the regeneration is done in bulk - in this case the image will not be immediately scheduled for processing but the user will need to launch the ShortPixel bulk after regenerating.
 
 
 
1571
  */
1572
  public function thumbnailsRegeneratedHook($postId, $originalMeta, $regeneratedSizes = array(), $bulk = false) {
1573
 
@@ -1583,10 +1661,11 @@ class WPShortPixel {
1583
  foreach($regeneratedSizes as $size) {
1584
  if(isset($size['file']) && in_array($size['file'], $shortPixelMeta["thumbsOptList"] )) {
1585
  $regeneratedThumbs[] = $size['file'];
1586
- $shortPixelMeta["thumbsOpt"] = max(0, $shortPixelMeta["thumbsOpt"] - 1);
1587
  $shortPixelMeta["retinasOpt"] = max(0, $shortPixelMeta["retinasOpt"] - 1);
1588
  }
1589
  }
 
1590
  $shortPixelMeta["thumbsOptList"] = array_diff($shortPixelMeta["thumbsOptList"], $regeneratedThumbs);
1591
  }
1592
  $meta = wp_get_attachment_metadata($postId);
@@ -1597,16 +1676,12 @@ class WPShortPixel {
1597
  }
1598
  //wp_update_attachment_metadata($postId, $meta);
1599
  update_post_meta($postId, '_wp_attachment_metadata', $meta);
1600
- $t = get_transient("wp-short-pixel-regenerating");
1601
- if(is_array($t) && isset($t[$postId])) {
1602
- unset($t[$postId]);
1603
- set_transient("wp-short-pixel-regenerating" . $t, true, 30);
1604
- }
1605
 
1606
  if(!$bulk) {
1607
  $this->prioQ->push($postId);
1608
  }
1609
  }
 
1610
  }
1611
 
1612
  public function shortpixelGetBackupFilter($imagePath) {
@@ -1624,10 +1699,11 @@ class WPShortPixel {
1624
  $this->prioQ->push($imageId);
1625
  //wp_update_attachment_metadata($imageId, $meta);
1626
  update_post_meta($imageId, '_wp_attachment_metadata', $meta);
 
1627
  }
1628
  }
1629
-
1630
-
1631
  //save error in file's meta data
1632
  public function handleError($ID, $result)
1633
  {
@@ -1645,7 +1721,7 @@ class WPShortPixel {
1645
  //another chance at glory, maybe cleanup was too much? (we tried first the cleaned up version for historical reason, don't disturb the sleeping dragon, right? :))
1646
  return $this->getBackupFolderInternal($file);
1647
  }
1648
-
1649
  private function getBackupFolderInternal($file) {
1650
  $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
1651
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
@@ -1668,7 +1744,7 @@ class WPShortPixel {
1668
  }
1669
  return SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir;
1670
  }
1671
-
1672
  public function getBackupFolderAny($file, $thumbs) {
1673
  $ret = $this->getBackupFolder($file);
1674
  //if(!$ret && !file_exists($file) && isset($thumbs)) {
@@ -1727,7 +1803,7 @@ class WPShortPixel {
1727
 
1728
  $pathInfo = pathinfo($file);
1729
  $sizes = isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array();
1730
-
1731
  //check if the images were converted from PNG
1732
  $png2jpgMain = isset($rawMeta['ShortPixelPng2Jpg']['originalFile']) ? $rawMeta['ShortPixelPng2Jpg']['originalFile'] : false;
1733
  $bkFolder = $this->getBackupFolderAny($file, $sizes);
@@ -1893,11 +1969,11 @@ class WPShortPixel {
1893
  }
1894
  return false;
1895
  }
1896
-
1897
  protected function renameWithRetina($bkFile, $file) {
1898
  @rename($bkFile, $file);
1899
  @rename($this->retinaName($bkFile), $this->retinaName($file));
1900
-
1901
  }
1902
 
1903
  protected function retinaName($file) {
@@ -1906,25 +1982,64 @@ class WPShortPixel {
1906
  }
1907
 
1908
  public function doCustomRestore($ID) {
1909
- $meta = $this->spMetaDao->getMeta($ID);
1910
- if(!$meta || $meta->getStatus() != 2) return false;
1911
-
 
 
 
 
 
 
 
 
 
 
 
 
 
1912
  $file = $meta->getPath();
1913
  $fullSubDir = str_replace(get_home_path(), "", dirname($file)) . '/';
1914
- $bkFile = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . ShortPixelAPI::MB_basename($file);
1915
 
1916
  if(file_exists($bkFile)) {
1917
- @rename($bkFile, $file);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1918
  $meta->setStatus(3);
1919
  $this->spMetaDao->update($meta);
 
 
1920
  }
1921
-
 
 
 
1922
  return $meta;
1923
  }
1924
-
1925
  public function handleRestoreBackup() {
1926
  $attachmentID = intval($_GET['attachment_ID']);
1927
-
1928
  self::log("Handle Restore Backup #{$attachmentID}");
1929
  $this->doRestore($attachmentID);
1930
 
@@ -1936,13 +2051,13 @@ class WPShortPixel {
1936
  wp_redirect($sendback);
1937
  // we are done
1938
  }
1939
-
1940
  public function handleRedo() {
1941
  self::log("Handle Redo #{$_GET['attachment_ID']} type {$_GET['type']}");
1942
-
1943
  die(json_encode($this->redo($_GET['attachment_ID'], $_GET['type'])));
1944
  }
1945
-
1946
  public function redo($qID, $type = false) {
1947
  $compressionType = ($type == 'lossless' ? 'lossless' : ($type == 'glossy' ? 'glossy' : 'lossy')); //sanity check
1948
 
@@ -1957,7 +2072,7 @@ class WPShortPixel {
1957
  $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
1958
  } else {
1959
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => __('Could not restore from backup: ','shortpixel-image-optimiser') . $qID);
1960
- }
1961
  } else {
1962
  $ID = intval($qID);
1963
  $meta = $this->doRestore($ID);
@@ -1976,20 +2091,20 @@ class WPShortPixel {
1976
  //wp_update_attachment_metadata($ID, $meta);
1977
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
1978
  $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
1979
- }
1980
  } else {
1981
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => __('Could not restore from backup: ','shortpixel-image-optimiser') . $ID);
1982
  }
1983
  }
1984
  return $ret;
1985
  }
1986
-
1987
  public function handleOptimizeThumbs() {
1988
  $ID = intval($_GET['attachment_ID']);
1989
  $meta = wp_get_attachment_metadata($ID);
1990
  //die(var_dump($meta));
1991
  $thumbsCount = WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
1992
- if( isset($meta['ShortPixelImprovement'])
1993
  && isset($meta['sizes']) && $thumbsCount
1994
  && ( !isset($meta['ShortPixel']['thumbsOpt']) || $meta['ShortPixel']['thumbsOpt'] == 0
1995
  || (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && $meta['ShortPixel']['thumbsOpt'] < $thumbsCount))) { //optimized without thumbs, thumbs exist
@@ -2009,13 +2124,13 @@ class WPShortPixel {
2009
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
2010
  }
2011
  $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
2012
- }
2013
  } else {
2014
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "message" => (isset($meta['ShortPixelImprovement']) ? __('No thumbnails to optimize for ID: ','shortpixel-image-optimiser') : __('Please optimize image for ID: ','shortpixel-image-optimiser')) . $ID);
2015
  }
2016
  die(json_encode($ret));
2017
  }
2018
-
2019
  public function handleCheckQuota() {
2020
  $this->getQuotaInformation();
2021
  // store the referring webpage location
@@ -2036,13 +2151,13 @@ class WPShortPixel {
2036
  $meta = wp_get_attachment_metadata($ID);
2037
 
2038
 
2039
- if(self::_isProcessable($ID) != false) //we use the static isProcessable to bypass the exclude patterns
2040
  {
2041
  try {
2042
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
2043
-
2044
  @unlink(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file));
2045
-
2046
  if ( !empty($meta['file']) )
2047
  {
2048
  $filesPath = SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir;//base BACKUP path
@@ -2052,14 +2167,14 @@ class WPShortPixel {
2052
  @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));//remove thumbs
2053
  }
2054
  }
2055
- }
2056
-
2057
  } catch(Exception $e) {
2058
  //what to do, what to do?
2059
  }
2060
  }
2061
  }
2062
-
2063
  public function deactivatePlugin() {
2064
  if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'sp_deactivate_plugin_nonce' ) ) {
2065
  wp_nonce_ays( '' );
@@ -2090,7 +2205,7 @@ class WPShortPixel {
2090
  if( !(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) && is_array($this->_settings->currentStats)
2091
  && $this->_settings->currentStats['optimizePdfs'] == $this->_settings->optimizePdfs
2092
  && isset($this->_settings->currentStats['time'])
2093
- && (time() - $this->_settings->currentStats['time'] < $time))
2094
  {
2095
  return $this->_settings->currentStats;
2096
  } else {
@@ -2105,7 +2220,7 @@ class WPShortPixel {
2105
  if($this->_settings->hasCustomFolders) {
2106
  $customImageCount = $this->spMetaDao->countAllProcessableFiles();
2107
  foreach($customImageCount as $key => $val) {
2108
- $quotaData[$key] = isset($quotaData[$key])
2109
  ? (is_array($quotaData[$key])
2110
  ? array_merge($quotaData[$key], $val)
2111
  : (is_numeric($quotaData[$key])
@@ -2118,7 +2233,7 @@ class WPShortPixel {
2118
  return $quotaData;
2119
  }
2120
  }
2121
-
2122
  public function checkQuotaAndAlert($quotaData = null, $recheck = false, $refreshFiles = 300) {
2123
  if(!$quotaData) {
2124
  $quotaData = $this->getQuotaInformation();
@@ -2144,7 +2259,7 @@ class WPShortPixel {
2144
  }
2145
  return $quotaData;
2146
  }
2147
-
2148
  public function isValidMetaId($id) {
2149
  return substr($id, 0, 2 ) == "C-" ? $this->spMetaDao->getMeta(substr($id, 2)) : wp_get_attachment_url($id);
2150
  }
@@ -2152,8 +2267,8 @@ class WPShortPixel {
2152
  public function listCustomMedia() {
2153
  if( ! class_exists( 'ShortPixelListTable' ) ) {
2154
  require_once('view/shortpixel-list-table.php');
2155
- }
2156
- if(isset($_REQUEST['refresh']) && esc_attr($_REQUEST['refresh']) == 1) {
2157
  $notice = null;
2158
  $this->refreshCustomFolders($notice);
2159
  }
@@ -2161,6 +2276,7 @@ class WPShortPixel {
2161
  //die(ShortPixelMetaFacade::queuedId(ShortPixelMetaFacade::CUSTOM_TYPE, $_REQUEST['image']));
2162
  $this->prioQ->push(ShortPixelMetaFacade::queuedId(ShortPixelMetaFacade::CUSTOM_TYPE, $_REQUEST['image']));
2163
  }
 
2164
  $customMediaListTable = new ShortPixelListTable($this, $this->spMetaDao, $this->hasNextGen);
2165
  $items = $customMediaListTable->prepare_items();
2166
  if ( isset($_GET['noheader']) ) {
@@ -2205,7 +2321,10 @@ class WPShortPixel {
2205
  </div>
2206
  </div> <?php
2207
  }
2208
-
 
 
 
2209
  public function bulkProcess() {
2210
  global $wpdb;
2211
 
@@ -2213,13 +2332,13 @@ class WPShortPixel {
2213
  ShortPixelView::displayActivationNotice();
2214
  return;
2215
  }
2216
-
2217
  $quotaData = $this->checkQuotaAndAlert(null, isset($_GET['checkquota']), 0);
2218
  //if($this->_settings->quotaExceeded != 0) {
2219
  //return;
2220
  //}
2221
-
2222
- if(isset($_POST['bulkProcessPause']))
2223
  {//pause an ongoing bulk processing, it might be needed sometimes
2224
  $this->prioQ->pauseBulk();
2225
  if($this->_settings->hasCustomFolders && $this->spMetaDao->getPendingMetaCount()) {
@@ -2227,7 +2346,7 @@ class WPShortPixel {
2227
  }
2228
  }
2229
 
2230
- if(isset($_POST['bulkProcessStop']))
2231
  {//stop an ongoing bulk processing
2232
  $this->prioQ->stopBulk();
2233
  if($this->_settings->hasCustomFolders && $this->spMetaDao->getPendingMetaCount()) {
@@ -2236,9 +2355,9 @@ class WPShortPixel {
2236
  $this->_settings->cancelPointer = NULL;
2237
  }
2238
 
2239
- if(isset($_POST["bulkProcess"]))
2240
  {
2241
- //set the thumbnails option
2242
  if ( isset($_POST['thumbnails']) ) {
2243
  $this->_settings->processThumbnails = 1;
2244
  } else {
@@ -2247,25 +2366,31 @@ class WPShortPixel {
2247
  //clean the custom files errors in order to process them again
2248
  if($this->_settings->hasCustomFolders) {
2249
  $this->spMetaDao->resetFailed();
 
 
2250
  }
2251
-
2252
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_OPTIMIZE);
2253
  $this->_settings->customBulkPaused = 0;
2254
  self::log("BULK: Start: " . $this->prioQ->getStartBulkId() . ", stop: " . $this->prioQ->getStopBulkId() . " PrioQ: "
2255
  .json_encode($this->prioQ->get()));
2256
- }//end bulk process was clicked
2257
-
2258
- if(isset($_POST["bulkRestore"]))
2259
  {
 
 
 
 
2260
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_RESTORE);
2261
  $this->_settings->customBulkPaused = 0;
2262
- }//end bulk restore was clicked
2263
-
2264
- if(isset($_POST["bulkCleanup"]))
2265
  {
2266
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_CLEANUP);
2267
  $this->_settings->customBulkPaused = 0;
2268
- }//end bulk restore was clicked
2269
 
2270
  if(isset($_POST["bulkCleanupPending"]))
2271
  {
@@ -2279,7 +2404,7 @@ class WPShortPixel {
2279
  $this->_settings->customBulkPaused = 0;
2280
  }//resume was clicked
2281
 
2282
- if(isset($_POST["skipToCustom"]))
2283
  {
2284
  $this->_settings->skipToCustom = true;
2285
  }//resume was clicked
@@ -2298,18 +2423,18 @@ class WPShortPixel {
2298
  {
2299
  $msg = $this->bulkProgressMessage($this->prioQ->getDeltaBulkPercent(), $this->prioQ->getTimeRemaining());
2300
 
2301
- $this->view->displayBulkProcessingRunning($this->getPercent($quotaData), $msg, $quotaData['APICallsRemaining'], $this->getAverageCompression(),
2302
- $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE ? 0 :
2303
  ( $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP
2304
  || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING ? -1 : ($pendingMeta !== null ? ($this->prioQ->bulkRunning() ? 3 : 2) : 1)), $quotaData);
2305
 
2306
- } else
2307
  {
2308
  if($this->prioQ->bulkRan() && !$this->prioQ->bulkPaused()) {
2309
  $this->prioQ->markBulkComplete();
2310
  }
2311
 
2312
- //image count
2313
  $thumbsProcessedCount = $this->_settings->thumbsCount;//amount of optimized thumbnails
2314
  $under5PercentCount = $this->_settings->under5Percent;//amount of under 5% optimized imgs.
2315
 
@@ -2317,13 +2442,28 @@ class WPShortPixel {
2317
  $averageCompression = self::getAverageCompression();
2318
  $percent = $this->prioQ->bulkPaused() ? $this->getPercent($quotaData) : false;
2319
 
2320
- $this->view->displayBulkProcessingForm($quotaData, $thumbsProcessedCount, $under5PercentCount,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2321
  $this->prioQ->bulkRan(), $averageCompression, $this->_settings->fileCount,
2322
  self::formatBytes($this->_settings->savedSpace), $percent, $pendingMeta);
 
2323
  }
2324
  }
2325
  //end bulk processing
2326
-
2327
  public function getPercent($quotaData) {
2328
  if($this->_settings->processThumbnails) {
2329
  return $quotaData["totalFiles"] ? min(99, round($quotaData["totalProcessedFiles"] *100.0 / $quotaData["totalFiles"])) : 0;
@@ -2331,7 +2471,7 @@ class WPShortPixel {
2331
  return $quotaData["mainFiles"] ? min(99, round($quotaData["mainProcessedFiles"] *100.0 / $quotaData["mainFiles"])) : 0;
2332
  }
2333
  }
2334
-
2335
  public function bulkProgressMessage($percent, $minutes) {
2336
  $timeEst = "";
2337
  self::log("bulkProgressMessage(): percent: " . $percent);
@@ -2352,10 +2492,10 @@ class WPShortPixel {
2352
  }
2353
  return $timeEst;
2354
  }
2355
-
2356
  public function emptyBackup(){
2357
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2358
-
2359
  //extract all images from DB in an array. of course
2360
  // Simon: WHY?!!! commenting for now...
2361
  /*
@@ -2366,12 +2506,12 @@ class WPShortPixel {
2366
  'post_mime_type' => 'image'
2367
  ));
2368
  */
2369
-
2370
  //delete the actual files on disk
2371
  $this->deleteDir(SHORTPIXEL_BACKUP_FOLDER);//call a recursive function to empty files and sub-dirs in backup dir
2372
  }
2373
  }
2374
-
2375
  public function backupFolderIsEmpty() {
2376
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2377
  return count(scandir(SHORTPIXEL_BACKUP_FOLDER)) > 2 ? false : true;
@@ -2384,14 +2524,14 @@ class WPShortPixel {
2384
  }
2385
  die(self::formatBytes(self::folderSize(SHORTPIXEL_BACKUP_FOLDER)));
2386
  }
2387
-
2388
  public function browseContent() {
2389
  if ( !current_user_can( 'manage_options' ) ) {
2390
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2391
  }
2392
-
2393
  $root = self::getCustomFolderBase();
2394
-
2395
 
2396
  $postDir = rawurldecode($root.(isset($_POST['dir']) ? trim($_POST['dir']) : null ));
2397
  // set checkbox if multiSelect set to true
@@ -2403,7 +2543,7 @@ class WPShortPixel {
2403
 
2404
  $files = scandir($postDir);
2405
  $returnDir = substr($postDir, strlen($root));
2406
-
2407
  natcasesort($files);
2408
 
2409
  if( count($files) > 2 ) { // The 2 accounts for . and ..
@@ -2411,7 +2551,7 @@ class WPShortPixel {
2411
  foreach( $files as $file ) {
2412
 
2413
  if($file == 'ShortpixelBackups' || ShortPixelMetaFacade::isMediaSubfolder($postDir . $file, false)) continue;
2414
-
2415
  $htmlRel = str_replace("'", "&apos;", $returnDir . $file);
2416
  $htmlName = htmlentities($file);
2417
  $ext = preg_replace('/^.*\./', '', $file);
@@ -2431,27 +2571,53 @@ class WPShortPixel {
2431
  }
2432
  die();
2433
  }
2434
-
2435
  public function getComparerData() {
2436
  if (!isset($_POST['id']) || !current_user_can( 'upload_files' ) && !current_user_can( 'edit_posts' ) ) {
2437
  wp_die(json_encode((object)array('origUrl' => false, 'optUrl' => false, 'width' => 0, 'height' => 0)));
2438
  }
2439
-
2440
  $ret = array();
2441
  $handle = new ShortPixelMetaFacade($_POST['id']);
 
2442
  $meta = $handle->getMeta();
2443
  $rawMeta = $handle->getRawMeta();
2444
  $backupUrl = content_url() . "/" . SHORTPIXEL_UPLOADS_NAME . "/" . SHORTPIXEL_BACKUP . "/";
2445
  $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
2446
  $urlBkPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
2447
  $ret['origUrl'] = $backupUrl . $urlBkPath . $meta->getName();
2448
- $ret['optUrl'] = wp_get_attachment_url( $_POST['id'] ); //$uploadsUrl . $urlBkPath . $meta->getName();
2449
- $ret['width'] = $rawMeta['width'];
2450
- $ret['height'] = $rawMeta['height'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2451
 
2452
  die(json_encode((object)$ret));
2453
  }
2454
-
2455
  public function newApiKey() {
2456
  if ( !current_user_can( 'manage_options' ) ) {
2457
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
@@ -2487,20 +2653,20 @@ class WPShortPixel {
2487
  if($body->Status == 'success') {
2488
  $key = trim($body->Details);
2489
  $validityData = $this->getQuotaInformation($key, true, true);
2490
- if($validityData['APIKeyValid']) {
2491
  $this->_settings->apiKey = $key;
2492
  $this->_settings->verifiedKey = true;
2493
  }
2494
  }
2495
  die(json_encode($body));
2496
-
2497
  }
2498
-
2499
  public function proposeUpgrade() {
2500
  if ( !current_user_can( 'manage_options' ) ) {
2501
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2502
  }
2503
-
2504
  $stats = $this->countAllIfNeeded($this->_settings->currentStats, 300);
2505
 
2506
  //$proposal = wp_remote_post($this->_settings->httpProto . "://shortpixel.com/propose-upgrade-frag", array(
@@ -2542,7 +2708,7 @@ class WPShortPixel {
2542
  die($proposal['body']);
2543
 
2544
  }
2545
-
2546
  public static function getCustomFolderBase() {
2547
  if(is_main_site()) {
2548
  $base = get_home_path();
@@ -2552,12 +2718,12 @@ class WPShortPixel {
2552
  return realpath($up['basedir']);
2553
  }
2554
  }
2555
-
2556
  protected function fullRefreshCustomFolder($path, &$notice) {
2557
  $folder = $this->spMetaDao->getFolder($path);
2558
  $diff = $folder->checkFolderContents(array('ShortPixelCustomMetaDao', 'getPathFiles'));
2559
  }
2560
-
2561
  protected function refreshCustomFolders(&$notice, $ignore = false) {
2562
  $customFolders = array();
2563
  if($this->_settings->hasCustomFolders) {
@@ -2588,10 +2754,17 @@ class WPShortPixel {
2588
  }
2589
 
2590
  protected static function alterHtaccess( $clear = false ){
 
 
 
 
2591
  if ( $clear ) {
2592
  insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
 
 
2593
  } else {
2594
- insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '
 
2595
  <IfModule mod_rewrite.c>
2596
  RewriteEngine On
2597
 
@@ -2629,7 +2802,11 @@ class WPShortPixel {
2629
  <IfModule mod_mime.c>
2630
  AddType image/webp .webp
2631
  </IfModule>
2632
- ' );
 
 
 
 
2633
  /* insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '
2634
  RewriteEngine On
2635
  RewriteBase /
@@ -2688,22 +2865,22 @@ Header append Vary Accept env=REDIRECT_webp
2688
  if(isset($_GET['setsparchive'])) {
2689
  $this->_settings->downloadArchive = intval($_GET['setsparchive']);
2690
  }
2691
-
2692
  //check all custom folders and update meta table if files appeared
2693
  $customFolders = $this->refreshCustomFolders($notice, isset($_POST['removeFolder']) ? $_POST['removeFolder'] : null);
2694
-
2695
  if(isset($_POST['request']) && $_POST['request'] == 'request') {
2696
  //a new API Key was requested
2697
  if(filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
2698
-
2699
  }
2700
  else {
2701
- $notice = array("status" => "error",
2702
  "msg" => __("Please provide a valid e-mail.",'shortpixel-image-optimiser')
2703
- . "<BR> "
2704
  . __('For any question regarding obtaining your API Key, please contact us at ','shortpixel-image-optimiser')
2705
  . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
2706
- . __(' or ','shortpixel-image-optimiser')
2707
  . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.");
2708
  }
2709
  }
@@ -2715,35 +2892,36 @@ Header append Vary Accept env=REDIRECT_webp
2715
  $this->cloudflareApi->set_up($cfApi, $cfAuth, $cfZone);
2716
  }
2717
 
2718
- if( isset($_POST['save']) || isset($_POST['saveAdv'])
2719
  || (isset($_POST['validate']) && $_POST['validate'] == "validate")
2720
  || isset($_POST['removeFolder']) || isset($_POST['recheckFolder'])) {
2721
 
2722
  //handle API Key - common for save and validate.
2723
  $_POST['key'] = trim(str_replace("*", "", isset($_POST['key']) ? $_POST['key'] : $this->_settings->apiKey)); //the API key might not be set if the editing is disabled.
2724
-
2725
  if ( strlen($_POST['key']) <> 20 ){
2726
  $KeyLength = strlen($_POST['key']);
2727
-
2728
- $notice = array("status" => "error",
2729
  "msg" => sprintf(__("The key you provided has %s characters. The API key should have 20 characters, letters and numbers only.",'shortpixel-image-optimiser'), $KeyLength)
2730
- . "<BR> <b>"
2731
- . __('Please check that the API key is the same as the one you received in your confirmation email.','shortpixel-image-optimiser')
2732
- . "</b><BR> "
2733
  . __('If this problem persists, please contact us at ','shortpixel-image-optimiser')
2734
  . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
2735
- . __(' or ','shortpixel-image-optimiser')
2736
  . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.");
2737
  }
2738
  else {
 
2739
  if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
2740
  //these are needed for the call to api-status, set them first.
2741
- $this->_settings->siteAuthUser = (isset($_POST['siteAuthUser']) ? $_POST['siteAuthUser'] : $this->_settings->siteAuthUser);
2742
- $this->_settings->siteAuthPass = (isset($_POST['siteAuthPass']) ? $_POST['siteAuthPass'] : $this->_settings->siteAuthPass);
2743
  }
2744
 
2745
  $validityData = $this->getQuotaInformation($_POST['key'], true, isset($_POST['validate']) && $_POST['validate'] == "validate", $_POST);
2746
-
2747
  $this->_settings->apiKey = $_POST['key'];
2748
  if($validityData['APIKeyValid']) {
2749
  if(isset($_POST['validate']) && $_POST['validate'] == "validate") {
@@ -2758,7 +2936,7 @@ Header append Vary Accept env=REDIRECT_webp
2758
  $notice = array("status" => "warn", "msg" => __("API Key is valid but your site is not accessible from our servers. Please make sure that your server is accessible from the Internet before using the API or otherwise we won't be able to optimize them.",'shortpixel-image-optimiser'));
2759
  } else {
2760
  if ( function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY"))
2761
- $notice = array("status" => "success", "msg" => __("Great, your API Key is valid! <br>You seem to be running a multisite, please note that API Key can also be configured in wp-config.php like this:",'shortpixel-image-optimiser')
2762
  . "<BR> <b>define('SHORTPIXEL_API_KEY', '".$this->_settings->apiKey."');</b>");
2763
  else
2764
  $notice = array("status" => "success", "msg" => __('Great, your API Key is valid. Please take a few moments to review the plugin settings below before starting to optimize your images.','shortpixel-image-optimiser'));
@@ -2767,8 +2945,8 @@ Header append Vary Accept env=REDIRECT_webp
2767
  $this->_settings->verifiedKey = true;
2768
  //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
2769
  if ( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && !@mkdir(SHORTPIXEL_BACKUP_FOLDER, 0777, true) )
2770
- $notice = array("status" => "error",
2771
- "msg" => sprintf(__("There is something preventing us to create a new folder for backing up your original files.<BR>Please make sure that folder <b>%s</b> has the necessary write and read rights.",'shortpixel-image-optimiser'),
2772
  WP_CONTENT_DIR . '/' . SHORTPIXEL_UPLOADS_NAME ));
2773
  } else {
2774
  if(isset($_POST['validate'])) {
@@ -2781,27 +2959,27 @@ Header append Vary Accept env=REDIRECT_webp
2781
 
2782
  //if save button - we process the rest of the form elements
2783
  if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
2784
- $this->_settings->compressionType = $_POST['compressionType'];
2785
  if(isset($_POST['thumbnails'])) { $this->_settings->processThumbnails = 1; } else { $this->_settings->processThumbnails = 0; }
2786
  if(isset($_POST['backupImages'])) { $this->_settings->backupImages = 1; } else { $this->_settings->backupImages = 0; }
2787
  if(isset($_POST['cmyk2rgb'])) { $this->_settings->CMYKtoRGBconversion = 1; } else { $this->_settings->CMYKtoRGBconversion = 0; }
2788
  $this->_settings->keepExif = isset($_POST['removeExif']) ? 0 : 1;
2789
  //delete_option('wp-short-pixel-keep-exif');
2790
  $this->_settings->resizeImages = (isset($_POST['resize']) ? 1: 0);
2791
- $this->_settings->resizeType = (isset($_POST['resize_type']) ? $_POST['resize_type']: false);
2792
  $this->_settings->resizeWidth = (isset($_POST['width']) ? intval($_POST['width']): $this->_settings->resizeWidth);
2793
  $this->_settings->resizeHeight = (isset($_POST['height']) ? intval($_POST['height']): $this->_settings->resizeHeight);
2794
  $uploadPath = realpath(SHORTPIXEL_UPLOADS_BASE);
2795
 
2796
- if(isset($_POST['nextGen'])) {
2797
  WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
2798
  $prevNextGen = $this->_settings->includeNextGen;
2799
- $this->_settings->includeNextGen = 1;
2800
  $ret = $this->addNextGenGalleriesToCustom($prevNextGen);
2801
  $folderMsg = $ret["message"];
2802
  $customFolders = $ret["customFolders"];
2803
- } else {
2804
- $this->_settings->includeNextGen = 0;
2805
  }
2806
  if(isset($_POST['addCustomFolder']) && strlen($_POST['addCustomFolder']) > 0) {
2807
  $folderMsg = $this->spMetaDao->newFolderFromPath(stripslashes($_POST['addCustomFolder']), $uploadPath, self::getCustomFolderBase());
@@ -2809,9 +2987,9 @@ Header append Vary Accept env=REDIRECT_webp
2809
  $notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
2810
  }
2811
  $customFolders = $this->spMetaDao->getFolders();
2812
- $this->_settings->hasCustomFolders = time();
2813
  }
2814
-
2815
  $this->_settings->createWebp = (isset($_POST['createWebp']) ? 1: 0);
2816
 
2817
 
@@ -2856,11 +3034,11 @@ Header append Vary Accept env=REDIRECT_webp
2856
  $this->_settings->optimizeUnlisted = (isset($_POST['optimizeUnlisted']) ? 1: 0);
2857
  $this->_settings->optimizePdfs = (isset($_POST['optimizePdfs']) ? 1: 0);
2858
  $this->_settings->png2jpg = (isset($_POST['png2jpg']) ? (isset($_POST['png2jpgForce']) ? 2 : 1): 0);
2859
-
2860
  //die(var_dump($_POST['excludePatterns']));
2861
-
2862
  if(isset($_POST['excludePatterns']) && strlen($_POST['excludePatterns'])) {
2863
- $patterns = array();
2864
  $items = explode(',', $_POST['excludePatterns']);
2865
  foreach($items as $pat) {
2866
  $parts = explode(':', $pat);
@@ -2879,18 +3057,18 @@ Header append Vary Accept env=REDIRECT_webp
2879
  $this->_settings->excludeSizes = (isset($_POST['excludeSizes']) ? $_POST['excludeSizes']: array());
2880
 
2881
  //Redirect to bulk processing if requested
2882
- if( isset($_POST['save']) && $_POST['save'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')
2883
  || isset($_POST['saveAdv']) && $_POST['saveAdv'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')) {
2884
  wp_redirect("upload.php?page=wp-short-pixel-bulk");
2885
  exit();
2886
- }
2887
  }
2888
- if(isset($_POST['removeFolder']) && strlen(($_POST['removeFolder']))) {
2889
  $this->spMetaDao->removeFolder($_POST['removeFolder']);
2890
  $customFolders = $this->spMetaDao->getFolders();
2891
  $_POST["saveAdv"] = true;
2892
  }
2893
- if(isset($_POST['recheckFolder']) && strlen(($_POST['recheckFolder']))) {
2894
  //$folder->fullRefreshCustomFolder($_POST['recheckFolder']); //aici singura solutie pare callback care spune daca exita url-ul complet
2895
  }
2896
  }
@@ -2899,14 +3077,14 @@ Header append Vary Accept env=REDIRECT_webp
2899
  if(isset($_REQUEST['noheader'])) {
2900
  require_once(ABSPATH . 'wp-admin/admin-header.php');
2901
  }
2902
-
2903
  //empty backup
2904
  if(isset($_POST['emptyBackup'])) {
2905
  $this->emptyBackup();
2906
  }
2907
-
2908
  $quotaData = $this->checkQuotaAndAlert(isset($validityData) ? $validityData : null, isset($_GET['checkquota']));
2909
-
2910
  if($this->hasNextGen) {
2911
  $ngg = array_map(array('ShortPixelNextGenAdapter','pathToAbsolute'), ShortPixelNextGenAdapter::getGalleries());
2912
  //die(var_dump($ngg));
@@ -2920,7 +3098,7 @@ Header append Vary Accept env=REDIRECT_webp
2920
  $showApiKey = ( (is_main_site() || (function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY")))
2921
  && !defined("SHORTPIXEL_HIDE_API_KEY"));
2922
  $editApiKey = !defined("SHORTPIXEL_API_KEY") && $showApiKey;
2923
-
2924
  if($this->_settings->verifiedKey) {
2925
  $fileCount = number_format($this->_settings->fileCount);
2926
  $savedSpace = self::formatBytes($this->_settings->savedSpace,2);
@@ -2941,17 +3119,17 @@ Header append Vary Accept env=REDIRECT_webp
2941
  $cloudflareAPI = true;
2942
 
2943
  $this->view->displaySettings($showApiKey, $editApiKey,
2944
- $quotaData, $notice, $resources, $averageCompression, $savedSpace, $savedBandwidth, $remainingImages,
2945
- $totalCallsMade, $fileCount, null /*folder size now on AJAX*/, $customFolders,
2946
  $folderMsg, $folderMsg ? $addedFolder : false, isset($_POST['saveAdv']), $cloudflareAPI, $htaccessWriteable, $isNginx );
2947
  } else {
2948
- $this->view->displaySettings($showApiKey, $editApiKey, $quotaData, $notice);
2949
  }
2950
-
2951
  }
2952
 
2953
  public function addNextGenGalleriesToCustom($silent) {
2954
- $customFolders = array();
2955
  $folderMsg = "";
2956
  if($this->_settings->includeNextGen) {
2957
  //add the NextGen galleries to custom folders
@@ -2962,30 +3140,30 @@ Header append Vary Accept env=REDIRECT_webp
2962
  $msg = $this->spMetaDao->newFolderFromPath($gallery, ABSPATH, self::getCustomFolderBase());
2963
  }
2964
  $folderMsg .= $msg;
2965
- $this->_settings->hasCustomFolders = time();
2966
  }
2967
  $customFolders = $this->spMetaDao->getFolders();
2968
  }
2969
  return array("message" => $silent? "" : $folderMsg, "customFolders" => $customFolders);
2970
  }
2971
-
2972
  public function getAverageCompression(){
2973
- return $this->_settings->totalOptimized > 0
2974
- ? round(( 1 - ( $this->_settings->totalOptimized / $this->_settings->totalOriginal ) ) * 100, 2)
2975
  : 0;
2976
  }
2977
-
2978
  /**
2979
- *
2980
  * @param type $apiKey
2981
  * @param type $appendUserAgent
2982
  * @param type $validate - true if we are validating the api key, send also the domain name and number of pics
2983
  * @return type
2984
  */
2985
  public function getQuotaInformation($apiKey = null, $appendUserAgent = false, $validate = false, $settings = false) {
2986
-
2987
  if(is_null($apiKey)) { $apiKey = $this->_settings->apiKey; }
2988
-
2989
  if($this->_settings->httpProto != 'https' && $this->_settings->httpProto != 'http') {
2990
  $this->_settings->httpProto = 'https';
2991
  }
@@ -3013,8 +3191,8 @@ Header append Vary Accept env=REDIRECT_webp
3013
  $argsStr .= "&host={$args['body']['host']}";
3014
  if(strlen($this->_settings->siteAuthUser)) {
3015
  $args['body']['user'] = $this->_settings->siteAuthUser;
3016
- $args['body']['pass'] = urlencode($this->_settings->siteAuthPass);
3017
- $argsStr .= "&user={$args['body']['user']}&pass={$args['body']['pass']}";
3018
  }
3019
  if($settings !== false) {
3020
  $args['body']['Settings'] = $settings;
@@ -3030,11 +3208,11 @@ Header append Vary Accept env=REDIRECT_webp
3030
  $response = wp_remote_post($requestURL, $args);
3031
 
3032
  $comm['A: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
3033
-
3034
  //some hosting providers won't allow https:// POST connections so we try http:// as well
3035
  if(is_wp_error( $response )) {
3036
  //echo("protocol " . $this->_settings->httpProto . " failed. switching...");
3037
- $requestURL = $this->_settings->httpProto == 'https' ?
3038
  str_replace('https://', 'http://', $requestURL) :
3039
  str_replace('http://', 'https://', $requestURL);
3040
  // add or remove the sslverify
@@ -3043,14 +3221,14 @@ Header append Vary Accept env=REDIRECT_webp
3043
  } else {
3044
  unset($args['sslverify']);
3045
  }
3046
- $response = wp_remote_post($requestURL, $args);
3047
  $comm['B: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
3048
-
3049
  if(!is_wp_error( $response )){
3050
  $this->_settings->httpProto = ($this->_settings->httpProto == 'https' ? 'http' : 'https');
3051
  //echo("protocol " . $this->_settings->httpProto . " succeeded");
3052
  } else {
3053
- //echo("protocol " . $this->_settings->httpProto . " failed too");
3054
  }
3055
  }
3056
  //Second fallback to HTTP get
@@ -3079,7 +3257,7 @@ Header append Vary Accept env=REDIRECT_webp
3079
  $defaultData = is_array($this->_settings->currentStats) ? array_merge( $this->_settings->currentStats, $defaultData) : $defaultData;
3080
 
3081
  if(is_object($response) && get_class($response) == 'WP_Error') {
3082
-
3083
  $urlElements = parse_url($requestURL);
3084
  $portConnect = @fsockopen($urlElements['host'],8,$errno,$errstr,15);
3085
  if(!$portConnect) {
@@ -3103,10 +3281,10 @@ Header append Vary Accept env=REDIRECT_webp
3103
  return $defaultData;
3104
  }
3105
 
3106
- if ( ( $data->APICallsMade + $data->APICallsMadeOneTime ) < ( $data->APICallsQuota + $data->APICallsQuotaOneTime ) ) //reset quota exceeded flag -> user is allowed to process more images.
3107
  $this->resetQuotaExceeded();
3108
  else
3109
- $this->_settings->quotaExceeded = 1;//activate quota limiting
3110
 
3111
  //if a non-valid status exists, delete it
3112
  $lastStatus = $this->_settings->bulkLastStatus = null;
@@ -3135,7 +3313,7 @@ Header append Vary Accept env=REDIRECT_webp
3135
 
3136
  return $dataArray;
3137
  }
3138
-
3139
  public function resetQuotaExceeded() {
3140
  if( $this->_settings->quotaExceeded == 1) {
3141
  $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
@@ -3155,7 +3333,7 @@ Header append Vary Accept env=REDIRECT_webp
3155
  return;
3156
  }
3157
 
3158
- $file = get_attached_file($id);
3159
  $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
3160
 
3161
  if($extended && isset($_GET['SHORTPIXEL_DEBUG'])) {
@@ -3175,25 +3353,25 @@ Header append Vary Accept env=REDIRECT_webp
3175
  $this->view->renderCustomColumn($id, $renderData, $extended);
3176
  return;
3177
  }
3178
-
3179
  //empty data means document, we handle only PDF
3180
  elseif (empty($data)) { //TODO asta devine if si decomentam returnurile
3181
  if($fileExtension == "pdf") {
3182
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3183
  $renderData['message'] = __('PDF not processed.','shortpixel-image-optimiser');
3184
- }
3185
  else { //Optimization N/A
3186
  $renderData['status'] = 'n/a';
3187
  }
3188
  $this->view->renderCustomColumn($id, $renderData, $extended);
3189
  return;
3190
- }
3191
-
3192
  if(!isset($data['ShortPixelImprovement'])) { //new image
3193
  $data['ShortPixelImprovement'] = '';
3194
  }
3195
-
3196
- if( is_numeric($data['ShortPixelImprovement'])
3197
  && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing'])) //for images that erroneously have ShortPixelImprovement = 0 when WaitingProcessing
3198
  ) { //already optimized
3199
  $sizesCount = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
@@ -3209,7 +3387,7 @@ Header append Vary Accept env=REDIRECT_webp
3209
  }
3210
  }
3211
  }
3212
-
3213
  $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
3214
  $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
3215
  $renderData['bonus'] = ($data['ShortPixelImprovement'] < 5);
@@ -3226,7 +3404,7 @@ Header append Vary Accept env=REDIRECT_webp
3226
  $renderData['exifKept'] = isset($data['ShortPixel']['exifKept']) ? $data['ShortPixel']['exifKept'] : null;
3227
  $renderData['png2jpg'] = isset($data['ShortPixelPng2Jpg']) ? $data['ShortPixelPng2Jpg'] : 0;
3228
  $renderData['date'] = isset($data['ShortPixel']['date']) ? $data['ShortPixel']['date'] : null;
3229
- $renderData['quotaExceeded'] = $quotaExceeded;
3230
  $webP = 0;
3231
  if($extended) {
3232
  if(file_exists(dirname($file) . '/' . ShortPixelAPI::MB_basename($file, '.'.$fileExtension) . '.webp' )){
@@ -3279,11 +3457,11 @@ Header append Vary Accept env=REDIRECT_webp
3279
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3280
  $sizes = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
3281
  $renderData['thumbsTotal'] = $sizes;
3282
- $renderData['message'] = ($fileExtension == "pdf" ? 'PDF' : __('Image','shortpixel-image-optimiser'))
3283
  . __(' not processed.','shortpixel-image-optimiser')
3284
- . ' (<a href="https://shortpixel.com/image-compression-test?site-url=' . urlencode(ShortPixelMetaFacade::safeGetAttachmentUrl($id)) . '" target="_blank">'
3285
  . __('Test&nbsp;for&nbsp;free','shortpixel-image-optimiser') . '</a>)';
3286
- }
3287
  $this->view->renderCustomColumn($id, $renderData, $extended);
3288
  }
3289
  }
@@ -3358,11 +3536,11 @@ Header append Vary Accept env=REDIRECT_webp
3358
  );
3359
  }
3360
  }
3361
-
3362
  function shortpixelInfoBoxContent( $post ) {
3363
  $this->generateCustomColumn( 'wp-shortPixel', $post->ID, true );
3364
  }
3365
-
3366
  public function onDeleteImage($post_id) {
3367
  $itemHandler = new ShortPixelMetaFacade($post_id);
3368
  $urlsPaths = $itemHandler->getURLsAndPATHs(true, false, true, array(), true);
@@ -3394,9 +3572,9 @@ Header append Vary Accept env=REDIRECT_webp
3394
  public function columns( $defaults ) {
3395
  $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
3396
  if(current_user_can( 'manage_options' )) {
3397
- $defaults['wp-shortPixel'] .=
3398
- '&nbsp;<a href="options-general.php?page=wp-shortpixel#stats" title="'
3399
- . __('ShortPixel Statistics','shortpixel-image-optimiser')
3400
  . '"><span class="dashicons dashicons-dashboard"></span></a>';
3401
  }
3402
  return $defaults;
@@ -3413,13 +3591,13 @@ Header append Vary Accept env=REDIRECT_webp
3413
  public function nggCountColumns( $count ) {
3414
  return $count + 1;
3415
  }
3416
-
3417
  public function nggColumnHeader( $default ) {
3418
  return __('ShortPixel Compression','shortpixel-image-optimiser');
3419
  }
3420
 
3421
  public function nggColumnContent( $unknown, $picture ) {
3422
-
3423
  $meta = $this->spMetaDao->getMetaForPath($picture->imagePath);
3424
  if($meta) {
3425
  switch($meta->getStatus()) {
@@ -3436,7 +3614,7 @@ Header append Vary Accept env=REDIRECT_webp
3436
  'thumbsTotal' => 0,
3437
  'retinasOpt' => 0,
3438
  'backup' => true
3439
- ));
3440
  break;
3441
  }
3442
  } else {
@@ -3448,7 +3626,7 @@ Header append Vary Accept env=REDIRECT_webp
3448
  'thumbsTotal' => 0,
3449
  'retinasOpt' => 0,
3450
  'message' => "Not optimized"
3451
- ));
3452
  }
3453
  // return var_dump($meta);
3454
  }
@@ -3470,17 +3648,17 @@ Header append Vary Accept env=REDIRECT_webp
3470
 
3471
  return round($bytes, $precision) . ' ' . $units[$pow];
3472
  }
3473
-
3474
  public function isProcessable($ID, $excludeExtensions = array()) {
3475
  $excludePatterns = $this->_settings->excludePatterns;
3476
  return self::_isProcessable($ID, $excludeExtensions, $excludePatterns);
3477
  }
3478
-
3479
  public function isProcessablePath($path, $excludeExtensions = array()) {
3480
  $excludePatterns = $this->_settings->excludePatterns;
3481
  return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
3482
  }
3483
-
3484
  static public function _isProcessable($ID, $excludeExtensions = array(), $excludePatterns = array(), $meta = false) {
3485
  $path = get_attached_file($ID);//get the full file PATH
3486
  if(isset($excludePatterns) && is_array($excludePatterns)) {
@@ -3494,10 +3672,10 @@ Header append Vary Accept env=REDIRECT_webp
3494
  }
3495
  }
3496
  }
3497
- }
3498
  return $path ? self::_isProcessablePath($path, $excludeExtensions, $excludePatterns) : false;
3499
  }
3500
-
3501
  static public function _isProcessablePath($path, $excludeExtensions = array(), $excludePatterns = array()) {
3502
  $pathParts = pathinfo($path);
3503
  $ext = isset($pathParts['extension']) ? $pathParts['extension'] : false;
@@ -3527,7 +3705,7 @@ Header append Vary Accept env=REDIRECT_webp
3527
  $heightBounds = isset($ranges[1]) ? explode("-", $ranges[1]) : false;
3528
  if(!isset($heightBounds[1])) $heightBounds[1] = $heightBounds[0];
3529
  if( $width >= 0 + $widthBounds[0] && $width <= 0 + $widthBounds[1]
3530
- && ( $heightBounds === false
3531
  || ($height >= 0 + $heightBounds[0] && $height <= 0 + $heightBounds[1]))) {
3532
  return false;
3533
  }
@@ -3544,7 +3722,7 @@ Header append Vary Accept env=REDIRECT_webp
3544
  public function getURLsAndPATHs($itemHandler, $meta = NULL, $onlyThumbs = false) {
3545
  return $itemHandler->getURLsAndPATHs($this->_settings->processThumbnails, $onlyThumbs, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
3546
  }
3547
-
3548
 
3549
  public static function deleteDir($dirPath) {
3550
  if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
@@ -3570,7 +3748,7 @@ Header append Vary Accept env=REDIRECT_webp
3570
  }
3571
  $cleanPath = rtrim($path, '/'). '/';
3572
  foreach($files as $t) {
3573
- if ($t<>"." && $t<>"..")
3574
  {
3575
  $currentFile = $cleanPath . $t;
3576
  if (is_dir($currentFile)) {
@@ -3585,7 +3763,7 @@ Header append Vary Accept env=REDIRECT_webp
3585
  }
3586
  return $total_size;
3587
  }
3588
-
3589
  public function migrateBackupFolder() {
3590
  $oldBackupFolder = WP_CONTENT_DIR . '/' . SHORTPIXEL_BACKUP;
3591
 
@@ -3612,7 +3790,7 @@ Header append Vary Accept env=REDIRECT_webp
3612
  @mkdir(SHORTPIXEL_BACKUP_FOLDER);
3613
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/'.SHORTPIXEL_UPLOADS_NAME);
3614
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
3615
- @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
3616
  }
3617
  }
3618
  //then create the wp-content level if not present
@@ -3621,7 +3799,7 @@ Header append Vary Accept env=REDIRECT_webp
3621
  @mkdir(SHORTPIXEL_BACKUP_FOLDER);
3622
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/' . basename(WP_CONTENT_DIR));
3623
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
3624
- @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
3625
  }
3626
  }
3627
  return;
@@ -3644,7 +3822,7 @@ Header append Vary Accept env=REDIRECT_webp
3644
  }
3645
  return $sizes;
3646
  }
3647
-
3648
  function getMaxIntermediateImageSize() {
3649
  global $_wp_additional_image_sizes;
3650
 
@@ -3667,7 +3845,7 @@ Header append Vary Accept env=REDIRECT_webp
3667
  return array('width' => max(100, $width), 'height' => max(100, $height));
3668
  }
3669
 
3670
- public function getOtherCompressionTypes($compressionType = false) {
3671
  return array_values(array_diff(array(0, 1, 2), array(0 + $compressionType)));
3672
  }
3673
 
@@ -3753,11 +3931,11 @@ Header append Vary Accept env=REDIRECT_webp
3753
  public function getApiKey() {
3754
  return $this->_settings->apiKey;
3755
  }
3756
-
3757
  public function getPrioQ() {
3758
  return $this->prioQ;
3759
  }
3760
-
3761
  public function backupImages() {
3762
  return $this->_settings->backupImages;
3763
  }
@@ -3765,11 +3943,11 @@ Header append Vary Accept env=REDIRECT_webp
3765
  public function processThumbnails() {
3766
  return $this->_settings->processThumbnails;
3767
  }
3768
-
3769
  public function getCMYKtoRGBconversion() {
3770
  return $this->_settings->CMYKtoRGBconversion;
3771
  }
3772
-
3773
  public function getSettings() {
3774
  return $this->_settings;
3775
  }
@@ -3801,7 +3979,7 @@ Header append Vary Accept env=REDIRECT_webp
3801
  public function hasNextGen() {
3802
  return $this->hasNextGen;
3803
  }
3804
-
3805
  public function getSpMetaDao() {
3806
  return $this->spMetaDao;
3807
  }
1
  <?php
2
 
3
  class WPShortPixel {
4
+
5
  const BULK_EMPTY_QUEUE = 0;
6
+
7
  private $_apiInterface = null;
8
  private $_settings = null;
9
  private $prioQ = null;
10
  private $view = null;
11
+ private $thumbnailsRegenerating = array();
12
+
13
  private $hasNextGen = false;
14
  private $spMetaDao = null;
15
+
16
  private $jsSuffix = '.min.js';
17
 
18
  private $timer;
19
+
20
  public static $PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'gif', 'png', 'pdf');
21
 
22
  public function __construct() {
27
  }
28
 
29
  load_plugin_textdomain('shortpixel-image-optimiser', false, plugin_basename(dirname( SHORTPIXEL_PLUGIN_FILE )).'/lang');
30
+
31
  $isAdminUser = current_user_can( 'manage_options' );
32
+
33
  $this->_settings = new WPShortPixelSettings();
34
  $this->_apiInterface = new ShortPixelAPI($this->_settings);
35
  $this->cloudflareApi = new ShortPixelCloudFlareApi($this->_settings->cloudflareEmail, $this->_settings->cloudflareAuthKey, $this->_settings->cloudflareZoneID);
37
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $this->_settings->excludePatterns);
38
  $this->prioQ = new ShortPixelQueue($this, $this->_settings);
39
  $this->view = new ShortPixelView($this);
40
+
41
+ $controllerClass = ShortPixelTools::namespaceit('ShortPixelController');
42
+ $controllerClass::init(); // load all subclassed controllers.
43
+
44
  define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
45
 
46
+ if( !defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES')) {
47
+ if(is_plugin_active('envira-gallery/envira-gallery.php') || is_plugin_active('soliloquy-lite/soliloquy-lite.php') || is_plugin_active('soliloquy/soliloquy.php')) {
48
+ define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', '_c,_tl,_tr,_br,_bl');
49
+ }
50
+ elseif(defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIX')) {
51
+ define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', SHORTPIXEL_CUSTOM_THUMB_SUFFIX);
52
+ }
53
  }
54
 
55
  $this->setDefaultViewModeList();//set default mode as list. only @ first run
59
  add_filter( 'plugin_action_links_' . plugin_basename(SHORTPIXEL_PLUGIN_FILE), array(&$this, 'generatePluginLinks'));//for plugin settings page
60
 
61
  //add_action( 'admin_footer', array(&$this, 'handleImageProcessing'));
62
+
63
  //Media custom column
64
  add_filter( 'manage_media_columns', array( &$this, 'columns' ) );//add media library column header
65
  add_action( 'manage_media_custom_column', array( &$this, 'generateCustomColumn' ), 10, 2 );//generate the media library column
71
  add_action( 'add_meta_boxes', array( &$this, 'shortpixelInfoBox') );
72
  //for cleaning up the WebP images when an attachment is deleted
73
  add_action( 'delete_attachment', array( &$this, 'onDeleteImage') );
74
+
75
  //for NextGen
76
  if($this->_settings->hasCustomFolders) {
77
  add_filter( 'ngg_manage_images_columns', array( &$this, 'nggColumns' ) );
81
  // hook on the NextGen gallery list update
82
  add_action('ngg_update_addgallery_page', array( &$this, 'addNextGenGalleriesToCustom'));
83
  }
84
+
85
  // integration with WP/LR Sync plugin
86
  add_action( 'wplr_update_media', array( &$this, 'onWpLrUpdateMedia' ), 10, 2);
87
 
96
  //add settings page
97
  add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );//display SP in Settings menu
98
  add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
99
+
100
  add_action('wp_ajax_shortpixel_browse_content', array(&$this, 'browseContent'));
101
  add_action('wp_ajax_shortpixel_get_backup_size', array(&$this, 'getBackupSize'));
102
  add_action('wp_ajax_shortpixel_get_comparer_data', array(&$this, 'getComparerData'));
103
 
104
  add_action('wp_ajax_shortpixel_new_api_key', array(&$this, 'newApiKey'));
105
  add_action('wp_ajax_shortpixel_propose_upgrade', array(&$this, 'proposeUpgrade'));
106
+
107
  add_action( 'delete_attachment', array( &$this, 'handleDeleteAttachmentInBackup' ) );
108
  add_action( 'load-upload.php', array( &$this, 'handleCustomBulk'));
109
 
115
  add_action('wp_ajax_shortpixel_optimize_thumbs', array(&$this, 'handleOptimizeThumbs'));
116
 
117
  //toolbar notifications
118
+ add_action( 'admin_bar_menu', array( &$this, 'toolbar_shortpixel_processing'), 999 );
119
  //deactivate plugin
120
  add_action( 'admin_post_shortpixel_deactivate_plugin', array(&$this, 'deactivatePlugin'));
121
  //only if the key is not yet valid or the user hasn't bought any credits.
126
  new ShortPixelFeedback( SHORTPIXEL_PLUGIN_FILE, 'shortpixel-image-optimiser', $this->_settings->apiKey, $this);
127
  }
128
  }
129
+
130
  //automatic optimization
131
  add_action( 'wp_ajax_shortpixel_image_processing', array( &$this, 'handleImageProcessing') );
132
  //manual optimization
140
  add_action('wp_ajax_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
141
  add_action('admin_action_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
142
  //This adds the constants used in PHP to be available also in JS
143
+ add_action( 'admin_footer', array( $this, 'shortPixelJS') );
144
+ add_action( 'admin_head', array( $this, 'headCSS') );
145
 
146
+ if($this->_settings->frontBootstrap && shortPixelCheckQueue()) {
147
+ //only if we have something in the queue - usually we never get here if the queue is empty but for some hooks...
148
  //also need to have it in the front footer then
149
  add_action( 'wp_footer', array( &$this, 'shortPixelJS') );
150
  //need to add the nopriv action for when items exist in the queue and no user is logged in
152
  }
153
  //register a method to display admin notices if necessary
154
  add_action('admin_notices', array( &$this, 'displayAdminNotices'));
155
+
156
  $this->migrateBackupFolder();
157
 
158
+ // [BS] Quite dangerous to do this in any constructor. Can hit if request is ajax to name something
159
  if(!$this->_settings->redirectedSettings && !$this->_settings->verifiedKey && (!function_exists("is_multisite") || !is_multisite())) {
160
  $this->_settings->redirectedSettings = 1;
161
  wp_redirect(admin_url("options-general.php?page=wp-shortpixel"));
162
  exit();
163
  }
164
+
165
  }
166
 
167
  //handling older
181
  /*translators: title and menu name for the Bulk Processing page*/
182
  add_media_page( __('ShortPixel Bulk Process','shortpixel-image-optimiser'), __('Bulk ShortPixel','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcess' ) );
183
  }
184
+
185
  public static function shortPixelActivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
186
  {
187
  self::shortPixelDeactivatePlugin();
188
  if(SHORTPIXEL_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
189
  WPShortPixelSettings::debugResetOptions();
190
+
191
  $settings = new WPShortPixelSettings();
192
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $settings->excludePatterns);
193
  $spMetaDao->dropTables();
197
  }
198
  WPShortPixelSettings::onActivate();
199
  }
200
+
201
  public static function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
202
  {
203
  ShortPixelQueue::resetBulk();
330
  }
331
  return $found;
332
  }
333
+
334
  public function displayAdminNotices() {
335
  if(!ShortPixelQueue::testQ()) {
336
  ShortPixelView::displayActivationNotice('fileperms');
340
  }
341
  $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
342
  $this->_settings->dismissedNotices = $dismissed;
343
+
344
  if(!$this->_settings->verifiedKey) {
345
  $now = time();
346
  $act = $this->_settings->activationDate ? $this->_settings->activationDate : $now;
377
  $screen = get_current_screen();
378
  $stats = $this->countAllIfNeeded($this->_settings->currentStats, 86400);
379
  $quotaData = $stats;
380
+
381
  //this is for bulk page - alert on the total credits for total images
382
  if( !isset($dismissed['upgbulk']) && $screen && $screen->id == 'media_page_wp-short-pixel-bulk' && $this->bulkUpgradeNeeded($stats)) {
383
  //looks like the user hasn't got enough credits to bulk process all media library
384
+ ShortPixelView::displayActivationNotice('upgbulk', array('filesTodo' => $stats['totalFiles'] - $stats['totalProcessedFiles'],
385
  'quotaAvailable' => max(0, $quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'])));
386
  }
387
  //consider the monthly plus 1/6 of the available one-time credits.
391
  }
392
  }
393
  }
394
+
395
  public function dismissAdminNotice() {
396
  $noticeId = preg_replace('|[^a-z0-9]|i', '', $_GET['notice_id']);
397
  $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
401
  $this->_settings->optimizeUnlisted = 1;
402
  }
403
  die(json_encode(array("Status" => 'success', "Message" => 'Notice ID: ' . $noticeId . ' dismissed')));
404
+ }
405
 
406
  public function dismissMediaAlert() {
407
  $this->_settings->mediaAlert = 1;
408
  die(json_encode(array("Status" => 'success', "Message" => __('Media alert dismissed','shortpixel-image-optimiser'))));
409
+ }
410
+
411
  protected function getMonthAvg($stats) {
412
  for($i = 4, $count = 0; $i>=1; $i--) {
413
  if($count == 0 && $stats['totalM' . $i] == 0) continue;
415
  }
416
  return ($stats['totalM1'] + $stats['totalM2'] + $stats['totalM3'] + $stats['totalM4']) / max(1,$count);
417
  }
418
+
419
  protected function monthlyUpgradeNeeded($quotaData) {
420
  return isset($quotaData['APICallsQuotaNumeric']) && $this->getMonthAvg($quotaData) > $quotaData['APICallsQuotaNumeric'] + ($quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'])/6 + 20;
421
  }
426
  }
427
 
428
  //set default move as "list". only set once, it won't try to set the default mode again.
429
+ public function setDefaultViewModeList()
430
  {
431
+ if($this->_settings->mediaLibraryViewMode === false)
432
  {
433
  $this->_settings->mediaLibraryViewMode = 1;
434
  $currentUserID = false;
438
  update_user_meta($currentUserID, "wp_media_library_mode", "list");
439
  }
440
  }
441
+
442
  }
443
 
444
+ static function log($message, $force = false) {
445
+ if (SHORTPIXEL_DEBUG === true || $force) {
446
  if (is_array($message) || is_object($message)) {
447
+ self::doLog(print_r($message, true), $force);
448
  } else {
449
+ self::doLog($message, $force);
450
  }
451
  }
452
  }
453
+
454
+ static protected function doLog($message, $force = false) {
455
+ if(defined('SHORTPIXEL_DEBUG_TARGET') || $force) {
456
  file_put_contents(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log", '[' . date('Y-m-d H:i:s') . "] $message\n", FILE_APPEND);
457
  } else {
458
  error_log($message);
462
  function headCSS() {
463
  echo('<style>.shortpixel-hide {display:none;}</style>');
464
  }
465
+
466
+ function shortPixelJS() {
467
  //require_once(ABSPATH . 'wp-admin/includes/screen.php');
468
  if(function_exists('get_current_screen')) {
469
  $screen = get_current_screen();
470
+
471
+ if(is_object($screen)) {
472
+ if( in_array($screen->id, array('attachment', 'upload', 'media_page_wp-short-pixel-custom'))) {
473
  //output the comparer html
474
  $this->view->outputComparerHTML();
475
  //render a template of the list cell to be used by the JS
480
  wp_enqueue_style('short-pixel-bar.min.css', plugins_url('/res/css/short-pixel-bar.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
481
  if( in_array($screen->id, array('attachment', 'upload', 'settings_page_wp-shortpixel', 'media_page_wp-short-pixel-bulk', 'media_page_wp-short-pixel-custom'))) {
482
  wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
483
+ //modal - used in settings for selecting folder
484
+ wp_enqueue_style('short-pixel-modal.min.css', plugins_url('/res/css/short-pixel-modal.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
485
+
486
+ wp_register_style('shortpixel-admin', plugins_url('/res/css/shortpixel-admin.css', SHORTPIXEL_PLUGIN_FILE),array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION );
487
+ wp_enqueue_style('shortpixel-admin');
488
  }
489
  }
490
  }
558
  wp_localize_script( 'shortpixel' . $this->jsSuffix, '_spTr', $jsTranslation );
559
  wp_localize_script( 'shortpixel' . $this->jsSuffix, 'ShortPixelConstants', $ShortPixelConstants );
560
  wp_enqueue_script('shortpixel' . $this->jsSuffix);
561
+
562
  wp_enqueue_script('jquery.knob.min.js', plugins_url('/res/js/jquery.knob.min.js',SHORTPIXEL_PLUGIN_FILE) );
563
  wp_enqueue_script('jquery.tooltip.min.js', plugins_url('/res/js/jquery.tooltip.min.js',SHORTPIXEL_PLUGIN_FILE) );
564
  wp_enqueue_script('punycode.min.js', plugins_url('/res/js/punycode.min.js',SHORTPIXEL_PLUGIN_FILE) );
565
  }
566
 
567
  function toolbar_shortpixel_processing( $wp_admin_bar ) {
568
+
569
  $extraClasses = " shortpixel-hide";
570
  /*translators: toolbar icon tooltip*/
571
  $id = 'short-pixel-notice-toolbar';
596
 
597
  $args = array(
598
  'id' => 'shortpixel_processing',
599
+ 'title' => '<div id="' . $id . '" title="' . $tooltip . '" ><img src="'
600
  . plugins_url( 'res/img/'.$icon, SHORTPIXEL_PLUGIN_FILE ) . '" success-url="' . $successLink . '"><span class="shp-alert">!</span>'
601
  .'<div class="cssload-container"><div class="cssload-speeding-wheel"></div></div></div>',
602
  'href' => $link,
640
  foreach( $mediaIds as $ID ) {
641
  $meta = wp_get_attachment_metadata($ID);
642
  if( ( !isset($meta['ShortPixel']) //never touched by ShortPixel
643
+ || (isset($meta['ShortPixel']['WaitingProcessing']) && $meta['ShortPixel']['WaitingProcessing'] == true))
644
  && (!isset($meta['ShortPixelImprovement']) || $meta['ShortPixelImprovement'] == __('Optimization N/A','shortpixel-image-optimiser'))) {
645
  $this->prioQ->push($ID);
646
  if(!isset($meta['ShortPixel'])) {
649
  $meta['ShortPixel']['WaitingProcessing'] = true;
650
  //wp_update_attachment_metadata($ID, $meta);
651
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
652
+ ShortPixelMetaFacade::optimizationStarted($ID);
653
  }
654
  }
655
  break;
705
  return $meta;
706
  }
707
 
708
+ if(isset($this->thumbnailsRegenerating[$ID])) {
 
709
  return $meta;
710
  }
711
+
712
  self::log("Handle Media Library Image Upload #{$ID}");
713
  //self::log("STACK: " . json_encode(debug_backtrace()));
714
 
728
  $meta['ShortPixelImprovement'] = __('Optimization N/A', 'shortpixel-image-optimiser');
729
  return $meta;
730
  }
731
+ else
732
  {//the kind of file we can process. goody.
733
 
734
  $this->prioQ->push($ID);
751
  //self::log("IMG: sent: " . json_encode($URLsAndPATHs));
752
  }
753
  $meta['ShortPixel']['WaitingProcessing'] = true;
754
+
755
  // check if the image was converted from PNG upon uploading.
756
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {//for the moment
757
  $imagePath = $itemHandler->getMeta()->getPath();
760
  $params = $conv[$imagePath];
761
  unset($conv[$imagePath]);
762
  $this->_settings->convertedPng2Jpg == $conv;
763
+ $meta['ShortPixelPng2Jpg'] = array('originalFile' => $params['pngFile'], 'originalSizes' => array(),
764
  'backup' => $params['backup'], 'optimizationPercent' => $params['optimizationPercent']);
765
  }
766
  }
767
+
768
  return $meta;
769
+ }
770
  }//end handleMediaLibraryImageUpload
771
 
772
  /**
773
  * if the image was optimized in the last hour, send a request to delete from picQueue
774
  * @param $itemHandler
775
  * @param bool $urlsAndPaths
776
+ * @see ShortPixelImage/maybeDump
777
  */
778
  public function maybeDumpFromProcessedOnServer($itemHandler, $urlsAndPaths) {
779
  $meta = $itemHandler->getMeta();
780
 
781
+ $doDump = false;
782
+
783
+ if ($meta->getStatus() <= 0)
784
+ {
785
+ $doDump = true; // dump any caching on files that ended in an error.
786
+ }
787
+ else if(time() - strtotime($meta->getTsOptimized()) < 3600) // check if this was optimized in last hour.
788
+ {
789
+ $doDump = true;
790
+ }
791
 
792
+ if ($doDump)
793
+ {
794
+ $this->_apiInterface->doDumpRequests($urlsAndPaths["URLs"]);
795
  }
796
  }
797
 
905
  }
906
  return $meta;
907
  }
908
+
909
  public function optimizeCustomImage($id) {
910
+ $itemHandler = new ShortPixelMetaFacade('C-' . $id);
911
+ $meta = $itemHandler->getMeta();
912
+
913
+ if ($meta->getStatus() <= 0) // image is in errorState. Dump when retrying.
914
+ {
915
+ $URLsAndPATHs = $itemHandler->getURLsAndPATHs(false);
916
+ $this->maybeDumpFromProcessedOnServer($itemHandler, $URLsAndPATHs);
917
+ }
918
+ if($meta->getStatus() != ShortPixelMeta::FILE_STATUS_SUCCESS) {
919
+
920
+
921
+ $meta->setStatus(ShortPixelMeta::FILE_STATUS_PENDING);
922
  $meta->setRetries(0);
923
+ /* [BS] This is being set because meta in other states does not keep previous values. The value 0 is problematic
924
+ since it can also mean not-initalized, new, etc . So push meta from settings.
925
+ */
926
+ $meta->setCompressionType($this->_settings->compressionType);
927
+ $meta->setKeepExif($this->_settings->keepExif);
928
+ $meta->setCmyk2rgb($this->_settings->CMYKtoRGBconversion);
929
+ $meta->setResize($this->_settings->resizeImages);
930
+ $meta->setResizeWidth($this->_settings->resizeWidth);
931
+ $meta->setResizeHeight($this->_settings->resizeHeight);
932
  $this->spMetaDao->update($meta);
933
  $this->prioQ->push('C-' . $id);
934
  }
936
 
937
  public function bulkRestore(){
938
  global $wpdb;
939
+
940
  $startQueryID = $crtStartQueryID = $this->prioQ->getStartBulkId();
941
+ $endQueryID = $this->prioQ->getStopBulkId();
942
 
943
  if ( $startQueryID <= $endQueryID ) {
944
  return false;
945
  }
946
+
947
  $this->prioQ->resetPrio();
948
 
949
+ $startTime = time();
950
  $maxTime = min(30, (is_numeric(SHORTPIXEL_MAX_EXECUTION_TIME) && SHORTPIXEL_MAX_EXECUTION_TIME > 10 ? SHORTPIXEL_MAX_EXECUTION_TIME - 5 : 25));
951
  $maxResults = SHORTPIXEL_MAX_RESULTS_QUERY * 2;
952
  if(in_array($this->prioQ->getBulkType(), array(ShortPixelQueue::BULK_TYPE_CLEANUP, ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING))) {
953
  $maxResults *= 20;
954
  }
955
  $restored = array();
956
+
957
  //$ind = 0;
958
  while( $crtStartQueryID >= $endQueryID && time() - $startTime < $maxTime) {
959
  //if($ind > 1) break;
960
  //$ind++;
961
+
962
+ // [BS] Request StartQueryID everytime to query for updated AdvanceBulk status
963
+ $crtStartQueryID = $this->prioQ->getStartBulkId();
964
  $resultsPostMeta = WpShortPixelMediaLbraryAdapter::getPostMetaSlice($crtStartQueryID, $endQueryID, $maxResults);
965
  if ( empty($resultsPostMeta) ) {
966
+ // check for custom work
967
+ $pendingCustomMeta = $this->spMetaDao->getPendingBulkRestore(SHORTPIXEL_MAX_RESULTS_QUERY * 2);
968
+ if (count($pendingCustomMeta) > 0)
969
+ {
970
+ foreach($pendingCustomMeta as $cObj)
971
+ {
972
+ $this->doCustomRestore($cObj->id);
973
+ }
974
+ }
975
+ else
976
+ {
977
+ $crtStartQueryID -= $maxResults; // this basically nukes the bulk.
978
+ $startQueryID = $crtStartQueryID;
979
+ $this->prioQ->setStartBulkId($startQueryID);
980
+ continue;
981
+ }
982
  }
983
 
984
  foreach ( $resultsPostMeta as $itemMetaData ) {
989
  if($meta->getStatus() == 2 || $meta->getStatus() == 1) {
990
  if($meta->getStatus() == 2 && $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE) {
991
  $res = $this->doRestore($crtStartQueryID); //this is restore, the real
992
+ } else {
993
  //this is only meta cleanup, no files are replaced (BACKUP REMAINS IN PLACE TOO)
994
  $item->cleanupMeta($this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING);
995
  $res = true;
999
  if($meta->getStatus() < 0) {//also cleanup errors either for restore or cleanup
1000
  $item->cleanupMeta();
1001
  }
1002
+ }
1003
+ // [BS] Fixed Bug. Advance Bulk was outside of this loop, causing infinite loops to happen.
1004
+ $this->advanceBulk($crtStartQueryID);
1005
  }
1006
+
1007
  return $restored;
1008
  }
1009
+
1010
  //TODO muta in bulkProvider
1011
  public function getBulkItemsFromDb(){
1012
  global $wpdb;
1013
+
1014
  $startQueryID = $this->prioQ->getStartBulkId();
1015
+ $endQueryID = $this->prioQ->getStopBulkId();
1016
  $skippedAlreadyProcessed = 0;
1017
+
1018
  if ( $startQueryID <= $endQueryID ) {
1019
  return false;
1020
  }
1021
  $idList = array();
1022
  $itemList = array();
1023
+ for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
1024
  ($crtStartQueryID >= $endQueryID) && (count($itemList) < SHORTPIXEL_PRESEND_ITEMS) && ($sanityCheck < 150)
1025
  && (SHORTPIXEL_MAX_EXECUTION_TIME < 10 || time() - $this->timer < SHORTPIXEL_MAX_EXECUTION_TIME - 5); $sanityCheck++) {
1026
+
1027
  self::log("GETDB: current StartID: " . $crtStartQueryID);
1028
 
1029
+ /* $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta
1030
+ WHERE ( post_id <= $crtStartQueryID AND post_id >= $endQueryID )
1031
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
1032
  ORDER BY post_id DESC
1033
  LIMIT " . SHORTPIXEL_MAX_RESULTS_QUERY;
1050
  if(!in_array($crtStartQueryID, $idList) && $this->isProcessable($crtStartQueryID, ($this->_settings->optimizePdfs ? array() : array('pdf')))) {
1051
  $item = new ShortPixelMetaFacade($crtStartQueryID);
1052
  $meta = $item->getMeta();//wp_get_attachment_metadata($crtStartQueryID);
1053
+
1054
  if($meta->getStatus() != 2) {
1055
  $addIt = (strpos($meta->getMessage(), __('Image files are missing.', 'shortpixel-image-optimiser')) === false);
1056
 
1076
  } else {
1077
  $skippedAlreadyProcessed++;
1078
  }
1079
+ }
1080
  elseif( $this->_settings->processThumbnails && $meta->getThumbsOpt() !== null
1081
  && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0
1082
  || $meta->getThumbsOpt() < WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes) && is_array($meta->getThumbsOptList()))) { //thumbs were chosen in settings
1099
  $leapStart = $this->prioQ->getStartBulkId();
1100
  $crtStartQueryID = $startQueryID = $itemMetaData->post_id - 1; //decrement it so we don't select it again
1101
  $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->_settings, $leapStart, $crtStartQueryID);
1102
+ $skippedAlreadyProcessed += $res["mainProcessedFiles"] - $res["mainProc".($this->getCompressionType() == 1 ? "Lossy" : "Lossless")."Files"];
1103
  self::log("GETDB: empty list. setStartBulkID to $startQueryID");
1104
  $this->prioQ->setStartBulkId($startQueryID);
1105
  } else {
1122
  }
1123
  return $items;
1124
  }
1125
+
1126
  private function checkKey($ID) {
1127
  if( $this->_settings->verifiedKey == false) {
1128
  if($ID == null){
1132
  $response = array("Status" => ShortPixelAPI::STATUS_NO_KEY, "ImageID" => $itemHandler ? $itemHandler->getId() : "-1", "Message" => __('Missing API Key','shortpixel-image-optimiser'));
1133
  $this->_settings->bulkLastStatus = $response;
1134
  die(json_encode($response));
1135
+ }
1136
  }
1137
+
1138
  private function sendEmptyQueue() {
1139
  $avg = $this->getAverageCompression();
1140
  $fileCount = $this->_settings->fileCount;
1141
+ $response = array("Status" => self::BULK_EMPTY_QUEUE,
1142
  /* translators: console message Empty queue 1234 -> 1234 */
1143
  "Message" => __('Empty queue ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
1144
+ "BulkStatus" => ($this->prioQ->bulkRunning()
1145
  ? "1" : ($this->prioQ->bulkPaused() ? "2" : "0")),
1146
  "AverageCompression" => $avg,
1147
  "FileCount" => $fileCount,
1148
  "BulkPercent" => $this->prioQ->getBulkPercent());
1149
+ die(json_encode($response));
1150
  }
1151
 
1152
+ /* Main Image Processing Function. Called from JS loop
1153
+ *
1154
+ * @param String $ID ApiKey
1155
+ */
1156
  public function handleImageProcessing($ID = null) {
1157
  //if(rand(1,2) == 2) {
1158
  // header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
1160
  //}
1161
  //0: check key
1162
  $this->checkKey($ID);
1163
+
1164
  if($this->_settings->frontBootstrap && is_admin() && !ShortPixelTools::requestIsFrontendAjax()) {
1165
  //if in backend, and front-end is activated, mark processing from backend to shut off the front-end for 10 min.
1166
  $this->_settings->lastBackAction = time();
1167
  }
1168
+
1169
  $rawPrioQ = $this->prioQ->get();
1170
  if(count($rawPrioQ)) { self::log("HIP: 0 Priority Queue: ".json_encode($rawPrioQ)); }
1171
  self::log("HIP: 0 Bulk running? " . $this->prioQ->bulkRunning() . " START " . $this->_settings->startBulkId . " STOP " . $this->_settings->stopBulkId);
1172
+
1173
  //handle the bulk restore and cleanup first - these are fast operations taking precedece over optimization
1174
+ if( $this->prioQ->bulkRunning()
1175
  && ( $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE
1176
  || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP
1177
  || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING)) {
1179
  if($res === false) {
1180
  $this->sendEmptyQueue();
1181
  } else {
1182
+ die(json_encode(array("Status" => ShortPixelAPI::STATUS_RETRY,
1183
  "Message" => __('Restoring images... ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
1184
  "BulkPercent" => $this->prioQ->getBulkPercent(),
1185
  "Restored" => $res )));
1186
  }
1187
+
1188
  }
1189
+
1190
  //1: get 3 ids to process. Take them with priority from the queue
1191
  $ids = $this->getFromPrioAndCheck(SHORTPIXEL_PRESEND_ITEMS);
1192
  if(count($ids) < SHORTPIXEL_PRESEND_ITEMS ) { //take from bulk if bulk processing active
1212
  $customIds = false;
1213
  if(count($ids) < SHORTPIXEL_PRESEND_ITEMS && $this->prioQ->bulkRan() && $this->_settings->hasCustomFolders
1214
  && (!$this->_settings->cancelPointer || $this->_settings->skipToCustom)
1215
+ && !$this->_settings->customBulkPaused)
1216
  { //take from custom images if any left to optimize - only if bulk was ever started
1217
  //but first refresh if it wasn't refreshed in the last hour
1218
  if(time() - $this->_settings->hasCustomFolders > 3600) {
1219
  $notice = null; $this->refreshCustomFolders($notice);
1220
  $this->_settings->hasCustomFolders = time();
1221
  }
1222
+
1223
+ $customIds = $this->spMetaDao->getPendingMetas( SHORTPIXEL_PRESEND_ITEMS - count($ids));
1224
  if(is_array($customIds)) {
1225
  $ids = array_merge($ids, array_map(array('ShortPixelMetaFacade', 'getNewFromRow'), $customIds));
1226
  }
1227
  }
1228
  //var_dump($ids);
1229
  //die("za stop 2");
1230
+
1231
  //self::log("HIP: 1 Ids: ".json_encode($ids));
1232
  if(count($ids)) {$idl='';foreach($ids as $i){$idl.=$i->getId().' ';} self::log("HIP: 1 Selected IDs: $idl");}
1233
 
1235
  for($i = 0, $itemHandler = false; $ids !== false && $i < min(SHORTPIXEL_PRESEND_ITEMS, count($ids)); $i++) {
1236
  $crtItemHandler = $ids[$i];
1237
  $tmpMeta = $crtItemHandler->getMeta();
1238
+
1239
  $compType = ($tmpMeta->getCompressionType() !== null ? $tmpMeta->getCompressionType() : $this->_settings->compressionType);
1240
+ try {
1241
  self::log("HIP: 1 sendToProcessing: ".$crtItemHandler->getId());
1242
  $URLsAndPATHs = $this->sendToProcessing($crtItemHandler, $compType, $tmpMeta->getThumbsTodo());
1243
  //self::log("HIP: 1 METADATA: ".json_encode($crtItemHandler->getRawMeta()));
1260
  if (!$itemHandler){
1261
  //if searching, than the script is searching for not processed items and found none yet, should be relaunced
1262
  if(isset($res['searching']) && $res['searching']) {
1263
+ die(json_encode(array("Status" => ShortPixelAPI::STATUS_RETRY,
1264
  "Message" => __('Searching images to optimize... ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId() )));
1265
  }
1266
  //in this case the queue is really empty
1294
  $result["ThumbsCount"] = $meta->getThumbsOpt()
1295
  ? $meta->getThumbsOpt() //below is the fallback for old optimized images that don't have thumbsOpt
1296
  : ($this->_settings->processThumbnails ? $result["ThumbsTotal"] : 0);
1297
+
1298
  $result["RetinasCount"] = $meta->getRetinasOpt();
1299
  $result["BackupEnabled"] = ($this->getBackupFolderAny($meta->getPath(), $meta->getThumbs()) ? true : false);//$this->_settings->backupImages;
1300
+
1301
+ $tsOptimized = $meta->getTsOptimized();
1302
+ if (! is_null($tsOptimized))
1303
+ {
1304
+ $tsOptObj = new DateTime($tsOptimized);
1305
+ if ($tsOptObj)
1306
+ $result['TsOptimized'] = ShortPixelTools::format_nice_date($tsOptObj);
1307
+ }
1308
+
1309
  if(!$prio && $itemId <= $this->prioQ->getStartBulkId()) {
1310
  $this->advanceBulk($itemId);
1311
  $this->setBulkInfo($itemId, $result);
1312
  }
1313
 
1314
  $result["AverageCompression"] = $this->getAverageCompression();
1315
+
1316
+ if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
1317
+
1318
  $thumb = $bkThumb = "";
1319
  //$percent = 0;
1320
  $percent = $meta->getImprovementPercent();
1321
  if($percent){
1322
  $filePath = explode("/", $meta->getPath());
1323
+
1324
  //Get a suitable thumb
1325
  $sizes = $meta->getThumbs();
1326
  if('pdf' == strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))) {
1427
  $prio = $this->prioQ->addToFailed($itemHandler->getQueuedId());
1428
  }
1429
  self::log("HIP RES: skipping $itemId");
1430
+ $this->advanceBulk($meta->getId());
1431
  if($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
1432
  $result["CustomImageLink"] = ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath();
1433
  }
1453
  elseif($result["Status"] == ShortPixelAPI::STATUS_RETRY && is_array($customIds)) {
1454
  $result["CustomImageLink"] = $thumb = ShortPixelMetaFacade::getHomeUrl() . $meta->getWebPath();
1455
  }
1456
+
1457
  if($result["Status"] !== ShortPixelAPI::STATUS_RETRY) {
1458
  $this->_settings->bulkLastStatus = $result;
1459
  }
1460
  die(json_encode($result));
1461
  }
1462
+
1463
+
1464
  private function advanceBulk($processedID) {
1465
  if($processedID <= $this->prioQ->getStartBulkId()) {
1466
  $this->prioQ->setStartBulkId($processedID - 1);
1467
  $this->prioQ->logBulkProgress();
1468
  }
1469
  }
1470
+
1471
  private function setBulkInfo($processedID, &$result) {
1472
+ $deltaBulkPercent = $this->prioQ->getDeltaBulkPercent();
1473
  $minutesRemaining = $this->prioQ->getTimeRemaining();
1474
  $pendingMeta = $this->_settings->hasCustomFolders ? $this->spMetaDao->getPendingMetaCount() : 0;
1475
  $percent = $this->prioQ->getBulkPercent();
1483
  $result["BulkPercent"] = $percent;
1484
  $result["BulkMsg"] = $this->bulkProgressMessage($deltaBulkPercent, $minutesRemaining);
1485
  }
1486
+
1487
  private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false) {
1488
 
1489
  //conversion of PNG 2 JPG for existing images
1490
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) { //currently only for ML
1491
  $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
1492
+
1493
  if(isset($rawMeta['type']) && $rawMeta['type'] == 'image/jpeg') {
1494
  $itemHandler->getMeta(true);
1495
  }
1496
  }
1497
+
1498
  //WpShortPixelMediaLbraryAdapter::cleanupFoundThumbs($itemHandler);
1499
  $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
1500
 
1503
  if( $itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE
1504
  && $this->_settings->optimizeUnlisted) {
1505
  $mainFile = $meta->getPath();
1506
+
1507
  $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($mainFile);
1508
  //first identify which thumbs are not in the sizes
1509
  $sizes = $meta->getThumbs();
1537
  );
1538
  $ind++;
1539
  }
1540
+ }
1541
  if($ind > $start) { // at least one thumbnail added, update
1542
  $meta->setThumbs($sizes);
1543
  $itemHandler->updateMeta($meta);
1544
  $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
1545
  }
1546
  }
1547
+
1548
  //find any missing thumbs files and mark them as such
1549
  $miss = $meta->getThumbsMissing();
1550
  /* TODO remove */if(is_numeric($miss)) $miss = array();
1551
+ if( isset($URLsAndPATHs['sizesMissing']) && count($URLsAndPATHs['sizesMissing'])
1552
  && (null === $miss || count(array_diff_key($miss, array_merge($URLsAndPATHs['sizesMissing'], $miss))))) {
1553
  //fix missing thumbs in the metadata before sending to processing
1554
  $meta->setThumbsMissing($URLsAndPATHs['sizesMissing']);
1555
+ $itemHandler->updateMeta();
1556
  }
1557
  //die(var_dump($itemHandler));
1558
  $refresh = $meta->getStatus() === ShortPixelAPI::ERR_INCORRECT_FILE_SIZE;
1559
  //echo("URLS: "); die(var_dump($URLsAndPATHs));
 
 
1560
  $itemHandler->setWaitingProcessing();
1561
+ $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $itemHandler,
1562
+ $compressionType === false ? $this->_settings->compressionType : $compressionType, $refresh);//send a request, do NOT wait for response
1563
  //$meta = wp_get_attachment_metadata($ID);
1564
  //$meta['ShortPixel']['WaitingProcessing'] = true;
1565
  //wp_update_attachment_metadata($ID, $meta);
1569
  public function handleManualOptimization() {
1570
  $imageId = $_GET['image_id'];
1571
  $cleanup = $_GET['cleanup'];
1572
+
1573
  self::log("Handle Manual Optimization #{$imageId}");
1574
+
1575
  switch(substr($imageId, 0, 2)) {
1576
  case "N-":
1577
  return "Add the gallery to the custom folders list in ShortPixel settings.";
1588
  break;
1589
  case "C-":
1590
  throw new Exception("HandleManualOptimization for custom images not implemented");
1591
+ default:
1592
  $this->optimizeNowHook(intval($imageId), true);
1593
  break;
1594
  }
1595
  //do_action('shortpixel-optimize-now', $imageId);
1596
+
1597
  }
1598
 
1599
  public function checkStatus() {
1633
  * @param $postId
1634
  */
1635
  public function thumbnailsBeforeRegenerateHook($postId) {
1636
+ $this->thumbnailsRegenerating[$postId] = true;
 
 
 
1637
  }
1638
 
1639
+
1640
  /**
1641
  * to be called by thumbnail regeneration plugins when regenerating the thumbnails for an image
1642
  * @param $postId - the postId of the image
1643
  * @param $originalMeta - the metadata before the regeneration
1644
  * @param array $regeneratedSizes - the list of the regenerated thumbnails - if empty then all were regenerated.
1645
  * @param bool $bulk - true if the regeneration is done in bulk - in this case the image will not be immediately scheduled for processing but the user will need to launch the ShortPixel bulk after regenerating.
1646
+ *
1647
+ *
1648
+ * Note - $regeneratedSizes expects a metadata array, with filename, not just the resized data.
1649
  */
1650
  public function thumbnailsRegeneratedHook($postId, $originalMeta, $regeneratedSizes = array(), $bulk = false) {
1651
 
1661
  foreach($regeneratedSizes as $size) {
1662
  if(isset($size['file']) && in_array($size['file'], $shortPixelMeta["thumbsOptList"] )) {
1663
  $regeneratedThumbs[] = $size['file'];
1664
+ $shortPixelMeta["thumbsOpt"] = max(0, $shortPixelMeta["thumbsOpt"] - 1); // this is a complicated count of number of thumbnails
1665
  $shortPixelMeta["retinasOpt"] = max(0, $shortPixelMeta["retinasOpt"] - 1);
1666
  }
1667
  }
1668
+ // This retains the thumbnails that were already regenerated, and removes what is passed via regeneratedSizes.
1669
  $shortPixelMeta["thumbsOptList"] = array_diff($shortPixelMeta["thumbsOptList"], $regeneratedThumbs);
1670
  }
1671
  $meta = wp_get_attachment_metadata($postId);
1676
  }
1677
  //wp_update_attachment_metadata($postId, $meta);
1678
  update_post_meta($postId, '_wp_attachment_metadata', $meta);
 
 
 
 
 
1679
 
1680
  if(!$bulk) {
1681
  $this->prioQ->push($postId);
1682
  }
1683
  }
1684
+ unset($this->thumbnailsRegenerating[$postId]);
1685
  }
1686
 
1687
  public function shortpixelGetBackupFilter($imagePath) {
1699
  $this->prioQ->push($imageId);
1700
  //wp_update_attachment_metadata($imageId, $meta);
1701
  update_post_meta($imageId, '_wp_attachment_metadata', $meta);
1702
+ ShortPixelMetaFacade::optimizationStarted($imageId);
1703
  }
1704
  }
1705
+
1706
+
1707
  //save error in file's meta data
1708
  public function handleError($ID, $result)
1709
  {
1721
  //another chance at glory, maybe cleanup was too much? (we tried first the cleaned up version for historical reason, don't disturb the sleeping dragon, right? :))
1722
  return $this->getBackupFolderInternal($file);
1723
  }
1724
+
1725
  private function getBackupFolderInternal($file) {
1726
  $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
1727
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
1744
  }
1745
  return SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir;
1746
  }
1747
+
1748
  public function getBackupFolderAny($file, $thumbs) {
1749
  $ret = $this->getBackupFolder($file);
1750
  //if(!$ret && !file_exists($file) && isset($thumbs)) {
1803
 
1804
  $pathInfo = pathinfo($file);
1805
  $sizes = isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array();
1806
+
1807
  //check if the images were converted from PNG
1808
  $png2jpgMain = isset($rawMeta['ShortPixelPng2Jpg']['originalFile']) ? $rawMeta['ShortPixelPng2Jpg']['originalFile'] : false;
1809
  $bkFolder = $this->getBackupFolderAny($file, $sizes);
1969
  }
1970
  return false;
1971
  }
1972
+
1973
  protected function renameWithRetina($bkFile, $file) {
1974
  @rename($bkFile, $file);
1975
  @rename($this->retinaName($bkFile), $this->retinaName($file));
1976
+
1977
  }
1978
 
1979
  protected function retinaName($file) {
1982
  }
1983
 
1984
  public function doCustomRestore($ID) {
1985
+ //$meta = $this->spMetaDao->getMeta($ID);
1986
+ // meta facade as a custom image
1987
+ $itemHandler = new ShortPixelMetaFacade('C-' . $ID);
1988
+ $meta = $itemHandler->getMeta();
1989
+
1990
+ // do this before putting the meta down, since maybeDump check for last timestamp
1991
+ // do this before checks, so it can clear ahead, and in case or errors
1992
+ $URLsAndPATHs = $itemHandler->getURLsAndPATHs(false);
1993
+ $this->maybeDumpFromProcessedOnServer($itemHandler, $URLsAndPATHs);
1994
+
1995
+ // TODO On manual restore also put status to toRestore, then run this function.
1996
+ if(!$meta || ($meta->getStatus() != shortPixelMeta::FILE_STATUS_SUCCESS && $meta->getStatus() != shortpixelMeta::FILE_STATUS_TORESTORE ) )
1997
+ {
1998
+ return false;
1999
+ }
2000
+
2001
  $file = $meta->getPath();
2002
  $fullSubDir = str_replace(get_home_path(), "", dirname($file)) . '/';
2003
+ $bkFile = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . ShortPixelAPI::MB_basename($file);
2004
 
2005
  if(file_exists($bkFile)) {
2006
+ $rename_result = @rename($bkFile, $file);
2007
+ if (! $rename_result)
2008
+ {
2009
+ self::log('Failure on rename to : ' . $file);
2010
+ }
2011
+
2012
+
2013
+ /* [BS] Reset all generated image meta. Bring back to start state.
2014
+ * Since Wpdb->prepare doesn't support 'null', zero values in this table should not be trusted */
2015
+
2016
+ $meta->setTsOptimized(0);
2017
+ $meta->setCompressedSize(0);
2018
+ $meta->setCompressionType(0);
2019
+ $meta->setKeepExif(0);
2020
+ $meta->setCmyk2rgb(0);
2021
+ $meta->setMessage('');
2022
+ $meta->setRetries(0);
2023
+ $meta->setBackup(0);
2024
+ $meta->setResizeWidth(0);
2025
+ $meta->setResizeHeight(0);
2026
+ $meta->setResize(0);
2027
+
2028
  $meta->setStatus(3);
2029
  $this->spMetaDao->update($meta);
2030
+
2031
+
2032
  }
2033
+ else {
2034
+ self::log('File ' . $bkFile . ' not found in backups');
2035
+ }
2036
+
2037
  return $meta;
2038
  }
2039
+
2040
  public function handleRestoreBackup() {
2041
  $attachmentID = intval($_GET['attachment_ID']);
2042
+
2043
  self::log("Handle Restore Backup #{$attachmentID}");
2044
  $this->doRestore($attachmentID);
2045
 
2051
  wp_redirect($sendback);
2052
  // we are done
2053
  }
2054
+
2055
  public function handleRedo() {
2056
  self::log("Handle Redo #{$_GET['attachment_ID']} type {$_GET['type']}");
2057
+
2058
  die(json_encode($this->redo($_GET['attachment_ID'], $_GET['type'])));
2059
  }
2060
+
2061
  public function redo($qID, $type = false) {
2062
  $compressionType = ($type == 'lossless' ? 'lossless' : ($type == 'glossy' ? 'glossy' : 'lossy')); //sanity check
2063
 
2072
  $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
2073
  } else {
2074
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => __('Could not restore from backup: ','shortpixel-image-optimiser') . $qID);
2075
+ }
2076
  } else {
2077
  $ID = intval($qID);
2078
  $meta = $this->doRestore($ID);
2091
  //wp_update_attachment_metadata($ID, $meta);
2092
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
2093
  $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
2094
+ }
2095
  } else {
2096
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => __('Could not restore from backup: ','shortpixel-image-optimiser') . $ID);
2097
  }
2098
  }
2099
  return $ret;
2100
  }
2101
+
2102
  public function handleOptimizeThumbs() {
2103
  $ID = intval($_GET['attachment_ID']);
2104
  $meta = wp_get_attachment_metadata($ID);
2105
  //die(var_dump($meta));
2106
  $thumbsCount = WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
2107
+ if( isset($meta['ShortPixelImprovement'])
2108
  && isset($meta['sizes']) && $thumbsCount
2109
  && ( !isset($meta['ShortPixel']['thumbsOpt']) || $meta['ShortPixel']['thumbsOpt'] == 0
2110
  || (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && $meta['ShortPixel']['thumbsOpt'] < $thumbsCount))) { //optimized without thumbs, thumbs exist
2124
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
2125
  }
2126
  $ret = array("Status" => ShortPixelAPI::STATUS_FAIL, "Message" => $e->getMessage());
2127
+ }
2128
  } else {
2129
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "message" => (isset($meta['ShortPixelImprovement']) ? __('No thumbnails to optimize for ID: ','shortpixel-image-optimiser') : __('Please optimize image for ID: ','shortpixel-image-optimiser')) . $ID);
2130
  }
2131
  die(json_encode($ret));
2132
  }
2133
+
2134
  public function handleCheckQuota() {
2135
  $this->getQuotaInformation();
2136
  // store the referring webpage location
2151
  $meta = wp_get_attachment_metadata($ID);
2152
 
2153
 
2154
+ if(self::_isProcessable($ID) != false) //we use the static isProcessable to bypass the exclude patterns
2155
  {
2156
  try {
2157
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
2158
+
2159
  @unlink(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file));
2160
+
2161
  if ( !empty($meta['file']) )
2162
  {
2163
  $filesPath = SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir;//base BACKUP path
2167
  @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));//remove thumbs
2168
  }
2169
  }
2170
+ }
2171
+
2172
  } catch(Exception $e) {
2173
  //what to do, what to do?
2174
  }
2175
  }
2176
  }
2177
+
2178
  public function deactivatePlugin() {
2179
  if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'sp_deactivate_plugin_nonce' ) ) {
2180
  wp_nonce_ays( '' );
2205
  if( !(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) && is_array($this->_settings->currentStats)
2206
  && $this->_settings->currentStats['optimizePdfs'] == $this->_settings->optimizePdfs
2207
  && isset($this->_settings->currentStats['time'])
2208
+ && (time() - $this->_settings->currentStats['time'] < $time))
2209
  {
2210
  return $this->_settings->currentStats;
2211
  } else {
2220
  if($this->_settings->hasCustomFolders) {
2221
  $customImageCount = $this->spMetaDao->countAllProcessableFiles();
2222
  foreach($customImageCount as $key => $val) {
2223
+ $quotaData[$key] = isset($quotaData[$key])
2224
  ? (is_array($quotaData[$key])
2225
  ? array_merge($quotaData[$key], $val)
2226
  : (is_numeric($quotaData[$key])
2233
  return $quotaData;
2234
  }
2235
  }
2236
+
2237
  public function checkQuotaAndAlert($quotaData = null, $recheck = false, $refreshFiles = 300) {
2238
  if(!$quotaData) {
2239
  $quotaData = $this->getQuotaInformation();
2259
  }
2260
  return $quotaData;
2261
  }
2262
+
2263
  public function isValidMetaId($id) {
2264
  return substr($id, 0, 2 ) == "C-" ? $this->spMetaDao->getMeta(substr($id, 2)) : wp_get_attachment_url($id);
2265
  }
2267
  public function listCustomMedia() {
2268
  if( ! class_exists( 'ShortPixelListTable' ) ) {
2269
  require_once('view/shortpixel-list-table.php');
2270
+ }
2271
+ if(isset($_REQUEST['refresh']) && esc_attr($_REQUEST['refresh']) == 1) {
2272
  $notice = null;
2273
  $this->refreshCustomFolders($notice);
2274
  }
2276
  //die(ShortPixelMetaFacade::queuedId(ShortPixelMetaFacade::CUSTOM_TYPE, $_REQUEST['image']));
2277
  $this->prioQ->push(ShortPixelMetaFacade::queuedId(ShortPixelMetaFacade::CUSTOM_TYPE, $_REQUEST['image']));
2278
  }
2279
+
2280
  $customMediaListTable = new ShortPixelListTable($this, $this->spMetaDao, $this->hasNextGen);
2281
  $items = $customMediaListTable->prepare_items();
2282
  if ( isset($_GET['noheader']) ) {
2321
  </div>
2322
  </div> <?php
2323
  }
2324
+
2325
+ /** Front End function that controls bulk processes.
2326
+ *
2327
+ */
2328
  public function bulkProcess() {
2329
  global $wpdb;
2330
 
2332
  ShortPixelView::displayActivationNotice();
2333
  return;
2334
  }
2335
+
2336
  $quotaData = $this->checkQuotaAndAlert(null, isset($_GET['checkquota']), 0);
2337
  //if($this->_settings->quotaExceeded != 0) {
2338
  //return;
2339
  //}
2340
+
2341
+ if(isset($_POST['bulkProcessPause']))
2342
  {//pause an ongoing bulk processing, it might be needed sometimes
2343
  $this->prioQ->pauseBulk();
2344
  if($this->_settings->hasCustomFolders && $this->spMetaDao->getPendingMetaCount()) {
2346
  }
2347
  }
2348
 
2349
+ if(isset($_POST['bulkProcessStop']))
2350
  {//stop an ongoing bulk processing
2351
  $this->prioQ->stopBulk();
2352
  if($this->_settings->hasCustomFolders && $this->spMetaDao->getPendingMetaCount()) {
2355
  $this->_settings->cancelPointer = NULL;
2356
  }
2357
 
2358
+ if(isset($_POST["bulkProcess"]))
2359
  {
2360
+ //set the thumbnails option
2361
  if ( isset($_POST['thumbnails']) ) {
2362
  $this->_settings->processThumbnails = 1;
2363
  } else {
2366
  //clean the custom files errors in order to process them again
2367
  if($this->_settings->hasCustomFolders) {
2368
  $this->spMetaDao->resetFailed();
2369
+ $this->spMetaDao->resetRestored();
2370
+
2371
  }
2372
+
2373
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_OPTIMIZE);
2374
  $this->_settings->customBulkPaused = 0;
2375
  self::log("BULK: Start: " . $this->prioQ->getStartBulkId() . ", stop: " . $this->prioQ->getStopBulkId() . " PrioQ: "
2376
  .json_encode($this->prioQ->get()));
2377
+ }//end bulk process was clicked
2378
+
2379
+ if(isset($_POST["bulkRestore"]))
2380
  {
2381
+ $bulkRestore = new \ShortPixel\BulkRestoreAll();
2382
+ $bulkRestore->setShortPixel($this);
2383
+ $bulkRestore->setupBulk();
2384
+
2385
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_RESTORE);
2386
  $this->_settings->customBulkPaused = 0;
2387
+ }//end bulk restore was clicked
2388
+
2389
+ if(isset($_POST["bulkCleanup"]))
2390
  {
2391
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_CLEANUP);
2392
  $this->_settings->customBulkPaused = 0;
2393
+ }//end bulk restore was clicked
2394
 
2395
  if(isset($_POST["bulkCleanupPending"]))
2396
  {
2404
  $this->_settings->customBulkPaused = 0;
2405
  }//resume was clicked
2406
 
2407
+ if(isset($_POST["skipToCustom"]))
2408
  {
2409
  $this->_settings->skipToCustom = true;
2410
  }//resume was clicked
2423
  {
2424
  $msg = $this->bulkProgressMessage($this->prioQ->getDeltaBulkPercent(), $this->prioQ->getTimeRemaining());
2425
 
2426
+ $this->view->displayBulkProcessingRunning($this->getPercent($quotaData), $msg, $quotaData['APICallsRemaining'], $this->getAverageCompression(),
2427
+ $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_RESTORE ? 0 :
2428
  ( $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP
2429
  || $this->prioQ->getBulkType() == ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING ? -1 : ($pendingMeta !== null ? ($this->prioQ->bulkRunning() ? 3 : 2) : 1)), $quotaData);
2430
 
2431
+ } else
2432
  {
2433
  if($this->prioQ->bulkRan() && !$this->prioQ->bulkPaused()) {
2434
  $this->prioQ->markBulkComplete();
2435
  }
2436
 
2437
+ //image count
2438
  $thumbsProcessedCount = $this->_settings->thumbsCount;//amount of optimized thumbnails
2439
  $under5PercentCount = $this->_settings->under5Percent;//amount of under 5% optimized imgs.
2440
 
2442
  $averageCompression = self::getAverageCompression();
2443
  $percent = $this->prioQ->bulkPaused() ? $this->getPercent($quotaData) : false;
2444
 
2445
+ // [BS] If some template part is around, use it and find the controller.
2446
+ $template_part = isset($_GET['part']) ? sanitize_text_field($_GET['part']) : false;
2447
+ $controller = ShortPixelTools::namespaceit('ShortPixelController');
2448
+ $partControl = $controller::findControllerbySlug($template_part);
2449
+
2450
+ if ($partControl)
2451
+ {
2452
+ $viewObj = new $partControl();
2453
+ $viewObj->setShortPixel($this);
2454
+ $viewObj->loadView();
2455
+ }
2456
+
2457
+ if (! $template_part)
2458
+ {
2459
+ $this->view->displayBulkProcessingForm($quotaData, $thumbsProcessedCount, $under5PercentCount,
2460
  $this->prioQ->bulkRan(), $averageCompression, $this->_settings->fileCount,
2461
  self::formatBytes($this->_settings->savedSpace), $percent, $pendingMeta);
2462
+ }
2463
  }
2464
  }
2465
  //end bulk processing
2466
+
2467
  public function getPercent($quotaData) {
2468
  if($this->_settings->processThumbnails) {
2469
  return $quotaData["totalFiles"] ? min(99, round($quotaData["totalProcessedFiles"] *100.0 / $quotaData["totalFiles"])) : 0;
2471
  return $quotaData["mainFiles"] ? min(99, round($quotaData["mainProcessedFiles"] *100.0 / $quotaData["mainFiles"])) : 0;
2472
  }
2473
  }
2474
+
2475
  public function bulkProgressMessage($percent, $minutes) {
2476
  $timeEst = "";
2477
  self::log("bulkProgressMessage(): percent: " . $percent);
2492
  }
2493
  return $timeEst;
2494
  }
2495
+
2496
  public function emptyBackup(){
2497
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2498
+
2499
  //extract all images from DB in an array. of course
2500
  // Simon: WHY?!!! commenting for now...
2501
  /*
2506
  'post_mime_type' => 'image'
2507
  ));
2508
  */
2509
+
2510
  //delete the actual files on disk
2511
  $this->deleteDir(SHORTPIXEL_BACKUP_FOLDER);//call a recursive function to empty files and sub-dirs in backup dir
2512
  }
2513
  }
2514
+
2515
  public function backupFolderIsEmpty() {
2516
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2517
  return count(scandir(SHORTPIXEL_BACKUP_FOLDER)) > 2 ? false : true;
2524
  }
2525
  die(self::formatBytes(self::folderSize(SHORTPIXEL_BACKUP_FOLDER)));
2526
  }
2527
+
2528
  public function browseContent() {
2529
  if ( !current_user_can( 'manage_options' ) ) {
2530
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2531
  }
2532
+
2533
  $root = self::getCustomFolderBase();
2534
+
2535
 
2536
  $postDir = rawurldecode($root.(isset($_POST['dir']) ? trim($_POST['dir']) : null ));
2537
  // set checkbox if multiSelect set to true
2543
 
2544
  $files = scandir($postDir);
2545
  $returnDir = substr($postDir, strlen($root));
2546
+
2547
  natcasesort($files);
2548
 
2549
  if( count($files) > 2 ) { // The 2 accounts for . and ..
2551
  foreach( $files as $file ) {
2552
 
2553
  if($file == 'ShortpixelBackups' || ShortPixelMetaFacade::isMediaSubfolder($postDir . $file, false)) continue;
2554
+
2555
  $htmlRel = str_replace("'", "&apos;", $returnDir . $file);
2556
  $htmlName = htmlentities($file);
2557
  $ext = preg_replace('/^.*\./', '', $file);
2571
  }
2572
  die();
2573
  }
2574
+
2575
  public function getComparerData() {
2576
  if (!isset($_POST['id']) || !current_user_can( 'upload_files' ) && !current_user_can( 'edit_posts' ) ) {
2577
  wp_die(json_encode((object)array('origUrl' => false, 'optUrl' => false, 'width' => 0, 'height' => 0)));
2578
  }
2579
+
2580
  $ret = array();
2581
  $handle = new ShortPixelMetaFacade($_POST['id']);
2582
+
2583
  $meta = $handle->getMeta();
2584
  $rawMeta = $handle->getRawMeta();
2585
  $backupUrl = content_url() . "/" . SHORTPIXEL_UPLOADS_NAME . "/" . SHORTPIXEL_BACKUP . "/";
2586
  $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
2587
  $urlBkPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
2588
  $ret['origUrl'] = $backupUrl . $urlBkPath . $meta->getName();
2589
+ if ($meta->getType() == ShortPixelMetaFacade::CUSTOM_TYPE)
2590
+ {
2591
+ $ret['optUrl'] = $uploadsUrl . $meta->getWebPath();
2592
+ self::log('Getting image - ' . $urlBkPath . $meta->getPath());
2593
+ // [BS] Another bug? Width / Height not stored in Shortpixel meta.
2594
+ $ret['width'] = $meta->getActualWidth();
2595
+ $ret['height'] = $meta->getActualHeight();
2596
+
2597
+ if (is_null($ret['width']))
2598
+ {
2599
+
2600
+ // $imageSizes = getimagesize($ret['optUrl']);
2601
+ // [BS] Fix - Use real path instead of URL on getimagesize.
2602
+ $imageSizes = getimagesize($meta->getPath());
2603
+
2604
+ if ($imageSizes)
2605
+ {
2606
+ $ret['width'] = $imageSizes[0];
2607
+ $ret['height']= $imageSizes[1];
2608
+ }
2609
+ }
2610
+ }
2611
+ else
2612
+ {
2613
+ $ret['optUrl'] = wp_get_attachment_url( $_POST['id'] ); //$uploadsUrl . $urlBkPath . $meta->getName();
2614
+ $ret['width'] = $rawMeta['width'];
2615
+ $ret['height'] = $rawMeta['height'];
2616
+ }
2617
 
2618
  die(json_encode((object)$ret));
2619
  }
2620
+
2621
  public function newApiKey() {
2622
  if ( !current_user_can( 'manage_options' ) ) {
2623
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2653
  if($body->Status == 'success') {
2654
  $key = trim($body->Details);
2655
  $validityData = $this->getQuotaInformation($key, true, true);
2656
+ if($validityData['APIKeyValid']) {
2657
  $this->_settings->apiKey = $key;
2658
  $this->_settings->verifiedKey = true;
2659
  }
2660
  }
2661
  die(json_encode($body));
2662
+
2663
  }
2664
+
2665
  public function proposeUpgrade() {
2666
  if ( !current_user_can( 'manage_options' ) ) {
2667
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2668
  }
2669
+
2670
  $stats = $this->countAllIfNeeded($this->_settings->currentStats, 300);
2671
 
2672
  //$proposal = wp_remote_post($this->_settings->httpProto . "://shortpixel.com/propose-upgrade-frag", array(
2708
  die($proposal['body']);
2709
 
2710
  }
2711
+
2712
  public static function getCustomFolderBase() {
2713
  if(is_main_site()) {
2714
  $base = get_home_path();
2718
  return realpath($up['basedir']);
2719
  }
2720
  }
2721
+
2722
  protected function fullRefreshCustomFolder($path, &$notice) {
2723
  $folder = $this->spMetaDao->getFolder($path);
2724
  $diff = $folder->checkFolderContents(array('ShortPixelCustomMetaDao', 'getPathFiles'));
2725
  }
2726
+
2727
  protected function refreshCustomFolders(&$notice, $ignore = false) {
2728
  $customFolders = array();
2729
  if($this->_settings->hasCustomFolders) {
2754
  }
2755
 
2756
  protected static function alterHtaccess( $clear = false ){
2757
+ // [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
2758
+ $upload_dir = wp_upload_dir();
2759
+ $upload_base = trailingslashit($upload_dir['basedir']);
2760
+
2761
  if ( $clear ) {
2762
  insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
2763
+ insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', '');
2764
+ insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', '');
2765
  } else {
2766
+
2767
+ $rules = '
2768
  <IfModule mod_rewrite.c>
2769
  RewriteEngine On
2770
 
2802
  <IfModule mod_mime.c>
2803
  AddType image/webp .webp
2804
  </IfModule>
2805
+ ' ;
2806
+
2807
+ insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', $rules);
2808
+ insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', $rules);
2809
+ insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', $rules);
2810
  /* insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '
2811
  RewriteEngine On
2812
  RewriteBase /
2865
  if(isset($_GET['setsparchive'])) {
2866
  $this->_settings->downloadArchive = intval($_GET['setsparchive']);
2867
  }
2868
+
2869
  //check all custom folders and update meta table if files appeared
2870
  $customFolders = $this->refreshCustomFolders($notice, isset($_POST['removeFolder']) ? $_POST['removeFolder'] : null);
2871
+
2872
  if(isset($_POST['request']) && $_POST['request'] == 'request') {
2873
  //a new API Key was requested
2874
  if(filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
2875
+
2876
  }
2877
  else {
2878
+ $notice = array("status" => "error",
2879
  "msg" => __("Please provide a valid e-mail.",'shortpixel-image-optimiser')
2880
+ . "<BR> "
2881
  . __('For any question regarding obtaining your API Key, please contact us at ','shortpixel-image-optimiser')
2882
  . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
2883
+ . __(' or ','shortpixel-image-optimiser')
2884
  . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.");
2885
  }
2886
  }
2892
  $this->cloudflareApi->set_up($cfApi, $cfAuth, $cfZone);
2893
  }
2894
 
2895
+ if( isset($_POST['save']) || isset($_POST['saveAdv'])
2896
  || (isset($_POST['validate']) && $_POST['validate'] == "validate")
2897
  || isset($_POST['removeFolder']) || isset($_POST['recheckFolder'])) {
2898
 
2899
  //handle API Key - common for save and validate.
2900
  $_POST['key'] = trim(str_replace("*", "", isset($_POST['key']) ? $_POST['key'] : $this->_settings->apiKey)); //the API key might not be set if the editing is disabled.
2901
+
2902
  if ( strlen($_POST['key']) <> 20 ){
2903
  $KeyLength = strlen($_POST['key']);
2904
+
2905
+ $notice = array("status" => "error",
2906
  "msg" => sprintf(__("The key you provided has %s characters. The API key should have 20 characters, letters and numbers only.",'shortpixel-image-optimiser'), $KeyLength)
2907
+ . "<BR> <b>"
2908
+ . __('Please check that the API key is the same as the one you received in your confirmation email.','shortpixel-image-optimiser')
2909
+ . "</b><BR> "
2910
  . __('If this problem persists, please contact us at ','shortpixel-image-optimiser')
2911
  . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
2912
+ . __(' or ','shortpixel-image-optimiser')
2913
  . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.");
2914
  }
2915
  else {
2916
+
2917
  if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
2918
  //these are needed for the call to api-status, set them first.
2919
+ $this->_settings->siteAuthUser = (isset($_POST['siteAuthUser']) ? sanitize_text_field($_POST['siteAuthUser']) : $this->_settings->siteAuthUser);
2920
+ $this->_settings->siteAuthPass = (isset($_POST['siteAuthPass']) ? sanitize_text_field($_POST['siteAuthPass']) : $this->_settings->siteAuthPass);
2921
  }
2922
 
2923
  $validityData = $this->getQuotaInformation($_POST['key'], true, isset($_POST['validate']) && $_POST['validate'] == "validate", $_POST);
2924
+
2925
  $this->_settings->apiKey = $_POST['key'];
2926
  if($validityData['APIKeyValid']) {
2927
  if(isset($_POST['validate']) && $_POST['validate'] == "validate") {
2936
  $notice = array("status" => "warn", "msg" => __("API Key is valid but your site is not accessible from our servers. Please make sure that your server is accessible from the Internet before using the API or otherwise we won't be able to optimize them.",'shortpixel-image-optimiser'));
2937
  } else {
2938
  if ( function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY"))
2939
+ $notice = array("status" => "success", "msg" => __("Great, your API Key is valid! <br>You seem to be running a multisite, please note that API Key can also be configured in wp-config.php like this:",'shortpixel-image-optimiser')
2940
  . "<BR> <b>define('SHORTPIXEL_API_KEY', '".$this->_settings->apiKey."');</b>");
2941
  else
2942
  $notice = array("status" => "success", "msg" => __('Great, your API Key is valid. Please take a few moments to review the plugin settings below before starting to optimize your images.','shortpixel-image-optimiser'));
2945
  $this->_settings->verifiedKey = true;
2946
  //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
2947
  if ( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && !@mkdir(SHORTPIXEL_BACKUP_FOLDER, 0777, true) )
2948
+ $notice = array("status" => "error",
2949
+ "msg" => sprintf(__("There is something preventing us to create a new folder for backing up your original files.<BR>Please make sure that folder <b>%s</b> has the necessary write and read rights.",'shortpixel-image-optimiser'),
2950
  WP_CONTENT_DIR . '/' . SHORTPIXEL_UPLOADS_NAME ));
2951
  } else {
2952
  if(isset($_POST['validate'])) {
2959
 
2960
  //if save button - we process the rest of the form elements
2961
  if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
2962
+ $this->_settings->compressionType = intval($_POST['compressionType']);
2963
  if(isset($_POST['thumbnails'])) { $this->_settings->processThumbnails = 1; } else { $this->_settings->processThumbnails = 0; }
2964
  if(isset($_POST['backupImages'])) { $this->_settings->backupImages = 1; } else { $this->_settings->backupImages = 0; }
2965
  if(isset($_POST['cmyk2rgb'])) { $this->_settings->CMYKtoRGBconversion = 1; } else { $this->_settings->CMYKtoRGBconversion = 0; }
2966
  $this->_settings->keepExif = isset($_POST['removeExif']) ? 0 : 1;
2967
  //delete_option('wp-short-pixel-keep-exif');
2968
  $this->_settings->resizeImages = (isset($_POST['resize']) ? 1: 0);
2969
+ $this->_settings->resizeType = (isset($_POST['resize_type']) ? sanitize_text_field($_POST['resize_type']) : false);
2970
  $this->_settings->resizeWidth = (isset($_POST['width']) ? intval($_POST['width']): $this->_settings->resizeWidth);
2971
  $this->_settings->resizeHeight = (isset($_POST['height']) ? intval($_POST['height']): $this->_settings->resizeHeight);
2972
  $uploadPath = realpath(SHORTPIXEL_UPLOADS_BASE);
2973
 
2974
+ if(isset($_POST['nextGen'])) {
2975
  WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
2976
  $prevNextGen = $this->_settings->includeNextGen;
2977
+ $this->_settings->includeNextGen = 1;
2978
  $ret = $this->addNextGenGalleriesToCustom($prevNextGen);
2979
  $folderMsg = $ret["message"];
2980
  $customFolders = $ret["customFolders"];
2981
+ } else {
2982
+ $this->_settings->includeNextGen = 0;
2983
  }
2984
  if(isset($_POST['addCustomFolder']) && strlen($_POST['addCustomFolder']) > 0) {
2985
  $folderMsg = $this->spMetaDao->newFolderFromPath(stripslashes($_POST['addCustomFolder']), $uploadPath, self::getCustomFolderBase());
2987
  $notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
2988
  }
2989
  $customFolders = $this->spMetaDao->getFolders();
2990
+ $this->_settings->hasCustomFolders = time();
2991
  }
2992
+
2993
  $this->_settings->createWebp = (isset($_POST['createWebp']) ? 1: 0);
2994
 
2995
 
3034
  $this->_settings->optimizeUnlisted = (isset($_POST['optimizeUnlisted']) ? 1: 0);
3035
  $this->_settings->optimizePdfs = (isset($_POST['optimizePdfs']) ? 1: 0);
3036
  $this->_settings->png2jpg = (isset($_POST['png2jpg']) ? (isset($_POST['png2jpgForce']) ? 2 : 1): 0);
3037
+
3038
  //die(var_dump($_POST['excludePatterns']));
3039
+
3040
  if(isset($_POST['excludePatterns']) && strlen($_POST['excludePatterns'])) {
3041
+ $patterns = array();
3042
  $items = explode(',', $_POST['excludePatterns']);
3043
  foreach($items as $pat) {
3044
  $parts = explode(':', $pat);
3057
  $this->_settings->excludeSizes = (isset($_POST['excludeSizes']) ? $_POST['excludeSizes']: array());
3058
 
3059
  //Redirect to bulk processing if requested
3060
+ if( isset($_POST['save']) && $_POST['save'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')
3061
  || isset($_POST['saveAdv']) && $_POST['saveAdv'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')) {
3062
  wp_redirect("upload.php?page=wp-short-pixel-bulk");
3063
  exit();
3064
+ }
3065
  }
3066
+ if(isset($_POST['removeFolder']) && strlen(($_POST['removeFolder']))) {
3067
  $this->spMetaDao->removeFolder($_POST['removeFolder']);
3068
  $customFolders = $this->spMetaDao->getFolders();
3069
  $_POST["saveAdv"] = true;
3070
  }
3071
+ if(isset($_POST['recheckFolder']) && strlen(($_POST['recheckFolder']))) {
3072
  //$folder->fullRefreshCustomFolder($_POST['recheckFolder']); //aici singura solutie pare callback care spune daca exita url-ul complet
3073
  }
3074
  }
3077
  if(isset($_REQUEST['noheader'])) {
3078
  require_once(ABSPATH . 'wp-admin/admin-header.php');
3079
  }
3080
+
3081
  //empty backup
3082
  if(isset($_POST['emptyBackup'])) {
3083
  $this->emptyBackup();
3084
  }
3085
+
3086
  $quotaData = $this->checkQuotaAndAlert(isset($validityData) ? $validityData : null, isset($_GET['checkquota']));
3087
+
3088
  if($this->hasNextGen) {
3089
  $ngg = array_map(array('ShortPixelNextGenAdapter','pathToAbsolute'), ShortPixelNextGenAdapter::getGalleries());
3090
  //die(var_dump($ngg));
3098
  $showApiKey = ( (is_main_site() || (function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY")))
3099
  && !defined("SHORTPIXEL_HIDE_API_KEY"));
3100
  $editApiKey = !defined("SHORTPIXEL_API_KEY") && $showApiKey;
3101
+
3102
  if($this->_settings->verifiedKey) {
3103
  $fileCount = number_format($this->_settings->fileCount);
3104
  $savedSpace = self::formatBytes($this->_settings->savedSpace,2);
3119
  $cloudflareAPI = true;
3120
 
3121
  $this->view->displaySettings($showApiKey, $editApiKey,
3122
+ $quotaData, $notice, $resources, $averageCompression, $savedSpace, $savedBandwidth, $remainingImages,
3123
+ $totalCallsMade, $fileCount, null /*folder size now on AJAX*/, $customFolders,
3124
  $folderMsg, $folderMsg ? $addedFolder : false, isset($_POST['saveAdv']), $cloudflareAPI, $htaccessWriteable, $isNginx );
3125
  } else {
3126
+ $this->view->displaySettings($showApiKey, $editApiKey, $quotaData, $notice);
3127
  }
3128
+
3129
  }
3130
 
3131
  public function addNextGenGalleriesToCustom($silent) {
3132
+ $customFolders = array();
3133
  $folderMsg = "";
3134
  if($this->_settings->includeNextGen) {
3135
  //add the NextGen galleries to custom folders
3140
  $msg = $this->spMetaDao->newFolderFromPath($gallery, ABSPATH, self::getCustomFolderBase());
3141
  }
3142
  $folderMsg .= $msg;
3143
+ $this->_settings->hasCustomFolders = time();
3144
  }
3145
  $customFolders = $this->spMetaDao->getFolders();
3146
  }
3147
  return array("message" => $silent? "" : $folderMsg, "customFolders" => $customFolders);
3148
  }
3149
+
3150
  public function getAverageCompression(){
3151
+ return $this->_settings->totalOptimized > 0
3152
+ ? round(( 1 - ( $this->_settings->totalOptimized / $this->_settings->totalOriginal ) ) * 100, 2)
3153
  : 0;
3154
  }
3155
+
3156
  /**
3157
+ *
3158
  * @param type $apiKey
3159
  * @param type $appendUserAgent
3160
  * @param type $validate - true if we are validating the api key, send also the domain name and number of pics
3161
  * @return type
3162
  */
3163
  public function getQuotaInformation($apiKey = null, $appendUserAgent = false, $validate = false, $settings = false) {
3164
+
3165
  if(is_null($apiKey)) { $apiKey = $this->_settings->apiKey; }
3166
+
3167
  if($this->_settings->httpProto != 'https' && $this->_settings->httpProto != 'http') {
3168
  $this->_settings->httpProto = 'https';
3169
  }
3191
  $argsStr .= "&host={$args['body']['host']}";
3192
  if(strlen($this->_settings->siteAuthUser)) {
3193
  $args['body']['user'] = $this->_settings->siteAuthUser;
3194
+ $args['body']['pass'] = $this->_settings->siteAuthPass;
3195
+ $argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
3196
  }
3197
  if($settings !== false) {
3198
  $args['body']['Settings'] = $settings;
3208
  $response = wp_remote_post($requestURL, $args);
3209
 
3210
  $comm['A: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
3211
+
3212
  //some hosting providers won't allow https:// POST connections so we try http:// as well
3213
  if(is_wp_error( $response )) {
3214
  //echo("protocol " . $this->_settings->httpProto . " failed. switching...");
3215
+ $requestURL = $this->_settings->httpProto == 'https' ?
3216
  str_replace('https://', 'http://', $requestURL) :
3217
  str_replace('http://', 'https://', $requestURL);
3218
  // add or remove the sslverify
3221
  } else {
3222
  unset($args['sslverify']);
3223
  }
3224
+ $response = wp_remote_post($requestURL, $args);
3225
  $comm['B: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
3226
+
3227
  if(!is_wp_error( $response )){
3228
  $this->_settings->httpProto = ($this->_settings->httpProto == 'https' ? 'http' : 'https');
3229
  //echo("protocol " . $this->_settings->httpProto . " succeeded");
3230
  } else {
3231
+ //echo("protocol " . $this->_settings->httpProto . " failed too");
3232
  }
3233
  }
3234
  //Second fallback to HTTP get
3257
  $defaultData = is_array($this->_settings->currentStats) ? array_merge( $this->_settings->currentStats, $defaultData) : $defaultData;
3258
 
3259
  if(is_object($response) && get_class($response) == 'WP_Error') {
3260
+
3261
  $urlElements = parse_url($requestURL);
3262
  $portConnect = @fsockopen($urlElements['host'],8,$errno,$errstr,15);
3263
  if(!$portConnect) {
3281
  return $defaultData;
3282
  }
3283
 
3284
+ if ( ( $data->APICallsMade + $data->APICallsMadeOneTime ) < ( $data->APICallsQuota + $data->APICallsQuotaOneTime ) ) //reset quota exceeded flag -> user is allowed to process more images.
3285
  $this->resetQuotaExceeded();
3286
  else
3287
+ $this->_settings->quotaExceeded = 1;//activate quota limiting
3288
 
3289
  //if a non-valid status exists, delete it
3290
  $lastStatus = $this->_settings->bulkLastStatus = null;
3313
 
3314
  return $dataArray;
3315
  }
3316
+
3317
  public function resetQuotaExceeded() {
3318
  if( $this->_settings->quotaExceeded == 1) {
3319
  $dismissed = $this->_settings->dismissedNotices ? $this->_settings->dismissedNotices : array();
3333
  return;
3334
  }
3335
 
3336
+ $file = get_attached_file($id);
3337
  $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
3338
 
3339
  if($extended && isset($_GET['SHORTPIXEL_DEBUG'])) {
3353
  $this->view->renderCustomColumn($id, $renderData, $extended);
3354
  return;
3355
  }
3356
+
3357
  //empty data means document, we handle only PDF
3358
  elseif (empty($data)) { //TODO asta devine if si decomentam returnurile
3359
  if($fileExtension == "pdf") {
3360
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3361
  $renderData['message'] = __('PDF not processed.','shortpixel-image-optimiser');
3362
+ }
3363
  else { //Optimization N/A
3364
  $renderData['status'] = 'n/a';
3365
  }
3366
  $this->view->renderCustomColumn($id, $renderData, $extended);
3367
  return;
3368
+ }
3369
+
3370
  if(!isset($data['ShortPixelImprovement'])) { //new image
3371
  $data['ShortPixelImprovement'] = '';
3372
  }
3373
+
3374
+ if( is_numeric($data['ShortPixelImprovement'])
3375
  && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing'])) //for images that erroneously have ShortPixelImprovement = 0 when WaitingProcessing
3376
  ) { //already optimized
3377
  $sizesCount = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
3387
  }
3388
  }
3389
  }
3390
+
3391
  $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
3392
  $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
3393
  $renderData['bonus'] = ($data['ShortPixelImprovement'] < 5);
3404
  $renderData['exifKept'] = isset($data['ShortPixel']['exifKept']) ? $data['ShortPixel']['exifKept'] : null;
3405
  $renderData['png2jpg'] = isset($data['ShortPixelPng2Jpg']) ? $data['ShortPixelPng2Jpg'] : 0;
3406
  $renderData['date'] = isset($data['ShortPixel']['date']) ? $data['ShortPixel']['date'] : null;
3407
+ $renderData['quotaExceeded'] = $quotaExceeded;
3408
  $webP = 0;
3409
  if($extended) {
3410
  if(file_exists(dirname($file) . '/' . ShortPixelAPI::MB_basename($file, '.'.$fileExtension) . '.webp' )){
3457
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3458
  $sizes = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
3459
  $renderData['thumbsTotal'] = $sizes;
3460
+ $renderData['message'] = ($fileExtension == "pdf" ? 'PDF' : __('Image','shortpixel-image-optimiser'))
3461
  . __(' not processed.','shortpixel-image-optimiser')
3462
+ . ' (<a href="https://shortpixel.com/image-compression-test?site-url=' . urlencode(ShortPixelMetaFacade::safeGetAttachmentUrl($id)) . '" target="_blank">'
3463
  . __('Test&nbsp;for&nbsp;free','shortpixel-image-optimiser') . '</a>)';
3464
+ }
3465
  $this->view->renderCustomColumn($id, $renderData, $extended);
3466
  }
3467
  }
3536
  );
3537
  }
3538
  }
3539
+
3540
  function shortpixelInfoBoxContent( $post ) {
3541
  $this->generateCustomColumn( 'wp-shortPixel', $post->ID, true );
3542
  }
3543
+
3544
  public function onDeleteImage($post_id) {
3545
  $itemHandler = new ShortPixelMetaFacade($post_id);
3546
  $urlsPaths = $itemHandler->getURLsAndPATHs(true, false, true, array(), true);
3572
  public function columns( $defaults ) {
3573
  $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
3574
  if(current_user_can( 'manage_options' )) {
3575
+ $defaults['wp-shortPixel'] .=
3576
+ '&nbsp;<a href="options-general.php?page=wp-shortpixel#stats" title="'
3577
+ . __('ShortPixel Statistics','shortpixel-image-optimiser')
3578
  . '"><span class="dashicons dashicons-dashboard"></span></a>';
3579
  }
3580
  return $defaults;
3591
  public function nggCountColumns( $count ) {
3592
  return $count + 1;
3593
  }
3594
+
3595
  public function nggColumnHeader( $default ) {
3596
  return __('ShortPixel Compression','shortpixel-image-optimiser');
3597
  }
3598
 
3599
  public function nggColumnContent( $unknown, $picture ) {
3600
+
3601
  $meta = $this->spMetaDao->getMetaForPath($picture->imagePath);
3602
  if($meta) {
3603
  switch($meta->getStatus()) {
3614
  'thumbsTotal' => 0,
3615
  'retinasOpt' => 0,
3616
  'backup' => true
3617
+ ));
3618
  break;
3619
  }
3620
  } else {
3626
  'thumbsTotal' => 0,
3627
  'retinasOpt' => 0,
3628
  'message' => "Not optimized"
3629
+ ));
3630
  }
3631
  // return var_dump($meta);
3632
  }
3648
 
3649
  return round($bytes, $precision) . ' ' . $units[$pow];
3650
  }
3651
+
3652
  public function isProcessable($ID, $excludeExtensions = array()) {
3653
  $excludePatterns = $this->_settings->excludePatterns;
3654
  return self::_isProcessable($ID, $excludeExtensions, $excludePatterns);
3655
  }
3656
+
3657
  public function isProcessablePath($path, $excludeExtensions = array()) {
3658
  $excludePatterns = $this->_settings->excludePatterns;
3659
  return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
3660
  }
3661
+
3662
  static public function _isProcessable($ID, $excludeExtensions = array(), $excludePatterns = array(), $meta = false) {
3663
  $path = get_attached_file($ID);//get the full file PATH
3664
  if(isset($excludePatterns) && is_array($excludePatterns)) {
3672
  }
3673
  }
3674
  }
3675
+ }
3676
  return $path ? self::_isProcessablePath($path, $excludeExtensions, $excludePatterns) : false;
3677
  }
3678
+
3679
  static public function _isProcessablePath($path, $excludeExtensions = array(), $excludePatterns = array()) {
3680
  $pathParts = pathinfo($path);
3681
  $ext = isset($pathParts['extension']) ? $pathParts['extension'] : false;
3705
  $heightBounds = isset($ranges[1]) ? explode("-", $ranges[1]) : false;
3706
  if(!isset($heightBounds[1])) $heightBounds[1] = $heightBounds[0];
3707
  if( $width >= 0 + $widthBounds[0] && $width <= 0 + $widthBounds[1]
3708
+ && ( $heightBounds === false
3709
  || ($height >= 0 + $heightBounds[0] && $height <= 0 + $heightBounds[1]))) {
3710
  return false;
3711
  }
3722
  public function getURLsAndPATHs($itemHandler, $meta = NULL, $onlyThumbs = false) {
3723
  return $itemHandler->getURLsAndPATHs($this->_settings->processThumbnails, $onlyThumbs, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
3724
  }
3725
+
3726
 
3727
  public static function deleteDir($dirPath) {
3728
  if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
3748
  }
3749
  $cleanPath = rtrim($path, '/'). '/';
3750
  foreach($files as $t) {
3751
+ if ($t<>"." && $t<>"..")
3752
  {
3753
  $currentFile = $cleanPath . $t;
3754
  if (is_dir($currentFile)) {
3763
  }
3764
  return $total_size;
3765
  }
3766
+
3767
  public function migrateBackupFolder() {
3768
  $oldBackupFolder = WP_CONTENT_DIR . '/' . SHORTPIXEL_BACKUP;
3769
 
3790
  @mkdir(SHORTPIXEL_BACKUP_FOLDER);
3791
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/'.SHORTPIXEL_UPLOADS_NAME);
3792
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
3793
+ @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
3794
  }
3795
  }
3796
  //then create the wp-content level if not present
3799
  @mkdir(SHORTPIXEL_BACKUP_FOLDER);
3800
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/' . basename(WP_CONTENT_DIR));
3801
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
3802
+ @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
3803
  }
3804
  }
3805
  return;
3822
  }
3823
  return $sizes;
3824
  }
3825
+
3826
  function getMaxIntermediateImageSize() {
3827
  global $_wp_additional_image_sizes;
3828
 
3845
  return array('width' => max(100, $width), 'height' => max(100, $height));
3846
  }
3847
 
3848
+ public function getOtherCompressionTypes($compressionType = false) {
3849
  return array_values(array_diff(array(0, 1, 2), array(0 + $compressionType)));
3850
  }
3851
 
3931
  public function getApiKey() {
3932
  return $this->_settings->apiKey;
3933
  }
3934
+
3935
  public function getPrioQ() {
3936
  return $this->prioQ;
3937
  }
3938
+
3939
  public function backupImages() {
3940
  return $this->_settings->backupImages;
3941
  }
3943
  public function processThumbnails() {
3944
  return $this->_settings->processThumbnails;
3945
  }
3946
+
3947
  public function getCMYKtoRGBconversion() {
3948
  return $this->_settings->CMYKtoRGBconversion;
3949
  }
3950
+
3951
  public function getSettings() {
3952
  return $this->_settings;
3953
  }
3979
  public function hasNextGen() {
3980
  return $this->hasNextGen;
3981
  }
3982
+
3983
  public function getSpMetaDao() {
3984
  return $this->spMetaDao;
3985
  }
class/wp-shortpixel-settings.php CHANGED
@@ -61,7 +61,7 @@ class WPShortPixelSettings {
61
  'thumbsCount' => array('key' => 'wp-short-pixel-thumbnail-count', 'default' => 0, 'group' => 'state'),
62
  'under5Percent' => array('key' => 'wp-short-pixel-files-under-5-percent', 'default' => 0, 'group' => 'state'),
63
  'savedSpace' => array('key' => 'wp-short-pixel-savedSpace', 'default' => 0, 'group' => 'state'),
64
- 'averageCompression' => array('key' => 'wp-short-pixel-averageCompression', 'default' => null, 'group' => 'state'),
65
  'apiRetries' => array('key' => 'wp-short-pixel-api-retries', 'default' => 0, 'group' => 'state'),
66
  'totalOptimized' => array('key' => 'wp-short-pixel-total-optimized', 'default' => 0, 'group' => 'state'),
67
  'totalOriginal' => array('key' => 'wp-short-pixel-total-original', 'default' => 0, 'group' => 'state'),
61
  'thumbsCount' => array('key' => 'wp-short-pixel-thumbnail-count', 'default' => 0, 'group' => 'state'),
62
  'under5Percent' => array('key' => 'wp-short-pixel-files-under-5-percent', 'default' => 0, 'group' => 'state'),
63
  'savedSpace' => array('key' => 'wp-short-pixel-savedSpace', 'default' => 0, 'group' => 'state'),
64
+ //'averageCompression' => array('key' => 'wp-short-pixel-averageCompression', 'default' => null, 'group' => 'state'),
65
  'apiRetries' => array('key' => 'wp-short-pixel-api-retries', 'default' => 0, 'group' => 'state'),
66
  'totalOptimized' => array('key' => 'wp-short-pixel-total-optimized', 'default' => 0, 'group' => 'state'),
67
  'totalOriginal' => array('key' => 'wp-short-pixel-total-original', 'default' => 0, 'group' => 'state'),
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: compressor, image, compression, optimize, image optimizer, image optimiser
4
  Requires at least: 3.2.0
5
  Tested up to: 5.1
6
  Requires PHP: 5.2
7
- Stable tag: 4.12.8
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -241,6 +241,21 @@ The ShortPixel Image Optimiser plugin calls the following actions and filters:
241
 
242
  == Changelog ==
243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  = 4.12.8 =
245
 
246
  Release date: 25th February 2019
4
  Requires at least: 3.2.0
5
  Tested up to: 5.1
6
  Requires PHP: 5.2
7
+ Stable tag: 4.13.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
241
 
242
  == Changelog ==
243
 
244
+ = 4.13.0 =
245
+
246
+ Release date: 10th April 2019
247
+ * Bulk restore for the Other Media
248
+ * make the filename extension be updated when manually optimizing a PNG from Media Library, if the convert to JPG is active, without refreshing the page
249
+ * Integration with Regenerate Thumbnails Advanced new 2.0 beta version
250
+ * Add the rules for WebP in the WP-CONTENT .htaccess
251
+ * ShortPixel Other Media - display the time of optimization in the grid and offer option to sort by it
252
+ * Keep sort order when optimizing / refreshing page on Other Media
253
+ * offer the visual comparer for Other Media too
254
+ * resolve the Settings inconsistency in Other Media (settings displayed were from when adding the folder not from when actually optimizing)
255
+ * Make pressing Escape or clicking outside of any popup close it.
256
+ * Fixed: Restoring an Other Media item and then Optimizing it again optimizes it Lossless
257
+ * fix generating the WebP <picture> tags when the images are either on a subdomain or on a CDN domain having the same root domain as the main site.
258
+
259
  = 4.12.8 =
260
 
261
  Release date: 25th February 2019
res/css/short-pixel-bar.css CHANGED
@@ -41,3 +41,7 @@ li.shortpixel-toolbar-processing.shortpixel-alert > a.ab-item > div,
41
  width:100%;
42
  float:left;
43
  }
 
 
 
 
41
  width:100%;
42
  float:left;
43
  }
44
+ .short-pixel-notice-icon {
45
+ float:left;
46
+ margin: 10px 10px 10px 0;
47
+ }
res/css/short-pixel-bar.min.css CHANGED
@@ -1 +1 @@
1
- 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}
1
+ 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}.short-pixel-notice-icon{float:left;margin:10px 10px 10px 0}
res/css/short-pixel-modal.css ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ div.sp-modal-shade {
2
+ display: none; /* Hidden by default */
3
+ position: fixed; /* Stay in place */
4
+ z-index: 10; /* Sit on top */
5
+ left: 0;
6
+ top: 0;
7
+ width: 100%; /* Full width */
8
+ height: 100%; /* Full height */
9
+ overflow: auto; /* Enable scroll if needed */
10
+ background-color: rgb(0,0,0); /* Fallback color */
11
+ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
12
+ }
13
+ div.shortpixel-modal {
14
+ background-color: #fefefe;
15
+ /*margin: 8% auto; 15% from the top and centered */
16
+ padding: 20px;
17
+ border: 1px solid #888;
18
+ width: 30%; /* Could be more or less, depending on screen size */
19
+ min-width: 300px; /* Could be more or less, depending on screen size */
20
+ z-index: 100; /* Proper z-index */
21
+ position: fixed;
22
+ top: 10%;
23
+ left: 50%;
24
+ max-height: 90%;
25
+ overflow-y: auto;
26
+ }
27
+ div.shortpixel-modal .sp-close-button, div.shortpixel-modal .sp-close-upgrade-button {
28
+ float: right;
29
+ margin-top: 0px;
30
+ background: transparent;
31
+ border: none;
32
+ font-size: 22px;
33
+ line-height: 10px;
34
+ cursor: pointer;
35
+ }
36
+ div.shortpixel-modal .sptw-modal-spinner {
37
+ background-image: url("../img/spinner2.gif");
38
+ background-repeat: no-repeat;
39
+ background-position: center;
40
+ }
41
+ div.sp-modal-title {
42
+ font-size: 22px;
43
+ }
44
+ div.sp-modal-body {
45
+ margin-top: 20px;
46
+ }
res/css/short-pixel-modal.min.css ADDED
@@ -0,0 +1 @@
 
1
+ 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;padding:20px;border:1px solid #888;width:30%;min-width:300px;z-index:100;position:fixed;top:10%;left:50%;max-height:90%;overflow-y:auto}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}div.shortpixel-modal .sptw-modal-spinner{background-image:url("../img/spinner2.gif");background-repeat:no-repeat;background-position:center}div.sp-modal-title{font-size:22px}div.sp-modal-body{margin-top:20px}
res/css/short-pixel.css CHANGED
@@ -29,7 +29,7 @@
29
  background-color: #3e8e41;
30
  }
31
  */
32
-
33
  /* The container <div> - needed to position the dropdown content */
34
  .sp-dropdown {
35
  position: relative;
@@ -63,10 +63,10 @@
63
 
64
  div.fb-like {
65
  transform: scale(1.3);
66
- -ms-transform: scale(1.3);
67
- -webkit-transform: scale(1.3);
68
- -o-transform: scale(1.3);
69
- -moz-transform: scale(1.3);
70
  transform-origin: bottom left;
71
  -ms-transform-origin: bottom left;
72
  -webkit-transform-origin: bottom left;
@@ -282,56 +282,16 @@ div.shortpixel-rate-us > a:focus {
282
  margin-right: 5px;
283
  }
284
 
285
- div.sp-modal-shade {
286
- display: none; /* Hidden by default */
287
- position: fixed; /* Stay in place */
288
- z-index: 10; /* Sit on top */
289
- left: 0;
290
- top: 0;
291
- width: 100%; /* Full width */
292
- height: 100%; /* Full height */
293
- overflow: auto; /* Enable scroll if needed */
294
- background-color: rgb(0,0,0); /* Fallback color */
295
- background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
296
- }
297
- div.shortpixel-modal {
298
- background-color: #fefefe;
299
- margin: 8% auto; /* 15% from the top and centered */
300
- padding: 20px;
301
- border: 1px solid #888;
302
- width: 30%; /* Could be more or less, depending on screen size */
303
- min-width: 300px; /* Could be more or less, depending on screen size */
304
- }
305
- div.shortpixel-modal .sp-close-button, div.shortpixel-modal .sp-close-upgrade-button {
306
- float: right;
307
- margin-top: 0px;
308
- background: transparent;
309
- border: none;
310
- font-size: 22px;
311
- line-height: 10px;
312
- cursor: pointer;
313
- }
314
  .twentytwenty-horizontal .twentytwenty-before-label:before, .twentytwenty-horizontal .twentytwenty-after-label:before {
315
  font-family: inherit;
316
  font-size: 16px;
317
  }
318
- div.sp-modal-title {
319
- font-size: 22px;
320
- }
321
- div.sp-modal-body {
322
- margin-top: 20px;
323
- }
324
  .short-pixel-bulk-page p {
325
  margin: 0.6em 0;
326
  }
327
- div.shortpixel-modal .sptw-modal-spinner {
328
- background-image: url("../img/spinner2.gif");
329
- background-repeat: no-repeat;
330
- background-position: center;
331
- }
332
  .short-pixel-bulk-page form.start {
333
- display:table;
334
- content:" ";
335
  width:98%;
336
  background-color:white;
337
  padding:10px 10px 0;
@@ -340,8 +300,8 @@ div.shortpixel-modal .sptw-modal-spinner {
340
  }
341
 
342
  .bulk-stats-container{
343
- display: inline-block;
344
- min-width:450px;
345
  width: 45%;
346
  float:left;
347
  padding-right: 50px;
@@ -349,8 +309,8 @@ div.shortpixel-modal .sptw-modal-spinner {
349
  line-height: 1.5em;
350
  }
351
  .bulk-text-container{
352
- display: inline-block;
353
- min-width:440px;
354
  width: 45%;
355
  float:left;
356
  padding-right: 50px;
@@ -361,27 +321,27 @@ div.shortpixel-modal .sptw-modal-spinner {
361
  padding-bottom: 0.5em;
362
  }
363
  .bulk-wide {
364
- display: inline-block;
365
  width: 90%;
366
- float:left;
367
  margin-top: 25px;
368
  }
369
  .bulk-stats-container .bulk-label{
370
- width:220px;
371
  display:inline-block;
372
  }
373
  .bulk-stats-container .bulk-val{
374
- width:50px;
375
- display:inline-block;
376
  text-align: right;
377
  }
378
  .bulk-stats-container .bulk-total{
379
- font-weight: bold;
380
  margin-top:10px;
381
  margin-bottom:10px;
382
  }
383
  .wp-core-ui .bulk-play{
384
- display: inline;
385
  width: 310px;
386
  float:left;
387
  margin-bottom:20px;
@@ -392,7 +352,7 @@ div.shortpixel-modal .sptw-modal-spinner {
392
  border: 1px solid;
393
  border-radius: 5px;
394
  margin-top: 60px;
395
- padding: 5px 12px;
396
  }
397
  .wp-core-ui .bulk-play a.button{
398
  height:60px;
@@ -442,10 +402,6 @@ th.sorted.column-wp-shortPixel a {
442
  .wp-core-ui .bulk-play a.button .bulk-btn-txt span.total {
443
  font-size: 1.4em;
444
  }
445
- .short-pixel-notice-icon {
446
- float:left;
447
- margin: 10px 10px 10px 0;
448
- }
449
  .bulk-progress {
450
  padding: 20px 32px 17px;
451
  background-color: #ffffff;
@@ -544,8 +500,8 @@ th.sorted.column-wp-shortPixel a {
544
  }
545
  .bulk-slider .img-original,
546
  .bulk-slider .img-optimized{
547
- display:inline-block;
548
- margin-right:20px;
549
  text-align: center;
550
  }
551
  .bulk-slider .img-original div,
@@ -558,9 +514,9 @@ th.sorted.column-wp-shortPixel a {
558
  max-width: 300px;
559
  }
560
  .bulk-slider .img-info{
561
- display:inline-block;
562
  vertical-align: top;
563
- font-size: 48px;
564
  max-width: 150px;
565
  padding: 10px 0 0 20px;
566
  }
@@ -570,7 +526,7 @@ th.sorted.column-wp-shortPixel a {
570
  padding: 15px 0 0 20px;
571
  }
572
  p.settings-info {
573
- padding-top: 0px;
574
  color: #818181;
575
  font-size:13px !important;
576
  }
@@ -871,4 +827,4 @@ section#tab-resources p {
871
 
872
  @-moz-keyframes cssload-spin {
873
  100%{ -moz-transform: rotate(360deg); transform: rotate(360deg); }
874
- }
29
  background-color: #3e8e41;
30
  }
31
  */
32
+
33
  /* The container <div> - needed to position the dropdown content */
34
  .sp-dropdown {
35
  position: relative;
63
 
64
  div.fb-like {
65
  transform: scale(1.3);
66
+ -ms-transform: scale(1.3);
67
+ -webkit-transform: scale(1.3);
68
+ -o-transform: scale(1.3);
69
+ -moz-transform: scale(1.3);
70
  transform-origin: bottom left;
71
  -ms-transform-origin: bottom left;
72
  -webkit-transform-origin: bottom left;
282
  margin-right: 5px;
283
  }
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  .twentytwenty-horizontal .twentytwenty-before-label:before, .twentytwenty-horizontal .twentytwenty-after-label:before {
286
  font-family: inherit;
287
  font-size: 16px;
288
  }
 
 
 
 
 
 
289
  .short-pixel-bulk-page p {
290
  margin: 0.6em 0;
291
  }
 
 
 
 
 
292
  .short-pixel-bulk-page form.start {
293
+ display:table;
294
+ content:" ";
295
  width:98%;
296
  background-color:white;
297
  padding:10px 10px 0;
300
  }
301
 
302
  .bulk-stats-container{
303
+ display: inline-block;
304
+ min-width:450px;
305
  width: 45%;
306
  float:left;
307
  padding-right: 50px;
309
  line-height: 1.5em;
310
  }
311
  .bulk-text-container{
312
+ display: inline-block;
313
+ min-width:440px;
314
  width: 45%;
315
  float:left;
316
  padding-right: 50px;
321
  padding-bottom: 0.5em;
322
  }
323
  .bulk-wide {
324
+ display: inline-block;
325
  width: 90%;
326
+ float:left;
327
  margin-top: 25px;
328
  }
329
  .bulk-stats-container .bulk-label{
330
+ width:220px;
331
  display:inline-block;
332
  }
333
  .bulk-stats-container .bulk-val{
334
+ width:50px;
335
+ display:inline-block;
336
  text-align: right;
337
  }
338
  .bulk-stats-container .bulk-total{
339
+ font-weight: bold;
340
  margin-top:10px;
341
  margin-bottom:10px;
342
  }
343
  .wp-core-ui .bulk-play{
344
+ display: inline;
345
  width: 310px;
346
  float:left;
347
  margin-bottom:20px;
352
  border: 1px solid;
353
  border-radius: 5px;
354
  margin-top: 60px;
355
+ padding: 5px 12px;
356
  }
357
  .wp-core-ui .bulk-play a.button{
358
  height:60px;
402
  .wp-core-ui .bulk-play a.button .bulk-btn-txt span.total {
403
  font-size: 1.4em;
404
  }
 
 
 
 
405
  .bulk-progress {
406
  padding: 20px 32px 17px;
407
  background-color: #ffffff;
500
  }
501
  .bulk-slider .img-original,
502
  .bulk-slider .img-optimized{
503
+ display:inline-block;
504
+ margin-right:20px;
505
  text-align: center;
506
  }
507
  .bulk-slider .img-original div,
514
  max-width: 300px;
515
  }
516
  .bulk-slider .img-info{
517
+ display:inline-block;
518
  vertical-align: top;
519
+ font-size: 48px;
520
  max-width: 150px;
521
  padding: 10px 0 0 20px;
522
  }
526
  padding: 15px 0 0 20px;
527
  }
528
  p.settings-info {
529
+ padding-top: 0px;
530
  color: #818181;
531
  font-size:13px !important;
532
  }
827
 
828
  @-moz-keyframes cssload-spin {
829
  100%{ -moz-transform: rotate(360deg); transform: rotate(360deg); }
830
+ }
res/css/short-pixel.min.css CHANGED
@@ -1 +1 @@
1
- .reset{font-weight:normal;font-style:normal}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.clearfix{zoom:1}.resumeLabel{float:right;line-height:30px;margin-right:20px;font-size:16px}.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}.sp-notice img{vertical-align:bottom}@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}.sp-conflict-plugins{display:table;border-spacing:10px;border-collapse:separate}.sp-conflict-plugins li{display:table-row}.sp-conflict-plugins li>*{display:table-cell}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 th{width:220px}.form-table td{position:relative}.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}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;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{display:inline-block;padding-top:6px}.wp-core-ui .bulk-play a.button .bulk-btn-txt{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;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%;margin:2em auto}article.sp-tabs section{position:absolute;display:block;top:1.8em;left:0;width:100%;max-width:100%;box-sizing:border-box;padding:10px 20px;z-index:0}article.sp-tabs section.sel-tab{box-shadow:0 3px 3px rgba(0,0,0,0.1)}article.sp-tabs section .wp-shortpixel-tab-content{visibility:hidden}article.sp-tabs section.sel-tab .wp-shortpixel-tab-content{visibility:visible}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}#tab-stats .sp-bulk-summary{position:absolute;right:0;top:0;z-index:100}.deliverWebpSettings,.deliverWebpTypes,.deliverWebpAlteringTypes{display:none}.deliverWebpTypes .sp-notice{color:red}.deliverWebpSettings{margin:16px 0}.deliverWebpSettings input:disabled+label{color:#818181}.deliverWebpTypes,.deliverWebpAlteringTypes{margin:16px 0 16px 16px}#png2jpg:not(:checked) ~ #png2jpgForce,#png2jpg:not(:checked) ~ label[for=png2jpgForce]{display:none}article.sp-tabs section #createWebp:checked ~ .deliverWebpSettings,article.sp-tabs section #deliverWebp:checked ~ .deliverWebpTypes,article.sp-tabs section #deliverWebpAltered:checked ~ .deliverWebpAlteringTypes{display:block}.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}.sp-column-actions-template+.sp-column-info{display: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
+ .reset{font-weight:normal;font-style:normal}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.clearfix{zoom:1}.resumeLabel{float:right;line-height:30px;margin-right:20px;font-size:16px}.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}.sp-notice img{vertical-align:bottom}@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}.sp-conflict-plugins{display:table;border-spacing:10px;border-collapse:separate}.sp-conflict-plugins li{display:table-row}.sp-conflict-plugins li>*{display:table-cell}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 th{width:220px}.form-table td{position:relative}.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}.twentytwenty-horizontal .twentytwenty-before-label:before,.twentytwenty-horizontal .twentytwenty-after-label:before{font-family:inherit;font-size:16px}.short-pixel-bulk-page p{margin:.6em 0}.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;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{display:inline-block;padding-top:6px}.wp-core-ui .bulk-play a.button .bulk-btn-txt{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}.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;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%;margin:2em auto}article.sp-tabs section{position:absolute;display:block;top:1.8em;left:0;width:100%;max-width:100%;box-sizing:border-box;padding:10px 20px;z-index:0}article.sp-tabs section.sel-tab{box-shadow:0 3px 3px rgba(0,0,0,0.1)}article.sp-tabs section .wp-shortpixel-tab-content{visibility:hidden}article.sp-tabs section.sel-tab .wp-shortpixel-tab-content{visibility:visible}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}#tab-stats .sp-bulk-summary{position:absolute;right:0;top:0;z-index:100}.deliverWebpSettings,.deliverWebpTypes,.deliverWebpAlteringTypes{display:none}.deliverWebpTypes .sp-notice{color:red}.deliverWebpSettings{margin:16px 0}.deliverWebpSettings input:disabled+label{color:#818181}.deliverWebpTypes,.deliverWebpAlteringTypes{margin:16px 0 16px 16px}#png2jpg:not(:checked) ~ #png2jpgForce,#png2jpg:not(:checked) ~ label[for=png2jpgForce]{display:none}article.sp-tabs section #createWebp:checked ~ .deliverWebpSettings,article.sp-tabs section #deliverWebp:checked ~ .deliverWebpTypes,article.sp-tabs section #deliverWebpAltered:checked ~ .deliverWebpAlteringTypes{display:block}.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}.sp-column-actions-template+.sp-column-info{display: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)}}
res/css/shortpixel-admin.css ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .short-pixel-bulk-page.bulk-restore-all ol li {
2
+ font-weight: 700; }
3
+ .short-pixel-bulk-page.bulk-restore-all section.select_folders {
4
+ margin: 20px 0; }
5
+ .short-pixel-bulk-page.bulk-restore-all section.select_folders .input {
6
+ margin: 10px 0 10px 15px;
7
+ font-size: 16px;
8
+ display: block;
9
+ clear: both; }
10
+ .short-pixel-bulk-page.bulk-restore-all section.select_folders .filecount {
11
+ font-size: 12px; }
12
+ .short-pixel-bulk-page.bulk-restore-all section.random_check .random_answer {
13
+ font-size: 16px;
14
+ font-weight: 700;
15
+ padding: 8px;
16
+ border: 1px solid #ccc;
17
+ display: inline-block; }
18
+ .short-pixel-bulk-page.bulk-restore-all section.random_check .inputs {
19
+ margin: 15px 0; }
20
+ .short-pixel-bulk-page.bulk-restore-all section.random_check .inputs span {
21
+ margin-right: 8px; }
22
+ .short-pixel-bulk-page.bulk-restore-all .button {
23
+ margin: 10px 0;
24
+ margin-right: 8px; }
25
+
26
+ /* Specific styles for advanced settings tab */
27
+ #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder {
28
+ margin: 10px 0; }
29
+ #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder .add-folder-text {
30
+ margin-left: 5px; }
31
+ #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder input[type="text"] {
32
+ width: 50em;
33
+ max-width: 70%; }
34
+ #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder input[name="saveAdv"] {
35
+ margin-left: 8px; }
res/css/shortpixel-admin.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .short-pixel-bulk-page.bulk-restore-all ol li{font-weight:700}.short-pixel-bulk-page.bulk-restore-all section.select_folders{margin:20px 0}.short-pixel-bulk-page.bulk-restore-all section.select_folders .input{margin:10px 0 10px 15px;font-size:16px;display:block;clear:both}.short-pixel-bulk-page.bulk-restore-all section.select_folders .filecount{font-size:12px}.short-pixel-bulk-page.bulk-restore-all section.random_check .random_answer{font-size:16px;font-weight:700;padding:8px;border:1px solid #ccc;display:inline-block}.short-pixel-bulk-page.bulk-restore-all section.random_check .inputs{margin:15px 0}.short-pixel-bulk-page.bulk-restore-all section.random_check .inputs span{margin-right:8px}.short-pixel-bulk-page.bulk-restore-all .button{margin:10px 0;margin-right:8px}#shortpixel-settings-tabs #tab-adv-settings .addCustomFolder{margin:10px 0}#shortpixel-settings-tabs #tab-adv-settings .addCustomFolder .add-folder-text{margin-left:5px}#shortpixel-settings-tabs #tab-adv-settings .addCustomFolder input[type="text"]{width:50em;max-width:70%}#shortpixel-settings-tabs #tab-adv-settings .addCustomFolder input[name="saveAdv"]{margin-left:8px}
res/css/sp-file-tree.css CHANGED
@@ -2,28 +2,40 @@ div.sp-folder-picker {
2
  margin: 20px 0; /* 15% from the top and centered */
3
  border: 1px solid #888;
4
  max-height: 400px;
 
5
  overflow: auto;}
6
 
7
  UL.jqueryFileTree LI.directory.selected {
8
  background-color: #209fd2;
9
  }
10
 
 
11
  UL.jqueryFileTree {
12
  font-family: Verdana, sans-serif;
13
  font-size: 11px;
14
  line-height: 18px;
15
- padding: 0;
16
  margin: 0;
17
  display: none;
 
 
 
 
 
18
  }
 
19
  UL.jqueryFileTree LI {
20
  list-style: none;
21
  padding: 0;
22
- padding-left: 20px;
23
  margin: 0;
24
  white-space: nowrap;
25
  }
26
- UL.jqueryFileTree LI.directory {
 
 
 
 
27
  background: url(../img/file-tree/directory.png) left top no-repeat;
28
  }
29
  UL.jqueryFileTree LI.directory-locked {
2
  margin: 20px 0; /* 15% from the top and centered */
3
  border: 1px solid #888;
4
  max-height: 400px;
5
+ min-height: 100px;
6
  overflow: auto;}
7
 
8
  UL.jqueryFileTree LI.directory.selected {
9
  background-color: #209fd2;
10
  }
11
 
12
+
13
  UL.jqueryFileTree {
14
  font-family: Verdana, sans-serif;
15
  font-size: 11px;
16
  line-height: 18px;
17
+ padding: 3px;
18
  margin: 0;
19
  display: none;
20
+ margin-left: 20px;
21
+ }
22
+ .sp-folder-picker > UL.jqueryFileTree
23
+ {
24
+ margin: 0;
25
  }
26
+
27
  UL.jqueryFileTree LI {
28
  list-style: none;
29
  padding: 0;
30
+ /* padding-left: 20px; */
31
  margin: 0;
32
  white-space: nowrap;
33
  }
34
+ UL.jqueryFileTree LI a
35
+ {
36
+ padding-left: 20px;
37
+ }
38
+ UL.jqueryFileTree LI.directory a {
39
  background: url(../img/file-tree/directory.png) left top no-repeat;
40
  }
41
  UL.jqueryFileTree LI.directory-locked {
res/css/sp-file-tree.min.css CHANGED
@@ -1,2 +1 @@
1
-
2
- div.sp-folder-picker{margin:20px 0;border:1px solid #888;max-height:400px;overflow:auto}UL.jqueryFileTree LI.directory.selected{background-color:#209fd2}UL.jqueryFileTree{font-family:Verdana,sans-serif;font-size:11px;line-height:18px;padding:0;margin:0;display:none}UL.jqueryFileTree LI{list-style:none;padding:0;padding-left:20px;margin:0;white-space:nowrap}UL.jqueryFileTree LI.directory{background:url(../img/file-tree/directory.png) left top no-repeat}UL.jqueryFileTree LI.directory-locked{background:url(../img/file-tree/directory-lock.png) left top no-repeat}UL.jqueryFileTree LI.expanded{background:url(../img/file-tree/folder_open.png) left top no-repeat}UL.jqueryFileTree LI.file{background:url(../img/file-tree/file.png) left top no-repeat}UL.jqueryFileTree LI.file-locked{background:url(../img/file-tree/file-lock.png) left top no-repeat !important}UL.jqueryFileTree LI.wait{background:url(../img/file-tree/spinner.gif) left top no-repeat}UL.jqueryFileTree LI.selected>a{font-weight:bold}UL.jqueryFileTree LI.ext_3gp{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_afp{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_afpa{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_asp{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_aspx{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_avi{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_bat{background:url(../img/file-tree/application.png) left top no-repeat}UL.jqueryFileTree LI.ext_bmp{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_c{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_cfm{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_cgi{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_com{background:url(../img/file-tree/application.png) left top no-repeat}UL.jqueryFileTree LI.ext_cpp{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_css{background:url(../img/file-tree/css.png) left top no-repeat}UL.jqueryFileTree LI.ext_doc{background:url(../img/file-tree/doc.png) left top no-repeat}UL.jqueryFileTree LI.ext_exe{background:url(../img/file-tree/application.png) left top no-repeat}UL.jqueryFileTree LI.ext_gif{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_fla{background:url(../img/file-tree/flash.png) left top no-repeat}UL.jqueryFileTree LI.ext_h{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_htm{background:url(../img/file-tree/html.png) left top no-repeat}UL.jqueryFileTree LI.ext_html{background:url(../img/file-tree/html.png) left top no-repeat}UL.jqueryFileTree LI.ext_jar{background:url(../img/file-tree/java.png) left top no-repeat}UL.jqueryFileTree LI.ext_jpg{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_jpeg{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_js{background:url(../img/file-tree/script.png) left top no-repeat}UL.jqueryFileTree LI.ext_lasso{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_log{background:url(../img/file-tree/txt.png) left top no-repeat}UL.jqueryFileTree LI.ext_m4p{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_mov{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_mp3{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_mp4{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_mpg{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_mpeg{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_ogg{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_ogv{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_pcx{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_pdf{background:url(../img/file-tree/pdf.png) left top no-repeat}UL.jqueryFileTree LI.ext_php{background:url(../img/file-tree/php.png) left top no-repeat}UL.jqueryFileTree LI.ext_png{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_ppt{background:url(../img/file-tree/ppt.png) left top no-repeat}UL.jqueryFileTree LI.ext_psd{background:url(../img/file-tree/psd.png) left top no-repeat}UL.jqueryFileTree LI.ext_pl{background:url(../img/file-tree/script.png) left top no-repeat}UL.jqueryFileTree LI.ext_py{background:url(../img/file-tree/script.png) left top no-repeat}UL.jqueryFileTree LI.ext_rb{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_rbx{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_rhtml{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_rpm{background:url(../img/file-tree/linux.png) left top no-repeat}UL.jqueryFileTree LI.ext_ruby{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_sql{background:url(../img/file-tree/db.png) left top no-repeat}UL.jqueryFileTree LI.ext_swf{background:url(../img/file-tree/flash.png) left top no-repeat}UL.jqueryFileTree LI.ext_tif{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_tiff{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_txt{background:url(../img/file-tree/txt.png) left top no-repeat}UL.jqueryFileTree LI.ext_vb{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_wav{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_webm{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_wmv{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_xls{background:url(../img/file-tree/xls.png) left top no-repeat}UL.jqueryFileTree LI.ext_xml{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_zip{background:url(../img/file-tree/zip.png) left top no-repeat}UL.jqueryFileTree A{color:#333;text-decoration:none;display:inline-block;padding:0 2px;cursor:pointer}UL.jqueryFileTree A:hover{background:#BDF}
1
+ div.sp-folder-picker{margin:20px 0;border:1px solid #888;max-height:400px;min-height:100px;overflow:auto}UL.jqueryFileTree LI.directory.selected{background-color:#209fd2}UL.jqueryFileTree{font-family:Verdana,sans-serif;font-size:11px;line-height:18px;padding:3px;margin:0;display:none;margin-left:20px}.sp-folder-picker>UL.jqueryFileTree{margin:0}UL.jqueryFileTree LI{list-style:none;padding:0;margin:0;white-space:nowrap}UL.jqueryFileTree LI a{padding-left:20px}UL.jqueryFileTree LI.directory a{background:url(../img/file-tree/directory.png) left top no-repeat}UL.jqueryFileTree LI.directory-locked{background:url(../img/file-tree/directory-lock.png) left top no-repeat}UL.jqueryFileTree LI.expanded{background:url(../img/file-tree/folder_open.png) left top no-repeat}UL.jqueryFileTree LI.file{background:url(../img/file-tree/file.png) left top no-repeat}UL.jqueryFileTree LI.file-locked{background:url(../img/file-tree/file-lock.png) left top no-repeat !important}UL.jqueryFileTree LI.wait{background:url(../img/file-tree/spinner.gif) left top no-repeat}UL.jqueryFileTree LI.selected>a{font-weight:bold}UL.jqueryFileTree LI.ext_3gp{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_afp{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_afpa{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_asp{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_aspx{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_avi{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_bat{background:url(../img/file-tree/application.png) left top no-repeat}UL.jqueryFileTree LI.ext_bmp{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_c{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_cfm{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_cgi{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_com{background:url(../img/file-tree/application.png) left top no-repeat}UL.jqueryFileTree LI.ext_cpp{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_css{background:url(../img/file-tree/css.png) left top no-repeat}UL.jqueryFileTree LI.ext_doc{background:url(../img/file-tree/doc.png) left top no-repeat}UL.jqueryFileTree LI.ext_exe{background:url(../img/file-tree/application.png) left top no-repeat}UL.jqueryFileTree LI.ext_gif{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_fla{background:url(../img/file-tree/flash.png) left top no-repeat}UL.jqueryFileTree LI.ext_h{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_htm{background:url(../img/file-tree/html.png) left top no-repeat}UL.jqueryFileTree LI.ext_html{background:url(../img/file-tree/html.png) left top no-repeat}UL.jqueryFileTree LI.ext_jar{background:url(../img/file-tree/java.png) left top no-repeat}UL.jqueryFileTree LI.ext_jpg{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_jpeg{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_js{background:url(../img/file-tree/script.png) left top no-repeat}UL.jqueryFileTree LI.ext_lasso{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_log{background:url(../img/file-tree/txt.png) left top no-repeat}UL.jqueryFileTree LI.ext_m4p{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_mov{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_mp3{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_mp4{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_mpg{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_mpeg{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_ogg{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_ogv{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_pcx{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_pdf{background:url(../img/file-tree/pdf.png) left top no-repeat}UL.jqueryFileTree LI.ext_php{background:url(../img/file-tree/php.png) left top no-repeat}UL.jqueryFileTree LI.ext_png{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_ppt{background:url(../img/file-tree/ppt.png) left top no-repeat}UL.jqueryFileTree LI.ext_psd{background:url(../img/file-tree/psd.png) left top no-repeat}UL.jqueryFileTree LI.ext_pl{background:url(../img/file-tree/script.png) left top no-repeat}UL.jqueryFileTree LI.ext_py{background:url(../img/file-tree/script.png) left top no-repeat}UL.jqueryFileTree LI.ext_rb{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_rbx{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_rhtml{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_rpm{background:url(../img/file-tree/linux.png) left top no-repeat}UL.jqueryFileTree LI.ext_ruby{background:url(../img/file-tree/ruby.png) left top no-repeat}UL.jqueryFileTree LI.ext_sql{background:url(../img/file-tree/db.png) left top no-repeat}UL.jqueryFileTree LI.ext_swf{background:url(../img/file-tree/flash.png) left top no-repeat}UL.jqueryFileTree LI.ext_tif{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_tiff{background:url(../img/file-tree/picture.png) left top no-repeat}UL.jqueryFileTree LI.ext_txt{background:url(../img/file-tree/txt.png) left top no-repeat}UL.jqueryFileTree LI.ext_vb{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_wav{background:url(../img/file-tree/music.png) left top no-repeat}UL.jqueryFileTree LI.ext_webm{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_wmv{background:url(../img/file-tree/film.png) left top no-repeat}UL.jqueryFileTree LI.ext_xls{background:url(../img/file-tree/xls.png) left top no-repeat}UL.jqueryFileTree LI.ext_xml{background:url(../img/file-tree/code.png) left top no-repeat}UL.jqueryFileTree LI.ext_zip{background:url(../img/file-tree/zip.png) left top no-repeat}UL.jqueryFileTree A{color:#333;text-decoration:none;display:inline-block;padding:0 2px;cursor:pointer}UL.jqueryFileTree A:hover{background:#BDF}
 
res/img/{robo-cool@2x.png@ → robo-cool@2x.png} RENAMED
File without changes
res/img/robo-cool@x2.png ADDED
Binary file
res/js/shortpixel.js CHANGED
@@ -4,7 +4,6 @@
4
 
5
  jQuery(document).ready(function(){ShortPixel.init();});
6
 
7
-
8
  var ShortPixel = function() {
9
 
10
  function init() {
@@ -50,11 +49,11 @@ var ShortPixel = function() {
50
  ShortPixel[opt] = options[opt];
51
  }
52
  }
53
-
54
  function isEmailValid(email) {
55
  return /^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,63})+$/.test(email);
56
  }
57
-
58
  function updateSignupEmail() {
59
  var email = jQuery('#pluginemail').val();
60
  if(ShortPixel.isEmailValid(email)) {
@@ -62,12 +61,12 @@ var ShortPixel = function() {
62
  }
63
  jQuery('#request_key').attr('href', jQuery('#request_key').attr('href').split('?')[0] + '?pluginemail=' + email);
64
  }
65
-
66
  function validateKey(){
67
  jQuery('#valid').val('validate');
68
  jQuery('#wp_shortpixel_options').submit();
69
  }
70
-
71
  jQuery("#key").keypress(function(e) {
72
  if(e.which == 13) {
73
  jQuery('#valid').val('validate');
@@ -81,7 +80,7 @@ var ShortPixel = function() {
81
  jQuery("#width,#height").attr("disabled", "disabled");
82
  }
83
  }
84
-
85
  function setupGeneralTab(rad, minWidth, minHeight) {
86
  for(var i = 0, prev = null; i < rad.length; i++) {
87
  rad[i].onclick = function() {
@@ -137,7 +136,7 @@ var ShortPixel = function() {
137
  jQuery(".wp-shortpixel-options .shortpixel-key-valid").css("display", "none");
138
  jQuery(".wp-shortpixel-options button#validate").css("display", "inline-block");
139
  }
140
-
141
  function setupAdvancedTab() {
142
  jQuery("input.remove-folder-button").click(function(){
143
  var path = jQuery(this).data("value");
@@ -194,7 +193,7 @@ var ShortPixel = function() {
194
  }
195
  });
196
  }
197
-
198
  function switchSettingsTab(target){
199
  var tab = target.replace("tab-",""),
200
  beacon = "",
@@ -228,7 +227,7 @@ var ShortPixel = function() {
228
  HS.beacon.suggest(beacon);
229
  }
230
  }
231
-
232
  function adjustSettingsTabsHeight(){
233
  var sectionHeight = jQuery('section#tab-settings .wp-shortpixel-options').height() + 90;
234
  sectionHeight = Math.max(sectionHeight, jQuery('section#tab-adv-settings .wp-shortpixel-options').height() + 20);
@@ -247,14 +246,14 @@ var ShortPixel = function() {
247
  }
248
  });
249
  }
250
-
251
  function checkQuota() {
252
  var data = { action : 'shortpixel_check_quota'};
253
  jQuery.get(ShortPixel.AJAX_URL, data, function() {
254
  console.log("quota refreshed");
255
  });
256
  }
257
-
258
  function onBulkThumbsCheck(check) {
259
  if(check.checked) {
260
  jQuery("#with-thumbs").css('display', 'inherit');
@@ -264,7 +263,7 @@ var ShortPixel = function() {
264
  jQuery("#with-thumbs").css('display', 'none');
265
  }
266
  }
267
-
268
  function successMsg(id, percent, type, thumbsCount, retinasCount) {
269
  return (percent > 0 ? "<div class='sp-column-info'>" + _spTr.reducedBy + " <strong><span class='percent'>" + percent + "%</span></strong> " : "")
270
  + (percent > 0 && percent < 5 ? "<br>" : '')
@@ -274,7 +273,7 @@ var ShortPixel = function() {
274
  + (0 + retinasCount > 0 ? "<br>" + _spTr.plusXretinasOpt.format(retinasCount) :"")
275
  + "</div>";
276
  }
277
-
278
  function percentDial(query, size) {
279
  jQuery(query).knob({
280
  'readOnly': true,
@@ -285,22 +284,22 @@ var ShortPixel = function() {
285
  return value + '%';
286
  }
287
  });
288
- }
289
-
290
  function successActions(id, type, thumbsCount, thumbsTotal, backupEnabled, fileName) {
291
  if(backupEnabled == 1) {
292
-
293
  var successActions = jQuery('.sp-column-actions-template').clone();
294
 
295
- if(!successActions.length) return false;
296
-
297
  var otherTypes;
298
  if(type.length == 0) {
299
  otherTypes = ['lossy', 'lossless'];
300
  } else {
301
  otherTypes = ['lossy','glossy','lossless'].filter(function(el) {return !(el == type);});
302
- }
303
-
304
  successActions.html(successActions.html().replace(/__SP_ID__/g, id));
305
  if(fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase() == 'pdf') {
306
  jQuery('.sp-action-compare', successActions).remove();
@@ -314,10 +313,10 @@ var ShortPixel = function() {
314
  successActions.html(successActions.html().replace(/__SP_FIRST_TYPE__/g, otherTypes[0]));
315
  successActions.html(successActions.html().replace(/__SP_SECOND_TYPE__/g, otherTypes[1]));
316
  return successActions.html();
317
- }
318
  return "";
319
  }
320
-
321
  function otherMediaUpdateActions(id, actions) {
322
  id = id.substring(2);
323
  if(jQuery(".shortpixel-other-media").length) {
@@ -330,7 +329,7 @@ var ShortPixel = function() {
330
  }
331
  }
332
  }
333
-
334
  function retry(msg) {
335
  ShortPixel.retries++;
336
  if(isNaN(ShortPixel.retries)) ShortPixel.retries = 1;
@@ -339,17 +338,17 @@ var ShortPixel = function() {
339
  setTimeout(checkBulkProgress, 5000);
340
  } else {
341
  ShortPixel.bulkShowError(-1,"Invalid response from server received 6 times. Please retry later by reloading this page, or <a href='https://shortpixel.com/contact' target='_blank'>contact support</a>. (Error: " + msg + ")", "");
342
- console.log("Invalid response from server 6 times. Giving up.");
343
  }
344
  }
345
-
346
  function browseContent(browseData) {
347
  browseData.action = 'shortpixel_browse_content';
348
  var browseResponse = "";
349
  jQuery.ajax({
350
  type: "POST",
351
- url: ShortPixel.AJAX_URL,
352
- data: browseData,
353
  success: function(response) {
354
  browseResponse = response;
355
  },
@@ -357,14 +356,14 @@ var ShortPixel = function() {
357
  });
358
  return browseResponse;
359
  }
360
-
361
  function getBackupSize() {
362
  var browseData = { 'action': 'shortpixel_get_backup_size'};
363
  var browseResponse = "";
364
  jQuery.ajax({
365
  type: "POST",
366
- url: ShortPixel.AJAX_URL,
367
- data: browseData,
368
  success: function(response) {
369
  browseResponse = response;
370
  },
@@ -393,8 +392,8 @@ var ShortPixel = function() {
393
  jQuery.ajax({
394
  type: "POST",
395
  async: false,
396
- url: ShortPixel.AJAX_URL,
397
- data: browseData,
398
  success: function(response) {
399
  data = JSON.parse(response);
400
  if(data["Status"] == 'success') {
@@ -418,7 +417,8 @@ var ShortPixel = function() {
418
  jQuery('#request_key').removeClass('disabled');
419
  jQuery('#pluginemail_spinner').removeClass('is-active');
420
  }
421
-
 
422
  function proposeUpgrade() {
423
  //first open the popup window with the spinner
424
  jQuery("#shortPixelProposeUpgrade .sp-modal-body").addClass('sptw-modal-spinner');
@@ -429,15 +429,15 @@ var ShortPixel = function() {
429
  var browseData = { 'action': 'shortpixel_propose_upgrade'};
430
  jQuery.ajax({
431
  type: "POST",
432
- url: ShortPixel.AJAX_URL,
433
- data: browseData,
434
  success: function(response) {
435
  jQuery("#shortPixelProposeUpgrade .sp-modal-body").removeClass('sptw-modal-spinner');
436
  jQuery("#shortPixelProposeUpgrade .sp-modal-body").html(response);
437
  }
438
  });
439
  }
440
-
441
  function closeProposeUpgrade() {
442
  jQuery("#shortPixelProposeUpgradeShade").css("display", "none");
443
  jQuery("#shortPixelProposeUpgrade").addClass('shortpixel-hide');
@@ -445,7 +445,7 @@ var ShortPixel = function() {
445
  ShortPixel.recheckQuota();
446
  }
447
  }
448
-
449
  function includeUnlisted() {
450
  jQuery("#short-pixel-notice-unlisted").hide();
451
  jQuery("#optimizeUnlisted").prop('checked', true);
@@ -460,34 +460,57 @@ var ShortPixel = function() {
460
  });
461
  }
462
 
463
-
464
  function initFolderSelector() {
465
  jQuery(".select-folder-button").click(function(){
466
- jQuery(".sp-folder-picker-shade").css("display", "block");
467
- jQuery(".sp-folder-picker").fileTree({
 
 
 
 
468
  script: ShortPixel.browseContent,
469
  //folderEvent: 'dblclick',
470
  multiFolder: false
471
  //onlyFolders: true
472
  });
473
  });
474
- jQuery(".shortpixel-modal input.select-folder-cancel").click(function(){
475
- jQuery(".sp-folder-picker-shade").css("display", "none");
 
476
  });
477
- jQuery(".shortpixel-modal input.select-folder").click(function(){
478
- var subPath = jQuery("UL.jqueryFileTree LI.directory.selected A").attr("rel").trim();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  if(subPath) {
480
  var fullPath = jQuery("#customFolderBase").val() + subPath;
481
  if(fullPath.slice(-1) == '/') fullPath = fullPath.slice(0, -1);
482
  jQuery("#addCustomFolder").val(fullPath);
483
  jQuery("#addCustomFolderView").val(fullPath);
484
- jQuery(".sp-folder-picker-shade").css("display", "none");
 
 
485
  } else {
486
  alert("Please select a folder from the list.");
487
  }
488
  });
489
  }
490
-
491
  function bulkShowLengthyMsg(id, fileName, customLink){
492
  var notice = jQuery(".bulk-notice-msg.bulk-lengthy");
493
  if(notice.length == 0) return;
@@ -498,24 +521,24 @@ var ShortPixel = function() {
498
  } else {
499
  link.attr("href", link.data("href").replace("__ID__", id));
500
  }
501
-
502
  notice.css("display", "block");
503
  }
504
-
505
  function bulkHideLengthyMsg(){
506
  jQuery(".bulk-notice-msg.bulk-lengthy").css("display", "none");
507
  }
508
-
509
  function bulkShowMaintenanceMsg(type){
510
  var notice = jQuery(".bulk-notice-msg.bulk-" + type);
511
  if(notice.length == 0) return;
512
  notice.css("display", "block");
513
  }
514
-
515
  function bulkHideMaintenanceMsg(type){
516
  jQuery(".bulk-notice-msg.bulk-" + type).css("display", "none");
517
  }
518
-
519
  function bulkShowError(id, msg, fileName, customLink) {
520
  var noticeTpl = jQuery("#bulk-error-template");
521
  if(noticeTpl.length == 0) return;
@@ -537,9 +560,9 @@ var ShortPixel = function() {
537
  }
538
  link.text(fileName);
539
  noticeTpl.after(notice);
540
- notice.css("display", "block");
541
  }
542
-
543
  function confirmBulkAction(type, e) {
544
  if(!confirm(_spTr['confirmBulk' + type])) {
545
  e.stopPropagation();
@@ -549,19 +572,39 @@ var ShortPixel = function() {
549
  return true;
550
  }
551
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
  function removeBulkMsg(me) {
553
  jQuery(me).parent().parent().remove();
554
  }
555
-
556
  function isCustomImageId(id) {
557
  return id.substring(0,2) == "C-";
558
  }
559
-
560
  function recheckQuota() {
561
  var parts = window.location.href.split('#');
562
  window.location.href=parts[0]+(parts[0].indexOf('?')>0?'&':'?')+'checkquota=1' + (typeof parts[1] === 'undefined' ? '' : '#' + parts[1]);
563
  }
564
-
565
  function openImageMenu(e) {
566
  e.preventDefault();
567
  //install (lazily) a window click event to close the menus
@@ -577,15 +620,14 @@ var ShortPixel = function() {
577
  jQuery('.sp-dropdown.sp-show').removeClass('sp-show');
578
  if(!shown) e.target.parentElement.classList.add("sp-show");
579
  }
580
-
581
  function loadComparer(id) {
582
  this.comparerData.origUrl = false;
583
-
584
- if(this.comparerData.cssLoaded === false) {
585
  jQuery('<link>')
586
  .appendTo('head')
587
  .attr({
588
- type: 'text/css',
589
  rel: 'stylesheet',
590
  href: this.WP_PLUGIN_URL + '/res/css/twentytwenty.min.css'
591
  });
@@ -599,13 +641,13 @@ var ShortPixel = function() {
599
  }
600
  });
601
  this.comparerData.jsLoaded = 1;
602
- jQuery(".sp-close-button").click(ShortPixel.closeComparerPopup);
603
  }
604
  if(this.comparerData.origUrl === false) {
605
  jQuery.ajax({
606
  type: "POST",
607
- url: ShortPixel.AJAX_URL,
608
- data: { action : 'shortpixel_get_comparer_data', id : id },
609
  success: function(response) {
610
  data = JSON.parse(response);
611
  jQuery.extend(ShortPixel.comparerData, data);
@@ -624,23 +666,36 @@ var ShortPixel = function() {
624
  //depending on the sizes choose the right modal
625
  var sideBySide = (height < 150 || width < 350);
626
  var modal = jQuery(sideBySide ? '#spUploadCompareSideBySide' : '#spUploadCompare');
 
 
627
  if(!sideBySide) {
628
  jQuery("#spCompareSlider").html('<img class="spUploadCompareOriginal"/><img class="spUploadCompareOptimized"/>');
629
  }
630
  //calculate the modal size
631
  width = Math.max(350, Math.min(800, (width < 350 ? (width + 25) * 2 : (height < 150 ? width + 25 : width))));
632
  height = Math.max(150, (sideBySide ? (origWidth > 350 ? 2 * (height + 45) : height + 45) : height * width / origWidth));
 
 
 
633
  //set modal sizes and display
634
  jQuery(".sp-modal-body", modal).css("width", width);
635
  jQuery(".shortpixel-slider", modal).css("width", width);
636
  modal.css("width", width);
 
637
  jQuery(".sp-modal-body", modal).css("height", height);
638
- modal.css('display', 'block');
639
- modal.parent().css('display', 'block');
 
 
640
  if(!sideBySide) {
641
  jQuery("#spCompareSlider").twentytwenty({slider_move: "mousemove"});
642
  }
 
 
 
643
  jQuery(document).on('keyup.sp_modal_active', ShortPixel.closeComparerPopup);
 
 
644
  //change images srcs
645
  var imgOpt = jQuery(".spUploadCompareOptimized", modal);
646
  jQuery(".spUploadCompareOriginal", modal).attr("src", imgOriginal);
@@ -653,12 +708,15 @@ var ShortPixel = function() {
653
  });
654
  imgOpt.attr("src", imgOptimized);
655
  }
656
-
657
  function closeComparerPopup(e) {
658
- jQuery("#spUploadCompareSideBySide").parent().css("display", 'none');
659
- jQuery("#spUploadCompareSideBySide").css("display", 'none');
660
- jQuery("#spUploadCompare").css("display", 'none');
 
661
  jQuery(document).unbind('keyup.sp_modal_active');
 
 
662
  }
663
 
664
  function convertPunycode(url) {
@@ -705,6 +763,7 @@ var ShortPixel = function() {
705
  bulkHideMaintenanceMsg : bulkHideMaintenanceMsg,
706
  bulkShowError : bulkShowError,
707
  confirmBulkAction : confirmBulkAction,
 
708
  removeBulkMsg : removeBulkMsg,
709
  isCustomImageId : isCustomImageId,
710
  recheckQuota : recheckQuota,
@@ -731,7 +790,7 @@ function showToolBarAlert($status, $message, id) {
731
  var robo = jQuery("li.shortpixel-toolbar-processing");
732
  switch($status) {
733
  case ShortPixel.STATUS_QUOTA_EXCEEDED:
734
- if( window.location.href.search("wp-short-pixel-bulk") > 0
735
  && jQuery(".sp-quota-exceeded-alert").length == 0) { //if we're in bulk and the alert is not displayed reload to see all options
736
  location.reload();
737
  return;
@@ -743,7 +802,7 @@ function showToolBarAlert($status, $message, id) {
743
  //jQuery("a div", robo).attr("title", "ShortPixel quota exceeded. Click to top-up");
744
  jQuery("a div", robo).attr("title", "ShortPixel quota exceeded. Click for details.");
745
  break;
746
- case ShortPixel.STATUS_SKIP:
747
  case ShortPixel.STATUS_FAIL:
748
  robo.addClass("shortpixel-alert shortpixel-processing");
749
  jQuery("a div", robo).attr("title", $message);
@@ -785,11 +844,11 @@ function checkQuotaExceededAlert() {
785
  }
786
  }
787
  /**
788
- * JavaScript image processing - this method gets executed on every footer load and afterwards
789
  * calls itself until receives an Empty queue message
790
  */
791
  function checkBulkProgress() {
792
- //the replace stands for malformed urls on some sites, like wp-admin//upload.php which are accepted by the browser.
793
  //using a replacer function to avoid replacing the first occurence (https:// ...)
794
  var replacer = function(match) {
795
  if(!first) {
@@ -801,16 +860,16 @@ function checkBulkProgress() {
801
 
802
  var first = false; //arm replacer
803
  var url = window.location.href.toLowerCase().replace(/\/\//g , replacer);
804
-
805
  first = false; //rearm replacer
806
  var adminUrl = ShortPixel.WP_ADMIN_URL.toLowerCase().replace(/\/\//g , replacer);
807
-
808
  //handle possible Punycode domain names.
809
  if(url.search(adminUrl) < 0) {
810
  url = ShortPixel.convertPunycode(url);
811
  adminUrl = ShortPixel.convertPunycode(adminUrl);
812
  }
813
-
814
  if( url.search(adminUrl + "upload.php") < 0
815
  && url.search(adminUrl + "edit.php") < 0
816
  && url.search(adminUrl + "edit-tags.php") < 0
@@ -822,20 +881,20 @@ function checkBulkProgress() {
822
  hideToolBarAlert();
823
  return;
824
  }
825
-
826
  //if i'm the bulk processor and i'm not the bulk page and a bulk page comes around, leave the bulk processor role
827
- if(ShortPixel.bulkProcessor == true && window.location.href.search("wp-short-pixel-bulk") < 0
828
  && typeof localStorage.bulkPage !== 'undefined' && localStorage.bulkPage > 0) {
829
  ShortPixel.bulkProcessor = false;
830
  }
831
-
832
  //if i'm the bulk page, steal the bulk processor
833
  if( window.location.href.search("wp-short-pixel-bulk") >= 0 ) {
834
  ShortPixel.bulkProcessor = true;
835
  localStorage.bulkTime = Math.floor(Date.now() / 1000);
836
  localStorage.bulkPage = 1;
837
  }
838
-
839
  //if I'm not the bulk processor, check every 20 sec. if the bulk processor is running, otherwise take the role
840
  if(ShortPixel.bulkProcessor == true || typeof localStorage.bulkTime == 'undefined' || Math.floor(Date.now() / 1000) - localStorage.bulkTime > 90) {
841
  ShortPixel.bulkProcessor = true;
@@ -854,8 +913,8 @@ function checkBulkProcessingCallApi(){
854
  jQuery.ajax({
855
  type: "POST",
856
  url: ShortPixel.AJAX_URL, //formerly ajaxurl , but changed it because it's not available in the front-end and now we have an option to run in the front-end
857
- data: data,
858
- success: function(response)
859
  {
860
  if(response.length > 0) {
861
  var data = null;
@@ -866,7 +925,7 @@ function checkBulkProcessingCallApi(){
866
  return;
867
  }
868
  ShortPixel.retries = 0;
869
-
870
  var id = data["ImageID"];
871
 
872
  var isBulkPage = (jQuery("div.short-pixel-bulk-page").length > 0);
@@ -878,7 +937,7 @@ function checkBulkProcessingCallApi(){
878
  showToolBarAlert(ShortPixel.STATUS_NO_KEY);
879
  break;
880
  case ShortPixel.STATUS_QUOTA_EXCEEDED:
881
- setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"https://shortpixel.com/login/"
882
  + ShortPixel.API_KEY + "\" target=\"_blank\">" + _spTr.extendQuota + "</a>"
883
  + "<a class='button button-smaller' href='admin.php?action=shortpixel_check_quota'>" + _spTr.check__Quota + "</a>");
884
  showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED);
@@ -888,7 +947,7 @@ function checkBulkProcessingCallApi(){
888
  ShortPixel.otherMediaUpdateActions(id, ['quota','view']);
889
  break;
890
  case ShortPixel.STATUS_FAIL:
891
- setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('" + id + "', false)\">"
892
  + _spTr.retry + "</a>");
893
  showToolBarAlert(ShortPixel.STATUS_FAIL, data["Message"], id);
894
  if(isBulkPage) {
@@ -925,11 +984,29 @@ function checkBulkProcessingCallApi(){
925
 
926
  showToolBarAlert(ShortPixel.STATUS_SUCCESS, "");
927
  //for now, until 4.1
928
- var successActions = ShortPixel.isCustomImageId(id)
929
- ? ""
930
  : ShortPixel.successActions(id, data["Type"], data['ThumbsCount'], data['ThumbsTotal'], data["BackupEnabled"], data['Filename']);
931
-
 
932
  setCellMessage(id, ShortPixel.successMsg(id, percent, data["Type"], data['ThumbsCount'], data['RetinasCount']), successActions);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
933
  var actions = jQuery(['restore', 'view', 'redolossy', 'redoglossy', 'redolossless']).not(['redo'+data["Type"]]).get();
934
  ShortPixel.otherMediaUpdateActions(id, actions);
935
  var animator = new PercentageAnimator("#sp-msg-" + id + " span.percent", percent);
@@ -945,7 +1022,8 @@ function checkBulkProcessingCallApi(){
945
  ShortPixel.percentDial("#sp-avg-optimization .dial", 60);
946
  }
947
  }
948
- }
 
949
  console.log('Server response: ' + response);
950
  if(isBulkPage && typeof data["BulkPercent"] !== 'undefined') {
951
  progressUpdate(data["BulkPercent"], data["BulkMsg"]);
@@ -1010,11 +1088,11 @@ function setCellMessage(id, message, actions){
1010
  msg.html("<div class='sp-column-actions'>" + actions + "</div>"
1011
  + "<div class='sp-column-info'>" + message + "</div>");
1012
  msg.css("color", "");
1013
- }
1014
  msg = jQuery("#sp-cust-msg-" + id);
1015
  if(msg.length > 0) {
1016
  msg.html("<div class='sp-column-info'>" + message + "</div>");
1017
- }
1018
  }
1019
 
1020
  function manualOptimization(id, cleanup) {
@@ -1031,6 +1109,7 @@ function manualOptimization(id, cleanup) {
1031
  success: function(response) {
1032
  var resp = JSON.parse(response);
1033
  if(resp["Status"] == ShortPixel.STATUS_SUCCESS) {
 
1034
  setTimeout(checkBulkProgress, 2000);
1035
  } else {
1036
  setCellMessage(id, typeof resp["Message"] !== "undefined" ? resp["Message"] : _spTr.thisContentNotProcessable, "");
@@ -1122,7 +1201,7 @@ function PercentageAnimator(outputSelector, targetPercentage) {
1122
  this.curPercentage = 0;
1123
  this.targetPercentage = targetPercentage;
1124
  this.outputSelector = outputSelector;
1125
-
1126
  this.animate = function(percentage) {
1127
  this.targetPercentage = percentage;
1128
  setTimeout(PercentageTimer.bind(null, this), this.animationSpeed);
@@ -1131,9 +1210,9 @@ function PercentageAnimator(outputSelector, targetPercentage) {
1131
 
1132
  function PercentageTimer(animator) {
1133
  if (animator.curPercentage - animator.targetPercentage < -animator.increment) {
1134
- animator.curPercentage += animator.increment;
1135
  } else if (animator.curPercentage - animator.targetPercentage > animator.increment) {
1136
- animator.curPercentage -= animator.increment;
1137
  } else {
1138
  animator.curPercentage = animator.targetPercentage;
1139
  }
@@ -1141,7 +1220,7 @@ function PercentageTimer(animator) {
1141
  jQuery(animator.outputSelector).text(animator.curPercentage + "%");
1142
 
1143
  if (animator.curPercentage != animator.targetPercentage) {
1144
- setTimeout(PercentageTimer.bind(null,animator), animator.animationSpeed)
1145
  }
1146
  }
1147
 
@@ -1155,7 +1234,7 @@ function progressUpdate(percent, message) {
1155
  jQuery(".progress-left", progress).html(percent + "%");
1156
  } else {
1157
  jQuery(".progress-img span", progress).html(percent + "%");
1158
- jQuery(".progress-left", progress).html("");
1159
  }
1160
  jQuery(".bulk-estimate").html(message);
1161
  }
@@ -1167,7 +1246,7 @@ function sliderUpdate(id, thumb, bkThumb, percent, filename){
1167
  var oldSlide = jQuery(".bulk-slider div.bulk-slide:first-child");
1168
  if(oldSlide.length === 0) {
1169
  return;
1170
- }
1171
  if(oldSlide.attr("id") != "empty-slide") {
1172
  oldSlide.hide();
1173
  }
@@ -1190,10 +1269,10 @@ function sliderUpdate(id, thumb, bkThumb, percent, filename){
1190
  jQuery(".img-original", newSlide).css("display", "none");
1191
  }
1192
  jQuery(".bulk-opt-percent", newSlide).html('<input type="text" class="dial" value="' + percent + '"/>');
1193
-
1194
  jQuery(".bulk-slider").append(newSlide);
1195
  ShortPixel.percentDial("#" + newSlide.attr("id") + " .dial", 100);
1196
-
1197
  jQuery(".bulk-slider-container span.filename").html("&nbsp;&nbsp;" + filename);
1198
  if(oldSlide.attr("id") == "empty-slide") {
1199
  oldSlide.remove();
@@ -1213,11 +1292,11 @@ function hideSlider() {
1213
  function showStats() {
1214
  var statsDiv = jQuery(".bulk-stats");
1215
  if(statsDiv.length > 0) {
1216
-
1217
  }
1218
  }
1219
 
1220
- if (!(typeof String.prototype.format == 'function')) {
1221
  String.prototype.format = function() {
1222
  var s = this,
1223
  i = arguments.length;
4
 
5
  jQuery(document).ready(function(){ShortPixel.init();});
6
 
 
7
  var ShortPixel = function() {
8
 
9
  function init() {
49
  ShortPixel[opt] = options[opt];
50
  }
51
  }
52
+
53
  function isEmailValid(email) {
54
  return /^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,63})+$/.test(email);
55
  }
56
+
57
  function updateSignupEmail() {
58
  var email = jQuery('#pluginemail').val();
59
  if(ShortPixel.isEmailValid(email)) {
61
  }
62
  jQuery('#request_key').attr('href', jQuery('#request_key').attr('href').split('?')[0] + '?pluginemail=' + email);
63
  }
64
+
65
  function validateKey(){
66
  jQuery('#valid').val('validate');
67
  jQuery('#wp_shortpixel_options').submit();
68
  }
69
+
70
  jQuery("#key").keypress(function(e) {
71
  if(e.which == 13) {
72
  jQuery('#valid').val('validate');
80
  jQuery("#width,#height").attr("disabled", "disabled");
81
  }
82
  }
83
+
84
  function setupGeneralTab(rad, minWidth, minHeight) {
85
  for(var i = 0, prev = null; i < rad.length; i++) {
86
  rad[i].onclick = function() {
136
  jQuery(".wp-shortpixel-options .shortpixel-key-valid").css("display", "none");
137
  jQuery(".wp-shortpixel-options button#validate").css("display", "inline-block");
138
  }
139
+
140
  function setupAdvancedTab() {
141
  jQuery("input.remove-folder-button").click(function(){
142
  var path = jQuery(this).data("value");
193
  }
194
  });
195
  }
196
+
197
  function switchSettingsTab(target){
198
  var tab = target.replace("tab-",""),
199
  beacon = "",
227
  HS.beacon.suggest(beacon);
228
  }
229
  }
230
+
231
  function adjustSettingsTabsHeight(){
232
  var sectionHeight = jQuery('section#tab-settings .wp-shortpixel-options').height() + 90;
233
  sectionHeight = Math.max(sectionHeight, jQuery('section#tab-adv-settings .wp-shortpixel-options').height() + 20);
246
  }
247
  });
248
  }
249
+
250
  function checkQuota() {
251
  var data = { action : 'shortpixel_check_quota'};
252
  jQuery.get(ShortPixel.AJAX_URL, data, function() {
253
  console.log("quota refreshed");
254
  });
255
  }
256
+
257
  function onBulkThumbsCheck(check) {
258
  if(check.checked) {
259
  jQuery("#with-thumbs").css('display', 'inherit');
263
  jQuery("#with-thumbs").css('display', 'none');
264
  }
265
  }
266
+
267
  function successMsg(id, percent, type, thumbsCount, retinasCount) {
268
  return (percent > 0 ? "<div class='sp-column-info'>" + _spTr.reducedBy + " <strong><span class='percent'>" + percent + "%</span></strong> " : "")
269
  + (percent > 0 && percent < 5 ? "<br>" : '')
273
  + (0 + retinasCount > 0 ? "<br>" + _spTr.plusXretinasOpt.format(retinasCount) :"")
274
  + "</div>";
275
  }
276
+
277
  function percentDial(query, size) {
278
  jQuery(query).knob({
279
  'readOnly': true,
284
  return value + '%';
285
  }
286
  });
287
+ }
288
+
289
  function successActions(id, type, thumbsCount, thumbsTotal, backupEnabled, fileName) {
290
  if(backupEnabled == 1) {
291
+
292
  var successActions = jQuery('.sp-column-actions-template').clone();
293
 
294
+ if(!successActions.length) return false;
295
+
296
  var otherTypes;
297
  if(type.length == 0) {
298
  otherTypes = ['lossy', 'lossless'];
299
  } else {
300
  otherTypes = ['lossy','glossy','lossless'].filter(function(el) {return !(el == type);});
301
+ }
302
+
303
  successActions.html(successActions.html().replace(/__SP_ID__/g, id));
304
  if(fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase() == 'pdf') {
305
  jQuery('.sp-action-compare', successActions).remove();
313
  successActions.html(successActions.html().replace(/__SP_FIRST_TYPE__/g, otherTypes[0]));
314
  successActions.html(successActions.html().replace(/__SP_SECOND_TYPE__/g, otherTypes[1]));
315
  return successActions.html();
316
+ }
317
  return "";
318
  }
319
+
320
  function otherMediaUpdateActions(id, actions) {
321
  id = id.substring(2);
322
  if(jQuery(".shortpixel-other-media").length) {
329
  }
330
  }
331
  }
332
+
333
  function retry(msg) {
334
  ShortPixel.retries++;
335
  if(isNaN(ShortPixel.retries)) ShortPixel.retries = 1;
338
  setTimeout(checkBulkProgress, 5000);
339
  } else {
340
  ShortPixel.bulkShowError(-1,"Invalid response from server received 6 times. Please retry later by reloading this page, or <a href='https://shortpixel.com/contact' target='_blank'>contact support</a>. (Error: " + msg + ")", "");
341
+ console.log("Invalid response from server 6 times. Giving up.");
342
  }
343
  }
344
+
345
  function browseContent(browseData) {
346
  browseData.action = 'shortpixel_browse_content';
347
  var browseResponse = "";
348
  jQuery.ajax({
349
  type: "POST",
350
+ url: ShortPixel.AJAX_URL,
351
+ data: browseData,
352
  success: function(response) {
353
  browseResponse = response;
354
  },
356
  });
357
  return browseResponse;
358
  }
359
+
360
  function getBackupSize() {
361
  var browseData = { 'action': 'shortpixel_get_backup_size'};
362
  var browseResponse = "";
363
  jQuery.ajax({
364
  type: "POST",
365
+ url: ShortPixel.AJAX_URL,
366
+ data: browseData,
367
  success: function(response) {
368
  browseResponse = response;
369
  },
392
  jQuery.ajax({
393
  type: "POST",
394
  async: false,
395
+ url: ShortPixel.AJAX_URL,
396
+ data: browseData,
397
  success: function(response) {
398
  data = JSON.parse(response);
399
  if(data["Status"] == 'success') {
417
  jQuery('#request_key').removeClass('disabled');
418
  jQuery('#pluginemail_spinner').removeClass('is-active');
419
  }
420
+
421
+ // [TODO] Check where this function is called and if modal is working here.
422
  function proposeUpgrade() {
423
  //first open the popup window with the spinner
424
  jQuery("#shortPixelProposeUpgrade .sp-modal-body").addClass('sptw-modal-spinner');
429
  var browseData = { 'action': 'shortpixel_propose_upgrade'};
430
  jQuery.ajax({
431
  type: "POST",
432
+ url: ShortPixel.AJAX_URL,
433
+ data: browseData,
434
  success: function(response) {
435
  jQuery("#shortPixelProposeUpgrade .sp-modal-body").removeClass('sptw-modal-spinner');
436
  jQuery("#shortPixelProposeUpgrade .sp-modal-body").html(response);
437
  }
438
  });
439
  }
440
+
441
  function closeProposeUpgrade() {
442
  jQuery("#shortPixelProposeUpgradeShade").css("display", "none");
443
  jQuery("#shortPixelProposeUpgrade").addClass('shortpixel-hide');
445
  ShortPixel.recheckQuota();
446
  }
447
  }
448
+
449
  function includeUnlisted() {
450
  jQuery("#short-pixel-notice-unlisted").hide();
451
  jQuery("#optimizeUnlisted").prop('checked', true);
460
  });
461
  }
462
 
463
+
464
  function initFolderSelector() {
465
  jQuery(".select-folder-button").click(function(){
466
+ jQuery(".sp-folder-picker-shade").fadeIn(100); //.css("display", "block");
467
+ jQuery(".shortpixel-modal.modal-folder-picker").show();
468
+
469
+ var picker = jQuery(".sp-folder-picker");
470
+ picker.parent().css('margin-left', -picker.width() / 2);
471
+ picker.fileTree({
472
  script: ShortPixel.browseContent,
473
  //folderEvent: 'dblclick',
474
  multiFolder: false
475
  //onlyFolders: true
476
  });
477
  });
478
+ jQuery(".shortpixel-modal input.select-folder-cancel, .sp-folder-picker-shade").click(function(){
479
+ jQuery(".sp-folder-picker-shade").fadeOut(100); //.css("display", "none");
480
+ jQuery(".shortpixel-modal.modal-folder-picker").hide();
481
  });
482
+ jQuery(".shortpixel-modal input.select-folder").click(function(e){
483
+ //var subPath = jQuery("UL.jqueryFileTree LI.directory.selected A").attr("rel").trim();
484
+
485
+ // check if selected item is a directory. If so, we are good.
486
+ var selected = jQuery('UL.jqueryFileTree LI.directory.selected');
487
+
488
+ // if not a file might be selected, check the nearest directory.
489
+ if (jQuery(selected).length == 0 )
490
+ var selected = jQuery('UL.jqueryFileTree LI.selected').parents('.directory');
491
+
492
+ // fail-saif check if there is really a rel.
493
+ var subPath = jQuery(selected).children('a').attr('rel');
494
+
495
+ if (typeof subPath === 'undefined') // nothing is selected
496
+ return;
497
+
498
+ subPath = subPath.trim();
499
+
500
  if(subPath) {
501
  var fullPath = jQuery("#customFolderBase").val() + subPath;
502
  if(fullPath.slice(-1) == '/') fullPath = fullPath.slice(0, -1);
503
  jQuery("#addCustomFolder").val(fullPath);
504
  jQuery("#addCustomFolderView").val(fullPath);
505
+ jQuery(".sp-folder-picker-shade").fadeOut(100);
506
+ jQuery(".shortpixel-modal.modal-folder-picker").css("display", "none");
507
+ jQuery('input[name="saveAdv"]').removeClass('hidden');
508
  } else {
509
  alert("Please select a folder from the list.");
510
  }
511
  });
512
  }
513
+
514
  function bulkShowLengthyMsg(id, fileName, customLink){
515
  var notice = jQuery(".bulk-notice-msg.bulk-lengthy");
516
  if(notice.length == 0) return;
521
  } else {
522
  link.attr("href", link.data("href").replace("__ID__", id));
523
  }
524
+
525
  notice.css("display", "block");
526
  }
527
+
528
  function bulkHideLengthyMsg(){
529
  jQuery(".bulk-notice-msg.bulk-lengthy").css("display", "none");
530
  }
531
+
532
  function bulkShowMaintenanceMsg(type){
533
  var notice = jQuery(".bulk-notice-msg.bulk-" + type);
534
  if(notice.length == 0) return;
535
  notice.css("display", "block");
536
  }
537
+
538
  function bulkHideMaintenanceMsg(type){
539
  jQuery(".bulk-notice-msg.bulk-" + type).css("display", "none");
540
  }
541
+
542
  function bulkShowError(id, msg, fileName, customLink) {
543
  var noticeTpl = jQuery("#bulk-error-template");
544
  if(noticeTpl.length == 0) return;
560
  }
561
  link.text(fileName);
562
  noticeTpl.after(notice);
563
+ notice.css("display", "block");
564
  }
565
+
566
  function confirmBulkAction(type, e) {
567
  if(!confirm(_spTr['confirmBulk' + type])) {
568
  e.stopPropagation();
572
  return true;
573
  }
574
 
575
+ // used in bulk restore all interface
576
+ function checkRandomAnswer(e)
577
+ {
578
+ var value = jQuery(e.target).val();
579
+ var answer = jQuery('input[name="random_answer"]').val();
580
+ var target = jQuery('input[name="random_answer"]').data('target');
581
+
582
+ if (value == answer)
583
+ {
584
+ jQuery(target).removeClass('disabled').prop('disabled', false);
585
+ jQuery(target).removeAttr('aria-disabled');
586
+
587
+ }
588
+ else
589
+ {
590
+ jQuery(target).addClass('disabled').prop('disabled', true);
591
+ }
592
+
593
+ }
594
+
595
  function removeBulkMsg(me) {
596
  jQuery(me).parent().parent().remove();
597
  }
598
+
599
  function isCustomImageId(id) {
600
  return id.substring(0,2) == "C-";
601
  }
602
+
603
  function recheckQuota() {
604
  var parts = window.location.href.split('#');
605
  window.location.href=parts[0]+(parts[0].indexOf('?')>0?'&':'?')+'checkquota=1' + (typeof parts[1] === 'undefined' ? '' : '#' + parts[1]);
606
  }
607
+
608
  function openImageMenu(e) {
609
  e.preventDefault();
610
  //install (lazily) a window click event to close the menus
620
  jQuery('.sp-dropdown.sp-show').removeClass('sp-show');
621
  if(!shown) e.target.parentElement.classList.add("sp-show");
622
  }
623
+
624
  function loadComparer(id) {
625
  this.comparerData.origUrl = false;
626
+ if(this.comparerData.cssLoaded === false) {
 
627
  jQuery('<link>')
628
  .appendTo('head')
629
  .attr({
630
+ type: 'text/css',
631
  rel: 'stylesheet',
632
  href: this.WP_PLUGIN_URL + '/res/css/twentytwenty.min.css'
633
  });
641
  }
642
  });
643
  this.comparerData.jsLoaded = 1;
644
+ //jQuery(".sp-close-button").click(ShortPixel.closeComparerPopup);
645
  }
646
  if(this.comparerData.origUrl === false) {
647
  jQuery.ajax({
648
  type: "POST",
649
+ url: ShortPixel.AJAX_URL,
650
+ data: { action : 'shortpixel_get_comparer_data', id : id },
651
  success: function(response) {
652
  data = JSON.parse(response);
653
  jQuery.extend(ShortPixel.comparerData, data);
666
  //depending on the sizes choose the right modal
667
  var sideBySide = (height < 150 || width < 350);
668
  var modal = jQuery(sideBySide ? '#spUploadCompareSideBySide' : '#spUploadCompare');
669
+ var modalShade = jQuery('.sp-modal-shade');
670
+
671
  if(!sideBySide) {
672
  jQuery("#spCompareSlider").html('<img class="spUploadCompareOriginal"/><img class="spUploadCompareOptimized"/>');
673
  }
674
  //calculate the modal size
675
  width = Math.max(350, Math.min(800, (width < 350 ? (width + 25) * 2 : (height < 150 ? width + 25 : width))));
676
  height = Math.max(150, (sideBySide ? (origWidth > 350 ? 2 * (height + 45) : height + 45) : height * width / origWidth));
677
+
678
+ var marginLeft = '-' + Math.round(width/2); // center
679
+
680
  //set modal sizes and display
681
  jQuery(".sp-modal-body", modal).css("width", width);
682
  jQuery(".shortpixel-slider", modal).css("width", width);
683
  modal.css("width", width);
684
+ modal.css('marginLeft', marginLeft + 'px');
685
  jQuery(".sp-modal-body", modal).css("height", height);
686
+ modal.show();
687
+ //modal.parent().css('display', 'block');
688
+ modalShade.show();
689
+
690
  if(!sideBySide) {
691
  jQuery("#spCompareSlider").twentytwenty({slider_move: "mousemove"});
692
  }
693
+
694
+ // Close Options
695
+ jQuery(".sp-close-button").on('click', ShortPixel.closeComparerPopup);
696
  jQuery(document).on('keyup.sp_modal_active', ShortPixel.closeComparerPopup);
697
+ jQuery('.sp-modal-shade').on('click', ShortPixel.closeComparerPopup);
698
+
699
  //change images srcs
700
  var imgOpt = jQuery(".spUploadCompareOptimized", modal);
701
  jQuery(".spUploadCompareOriginal", modal).attr("src", imgOriginal);
708
  });
709
  imgOpt.attr("src", imgOptimized);
710
  }
711
+
712
  function closeComparerPopup(e) {
713
+ // jQuery("#spUploadCompareSideBySide").parent().css("display", 'none');
714
+ jQuery("#spUploadCompareSideBySide").hide();
715
+ jQuery("#spUploadCompare").hide();
716
+ jQuery('.sp-modal-shade').hide();
717
  jQuery(document).unbind('keyup.sp_modal_active');
718
+ jQuery('.sp-modal-shade').off('click');
719
+ jQuery(".sp-close-button").off('click');
720
  }
721
 
722
  function convertPunycode(url) {
763
  bulkHideMaintenanceMsg : bulkHideMaintenanceMsg,
764
  bulkShowError : bulkShowError,
765
  confirmBulkAction : confirmBulkAction,
766
+ checkRandomAnswer : checkRandomAnswer,
767
  removeBulkMsg : removeBulkMsg,
768
  isCustomImageId : isCustomImageId,
769
  recheckQuota : recheckQuota,
790
  var robo = jQuery("li.shortpixel-toolbar-processing");
791
  switch($status) {
792
  case ShortPixel.STATUS_QUOTA_EXCEEDED:
793
+ if( window.location.href.search("wp-short-pixel-bulk") > 0
794
  && jQuery(".sp-quota-exceeded-alert").length == 0) { //if we're in bulk and the alert is not displayed reload to see all options
795
  location.reload();
796
  return;
802
  //jQuery("a div", robo).attr("title", "ShortPixel quota exceeded. Click to top-up");
803
  jQuery("a div", robo).attr("title", "ShortPixel quota exceeded. Click for details.");
804
  break;
805
+ case ShortPixel.STATUS_SKIP:
806
  case ShortPixel.STATUS_FAIL:
807
  robo.addClass("shortpixel-alert shortpixel-processing");
808
  jQuery("a div", robo).attr("title", $message);
844
  }
845
  }
846
  /**
847
+ * JavaScript image processing - this method gets executed on every footer load and afterwards
848
  * calls itself until receives an Empty queue message
849
  */
850
  function checkBulkProgress() {
851
+ //the replace stands for malformed urls on some sites, like wp-admin//upload.php which are accepted by the browser.
852
  //using a replacer function to avoid replacing the first occurence (https:// ...)
853
  var replacer = function(match) {
854
  if(!first) {
860
 
861
  var first = false; //arm replacer
862
  var url = window.location.href.toLowerCase().replace(/\/\//g , replacer);
863
+
864
  first = false; //rearm replacer
865
  var adminUrl = ShortPixel.WP_ADMIN_URL.toLowerCase().replace(/\/\//g , replacer);
866
+
867
  //handle possible Punycode domain names.
868
  if(url.search(adminUrl) < 0) {
869
  url = ShortPixel.convertPunycode(url);
870
  adminUrl = ShortPixel.convertPunycode(adminUrl);
871
  }
872
+
873
  if( url.search(adminUrl + "upload.php") < 0
874
  && url.search(adminUrl + "edit.php") < 0
875
  && url.search(adminUrl + "edit-tags.php") < 0
881
  hideToolBarAlert();
882
  return;
883
  }
884
+
885
  //if i'm the bulk processor and i'm not the bulk page and a bulk page comes around, leave the bulk processor role
886
+ if(ShortPixel.bulkProcessor == true && window.location.href.search("wp-short-pixel-bulk") < 0
887
  && typeof localStorage.bulkPage !== 'undefined' && localStorage.bulkPage > 0) {
888
  ShortPixel.bulkProcessor = false;
889
  }
890
+
891
  //if i'm the bulk page, steal the bulk processor
892
  if( window.location.href.search("wp-short-pixel-bulk") >= 0 ) {
893
  ShortPixel.bulkProcessor = true;
894
  localStorage.bulkTime = Math.floor(Date.now() / 1000);
895
  localStorage.bulkPage = 1;
896
  }
897
+
898
  //if I'm not the bulk processor, check every 20 sec. if the bulk processor is running, otherwise take the role
899
  if(ShortPixel.bulkProcessor == true || typeof localStorage.bulkTime == 'undefined' || Math.floor(Date.now() / 1000) - localStorage.bulkTime > 90) {
900
  ShortPixel.bulkProcessor = true;
913
  jQuery.ajax({
914
  type: "POST",
915
  url: ShortPixel.AJAX_URL, //formerly ajaxurl , but changed it because it's not available in the front-end and now we have an option to run in the front-end
916
+ data: data,
917
+ success: function(response)
918
  {
919
  if(response.length > 0) {
920
  var data = null;
925
  return;
926
  }
927
  ShortPixel.retries = 0;
928
+
929
  var id = data["ImageID"];
930
 
931
  var isBulkPage = (jQuery("div.short-pixel-bulk-page").length > 0);
937
  showToolBarAlert(ShortPixel.STATUS_NO_KEY);
938
  break;
939
  case ShortPixel.STATUS_QUOTA_EXCEEDED:
940
+ setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"https://shortpixel.com/login/"
941
  + ShortPixel.API_KEY + "\" target=\"_blank\">" + _spTr.extendQuota + "</a>"
942
  + "<a class='button button-smaller' href='admin.php?action=shortpixel_check_quota'>" + _spTr.check__Quota + "</a>");
943
  showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED);
947
  ShortPixel.otherMediaUpdateActions(id, ['quota','view']);
948
  break;
949
  case ShortPixel.STATUS_FAIL:
950
+ setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('" + id + "', false)\">"
951
  + _spTr.retry + "</a>");
952
  showToolBarAlert(ShortPixel.STATUS_FAIL, data["Message"], id);
953
  if(isBulkPage) {
984
 
985
  showToolBarAlert(ShortPixel.STATUS_SUCCESS, "");
986
  //for now, until 4.1
987
+ var successActions = ShortPixel.isCustomImageId(id)
988
+ ? ""
989
  : ShortPixel.successActions(id, data["Type"], data['ThumbsCount'], data['ThumbsTotal'], data["BackupEnabled"], data['Filename']);
990
+
991
+ // [BS] Set success message to Box.
992
  setCellMessage(id, ShortPixel.successMsg(id, percent, data["Type"], data['ThumbsCount'], data['RetinasCount']), successActions);
993
+
994
+ // [BS] Replace fileName in Media Library Row to return fileName.
995
+ if (jQuery('#post-' + id).length > 0)
996
+ jQuery('#post-' + id).find('.filename').text(data['Filename']);
997
+
998
+ // [BS] Replace filename if in media item edit view
999
+ if (jQuery('.misc-pub-filename strong').length > 0)
1000
+ jQuery('.misc-pub-filename strong').text(data['Filename']);
1001
+
1002
+ // [BS] Only update date on Custom Media Page.
1003
+ if (ShortPixel.isCustomImageId(id) && data['TsOptimized'] && data['TsOptimized'].length > 0)
1004
+ {
1005
+ console.log(id);
1006
+ jQuery('.date-' + id).text(data['TsOptimized']);
1007
+ }
1008
+
1009
+
1010
  var actions = jQuery(['restore', 'view', 'redolossy', 'redoglossy', 'redolossless']).not(['redo'+data["Type"]]).get();
1011
  ShortPixel.otherMediaUpdateActions(id, actions);
1012
  var animator = new PercentageAnimator("#sp-msg-" + id + " span.percent", percent);
1022
  ShortPixel.percentDial("#sp-avg-optimization .dial", 60);
1023
  }
1024
  }
1025
+ }
1026
+
1027
  console.log('Server response: ' + response);
1028
  if(isBulkPage && typeof data["BulkPercent"] !== 'undefined') {
1029
  progressUpdate(data["BulkPercent"], data["BulkMsg"]);
1088
  msg.html("<div class='sp-column-actions'>" + actions + "</div>"
1089
  + "<div class='sp-column-info'>" + message + "</div>");
1090
  msg.css("color", "");
1091
+ }
1092
  msg = jQuery("#sp-cust-msg-" + id);
1093
  if(msg.length > 0) {
1094
  msg.html("<div class='sp-column-info'>" + message + "</div>");
1095
+ }
1096
  }
1097
 
1098
  function manualOptimization(id, cleanup) {
1109
  success: function(response) {
1110
  var resp = JSON.parse(response);
1111
  if(resp["Status"] == ShortPixel.STATUS_SUCCESS) {
1112
+ //TODO - when calling several manual optimizations, the checkBulkProgress gets scheduled several times so several loops run in || - make only one.
1113
  setTimeout(checkBulkProgress, 2000);
1114
  } else {
1115
  setCellMessage(id, typeof resp["Message"] !== "undefined" ? resp["Message"] : _spTr.thisContentNotProcessable, "");
1201
  this.curPercentage = 0;
1202
  this.targetPercentage = targetPercentage;
1203
  this.outputSelector = outputSelector;
1204
+
1205
  this.animate = function(percentage) {
1206
  this.targetPercentage = percentage;
1207
  setTimeout(PercentageTimer.bind(null, this), this.animationSpeed);
1210
 
1211
  function PercentageTimer(animator) {
1212
  if (animator.curPercentage - animator.targetPercentage < -animator.increment) {
1213
+ animator.curPercentage += animator.increment;
1214
  } else if (animator.curPercentage - animator.targetPercentage > animator.increment) {
1215
+ animator.curPercentage -= animator.increment;
1216
  } else {
1217
  animator.curPercentage = animator.targetPercentage;
1218
  }
1220
  jQuery(animator.outputSelector).text(animator.curPercentage + "%");
1221
 
1222
  if (animator.curPercentage != animator.targetPercentage) {
1223
+ setTimeout(PercentageTimer.bind(null,animator), animator.animationSpeed)
1224
  }
1225
  }
1226
 
1234
  jQuery(".progress-left", progress).html(percent + "%");
1235
  } else {
1236
  jQuery(".progress-img span", progress).html(percent + "%");
1237
+ jQuery(".progress-left", progress).html("");
1238
  }
1239
  jQuery(".bulk-estimate").html(message);
1240
  }
1246
  var oldSlide = jQuery(".bulk-slider div.bulk-slide:first-child");
1247
  if(oldSlide.length === 0) {
1248
  return;
1249
+ }
1250
  if(oldSlide.attr("id") != "empty-slide") {
1251
  oldSlide.hide();
1252
  }
1269
  jQuery(".img-original", newSlide).css("display", "none");
1270
  }
1271
  jQuery(".bulk-opt-percent", newSlide).html('<input type="text" class="dial" value="' + percent + '"/>');
1272
+
1273
  jQuery(".bulk-slider").append(newSlide);
1274
  ShortPixel.percentDial("#" + newSlide.attr("id") + " .dial", 100);
1275
+
1276
  jQuery(".bulk-slider-container span.filename").html("&nbsp;&nbsp;" + filename);
1277
  if(oldSlide.attr("id") == "empty-slide") {
1278
  oldSlide.remove();
1292
  function showStats() {
1293
  var statsDiv = jQuery(".bulk-stats");
1294
  if(statsDiv.length > 0) {
1295
+
1296
  }
1297
  }
1298
 
1299
+ if (!(typeof String.prototype.format == 'function')) {
1300
  String.prototype.format = function() {
1301
  var s = this,
1302
  i = arguments.length;
res/js/shortpixel.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(){ShortPixel.init()});var ShortPixel=function(){function I(){if(typeof ShortPixel.API_KEY!=="undefined"){return}if(jQuery("table.wp-list-table.media").length>0){jQuery('select[name^="action"] option:last-child').before('<option value="short-pixel-bulk">'+_spTr.optimizeWithSP+'</option><option value="short-pixel-bulk-lossy"> → '+_spTr.redoLossy+'</option><option value="short-pixel-bulk-glossy"> → '+_spTr.redoGlossy+'</option><option value="short-pixel-bulk-lossless"> → '+_spTr.redoLossless+'</option><option value="short-pixel-bulk-restore"> → '+_spTr.restoreOriginal+"</option>")}ShortPixel.setOptions(ShortPixelConstants[0]);if(jQuery("#backup-folder-size").length){jQuery("#backup-folder-size").html(ShortPixel.getBackupSize())}if(ShortPixel.MEDIA_ALERT=="todo"&&jQuery("div.media-frame.mode-grid").length>0){jQuery("div.media-frame.mode-grid").before('<div id="short-pixel-media-alert" class="notice notice-warning"><p>'+_spTr.changeMLToListMode.format('<a href="upload.php?mode=list" class="view-list"><span class="screen-reader-text">'," </span>",'</a><a class="alignright" href="javascript:ShortPixel.dismissMediaAlert();">',"</a>")+"</p></div>")}jQuery(window).on("beforeunload",function(){if(ShortPixel.bulkProcessor==true){clearBulkProcessor()}});checkQuotaExceededAlert();checkBulkProgress()}function m(Q){for(var R in Q){ShortPixel[R]=Q[R]}}function t(Q){return/^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,63})+$/.test(Q)}function n(){var Q=jQuery("#pluginemail").val();if(ShortPixel.isEmailValid(Q)){jQuery("#request_key").removeClass("disabled")}jQuery("#request_key").attr("href",jQuery("#request_key").attr("href").split("?")[0]+"?pluginemail="+Q)}function a(){jQuery("#valid").val("validate");jQuery("#wp_shortpixel_options").submit()}jQuery("#key").keypress(function(Q){if(Q.which==13){jQuery("#valid").val("validate")}});function L(Q){if(jQuery(Q).is(":checked")){jQuery("#width,#height").removeAttr("disabled")}else{jQuery("#width,#height").attr("disabled","disabled")}}function e(Q,S,U){for(var R=0,T=null;R<Q.length;R++){Q[R].onclick=function(){if(this!==T){T=this}if(typeof ShortPixel.setupGeneralTabAlert!=="undefined"){return}alert(_spTr.alertOnlyAppliesToNewImages);ShortPixel.setupGeneralTabAlert=1}}ShortPixel.enableResize("#resize");jQuery("#resize").change(function(){L(this)});jQuery(".resize-sizes").blur(function(W){var X=jQuery(this);if(ShortPixel.resizeSizesAlert==X.val()){return}ShortPixel.resizeSizesAlert=X.val();var V=jQuery("#min-"+X.attr("name")).val();if(X.val()<Math.min(V,1024)){if(V>1024){alert(_spTr.pleaseDoNotSetLesser1024.format(X.attr("name")))}else{alert(_spTr.pleaseDoNotSetLesserSize.format(X.attr("name"),X.attr("name"),V))}W.preventDefault();X.focus()}else{this.defaultValue=X.val()}});jQuery(".shortpixel-confirm").click(function(W){var V=confirm(W.target.getAttribute("data-confirm"));if(!V){W.preventDefault();return false}return true})}function C(){jQuery(".wp-shortpixel-options .shortpixel-key-valid").css("display","none");jQuery(".wp-shortpixel-options button#validate").css("display","inline-block")}function u(){jQuery("input.remove-folder-button").click(function(){var R=jQuery(this).data("value");var Q=confirm(_spTr.areYouSureStopOptimizing.format(R));if(Q==true){jQuery("#removeFolder").val(R);jQuery("#wp_shortpixel_options").submit()}});jQuery("input.recheck-folder-button").click(function(){var R=jQuery(this).data("value");var Q=confirm(_spTr.areYouSureStopOptimizing.format(R));if(Q==true){jQuery("#recheckFolder").val(R);jQuery("#wp_shortpixel_options").submit()}})}function K(Q){var R=jQuery("#"+(Q.checked?"total":"main")+"ToProcess").val();jQuery("div.bulk-play span.total").text(R);jQuery("#displayTotal").text(R)}function g(){ShortPixel.adjustSettingsTabs();jQuery(window).resize(function(){ShortPixel.adjustSettingsTabs()});if(window.location.hash){var Q=("tab-"+window.location.hash.substring(window.location.hash.indexOf("#")+1)).replace(/\//,"");if(jQuery("section#"+Q).length){ShortPixel.switchSettingsTab(Q)}}jQuery("article.sp-tabs a.tab-link").click(function(){var R=jQuery(this).data("id");ShortPixel.switchSettingsTab(R)});jQuery("input[type=radio][name=deliverWebpType]").change(function(){if(this.value=="deliverWebpAltered"){if(window.confirm(_spTr.alertDeliverWebPAltered)){var R=jQuery("input[type=radio][name=deliverWebpAlteringType]:checked").length;if(R==0){jQuery("#deliverWebpAlteredWP").prop("checked",true)}}else{jQuery(this).prop("checked",false)}}else{if(this.value=="deliverWebpUnaltered"){window.alert(_spTr.alertDeliverWebPUnaltered)}}})}function x(U){var S=U.replace("tab-",""),Q="",T=jQuery("section#"+U),R=location.href.replace(location.hash,"")+"#"+S;if(history.pushState){history.pushState(null,null,R)}else{location.hash=R}if(T.length>0){jQuery("section").removeClass("sel-tab");jQuery("section#"+U).addClass("sel-tab")}if(typeof HS.beacon.suggest!=="undefined"){switch(S){case"settings":Q=shortpixel_suggestions_settings;break;case"adv-settings":Q=shortpixel_suggestions_adv_settings;break;case"cloudflare":case"stats":Q=shortpixel_suggestions_cloudflare;break;default:break}HS.beacon.suggest(Q)}}function y(){var Q=jQuery("section#tab-settings .wp-shortpixel-options").height()+90;Q=Math.max(Q,jQuery("section#tab-adv-settings .wp-shortpixel-options").height()+20);Q=Math.max(Q,jQuery("section#tab-resources .area1").height()+60);jQuery("#shortpixel-settings-tabs").css("height",Q)}function M(){var Q={action:"shortpixel_dismiss_media_alert"};jQuery.get(ShortPixel.AJAX_URL,Q,function(R){Q=JSON.parse(R);if(Q.Status=="success"){jQuery("#short-pixel-media-alert").hide();console.log("dismissed")}})}function k(){var Q={action:"shortpixel_check_quota"};jQuery.get(ShortPixel.AJAX_URL,Q,function(){console.log("quota refreshed")})}function B(Q){if(Q.checked){jQuery("#with-thumbs").css("display","inherit");jQuery("#without-thumbs").css("display","none")}else{jQuery("#without-thumbs").css("display","inherit");jQuery("#with-thumbs").css("display","none")}}function b(U,S,R,T,Q){return(S>0?"<div class='sp-column-info'>"+_spTr.reducedBy+" <strong><span class='percent'>"+S+"%</span></strong> ":"")+(S>0&&S<5?"<br>":"")+(S<5?_spTr.bonusProcessing:"")+(R.length>0?" ("+R+")":"")+(0+T>0?"<br>"+_spTr.plusXthumbsOpt.format(T):"")+(0+Q>0?"<br>"+_spTr.plusXretinasOpt.format(Q):"")+"</div>"}function p(R,Q){jQuery(R).knob({readOnly:true,width:Q,height:Q,fgColor:"#1CAECB",format:function(S){return S+"%"}})}function c(X,S,V,U,R,W){if(R==1){var T=jQuery(".sp-column-actions-template").clone();if(!T.length){return false}var Q;if(S.length==0){Q=["lossy","lossless"]}else{Q=["lossy","glossy","lossless"].filter(function(Y){return !(Y==S)})}T.html(T.html().replace(/__SP_ID__/g,X));if(W.substr(W.lastIndexOf(".")+1).toLowerCase()=="pdf"){jQuery(".sp-action-compare",T).remove()}if(V==0&&U>0){T.html(T.html().replace("__SP_THUMBS_TOTAL__",U))}else{jQuery(".sp-action-optimize-thumbs",T).remove();jQuery(".sp-dropbtn",T).removeClass("button-primary")}T.html(T.html().replace(/__SP_FIRST_TYPE__/g,Q[0]));T.html(T.html().replace(/__SP_SECOND_TYPE__/g,Q[1]));return T.html()}return""}function i(U,T){U=U.substring(2);if(jQuery(".shortpixel-other-media").length){var S=["optimize","retry","restore","redo","quota","view"];for(var R=0,Q=S.length;R<Q;R++){jQuery("#"+S[R]+"_"+U).css("display","none")}for(var R=0,Q=T.length;R<Q;R++){jQuery("#"+T[R]+"_"+U).css("display","")}}}function j(Q){ShortPixel.retries++;if(isNaN(ShortPixel.retries)){ShortPixel.retries=1}if(ShortPixel.retries<6){console.log("Invalid response from server (Error: "+Q+"). Retrying pass "+(ShortPixel.retries+1)+"...");setTimeout(checkBulkProgress,5000)}else{ShortPixel.bulkShowError(-1,"Invalid response from server received 6 times. Please retry later by reloading this page, or <a href='https://shortpixel.com/contact' target='_blank'>contact support</a>. (Error: "+Q+")","");console.log("Invalid response from server 6 times. Giving up.")}}function l(Q){Q.action="shortpixel_browse_content";var R="";jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:Q,success:function(S){R=S},async:false});return R}function d(){var Q={action:"shortpixel_get_backup_size"};var R="";jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:Q,success:function(S){R=S},async:false});return R}function f(R){if(!jQuery("#tos").is(":checked")){R.preventDefault();jQuery("#tos-robo").fadeIn(400,function(){jQuery("#tos-hand").fadeIn()});jQuery("#tos").click(function(){jQuery("#tos-robo").css("display","none");jQuery("#tos-hand").css("display","none")});return}jQuery("#request_key").addClass("disabled");jQuery("#pluginemail_spinner").addClass("is-active");ShortPixel.updateSignupEmail();if(ShortPixel.isEmailValid(jQuery("#pluginemail").val())){jQuery("#pluginemail-error").css("display","none");var Q={action:"shortpixel_new_api_key",email:jQuery("#pluginemail").val()};jQuery.ajax({type:"POST",async:false,url:ShortPixel.AJAX_URL,data:Q,success:function(S){data=JSON.parse(S);if(data.Status=="success"){R.preventDefault();window.location.reload()}else{if(data.Status=="invalid"){jQuery("#pluginemail-error").html("<b>"+data.Details+"</b>");jQuery("#pluginemail-error").css("display","");jQuery("#pluginemail-info").css("display","none");R.preventDefault()}else{}}}});jQuery("#request_key").removeAttr("onclick")}else{jQuery("#pluginemail-error").css("display","");jQuery("#pluginemail-info").css("display","none");R.preventDefault()}jQuery("#request_key").removeClass("disabled");jQuery("#pluginemail_spinner").removeClass("is-active")}function N(){jQuery("#shortPixelProposeUpgrade .sp-modal-body").addClass("sptw-modal-spinner");jQuery("#shortPixelProposeUpgrade .sp-modal-body").html("");jQuery("#shortPixelProposeUpgradeShade").css("display","block");jQuery("#shortPixelProposeUpgrade").removeClass("shortpixel-hide");var Q={action:"shortpixel_propose_upgrade"};jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:Q,success:function(R){jQuery("#shortPixelProposeUpgrade .sp-modal-body").removeClass("sptw-modal-spinner");jQuery("#shortPixelProposeUpgrade .sp-modal-body").html(R)}})}function G(){jQuery("#shortPixelProposeUpgradeShade").css("display","none");jQuery("#shortPixelProposeUpgrade").addClass("shortpixel-hide");if(ShortPixel.toRefresh){ShortPixel.recheckQuota()}}function v(){jQuery("#short-pixel-notice-unlisted").hide();jQuery("#optimizeUnlisted").prop("checked",true);var Q={action:"shortpixel_dismiss_notice",notice_id:"unlisted",notice_data:"true"};jQuery.get(ShortPixel.AJAX_URL,Q,function(R){Q=JSON.parse(R);if(Q.Status==ShortPixel.STATUS_SUCCESS){console.log("dismissed")}})}function o(){jQuery(".select-folder-button").click(function(){jQuery(".sp-folder-picker-shade").css("display","block");jQuery(".sp-folder-picker").fileTree({script:ShortPixel.browseContent,multiFolder:false})});jQuery(".shortpixel-modal input.select-folder-cancel").click(function(){jQuery(".sp-folder-picker-shade").css("display","none")});jQuery(".shortpixel-modal input.select-folder").click(function(){var Q=jQuery("UL.jqueryFileTree LI.directory.selected A").attr("rel").trim();if(Q){var R=jQuery("#customFolderBase").val()+Q;if(R.slice(-1)=="/"){R=R.slice(0,-1)}jQuery("#addCustomFolder").val(R);jQuery("#addCustomFolderView").val(R);jQuery(".sp-folder-picker-shade").css("display","none")}else{alert("Please select a folder from the list.")}})}function F(U,T,S){var R=jQuery(".bulk-notice-msg.bulk-lengthy");if(R.length==0){return}var Q=jQuery("a",R);Q.text(T);if(S){Q.attr("href",S)}else{Q.attr("href",Q.data("href").replace("__ID__",U))}R.css("display","block")}function A(){jQuery(".bulk-notice-msg.bulk-lengthy").css("display","none")}function w(Q){var R=jQuery(".bulk-notice-msg.bulk-"+Q);if(R.length==0){return}R.css("display","block")}function O(Q){jQuery(".bulk-notice-msg.bulk-"+Q).css("display","none")}function s(W,U,V,T){var Q=jQuery("#bulk-error-template");if(Q.length==0){return}var S=Q.clone();S.attr("id","bulk-error-"+W);if(W==-1){jQuery("span.sp-err-title",S).remove();S.addClass("bulk-error-fatal")}else{jQuery("img",S).remove();jQuery("#bulk-error-".id).remove()}jQuery("span.sp-err-content",S).html(U);var R=jQuery("a.sp-post-link",S);if(T){R.attr("href",T)}else{R.attr("href",R.attr("href").replace("__ID__",W))}R.text(V);Q.after(S);S.css("display","block")}function D(Q,R){if(!confirm(_spTr["confirmBulk"+Q])){R.stopPropagation();R.preventDefault();return false}return true}function r(Q){jQuery(Q).parent().parent().remove()}function H(Q){return Q.substring(0,2)=="C-"}function J(){var Q=window.location.href.split("#");window.location.href=Q[0]+(Q[0].indexOf("?")>0?"&":"?")+"checkquota=1"+(typeof Q[1]==="undefined"?"":"#"+Q[1])}function h(R){R.preventDefault();if(!this.menuCloseEvent){jQuery(window).click(function(S){if(!S.target.matches(".sp-dropbtn")){jQuery(".sp-dropdown.sp-show").removeClass("sp-show")}});this.menuCloseEvent=true}var Q=R.target.parentElement.classList.contains("sp-show");jQuery(".sp-dropdown.sp-show").removeClass("sp-show");if(!Q){R.target.parentElement.classList.add("sp-show")}}function P(Q){this.comparerData.origUrl=false;if(this.comparerData.cssLoaded===false){jQuery("<link>").appendTo("head").attr({type:"text/css",rel:"stylesheet",href:this.WP_PLUGIN_URL+"/res/css/twentytwenty.min.css"});this.comparerData.cssLoaded=2}if(this.comparerData.jsLoaded===false){jQuery.getScript(this.WP_PLUGIN_URL+"/res/js/jquery.twentytwenty.min.js",function(){ShortPixel.comparerData.jsLoaded=2;if(ShortPixel.comparerData.origUrl.length>0){ShortPixel.displayComparerPopup(ShortPixel.comparerData.width,ShortPixel.comparerData.height,ShortPixel.comparerData.origUrl,ShortPixel.comparerData.optUrl)}});this.comparerData.jsLoaded=1;jQuery(".sp-close-button").click(ShortPixel.closeComparerPopup)}if(this.comparerData.origUrl===false){jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:{action:"shortpixel_get_comparer_data",id:Q},success:function(R){data=JSON.parse(R);jQuery.extend(ShortPixel.comparerData,data);if(ShortPixel.comparerData.jsLoaded==2){ShortPixel.displayComparerPopup(ShortPixel.comparerData.width,ShortPixel.comparerData.height,ShortPixel.comparerData.origUrl,ShortPixel.comparerData.optUrl)}}});this.comparerData.origUrl=""}}function E(U,S,R,T){var X=U;var W=(S<150||U<350);var V=jQuery(W?"#spUploadCompareSideBySide":"#spUploadCompare");if(!W){jQuery("#spCompareSlider").html('<img class="spUploadCompareOriginal"/><img class="spUploadCompareOptimized"/>')}U=Math.max(350,Math.min(800,(U<350?(U+25)*2:(S<150?U+25:U))));S=Math.max(150,(W?(X>350?2*(S+45):S+45):S*U/X));jQuery(".sp-modal-body",V).css("width",U);jQuery(".shortpixel-slider",V).css("width",U);V.css("width",U);jQuery(".sp-modal-body",V).css("height",S);V.css("display","block");V.parent().css("display","block");if(!W){jQuery("#spCompareSlider").twentytwenty({slider_move:"mousemove"})}jQuery(document).on("keyup.sp_modal_active",ShortPixel.closeComparerPopup);var Q=jQuery(".spUploadCompareOptimized",V);jQuery(".spUploadCompareOriginal",V).attr("src",R);setTimeout(function(){jQuery(window).trigger("resize")},1000);Q.load(function(){jQuery(window).trigger("resize")});Q.attr("src",T)}function q(Q){jQuery("#spUploadCompareSideBySide").parent().css("display","none");jQuery("#spUploadCompareSideBySide").css("display","none");jQuery("#spUploadCompare").css("display","none");jQuery(document).unbind("keyup.sp_modal_active")}function z(Q){var R=document.createElement("a");R.href=Q;if(Q.indexOf(R.protocol+"//"+R.hostname)<0){return R.href}return Q.replace(R.protocol+"//"+R.hostname,R.protocol+"//"+R.hostname.split(".").map(function(S){return sp_punycode.toASCII(S)}).join("."))}return{init:I,setOptions:m,isEmailValid:t,updateSignupEmail:n,validateKey:a,enableResize:L,setupGeneralTab:e,apiKeyChanged:C,setupAdvancedTab:u,checkThumbsUpdTotal:K,initSettings:g,switchSettingsTab:x,adjustSettingsTabs:y,onBulkThumbsCheck:B,dismissMediaAlert:M,checkQuota:k,percentDial:p,successMsg:b,successActions:c,otherMediaUpdateActions:i,retry:j,initFolderSelector:o,browseContent:l,getBackupSize:d,newApiKey:f,proposeUpgrade:N,closeProposeUpgrade:G,includeUnlisted:v,bulkShowLengthyMsg:F,bulkHideLengthyMsg:A,bulkShowMaintenanceMsg:w,bulkHideMaintenanceMsg:O,bulkShowError:s,confirmBulkAction:D,removeBulkMsg:r,isCustomImageId:H,recheckQuota:J,openImageMenu:h,menuCloseEvent:false,loadComparer:P,displayComparerPopup:E,closeComparerPopup:q,convertPunycode:z,comparerData:{cssLoaded:false,jsLoaded:false,origUrl:false,optUrl:false,width:0,height:0},toRefresh:false,resizeSizesAlert:false}}();function showToolBarAlert(c,b,d){var a=jQuery("li.shortpixel-toolbar-processing");switch(c){case ShortPixel.STATUS_QUOTA_EXCEEDED:if(window.location.href.search("wp-short-pixel-bulk")>0&&jQuery(".sp-quota-exceeded-alert").length==0){location.reload();return}a.addClass("shortpixel-alert");a.addClass("shortpixel-quota-exceeded");jQuery("a",a).attr("href","options-general.php?page=wp-shortpixel");jQuery("a div",a).attr("title","ShortPixel quota exceeded. Click for details.");break;case ShortPixel.STATUS_SKIP:case ShortPixel.STATUS_FAIL:a.addClass("shortpixel-alert shortpixel-processing");jQuery("a div",a).attr("title",b);if(typeof d!=="undefined"){jQuery("a",a).attr("href","post.php?post="+d+"&action=edit")}break;case ShortPixel.STATUS_NO_KEY:a.addClass("shortpixel-alert");a.addClass("shortpixel-quota-exceeded");jQuery("a",a).attr("href","options-general.php?page=wp-shortpixel");jQuery("a div",a).attr("title","Get API Key");break;case ShortPixel.STATUS_SUCCESS:case ShortPixel.STATUS_RETRY:a.addClass("shortpixel-processing");a.removeClass("shortpixel-alert");jQuery("a",a).removeAttr("target");jQuery("a",a).attr("href",jQuery("a img",a).attr("success-url"))}a.removeClass("shortpixel-hide")}function hideToolBarAlert(){jQuery("li.shortpixel-toolbar-processing.shortpixel-processing").addClass("shortpixel-hide")}function hideQuotaExceededToolBarAlert(){jQuery("li.shortpixel-toolbar-processing.shortpixel-quota-exceeded").addClass("shortpixel-hide")}function checkQuotaExceededAlert(){if(typeof shortPixelQuotaExceeded!="undefined"){if(shortPixelQuotaExceeded==1){showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED)}else{hideQuotaExceededToolBarAlert()}}}function checkBulkProgress(){var b=function(e){if(!d){d=true;return e}return"/"};var d=false;var a=window.location.href.toLowerCase().replace(/\/\//g,b);d=false;var c=ShortPixel.WP_ADMIN_URL.toLowerCase().replace(/\/\//g,b);if(a.search(c)<0){a=ShortPixel.convertPunycode(a);c=ShortPixel.convertPunycode(c)}if(a.search(c+"upload.php")<0&&a.search(c+"edit.php")<0&&a.search(c+"edit-tags.php")<0&&a.search(c+"post-new.php")<0&&a.search(c+"post.php")<0&&a.search("page=nggallery-manage-gallery")<0&&(ShortPixel.FRONT_BOOTSTRAP==0||a.search(c)==0)){hideToolBarAlert();return}if(ShortPixel.bulkProcessor==true&&window.location.href.search("wp-short-pixel-bulk")<0&&typeof localStorage.bulkPage!=="undefined"&&localStorage.bulkPage>0){ShortPixel.bulkProcessor=false}if(window.location.href.search("wp-short-pixel-bulk")>=0){ShortPixel.bulkProcessor=true;localStorage.bulkTime=Math.floor(Date.now()/1000);localStorage.bulkPage=1}if(ShortPixel.bulkProcessor==true||typeof localStorage.bulkTime=="undefined"||Math.floor(Date.now()/1000)-localStorage.bulkTime>90){ShortPixel.bulkProcessor=true;localStorage.bulkPage=(window.location.href.search("wp-short-pixel-bulk")>=0?1:0);localStorage.bulkTime=Math.floor(Date.now()/1000);console.log(localStorage.bulkTime);checkBulkProcessingCallApi()}else{setTimeout(checkBulkProgress,5000)}}function checkBulkProcessingCallApi(){var a={action:"shortpixel_image_processing"};jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:a,success:function(g){if(g.length>0){var i=null;try{var i=JSON.parse(g)}catch(k){ShortPixel.retry(k.message);return}ShortPixel.retries=0;var d=i.ImageID;var j=(jQuery("div.short-pixel-bulk-page").length>0);switch(i.Status){case ShortPixel.STATUS_NO_KEY:setCellMessage(d,i.Message,"<a class='button button-smaller button-primary' href=\"https://shortpixel.com/wp-apikey"+ShortPixel.AFFILIATE+'" target="_blank">'+_spTr.getApiKey+"</a>");showToolBarAlert(ShortPixel.STATUS_NO_KEY);break;case ShortPixel.STATUS_QUOTA_EXCEEDED:setCellMessage(d,i.Message,"<a class='button button-smaller button-primary' href=\"https://shortpixel.com/login/"+ShortPixel.API_KEY+'" target="_blank">'+_spTr.extendQuota+"</a><a class='button button-smaller' href='admin.php?action=shortpixel_check_quota'>"+_spTr.check__Quota+"</a>");showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED);if(i.Stop==false){setTimeout(checkBulkProgress,5000)}ShortPixel.otherMediaUpdateActions(d,["quota","view"]);break;case ShortPixel.STATUS_FAIL:setCellMessage(d,i.Message,"<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('"+d+"', false)\">"+_spTr.retry+"</a>");showToolBarAlert(ShortPixel.STATUS_FAIL,i.Message,d);if(j){ShortPixel.bulkShowError(d,i.Message,i.Filename,i.CustomImageLink);if(i.BulkPercent){progressUpdate(i.BulkPercent,i.BulkMsg)}ShortPixel.otherMediaUpdateActions(d,["retry","view"])}console.log(i.Message);setTimeout(checkBulkProgress,5000);break;case ShortPixel.STATUS_EMPTY_QUEUE:console.log(i.Message);clearBulkProcessor();hideToolBarAlert();var c=jQuery("#bulk-progress");if(j&&c.length&&i.BulkStatus!="2"){progressUpdate(100,"Bulk finished!");jQuery("a.bulk-cancel").attr("disabled","disabled");hideSlider();setTimeout(function(){window.location.reload()},3000)}break;case ShortPixel.STATUS_SUCCESS:if(j){ShortPixel.bulkHideLengthyMsg();ShortPixel.bulkHideMaintenanceMsg()}var l=i.PercentImprovement;showToolBarAlert(ShortPixel.STATUS_SUCCESS,"");var b=ShortPixel.isCustomImageId(d)?"":ShortPixel.successActions(d,i.Type,i.ThumbsCount,i.ThumbsTotal,i.BackupEnabled,i.Filename);setCellMessage(d,ShortPixel.successMsg(d,l,i.Type,i.ThumbsCount,i.RetinasCount),b);var h=jQuery(["restore","view","redolossy","redoglossy","redolossless"]).not(["redo"+i.Type]).get();ShortPixel.otherMediaUpdateActions(d,h);var f=new PercentageAnimator("#sp-msg-"+d+" span.percent",l);f.animate(l);if(j&&typeof i.Thumb!=="undefined"){if(i.BulkPercent){progressUpdate(i.BulkPercent,i.BulkMsg)}if(i.Thumb.length>0){sliderUpdate(d,i.Thumb,i.BkThumb,i.PercentImprovement,i.Filename);if(typeof i.AverageCompression!=="undefined"&&0+i.AverageCompression>0){jQuery("#sp-avg-optimization").html('<input type="text" class="dial" value="'+Math.round(i.AverageCompression)+'"/>');ShortPixel.percentDial("#sp-avg-optimization .dial",60)}}}console.log("Server response: "+g);if(j&&typeof i.BulkPercent!=="undefined"){progressUpdate(i.BulkPercent,i.BulkMsg)}setTimeout(checkBulkProgress,5000);break;case ShortPixel.STATUS_SKIP:if(i.Silent!==1){ShortPixel.bulkShowError(d,i.Message,i.Filename,i.CustomImageLink)}case ShortPixel.STATUS_ERROR:if(typeof i.Message!=="undefined"){showToolBarAlert(ShortPixel.STATUS_SKIP,i.Message+" Image ID: "+d);setCellMessage(d,i.Message,"")}ShortPixel.otherMediaUpdateActions(d,["retry","view"]);case ShortPixel.STATUS_RETRY:console.log("Server response: "+g);showToolBarAlert(ShortPixel.STATUS_RETRY,"");if(j&&typeof i.BulkPercent!=="undefined"){progressUpdate(i.BulkPercent,i.BulkMsg)}if(j&&i.Count>3){ShortPixel.bulkShowLengthyMsg(d,i.Filename,i.CustomImageLink)}setTimeout(checkBulkProgress,5000);break;case ShortPixel.STATUS_MAINTENANCE:ShortPixel.bulkShowMaintenanceMsg("maintenance");setTimeout(checkBulkProgress,60000);break;case ShortPixel.STATUS_QUEUE_FULL:ShortPixel.bulkShowMaintenanceMsg("queue-full");setTimeout(checkBulkProgress,60000);break;default:ShortPixel.retry("Unknown status "+i.Status+". Retrying...");break}}},error:function(b){ShortPixel.retry(b.statusText)}})}function clearBulkProcessor(){ShortPixel.bulkProcessor=false;localStorage.bulkTime=0;if(window.location.href.search("wp-short-pixel-bulk")>=0){localStorage.bulkPage=0}}function setCellMessage(d,a,c){var b=jQuery("#sp-msg-"+d);if(b.length>0){b.html("<div class='sp-column-actions'>"+c+"</div><div class='sp-column-info'>"+a+"</div>");b.css("color","")}b=jQuery("#sp-cust-msg-"+d);if(b.length>0){b.html("<div class='sp-column-info'>"+a+"</div>")}}function manualOptimization(c,a){setCellMessage(c,"<img src='"+ShortPixel.WP_PLUGIN_URL+"/res/img/loading.gif' class='sp-loading-small'>Image waiting to be processed","");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-hide");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-alert");jQuery("li.shortpixel-toolbar-processing").addClass("shortpixel-processing");var b={action:"shortpixel_manual_optimization",image_id:c,cleanup:a};jQuery.ajax({type:"GET",url:ShortPixel.AJAX_URL,data:b,success:function(d){var e=JSON.parse(d);if(e.Status==ShortPixel.STATUS_SUCCESS){setTimeout(checkBulkProgress,2000)}else{setCellMessage(c,typeof e.Message!=="undefined"?e.Message:_spTr.thisContentNotProcessable,"")}},error:function(d){b.action="shortpixel_check_status";jQuery.ajax({type:"GET",url:ShortPixel.AJAX_URL,data:b,success:function(e){var f=JSON.parse(e);if(f.Status!==ShortPixel.STATUS_SUCCESS){setCellMessage(c,typeof f.Message!=="undefined"?f.Message:_spTr.thisContentNotProcessable,"")}}})}})}function reoptimize(c,a){setCellMessage(c,"<img src='"+ShortPixel.WP_PLUGIN_URL+"/res/img/loading.gif' class='sp-loading-small'>Image waiting to be reprocessed","");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-hide");jQuery("li.shortpixel-toolbar-processing").addClass("shortpixel-processing");var b={action:"shortpixel_redo",attachment_ID:c,type:a};jQuery.get(ShortPixel.AJAX_URL,b,function(d){b=JSON.parse(d);if(b.Status==ShortPixel.STATUS_SUCCESS){setTimeout(checkBulkProgress,2000)}else{$msg=typeof b.Message!=="undefined"?b.Message:_spTr.thisContentNotProcessable;setCellMessage(c,$msg,"");showToolBarAlert(ShortPixel.STATUS_FAIL,$msg)}})}function optimizeThumbs(b){setCellMessage(b,"<img src='"+ShortPixel.WP_PLUGIN_URL+"/res/img/loading.gif' class='sp-loading-small'>"+_spTr.imageWaitOptThumbs,"");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-hide");jQuery("li.shortpixel-toolbar-processing").addClass("shortpixel-processing");var a={action:"shortpixel_optimize_thumbs",attachment_ID:b};jQuery.get(ShortPixel.AJAX_URL,a,function(c){a=JSON.parse(c);if(a.Status==ShortPixel.STATUS_SUCCESS){setTimeout(checkBulkProgress,2000)}else{setCellMessage(b,typeof a.Message!=="undefined"?a.Message:_spTr.thisContentNotProcessable,"")}})}function dismissShortPixelNoticeExceed(b){jQuery("#wp-admin-bar-shortpixel_processing").hide();var a={action:"shortpixel_dismiss_notice",notice_id:"exceed"};jQuery.get(ShortPixel.AJAX_URL,a,function(c){a=JSON.parse(c);if(a.Status==ShortPixel.STATUS_SUCCESS){console.log("dismissed")}});b.preventDefault()}function dismissShortPixelNotice(b){jQuery("#short-pixel-notice-"+b).hide();var a={action:"shortpixel_dismiss_notice",notice_id:b};jQuery.get(ShortPixel.AJAX_URL,a,function(c){a=JSON.parse(c);if(a.Status==ShortPixel.STATUS_SUCCESS){console.log("dismissed")}})}function PercentageAnimator(b,a){this.animationSpeed=10;this.increment=2;this.curPercentage=0;this.targetPercentage=a;this.outputSelector=b;this.animate=function(c){this.targetPercentage=c;setTimeout(PercentageTimer.bind(null,this),this.animationSpeed)}}function PercentageTimer(a){if(a.curPercentage-a.targetPercentage<-a.increment){a.curPercentage+=a.increment}else{if(a.curPercentage-a.targetPercentage>a.increment){a.curPercentage-=a.increment}else{a.curPercentage=a.targetPercentage}}jQuery(a.outputSelector).text(a.curPercentage+"%");if(a.curPercentage!=a.targetPercentage){setTimeout(PercentageTimer.bind(null,a),a.animationSpeed)}}function progressUpdate(c,b){var a=jQuery("#bulk-progress");if(a.length){jQuery(".progress-left",a).css("width",c+"%");jQuery(".progress-img",a).css("left",c+"%");if(c>24){jQuery(".progress-img span",a).html("");jQuery(".progress-left",a).html(c+"%")}else{jQuery(".progress-img span",a).html(c+"%");jQuery(".progress-left",a).html("")}jQuery(".bulk-estimate").html(b)}}function sliderUpdate(g,c,d,e,b){var f=jQuery(".bulk-slider div.bulk-slide:first-child");if(f.length===0){return}if(f.attr("id")!="empty-slide"){f.hide()}f.css("z-index",1000);jQuery(".bulk-img-opt",f).attr("src","");if(typeof d==="undefined"){d=""}if(d.length>0){jQuery(".bulk-img-orig",f).attr("src","")}var a=f.clone();a.attr("id","slide-"+g);jQuery(".bulk-img-opt",a).attr("src",c);if(d.length>0){jQuery(".img-original",a).css("display","inline-block");jQuery(".bulk-img-orig",a).attr("src",d)}else{jQuery(".img-original",a).css("display","none")}jQuery(".bulk-opt-percent",a).html('<input type="text" class="dial" value="'+e+'"/>');jQuery(".bulk-slider").append(a);ShortPixel.percentDial("#"+a.attr("id")+" .dial",100);jQuery(".bulk-slider-container span.filename").html("&nbsp;&nbsp;"+b);if(f.attr("id")=="empty-slide"){f.remove();jQuery(".bulk-slider-container").css("display","block")}else{f.animate({left:f.width()+f.position().left},"slow","swing",function(){f.remove();a.fadeIn("slow")})}}function hideSlider(){jQuery(".bulk-slider-container").css("display","none")}function showStats(){var a=jQuery(".bulk-stats");if(a.length>0){}}if(!(typeof String.prototype.format=="function")){String.prototype.format=function(){var b=this,a=arguments.length;while(a--){b=b.replace(new RegExp("\\{"+a+"\\}","gm"),arguments[a])}return b}};
1
+ jQuery(document).ready(function(){ShortPixel.init()});var ShortPixel=function(){function J(){if(typeof ShortPixel.API_KEY!=="undefined"){return}if(jQuery("table.wp-list-table.media").length>0){jQuery('select[name^="action"] option:last-child').before('<option value="short-pixel-bulk">'+_spTr.optimizeWithSP+'</option><option value="short-pixel-bulk-lossy"> → '+_spTr.redoLossy+'</option><option value="short-pixel-bulk-glossy"> → '+_spTr.redoGlossy+'</option><option value="short-pixel-bulk-lossless"> → '+_spTr.redoLossless+'</option><option value="short-pixel-bulk-restore"> → '+_spTr.restoreOriginal+"</option>")}ShortPixel.setOptions(ShortPixelConstants[0]);if(jQuery("#backup-folder-size").length){jQuery("#backup-folder-size").html(ShortPixel.getBackupSize())}if(ShortPixel.MEDIA_ALERT=="todo"&&jQuery("div.media-frame.mode-grid").length>0){jQuery("div.media-frame.mode-grid").before('<div id="short-pixel-media-alert" class="notice notice-warning"><p>'+_spTr.changeMLToListMode.format('<a href="upload.php?mode=list" class="view-list"><span class="screen-reader-text">'," </span>",'</a><a class="alignright" href="javascript:ShortPixel.dismissMediaAlert();">',"</a>")+"</p></div>")}jQuery(window).on("beforeunload",function(){if(ShortPixel.bulkProcessor==true){clearBulkProcessor()}});checkQuotaExceededAlert();checkBulkProgress()}function m(R){for(var S in R){ShortPixel[S]=R[S]}}function t(R){return/^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,63})+$/.test(R)}function n(){var R=jQuery("#pluginemail").val();if(ShortPixel.isEmailValid(R)){jQuery("#request_key").removeClass("disabled")}jQuery("#request_key").attr("href",jQuery("#request_key").attr("href").split("?")[0]+"?pluginemail="+R)}function a(){jQuery("#valid").val("validate");jQuery("#wp_shortpixel_options").submit()}jQuery("#key").keypress(function(R){if(R.which==13){jQuery("#valid").val("validate")}});function M(R){if(jQuery(R).is(":checked")){jQuery("#width,#height").removeAttr("disabled")}else{jQuery("#width,#height").attr("disabled","disabled")}}function e(R,T,V){for(var S=0,U=null;S<R.length;S++){R[S].onclick=function(){if(this!==U){U=this}if(typeof ShortPixel.setupGeneralTabAlert!=="undefined"){return}alert(_spTr.alertOnlyAppliesToNewImages);ShortPixel.setupGeneralTabAlert=1}}ShortPixel.enableResize("#resize");jQuery("#resize").change(function(){M(this)});jQuery(".resize-sizes").blur(function(X){var Y=jQuery(this);if(ShortPixel.resizeSizesAlert==Y.val()){return}ShortPixel.resizeSizesAlert=Y.val();var W=jQuery("#min-"+Y.attr("name")).val();if(Y.val()<Math.min(W,1024)){if(W>1024){alert(_spTr.pleaseDoNotSetLesser1024.format(Y.attr("name")))}else{alert(_spTr.pleaseDoNotSetLesserSize.format(Y.attr("name"),Y.attr("name"),W))}X.preventDefault();Y.focus()}else{this.defaultValue=Y.val()}});jQuery(".shortpixel-confirm").click(function(X){var W=confirm(X.target.getAttribute("data-confirm"));if(!W){X.preventDefault();return false}return true})}function D(){jQuery(".wp-shortpixel-options .shortpixel-key-valid").css("display","none");jQuery(".wp-shortpixel-options button#validate").css("display","inline-block")}function u(){jQuery("input.remove-folder-button").click(function(){var S=jQuery(this).data("value");var R=confirm(_spTr.areYouSureStopOptimizing.format(S));if(R==true){jQuery("#removeFolder").val(S);jQuery("#wp_shortpixel_options").submit()}});jQuery("input.recheck-folder-button").click(function(){var S=jQuery(this).data("value");var R=confirm(_spTr.areYouSureStopOptimizing.format(S));if(R==true){jQuery("#recheckFolder").val(S);jQuery("#wp_shortpixel_options").submit()}})}function L(R){var S=jQuery("#"+(R.checked?"total":"main")+"ToProcess").val();jQuery("div.bulk-play span.total").text(S);jQuery("#displayTotal").text(S)}function g(){ShortPixel.adjustSettingsTabs();jQuery(window).resize(function(){ShortPixel.adjustSettingsTabs()});if(window.location.hash){var R=("tab-"+window.location.hash.substring(window.location.hash.indexOf("#")+1)).replace(/\//,"");if(jQuery("section#"+R).length){ShortPixel.switchSettingsTab(R)}}jQuery("article.sp-tabs a.tab-link").click(function(){var S=jQuery(this).data("id");ShortPixel.switchSettingsTab(S)});jQuery("input[type=radio][name=deliverWebpType]").change(function(){if(this.value=="deliverWebpAltered"){if(window.confirm(_spTr.alertDeliverWebPAltered)){var S=jQuery("input[type=radio][name=deliverWebpAlteringType]:checked").length;if(S==0){jQuery("#deliverWebpAlteredWP").prop("checked",true)}}else{jQuery(this).prop("checked",false)}}else{if(this.value=="deliverWebpUnaltered"){window.alert(_spTr.alertDeliverWebPUnaltered)}}})}function y(V){var T=V.replace("tab-",""),R="",U=jQuery("section#"+V),S=location.href.replace(location.hash,"")+"#"+T;if(history.pushState){history.pushState(null,null,S)}else{location.hash=S}if(U.length>0){jQuery("section").removeClass("sel-tab");jQuery("section#"+V).addClass("sel-tab")}if(typeof HS.beacon.suggest!=="undefined"){switch(T){case"settings":R=shortpixel_suggestions_settings;break;case"adv-settings":R=shortpixel_suggestions_adv_settings;break;case"cloudflare":case"stats":R=shortpixel_suggestions_cloudflare;break;default:break}HS.beacon.suggest(R)}}function z(){var R=jQuery("section#tab-settings .wp-shortpixel-options").height()+90;R=Math.max(R,jQuery("section#tab-adv-settings .wp-shortpixel-options").height()+20);R=Math.max(R,jQuery("section#tab-resources .area1").height()+60);jQuery("#shortpixel-settings-tabs").css("height",R)}function N(){var R={action:"shortpixel_dismiss_media_alert"};jQuery.get(ShortPixel.AJAX_URL,R,function(S){R=JSON.parse(S);if(R.Status=="success"){jQuery("#short-pixel-media-alert").hide();console.log("dismissed")}})}function k(){var R={action:"shortpixel_check_quota"};jQuery.get(ShortPixel.AJAX_URL,R,function(){console.log("quota refreshed")})}function C(R){if(R.checked){jQuery("#with-thumbs").css("display","inherit");jQuery("#without-thumbs").css("display","none")}else{jQuery("#without-thumbs").css("display","inherit");jQuery("#with-thumbs").css("display","none")}}function b(V,T,S,U,R){return(T>0?"<div class='sp-column-info'>"+_spTr.reducedBy+" <strong><span class='percent'>"+T+"%</span></strong> ":"")+(T>0&&T<5?"<br>":"")+(T<5?_spTr.bonusProcessing:"")+(S.length>0?" ("+S+")":"")+(0+U>0?"<br>"+_spTr.plusXthumbsOpt.format(U):"")+(0+R>0?"<br>"+_spTr.plusXretinasOpt.format(R):"")+"</div>"}function p(S,R){jQuery(S).knob({readOnly:true,width:R,height:R,fgColor:"#1CAECB",format:function(T){return T+"%"}})}function c(Y,T,W,V,S,X){if(S==1){var U=jQuery(".sp-column-actions-template").clone();if(!U.length){return false}var R;if(T.length==0){R=["lossy","lossless"]}else{R=["lossy","glossy","lossless"].filter(function(Z){return !(Z==T)})}U.html(U.html().replace(/__SP_ID__/g,Y));if(X.substr(X.lastIndexOf(".")+1).toLowerCase()=="pdf"){jQuery(".sp-action-compare",U).remove()}if(W==0&&V>0){U.html(U.html().replace("__SP_THUMBS_TOTAL__",V))}else{jQuery(".sp-action-optimize-thumbs",U).remove();jQuery(".sp-dropbtn",U).removeClass("button-primary")}U.html(U.html().replace(/__SP_FIRST_TYPE__/g,R[0]));U.html(U.html().replace(/__SP_SECOND_TYPE__/g,R[1]));return U.html()}return""}function i(V,U){V=V.substring(2);if(jQuery(".shortpixel-other-media").length){var T=["optimize","retry","restore","redo","quota","view"];for(var S=0,R=T.length;S<R;S++){jQuery("#"+T[S]+"_"+V).css("display","none")}for(var S=0,R=U.length;S<R;S++){jQuery("#"+U[S]+"_"+V).css("display","")}}}function j(R){ShortPixel.retries++;if(isNaN(ShortPixel.retries)){ShortPixel.retries=1}if(ShortPixel.retries<6){console.log("Invalid response from server (Error: "+R+"). Retrying pass "+(ShortPixel.retries+1)+"...");setTimeout(checkBulkProgress,5000)}else{ShortPixel.bulkShowError(-1,"Invalid response from server received 6 times. Please retry later by reloading this page, or <a href='https://shortpixel.com/contact' target='_blank'>contact support</a>. (Error: "+R+")","");console.log("Invalid response from server 6 times. Giving up.")}}function l(R){R.action="shortpixel_browse_content";var S="";jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:R,success:function(T){S=T},async:false});return S}function d(){var R={action:"shortpixel_get_backup_size"};var S="";jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:R,success:function(T){S=T},async:false});return S}function f(S){if(!jQuery("#tos").is(":checked")){S.preventDefault();jQuery("#tos-robo").fadeIn(400,function(){jQuery("#tos-hand").fadeIn()});jQuery("#tos").click(function(){jQuery("#tos-robo").css("display","none");jQuery("#tos-hand").css("display","none")});return}jQuery("#request_key").addClass("disabled");jQuery("#pluginemail_spinner").addClass("is-active");ShortPixel.updateSignupEmail();if(ShortPixel.isEmailValid(jQuery("#pluginemail").val())){jQuery("#pluginemail-error").css("display","none");var R={action:"shortpixel_new_api_key",email:jQuery("#pluginemail").val()};jQuery.ajax({type:"POST",async:false,url:ShortPixel.AJAX_URL,data:R,success:function(T){data=JSON.parse(T);if(data.Status=="success"){S.preventDefault();window.location.reload()}else{if(data.Status=="invalid"){jQuery("#pluginemail-error").html("<b>"+data.Details+"</b>");jQuery("#pluginemail-error").css("display","");jQuery("#pluginemail-info").css("display","none");S.preventDefault()}else{}}}});jQuery("#request_key").removeAttr("onclick")}else{jQuery("#pluginemail-error").css("display","");jQuery("#pluginemail-info").css("display","none");S.preventDefault()}jQuery("#request_key").removeClass("disabled");jQuery("#pluginemail_spinner").removeClass("is-active")}function O(){jQuery("#shortPixelProposeUpgrade .sp-modal-body").addClass("sptw-modal-spinner");jQuery("#shortPixelProposeUpgrade .sp-modal-body").html("");jQuery("#shortPixelProposeUpgradeShade").css("display","block");jQuery("#shortPixelProposeUpgrade").removeClass("shortpixel-hide");var R={action:"shortpixel_propose_upgrade"};jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:R,success:function(S){jQuery("#shortPixelProposeUpgrade .sp-modal-body").removeClass("sptw-modal-spinner");jQuery("#shortPixelProposeUpgrade .sp-modal-body").html(S)}})}function H(){jQuery("#shortPixelProposeUpgradeShade").css("display","none");jQuery("#shortPixelProposeUpgrade").addClass("shortpixel-hide");if(ShortPixel.toRefresh){ShortPixel.recheckQuota()}}function v(){jQuery("#short-pixel-notice-unlisted").hide();jQuery("#optimizeUnlisted").prop("checked",true);var R={action:"shortpixel_dismiss_notice",notice_id:"unlisted",notice_data:"true"};jQuery.get(ShortPixel.AJAX_URL,R,function(S){R=JSON.parse(S);if(R.Status==ShortPixel.STATUS_SUCCESS){console.log("dismissed")}})}function o(){jQuery(".select-folder-button").click(function(){jQuery(".sp-folder-picker-shade").fadeIn(100);jQuery(".shortpixel-modal.modal-folder-picker").show();var R=jQuery(".sp-folder-picker");R.parent().css("margin-left",-R.width()/2);R.fileTree({script:ShortPixel.browseContent,multiFolder:false})});jQuery(".shortpixel-modal input.select-folder-cancel, .sp-folder-picker-shade").click(function(){jQuery(".sp-folder-picker-shade").fadeOut(100);jQuery(".shortpixel-modal.modal-folder-picker").hide()});jQuery(".shortpixel-modal input.select-folder").click(function(U){var T=jQuery("UL.jqueryFileTree LI.directory.selected");if(jQuery(T).length==0){var T=jQuery("UL.jqueryFileTree LI.selected").parents(".directory")}var R=jQuery(T).children("a").attr("rel");if(typeof R==="undefined"){return}R=R.trim();if(R){var S=jQuery("#customFolderBase").val()+R;if(S.slice(-1)=="/"){S=S.slice(0,-1)}jQuery("#addCustomFolder").val(S);jQuery("#addCustomFolderView").val(S);jQuery(".sp-folder-picker-shade").fadeOut(100);jQuery(".shortpixel-modal.modal-folder-picker").css("display","none");jQuery('input[name="saveAdv"]').removeClass("hidden")}else{alert("Please select a folder from the list.")}})}function G(V,U,T){var S=jQuery(".bulk-notice-msg.bulk-lengthy");if(S.length==0){return}var R=jQuery("a",S);R.text(U);if(T){R.attr("href",T)}else{R.attr("href",R.data("href").replace("__ID__",V))}S.css("display","block")}function B(){jQuery(".bulk-notice-msg.bulk-lengthy").css("display","none")}function w(R){var S=jQuery(".bulk-notice-msg.bulk-"+R);if(S.length==0){return}S.css("display","block")}function P(R){jQuery(".bulk-notice-msg.bulk-"+R).css("display","none")}function s(X,V,W,U){var R=jQuery("#bulk-error-template");if(R.length==0){return}var T=R.clone();T.attr("id","bulk-error-"+X);if(X==-1){jQuery("span.sp-err-title",T).remove();T.addClass("bulk-error-fatal")}else{jQuery("img",T).remove();jQuery("#bulk-error-".id).remove()}jQuery("span.sp-err-content",T).html(V);var S=jQuery("a.sp-post-link",T);if(U){S.attr("href",U)}else{S.attr("href",S.attr("href").replace("__ID__",X))}S.text(W);R.after(T);T.css("display","block")}function E(R,S){if(!confirm(_spTr["confirmBulk"+R])){S.stopPropagation();S.preventDefault();return false}return true}function x(U){var S=jQuery(U.target).val();var R=jQuery('input[name="random_answer"]').val();var T=jQuery('input[name="random_answer"]').data("target");if(S==R){jQuery(T).removeClass("disabled").prop("disabled",false);jQuery(T).removeAttr("aria-disabled")}else{jQuery(T).addClass("disabled").prop("disabled",true)}}function r(R){jQuery(R).parent().parent().remove()}function I(R){return R.substring(0,2)=="C-"}function K(){var R=window.location.href.split("#");window.location.href=R[0]+(R[0].indexOf("?")>0?"&":"?")+"checkquota=1"+(typeof R[1]==="undefined"?"":"#"+R[1])}function h(S){S.preventDefault();if(!this.menuCloseEvent){jQuery(window).click(function(T){if(!T.target.matches(".sp-dropbtn")){jQuery(".sp-dropdown.sp-show").removeClass("sp-show")}});this.menuCloseEvent=true}var R=S.target.parentElement.classList.contains("sp-show");jQuery(".sp-dropdown.sp-show").removeClass("sp-show");if(!R){S.target.parentElement.classList.add("sp-show")}}function Q(R){this.comparerData.origUrl=false;if(this.comparerData.cssLoaded===false){jQuery("<link>").appendTo("head").attr({type:"text/css",rel:"stylesheet",href:this.WP_PLUGIN_URL+"/res/css/twentytwenty.min.css"});this.comparerData.cssLoaded=2}if(this.comparerData.jsLoaded===false){jQuery.getScript(this.WP_PLUGIN_URL+"/res/js/jquery.twentytwenty.min.js",function(){ShortPixel.comparerData.jsLoaded=2;if(ShortPixel.comparerData.origUrl.length>0){ShortPixel.displayComparerPopup(ShortPixel.comparerData.width,ShortPixel.comparerData.height,ShortPixel.comparerData.origUrl,ShortPixel.comparerData.optUrl)}});this.comparerData.jsLoaded=1}if(this.comparerData.origUrl===false){jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:{action:"shortpixel_get_comparer_data",id:R},success:function(S){data=JSON.parse(S);jQuery.extend(ShortPixel.comparerData,data);if(ShortPixel.comparerData.jsLoaded==2){ShortPixel.displayComparerPopup(ShortPixel.comparerData.width,ShortPixel.comparerData.height,ShortPixel.comparerData.origUrl,ShortPixel.comparerData.optUrl)}}});this.comparerData.origUrl=""}}function F(T,Z,aa,U){var Y=T;var S=(Z<150||T<350);var X=jQuery(S?"#spUploadCompareSideBySide":"#spUploadCompare");var V=jQuery(".sp-modal-shade");if(!S){jQuery("#spCompareSlider").html('<img class="spUploadCompareOriginal"/><img class="spUploadCompareOptimized"/>')}T=Math.max(350,Math.min(800,(T<350?(T+25)*2:(Z<150?T+25:T))));Z=Math.max(150,(S?(Y>350?2*(Z+45):Z+45):Z*T/Y));var W="-"+Math.round(T/2);jQuery(".sp-modal-body",X).css("width",T);jQuery(".shortpixel-slider",X).css("width",T);X.css("width",T);X.css("marginLeft",W+"px");jQuery(".sp-modal-body",X).css("height",Z);X.show();V.show();if(!S){jQuery("#spCompareSlider").twentytwenty({slider_move:"mousemove"})}jQuery(".sp-close-button").on("click",ShortPixel.closeComparerPopup);jQuery(document).on("keyup.sp_modal_active",ShortPixel.closeComparerPopup);jQuery(".sp-modal-shade").on("click",ShortPixel.closeComparerPopup);var R=jQuery(".spUploadCompareOptimized",X);jQuery(".spUploadCompareOriginal",X).attr("src",aa);setTimeout(function(){jQuery(window).trigger("resize")},1000);R.load(function(){jQuery(window).trigger("resize")});R.attr("src",U)}function q(R){jQuery("#spUploadCompareSideBySide").hide();jQuery("#spUploadCompare").hide();jQuery(".sp-modal-shade").hide();jQuery(document).unbind("keyup.sp_modal_active");jQuery(".sp-modal-shade").off("click");jQuery(".sp-close-button").off("click")}function A(R){var S=document.createElement("a");S.href=R;if(R.indexOf(S.protocol+"//"+S.hostname)<0){return S.href}return R.replace(S.protocol+"//"+S.hostname,S.protocol+"//"+S.hostname.split(".").map(function(T){return sp_punycode.toASCII(T)}).join("."))}return{init:J,setOptions:m,isEmailValid:t,updateSignupEmail:n,validateKey:a,enableResize:M,setupGeneralTab:e,apiKeyChanged:D,setupAdvancedTab:u,checkThumbsUpdTotal:L,initSettings:g,switchSettingsTab:y,adjustSettingsTabs:z,onBulkThumbsCheck:C,dismissMediaAlert:N,checkQuota:k,percentDial:p,successMsg:b,successActions:c,otherMediaUpdateActions:i,retry:j,initFolderSelector:o,browseContent:l,getBackupSize:d,newApiKey:f,proposeUpgrade:O,closeProposeUpgrade:H,includeUnlisted:v,bulkShowLengthyMsg:G,bulkHideLengthyMsg:B,bulkShowMaintenanceMsg:w,bulkHideMaintenanceMsg:P,bulkShowError:s,confirmBulkAction:E,checkRandomAnswer:x,removeBulkMsg:r,isCustomImageId:I,recheckQuota:K,openImageMenu:h,menuCloseEvent:false,loadComparer:Q,displayComparerPopup:F,closeComparerPopup:q,convertPunycode:A,comparerData:{cssLoaded:false,jsLoaded:false,origUrl:false,optUrl:false,width:0,height:0},toRefresh:false,resizeSizesAlert:false}}();function showToolBarAlert(c,b,d){var a=jQuery("li.shortpixel-toolbar-processing");switch(c){case ShortPixel.STATUS_QUOTA_EXCEEDED:if(window.location.href.search("wp-short-pixel-bulk")>0&&jQuery(".sp-quota-exceeded-alert").length==0){location.reload();return}a.addClass("shortpixel-alert");a.addClass("shortpixel-quota-exceeded");jQuery("a",a).attr("href","options-general.php?page=wp-shortpixel");jQuery("a div",a).attr("title","ShortPixel quota exceeded. Click for details.");break;case ShortPixel.STATUS_SKIP:case ShortPixel.STATUS_FAIL:a.addClass("shortpixel-alert shortpixel-processing");jQuery("a div",a).attr("title",b);if(typeof d!=="undefined"){jQuery("a",a).attr("href","post.php?post="+d+"&action=edit")}break;case ShortPixel.STATUS_NO_KEY:a.addClass("shortpixel-alert");a.addClass("shortpixel-quota-exceeded");jQuery("a",a).attr("href","options-general.php?page=wp-shortpixel");jQuery("a div",a).attr("title","Get API Key");break;case ShortPixel.STATUS_SUCCESS:case ShortPixel.STATUS_RETRY:a.addClass("shortpixel-processing");a.removeClass("shortpixel-alert");jQuery("a",a).removeAttr("target");jQuery("a",a).attr("href",jQuery("a img",a).attr("success-url"))}a.removeClass("shortpixel-hide")}function hideToolBarAlert(){jQuery("li.shortpixel-toolbar-processing.shortpixel-processing").addClass("shortpixel-hide")}function hideQuotaExceededToolBarAlert(){jQuery("li.shortpixel-toolbar-processing.shortpixel-quota-exceeded").addClass("shortpixel-hide")}function checkQuotaExceededAlert(){if(typeof shortPixelQuotaExceeded!="undefined"){if(shortPixelQuotaExceeded==1){showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED)}else{hideQuotaExceededToolBarAlert()}}}function checkBulkProgress(){var b=function(e){if(!d){d=true;return e}return"/"};var d=false;var a=window.location.href.toLowerCase().replace(/\/\//g,b);d=false;var c=ShortPixel.WP_ADMIN_URL.toLowerCase().replace(/\/\//g,b);if(a.search(c)<0){a=ShortPixel.convertPunycode(a);c=ShortPixel.convertPunycode(c)}if(a.search(c+"upload.php")<0&&a.search(c+"edit.php")<0&&a.search(c+"edit-tags.php")<0&&a.search(c+"post-new.php")<0&&a.search(c+"post.php")<0&&a.search("page=nggallery-manage-gallery")<0&&(ShortPixel.FRONT_BOOTSTRAP==0||a.search(c)==0)){hideToolBarAlert();return}if(ShortPixel.bulkProcessor==true&&window.location.href.search("wp-short-pixel-bulk")<0&&typeof localStorage.bulkPage!=="undefined"&&localStorage.bulkPage>0){ShortPixel.bulkProcessor=false}if(window.location.href.search("wp-short-pixel-bulk")>=0){ShortPixel.bulkProcessor=true;localStorage.bulkTime=Math.floor(Date.now()/1000);localStorage.bulkPage=1}if(ShortPixel.bulkProcessor==true||typeof localStorage.bulkTime=="undefined"||Math.floor(Date.now()/1000)-localStorage.bulkTime>90){ShortPixel.bulkProcessor=true;localStorage.bulkPage=(window.location.href.search("wp-short-pixel-bulk")>=0?1:0);localStorage.bulkTime=Math.floor(Date.now()/1000);console.log(localStorage.bulkTime);checkBulkProcessingCallApi()}else{setTimeout(checkBulkProgress,5000)}}function checkBulkProcessingCallApi(){var a={action:"shortpixel_image_processing"};jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:a,success:function(g){if(g.length>0){var i=null;try{var i=JSON.parse(g)}catch(k){ShortPixel.retry(k.message);return}ShortPixel.retries=0;var d=i.ImageID;var j=(jQuery("div.short-pixel-bulk-page").length>0);switch(i.Status){case ShortPixel.STATUS_NO_KEY:setCellMessage(d,i.Message,"<a class='button button-smaller button-primary' href=\"https://shortpixel.com/wp-apikey"+ShortPixel.AFFILIATE+'" target="_blank">'+_spTr.getApiKey+"</a>");showToolBarAlert(ShortPixel.STATUS_NO_KEY);break;case ShortPixel.STATUS_QUOTA_EXCEEDED:setCellMessage(d,i.Message,"<a class='button button-smaller button-primary' href=\"https://shortpixel.com/login/"+ShortPixel.API_KEY+'" target="_blank">'+_spTr.extendQuota+"</a><a class='button button-smaller' href='admin.php?action=shortpixel_check_quota'>"+_spTr.check__Quota+"</a>");showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED);if(i.Stop==false){setTimeout(checkBulkProgress,5000)}ShortPixel.otherMediaUpdateActions(d,["quota","view"]);break;case ShortPixel.STATUS_FAIL:setCellMessage(d,i.Message,"<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('"+d+"', false)\">"+_spTr.retry+"</a>");showToolBarAlert(ShortPixel.STATUS_FAIL,i.Message,d);if(j){ShortPixel.bulkShowError(d,i.Message,i.Filename,i.CustomImageLink);if(i.BulkPercent){progressUpdate(i.BulkPercent,i.BulkMsg)}ShortPixel.otherMediaUpdateActions(d,["retry","view"])}console.log(i.Message);setTimeout(checkBulkProgress,5000);break;case ShortPixel.STATUS_EMPTY_QUEUE:console.log(i.Message);clearBulkProcessor();hideToolBarAlert();var c=jQuery("#bulk-progress");if(j&&c.length&&i.BulkStatus!="2"){progressUpdate(100,"Bulk finished!");jQuery("a.bulk-cancel").attr("disabled","disabled");hideSlider();setTimeout(function(){window.location.reload()},3000)}break;case ShortPixel.STATUS_SUCCESS:if(j){ShortPixel.bulkHideLengthyMsg();ShortPixel.bulkHideMaintenanceMsg()}var l=i.PercentImprovement;showToolBarAlert(ShortPixel.STATUS_SUCCESS,"");var b=ShortPixel.isCustomImageId(d)?"":ShortPixel.successActions(d,i.Type,i.ThumbsCount,i.ThumbsTotal,i.BackupEnabled,i.Filename);setCellMessage(d,ShortPixel.successMsg(d,l,i.Type,i.ThumbsCount,i.RetinasCount),b);if(jQuery("#post-"+d).length>0){jQuery("#post-"+d).find(".filename").text(i.Filename)}if(jQuery(".misc-pub-filename strong").length>0){jQuery(".misc-pub-filename strong").text(i.Filename)}if(ShortPixel.isCustomImageId(d)&&i.TsOptimized&&i.TsOptimized.length>0){console.log(d);jQuery(".date-"+d).text(i.TsOptimized)}var h=jQuery(["restore","view","redolossy","redoglossy","redolossless"]).not(["redo"+i.Type]).get();ShortPixel.otherMediaUpdateActions(d,h);var f=new PercentageAnimator("#sp-msg-"+d+" span.percent",l);f.animate(l);if(j&&typeof i.Thumb!=="undefined"){if(i.BulkPercent){progressUpdate(i.BulkPercent,i.BulkMsg)}if(i.Thumb.length>0){sliderUpdate(d,i.Thumb,i.BkThumb,i.PercentImprovement,i.Filename);if(typeof i.AverageCompression!=="undefined"&&0+i.AverageCompression>0){jQuery("#sp-avg-optimization").html('<input type="text" class="dial" value="'+Math.round(i.AverageCompression)+'"/>');ShortPixel.percentDial("#sp-avg-optimization .dial",60)}}}console.log("Server response: "+g);if(j&&typeof i.BulkPercent!=="undefined"){progressUpdate(i.BulkPercent,i.BulkMsg)}setTimeout(checkBulkProgress,5000);break;case ShortPixel.STATUS_SKIP:if(i.Silent!==1){ShortPixel.bulkShowError(d,i.Message,i.Filename,i.CustomImageLink)}case ShortPixel.STATUS_ERROR:if(typeof i.Message!=="undefined"){showToolBarAlert(ShortPixel.STATUS_SKIP,i.Message+" Image ID: "+d);setCellMessage(d,i.Message,"")}ShortPixel.otherMediaUpdateActions(d,["retry","view"]);case ShortPixel.STATUS_RETRY:console.log("Server response: "+g);showToolBarAlert(ShortPixel.STATUS_RETRY,"");if(j&&typeof i.BulkPercent!=="undefined"){progressUpdate(i.BulkPercent,i.BulkMsg)}if(j&&i.Count>3){ShortPixel.bulkShowLengthyMsg(d,i.Filename,i.CustomImageLink)}setTimeout(checkBulkProgress,5000);break;case ShortPixel.STATUS_MAINTENANCE:ShortPixel.bulkShowMaintenanceMsg("maintenance");setTimeout(checkBulkProgress,60000);break;case ShortPixel.STATUS_QUEUE_FULL:ShortPixel.bulkShowMaintenanceMsg("queue-full");setTimeout(checkBulkProgress,60000);break;default:ShortPixel.retry("Unknown status "+i.Status+". Retrying...");break}}},error:function(b){ShortPixel.retry(b.statusText)}})}function clearBulkProcessor(){ShortPixel.bulkProcessor=false;localStorage.bulkTime=0;if(window.location.href.search("wp-short-pixel-bulk")>=0){localStorage.bulkPage=0}}function setCellMessage(d,a,c){var b=jQuery("#sp-msg-"+d);if(b.length>0){b.html("<div class='sp-column-actions'>"+c+"</div><div class='sp-column-info'>"+a+"</div>");b.css("color","")}b=jQuery("#sp-cust-msg-"+d);if(b.length>0){b.html("<div class='sp-column-info'>"+a+"</div>")}}function manualOptimization(c,a){setCellMessage(c,"<img src='"+ShortPixel.WP_PLUGIN_URL+"/res/img/loading.gif' class='sp-loading-small'>Image waiting to be processed","");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-hide");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-alert");jQuery("li.shortpixel-toolbar-processing").addClass("shortpixel-processing");var b={action:"shortpixel_manual_optimization",image_id:c,cleanup:a};jQuery.ajax({type:"GET",url:ShortPixel.AJAX_URL,data:b,success:function(d){var e=JSON.parse(d);if(e.Status==ShortPixel.STATUS_SUCCESS){setTimeout(checkBulkProgress,2000)}else{setCellMessage(c,typeof e.Message!=="undefined"?e.Message:_spTr.thisContentNotProcessable,"")}},error:function(d){b.action="shortpixel_check_status";jQuery.ajax({type:"GET",url:ShortPixel.AJAX_URL,data:b,success:function(e){var f=JSON.parse(e);if(f.Status!==ShortPixel.STATUS_SUCCESS){setCellMessage(c,typeof f.Message!=="undefined"?f.Message:_spTr.thisContentNotProcessable,"")}}})}})}function reoptimize(c,a){setCellMessage(c,"<img src='"+ShortPixel.WP_PLUGIN_URL+"/res/img/loading.gif' class='sp-loading-small'>Image waiting to be reprocessed","");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-hide");jQuery("li.shortpixel-toolbar-processing").addClass("shortpixel-processing");var b={action:"shortpixel_redo",attachment_ID:c,type:a};jQuery.get(ShortPixel.AJAX_URL,b,function(d){b=JSON.parse(d);if(b.Status==ShortPixel.STATUS_SUCCESS){setTimeout(checkBulkProgress,2000)}else{$msg=typeof b.Message!=="undefined"?b.Message:_spTr.thisContentNotProcessable;setCellMessage(c,$msg,"");showToolBarAlert(ShortPixel.STATUS_FAIL,$msg)}})}function optimizeThumbs(b){setCellMessage(b,"<img src='"+ShortPixel.WP_PLUGIN_URL+"/res/img/loading.gif' class='sp-loading-small'>"+_spTr.imageWaitOptThumbs,"");jQuery("li.shortpixel-toolbar-processing").removeClass("shortpixel-hide");jQuery("li.shortpixel-toolbar-processing").addClass("shortpixel-processing");var a={action:"shortpixel_optimize_thumbs",attachment_ID:b};jQuery.get(ShortPixel.AJAX_URL,a,function(c){a=JSON.parse(c);if(a.Status==ShortPixel.STATUS_SUCCESS){setTimeout(checkBulkProgress,2000)}else{setCellMessage(b,typeof a.Message!=="undefined"?a.Message:_spTr.thisContentNotProcessable,"")}})}function dismissShortPixelNoticeExceed(b){jQuery("#wp-admin-bar-shortpixel_processing").hide();var a={action:"shortpixel_dismiss_notice",notice_id:"exceed"};jQuery.get(ShortPixel.AJAX_URL,a,function(c){a=JSON.parse(c);if(a.Status==ShortPixel.STATUS_SUCCESS){console.log("dismissed")}});b.preventDefault()}function dismissShortPixelNotice(b){jQuery("#short-pixel-notice-"+b).hide();var a={action:"shortpixel_dismiss_notice",notice_id:b};jQuery.get(ShortPixel.AJAX_URL,a,function(c){a=JSON.parse(c);if(a.Status==ShortPixel.STATUS_SUCCESS){console.log("dismissed")}})}function PercentageAnimator(b,a){this.animationSpeed=10;this.increment=2;this.curPercentage=0;this.targetPercentage=a;this.outputSelector=b;this.animate=function(c){this.targetPercentage=c;setTimeout(PercentageTimer.bind(null,this),this.animationSpeed)}}function PercentageTimer(a){if(a.curPercentage-a.targetPercentage<-a.increment){a.curPercentage+=a.increment}else{if(a.curPercentage-a.targetPercentage>a.increment){a.curPercentage-=a.increment}else{a.curPercentage=a.targetPercentage}}jQuery(a.outputSelector).text(a.curPercentage+"%");if(a.curPercentage!=a.targetPercentage){setTimeout(PercentageTimer.bind(null,a),a.animationSpeed)}}function progressUpdate(c,b){var a=jQuery("#bulk-progress");if(a.length){jQuery(".progress-left",a).css("width",c+"%");jQuery(".progress-img",a).css("left",c+"%");if(c>24){jQuery(".progress-img span",a).html("");jQuery(".progress-left",a).html(c+"%")}else{jQuery(".progress-img span",a).html(c+"%");jQuery(".progress-left",a).html("")}jQuery(".bulk-estimate").html(b)}}function sliderUpdate(g,c,d,e,b){var f=jQuery(".bulk-slider div.bulk-slide:first-child");if(f.length===0){return}if(f.attr("id")!="empty-slide"){f.hide()}f.css("z-index",1000);jQuery(".bulk-img-opt",f).attr("src","");if(typeof d==="undefined"){d=""}if(d.length>0){jQuery(".bulk-img-orig",f).attr("src","")}var a=f.clone();a.attr("id","slide-"+g);jQuery(".bulk-img-opt",a).attr("src",c);if(d.length>0){jQuery(".img-original",a).css("display","inline-block");jQuery(".bulk-img-orig",a).attr("src",d)}else{jQuery(".img-original",a).css("display","none")}jQuery(".bulk-opt-percent",a).html('<input type="text" class="dial" value="'+e+'"/>');jQuery(".bulk-slider").append(a);ShortPixel.percentDial("#"+a.attr("id")+" .dial",100);jQuery(".bulk-slider-container span.filename").html("&nbsp;&nbsp;"+b);if(f.attr("id")=="empty-slide"){f.remove();jQuery(".bulk-slider-container").css("display","block")}else{f.animate({left:f.width()+f.position().left},"slow","swing",function(){f.remove();a.fadeIn("slow")})}}function hideSlider(){jQuery(".bulk-slider-container").css("display","none")}function showStats(){var a=jQuery(".bulk-stats");if(a.length>0){}}if(!(typeof String.prototype.format=="function")){String.prototype.format=function(){var b=this,a=arguments.length;while(a--){b=b.replace(new RegExp("\\{"+a+"\\}","gm"),arguments[a])}return b}};
res/scss/shortpixel-admin.scss ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+
2
+ @import 'view/bulk-restore-all';
3
+ @import 'view/settings-advanced';
res/scss/view/_bulk-restore-all.scss ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .short-pixel-bulk-page.bulk-restore-all{
3
+
4
+ ol {
5
+ li {
6
+ font-weight: 700;
7
+ }
8
+ }
9
+
10
+ section.select_folders
11
+ {
12
+ // border-bottom: 1px solid #ccc;
13
+ margin: 20px 0;
14
+ .input
15
+ {
16
+ margin: 10px 0 10px 15px;
17
+ font-size: 16px;
18
+ display: block;
19
+ clear: both;
20
+ }
21
+
22
+ .filecount
23
+ {
24
+ font-size: 12px;
25
+ }
26
+ }
27
+
28
+ section.random_check
29
+ {
30
+ .random_answer
31
+ {
32
+ font-size:16px;
33
+ font-weight: 700;
34
+ padding: 8px;
35
+ border: 1px solid #ccc;
36
+ display: inline-block;
37
+
38
+ }
39
+
40
+ .inputs
41
+ {
42
+ margin: 15px 0;
43
+ span { margin-right: 8px; }
44
+ }
45
+ }
46
+
47
+
48
+
49
+ .button
50
+ {
51
+ margin: 10px 0;
52
+ margin-right: 8px;
53
+ }
54
+ }
res/scss/view/_settings-advanced.scss ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Specific styles for advanced settings tab */
2
+
3
+ #shortpixel-settings-tabs #tab-adv-settings
4
+ {
5
+
6
+ .addCustomFolder
7
+ {
8
+ margin: 10px 0;
9
+ .add-folder-text
10
+ {
11
+ margin-left: 5px;
12
+ }
13
+ input[type="text"]
14
+ {
15
+ width: 50em;
16
+ max-width: 70%;
17
+ }
18
+ input[name="saveAdv"]
19
+ {
20
+ margin-left: 8px;
21
+ }
22
+ }
23
+
24
+
25
+
26
+ }
shortpixel_api.php CHANGED
@@ -1,874 +1,884 @@
1
- <?php
2
- if ( !function_exists( 'download_url' ) ) {
3
- require_once( ABSPATH . 'wp-admin/includes/file.php' );
4
- }
5
-
6
- class ShortPixelAPI {
7
-
8
- const STATUS_SUCCESS = 1;
9
- const STATUS_UNCHANGED = 0;
10
- const STATUS_ERROR = -1;
11
- const STATUS_FAIL = -2;
12
- const STATUS_QUOTA_EXCEEDED = -3;
13
- const STATUS_SKIP = -4;
14
- const STATUS_NOT_FOUND = -5;
15
- const STATUS_NO_KEY = -6;
16
- const STATUS_RETRY = -7;
17
- const STATUS_QUEUE_FULL = -404;
18
- const STATUS_MAINTENANCE = -500;
19
-
20
- const ERR_FILE_NOT_FOUND = -2;
21
- const ERR_TIMEOUT = -3;
22
- const ERR_SAVE = -4;
23
- const ERR_SAVE_BKP = -5;
24
- const ERR_INCORRECT_FILE_SIZE = -6;
25
- const ERR_DOWNLOAD = -7;
26
- const ERR_PNG2JPG_MEMORY = -8;
27
- const ERR_POSTMETA_CORRUPT = -9;
28
- const ERR_UNKNOWN = -999;
29
-
30
- private $_settings;
31
- private $_apiEndPoint;
32
- private $_apiDumpEndPoint;
33
-
34
-
35
- public function __construct($settings) {
36
- $this->_settings = $settings;
37
- $this->_apiEndPoint = $this->_settings->httpProto . '://' . SHORTPIXEL_API . '/v2/reducer.php';
38
- $this->_apiDumpEndPoint = $this->_settings->httpProto . '://' . SHORTPIXEL_API . '/v2/cleanup.php';
39
- }
40
-
41
- protected function prepareRequest($requestParameters, $Blocking = false) {
42
- $arguments = array(
43
- 'method' => 'POST',
44
- 'timeout' => 15,
45
- 'redirection' => 3,
46
- 'sslverify' => false,
47
- 'httpversion' => '1.0',
48
- 'blocking' => $Blocking,
49
- 'headers' => array(),
50
- 'body' => json_encode($requestParameters),
51
- 'cookies' => array()
52
- );
53
- //die(var_dump($requestParameters));
54
- //add this explicitely only for https, otherwise (for http) it slows down the request
55
- if($this->_settings->httpProto !== 'https') {
56
- unset($arguments['sslverify']);
57
- }
58
-
59
- return $arguments;
60
- }
61
-
62
- public function doDumpRequests($URLs) {
63
- if(!count($URLs)) {
64
- return false;
65
- }
66
- return wp_remote_post($this->_apiDumpEndPoint, $this->prepareRequest(array(
67
- 'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
68
- 'key' => $this->_settings->apiKey,
69
- 'urllist' => $URLs
70
- ) ) );
71
- }
72
-
73
- /**
74
- * sends a compression request to the API
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
- $added = array();
89
- $files = " (";
90
- foreach ($meta->getThumbsMissing() as $miss) {
91
- if(isset($added[$miss])) continue;
92
- $files .= $miss . ", ";
93
- $added[$miss] = true;
94
- }
95
- if(strrpos($files, ', ')) {
96
- $files = substr_replace($files , ')', strrpos($files , ', '));
97
- }
98
- throw new Exception(__('Image files are missing.', 'shortpixel-image-optimiser') . (strlen($files) > 1 ? $files : ''));
99
- }
100
- else throw new Exception(__('Image files are missing.', 'shortpixel-image-optimiser'));
101
- }
102
-
103
- //WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
104
-
105
- $requestParameters = array(
106
- 'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
107
- 'key' => $this->_settings->apiKey,
108
- 'lossy' => $compressionType === false ? $this->_settings->compressionType : $compressionType,
109
- 'cmyk2rgb' => $this->_settings->CMYKtoRGBconversion,
110
- 'keep_exif' => ($this->_settings->keepExif ? "1" : "0"),
111
- 'convertto' => ($this->_settings->createWebp ? urlencode("+webp") : ""),
112
- 'resize' => $this->_settings->resizeImages ? 1 + 2 * ($this->_settings->resizeType == 'inner' ? 1 : 0) : 0,
113
- 'resize_width' => $this->_settings->resizeWidth,
114
- 'resize_height' => $this->_settings->resizeHeight,
115
- 'urllist' => $URLs
116
- );
117
- if(/*false &&*/ $this->_settings->downloadArchive == 7 && class_exists('PharData')) {
118
- $requestParameters['group'] = $itemHandler->getId();
119
- }
120
- if($refresh) {
121
- $requestParameters['refresh'] = 1;
122
- }
123
-
124
- //WpShortPixel::log("DO REQUESTS SENDING: " . json_encode($requestParameters));
125
-
126
- $response = wp_remote_post($this->_apiEndPoint, $this->prepareRequest($requestParameters, $Blocking) );
127
-
128
- //WpShortPixel::log('RESPONSE: ' . json_encode($response));
129
-
130
- //only if $Blocking is true analyze the response
131
- if ( $Blocking )
132
- {
133
- //WpShortPixel::log("API response : " . json_encode($response));
134
-
135
- //die(var_dump(array('URL: ' => $this->_apiEndPoint, '<br><br>REQUEST:' => $requestParameters, '<br><br>RESPONSE: ' => $response, '<br><br>BODY: ' => isset($response['body']) ? $response['body'] : '' )));
136
- //there was an error, save this error inside file's SP optimization field
137
- if ( is_object($response) && get_class($response) == 'WP_Error' )
138
- {
139
- $errorMessage = $response->errors['http_request_failed'][0];
140
- $errorCode = 503;
141
- }
142
- elseif ( isset($response['response']['code']) && $response['response']['code'] <> 200 )
143
- {
144
- $errorMessage = $response['response']['code'] . " - " . $response['response']['message'];
145
- $errorCode = $response['response']['code'];
146
- }
147
-
148
- if ( isset($errorMessage) )
149
- {//set details inside file so user can know what happened
150
- $itemHandler->incrementRetries(1, $errorCode, $errorMessage);
151
- return array("response" => array("code" => $errorCode, "message" => $errorMessage ));
152
- }
153
-
154
- return $response;//this can be an error or a good response
155
- }
156
-
157
- return $response;
158
- }
159
-
160
- /**
161
- * parse the JSON response
162
- * @param $response
163
- * @return array parsed
164
- */
165
- public function parseResponse($response) {
166
- $data = $response['body'];
167
- $data = json_decode($data);
168
- return (array)$data;
169
- }
170
-
171
- /**
172
- * handles the processing of the image using the ShortPixel API
173
- * @param array $URLs - list of urls to send to API
174
- * @param array $PATHs - list of local paths for the images
175
- * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
176
- * @return array status/message array
177
- */
178
- public function processImage($URLs, $PATHs, $itemHandler = null) {
179
- return $this->processImageRecursive($URLs, $PATHs, $itemHandler, 0);
180
- }
181
-
182
- /**
183
- * handles the processing of the image using the ShortPixel API - cals itself recursively until success
184
- * @param array $URLs - list of urls to send to API
185
- * @param array $PATHs - list of local paths for the images
186
- * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
187
- * @param int $startTime - time of the first call
188
- * @return array status/message array
189
- */
190
- private function processImageRecursive($URLs, $PATHs, $itemHandler = null, $startTime = 0)
191
- {
192
- //WPShortPixel::log("processImageRecursive ID: " . $itemHandler->getId() . " PATHs: " . json_encode($PATHs));
193
-
194
- $PATHs = self::CheckAndFixImagePaths($PATHs);//check for images to make sure they exist on disk
195
- if ( $PATHs === false || isset($PATHs['error'])) {
196
- $missingFiles = '';
197
- if(isset($PATHs['error'])) {
198
- foreach($PATHs['error'] as $errPath) {
199
- $missingFiles .= (strlen($missingFiles) ? ', ':'') . basename(stripslashes($errPath));
200
- }
201
- }
202
- $msg = __('The file(s) do not exist on disk: ','shortpixel-image-optimiser') . $missingFiles;
203
- $itemHandler->setError(self::ERR_FILE_NOT_FOUND, $msg );
204
- return array("Status" => self::STATUS_SKIP, "Message" => $msg, "Silent" => $itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE ? 1 : 0);
205
- }
206
-
207
- //tries multiple times (till timeout almost reached) to fetch images.
208
- if($startTime == 0) {
209
- $startTime = time();
210
- }
211
- $apiRetries = $this->_settings->apiRetries;
212
-
213
- if( time() - $startTime > SHORTPIXEL_MAX_EXECUTION_TIME2)
214
- {//keeps track of time
215
- if ( $apiRetries > SHORTPIXEL_MAX_API_RETRIES )//we tried to process this time too many times, giving up...
216
- {
217
- $itemHandler->incrementRetries(1, self::ERR_TIMEOUT, __('Timed out while processing.','shortpixel-image-optimiser'));
218
- $this->_settings->apiRetries = 0; //fai added to solve a bug?
219
- return array("Status" => self::STATUS_SKIP,
220
- "Message" => ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE ? __('Image ID','shortpixel-image-optimiser') : __('Media ID','shortpixel-image-optimiser'))
221
- . ": " . $itemHandler->getId() .' ' . __('Skip this image, try the next one.','shortpixel-image-optimiser'));
222
- }
223
- else
224
- {//we'll try again next time user visits a page on admin panel
225
- $apiRetries++;
226
- $this->_settings->apiRetries = $apiRetries;
227
- return array("Status" => self::STATUS_RETRY, "Message" => __('Timed out while processing.','shortpixel-image-optimiser') . ' (pass '.$apiRetries.')',
228
- "Count" => $apiRetries);
229
- }
230
- }
231
-
232
- //#$compressionType = isset($meta['ShortPixel']['type']) ? ($meta['ShortPixel']['type'] == 'lossy' ? 1 : 0) : $this->_settings->compressionType;
233
- $meta = $itemHandler->getMeta();
234
- $compressionType = $meta->getCompressionType() !== null ? $meta->getCompressionType() : $this->_settings->compressionType;
235
- $response = $this->doRequests($URLs, true, $itemHandler, $compressionType);//send requests to API
236
-
237
- //die($response['body']);
238
-
239
- if($response['response']['code'] != 200) {//response <> 200 -> there was an error apparently?
240
- return array("Status" => self::STATUS_FAIL, "Message" => __('There was an error and your request was not processed.', 'shortpixel-image-optimiser')
241
- . (isset($response['response']['message']) ? ' (' . $response['response']['message'] . ')' : ''), "Code" => $response['response']['code']);
242
- }
243
-
244
- $APIresponse = $this->parseResponse($response);//get the actual response from API, its an array
245
-
246
- if ( isset($APIresponse[0]) ) //API returned image details
247
- {
248
- foreach ( $APIresponse as $imageObject ) {//this part makes sure that all the sizes were processed and ready to be downloaded
249
- if ( isset($imageObject->Status) && ( $imageObject->Status->Code == 0 || $imageObject->Status->Code == 1 ) ) {
250
- sleep(1);
251
- return $this->processImageRecursive($URLs, $PATHs, $itemHandler, $startTime);
252
- }
253
- }
254
-
255
- $firstImage = $APIresponse[0];//extract as object first image
256
- switch($firstImage->Status->Code)
257
- {
258
- case 2:
259
- //handle image has been processed
260
- if(!isset($firstImage->Status->QuotaExceeded)) {
261
- $this->_settings->quotaExceeded = 0;//reset the quota exceeded flag
262
- }
263
- return $this->handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType);
264
- default:
265
- //handle error
266
- $incR = 1;
267
- if ( !file_exists($PATHs[0]) ) {
268
- $err = array("Status" => self::STATUS_NOT_FOUND, "Message" => "File not found on disk. "
269
- . ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE ? "Image" : "Media")
270
- . " ID: " . $itemHandler->getId(), "Code" => self::ERR_FILE_NOT_FOUND);
271
- $incR = 3;
272
- }
273
- elseif ( isset($APIresponse[0]->Status->Message) ) {
274
- //return array("Status" => self::STATUS_FAIL, "Message" => "There was an error and your request was not processed (" . $APIresponse[0]->Status->Message . "). REQ: " . json_encode($URLs));
275
- $err = array("Status" => self::STATUS_FAIL, "Code" => (isset($APIresponse[0]->Status->Code) ? $APIresponse[0]->Status->Code : self::ERR_UNKNOWN),
276
- "Message" => __('There was an error and your request was not processed.','shortpixel-image-optimiser')
277
- . " (" . wp_basename($APIresponse[0]->OriginalURL) . ": " . $APIresponse[0]->Status->Message . ")");
278
- } else {
279
- $err = array("Status" => self::STATUS_FAIL, "Message" => __('There was an error and your request was not processed.','shortpixel-image-optimiser'),
280
- "Code" => (isset($APIresponse[0]->Status->Code) ? $APIresponse[0]->Status->Code : self::ERR_UNKNOWN));
281
- }
282
-
283
- $itemHandler->incrementRetries($incR, $err["Code"], $err["Message"]);
284
- $meta = $itemHandler->getMeta();
285
- if($meta->getRetries() >= SHORTPIXEL_MAX_FAIL_RETRIES) {
286
- $meta->setStatus($APIresponse[0]->Status->Code);
287
- $meta->setMessage($APIresponse[0]->Status->Message);
288
- $itemHandler->updateMeta($meta);
289
- }
290
- return $err;
291
- }
292
- }
293
-
294
- if(!isset($APIresponse['Status'])) {
295
- WpShortPixel::log("API Response Status unfound : " . json_encode($APIresponse));
296
- return array("Status" => self::STATUS_FAIL, "Message" => __('Unrecognized API response. Please contact support.','shortpixel-image-optimiser'),
297
- "Code" => self::ERR_UNKNOWN, "Debug" => ' (SERVER RESPONSE: ' . json_encode($response) . ')');
298
- } else {
299
- switch($APIresponse['Status']->Code)
300
- {
301
- case -403:
302
- @delete_option('bulkProcessingStatus');
303
- $this->_settings->quotaExceeded = 1;
304
- return array("Status" => self::STATUS_QUOTA_EXCEEDED, "Message" => __('Quota exceeded.','shortpixel-image-optimiser'));
305
- break;
306
- case -404:
307
- return array("Status" => self::STATUS_QUEUE_FULL, "Message" => $APIresponse['Status']->Message);
308
- case -500:
309
- return array("Status" => self::STATUS_MAINTENANCE, "Message" => $APIresponse['Status']->Message);
310
- }
311
-
312
- //sometimes the response array can be different
313
- if (is_numeric($APIresponse['Status']->Code)) {
314
- return array("Status" => self::STATUS_FAIL, "Message" => $APIresponse['Status']->Message);
315
- } else {
316
- return array("Status" => self::STATUS_FAIL, "Message" => $APIresponse[0]->Status->Message);
317
- }
318
- }
319
- }
320
-
321
- /**
322
- * sets the preferred protocol of URL using the globally set preferred protocol.
323
- * If global protocol not set, sets it by testing the download of a http test image from ShortPixel site.
324
- * If http works then it's http, otherwise sets https
325
- * @param string $url
326
- * @param bool $reset - forces recheck even if preferred protocol is already set
327
- * @return string url with the preferred protocol
328
- */
329
- public function setPreferredProtocol($url, $reset = false) {
330
- //switch protocol based on the formerly detected working protocol
331
- if($this->_settings->downloadProto == '' || $reset) {
332
- //make a test to see if the http is working
333
- $testURL = 'http://' . SHORTPIXEL_API . '/img/connection-test-image.png';
334
- $result = download_url($testURL, 10);
335
- $this->_settings->downloadProto = is_wp_error( $result ) ? 'https' : 'http';
336
- }
337
- return $this->_settings->downloadProto == 'http' ?
338
- str_replace('https://', 'http://', $url) :
339
- str_replace('http://', 'https://', $url);
340
-
341
-
342
- }
343
-
344
- function fromArchive($path, $optimizedUrl, $optimizedSize, $originalSize, $webpUrl) {
345
- $webpTempFile = "NA";
346
- if($webpUrl !== "NA") {
347
- $webpTempFile = $path . '/' . wp_basename($webpUrl);
348
- $webpTempFile = file_exists($webpTempFile) ? $webpTempFile : 'NA';
349
- }
350
-
351
- //if there is no improvement in size then we do not download this file
352
- if ( $originalSize == $optimizedSize )
353
- return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
354
-
355
- $correctFileSize = $optimizedSize;
356
- $tempFile = $path . '/' . wp_basename($optimizedUrl);
357
-
358
- if(file_exists($tempFile)) {
359
- //on success we return this
360
- if( filesize($tempFile) != $correctFileSize) {
361
- $size = filesize($tempFile);
362
- @unlink($tempFile);
363
- @unlink($webpTempFile);
364
- $returnMessage = array(
365
- "Status" => self::STATUS_ERROR,
366
- "Code" => self::ERR_INCORRECT_FILE_SIZE,
367
- "Message" => sprintf(__('Error in archive - incorrect file size (downloaded: %s, correct: %s )','shortpixel-image-optimiser'),$size, $correctFileSize));
368
- } else {
369
- $returnMessage = array("Status" => self::STATUS_SUCCESS, "Message" => $tempFile, "WebP" => $webpTempFile);
370
- }
371
- } else {
372
- $returnMessage = array("Status" => self::STATUS_ERROR,
373
- "Code" => self::ERR_FILE_NOT_FOUND,
374
- "Message" => __('Unable to locate downloaded file','shortpixel-image-optimiser') . " " . $tempFile);
375
- }
376
-
377
- return $returnMessage;
378
- }
379
-
380
- /**
381
- * handles the download of an optimized image from ShortPixel API
382
- * @param string $optimizedUrl
383
- * @param int $optimizedSize
384
- * @param int $originalSize
385
- * @param string $webpUrl
386
- * @return array status /message array
387
- */
388
- private function handleDownload($optimizedUrl, $optimizedSize, $originalSize, $webpUrl){
389
-
390
- $downloadTimeout = max(ini_get('max_execution_time') - 10, 15);
391
-
392
- $webpTempFile = "NA";
393
- if($webpUrl !== "NA") {
394
- $webpURL = $this->setPreferredProtocol(urldecode($webpUrl));
395
- $webpTempFile = download_url($webpURL, $downloadTimeout);
396
- $webpTempFile = is_wp_error( $webpTempFile ) ? "NA" : $webpTempFile;
397
- }
398
-
399
- //if there is no improvement in size then we do not download this file
400
- if ( $originalSize == $optimizedSize )
401
- return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
402
-
403
- $correctFileSize = $optimizedSize;
404
- $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl));
405
-
406
- $tempFile = download_url($fileURL, $downloadTimeout);
407
- WPShortPixel::log('Downloading file: '.json_encode($tempFile));
408
- if(is_wp_error( $tempFile ))
409
- { //try to switch the default protocol
410
- $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl), true); //force recheck of the protocol
411
- $tempFile = download_url($fileURL, $downloadTimeout);
412
- }
413
-
414
- //on success we return this
415
- $returnMessage = array("Status" => self::STATUS_SUCCESS, "Message" => $tempFile, "WebP" => $webpTempFile);
416
-
417
- if ( is_wp_error( $tempFile ) ) {
418
- @unlink($tempFile);
419
- @unlink($webpTempFile);
420
- $returnMessage = array(
421
- "Status" => self::STATUS_ERROR,
422
- "Code" => self::ERR_DOWNLOAD,
423
- "Message" => __('Error downloading file','shortpixel-image-optimiser') . " ({$optimizedUrl}) " . $tempFile->get_error_message());
424
- }
425
- //check response so that download is OK
426
- elseif (!file_exists($tempFile)) {
427
- $returnMessage = array("Status" => self::STATUS_ERROR,
428
- "Code" => self::ERR_FILE_NOT_FOUND,
429
- "Message" => __('Unable to locate downloaded file','shortpixel-image-optimiser') . " " . $tempFile);
430
- }
431
- elseif( filesize($tempFile) != $correctFileSize) {
432
- $size = filesize($tempFile);
433
- @unlink($tempFile);
434
- @unlink($webpTempFile);
435
- $returnMessage = array(
436
- "Status" => self::STATUS_ERROR,
437
- "Code" => self::ERR_INCORRECT_FILE_SIZE,
438
- "Message" => sprintf(__('Error downloading file - incorrect file size (downloaded: %s, correct: %s )','shortpixel-image-optimiser'),$size, $correctFileSize));
439
- }
440
- return $returnMessage;
441
- }
442
-
443
- public static function backupImage($mainPath, $PATHs) {
444
- //$fullSubDir = str_replace(wp_normalize_path(get_home_path()), "", wp_normalize_path(dirname($itemHandler->getMeta()->getPath()))) . '/';
445
- //$SubDir = ShortPixelMetaFacade::returnSubDir($itemHandler->getMeta()->getPath(), $itemHandler->getType());
446
- $fullSubDir = ShortPixelMetaFacade::returnSubDir($mainPath);
447
- $source = $PATHs; //array with final paths for these files
448
-
449
- if( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && !@mkdir(SHORTPIXEL_BACKUP_FOLDER, 0777, true) ) {//creates backup folder if it doesn't exist
450
- return array("Status" => self::STATUS_FAIL, "Message" => __('Backup folder does not exist and it cannot be created','shortpixel-image-optimiser'));
451
- }
452
- //create subdir in backup folder if needed
453
- @mkdir( SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir, 0777, true);
454
-
455
- foreach ( $source as $fileID => $filePATH )//create destination files array
456
- {
457
- $destination[$fileID] = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . self::MB_basename($source[$fileID]);
458
- }
459
- //die("IZ BACKUP: " . SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . var_dump($destination));
460
-
461
- //now that we have original files and where we should back them up we attempt to do just that
462
- if(is_writable(SHORTPIXEL_BACKUP_FOLDER))
463
- {
464
- foreach ( $destination as $fileID => $filePATH )
465
- {
466
- if ( !file_exists($filePATH) )
467
- {
468
- if ( !@copy($source[$fileID], $filePATH) )
469
- {//file couldn't be saved in backup folder
470
- $msg = sprintf(__('Cannot save file <i>%s</i> in backup directory','shortpixel-image-optimiser'),self::MB_basename($source[$fileID]));
471
- return array("Status" => self::STATUS_FAIL, "Message" => $msg);
472
- }
473
- }
474
- }
475
- return array("Status" => self::STATUS_SUCCESS);
476
- }
477
- else {//cannot write to the backup dir, return with an error
478
- $msg = __('Cannot save file in backup directory','shortpixel-image-optimiser');
479
- return array("Status" => self::STATUS_FAIL, "Message" => $msg);
480
- }
481
- }
482
-
483
- private function createArchiveTempFolder($archiveBasename) {
484
- $archiveTempDir = get_temp_dir() . '/' . $archiveBasename;
485
- if(file_exists($archiveTempDir) && is_dir($archiveTempDir) && (time() - filemtime($archiveTempDir) < max(30, SHORTPIXEL_MAX_EXECUTION_TIME) + 10)) {
486
- WPShortPixel::log("CONFLICT. Folder already exists and is modified in the last minute. Current IP:" . $_SERVER['REMOTE_ADDR']);
487
- return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
488
- }
489
- if( !file_exists($archiveTempDir) && !@mkdir($archiveTempDir) ) {
490
- return array("Status" => self::STATUS_ERROR, "Code" => self::ERR_SAVE, "Message" => "Could not create temporary folder.");
491
- }
492
- return array("Status" => self::STATUS_SUCCESS, "Dir" => $archiveTempDir);
493
- }
494
-
495
- private function downloadArchive($archive, $compressionType, $first = true) {
496
- if($archive->ArchiveStatus->Code == 1 || $archive->ArchiveStatus->Code == 0) {
497
- return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
498
- } elseif($archive->ArchiveStatus->Code == 2) {
499
-
500
- $suffix = ($compressionType == 0 ? "-lossless" : "");
501
- $archiveURL = "Archive" . ($compressionType == 0 ? "Lossless" : "") . "URL";
502
- $archiveSize = "Archive" . ($compressionType == 0 ? "Lossless" : "") . "Size";
503
-
504
- $archiveTemp = $this->createArchiveTempFolder(wp_basename($archive->$archiveURL, '.tar'));
505
- if($archiveTemp["Status"] == self::STATUS_SUCCESS) { $archiveTempDir = $archiveTemp["Dir"]; }
506
- else { return $archiveTemp; }
507
-
508
- $downloadResult = $this->handleDownload($archive->$archiveURL, $archive->$archiveSize, 0, 'NA');
509
-
510
- if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
511
- $archiveFile = $downloadResult['Message'];
512
- if(filesize($archiveFile) !== $archive->$archiveSize) {
513
- @unlink($archiveFile);
514
- ShortpixelFolder::deleteFolder($archiveTempDir);
515
- return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
516
- }
517
- $pharData = new PharData($archiveFile);
518
- try {
519
- if (SHORTPIXEL_DEBUG === true) {
520
- $info = "Current IP:" . $_SERVER['REMOTE_ADDR'] . "ARCHIVE CONTENTS: COUNT " . $pharData->count() . ", ";
521
- foreach($pharData as $file) {
522
- $info .= $file . ", ";
523
- }
524
- WPShortPixel::log($info);
525
- }
526
- $pharData->extractTo($archiveTempDir, null, true);
527
- WPShortPixel::log("ARCHIVE EXTRACTED " . json_encode(scandir($archiveTempDir)));
528
- @unlink($archiveFile);
529
- } catch (Exception $ex) {
530
- @unlink($archiveFile);
531
- ShortpixelFolder::deleteFolder($archiveTempDir);
532
- return array("Status" => self::STATUS_ERROR, "Code" => $ex->getCode(), "Message" => $ex->getMessage());
533
- }
534
- return array("Status" => self::STATUS_SUCCESS, "Code" => 2, "Message" => "Success", "Path" => $archiveTempDir);
535
-
536
- } else {
537
- WPShortPixel::log("ARCHIVE ERROR (" . $archive->$archiveURL . "): " . json_encode($downloadResult));
538
- if($first && $downloadResult['Code'] == self::ERR_INCORRECT_FILE_SIZE) {
539
- WPShortPixel::log("RETRYING AFTER ARCHIVE ERROR");
540
- return $this->downloadArchive($archive, $compressionType, false); // try again, maybe the archive was flushing...
541
- }
542
- @rmdir($archiveTempDir); //in the case it was just created and it's empty...
543
- return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
544
- }
545
- }
546
- return false;
547
- }
548
-
549
- /**
550
- * handles a successful optimization, setting metadata and handling download for each file in the set
551
- * @param array $APIresponse - the response from the API - contains the optimized images URLs to download
552
- * @param array $PATHs - list of local paths for the files
553
- * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
554
- * @param int $compressionType - 1 - lossy, 2 - glossy, 0 - lossless
555
- * @return array status/message
556
- */
557
- private function handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType) {
558
- WPShortPixel::log('Handling Success!');
559
-
560
- $counter = $savedSpace = $originalSpace = $optimizedSpace = $averageCompression = 0;
561
- $NoBackup = true;
562
-
563
- if($compressionType) {
564
- $fileType = "LossyURL";
565
- $fileSize = "LossySize";
566
- } else {
567
- $fileType = "LosslessURL";
568
- $fileSize = "LoselessSize";
569
- }
570
- $webpType = "WebP" . $fileType;
571
-
572
- $archive = /*false &&*/
573
- ($this->_settings->downloadArchive == 7 && class_exists('PharData') && isset($APIresponse[count($APIresponse) - 1]->ArchiveStatus))
574
- ? $this->downloadArchive($APIresponse[count($APIresponse) - 1], $compressionType) : false;
575
- if($archive !== false && $archive['Status'] !== self::STATUS_SUCCESS) {
576
- return $archive;
577
- }
578
-
579
- //download each file from array and process it
580
- foreach ( $APIresponse as $fileData )
581
- {
582
- if(!isset($fileData->Status)) continue; //if optimized images archive is activated, last entry of APIResponse if the Archive data.
583
-
584
- if ( $fileData->Status->Code == 2 ) //file was processed OK
585
- {
586
- if ( $counter == 0 ) { //save percent improvement for main file
587
- $percentImprovement = $fileData->PercentImprovement;
588
- } else { //count thumbnails only
589
- $this->_settings->thumbsCount = $this->_settings->thumbsCount + 1;
590
- }
591
- //TODO la sfarsit sa faca fallback la handleDownload
592
- if($archive) {
593
- $downloadResult = $this->fromArchive($archive['Path'], $fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize, isset($fileData->$webpType) ? $fileData->$webpType : 'NA');
594
- } else {
595
- $downloadResult = $this->handleDownload($fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize, isset($fileData->$webpType) ? $fileData->$webpType : 'NA');
596
- }
597
-
598
- $tempFiles[$counter] = $downloadResult;
599
- if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
600
- //nothing to do
601
- }
602
- //when the status is STATUS_UNCHANGED we just skip the array line for that one
603
- elseif( $downloadResult['Status'] == self::STATUS_UNCHANGED ) {
604
- //this image is unchanged so won't be copied below, only the optimization stats need to be computed
605
- $originalSpace += $fileData->OriginalSize;
606
- $optimizedSpace += $fileData->$fileSize;
607
- }
608
- else {
609
- self::cleanupTemporaryFiles($archive, $tempFiles);
610
- return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
611
- }
612
-
613
- }
614
- else { //there was an error while trying to download a file
615
- $tempFiles[$counter] = "";
616
- }
617
- $counter++;
618
- }
619
-
620
- //figure out in what SubDir files should land
621
- $mainPath = $itemHandler->getMeta()->getPath();
622
-
623
- //if backup is enabled - we try to save the images
624
- if( $this->_settings->backupImages )
625
- {
626
- $backupStatus = self::backupImage($mainPath, $PATHs);
627
- if($backupStatus == self::STATUS_FAIL) {
628
- $itemHandler->incrementRetries(1, self::ERR_SAVE_BKP, $backupStatus["Message"]);
629
- self::cleanupTemporaryFiles($archive, empty($tempFiles) ? array() : $tempFiles);
630
- return array("Status" => self::STATUS_FAIL, "Code" =>"backup-fail", "Message" => "Failed to back the image up.");
631
- }
632
- $NoBackup = false;
633
- }//end backup section
634
-
635
- $writeFailed = 0;
636
- $width = $height = null;
637
- $resize = $this->_settings->resizeImages;
638
- $retinas = 0;
639
- $thumbsOpt = 0;
640
- $thumbsOptList = array();
641
-
642
- if ( !empty($tempFiles) )
643
- {
644
- //overwrite the original files with the optimized ones
645
- foreach ( $tempFiles as $tempFileID => $tempFile )
646
- {
647
- if(!is_array($tempFile)) continue;
648
-
649
- $targetFile = $PATHs[$tempFileID];
650
- $isRetina = ShortPixelMetaFacade::isRetina($targetFile);
651
-
652
- if( ($tempFile['Status'] == self::STATUS_UNCHANGED || $tempFile['Status'] == self::STATUS_SUCCESS) && !$isRetina
653
- && $targetFile !== $mainPath) {
654
- $thumbsOpt++;
655
- $thumbsOptList[] = self::MB_basename($targetFile);
656
- }
657
-
658
- if($tempFile['Status'] == self::STATUS_SUCCESS) { //if it's unchanged it will still be in the array but only for WebP (handled below)
659
- $tempFilePATH = $tempFile["Message"];
660
- if ( file_exists($tempFilePATH) && file_exists($targetFile) && is_writable($targetFile) ) {
661
- copy($tempFilePATH, $targetFile);
662
- if(ShortPixelMetaFacade::isRetina($targetFile)) {
663
- $retinas ++;
664
- }
665
- if($resize && $itemHandler->getMeta()->getPath() == $targetFile) { //this is the main image
666
- $size = getimagesize($PATHs[$tempFileID]);
667
- $width = $size[0];
668
- $height = $size[1];
669
- }
670
- //Calculate the saved space
671
- $fileData = $APIresponse[$tempFileID];
672
- $savedSpace += $fileData->OriginalSize - $fileData->$fileSize;
673
- $originalSpace += $fileData->OriginalSize;
674
- $optimizedSpace += $fileData->$fileSize;
675
- $averageCompression += $fileData->PercentImprovement;
676
- WPShortPixel::log("HANDLE SUCCESS: Image " . $PATHs[$tempFileID] . " original size: ".$fileData->OriginalSize . " optimized: " . $fileData->$fileSize);
677
-
678
- //add the number of files with < 5% optimization
679
- if ( ( ( 1 - $APIresponse[$tempFileID]->$fileSize/$APIresponse[$tempFileID]->OriginalSize ) * 100 ) < 5 ) {
680
- $this->_settings->under5Percent++;
681
- }
682
- }
683
- else {
684
- if($archive && SHORTPIXEL_DEBUG === true) {
685
- if(!file_exists($tempFilePATH)) {
686
- WPShortPixel::log("MISSING FROM ARCHIVE. tempFilePath: $tempFilePATH with ID: $tempFileID");
687
- } elseif(!file_exists($targetFile)){
688
- WPShortPixel::log("MISSING TARGET: $targetFile");
689
- } elseif(!is_writable($targetFile)){
690
- WPShortPixel::log("TARGET NOT WRITABLE: $targetFile");
691
- }
692
- }
693
- $writeFailed++;
694
- }
695
- @unlink($tempFilePATH);
696
- }
697
-
698
- $tempWebpFilePATH = $tempFile["WebP"];
699
- if(file_exists($tempWebpFilePATH)) {
700
- $targetWebPFileCompat = dirname($targetFile) . '/'. self::MB_basename($targetFile, '.' . pathinfo($targetFile, PATHINFO_EXTENSION)) . ".webp";
701
- $targetWebPFile = dirname($targetFile) . '/' . self::MB_basename($targetFile) . ".webp";
702
- copy($tempWebpFilePATH, $targetWebPFile);
703
- if(!file_exists($targetWebPFileCompat)) {
704
- @symlink($targetWebPFile,$targetWebPFileCompat);
705
- }
706
- @unlink($tempWebpFilePATH);
707
- }
708
- }
709
- self::cleanupTemporaryFiles($archive, $tempFiles);
710
-
711
- if ( $writeFailed > 0 )//there was an error
712
- {
713
- if($archive && SHORTPIXEL_DEBUG === true) {
714
- WPShortPixel::log("ARCHIVE HAS MISSING FILES. EXPECTED: " . json_encode($PATHs)
715
- . " AND: " . json_encode($APIresponse)
716
- . " GOT ARCHIVE: " . $APIresponse[count($APIresponse) - 1]->ArchiveURL . " LOSSLESS: " . $APIresponse[count($APIresponse) - 1]->ArchiveLosslessURL
717
- . " CONTAINING: " . json_encode(scandir($archive['Path'])));
718
- }
719
- $msg = sprintf(__('Optimized version of %s file(s) couldn\'t be updated.','shortpixel-image-optimiser'),$writeFailed);
720
- $itemHandler->incrementRetries(1, self::ERR_SAVE, $msg);
721
- $this->_settings->bulkProcessingStatus = "error";
722
- return array("Status" => self::STATUS_FAIL, "Code" =>"write-fail", "Message" => $msg);
723
- }
724
- } elseif( 0 + $fileData->PercentImprovement < 5) {
725
- $this->_settings->under5Percent++;
726
- }
727
- //old average counting
728
- $this->_settings->savedSpace += $savedSpace;
729
- $averageCompression = $this->_settings->averageCompression * $this->_settings->fileCount / ($this->_settings->fileCount + count($APIresponse));
730
- $this->_settings->averageCompression = $averageCompression;
731
- $this->_settings->fileCount += count($APIresponse);
732
- //new average counting
733
- $this->_settings->totalOriginal += $originalSpace;
734
- $this->_settings->totalOptimized += $optimizedSpace;
735
-
736
- //update metadata for this file
737
- $meta = $itemHandler->getMeta();
738
- // die(var_dump($percentImprovement));
739
- if($meta->getThumbsTodo()) {
740
- $percentImprovement = $meta->getImprovementPercent();
741
- }
742
- $png2jpg = $meta->getPng2Jpg();
743
- $png2jpg = is_array($png2jpg) ? $png2jpg['optimizationPercent'] : 0;
744
- $meta->setMessage($originalSpace
745
- ? number_format(100.0 * (1.0 - $optimizedSpace / $originalSpace), 2)
746
- : "Couldn't compute thumbs optimization percent. Main image: " . $percentImprovement);
747
- WPShortPixel::log("HANDLE SUCCESS: Image optimization: ".$meta->getMessage());
748
- $meta->setCompressionType($compressionType);
749
- $meta->setCompressedSize(@filesize($meta->getPath()));
750
- $meta->setKeepExif($this->_settings->keepExif);
751
- $meta->setTsOptimized(date("Y-m-d H:i:s"));
752
- $meta->setThumbsOptList(is_array($meta->getThumbsOptList()) ? array_unique(array_merge($meta->getThumbsOptList(), $thumbsOptList)) : $thumbsOptList);
753
- $meta->setThumbsOpt(($meta->getThumbsTodo() || $this->_settings->processThumbnails) ? count($meta->getThumbsOptList()) : 0);
754
- $meta->setRetinasOpt($retinas);
755
- if(null !== $this->_settings->excludeSizes) {
756
- $meta->setExcludeSizes($this->_settings->excludeSizes);
757
- }
758
- $meta->setThumbsTodo(false);
759
- //* Not yet as it doesn't seem to work... */$meta->addThumbs($webpSizes);
760
- if($width && $height) {
761
- $meta->setActualWidth($width);
762
- $meta->setActualHeight($height);
763
- }
764
- $meta->setRetries($meta->getRetries() + 1);
765
- $meta->setBackup(!$NoBackup);
766
- $meta->setStatus(2);
767
-
768
- $itemHandler->updateMeta($meta);
769
- $itemHandler->optimizationSucceeded();
770
- WPShortPixel::log("HANDLE SUCCESS: Metadata saved.");
771
-
772
- if(!$originalSpace) { //das kann nicht sein, alles klar?!
773
- throw new Exception("OriginalSpace = 0. APIResponse" . json_encode($APIresponse));
774
- }
775
-
776
- //we reset the retry counter in case of success
777
- $this->_settings->apiRetries = 0;
778
-
779
- return array("Status" => self::STATUS_SUCCESS, "Message" => 'Success: No pixels remained unsqueezed :-)',
780
- "PercentImprovement" => $originalSpace
781
- ? number_format(100.0 * (1.0 - (1.0 - $png2jpg / 100.0) * $optimizedSpace / $originalSpace), 2)
782
- : "Couldn't compute thumbs optimization percent. Main image: " . $percentImprovement);
783
- }//end handleSuccess
784
-
785
- /**
786
- * @param $archive
787
- * @param $tempFiles
788
- */
789
- protected static function cleanupTemporaryFiles($archive, $tempFiles)
790
- {
791
- if ($archive) {
792
- ShortpixelFolder::deleteFolder($archive['Path']);
793
- } else {
794
- if (!empty($tempFiles) && is_array($tempFiles)) {
795
- foreach ($tempFiles as $tmpFile) {
796
- @unlink($tmpFile["Message"]);
797
- }
798
- }
799
- }
800
- }
801
-
802
- /**
803
- * a basename alternative that deals OK with multibyte charsets (e.g. Arabic)
804
- * @param string $Path
805
- * @return string
806
- */
807
- static public function MB_basename($Path, $suffix = false){
808
- $Separator = " qq ";
809
- $qqPath = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $Path);
810
- if(!$qqPath) { //this is not an UTF8 string!! Don't rely on basename either, since if filename starts with a non-ASCII character it strips it off
811
- $fileName = end(explode(DIRECTORY_SEPARATOR, $Path));
812
- $pos = strpos($fileName, $suffix);
813
- if($pos !== false) {
814
- return substr($fileName, 0, $pos);
815
- }
816
- return $fileName;
817
- }
818
- $suffix = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $suffix);
819
- $Base = basename($qqPath, $suffix);
820
- $Base = str_replace($Separator, "", $Base);
821
- return $Base;
822
- }
823
-
824
- /**
825
- * sometimes, the paths to the files as defined in metadata are wrong, we try to automatically correct them
826
- * @param array $PATHs
827
- * @return boolean|string
828
- */
829
- static public function CheckAndFixImagePaths($PATHs){
830
-
831
- $ErrorCount = 0;
832
- $Tmp = explode("/", SHORTPIXEL_UPLOADS_BASE);
833
- $TmpCount = count($Tmp);
834
- $StichString = $Tmp[$TmpCount-2] . "/" . $Tmp[$TmpCount-1];
835
- //files exist on disk?
836
- $missingFiles = array();
837
- foreach ( $PATHs as $Id => $File )
838
- {
839
- //we try again with a different path
840
- if ( !file_exists($File) ){
841
- //$NewFile = $uploadDir['basedir'] . "/" . substr($File,strpos($File, $StichString));//+strlen($StichString));
842
- $NewFile = SHORTPIXEL_UPLOADS_BASE . substr($File,strpos($File, $StichString)+strlen($StichString));
843
- if (file_exists($NewFile)) {
844
- $PATHs[$Id] = $NewFile;
845
- } else {
846
- $NewFile = SHORTPIXEL_UPLOADS_BASE . "/" . $File;
847
- if (file_exists($NewFile)) {
848
- $PATHs[$Id] = $NewFile;
849
- } else {
850
- $missingFiles[] = $File;
851
- $ErrorCount++;
852
- }
853
- }
854
- }
855
- }
856
-
857
- if ( $ErrorCount > 0 ) {
858
- return array("error" => $missingFiles);//false;
859
- } else {
860
- return $PATHs;
861
- }
862
- }
863
-
864
- static public function getCompressionTypeName($compressionType) {
865
- if(is_array($compressionType)) {
866
- return array_map(array('ShortPixelAPI', 'getCompressionTypeName'), $compressionType);
867
- }
868
- return 0 + $compressionType == 2 ? 'glossy' : (0 + $compressionType == 1 ? 'lossy' : 'lossless');
869
- }
870
-
871
- static public function getCompressionTypeCode($compressionName) {
872
- return $compressionName == 'glossy' ? 2 : ($compressionName == 'lossy' ? 1 : 0);
873
- }
874
- }
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( !function_exists( 'download_url' ) ) {
3
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
4
+ }
5
+
6
+ class ShortPixelAPI {
7
+
8
+ const STATUS_SUCCESS = 1;
9
+ const STATUS_UNCHANGED = 0;
10
+ const STATUS_ERROR = -1;
11
+ const STATUS_FAIL = -2;
12
+ const STATUS_QUOTA_EXCEEDED = -3;
13
+ const STATUS_SKIP = -4;
14
+ const STATUS_NOT_FOUND = -5;
15
+ const STATUS_NO_KEY = -6;
16
+ const STATUS_RETRY = -7;
17
+ const STATUS_QUEUE_FULL = -404;
18
+ const STATUS_MAINTENANCE = -500;
19
+
20
+ const ERR_FILE_NOT_FOUND = -2;
21
+ const ERR_TIMEOUT = -3;
22
+ const ERR_SAVE = -4;
23
+ const ERR_SAVE_BKP = -5;
24
+ const ERR_INCORRECT_FILE_SIZE = -6;
25
+ const ERR_DOWNLOAD = -7;
26
+ const ERR_PNG2JPG_MEMORY = -8;
27
+ const ERR_POSTMETA_CORRUPT = -9;
28
+ const ERR_UNKNOWN = -999;
29
+
30
+ private $_settings;
31
+ private $_apiEndPoint;
32
+ private $_apiDumpEndPoint;
33
+
34
+
35
+ public function __construct($settings) {
36
+ $this->_settings = $settings;
37
+ $this->_apiEndPoint = $this->_settings->httpProto . '://' . SHORTPIXEL_API . '/v2/reducer.php';
38
+ $this->_apiDumpEndPoint = $this->_settings->httpProto . '://' . SHORTPIXEL_API . '/v2/cleanup.php';
39
+ }
40
+
41
+ protected function prepareRequest($requestParameters, $Blocking = false) {
42
+ $arguments = array(
43
+ 'method' => 'POST',
44
+ 'timeout' => 15,
45
+ 'redirection' => 3,
46
+ 'sslverify' => false,
47
+ 'httpversion' => '1.0',
48
+ 'blocking' => $Blocking,
49
+ 'headers' => array(),
50
+ 'body' => json_encode($requestParameters),
51
+ 'cookies' => array()
52
+ );
53
+ //die(var_dump($requestParameters));
54
+ //add this explicitely only for https, otherwise (for http) it slows down the request
55
+ if($this->_settings->httpProto !== 'https') {
56
+ unset($arguments['sslverify']);
57
+ }
58
+
59
+ return $arguments;
60
+ }
61
+
62
+ public function doDumpRequests($URLs) {
63
+ if(!count($URLs)) {
64
+ return false;
65
+ }
66
+ $ret = wp_remote_post($this->_apiDumpEndPoint, $this->prepareRequest(array(
67
+ 'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
68
+ 'key' => $this->_settings->apiKey,
69
+ 'urllist' => $URLs
70
+ ) ) );
71
+ return $ret;
72
+ }
73
+
74
+ /**
75
+ * sends a compression request to the API
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 bool|int $compressionType 1 - lossy, 2 - glossy, 0 - lossless
80
+ * @param bool $refresh
81
+ * @return Array response from wp_remote_post or error
82
+ * @throws Exception
83
+ */
84
+ public function doRequests($URLs, $Blocking, $itemHandler, $compressionType = false, $refresh = false) {
85
+
86
+ if(!count($URLs)) {
87
+ $meta = $itemHandler->getMeta();
88
+ if(count($meta->getThumbsMissing())) {
89
+ $added = array();
90
+ $files = " (";
91
+ foreach ($meta->getThumbsMissing() as $miss) {
92
+ if(isset($added[$miss])) continue;
93
+ $files .= $miss . ", ";
94
+ $added[$miss] = true;
95
+ }
96
+ if(strrpos($files, ', ')) {
97
+ $files = substr_replace($files , ')', strrpos($files , ', '));
98
+ }
99
+ throw new Exception(__('Image files are missing.', 'shortpixel-image-optimiser') . (strlen($files) > 1 ? $files : ''));
100
+ }
101
+ else throw new Exception(__('Image files are missing.', 'shortpixel-image-optimiser'));
102
+ }
103
+
104
+ $apiKey = $this->_settings->apiKey;
105
+ if(strlen($apiKey) < 20) { //found in the logs many cases when the API Key is '', probably deleted from the DB but the verifiedKey setting is not changed
106
+ $this->_settings->verifiedKey = false;
107
+ throw new Exception(__('Invalid API Key', 'shortpixel-image-optimiser'));
108
+ }
109
+
110
+ // WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
111
+
112
+ $requestParameters = array(
113
+ 'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
114
+ 'key' => $apiKey,
115
+ 'lossy' => $compressionType === false ? $this->_settings->compressionType : $compressionType,
116
+ 'cmyk2rgb' => $this->_settings->CMYKtoRGBconversion,
117
+ 'keep_exif' => ($this->_settings->keepExif ? "1" : "0"),
118
+ 'convertto' => ($this->_settings->createWebp ? urlencode("+webp") : ""),
119
+ 'resize' => $this->_settings->resizeImages ? 1 + 2 * ($this->_settings->resizeType == 'inner' ? 1 : 0) : 0,
120
+ 'resize_width' => $this->_settings->resizeWidth,
121
+ 'resize_height' => $this->_settings->resizeHeight,
122
+ 'urllist' => $URLs
123
+ );
124
+ if(/*false &&*/ $this->_settings->downloadArchive == 7 && class_exists('PharData')) {
125
+ $requestParameters['group'] = $itemHandler->getId();
126
+ }
127
+ if($refresh) {
128
+ $requestParameters['refresh'] = 1;
129
+ }
130
+
131
+ //WpShortPixel::log("ShortPixel API Request Settings: " . json_encode($requestParameters));
132
+
133
+ $response = wp_remote_post($this->_apiEndPoint, $this->prepareRequest($requestParameters, $Blocking) );
134
+
135
+ //WpShortPixel::log('RESPONSE: ' . json_encode($response));
136
+
137
+ //only if $Blocking is true analyze the response
138
+ if ( $Blocking )
139
+ {
140
+ //WpShortPixel::log("API response : " . json_encode($response));
141
+
142
+ //die(var_dump(array('URL: ' => $this->_apiEndPoint, '<br><br>REQUEST:' => $requestParameters, '<br><br>RESPONSE: ' => $response, '<br><br>BODY: ' => isset($response['body']) ? $response['body'] : '' )));
143
+ //there was an error, save this error inside file's SP optimization field
144
+ if ( is_object($response) && get_class($response) == 'WP_Error' )
145
+ {
146
+ $errorMessage = $response->errors['http_request_failed'][0];
147
+ $errorCode = 503;
148
+ }
149
+ elseif ( isset($response['response']['code']) && $response['response']['code'] <> 200 )
150
+ {
151
+ $errorMessage = $response['response']['code'] . " - " . $response['response']['message'];
152
+ $errorCode = $response['response']['code'];
153
+ }
154
+
155
+ if ( isset($errorMessage) )
156
+ {//set details inside file so user can know what happened
157
+ $itemHandler->incrementRetries(1, $errorCode, $errorMessage);
158
+ return array("response" => array("code" => $errorCode, "message" => $errorMessage ));
159
+ }
160
+
161
+ return $response;//this can be an error or a good response
162
+ }
163
+
164
+ return $response;
165
+ }
166
+
167
+ /**
168
+ * parse the JSON response
169
+ * @param $response
170
+ * @return array parsed
171
+ */
172
+ public function parseResponse($response) {
173
+ $data = $response['body'];
174
+ $data = json_decode($data);
175
+ return (array)$data;
176
+ }
177
+
178
+ /**
179
+ * handles the processing of the image using the ShortPixel API
180
+ * @param array $URLs - list of urls to send to API
181
+ * @param array $PATHs - list of local paths for the images
182
+ * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
183
+ * @return array status/message array
184
+ */
185
+ public function processImage($URLs, $PATHs, $itemHandler = null) {
186
+ return $this->processImageRecursive($URLs, $PATHs, $itemHandler, 0);
187
+ }
188
+
189
+ /**
190
+ * handles the processing of the image using the ShortPixel API - cals itself recursively until success
191
+ * @param array $URLs - list of urls to send to API
192
+ * @param array $PATHs - list of local paths for the images
193
+ * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
194
+ * @param int $startTime - time of the first call
195
+ * @return array status/message array
196
+ */
197
+ private function processImageRecursive($URLs, $PATHs, $itemHandler = null, $startTime = 0)
198
+ {
199
+ //WPShortPixel::log("processImageRecursive ID: " . $itemHandler->getId() . " PATHs: " . json_encode($PATHs));
200
+
201
+ $PATHs = self::CheckAndFixImagePaths($PATHs);//check for images to make sure they exist on disk
202
+ if ( $PATHs === false || isset($PATHs['error'])) {
203
+ $missingFiles = '';
204
+ if(isset($PATHs['error'])) {
205
+ foreach($PATHs['error'] as $errPath) {
206
+ $missingFiles .= (strlen($missingFiles) ? ', ':'') . basename(stripslashes($errPath));
207
+ }
208
+ }
209
+ $msg = __('The file(s) do not exist on disk: ','shortpixel-image-optimiser') . $missingFiles;
210
+ $itemHandler->setError(self::ERR_FILE_NOT_FOUND, $msg );
211
+ return array("Status" => self::STATUS_SKIP, "Message" => $msg, "Silent" => $itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE ? 1 : 0);
212
+ }
213
+
214
+ //tries multiple times (till timeout almost reached) to fetch images.
215
+ if($startTime == 0) {
216
+ $startTime = time();
217
+ }
218
+ $apiRetries = $this->_settings->apiRetries;
219
+
220
+ if( time() - $startTime > SHORTPIXEL_MAX_EXECUTION_TIME2)
221
+ {//keeps track of time
222
+ if ( $apiRetries > SHORTPIXEL_MAX_API_RETRIES )//we tried to process this time too many times, giving up...
223
+ {
224
+ $itemHandler->incrementRetries(1, self::ERR_TIMEOUT, __('Timed out while processing.','shortpixel-image-optimiser'));
225
+ $this->_settings->apiRetries = 0; //fai added to solve a bug?
226
+ return array("Status" => self::STATUS_SKIP,
227
+ "Message" => ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE ? __('Image ID','shortpixel-image-optimiser') : __('Media ID','shortpixel-image-optimiser'))
228
+ . ": " . $itemHandler->getId() .' ' . __('Skip this image, try the next one.','shortpixel-image-optimiser'));
229
+ }
230
+ else
231
+ {//we'll try again next time user visits a page on admin panel
232
+ $apiRetries++;
233
+ $this->_settings->apiRetries = $apiRetries;
234
+ return array("Status" => self::STATUS_RETRY, "Message" => __('Timed out while processing.','shortpixel-image-optimiser') . ' (pass '.$apiRetries.')',
235
+ "Count" => $apiRetries);
236
+ }
237
+ }
238
+
239
+ //#$compressionType = isset($meta['ShortPixel']['type']) ? ($meta['ShortPixel']['type'] == 'lossy' ? 1 : 0) : $this->_settings->compressionType;
240
+ $meta = $itemHandler->getMeta();
241
+ $compressionType = $meta->getCompressionType() !== null ? $meta->getCompressionType() : $this->_settings->compressionType;
242
+ $response = $this->doRequests($URLs, true, $itemHandler, $compressionType);//send requests to API
243
+
244
+ //die($response['body']);
245
+
246
+ if($response['response']['code'] != 200) {//response <> 200 -> there was an error apparently?
247
+ return array("Status" => self::STATUS_FAIL, "Message" => __('There was an error and your request was not processed.', 'shortpixel-image-optimiser')
248
+ . (isset($response['response']['message']) ? ' (' . $response['response']['message'] . ')' : ''), "Code" => $response['response']['code']);
249
+ }
250
+
251
+ $APIresponse = $this->parseResponse($response);//get the actual response from API, its an array
252
+
253
+ if ( isset($APIresponse[0]) ) //API returned image details
254
+ {
255
+ foreach ( $APIresponse as $imageObject ) {//this part makes sure that all the sizes were processed and ready to be downloaded
256
+ if ( isset($imageObject->Status) && ( $imageObject->Status->Code == 0 || $imageObject->Status->Code == 1 ) ) {
257
+ sleep(1);
258
+ return $this->processImageRecursive($URLs, $PATHs, $itemHandler, $startTime);
259
+ }
260
+ }
261
+
262
+ $firstImage = $APIresponse[0];//extract as object first image
263
+ switch($firstImage->Status->Code)
264
+ {
265
+ case 2:
266
+ //handle image has been processed
267
+ if(!isset($firstImage->Status->QuotaExceeded)) {
268
+ $this->_settings->quotaExceeded = 0;//reset the quota exceeded flag
269
+ }
270
+ return $this->handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType);
271
+ default:
272
+ //handle error
273
+ $incR = 1;
274
+ if ( !file_exists($PATHs[0]) ) {
275
+ $err = array("Status" => self::STATUS_NOT_FOUND, "Message" => "File not found on disk. "
276
+ . ($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE ? "Image" : "Media")
277
+ . " ID: " . $itemHandler->getId(), "Code" => self::ERR_FILE_NOT_FOUND);
278
+ $incR = 3;
279
+ }
280
+ elseif ( isset($APIresponse[0]->Status->Message) ) {
281
+ //return array("Status" => self::STATUS_FAIL, "Message" => "There was an error and your request was not processed (" . $APIresponse[0]->Status->Message . "). REQ: " . json_encode($URLs));
282
+ $err = array("Status" => self::STATUS_FAIL, "Code" => (isset($APIresponse[0]->Status->Code) ? $APIresponse[0]->Status->Code : self::ERR_UNKNOWN),
283
+ "Message" => __('There was an error and your request was not processed.','shortpixel-image-optimiser')
284
+ . " (" . wp_basename($APIresponse[0]->OriginalURL) . ": " . $APIresponse[0]->Status->Message . ")");
285
+ } else {
286
+ $err = array("Status" => self::STATUS_FAIL, "Message" => __('There was an error and your request was not processed.','shortpixel-image-optimiser'),
287
+ "Code" => (isset($APIresponse[0]->Status->Code) ? $APIresponse[0]->Status->Code : self::ERR_UNKNOWN));
288
+ }
289
+
290
+ $itemHandler->incrementRetries($incR, $err["Code"], $err["Message"]);
291
+ $meta = $itemHandler->getMeta();
292
+ if($meta->getRetries() >= SHORTPIXEL_MAX_FAIL_RETRIES) {
293
+ $meta->setStatus($APIresponse[0]->Status->Code);
294
+ $meta->setMessage($APIresponse[0]->Status->Message);
295
+ $itemHandler->updateMeta($meta);
296
+ }
297
+ return $err;
298
+ }
299
+ }
300
+
301
+ if(!isset($APIresponse['Status'])) {
302
+ WpShortPixel::log("API Response Status unfound : " . json_encode($APIresponse));
303
+ return array("Status" => self::STATUS_FAIL, "Message" => __('Unrecognized API response. Please contact support.','shortpixel-image-optimiser'),
304
+ "Code" => self::ERR_UNKNOWN, "Debug" => ' (SERVER RESPONSE: ' . json_encode($response) . ')');
305
+ } else {
306
+ switch($APIresponse['Status']->Code)
307
+ {
308
+ case -403:
309
+ @delete_option('bulkProcessingStatus');
310
+ $this->_settings->quotaExceeded = 1;
311
+ return array("Status" => self::STATUS_QUOTA_EXCEEDED, "Message" => __('Quota exceeded.','shortpixel-image-optimiser'));
312
+ break;
313
+ case -404:
314
+ return array("Status" => self::STATUS_QUEUE_FULL, "Message" => $APIresponse['Status']->Message);
315
+ case -500:
316
+ return array("Status" => self::STATUS_MAINTENANCE, "Message" => $APIresponse['Status']->Message);
317
+ }
318
+
319
+ //sometimes the response array can be different
320
+ if (is_numeric($APIresponse['Status']->Code)) {
321
+ return array("Status" => self::STATUS_FAIL, "Message" => $APIresponse['Status']->Message);
322
+ } else {
323
+ return array("Status" => self::STATUS_FAIL, "Message" => $APIresponse[0]->Status->Message);
324
+ }
325
+ }
326
+ }
327
+
328
+ /**
329
+ * sets the preferred protocol of URL using the globally set preferred protocol.
330
+ * If global protocol not set, sets it by testing the download of a http test image from ShortPixel site.
331
+ * If http works then it's http, otherwise sets https
332
+ * @param string $url
333
+ * @param bool $reset - forces recheck even if preferred protocol is already set
334
+ * @return string url with the preferred protocol
335
+ */
336
+ public function setPreferredProtocol($url, $reset = false) {
337
+ //switch protocol based on the formerly detected working protocol
338
+ if($this->_settings->downloadProto == '' || $reset) {
339
+ //make a test to see if the http is working
340
+ $testURL = 'http://' . SHORTPIXEL_API . '/img/connection-test-image.png';
341
+ $result = download_url($testURL, 10);
342
+ $this->_settings->downloadProto = is_wp_error( $result ) ? 'https' : 'http';
343
+ }
344
+ return $this->_settings->downloadProto == 'http' ?
345
+ str_replace('https://', 'http://', $url) :
346
+ str_replace('http://', 'https://', $url);
347
+
348
+
349
+ }
350
+
351
+ function fromArchive($path, $optimizedUrl, $optimizedSize, $originalSize, $webpUrl) {
352
+ $webpTempFile = "NA";
353
+ if($webpUrl !== "NA") {
354
+ $webpTempFile = $path . '/' . wp_basename($webpUrl);
355
+ $webpTempFile = file_exists($webpTempFile) ? $webpTempFile : 'NA';
356
+ }
357
+
358
+ //if there is no improvement in size then we do not download this file
359
+ if ( $originalSize == $optimizedSize )
360
+ return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
361
+
362
+ $correctFileSize = $optimizedSize;
363
+ $tempFile = $path . '/' . wp_basename($optimizedUrl);
364
+
365
+ if(file_exists($tempFile)) {
366
+ //on success we return this
367
+ if( filesize($tempFile) != $correctFileSize) {
368
+ $size = filesize($tempFile);
369
+ @unlink($tempFile);
370
+ @unlink($webpTempFile);
371
+ $returnMessage = array(
372
+ "Status" => self::STATUS_ERROR,
373
+ "Code" => self::ERR_INCORRECT_FILE_SIZE,
374
+ "Message" => sprintf(__('Error in archive - incorrect file size (downloaded: %s, correct: %s )','shortpixel-image-optimiser'),$size, $correctFileSize));
375
+ } else {
376
+ $returnMessage = array("Status" => self::STATUS_SUCCESS, "Message" => $tempFile, "WebP" => $webpTempFile);
377
+ }
378
+ } else {
379
+ $returnMessage = array("Status" => self::STATUS_ERROR,
380
+ "Code" => self::ERR_FILE_NOT_FOUND,
381
+ "Message" => __('Unable to locate downloaded file','shortpixel-image-optimiser') . " " . $tempFile);
382
+ }
383
+
384
+ return $returnMessage;
385
+ }
386
+
387
+ /**
388
+ * handles the download of an optimized image from ShortPixel API
389
+ * @param string $optimizedUrl
390
+ * @param int $optimizedSize
391
+ * @param int $originalSize
392
+ * @param string $webpUrl
393
+ * @return array status /message array
394
+ */
395
+ private function handleDownload($optimizedUrl, $optimizedSize, $originalSize, $webpUrl){
396
+
397
+ $downloadTimeout = max(ini_get('max_execution_time') - 10, 15);
398
+
399
+ $webpTempFile = "NA";
400
+ if($webpUrl !== "NA") {
401
+ $webpURL = $this->setPreferredProtocol(urldecode($webpUrl));
402
+ $webpTempFile = download_url($webpURL, $downloadTimeout);
403
+ $webpTempFile = is_wp_error( $webpTempFile ) ? "NA" : $webpTempFile;
404
+ }
405
+
406
+ //if there is no improvement in size then we do not download this file
407
+ if ( $originalSize == $optimizedSize )
408
+ return array("Status" => self::STATUS_UNCHANGED, "Message" => "File wasn't optimized so we do not download it.", "WebP" => $webpTempFile);
409
+
410
+ $correctFileSize = $optimizedSize;
411
+ $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl));
412
+
413
+ $tempFile = download_url($fileURL, $downloadTimeout);
414
+ WPShortPixel::log('Downloading file: '.json_encode($tempFile));
415
+ if(is_wp_error( $tempFile ))
416
+ { //try to switch the default protocol
417
+ $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl), true); //force recheck of the protocol
418
+ $tempFile = download_url($fileURL, $downloadTimeout);
419
+ }
420
+
421
+ //on success we return this
422
+ $returnMessage = array("Status" => self::STATUS_SUCCESS, "Message" => $tempFile, "WebP" => $webpTempFile);
423
+
424
+ if ( is_wp_error( $tempFile ) ) {
425
+ @unlink($tempFile);
426
+ @unlink($webpTempFile);
427
+ $returnMessage = array(
428
+ "Status" => self::STATUS_ERROR,
429
+ "Code" => self::ERR_DOWNLOAD,
430
+ "Message" => __('Error downloading file','shortpixel-image-optimiser') . " ({$optimizedUrl}) " . $tempFile->get_error_message());
431
+ }
432
+ //check response so that download is OK
433
+ elseif (!file_exists($tempFile)) {
434
+ $returnMessage = array("Status" => self::STATUS_ERROR,
435
+ "Code" => self::ERR_FILE_NOT_FOUND,
436
+ "Message" => __('Unable to locate downloaded file','shortpixel-image-optimiser') . " " . $tempFile);
437
+ }
438
+ elseif( filesize($tempFile) != $correctFileSize) {
439
+ $size = filesize($tempFile);
440
+ @unlink($tempFile);
441
+ @unlink($webpTempFile);
442
+ $returnMessage = array(
443
+ "Status" => self::STATUS_ERROR,
444
+ "Code" => self::ERR_INCORRECT_FILE_SIZE,
445
+ "Message" => sprintf(__('Error downloading file - incorrect file size (downloaded: %s, correct: %s )','shortpixel-image-optimiser'),$size, $correctFileSize));
446
+ }
447
+ return $returnMessage;
448
+ }
449
+
450
+ public static function backupImage($mainPath, $PATHs) {
451
+ //$fullSubDir = str_replace(wp_normalize_path(get_home_path()), "", wp_normalize_path(dirname($itemHandler->getMeta()->getPath()))) . '/';
452
+ //$SubDir = ShortPixelMetaFacade::returnSubDir($itemHandler->getMeta()->getPath(), $itemHandler->getType());
453
+ $fullSubDir = ShortPixelMetaFacade::returnSubDir($mainPath);
454
+ $source = $PATHs; //array with final paths for these files
455
+
456
+ if( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && !@mkdir(SHORTPIXEL_BACKUP_FOLDER, 0777, true) ) {//creates backup folder if it doesn't exist
457
+ return array("Status" => self::STATUS_FAIL, "Message" => __('Backup folder does not exist and it cannot be created','shortpixel-image-optimiser'));
458
+ }
459
+ //create subdir in backup folder if needed
460
+ @mkdir( SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir, 0777, true);
461
+
462
+ foreach ( $source as $fileID => $filePATH )//create destination files array
463
+ {
464
+ $destination[$fileID] = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . self::MB_basename($source[$fileID]);
465
+ }
466
+ //die("IZ BACKUP: " . SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . var_dump($destination));
467
+
468
+ //now that we have original files and where we should back them up we attempt to do just that
469
+ if(is_writable(SHORTPIXEL_BACKUP_FOLDER))
470
+ {
471
+ foreach ( $destination as $fileID => $filePATH )
472
+ {
473
+ if ( !file_exists($filePATH) )
474
+ {
475
+ if ( !@copy($source[$fileID], $filePATH) )
476
+ {//file couldn't be saved in backup folder
477
+ $msg = sprintf(__('Cannot save file <i>%s</i> in backup directory','shortpixel-image-optimiser'),self::MB_basename($source[$fileID]));
478
+ return array("Status" => self::STATUS_FAIL, "Message" => $msg);
479
+ }
480
+ }
481
+ }
482
+ return array("Status" => self::STATUS_SUCCESS);
483
+ }
484
+ else {//cannot write to the backup dir, return with an error
485
+ $msg = __('Cannot save file in backup directory','shortpixel-image-optimiser');
486
+ return array("Status" => self::STATUS_FAIL, "Message" => $msg);
487
+ }
488
+ }
489
+
490
+ private function createArchiveTempFolder($archiveBasename) {
491
+ $archiveTempDir = get_temp_dir() . '/' . $archiveBasename;
492
+ if(file_exists($archiveTempDir) && is_dir($archiveTempDir) && (time() - filemtime($archiveTempDir) < max(30, SHORTPIXEL_MAX_EXECUTION_TIME) + 10)) {
493
+ WPShortPixel::log("CONFLICT. Folder already exists and is modified in the last minute. Current IP:" . $_SERVER['REMOTE_ADDR']);
494
+ return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
495
+ }
496
+ if( !file_exists($archiveTempDir) && !@mkdir($archiveTempDir) ) {
497
+ return array("Status" => self::STATUS_ERROR, "Code" => self::ERR_SAVE, "Message" => "Could not create temporary folder.");
498
+ }
499
+ return array("Status" => self::STATUS_SUCCESS, "Dir" => $archiveTempDir);
500
+ }
501
+
502
+ private function downloadArchive($archive, $compressionType, $first = true) {
503
+ if($archive->ArchiveStatus->Code == 1 || $archive->ArchiveStatus->Code == 0) {
504
+ return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
505
+ } elseif($archive->ArchiveStatus->Code == 2) {
506
+
507
+ $suffix = ($compressionType == 0 ? "-lossless" : "");
508
+ $archiveURL = "Archive" . ($compressionType == 0 ? "Lossless" : "") . "URL";
509
+ $archiveSize = "Archive" . ($compressionType == 0 ? "Lossless" : "") . "Size";
510
+
511
+ $archiveTemp = $this->createArchiveTempFolder(wp_basename($archive->$archiveURL, '.tar'));
512
+ if($archiveTemp["Status"] == self::STATUS_SUCCESS) { $archiveTempDir = $archiveTemp["Dir"]; }
513
+ else { return $archiveTemp; }
514
+
515
+ $downloadResult = $this->handleDownload($archive->$archiveURL, $archive->$archiveSize, 0, 'NA');
516
+
517
+ if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
518
+ $archiveFile = $downloadResult['Message'];
519
+ if(filesize($archiveFile) !== $archive->$archiveSize) {
520
+ @unlink($archiveFile);
521
+ ShortpixelFolder::deleteFolder($archiveTempDir);
522
+ return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
523
+ }
524
+ $pharData = new PharData($archiveFile);
525
+ try {
526
+ if (SHORTPIXEL_DEBUG === true) {
527
+ $info = "Current IP:" . $_SERVER['REMOTE_ADDR'] . "ARCHIVE CONTENTS: COUNT " . $pharData->count() . ", ";
528
+ foreach($pharData as $file) {
529
+ $info .= $file . ", ";
530
+ }
531
+ WPShortPixel::log($info);
532
+ }
533
+ $pharData->extractTo($archiveTempDir, null, true);
534
+ WPShortPixel::log("ARCHIVE EXTRACTED " . json_encode(scandir($archiveTempDir)));
535
+ @unlink($archiveFile);
536
+ } catch (Exception $ex) {
537
+ @unlink($archiveFile);
538
+ ShortpixelFolder::deleteFolder($archiveTempDir);
539
+ return array("Status" => self::STATUS_ERROR, "Code" => $ex->getCode(), "Message" => $ex->getMessage());
540
+ }
541
+ return array("Status" => self::STATUS_SUCCESS, "Code" => 2, "Message" => "Success", "Path" => $archiveTempDir);
542
+
543
+ } else {
544
+ WPShortPixel::log("ARCHIVE ERROR (" . $archive->$archiveURL . "): " . json_encode($downloadResult));
545
+ if($first && $downloadResult['Code'] == self::ERR_INCORRECT_FILE_SIZE) {
546
+ WPShortPixel::log("RETRYING AFTER ARCHIVE ERROR");
547
+ return $this->downloadArchive($archive, $compressionType, false); // try again, maybe the archive was flushing...
548
+ }
549
+ @rmdir($archiveTempDir); //in the case it was just created and it's empty...
550
+ return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
551
+ }
552
+ }
553
+ return false;
554
+ }
555
+
556
+ /**
557
+ * handles a successful optimization, setting metadata and handling download for each file in the set
558
+ * @param array $APIresponse - the response from the API - contains the optimized images URLs to download
559
+ * @param array $PATHs - list of local paths for the files
560
+ * @param ShortPixelMetaFacade $itemHandler - the Facade that manages different types of image metadatas: MediaLibrary (postmeta table), ShortPixel custom (shortpixel_meta table)
561
+ * @param int $compressionType - 1 - lossy, 2 - glossy, 0 - lossless
562
+ * @return array status/message
563
+ */
564
+ private function handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType) {
565
+ WPShortPixel::log('Handling Success!');
566
+
567
+ $counter = $savedSpace = $originalSpace = $optimizedSpace /* = $averageCompression */ = 0;
568
+ $NoBackup = true;
569
+
570
+ if($compressionType) {
571
+ $fileType = "LossyURL";
572
+ $fileSize = "LossySize";
573
+ } else {
574
+ $fileType = "LosslessURL";
575
+ $fileSize = "LoselessSize";
576
+ }
577
+ $webpType = "WebP" . $fileType;
578
+
579
+ $archive = /*false &&*/
580
+ ($this->_settings->downloadArchive == 7 && class_exists('PharData') && isset($APIresponse[count($APIresponse) - 1]->ArchiveStatus))
581
+ ? $this->downloadArchive($APIresponse[count($APIresponse) - 1], $compressionType) : false;
582
+ if($archive !== false && $archive['Status'] !== self::STATUS_SUCCESS) {
583
+ return $archive;
584
+ }
585
+
586
+ //download each file from array and process it
587
+ foreach ( $APIresponse as $fileData )
588
+ {
589
+ if(!isset($fileData->Status)) continue; //if optimized images archive is activated, last entry of APIResponse if the Archive data.
590
+
591
+ if ( $fileData->Status->Code == 2 ) //file was processed OK
592
+ {
593
+ if ( $counter == 0 ) { //save percent improvement for main file
594
+ $percentImprovement = $fileData->PercentImprovement;
595
+ } else { //count thumbnails only
596
+ $this->_settings->thumbsCount = $this->_settings->thumbsCount + 1;
597
+ }
598
+ //TODO la sfarsit sa faca fallback la handleDownload
599
+ if($archive) {
600
+ $downloadResult = $this->fromArchive($archive['Path'], $fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize, isset($fileData->$webpType) ? $fileData->$webpType : 'NA');
601
+ } else {
602
+ $downloadResult = $this->handleDownload($fileData->$fileType, $fileData->$fileSize, $fileData->OriginalSize, isset($fileData->$webpType) ? $fileData->$webpType : 'NA');
603
+ }
604
+
605
+ $tempFiles[$counter] = $downloadResult;
606
+ if ( $downloadResult['Status'] == self::STATUS_SUCCESS ) {
607
+ //nothing to do
608
+ }
609
+ //when the status is STATUS_UNCHANGED we just skip the array line for that one
610
+ elseif( $downloadResult['Status'] == self::STATUS_UNCHANGED ) {
611
+ //this image is unchanged so won't be copied below, only the optimization stats need to be computed
612
+ $originalSpace += $fileData->OriginalSize;
613
+ $optimizedSpace += $fileData->$fileSize;
614
+ }
615
+ else {
616
+ self::cleanupTemporaryFiles($archive, $tempFiles);
617
+ return array("Status" => $downloadResult['Status'], "Code" => $downloadResult['Code'], "Message" => $downloadResult['Message']);
618
+ }
619
+
620
+ }
621
+ else { //there was an error while trying to download a file
622
+ $tempFiles[$counter] = "";
623
+ }
624
+ $counter++;
625
+ }
626
+
627
+ //figure out in what SubDir files should land
628
+ $mainPath = $itemHandler->getMeta()->getPath();
629
+
630
+ //if backup is enabled - we try to save the images
631
+ if( $this->_settings->backupImages )
632
+ {
633
+ $backupStatus = self::backupImage($mainPath, $PATHs);
634
+ if($backupStatus == self::STATUS_FAIL) {
635
+ $itemHandler->incrementRetries(1, self::ERR_SAVE_BKP, $backupStatus["Message"]);
636
+ self::cleanupTemporaryFiles($archive, empty($tempFiles) ? array() : $tempFiles);
637
+ return array("Status" => self::STATUS_FAIL, "Code" =>"backup-fail", "Message" => "Failed to back the image up.");
638
+ }
639
+ $NoBackup = false;
640
+ }//end backup section
641
+
642
+ $writeFailed = 0;
643
+ $width = $height = null;
644
+ $resize = $this->_settings->resizeImages;
645
+ $retinas = 0;
646
+ $thumbsOpt = 0;
647
+ $thumbsOptList = array();
648
+
649
+ if ( !empty($tempFiles) )
650
+ {
651
+ //overwrite the original files with the optimized ones
652
+ foreach ( $tempFiles as $tempFileID => $tempFile )
653
+ {
654
+ if(!is_array($tempFile)) continue;
655
+
656
+ $targetFile = $PATHs[$tempFileID];
657
+ $isRetina = ShortPixelMetaFacade::isRetina($targetFile);
658
+
659
+ if( ($tempFile['Status'] == self::STATUS_UNCHANGED || $tempFile['Status'] == self::STATUS_SUCCESS) && !$isRetina
660
+ && $targetFile !== $mainPath) {
661
+ $thumbsOpt++;
662
+ $thumbsOptList[] = self::MB_basename($targetFile);
663
+ }
664
+
665
+ if($tempFile['Status'] == self::STATUS_SUCCESS) { //if it's unchanged it will still be in the array but only for WebP (handled below)
666
+ $tempFilePATH = $tempFile["Message"];
667
+ if ( file_exists($tempFilePATH) && file_exists($targetFile) && is_writable($targetFile) ) {
668
+ copy($tempFilePATH, $targetFile);
669
+ if(ShortPixelMetaFacade::isRetina($targetFile)) {
670
+ $retinas ++;
671
+ }
672
+ if($resize && $itemHandler->getMeta()->getPath() == $targetFile) { //this is the main image
673
+ $size = getimagesize($PATHs[$tempFileID]);
674
+ $width = $size[0];
675
+ $height = $size[1];
676
+ }
677
+ //Calculate the saved space
678
+ $fileData = $APIresponse[$tempFileID];
679
+ $savedSpace += $fileData->OriginalSize - $fileData->$fileSize;
680
+ $originalSpace += $fileData->OriginalSize;
681
+ $optimizedSpace += $fileData->$fileSize;
682
+ //$averageCompression += $fileData->PercentImprovement;
683
+ WPShortPixel::log("HANDLE SUCCESS: Image " . $PATHs[$tempFileID] . " original size: ".$fileData->OriginalSize . " optimized: " . $fileData->$fileSize);
684
+
685
+ //add the number of files with < 5% optimization
686
+ if ( ( ( 1 - $APIresponse[$tempFileID]->$fileSize/$APIresponse[$tempFileID]->OriginalSize ) * 100 ) < 5 ) {
687
+ $this->_settings->under5Percent++;
688
+ }
689
+ }
690
+ else {
691
+ if($archive && SHORTPIXEL_DEBUG === true) {
692
+ if(!file_exists($tempFilePATH)) {
693
+ WPShortPixel::log("MISSING FROM ARCHIVE. tempFilePath: $tempFilePATH with ID: $tempFileID");
694
+ } elseif(!file_exists($targetFile)){
695
+ WPShortPixel::log("MISSING TARGET: $targetFile");
696
+ } elseif(!is_writable($targetFile)){
697
+ WPShortPixel::log("TARGET NOT WRITABLE: $targetFile");
698
+ }
699
+ }
700
+ $writeFailed++;
701
+ }
702
+ @unlink($tempFilePATH);
703
+ }
704
+
705
+ $tempWebpFilePATH = $tempFile["WebP"];
706
+ if(file_exists($tempWebpFilePATH)) {
707
+ $targetWebPFileCompat = dirname($targetFile) . '/'. self::MB_basename($targetFile, '.' . pathinfo($targetFile, PATHINFO_EXTENSION)) . ".webp";
708
+ $targetWebPFile = dirname($targetFile) . '/' . self::MB_basename($targetFile) . ".webp";
709
+ //if the WebP fileCompat already exists, it means that there is another file with the same basename but different extension which has its .webP counterpart
710
+ //save it with double extension
711
+ if(file_exists($targetWebPFileCompat)) {
712
+ copy($targetWebPFile,$targetWebPFile);
713
+ } else {
714
+ copy($tempWebpFilePATH, $targetWebPFileCompat);
715
+ }
716
+ @unlink($tempWebpFilePATH);
717
+ }
718
+ }
719
+ self::cleanupTemporaryFiles($archive, $tempFiles);
720
+
721
+ if ( $writeFailed > 0 )//there was an error
722
+ {
723
+ if($archive && SHORTPIXEL_DEBUG === true) {
724
+ WPShortPixel::log("ARCHIVE HAS MISSING FILES. EXPECTED: " . json_encode($PATHs)
725
+ . " AND: " . json_encode($APIresponse)
726
+ . " GOT ARCHIVE: " . $APIresponse[count($APIresponse) - 1]->ArchiveURL . " LOSSLESS: " . $APIresponse[count($APIresponse) - 1]->ArchiveLosslessURL
727
+ . " CONTAINING: " . json_encode(scandir($archive['Path'])));
728
+ }
729
+ $msg = sprintf(__('Optimized version of %s file(s) couldn\'t be updated.','shortpixel-image-optimiser'),$writeFailed);
730
+ $itemHandler->incrementRetries(1, self::ERR_SAVE, $msg);
731
+ $this->_settings->bulkProcessingStatus = "error";
732
+ return array("Status" => self::STATUS_FAIL, "Code" =>"write-fail", "Message" => $msg);
733
+ }
734
+ } elseif( 0 + $fileData->PercentImprovement < 5) {
735
+ $this->_settings->under5Percent++;
736
+ }
737
+ //old average counting
738
+ $this->_settings->savedSpace += $savedSpace;
739
+ //$averageCompression = $this->_settings->averageCompression * $this->_settings->fileCount / ($this->_settings->fileCount + count($APIresponse));
740
+ //$this->_settings->averageCompression = $averageCompression;
741
+ $this->_settings->fileCount += count($APIresponse);
742
+ //new average counting
743
+ $this->_settings->totalOriginal += $originalSpace;
744
+ $this->_settings->totalOptimized += $optimizedSpace;
745
+
746
+ //update metadata for this file
747
+ $meta = $itemHandler->getMeta();
748
+ // die(var_dump($percentImprovement));
749
+ if($meta->getThumbsTodo()) {
750
+ $percentImprovement = $meta->getImprovementPercent();
751
+ }
752
+ $png2jpg = $meta->getPng2Jpg();
753
+ $png2jpg = is_array($png2jpg) ? $png2jpg['optimizationPercent'] : 0;
754
+ $meta->setMessage($originalSpace
755
+ ? number_format(100.0 * (1.0 - $optimizedSpace / $originalSpace), 2)
756
+ : "Couldn't compute thumbs optimization percent. Main image: " . $percentImprovement);
757
+ WPShortPixel::log("HANDLE SUCCESS: Image optimization: ".$meta->getMessage());
758
+ $meta->setCompressionType($compressionType);
759
+ $meta->setCompressedSize(@filesize($meta->getPath()));
760
+ $meta->setKeepExif($this->_settings->keepExif);
761
+ $meta->setTsOptimized(date("Y-m-d H:i:s"));
762
+ $meta->setThumbsOptList(is_array($meta->getThumbsOptList()) ? array_unique(array_merge($meta->getThumbsOptList(), $thumbsOptList)) : $thumbsOptList);
763
+ $meta->setThumbsOpt(($meta->getThumbsTodo() || $this->_settings->processThumbnails) ? count($meta->getThumbsOptList()) : 0);
764
+ $meta->setRetinasOpt($retinas);
765
+ if(null !== $this->_settings->excludeSizes) {
766
+ $meta->setExcludeSizes($this->_settings->excludeSizes);
767
+ }
768
+ $meta->setThumbsTodo(false);
769
+ //* Not yet as it doesn't seem to work... */$meta->addThumbs($webpSizes);
770
+ if($width && $height) {
771
+ $meta->setActualWidth($width);
772
+ $meta->setActualHeight($height);
773
+ }
774
+ $meta->setRetries($meta->getRetries() + 1);
775
+ $meta->setBackup(!$NoBackup);
776
+ $meta->setStatus(2);
777
+
778
+ $itemHandler->updateMeta($meta);
779
+ $itemHandler->optimizationSucceeded();
780
+ WPShortPixel::log("HANDLE SUCCESS: Metadata saved.");
781
+
782
+ if(!$originalSpace) { //das kann nicht sein, alles klar?!
783
+ throw new Exception("OriginalSpace = 0. APIResponse" . json_encode($APIresponse));
784
+ }
785
+
786
+ //we reset the retry counter in case of success
787
+ $this->_settings->apiRetries = 0;
788
+
789
+ return array("Status" => self::STATUS_SUCCESS, "Message" => 'Success: No pixels remained unsqueezed :-)',
790
+ "PercentImprovement" => $originalSpace
791
+ ? number_format(100.0 * (1.0 - (1.0 - $png2jpg / 100.0) * $optimizedSpace / $originalSpace), 2)
792
+ : "Couldn't compute thumbs optimization percent. Main image: " . $percentImprovement);
793
+ }//end handleSuccess
794
+
795
+ /**
796
+ * @param $archive
797
+ * @param $tempFiles
798
+ */
799
+ protected static function cleanupTemporaryFiles($archive, $tempFiles)
800
+ {
801
+ if ($archive) {
802
+ ShortpixelFolder::deleteFolder($archive['Path']);
803
+ } else {
804
+ if (!empty($tempFiles) && is_array($tempFiles)) {
805
+ foreach ($tempFiles as $tmpFile) {
806
+ @unlink($tmpFile["Message"]);
807
+ }
808
+ }
809
+ }
810
+ }
811
+
812
+ /**
813
+ * a basename alternative that deals OK with multibyte charsets (e.g. Arabic)
814
+ * @param string $Path
815
+ * @return string
816
+ */
817
+ static public function MB_basename($Path, $suffix = false){
818
+ $Separator = " qq ";
819
+ $qqPath = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $Path);
820
+ if(!$qqPath) { //this is not an UTF8 string!! Don't rely on basename either, since if filename starts with a non-ASCII character it strips it off
821
+ $fileName = end(explode(DIRECTORY_SEPARATOR, $Path));
822
+ $pos = strpos($fileName, $suffix);
823
+ if($pos !== false) {
824
+ return substr($fileName, 0, $pos);
825
+ }
826
+ return $fileName;
827
+ }
828
+ $suffix = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $suffix);
829
+ $Base = basename($qqPath, $suffix);
830
+ $Base = str_replace($Separator, "", $Base);
831
+ return $Base;
832
+ }
833
+
834
+ /**
835
+ * sometimes, the paths to the files as defined in metadata are wrong, we try to automatically correct them
836
+ * @param array $PATHs
837
+ * @return boolean|string
838
+ */
839
+ static public function CheckAndFixImagePaths($PATHs){
840
+
841
+ $ErrorCount = 0;
842
+ $Tmp = explode("/", SHORTPIXEL_UPLOADS_BASE);
843
+ $TmpCount = count($Tmp);
844
+ $StichString = $Tmp[$TmpCount-2] . "/" . $Tmp[$TmpCount-1];
845
+ //files exist on disk?
846
+ $missingFiles = array();
847
+ foreach ( $PATHs as $Id => $File )
848
+ {
849
+ //we try again with a different path
850
+ if ( !file_exists($File) ){
851
+ //$NewFile = $uploadDir['basedir'] . "/" . substr($File,strpos($File, $StichString));//+strlen($StichString));
852
+ $NewFile = SHORTPIXEL_UPLOADS_BASE . substr($File,strpos($File, $StichString)+strlen($StichString));
853
+ if (file_exists($NewFile)) {
854
+ $PATHs[$Id] = $NewFile;
855
+ } else {
856
+ $NewFile = SHORTPIXEL_UPLOADS_BASE . "/" . $File;
857
+ if (file_exists($NewFile)) {
858
+ $PATHs[$Id] = $NewFile;
859
+ } else {
860
+ $missingFiles[] = $File;
861
+ $ErrorCount++;
862
+ }
863
+ }
864
+ }
865
+ }
866
+
867
+ if ( $ErrorCount > 0 ) {
868
+ return array("error" => $missingFiles);//false;
869
+ } else {
870
+ return $PATHs;
871
+ }
872
+ }
873
+
874
+ static public function getCompressionTypeName($compressionType) {
875
+ if(is_array($compressionType)) {
876
+ return array_map(array('ShortPixelAPI', 'getCompressionTypeName'), $compressionType);
877
+ }
878
+ return 0 + $compressionType == 2 ? 'glossy' : (0 + $compressionType == 1 ? 'lossy' : 'lossless');
879
+ }
880
+
881
+ static public function getCompressionTypeCode($compressionName) {
882
+ return $compressionName == 'glossy' ? 2 : ($compressionName == 'lossy' ? 1 : 0);
883
+ }
884
+ }
wp-shortpixel-req.php CHANGED
@@ -29,8 +29,11 @@ require_once('class/view/shortpixel_view.php');
29
 
30
  require_once('class/shortpixel-tools.php');
31
 
 
 
 
32
  require_once( ABSPATH . 'wp-admin/includes/image.php' );
33
- include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
34
 
35
  // for retro compatibility with WP < 3.5
36
  if( !function_exists('wp_normalize_path') ){
@@ -41,15 +44,13 @@ if( !function_exists('wp_normalize_path') ){
41
  $path = ucfirst( $path );
42
  }
43
  return $path;
44
- }
45
  }
46
 
47
  /*
48
  if ( !is_plugin_active( 'wpmandrill/wpmandrill.php' ) //avoid conflicts with some plugins
49
- && !is_plugin_active( 'wp-ses/wp-ses.php' )
50
  && !is_plugin_active( 'wordfence/wordfence.php') ) {
51
  require_once( ABSPATH . 'wp-includes/pluggable.php' );
52
- }
53
  */
54
-
55
-
29
 
30
  require_once('class/shortpixel-tools.php');
31
 
32
+ require_once('class/controller/controller.php');
33
+ require_once('class/controller/bulk-restore-all.php');
34
+
35
  require_once( ABSPATH . 'wp-admin/includes/image.php' );
36
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
37
 
38
  // for retro compatibility with WP < 3.5
39
  if( !function_exists('wp_normalize_path') ){
44
  $path = ucfirst( $path );
45
  }
46
  return $path;
47
+ }
48
  }
49
 
50
  /*
51
  if ( !is_plugin_active( 'wpmandrill/wpmandrill.php' ) //avoid conflicts with some plugins
52
+ && !is_plugin_active( 'wp-ses/wp-ses.php' )
53
  && !is_plugin_active( 'wordfence/wordfence.php') ) {
54
  require_once( ABSPATH . 'wp-includes/pluggable.php' );
55
+ }
56
  */
 
 
wp-shortpixel.php CHANGED
@@ -1,238 +1,245 @@
1
- <?php
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.8
7
- * Author: ShortPixel
8
- * Author URI: https://shortpixel.com
9
- * Text Domain: shortpixel-image-optimiser
10
- * Domain Path: /lang
11
- */
12
-
13
- define('SHORTPIXEL_RESET_ON_ACTIVATE', false); //if true TODO set false
14
- //define('SHORTPIXEL_DEBUG', true);
15
- //define('SHORTPIXEL_DEBUG_TARGET', true);
16
-
17
- define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
18
-
19
- //define('SHORTPIXEL_AFFILIATE_CODE', '');
20
-
21
- define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.12.8");
22
- define('SHORTPIXEL_MAX_TIMEOUT', 10);
23
- define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
24
- define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
25
- define('SHORTPIXEL_MAX_API_RETRIES', 50);
26
- define('SHORTPIXEL_MAX_ERR_RETRIES', 5);
27
- define('SHORTPIXEL_MAX_FAIL_RETRIES', 3);
28
- if(!defined('SHORTPIXEL_MAX_THUMBS')) { //can be defined in wp-config.php
29
- define('SHORTPIXEL_MAX_THUMBS', 149);
30
- }
31
-
32
- define('SHORTPIXEL_PRESEND_ITEMS', 3);
33
- define('SHORTPIXEL_API', 'api.shortpixel.com');
34
-
35
- define('SHORTPIXEL_MAX_EXECUTION_TIME', ini_get('max_execution_time'));
36
-
37
- require_once(ABSPATH . 'wp-admin/includes/file.php');
38
-
39
- $sp__uploads = wp_upload_dir();
40
- define('SHORTPIXEL_UPLOADS_BASE', (file_exists($sp__uploads['basedir']) ? '' : ABSPATH) . $sp__uploads['basedir'] );
41
- //define('SHORTPIXEL_UPLOADS_URL', is_main_site() ? $sp__uploads['baseurl'] : dirname(dirname($sp__uploads['baseurl'])));
42
- define('SHORTPIXEL_UPLOADS_NAME', basename(is_main_site() ? SHORTPIXEL_UPLOADS_BASE : dirname(dirname(SHORTPIXEL_UPLOADS_BASE))));
43
- $sp__backupBase = is_main_site() ? SHORTPIXEL_UPLOADS_BASE : dirname(dirname(SHORTPIXEL_UPLOADS_BASE));
44
- define('SHORTPIXEL_BACKUP_FOLDER', $sp__backupBase . '/' . SHORTPIXEL_BACKUP);
45
- define('SHORTPIXEL_BACKUP_URL',
46
- ((is_main_site() || (defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL))
47
- ? $sp__uploads['baseurl']
48
- : dirname(dirname($sp__uploads['baseurl'])))
49
- . '/' . SHORTPIXEL_BACKUP);
50
-
51
- /*
52
- if ( is_numeric(SHORTPIXEL_MAX_EXECUTION_TIME) && SHORTPIXEL_MAX_EXECUTION_TIME > 10 )
53
- define('SHORTPIXEL_MAX_EXECUTION_TIME', SHORTPIXEL_MAX_EXECUTION_TIME - 5 ); //in seconds
54
- else
55
- define('SHORTPIXEL_MAX_EXECUTION_TIME', 25 );
56
- */
57
-
58
- define('SHORTPIXEL_MAX_EXECUTION_TIME2', 2 );
59
- define("SHORTPIXEL_MAX_RESULTS_QUERY", 30);
60
-
61
- function shortpixelInit() {
62
- global $shortPixelPluginInstance;
63
- //limit to certain admin pages if function available
64
- $loadOnThisPage = !function_exists('get_current_screen');
65
- if(!$loadOnThisPage) {
66
- $screen = get_current_screen();
67
- if(is_object($screen) && !in_array($screen->id, array('upload', 'edit', 'edit-tags', 'post-new', 'post'))) {
68
- return;
69
- }
70
- }
71
- require_once('class/shortpixel_queue.php');
72
- $prio = ShortPixelQueue::get();
73
- $isAjaxButNotSP = defined( 'DOING_AJAX' ) && DOING_AJAX && !(isset($_REQUEST['action']) && (strpos($_REQUEST['action'], 'shortpixel_') === 0));
74
- if (!isset($shortPixelPluginInstance)
75
- && ( ($prio && is_array($prio) && count($prio) && get_option('wp-short-pixel-front-bootstrap'))
76
- || is_admin() && !$isAjaxButNotSP
77
- && (function_exists("is_user_logged_in") && is_user_logged_in()) //is admin, is logged in - :) seems funny but it's not, ajax scripts are admin even if no admin is logged in.
78
- && ( current_user_can( 'manage_options' )
79
- || current_user_can( 'upload_files' )
80
- || current_user_can( 'edit_posts' )
81
- )
82
- )
83
- )
84
- {
85
- require_once('wp-shortpixel-req.php');
86
- $shortPixelPluginInstance = new WPShortPixel;
87
- }
88
- }
89
-
90
- /**
91
- * this is hooked into wp_generate_attachment_metadata
92
- * @param $meta
93
- * @param null $ID
94
- * @return WPShortPixel the instance
95
- */
96
- function shortPixelHandleImageUploadHook($meta, $ID = null) {
97
- global $shortPixelPluginInstance;
98
- if(!isset($shortPixelPluginInstance)) {
99
- require_once('wp-shortpixel-req.php');
100
- $shortPixelPluginInstance = new WPShortPixel;
101
- }
102
- return $shortPixelPluginInstance->handleMediaLibraryImageUpload($meta, $ID);
103
- }
104
-
105
- function shortPixelReplaceHook($params) {
106
- if(isset($params['post_id'])) { //integration with EnableMediaReplace - that's an upload for replacing an existing ID
107
- global $shortPixelPluginInstance;
108
- if (!isset($shortPixelPluginInstance)) {
109
- require_once('wp-shortpixel-req.php');
110
- $shortPixelPluginInstance = new WPShortPixel;
111
- }
112
- $itemHandler = $shortPixelPluginInstance->onDeleteImage($params['post_id']);
113
- $itemHandler->deleteAllSPMeta();
114
- }
115
- }
116
-
117
- function shortPixelPng2JpgHook($params) {
118
- global $shortPixelPluginInstance;
119
- if(!isset($shortPixelPluginInstance)) {
120
- require_once('wp-shortpixel-req.php');
121
- $shortPixelPluginInstance = new WPShortPixel;
122
- }
123
- return $shortPixelPluginInstance->convertPng2Jpg($params);
124
- }
125
-
126
- function shortPixelNggAdd($image) {
127
- global $shortPixelPluginInstance;
128
- if(!isset($shortPixelPluginInstance)) {
129
- require_once('wp-shortpixel-req.php');
130
- $shortPixelPluginInstance = new WPShortPixel;
131
- }
132
- $shortPixelPluginInstance->handleNextGenImageUpload($image);
133
- }
134
-
135
- function shortPixelActivatePlugin () {
136
- require_once('wp-shortpixel-req.php');
137
- WPShortPixel::shortPixelActivatePlugin();
138
- }
139
-
140
- function shortPixelDeactivatePlugin () {
141
- require_once('wp-shortpixel-req.php');
142
- WPShortPixel::shortPixelDeactivatePlugin();
143
- }
144
-
145
- function shortPixelUninstallPlugin () {
146
- require_once('wp-shortpixel-req.php');
147
- WPShortPixel::shortPixelUninstallPlugin();
148
- }
149
-
150
- //Picture generation, hooked on the_content filter
151
- function shortPixelConvertImgToPictureAddWebp($content) {
152
- if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
153
- //for AMP pages the <picture> tag is not allowed
154
- return $content;
155
- }
156
- require_once('class/front/img-to-picture-webp.php');
157
- return ShortPixelImgToPictureWebp::convert($content);// . "<!-- PICTURE TAGS BY SHORTPIXEL -->";
158
- }
159
- function shortPixelAddPictureJs() {
160
- // Don't do anything with the RSS feed.
161
- if ( is_feed() || is_admin() ) { return; }
162
-
163
- echo '<script>'
164
- . 'var spPicTest = document.createElement( "picture" );'
165
- . 'if(!window.HTMLPictureElement && document.addEventListener) {'
166
- . 'window.addEventListener("DOMContentLoaded", function() {'
167
- . 'var scriptTag = document.createElement("script");'
168
- . 'scriptTag.src = "' . plugins_url('/res/js/picturefill.min.js', __FILE__) . '";'
169
- . 'document.body.appendChild(scriptTag);'
170
- . '});'
171
- . '}'
172
- . '</script>';
173
- }
174
-
175
- add_filter( 'gform_save_field_value', 'shortPixelGravityForms', 10, 5 );
176
-
177
- function shortPixelGravityForms( $value, $lead, $field, $form ) {
178
- global $shortPixelPluginInstance;
179
- if($field->type == 'post_image') {
180
- require_once('wp-shortpixel-req.php');
181
- $shortPixelPluginInstance = new WPShortPixel;
182
- $shortPixelPluginInstance->handleGravityFormsImageField($value);
183
- }
184
- return $value;
185
- }
186
-
187
- function shortPixelInitOB() {
188
- if(!is_admin() || (function_exists("wp_doing_ajax") && wp_doing_ajax()) || (defined( 'DOING_AJAX' ) && DOING_AJAX)) {
189
- ob_start('shortPixelConvertImgToPictureAddWebp');
190
- }
191
- }
192
-
193
- function shortPixelIsPluginActive($plugin) {
194
- $activePlugins = apply_filters( 'active_plugins', get_option( 'active_plugins', array()));
195
- if ( is_multisite() ) {
196
- $activePlugins = array_merge($activePlugins, get_site_option( 'active_sitewide_plugins'));
197
- }
198
- return in_array( $plugin, $activePlugins);
199
- }
200
-
201
- $option = get_option('wp-short-pixel-create-webp-markup');
202
- if ( $option ) {
203
- if(shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {
204
- set_transient("shortpixel_thrown_notice", array('when' => 'spai', 'extra' => __('Please deactivate the ShortPixel Image Optimizer\'s
205
- <a href="options-general.php?page=wp-shortpixel#adv-settings">Deliver WebP using PICTURE tag</a>
206
- option when the ShortPixel Adaptive Images plugin is active.','shortpixel-image-optimiser')), 1800);
207
- }
208
- elseif( $option == 1 ){
209
- add_action( 'wp_head', 'shortPixelAddPictureJs');
210
- add_action( 'init', 'shortPixelInitOB', 1 );
211
- } elseif ($option == 2){
212
- add_filter( 'the_content', 'shortPixelConvertImgToPictureAddWebp', 10000 ); // priority big, so it will be executed last
213
- add_filter( 'the_excerpt', 'shortPixelConvertImgToPictureAddWebp', 10000 );
214
- add_filter( 'post_thumbnail_html', 'shortPixelConvertImgToPictureAddWebp');
215
- }
216
- // add_action( 'wp_enqueue_scripts', 'spAddPicturefillJs' );
217
- }
218
-
219
- if ( !function_exists( 'vc_action' ) || vc_action() !== 'vc_inline' ) { //handle incompatibility with Visual Composer
220
- add_action( 'init', 'shortpixelInit');
221
- add_action('ngg_added_new_image', 'shortPixelNggAdd');
222
-
223
- $autoPng2Jpg = get_option('wp-short-pixel-png2jpg');
224
- $autoMediaLibrary = get_option('wp-short-pixel-auto-media-library');
225
- if($autoPng2Jpg && $autoMediaLibrary) {
226
- add_action( 'wp_handle_upload', 'shortPixelPng2JpgHook');
227
- add_action( 'mpp_handle_upload', 'shortPixelPng2JpgHook');
228
- }
229
- add_action('wp_handle_replace', 'shortPixelReplaceHook');
230
- if($autoMediaLibrary) {
231
- add_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
232
- add_filter( 'mpp_generate_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
233
- }
234
-
235
- register_activation_hook( __FILE__, 'shortPixelActivatePlugin' );
236
- register_deactivation_hook( __FILE__, 'shortPixelDeactivatePlugin' );
237
- register_uninstall_hook(__FILE__, 'shortPixelUninstallPlugin');}
238
- ?>
 
 
 
 
 
 
 
1
+ <?php
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.13.0
7
+ * Author: ShortPixel
8
+ * Author URI: https://shortpixel.com
9
+ * Text Domain: shortpixel-image-optimiser
10
+ * Domain Path: /lang
11
+ */
12
+
13
+ define('SHORTPIXEL_RESET_ON_ACTIVATE', false); //if true TODO set false
14
+ //define('SHORTPIXEL_DEBUG', true);
15
+ //define('SHORTPIXEL_DEBUG_TARGET', true);
16
+
17
+ define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
18
+
19
+ //define('SHORTPIXEL_AFFILIATE_CODE', '');
20
+
21
+ define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.13.0");
22
+ define('SHORTPIXEL_MAX_TIMEOUT', 10);
23
+ define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
24
+ define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
25
+ define('SHORTPIXEL_MAX_API_RETRIES', 50);
26
+ define('SHORTPIXEL_MAX_ERR_RETRIES', 5);
27
+ define('SHORTPIXEL_MAX_FAIL_RETRIES', 3);
28
+ if(!defined('SHORTPIXEL_MAX_THUMBS')) { //can be defined in wp-config.php
29
+ define('SHORTPIXEL_MAX_THUMBS', 149);
30
+ }
31
+
32
+ define('SHORTPIXEL_PRESEND_ITEMS', 3);
33
+ define('SHORTPIXEL_API', 'api.shortpixel.com');
34
+
35
+ define('SHORTPIXEL_MAX_EXECUTION_TIME', ini_get('max_execution_time'));
36
+
37
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
38
+
39
+ $sp__uploads = wp_upload_dir();
40
+ define('SHORTPIXEL_UPLOADS_BASE', (file_exists($sp__uploads['basedir']) ? '' : ABSPATH) . $sp__uploads['basedir'] );
41
+ //define('SHORTPIXEL_UPLOADS_URL', is_main_site() ? $sp__uploads['baseurl'] : dirname(dirname($sp__uploads['baseurl'])));
42
+ define('SHORTPIXEL_UPLOADS_NAME', basename(is_main_site() ? SHORTPIXEL_UPLOADS_BASE : dirname(dirname(SHORTPIXEL_UPLOADS_BASE))));
43
+ $sp__backupBase = is_main_site() ? SHORTPIXEL_UPLOADS_BASE : dirname(dirname(SHORTPIXEL_UPLOADS_BASE));
44
+ define('SHORTPIXEL_BACKUP_FOLDER', $sp__backupBase . '/' . SHORTPIXEL_BACKUP);
45
+ define('SHORTPIXEL_BACKUP_URL',
46
+ ((is_main_site() || (defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL))
47
+ ? $sp__uploads['baseurl']
48
+ : dirname(dirname($sp__uploads['baseurl'])))
49
+ . '/' . SHORTPIXEL_BACKUP);
50
+
51
+ /*
52
+ if ( is_numeric(SHORTPIXEL_MAX_EXECUTION_TIME) && SHORTPIXEL_MAX_EXECUTION_TIME > 10 )
53
+ define('SHORTPIXEL_MAX_EXECUTION_TIME', SHORTPIXEL_MAX_EXECUTION_TIME - 5 ); //in seconds
54
+ else
55
+ define('SHORTPIXEL_MAX_EXECUTION_TIME', 25 );
56
+ */
57
+
58
+ define('SHORTPIXEL_MAX_EXECUTION_TIME2', 2 );
59
+ define("SHORTPIXEL_MAX_RESULTS_QUERY", 30);
60
+
61
+ function shortpixelInit() {
62
+ global $shortPixelPluginInstance;
63
+ //limit to certain admin pages if function available
64
+ $loadOnThisPage = !function_exists('get_current_screen');
65
+ if(!$loadOnThisPage) {
66
+ $screen = get_current_screen();
67
+ if(is_object($screen) && !in_array($screen->id, array('upload', 'edit', 'edit-tags', 'post-new', 'post'))) {
68
+ return;
69
+ }
70
+ }
71
+ $isAjaxButNotSP = false; //defined( 'DOING_AJAX' ) && DOING_AJAX && !(isset($_REQUEST['action']) && (strpos($_REQUEST['action'], 'shortpixel_') === 0));
72
+ if (!isset($shortPixelPluginInstance)
73
+ && ( (shortPixelCheckQueue() && get_option('wp-short-pixel-front-bootstrap'))
74
+ || is_admin() && !$isAjaxButNotSP
75
+ && (function_exists("is_user_logged_in") && is_user_logged_in()) //is admin, is logged in - :) seems funny but it's not, ajax scripts are admin even if no admin is logged in.
76
+ && ( current_user_can( 'manage_options' )
77
+ || current_user_can( 'upload_files' )
78
+ || current_user_can( 'edit_posts' )
79
+ )
80
+ )
81
+ )
82
+ {
83
+ require_once('wp-shortpixel-req.php');
84
+ $shortPixelPluginInstance = new WPShortPixel;
85
+ }
86
+
87
+ }
88
+
89
+ function shortPixelCheckQueue(){
90
+ require_once('class/shortpixel_queue.php');
91
+ $prio = ShortPixelQueue::get();
92
+ return $prio && is_array($prio) && count($prio);
93
+ }
94
+
95
+ /**
96
+ * this is hooked into wp_generate_attachment_metadata
97
+ * @param $meta
98
+ * @param null $ID
99
+ * @return WPShortPixel the instance
100
+ */
101
+ function shortPixelHandleImageUploadHook($meta, $ID = null) {
102
+ global $shortPixelPluginInstance;
103
+ if(!isset($shortPixelPluginInstance)) {
104
+ require_once('wp-shortpixel-req.php');
105
+ $shortPixelPluginInstance = new WPShortPixel;
106
+ }
107
+ return $shortPixelPluginInstance->handleMediaLibraryImageUpload($meta, $ID);
108
+ }
109
+
110
+ function shortPixelReplaceHook($params) {
111
+ if(isset($params['post_id'])) { //integration with EnableMediaReplace - that's an upload for replacing an existing ID
112
+ global $shortPixelPluginInstance;
113
+ if (!isset($shortPixelPluginInstance)) {
114
+ require_once('wp-shortpixel-req.php');
115
+ $shortPixelPluginInstance = new WPShortPixel;
116
+ }
117
+ $itemHandler = $shortPixelPluginInstance->onDeleteImage($params['post_id']);
118
+ $itemHandler->deleteAllSPMeta();
119
+ }
120
+ }
121
+
122
+ function shortPixelPng2JpgHook($params) {
123
+ global $shortPixelPluginInstance;
124
+ if(!isset($shortPixelPluginInstance)) {
125
+ require_once('wp-shortpixel-req.php');
126
+ $shortPixelPluginInstance = new WPShortPixel;
127
+ }
128
+ return $shortPixelPluginInstance->convertPng2Jpg($params);
129
+ }
130
+
131
+ function shortPixelNggAdd($image) {
132
+ global $shortPixelPluginInstance;
133
+ if(!isset($shortPixelPluginInstance)) {
134
+ require_once('wp-shortpixel-req.php');
135
+ $shortPixelPluginInstance = new WPShortPixel;
136
+ }
137
+ $shortPixelPluginInstance->handleNextGenImageUpload($image);
138
+ }
139
+
140
+ function shortPixelActivatePlugin () {
141
+ require_once('wp-shortpixel-req.php');
142
+ WPShortPixel::shortPixelActivatePlugin();
143
+ }
144
+
145
+ function shortPixelDeactivatePlugin () {
146
+ require_once('wp-shortpixel-req.php');
147
+ WPShortPixel::shortPixelDeactivatePlugin();
148
+ }
149
+
150
+ function shortPixelUninstallPlugin () {
151
+ require_once('wp-shortpixel-req.php');
152
+ WPShortPixel::shortPixelUninstallPlugin();
153
+ }
154
+
155
+ //Picture generation, hooked on the_content filter
156
+ function shortPixelConvertImgToPictureAddWebp($content) {
157
+ if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
158
+ //for AMP pages the <picture> tag is not allowed
159
+ return $content . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG is AMP -->' : '');
160
+ }
161
+ require_once('class/front/img-to-picture-webp.php');
162
+ return ShortPixelImgToPictureWebp::convert($content);// . "<!-- PICTURE TAGS BY SHORTPIXEL -->";
163
+ }
164
+ function shortPixelAddPictureJs() {
165
+ // Don't do anything with the RSS feed.
166
+ if ( is_feed() || is_admin() ) { return; }
167
+
168
+ echo '<script>'
169
+ . 'var spPicTest = document.createElement( "picture" );'
170
+ . 'if(!window.HTMLPictureElement && document.addEventListener) {'
171
+ . 'window.addEventListener("DOMContentLoaded", function() {'
172
+ . 'var scriptTag = document.createElement("script");'
173
+ . 'scriptTag.src = "' . plugins_url('/res/js/picturefill.min.js', __FILE__) . '";'
174
+ . 'document.body.appendChild(scriptTag);'
175
+ . '});'
176
+ . '}'
177
+ . '</script>';
178
+ }
179
+
180
+ add_filter( 'gform_save_field_value', 'shortPixelGravityForms', 10, 5 );
181
+
182
+ function shortPixelGravityForms( $value, $lead, $field, $form ) {
183
+ global $shortPixelPluginInstance;
184
+ if($field->type == 'post_image') {
185
+ require_once('wp-shortpixel-req.php');
186
+ $shortPixelPluginInstance = new WPShortPixel;
187
+ $shortPixelPluginInstance->handleGravityFormsImageField($value);
188
+ }
189
+ return $value;
190
+ }
191
+
192
+ function shortPixelInitOB() {
193
+ if(!is_admin() || (function_exists("wp_doing_ajax") && wp_doing_ajax()) || (defined( 'DOING_AJAX' ) && DOING_AJAX)) {
194
+ ob_start('shortPixelConvertImgToPictureAddWebp');
195
+ }
196
+ }
197
+
198
+ function shortPixelIsPluginActive($plugin) {
199
+ $activePlugins = apply_filters( 'active_plugins', get_option( 'active_plugins', array()));
200
+ if ( is_multisite() ) {
201
+ $activePlugins = array_merge($activePlugins, get_site_option( 'active_sitewide_plugins'));
202
+ }
203
+ return in_array( $plugin, $activePlugins);
204
+ }
205
+
206
+ // [BS] Start runtime here
207
+ $option = get_option('wp-short-pixel-create-webp-markup');
208
+ if ( $option ) {
209
+ if(shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {
210
+ set_transient("shortpixel_thrown_notice", array('when' => 'spai', 'extra' => __('Please deactivate the ShortPixel Image Optimizer\'s
211
+ <a href="options-general.php?page=wp-shortpixel#adv-settings">Deliver WebP using PICTURE tag</a>
212
+ option when the ShortPixel Adaptive Images plugin is active.','shortpixel-image-optimiser')), 1800);
213
+ }
214
+ elseif( $option == 1 ){
215
+ add_action( 'wp_head', 'shortPixelAddPictureJs'); // adds polyfill JS to the header
216
+ add_action( 'init', 'shortPixelInitOB', 1 ); // start output buffer to capture content
217
+ } elseif ($option == 2){
218
+ add_filter( 'the_content', 'shortPixelConvertImgToPictureAddWebp', 10000 ); // priority big, so it will be executed last
219
+ add_filter( 'the_excerpt', 'shortPixelConvertImgToPictureAddWebp', 10000 );
220
+ add_filter( 'post_thumbnail_html', 'shortPixelConvertImgToPictureAddWebp');
221
+ }
222
+ // add_action( 'wp_enqueue_scripts', 'spAddPicturefillJs' );
223
+ }
224
+
225
+ if ( !function_exists( 'vc_action' ) || vc_action() !== 'vc_inline' ) { //handle incompatibility with Visual Composer
226
+ add_action( 'init', 'shortpixelInit');
227
+ add_action('ngg_added_new_image', 'shortPixelNggAdd');
228
+
229
+ $autoPng2Jpg = get_option('wp-short-pixel-png2jpg');
230
+ $autoMediaLibrary = get_option('wp-short-pixel-auto-media-library');
231
+ if($autoPng2Jpg && $autoMediaLibrary) {
232
+ add_action( 'wp_handle_upload', 'shortPixelPng2JpgHook');
233
+ add_action( 'mpp_handle_upload', 'shortPixelPng2JpgHook');
234
+ }
235
+ add_action('wp_handle_replace', 'shortPixelReplaceHook');
236
+ if($autoMediaLibrary) {
237
+ add_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
238
+ add_filter( 'mpp_generate_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
239
+ }
240
+
241
+ register_activation_hook( __FILE__, 'shortPixelActivatePlugin' );
242
+ register_deactivation_hook( __FILE__, 'shortPixelDeactivatePlugin' );
243
+ register_uninstall_hook(__FILE__, 'shortPixelUninstallPlugin');
244
+ }
245
+ ?>