ShortPixel Image Optimizer - Version 4.14.3

Version Description

=

Release date: 22nd July 2019 * Compatibility with Flywheel hosting by not using flock if Flywheel detected * When using the PICTURE tag to deliver WebP, keep the width and height at level * Refactoring of the API Key settings page, refactoring of Other media * Performance improvements for selecting from large wp_posts and wp_postmeta tables when doing bulk optimization * Display a message when bulk is skipping many processed images, if bulk processing is ran again. * Fixed: Other media - Recompress adds "Preserve CMYK" * Fixed: duplicate error message when validating wrong API key * Conflict message: align the text vertically and provide space between text and button when displayed on any admin page * Fixed: Other media Restore fails when original file is readable, not writable

=

Download this release

Release Info

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

Code changes from version 4.14.2 to 4.14.3

Files changed (55) hide show
  1. build/shortpixel/PackageLoader.php +73 -0
  2. build/shortpixel/autoload.php +5 -0
  3. build/shortpixel/composer.json +1 -0
  4. build/shortpixel/log/composer.json +18 -0
  5. build/shortpixel/log/src/DebugItem.php +141 -0
  6. build/shortpixel/log/src/ShortPixelLogger.php +322 -0
  7. build/shortpixel/log/src/view-debug-box.php +57 -0
  8. build/shortpixel/notices/composer.json +31 -0
  9. build/shortpixel/notices/src/NoticeController.php +194 -0
  10. build/shortpixel/notices/src/NoticeModel.php +116 -0
  11. build/shortpixel/notices/src/css/notices.css +21 -0
  12. build/shortpixel/notices/src/css/notices.min.css +1 -0
  13. build/shortpixel/notices/src/css/notices.scss +39 -0
  14. class/controller/apikey_controller.php +30 -0
  15. class/controller/bulk-restore-all.php +0 -1
  16. class/controller/controller.php +23 -7
  17. class/controller/filesystem_controller.php +66 -0
  18. class/controller/settings.php +89 -90
  19. class/db/shortpixel-custom-meta-dao.php +4 -1
  20. class/db/shortpixel-meta-facade.php +74 -71
  21. class/db/wp-shortpixel-media-library-adapter.php +59 -2
  22. class/external/flywheel.php +27 -0
  23. class/external/helpscout.php +39 -2
  24. class/external/nextgen.php +1 -1
  25. class/external/shortpixel_queue_db.php +330 -0
  26. class/front/img-to-picture-webp.php +20 -7
  27. class/model/apikey_model.php +294 -0
  28. class/model/directory_model.php +93 -0
  29. class/model/environment_model.php +32 -0
  30. class/model/file_model.php +183 -0
  31. class/shortpixel-model.php +1 -3
  32. class/shortpixel_queue.php +8 -5
  33. class/view/settings/part-general.php +3 -3
  34. class/view/settings/part-nokey.php +10 -7
  35. class/view/settings/part-statistics.php +9 -1
  36. class/view/shortpixel-list-table.php +9 -4
  37. class/view/shortpixel_view.php +26 -17
  38. class/view/view-restore-all.php +1 -1
  39. class/view/view-settings.php +3 -2
  40. class/wp-short-pixel.php +256 -409
  41. class/wp-shortpixel-settings.php +4 -2
  42. readme.txt +35 -19
  43. res/css/short-pixel-bar.css +22 -0
  44. res/css/short-pixel-bar.min.css +1 -1
  45. res/css/short-pixel.css +0 -20
  46. res/css/short-pixel.min.css +1 -1
  47. res/css/shortpixel-admin.min.css +1 -1
  48. res/js/jquery.tooltip.js +43 -38
  49. res/js/jquery.tooltip.min.js +1 -1
  50. res/js/shortpixel.js +38 -3
  51. res/js/shortpixel.min.js +1 -1
  52. shortpixel-plugin.php +27 -9
  53. shortpixel_api.php +7 -3
  54. wp-shortpixel-req.php +4 -4
  55. wp-shortpixel.php +24 -5
build/shortpixel/PackageLoader.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel\Build;
3
+
4
+ class PackageLoader
5
+ {
6
+ public $dir;
7
+
8
+ public function getComposerFile()
9
+ {
10
+ return json_decode(file_get_contents($this->dir."/composer.json"), 1);
11
+ }
12
+
13
+ public function load($dir)
14
+ {
15
+ $this->dir = $dir;
16
+ $composer = $this->getComposerFile();
17
+ if(isset($composer["autoload"]["psr-4"])){
18
+ $this->loadPSR4($composer['autoload']['psr-4']);
19
+ }
20
+ if(isset($composer["autoload"]["psr-0"])){
21
+ $this->loadPSR0($composer['autoload']['psr-0']);
22
+ }
23
+ if(isset($composer["autoload"]["files"])){
24
+ $this->loadFiles($composer["autoload"]["files"]);
25
+ }
26
+ }
27
+
28
+ public function loadFiles($files){
29
+ foreach($files as $file){
30
+ $fullpath = $this->dir."/".$file;
31
+ if(file_exists($fullpath)){
32
+ include_once($fullpath);
33
+ }
34
+ }
35
+ }
36
+
37
+ public function loadPSR4($namespaces)
38
+ {
39
+ $this->loadPSR($namespaces, true);
40
+ }
41
+
42
+ public function loadPSR0($namespaces)
43
+ {
44
+ $this->loadPSR($namespaces, false);
45
+ }
46
+
47
+ public function loadPSR($namespaces, $psr4)
48
+ {
49
+ $dir = $this->dir;
50
+ // Foreach namespace specified in the composer, load the given classes
51
+ foreach ($namespaces as $namespace => $classpaths) {
52
+ if (!is_array($classpaths)) {
53
+ $classpaths = array($classpaths);
54
+ }
55
+ spl_autoload_register(function ($classname) use ($namespace, $classpaths, $dir, $psr4) {
56
+ // Check if the namespace matches the class we are looking for
57
+ if (preg_match("#^".preg_quote($namespace)."#", $classname)) {
58
+ // Remove the namespace from the file path since it's psr4
59
+ if ($psr4) {
60
+ $classname = str_replace($namespace, "", $classname);
61
+ }
62
+ $filename = preg_replace("#\\\\#", "/", $classname).".php";
63
+ foreach ($classpaths as $classpath) {
64
+ $fullpath = $this->dir."/".$classpath."/$filename";
65
+ if (file_exists($fullpath)) {
66
+ include_once $fullpath;
67
+ }
68
+ }
69
+ }
70
+ });
71
+ }
72
+ }
73
+ }
build/shortpixel/autoload.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+ require_once "PackageLoader.php";
3
+ $loader = new ShortPixel\Build\PackageLoader();
4
+ $loader->load(__DIR__);
5
+
build/shortpixel/composer.json ADDED
@@ -0,0 +1 @@
 
1
+ {"name":"ShortPixel\/shortpixelmodules","description":"ShortPixel submodules","type":"function","autoload":{"psr-4":{"ShortPixel\\ShortPixelLogger\\":"log\/src\/","ShortPixel\\Notices\\":"notices\/src\/"}}}
build/shortpixel/log/composer.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "shortpixel/log",
3
+ "description": "ShortPixel Logging",
4
+ "version": "1.1",
5
+ "type": "library",
6
+ "license": "MIT",
7
+ "authors": [
8
+ {
9
+ "name": "Bas",
10
+ "email": "bas@weblogmechanic.com"
11
+ }
12
+ ],
13
+ "minimum-stability": "dev",
14
+ "require": {},
15
+ "autoload": {
16
+ "psr-4": { "ShortPixel\\ShortPixelLogger\\" : "src/" }
17
+ }
18
+ }
build/shortpixel/log/src/DebugItem.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // The data models.
3
+ namespace ShortPixel\ShortPixelLogger;
4
+
5
+
6
+ class DebugItem
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
+
127
+ $i = 4;
128
+ if (isset($debug[$i]))
129
+ {
130
+ $info = $debug[$i];
131
+ $line = isset($info['line']) ? $info['line'] : 'Line unknown';
132
+ $file = isset($info['file']) ? basename($info['file']) : 'File not set';
133
+
134
+ $this->caller = array('line' => $line, 'file' => $file, 'function' => $info['function']);
135
+ }
136
+
137
+
138
+ }
139
+
140
+
141
+ }
build/shortpixel/log/src/ShortPixelLogger.php ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel\ShortPixelLogger;
3
+
4
+ /*** Logger class
5
+ *
6
+ * Class uses the debug data model for keeping log entries.
7
+ * Logger should not be called before init hook!
8
+ */
9
+ class ShortPixelLogger
10
+ {
11
+ static protected $instance = null;
12
+ protected $start_time;
13
+
14
+ protected $is_active = false;
15
+ protected $is_manual_request = false;
16
+ protected $show_debug_view = false;
17
+
18
+ protected $items = array();
19
+ protected $logPath = false;
20
+ protected $logMode = FILE_APPEND;
21
+
22
+ protected $logLevel;
23
+ protected $format = "[ %%time%% ] %%color%% %%level%% %%color_end%% \t %%message%% \t %%caller%% ( %%time_passed%% )";
24
+ protected $format_data = "\t %%data%% ";
25
+
26
+ protected $hooks = array();
27
+ /* protected $hooks = array(
28
+ 'shortpixel_image_exists' => array('numargs' => 3),
29
+ 'shortpixel_webp_image_base' => array('numargs' => 2),
30
+ 'shortpixel_image_urls' => array('numargs' => 2),
31
+ ); // @todo monitor hooks, but this should be more dynamic. Do when moving to module via config.
32
+ */
33
+
34
+ // utility
35
+ private $namespace;
36
+ private $view;
37
+
38
+ protected $template = 'view-debug-box';
39
+
40
+ /** Debugger constructor
41
+ * 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
42
+ * 2) Put SHORTPIXEL_DEBUG in the request. Either true or number.
43
+ */
44
+ public function __construct()
45
+ {
46
+ $this->start_time = microtime(true);
47
+ $this->logLevel = DebugItem::LEVEL_WARN;
48
+
49
+ $ns = __NAMESPACE__;
50
+ $this->namespace = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
51
+
52
+ if ($this->logPath === false)
53
+ {
54
+ $upload_dir = wp_upload_dir(null,false,false);
55
+ $this->logPath = $upload_dir['basedir'] . '/' . $this->namespace . ".log";
56
+ }
57
+
58
+ if (isset($_REQUEST['SHORTPIXEL_DEBUG'])) // manual takes precedence over constants
59
+ {
60
+ $this->is_manual_request = true;
61
+ $this->is_active = true;
62
+
63
+ if ($_REQUEST['SHORTPIXEL_DEBUG'] === 'true')
64
+ {
65
+ $this->logLevel = DebugItem::LEVEL_INFO;
66
+ }
67
+ else {
68
+ $this->logLevel = intval($_REQUEST['SHORTPIXEL_DEBUG']);
69
+ }
70
+
71
+ }
72
+ else if ( (defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG > 0) )
73
+ {
74
+ $this->is_active = true;
75
+ if (SHORTPIXEL_DEBUG === true)
76
+ $this->logLevel = DebugItem::LEVEL_INFO;
77
+ else {
78
+ $this->logLevel = intval(SHORTPIXEL_DEBUG);
79
+ }
80
+ }
81
+
82
+ if (defined('SHORTPIXEL_DEBUG_TARGET') && SHORTPIXEL_DEBUG_TARGET || $this->is_manual_request)
83
+ {
84
+ //$this->logPath = SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log";
85
+ //$this->logMode = defined('SHORTPIXEL_LOG_OVERWRITE') ? 0 : FILE_APPEND;
86
+ if (defined('SHORTPIXEL_LOG_OVERWRITE')) // if overwrite, do this on init once.
87
+ file_put_contents($this->logPath,'-- Log Reset -- ' .PHP_EOL);
88
+
89
+ }
90
+
91
+ /* On Early init, this function might not exist, then queue it when needed */
92
+ if (! function_exists('wp_get_current_user'))
93
+ add_action('plugins_loaded', array($this, 'initView'));
94
+ else
95
+ $this->initView();
96
+
97
+
98
+ if ($this->is_active && count($this->hooks) > 0)
99
+ $this->monitorHooks();
100
+ }
101
+
102
+ /** Init the view when needed. Private function ( public because of WP_HOOK )
103
+ * Never call directly */
104
+ public function initView()
105
+ {
106
+ $user_is_administrator = (current_user_can('manage_options')) ? true : false;
107
+
108
+ if ($this->is_active && $this->is_manual_request && $user_is_administrator )
109
+ {
110
+ $content_url = content_url();
111
+ $logPath = $this->logPath;
112
+ $pathpos = strpos($logPath, 'wp-content') + strlen('wp-content');
113
+ $logPart = substr($logPath, $pathpos);
114
+ $logLink = $content_url . $logPart;
115
+
116
+ $this->view = new \stdClass;
117
+ $this->view->logLink = $logLink;
118
+ add_action('admin_footer', array($this, 'loadView'));
119
+ }
120
+ }
121
+
122
+ public static function getInstance()
123
+ {
124
+ if ( self::$instance === null)
125
+ {
126
+ self::$instance = new ShortPixelLogger();
127
+ }
128
+ return self::$instance;
129
+ }
130
+
131
+ public function setLogPath($logPath)
132
+ {
133
+ $this->logPath = $logPath;
134
+ }
135
+ protected static function addLog($message, $level, $data = array())
136
+ {
137
+ $log = self::getInstance();
138
+
139
+ // don't log anything too low.
140
+ if ($log->logLevel < $level)
141
+ {
142
+ return;
143
+ }
144
+
145
+ $arg = array();
146
+ $args['level'] = $level;
147
+ $args['data'] = $data;
148
+
149
+ $newItem = new DebugItem($message, $args);
150
+ $log->items[] = $newItem;
151
+
152
+ if ($log->is_active)
153
+ {
154
+ $log->write($newItem);
155
+ }
156
+ }
157
+
158
+ /** Writes to log File. */
159
+ protected function write($debugItem, $mode = 'file')
160
+ {
161
+ $items = $debugItem->getForFormat();
162
+ $items['time_passed'] = round ( ($items['time'] - $this->start_time), 5);
163
+ $items['time'] = date('Y-m-d H:i:s', $items['time'] );
164
+
165
+ if ( ($items['caller']) && is_array($items['caller']) && count($items['caller']) > 0)
166
+ {
167
+ $caller = $items['caller'];
168
+ $items['caller'] = $caller['file'] . ' in ' . $caller['function'] . '(' . $caller['line'] . ')';
169
+ }
170
+
171
+ $line = $this->formatLine($items);
172
+
173
+ if ($this->logPath)
174
+ {
175
+ file_put_contents($this->logPath,$line, FILE_APPEND);
176
+ }
177
+ else {
178
+ error_log($line);
179
+ }
180
+ }
181
+
182
+ protected function formatLine($args = array() )
183
+ {
184
+ $line= $this->format;
185
+ foreach($args as $key => $value)
186
+ {
187
+ if (! is_array($value) && ! is_object($value))
188
+ $line = str_replace('%%' . $key . '%%', $value, $line);
189
+ }
190
+
191
+ $line .= PHP_EOL;
192
+
193
+ if (isset($args['data']))
194
+ {
195
+ $data = array_filter($args['data']);
196
+ if (count($data) > 0)
197
+ {
198
+ foreach($data as $item)
199
+ {
200
+ $line .= $item . PHP_EOL;
201
+ }
202
+ }
203
+ }
204
+
205
+ return $line;
206
+ }
207
+
208
+ protected function setLogLevel($level)
209
+ {
210
+ $this->logLevel = $level;
211
+ }
212
+
213
+ protected function getEnv($name)
214
+ {
215
+ if (isset($this->{$name}))
216
+ {
217
+ return $this->{$name};
218
+ }
219
+ else {
220
+ return false;
221
+ }
222
+ }
223
+
224
+ public static function addError($message, $args = array())
225
+ {
226
+ $level = DebugItem::LEVEL_ERROR;
227
+ static::addLog($message, $level, $args);
228
+ }
229
+ public static function addWarn($message, $args = array())
230
+ {
231
+ $level = DebugItem::LEVEL_WARN;
232
+ static::addLog($message, $level, $args);
233
+ }
234
+ public static function addInfo($message, $args = array())
235
+ {
236
+ $level = DebugItem::LEVEL_INFO;
237
+ static::addLog($message, $level, $args);
238
+ }
239
+ public static function addDebug($message, $args = array())
240
+ {
241
+ $level = DebugItem::LEVEL_DEBUG;
242
+ static::addLog($message, $level, $args);
243
+ }
244
+
245
+ public static function logLevel($level)
246
+ {
247
+ $log = self::getInstance();
248
+ static::addInfo('Changing Log level' . $level);
249
+ $log->setLogLevel($level);
250
+ }
251
+
252
+ public static function getLogLevel()
253
+ {
254
+ $log = self::getInstance();
255
+ return $log->getEnv('logLevel');
256
+ }
257
+
258
+ public static function isManualDebug()
259
+ {
260
+ $log = self::getInstance();
261
+ return $log->getEnv('is_manual_request');
262
+ }
263
+
264
+ public static function getLogPath()
265
+ {
266
+ $log = self::getInstance();
267
+ return $log->getEnv('logPath');
268
+ }
269
+
270
+ /** Function to test if the debugger is active
271
+ * @return boolean true when active.
272
+ */
273
+ public static function debugIsActive()
274
+ {
275
+ $log = self::getInstance();
276
+ return $log->getEnv('is_active');
277
+ }
278
+
279
+ protected function monitorHooks()
280
+ {
281
+
282
+ foreach($this->hooks as $hook => $data)
283
+ {
284
+ $numargs = isset($data['numargs']) ? $data['numargs'] : 1;
285
+ $prio = isset($data['priority']) ? $data['priority'] : 10;
286
+
287
+ add_filter($hook, function($value) use ($hook) {
288
+ $args = func_get_args();
289
+ return $this->logHook($hook, $value, $args); }, $prio, $numargs);
290
+ }
291
+ }
292
+
293
+ public function logHook($hook, $value, $args)
294
+ {
295
+ array_shift($args);
296
+ self::addInfo('[Hook] - ' . $hook . ' with ' . var_export($value,true), $args);
297
+ return $value;
298
+ }
299
+
300
+ public function loadView()
301
+ {
302
+ // load either param or class template.
303
+ $template = $this->template;
304
+
305
+ $view = $this->view;
306
+ $view->namespace = $this->namespace;
307
+ $controller = $this;
308
+
309
+ $template_path = __DIR__ . '/' . $this->template . '.php';
310
+ if (file_exists($template_path))
311
+ {
312
+
313
+ include($template_path);
314
+ }
315
+ else {
316
+ self::addError("View $template could not be found in " . $template_path,
317
+ array('class' => get_class($this), 'req' => $_REQUEST));
318
+ }
319
+ }
320
+
321
+
322
+ } // class debugController
build/shortpixel/log/src/view-debug-box.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Debug Box to load Log File
3
+ namespace ShortPixel\ShortPixelLogger;
4
+ wp_enqueue_script( 'jquery-ui-draggable' );
5
+
6
+ ?>
7
+
8
+ <style>
9
+ .sp_debug_wrap
10
+ {
11
+ position: relative;
12
+ clear: both;
13
+ }
14
+ .sp_debug_box
15
+ {
16
+ position: absolute;
17
+ right: 0px;
18
+ top: 50px;
19
+ background-color: #fff;
20
+ width: 150px;
21
+ z-index: 1000000;
22
+ border: 1px solid #000;
23
+
24
+ }
25
+ .sp_debug_box .header
26
+ {
27
+ min-height: 10px;
28
+ background: #000;
29
+ color: #fff;
30
+ padding: 8px
31
+ }
32
+ .sp_debug_box .content_box
33
+ {
34
+ background: #ccc;
35
+ }
36
+ .content_box
37
+ {
38
+ padding: 8px;
39
+ }
40
+ </style>
41
+
42
+ <script language='javascript'>
43
+ jQuery(document).ready(function($)
44
+ {
45
+ $( ".sp_debug_box" ).draggable();
46
+
47
+ });
48
+ </script>
49
+
50
+
51
+ <div class='sp_debug_box'>
52
+ <div class='header'><?php echo $view->namespace ?> Debug Box </div>
53
+ <a target="_blank" href='<?php echo $view->logLink ?>'>Logfile</a>
54
+ <div class='content_box'>
55
+
56
+ </div>
57
+ </div>
build/shortpixel/notices/composer.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "shortpixel/notices",
3
+ "description": "ShortPixel WordPress Notice System",
4
+ "version": "1.1",
5
+ "type": "library",
6
+ "license": "MIT",
7
+ "authors": [
8
+ {
9
+ "name": "Bas",
10
+ "email": "bas@weblogmechanic.com"
11
+ }
12
+ ],
13
+ "minimum-stability": "dev",
14
+ "require": {
15
+ "shortpixel/log" : "1.1.*"
16
+ },
17
+ "repositories": [
18
+ {
19
+ "packagist.org": false,
20
+ "type": "path",
21
+ "url": "../modules/",
22
+ "options": {
23
+ "symlink": true
24
+ }
25
+ }
26
+ ],
27
+
28
+ "autoload": {
29
+ "psr-4": { "ShortPixel\\Notices\\" : "src/" }
30
+ }
31
+ }
build/shortpixel/notices/src/NoticeController.php ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel\Notices;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+
5
+ class NoticeController //extends ShortPixelController
6
+ {
7
+ protected static $notices = array();
8
+ protected static $instance = null;
9
+ protected static $cssHookLoaded = false; // prevent css output more than once.
10
+
11
+ public $notice_count = 0;
12
+
13
+ protected $has_stored = false;
14
+
15
+ protected $notice_option = ''; // The wp_options name for notices here.
16
+
17
+ /** For backward compat. Never call constructor directly. */
18
+ public function __construct()
19
+ {
20
+ // $this->loadModel('notice');
21
+ $ns = __NAMESPACE__;
22
+ $ns = substr($ns, 0, strpos($ns, '\\')); // try to get first part of namespace
23
+ $this->notice_option = $ns . '-notices';
24
+
25
+ $this->loadNotices();
26
+ //$this->loadConfig();
27
+ }
28
+
29
+ /** Load Notices Config File, if any
30
+ *
31
+ * [ Future Use ]
32
+ */
33
+ public function loadConfig()
34
+ {
35
+ if (file_exists('../notice_config.json'))
36
+ {
37
+ $config = file_get_contents('../notice_config.json');
38
+ $json_config = json_decode($config);
39
+ }
40
+ }
41
+
42
+ public function loadIcons($icons)
43
+ {
44
+ foreach($icons as $name => $icon)
45
+ NoticeModel::setIcon($name, $icon);
46
+ }
47
+
48
+
49
+ protected function loadNotices()
50
+ {
51
+ $notices = get_option($this->notice_option, false);
52
+ $cnotice = (is_array($notices)) ? count($notices) : 0;
53
+ Log::addDebug('Notice Control - #num notices' . $cnotice);
54
+ if ($notices !== false)
55
+ {
56
+ self::$notices = $notices;
57
+ $this->has_stored = true;
58
+ }
59
+ else {
60
+ self::$notices = array();
61
+ $this->has_stored = false;
62
+ }
63
+ $this->countNotices();
64
+ }
65
+
66
+
67
+ public function addNotice($message, $code)
68
+ {
69
+ $notice = new NoticeModel($message, $code);
70
+ self::$notices[] = $notice;
71
+ $this->countNotices();
72
+ Log::addDebug('Adding notice - ', $notice);
73
+ $this->update();
74
+ return $notice;
75
+ }
76
+
77
+ /** Update the notices to store, check what to remove, returns count. */
78
+ public function update()
79
+ {
80
+ if (! is_array(self::$notices) || count(self::$notices) == 0)
81
+ {
82
+ if ($this->has_stored)
83
+ delete_option($this->notice_option);
84
+
85
+ return 0;
86
+ }
87
+
88
+ $new_notices = array();
89
+ foreach(self::$notices as $item)
90
+ {
91
+ if (! $item->isDone() )
92
+ {
93
+ $new_notices[] = $item;
94
+ }
95
+ }
96
+
97
+ update_option($this->notice_option, $new_notices);
98
+ self::$notices = $new_notices;
99
+
100
+ return $this->countNotices();
101
+ }
102
+
103
+ public function countNotices()
104
+ {
105
+ $this->notice_count = count(self::$notices);
106
+ return $this->notice_count;
107
+ }
108
+
109
+
110
+ public function getNotices()
111
+ {
112
+ return self::$notices;
113
+ }
114
+
115
+ public static function getInstance()
116
+ {
117
+ if ( self::$instance === null)
118
+ {
119
+ self::$instance = new NoticeController();
120
+ }
121
+
122
+ return self::$instance;
123
+ }
124
+
125
+ /** Adds a notice, quick and fast method
126
+ * @param String $message The Message you want to notify
127
+ * @param int $code A value of messageType as defined in model
128
+ * @returm Object Instance of noticeModel
129
+ */
130
+
131
+ public static function addNormal($message)
132
+ {
133
+ $noticeController = self::getInstance();
134
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_NORMAL);
135
+ return $notice;
136
+
137
+ }
138
+
139
+ public static function addError($message)
140
+ {
141
+ $noticeController = self::getInstance();
142
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_ERROR);
143
+ return $notice;
144
+
145
+ }
146
+
147
+ public static function addWarning($message)
148
+ {
149
+ $noticeController = self::getInstance();
150
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_WARNING);
151
+ return $notice;
152
+
153
+ }
154
+
155
+ public static function addSuccess($message)
156
+ {
157
+ $noticeController = self::getInstance();
158
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_SUCCESS);
159
+ return $notice;
160
+
161
+ }
162
+
163
+ public function admin_notices()
164
+ {
165
+ if ($this->countNotices() > 0)
166
+ {
167
+ if (! self::$cssHookLoaded)
168
+ {
169
+ add_action('admin_print_footer_scripts', array($this, 'printNoticeStyle'));
170
+ self::$cssHookLoaded = true;
171
+ }
172
+ foreach($this->getNotices() as $notice)
173
+ {
174
+ echo $notice->getForDisplay();
175
+ }
176
+ }
177
+ $this->update(); // puts views, and updates
178
+ }
179
+
180
+ public function printNoticeStyle()
181
+ {
182
+ if (file_exists(__DIR__ . '/css/notices.css'))
183
+ {
184
+ echo '<style>' . file_get_contents(__DIR__ . '/css/notices.css') . '</style>';
185
+ }
186
+ else {
187
+ Log::addDebug('Notices : css/notices.css could not be loaded');
188
+ }
189
+ }
190
+
191
+
192
+
193
+
194
+ }
build/shortpixel/notices/src/NoticeModel.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel\Notices;
3
+
4
+ class NoticeModel //extends ShortPixelModel
5
+ {
6
+ protected $message;
7
+ public $code;
8
+
9
+ protected $viewed = false;
10
+ public $is_persistent = false; // This is a fatal issue, display until something was fixed.
11
+ public $is_removable = true; // if removable, display a notice dialog with red X or so.
12
+ public $messageType = self::NOTICE_NORMAL;
13
+
14
+ public static $icons = array();
15
+
16
+ const NOTICE_NORMAL = 1;
17
+ const NOTICE_ERROR = 2;
18
+ const NOTICE_SUCCESS = 3;
19
+ const NOTICE_WARNING = 4;
20
+
21
+
22
+ public function __construct($message, $messageType = self::NOTICE_NORMAL)
23
+ {
24
+ $this->message = $message;
25
+ $this->messageType = $messageType;
26
+
27
+ }
28
+
29
+ public function isDone()
30
+ {
31
+ if ($this->viewed && ! $this->is_persistent)
32
+ return true;
33
+ else
34
+ return false;
35
+
36
+ }
37
+
38
+ public static function setIcon($notice_type, $icon)
39
+ {
40
+ switch($notice_type)
41
+ {
42
+ case 'error':
43
+ $type = self::NOTICE_ERROR;
44
+ break;
45
+ case 'success':
46
+ $type = self::NOTICE_SUCCESS;
47
+ break;
48
+ case 'warning':
49
+ $type = self::NOTICE_WARNING;
50
+ break;
51
+ case 'normal':
52
+ default:
53
+ $type = self::NOTICE_NORMAL;
54
+ break;
55
+ }
56
+ self::$icons[$type] = $icon;
57
+ }
58
+
59
+ public function getForDisplay()
60
+ {
61
+ $this->viewed = true;
62
+ $class = 'shortpixel notice ';
63
+
64
+ $icon = '';
65
+
66
+ switch($this->messageType)
67
+ {
68
+ case self::NOTICE_ERROR:
69
+ $class .= 'notice-error ';
70
+ $icon = isset(self::$icons[self::NOTICE_ERROR]) ? self::$icons[self::NOTICE_ERROR] : '';
71
+ //$icon = 'scared';
72
+ break;
73
+ case self::NOTICE_SUCCESS:
74
+ $class .= 'notice-success ';
75
+ $icon = isset(self::$icons[self::NOTICE_SUCCESS]) ? self::$icons[self::NOTICE_SUCCESS] : '';
76
+ break;
77
+ case self::NOTICE_WARNING:
78
+ $class .= 'notice-warning ';
79
+ $icon = isset(self::$icons[self::NOTICE_WARNING]) ? self::$icons[self::NOTICE_WARNING] : '';
80
+ break;
81
+ case self::NOTICE_NORMAL:
82
+ $class .= 'notice-info ';
83
+ $icon = isset(self::$icons[self::NOTICE_NORMAL]) ? self::$icons[self::NOTICE_NORMAL] : '';
84
+ break;
85
+ default:
86
+ $class .= 'notice-info ';
87
+ $icon = '';
88
+ break;
89
+ }
90
+
91
+ /*$image = '<img src="' . plugins_url('/shortpixel-image-optimiser/res/img/robo-' . $icon . '.png') . '"
92
+ 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">';
93
+ */
94
+
95
+ if ($this->is_removable)
96
+ {
97
+ $class .= 'is-dismissible ';
98
+ }
99
+
100
+ if ($this->is_persistent)
101
+ {
102
+ $class .= '';
103
+ }
104
+
105
+ return "<div class='$class'>" . $icon . "<p>" . $this->message . "</p></div>";
106
+
107
+ }
108
+
109
+
110
+
111
+ // @todo Transient save, since that is used in some parts.
112
+ // save
113
+ // load
114
+
115
+
116
+ }
build/shortpixel/notices/src/css/notices.css ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .shortpixel.notice {
2
+ padding: 8px; }
3
+ .shortpixel.notice img {
4
+ display: inline-block;
5
+ margin: 0 25px 0 0;
6
+ max-height: 50px; }
7
+ .shortpixel.notice .notice-dismiss {
8
+ margin-top: 10px; }
9
+
10
+ /* In-view notice ( not on top, between the options ) - styled after WP notice */
11
+ .view-notice {
12
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
13
+ border: 4px solid #fff;
14
+ padding: 1px 12px; }
15
+ .view-notice p {
16
+ margin: 1em 0 !important; }
17
+ .view-notice.warning {
18
+ border-left-color: #ffb900; }
19
+
20
+ .view-notice-row {
21
+ display: none; }
build/shortpixel/notices/src/css/notices.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .shortpixel.notice{padding:8px}.shortpixel.notice img{display:inline-block;margin:0 25px 0 0;max-height:50px}.shortpixel.notice .notice-dismiss{margin-top:10px}.view-notice{box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);border:4px solid #fff;padding:1px 12px}.view-notice p{margin:1em 0 !important}.view-notice.warning{border-left-color:#ffb900}.view-notice-row{display:none}
build/shortpixel/notices/src/css/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
+ }
class/controller/apikey_controller.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ /* Main function of this controller is to load key on runtime
6
+ This should probably in future incorporate some apikey checking functions that shouldn't be in model.
7
+ */
8
+ class ApiKeyController extends shortPixelController
9
+ {
10
+
11
+ public function __construct()
12
+ {
13
+ $this->loadModel('apikey');
14
+ $this->model = new apiKeyModel();
15
+ }
16
+
17
+ // glue method.
18
+ public function setShortPixel($pixel)
19
+ {
20
+ parent::setShortPixel($pixel);
21
+ $this->model->shortPixel = $pixel;
22
+ }
23
+
24
+ public function load()
25
+ {
26
+ $this->model->loadKey();
27
+ }
28
+
29
+
30
+ }
class/controller/bulk-restore-all.php CHANGED
@@ -1,7 +1,6 @@
1
  <?php
2
  namespace ShortPixel;
3
 
4
-
5
  class BulkRestoreAll extends ShortPixelController
