ShortPixel Image Optimizer - Version 4.14.0

Version Description

=

Release date: 17th June 2019 * Add new filters proposed by WP Stateless: shortpixel_backup_folder, shortpixel_image_exists, shortpixel_image_urls * Better placement of the elements on the Other Media page * Fix custom bulk for PDFs when the pdf thumbnails are not activated * Fix selecting items from DB twice for bulk in some circumstances * Warn user that converting PNG to JPG while keeping EXIF in options doesn't keep it (no EXIF for PNGs). * When SHORTPIXEL_DEBUG=x get parameter is provided, display a floating link to the shortpixel_log file * Adaptive Max execution time and capped to 90 sec. for the bulk background AJAX calls. (Kinsta has a max_execution_time of 300 sec. in PHP but the HTTP connection is cut after 180 sec.) * Fix custom 404 page for missing images not working when using .htaccess for WebP * Fix WebP picture tag with relative URLs not working in some circumstances * Fix replacing the inside an existing tag with another tag. * Clear SP optimization cache in order to be able to optimize an image which initially had permissions error, after changing the permissions. * Fix being able to list the contents of ShortpixelBackups on some badly configured servers. * Fix error when inputting D'Artagnan in the AUTH pass field of settings.

Download this release

Release Info

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

Code changes from version 4.13.1 to 4.14.0

Files changed (50) hide show
  1. class/controller/bulk-restore-all.php +2 -0
  2. class/controller/controller.php +102 -15
  3. class/controller/debug.php +266 -0
  4. class/controller/notice.php +134 -0
  5. class/controller/settings.php +523 -0
  6. class/db/shortpixel-custom-meta-dao.php +21 -6
  7. class/db/shortpixel-meta-facade.php +30 -8
  8. class/db/wp-shortpixel-media-library-adapter.php +31 -32
  9. class/external/helpscout.php +128 -0
  10. class/external/nextgen.php +62 -0
  11. class/front/img-to-picture-webp.php +101 -13
  12. class/model/notice_model.php +87 -0
  13. class/model/shortpixel-debug.php +140 -0
  14. class/model/shortpixel-folder.php +51 -23
  15. class/model/shortpixel-image.php +32 -0
  16. class/shortpixel-model.php +149 -0
  17. class/shortpixel-tools.php +2 -1
  18. class/shortpixel_queue.php +65 -56
  19. class/view/settings/part-advanced.php +357 -0
  20. class/view/settings/part-cloudflare.php +66 -0
  21. class/view/settings/part-debug.php +40 -0
  22. class/view/settings/part-general.php +186 -0
  23. class/view/settings/part-nokey.php +129 -0
  24. class/view/settings/part-statistics.php +170 -0
  25. class/view/shortpixel-list-table.php +1 -1
  26. class/view/shortpixel_view.php +66 -28
  27. class/view/view-debug-box.php +44 -0
  28. class/view/view-restore-all.php +3 -0
  29. class/view/view-settings.php +59 -0
  30. class/wp-short-pixel.php +481 -141
  31. class/wp-shortpixel-settings.php +61 -26
  32. readme.txt +21 -2
  33. res/css/short-pixel-modal.min.css +1 -1
  34. res/css/short-pixel.css +3 -36
  35. res/css/short-pixel.min.css +1 -1
  36. res/css/shortpixel-admin.css +76 -0
  37. res/css/shortpixel-admin.min.css +1 -1
  38. res/css/sp-file-tree.min.css +1 -1
  39. res/img/notes-sp.png +0 -0
  40. res/img/notes-sp@2x.png +0 -0
  41. res/js/shortpixel.js +72 -25
  42. res/js/shortpixel.min.js +1 -1
  43. res/scss/shortpixel-admin.scss +4 -1
  44. res/scss/utils/_notices.scss +39 -0
  45. res/scss/view/_bulk_dashboard.scss +8 -0
  46. res/scss/view/_settings.scss +85 -0
  47. shortpixel-plugin.php +192 -0
  48. shortpixel_api.php +33 -12
  49. wp-shortpixel-req.php +44 -8
  50. wp-shortpixel.php +9 -5
class/controller/bulk-restore-all.php CHANGED
@@ -64,6 +64,8 @@ class BulkRestoreAll extends ShortPixelController
64
 
65
  public function setupBulk()
66
  {
 
 
67
  // handle the custom folders if there are any.
68
  if (count($this->selected_folders) > 0)
69
  {
64
 
65
  public function setupBulk()
66
  {
67
+ $this->checkPost(); // check if any POST vars are there ( which should be if custom restore is on )
68
+
69
  // handle the custom folders if there are any.
70
  if (count($this->selected_folders) > 0)
71
  {
class/controller/controller.php CHANGED
@@ -1,16 +1,22 @@
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
  {
@@ -20,10 +26,17 @@ class ShortPixelController
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!
@@ -33,9 +46,22 @@ class ShortPixelController
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
  }
@@ -47,37 +73,98 @@ class ShortPixelController
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
- }
1
  <?php
 
2
  namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger as Log;
4
 
5
  class ShortPixelController
6
  {
7
  protected static $controllers = array();
8
+ protected static $modelsLoaded = array(); // don't require twice, limit amount of require looksups..
9
 
10
+ protected $shortPixel;
 
 
11
 
12
+ protected $model;
13
  protected $template = null; // template name to include when loading.
14
+ protected $data = array(); // data array for usage with databases data and such
15
+ protected $postData = array(); // data coming from form posts.
16
+ protected $mapper; // Mapper is array of View Name => Model Name. Convert between the two
17
+ protected $is_form_submit = false;
18
+ protected $view; // object to use in the view.
19
+ protected $url; // if controller is home to a page, sets the URL here. For redirects and what not.
20
 
21
  public static function init()
22
  {
26
  }
27
  }
28
 
29
+ /* Static function to use for finding a associated controller within the WP page ecosystem
30
+ *
31
+ * e.g. My page path in Wp-admin is bulk-restore-all, it can autofind needed controller ( and view )
32
+ */
33
  public static function findControllerbySlug($name)
34
  {
35
  foreach(self::$controllers as $className)
36
  {
37
+ if (! isset($className::$slug)) // controllers not connected by slugs
38
+ continue;
39
+
40
  if ($className::$slug == $name)
41
  {
42
  return $className; // found!
46
 
47
  public function __construct()
48
  {
49
+ $this->view = new \stdClass;
50
+ // Basic View Construct
51
+ $this->view->notices = null; // Notices of class notice, for everything noticable
52
+ $this->view->data = null; // Data(base), to separate from regular view data
53
+
54
+
55
+ }
56
+
57
+ /* Check if postData has been submitted.
58
+ * This function should always be called at any ACTION function ( load, load_$action etc ).
59
+ */
60
+ protected function checkPost()
61
+ {
62
  if (isset($_POST) && count($_POST) > 0)
63
  {
64
+ $this->is_form_submit = true;
65
  $this->processPostData($_POST);
66
  }
67
  }
73
 
74
  public function setShortPixel($pixel)
75
  {
76
+ $this->shortPixel = $pixel; // notice the capital, case-sensitive!
77
  }
78
 
79
+ /** Loads a view
80
+ *
81
+ *
82
+ */
83
+ public function loadView($template = null)
84
  {
85
+ if (strlen(trim($template)) == 0)
86
+ $template = null;
87
+
88
+ if (is_null($this->template) && is_null($template))
89
  {
90
  // error
91
  return false;
92
  }
93
+ // load either param or class template.
94
+ $template = (is_null($template)) ? $this->template : $template;
95
 
96
+ $view = $this->view;
97
  $controller = $this;
98
 
99
+ $template_path = \ShortPixelTools::getPluginPath() . 'class/view/' . $template . '.php';
100
  if (file_exists($template_path))
101
+ {
102
  include($template_path);
103
+ }
104
+ else {
105
+ Log::addError("View $template could not be found in " . $template_path,
106
+ array('class' => get_class($this), 'req' => $_REQUEST));
107
+ }
108
 
109
  }
110
 
111
+ /** Loads the Model Data Structure upon request
112
+ *
113
+ * @param string $name Name of the model
114
+ */
115
+ protected function loadModel($name){
116
+ $path = \ShortPixelTools::getPluginPath() . 'class/model/' . $name . '_model.php';
117
+
118
+ if (! in_array($name, self::$modelsLoaded))
119
+ {
120
+ self::$modelsLoaded[] = $name;
121
+ if(file_exists($path)){
122
+ require_once($path);
123
+ }
124
+ else {
125
+ Log::addError('Model $name could not be found');
126
+ }
127
+ }
128
+ }
129
+
130
+ /** Accepts POST data, maps, checks missing fields, and applies sanitization to it.
131
+ * @param array $post POST data
132
+ */
133
  protected function processPostData($post)
134
  {
135
+
136
+ // If there is something to map, map.
137
+ if ($this->mapper && is_array($this->mapper) && count($this->mapper) > 0)
138
+ {
139
+ foreach($this->mapper as $item => $replace)
140
+ {
141
+ if ( isset($post[$item]))
142
+ {
143
+ $post[$replace] = $post[$item];
144
+ unset($post[$item]);
145
+ }
146
+ }
147
+ }
148
+
149
+ if (is_null($this->model))
150
  {
151
+ foreach($post as $name => $value )
152
+ {
153
+ $this->postData[sanitize_text_field($name)] = sanitize_text_field($value);
154
+ return true;
155
+ }
156
  }
 
157
 
158
+ $model = $this->model;
159
+ $this->postData = $model->getSanitizedData($post);
160
 
161
+ return $this->postData;
162
 
163
+ }
164
 
165
+ public function setControllerURL($url)
166
+ {
167
+ $this->url = $url;
168
+ }
169
 
170
+ } // controller
class/controller/debug.php ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ /*** Logger class
5
+ *
6
+ * Class uses the debug data model for keeping log entries.
7
+ */
8
+ class ShortPixelLogger extends shortPixelController
9
+ {
10
+ static protected $instance = null;
11
+ protected $start_time;
12
+
13
+ protected $is_active = false;
14
+ protected $is_manual_request = false;
15
+ protected $show_debug_view = false;
16
+
17
+ protected $items = array();
18
+ protected $logPath = false;
19
+ protected $logMode = FILE_APPEND;
20
+
21
+ protected $logLevel;
22
+ protected $format = "[ %%time%% ] %%color%% %%level%% %%color_end%% \t %%message%% \t %%caller%% ( %%time_passed%% )";
23
+ protected $format_data = "\t %%data%% ";
24
+
25
+ /* protected $hooks = array(
26
+ 'shortpixel_image_exists' => array('numargs' => 3),
27
+ 'shortpixel_webp_image_base' => array('numargs' => 2),
28
+ 'shortpixel_image_urls' => array('numargs' => 2),
29
+ ); // @todo monitor hooks, but this should be more dynamic. Do when moving to module via config.
30
+ */
31
+ protected $hooks = array();
32
+
33
+ protected $template = 'view-debug-box';
34
+
35
+ /** Debugger constructor
36
+ * Two ways to activate the debugger. 1) Define SHORTPIXEL_DEBUG in wp-config.php. Either must be true or a number corresponding to required LogLevel
37
+ * 2) Put SHORTPIXEL_DEBUG in the request. Either true or number.
38
+ */
39
+ public function __construct()
40
+ {
41
+ $this->start_time = microtime(true);
42
+ $this->logLevel = DebugItem::LEVEL_WARN;
43
+
44
+ if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) // manual takes precedence over constants
45
+ {
46
+ $this->is_manual_request = true;
47
+ $this->is_active = true;
48
+
49
+ if ($_REQUEST['SHORTPIXEL_DEBUG'] === 'true')
50
+ {
51
+ $this->logLevel = DebugItem::LEVEL_INFO;
52
+ }
53
+ else {
54
+ $this->logLevel = intval($_REQUEST['SHORTPIXEL_DEBUG']);
55
+ }
56
+
57
+ }
58
+ else if ( (defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG > 0) )
59
+ {
60
+ $this->is_active = true;
61
+ if (SHORTPIXEL_DEBUG === true)
62
+ $this->logLevel = DebugItem::LEVEL_INFO;
63
+ else {
64
+ $this->logLevel = intval(SHORTPIXEL_DEBUG);
65
+ }
66
+ }
67
+
68
+ if (defined('SHORTPIXEL_DEBUG_TARGET') && SHORTPIXEL_DEBUG_TARGET || $this->is_manual_request)
69
+ {
70
+ $this->logPath = SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log";
71
+ //$this->logMode = defined('SHORTPIXEL_LOG_OVERWRITE') ? 0 : FILE_APPEND;
72
+ if (defined('SHORTPIXEL_LOG_OVERWRITE')) // if overwrite, do this on init once.
73
+ file_put_contents($this->logPath,'-- Log Reset -- ' .PHP_EOL);
74
+
75
+ }
76
+
77
+ $user_is_administrator = (current_user_can('manage_options')) ? true : false;
78
+
79
+ if ($this->is_active && $this->is_manual_request && $user_is_administrator )
80
+ {
81
+ $this->layout = new \stdClass;
82
+ $this->layout->logLink = SHORTPIXEL_BACKUP_URL . "/shortpixel_log";
83
+
84
+ add_action('admin_footer', array($this, 'loadView'));
85
+ }
86
+
87
+ if ($this->is_active && count($this->hooks) > 0)
88
+ $this->monitorHooks();
89
+ }
90
+
91
+ public static function getInstance()
92
+ {
93
+ if ( self::$instance === null)
94
+ {
95
+ self::$instance = new ShortPixelLogger();
96
+ }
97
+ return self::$instance;
98
+ }
99
+
100
+ protected static function addLog($message, $level, $data = array())
101
+ {
102
+ $log = self::getInstance();
103
+
104
+ // don't log anything too low.
105
+ if ($log->logLevel < $level)
106
+ {
107
+ return;
108
+ }
109
+
110
+ $arg = array();
111
+ $args['level'] = $level;
112
+ $args['data'] = $data;
113
+
114
+ $newItem = new \ShortPixel\DebugItem($message, $args);
115
+ $log->items[] = $newItem;
116
+
117
+ if ($log->is_active)
118
+ {
119
+ $log->write($newItem);
120
+ }
121
+ }
122
+
123
+ /** Writes to log File. */
124
+ protected function write($debugItem, $mode = 'file')
125
+ {
126
+ $items = $debugItem->getForFormat();
127
+ $items['time_passed'] = round ( ($items['time'] - $this->start_time), 5);
128
+ $items['time'] = date('Y-m-d H:i:s', $items['time'] );
129
+
130
+ if ( ($items['caller']) && is_array($items['caller']) && count($items['caller']) > 0)
131
+ {
132
+ $caller = $items['caller'];
133
+ $items['caller'] = $caller['file'] . ' in ' . $caller['function'] . '(' . $caller['line'] . ')';
134
+ }
135
+
136
+ $line = $this->formatLine($items);
137
+
138
+ if ($this->logPath)
139
+ {
140
+ file_put_contents($this->logPath,$line, FILE_APPEND);
141
+ }
142
+ else {
143
+ error_log($line);
144
+ }
145
+ }
146
+
147
+ protected function formatLine($args = array() )
148
+ {
149
+ $line= $this->format;
150
+ foreach($args as $key => $value)
151
+ {
152
+ if (! is_array($value) && ! is_object($value))
153
+ $line = str_replace('%%' . $key . '%%', $value, $line);
154
+ }
155
+
156
+ $line .= PHP_EOL;
157
+
158
+ if (isset($args['data']))
159
+ {
160
+ $data = array_filter($args['data']);
161
+ if (count($data) > 0)
162
+ {
163
+ foreach($data as $item)
164
+ {
165
+ $line .= $item . PHP_EOL;
166
+ }
167
+ }
168
+ }
169
+
170
+ return $line;
171
+ }
172
+
173
+ protected function setLogLevel($level)
174
+ {
175
+ $this->logLevel = $level;
176
+ }
177
+
178
+ protected function getEnv($name)
179
+ {
180
+ if (isset($this->{$name}))
181
+ {
182
+ return $this->{$name};
183
+ }
184
+ else {
185
+ return false;
186
+ }
187
+ }
188
+
189
+ public static function addError($message, $args = array())
190
+ {
191
+ $level = DebugItem::LEVEL_ERROR;
192
+ static::addLog($message, $level, $args);
193
+ }
194
+ public static function addWarn($message, $args = array())
195
+ {
196
+ $level = DebugItem::LEVEL_WARN;
197
+ static::addLog($message, $level, $args);
198
+ }
199
+ public static function addInfo($message, $args = array())
200
+ {
201
+ $level = DebugItem::LEVEL_INFO;
202
+ static::addLog($message, $level, $args);
203
+ }
204
+ public static function addDebug($message, $args = array())
205
+ {
206
+ $level = DebugItem::LEVEL_DEBUG;
207
+ static::addLog($message, $level, $args);
208
+ }
209
+
210
+ public static function logLevel($level)
211
+ {
212
+ $log = self::getInstance();
213
+ static::addInfo('Changing Log level' . $level);
214
+ $log->setLogLevel($level);
215
+ }
216
+
217
+ public static function getLogLevel()
218
+ {
219
+ $log = self::getInstance();
220
+ return $log->getEnv('logLevel');
221
+ }
222
+
223
+ public static function isManualDebug()
224
+ {
225
+ $log = self::getInstance();
226
+ return $log->getEnv('is_manual_request');
227
+ }
228
+
229
+ public static function getLogPath()
230
+ {
231
+ $log = self::getInstance();
232
+ return $log->getEnv('logPath');
233
+ }
234
+
235
+ /** Function to test if the debugger is active
236
+ * @return boolean true when active.
237
+ */
238
+ public static function debugIsActive()
239
+ {
240
+ $log = self::getInstance();
241
+ return $log->getEnv('is_active');
242
+ }
243
+
244
+ protected function monitorHooks()
245
+ {
246
+
247
+ foreach($this->hooks as $hook => $data)
248
+ {
249
+ $numargs = isset($data['numargs']) ? $data['numargs'] : 1;
250
+ $prio = isset($data['priority']) ? $data['priority'] : 10;
251
+
252
+ add_filter($hook, function($value) use ($hook) {
253
+ $args = func_get_args();
254
+ return $this->logHook($hook, $value, $args); }, $prio, $numargs);
255
+ }
256
+ }
257
+
258
+ public function logHook($hook, $value, $args)
259
+ {
260
+ array_shift($args);
261
+ self::addInfo('[Hook] - ' . $hook . ' with ' . var_export($value,true), $args);
262
+ return $value;
263
+ }
264
+
265
+
266
+ } // class debugController
class/controller/notice.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger as Log;
4
+
5
+ class NoticeController extends ShortPixelController
6
+ {
7
+ protected static $notices;
8
+ protected static $instance = null;
9
+ public $notice_count = 0;
10
+
11
+ protected $has_stored = false;
12
+
13
+ public function __construct()
14
+ {
15
+ $this->loadModel('notice');
16
+ $this->loadNotices();
17
+ }
18
+
19
+
20
+ protected function loadNotices()
21
+ {
22
+ $notices = get_option('shortpixel-notices', false);
23
+ $cnotice = (is_array($notices)) ? count($notices) : 0;
24
+ Log::addDebug('Notice Control - #num notices' . $cnotice);
25
+ if ($notices !== false)
26
+ {
27
+ self::$notices = $notices;
28
+ $this->has_stored = true;
29
+ }
30
+ else {
31
+ self::$notices = array();
32
+ $this->has_stored = false;
33
+ }
34
+ $this->countNotices();
35
+ }
36
+
37
+ public function addNotice($message, $code)
38
+ {
39
+ $notice = new NoticeModel($message, $code);
40
+ self::$notices[] = $notice;
41
+ $this->countNotices();
42
+ Log::addDebug('Adding notice - ', $notice);
43
+ $this->update();
44
+ return $notice;
45
+ }
46
+
47
+ /** Update the notices to store, check what to remove, returns count. */
48
+ public function update()
49
+ {
50
+ if (! is_array(self::$notices) || count(self::$notices) == 0)
51
+ {
52
+ if ($this->has_stored)
53
+ delete_option('shortpixel-notices');
54
+
55
+ return 0;
56
+ }
57
+
58
+ $new_notices = array();
59
+ foreach(self::$notices as $item)
60
+ {
61
+ if (! $item->isDone() )
62
+ {
63
+ $new_notices[] = $item;
64
+ }
65
+ }
66
+
67
+ update_option('shortpixel-notices', $new_notices);
68
+ self::$notices = $new_notices;
69
+
70
+ return $this->countNotices();
71
+ }
72
+
73
+ public function countNotices()
74
+ {
75
+ $this->notice_count = count(self::$notices);
76
+ return $this->notice_count;
77
+ }
78
+
79
+
80
+ public function getNotices()
81
+ {
82
+ return self::$notices;
83
+ }
84
+
85
+ public static function getInstance()
86
+ {
87
+ if ( self::$instance === null)
88
+ {
89
+ self::$instance = new NoticeController();
90
+ }
91
+
92
+ return self::$instance;
93
+ }
94
+
95
+ /** Adds a notice, quick and fast method
96
+ * @param String $message The Message you want to notify
97
+ * @param int $code A value of messageType as defined in model
98
+ * @returm Object Instance of noticeModel
99
+ */
100
+
101
+ public static function addNormal($message)
102
+ {
103
+ $noticeController = self::getInstance();
104
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_NORMAL);
105
+ return $notice;
106
+
107
+ }
108
+
109
+ public static function addError($message)
110
+ {
111
+ $noticeController = self::getInstance();
112
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_ERROR);
113
+ return $notice;
114
+
115
+ }
116
+
117
+ public static function addWarning($message)
118
+ {
119
+ $noticeController = self::getInstance();
120
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_WARNING);
121
+ return $notice;
122
+
123
+ }
124
+
125
+ public static function addSuccess($message)
126
+ {
127
+ $noticeController = self::getInstance();
128
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_SUCCESS);
129
+ return $notice;
130
+
131
+ }
132
+
133
+
134
+ }
class/controller/settings.php ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger as Log;
4
+ use ShortPixel\DebugItem as DebugItem;
5
+ use ShortPixel\NoticeController as Notice;
6
+
7
+
8
+ class SettingsController extends shortPixelController
9
+ {
10
+
11
+ //env
12
+ protected $is_nginx;
13
+ protected $is_verifiedkey;
14
+ protected $is_htaccess_writable;
15
+ protected $is_multisite;
16
+ protected $is_mainsite;
17
+ protected $is_constant_key;
18
+ protected $hide_api_key;
19
+ protected $has_nextgen;
20
+ protected $do_redirect = false;
21
+ protected $postkey_needs_validation = false;
22
+
23
+ protected $quotaData = null;
24
+
25
+ protected $mapper = array(
26
+ 'key' => 'apiKey',
27
+ 'cmyk2rgb' => 'CMYKtoRGBconversion',
28
+ );
29
+
30
+ protected $display_part = 'settings';
31
+
32
+ public function __construct()
33
+ {
34
+ // @todo Remove Debug Call
35
+ $this->model = new \WPShortPixelSettings();
36
+
37
+
38
+ parent::__construct();
39
+
40
+ }
41
+
42
+ // default action of controller
43
+ public function load()
44
+ {
45
+ $this->loadEnv();
46
+ $this->checkPost(); // sets up post data
47
+
48
+ $this->model->redirectedSettings = 2; // not sure what this does.
49
+ $this->checkKey(); // needs post data
50
+
51
+
52
+ if ($this->is_form_submit)
53
+ {
54
+ $this->processSave();
55
+ }
56
+
57
+ $this->load_settings();
58
+ }
59
+
60
+ // this is the nokey form, submitting api key
61
+ public function action_addkey()
62
+ {
63
+ $this->loadEnv();
64
+ $this->checkPost();
65
+
66
+ Log::addDebug($this->postData);
67
+ if ($this->is_form_submit && isset($this->postData['apiKey']))
68
+ {
69
+ $this->checkKey();
70
+ if (isset($this->postData['verifiedKey']) && $this->postData['verifiedKey'])
71
+ {
72
+ $this->model->apiKey = $this->postData['apiKey'];
73
+ $this->model->verifiedKey = $this->postData['verifiedKey'];
74
+ }
75
+ }
76
+
77
+ $this->doRedirect();
78
+ //exit();
79
+
80
+ }
81
+
82
+ public function processSave()
83
+ {
84
+ Log::addDebug('after process postData', $this->postData);
85
+ // Split this in the several screens. I.e. settings, advanced, Key Request IF etc.
86
+
87
+ if ($this->postData['includeNextGen'] == 1)
88
+ {
89
+ $nextgen = new NextGen($this->shortPixel);
90
+ $previous = $this->model->includeNextGen;
91
+ $nextgen->nextGenEnabled($previous);
92
+ }
93
+
94
+ // write checked and verified post data to model. With normal models, this should just be call to update() function
95
+ foreach($this->postData as $name => $value)
96
+ {
97
+ $this->model->{$name} = $value;
98
+ }
99
+
100
+ // end
101
+ if ($this->do_redirect)
102
+ $this->doRedirect('bulk');
103
+ else {
104
+ $this->doRedirect();
105
+ }
106
+ }
107
+
108
+ /* Loads the view data and the view */
109
+ public function load_settings()
110
+ {
111
+ $this->loadQuotaData();
112
+ $this->view->data = (Object) $this->model->getData();
113
+ if (($this->is_constant_key))
114
+ $this->view->data->apiKey = SHORTPIXEL_API_KEY;
115
+
116
+ $this->view->minSizes = $this->getMaxIntermediateImageSize();
117
+ $this->view->customFolders= $this->loadCustomFolders();
118
+ $this->view->allThumbSizes = $this->shortPixel->getAllThumbnailSizes();
119
+ $this->view->averageCompression = $this->shortPixel->getAverageCompression();
120
+ $this->view->savedBandwidth = \WpShortPixel::formatBytes($this->view->data->savedSpace * 10000,2);
121
+ $this->view->resources = wp_remote_post($this->model->httpProto . "://shortpixel.com/resources-frag");
122
+ if (is_wp_error($this->view->resources))
123
+ $this->view->resources = null;
124
+
125
+ $this->loadView('view-settings');
126
+ }
127
+
128
+ /** Checks on things and set them for information. */
129
+ public function loadEnv()
130
+ {
131
+ $this->is_nginx = strpos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false ? true : false;
132
+ $this->is_gd_installed = function_exists('imagecreatefrompng');
133
+ $this->is_curl_installed = function_exists('curl_init');
134
+
135
+ $this->is_htaccess_writable = $this->HTisWritable();
136
+
137
+ $this->is_multisite = (function_exists("is_multisite") && is_multisite()) ? true : false;
138
+ $this->is_mainsite = is_main_site();
139
+
140
+ $this->has_nextgen = \ShortPixelNextGenAdapter::hasNextGen();
141
+
142
+ $this->display_part = isset($_GET['part']) ? sanitize_text_field($_GET['part']) : 'settings';
143
+ }
144
+
145
+ /** Check if everything is OK with the Key **/
146
+ public function checkKey()
147
+ {
148
+ $this->is_constant_key = (defined("SHORTPIXEL_API_KEY")) ? true : false;
149
+ $this->hide_api_key = (defined("SHORTPIXEL_HIDE_API_KEY")) ? SHORTPIXEL_HIDE_API_KEY : false;
150
+
151
+ $verified_key = $this->model->verifiedKey;
152
+ $this->is_verifiedkey = ($verified_key) ? true : false;
153
+
154
+ $key_in_db = $this->model->apiKey;
155
+
156
+ //Log::addDebug()
157
+ if($this->is_constant_key)
158
+ {
159
+ if (strlen(SHORTPIXEL_API_KEY) <> 20)
160
+ {
161
+ $this->noticeApiKeyLength(SHORTPIXEL_API_KEY);
162
+ }
163
+ elseif ($key_in_db != SHORTPIXEL_API_KEY)
164
+ {
165
+ $this->validateKey(SHORTPIXEL_API_KEY);
166
+ }
167
+ }
168
+ elseif ($this->postkey_needs_validation)
169
+ {
170
+ $key = isset($this->postData['apiKey']) ? $this->postData['apiKey'] : $this->model->apiKey;
171
+ if (strlen($key) <> 20)
172
+ {
173
+ $this->NoticeApiKeyLength($key);
174
+ }
175
+ else // key good to go.
176
+ {
177
+ $this->validateKey($key);
178
+ }
179
+ } // postkey_needs_validation
180
+ }
181
+
182
+ /** Check remotely if key is alright **/
183
+ public function validateKey($key)
184
+ {
185
+ Log::addDebug('Validating Key ' . $key);
186
+ // first, save Auth to satisfy getquotainformation
187
+ if ($this->is_form_submit)
188
+ {
189
+ if (strlen($this->postData['siteAuthUser']) > 0 || strlen($this->postData['siteAuthPass']) > 0)
190
+ {
191
+ $this->model->siteAuthUser = $this->postData['siteAuthUser'];
192
+ $this->model->siteAuthPass = $this->postData['siteAuthPass'];
193
+ }
194
+ }
195
+
196
+ $this->quotaData = $this->shortPixel->getQuotaInformation($key, true, 'validate', $this->postData);
197
+ $this->is_verifiedkey = ($this->quotaData['APIKeyValid']) ? true : false;
198
+
199
+ Log::addDebug('Verify Result', $this->quotaData);
200
+
201
+ if ($this->is_form_submit) // are we saving a form?
202
+ {
203
+ $this->postData['verifiedKey'] = $this->is_verifiedkey;
204
+ $this->postData['apiKey'] = $key;
205
+ }
206
+ else { // if not, put it to the model directly.
207
+ $this->model->verifiedKey = $this->is_verifiedkey;
208
+ $this->model->apiKey = $key;
209
+ }
210
+
211
+
212
+ if (! $this->is_verifiedkey)
213
+ {
214
+ Notice::addError(sprintf(__('Error during verifying API key: %s','shortpixel-image-optimizer'), $this->quotaData['Message'] ));
215
+ }
216
+ elseif ($this->is_form_submit) {
217
+ $this->processNewKey();
218
+ }
219
+
220
+ }
221
+
222
+ /** Process some things when key has been added. This is from original wp-short-pixel.php */
223
+ protected function processNewKey()
224
+ {
225
+ $lastStatus = $this->model->bulkLastStatus;
226
+ if(isset($lastStatus['Status']) && $lastStatus['Status'] == \ShortPixelAPI::STATUS_NO_KEY) {
227
+ $this->model->bulkLastStatus = null;
228
+ }
229
+ //display notification
230
+ $urlParts = explode("/", get_site_url());
231
+ if( $this->quotaData['DomainCheck'] == 'NOT Accessible'){
232
+ $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'));
233
+ Notice::addWarning($notice);
234
+ } else {
235
+ if ( function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY"))
236
+ $notice = __("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')
237
+ . "<BR> <b>define('SHORTPIXEL_API_KEY', '". $this->postData['apiKey'] ."');</b>";
238
+ else
239
+ $notice = __('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');
240
+
241
+ Notice::addSuccess($notice);
242
+ }
243
+
244
+ //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
245
+ if ( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && ! \ShortPixelFolder::createBackUpFolder() )
246
+ {
247
+ $notice = 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'),
248
+ WP_CONTENT_DIR . '/' . SHORTPIXEL_UPLOADS_NAME );
249
+ Notice::addError($notice);
250
+ }
251
+ }
252
+
253
+ /* Temporary function to check if HTaccess is writable.
254
+ * HTaccess is writable if it exists *and* is_writable, or can be written if directory is writable.
255
+ * @todo Should be replaced when File / Folder model are complete. Function check should go there.
256
+ */
257
+ private function HTisWritable()
258
+ {
259
+ if ($this->is_nginx)
260
+ return false;
261
+
262
+ if (file_exists(get_home_path() . 'htaccess') && is_writable(get_home_path() . 'htaccess'))
263
+ {
264
+ return true;
265
+ }
266
+ if (file_exists(get_home_path()) && is_writable(get_home_path()))
267
+ {
268
+ return true;
269
+ }
270
+ return false;
271
+ // (is_writable(get_home_path() . 'htaccess')) ? true : false;
272
+ }
273
+
274
+ protected function getMaxIntermediateImageSize() {
275
+ global $_wp_additional_image_sizes;
276
+
277
+ $width = 0;
278
+ $height = 0;
279
+ $get_intermediate_image_sizes = get_intermediate_image_sizes();
280
+
281
+ // Create the full array with sizes and crop info
282
+ if(is_array($get_intermediate_image_sizes)) foreach( $get_intermediate_image_sizes as $_size ) {
283
+ if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
284
+ $width = max($width, get_option( $_size . '_size_w' ));
285
+ $height = max($height, get_option( $_size . '_size_h' ));
286
+ //$sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' );
287
+ } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
288
+ $width = max($width, $_wp_additional_image_sizes[ $_size ]['width']);
289
+ $height = max($height, $_wp_additional_image_sizes[ $_size ]['height']);
290
+ //'crop' => $_wp_additional_image_sizes[ $_size ]['crop']
291
+ }
292
+ }
293
+ return array('width' => max(100, $width), 'height' => max(100, $height));
294
+ }
295
+
296
+ protected function loadQuotaData()
297
+ {
298
+ // @todo Probably good idea to put this in a 2-5 min transient or so.
299
+ if (is_null($this->quotaData))
300
+ $this->quotaData = $this->shortPixel->checkQuotaAndAlert();
301
+
302
+ $quotaData = $this->quotaData;
303
+ $this->view->thumbnailsToProcess = isset($quotaData['totalFiles']) ? ($quotaData['totalFiles'] - $quotaData['mainFiles']) - ($quotaData['totalProcessedFiles'] - $quotaData['mainProcessedFiles']) : 0;
304
+
305
+ $remainingImages = $quotaData['APICallsRemaining'];
306
+ $remainingImages = ( $remainingImages < 0 ) ? 0 : number_format($remainingImages);
307
+ $this->view->remainingImages = $remainingImages;
308
+
309
+ $this->view->totalCallsMade = array( 'plan' => $quotaData['APICallsMadeNumeric'] , 'oneTime' => $quotaData['APICallsMadeOneTimeNumeric'] );
310
+
311
+ }
312
+
313
+ protected function loadCustomFolders()
314
+ {
315
+ $notice = null;
316
+ $customFolders = $this->shortPixel->refreshCustomFolders($notice);
317
+
318
+ if (! is_null($notice))
319
+ {
320
+ $message = $notice['msg'];
321
+ if ($notice['status'] == 'error')
322
+ Notice::addError($message);
323
+ else
324
+ Notice::addNormal($message);
325
+
326
+
327
+ }
328
+
329
+ if ($this->has_nextgen)
330
+ {
331
+ $ngg = array_map(array('ShortPixelNextGenAdapter','pathToAbsolute'), \ShortPixelNextGenAdapter::getGalleries());
332
+ for($i = 0; $i < count($customFolders); $i++) {
333
+ if(in_array($customFolders[$i]->getPath(), $ngg )) {
334
+ $customFolders[$i]->setType("NextGen");
335
+ }
336
+ }
337
+ }
338
+ return $customFolders;
339
+ }
340
+
341
+ // This is done before handing it off to the parent controller, to sanitize and check against model.
342
+ protected function processPostData($post)
343
+ {
344
+ Log::addDebug('raw post data', $post);
345
+
346
+ if (isset($post['display_part']) && strlen($post['display_part']) > 0)
347
+ {
348
+ $this->display_part = sanitize_text_field($post['display_part']);
349
+ }
350
+ unset($post['display_part']);
351
+
352
+ // analyse the save button
353
+ if (isset($post['save_bulk']))
354
+ {
355
+ $this->do_redirect = true;
356
+ }
357
+ unset($post['save_bulk']);
358
+ unset($post['save']);
359
+
360
+ // handle 'reverse' checkbox.
361
+ $keepExif = isset($post['removeExif']) ? 0 : 1;
362
+ $post['keepExif'] = $keepExif;
363
+ unset($post['removeExif']);
364
+
365
+ // checkbox overloading
366
+ $png2jpg = (isset($post['png2jpg']) ? (isset($post['png2jpgForce']) ? 2 : 1): 0);
367
+ $post['png2jpg'] = $png2jpg;
368
+ unset($post['png2jpgForce']);
369
+
370
+ // must be an array
371
+ $post['excludeSizes'] = (isset($post['excludeSizes']) && is_array($post['excludeSizes']) ? $post['excludeSizes']: array());
372
+
373
+ // key check, if validate is set to valid, check the key
374
+ if (isset($post['validate']))
375
+ {
376
+ if ($post['validate'] == 'validate')
377
+ $this->postkey_needs_validation = true;
378
+
379
+ unset($post['validate']);
380
+ }
381
+
382
+ if (isset($post['addCustomFolder']) && strlen($post['addCustomFolder']) > 0)
383
+ {
384
+ $folder = sanitize_text_field(stripslashes($post['addCustomFolder']));
385
+ $uploadPath = realpath(SHORTPIXEL_UPLOADS_BASE);
386
+
387
+ $metaDao = $this->shortPixel->getSpMetaDao();
388
+ $folderMsg = $metaDao->newFolderFromPath($folder, $uploadPath, \WPShortPixel::getCustomFolderBase());
389
+ $is_warning = true;
390
+ if(!$folderMsg) {
391
+ //$notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
392
+ $folderMsg = __('Folder added successfully.','shortpixel-image-optimiser');
393
+
394
+ $is_warning = false;
395
+ }
396
+ if ($is_warning)
397
+ Notice::addWarning($folderMsg);
398
+ else
399
+ Notice::addNormal($folderMsg);
400
+
401
+ $this->model->hasCustomFolders = time();
402
+ }
403
+ unset($post['addCustomFolder']);
404
+
405
+ if(isset($post['removeFolder']) && strlen( trim($post['removeFolder'])) > 0) {
406
+ $metaDao = $this->shortPixel->getSpMetaDao();
407
+ Log::addDebug('Removing folder ' . $post['removeFolder']);
408
+ $metaDao->removeFolder( sanitize_text_field($post['removeFolder']) );
409
+
410
+ }
411
+ unset($post['removeFolder']);
412
+
413
+ if (isset($post['emptyBackup']))
414
+ {
415
+ $this->shortPixel->emptyBackup();
416
+ }
417
+ unset($post['emptyBackup']);
418
+
419
+
420
+ $post = $this->processWebp($post);
421
+ $post = $this->processExcludeFolders($post);
422
+
423
+ parent::processPostData($post);
424
+
425
+ }
426
+
427
+ /** Function for the WebP settings overload
428
+ *
429
+ */
430
+ protected function processWebP($post)
431
+ {
432
+ $deliverwebp = 0;
433
+ \WPShortPixel::alterHtaccess(true); // always remove the statements.
434
+
435
+ if (isset($post['createWebp']) && $post['createWebp'] == 1)
436
+ {
437
+ if (isset($post['deliverWebp']) && $post['deliverWebp'] == 1)
438
+ {
439
+ $type = isset($post['deliverWebpType']) ? $post['deliverWebpType'] : '';
440
+ $altering = isset($post['deliverWebpAlteringType']) ? $post['deliverWebpAlteringType'] : '';
441
+
442
+ if ($type == 'deliverWebpAltered')
443
+ {
444
+ if ($altering == 'deliverWebpAlteredWP')
445
+ {
446
+ $deliverwebp = 2;
447
+ }
448
+ elseif($altering = 'deliverWebpAlteredGlobal')
449
+ {
450
+ $deliverwebp = 1;
451
+ }
452
+ }
453
+ elseif ($type == 'deliverWebpUnaltered') {
454
+ $deliverwebp = 3;
455
+ }
456
+ }
457
+ }
458
+
459
+ if (! $this->is_nginx && $deliverwebp == 3) // unaltered wepb via htaccess
460
+ {
461
+ \WPShortPixel::alterHtaccess();
462
+ }
463
+
464
+
465
+ $post['deliverWebp'] = $deliverwebp;
466
+ unset($post['deliverWebpAlteringType']);
467
+ unset($post['deliverWebpType']);
468
+
469
+ return $post;
470
+ }
471
+
472
+ protected function processExcludeFolders($post)
473
+ {
474
+ $patterns = array();
475
+ if(isset($post['excludePatterns']) && strlen($post['excludePatterns'])) {
476
+ $items = explode(',', $post['excludePatterns']);
477
+ foreach($items as $pat) {
478
+ $parts = explode(':', $pat);
479
+ if(count($parts) == 1) {
480
+ $patterns[] = array("type" =>"name", "value" => str_replace('\\\\','\\',trim($pat)));
481
+ } else {
482
+ $patterns[] = array("type" =>trim($parts[0]), "value" => str_replace('\\\\','\\',trim($parts[1])));
483
+ }
484
+ }
485
+
486
+ }
487
+ $post['excludePatterns'] = $patterns;
488
+ return $post;
489
+ }
490
+
491
+
492
+ protected function doRedirect($redirect = 'self')
493
+ {
494
+ if ($redirect == 'self')
495
+ {
496
+ $url = add_query_arg('part', $this->display_part);
497
+ $url = remove_query_arg('noheader', $url);
498
+ $url = remove_query_arg('sp-action', $url);
499
+ }
500
+ elseif($redirect == 'bulk')
501
+ {
502
+ $url = "upload.php?page=wp-short-pixel-bulk";
503
+ }
504
+ Log::addDebug('Redirecting: ', $url );
505
+ wp_redirect($url);
506
+ exit();
507
+ }
508
+
509
+ protected function NoticeApiKeyLength($key)
510
+ {
511
+ $KeyLength = strlen($key);
512
+
513
+ $notice = sprintf(__("The key you provided has %s characters. The API key should have 20 characters, letters and numbers only.",'shortpixel-image-optimiser'), $KeyLength)
514
+ . "<BR> <b>"
515
+ . __('Please check that the API key is the same as the one you received in your confirmation email.','shortpixel-image-optimiser')
516
+ . "</b><BR> "
517
+ . __('If this problem persists, please contact us at ','shortpixel-image-optimiser')
518
+ . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
519
+ . __(' or ','shortpixel-image-optimiser')
520
+ . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.";
521
+ Notice::addError($notice);
522
+ }
523
+ }
class/db/shortpixel-custom-meta-dao.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
 
3
  class ShortPixelCustomMetaDao {
4
  const META_VERSION = 1;
@@ -176,17 +178,29 @@ class ShortPixelCustomMetaDao {
176
  public function removeFolder($folderPath) {
177
  $sql = "SELECT id FROM {$this->db->getPrefix()}shortpixel_folders WHERE path = %s";
178
  $row = $this->db->query($sql, array(stripslashes($folderPath)));
 
179
  if(!isset($row[0]->id)) return false;
180
  $id = $row[0]->id;
181
  $sql = "UPDATE {$this->db->getPrefix()}shortpixel_folders SET status = -1 WHERE id = %d";
182
  $this->db->query($sql, array($id));
183
 
184
- $this->db->hideErrors();
185
- $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id = %d AND status <> 1 AND status <> 2";
186
- @$this->db->query($sql, array($id));
187
- $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_folders WHERE path = %s";
188
- @$this->db->query($sql, array($folderPath));
189
- $this->db->restoreErrors();
 
 
 
 
 
 
 
 
 
 
 
190
  }
191
 
192
  public function newFolderFromPath($path, $uploadPath, $rootPath) {
@@ -390,6 +404,7 @@ class ShortPixelCustomMetaDao {
390
  */
391
  public function setBulkRestore($folder_id)
392
  {
 
393
  if (! is_numeric($folder_id) || $folder_id <= 0)
394
  return false;
395
 
1
  <?php
2
+ use ShortPixel\ShortPixelLogger as Log;
3
+
4
 
5
  class ShortPixelCustomMetaDao {
6
  const META_VERSION = 1;
178
  public function removeFolder($folderPath) {
179
  $sql = "SELECT id FROM {$this->db->getPrefix()}shortpixel_folders WHERE path = %s";
180
  $row = $this->db->query($sql, array(stripslashes($folderPath)));
181
+
182
  if(!isset($row[0]->id)) return false;
183
  $id = $row[0]->id;
184
  $sql = "UPDATE {$this->db->getPrefix()}shortpixel_folders SET status = -1 WHERE id = %d";
185
  $this->db->query($sql, array($id));
186
 
187
+ //$this->db->hideErrors();
188
+ // If images are optimized, not all are removed here.
189
+ $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id = %d AND status <> %d AND status <> %d";
190
+ $this->db->query($sql, array($id, ShortPixelMeta::FILE_STATUS_PENDING, ShortPixelMeta::FILE_STATUS_SUCCESS));
191
+
192
+ $sql = "SELECT FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id = %d ";
193
+ $still_has_images = $this->db->query($sql, array($id));
194
+
195
+ // if there are no images left, remove the folder. Otherwise keep it at -1.
196
+ if (count($still_has_images) == 0)
197
+ {
198
+ $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_folders WHERE path = %s";
199
+ $this->db->query($sql, array($folderPath));
200
+ }
201
+
202
+
203
+ //$this->db->restoreErrors();
204
  }
205
 
206
  public function newFolderFromPath($path, $uploadPath, $rootPath) {
404
  */
405
  public function setBulkRestore($folder_id)
406
  {
407
+ LOG::addDebug('Set Bulk Restore', array('folderid' => $folder_id));
408
  if (! is_numeric($folder_id) || $folder_id <= 0)
409
  return false;
410
 
class/db/shortpixel-meta-facade.php CHANGED
@@ -86,9 +86,9 @@ class ShortPixelMetaFacade {
86
  }
87
  }
88
 
89
- static function sanitizeMeta($rawMeta){
90
  if(!is_array($rawMeta)) {
91
- if($rawMeta == '') { return array('ShortPixel' => array()); }
92
  else {
93
  $meta = @unserialize($rawMeta);
94
  if(is_array($meta)) {
@@ -203,7 +203,7 @@ class ShortPixelMetaFacade {
203
  //status and optimization percent in the same time, for sorting purposes :)
204
  $status = $this->meta->getStatus();
205
  if($status == 2) {
206
- $status += 0.01 * $rawMeta['ShortPixelImprovement'];
207
  }
208
  update_post_meta($_ID, '_shortpixel_status', number_format($status, 4));
209
 
@@ -368,10 +368,19 @@ class ShortPixelMetaFacade {
368
  $filePaths[] = $meta->getPath();
369
  } else {
370
  $path = get_attached_file($this->ID);//get the full file PATH
371
- $mainExists = file_exists($path);
372
  $url = self::safeGetAttachmentUrl($this->ID);
373
  $urlList = array(); $filePaths = array();
374
 
 
 
 
 
 
 
 
 
 
375
  if($mainExists) {
376
  $urlList[] = $url;
377
  $filePaths[] = $path;
@@ -419,14 +428,27 @@ class ShortPixelMetaFacade {
419
  $count++;
420
 
421
  $origPath = $tPath = str_replace(ShortPixelAPI::MB_basename($path), $thumbnailInfo['file'], $path);
422
- if ( !file_exists($tPath) ) {
 
 
423
  $tPath = SHORTPIXEL_UPLOADS_BASE . substr($tPath, strpos($tPath, $StichString) + strlen($StichString));
424
  }
425
- if ( !file_exists($tPath) ) {
 
426
  $tPath = trailingslashit(SHORTPIXEL_UPLOADS_BASE) . $origPath;
427
  }
428
- if (file_exists($tPath)) {
429
- $tUrl = str_replace(ShortPixelAPI::MB_basename($url), $thumbnailInfo['file'], $url);
 
 
 
 
 
 
 
 
 
 
430
  if(in_array($tUrl, $urlList)) continue;
431
  $urlList[] = $tUrl;
432
  $filePaths[] = $tPath;
86
  }
87
  }
88
 
89
+ static function sanitizeMeta($rawMeta, $createSPArray = true){
90
  if(!is_array($rawMeta)) {
91
+ if($rawMeta == '') { return $createSPArray ? array('ShortPixel' => array()) : array(); }
92
  else {
93
  $meta = @unserialize($rawMeta);
94
  if(is_array($meta)) {
203
  //status and optimization percent in the same time, for sorting purposes :)
204
  $status = $this->meta->getStatus();
205
  if($status == 2) {
206
+ $status += 0.01 * intval($rawMeta['ShortPixelImprovement']);
207
  }
208
  update_post_meta($_ID, '_shortpixel_status', number_format($status, 4));
209
 
368
  $filePaths[] = $meta->getPath();
369
  } else {
370
  $path = get_attached_file($this->ID);//get the full file PATH
371
+ $mainExists = apply_filters('shortpixel_image_exists', file_exists($path), $path, $this->ID);
372
  $url = self::safeGetAttachmentUrl($this->ID);
373
  $urlList = array(); $filePaths = array();
374
 
375
+ if(!$mainExists) {
376
+ //try and download the image from the URL (images present only on CDN)
377
+ $downloadTimeout = max(ini_get('max_execution_time') - 10, 15);
378
+ $tempOriginal = download_url($url, $downloadTimeout);
379
+ if(!is_wp_error( $tempOriginal )) {
380
+ $mainExists = @copy($tempOriginal, $path);
381
+ }
382
+ }
383
+
384
  if($mainExists) {
385
  $urlList[] = $url;
386
  $filePaths[] = $path;
428
  $count++;
429
 
430
  $origPath = $tPath = str_replace(ShortPixelAPI::MB_basename($path), $thumbnailInfo['file'], $path);
431
+ $file_exists = apply_filters('shortpixel_image_exists', file_exists($origPath), $origPath, $this->ID);
432
+ $tUrl = str_replace(ShortPixelAPI::MB_basename($url), $thumbnailInfo['file'], $url);
433
+ if ( !$file_exists && !file_exists($tPath) ) {
434
  $tPath = SHORTPIXEL_UPLOADS_BASE . substr($tPath, strpos($tPath, $StichString) + strlen($StichString));
435
  }
436
+
437
+ if ( !$file_exists && !file_exists($tPath) ) {
438
  $tPath = trailingslashit(SHORTPIXEL_UPLOADS_BASE) . $origPath;
439
  }
440
+
441
+ if ( !$file_exists && !file_exists($tPath) ) {
442
+ //try and download the image from the URL (images present only on CDN)
443
+ $tempThumb = download_url($tUrl, $downloadTimeout);
444
+ if(!is_wp_error( $tempThumb )) {
445
+ if(@copy($tempThumb, $origPath)) {
446
+ $tPath = $origPath;
447
+ }
448
+ }
449
+ }
450
+
451
+ if ($file_exists || file_exists($tPath)) {
452
  if(in_array($tUrl, $urlList)) continue;
453
  $urlList[] = $tUrl;
454
  $filePaths[] = $tPath;
class/db/wp-shortpixel-media-library-adapter.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
  class WpShortPixelMediaLbraryAdapter {
4
-
5
  //count all the processable files in media library (while limiting the results to max 10000)
6
  public static function countAllProcessableFiles($settings = array(), $maxId = PHP_INT_MAX, $minId = 0){
7
  global $wpdb;
@@ -30,18 +30,18 @@ class WpShortPixelMediaLbraryAdapter {
30
  }
31
 
32
  $counter = 0; $foundUnlistedThumbs = false;
33
-
34
- //count all the files, main and thumbs
35
  while ( 1 ) {
36
  $idInfo = self::getPostIdsChunk($minId, $maxId, $pointer, $limit);
37
- if($idInfo === null) {
38
  break; //we parsed all the results
39
- }
40
  elseif(count($idInfo->ids) == 0) {
41
  $pointer += $limit;
42
  continue;
43
  }
44
-
45
  $filesList= $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "postmeta
46
  WHERE post_id IN (" . implode(',', $idInfo->ids) . ")
47
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )");
@@ -51,12 +51,12 @@ class WpShortPixelMediaLbraryAdapter {
51
  $limit = 1000;
52
  continue;
53
  }
54
-
55
- foreach ( $filesList as $file )
56
- {
57
  $totalFilesThis = $processedFilesThis = 0;
58
  //if($file->post_id == 945) {var_dump($file);}
59
-
60
  if ( $file->meta_key == "_wp_attached_file" )
61
  {//count pdf files only
62
  $extension = substr($file->meta_value, strrpos($file->meta_value,".") + 1 );
@@ -65,7 +65,7 @@ class WpShortPixelMediaLbraryAdapter {
65
  $totalFiles++;
66
  $totalFilesThis++;
67
  $mainFiles++;
68
- $filesMap[$file->meta_value] = 1;
69
  }
70
  }
71
  elseif ( $file->meta_key == "_wp_attachment_metadata" ) //_wp_attachment_metadata
@@ -81,8 +81,8 @@ class WpShortPixelMediaLbraryAdapter {
81
  {
82
  $filePath = isset($attachment['file']) ? trailingslashit(SHORTPIXEL_UPLOADS_BASE).$attachment['file'] : false;
83
  if ($filePath && file_exists($filePath) && isset($attachment['sizes']) &&
84
- ( !isset($attachment['ShortPixelImprovement']) || $attachment['ShortPixelImprovement'] === 0
85
- || $attachment['ShortPixelImprovement'] === 0.0 || $attachment['ShortPixelImprovement'] === "0"))
86
  {
87
  $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($filePath);
88
  $foundCount = count($foundThumbs);
@@ -102,10 +102,10 @@ class WpShortPixelMediaLbraryAdapter {
102
  } else {
103
  $counter--; // will take the next one
104
  $realSizesCount = $sizesCount;
105
- }
106
  }
107
  $counter++;
108
-
109
  //processable
110
  $isProcessable = false;
111
  $isProcessed = isset($attachment['ShortPixelImprovement'])
@@ -150,14 +150,14 @@ class WpShortPixelMediaLbraryAdapter {
150
  $procUndefMainFiles++;
151
  $procUndefTotalFiles++;
152
  }
153
-
154
  //get the thumbs processed for that attachment
155
  $thumbs = $allThumbs = 0;
156
  if ( isset($attachment['ShortPixel']['thumbsOpt']) ) {
157
  $thumbs = $attachment['ShortPixel']['thumbsOpt'];
158
  }
159
  elseif ( isset($attachment['sizes']) ) {
160
- $thumbs = $sizesCount;
161
  }
162
  if(!isset($attachment['file'])) { //for the pdfs that have thumbs, have to add the thumbs too (not added above )
163
  $totalFiles += $thumbs;
@@ -167,8 +167,8 @@ class WpShortPixelMediaLbraryAdapter {
167
 
168
  if ( isset($attachment['sizes']) && $sizesCount > $thumbs + count($thumbsMissing)) {
169
  $mainUnprocessedThumbs++;
170
- }
171
-
172
  //increment with thumbs processed
173
  $processedTotalFiles += $thumbs;
174
  $processedFilesThis += $thumbs;
@@ -179,7 +179,7 @@ class WpShortPixelMediaLbraryAdapter {
179
  } else {
180
  $procLosslessTotalFiles += $thumbs;
181
  }
182
-
183
  if ( isset($attachment['file']) ) {
184
  $processedFilesMap[$attachment['file']] = 1;
185
  }
@@ -208,12 +208,12 @@ class WpShortPixelMediaLbraryAdapter {
208
  $totalFilesM4 += $totalFilesThis;
209
  }
210
  }
211
- }
212
  unset($filesList);
213
  $pointer += $limit;
214
  }//end while
215
 
216
- return array("totalFiles" => $totalFiles, "mainFiles" => $mainFiles,
217
  "totalProcessedFiles" => $processedTotalFiles, "mainProcessedFiles" => $processedMainFiles,
218
  "totalProcLossyFiles" => $procLossyTotalFiles, "mainProcLossyFiles" => $procLossyMainFiles,
219
  "totalProcGlossyFiles" => $procGlossyTotalFiles, "mainProcGlossyFiles" => $procGlossyMainFiles,
@@ -229,19 +229,19 @@ class WpShortPixelMediaLbraryAdapter {
229
  "moreFilesWithErrors" => $moreFilesWithErrors,
230
  "foundUnlistedThumbs" => $foundUnlistedThumbs
231
  );
232
- }
233
-
234
  public static function getPostMetaSlice($startId, $endId, $limit) {
235
  global $wpdb;
236
- $queryPostMeta = "SELECT pm.* FROM " . $wpdb->prefix . "postmeta pm
237
  INNER JOIN " . $wpdb->prefix . "posts p ON p.ID = pm.post_id
238
  WHERE ( p.ID <= $startId AND p.ID >= $endId )
239
  AND ( pm.meta_key = '_wp_attached_file' OR pm.meta_key = '_wp_attachment_metadata' )
240
  ORDER BY pm.post_id DESC
241
  LIMIT " . $limit;
242
- return $wpdb->get_results($queryPostMeta);
243
  }
244
-
245
  public static function getSizesNotExcluded($sizes, $exclude = false) {
246
  $uniq = array();
247
  $exclude = is_array($exclude) ? $exclude : array(); //this is because it sometimes receives directly the setting which could be false
@@ -257,12 +257,11 @@ class WpShortPixelMediaLbraryAdapter {
257
  return $uniq;
258
  }
259
 
260
- public static function countSizesNotExcluded($sizes, $exclude = false)
261
  {
262
  return count(self::getSizesNotExcluded($sizes, $exclude));
263
  }
264
 
265
-
266
  public static function cleanupFoundThumbs($itemHandler) {
267
  $meta = $itemHandler->getMeta();
268
  $sizesAll = $meta->getThumbs();
@@ -280,7 +279,7 @@ class WpShortPixelMediaLbraryAdapter {
280
  $meta->setThumbs($sizes);
281
  $itemHandler->updateMeta($meta, true);
282
  }
283
-
284
  public static function findThumbs($mainFile) {
285
  $ext = pathinfo($mainFile, PATHINFO_EXTENSION);
286
  $base = substr($mainFile, 0, strlen($mainFile) - strlen($ext) - 1);
@@ -329,10 +328,10 @@ class WpShortPixelMediaLbraryAdapter {
329
  return 500;
330
  }
331
  }
332
-
333
  protected static function getPostIdsChunk($minId, $maxId, $pointer, $limit) {
334
  global $wpdb;
335
-
336
  $ids = $idDates = array();
337
  $idList = $wpdb->get_results("SELECT ID, post_mime_type, post_date FROM " . $wpdb->prefix . "posts
338
  WHERE ( ID <= $maxId AND ID > $minId )
1
  <?php
2
 
3
  class WpShortPixelMediaLbraryAdapter {
4
+
5
  //count all the processable files in media library (while limiting the results to max 10000)
6
  public static function countAllProcessableFiles($settings = array(), $maxId = PHP_INT_MAX, $minId = 0){
7
  global $wpdb;
30
  }
31
 
32
  $counter = 0; $foundUnlistedThumbs = false;
33
+
34
+ //count all the files, main and thumbs
35
  while ( 1 ) {
36
  $idInfo = self::getPostIdsChunk($minId, $maxId, $pointer, $limit);
37
+ if($idInfo === null) {
38
  break; //we parsed all the results
39
+ }
40
  elseif(count($idInfo->ids) == 0) {
41
  $pointer += $limit;
42
  continue;
43
  }
44
+
45
  $filesList= $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "postmeta
46
  WHERE post_id IN (" . implode(',', $idInfo->ids) . ")
47
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )");
51
  $limit = 1000;
52
  continue;
53
  }
54
+
55
+ foreach ( $filesList as $file )
56
+ {
57
  $totalFilesThis = $processedFilesThis = 0;
58
  //if($file->post_id == 945) {var_dump($file);}
59
+
60
  if ( $file->meta_key == "_wp_attached_file" )
61
  {//count pdf files only
62
  $extension = substr($file->meta_value, strrpos($file->meta_value,".") + 1 );
65
  $totalFiles++;
66
  $totalFilesThis++;
67
  $mainFiles++;
68
+ $filesMap[$file->meta_value] = 1;
69
  }
70
  }
71
  elseif ( $file->meta_key == "_wp_attachment_metadata" ) //_wp_attachment_metadata
81
  {
82
  $filePath = isset($attachment['file']) ? trailingslashit(SHORTPIXEL_UPLOADS_BASE).$attachment['file'] : false;
83
  if ($filePath && file_exists($filePath) && isset($attachment['sizes']) &&
84
+ ( !isset($attachment['ShortPixelImprovement']) || $attachment['ShortPixelImprovement'] === 0
85
+ || $attachment['ShortPixelImprovement'] === 0.0 || $attachment['ShortPixelImprovement'] === "0"))
86
  {
87
  $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($filePath);
88
  $foundCount = count($foundThumbs);
102
  } else {
103
  $counter--; // will take the next one
104
  $realSizesCount = $sizesCount;
105
+ }
106
  }
107
  $counter++;
108
+
109
  //processable
110
  $isProcessable = false;
111
  $isProcessed = isset($attachment['ShortPixelImprovement'])
150
  $procUndefMainFiles++;
151
  $procUndefTotalFiles++;
152
  }
153
+
154
  //get the thumbs processed for that attachment
155
  $thumbs = $allThumbs = 0;
156
  if ( isset($attachment['ShortPixel']['thumbsOpt']) ) {
157
  $thumbs = $attachment['ShortPixel']['thumbsOpt'];
158
  }
159
  elseif ( isset($attachment['sizes']) ) {
160
+ $thumbs = $sizesCount;
161
  }
162
  if(!isset($attachment['file'])) { //for the pdfs that have thumbs, have to add the thumbs too (not added above )
163
  $totalFiles += $thumbs;
167
 
168
  if ( isset($attachment['sizes']) && $sizesCount > $thumbs + count($thumbsMissing)) {
169
  $mainUnprocessedThumbs++;
170
+ }
171
+
172
  //increment with thumbs processed
173
  $processedTotalFiles += $thumbs;
174
  $processedFilesThis += $thumbs;
179
  } else {
180
  $procLosslessTotalFiles += $thumbs;
181
  }
182
+
183
  if ( isset($attachment['file']) ) {
184
  $processedFilesMap[$attachment['file']] = 1;
185
  }
208
  $totalFilesM4 += $totalFilesThis;
209
  }
210
  }
211
+ }
212
  unset($filesList);
213
  $pointer += $limit;
214
  }//end while
215
 
216
+ return array("totalFiles" => $totalFiles, "mainFiles" => $mainFiles,
217
  "totalProcessedFiles" => $processedTotalFiles, "mainProcessedFiles" => $processedMainFiles,
218
  "totalProcLossyFiles" => $procLossyTotalFiles, "mainProcLossyFiles" => $procLossyMainFiles,
219
  "totalProcGlossyFiles" => $procGlossyTotalFiles, "mainProcGlossyFiles" => $procGlossyMainFiles,
229
  "moreFilesWithErrors" => $moreFilesWithErrors,
230
  "foundUnlistedThumbs" => $foundUnlistedThumbs
231
  );
232
+ }
233
+
234
  public static function getPostMetaSlice($startId, $endId, $limit) {
235
  global $wpdb;
236
+ $queryPostMeta = "SELECT DISTINCT pm.post_id FROM " . $wpdb->prefix . "postmeta pm
237
  INNER JOIN " . $wpdb->prefix . "posts p ON p.ID = pm.post_id
238
  WHERE ( p.ID <= $startId AND p.ID >= $endId )
239
  AND ( pm.meta_key = '_wp_attached_file' OR pm.meta_key = '_wp_attachment_metadata' )
240
  ORDER BY pm.post_id DESC
241
  LIMIT " . $limit;
242
+ return $wpdb->get_results($queryPostMeta);
243
  }
244
+
245
  public static function getSizesNotExcluded($sizes, $exclude = false) {
246
  $uniq = array();
247
  $exclude = is_array($exclude) ? $exclude : array(); //this is because it sometimes receives directly the setting which could be false
257
  return $uniq;
258
  }
259
 
260
+ public static function countSizesNotExcluded($sizes, $exclude= false)
261
  {
262
  return count(self::getSizesNotExcluded($sizes, $exclude));
263
  }
264
 
 
265
  public static function cleanupFoundThumbs($itemHandler) {
266
  $meta = $itemHandler->getMeta();
267
  $sizesAll = $meta->getThumbs();
279
  $meta->setThumbs($sizes);
280
  $itemHandler->updateMeta($meta, true);
281
  }
282
+
283
  public static function findThumbs($mainFile) {
284
  $ext = pathinfo($mainFile, PATHINFO_EXTENSION);
285
  $base = substr($mainFile, 0, strlen($mainFile) - strlen($ext) - 1);
328
  return 500;
329
  }
330
  }
331
+
332
  protected static function getPostIdsChunk($minId, $maxId, $pointer, $limit) {
333
  global $wpdb;
334
+
335
  $ids = $idDates = array();
336
  $idList = $wpdb->get_results("SELECT ID, post_mime_type, post_date FROM " . $wpdb->prefix . "posts
337
  WHERE ( ID <= $maxId AND ID > $minId )
class/external/helpscout.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ // Integration class for HelpScout
5
+ class HelpScout
6
+ {
7
+ public static function outputBeacon($apiKey)
8
+ {
9
+ ?>
10
+ <style>
11
+ .shortpixel-hs-blind {
12
+ position: fixed;
13
+ bottom: 18px;
14
+ right: 0;
15
+ z-index: 20003;
16
+ background-color: white;
17
+ width: 87px;
18
+ height: 174px;
19
+ border-radius: 20px 0 0 20px;
20
+ }
21
+ .shortpixel-hs-button-blind {
22
+ display:none;
23
+ position: fixed;
24
+ bottom: 115px;right: 0;
25
+ z-index: 20003;
26
+ background-color: white;
27
+ width: 237px;
28
+ height: 54px;
29
+ }
30
+ .shortpixel-hs-tools {
31
+ position: fixed;
32
+ bottom: 116px;
33
+ right: 0px;
34
+ z-index: 20003;
35
+ background-color: #ecf9fc;
36
+ padding: 8px 18px 3px 12px;
37
+ border-radius: 26px 0 0 26px;
38
+ -webkit-box-shadow: 1px 1px 5px 0px rgba(6,109,117,1);
39
+ -moz-box-shadow: 1px 1px 5px 0px rgba(6,109,117,1);
40
+ box-shadow: 1px 1px 10px 0px rgb(172, 173, 173);
41
+ }
42
+ @media (max-width: 767px) {
43
+ .shortpixel-hs-blind {
44
+ bottom: 8px;
45
+ height: 194px;
46
+ }
47
+ .shortpixel-hs-button-blind {
48
+ bottom: 100px;
49
+ }
50
+ }
51
+ </style>
52
+ <div id="shortpixel-hs-blind" class="shortpixel-hs-blind"></div>
53
+ <div id="shortpixel-hs-button-blind" class="shortpixel-hs-button-blind"></div>
54
+ <div id="shortpixel-hs-tools" class="shortpixel-hs-tools">
55
+ <a href="javascript:shortpixelToggleHS();" class="shortpixel-hs-tools-docs" title="Search through our online documentation.">
56
+ <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/notes-sp.png'));?>" style="margin-bottom: 2px;width: 36px;">
57
+ </a>
58
+ </div>
59
+ <script>
60
+ window.shortpixelHSOpen = -1;
61
+ function shortpixelToggleHS() {
62
+ if(window.shortpixelHSOpen == -1) {
63
+ HS.beacon.init();
64
+ }
65
+ if(window.shortpixelHSOpen == 1) {
66
+ HS.beacon.close();
67
+ jQuery("#shortpixel-hs-button-blind").css('display', 'none');
68
+ window.shortpixelHSOpen = 0;
69
+ } else {
70
+ HS.beacon.open();
71
+ jQuery("#shortpixel-hs-button-blind").css('display', 'block');
72
+ window.shortpixelHSOpen = 1;
73
+ }
74
+ }
75
+ </script>
76
+ <script type="text/javascript" src="https://quriobot.com/qb/widget/KoPqxmzqzjbg5eNl/V895xbyndnmeqZYd" async defer></script>
77
+
78
+ <script>
79
+ <?php
80
+ $screen = get_current_screen();
81
+ if($screen) {
82
+ switch($screen->id) {
83
+ case 'media_page_wp-short-pixel-bulk':
84
+ echo("var shortpixel_suggestions = [ '5a5de2782c7d3a19436843af', '5a5de6902c7d3a19436843e9', '5a5de5c42c7d3a19436843d0', '5a9945e42c7d3a75495145d0', '5a5de1c2042863193801047c', '5a5de66f2c7d3a19436843e0', '5a9946e62c7d3a75495145d8', '5a5de4f02c7d3a19436843c8', '5a5de65f042863193801049f', '5a5de2df0428631938010485' ]; ");
85
+ $suggestions = "shortpixel_suggestions";
86
+ break;
87
+ case 'settings_page_wp-shortpixel-settings':
88
+ echo("var shortpixel_suggestions_settings = [ '5a5de1de2c7d3a19436843a8', '5a6612032c7d3a39e6263a1d', '5a5de1c2042863193801047c', '5a5de2782c7d3a19436843af', '5a6610c62c7d3a39e6263a02', '5a9945e42c7d3a75495145d0', '5a5de66f2c7d3a19436843e0', '5a6597e80428632faf620487', '5a5de5c42c7d3a19436843d0', '5a5de5642c7d3a19436843cc' ]; ");
89
+ echo("var shortpixel_suggestions_adv_settings = [ '5a5de4f02c7d3a19436843c8', '5a8431f00428634376d01dc4', '5a5de58b0428631938010497', '5a5de65f042863193801049f', '5a9945e42c7d3a75495145d0', '5a9946e62c7d3a75495145d8', '5a5de57c0428631938010495', '5a5de2d22c7d3a19436843b1', '5a5de5c42c7d3a19436843d0', '5a5de5642c7d3a19436843cc' ]; ");
90
+ echo("var shortpixel_suggestions_cloudflare = [ '5a5de1f62c7d3a19436843a9', '5a5de58b0428631938010497', '5a5de66f2c7d3a19436843e0', '5a5de5c42c7d3a19436843d0', '5a5de6902c7d3a19436843e9', '5a5de51a2c7d3a19436843c9', '5a9946e62c7d3a75495145d8', '5a5de46c2c7d3a19436843c1', '5a5de1de2c7d3a19436843a8', '5a6597e80428632faf620487' ]; ");
91
+ $suggestions = "shortpixel_suggestions_settings";
92
+ break;
93
+ case 'media_page_wp-short-pixel-custom':
94
+ echo("var shortpixel_suggestions = [ '5a9946e62c7d3a75495145d8', '5a5de1c2042863193801047c', '5a5de2782c7d3a19436843af', '5a5de6902c7d3a19436843e9', '5a5de4f02c7d3a19436843c8', '5a6610c62c7d3a39e6263a02', '5a9945e42c7d3a75495145d0', '5a5de46c2c7d3a19436843c1', '5a5de1de2c7d3a19436843a8', '5a5de25c2c7d3a19436843ad' ]; ");
95
+ $suggestions = "shortpixel_suggestions";
96
+ break;
97
+ }
98
+ }
99
+ ?>
100
+ !function(e,o,n){ window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={
101
+ color: "#1CBECB",
102
+ icon: "question",
103
+ instructions: "Send ShortPixel a message",
104
+ topArticles: true,
105
+ poweredBy: false,
106
+ showContactFields: true,
107
+ showName: false,
108
+ showSubject: true,
109
+ translation: {
110
+ searchLabel: "What can ShortPixel help you with?",
111
+ contactSuccessDescription: "Thanks for reaching out! Someone from our team will get back to you in 24h max."
112
+ }
113
+
114
+ },t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!0,baseUrl:"//shortpixel.helpscoutdocs.com/"},contact:{enabled:!0,formId:"278a7825-fce0-11e7-b466-0ec85169275a"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");
115
+ c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r);
116
+ }(document,window.HSCW||{},window.HS||{});
117
+
118
+ window.HS.beacon.ready(function(){
119
+ HS.beacon.identify({
120
+ email: "<?php $u = wp_get_current_user(); echo($u->user_email); ?>",
121
+ apiKey: "<?php echo($apiKey);?>"
122
+ });
123
+ HS.beacon.suggest( <?php echo( $suggestions ) ?> );
124
+ });
125
+ </script>
126
+ <?php
127
+ }
128
+ }
class/external/nextgen.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\NoticeController as Notice;
4
+
5
+ class NextGen
6
+ {
7
+ protected $shortPixel;
8
+
9
+ /** @todo Temporary constructor. In future, shortpixel should not have to be passed all the time */
10
+ public function __construct($shortPixel)
11
+ {
12
+ $this->shortPixel = $shortPixel;
13
+ }
14
+
15
+ /** Enables nextGen, add galleries to custom folders
16
+ * @param boolean $silent Throw a notice or not. This seems to be based if nextgen was already activated previously or not.
17
+ */
18
+ public function nextGenEnabled($silent)
19
+ {
20
+ \WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
21
+ // $prevNextGen = $this->_settings->includeNextGen;
22
+ $this->addNextGenGalleriesToCustom($silent);
23
+ // $folderMsg = $ret["message"];
24
+ // $customFolders = $ret["customFolders"];
25
+ }
26
+
27
+ /** Adds nextGen galleries to custom table
28
+ * Note - this function does *Not* check if nextgen is enabled, not if checks custom Tables. Use nextgenEnabled for this.
29
+ * Enabled checks are not an external class issue, so must be done before calling.
30
+ */
31
+ public function addNextGenGalleriesToCustom($silent = true) {
32
+ // $customFolders = array();
33
+ $folderMsg = "";
34
+
35
+ //add the NextGen galleries to custom folders
36
+ $ngGalleries = \ShortPixelNextGenAdapter::getGalleries();
37
+ $meta = $this->shortPixel->getSpMetaDao();
38
+ foreach($ngGalleries as $gallery) {
39
+ $msg = $meta->newFolderFromPath($gallery, get_home_path(), \WPShortPixel::getCustomFolderBase());
40
+ if($msg) { //try again with ABSPATH as maybe WP is in a subdir
41
+ $msg = $meta->newFolderFromPath($gallery, ABSPATH, \WPShortPixel::getCustomFolderBase());
42
+ }
43
+ $folderMsg .= $msg;
44
+ //$this->_settings->hasCustomFolders = time();
45
+ }
46
+
47
+ if (count($ngGalleries) > 0)
48
+ {
49
+ // put timestamp to this setting.
50
+ $settings = new \WPShortPixelSettings();
51
+ $settings->hasCustomFolders = time();
52
+
53
+ }
54
+ // $customFolders = $this->spMetaDao->getFolders();
55
+ if (! $silent)
56
+ {
57
+ Notice::addNormal($folderMsg);
58
+ }
59
+
60
+ // return array("message" => $silent? "" : $folderMsg, "customFolders" => $customFolders);
61
+ }
62
+ }
class/front/img-to-picture-webp.php CHANGED
@@ -4,8 +4,12 @@
4
  * thanks to the Responsify WP plugin for some of the code
5
  */
6
 
 
 
 
7
  class ShortPixelImgToPictureWebp
8
  {
 
9
  public static function lazyGet($img, $type)
10
  {
11
  return array(
@@ -26,23 +30,80 @@ class ShortPixelImgToPictureWebp
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]);
@@ -64,6 +125,7 @@ class ShortPixelImgToPictureWebp
64
  $sizesPrefix = $sizesInfo['prefix'];
65
 
66
  $altAttr = isset($img['alt']) && strlen($img['alt']) ? ' alt="' . $img['alt'] . '"' : '';
 
67
 
68
  //check if there are webps
69
  /*$id = $thisClass::url_to_attachment_id( $src );
@@ -105,11 +167,15 @@ class ShortPixelImgToPictureWebp
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']);
115
  unset($img['data-src']);
@@ -117,6 +183,7 @@ class ShortPixelImgToPictureWebp
117
  unset($img['srcset']);
118
  unset($img['sizes']);
119
  unset($img['alt']);
 
120
  $srcsetWebP = '';
121
 
122
  if ($srcset) {
@@ -128,7 +195,7 @@ class ShortPixelImgToPictureWebp
128
 
129
  $fileWebPCompat = $imageBase . wp_basename($parts[0], '.' . pathinfo($parts[0], PATHINFO_EXTENSION)) . '.webp';
130
  $fileWebP = $imageBase . wp_basename($parts[0]) . '.webp';
131
- if (file_exists($fileWebP)) {
132
  $srcsetWebP .= (strlen($srcsetWebP) ? ',': '')
133
  . $parts[0].'.webp'
134
  . (isset($parts[1]) ? ' ' . $parts[1] : '');
@@ -138,6 +205,9 @@ class ShortPixelImgToPictureWebp
138
  .preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $parts[0])
139
  .(isset($parts[1]) ? ' ' . $parts[1] : '');
140
  }
 
 
 
141
  }
142
  //$srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+\s+/', '.webp ', $srcset);
143
  } else {
@@ -146,17 +216,21 @@ class ShortPixelImgToPictureWebp
146
 
147
  $fileWebPCompat = $imageBase . wp_basename($srcset, '.' . pathinfo($srcset, PATHINFO_EXTENSION)) . '.webp';
148
  $fileWebP = $imageBase . wp_basename($srcset) . '.webp';
149
- if (file_exists($fileWebP)) {
150
  $srcsetWebP = $srcset.".webp";
151
  } else {
152
- if (file_exists($fileWebPCompat)) {
153
  $srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $srcset);
154
  }
 
 
 
155
  }
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
@@ -165,7 +239,7 @@ class ShortPixelImgToPictureWebp
165
  return '<picture ' . self::create_attributes($img) . '>'
166
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
167
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
168
- .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . $altAttr
169
  . (strlen($srcset) ? ' srcset="' . $srcset . '"': '') . (strlen($sizes) ? ' sizes="' . $sizes . '"': '') . '>'
170
  .'</picture>';
171
  }
@@ -257,7 +331,20 @@ class ShortPixelImgToPictureWebp
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
@@ -276,7 +363,6 @@ class ShortPixelImgToPictureWebp
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']));
@@ -305,8 +391,10 @@ class ShortPixelImgToPictureWebp
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);
4
  * thanks to the Responsify WP plugin for some of the code
5
  */
6
 
7
+ use ShortPixel\DebugItem as DebugItem;
8
+ use ShortPixel\ShortPixelLogger as Log;
9
+
10
  class ShortPixelImgToPictureWebp
11
  {
12
+ /** If lazy loading is happening, get source (src) from those values */
13
  public static function lazyGet($img, $type)
14
  {
15
  return array(
30
  {
31
  // Don't do anything with the RSS feed.
32
  if (is_feed() || is_admin()) {
33
+ Log::addInfo('SPDBG convert is_feed or is_admin');
34
+ return $content; // . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- -->' : '');
35
  }
36
+
37
+ $new_content = self::testPictures($content);
38
+ if ($new_content !== false)
39
+ $content = $new_content;
40
+
41
  $content = preg_replace_callback('/<img[^>]*>/', array('self', 'convertImage'), $content);
42
  //$content = preg_replace_callback('/background.*[^:](url\(.*\)[,;])/im', array('self', 'convertInlineStyle'), $content);
43
 
44
  // [BS] No callback because we need preg_match_all
45
+ $content = self::testInlineStyle($content);
46
  // $content = preg_replace_callback('/background.*[^:]url\([\'|"](.*)[\'|"]\)[,;]/imU',array('self', 'convertInlineStyle'), $content);
47
+ Log::addDebug('SPDBG WebP process done');
48
+
49
+ return $content; // . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG WebP converted -->' : '');
50
 
51
  }
52
 
53
+ public static function testPictures($content)
54
+ {
55
+ // [BS] Escape when DOM Module not installed
56
+ if (! class_exists('DOMDocument'))
57
+ return false;
58
+
59
+ //$dom = new DOMDocument();
60
+ //@$dom->loadHTML($content, LIBXML_HTML_NOIMPLIED, LIBXML_HTML_NODEFDTD);
61
+
62
+ $dom = new DomDocument();
63
+ $fragment = $dom->createDocumentFragment();
64
+ $fragment->appendXML($content);
65
+ $dom->appendChild($fragment);
66
+
67
+ $elements = $dom->getElementsByTagName('picture');
68
+
69
+ if ($elements->length == 0)
70
+ return false;
71
+
72
+ foreach($elements as $element)
73
+ {
74
+ if ($element->hasChildNodes() )
75
+ {
76
+ foreach($element->childNodes as $elchild)
77
+ {
78
+ if ($elchild->tagName == 'img')
79
+ {
80
+ $class = ($elchild->hasAttribute('class')) ? $elchild->getAttribute('class') . ' ' : '';
81
+ $class .= 'sp-no-webp';
82
+ $elchild->setAttribute('class', $class);
83
+ Log::addInfo('Found Picture with Img, added skip class', array($elchild->getAttribute('src')) );
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ return $dom->saveHTML();
90
+
91
+ }
92
+
93
+ /* This might be a future solution for regex callbacks.
94
+ public static function processImageNode($node, $type)
95
+ {
96
+ $srcsets = $node->getElementsByTagName('srcset');
97
+ $srcs = $node->getElementsByTagName('src');
98
+ $imgs = $node->getElementsByTagName('img');
99
+ } */
100
+
101
  public static function convertImage($match)
102
  {
103
  // Do nothing with images that have the 'sp-no-webp' class.
104
  if (strpos($match[0], 'sp-no-webp')) {
105
+ Log::addInfo('SPDBG convertImage skipped, sp-no-webp found');
106
+ return $match[0]; //. (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG convertImage sp-no-webp -->' : '');
107
  }
108
 
109
  $img = self::get_attributes($match[0]);
125
  $sizesPrefix = $sizesInfo['prefix'];
126
 
127
  $altAttr = isset($img['alt']) && strlen($img['alt']) ? ' alt="' . $img['alt'] . '"' : '';
128
+ $idAttr = isset($img['id']) && strlen($img['id']) ? ' id="' . $img['id'] . '"' : '';
129
 
130
  //check if there are webps
131
  /*$id = $thisClass::url_to_attachment_id( $src );
167
  }
168
  $imageBase = dirname($imageBase) . '/';
169
  */
170
+
171
+ $imageBase = apply_filters( 'shortpixel_webp_image_base', static::getImageBase($src), $src);
172
+
173
  if($imageBase === false) {
174
+ return $match[0]; // . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG baseurl doesn\'t match ' . $src . ' -->' : '');
175
+ Log::addInfo('SPDBG baseurl doesn\'t match ' . $src);
176
  }
177
 
178
+
179
  // We don't wanna have src-ish attributes on the <picture>
180
  unset($img['src']);
181
  unset($img['data-src']);
183
  unset($img['srcset']);
184
  unset($img['sizes']);
185
  unset($img['alt']);
186
+ unset($img['id']);
187
  $srcsetWebP = '';
188
 
189
  if ($srcset) {
195
 
196
  $fileWebPCompat = $imageBase . wp_basename($parts[0], '.' . pathinfo($parts[0], PATHINFO_EXTENSION)) . '.webp';
197
  $fileWebP = $imageBase . wp_basename($parts[0]) . '.webp';
198
+ if (apply_filters( 'shortpixel_image_exists', file_exists($fileWebP), $fileWebP)) {
199
  $srcsetWebP .= (strlen($srcsetWebP) ? ',': '')
200
  . $parts[0].'.webp'
201
  . (isset($parts[1]) ? ' ' . $parts[1] : '');
205
  .preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $parts[0])
206
  .(isset($parts[1]) ? ' ' . $parts[1] : '');
207
  }
208
+ else {
209
+ Log::addDebug('Image srcset for webp doesn\'t exist', array($fileWebP));
210
+ }
211
  }
212
  //$srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+\s+/', '.webp ', $srcset);
213
  } else {
216
 
217
  $fileWebPCompat = $imageBase . wp_basename($srcset, '.' . pathinfo($srcset, PATHINFO_EXTENSION)) . '.webp';
218
  $fileWebP = $imageBase . wp_basename($srcset) . '.webp';
219
+ if (apply_filters( 'shortpixel_image_exists', file_exists($fileWebP), $fileWebP)) {
220
  $srcsetWebP = $srcset.".webp";
221
  } else {
222
+ if (apply_filters( 'shortpixel_image_exists', file_exists($fileWebPCompat), $fileWebPCompat) ) {
223
  $srcsetWebP = preg_replace('/\.[a-zA-Z0-9]+$/', '.webp', $srcset);
224
  }
225
+ else {
226
+ Log::addDebug('Image file for webp doesn\'t exist', array($fileWebP));
227
+ }
228
  }
229
  }
230
  //return($match[0]. "<!-- srcsetTZF:".$srcsetWebP." -->");
231
  if (!strlen($srcsetWebP)) {
232
+ return $match[0]; //. (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG no srcsetWebP found (' . $srcsetWebP . ') -->' : '');
233
+ Log::addInfo(' SPDBG no srcsetWebP found (' . $srcsetWebP . ')');
234
  }
235
 
236
  //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
239
  return '<picture ' . self::create_attributes($img) . '>'
240
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
241
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
242
+ .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . $idAttr . $altAttr
243
  . (strlen($srcset) ? ' srcset="' . $srcset . '"': '') . (strlen($sizes) ? ' sizes="' . $sizes . '"': '') . '>'
244
  .'</picture>';
245
  }
331
  **/
332
  public static function getImageBase($src)
333
  {
334
+ $urlParsed = parse_url($src);
335
+ if(!isset($urlParsed['host'])) {
336
+ if($src[0] == '/') { //absolute URL, current domain
337
+ $src = get_site_url() . $src;
338
+ } else {
339
+ global $wp;
340
+ $src = trailingslashit(home_url( $wp->request )) . $src;
341
+ }
342
+ $urlParsed = parse_url($src);
343
+ }
344
  $updir = wp_upload_dir();
345
+ if(substr($src, 0, 2) == '//') {
346
+ $src = (stripos($_SERVER['SERVER_PROTOCOL'],'https') === false ? 'http:' : 'https:') . $src;
347
+ }
348
  $proto = explode("://", $src);
349
  if (count($proto) > 1) {
350
  //check that baseurl uses the same http/https proto and if not, change
363
  }
364
 
365
  if ($imageBase == $src) { //maybe the site uses a CDN or a subdomain? - Or relative link
 
366
  $baseParsed = parse_url($updir['baseurl']);
367
 
368
  $srcHost = array_reverse(explode('.', $urlParsed['host']));
391
  }
392
  // [BS] Escape when DOM Module not installed
393
  if (! class_exists('DOMDocument'))
394
+ {
395
+ Log::addWarn('Webp Active, but DomDocument class not found ( missing xmldom library )');
396
  return false;
397
+ }
398
  $dom = new DOMDocument();
399
  @$dom->loadHTML($image_node);
400
  $image = $dom->getElementsByTagName('img')->item(0);
class/model/notice_model.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace ShortPixel;
4
+
5
+ class NoticeModel extends ShortPixelModel
6
+ {
7
+ protected $message;
8
+ public $code;
9
+
10
+ protected $viewed = false;
11
+ public $is_persistent = false; // This is a fatal issue, display until something was fixed.
12
+ public $is_removable = true; // if removable, display a notice dialog with red X or so.
13
+ public $messageType = self::NOTICE_NORMAL;
14
+
15
+ const NOTICE_NORMAL = 1;
16
+ const NOTICE_ERROR = 2;
17
+ const NOTICE_SUCCESS = 3;
18
+ const NOTICE_WARNING = 4;
19
+
20
+
21
+ public function __construct($message, $messageType = self::NOTICE_NORMAL)
22
+ {
23
+ $this->message = $message;
24
+ $this->messageType = $messageType;
25
+
26
+ }
27
+
28
+ public function isDone()
29
+ {
30
+ if ($this->viewed && ! $this->is_persistent)
31
+ return true;
32
+ else
33
+ return false;
34
+
35
+ }
36
+
37
+ public function getForDisplay()
38
+ {
39
+ $this->viewed = true;
40
+ $class = 'shortpixel notice ';
41
+
42
+ $icon = 'slider';
43
+
44
+ switch($this->messageType)
45
+ {
46
+ case self::NOTICE_ERROR:
47
+ $class .= 'notice-error ';
48
+ $icon = 'scared';
49
+ break;
50
+ case self::NOTICE_SUCCESS:
51
+ $class .= 'notice-success ';
52
+ break;
53
+ case self::NOTICE_WARNING:
54
+ $class .= 'notice-warning ';
55
+ break;
56
+ case self::NOTICE_NORMAL:
57
+ default:
58
+ $class .= 'notice-info ';
59
+ break;
60
+ }
61
+
62
+ $image = '<img src="' . plugins_url('/shortpixel-image-optimiser/res/img/robo-' . $icon . '.png') . '"
63
+ srcset="' . plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '.png' ) . ' 1x, ' . plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '@2x.png') . ' 2x" class="short-pixel-notice-icon">';
64
+
65
+
66
+ if ($this->is_removable)
67
+ {
68
+ $class .= 'is-dismissible ';
69
+ }
70
+
71
+ if ($this->is_persistent)
72
+ {
73
+ $class .= '';
74
+ }
75
+
76
+ return "<div class='$class'>" . $image . "<p>" . $this->message . "</p></div>";
77
+
78
+ }
79
+
80
+
81
+
82
+ // @todo Transient save, since that is used in some parts.
83
+ // save
84
+ // load
85
+
86
+
87
+ }
class/model/shortpixel-debug.php ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // The data models.
3
+ namespace ShortPixel;
4
+
5
+
6
+ class DebugItem // extends ShortPixelModel Too early init for this.
7
+ {
8
+ protected $time;
9
+ protected $level;
10
+ protected $message;
11
+ protected $data = array();
12
+ protected $caller = false; // array when filled
13
+
14
+ protected $model;
15
+
16
+ const LEVEL_ERROR = 1;
17
+ const LEVEL_WARN = 2;
18
+ const LEVEL_INFO = 3;
19
+ const LEVEL_DEBUG = 4;
20
+
21
+ public function __construct($message, $args)
22
+ {
23
+ $this->level = $args['level'];
24
+ $data = $args['data'];
25
+
26
+ $this->message = $message;
27
+ $this->time = microtime(true);
28
+
29
+ $this->setCaller();
30
+
31
+ // Add message to data if it seems to be some debug variable.
32
+ if (is_object($this->message) || is_array($this->message))
33
+ {
34
+ $data[] = $this->message;
35
+ $this->message = __('[Data]');
36
+ }
37
+ if (is_array($data) && count($data) > 0)
38
+ {
39
+ $dataType = $this->getDataType($data);
40
+ if ($dataType == 1) // singular
41
+ {
42
+ $this->data[] = print_r($data, true);
43
+ }
44
+ if ($dataType == 2) //array
45
+ {
46
+ foreach($data as $index => $item)
47
+ {
48
+ if (is_object($item) || is_array($item))
49
+ {
50
+ $this->data[] = print_r($item, true);
51
+ }
52
+ }
53
+ }
54
+ } // if
55
+ elseif (! is_array($data)) // this leaves out empty default arrays
56
+ {
57
+ $this->data[] = print_r($data, true);
58
+ }
59
+ }
60
+
61
+ public function getData()
62
+ {
63
+ return array('time' => $this->time, 'level' => $this->level, 'message' => $this->message, 'data' => $this->data, 'caller' => $this->caller);
64
+ }
65
+
66
+ /** Test Data Array for possible values
67
+ *
68
+ * Data can be a collection of several debug vars, a single var, or just an normal array. Test if array has single types,
69
+ * which is a sign the array is not a collection.
70
+ */
71
+ protected function getDataType($data)
72
+ {
73
+ $single_type = array('integer', 'boolean', 'string');
74
+ if (in_array(gettype(reset($data)), $single_type))
75
+ {
76
+ return 1;
77
+ }
78
+ else
79
+ {
80
+ return 2;
81
+ }
82
+ }
83
+
84
+ public function getForFormat()
85
+ {
86
+ $data = $this->getData();
87
+ switch($this->level)
88
+ {
89
+ case self::LEVEL_ERROR:
90
+ $level = 'ERR';
91
+ $color = "\033[31m";
92
+ break;
93
+ case self::LEVEL_WARN:
94
+ $level = 'WRN';
95
+ $color = "\033[33m";
96
+ break;
97
+ case self::LEVEL_INFO:
98
+ $level = 'INF';
99
+ $color = "\033[37m";
100
+ break;
101
+ case self::LEVEL_DEBUG:
102
+ $level = 'DBG';
103
+ $color = "\033[37m";
104
+ break;
105
+
106
+ }
107
+ $color_end = "\033[0m";
108
+
109
+ $data['color'] = $color;
110
+ $data['color_end'] = $color_end;
111
+ $data['level'] = $level;
112
+
113
+ return $data;
114
+
115
+ //return array('time' => $this->time, 'level' => $level, 'message' => $this->message, 'data' => $this->data, 'color' => $color, 'color_end' => $color_end, 'caller' => $this->caller);
116
+
117
+ }
118
+
119
+ protected function setCaller()
120
+ {
121
+ if(PHP_VERSION_ID < 50400) {
122
+ $debug=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
123
+ } else {
124
+ $debug=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,5);
125
+ }
126
+ $i = 4;
127
+ if (isset($debug[$i]))
128
+ {
129
+ $info = $debug[$i];
130
+ $line = isset($info['line']) ? $info['line'] : 'Line unknown';
131
+ $file = isset($info['file']) ? basename($info['file']) : 'File not set';
132
+
133
+ $this->caller = array('line' => $line, 'file' => $file, 'function' => $info['function']);
134
+ }
135
+
136
+
137
+ }
138
+
139
+
140
+ }
class/model/shortpixel-folder.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class ShortPixelFolder extends ShortPixelEntity{
4
  const META_VERSION = 1;
5
-
6
  protected $id;
7
  protected $path;
8
  protected $type;
@@ -10,28 +10,56 @@ class ShortPixelFolder extends ShortPixelEntity{
10
  protected $fileCount;
11
  protected $tsCreated;
12
  protected $tsUpdated;
13
-
14
  protected $excludePatterns;
15
-
16
  const TABLE_SUFFIX = 'folders';
17
-
18
  public function __construct($data, $excludePatterns = false) {
19
  parent::__construct($data);
20
  $this->excludePatterns = $excludePatterns;
21
  }
22
-
23
  public static function checkFolder($folder, $base) {
24
  if(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && substr($folder, 0, 1) !== '/') {
25
  $folder = '/' . $folder;
26
  }
27
  if(is_dir($folder)) {
28
  return $folder;
29
- } elseif(is_dir($base . $folder)) {
30
  return $base . $folder;
31
  }
32
  return false;
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  public static function deleteFolder($dirname) {
36
  if (is_dir($dirname))
37
  $dir_handle = opendir($dirname);
@@ -49,7 +77,7 @@ class ShortPixelFolder extends ShortPixelEntity{
49
  @rmdir($dirname);
50
  return true;
51
  }
52
-
53
  /**
54
  * returns the first from parents that is a parent folder of $folder
55
  * @param string $folder
@@ -67,7 +95,7 @@ class ShortPixelFolder extends ShortPixelEntity{
67
  }
68
  return false;
69
  }
70
-
71
  /**
72
  * finds the first from the subfolders that has the folder as parent
73
  * @param string $folder
@@ -104,11 +132,11 @@ class ShortPixelFolder extends ShortPixelEntity{
104
  $size += $this->countFiles($tpath);
105
  } elseif(WPShortPixel::_isProcessablePath($tpath, array(), $this->excludePatterns)) {
106
  $size++;
107
- }
108
  }
109
  return $size;
110
- }
111
-
112
  public function getFileList($onlyNewerThan = 0) {
113
  $fileListPath = tempnam(SHORTPIXEL_UPLOADS_BASE . '/', 'sp_');
114
  $fileHandle = fopen($fileListPath, 'w+');
@@ -116,14 +144,14 @@ class ShortPixelFolder extends ShortPixelEntity{
116
  fclose($fileHandle);
117
  return $fileListPath;
118
  }
119
-
120
  protected static function getFileListRecursive($path, $fileHandle, $onlyNewerThan) {
121
  $ignore = array('.','..');
122
  $files = scandir($path);
123
  $add = (filemtime($path) > $onlyNewerThan);
124
  /*
125
  if($add && $onlyNewerThan) {
126
- echo("<br> FOLDER UPDATED: " . $path);
127
  }
128
  */
129
  foreach($files as $t) {
@@ -133,16 +161,16 @@ class ShortPixelFolder extends ShortPixelEntity{
133
  self::getFileListRecursive($tpath, $fileHandle, $onlyNewerThan);
134
  } elseif($add && WPShortPixel::_isProcessablePath($tpath, array(), WPShortPixelSettings::getOpt('excludePatterns'))) {
135
  fwrite($fileHandle, $tpath . "\n");
136
- }
137
  }
138
  }
139
-
140
  public function checkFolderContents($callback) {
141
  $changed = array();
142
  self::checkFolderContentsRecursive($this->getPath(), $changed, $callback);
143
  return $changed;
144
  }
145
-
146
  protected static function checkFolderContentsRecursive($path, &$changed, $callback) {
147
  $ignore = array('.','..');
148
  $files = scandir($path);
@@ -155,14 +183,14 @@ class ShortPixelFolder extends ShortPixelEntity{
155
  } elseif( WPShortPixel::_isProcessablePath($tpath, array(), WPShortPixelSettings::getOpt('excludePatterns'))
156
  && !(in_array($tpath, $reference) && $reference[$tpath]->compressedSize == filesize($tpath))) {
157
  $changed[] = $tpath;
158
- }
159
  }
160
  }
161
-
162
  public function getFolderContentsChangeDate() {
163
  return self::getFolderContentsChangeDateRecursive($this->getPath(), 0, strtotime($this->getTsUpdated()));
164
  }
165
-
166
  protected static function getFolderContentsChangeDateRecursive($path, $mtime, $refMtime) {
167
  $ignore = array('.','..');
168
  if(!is_writable($path)) {
@@ -176,11 +204,11 @@ class ShortPixelFolder extends ShortPixelEntity{
176
  if (is_dir($tpath)) {
177
  $mtime = max($mtime, filemtime($tpath));
178
  self::getFolderContentsChangeDateRecursive($tpath, $mtime, $refMtime);
179
- }
180
  }
181
  return $mtime;
182
- }
183
-
184
  function getId() {
185
  return $this->id;
186
  }
@@ -236,7 +264,7 @@ class ShortPixelFolder extends ShortPixelEntity{
236
  function setTsUpdated($tsUpdated) {
237
  $this->tsUpdated = $tsUpdated;
238
  }
239
-
240
  /**
241
  * needed as callback
242
  * @param ShortPixelFolder $item
2
 
3
  class ShortPixelFolder extends ShortPixelEntity{
4
  const META_VERSION = 1;
5
+
6
  protected $id;
7
  protected $path;
8
  protected $type;
10
  protected $fileCount;
11
  protected $tsCreated;
12
  protected $tsUpdated;
13
+
14
  protected $excludePatterns;
15
+
16
  const TABLE_SUFFIX = 'folders';
17
+
18
  public function __construct($data, $excludePatterns = false) {
19
  parent::__construct($data);
20
  $this->excludePatterns = $excludePatterns;
21
  }
22
+
23
  public static function checkFolder($folder, $base) {
24
  if(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && substr($folder, 0, 1) !== '/') {
25
  $folder = '/' . $folder;
26
  }
27
  if(is_dir($folder)) {
28
  return $folder;
29
+ } elseif(is_dir($base . $folder)) {
30
  return $base . $folder;
31
  }
32
  return false;
33
  }
34
 
35
+ /** This creates the general backup folder **/
36
+ public static function createBackUpFolder($folder = SHORTPIXEL_BACKUP_FOLDER)
37
+ {
38
+ // create backup folder
39
+ $result = @mkdir($folder, 0777, true);
40
+
41
+ if ($result)
42
+ {
43
+ self::protectDirectoryListing($folder);
44
+ }
45
+
46
+ return $result;
47
+ }
48
+
49
+ public static function protectDirectoryListing($dirname)
50
+ {
51
+ $rules = "Options -Indexes";
52
+ /* Plugin init is before loading these admin scripts. So it can happen misc.php is not yet loaded */
53
+ if (! function_exists('insert_with_markers'))
54
+ {
55
+ require_once( ABSPATH . 'wp-admin/includes/misc.php' );
56
+ }
57
+ insert_with_markers( trailingslashit($dirname) . '.htaccess', 'ShortPixel', $rules);
58
+ // note - this doesn't bring the same protection. Subdirs without files written will still be listable.
59
+ file_put_contents(trailingslashit($dirname) . 'index.html', chr(0)); // extra - for non-apache
60
+
61
+ }
62
+
63
  public static function deleteFolder($dirname) {
64
  if (is_dir($dirname))
65
  $dir_handle = opendir($dirname);
77
  @rmdir($dirname);
78
  return true;
79
  }
80
+
81
  /**
82
  * returns the first from parents that is a parent folder of $folder
83
  * @param string $folder
95
  }
96
  return false;
97
  }
98
+
99
  /**
100
  * finds the first from the subfolders that has the folder as parent
101
  * @param string $folder
132
  $size += $this->countFiles($tpath);
133
  } elseif(WPShortPixel::_isProcessablePath($tpath, array(), $this->excludePatterns)) {
134
  $size++;
135
+ }
136
  }
137
  return $size;
138
+ }
139
+
140
  public function getFileList($onlyNewerThan = 0) {
141
  $fileListPath = tempnam(SHORTPIXEL_UPLOADS_BASE . '/', 'sp_');
142
  $fileHandle = fopen($fileListPath, 'w+');
144
  fclose($fileHandle);
145
  return $fileListPath;
146
  }
147
+
148
  protected static function getFileListRecursive($path, $fileHandle, $onlyNewerThan) {
149
  $ignore = array('.','..');
150
  $files = scandir($path);
151
  $add = (filemtime($path) > $onlyNewerThan);
152
  /*
153
  if($add && $onlyNewerThan) {
154
+ echo("<br> FOLDER UPDATED: " . $path);
155
  }
156
  */
157
  foreach($files as $t) {
161
  self::getFileListRecursive($tpath, $fileHandle, $onlyNewerThan);
162
  } elseif($add && WPShortPixel::_isProcessablePath($tpath, array(), WPShortPixelSettings::getOpt('excludePatterns'))) {
163
  fwrite($fileHandle, $tpath . "\n");
164
+ }
165
  }
166
  }
167
+
168
  public function checkFolderContents($callback) {
169
  $changed = array();
170
  self::checkFolderContentsRecursive($this->getPath(), $changed, $callback);
171
  return $changed;
172
  }
173
+
174
  protected static function checkFolderContentsRecursive($path, &$changed, $callback) {
175
  $ignore = array('.','..');
176
  $files = scandir($path);
183
  } elseif( WPShortPixel::_isProcessablePath($tpath, array(), WPShortPixelSettings::getOpt('excludePatterns'))
184
  && !(in_array($tpath, $reference) && $reference[$tpath]->compressedSize == filesize($tpath))) {
185
  $changed[] = $tpath;
186
+ }
187
  }
188
  }
189
+
190
  public function getFolderContentsChangeDate() {
191
  return self::getFolderContentsChangeDateRecursive($this->getPath(), 0, strtotime($this->getTsUpdated()));
192
  }
193
+
194
  protected static function getFolderContentsChangeDateRecursive($path, $mtime, $refMtime) {
195
  $ignore = array('.','..');
196
  if(!is_writable($path)) {
204
  if (is_dir($tpath)) {
205
  $mtime = max($mtime, filemtime($tpath));
206
  self::getFolderContentsChangeDateRecursive($tpath, $mtime, $refMtime);
207
+ }
208
  }
209
  return $mtime;
210
+ }
211
+
212
  function getId() {
213
  return $this->id;
214
  }
264
  function setTsUpdated($tsUpdated) {
265
  $this->tsUpdated = $tsUpdated;
266
  }
267
+
268
  /**
269
  * needed as callback
270
  * @param ShortPixelFolder $item
class/model/shortpixel-image.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // NOT FOR COMMIT
3
+ namespace shortPixel;
4
+
5
+ /* Image class.
6
+ *
7
+ *
8
+ * - Represents a -single- image.
9
+ * - Can handle any type
10
+ * - Usually controllers would use a collection of images
11
+ * -
12
+ *
13
+ */
14
+ class shortPixelImage
15
+ {
16
+
17
+ protected $meta; // MetaFacade
18
+
19
+ public function __construct($path)
20
+ {
21
+
22
+ }
23
+
24
+
25
+ }
26
+
27
+ /*
28
+ // do this before putting the meta down, since maybeDump check for last timestamp
29
+ $URLsAndPATHs = $itemHandler->getURLsAndPATHs(false);
30
+ $this->maybeDumpFromProcessedOnServer($itemHandler, $URLsAndPATHs);
31
+
32
+ */
class/shortpixel-model.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger as Log;
4
+ use ShortPixel\DebugItem as DebugItem;
5
+
6
+
7
+ abstract class ShortPixelModel
8
+ {
9
+ protected $model = array();
10
+
11
+ public function getData()
12
+ {
13
+ $data = array();
14
+ foreach($this->model as $item => $options)
15
+ {
16
+ $data[$item] = $this->{$item};
17
+ }
18
+ return $data;
19
+ }
20
+
21
+ public function getModel()
22
+ {
23
+ return array_keys($this->model); // only the variable names are public.
24
+ }
25
+
26
+ protected function sanitize($name, $value)
27
+ {
28
+ if (! isset($this->model[$name]))
29
+ return null;
30
+
31
+ // if no sanitize method is set, default to strictest string.
32
+ $sanitize = isset($this->model[$name]['s']) ? $this->model[$name]['s'] : 'string';
33
+ switch($sanitize)
34
+ {
35
+ case "string":
36
+ $value = $this->sanitizeString($value);
37
+ break;
38
+ case "int":
39
+ $value = $this->sanitizeInteger($value);
40
+ break;
41
+ case "boolean":
42
+ $value = $this->sanitizeBoolean($value);
43
+ break;
44
+ case 'array':
45
+ case 'Array':
46
+ $value = $this->sanitizeArray($value);
47
+ break;
48
+ case 'exception': // for exceptional situations. The data will not be sanitized! Need to do this elsewhere.
49
+ return $value;
50
+ break;
51
+ case 'skip': // skips should not be in any save candidate and not be sanitized.
52
+ return null;
53
+ break;
54
+ }
55
+
56
+ return $value;
57
+ }
58
+
59
+ /** Sanitize the passed post data against the model attribute formats.
60
+ *
61
+ * @param Array $post The Post data
62
+ * @param boolean $missing If fields are missing, include them empty in the output
63
+ * @return Array Sanitized Post Data
64
+ */
65
+ public function getSanitizedData($post, $missing = true)
66
+ {
67
+ $postData = array();
68
+ foreach($post as $name => $value)
69
+ {
70
+ $name = sanitize_text_field($name);
71
+ $value = $this->sanitize($name, $value);
72
+ if ($value !== null)
73
+ $postData[$name] = $value;
74
+ else {
75
+ Log::addWarn("Provided field $name not part of model " . get_class() );
76
+ }
77
+ }
78
+
79
+ if ($missing)
80
+ {
81
+ $model_fields = $this->getModel();
82
+ $post_fields = array_keys($postData);
83
+
84
+ $missing_fields = array_diff($model_fields, $post_fields);
85
+ foreach($missing_fields as $index => $field_name)
86
+ {
87
+ $field_name = sanitize_text_field($field_name);
88
+ $type = $this->getType($field_name);
89
+ if ($type === 'boolean')
90
+ {
91
+ $postData[$field_name] = 0;
92
+ }
93
+ elseif ($type !== false && $type !== 'skip')
94
+ {
95
+ $postData[$field_name] = '';
96
+ }
97
+
98
+ }
99
+ }
100
+
101
+ return $postData;
102
+ }
103
+
104
+
105
+ public function getType($name)
106
+ {
107
+ if (! isset($this->model[$name]))
108
+ {
109
+ return null;
110
+ Log::addWarn("Provided field $name not part of model " . get_class() );
111
+ }
112
+
113
+
114
+ $type = isset($this->model[$name]['s']) ? $this->model[$name]['s'] : false;
115
+ return $type;
116
+ }
117
+
118
+ public function sanitizeString($string)
119
+ {
120
+ return (string) sanitize_text_field($string);
121
+ }
122
+ public function sanitizeInteger($int)
123
+ {
124
+ return intval($int);
125
+ }
126
+ public function sanitizeBoolean($bool)
127
+ {
128
+ return ($bool) ? true : false;
129
+ }
130
+
131
+ public function sanitizeArray($array)
132
+ {
133
+ if (! is_array($array))
134
+ {
135
+ Log::addWarn('Field is of type Array, but Array not provided');
136
+ return null;
137
+ }
138
+ $new_array = array();
139
+ foreach($array as $key => $value)
140
+ {
141
+ $newkey = $this->sanitizeString($key);
142
+ $newval = $this->sanitizeString($value);
143
+ $new_array[$newkey] = $newval ;
144
+ }
145
+
146
+ return $new_array;
147
+ }
148
+
149
+ }
class/shortpixel-tools.php CHANGED
@@ -23,7 +23,7 @@ class ShortPixelTools {
23
 
24
  public static function namespaceit($name)
25
  {
26
- return '\ShortPixel\\' . $name;
27
  }
28
 
29
  public static function requestIsFrontendAjax()
@@ -88,6 +88,7 @@ class ShortPixelTools {
88
  public static function sendJSON($response) {
89
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
90
  die(json_encode($response));
 
91
  }
92
 
93
 
23
 
24
  public static function namespaceit($name)
25
  {
26
+ return '\ShortPixel\\' . $name;
27
  }
28
 
29
  public static function requestIsFrontendAjax()
88
  public static function sendJSON($response) {
89
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
90
  die(json_encode($response));
91
+ //wp_send_json($response); // send json proper, dies.
92
  }
93
 
94
 
class/shortpixel_queue.php CHANGED
@@ -1,10 +1,10 @@
1
  <?php
2
 
3
  class ShortPixelQueue {
4
-
5
  private $ctrl;
6
  private $settings;
7
-
8
  const BULK_TYPE_OPTIMIZE = 0;
9
  const BULK_TYPE_RESTORE = 1;
10
  const BULK_TYPE_CLEANUP = 2;
@@ -14,12 +14,12 @@ class ShortPixelQueue {
14
  const BULK_RUNNING = 1; //bulk is running
15
  const BULK_PAUSED = 2; //bulk is paused
16
  const BULK_FINISHED = 3; //bulk finished
17
-
18
  public function __construct($controller, $settings) {
19
  $this->ctrl = $controller;
20
  $this->settings = $settings;
21
  }
22
-
23
  //handling older
24
  public function ShortPixelQueue($controller) {
25
  $this->__construct($controller);
@@ -88,42 +88,42 @@ class ShortPixelQueue {
88
  protected static function parseQ($items) {
89
  return explode(',', preg_replace("/[^0-9,C-]/", "", $items));
90
  }
91
-
92
  public function skip($id) {
93
  if(is_array($this->settings->prioritySkip)) {
94
  $this->settings->prioritySkip = array_merge($this->settings->prioritySkip, array($id));
95
  } else {
96
  $this->settings->prioritySkip = array($id);
97
- }
98
  }
99
 
100
  public function unskip($id) {
101
  $prioSkip = $this->settings->prioritySkip;
102
  $this->settings->prioritySkip = is_array($prioSkip) ? array_diff($prioSkip, array($id)) : array();
103
  }
104
-
105
  public function allSkipped() {
106
  if( !is_array($this->settings->prioritySkip) ) return false;
107
  count(array_diff($this->get(), $this->settings->prioritySkip));
108
  }
109
-
110
  public function skippedCount() {
111
- return is_array($this->settings->prioritySkip) ? count($this->settings->prioritySkip) : 0;
112
  }
113
-
114
  public function isSkipped($id) {
115
  return is_array($this->settings->prioritySkip) && in_array($id, $this->settings->prioritySkip);
116
  }
117
-
118
  public function isPrio($id) {
119
  $prioItems = $this->get();
120
  return is_array($prioItems) && in_array($id, $prioItems);
121
  }
122
-
123
  public function getSkipped() {
124
  return $this->settings->prioritySkip;
125
  }
126
-
127
  public function reverse() {
128
  $this->apply('array_reverse');
129
  //$this->settings->priorityQueue = $_SESSION["wp-short-pixel-priorityQueue"] = array_reverse($_SESSION["wp-short-pixel-priorityQueue"]);
@@ -162,7 +162,7 @@ class ShortPixelQueue {
162
  $count = min(count($priorityQueue), $count);
163
  return(array_slice($priorityQueue, count($priorityQueue) - $count, $count));
164
  }
165
-
166
  public function getFromPrioAndCheck() {
167
  $idsPrio = $this->get();
168
 
@@ -183,7 +183,7 @@ class ShortPixelQueue {
183
  $this->remove($rId);
184
  }
185
  return $ids;
186
- }
187
 
188
  public function remove($ID)//remove an ID from priority queue
189
  {
@@ -210,7 +210,7 @@ class ShortPixelQueue {
210
  $this->closeQ($fp);
211
  return $found;
212
  }
213
-
214
  public function removeFromFailed($ID) {
215
  $failed = explode(",", $this->settings->failedImages);
216
  $key = array_search($ID, $failed);
@@ -218,14 +218,14 @@ class ShortPixelQueue {
218
  unset($failed[$key]);
219
  $failed = array_values($failed);
220
  $this->settings->failedImages = implode(",", $failed) ;
221
- }
222
  }
223
-
224
  public function addToFailed($ID) {
225
  $failed = $this->settings->failedImages;
226
  if(!in_array($ID, explode(",", $failed))) {
227
  $this->settings->failedImages = (strlen($failed) ? $failed . "," : "") . $ID;
228
- }
229
  }
230
 
231
  public function getFailed() {
@@ -233,11 +233,11 @@ class ShortPixelQueue {
233
  if(!strlen($failed)) return array();
234
  $ret = explode(",", $failed);
235
  $fails = array();
236
- foreach($ret as $fail) {
237
  if(ShortPixelMetaFacade::isCustomQueuedId($fail)) {
238
  $meta = $this->ctrl->getSpMetaDao()->getMeta(ShortPixelMetaFacade::stripQueuedIdType($fail));
239
  if($meta) {
240
- $fails[] = (object)array("id" => ShortPixelMetaFacade::stripQueuedIdType($fail), "type" => ShortPixelMetaFacade::CUSTOM_TYPE, "meta" => $meta);
241
  }
242
  } else {
243
  $meta = wp_get_attachment_metadata($fail);
@@ -255,21 +255,21 @@ class ShortPixelQueue {
255
  //$bulkProcessingStatus = get_option('bulkProcessingStatus');
256
  return $this->settings->startBulkId > $this->settings->stopBulkId;
257
  }
258
-
259
  public function bulkPaused() {
260
  //WPShortPixel::log("Bulk Paused: " . $this->settings->cancelPointer);
261
  return $this->settings->cancelPointer;
262
  }
263
-
264
  public function bulkRan() {
265
  return $this->settings->bulkEverRan != 0;
266
  }
267
-
268
  public function processing() {
269
  //WPShortPixel::log("QUEUE: processing(): get:" . json_encode($this->get()));
270
  return $this->bulkRunning() || count($this->get());
271
  }
272
-
273
  public function getFlagBulkId() {
274
  return $this->settings->flagId;
275
  }
@@ -281,7 +281,7 @@ class ShortPixelQueue {
281
  public function resetStartBulkId() {
282
  $this->setStartBulkId(ShortPixelMetaFacade::getMaxMediaId());
283
  }
284
-
285
  public function setStartBulkId($start){
286
  $this->settings->startBulkId = $start;
287
  }
@@ -293,12 +293,12 @@ class ShortPixelQueue {
293
  public function resetStopBulkId() {
294
  $this->settings->stopBulkId = ShortPixelMetaFacade::getMinMediaId();
295
  }
296
-
297
  public function setBulkPreviousPercent() {
298
  //processable and already processed
299
  $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->settings, $this->getFlagBulkId(), $this->settings->stopBulkId);
300
  $this->settings->bulkCount = $res["mainFiles"];
301
-
302
  //if compression type changed, add also the images with the other compression type
303
  switch (0 + $this->ctrl->getCompressionType()) {
304
  case 2:
@@ -310,7 +310,7 @@ class ShortPixelQueue {
310
  default: //lossless
311
  $this->settings->bulkAlreadyDoneCount = $res["mainProcessedFiles"] - $res["mainProcLossyFiles"] - $res["mainProcGlossyFiles"];
312
  break;
313
-
314
  }
315
  //$this->settings->bulkAlreadyDoneCount = $res["mainProcessedFiles"] - $res["mainProc".((0 + $this->ctrl->getCompressionType() == 1) ? "Lossless" : "Lossy")."Files"];
316
 
@@ -318,41 +318,49 @@ class ShortPixelQueue {
318
  if($this->settings->processThumbnails) {
319
  $this->settings->bulkAlreadyDoneCount -= $res["mainUnprocessedThumbs"];
320
  }
321
-
322
  //percent already done
323
  $this->settings->bulkPreviousPercent = round($this->settings->bulkAlreadyDoneCount / ($this->settings->bulkCount ? $this->settings->bulkCount : 1) * 100);
324
  }
325
-
326
  public function getBulkToProcess() {
327
  //check numeric as per https://secure.helpscout.net/conversation/764815647/12934?folderId=1117588
328
  if(!is_numeric($this->settings->bulkCount)) $this->settings->bulkCount = 0;
329
  if(!is_numeric($this->settings->bulkAlreadyDoneCount)) $this->settings->bulkAlreadyDoneCount = 0;
330
  return $this->settings->bulkCount - $this->settings->bulkAlreadyDoneCount;
331
  }
332
-
333
  public function flagBulkStart() {
334
  $this->settings->flagId = $this->settings->startBulkId;
335
- $this->settings->bulkProcessingStatus = 'running';//set bulk flag
336
  }
337
-
338
  public function setBulkType($type) {
339
  $this->settings->bulkType = $type;
340
  }
341
-
342
  public function getBulkType() {
343
  return $this->settings->bulkType;
344
  }
345
-
 
 
 
 
 
 
 
 
346
  public function startBulk($type = self::BULK_TYPE_OPTIMIZE) {
347
- $this->resetStartBulkId(); //start downwards from the biggest item ID
348
  $this->resetStopBulkId();
349
- $this->flagBulkStart(); //we use this to detect new added files while bulk is running
350
  $this->setBulkPreviousPercent();
351
  $this->resetBulkCurrentlyProcessed();
352
  $this->setBulkType($type);
353
  $this->settings->bulkEverRan = 1;
354
  }
355
-
356
  public function pauseBulk() {
357
  $cancelPointer = $this->settings->startBulkId;
358
  $bulkStartId = $this->getFlagBulkId();
@@ -367,50 +375,51 @@ class ShortPixelQueue {
367
  }
368
  $this->stopBulk();
369
  }
370
-
371
  public function cancelBulk() {
372
  $this->pauseBulk();
373
  WPShortPixel::log("STOP, delete pointer.");
374
  $this->settings->cancelPointer = NULL;
375
  }
376
-
377
  public function stopBulk() {
378
  $this->settings->startBulkId = ShortPixelMetaFacade::getMaxMediaId();
379
  $this->settings->stopBulkId = $this->settings->startBulkId;
380
  $this->settings->bulkProcessingStatus = null;
381
  return $this->settings->bulkEverRan;
382
  }
383
-
384
  public function resumeBulk() {
385
  $this->settings->startBulkId = $this->settings->cancelPointer;
386
  $this->settings->stopBulkId = ShortPixelMetaFacade::getMinMediaId();
387
  //$this->settings->setOpt("wp-short-pixel-flag-id", $this->startBulkId);//we use to detect new added files while bulk is running
388
- $this->settings->bulkProcessingStatus = 'running';//set bulk flag
389
  $this->settings->cancelPointer = null;
390
  WPShortPixel::log("Resumed: (pause says: " . $this->bulkPaused() . ") Start from: " . $this->settings->startBulkId . " to " . $this->settings->stopBulkId);
391
  }
392
-
393
  public function resetBulkCurrentlyProcessed() {
394
  $this->settings->bulkCurrentlyProcessed = 0;
395
  }
396
-
397
  public function incrementBulkCurrentlyProcessed() {
398
  $this->settings->bulkCurrentlyProcessed = $this->settings->bulkCurrentlyProcessed + 1;
399
  }
400
-
401
  public function markBulkComplete() {
402
  $this->settings->bulkProcessingStatus = null;
403
  $this->settings->cancelPointer = null;
 
404
  }
405
-
406
  public static function resetBulk() {
407
- delete_option('wp-short-pixel-bulk-type');
408
- delete_option('bulkProcessingStatus');
409
  delete_option( 'wp-short-pixel-cancel-pointer');
410
  delete_option( "wp-short-pixel-flag-id");
411
  $startBulkId = $stopBulkId = ShortPixelMetaFacade::getMaxMediaId();
412
  update_option( 'wp-short-pixel-query-id-stop', $startBulkId, 'no');
413
- update_option( 'wp-short-pixel-query-id-start', $startBulkId, 'no');
414
  delete_option( "wp-short-pixel-bulk-previous-percent");
415
  delete_option( "wp-short-pixel-bulk-processed-items");
416
  delete_option('wp-short-pixel-bulk-running-time');
@@ -420,12 +429,12 @@ class ShortPixelQueue {
420
  delete_option( "wp-short-pixel-bulk-count");
421
  delete_option( "wp-short-pixel-bulk-done-count");
422
  }
423
-
424
  public static function resetPrio() {
425
  //delete_option( "wp-short-pixel-priorityQueue");
426
  self::set(array());
427
  }
428
-
429
  public function logBulkProgress() {
430
  $t = time();
431
  $this->incrementBulkCurrentlyProcessed();
@@ -438,26 +447,26 @@ class ShortPixelQueue {
438
  $this->settings->lastBulkSuccessTime = $t;
439
  }
440
  }
441
-
442
  public function getBulkPercent() {
443
  $previousPercent = $this->settings->bulkPreviousPercent;
444
  //WPShortPixel::log("QUEUE - BulkPrevPercent: " . $previousPercent . " BulkCurrentlyProcessing: "
445
  // . $this->settings->bulkCurrentlyProcessed . " out of " . $this->getBulkToProcess());
446
-
447
  if($this->getBulkToProcess() <= 0) return ($this->processing () ? 99: 100);
448
  // return maximum 99%
449
  $percent = $previousPercent + round($this->settings->bulkCurrentlyProcessed / $this->getBulkToProcess()
450
  * (100 - $previousPercent));
451
 
452
  //WPShortPixel::log("QUEUE - Calculated Percent: " . $percent);
453
-
454
  return min(99, $percent);
455
  }
456
 
457
  public function getDeltaBulkPercent() {
458
  return $this->getBulkPercent() - $this->settings->bulkPreviousPercent;
459
  }
460
-
461
  public function getTimeRemaining (){
462
  $p = $this->getBulkPercent();
463
  $pAlready = $this->settings->bulkCount == 0 ? 0 : round($this->settings->bulkAlreadyDoneCount / $this->settings->bulkCount * 100);
1
  <?php
2
 
3
  class ShortPixelQueue {
4
+
5
  private $ctrl;
6
  private $settings;
7
+
8
  const BULK_TYPE_OPTIMIZE = 0;
9
  const BULK_TYPE_RESTORE = 1;
10
  const BULK_TYPE_CLEANUP = 2;
14
  const BULK_RUNNING = 1; //bulk is running
15
  const BULK_PAUSED = 2; //bulk is paused
16
  const BULK_FINISHED = 3; //bulk finished
17
+
18
  public function __construct($controller, $settings) {
19
  $this->ctrl = $controller;
20
  $this->settings = $settings;
21
  }
22
+
23
  //handling older
24
  public function ShortPixelQueue($controller) {
25
  $this->__construct($controller);
88
  protected static function parseQ($items) {
89
  return explode(',', preg_replace("/[^0-9,C-]/", "", $items));
90
  }
91
+
92
  public function skip($id) {
93
  if(is_array($this->settings->prioritySkip)) {
94
  $this->settings->prioritySkip = array_merge($this->settings->prioritySkip, array($id));
95
  } else {
96
  $this->settings->prioritySkip = array($id);
97
+ }
98
  }
99
 
100
  public function unskip($id) {
101
  $prioSkip = $this->settings->prioritySkip;
102
  $this->settings->prioritySkip = is_array($prioSkip) ? array_diff($prioSkip, array($id)) : array();
103
  }
104
+
105
  public function allSkipped() {
106
  if( !is_array($this->settings->prioritySkip) ) return false;
107
  count(array_diff($this->get(), $this->settings->prioritySkip));
108
  }
109
+
110
  public function skippedCount() {
111
+ return is_array($this->settings->prioritySkip) ? count($this->settings->prioritySkip) : 0;
112
  }
113
+
114
  public function isSkipped($id) {
115
  return is_array($this->settings->prioritySkip) && in_array($id, $this->settings->prioritySkip);
116
  }
117
+
118
  public function isPrio($id) {
119
  $prioItems = $this->get();
120
  return is_array($prioItems) && in_array($id, $prioItems);
121
  }
122
+
123
  public function getSkipped() {
124
  return $this->settings->prioritySkip;
125
  }
126
+
127
  public function reverse() {
128
  $this->apply('array_reverse');
129
  //$this->settings->priorityQueue = $_SESSION["wp-short-pixel-priorityQueue"] = array_reverse($_SESSION["wp-short-pixel-priorityQueue"]);
162
  $count = min(count($priorityQueue), $count);
163
  return(array_slice($priorityQueue, count($priorityQueue) - $count, $count));
164
  }
165
+
166
  public function getFromPrioAndCheck() {
167
  $idsPrio = $this->get();
168
 
183
  $this->remove($rId);
184
  }
185
  return $ids;
186
+ }
187
 
188
  public function remove($ID)//remove an ID from priority queue
189
  {
210
  $this->closeQ($fp);
211
  return $found;
212
  }
213
+
214
  public function removeFromFailed($ID) {
215
  $failed = explode(",", $this->settings->failedImages);
216
  $key = array_search($ID, $failed);
218
  unset($failed[$key]);
219
  $failed = array_values($failed);
220
  $this->settings->failedImages = implode(",", $failed) ;
221
+ }
222
  }
223
+
224
  public function addToFailed($ID) {
225
  $failed = $this->settings->failedImages;
226
  if(!in_array($ID, explode(",", $failed))) {
227
  $this->settings->failedImages = (strlen($failed) ? $failed . "," : "") . $ID;
228
+ }
229
  }
230
 
231
  public function getFailed() {
233
  if(!strlen($failed)) return array();
234
  $ret = explode(",", $failed);
235
  $fails = array();
236
+ foreach($ret as $fail) {
237
  if(ShortPixelMetaFacade::isCustomQueuedId($fail)) {
238
  $meta = $this->ctrl->getSpMetaDao()->getMeta(ShortPixelMetaFacade::stripQueuedIdType($fail));
239
  if($meta) {
240
+ $fails[] = (object)array("id" => ShortPixelMetaFacade::stripQueuedIdType($fail), "type" => ShortPixelMetaFacade::CUSTOM_TYPE, "meta" => $meta);
241
  }
242
  } else {
243
  $meta = wp_get_attachment_metadata($fail);
255
  //$bulkProcessingStatus = get_option('bulkProcessingStatus');
256
  return $this->settings->startBulkId > $this->settings->stopBulkId;
257
  }
258
+
259
  public function bulkPaused() {
260
  //WPShortPixel::log("Bulk Paused: " . $this->settings->cancelPointer);
261
  return $this->settings->cancelPointer;
262
  }
263
+
264
  public function bulkRan() {
265
  return $this->settings->bulkEverRan != 0;
266
  }
267
+
268
  public function processing() {
269
  //WPShortPixel::log("QUEUE: processing(): get:" . json_encode($this->get()));
270
  return $this->bulkRunning() || count($this->get());
271
  }
272
+
273
  public function getFlagBulkId() {
274
  return $this->settings->flagId;
275
  }
281
  public function resetStartBulkId() {
282
  $this->setStartBulkId(ShortPixelMetaFacade::getMaxMediaId());
283
  }
284
+
285
  public function setStartBulkId($start){
286
  $this->settings->startBulkId = $start;
287
  }
293
  public function resetStopBulkId() {
294
  $this->settings->stopBulkId = ShortPixelMetaFacade::getMinMediaId();
295
  }
296
+
297
  public function setBulkPreviousPercent() {
298
  //processable and already processed
299
  $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->settings, $this->getFlagBulkId(), $this->settings->stopBulkId);
300
  $this->settings->bulkCount = $res["mainFiles"];
301
+
302
  //if compression type changed, add also the images with the other compression type
303
  switch (0 + $this->ctrl->getCompressionType()) {
304
  case 2:
310
  default: //lossless
311
  $this->settings->bulkAlreadyDoneCount = $res["mainProcessedFiles"] - $res["mainProcLossyFiles"] - $res["mainProcGlossyFiles"];
312
  break;
313
+
314
  }
315
  //$this->settings->bulkAlreadyDoneCount = $res["mainProcessedFiles"] - $res["mainProc".((0 + $this->ctrl->getCompressionType() == 1) ? "Lossless" : "Lossy")."Files"];
316
 
318
  if($this->settings->processThumbnails) {
319
  $this->settings->bulkAlreadyDoneCount -= $res["mainUnprocessedThumbs"];
320
  }
321
+
322
  //percent already done
323
  $this->settings->bulkPreviousPercent = round($this->settings->bulkAlreadyDoneCount / ($this->settings->bulkCount ? $this->settings->bulkCount : 1) * 100);
324
  }
325
+
326
  public function getBulkToProcess() {
327
  //check numeric as per https://secure.helpscout.net/conversation/764815647/12934?folderId=1117588
328
  if(!is_numeric($this->settings->bulkCount)) $this->settings->bulkCount = 0;
329
  if(!is_numeric($this->settings->bulkAlreadyDoneCount)) $this->settings->bulkAlreadyDoneCount = 0;
330
  return $this->settings->bulkCount - $this->settings->bulkAlreadyDoneCount;
331
  }
332
+
333
  public function flagBulkStart() {
334
  $this->settings->flagId = $this->settings->startBulkId;
335
+ $this->settings->bulkProcessingStatus = 'running';//set bulk flag
336
  }
337
+
338
  public function setBulkType($type) {
339
  $this->settings->bulkType = $type;
340
  }
341
+
342
  public function getBulkType() {
343
  return $this->settings->bulkType;
344
  }
345
+
346
+ // hack
347
+ public function getBulkTypeForDisplay()
348
+ {
349
+ $bulk = $this->settings->bulkType;
350
+ $this->settings->bulkType = null;
351
+ return $bulk;
352
+ }
353
+
354
  public function startBulk($type = self::BULK_TYPE_OPTIMIZE) {
355
+ $this->resetStartBulkId(); //start downwards from the biggest item ID
356
  $this->resetStopBulkId();
357
+ $this->flagBulkStart(); //we use this to detect new added files while bulk is running
358
  $this->setBulkPreviousPercent();
359
  $this->resetBulkCurrentlyProcessed();
360
  $this->setBulkType($type);
361
  $this->settings->bulkEverRan = 1;
362
  }
363
+
364
  public function pauseBulk() {
365
  $cancelPointer = $this->settings->startBulkId;
366
  $bulkStartId = $this->getFlagBulkId();
375
  }
376
  $this->stopBulk();
377
  }
378
+
379
  public function cancelBulk() {
380
  $this->pauseBulk();
381
  WPShortPixel::log("STOP, delete pointer.");
382
  $this->settings->cancelPointer = NULL;
383
  }
384
+
385
  public function stopBulk() {
386
  $this->settings->startBulkId = ShortPixelMetaFacade::getMaxMediaId();
387
  $this->settings->stopBulkId = $this->settings->startBulkId;
388
  $this->settings->bulkProcessingStatus = null;
389
  return $this->settings->bulkEverRan;
390
  }
391
+
392
  public function resumeBulk() {
393
  $this->settings->startBulkId = $this->settings->cancelPointer;
394
  $this->settings->stopBulkId = ShortPixelMetaFacade::getMinMediaId();
395
  //$this->settings->setOpt("wp-short-pixel-flag-id", $this->startBulkId);//we use to detect new added files while bulk is running
396
+ $this->settings->bulkProcessingStatus = 'running';//set bulk flag
397
  $this->settings->cancelPointer = null;
398
  WPShortPixel::log("Resumed: (pause says: " . $this->bulkPaused() . ") Start from: " . $this->settings->startBulkId . " to " . $this->settings->stopBulkId);
399
  }
400
+
401
  public function resetBulkCurrentlyProcessed() {
402
  $this->settings->bulkCurrentlyProcessed = 0;
403
  }
404
+
405
  public function incrementBulkCurrentlyProcessed() {
406
  $this->settings->bulkCurrentlyProcessed = $this->settings->bulkCurrentlyProcessed + 1;
407
  }
408
+
409
  public function markBulkComplete() {
410
  $this->settings->bulkProcessingStatus = null;
411
  $this->settings->cancelPointer = null;
412
+ // $this->settings->bulkType = null;
413
  }
414
+
415
  public static function resetBulk() {
416
+ delete_option('wp-short-pixel-bulk-type');
417
+ delete_option('bulkProcessingStatus');
418
  delete_option( 'wp-short-pixel-cancel-pointer');
419
  delete_option( "wp-short-pixel-flag-id");
420
  $startBulkId = $stopBulkId = ShortPixelMetaFacade::getMaxMediaId();
421
  update_option( 'wp-short-pixel-query-id-stop', $startBulkId, 'no');
422
+ update_option( 'wp-short-pixel-query-id-start', $startBulkId, 'no');
423
  delete_option( "wp-short-pixel-bulk-previous-percent");
424
  delete_option( "wp-short-pixel-bulk-processed-items");
425
  delete_option('wp-short-pixel-bulk-running-time');
429
  delete_option( "wp-short-pixel-bulk-count");
430
  delete_option( "wp-short-pixel-bulk-done-count");
431
  }
432
+
433
  public static function resetPrio() {
434
  //delete_option( "wp-short-pixel-priorityQueue");
435
  self::set(array());
436
  }
437
+
438
  public function logBulkProgress() {
439
  $t = time();
440
  $this->incrementBulkCurrentlyProcessed();
447
  $this->settings->lastBulkSuccessTime = $t;
448
  }
449
  }
450
+
451
  public function getBulkPercent() {
452
  $previousPercent = $this->settings->bulkPreviousPercent;
453
  //WPShortPixel::log("QUEUE - BulkPrevPercent: " . $previousPercent . " BulkCurrentlyProcessing: "
454
  // . $this->settings->bulkCurrentlyProcessed . " out of " . $this->getBulkToProcess());
455
+
456
  if($this->getBulkToProcess() <= 0) return ($this->processing () ? 99: 100);
457
  // return maximum 99%
458
  $percent = $previousPercent + round($this->settings->bulkCurrentlyProcessed / $this->getBulkToProcess()
459
  * (100 - $previousPercent));
460
 
461
  //WPShortPixel::log("QUEUE - Calculated Percent: " . $percent);
462
+
463
  return min(99, $percent);
464
  }
465
 
466
  public function getDeltaBulkPercent() {
467
  return $this->getBulkPercent() - $this->settings->bulkPreviousPercent;
468
  }
469
+
470
  public function getTimeRemaining (){
471
  $p = $this->getBulkPercent();
472
  $pAlready = $this->settings->bulkCount == 0 ? 0 : round($this->settings->bulkAlreadyDoneCount / $this->settings->bulkCount * 100);
class/view/settings/part-advanced.php ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ ?>
4
+
5
+ <section id="tab-adv-settings" class="clearfix <?php echo ($this->display_part == 'adv-settings') ? ' sel-tab ' :''; ?> ">
6
+ <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-adv-settings"><?php _e('Advanced','shortpixel-image-optimiser');?></a></h2>
7
+
8
+ <?php
9
+ $deliverWebpAlteredDisabled = '';
10
+ $deliverWebpUnalteredDisabled = '';
11
+ $deliverWebpAlteredDisabledNotice = false;
12
+ $deliverWebpUnalteredLabel ='';
13
+
14
+ if( $this->is_nginx ){
15
+ $deliverWebpUnaltered = ''; // Uncheck
16
+ $deliverWebpUnalteredDisabled = 'disabled'; // Disable
17
+ $deliverWebpUnalteredLabel = __('It looks like you\'re running your site on an NginX server. This means that you can only achieve this functionality by directly configuring the server config files. Please follow this link for instructions on how to achieve this:','shortpixel-image-optimiser')." <a href=\"javascript:void(0)\" data-beacon-article=\"5bfeb9de2c7d3a31944e78ee\">Open article</a>";
18
+ } else {
19
+ if( !$this->is_htaccess_writable ){
20
+ $deliverWebpUnalteredDisabled = 'disabled'; // Disable
21
+ if( $deliverWebp == 3 ){
22
+ $deliverWebpAlteredDisabled = 'disabled'; // Disable
23
+ $deliverWebpUnalteredLabel = __('It looks like you recently moved from an Apache server to an NGINX server, while the option to use .htacces was in use. Please follow this tutorial to see how you could implement by yourself this functionality, outside of the WP plugin. ','shortpixel-image-optimiser');
24
+ } else {
25
+ $deliverWebpUnalteredLabel = __('It looks like your .htaccess file cannot be written. Please fix this and then return to refresh this page to enable this option.','shortpixel-image-optimiser');
26
+ }
27
+ } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false) {
28
+ // Show a message about the risks and caveats of serving WEBP images via .htaccess
29
+ $deliverWebpUnalteredLabel = '<span style="color: initial;">'.__('Based on testing your particular hosting configuration, we determined that your server','shortpixel-image-optimiser').
30
+ '&nbsp;<img src="'. plugins_url( 'res/img/test.jpg' , SHORTPIXEL_PLUGIN_FILE) .'">&nbsp;'.
31
+ __('serve the WEBP versions of the JPEG files seamlessly, via .htaccess.','shortpixel-image-optimiser').' <a href="javascript:void(0)" data-beacon-article="5c1d050e04286304a71d9ce4">Open article to read more about this.</a></span>';
32
+ }
33
+ }
34
+
35
+
36
+
37
+ $excludePatterns = '';
38
+ if($view->data->excludePatterns) {
39
+ foreach($view->data->excludePatterns as $item) {
40
+ $excludePatterns .= $item['type'] . ":" . $item['value'] . ", ";
41
+ }
42
+ $excludePatterns = substr($excludePatterns, 0, -2);
43
+ }
44
+ ?>
45
+
46
+ <div class="wp-shortpixel-options wp-shortpixel-tab-content" style='visibility: hidden'>
47
+ <table class="form-table">
48
+ <tbody>
49
+ <tr>
50
+ <th scope="row"><label for="additional-media"><?php _e('Additional media folders','shortpixel-image-optimiser');?></label></th>
51
+ <td>
52
+ <span style="display:none;">Current PHP version: <?php echo(phpversion()) ?></span>
53
+ <?php if($view->customFolders) { ?>
54
+ <table class="shortpixel-folders-list">
55
+ <tr style="font-weight: bold;">
56
+ <td><?php _e('Folder name','shortpixel-image-optimiser');?></td>
57
+ <td><?php _e('Type &amp;<br>Status','shortpixel-image-optimiser');?></td>
58
+ <td><?php _e('Files','shortpixel-image-optimiser');?></td>
59
+ <td><?php _e('Last change','shortpixel-image-optimiser');?></td>
60
+ <td></td>
61
+ </tr>
62
+ <?php foreach($view->customFolders as $folder) {
63
+ $typ = $folder->getType();
64
+ $typ = $typ ? $typ . "<br>" : "";
65
+ $stat = $this->shortPixel->getSpMetaDao()->getFolderOptimizationStatus($folder->getId());
66
+ $cnt = $folder->getFileCount();
67
+ $st = ($cnt == 0
68
+ ? __("Empty",'shortpixel-image-optimiser')
69
+ : ($stat->Total == $stat->Optimized
70
+ ? __("Optimized",'shortpixel-image-optimiser')
71
+ : ($stat->Optimized + $stat->Pending > 0 ? __("Pending",'shortpixel-image-optimiser') : __("Waiting",'shortpixel-image-optimiser'))));
72
+
73
+ $err = $stat->Failed > 0 && !$st == __("Empty",'shortpixel-image-optimiser') ? " ({$stat->Failed} failed)" : "";
74
+
75
+ $action = ($st == __("Optimized",'shortpixel-image-optimiser') || $st == __("Empty",'shortpixel-image-optimiser') ? __("Stop monitoring",'shortpixel-image-optimiser') : __("Stop optimizing",'shortpixel-image-optimiser'));
76
+
77
+ $fullStat = $st == __("Empty",'shortpixel-image-optimiser') ? "" : __("Optimized",'shortpixel-image-optimiser') . ": " . $stat->Optimized . ", "
78
+ . __("Pending",'shortpixel-image-optimiser') . ": " . $stat->Pending . ", " . __("Waiting",'shortpixel-image-optimiser') . ": " . $stat->Waiting . ", "
79
+ . __("Failed",'shortpixel-image-optimiser') . ": " . $stat->Failed;
80
+ ?>
81
+ <tr>
82
+ <td>
83
+ <?php echo($folder->getPath()); ?>
84
+ </td>
85
+ <td>
86
+ <?php if(!($st == "Empty")) { ?>
87
+ <a href="javascript:none();" title="<?php echo $fullStat; ?>" style="text-decoration: none;">
88
+ <img src='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/info-icon.png' ));?>' style="margin-bottom: -2px;"/>
89
+ </a>&nbsp;<?php } echo($typ.$st.$err); ?>
90
+
91
+ </td>
92
+ <td>
93
+ <?php echo($cnt); ?> files
94
+ </td>
95
+ <td>
96
+ <?php echo($folder->getTsUpdated()); ?>
97
+ </td>
98
+ <td>
99
+ <input type="button" class="button remove-folder-button" data-value="<?php echo($folder->getPath()); ?>" title="<?php echo($action . " " . $folder->getPath()); ?>" value="<?php echo $action;?>">
100
+ <input type="button" style="display:none;" class="button button-alert recheck-folder-button" data-value="<?php echo($folder->getPath()); ?>"
101
+ 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');?>"
102
+ value="<?php _e('Refresh','shortpixel-image-optimiser');?>">
103
+ </td>
104
+ </tr>
105
+ <?php }?>
106
+ </table>
107
+ <?php } ?>
108
+
109
+ <div class='addCustomFolder'>
110
+
111
+ <input type="hidden" name="removeFolder" id="removeFolder"/>
112
+ <p class='add-folder-text'><strong><?php _e('Add a custom folder', 'shortpixel-image-optimiser'); ?></strong></p>
113
+ <input type="text" name="addCustomFolderView" id="addCustomFolderView" class="regular-text" value="" disabled style="">&nbsp;
114
+ <input type="hidden" name="addCustomFolder" id="addCustomFolder" value=""/>
115
+ <input type="hidden" id="customFolderBase" value="<?php echo $this->shortPixel->getCustomFolderBase(); ?>">
116
+
117
+ <a class="button select-folder-button" title="<?php _e('Select the images folder on your server.','shortpixel-image-optimiser');?>" href="javascript:void(0);">
118
+ <?php _e('Select ...','shortpixel-image-optimiser');?>
119
+ </a>
120
+ <input type="submit" name="save" 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');?>">
121
+ <p class="settings-info">
122
+ <?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');?>
123
+ <a href="https://blog.shortpixel.com/optimize-images-outside-media-library/" target="_blank" class="shortpixel-help-link">
124
+ <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
125
+ </a>
126
+ </p>
127
+
128
+ <div class="sp-modal-shade sp-folder-picker-shade"></div>
129
+ <div class="shortpixel-modal modal-folder-picker shortpixel-hide">
130
+ <div class="sp-modal-title"><?php _e('Select the images folder','shortpixel-image-optimiser');?></div>
131
+ <div class="sp-folder-picker"></div>
132
+ <input type="button" class="button button-info select-folder-cancel" value="<?php _e('Cancel','shortpixel-image-optimiser');?>" style="margin-right: 30px;">
133
+ <input type="button" class="button button-primary select-folder" value="<?php _e('Select','shortpixel-image-optimiser');?>">
134
+ </div>
135
+
136
+ <script>
137
+ jQuery(document).ready(function () {
138
+ ShortPixel.initFolderSelector();
139
+ });
140
+ </script>
141
+ </div> <!-- end of AddCustomFolder -->
142
+ </td>
143
+ </tr>
144
+ <?php if($this->has_nextgen) { ?>
145
+ <tr>
146
+ <th scope="row"><?php _e('Optimize NextGen galleries','shortpixel-image-optimiser');?></th>
147
+ <td>
148
+ <input name="includeNextGen" type="checkbox" id="nextGen" value='1' <?php echo checked($view->data->includeNextGen,'1' );?>> <label for="nextGen"><?php _e('Optimize NextGen galleries.','shortpixel-image-optimiser');?></label>
149
+ <p class="settings-info">
150
+ <?php _e('Check this to add all your current NextGen galleries to the custom folders list and to also have all the future NextGen galleries and images optimized automatically by ShortPixel.','shortpixel-image-optimiser');?>
151
+ </p>
152
+ </td>
153
+ </tr>
154
+ <?php } ?>
155
+ <tr>
156
+ <th scope="row"><?php _e('Convert PNG images to JPEG','shortpixel-image-optimiser');?></th>
157
+ <td>
158
+ <input name="png2jpg" type="checkbox" id="png2jpg" value="1" <?php checked( ($view->data->png2jpg > 0), true);?> <?php echo($this->is_gd_installed ? '' : 'disabled') ?>>
159
+ <label for="png2jpg"><?php _e('Automatically convert the PNG images to JPEG if possible.','shortpixel-image-optimiser');
160
+ if(!$this->is_gd_installed) {echo("&nbsp;<span style='color:red;'>" . __('You need PHP GD for this. Please ask your hosting to install it.','shortpixel-image-optimiser') . "</span>");}
161
+ ?></label>
162
+ <p class="settings-info">
163
+ <?php _e('Converts all PNGs that don\'t have transparent pixels to JPEG. This can dramatically reduce the file size, especially if you have camera pictures that are saved in PNG format. The plugin will also search for references of the image in posts and will replace them.','shortpixel-image-optimiser');?>
164
+ <strong><?php _e('The image will NOT be converted if the resulting JPEG is larger than the original PNG.','shortpixel-image-optimiser');?></strong>
165
+ </p><br>
166
+ <?php // @todo Issue with this. png2jpg > 0, is force ?>
167
+ <input name="png2jpgForce" type="checkbox" id="png2jpgForce" value="1" <?php checked(($view->data->png2jpg > 1), true);?> <?php echo($this->is_gd_installed ? '' : 'disabled') ?>>
168
+ <label for="png2jpgForce">
169
+ <?php _e('Also force the conversion of images with transparency.','shortpixel-image-optimiser'); ?>
170
+ </label>
171
+ </td>
172
+ </tr>
173
+ <tr class='exif_warning view-notice-row'>
174
+ <th scope="row">&nbsp;</th>
175
+ <td>
176
+ <div class='view-notice warning'><p><?php printf(__('Warning - Converting from PNG to JPG will %s not %s keep the EXIF-information!'), "<strong>","</strong>"); ?></p></div>
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <th scope="row"><?php _e('CMYK to RGB conversion','shortpixel-image-optimiser');?></th>
181
+ <td>
182
+ <input name="cmyk2rgb" type="checkbox" id="cmyk2rgb" value="1" <?php checked( $view->data->CMYKtoRGBconversion, "1" );?>>
183
+ <label for="cmyk2rgb"><?php _e('Adjust your images\' colours for computer and mobile screen display.','shortpixel-image-optimiser');?></label>
184
+ <p class="settings-info"><?php _e('Images for the web only need RGB format and converting them from CMYK to RGB makes them smaller.','shortpixel-image-optimiser');?></p>
185
+ </td>
186
+ </tr>
187
+ <tr>
188
+ <th scope="row"><?php _e('WebP Images:','shortpixel-image-optimiser');?></th>
189
+ <td>
190
+ <input name="createWebp" type="checkbox" id="createWebp" value="1" <?php checked( $view->data->createWebp, "1" );?>>
191
+ <label for="createWebp">
192
+ <?php _e('Also create <a href="http://blog.shortpixel.com/how-webp-images-can-speed-up-your-site/" target="_blank">WebP versions</a> of the images, <strong>for free</strong>.','shortpixel-image-optimiser');?>
193
+ </label>
194
+ <p class="settings-info">
195
+ <?php _e('WebP images can be up to three times smaller than PNGs and 25% smaller than JPGs. Choosing this option <strong>does not use up additional credits</strong>.','shortpixel-image-optimiser');?>
196
+ <a href="http://blog.shortpixel.com/how-webp-images-can-speed-up-your-site/" target="_blank" class="shortpixel-help-link">
197
+ <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
198
+ </a>
199
+ </p>
200
+ <div class="deliverWebpSettings">
201
+ <input name="deliverWebp" type="checkbox" id="deliverWebp" value="1" <?php checked( ($view->data->deliverWebp > 0), true);?>>
202
+ <label for="deliverWebp">
203
+ <?php _e('Deliver the WebP versions of the images in the front-end:','shortpixel-image-optimiser');?>
204
+ </label>
205
+ <ul class="deliverWebpTypes">
206
+ <li>
207
+ <input type="radio" name="deliverWebpType" id="deliverWebpAltered" <?php checked( ($view->data->deliverWebp >= 1 && $view->data->deliverWebp <= 2), true); ?> <?php echo( $deliverWebpAlteredDisabled );?> value="deliverWebpAltered">
208
+ <label for="deliverWebpAltered">
209
+ <?php _e('Using the &lt;PICTURE&gt; tag syntax','shortpixel-image-optimiser');?>
210
+ </label>
211
+ <?php if($deliverWebpAlteredDisabledNotice){ ?>
212
+ <p class="sp-notice">
213
+ <?php _e('After the option to work on .htaccess was selected, the .htaccess file has become unaccessible / readonly. Please make the .htaccess file writeable again to be able to further set up this option.','shortpixel-image-optimiser')?>
214
+ </p>
215
+ <?php } ?>
216
+ <p class="settings-info">
217
+ <?php _e('Each &lt;img&gt; will be replaced with a &lt;picture&gt; tag that will also provide the WebP image as a choice for browsers that support it. Also loads the picturefill.js for browsers that don\'t support the &lt;picture&gt; tag. You don\'t need to activate this if you\'re using the Cache Enabler plugin because your WebP images are already handled by this plugin. <strong>Please make a test before using this option</strong>, as if the styles that your theme is using rely on the position of your &lt;img&gt; tag, you might experience display problems.','shortpixel-image-optimiser'); ?>
218
+ <strong><?php _e('You can revert anytime to the previous state by just deactivating the option.','shortpixel-image-optimiser'); ?></strong>
219
+ </p>
220
+ <ul class="deliverWebpAlteringTypes">
221
+ <li>
222
+ <input type="radio" name="deliverWebpAlteringType" id="deliverWebpAlteredWP" <?php checked(($view->data->deliverWebp == 2), true);?> value="deliverWebpAlteredWP">
223
+ <label for="deliverWebpAlteredWP">
224
+ <?php _e('Only via Wordpress hooks (like the_content, the_excerpt, etc)');?>
225
+ </label>
226
+ </li>
227
+ <li>
228
+ <input type="radio" name="deliverWebpAlteringType" id="deliverWebpAlteredGlobal" <?php checked(($view->data->deliverWebp == 1),true)?> value="deliverWebpAlteredGlobal">
229
+ <label for="deliverWebpAlteredGlobal">
230
+ <?php _e('Global (processes the whole output buffer before sending the HTML to the browser)','shortpixel-image-optimiser');?>
231
+ </label>
232
+ </li>
233
+ </ul>
234
+ </li>
235
+ <li>
236
+ <input type="radio" name="deliverWebpType" id="deliverWebpUnaltered" <?php checked(($view->data->deliverWebp == 3), true);?> <?php echo( $deliverWebpUnalteredDisabled );?> value="deliverWebpUnaltered">
237
+ <label for="deliverWebpUnaltered">
238
+ <?php _e('Without altering the page code (via .htaccess)','shortpixel-image-optimiser')?>
239
+ </label>
240
+ <?php if($deliverWebpUnalteredLabel){ ?>
241
+ <p class="sp-notice">
242
+ <?php echo( $deliverWebpUnalteredLabel );?>
243
+ </p>
244
+ <?php } ?>
245
+ </li>
246
+ </ul>
247
+ </div>
248
+ </td>
249
+ </tr>
250
+ <tr>
251
+ <th scope="row"><?php _e('Optimize Retina images','shortpixel-image-optimiser');?></th>
252
+ <td>
253
+ <input name="optimizeRetina" type="checkbox" id="optimizeRetina" value="1" <?php checked( $view->data->optimizeRetina, "1"); ?>>
254
+ <label for="optimizeRetina"><?php _e('Also optimize the Retina images (@2x) if they exist.','shortpixel-image-optimiser');?></label>
255
+ <p class="settings-info">
256
+ <?php _e('If you have a Retina plugin that generates Retina-specific images (@2x), ShortPixel can optimize them too, alongside the regular Media Library images and thumbnails.','shortpixel-image-optimiser');?>
257
+ <a href="http://blog.shortpixel.com/how-to-use-optimized-retina-images-on-your-wordpress-site-for-best-user-experience-on-apple-devices/" target="_blank" class="shortpixel-help-link">
258
+ <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
259
+ </a>
260
+ </p>
261
+ </td>
262
+ </tr>
263
+ <tr>
264
+ <th scope="row"><?php _e('Optimize other thumbs','shortpixel-image-optimiser');?></th>
265
+ <td>
266
+ <input name="optimizeUnlisted" type="checkbox" id="optimizeUnlisted" value="1" <?php checked( $view->data->optimizeUnlisted, "1" );?>>
267
+ <label for="optimizeUnlisted"><?php _e('Also optimize the unlisted thumbs if found.','shortpixel-image-optimiser');?></label>
268
+ <p class="settings-info">
269
+ <?php _e('Some plugins create thumbnails which are not registered in the metadata but instead only create them alongside the other thumbnails. Let ShortPixel optimize them as well.','shortpixel-image-optimiser');?>
270
+ </p>
271
+ </td>
272
+ </tr>
273
+ <tr>
274
+ <th scope="row"><?php _e('Optimize PDFs','shortpixel-image-optimiser');?></th>
275
+ <td>
276
+ <input name="optimizePdfs" type="checkbox" id="optimizePdfs" value="1" <?php checked( $view->data->optimizePdfs, "1" );?>>
277
+ <label for="optimizePdfs"><?php _e('Automatically optimize PDF documents.','shortpixel-image-optimiser');?></label>
278
+ </td>
279
+ </tr>
280
+ <tr>
281
+ <th scope="row"><label for="excludePatterns"><?php _e('Exclude patterns','shortpixel-image-optimiser');?></label></th>
282
+ <td>
283
+ <input name="excludePatterns" type="text" id="excludePatterns" value="<?php echo( $excludePatterns );?>" class="regular-text" placeholder="<?php
284
+ _e('name:keepbig, path:/ignore_regex/i, size:1000x2000','shortpixel-image-optimiser');?>">
285
+ <?php _e('Exclude certain images from being optimized, based on patterns.','shortpixel-image-optimiser');?>
286
+ <p class="settings-info">
287
+ <?php _e('Add patterns separated by comma. A pattern consist of a <strong>type:value</strong> pair; the accepted types are
288
+ <strong>"name"</strong>, <strong>"path"</strong> and <strong>"size"</strong>.
289
+ A file will be excluded if it matches any of the patterns.
290
+ <br>For a <strong>"name"</strong> pattern only the filename will be matched but for a <strong>"path"</strong>,
291
+ all the path will be matched (useful for excluding certain subdirectories altoghether).
292
+ For these you can also use regular expressions accepted by preg_match, but without "," or ":".
293
+ A pattern will be considered a regex if it starts with a "/" and is valid.
294
+ <br>For the <strong>"size"</strong> type,
295
+ which applies only to Media Library images, <strong>the main images (not thumbnails)</strong> that have the size in the specified range will be excluded.
296
+ The format for the "size" exclude is: <strong>minWidth</strong>-<strong>maxWidth</strong>x<strong>minHeight</strong>-<strong>maxHeight</strong>, for example <strong>size:1000-1100x2000-2200</strong>. You can also specify a precise size, as <strong>1000x2000</strong>.','shortpixel-image-optimiser');?>
297
+ <a href="http://blog.shortpixel.com/shortpixel-how-to-exclude-images-and-folders-from-optimization/" target="_blank" class="shortpixel-help-link">
298
+ <span class="dashicons dashicons-editor-help"></span><?php _e('More info','shortpixel-image-optimiser');?>
299
+ </a>
300
+ </p>
301
+ </td>
302
+ </tr>
303
+ <tr>
304
+ <th scope="row"><label for="authentication"><?php _e('HTTP AUTH credentials','shortpixel-image-optimiser');?></label></th>
305
+ <td>
306
+ <input name="siteAuthUser" type="text" id="siteAuthUser" value="<?php echo( stripslashes(esc_html($view->data->siteAuthUser )));?>" class="regular-text" placeholder="<?php _e('User','shortpixel-image-optimiser');?>"><br>
307
+ <input name="siteAuthPass" type="text" id="siteAuthPass" value="<?php echo( stripslashes(esc_html($view->data->siteAuthPass )));?>" class="regular-text" placeholder="<?php _e('Password','shortpixel-image-optimiser');?>">
308
+ <p class="settings-info">
309
+ <?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');?>
310
+ </p>
311
+ </td>
312
+ </tr>
313
+ <tr>
314
+ <th scope="row"><?php _e('Process in front-end','shortpixel-image-optimiser');?></th>
315
+ <td>
316
+ <input name="frontBootstrap" type="checkbox" id="frontBootstrap" value="1" <?php checked( $view->data->frontBootstrap, '1' );?>>
317
+ <label for="frontBootstrap"><?php _e('Automatically optimize images added by users in front end.','shortpixel-image-optimiser');?></label>
318
+ <p class="settings-info">
319
+ <?php _e('Check this if you have users that add images or PDF documents from custom forms in the front-end. This could increase the load on your server if you have a lot of users simultaneously connected.','shortpixel-image-optimiser');?>
320
+ </p>
321
+ </td>
322
+ </tr>
323
+ <tr>
324
+ <th scope="row"><?php _e('Optimize media on upload','shortpixel-image-optimiser');?></th>
325
+ <td>
326
+ <input name="autoMediaLibrary" type="checkbox" id="autoMediaLibrary" value="1" <?php checked( $view->data->autoMediaLibrary, "1" );?>>
327
+ <label for="autoMediaLibrary"><?php _e('Automatically optimize Media Library items after they are uploaded (recommended).','shortpixel-image-optimiser');?></label>
328
+ <p class="settings-info">
329
+ <?php _e('By default, ShortPixel will automatically optimize all the freshly uploaded image and PDF files. If you uncheck this you\'ll need to either run Bulk ShortPixel or go to Media Library (in list view) and click on the right side "Optimize now" button(s).','shortpixel-image-optimiser');?>
330
+ </p>
331
+ </td>
332
+ </tr>
333
+ <tr>
334
+ <th scope="row"><label for="excludeSizes"><?php _e('Exclude thumbnail sizes','shortpixel-image-optimiser');?></label></th>
335
+ <td>
336
+ <?php foreach($view->allThumbSizes as $sizeKey => $sizeVal) {?>
337
+ <span style="margin-right: 20px;white-space:nowrap">
338
+ <input name="excludeSizes[]" type="checkbox" id="excludeSizes_<?php echo($sizeKey);?>" <?php echo((in_array($sizeKey, $view->data->excludeSizes) ? 'checked' : ''));?>
339
+ value="<?php echo($sizeKey);?>">&nbsp;<?php $w=$sizeVal['width']?$sizeVal['width'].'px':'*';$h=$sizeVal['height']?$sizeVal['height'].'px':'*';echo("$sizeKey ({$w} &times; {$h})");?>&nbsp;&nbsp;
340
+ </span><br>
341
+ <?php } ?>
342
+ <p class="settings-info">
343
+ <?php _e('Please check the thumbnail sizes you would like to <strong>exclude</strong> from optimization. There might be sizes created by themes or plugins which do not appear here, because they were not properly registered with WordPress. If you want to ignore them too, please uncheck the option <strong>Optimize other thumbs</strong> above.','shortpixel-image-optimiser');?>
344
+ </p>
345
+ </td>
346
+ </tr>
347
+ </tbody>
348
+ </table>
349
+ <p class="submit">
350
+ <input type="submit" name="save" id="saveAdv" class="button button-primary" title="<?php _e('Save Changes','shortpixel-image-optimiser');?>" value="<?php _e('Save Changes','shortpixel-image-optimiser');?>"> &nbsp;
351
+ <input type="submit" name="save_bulk" id="bulkAdvGo" class="button button-primary" title="<?php _e('Save and go to the Bulk Processing page','shortpixel-image-optimiser');?>" value="<?php _e('Save and Go to Bulk Process','shortpixel-image-optimiser');?>"> &nbsp;
352
+ </p>
353
+ </div>
354
+ <script>
355
+ jQuery(document).ready(function () { ShortPixel.setupAdvancedTab();});
356
+ </script>
357
+ </section>
class/view/settings/part-cloudflare.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ ?>
4
+
5
+ <section id="tab-cloudflare" <?php echo ($this->display_part == 'cloudflare') ? ' class="sel-tab" ' :''; ?>>
6
+ <h2><a class='tab-link' href='javascript:void(0);'
7
+ data-id="tab-cloudflare"><?php _e('Cloudflare API', 'shortpixel-image-optimiser'); ?></a>
8
+ </h2>
9
+
10
+ <div class="wp-shortpixel-tab-content" style="visibility: hidden">
11
+ <?php
12
+
13
+ if(! $this->is_curl_installed) {
14
+ echo('<p style="font-weight:bold;color:red">' . __("Please enable PHP cURL extension for the Cloudflare integration to work.", 'shortpixel-image-optimiser') . '</p>' );
15
+ }
16
+ ?>
17
+ <p><?php _e("If you're using Cloudflare on your site then we advise you to fill in the details below. This will allow ShortPixel to work seamlessly with Cloudflare so that any image optimized/restored by ShortPixel will be automatically updated on Cloudflare as well.",'shortpixel-image-optimiser');?></p>
18
+
19
+ <table class="form-table">
20
+ <tbody>
21
+ <tr>
22
+ <th scope="row">
23
+ <label for="cloudflare-email"><?php _e('Cloudflare E-mail:', 'shortpixel-image-optimiser'); ?></label>
24
+ </th>
25
+ <td>
26
+ <input name="cloudflareEmail" type="text" id="cloudflare-email" <?php echo(! $this->is_curl_installed ? 'disabled' : '');?>
27
+ value="<?php echo( stripslashes(esc_html($view->data->cloudflareEmail))); ?>" class="regular-text">
28
+ <p class="settings-info">
29
+ <?php _e('The e-mail address you use to login to CloudFlare.','shortpixel-image-optimiser');?>
30
+ </p>
31
+ </td>
32
+ </tr>
33
+ <tr>
34
+ <th scope="row"><label
35
+ for="cloudflare-auth-key"><?php _e('Global API Key:', 'shortpixel-image-optimiser'); ?></label>
36
+ </th>
37
+ <td>
38
+ <input name="cloudflareAuthKey" type="text" id="cloudflare-auth-key" <?php echo(! $this->is_curl_installed ? 'disabled' : '');?>
39
+ value="<?php echo(stripslashes(esc_html($view->data->cloudflareAuthKey))); ?>" class="regular-text">
40
+ <p class="settings-info">
41
+ <?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>
42
+ </p>
43
+ </td>
44
+ </tr>
45
+ <tr>
46
+ <th scope="row"><label
47
+ for="cloudflare-zone-id"><?php _e('Zone ID:', 'shortpixel-image-optimiser'); ?></label>
48
+ </th>
49
+ <td>
50
+ <input name="cloudflareZoneID" type="text" id="cloudflare-zone-id" <?php echo(! $this->is_curl_installed ? 'disabled' : '');?>
51
+ value="<?php echo(stripslashes(esc_html($view->data->cloudflareZoneID))); ?>" class="regular-text">
52
+ <p class="settings-info">
53
+ <?php _e('This can be found in your Cloudflare account in the "Overview" section for your domain.','shortpixel-image-optimiser');?>
54
+ </p>
55
+ </td>
56
+ </tr>
57
+ </tbody>
58
+ </table>
59
+ <p class="submit">
60
+ <input type="submit" name="saveCloudflare" id="saveCloudflare" class="button button-primary"
61
+ title="<?php _e('Save Changes', 'shortpixel-image-optimiser'); ?>"
62
+ value="<?php _e('Save Changes', 'shortpixel-image-optimiser'); ?>"> &nbsp;
63
+ </p>
64
+ </div>
65
+
66
+ </section>
class/view/settings/part-debug.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ ?>
5
+
6
+ <section id="tab-debug" <?php echo ($this->display_part == 'debug') ? ' class="sel-tab" ' :''; ?>>
7
+ <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-debug">
8
+ <?php _e('Debug','shortpixel-image-optimiser');?></a>
9
+ </h2>
10
+
11
+ <div class="wp-shortpixel-options wp-shortpixel-tab-content" style="visibility: hidden">
12
+ <div class='env'>
13
+ <h3><?php _e('Environment', 'shortpixel'); ?></h3>
14
+ <div class='flex'>
15
+ <span>Nginx</span><span><?php var_export($this->is_nginx); ?></span>
16
+ <span>KeyVerified</span><span><?php var_export($this->is_verifiedkey); ?></span>
17
+ <span>HtAccess writable</span><span><?php var_export($this->is_htaccess_writable); ?></span>
18
+ <span>Multisite</span><span><?php var_export($this->is_multisite); ?></span>
19
+ <span>Main site</span><span><?php var_export($this->is_mainsite); ?></span>
20
+ <span>Constant key</span><span><?php var_export($this->is_constant_key); ?></span>
21
+ <span>Hide Key</span><span><?php var_export($this->hide_api_key); ?></span>
22
+ <span>Has Nextgen</span><span><?php var_export($this->has_nextgen); ?></span>
23
+
24
+ </div>
25
+ </div>
26
+
27
+ <div class='settings'>
28
+ <h3><?php _e('Settings', 'shortpixel'); ?></h3>
29
+ <?php $local = $this->view->data;
30
+ $local->apiKey = strlen($local->apiKey) . ' chars'; ?>
31
+ <pre><?php var_export($local); ?></pre>
32
+ </div>
33
+
34
+ <div class='quotadata'>
35
+ <h3><?php _e('Quota Data', 'shortpixel'); ?></h3>
36
+ <pre><?php var_export($this->quotaData); ?></pre>
37
+ </div>
38
+
39
+ </div> <!-- tab-content -->
40
+ </section>
class/view/settings/part-general.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php namespace ShortPixel; ?>
2
+ <section id="tab-settings" <?php echo ($this->display_part == 'settings') ? ' class="sel-tab" ' :''; ?> >
3
+ <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-settings">
4
+ <?php _e('General','shortpixel-image-optimiser');?></a>
5
+ </h2>
6
+
7
+ <div class="wp-shortpixel-options wp-shortpixel-tab-content" style="visibility: hidden">
8
+
9
+ <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>
10
+ <table class="form-table">
11
+ <tbody>
12
+ <tr>
13
+ <th scope="row"><label for="key"><?php _e('API Key:','shortpixel-image-optimiser');?></label></th>
14
+ <td>
15
+ <?php
16
+ $canValidate = false;
17
+ // Several conditions for showing API key.
18
+ if ($this->hide_api_key)
19
+ $showApiKey = false;
20
+ elseif($this->is_multisite && $this->is_constant_key)
21
+ $showApiKey = false;
22
+ else {
23
+ $showApiKey = true; // is_mainsite, multisite, no constant.
24
+ }
25
+
26
+ $editApiKey = (! $this->is_constant_key && $showApiKey) ? true : false; ;
27
+
28
+ if($showApiKey) {
29
+ $canValidate = true;?>
30
+ <input name="key" type="text" id="key" value="<?php echo( $view->data->apiKey );?>"
31
+ class="regular-text" <?php echo($editApiKey ? "" : 'disabled') ?> <?php echo $this->is_verifiedkey ? 'onkeyup="ShortPixel.apiKeyChanged()"' : '' ?> >
32
+ <?php
33
+ }
34
+ elseif(defined("SHORTPIXEL_API_KEY")) {
35
+ $canValidate = true;?>
36
+ <input name="key" type="text" id="key" disabled="true" placeholder="<?php
37
+ if( $this->hide_api_key ) {
38
+ echo("********************");
39
+ } else {
40
+ _e('Multisite API Key','shortpixel-image-optimiser');
41
+ }
42
+ ?>" class="regular-text">
43
+ <?php } ?>
44
+ <input type="hidden" name="validate" id="valid" value=""/>
45
+ <span class="spinner" id="pluginemail_spinner" style="float:none;"></span>
46
+ <button type="button" id="validate" class="button button-primary" title="<?php _e('Validate the provided API key','shortpixel-image-optimiser');?>"
47
+ onclick="ShortPixel.validateKey(this)" <?php echo $canValidate ? "" : "disabled"?> <?php echo $this->is_verifiedkey ? 'style="display:none;"' : '' ?>>
48
+ <?php _e('Validate','shortpixel-image-optimiser');?>
49
+ </button>
50
+ <span class="shortpixel-key-valid" <?php echo $this->is_verifiedkey ? '' : 'style="display:none;"' ?>>
51
+ <span class="dashicons dashicons-yes"></span><?php _e('Your API key is valid.','shortpixel-image-optimiser');?>
52
+ </span>
53
+ <?php if($this->is_constant_key) { ?>
54
+ <p class="settings-info"><?php _e('Key defined in wp-config.php.','shortpixel-image-optimiser');?></p>
55
+ <?php } ?>
56
+
57
+ </td>
58
+ </tr>
59
+ <?php if (! $this->is_verifiedkey) { //if invalid key we display the link to the API Key ?>
60
+ </tbody>
61
+ </table>
62
+ <?php } else { //if valid key we display the rest of the options ?>
63
+ <tr>
64
+ <th scope="row">
65
+ <label for="compressionType"><?php _e('Compression type:','shortpixel-image-optimiser');?></label>
66
+ </th>
67
+ <td>
68
+ <div class="shortpixel-compression">
69
+ <div class="shortpixel-compression-options">
70
+ <label class="lossy" title="<?php _e('This is the recommended option in most cases, producing results that look the same as the original to the human eye.','shortpixel-image-optimiser');?>">
71
+ <input type="radio" class="shortpixel-radio-lossy" name="compressionType" value="1" <?php echo( $view->data->compressionType == 1 ? "checked" : "" );?>><span><?php _e('Lossy','shortpixel-image-optimiser');?></span>
72
+ </label>
73
+ <label class="glossy" title="<?php _e('Best option for photographers and other professionals that use very high quality images on their sites and want best compression while keeping the quality untouched.','shortpixel-image-optimiser');?>">
74
+ <input type="radio" class="shortpixel-radio-glossy" name="compressionType" value="2" <?php echo( $view->data->compressionType == 2 ? "checked" : "" );?>><span><?php _e('Glossy','shortpixel-image-optimiser');?></span>
75
+ </label>
76
+ <label class="lossless" title="<?php _e('Make sure not a single pixel looks different in the optimized image compared with the original. In some rare cases you will need to use this type of compression. Some technical drawings or images from vector graphics are possible situations.','shortpixel-image-optimiser');?>">
77
+ <input type="radio" class="shortpixel-radio-lossless" name="compressionType" value="0" <?php echo( $view->data->compressionType == 0 ? "checked" : "" );?>><span><?php _e('Lossless','shortpixel-image-optimiser');?></span>
78
+ </label>
79
+ <?php _e('<a href="https://shortpixel.com/online-image-compression" style="margin-left:20px;" target="_blank">Make a few tests</a> to help you decide.'); ?>
80
+ </div>
81
+ <p class="settings-info shortpixel-radio-info shortpixel-radio-lossy" <?php echo( $view->data->compressionType == 1 ? "" : 'style="display:none"' );?>>
82
+ <?php _e('<b>Lossy compression (recommended): </b>offers the best compression rate.</br> This is the recommended option for most users, producing results that look the same as the original to the human eye.','shortpixel-image-optimiser');?>
83
+ </p>
84
+ <p class="settings-info shortpixel-radio-info shortpixel-radio-glossy" <?php echo( $view->data->compressionType == 2 ? "" : 'style="display:none"' );?>>
85
+ <?php _e('<b>Glossy compression: </b>creates images that are almost pixel-perfect identical to the originals.</br> Best option for photographers and other professionals that use very high quality images on their sites and want best compression while keeping the quality untouched.','shortpixel-image-optimiser');?>
86
+ <a href="http://blog.shortpixel.com/glossy-image-optimization-for-photographers/" target="_blank" class="shortpixel-help-link">
87
+ <span class="dashicons dashicons-editor-help"></span><?php _e('More info about glossy','shortpixel-image-optimiser');?>
88
+ </a></p>
89
+ <p class="settings-info shortpixel-radio-info shortpixel-radio-lossless" <?php echo( $view->data->compressionType == 0 ? "" : 'style="display:none"' );?>>
90
+ <?php _e('<b>Lossless compression: </b> the resulting image is pixel-identical with the original image.</br>Make sure not a single pixel looks different in the optimized image compared with the original.
91
+ In some rare cases you will need to use this type of compression. Some technical drawings or images from vector graphics are possible situations.','shortpixel-image-optimiser');?>
92
+ </p>
93
+ </div>
94
+ <script>
95
+ // @todo Remove JS from interface
96
+ function shortpixelCompressionLevelInfo() {
97
+ jQuery(".shortpixel-compression p").css("display", "none");
98
+ jQuery(".shortpixel-compression p." + jQuery(".shortpixel-compression-options input:radio:checked").attr('class')).css("display", "block");
99
+ }
100
+ //shortpixelCompressionLevelInfo();
101
+ jQuery(".shortpixel-compression-options input:radio").change(shortpixelCompressionLevelInfo);
102
+ </script>
103
+ </td>
104
+ </tr>
105
+ <tr>
106
+ <th scope="row"><?php _e('Also include thumbnails:','shortpixel-image-optimiser');?></th>
107
+ <td><input name="processThumbnails" type="checkbox" id="thumbnails" value="1" <?php checked($view->data->processThumbnails, '1');?>>
108
+ <label for="thumbnails"><?php _e('Apply compression also to <strong>image thumbnails.</strong> ','shortpixel-image-optimiser');?></label>
109
+ <?php echo($view->thumbnailsToProcess > 0 ? "(" . number_format($view->thumbnailsToProcess) . " " . __('thumbnails to optimize','shortpixel-image-optimiser') . ")" : "");?>
110
+ <p class="settings-info">
111
+ <?php _e('It is highly recommended that you optimize the thumbnails as they are usually the images most viewed by end users and can generate most traffic.<br>Please note that thumbnails count up to your total quota.','shortpixel-image-optimiser');?>
112
+ </p>
113
+ </td>
114
+ </tr>
115
+ <tr>
116
+ <th scope="row"><?php _e('Image backup','shortpixel-image-optimiser');?></th>
117
+ <td>
118
+ <input name="backupImages" type="checkbox" id="backupImages" value="1" <?php checked($view->data->backupImages,'1'); ?>>
119
+ <label for="backupImages"><?php _e('Save and keep a backup of your original images in a separate folder.','shortpixel-image-optimiser');?></label>
120
+ <p class="settings-info"><?php _e('You <strong>need to have backup active</strong> in order to be able to restore images to originals or to convert from Lossy to Lossless and back.','shortpixel-image-optimiser');?></p>
121
+ </td>
122
+ </tr>
123
+ <tr>
124
+ <th scope="row"><?php _e('Remove EXIF','shortpixel-image-optimiser');?></th>
125
+ <td>
126
+ <input name="removeExif" type="checkbox" id="removeExif" value="1" <?php checked($view->data->keepExif, 0);?>>
127
+ <label for="removeExif"><?php _e('Remove the EXIF tag of the image (recommended).','shortpixel-image-optimiser');?></label>
128
+ <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.
129
+ 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>
130
+ </td>
131
+ </tr>
132
+ <tr class='exif_warning view-notice-row'>
133
+ <th scope="row">&nbsp;</th>
134
+ <td>
135
+ <div class='view-notice warning'><p><?php printf(__('Warning - Converting from PNG to JPG will %s not %s keep the EXIF-information!'), "<strong>","</strong>"); ?></p></div>
136
+ </td>
137
+ </tr>
138
+ <tr>
139
+ <?php $resizeDisabled = (! $this->view->data->resizeImages) ? 'disabled' : '';
140
+ // @todo Inline styling here can be decluttered.
141
+ ?>
142
+ <th scope="row"><?php _e('Resize large images','shortpixel-image-optimiser');?></th>
143
+ <td>
144
+ <input name="resizeImages" type="checkbox" id="resize" value="1" <?php checked( $view->data->resizeImages, true );?>>
145
+ <label for="resize"><?php _e('to maximum','shortpixel-image-optimiser');?></label>
146
+ <input type="text" name="resizeWidth" id="width" style="width:70px" class="resize-sizes"
147
+ value="<?php echo( $view->data->resizeWidth > 0 ? $view->data->resizeWidth : min(924, $view->minSizes['width']) );?>" <?php echo( $resizeDisabled );?>/> <?php
148
+ _e('pixels wide &times;','shortpixel-image-optimiser');?>
149
+ <input type="text" name="resizeHeight" id="height" class="resize-sizes" style="width:70px"
150
+ value="<?php echo( $view->data->resizeHeight > 0 ? $view->data->resizeHeight : min(924, $view->minSizes['height']) );?>" <?php echo( $resizeDisabled );?>/> <?php
151
+ _e('pixels high (original aspect ratio is preserved and image is not cropped)','shortpixel-image-optimiser');?>
152
+ <input type="hidden" id="min-resizeWidth" value="<?php echo($view->minSizes['width']);?>" data-nicename="<?php _e('Width', 'shortpixel-image-optimiser'); ?>" />
153
+ <input type="hidden" id="min-resizeHeight" value="<?php echo($view->minSizes['height']);?>" data-nicename="<?php _e('Height', 'shortpixel-image-optimiser'); ?>"/>
154
+ <p class="settings-info">
155
+ <?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');?>
156
+ <a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
157
+ <span class="dashicons dashicons-editor-help"></span><?php _e('Read more','shortpixel-image-optimiser');?>
158
+ </a><br/>
159
+ </p>
160
+ <div style="margin-top: 10px;">
161
+ <input type="radio" name="resizeType" id="resize_type_outer" value="outer" <?php echo($view->data->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
162
+ <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?>"
163
+ 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'
164
+ 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');?>">
165
+ <input type="radio" name="resizeType" id="resize_type_inner" value="inner" <?php echo($view->data->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
166
+ <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?>"
167
+ 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'
168
+ 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');?>">
169
+ <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
170
+ <span class="dashicons dashicons-editor-help"></span><?php _e('What is this?','shortpixel-image-optimiser');?></a>
171
+ </div>
172
+ </div>
173
+ </td>
174
+ </tr>
175
+ </tbody>
176
+ </table>
177
+
178
+
179
+ <?php } ?>
180
+ <p class="submit">
181
+ <input type="submit" name="save" id="save" class="button button-primary" title="<?php _e('Save Changes','shortpixel-image-optimiser');?>" value="<?php _e('Save Changes','shortpixel-image-optimiser');?>"> &nbsp;
182
+ <input type="submit" name="save_bulk" id="bulk" class="button button-primary" title="<?php _e('Save and go to the Bulk Processing page','shortpixel-image-optimiser');?>" value="<?php _e('Save and Go to Bulk Process','shortpixel-image-optimiser');?>"> &nbsp;
183
+ </p>
184
+ </div>
185
+
186
+ </section>
class/view/settings/part-nokey.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\NoticeController as Notice;
4
+ use ShortPixel\ShortPixelLogger as Log;
5
+
6
+ $canValidate = false;
7
+ // Several conditions for showing API key.
8
+ if ($this->hide_api_key)
9
+ $showApiKey = false;
10
+ elseif($this->is_multisite && $this->is_constant_key)
11
+ $showApiKey = false;
12
+ else {
13
+ $showApiKey = true; // is_mainsite, multisite, no constant.
14
+ }
15
+
16
+ $editApiKey = (! $this->is_constant_key && $showApiKey) ? true : false;
17
+
18
+ // Notices for fringe cases
19
+ if ($this->hide_api_key && ! $this->is_constant_key)
20
+ {
21
+ Notice::addError(__('wp-config.php has API key hidden, but no API key has been entered. Please do so, or remove the Hide constant', 'shortpixel-image-optimiser'));
22
+ }
23
+ elseif ($this->is_constant_key && ! $this->is_verifiedkey)
24
+ {
25
+ $dkey = ($this->hide_api_key) ? '' : '(' . SHORTPIXEL_API_KEY. ')';
26
+ Notice::addError(sprintf(__('Constant API Key is not verified. Please check if this is a valid API key %s'),$dkey));
27
+ }
28
+
29
+ $adminEmail = get_bloginfo('admin_email');
30
+ if($adminEmail == 'noreply@addendio.com') $adminEmail = false; //hack for the addendio sandbox e-mail
31
+
32
+ ?>
33
+ <section id="tab-settings" <?php echo ($this->display_part == 'settings') ? ' class="sel-tab" ' :''; ?> >
34
+ <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-settings">
35
+ <?php _e('Join ShortPixel','shortpixel-image-optimiser');?></a>
36
+ </h2>
37
+ <div class="wp-shortpixel-options wp-shortpixel-tab-content">
38
+ <?php if($showApiKey): ?>
39
+ <h3><?php _e('Request an API Key:','shortpixel-image-optimiser');?></h3>
40
+ <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>
41
+
42
+ <table class="form-table">
43
+ <tbody>
44
+ <tr>
45
+ <th scope="row"><label for="key"><?php _e('E-mail address:','shortpixel-image-optimiser');?></label></th>
46
+ <td>
47
+ <input name="pluginemail" type="text" id="pluginemail" value="<?php echo( $adminEmail );?>"
48
+ onchange="ShortPixel.updateSignupEmail();" class="regular-text">
49
+ <span class="spinner" id="pluginemail_spinner" style="float:none;"></span>
50
+ <a type="button" id="request_key" class="button button-primary" title="<?php _e('Request a new API key','shortpixel-image-optimiser');?>"
51
+ href="https://shortpixel.com/free-sign-up?pluginemail=<?php echo( $adminEmail );?>"
52
+ onclick="ShortPixel.newApiKey(event);"
53
+ onmouseenter="ShortPixel.updateSignupEmail();">
54
+ <?php _e('Request Key','shortpixel-image-optimiser');?>
55
+ </a>
56
+ <p class="settings-info shortpixel-settings-error" style='display:none;' id='pluginemail-error'>
57
+ <b><?php _e('Please provide a valid e-mail address.', 'shortpixel-image-optimiser');?></b>
58
+ </p>
59
+ <p class="settings-info" id='pluginemail-info'>
60
+ <?php if($adminEmail) {
61
+ printf(__('<b>%s</b> is the e-mail address in your WordPress Settings. You can use it, or change it to any valid e-mail address that you own.','shortpixel-image-optimiser'), $adminEmail);
62
+ } else {
63
+ _e('Please input your e-mail address and press the Request Key button.','shortpixel-image-optimiser');
64
+ }
65
+ ?><br><span style="position:relative;">
66
+ <input name="tos" type="checkbox" id="tos">
67
+ <img id="tos-robo" src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>" style="position: absolute;left: -95px;bottom: -26px;display:none;">
68
+ <img id="tos-hand" src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/point.png' ));?>" style="position: absolute;left: -39px;bottom: -9px;display:none;">
69
+ </span>
70
+ <?php _e('I have read and I agree to the <a href="https://shortpixel.com/tos" target="_blank">Terms of Service</a> and the <a href="https://shortpixel.com/privacy" target="_blank">Privacy Policy</a> (<a href="https://shortpixel.com/privacy#gdpr" target="_blank">GDPR compliant</a>).','shortpixel-image-optimiser');
71
+ ?>
72
+ </p>
73
+ </td>
74
+ </tr>
75
+ </tbody>
76
+ </table>
77
+ <?php endif; ?>
78
+ <h3>
79
+ <?php _e('Already have an API Key:','shortpixel-image-optimiser');?>
80
+ </h3>
81
+ <p style='font-size: 14px'>
82
+ <?php _e('If you already have an API Key please input it below and press Validate.','shortpixel-image-optimiser');?>
83
+ </p>
84
+
85
+ <form method="POST" action="<?php echo add_query_arg(array('noheader' => 'true', 'sp-action' => 'action_addkey')) ?>" id="shortpixel-form-nokey">
86
+ <table class="form-table">
87
+ <tbody>
88
+ <tr>
89
+ <th scope="row"><label for="key"><?php _e('API Key:','shortpixel-image-optimiser');?></label></th>
90
+ <td>
91
+ <?php
92
+ if($showApiKey) {
93
+
94
+ if (! $this->is_constant_key)
95
+ $canValidate = true;
96
+
97
+ ?>
98
+ <input name="key" type="text" id="key" value="<?php echo( $view->data->apiKey );?>"
99
+ class="regular-text" <?php echo($editApiKey ? "" : 'disabled') ?> >
100
+ <?php
101
+ }
102
+ elseif(defined("SHORTPIXEL_API_KEY")) {
103
+ $canValidate = true;?>
104
+ <input name="key" type="text" id="key" disabled="true" placeholder="<?php
105
+ if( $this->hide_api_key ) {
106
+ echo("********************");
107
+ } else {
108
+ _e('Multisite API Key','shortpixel-image-optimiser');
109
+ }
110
+ ?>" class="regular-text">
111
+ <?php } ?>
112
+ <input type="hidden" name="validate" id="valid" value="validate"/>
113
+ <span class="spinner" id="pluginemail_spinner" style="float:none;"></span>
114
+ <button type="button" id="validate" class="button button-primary" title="<?php _e('Validate the provided API key','shortpixel-image-optimiser');?>"
115
+ onclick="ShortPixel.validateKey(this)" <?php echo $canValidate ? "" : "disabled"?> >
116
+ <?php _e('Validate','shortpixel-image-optimiser');?>
117
+ </button>
118
+
119
+ <?php if($this->is_constant_key) { ?>
120
+ <p class="settings-info"><?php _e('Key defined in wp-config.php.','shortpixel-image-optimiser');?></p>
121
+ <?php } ?>
122
+
123
+ </td>
124
+ </tr>
125
+ </tbody>
126
+ </table>
127
+ </form>
128
+ </div> <!-- tab content -->
129
+ </section>
class/view/settings/part-statistics.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ $quotaData = $this->quotaData;
5
+
6
+ ?>
7
+
8
+ <section id="tab-stats" <?php echo ($this->display_part == 'stats') ? ' class="sel-tab" ' :''; ?>>
9
+ <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-stats"><?php _e('Statistics','shortpixel-image-optimiser');?></a></h2>
10
+
11
+ <div class="wp-shortpixel-tab-content" style="visibility: hidden">
12
+ <a id="facts"></a>
13
+ <h3><?php _e('Your ShortPixel Stats','shortpixel-image-optimiser');?></h3>
14
+ <table class="form-table">
15
+ <tbody>
16
+ <tr>
17
+ <th scope="row">
18
+ <?php _e('Average compression of your files:','shortpixel-image-optimiser');?>
19
+ </th>
20
+ <td>
21
+ <strong><?php echo($view->averageCompression);?>%</strong>
22
+ <div class="sp-bulk-summary">
23
+ <input type="text" value="<?php echo("" . round($view->averageCompression))?>" id="sp-total-optimization-dial" class="dial">
24
+ </div>
25
+ <div id="sp-bulk-stats" style="display:none">
26
+ <?php
27
+ $under5PercentCount = $view->data->under5Percent; //amount of under 5% optimized imgs.
28
+ $totalOptimized = isset($quotaData['totalProcessedFiles']) ? $quotaData['totalProcessedFiles'] : 0;
29
+ $mainOptimized = isset($quotaData['mainProcessedFiles']) ? $quotaData['mainProcessedFiles'] : 0;
30
+ ?>
31
+ <div class="bulk-progress bulk-stats">
32
+ <div class="label"><?php _e('Processed Images and PDFs:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($mainOptimized));?></div><br>
33
+ <div class="label"><?php _e('Processed Thumbnails:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($totalOptimized - $mainOptimized));?></div><br>
34
+ <div class="label totals"><?php _e('Total files processed:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($totalOptimized));?></div><br>
35
+ <div class="label totals"><?php _e('Minus files with <5% optimization (free):','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($under5PercentCount));?></div><br><br>
36
+ <div class="label totals"><?php _e('Used quota:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo(number_format($totalOptimized - $under5PercentCount));?></div><br>
37
+ <br>
38
+ <div class="label"><?php _e('Average optimization:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo($view->averageCompression);?>%</div><br>
39
+ <div class="label"><?php _e('Saved space:','shortpixel-image-optimiser');?></div><div class="stat-value"><?php echo($view->data->savedSpace);?></div>
40
+ </div>
41
+ </div>
42
+ <script>
43
+ jQuery(function() {
44
+ jQuery("#sp-total-optimization-dial").val("<?php echo("" . round($view->averageCompression))?>");
45
+ ShortPixel.percentDial("#sp-total-optimization-dial", 160);
46
+
47
+ jQuery(".sp-bulk-summary").spTooltip({
48
+ tooltipSource: "inline",
49
+ tooltipSourceID: "#sp-bulk-stats"
50
+ });
51
+ });
52
+ !function(d,s,id){//Just optimized my site with ShortPixel image optimization plugin
53
+ var js,
54
+ fjs=d.getElementsByTagName(s)[0],
55
+ p=/^http:/.test(d.location)?'http':'https';
56
+ if(!d.getElementById(id)){js=d.createElement(s);
57
+ js.id=id;js.src=p+'://platform.twitter.com/widgets.js';
58
+ fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
59
+ </script>
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <th scope="row">
64
+ <?php _e('Disk space saved by ShortPixel:','shortpixel-image-optimiser');?>
65
+ </th>
66
+ <td><?php echo(\WpShortPixel::formatBytes($view->data->savedSpace));?></td>
67
+ </tr>
68
+ <tr>
69
+ <th scope="row">
70
+ <?php _e('Bandwith* saved by ShortPixel:','shortpixel-image-optimiser');?>
71
+ </th>
72
+ <td><?php echo($view->savedBandwidth);?></td>
73
+ </tr>
74
+ </tbody>
75
+ </table>
76
+
77
+ <h3><?php _e('Your ShortPixel Credits','shortpixel-image-optimiser');?></h3>
78
+ <table class="form-table">
79
+ <tbody>
80
+ <tr>
81
+ <th scope="row" bgcolor="#ffffff">
82
+ <?php _e('Your monthly plan','shortpixel-image-optimiser');?>:
83
+ </th>
84
+ <td bgcolor="#ffffff">
85
+ <?php
86
+ $DateNow = time();
87
+ $DateSubscription = strtotime($quotaData['APILastRenewalDate']);
88
+ $DaysToReset = 30 - ((($DateNow - $DateSubscription) / 84600) % 30);
89
+ printf(__('%s/month, renews in %s days, on %s ( <a href="https://shortpixel.com/login/%s" target="_blank">Need More? See the options available</a> )','shortpixel-image-optimiser'),
90
+ $quotaData['APICallsQuota'], $DaysToReset,
91
+ date('M d, Y', strtotime(date('M d, Y') . ' + ' . $DaysToReset . ' days')), ( $this->hide_api_key) ? '' : $view->data->apiKey ); ?><br/>
92
+ <?php printf(__('<a href="https://shortpixel.com/login/%s/tell-a-friend" target="_blank">Join our friend referral system</a> to win more credits. For each user that joins, you receive +100 images credits/month.','shortpixel-image-optimiser'),
93
+ ( $this->hide_api_key ? '' : $view->data->apiKey));?>
94
+ <br><br>
95
+ <?php _e('Consumed: ','shortpixel-image-optimiser'); ?>
96
+ <strong><?php echo( number_format( $view->totalCallsMade['plan'] ) ); ?></strong>
97
+ <?php _e('; Remaining: ','shortpixel-image-optimiser'); ?>
98
+ <strong><?php echo( number_format( $quotaData['APICallsQuotaNumeric'] - $view->totalCallsMade['plan'] ) ); ?></strong>
99
+ </td>
100
+ </tr>
101
+ <tr>
102
+ <th scope="row">
103
+ <?php _e('Your One Time credits:','shortpixel-image-optimiser');?>
104
+ </th>
105
+ <td>
106
+ <?php _e('Total: ','shortpixel-image-optimiser'); ?>
107
+ <strong><?php echo( number_format($quotaData['APICallsQuotaOneTimeNumeric'])); ?></strong>
108
+ <br><br>
109
+ <?php _e('Consumed: ','shortpixel-image-optimiser'); ?>
110
+ <strong><?php echo( number_format($view->totalCallsMade['oneTime']) ); ?></strong>
111
+ <?php _e('; Remaining: ','shortpixel-image-optimiser'); ?>
112
+ <strong><?php echo( number_format( $quotaData['APICallsQuotaOneTimeNumeric'] - $view->totalCallsMade['oneTime'] ) ); ?></strong>**
113
+ </td>
114
+ </tr>
115
+ <tr>
116
+ <th><a href="https://<?php echo(SHORTPIXEL_API);?>/v2/report.php?key=<?php echo($this->hide_api_key ? '' : $view->data->apiKey);?>" target="_blank">
117
+ <?php _e('See report (last 40 days)','shortpixel-image-optimiser');?>
118
+ </a></th>
119
+ <td>&nbsp;</td>
120
+ </tr>
121
+ <tr>
122
+ <th scope="row">
123
+ <?php _e('Credits consumed on','shortpixel-image-optimiser');?>
124
+ <?php echo(parse_url(get_site_url(),PHP_URL_HOST));?>:
125
+ </th>
126
+ <td><strong><?php echo($view->data->fileCount);?></strong></td>
127
+ </tr>
128
+ <?php
129
+ // @todo This is always true, but must it be?
130
+ if(true || $view->data->backupImages) { ?>
131
+ <tr>
132
+ <th scope="row">
133
+ <?php _e('Original images are stored in a backup folder. Your backup folder\'s size is now:','shortpixel-image-optimiser');?>
134
+ </th>
135
+ <td>
136
+ <form action="" method="POST">
137
+ <?php $backupFolderSize = null; ?>
138
+ <?php if ($backupFolderSize === null) { ?>
139
+ <span id='backup-folder-size'>Calculating...</span>
140
+ <?php } else { echo($backupFolderSize); }?>
141
+ <input type="submit" style="margin-left: 15px; vertical-align: middle;" class="button button-secondary shortpixel-confirm"
142
+ name="emptyBackup" value="<?php _e('Empty backups','shortpixel-image-optimiser');?>"
143
+ data-confirm="<?php _e('Are you sure you want to delete all the backup images? You won\'t be able to restore from backup or to reoptimize with different settings if you delete the backups.','shortpixel-image-optimiser'); ?>"/>
144
+ </form>
145
+ </td>
146
+ </tr>
147
+ <?php } ?>
148
+ </tbody>
149
+ </table>
150
+ <div style="display:none">
151
+
152
+ </div>
153
+ <p style="padding-top: 0px; color: #818181;" ><?php _e('* Saved bandwidth is calculated at 10,000 impressions/image','shortpixel-image-optimiser');?></p>
154
+ <p style="padding-top: 0px; color: #818181;" >
155
+ <?php printf(__('** Increase your image quota by <a href="https://shortpixel.com/login/%s" target="_blank">upgrading your ShortPixel plan.</a>','shortpixel-image-optimiser'),
156
+ $this->hide_api_key ? '' : $view->data->apiKey );?>
157
+ </p>
158
+ </div>
159
+ </section>
160
+
161
+ <?php
162
+
163
+ if( $view->resources !== null && $quotaData['APICallsQuotaOneTimeNumeric']<10000 && $quotaData['APICallsQuotaNumeric']<5000 ) {?>
164
+ <section id="tab-resources" <?php echo ($this->display_part == 'resources') ? ' class="sel-tab" ' :''; ?>>
165
+ <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-resources"><?php _e('WP Resources','shortpixel-image-optimiser');?></a></h2>
166
+ <div class="wp-shortpixel-tab-content" style="visibility: hidden">
167
+ <?php echo((isset($view->resources['body']) ? $view->resources['body'] : __("Please reload",'shortpixel-image-optimiser')));?>
168
+ </div>
169
+ </section>
170
+ <?php } ?>
class/view/shortpixel-list-table.php CHANGED
@@ -197,7 +197,7 @@ class ShortPixelListTable extends WP_List_Table {
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
  /**
197
  }
198
 
199
  public function no_items() {
200
+ echo(__('No images avaliable. Go to <a href="options-general.php?page=wp-shortpixel-settings&part=adv-settings">Advanced Settings</a> to configure additional folders to be optimized.','shortpixel-image-optimiser'));
201
  }
202
 
203
  /**
class/view/shortpixel_view.php CHANGED
@@ -1,9 +1,12 @@
1
  <?php
 
2
 
3
  class ShortPixelView {
4
 
5
  private $ctrl;
6
 
 
 
7
  public function __construct($controller) {
8
  $this->ctrl = $controller;
9
  }
@@ -48,7 +51,7 @@ class ShortPixelView {
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>
@@ -68,7 +71,7 @@ class ShortPixelView {
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');?>
72
  </p>
73
  <p><?php _e('If you don’t have an API Key, you can get one delivered to your inbox, for free.','shortpixel-image-optimiser');?></p>
74
  <p><?php _e('Please <a href="https://shortpixel.com/wp-apikey' . WPShortPixel::getAffiliateSufix() . '" target="_blank">sign up to get your API key.</a>','shortpixel-image-optimiser');?>
@@ -96,7 +99,7 @@ class ShortPixelView {
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;">
@@ -158,10 +161,10 @@ class ShortPixelView {
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;
@@ -204,7 +207,11 @@ class ShortPixelView {
204
  public function displayBulkProcessingForm($quotaData, $thumbsProcessedCount, $under5PercentCount, $bulkRan,
205
  $averageCompression, $filesOptimized, $savedSpace, $percent, $customCount) {
206
  $settings = $this->ctrl->getSettings();
207
- $this->ctrl->outputHSBeacon();
 
 
 
 
208
  ?>
209
  <div class="wrap short-pixel-bulk-page">
210
  <h1><?php _e('Bulk Image Optimization by ShortPixel','shortpixel-image-optimiser');?></h1>
@@ -271,19 +278,27 @@ class ShortPixelView {
271
  </div>
272
  </a>
273
  </div>
274
- <?php if($quotaData['mainProcessedMlFiles'] > 0) {?>
275
- <div style="position: absolute;bottom: 10px;right: 10px;">
276
- <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="margin-bottom:10px;"><br>
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">
284
  <?php _e('Nothing to optimize! The images that you add to Media Gallery will be automatically optimized after upload.','shortpixel-image-optimiser');?>
285
  </div>
286
  <?php } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  </form>
288
  </div>
289
  <?php if($quotaData['totalFiles'] - $quotaData['totalProcessedFiles'] > 0) { ?>
@@ -329,16 +344,24 @@ class ShortPixelView {
329
  <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>"
330
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider@2x.png' ));?> 2x'>
331
  </div>
332
- <div class="sp-bulk-summary">
333
  <input type="text" value="<?php echo("" . round($averageCompression))?>" id="sp-total-optimization-dial" class="dial">
334
  </div>
 
335
  <p style="margin-top:4px;">
336
  <span style="font-size:1.2em;font-weight:bold"><?php _e('Congratulations!','shortpixel-image-optimiser');?></span><br>
337
  <?php _e('Your media library has been successfully optimized!','shortpixel-image-optimiser');?>
338
  <span class="sp-bulk-summary"><a href='javascript:void(0);'><?php _e('Summary','shortpixel-image-optimiser');?></a></span>
339
  </p>
 
 
 
 
 
 
 
340
  </div>
341
- <div class='sp-notice sp-notice-success sp-floating-block sp-single-width' style="height: 80px;overflow:hidden;padding-right: 0;">
342
  <div style="float:left; margin-top:-5px">
343
  <p style='margin-bottom: -2px; font-weight: bold;'>
344
  <?php _e('Share your optimization results:','shortpixel-image-optimiser');?>
@@ -399,11 +422,11 @@ class ShortPixelView {
399
  </div>
400
  <?php } ?>
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>
@@ -530,7 +553,7 @@ class ShortPixelView {
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')."\"" : ""); ?>>
@@ -538,7 +561,9 @@ class ShortPixelView {
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>
543
  </div>
544
  <?php } ?>
@@ -623,16 +648,25 @@ class ShortPixelView {
623
  } else {
624
  $percentAfter = $percent . "%";
625
  }
 
 
 
 
626
  ?>
627
  <div class="sp-notice sp-notice-info bulk-progress sp-floating-block sp-full-width">
628
  <div style="float:right">
629
- <?php if(false) { ?>
630
  <div class="bulk-progress-indicator">
631
  <div style="margin-bottom:5px"><?php _e('Remaining credits','shortpixel-image-optimiser');?></div>
632
  <div style="margin-top:22px;margin-bottom: 5px;font-size:2em;font-weight: bold;"><?php echo(number_format($remainingQuota))?></div>
633
  <div>images</div>
634
  </div>
635
  <?php } ?>
 
 
 
 
 
636
  <div class="bulk-progress-indicator">
637
  <div style="margin-bottom:5px"><?php _e('Average reduction','shortpixel-image-optimiser');?></div>
638
  <div id="sp-avg-optimization"><input type="text" id="sp-avg-optimization-dial-bulk" value="<?php echo("" . round($averageCompression))?>" class="dial"></div>
@@ -642,6 +676,7 @@ class ShortPixelView {
642
  });
643
  </script>
644
  </div>
 
645
  </div>
646
  <?php if($running) {
647
  if($type > 0) { ?>
@@ -694,7 +729,7 @@ class ShortPixelView {
694
  <span class="resumeLabel"><?php echo( !$running ? __('Resume: ','shortpixel-image-optimiser') : "");?></span>
695
  </form>
696
  <?php } else { ?>
697
- <a href="options-general.php?page=wp-shortpixel" class="button button-primary bulk-cancel" style="margin-left:10px"><?php _e('Manage custom folders','shortpixel-image-optimiser');?></a>
698
  <?php }?>
699
  </div>
700
  <?php
@@ -729,6 +764,8 @@ class ShortPixelView {
729
  </div>
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 ) {
@@ -768,7 +805,7 @@ class ShortPixelView {
768
  <?php } ?>
769
 
770
  <article id="shortpixel-settings-tabs" class="sp-tabs">
771
- <form name='wp_shortpixel_options' action='options-general.php?page=wp-shortpixel&noheader=true' method='post' id='wp_shortpixel_options'>
772
  <section <?php echo($showAdvanced ? "" : "class='sel-tab'");?> id="tab-settings">
773
  <?php if($this->ctrl->getVerifiedKey()) { ?>
774
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-settings"><?php _e('General','shortpixel-image-optimiser');?></a></h2>
@@ -1417,7 +1454,7 @@ class ShortPixelView {
1417
  /**
1418
  * @desc This form is used in WP back-end to allow users that use CloudFlare to save their settings
1419
  *
1420
- * @link wp-admin/options-general.php?page=wp-shortpixel
1421
  */
1422
  function display_cloudflare_settings_form()
1423
  { ?>
@@ -1429,7 +1466,7 @@ class ShortPixelView {
1429
  }
1430
  ?>
1431
  <p><?php _e("If you're using Cloudflare on your site then we advise you to fill in the details below. This will allow ShortPixel to work seamlessly with Cloudflare so that any image optimized/restored by ShortPixel will be automatically updated on Cloudflare as well.",'shortpixel-image-optimiser');?></p>
1432
- <form name='wp_shortpixel_cloudflareAPI' action='options-general.php?page=wp-shortpixel&noheader=true'
1433
  method='post' id='wp_shortpixel_cloudflareAPI'>
1434
  <table class="form-table">
1435
  <tbody>
@@ -1633,9 +1670,9 @@ class ShortPixelView {
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':
@@ -1726,6 +1763,7 @@ class ShortPixelView {
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);?>'>
1731
  <div class="sp-dropdown">
1
  <?php
2
+ use \ShortPixel\ShortPixelLogger as Log;
3
 
4
  class ShortPixelView {
5
 
6
  private $ctrl;
7
 
8
+ protected $bulkType;
9
+
10
  public function __construct($controller) {
11
  $this->ctrl = $controller;
12
  }
51
  <?php } ?></p>
52
  <div> <!-- style='float:right;margin-top:20px;'> -->
53
  <button class="button button-primary" id="shortpixel-upgrade-advice" onclick="ShortPixel.proposeUpgrade()" style="margin-right:10px;"><strong>
54
+ <?php _e('Show me the best available options', 'shortpixel-image-optimiser'); ?></strong></button>
55
  <a class='button button-primary' href='https://shortpixel.com/login/<?php echo($this->ctrl->getApiKey());?>'
56
  title='<?php _e('Go to my account and select a plan','shortpixel-image-optimiser');?>' target='_blank' style="margin-right:10px;">
57
  <strong><?php _e('Upgrade','shortpixel-image-optimiser');?></strong>
71
  public static function displayApiKeyAlert()
72
  { ?>
73
  <p><?php _e('In order to start the optimization process, you need to validate your API Key in the '
74
+ . '<a href="options-general.php?page=wp-shortpixel-settings">ShortPixel Settings</a> page in your WordPress Admin.','shortpixel-image-optimiser');?>
75
  </p>
76
  <p><?php _e('If you don’t have an API Key, you can get one delivered to your inbox, for free.','shortpixel-image-optimiser');?></p>
77
  <p><?php _e('Please <a href="https://shortpixel.com/wp-apikey' . WPShortPixel::getAffiliateSufix() . '" target="_blank">sign up to get your API key.</a>','shortpixel-image-optimiser');?>
99
  <div style="float:right;">
100
  <?php if($when == 'upgmonth' || $when == 'upgbulk'){ ?>
101
  <button class="button button-primary" id="shortpixel-upgrade-advice" onclick="ShortPixel.proposeUpgrade()" style="margin-top:10px;margin-left:10px;"><strong>
102
+ <?php _e('Show me the best available options', 'shortpixel-image-optimiser'); ?></strong></button>
103
  <?php } ?>
104
  <?php if($when == 'unlisted'){ ?>
105
  <a href="javascript:ShortPixel.includeUnlisted()" class="button button-primary" style="margin-top:10px;margin-left:10px;">
161
  <p> <?php
162
  if($when == 'upgmonth') {
163
  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>."
164
+ . " You might need to upgrade your plan in order to have all your images optimized.", 'shortpixel-image-optimiser'), $extra['monthAvg'], $extra['monthlyQuota']);
165
  } else {
166
  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."
167
+ . " You might need to upgrade your plan in order to have all your images optimized.", 'shortpixel-image-optimiser'), $extra['filesTodo'], $extra['quotaAvailable']);
168
  }?></p><?php
169
  self::includeProposeUpgradePopup();
170
  break;
207
  public function displayBulkProcessingForm($quotaData, $thumbsProcessedCount, $under5PercentCount, $bulkRan,
208
  $averageCompression, $filesOptimized, $savedSpace, $percent, $customCount) {
209
  $settings = $this->ctrl->getSettings();
210
+ //$this->ctrl->outputHSBeacon();
211
+ \ShortPixel\HelpScout::outputBeacon($this->ctrl->getApiKey());
212
+
213
+ $this->bulkType = $this->ctrl->getPrioQ()->getBulkTypeForDisplay(); // adding to the mess
214
+ $hider = ($this->bulkType == ShortPixelQueue::BULK_TYPE_RESTORE) ? 'sp-hidden' : '';
215
  ?>
216
  <div class="wrap short-pixel-bulk-page">
217
  <h1><?php _e('Bulk Image Optimization by ShortPixel','shortpixel-image-optimiser');?></h1>
278
  </div>
279
  </a>
280
  </div>
281
+ <?php } else {?>
 
 
 
 
 
 
 
 
282
  <div class="bulk-play bulk-nothing-optimize">
283
  <?php _e('Nothing to optimize! The images that you add to Media Gallery will be automatically optimized after upload.','shortpixel-image-optimiser');?>
284
  </div>
285
  <?php } ?>
286
+ <?php if($quotaData['mainProcessedMlFiles'] > 0) {?>
287
+ <div style="position: absolute;bottom: 10px;right: 10px;">
288
+ <?php /* <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="margin-bottom:10px;">
289
+ */ ?>
290
+ <p><a class='button' style="width:100%; text-align: center" href='<?php echo add_query_arg('part','bulk-restore-all'); ?> '><?php _e('Bulk Restore Images','shortpixel-image-optimiser'); ?></a></p>
291
+
292
+
293
+ <input type='submit' name='bulkCleanup' id='bulkCleanup' class='button' value='<?php _e('Bulk Delete SP Metadata','shortpixel-image-optimiser');?>'
294
+ onclick="ShortPixel.confirmBulkAction('Cleanup', event)" style="width:100%; text-align: center">
295
+
296
+
297
+ <?php if (Log::debugIsActive() ) { ?>
298
+ <input type='submit' name='bulkCleanupPending' id='bulkCleanupPending' class='button' value='<?php _e('Bulk Delete Pending Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('CleanupPending', event)">
299
+ <?php } ?>
300
+ </div>
301
+ <?php } ?>
302
  </form>
303
  </div>
304
  <?php if($quotaData['totalFiles'] - $quotaData['totalProcessedFiles'] > 0) { ?>
344
  <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>"
345
  srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider@2x.png' ));?> 2x'>
346
  </div>
347
+ <div class="sp-bulk-summary <?php echo $hider ?>">
348
  <input type="text" value="<?php echo("" . round($averageCompression))?>" id="sp-total-optimization-dial" class="dial">
349
  </div>
350
+ <?php if ($this->bulkType != ShortPixelQueue::BULK_TYPE_RESTORE): ?>
351
  <p style="margin-top:4px;">
352
  <span style="font-size:1.2em;font-weight:bold"><?php _e('Congratulations!','shortpixel-image-optimiser');?></span><br>
353
  <?php _e('Your media library has been successfully optimized!','shortpixel-image-optimiser');?>
354
  <span class="sp-bulk-summary"><a href='javascript:void(0);'><?php _e('Summary','shortpixel-image-optimiser');?></a></span>
355
  </p>
356
+ <?php else: ?>
357
+ <p style="margin-top:4px;">
358
+ <span style="font-size:1.2em;font-weight:bold"><?php _e('Restoring completed','shortpixel-image-optimiser');?></span><br>
359
+ <?php _e('Your images have been restored','shortpixel-image-optimiser');?>
360
+ <span class="sp-bulk-summary"><a href='javascript:void(0);'><?php _e('Summary','shortpixel-image-optimiser');?></a></span>
361
+ </p>
362
+ <?php endif; ?>
363
  </div>
364
+ <div class='sp-notice sp-notice-success sp-floating-block sp-single-width <?php echo $hider ?>' style="height: 80px;overflow:hidden;padding-right: 0;">
365
  <div style="float:left; margin-top:-5px">
366
  <p style='margin-bottom: -2px; font-weight: bold;'>
367
  <?php _e('Share your optimization results:','shortpixel-image-optimiser');?>
422
  </div>
423
  <?php } ?>
424
  </div>
425
+ <div id="sp-bulk-stats <?php echo $hider ?>" style="display:none">
426
  <?php $this->displayBulkStats($quotaData['totalProcessedFiles'], $quotaData['mainProcessedFiles'], $under5PercentCount, $averageCompression, $savedSpace);?>
427
  </div>
428
  </div>
429
+ <p><?php printf(__('Go to the ShortPixel <a href="%soptions-general.php?page=wp-shortpixel-settings&part=stats">Stats</a>
430
  and see all your websites\' optimized stats. Download your detailed <a href="https://%s/v2/report.php?key=%s">Optimization Report</a>
431
  to check your image optimization statistics for the last 40 days.','shortpixel-image-optimiser'),
432
  get_admin_url(), SHORTPIXEL_API, (defined("SHORTPIXEL_HIDE_API_KEY") ? '' : $this->ctrl->getApiKey()) );?></p>
553
  <input type='checkbox' id='bulk-thumbnails' name='thumbnails' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>
554
  onchange="ShortPixel.onBulkThumbsCheck(this)"> <?php _e('Include thumbnails','shortpixel-image-optimiser');?><br><br>
555
 
556
+ <a class='button' style="float: right;" href='<?php echo add_query_arg('part','bulk-restore-all'); ?> '><?php _e('Bulk Restore Images','shortpixel-image-optimiser'); ?></a>
557
 
558
  <input type='submit' name='bulkProcess' id='bulkProcess' class='button button-primary' value='<?php _e('Restart Optimizing','shortpixel-image-optimiser');?>'
559
  <?php echo($settings->quotaExceeded? "disabled title=\"" . __("Top-up your account to optimize more images.",'shortpixel-image-optimiser')."\"" : ""); ?>>
561
 
562
 
563
  <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;">
564
+ <?php if (Log::debugIsActive() ) { ?>
565
+ <input type='submit' name='bulkCleanupPending' id='bulkCleanupPending' class='button' value='<?php _e('Bulk Delete Pending Metadata','shortpixel-image-optimiser');?>' onclick="ShortPixel.confirmBulkAction('CleanupPending', event)">
566
+ <?php } ?>
567
  </form>
568
  </div>
569
  <?php } ?>
648
  } else {
649
  $percentAfter = $percent . "%";
650
  }
651
+
652
+ // should not be needed.. (but for some reason works)
653
+ $this->bulkType = $this->ctrl->getPrioQ()->getBulkType(); // adding to the mess
654
+
655
  ?>
656
  <div class="sp-notice sp-notice-info bulk-progress sp-floating-block sp-full-width">
657
  <div style="float:right">
658
+ <?php if(false) { // @todo This never runs? ?>
659
  <div class="bulk-progress-indicator">
660
  <div style="margin-bottom:5px"><?php _e('Remaining credits','shortpixel-image-optimiser');?></div>
661
  <div style="margin-top:22px;margin-bottom: 5px;font-size:2em;font-weight: bold;"><?php echo(number_format($remainingQuota))?></div>
662
  <div>images</div>
663
  </div>
664
  <?php } ?>
665
+
666
+ <?php
667
+ Log::addDebug('bulktype', array('b' => $this->bulkType));
668
+ Log::addDebug($this->bulkType);
669
+ if ($this->bulkType != ShortPixelQueue::BULK_TYPE_RESTORE): ?>
670
  <div class="bulk-progress-indicator">
671
  <div style="margin-bottom:5px"><?php _e('Average reduction','shortpixel-image-optimiser');?></div>
672
  <div id="sp-avg-optimization"><input type="text" id="sp-avg-optimization-dial-bulk" value="<?php echo("" . round($averageCompression))?>" class="dial"></div>
676
  });
677
  </script>
678
  </div>
679
+ <?php endif; ?>
680
  </div>
681
  <?php if($running) {
682
  if($type > 0) { ?>
729
  <span class="resumeLabel"><?php echo( !$running ? __('Resume: ','shortpixel-image-optimiser') : "");?></span>
730
  </form>
731
  <?php } else { ?>
732
+ <a href="options-general.php?page=wp-shortpixel-settings" class="button button-primary bulk-cancel" style="margin-left:10px"><?php _e('Manage custom folders','shortpixel-image-optimiser');?></a>
733
  <?php }?>
734
  </div>
735
  <?php
764
  </div>
765
  <?php
766
  }
767
+
768
+ // @TODO Remove this function
769
  function displaySettings($showApiKey, $editApiKey, $quotaData, $notice, $resources = null, $averageCompression = null, $savedSpace = null, $savedBandwidth = null,
770
  $remainingImages = null, $totalCallsMade = null, $fileCount = null, $backupFolderSize = null,
771
  $customFolders = null, $folderMsg = false, $addedFolder = false, $showAdvanced = false, $cloudflareAPI = false, $htaccessWriteable = false, $isNginx = false ) {
805
  <?php } ?>
806
 
807
  <article id="shortpixel-settings-tabs" class="sp-tabs">
808
+ <form name='wp_shortpixel_options' action='options-general.php?page=wp-shortpixel-settings&noheader=true' method='post' id='wp_shortpixel_options'>
809
  <section <?php echo($showAdvanced ? "" : "class='sel-tab'");?> id="tab-settings">
810
  <?php if($this->ctrl->getVerifiedKey()) { ?>
811
  <h2><a class='tab-link' href='javascript:void(0);' data-id="tab-settings"><?php _e('General','shortpixel-image-optimiser');?></a></h2>
1454
  /**
1455
  * @desc This form is used in WP back-end to allow users that use CloudFlare to save their settings
1456
  *
1457
+ * @link wp-admin/options-general.php?page=wp-shortpixel-settings
1458
  */
1459
  function display_cloudflare_settings_form()
1460
  { ?>
1466
  }
1467
  ?>
1468
  <p><?php _e("If you're using Cloudflare on your site then we advise you to fill in the details below. This will allow ShortPixel to work seamlessly with Cloudflare so that any image optimized/restored by ShortPixel will be automatically updated on Cloudflare as well.",'shortpixel-image-optimiser');?></p>
1469
+ <form name='wp_shortpixel_cloudflareAPI' action='options-general.php?page=wp-shortpixel-settings&noheader=true'
1470
  method='post' id='wp_shortpixel_cloudflareAPI'>
1471
  <table class="form-table">
1472
  <tbody>
1670
  break;
1671
  case 'invalidKey':
1672
  if(defined("SHORTPIXEL_API_KEY")) { // multisite key - need to be validated on each site but it's not invalid
1673
+ ?> <?php _e('Please <a href="options-general.php?page=wp-shortpixel-settings">go to Settings</a> to validate the API Key.','shortpixel-image-optimiser');?> <?php
1674
  } else {
1675
+ ?> <?php _e('Invalid API Key. <a href="options-general.php?page=wp-shortpixel-settings">Check your Settings</a>','shortpixel-image-optimiser');?> <?php
1676
  }
1677
  break;
1678
  case 'quotaExceeded':
1763
  }
1764
 
1765
  public function renderListCell($id, $status, $showActions, $thumbsRemain, $backup, $type, $invType, $message, $extraClass = '') {
1766
+
1767
  if($showActions && ($backup || $thumbsRemain)) { ?>
1768
  <div class='sp-column-actions <?php echo($extraClass);?>'>
1769
  <div class="sp-dropdown">
class/view/view-debug-box.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Debug Box to load Log File
3
+ namespace ShortPixel;
4
+ wp_enqueue_script( 'jquery-ui-draggable' );
5
+ ?>
6
+
7
+ <style>
8
+ .sp_debug_box
9
+ {
10
+ position: absolute;
11
+ right: 0px;
12
+ top: 50px;
13
+ background-color: #fff;
14
+ width: 100px;
15
+ z-index: 1000000;
16
+ border: 1px solid #000;
17
+ }
18
+ .sp_debug_box .header
19
+ {
20
+ min-height: 10px;
21
+ background: #000;
22
+ color: #fff;
23
+ }
24
+ .sp_debug_box .content_box
25
+ {
26
+ background: #ccc;
27
+ }
28
+ </style>
29
+
30
+ <script language='javascript'>
31
+ jQuery(document).ready(function($)
32
+ {
33
+ $( ".sp_debug_box" ).draggable();
34
+
35
+ });
36
+ </script>
37
+
38
+ <div class='sp_debug_box'>
39
+ <div class='header'>Debug Box </div>
40
+ <a target="_blank" href='<?php echo $this->layout->logLink ?>'>Logfile</a>
41
+ <div class='content_box'>
42
+
43
+ </div>
44
+ </div>
class/view/view-restore-all.php CHANGED
@@ -42,6 +42,9 @@
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 -->
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 class='error'><p><?php _e('It is strongly recommended to backup your uploads', 'shortpixel-image-optimizer'); ?></p>
46
+ </div>
47
+
48
  </div> <!-- sp-notice -->
49
  </form>
50
  </div> <!-- wrap -->
class/view/view-settings.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use Shortpixel\ShortPixelLogger as Log;
4
+
5
+ HelpScout::outputBeacon($view->data->apiKey);
6
+
7
+ ?>
8
+ <div class="wrap">
9
+ <h1><?php _e('ShortPixel Plugin Settings','shortpixel-image-optimiser');?></h1>
10
+ <p class='top-menu'>
11
+
12
+ <a href="https://shortpixel.com/<?php
13
+ echo(($view->data->apiKey ? "login/".( $this->hide_api_key ? '' : $view->data->apiKey) : "pricing"));
14
+ ?>" target="_blank">
15
+ <?php _e('Upgrade now','shortpixel-image-optimiser');?>
16
+ </a> | <a href="https://shortpixel.com/pricing>#faq" target="_blank"><?php _e('FAQ','shortpixel-image-optimiser');?> </a> |
17
+ <a href="https://shortpixel.com/contact" target="_blank"><?php _e('Support','shortpixel-image-optimiser');?> </a>
18
+ </p>
19
+
20
+
21
+ <article id="shortpixel-settings-tabs" class="sp-tabs">
22
+ <?php if (! $this->is_verifiedkey)
23
+ {
24
+ $this->loadView('settings/part-nokey');
25
+ } ?>
26
+
27
+ <?php
28
+ if ($this->is_verifiedkey):
29
+ ?>
30
+ <form name='wp_shortpixel_options' action='<?php echo add_query_arg('noheader', 'true') ?>' method='post' id='wp_shortpixel_options'>
31
+ <input type='hidden' name='display_part' value="<?php echo $this->display_part ?>" />
32
+ <div class='section-wrapper'>
33
+ <?php
34
+ $this->loadView('settings/part-general');
35
+ $this->loadView('settings/part-advanced');
36
+ $this->loadView('settings/part-cloudflare');
37
+ if ($view->averageCompression !== null)
38
+ {
39
+ $this->loadView('settings/part-statistics');
40
+ }
41
+ if (Log::debugIsActive())
42
+ {
43
+ $this->loadView('settings/part-debug');
44
+ }
45
+ ?>
46
+ </div>
47
+ </form>
48
+ <?php
49
+ endif;
50
+ ?>
51
+
52
+ </article>
53
+
54
+ <?php // @todo inline JS ?>
55
+ <script>
56
+ jQuery(document).ready(function(){
57
+ ShortPixel.initSettings();
58
+ });
59
+ </script>
class/wp-short-pixel.php CHANGED
@@ -1,5 +1,10 @@
1
  <?php
2
 
 
 
 
 
 
3
  class WPShortPixel {
4
 
5
  const BULK_EMPTY_QUEUE = 0;
@@ -41,6 +46,9 @@ class WPShortPixel {
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')) {
@@ -94,7 +102,7 @@ class WPShortPixel {
94
 
95
  if($isAdminUser) {
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'));
@@ -140,7 +148,8 @@ class WPShortPixel {
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()) {
@@ -156,9 +165,10 @@ class WPShortPixel {
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
 
@@ -169,10 +179,14 @@ class WPShortPixel {
169
  $this->__construct();
170
  }
171
 
 
 
172
  public function registerSettingsPage() {
173
- add_options_page( __('ShortPixel Settings','shortpixel-image-optimiser'), 'ShortPixel', 'manage_options', 'wp-shortpixel', array($this, 'renderSettingsMenu'));
174
  }
175
 
 
 
176
  function registerAdminPage( ) {
177
  if($this->spMetaDao->hasFoldersTable() && count($this->spMetaDao->getFolders())) {
178
  /*translators: title and menu name for the Other media page*/
@@ -331,6 +345,9 @@ class WPShortPixel {
331
  return $found;
332
  }
333
 
 
 
 
334
  public function displayAdminNotices() {
335
  if(!ShortPixelQueue::testQ()) {
336
  ShortPixelView::displayActivationNotice('fileperms');
@@ -442,40 +459,40 @@ class WPShortPixel {
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);
459
- }
460
  }
461
 
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
476
- $this->view->renderListCell("__SP_ID__", 'imgOptimized', true, "__SP_THUMBS_TOTAL__", true, true,
477
- array("__SP_FIRST_TYPE__", "__SP_SECOND_TYPE__"), "__SP_CELL_MESSAGE__", 'sp-column-actions-template');
478
- }
479
 
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'))) {
@@ -483,25 +500,16 @@ class WPShortPixel {
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
  }
491
- ?>
492
- <script type="text/javascript" >
493
- //check after 10 seconds if ShortPixel initialized OK, if not, force the init (could happen if a JS error somewhere else stopped the JS execution).
494
- function delayedInit() {
495
- if(typeof ShortPixel !== "undefined") {
496
- ShortPixel.init();
497
- } else {
498
- setTimeout(delayedInit, 10000);
499
- }
500
- }
501
- setTimeout(delayedInit, 10000);
502
- </script> <?php
503
 
504
- wp_register_script('shortpixel' . $this->jsSuffix, plugins_url('/res/js/shortpixel' . $this->jsSuffix,SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
 
 
505
 
506
  // Using an Array within another Array to protect the primitive values from being cast to strings
507
  $ShortPixelConstants = array(array(
@@ -518,13 +526,20 @@ class WPShortPixel {
518
  'WP_PLUGIN_URL'=>plugins_url( '', SHORTPIXEL_PLUGIN_FILE ),
519
  'WP_ADMIN_URL'=>admin_url(),
520
  'API_KEY'=> (defined("SHORTPIXEL_HIDE_API_KEY" ) || !is_admin() ) ? '' : $this->_settings->apiKey,
521
- 'DEFAULT_COMPRESSION'=>0 + $this->_settings->compressionType,
522
  'MEDIA_ALERT'=>$this->_settings->mediaAlert ? "done" : "todo",
523
  'FRONT_BOOTSTRAP'=>$this->_settings->frontBootstrap && (!isset($this->_settings->lastBackAction) || (time() - $this->_settings->lastBackAction > 600)) ? 1 : 0,
524
  'AJAX_URL'=>admin_url('admin-ajax.php'),
525
  'AFFILIATE'=>self::getAffiliateSufix()
526
  ));
527
 
 
 
 
 
 
 
 
528
  $jsTranslation = array(
529
  'optimizeWithSP' => __( 'Optimize with ShortPixel', 'shortpixel-image-optimiser' ),
530
  'redoLossy' => __( 'Re-optimize Lossy', 'shortpixel-image-optimiser' ),
@@ -564,6 +579,42 @@ class WPShortPixel {
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";
@@ -583,7 +634,7 @@ class WPShortPixel {
583
  $tooltip = '';
584
  $exceedTooltip = __('ShortPixel quota exceeded. Click for details.','shortpixel-image-optimiser');
585
  //$link = "http://shortpixel.com/login/" . $this->_settings->apiKey;
586
- $link = "options-general.php?page=wp-shortpixel";
587
  //$blank = '_blank';
588
  //$icon = "shortpixel-alert.png";
589
  }
@@ -638,7 +689,21 @@ class WPShortPixel {
638
  // 2. Perform the action
639
  case 'short-pixel-bulk':
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'))) {
@@ -779,7 +844,7 @@ class WPShortPixel {
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.
@@ -1020,11 +1085,19 @@ class WPShortPixel {
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 )
@@ -1033,7 +1106,9 @@ class WPShortPixel {
1033
  LIMIT " . SHORTPIXEL_MAX_RESULTS_QUERY;
1034
  $resultsPostMeta = $wpdb->get_results($queryPostMeta);
1035
  */
1036
- $resultsPostMeta = WpShortPixelMediaLbraryAdapter::getPostMetaSlice($crtStartQueryID, $endQueryID, SHORTPIXEL_MAX_RESULTS_QUERY);
 
 
1037
 
1038
  if ( empty($resultsPostMeta) ) {
1039
  $crtStartQueryID -= SHORTPIXEL_MAX_RESULTS_QUERY;
@@ -1045,11 +1120,21 @@ class WPShortPixel {
1045
  continue;
1046
  }
1047
 
 
 
1048
  foreach ( $resultsPostMeta as $itemMetaData ) {
1049
  $crtStartQueryID = $itemMetaData->post_id;
 
 
 
 
 
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);
@@ -1079,7 +1164,9 @@ class WPShortPixel {
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
 
 
1083
  $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
1084
  if(count($URLsAndPATHs["URLs"])) {
1085
  $meta->setThumbsTodo(true);
@@ -1104,9 +1191,12 @@ class WPShortPixel {
1104
  $this->prioQ->setStartBulkId($startQueryID);
1105
  } else {
1106
  $crtStartQueryID--;
 
1107
  }
1108
  }
1109
- return array("items" => $itemList, "skipped" => $skippedAlreadyProcessed, "searching" => ($sanityCheck >= 150) || (SHORTPIXEL_MAX_EXECUTION_TIME >= 10 && time() - $this->timer >= SHORTPIXEL_MAX_EXECUTION_TIME - 5));
 
 
1110
  }
1111
 
1112
  /**
@@ -1123,6 +1213,7 @@ class WPShortPixel {
1123
  return $items;
1124
  }
1125
 
 
1126
  private function checkKey($ID) {
1127
  if( $this->_settings->verifiedKey == false) {
1128
  if($ID == null){
@@ -1168,7 +1259,7 @@ class WPShortPixel {
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()
@@ -1457,7 +1548,9 @@ class WPShortPixel {
1457
  if($result["Status"] !== ShortPixelAPI::STATUS_RETRY) {
1458
  $this->_settings->bulkLastStatus = $result;
1459
  }
1460
- die(json_encode($result));
 
 
1461
  }
1462
 
1463
 
@@ -1566,11 +1659,12 @@ class WPShortPixel {
1566
  return $URLsAndPATHs;
1567
  }
1568
 
 
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-":
@@ -1587,6 +1681,7 @@ class WPShortPixel {
1587
  return array("Status" => ShortPixelAPI::STATUS_FAIL, "message" => __('NextGen image not found','shortpixel-image-optimiser'));
1588
  break;
1589
  case "C-":
 
1590
  throw new Exception("HandleManualOptimization for custom images not implemented");
1591
  default:
1592
  $this->optimizeNowHook(intval($imageId), true);
@@ -1596,13 +1691,20 @@ class WPShortPixel {
1596
 
1597
  }
1598
 
 
 
 
 
1599
  public function checkStatus() {
1600
- $itemHandler = new ShortPixelMetaFacade($_GET['image_id']);
1601
  $meta = $itemHandler->getMeta();
1602
  die(json_encode(array("Status" => $meta->getStatus(), "Message" => $meta->getMessage())));
1603
  }
1604
 
1605
- //custom hook
 
 
 
1606
  public function optimizeNowHook($imageId, $manual = false) {
1607
  //WpShortPixel::log("OPTIMIZE NOW HOOK for ID: $imageId STACK: " . json_encode(debug_backtrace()));
1608
  if($this->isProcessable($imageId)) {
@@ -1645,7 +1747,7 @@ class WPShortPixel {
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
 
@@ -1684,6 +1786,10 @@ class WPShortPixel {
1684
  unset($this->thumbnailsRegenerating[$postId]);
1685
  }
1686
 
 
 
 
 
1687
  public function shortpixelGetBackupFilter($imagePath) {
1688
  $backup = str_replace(dirname(dirname(dirname(SHORTPIXEL_BACKUP_FOLDER))),SHORTPIXEL_BACKUP_FOLDER, $imagePath);
1689
  return file_exists($backup) ? $backup : false;
@@ -1704,7 +1810,10 @@ class WPShortPixel {
1704
  }
1705
 
1706
 
1707
- //save error in file's meta data
 
 
 
1708
  public function handleError($ID, $result)
1709
  {
1710
  $meta = wp_get_attachment_metadata($ID);
@@ -1713,6 +1822,10 @@ class WPShortPixel {
1713
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
1714
  }
1715
 
 
 
 
 
1716
  public function getBackupFolder($file) {
1717
  if(realpath($file)) {
1718
  $ret = $this->getBackupFolderInternal(realpath($file)); //found cases when $file contains for example /wp/../wp-content - clean it up
@@ -1722,6 +1835,10 @@ class WPShortPixel {
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);
@@ -1745,6 +1862,11 @@ class WPShortPixel {
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)) {
@@ -1752,12 +1874,20 @@ class WPShortPixel {
1752
  //try with the thumbnails
1753
  foreach($thumbs as $size) {
1754
  $backup = $this->getBackupFolder(trailingslashit(dirname($file)) . $size['file']);
1755
- if($backup) return $backup;
 
 
 
1756
  }
1757
  }
1758
- return apply_filters("shortpixel_backup_folder", $ret);
1759
  }
1760
 
 
 
 
 
 
1761
  protected function setFilePerms($file) {
1762
  //die(getenv('USERNAME') ? getenv('USERNAME') : getenv('USER'));
1763
 
@@ -1956,6 +2086,8 @@ class WPShortPixel {
1956
  set_transient("shortpixel_thrown_notice", array('when' => $when, 'extra' => $extra), 120);
1957
  }
1958
 
 
 
1959
  protected function catchNotice() {
1960
  $notice = get_transient("shortpixel_thrown_notice");
1961
  if(isset($notice['when'])) {
@@ -1981,6 +2113,9 @@ class WPShortPixel {
1981
  return substr($file, 0, strlen($file) - 1 - strlen($ext)) . "@2x." . $ext;
1982
  }
1983
 
 
 
 
1984
  public function doCustomRestore($ID) {
1985
  //$meta = $this->spMetaDao->getMeta($ID);
1986
  // meta facade as a custom image
@@ -2006,7 +2141,7 @@ class WPShortPixel {
2006
  $rename_result = @rename($bkFile, $file);
2007
  if (! $rename_result)
2008
  {
2009
- self::log('Failure on rename to : ' . $file);
2010
  }
2011
 
2012
 
@@ -2031,7 +2166,7 @@ class WPShortPixel {
2031
 
2032
  }
2033
  else {
2034
- self::log('File ' . $bkFile . ' not found in backups');
2035
  }
2036
 
2037
  return $meta;
@@ -2099,13 +2234,15 @@ class WPShortPixel {
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
 
2107
- //$thumbsCount = WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
 
2108
  $error = array('Status' => ShortPixelAPI::STATUS_SKIP, 'message' => __('Unspecified Error on Thumbnails for: ') . $ID);
 
2109
  $includedSizes = WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
2110
  $thumbsCount = count($includedSizes);
2111
 
@@ -2114,11 +2251,14 @@ class WPShortPixel {
2114
  $error['message'] = __('Please optimize image for ID: ','shortpixel-image-optimiser') . $ID;
2115
  die(json_encode($error));
2116
  }
 
2117
  if (! isset($meta['sizes']) || count($meta['sizes']) == 0)
2118
  {
2119
  $error['message'] = __('No thumbnails to optimize for ID: ','shortpixel-image-optimiser') . $ID;
2120
  die(json_encode($error));
2121
  }
 
 
2122
  /* Check ThumbList against current Sizes. It's possible when a size was dropped, the SP meta was not updated, playing
2123
  * tricks with the thumbcount.
2124
  *
@@ -2136,16 +2276,26 @@ class WPShortPixel {
2136
  $meta['ShortPixel']['thumbsOptList'] = $thumbList;
2137
  }
2138
 
 
 
 
 
 
 
 
 
2139
 
2140
- // if( isset($meta['ShortPixelImprovement'])
2141
- // && isset($meta['sizes']) && $thumbsCount
2142
- // && ( !isset($meta['ShortPixel']['thumbsOpt']) || $meta['ShortPixel']['thumbsOpt'] == 0
2143
- // || (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && $meta['ShortPixel']['thumbsOpt'] < $thumbsCount))) { //optimized without thumbs, thumbs exist
 
2144
 
2145
  if( $thumbsCount
2146
  && (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && count($meta['ShortPixel']['thumbsOptList']) < $thumbsCount))
2147
  {
2148
  $meta['ShortPixel']['thumbsTodo'] = true;
 
2149
  //wp_update_attachment_metadata($ID, $meta);
2150
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
2151
  $this->prioQ->push($ID);
@@ -2165,6 +2315,7 @@ class WPShortPixel {
2165
  } else {
2166
  $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);
2167
  }
 
2168
  die(json_encode($ret));
2169
  }
2170
 
@@ -2212,6 +2363,9 @@ class WPShortPixel {
2212
  }
2213
  }
2214
 
 
 
 
2215
  public function deactivatePlugin() {
2216
  if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'sp_deactivate_plugin_nonce' ) ) {
2217
  wp_nonce_ays( '' );
@@ -2230,9 +2384,12 @@ class WPShortPixel {
2230
 
2231
  }
2232
 
 
 
 
2233
  protected function deactivateAndRedirect($url){
2234
  //die(ShortPixelVDD($url));
2235
- deactivate_plugins( $_GET['plugin'] );
2236
  wp_safe_redirect( $url );
2237
  die();
2238
 
@@ -2276,7 +2433,8 @@ class WPShortPixel {
2276
  $quotaData = $this->getQuotaInformation();
2277
  }
2278
  if ( !$quotaData['APIKeyValid']) {
2279
- if(strlen($this->_settings->apiKey)) $this->view->displayActivationNotice('generic', $quotaData['Message']);
 
2280
  return $quotaData;
2281
  }
2282
  //$tempus = microtime(true);
@@ -2286,7 +2444,7 @@ class WPShortPixel {
2286
  if($quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] > $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']) {
2287
  $this->_settings->quotaExceeded = '0';
2288
  $this->_settings->prioritySkip = NULL;
2289
- self::log("CHECK QUOTA: Skipped: ".json_encode($this->prioQ->getSkipped()));
2290
 
2291
  ?><script>var shortPixelQuotaExceeded = 0;</script><?php
2292
  }
@@ -2297,10 +2455,17 @@ class WPShortPixel {
2297
  return $quotaData;
2298
  }
2299
 
 
 
 
 
2300
  public function isValidMetaId($id) {
2301
  return substr($id, 0, 2 ) == "C-" ? $this->spMetaDao->getMeta(substr($id, 2)) : wp_get_attachment_url($id);
2302
  }
2303
 
 
 
 
2304
  public function listCustomMedia() {
2305
  if( ! class_exists( 'ShortPixelListTable' ) ) {
2306
  require_once('view/shortpixel-list-table.php');
@@ -2319,21 +2484,22 @@ class WPShortPixel {
2319
  if ( isset($_GET['noheader']) ) {
2320
  require_once(ABSPATH . 'wp-admin/admin-header.php');
2321
  }
2322
- $this->outputHSBeacon();
 
2323
  ?>
2324
  <div class="wrap shortpixel-other-media">
2325
  <h2>
2326
- <div style="float:right;">
2327
- <a href="upload.php?page=wp-short-pixel-custom&refresh=1" id="refresh" class="button button-primary" title="<?php _e('Refresh custom folders content','shortpixel-image-optimiser');?>">
2328
- <?php _e('Refresh folders','shortpixel-image-optimiser');?>
2329
- </a>
2330
- </div>
2331
  <?php _e('Other Media optimized by ShortPixel','shortpixel-image-optimiser');?>
2332
  </h2>
2333
 
2334
- <div id="poststuff">
2335
- <div id="post-body" class="metabox-holder columns-2">
2336
- <div id="post-body-content">
 
 
 
 
 
2337
  <div class="meta-box-sortables ui-sortable">
2338
  <form method="get">
2339
  <input type="hidden" name="page" value="wp-short-pixel-custom" />
@@ -2360,7 +2526,7 @@ class WPShortPixel {
2360
  }
2361
 
2362
  /** Front End function that controls bulk processes.
2363
- *
2364
  */
2365
  public function bulkProcess() {
2366
  global $wpdb;
@@ -2415,7 +2581,7 @@ class WPShortPixel {
2415
 
2416
  if(isset($_POST["bulkRestore"]))
2417
  {
2418
- $bulkRestore = new \ShortPixel\BulkRestoreAll();
2419
  $bulkRestore->setShortPixel($this);
2420
  $bulkRestore->setupBulk();
2421
 
@@ -2488,7 +2654,7 @@ class WPShortPixel {
2488
  {
2489
  $viewObj = new $partControl();
2490
  $viewObj->setShortPixel($this);
2491
- $viewObj->loadView();
2492
  }
2493
 
2494
  if (! $template_part)
@@ -2509,6 +2675,7 @@ class WPShortPixel {
2509
  }
2510
  }
2511
 
 
2512
  public function bulkProgressMessage($percent, $minutes) {
2513
  $timeEst = "";
2514
  self::log("bulkProgressMessage(): percent: " . $percent);
@@ -2530,6 +2697,7 @@ class WPShortPixel {
2530
  return $timeEst;
2531
  }
2532
 
 
2533
  public function emptyBackup(){
2534
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2535
 
@@ -2549,6 +2717,7 @@ class WPShortPixel {
2549
  }
2550
  }
2551
 
 
2552
  public function backupFolderIsEmpty() {
2553
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2554
  return count(scandir(SHORTPIXEL_BACKUP_FOLDER)) > 2 ? false : true;
@@ -2609,13 +2778,19 @@ class WPShortPixel {
2609
  die();
2610
  }
2611
 
 
 
 
 
 
2612
  public function getComparerData() {
2613
  if (!isset($_POST['id']) || !current_user_can( 'upload_files' ) && !current_user_can( 'edit_posts' ) ) {
2614
  wp_die(json_encode((object)array('origUrl' => false, 'optUrl' => false, 'width' => 0, 'height' => 0)));
2615
  }
2616
 
2617
  $ret = array();
2618
- $handle = new ShortPixelMetaFacade($_POST['id']);
 
2619
 
2620
  $meta = $handle->getMeta();
2621
  $rawMeta = $handle->getRawMeta();
@@ -2655,6 +2830,7 @@ class WPShortPixel {
2655
  die(json_encode((object)$ret));
2656
  }
2657
 
 
2658
  public function newApiKey() {
2659
  if ( !current_user_can( 'manage_options' ) ) {
2660
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
@@ -2678,7 +2854,7 @@ class WPShortPixel {
2678
  )
2679
  );
2680
 
2681
- $newKeyResponse = wp_remote_post("https://shortpixel.com/free-sign-up-plugin" . $this->getAffiliateSufix(), $params);
2682
 
2683
  if ( is_object($newKeyResponse) && get_class($newKeyResponse) == 'WP_Error' ) {
2684
  die(json_encode((object)array('Status' => 'fail', 'Details' => '503')));
@@ -2746,6 +2922,7 @@ class WPShortPixel {
2746
 
2747
  }
2748
 
 
2749
  public static function getCustomFolderBase() {
2750
  if(is_main_site()) {
2751
  $base = get_home_path();
@@ -2756,12 +2933,14 @@ class WPShortPixel {
2756
  }
2757
  }
2758
 
 
2759
  protected function fullRefreshCustomFolder($path, &$notice) {
2760
  $folder = $this->spMetaDao->getFolder($path);
2761
  $diff = $folder->checkFolderContents(array('ShortPixelCustomMetaDao', 'getPathFiles'));
2762
  }
2763
 
2764
- protected function refreshCustomFolders(&$notice, $ignore = false) {
 
2765
  $customFolders = array();
2766
  if($this->_settings->hasCustomFolders) {
2767
  $customFolders = $this->spMetaDao->getFolders();
@@ -2790,7 +2969,10 @@ class WPShortPixel {
2790
  return $customFolders;
2791
  }
2792
 
2793
- protected static function alterHtaccess( $clear = false ){
 
 
 
2794
  // [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
2795
  $upload_dir = wp_upload_dir();
2796
  $upload_base = trailingslashit($upload_dir['basedir']);
@@ -2842,6 +3024,11 @@ class WPShortPixel {
2842
  ' ;
2843
 
2844
  insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', $rules);
 
 
 
 
 
2845
  insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', $rules);
2846
  insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', $rules);
2847
  /* insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '
@@ -2857,13 +3044,16 @@ Header append Vary Accept env=REDIRECT_webp
2857
  }
2858
  }
2859
 
 
 
 
2860
  public function renderSettingsMenu() {
2861
  if ( !current_user_can( 'manage_options' ) ) {
2862
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2863
  }
2864
 
2865
- wp_enqueue_style('sp-file-tree.min.css', plugins_url('/res/css/sp-file-tree.min.css',SHORTPIXEL_PLUGIN_FILE) );
2866
- wp_enqueue_script('sp-file-tree.min.js', plugins_url('/res/js/sp-file-tree.min.js',SHORTPIXEL_PLUGIN_FILE) );
2867
 
2868
  //die(var_dump($_POST));
2869
  $noticeHTML = "";
@@ -2871,13 +3061,13 @@ Header append Vary Accept env=REDIRECT_webp
2871
  $folderMsg = false;
2872
  $addedFolder = false;
2873
 
2874
- $this->_settings->redirectedSettings = 2;
2875
 
2876
  // Check if NGINX Server
2877
- $isNginx = strpos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false ? true : false;
2878
 
2879
  // BEGIN: Verify .htaccess writeability
2880
- $htaccessWriteable = true;
2881
  if( !$isNginx ) {
2882
  $htaccessPath = get_home_path() . '.htaccess';
2883
  $htaccessExisted = file_exists( $htaccessPath );
@@ -2886,25 +3076,26 @@ Header append Vary Accept env=REDIRECT_webp
2886
  if( !$htaccessExisted ){
2887
  unlink( $htaccessPath );
2888
  }
2889
- }
2890
  // END: Verify .htaccess writeability
2891
 
2892
 
2893
  //by default we try to fetch the API Key from wp-config.php (if defined)
2894
- if ( defined("SHORTPIXEL_API_KEY") && strlen(SHORTPIXEL_API_KEY) == 20)
2895
  {
2896
  if(!isset($_POST['save']) && (strlen($this->getApiKey()) == 0 || SHORTPIXEL_API_KEY != $this->getApiKey())) {
2897
  $_POST['validate'] = "validate";
2898
  }
2899
  $_POST['key'] = SHORTPIXEL_API_KEY;
2900
- }
2901
 
2902
  if(isset($_GET['setsparchive'])) {
2903
  $this->_settings->downloadArchive = intval($_GET['setsparchive']);
2904
  }
2905
 
2906
  //check all custom folders and update meta table if files appeared
2907
- $customFolders = $this->refreshCustomFolders($notice, isset($_POST['removeFolder']) ? $_POST['removeFolder'] : null);
 
2908
 
2909
  if(isset($_POST['request']) && $_POST['request'] == 'request') {
2910
  //a new API Key was requested
@@ -2922,21 +3113,21 @@ Header append Vary Accept env=REDIRECT_webp
2922
  }
2923
  }
2924
 
2925
- if ( isset( $_POST["saveCloudflare"] ) ) {
2926
  $cfApi = $this->_settings->cloudflareEmail = sanitize_text_field( $_POST['cloudflare-email'] );
2927
  $cfAuth = $this->_settings->cloudflareAuthKey = sanitize_text_field( $_POST['cloudflare-auth-key'] );
2928
- $cfZone = $this->_settings->cloudflareZoneID = sanitize_text_field( $_POST['cloudflare-zone-id'] );
2929
  $this->cloudflareApi->set_up($cfApi, $cfAuth, $cfZone);
2930
- }
2931
 
2932
  if( isset($_POST['save']) || isset($_POST['saveAdv'])
2933
  || (isset($_POST['validate']) && $_POST['validate'] == "validate")
2934
  || isset($_POST['removeFolder']) || isset($_POST['recheckFolder'])) {
2935
 
2936
  //handle API Key - common for save and validate.
2937
- $_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.
2938
 
2939
- if ( strlen($_POST['key']) <> 20 ){
2940
  $KeyLength = strlen($_POST['key']);
2941
 
2942
  $notice = array("status" => "error",
@@ -2958,8 +3149,8 @@ Header append Vary Accept env=REDIRECT_webp
2958
  }
2959
 
2960
  $validityData = $this->getQuotaInformation($_POST['key'], true, isset($_POST['validate']) && $_POST['validate'] == "validate", $_POST);
2961
-
2962
- $this->_settings->apiKey = $_POST['key'];
2963
  if($validityData['APIKeyValid']) {
2964
  if(isset($_POST['validate']) && $_POST['validate'] == "validate") {
2965
  // delete last status if it was no valid key
@@ -2981,7 +3172,7 @@ Header append Vary Accept env=REDIRECT_webp
2981
  }
2982
  $this->_settings->verifiedKey = true;
2983
  //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
2984
- if ( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && !@mkdir(SHORTPIXEL_BACKUP_FOLDER, 0777, true) )
2985
  $notice = array("status" => "error",
2986
  "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'),
2987
  WP_CONTENT_DIR . '/' . SHORTPIXEL_UPLOADS_NAME ));
@@ -2991,12 +3182,12 @@ Header append Vary Accept env=REDIRECT_webp
2991
  $notice = array("status" => "error", "msg" => $validityData["Message"]);
2992
  }
2993
  $this->_settings->verifiedKey = false;
2994
- }
2995
- }
2996
 
2997
  //if save button - we process the rest of the form elements
2998
  if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
2999
- $this->_settings->compressionType = intval($_POST['compressionType']);
3000
  if(isset($_POST['thumbnails'])) { $this->_settings->processThumbnails = 1; } else { $this->_settings->processThumbnails = 0; }
3001
  if(isset($_POST['backupImages'])) { $this->_settings->backupImages = 1; } else { $this->_settings->backupImages = 0; }
3002
  if(isset($_POST['cmyk2rgb'])) { $this->_settings->CMYKtoRGBconversion = 1; } else { $this->_settings->CMYKtoRGBconversion = 0; }
@@ -3006,9 +3197,9 @@ Header append Vary Accept env=REDIRECT_webp
3006
  $this->_settings->resizeType = (isset($_POST['resize_type']) ? sanitize_text_field($_POST['resize_type']) : false);
3007
  $this->_settings->resizeWidth = (isset($_POST['width']) ? intval($_POST['width']): $this->_settings->resizeWidth);
3008
  $this->_settings->resizeHeight = (isset($_POST['height']) ? intval($_POST['height']): $this->_settings->resizeHeight);
3009
- $uploadPath = realpath(SHORTPIXEL_UPLOADS_BASE);
3010
 
3011
- if(isset($_POST['nextGen'])) {
3012
  WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
3013
  $prevNextGen = $this->_settings->includeNextGen;
3014
  $this->_settings->includeNextGen = 1;
@@ -3017,21 +3208,19 @@ Header append Vary Accept env=REDIRECT_webp
3017
  $customFolders = $ret["customFolders"];
3018
  } else {
3019
  $this->_settings->includeNextGen = 0;
3020
- }
3021
- if(isset($_POST['addCustomFolder']) && strlen($_POST['addCustomFolder']) > 0) {
3022
  $folderMsg = $this->spMetaDao->newFolderFromPath(stripslashes($_POST['addCustomFolder']), $uploadPath, self::getCustomFolderBase());
3023
  if(!$folderMsg) {
3024
  $notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
3025
  }
3026
  $customFolders = $this->spMetaDao->getFolders();
3027
  $this->_settings->hasCustomFolders = time();
3028
- }
3029
-
3030
- $this->_settings->createWebp = (isset($_POST['createWebp']) ? 1: 0);
3031
 
 
3032
 
3033
-
3034
- if( isset( $_POST['createWebp'] ) && $_POST['createWebp'] == 'on' ){
3035
  if( isset( $_POST['deliverWebp'] ) && $_POST['deliverWebp'] == 'on' ){
3036
  if( isset( $_POST['deliverWebpType'] ) ) {
3037
  switch( $_POST['deliverWebpType'] ) {
@@ -3061,20 +3250,15 @@ Header append Vary Accept env=REDIRECT_webp
3061
  } else {
3062
  if(!$isNginx) self::alterHtaccess(true);
3063
  $this->_settings->deliverWebp = 0;
3064
- }
3065
-
3066
- //die(ShortPixelVDD($_POST));
3067
 
3068
- //if(isset($_POST['optimizeRetina'])
3069
-
3070
- $this->_settings->optimizeRetina = (isset($_POST['optimizeRetina']) ? 1: 0);
3071
  $this->_settings->optimizeUnlisted = (isset($_POST['optimizeUnlisted']) ? 1: 0);
3072
  $this->_settings->optimizePdfs = (isset($_POST['optimizePdfs']) ? 1: 0);
3073
- $this->_settings->png2jpg = (isset($_POST['png2jpg']) ? (isset($_POST['png2jpgForce']) ? 2 : 1): 0);
3074
-
3075
- //die(var_dump($_POST['excludePatterns']));
3076
 
3077
- if(isset($_POST['excludePatterns']) && strlen($_POST['excludePatterns'])) {
3078
  $patterns = array();
3079
  $items = explode(',', $_POST['excludePatterns']);
3080
  foreach($items as $pat) {
@@ -3089,9 +3273,10 @@ Header append Vary Accept env=REDIRECT_webp
3089
  } else {
3090
  $this->_settings->excludePatterns = array();
3091
  }
3092
- $this->_settings->frontBootstrap = (isset($_POST['frontBootstrap']) ? 1: 0);
3093
- $this->_settings->autoMediaLibrary = (isset($_POST['autoMediaLibrary']) ? 1: 0);
3094
- $this->_settings->excludeSizes = (isset($_POST['excludeSizes']) ? $_POST['excludeSizes']: array());
 
3095
 
3096
  //Redirect to bulk processing if requested
3097
  if( isset($_POST['save']) && $_POST['save'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')
@@ -3100,29 +3285,29 @@ Header append Vary Accept env=REDIRECT_webp
3100
  exit();
3101
  }
3102
  }
3103
- if(isset($_POST['removeFolder']) && strlen(($_POST['removeFolder']))) {
3104
  $this->spMetaDao->removeFolder($_POST['removeFolder']);
3105
  $customFolders = $this->spMetaDao->getFolders();
3106
  $_POST["saveAdv"] = true;
3107
- }
3108
  if(isset($_POST['recheckFolder']) && strlen(($_POST['recheckFolder']))) {
3109
  //$folder->fullRefreshCustomFolder($_POST['recheckFolder']); //aici singura solutie pare callback care spune daca exita url-ul complet
3110
  }
3111
  }
3112
 
3113
  //now output headers. They were prevented with noheaders=true in the form url in order to be able to redirect if bulk was pressed
3114
- if(isset($_REQUEST['noheader'])) {
3115
  require_once(ABSPATH . 'wp-admin/admin-header.php');
3116
- }
3117
 
3118
  //empty backup
3119
- if(isset($_POST['emptyBackup'])) {
3120
  $this->emptyBackup();
3121
- }
3122
 
3123
- $quotaData = $this->checkQuotaAndAlert(isset($validityData) ? $validityData : null, isset($_GET['checkquota']));
3124
 
3125
- if($this->hasNextGen) {
3126
  $ngg = array_map(array('ShortPixelNextGenAdapter','pathToAbsolute'), ShortPixelNextGenAdapter::getGalleries());
3127
  //die(var_dump($ngg));
3128
  for($i = 0; $i < count($customFolders); $i++) {
@@ -3130,14 +3315,14 @@ Header append Vary Accept env=REDIRECT_webp
3130
  $customFolders[$i]->setType("NextGen");
3131
  }
3132
  }
3133
- }
3134
 
3135
- $showApiKey = ( (is_main_site() || (function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY")))
3136
  && !defined("SHORTPIXEL_HIDE_API_KEY"));
3137
- $editApiKey = !defined("SHORTPIXEL_API_KEY") && $showApiKey;
3138
 
3139
  if($this->_settings->verifiedKey) {
3140
- $fileCount = number_format($this->_settings->fileCount);
3141
  $savedSpace = self::formatBytes($this->_settings->savedSpace,2);
3142
  $averageCompression = $this->getAverageCompression();
3143
  $savedBandwidth = self::formatBytes($this->_settings->savedSpace * 10000,2);
@@ -3147,11 +3332,11 @@ Header append Vary Accept env=REDIRECT_webp
3147
  $remainingImages = $quotaData['APICallsRemaining'];
3148
  $remainingImages = ( $remainingImages < 0 ) ? 0 : number_format($remainingImages);
3149
  $totalCallsMade = array( 'plan' => $quotaData['APICallsMadeNumeric'] , 'oneTime' => $quotaData['APICallsMadeOneTimeNumeric'] );
3150
-
3151
- $resources = wp_remote_post($this->_settings->httpProto . "://shortpixel.com/resources-frag");
3152
  if(is_wp_error( $resources )) {
3153
  $resources = array();
3154
- }
3155
 
3156
  $cloudflareAPI = true;
3157
 
@@ -3165,6 +3350,11 @@ Header append Vary Accept env=REDIRECT_webp
3165
 
3166
  }
3167
 
 
 
 
 
 
3168
  public function addNextGenGalleriesToCustom($silent) {
3169
  $customFolders = array();
3170
  $folderMsg = "";
@@ -3184,6 +3374,10 @@ Header append Vary Accept env=REDIRECT_webp
3184
  return array("message" => $silent? "" : $folderMsg, "customFolders" => $customFolders);
3185
  }
3186
 
 
 
 
 
3187
  public function getAverageCompression(){
3188
  return $this->_settings->totalOptimized > 0
3189
  ? round(( 1 - ( $this->_settings->totalOptimized / $this->_settings->totalOriginal ) ) * 100, 2)
@@ -3227,8 +3421,8 @@ Header append Vary Accept env=REDIRECT_webp
3227
  $args['body']['host'] = parse_url(get_site_url(),PHP_URL_HOST);
3228
  $argsStr .= "&host={$args['body']['host']}";
3229
  if(strlen($this->_settings->siteAuthUser)) {
3230
- $args['body']['user'] = $this->_settings->siteAuthUser;
3231
- $args['body']['pass'] = $this->_settings->siteAuthPass;
3232
  $argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
3233
  }
3234
  if($settings !== false) {
@@ -3275,7 +3469,7 @@ Header append Vary Accept env=REDIRECT_webp
3275
  $response = wp_remote_get($requestURL, $args);
3276
  $comm['C: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
3277
  }
3278
- self::log("API STATUS COMM: " . json_encode($comm));
3279
 
3280
  $defaultData = array(
3281
  "APIKeyValid" => false,
@@ -3361,6 +3555,9 @@ Header append Vary Accept env=REDIRECT_webp
3361
  $this->_settings->quotaExceeded = 0;
3362
  }
3363
 
 
 
 
3364
  public function generateCustomColumn( $column_name, $id, $extended = false ) {
3365
  if( 'wp-shortPixel' == $column_name ) {
3366
 
@@ -3374,9 +3571,10 @@ Header append Vary Accept env=REDIRECT_webp
3374
  $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
3375
 
3376
  if($extended && isset($_GET['SHORTPIXEL_DEBUG'])) {
 
3377
  var_dump(wp_get_attachment_url($id));
3378
  echo('<br><br>' . json_encode(ShortPixelMetaFacade::getWPMLDuplicates($id)));
3379
- echo('<br><br>' . json_encode($data));
3380
  echo('<br><br>');
3381
  }
3382
 
@@ -3503,11 +3701,22 @@ Header append Vary Accept env=REDIRECT_webp
3503
  }
3504
  }
3505
 
 
 
 
 
 
3506
  function columnRegisterSortable($columns) {
3507
  $columns['wp-shortPixel'] = 'ShortPixel Compression';
3508
  return $columns;
3509
  }
3510
 
 
 
 
 
 
 
3511
  function columnOrderFilterBy($vars) {
3512
  if ( isset( $vars['orderby'] ) && 'ShortPixel Compression' == $vars['orderby'] ) {
3513
  $vars = array_merge( $vars, array(
@@ -3534,6 +3743,10 @@ Header append Vary Accept env=REDIRECT_webp
3534
  return $vars;
3535
  }
3536
 
 
 
 
 
3537
  function mediaAddFilterDropdown() {
3538
  $scr = get_current_screen();
3539
  if ( $scr->base !== 'upload' ) return;
@@ -3556,11 +3769,19 @@ Header append Vary Accept env=REDIRECT_webp
3556
  . "</select>");
3557
  }
3558
 
 
 
 
 
3559
  function optimizationPercentIfPng2Jpg($meta) {
3560
  $png2jpgPercent = isset($meta['ShortPixelPng2Jpg']['optimizationPercent']) ? $meta['ShortPixelPng2Jpg']['optimizationPercent'] : 0;
3561
  return number_format(100.0 - (100.0 - $png2jpgPercent) * (100.0 - $meta['ShortPixelImprovement']) / 100.0, 2);
3562
  }
3563
 
 
 
 
 
3564
  function shortpixelInfoBox() {
3565
  if(get_post_type( ) == 'attachment') {
3566
  add_meta_box(
@@ -3574,10 +3795,18 @@ Header append Vary Accept env=REDIRECT_webp
3574
  }
3575
  }
3576
 
 
 
 
3577
  function shortpixelInfoBoxContent( $post ) {
3578
  $this->generateCustomColumn( 'wp-shortPixel', $post->ID, true );
3579
  }
3580
 
 
 
 
 
 
3581
  public function onDeleteImage($post_id) {
3582
  $itemHandler = new ShortPixelMetaFacade($post_id);
3583
  $urlsPaths = $itemHandler->getURLsAndPATHs(true, false, true, array(), true);
@@ -3588,7 +3817,17 @@ Header append Vary Accept env=REDIRECT_webp
3588
  return $itemHandler; //return it because we call it also on replace and on replace we need to follow this by deleting SP metadata, on delete it
3589
  }
3590
 
 
 
3591
  public function deleteBackupsAndWebPs($paths) {
 
 
 
 
 
 
 
 
3592
  $backupFolder = trailingslashit($this->getBackupFolder($paths[0]));
3593
  foreach($paths as $path) {
3594
  $pos = strrpos($path, ".");
@@ -3605,18 +3844,23 @@ Header append Vary Accept env=REDIRECT_webp
3605
  @unlink($backupFolder . preg_replace("/\." . $extension . "$/i", '@2x.' . $extension, $fileName));
3606
  }
3607
  }
3608
-
 
 
 
 
3609
  public function columns( $defaults ) {
3610
  $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
3611
  if(current_user_can( 'manage_options' )) {
3612
  $defaults['wp-shortPixel'] .=
3613
- '&nbsp;<a href="options-general.php?page=wp-shortpixel#stats" title="'
3614
  . __('ShortPixel Statistics','shortpixel-image-optimiser')
3615
  . '"><span class="dashicons dashicons-dashboard"></span></a>';
3616
  }
3617
  return $defaults;
3618
  }
3619
 
 
3620
  public function nggColumns( $defaults ) {
3621
  $this->nggColumnIndex = count($defaults) + 1;
3622
  add_filter( 'ngg_manage_images_column_' . $this->nggColumnIndex . '_header', array( &$this, 'nggColumnHeader' ) );
@@ -3669,11 +3913,12 @@ Header append Vary Accept env=REDIRECT_webp
3669
  }
3670
 
3671
  public function generatePluginLinks($links) {
3672
- $in = '<a href="options-general.php?page=wp-shortpixel">Settings</a>';
3673
  array_unshift($links, $in);
3674
  return $links;
3675
  }
3676
 
 
3677
  static public function formatBytes($bytes, $precision = 2) {
3678
  $units = array('B', 'KB', 'MB', 'GB', 'TB');
3679
 
@@ -3686,11 +3931,21 @@ Header append Vary Accept env=REDIRECT_webp
3686
  return round($bytes, $precision) . ' ' . $units[$pow];
3687
  }
3688
 
 
 
 
 
 
3689
  public function isProcessable($ID, $excludeExtensions = array()) {
3690
  $excludePatterns = $this->_settings->excludePatterns;
3691
  return self::_isProcessable($ID, $excludeExtensions, $excludePatterns);
3692
  }
3693
 
 
 
 
 
 
3694
  public function isProcessablePath($path, $excludeExtensions = array()) {
3695
  $excludePatterns = $this->_settings->excludePatterns;
3696
  return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
@@ -3760,7 +4015,10 @@ Header append Vary Accept env=REDIRECT_webp
3760
  return $itemHandler->getURLsAndPATHs($this->_settings->processThumbnails, $onlyThumbs, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
3761
  }
3762
 
3763
-
 
 
 
3764
  public static function deleteDir($dirPath) {
3765
  if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
3766
  $dirPath .= '/';
@@ -3776,6 +4034,10 @@ Header append Vary Accept env=REDIRECT_webp
3776
  }
3777
  }
3778
 
 
 
 
 
3779
  static public function folderSize($path) {
3780
  $total_size = 0;
3781
  if(file_exists($path)) {
@@ -3808,7 +4070,7 @@ Header append Vary Accept env=REDIRECT_webp
3808
 
3809
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
3810
  //we check that the backup folder exists, if not we create it so we can copy into it
3811
- if(!mkdir(SHORTPIXEL_BACKUP_FOLDER, 0777, true)) return;
3812
  }
3813
 
3814
  $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
@@ -3820,11 +4082,12 @@ Header append Vary Accept env=REDIRECT_webp
3820
  @rmdir($oldBackupFolder);
3821
  }
3822
  }
 
3823
  //now if the backup folder does not contain the uploads level, create it
3824
  if( !is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . SHORTPIXEL_UPLOADS_NAME )
3825
  && !is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR))) {
3826
  @rename(SHORTPIXEL_BACKUP_FOLDER, SHORTPIXEL_BACKUP_FOLDER."_tmp");
3827
- @mkdir(SHORTPIXEL_BACKUP_FOLDER);
3828
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/'.SHORTPIXEL_UPLOADS_NAME);
3829
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
3830
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
@@ -3833,12 +4096,17 @@ Header append Vary Accept env=REDIRECT_webp
3833
  //then create the wp-content level if not present
3834
  if(!is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR))) {
3835
  @rename(SHORTPIXEL_BACKUP_FOLDER, SHORTPIXEL_BACKUP_FOLDER."_tmp");
3836
- @mkdir(SHORTPIXEL_BACKUP_FOLDER);
3837
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/' . basename(WP_CONTENT_DIR));
3838
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
3839
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
3840
  }
3841
  }
 
 
 
 
 
3842
  return;
3843
  }
3844
 
@@ -3860,6 +4128,8 @@ Header append Vary Accept env=REDIRECT_webp
3860
  return $sizes;
3861
  }
3862
 
 
 
3863
  function getMaxIntermediateImageSize() {
3864
  global $_wp_additional_image_sizes;
3865
 
@@ -3886,7 +4156,76 @@ Header append Vary Accept env=REDIRECT_webp
3886
  return array_values(array_diff(array(0, 1, 2), array(0 + $compressionType)));
3887
  }
3888
 
3889
- function outputHSBeacon() { ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3890
  <script>
3891
  <?php
3892
  $screen = get_current_screen();
@@ -4001,6 +4340,7 @@ Header append Vary Accept env=REDIRECT_webp
4001
  return $this->_settings->resizeHeight;
4002
  }
4003
  public static function getAffiliateSufix() {
 
4004
  // not allowed anymore by WP as of Sept.27 2018
4005
  // return isset($_COOKIE["AffiliateShortPixel"])
4006
  // ? "/affiliate/" . $_COOKIE["AffiliateShortPixel"]
1
  <?php
2
 
3
+ use ShortPixel\DebugItem as DebugItem;
4
+ use ShortPixel\ShortPixelLogger as Log;
5
+ use ShortPixel\NoticeController as Notice;
6
+
7
+
8
  class WPShortPixel {
9
 
10
  const BULK_EMPTY_QUEUE = 0;
46
  $controllerClass = ShortPixelTools::namespaceit('ShortPixelController');
47
  $controllerClass::init(); // load all subclassed controllers.
48
 
49
+ /*$debugClass = ShortPixelTools::namespaceit('Debug');
50
+ $debugClass->init(); */
51
+
52
  define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
53
 
54
  if( !defined('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES')) {
102
 
103
  if($isAdminUser) {
104
  //add settings page
105
+ //add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );//display SP in Settings menu
106
  add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
107
 
108
  add_action('wp_ajax_shortpixel_browse_content', array(&$this, 'browseContent'));
148
  add_action('wp_ajax_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
149
  add_action('admin_action_shortpixel_check_quota', array(&$this, 'handleCheckQuota'));
150
  //This adds the constants used in PHP to be available also in JS
151
+ add_action( 'admin_enqueue_scripts', array( $this, 'shortPixelJS') );
152
+ add_action( 'admin_footer', array($this, 'admin_footer_js') );
153
  add_action( 'admin_head', array( $this, 'headCSS') );
154
 
155
  if($this->_settings->frontBootstrap && shortPixelCheckQueue()) {
165
  $this->migrateBackupFolder();
166
 
167
  // [BS] Quite dangerous to do this in any constructor. Can hit if request is ajax to name something
168
+ // @todo This is intended to run only once, on activation. ( it does )
169
  if(!$this->_settings->redirectedSettings && !$this->_settings->verifiedKey && (!function_exists("is_multisite") || !is_multisite())) {
170
  $this->_settings->redirectedSettings = 1;
171
+ wp_redirect(admin_url("options-general.php?page=wp-shortpixel-settings"));
172
  exit();
173
  }
174
 
179
  $this->__construct();
180
  }
181
 
182
+ // @hook admin menu
183
+ // @todo move to plugin class
184
  public function registerSettingsPage() {
185
+
186
  }
187
 
188
+ // @hook admin menu
189
+ // @todo move to plugin class
190
  function registerAdminPage( ) {
191
  if($this->spMetaDao->hasFoldersTable() && count($this->spMetaDao->getFolders())) {
192
  /*translators: title and menu name for the Other media page*/
345
  return $found;
346
  }
347
 
348
+ /** Displays notices to admin, if there are any
349
+ * TODO - Probably should be a controller
350
+ */
351
  public function displayAdminNotices() {
352
  if(!ShortPixelQueue::testQ()) {
353
  ShortPixelView::displayActivationNotice('fileperms');
459
  }
460
 
461
  static function log($message, $force = false) {
462
+ Log::addInfo($message);
463
+ /*
464
  if (SHORTPIXEL_DEBUG === true || $force) {
465
  if (is_array($message) || is_object($message)) {
466
  self::doLog(print_r($message, true), $force);
467
  } else {
468
  self::doLog($message, $force);
469
  }
470
+ } */
471
  }
472
 
473
+ /** [TODO] This should report to the Shortpixel Logger **/
474
  static protected function doLog($message, $force = false) {
475
+
476
+ /*if(defined('SHORTPIXEL_DEBUG_TARGET') || $force) {
477
  file_put_contents(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log", '[' . date('Y-m-d H:i:s') . "] $message\n", FILE_APPEND);
478
  } else {
479
  error_log($message);
480
+ }*/
481
  }
482
 
483
  function headCSS() {
484
  echo('<style>.shortpixel-hide {display:none;}</style>');
485
  }
486
 
487
+ /** @todo Plugin init class. Try to get rid of inline JS. Also still loads on all WP pages, prevent that. */
488
  function shortPixelJS() {
489
+
490
  //require_once(ABSPATH . 'wp-admin/includes/screen.php');
491
  if(function_exists('get_current_screen')) {
492
  $screen = get_current_screen();
493
 
494
  if(is_object($screen)) {
495
+
 
 
 
 
 
 
496
 
497
  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);
498
  if( in_array($screen->id, array('attachment', 'upload', 'settings_page_wp-shortpixel', 'media_page_wp-short-pixel-bulk', 'media_page_wp-short-pixel-custom'))) {
500
  //modal - used in settings for selecting folder
501
  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);
502
 
503
+ // @todo Might need to be removed later on
504
  wp_register_style('shortpixel-admin', plugins_url('/res/css/shortpixel-admin.css', SHORTPIXEL_PLUGIN_FILE),array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION );
505
  wp_enqueue_style('shortpixel-admin');
506
  }
507
  }
508
  }
 
 
 
 
 
 
 
 
 
 
 
 
509
 
510
+
511
+ wp_register_script('shortpixel' . $this->jsSuffix, plugins_url('/res/js/shortpixel' . $this->jsSuffix,SHORTPIXEL_PLUGIN_FILE), array('jquery'), SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
512
+
513
 
514
  // Using an Array within another Array to protect the primitive values from being cast to strings
515
  $ShortPixelConstants = array(array(
526
  'WP_PLUGIN_URL'=>plugins_url( '', SHORTPIXEL_PLUGIN_FILE ),
527
  'WP_ADMIN_URL'=>admin_url(),
528
  'API_KEY'=> (defined("SHORTPIXEL_HIDE_API_KEY" ) || !is_admin() ) ? '' : $this->_settings->apiKey,
529
+ 'DEFAULT_COMPRESSION'=>0 + intval($this->_settings->compressionType), // no int can happen when settings are empty still
530
  'MEDIA_ALERT'=>$this->_settings->mediaAlert ? "done" : "todo",
531
  'FRONT_BOOTSTRAP'=>$this->_settings->frontBootstrap && (!isset($this->_settings->lastBackAction) || (time() - $this->_settings->lastBackAction > 600)) ? 1 : 0,
532
  'AJAX_URL'=>admin_url('admin-ajax.php'),
533
  'AFFILIATE'=>self::getAffiliateSufix()
534
  ));
535
 
536
+ if (Log::isManualDebug() )
537
+ {
538
+ Log::addInfo('Ajax Manual Debug Mode');
539
+ $logLevel = Log::getLogLevel();
540
+ $ShortPixelConstants[0]['AJAX_URL'] = admin_url('admin-ajax.php?SHORTPIXEL_DEBUG=' . $logLevel);
541
+ }
542
+
543
  $jsTranslation = array(
544
  'optimizeWithSP' => __( 'Optimize with ShortPixel', 'shortpixel-image-optimiser' ),
545
  'redoLossy' => __( 'Re-optimize Lossy', 'shortpixel-image-optimiser' ),
579
  wp_enqueue_script('punycode.min.js', plugins_url('/res/js/punycode.min.js',SHORTPIXEL_PLUGIN_FILE) );
580
  }
581
 
582
+ /** Outputs direct JS to the admin footer
583
+ * @todo Find a better solution for this */
584
+ public function admin_footer_js()
585
+ {
586
+ if(function_exists('get_current_screen')) {
587
+ $screen = get_current_screen();
588
+ if(is_object($screen)) {
589
+
590
+ if( in_array($screen->id, array('attachment', 'upload', 'media_page_wp-short-pixel-custom'))) {
591
+ //output the comparer html
592
+ $this->view->outputComparerHTML();
593
+ //render a template of the list cell to be used by the JS
594
+ $this->view->renderListCell("__SP_ID__", 'imgOptimized', true, "__SP_THUMBS_TOTAL__", true, true,
595
+ array("__SP_FIRST_TYPE__", "__SP_SECOND_TYPE__"), "__SP_CELL_MESSAGE__", 'sp-column-actions-template');
596
+ }
597
+ }
598
+ }
599
+ ?>
600
+ <script type="text/javascript" >
601
+ //check after 10 seconds if ShortPixel initialized OK, if not, force the init (could happen if a JS error somewhere else stopped the JS execution).
602
+ function delayedInit() {
603
+ if(typeof ShortPixel !== "undefined") {
604
+ ShortPixel.init();
605
+ } else {
606
+ setTimeout(delayedInit, 10000);
607
+ }
608
+ }
609
+ setTimeout(delayedInit, 10000);
610
+ </script>
611
+ <?php
612
+ }
613
+
614
+ /** Displays an icon in the toolbar when processing images
615
+ * hook - admin_bar_menu
616
+ * @param Obj $wp_admin_bar
617
+ */
618
  function toolbar_shortpixel_processing( $wp_admin_bar ) {
619
 
620
  $extraClasses = " shortpixel-hide";
634
  $tooltip = '';
635
  $exceedTooltip = __('ShortPixel quota exceeded. Click for details.','shortpixel-image-optimiser');
636
  //$link = "http://shortpixel.com/login/" . $this->_settings->apiKey;
637
+ $link = "options-general.php?page=wp-shortpixel-settings";
638
  //$blank = '_blank';
639
  //$icon = "shortpixel-alert.png";
640
  }
689
  // 2. Perform the action
690
  case 'short-pixel-bulk':
691
  foreach( $mediaIds as $ID ) {
692
+
693
  $meta = wp_get_attachment_metadata($ID);
694
+ if(!is_array($meta)) {
695
+ self::log('CUSTOM BULK META NOT AN ARRAY: ' . json_encode($meta));
696
+ $meta = ShortPixelMetaFacade::sanitizeMeta($meta, false);
697
+ if(isset($meta['previous_meta'])) {
698
+ self::log('COULDN\'T SANITIZE PROPERLY.');
699
+ continue;
700
+ }
701
+ else {
702
+ self::log('SANITIZED.');
703
+ }
704
+ }
705
+
706
+ if(!is_array($meta)) continue;
707
  if( ( !isset($meta['ShortPixel']) //never touched by ShortPixel
708
  || (isset($meta['ShortPixel']['WaitingProcessing']) && $meta['ShortPixel']['WaitingProcessing'] == true))
709
  && (!isset($meta['ShortPixelImprovement']) || $meta['ShortPixelImprovement'] == __('Optimization N/A','shortpixel-image-optimiser'))) {
844
  $meta = $itemHandler->getMeta();
845
 
846
  $doDump = false;
847
+
848
  if ($meta->getStatus() <= 0)
849
  {
850
  $doDump = true; // dump any caching on files that ended in an error.
1085
  }
1086
  $idList = array();
1087
  $itemList = array();
1088
+ $timeoutMinThreshold = SHORTPIXEL_MAX_EXECUTION_TIME < 10 ? 2 : (SHORTPIXEL_MAX_EXECUTION_TIME < 30 ? 3 : 5);
1089
+ $maxTime = min(SHORTPIXEL_MAX_EXECUTION_TIME, 90);
1090
+ $timeoutThreshold = 5; // will adapt this with the maximum time needed for one pass
1091
+ $passTime = time();
1092
  for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
1093
  ($crtStartQueryID >= $endQueryID) && (count($itemList) < SHORTPIXEL_PRESEND_ITEMS) && ($sanityCheck < 150)
1094
+ && (time() - $this->timer < $maxTime - $timeoutThreshold); $sanityCheck++) {
1095
 
1096
+ $timeoutThreshold = max($timeoutThreshold, $timeoutMinThreshold + time() - $passTime);
1097
+ $passTime = time();
1098
+ $maxResults = $timeoutThreshold > 15 ? SHORTPIXEL_MAX_RESULTS_QUERY / 3 :
1099
+ ($timeoutThreshold > 10 ? SHORTPIXEL_MAX_RESULTS_QUERY / 2 : SHORTPIXEL_MAX_RESULTS_QUERY);
1100
+ self::log("GETDB: pass $sanityCheck current StartID: $crtStartQueryID Threshold: $timeoutThreshold, MaxResults: $maxResults" );
1101
 
1102
  /* $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta
1103
  WHERE ( post_id <= $crtStartQueryID AND post_id >= $endQueryID )
1106
  LIMIT " . SHORTPIXEL_MAX_RESULTS_QUERY;
1107
  $resultsPostMeta = $wpdb->get_results($queryPostMeta);
1108
  */
1109
+
1110
+ $resultsPostMeta = WpShortPixelMediaLbraryAdapter::getPostMetaSlice($crtStartQueryID, $endQueryID, $maxResults);
1111
+ if(time() - $this->timer >= 60) self::log("GETDB is SLOW. Got meta slice.");
1112
 
1113
  if ( empty($resultsPostMeta) ) {
1114
  $crtStartQueryID -= SHORTPIXEL_MAX_RESULTS_QUERY;
1120
  continue;
1121
  }
1122
 
1123
+ if($timeoutThreshold > 10) self::log("GETDB is SLOW. Meta slice has " . count($resultsPostMeta) . ' items.');
1124
+
1125
  foreach ( $resultsPostMeta as $itemMetaData ) {
1126
  $crtStartQueryID = $itemMetaData->post_id;
1127
+ if(time() - $this->timer >= 60) self::log("GETDB is SO SLOW. Check processable for $crtStartQueryID.");
1128
+ if(time() - $this->timer >= $maxTime - $timeoutThreshold){
1129
+ break;
1130
+ }
1131
+
1132
  if(!in_array($crtStartQueryID, $idList) && $this->isProcessable($crtStartQueryID, ($this->_settings->optimizePdfs ? array() : array('pdf')))) {
1133
  $item = new ShortPixelMetaFacade($crtStartQueryID);
1134
+
1135
+ if($timeoutThreshold > 15) self::log("GETDB is SO SLOW. Get meta for $crtStartQueryID.");
1136
  $meta = $item->getMeta();//wp_get_attachment_metadata($crtStartQueryID);
1137
+ if($timeoutThreshold > 15) self::log("GETDB is SO SLOW. Got meta.");
1138
 
1139
  if($meta->getStatus() != 2) {
1140
  $addIt = (strpos($meta->getMessage(), __('Image files are missing.', 'shortpixel-image-optimiser')) === false);
1164
  }
1165
  elseif( $this->_settings->processThumbnails && $meta->getThumbsOpt() !== null
1166
  && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0
1167
+ || is_array($meta->getThumbsOptList())
1168
+ && count(array_diff(array_keys(WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes)),
1169
+ $meta->getThumbsOptList())))) { //thumbs were chosen in settings
1170
  $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
1171
  if(count($URLsAndPATHs["URLs"])) {
1172
  $meta->setThumbsTodo(true);
1191
  $this->prioQ->setStartBulkId($startQueryID);
1192
  } else {
1193
  $crtStartQueryID--;
1194
+ self::log("GETDB just decrementing. Crt: $crtStartQueryID Start: $startQueryID, list: " . json_encode($idList));
1195
  }
1196
  }
1197
+ $ret = array("items" => $itemList, "skipped" => $skippedAlreadyProcessed, "searching" => ($sanityCheck >= 150) || (time() - $this->timer >= $maxTime - $timeoutThreshold));
1198
+ self::log('GETDB returns ' . json_encode($ret));
1199
+ return $ret;
1200
  }
1201
 
1202
  /**
1213
  return $items;
1214
  }
1215
 
1216
+ /** Checks the API key **/
1217
  private function checkKey($ID) {
1218
  if( $this->_settings->verifiedKey == false) {
1219
  if($ID == null){
1259
 
1260
  $rawPrioQ = $this->prioQ->get();
1261
  if(count($rawPrioQ)) { self::log("HIP: 0 Priority Queue: ".json_encode($rawPrioQ)); }
1262
+ self::log("HIP: 0 Bulk running? " . $this->prioQ->bulkRunning() . " START " . $this->_settings->startBulkId . " STOP " . $this->_settings->stopBulkId . " MaxTime: " . SHORTPIXEL_MAX_EXECUTION_TIME);
1263
 
1264
  //handle the bulk restore and cleanup first - these are fast operations taking precedece over optimization
1265
  if( $this->prioQ->bulkRunning()
1548
  if($result["Status"] !== ShortPixelAPI::STATUS_RETRY) {
1549
  $this->_settings->bulkLastStatus = $result;
1550
  }
1551
+ $ret = json_encode($result);
1552
+ self::log("HIP RET " . $ret);
1553
+ die($ret);
1554
  }
1555
 
1556
 
1659
  return $URLsAndPATHs;
1660
  }
1661
 
1662
+ /** Manual optimization request. This is only called from the Media Library, never from the Custom media */
1663
  public function handleManualOptimization() {
1664
+ $imageId = intval($_GET['image_id']);
1665
  $cleanup = $_GET['cleanup'];
1666
 
1667
+ Log::addInfo("Handle Manual Optimization #{$imageId}");
1668
 
1669
  switch(substr($imageId, 0, 2)) {
1670
  case "N-":
1681
  return array("Status" => ShortPixelAPI::STATUS_FAIL, "message" => __('NextGen image not found','shortpixel-image-optimiser'));
1682
  break;
1683
  case "C-":
1684
+ Log::addError("Throw: HandleManualOptimization for custom images not implemented");
1685
  throw new Exception("HandleManualOptimization for custom images not implemented");
1686
  default:
1687
  $this->optimizeNowHook(intval($imageId), true);
1691
 
1692
  }
1693
 
1694
+ /** Returns status of an image *
1695
+ * Uses a request parameter for image_id, ends in json encode.
1696
+ * @hook - wp_ajax_shortpixel_check_status
1697
+ */
1698
  public function checkStatus() {
1699
+ $itemHandler = new ShortPixelMetaFacade(intval($_GET['image_id']));
1700
  $meta = $itemHandler->getMeta();
1701
  die(json_encode(array("Status" => $meta->getStatus(), "Message" => $meta->getMessage())));
1702
  }
1703
 
1704
+ /** Hook action for optimization (Ajax-call)
1705
+ * @hook shortpixel-optimize-now ( seems not in use )
1706
+ * Call by handleManualOptimization
1707
+ */
1708
  public function optimizeNowHook($imageId, $manual = false) {
1709
  //WpShortPixel::log("OPTIMIZE NOW HOOK for ID: $imageId STACK: " . json_encode(debug_backtrace()));
1710
  if($this->isProcessable($imageId)) {
1747
  * @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.
1748
  *
1749
  *
1750
+ * Note - $regeneratedSizes expects part of the metadata array called [sizes], with filename, not just the resized data.
1751
  */
1752
  public function thumbnailsRegeneratedHook($postId, $originalMeta, $regeneratedSizes = array(), $bulk = false) {
1753
 
1786
  unset($this->thumbnailsRegenerating[$postId]);
1787
  }
1788
 
1789
+ /** Check if a certain files exists in the backup
1790
+ * ( by calling a random number of dir functions )
1791
+ * TODO - Should be of the folder model.
1792
+ */
1793
  public function shortpixelGetBackupFilter($imagePath) {
1794
  $backup = str_replace(dirname(dirname(dirname(SHORTPIXEL_BACKUP_FOLDER))),SHORTPIXEL_BACKUP_FOLDER, $imagePath);
1795
  return file_exists($backup) ? $backup : false;
1810
  }
1811
 
1812
 
1813
+ /** on Image error, Save error in file's meta data
1814
+ * @param int $ID image_id
1815
+ * @param string $result - Error String
1816
+ */
1817
  public function handleError($ID, $result)
1818
  {
1819
  $meta = wp_get_attachment_metadata($ID);
1822
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
1823
  }
1824
 
1825
+ /* Gets backup folder of file
1826
+ * @param string $file Filepath - probably
1827
+ * @return string backupFolder
1828
+ */
1829
  public function getBackupFolder($file) {
1830
  if(realpath($file)) {
1831
  $ret = $this->getBackupFolderInternal(realpath($file)); //found cases when $file contains for example /wp/../wp-content - clean it up
1835
  return $this->getBackupFolderInternal($file);
1836
  }
1837
 
1838
+ /** Gets backup from file
1839
+ * @param string $file Filename
1840
+ * @return string FolderName
1841
+ */
1842
  private function getBackupFolderInternal($file) {
1843
  $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
1844
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
1862
  return SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir;
1863
  }
1864
 
1865
+ /** Gets BackupFolder. If that doesn't work, search thumbs for a backupFolder
1866
+ * @param string $file FileName
1867
+ * @param array $thumbs Array of thumbnails
1868
+ * @return string|boolean Returns either backupfolder or false.
1869
+ */
1870
  public function getBackupFolderAny($file, $thumbs) {
1871
  $ret = $this->getBackupFolder($file);
1872
  //if(!$ret && !file_exists($file) && isset($thumbs)) {
1874
  //try with the thumbnails
1875
  foreach($thumbs as $size) {
1876
  $backup = $this->getBackupFolder(trailingslashit(dirname($file)) . $size['file']);
1877
+ if($backup) {
1878
+ $ret = $backup;
1879
+ break;
1880
+ }
1881
  }
1882
  }
1883
+ return apply_filters("shortpixel_backup_folder", $ret, $file, $thumbs);
1884
  }
1885
 
1886
+ /** Sets file permissions
1887
+ * @param string $file FileName
1888
+ * @return boolean Success
1889
+ * TODO - Move to File Model
1890
+ */
1891
  protected function setFilePerms($file) {
1892
  //die(getenv('USERNAME') ? getenv('USERNAME') : getenv('USER'));
1893
 
2086
  set_transient("shortpixel_thrown_notice", array('when' => $when, 'extra' => $extra), 120);
2087
  }
2088
 
2089
+ /** Checks if a notice was thrown
2090
+ * @return boolean true, if there are notices */
2091
  protected function catchNotice() {
2092
  $notice = get_transient("shortpixel_thrown_notice");
2093
  if(isset($notice['when'])) {
2113
  return substr($file, 0, strlen($file) - 1 - strlen($ext)) . "@2x." . $ext;
2114
  }
2115
 
2116
+ /** Restores a non-media-library image
2117
+ * @param int $ID image_id, without any prefixes
2118
+ */
2119
  public function doCustomRestore($ID) {
2120
  //$meta = $this->spMetaDao->getMeta($ID);
2121
  // meta facade as a custom image
2141
  $rename_result = @rename($bkFile, $file);
2142
  if (! $rename_result)
2143
  {
2144
+ Log::addError('Failure on rename to : ' . $file);
2145
  }
2146
 
2147
 
2166
 
2167
  }
2168
  else {
2169
+ Log::addWarn('File ' . $bkFile . ' not found in backups');
2170
  }
2171
 
2172
  return $meta;
2234
  return $ret;
2235
  }
2236
 
2237
+ // TODO - [BS] json_encode should be replaced by a call to shortPixelTools:sendJson, but this crashes the JS parse - for some reason -
2238
  public function handleOptimizeThumbs() {
2239
  $ID = intval($_GET['attachment_ID']);
2240
  $meta = wp_get_attachment_metadata($ID);
 
2241
 
2242
+ // default return;
2243
+ //$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);
2244
  $error = array('Status' => ShortPixelAPI::STATUS_SKIP, 'message' => __('Unspecified Error on Thumbnails for: ') . $ID);
2245
+
2246
  $includedSizes = WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
2247
  $thumbsCount = count($includedSizes);
2248
 
2251
  $error['message'] = __('Please optimize image for ID: ','shortpixel-image-optimiser') . $ID;
2252
  die(json_encode($error));
2253
  }
2254
+
2255
  if (! isset($meta['sizes']) || count($meta['sizes']) == 0)
2256
  {
2257
  $error['message'] = __('No thumbnails to optimize for ID: ','shortpixel-image-optimiser') . $ID;
2258
  die(json_encode($error));
2259
  }
2260
+
2261
+
2262
  /* Check ThumbList against current Sizes. It's possible when a size was dropped, the SP meta was not updated, playing
2263
  * tricks with the thumbcount.
2264
  *
2276
  $meta['ShortPixel']['thumbsOptList'] = $thumbList;
2277
  }
2278
 
2279
+ /*
2280
+ if (isset($meta['Shortpixel']['thumbsOptList']))
2281
+ {
2282
+ $sizeFiles = array();
2283
+ foreach($sizeFiles as $size => $data)
2284
+ {
2285
+ $file = pathinfo($data['file'], )
2286
+ }
2287
 
2288
+ } */
2289
+
2290
+ /* if( $thumbsCount
2291
+ && ( !isset($meta['ShortPixel']['thumbsOpt']) || $meta['ShortPixel']['thumbsOpt'] == 0
2292
+ || (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && $meta['ShortPixel']['thumbsOpt'] < $thumbsCount))) { //optimized without thumbs, thumbs exist */
2293
 
2294
  if( $thumbsCount
2295
  && (isset($meta['sizes']) && isset($meta['ShortPixel']['thumbsOptList']) && count($meta['ShortPixel']['thumbsOptList']) < $thumbsCount))
2296
  {
2297
  $meta['ShortPixel']['thumbsTodo'] = true;
2298
+
2299
  //wp_update_attachment_metadata($ID, $meta);
2300
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
2301
  $this->prioQ->push($ID);
2315
  } else {
2316
  $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);
2317
  }
2318
+ //shortPixelTools::sendJSON($ret);
2319
  die(json_encode($ret));
2320
  }
2321
 
2363
  }
2364
  }
2365
 
2366
+ /** Runs on plugin deactivation
2367
+ * @hook admin_post_shortpixel_deactivate_plugin
2368
+ */
2369
  public function deactivatePlugin() {
2370
  if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'sp_deactivate_plugin_nonce' ) ) {
2371
  wp_nonce_ays( '' );
2384
 
2385
  }
2386
 
2387
+ /** Deactivates plugin and redirects
2388
+ * @param string @url URL to redirect after deactivate
2389
+ */
2390
  protected function deactivateAndRedirect($url){
2391
  //die(ShortPixelVDD($url));
2392
+ deactivate_plugins( sanitize_text_field($_GET['plugin']) );
2393
  wp_safe_redirect( $url );
2394
  die();
2395
 
2433
  $quotaData = $this->getQuotaInformation();
2434
  }
2435
  if ( !$quotaData['APIKeyValid']) {
2436
+ if(strlen($this->_settings->apiKey))
2437
+ Notice::addError(sprintf(__('Shortpixel Remote API Error: %s','shortpixel-image-optimizer'), $quotaData['Message'] ));
2438
  return $quotaData;
2439
  }
2440
  //$tempus = microtime(true);
2444
  if($quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] > $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']) {
2445
  $this->_settings->quotaExceeded = '0';
2446
  $this->_settings->prioritySkip = NULL;
2447
+ Log::addInfo("CHECK QUOTA: Skipped: ".json_encode($this->prioQ->getSkipped()));
2448
 
2449
  ?><script>var shortPixelQuotaExceeded = 0;</script><?php
2450
  }
2455
  return $quotaData;
2456
  }
2457
 
2458
+ /** Checks if ID has a meta component
2459
+ * @param int $id $imageId
2460
+ * @return string|array|null Returns array custom metadata ( or null ) or URL to attachment.
2461
+ */
2462
  public function isValidMetaId($id) {
2463
  return substr($id, 0, 2 ) == "C-" ? $this->spMetaDao->getMeta(substr($id, 2)) : wp_get_attachment_url($id);
2464
  }
2465
 
2466
+ /** View for Custom media
2467
+ * @todo Move this
2468
+ */
2469
  public function listCustomMedia() {
2470
  if( ! class_exists( 'ShortPixelListTable' ) ) {
2471
  require_once('view/shortpixel-list-table.php');
2484
  if ( isset($_GET['noheader']) ) {
2485
  require_once(ABSPATH . 'wp-admin/admin-header.php');
2486
  }
2487
+ //$this->outputHSBeacon();
2488
+ \ShortPixel\HelpScout::outputBeacon($this->getApiKey());
2489
  ?>
2490
  <div class="wrap shortpixel-other-media">
2491
  <h2>
 
 
 
 
 
2492
  <?php _e('Other Media optimized by ShortPixel','shortpixel-image-optimiser');?>
2493
  </h2>
2494
 
2495
+ <div id="legacy">
2496
+ <div id="legacy" class="metabox-holder">
2497
+ <div id="legacy">
2498
+ <div style="float:left;">
2499
+ <a href="upload.php?page=wp-short-pixel-custom&refresh=1" id="refresh" class="button button-primary" title="<?php _e('Refresh custom folders content','shortpixel-image-optimiser');?>">
2500
+ <?php _e('Refresh folders','shortpixel-image-optimiser');?>
2501
+ </a>
2502
+ </div>
2503
  <div class="meta-box-sortables ui-sortable">
2504
  <form method="get">
2505
  <input type="hidden" name="page" value="wp-short-pixel-custom" />
2526
  }
2527
 
2528
  /** Front End function that controls bulk processes.
2529
+ * TODO This is a Bulk controller
2530
  */
2531
  public function bulkProcess() {
2532
  global $wpdb;
2581
 
2582
  if(isset($_POST["bulkRestore"]))
2583
  {
2584
+ $bulkRestore = new \ShortPixel\BulkRestoreAll(); // controller
2585
  $bulkRestore->setShortPixel($this);
2586
  $bulkRestore->setupBulk();
2587
 
2654
  {
2655
  $viewObj = new $partControl();
2656
  $viewObj->setShortPixel($this);
2657
+ $viewObj->loadView(); // TODO [BS] This should call load, which should init and call view inside controller.
2658
  }
2659
 
2660
  if (! $template_part)
2675
  }
2676
  }
2677
 
2678
+ // TODO - Calculate time left Utility function -Called in bulkProcess.
2679
  public function bulkProgressMessage($percent, $minutes) {
2680
  $timeEst = "";
2681
  self::log("bulkProgressMessage(): percent: " . $percent);
2697
  return $timeEst;
2698
  }
2699
 
2700
+ // TODO - Folder Model action
2701
  public function emptyBackup(){
2702
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2703
 
2717
  }
2718
  }
2719
 
2720
+
2721
  public function backupFolderIsEmpty() {
2722
  if(file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
2723
  return count(scandir(SHORTPIXEL_BACKUP_FOLDER)) > 2 ? false : true;
2778
  die();
2779
  }
2780
 
2781
+ /** Gets data for image comparison. Returns JSON
2782
+ *
2783
+ * @return json JSON data.
2784
+ * TODO - Should return via JSON function in tools
2785
+ */
2786
  public function getComparerData() {
2787
  if (!isset($_POST['id']) || !current_user_can( 'upload_files' ) && !current_user_can( 'edit_posts' ) ) {
2788
  wp_die(json_encode((object)array('origUrl' => false, 'optUrl' => false, 'width' => 0, 'height' => 0)));
2789
  }
2790
 
2791
  $ret = array();
2792
+ // This shall not be Intval, since Post_id can be custom (C-xx)
2793
+ $handle = new ShortPixelMetaFacade( sanitize_text_field($_POST['id']) );
2794
 
2795
  $meta = $handle->getMeta();
2796
  $rawMeta = $handle->getRawMeta();
2830
  die(json_encode((object)$ret));
2831
  }
2832
 
2833
+ // TODO This could be something in an install class.
2834
  public function newApiKey() {
2835
  if ( !current_user_can( 'manage_options' ) ) {
2836
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2854
  )
2855
  );
2856
 
2857
+ $newKeyResponse = wp_remote_post("https://shortpixel.com/free-sign-up-plugin", $params);
2858
 
2859
  if ( is_object($newKeyResponse) && get_class($newKeyResponse) == 'WP_Error' ) {
2860
  die(json_encode((object)array('Status' => 'fail', 'Details' => '503')));
2922
 
2923
  }
2924
 
2925
+ // TODO - Part of the folder model.
2926
  public static function getCustomFolderBase() {
2927
  if(is_main_site()) {
2928
  $base = get_home_path();
2933
  }
2934
  }
2935
 
2936
+ // TODO - Should be part of folder model
2937
  protected function fullRefreshCustomFolder($path, &$notice) {
2938
  $folder = $this->spMetaDao->getFolder($path);
2939
  $diff = $folder->checkFolderContents(array('ShortPixelCustomMetaDao', 'getPathFiles'));
2940
  }
2941
 
2942
+ // @todo - Should be part of folder model
2943
+ public function refreshCustomFolders(&$notice, $ignore = false) {
2944
  $customFolders = array();
2945
  if($this->_settings->hasCustomFolders) {
2946
  $customFolders = $this->spMetaDao->getFolders();
2969
  return $customFolders;
2970
  }
2971
 
2972
+ /** Updates HTAccess files for Webp
2973
+ * @param boolean $clear Clear removes all statements from htaccess. For disabling webp.
2974
+ */
2975
+ public static function alterHtaccess( $clear = false ){
2976
  // [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
2977
  $upload_dir = wp_upload_dir();
2978
  $upload_base = trailingslashit($upload_dir['basedir']);
3024
  ' ;
3025
 
3026
  insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', $rules);
3027
+
3028
+ /** In uploads and on, it needs Inherit. Otherwise things such as the 404 error page will not be loaded properly
3029
+ * since the WP rewrite will not be active at that point (overruled) **/
3030
+ $rules = str_replace('RewriteEngine On', 'RewriteEngine On' . PHP_EOL . 'RewriteOptions Inherit', $rules);
3031
+
3032
  insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', $rules);
3033
  insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', $rules);
3034
  /* insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '
3044
  }
3045
  }
3046
 
3047
+ /** Settings menu controller
3048
+ * @todo Make it a controller ||commented out means replaced
3049
+ */
3050
  public function renderSettingsMenu() {
3051
  if ( !current_user_can( 'manage_options' ) ) {
3052
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
3053
  }
3054
 
3055
+ // wp_enqueue_style('sp-file-tree.min.css', plugins_url('/res/css/sp-file-tree.min.css',SHORTPIXEL_PLUGIN_FILE) );
3056
+ // wp_enqueue_script('sp-file-tree.min.js', plugins_url('/res/js/sp-file-tree.min.js',SHORTPIXEL_PLUGIN_FILE) );
3057
 
3058
  //die(var_dump($_POST));
3059
  $noticeHTML = "";
3061
  $folderMsg = false;
3062
  $addedFolder = false;
3063
 
3064
+ //$this->_settings->redirectedSettings = 2;
3065
 
3066
  // Check if NGINX Server
3067
+ //$isNginx = strpos($_SERVER["SERVER_SOFTWARE"], 'nginx') !== false ? true : false;
3068
 
3069
  // BEGIN: Verify .htaccess writeability
3070
+ /*$htaccessWriteable = true;
3071
  if( !$isNginx ) {
3072
  $htaccessPath = get_home_path() . '.htaccess';
3073
  $htaccessExisted = file_exists( $htaccessPath );
3076
  if( !$htaccessExisted ){
3077
  unlink( $htaccessPath );
3078
  }
3079
+ } */
3080
  // END: Verify .htaccess writeability
3081
 
3082
 
3083
  //by default we try to fetch the API Key from wp-config.php (if defined)
3084
+ /*if ( defined("SHORTPIXEL_API_KEY") && strlen(SHORTPIXEL_API_KEY) == 20)
3085
  {
3086
  if(!isset($_POST['save']) && (strlen($this->getApiKey()) == 0 || SHORTPIXEL_API_KEY != $this->getApiKey())) {
3087
  $_POST['validate'] = "validate";
3088
  }
3089
  $_POST['key'] = SHORTPIXEL_API_KEY;
3090
+ } */
3091
 
3092
  if(isset($_GET['setsparchive'])) {
3093
  $this->_settings->downloadArchive = intval($_GET['setsparchive']);
3094
  }
3095
 
3096
  //check all custom folders and update meta table if files appeared
3097
+ // This var is overwritten in the Save procedure. Notice is possibly overwritten below.
3098
+ //$customFolders = $this->refreshCustomFolders($notice, isset($_POST['removeFolder']) ? $_POST['removeFolder'] : null);
3099
 
3100
  if(isset($_POST['request']) && $_POST['request'] == 'request') {
3101
  //a new API Key was requested
3113
  }
3114
  }
3115
 
3116
+ /* if ( isset( $_POST["saveCloudflare"] ) ) {
3117
  $cfApi = $this->_settings->cloudflareEmail = sanitize_text_field( $_POST['cloudflare-email'] );
3118
  $cfAuth = $this->_settings->cloudflareAuthKey = sanitize_text_field( $_POST['cloudflare-auth-key'] );
3119
+ $cfZone = $this->_settings->cloudflareZoneID = sanitize_text_field( $_POST['cloudflare-zone-id'] ); */
3120
  $this->cloudflareApi->set_up($cfApi, $cfAuth, $cfZone);
3121
+ //}
3122
 
3123
  if( isset($_POST['save']) || isset($_POST['saveAdv'])
3124
  || (isset($_POST['validate']) && $_POST['validate'] == "validate")
3125
  || isset($_POST['removeFolder']) || isset($_POST['recheckFolder'])) {
3126
 
3127
  //handle API Key - common for save and validate.
3128
+ //$_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.
3129
 
3130
+ /* if ( strlen($_POST['key']) <> 20 ){
3131
  $KeyLength = strlen($_POST['key']);
3132
 
3133
  $notice = array("status" => "error",
3149
  }
3150
 
3151
  $validityData = $this->getQuotaInformation($_POST['key'], true, isset($_POST['validate']) && $_POST['validate'] == "validate", $_POST);
3152
+ */
3153
+ /* $this->_settings->apiKey = $_POST['key'];
3154
  if($validityData['APIKeyValid']) {
3155
  if(isset($_POST['validate']) && $_POST['validate'] == "validate") {
3156
  // delete last status if it was no valid key
3172
  }
3173
  $this->_settings->verifiedKey = true;
3174
  //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
3175
+ if ( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && ! ShortPixelFolder::createBackUpFolder() )
3176
  $notice = array("status" => "error",
3177
  "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'),
3178
  WP_CONTENT_DIR . '/' . SHORTPIXEL_UPLOADS_NAME ));
3182
  $notice = array("status" => "error", "msg" => $validityData["Message"]);
3183
  }
3184
  $this->_settings->verifiedKey = false;
3185
+ } */
3186
+ // }
3187
 
3188
  //if save button - we process the rest of the form elements
3189
  if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
3190
+ /* $this->_settings->compressionType = intval($_POST['compressionType']);
3191
  if(isset($_POST['thumbnails'])) { $this->_settings->processThumbnails = 1; } else { $this->_settings->processThumbnails = 0; }
3192
  if(isset($_POST['backupImages'])) { $this->_settings->backupImages = 1; } else { $this->_settings->backupImages = 0; }
3193
  if(isset($_POST['cmyk2rgb'])) { $this->_settings->CMYKtoRGBconversion = 1; } else { $this->_settings->CMYKtoRGBconversion = 0; }
3197
  $this->_settings->resizeType = (isset($_POST['resize_type']) ? sanitize_text_field($_POST['resize_type']) : false);
3198
  $this->_settings->resizeWidth = (isset($_POST['width']) ? intval($_POST['width']): $this->_settings->resizeWidth);
3199
  $this->_settings->resizeHeight = (isset($_POST['height']) ? intval($_POST['height']): $this->_settings->resizeHeight);
3200
+ $uploadPath = realpath(SHORTPIXEL_UPLOADS_BASE); */
3201
 
3202
+ /* if(isset($_POST['nextGen'])) {
3203
  WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
3204
  $prevNextGen = $this->_settings->includeNextGen;
3205
  $this->_settings->includeNextGen = 1;
3208
  $customFolders = $ret["customFolders"];
3209
  } else {
3210
  $this->_settings->includeNextGen = 0;
3211
+ } */
3212
+ /* if(isset($_POST['addCustomFolder']) && strlen($_POST['addCustomFolder']) > 0) {
3213
  $folderMsg = $this->spMetaDao->newFolderFromPath(stripslashes($_POST['addCustomFolder']), $uploadPath, self::getCustomFolderBase());
3214
  if(!$folderMsg) {
3215
  $notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
3216
  }
3217
  $customFolders = $this->spMetaDao->getFolders();
3218
  $this->_settings->hasCustomFolders = time();
3219
+ } */
 
 
3220
 
3221
+ // $this->_settings->createWebp = (isset($_POST['createWebp']) ? 1: 0);
3222
 
3223
+ /* if( isset( $_POST['createWebp'] ) && $_POST['createWebp'] == 'on' ){
 
3224
  if( isset( $_POST['deliverWebp'] ) && $_POST['deliverWebp'] == 'on' ){
3225
  if( isset( $_POST['deliverWebpType'] ) ) {
3226
  switch( $_POST['deliverWebpType'] ) {
3250
  } else {
3251
  if(!$isNginx) self::alterHtaccess(true);
3252
  $this->_settings->deliverWebp = 0;
3253
+ } */
 
 
3254
 
3255
+ /*$this->_settings->optimizeRetina = (isset($_POST['optimizeRetina']) ? 1: 0);
 
 
3256
  $this->_settings->optimizeUnlisted = (isset($_POST['optimizeUnlisted']) ? 1: 0);
3257
  $this->_settings->optimizePdfs = (isset($_POST['optimizePdfs']) ? 1: 0);
3258
+ */
3259
+ //$this->_settings->png2jpg = (isset($_POST['png2jpg']) ? (isset($_POST['png2jpgForce']) ? 2 : 1): 0);
 
3260
 
3261
+ /* if(isset($_POST['excludePatterns']) && strlen($_POST['excludePatterns'])) {
3262
  $patterns = array();
3263
  $items = explode(',', $_POST['excludePatterns']);
3264
  foreach($items as $pat) {
3273
  } else {
3274
  $this->_settings->excludePatterns = array();
3275
  }
3276
+ */
3277
+ // $this->_settings->frontBootstrap = (isset($_POST['frontBootstrap']) ? 1: 0);
3278
+ // $this->_settings->autoMediaLibrary = (isset($_POST['autoMediaLibrary']) ? 1: 0);
3279
+ // $this->_settings->excludeSizes = (isset($_POST['excludeSizes']) ? $_POST['excludeSizes']: array());
3280
 
3281
  //Redirect to bulk processing if requested
3282
  if( isset($_POST['save']) && $_POST['save'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')
3285
  exit();
3286
  }
3287
  }
3288
+ /*if(isset($_POST['removeFolder']) && strlen(($_POST['removeFolder']))) {
3289
  $this->spMetaDao->removeFolder($_POST['removeFolder']);
3290
  $customFolders = $this->spMetaDao->getFolders();
3291
  $_POST["saveAdv"] = true;
3292
+ } */
3293
  if(isset($_POST['recheckFolder']) && strlen(($_POST['recheckFolder']))) {
3294
  //$folder->fullRefreshCustomFolder($_POST['recheckFolder']); //aici singura solutie pare callback care spune daca exita url-ul complet
3295
  }
3296
  }
3297
 
3298
  //now output headers. They were prevented with noheaders=true in the form url in order to be able to redirect if bulk was pressed
3299
+ /* if(isset($_REQUEST['noheader'])) {
3300
  require_once(ABSPATH . 'wp-admin/admin-header.php');
3301
+ } */
3302
 
3303
  //empty backup
3304
+ /*if(isset($_POST['emptyBackup'])) {
3305
  $this->emptyBackup();
3306
+ } */
3307
 
3308
+ //$quotaData = $this->checkQuotaAndAlert(isset($validityData) ? $validityData : null, isset($_GET['checkquota']));
3309
 
3310
+ /* if($this->hasNextGen) {
3311
  $ngg = array_map(array('ShortPixelNextGenAdapter','pathToAbsolute'), ShortPixelNextGenAdapter::getGalleries());
3312
  //die(var_dump($ngg));
3313
  for($i = 0; $i < count($customFolders); $i++) {
3315
  $customFolders[$i]->setType("NextGen");
3316
  }
3317
  }
3318
+ } */
3319
 
3320
+ /* $showApiKey = ( (is_main_site() || (function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY")))
3321
  && !defined("SHORTPIXEL_HIDE_API_KEY"));
3322
+ $editApiKey = !defined("SHORTPIXEL_API_KEY") && $showApiKey; */
3323
 
3324
  if($this->_settings->verifiedKey) {
3325
+ /* $fileCount = number_format($this->_settings->fileCount);
3326
  $savedSpace = self::formatBytes($this->_settings->savedSpace,2);
3327
  $averageCompression = $this->getAverageCompression();
3328
  $savedBandwidth = self::formatBytes($this->_settings->savedSpace * 10000,2);
3332
  $remainingImages = $quotaData['APICallsRemaining'];
3333
  $remainingImages = ( $remainingImages < 0 ) ? 0 : number_format($remainingImages);
3334
  $totalCallsMade = array( 'plan' => $quotaData['APICallsMadeNumeric'] , 'oneTime' => $quotaData['APICallsMadeOneTimeNumeric'] );
3335
+ */
3336
+ /* $resources = wp_remote_post($this->_settings->httpProto . "://shortpixel.com/resources-frag");
3337
  if(is_wp_error( $resources )) {
3338
  $resources = array();
3339
+ } */
3340
 
3341
  $cloudflareAPI = true;
3342
 
3350
 
3351
  }
3352
 
3353
+ /** Adds NextGenGalleries to Custom Images Library
3354
+ * @param boolean $silent Will not return messages if silent
3355
+ * @return array Array for information
3356
+ * @todo Move to a integration class || This can be removed after nextgen.php in externals is released.
3357
+ */
3358
  public function addNextGenGalleriesToCustom($silent) {
3359
  $customFolders = array();
3360
  $folderMsg = "";
3374
  return array("message" => $silent? "" : $folderMsg, "customFolders" => $customFolders);
3375
  }
3376
 
3377
+ /** Gets the average compression
3378
+ * @return int Average compressions percentage
3379
+ * @todo Move to utility (?)
3380
+ */
3381
  public function getAverageCompression(){
3382
  return $this->_settings->totalOptimized > 0
3383
  ? round(( 1 - ( $this->_settings->totalOptimized / $this->_settings->totalOriginal ) ) * 100, 2)
3421
  $args['body']['host'] = parse_url(get_site_url(),PHP_URL_HOST);
3422
  $argsStr .= "&host={$args['body']['host']}";
3423
  if(strlen($this->_settings->siteAuthUser)) {
3424
+ $args['body']['user'] = stripslashes($this->_settings->siteAuthUser);
3425
+ $args['body']['pass'] = stripslashes($this->_settings->siteAuthPass);
3426
  $argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
3427
  }
3428
  if($settings !== false) {
3469
  $response = wp_remote_get($requestURL, $args);
3470
  $comm['C: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
3471
  }
3472
+ Log::addInfo("API STATUS COMM: " . json_encode($comm));
3473
 
3474
  $defaultData = array(
3475
  "APIKeyValid" => false,
3555
  $this->_settings->quotaExceeded = 0;
3556
  }
3557
 
3558
+ /** Generates column for custom media library
3559
+ * @todo Move this to custom media controller
3560
+ */
3561
  public function generateCustomColumn( $column_name, $id, $extended = false ) {
3562
  if( 'wp-shortPixel' == $column_name ) {
3563
 
3571
  $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
3572
 
3573
  if($extended && isset($_GET['SHORTPIXEL_DEBUG'])) {
3574
+ // var_dump($data);
3575
  var_dump(wp_get_attachment_url($id));
3576
  echo('<br><br>' . json_encode(ShortPixelMetaFacade::getWPMLDuplicates($id)));
3577
+ echo('<br><br>'); print_r($data); echo ''; //json_encode($data))
3578
  echo('<br><br>');
3579
  }
3580
 
3701
  }
3702
  }
3703
 
3704
+ /** Make columns sortable in Media Library
3705
+ * @hook manage_upload_sortable_columns
3706
+ * @param array $columns Array of colums sortable
3707
+ * @todo Should be part of media library controller.
3708
+ */
3709
  function columnRegisterSortable($columns) {
3710
  $columns['wp-shortPixel'] = 'ShortPixel Compression';
3711
  return $columns;
3712
  }
3713
 
3714
+ /** Apply sort filter in Media Library
3715
+ * @hook request
3716
+ * @param array $columns Array of colums sortable
3717
+ * @todo Should be part of media library controller. ( is request best hook for this?)
3718
+ */
3719
+
3720
  function columnOrderFilterBy($vars) {
3721
  if ( isset( $vars['orderby'] ) && 'ShortPixel Compression' == $vars['orderby'] ) {
3722
  $vars = array_merge( $vars, array(
3743
  return $vars;
3744
  }
3745
 
3746
+ /*
3747
+ * @hook restrict_manage_posts
3748
+ * @todo Should be part of media library controller. ( is request best hook for this?)
3749
+ */
3750
  function mediaAddFilterDropdown() {
3751
  $scr = get_current_screen();
3752
  if ( $scr->base !== 'upload' ) return;
3769
  . "</select>");
3770
  }
3771
 
3772
+ /** Calculates Optimization if PNG2Jpg does something
3773
+ * @param array $meta Image metadata
3774
+ * @return string Formatted improvement
3775
+ */
3776
  function optimizationPercentIfPng2Jpg($meta) {
3777
  $png2jpgPercent = isset($meta['ShortPixelPng2Jpg']['optimizationPercent']) ? $meta['ShortPixelPng2Jpg']['optimizationPercent'] : 0;
3778
  return number_format(100.0 - (100.0 - $png2jpgPercent) * (100.0 - $meta['ShortPixelImprovement']) / 100.0, 2);
3779
  }
3780
 
3781
+ /** Meta box for shortpixel in view image
3782
+ * @hook add_meta_boxes
3783
+ * @todo move to appr. controller
3784
+ */
3785
  function shortpixelInfoBox() {
3786
  if(get_post_type( ) == 'attachment') {
3787
  add_meta_box(
3795
  }
3796
  }
3797
 
3798
+ /** Meta box for view image
3799
+ * @todo move to appr. controller
3800
+ */
3801
  function shortpixelInfoBoxContent( $post ) {
3802
  $this->generateCustomColumn( 'wp-shortPixel', $post->ID, true );
3803
  }
3804
 
3805
+ /** When an image is deleted
3806
+ * @hook delete_attachment
3807
+ * @param int $post_id ID of Post
3808
+ * @return itemHandler ItemHandler object.
3809
+ */
3810
  public function onDeleteImage($post_id) {
3811
  $itemHandler = new ShortPixelMetaFacade($post_id);
3812
  $urlsPaths = $itemHandler->getURLsAndPATHs(true, false, true, array(), true);
3817
  return $itemHandler; //return it because we call it also on replace and on replace we need to follow this by deleting SP metadata, on delete it
3818
  }
3819
 
3820
+ /** Removes webp and backup from specified paths
3821
+ */
3822
  public function deleteBackupsAndWebPs($paths) {
3823
+ /**
3824
+ * Passing a truthy value to the filter will effectively short-circuit this function.
3825
+ * So third party plugins can handle deletion by there own.
3826
+ */
3827
+ if(apply_filters('shortpixel_skip_delete_backups_and_webps', false, $paths)){
3828
+ return;
3829
+ }
3830
+
3831
  $backupFolder = trailingslashit($this->getBackupFolder($paths[0]));
3832
  foreach($paths as $path) {
3833
  $pos = strrpos($path, ".");
3844
  @unlink($backupFolder . preg_replace("/\." . $extension . "$/i", '@2x.' . $extension, $fileName));
3845
  }
3846
  }
3847
+ //
3848
+ /**
3849
+ * @hook manage_media_columns
3850
+ * @todo Move to appr. controller.
3851
+ */
3852
  public function columns( $defaults ) {
3853
  $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
3854
  if(current_user_can( 'manage_options' )) {
3855
  $defaults['wp-shortPixel'] .=
3856
+ '&nbsp;<a href="options-general.php?page=wp-shortpixel-settings&part=stats" title="'
3857
  . __('ShortPixel Statistics','shortpixel-image-optimiser')
3858
  . '"><span class="dashicons dashicons-dashboard"></span></a>';
3859
  }
3860
  return $defaults;
3861
  }
3862
 
3863
+ // todo move NGG specific function to own integration
3864
  public function nggColumns( $defaults ) {
3865
  $this->nggColumnIndex = count($defaults) + 1;
3866
  add_filter( 'ngg_manage_images_column_' . $this->nggColumnIndex . '_header', array( &$this, 'nggColumnHeader' ) );
3913
  }
3914
 
3915
  public function generatePluginLinks($links) {
3916
+ $in = '<a href="options-general.php?page=wp-shortpixel-settings">Settings</a>';
3917
  array_unshift($links, $in);
3918
  return $links;
3919
  }
3920
 
3921
+ // @todo Should be utility function
3922
  static public function formatBytes($bytes, $precision = 2) {
3923
  $units = array('B', 'KB', 'MB', 'GB', 'TB');
3924
 
3931
  return round($bytes, $precision) . ' ' . $units[$pow];
3932
  }
3933
 
3934
+ /** Checks if file can be processed. Mainly against exclusion
3935
+ * @param int $ID ImageID
3936
+ * @param array $excludeExtensions Excludes Extentions from settings
3937
+ * @todo Part of Image model
3938
+ */
3939
  public function isProcessable($ID, $excludeExtensions = array()) {
3940
  $excludePatterns = $this->_settings->excludePatterns;
3941
  return self::_isProcessable($ID, $excludeExtensions, $excludePatterns);
3942
  }
3943
 
3944
+ /** Checks if path can be processed. Mainly against exclusion
3945
+ * @param string $path Path
3946
+ * @param array $excludeExtensions Excludes Extentions from settings
3947
+ * @todo Part of Image / Folder(?) model
3948
+ */
3949
  public function isProcessablePath($path, $excludeExtensions = array()) {
3950
  $excludePatterns = $this->_settings->excludePatterns;
3951
  return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
4015
  return $itemHandler->getURLsAndPATHs($this->_settings->processThumbnails, $onlyThumbs, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
4016
  }
4017
 
4018
+ /** Remove a directory
4019
+ * @param string $dirPath Path of directory to remove.
4020
+ * @todo Part of folder model.
4021
+ */
4022
  public static function deleteDir($dirPath) {
4023
  if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
4024
  $dirPath .= '/';
4034
  }
4035
  }
4036
 
4037
+ /** Gets size of folder recursivly
4038
+ * @param string $path Path
4039
+ * @todo Move to folder model
4040
+ */
4041
  static public function folderSize($path) {
4042
  $total_size = 0;
4043
  if(file_exists($path)) {
4070
 
4071
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {
4072
  //we check that the backup folder exists, if not we create it so we can copy into it
4073
+ if(! ShortPixelFolder::createBackUpFolder() ) return;
4074
  }
4075
 
4076
  $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
4082
  @rmdir($oldBackupFolder);
4083
  }
4084
  }
4085
+
4086
  //now if the backup folder does not contain the uploads level, create it
4087
  if( !is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . SHORTPIXEL_UPLOADS_NAME )
4088
  && !is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR))) {
4089
  @rename(SHORTPIXEL_BACKUP_FOLDER, SHORTPIXEL_BACKUP_FOLDER."_tmp");
4090
+ ShortPixelFolder::createBackUpFolder();
4091
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/'.SHORTPIXEL_UPLOADS_NAME);
4092
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
4093
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
4096
  //then create the wp-content level if not present
4097
  if(!is_dir(SHORTPIXEL_BACKUP_FOLDER . '/' . basename(WP_CONTENT_DIR))) {
4098
  @rename(SHORTPIXEL_BACKUP_FOLDER, SHORTPIXEL_BACKUP_FOLDER."_tmp");
4099
+ ShortPixelFolder::createBackUpFolder();
4100
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER.'/' . basename(WP_CONTENT_DIR));
4101
  if(!file_exists(SHORTPIXEL_BACKUP_FOLDER)) {//just in case..
4102
  @rename(SHORTPIXEL_BACKUP_FOLDER."_tmp", SHORTPIXEL_BACKUP_FOLDER);
4103
  }
4104
  }
4105
+
4106
+ if (! file_exists( trailingslashit(SHORTPIXEL_BACKUP_FOLDER) . '.htaccess') )
4107
+ {
4108
+ ShortPixelFolder::protectDirectoryListing(SHORTPIXEL_BACKUP_FOLDER);
4109
+ }
4110
  return;
4111
  }
4112
 
4128
  return $sizes;
4129
  }
4130
 
4131
+ /** @todo Remove here
4132
+ * */
4133
  function getMaxIntermediateImageSize() {
4134
  global $_wp_additional_image_sizes;
4135
 
4156
  return array_values(array_diff(array(0, 1, 2), array(0 + $compressionType)));
4157
  }
4158
 
4159
+ function outputHSBeacon() {
4160
+ Log::addWarn('OutputHSBeacon called on old function');
4161
+ ?>
4162
+ <style>
4163
+ .shortpixel-hs-blind {
4164
+ position: fixed;
4165
+ bottom: 18px;
4166
+ right: 0;
4167
+ z-index: 20003;
4168
+ background-color: white;
4169
+ width: 87px;
4170
+ height: 174px;
4171
+ border-radius: 20px 0 0 20px;
4172
+ }
4173
+ .shortpixel-hs-button-blind {
4174
+ display:none;
4175
+ position: fixed;
4176
+ bottom: 115px;right: 0;
4177
+ z-index: 20003;
4178
+ background-color: white;
4179
+ width: 237px;
4180
+ height: 54px;
4181
+ }
4182
+ .shortpixel-hs-tools {
4183
+ position: fixed;
4184
+ bottom: 116px;
4185
+ right: 0px;
4186
+ z-index: 20003;
4187
+ background-color: #ecf9fc;
4188
+ padding: 8px 18px 3px 12px;
4189
+ border-radius: 26px 0 0 26px;
4190
+ -webkit-box-shadow: 1px 1px 5px 0px rgba(6,109,117,1);
4191
+ -moz-box-shadow: 1px 1px 5px 0px rgba(6,109,117,1);
4192
+ box-shadow: 1px 1px 10px 0px rgb(172, 173, 173);
4193
+ }
4194
+ @media (max-width: 767px) {
4195
+ .shortpixel-hs-blind {
4196
+ bottom: 8px;
4197
+ height: 194px;
4198
+ }
4199
+ .shortpixel-hs-button-blind {
4200
+ bottom: 100px;
4201
+ }
4202
+ }
4203
+ </style>
4204
+ <div id="shortpixel-hs-blind" class="shortpixel-hs-blind"></div>
4205
+ <div id="shortpixel-hs-button-blind" class="shortpixel-hs-button-blind"></div>
4206
+ <div id="shortpixel-hs-tools" class="shortpixel-hs-tools">
4207
+ <a href="javascript:shortpixelToggleHS();" class="shortpixel-hs-tools-docs" title="Search through our online documentation.">
4208
+ <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/notes-sp.png'));?>" style="margin-bottom: 2px;width: 36px;">
4209
+ </a>
4210
+ </div>
4211
+ <script>
4212
+ window.shortpixelHSOpen = -1;
4213
+ function shortpixelToggleHS() {
4214
+ if(window.shortpixelHSOpen == -1) {
4215
+ HS.beacon.init();
4216
+ }
4217
+ if(window.shortpixelHSOpen == 1) {
4218
+ HS.beacon.close();
4219
+ jQuery("#shortpixel-hs-button-blind").css('display', 'none');
4220
+ window.shortpixelHSOpen = 0;
4221
+ } else {
4222
+ HS.beacon.open();
4223
+ jQuery("#shortpixel-hs-button-blind").css('display', 'block');
4224
+ window.shortpixelHSOpen = 1;
4225
+ }
4226
+ }
4227
+ </script>
4228
+ <script type="text/javascript" src="https://quriobot.com/qb/widget/KoPqxmzqzjbg5eNl/V895xbyndnmeqZYd" async defer></script>
4229
  <script>
4230
  <?php
4231
  $screen = get_current_screen();
4340
  return $this->_settings->resizeHeight;
4341
  }
4342
  public static function getAffiliateSufix() {
4343
+ Log::addDebug('Function call - getAffiliateSufix should be removed');
4344
  // not allowed anymore by WP as of Sept.27 2018
4345
  // return isset($_COOKIE["AffiliateShortPixel"])
4346
  // ? "/affiliate/" . $_COOKIE["AffiliateShortPixel"]
class/wp-shortpixel-settings.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
-
3
- class WPShortPixelSettings {
4
  private $_apiKey = '';
5
  private $_compressionType = 1;
6
  private $_keepExif = 0;
@@ -8,7 +8,7 @@ class WPShortPixelSettings {
8
  private $_CMYKtoRGBconversion = 1;
9
  private $_backupImages = 1;
10
  private $_verifiedKey = false;
11
-
12
  private $_resizeImages = false;
13
  private $_resizeWidth = 0;
14
  private $_resizeHeight = 0;
@@ -77,7 +77,7 @@ class WPShortPixelSettings {
77
  'mediaLibraryViewMode' => array('key' => 'wp-short-pixel-view-mode', 'default' => null, 'group' => 'state'),
78
  'redirectedSettings' => array('key' => 'wp-short-pixel-redirected-settings', 'default' => null, 'group' => 'state'),
79
  'convertedPng2Jpg' => array('key' => 'wp-short-pixel-converted-png2jpg', 'default' => array(), 'group' => 'state'),
80
-
81
  //bulk state machine
82
  'bulkType' => array('key' => 'wp-short-pixel-bulk-type', 'default' => null, 'group' => 'bulk'),
83
  'bulkLastStatus' => array('key' => 'wp-short-pixel-bulk-last-status', 'default' => null, 'group' => 'bulk'),
@@ -96,17 +96,52 @@ class WPShortPixelSettings {
96
  'flagId' => array('key' => 'wp-short-pixel-flag-id', 'default' => 0, 'group' => 'bulk'),
97
  'failedImages' => array('key' => 'wp-short-pixel-failed-imgs', 'default' => 0, 'group' => 'bulk'),
98
  'bulkProcessingStatus' => array('key' => 'bulkProcessingStatus', 'default' => null, 'group' => 'bulk'),
99
-
100
  //'priorityQueue' => array('key' => 'wp-short-pixel-priorityQueue', 'default' => array()),
101
  'prioritySkip' => array('key' => 'wp-short-pixel-prioritySkip', 'default' => array(), 'group' => 'state'),
102
-
103
  //'' => array('key' => 'wp-short-pixel-', 'default' => null),
104
  );
105
-
106
- public function __construct() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  $this->populateOptions();
108
- }
109
-
110
  public function populateOptions() {
111
 
112
  $this->_apiKey = self::getOpt('wp-short-pixel-apiKey', '');
@@ -115,9 +150,9 @@ class WPShortPixelSettings {
115
  $this->_processThumbnails = self::getOpt('wp-short-process_thumbnails', $this->_processThumbnails);
116
  $this->_CMYKtoRGBconversion = self::getOpt('wp-short-pixel_cmyk2rgb', $this->_CMYKtoRGBconversion);
117
  $this->_backupImages = self::getOpt('wp-short-backup_images', $this->_backupImages);
118
- $this->_resizeImages = self::getOpt( 'wp-short-pixel-resize-images', 0);
119
- $this->_resizeWidth = self::getOpt( 'wp-short-pixel-resize-width', 0);
120
- $this->_resizeHeight = self::getOpt( 'wp-short-pixel-resize-height', 0);
121
 
122
  // the following lines practically set defaults for options if they're not set
123
  foreach(self::$_optionsMap as $opt) {
@@ -128,14 +163,14 @@ class WPShortPixelSettings {
128
  self::setOpt(self::$_optionsMap["downloadArchive"]['key'], crc32(get_site_url())%10);
129
  }
130
  }
131
-
132
  public static function debugResetOptions() {
133
  foreach(self::$_optionsMap as $key => $val) {
134
  delete_option($val['key']);
135
  }
136
  delete_option("wp-short-pixel-bulk-previous-percent");
137
  }
138
-
139
  public static function onActivate() {
140
  if(!self::getOpt('wp-short-pixel-verifiedKey', false)) {
141
  update_option('wp-short-pixel-activation-notice', true, 'no');
@@ -155,12 +190,12 @@ class WPShortPixelSettings {
155
  delete_option('wp-short-pixel-priorityQueue');
156
  }
157
  }
158
-
159
  public static function onDeactivate() {
160
  delete_option('wp-short-pixel-activation-notice');
161
  }
162
 
163
-
164
  public function __get($name)
165
  {
166
  if (array_key_exists($name, self::$_optionsMap)) {
@@ -181,8 +216,8 @@ class WPShortPixelSettings {
181
  $this->setOpt(self::$_optionsMap[$name]['key'], $value);
182
  } else {
183
  delete_option(self::$_optionsMap[$name]['key']);
184
- }
185
- }
186
  }
187
 
188
  public static function getOpt($key, $default = null) {
@@ -194,7 +229,7 @@ class WPShortPixelSettings {
194
  }
195
  return get_option($key);
196
  }
197
-
198
  public function setOpt($key, $val) {
199
  $ret = update_option($key, $val, 'no');
200
 
@@ -216,17 +251,17 @@ class WPShortPixelSettings {
216
  $sql = "SELECT * FROM {$wpdb->prefix}options WHERE option_name = '" . $key . "'";
217
  $rows = $wpdb->get_results($sql);
218
  if(count($rows) === 0) {
219
- $wpdb->insert($wpdb->prefix.'options',
220
- array("option_name" => $key, "option_value" => (is_array($val) ? serialize($val) : $val), "autoload" => "no"),
221
  array("option_name" => "%s", "option_value" => (is_numeric($val) ? "%d" : "%s")));
222
  } else { //update
223
- $sql = "update {$wpdb->prefix}options SET option_value=" .
224
- (is_array($val)
225
- ? "'" . serialize($val) . "'"
226
  : (is_numeric($val) ? $val : "'" . $val . "'")) . " WHERE option_name = '" . $key . "'";
227
  $rows = $wpdb->get_results($sql);
228
  }
229
-
230
  if($val != get_option($key)) {
231
  //tough luck, gonna use the bomb...
232
  wp_cache_flush();
1
  <?php
2
+ /** Settings Model **/
3
+ class WPShortPixelSettings extends ShortPixel\ShortPixelModel {
4
  private $_apiKey = '';
5
  private $_compressionType = 1;
6
  private $_keepExif = 0;
8
  private $_CMYKtoRGBconversion = 1;
9
  private $_backupImages = 1;
10
  private $_verifiedKey = false;
11
+
12
  private $_resizeImages = false;
13
  private $_resizeWidth = 0;
14
  private $_resizeHeight = 0;
77
  'mediaLibraryViewMode' => array('key' => 'wp-short-pixel-view-mode', 'default' => null, 'group' => 'state'),
78
  'redirectedSettings' => array('key' => 'wp-short-pixel-redirected-settings', 'default' => null, 'group' => 'state'),
79
  'convertedPng2Jpg' => array('key' => 'wp-short-pixel-converted-png2jpg', 'default' => array(), 'group' => 'state'),
80
+
81
  //bulk state machine
82
  'bulkType' => array('key' => 'wp-short-pixel-bulk-type', 'default' => null, 'group' => 'bulk'),
83
  'bulkLastStatus' => array('key' => 'wp-short-pixel-bulk-last-status', 'default' => null, 'group' => 'bulk'),
96
  'flagId' => array('key' => 'wp-short-pixel-flag-id', 'default' => 0, 'group' => 'bulk'),
97
  'failedImages' => array('key' => 'wp-short-pixel-failed-imgs', 'default' => 0, 'group' => 'bulk'),
98
  'bulkProcessingStatus' => array('key' => 'bulkProcessingStatus', 'default' => null, 'group' => 'bulk'),
99
+
100
  //'priorityQueue' => array('key' => 'wp-short-pixel-priorityQueue', 'default' => array()),
101
  'prioritySkip' => array('key' => 'wp-short-pixel-prioritySkip', 'default' => array(), 'group' => 'state'),
102
+
103
  //'' => array('key' => 'wp-short-pixel-', 'default' => null),
104
  );
105
+
106
+ // This array -- field_name -> (s)anitize mask
107
+ protected $model = array(
108
+ 'apiKey' => array('s' => 'string'), // string
109
+ // 'verifiedKey' => array('s' => 'string'), // string
110
+ 'compressionType' => array('s' => 'int'), // int
111
+ 'resizeWidth' => array('s' => 'int'), // int
112
+ 'resizeHeight' => array('s' => 'int'), // int
113
+ 'processThumbnails' => array('s' => 'boolean'), // checkbox
114
+ 'backupImages' => array('s' => 'boolean'), // checkbox
115
+ 'keepExif' => array('s' => 'int'), // checkbox
116
+ 'resizeImages' => array('s' => 'boolean'),
117
+ 'resizeType' => array('s' => 'string'),
118
+ 'includeNextGen' => array('s' => 'boolean'), // checkbox
119
+ 'png2jpg' => array('s' => 'int'), // checkbox
120
+ 'CMYKtoRGBconversion' => array('s' => 'boolean'), //checkbox
121
+ 'createWebp' => array('s' => 'boolean'), // checkbox
122
+ 'deliverWebp' => array('s' => 'int'), // checkbox
123
+ 'optimizeRetina' => array('s' => 'boolean'), // checkbox
124
+ 'optimizeUnlisted' => array('s' => 'boolean'), // $checkbox
125
+ 'optimizePdfs' => array('s' => 'boolean'), //checkbox
126
+ 'excludePatterns' => array('s' => 'exception'), // - processed, multi-layer, so skip
127
+ 'siteAuthUser' => array('s' => 'string'), // string
128
+ 'siteAuthPass' => array('s' => 'string'), // string
129
+ 'frontBootstrap' => array('s' =>'boolean'), // checkbox
130
+ 'autoMediaLibrary' => array('s' => 'boolean'), // checkbox
131
+ 'excludeSizes' => array('s' => 'array'), // Array
132
+ 'cloudflareEmail' => array('s' => 'string'), // string
133
+ 'cloudflareAuthKey' => array('s' => 'string'), // string
134
+ 'cloudflareZoneID' => array('s' => 'string'), // string
135
+ 'savedSpace' => array('s' => 'skip'),
136
+ 'fileCount' => array('s' => 'skip'), // int
137
+ 'under5Percent' => array('s' => 'skip'), // int
138
+ );
139
+
140
+ // @todo Eventually, this should not happen onLoad, but on demand.
141
+ public function __construct() {
142
  $this->populateOptions();
143
+ }
144
+
145
  public function populateOptions() {
146
 
147
  $this->_apiKey = self::getOpt('wp-short-pixel-apiKey', '');
150
  $this->_processThumbnails = self::getOpt('wp-short-process_thumbnails', $this->_processThumbnails);
151
  $this->_CMYKtoRGBconversion = self::getOpt('wp-short-pixel_cmyk2rgb', $this->_CMYKtoRGBconversion);
152
  $this->_backupImages = self::getOpt('wp-short-backup_images', $this->_backupImages);
153
+ $this->_resizeImages = self::getOpt( 'wp-short-pixel-resize-images', 0);
154
+ $this->_resizeWidth = self::getOpt( 'wp-short-pixel-resize-width', 0);
155
+ $this->_resizeHeight = self::getOpt( 'wp-short-pixel-resize-height', 0);
156
 
157
  // the following lines practically set defaults for options if they're not set
158
  foreach(self::$_optionsMap as $opt) {
163
  self::setOpt(self::$_optionsMap["downloadArchive"]['key'], crc32(get_site_url())%10);
164
  }
165
  }
166
+
167
  public static function debugResetOptions() {
168
  foreach(self::$_optionsMap as $key => $val) {
169
  delete_option($val['key']);
170
  }
171
  delete_option("wp-short-pixel-bulk-previous-percent");
172
  }
173
+
174
  public static function onActivate() {
175
  if(!self::getOpt('wp-short-pixel-verifiedKey', false)) {
176
  update_option('wp-short-pixel-activation-notice', true, 'no');
190
  delete_option('wp-short-pixel-priorityQueue');
191
  }
192
  }
193
+
194
  public static function onDeactivate() {
195
  delete_option('wp-short-pixel-activation-notice');
196
  }
197
 
198
+
199
  public function __get($name)
200
  {
201
  if (array_key_exists($name, self::$_optionsMap)) {
216
  $this->setOpt(self::$_optionsMap[$name]['key'], $value);
217
  } else {
218
  delete_option(self::$_optionsMap[$name]['key']);
219
+ }
220
+ }
221
  }
222
 
223
  public static function getOpt($key, $default = null) {
229
  }
230
  return get_option($key);
231
  }
232
+
233
  public function setOpt($key, $val) {
234
  $ret = update_option($key, $val, 'no');
235
 
251
  $sql = "SELECT * FROM {$wpdb->prefix}options WHERE option_name = '" . $key . "'";
252
  $rows = $wpdb->get_results($sql);
253
  if(count($rows) === 0) {
254
+ $wpdb->insert($wpdb->prefix.'options',
255
+ array("option_name" => $key, "option_value" => (is_array($val) ? serialize($val) : $val), "autoload" => "no"),
256
  array("option_name" => "%s", "option_value" => (is_numeric($val) ? "%d" : "%s")));
257
  } else { //update
258
+ $sql = "update {$wpdb->prefix}options SET option_value=" .
259
+ (is_array($val)
260
+ ? "'" . serialize($val) . "'"
261
  : (is_numeric($val) ? $val : "'" . $val . "'")) . " WHERE option_name = '" . $key . "'";
262
  $rows = $wpdb->get_results($sql);
263
  }
264
+
265
  if($val != get_option($key)) {
266
  //tough luck, gonna use the bomb...
267
  wp_cache_flush();
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.2
6
  Requires PHP: 5.3
7
- Stable tag: 4.13.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -216,7 +216,9 @@ The ShortPixel Image Optimiser plugin calls the following actions and filters:
216
  > do_action( 'shortpixel_image_optimised', $post_id ); //upon successful optimization
217
  > do_action("shortpixel_before_restore_image", $post_id); //before restoring an image from backup
218
  > do_action("shortpixel_after_restore_image", $post_id); //after succesful restore
219
- > apply_filters("shortpixel_backup_folder", $backup_folder); //just before returning the ShortPixel backup folder, usually ...wp-content/uploads/ShortpixelBackups
 
 
220
 
221
 
222
  == Screenshots ==
@@ -241,6 +243,23 @@ The ShortPixel Image Optimiser plugin calls the following actions and filters:
241
 
242
  == Changelog ==
243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  = 4.13.1 =
245
 
246
  Release date: 16th April 2019
4
  Requires at least: 3.2.0
5
  Tested up to: 5.2
6
  Requires PHP: 5.3
7
+ Stable tag: 4.14.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
216
  > do_action( 'shortpixel_image_optimised', $post_id ); //upon successful optimization
217
  > do_action("shortpixel_before_restore_image", $post_id); //before restoring an image from backup
218
  > do_action("shortpixel_after_restore_image", $post_id); //after succesful restore
219
+ > apply_filters("shortpixel_backup_folder", $backup_folder, $main_file_path, $sizes); //just before returning the ShortPixel backup folder, usually /wp-content/uploads/ShortpixelBackups. The $sizes are the sizes array from metadata.
220
+ > apply_filters('shortpixel_image_exists', file_exists($path), $path, $post_id); //post ID is not always set, only if it's an image from Media Library
221
+ > apply_filters('shortpixel_image_urls', $URLs, $post_id) // filters the URLs that will be sent to optimization, $URLs is a plain array
222
 
223
 
224
  == Screenshots ==
243
 
244
  == Changelog ==
245
 
246
+ == 4.14.0 ==
247
+
248
+ Release date: 17th June 2019
249
+ * Add new filters proposed by WP Stateless: shortpixel_backup_folder, shortpixel_image_exists, shortpixel_image_urls
250
+ * Better placement of the elements on the Other Media page
251
+ * Fix custom bulk for PDFs when the pdf thumbnails are not activated
252
+ * Fix selecting items from DB twice for bulk in some circumstances
253
+ * Warn user that converting PNG to JPG while keeping EXIF in options doesn't keep it (no EXIF for PNGs).
254
+ * When SHORTPIXEL_DEBUG=x get parameter is provided, display a floating link to the shortpixel_log file
255
+ * Adaptive Max execution time and capped to 90 sec. for the bulk background AJAX calls. (Kinsta has a max_execution_time of 300 sec. in PHP but the HTTP connection is cut after 180 sec.)
256
+ * Fix custom 404 page for missing images not working when using .htaccess for WebP
257
+ * Fix WebP picture tag with relative URLs not working in some circumstances
258
+ * Fix replacing the <img> inside an existing <picture> tag with another <picture> tag.
259
+ * Clear SP optimization cache in order to be able to optimize an image which initially had permissions error, after changing the permissions.
260
+ * Fix being able to list the contents of ShortpixelBackups on some badly configured servers.
261
+ * Fix error when inputting D'Artagnan in the AUTH pass field of settings.
262
+
263
  = 4.13.1 =
264
 
265
  Release date: 16th April 2019
res/css/short-pixel-modal.min.css CHANGED
@@ -1 +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}
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,.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:0 0;border:none;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
@@ -79,6 +79,7 @@ div.fb-like {
79
  .wp-core-ui .button.remove-folder-button {
80
  min-width: 120px;
81
  }
 
82
  .sp-notice {
83
  background: #fff;
84
  border-left: 4px solid #fff;
@@ -605,46 +606,12 @@ article.sp-tabs section .wp-shortpixel-tab-content {
605
  visibility: hidden;
606
  }
607
  article.sp-tabs section.sel-tab .wp-shortpixel-tab-content {
608
- visibility: visible;
609
  }
610
  article.sp-tabs section:first-child {
611
  z-index: 1;
612
  }
613
- article.sp-tabs section h2 {
614
- position: absolute;
615
- font-size: 1.3em;
616
- font-weight: normal;
617
- width: 180px;
618
- height: 1.8em;
619
- top: -1.8em;
620
- left: 10px;
621
- padding: 0;
622
- margin: 0;
623
- color: #999;
624
- background-color: #ddd;
625
- /* border-radius: 5px 5px 0 0; */
626
- }
627
- article.sp-tabs section:nth-child(2) h2 {
628
- left: 192px;
629
- }
630
- article.sp-tabs section:nth-child(3) h2 {
631
- left: 374px;
632
- }
633
- article.sp-tabs section:nth-child(4) h2 {
634
- left: 556px;
635
- }
636
- article.sp-tabs section:nth-child(5) h2 {
637
- left: 738px;
638
- }
639
- article.sp-tabs section h2 a {
640
- display: block;
641
- width: 100%;
642
- line-height: 1.8em;
643
- text-align: center;
644
- text-decoration: none;
645
- color: #23282d;
646
- outline: 0 none;
647
- }
648
  article.sp-tabs section h2 a:focus,
649
  article.sp-tabs section#tab-resources a:focus {
650
  box-shadow: none;
79
  .wp-core-ui .button.remove-folder-button {
80
  min-width: 120px;
81
  }
82
+ /** In time this is not needed, new notice model **/
83
  .sp-notice {
84
  background: #fff;
85
  border-left: 4px solid #fff;
606
  visibility: hidden;
607
  }
608
  article.sp-tabs section.sel-tab .wp-shortpixel-tab-content {
609
+ visibility: visible !important;
610
  }
611
  article.sp-tabs section:first-child {
612
  z-index: 1;
613
  }
614
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  article.sp-tabs section h2 a:focus,
616
  article.sp-tabs section#tab-resources a:focus {
617
  box-shadow: none;
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}.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)}}
1
+ .reset{font-weight:400;font-style:normal}.clearfix:after,.clearfix:before{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,.2);z-index:1}.sp-dropdown-content a{color:#000;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 #fff;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:700}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:focus,div.shortpixel-rate-us>a:hover{outline:0;border-style:none}.sp-loading-small{margin-top:2px;float:left;margin-right:5px}.twentytwenty-horizontal .twentytwenty-after-label:before,.twentytwenty-horizontal .twentytwenty-before-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:#fff;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:700;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:700;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:700}.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:700;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:700;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:400}.bulk-slider{display:table;margin:0 auto}.bulk-slider .bulk-slide{margin:0 auto;padding-left:120px;display:inline-block;font-weight:700}.bulk-slider .img-optimized,.bulk-slider .img-original{display:inline-block;margin-right:20px;text-align:center}.bulk-slider .img-optimized div,.bulk-slider .img-original div{max-height:450px;overflow:hidden}.bulk-slider .img-optimized img,.bulk-slider .img-original 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:700}.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:700;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,.1)}article.sp-tabs section .wp-shortpixel-tab-content{visibility:hidden}article.sp-tabs section.sel-tab .wp-shortpixel-tab-content{visibility:visible!important}article.sp-tabs section:first-child{z-index:1}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}.deliverWebpAlteringTypes,.deliverWebpSettings,.deliverWebpTypes{display:none}.deliverWebpTypes .sp-notice{color:red}.deliverWebpSettings{margin:16px 0}.deliverWebpSettings input:disabled+label{color:#818181}.deliverWebpAlteringTypes,.deliverWebpTypes{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-alert .cssload-container,#wpadminbar .shortpixel-toolbar-processing.shortpixel-quota-exceeded .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 2s infinite linear;-o-animation:cssload-spin 2s infinite linear;-ms-animation:cssload-spin 2s infinite linear;-webkit-animation:cssload-spin 2s infinite linear;-moz-animation:cssload-spin 2s 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 CHANGED
@@ -1,3 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 {
@@ -23,6 +46,9 @@
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; }
@@ -33,3 +59,53 @@
33
  max-width: 70%; }
34
  #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder input[name="saveAdv"] {
35
  margin-left: 8px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .shortpixel.notice {
2
+ min-height: 50px;
3
+ padding: 8px; }
4
+ .shortpixel.notice img {
5
+ display: inline-block;
6
+ margin: 0 25px 0 0;
7
+ max-height: 50px; }
8
+ .shortpixel.notice .notice-dismiss {
9
+ margin-top: 10px; }
10
+
11
+ /* In-view notice ( not on top, between the options ) - styled after WP notice */
12
+ .view-notice {
13
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
14
+ border: 4px solid #fff;
15
+ padding: 1px 12px; }
16
+ .view-notice p {
17
+ margin: 1em 0 !important; }
18
+ .view-notice.warning {
19
+ border-left-color: #ffb900; }
20
+
21
+ .view-notice-row {
22
+ display: none; }
23
+
24
  .short-pixel-bulk-page.bulk-restore-all ol li {
25
  font-weight: 700; }
26
  .short-pixel-bulk-page.bulk-restore-all section.select_folders {
46
  margin: 10px 0;
47
  margin-right: 8px; }
48
 
49
+ .short-pixel-bulk-page .sp-hidden {
50
+ display: none; }
51
+
52
  /* Specific styles for advanced settings tab */
53
  #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder {
54
  margin: 10px 0; }
59
  max-width: 70%; }
60
  #shortpixel-settings-tabs #tab-adv-settings .addCustomFolder input[name="saveAdv"] {
61
  margin-left: 8px; }
62
+
63
+ .settings_page_wp-shortpixel-settings .top-menu {
64
+ font-size: 18px; }
65
+ .settings_page_wp-shortpixel-settings .top-menu a {
66
+ font-size: 18px; }
67
+ .settings_page_wp-shortpixel-settings .wp-shortpixel-tab-content {
68
+ transition: all 1000ms linear; }
69
+ .settings_page_wp-shortpixel-settings article.sp-tabs section .wp-shortpixel-tab-content {
70
+ opacity: 0; }
71
+ .settings_page_wp-shortpixel-settings article.sp-tabs section.sel-tab .wp-shortpixel-tab-content {
72
+ opacity: 1; }
73
+ .settings_page_wp-shortpixel-settings article.sp-tabs section h2 {
74
+ position: absolute;
75
+ font-size: 1.3em;
76
+ font-weight: normal;
77
+ width: 180px;
78
+ height: 1.8em;
79
+ top: -1.8em;
80
+ left: 10px;
81
+ padding: 0;
82
+ margin: 0;
83
+ color: #999;
84
+ background-color: #ddd;
85
+ /* border-radius: 5px 5px 0 0; */ }
86
+ .settings_page_wp-shortpixel-settings article.sp-tabs section h2 a {
87
+ display: block;
88
+ width: 100%;
89
+ line-height: 1.8em;
90
+ text-align: center;
91
+ text-decoration: none;
92
+ color: #23282d;
93
+ outline: 0 none; }
94
+ .settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(2) h2 {
95
+ left: 192px; }
96
+ .settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(3) h2 {
97
+ left: 374px; }
98
+ .settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(4) h2 {
99
+ left: 556px; }
100
+ .settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(5) h2 {
101
+ left: 738px; }
102
+ .settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(6) h2 {
103
+ left: 920px; }
104
+ .settings_page_wp-shortpixel-settings section#tab-debug .flex {
105
+ display: flex; }
106
+ .settings_page_wp-shortpixel-settings section#tab-debug .env .flex {
107
+ flex-wrap: wrap;
108
+ max-width: 450px; }
109
+ .settings_page_wp-shortpixel-settings section#tab-debug .env .flex span {
110
+ width: 45%;
111
+ padding: 4px; }
res/css/shortpixel-admin.min.css CHANGED
@@ -1 +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}
1
+ .short-pixel-bulk-page.bulk-restore-all ol li{font-weight:700}.short-pixel-bulk-page.bulk-restore-all .random_answer{font-size:16px;font-weight:700;padding:8px;border:1px solid #ccc;display:inline-block}.short-pixel-bulk-page.bulk-restore-all .inputs{margin:15px 0}.short-pixel-bulk-page.bulk-restore-all .inputs span{margin-right:8px}.short-pixel-bulk-page.bulk-restore-all .button{margin:10px 0;margin-right:8px}
res/css/sp-file-tree.min.css CHANGED
@@ -1 +1 @@
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}
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:700}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/notes-sp.png ADDED
Binary file
res/img/notes-sp@2x.png ADDED
Binary file
res/js/shortpixel.js CHANGED
@@ -2,6 +2,7 @@
2
  * Short Pixel WordPress Plugin javascript
3
  */
4
 
 
5
  jQuery(document).ready(function(){ShortPixel.init();});
6
 
7
  var ShortPixel = function() {
@@ -62,9 +63,11 @@ var ShortPixel = function() {
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) {
@@ -81,30 +84,52 @@ var ShortPixel = function() {
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() {
87
 
88
  if(this !== prev) {
89
  prev = this;
90
  }
 
91
  if(typeof ShortPixel.setupGeneralTabAlert !== 'undefined') return;
92
  alert(_spTr.alertOnlyAppliesToNewImages);
93
  ShortPixel.setupGeneralTabAlert = 1;
94
  };
95
  }
 
96
  ShortPixel.enableResize("#resize");
 
97
  jQuery("#resize").change(function(){ enableResize(this); });
98
  jQuery(".resize-sizes").blur(function(e){
99
- var elm = jQuery(this);
100
- if(ShortPixel.resizeSizesAlert == elm.val()) return;
 
 
 
101
  ShortPixel.resizeSizesAlert = elm.val();
102
  var minSize = jQuery("#min-" + elm.attr('name')).val();
103
- if(elm.val() < Math.min(minSize, 1024)) {
 
104
  if(minSize > 1024) {
105
- alert( _spTr.pleaseDoNotSetLesser1024.format(elm.attr('name')) );
106
  } else {
107
- alert( _spTr.pleaseDoNotSetLesserSize.format(elm.attr('name'), elm.attr('name'), minSize) );
108
  }
109
  e.preventDefault();
110
  //elm.val(this.defaultValue);
@@ -130,6 +155,13 @@ var ShortPixel = function() {
130
  }
131
  return true;
132
  });
 
 
 
 
 
 
 
133
  }
134
 
135
  function apiKeyChanged() {
@@ -164,17 +196,18 @@ var ShortPixel = function() {
164
 
165
  function initSettings() {
166
  ShortPixel.adjustSettingsTabs();
 
167
  jQuery( window ).resize(function() {
168
  ShortPixel.adjustSettingsTabs();
169
  });
170
- if(window.location.hash) {
171
  var target = ('tab-' + window.location.hash.substring(window.location.hash.indexOf("#")+1)).replace(/\//, '');
172
  if(jQuery("section#" + target).length) {
173
  ShortPixel.switchSettingsTab( target );
174
  }
175
- }
176
- jQuery("article.sp-tabs a.tab-link").click(function(){
177
- var theID = jQuery(this).data("id");
178
  ShortPixel.switchSettingsTab( theID );
179
  });
180
 
@@ -194,20 +227,33 @@ var ShortPixel = function() {
194
  });
195
  }
196
 
 
197
  function switchSettingsTab(target){
 
198
  var tab = target.replace("tab-",""),
199
  beacon = "",
200
- section = jQuery("section#" +target),
201
- url = location.href.replace(location.hash,"") + '#' + tab;
202
- if(history.pushState) {
203
  history.pushState(null, null, url);
204
  }
205
  else {
206
  location.hash = url;
 
 
 
 
 
 
 
207
  }
 
208
  if(section.length > 0){
209
  jQuery("section").removeClass("sel-tab");
210
- jQuery("section#" +target).addClass("sel-tab");
 
 
 
211
  }
212
  if(typeof HS.beacon.suggest !== 'undefined' ){
213
  switch(tab){
@@ -228,11 +274,12 @@ var ShortPixel = function() {
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);
234
- sectionHeight = Math.max(sectionHeight, jQuery('section#tab-resources .area1').height() + 60);
235
- jQuery('#shortpixel-settings-tabs').css('height', sectionHeight);
236
  //jQuery('#shortpixel-settings-tabs section').css('height', sectionHeight);
237
  }
238
 
@@ -242,7 +289,7 @@ var ShortPixel = function() {
242
  data = JSON.parse(response);
243
  if(data["Status"] == 'success') {
244
  jQuery("#short-pixel-media-alert").hide();
245
- console.log("dismissed");
246
  }
247
  });
248
  }
@@ -504,7 +551,7 @@ var ShortPixel = function() {
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
  }
@@ -773,6 +820,7 @@ var ShortPixel = function() {
773
  displayComparerPopup: displayComparerPopup,
774
  closeComparerPopup : closeComparerPopup,
775
  convertPunycode : convertPunycode,
 
776
  comparerData : {
777
  cssLoaded : false,
778
  jsLoaded : false,
@@ -797,7 +845,7 @@ function showToolBarAlert($status, $message, id) {
797
  }
798
  robo.addClass("shortpixel-alert");
799
  robo.addClass("shortpixel-quota-exceeded");
800
- jQuery("a", robo).attr("href", "options-general.php?page=wp-shortpixel");
801
  //jQuery("a", robo).attr("target", "_blank");
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.");
@@ -813,7 +861,7 @@ function showToolBarAlert($status, $message, id) {
813
  case ShortPixel.STATUS_NO_KEY:
814
  robo.addClass("shortpixel-alert");
815
  robo.addClass("shortpixel-quota-exceeded");
816
- jQuery("a", robo).attr("href", "options-general.php?page=wp-shortpixel");//"http://shortpixel.com/wp-apikey");
817
  //jQuery("a", robo).attr("target", "_blank");
818
  jQuery("a div", robo).attr("title", "Get API Key");
819
  break;
@@ -863,7 +911,6 @@ function checkBulkProgress() {
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);
2
  * Short Pixel WordPress Plugin javascript
3
  */
4
 
5
+ // init checks bulkProcess on each page. initSettings is when the settings View is being loaded.
6
  jQuery(document).ready(function(){ShortPixel.init();});
7
 
8
  var ShortPixel = function() {
63
  jQuery('#request_key').attr('href', jQuery('#request_key').attr('href').split('?')[0] + '?pluginemail=' + email);
64
  }
65
 
66
+ function validateKey(button){
67
+ console.log('validate');
68
  jQuery('#valid').val('validate');
69
+
70
+ jQuery(button).parents('form').submit();
71
  }
72
 
73
  jQuery("#key").keypress(function(e) {
84
  }
85
  }
86
 
87
+
88
+ function checkExifWarning()
89
+ {
90
+ if (! jQuery('input[name="removeExif"]').is(':checked') && jQuery('input[name="png2jpg"]').is(':checked') )
91
+ {
92
+ jQuery('.exif_warning').fadeIn();
93
+ }
94
+ else {
95
+ jQuery('.exif_warning').fadeOut();
96
+ }
97
+ }
98
+
99
+ function setupGeneralTab() {
100
+ var rad = 0;
101
+ if (typeof document.wp_shortpixel_options !== 'undefined')
102
+ rad = document.wp_shortpixel_options.compressionType;
103
  for(var i = 0, prev = null; i < rad.length; i++) {
104
  rad[i].onclick = function() {
105
 
106
  if(this !== prev) {
107
  prev = this;
108
  }
109
+ // Warns once that changing compressType is only for new images.
110
  if(typeof ShortPixel.setupGeneralTabAlert !== 'undefined') return;
111
  alert(_spTr.alertOnlyAppliesToNewImages);
112
  ShortPixel.setupGeneralTabAlert = 1;
113
  };
114
  }
115
+
116
  ShortPixel.enableResize("#resize");
117
+
118
  jQuery("#resize").change(function(){ enableResize(this); });
119
  jQuery(".resize-sizes").blur(function(e){
120
+ var elm = jQuery(e.target);
121
+
122
+ if(ShortPixel.resizeSizesAlert == elm.val())
123
+ return; // returns if check in progress, presumed.
124
+
125
  ShortPixel.resizeSizesAlert = elm.val();
126
  var minSize = jQuery("#min-" + elm.attr('name')).val();
127
+ var niceName = jQuery("#min-" + elm.attr('name')).data('nicename');
128
+ if(elm.val() < Math.min(minSize, 1024)) { // @todo is this correct? This will always be < 1024, and give first error
129
  if(minSize > 1024) {
130
+ alert( _spTr.pleaseDoNotSetLesser1024.format(niceName) );
131
  } else {
132
+ alert( _spTr.pleaseDoNotSetLesserSize.format(niceName, niceName, minSize) );
133
  }
134
  e.preventDefault();
135
  //elm.val(this.defaultValue);
155
  }
156
  return true;
157
  });
158
+
159
+ jQuery('input[name="removeExif"], input[name="png2jpg"]').on('change', function()
160
+ {
161
+ ShortPixel.checkExifWarning();
162
+ });
163
+ ShortPixel.checkExifWarning();
164
+
165
  }
166
 
167
  function apiKeyChanged() {
196
 
197
  function initSettings() {
198
  ShortPixel.adjustSettingsTabs();
199
+ ShortPixel.setupGeneralTab(); // certain alerts.
200
  jQuery( window ).resize(function() {
201
  ShortPixel.adjustSettingsTabs();
202
  });
203
+ /*if(window.location.hash) {
204
  var target = ('tab-' + window.location.hash.substring(window.location.hash.indexOf("#")+1)).replace(/\//, '');
205
  if(jQuery("section#" + target).length) {
206
  ShortPixel.switchSettingsTab( target );
207
  }
208
+ } */
209
+ jQuery("article.sp-tabs a.tab-link").click(function(e){
210
+ var theID = jQuery(e.target).data("id");
211
  ShortPixel.switchSettingsTab( theID );
212
  });
213
 
227
  });
228
  }
229
 
230
+ // Switch between settings tabs.
231
  function switchSettingsTab(target){
232
+
233
  var tab = target.replace("tab-",""),
234
  beacon = "",
235
+ section = jQuery("section#" +target);
236
+ // url = location.href.replace(location.hash,"") + '#' + tab;
237
+ /*if(history.pushState) {
238
  history.pushState(null, null, url);
239
  }
240
  else {
241
  location.hash = url;
242
+ } */
243
+ jQuery('input[name="display_part"]').val(tab);
244
+ var uri = window.location.href.toString();
245
+ if (uri.indexOf("?") > 0) {
246
+ var clean_uri = uri.substring(0, uri.indexOf("?"));
247
+ clean_uri += '?' + jQuery.param({'page':'wp-shortpixel-settings', 'part': tab});
248
+ window.history.replaceState({}, document.title, clean_uri);
249
  }
250
+
251
  if(section.length > 0){
252
  jQuery("section").removeClass("sel-tab");
253
+ jQuery('section .wp-shortpixel-tab-content').fadeOut(50);
254
+ jQuery(section).addClass("sel-tab");
255
+ ShortPixel.adjustSettingsTabs();
256
+ jQuery(section).find('.wp-shortpixel-tab-content').fadeIn(50);
257
  }
258
  if(typeof HS.beacon.suggest !== 'undefined' ){
259
  switch(tab){
274
  }
275
  }
276
 
277
+ // Fixes the height of the current active tab.
278
  function adjustSettingsTabsHeight(){
279
+ var sectionHeight = jQuery('section.sel-tab').height() + 90;
280
+ //sectionHeight = Math.max(sectionHeight, jQuery('section#tab-adv-settings .wp-shortpixel-options').height() + 20);
281
+ // sectionHeight = Math.max(sectionHeight, jQuery('section#tab-resources .area1').height() + 60);
282
+ jQuery('.section-wrapper').css('height', sectionHeight);
283
  //jQuery('#shortpixel-settings-tabs section').css('height', sectionHeight);
284
  }
285
 
289
  data = JSON.parse(response);
290
  if(data["Status"] == 'success') {
291
  jQuery("#short-pixel-media-alert").hide();
292
+ //console.log("dismissed");
293
  }
294
  });
295
  }
551
  jQuery("#addCustomFolderView").val(fullPath);
552
  jQuery(".sp-folder-picker-shade").fadeOut(100);
553
  jQuery(".shortpixel-modal.modal-folder-picker").css("display", "none");
554
+ jQuery('#saveAdvAddFolder').removeClass('hidden');
555
  } else {
556
  alert("Please select a folder from the list.");
557
  }
820
  displayComparerPopup: displayComparerPopup,
821
  closeComparerPopup : closeComparerPopup,
822
  convertPunycode : convertPunycode,
823
+ checkExifWarning : checkExifWarning,
824
  comparerData : {
825
  cssLoaded : false,
826
  jsLoaded : false,
845
  }
846
  robo.addClass("shortpixel-alert");
847
  robo.addClass("shortpixel-quota-exceeded");
848
+ jQuery("a", robo).attr("href", "options-general.php?page=wp-shortpixel-settings");
849
  //jQuery("a", robo).attr("target", "_blank");
850
  //jQuery("a div", robo).attr("title", "ShortPixel quota exceeded. Click to top-up");
851
  jQuery("a div", robo).attr("title", "ShortPixel quota exceeded. Click for details.");
861
  case ShortPixel.STATUS_NO_KEY:
862
  robo.addClass("shortpixel-alert");
863
  robo.addClass("shortpixel-quota-exceeded");
864
+ jQuery("a", robo).attr("href", "options-general.php?page=wp-shortpixel-settings");//"http://shortpixel.com/wp-apikey");
865
  //jQuery("a", robo).attr("target", "_blank");
866
  jQuery("a div", robo).attr("title", "Get API Key");
867
  break;
911
 
912
  first = false; //rearm replacer
913
  var adminUrl = ShortPixel.WP_ADMIN_URL.toLowerCase().replace(/\/\//g , replacer);
 
914
  //handle possible Punycode domain names.
915
  if(url.search(adminUrl) < 0) {
916
  url = ShortPixel.convertPunycode(url);
res/js/shortpixel.min.js CHANGED
@@ -1 +1 @@
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}};
1
+ function showToolBarAlert(e,r,t){var o=jQuery("li.shortpixel-toolbar-processing");switch(e){case ShortPixel.STATUS_QUOTA_EXCEEDED:if(window.location.href.search("wp-short-pixel-bulk")>0&&0==jQuery(".sp-quota-exceeded-alert").length)return void location.reload();o.addClass("shortpixel-alert"),o.addClass("shortpixel-quota-exceeded"),jQuery("a",o).attr("href","options-general.php?page=wp-shortpixel-settings"),jQuery("a div",o).attr("title","ShortPixel quota exceeded. Click for details.");break;case ShortPixel.STATUS_SKIP:case ShortPixel.STATUS_FAIL:o.addClass("shortpixel-alert shortpixel-processing"),jQuery("a div",o).attr("title",r),void 0!==t&&jQuery("a",o).attr("href","post.php?post="+t+"&action=edit");break;case ShortPixel.STATUS_NO_KEY:o.addClass("shortpixel-alert"),o.addClass("shortpixel-quota-exceeded"),jQuery("a",o).attr("href","options-general.php?page=wp-shortpixel-settings"),jQuery("a div",o).attr("title","Get API Key");break;case ShortPixel.STATUS_SUCCESS:case ShortPixel.STATUS_RETRY:o.addClass("shortpixel-processing"),o.removeClass("shortpixel-alert"),jQuery("a",o).removeAttr("target"),jQuery("a",o).attr("href",jQuery("a img",o).attr("success-url"))}o.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(){"undefined"!=typeof shortPixelQuotaExceeded&&(1==shortPixelQuotaExceeded?showToolBarAlert(ShortPixel.STATUS_QUOTA_EXCEEDED):hideQuotaExceededToolBarAlert())}function checkBulkProgress(){var e=function(e){return r?"/":(r=!0,e)},r=!1,t=window.location.href.toLowerCase().replace(/\/\//g,e);r=!1;var o=ShortPixel.WP_ADMIN_URL.toLowerCase().replace(/\/\//g,e);t.search(o)<0&&(t=ShortPixel.convertPunycode(t),o=ShortPixel.convertPunycode(o)),t.search(o+"upload.php")<0&&t.search(o+"edit.php")<0&&t.search(o+"edit-tags.php")<0&&t.search(o+"post-new.php")<0&&t.search(o+"post.php")<0&&t.search("page=nggallery-manage-gallery")<0&&(0==ShortPixel.FRONT_BOOTSTRAP||0==t.search(o))?hideToolBarAlert():(1==ShortPixel.bulkProcessor&&window.location.href.search("wp-short-pixel-bulk")<0&&void 0!==localStorage.bulkPage&&localStorage.bulkPage>0&&(ShortPixel.bulkProcessor=!1),window.location.href.search("wp-short-pixel-bulk")>=0&&(ShortPixel.bulkProcessor=!0,localStorage.bulkTime=Math.floor(Date.now()/1e3),localStorage.bulkPage=1),1==ShortPixel.bulkProcessor||void 0===localStorage.bulkTime||Math.floor(Date.now()/1e3)-localStorage.bulkTime>90?(ShortPixel.bulkProcessor=!0,localStorage.bulkPage=window.location.href.search("wp-short-pixel-bulk")>=0?1:0,localStorage.bulkTime=Math.floor(Date.now()/1e3),console.log(localStorage.bulkTime),checkBulkProcessingCallApi()):setTimeout(checkBulkProgress,5e3))}function checkBulkProcessingCallApi(){jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:{action:"shortpixel_image_processing"},success:function(e){if(e.length>0){r=null;try{var r=JSON.parse(e)}catch(e){return void ShortPixel.retry(e.message)}ShortPixel.retries=0;var t=r.ImageID,o=jQuery("div.short-pixel-bulk-page").length>0;switch(r.Status){case ShortPixel.STATUS_NO_KEY:setCellMessage(t,r.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(t,r.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),0==r.Stop&&setTimeout(checkBulkProgress,5e3),ShortPixel.otherMediaUpdateActions(t,["quota","view"]);break;case ShortPixel.STATUS_FAIL:setCellMessage(t,r.Message,"<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('"+t+"', false)\">"+_spTr.retry+"</a>"),showToolBarAlert(ShortPixel.STATUS_FAIL,r.Message,t),o&&(ShortPixel.bulkShowError(t,r.Message,r.Filename,r.CustomImageLink),r.BulkPercent&&progressUpdate(r.BulkPercent,r.BulkMsg),ShortPixel.otherMediaUpdateActions(t,["retry","view"])),console.log(r.Message),setTimeout(checkBulkProgress,5e3);break;case ShortPixel.STATUS_EMPTY_QUEUE:console.log(r.Message),clearBulkProcessor(),hideToolBarAlert();var s=jQuery("#bulk-progress");o&&s.length&&"2"!=r.BulkStatus&&(progressUpdate(100,"Bulk finished!"),jQuery("a.bulk-cancel").attr("disabled","disabled"),hideSlider(),setTimeout(function(){window.location.reload()},3e3));break;case ShortPixel.STATUS_SUCCESS:o&&(ShortPixel.bulkHideLengthyMsg(),ShortPixel.bulkHideMaintenanceMsg());var i=r.PercentImprovement;showToolBarAlert(ShortPixel.STATUS_SUCCESS,"");var a=ShortPixel.isCustomImageId(t)?"":ShortPixel.successActions(t,r.Type,r.ThumbsCount,r.ThumbsTotal,r.BackupEnabled,r.Filename);setCellMessage(t,ShortPixel.successMsg(t,i,r.Type,r.ThumbsCount,r.RetinasCount),a),jQuery("#post-"+t).length>0&&jQuery("#post-"+t).find(".filename").text(r.Filename),jQuery(".misc-pub-filename strong").length>0&&jQuery(".misc-pub-filename strong").text(r.Filename),ShortPixel.isCustomImageId(t)&&r.TsOptimized&&r.TsOptimized.length>0&&(console.log(t),jQuery(".date-"+t).text(r.TsOptimized));var l=jQuery(["restore","view","redolossy","redoglossy","redolossless"]).not(["redo"+r.Type]).get();ShortPixel.otherMediaUpdateActions(t,l);new PercentageAnimator("#sp-msg-"+t+" span.percent",i).animate(i),o&&void 0!==r.Thumb&&(r.BulkPercent&&progressUpdate(r.BulkPercent,r.BulkMsg),r.Thumb.length>0&&(sliderUpdate(t,r.Thumb,r.BkThumb,r.PercentImprovement,r.Filename),void 0!==r.AverageCompression&&0+r.AverageCompression>0&&(jQuery("#sp-avg-optimization").html('<input type="text" class="dial" value="'+Math.round(r.AverageCompression)+'"/>'),ShortPixel.percentDial("#sp-avg-optimization .dial",60)))),console.log("Server response: "+e),o&&void 0!==r.BulkPercent&&progressUpdate(r.BulkPercent,r.BulkMsg),setTimeout(checkBulkProgress,5e3);break;case ShortPixel.STATUS_SKIP:1!==r.Silent&&ShortPixel.bulkShowError(t,r.Message,r.Filename,r.CustomImageLink);case ShortPixel.STATUS_ERROR:void 0!==r.Message&&(showToolBarAlert(ShortPixel.STATUS_SKIP,r.Message+" Image ID: "+t),setCellMessage(t,r.Message,"")),ShortPixel.otherMediaUpdateActions(t,["retry","view"]);case ShortPixel.STATUS_RETRY:console.log("Server response: "+e),showToolBarAlert(ShortPixel.STATUS_RETRY,""),o&&void 0!==r.BulkPercent&&progressUpdate(r.BulkPercent,r.BulkMsg),o&&r.Count>3&&ShortPixel.bulkShowLengthyMsg(t,r.Filename,r.CustomImageLink),setTimeout(checkBulkProgress,5e3);break;case ShortPixel.STATUS_MAINTENANCE:ShortPixel.bulkShowMaintenanceMsg("maintenance"),setTimeout(checkBulkProgress,6e4);break;case ShortPixel.STATUS_QUEUE_FULL:ShortPixel.bulkShowMaintenanceMsg("queue-full"),setTimeout(checkBulkProgress,6e4);break;default:ShortPixel.retry("Unknown status "+r.Status+". Retrying...")}}},error:function(e){ShortPixel.retry(e.statusText)}})}function clearBulkProcessor(){ShortPixel.bulkProcessor=!1,localStorage.bulkTime=0,window.location.href.search("wp-short-pixel-bulk")>=0&&(localStorage.bulkPage=0)}function setCellMessage(e,r,t){var o=jQuery("#sp-msg-"+e);o.length>0&&(o.html("<div class='sp-column-actions'>"+t+"</div><div class='sp-column-info'>"+r+"</div>"),o.css("color","")),(o=jQuery("#sp-cust-msg-"+e)).length>0&&o.html("<div class='sp-column-info'>"+r+"</div>")}function manualOptimization(e,r){setCellMessage(e,"<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 t={action:"shortpixel_manual_optimization",image_id:e,cleanup:r};jQuery.ajax({type:"GET",url:ShortPixel.AJAX_URL,data:t,success:function(r){var t=JSON.parse(r);t.Status==ShortPixel.STATUS_SUCCESS?setTimeout(checkBulkProgress,2e3):setCellMessage(e,void 0!==t.Message?t.Message:_spTr.thisContentNotProcessable,"")},error:function(r){t.action="shortpixel_check_status",jQuery.ajax({type:"GET",url:ShortPixel.AJAX_URL,data:t,success:function(r){var t=JSON.parse(r);t.Status!==ShortPixel.STATUS_SUCCESS&&setCellMessage(e,void 0!==t.Message?t.Message:_spTr.thisContentNotProcessable,"")}})}})}function reoptimize(e,r){setCellMessage(e,"<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 t={action:"shortpixel_redo",attachment_ID:e,type:r};jQuery.get(ShortPixel.AJAX_URL,t,function(r){(t=JSON.parse(r)).Status==ShortPixel.STATUS_SUCCESS?setTimeout(checkBulkProgress,2e3):($msg=void 0!==t.Message?t.Message:_spTr.thisContentNotProcessable,setCellMessage(e,$msg,""),showToolBarAlert(ShortPixel.STATUS_FAIL,$msg))})}function optimizeThumbs(e){setCellMessage(e,"<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 r={action:"shortpixel_optimize_thumbs",attachment_ID:e};jQuery.get(ShortPixel.AJAX_URL,r,function(t){(r=JSON.parse(t)).Status==ShortPixel.STATUS_SUCCESS?setTimeout(checkBulkProgress,2e3):setCellMessage(e,void 0!==r.Message?r.Message:_spTr.thisContentNotProcessable,"")})}function dismissShortPixelNoticeExceed(e){jQuery("#wp-admin-bar-shortpixel_processing").hide();var r={action:"shortpixel_dismiss_notice",notice_id:"exceed"};jQuery.get(ShortPixel.AJAX_URL,r,function(e){(r=JSON.parse(e)).Status==ShortPixel.STATUS_SUCCESS&&console.log("dismissed")}),e.preventDefault()}function dismissShortPixelNotice(e){jQuery("#short-pixel-notice-"+e).hide();var r={action:"shortpixel_dismiss_notice",notice_id:e};jQuery.get(ShortPixel.AJAX_URL,r,function(e){(r=JSON.parse(e)).Status==ShortPixel.STATUS_SUCCESS&&console.log("dismissed")})}function PercentageAnimator(e,r){this.animationSpeed=10,this.increment=2,this.curPercentage=0,this.targetPercentage=r,this.outputSelector=e,this.animate=function(e){this.targetPercentage=e,setTimeout(PercentageTimer.bind(null,this),this.animationSpeed)}}function PercentageTimer(e){e.curPercentage-e.targetPercentage<-e.increment?e.curPercentage+=e.increment:e.curPercentage-e.targetPercentage>e.increment?e.curPercentage-=e.increment:e.curPercentage=e.targetPercentage,jQuery(e.outputSelector).text(e.curPercentage+"%"),e.curPercentage!=e.targetPercentage&&setTimeout(PercentageTimer.bind(null,e),e.animationSpeed)}function progressUpdate(e,r){var t=jQuery("#bulk-progress");t.length&&(jQuery(".progress-left",t).css("width",e+"%"),jQuery(".progress-img",t).css("left",e+"%"),e>24?(jQuery(".progress-img span",t).html(""),jQuery(".progress-left",t).html(e+"%")):(jQuery(".progress-img span",t).html(e+"%"),jQuery(".progress-left",t).html("")),jQuery(".bulk-estimate").html(r))}function sliderUpdate(e,r,t,o,s){var i=jQuery(".bulk-slider div.bulk-slide:first-child");if(0!==i.length){"empty-slide"!=i.attr("id")&&i.hide(),i.css("z-index",1e3),jQuery(".bulk-img-opt",i).attr("src",""),void 0===t&&(t=""),t.length>0&&jQuery(".bulk-img-orig",i).attr("src","");var a=i.clone();a.attr("id","slide-"+e),jQuery(".bulk-img-opt",a).attr("src",r),t.length>0?(jQuery(".img-original",a).css("display","inline-block"),jQuery(".bulk-img-orig",a).attr("src",t)):jQuery(".img-original",a).css("display","none"),jQuery(".bulk-opt-percent",a).html('<input type="text" class="dial" value="'+o+'"/>'),jQuery(".bulk-slider").append(a),ShortPixel.percentDial("#"+a.attr("id")+" .dial",100),jQuery(".bulk-slider-container span.filename").html("&nbsp;&nbsp;"+s),"empty-slide"==i.attr("id")?(i.remove(),jQuery(".bulk-slider-container").css("display","block")):i.animate({left:i.width()+i.position().left},"slow","swing",function(){i.remove(),a.fadeIn("slow")})}}function hideSlider(){jQuery(".bulk-slider-container").css("display","none")}function showStats(){jQuery(".bulk-stats").length}jQuery(document).ready(function(){ShortPixel.init()});var ShortPixel=function(){function e(e){jQuery(e).is(":checked")?jQuery("#width,#height").removeAttr("disabled"):jQuery("#width,#height").attr("disabled","disabled")}return jQuery("#key").keypress(function(e){13==e.which&&jQuery("#valid").val("validate")}),{init:function(){void 0===ShortPixel.API_KEY&&(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]),jQuery("#backup-folder-size").length&&jQuery("#backup-folder-size").html(ShortPixel.getBackupSize()),"todo"==ShortPixel.MEDIA_ALERT&&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(){1==ShortPixel.bulkProcessor&&clearBulkProcessor()}),checkQuotaExceededAlert(),checkBulkProgress())},setOptions:function(e){for(var r in e)ShortPixel[r]=e[r]},isEmailValid:function(e){return/^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,63})+$/.test(e)},updateSignupEmail:function(){var e=jQuery("#pluginemail").val();ShortPixel.isEmailValid(e)&&jQuery("#request_key").removeClass("disabled"),jQuery("#request_key").attr("href",jQuery("#request_key").attr("href").split("?")[0]+"?pluginemail="+e)},validateKey:function(e){console.log("validate"),jQuery("#valid").val("validate"),jQuery(e).parents("form").submit()},enableResize:e,setupGeneralTab:function(){var r=0;void 0!==document.wp_shortpixel_options&&(r=document.wp_shortpixel_options.compressionType);for(var t=0,o=null;t<r.length;t++)r[t].onclick=function(){this!==o&&(o=this),void 0===ShortPixel.setupGeneralTabAlert&&(alert(_spTr.alertOnlyAppliesToNewImages),ShortPixel.setupGeneralTabAlert=1)};ShortPixel.enableResize("#resize"),jQuery("#resize").change(function(){e(this)}),jQuery(".resize-sizes").blur(function(e){var r=jQuery(e.target);if(ShortPixel.resizeSizesAlert!=r.val()){ShortPixel.resizeSizesAlert=r.val();var t=jQuery("#min-"+r.attr("name")).val(),o=jQuery("#min-"+r.attr("name")).data("nicename");r.val()<Math.min(t,1024)?(t>1024?alert(_spTr.pleaseDoNotSetLesser1024.format(o)):alert(_spTr.pleaseDoNotSetLesserSize.format(o,o,t)),e.preventDefault(),r.focus()):this.defaultValue=r.val()}}),jQuery(".shortpixel-confirm").click(function(e){return!!confirm(e.target.getAttribute("data-confirm"))||(e.preventDefault(),!1)}),jQuery('input[name="removeExif"], input[name="png2jpg"]').on("change",function(){ShortPixel.checkExifWarning()}),ShortPixel.checkExifWarning()},apiKeyChanged:function(){jQuery(".wp-shortpixel-options .shortpixel-key-valid").css("display","none"),jQuery(".wp-shortpixel-options button#validate").css("display","inline-block")},setupAdvancedTab:function(){jQuery("input.remove-folder-button").click(function(){var e=jQuery(this).data("value");1==confirm(_spTr.areYouSureStopOptimizing.format(e))&&(jQuery("#removeFolder").val(e),jQuery("#wp_shortpixel_options").submit())}),jQuery("input.recheck-folder-button").click(function(){var e=jQuery(this).data("value");1==confirm(_spTr.areYouSureStopOptimizing.format(e))&&(jQuery("#recheckFolder").val(e),jQuery("#wp_shortpixel_options").submit())})},checkThumbsUpdTotal:function(e){var r=jQuery("#"+(e.checked?"total":"main")+"ToProcess").val();jQuery("div.bulk-play span.total").text(r),jQuery("#displayTotal").text(r)},initSettings:function(){ShortPixel.adjustSettingsTabs(),ShortPixel.setupGeneralTab(),jQuery(window).resize(function(){ShortPixel.adjustSettingsTabs()}),jQuery("article.sp-tabs a.tab-link").click(function(e){var r=jQuery(e.target).data("id");ShortPixel.switchSettingsTab(r)}),jQuery("input[type=radio][name=deliverWebpType]").change(function(){"deliverWebpAltered"==this.value?window.confirm(_spTr.alertDeliverWebPAltered)?0==jQuery("input[type=radio][name=deliverWebpAlteringType]:checked").length&&jQuery("#deliverWebpAlteredWP").prop("checked",!0):jQuery(this).prop("checked",!1):"deliverWebpUnaltered"==this.value&&window.alert(_spTr.alertDeliverWebPUnaltered)})},switchSettingsTab:function(e){var r=e.replace("tab-",""),t="",o=jQuery("section#"+e);jQuery('input[name="display_part"]').val(r);var s=window.location.href.toString();if(s.indexOf("?")>0){var i=s.substring(0,s.indexOf("?"));i+="?"+jQuery.param({page:"wp-shortpixel-settings",part:r}),window.history.replaceState({},document.title,i)}if(o.length>0&&(jQuery("section").removeClass("sel-tab"),jQuery("section .wp-shortpixel-tab-content").fadeOut(50),jQuery(o).addClass("sel-tab"),ShortPixel.adjustSettingsTabs(),jQuery(o).find(".wp-shortpixel-tab-content").fadeIn(50)),void 0!==HS.beacon.suggest){switch(r){case"settings":t=shortpixel_suggestions_settings;break;case"adv-settings":t=shortpixel_suggestions_adv_settings;break;case"cloudflare":case"stats":t=shortpixel_suggestions_cloudflare}HS.beacon.suggest(t)}},adjustSettingsTabs:function(){var e=jQuery("section.sel-tab").height()+90;jQuery(".section-wrapper").css("height",e)},onBulkThumbsCheck:function(e){e.checked?(jQuery("#with-thumbs").css("display","inherit"),jQuery("#without-thumbs").css("display","none")):(jQuery("#without-thumbs").css("display","inherit"),jQuery("#with-thumbs").css("display","none"))},dismissMediaAlert:function(){var e={action:"shortpixel_dismiss_media_alert"};jQuery.get(ShortPixel.AJAX_URL,e,function(r){"success"==(e=JSON.parse(r)).Status&&jQuery("#short-pixel-media-alert").hide()})},checkQuota:function(){jQuery.get(ShortPixel.AJAX_URL,{action:"shortpixel_check_quota"},function(){console.log("quota refreshed")})},percentDial:function(e,r){jQuery(e).knob({readOnly:!0,width:r,height:r,fgColor:"#1CAECB",format:function(e){return e+"%"}})},successMsg:function(e,r,t,o,s){return(r>0?"<div class='sp-column-info'>"+_spTr.reducedBy+" <strong><span class='percent'>"+r+"%</span></strong> ":"")+(r>0&&r<5?"<br>":"")+(r<5?_spTr.bonusProcessing:"")+(t.length>0?" ("+t+")":"")+(0+o>0?"<br>"+_spTr.plusXthumbsOpt.format(o):"")+(0+s>0?"<br>"+_spTr.plusXretinasOpt.format(s):"")+"</div>"},successActions:function(e,r,t,o,s,i){if(1==s){var a=jQuery(".sp-column-actions-template").clone();if(!a.length)return!1;var l;return l=0==r.length?["lossy","lossless"]:["lossy","glossy","lossless"].filter(function(e){return!(e==r)}),a.html(a.html().replace(/__SP_ID__/g,e)),"pdf"==i.substr(i.lastIndexOf(".")+1).toLowerCase()&&jQuery(".sp-action-compare",a).remove(),0==t&&o>0?a.html(a.html().replace("__SP_THUMBS_TOTAL__",o)):(jQuery(".sp-action-optimize-thumbs",a).remove(),jQuery(".sp-dropbtn",a).removeClass("button-primary")),a.html(a.html().replace(/__SP_FIRST_TYPE__/g,l[0])),a.html(a.html().replace(/__SP_SECOND_TYPE__/g,l[1])),a.html()}return""},otherMediaUpdateActions:function(e,r){if(e=e.substring(2),jQuery(".shortpixel-other-media").length){for(var t=["optimize","retry","restore","redo","quota","view"],o=0,s=t.length;o<s;o++)jQuery("#"+t[o]+"_"+e).css("display","none");for(var o=0,s=r.length;o<s;o++)jQuery("#"+r[o]+"_"+e).css("display","")}},retry:function(e){ShortPixel.retries++,isNaN(ShortPixel.retries)&&(ShortPixel.retries=1),ShortPixel.retries<6?(console.log("Invalid response from server (Error: "+e+"). Retrying pass "+(ShortPixel.retries+1)+"..."),setTimeout(checkBulkProgress,5e3)):(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: "+e+")",""),console.log("Invalid response from server 6 times. Giving up."))},initFolderSelector:function(){jQuery(".select-folder-button").click(function(){jQuery(".sp-folder-picker-shade").fadeIn(100),jQuery(".shortpixel-modal.modal-folder-picker").show();var e=jQuery(".sp-folder-picker");e.parent().css("margin-left",-e.width()/2),e.fileTree({script:ShortPixel.browseContent,multiFolder:!1})}),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(e){if(r=jQuery("UL.jqueryFileTree LI.directory.selected"),0==jQuery(r).length)var r=jQuery("UL.jqueryFileTree LI.selected").parents(".directory");var t=jQuery(r).children("a").attr("rel");if(void 0!==t)if(t=t.trim()){var o=jQuery("#customFolderBase").val()+t;"/"==o.slice(-1)&&(o=o.slice(0,-1)),jQuery("#addCustomFolder").val(o),jQuery("#addCustomFolderView").val(o),jQuery(".sp-folder-picker-shade").fadeOut(100),jQuery(".shortpixel-modal.modal-folder-picker").css("display","none"),jQuery("#saveAdvAddFolder").removeClass("hidden")}else alert("Please select a folder from the list.")})},browseContent:function(e){e.action="shortpixel_browse_content";var r="";return jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:e,success:function(e){r=e},async:!1}),r},getBackupSize:function(){var e="";return jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:{action:"shortpixel_get_backup_size"},success:function(r){e=r},async:!1}),e},newApiKey:function(e){if(!jQuery("#tos").is(":checked"))return e.preventDefault(),jQuery("#tos-robo").fadeIn(400,function(){jQuery("#tos-hand").fadeIn()}),void jQuery("#tos").click(function(){jQuery("#tos-robo").css("display","none"),jQuery("#tos-hand").css("display","none")});if(jQuery("#request_key").addClass("disabled"),jQuery("#pluginemail_spinner").addClass("is-active"),ShortPixel.updateSignupEmail(),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:!1,url:ShortPixel.AJAX_URL,data:r,success:function(r){data=JSON.parse(r),"success"==data.Status?(e.preventDefault(),window.location.reload()):"invalid"==data.Status&&(jQuery("#pluginemail-error").html("<b>"+data.Details+"</b>"),jQuery("#pluginemail-error").css("display",""),jQuery("#pluginemail-info").css("display","none"),e.preventDefault())}}),jQuery("#request_key").removeAttr("onclick")}else jQuery("#pluginemail-error").css("display",""),jQuery("#pluginemail-info").css("display","none"),e.preventDefault();jQuery("#request_key").removeClass("disabled"),jQuery("#pluginemail_spinner").removeClass("is-active")},proposeUpgrade:function(){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"),jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:{action:"shortpixel_propose_upgrade"},success:function(e){jQuery("#shortPixelProposeUpgrade .sp-modal-body").removeClass("sptw-modal-spinner"),jQuery("#shortPixelProposeUpgrade .sp-modal-body").html(e)}})},closeProposeUpgrade:function(){jQuery("#shortPixelProposeUpgradeShade").css("display","none"),jQuery("#shortPixelProposeUpgrade").addClass("shortpixel-hide"),ShortPixel.toRefresh&&ShortPixel.recheckQuota()},includeUnlisted:function(){jQuery("#short-pixel-notice-unlisted").hide(),jQuery("#optimizeUnlisted").prop("checked",!0);var e={action:"shortpixel_dismiss_notice",notice_id:"unlisted",notice_data:"true"};jQuery.get(ShortPixel.AJAX_URL,e,function(r){(e=JSON.parse(r)).Status==ShortPixel.STATUS_SUCCESS&&console.log("dismissed")})},bulkShowLengthyMsg:function(e,r,t){var o=jQuery(".bulk-notice-msg.bulk-lengthy");if(0!=o.length){var s=jQuery("a",o);s.text(r),t?s.attr("href",t):s.attr("href",s.data("href").replace("__ID__",e)),o.css("display","block")}},bulkHideLengthyMsg:function(){jQuery(".bulk-notice-msg.bulk-lengthy").css("display","none")},bulkShowMaintenanceMsg:function(e){var r=jQuery(".bulk-notice-msg.bulk-"+e);0!=r.length&&r.css("display","block")},bulkHideMaintenanceMsg:function(e){jQuery(".bulk-notice-msg.bulk-"+e).css("display","none")},bulkShowError:function(e,r,t,o){var s=jQuery("#bulk-error-template");if(0!=s.length){var i=s.clone();i.attr("id","bulk-error-"+e),-1==e?(jQuery("span.sp-err-title",i).remove(),i.addClass("bulk-error-fatal")):(jQuery("img",i).remove(),jQuery("#bulk-error-".id).remove()),jQuery("span.sp-err-content",i).html(r);var a=jQuery("a.sp-post-link",i);o?a.attr("href",o):a.attr("href",a.attr("href").replace("__ID__",e)),a.text(t),s.after(i),i.css("display","block")}},confirmBulkAction:function(e,r){return!!confirm(_spTr["confirmBulk"+e])||(r.stopPropagation(),r.preventDefault(),!1)},checkRandomAnswer:function(e){var r=jQuery(e.target).val(),t=jQuery('input[name="random_answer"]').val(),o=jQuery('input[name="random_answer"]').data("target");r==t?(jQuery(o).removeClass("disabled").prop("disabled",!1),jQuery(o).removeAttr("aria-disabled")):jQuery(o).addClass("disabled").prop("disabled",!0)},removeBulkMsg:function(e){jQuery(e).parent().parent().remove()},isCustomImageId:function(e){return"C-"==e.substring(0,2)},recheckQuota:function(){var e=window.location.href.split("#");window.location.href=e[0]+(e[0].indexOf("?")>0?"&":"?")+"checkquota=1"+(void 0===e[1]?"":"#"+e[1])},openImageMenu:function(e){e.preventDefault(),this.menuCloseEvent||(jQuery(window).click(function(e){e.target.matches(".sp-dropbtn")||jQuery(".sp-dropdown.sp-show").removeClass("sp-show")}),this.menuCloseEvent=!0);var r=e.target.parentElement.classList.contains("sp-show");jQuery(".sp-dropdown.sp-show").removeClass("sp-show"),r||e.target.parentElement.classList.add("sp-show")},menuCloseEvent:!1,loadComparer:function(e){this.comparerData.origUrl=!1,!1===this.comparerData.cssLoaded&&(jQuery("<link>").appendTo("head").attr({type:"text/css",rel:"stylesheet",href:this.WP_PLUGIN_URL+"/res/css/twentytwenty.min.css"}),this.comparerData.cssLoaded=2),!1===this.comparerData.jsLoaded&&(jQuery.getScript(this.WP_PLUGIN_URL+"/res/js/jquery.twentytwenty.min.js",function(){ShortPixel.comparerData.jsLoaded=2,ShortPixel.comparerData.origUrl.length>0&&ShortPixel.displayComparerPopup(ShortPixel.comparerData.width,ShortPixel.comparerData.height,ShortPixel.comparerData.origUrl,ShortPixel.comparerData.optUrl)}),this.comparerData.jsLoaded=1),!1===this.comparerData.origUrl&&(jQuery.ajax({type:"POST",url:ShortPixel.AJAX_URL,data:{action:"shortpixel_get_comparer_data",id:e},success:function(e){data=JSON.parse(e),jQuery.extend(ShortPixel.comparerData,data),2==ShortPixel.comparerData.jsLoaded&&ShortPixel.displayComparerPopup(ShortPixel.comparerData.width,ShortPixel.comparerData.height,ShortPixel.comparerData.origUrl,ShortPixel.comparerData.optUrl)}}),this.comparerData.origUrl="")},displayComparerPopup:function(e,r,t,o){var s=e,i=r<150||e<350,a=jQuery(i?"#spUploadCompareSideBySide":"#spUploadCompare"),l=jQuery(".sp-modal-shade");i||jQuery("#spCompareSlider").html('<img class="spUploadCompareOriginal"/><img class="spUploadCompareOptimized"/>'),e=Math.max(350,Math.min(800,e<350?2*(e+25):r<150?e+25:e)),r=Math.max(150,i?s>350?2*(r+45):r+45:r*e/s);var n="-"+Math.round(e/2);jQuery(".sp-modal-body",a).css("width",e),jQuery(".shortpixel-slider",a).css("width",e),a.css("width",e),a.css("marginLeft",n+"px"),jQuery(".sp-modal-body",a).css("height",r),a.show(),l.show(),i||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 u=jQuery(".spUploadCompareOptimized",a);jQuery(".spUploadCompareOriginal",a).attr("src",t),setTimeout(function(){jQuery(window).trigger("resize")},1e3),u.load(function(){jQuery(window).trigger("resize")}),u.attr("src",o)},closeComparerPopup:function(e){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")},convertPunycode:function(e){var r=document.createElement("a");return r.href=e,e.indexOf(r.protocol+"//"+r.hostname)<0?r.href:e.replace(r.protocol+"//"+r.hostname,r.protocol+"//"+r.hostname.split(".").map(function(e){return sp_punycode.toASCII(e)}).join("."))},checkExifWarning:function(){!jQuery('input[name="removeExif"]').is(":checked")&&jQuery('input[name="png2jpg"]').is(":checked")?jQuery(".exif_warning").fadeIn():jQuery(".exif_warning").fadeOut()},comparerData:{cssLoaded:!1,jsLoaded:!1,origUrl:!1,optUrl:!1,width:0,height:0},toRefresh:!1,resizeSizesAlert:!1}}();"function"!=typeof String.prototype.format&&(String.prototype.format=function(){for(var e=this,r=arguments.length;r--;)e=e.replace(new RegExp("\\{"+r+"\\}","gm"),arguments[r]);return e});
res/scss/shortpixel-admin.scss CHANGED
@@ -1,3 +1,6 @@
1
 
 
2
  @import 'view/bulk-restore-all';
3
- @import 'view/settings-advanced';
 
 
1
 
2
+ @import 'utils/notices';
3
  @import 'view/bulk-restore-all';
4
+ @import 'view/bulk_dashboard';
5
+ @import 'view/settings-advanced';
6
+ @import 'view/settings';
res/scss/utils/_notices.scss ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .shortpixel.notice
3
+ {
4
+ //padding: 18px;
5
+ min-height: 50px;
6
+ padding: 8px;
7
+ img
8
+ {
9
+ display:inline-block;
10
+ margin: 0 25px 0 0;
11
+ max-height: 50px;
12
+ }
13
+ .notice-dismiss
14
+ {
15
+ margin-top: 10px;
16
+ }
17
+ }
18
+
19
+ /* In-view notice ( not on top, between the options ) - styled after WP notice */
20
+ .view-notice
21
+ {
22
+
23
+ box-shadow: 0 1px 1px 0 rgba( 0, 0, 0, 0.1 );
24
+ border: 4px solid #fff;
25
+
26
+ padding: 1px 12px;
27
+ p {
28
+ margin: 1em 0 !important;
29
+ }
30
+ &.warning
31
+ {
32
+ border-left-color: #ffb900;
33
+ }
34
+ }
35
+
36
+ .view-notice-row
37
+ {
38
+ display: none;
39
+ }
res/scss/view/_bulk_dashboard.scss ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+
2
+ .short-pixel-bulk-page
3
+ {
4
+ .sp-hidden
5
+ {
6
+ display: none;
7
+ }
8
+ }
res/scss/view/_settings.scss ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // in our own scope.
3
+ .settings_page_wp-shortpixel-settings
4
+ {
5
+ .top-menu
6
+ {
7
+ font-size: 18px;
8
+ a { font-size: 18px; }
9
+ }
10
+
11
+ .wp-shortpixel-tab-content
12
+ {
13
+ transition: all 1000ms linear;
14
+ }
15
+
16
+ //tabs
17
+ article.sp-tabs
18
+ {
19
+ section
20
+ {
21
+ .wp-shortpixel-tab-content
22
+ { opacity: 0; }
23
+ &.sel-tab .wp-shortpixel-tab-content
24
+ {
25
+ opacity: 1;
26
+ }
27
+ }
28
+ section h2 {
29
+ position: absolute;
30
+ font-size: 1.3em;
31
+ font-weight: normal;
32
+ width: 180px;
33
+ height: 1.8em;
34
+ top: -1.8em;
35
+ left: 10px;
36
+ padding: 0;
37
+ margin: 0;
38
+ color: #999;
39
+ background-color: #ddd;
40
+ /* border-radius: 5px 5px 0 0; */
41
+ a {
42
+ display: block;
43
+ width: 100%;
44
+ line-height: 1.8em;
45
+ text-align: center;
46
+ text-decoration: none;
47
+ color: #23282d;
48
+ outline: 0 none;
49
+ }
50
+ }
51
+ }
52
+
53
+ article.sp-tabs section:nth-child(2) h2 {
54
+ left: 192px;
55
+ }
56
+ article.sp-tabs section:nth-child(3) h2 {
57
+ left: 374px;
58
+ }
59
+ article.sp-tabs section:nth-child(4) h2 {
60
+ left: 556px;
61
+ }
62
+ article.sp-tabs section:nth-child(5) h2 {
63
+ left: 738px;
64
+ }
65
+ article.sp-tabs section:nth-child(6) h2 {
66
+ left: 920px;
67
+ }
68
+ //article.sp-tabs section h2
69
+
70
+ section#tab-debug
71
+ {
72
+ .flex {
73
+ display: flex;
74
+ }
75
+ .env .flex {
76
+ flex-wrap: wrap;
77
+ max-width: 450px;
78
+ span {
79
+ width: 45%;
80
+ padding: 4px;
81
+ }
82
+ }
83
+ }
84
+
85
+ }
shortpixel-plugin.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger as Log;
4
+ use ShortPixel\NoticeController as Notice;
5
+
6
+
7
+ /** Plugin class
8
+ * This class is meant for: WP Hooks, init of runtime and Controller Routing.
9
+
10
+ */
11
+ class ShortPixelPlugin
12
+ {
13
+ static $instance;
14
+ private $paths = array('class', 'class/controller', 'class/external');
15
+
16
+ protected $is_noheaders = false;
17
+
18
+ protected $plugin_path;
19
+ protected $plugin_url;
20
+
21
+ public function __construct()
22
+ {
23
+ $this->plugin_path = plugin_dir_path(SHORTPIXEL_PLUGIN_FILE);
24
+ $this->plugin_url = plugin_dir_url(SHORTPIXEL_PLUGIN_FILE);
25
+
26
+ $this->initRuntime();
27
+ $this->initHooks();
28
+
29
+ if(isset($_REQUEST['noheader'])) {
30
+ $this->is_noheaders = true;
31
+ }
32
+ }
33
+
34
+ /** Create instance. This should not be needed to call anywhere else than main plugin file **/
35
+ public static function getInstance()
36
+ {
37
+ if (is_null(self::$instance))
38
+ {
39
+ self::$instance = new shortPixelPlugin();
40
+ }
41
+ return self::$instance;
42
+ }
43
+
44
+
45
+ public function initRuntime()
46
+ {
47
+ $plugin_path = plugin_dir_path(SHORTPIXEL_PLUGIN_FILE);
48
+ foreach($this->paths as $short_path)
49
+ {
50
+ $directory_path = realpath($plugin_path . $short_path);
51
+
52
+ if ($directory_path !== false)
53
+ {
54
+ $it = new \DirectoryIterator($directory_path);
55
+ foreach($it as $file)
56
+ {
57
+ $file_path = $file->getRealPath();
58
+
59
+ if ($file->isFile() && pathinfo($file_path, PATHINFO_EXTENSION) == 'php')
60
+ {
61
+ require_once($file_path);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ /** Hooks for all WordPress related hooks
69
+ */
70
+ public function initHooks()
71
+ {
72
+ add_action('admin_menu', array($this,'admin_pages'));
73
+ add_action('admin_enqueue_scripts', array($this, 'admin_scripts'));
74
+ add_action('admin_notices', array($this, 'admin_notices')); // notices occured before page load
75
+ add_action('admin_footer', array($this, 'admin_notices')); // called in views.
76
+
77
+ }
78
+
79
+ public function admin_pages()
80
+ {
81
+ // settings page
82
+ add_options_page( __('ShortPixel Settings','shortpixel-image-optimiser'), 'ShortPixel', 'manage_options', 'wp-shortpixel-settings', array($this, 'route'));
83
+ }
84
+
85
+ /** All scripts should be registed, not enqueued here (unless global wp-admin is needed )
86
+ *
87
+ * Not all those registered must be enqueued however.
88
+ */
89
+ public function admin_scripts()
90
+ {
91
+ // FileTree in Settings
92
+ wp_register_style('sp-file-tree', plugins_url('/res/css/sp-file-tree.min.css',SHORTPIXEL_PLUGIN_FILE),array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION );
93
+ wp_register_script('sp-file-tree', plugins_url('/res/js/sp-file-tree.min.js',SHORTPIXEL_PLUGIN_FILE) );
94
+
95
+ wp_register_style('shortpixel-admin', plugins_url('/res/css/shortpixel-admin.css', SHORTPIXEL_PLUGIN_FILE),array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION );
96
+
97
+ wp_register_style('shortpixel', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
98
+ //modal - used in settings for selecting folder
99
+ wp_register_style('shortpixel-modal', plugins_url('/res/css/short-pixel-modal.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
100
+
101
+ }
102
+
103
+ public function admin_notices()
104
+ {
105
+ $noticeControl = Notice::getInstance();
106
+
107
+ if ($noticeControl->countNotices() > 0)
108
+ {
109
+ foreach($noticeControl->getNotices() as $notice)
110
+ {
111
+ echo $notice->getForDisplay();
112
+ }
113
+ }
114
+ $noticeControl->update(); // puts views, and updates
115
+ }
116
+
117
+ /** Load Style via Route, on demand */
118
+ public function load_style($name)
119
+ {
120
+ if ($this->is_noheaders) // fail silently, if this is a no-headers request.
121
+ return;
122
+
123
+ if (wp_style_is($name, 'registered'))
124
+ {
125
+ wp_enqueue_style($name);
126
+ }
127
+ else {
128
+ Log::addWarn("Style $name was asked for, but not registered");
129
+ }
130
+ }
131
+
132
+ /** Load Style via Route, on demand */
133
+ public function load_script($name)
134
+ {
135
+ if ($this->is_noheaders) // fail silently, if this is a no-headers request.
136
+ return;
137
+
138
+
139
+ if (wp_script_is($name, 'registered'))
140
+ {
141
+ wp_enqueue_script($name);
142
+ }
143
+ else {
144
+
145
+ Log::addWarn("Script $name was asked for, but not registered");
146
+ }
147
+ }
148
+
149
+ /** Route, based on the page slug
150
+ *
151
+ * Principially all page controller should be routed from here.
152
+ */
153
+ public function route()
154
+ {
155
+ global $plugin_page;
156
+ global $shortPixelPluginInstance; //brrr @todo Find better solution for this some day.
157
+ $default_action = 'load'; // generic action on controller.
158
+ $action = isset($_REQUEST['sp-action']) ? sanitize_text_field($_REQUEST['sp-action']) : $default_action;
159
+ Log::addDebug('Request', $_REQUEST);
160
+ $controller = false;
161
+
162
+ switch($plugin_page)
163
+ {
164
+ case 'wp-shortpixel-settings':
165
+ $this->load_style('shortpixel-admin');
166
+ $this->load_style('shortpixel');
167
+ $this->load_style('shortpixel-modal');
168
+ $this->load_style('sp-file-tree');
169
+ $this->load_script('sp-file-tree');
170
+ $controller = \shortPixelTools::namespaceit("SettingsController");
171
+ $url = menu_page_url($plugin_page, false);
172
+ break;
173
+ }
174
+
175
+ if ($controller !== false)
176
+ {
177
+ $c = new $controller();
178
+ $c->setShortPixel($shortPixelPluginInstance);
179
+ $c->setControllerURL($url);
180
+ if (method_exists($c, $action))
181
+ $c->$action();
182
+ else {
183
+ Log::addWarn("Attempted Action $action on $controller does not exist!");
184
+ $c->$default_action();
185
+ }
186
+
187
+ }
188
+ }
189
+
190
+
191
+
192
+ }
shortpixel_api.php CHANGED
@@ -3,6 +3,8 @@ 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;
@@ -104,10 +106,12 @@ class ShortPixelAPI {
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,
@@ -411,7 +415,7 @@ class ShortPixelAPI {
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
@@ -447,27 +451,42 @@ class ShortPixelAPI {
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) )
@@ -483,6 +502,7 @@ class ShortPixelAPI {
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
  }
@@ -490,7 +510,7 @@ class ShortPixelAPI {
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) ) {
@@ -562,7 +582,7 @@ class ShortPixelAPI {
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;
@@ -628,12 +648,15 @@ class ShortPixelAPI {
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;
@@ -664,7 +687,7 @@ class ShortPixelAPI {
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 ++;
@@ -691,9 +714,7 @@ class ShortPixelAPI {
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
  }
@@ -709,7 +730,7 @@ class ShortPixelAPI {
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
  }
@@ -847,7 +868,7 @@ class ShortPixelAPI {
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)) {
3
  require_once( ABSPATH . 'wp-admin/includes/file.php' );
4
  }
5
 
6
+ use \ShortPixel\ShortPixelLogger as Log;
7
+
8
  class ShortPixelAPI {
9
 
10
  const STATUS_SUCCESS = 1;
106
  $apiKey = $this->_settings->apiKey;
107
  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
108
  $this->_settings->verifiedKey = false;
109
+ Log::addWarn('Invalid API Key');
110
  throw new Exception(__('Invalid API Key', 'shortpixel-image-optimiser'));
111
  }
112
 
113
  // WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
114
+ $URLs = apply_filters('shortpixel_image_urls', $URLs, $itemHandler->getId()) ;
115
 
116
  $requestParameters = array(
117
  'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
415
  $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl));
416
 
417
  $tempFile = download_url($fileURL, $downloadTimeout);
418
+ Log::addInfo('Downloading file: '.json_encode($tempFile));
419
  if(is_wp_error( $tempFile ))
420
  { //try to switch the default protocol
421
  $fileURL = $this->setPreferredProtocol(urldecode($optimizedUrl), true); //force recheck of the protocol
451
  return $returnMessage;
452
  }
453
 
454
+ /** Tries to create backup
455
+ *
456
+ * @param $mainPath
457
+ * @param $PATHs
458
+ * @return Array Array with Status and optional Message */
459
  public static function backupImage($mainPath, $PATHs) {
460
+ /**
461
+ * Passing a truthy value to the filter will effectively short-circuit this function.
462
+ * So third party plugins can handle Backup by there own.
463
+ */
464
+ if(apply_filters('shortpixel_skip_backup', false, $mainPath, $PATHs)){
465
+ return array("Status" => self::STATUS_SUCCESS);
466
+ }
467
+
468
  //$fullSubDir = str_replace(wp_normalize_path(get_home_path()), "", wp_normalize_path(dirname($itemHandler->getMeta()->getPath()))) . '/';
469
  //$SubDir = ShortPixelMetaFacade::returnSubDir($itemHandler->getMeta()->getPath(), $itemHandler->getType());
470
  $fullSubDir = ShortPixelMetaFacade::returnSubDir($mainPath);
471
  $source = $PATHs; //array with final paths for these files
472
 
473
+ if( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && ! ShortPixelFolder::createBackUpFolder() ) {//creates backup folder if it doesn't exist
474
+ Log::addWarn('Backup folder does not exist and it cannot be created');
475
  return array("Status" => self::STATUS_FAIL, "Message" => __('Backup folder does not exist and it cannot be created','shortpixel-image-optimiser'));
476
  }
477
  //create subdir in backup folder if needed
478
+ //@mkdir( SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir, 0777, true);
479
+ ShortPixelFolder::createBackUpFolder(SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir);
480
 
481
  foreach ( $source as $fileID => $filePATH )//create destination files array
482
  {
483
  $destination[$fileID] = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . self::MB_basename($source[$fileID]);
484
  }
 
485
 
486
  //now that we have original files and where we should back them up we attempt to do just that
487
  if(is_writable(SHORTPIXEL_BACKUP_FOLDER))
488
  {
489
+ Log::addDebug('Creating backups from source - destination', array('source' => $source, 'destination' => $destination));
490
  foreach ( $destination as $fileID => $filePATH )
491
  {
492
  if ( !file_exists($filePATH) )
502
  }
503
  else {//cannot write to the backup dir, return with an error
504
  $msg = __('Cannot save file in backup directory','shortpixel-image-optimiser');
505
+ Log::addWarn('Backup directory not writable');
506
  return array("Status" => self::STATUS_FAIL, "Message" => $msg);
507
  }
508
  }
510
  private function createArchiveTempFolder($archiveBasename) {
511
  $archiveTempDir = get_temp_dir() . '/' . $archiveBasename;
512
  if(file_exists($archiveTempDir) && is_dir($archiveTempDir) && (time() - filemtime($archiveTempDir) < max(30, SHORTPIXEL_MAX_EXECUTION_TIME) + 10)) {
513
+ Log::addWarn("CONFLICT. Folder already exists and is modified in the last minute. Current IP:" . $_SERVER['REMOTE_ADDR']);
514
  return array("Status" => self::STATUS_RETRY, "Code" => 1, "Message" => "Pending");
515
  }
516
  if( !file_exists($archiveTempDir) && !@mkdir($archiveTempDir) ) {
582
  * @return array status/message
583
  */
584
  private function handleSuccess($APIresponse, $PATHs, $itemHandler, $compressionType) {
585
+ Log::addDebug('Shortpixel API : Handling Success!');
586
 
587
  $counter = $savedSpace = $originalSpace = $optimizedSpace /* = $averageCompression */ = 0;
588
  $NoBackup = true;
648
  $mainPath = $itemHandler->getMeta()->getPath();
649
 
650
  //if backup is enabled - we try to save the images
651
+ Log::addDebug('Check setting backup', array($this->_settings->backupImages));
652
  if( $this->_settings->backupImages )
653
  {
654
  $backupStatus = self::backupImage($mainPath, $PATHs);
655
+ Log::addDebug('Status', $backupStatus);
656
  if($backupStatus == self::STATUS_FAIL) {
657
  $itemHandler->incrementRetries(1, self::ERR_SAVE_BKP, $backupStatus["Message"]);
658
  self::cleanupTemporaryFiles($archive, empty($tempFiles) ? array() : $tempFiles);
659
+ Log::addError('Failed to create image backup!', array('status' => $backupStatus));
660
  return array("Status" => self::STATUS_FAIL, "Code" =>"backup-fail", "Message" => "Failed to back the image up.");
661
  }
662
  $NoBackup = false;
687
 
688
  if($tempFile['Status'] == self::STATUS_SUCCESS) { //if it's unchanged it will still be in the array but only for WebP (handled below)
689
  $tempFilePATH = $tempFile["Message"];
690
+ if ( file_exists($tempFilePATH) && (!file_exists($targetFile) || is_writable($targetFile)) ) {
691
  copy($tempFilePATH, $targetFile);
692
  if(ShortPixelMetaFacade::isRetina($targetFile)) {
693
  $retinas ++;
714
  if($archive && SHORTPIXEL_DEBUG === true) {
715
  if(!file_exists($tempFilePATH)) {
716
  WPShortPixel::log("MISSING FROM ARCHIVE. tempFilePath: $tempFilePATH with ID: $tempFileID");
717
+ } elseif(!wp_is_writable($targetFile)){
 
 
718
  WPShortPixel::log("TARGET NOT WRITABLE: $targetFile");
719
  }
720
  }
730
  //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
731
  //save it with double extension
732
  if(file_exists($targetWebPFileCompat)) {
733
+ copy($tempWebpFilePATH, $targetWebPFile);
734
  } else {
735
  copy($tempWebpFilePATH, $targetWebPFileCompat);
736
  }
868
  foreach ( $PATHs as $Id => $File )
869
  {
870
  //we try again with a different path
871
+ if ( !apply_filters( 'shortpixel_image_exists', file_exists($File), $File ) ){
872
  //$NewFile = $uploadDir['basedir'] . "/" . substr($File,strpos($File, $StichString));//+strlen($StichString));
873
  $NewFile = SHORTPIXEL_UPLOADS_BASE . substr($File,strpos($File, $StichString)+strlen($StichString));
874
  if (file_exists($NewFile)) {
wp-shortpixel-req.php CHANGED
@@ -1,16 +1,52 @@
1
  <?php
 
2
  if(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) {
3
  require_once('shortpixel-debug.php');
4
  } else {
5
  define('SHORTPIXEL_DEBUG', false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  }
7
 
8
- require_once('class/wp-short-pixel.php');
9
- require_once('class/wp-shortpixel-settings.php');
10
- require_once('class/wp-shortpixel-cloudflare-api.php');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  require_once('shortpixel_api.php');
12
- require_once('class/shortpixel_queue.php');
13
- require_once('class/shortpixel-png2jpg.php');
14
  //entities
15
  require_once('class/model/shortpixel-entity.php');
16
  require_once('class/model/shortpixel-meta.php');
@@ -27,15 +63,15 @@ require_once('class/db/shortpixel-meta-facade.php');
27
  //view
28
  require_once('class/view/shortpixel_view.php');
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') ){
40
  function wp_normalize_path( $path ) {
41
  $path = str_replace( '\\', '/', $path );
1
  <?php
2
+ /*
3
  if(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) {
4
  require_once('shortpixel-debug.php');
5
  } else {
6
  define('SHORTPIXEL_DEBUG', false);
7
+ } */
8
+
9
+ // Debug. Hook as early as possible.
10
+ require_once('class/controller/controller.php');
11
+ require_once('class/controller/debug.php');
12
+ require_once('class/model/shortpixel-debug.php');
13
+
14
+ // @todo wp-shortpixel-settings which depends on this model should be called when needed; in the model/ directory. That will be some work, so for now here.
15
+ require_once('class/shortpixel-model.php');
16
+
17
+ use ShortPixel\DebugItem as DebugItem;
18
+ use ShortPixel\ShortPixelLogger as Log;
19
+
20
+ if (! defined('SHORTPIXEL_DEBUG'))
21
+ {
22
+ define('SHORTPIXEL_DEBUG', false);
23
  }
24
 
25
+ Log::addDebug('Plugin Req Init');
26
+
27
+
28
+ // [BS] New plugin runtime.
29
+ require_once('shortpixel-plugin.php'); // loads runtime and needed classes.
30
+ new Shortpixel\ShortPixelPlugin();
31
+
32
+ // @todo Temporary until main plugin file will receive it's unclutter. Require the things loaded by new plugin main
33
+ /*if (! class_exists('ShortPixel\ShortPixelPlugin'))
34
+ {
35
+ require_once('class/shortpixel_queue.php');
36
+ require_once('class/shortpixel-png2jpg.php');
37
+ require_once('class/wp-short-pixel.php');
38
+ require_once('class/wp-shortpixel-settings.php');
39
+ require_once('class/wp-shortpixel-cloudflare-api.php');
40
+ require_once('class/shortpixel-tools.php');
41
+ require_once('class/controller/bulk-restore-all.php');
42
+ } */
43
+
44
+ //require_once('class/wp-short-pixel.php');
45
+ //require_once('class/wp-shortpixel-settings.php');
46
+ //require_once('class/wp-shortpixel-cloudflare-api.php');
47
  require_once('shortpixel_api.php');
48
+ //require_once('class/shortpixel_queue.php');
49
+ //require_once('class/shortpixel-png2jpg.php');
50
  //entities
51
  require_once('class/model/shortpixel-entity.php');
52
  require_once('class/model/shortpixel-meta.php');
63
  //view
64
  require_once('class/view/shortpixel_view.php');
65
 
66
+ //require_once('class/shortpixel-tools.php');
67
 
68
+ //require_once('class/controller/bulk-restore-all.php');
 
69
 
70
  require_once( ABSPATH . 'wp-admin/includes/image.php' );
71
  include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
72
 
73
  // for retro compatibility with WP < 3.5
74
+ // @todo Move this to compatibility file.
75
  if( !function_exists('wp_normalize_path') ){
76
  function wp_normalize_path( $path ) {
77
  $path = str_replace( '\\', '/', $path );
wp-shortpixel.php CHANGED
@@ -2,15 +2,16 @@
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.1
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
 
@@ -18,7 +19,7 @@ define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
18
 
19
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
20
 
21
- define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.13.1");
22
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
23
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
24
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
@@ -81,6 +82,7 @@ function shortpixelInit() {
81
  )
82
  {
83
  require_once('wp-shortpixel-req.php');
 
84
  $shortPixelPluginInstance = new WPShortPixel;
85
  }
86
 
@@ -158,7 +160,9 @@ function shortPixelConvertImgToPictureAddWebp($content) {
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() {
@@ -208,7 +212,7 @@ $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 ){
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-settings" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
+ * Version: 4.14.0
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
10
  * Domain Path: /lang
11
  */
12
 
13
+ if (! defined('SHORTPIXEL_RESET_ON_ACTIVATE'))
14
+ define('SHORTPIXEL_RESET_ON_ACTIVATE', false); //if true TODO set false
15
  //define('SHORTPIXEL_DEBUG', true);
16
  //define('SHORTPIXEL_DEBUG_TARGET', true);
17
 
19
 
20
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
21
 
22
+ define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.14.0");
23
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
24
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
25
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
82
  )
83
  {
84
  require_once('wp-shortpixel-req.php');
85
+
86
  $shortPixelPluginInstance = new WPShortPixel;
87
  }
88
 
160
  //for AMP pages the <picture> tag is not allowed
161
  return $content . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG is AMP -->' : '');
162
  }
163
+ require_once('wp-shortpixel-req.php');
164
  require_once('class/front/img-to-picture-webp.php');
165
+
166
  return ShortPixelImgToPictureWebp::convert($content);// . "<!-- PICTURE TAGS BY SHORTPIXEL -->";
167
  }
168
  function shortPixelAddPictureJs() {
212
  if ( $option ) {
213
  if(shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {
214
  set_transient("shortpixel_thrown_notice", array('when' => 'spai', 'extra' => __('Please deactivate the ShortPixel Image Optimizer\'s
215
+ <a href="options-general.php?page=wp-shortpixel-settings&part=adv-settings">Deliver WebP using PICTURE tag</a>
216
  option when the ShortPixel Adaptive Images plugin is active.','shortpixel-image-optimiser')), 1800);
217
  }
218
  elseif( $option == 1 ){