6
  {
7
  protected static $slug = 'bulk-restore-all';
1
  <?php
2
  namespace ShortPixel;
3
 
 
4
  class BulkRestoreAll extends ShortPixelController
5
  {
6
  protected static $slug = 'bulk-restore-all';
class/controller/controller.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  namespace ShortPixel;
3
- use ShortPixel\ShortPixelLogger as Log;
4
 
5
  class ShortPixelController
6
  {
@@ -18,6 +18,8 @@ class ShortPixelController
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
  {
23
  foreach (get_declared_classes() as $class) {
@@ -59,8 +61,19 @@ class ShortPixelController
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
  }
@@ -122,10 +135,11 @@ class ShortPixelController
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
@@ -154,9 +168,11 @@ class ShortPixelController
154
  return true;
155
  }
156
  }
157
-
158
- $model = $this->model;
159
- $this->postData = $model->getSanitizedData($post);
 
 
160
 
161
  return $this->postData;
162
 
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
 
5
  class ShortPixelController
6
  {
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
+ protected $form_action = 'sp-action';
22
+
23
  public static function init()
24
  {
25
  foreach (get_declared_classes() as $class) {
61
  */
62
  protected function checkPost()
63
  {
64
+ if (count($_POST) == 0) // no post, nothing to check, return silent.
65
+ return;
66
+
67
+ if (! isset($_POST['sp-nonce']) || ! wp_verify_nonce( $_POST['sp-nonce'], $this->form_action))
68
  {
69
+ Log::addInfo('Check Post fails nonce check' . $this->form_action, array($_POST) );
70
+ return false;
71
+ }
72
+ else if (isset($_POST) && count($_POST) > 0)
73
+ {
74
+ check_admin_referer( $this->form_action, 'sp-nonce' ); // extra check, when we are wrong here, it dies.
75
+ unset($_POST['sp-nonce']);
76
+ unset($_POST['_wp_http_referer']);
77
  $this->is_form_submit = true;
78
  $this->processPostData($_POST);
79
  }
135
  require_once($path);
136
  }
137
  else {
138
+ Log::addError("Model $name could not be found");
139
  }
140
  }
141
+ }
142
+
143
 
144
  /** Accepts POST data, maps, checks missing fields, and applies sanitization to it.
145
  * @param array $post POST data
168
  return true;
169
  }
170
  }
171
+ else
172
+ {
173
+ $model = $this->model;
174
+ $this->postData = $model->getSanitizedData($post);
175
+ }
176
 
177
  return $this->postData;
178
 
class/controller/filesystem_controller.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+
6
+ /** Controller for FileSystem operations
7
+ *
8
+ * This controller is used for -compound- ( complex ) FS operations, using the provided models File en Directory.
9
+ */
10
+ Class FileSystemController extends ShortPixelController
11
+ {
12
+ public function __construct()
13
+ {
14
+ $this->loadModel('file');
15
+ $this->loadModel('directory');
16
+ }
17
+
18
+ /** Get FileModel for a certain path. This can exist or not
19
+ *
20
+ * @param String Path Full Path to the file
21
+ * @return FileModel FileModel Object. If file does not exist, not all values are set.
22
+ */
23
+ public function getFile($path)
24
+ {
25
+ return new FileModel($path);
26
+ }
27
+
28
+ /** Get DirectoryModel for a certain path. This can exist or not
29
+ *
30
+ * @param String $path Full Path to the Directory.
31
+ * @return DirectoryModel Object with status set on current directory.
32
+ */
33
+ public function getDirectory($path)
34
+ {
35
+ return new DirectoryModel($path);
36
+ }
37
+
38
+ /** Get the BackupLocation for a FileModel. FileModel should be not a backup itself or it will recurse
39
+ *
40
+ * For now this function should only be used for *new* backup files. Retrieving backup files via this method
41
+ * doesn't take into account legacy ways of storage.
42
+ *
43
+ * @param FileModel $file FileModel with file that needs a backup.
44
+ * @return DirectoryModel | Boolean DirectoryModel pointing to the backup directory. Returns false if the directory could not be created, or initialized.
45
+ */
46
+ public function getBackupDirectory(FileModel $file)
47
+ {
48
+ $wp_home = get_home_path();
49
+ $filepath = $file->getFullPath();
50
+
51
+ // Implement this bastard better here.
52
+ $backup_subdir = \ShortPixelMetaFacade::returnSubDir($filepath);
53
+
54
+ $backup_fulldir = SHORTPIXEL_BACKUP_FOLDER . '/' . $backup_subdir;
55
+ Log::addDebug('Get File BackupDirectory' . $backup_fulldir);
56
+
57
+ $directory = $this->getDirectory($backup_fulldir);
58
+
59
+ if ($directory->check())
60
+ return $directory;
61
+ else {
62
+ return false;
63
+ }
64
+
65
+ }
66
+ }
class/controller/settings.php CHANGED
@@ -1,9 +1,7 @@
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
  {
@@ -22,21 +20,39 @@ class SettingsController extends shortPixelController
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
@@ -45,9 +61,7 @@ class SettingsController extends shortPixelController
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
  {
@@ -63,15 +77,16 @@ class SettingsController extends shortPixelController
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();
@@ -91,12 +106,26 @@ class SettingsController extends shortPixelController
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');
@@ -108,7 +137,9 @@ class SettingsController extends shortPixelController
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;
@@ -122,65 +153,77 @@ class SettingsController extends shortPixelController
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
@@ -193,62 +236,17 @@ class SettingsController extends shortPixelController
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.
@@ -430,7 +428,8 @@ class SettingsController extends shortPixelController
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
  {
@@ -461,7 +460,6 @@ class SettingsController extends shortPixelController
461
  \WPShortPixel::alterHtaccess();
462
  }
463
 
464
-
465
  $post['deliverWebp'] = $deliverwebp;
466
  unset($post['deliverWebpAlteringType']);
467
  unset($post['deliverWebpType']);
@@ -506,6 +504,7 @@ class SettingsController extends shortPixelController
506
  exit();
507
  }
508
 
 
509
  protected function NoticeApiKeyLength($key)
510
  {
511
  $KeyLength = strlen($key);
@@ -519,5 +518,5 @@ class SettingsController extends shortPixelController
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
  }
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use ShortPixel\Notices\NoticeController as Notice;
 
 
5
 
6
  class SettingsController extends shortPixelController
7
  {
20
 
21
  protected $quotaData = null;
22
 
23
+ protected $keyModel;
24
+
25
  protected $mapper = array(
26
  'key' => 'apiKey',
27
  'cmyk2rgb' => 'CMYKtoRGBconversion',
28
  );
29
 
30
  protected $display_part = 'settings';
31
+ protected $form_action = 'save-settings';
32
 
33
  public function __construct()
34
  {
35
  // @todo Remove Debug Call
36
  $this->model = new \WPShortPixelSettings();
37
 
38
+ $this->loadModel('apikey');
39
+ $this->keyModel = new ApiKeyModel();
40
 
41
  parent::__construct();
42
+ }
43
 
44
+ // glue method.
45
+ public function setShortPixel($pixel)
46
+ {
47
+ parent::setShortPixel($pixel);
48
+ $this->keyModel->shortPixel = $pixel;
49
+
50
+ // It's loading here since it can do validation, which requires Shortpixel.
51
+ // Otherwise this should be loaded on construct.
52
+ $this->keyModel->loadKey();
53
+ $this->is_verifiedkey = $this->keyModel->is_verified();
54
+ $this->is_constant_key = $this->keyModel->is_constant();
55
+ $this->hide_api_key = $this->keyModel->is_hidden();
56
  }
57
 
58
  // default action of controller
61
  $this->loadEnv();
62
  $this->checkPost(); // sets up post data
63
 
64
+ $this->model->redirectedSettings = 2; // Prevents any redirects after loading settings
 
 
65
 
66
  if ($this->is_form_submit)
67
  {
77
  $this->loadEnv();
78
  $this->checkPost();
79
 
80
+ Log::addDebug('Settings Action - addkey ', array($this->is_form_submit, $this->postData) );
81
  if ($this->is_form_submit && isset($this->postData['apiKey']))
82
  {
83
+ $this->keyModel->resetTried();
84
+ $this->keyModel->checkKey($this->postData['apiKey']);
85
+ /*if (isset($this->postData['verifiedKey']) && $this->postData['verifiedKey'])
86
  {
87
  $this->model->apiKey = $this->postData['apiKey'];
88
  $this->model->verifiedKey = $this->postData['verifiedKey'];
89
+ } */
90
  }
91
 
92
  $this->doRedirect();
106
  $nextgen->nextGenEnabled($previous);
107
  }
108
 
109
+ $check_key = false;
110
+ if (isset($this->postData['apiKey']))
111
+ {
112
+ $check_key = $this->postData['apiKey'];
113
+ unset($this->postData['apiKey']); // unset, since keyModel does the saving.
114
+ }
115
+
116
  // write checked and verified post data to model. With normal models, this should just be call to update() function
117
  foreach($this->postData as $name => $value)
118
  {
119
  $this->model->{$name} = $value;
120
  }
121
 
122
+ // first save all other settings ( like http credentials etc ), then check
123
+ if (! $this->keyModel->is_constant() && $check_key !== false) // don't allow settings key if there is a constant
124
+ {
125
+ $this->keyModel->resetTried(); // reset the tried api keys on a specific post request.
126
+ $this->keyModel->checkKey($check_key);
127
+ }
128
+
129
  // end
130
  if ($this->do_redirect)
131
  $this->doRedirect('bulk');
137
  /* Loads the view data and the view */
138
  public function load_settings()
139
  {
140
+ if ($this->is_verifiedkey) // supress quotaData alerts when handing unset API's.
141
+ $this->loadQuotaData();
142
+
143
  $this->view->data = (Object) $this->model->getData();
144
  if (($this->is_constant_key))
145
  $this->view->data->apiKey = SHORTPIXEL_API_KEY;
153
  if (is_wp_error($this->view->resources))
154
  $this->view->resources = null;
155
 
156
+ $settings = $this->shortPixel->getSettings();
157
+ $this->view->dismissedNotices = $settings->dismissedNotices;
158
+
159
  $this->loadView('view-settings');
160
  }
161
 
162
  /** Checks on things and set them for information. */
163
+ protected function loadEnv()
164
  {
165
+ $env = $this->getEnv();
 
 
166
 
167
+ $this->is_nginx = $env->is_nginx;
168
+ $this->is_gd_installed = $env->is_gd_installed;
169
+ $this->is_curl_installed = $env->is_curl_installed;
170
 
171
+ $this->is_htaccess_writable = $this->HTisWritable();
 
172
 
173
+ $this->is_multisite = $env->is_multisite;
174
+ $this->is_mainsite = $env->is_mainsite;
175
+ $this->has_nextgen = $env->has_nextgen;
176
 
177
  $this->display_part = isset($_GET['part']) ? sanitize_text_field($_GET['part']) : 'settings';
178
+
179
+ }
180
+
181
+ public function getEnv()
182
+ {
183
+ $this->loadModel('environment');
184
+ $env = new EnvironmentModel();
185
+
186
+ return $env;
187
  }
188
 
189
  /** Check if everything is OK with the Key **/
190
+ /*public function checkKey()
191
  {
192
+ //$this->is_constant_key = (defined("SHORTPIXEL_API_KEY")) ? true : false;
193
+ // $this->hide_api_key = (defined("SHORTPIXEL_HIDE_API_KEY")) ? SHORTPIXEL_HIDE_API_KEY : false;
194
 
195
  $verified_key = $this->model->verifiedKey;
196
  $this->is_verifiedkey = ($verified_key) ? true : false;
197
 
198
  $key_in_db = $this->model->apiKey;
199
 
200
+ // if form submit, but no validation already pushed, check if api key was changed.
201
+ if ($this->is_form_submit && ! $this->postkey_needs_validation)
202
+ {
203
+ // api key was changed on the form.
204
+ if ($this->postData['apiKey'] != $key_in_db)
205
+ {
206
+ $this->postkey_needs_validation = true;
207
+ }
208
+ }
209
+
210
  if($this->is_constant_key)
211
  {
212
+ if ($key_in_db != SHORTPIXEL_API_KEY)
 
 
 
 
213
  {
214
  $this->validateKey(SHORTPIXEL_API_KEY);
215
  }
216
  }
217
  elseif ($this->postkey_needs_validation)
218
  {
219
+ $key = isset($this->postData['apiKey']) ? $this->postData['apiKey'] : $this->model->apiKey;
 
 
 
 
 
 
220
  $this->validateKey($key);
221
+
222
  } // postkey_needs_validation
223
+ } */
224
 
225
  /** Check remotely if key is alright **/
226
+ /*public function validateKey($key)
227
  {
228
  Log::addDebug('Validating Key ' . $key);
229
  // first, save Auth to satisfy getquotainformation
236
  }
237
  }
238
 
239
+ /*if (! $this->is_verifiedkey)
 
 
 
 
 
240
  {
241
+ Notice::addError(sprintf(__('Error during verifying API key: %s','shortpixel-image-optimiser'), $this->quotaData['Message'] ));
 
 
 
 
 
 
 
 
 
 
 
242
  }
243
  elseif ($this->is_form_submit) {
244
  $this->processNewKey();
245
  }
246
 
247
+ } */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
 
 
249
 
 
 
 
 
 
 
 
 
250
 
251
  /* Temporary function to check if HTaccess is writable.
252
  * HTaccess is writable if it exists *and* is_writable, or can be written if directory is writable.
428
  protected function processWebP($post)
429
  {
430
  $deliverwebp = 0;
431
+ if (! $this->is_nginx)
432
+ \WPShortPixel::alterHtaccess(true); // always remove the statements.
433
 
434
  if (isset($post['createWebp']) && $post['createWebp'] == 1)
435
  {
460
  \WPShortPixel::alterHtaccess();
461
  }
462
 
 
463
  $post['deliverWebp'] = $deliverwebp;
464
  unset($post['deliverWebpAlteringType']);
465
  unset($post['deliverWebpType']);
504
  exit();
505
  }
506
 
507
+ /*
508
  protected function NoticeApiKeyLength($key)
509
  {
510
  $KeyLength = strlen($key);
518
  . __(' or ','shortpixel-image-optimiser')
519
  . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.";
520
  Notice::addError($notice);
521
+ } */
522
  }
class/db/shortpixel-custom-meta-dao.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- use ShortPixel\ShortPixelLogger as Log;
3
 
4
 
5
  class ShortPixelCustomMetaDao {
@@ -482,6 +482,7 @@ class ShortPixelCustomMetaDao {
482
  $tableSuffix = "";
483
  $tableSuffix = $metaClass::TABLE_SUFFIX;
484
  $prefix = $this->db->getPrefix();
 
485
  // eval( '$tableSuffix = ' . $metaClass . '::TABLE_SUFFIX;'); // horror!
486
  $sql = "UPDATE " . $prefix . "shortpixel_" . $tableSuffix . " SET ";
487
  foreach(self::$fields[$tableSuffix] as $field => $type) {
@@ -500,12 +501,14 @@ class ShortPixelCustomMetaDao {
500
  $sql = rtrim($sql, ",");
501
  $sql .= " WHERE id = %d";
502
  $params[] = $meta->getId();
 
503
  $this->db->query($sql, $params);
504
  }
505
 
506
  public function delete($meta) {
507
  $metaClass = get_class($meta);
508
  $tableSuffix = "";
 
509
  eval( '$tableSuffix = ' . $metaClass . '::TABLE_SUFFIX;');
510
  $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_" . $tableSuffix . " WHERE id = %d";
511
  $this->db->query($sql, array($meta->getId()));
1
  <?php
2
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
3
 
4
 
5
  class ShortPixelCustomMetaDao {
482
  $tableSuffix = "";
483
  $tableSuffix = $metaClass::TABLE_SUFFIX;
484
  $prefix = $this->db->getPrefix();
485
+
486
  // eval( '$tableSuffix = ' . $metaClass . '::TABLE_SUFFIX;'); // horror!
487
  $sql = "UPDATE " . $prefix . "shortpixel_" . $tableSuffix . " SET ";
488
  foreach(self::$fields[$tableSuffix] as $field => $type) {
501
  $sql = rtrim($sql, ",");
502
  $sql .= " WHERE id = %d";
503
  $params[] = $meta->getId();
504
+ Log::addDebug('Update Custom Meta' . $sql, $params);
505
  $this->db->query($sql, $params);
506
  }
507
 
508
  public function delete($meta) {
509
  $metaClass = get_class($meta);
510
  $tableSuffix = "";
511
+ // @todo Why O why
512
  eval( '$tableSuffix = ' . $metaClass . '::TABLE_SUFFIX;');
513
  $sql = "DELETE FROM {$this->db->getPrefix()}shortpixel_" . $tableSuffix . " WHERE id = %d";
514
  $this->db->query($sql, array($meta->getId()));
class/db/shortpixel-meta-facade.php CHANGED
@@ -3,13 +3,13 @@
3
  class ShortPixelMetaFacade {
4
  const MEDIA_LIBRARY_TYPE = 1;
5
  const CUSTOM_TYPE = 2;
6
-
7
  private $ID;
8
  private $type;
9
  private $meta;
10
  private $spMetaDao;
11
  private $rawMeta;
12
-
13
  public function __construct($ID) {
14
  if(strpos($ID, 'C-') === 0) {
15
  $this->ID = substr($ID, 2);
@@ -17,14 +17,14 @@ class ShortPixelMetaFacade {
17
  } else {
18
  $this->ID = $ID;
19
  $this->type = self::MEDIA_LIBRARY_TYPE;
20
- }
21
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb());
22
  }
23
-
24
  public static function getNewFromRow($item) {
25
- return new ShortPixelMetaFacade("C-" . $item->id);
26
  }
27
-
28
  function setRawMeta($rawMeta) {
29
  if($this->type == self::MEDIA_LIBRARY_TYPE) {
30
  $this->rawMeta = $rawMeta;
@@ -40,11 +40,11 @@ class ShortPixelMetaFacade {
40
  $rawMeta = $this->sanitizeMeta(wp_get_attachment_metadata($this->ID));
41
  $this->meta = self::rawMetaToMeta($this->ID, $rawMeta);
42
  $this->rawMeta = $rawMeta;
43
- }
44
  }
45
  return $this->meta;
46
  }
47
-
48
  private static function rawMetaToMeta($ID, $rawMeta) {
49
  $path = get_attached_file($ID);
50
  return new ShortPixelMeta(array(
@@ -55,7 +55,7 @@ class ShortPixelMetaFacade {
55
  "thumbs" => (isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array()),
56
  "message" =>(isset($rawMeta["ShortPixelImprovement"]) ? $rawMeta["ShortPixelImprovement"] : null),
57
  "png2jpg" => (isset($rawMeta["ShortPixelPng2Jpg"]) ? $rawMeta["ShortPixelPng2Jpg"] : false),
58
- "compressionType" =>(isset($rawMeta["ShortPixel"]["type"])
59
  ? ($rawMeta["ShortPixel"]["type"] == 'glossy' ? 2 : ($rawMeta["ShortPixel"]["type"] == "lossy" ? 1 : 0) )
60
  : null),
61
  "thumbsOpt" =>(isset($rawMeta["ShortPixel"]["thumbsOpt"]) ? $rawMeta["ShortPixel"]["thumbsOpt"] : null),
@@ -66,12 +66,12 @@ class ShortPixelMetaFacade {
66
  "thumbsTodo" =>(isset($rawMeta["ShortPixel"]["thumbsTodo"]) ? $rawMeta["ShortPixel"]["thumbsTodo"] : false),
67
  "tsOptimized" => (isset($rawMeta["ShortPixel"]["date"]) ? $rawMeta["ShortPixel"]["date"] : false),
68
  "backup" => !isset($rawMeta['ShortPixel']['NoBackup']),
69
- "status" => (!isset($rawMeta["ShortPixel"]) ? 0
70
- : (isset($rawMeta["ShortPixelImprovement"]) && is_numeric($rawMeta["ShortPixelImprovement"])
71
- && !( $rawMeta['ShortPixelImprovement'] == 0
72
- && ( isset($rawMeta['ShortPixel']['WaitingProcessing'])
73
- || isset($rawMeta['ShortPixel']['date']) && $rawMeta['ShortPixel']['date'] == '1970-01-01')) ? 2
74
- : (isset($rawMeta["ShortPixel"]["WaitingProcessing"]) ? 1
75
  : (isset($rawMeta["ShortPixel"]['ErrCode']) ? $rawMeta["ShortPixel"]['ErrCode'] : -500)))),
76
  "retries" =>(isset($rawMeta["ShortPixel"]["Retries"]) ? $rawMeta["ShortPixel"]["Retries"] : 0),
77
  ));
@@ -83,9 +83,9 @@ class ShortPixelMetaFacade {
83
  return $this->meta;
84
  } else {
85
  return wp_get_attachment_url($this->ID);
86
- }
87
  }
88
-
89
  static function sanitizeMeta($rawMeta, $createSPArray = true){
90
  if(!is_array($rawMeta)) {
91
  if($rawMeta == '') { return $createSPArray ? array('ShortPixel' => array()) : array(); }
@@ -99,7 +99,7 @@ class ShortPixelMetaFacade {
99
  }
100
  return $rawMeta;
101
  }
102
-
103
  function updateMeta($newMeta = null, $replaceThumbs = false) {
104
  if($newMeta) {
105
  $this->meta = $newMeta;
@@ -117,7 +117,7 @@ class ShortPixelMetaFacade {
117
 
118
  if(isset($rawMeta['sizes']) && is_array($rawMeta['sizes'])) {
119
  if($replaceThumbs) {
120
- $rawMeta['sizes'] = $this->meta->getThumbs();
121
  } else {
122
  //use this instead of array_merge because we don't want to duplicate numeric keys
123
  foreach($this->meta->getThumbs() as $key => $val) {
@@ -133,7 +133,7 @@ class ShortPixelMetaFacade {
133
  } else {
134
  $rawMeta['ShortPixel']['type'] = ShortPixelAPI::getCompressionTypeName($this->meta->getCompressionType());
135
  }
136
-
137
  if(null === $this->meta->getKeepExif()) {
138
  unset($rawMeta['ShortPixel']['exifKept']);
139
  } else {
@@ -145,7 +145,7 @@ class ShortPixelMetaFacade {
145
  } else {
146
  $rawMeta['ShortPixel']['date'] = date("Y-m-d H:i:s", strtotime($this->meta->getTsOptimized()));
147
  }
148
-
149
  //thumbs were processed if settings or if they were explicitely requested
150
  if(null === $this->meta->getThumbsOpt()) {
151
  unset($rawMeta['ShortPixel']['thumbsOpt']);
@@ -168,7 +168,7 @@ class ShortPixelMetaFacade {
168
  } else {
169
  unset($rawMeta['ShortPixel']['thumbsMissing']);
170
  }
171
-
172
  if(null === $this->meta->getRetinasOpt()) {
173
  unset($rawMeta['ShortPixel']['retinasOpt']);
174
  } else {
@@ -211,9 +211,9 @@ class ShortPixelMetaFacade {
211
  $this->rawMeta = $rawMeta;
212
  }
213
  }
214
- }
215
  }
216
-
217
  function cleanupMeta($fakeOptPending = false) {
218
  if($this->type == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
219
  if(!isset($this->rawMeta)) {
@@ -248,7 +248,7 @@ class ShortPixelMetaFacade {
248
  unset($this->rawMeta['ShortPixel']);
249
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
250
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
251
- }
252
  }
253
 
254
  function deleteAllSPMeta() {
@@ -271,10 +271,10 @@ class ShortPixelMetaFacade {
271
  if(!isset($this->rawMeta['ShortPixel'])) {$this->rawMeta['ShortPixel'] = array();}
272
  $this->rawMeta['ShortPixel']['Retries'] = isset($this->rawMeta['ShortPixel']['Retries']) ? $this->rawMeta['ShortPixel']['Retries'] + $count : $count;
273
  $this->meta->setRetries($this->rawMeta['ShortPixel']['Retries']);
274
- }
275
  $this->setError($errorCode, $errorMessage);
276
  }
277
-
278
  function setWaitingProcessing($status = true) {
279
  if($status) {
280
  $this->meta->setStatus(1);
@@ -293,9 +293,9 @@ class ShortPixelMetaFacade {
293
  }
294
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
295
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
296
- }
297
  }
298
-
299
  function setError($errorCode, $errorMessage) {
300
  $this->meta->setMessage(__('Error','shortpixel-image-optimiser') . ': <i>' . $errorMessage . '</i>');
301
  $this->meta->setStatus($errorCode);
@@ -311,9 +311,9 @@ class ShortPixelMetaFacade {
311
  unset($this->rawMeta['ShortPixel']['WaitingProcessing']);
312
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
313
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
314
- }
315
  }
316
-
317
  function setMessage($message) {
318
  $this->meta->setMessage($message);
319
  $this->meta->setStatus(-1);
@@ -326,12 +326,12 @@ class ShortPixelMetaFacade {
326
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
327
  }
328
  }
329
-
330
  public static function getHomeUrl() {
331
  //trim is because we found a site set up with a tab, like this: https://modernpeasantcooking.com\t
332
  return trailingslashit((function_exists("is_multisite") && is_multisite()) ? trim(network_site_url("/")) : trim(home_url()));
333
  }
334
-
335
  //this is in test
336
  public static function getHomeUrl2() {
337
  return trailingslashit(ShortPixelTools::commonPrefix(self::getHomeUrl(), content_url()));
@@ -343,7 +343,7 @@ class ShortPixelMetaFacade {
343
  * @throws Exception
344
  */
345
  public static function safeGetAttachmentUrl($id) {
346
- $attURL = wp_get_attachment_url($id);
347
  if(!$attURL || !strlen($attURL)) {
348
  throw new Exception("Post metadata is corrupt (No attachment URL for $id)", ShortPixelAPI::ERR_POSTMETA_CORRUPT);
349
  }
@@ -354,17 +354,17 @@ class ShortPixelMetaFacade {
354
  return $attURL;//get the file URL
355
  }
356
  }
357
-
358
  public function getURLsAndPATHs($processThumbnails, $onlyThumbs = false, $addRetina = true, $excludeSizes = array(), $includeOptimized = false) {
359
  $sizesMissing = array();
360
-
361
  if($this->type == self::CUSTOM_TYPE) {
362
  $meta = $this->getMeta();
363
 
364
  //fix for situations where site_url is lala.com/en and home_url is lala.com - if using the site_url will get a duplicated /en in the URL
365
  $homeUrl = self::getHomeUrl();
366
  $urlList[] = self::replaceHomePath($meta->getPath(), $homeUrl);
367
-
368
  $filePaths[] = $meta->getPath();
369
  } else {
370
  $path = get_attached_file($this->ID);//get the full file PATH
@@ -374,7 +374,7 @@ class ShortPixelMetaFacade {
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);
@@ -388,19 +388,19 @@ class ShortPixelMetaFacade {
388
  $this->addRetina($path, $url, $filePaths, $urlList);
389
  }
390
  }
391
-
392
  $meta = $this->getMeta();
393
  $sizes = $meta->getThumbs();
394
 
395
  //it is NOT a PDF file and thumbs are processable
396
- if ( /* strtolower(substr($path,strrpos($path, ".")+1)) != "pdf"
397
- &&*/ ($processThumbnails || $onlyThumbs)
398
  && count($sizes))
399
  {
400
  $Tmp = explode("/", SHORTPIXEL_UPLOADS_BASE);
401
  $TmpCount = count($Tmp);
402
  $StichString = $Tmp[$TmpCount-2] . "/" . $Tmp[$TmpCount-1];
403
-
404
  $count = 1;
405
  foreach( $sizes as $thumbnailName => $thumbnailInfo ) {
406
 
@@ -419,14 +419,14 @@ class ShortPixelMetaFacade {
419
  if(strpos($thumbnailName, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) {
420
  continue;
421
  }
422
-
423
  if(!$includeOptimized && in_array($thumbnailInfo['file'], $meta->getThumbsOptList())) {
424
  continue;
425
  }
426
-
427
  if($count >= SHORTPIXEL_MAX_THUMBS) break;
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);
@@ -440,6 +440,7 @@ class ShortPixelMetaFacade {
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)) {
@@ -464,13 +465,13 @@ class ShortPixelMetaFacade {
464
  if(!count($sizes)) {
465
  WPShortPixel::log("getURLsAndPATHs: no meta sizes for ID " . $this->ID . " : " . json_encode($this->rawMeta));
466
  }
467
-
468
  if($onlyThumbs && $mainExists && count($urlList) >= 1) { //remove the main image
469
  array_shift($urlList);
470
  array_shift($filePaths);
471
- }
472
  }
473
-
474
  //convert the + which are replaced with spaces by wp_remote_post
475
  array_walk($urlList, array( &$this, 'replacePlusChar') );
476
 
@@ -479,11 +480,11 @@ class ShortPixelMetaFacade {
479
  //die(var_dump(array("URLs" => $urlList, "PATHs" => $filePaths)));
480
  return array("URLs" => $urlList, "PATHs" => $filePaths, "sizesMissing" => $sizesMissing);
481
  }
482
-
483
  protected function replacePlusChar(&$url) {
484
  $url = str_replace("+", "%2B", $url);
485
  }
486
-
487
  protected function addRetina($path, $url, &$fileList, &$urlList) {
488
  $ext = pathinfo($path, PATHINFO_EXTENSION);
489
  $retinaPath = substr($path, 0, strlen($path) - 1 - strlen($ext)) . "@2x." . $ext;
@@ -497,18 +498,18 @@ class ShortPixelMetaFacade {
497
  $baseName = pathinfo(ShortPixelAPI::MB_basename($path), PATHINFO_FILENAME);
498
  return (substr($baseName, -3) === '@2x');
499
  }
500
-
501
  public static function getWPMLDuplicates( $id ) {
502
  global $wpdb;
503
-
504
  $parentId = get_post_meta ($id, '_icl_lang_duplicate_of', true );
505
  if($parentId) $id = $parentId;
506
-
507
  $mainFile = get_attached_file($id);
508
 
509
  $duplicates = $wpdb->get_col( $wpdb->prepare( "
510
  SELECT pm.post_id FROM {$wpdb->postmeta} pm
511
- WHERE pm.meta_value = %s AND pm.meta_key = '_icl_lang_duplicate_of'
512
  ", $id ) );
513
 
514
  //Polylang
@@ -548,7 +549,7 @@ class ShortPixelMetaFacade {
548
  }
549
  return array_unique($duplicates);
550
  }
551
-
552
  public static function pathToWebPath($path) {
553
  //$upl = wp_upload_dir();
554
  //return str_replace($upl["basedir"], $upl["baseurl"], $path);
@@ -562,11 +563,11 @@ class ShortPixelMetaFacade {
562
  $path = implode('/', $pathParts);
563
  return self::filenameToRootRelative($path);
564
  }
565
-
566
  public static function filenameToRootRelative($path) {
567
  return self::replaceHomePath($path, "");
568
  }
569
-
570
  private static function replaceHomePath($path, $with) {
571
  if(strpos($path, get_home_path()) === 0) {
572
  return str_replace(get_home_path(), $with, $path);
@@ -574,7 +575,7 @@ class ShortPixelMetaFacade {
574
  return str_replace(trailingslashit(realpath(get_home_path())), $with, $path);
575
  }
576
  }
577
-
578
  public function getWebpSizeMeta($path) {
579
  $meta = $this->getMeta();
580
  $thumbs = $meta->getThumbs(); $thumbs = is_array($thumbs) ? $thumbs : array();
@@ -591,7 +592,7 @@ class ShortPixelMetaFacade {
591
  return false;
592
  }
593
  $size = getimagesize($path);
594
- return array('key' => ShortPixelMeta::WEBP_THUMB_PREFIX . 'main',
595
  'val' => array( // it's a file that has no corresponding thumb so it's the WEBP for the main file
596
  'file' => pathinfo(ShortPixelAPI::MB_basename($path), PATHINFO_FILENAME) . '.webp',
597
  'width' => $size[0],
@@ -608,30 +609,30 @@ class ShortPixelMetaFacade {
608
  $resultQuery = $wpdb->get_results($queryMax);
609
  return $resultQuery[0]->QueryID;
610
  }
611
-
612
  public static function getMinMediaId() {
613
  global $wpdb;
614
  $queryMax = "SELECT min(post_id) as QueryID FROM " . $wpdb->prefix . "postmeta";
615
  $resultQuery = $wpdb->get_results($queryMax);
616
  return $resultQuery[0]->QueryID;
617
  }
618
-
619
  public static function isCustomQueuedId($id) {
620
  return substr($id, 0, 2) == "C-";
621
  }
622
-
623
  public static function stripQueuedIdType($id) {
624
  return intval(substr($id, 2));
625
  }
626
-
627
  public function getQueuedId() {
628
  return self::queuedId($this->type, $this->ID);
629
  }
630
-
631
  public static function queuedId($type, $id) {
632
  return ($type == self::CUSTOM_TYPE ? "C-" : "") . $id;
633
  }
634
-
635
  function getId() {
636
  return $this->ID;
637
  }
@@ -647,11 +648,11 @@ class ShortPixelMetaFacade {
647
  function setType($type) {
648
  $this->type = $type;
649
  }
650
-
651
  function getRawMeta() {
652
  return $this->rawMeta;
653
  }
654
-
655
  /**
656
  * return subdir for that particular attached file - if it's media library then last 3 path items, otherwise substract the uploads path
657
  * Has trailing directory separator (/)
@@ -684,7 +685,9 @@ class ShortPixelMetaFacade {
684
  }
685
  $hp = wp_normalize_path($homePath);
686
  $file = wp_normalize_path($file);
687
- $sp__uploads = wp_upload_dir();
 
 
688
  if(strstr($file, $hp)) {
689
  $path = str_replace( $hp, "", $file);
690
  } elseif( strstr($file, dirname( WP_CONTENT_DIR ))) { //in some situations the content dir is not inside the root, check this also (ex. single.shortpixel.com)
@@ -712,7 +715,7 @@ class ShortPixelMetaFacade {
712
  //contains one of the year subfolders of the media library
713
  if(strpos($path, $uploadPath) == 0) {
714
  $pathArr = explode('/', str_replace($uploadBase . '/', "", $path));
715
- if( count($pathArr) >= 1
716
  && is_numeric($pathArr[0]) && $pathArr[0] > 1900 && $pathArr[0] < 2100 //contains the year subfolder
717
  && ( count($pathArr) == 1 //if there is another subfolder then it's the month subfolder
718
  || (is_numeric($pathArr[1]) && $pathArr[1] > 0 && $pathArr[1] < 13) )) {
@@ -721,7 +724,7 @@ class ShortPixelMetaFacade {
721
  }
722
  return false;
723
  }
724
-
725
  public function optimizationSucceeded() {
726
  if($this->getType() == self::MEDIA_LIBRARY_TYPE) {
727
  do_action( 'shortpixel_image_optimised', $this->getId() );
3
  class ShortPixelMetaFacade {
4
  const MEDIA_LIBRARY_TYPE = 1;
5
  const CUSTOM_TYPE = 2;
6
+
7
  private $ID;
8
  private $type;
9
  private $meta;
10
  private $spMetaDao;
11
  private $rawMeta;
12
+
13
  public function __construct($ID) {
14
  if(strpos($ID, 'C-') === 0) {
15
  $this->ID = substr($ID, 2);
17
  } else {
18
  $this->ID = $ID;
19
  $this->type = self::MEDIA_LIBRARY_TYPE;
20
+ }
21
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb());
22
  }
23
+
24
  public static function getNewFromRow($item) {
25
+ return new ShortPixelMetaFacade("C-" . $item->id);
26
  }
27
+
28
  function setRawMeta($rawMeta) {
29
  if($this->type == self::MEDIA_LIBRARY_TYPE) {
30
  $this->rawMeta = $rawMeta;
40
  $rawMeta = $this->sanitizeMeta(wp_get_attachment_metadata($this->ID));
41
  $this->meta = self::rawMetaToMeta($this->ID, $rawMeta);
42
  $this->rawMeta = $rawMeta;
43
+ }
44
  }
45
  return $this->meta;
46
  }
47
+
48
  private static function rawMetaToMeta($ID, $rawMeta) {
49
  $path = get_attached_file($ID);
50
  return new ShortPixelMeta(array(
55
  "thumbs" => (isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array()),
56
  "message" =>(isset($rawMeta["ShortPixelImprovement"]) ? $rawMeta["ShortPixelImprovement"] : null),
57
  "png2jpg" => (isset($rawMeta["ShortPixelPng2Jpg"]) ? $rawMeta["ShortPixelPng2Jpg"] : false),
58
+ "compressionType" =>(isset($rawMeta["ShortPixel"]["type"])
59
  ? ($rawMeta["ShortPixel"]["type"] == 'glossy' ? 2 : ($rawMeta["ShortPixel"]["type"] == "lossy" ? 1 : 0) )
60
  : null),
61
  "thumbsOpt" =>(isset($rawMeta["ShortPixel"]["thumbsOpt"]) ? $rawMeta["ShortPixel"]["thumbsOpt"] : null),
66
  "thumbsTodo" =>(isset($rawMeta["ShortPixel"]["thumbsTodo"]) ? $rawMeta["ShortPixel"]["thumbsTodo"] : false),
67
  "tsOptimized" => (isset($rawMeta["ShortPixel"]["date"]) ? $rawMeta["ShortPixel"]["date"] : false),
68
  "backup" => !isset($rawMeta['ShortPixel']['NoBackup']),
69
+ "status" => (!isset($rawMeta["ShortPixel"]) ? 0
70
+ : (isset($rawMeta["ShortPixelImprovement"]) && is_numeric($rawMeta["ShortPixelImprovement"])
71
+ && !( $rawMeta['ShortPixelImprovement'] == 0
72
+ && ( isset($rawMeta['ShortPixel']['WaitingProcessing'])
73
+ || isset($rawMeta['ShortPixel']['date']) && $rawMeta['ShortPixel']['date'] == '1970-01-01')) ? 2
74
+ : (isset($rawMeta["ShortPixel"]["WaitingProcessing"]) ? 1
75
  : (isset($rawMeta["ShortPixel"]['ErrCode']) ? $rawMeta["ShortPixel"]['ErrCode'] : -500)))),
76
  "retries" =>(isset($rawMeta["ShortPixel"]["Retries"]) ? $rawMeta["ShortPixel"]["Retries"] : 0),
77
  ));
83
  return $this->meta;
84
  } else {
85
  return wp_get_attachment_url($this->ID);
86
+ }
87
  }
88
+
89
  static function sanitizeMeta($rawMeta, $createSPArray = true){
90
  if(!is_array($rawMeta)) {
91
  if($rawMeta == '') { return $createSPArray ? array('ShortPixel' => array()) : array(); }
99
  }
100
  return $rawMeta;
101
  }
102
+
103
  function updateMeta($newMeta = null, $replaceThumbs = false) {
104
  if($newMeta) {
105
  $this->meta = $newMeta;
117
 
118
  if(isset($rawMeta['sizes']) && is_array($rawMeta['sizes'])) {
119
  if($replaceThumbs) {
120
+ $rawMeta['sizes'] = $this->meta->getThumbs();
121
  } else {
122
  //use this instead of array_merge because we don't want to duplicate numeric keys
123
  foreach($this->meta->getThumbs() as $key => $val) {
133
  } else {
134
  $rawMeta['ShortPixel']['type'] = ShortPixelAPI::getCompressionTypeName($this->meta->getCompressionType());
135
  }
136
+
137
  if(null === $this->meta->getKeepExif()) {
138
  unset($rawMeta['ShortPixel']['exifKept']);
139
  } else {
145
  } else {
146
  $rawMeta['ShortPixel']['date'] = date("Y-m-d H:i:s", strtotime($this->meta->getTsOptimized()));
147
  }
148
+
149
  //thumbs were processed if settings or if they were explicitely requested
150
  if(null === $this->meta->getThumbsOpt()) {
151
  unset($rawMeta['ShortPixel']['thumbsOpt']);
168
  } else {
169
  unset($rawMeta['ShortPixel']['thumbsMissing']);
170
  }
171
+
172
  if(null === $this->meta->getRetinasOpt()) {
173
  unset($rawMeta['ShortPixel']['retinasOpt']);
174
  } else {
211
  $this->rawMeta = $rawMeta;
212
  }
213
  }
214
+ }
215
  }
216
+
217
  function cleanupMeta($fakeOptPending = false) {
218
  if($this->type == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
219
  if(!isset($this->rawMeta)) {
248
  unset($this->rawMeta['ShortPixel']);
249
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
250
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
251
+ }
252
  }
253
 
254
  function deleteAllSPMeta() {
271
  if(!isset($this->rawMeta['ShortPixel'])) {$this->rawMeta['ShortPixel'] = array();}
272
  $this->rawMeta['ShortPixel']['Retries'] = isset($this->rawMeta['ShortPixel']['Retries']) ? $this->rawMeta['ShortPixel']['Retries'] + $count : $count;
273
  $this->meta->setRetries($this->rawMeta['ShortPixel']['Retries']);
274
+ }
275
  $this->setError($errorCode, $errorMessage);
276
  }
277
+
278
  function setWaitingProcessing($status = true) {
279
  if($status) {
280
  $this->meta->setStatus(1);
293
  }
294
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
295
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
296
+ }
297
  }
298
+
299
  function setError($errorCode, $errorMessage) {
300
  $this->meta->setMessage(__('Error','shortpixel-image-optimiser') . ': <i>' . $errorMessage . '</i>');
301
  $this->meta->setStatus($errorCode);
311
  unset($this->rawMeta['ShortPixel']['WaitingProcessing']);
312
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
313
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
314
+ }
315
  }
316
+
317
  function setMessage($message) {
318
  $this->meta->setMessage($message);
319
  $this->meta->setStatus(-1);
326
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
327
  }
328
  }
329
+
330
  public static function getHomeUrl() {
331
  //trim is because we found a site set up with a tab, like this: https://modernpeasantcooking.com\t
332
  return trailingslashit((function_exists("is_multisite") && is_multisite()) ? trim(network_site_url("/")) : trim(home_url()));
333
  }
334
+
335
  //this is in test
336
  public static function getHomeUrl2() {
337
  return trailingslashit(ShortPixelTools::commonPrefix(self::getHomeUrl(), content_url()));
343
  * @throws Exception
344
  */
345
  public static function safeGetAttachmentUrl($id) {
346
+ $attURL = wp_get_attachment_url($id);
347
  if(!$attURL || !strlen($attURL)) {
348
  throw new Exception("Post metadata is corrupt (No attachment URL for $id)", ShortPixelAPI::ERR_POSTMETA_CORRUPT);
349
  }
354
  return $attURL;//get the file URL
355
  }
356
  }
357
+
358
  public function getURLsAndPATHs($processThumbnails, $onlyThumbs = false, $addRetina = true, $excludeSizes = array(), $includeOptimized = false) {
359
  $sizesMissing = array();
360
+
361
  if($this->type == self::CUSTOM_TYPE) {
362
  $meta = $this->getMeta();
363
 
364
  //fix for situations where site_url is lala.com/en and home_url is lala.com - if using the site_url will get a duplicated /en in the URL
365
  $homeUrl = self::getHomeUrl();
366
  $urlList[] = self::replaceHomePath($meta->getPath(), $homeUrl);
367
+
368
  $filePaths[] = $meta->getPath();
369
  } else {
370
  $path = get_attached_file($this->ID);//get the full file PATH
374
 
375
  if(!$mainExists) {
376
  //try and download the image from the URL (images present only on CDN)
377
+ $downloadTimeout = max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15);
378
  $tempOriginal = download_url($url, $downloadTimeout);
379
  if(!is_wp_error( $tempOriginal )) {
380
  $mainExists = @copy($tempOriginal, $path);
388
  $this->addRetina($path, $url, $filePaths, $urlList);
389
  }
390
  }
391
+
392
  $meta = $this->getMeta();
393
  $sizes = $meta->getThumbs();
394
 
395
  //it is NOT a PDF file and thumbs are processable
396
+ if ( /* strtolower(substr($path,strrpos($path, ".")+1)) != "pdf"
397
+ &&*/ ($processThumbnails || $onlyThumbs)
398
  && count($sizes))
399
  {
400
  $Tmp = explode("/", SHORTPIXEL_UPLOADS_BASE);
401
  $TmpCount = count($Tmp);
402
  $StichString = $Tmp[$TmpCount-2] . "/" . $Tmp[$TmpCount-1];
403
+
404
  $count = 1;
405
  foreach( $sizes as $thumbnailName => $thumbnailInfo ) {
406
 
419
  if(strpos($thumbnailName, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) {
420
  continue;
421
  }
422
+
423
  if(!$includeOptimized && in_array($thumbnailInfo['file'], $meta->getThumbsOptList())) {
424
  continue;
425
  }
426
+
427
  if($count >= SHORTPIXEL_MAX_THUMBS) break;
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);
440
 
441
  if ( !$file_exists && !file_exists($tPath) ) {
442
  //try and download the image from the URL (images present only on CDN)
443
+ $downloadTimeout = max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15);
444
  $tempThumb = download_url($tUrl, $downloadTimeout);
445
  if(!is_wp_error( $tempThumb )) {
446
  if(@copy($tempThumb, $origPath)) {
465
  if(!count($sizes)) {
466
  WPShortPixel::log("getURLsAndPATHs: no meta sizes for ID " . $this->ID . " : " . json_encode($this->rawMeta));
467
  }
468
+
469
  if($onlyThumbs && $mainExists && count($urlList) >= 1) { //remove the main image
470
  array_shift($urlList);
471
  array_shift($filePaths);
472
+ }
473
  }
474
+
475
  //convert the + which are replaced with spaces by wp_remote_post
476
  array_walk($urlList, array( &$this, 'replacePlusChar') );
477
 
480
  //die(var_dump(array("URLs" => $urlList, "PATHs" => $filePaths)));
481
  return array("URLs" => $urlList, "PATHs" => $filePaths, "sizesMissing" => $sizesMissing);
482
  }
483
+
484
  protected function replacePlusChar(&$url) {
485
  $url = str_replace("+", "%2B", $url);
486
  }
487
+
488
  protected function addRetina($path, $url, &$fileList, &$urlList) {
489
  $ext = pathinfo($path, PATHINFO_EXTENSION);
490
  $retinaPath = substr($path, 0, strlen($path) - 1 - strlen($ext)) . "@2x." . $ext;
498
  $baseName = pathinfo(ShortPixelAPI::MB_basename($path), PATHINFO_FILENAME);
499
  return (substr($baseName, -3) === '@2x');
500
  }
501
+
502
  public static function getWPMLDuplicates( $id ) {
503
  global $wpdb;
504
+
505
  $parentId = get_post_meta ($id, '_icl_lang_duplicate_of', true );
506
  if($parentId) $id = $parentId;
507
+
508
  $mainFile = get_attached_file($id);
509
 
510
  $duplicates = $wpdb->get_col( $wpdb->prepare( "
511
  SELECT pm.post_id FROM {$wpdb->postmeta} pm
512
+ WHERE pm.meta_value = %s AND pm.meta_key = '_icl_lang_duplicate_of'
513
  ", $id ) );
514
 
515
  //Polylang
549
  }
550
  return array_unique($duplicates);
551
  }
552
+
553
  public static function pathToWebPath($path) {
554
  //$upl = wp_upload_dir();
555
  //return str_replace($upl["basedir"], $upl["baseurl"], $path);
563
  $path = implode('/', $pathParts);
564
  return self::filenameToRootRelative($path);
565
  }
566
+
567
  public static function filenameToRootRelative($path) {
568
  return self::replaceHomePath($path, "");
569
  }
570
+
571
  private static function replaceHomePath($path, $with) {
572
  if(strpos($path, get_home_path()) === 0) {
573
  return str_replace(get_home_path(), $with, $path);
575
  return str_replace(trailingslashit(realpath(get_home_path())), $with, $path);
576
  }
577
  }
578
+
579
  public function getWebpSizeMeta($path) {
580
  $meta = $this->getMeta();
581
  $thumbs = $meta->getThumbs(); $thumbs = is_array($thumbs) ? $thumbs : array();
592
  return false;
593
  }
594
  $size = getimagesize($path);
595
+ return array('key' => ShortPixelMeta::WEBP_THUMB_PREFIX . 'main',
596
  'val' => array( // it's a file that has no corresponding thumb so it's the WEBP for the main file
597
  'file' => pathinfo(ShortPixelAPI::MB_basename($path), PATHINFO_FILENAME) . '.webp',
598
  'width' => $size[0],
609
  $resultQuery = $wpdb->get_results($queryMax);
610
  return $resultQuery[0]->QueryID;
611
  }
612
+
613
  public static function getMinMediaId() {
614
  global $wpdb;
615
  $queryMax = "SELECT min(post_id) as QueryID FROM " . $wpdb->prefix . "postmeta";
616
  $resultQuery = $wpdb->get_results($queryMax);
617
  return $resultQuery[0]->QueryID;
618
  }
619
+
620
  public static function isCustomQueuedId($id) {
621
  return substr($id, 0, 2) == "C-";
622
  }
623
+
624
  public static function stripQueuedIdType($id) {
625
  return intval(substr($id, 2));
626
  }
627
+
628
  public function getQueuedId() {
629
  return self::queuedId($this->type, $this->ID);
630
  }
631
+
632
  public static function queuedId($type, $id) {
633
  return ($type == self::CUSTOM_TYPE ? "C-" : "") . $id;
634
  }
635
+
636
  function getId() {
637
  return $this->ID;
638
  }
648
  function setType($type) {
649
  $this->type = $type;
650
  }
651
+
652
  function getRawMeta() {
653
  return $this->rawMeta;
654
  }
655
+
656
  /**
657
  * return subdir for that particular attached file - if it's media library then last 3 path items, otherwise substract the uploads path
658
  * Has trailing directory separator (/)
685
  }
686
  $hp = wp_normalize_path($homePath);
687
  $file = wp_normalize_path($file);
688
+
689
+ // $sp__uploads = wp_upload_dir();
690
+
691
  if(strstr($file, $hp)) {
692
  $path = str_replace( $hp, "", $file);
693
  } elseif( strstr($file, dirname( WP_CONTENT_DIR ))) { //in some situations the content dir is not inside the root, check this also (ex. single.shortpixel.com)
715
  //contains one of the year subfolders of the media library
716
  if(strpos($path, $uploadPath) == 0) {
717
  $pathArr = explode('/', str_replace($uploadBase . '/', "", $path));
718
+ if( count($pathArr) >= 1
719
  && is_numeric($pathArr[0]) && $pathArr[0] > 1900 && $pathArr[0] < 2100 //contains the year subfolder
720
  && ( count($pathArr) == 1 //if there is another subfolder then it's the month subfolder
721
  || (is_numeric($pathArr[1]) && $pathArr[1] > 0 && $pathArr[1] < 13) )) {
724
  }
725
  return false;
726
  }
727
+
728
  public function optimizationSucceeded() {
729
  if($this->getType() == self::MEDIA_LIBRARY_TYPE) {
730
  do_action( 'shortpixel_image_optimised', $this->getId() );
class/db/wp-shortpixel-media-library-adapter.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
 
3
  class WpShortPixelMediaLbraryAdapter {
4
 
@@ -233,13 +235,68 @@ class WpShortPixelMediaLbraryAdapter {
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) {
1
  <?php
2
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
3
+
4
 
5
  class WpShortPixelMediaLbraryAdapter {
6
 
235
 
236
  public static function getPostMetaSlice($startId, $endId, $limit) {
237
  global $wpdb;
238
+ $time = microtime(true);
239
+ $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta pm
240
  INNER JOIN " . $wpdb->prefix . "posts p ON p.ID = pm.post_id
241
  WHERE ( p.ID <= $startId AND p.ID >= $endId )
242
  AND ( pm.meta_key = '_wp_attached_file' OR pm.meta_key = '_wp_attachment_metadata' )
243
  ORDER BY pm.post_id DESC
244
  LIMIT " . $limit;
245
+ $result = $wpdb->get_results($queryPostMeta);
246
+ $time_end = microtime(true);
247
+ // Log::addDebug('Post Meta Slice query took ' . ($time_end-$time) . ' sec. - Result count ' . count($result), array( $queryPostMeta));
248
+ return $result;
249
+ }
250
+
251
+ public static function getPostMetaJoinLess($startId, $endId, $limit)
252
+ {
253
+ global $wpdb;
254
+ $time = microtime(true);
255
+ $sql = "SELECT ID FROM " . $wpdb->prefix . "posts WHERE ID <= %d AND ID >= %d ORDER BY ID DESC LIMIT %d ";
256
+ $sql = $wpdb->prepare($sql, $startId, $endId, $limit);
257
+ $result = $wpdb->get_col($sql);
258
+
259
+ if (is_null($result))
260
+ return array();
261
+
262
+ $id_placeholders = implode( ', ', array_fill( 0, count( $result ), '%d'));
263
+
264
+ $sqlmeta = "SELECT DISTINCT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta where (meta_key = %s or meta_key = %s) and post_id in (" . $id_placeholders . ") order by post_id DESC";
265
+
266
+ $placeholders = array_merge(array('_wp_attached_file', '_wp_attachment_metadata'), array_values($result));
267
+ $sqlmeta = $wpdb->prepare($sqlmeta, $placeholders);
268
+ $metaresult = $wpdb->get_results($sql);
269
+
270
+ $time_end = microtime(true);
271
+
272
+ // Log::addDebug('Post Meta JoinLESS query took ' . ($time_end-$time) . ' sec. - Result count ' . count($metaresult), array($sql, $sqlmeta));
273
+
274
+ return $metaresult;
275
+ }
276
+
277
+ public static function getPostsJoinLessReverse($startId, $endId, $limit)
278
+ {
279
+ global $wpdb;
280
+ $time = microtime(true);
281
+
282
+ $sqlmeta = "SELECT DISTINCT post_id FROM " . $wpdb->prefix . "postmeta where (meta_key = %s or meta_key = %s) and post_id <= %d and post_id >= %d order by post_id DESC LIMIT %d";
283
+ $sqlmeta = $wpdb->prepare($sqlmeta, '_wp_attached_file', '_wp_attachment_metadata', $startId, $endId, $limit);
284
+
285
+ $result = $wpdb->get_col($sqlmeta);
286
+
287
+ $id_placeholders = implode( ', ', array_fill( 0, count( $result ), '%d'));
288
+
289
+ $sql = 'SELECT ID from ' . $wpdb->prefix . 'posts where ID in (' . $id_placeholders . ') ORDER BY ID DESC';
290
+ $sql = $wpdb->prepare($sql, array_values($result));
291
+
292
+ $postresult = $wpdb->get_col($sql);
293
+
294
+ $postAr = array_intersect($result, $postresult);
295
+
296
+ $time_end = microtime(true);
297
+ // Log::addDebug('Post Meta JoinLESS **REVERSE** query took ' . ($time_end-$time) . ' sec. - Result count ' . count($postAr), array($sql, $sqlmeta));
298
+
299
+ return $postAr;
300
  }
301
 
302
  public static function getSizesNotExcluded($sizes, $exclude = false) {
class/external/flywheel.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+
5
+ /* Prevent Flock Queue on FlyWheel Cloud Hosting due to performance issues.
6
+ *
7
+ * Detect Flywheel and set Constant
8
+ */
9
+
10
+ if (isset($_SERVER['SERVER_SOFTWARE']))
11
+ {
12
+ $server = strtolower($_SERVER['SERVER_SOFTWARE']);
13
+
14
+ if (strpos($server, 'flywheel') !== false) // server detected
15
+ {
16
+ $pos = strpos($server, '/') + 1;
17
+
18
+ $version = substr($server, $pos);
19
+
20
+ if (version_compare($version, '5.0.0') >= 0)
21
+ {
22
+ Log::addInfo('Flywheel detected on ' . $server . ' . Starting NOFLOCK Queue.');
23
+ if (! defined('SHORTPIXEL_NOFLOCK'))
24
+ define('SHORTPIXEL_NOFLOCK', 1);
25
+ }
26
+ }
27
+ }
class/external/helpscout.php CHANGED
@@ -6,6 +6,11 @@ class HelpScout
6
  {
7
  public static function outputBeacon($apiKey)
8
  {
 
 
 
 
 
9
  ?>
10
  <style>
11
  .shortpixel-hs-blind {
@@ -17,6 +22,31 @@ class HelpScout
17
  width: 87px;
18
  height: 174px;
19
  border-radius: 20px 0 0 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
  .shortpixel-hs-button-blind {
22
  display:none;
@@ -49,10 +79,17 @@ class HelpScout
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>
6
  {
7
  public static function outputBeacon($apiKey)
8
  {
9
+ global $shortPixelPluginInstance;
10
+ $dismissed = $shortPixelPluginInstance->getSettings()->dismissedNotices ? $shortPixelPluginInstance->getSettings()->dismissedNotices : array();
11
+ if(isset($dismissed['help'])) {
12
+ return;
13
+ }
14
  ?>
15
  <style>
16
  .shortpixel-hs-blind {
22
  width: 87px;
23
  height: 174px;
24
  border-radius: 20px 0 0 20px;
25
+ text-align: right;
26
+ padding-right: 15px;
27
+ }
28
+ .shortpixel-hs-blind a {
29
+ color: lightgray;
30
+ text-decoration: none;
31
+ }
32
+ .shortpixel-hs-blind .dashicons-minus {
33
+ border: 3px solid;
34
+ border-radius: 12px;
35
+ font-size: 12px;
36
+ font-weight: bold;
37
+ line-height: 15px;
38
+ height: 13px;
39
+ width: 13px;
40
+ display:none;
41
+ }
42
+ .shortpixel-hs-blind .dashicons-dismiss {
43
+ font-size: 23px;
44
+ line-height: 19px;
45
+ display: none;
46
+ }
47
+ .shortpixel-hs-blind:hover .dashicons-minus,
48
+ .shortpixel-hs-blind:hover .dashicons-dismiss {
49
+ display: inline-block;
50
  }
51
  .shortpixel-hs-button-blind {
52
  display:none;
79
  }
80
  }
81
  </style>
82
+ <div id="shortpixel-hs-blind" class="shortpixel-hs-blind">
83
+ <a href="javascript:ShortPixel.closeHelpPane();">
84
+ <i class="dashicons dashicons-minus" title="<?php _e('Dismiss for now', 'shortpixel-image-optimiser'); ?> "></i>
85
+ </a>
86
+ <a href="javascript:ShortPixel.dismissHelpPane();">
87
+ <i class="dashicons dashicons-dismiss" title="<?php _e('Never display again', 'shortpixel-image-optimiser'); ?>"></i>
88
+ </a>
89
+ </div>
90
  <div id="shortpixel-hs-button-blind" class="shortpixel-hs-button-blind"></div>
91
  <div id="shortpixel-hs-tools" class="shortpixel-hs-tools">
92
+ <a href="javascript:shortpixelToggleHS();" class="shortpixel-hs-tools-docs" title="<?php _e('Search through our online documentation.', 'shortpixel-image-optimiser'); ?>">
93
  <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/notes-sp.png'));?>" style="margin-bottom: 2px;width: 36px;">
94
  </a>
95
  </div>
class/external/nextgen.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  namespace ShortPixel;
3
- use ShortPixel\NoticeController as Notice;
4
 
5
  class NextGen
6
  {
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\Notices\NoticeController as Notice;
4
 
5
  class NextGen
6
  {
class/external/shortpixel_queue_db.php ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
3
+
4
+ class ShortPixelQueueDB extends ShortPixelQueue{
5
+
6
+ protected $ctrl;
7
+ protected $settings;
8
+
9
+ const THE_OPTION = 'shortpixel_prioq';
10
+ const THE_TRANSIENT = 'shortpixel_prioq_lock';
11
+
12
+ const BULK_TYPE_OPTIMIZE = 0;
13
+ const BULK_TYPE_RESTORE = 1;
14
+ const BULK_TYPE_CLEANUP = 2;
15
+ const BULK_TYPE_CLEANUP_PENDING = 3;
16
+
17
+ const BULK_NEVER = 0; //bulk never ran
18
+ const BULK_RUNNING = 1; //bulk is running
19
+ const BULK_PAUSED = 2; //bulk is paused
20
+ const BULK_FINISHED = 3; //bulk finished
21
+
22
+ public function __construct($controller, $settings) {
23
+ $this->ctrl = $controller;
24
+ $this->settings = $settings;
25
+ //parent::__construct($controller, $settings);
26
+ }
27
+
28
+ //handling older
29
+ /* public function ShortPixelQueue($controller) {
30
+ $this->__construct($controller);
31
+ }
32
+ */
33
+
34
+ // @todo Replace
35
+ public static function get() {
36
+ $queue = self::openQ(LOCK_SH);
37
+ if($queue === false) return array();
38
+
39
+ Log::addDebug('DBQ - Get ' . $queue);
40
+ $itemsRaw = $queue;
41
+ $items = strlen($itemsRaw) ? self::parseQ($itemsRaw) : array();
42
+ $fp = null;
43
+ self::closeQ($fp);
44
+
45
+ return $items;
46
+ }
47
+
48
+ // @todo Replace
49
+ public static function set($items) {
50
+
51
+ $queue = self::openQ();
52
+ if($queue === false) return false;
53
+
54
+ /* fseek($fp, 0);
55
+ ftruncate($fp, 0); // truncate file
56
+ fwrite($fp, implode(',', $items));
57
+ fflush($fp); // flush output before releasing the lock */
58
+ Log::addDebug('DBQ - Set ' . implode(',', $items));
59
+ update_option(self::THE_OPTION, implode(',', $items), false);
60
+
61
+ $fp =null;
62
+ self::closeQ($fp);
63
+ return true;
64
+ }
65
+
66
+ // @todo Replace
67
+ public function apply($callable, $extra = false) {
68
+ $queue = self::openQ();
69
+ if($queue === false) return false;
70
+
71
+ $itemsRaw = $queue;
72
+ Log::addDebug('Apply' . $itemsRaw);
73
+ $items = strlen($itemsRaw) ? self::parseQ($itemsRaw) : array();
74
+ if($extra) {
75
+ $items = call_user_func($callable, $items, $extra);
76
+ } else {
77
+ $items = call_user_func($callable, $items);
78
+ }
79
+
80
+ update_option(self::THE_OPTION, implode(',',$items), false);
81
+ /*fseek($fp, 0);
82
+ ftruncate($fp, 0); // truncate file
83
+ fwrite($fp, implode(',', $items));
84
+ fflush($fp); // flush output before releasing the lock */
85
+ $fp = null;
86
+ self::closeQ($fp);
87
+ return $items;
88
+ }
89
+
90
+ public static function testQ() {
91
+ $fp = self::openQ();
92
+ if($fp === false) return false;
93
+ self::closeQ($fp);
94
+ return true;
95
+ }
96
+
97
+
98
+ // @todo Replace - main thing here.
99
+ protected static function openQ($lock = LOCK_EX) {
100
+
101
+ //$queueName = SHORTPIXEL_UPLOADS_BASE . "/.shortpixel-q-" . get_current_blog_id();
102
+ $trans = get_transient(self::THE_TRANSIENT);
103
+ Log::addDebug('OpenQ', array($trans));
104
+ if (! $trans === false) // if lock, then no beans.
105
+ return false;
106
+
107
+ Log::addDebug('OpenQ -- opened');
108
+
109
+ wp_cache_delete( self::THE_OPTION, 'options' ); // ensure uncached goodness here.
110
+ $queue = get_option(self::THE_OPTION, '');
111
+
112
+ set_transient(self::THE_TRANSIENT, 'true', 60);
113
+ return $queue;
114
+
115
+ /* $fp = @fopen($queueName, "r+");
116
+ if(!$fp) {
117
+ $fp = @fopen($queueName, "w");
118
+ if(!$fp) return false;
119
+ }
120
+
121
+ flock($fp, $lock);
122
+ return $fp; */
123
+ }
124
+
125
+ // @todo replace
126
+ protected static function closeQ($fp) {
127
+ Log::addDebug('CloseQ');
128
+ delete_transient(self::THE_TRANSIENT);
129
+
130
+ // flock($fp, LOCK_UN); // release the lock
131
+ // fclose($fp);
132
+ }
133
+
134
+ public static function resetPrio() {
135
+ //delete_option( "wp-short-pixel-priorityQueue");
136
+ self::set(array());
137
+ }
138
+
139
+ public function processing() {
140
+ //WPShortPixel::log("QUEUE: processing(): get:" . json_encode($this->get()));
141
+ return $this->bulkRunning() || count($this->get());
142
+ }
143
+
144
+
145
+ /* protected static function parseQ($items) {
146
+ return explode(',', preg_replace("/[^0-9,C-]/", "", $items));
147
+ }
148
+ */
149
+
150
+ /* public function skip($id) {
151
+ if(is_array($this->settings->prioritySkip)) {
152
+ $this->settings->prioritySkip = array_merge($this->settings->prioritySkip, array($id));
153
+ } else {
154
+ $this->settings->prioritySkip = array($id);
155
+ }
156
+ }
157
+ */
158
+ /* public function unskip($id) {
159
+ $prioSkip = $this->settings->prioritySkip;
160
+ $this->settings->prioritySkip = is_array($prioSkip) ? array_diff($prioSkip, array($id)) : array();
161
+ }
162
+ */
163
+ /* public function allSkipped() {
164
+ if( !is_array($this->settings->prioritySkip) ) return false;
165
+ count(array_diff($this->get(), $this->settings->prioritySkip));
166
+ }
167
+ */
168
+ /* public function skippedCount() {
169
+ return is_array($this->settings->prioritySkip) ? count($this->settings->prioritySkip) : 0;
170
+ }
171
+ */
172
+ /* public function isSkipped($id) {
173
+ return is_array($this->settings->prioritySkip) && in_array($id, $this->settings->prioritySkip);
174
+ }
175
+ */
176
+ /* public function isPrio($id) {
177
+ $prioItems = $this->get();
178
+ return is_array($prioItems) && in_array($id, $prioItems);
179
+ }
180
+ */
181
+ /* public function getSkipped() {
182
+ return $this->settings->prioritySkip;
183
+ }
184
+ */
185
+ /* public function reverse() {
186
+ $this->apply('array_reverse');
187
+ //$this->settings->priorityQueue = $_SESSION["wp-short-pixel-priorityQueue"] = array_reverse($_SESSION["wp-short-pixel-priorityQueue"]);
188
+
189
+ }
190
+ */
191
+ /* protected function pushCallback($priorityQueue, $ID) {
192
+ WPShortPixel::log("PUSH: Push ID $ID into queue " . json_encode($priorityQueue));
193
+ array_push($priorityQueue, $ID);
194
+ $prioQ = array_unique($priorityQueue);
195
+ WPShortPixel::log("PUSH: Updated: " . json_encode($prioQ));//get_option("wp-short-pixel-priorityQueue")));
196
+ return $prioQ;
197
+ }
198
+ */
199
+
200
+ /* public function push($ID)//add an ID to priority queue
201
+ {
202
+ $this->apply(array(&$this, 'pushCallback'), $ID);
203
+ }
204
+ */
205
+ /* protected function enqueueCallback($priorityQueue, $ID) {
206
+ WPShortPixel::log("ENQUEUE: Enqueue ID $ID into queue " . json_encode($priorityQueue));
207
+ array_unshift($priorityQueue, $ID);
208
+ $prioQ = array_unique($priorityQueue);
209
+ WPShortPixel::log("ENQUEUE: Updated: " . json_encode($prioQ));//get_option("wp-short-pixel-priorityQueue")));
210
+ return $prioQ;
211
+ }
212
+ */
213
+ /* public function enqueue($ID)//add an ID to priority queue as LAST
214
+ {
215
+ $this->apply(array(&$this, 'enqueueCallback'), $ID);
216
+ }
217
+ */
218
+ /* public function getFirst($count = 1)//return the first values added to priority queue
219
+ {
220
+ $priorityQueue = $this->get();
221
+ $count = min(count($priorityQueue), $count);
222
+ return(array_slice($priorityQueue, count($priorityQueue) - $count, $count));
223
+ }
224
+ */
225
+ /* public function getFromPrioAndCheck() {
226
+ $idsPrio = $this->get();
227
+
228
+ $ids = array();
229
+ $removeIds = array();
230
+ for($i = count($idsPrio) - 1, $cnt = 0; $i>=0 && $cnt < 3; $i--) {
231
+ if(!isset($idsPrio[$i])) continue; //saw this situation but then couldn't reproduce it to see the cause, so at least treat the effects.
232
+ $id = $idsPrio[$i];
233
+ if(!$this->isSkipped($id) && $this->ctrl->isValidMetaId($id)) {
234
+ $ids[] = $id; //valid ID
235
+ $cnt++;
236
+ } elseif(!$this->isSkipped($id)) {
237
+ $removeIds[] = $id;//not skipped, url not found, means it's absent, to remove
238
+ }
239
+ }
240
+ foreach($removeIds as $rId){
241
+ WPShortPixel::log("HIP: Unfound ID $rId Remove from Priority Queue: ".json_encode($this->get()));
242
+ $this->remove($rId);
243
+ }
244
+ return $ids;
245
+ }
246
+ */
247
+
248
+ // @todo Replace
249
+ public function remove($ID)//remove an ID from priority queue
250
+ {
251
+ $queue = $this->openQ();
252
+ if($queue === false) return false;
253
+ $items = $queue;
254
+ $items = self::parseQ($items);
255
+ $items = is_array($items) ? $items : array();
256
+ $newItems = array();
257
+ $found = false;
258
+ foreach($items as $item) { // this instead of array_values(array_diff(.. because we need to know if we actually removed it
259
+ if($item != $ID) {
260
+ $newItems[] = $item;
261
+ } else {
262
+ $found = true;
263
+ }
264
+ }
265
+ if($found) {
266
+ /* fseek($fp, 0);
267
+ ftruncate($fp, 0);
268
+ fwrite($fp, implode(',', $newItems));
269
+ fflush($fp); // flush output before releasing the lock */
270
+ update_option(self::THE_OPTION, implode(',',$newItems), false);
271
+ Log::addDebug('DBQ - Found and Removing ' . $ID);
272
+ }
273
+ $fp = null;
274
+ $this->closeQ($fp);
275
+ return $found;
276
+ }
277
+ /*
278
+ public function removeFromFailed($ID) {
279
+ $failed = explode(",", $this->settings->failedImages);
280
+ $key = array_search($ID, $failed);
281
+ if($key !== false) {
282
+ unset($failed[$key]);
283
+ $failed = array_values($failed);
284
+ $this->settings->failedImages = implode(",", $failed) ;
285
+ }
286
+ }
287
+ */
288
+ /*
289
+ public function addToFailed($ID) {
290
+ $failed = $this->settings->failedImages;
291
+ if(!in_array($ID, explode(",", $failed))) {
292
+ $this->settings->failedImages = (strlen($failed) ? $failed . "," : "") . $ID;
293
+ }
294
+ }
295
+ */
296
+
297
+ /*
298
+ public function getFailed() {
299
+ $failed = $this->settings->failedImages;
300
+ if(!strlen($failed)) return array();
301
+ $ret = explode(",", $failed);
302
+ $fails = array();
303
+ foreach($ret as $fail) {
304
+ if(ShortPixelMetaFacade::isCustomQueuedId($fail)) {
305
+ $meta = $this->ctrl->getSpMetaDao()->getMeta(ShortPixelMetaFacade::stripQueuedIdType($fail));
306
+ if($meta) {
307
+ $fails[] = (object)array("id" => ShortPixelMetaFacade::stripQueuedIdType($fail), "type" => ShortPixelMetaFacade::CUSTOM_TYPE, "meta" => $meta);
308
+ }
309
+ } else {
310
+ $meta = wp_get_attachment_metadata($fail);
311
+ if(!$meta || (isset($meta["ShortPixelImprovement"]) && is_numeric($meta["ShortPixelImprovement"]))){
312
+ $this->removeFromFailed($fail);
313
+ } else {
314
+ $fails[] = (object)array("id" => $fail, "type" => ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE, "meta" => $meta);
315
+ }
316
+ }
317
+ }
318
+ return $fails;
319
+ }
320
+ */
321
+ /*
322
+ public function bulkRunning() {
323
+ //$bulkProcessingStatus = get_option('bulkProcessingStatus');
324
+ return $this->settings->startBulkId > $this->settings->stopBulkId;
325
+ }
326
+ */
327
+
328
+
329
+
330
+ }
class/front/img-to-picture-webp.php CHANGED
@@ -4,8 +4,8 @@
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
  {
@@ -28,6 +28,7 @@ class ShortPixelImgToPictureWebp
28
 
29
  public static function convert($content)
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');
@@ -38,7 +39,7 @@ class ShortPixelImgToPictureWebp
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
@@ -128,9 +129,6 @@ class ShortPixelImgToPictureWebp
128
  $sizes = $sizesInfo['value'];
129
  $sizesPrefix = $sizesInfo['prefix'];
130
 
131
- $altAttr = isset($img['alt']) && strlen($img['alt']) ? ' alt="' . $img['alt'] . '"' : '';
132
- $idAttr = isset($img['id']) && strlen($img['id']) ? ' id="' . $img['id'] . '"' : '';
133
-
134
  //check if there are webps
135
  /*$id = $thisClass::url_to_attachment_id( $src );
136
  if(!$id) {
@@ -179,6 +177,11 @@ class ShortPixelImgToPictureWebp
179
  Log::addInfo('SPDBG baseurl doesn\'t match ' . $src);
180
  }
181
 
 
 
 
 
 
182
 
183
  // We don't wanna have src-ish attributes on the <picture>
184
  unset($img['src']);
@@ -186,8 +189,12 @@ class ShortPixelImgToPictureWebp
186
  unset($img['data-lazy-src']);
187
  unset($img['srcset']);
188
  unset($img['sizes']);
 
189
  unset($img['alt']);
190
  unset($img['id']);
 
 
 
191
  $srcsetWebP = '';
192
 
193
  if ($srcset) {
@@ -243,7 +250,7 @@ class ShortPixelImgToPictureWebp
243
  return '<picture ' . self::create_attributes($img) . '>'
244
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
245
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
246
- .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . $idAttr . $altAttr
247
  . (strlen($srcset) ? ' srcset="' . $srcset . '"': '') . (strlen($sizes) ? ' sizes="' . $sizes . '"': '') . '>'
248
  .'</picture>';
249
  }
@@ -403,6 +410,12 @@ class ShortPixelImgToPictureWebp
403
  @$dom->loadHTML($image_node);
404
  $image = $dom->getElementsByTagName('img')->item(0);
405
  $attributes = array();
 
 
 
 
 
 
406
  foreach ($image->attributes as $attr) {
407
  $attributes[$attr->nodeName] = $attr->nodeValue;
408
  }
4
  * thanks to the Responsify WP plugin for some of the code
5
  */
6
 
7
+ //use ShortPixel\DebugItem as DebugItem;
8
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
9
 
10
  class ShortPixelImgToPictureWebp
11
  {
28
 
29
  public static function convert($content)
30
  {
31
+
32
  // Don't do anything with the RSS feed.
33
  if (is_feed() || is_admin()) {
34
  Log::addInfo('SPDBG convert is_feed or is_admin');
39
  if ($new_content !== false)
40
  $content = $new_content;
41
 
42
+ $content = preg_replace_callback('/<img[^>]*>/i', array('self', 'convertImage'), $content);
43
  //$content = preg_replace_callback('/background.*[^:](url\(.*\)[,;])/im', array('self', 'convertInlineStyle'), $content);
44
 
45
  // [BS] No callback because we need preg_match_all
129
  $sizes = $sizesInfo['value'];
130
  $sizesPrefix = $sizesInfo['prefix'];
131
 
 
 
 
132
  //check if there are webps
133
  /*$id = $thisClass::url_to_attachment_id( $src );
134
  if(!$id) {
177
  Log::addInfo('SPDBG baseurl doesn\'t match ' . $src);
178
  }
179
 
180
+ //some attributes should not be moved from <img>
181
+ $altAttr = isset($img['alt']) && strlen($img['alt']) ? ' alt="' . $img['alt'] . '"' : '';
182
+ $idAttr = isset($img['id']) && strlen($img['id']) ? ' id="' . $img['id'] . '"' : '';
183
+ $heightAttr = isset($img['height']) && strlen($img['height']) ? ' height="' . $img['height'] . '"' : '';
184
+ $widthAttr = isset($img['width']) && strlen($img['width']) ? ' width="' . $img['width'] . '"' : '';
185
 
186
  // We don't wanna have src-ish attributes on the <picture>
187
  unset($img['src']);
189
  unset($img['data-lazy-src']);
190
  unset($img['srcset']);
191
  unset($img['sizes']);
192
+ //nor the ones that belong to <img>
193
  unset($img['alt']);
194
  unset($img['id']);
195
+ unset($img['width']);
196
+ unset($img['height']);
197
+
198
  $srcsetWebP = '';
199
 
200
  if ($srcset) {
250
  return '<picture ' . self::create_attributes($img) . '>'
251
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
252
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
253
+ .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . $idAttr . $altAttr . $heightAttr . $widthAttr
254
  . (strlen($srcset) ? ' srcset="' . $srcset . '"': '') . (strlen($sizes) ? ' sizes="' . $sizes . '"': '') . '>'
255
  .'</picture>';
256
  }
410
  @$dom->loadHTML($image_node);
411
  $image = $dom->getElementsByTagName('img')->item(0);
412
  $attributes = array();
413
+
414
+ /* This can happen with mismatches, or extremely malformed HTML.
415
+ In customer case, a javascript that did for (i<imgDefer) --- </script> */
416
+ if (! is_object($image))
417
+ return false;
418
+
419
  foreach ($image->attributes as $attr) {
420
  $attributes[$attr->nodeName] = $attr->nodeValue;
421
  }
class/model/apikey_model.php ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use ShortPixel\Notices\NoticeController as Notice;
5
+
6
+ class ApiKeyModel extends ShortPixelModel
7
+ {
8
+
9
+ // variables
10
+ protected $apiKey;
11
+ protected $apiKeyTried; // stop retrying the same key over and over if not valid.
12
+ protected $verifiedKey;
13
+ protected $redirectedSettings;
14
+
15
+ // states
16
+ // key is verified is set by checkKey *after* checks and validation
17
+ protected $key_is_verified = false; // this state doesn't have to be the same as the verifiedKey field in DB.
18
+ protected $key_is_empty = false;
19
+ protected $key_is_constant = false;
20
+ protected $key_is_hidden = false;
21
+
22
+ protected static $notified = array();
23
+
24
+ protected $model = array(
25
+ 'apiKey' => array('s' => 'string',
26
+ 'key' => 'wp-short-pixel-apiKey',
27
+ ),
28
+ 'apiKeyTried' => array('s' => 'string',
29
+ 'key' => 'wp-short-pixel-apiKeyTried'
30
+ ),
31
+ 'verifiedKey' => array('s' => 'boolean',
32
+ 'key' => 'wp-short-pixel-verifiedKey',
33
+ ),
34
+ 'redirectedSettings' => array('s' => 'int',
35
+ 'key' => 'wp-short-pixel-redirected-settings',
36
+ ),
37
+ );
38
+
39
+ public $shortPixel;
40
+
41
+ /** Constructor. Check for constants, load the key */
42
+ public function __construct()
43
+ {
44
+ $this->key_is_constant = (defined("SHORTPIXEL_API_KEY")) ? true : false;
45
+ $this->key_is_hidden = (defined("SHORTPIXEL_HIDE_API_KEY")) ? SHORTPIXEL_HIDE_API_KEY : false;
46
+
47
+ }
48
+
49
+ /** Load the key from storage. This can be a constant, or the database. Check if key is valid.
50
+ *
51
+ */
52
+ public function loadKey()
53
+ {
54
+ $this->apiKey = get_option($this->model['apiKey']['key'], false);
55
+ $this->verifiedKey = get_option($this->model['verifiedKey']['key'], false);
56
+ $this->redirectedSettings = get_option($this->model['redirectedSettings']['key'], false);
57
+ $this->apiKeyTried = get_option($this->model['apiKeyTried']['key'], false);
58
+
59
+ if ($this->key_is_constant)
60
+ {
61
+ $key = SHORTPIXEL_API_KEY;
62
+ }
63
+ else
64
+ {
65
+ $key = $this->apiKey;
66
+ }
67
+
68
+ $valid = $this->checkKey($key);
69
+
70
+
71
+ Log::addDebug('Checked Key', array('valid' => $valid, 'verified' => $this->key_is_verified));
72
+ return $valid;
73
+ }
74
+
75
+ protected function update()
76
+ {
77
+ update_option($this->model['apiKey']['key'], trim($this->apiKey));
78
+ update_option($this->model['verifiedKey']['key'], $this->verifiedKey);
79
+ update_option($this->model['redirectedSettings']['key'], $this->redirectedSettings);
80
+ update_option($this->model['apiKeyTried']['key'], $this->apiKeyTried);
81
+ Log::addDebug('Update Verified', array($this->apiKey, $this->verifiedKey));
82
+ }
83
+
84
+ /** Resets the last APIkey that was attempted with validation
85
+ *
86
+ * The last apikey tried is saved to prevent server and notice spamming when using a constant key, or a wrong key in the database without updating.
87
+ */
88
+ public function resetTried()
89
+ {
90
+ if (is_null($this->apiKeyTried))
91
+ {
92
+ return; // if already null, no need for additional activity
93
+ }
94
+ $this->apiKeyTried = null;
95
+ $this->update();
96
+ Log::addDebug('Reset Tried', $this->apiKeyTried);
97
+ }
98
+
99
+ /** Checks the API key to see if we have a validated situation
100
+ * @param $key String The 20-character Shortpixel API Key or empty string
101
+ * @return boolean Returns a boolean indicating valid key or not
102
+ *
103
+ * An Api key can be removed from the system by passing an empty string when the key is not hidden.
104
+ * If the key has changed from stored key, the function will pass a validation request to the server
105
+ * Failing to key a 20char string, or passing an empty key will result in notices.
106
+ */
107
+ public function checkKey($key)
108
+ {
109
+ Log::addDebug("Model, checking key ". $key . ' not -' . $this->apiKeyTried);
110
+ if (strlen($key) == 0)
111
+ {
112
+ // first-timers, redirect to nokey screen
113
+ $this->checkRedirect(); // this should be a one-time thing.
114
+ if($this->key_is_hidden) // hidden empty keys shouldn't be checked
115
+ {
116
+ $this->key_is_verified = $this->verifiedKey;
117
+ return $this->key_is_verified;
118
+ }
119
+ elseif ($key != $this->apiKey)
120
+ {
121
+ Notice::addWarning(__('Your API Key has been removed', 'shortpixel-image-optimiser'));
122
+ $this->clearApiKey(); // notice and remove;
123
+ return false;
124
+ }
125
+ $valid = false;
126
+
127
+ }
128
+ elseif (strlen($key) <> 20 && $key != $this->apiKeyTried)
129
+ {
130
+ $this->NoticeApiKeyLength($key);
131
+ Log::addDebug('Key Wrong Length');
132
+ $valid = $this->verifiedKey; // if we already had a verified key, and a wrong new one is giving keep status.
133
+ }
134
+ elseif( ($key != $this->apiKey || ! $this->verifiedKey) && $key != $this->apiKeyTried)
135
+ {
136
+ Log::addDebug('Validate Key' . $key);
137
+ $valid = $this->validateKey($key);
138
+ }
139
+ elseif($key == $this->apiKey) // all is fine!
140
+ {
141
+ $valid = $this->verifiedKey;
142
+ }
143
+
144
+ // if key is not valid on load, means not valid at all
145
+ if (! $valid)
146
+ {
147
+ $this->verifiedKey = false;
148
+ $this->key_is_verified = false;
149
+ $this->apiKeyTried = $key;
150
+ $this->update();
151
+ }
152
+ else {
153
+ $this->key_is_verified = true;
154
+ }
155
+
156
+ return $this->key_is_verified; // first time this is set! *after* this function
157
+ }
158
+
159
+ public function is_verified()
160
+ {
161
+ return $this->key_is_verified;
162
+ }
163
+
164
+ public function is_constant()
165
+ {
166
+ return $this->key_is_constant;
167
+ }
168
+
169
+ public function is_hidden()
170
+ {
171
+ return $this->key_is_hidden;
172
+ }
173
+
174
+ public function getKey()
175
+ {
176
+ return $this->apiKey;
177
+ }
178
+
179
+ protected function clearApiKey()
180
+ {
181
+ $this->key_is_empty = true;
182
+ $this->apiKey = '';
183
+ $this->verifiedKey = false;
184
+ $this->apiKeyTried = '';
185
+ $this->key_is_verified = false;
186
+ Log::addDebug('Clearing API Key');
187
+
188
+ $this->update();
189
+
190
+ }
191
+
192
+ protected function validateKey($key)
193
+ {
194
+ Log::addDebug('Validating Key ' . $key);
195
+ // first, save Auth to satisfy getquotainformation
196
+
197
+ $quotaData = $this->remoteValidate($key);
198
+ $checked_key = ($quotaData['APIKeyValid']) ? true : false;
199
+
200
+ Log::addDebug('Verify Result', $quotaData);
201
+ Log::addDebug('Key is verified ', array( $this->verifiedKey));
202
+
203
+ if (! $checked_key)
204
+ {
205
+ Notice::addError(sprintf(__('Error during verifying API key: %s','shortpixel-image-optimiser'), $quotaData['Message'] ));
206
+ }
207
+ elseif ($checked_key) {
208
+ $this->apiKey = $key;
209
+ $this->verifiedKey = $checked_key;
210
+ $this->processNewKey($quotaData);
211
+ $this->update();
212
+ }
213
+ return $this->verifiedKey;
214
+ }
215
+
216
+
217
+ /** Process some things when key has been added. This is from original wp-short-pixel.php */
218
+ protected function processNewKey($quotaData)
219
+ {
220
+ $settingsObj = $this->shortPixel->getSettings();
221
+ $lastStatus = $settingsObj->bulkLastStatus;
222
+ if(isset($lastStatus['Status']) && $lastStatus['Status'] == \ShortPixelAPI::STATUS_NO_KEY) {
223
+ $settingsObj->bulkLastStatus = null;
224
+ }
225
+ //display notification
226
+ $urlParts = explode("/", get_site_url());
227
+ if( $quotaData['DomainCheck'] == 'NOT Accessible'){
228
+ $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'));
229
+ Notice::addWarning($notice);
230
+ } else {
231
+ if ( function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY"))
232
+ $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')
233
+ . "<BR> <b>define('SHORTPIXEL_API_KEY', '". $this->apiKey ."');</b>";
234
+ else
235
+ $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');
236
+
237
+ Notice::addSuccess($notice);
238
+ }
239
+
240
+ //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
241
+ if ( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && ! \ShortPixelFolder::createBackUpFolder() )
242
+ {
243
+ $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'),
244
+ WP_CONTENT_DIR . '/' . SHORTPIXEL_UPLOADS_NAME );
245
+ Notice::addError($notice);
246
+ }
247
+ }
248
+
249
+
250
+ protected function NoticeApiKeyLength($key)
251
+ {
252
+ // repress double warning.
253
+ if (isset(self::$notified['apilength']) && self::$notified['apilength'])
254
+ return;
255
+
256
+ $KeyLength = strlen($key);
257
+ $notice = sprintf(__("The key you provided has %s characters. The API key should have 20 characters, letters and numbers only.",'shortpixel-image-optimiser'), $KeyLength)
258
+ . "<BR> <b>"
259
+ . __('Please check that the API key is the same as the one you received in your confirmation email.','shortpixel-image-optimiser')
260
+ . "</b><BR> "
261
+ . __('If this problem persists, please contact us at ','shortpixel-image-optimiser')
262
+ . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
263
+ . __(' or ','shortpixel-image-optimiser')
264
+ . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.";
265
+ self::$notified['apilength'] = true;
266
+ Notice::addError($notice);
267
+ }
268
+
269
+ // Does remote Validation of key. In due time should be replaced with something more lean.
270
+ private function remoteValidate($key)
271
+ {
272
+
273
+ return $this->shortPixel->getQuotaInformation($key, true, true);
274
+ }
275
+
276
+ protected function checkRedirect()
277
+ {
278
+ if(!$this->redirectedSettings && !$this->verifiedKey && (!function_exists("is_multisite") || ! is_multisite())) {
279
+ $this->redirectedSettings = 1;
280
+ $this->update();
281
+ wp_redirect(admin_url("options-general.php?page=wp-shortpixel-settings"));
282
+ exit();
283
+ }
284
+ /*elseif (function_exists('is_multisite') && is_multisite() && !$this->_settings->verifiedKey)
285
+ { // @todo not optimal, License key needs it's own model to do checks upon.
286
+ $scontrolname = \shortPixelTools::namespaceit("SettingsController");
287
+ $scontrol = new $scontrolname();
288
+ $scontrol->setShortPixel($this);
289
+ $scontrol->checkKey();
290
+ } */
291
+ }
292
+
293
+
294
+ }
class/model/directory_model.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ /* Model for Directories
6
+ *
7
+ * For all low-level operations on directories
8
+ *
9
+ */
10
+
11
+ class DirectoryModel extends ShortPixelModel
12
+ {
13
+ // Directory info
14
+ protected $path;
15
+
16
+ // Directory status
17
+ protected $exists = false;
18
+ protected $is_writable = false;
19
+
20
+ protected $new_directory_permission = 0755;
21
+
22
+ /** Creates a directory model object. DirectoryModel directories don't need to exist on FileSystem */
23
+ public function __construct($path)
24
+ {
25
+ //$this->new_directory_permission = octdec(06440);
26
+ $this->path = wp_normalize_path(trailingslashit($path));
27
+ Log::addDebug("DirectoryModel LoadPath - " . $this->path);
28
+ if (file_exists($this->path))
29
+ {
30
+ $this->exists();
31
+ $this->is_writable();
32
+ }
33
+ }
34
+
35
+ public function __toString()
36
+ {
37
+ return (string) $this->path;
38
+ }
39
+
40
+ public function getPath()
41
+ {
42
+ return $this->path;
43
+ }
44
+
45
+ public function exists()
46
+ {
47
+ $this->exists = file_exists($this->path);
48
+ return $this->exists;
49
+ }
50
+
51
+ public function is_writable()
52
+ {
53
+ $this->is_writable = is_writable($this->path);
54
+ return $this->is_writable;
55
+ }
56
+
57
+ /** Checks the directory
58
+ *
59
+ */
60
+ public function check($check_writable = false)
61
+ {
62
+ if (! $this->exists())
63
+ {
64
+ Log::addDebug('Direct does not exists. Try to create recursive ' . $this->path . ' with ' . $this->new_directory_permission);
65
+ $result = @mkdir($this->path, $this->new_directory_permission , true);
66
+ if (! $result)
67
+ {
68
+ $error = error_get_last();
69
+ echo $error['message'];
70
+ Log::addWarn('MkDir failed: ' . $error['message'], array($error));
71
+ }
72
+
73
+ }
74
+ if ($this->exists() && $check_writable && ! $this->is_writable())
75
+ {
76
+ chmod($this->path, $this->new_directory_permission);
77
+ }
78
+
79
+ if (! $this->exists())
80
+ {
81
+ Log::addInfo('Directory does not exist :' . $this->path);
82
+ return false;
83
+ }
84
+ if ($check_writable && !$this->is_writable())
85
+ {
86
+ Log::addInfo('Directory not writable :' . $this->path);
87
+ return false;
88
+ }
89
+ return true;
90
+ }
91
+
92
+
93
+ }
class/model/environment_model.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ class EnvironmentModel extends ShortPixelModel
5
+ {
6
+ // Server and PHP
7
+ public $is_nginx;
8
+ public $is_apache;
9
+ public $is_gd_installed;
10
+ public $is_curl_installed;
11
+
12
+ // MultiSite
13
+ public $is_multisite;
14
+ public $is_mainsite;
15
+
16
+ // Integrations
17
+ public $has_nextgen;
18
+
19
+ public function __construct()
20
+ {
21
+ $this->is_nginx = strpos(strtolower($_SERVER["SERVER_SOFTWARE"]), 'nginx') !== false ? true : false;
22
+ $this->is_apache = strpos(strtolower($_SERVER["SERVER_SOFTWARE"]), 'apache') !== false ? true : false;
23
+ $this->is_gd_installed = function_exists('imagecreatefrompng');
24
+ $this->is_curl_installed = function_exists('curl_init');
25
+
26
+ $this->is_multisite = (function_exists("is_multisite") && is_multisite()) ? true : false;
27
+ $this->is_mainsite = is_main_site();
28
+
29
+ $this->has_nextgen = \ShortPixelNextGenAdapter::hasNextGen();
30
+
31
+ }
32
+ }
class/model/file_model.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ /* FileModel class.
6
+ *
7
+ *
8
+ * - Represents a -single- file.
9
+ * - Can handle any type
10
+ * - Usually controllers would use a collection of files
11
+ * - Meant for all low-level file operations and checks.
12
+ * - Every file can have a backup counterpart.
13
+ *
14
+ */
15
+ class FileModel extends ShortPixelModel
16
+ {
17
+
18
+ // File info
19
+ protected $fullpath;
20
+ protected $filename;
21
+ protected $directory;
22
+ protected $extension;
23
+
24
+ // File Status
25
+ protected $exists = false;
26
+ protected $is_writable = false;
27
+
28
+ protected $backupDirectory;
29
+
30
+ /** Creates a file model object. FileModel files don't need to exist on FileSystem */
31
+ public function __construct($path)
32
+ {
33
+ $this->fullpath = wp_normalize_path($path);
34
+ $this->setFileInfo();
35
+ }
36
+
37
+ public function __toString()
38
+ {
39
+ return (string) $this->fullpath;
40
+ }
41
+
42
+ protected function setFileInfo()
43
+ {
44
+ if (file_exists($this->fullpath))
45
+ {
46
+ $this->exists = true;
47
+ $info = pathinfo($this->fullpath);
48
+ $this->filename = isset($info['basename']) ? $info['basename'] : null;
49
+ $this->extension = isset($info['extension']) ? $info['extension'] : null;
50
+ $this->directory = isset($info['dirname']) ? new DirectoryModel($info['dirname']) : null;
51
+ Log::addDebug('File info', array($info));
52
+ $this->is_writable = is_writable($this->fullpath);
53
+ }
54
+ else {
55
+ $this->exists = false;
56
+ $this->writable = false;
57
+
58
+ if (is_null($this->filename))
59
+ $this->filename = basename($this->fullpath);
60
+
61
+ if (is_null($this->directory))
62
+ $this->directory = new DirectoryModel(dirname($this->fullpath));
63
+ }
64
+ }
65
+
66
+ // Util function to get location of backup Directory.
67
+ private function getBackupDirectory()
68
+ {
69
+ if (is_null($this->backupDirectory))
70
+ {
71
+ $backup_dir = str_replace(get_home_path(), "", $this->directory->getPath());
72
+ Log::addDebug('Bkup ' . get_home_path() . ' ' . $this->directory->getPath() . '-->' . $backup_dir );
73
+ $backupDirectory = SHORTPIXEL_BACKUP_FOLDER . '/' . $backup_dir;
74
+ $directory = new DirectoryModel($backupDirectory);
75
+
76
+ if (! $directory->exists()) // check if exists. FileModel should not attempt to create.
77
+ return false;
78
+
79
+ $this->backupDirectory = $directory;
80
+ }
81
+
82
+ return $this->backupDirectory;
83
+ }
84
+
85
+ public function exists()
86
+ {
87
+ $this->exists = file_exists($this->fullpath);
88
+ return $this->exists;
89
+ }
90
+
91
+ public function is_writable()
92
+ {
93
+ $this->is_writable = is_writable($this->fullpath);
94
+ return $this->is_writable;
95
+ }
96
+
97
+ public function hasBackup()
98
+ {
99
+ $directory = $this->getBackupDirectory();
100
+ if (! $directory)
101
+ return false;
102
+
103
+ $backupFile = $directory . $this->filename;
104
+
105
+ if (file_exists($backupFile))
106
+ return true;
107
+ else {
108
+ return false;
109
+ }
110
+ }
111
+
112
+ /** Tries to retrieve an *existing* BackupFile. Returns false if not present.
113
+ * This file might not be writable.
114
+ * To get writable directory reference to backup, use FileSystemController
115
+ */
116
+ public function getBackupFile()
117
+ {
118
+ if ($this->hasBackup())
119
+ return new FileModel($this->getBackupDirectory() . $this->filename);
120
+ else
121
+ return false;
122
+ }
123
+
124
+ public function getFileDir()
125
+ {
126
+ return $this->directory;
127
+ }
128
+
129
+ /** Copy a file to somewhere
130
+ *
131
+ * @param $destination String Full Path to new file.
132
+ */
133
+ public function copy(FileModel $destination)
134
+ {
135
+ $status = copy($this->getFullPath(), $destination->getFullPath());
136
+ if (! $status)
137
+ Log::addWarn('Could not copy file ' . $this->getFullPath() . ' to' . $destination->getFullPath());
138
+ else {
139
+ $destination->setFileInfo(); // refresh info.
140
+ }
141
+ return $status;
142
+ }
143
+
144
+ /** Deletes current file
145
+ * This uses the WP function since it has a filter that might be useful
146
+ */
147
+ public function delete()
148
+ {
149
+ \wp_delete_file($this->fullpath);
150
+ if (! file_exists($this->fullpath))
151
+ {
152
+ $this->setFileInfo(); // update info
153
+ return true;
154
+ }
155
+ else {
156
+ return false;
157
+ Log::addWarn('File seems not removed - ' . $this->fullpath);
158
+ }
159
+ }
160
+
161
+ public function getFullPath()
162
+ {
163
+ return $this->fullpath;
164
+ }
165
+
166
+ public function getFileName()
167
+ {
168
+ return $this->filename;
169
+ }
170
+
171
+ public function getExtension()
172
+ {
173
+ return $this->extension;
174
+ }
175
+
176
+ }
177
+
178
+ /*
179
+ // do this before putting the meta down, since maybeDump check for last timestamp
180
+ $URLsAndPATHs = $itemHandler->getURLsAndPATHs(false);
181
+ $this->maybeDumpFromProcessedOnServer($itemHandler, $URLsAndPATHs);
182
+
183
+ */
class/shortpixel-model.php CHANGED
@@ -1,8 +1,6 @@
1
  <?php
2
  namespace ShortPixel;
3
- use ShortPixel\ShortPixelLogger as Log;
4
- use ShortPixel\DebugItem as DebugItem;
5
-
6
 
7
  abstract class ShortPixelModel
8
  {
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
 
 
4
 
5
  abstract class ShortPixelModel
6
  {
class/shortpixel_queue.php CHANGED
@@ -1,9 +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;
@@ -26,6 +27,7 @@ class ShortPixelQueue {
26
  }
27
 
28
  public static function get() {
 
29
  $fp = self::openQ(LOCK_SH);
30
  if(!$fp) return array();
31
  $itemsRaw = fgets($fp);
@@ -71,6 +73,7 @@ class ShortPixelQueue {
71
  }
72
 
73
  protected static function openQ($lock = LOCK_EX) {
 
74
  $queueName = SHORTPIXEL_UPLOADS_BASE . "/.shortpixel-q-" . get_current_blog_id();
75
  $fp = @fopen($queueName, "r+");
76
  if(!$fp) {
@@ -343,7 +346,7 @@ class ShortPixelQueue {
343
  return $this->settings->bulkType;
344
  }
345
 
346
- // hack
347
  public function getBulkTypeForDisplay()
348
  {
349
  $bulk = $this->settings->bulkType;
@@ -366,7 +369,7 @@ class ShortPixelQueue {
366
  $bulkStartId = $this->getFlagBulkId();
367
  $this->settings->cancelPointer = $cancelPointer;//we save this so we can resume bulk processing
368
  $this->settings->skipToCustom = NULL;
369
- WPShortPixel::log("PAUSE: Pointer = ".$this->settings->cancelPointer);
370
  //remove the bulk items from prio queue
371
  foreach($this->get() as $qItem) {
372
  if($qItem < $bulkStartId) {
@@ -378,7 +381,7 @@ class ShortPixelQueue {
378
 
379
  public function cancelBulk() {
380
  $this->pauseBulk();
381
- WPShortPixel::log("STOP, delete pointer.");
382
  $this->settings->cancelPointer = NULL;
383
  }
384
 
1
  <?php
2
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
3
 
4
  class ShortPixelQueue {
5
 
6
+ protected $ctrl;
7
+ protected $settings;
8
 
9
  const BULK_TYPE_OPTIMIZE = 0;
10
  const BULK_TYPE_RESTORE = 1;
27
  }
28
 
29
  public static function get() {
30
+
31
  $fp = self::openQ(LOCK_SH);
32
  if(!$fp) return array();
33
  $itemsRaw = fgets($fp);
73
  }
74
 
75
  protected static function openQ($lock = LOCK_EX) {
76
+
77
  $queueName = SHORTPIXEL_UPLOADS_BASE . "/.shortpixel-q-" . get_current_blog_id();
78
  $fp = @fopen($queueName, "r+");
79
  if(!$fp) {
346
  return $this->settings->bulkType;
347
  }
348
 
349
+ // hack
350
  public function getBulkTypeForDisplay()
351
  {
352
  $bulk = $this->settings->bulkType;
369
  $bulkStartId = $this->getFlagBulkId();
370
  $this->settings->cancelPointer = $cancelPointer;//we save this so we can resume bulk processing
371
  $this->settings->skipToCustom = NULL;
372
+ Log::addDebug("PAUSE: Pointer = ".$this->settings->cancelPointer);
373
  //remove the bulk items from prio queue
374
  foreach($this->get() as $qItem) {
375
  if($qItem < $bulkStartId) {
381
 
382
  public function cancelBulk() {
383
  $this->pauseBulk();
384
+ Log::addDebug("STOP, delete pointer.");
385
  $this->settings->cancelPointer = NULL;
386
  }
387
 
class/view/settings/part-general.php CHANGED
@@ -28,7 +28,7 @@
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")) {
@@ -43,10 +43,10 @@
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>
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') ?> >
32
  <?php
33
  }
34
  elseif(defined("SHORTPIXEL_API_KEY")) {
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>
class/view/settings/part-nokey.php CHANGED
@@ -1,7 +1,7 @@
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.
@@ -16,9 +16,9 @@ else {
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
  {
@@ -82,7 +82,10 @@ if($adminEmail == 'noreply@addendio.com') $adminEmail = false; //hack for the ad
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>
@@ -111,8 +114,8 @@ if($adminEmail == 'noreply@addendio.com') $adminEmail = false; //hack for the ad
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
 
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\Notices\NoticeController as Notice;
4
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
5
 
6
  $canValidate = false;
7
  // Several conditions for showing API key.
16
  $editApiKey = (! $this->is_constant_key && $showApiKey) ? true : false;
17
 
18
  // Notices for fringe cases
19
+ if (! $this->is_verifiedkey && $this->hide_api_key && ! $this->is_constant_key)
20
  {
21
+ Notice::addError(__('wp-config.php is hiding the API key, but no API key was found. Remove the constant, or define the SHORTPIXEL_API_KEY constant as well', 'shortpixel-image-optimiser'));
22
  }
23
  elseif ($this->is_constant_key && ! $this->is_verifiedkey)
24
  {
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')) ?>"
86
+ id="shortpixel-form-nokey">
87
+ <?php wp_nonce_field($this->form_action, 'sp-nonce'); ?>
88
+
89
  <table class="form-table">
90
  <tbody>
91
  <tr>
114
  <?php } ?>
115
  <input type="hidden" name="validate" id="valid" value="validate"/>
116
  <span class="spinner" id="pluginemail_spinner" style="float:none;"></span>
117
+ <button type="submit" id="validate" class="button button-primary" title="<?php _e('Validate the provided API key','shortpixel-image-optimiser');?>"
118
+ >
119
  <?php _e('Validate','shortpixel-image-optimiser');?>
120
  </button>
121
 
class/view/settings/part-statistics.php CHANGED
@@ -113,7 +113,15 @@ $quotaData = $this->quotaData;
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>
113
  </td>
114
  </tr>
115
  <tr>
116
+ <?php
117
+
118
+ if ($this->hide_api_key)
119
+ $link = 'https://shortpixel.com/login';
120
+ else {
121
+ $link = 'https://' . SHORTPIXEL_API . '/v2/report.php?key=' . $view->data->apiKey;
122
+ }
123
+ ?>
124
+ <th><a href="<?php echo $link ?>" target="_blank">
125
  <?php _e('See report (last 40 days)','shortpixel-image-optimiser');?>
126
  </a></th>
127
  <td>&nbsp;</td>
class/view/shortpixel-list-table.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
 
3
  if( ! class_exists( 'WP_List_Table' ) ) {
4
  require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
@@ -260,15 +262,15 @@ class ShortPixelListTable extends WP_List_Table {
260
  }
261
 
262
  public function action_optimize_image( $id ) {
263
- $this->ctrl->optimizeCustomImage($id);
264
  }
265
 
266
  public function action_restore_image( $id ) {
267
- $this->ctrl->doCustomRestore($id);
268
  }
269
 
270
  public function action_redo_image( $id, $type = false ) {
271
- $this->ctrl->redo('C-' . $id, $type);
272
  }
273
 
274
  public function process_actions() {
@@ -291,7 +293,10 @@ class ShortPixelListTable extends WP_List_Table {
291
  if (!wp_verify_nonce($nonce, 'sp_restore_image')) {
292
  die('Error.');
293
  } else {
294
- $this->action_restore_image(absint($_GET['image']));
 
 
 
295
  wp_redirect($redirect_url);
296
  exit;
297
  }
1
  <?php
2
+ use ShortPixel\Notices\NoticeController as Notices;
3
+
4
 
5
  if( ! class_exists( 'WP_List_Table' ) ) {
6
  require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
262
  }
263
 
264
  public function action_optimize_image( $id ) {
265
+ return $this->ctrl->optimizeCustomImage($id);
266
  }
267
 
268
  public function action_restore_image( $id ) {
269
+ return $this->ctrl->doCustomRestore($id);
270
  }
271
 
272
  public function action_redo_image( $id, $type = false ) {
273
+ return $this->ctrl->redo('C-' . $id, $type);
274
  }
275
 
276
  public function process_actions() {
293
  if (!wp_verify_nonce($nonce, 'sp_restore_image')) {
294
  die('Error.');
295
  } else {
296
+ if($this->action_restore_image(absint($_GET['image'])))
297
+ {
298
+ Notices::addSuccess(__('File Successfully restored', 'shortpixel-image-optimiser'));
299
+ }
300
  wp_redirect($redirect_url);
301
  exit;
302
  }
class/view/shortpixel_view.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- use \ShortPixel\ShortPixelLogger as Log;
3
 
4
  class ShortPixelView {
5
 
@@ -153,6 +153,8 @@ class ShortPixelView {
153
  echo('<li class="sp-conflict-plugins-list"><strong>' . $plugin['name'] . '</strong>');
154
  echo('<a href="' . $link . '" class="button button-primary">'
155
  . __( $action, 'shortpixel_image_optimiser' ) . '</a>');
 
 
156
  }
157
  echo("</ul>");
158
  break;
@@ -399,7 +401,7 @@ class ShortPixelView {
399
 
400
  jQuery(".sp-bulk-summary").spTooltip({
401
  tooltipSource: "inline",
402
- tooltipSourceID: "#sp-bulk-stats"
403
  });
404
  });
405
  !function(d,s,id){//Just optimized my site with ShortPixel image optimization plugin
@@ -422,7 +424,7 @@ class ShortPixelView {
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>
@@ -614,6 +616,10 @@ class ShortPixelView {
614
  <span class="sp-err-title"><?php _e('Error processing file:','shortpixel-image-optimiser');?><br></span>
615
  <span class="sp-err-content"><?php echo $message; ?></span> <a class="sp-post-link" href="<?php echo(get_admin_url());?>/post.php?post=__ID__&action=edit" target="_blank">placeholder.png</a>
616
  </div>
 
 
 
 
617
  </div>
618
  </div>
619
  <div class="bulk-progress bulk-slider-container sp-notice sp-notice-info sp-floating-block sp-full-width">
@@ -664,8 +670,7 @@ class ShortPixelView {
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>
@@ -716,7 +721,6 @@ class ShortPixelView {
716
  <div class="bulk-estimate">
717
  &nbsp;<?php echo($message);?>
718
  </div>
719
- <?php if (true || ($type & 1)) { //now we display the action buttons always when a type of bulk is running ?>
720
  <form action='' method='POST' style="display:inline;">
721
  <input type="submit" class="button button-primary bulk-cancel" onclick="clearBulkProcessor();"
722
  name="bulkProcessStop" value="Stop" style="margin-left:10px"/>
@@ -728,9 +732,7 @@ class ShortPixelView {
728
  <?php }?>
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
736
  }
@@ -1696,10 +1698,10 @@ class ShortPixelView {
1696
  <?php _e('Cleanup&Retry','shortpixel-image-optimiser');?>
1697
  </a> <?php
1698
  } else {
1699
- if($data['status'] == 'retry') { ?>
1700
  <div style="overflow:hidden">
1701
  <a class="button button-smaller sp-action-restore" href="admin.php?action=shortpixel_restore_backup&attachment_ID=<?php echo($id)?>" style="margin-left:5px;"
1702
- title="Cleanup the metadata and return the image to the status before the error.">
1703
  <?php _e('Cleanup','shortpixel-image-optimiser');?>
1704
  </a>
1705
  <?php } ?>
@@ -1714,19 +1716,26 @@ class ShortPixelView {
1714
  case 'imgOptimized':
1715
  $excluded = (isset($data['excludeSizes']) ? count($data['excludeSizes']) : 0);
1716
  $successText = $this->getSuccessText($data['percent'],$data['bonus'],$data['type'],$data['thumbsOpt'],$data['thumbsTotal'], $data['retinasOpt'], $data['excludeSizes']);
1717
- $missingThumbs = $excludeSizes = '';
1718
  if($extended) {
 
 
 
 
 
 
 
1719
  if(isset($data['excludeSizes'])) {
1720
- $excludeSizes .= "<br><span> <span style='font-weight: bold;'>" . __("Excluded thumbnails:", 'shortpixel-image-optimiser') . "</span>";
1721
  foreach($data['excludeSizes'] as $excludedItem) {
1722
- $excludeSizes .= "<br> &#8226; " . $excludedItem;
1723
  }
1724
  $excludeSizes .= '</span>';
1725
  }
1726
  if(count($data['thumbsMissing'])) {
1727
- $missingThumbs .= "<br><span> <span style='font-weight: bold;'>" . __("Missing thumbnails:", 'shortpixel-image-optimiser') . "</span>";
1728
  foreach($data['thumbsMissing'] as $miss) {
1729
- $missingThumbs .= "<br> &#8226; " . $miss;
1730
  }
1731
  $missingThumbs .= '</span>';
1732
  }
@@ -1734,7 +1743,7 @@ class ShortPixelView {
1734
  . "<br>EXIF: " . ($data['exifKept'] ? __('kept','shortpixel-image-optimiser') : __('removed','shortpixel-image-optimiser'))
1735
  . ($data['png2jpg'] ? '<br>' . __('Converted from PNG','shortpixel-image-optimiser'): '')
1736
  . "<br>" . __("Optimized on", 'shortpixel-image-optimiser') . ": " . $data['date']
1737
- . $excludeSizes . $missingThumbs;
1738
  }
1739
  $this->renderListCell($id, $data['status'], $data['showActions'], $data['thumbsToOptimize'],
1740
  $data['backup'], $data['type'], $data['invType'], $successText);
1
  <?php
2
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
3
 
4
  class ShortPixelView {
5
 
153
  echo('<li class="sp-conflict-plugins-list"><strong>' . $plugin['name'] . '</strong>');
154
  echo('<a href="' . $link . '" class="button button-primary">'
155
  . __( $action, 'shortpixel_image_optimiser' ) . '</a>');
156
+ if($plugin['details']) echo('<br>');
157
+ if($plugin['details']) echo('<span>' . $plugin['details'] . '</span>');
158
  }
159
  echo("</ul>");
160
  break;
401
 
402
  jQuery(".sp-bulk-summary").spTooltip({
403
  tooltipSource: "inline",
404
+ tooltipSourceID: "#sp-bulk-stats",
405
  });
406
  });
407
  !function(d,s,id){//Just optimized my site with ShortPixel image optimization plugin
424
  </div>
425
  <?php } ?>
426
  </div>
427
+ <div id="sp-bulk-stats" class='<?php echo $hider ?>' style="display:none">
428
  <?php $this->displayBulkStats($quotaData['totalProcessedFiles'], $quotaData['mainProcessedFiles'], $under5PercentCount, $averageCompression, $savedSpace);?>
429
  </div>
430
  </div>
616
  <span class="sp-err-title"><?php _e('Error processing file:','shortpixel-image-optimiser');?><br></span>
617
  <span class="sp-err-content"><?php echo $message; ?></span> <a class="sp-post-link" href="<?php echo(get_admin_url());?>/post.php?post=__ID__&action=edit" target="_blank">placeholder.png</a>
618
  </div>
619
+ <div class="bulk-notice-msg bulk-searching">
620
+ <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/loading-dark-big.gif' ));?>">
621
+ <?php _e('Please bear with me. ShortPixel is checking many already optimized images to see if they\'re OK, so the progress bar could stop for a while.','shortpixel-image-optimiser');?><br>
622
+ </div>
623
  </div>
624
  </div>
625
  <div class="bulk-progress bulk-slider-container sp-notice sp-notice-info sp-floating-block sp-full-width">
670
  <?php } ?>
671
 
672
  <?php
673
+ Log::addDebug('Shortpixel View - Bulktype', array('b' => $this->bulkType));
 
674
  if ($this->bulkType != ShortPixelQueue::BULK_TYPE_RESTORE): ?>
675
  <div class="bulk-progress-indicator">
676
  <div style="margin-bottom:5px"><?php _e('Average reduction','shortpixel-image-optimiser');?></div>
721
  <div class="bulk-estimate">
722
  &nbsp;<?php echo($message);?>
723
  </div>
 
724
  <form action='' method='POST' style="display:inline;">
725
  <input type="submit" class="button button-primary bulk-cancel" onclick="clearBulkProcessor();"
726
  name="bulkProcessStop" value="Stop" style="margin-left:10px"/>
732
  <?php }?>
733
  <span class="resumeLabel"><?php echo( !$running ? __('Resume: ','shortpixel-image-optimiser') : "");?></span>
734
  </form>
735
+
 
 
736
  </div>
737
  <?php
738
  }
1698
  <?php _e('Cleanup&Retry','shortpixel-image-optimiser');?>
1699
  </a> <?php
1700
  } else {
1701
+ if($data['status'] == 'retry' && (isset($data['backup']) && $data['backup']) ) { ?>
1702
  <div style="overflow:hidden">
1703
  <a class="button button-smaller sp-action-restore" href="admin.php?action=shortpixel_restore_backup&attachment_ID=<?php echo($id)?>" style="margin-left:5px;"
1704
+ title="<?php _e('Restore Image from Backup', 'shortpixel-image-optimiser') ?>">
1705
  <?php _e('Cleanup','shortpixel-image-optimiser');?>
1706
  </a>
1707
  <?php } ?>
1716
  case 'imgOptimized':
1717
  $excluded = (isset($data['excludeSizes']) ? count($data['excludeSizes']) : 0);
1718
  $successText = $this->getSuccessText($data['percent'],$data['bonus'],$data['type'],$data['thumbsOpt'],$data['thumbsTotal'], $data['retinasOpt'], $data['excludeSizes']);
1719
+ $todoSizes = $missingThumbs = $excludeSizes = '';
1720
  if($extended) {
1721
+ if(isset($data['thumbsToOptimizeList']) && count($data['thumbsToOptimizeList'])) {
1722
+ $todoSizes .= "<br><span style='word-break: break-all;'> <span style='font-weight: bold;'>" . __("To optimize:", 'shortpixel-image-optimiser') . "</span>";
1723
+ foreach($data['thumbsToOptimizeList'] as $todoItem) {
1724
+ $todoSizes .= "<br> &#8226;&nbsp;" . $todoItem;
1725
+ }
1726
+ $excludeSizes .= '</span>';
1727
+ }
1728
  if(isset($data['excludeSizes'])) {
1729
+ $excludeSizes .= "<br><span style='word-break: break-all;'> <span style='font-weight: bold;'>" . __("Excluded thumbnails:", 'shortpixel-image-optimiser') . "</span>";
1730
  foreach($data['excludeSizes'] as $excludedItem) {
1731
+ $excludeSizes .= "<br> &#8226;&nbsp;" . $excludedItem;
1732
  }
1733
  $excludeSizes .= '</span>';
1734
  }
1735
  if(count($data['thumbsMissing'])) {
1736
+ $missingThumbs .= "<br><span style='word-break: break-all;'> <span style='font-weight: bold;'>" . __("Missing thumbnails:", 'shortpixel-image-optimiser') . "</span>";
1737
  foreach($data['thumbsMissing'] as $miss) {
1738
+ $missingThumbs .= "<br> &#8226&nbsp;" . $miss;
1739
  }
1740
  $missingThumbs .= '</span>';
1741
  }
1743
  . "<br>EXIF: " . ($data['exifKept'] ? __('kept','shortpixel-image-optimiser') : __('removed','shortpixel-image-optimiser'))
1744
  . ($data['png2jpg'] ? '<br>' . __('Converted from PNG','shortpixel-image-optimiser'): '')
1745
  . "<br>" . __("Optimized on", 'shortpixel-image-optimiser') . ": " . $data['date']
1746
+ . $todoSizes . $excludeSizes . $missingThumbs;
1747
  }
1748
  $this->renderListCell($id, $data['status'], $data['showActions'], $data['thumbsToOptimize'],
1749
  $data['backup'], $data['type'], $data['invType'], $successText);
class/view/view-restore-all.php CHANGED
@@ -42,7 +42,7 @@
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 -->
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-optimiser'); ?></p>
46
  </div>
47
 
48
  </div> <!-- sp-notice -->
class/view/view-settings.php CHANGED
@@ -1,8 +1,8 @@
1
  <?php
2
  namespace ShortPixel;
3
- use Shortpixel\ShortPixelLogger as Log;
4
 
5
- HelpScout::outputBeacon($view->data->apiKey);
6
 
7
  ?>
8
  <div class="wrap">
@@ -29,6 +29,7 @@ HelpScout::outputBeacon($view->data->apiKey);
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');
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
 
5
+ HelpScout::outputBeacon($this->hide_api_key ? '' : $view->data->apiKey);
6
 
7
  ?>
8
  <div class="wrap">
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
+ <?php wp_nonce_field($this->form_action, 'sp-nonce'); ?>
33
  <div class='section-wrapper'>
34
  <?php
35
  $this->loadView('settings/part-general');
class/wp-short-pixel.php CHANGED
@@ -1,9 +1,7 @@
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
 
@@ -26,8 +24,9 @@ class WPShortPixel {
26
 
27
  public function __construct() {
28
  $this->timer = time();
 
29
 
30
- if (SHORTPIXEL_DEBUG === true) {
31
  $this->jsSuffix = '.js'; //use unminified versions for easier debugging
32
  }
33
 
@@ -40,14 +39,9 @@ class WPShortPixel {
40
  $this->cloudflareApi = new ShortPixelCloudFlareApi($this->_settings->cloudflareEmail, $this->_settings->cloudflareAuthKey, $this->_settings->cloudflareZoneID);
41
  $this->hasNextGen = ShortPixelNextGenAdapter::hasNextGen();
42
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $this->_settings->excludePatterns);
43
- $this->prioQ = new ShortPixelQueue($this, $this->_settings);
44
  $this->view = new ShortPixelView($this);
45
 
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
 
@@ -166,12 +160,28 @@ class WPShortPixel {
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
 
 
 
 
 
 
 
 
175
  }
176
 
177
  //handling older
@@ -206,7 +216,12 @@ class WPShortPixel {
206
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $settings->excludePatterns);
207
  $spMetaDao->dropTables();
208
  }
209
- if(WPShortPixelSettings::getOpt('deliverWebp') == 3) {
 
 
 
 
 
210
  self::alterHtaccess(); //add the htaccess lines
211
  }
212
  WPShortPixelSettings::onActivate();
@@ -215,9 +230,15 @@ class WPShortPixel {
215
  public static function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
216
  {
217
  ShortPixelQueue::resetBulk();
218
- ShortPixelQueue::resetPrio();
219
  WPShortPixelSettings::onDeactivate();
220
- self::alterHtaccess(true);
 
 
 
 
 
 
221
  @unlink(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
222
  }
223
 
@@ -313,18 +334,22 @@ class WPShortPixel {
313
  */
314
  );
315
  if($this->_settings->processThumbnails) {
 
 
316
  $conflictPlugins = array_merge($conflictPlugins, array(
317
- 'Regenerate Thumbnails: recreating image files may require re-optimization of the resulting thumbnails, even if they were previously optimized.'
318
  => array(
319
  'action'=>'Deactivate',
320
  'data'=>'regenerate-thumbnails/regenerate-thumbnails.php',
321
- 'page'=>'regenerate-thumbnails'
 
322
  ),
323
- 'Force Regenerate Thumbnails: recreating image files may require re-optimization of the resulting thumbnails, even if they were previously optimized.'
324
  => array(
325
  'action'=>'Deactivate',
326
  'data'=>'force-regenerate-thumbnails/force-regenerate-thumbnails.php',
327
- 'page'=>'force-regenerate-thumbnails'
 
328
  )
329
  ));
330
  }
@@ -334,12 +359,13 @@ class WPShortPixel {
334
  $data = ( isset($path['data']) ) ? $path['data'] : null;
335
  $href = ( isset($path['href']) ) ? $path['href'] : null;
336
  $page = ( isset($path['page']) ) ? $path['page'] : null;
 
337
  if(is_plugin_active($data)) {
338
  if( $data == 'jetpack/jetpack.php' ){
339
  $jetPackPhoton = get_option('jetpack_active_modules') ? in_array('photon', get_option('jetpack_active_modules')) : false;
340
  if( !$jetPackPhoton ){ continue; }
341
  }
342
- $found[] = array( 'name' => $name, 'action'=> $action, 'path' => $data, 'href' => $href , 'page' => $page );
343
  }
344
  }
345
  return $found;
@@ -349,7 +375,8 @@ class WPShortPixel {
349
  * TODO - Probably should be a controller
350
  */
351
  public function displayAdminNotices() {
352
- if(!ShortPixelQueue::testQ()) {
 
353
  ShortPixelView::displayActivationNotice('fileperms');
354
  }
355
  if($this->catchNotice()) { //notices for errors like for example a failed restore notice - these are one time so display them with priority.
@@ -472,7 +499,7 @@ class WPShortPixel {
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 {
@@ -523,6 +550,7 @@ class WPShortPixel {
523
  'STATUS_RETRY'=>ShortPixelAPI::STATUS_RETRY,
524
  'STATUS_QUEUE_FULL'=>ShortPixelAPI::STATUS_QUEUE_FULL,
525
  'STATUS_MAINTENANCE'=>ShortPixelAPI::STATUS_MAINTENANCE,
 
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,
@@ -530,7 +558,7 @@ class WPShortPixel {
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() )
@@ -576,6 +604,8 @@ class WPShortPixel {
576
 
577
  wp_enqueue_script('jquery.knob.min.js', plugins_url('/res/js/jquery.knob.min.js',SHORTPIXEL_PLUGIN_FILE) );
578
  wp_enqueue_script('jquery.tooltip.min.js', plugins_url('/res/js/jquery.tooltip.min.js',SHORTPIXEL_PLUGIN_FILE) );
 
 
579
  wp_enqueue_script('punycode.min.js', plugins_url('/res/js/punycode.min.js',SHORTPIXEL_PLUGIN_FILE) );
580
  }
581
 
@@ -1097,7 +1127,7 @@ class WPShortPixel {
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,35 +1136,53 @@ class WPShortPixel {
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;
1115
  $startQueryID = $crtStartQueryID;
1116
  if(!count($idList)) { //none found so far, so decrease the start ID
1117
- self::log("GETDB: empty slice. setStartBulkID to $startQueryID");
1118
  $this->prioQ->setStartBulkId($startQueryID);
1119
  }
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);
@@ -1162,11 +1210,16 @@ class WPShortPixel {
1162
  $skippedAlreadyProcessed++;
1163
  }
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);
@@ -1176,22 +1229,24 @@ class WPShortPixel {
1176
  if(count($itemList) > SHORTPIXEL_PRESEND_ITEMS) break;
1177
  }
1178
  }
 
1179
  elseif($itemMetaData->meta_key == '_wp_attachment_metadata') { //count skipped
1180
  $skippedAlreadyProcessed++;
1181
  }
 
1182
  }
1183
  }
1184
  if(!count($idList) && $crtStartQueryID <= $startQueryID) {
1185
  //daca n-am adaugat niciuna pana acum, n-are sens sa mai selectez zona asta de id-uri in bulk-ul asta.
1186
  $leapStart = $this->prioQ->getStartBulkId();
1187
- $crtStartQueryID = $startQueryID = $itemMetaData->post_id - 1; //decrement it so we don't select it again
1188
  $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->_settings, $leapStart, $crtStartQueryID);
1189
  $skippedAlreadyProcessed += $res["mainProcessedFiles"] - $res["mainProc".($this->getCompressionType() == 1 ? "Lossy" : "Lossless")."Files"];
1190
- self::log("GETDB: empty list. setStartBulkID to $startQueryID");
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));
@@ -1215,7 +1270,7 @@ class WPShortPixel {
1215
 
1216
  /** Checks the API key **/
1217
  private function checkKey($ID) {
1218
- if( $this->_settings->verifiedKey == false) {
1219
  if($ID == null){
1220
  $ids = $this->getFromPrioAndCheck(1);
1221
  $itemHandler = (count($ids) > 0 ? $ids[0] : null);
@@ -1229,11 +1284,23 @@ class WPShortPixel {
1229
  private function sendEmptyQueue() {
1230
  $avg = $this->getAverageCompression();
1231
  $fileCount = $this->_settings->fileCount;
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
  $response = array("Status" => self::BULK_EMPTY_QUEUE,
1233
  /* translators: console message Empty queue 1234 -> 1234 */
1234
  "Message" => __('Empty queue ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
1235
- "BulkStatus" => ($this->prioQ->bulkRunning()
1236
- ? "1" : ($this->prioQ->bulkPaused() ? "2" : "0")),
1237
  "AverageCompression" => $avg,
1238
  "FileCount" => $fileCount,
1239
  "BulkPercent" => $this->prioQ->getBulkPercent());
@@ -1258,8 +1325,8 @@ class WPShortPixel {
1258
  }
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()
@@ -1320,7 +1387,8 @@ class WPShortPixel {
1320
  //die("za stop 2");
1321
 
1322
  //self::log("HIP: 1 Ids: ".json_encode($ids));
1323
- if(count($ids)) {$idl='';foreach($ids as $i){$idl.=$i->getId().' ';} self::log("HIP: 1 Selected IDs: $idl");}
 
1324
 
1325
  //2: Send up to SHORTPIXEL_PRESEND_ITEMS files to the server for processing
1326
  for($i = 0, $itemHandler = false; $ids !== false && $i < min(SHORTPIXEL_PRESEND_ITEMS, count($ids)); $i++) {
@@ -1351,7 +1419,7 @@ class WPShortPixel {
1351
  if (!$itemHandler){
1352
  //if searching, than the script is searching for not processed items and found none yet, should be relaunced
1353
  if(isset($res['searching']) && $res['searching']) {
1354
- die(json_encode(array("Status" => ShortPixelAPI::STATUS_RETRY,
1355
  "Message" => __('Searching images to optimize... ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId() )));
1356
  }
1357
  //in this case the queue is really empty
@@ -1450,6 +1518,7 @@ class WPShortPixel {
1450
  $bkThumb = $backupUrl . $urlBkPath . $thumb;
1451
  }
1452
  if(strlen($thumb)) {
 
1453
  if($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
1454
  $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
1455
  $urlPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
@@ -2117,7 +2186,7 @@ class WPShortPixel {
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
2122
  $itemHandler = new ShortPixelMetaFacade('C-' . $ID);
2123
  $meta = $itemHandler->getMeta();
@@ -2137,37 +2206,48 @@ class WPShortPixel {
2137
  $fullSubDir = str_replace(get_home_path(), "", dirname($file)) . '/';
2138
  $bkFile = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . ShortPixelAPI::MB_basename($file);
2139
 
2140
- if(file_exists($bkFile)) {
2141
- $rename_result = @rename($bkFile, $file);
2142
- if (! $rename_result)
2143
- {
2144
- Log::addError('Failure on rename to : ' . $file);
2145
- }
2146
 
 
 
2147
 
2148
- /* [BS] Reset all generated image meta. Bring back to start state.
2149
- * Since Wpdb->prepare doesn't support 'null', zero values in this table should not be trusted */
 
 
 
 
2150
 
2151
- $meta->setTsOptimized(0);
2152
- $meta->setCompressedSize(0);
2153
- $meta->setCompressionType(0);
2154
- $meta->setKeepExif(0);
2155
- $meta->setCmyk2rgb(0);
2156
- $meta->setMessage('');
2157
- $meta->setRetries(0);
2158
- $meta->setBackup(0);
2159
- $meta->setResizeWidth(0);
2160
- $meta->setResizeHeight(0);
2161
- $meta->setResize(0);
2162
 
2163
- $meta->setStatus(3);
2164
- $this->spMetaDao->update($meta);
2165
 
 
 
 
 
 
 
 
 
 
 
 
2166
 
2167
- }
2168
- else {
2169
- Log::addWarn('File ' . $bkFile . ' not found in backups');
2170
- }
 
2171
 
2172
  return $meta;
2173
  }
@@ -2198,7 +2278,19 @@ class WPShortPixel {
2198
 
2199
  if(ShortPixelMetaFacade::isCustomQueuedId($qID)) {
2200
  $ID = ShortPixelMetaFacade::stripQueuedIdType($qID);
2201
- $meta = $this->doCustomRestore($ID);
 
 
 
 
 
 
 
 
 
 
 
 
2202
  if($meta) {
2203
  $meta->setCompressionType(ShortPixelAPI::getCompressionTypeCode($compressionType));
2204
  $meta->setStatus(1);
@@ -2243,7 +2335,8 @@ class WPShortPixel {
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
 
2249
  if (! isset($meta['ShortPixelImprovement']))
@@ -2434,7 +2527,7 @@ class WPShortPixel {
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);
@@ -2581,6 +2674,8 @@ class WPShortPixel {
2581
 
2582
  if(isset($_POST["bulkRestore"]))
2583
  {
 
 
2584
  $bulkRestore = new \ShortPixel\BulkRestoreAll(); // controller
2585
  $bulkRestore->setShortPixel($this);
2586
  $bulkRestore->setupBulk();
@@ -2591,25 +2686,31 @@ class WPShortPixel {
2591
 
2592
  if(isset($_POST["bulkCleanup"]))
2593
  {
 
2594
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_CLEANUP);
2595
  $this->_settings->customBulkPaused = 0;
2596
  }//end bulk restore was clicked
2597
 
2598
  if(isset($_POST["bulkCleanupPending"]))
2599
  {
 
2600
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING);
2601
  $this->_settings->customBulkPaused = 0;
2602
  }//end bulk restore was clicked
2603
 
2604
  if(isset($_POST["bulkProcessResume"]))
2605
  {
 
2606
  $this->prioQ->resumeBulk();
2607
  $this->_settings->customBulkPaused = 0;
2608
  }//resume was clicked
2609
 
2610
  if(isset($_POST["skipToCustom"]))
2611
  {
 
2612
  $this->_settings->skipToCustom = true;
 
 
2613
  }//resume was clicked
2614
 
2615
  //figure out the files that are left to be processed
@@ -2619,6 +2720,8 @@ class WPShortPixel {
2619
 
2620
  //check the custom bulk
2621
  $pendingMeta = $this->_settings->hasCustomFolders ? $this->spMetaDao->getPendingMetaCount() : 0;
 
 
2622
 
2623
  if ( ($filesLeft[0]->FilesLeftToBeProcessed > 0 && $this->prioQ->bulkRunning())
2624
  || (0 + $pendingMeta > 0 && !$this->_settings->customBulkPaused && $this->prioQ->bulkRan())//bulk processing was started
@@ -2635,6 +2738,7 @@ class WPShortPixel {
2635
  {
2636
  if($this->prioQ->bulkRan() && !$this->prioQ->bulkPaused()) {
2637
  $this->prioQ->markBulkComplete();
 
2638
  }
2639
 
2640
  //image count
@@ -3031,324 +3135,11 @@ class WPShortPixel {
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', '
3035
- RewriteEngine On
3036
- RewriteBase /
3037
- RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
3038
- RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
3039
- RewriteCond %{HTTP_ACCEPT} image/webp [OR]
3040
- RewriteCond %{DOCUMENT_ROOT}/$1\.webp -f
3041
- RewriteRule (.+)\.(?:jpe?g|png)$ $1.webp [NC,T=image/webp,E=webp,L]
3042
- Header append Vary Accept env=REDIRECT_webp
3043
- ' ); */
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 = "";
3060
- $notice = null;
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 );
3074
- //$htaccessWriteable = insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '' );
3075
- $htaccessWriteable = @fopen($htaccessPath, "a+") ? true : false;
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
3102
- if(filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
3103
-
3104
- }
3105
- else {
3106
- $notice = array("status" => "error",
3107
- "msg" => __("Please provide a valid e-mail.",'shortpixel-image-optimiser')
3108
- . "<BR> "
3109
- . __('For any question regarding obtaining your API Key, please contact us at ','shortpixel-image-optimiser')
3110
- . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
3111
- . __(' or ','shortpixel-image-optimiser')
3112
- . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.");
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",
3134
- "msg" => sprintf(__("The key you provided has %s characters. The API key should have 20 characters, letters and numbers only.",'shortpixel-image-optimiser'), $KeyLength)
3135
- . "<BR> <b>"
3136
- . __('Please check that the API key is the same as the one you received in your confirmation email.','shortpixel-image-optimiser')
3137
- . "</b><BR> "
3138
- . __('If this problem persists, please contact us at ','shortpixel-image-optimiser')
3139
- . "<a href='mailto:help@shortpixel.com?Subject=API Key issues' target='_top'>help@shortpixel.com</a>"
3140
- . __(' or ','shortpixel-image-optimiser')
3141
- . "<a href='https://shortpixel.com/contact' target='_blank'>" . __('here','shortpixel-image-optimiser') . "</a>.");
3142
- }
3143
- else {
3144
-
3145
- if(isset($_POST['save']) || isset($_POST['saveAdv'])) {
3146
- //these are needed for the call to api-status, set them first.
3147
- $this->_settings->siteAuthUser = (isset($_POST['siteAuthUser']) ? sanitize_text_field($_POST['siteAuthUser']) : $this->_settings->siteAuthUser);
3148
- $this->_settings->siteAuthPass = (isset($_POST['siteAuthPass']) ? sanitize_text_field($_POST['siteAuthPass']) : $this->_settings->siteAuthPass);
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
3157
- $lastStatus = $this->_settings->bulkLastStatus;
3158
- if(isset($lastStatus['Status']) && $lastStatus['Status'] == ShortPixelAPI::STATUS_NO_KEY) {
3159
- $this->_settings->bulkLastStatus = null;
3160
- }
3161
- //display notification
3162
- $urlParts = explode("/", get_site_url());
3163
- if( $validityData['DomainCheck'] == 'NOT Accessible'){
3164
- $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'));
3165
- } else {
3166
- if ( function_exists("is_multisite") && is_multisite() && !defined("SHORTPIXEL_API_KEY"))
3167
- $notice = array("status" => "success", "msg" => __("Great, your API Key is valid! <br>You seem to be running a multisite, please note that API Key can also be configured in wp-config.php like this:",'shortpixel-image-optimiser')
3168
- . "<BR> <b>define('SHORTPIXEL_API_KEY', '".$this->_settings->apiKey."');</b>");
3169
- else
3170
- $notice = array("status" => "success", "msg" => __('Great, your API Key is valid. Please take a few moments to review the plugin settings below before starting to optimize your images.','shortpixel-image-optimiser'));
3171
- }
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 ));
3179
- } else {
3180
- if(isset($_POST['validate'])) {
3181
- //display notification
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; }
3194
- $this->_settings->keepExif = isset($_POST['removeExif']) ? 0 : 1;
3195
- //delete_option('wp-short-pixel-keep-exif');
3196
- $this->_settings->resizeImages = (isset($_POST['resize']) ? 1: 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;
3206
- $ret = $this->addNextGenGalleriesToCustom($prevNextGen);
3207
- $folderMsg = $ret["message"];
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'] ) {
3227
- case 'deliverWebpUnaltered':
3228
- $this->_settings->deliverWebp = 3;
3229
- if(!$isNginx) self::alterHtaccess();
3230
- break;
3231
- case 'deliverWebpAltered':
3232
- self::alterHtaccess(true);
3233
- if( isset( $_POST['deliverWebpAlteringType'] ) ){
3234
- switch ($_POST['deliverWebpAlteringType']) {
3235
- case 'deliverWebpAlteredWP':
3236
- $this->_settings->deliverWebp = 2;
3237
- break;
3238
- case 'deliverWebpAlteredGlobal':
3239
- $this->_settings->deliverWebp = 1;
3240
- break;
3241
- }
3242
- }
3243
- break;
3244
- }
3245
- }
3246
- } else {
3247
- if(!$isNginx) self::alterHtaccess(true);
3248
- $this->_settings->deliverWebp = 0;
3249
- }
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) {
3265
- $parts = explode(':', $pat);
3266
- if(count($parts) == 1) {
3267
- $patterns[] = array("type" =>"name", "value" => str_replace('\\\\','\\',trim($pat)));
3268
- } else {
3269
- $patterns[] = array("type" =>trim($parts[0]), "value" => str_replace('\\\\','\\',trim($parts[1])));
3270
- }
3271
- }
3272
- $this->_settings->excludePatterns = $patterns;
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')
3283
- || isset($_POST['saveAdv']) && $_POST['saveAdv'] == __("Save and Go to Bulk Process",'shortpixel-image-optimiser')) {
3284
- wp_redirect("upload.php?page=wp-short-pixel-bulk");
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++) {
3314
- if(in_array($customFolders[$i]->getPath(), $ngg )) {
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);
3329
- if (is_numeric($quotaData['APICallsQuota'])) {
3330
- $quotaData['APICallsQuota'] .= "/month";
3331
- }
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
-
3343
- $this->view->displaySettings($showApiKey, $editApiKey,
3344
- $quotaData, $notice, $resources, $averageCompression, $savedSpace, $savedBandwidth, $remainingImages,
3345
- $totalCallsMade, $fileCount, null /*folder size now on AJAX*/, $customFolders,
3346
- $folderMsg, $folderMsg ? $addedFolder : false, isset($_POST['saveAdv']), $cloudflareAPI, $htaccessWriteable, $isNginx );
3347
- } else {
3348
- $this->view->displaySettings($showApiKey, $editApiKey, $quotaData, $notice);
3349
- }
3350
 
3351
- }
3352
 
3353
  /** Adds NextGenGalleries to Custom Images Library
3354
  * @param boolean $silent Will not return messages if silent
@@ -3421,6 +3212,7 @@ Header append Vary Accept env=REDIRECT_webp
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']);
@@ -3590,7 +3382,7 @@ Header append Vary Accept env=REDIRECT_webp
3590
  }
3591
 
3592
  //empty data means document, we handle only PDF
3593
- elseif (empty($data)) { //TODO asta devine if si decomentam returnurile
3594
  if($fileExtension == "pdf") {
3595
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3596
  $renderData['message'] = __('PDF not processed.','shortpixel-image-optimiser');
@@ -3609,19 +3401,8 @@ Header append Vary Accept env=REDIRECT_webp
3609
  if( is_numeric($data['ShortPixelImprovement'])
3610
  && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing'])) //for images that erroneously have ShortPixelImprovement = 0 when WaitingProcessing
3611
  ) { //already optimized
3612
- $sizesCount = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
3613
-
3614
- $thumbsToOptimize = 0;
3615
  $thumbsOptList = isset($data['ShortPixel']['thumbsOptList']) ? $data['ShortPixel']['thumbsOptList'] : array();
3616
- if($sizesCount && $this->_settings->processThumbnails) {
3617
- $exclude = $this->_settings->excludeSizes;
3618
- $exclude = is_array($exclude) ? $exclude : array();
3619
- foreach($data['sizes'] as $size => $sizeData) {
3620
- if(!in_array($size, $exclude) && !in_array($sizeData['file'], $thumbsOptList)) {
3621
- $thumbsToOptimize++;
3622
- }
3623
- }
3624
- }
3625
 
3626
  $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
3627
  $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
@@ -3631,7 +3412,8 @@ Header append Vary Accept env=REDIRECT_webp
3631
  $renderData['invType'] = ShortPixelAPI::getCompressionTypeName($this->getOtherCompressionTypes(ShortPixelAPI::getCompressionTypeCode($renderData['type'])));
3632
  $renderData['thumbsTotal'] = $sizesCount;
3633
  $renderData['thumbsOpt'] = isset($data['ShortPixel']['thumbsOpt']) ? $data['ShortPixel']['thumbsOpt'] : $sizesCount;
3634
- $renderData['thumbsToOptimize'] = $thumbsToOptimize;
 
3635
  $renderData['thumbsOptList'] = $thumbsOptList;
3636
  $renderData['excludeSizes'] = isset($data['ShortPixel']['excludeSizes']) ? $data['ShortPixel']['excludeSizes'] : null;
3637
  $renderData['thumbsMissing'] = isset($data['ShortPixel']['thumbsMissing']) ? $data['ShortPixel']['thumbsMissing'] : array();
@@ -3660,10 +3442,12 @@ Header append Vary Accept env=REDIRECT_webp
3660
  /* elseif($data['ShortPixelImprovement'] == __('Optimization N/A','shortpixel-image-optimiser')) { //We don't optimize this
3661
  $renderData['status'] = 'n/a';
3662
  }*/
3663
- elseif(isset($meta['ShortPixel']['BulkProcessing'])) { //Scheduled to bulk.
 
3664
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3665
  $renderData['message'] = 'Waiting for bulk processing.';
3666
  }
 
3667
  elseif( trim(strip_tags($data['ShortPixelImprovement'])) == __("Cannot write optimized file",'shortpixel-image-optimiser') ) {
3668
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'retry';
3669
  $renderData['message'] = __("Cannot write optimized file",'shortpixel-image-optimiser') . " - <a href='https://shortpixel.com/faq#cannot-write-optimized-file' target='_blank'>"
@@ -3701,6 +3485,37 @@ Header append Vary Accept env=REDIRECT_webp
3701
  }
3702
  }
3703
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3704
  /** Make columns sortable in Media Library
3705
  * @hook manage_upload_sortable_columns
3706
  * @param array $columns Array of colums sortable
@@ -4169,6 +3984,31 @@ Header append Vary Accept env=REDIRECT_webp
4169
  width: 87px;
4170
  height: 174px;
4171
  border-radius: 20px 0 0 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4172
  }
4173
  .shortpixel-hs-button-blind {
4174
  display:none;
@@ -4201,10 +4041,17 @@ Header append Vary Accept env=REDIRECT_webp
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>
1
  <?php
2
+ //use ShortPixel\DebugItem as DebugItem;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use ShortPixel\Notices\NoticeController as Notices;
 
 
5
 
6
  class WPShortPixel {
7
 
24
 
25
  public function __construct() {
26
  $this->timer = time();
27
+
28
 
29
+ if (Log::debugIsActive()) {
30
  $this->jsSuffix = '.js'; //use unminified versions for easier debugging
31
  }
32
 
39
  $this->cloudflareApi = new ShortPixelCloudFlareApi($this->_settings->cloudflareEmail, $this->_settings->cloudflareAuthKey, $this->_settings->cloudflareZoneID);
40
  $this->hasNextGen = ShortPixelNextGenAdapter::hasNextGen();
41
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $this->_settings->excludePatterns);
42
+ $this->prioQ = (! defined('SHORTPIXEL_NOFLOCK')) ? new ShortPixelQueue($this, $this->_settings) : new ShortPixelQueueDB($this, $this->_settings);
43
  $this->view = new ShortPixelView($this);
44
 
 
 
 
 
 
45
 
46
  define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
47
 
160
 
161
  // [BS] Quite dangerous to do this in any constructor. Can hit if request is ajax to name something
162
  // @todo This is intended to run only once, on activation. ( it does )
163
+ /*if(!$this->_settings->redirectedSettings && !$this->_settings->verifiedKey && (!function_exists("is_multisite") || !is_multisite())) {
164
  $this->_settings->redirectedSettings = 1;
165
  wp_redirect(admin_url("options-general.php?page=wp-shortpixel-settings"));
166
  exit();
167
  }
168
+ elseif (function_exists('is_multisite') && is_multisite() && !$this->_settings->verifiedKey)
169
+ { // @todo not optimal, License key needs it's own model to do checks upon.
170
+ $scontrolname = \shortPixelTools::namespaceit("SettingsController");
171
+ $scontrol = new $scontrolname();
172
+ $scontrol->setShortPixel($this);
173
+ $scontrol->checkKey();
174
+ } */
175
+
176
+ //
177
 
178
+ // only load backed, or when frontend processing is enabled.
179
+ if (is_admin() || $this->_settings->frontBootstrap )
180
+ {
181
+ $keyControl = new \ShortPixel\apiKeyController();
182
+ $keyControl->setShortPixel($this);
183
+ $keyControl->load();
184
+ }
185
  }
186
 
187
  //handling older
216
  $spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $settings->excludePatterns);
217
  $spMetaDao->dropTables();
218
  }
219
+
220
+ $settingsControl = new \ShortPixel\SettingsController();
221
+ $env = $settingsControl->getEnv();
222
+
223
+
224
+ if(WPShortPixelSettings::getOpt('deliverWebp') == 3 && ! $env->is_nginx) {
225
  self::alterHtaccess(); //add the htaccess lines
226
  }
227
  WPShortPixelSettings::onActivate();
230
  public static function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
231
  {
232
  ShortPixelQueue::resetBulk();
233
+ (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::resetPrio() : ShortPixelQueueDB::resetPrio();
234
  WPShortPixelSettings::onDeactivate();
235
+
236
+ $settingsControl = new \ShortPixel\SettingsController();
237
+ $env = $settingsControl->getEnv();
238
+
239
+ if (! $env->is_nginx)
240
+ self::alterHtaccess(true);
241
+
242
  @unlink(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
243
  }
244
 
334
  */
335
  );
336
  if($this->_settings->processThumbnails) {
337
+ $details = __('Details: recreating image files may require re-optimization of the resulting thumbnails, even if they were previously optimized. Please use <a href="https://wordpress.org/plugins/regenerate-thumbnails-advanced/" target="_blank">reGenerate Thumbnails Advanced</a> instead.','shortpixel-image-optimiser');
338
+
339
  $conflictPlugins = array_merge($conflictPlugins, array(
340
+ 'Regenerate Thumbnails'
341
  => array(
342
  'action'=>'Deactivate',
343
  'data'=>'regenerate-thumbnails/regenerate-thumbnails.php',
344
+ 'page'=>'regenerate-thumbnails',
345
+ 'details' => $details
346
  ),
347
+ 'Force Regenerate Thumbnails'
348
  => array(
349
  'action'=>'Deactivate',
350
  'data'=>'force-regenerate-thumbnails/force-regenerate-thumbnails.php',
351
+ 'page'=>'force-regenerate-thumbnails',
352
+ 'details' => $details
353
  )
354
  ));
355
  }
359
  $data = ( isset($path['data']) ) ? $path['data'] : null;
360
  $href = ( isset($path['href']) ) ? $path['href'] : null;
361
  $page = ( isset($path['page']) ) ? $path['page'] : null;
362
+ $details = ( isset($path['details']) ) ? $path['details'] : null;
363
  if(is_plugin_active($data)) {
364
  if( $data == 'jetpack/jetpack.php' ){
365
  $jetPackPhoton = get_option('jetpack_active_modules') ? in_array('photon', get_option('jetpack_active_modules')) : false;
366
  if( !$jetPackPhoton ){ continue; }
367
  }
368
+ $found[] = array( 'name' => $name, 'action'=> $action, 'path' => $data, 'href' => $href , 'page' => $page, 'details' => $details);
369
  }
370
  }
371
  return $found;
375
  * TODO - Probably should be a controller
376
  */
377
  public function displayAdminNotices() {
378
+ $testQ = (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::testQ() : ShortPixelQueueDB::testQ();
379
+ if(! $testQ) {
380
  ShortPixelView::displayActivationNotice('fileperms');
381
  }
382
  if($this->catchNotice()) { //notices for errors like for example a failed restore notice - these are one time so display them with priority.
499
 
500
  /** [TODO] This should report to the Shortpixel Logger **/
501
  static protected function doLog($message, $force = false) {
502
+ // Log::addInfo($message);
503
  /*if(defined('SHORTPIXEL_DEBUG_TARGET') || $force) {
504
  file_put_contents(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log", '[' . date('Y-m-d H:i:s') . "] $message\n", FILE_APPEND);
505
  } else {
550
  'STATUS_RETRY'=>ShortPixelAPI::STATUS_RETRY,
551
  'STATUS_QUEUE_FULL'=>ShortPixelAPI::STATUS_QUEUE_FULL,
552
  'STATUS_MAINTENANCE'=>ShortPixelAPI::STATUS_MAINTENANCE,
553
+ 'STATUS_SEARCHING' => ShortPixelAPI::STATUS_SEARCHING,
554
  'WP_PLUGIN_URL'=>plugins_url( '', SHORTPIXEL_PLUGIN_FILE ),
555
  'WP_ADMIN_URL'=>admin_url(),
556
  'API_KEY'=> (defined("SHORTPIXEL_HIDE_API_KEY" ) || !is_admin() ) ? '' : $this->_settings->apiKey,
558
  'MEDIA_ALERT'=>$this->_settings->mediaAlert ? "done" : "todo",
559
  'FRONT_BOOTSTRAP'=>$this->_settings->frontBootstrap && (!isset($this->_settings->lastBackAction) || (time() - $this->_settings->lastBackAction > 600)) ? 1 : 0,
560
  'AJAX_URL'=>admin_url('admin-ajax.php'),
561
+ 'AFFILIATE'=>false
562
  ));
563
 
564
  if (Log::isManualDebug() )
604
 
605
  wp_enqueue_script('jquery.knob.min.js', plugins_url('/res/js/jquery.knob.min.js',SHORTPIXEL_PLUGIN_FILE) );
606
  wp_enqueue_script('jquery.tooltip.min.js', plugins_url('/res/js/jquery.tooltip.min.js',SHORTPIXEL_PLUGIN_FILE) );
607
+
608
+
609
  wp_enqueue_script('punycode.min.js', plugins_url('/res/js/punycode.min.js',SHORTPIXEL_PLUGIN_FILE) );
610
  }
611
 
1127
  $passTime = time();
1128
  $maxResults = $timeoutThreshold > 15 ? SHORTPIXEL_MAX_RESULTS_QUERY / 3 :
1129
  ($timeoutThreshold > 10 ? SHORTPIXEL_MAX_RESULTS_QUERY / 2 : SHORTPIXEL_MAX_RESULTS_QUERY);
1130
+ Log::addInfo("GETDB: pass $sanityCheck current StartID: $crtStartQueryID Threshold: $timeoutThreshold, MaxResults: $maxResults" );
1131
 
1132
  /* $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta
1133
  WHERE ( post_id <= $crtStartQueryID AND post_id >= $endQueryID )
1136
  LIMIT " . SHORTPIXEL_MAX_RESULTS_QUERY;
1137
  $resultsPostMeta = $wpdb->get_results($queryPostMeta);
1138
  */
1139
+ // $resultsPostMeta = WpShortPixelMediaLbraryAdapter::getPostMetaSlice($crtStartQueryID, $endQueryID, $maxResults);
1140
+ // @todo Remove. Just Speed Test
1141
+ // Log::addDebug('PostMetaSlice took ' . (microtime(true) - $time) . ' sec.');
1142
 
1143
+ // $resultsPostMeta2 = WpShortPixelMediaLbraryAdapter::getPostMetaJoinLess($crtStartQueryID, $endQueryID, $maxResults);
1144
+ // Log::addDebug('PostMetaJoinLess took ' . (microtime(true) - $time) . ' sec.');
1145
 
1146
+ $resultsPosts = WpShortPixelMediaLbraryAdapter::getPostsJoinLessReverse($crtStartQueryID, $endQueryID, $maxResults);
1147
+ // Log::addDebug('PostMetaJoinLess *REV took ' . (microtime(true) - $time) . ' sec.');
1148
+ // */
1149
+ if(time() - $this->timer >= 60)
1150
+ Log::addWarn("GETDB is SLOW. Got meta slice.");
1151
+
1152
+ // @todo MAX RESULTS constant is not the same as queries maxResults? Is this correct?
1153
+ if ( empty($resultsPosts) ) {
1154
  $crtStartQueryID -= SHORTPIXEL_MAX_RESULTS_QUERY;
1155
  $startQueryID = $crtStartQueryID;
1156
  if(!count($idList)) { //none found so far, so decrease the start ID
1157
+ Log::addInfo("GETDB: empty slice. setStartBulkID to $startQueryID");
1158
  $this->prioQ->setStartBulkId($startQueryID);
1159
  }
1160
  continue;
1161
  }
1162
 
1163
+ if($timeoutThreshold > 10) Log::addInfo("GETDB is SLOW. Meta slice has " . count($resultsPosts) . ' items.');
1164
 
1165
+ $counter = 0;
1166
+ foreach ( $resultsPosts as $index => $post_id ) {
1167
+ $crtStartQueryID = $post_id; // $itemMetaData->post_id;
1168
+ if(time() - $this->timer >= 60) Log::addInfo("GETDB is SO SLOW. Check processable for $crtStartQueryID.");
1169
  if(time() - $this->timer >= $maxTime - $timeoutThreshold){
1170
+ if($counter == 0 && set_time_limit(30)) {
1171
+ self::log("GETDB is SO SLOW. Increasing time limit by 30 sec succeeded.");
1172
+ $maxTime += 30 - $timeoutThreshold;
1173
+ } else {
1174
+ self::log("GETDB is SO SLOW. Breaking after processing $counter items. Time limit is over: " . ($maxTime - $timeoutThreshold));
1175
+ break;
1176
+ }
1177
  }
1178
+ $counter++;
1179
 
1180
  if(!in_array($crtStartQueryID, $idList) && $this->isProcessable($crtStartQueryID, ($this->_settings->optimizePdfs ? array() : array('pdf')))) {
1181
  $item = new ShortPixelMetaFacade($crtStartQueryID);
1182
 
1183
+ if($timeoutThreshold > 15) Log::addInfo("GETDB is SO SLOW. Get meta for $crtStartQueryID.");
1184
  $meta = $item->getMeta();//wp_get_attachment_metadata($crtStartQueryID);
1185
+ if($timeoutThreshold > 15) Log::addInfo("GETDB is SO SLOW. Got meta.");
1186
 
1187
  if($meta->getStatus() != 2) {
1188
  $addIt = (strpos($meta->getMessage(), __('Image files are missing.', 'shortpixel-image-optimiser')) === false);
1210
  $skippedAlreadyProcessed++;
1211
  }
1212
  }
1213
+ elseif( $this->_settings->processThumbnails && $meta->getThumbsOpt() !== null //thumbs were chosen in settings
1214
+ && ($meta->getThumbsOpt() == 0 && count($meta->getThumbs()) > 0 //no thumbnails optimized
1215
  || is_array($meta->getThumbsOptList())
1216
  && count(array_diff(array_keys(WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta->getThumbs(), $this->_settings->excludeSizes)),
1217
+ $meta->getThumbsOptList()))
1218
+ || ( $this->_settings->optimizeUnlisted
1219
+ && count(array_diff(WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath()), $meta->getThumbsOptList()))
1220
+ )
1221
+ )
1222
+ ) {
1223
  $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
1224
  if(count($URLsAndPATHs["URLs"])) {
1225
  $meta->setThumbsTodo(true);
1229
  if(count($itemList) > SHORTPIXEL_PRESEND_ITEMS) break;
1230
  }
1231
  }
1232
+ /* New query selects this out, also no metadata returned.
1233
  elseif($itemMetaData->meta_key == '_wp_attachment_metadata') { //count skipped
1234
  $skippedAlreadyProcessed++;
1235
  }
1236
+ */
1237
  }
1238
  }
1239
  if(!count($idList) && $crtStartQueryID <= $startQueryID) {
1240
  //daca n-am adaugat niciuna pana acum, n-are sens sa mai selectez zona asta de id-uri in bulk-ul asta.
1241
  $leapStart = $this->prioQ->getStartBulkId();
1242
+ $crtStartQueryID = $startQueryID = $post_id - 1; //decrement it so we don't select it again
1243
  $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->_settings, $leapStart, $crtStartQueryID);
1244
  $skippedAlreadyProcessed += $res["mainProcessedFiles"] - $res["mainProc".($this->getCompressionType() == 1 ? "Lossy" : "Lossless")."Files"];
1245
+ Log::addInfo("GETDB: empty list. setStartBulkID to $startQueryID");
1246
  $this->prioQ->setStartBulkId($startQueryID);
1247
  } else {
1248
  $crtStartQueryID--;
1249
+ Log::addInfo("GETDB just decrementing. Crt: $crtStartQueryID Start: $startQueryID, list: " . json_encode($idList));
1250
  }
1251
  }
1252
  $ret = array("items" => $itemList, "skipped" => $skippedAlreadyProcessed, "searching" => ($sanityCheck >= 150) || (time() - $this->timer >= $maxTime - $timeoutThreshold));
1270
 
1271
  /** Checks the API key **/
1272
  private function checkKey($ID) {
1273
+ if( $this->_settings->verifiedKey == false) {
1274
  if($ID == null){
1275
  $ids = $this->getFromPrioAndCheck(1);
1276
  $itemHandler = (count($ids) > 0 ? $ids[0] : null);
1284
  private function sendEmptyQueue() {
1285
  $avg = $this->getAverageCompression();
1286
  $fileCount = $this->_settings->fileCount;
1287
+
1288
+ if($this->prioQ->bulkRunning())
1289
+ {
1290
+ $bulkstatus = '1';
1291
+ }
1292
+ elseif ($this->prioQ->bulkPaused())
1293
+ {
1294
+ $bulkstatus = '2';
1295
+ }
1296
+ else {
1297
+ $bulkstatus = '0';
1298
+ }
1299
+
1300
  $response = array("Status" => self::BULK_EMPTY_QUEUE,
1301
  /* translators: console message Empty queue 1234 -> 1234 */
1302
  "Message" => __('Empty queue ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
1303
+ "BulkStatus" => $bulkstatus,
 
1304
  "AverageCompression" => $avg,
1305
  "FileCount" => $fileCount,
1306
  "BulkPercent" => $this->prioQ->getBulkPercent());
1325
  }
1326
 
1327
  $rawPrioQ = $this->prioQ->get();
1328
+ if(count($rawPrioQ)) { Log::addInfo("HIP: 0 Priority Queue: ".json_encode($rawPrioQ)); }
1329
+ Log::addInfo("HIP: 0 Bulk running? " . $this->prioQ->bulkRunning() . " START " . $this->_settings->startBulkId . " STOP " . $this->_settings->stopBulkId . " MaxTime: " . SHORTPIXEL_MAX_EXECUTION_TIME);
1330
 
1331
  //handle the bulk restore and cleanup first - these are fast operations taking precedece over optimization
1332
  if( $this->prioQ->bulkRunning()
1387
  //die("za stop 2");
1388
 
1389
  //self::log("HIP: 1 Ids: ".json_encode($ids));
1390
+ if(count($ids)) {$idl='';foreach($ids as $i){$idl.=$i->getId().' ';}
1391
+ Log::addInfo("HIP: 1 Selected IDs: $idl");}
1392
 
1393
  //2: Send up to SHORTPIXEL_PRESEND_ITEMS files to the server for processing
1394
  for($i = 0, $itemHandler = false; $ids !== false && $i < min(SHORTPIXEL_PRESEND_ITEMS, count($ids)); $i++) {
1419
  if (!$itemHandler){
1420
  //if searching, than the script is searching for not processed items and found none yet, should be relaunced
1421
  if(isset($res['searching']) && $res['searching']) {
1422
+ die(json_encode(array("Status" => ShortPixelAPI::STATUS_SEARCHING,
1423
  "Message" => __('Searching images to optimize... ','shortpixel-image-optimiser') . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId() )));
1424
  }
1425
  //in this case the queue is really empty
1518
  $bkThumb = $backupUrl . $urlBkPath . $thumb;
1519
  }
1520
  if(strlen($thumb)) {
1521
+ /** @todo This Check is maybe within a getType for Media_Library_Type, so this should not run. **/
1522
  if($itemHandler->getType() == ShortPixelMetaFacade::CUSTOM_TYPE) {
1523
  $uploadsUrl = ShortPixelMetaFacade::getHomeUrl();
1524
  $urlPath = ShortPixelMetaFacade::returnSubDir($meta->getPath());
2186
  * @param int $ID image_id, without any prefixes
2187
  */
2188
  public function doCustomRestore($ID) {
2189
+
2190
  // meta facade as a custom image
2191
  $itemHandler = new ShortPixelMetaFacade('C-' . $ID);
2192
  $meta = $itemHandler->getMeta();
2206
  $fullSubDir = str_replace(get_home_path(), "", dirname($file)) . '/';
2207
  $bkFile = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir . ShortPixelAPI::MB_basename($file);
2208
 
2209
+ $fs = new \ShortPixel\FileSystemController();
 
 
 
 
 
2210
 
2211
+ $fileObj = $fs->getFile($file);
2212
+ $backupFile = $fileObj->getBackupFile(); // returns FileModel
2213
 
2214
+ if($backupFile === false)
2215
+ {
2216
+ Log::addWarn("Custom File $ID - $file does not have a backup");
2217
+ Notices::addWarning(sprintf(__('Not able to restore file %s. Could not find backup', 'shortpixel-image-optimiser'), $file));
2218
+ return false;
2219
+ }
2220
 
2221
+ if ($backupFile->copy($fileObj))
2222
+ {
2223
+ $backupFile->delete();
2224
+ }
2225
+ else {
2226
+ Log::addError('Could not restore back to source' . $backupFile->getFullPath() );
2227
+ Notices::addError('The file could not be restored from backup. Plugin could not copy backup back to original location. Check file permissions. ', 'shortpixel-image-optimiser');
2228
+ return false;
2229
+ }
 
 
2230
 
2231
+ /* [BS] Reset all generated image meta. Bring back to start state.
2232
+ * Since Wpdb->prepare doesn't support 'null', zero values in this table should not be trusted */
2233
 
2234
+ $meta->setTsOptimized(0);
2235
+ $meta->setCompressedSize(0);
2236
+ $meta->setCompressionType(0);
2237
+ $meta->setKeepExif(0);
2238
+ $meta->setCmyk2rgb(0);
2239
+ $meta->setMessage('');
2240
+ $meta->setRetries(0);
2241
+ $meta->setBackup(0);
2242
+ $meta->setResizeWidth(0);
2243
+ $meta->setResizeHeight(0);
2244
+ $meta->setResize(0);
2245
 
2246
+ $meta->setStatus(3);
2247
+ $this->spMetaDao->update($meta);
2248
+
2249
+
2250
+ //}
2251
 
2252
  return $meta;
2253
  }
2278
 
2279
  if(ShortPixelMetaFacade::isCustomQueuedId($qID)) {
2280
  $ID = ShortPixelMetaFacade::stripQueuedIdType($qID);
2281
+ /** BS . Moved this function from customRestore to Delete, plus Re-add 19/06/2019
2282
+ * Reason: doCustomRestore puts all options to 0 including once that needs preserving, which
2283
+ * will result in setting loss.
2284
+ * *But* the backup still needs to be restoring on 'redo' *so* do restore, but ignore that meta, then delete, and readd path.
2285
+ */
2286
+ $meta = $this->spMetaDao->getMeta($ID);
2287
+ $path = $meta->getPath();
2288
+ $folder_id = $meta->getFolderId();
2289
+ $this->doCustomRestore($ID);
2290
+
2291
+ $this->spMetaDao->delete($meta);
2292
+ $meta = $this->addPathToCustomFolder($path, $folder_id, NULL);
2293
+
2294
  if($meta) {
2295
  $meta->setCompressionType(ShortPixelAPI::getCompressionTypeCode($compressionType));
2296
  $meta->setStatus(1);
2335
  //$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);
2336
  $error = array('Status' => ShortPixelAPI::STATUS_SKIP, 'message' => __('Unspecified Error on Thumbnails for: ') . $ID);
2337
 
2338
+ list($includedSizes, $thumbsCount) = $this->getThumbsToOptimize($meta, get_attached_file($ID));
2339
+ //WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
2340
  $thumbsCount = count($includedSizes);
2341
 
2342
  if (! isset($meta['ShortPixelImprovement']))
2527
  }
2528
  if ( !$quotaData['APIKeyValid']) {
2529
  if(strlen($this->_settings->apiKey))
2530
+ Notices::addError(sprintf(__('Shortpixel Remote API Error: %s','shortpixel-image-optimiser'), $quotaData['Message'] ));
2531
  return $quotaData;
2532
  }
2533
  //$tempus = microtime(true);
2674
 
2675
  if(isset($_POST["bulkRestore"]))
2676
  {
2677
+ Log::addInfo('Bulk Process - Bulk Restore');
2678
+
2679
  $bulkRestore = new \ShortPixel\BulkRestoreAll(); // controller
2680
  $bulkRestore->setShortPixel($this);
2681
  $bulkRestore->setupBulk();
2686
 
2687
  if(isset($_POST["bulkCleanup"]))
2688
  {
2689
+ Log::addInfo('Bulk Process - Bulk Cleanup ');
2690
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_CLEANUP);
2691
  $this->_settings->customBulkPaused = 0;
2692
  }//end bulk restore was clicked
2693
 
2694
  if(isset($_POST["bulkCleanupPending"]))
2695
  {
2696
+ Log::addInfo('Bulk Process - Clean Pending');
2697
  $this->prioQ->startBulk(ShortPixelQueue::BULK_TYPE_CLEANUP_PENDING);
2698
  $this->_settings->customBulkPaused = 0;
2699
  }//end bulk restore was clicked
2700
 
2701
  if(isset($_POST["bulkProcessResume"]))
2702
  {
2703
+ Log::addInfo('Bulk Process - Bulk Resume');
2704
  $this->prioQ->resumeBulk();
2705
  $this->_settings->customBulkPaused = 0;
2706
  }//resume was clicked
2707
 
2708
  if(isset($_POST["skipToCustom"]))
2709
  {
2710
+ Log::addInfo('Bulk Process - Skipping to Custom Media Process');
2711
  $this->_settings->skipToCustom = true;
2712
+ $this->_settings->customBulkPaused = 0;
2713
+
2714
  }//resume was clicked
2715
 
2716
  //figure out the files that are left to be processed
2720
 
2721
  //check the custom bulk
2722
  $pendingMeta = $this->_settings->hasCustomFolders ? $this->spMetaDao->getPendingMetaCount() : 0;
2723
+ Log::addInfo('Bulk Process - Pending Meta Count ' . $pendingMeta);
2724
+ Log::addInfo('Bulk Process - File left ' . $filesLeft[0]->FilesLeftToBeProcessed );
2725
 
2726
  if ( ($filesLeft[0]->FilesLeftToBeProcessed > 0 && $this->prioQ->bulkRunning())
2727
  || (0 + $pendingMeta > 0 && !$this->_settings->customBulkPaused && $this->prioQ->bulkRan())//bulk processing was started
2738
  {
2739
  if($this->prioQ->bulkRan() && !$this->prioQ->bulkPaused()) {
2740
  $this->prioQ->markBulkComplete();
2741
+ Log::addInfo("Bulk Process - Marked Bulk Complete");
2742
  }
2743
 
2744
  //image count
3135
 
3136
  insert_with_markers( $upload_base . '.htaccess', 'ShortPixelWebp', $rules);
3137
  insert_with_markers( trailingslashit(WP_CONTENT_DIR) . '.htaccess', 'ShortPixelWebp', $rules);
 
 
 
 
 
 
 
 
 
 
 
 
3138
 
 
 
 
 
 
 
3139
  }
3140
+ }
3141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3142
 
 
3143
 
3144
  /** Adds NextGenGalleries to Custom Images Library
3145
  * @param boolean $silent Will not return messages if silent
3212
  $args['body']['host'] = parse_url(get_site_url(),PHP_URL_HOST);
3213
  $argsStr .= "&host={$args['body']['host']}";
3214
  if(strlen($this->_settings->siteAuthUser)) {
3215
+
3216
  $args['body']['user'] = stripslashes($this->_settings->siteAuthUser);
3217
  $args['body']['pass'] = stripslashes($this->_settings->siteAuthPass);
3218
  $argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
3382
  }
3383
 
3384
  //empty data means document, we handle only PDF
3385
+ elseif (empty($data)) {
3386
  if($fileExtension == "pdf") {
3387
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3388
  $renderData['message'] = __('PDF not processed.','shortpixel-image-optimiser');
3401
  if( is_numeric($data['ShortPixelImprovement'])
3402
  && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing'])) //for images that erroneously have ShortPixelImprovement = 0 when WaitingProcessing
3403
  ) { //already optimized
 
 
 
3404
  $thumbsOptList = isset($data['ShortPixel']['thumbsOptList']) ? $data['ShortPixel']['thumbsOptList'] : array();
3405
+ list($thumbsToOptimizeList, $sizesCount) = $this->getThumbsToOptimize($data, $file);
 
 
 
 
 
 
 
 
3406
 
3407
  $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
3408
  $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
3412
  $renderData['invType'] = ShortPixelAPI::getCompressionTypeName($this->getOtherCompressionTypes(ShortPixelAPI::getCompressionTypeCode($renderData['type'])));
3413
  $renderData['thumbsTotal'] = $sizesCount;
3414
  $renderData['thumbsOpt'] = isset($data['ShortPixel']['thumbsOpt']) ? $data['ShortPixel']['thumbsOpt'] : $sizesCount;
3415
+ $renderData['thumbsToOptimize'] = count($thumbsToOptimizeList);
3416
+ $renderData['thumbsToOptimizeList'] = $thumbsToOptimizeList;
3417
  $renderData['thumbsOptList'] = $thumbsOptList;
3418
  $renderData['excludeSizes'] = isset($data['ShortPixel']['excludeSizes']) ? $data['ShortPixel']['excludeSizes'] : null;
3419
  $renderData['thumbsMissing'] = isset($data['ShortPixel']['thumbsMissing']) ? $data['ShortPixel']['thumbsMissing'] : array();
3442
  /* elseif($data['ShortPixelImprovement'] == __('Optimization N/A','shortpixel-image-optimiser')) { //We don't optimize this
3443
  $renderData['status'] = 'n/a';
3444
  }*/
3445
+ /*
3446
+ elseif(isset($meta['ShortPixel']['BulkProcessing'])) { //Scheduled to bulk. !!! removed as the BulkProcessing is never set and it should be $data anyway.... :)
3447
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'optimizeNow';
3448
  $renderData['message'] = 'Waiting for bulk processing.';
3449
  }
3450
+ */
3451
  elseif( trim(strip_tags($data['ShortPixelImprovement'])) == __("Cannot write optimized file",'shortpixel-image-optimiser') ) {
3452
  $renderData['status'] = $quotaExceeded ? 'quotaExceeded' : 'retry';
3453
  $renderData['message'] = __("Cannot write optimized file",'shortpixel-image-optimiser') . " - <a href='https://shortpixel.com/faq#cannot-write-optimized-file' target='_blank'>"
3485
  }
3486
  }
3487
 
3488
+ /**
3489
+ * return the thumbnails that remain to optimize and the total count of sizes registered in metdata (and not excluded)
3490
+ * @param $data
3491
+ * @param $file
3492
+ * @return array
3493
+ */
3494
+ function getThumbsToOptimize($data, $file) {
3495
+ $sizesCount = isset($data['sizes']) ? WpShortPixelMediaLbraryAdapter::countSizesNotExcluded($data['sizes']) : 0;
3496
+ $basedir = trailingslashit(dirname($file));
3497
+ $thumbsOptList = isset($data['ShortPixel']['thumbsOptList']) ? $data['ShortPixel']['thumbsOptList'] : array();
3498
+ if($sizesCount && $this->_settings->processThumbnails) {
3499
+
3500
+ $thumbsToOptimizeList = array();
3501
+ $found = $this->_settings->optimizeUnlisted ? WpShortPixelMediaLbraryAdapter::findThumbs($file) : array();
3502
+
3503
+ $exclude = $this->_settings->excludeSizes;
3504
+ $exclude = is_array($exclude) ? $exclude : array();
3505
+ foreach($data['sizes'] as $size => $sizeData) {
3506
+ unset($found[\array_search($basedir . $sizeData['file'], $found)]);
3507
+ if(!in_array($size, $exclude) && !in_array($sizeData['file'], $thumbsOptList)) {
3508
+ $thumbsToOptimizeList[] = $sizeData['file'];
3509
+ }
3510
+ }
3511
+ $found = array_diff($found, $thumbsOptList);
3512
+ foreach($found as $item) {
3513
+ $thumbsToOptimizeList[] = wp_basename($item);
3514
+ }
3515
+ }
3516
+ return array($thumbsToOptimizeList, $sizesCount);
3517
+ }
3518
+
3519
  /** Make columns sortable in Media Library
3520
  * @hook manage_upload_sortable_columns
3521
  * @param array $columns Array of colums sortable
3984
  width: 87px;
3985
  height: 174px;
3986
  border-radius: 20px 0 0 20px;
3987
+ text-align: right;
3988
+ padding-right: 15px;
3989
+ }
3990
+ .shortpixel-hs-blind a {
3991
+ color: lightgray;
3992
+ text-decoration: none;
3993
+ }
3994
+ .shortpixel-hs-blind .dashicons-minus {
3995
+ border: 3px solid;
3996
+ border-radius: 12px;
3997
+ font-size: 12px;
3998
+ font-weight: bold;
3999
+ line-height: 15px;
4000
+ height: 13px;
4001
+ width: 13px;
4002
+ display:none;
4003
+ }
4004
+ .shortpixel-hs-blind .dashicons-dismiss {
4005
+ font-size: 23px;
4006
+ line-height: 19px;
4007
+ display: none;
4008
+ }
4009
+ .shortpixel-hs-blind:hover .dashicons-minus,
4010
+ .shortpixel-hs-blind:hover .dashicons-dismiss {
4011
+ display: inline-block;
4012
  }
4013
  .shortpixel-hs-button-blind {
4014
  display:none;
4041
  }
4042
  }
4043
  </style>
4044
+ <div id="shortpixel-hs-blind" class="shortpixel-hs-blind">
4045
+ <a href="javascript:ShortPixel.closeHelpPane();">
4046
+ <i class="dashicons dashicons-minus" title="<?php _e('Dismiss for now', 'shortpixel-image-optimiser'); ?> "></i>
4047
+ </a>
4048
+ <a href="javascript:ShortPixel.dismissHelpPane();">
4049
+ <i class="dashicons dashicons-dismiss" title="<?php _e('Never display again', 'shortpixel-image-optimiser'); ?>"></i>
4050
+ </a>
4051
+ </div>
4052
  <div id="shortpixel-hs-button-blind" class="shortpixel-hs-button-blind"></div>
4053
  <div id="shortpixel-hs-tools" class="shortpixel-hs-tools">
4054
+ <a href="javascript:shortpixelToggleHS();" class="shortpixel-hs-tools-docs" title="<?php _e('Search through our online documentation.', 'shortpixel-image-optimiser'); ?>">
4055
  <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/notes-sp.png'));?>" style="margin-bottom: 2px;width: 36px;">
4056
  </a>
4057
  </div>
class/wp-shortpixel-settings.php CHANGED
@@ -185,8 +185,10 @@ class WPShortPixelSettings extends ShortPixel\ShortPixelModel {
185
  update_option('wp-short-pixel-dismissed-notices', $dismissed, 'no');
186
  }
187
  $formerPrio = get_option('wp-short-pixel-priorityQueue');
188
- if(is_array($formerPrio) && !count(ShortPixelQueue::get())) {
189
- ShortPixelQueue::set($formerPrio);
 
 
190
  delete_option('wp-short-pixel-priorityQueue');
191
  }
192
  }
185
  update_option('wp-short-pixel-dismissed-notices', $dismissed, 'no');
186
  }
187
  $formerPrio = get_option('wp-short-pixel-priorityQueue');
188
+ $qGet = (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::get() : ShortPixelQueueDB::get();
189
+ if(is_array($formerPrio) && !count($qGet)) {
190
+
191
+ (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::set($formerPrio) : ShortPixelQueueDB::set($formerPrio);
192
  delete_option('wp-short-pixel-priorityQueue');
193
  }
194
  }
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === ShortPixel Image Optimizer ===
2
- Contributors: ShortPixel
3
  Tags: compressor, image, compression, optimize, image optimizer, image optimiser, image compression, resize, compress pdf, compress jpg, compress png, image compression
4
  Requires at least: 3.2.0
5
  Tested up to: 5.2
6
  Requires PHP: 5.3
7
- Stable tag: 4.14.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -61,17 +61,17 @@ Make an instant <a href="http://shortpixel.com/image-compression-test" target="_
61
  * 40 days optimization report with all image details and overall statistics
62
  * We are GDPR compliant! <a href="https://shortpixel.com/privacy#gdpr" target="_blank">Read more.</a>
63
  * **free optimization credits for non-profits**, <a href="https://shortpixel.com/contact" target="_blank">contact us</a> for details
64
-
65
  **How much does it cost?**
66
  ShortPixel comes with 100 free credits/month and additional credits can be bought for as little as $4.99 for 5,000 image credits.
67
  Check out <a href="https://shortpixel.com/pricing" target="_blank">our prices</a>.
68
 
69
- > **Testimonials:**
70
- > ★★★★★ **A Super Plugin works very well 62% reduction overall.** [robertvarns](https://wordpress.org/support/topic/a-super-plugin-works-very-well-62-reduction-overall/)
71
- > ★★★★★ **The secret sauce for a WordPress website.** [mark1mark](https://wordpress.org/support/topic/the-secret-sauce-for-a-wordpress-website/)
72
- > ★★★★★ **A must have plugin, great support!** [ElColo13](https://wordpress.org/support/topic/a-must-have-plugin-great-support/)
73
- > ★★★★★ **Excellent Plugin! Even Better Customer Service!** [scaliendo](https://wordpress.org/support/topic/great-plugin-great-support-508/)
74
- > ★★★★★ **Great image compression, solid plugin, equally great support.** [matters1959](https://wordpress.org/support/topic/support-shortpixel-image-optimizer/)
75
  > [more testimonials](https://wordpress.org/support/plugin/shortpixel-image-optimiser/reviews/?filter=5)
76
 
77
  [youtube https://www.youtube.com/watch?v=5EbX0Hsy6j4]
@@ -104,21 +104,21 @@ Let's get ShortPixel plugin running on your WordPress website:
104
  = How does ShortPixel compare to other image optimisation plugins (e.g Smush, Imagify, TinyPNG, Kraken, EWWW)? =
105
  ShortPixel has better compression rates, more features, supports backups and has very affordable one-time plans.
106
  If you are serious about making an informed decision please take 10 minutes and read this <a href="https://blog.shortpixel.com/wp-image-optimization-wordpress-plugins/">article</a>.
107
-
108
  = Can I use the same API Key on multiple web sites? =
109
- Yes, you can.
110
  As long as you have available credits you can use a single API Key on as many websites as you wish!
111
 
112
  = Can I test/use the plugin for free? =
113
- Yes you can.
114
  We offer 100 free image optimization credits each month. Exceeding the monthly free quota will pause the optimization process till the quota is reset or extended by buying one of our plans.
115
 
116
  = Can I optimize images that aren't in Media Library? =
117
- Absolutely.
118
  You can actually optimize any image you have on your site regardless of its place. You just need to add - in the Advanced section of the ShortPixel Settings - the folders where the images you want to optimize are located and ShortPixel will work its magic and do the rest.
119
 
120
  = Can I optimize both past and new images? =
121
- Sure!
122
  You can optimize all your past/current images using our "Bulk ShortPixel" page in your Media with a single click.
123
 
124
  = A credit = an optimized image? =
@@ -166,7 +166,7 @@ Let's get ShortPixel plugin running on your WordPress website:
166
  Please also note that usually images in your Media Library have 3-5 thumbs associated and a credit will be used for each featured image or associated thumbnail that is optimized.
167
 
168
  = Why shall I use a wordpress plugin and not an offline tool? =
169
- Because ShortPixel algorithms were perfected while optimizing over a hundred million real-life images.
170
  ShortPixel not only offers the best compression for JPEG, PNG, GIF and PDF files but it also saves you a lot of time. You just install it on your site and then ShortPixel will take care that all the images on your site are immediately optimized after upload.
171
 
172
  = Does optimizing images affect my ALT tags? =
@@ -177,18 +177,18 @@ Let's get ShortPixel plugin running on your WordPress website:
177
  Once optimized the images will remain optimized unless you explicitly choose to restore them. But why would you do that? :-)
178
 
179
  = Do I have to pay monthly or one time? =
180
- We have both options available.
181
  One-time credits never expire are a bit more expensive. Check out our prices <a href="https://shortpixel.com/pricing" >here</a>
182
 
183
  = When can I cancel a monthly plan? =
184
- Whenever you want.
185
  The credits you still have available for the current billing period will still be available until the end of the billing period. At the end of it, you won't be billed again and the plan will be reset to the free plan.
186
 
187
  = When credits expire? =
188
  Monthly credits expire after 30 days while one-time credits never expire.
189
 
190
  = Do you have an API? =
191
- Yes, we have several APIs and tools.
192
  You can learn more about it here:
193
  <a href="https://shortpixel.com/api-tools">https://shortpixel.com/api-tools</a>
194
 
@@ -220,6 +220,9 @@ The ShortPixel Image Optimiser plugin calls the following actions and filters:
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 ==
225
 
@@ -243,10 +246,23 @@ The ShortPixel Image Optimiser plugin calls the following actions and filters:
243
 
244
  == Changelog ==
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  == 4.14.2 ==
247
 
248
  Release date: 1st July 2019
249
- * Add the possibility to define custom thumb infixes like for example -uat to also select thumbs like image-uat-100x100.jpg
250
 
251
  == 4.14.1 ==
252
 
1
  === ShortPixel Image Optimizer ===
2
+ Contributors: ShortPixel
3
  Tags: compressor, image, compression, optimize, image optimizer, image optimiser, image compression, resize, compress pdf, compress jpg, compress png, image compression
4
  Requires at least: 3.2.0
5
  Tested up to: 5.2
6
  Requires PHP: 5.3
7
+ Stable tag: 4.14.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
61
  * 40 days optimization report with all image details and overall statistics
62
  * We are GDPR compliant! <a href="https://shortpixel.com/privacy#gdpr" target="_blank">Read more.</a>
63
  * **free optimization credits for non-profits**, <a href="https://shortpixel.com/contact" target="_blank">contact us</a> for details
64
+
65
  **How much does it cost?**
66
  ShortPixel comes with 100 free credits/month and additional credits can be bought for as little as $4.99 for 5,000 image credits.
67
  Check out <a href="https://shortpixel.com/pricing" target="_blank">our prices</a>.
68
 
69
+ > **Testimonials:**
70
+ > ★★★★★ **A Super Plugin works very well 62% reduction overall.** [robertvarns](https://wordpress.org/support/topic/a-super-plugin-works-very-well-62-reduction-overall/)
71
+ > ★★★★★ **The secret sauce for a WordPress website.** [mark1mark](https://wordpress.org/support/topic/the-secret-sauce-for-a-wordpress-website/)
72
+ > ★★★★★ **A must have plugin, great support!** [ElColo13](https://wordpress.org/support/topic/a-must-have-plugin-great-support/)
73
+ > ★★★★★ **Excellent Plugin! Even Better Customer Service!** [scaliendo](https://wordpress.org/support/topic/great-plugin-great-support-508/)
74
+ > ★★★★★ **Great image compression, solid plugin, equally great support.** [matters1959](https://wordpress.org/support/topic/support-shortpixel-image-optimiser/)
75
  > [more testimonials](https://wordpress.org/support/plugin/shortpixel-image-optimiser/reviews/?filter=5)
76
 
77
  [youtube https://www.youtube.com/watch?v=5EbX0Hsy6j4]
104
  = How does ShortPixel compare to other image optimisation plugins (e.g Smush, Imagify, TinyPNG, Kraken, EWWW)? =
105
  ShortPixel has better compression rates, more features, supports backups and has very affordable one-time plans.
106
  If you are serious about making an informed decision please take 10 minutes and read this <a href="https://blog.shortpixel.com/wp-image-optimization-wordpress-plugins/">article</a>.
107
+
108
  = Can I use the same API Key on multiple web sites? =
109
+ Yes, you can.
110
  As long as you have available credits you can use a single API Key on as many websites as you wish!
111
 
112
  = Can I test/use the plugin for free? =
113
+ Yes you can.
114
  We offer 100 free image optimization credits each month. Exceeding the monthly free quota will pause the optimization process till the quota is reset or extended by buying one of our plans.
115
 
116
  = Can I optimize images that aren't in Media Library? =
117
+ Absolutely.
118
  You can actually optimize any image you have on your site regardless of its place. You just need to add - in the Advanced section of the ShortPixel Settings - the folders where the images you want to optimize are located and ShortPixel will work its magic and do the rest.
119
 
120
  = Can I optimize both past and new images? =
121
+ Sure!
122
  You can optimize all your past/current images using our "Bulk ShortPixel" page in your Media with a single click.
123
 
124
  = A credit = an optimized image? =
166
  Please also note that usually images in your Media Library have 3-5 thumbs associated and a credit will be used for each featured image or associated thumbnail that is optimized.
167
 
168
  = Why shall I use a wordpress plugin and not an offline tool? =
169
+ Because ShortPixel algorithms were perfected while optimizing over a hundred million real-life images.
170
  ShortPixel not only offers the best compression for JPEG, PNG, GIF and PDF files but it also saves you a lot of time. You just install it on your site and then ShortPixel will take care that all the images on your site are immediately optimized after upload.
171
 
172
  = Does optimizing images affect my ALT tags? =
177
  Once optimized the images will remain optimized unless you explicitly choose to restore them. But why would you do that? :-)
178
 
179
  = Do I have to pay monthly or one time? =
180
+ We have both options available.
181
  One-time credits never expire are a bit more expensive. Check out our prices <a href="https://shortpixel.com/pricing" >here</a>
182
 
183
  = When can I cancel a monthly plan? =
184
+ Whenever you want.
185
  The credits you still have available for the current billing period will still be available until the end of the billing period. At the end of it, you won't be billed again and the plan will be reset to the free plan.
186
 
187
  = When credits expire? =
188
  Monthly credits expire after 30 days while one-time credits never expire.
189
 
190
  = Do you have an API? =
191
+ Yes, we have several APIs and tools.
192
  You can learn more about it here:
193
  <a href="https://shortpixel.com/api-tools">https://shortpixel.com/api-tools</a>
194
 
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
+ In order to define custom thumbnails to be picked up by the optimization you have two options, both comma separated defines:
224
+ define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', '_tl,_tr'); will handle custom thumbnails like image-100x100_tl.jpg
225
+ define('SHORTPIXEL_CUSTOM_THUMB_INFIXES', '-uae'); will handle custom thumbnails like image-uae-100x100.jpg
226
 
227
  == Screenshots ==
228
 
246
 
247
  == Changelog ==
248
 
249
+ == 4.14.3 ==
250
+
251
+ Release date: 22nd July 2019
252
+ * Compatibility with Flywheel hosting by not using flock if Flywheel detected
253
+ * When using the PICTURE tag to deliver WebP, keep the width and height at <IMG> level
254
+ * Refactoring of the API Key settings page, refactoring of Other media
255
+ * Performance improvements for selecting from large wp_posts and wp_postmeta tables when doing bulk optimization
256
+ * Display a message when bulk is skipping many processed images, if bulk processing is ran again.
257
+ * Fixed: Other media - Recompress adds "Preserve CMYK"
258
+ * Fixed: duplicate error message when validating wrong API key
259
+ * Conflict message: align the text vertically and provide space between text and button when displayed on any admin page
260
+ * Fixed: Other media Restore fails when original file is readable, not writable
261
+
262
  == 4.14.2 ==
263
 
264
  Release date: 1st July 2019
265
+ * add the possibility to define custom infixes for thumbnails (like image-uai-150x150.jpg for image.jpg) - comma separated define SHORTPIXEL_CUSTOM_THUMB_INFIXES
266
 
267
  == 4.14.1 ==
268
 
res/css/short-pixel-bar.css CHANGED
@@ -45,3 +45,25 @@ li.shortpixel-toolbar-processing.shortpixel-alert > a.ab-item > div,
45
  float:left;
46
  margin: 10px 10px 10px 0;
47
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  float:left;
46
  margin: 10px 10px 10px 0;
47
  }
48
+
49
+ .sp-conflict-plugins {
50
+ display: table;
51
+ border-spacing: 10px;
52
+ border-collapse: separate;
53
+ }
54
+ .sp-conflict-plugins li {
55
+ display: table-row;
56
+ }
57
+ .sp-conflict-plugins li > * {
58
+ display: table-cell;
59
+ }
60
+
61
+ li.sp-conflict-plugins-list {
62
+ line-height: 28px;
63
+ list-style: disc;
64
+ margin-left: 80px;
65
+ }
66
+ li.sp-conflict-plugins-list a.button {
67
+ margin-left: 10px;
68
+ }
69
+
res/css/short-pixel-bar.min.css CHANGED
@@ -1 +1 @@
1
- li.shortpixel-toolbar-processing>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div{height:33px;margin-top:-1px;padding:0 3px}li.shortpixel-toolbar-processing>a.ab-item>div>img,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>img{margin-right:2px;margin-top:6px}li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert{display:none}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert{display:inline;font-size:26px;color:red;font-weight:bold;vertical-align:top}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div{background-image:none}.sp-quota-exceeded-alert{background-color:#fff;border-left:4px solid red;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);padding:1px 12px}.shortpixel-clearfix{width:100%;float:left}.short-pixel-notice-icon{float:left;margin:10px 10px 10px 0}
1
+ li.shortpixel-toolbar-processing>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div{height:33px;margin-top:-1px;padding:0 3px}li.shortpixel-toolbar-processing>a.ab-item>div>img,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>img{margin-right:2px;margin-top:6px}li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing>a.ab-item>div>span.shp-alert{display:none}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div>span.shp-alert{display:inline;font-size:26px;color:red;font-weight:bold;vertical-align:top}li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div,#wpadminbar li.shortpixel-toolbar-processing.shortpixel-alert>a.ab-item>div{background-image:none}.sp-quota-exceeded-alert{background-color:#fff;border-left:4px solid red;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);padding:1px 12px}.shortpixel-clearfix{width:100%;float:left}.short-pixel-notice-icon{float:left;margin:10px 10px 10px 0}.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}
res/css/short-pixel.css CHANGED
@@ -105,26 +105,6 @@ div.fb-like {
105
  border-left-color: #f1e02a;
106
  }
107
 
108
- .sp-conflict-plugins {
109
- display: table;
110
- border-spacing: 10px;
111
- border-collapse: separate;
112
- }
113
- .sp-conflict-plugins li {
114
- display: table-row;
115
- }
116
- .sp-conflict-plugins li > * {
117
- display: table-cell;
118
- }
119
-
120
- li.sp-conflict-plugins-list {
121
- line-height: 28px;
122
- list-style: disc;
123
- margin-left: 80px;
124
- }
125
- li.sp-conflict-plugins-list a.button {
126
- margin-left: 10px;
127
- }
128
  div.short-pixel-bulk-page input.dial {
129
  font-size: 16px !important;
130
  }
105
  border-left-color: #f1e02a;
106
  }
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  div.short-pixel-bulk-page input.dial {
109
  font-size: 16px !important;
110
  }
res/css/short-pixel.min.css CHANGED
@@ -1 +1 @@
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)}}
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}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 !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}.deliverWebpSettings,.deliverWebpTypes,.deliverWebpAlteringTypes{display:none}.deliverWebpTypes .sp-notice{color:red}.deliverWebpSettings{margin:16px 0}.deliverWebpSettings input:disabled+label{color:#818181}.deliverWebpTypes,.deliverWebpAlteringTypes{margin:16px 0 16px 16px}#png2jpg:not(:checked) ~ #png2jpgForce,#png2jpg:not(:checked) ~ label[for=png2jpgForce]{display:none}article.sp-tabs section #createWebp:checked ~ .deliverWebpSettings,article.sp-tabs section #deliverWebp:checked ~ .deliverWebpTypes,article.sp-tabs section #deliverWebpAltered:checked ~ .deliverWebpAlteringTypes{display:block}.shortpixel-help-link span.dashicons{text-decoration:none;margin-top:-1px}@media(min-width:1000px){section#tab-resources .col-md-6{display:inline-block;width:45%}}@media(max-width:999px){section#tab-resources .col-sm-12{display:inline-block;width:100%}}section#tab-resources .text-center{text-align:center}section#tab-resources p{font-size:16px}.wrap.short-pixel-bulk-page{margin-right:0}.sp-container{overflow:hidden;display:block;width:100%}.sp-floating-block{overflow:hidden;display:inline-block;float:left;margin-right:1.1% !important}.sp-full-width{width:98.8%;box-sizing:border-box}.sp-double-width{width:65.52%;box-sizing:border-box}.sp-single-width{width:32.23%;box-sizing:border-box}@media(max-width:1759px){.sp-floating-block{margin-right:1.3% !important}.sp-double-width,.sp-full-width{width:98.65%}.sp-single-width{width:48.7%}}@media(max-width:1249px){.sp-floating-block{margin-right:2% !important}.sp-double-width,.sp-full-width,.sp-single-width{width:97%}}.sp-tabs h2:before{content:none}.sp-column-actions-template+.sp-column-info{display:none}#wpadminbar .shortpixel-toolbar-processing .cssload-container{width:100%;height:24px;text-align:center;position:absolute;top:0;left:-1px}#wpadminbar .shortpixel-toolbar-processing.shortpixel-quota-exceeded .cssload-container,#wpadminbar .shortpixel-toolbar-processing.shortpixel-alert .cssload-container{display:none}#wpadminbar .shortpixel-toolbar-processing .cssload-speeding-wheel{width:24px;height:24px;opacity:.7;margin:0 auto;border:4px solid #1cbfcb;border-radius:50%;border-left-color:transparent;animation:cssload-spin 2000ms infinite linear;-o-animation:cssload-spin 2000ms infinite linear;-ms-animation:cssload-spin 2000ms infinite linear;-webkit-animation:cssload-spin 2000ms infinite linear;-moz-animation:cssload-spin 2000ms infinite linear}@keyframes cssload-spin{100%{transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes cssload-spin{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes cssload-spin{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes cssload-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes cssload-spin{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}
res/css/shortpixel-admin.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 .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}
1
+ .shortpixel.notice{min-height:50px;padding:8px}.shortpixel.notice img{display:inline-block;margin:0 25px 0 0;max-height:50px}.shortpixel.notice .notice-dismiss{margin-top:10px}.view-notice{box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);border:4px solid #fff;padding:1px 12px}.view-notice p{margin:1em 0 !important}.view-notice.warning{border-left-color:#ffb900}.view-notice-row{display:none}.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}.short-pixel-bulk-page .sp-hidden{display:none}#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}.settings_page_wp-shortpixel-settings .top-menu{font-size:18px}.settings_page_wp-shortpixel-settings .top-menu a{font-size:18px}.settings_page_wp-shortpixel-settings .wp-shortpixel-tab-content{transition:all 1000ms linear}.settings_page_wp-shortpixel-settings article.sp-tabs section .wp-shortpixel-tab-content{opacity:0}.settings_page_wp-shortpixel-settings article.sp-tabs section.sel-tab .wp-shortpixel-tab-content{opacity:1}.settings_page_wp-shortpixel-settings 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}.settings_page_wp-shortpixel-settings 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}.settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(2) h2{left:192px}.settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(3) h2{left:374px}.settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(4) h2{left:556px}.settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(5) h2{left:738px}.settings_page_wp-shortpixel-settings article.sp-tabs section:nth-child(6) h2{left:920px}.settings_page_wp-shortpixel-settings section#tab-debug .flex{display:flex}.settings_page_wp-shortpixel-settings section#tab-debug .env .flex{flex-wrap:wrap;max-width:450px}.settings_page_wp-shortpixel-settings section#tab-debug .env .flex span{width:45%;padding:4px}
res/js/jquery.tooltip.js CHANGED
@@ -1,7 +1,7 @@
1
  (function($){
2
 
3
  $.fn.spTooltip = function(instanceSettings){
4
-
5
  $.fn.spTooltip.defaultsSettings = {
6
  attributeName:'title',
7
  borderColor:'#ccc',
@@ -27,12 +27,13 @@
27
  tooltipSourceURL:'',
28
  tooltipID:'tooltip'
29
  };
30
-
31
  //s = settings
32
  var s = $.extend({}, $.fn.spTooltip.defaultsSettings , instanceSettings || {});
33
-
34
  var positionTooltip = function(e){
35
-
 
36
  var posx = 0;
37
  var posy = 0;
38
  if (!e) var e = window.event;
@@ -44,21 +45,21 @@
44
  posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
45
  posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
46
  }
47
-
48
  var p = {
49
- x: posx + s.positionLeft,
50
  y: posy + s.positionTop,
51
- w: $('#'+s.tooltipID).width(),
52
  h: $('#'+s.tooltipID).height()
53
  }
54
-
55
  var v = {
56
  x: $(window).scrollLeft(),
57
  y: $(window).scrollTop(),
58
  w: $(window).width() - 20,
59
  h: $(window).height() - 20
60
  };
61
-
62
  //don't go off screen
63
  if(p.y + p.h > v.y + v.h && p.x + p.w > v.x + v.w){
64
  p.x = (p.x - p.w) - 45;
@@ -68,26 +69,27 @@
68
  }else if(p.y + p.h > v.y + v.h){
69
  p.y = p.y - (((p.y+p.h)-(v.y+v.h)) + 20);
70
  }
71
-
72
  $('#'+s.tooltipID).css({'left':p.x + 'px','top':p.y + 'px'});
73
  }
74
-
75
  var showTooltip = function(){
76
  $('#tooltipLoader').remove();
 
77
  $('#'+s.tooltipID+' #tooltipContent').show();
78
-
79
  if($.browser.version == '6.0'){//IE6 only
80
  $('#'+s.tooltipID).append('<iframe id="tooltipIE6FixIframe" style="width:'+($('#'+s.tooltipID).width()+parseFloat(s.borderSize)+parseFloat(s.borderSize)+20)+'px;height:'+($('#'+s.tooltipID).height()+parseFloat(s.borderSize)+parseFloat(s.borderSize)+20)+'px;position:absolute;top:-'+s.borderSize+'px;left:-'+s.borderSize+'px;filter:alpha(opacity=0);"src="blank.html"></iframe>');
81
  };
82
  }
83
-
84
  var hideTooltip = function(valueOfThis){
85
  $('#'+s.tooltipID).fadeOut('fast').trigger("unload").remove();
86
  if($(valueOfThis).filter('[title]')){
87
  $(valueOfThis).attr('title',s.titleAttributeContent);
88
  }
89
  }
90
-
91
  var urlQueryToObject = function(s){
92
  var query = {};
93
  s.replace(/b([^&=]*)=([^&=]*)b/g, function (m, a, d) {
@@ -99,13 +101,13 @@
99
  });
100
  return query;
101
  };
102
-
103
  return this.each(function(index){
104
-
105
  if(s.cancelClick){
106
  $(this).bind("click", function(){return false});
107
  }
108
-
109
  if($.fn.hoverIntent){
110
  $(this).hoverIntent({
111
  sensitivity:s.hoverIntent.sensitivity,
@@ -117,36 +119,38 @@
117
  }else{
118
  $(this).hover(on,off);
119
  }
120
-
121
- function on(e){
122
-
123
  $('body').append('<div id="'+s.tooltipID+'" style="background-repeat:no-repeat;background-image:url('+s.tooltipBGImage+');padding:'+s.tooltipPadding+'px;display:none;height:'+s.height+';width:'+s.width+';background-color:'+s.tooltipBGColor+';border:'+s.borderSize+'px solid '+s.borderColor+'; position:absolute;z-index:100000000000;"><div id="tooltipContent" style="display:none;"></div></div>');
124
-
125
  var $tt = $('#'+s.tooltipID);
126
  var $ttContent = $('#'+s.tooltipID+' #tooltipContent');
127
-
128
  if(s.loader && s.loaderImagePath != ''){
129
- $tt.append('<div id="tooltipLoader" style="width:'+s.loaderWidth+'px;height:'+s.loaderHeight+'px;"><img src="'+s.loaderImagePath+'" /></div>');
130
  }
131
-
132
  if($(this).attr('title')){
133
  s.titleAttributeContent = $(this).attr('title');
134
  $(this).attr('title','');
135
  }
136
-
137
  if($(this).is('input')){
138
  $(this).focus(function(){ hideTooltip(this); });
139
  }
140
-
141
  e.preventDefault();//stop
142
  positionTooltip(e);
143
-
144
  $tt.show();
145
-
146
  //get values from element clicked, or assume its passed as an option
147
  s.tooltipSourceID = $(this).attr('href') || s.tooltipSourceID;
148
  s.tooltipSourceURL = $(this).attr('href') || s.tooltipSourceURL;
149
-
 
 
 
150
  switch(s.tooltipSource){
151
  case 'attribute':/*/////////////////////////////// attribute //////////////////////////////////////////*/
152
  $ttContent.text(s.titleAttributeContent);
@@ -155,11 +159,12 @@
155
  case 'inline':/*/////////////////////////////// inline //////////////////////////////////////////*/
156
  $ttContent.html($(s.tooltipSourceID).children());
157
  $tt.unload(function(){// move elements back when you're finished
158
- $(s.tooltipSourceID).html($ttContent.children());
159
  });
160
  showTooltip();
161
  break;
162
- case 'ajax':/*/////////////////////////////// ajax //////////////////////////////////////////*/
 
163
  if(s.tooltipHTTPType == 'post'){
164
  var urlOnly, urlQueryObject;
165
  if(s.tooltipSourceURL.indexOf("?") !== -1){//has a query string
@@ -183,25 +188,25 @@
183
  }
184
  break;
185
  };
186
-
187
  return false;
188
-
189
  };
190
-
191
-
192
  function off(e){
193
  hideTooltip(this);
194
  return false;
195
  };
196
-
197
  if(s.followMouse){
198
  $(this).bind("mousemove", function(e){
199
  positionTooltip(e);
200
  return false;
201
  });
202
  }
203
-
204
  });
205
  };
206
-
207
  })(jQuery);
1
  (function($){
2
 
3
  $.fn.spTooltip = function(instanceSettings){
4
+
5
  $.fn.spTooltip.defaultsSettings = {
6
  attributeName:'title',
7
  borderColor:'#ccc',
27
  tooltipSourceURL:'',
28
  tooltipID:'tooltip'
29
  };
30
+
31
  //s = settings
32
  var s = $.extend({}, $.fn.spTooltip.defaultsSettings , instanceSettings || {});
33
+
34
  var positionTooltip = function(e){
35
+ e.preventDefault();
36
+
37
  var posx = 0;
38
  var posy = 0;
39
  if (!e) var e = window.event;
45
  posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
46
  posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
47
  }
48
+
49
  var p = {
50
+ x: posx + s.positionLeft,
51
  y: posy + s.positionTop,
52
+ w: $('#'+s.tooltipID).width(),
53
  h: $('#'+s.tooltipID).height()
54
  }
55
+
56
  var v = {
57
  x: $(window).scrollLeft(),
58
  y: $(window).scrollTop(),
59
  w: $(window).width() - 20,
60
  h: $(window).height() - 20
61
  };
62
+
63
  //don't go off screen
64
  if(p.y + p.h > v.y + v.h && p.x + p.w > v.x + v.w){
65
  p.x = (p.x - p.w) - 45;
69
  }else if(p.y + p.h > v.y + v.h){
70
  p.y = p.y - (((p.y+p.h)-(v.y+v.h)) + 20);
71
  }
72
+
73
  $('#'+s.tooltipID).css({'left':p.x + 'px','top':p.y + 'px'});
74
  }
75
+
76
  var showTooltip = function(){
77
  $('#tooltipLoader').remove();
78
+
79
  $('#'+s.tooltipID+' #tooltipContent').show();
80
+
81
  if($.browser.version == '6.0'){//IE6 only
82
  $('#'+s.tooltipID).append('<iframe id="tooltipIE6FixIframe" style="width:'+($('#'+s.tooltipID).width()+parseFloat(s.borderSize)+parseFloat(s.borderSize)+20)+'px;height:'+($('#'+s.tooltipID).height()+parseFloat(s.borderSize)+parseFloat(s.borderSize)+20)+'px;position:absolute;top:-'+s.borderSize+'px;left:-'+s.borderSize+'px;filter:alpha(opacity=0);"src="blank.html"></iframe>');
83
  };
84
  }
85
+
86
  var hideTooltip = function(valueOfThis){
87
  $('#'+s.tooltipID).fadeOut('fast').trigger("unload").remove();
88
  if($(valueOfThis).filter('[title]')){
89
  $(valueOfThis).attr('title',s.titleAttributeContent);
90
  }
91
  }
92
+
93
  var urlQueryToObject = function(s){
94
  var query = {};
95
  s.replace(/b([^&=]*)=([^&=]*)b/g, function (m, a, d) {
101
  });
102
  return query;
103
  };
104
+
105
  return this.each(function(index){
106
+
107
  if(s.cancelClick){
108
  $(this).bind("click", function(){return false});
109
  }
110
+
111
  if($.fn.hoverIntent){
112
  $(this).hoverIntent({
113
  sensitivity:s.hoverIntent.sensitivity,
119
  }else{
120
  $(this).hover(on,off);
121
  }
122
+ function on(e){
123
+
 
124
  $('body').append('<div id="'+s.tooltipID+'" style="background-repeat:no-repeat;background-image:url('+s.tooltipBGImage+');padding:'+s.tooltipPadding+'px;display:none;height:'+s.height+';width:'+s.width+';background-color:'+s.tooltipBGColor+';border:'+s.borderSize+'px solid '+s.borderColor+'; position:absolute;z-index:100000000000;"><div id="tooltipContent" style="display:none;"></div></div>');
125
+
126
  var $tt = $('#'+s.tooltipID);
127
  var $ttContent = $('#'+s.tooltipID+' #tooltipContent');
128
+
129
  if(s.loader && s.loaderImagePath != ''){
130
+ $tt.append('<div id="tooltipLoader" style="width:'+s.loaderWidth+'px;height:'+s.loaderHeight+'px;"><img src="'+s.loaderImagePath+'" /></div>');
131
  }
132
+
133
  if($(this).attr('title')){
134
  s.titleAttributeContent = $(this).attr('title');
135
  $(this).attr('title','');
136
  }
137
+
138
  if($(this).is('input')){
139
  $(this).focus(function(){ hideTooltip(this); });
140
  }
141
+
142
  e.preventDefault();//stop
143
  positionTooltip(e);
144
+
145
  $tt.show();
146
+
147
  //get values from element clicked, or assume its passed as an option
148
  s.tooltipSourceID = $(this).attr('href') || s.tooltipSourceID;
149
  s.tooltipSourceURL = $(this).attr('href') || s.tooltipSourceURL;
150
+
151
+ console.log(s.tooltipSourceID);
152
+ console.log(s.tooltipSourceURL);
153
+
154
  switch(s.tooltipSource){
155
  case 'attribute':/*/////////////////////////////// attribute //////////////////////////////////////////*/
156
  $ttContent.text(s.titleAttributeContent);
159
  case 'inline':/*/////////////////////////////// inline //////////////////////////////////////////*/
160
  $ttContent.html($(s.tooltipSourceID).children());
161
  $tt.unload(function(){// move elements back when you're finished
162
+ $(s.tooltipSourceID).html($ttContent.children());
163
  });
164
  showTooltip();
165
  break;
166
+ case 'ajax':/*/////////////////////////////// ajax //////////////////////////////////////////*/
167
+
168
  if(s.tooltipHTTPType == 'post'){
169
  var urlOnly, urlQueryObject;
170
  if(s.tooltipSourceURL.indexOf("?") !== -1){//has a query string
188
  }
189
  break;
190
  };
191
+
192
  return false;
193
+
194
  };
195
+
196
+
197
  function off(e){
198
  hideTooltip(this);
199
  return false;
200
  };
201
+
202
  if(s.followMouse){
203
  $(this).bind("mousemove", function(e){
204
  positionTooltip(e);
205
  return false;
206
  });
207
  }
208
+
209
  });
210
  };
211
+
212
  })(jQuery);
res/js/jquery.tooltip.min.js CHANGED
@@ -1 +1 @@
1
- (function(a){a.fn.spTooltip=function(d){a.fn.spTooltip.defaultsSettings={attributeName:"title",borderColor:"#ccc",borderSize:"1",cancelClick:0,followMouse:1,height:"auto",hoverIntent:{sensitivity:7,interval:100,timeout:0},loader:0,loaderHeight:0,loaderImagePath:"",loaderWidth:0,positionTop:12,positionLeft:12,width:"auto",titleAttributeContent:"",tooltipBGColor:"#fff",tooltipBGImage:"none",tooltipHTTPType:"get",tooltipPadding:10,tooltipSource:"attribute",tooltipSourceID:"",tooltipSourceURL:"",tooltipID:"tooltip"};var e=a.extend({},a.fn.spTooltip.defaultsSettings,d||{});var g=function(k){var h=0;var l=0;if(!k){var k=window.event}if(k.pageX||k.pageY){h=k.pageX;l=k.pageY}else{if(k.clientX||k.clientY){h=k.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;l=k.clientY+document.body.scrollTop+document.documentElement.scrollTop}}var j={x:h+e.positionLeft,y:l+e.positionTop,w:a("#"+e.tooltipID).width(),h:a("#"+e.tooltipID).height()};var i={x:a(window).scrollLeft(),y:a(window).scrollTop(),w:a(window).width()-20,h:a(window).height()-20};if(j.y+j.h>i.y+i.h&&j.x+j.w>i.x+i.w){j.x=(j.x-j.w)-45;j.y=(j.y-j.h)-45}else{if(j.x+j.w>i.x+i.w){j.x=j.x-(((j.x+j.w)-(i.x+i.w))+20)}else{if(j.y+j.h>i.y+i.h){j.y=j.y-(((j.y+j.h)-(i.y+i.h))+20)}}}a("#"+e.tooltipID).css({left:j.x+"px",top:j.y+"px"})};var f=function(){a("#tooltipLoader").remove();a("#"+e.tooltipID+" #tooltipContent").show();if(a.browser.version=="6.0"){a("#"+e.tooltipID).append('<iframe id="tooltipIE6FixIframe" style="width:'+(a("#"+e.tooltipID).width()+parseFloat(e.borderSize)+parseFloat(e.borderSize)+20)+"px;height:"+(a("#"+e.tooltipID).height()+parseFloat(e.borderSize)+parseFloat(e.borderSize)+20)+"px;position:absolute;top:-"+e.borderSize+"px;left:-"+e.borderSize+'px;filter:alpha(opacity=0);"src="blank.html"></iframe>')}};var b=function(h){a("#"+e.tooltipID).fadeOut("fast").trigger("unload").remove();if(a(h).filter("[title]")){a(h).attr("title",e.titleAttributeContent)}};var c=function(h){var i={};h.replace(/b([^&=]*)=([^&=]*)b/g,function(j,k,l){if(typeof i[k]!="undefined"){i[k]+=","+l}else{i[k]=l}});return i};return this.each(function(i){if(e.cancelClick){a(this).bind("click",function(){return false})}if(a.fn.hoverIntent){a(this).hoverIntent({sensitivity:e.hoverIntent.sensitivity,interval:e.hoverIntent.interval,over:h,timeout:e.hoverIntent.timeout,out:j})}else{a(this).hover(h,j)}function h(n){a("body").append('<div id="'+e.tooltipID+'" style="background-repeat:no-repeat;background-image:url('+e.tooltipBGImage+");padding:"+e.tooltipPadding+"px;display:none;height:"+e.height+";width:"+e.width+";background-color:"+e.tooltipBGColor+";border:"+e.borderSize+"px solid "+e.borderColor+'; position:absolute;z-index:100000000000;"><div id="tooltipContent" style="display:none;"></div></div>');var l=a("#"+e.tooltipID);var o=a("#"+e.tooltipID+" #tooltipContent");if(e.loader&&e.loaderImagePath!=""){l.append('<div id="tooltipLoader" style="width:'+e.loaderWidth+"px;height:"+e.loaderHeight+'px;"><img src="'+e.loaderImagePath+'" /></div>')}if(a(this).attr("title")){e.titleAttributeContent=a(this).attr("title");a(this).attr("title","")}if(a(this).is("input")){a(this).focus(function(){b(this)})}n.preventDefault();g(n);l.show();e.tooltipSourceID=a(this).attr("href")||e.tooltipSourceID;e.tooltipSourceURL=a(this).attr("href")||e.tooltipSourceURL;switch(e.tooltipSource){case"attribute":o.text(e.titleAttributeContent);f();break;case"inline":o.html(a(e.tooltipSourceID).children());l.unload(function(){a(e.tooltipSourceID).html(o.children())});f();break;case"ajax":if(e.tooltipHTTPType=="post"){var m,k;if(e.tooltipSourceURL.indexOf("?")!==-1){m=e.windowSourceURL.substr(0,e.windowSourceURL.indexOf("?"));k=c(e.tooltipSourceURL)}else{m=e.tooltipSourceURL;k={}}o.load(m,k,function(){f()})}else{if(e.tooltipSourceURL.indexOf("?")==-1){e.tooltipSourceURL+="?"}o.load(e.tooltipSourceURL+"&random="+(new Date().getTime()),function(){f()})}break}return false}function j(k){b(this);return false}if(e.followMouse){a(this).bind("mousemove",function(k){g(k);return false})}})}})(jQuery);
1
+ (function(a){a.fn.spTooltip=function(d){a.fn.spTooltip.defaultsSettings={attributeName:"title",borderColor:"#ccc",borderSize:"1",cancelClick:0,followMouse:1,height:"auto",hoverIntent:{sensitivity:7,interval:100,timeout:0},loader:0,loaderHeight:0,loaderImagePath:"",loaderWidth:0,positionTop:12,positionLeft:12,width:"auto",titleAttributeContent:"",tooltipBGColor:"#fff",tooltipBGImage:"none",tooltipHTTPType:"get",tooltipPadding:10,tooltipSource:"attribute",tooltipSourceID:"",tooltipSourceURL:"",tooltipID:"tooltip"};var e=a.extend({},a.fn.spTooltip.defaultsSettings,d||{});var g=function(k){k.preventDefault();var h=0;var l=0;if(!k){var k=window.event}if(k.pageX||k.pageY){h=k.pageX;l=k.pageY}else{if(k.clientX||k.clientY){h=k.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;l=k.clientY+document.body.scrollTop+document.documentElement.scrollTop}}var j={x:h+e.positionLeft,y:l+e.positionTop,w:a("#"+e.tooltipID).width(),h:a("#"+e.tooltipID).height()};var i={x:a(window).scrollLeft(),y:a(window).scrollTop(),w:a(window).width()-20,h:a(window).height()-20};if(j.y+j.h>i.y+i.h&&j.x+j.w>i.x+i.w){j.x=(j.x-j.w)-45;j.y=(j.y-j.h)-45}else{if(j.x+j.w>i.x+i.w){j.x=j.x-(((j.x+j.w)-(i.x+i.w))+20)}else{if(j.y+j.h>i.y+i.h){j.y=j.y-(((j.y+j.h)-(i.y+i.h))+20)}}}a("#"+e.tooltipID).css({left:j.x+"px",top:j.y+"px"})};var f=function(){a("#tooltipLoader").remove();a("#"+e.tooltipID+" #tooltipContent").show();if(a.browser.version=="6.0"){a("#"+e.tooltipID).append('<iframe id="tooltipIE6FixIframe" style="width:'+(a("#"+e.tooltipID).width()+parseFloat(e.borderSize)+parseFloat(e.borderSize)+20)+"px;height:"+(a("#"+e.tooltipID).height()+parseFloat(e.borderSize)+parseFloat(e.borderSize)+20)+"px;position:absolute;top:-"+e.borderSize+"px;left:-"+e.borderSize+'px;filter:alpha(opacity=0);"src="blank.html"></iframe>')}};var b=function(h){a("#"+e.tooltipID).fadeOut("fast").trigger("unload").remove();if(a(h).filter("[title]")){a(h).attr("title",e.titleAttributeContent)}};var c=function(h){var i={};h.replace(/b([^&=]*)=([^&=]*)b/g,function(j,k,l){if(typeof i[k]!="undefined"){i[k]+=","+l}else{i[k]=l}});return i};return this.each(function(i){if(e.cancelClick){a(this).bind("click",function(){return false})}if(a.fn.hoverIntent){a(this).hoverIntent({sensitivity:e.hoverIntent.sensitivity,interval:e.hoverIntent.interval,over:h,timeout:e.hoverIntent.timeout,out:j})}else{a(this).hover(h,j)}function h(n){a("body").append('<div id="'+e.tooltipID+'" style="background-repeat:no-repeat;background-image:url('+e.tooltipBGImage+");padding:"+e.tooltipPadding+"px;display:none;height:"+e.height+";width:"+e.width+";background-color:"+e.tooltipBGColor+";border:"+e.borderSize+"px solid "+e.borderColor+'; position:absolute;z-index:100000000000;"><div id="tooltipContent" style="display:none;"></div></div>');var l=a("#"+e.tooltipID);var o=a("#"+e.tooltipID+" #tooltipContent");if(e.loader&&e.loaderImagePath!=""){l.append('<div id="tooltipLoader" style="width:'+e.loaderWidth+"px;height:"+e.loaderHeight+'px;"><img src="'+e.loaderImagePath+'" /></div>')}if(a(this).attr("title")){e.titleAttributeContent=a(this).attr("title");a(this).attr("title","")}if(a(this).is("input")){a(this).focus(function(){b(this)})}n.preventDefault();g(n);l.show();e.tooltipSourceID=a(this).attr("href")||e.tooltipSourceID;e.tooltipSourceURL=a(this).attr("href")||e.tooltipSourceURL;console.log(e.tooltipSourceID);console.log(e.tooltipSourceURL);switch(e.tooltipSource){case"attribute":o.text(e.titleAttributeContent);f();break;case"inline":o.html(a(e.tooltipSourceID).children());l.unload(function(){a(e.tooltipSourceID).html(o.children())});f();break;case"ajax":if(e.tooltipHTTPType=="post"){var m,k;if(e.tooltipSourceURL.indexOf("?")!==-1){m=e.windowSourceURL.substr(0,e.windowSourceURL.indexOf("?"));k=c(e.tooltipSourceURL)}else{m=e.tooltipSourceURL;k={}}o.load(m,k,function(){f()})}else{if(e.tooltipSourceURL.indexOf("?")==-1){e.tooltipSourceURL+="?"}o.load(e.tooltipSourceURL+"&random="+(new Date().getTime()),function(){f()})}break}return false}function j(k){b(this);return false}if(e.followMouse){a(this).bind("mousemove",function(k){g(k);return false})}})}})(jQuery);
res/js/shortpixel.js CHANGED
@@ -63,12 +63,13 @@ 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) {
74
  if(e.which == 13) {
@@ -294,6 +295,19 @@ var ShortPixel = function() {
294
  });
295
  }
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  function checkQuota() {
298
  var data = { action : 'shortpixel_check_quota'};
299
  jQuery.get(ShortPixel.AJAX_URL, data, function() {
@@ -780,7 +794,7 @@ var ShortPixel = function() {
780
  setOptions : setOptions,
781
  isEmailValid : isEmailValid,
782
  updateSignupEmail : updateSignupEmail,
783
- validateKey : validateKey,
784
  enableResize : enableResize,
785
  setupGeneralTab : setupGeneralTab,
786
  apiKeyChanged : apiKeyChanged,
@@ -791,6 +805,8 @@ var ShortPixel = function() {
791
  adjustSettingsTabs : adjustSettingsTabsHeight,
792
  onBulkThumbsCheck : onBulkThumbsCheck,
793
  dismissMediaAlert : dismissMediaAlert,
 
 
794
  checkQuota : checkQuota,
795
  percentDial : percentDial,
796
  successMsg : successMsg,
@@ -830,7 +846,8 @@ var ShortPixel = function() {
830
  height : 0
831
  },
832
  toRefresh : false,
833
- resizeSizesAlert: false
 
834
  }
835
  }();
836
 
@@ -977,6 +994,15 @@ function checkBulkProcessingCallApi(){
977
 
978
  var isBulkPage = (jQuery("div.short-pixel-bulk-page").length > 0);
979
 
 
 
 
 
 
 
 
 
 
980
  switch (data["Status"]) {
981
  case ShortPixel.STATUS_NO_KEY:
982
  setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"https://shortpixel.com/wp-apikey"
@@ -1101,6 +1127,15 @@ function checkBulkProcessingCallApi(){
1101
  }
1102
  setTimeout(checkBulkProgress, 5000);
1103
  break;
 
 
 
 
 
 
 
 
 
1104
  case ShortPixel.STATUS_MAINTENANCE:
1105
  ShortPixel.bulkShowMaintenanceMsg('maintenance');
1106
  setTimeout(checkBulkProgress, 60000);
63
  jQuery('#request_key').attr('href', jQuery('#request_key').attr('href').split('?')[0] + '?pluginemail=' + email);
64
  }
65
 
66
+ /* Can be removed.
67
  function validateKey(button){
68
  console.log('validate');
69
  jQuery('#valid').val('validate');
70
 
71
  jQuery(button).parents('form').submit();
72
+ } */
73
 
74
  jQuery("#key").keypress(function(e) {
75
  if(e.which == 13) {
295
  });
296
  }
297
 
298
+ function closeHelpPane() {
299
+ jQuery('#shortpixel-hs-button-blind').remove();
300
+ jQuery('#shortpixel-hs-tools').remove();
301
+ jQuery('#hs-beacon').remove();
302
+ jQuery('#botbutton').remove();
303
+ jQuery('#shortpixel-hs-blind').remove();
304
+ }
305
+
306
+ function dismissHelpPane() {
307
+ closeHelpPane();
308
+ dismissShortPixelNotice('help');
309
+ }
310
+
311
  function checkQuota() {
312
  var data = { action : 'shortpixel_check_quota'};
313
  jQuery.get(ShortPixel.AJAX_URL, data, function() {
794
  setOptions : setOptions,
795
  isEmailValid : isEmailValid,
796
  updateSignupEmail : updateSignupEmail,
797
+ //validateKey : validateKey,
798
  enableResize : enableResize,
799
  setupGeneralTab : setupGeneralTab,
800
  apiKeyChanged : apiKeyChanged,
805
  adjustSettingsTabs : adjustSettingsTabsHeight,
806
  onBulkThumbsCheck : onBulkThumbsCheck,
807
  dismissMediaAlert : dismissMediaAlert,
808
+ closeHelpPane : closeHelpPane,
809
+ dismissHelpPane : dismissHelpPane,
810
  checkQuota : checkQuota,
811
  percentDial : percentDial,
812
  successMsg : successMsg,
846
  height : 0
847
  },
848
  toRefresh : false,
849
+ resizeSizesAlert: false,
850
+ returnedStatusSearching: 0, // How often this status has come back in a row from server.
851
  }
852
  }();
853
 
994
 
995
  var isBulkPage = (jQuery("div.short-pixel-bulk-page").length > 0);
996
 
997
+ if (data["Status"] && data["Status"] != ShortPixel.STATUS_SEARCHING)
998
+ {
999
+ if (ShortPixel.returnedStatusSearching >= 2)
1000
+ jQuery('.bulk-notice-msg.bulk-searching').hide();
1001
+
1002
+ ShortPixel.returnedStatusSearching = 0;
1003
+ }
1004
+
1005
+
1006
  switch (data["Status"]) {
1007
  case ShortPixel.STATUS_NO_KEY:
1008
  setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"https://shortpixel.com/wp-apikey"
1127
  }
1128
  setTimeout(checkBulkProgress, 5000);
1129
  break;
1130
+ case ShortPixel.STATUS_SEARCHING:
1131
+ console.log('Server response: ' + response);
1132
+ ShortPixel.returnedStatusSearching++;
1133
+ if (ShortPixel.returnedStatusSearching >= 2)
1134
+ {
1135
+ jQuery('.bulk-notice-msg.bulk-searching').show();
1136
+ }
1137
+ setTimeout(checkBulkProgress, 2500);
1138
+ break;
1139
  case ShortPixel.STATUS_MAINTENANCE:
1140
  ShortPixel.bulkShowMaintenanceMsg('maintenance');
1141
  setTimeout(checkBulkProgress, 60000);
res/js/shortpixel.min.js CHANGED
@@ -1 +1 @@
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});
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&&r.Status!=ShortPixel.STATUS_SEARCHING&&(ShortPixel.returnedStatusSearching>=2&&jQuery(".bulk-notice-msg.bulk-searching").hide(),ShortPixel.returnedStatusSearching=0),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_SEARCHING:console.log("Server response: "+e),ShortPixel.returnedStatusSearching++,ShortPixel.returnedStatusSearching>=2&&jQuery(".bulk-notice-msg.bulk-searching").show(),setTimeout(checkBulkProgress,2500);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")}function r(){jQuery("#shortpixel-hs-button-blind").remove(),jQuery("#shortpixel-hs-tools").remove(),jQuery("#hs-beacon").remove(),jQuery("#botbutton").remove(),jQuery("#shortpixel-hs-blind").remove()}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)},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()})},closeHelpPane:r,dismissHelpPane:function(){r(),dismissShortPixelNotice("help")},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,returnedStatusSearching:0}}();"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});
shortpixel-plugin.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  namespace ShortPixel;
3
- use ShortPixel\ShortPixelLogger as Log;
4
- use ShortPixel\NoticeController as Notice;
5
 
6
 
7
  /** Plugin class
@@ -11,7 +11,7 @@ use ShortPixel\NoticeController as Notice;
11
  class ShortPixelPlugin
12
  {
13
  static $instance;
14
- private $paths = array('class', 'class/controller', 'class/external');
15
 
16
  protected $is_noheaders = false;
17
 
@@ -41,8 +41,8 @@ class ShortPixelPlugin
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)
@@ -55,7 +55,6 @@ class ShortPixelPlugin
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);
@@ -63,6 +62,10 @@ class ShortPixelPlugin
63
  }
64
  }
65
  }
 
 
 
 
66
  }
67
 
68
  /** Hooks for all WordPress related hooks
@@ -76,12 +79,19 @@ class ShortPixelPlugin
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.
@@ -102,10 +112,17 @@ class ShortPixelPlugin
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();
@@ -135,13 +152,11 @@ class ShortPixelPlugin
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
  }
@@ -154,6 +169,9 @@ class ShortPixelPlugin
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);
1
  <?php
2
  namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+ use ShortPixel\Notices\NoticeController as Notices;
5
 
6
 
7
  /** Plugin class
11
  class ShortPixelPlugin
12
  {
13
  static $instance;
14
+ private $paths = array('class', 'class/controller', 'class/external'); // classes that are autoloaded
15
 
16
  protected $is_noheaders = false;
17
 
41
  return self::$instance;
42
  }
43
 
44
+ /** Init Runtime. Loads all classes. */
45
+ protected function initRuntime()
46
  {
47
  $plugin_path = plugin_dir_path(SHORTPIXEL_PLUGIN_FILE);
48
  foreach($this->paths as $short_path)
55
  foreach($it as $file)
56
  {
57
  $file_path = $file->getRealPath();
 
58
  if ($file->isFile() && pathinfo($file_path, PATHINFO_EXTENSION) == 'php')
59
  {
60
  require_once($file_path);
62
  }
63
  }
64
  }
65
+
66
+ // Loads all subclassed controllers. This is used for slug-based discovery of which controller to run
67
+ $controllerClass = \ShortPixelTools::namespaceit('ShortPixelController');
68
+ $controllerClass::init();
69
  }
70
 
71
  /** Hooks for all WordPress related hooks
79
 
80
  }
81
 
82
+ /** Hook in our admin pages */
83
  public function admin_pages()
84
  {
85
  // settings page
86
  add_options_page( __('ShortPixel Settings','shortpixel-image-optimiser'), 'ShortPixel', 'manage_options', 'wp-shortpixel-settings', array($this, 'route'));
87
  }
88
 
89
+ /** PluginRunTime. Items that should be initialized *only* when doing our pages and territory. */
90
+ protected function initPluginRunTime()
91
+ {
92
+
93
+ }
94
+
95
  /** All scripts should be registed, not enqueued here (unless global wp-admin is needed )
96
  *
97
  * Not all those registered must be enqueued however.
112
 
113
  public function admin_notices()
114
  {
115
+ $noticeControl = Notices::getInstance();
116
+ $noticeControl->loadIcons(array(
117
+ 'normal' => '<img class="short-pixel-notice-icon" src="' . plugins_url('res/img/robo-cool.png', SHORTPIXEL_PLUGIN_FILE) . '">',
118
+ 'success' => '<img class="short-pixel-notice-icon" src="' . plugins_url('res/img/robo-cool.png', SHORTPIXEL_PLUGIN_FILE) . '">',
119
+ 'warning' => '<img class="short-pixel-notice-icon" src="' . plugins_url('res/img/robo-scared.png', SHORTPIXEL_PLUGIN_FILE) . '">',
120
+ 'error' => '<img class="short-pixel-notice-icon" src="' . plugins_url('res/img/robo-scared.png', SHORTPIXEL_PLUGIN_FILE) . '">',
121
+ ));
122
 
123
  if ($noticeControl->countNotices() > 0)
124
  {
125
+ wp_enqueue_style('shortpixel-admin'); // queue on places when it's not our runtime.
126
  foreach($noticeControl->getNotices() as $notice)
127
  {
128
  echo $notice->getForDisplay();
152
  if ($this->is_noheaders) // fail silently, if this is a no-headers request.
153
  return;
154
 
 
155
  if (wp_script_is($name, 'registered'))
156
  {
157
  wp_enqueue_script($name);
158
  }
159
  else {
 
160
  Log::addWarn("Script $name was asked for, but not registered");
161
  }
162
  }
169
  {
170
  global $plugin_page;
171
  global $shortPixelPluginInstance; //brrr @todo Find better solution for this some day.
172
+
173
+ $this->initPluginRunTime();
174
+
175
  $default_action = 'load'; // generic action on controller.
176
  $action = isset($_REQUEST['sp-action']) ? sanitize_text_field($_REQUEST['sp-action']) : $default_action;
177
  Log::addDebug('Request', $_REQUEST);
shortpixel_api.php CHANGED
@@ -3,7 +3,7 @@ if ( !function_exists( 'download_url' ) ) {
3
  require_once( ABSPATH . 'wp-admin/includes/file.php' );
4
  }
5
 
6
- use \ShortPixel\ShortPixelLogger as Log;
7
 
8
  class ShortPixelAPI {
9
 
@@ -16,6 +16,7 @@ class ShortPixelAPI {
16
  const STATUS_NOT_FOUND = -5;
17
  const STATUS_NO_KEY = -6;
18
  const STATUS_RETRY = -7;
 
19
  const STATUS_QUEUE_FULL = -404;
20
  const STATUS_MAINTENANCE = -500;
21
 
@@ -111,7 +112,7 @@ class ShortPixelAPI {
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,
@@ -839,7 +840,10 @@ class ShortPixelAPI {
839
  $Separator = " qq ";
840
  $qqPath = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $Path);
841
  if(!$qqPath) { //this is not an UTF8 string!! Don't rely on basename either, since if filename starts with a non-ASCII character it strips it off
842
- $fileName = end(explode(DIRECTORY_SEPARATOR, $Path));
 
 
 
843
  $pos = strpos($fileName, $suffix);
844
  if($pos !== false) {
845
  return substr($fileName, 0, $pos);
3
  require_once( ABSPATH . 'wp-admin/includes/file.php' );
4
  }
5
 
6
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
7
 
8
  class ShortPixelAPI {
9
 
16
  const STATUS_NOT_FOUND = -5;
17
  const STATUS_NO_KEY = -6;
18
  const STATUS_RETRY = -7;
19
+ const STATUS_SEARCHING = -8; // when the Queue is looping over images, but in batch none were found.
20
  const STATUS_QUEUE_FULL = -404;
21
  const STATUS_MAINTENANCE = -500;
22
 
112
  }
113
 
114
  // WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
115
+ $URLs = apply_filters('shortpixel_image_urls', $URLs, $itemHandler->getId()) ;
116
 
117
  $requestParameters = array(
118
  'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
840
  $Separator = " qq ";
841
  $qqPath = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $Path);
842
  if(!$qqPath) { //this is not an UTF8 string!! Don't rely on basename either, since if filename starts with a non-ASCII character it strips it off
843
+
844
+ // This line is separated because of 'passed by reference' errors otherwise.
845
+ $pathAr = explode(DIRECTORY_SEPARATOR, $Path);
846
+ $fileName = end($pathAr);
847
  $pos = strpos($fileName, $suffix);
848
  if($pos !== false) {
849
  return substr($fileName, 0, $pos);
wp-shortpixel-req.php CHANGED
@@ -8,14 +8,14 @@ if(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) {
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
  {
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\ShortPixelLogger as Log;
19
 
20
  if (! defined('SHORTPIXEL_DEBUG'))
21
  {
wp-shortpixel.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: ShortPixel Image Optimizer
4
  * Plugin URI: https://shortpixel.com/
5
  * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="options-general.php?page=wp-shortpixel-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.2
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
@@ -16,10 +16,11 @@ if (! defined('SHORTPIXEL_RESET_ON_ACTIVATE'))
16
  //define('SHORTPIXEL_DEBUG_TARGET', true);
17
 
18
  define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
 
19
 
20
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
21
 
22
- define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.14.2");
23
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
24
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
25
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
@@ -33,9 +34,15 @@ if(!defined('SHORTPIXEL_MAX_THUMBS')) { //can be defined in wp-config.php
33
  define('SHORTPIXEL_PRESEND_ITEMS', 3);
34
  define('SHORTPIXEL_API', 'api.shortpixel.com');
35
 
36
- define('SHORTPIXEL_MAX_EXECUTION_TIME', ini_get('max_execution_time'));
 
 
 
 
 
 
 
37
 
38
- require_once(ABSPATH . 'wp-admin/includes/file.php');
39
 
40
  $sp__uploads = wp_upload_dir();
41
  define('SHORTPIXEL_UPLOADS_BASE', (file_exists($sp__uploads['basedir']) ? '' : ABSPATH) . $sp__uploads['basedir'] );
@@ -59,6 +66,10 @@ else
59
  define('SHORTPIXEL_MAX_EXECUTION_TIME2', 2 );
60
  define("SHORTPIXEL_MAX_RESULTS_QUERY", 30);
61
 
 
 
 
 
62
  function shortpixelInit() {
63
  global $shortPixelPluginInstance;
64
  //limit to certain admin pages if function available
@@ -88,9 +99,11 @@ function shortpixelInit() {
88
 
89
  }
90
 
 
91
  function shortPixelCheckQueue(){
92
  require_once('class/shortpixel_queue.php');
93
- $prio = ShortPixelQueue::get();
 
94
  return $prio && is_array($prio) && count($prio);
95
  }
96
 
@@ -208,6 +221,12 @@ function shortPixelIsPluginActive($plugin) {
208
  }
209
 
210
  // [BS] Start runtime here
 
 
 
 
 
 
211
  $option = get_option('wp-short-pixel-create-webp-markup');
212
  if ( $option ) {
213
  if(shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {
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.3
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
16
  //define('SHORTPIXEL_DEBUG_TARGET', true);
17
 
18
  define('SHORTPIXEL_PLUGIN_FILE', __FILE__);
19
+ define('SHORTPIXEL_PLUGIN_DIR', __DIR__);
20
 
21
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
22
 
23
+ define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.14.3");
24
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
25
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
26
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
34
  define('SHORTPIXEL_PRESEND_ITEMS', 3);
35
  define('SHORTPIXEL_API', 'api.shortpixel.com');
36
 
37
+ $max_exec = intval(ini_get('max_execution_time'));
38
+ if ($max_exec === 0) // max execution time of zero means infinite. Quantify.
39
+ $max_exec = 60;
40
+ define('SHORTPIXEL_MAX_EXECUTION_TIME', $max_exec);
41
+
42
+ // ** @todo For what is this needed? */
43
+ //require_once(ABSPATH . 'wp-admin/includes/file.php');
44
+ require_once(SHORTPIXEL_PLUGIN_DIR . '/build/shortpixel/autoload.php');
45
 
 
46
 
47
  $sp__uploads = wp_upload_dir();
48
  define('SHORTPIXEL_UPLOADS_BASE', (file_exists($sp__uploads['basedir']) ? '' : ABSPATH) . $sp__uploads['basedir'] );
66
  define('SHORTPIXEL_MAX_EXECUTION_TIME2', 2 );
67
  define("SHORTPIXEL_MAX_RESULTS_QUERY", 30);
68
 
69
+ /** @todo This is a test in progress var */
70
+
71
+ //define("SHORTPIXEL_NOFLOCK", true);
72
+
73
  function shortpixelInit() {
74
  global $shortPixelPluginInstance;
75
  //limit to certain admin pages if function available
99
 
100
  }
101
 
102
+
103
  function shortPixelCheckQueue(){
104
  require_once('class/shortpixel_queue.php');
105
+ require_once('class/external/shortpixel_queue_db.php');
106
+ $prio = (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::get() : ShortPixelQueueDB::get();
107
  return $prio && is_array($prio) && count($prio);
108
  }
109
 
221
  }
222
 
223
  // [BS] Start runtime here
224
+ $log = ShortPixel\ShortPixelLogger\ShortPixelLogger::getInstance();
225
+ $log->setLogPath(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
226
+
227
+ // Pre-Runtime Checks
228
+ require_once('class/external/flywheel.php'); // check if SP runs on flywheel
229
+
230
  $option = get_option('wp-short-pixel-create-webp-markup');
231
  if ( $option ) {
232
  if(shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {