ShortPixel Image Optimizer - Version 4.15.0

Version Description

Release date: 27th November 2019 * Ask if WebP files should be created before bulk optimization : checkbox added in Bulk page. * Added filters for optimal chunk size when selecting records from wp_postmeta while bulk processing: 'shortpixel/db/chunk_size'. * Optimize images also on edit-media screen. * Experimental support for static cache firewalls, and sending images w/ timestamp: SHORTPIXEL_EXPERIMENTAL_SECURICACHE. * Limit loading of JS / CSS to pages we do work on. * Refactoring & speed optimizations: - new external class for gravity forms, nextgen and visual composer, new controller for global admin hooks, new front controller, updated plugin init, clean(ish) root file, database optimization(reduced number of queries), unlistedThumbs checker only fires when the optimize unlisted check is active, optimizations for filesystem and findFileByPattern. * WPML Duplicates - Don't mark empty GUID's as duplicate. * Fix broken styles on WP 5.3. * Fix in case meta_value in processCountable has a WP_Error object instead of normal metadata. * Fix on WP Engine when large wp_postmeta table: limit to 16K the size of the query with ID list. * Fixed: error when log path is not writeable * Fixed: double sends within the same images, can happen with something like WPML and it's duplicated media. * Fixed: bug when searching unlisted images, in some circumstances. * Fixed: max_execution_time when time is set lower than 0. * Fixed: directories with no permission would give PHP error * Fixed: pathToUrl now also works for paths outside wp uploads * Fixed: check for DOING_AJAX on redirect to settings. * Fixed: Shortpixel icon + exclamation mark in toolbar showing on every page load. * Fixed: Add Custom media browser doesn't display files anymore * Fixed: WebP option adds an extra border if image already has a border -> borders will not be replicated to tags. * Fixed: Validating empty key doesn't show any message. * Fixed: on Nginx writes .htaccess files. * Fixed: Bug with safeGetAttachmentUrl for URLs that start with //. * Fixed: New S3-Offload version breaks Shortpixel and offloading. * Fixed: get_attached_file when S3-Offload is active, breaks other plugins. * Fixed: crash when doing .htaccess files ( WP 5.3 specific ). * Fixed: double file occurences on png2jpg in conjunction with s3offload. * Language 1 new strings added, 0 updated, 0 fuzzied, and 0 obsoleted

Download this release

Release Info

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

Code changes from version 4.14.6 to 4.15.0

Files changed (51) hide show
  1. build/shortpixel/log/composer.json +1 -1
  2. build/shortpixel/log/src/ShortPixelLogger.php +9 -7
  3. build/shortpixel/notices/composer.json +1 -1
  4. build/shortpixel/notices/src/NoticeController.php +19 -9
  5. build/shortpixel/notices/src/NoticeModel.php +1 -10
  6. changelog.txt +184 -0
  7. class/controller/admin_controller.php +55 -0
  8. class/controller/cache_controller.php +65 -0
  9. class/controller/controller.php +1 -13
  10. class/controller/edit_media_controller.php +22 -1
  11. class/controller/filesystem_controller.php +105 -2
  12. class/controller/front_controller.php +104 -0
  13. class/controller/settings.php +13 -14
  14. class/db/shortpixel-custom-meta-dao.php +106 -39
  15. class/db/shortpixel-meta-facade.php +234 -23
  16. class/db/wp-shortpixel-db.php +26 -1
  17. class/db/wp-shortpixel-media-library-adapter.php +60 -30
  18. class/external/gravityforms.php +40 -0
  19. class/external/helpscout.php +1 -1
  20. class/external/nextgen.php +28 -13
  21. class/external/securi.php +33 -0
  22. class/external/visualcomposer.php +23 -0
  23. class/external/wp-offload-media.php +145 -40
  24. class/external/wpengine.php +14 -0
  25. class/front/img-to-picture-webp.php +35 -3
  26. class/model/apikey_model.php +4 -2
  27. class/model/cache_model.php +76 -0
  28. class/model/directory_model.php +125 -4
  29. class/model/environment_model.php +93 -8
  30. class/model/file_model.php +87 -47
  31. class/model/image_model.php +59 -5
  32. class/model/shortpixel-folder.php +20 -7
  33. class/shortpixel-png2jpg.php +40 -12
  34. class/shortpixel-tools.php +24 -0
  35. class/shortpixel_queue.php +2 -1
  36. class/view/settings/part-advanced.php +1 -1
  37. class/view/settings/part-debug.php +2 -0
  38. class/view/settings/part-general.php +4 -4
  39. class/view/settings/part-nokey.php +2 -2
  40. class/view/shortpixel-list-table.php +1 -1
  41. class/view/shortpixel_view.php +44 -28
  42. class/wp-short-pixel.php +347 -136
  43. class/wp-shortpixel-settings.php +12 -4
  44. readme.txt +59 -193
  45. res/css/short-pixel.css +2 -1
  46. res/css/short-pixel.min.css +1 -1
  47. res/js/shortpixel.js +1 -1
  48. shortpixel-plugin.php +193 -9
  49. shortpixel_api.php +22 -7
  50. wp-shortpixel-req.php +7 -10
  51. wp-shortpixel.php +24 -191
build/shortpixel/log/composer.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "name": "shortpixel/log",
3
  "description": "ShortPixel Logging",
4
- "version": "1.1.2",
5
  "type": "library",
6
  "license": "MIT",
7
  "authors": [
1
  {
2
  "name": "shortpixel/log",
3
  "description": "ShortPixel Logging",
4
+ "version": "1.1.3",
5
  "type": "library",
6
  "license": "MIT",
7
  "authors": [
build/shortpixel/log/src/ShortPixelLogger.php CHANGED
@@ -88,12 +88,14 @@ namespace ShortPixel\ShortPixelLogger;
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();
@@ -171,7 +173,7 @@ namespace ShortPixel\ShortPixelLogger;
171
  $line = $this->formatLine($items);
172
 
173
  // try to write to file. Don't write if directory doesn't exists (leads to notices)
174
- if ($this->logPath && is_dir(dirname($this->logPath) ))
175
  {
176
  file_put_contents($this->logPath,$line, FILE_APPEND);
177
  }
88
 
89
  }
90
 
91
+ if ($this->is_active)
92
+ {
93
+ /* On Early init, this function might not exist, then queue it when needed */
94
+ if (! function_exists('wp_get_current_user'))
95
+ add_action('init', array($this, 'initView'));
96
+ else
97
+ $this->initView();
98
+ }
99
 
100
  if ($this->is_active && count($this->hooks) > 0)
101
  $this->monitorHooks();
173
  $line = $this->formatLine($items);
174
 
175
  // try to write to file. Don't write if directory doesn't exists (leads to notices)
176
+ if ($this->logPath && is_dir(dirname($this->logPath)) )
177
  {
178
  file_put_contents($this->logPath,$line, FILE_APPEND);
179
  }
build/shortpixel/notices/composer.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "name": "shortpixel/notices",
3
  "description": "ShortPixel WordPress Notice System",
4
- "version": "1.1",
5
  "type": "library",
6
  "license": "MIT",
7
  "authors": [
1
  {
2
  "name": "shortpixel/notices",
3
  "description": "ShortPixel WordPress Notice System",
4
+ "version": "1.2",
5
  "type": "library",
6
  "license": "MIT",
7
  "authors": [
build/shortpixel/notices/src/NoticeController.php CHANGED
@@ -64,9 +64,18 @@ class NoticeController //extends ShortPixelController
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);
@@ -124,38 +133,39 @@ class NoticeController //extends ShortPixelController
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
  }
64
  }
65
 
66
 
67
+ public function addNotice($message, $code, $unique)
68
  {
69
  $notice = new NoticeModel($message, $code);
70
+
71
+ if ($unique)
72
+ {
73
+ foreach(self::$notices as $nitem)
74
+ {
75
+ if ($nitem->message == $notice->message && $nitem->code == $notice->code) // same message.
76
+ return false;
77
+ }
78
+ }
79
  self::$notices[] = $notice;
80
  $this->countNotices();
81
  Log::addDebug('Adding notice - ', $notice);
133
 
134
  /** Adds a notice, quick and fast method
135
  * @param String $message The Message you want to notify
136
+ * @param Boolean $unique If unique, check to not repeat notice exact same text in notices. Discard if so
137
  * @param int $code A value of messageType as defined in model
138
  * @returm Object Instance of noticeModel
139
  */
140
 
141
+ public static function addNormal($message, $unique = false)
142
  {
143
  $noticeController = self::getInstance();
144
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_NORMAL, $unique);
145
  return $notice;
146
 
147
  }
148
 
149
+ public static function addError($message, $unique = false)
150
  {
151
  $noticeController = self::getInstance();
152
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_ERROR, $unique);
153
  return $notice;
154
 
155
  }
156
 
157
+ public static function addWarning($message, $unique = false)
158
  {
159
  $noticeController = self::getInstance();
160
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_WARNING, $unique);
161
  return $notice;
162
 
163
  }
164
 
165
+ public static function addSuccess($message, $unique = false)
166
  {
167
  $noticeController = self::getInstance();
168
+ $notice = $noticeController->addNotice($message, NoticeModel::NOTICE_SUCCESS, $unique);
169
  return $notice;
170
 
171
  }
build/shortpixel/notices/src/NoticeModel.php CHANGED
@@ -3,7 +3,7 @@ namespace ShortPixel\Notices;
3
 
4
  class NoticeModel //extends ShortPixelModel
5
  {
6
- protected $message;
7
  public $code;
8
 
9
  protected $viewed = false;
@@ -88,9 +88,6 @@ class NoticeModel //extends ShortPixelModel
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
  {
@@ -107,10 +104,4 @@ class NoticeModel //extends ShortPixelModel
107
  }
108
 
109
 
110
-
111
- // @todo Transient save, since that is used in some parts.
112
- // save
113
- // load
114
-
115
-
116
  }
3
 
4
  class NoticeModel //extends ShortPixelModel
5
  {
6
+ public $message;
7
  public $code;
8
 
9
  protected $viewed = false;
88
  break;
89
  }
90
 
 
 
 
91
 
92
  if ($this->is_removable)
93
  {
104
  }
105
 
106
 
 
 
 
 
 
 
107
  }
changelog.txt CHANGED
@@ -1,3 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = 4.8.10 =
2
  * restore compatibility with PHP 5.2.x
3
  * finding unlisted thumbnails - don't bother if dialog dismissed.
1
+ = 4.12.8 =
2
+
3
+ Release date: 25th February 2019
4
+
5
+ * fix CSS for the top bar ShortPixel icon on post pages
6
+ * i18n some text which was left out by mistake
7
+ * include the green "CAN" WebP image which shows that the .htaccess works with WebP images having extensions like .jpg.webp
8
+ * display notice if ShortPixel Adaptive Images is active and the Generate WebP markup option is checked. Do not generate the WebP markup in this case.
9
+
10
+ = 4.12.7 =
11
+
12
+ Release date: 12th February 2019
13
+
14
+ * solved conflicting WebP file names when image.jpg and image.png exist in the same folder - use image.jpg.webp filename.
15
+ * fixed .htaccess rules for some Apache versions which seemingly don't honour the RewriteRule backreferences in the RewriteCond's (Apache bug?)
16
+ * remove the WebP .htaccess rules on plugin deactivation and add them back on plugin activation
17
+ * fixed alt attribute for <picture> tags - now it is included properly only on the enclosed <img> tag.
18
+
19
+ = 4.12.6 =
20
+
21
+ Release date: 27th January 2019
22
+
23
+ * Improvements to the .htaccess WebP method
24
+ * Improve performance of backup deletion - get rid of unnecessary checks
25
+ * Fixed: wrong calculation of remaining credits
26
+ * Fixed: discrepancy between the description of the exclude size option and the behaviour for the exact size case.
27
+
28
+ = 4.12.5 =
29
+
30
+ Release date: 10th Ianuary 2019
31
+
32
+ * change the JS name in order to circumveit cache problem on many WP installs
33
+ * sorting the Media Library entries by ShortPixel optimization: also sort based on compression level
34
+ * Fixed: case sensitive search for guid duplicates of image posts (needed for finding Polylang versions)
35
+ * Fixed: the data-lazy-src/srcset detection for WebP
36
+ * Improvements to the Deliver WebP options and especially messages with caveats
37
+ * Load the ShortPixel CSS only on admin pages that need it
38
+
39
+ = 4.12.4 =
40
+
41
+ Release date: 27th December 2018
42
+
43
+ * Fixed: shortpixel-thumbnails-regenerated action when not all the thumbnails were regenerated
44
+
45
+ = 4.12.3 =
46
+
47
+ Release date: 19th December 2018
48
+
49
+ * Fixed: error in getting the lazy- attributes of <img> for WebP handling.
50
+
51
+ = 4.12.2 =
52
+
53
+ Release date: 13th December 2018
54
+
55
+ * Improved: The Webp options interface. Now the user can implement Webp images both via .htaccess and by altering the page code on the server before being sent to the browser.
56
+ * Improved: The settings data handling interface in the Plugin deactivation dialogue. Now the option to delete or keep the user settings on plugin deletion is more clear.
57
+ * Added: Option to download image with thumbnails in a single archive file, to speed-up the optimization.
58
+ * Added: A "shortpixel_get_backup" filter, which receives the local path of the media image and returns the ShortPixel backup path, if a backup image exists
59
+ * Added: The "Simple Image Sizes" plugin to the conflicting plugins list
60
+ * Added: A new compatibility check for the "Jetpack" plugin, alerting the user about potential overlapping functionality
61
+ * Added: A safety alert before switching to Code Altering mode (where IMG tgs get inserted into PICTURE tags, to better serve Webp images)
62
+ * Added: Enhanced "Envira" plugin compatibility by adding more suffixes to be looked for: _tl, _tr, _bl, _br
63
+ * Added: More customized FAQ suggestions in the HelpScout Beacon helper, to address each Plugin TAB separately
64
+ * Fixed: The post-uninstall redirect when uninstalling a plugin from within the respective plugin's Settings page
65
+ * Fixed: The credits display on the Statistics page
66
+ * Fixed: Refreshing a plugin page now loads directly in the previously selected TAB
67
+ * Fixed: Removed a stray "SP_CELL_MESSAGE" div from the interface
68
+
69
+ = 4.12.1 =
70
+
71
+ Release date: 6th November 2018
72
+
73
+ * Fix WebP replacement for lazy-loaded images
74
+ * Fix WebP replacement with output buffering on some WP installs
75
+
76
+ = 4.12.0 =
77
+
78
+ Release date: 31st October 2018
79
+
80
+ * Generate WebP &lt;picture&gt; tags - use the output buffer instead of the_content which is not triggered by some themes on all content.
81
+ * compatibility of the WebP &lt;picture&gt; tag with lazy loading plugins (that support &lt;picture&gt;)
82
+ * Compatibility with Polylang.
83
+ * hooks to be used by thumbnail regeneration plugins: 'shortpixel-thumbnails-before-regenerate' and 'shortpixel-thumbnails-regenerated'
84
+ * Proper error message when the custom tables cannot be created.
85
+ * exclude the PNGs from conversion to JPEG when they match the exclude patterns.
86
+ * properly warn when cURL is not enabled that Cloudflare integration won't work.
87
+ * send only one url for metadata thumbnails which correspond to the same physical file.
88
+ * JavaScript delayed init for cases when some plugins deffer the load of javascript files.
89
+ * fix identifying filenames with basename length == 3 as retina
90
+ * display improvements for the bulk errors list
91
+
92
+ = 4.11.3 =
93
+
94
+ Release date: 27th September 2018
95
+
96
+ * fix error when metadata is returned as string by wp_get_attachment_metadata (happens to PDFs when using PDF Image Generator)
97
+ * remove the configurable Affiliate code as per new WP Themes rules.
98
+
99
+ = 4.11.2 =
100
+
101
+ Release date: 30th August 2018
102
+
103
+ * Fix "Image files are missing" warning when thumbails optimization is activated but all the thumbnails are excepted from optimization and the bulk is ran a second time.
104
+ * Fix not saving properly the metadata on some situations
105
+
106
+ = 4.11.1 =
107
+
108
+ Release date: 28th August 2018
109
+
110
+ * compatibility with the MediaPress plugin
111
+ * new action to be called by when thumbnails are regenerated: shortpixel-thumbnails-regenerated
112
+ * accept '+' inside the e-mail address
113
+ * fix optimization not working on internationalized domain names
114
+ * better count of the not optimized thumbs for an image, in some circumstances
115
+ * fallback to ABSPATH when get_home_path() returns '/'
116
+ * fix settings tabs navigation when url ends with #/
117
+ * extract all release notes < 4.9 from readme.txt into changelog.txt
118
+ * display the thumbnail name for some errors which refer only to a specific thumbnail.
119
+ * use update_post_meta() instead of wp_update_attachment_metadata() for cases when other plugins cannot be concerned by the meta change (specific to ShortPixel)
120
+ * add the attributes of the original <img> to the <picture> replacement tag, in case the "Generate WebP Markup" option is active.
121
+ * fix action buttons in media edit view overflowing their box
122
+ * restore full compatibility with WP < 4.1 by checking first before using wp_json_encode
123
+ * fix admin when domain is internationalized but the setting in admin uses the punycode-encrypted version
124
+
125
+ = 4.11.0 =
126
+
127
+ Release date: 3rd July 2018
128
+
129
+ * add bulk menu options: restore, reoptimize
130
+ * filter the media list by optimization status
131
+ * sort the media list by optimization status
132
+ * do not display the Media Library (years) folders in the selection list for Other Media folders
133
+ * force PNG 2 JPG conversion option
134
+ * integrate with Gravity Forms
135
+ * integrate with WP Stateless
136
+ * add several actions and a filter (see the Actions and Filters section of the readme)
137
+ * UI improvements to the settings page
138
+ * fix the WPML compatibility when converting from PNG
139
+ * fix SELECT IN image counting bug on rare cases and when >100k records in wp_postmeta
140
+ * add option to delete ShortPixel settings and give feedback form on deactivate plugin
141
+
142
+ = 4.10.5 =
143
+ * GDPR compliance
144
+
145
+ = 4.10.4 =
146
+ * replace back the PNG links when restoring a PNG converted to JPG
147
+ * fix incompatibility with Dynamics 365 Integration plugin
148
+ * improve restore capabilities after certain types of PNG to JPG errors which left the media item in an unconsistent state.
149
+ * remove AUTH credentials on server too, if removed in plugin's settings.
150
+ * more performance improvements to PNG 2 JPG conversion
151
+ * fix replacing PNG urls having http:// instead of https:// for a SSL site. (and viceversa)
152
+ * fix string not appearing in translations
153
+
154
+ = 4.10.3 =
155
+ * improvements to context help beacon
156
+ * performance improvements to PNG to JPG conversion
157
+
158
+ = 4.10.2 =
159
+ * fix error when listing Other media in some circumstances
160
+
161
+ = 4.10.1 =
162
+ * fix missing file from commit
163
+
164
+ = 4.10.0 =
165
+ * option to exclude thumbnails from optimization
166
+ * options to delete Cloudflare cache for optimized images
167
+ * method to define affilate codes for themes
168
+ * error message when restore could not be performed
169
+ * better handling of situations with files with different owner but with write permissions for all
170
+ * fix bug for inner resize when setting and unsetting the resize parameter
171
+ * fix bug for third-party WebP thumbnails registered in the 'sizes' metadata array which were sent to optimization.
172
+ * check if function mb_convert_encoding exists before using it
173
+
174
+ = 4.9.1 =
175
+ * fix error for older WP versions which don't have wp_raise_memory_limit
176
+
177
+ = 4.9.0 =
178
+ * inline help beacon
179
+ * fix exclude patterns not working after last update
180
+ * handle situations when not enough memory to convert from PNG to JPG.
181
+ * fix particular situations where there was no 'file' property in the metadata.
182
+ * fix slider optimized percent over the bulk warning box.
183
+ * display the x close link for the bulk warning box.
184
+
185
  = 4.8.10 =
186
  * restore compatibility with PHP 5.2.x
187
  * finding unlisted thumbnails - don't bother if dialog dismissed.
class/controller/admin_controller.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use ShortPixel\Notices\NoticeController as Notices;
5
+
6
+ /* AdminController is meant for handling events, hooks, filters in WordPress where there is *NO* specific or more precise Shortpixel Page active.
7
+ *
8
+ * This should be a delegation class connection global hooks and such to the best shortpixel handler.
9
+ */
10
+ class adminController extends ShortPixelController
11
+ {
12
+ protected static $instance;
13
+
14
+ public function __construct()
15
+ {
16
+
17
+ }
18
+
19
+ public static function getInstance()
20
+ {
21
+ if (is_null(self::$instance))
22
+ self::$instance = new adminController();
23
+
24
+ return self::$instance;
25
+ }
26
+
27
+ /** Handling upload actions
28
+ * @hook wp_generate_attachment_metadata
29
+ */
30
+ public function handleImageUploadHook($meta, $ID = null)
31
+ {
32
+ return \wpSPIO()->getShortPixel()->handleMediaLibraryImageUpload($meta, $ID);
33
+ }
34
+
35
+ /** For conversion
36
+ * @hook wp_handle_upload
37
+ */
38
+ public function handlePng2JpgHook($params)
39
+ {
40
+ return \wpSPIO()->getShortPixel()->convertPng2Jpg($params);
41
+ }
42
+
43
+ /** When replacing happens.
44
+ * @hook wp_handle_replace
45
+ */
46
+ public function handleReplaceHook($params)
47
+ {
48
+ if(isset($params['post_id'])) { //integration with EnableMediaReplace - that's an upload for replacing an existing ID
49
+ $itemHandler = \wpSPIO()->getShortPixel()->onDeleteImage( intval($params['post_id']) );
50
+ $itemHandler->deleteAllSPMeta();
51
+ }
52
+ }
53
+
54
+
55
+ }
class/controller/cache_controller.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+ // Future replacement for everything that needs temporary storage
6
+ // Storage agnostic -> called function should not need to know what is stored where, this is job of controller.
7
+ // Works with cache-model, which handles the data representation and storage.
8
+ //
9
+
10
+ class CacheController extends ShortPixelController
11
+ {
12
+ protected static $cached_items = array();
13
+
14
+ public function __construct()
15
+ {
16
+ $this->loadModel('cache');
17
+ }
18
+
19
+ public function storeItem($name, $value, $expires = HOUR_IN_SECONDS)
20
+ {
21
+ $cache = $this->getItem($name);
22
+ $cache->setValue($value);
23
+ $cache->setExpires($expires);
24
+ $cache->save();
25
+ $cache = apply_filters('shortpixel/cache/save', $cache, $name);
26
+ self::$cached_items[$name] = $cache;
27
+
28
+ return $cache;
29
+ }
30
+
31
+ /** Store a cacheModel Object.
32
+ * This can be used after requesting a cache item for instance.
33
+ * @param CacheModel $cache The Cache Model Item.
34
+ */
35
+ public function storeItemObject(CacheModel $cache)
36
+ {
37
+ self::$cached_items[$cache->getName()] = $cache;
38
+ $cache->save();
39
+ }
40
+
41
+ public function getItem($name)
42
+ {
43
+ if (isset(self::$cached_items[$name]))
44
+ return self::$cached_items[$name];
45
+
46
+ $cache = new cacheModel($name);
47
+ $cache = apply_filters('shortpixel/cache/get', $cache, $name);
48
+ self::$cached_items[$name] = $cache;
49
+
50
+ return $cache;
51
+ }
52
+
53
+ public function deleteItem($name)
54
+ {
55
+ $cache = $this->getItem($name);
56
+
57
+ if ($cache->exists())
58
+ {
59
+ $cache->delete();
60
+ }
61
+
62
+
63
+ }
64
+
65
+ }
class/controller/controller.php CHANGED
@@ -5,7 +5,6 @@ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
5
  class ShortPixelController
6
  {
7
  protected static $controllers = array();
8
- protected static $modelsLoaded = array(); // don't require twice, limit amount of require looksups..
9
 
10
  protected $shortPixel;
11
 
@@ -126,18 +125,7 @@ class ShortPixelController
126
  * @param string $name Name of the model
127
  */
128
  protected function loadModel($name){
129
- $path = \ShortPixelTools::getPluginPath() . 'class/model/' . $name . '_model.php';
130
-
131
- if (! in_array($name, self::$modelsLoaded))
132
- {
133
- self::$modelsLoaded[] = $name;
134
- if(file_exists($path)){
135
- require_once($path);
136
- }
137
- else {
138
- Log::addError("Model $name could not be found");
139
- }
140
- }
141
  }
142
 
143
 
5
  class ShortPixelController
6
  {
7
  protected static $controllers = array();
 
8
 
9
  protected $shortPixel;
10
 
125
  * @param string $name Name of the model
126
  */
127
  protected function loadModel($name){
128
+ return wpSPIO()->loadModel($name);
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
 
131
 
class/controller/edit_media_controller.php CHANGED
@@ -18,6 +18,7 @@ class editMediaController extends ShortPixelController
18
  {
19
 
20
  $this->loadModel($this->model);
 
21
  parent::__construct();
22
  }
23
 
@@ -216,6 +217,10 @@ class editMediaController extends ShortPixelController
216
  return null;
217
  }
218
 
 
 
 
 
219
  $sizes = isset($this->data['sizes']) ? $this->data['sizes'] : array();
220
 
221
  $debugInfo = array();
@@ -223,7 +228,23 @@ class editMediaController extends ShortPixelController
223
  $debugInfo[] = array(__('WPML Duplicates'), json_encode(\ShortPixelMetaFacade::getWPMLDuplicates($this->post_id)) );
224
  $debugInfo[] = array(__('Data'), $this->data);
225
  $debugInfo[] = array(__('Meta'), wp_get_attachment_metadata($this->post_id) );
226
- $debugInfo[] = array(__('Backup Folder'), $this->shortPixel->getBackupFolderAny($this->imageModel->getFile()->getFullPath(), $sizes));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  $debugInfo[] = array(__('Status'), $this->imageModel->getMeta()->getStatus() );
228
 
229
  return $debugInfo;
18
  {
19
 
20
  $this->loadModel($this->model);
21
+ $this->loadModel('image');
22
  parent::__construct();
23
  }
24
 
217
  return null;
218
  }
219
 
220
+ $imageObj = new ImageModel();
221
+ $imageObj->setByPostID($this->post_id);
222
+ $imageFile = $imageObj->getFile();
223
+
224
  $sizes = isset($this->data['sizes']) ? $this->data['sizes'] : array();
225
 
226
  $debugInfo = array();
228
  $debugInfo[] = array(__('WPML Duplicates'), json_encode(\ShortPixelMetaFacade::getWPMLDuplicates($this->post_id)) );
229
  $debugInfo[] = array(__('Data'), $this->data);
230
  $debugInfo[] = array(__('Meta'), wp_get_attachment_metadata($this->post_id) );
231
+ if ($imageFile->hasBackup())
232
+ {
233
+ $backupFile = $imageFile->getBackupFile();
234
+ $debugInfo[] = array(__('Backup Folder'), $this->shortPixel->getBackupFolderAny($this->imageModel->getFile()->getFullPath(), $sizes));
235
+ $debugInfo[] = array(__('Backup File'), (string) $backupFile . '(' . \ShortPixelTools::formatBytes($backupFile->getFileSize()) . ')' );
236
+ }
237
+ else {
238
+ $debugInfo[] = array(__("No Backup Available"), '');
239
+ }
240
+ if ($or = $imageObj->has_original())
241
+ {
242
+ $debugInfo[] = array(__('Original File'), $or->getFullPath() . '(' . \ShortPixelTools::formatBytes($or->getFileSize()) . ')');
243
+ $orbackup = $or->getBackupFile();
244
+ if ($orbackup)
245
+ $debugInfo[] = array(__('Backup'), $orbackup->getFullPath() . '(' . \ShortPixelTools::formatBytes($orbackup->getFileSize()) . ')');
246
+ }
247
+
248
  $debugInfo[] = array(__('Status'), $this->imageModel->getMeta()->getStatus() );
249
 
250
  return $debugInfo;
class/controller/filesystem_controller.php CHANGED
@@ -15,9 +15,9 @@ Class FileSystemController extends ShortPixelController
15
  {
16
  $this->loadModel('file');
17
  $this->loadModel('directory');
18
- $this->loadModel('environment');
19
 
20
- $this->env = new EnvironmentModel();
21
 
22
  }
23
 
@@ -31,6 +31,33 @@ Class FileSystemController extends ShortPixelController
31
  return new FileModel($path);
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  /** Get DirectoryModel for a certain path. This can exist or not
35
  *
36
  * @param String $path Full Path to the Directory.
@@ -69,6 +96,26 @@ Class FileSystemController extends ShortPixelController
69
  }
70
  }
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  /** Not in use yet, do not use. Future replacement. */
73
  public function createBackUpFolder($folder = SHORTPIXEL_BACKUP_FOLDER)
74
  {
@@ -99,6 +146,15 @@ Class FileSystemController extends ShortPixelController
99
  }
100
  }
101
 
 
 
 
 
 
 
 
 
 
102
  if (parse_url($url) !== false)
103
  return $url;
104
  else {
@@ -106,9 +162,56 @@ Class FileSystemController extends ShortPixelController
106
  }
107
  }
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
 
110
 
 
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
 
114
  }
15
  {
16
  $this->loadModel('file');
17
  $this->loadModel('directory');
18
+ // $this->loadModel('environment');
19
 
20
+ $this->env = wpSPIO()->env();
21
 
22
  }
23
 
31
  return new FileModel($path);
32
  }
33
 
34
+ /** Get FileModel for a mediaLibrary post_id .
35
+ *
36
+ * This function exists to put get_attached_file to plugin control
37
+ * Externals / Interals maybe filter it.
38
+ *
39
+ * @param $id Attachement ID for the media library item
40
+ * @return FileModel returns a FileModel file.
41
+ * @todo This function will be more at home in a medialibrary_model
42
+ */
43
+ public function getAttachedFile($id)
44
+ {
45
+ $filepath = get_attached_file($id);
46
+ // same signature as wordpress' filter. Only for this plugin.
47
+ $filepath = apply_filters('shortpixel_get_attached_file', $filepath, $id);
48
+
49
+ return new FileModel($filepath);
50
+
51
+ }
52
+
53
+ /* wp_get_original_image_path with specific ShortPixel filter */
54
+ public function getOriginalPath($id)
55
+ {
56
+ $filepath = \wp_get_original_image_path($id);
57
+ $filepath = apply_filters('shortpixel_get_original_image_path', $filepath, $id);
58
+ return new FileModel($filepath);
59
+ }
60
+
61
  /** Get DirectoryModel for a certain path. This can exist or not
62
  *
63
  * @param String $path Full Path to the Directory.
96
  }
97
  }
98
 
99
+ /** Get the base folder from where custom paths are possible (from WP-base / sitebase)
100
+
101
+ */
102
+ public function getWPFileBase()
103
+ {
104
+ if(\wpSPIO()->env()->is_mainsite) {
105
+ $path = get_home_path();
106
+
107
+ } else {
108
+ $up = wp_upload_dir();
109
+ $path = realpath($up['basedir']);
110
+ }
111
+ $dir = $this->getDirectory($path);
112
+ if (! $dir->exists())
113
+ Log::addWarn('getWPFileBase - Base path doesnt exist');
114
+
115
+ return $dir;
116
+
117
+ }
118
+
119
  /** Not in use yet, do not use. Future replacement. */
120
  public function createBackUpFolder($folder = SHORTPIXEL_BACKUP_FOLDER)
121
  {
146
  }
147
  }
148
 
149
+ $wp_home_path = trailingslashit(get_home_path());
150
+ // If the whole WP homepath is still in URL, assume the replace when wrong ( not replaced w/ URL)
151
+ // This happens when file is outside of wp_uploads_dir
152
+ if (strpos($url, $wp_home_path) !== false)
153
+ {
154
+ $home_url = trailingslashit(get_home_url());
155
+ $url = str_replace($wp_home_path, $home_url, $filepath);
156
+ }
157
+
158
  if (parse_url($url) !== false)
159
  return $url;
160
  else {
162
  }
163
  }
164
 
165
+ /** Sort files / directories in a certain way.
166
+ * Future dev to include options via arg.
167
+ */
168
+ public function sortFiles($array, $args = array() )
169
+ {
170
+ if (count($array) == 0)
171
+ return $array;
172
+
173
+ // what are we sorting.
174
+ $class = get_class($array[0]);
175
+ $is_files = ($class == 'ShortPixel\FileModel') ? true : false; // if not files, then dirs.
176
+
177
+ usort($array, function ($a, $b) use ($is_files)
178
+ {
179
+ if ($is_files)
180
+ return strcmp($a->getFileName(), $b->getFileName());
181
+ else {
182
+ return strcmp($a->getName(), $b->getName());
183
+ }
184
+ }
185
+ );
186
 
187
+ return $array;
188
 
189
+ }
190
 
191
+ /** Get all files from a directory tree, starting at given dir.
192
+ * @param DirectoryModel $dir to recursive into
193
+ * @param Array $filters Collection of optional filters as accepted by FileFilter in directoryModel
194
+ * @return Array Array of FileModel Objects
195
+ **/
196
+ public function getFilesRecursive(DirectoryModel $dir, $filters = array() )
197
+ {
198
+ $fileArray = array();
199
+
200
+ if (! $dir->exists())
201
+ return $fileArray;
202
+
203
+ $files = $dir->getFiles($filters);
204
+ $fileArray = array_merge($fileArray, $files);
205
+
206
+ $subdirs = $dir->getSubDirectories();
207
+
208
+ foreach($subdirs as $subdir)
209
+ {
210
+ $fileArray = array_merge($fileArray, $this->getFilesRecursive($subdir, $filters));
211
+ }
212
+
213
+ return $fileArray;
214
+ }
215
 
216
 
217
  }
class/controller/front_controller.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+ use ShortPixel\Notices\NoticeController as Notices;
5
+
6
+
7
+ /** Handle everything that SP is doing front-wise */
8
+ class frontController extends ShortPixelController
9
+ {
10
+ // DeliverWebp option settings for front-end delivery of webp
11
+ const WEBP_GLOBAL = 1;
12
+ const WEBP_WP = 2;
13
+ const WEBP_NOCHANGE = 3;
14
+
15
+ public function __construct()
16
+ {
17
+ if (wpSPIO()->env()->is_front) // if is front.
18
+ {
19
+ $this->initWebpHooks();
20
+ $this->hookFrontProcessing();
21
+ }
22
+ }
23
+
24
+ public function initWebpHooks()
25
+ {
26
+ $webp_option = \wpSPIO()->settings()->deliverWebp;
27
+
28
+ if ( $webp_option ) {
29
+ if(\ShortPixelTools::shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {
30
+ Notices::addWarning(__('Please deactivate the ShortPixel Image Optimizer\'s
31
+ <a href="options-general.php?page=wp-shortpixel-settings&part=adv-settings">Deliver WebP using PICTURE tag</a>
32
+ option when the ShortPixel Adaptive Images plugin is active.','shortpixel-image-optimiser'), true);
33
+ }
34
+ elseif( $webp_option == self::WEBP_GLOBAL ){
35
+ add_action( 'wp_head', array($this, 'addPictureJs') ); // adds polyfill JS to the header
36
+ add_action( 'init', array($this, 'startOutputBuffer'), 1 ); // start output buffer to capture content
37
+ } elseif ($webp_option == self::WEBP_WP){
38
+ add_filter( 'the_content', array($this, 'convertImgToPictureAddWebp'), 10000 ); // priority big, so it will be executed last
39
+ add_filter( 'the_excerpt', array($this, 'convertImgToPictureAddWebp'), 10000 );
40
+ add_filter( 'post_thumbnail_html', array($this,'convertImgToPictureAddWebp') );
41
+ }
42
+ }
43
+ }
44
+
45
+ public function hookFrontProcessing()
46
+ {
47
+ if (! \wpSPIO()->settings()->frontBootstrap)
48
+ return;
49
+
50
+ $prio = (! defined('SHORTPIXEL_NOFLOCK')) ? \ShortPixelQueue::get() : \ShortPixelQueueDB::get();
51
+
52
+ if ($prio && is_array($prio) && count($prio))
53
+ {
54
+ //also need to have it in the front footer then
55
+ add_action( 'wp_footer', array( \wpSPIO()->getShortPixel(), 'shortPixelJS') );
56
+ //need to add the nopriv action for when items exist in the queue and no user is logged in
57
+ add_action( 'wp_ajax_nopriv_shortpixel_image_processing', array( \wpSPIO()->getShortPixel(), 'handleImageProcessing') );
58
+
59
+ }
60
+ }
61
+
62
+ /* Picture generation, hooked on the_content filter
63
+ * @param $content String The content to check and convert
64
+ * @return String Converted content
65
+ */
66
+ public function convertImgToPictureAddWebp($content) {
67
+
68
+ if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
69
+ //for AMP pages the <picture> tag is not allowed
70
+ return $content . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG is AMP -->' : '');
71
+ }
72
+ require_once(\ShortPixelTools::getPluginPath() . 'class/front/img-to-picture-webp.php');
73
+ return \ShortPixelImgToPictureWebp::convert($content);// . "<!-- PICTURE TAGS BY SHORTPIXEL -->";
74
+ }
75
+
76
+ public function addPictureJs() {
77
+ // Don't do anything with the RSS feed.
78
+ if ( is_feed() || is_admin() ) { return; }
79
+
80
+ echo '<script>'
81
+ . 'var spPicTest = document.createElement( "picture" );'
82
+ . 'if(!window.HTMLPictureElement && document.addEventListener) {'
83
+ . 'window.addEventListener("DOMContentLoaded", function() {'
84
+ . 'var scriptTag = document.createElement("script");'
85
+ . 'scriptTag.src = "' . plugins_url('/res/js/picturefill.min.js', __FILE__) . '";'
86
+ . 'document.body.appendChild(scriptTag);'
87
+ . '});'
88
+ . '}'
89
+ . '</script>';
90
+ }
91
+
92
+
93
+ public function startOutputBuffer() {
94
+ $env = wpSPIO()->env();
95
+ if ($env->is_admin || $env->is_ajaxcall)
96
+ return;
97
+
98
+ $call = array($this, 'convertImgToPictureAddWebp');
99
+ ob_start( $call );
100
+ }
101
+
102
+
103
+
104
+ } // class
class/controller/settings.php CHANGED
@@ -80,8 +80,16 @@ class SettingsController extends shortPixelController
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'];
@@ -111,7 +119,7 @@ class SettingsController extends shortPixelController
111
 
112
  if ($this->postData['includeNextGen'] == 1)
113
  {
114
- $nextgen = new NextGen($this->shortPixel);
115
  $previous = $this->model->includeNextGen;
116
  $nextgen->nextGenEnabled($previous);
117
  }
@@ -172,7 +180,7 @@ class SettingsController extends shortPixelController
172
  /** Checks on things and set them for information. */
173
  protected function loadEnv()
174
  {
175
- $env = $this->getEnv();
176
 
177
  $this->is_nginx = $env->is_nginx;
178
  $this->is_gd_installed = $env->is_gd_installed;
@@ -188,13 +196,6 @@ class SettingsController extends shortPixelController
188
 
189
  }
190
 
191
- public function getEnv()
192
- {
193
- $this->loadModel('environment');
194
- $env = new EnvironmentModel();
195
-
196
- return $env;
197
- }
198
 
199
  /** Check if everything is OK with the Key **/
200
  /*public function checkKey()
@@ -321,7 +322,7 @@ class SettingsController extends shortPixelController
321
  protected function loadCustomFolders()
322
  {
323
  $notice = null;
324
- $customFolders = $this->shortPixel->refreshCustomFolders($notice);
325
 
326
  if (! is_null($notice))
327
  {
@@ -331,7 +332,6 @@ class SettingsController extends shortPixelController
331
  else
332
  Notice::addNormal($message);
333
 
334
-
335
  }
336
 
337
  if ($this->has_nextgen)
@@ -398,7 +398,6 @@ class SettingsController extends shortPixelController
398
  if(!$folderMsg) {
399
  //$notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
400
  $folderMsg = __('Folder added successfully.','shortpixel-image-optimiser');
401
-
402
  $is_warning = false;
403
  }
404
  if ($is_warning)
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
+ $apiKey = $this->postData['apiKey'];
84
+ if (strlen(trim($apiKey)) == 0) // display notice when submitting empty API key
85
+ {
86
+ Notice::addError(sprintf(__("The key you provided has %s characters. The API key should have 20 characters, letters and numbers only.",'shortpixel-image-optimiser'), strlen($apiKey) ));
87
+ }
88
+ else
89
+ {
90
+ $this->keyModel->resetTried();
91
+ $this->keyModel->checkKey($this->postData['apiKey']);
92
+ }
93
  /*if (isset($this->postData['verifiedKey']) && $this->postData['verifiedKey'])
94
  {
95
  $this->model->apiKey = $this->postData['apiKey'];
119
 
120
  if ($this->postData['includeNextGen'] == 1)
121
  {
122
+ $nextgen = new NextGen();
123
  $previous = $this->model->includeNextGen;
124
  $nextgen->nextGenEnabled($previous);
125
  }
180
  /** Checks on things and set them for information. */
181
  protected function loadEnv()
182
  {
183
+ $env = wpSPIO()->env();
184
 
185
  $this->is_nginx = $env->is_nginx;
186
  $this->is_gd_installed = $env->is_gd_installed;
196
 
197
  }
198
 
 
 
 
 
 
 
 
199
 
200
  /** Check if everything is OK with the Key **/
201
  /*public function checkKey()
322
  protected function loadCustomFolders()
323
  {
324
  $notice = null;
325
+ $customFolders = $this->shortPixel->refreshCustomFolders();
326
 
327
  if (! is_null($notice))
328
  {
332
  else
333
  Notice::addNormal($message);
334
 
 
335
  }
336
 
337
  if ($this->has_nextgen)
398
  if(!$folderMsg) {
399
  //$notice = array("status" => "success", "msg" => __('Folder added successfully.','shortpixel-image-optimiser'));
400
  $folderMsg = __('Folder added successfully.','shortpixel-image-optimiser');
 
401
  $is_warning = false;
402
  }
403
  if ($is_warning)
class/db/shortpixel-custom-meta-dao.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
3
-
4
 
5
  class ShortPixelCustomMetaDao {
6
  const META_VERSION = 1;
@@ -138,7 +138,7 @@ class ShortPixelCustomMetaDao {
138
  }
139
 
140
  public function getFolder($path, $deleted = false) {
141
- $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_folders" . ($deleted ? "" : " WHERE path = %s AND status <> -1");
142
  $rows = $this->db->query($sql, array($path));
143
  $folders = array();
144
  foreach($rows as $row) {
@@ -156,11 +156,14 @@ class ShortPixelCustomMetaDao {
156
  return false;
157
  }
158
 
159
- public function addFolder($folder, $fileCount = 0) {
160
- //$sql = "INSERT INTO {$this->db->getPrefix()}shortpixel_folders (path, file_count, ts_created) values (%s, %d, now())";
161
- //$this->db->query($sql, array($folder, $fileCount));
 
 
 
162
  return $this->db->insert($this->db->getPrefix().'shortpixel_folders',
163
- array("path" => $folder, "path_md5" => md5($folder), "file_count" => $fileCount, "ts_updated" => date("Y-m-d H:i:s")),
164
  array("path" => "%s", "path_md5" => "%s", "file_count" => "%d", "ts_updated" => "%s"));
165
  }
166
 
@@ -199,37 +202,46 @@ class ShortPixelCustomMetaDao {
199
  $this->db->query($sql, array($folderPath));
200
  }
201
 
202
-
203
  //$this->db->restoreErrors();
204
  }
205
 
206
  public function newFolderFromPath($path, $uploadPath, $rootPath) {
207
  WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
208
- $addedFolder = ShortPixelFolder::checkFolder($path, $uploadPath);
209
- if(!$addedFolder) {
 
 
 
 
 
 
210
  return __('Folder could not be found: ' . $uploadPath . $path ,'shortpixel-image-optimiser');
211
  }
212
- $addedFolderReal = realpath($addedFolder);
213
- $addedFolder = wp_normalize_path($addedFolder); $addedFolderReal = wp_normalize_path($addedFolderReal); $rootPath = wp_normalize_path($rootPath);
214
- if(strpos($addedFolder, $rootPath) !== 0) {
215
- if(strpos($addedFolderReal, $rootPath) !== 0) {
216
  return( sprintf(__('The %s folder cannot be processed as it\'s not inside the root path of your website (%s).','shortpixel-image-optimiser'),$addedFolder, $rootPath));
217
- } else {
218
- $addedFolder = $addedFolderReal; //addedFolder is a symlink inside the root to a folder outside root - addedFolderReal. Use the inside symlink
219
- }
220
  }
221
- if($this->getFolder($addedFolder)) {
 
222
  return __('Folder already added.','shortpixel-image-optimiser');
223
  }
224
- $folder = new ShortPixelFolder(array("path" => $addedFolder), $this->excludePatterns);
225
- try {
 
226
  $folder->setFileCount($folder->countFiles());
227
  } catch(ShortPixelFileRightsException $ex) {
228
  return $ex->getMessage();
229
- }
 
230
  if(ShortPixelMetaFacade::isMediaSubfolder($folder->getPath())) {
231
  return __('This folder contains Media Library images. To optimize Media Library images please go to <a href="upload.php?mode=list">Media Library list view</a> or to <a href="upload.php?page=wp-short-pixel-bulk">SortPixel Bulk page</a>.','shortpixel-image-optimiser');
232
  }
 
 
 
 
 
233
  $folderMsg = $this->saveFolder($folder);
234
  if(!$folder->getId()) {
235
  //try again creating the tables first.
@@ -242,12 +254,54 @@ class ShortPixelCustomMetaDao {
242
  }
243
 
244
  if(!$folderMsg) {
245
- $fileList = $folder->getFileList();
246
- $this->batchInsertImages($fileList, $folder->getId());
247
  }
248
  return $folderMsg;
249
 
250
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  /**
252
  *
253
  * @param type $path
@@ -269,7 +323,7 @@ class ShortPixelCustomMetaDao {
269
  if($sub) {
270
  $id = $this->updateFolder($sub, $addedPath, 0, $folder->getFileCount());
271
  } else {
272
- $id = $this->addFolder($addedPath, $folder->getFileCount());
273
  }
274
  $folder->setId($id);
275
  return false;
@@ -309,31 +363,44 @@ class ShortPixelCustomMetaDao {
309
  return $id;
310
  }
311
 
312
- public function batchInsertImages($pathsFile, $folderId) {
313
- $pathsFileHandle = fopen($pathsFile, 'r');
314
-
315
  //facem un delete pe cele care nu au shortpixel_folder, pentru curatenie - am mai intalnit situatii in care stergerea s-a agatat (stop monitoring)
 
 
316
  $sqlCleanup = "DELETE FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id NOT IN (SELECT id FROM {$this->db->getPrefix()}shortpixel_folders)";
317
  $this->db->query($sqlCleanup);
318
 
319
- $values = ''; $inserted = 0;
320
  $sql = "INSERT IGNORE INTO {$this->db->getPrefix()}shortpixel_meta(folder_id, path, name, path_md5, status) VALUES ";
321
- for ($i = 0; ($path = fgets($pathsFileHandle)) !== false; $i++) {
322
- $pathParts = explode('/', trim($path));
323
- $namePrep = $this->db->prepare("%s",$pathParts[count($pathParts) - 1]);
324
- $values .= (strlen($values) ? ", ": "") . "(" . $folderId . ", ". $this->db->prepare("%s", trim($path)) . ", ". $namePrep .", '". md5($path) ."', 0)";
325
- if($i % 1000 == 999) {
326
- $id = $this->db->query($sql . $values);
327
- $values = '';
328
- $inserted++;
 
 
 
 
 
 
 
 
 
 
329
  }
 
330
  }
331
  if($values) {
332
- $id = $this->db->query($sql . $values);
 
 
 
 
333
  }
334
- fclose($pathsFileHandle);
335
- unlink($pathsFile);
336
- return $inserted;
337
  }
338
 
339
  public function resetFailed() {
1
  <?php
2
  use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
3
+ use ShortPixel\Notices\NoticeController as Notice;
4
 
5
  class ShortPixelCustomMetaDao {
6
  const META_VERSION = 1;
138
  }
139
 
140
  public function getFolder($path, $deleted = false) {
141
+ $sql = "SELECT * FROM {$this->db->getPrefix()}shortpixel_folders" . ($deleted ? "WHERE path = %s " : " WHERE path = %s AND status <> -1");
142
  $rows = $this->db->query($sql, array($path));
143
  $folders = array();
144
  foreach($rows as $row) {
156
  return false;
157
  }
158
 
159
+ /** Folder is ShortPixelFolder object */
160
+ public function addFolder(ShortPixelFolder $folder, $fileCount = 0) {
161
+ $path = $folder->getPath();
162
+ $tsUpdated = date("Y-m-d H:i:s", $folder->getTsUpdated());
163
+
164
+
165
  return $this->db->insert($this->db->getPrefix().'shortpixel_folders',
166
+ array("path" => $path, "path_md5" => md5($path), "file_count" => $fileCount, "ts_updated" => $tsUpdated, "ts_created" => date("Y-m-d H:i:s")),
167
  array("path" => "%s", "path_md5" => "%s", "file_count" => "%d", "ts_updated" => "%s"));
168
  }
169
 
202
  $this->db->query($sql, array($folderPath));
203
  }
204
 
 
205
  //$this->db->restoreErrors();
206
  }
207
 
208
  public function newFolderFromPath($path, $uploadPath, $rootPath) {
209
  WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
210
+
211
+ $fs = \wpSPIO()->filesystem();
212
+
213
+ //$addedFolder = ShortPixelFolder::checkFolder($path, $uploadPath);
214
+ $newfolder = $fs->getDirectory($path);
215
+ $rootPath = $fs->getWPFileBase();
216
+
217
+ if(! $newfolder->exists() ) {
218
  return __('Folder could not be found: ' . $uploadPath . $path ,'shortpixel-image-optimiser');
219
  }
220
+
221
+ if (! $newfolder->isSubFolderOf($rootPath))
222
+ {
 
223
  return( sprintf(__('The %s folder cannot be processed as it\'s not inside the root path of your website (%s).','shortpixel-image-optimiser'),$addedFolder, $rootPath));
 
 
 
224
  }
225
+
226
+ if($this->getFolder($newfolder->getPath())) {
227
  return __('Folder already added.','shortpixel-image-optimiser');
228
  }
229
+
230
+ $folder = new ShortPixelFolder(array("path" => $newfolder->getPath()), $this->excludePatterns);
231
+ /* try {
232
  $folder->setFileCount($folder->countFiles());
233
  } catch(ShortPixelFileRightsException $ex) {
234
  return $ex->getMessage();
235
+ } */
236
+
237
  if(ShortPixelMetaFacade::isMediaSubfolder($folder->getPath())) {
238
  return __('This folder contains Media Library images. To optimize Media Library images please go to <a href="upload.php?mode=list">Media Library list view</a> or to <a href="upload.php?page=wp-short-pixel-bulk">SortPixel Bulk page</a>.','shortpixel-image-optimiser');
239
  }
240
+
241
+ // Set this to 0 on new, not null since mysql will auto-complete that to current TS.
242
+ $folder->setTSUpdated(0);
243
+ $folder->setFileCount(0);
244
+
245
  $folderMsg = $this->saveFolder($folder);
246
  if(!$folder->getId()) {
247
  //try again creating the tables first.
254
  }
255
 
256
  if(!$folderMsg) {
257
+ //$fileList = $folder->getFileList();
258
+ $this->refreshFolder($newfolder);
259
  }
260
  return $folderMsg;
261
 
262
  }
263
+
264
+ /** Check files and add what's needed */
265
+ public function refreshFolder(ShortPixel\DirectoryModel $folder)
266
+ {
267
+
268
+ $folderObj = $this->getFolder($folder->getPath());
269
+
270
+ if ($folderObj === false)
271
+ {
272
+ Log::addWarn('FolderObj from database is not there, while folder seems ok ' . $folder->getPath() );
273
+ return false;
274
+ }
275
+
276
+ Log::addDebug('Doing Refresh Folder for (DirectoryModel / ShortpixelFolder) ', array($folder->getPath(), $folderObj->getPath()) );
277
+
278
+ $fs = \wpSPIO()->fileSystem();
279
+
280
+ if (! $folder->exists())
281
+ {
282
+ Notice::addError( sprintf(__('Folder %s does not exist! ', 'shortpixel-image-optimiser'), $folder->getPath()) );
283
+ return false;
284
+ }
285
+ if (! $folder->is_writable())
286
+ {
287
+ Notice::addWarning( sprintf(__('Folder %s is not writeable. Please check permissions and try again.','shortpixel-image-optimiser'),$folder->getPath()) );
288
+ }
289
+
290
+ $filter = array('date_newer' => strtotime($folderObj->getTsUpdated()));
291
+ $files = $fs->getFilesRecursive($folder, $filter);
292
+
293
+ $shortpixel = \wpSPIO()->getShortPixel();
294
+ // check processable by invoking filter, for now processablepath takes only paths, not objects.
295
+ $files = array_filter($files, function($file) use($shortpixel) { return $shortpixel->isProcessablePath($file->getFullPath()); });
296
+
297
+ Log::addDebug('Found Files for custom media ' . count($files));
298
+ $folderObj->setTsUpdated(date("Y-m-d H:i:s", $folderObj->getFolderContentsChangeDate()) );
299
+ $folderObj->setFileCount($folderObj->countFiles() );
300
+ $this->update($folderObj);
301
+
302
+ $this->batchInsertImages($files, $folderObj->getId());
303
+ }
304
+
305
  /**
306
  *
307
  * @param type $path
323
  if($sub) {
324
  $id = $this->updateFolder($sub, $addedPath, 0, $folder->getFileCount());
325
  } else {
326
+ $id = $this->addFolder($folder, $folder->getFileCount());
327
  }
328
  $folder->setId($id);
329
  return false;
363
  return $id;
364
  }
365
 
366
+ private function batchInsertImages($files, $folderId) {
 
 
367
  //facem un delete pe cele care nu au shortpixel_folder, pentru curatenie - am mai intalnit situatii in care stergerea s-a agatat (stop monitoring)
368
+ global $wpdb;
369
+
370
  $sqlCleanup = "DELETE FROM {$this->db->getPrefix()}shortpixel_meta WHERE folder_id NOT IN (SELECT id FROM {$this->db->getPrefix()}shortpixel_folders)";
371
  $this->db->query($sqlCleanup);
372
 
373
+ $values = array();
374
  $sql = "INSERT IGNORE INTO {$this->db->getPrefix()}shortpixel_meta(folder_id, path, name, path_md5, status) VALUES ";
375
+ $format = '(%d,%s,%s,%s,%d)';
376
+ $i = 0;
377
+ $count = 0;
378
+ $placeholders = array();
379
+ foreach($files as $file) {
380
+ $filepath = $file->getFullPath();
381
+ $filename = $file->getFileName();
382
+
383
+ array_push($values, $folderId, $filepath, $filename, md5($filepath), 0);
384
+ $placeholders[] = $format;
385
+
386
+
387
+ if($i % 500 == 499) {
388
+ $query = $sql;
389
+ $query .= implode(', ', $placeholders);
390
+ $this->db->query( $this->db->prepare("$query ", $values));
391
+
392
+ $values = array();
393
  }
394
+ $i++;
395
  }
396
  if($values) {
397
+ $query = $sql;
398
+ $query .= implode(', ', $placeholders);
399
+ $result = $wpdb->query( $wpdb->prepare("$query ", $values) );
400
+ Log::addDebug('Q Result', array($result, $wpdb->last_error));
401
+ //$this->db->query( $this->db->prepare("$query ", $values));
402
  }
403
+
 
 
404
  }
405
 
406
  public function resetFailed() {
class/db/shortpixel-meta-facade.php CHANGED
@@ -1,5 +1,8 @@
1
  <?php
2
  use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
 
 
 
3
 
4
  class ShortPixelMetaFacade {
5
  const MEDIA_LIBRARY_TYPE = 1;
@@ -47,7 +50,9 @@ class ShortPixelMetaFacade {
47
  }
48
 
49
  private static function rawMetaToMeta($ID, $rawMeta) {
50
- $path = get_attached_file($ID);
 
 
51
  return new ShortPixelMeta(array(
52
  "id" => $ID,
53
  "name" => ShortPixelAPI::MB_basename($path),
@@ -103,6 +108,9 @@ class ShortPixelMetaFacade {
103
 
104
  // Update MetaData of Image.
105
  public function updateMeta($newMeta = null, $replaceThumbs = false) {
 
 
 
106
  if($newMeta) {
107
  $this->meta = $newMeta;
108
  }
@@ -222,6 +230,8 @@ class ShortPixelMetaFacade {
222
  * This function only hits with images that were optimized, pending or have an error state.
223
  */
224
  public function cleanupMeta($fakeOptPending = false) {
 
 
225
  if($this->type == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
226
  if(!isset($this->rawMeta)) {
227
  $rawMeta = $this->sanitizeMeta(wp_get_attachment_metadata($this->getId()));
@@ -250,9 +260,108 @@ class ShortPixelMetaFacade {
250
  }
251
  }
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  // remove SPFoudnMeta from image. Dirty. @todo <--
254
  public function removeSPFoundMeta()
255
  {
 
256
  if($this->type == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
257
  if(!isset($this->rawMeta)) {
258
  $rawMeta = $this->sanitizeMeta(wp_get_attachment_metadata($this->getId()));
@@ -266,16 +375,22 @@ class ShortPixelMetaFacade {
266
  if (strpos($size, ShortPixelMeta::FOUND_THUMB_PREFIX) !== false)
267
  {
268
  unset($rawMeta['sizes'][$size]);
 
269
  Log::addDebug('Unset sp-found- size' . $size);
270
  }
271
  }
272
  }
273
  $this->rawMeta = $rawMeta;
274
- update_post_meta($this->ID, '_wp_attachment_metadata', $rawMeta);
 
 
 
 
275
  }
276
  }
277
 
278
  function deleteMeta() {
 
279
  if($this->type == self::CUSTOM_TYPE) {
280
  throw new Exception("Not implemented 1");
281
  } else {
@@ -283,9 +398,11 @@ class ShortPixelMetaFacade {
283
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
284
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
285
  }
 
286
  }
287
 
288
  function deleteAllSPMeta() {
 
289
  if($this->type == self::CUSTOM_TYPE) {
290
  throw new Exception("Not implemented 1");
291
  } else {
@@ -375,14 +492,21 @@ class ShortPixelMetaFacade {
375
  * @param $id
376
  * @return false|string
377
  * @throws Exception
 
378
  */
379
  public static function safeGetAttachmentUrl($id) {
380
  $attURL = wp_get_attachment_url($id);
381
- Log::addDebug('Attachment URL - safeGotten - ' . $attURL);
382
  if(!$attURL || !strlen($attURL)) {
383
  throw new Exception("Post metadata is corrupt (No attachment URL for $id)", ShortPixelAPI::ERR_POSTMETA_CORRUPT);
384
  }
385
  if ( !parse_url($attURL, PHP_URL_SCHEME) ) {//no absolute URLs used -> we implement a hack
 
 
 
 
 
 
 
386
  return self::getHomeUrl() . ltrim($attURL,'/');//get the file URL
387
  }
388
  else {
@@ -390,22 +514,63 @@ class ShortPixelMetaFacade {
390
  }
391
  }
392
 
393
- public function getURLsAndPATHs($processThumbnails, $onlyThumbs = false, $addRetina = true, $excludeSizes = array(), $includeOptimized = false) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  $sizesMissing = array();
395
- $fs = new \ShortPixel\FileSystemController();
 
 
 
 
 
 
 
 
 
 
396
 
397
  if($this->type == self::CUSTOM_TYPE) {
398
  $meta = $this->getMeta();
399
 
 
 
 
 
400
  //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
401
- $homeUrl = self::getHomeUrl();
402
- $urlList[] = self::replaceHomePath($meta->getPath(), $homeUrl);
403
 
404
  $filePaths[] = $meta->getPath();
405
  } else {
406
- $path = get_attached_file($this->ID);//get the full file PATH
407
- $fsFile = $fs->getFile($path);
408
- $mainExists = apply_filters('shortpixel_image_exists', file_exists($path), $path, $this->ID);
 
 
 
 
 
409
  try
410
  {
411
  $predownload_url = $url = self::safeGetAttachmentUrl($this->ID); // This function *can* return an PHP error.
@@ -417,11 +582,12 @@ class ShortPixelMetaFacade {
417
  return array("URLs" => array(), "PATHs" => array(), "sizesMissing" => array());
418
  }
419
  $urlList = array(); $filePaths = array();
420
-
421
- Log::addDebug('attached file path: ' . $path, array( (string) $fsFile->getFileDir() ) );
 
422
 
423
  if(!$mainExists) {
424
- list($url, $path) = $this->attemptRemoteDownload($url, $path, $this->ID);
425
  $downloadFile = $fs->getFile($path);
426
  if ($downloadFile->exists()) // check for success.
427
  {
@@ -432,13 +598,38 @@ class ShortPixelMetaFacade {
432
 
433
  if($mainExists) {
434
  $urlList[] = $url;
435
- $filePaths[] = $path;
436
  if($addRetina) {
437
- $this->addRetina($path, $url, $filePaths, $urlList);
438
  }
439
  }
440
 
441
- Log::addDebug('Main file turnout - ', array($url, $path));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
  $meta = $this->getMeta();
444
  $sizes = $meta->getThumbs();
@@ -479,7 +670,7 @@ class ShortPixelMetaFacade {
479
  if($count >= SHORTPIXEL_MAX_THUMBS) break;
480
  $count++;
481
 
482
- $origPath = $tPath = str_replace(ShortPixelAPI::MB_basename($path), $thumbnailInfo['file'], $path);
483
  $origFile = $fs->getFile($origPath);
484
 
485
  if ($origFile->getExtension() == 'webp') // never include any webp extension.
@@ -488,6 +679,8 @@ class ShortPixelMetaFacade {
488
  $file_exists = apply_filters('shortpixel_image_exists', file_exists($origPath), $origPath, $this->ID);
489
  $tUrl = str_replace(ShortPixelAPI::MB_basename($predownload_url), $thumbnailInfo['file'], $predownload_url);
490
 
 
 
491
  // Working on low-key replacement for path handling via FileSystemController.
492
  // This specific fix is related to the possibility of URLs' in metadata
493
  if ( !$file_exists && !file_exists($tPath) )
@@ -519,6 +712,11 @@ class ShortPixelMetaFacade {
519
  //try and download the image from the URL (images present only on CDN)
520
  // Log::addDebug('URLs and Paths - File didnt exists, trying to download', array($tUrl, $origPath));
521
  // $tempThumb = download_url($tUrl, $downloadTimeout);
 
 
 
 
 
522
 
523
  list($tUrl, $tPath) = $this->attemptRemoteDownload($tUrl, $tPath, $this->ID);
524
 
@@ -553,11 +751,22 @@ class ShortPixelMetaFacade {
553
  //convert the + which are replaced with spaces by wp_remote_post
554
  array_walk($urlList, array( &$this, 'replacePlusChar') );
555
 
556
- $filePaths = ShortPixelAPI::CheckAndFixImagePaths($filePaths);//check for images to make sure they exist on disk
 
 
 
 
 
 
 
 
557
  return array("URLs" => $urlList, "PATHs" => $filePaths, "sizesMissing" => $sizesMissing);
558
  }
559
 
560
- private function attemptRemoteDownload($url, $path, $attach_id)
 
 
 
561
  {
562
  $downloadTimeout = max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15);
563
  $fs = new \ShortPixel\FileSystemController();
@@ -631,14 +840,15 @@ class ShortPixelMetaFacade {
631
  return (substr($baseName, -3) === '@2x');
632
  }
633
 
634
- // @todo Not clear what this function does.
635
  public static function getWPMLDuplicates( $id ) {
636
  global $wpdb;
 
637
 
638
  $parentId = get_post_meta ($id, '_icl_lang_duplicate_of', true );
639
  if($parentId) $id = $parentId;
640
 
641
- $mainFile = get_attached_file($id);
642
 
643
  $duplicates = $wpdb->get_col( $wpdb->prepare( "
644
  SELECT pm.post_id FROM {$wpdb->postmeta} pm
@@ -649,7 +859,7 @@ class ShortPixelMetaFacade {
649
  $moreDuplicates = $wpdb->get_results( $wpdb->prepare( "
650
  SELECT p.ID, p.guid FROM {$wpdb->posts} p
651
  INNER JOIN {$wpdb->posts} pbase ON p.guid = pbase.guid
652
- WHERE pbase.ID = %s
653
  ", $id ) );
654
  //MySQL is doing a CASE INSENSITIVE join on p.guid!! so double check the results.
655
  $guid = false;
@@ -674,7 +884,8 @@ class ShortPixelMetaFacade {
674
  if(count($transGroupId)) {
675
  $transGroup = $wpdb->get_results("SELECT element_id FROM {$wpdb->prefix}icl_translations WHERE trid = " . $transGroupId[0]->trid);
676
  foreach($transGroup as $trans) {
677
- if($mainFile == get_attached_file($trans->element_id)){
 
678
  $duplicates[] = $trans->element_id;
679
  }
680
  }
1
  <?php
2
  use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
3
+ use ShortPixel\ImageModel as ImageModel;
4
+ use ShortPixel\FileSystemController as FileSystem;
5
+ use ShortPixel\CacheController as Cache;
6
 
7
  class ShortPixelMetaFacade {
8
  const MEDIA_LIBRARY_TYPE = 1;
50
  }
51
 
52
  private static function rawMetaToMeta($ID, $rawMeta) {
53
+ $file = \wpSPIO()->filesystem()->getAttachedFile($ID);
54
+ $path = $file->getFullPath();
55
+
56
  return new ShortPixelMeta(array(
57
  "id" => $ID,
58
  "name" => ShortPixelAPI::MB_basename($path),
108
 
109
  // Update MetaData of Image.
110
  public function updateMeta($newMeta = null, $replaceThumbs = false) {
111
+
112
+ $this->deleteItemCache();
113
+
114
  if($newMeta) {
115
  $this->meta = $newMeta;
116
  }
230
  * This function only hits with images that were optimized, pending or have an error state.
231
  */
232
  public function cleanupMeta($fakeOptPending = false) {
233
+ $this->deleteItemCache(); // remove any caching.
234
+
235
  if($this->type == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
236
  if(!isset($this->rawMeta)) {
237
  $rawMeta = $this->sanitizeMeta(wp_get_attachment_metadata($this->getId()));
260
  }
261
  }
262
 
263
+ /** Checks if there are unlisted files present in system. Save them into sizes
264
+ * @return int Number 'Unlisted' Items in metadta.
265
+ */
266
+ public function searchUnlistedFiles()
267
+ {
268
+ // must be media library, setting must be on.
269
+ $settings = \wpSPIO()->settings();
270
+
271
+ if($this->getType() != ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE
272
+ || ! $settings->optimizeUnlisted) {
273
+ return 0;
274
+ }
275
+
276
+ //exit($this->_settings->optimizeUnlisted);
277
+
278
+ $meta = $this->getMeta();
279
+ Log::addDebug('Finding Thumbs on path' . $meta->getPath());
280
+ $thumbs = WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath());
281
+
282
+ $fs = new \ShortPixel\FileSystemController();
283
+ $mainFile = $fs->getFile($meta->getPath());
284
+
285
+ // Find Thumbs returns *full file path*
286
+ $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($mainFile->getFullPath());
287
+
288
+ // no thumbs, then done.
289
+ if (count($foundThumbs) == 0)
290
+ return 0;
291
+
292
+ //first identify which thumbs are not in the sizes
293
+ $sizes = $meta->getThumbs();
294
+ $mimeType = false;
295
+
296
+ $allSizes = array();
297
+ $basepath = $mainFile->getFileDir()->getPath();
298
+
299
+ foreach($sizes as $size) {
300
+ // Thumbs should have filename only. This is shortpixel-meta ! Not metadata!
301
+ // Provided filename can be unexpected (URL, fullpath), so first do check, get filename, then check the full path
302
+ $sizeFileCheck = $fs->getFile($size['file']);
303
+ $sizeFilePath = $basepath . $sizeFileCheck->getFileName();
304
+ $sizeFile = $fs->getFile($sizeFilePath);
305
+
306
+ //get the mime-type from one of the thumbs metas
307
+ if(isset($size['mime-type'])) { //situation from support case #9351 Ramesh Mehay
308
+ $mimeType = $size['mime-type'];
309
+ }
310
+ $allSizes[] = $sizeFile;
311
+ }
312
+
313
+ foreach($foundThumbs as $id => $found) {
314
+ $foundFile = $fs->getFile($found);
315
+
316
+ foreach($allSizes as $sizeFile) {
317
+ if ($sizeFile->getExtension() !== $foundFile->getExtension())
318
+ {
319
+ $foundThumbs[$id] = false;
320
+ }
321
+ elseif ($sizeFile->getFileName() === $foundFile->getFileName())
322
+ {
323
+ $foundThumbs[$id] = false;
324
+ }
325
+ }
326
+ }
327
+ // add the unfound ones to the sizes array
328
+ $ind = 1;
329
+ $counter = 0;
330
+ // Assumption:: there is no point in adding to this array since findThumbs should find *all* thumbs that are relevant to this image.
331
+ /*while (isset($sizes[ShortPixelMeta::FOUND_THUMB_PREFIX . str_pad("".$start, 2, '0', STR_PAD_LEFT)]))
332
+ {
333
+ $start++;
334
+ } */
335
+ // $start = $ind;
336
+
337
+ foreach($foundThumbs as $found) {
338
+ if($found !== false) {
339
+ Log::addDebug('Adding File to sizes -> ' . $found);
340
+ $size = getimagesize($found);
341
+ Log::addDebug('Add Unlisted, add size' . $found );
342
+
343
+ $sizes[ShortPixelMeta::FOUND_THUMB_PREFIX . str_pad("".$ind, 2, '0', STR_PAD_LEFT)]= array( // it's a file that has no corresponding thumb so it's the WEBP for the main file
344
+ 'file' => ShortPixelAPI::MB_basename($found),
345
+ 'width' => $size[0],
346
+ 'height' => $size[1],
347
+ 'mime-type' => $mimeType
348
+ );
349
+ $ind++;
350
+ $counter++;
351
+ }
352
+ }
353
+ if($ind > 1) { // at least one thumbnail added, update
354
+ $meta->setThumbs($sizes);
355
+ $this->updateMeta($meta);
356
+ }
357
+
358
+ return $counter;
359
+ }
360
+
361
  // remove SPFoudnMeta from image. Dirty. @todo <--
362
  public function removeSPFoundMeta()
363
  {
364
+ $unset = false; // something was removed?
365
  if($this->type == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) {
366
  if(!isset($this->rawMeta)) {
367
  $rawMeta = $this->sanitizeMeta(wp_get_attachment_metadata($this->getId()));
375
  if (strpos($size, ShortPixelMeta::FOUND_THUMB_PREFIX) !== false)
376
  {
377
  unset($rawMeta['sizes'][$size]);
378
+ $unset = true;
379
  Log::addDebug('Unset sp-found- size' . $size);
380
  }
381
  }
382
  }
383
  $this->rawMeta = $rawMeta;
384
+ if ($unset) // only update on changes.
385
+ {
386
+ $this->deleteItemCache();
387
+ update_post_meta($this->ID, '_wp_attachment_metadata', $rawMeta);
388
+ }
389
  }
390
  }
391
 
392
  function deleteMeta() {
393
+ $this->deleteItemCache();
394
  if($this->type == self::CUSTOM_TYPE) {
395
  throw new Exception("Not implemented 1");
396
  } else {
398
  update_post_meta($this->ID, '_wp_attachment_metadata', $this->rawMeta);
399
  //wp_update_attachment_metadata($this->ID, $this->rawMeta);
400
  }
401
+
402
  }
403
 
404
  function deleteAllSPMeta() {
405
+ $this->deleteItemCache();
406
  if($this->type == self::CUSTOM_TYPE) {
407
  throw new Exception("Not implemented 1");
408
  } else {
492
  * @param $id
493
  * @return false|string
494
  * @throws Exception
495
+ * @todo hack the hack to a solid solution.
496
  */
497
  public static function safeGetAttachmentUrl($id) {
498
  $attURL = wp_get_attachment_url($id);
 
499
  if(!$attURL || !strlen($attURL)) {
500
  throw new Exception("Post metadata is corrupt (No attachment URL for $id)", ShortPixelAPI::ERR_POSTMETA_CORRUPT);
501
  }
502
  if ( !parse_url($attURL, PHP_URL_SCHEME) ) {//no absolute URLs used -> we implement a hack
503
+
504
+ if (! is_null($attURL, PHP_URL_HOST)) // This is for URL's for // without http or https. hackhack.
505
+ {
506
+ $scheme = is_ssl() ? 'https:' : 'http:';
507
+ return $scheme. $attURL;
508
+
509
+ }
510
  return self::getHomeUrl() . ltrim($attURL,'/');//get the file URL
511
  }
512
  else {
514
  }
515
  }
516
 
517
+ /** Get the name for cached items, for now just the URLPATH stuff
518
+ */
519
+ protected function getCacheName()
520
+ {
521
+ return trim('META_URLPATH_' . $this->getQueuedId());
522
+ }
523
+
524
+ public function deleteItemCache()
525
+ {
526
+ // in any update, clear the caching
527
+ $cacheController = new Cache();
528
+ Log::adDDebug('Removing Item Cache -> ' . $this->getCacheName() );
529
+ $cacheController->deleteItem( $this->getCacheName());
530
+
531
+ }
532
+
533
+ /* Function that gathers URLs' and PATHs of attachment. When local file does not exist ( offloading, CDN, etc ), try to download it.
534
+ * This function caches it's result! use deleteItemCache
535
+ *
536
+ * @param $no_exist_check Don't check if returned file exists. This prevents remote downloading it ( in use for onDeleteImage atm)
537
+ * @todo This function needs splitting into urls / paths and made to function more clear .
538
+ */
539
+ public function getURLsAndPATHs($processThumbnails, $onlyThumbs = false, $addRetina = true, $excludeSizes = array(), $includeOptimized = false, $no_exist_check = false) {
540
  $sizesMissing = array();
541
+ $cacheController = new Cache();
542
+
543
+ $cacheItem = $cacheController->getItem( $this->getCacheName() );
544
+ if ($cacheItem->exists())
545
+ {
546
+ Log::addDebug('Get Urls / Paths hit cache -> ', $this->getCacheName());
547
+ return $cacheItem->getValue();
548
+ }
549
+
550
+ $fs = new FileSystem();
551
+ \wpSPIO()->loadModel('image');
552
 
553
  if($this->type == self::CUSTOM_TYPE) {
554
  $meta = $this->getMeta();
555
 
556
+ $path = $meta->getPath();
557
+ $fsFile = $fs->getFile($path);
558
+ $url = $fs->pathToUrl($fsFile);
559
+
560
  //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
561
+ // $homeUrl = self::getHomeUrl();
562
+ $urlList[] = $url; // self::replaceHomePath($meta->getPath(), $homeUrl);
563
 
564
  $filePaths[] = $meta->getPath();
565
  } else {
566
+
567
+ $imageObj = new \ShortPixel\ImageModel();
568
+ $imageObj->setbyPostID($this->ID);
569
+
570
+ $fsFile = $imageObj->getFile(); //\wpSPIO()->filesystem()->getAttachedFile($this->ID);//get the full file PATH
571
+
572
+ //$fsFile = $fs->getFile($path);
573
+ $mainExists = apply_filters('shortpixel_image_exists', $fsFile->exists(), $fsFile->getFullPath(), $this->ID);
574
  try
575
  {
576
  $predownload_url = $url = self::safeGetAttachmentUrl($this->ID); // This function *can* return an PHP error.
582
  return array("URLs" => array(), "PATHs" => array(), "sizesMissing" => array());
583
  }
584
  $urlList = array(); $filePaths = array();
585
+ Log::addDebug('attached file path: ' . (string) $fsFile, array( (string) $fsFile->getFileDir() ) );
586
+ if ($no_exist_check)
587
+ $mainExists = true;
588
 
589
  if(!$mainExists) {
590
+ list($url, $path) = $this->attemptRemoteDownload($url, $fsFile->getFullPath(), $this->ID);
591
  $downloadFile = $fs->getFile($path);
592
  if ($downloadFile->exists()) // check for success.
593
  {
598
 
599
  if($mainExists) {
600
  $urlList[] = $url;
601
+ $filePaths[] = $fsFile->getFullPath();
602
  if($addRetina) {
603
+ $this->addRetina($fsFile->getFullPath(), $url, $filePaths, $urlList);
604
  }
605
  }
606
 
607
+ // new WP 5.3 function, check if file has original ( was scaled )
608
+ $origFile = $imageObj->has_original();
609
+ Log::addDebug('Get Paths and such, original', $origFile);
610
+ if (is_object($origFile))
611
+ {
612
+ //$origFile = $imageObj->getOriginalFile();
613
+ $origurl = wp_get_original_image_url($this->ID); //$fs->pathToUrl($origFile);
614
+ if (! $origFile->exists() && ! $no_exist_check )
615
+ {
616
+ list($origurl, $path) = $this->attemptRemoteDownload($origurl, $origFile->getFullPath(), $this->ID);
617
+ $downloadFile = $fs->getFile($path);
618
+ if ($downloadFile->exists())
619
+ {
620
+ $urlList[] = $origurl;
621
+ $filePaths[] = $downloadFile->getFullPath();
622
+ }
623
+ }
624
+ else
625
+ {
626
+ $urlList[] = $origurl;
627
+ $filePaths[] = $origFile->getFullPath();
628
+ }
629
+
630
+ }
631
+
632
+ Log::addDebug('Main file turnout - ', array($url, (string) $fsFile));
633
 
634
  $meta = $this->getMeta();
635
  $sizes = $meta->getThumbs();
670
  if($count >= SHORTPIXEL_MAX_THUMBS) break;
671
  $count++;
672
 
673
+ $origPath = $tPath = str_replace(ShortPixelAPI::MB_basename($fsFile->getFullPath() ), $thumbnailInfo['file'], $fsFile->getFullPath());
674
  $origFile = $fs->getFile($origPath);
675
 
676
  if ($origFile->getExtension() == 'webp') // never include any webp extension.
679
  $file_exists = apply_filters('shortpixel_image_exists', file_exists($origPath), $origPath, $this->ID);
680
  $tUrl = str_replace(ShortPixelAPI::MB_basename($predownload_url), $thumbnailInfo['file'], $predownload_url);
681
 
682
+ if ($no_exist_check)
683
+ $file_exists = true;
684
  // Working on low-key replacement for path handling via FileSystemController.
685
  // This specific fix is related to the possibility of URLs' in metadata
686
  if ( !$file_exists && !file_exists($tPath) )
712
  //try and download the image from the URL (images present only on CDN)
713
  // Log::addDebug('URLs and Paths - File didnt exists, trying to download', array($tUrl, $origPath));
714
  // $tempThumb = download_url($tUrl, $downloadTimeout);
715
+ $args_for_get = array(
716
+ 'stream' => true,
717
+ 'filename' => $origFile->getFullPath(),
718
+ 'timeout' => max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15),
719
+ );
720
 
721
  list($tUrl, $tPath) = $this->attemptRemoteDownload($tUrl, $tPath, $this->ID);
722
 
751
  //convert the + which are replaced with spaces by wp_remote_post
752
  array_walk($urlList, array( &$this, 'replacePlusChar') );
753
 
754
+ if (! $no_exist_check)
755
+ $filePaths = ShortPixelAPI::CheckAndFixImagePaths($filePaths);//check for images to make sure they exist on disk
756
+
757
+ $result = array("URLs" => $urlList, "PATHs" => $filePaths, "sizesMissing" => $sizesMissing);
758
+
759
+ $cacheItem->setValue($result);
760
+ $cacheItem->setExpires(5 * MINUTE_IN_SECONDS);
761
+ $cacheController->storeItemObject($cacheItem);
762
+
763
  return array("URLs" => $urlList, "PATHs" => $filePaths, "sizesMissing" => $sizesMissing);
764
  }
765
 
766
+ /** @todo Separate download try and post / attach_id functions .
767
+ * Also used by S3-Offload
768
+ */
769
+ public function attemptRemoteDownload($url, $path, $attach_id)
770
  {
771
  $downloadTimeout = max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15);
772
  $fs = new \ShortPixel\FileSystemController();
840
  return (substr($baseName, -3) === '@2x');
841
  }
842
 
843
+ // @todo Function gets duplicated images over WPML. Same image, different language so doesn't need duplication.
844
  public static function getWPMLDuplicates( $id ) {
845
  global $wpdb;
846
+ $fs = \wpSPIO()->filesystem();
847
 
848
  $parentId = get_post_meta ($id, '_icl_lang_duplicate_of', true );
849
  if($parentId) $id = $parentId;
850
 
851
+ $mainFile = $fs->getAttachedFile($id);
852
 
853
  $duplicates = $wpdb->get_col( $wpdb->prepare( "
854
  SELECT pm.post_id FROM {$wpdb->postmeta} pm
859
  $moreDuplicates = $wpdb->get_results( $wpdb->prepare( "
860
  SELECT p.ID, p.guid FROM {$wpdb->posts} p
861
  INNER JOIN {$wpdb->posts} pbase ON p.guid = pbase.guid
862
+ WHERE pbase.ID = %s and p.guid != ''
863
  ", $id ) );
864
  //MySQL is doing a CASE INSENSITIVE join on p.guid!! so double check the results.
865
  $guid = false;
884
  if(count($transGroupId)) {
885
  $transGroup = $wpdb->get_results("SELECT element_id FROM {$wpdb->prefix}icl_translations WHERE trid = " . $transGroupId[0]->trid);
886
  foreach($transGroup as $trans) {
887
+ $transFile = $fs->getFile($trans->element_id);
888
+ if($mainFile->getFullPath() == $transFile->getFullPath() ){
889
  $duplicates[] = $trans->element_id;
890
  }
891
  }
class/db/wp-shortpixel-db.php CHANGED
@@ -1,10 +1,17 @@
1
  <?php
 
 
2
 
3
  class WpShortPixelDb implements ShortPixelDb {
4
 
5
  protected $prefix;
6
  protected $defaultShowErrors;
7
 
 
 
 
 
 
8
  public function __construct($prefix = null) {
9
  $this->prefix = $prefix;
10
  }
@@ -62,7 +69,11 @@ class WpShortPixelDb implements ShortPixelDb {
62
 
63
  public function insert($table, $params, $format = null) {
64
  global $wpdb;
65
- $wpdb->insert($table, $params, $format);
 
 
 
 
66
  return $wpdb->insert_id;
67
  }
68
 
@@ -79,6 +90,20 @@ class WpShortPixelDb implements ShortPixelDb {
79
  return $wpdb->prepare($query, $args);
80
  }
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  public function hideErrors() {
83
  global $wpdb;
84
  $this->defaultShowErrors = $wpdb->show_errors;
1
  <?php
2
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
3
+ use ShortPixel\Notices\NoticeController as Notice;
4
 
5
  class WpShortPixelDb implements ShortPixelDb {
6
 
7
  protected $prefix;
8
  protected $defaultShowErrors;
9
 
10
+ // mostly unimplemented.
11
+ const QTYPE_INSERT = 1;
12
+ const QTYPE_DELETE = 2;
13
+ const QTYPE_UPDATE = 3;
14
+
15
  public function __construct($prefix = null) {
16
  $this->prefix = $prefix;
17
  }
69
 
70
  public function insert($table, $params, $format = null) {
71
  global $wpdb;
72
+
73
+ $num_inserted = $wpdb->insert($table, $params, $format);
74
+ if ($num_inserted === false)
75
+ $this->handleError(self::QTYPE_INSERT);
76
+
77
  return $wpdb->insert_id;
78
  }
79
 
90
  return $wpdb->prepare($query, $args);
91
  }
92
 
93
+ public function handleError($error_type)
94
+ {
95
+ Log::addError('WP Database error: ' . $wpdb->last_error);
96
+
97
+ global $wpdb;
98
+ switch($error_type)
99
+ {
100
+ case self::QTYPE_INSERT:
101
+ Notice::addError('Shortpixel tried to run a database query, but it failed. See the logs for details', 'shortpixel-image-optimiser');
102
+ break;
103
+ }
104
+
105
+ }
106
+
107
  public function hideErrors() {
108
  global $wpdb;
109
  $this->defaultShowErrors = $wpdb->show_errors;
class/db/wp-shortpixel-media-library-adapter.php CHANGED
@@ -3,6 +3,8 @@ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
3
 
4
  class WpShortPixelMediaLbraryAdapter {
5
 
 
 
6
  // Testing is this is better / faster than previous function.
7
  public static function countAllProcessable($settings, $maxId = PHP_INT_MAX, $minId = 0)
8
  {
@@ -20,6 +22,7 @@ class WpShortPixelMediaLbraryAdapter {
20
 
21
  $foundUnlistedThumbs = false;
22
  $counter = 0;
 
23
 
24
  $filesWithErrors = array(); $moreFilesWithErrors = 0;
25
  $excludePatterns = WPShortPixelSettings::getOpt("excludePatterns");
@@ -51,9 +54,7 @@ class WpShortPixelMediaLbraryAdapter {
51
  continue;
52
  }
53
 
54
- $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta
55
- WHERE post_id IN (" . implode(',', $idInfo->ids) . ")
56
- AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )");
57
  /* $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta
58
  WHERE post_id >= $minId and post_id <= $maxId
59
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )"); */
@@ -85,10 +86,15 @@ class WpShortPixelMediaLbraryAdapter {
85
  elseif ( $file->meta_key == "_wp_attachment_metadata" ) //_wp_attachment_metadata
86
  {
87
  $attachment = maybe_unserialize($file->meta_value);
 
 
 
 
88
  $sizesCount = isset($attachment['sizes']) ? self::countSizesNotExcluded($attachment['sizes'], $settings->excludeSizes) : 0;
89
 
90
  // LA FIECARE 100 de imagini facem un test si daca findThumbs da diferit, sa dam o avertizare si eventual optiune
91
  $dismissed = $settings->dismissedNotices ? $settings->dismissedNotices : array();
 
92
  if( $foundUnlistedThumbs === false && $maxId == PHP_INT_MAX && !isset($dismissed['unlisted'])
93
  && ( in_array($counter, array(2,4,6,8)) || floor($counter/100) == 0 && $counter%10 == 0
94
  || floor($counter/1000) == 0 && $counter%100 == 0 || floor($counter/10000) == 0 && $counter%1000 == 0))
@@ -98,8 +104,12 @@ class WpShortPixelMediaLbraryAdapter {
98
  ( !isset($attachment['ShortPixelImprovement']) || $attachment['ShortPixelImprovement'] === 0
99
  || $attachment['ShortPixelImprovement'] === 0.0 || $attachment['ShortPixelImprovement'] === "0"))
100
  {
101
- $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($filePath);
 
 
102
 
 
 
103
  $foundCount = count($foundThumbs);
104
 
105
  if(count($foundThumbs) > $sizesCount) {
@@ -117,6 +127,8 @@ class WpShortPixelMediaLbraryAdapter {
117
  $realSizesCount = $sizesCount;
118
  }
119
  }
 
 
120
  //processable
121
  $isProcessable = false;
122
  $isProcessed = isset($attachment['ShortPixelImprovement'])
@@ -219,10 +231,10 @@ class WpShortPixelMediaLbraryAdapter {
219
  $totalFilesM4 += $totalFilesThis;
220
  }
221
  }
222
- }
223
  unset($filesList);
224
  $pointer += $limit;
225
- $counter++;
226
  }//end while
227
 
228
  return array("totalFiles" => $totalFiles, "mainFiles" => $mainFiles,
@@ -284,9 +296,8 @@ class WpShortPixelMediaLbraryAdapter {
284
  continue;
285
  }
286
 
287
- $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta
288
- WHERE post_id IN (" . implode(',', $idInfo->ids) . ")
289
- AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )");
290
  /* $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta
291
  WHERE post_id >= $minId and post_id <= $maxId
292
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )"); */
@@ -577,6 +588,9 @@ class WpShortPixelMediaLbraryAdapter {
577
  global $wpdb;
578
  //$time = microtime(true);
579
 
 
 
 
580
  $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";
581
  $sqlmeta = $wpdb->prepare($sqlmeta, '_wp_attached_file', '_wp_attachment_metadata', $startId, $endId, $limit);
582
 
@@ -732,7 +746,7 @@ class WpShortPixelMediaLbraryAdapter {
732
  if (! $thumbfile->exists()) // thing must exist.
733
  continue;
734
 
735
- $results[] = $thumbfile->getFullPath();
736
  }
737
 
738
  /* Returns array with full path, as string */
@@ -741,24 +755,16 @@ class WpShortPixelMediaLbraryAdapter {
741
 
742
  private static function getFilesByPattern($path, $pattern)
743
  {
744
- $fs = new \ShortPixel\FileSystemController();
745
-
746
- try
747
- {
748
- $dirIterator = new \DirectoryIterator($path);
749
- $regExIterator = new \RegexIterator($dirIterator, $pattern);
750
- }
751
- catch(\Exception $e)
752
- {
753
- Log::addWarn('GetFilesbyPattern issue with directory. ', $e->getMessage());
754
- return array();
755
- }
756
 
 
 
 
757
 
758
  $images = array();
759
- foreach($regExIterator as $fileinfo)
760
  {
761
- $images[] = $fs->getFile($fileinfo->getPathname());
762
  }
763
 
764
  return $images;
@@ -773,17 +779,20 @@ class WpShortPixelMediaLbraryAdapter {
773
  } else {
774
  $cnt = $wpdb->get_results("SELECT count(*) posts FROM " . $wpdb->prefix . $table);
775
  }
776
- //json_encode($wpdb->get_results("SHOW VARIABLES LIKE 'max_allowed_packet'"));
777
  $posts = isset($cnt) && count($cnt) > 0 ? $cnt[0]->posts : 0;
778
  if($posts > 100000) {
779
- return 10000;
780
  } elseif ($posts > 50000) {
781
- return 5000;
782
  } elseif($posts > 10000) {
783
- return 2000;
784
  } else {
785
- return 500;
786
  }
 
 
 
787
  }
788
 
789
  protected static function getPostIdsChunk($minId, $maxId, $pointer, $limit, $byMinMax = false) {
@@ -817,7 +826,28 @@ class WpShortPixelMediaLbraryAdapter {
817
  }
818
  }
819
 
820
- return (object)array('ids' => $ids, 'idDates' => $idDates, 'last_id' => $ids[count($ids)-1] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
821
  }
822
 
823
  /* Recount images from the media library when something went wrong badly */
3
 
4
  class WpShortPixelMediaLbraryAdapter {
5
 
6
+ private static $urls_this_run = array();
7
+
8
  // Testing is this is better / faster than previous function.
9
  public static function countAllProcessable($settings, $maxId = PHP_INT_MAX, $minId = 0)
10
  {
22
 
23
  $foundUnlistedThumbs = false;
24
  $counter = 0;
25
+ $fs = new \ShortPixel\FileSystemController();
26
 
27
  $filesWithErrors = array(); $moreFilesWithErrors = 0;
28
  $excludePatterns = WPShortPixelSettings::getOpt("excludePatterns");
54
  continue;
55
  }
56
 
57
+ $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta WHERE post_id IN (" . implode(',', $idInfo->ids) . ") AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )");
 
 
58
  /* $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta
59
  WHERE post_id >= $minId and post_id <= $maxId
60
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )"); */
86
  elseif ( $file->meta_key == "_wp_attachment_metadata" ) //_wp_attachment_metadata
87
  {
88
  $attachment = maybe_unserialize($file->meta_value);
89
+ /* Check if array. It's possible to find garbage like WP_Error objects or other random garble in meta_value, so be sure it's an ok thingie.
90
+ */
91
+ if (! is_array($attachment))
92
+ continue;
93
  $sizesCount = isset($attachment['sizes']) ? self::countSizesNotExcluded($attachment['sizes'], $settings->excludeSizes) : 0;
94
 
95
  // LA FIECARE 100 de imagini facem un test si daca findThumbs da diferit, sa dam o avertizare si eventual optiune
96
  $dismissed = $settings->dismissedNotices ? $settings->dismissedNotices : array();
97
+ // @ todo Figure out what this is intended to do.
98
  if( $foundUnlistedThumbs === false && $maxId == PHP_INT_MAX && !isset($dismissed['unlisted'])
99
  && ( in_array($counter, array(2,4,6,8)) || floor($counter/100) == 0 && $counter%10 == 0
100
  || floor($counter/1000) == 0 && $counter%100 == 0 || floor($counter/10000) == 0 && $counter%1000 == 0))
104
  ( !isset($attachment['ShortPixelImprovement']) || $attachment['ShortPixelImprovement'] === 0
105
  || $attachment['ShortPixelImprovement'] === 0.0 || $attachment['ShortPixelImprovement'] === "0"))
106
  {
107
+ // $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($filePath);
108
+ // findThumbs returns fullfilepath.
109
+ $foundThumbs = array();
110
 
111
+ if ($settings->optimizeUnlisted)
112
+ $foundThumbs = WpShortPixelMediaLbraryAdapter::findThumbs($fs->getFile($filePath));
113
  $foundCount = count($foundThumbs);
114
 
115
  if(count($foundThumbs) > $sizesCount) {
127
  $realSizesCount = $sizesCount;
128
  }
129
  }
130
+ $counter++;
131
+
132
  //processable
133
  $isProcessable = false;
134
  $isProcessed = isset($attachment['ShortPixelImprovement'])
231
  $totalFilesM4 += $totalFilesThis;
232
  }
233
  }
234
+ } // foreach fileslist
235
  unset($filesList);
236
  $pointer += $limit;
237
+ // $counter++;
238
  }//end while
239
 
240
  return array("totalFiles" => $totalFiles, "mainFiles" => $mainFiles,
296
  continue;
297
  }
298
 
299
+ $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta WHERE post_id IN (" . implode(',', $idInfo->ids) . ") AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )");
300
+
 
301
  /* $filesList= $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM " . $wpdb->prefix . "postmeta
302
  WHERE post_id >= $minId and post_id <= $maxId
303
  AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )"); */
588
  global $wpdb;
589
  //$time = microtime(true);
590
 
591
+ // Current idea for salvation::
592
+ //SELECT DISTINCT post_id, meta_value FROM wp_postmeta where (meta_key = '_wp_attached_file' or meta_key = '_wp_attachment_metadata') and post_id <= 68677 group by meta_value order by post_id DESC LIMIT 50
593
+
594
  $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";
595
  $sqlmeta = $wpdb->prepare($sqlmeta, '_wp_attached_file', '_wp_attachment_metadata', $startId, $endId, $limit);
596
 
746
  if (! $thumbfile->exists()) // thing must exist.
747
  continue;
748
 
749
+ $results[] = (string) $thumbfile;
750
  }
751
 
752
  /* Returns array with full path, as string */
755
 
756
  private static function getFilesByPattern($path, $pattern)
757
  {
758
+ $fs = \wpSPIO()->filesystem();
 
 
 
 
 
 
 
 
 
 
 
759
 
760
+ $path = trailingslashit($path);
761
+ $files = scandir($path, SCANDIR_SORT_NONE);
762
+ $files = preg_grep($pattern, $files);
763
 
764
  $images = array();
765
+ foreach ($files as $filepath)
766
  {
767
+ $images[] = $fs->getFile($path . $filepath);
768
  }
769
 
770
  return $images;
779
  } else {
780
  $cnt = $wpdb->get_results("SELECT count(*) posts FROM " . $wpdb->prefix . $table);
781
  }
782
+
783
  $posts = isset($cnt) && count($cnt) > 0 ? $cnt[0]->posts : 0;
784
  if($posts > 100000) {
785
+ $chunk = 10000;
786
  } elseif ($posts > 50000) {
787
+ $chunk = 5000;
788
  } elseif($posts > 10000) {
789
+ $chunk = 2000;
790
  } else {
791
+ $chunk = 500;
792
  }
793
+
794
+ // allow a filter to fine-tune specific sql-engines
795
+ return apply_filters('shortpixel/db/chunk_size', $chunk);
796
  }
797
 
798
  protected static function getPostIdsChunk($minId, $maxId, $pointer, $limit, $byMinMax = false) {
826
  }
827
  }
828
 
829
+ $last_id = end($ids);
830
+ reset($ids); // just in case.
831
+ return (object)array('ids' => $ids, 'idDates' => $idDates, 'last_id' => $last_id );
832
+ }
833
+
834
+ /** It happens that URLS are multiple times offered in the same run (sendProcessing) to be processed.
835
+ * - WPML can have duplicate URL's
836
+ * - This function prevents sending a URL's twice in a --single run--
837
+ */
838
+ public static function checkRequestLimiter($urls)
839
+ {
840
+ $hash = md5(serialize($urls));
841
+ Log::addDebug('New Hash -->' . $hash);
842
+
843
+ if (in_array($hash, self::$urls_this_run))
844
+ {
845
+ return false; // no!.
846
+ }
847
+
848
+ self::$urls_this_run[] = $hash;
849
+ Log::addDebug('Hash not found, adding', self::$urls_this_run);
850
+ return true; // ok, process.
851
  }
852
 
853
  /* Recount images from the media library when something went wrong badly */
class/external/gravityforms.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+
4
+ // Gravity Forms integrations.
5
+ class gravityForms
6
+ {
7
+
8
+ public function __construct()
9
+ {
10
+ add_filter( 'gform_save_field_value', array($this,'shortPixelGravityForms'), 10, 5 );
11
+ }
12
+
13
+ function shortPixelGravityForms( $value, $lead, $field, $form ) {
14
+ if($field->type == 'post_image') {
15
+ $this->handleGravityFormsImageField($value);
16
+ }
17
+ return $value;
18
+ }
19
+
20
+ public function handleGravityFormsImageField($value) {
21
+
22
+ $shortPixelObj = wpSPIO()->getShortPixel();
23
+
24
+ if(!($folder = $shortpixelObj->getSpMetaDao()->getFolder(SHORTPIXEL_UPLOADS_BASE . '/gravity_forms'))) {
25
+ return;
26
+ }
27
+ if(strpos($value , '|:|')) {
28
+ $cleanup = explode('|:|', $value);
29
+ $value = $cleanup[0];
30
+ }
31
+ //ShortPixel is monitoring the gravity forms folder, add the image to queue
32
+ $uploadDir = wp_upload_dir();
33
+ $localPath = str_replace($uploadDir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $value);
34
+
35
+ return $shortpixelObj->addPathToCustomFolder($localPath, $folder->getId(), 0);
36
+ }
37
+
38
+ } // class
39
+
40
+ $g = new gravityForms();
class/external/helpscout.php CHANGED
@@ -90,7 +90,7 @@ class HelpScout
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>
96
  <script>
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( wpSPIO()->plugin_url('res/img/notes-sp.png') );?>" style="margin-bottom: 2px;width: 36px;">
94
  </a>
95
  </div>
96
  <script>
class/external/nextgen.php CHANGED
@@ -4,24 +4,30 @@ use ShortPixel\Notices\NoticeController as Notice;
4
 
5
  class NextGen
6
  {
7
- protected $shortPixel;
8
 
9
- /** @todo Temporary constructor. In future, shortpixel should not have to be passed all the time */
10
- public function __construct($shortPixel)
11
  {
12
- $this->shortPixel = $shortPixel;
13
  }
14
 
 
 
 
 
 
 
 
 
15
  /** Enables nextGen, add galleries to custom folders
16
  * @param boolean $silent Throw a notice or not. This seems to be based if nextgen was already activated previously or not.
17
  */
18
  public function nextGenEnabled($silent)
19
  {
20
  \WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
21
- // $prevNextGen = $this->_settings->includeNextGen;
22
  $this->addNextGenGalleriesToCustom($silent);
23
- // $folderMsg = $ret["message"];
24
- // $customFolders = $ret["customFolders"];
25
  }
26
 
27
  /** Adds nextGen galleries to custom table
@@ -29,12 +35,13 @@ class NextGen
29
  * Enabled checks are not an external class issue, so must be done before calling.
30
  */
31
  public function addNextGenGalleriesToCustom($silent = true) {
32
- // $customFolders = array();
 
33
  $folderMsg = "";
34
 
35
  //add the NextGen galleries to custom folders
36
  $ngGalleries = \ShortPixelNextGenAdapter::getGalleries();
37
- $meta = $this->shortPixel->getSpMetaDao();
38
  foreach($ngGalleries as $gallery) {
39
  $msg = $meta->newFolderFromPath($gallery, get_home_path(), \WPShortPixel::getCustomFolderBase());
40
  if($msg) { //try again with ABSPATH as maybe WP is in a subdir
@@ -47,16 +54,24 @@ class NextGen
47
  if (count($ngGalleries) > 0)
48
  {
49
  // put timestamp to this setting.
50
- $settings = new \WPShortPixelSettings();
51
  $settings->hasCustomFolders = time();
52
 
53
  }
54
- // $customFolders = $this->spMetaDao->getFolders();
55
  if (! $silent)
56
  {
57
  Notice::addNormal($folderMsg);
58
  }
59
 
60
- // return array("message" => $silent? "" : $folderMsg, "customFolders" => $customFolders);
61
  }
62
- }
 
 
 
 
 
 
 
 
 
 
4
 
5
  class NextGen
6
  {
7
+ protected $instance;
8
 
9
+ public function __construct()
 
10
  {
11
+ add_action('ngg_added_new_image', array($this,'new_image'));
12
  }
13
 
14
+
15
+ public static function getInstance()
16
+ {
17
+ if (is_null(self::$instance))
18
+ self::$instance = new nextGen();
19
+
20
+ return self::$instance;
21
+ }
22
  /** Enables nextGen, add galleries to custom folders
23
  * @param boolean $silent Throw a notice or not. This seems to be based if nextgen was already activated previously or not.
24
  */
25
  public function nextGenEnabled($silent)
26
  {
27
  \WpShortPixelDb::checkCustomTables(); // check if custom tables are created, if not, create them
28
+
29
  $this->addNextGenGalleriesToCustom($silent);
30
+
 
31
  }
32
 
33
  /** Adds nextGen galleries to custom table
35
  * Enabled checks are not an external class issue, so must be done before calling.
36
  */
37
  public function addNextGenGalleriesToCustom($silent = true) {
38
+ $shortPixel = \wpSPIO()->getShortPixel();
39
+
40
  $folderMsg = "";
41
 
42
  //add the NextGen galleries to custom folders
43
  $ngGalleries = \ShortPixelNextGenAdapter::getGalleries();
44
+ $meta = $shortPixel->getSpMetaDao();
45
  foreach($ngGalleries as $gallery) {
46
  $msg = $meta->newFolderFromPath($gallery, get_home_path(), \WPShortPixel::getCustomFolderBase());
47
  if($msg) { //try again with ABSPATH as maybe WP is in a subdir
54
  if (count($ngGalleries) > 0)
55
  {
56
  // put timestamp to this setting.
57
+ $settings = \wpSPIO()->settings();
58
  $settings->hasCustomFolders = time();
59
 
60
  }
 
61
  if (! $silent)
62
  {
63
  Notice::addNormal($folderMsg);
64
  }
65
 
 
66
  }
67
+
68
+ /** @todo Move handling also to the integration */
69
+ public function add_image($image)
70
+ {
71
+ wpSPIO()->getShortPixel()->handleNextGenImageUpload($image);
72
+ }
73
+ } // class .
74
+
75
+
76
+
77
+ $ng = new nextGen();
class/external/securi.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+
5
+ if (defined("SHORTPIXEL_EXPERIMENTAL_SECURICACHE"))
6
+ {
7
+ $v = new securiVersion();
8
+ }
9
+
10
+ class securiVersion
11
+ {
12
+ public function __construct()
13
+ {
14
+ add_filter('shortpixel_image_urls', array($this, 'timestamp_cache'), 10, 2 );
15
+ }
16
+
17
+ public function timestamp_cache($urls, $id)
18
+ {
19
+
20
+ //$urls = add_filter('shortpixel_image_urls', );
21
+ // https://developer.wordpress.org/reference/functions/get_post_modified_time/
22
+ $time = get_post_modified_time('U', false, $id );
23
+ foreach($urls as $index => $url)
24
+ {
25
+ $urls[$index] = add_query_arg('ver', $time, $url);
26
+ }
27
+
28
+ Log::addDebug('SecuriVersion - URLS being versioned', $urls);
29
+ return $urls;
30
+ }
31
+
32
+
33
+ }
class/external/visualcomposer.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Visual Composer and compat class.
4
+ class visualComp
5
+ {
6
+
7
+ public function __construct()
8
+ {
9
+ add_filter('shortpixel/init/automedialibrary', array($this, 'check_vcinline'));
10
+ }
11
+
12
+ // autolibrary should not do things when VC is being inline somewhere.
13
+ public function check_vcinline($bool)
14
+ {
15
+ if ( function_exists( 'vc_action' ) && vc_action() == 'vc_inline' )
16
+ return false;
17
+ else
18
+ return $bool;
19
+ }
20
+
21
+ } // Class
22
+
23
+ $vc = new visualComp();
class/external/wp-offload-media.php CHANGED
@@ -2,11 +2,13 @@
2
  namespace ShortPixel;
3
  use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
  use ShortPixel\FileSystemController as FileSystem;
 
5
 
6
  class wpOffload
7
  {
8
  protected $as3cf;
9
  protected $active = false;
 
10
 
11
  protected $settings;
12
 
@@ -18,16 +20,27 @@ class wpOffload
18
 
19
  public function init($as3cf)
20
  {
21
-
22
-
 
 
 
 
 
23
 
24
  $this->as3cf = $as3cf;
25
  $this->active = true;
26
 
 
 
 
 
 
 
27
  add_action('shortpixel_image_optimised', array($this, 'image_upload'));
28
  add_action('shortpixel_after_restore_image', array($this, 'image_restore')); // hit this when restoring.
29
  add_action('shortpixel/image/convertpng2jpg_after', array($this, 'image_converted'));
30
- add_action('shortpixel_before_restore_image', array($this, 'remove_remote')); // not optimal, when backup fails this will cause issues.
31
  add_action('shortpixel/image/convertpng2jpg_before', array($this, 'remove_remote'));
32
  add_filter('as3cf_attachment_file_paths', array($this, 'add_webp_paths'));
33
  add_filter('as3cf_remove_attachment_paths', array($this, 'remove_webp_paths'));
@@ -36,15 +49,39 @@ class wpOffload
36
 
37
  add_filter('as3cf_pre_update_attachment_metadata', array($this, 'preventInitialUpload'), 10,4);
38
 
39
- add_filter('get_attached_file', function($file, $id)
 
 
 
 
 
 
 
40
  {
41
- $scheme = parse_url($file, PHP_URL_SCHEME);
42
- if ($scheme !== false && strpos($scheme, 's3') !== false)
43
- {
44
- return get_attached_file($id, true);
45
- }
46
- return $file;
47
- },10, 2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
  public function addURLforDownload($bool, $url, $host)
@@ -64,15 +101,39 @@ class wpOffload
64
 
65
  public function image_restore($id)
66
  {
67
- //$provider_object = $this->as3cf->get_attachment_provider_info($id);
68
- //$this->as3cf->remove_attachment_files_from_provider($id, $provider_object);
69
- $this->remove_remote($id);
 
 
 
 
 
 
 
 
 
 
70
 
71
- //Log::addDebug('S3Offload - Image restore - ', array($id, $provider_object, get_attached_file($id)));
72
- // $provider_object['key'] =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- // add_post_meta( $id, 'amazonS3_info', $provider_object );
75
- // delete_post_meta( $post_id, 'amazonS3_info' );
76
 
77
  $this->image_upload($id);
78
 
@@ -80,44 +141,82 @@ class wpOffload
80
 
81
  public function remove_remote($id)
82
  {
83
- $provider_object = $this->as3cf->get_attachment_provider_info($id);
84
- $this->as3cf->remove_attachment_files_from_provider($id, $provider_object);
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  }
86
 
87
  public function image_converted($id)
88
  {
89
  $fs = new \ShortPixel\FileSystemController();
90
 
 
91
  // delete the old file.
92
- $provider_object = $this->as3cf->get_attachment_provider_info($id);
93
- // $this->as3cf->remove_attachment_files_from_provider($id, $provider_object);
94
 
 
95
  // get some new ones.
96
- $providerFile = $fs->getFile($provider_object['key']);
 
 
 
 
 
 
 
 
 
 
97
  $newFile = $fs->getFile($this->returnOriginalFile(null, $id));
98
 
99
  // convert
100
- $newfilemeta = $provider_object['key'];
101
  if ($providerFile->getExtension() !== $newFile->getExtension())
102
  {
103
- $newfilemeta = str_replace($providerFile->getFileName(), $newFile->getFileName(), $newfilemeta);
104
- Log::addDebug('S3Offload, replacing image in provider meta', array($newfilemeta));
105
- }
106
- else {
107
- Log::addDebug('ProviderFile and NewFile same extension', array($providerFile->getFullPath(), $newFile->getFullPath()));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
109
 
110
  // upload
111
- $provider_object['key'] = $newfilemeta;
112
- update_post_meta( $id, 'amazonS3_info', $provider_object );
113
-
114
  $this->image_upload($id); // delete and reupload
115
  }
116
 
117
  public function image_upload($id)
118
  {
119
- //$this->as3cf->get_setting( 'copy-to-s3' )
120
- if ( ! ( $old_provider_object = $this->as3cf->get_attachment_provider_info( $id ) ) && ! $this->as3cf->get_setting( 'copy-to-s3' ) ) {
 
121
  // abort if not already uploaded to provider and the copy setting is off
122
  Log::addDebug('As3cf image upload is off and object not previously uploaded');
123
  return false;
@@ -128,15 +227,21 @@ class wpOffload
128
  }
129
 
130
  /** This function will cut out the initial upload to S3Offload and rely solely on the image_upload function provided here, after shortpixel optimize.
131
- * Function will only work when plugin is set to auto-optimize new entries to the media library */
 
 
132
  public function preventInitialUpload($bool, $data, $post_id, $old_provider_object)
133
  {
134
- // @todo weak call. See how in future settings might come via central provider.
135
- $settings = new \WPShortPixelSettings();
136
 
137
  if ($settings->autoMediaLibrary)
138
  {
139
- return true;
 
 
 
 
 
140
  }
141
  return $bool;
142
  }
@@ -185,14 +290,14 @@ class wpOffload
185
  {
186
  // Log::addDebug('Received Paths', array($paths));
187
  $paths = $this->getWebpPaths($paths, true);
188
- Log::addDebug('Webp Path Founder (S3)', array($paths));
189
  return $paths;
190
  }
191
 
192
  public function remove_webp_paths($paths)
193
  {
194
  $paths = $this->getWebpPaths($paths, false);
195
- Log::addDebug('Remove S3 Paths', array($paths));
196
  return $paths;
197
  }
198
 
2
  namespace ShortPixel;
3
  use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
  use ShortPixel\FileSystemController as FileSystem;
5
+ use ShortPixel\Notices\NoticeController as Notice;
6
 
7
  class wpOffload
8
  {
9
  protected $as3cf;
10
  protected $active = false;
11
+ private $itemClassName;
12
 
13
  protected $settings;
14
 
20
 
21
  public function init($as3cf)
22
  {
23
+ if (! class_exists('\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item'))
24
+ {
25
+ Notice::addWarning(__('Your S3-Offload plugin version doesn\'t seem to be compatible. Please upgrade the S3-Offload plugin', 'shortpixel-image-optimiser'));
26
+ }
27
+ else {
28
+ $this->itemClassName = '\DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item';
29
+ }
30
 
31
  $this->as3cf = $as3cf;
32
  $this->active = true;
33
 
34
+ // if setting to upload to bucket is off, don't hook or do anything really.
35
+ if (! $this->as3cf->get_setting( 'copy-to-s3' ))
36
+ {
37
+ return;
38
+ }
39
+
40
  add_action('shortpixel_image_optimised', array($this, 'image_upload'));
41
  add_action('shortpixel_after_restore_image', array($this, 'image_restore')); // hit this when restoring.
42
  add_action('shortpixel/image/convertpng2jpg_after', array($this, 'image_converted'));
43
+ add_action('shortpixel_restore_after_pathget', array($this, 'remove_remote')); // not optimal -> has to do w/ doRestore and when URL/PATH is available when not on server .
44
  add_action('shortpixel/image/convertpng2jpg_before', array($this, 'remove_remote'));
45
  add_filter('as3cf_attachment_file_paths', array($this, 'add_webp_paths'));
46
  add_filter('as3cf_remove_attachment_paths', array($this, 'remove_webp_paths'));
49
 
50
  add_filter('as3cf_pre_update_attachment_metadata', array($this, 'preventInitialUpload'), 10,4);
51
 
52
+ add_filter('shortpixel_get_attached_file', array($this, 'get_raw_attached_file'),10, 2);
53
+ add_filter('shortpixel_get_original_image_path', array($this, 'get_raw_original_path'), 10, 2);
54
+ }
55
+
56
+ public function get_raw_attached_file($file, $id)
57
+ {
58
+ $scheme = parse_url($file, PHP_URL_SCHEME);
59
+ if ($scheme !== false && strpos($scheme, 's3') !== false)
60
  {
61
+ return get_attached_file($id, true);
62
+ }
63
+ return $file;
64
+ }
65
+
66
+ // partial copy of the wp_get_original_image_path function. It doesn't support raw filter on get_attached_file
67
+ public function get_raw_original_path($file, $id)
68
+ {
69
+
70
+ $scheme = parse_url($file, PHP_URL_SCHEME);
71
+ if ($scheme !== false && strpos($scheme, 's3') !== false)
72
+ {
73
+ $image_meta = wp_get_attachment_metadata( $id );
74
+ $image_file = get_attached_file( $id, true );
75
+
76
+ if ( empty( $image_meta['original_image'] ) ) {
77
+ $original_image = $image_file;
78
+ } else {
79
+ $original_image = path_join( dirname( $image_file ), $image_meta['original_image'] );
80
+ }
81
+ $file = $original_image;
82
+ }
83
+
84
+ return $file;
85
  }
86
 
87
  public function addURLforDownload($bool, $url, $host)
101
 
102
  public function image_restore($id)
103
  {
104
+ /* voodoo . When images is excluded via S3, it might not exist anymore on server (when option is on). And it will not be in the backups. So before removing remote, and restoring, check this */
105
+ /*
106
+ Seems not needed to make it work, for now.
107
+ $settings = \wpSPIO()->settings();
108
+ $fs = \wpSPIO()->filesystem();
109
+ $excludeSizes = $settings->excludeSizes;
110
+
111
+ $itemHandler = new \ShortPixelMetaFacade($id);
112
+ $itemHandler->deleteItemCache();
113
+
114
+ // get all paths, without anything excluded.
115
+ $paths_all = $itemHandler->getURLsAndPATHs(true, false, true, array(), true,true);
116
+ Log::addDebug('Image Restore, Paths ALL', array($paths_all));
117
 
118
+ if (isset($paths_all['PATHs']))
119
+ {
120
+ foreach($paths_all['PATHs'] as $index => $path)
121
+ {
122
+ $restoredFile = $fs->getFile($path);
123
+ if (! $restoredFile->exists())
124
+ {
125
+ $url = $paths_all['URLs'][$index];
126
+ Log::addDebug('Missing size on restored image data, doing remote download :' . $path);
127
+ $itemHandler->attemptRemoteDownload($url, $path, $id);
128
+ }
129
+ }
130
+ } */
131
+ // sizes without excluded paths
132
+ //$paths_excludes = $itemHandler->getURLsAndPATHs(true, false, true, $excludeSizes, true,true);
133
+
134
+ //$itemHandler->deleteItemCache();
135
 
136
+ $this->remove_remote($id);
 
137
 
138
  $this->image_upload($id);
139
 
141
 
142
  public function remove_remote($id)
143
  {
144
+ $mediaItem = $this->getItemById($id);
145
+ if ($mediaItem === false)
146
+ {
147
+ Log::addDebug('S3-Offload MediaItem not remote - ' . $id);
148
+ return false;
149
+ }
150
+ // $provider_object = $this->as3cf->get_attachment_provider_info($id);
151
+ $this->as3cf->remove_attachment_files_from_provider($id, $mediaItem);
152
+ }
153
+
154
+ /** @return Returns S3Ofload MediaItem, or false when this does not exist */
155
+ protected function getItemById($id)
156
+ {
157
+ $mediaItem = $this->itemClassName::get_by_source_id($id);
158
+ return $mediaItem;
159
  }
160
 
161
  public function image_converted($id)
162
  {
163
  $fs = new \ShortPixel\FileSystemController();
164
 
165
+ // Don't offload when setting is off.
166
  // delete the old file.
167
+ // $provider_object = $this->as3cf->get_attachment_provider_info($id);
 
168
 
169
+ // $this->as3cf->remove_attachment_files_from_provider($id, $provider_object);
170
  // get some new ones.
171
+
172
+ // delete the old file
173
+ $mediaItem = $this->getItemById($id);
174
+ if ($mediaItem === false) // mediaItem seems not present. Probably not a remote file
175
+ return;
176
+
177
+ $this->as3cf->remove_attachment_files_from_provider($id, $mediaItem);
178
+ $providerSourcePath = $mediaItem->source_path();
179
+
180
+ //$providerFile = $fs->getFile($provider_object['key']);
181
+ $providerFile = $fs->getFile($providerSourcePath);
182
  $newFile = $fs->getFile($this->returnOriginalFile(null, $id));
183
 
184
  // convert
185
+ //$newfilemeta = $provider_object['key'];
186
  if ($providerFile->getExtension() !== $newFile->getExtension())
187
  {
188
+ // $newfilemeta = str_replace($providerFile->getFileName(), $newFile->getFileName(), $newfilemeta);
189
+ $data = $mediaItem->key_values(true);
190
+ $record_id = $data['id'];
191
+ /* $data['path']
192
+ $data['original_path']
193
+ $data['original_source_path']
194
+ $data['source_path'] */
195
+
196
+ $data['path'] = str_replace($providerFile->getFileName(), $newFile->getFileName(), $data['path']);
197
+ /*$data['original_path'] = str_replace($providerFile->getFileName(), $newFile->getFileName(), $data['original_path']);
198
+ $data['source_path'] = str_replace($providerFile->getFileName(), $newFile->getFileName(), $data['source_path']);
199
+ $data['original_source_path'] = str_replace($providerFile->getFileName(), $newFile->getFileName(), $data['original_source_path']);
200
+ */
201
+
202
+
203
+ //$provider, $region, $bucket, $path, $is_private, $source_id, $source_path, $original_filename = null, $private_sizes = array(), $id = null
204
+ $newItem = new $this->itemClassName($data['provider'], $data['region'], $data['bucket'], $data['path'], $data['is_private'], $data['source_id'], $data['source_path'], $newFile->getFileName(), $data['extra_info'], $record_id );
205
+
206
+ $newItem->save();
207
+
208
+ Log::addDebug('S3Offload - Uploading converted file ');
209
  }
210
 
211
  // upload
 
 
 
212
  $this->image_upload($id); // delete and reupload
213
  }
214
 
215
  public function image_upload($id)
216
  {
217
+ $item = $this->getItemById($id);
218
+
219
+ if ( $item === false && ! $this->as3cf->get_setting( 'copy-to-s3' ) ) {
220
  // abort if not already uploaded to provider and the copy setting is off
221
  Log::addDebug('As3cf image upload is off and object not previously uploaded');
222
  return false;
227
  }
228
 
229
  /** This function will cut out the initial upload to S3Offload and rely solely on the image_upload function provided here, after shortpixel optimize.
230
+ * Function will only work when plugin is set to auto-optimize new entries to the media library
231
+ * Since S3-Offload 2.3 this will be called on every thumbnail ( changes in WP 5.3 )
232
+ */
233
  public function preventInitialUpload($bool, $data, $post_id, $old_provider_object)
234
  {
235
+ $settings = \wpSPIO()->settings();
 
236
 
237
  if ($settings->autoMediaLibrary)
238
  {
239
+ // Don't prevent whaffever if shortpixel is already done. This can be caused by plugins doing a metadata update, we don't care then.
240
+ if (! isset($data['ShortPixelImprovement']))
241
+ {
242
+ Log::addDebug('Preventing Initial Upload', $post_id);
243
+ return true;
244
+ }
245
  }
246
  return $bool;
247
  }
290
  {
291
  // Log::addDebug('Received Paths', array($paths));
292
  $paths = $this->getWebpPaths($paths, true);
293
+ // Log::addDebug('Webp Path Founder (S3)', array($paths));
294
  return $paths;
295
  }
296
 
297
  public function remove_webp_paths($paths)
298
  {
299
  $paths = $this->getWebpPaths($paths, false);
300
+ // Log::addDebug('Remove S3 Paths', array($paths));
301
  return $paths;
302
  }
303
 
class/external/wpengine.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
4
+
5
+ if ( class_exists( 'WPE_API', false ) ) {
6
+
7
+ /* WPE has a limit on Query size (16K). After that it won't execute the query. */
8
+ add_filter('shortpixel/db/chunk_size', function ($size)
9
+ {
10
+ // 16K chars = 2500 * size of ID 6 chars (until post_id 100.000);
11
+ if ($size > 2500)
12
+ return 2500;
13
+ });
14
+ }
class/front/img-to-picture-webp.php CHANGED
@@ -106,16 +106,18 @@ class ShortPixelImgToPictureWebp
106
  public static function convertImage($match)
107
  {
108
  // Do nothing with images that have the 'sp-no-webp' class.
109
- if (strpos($match[0], 'sp-no-webp')) {
110
  Log::addInfo('SPDBG convertImage skipped, sp-no-webp found');
111
  return $match[0]; //. (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG convertImage sp-no-webp -->' : '');
112
  }
113
 
114
  $img = self::get_attributes($match[0]);
 
115
  if(isset($img['style']) && strpos($img['style'], 'background') !== false) {
116
  //don't replace for <img>'s that have background
117
  return $match[0];
118
  }
 
119
  // [BS] Can return false in case of Module fail. Escape in that case with unmodified image
120
  if ($img === false)
121
  return $match[0];
@@ -138,7 +140,6 @@ class ShortPixelImgToPictureWebp
138
  if(!$id) {
139
  return $match[0];
140
  }
141
- $imageBase = dirname(get_attached_file($id)) . '/';
142
  */
143
 
144
  /* [BS] $updir = wp_upload_dir();
@@ -193,6 +194,7 @@ class ShortPixelImgToPictureWebp
193
  unset($img['data-lazy-src']);
194
  unset($img['srcset']);
195
  unset($img['sizes']);
 
196
  //nor the ones that belong to <img>
197
  unset($img['alt']);
198
  unset($img['id']);
@@ -251,7 +253,11 @@ class ShortPixelImgToPictureWebp
251
  //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
252
  $img['class'] = (isset($img['class']) ? $img['class'] . " " : "") . "sp-no-webp";
253
 
254
- return '<picture ' . self::create_attributes($img) . '>'
 
 
 
 
255
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
256
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
257
  .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . $idAttr . $altAttr . $heightAttr . $widthAttr
@@ -259,6 +265,31 @@ class ShortPixelImgToPictureWebp
259
  .'</picture>';
260
  }
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  public static function testInlineStyle($content)
263
  {
264
  //preg_match_all('/background.*[^:](url\(.*\))[;]/isU', $content, $matches);
@@ -438,6 +469,7 @@ class ShortPixelImgToPictureWebp
438
  foreach ($attribute_array as $attribute => $value) {
439
  $attributes .= $attribute . '="' . $value . '" ';
440
  }
 
441
  // Removes the extra space after the last attribute
442
  return substr($attributes, 0, -1);
443
  }
106
  public static function convertImage($match)
107
  {
108
  // Do nothing with images that have the 'sp-no-webp' class.
109
+ if (strpos($match[0], 'sp-no-webp') || strpos($match[0], 'rev-sildebg')) {
110
  Log::addInfo('SPDBG convertImage skipped, sp-no-webp found');
111
  return $match[0]; //. (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG convertImage sp-no-webp -->' : '');
112
  }
113
 
114
  $img = self::get_attributes($match[0]);
115
+
116
  if(isset($img['style']) && strpos($img['style'], 'background') !== false) {
117
  //don't replace for <img>'s that have background
118
  return $match[0];
119
  }
120
+
121
  // [BS] Can return false in case of Module fail. Escape in that case with unmodified image
122
  if ($img === false)
123
  return $match[0];
140
  if(!$id) {
141
  return $match[0];
142
  }
 
143
  */
144
 
145
  /* [BS] $updir = wp_upload_dir();
194
  unset($img['data-lazy-src']);
195
  unset($img['srcset']);
196
  unset($img['sizes']);
197
+
198
  //nor the ones that belong to <img>
199
  unset($img['alt']);
200
  unset($img['id']);
253
  //add the exclude class so if this content is processed again in other filter, the img is not converted again in picture
254
  $img['class'] = (isset($img['class']) ? $img['class'] . " " : "") . "sp-no-webp";
255
 
256
+ $imgpicture = $img;
257
+ // remove certain elements for the main picture element.
258
+ $imgpicture = self::filterForPicture($imgpicture);
259
+
260
+ return '<picture ' . self::create_attributes($imgpicture) . '>'
261
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcsetWebP . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . ' type="image/webp">'
262
  .'<source ' . $srcsetPrefix . 'srcset="' . $srcset . '"' . ($sizes ? ' ' . $sizesPrefix . 'sizes="' . $sizes . '"' : '') . '>'
263
  .'<img ' . $srcPrefix . 'src="' . $src . '" ' . self::create_attributes($img) . $idAttr . $altAttr . $heightAttr . $widthAttr
265
  .'</picture>';
266
  }
267
 
268
+ /** Check and remove elements that should not be in the picture tag. Especially items within attributes. */
269
+ private static function filterForPicture($img)
270
+ {
271
+
272
+ if (isset($img['style']))
273
+ {
274
+ $bordercount = substr_count($img['style'], 'border');
275
+ for ($i = 0; $i <= $bordercount; $i++)
276
+ {
277
+ $offset = strpos($img['style'], 'border');
278
+ $end = strpos($img['style'], ';', $offset);
279
+
280
+ $nstyle = substr($img['style'], 0, $offset);
281
+
282
+ // if end is false, ; terminator does not exist, assume full string is border.
283
+ if ($end !== false)
284
+ $nstyle .= substr($img['style'], ($end+1) ); // do not include ;
285
+
286
+ $img['style'] = $nstyle;
287
+ }
288
+ }
289
+
290
+ return $img;
291
+ }
292
+
293
  public static function testInlineStyle($content)
294
  {
295
  //preg_match_all('/background.*[^:](url\(.*\))[;]/isU', $content, $matches);
469
  foreach ($attribute_array as $attribute => $value) {
470
  $attributes .= $attribute . '="' . $value . '" ';
471
  }
472
+
473
  // Removes the extra space after the last attribute
474
  return substr($attributes, 0, -1);
475
  }
class/model/apikey_model.php CHANGED
@@ -104,7 +104,7 @@ class ApiKeyModel extends ShortPixelModel
104
  */
105
  public function checkKey($key)
106
  {
107
- //Log::addDebug("Model, checking key ". $key . ' not -' . $this->apiKeyTried);
108
  if (strlen($key) == 0)
109
  {
110
  // first-timers, redirect to nokey screen
@@ -193,6 +193,7 @@ class ApiKeyModel extends ShortPixelModel
193
  // first, save Auth to satisfy getquotainformation
194
 
195
  $quotaData = $this->remoteValidate($key);
 
196
  $checked_key = ($quotaData['APIKeyValid']) ? true : false;
197
 
198
  Log::addDebug('Verify Result', $quotaData);
@@ -273,7 +274,8 @@ class ApiKeyModel extends ShortPixelModel
273
 
274
  protected function checkRedirect()
275
  {
276
- if(!$this->redirectedSettings && !$this->verifiedKey && (!function_exists("is_multisite") || ! is_multisite())) {
 
277
  $this->redirectedSettings = 1;
278
  $this->update();
279
  wp_redirect(admin_url("options-general.php?page=wp-shortpixel-settings"));
104
  */
105
  public function checkKey($key)
106
  {
107
+
108
  if (strlen($key) == 0)
109
  {
110
  // first-timers, redirect to nokey screen
193
  // first, save Auth to satisfy getquotainformation
194
 
195
  $quotaData = $this->remoteValidate($key);
196
+
197
  $checked_key = ($quotaData['APIKeyValid']) ? true : false;
198
 
199
  Log::addDebug('Verify Result', $quotaData);
274
 
275
  protected function checkRedirect()
276
  {
277
+
278
+ if(! \wpSPIO()->env()->is_ajaxcall && !$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"));
class/model/cache_model.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ShortPixel;
3
+ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
4
+
5
+
6
+ /* Model for storing cached data
7
+ *
8
+ * Use this in conjunction with cache controller, don't call it stand-alone.
9
+ */
10
+ class CacheModel
11
+ {
12
+
13
+ protected $name;
14
+ protected $value;
15
+ protected $expires = HOUR_IN_SECONDS;
16
+
17
+ protected $exists = false;
18
+
19
+
20
+ public function __construct($name)
21
+ {
22
+ $this->name = $name;
23
+ $this->load($name);
24
+ }
25
+
26
+ /** Set the expiration of this item. In seconds
27
+ * @param $time Expiration in Seconds
28
+ */
29
+ public function setExpires($time)
30
+ {
31
+ $this->expires = $time;
32
+ }
33
+
34
+ public function setValue($value)
35
+ {
36
+ $this->value = $value;
37
+ }
38
+
39
+ public function exists()
40
+ {
41
+ return $this->exists;
42
+ }
43
+
44
+ public function getValue()
45
+ {
46
+ return $this->value;
47
+ }
48
+
49
+ public function getName()
50
+ {
51
+ return $this->name;
52
+ }
53
+
54
+ public function save()
55
+ {
56
+ $this->exists = set_transient($this->name, $this->value, $this->expires);
57
+ }
58
+
59
+ public function delete()
60
+ {
61
+ delete_transient($this->name);
62
+ $this->exists = false;
63
+ }
64
+
65
+ protected function load()
66
+ {
67
+ $item = get_transient($this->name);
68
+
69
+ if ($item)
70
+ {
71
+ $this->value = $item;
72
+ $this->exists = true;
73
+ }
74
+ }
75
+
76
+ }
class/model/directory_model.php CHANGED
@@ -13,20 +13,29 @@ class DirectoryModel extends ShortPixelModel
13
  {
14
  // Directory info
15
  protected $path;
 
16
 
17
  // Directory status
18
  protected $exists = false;
19
  protected $is_writable = false;
 
 
 
20
 
21
  protected $new_directory_permission = 0755;
22
 
23
  /** Creates a directory model object. DirectoryModel directories don't need to exist on FileSystem
24
  *
25
  * When a filepath is given, it will remove the file part.
 
26
  */
27
  public function __construct($path)
28
  {
29
- //$this->new_directory_permission = octdec(06440);
 
 
 
 
30
 
31
  $path = wp_normalize_path($path);
32
  if (! is_dir($path)) // path is wrong, *or* simply doesn't exist.
@@ -45,11 +54,13 @@ class DirectoryModel extends ShortPixelModel
45
  }
46
 
47
  $this->path = trailingslashit($path);
 
48
 
49
  if (file_exists($this->path))
50
  {
51
  $this->exists();
52
  $this->is_writable();
 
53
  }
54
  }
55
 
@@ -67,6 +78,17 @@ class DirectoryModel extends ShortPixelModel
67
  return $this->path;
68
  }
69
 
 
 
 
 
 
 
 
 
 
 
 
70
  public function exists()
71
  {
72
  $this->exists = file_exists($this->path);
@@ -79,6 +101,11 @@ class DirectoryModel extends ShortPixelModel
79
  return $this->is_writable;
80
  }
81
 
 
 
 
 
 
82
  /** Try to obtain the path, minus the installation directory.
83
  * @return Mixed False if this didn't work, Path as string without basedir if it did. With trailing slash, without starting slash.
84
  */
@@ -93,7 +120,6 @@ class DirectoryModel extends ShortPixelModel
93
  }
94
 
95
  $install_dir = trailingslashit($install_dir);
96
- //Log::addDebug('Install Dir - ' . $install_dir);
97
 
98
  $path = $this->getPath();
99
  // try to build relativePath without first slash.
@@ -161,8 +187,10 @@ class DirectoryModel extends ShortPixelModel
161
  return $testpath;
162
  }
163
 
164
- /** Checks the directory
165
- *
 
 
166
  */
167
  public function check($check_writable = false)
168
  {
@@ -196,5 +224,98 @@ class DirectoryModel extends ShortPixelModel
196
  return true;
197
  }
198
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
  }
13
  {
14
  // Directory info
15
  protected $path;
16
+ protected $name;
17
 
18
  // Directory status
19
  protected $exists = false;
20
  protected $is_writable = false;
21
+ protected $is_readable = false;
22
+
23
+
24
 
25
  protected $new_directory_permission = 0755;
26
 
27
  /** Creates a directory model object. DirectoryModel directories don't need to exist on FileSystem
28
  *
29
  * When a filepath is given, it will remove the file part.
30
+ * @param $path String The Path
31
  */
32
  public function __construct($path)
33
  {
34
+ /* Check if realpath improves things. We support non-existing paths, which realpath fails on, so only apply on result.
35
+ */
36
+ $testpath = realpath($path);
37
+ if ($testpath)
38
+ $path = $testpath;
39
 
40
  $path = wp_normalize_path($path);
41
  if (! is_dir($path)) // path is wrong, *or* simply doesn't exist.
54
  }
55
 
56
  $this->path = trailingslashit($path);
57
+ $this->name = basename($this->path);
58
 
59
  if (file_exists($this->path))
60
  {
61
  $this->exists();
62
  $this->is_writable();
63
+ $this->is_readable();
64
  }
65
  }
66
 
78
  return $this->path;
79
  }
80
 
81
+ public function getModified()
82
+ {
83
+ return filemtime($this->path);
84
+ }
85
+
86
+ /** Get basename of the directory. Without path */
87
+ public function getName()
88
+ {
89
+ return $this->name;
90
+ }
91
+
92
  public function exists()
93
  {
94
  $this->exists = file_exists($this->path);
101
  return $this->is_writable;
102
  }
103
 
104
+ public function is_readable()
105
+ {
106
+ $this->is_readable = is_readable($this->path);
107
+ return $this->is_readable;
108
+ }
109
  /** Try to obtain the path, minus the installation directory.
110
  * @return Mixed False if this didn't work, Path as string without basedir if it did. With trailing slash, without starting slash.
111
  */
120
  }
121
 
122
  $install_dir = trailingslashit($install_dir);
 
123
 
124
  $path = $this->getPath();
125
  // try to build relativePath without first slash.
187
  return $testpath;
188
  }
189
 
190
+ /** Checks the directory into working order
191
+ * Tries to create directory if it doesn't exist
192
+ * Tries to fix file permission if writable is needed
193
+ * @param $check_writable Boolean Directory should be writable
194
  */
195
  public function check($check_writable = false)
196
  {
224
  return true;
225
  }
226
 
227
+ /* Get files from directory
228
+ * @todo In future this should accept some basic filters via args.
229
+ * @returns Array|boolean Returns false if something wrong w/ directory, otherwise a files array of FileModel Object.
230
+ */
231
+ public function getFiles($args = array())
232
+ {
233
+
234
+ $defaults = array(
235
+ 'date_newer' => null,
236
+ );
237
+ $args = wp_parse_args($args, $defaults);
238
+
239
+ // if all filters are set to null, so point in checking those.
240
+ $has_filters = (count(array_filter($args)) > 0) ? true : false;
241
+
242
+ if ( ! $this->exists() || ! $this->is_readable() )
243
+ return false;
244
+
245
+ $fileArray = array();
246
+
247
+ if ($handle = opendir($this->path)) {
248
+ while (false !== ($entry = readdir($handle))) {
249
+ if ( ($entry != "." && $entry != "..") && ! is_dir($this->path . $entry) ) {
250
+ $fileArray[] = new FileModel($this->path . $entry);
251
+ }
252
+ }
253
+ closedir($handle);
254
+ }
255
+
256
+ if ($has_filters)
257
+ {
258
+ $fileArray = array_filter($fileArray, function ($file) use ($args) {
259
+ return $this->fileFilter($file, $args);
260
+ } );
261
+ }
262
+ return $fileArray;
263
+ }
264
+
265
+ // @return boolean true if it should be kept in array, false if not.
266
+ private function fileFilter(FileModel $file, $args)
267
+ {
268
+ $filter = true;
269
+
270
+ if (! is_null($args['date_newer']))
271
+ {
272
+ $modified = $file->getModified();
273
+ if ($modified < $args['date_newer'] )
274
+ $filter = false;
275
+ }
276
+
277
+ return $filter;
278
+ }
279
+
280
+ /** Get subdirectories from directory
281
+ * * @returns Array|boolean Returns false if something wrong w/ directory, otherwise a files array of DirectoryModel Object.
282
+ */
283
+ public function getSubDirectories()
284
+ {
285
+ $fs = \wpSPIO()->fileSystem();
286
+
287
+ if (! $this->exists() || ! $this->is_readable() )
288
+ return false;
289
+
290
+ $dirIt = new \DirectoryIterator($this->path);
291
+ $dirArray = array();
292
+ foreach($dirIt as $fileInfo)
293
+ {
294
+ if ($fileInfo->isDir() && $fileInfo->isReadable() && ! $fileInfo->isDot() )
295
+ {
296
+ $dir = new DirectoryModel($fileInfo->getRealPath());
297
+ if ($dir->exists())
298
+ $dirArray[] = $dir;
299
+ }
300
+
301
+ }
302
+ return $dirArray;
303
+ }
304
+
305
+ /** Check if this dir is a subfolder
306
+ * @param DirectoryModel The directoryObject that is tested as the parent */
307
+ public function isSubFolderOf(DirectoryModel $dir)
308
+ {
309
+ // the same path, is not a subdir of.
310
+ if ($this->getPath() === $dir->getPath())
311
+ return false;
312
+
313
+ // the main path must be followed from the beginning to be a subfolder.
314
+ if (strpos($this->getPath(), $dir->getPath() ) === 0)
315
+ {
316
+ return true;
317
+ }
318
+ return false;
319
+ }
320
 
321
  }
class/model/environment_model.php CHANGED
@@ -3,7 +3,7 @@ namespace ShortPixel;
3
 
4
  /** Loads a few environment variables handy to have nearby
5
  *
6
- * Notice - This is meant to be loaded *often*, so it shouldn't do any heavy lifting without caching the results.
7
  */
8
  class EnvironmentModel extends ShortPixelModel
9
  {
@@ -20,17 +20,102 @@ class EnvironmentModel extends ShortPixelModel
20
  // Integrations
21
  public $has_nextgen;
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  public function __construct()
24
  {
25
- $this->is_nginx = strpos(strtolower($_SERVER["SERVER_SOFTWARE"]), 'nginx') !== false ? true : false;
26
- $this->is_apache = strpos(strtolower($_SERVER["SERVER_SOFTWARE"]), 'apache') !== false ? true : false;
27
- $this->is_gd_installed = function_exists('imagecreatefrompng');
28
- $this->is_curl_installed = function_exists('curl_init');
 
 
 
 
 
 
29
 
30
- $this->is_multisite = (function_exists("is_multisite") && is_multisite()) ? true : false;
31
- $this->is_mainsite = is_main_site();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- $this->has_nextgen = \ShortPixelNextGenAdapter::hasNextGen();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  }
36
  }
3
 
4
  /** Loads a few environment variables handy to have nearby
5
  *
6
+ * Notice - This is meant to be loaded via the plugin class. Easy access with wpSPIO()->getEnv().
7
  */
8
  class EnvironmentModel extends ShortPixelModel
9
  {
20
  // Integrations
21
  public $has_nextgen;
22
 
23
+ // WordPress
24
+ public $is_front = false;
25
+ public $is_admin = false;
26
+ public $is_ajaxcall = false;
27
+
28
+ private $screen_is_set = false;
29
+ public $is_screen_to_use = false; // where shortpixel loads
30
+ public $is_our_screen = false;
31
+
32
+
33
+ protected static $instance;
34
+
35
+
36
  public function __construct()
37
  {
38
+ $this->setServer();
39
+ $this->setWordPress();
40
+ $this->setIntegrations();
41
+ $this->setScreen(); // This might not be set on construct time!
42
+ }
43
+
44
+ public static function getInstance()
45
+ {
46
+ if (is_null(self::$instance))
47
+ self::$instance = new EnvironmentModel();
48
 
49
+ if (! self::$instance->screen_is_set)
50
+ self::$instance->setScreen();
51
+
52
+ return self::$instance;
53
+ }
54
+
55
+ private function setServer()
56
+ {
57
+ $this->is_nginx = strpos(strtolower($_SERVER["SERVER_SOFTWARE"]), 'nginx') !== false ? true : false;
58
+ $this->is_apache = strpos(strtolower($_SERVER["SERVER_SOFTWARE"]), 'apache') !== false ? true : false;
59
+ $this->is_gd_installed = function_exists('imagecreatefrompng');
60
+ $this->is_curl_installed = function_exists('curl_init');
61
+
62
+ }
63
+
64
+ private function setWordPress()
65
+ {
66
+ $this->is_multisite = (function_exists("is_multisite") && is_multisite()) ? true : false;
67
+ $this->is_mainsite = is_main_site();
68
 
69
+ if ( is_admin() )
70
+ $this->is_admin = true;
71
+ else
72
+ $this->is_front = true;
73
+
74
+ if (defined('DOING_AJAX') && DOING_AJAX)
75
+ {
76
+ $this->is_ajaxcall = true;
77
+ }
78
+
79
+ }
80
+
81
+ public function setScreen()
82
+ {
83
+ if (! function_exists('get_current_screen')) // way too early.
84
+ return false;
85
+
86
+ $screen = get_current_screen();
87
+
88
+ if (is_null($screen)) // too early
89
+ return false;
90
+
91
+ // WordPress pages where we'll be active on.
92
+ if(in_array($screen->id, array('upload', 'edit', 'edit-tags', 'post-new', 'post', 'attachment'))) {
93
+ $this->is_screen_to_use = true;
94
+ }
95
+
96
+ // Our pages.
97
+ $pages = \wpSPIO()->get_admin_pages();
98
+
99
+ /* pages can be null in certain cases i.e. plugin activation.
100
+ * treat those cases as improper screen set.
101
+ */
102
+ if (is_null($pages))
103
+ {
104
+ return false;
105
+ }
106
+
107
+ if ( in_array($screen->id, $pages))
108
+ {
109
+ $this->is_screen_to_use = true;
110
+ $this->is_our_screen = true;
111
+ }
112
+
113
+ $this->screen_is_set = true;
114
+ }
115
+
116
+ private function setIntegrations()
117
+ {
118
+ $this->has_nextgen = \ShortPixelNextGenAdapter::hasNextGen();
119
 
120
  }
121
  }
class/model/file_model.php CHANGED
@@ -16,17 +16,16 @@ class FileModel extends ShortPixelModel
16
  {
17
 
18
  // File info
19
- protected $fullpath;
20
- protected $filename; // filename + extension
21
- protected $filebase; // filename without extension
22
- protected $directory;
23
- protected $extension;
24
 
25
  // File Status
26
- protected $exists = false;
27
- protected $is_writable = false;
28
- protected $is_readable = false;
29
-
30
 
31
  protected $status;
32
 
@@ -39,17 +38,13 @@ class FileModel extends ShortPixelModel
39
  /** Creates a file model object. FileModel files don't need to exist on FileSystem */
40
  public function __construct($path)
41
  {
42
- $processed_path = $this->processPath($path);
43
- if ($processed_path !== false)
44
- $this->fullpath = $processed_path; // set processed path if that went alright
45
- else {
46
- $this->fullpath = $path; // fallback, but there should be error state
47
- }
48
-
49
- // $this->fullpath =
50
- $this->setFileInfo();
51
  }
52
 
 
 
 
 
53
  public function __toString()
54
  {
55
  return (string) $this->fullpath;
@@ -57,57 +52,69 @@ class FileModel extends ShortPixelModel
57
 
58
  protected function setFileInfo()
59
  {
60
- if (file_exists($this->fullpath))
61
- {
62
- $this->exists = true;
 
 
 
 
 
 
63
  $info = pathinfo($this->fullpath);
 
64
  $this->filename = isset($info['basename']) ? $info['basename'] : null; // filename + extension
65
  $this->filebase = isset($info['filename']) ? $info['filename'] : null; // only filename
66
  $this->extension = isset($info['extension']) ? $info['extension'] : null; // only (last) extension
67
- $this->directory = isset($info['dirname']) ? new DirectoryModel($info['dirname']) : null;
68
- $this->is_writable();
69
- $this->is_readable();
70
- }
71
- else {
72
- $this->exists = false;
73
- $this->is_writable = false;
74
- $this->is_readable = false;
75
 
76
- if (is_null($this->filename))
77
- $this->filename = basename($this->fullpath);
78
 
79
- if (is_null($this->directory) && ! is_null($this->filename) && strlen($this->filename) > 0)
80
- $this->directory = new DirectoryModel(dirname($this->fullpath));
81
- }
 
 
 
82
  }
83
 
84
  public function exists()
85
  {
86
- $this->exists = file_exists($this->fullpath);
 
 
87
  return $this->exists;
88
  }
89
 
90
  public function is_writable()
91
  {
92
- $this->is_writable = is_writable($this->fullpath);
 
 
93
  return $this->is_writable;
94
  }
95
 
96
  public function is_readable()
97
  {
98
- $this->is_readable = is_readable($this->fullpath);
 
 
99
  return $this->is_readable;
100
  }
101
 
 
 
 
 
 
102
  public function hasBackup()
103
  {
104
  $directory = $this->getBackupDirectory();
105
  if (! $directory)
106
  return false;
107
 
108
- $backupFile = $directory . $this->filename;
109
 
110
- if (file_exists($backupFile))
111
  return true;
112
  else {
113
  return false;
@@ -121,7 +128,7 @@ class FileModel extends ShortPixelModel
121
  public function getBackupFile()
122
  {
123
  if ($this->hasBackup())
124
- return new FileModel($this->getBackupDirectory() . $this->filename);
125
  else
126
  return false;
127
  }
@@ -131,10 +138,22 @@ class FileModel extends ShortPixelModel
131
  * @return DirectoryModel Directorymodel Object
132
  */
133
  public function getFileDir()
134
- {
 
 
 
 
135
  return $this->directory;
136
  }
137
 
 
 
 
 
 
 
 
 
138
  /** Copy a file to somewhere
139
  *
140
  * @param $destination String Full Path to new file.
@@ -151,12 +170,19 @@ class FileModel extends ShortPixelModel
151
  return false;
152
  }
153
 
 
 
 
 
 
 
154
  $is_new = ($destination->exists()) ? false : true;
155
  $status = copy($sourcePath, $destinationPath);
156
 
157
  if (! $status)
158
  Log::addWarn('Could not copy file ' . $sourcePath . ' to' . $destinationPath);
159
  else {
 
160
  $destination->setFileInfo(); // refresh info.
161
  }
162
  //
@@ -174,6 +200,8 @@ class FileModel extends ShortPixelModel
174
  if ($this->copy($destination))
175
  {
176
  $result = $this->delete();
 
 
177
  }
178
  return $result;
179
  }
@@ -183,36 +211,50 @@ class FileModel extends ShortPixelModel
183
  */
184
  public function delete()
185
  {
186
- \wp_delete_file($this->fullpath); // delete file hook via wp_delet_file
187
- $this->setFileInfo(); // update info
188
 
189
  if (! file_exists($this->fullpath))
190
  {
 
191
  return true;
192
  }
193
  else {
194
  return false;
195
  Log::addWarn('File seems not removed - ' . $this->fullpath);
196
  }
 
197
  }
198
 
199
  public function getFullPath()
200
  {
 
 
 
201
  return $this->fullpath;
202
  }
203
 
204
  public function getFileName()
205
  {
 
 
 
206
  return $this->filename;
207
  }
208
 
209
  public function getFileBase()
210
  {
 
 
 
211
  return $this->filebase;
212
  }
213
 
214
  public function getExtension()
215
  {
 
 
 
216
  return $this->extension;
217
  }
218
 
@@ -221,7 +263,7 @@ class FileModel extends ShortPixelModel
221
  */
222
  private function getBackupDirectory()
223
  {
224
- if (is_null($this->directory))
225
  {
226
  return false;
227
  }
@@ -248,9 +290,7 @@ class FileModel extends ShortPixelModel
248
  */
249
  protected function processPath($path)
250
  {
251
-
252
  $original_path = $path;
253
- $path = trim($path);
254
 
255
  if ($this->pathIsUrl($path))
256
  {
@@ -359,7 +399,7 @@ class FileModel extends ShortPixelModel
359
 
360
  private function getUploadPath()
361
  {
362
- $upload_dir = wp_upload_dir(null, false);
363
  $basedir = $upload_dir['basedir'];
364
 
365
  return $basedir;
16
  {
17
 
18
  // File info
19
+ protected $fullpath = null;
20
+ protected $filename = null; // filename + extension
21
+ protected $filebase = null; // filename without extension
22
+ protected $directory = null;
23
+ protected $extension = null;
24
 
25
  // File Status
26
+ protected $exists = null;
27
+ protected $is_writable = null;
28
+ protected $is_readable = null;
 
29
 
30
  protected $status;
31
 
38
  /** Creates a file model object. FileModel files don't need to exist on FileSystem */
39
  public function __construct($path)
40
  {
41
+ $this->fullpath = trim($path);
 
 
 
 
 
 
 
 
42
  }
43
 
44
+ /* Get a string representation of file, the fullpath
45
+ * Note - this might be risky, without processedpath, in cases.
46
+ * @return String Full path processed or unprocessed.
47
+ */
48
  public function __toString()
49
  {
50
  return (string) $this->fullpath;
52
 
53
  protected function setFileInfo()
54
  {
55
+
56
+ $processed_path = $this->processPath($this->fullpath);
57
+ if ($processed_path !== false)
58
+ $this->fullpath = $processed_path; // set processed path if that went alright
59
+
60
+ /* else {
61
+ $this->fullpath = $path; // fallback, but there should be error state
62
+ } */
63
+
64
  $info = pathinfo($this->fullpath);
65
+ // Todo, maybe replace this with splFileINfo.
66
  $this->filename = isset($info['basename']) ? $info['basename'] : null; // filename + extension
67
  $this->filebase = isset($info['filename']) ? $info['filename'] : null; // only filename
68
  $this->extension = isset($info['extension']) ? $info['extension'] : null; // only (last) extension
 
 
 
 
 
 
 
 
69
 
70
+ }
 
71
 
72
+ /** Call when file status changed, so writable / readable / exists are not reliable anymore */
73
+ public function resetStatus()
74
+ {
75
+ $this->is_writable = null;
76
+ $this->is_readable = null;
77
+ $this->exists = null;
78
  }
79
 
80
  public function exists()
81
  {
82
+ if (is_null($this->exists))
83
+ $this->exists = file_exists($this->fullpath);
84
+
85
  return $this->exists;
86
  }
87
 
88
  public function is_writable()
89
  {
90
+ if (is_null($this->is_writable))
91
+ $this->is_writable = is_writable($this->fullpath);
92
+
93
  return $this->is_writable;
94
  }
95
 
96
  public function is_readable()
97
  {
98
+ if (is_null($this->is_readable))
99
+ $this->is_readable = is_readable($this->fullpath);
100
+
101
  return $this->is_readable;
102
  }
103
 
104
+ public function getModified()
105
+ {
106
+ return filemtime($this->fullpath);
107
+ }
108
+
109
  public function hasBackup()
110
  {
111
  $directory = $this->getBackupDirectory();
112
  if (! $directory)
113
  return false;
114
 
115
+ $backupFile = $directory . $this->getFileName();
116
 
117
+ if (file_exists($backupFile) && ! is_dir($backupFile) )
118
  return true;
119
  else {
120
  return false;
128
  public function getBackupFile()
129
  {
130
  if ($this->hasBackup())
131
+ return new FileModel($this->getBackupDirectory() . $this->getFileName() );
132
  else
133
  return false;
134
  }
138
  * @return DirectoryModel Directorymodel Object
139
  */
140
  public function getFileDir()
141
+ {
142
+ // create this only when needed.
143
+ if (is_null($this->directory) && strlen($this->fullpath) > 0)
144
+ $this->directory = new DirectoryModel(dirname($this->fullpath));
145
+
146
  return $this->directory;
147
  }
148
 
149
+ public function getFileSize()
150
+ {
151
+ if ($this->exists())
152
+ return filesize($this->fullpath);
153
+ else
154
+ return 0;
155
+ }
156
+
157
  /** Copy a file to somewhere
158
  *
159
  * @param $destination String Full Path to new file.
170
  return false;
171
  }
172
 
173
+ if (! $this->exists())
174
+ {
175
+ Log::addWarn('Tried to copy non-existing file - ' . $sourcePath);
176
+ return false;
177
+ }
178
+
179
  $is_new = ($destination->exists()) ? false : true;
180
  $status = copy($sourcePath, $destinationPath);
181
 
182
  if (! $status)
183
  Log::addWarn('Could not copy file ' . $sourcePath . ' to' . $destinationPath);
184
  else {
185
+ $destination->resetStatus();
186
  $destination->setFileInfo(); // refresh info.
187
  }
188
  //
200
  if ($this->copy($destination))
201
  {
202
  $result = $this->delete();
203
+ $this->resetStatus();
204
+ $destination->resetStatus();
205
  }
206
  return $result;
207
  }
211
  */
212
  public function delete()
213
  {
214
+ if ($this->exists())
215
+ \wp_delete_file($this->fullpath); // delete file hook via wp_delete_file
216
 
217
  if (! file_exists($this->fullpath))
218
  {
219
+ $this->resetStatus();
220
  return true;
221
  }
222
  else {
223
  return false;
224
  Log::addWarn('File seems not removed - ' . $this->fullpath);
225
  }
226
+
227
  }
228
 
229
  public function getFullPath()
230
  {
231
+ if (is_null($this->filename))
232
+ $this->setFileInfo();
233
+
234
  return $this->fullpath;
235
  }
236
 
237
  public function getFileName()
238
  {
239
+ if (is_null($this->filename))
240
+ $this->setFileInfo();
241
+
242
  return $this->filename;
243
  }
244
 
245
  public function getFileBase()
246
  {
247
+ if (is_null($this->filename))
248
+ $this->setFileInfo();
249
+
250
  return $this->filebase;
251
  }
252
 
253
  public function getExtension()
254
  {
255
+ if (is_null($this->filename))
256
+ $this->setFileInfo();
257
+
258
  return $this->extension;
259
  }
260
 
263
  */
264
  private function getBackupDirectory()
265
  {
266
+ if (is_null($this->getFileDir()))
267
  {
268
  return false;
269
  }
290
  */
291
  protected function processPath($path)
292
  {
 
293
  $original_path = $path;
 
294
 
295
  if ($this->pathIsUrl($path))
296
  {
399
 
400
  private function getUploadPath()
401
  {
402
+ $upload_dir = wp_upload_dir(null, false, false);
403
  $basedir = $upload_dir['basedir'];
404
 
405
  return $basedir;
class/model/image_model.php CHANGED
@@ -5,7 +5,7 @@ use ShortPixel\ShortpixelLogger\ShortPixelLogger as Log;
5
  /* ImageModel class.
6
  *
7
  *
8
- * - Represents a -single- image *not file*.
9
  * - Can be either MediaLibrary, or Custom .
10
  * - Not a replacement of Meta, but might be.
11
  * - Goal: Structural ONE method calls of image related information, and combining information. Same task is now done on many places.
@@ -19,6 +19,10 @@ class ImageModel extends ShortPixelModel
19
  private $facade; // ShortPixelMetaFacade
20
 
21
  protected $thumbsnails = array(); // thumbnails of this
 
 
 
 
22
 
23
 
24
  public function __construct()
@@ -29,15 +33,52 @@ class ImageModel extends ShortPixelModel
29
  public function setByPostID($post_id)
30
  {
31
  // Set Meta
32
- $fs = new FileSystemController();
 
33
  $this->facade = new \ShortPixelMetaFacade($post_id);
34
  $this->meta = $this->facade->getMeta();
35
 
36
- $file = get_attached_file($post_id);
37
- $this->file = $fs->getFile($file);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  public function getMeta()
42
  {
43
  return $this->meta;
@@ -48,6 +89,19 @@ class ImageModel extends ShortPixelModel
48
  return $this->file;
49
  }
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  /* Sanity check in process. Should only be called upon special request, or with single image displays. Should check and recheck stats, thumbs, unlistedthumbs and all assumptions of data that might corrupt or change outside of this plugin */
52
  public function reAcquire()
53
  {
@@ -111,7 +165,7 @@ class ImageModel extends ShortPixelModel
111
  Log::addDebug('Finding Thumbs on path' . $meta->getPath());
112
  $thumbs = \WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath());
113
 
114
- $fs = new FileSystemController();
115
  $mainFile = $this->file;
116
 
117
  // Find Thumbs returns *full file path*
5
  /* ImageModel class.
6
  *
7
  *
8
+ * - Represents a -single- image entity *not file*.
9
  * - Can be either MediaLibrary, or Custom .
10
  * - Not a replacement of Meta, but might be.
11
  * - Goal: Structural ONE method calls of image related information, and combining information. Same task is now done on many places.
19
  private $facade; // ShortPixelMetaFacade
20
 
21
  protected $thumbsnails = array(); // thumbnails of this
22
+ protected $original_file;
23
+
24
+ private $post_id;
25
+ private $is_scaled = false;
26
 
27
 
28
  public function __construct()
33
  public function setByPostID($post_id)
34
  {
35
  // Set Meta
36
+ $fs = \wpSPIO()->filesystem();
37
+ $this->post_id = $post_id;
38
  $this->facade = new \ShortPixelMetaFacade($post_id);
39
  $this->meta = $this->facade->getMeta();
40
 
41
+ $this->file = $fs->getAttachedFile($post_id);
42
+
43
+ // WP 5.3 and higher. Check for original file.
44
+ if (function_exists('wp_get_original_image_path'))
45
+ {
46
+ $this->setOriginalFile();
47
+ }
48
+ }
49
+
50
+
51
+ protected function setOriginalFile()
52
+ {
53
+ $fs = \wpSPIO()->filesystem();
54
+
55
+ if (is_null($this->post_id))
56
+ return false;
57
+
58
+ $originalFile = $fs->getOriginalPath($this->post_id);
59
+
60
+ if ($originalFile->getFullPath() !== $this->file->getfullPath() )
61
+ {
62
+ $this->original_file = $originalFile;
63
+ $this->is_scaled = true;
64
+ }
65
 
66
  }
67
 
68
+ // Not sure if it will work like this.
69
+ public function is_scaled()
70
+ {
71
+ return $this->is_scaled;
72
+ }
73
+
74
+ public function has_original()
75
+ {
76
+ if (is_null($this->original_file))
77
+ return false;
78
+
79
+ return $this->original_file;
80
+ }
81
+
82
  public function getMeta()
83
  {
84
  return $this->meta;
89
  return $this->file;
90
  }
91
 
92
+ /** Get the facade object.
93
+ * @todo Ideally, the facade will be an internal thing, separating the custom and media library functions.
94
+ */
95
+ public function getFacade()
96
+ {
97
+ return $this->facade;
98
+ }
99
+
100
+ /* public function getOriginalFile()
101
+ {
102
+ return $this->origin_file;
103
+ } */
104
+
105
  /* Sanity check in process. Should only be called upon special request, or with single image displays. Should check and recheck stats, thumbs, unlistedthumbs and all assumptions of data that might corrupt or change outside of this plugin */
106
  public function reAcquire()
107
  {
165
  Log::addDebug('Finding Thumbs on path' . $meta->getPath());
166
  $thumbs = \WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath());
167
 
168
+ $fs = \wpSPIO()->filesystem();
169
  $mainFile = $this->file;
170
 
171
  // Find Thumbs returns *full file path*
class/model/shortpixel-folder.php CHANGED
@@ -36,11 +36,16 @@ class ShortPixelFolder extends ShortPixelEntity{
36
  public static function createBackUpFolder($folder = SHORTPIXEL_BACKUP_FOLDER)
37
  {
38
  // create backup folder
39
- $result = @mkdir($folder, 0777, true);
 
 
40
 
41
- if ($result)
42
  {
43
- self::protectDirectoryListing($folder);
 
 
 
44
  }
45
 
46
  return $result;
@@ -50,14 +55,22 @@ class ShortPixelFolder extends ShortPixelEntity{
50
  {
51
  $rules = "Options -Indexes";
52
  /* Plugin init is before loading these admin scripts. So it can happen misc.php is not yet loaded */
53
- if (! function_exists('insert_with_markers'))
 
54
  {
55
- require_once( ABSPATH . 'wp-admin/includes/misc.php' );
56
- }
57
- insert_with_markers( trailingslashit($dirname) . '.htaccess', 'ShortPixel', $rules);
 
 
58
  // note - this doesn't bring the same protection. Subdirs without files written will still be listable.
59
  file_put_contents(trailingslashit($dirname) . 'index.html', chr(0)); // extra - for non-apache
60
 
 
 
 
 
 
61
  }
62
 
63
  /** @todo This function is double with wp-short-pixel - deleteDir */
36
  public static function createBackUpFolder($folder = SHORTPIXEL_BACKUP_FOLDER)
37
  {
38
  // create backup folder
39
+ $fs = \wpSPIO()->filesystem();
40
+ $dir = $fs->getDirectory($folder);
41
+ $result = false;
42
 
43
+ if (! $dir->exists() )
44
  {
45
+ $dir->check();
46
+ //$result = @mkdir($folder, 0777, true);
47
+ self::protectDirectoryListing($folder);
48
+ $result = true;
49
  }
50
 
51
  return $result;
55
  {
56
  $rules = "Options -Indexes";
57
  /* Plugin init is before loading these admin scripts. So it can happen misc.php is not yet loaded */
58
+ // This crashes at 5.3.
59
+ /* if (! function_exists('insert_with_markers'))
60
  {
61
+ //require_once( ABSPATH . 'wp-admin/includes/misc.php' );
62
+ return; // sadly then no.
63
+ } */
64
+
65
+ // insert_with_markers( trailingslashit($dirname) . '.htaccess', 'ShortPixel', $rules);
66
  // note - this doesn't bring the same protection. Subdirs without files written will still be listable.
67
  file_put_contents(trailingslashit($dirname) . 'index.html', chr(0)); // extra - for non-apache
68
 
69
+ if (\wpSPIO()->env()->is_nginx) // nginx has no htaccess support.
70
+ return;
71
+
72
+ file_put_contents(trailingslashit($dirname) . '.htaccess', $rules);
73
+
74
  }
75
 
76
  /** @todo This function is double with wp-short-pixel - deleteDir */
class/shortpixel-png2jpg.php CHANGED
@@ -43,7 +43,7 @@ class ShortPixelPng2Jpg {
43
  $img = @imagecreatefrompng($image);
44
  WPShortPixel::log("PNG2JPG created from png");
45
  if(!$img) {
46
- WPShortPixel::log("PNG2JPG not a PNG");
47
  $transparent = true; //it's not a PNG, can't convert it
48
  } else {
49
  WPShortPixel::log("PNG2JPG is PNG");
@@ -62,7 +62,7 @@ class ShortPixelPng2Jpg {
62
  }
63
  }
64
  }
65
- }
66
 
67
  WPShortPixel::log("PNG2JPG is " . (!$transparent && !$transparent_pixel ? " not" : "") . " transparent");
68
  //pass on the img too, if it was already loaded from PNG, matter of performance
@@ -80,6 +80,8 @@ class ShortPixelPng2Jpg {
80
 
81
  protected function doConvertPng2Jpg($params, $backup, $suffixRegex = false, $img = false) {
82
  $image = $params['file'];
 
 
83
  WPShortPixel::log("PNG2JPG doConvert $image");
84
  if(!$img) {
85
  WPShortPixel::log("PNG2JPG doConvert create from PNG");
@@ -101,15 +103,24 @@ class ShortPixelPng2Jpg {
101
  imagealphablending($bg, 1);
102
  imagecopy($bg, $img, 0, 0, 0, 0, $x, $y);
103
  imagedestroy($img);
104
- $newPath = preg_replace("/\.png$/i", ".jpg", $image);
105
- $newUrl = preg_replace("/\.png$/i", ".jpg", $params['url']);
106
- for ($i = 1; file_exists($newPath); $i++) {
 
 
 
 
 
 
 
 
 
107
  if($suffixRegex) {
108
  $newPath = preg_replace("/(" . $suffixRegex . ")\.png$/i", $i . '-$1.jpg', $image);
109
  }else {
110
  $newPath = preg_replace("/\.png$/i", "-" . $i . ".jpg", $image);
111
  }
112
- }
113
  if (imagejpeg($bg, $newPath, 90)) {
114
  WPShortPixel::log("PNG2JPG doConvert created JPEG at $newPath");
115
  $newSize = filesize($newPath);
@@ -170,7 +181,7 @@ class ShortPixelPng2Jpg {
170
 
171
  /**
172
  * Convert an uploaded image from PNG to JPG
173
- * @param type $params
174
  * @return string
175
  */
176
  public function convertPng2Jpg($params) {
@@ -182,7 +193,7 @@ class ShortPixelPng2Jpg {
182
  if($this->isExcluded($params)) { return $params; }
183
 
184
  $image = $params['file'];
185
- WPShortPixel::log("Convert Media PNG to JPG on upload: {$image}");
186
 
187
  if($this->_settings->png2jpg == 2) {
188
  $doConvert = true;
@@ -222,7 +233,7 @@ class ShortPixelPng2Jpg {
222
 
223
  $meta = $itemHandler->getRawMeta();
224
  $ID = $itemHandler->getId();
225
- $fs = new \Shortpixel\FileSystemController;
226
 
227
  if(!$this->_settings->png2jpg || !isset($meta['file']) || strtolower(substr($meta['file'], -4)) !== '.png') {
228
  return ;
@@ -232,7 +243,8 @@ class ShortPixelPng2Jpg {
232
  WPShortPixel::log("Send to processing: Convert Media PNG to JPG #{$ID} META: " . json_encode($meta));
233
 
234
  $image = $meta['file']; // This is not a full path!
235
- $imagePath = get_attached_file($ID); // This is a full path.
 
236
  $basePath = trailingslashit(str_replace($image, "", $imagePath));
237
  $imageUrl = wp_get_attachment_url($ID);
238
  $baseUrl = self::removeUrlProtocol(trailingslashit(str_replace($image, "", $imageUrl))); //make the base url protocol agnostic if it's not already
@@ -250,16 +262,18 @@ class ShortPixelPng2Jpg {
250
  $meta['ShortPixel']['Retries'] = isset($meta['ShortPixel']['Retries']) ? $meta['ShortPixel']['Retries'] + 1 : 1;
251
  $meta['ShortPixel']['ErrCode'] = ShortPixelAPI::ERR_PNG2JPG_MEMORY;
252
  //wp_update_attachment_metadata($ID, $meta);
 
253
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
254
 
255
  if($this->_settings->png2jpg == 2) {
256
  $doConvert = true;
257
  } else {
258
  $retC = $this->canConvertPng2Jpg($imagePath);
 
259
  $doConvert = $retC['notTransparent'];
260
  }
261
  if (!$doConvert) {
262
- Log::addDebug("PNG2JPG not a PNG");
263
  return $meta; //cannot convert it
264
  }
265
 
@@ -332,11 +346,14 @@ class ShortPixelPng2Jpg {
332
  'optimizationPercent' => round(100.0 * (1.00 - $jpgSize / $pngSize)));
333
  //wp_update_attachment_metadata($ID, $meta);
334
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
 
335
  Log::addDebug("Updated meta: " . json_encode($meta));
336
  do_action('shortpixel/image/convertpng2jpg_after', $ID, $meta);
337
  }
338
 
339
- self::png2JpgUpdateUrls(array(), $toReplace);
 
 
340
  $fs = new \ShortPixel\FileSystemController();
341
 
342
  foreach($toUnlink as $unlink) {
@@ -412,6 +429,7 @@ class ShortPixelPng2Jpg {
412
  if(count($options) == 0) {
413
  $options = array_keys($queries);
414
  }
 
415
  foreach($options as $option){
416
  WPShortPixel::log("PNG2JPG update URLS on $option ");
417
  if( $option == 'custom' ){
@@ -439,6 +457,16 @@ class ShortPixelPng2Jpg {
439
  $n++;
440
  }
441
  }
 
 
 
 
 
 
 
 
 
 
442
  }
443
  $results[$option] = array($n, $queries[$option][1]);
444
  }
43
  $img = @imagecreatefrompng($image);
44
  WPShortPixel::log("PNG2JPG created from png");
45
  if(!$img) {
46
+ WPShortPixel::log("PNG2JPG not a PNG, imagecreatefrompng failed ");
47
  $transparent = true; //it's not a PNG, can't convert it
48
  } else {
49
  WPShortPixel::log("PNG2JPG is PNG");
62
  }
63
  }
64
  }
65
+ } // non-transparant.
66
 
67
  WPShortPixel::log("PNG2JPG is " . (!$transparent && !$transparent_pixel ? " not" : "") . " transparent");
68
  //pass on the img too, if it was already loaded from PNG, matter of performance
80
 
81
  protected function doConvertPng2Jpg($params, $backup, $suffixRegex = false, $img = false) {
82
  $image = $params['file'];
83
+ $fs = \wpSPIO()->filesystem();
84
+
85
  WPShortPixel::log("PNG2JPG doConvert $image");
86
  if(!$img) {
87
  WPShortPixel::log("PNG2JPG doConvert create from PNG");
103
  imagealphablending($bg, 1);
104
  imagecopy($bg, $img, 0, 0, 0, 0, $x, $y);
105
  imagedestroy($img);
106
+ //$newPath = preg_replace("/\.png$/i", ".jpg", $image);
107
+
108
+ $fsFile = $fs->getFile($image); // the original png file
109
+ $filename = $fsFile->getFileName();
110
+ $newFileName = $fsFile->getFileBase() . '.jpg'; // convert extension to .png
111
+
112
+ $uniquepath = wp_unique_filename($fsFile->getFullPath(), $newFileName);
113
+ $newPath = (string) $fsFile->getFileDir() . $uniquepath;
114
+
115
+ // check old filename, replace with uniqued filename.
116
+ $newUrl = str_replace($filename, $uniquepath, $params['url']); //preg_replace("/\.png$/i", ".jpg", $params['url']);
117
+ /*(for ($i = 1; file_exists($newPath); $i++) {
118
  if($suffixRegex) {
119
  $newPath = preg_replace("/(" . $suffixRegex . ")\.png$/i", $i . '-$1.jpg', $image);
120
  }else {
121
  $newPath = preg_replace("/\.png$/i", "-" . $i . ".jpg", $image);
122
  }
123
+ } */
124
  if (imagejpeg($bg, $newPath, 90)) {
125
  WPShortPixel::log("PNG2JPG doConvert created JPEG at $newPath");
126
  $newSize = filesize($newPath);
181
 
182
  /**
183
  * Convert an uploaded image from PNG to JPG
184
+ * @param type $params ( file, url, type ) - Connected to https://developer.wordpress.org/reference/hooks/wp_handle_upload/
185
  * @return string
186
  */
187
  public function convertPng2Jpg($params) {
193
  if($this->isExcluded($params)) { return $params; }
194
 
195
  $image = $params['file'];
196
+ Log::addDebug("Convert Media PNG to JPG on upload: {$image}");
197
 
198
  if($this->_settings->png2jpg == 2) {
199
  $doConvert = true;
233
 
234
  $meta = $itemHandler->getRawMeta();
235
  $ID = $itemHandler->getId();
236
+ $fs = \wpSPIO()->filesystem();
237
 
238
  if(!$this->_settings->png2jpg || !isset($meta['file']) || strtolower(substr($meta['file'], -4)) !== '.png') {
239
  return ;
243
  WPShortPixel::log("Send to processing: Convert Media PNG to JPG #{$ID} META: " . json_encode($meta));
244
 
245
  $image = $meta['file']; // This is not a full path!
246
+ $imageFile = $fs->getAttachedFile($ID);
247
+ $imagePath = $imageFile->getFullPath(); // This is a full path.
248
  $basePath = trailingslashit(str_replace($image, "", $imagePath));
249
  $imageUrl = wp_get_attachment_url($ID);
250
  $baseUrl = self::removeUrlProtocol(trailingslashit(str_replace($image, "", $imageUrl))); //make the base url protocol agnostic if it's not already
262
  $meta['ShortPixel']['Retries'] = isset($meta['ShortPixel']['Retries']) ? $meta['ShortPixel']['Retries'] + 1 : 1;
263
  $meta['ShortPixel']['ErrCode'] = ShortPixelAPI::ERR_PNG2JPG_MEMORY;
264
  //wp_update_attachment_metadata($ID, $meta);
265
+
266
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
267
 
268
  if($this->_settings->png2jpg == 2) {
269
  $doConvert = true;
270
  } else {
271
  $retC = $this->canConvertPng2Jpg($imagePath);
272
+
273
  $doConvert = $retC['notTransparent'];
274
  }
275
  if (!$doConvert) {
276
+ Log::addDebug("PNG2JPG not a PNG, or transparent when this setting is off - " . $imagePath);
277
  return $meta; //cannot convert it
278
  }
279
 
346
  'optimizationPercent' => round(100.0 * (1.00 - $jpgSize / $pngSize)));
347
  //wp_update_attachment_metadata($ID, $meta);
348
  update_post_meta($ID, '_wp_attachment_metadata', $meta);
349
+ $itemHandler->deleteItemCache(); // remove cache since filetype changes.
350
  Log::addDebug("Updated meta: " . json_encode($meta));
351
  do_action('shortpixel/image/convertpng2jpg_after', $ID, $meta);
352
  }
353
 
354
+ if(count($toReplace)) {
355
+ self::png2JpgUpdateUrls(array(), $toReplace);
356
+ }
357
  $fs = new \ShortPixel\FileSystemController();
358
 
359
  foreach($toUnlink as $unlink) {
429
  if(count($options) == 0) {
430
  $options = array_keys($queries);
431
  }
432
+ $startTime = microtime(true);
433
  foreach($options as $option){
434
  WPShortPixel::log("PNG2JPG update URLS on $option ");
435
  if( $option == 'custom' ){
457
  $n++;
458
  }
459
  }
460
+ //check time. This loop could take long because it's scanning all the postmeta table which in some cases becomes huge...
461
+ $timeElapsed = microtime(true) - $startTime;
462
+ if($timeElapsed > SHORTPIXEL_MAX_EXECUTION_TIME / 2) {
463
+ //try to add some time or get out if not
464
+ if(set_time_limit(SHORTPIXEL_MAX_EXECUTION_TIME)) {
465
+ $startTime += SHORTPIXEL_MAX_EXECUTION_TIME / 2;
466
+ } else {
467
+ break;
468
+ }
469
+ }
470
  }
471
  $results[$option] = array($n, $queries[$option][1]);
472
  }
class/shortpixel-tools.php CHANGED
@@ -12,6 +12,18 @@ class ShortPixelTools {
12
  return $data;
13
  }*/
14
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  public static function snakeToCamel($snake_case) {
16
  return str_replace(' ', '', ucwords(str_replace('_', ' ', $snake_case)));
17
  }
@@ -75,6 +87,18 @@ class ShortPixelTools {
75
  return $h_time;
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  public static function commonPrefix($str1, $str2) {
79
  $limit = min(strlen($str1), strlen($str2));
80
  for ($i = 0; $i < $limit && $str1[$i] === $str2[$i]; $i++);
12
  return $data;
13
  }*/
14
 
15
+ /** Find if a certain plugin is active
16
+ * @param String $plugin The name of plugin being searched for
17
+ * @return Boolean Active or not
18
+ */
19
+ public static function shortPixelIsPluginActive($plugin) {
20
+ $activePlugins = apply_filters( 'active_plugins', get_option( 'active_plugins', array()));
21
+ if ( is_multisite() ) {
22
+ $activePlugins = array_merge($activePlugins, get_site_option( 'active_sitewide_plugins'));
23
+ }
24
+ return in_array( $plugin, $activePlugins);
25
+ }
26
+
27
  public static function snakeToCamel($snake_case) {
28
  return str_replace(' ', '', ucwords(str_replace('_', ' ', $snake_case)));
29
  }
87
  return $h_time;
88
  }
89
 
90
+ static public function formatBytes($bytes, $precision = 2) {
91
+ $units = array('B', 'KB', 'MB', 'GB', 'TB');
92
+
93
+ $bytes = max($bytes, 0);
94
+ $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
95
+ $pow = min($pow, count($units) - 1);
96
+
97
+ $bytes /= pow(1024, $pow);
98
+
99
+ return round($bytes, $precision) . ' ' . $units[$pow];
100
+ }
101
+
102
  public static function commonPrefix($str1, $str2) {
103
  $limit = min(strlen($str1), strlen($str2));
104
  for ($i = 0; $i < $limit && $str1[$i] === $str2[$i]; $i++);
class/shortpixel_queue.php CHANGED
@@ -299,7 +299,7 @@ class ShortPixelQueue {
299
 
300
  public function setBulkPreviousPercent() {
301
  //processable and already processed
302
- $res = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->settings, $this->getFlagBulkId(), $this->settings->stopBulkId);
303
  $this->settings->bulkCount = $res["mainFiles"];
304
 
305
  //if compression type changed, add also the images with the other compression type
@@ -355,6 +355,7 @@ class ShortPixelQueue {
355
  }
356
 
357
  public function startBulk($type = self::BULK_TYPE_OPTIMIZE) {
 
358
  $this->resetStartBulkId(); //start downwards from the biggest item ID
359
  $this->resetStopBulkId();
360
  $this->flagBulkStart(); //we use this to detect new added files while bulk is running
299
 
300
  public function setBulkPreviousPercent() {
301
  //processable and already processed
302
+ $res = $this->settings->currentStats; //this is only called when the bulk is started and the stats are already refreshed in bulkProcess()...
303
  $this->settings->bulkCount = $res["mainFiles"];
304
 
305
  //if compression type changed, add also the images with the other compression type
355
  }
356
 
357
  public function startBulk($type = self::BULK_TYPE_OPTIMIZE) {
358
+ Log::addDebug('startBulk 0');
359
  $this->resetStartBulkId(); //start downwards from the biggest item ID
360
  $this->resetStopBulkId();
361
  $this->flagBulkStart(); //we use this to detect new added files while bulk is running
class/view/settings/part-advanced.php CHANGED
@@ -85,7 +85,7 @@ namespace ShortPixel;
85
  <td>
86
  <?php if(!($st == "Empty")) { ?>
87
  <a href="javascript:none();" title="<?php echo $fullStat; ?>" style="text-decoration: none;">
88
- <img src='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/info-icon.png' ));?>' style="margin-bottom: -2px;"/>
89
  </a>&nbsp;<?php } echo($typ.$st.$err); ?>
90
 
91
  </td>
85
  <td>
86
  <?php if(!($st == "Empty")) { ?>
87
  <a href="javascript:none();" title="<?php echo $fullStat; ?>" style="text-decoration: none;">
88
+ <img src='<?php echo( wpSPIO()->plugin_url('res/img/info-icon.png' ));?>' style="margin-bottom: -2px;"/>
89
  </a>&nbsp;<?php } echo($typ.$st.$err); ?>
90
 
91
  </td>
class/view/settings/part-debug.php CHANGED
@@ -1,6 +1,8 @@
1
  <?php
2
  namespace ShortPixel;
3
 
 
 
4
  ?>
5
 
6
  <section id="tab-debug" <?php echo ($this->display_part == 'debug') ? ' class="sel-tab" ' :''; ?>>
1
  <?php
2
  namespace ShortPixel;
3
 
4
+ $path = '/var/www/shortpixel/wp-content/uploads/2019/09/';
5
+
6
  ?>
7
 
8
  <section id="tab-debug" <?php echo ($this->display_part == 'debug') ? ' class="sel-tab" ' :''; ?>>
class/view/settings/part-general.php CHANGED
@@ -159,12 +159,12 @@
159
  </p>
160
  <div style="margin-top: 10px;">
161
  <input type="radio" name="resizeType" id="resize_type_outer" value="outer" <?php echo($view->data->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
162
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?>"
163
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer@2x.png' ));?> 2x'
164
  title="<?php _e('Sizes will be greater or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 1000x1500px while an image of 3000x2000px will be resized to 1800x1200px','shortpixel-image-optimiser');?>">
165
  <input type="radio" name="resizeType" id="resize_type_inner" value="inner" <?php echo($view->data->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
166
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?>"
167
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner@2x.png' ));?> 2x'
168
  title="<?php _e('Sizes will be smaller or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 800x1200px while an image of 3000x2000px will be resized to 1000x667px','shortpixel-image-optimiser');?>">
169
  <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
170
  <span class="dashicons dashicons-editor-help"></span><?php _e('What is this?','shortpixel-image-optimiser');?></a>
159
  </p>
160
  <div style="margin-top: 10px;">
161
  <input type="radio" name="resizeType" id="resize_type_outer" value="outer" <?php echo($view->data->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
162
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/resize-outer.png' ));?>"
163
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/resize-outer.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/resize-outer@2x.png' ));?> 2x'
164
  title="<?php _e('Sizes will be greater or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 1000x1500px while an image of 3000x2000px will be resized to 1800x1200px','shortpixel-image-optimiser');?>">
165
  <input type="radio" name="resizeType" id="resize_type_inner" value="inner" <?php echo($view->data->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
166
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/resize-inner.png' ));?>"
167
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/resize-inner.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/resize-inner@2x.png' ));?> 2x'
168
  title="<?php _e('Sizes will be smaller or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 800x1200px while an image of 3000x2000px will be resized to 1000x667px','shortpixel-image-optimiser');?>">
169
  <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
170
  <span class="dashicons dashicons-editor-help"></span><?php _e('What is this?','shortpixel-image-optimiser');?></a>
class/view/settings/part-nokey.php CHANGED
@@ -64,8 +64,8 @@ if($adminEmail == 'noreply@addendio.com') $adminEmail = false; //hack for the ad
64
  }
65
  ?><br><span style="position:relative;">
66
  <input name="tos" type="checkbox" id="tos">
67
- <img id="tos-robo" src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>" style="position: absolute;left: -95px;bottom: -26px;display:none;">
68
- <img id="tos-hand" src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/point.png' ));?>" style="position: absolute;left: -39px;bottom: -9px;display:none;">
69
  </span>
70
  <?php _e('I have read and I agree to the <a href="https://shortpixel.com/tos" target="_blank">Terms of Service</a> and the <a href="https://shortpixel.com/privacy" target="_blank">Privacy Policy</a> (<a href="https://shortpixel.com/privacy#gdpr" target="_blank">GDPR compliant</a>).','shortpixel-image-optimiser');
71
  ?>
64
  }
65
  ?><br><span style="position:relative;">
66
  <input name="tos" type="checkbox" id="tos">
67
+ <img id="tos-robo" src="<?php echo(wpSPIO()->plugin_url('res/img/slider.png' ));?>" style="position: absolute;left: -95px;bottom: -26px;display:none;">
68
+ <img id="tos-hand" src="<?php echo(wpSPIO()->plugin_url('res/img/point.png' ));?>" style="position: absolute;left: -39px;bottom: -9px;display:none;">
69
  </span>
70
  <?php _e('I have read and I agree to the <a href="https://shortpixel.com/tos" target="_blank">Terms of Service</a> and the <a href="https://shortpixel.com/privacy" target="_blank">Privacy Policy</a> (<a href="https://shortpixel.com/privacy#gdpr" target="_blank">GDPR compliant</a>).','shortpixel-image-optimiser');
71
  ?>
class/view/shortpixel-list-table.php CHANGED
@@ -155,7 +155,7 @@ class ShortPixelListTable extends WP_List_Table {
155
  : __('Reduced by','shortpixel-image-optimiser') . " <strong>" . $item->message . "%</strong>"
156
  . (0 + intval($item->message) < 5 ? "<br>" . __('Bonus processing','shortpixel-image-optimiser') . "." : "");
157
  break;
158
- case 1: $msg = "<img src=\"" . plugins_url( 'shortpixel-image-optimiser/res/img/loading.gif') . "\" class='sp-loading-small'>&nbsp;"
159
  . __('Pending','shortpixel-image-optimiser');
160
  break;
161
  case 0: $msg = __('Waiting','shortpixel-image-optimiser');
155
  : __('Reduced by','shortpixel-image-optimiser') . " <strong>" . $item->message . "%</strong>"
156
  . (0 + intval($item->message) < 5 ? "<br>" . __('Bonus processing','shortpixel-image-optimiser') . "." : "");
157
  break;
158
+ case 1: $msg = "<img src=\"" . wpSPIO()->plugin_url('res/img/loading.gif') . "\" class='sp-loading-small'>&nbsp;"
159
  . __('Pending','shortpixel-image-optimiser');
160
  break;
161
  case 0: $msg = __('Waiting','shortpixel-image-optimiser');
class/view/shortpixel_view.php CHANGED
@@ -33,8 +33,8 @@ class ShortPixelView {
33
  </div>
34
  </div>
35
  <?php } ?>
36
- <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/robo-scared.png'));?>"
37
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-scared.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-scared@2x.png' ));?> 2x'
38
  class='short-pixel-notice-icon'>
39
  <h3><?php /* translators: header of the alert box */ _e('Quota Exceeded','shortpixel-image-optimiser');?></h3>
40
  <p><?php /* translators: body of the alert box */
@@ -118,8 +118,8 @@ class ShortPixelView {
118
  <?php }
119
  }
120
  if($icon){ ?>
121
- <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/robo-' . $icon . '.png'));?>"
122
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '@2x.png' ));?> 2x'
123
  class='short-pixel-notice-icon'>
124
  <?php } ?>
125
  <h3><?php _e('ShortPixel Image Optimizer','shortpixel-image-optimiser');
@@ -212,7 +212,6 @@ class ShortPixelView {
212
  //$this->ctrl->outputHSBeacon();
213
  \ShortPixel\HelpScout::outputBeacon($this->ctrl->getApiKey());
214
 
215
-
216
  $this->bulkType = $this->ctrl->getPrioQ()->getBulkTypeForDisplay(); // adding to the mess
217
  $hider = ($this->bulkType == ShortPixelQueue::BULK_TYPE_RESTORE) ? 'sp-hidden' : '';
218
  ?>
@@ -234,6 +233,14 @@ class ShortPixelView {
234
  <div style='width:165px; display:inline-block; padding-left: 5px'>
235
  <input type='checkbox' id='thumbnails' name='thumbnails' onclick='ShortPixel.checkThumbsUpdTotal(this)' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>>
236
  <?php _e('Include thumbnails','shortpixel-image-optimiser');?>
 
 
 
 
 
 
 
 
237
  </div><br>
238
  <?php if($quotaData["totalProcessedMlFiles"] > 0) { ?>
239
  <div class="bulk-label bulk-total"><?php _e('Total images','shortpixel-image-optimiser');?></div>
@@ -266,8 +273,8 @@ class ShortPixelView {
266
  : "onclick=\"document.getElementById('startBulk').submit();\""); ?> class='button'>
267
  <div style="width: 320px">
268
  <div class="bulk-btn-img" class="bulk-btn-img">
269
- <img src='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-slider.png' ));?>'
270
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-slider.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-slider@2x.png' ));?> 2x'/>
271
  </div>
272
  <div class="bulk-btn-txt">
273
  <?php printf(__('<span class="label">Start Optimizing</span><br> <span class="total">%s</span> images','shortpixel-image-optimiser'),
@@ -276,7 +283,7 @@ class ShortPixelView {
276
  number_format(max(0, $quotaData['mainMlFiles'] - $quotaData['mainProcessedMlFiles']) + $customCount));?>
277
  </div>
278
  <div class="bulk-btn-img" class="bulk-btn-img">
279
- <img src='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/arrow.png' ));?>'/>
280
  </div>
281
  </div>
282
  </a>
@@ -344,8 +351,8 @@ class ShortPixelView {
344
  <div class="sp-container">
345
  <div class='sp-notice sp-notice-success sp-floating-block sp-single-width' style="height: 80px;overflow:hidden;">
346
  <div style='float:left;margin:5px 20px 5px 0'>
347
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>"
348
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider@2x.png' ));?> 2x'>
349
  </div>
350
  <div class="sp-bulk-summary <?php echo $hider ?>">
351
  <input type="text" value="<?php echo("" . round($averageCompression))?>" id="sp-total-optimization-dial" class="dial">
@@ -420,7 +427,7 @@ class ShortPixelView {
420
  <a href="https://wordpress.org/support/view/plugin-reviews/shortpixel-image-optimiser?rate=5#postform" target="_blank">
421
  <span>
422
  <?php _e('Please rate us!','shortpixel-image-optimiser');?>&nbsp;
423
- </span><br><img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/stars.png' ));?>">
424
  </a>
425
  </div>
426
  <?php } ?>
@@ -556,6 +563,15 @@ class ShortPixelView {
556
  <input type='checkbox' id='bulk-thumbnails' name='thumbnails' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>
557
  onchange="ShortPixel.onBulkThumbsCheck(this)"> <?php _e('Include thumbnails','shortpixel-image-optimiser');?><br><br>
558
 
 
 
 
 
 
 
 
 
 
559
  <a class='button' style="float: right;" href='<?php echo add_query_arg('part','bulk-restore-all'); ?> '><?php _e('Bulk Restore Images','shortpixel-image-optimiser'); ?></a>
560
 
561
  <input type='submit' name='bulkProcess' id='bulkProcess' class='button button-primary' value='<?php _e('Restart Optimizing','shortpixel-image-optimiser');?>'
@@ -588,7 +604,7 @@ class ShortPixelView {
588
  <div id="short-pixel-notice-squirrly" class="sp-notice sp-notice-info bulk-progress bulk-progress-partners sp-floating-block sp-full-width">
589
  <div style="float:right"><a href="javascript:dismissShortPixelNotice('squirrly')"><?php _e('Dismiss','shortpixel-image-optimiser');?></a></div>
590
  <a href="https://my.squirrly.co/go120073/squirrly.co/short-pixel-seo" target="_blank">
591
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/squirrly.png' ));?>" height="50">
592
  <div><?php _e('While you wait for your images to optimize, check out Squirrly, a great plugin for further boosting your SEO.','shortpixel-image-optimiser');?></div>
593
  </a>
594
  </div>
@@ -597,28 +613,28 @@ class ShortPixelView {
597
  <div class="sp-floating-block sp-notice bulk-notices-parent">
598
  <div class="bulk-notice-container">
599
  <div class="bulk-notice-msg bulk-lengthy">
600
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/loading-dark-big.gif' ));?>">
601
  <?php _e('Lengthy operation in progress:','shortpixel-image-optimiser');?><br>
602
  <?php _e('Optimizing image','shortpixel-image-optimiser');?> <a href="#" data-href="<?php echo(get_admin_url());?>/post.php?post=__ID__&action=edit" target="_blank">placeholder.png</a>
603
  </div>
604
  <div class="bulk-notice-msg bulk-maintenance">
605
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/loading-dark-big.gif' ));?>">
606
  <?php _e("The ShortPixel API is in maintenance mode. Please don't close this window. The bulk will resume automatically as soon as the API is back online.",'shortpixel-image-optimiser');?>
607
  </div>
608
  <div class="bulk-notice-msg bulk-queue-full">
609
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/loading-dark-big.gif' ));?>">
610
  <?php _e("Too many images processing simultaneously for your site, automatically retrying in 1 min. Please don't close this window.",'shortpixel-image-optimiser');?>
611
  </div>
612
  <div class="bulk-notice-msg bulk-error" id="bulk-error-template">
613
  <div style="float: right; margin-top: -4px; margin-right: -3px;">
614
  <a href="javascript:void(0);" onclick="ShortPixel.removeBulkMsg(this)" style='color: #c32525;font-size: 20px;text-decoration: none;'>&times;</a>
615
  </div>
616
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/exclamation-big.png' ));?>">
617
  <span class="sp-err-title"><?php _e('Error processing file:','shortpixel-image-optimiser');?><br></span>
618
  <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>
619
  </div>
620
  <div class="bulk-notice-msg bulk-searching">
621
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/loading-dark-big.gif' ));?>">
622
  <?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>
623
  </div>
624
  </div>
@@ -713,8 +729,8 @@ class ShortPixelView {
713
  <?php }?>
714
  <div id="bulk-progress" class="progress" >
715
  <div class="progress-img" style="left: <?php echo($percent);?>%;">
716
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>"
717
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider@2x.png' ));?> 2x'>
718
  <span><?php echo($percentAfter);?></span>
719
  </div>
720
  <div class="progress-left" style="width: <?php echo($percent);?>%"><?php echo($percentBefore);?></div>
@@ -794,8 +810,8 @@ class ShortPixelView {
794
  ?>
795
  <br/>
796
  <div class="clearfix <?php echo($extraClass);?>" style="background-color: #fff; border-left-style: solid; border-left-width: 4px; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1); padding: 1px 12px;;width: 95%">
797
- <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/robo-' . $icon . '.png'));?>"
798
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/robo-' . $icon . '@2x.png' ));?> 2x'
799
  class='short-pixel-notice-icon'>
800
  <p><?php echo($notice['msg']);?></p>
801
  </div>
@@ -900,8 +916,8 @@ class ShortPixelView {
900
  }
901
  ?><br><span style="position:relative;">
902
  <input name="tos" type="checkbox" id="tos">
903
- <img id="tos-robo" src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/slider.png' ));?>" style="position: absolute;left: -95px;bottom: -26px;display:none;">
904
- <img id="tos-hand" src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/point.png' ));?>" style="position: absolute;left: -39px;bottom: -9px;display:none;">
905
  </span>
906
  <?php _e('I have read and I agree to the <a href="https://shortpixel.com/tos" target="_blank">Terms of Service</a> and the <a href="https://shortpixel.com/privacy" target="_blank">Privacy Policy</a> (<a href="https://shortpixel.com/privacy#gdpr" target="_blank">GDPR compliant</a>).','shortpixel-image-optimiser');
907
  ?>
@@ -1049,12 +1065,12 @@ class ShortPixelView {
1049
  </p>
1050
  <div style="margin-top: 10px;">
1051
  <input type="radio" name="resize_type" id="resize_type_outer" value="outer" <?php echo($settings->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
1052
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?>"
1053
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-outer@2x.png' ));?> 2x'
1054
  title="<?php _e('Sizes will be greater or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 1000x1500px while an image of 3000x2000px will be resized to 1800x1200px','shortpixel-image-optimiser');?>">
1055
  <input type="radio" name="resize_type" id="resize_type_inner" value="inner" <?php echo($settings->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
1056
- <img src="<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?>"
1057
- srcset='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner.png' ));?> 1x, <?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/resize-inner@2x.png' ));?> 2x'
1058
  title="<?php _e('Sizes will be smaller or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 800x1200px while an image of 3000x2000px will be resized to 1000x667px','shortpixel-image-optimiser');?>">
1059
  <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
1060
  <span class="dashicons dashicons-editor-help"></span><?php _e('What is this?','shortpixel-image-optimiser');?></a>
@@ -1188,7 +1204,7 @@ class ShortPixelView {
1188
  <td>
1189
  <?php if(!($st == "Empty")) { ?>
1190
  <a href="javascript:none();" title="<?php echo $fullStat; ?>" style="text-decoration: none;">
1191
- <img src='<?php echo(plugins_url( 'shortpixel-image-optimiser/res/img/info-icon.png' ));?>' style="margin-bottom: -2px;"/>
1192
  </a>&nbsp;<?php } echo($typ.$st.$err); ?>
1193
 
1194
  </td>
33
  </div>
34
  </div>
35
  <?php } ?>
36
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/robo-scared.png'));?>"
37
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/robo-scared.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/robo-scared@2x.png' ));?> 2x'
38
  class='short-pixel-notice-icon'>
39
  <h3><?php /* translators: header of the alert box */ _e('Quota Exceeded','shortpixel-image-optimiser');?></h3>
40
  <p><?php /* translators: body of the alert box */
118
  <?php }
119
  }
120
  if($icon){ ?>
121
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/robo-' . $icon . '.png'));?>"
122
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/robo-' . $icon . '.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/robo-' . $icon . '@2x.png' ));?> 2x'
123
  class='short-pixel-notice-icon'>
124
  <?php } ?>
125
  <h3><?php _e('ShortPixel Image Optimizer','shortpixel-image-optimiser');
212
  //$this->ctrl->outputHSBeacon();
213
  \ShortPixel\HelpScout::outputBeacon($this->ctrl->getApiKey());
214
 
 
215
  $this->bulkType = $this->ctrl->getPrioQ()->getBulkTypeForDisplay(); // adding to the mess
216
  $hider = ($this->bulkType == ShortPixelQueue::BULK_TYPE_RESTORE) ? 'sp-hidden' : '';
217
  ?>
233
  <div style='width:165px; display:inline-block; padding-left: 5px'>
234
  <input type='checkbox' id='thumbnails' name='thumbnails' onclick='ShortPixel.checkThumbsUpdTotal(this)' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>>
235
  <?php _e('Include thumbnails','shortpixel-image-optimiser');?>
236
+ </div><br><br>
237
+ <div>
238
+
239
+ <input name="createWebp" type="checkbox" id="createWebp" value="1" <?php checked( $settings->createWebp, "1" );?> >
240
+ <label for="createWebp">
241
+ <?php _e('Also create <a href="http://blog.shortpixel.com/how-webp-images-can-speed-up-your-site/" target="_blank">WebP versions</a> of the images, <strong>for free</strong>.','shortpixel-image-optimiser');?>
242
+ </label>
243
+
244
  </div><br>
245
  <?php if($quotaData["totalProcessedMlFiles"] > 0) { ?>
246
  <div class="bulk-label bulk-total"><?php _e('Total images','shortpixel-image-optimiser');?></div>
273
  : "onclick=\"document.getElementById('startBulk').submit();\""); ?> class='button'>
274
  <div style="width: 320px">
275
  <div class="bulk-btn-img" class="bulk-btn-img">
276
+ <img src='<?php echo(wpSPIO()->plugin_url('res/img/robo-slider.png' ));?>'
277
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/robo-slider.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/robo-slider@2x.png' ));?> 2x'/>
278
  </div>
279
  <div class="bulk-btn-txt">
280
  <?php printf(__('<span class="label">Start Optimizing</span><br> <span class="total">%s</span> images','shortpixel-image-optimiser'),
283
  number_format(max(0, $quotaData['mainMlFiles'] - $quotaData['mainProcessedMlFiles']) + $customCount));?>
284
  </div>
285
  <div class="bulk-btn-img" class="bulk-btn-img">
286
+ <img src='<?php echo(wpSPIO()->plugin_url('res/img/arrow.png' ));?>'/>
287
  </div>
288
  </div>
289
  </a>
351
  <div class="sp-container">
352
  <div class='sp-notice sp-notice-success sp-floating-block sp-single-width' style="height: 80px;overflow:hidden;">
353
  <div style='float:left;margin:5px 20px 5px 0'>
354
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/slider.png' ));?>"
355
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/slider.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/slider@2x.png' ));?> 2x'>
356
  </div>
357
  <div class="sp-bulk-summary <?php echo $hider ?>">
358
  <input type="text" value="<?php echo("" . round($averageCompression))?>" id="sp-total-optimization-dial" class="dial">
427
  <a href="https://wordpress.org/support/view/plugin-reviews/shortpixel-image-optimiser?rate=5#postform" target="_blank">
428
  <span>
429
  <?php _e('Please rate us!','shortpixel-image-optimiser');?>&nbsp;
430
+ </span><br><img src="<?php echo(wpSPIO()->plugin_url('res/img/stars.png' ));?>">
431
  </a>
432
  </div>
433
  <?php } ?>
563
  <input type='checkbox' id='bulk-thumbnails' name='thumbnails' <?php echo($this->ctrl->processThumbnails() ? "checked":"");?>
564
  onchange="ShortPixel.onBulkThumbsCheck(this)"> <?php _e('Include thumbnails','shortpixel-image-optimiser');?><br><br>
565
 
566
+ <div>
567
+
568
+ <input name="createWebp" type="checkbox" id="createWebp" value="1" <?php checked( $settings->createWebp, "1" );?> >
569
+ <label for="createWebp">
570
+ <?php _e('Also create <a href="http://blog.shortpixel.com/how-webp-images-can-speed-up-your-site/" target="_blank">WebP versions</a> of the images, <strong>for free</strong>.','shortpixel-image-optimiser');?>
571
+ </label>
572
+
573
+ </div><br>
574
+
575
  <a class='button' style="float: right;" href='<?php echo add_query_arg('part','bulk-restore-all'); ?> '><?php _e('Bulk Restore Images','shortpixel-image-optimiser'); ?></a>
576
 
577
  <input type='submit' name='bulkProcess' id='bulkProcess' class='button button-primary' value='<?php _e('Restart Optimizing','shortpixel-image-optimiser');?>'
604
  <div id="short-pixel-notice-squirrly" class="sp-notice sp-notice-info bulk-progress bulk-progress-partners sp-floating-block sp-full-width">
605
  <div style="float:right"><a href="javascript:dismissShortPixelNotice('squirrly')"><?php _e('Dismiss','shortpixel-image-optimiser');?></a></div>
606
  <a href="https://my.squirrly.co/go120073/squirrly.co/short-pixel-seo" target="_blank">
607
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/squirrly.png' ));?>" height="50">
608
  <div><?php _e('While you wait for your images to optimize, check out Squirrly, a great plugin for further boosting your SEO.','shortpixel-image-optimiser');?></div>
609
  </a>
610
  </div>
613
  <div class="sp-floating-block sp-notice bulk-notices-parent">
614
  <div class="bulk-notice-container">
615
  <div class="bulk-notice-msg bulk-lengthy">
616
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/loading-dark-big.gif' ));?>">
617
  <?php _e('Lengthy operation in progress:','shortpixel-image-optimiser');?><br>
618
  <?php _e('Optimizing image','shortpixel-image-optimiser');?> <a href="#" data-href="<?php echo(get_admin_url());?>/post.php?post=__ID__&action=edit" target="_blank">placeholder.png</a>
619
  </div>
620
  <div class="bulk-notice-msg bulk-maintenance">
621
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/loading-dark-big.gif' ));?>">
622
  <?php _e("The ShortPixel API is in maintenance mode. Please don't close this window. The bulk will resume automatically as soon as the API is back online.",'shortpixel-image-optimiser');?>
623
  </div>
624
  <div class="bulk-notice-msg bulk-queue-full">
625
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/loading-dark-big.gif' ));?>">
626
  <?php _e("Too many images processing simultaneously for your site, automatically retrying in 1 min. Please don't close this window.",'shortpixel-image-optimiser');?>
627
  </div>
628
  <div class="bulk-notice-msg bulk-error" id="bulk-error-template">
629
  <div style="float: right; margin-top: -4px; margin-right: -3px;">
630
  <a href="javascript:void(0);" onclick="ShortPixel.removeBulkMsg(this)" style='color: #c32525;font-size: 20px;text-decoration: none;'>&times;</a>
631
  </div>
632
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/exclamation-big.png' ));?>">
633
  <span class="sp-err-title"><?php _e('Error processing file:','shortpixel-image-optimiser');?><br></span>
634
  <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>
635
  </div>
636
  <div class="bulk-notice-msg bulk-searching">
637
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/loading-dark-big.gif' ));?>">
638
  <?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>
639
  </div>
640
  </div>
729
  <?php }?>
730
  <div id="bulk-progress" class="progress" >
731
  <div class="progress-img" style="left: <?php echo($percent);?>%;">
732
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/slider.png' ));?>"
733
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/slider.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/slider@2x.png' ));?> 2x'>
734
  <span><?php echo($percentAfter);?></span>
735
  </div>
736
  <div class="progress-left" style="width: <?php echo($percent);?>%"><?php echo($percentBefore);?></div>
810
  ?>
811
  <br/>
812
  <div class="clearfix <?php echo($extraClass);?>" style="background-color: #fff; border-left-style: solid; border-left-width: 4px; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1); padding: 1px 12px;;width: 95%">
813
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/robo-' . $icon . '.png'));?>"
814
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/robo-' . $icon . '.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/robo-' . $icon . '@2x.png' ));?> 2x'
815
  class='short-pixel-notice-icon'>
816
  <p><?php echo($notice['msg']);?></p>
817
  </div>
916
  }
917
  ?><br><span style="position:relative;">
918
  <input name="tos" type="checkbox" id="tos">
919
+ <img id="tos-robo" src="<?php echo(wpSPIO()->plugin_url('res/img/slider.png' ));?>" style="position: absolute;left: -95px;bottom: -26px;display:none;">
920
+ <img id="tos-hand" src="<?php echo(wpSPIO()->plugin_url('res/img/point.png' ));?>" style="position: absolute;left: -39px;bottom: -9px;display:none;">
921
  </span>
922
  <?php _e('I have read and I agree to the <a href="https://shortpixel.com/tos" target="_blank">Terms of Service</a> and the <a href="https://shortpixel.com/privacy" target="_blank">Privacy Policy</a> (<a href="https://shortpixel.com/privacy#gdpr" target="_blank">GDPR compliant</a>).','shortpixel-image-optimiser');
923
  ?>
1065
  </p>
1066
  <div style="margin-top: 10px;">
1067
  <input type="radio" name="resize_type" id="resize_type_outer" value="outer" <?php echo($settings->resizeType == 'inner' ? '' : 'checked') ?> style="margin: -50px 10px 60px 0;">
1068
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/resize-outer.png' ));?>"
1069
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/resize-outer.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/resize-outer@2x.png' ));?> 2x'
1070
  title="<?php _e('Sizes will be greater or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 1000x1500px while an image of 3000x2000px will be resized to 1800x1200px','shortpixel-image-optimiser');?>">
1071
  <input type="radio" name="resize_type" id="resize_type_inner" value="inner" <?php echo($settings->resizeType == 'inner' ? 'checked' : '') ?> style="margin: -50px 10px 60px 35px;">
1072
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/resize-inner.png' ));?>"
1073
+ srcset='<?php echo(wpSPIO()->plugin_url('res/img/resize-inner.png' ));?> 1x, <?php echo(wpSPIO()->plugin_url('res/img/resize-inner@2x.png' ));?> 2x'
1074
  title="<?php _e('Sizes will be smaller or equal to the corresponding value. For example, if you set the resize dimensions at 1000x1200, an image of 2000x3000px will be resized to 800x1200px while an image of 3000x2000px will be resized to 1000x667px','shortpixel-image-optimiser');?>">
1075
  <div style="display:inline-block;margin-left: 20px;"><a href="https://blog.shortpixel.com/resize-images/" class="shortpixel-help-link" target="_blank">
1076
  <span class="dashicons dashicons-editor-help"></span><?php _e('What is this?','shortpixel-image-optimiser');?></a>
1204
  <td>
1205
  <?php if(!($st == "Empty")) { ?>
1206
  <a href="javascript:none();" title="<?php echo $fullStat; ?>" style="text-decoration: none;">
1207
+ <img src='<?php echo(wpSPIO()->plugin_url('res/img/info-icon.png' ));?>' style="margin-bottom: -2px;"/>
1208
  </a>&nbsp;<?php } echo($typ.$st.$err); ?>
1209
 
1210
  </td>
class/wp-short-pixel.php CHANGED
@@ -22,6 +22,8 @@ class WPShortPixel {
22
 
23
  public static $PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'gif', 'png', 'pdf');
24
 
 
 
25
  public function __construct() {
26
  $this->timer = time();
27
 
@@ -37,11 +39,33 @@ class WPShortPixel {
37
  $this->_settings = new WPShortPixelSettings();
38
  $this->_apiInterface = new ShortPixelAPI($this->_settings);
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
 
@@ -148,36 +172,18 @@ class WPShortPixel {
148
  add_action( 'admin_footer', array($this, 'admin_footer_js') );
149
  add_action( 'admin_head', array( $this, 'headCSS') );
150
 
151
- if($this->_settings->frontBootstrap && shortPixelCheckQueue()) {
152
- //only if we have something in the queue - usually we never get here if the queue is empty but for some hooks...
153
- //also need to have it in the front footer then
154
- add_action( 'wp_footer', array( &$this, 'shortPixelJS') );
155
- //need to add the nopriv action for when items exist in the queue and no user is logged in
156
- add_action( 'wp_ajax_nopriv_shortpixel_image_processing', array( &$this, 'handleImageProcessing') );
157
- }
158
  //register a method to display admin notices if necessary
159
  add_action('admin_notices', array( &$this, 'displayAdminNotices'));
160
 
161
  $this->migrateBackupFolder();
162
-
163
-
164
- // only load backed, or when frontend processing is enabled.
165
- if (is_admin() || $this->_settings->frontBootstrap )
166
- {
167
- $keyControl = new \ShortPixel\apiKeyController();
168
- $keyControl->setShortPixel($this);
169
- $keyControl->load();
170
- }
171
  }
172
 
173
- //handling older
174
- public function WPShortPixel() {
175
- $this->__construct();
176
- }
177
 
178
  // @hook admin menu
179
  // @todo move to plugin class
180
  function registerAdminPage( ) {
 
181
  if($this->spMetaDao->hasFoldersTable() && count($this->spMetaDao->getFolders())) {
182
  /*translators: title and menu name for the Other media page*/
183
  add_media_page( __('Other Media Optimized by ShortPixel','shortpixel-image-optimiser'), __('Other Media','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-custom', array( &$this, 'listCustomMedia' ) );
@@ -186,7 +192,7 @@ class WPShortPixel {
186
  add_media_page( __('ShortPixel Bulk Process','shortpixel-image-optimiser'), __('Bulk ShortPixel','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcess' ) );
187
  }
188
 
189
- public static function shortPixelActivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
190
  {
191
  self::shortPixelDeactivatePlugin();
192
  if(SHORTPIXEL_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
@@ -205,9 +211,9 @@ class WPShortPixel {
205
  self::alterHtaccess(); //add the htaccess lines
206
  }
207
  WPShortPixelSettings::onActivate();
208
- }
209
 
210
- public static function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
211
  {
212
  ShortPixelQueue::resetBulk();
213
  (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::resetPrio() : ShortPixelQueueDB::resetPrio();
@@ -220,16 +226,16 @@ class WPShortPixel {
220
  self::alterHtaccess(true);
221
 
222
  @unlink(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
223
- }
224
 
225
- public static function shortPixelUninstallPlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
226
  {
227
  $settings = new WPShortPixelSettings();
228
  if($settings->removeSettingsOnDeletePlugin == 1) {
229
  WPShortPixelSettings::debugResetOptions();
230
  insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
231
  }
232
- }
233
 
234
  public function getConflictingPlugins() {
235
  $conflictPlugins = array(
@@ -333,6 +339,15 @@ class WPShortPixel {
333
  )
334
  ));
335
  }
 
 
 
 
 
 
 
 
 
336
  $found = array();
337
  foreach($conflictPlugins as $name => $path) {
338
  $action = ( isset($path['action']) ) ? $path['action'] : null;
@@ -481,13 +496,15 @@ class WPShortPixel {
481
  /** @todo Plugin init class. Try to get rid of inline JS. Also still loads on all WP pages, prevent that. */
482
  function shortPixelJS() {
483
 
 
 
 
484
  //require_once(ABSPATH . 'wp-admin/includes/screen.php');
485
  if(function_exists('get_current_screen')) {
486
  $screen = get_current_screen();
487
 
488
  if(is_object($screen)) {
489
 
490
-
491
  wp_enqueue_style('short-pixel-bar.min.css', plugins_url('/res/css/short-pixel-bar.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
492
  if( in_array($screen->id, array('attachment', 'upload', 'settings_page_wp-shortpixel', 'media_page_wp-short-pixel-bulk', 'media_page_wp-short-pixel-custom'))) {
493
  wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
@@ -580,6 +597,9 @@ class WPShortPixel {
580
  * @todo Find a better solution for this */
581
  public function admin_footer_js()
582
  {
 
 
 
583
  if(function_exists('get_current_screen')) {
584
  $screen = get_current_screen();
585
  if(is_object($screen)) {
@@ -759,8 +779,12 @@ class WPShortPixel {
759
  return $meta;
760
  }
761
 
 
 
762
  // some plugins (e.g. WP e-Commerce) call the wp_attachment_metadata on just editing the image...
763
  $dbMeta = wp_get_attachment_metadata($ID);
 
 
764
  $refresh = false;
765
 
766
  if(isset($dbMeta['ShortPixelImprovement'])) {
@@ -774,11 +798,11 @@ class WPShortPixel {
774
  self::log("Handle Media Library Image Upload #{$ID}");
775
  //self::log("STACK: " . json_encode(debug_backtrace()));
776
 
777
- if(!$this->_settings->optimizePdfs && 'pdf' === pathinfo(get_attached_file($ID), PATHINFO_EXTENSION)) {
778
  //pdf is not optimized automatically as per the option, but can be optimized by button. Nothing to do.
779
  return $meta;
780
  }
781
- elseif(!get_attached_file($ID) && isset($meta['file']) && in_array(strtolower(pathinfo($meta['file'], PATHINFO_EXTENSION)), self::$PROCESSABLE_EXTENSIONS)) {
782
  //in some rare cases (images added from the front-end) it's an image but get_attached_file returns null (the record is not yet saved in the DB)
783
  //in this case add it to the queue nevertheless
784
  $this->prioQ->push($ID);
@@ -880,7 +904,8 @@ class WPShortPixel {
880
  return $converter->checkConvertMediaPng2Jpg($itemHandler);
881
  }
882
 
883
- public function handleGravityFormsImageField($value) {
 
884
  if(!($folder = $this->spMetaDao->getFolder(SHORTPIXEL_UPLOADS_BASE . '/gravity_forms'))) {
885
  return;
886
  }
@@ -893,7 +918,7 @@ class WPShortPixel {
893
  $localPath = str_replace($uploadDir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $value);
894
 
895
  return $this->addPathToCustomFolder($localPath, $folder->getId(), 0);
896
- }
897
 
898
  /**
899
  * this is hooked onto the NextGen upload
@@ -1093,6 +1118,7 @@ class WPShortPixel {
1093
  $maxTime = min(SHORTPIXEL_MAX_EXECUTION_TIME, 90);
1094
  $timeoutThreshold = 5; // will adapt this with the maximum time needed for one pass
1095
  $passTime = time();
 
1096
  for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
1097
  ($crtStartQueryID >= $endQueryID) && (count($itemList) < SHORTPIXEL_PRESEND_ITEMS) && ($sanityCheck < 150)
1098
  && (time() - $this->timer < $maxTime - $timeoutThreshold); $sanityCheck++) {
@@ -1195,7 +1221,7 @@ class WPShortPixel {
1195
  )
1196
  ) {
1197
 
1198
- $changes = $this->addUnlistedThumbs($item); // search for unlisted thumbs, if that is the setting.
1199
  $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
1200
  Log::addDebug('Gathering URLS AND PATHS', array($URLsAndPATHs));
1201
  if(count($URLsAndPATHs["URLs"])) {
@@ -1348,7 +1374,7 @@ class WPShortPixel {
1348
  { //take from custom images if any left to optimize - only if bulk was ever started
1349
  //but first refresh if it wasn't refreshed in the last hour
1350
  if(time() - $this->_settings->hasCustomFolders > 3600) {
1351
- $notice = null; $this->refreshCustomFolders($notice);
1352
  $this->_settings->hasCustomFolders = time();
1353
  }
1354
 
@@ -1458,7 +1484,7 @@ class WPShortPixel {
1458
  $sizes = $meta->getThumbs();
1459
  if('pdf' == strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))) {
1460
  // echo($result["Filename"] . " ESTE --> "); die(var_dump(strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))));
1461
- $thumb = plugins_url( 'shortpixel-image-optimiser/res/img/logo-pdf.png' );
1462
  $bkThumb = '';
1463
  } else {
1464
  if(count($sizes)) {
@@ -1487,7 +1513,6 @@ class WPShortPixel {
1487
  //$backupUrl = content_url() . "/" . SHORTPIXEL_UPLOADS_NAME . "/" . SHORTPIXEL_BACKUP . "/";
1488
  //or even better:
1489
  $backupUrl = SHORTPIXEL_BACKUP_URL . "/";
1490
- //$urlBkPath = $this->_apiInterface->returnSubDir(get_attached_file($ID));
1491
  $urlBkPath = ShortPixelMetaFacade::returnSubDir($meta->getPath(), ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE);
1492
  $bkThumb = $backupUrl . $urlBkPath . $thumb;
1493
  }
@@ -1645,7 +1670,7 @@ class WPShortPixel {
1645
  $meta = $itemHandler->getMeta();
1646
 
1647
  Log::addDebug('Finding Thumbs on path' . $meta->getPath());
1648
- $thumbs = WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath());
1649
 
1650
  $fs = new \ShortPixel\FileSystemController();
1651
  $mainFile = $fs->getFile($meta->getPath());
@@ -1729,6 +1754,7 @@ class WPShortPixel {
1729
 
1730
  private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false) {
1731
  //conversion of PNG 2 JPG for existing images
 
1732
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) { //currently only for ML
1733
  $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
1734
 
@@ -1741,9 +1767,18 @@ class WPShortPixel {
1741
  $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
1742
  Log::addDebug('Send to PRocessing - URLS -', array($URLsAndPATHs) );
1743
 
 
 
 
 
 
 
 
 
 
 
1744
  //find thumbs that are not listed in the metadata and add them in the sizes array
1745
- $this->addUnlistedThumbs($itemHandler);
1746
- $meta = $itemHandler->getMeta();
1747
 
1748
  //find any missing thumbs files and mark them as such
1749
  $miss = $meta->getThumbsMissing();
@@ -1775,8 +1810,8 @@ class WPShortPixel {
1775
  }
1776
  }
1777
 
1778
- $thumbObtList = $meta->getThumbsOptList();
1779
- $missing = $meta->getThumbsMissing();
1780
 
1781
 
1782
  $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $itemHandler,
@@ -1839,14 +1874,15 @@ class WPShortPixel {
1839
  $this->prioQ->push($imageId);
1840
  $itemHandler = new ShortPixelMetaFacade($imageId);
1841
 
1842
- $path = get_attached_file($imageId);//get the full file PATH
1843
- if(!$manual && 'pdf' === pathinfo($path, PATHINFO_EXTENSION) && !$this->_settings->optimizePdfs) {
 
1844
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => $imageId);
1845
  } else {
1846
  try {
1847
  $this->sendToProcessing($itemHandler, false, $itemHandler->getMeta()->getThumbsTodo());
1848
  $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
1849
- } catch(Exception $e) { // Exception("Post metadata is corrupt (No attachment URL)")
1850
  $itemHandler->getMeta();
1851
  $errCode = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::ERR_FILE_NOT_FOUND;
1852
  $itemHandler->setError($errCode, $e->getMessage());
@@ -2045,15 +2081,19 @@ class WPShortPixel {
2045
  protected function doRestore($attachmentID, $rawMeta = null) {
2046
  do_action("shortpixel_before_restore_image", $attachmentID);
2047
 
2048
- $fs = new \ShortPixel\FileSystemController();
2049
- $origFile = get_attached_file($attachmentID);
2050
- // $file = get_attached_file($attachmentID);
2051
 
2052
  // Setup Original File and Data. This is used to determine backup path.
2053
- $fsFile = $fs->getFile($origFile);
 
 
 
 
 
 
2054
  $filePath = (string) $fsFile->getFileDir();
2055
 
2056
- $itemHandler = new ShortPixelMetaFacade($attachmentID);
2057
  if($rawMeta) {
2058
  $itemHandler->setRawMeta($rawMeta); //prevent another database trip
2059
  } else {
@@ -2068,6 +2108,9 @@ class WPShortPixel {
2068
  return false;
2069
  }
2070
 
 
 
 
2071
  // Get correct Backup Folder and file. .
2072
  $sizes = isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array();
2073
  $bkFolder = $fs->getDirectory($this->getBackupFolderAny($fsFile->getFullPath(), $sizes));
@@ -2097,14 +2140,13 @@ class WPShortPixel {
2097
 
2098
  // find the jpg optimized image in backups, and mark to remove
2099
  if ($bkFile->exists())
2100
- $toUnlink['PATHs'][] = $bkFile->getFullPath();
2101
 
2102
  // $baseUrl = ShortPixelPng2Jpg::removeUrlProtocol(trailingslashit(str_replace($image, "", $imageUrl))); //make the base url protocol agnostic if it's not already
2103
 
2104
  // not needed, we don't do this weird remove anymore.
2105
  $baseRelPath = ''; // trailingslashit(dirname($image)); // @todo Replace this (string) $fsFile->getFileDir();
2106
 
2107
-
2108
  $toReplace[ShortPixelPng2Jpg::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($png2jpgMain);
2109
  foreach($sizes as $key => $size) {
2110
  if(isset($png2jpgSizes[$key])) {
@@ -2112,7 +2154,7 @@ class WPShortPixel {
2112
  }
2113
 
2114
  $backuppedSize = $fs->getFile($backupFileDir . $size['file'] );
2115
- Log::addDebug('Find optimized JPGEG backupFile Thing', array( $backuppedSize->getFullPath() ));
2116
  if ($backuppedSize->exists())
2117
  {
2118
  $toUnlink['PATHs'][] = $backuppedSize ->getFullPath();
@@ -2164,7 +2206,9 @@ class WPShortPixel {
2164
  return false;
2165
  }
2166
  $bkCount++;
2167
- $thumbsPaths[] = array('source' => $source, 'destination' => $destination);
 
 
2168
  }
2169
  }
2170
  if(!$bkCount) {
@@ -2183,11 +2227,22 @@ class WPShortPixel {
2183
  if($bkCount) { // backups, if exist
2184
  //main file
2185
  if($main) {
 
 
 
 
 
 
 
 
 
 
2186
  //$this->renameWithRetina($bkFile, $file);
2187
  if (! $bkFile->move($fsFile))
2188
  {
2189
  Log::addError('DoRestore failed restoring backup', array($bkFile->getFullPath(), $fsFile->getFullPath() ));
2190
  }
 
2191
  $retinaBK = $fs->getFile( $bkFile->getFileDir()->getPath() . $bkFile->getFileBase() . '@2x' . $bkFile->getExtension() );
2192
  if ($retinaBK->exists())
2193
  {
@@ -2229,7 +2284,14 @@ class WPShortPixel {
2229
  $duplicates = ShortPixelMetaFacade::getWPMLDuplicates($attachmentID);
2230
  foreach($duplicates as $ID) {
2231
  //Added sanitizeMeta (improved with @unserialize) as per https://secure.helpscout.net/conversation/725053586/11656?folderId=1117588
2232
- $crtMeta = $attachmentID == $ID ? $rawMeta : ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($ID));
 
 
 
 
 
 
 
2233
  if(isset($crtMeta['previous_meta'])) continue;
2234
  if( isset($crtMeta["ShortPixelImprovement"]) && is_numeric($crtMeta["ShortPixelImprovement"])
2235
  && 0 + $crtMeta["ShortPixelImprovement"] < 5 && $this->_settings->under5Percent > 0) {
@@ -2251,36 +2313,59 @@ class WPShortPixel {
2251
  $crtMeta['sizes'] = $png2jpgSizes;
2252
  } else {
2253
  //this was an image converted on upload, regenerate the thumbs using the PNG main image BUT deactivate temporarily the filter!!
2254
- remove_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook');
 
 
 
 
 
 
2255
  $crtMeta = wp_generate_attachment_metadata($ID, $png2jpgMain);
2256
- add_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
2257
  }
2258
  }
2259
  //wp_update_attachment_metadata($ID, $crtMeta);
 
2260
  update_post_meta($ID, '_wp_attachment_metadata', $crtMeta);
 
2261
  if($attachmentID == $ID) { //copy back the metadata which will be returned.
2262
  $rawMeta = $crtMeta;
2263
  }
 
2264
  }
2265
 
2266
  if($png2jpgMain) {
2267
  $spPng2Jpg = new ShortPixelPng2Jpg($this->_settings);
2268
  $spPng2Jpg->png2JpgUpdateUrls(array(), $toReplace);
2269
  }
2270
- Log::addDebug('DoRestore, Unlinking', array($toUnlink) );
2271
  if(isset($toUnlink['PATHs'])) foreach($toUnlink['PATHs'] as $unlink) {
2272
  if($png2jpgMain) {
2273
  WPShortPixel::log("PNG2JPG unlink $unlink");
2274
  $unlinkFile = $fs->getFile($unlink);
2275
  $unlinkFile->delete();
2276
- // @unlink($unlink);
2277
  }
2278
  //try also the .webp
2279
  $unlinkWebpSymlink = trailingslashit(dirname($unlink)) . wp_basename($unlink, '.' . pathinfo($unlink, PATHINFO_EXTENSION)) . '.webp';
2280
  $unlinkWebp = $unlink . '.webp';
2281
- WPShortPixel::log("PNG2JPG unlink $unlinkWebp");
2282
- @unlink($unlinkWebpSymlink);
2283
- @unlink($unlinkWebp);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2284
  }
2285
  } catch(Exception $e) {
2286
  $this->throwNotice('generic-err', $e->getMessage());
@@ -2289,6 +2374,8 @@ class WPShortPixel {
2289
 
2290
  /** It's being dumped because settings like .webp can be cached */
2291
  $this->maybeDumpFromProcessedOnServer($itemHandler, $toUnlink);
 
 
2292
  do_action("shortpixel_after_restore_image", $attachmentID);
2293
  return $rawMeta;
2294
  }
@@ -2383,7 +2470,7 @@ class WPShortPixel {
2383
  $meta->setStatus(3);
2384
  $this->spMetaDao->update($meta);
2385
 
2386
-
2387
  //}
2388
 
2389
  return $meta;
@@ -2467,12 +2554,14 @@ class WPShortPixel {
2467
  public function handleOptimizeThumbs() {
2468
  $ID = intval($_GET['attachment_ID']);
2469
  $meta = wp_get_attachment_metadata($ID);
 
2470
 
2471
  // default return;
2472
  //$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);
2473
  $error = array('Status' => ShortPixelAPI::STATUS_SKIP, 'message' => __('Unspecified Error on Thumbnails for: ') . $ID);
2474
 
2475
- list($includedSizes, $thumbsCount) = $this->getThumbsToOptimize($meta, get_attached_file($ID));
 
2476
  //WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
2477
  $thumbsCount = count($includedSizes);
2478
 
@@ -2564,8 +2653,10 @@ class WPShortPixel {
2564
  $this->getQuotaInformation();
2565
  }
2566
 
 
2567
  public function handleDeleteAttachmentInBackup($ID) {
2568
- $file = get_attached_file($ID);
 
2569
  $meta = wp_get_attachment_metadata($ID);
2570
 
2571
 
@@ -2574,7 +2665,8 @@ class WPShortPixel {
2574
  try {
2575
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
2576
 
2577
- @unlink(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file));
 
2578
 
2579
  if ( !empty($meta['file']) )
2580
  {
@@ -2582,7 +2674,8 @@ class WPShortPixel {
2582
  //remove thumbs thumbnails
2583
  if(isset($meta["sizes"])) {
2584
  foreach($meta["sizes"] as $size => $imageData) {
2585
- @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));//remove thumbs
 
2586
  }
2587
  }
2588
  }
@@ -2631,8 +2724,10 @@ class WPShortPixel {
2631
  && isset($this->_settings->currentStats['time'])
2632
  && (time() - $this->_settings->currentStats['time'] < $time))
2633
  {
 
2634
  return $this->_settings->currentStats;
2635
  } else {
 
2636
  $imageCount = WpShortPixelMediaLbraryAdapter::countAllProcessable($this->_settings);
2637
  $quotaData['time'] = time();
2638
  $quotaData['optimizePdfs'] = $this->_settings->optimizePdfs;
@@ -2694,7 +2789,7 @@ class WPShortPixel {
2694
  }
2695
 
2696
  /** View for Custom media
2697
- * @todo Move this
2698
  */
2699
  public function listCustomMedia() {
2700
  if( ! class_exists( 'ShortPixelListTable' ) ) {
@@ -2702,7 +2797,7 @@ class WPShortPixel {
2702
  }
2703
  if(isset($_REQUEST['refresh']) && esc_attr($_REQUEST['refresh']) == 1) {
2704
  $notice = null;
2705
- $this->refreshCustomFolders($notice);
2706
  }
2707
  if(isset($_REQUEST['action']) && esc_attr($_REQUEST['action']) == 'optimize' && isset($_REQUEST['image'])) {
2708
  //die(ShortPixelMetaFacade::queuedId(ShortPixelMetaFacade::CUSTOM_TYPE, $_REQUEST['image']));
@@ -2796,6 +2891,12 @@ class WPShortPixel {
2796
  } else {
2797
  $this->_settings->processThumbnails = 0;
2798
  }
 
 
 
 
 
 
2799
  //clean the custom files errors in order to process them again
2800
  if($this->_settings->hasCustomFolders) {
2801
  $this->spMetaDao->resetFailed();
@@ -2972,13 +3073,13 @@ class WPShortPixel {
2972
  die(self::formatBytes(self::folderSize(SHORTPIXEL_BACKUP_FOLDER)));
2973
  }
2974
 
 
2975
  public function browseContent() {
2976
  if ( !current_user_can( 'manage_options' ) ) {
2977
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
2978
  }
2979
-
2980
  $root = self::getCustomFolderBase();
2981
-
2982
 
2983
  $postDir = rawurldecode($root.(isset($_POST['dir']) ? trim($_POST['dir']) : null ));
2984
  // set checkbox if multiSelect set to true
@@ -2988,28 +3089,35 @@ class WPShortPixel {
2988
 
2989
  if( file_exists($postDir) ) {
2990
 
2991
- $files = scandir($postDir);
 
 
 
 
 
2992
  $returnDir = substr($postDir, strlen($root));
2993
 
2994
- natcasesort($files);
2995
 
2996
- if( count($files) > 2 ) { // The 2 accounts for . and ..
2997
  echo "<ul class='jqueryFileTree'>";
2998
- foreach( $files as $file ) {
2999
 
3000
- if($file == 'ShortpixelBackups' || ShortPixelMetaFacade::isMediaSubfolder($postDir . $file, false)) continue;
 
 
3001
 
3002
- $htmlRel = str_replace("'", "&apos;", $returnDir . $file);
3003
- $htmlName = htmlentities($file);
3004
- $ext = preg_replace('/^.*\./', '', $file);
3005
 
3006
- if( file_exists($postDir . $file) && $file != '.' && $file != '..' ) {
3007
  //KEEP the spaces in front of the rel values - it's a trick to make WP Hide not replace the wp-content path
3008
- if( is_dir($postDir . $file) && (!$onlyFiles || $onlyFolders) ) {
3009
  echo "<li class='directory collapsed'>{$checkbox}<a rel=' " .$htmlRel. "/'>" . $htmlName . "</a></li>";
3010
- } else if (!$onlyFolders || $onlyFiles) {
3011
  echo "<li class='file ext_{$ext}'>{$checkbox}<a rel=' " . $htmlRel . "'>" . $htmlName . "</a></li>";
3012
- }
3013
  }
3014
  }
3015
 
@@ -3152,7 +3260,7 @@ class WPShortPixel {
3152
  'filesTodo' => $stats['totalFiles'] - $stats['totalProcessedFiles'],
3153
  'estimated' => $this->_settings->optimizeUnlisted || $this->_settings->optimizeRetina ? 'true' : 'false',
3154
  /* */
3155
- 'iconsUrl' => base64_encode(plugins_url('/shortpixel-image-optimiser/res/img'))
3156
  ))),
3157
  'cookies' => array()
3158
  ));
@@ -3165,29 +3273,56 @@ class WPShortPixel {
3165
 
3166
  // TODO - Part of the folder model.
3167
  public static function getCustomFolderBase() {
3168
- if(is_main_site()) {
3169
- $base = get_home_path();
3170
- return realpath(rtrim($base, '/'));
3171
- } else {
3172
- $up = wp_upload_dir();
3173
- return realpath($up['basedir']);
3174
- }
3175
  }
3176
 
3177
- // TODO - Should be part of folder model
 
3178
  protected function fullRefreshCustomFolder($path, &$notice) {
3179
  $folder = $this->spMetaDao->getFolder($path);
3180
  $diff = $folder->checkFolderContents(array('ShortPixelCustomMetaDao', 'getPathFiles'));
3181
- }
 
3182
 
3183
  // @todo - Should be part of folder model
3184
- public function refreshCustomFolders(&$notice, $ignore = false) {
 
3185
  $customFolders = array();
 
 
3186
  if($this->_settings->hasCustomFolders) {
3187
  $customFolders = $this->spMetaDao->getFolders();
3188
  foreach($customFolders as $folder) {
3189
- if($folder->getPath() === $ignore) continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3190
  try {
 
3191
  $mt = $folder->getFolderContentsChangeDate();
3192
  if($mt > strtotime($folder->getTsUpdated())) {
3193
  $fileList = $folder->getFileList(strtotime($folder->getTsUpdated()));
@@ -3204,8 +3339,8 @@ class WPShortPixel {
3204
  } else {
3205
  $notice = array("status" => "error", "msg" => $ex->getMessage());
3206
  }
3207
- }
3208
- }
3209
  }
3210
  return $customFolders;
3211
  }
@@ -3215,6 +3350,13 @@ class WPShortPixel {
3215
  */
3216
  public static function alterHtaccess( $clear = false ){
3217
  // [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
 
 
 
 
 
 
 
3218
  $upload_dir = wp_upload_dir();
3219
  $upload_base = trailingslashit($upload_dir['basedir']);
3220
 
@@ -3358,7 +3500,7 @@ class WPShortPixel {
3358
  if($validate) {
3359
  $args['body']['DomainCheck'] = get_site_url();
3360
  $args['body']['Info'] = get_bloginfo('version') . '|' . phpversion();
3361
- $imageCount = WpShortPixelMediaLbraryAdapter::countAllProcessableFiles($this->_settings);
3362
  $args['body']['ImagesCount'] = $imageCount['mainFiles'];
3363
  $args['body']['ThumbsCount'] = $imageCount['totalFiles'] - $imageCount['mainFiles'];
3364
  $argsStr .= "&DomainCheck={$args['body']['DomainCheck']}&Info={$args['body']['Info']}&ImagesCount={$imageCount['mainFiles']}&ThumbsCount={$args['body']['ThumbsCount']}";
@@ -3513,12 +3655,13 @@ class WPShortPixel {
3513
  return;
3514
  }
3515
 
3516
- $file = get_attached_file($id);
 
3517
  $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
3518
  $itemHandler = new ShortPixelMetaFacade($id);
3519
  $meta = $itemHandler->getMeta();
3520
 
3521
- $fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
3522
  $invalidKey = !$this->_settings->verifiedKey;
3523
  $quotaExceeded = $this->_settings->quotaExceeded;
3524
  $renderData = array("id" => $id, "showActions" => (current_user_can( 'manage_options' ) || current_user_can( 'upload_files' ) || current_user_can( 'edit_posts' )));
@@ -3550,12 +3693,12 @@ class WPShortPixel {
3550
  && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing'])) //for images that erroneously have ShortPixelImprovement = 0 when WaitingProcessing
3551
  ) { //already optimized
3552
  $thumbsOptList = isset($data['ShortPixel']['thumbsOptList']) ? $data['ShortPixel']['thumbsOptList'] : array();
3553
- list($thumbsToOptimizeList, $sizesCount) = $this->getThumbsToOptimize($data, $file);
3554
 
3555
  $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
3556
  $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
3557
  $renderData['bonus'] = ($data['ShortPixelImprovement'] < 5);
3558
- $renderData['backup'] = $this->getBackupFolderAny($file, $sizesCount? $data['sizes'] : array());
3559
  $renderData['type'] = isset($data['ShortPixel']['type']) ? $data['ShortPixel']['type'] : '';
3560
  $renderData['invType'] = ShortPixelAPI::getCompressionTypeName($this->getOtherCompressionTypes(ShortPixelAPI::getCompressionTypeCode($renderData['type'])));
3561
  $renderData['thumbsTotal'] = $sizesCount;
@@ -3572,14 +3715,14 @@ class WPShortPixel {
3572
  $renderData['quotaExceeded'] = $quotaExceeded;
3573
  $webP = 0;
3574
  if($extended) {
3575
- if(file_exists(dirname($file) . '/' . ShortPixelAPI::MB_basename($file, '.'.$fileExtension) . '.webp' )){
3576
  $webP++;
3577
  }
3578
  if(isset($data['sizes'])) {
3579
  foreach($data['sizes'] as $key => $size) {
3580
  if (strpos($key, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) continue;
3581
  $sizeName = $size['file'];
3582
- if(file_exists(dirname($file) . '/' . ShortPixelAPI::MB_basename($sizeName, '.'.$fileExtension) . '.webp' )){
3583
  $webP++;
3584
  }
3585
  }
@@ -3640,7 +3783,7 @@ class WPShortPixel {
3640
  * @return array Array of Thumbs to Optimize - only the filename - , and count of sizes not excluded ...
3641
  */
3642
  function getThumbsToOptimize($data, $filepath) {
3643
- // This function moved, but lack of other destination.
3644
  return WpShortPixelMediaLbraryAdapter::getThumbsToOptimize($data, $filepath);
3645
 
3646
  }
@@ -3660,7 +3803,6 @@ class WPShortPixel {
3660
  * @param array $columns Array of colums sortable
3661
  * @todo Should be part of media library controller. ( is request best hook for this?)
3662
  */
3663
-
3664
  function columnOrderFilterBy($vars) {
3665
  if ( isset( $vars['orderby'] ) && 'ShortPixel Compression' == $vars['orderby'] ) {
3666
  $vars = array_merge( $vars, array(
@@ -3668,11 +3810,35 @@ class WPShortPixel {
3668
  'orderby' => 'meta_value_num',
3669
  ) );
3670
  }
3671
- if ( 'upload.php' == $GLOBALS['pagenow'] && !empty( $_GET['shortpixel_status'] ) ) {
3672
 
3673
- $status = $_GET['shortpixel_status'];
3674
  $metaKey = '_shortpixel_status';
3675
- $metaCompare = $status == 0 ? 'NOT EXISTS' : ($status < 0 ? '<' : '=');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3676
 
3677
  $vars = array_merge( $vars, array(
3678
  'meta_query' => array(
@@ -3684,6 +3850,7 @@ class WPShortPixel {
3684
  )
3685
  ));
3686
  }
 
3687
  return $vars;
3688
  }
3689
 
@@ -3696,21 +3863,36 @@ class WPShortPixel {
3696
  if ( $scr->base !== 'upload' ) return;
3697
 
3698
  $status = filter_input(INPUT_GET, 'shortpixel_status', FILTER_SANITIZE_STRING );
3699
- $selected = (int)$status > 0 ? $status : 0;
3700
- $args = array(
3701
  'show_option_none' => 'ShortPixel',
3702
  'name' => 'shortpixel_status',
3703
  'selected' => $selected
3704
- );
3705
  // wp_dropdown_users( $args );
 
 
 
 
 
 
 
3706
 
3707
- echo("<select name='shortpixel_status' id='shortpixel_status'>\n"
 
 
 
 
 
 
 
 
3708
  . "\t<option value='0'" . ($status == 0 ? " selected='selected'" : "") . ">All images</option>\n"
3709
  . "\t<option value='2'" . ($status == 2 ? " selected='selected'" : "") . ">Optimized</option>\n"
3710
  . "\t<option value='none'" . ($status == 'none' ? " selected='selected'" : "") . ">Unoptimized</option>\n"
3711
  . "\t<option value='1'" . ($status == 1 ? " selected='selected'" : "") . ">Pending</option>\n"
3712
  . "\t<option value='-1'" . ($status < 0 ? " selected='selected'" : "") . ">Errors</option>\n"
3713
- . "</select>");
3714
  }
3715
 
3716
  /** Calculates Optimization if PNG2Jpg does something
@@ -3753,11 +3935,12 @@ class WPShortPixel {
3753
  */
3754
  public function onDeleteImage($post_id) {
3755
  $itemHandler = new ShortPixelMetaFacade($post_id);
3756
- $urlsPaths = $itemHandler->getURLsAndPATHs(true, false, true, array(), true);
3757
  if(count($urlsPaths['PATHs'])) {
3758
  $this->maybeDumpFromProcessedOnServer($itemHandler, $urlsPaths);
3759
  $this->deleteBackupsAndWebPs($urlsPaths['PATHs']);
3760
  }
 
3761
  return $itemHandler; //return it because we call it also on replace and on replace we need to follow this by deleting SP metadata, on delete it
3762
  }
3763
 
@@ -3773,20 +3956,35 @@ class WPShortPixel {
3773
  return;
3774
  }
3775
 
 
 
 
3776
  $backupFolder = trailingslashit($this->getBackupFolder($paths[0]));
3777
  foreach($paths as $path) {
3778
  $pos = strrpos($path, ".");
 
3779
  if ($pos !== false) {
3780
  //$webpPath = substr($path, 0, $pos) . ".webp";
3781
  //echo($webpPath . "<br>");
3782
- @unlink(substr($path, 0, $pos) . ".webp");
3783
- @unlink(substr($path, 0, $pos) . "@2x.webp");
 
 
3784
  }
3785
  //delte also the backups for image and retina correspondent
3786
- $fileName = wp_basename($path);
3787
- $extension = pathinfo($fileName, PATHINFO_EXTENSION);
3788
- @unlink($backupFolder . $fileName);
3789
- @unlink($backupFolder . preg_replace("/\." . $extension . "$/i", '@2x.' . $extension, $fileName));
 
 
 
 
 
 
 
 
 
3790
  }
3791
  }
3792
  //
@@ -3805,7 +4003,7 @@ class WPShortPixel {
3805
  return $defaults;
3806
  }
3807
 
3808
- // todo move NGG specific function to own integration
3809
  public function nggColumns( $defaults ) {
3810
  $this->nggColumnIndex = count($defaults) + 1;
3811
  add_filter( 'ngg_manage_images_column_' . $this->nggColumnIndex . '_header', array( &$this, 'nggColumnHeader' ) );
@@ -3865,15 +4063,8 @@ class WPShortPixel {
3865
 
3866
  // @todo Should be utility function
3867
  static public function formatBytes($bytes, $precision = 2) {
3868
- $units = array('B', 'KB', 'MB', 'GB', 'TB');
3869
-
3870
- $bytes = max($bytes, 0);
3871
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
3872
- $pow = min($pow, count($units) - 1);
3873
 
3874
- $bytes /= pow(1024, $pow);
3875
-
3876
- return round($bytes, $precision) . ' ' . $units[$pow];
3877
  }
3878
 
3879
  /** Checks if file can be processed. Mainly against exclusion
@@ -3896,8 +4087,11 @@ class WPShortPixel {
3896
  return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
3897
  }
3898
 
 
3899
  static public function _isProcessable($ID, $excludeExtensions = array(), $excludePatterns = array(), $meta = false) {
3900
- $path = get_attached_file($ID);//get the full file PATH
 
 
3901
  if(isset($excludePatterns) && is_array($excludePatterns)) {
3902
  foreach($excludePatterns as $excludePattern) {
3903
  $type = $excludePattern["type"];
@@ -3986,13 +4180,30 @@ class WPShortPixel {
3986
  */
3987
  static public function folderSize($path) {
3988
  $total_size = 0;
3989
- if(file_exists($path)) {
3990
- $files = scandir($path); // @todo This gives a warning if directory is not writable.
 
 
 
 
 
3991
  } else {
3992
  return $total_size;
3993
  }
3994
- $cleanPath = rtrim($path, '/'). '/';
3995
- foreach($files as $t) {
 
 
 
 
 
 
 
 
 
 
 
 
3996
  if ($t<>"." && $t<>"..")
3997
  {
3998
  $currentFile = $cleanPath . $t;
@@ -4005,7 +4216,7 @@ class WPShortPixel {
4005
  $total_size += $size;
4006
  }
4007
  }
4008
- }
4009
  return $total_size;
4010
  }
4011
 
@@ -4183,7 +4394,7 @@ class WPShortPixel {
4183
  <div id="shortpixel-hs-button-blind" class="shortpixel-hs-button-blind"></div>
4184
  <div id="shortpixel-hs-tools" class="shortpixel-hs-tools">
4185
  <a href="javascript:shortpixelToggleHS();" class="shortpixel-hs-tools-docs" title="<?php _e('Search through our online documentation.', 'shortpixel-image-optimiser'); ?>">
4186
- <img src="<?php echo(plugins_url('/shortpixel-image-optimiser/res/img/notes-sp.png'));?>" style="margin-bottom: 2px;width: 36px;">
4187
  </a>
4188
  </div>
4189
  <script>
22
 
23
  public static $PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'gif', 'png', 'pdf');
24
 
25
+ private static $first_run = false;
26
+
27
  public function __construct() {
28
  $this->timer = time();
29
 
39
  $this->_settings = new WPShortPixelSettings();
40
  $this->_apiInterface = new ShortPixelAPI($this->_settings);
41
  $this->cloudflareApi = new ShortPixelCloudFlareApi($this->_settings->cloudflareEmail, $this->_settings->cloudflareAuthKey, $this->_settings->cloudflareZoneID);
42
+ $this->hasNextGen = wpSPIO()->env()->has_nextgen; //ShortPixelNextGenAdapter::hasNextGen();
43
  $this->spMetaDao = new ShortPixelCustomMetaDao(new WpShortPixelDb(), $this->_settings->excludePatterns);
44
  $this->prioQ = (! defined('SHORTPIXEL_NOFLOCK')) ? new ShortPixelQueue($this, $this->_settings) : new ShortPixelQueueDB($this, $this->_settings);
45
  $this->view = new ShortPixelView($this);
46
 
47
+ if (self::$first_run === false)
48
+ {
49
+ $this->loadHooks();
50
+ }
51
+
52
+ // only load backed, or when frontend processing is enabled.
53
+ if (is_admin() || $this->_settings->frontBootstrap )
54
+ {
55
+ $keyControl = new \ShortPixel\apiKeyController();
56
+ $keyControl->setShortPixel($this);
57
+ $keyControl->load();
58
+ }
59
+
60
+ }
61
+
62
+ /** Fire only once hooks. In time these function mostly should be divided between controllers / hook itself moved to ShortPixel Plugin */
63
+ protected function loadHooks()
64
+ {
65
+ self::$first_run = true;
66
+ load_plugin_textdomain('shortpixel-image-optimiser', false, plugin_basename(dirname( SHORTPIXEL_PLUGIN_FILE )).'/lang');
67
+
68
+ $isAdminUser = current_user_can( 'manage_options' );
69
 
70
  define('QUOTA_EXCEEDED', $this->view->getQuotaExceededHTML());
71
 
172
  add_action( 'admin_footer', array($this, 'admin_footer_js') );
173
  add_action( 'admin_head', array( $this, 'headCSS') );
174
 
 
 
 
 
 
 
 
175
  //register a method to display admin notices if necessary
176
  add_action('admin_notices', array( &$this, 'displayAdminNotices'));
177
 
178
  $this->migrateBackupFolder();
 
 
 
 
 
 
 
 
 
179
  }
180
 
181
+
 
 
 
182
 
183
  // @hook admin menu
184
  // @todo move to plugin class
185
  function registerAdminPage( ) {
186
+ return;
187
  if($this->spMetaDao->hasFoldersTable() && count($this->spMetaDao->getFolders())) {
188
  /*translators: title and menu name for the Other media page*/
189
  add_media_page( __('Other Media Optimized by ShortPixel','shortpixel-image-optimiser'), __('Other Media','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-custom', array( &$this, 'listCustomMedia' ) );
192
  add_media_page( __('ShortPixel Bulk Process','shortpixel-image-optimiser'), __('Bulk ShortPixel','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcess' ) );
193
  }
194
 
195
+ /*public static function shortPixelActivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
196
  {
197
  self::shortPixelDeactivatePlugin();
198
  if(SHORTPIXEL_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
211
  self::alterHtaccess(); //add the htaccess lines
212
  }
213
  WPShortPixelSettings::onActivate();
214
+ } */
215
 
216
+ /* public static function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
217
  {
218
  ShortPixelQueue::resetBulk();
219
  (! defined('SHORTPIXEL_NOFLOCK')) ? ShortPixelQueue::resetPrio() : ShortPixelQueueDB::resetPrio();
226
  self::alterHtaccess(true);
227
 
228
  @unlink(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
229
+ } */
230
 
231
+ /* public static function shortPixelUninstallPlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
232
  {
233
  $settings = new WPShortPixelSettings();
234
  if($settings->removeSettingsOnDeletePlugin == 1) {
235
  WPShortPixelSettings::debugResetOptions();
236
  insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
237
  }
238
+ } */
239
 
240
  public function getConflictingPlugins() {
241
  $conflictPlugins = array(
339
  )
340
  ));
341
  }
342
+ if(!$this->_settings->frontBootstrap){
343
+ $conflictPlugins['Bulk Images to Posts Frontend'] = array (
344
+ 'action'=>'Change Setting',
345
+ 'data'=>'bulk-images-to-posts-front/bulk-images-to-posts.php',
346
+ 'href'=>'options-general.php?page=wp-shortpixel-settings&part=adv-settings#siteAuthUser',
347
+ 'details' => __('This plugin is uploading images in front-end so please activate the "Process in front-end" advanced option in ShortPixel in order to have your images optimized.','shortpixel-image-optimiser')
348
+ );
349
+ }
350
+
351
  $found = array();
352
  foreach($conflictPlugins as $name => $path) {
353
  $action = ( isset($path['action']) ) ? $path['action'] : null;
496
  /** @todo Plugin init class. Try to get rid of inline JS. Also still loads on all WP pages, prevent that. */
497
  function shortPixelJS() {
498
 
499
+ if (! \wpSPIO()->env()->is_screen_to_use )
500
+ return; // not ours, don't load JS and such.
501
+
502
  //require_once(ABSPATH . 'wp-admin/includes/screen.php');
503
  if(function_exists('get_current_screen')) {
504
  $screen = get_current_screen();
505
 
506
  if(is_object($screen)) {
507
 
 
508
  wp_enqueue_style('short-pixel-bar.min.css', plugins_url('/res/css/short-pixel-bar.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
509
  if( in_array($screen->id, array('attachment', 'upload', 'settings_page_wp-shortpixel', 'media_page_wp-short-pixel-bulk', 'media_page_wp-short-pixel-custom'))) {
510
  wp_enqueue_style('short-pixel.min.css', plugins_url('/res/css/short-pixel.min.css',SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION);
597
  * @todo Find a better solution for this */
598
  public function admin_footer_js()
599
  {
600
+ if (! \wpSPIO()->env()->is_screen_to_use )
601
+ return; // not ours, don't load JS and such.
602
+
603
  if(function_exists('get_current_screen')) {
604
  $screen = get_current_screen();
605
  if(is_object($screen)) {
779
  return $meta;
780
  }
781
 
782
+ $fs = \wpSPIO()->filesystem();
783
+
784
  // some plugins (e.g. WP e-Commerce) call the wp_attachment_metadata on just editing the image...
785
  $dbMeta = wp_get_attachment_metadata($ID);
786
+ $currentFile = $fs->getAttachedFile($ID);
787
+
788
  $refresh = false;
789
 
790
  if(isset($dbMeta['ShortPixelImprovement'])) {
798
  self::log("Handle Media Library Image Upload #{$ID}");
799
  //self::log("STACK: " . json_encode(debug_backtrace()));
800
 
801
+ if(!$this->_settings->optimizePdfs && 'pdf' === $currentFile->getExtension() ) {
802
  //pdf is not optimized automatically as per the option, but can be optimized by button. Nothing to do.
803
  return $meta;
804
  }
805
+ elseif(! $currentFile->exists() && isset($meta['file']) && in_array(strtolower(pathinfo($meta['file'], PATHINFO_EXTENSION)), self::$PROCESSABLE_EXTENSIONS)) {
806
  //in some rare cases (images added from the front-end) it's an image but get_attached_file returns null (the record is not yet saved in the DB)
807
  //in this case add it to the queue nevertheless
808
  $this->prioQ->push($ID);
904
  return $converter->checkConvertMediaPng2Jpg($itemHandler);
905
  }
906
 
907
+ // moved to external.
908
+ /* public function handleGravityFormsImageField($value) {
909
  if(!($folder = $this->spMetaDao->getFolder(SHORTPIXEL_UPLOADS_BASE . '/gravity_forms'))) {
910
  return;
911
  }
918
  $localPath = str_replace($uploadDir['baseurl'], SHORTPIXEL_UPLOADS_BASE, $value);
919
 
920
  return $this->addPathToCustomFolder($localPath, $folder->getId(), 0);
921
+ } */
922
 
923
  /**
924
  * this is hooked onto the NextGen upload
1118
  $maxTime = min(SHORTPIXEL_MAX_EXECUTION_TIME, 90);
1119
  $timeoutThreshold = 5; // will adapt this with the maximum time needed for one pass
1120
  $passTime = time();
1121
+ // @todo If this fails, the bulk will since no start/stop Id's will change */
1122
  for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
1123
  ($crtStartQueryID >= $endQueryID) && (count($itemList) < SHORTPIXEL_PRESEND_ITEMS) && ($sanityCheck < 150)
1124
  && (time() - $this->timer < $maxTime - $timeoutThreshold); $sanityCheck++) {
1221
  )
1222
  ) {
1223
 
1224
+ $item->searchUnlistedFiles(); // $this->addUnlistedThumbs($item); // search for unlisted thumbs, if that is the setting.
1225
  $URLsAndPATHs = $item->getURLsAndPATHs(true, true, $this->_settings->optimizeRetina, $this->_settings->excludeSizes);
1226
  Log::addDebug('Gathering URLS AND PATHS', array($URLsAndPATHs));
1227
  if(count($URLsAndPATHs["URLs"])) {
1374
  { //take from custom images if any left to optimize - only if bulk was ever started
1375
  //but first refresh if it wasn't refreshed in the last hour
1376
  if(time() - $this->_settings->hasCustomFolders > 3600) {
1377
+ $notice = null; $this->refreshCustomFolders();
1378
  $this->_settings->hasCustomFolders = time();
1379
  }
1380
 
1484
  $sizes = $meta->getThumbs();
1485
  if('pdf' == strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))) {
1486
  // echo($result["Filename"] . " ESTE --> "); die(var_dump(strtolower(pathinfo($result["Filename"], PATHINFO_EXTENSION))));
1487
+ $thumb = wpSPIO()->plugin_url('res/img/logo-pdf.png' );
1488
  $bkThumb = '';
1489
  } else {
1490
  if(count($sizes)) {
1513
  //$backupUrl = content_url() . "/" . SHORTPIXEL_UPLOADS_NAME . "/" . SHORTPIXEL_BACKUP . "/";
1514
  //or even better:
1515
  $backupUrl = SHORTPIXEL_BACKUP_URL . "/";
 
1516
  $urlBkPath = ShortPixelMetaFacade::returnSubDir($meta->getPath(), ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE);
1517
  $bkThumb = $backupUrl . $urlBkPath . $thumb;
1518
  }
1670
  $meta = $itemHandler->getMeta();
1671
 
1672
  Log::addDebug('Finding Thumbs on path' . $meta->getPath());
1673
+ //$thumbs = WpShortPixelMediaLbraryAdapter::findThumbs($meta->getPath());
1674
 
1675
  $fs = new \ShortPixel\FileSystemController();
1676
  $mainFile = $fs->getFile($meta->getPath());
1754
 
1755
  private function sendToProcessing($itemHandler, $compressionType = false, $onlyThumbs = false) {
1756
  //conversion of PNG 2 JPG for existing images
1757
+
1758
  if($itemHandler->getType() == ShortPixelMetaFacade::MEDIA_LIBRARY_TYPE) { //currently only for ML
1759
  $rawMeta = $this->checkConvertMediaPng2Jpg($itemHandler);
1760
 
1767
  $URLsAndPATHs = $this->getURLsAndPATHs($itemHandler, NULL, $onlyThumbs);
1768
  Log::addDebug('Send to PRocessing - URLS -', array($URLsAndPATHs) );
1769
 
1770
+ // Limit 'send to processing' by URL, see function.
1771
+ $result = WpShortPixelMediaLbraryAdapter::checkRequestLimiter($URLsAndPATHs['URLs']);
1772
+
1773
+ if (! $result) // already passed onto the processor.
1774
+ {
1775
+ Log::addDebug('Preventing sentToProcessing. Reported as already sent');
1776
+ return $URLsAndPATHs;
1777
+ }
1778
+
1779
+ $meta = $itemHandler->getMeta();
1780
  //find thumbs that are not listed in the metadata and add them in the sizes array
1781
+ $itemHandler->searchUnlistedFiles(); // $this->addUnlistedThumbs($itemHandler);
 
1782
 
1783
  //find any missing thumbs files and mark them as such
1784
  $miss = $meta->getThumbsMissing();
1810
  }
1811
  }
1812
 
1813
+ $thumbObtList = $meta->getThumbsOptList();
1814
+ $missing = $meta->getThumbsMissing();
1815
 
1816
 
1817
  $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $itemHandler,
1874
  $this->prioQ->push($imageId);
1875
  $itemHandler = new ShortPixelMetaFacade($imageId);
1876
 
1877
+ $itemFile = \wpSPIO()->filesystem()->getAttachedFile($imageId);
1878
+
1879
+ if(!$manual && 'pdf' === $itemFile->getExtension() && !$this->_settings->optimizePdfs) {
1880
  $ret = array("Status" => ShortPixelAPI::STATUS_SKIP, "Message" => $imageId);
1881
  } else {
1882
  try {
1883
  $this->sendToProcessing($itemHandler, false, $itemHandler->getMeta()->getThumbsTodo());
1884
  $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "Message" => "");
1885
+ } catch(Exception $e) { //$path Exception("Post metadata is corrupt (No attachment URL)")
1886
  $itemHandler->getMeta();
1887
  $errCode = $e->getCode() < 0 ? $e->getCode() : ShortPixelAPI::ERR_FILE_NOT_FOUND;
1888
  $itemHandler->setError($errCode, $e->getMessage());
2081
  protected function doRestore($attachmentID, $rawMeta = null) {
2082
  do_action("shortpixel_before_restore_image", $attachmentID);
2083
 
2084
+ $fs = \wpSPIO()->filesystem();
 
 
2085
 
2086
  // Setup Original File and Data. This is used to determine backup path.
2087
+ \wpSPIO()->loadModel('image');
2088
+
2089
+ $imageObj = new \ShortPixel\ImageModel();
2090
+ $imageObj->setbyPostID($attachmentID);
2091
+
2092
+ $fsFile = $imageObj->getFile();
2093
+ //$fsFile = $fs->getAttachedFile($attachmentID);
2094
  $filePath = (string) $fsFile->getFileDir();
2095
 
2096
+ $itemHandler = $imageObj->getFacade(); //new ShortPixelMetaFacade($attachmentID);
2097
  if($rawMeta) {
2098
  $itemHandler->setRawMeta($rawMeta); //prevent another database trip
2099
  } else {
2108
  return false;
2109
  }
2110
 
2111
+ // -sigh- to do something after possibly downloading and getting paths, but before any conversions.
2112
+ do_action('shortpixel_restore_after_pathget', $attachmentID);
2113
+
2114
  // Get correct Backup Folder and file. .
2115
  $sizes = isset($rawMeta["sizes"]) ? $rawMeta["sizes"] : array();
2116
  $bkFolder = $fs->getDirectory($this->getBackupFolderAny($fsFile->getFullPath(), $sizes));
2140
 
2141
  // find the jpg optimized image in backups, and mark to remove
2142
  if ($bkFile->exists())
2143
+ $toUnlink['PATHs'][] = $bkFile->getFullPath();
2144
 
2145
  // $baseUrl = ShortPixelPng2Jpg::removeUrlProtocol(trailingslashit(str_replace($image, "", $imageUrl))); //make the base url protocol agnostic if it's not already
2146
 
2147
  // not needed, we don't do this weird remove anymore.
2148
  $baseRelPath = ''; // trailingslashit(dirname($image)); // @todo Replace this (string) $fsFile->getFileDir();
2149
 
 
2150
  $toReplace[ShortPixelPng2Jpg::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($png2jpgMain);
2151
  foreach($sizes as $key => $size) {
2152
  if(isset($png2jpgSizes[$key])) {
2154
  }
2155
 
2156
  $backuppedSize = $fs->getFile($backupFileDir . $size['file'] );
2157
+ Log::addDebug('Checking for PNG Backup at - ', $backuppedSize->getFullPath() );
2158
  if ($backuppedSize->exists())
2159
  {
2160
  $toUnlink['PATHs'][] = $backuppedSize ->getFullPath();
2206
  return false;
2207
  }
2208
  $bkCount++;
2209
+ //$thumbsPaths[] = array('source' => $source, 'destination' => $destination);
2210
+ // This is to prevent double attempts on moving. If sizes have same definition, can have multiple same files in sizes, but they will be written to same path.
2211
+ $thumbsPaths[$destination->getFileName()] = array('source' => $source, 'destination' => $destination);
2212
  }
2213
  }
2214
  if(!$bkCount) {
2227
  if($bkCount) { // backups, if exist
2228
  //main file
2229
  if($main) {
2230
+ // new WP 5.3 feature when image is scaled if big.
2231
+ $origFile = $imageObj->has_original();
2232
+ if (is_object($origFile))
2233
+ {
2234
+ $bkOrigFile = $origFile->getBackUpFile();
2235
+ if ($bkOrigFile && $bkOrigFile->exists())
2236
+ $bkOrigFile->move($origFile);
2237
+
2238
+ Log::addDebug('Restore result - Backup oringal file', array($bkOrigFile, $origFile));
2239
+ }
2240
  //$this->renameWithRetina($bkFile, $file);
2241
  if (! $bkFile->move($fsFile))
2242
  {
2243
  Log::addError('DoRestore failed restoring backup', array($bkFile->getFullPath(), $fsFile->getFullPath() ));
2244
  }
2245
+
2246
  $retinaBK = $fs->getFile( $bkFile->getFileDir()->getPath() . $bkFile->getFileBase() . '@2x' . $bkFile->getExtension() );
2247
  if ($retinaBK->exists())
2248
  {
2284
  $duplicates = ShortPixelMetaFacade::getWPMLDuplicates($attachmentID);
2285
  foreach($duplicates as $ID) {
2286
  //Added sanitizeMeta (improved with @unserialize) as per https://secure.helpscout.net/conversation/725053586/11656?folderId=1117588
2287
+ // $crtMeta = $attachmentID == $ID ? $rawMeta : ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($ID));
2288
+ $facade = new ShortPixelMetaFacade($ID);
2289
+ if ($attachmentID == $ID)
2290
+ $crtMeta = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($ID));
2291
+ else {
2292
+ $crtMeta = $rawMeta;
2293
+ }
2294
+
2295
  if(isset($crtMeta['previous_meta'])) continue;
2296
  if( isset($crtMeta["ShortPixelImprovement"]) && is_numeric($crtMeta["ShortPixelImprovement"])
2297
  && 0 + $crtMeta["ShortPixelImprovement"] < 5 && $this->_settings->under5Percent > 0) {
2313
  $crtMeta['sizes'] = $png2jpgSizes;
2314
  } else {
2315
  //this was an image converted on upload, regenerate the thumbs using the PNG main image BUT deactivate temporarily the filter!!
2316
+ $admin = \ShortPixel\adminController::getInstance();
2317
+
2318
+ //@todo Can be removed when test seems working.
2319
+ $test = remove_filter( 'wp_generate_attachment_metadata', array($admin,'handleImageUploadHook'),10);
2320
+
2321
+ if (! $test)
2322
+ Log::addWarn('Wp generate Attachment metadta filter not removed');
2323
  $crtMeta = wp_generate_attachment_metadata($ID, $png2jpgMain);
2324
+ add_filter( 'wp_generate_attachment_metadata', array($admin,'handleImageUploadHook'), 10, 2 );
2325
  }
2326
  }
2327
  //wp_update_attachment_metadata($ID, $crtMeta);
2328
+ // @todo Should call MetaFacade here!
2329
  update_post_meta($ID, '_wp_attachment_metadata', $crtMeta);
2330
+
2331
  if($attachmentID == $ID) { //copy back the metadata which will be returned.
2332
  $rawMeta = $crtMeta;
2333
  }
2334
+
2335
  }
2336
 
2337
  if($png2jpgMain) {
2338
  $spPng2Jpg = new ShortPixelPng2Jpg($this->_settings);
2339
  $spPng2Jpg->png2JpgUpdateUrls(array(), $toReplace);
2340
  }
2341
+
2342
  if(isset($toUnlink['PATHs'])) foreach($toUnlink['PATHs'] as $unlink) {
2343
  if($png2jpgMain) {
2344
  WPShortPixel::log("PNG2JPG unlink $unlink");
2345
  $unlinkFile = $fs->getFile($unlink);
2346
  $unlinkFile->delete();
2347
+
2348
  }
2349
  //try also the .webp
2350
  $unlinkWebpSymlink = trailingslashit(dirname($unlink)) . wp_basename($unlink, '.' . pathinfo($unlink, PATHINFO_EXTENSION)) . '.webp';
2351
  $unlinkWebp = $unlink . '.webp';
2352
+ WPShortPixel::log("DoRestore webp unlink $unlinkWebp");
2353
+ //@unlink($unlinkWebpSymlink);
2354
+
2355
+ $unlinkFile = $fs->getFile($unlinkWebpSymlink);
2356
+ if ($unlinkFile->exists())
2357
+ {
2358
+ Log::addDebug('DoRestore, Deleting - ', $unlinkWebpSymlink );
2359
+ $unlinkFile->delete();
2360
+ }
2361
+
2362
+ $unlinkFile = $fs->getFile($unlinkWebp);
2363
+ if ($unlinkFile->exists())
2364
+ {
2365
+ Log::addDebug('DoRestore, Deleting - ', $unlinkWebp );
2366
+ $unlinkFile->delete();
2367
+ }
2368
+
2369
  }
2370
  } catch(Exception $e) {
2371
  $this->throwNotice('generic-err', $e->getMessage());
2374
 
2375
  /** It's being dumped because settings like .webp can be cached */
2376
  $this->maybeDumpFromProcessedOnServer($itemHandler, $toUnlink);
2377
+ $itemHandler->deleteItemCache(); // remove any cache
2378
+ $rawMeta = $itemHandler->getRawMeta();
2379
  do_action("shortpixel_after_restore_image", $attachmentID);
2380
  return $rawMeta;
2381
  }
2470
  $meta->setStatus(3);
2471
  $this->spMetaDao->update($meta);
2472
 
2473
+ $itemHandler->deleteItemCache();
2474
  //}
2475
 
2476
  return $meta;
2554
  public function handleOptimizeThumbs() {
2555
  $ID = intval($_GET['attachment_ID']);
2556
  $meta = wp_get_attachment_metadata($ID);
2557
+ $fs = \wpSPIO()->filesystem();
2558
 
2559
  // default return;
2560
  //$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);
2561
  $error = array('Status' => ShortPixelAPI::STATUS_SKIP, 'message' => __('Unspecified Error on Thumbnails for: ') . $ID);
2562
 
2563
+ $optFile = $fs->getAttachedFile($ID);
2564
+ list($includedSizes, $thumbsCount) = $this->getThumbsToOptimize($meta, $optFile->getFullPath());
2565
  //WpShortPixelMediaLbraryAdapter::getSizesNotExcluded($meta['sizes'], $this->_settings->excludeSizes);
2566
  $thumbsCount = count($includedSizes);
2567
 
2653
  $this->getQuotaInformation();
2654
  }
2655
 
2656
+ // @todo integrate this in a normal way / move @unlinks to proper fs delete.
2657
  public function handleDeleteAttachmentInBackup($ID) {
2658
+ $fileObj = \wpSPIO()->filesystem()->getAttachedFile($ID);
2659
+ $file = $fileObj->getFullPath();
2660
  $meta = wp_get_attachment_metadata($ID);
2661
 
2662
 
2665
  try {
2666
  $SubDir = ShortPixelMetaFacade::returnSubDir($file);
2667
 
2668
+ if (file_exists(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file)))
2669
+ @unlink(SHORTPIXEL_BACKUP_FOLDER . '/' . $SubDir . ShortPixelAPI::MB_basename($file));
2670
 
2671
  if ( !empty($meta['file']) )
2672
  {
2674
  //remove thumbs thumbnails
2675
  if(isset($meta["sizes"])) {
2676
  foreach($meta["sizes"] as $size => $imageData) {
2677
+ if (file_exists($filesPath . ShortPixelAPI::MB_basename($imageData['file'])))
2678
+ @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));//remove thumbs
2679
  }
2680
  }
2681
  }
2724
  && isset($this->_settings->currentStats['time'])
2725
  && (time() - $this->_settings->currentStats['time'] < $time))
2726
  {
2727
+ Log::addDebug("CURRENT STATS FROM CACHE (not older than $time sec., currently " . (time() - $this->_settings->currentStats['time']) . ' sec. old)');
2728
  return $this->_settings->currentStats;
2729
  } else {
2730
+ Log::addDebug("CURRENT STATS (not older than $time) ARE BEING CALCULATED...");
2731
  $imageCount = WpShortPixelMediaLbraryAdapter::countAllProcessable($this->_settings);
2732
  $quotaData['time'] = time();
2733
  $quotaData['optimizePdfs'] = $this->_settings->optimizePdfs;
2789
  }
2790
 
2791
  /** View for Custom media
2792
+ * @todo Move this to own view.
2793
  */
2794
  public function listCustomMedia() {
2795
  if( ! class_exists( 'ShortPixelListTable' ) ) {
2797
  }
2798
  if(isset($_REQUEST['refresh']) && esc_attr($_REQUEST['refresh']) == 1) {
2799
  $notice = null;
2800
+ $this->refreshCustomFolders(true);
2801
  }
2802
  if(isset($_REQUEST['action']) && esc_attr($_REQUEST['action']) == 'optimize' && isset($_REQUEST['image'])) {
2803
  //die(ShortPixelMetaFacade::queuedId(ShortPixelMetaFacade::CUSTOM_TYPE, $_REQUEST['image']));
2891
  } else {
2892
  $this->_settings->processThumbnails = 0;
2893
  }
2894
+
2895
+ if ( isset($_POST['createWebp']) )
2896
+ $this->_settings->createWebp = 1;
2897
+ else
2898
+ $this->_settings->createWebp = 0;
2899
+
2900
  //clean the custom files errors in order to process them again
2901
  if($this->_settings->hasCustomFolders) {
2902
  $this->spMetaDao->resetFailed();
3073
  die(self::formatBytes(self::folderSize(SHORTPIXEL_BACKUP_FOLDER)));
3074
  }
3075
 
3076
+ // ** Function to get filedata for a directory when adding custom media directory */
3077
  public function browseContent() {
3078
  if ( !current_user_can( 'manage_options' ) ) {
3079
  wp_die(__('You do not have sufficient permissions to access this page.','shortpixel-image-optimiser'));
3080
  }
 
3081
  $root = self::getCustomFolderBase();
3082
+ $fs = \wpSPIO()->filesystem();
3083
 
3084
  $postDir = rawurldecode($root.(isset($_POST['dir']) ? trim($_POST['dir']) : null ));
3085
  // set checkbox if multiSelect set to true
3089
 
3090
  if( file_exists($postDir) ) {
3091
 
3092
+
3093
+ $dir = $fs->getDirectory($postDir);
3094
+ $files = $dir->getFiles();
3095
+ $subdirs = $fs->sortFiles($dir->getSubDirectories()); // runs through FS sort.
3096
+
3097
+ // $files = scandir($postDir);
3098
  $returnDir = substr($postDir, strlen($root));
3099
 
3100
+ //natcasesort($files);
3101
 
3102
+ if( count($subdirs) > 0 ) {
3103
  echo "<ul class='jqueryFileTree'>";
3104
+ foreach($subdirs as $dir ) {
3105
 
3106
+ $dirpath = $dir->getPath();
3107
+ $dirname = $dir->getName();
3108
+ if($dirname == 'ShortpixelBackups' || ShortPixelMetaFacade::isMediaSubfolder($dirname, false)) continue;
3109
 
3110
+ $htmlRel = str_replace("'", "&apos;", $returnDir . $dirname);
3111
+ $htmlName = htmlentities($dirname);
3112
+ //$ext = preg_replace('/^.*\./', '', $file);
3113
 
3114
+ if( $dir->exists() ) {
3115
  //KEEP the spaces in front of the rel values - it's a trick to make WP Hide not replace the wp-content path
3116
+ // if( is_dir($postDir . $file) && (!$onlyFiles || $onlyFolders) ) {
3117
  echo "<li class='directory collapsed'>{$checkbox}<a rel=' " .$htmlRel. "/'>" . $htmlName . "</a></li>";
3118
+ /* } else if (!$onlyFolders || $onlyFiles) {
3119
  echo "<li class='file ext_{$ext}'>{$checkbox}<a rel=' " . $htmlRel . "'>" . $htmlName . "</a></li>";
3120
+ } */
3121
  }
3122
  }
3123
 
3260
  'filesTodo' => $stats['totalFiles'] - $stats['totalProcessedFiles'],
3261
  'estimated' => $this->_settings->optimizeUnlisted || $this->_settings->optimizeRetina ? 'true' : 'false',
3262
  /* */
3263
+ 'iconsUrl' => base64_encode(wpSPIO()->plugin_url('res/img'))
3264
  ))),
3265
  'cookies' => array()
3266
  ));
3273
 
3274
  // TODO - Part of the folder model.
3275
  public static function getCustomFolderBase() {
3276
+ Log::addDebug('Call to legacy function getCustomFolderBase');
3277
+ $fs = \wpSPIO()->filesystem();
3278
+ $dir = $fs->getWPFileBase();
3279
+ return $dir->getPath();
 
 
 
3280
  }
3281
 
3282
+ // @TODO - Should be part of folder model
3283
+ /* Seems not in use @todo marked for removal.
3284
  protected function fullRefreshCustomFolder($path, &$notice) {
3285
  $folder = $this->spMetaDao->getFolder($path);
3286
  $diff = $folder->checkFolderContents(array('ShortPixelCustomMetaDao', 'getPathFiles'));
3287
+ } */
3288
+
3289
 
3290
  // @todo - Should be part of folder model
3291
+ // @param force boolean Force a recheck.
3292
+ public function refreshCustomFolders($force = false) {
3293
  $customFolders = array();
3294
+ $fs = \wpSPIO()->fileSystem();
3295
+
3296
  if($this->_settings->hasCustomFolders) {
3297
  $customFolders = $this->spMetaDao->getFolders();
3298
  foreach($customFolders as $folder) {
3299
+
3300
+ try {
3301
+ $mt = $folder->getFolderContentsChangeDate();
3302
+ }
3303
+ catch(ShortPixelFileRightsException $ex) {
3304
+ Notices::addWarning($ex->getMessage());
3305
+ }
3306
+
3307
+ if($mt > strtotime($folder->getTsUpdated()) || $force) {
3308
+ // when forcing, set to never updated.
3309
+ if ($force)
3310
+ {
3311
+ $folder->setTsUpdated(date("Y-m-d H:i:s", 0) ); //
3312
+ $this->spMetaDao->update($folder);
3313
+ }
3314
+
3315
+ $fsFolder = $fs->getDirectory($folder->getPath());
3316
+ if ($fsFolder->exists())
3317
+ $this->spMetaDao->refreshFolder($fsFolder);
3318
+ else {
3319
+ Log::addWarn('Custom folder does not exist: ' . $fsFolder->getPath() );
3320
+ }
3321
+
3322
+ }
3323
+ /* if($folder->getPath() === $ignore) continue;
3324
  try {
3325
+
3326
  $mt = $folder->getFolderContentsChangeDate();
3327
  if($mt > strtotime($folder->getTsUpdated())) {
3328
  $fileList = $folder->getFileList(strtotime($folder->getTsUpdated()));
3339
  } else {
3340
  $notice = array("status" => "error", "msg" => $ex->getMessage());
3341
  }
3342
+ }*/
3343
+ } // folders
3344
  }
3345
  return $customFolders;
3346
  }
3350
  */
3351
  public static function alterHtaccess( $clear = false ){
3352
  // [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
3353
+ /* Plugin init is before loading these admin scripts. So it can happen misc.php is not yet loaded */
3354
+ if (! function_exists('insert_with_markers'))
3355
+ {
3356
+ Log::addWarn('AlterHtaccess Called before WP init');
3357
+ return;
3358
+ //require_once( ABSPATH . 'wp-admin/includes/misc.php' );
3359
+ }
3360
  $upload_dir = wp_upload_dir();
3361
  $upload_base = trailingslashit($upload_dir['basedir']);
3362
 
3500
  if($validate) {
3501
  $args['body']['DomainCheck'] = get_site_url();
3502
  $args['body']['Info'] = get_bloginfo('version') . '|' . phpversion();
3503
+ $imageCount = WpShortPixelMediaLbraryAdapter::countAllProcessable($this->_settings);
3504
  $args['body']['ImagesCount'] = $imageCount['mainFiles'];
3505
  $args['body']['ThumbsCount'] = $imageCount['totalFiles'] - $imageCount['mainFiles'];
3506
  $argsStr .= "&DomainCheck={$args['body']['DomainCheck']}&Info={$args['body']['Info']}&ImagesCount={$imageCount['mainFiles']}&ThumbsCount={$args['body']['ThumbsCount']}";
3655
  return;
3656
  }
3657
 
3658
+ $fs = \wpSPIO()->filesystem();
3659
+ $file = $fs->getAttachedFile($id);
3660
  $data = ShortPixelMetaFacade::sanitizeMeta(wp_get_attachment_metadata($id));
3661
  $itemHandler = new ShortPixelMetaFacade($id);
3662
  $meta = $itemHandler->getMeta();
3663
 
3664
+ $fileExtension = strtolower( $file->getExtension() );
3665
  $invalidKey = !$this->_settings->verifiedKey;
3666
  $quotaExceeded = $this->_settings->quotaExceeded;
3667
  $renderData = array("id" => $id, "showActions" => (current_user_can( 'manage_options' ) || current_user_can( 'upload_files' ) || current_user_can( 'edit_posts' )));
3693
  && !($data['ShortPixelImprovement'] == 0 && isset($data['ShortPixel']['WaitingProcessing'])) //for images that erroneously have ShortPixelImprovement = 0 when WaitingProcessing
3694
  ) { //already optimized
3695
  $thumbsOptList = isset($data['ShortPixel']['thumbsOptList']) ? $data['ShortPixel']['thumbsOptList'] : array();
3696
+ list($thumbsToOptimizeList, $sizesCount) = $this->getThumbsToOptimize($data, $file->getFullPath());
3697
 
3698
  $renderData['status'] = $fileExtension == "pdf" ? 'pdfOptimized' : 'imgOptimized';
3699
  $renderData['percent'] = $this->optimizationPercentIfPng2Jpg($data);
3700
  $renderData['bonus'] = ($data['ShortPixelImprovement'] < 5);
3701
+ $renderData['backup'] = $this->getBackupFolderAny($file->getFullPath(), $sizesCount? $data['sizes'] : array());
3702
  $renderData['type'] = isset($data['ShortPixel']['type']) ? $data['ShortPixel']['type'] : '';
3703
  $renderData['invType'] = ShortPixelAPI::getCompressionTypeName($this->getOtherCompressionTypes(ShortPixelAPI::getCompressionTypeCode($renderData['type'])));
3704
  $renderData['thumbsTotal'] = $sizesCount;
3715
  $renderData['quotaExceeded'] = $quotaExceeded;
3716
  $webP = 0;
3717
  if($extended) {
3718
+ if(file_exists(dirname($file->getFullPath()) . '/' . ShortPixelAPI::MB_basename($file->getFullPath(), '.'.$fileExtension) . '.webp' )){
3719
  $webP++;
3720
  }
3721
  if(isset($data['sizes'])) {
3722
  foreach($data['sizes'] as $key => $size) {
3723
  if (strpos($key, ShortPixelMeta::WEBP_THUMB_PREFIX) === 0) continue;
3724
  $sizeName = $size['file'];
3725
+ if(file_exists(dirname($file->getFullPath()) . '/' . ShortPixelAPI::MB_basename($sizeName, '.'.$fileExtension) . '.webp' )){
3726
  $webP++;
3727
  }
3728
  }
3783
  * @return array Array of Thumbs to Optimize - only the filename - , and count of sizes not excluded ...
3784
  */
3785
  function getThumbsToOptimize($data, $filepath) {
3786
+ // This function moved, but lack of other destination.
3787
  return WpShortPixelMediaLbraryAdapter::getThumbsToOptimize($data, $filepath);
3788
 
3789
  }
3803
  * @param array $columns Array of colums sortable
3804
  * @todo Should be part of media library controller. ( is request best hook for this?)
3805
  */
 
3806
  function columnOrderFilterBy($vars) {
3807
  if ( isset( $vars['orderby'] ) && 'ShortPixel Compression' == $vars['orderby'] ) {
3808
  $vars = array_merge( $vars, array(
3810
  'orderby' => 'meta_value_num',
3811
  ) );
3812
  }
3813
+ if ( 'upload.php' == $GLOBALS['pagenow'] && isset( $_GET['shortpixel_status'] ) ) {
3814
 
3815
+ $status = sanitize_text_field($_GET['shortpixel_status']);
3816
  $metaKey = '_shortpixel_status';
3817
+ //$metaCompare = $status == 0 ? 'NOT EXISTS' : ($status < 0 ? '<' : '=');
3818
+
3819
+ if ($status == 'all')
3820
+ return $vars; // not for us
3821
+
3822
+ switch($status)
3823
+ {
3824
+ case "opt":
3825
+ $status = ShortPixelMeta::FILE_STATUS_SUCCESS;
3826
+ $metaCompare = ">="; // somehow this meta stores optimization percentage.
3827
+ break;
3828
+ case "unopt":
3829
+ $status = ShortPixelMeta::FILE_STATUS_UNPROCESSED;
3830
+ $metaCompare = "NOT EXISTS";
3831
+ break;
3832
+ case "pending":
3833
+ $status = ShortPixelMeta::FILE_STATUS_PENDING;
3834
+ $metaCompare = "=";
3835
+ break;
3836
+ case "error":
3837
+ $status = -1;
3838
+ $metaCompare = "<=";
3839
+ break;
3840
+
3841
+ }
3842
 
3843
  $vars = array_merge( $vars, array(
3844
  'meta_query' => array(
3850
  )
3851
  ));
3852
  }
3853
+
3854
  return $vars;
3855
  }
3856
 
3863
  if ( $scr->base !== 'upload' ) return;
3864
 
3865
  $status = filter_input(INPUT_GET, 'shortpixel_status', FILTER_SANITIZE_STRING );
3866
+ // $selected = (int)$status > 0 ? $status : 0;
3867
+ /* $args = array(
3868
  'show_option_none' => 'ShortPixel',
3869
  'name' => 'shortpixel_status',
3870
  'selected' => $selected
3871
+ ); */
3872
  // wp_dropdown_users( $args );
3873
+ $options = array(
3874
+ 'all' => __('All Images', 'shortpixel-image-optimiser'),
3875
+ 'opt' => __('Optimized', 'shortpixel-image-optimiser'),
3876
+ 'unopt' => __('Unoptimized', 'shortpixel-image-optimiser'),
3877
+ // 'pending' => __('Pending', 'shortpixel-image-optimiser'),
3878
+ // 'error' => __('Errors', 'shortpixel-image-optimiser'),
3879
+ );
3880
 
3881
+ echo "<select name='shortpixel_status' id='shortpixel_status'>\n";
3882
+ foreach($options as $optname => $optval)
3883
+ {
3884
+ $selected = ($status == $optname) ? 'selected' : '';
3885
+ echo "<option value='". $optname . "' $selected>" . $optval . "</option>\n";
3886
+ }
3887
+ echo "</select>";
3888
+
3889
+ /*echo("<select name='shortpixel_status' id='shortpixel_status'>\n"
3890
  . "\t<option value='0'" . ($status == 0 ? " selected='selected'" : "") . ">All images</option>\n"
3891
  . "\t<option value='2'" . ($status == 2 ? " selected='selected'" : "") . ">Optimized</option>\n"
3892
  . "\t<option value='none'" . ($status == 'none' ? " selected='selected'" : "") . ">Unoptimized</option>\n"
3893
  . "\t<option value='1'" . ($status == 1 ? " selected='selected'" : "") . ">Pending</option>\n"
3894
  . "\t<option value='-1'" . ($status < 0 ? " selected='selected'" : "") . ">Errors</option>\n"
3895
+ . "</select>"); */
3896
  }
3897
 
3898
  /** Calculates Optimization if PNG2Jpg does something
3935
  */
3936
  public function onDeleteImage($post_id) {
3937
  $itemHandler = new ShortPixelMetaFacade($post_id);
3938
+ $urlsPaths = $itemHandler->getURLsAndPATHs(true, false, true, array(), true, true);
3939
  if(count($urlsPaths['PATHs'])) {
3940
  $this->maybeDumpFromProcessedOnServer($itemHandler, $urlsPaths);
3941
  $this->deleteBackupsAndWebPs($urlsPaths['PATHs']);
3942
  }
3943
+ $itemHandler->deleteItemCache();
3944
  return $itemHandler; //return it because we call it also on replace and on replace we need to follow this by deleting SP metadata, on delete it
3945
  }
3946
 
3956
  return;
3957
  }
3958
 
3959
+
3960
+ $fs = \wpSPIO()->filesystem();
3961
+
3962
  $backupFolder = trailingslashit($this->getBackupFolder($paths[0]));
3963
  foreach($paths as $path) {
3964
  $pos = strrpos($path, ".");
3965
+ $pathFile = $fs->getFile($path);
3966
  if ($pos !== false) {
3967
  //$webpPath = substr($path, 0, $pos) . ".webp";
3968
  //echo($webpPath . "<br>");
3969
+ $file = $fs->getFile(substr($path, 0, $pos) . ".webp");
3970
+ $file->delete();
3971
+ $file = $fs->getFile(substr($path, 0, $pos) . "@2x.webp");
3972
+ $file->delete();
3973
  }
3974
  //delte also the backups for image and retina correspondent
3975
+ $fileName = $pathFile->getFileName();
3976
+ $extension = $pathFile->getExtension();
3977
+
3978
+ $backupFile = $fs->getFile($backupFolder . $fileName);
3979
+ if ($backupFile->exists())
3980
+ $backupFile->delete();
3981
+ //@unlink($backupFolder . $fileName);
3982
+
3983
+ $backupFile = $fs->getFile($backupFolder . preg_replace("/\." . $extension . "$/i", '@2x.' . $extension, $fileName));
3984
+ if ($backupFile->exists())
3985
+ $backupFile->delete();
3986
+
3987
+ // @unlink($backupFolder . preg_replace("/\." . $extension . "$/i", '@2x.' . $extension, $fileName));
3988
  }
3989
  }
3990
  //
4003
  return $defaults;
4004
  }
4005
 
4006
+ // @todo move NGG specific function to own integration
4007
  public function nggColumns( $defaults ) {
4008
  $this->nggColumnIndex = count($defaults) + 1;
4009
  add_filter( 'ngg_manage_images_column_' . $this->nggColumnIndex . '_header', array( &$this, 'nggColumnHeader' ) );
4063
 
4064
  // @todo Should be utility function
4065
  static public function formatBytes($bytes, $precision = 2) {
4066
+ return \ShortPixelTools::formatBytes($bytes, $precision);
 
 
 
 
4067
 
 
 
 
4068
  }
4069
 
4070
  /** Checks if file can be processed. Mainly against exclusion
4087
  return self::_isProcessablePath($path, $excludeExtensions, $excludePatterns);
4088
  }
4089
 
4090
+ /** @todo pretty much every caller of this function already has a path. Check if get/attached/file is really needed -again- */
4091
  static public function _isProcessable($ID, $excludeExtensions = array(), $excludePatterns = array(), $meta = false) {
4092
+ $file = \wpSPIO()->filesystem()->getAttachedFile($ID);
4093
+ $path = $file->getFullPath(); //get the full file PATH
4094
+
4095
  if(isset($excludePatterns) && is_array($excludePatterns)) {
4096
  foreach($excludePatterns as $excludePattern) {
4097
  $type = $excludePattern["type"];
4180
  */
4181
  static public function folderSize($path) {
4182
  $total_size = 0;
4183
+ $fs = wpSPIO()->filesystem();
4184
+ $dir = $fs->getDirectory($path);
4185
+
4186
+ if($dir->exists()) {
4187
+ $files = $dir->getFiles(); // @todo This gives a warning if directory is not writable.
4188
+ $subdirs = $dir->getSubDirectories();
4189
+
4190
  } else {
4191
  return $total_size;
4192
  }
4193
+ //$cleanPath = rtrim($path, '/'). '/';
4194
+ foreach($files as $file)
4195
+ {
4196
+ $total_size += $file->getFileSize();
4197
+ }
4198
+
4199
+ foreach($subdirs as $dir)
4200
+ {
4201
+ $total_size += self::folderSize($dir->getPath());
4202
+ }
4203
+
4204
+ return $total_size;
4205
+
4206
+ /* foreach($files as $t) {
4207
  if ($t<>"." && $t<>"..")
4208
  {
4209
  $currentFile = $cleanPath . $t;
4216
  $total_size += $size;
4217
  }
4218
  }
4219
+ } */
4220
  return $total_size;
4221
  }
4222
 
4394
  <div id="shortpixel-hs-button-blind" class="shortpixel-hs-button-blind"></div>
4395
  <div id="shortpixel-hs-tools" class="shortpixel-hs-tools">
4396
  <a href="javascript:shortpixelToggleHS();" class="shortpixel-hs-tools-docs" title="<?php _e('Search through our online documentation.', 'shortpixel-image-optimiser'); ?>">
4397
+ <img src="<?php echo(wpSPIO()->plugin_url('res/img/notes-sp.png'));?>" style="margin-bottom: 2px;width: 36px;">
4398
  </a>
4399
  </div>
4400
  <script>
class/wp-shortpixel-settings.php CHANGED
@@ -233,7 +233,15 @@ class WPShortPixelSettings extends ShortPixel\ShortPixelModel {
233
  }
234
 
235
  public function setOpt($key, $val) {
236
- $ret = update_option($key, $val, 'no');
 
 
 
 
 
 
 
 
237
 
238
  //hack for the situation when the option would just not update....
239
  if($ret === false && !is_array($val) && $val != get_option($key)) {
@@ -245,7 +253,7 @@ class WPShortPixelSettings extends ShortPixel\ShortPixelModel {
245
  wp_cache_delete( $key, 'options' );
246
  }
247
  delete_option($key);
248
- add_option($key, $val, '', 'no');
249
 
250
  // still not? try the DB way...
251
  if($ret === false && $val != get_option($key)) {
@@ -254,7 +262,7 @@ class WPShortPixelSettings extends ShortPixel\ShortPixelModel {
254
  $rows = $wpdb->get_results($sql);
255
  if(count($rows) === 0) {
256
  $wpdb->insert($wpdb->prefix.'options',
257
- array("option_name" => $key, "option_value" => (is_array($val) ? serialize($val) : $val), "autoload" => "no"),
258
  array("option_name" => "%s", "option_value" => (is_numeric($val) ? "%d" : "%s")));
259
  } else { //update
260
  $sql = "update {$wpdb->prefix}options SET option_value=" .
@@ -268,7 +276,7 @@ class WPShortPixelSettings extends ShortPixel\ShortPixelModel {
268
  //tough luck, gonna use the bomb...
269
  wp_cache_flush();
270
  delete_option($key);
271
- add_option($key, $val, '', 'no');
272
  }
273
  }
274
  }
233
  }
234
 
235
  public function setOpt($key, $val) {
236
+ $autoload = true;
237
+ /*if (isset(self::$_optionsMap[$key]))
238
+ {
239
+ if (self::$_optionsMap[$key]['group'] == 'options')
240
+ $autoload = true; // add most used to autoload, because performance.
241
+
242
+ } */
243
+
244
+ $ret = update_option($key, $val, $autoload);
245
 
246
  //hack for the situation when the option would just not update....
247
  if($ret === false && !is_array($val) && $val != get_option($key)) {
253
  wp_cache_delete( $key, 'options' );
254
  }
255
  delete_option($key);
256
+ add_option($key, $val, '', $autoload);
257
 
258
  // still not? try the DB way...
259
  if($ret === false && $val != get_option($key)) {
262
  $rows = $wpdb->get_results($sql);
263
  if(count($rows) === 0) {
264
  $wpdb->insert($wpdb->prefix.'options',
265
+ array("option_name" => $key, "option_value" => (is_array($val) ? serialize($val) : $val), "autoload" => $autoload),
266
  array("option_name" => "%s", "option_value" => (is_numeric($val) ? "%d" : "%s")));
267
  } else { //update
268
  $sql = "update {$wpdb->prefix}options SET option_value=" .
276
  //tough luck, gonna use the bomb...
277
  wp_cache_flush();
278
  delete_option($key);
279
+ add_option($key, $val, '', $autoload);
280
  }
281
  }
282
  }
readme.txt CHANGED
@@ -2,22 +2,22 @@
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.6
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- Speed up your website and boost your SEO by compressing old & new images and PDFs. Compatible with any gallery, slider or ecommerce plugin.
12
 
13
  == Description ==
14
 
15
  **A freemium easy to use, comprehensive, stable and frequently updated image compression plugin supported by the friendly team that created it. :)**
16
 
17
  Increase your website's SEO ranking, number of visitors and ultimately your sales by optimizing any image or PDF document on your website.
18
- ShortPixel is an easy to use, lightweight, install-and-forget-about-it <a href="https://shortpixel.com" target="_blank">image optimization</a> plugin that can compress all your past images and PDF documents with a single click. New images are automatically resized/rescaled and optimized on the fly, in the background.
19
 
20
- **Ready for a quick DEMO? Test <a href="https://sandboxwordpress.com/?htmldata=https://shortpixel.com/sp.html&slug=shortpixel-image-optimiser&redirect=plugins.php&title=Test%20SHORTPIXEL%20Now!&ga=UA-55918546-1" target="_blank">here</a> or <a href="http://poopy.life/create?url=/wp-admin/admin.php?page=sandbox" target="_blank">here</a>.**
21
 
22
  Short Pixel uses minimal resources and works well with any shared, cloud, VPS or dedicated web hosting. It can optimize any image you have on your website even the images that aren't listed in Media Library like those in galleries like <a href="https://wordpress.org/plugins/nextgen-gallery/" target="_blank">NextGEN</a>, <a href="https://wordpress.org/plugins/modula-best-grid-gallery/" target="_blank">Modula</a> or added directly via FTP!
23
 
@@ -29,7 +29,7 @@ Make an instant <a href="http://shortpixel.com/image-compression-test" target="_
29
 
30
  **Why is ShortPixel the best choice when it comes to image optimization or PDF compression?**
31
 
32
- * popular plugin with over 100,000 active installations according to WordPress
33
  * compress JPG, PNG, GIF (still or animated) images and also PDF documents
34
  * option to automatically convert PNG to JPG if that will result in smaller images. Ideal for large images in PNG format.
35
  * no file size limit
@@ -71,7 +71,7 @@ Check out <a href="https://shortpixel.com/pricing" target="_blank">our prices</a
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]
@@ -214,15 +214,30 @@ The ShortPixel team is here to help. <a href="https://shortpixel.com/contact">Co
214
 
215
  The ShortPixel Image Optimiser plugin calls the following actions and filters:
216
  > do_action( 'shortpixel_image_optimised', $post_id ); //upon successful optimization
 
217
  > do_action("shortpixel_before_restore_image", $post_id); //before restoring an image from backup
 
218
  > do_action("shortpixel_after_restore_image", $post_id); //after succesful restore
 
219
  > apply_filters("shortpixel_backup_folder", $backup_folder, $main_file_path, $sizes); //just before returning the ShortPixel backup folder, usually /wp-content/uploads/ShortpixelBackups. The $sizes are the sizes array from metadata.
 
220
  > apply_filters('shortpixel_image_exists', file_exists($path), $path, $post_id); //post ID is not always set, only if it's an image from Media Library
 
221
  > apply_filters('shortpixel_image_urls', $URLs, $post_id) // filters the URLs that will be sent to optimization, $URLs is a plain array
222
 
 
 
223
  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,9 +261,42 @@ define('SHORTPIXEL_CUSTOM_THUMB_INFIXES', '-uae'); will handle custom thumbnails
246
 
247
  == Changelog ==
248
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  = 4.14.6 =
 
250
  Release date: 9th October 2019
251
- * Don't convert to <picture> the <img>s with backgrounds.
252
  * Remove unused eval() call.
253
  * Restore the validate button next to API Key but change label to "Save and validate"
254
  * Fixed: PNGtoJPG issue with already uploaded images
@@ -263,9 +311,10 @@ Release date: 9th October 2019
263
  * Fixed: notice in filemodel due meta-facade feeding array
264
  * Fixed: bug in File2Url in filesystemcontroller
265
  * Fixed: download issue in attempt to remote download
266
- * Language – 0 new strings added, 0 updated, 0 fuzzied, and 0 obsoleted
267
 
268
  = 4.14.5 =
 
269
  Release date: 29th August 2019
270
  * If constant SHORTPIXEL_USE_DOUBLE_WEBP_EXTENSION is defined as true, use double extension for WebP (.jpg.webp)
271
  * Fixed: Javascript - String.prototype causes errors on React apps
@@ -276,6 +325,7 @@ Release date: 29th August 2019
276
  * Language – 0 new strings added, 1 updated, 0 fuzzied, and 0 obsoleted
277
 
278
  = 4.14.4 =
 
279
  Release date: 19th August 2019
280
  * Check if unlisted thumbnails present for already optimized images (in case the thumbnails were added later) in Media Library list and when doing bulk. This also integrates with the Unicode plugin.
281
  * If JSON PHP module not present, add a proper error
@@ -345,189 +395,5 @@ Release date: 10th April 2019
345
  * Fixed: Restoring an Other Media item and then Optimizing it again optimizes it Lossless
346
  * fix generating the WebP <picture> tags when the images are either on a subdomain or on a CDN domain having the same root domain as the main site.
347
 
348
- = 4.12.8 =
349
-
350
- Release date: 25th February 2019
351
-
352
- * fix CSS for the top bar ShortPixel icon on post pages
353
- * i18n some text which was left out by mistake
354
- * include the green "CAN" WebP image which shows that the .htaccess works with WebP images having extensions like .jpg.webp
355
- * display notice if ShortPixel Adaptive Images is active and the Generate WebP markup option is checked. Do not generate the WebP markup in this case.
356
-
357
- = 4.12.7 =
358
-
359
- Release date: 12th February 2019
360
-
361
- * solved conflicting WebP file names when image.jpg and image.png exist in the same folder - use image.jpg.webp filename.
362
- * fixed .htaccess rules for some Apache versions which seemingly don't honour the RewriteRule backreferences in the RewriteCond's (Apache bug?)
363
- * remove the WebP .htaccess rules on plugin deactivation and add them back on plugin activation
364
- * fixed alt attribute for <picture> tags - now it is included properly only on the enclosed <img> tag.
365
-
366
- = 4.12.6 =
367
-
368
- Release date: 27th January 2019
369
-
370
- * Improvements to the .htaccess WebP method
371
- * Improve performance of backup deletion - get rid of unnecessary checks
372
- * Fixed: wrong calculation of remaining credits
373
- * Fixed: discrepancy between the description of the exclude size option and the behaviour for the exact size case.
374
-
375
- = 4.12.5 =
376
-
377
- Release date: 10th Ianuary 2019
378
-
379
- * change the JS name in order to circumveit cache problem on many WP installs
380
- * sorting the Media Library entries by ShortPixel optimization: also sort based on compression level
381
- * Fixed: case sensitive search for guid duplicates of image posts (needed for finding Polylang versions)
382
- * Fixed: the data-lazy-src/srcset detection for WebP
383
- * Improvements to the Deliver WebP options and especially messages with caveats
384
- * Load the ShortPixel CSS only on admin pages that need it
385
-
386
- = 4.12.4 =
387
-
388
- Release date: 27th December 2018
389
-
390
- * Fixed: shortpixel-thumbnails-regenerated action when not all the thumbnails were regenerated
391
-
392
- = 4.12.3 =
393
-
394
- Release date: 19th December 2018
395
-
396
- * Fixed: error in getting the lazy- attributes of <img> for WebP handling.
397
-
398
- = 4.12.2 =
399
-
400
- Release date: 13th December 2018
401
-
402
- * Improved: The Webp options interface. Now the user can implement Webp images both via .htaccess and by altering the page code on the server before being sent to the browser.
403
- * Improved: The settings data handling interface in the Plugin deactivation dialogue. Now the option to delete or keep the user settings on plugin deletion is more clear.
404
- * Added: Option to download image with thumbnails in a single archive file, to speed-up the optimization.
405
- * Added: A "shortpixel_get_backup" filter, which receives the local path of the media image and returns the ShortPixel backup path, if a backup image exists
406
- * Added: The "Simple Image Sizes" plugin to the conflicting plugins list
407
- * Added: A new compatibility check for the "Jetpack" plugin, alerting the user about potential overlapping functionality
408
- * Added: A safety alert before switching to Code Altering mode (where IMG tgs get inserted into PICTURE tags, to better serve Webp images)
409
- * Added: Enhanced "Envira" plugin compatibility by adding more suffixes to be looked for: _tl, _tr, _bl, _br
410
- * Added: More customized FAQ suggestions in the HelpScout Beacon helper, to address each Plugin TAB separately
411
- * Fixed: The post-uninstall redirect when uninstalling a plugin from within the respective plugin's Settings page
412
- * Fixed: The credits display on the Statistics page
413
- * Fixed: Refreshing a plugin page now loads directly in the previously selected TAB
414
- * Fixed: Removed a stray "SP_CELL_MESSAGE" div from the interface
415
-
416
- = 4.12.1 =
417
-
418
- Release date: 6th November 2018
419
-
420
- * Fix WebP replacement for lazy-loaded images
421
- * Fix WebP replacement with output buffering on some WP installs
422
-
423
- = 4.12.0 =
424
-
425
- Release date: 31st October 2018
426
-
427
- * Generate WebP &lt;picture&gt; tags - use the output buffer instead of the_content which is not triggered by some themes on all content.
428
- * compatibility of the WebP &lt;picture&gt; tag with lazy loading plugins (that support &lt;picture&gt;)
429
- * Compatibility with Polylang.
430
- * hooks to be used by thumbnail regeneration plugins: 'shortpixel-thumbnails-before-regenerate' and 'shortpixel-thumbnails-regenerated'
431
- * Proper error message when the custom tables cannot be created.
432
- * exclude the PNGs from conversion to JPEG when they match the exclude patterns.
433
- * properly warn when cURL is not enabled that Cloudflare integration won't work.
434
- * send only one url for metadata thumbnails which correspond to the same physical file.
435
- * JavaScript delayed init for cases when some plugins deffer the load of javascript files.
436
- * fix identifying filenames with basename length == 3 as retina
437
- * display improvements for the bulk errors list
438
-
439
- = 4.11.3 =
440
-
441
- Release date: 27th September 2018
442
-
443
- * fix error when metadata is returned as string by wp_get_attachment_metadata (happens to PDFs when using PDF Image Generator)
444
- * remove the configurable Affiliate code as per new WP Themes rules.
445
-
446
- = 4.11.2 =
447
-
448
- Release date: 30th August 2018
449
-
450
- * Fix "Image files are missing" warning when thumbails optimization is activated but all the thumbnails are excepted from optimization and the bulk is ran a second time.
451
- * Fix not saving properly the metadata on some situations
452
-
453
- = 4.11.1 =
454
-
455
- Release date: 28th August 2018
456
-
457
- * compatibility with the MediaPress plugin
458
- * new action to be called by when thumbnails are regenerated: shortpixel-thumbnails-regenerated
459
- * accept '+' inside the e-mail address
460
- * fix optimization not working on internationalized domain names
461
- * better count of the not optimized thumbs for an image, in some circumstances
462
- * fallback to ABSPATH when get_home_path() returns '/'
463
- * fix settings tabs navigation when url ends with #/
464
- * extract all release notes < 4.9 from readme.txt into changelog.txt
465
- * display the thumbnail name for some errors which refer only to a specific thumbnail.
466
- * use update_post_meta() instead of wp_update_attachment_metadata() for cases when other plugins cannot be concerned by the meta change (specific to ShortPixel)
467
- * add the attributes of the original <img> to the <picture> replacement tag, in case the "Generate WebP Markup" option is active.
468
- * fix action buttons in media edit view overflowing their box
469
- * restore full compatibility with WP < 4.1 by checking first before using wp_json_encode
470
- * fix admin when domain is internationalized but the setting in admin uses the punycode-encrypted version
471
-
472
- = 4.11.0 =
473
-
474
- Release date: 3rd July 2018
475
-
476
- * add bulk menu options: restore, reoptimize
477
- * filter the media list by optimization status
478
- * sort the media list by optimization status
479
- * do not display the Media Library (years) folders in the selection list for Other Media folders
480
- * force PNG 2 JPG conversion option
481
- * integrate with Gravity Forms
482
- * integrate with WP Stateless
483
- * add several actions and a filter (see the Actions and Filters section of the readme)
484
- * UI improvements to the settings page
485
- * fix the WPML compatibility when converting from PNG
486
- * fix SELECT IN image counting bug on rare cases and when >100k records in wp_postmeta
487
- * add option to delete ShortPixel settings and give feedback form on deactivate plugin
488
-
489
- = 4.10.5 =
490
- * GDPR compliance
491
-
492
- = 4.10.4 =
493
- * replace back the PNG links when restoring a PNG converted to JPG
494
- * fix incompatibility with Dynamics 365 Integration plugin
495
- * improve restore capabilities after certain types of PNG to JPG errors which left the media item in an unconsistent state.
496
- * remove AUTH credentials on server too, if removed in plugin's settings.
497
- * more performance improvements to PNG 2 JPG conversion
498
- * fix replacing PNG urls having http:// instead of https:// for a SSL site. (and viceversa)
499
- * fix string not appearing in translations
500
-
501
- = 4.10.3 =
502
- * improvements to context help beacon
503
- * performance improvements to PNG to JPG conversion
504
-
505
- = 4.10.2 =
506
- * fix error when listing Other media in some circumstances
507
-
508
- = 4.10.1 =
509
- * fix missing file from commit
510
-
511
- = 4.10.0 =
512
- * option to exclude thumbnails from optimization
513
- * options to delete Cloudflare cache for optimized images
514
- * method to define affilate codes for themes
515
- * error message when restore could not be performed
516
- * better handling of situations with files with different owner but with write permissions for all
517
- * fix bug for inner resize when setting and unsetting the resize parameter
518
- * fix bug for third-party WebP thumbnails registered in the 'sizes' metadata array which were sent to optimization.
519
- * check if function mb_convert_encoding exists before using it
520
-
521
- = 4.9.1 =
522
- * fix error for older WP versions which don't have wp_raise_memory_limit
523
-
524
- = 4.9.0 =
525
- * inline help beacon
526
- * fix exclude patterns not working after last update
527
- * handle situations when not enough memory to convert from PNG to JPG.
528
- * fix particular situations where there was no 'file' property in the metadata.
529
- * fix slider optimized percent over the bulk warning box.
530
- * display the x close link for the bulk warning box.
531
-
532
  = EARLIER VERSIONS =
533
  * please refer to the changelog.txt file inside the plugin archive.
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.3
6
  Requires PHP: 5.3
7
+ Stable tag: 4.15.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ Speed up your website & boost your SEO by compressing old & new images and PDFs. WebP convert and optimize support.
12
 
13
  == Description ==
14
 
15
  **A freemium easy to use, comprehensive, stable and frequently updated image compression plugin supported by the friendly team that created it. :)**
16
 
17
  Increase your website's SEO ranking, number of visitors and ultimately your sales by optimizing any image or PDF document on your website.
18
+ ShortPixel is an easy to use, lightweight, install-and-forget-about-it <a href="https://shortpixel.com" target="_blank">image optimization</a> plugin that can compress all your past images and PDF documents with a single click. New images are automatically resized/rescaled and optimized on the fly, in the background. It's also compatible with any gallery, slider or ecommerce plugin.
19
 
20
+ **Ready for a quick DEMO? Test <a href="https://sandboxwordpress.com/?htmldata=https://shortpixel.com/sp.html&slug=shortpixel-image-optimiser&redirect=plugins.php&title=Test%20SHORTPIXEL%20Now!&ga=UA-55918546-1" target="_blank">here</a>.**
21
 
22
  Short Pixel uses minimal resources and works well with any shared, cloud, VPS or dedicated web hosting. It can optimize any image you have on your website even the images that aren't listed in Media Library like those in galleries like <a href="https://wordpress.org/plugins/nextgen-gallery/" target="_blank">NextGEN</a>, <a href="https://wordpress.org/plugins/modula-best-grid-gallery/" target="_blank">Modula</a> or added directly via FTP!
23
 
29
 
30
  **Why is ShortPixel the best choice when it comes to image optimization or PDF compression?**
31
 
32
+ * popular plugin with over 200,000 active installations according to WordPress
33
  * compress JPG, PNG, GIF (still or animated) images and also PDF documents
34
  * option to automatically convert PNG to JPG if that will result in smaller images. Ideal for large images in PNG format.
35
  * no file size limit
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]
214
 
215
  The ShortPixel Image Optimiser plugin calls the following actions and filters:
216
  > do_action( 'shortpixel_image_optimised', $post_id ); //upon successful optimization
217
+
218
  > do_action("shortpixel_before_restore_image", $post_id); //before restoring an image from backup
219
+
220
  > do_action("shortpixel_after_restore_image", $post_id); //after succesful restore
221
+
222
  > apply_filters("shortpixel_backup_folder", $backup_folder, $main_file_path, $sizes); //just before returning the ShortPixel backup folder, usually /wp-content/uploads/ShortpixelBackups. The $sizes are the sizes array from metadata.
223
+
224
  > 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
225
+
226
  > apply_filters('shortpixel_image_urls', $URLs, $post_id) // filters the URLs that will be sent to optimization, $URLs is a plain array
227
 
228
+ > apply_filters('shortpixel/db/chunk_size', $chunk); //the $chunk is the value ShortPixel chooses to use as number of selected records in one query (based on total table size), some hosts work better with a different value
229
+
230
  In order to define custom thumbnails to be picked up by the optimization you have two options, both comma separated defines:
231
+
232
  define('SHORTPIXEL_CUSTOM_THUMB_SUFFIXES', '_tl,_tr'); will handle custom thumbnails like image-100x100_tl.jpg
233
+
234
  define('SHORTPIXEL_CUSTOM_THUMB_INFIXES', '-uae'); will handle custom thumbnails like image-uae-100x100.jpg
235
+ define('SHORTPIXEL_USE_DOUBLE_WEBP_EXTENSION', true); will tell the plugin to create double extensions for the WebP image counterparts, for example image.jpg.webp for image.jpg
236
+
237
+ define("SHORTPIXEL_NOFLOCK", true); // don't use flock queue, only activate this when you have flock() denied errors on your installation.
238
+ .
239
+ define("SHORTPIXEL_EXPERIMENTAL_SECURICACHE", true); // adds timestamps to URLS, to prevent hitting the cache. Useful for persistent caches.
240
+
241
 
242
  == Screenshots ==
243
 
261
 
262
  == Changelog ==
263
 
264
+ = 4.15.0 =
265
+
266
+ Release date: 27th November 2019
267
+ * Ask if WebP files should be created before bulk optimization : checkbox added in Bulk page.
268
+ * Added filters for optimal chunk size when selecting records from wp_postmeta while bulk processing: 'shortpixel/db/chunk_size'.
269
+ * Optimize images also on edit-media screen.
270
+ * Experimental support for static cache firewalls, and sending images w/ timestamp: SHORTPIXEL_EXPERIMENTAL_SECURICACHE.
271
+ * Limit loading of JS / CSS to pages we do work on.
272
+ * Refactoring & speed optimizations: - new external class for gravity forms, nextgen and visual composer, new controller for global admin hooks, new front controller, updated plugin init, clean(ish) root file, database optimization(reduced number of queries), unlistedThumbs checker only fires when the optimize unlisted check is active, optimizations for filesystem and findFileByPattern.
273
+ * WPML Duplicates - Don't mark empty GUID's as duplicate.
274
+ * Fix broken styles on WP 5.3.
275
+ * Fix in case meta_value in processCountable has a WP_Error object instead of normal metadata.
276
+ * Fix on WP Engine when large wp_postmeta table: limit to 16K the size of the query with ID list.
277
+ * Fixed: error when log path is not writeable
278
+ * Fixed: double sends within the same images, can happen with something like WPML and it's duplicated media.
279
+ * Fixed: bug when searching unlisted images, in some circumstances.
280
+ * Fixed: max_execution_time when time is set lower than 0.
281
+ * Fixed: directories with no permission would give PHP error
282
+ * Fixed: pathToUrl now also works for paths outside wp uploads
283
+ * Fixed: check for DOING_AJAX on redirect to settings.
284
+ * Fixed: Shortpixel icon + exclamation mark in toolbar showing on every page load.
285
+ * Fixed: Add Custom media browser doesn't display files anymore
286
+ * Fixed: WebP option adds an extra border if image already has a border -> borders will not be replicated to <picture> tags.
287
+ * Fixed: Validating empty key doesn't show any message.
288
+ * Fixed: on Nginx writes .htaccess files.
289
+ * Fixed: Bug with safeGetAttachmentUrl for URLs that start with //.
290
+ * Fixed: New S3-Offload version breaks Shortpixel and offloading.
291
+ * Fixed: get_attached_file when S3-Offload is active, breaks other plugins.
292
+ * Fixed: crash when doing .htaccess files ( WP 5.3 specific ).
293
+ * Fixed: double file occurences on png2jpg in conjunction with s3offload.
294
+ * Language – 1 new strings added, 0 updated, 0 fuzzied, and 0 obsoleted
295
+
296
  = 4.14.6 =
297
+
298
  Release date: 9th October 2019
299
+ * Don't convert to &lt;picture&gt; the &lt;img&gt;s with backgrounds.
300
  * Remove unused eval() call.
301
  * Restore the validate button next to API Key but change label to "Save and validate"
302
  * Fixed: PNGtoJPG issue with already uploaded images
311
  * Fixed: notice in filemodel due meta-facade feeding array
312
  * Fixed: bug in File2Url in filesystemcontroller
313
  * Fixed: download issue in attempt to remote download
314
+ * Language – 0 new strings added, 1 updated, 0 fuzzied, and 0 obsoleted
315
 
316
  = 4.14.5 =
317
+
318
  Release date: 29th August 2019
319
  * If constant SHORTPIXEL_USE_DOUBLE_WEBP_EXTENSION is defined as true, use double extension for WebP (.jpg.webp)
320
  * Fixed: Javascript - String.prototype causes errors on React apps
325
  * Language – 0 new strings added, 1 updated, 0 fuzzied, and 0 obsoleted
326
 
327
  = 4.14.4 =
328
+
329
  Release date: 19th August 2019
330
  * Check if unlisted thumbnails present for already optimized images (in case the thumbnails were added later) in Media Library list and when doing bulk. This also integrates with the Unicode plugin.
331
  * If JSON PHP module not present, add a proper error
395
  * Fixed: Restoring an Other Media item and then Optimizing it again optimizes it Lossless
396
  * fix generating the WebP <picture> tags when the images are either on a subdomain or on a CDN domain having the same root domain as the main site.
397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  = EARLIER VERSIONS =
399
  * please refer to the changelog.txt file inside the plugin archive.
res/css/short-pixel.css CHANGED
@@ -18,6 +18,7 @@
18
  .sp-dropbtn.button {
19
  padding: 1px 24px 20px 5px;
20
  font-size: 20px;
 
21
  /*background-color: #4CAF50;
22
  color: white;
23
  border: none;*/
@@ -354,7 +355,7 @@ div.shortpixel-rate-us > a:focus {
354
  padding:0px 5px;
355
  margin-bottom: 4px;
356
  height:20px;
357
- line-height:16px;
358
  float:right;
359
  }
360
 
18
  .sp-dropbtn.button {
19
  padding: 1px 24px 20px 5px;
20
  font-size: 20px;
21
+ line-height: 28px;
22
  /*background-color: #4CAF50;
23
  color: white;
24
  border: none;*/
355
  padding:0px 5px;
356
  margin-bottom: 4px;
357
  height:20px;
358
+ /* line-height:16px; */
359
  float:right;
360
  }
361
 
res/css/short-pixel.min.css CHANGED
@@ -1 +1 @@
1
- .reset{font-weight:normal;font-style:normal}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.clearfix{zoom:1}.resumeLabel{float:right;line-height:30px;margin-right:20px;font-size:16px}.sp-dropbtn.button{padding:1px 24px 20px 5px;font-size:20px;cursor:pointer}.sp-dropdown{position:relative;display:inline-block}.sp-dropdown-content{display:none;right:0;position:absolute;background-color:#f9f9f9;min-width:190px;box-shadow:0 8px 16px 0 rgba(0,0,0,0.2);z-index:1}.sp-dropdown-content a{color:black;padding:12px 16px;text-decoration:none;display:block}.sp-dropdown-content a:hover{background-color:#f1f1f1}.sp-dropdown.sp-show .sp-dropdown-content{display:block}div.fb-like{transform:scale(1.3);-ms-transform:scale(1.3);-webkit-transform:scale(1.3);-o-transform:scale(1.3);-moz-transform:scale(1.3);transform-origin:bottom left;-ms-transform-origin:bottom left;-webkit-transform-origin:bottom left;-moz-transform-origin:bottom left;-webkit-transform-origin:bottom left}.wp-core-ui .button.button-alert,.wp-core-ui .button.button-alert:hover{background:#f79797}.wp-core-ui .button.remove-folder-button{min-width:120px}.sp-notice{background:#fff;border-left:4px solid #fff;-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);box-shadow:0 1px 1px 0 rgba(0,0,0,.1);padding:1px 12px}.sp-notice img{vertical-align:bottom}@media(max-width:1249px){.sp-notice{margin:5px 15px 2px}}.sp-notice-info{border-left-color:#00a0d2}.sp-notice-success{border-left-color:#46b450}.sp-notice-warning{border-left-color:#f1e02a}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)}}
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;line-height:28px;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}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;float:right}th.sortable.column-wp-shortPixel a,th.sorted.column-wp-shortPixel a{display:inline-block}.column-wp-shortPixel .sorting-indicator{display:inline-block}.wp-core-ui .bulk-play a.button .bulk-btn-img{display:inline-block;padding-top:6px}.wp-core-ui .bulk-play a.button .bulk-btn-txt{display:inline-block;text-align:right;line-height:1.3em;margin:11px 10px}.wp-core-ui .bulk-play a.button .bulk-btn-txt span.label{font-size:1.6em}.wp-core-ui .bulk-play a.button .bulk-btn-txt span.total{font-size:1.4em}.bulk-progress{padding:20px 32px 17px;background-color:#fff}.bulk-progress.bulk-stats>div{display:inline-block}.bulk-progress.bulk-stats>div.label{width:320px}.bulk-progress.bulk-stats>div.stat-value{width:80px;text-align:right}.short-pixel-bulk-page .progress{background-color:#ecedee;height:30px;position:relative;width:60%;display:inline-block;margin-right:28px;overflow:visible}.progress .progress-img{position:absolute;top:-10px;z-index:2;margin-left:-35px;line-height:48px;font-size:22px;font-weight:700}.progress .progress-img span{vertical-align:top;margin-left:-7px}.progress .progress-left{background-color:#1cbecb;bottom:0;left:0;position:absolute;top:0;z-index:1;font-size:22px;font-weight:700;line-height:28px;text-align:center;color:#fff}.bulk-estimate{font-size:20px;line-height:30px;vertical-align:top;display:inline-block}.wp-core-ui .button-primary.bulk-cancel{float:right;height:30px}.short-pixel-block-title{font-size:22px;font-weight:700;text-align:center;margin-bottom:30px}.sp-floating-block.bulk-slider-container{display:none}.sp-floating-block.sp-notice.bulk-notices-parent{padding:0;margin:0;float:right;margin-right:500px!important}.bulk-slider-container{margin-top:20px;min-height:300px;overflow:hidden}.bulk-slider-container h2{margin-bottom:15px}.bulk-slider-container span.filename{font-weight:400}.bulk-slider{display:table;margin:0 auto}.bulk-slider .bulk-slide{margin:0 auto;padding-left:120px;display:inline-block;font-weight:700}.bulk-slider .img-optimized,.bulk-slider .img-original{display:inline-block;margin-right:20px;text-align:center}.bulk-slider .img-optimized div,.bulk-slider .img-original div{max-height:450px;overflow:hidden}.bulk-slider .img-optimized img,.bulk-slider .img-original img{max-width:300px}.bulk-slider .img-info{display:inline-block;vertical-align:top;font-size:48px;max-width:150px;padding:10px 0 0 20px}.bulk-slide-images{display:inline-block;border:1px solid #1caecb;padding:15px 0 0 20px}p.settings-info{padding-top:0;color:#818181;font-size:13px!important}p.settings-info.shortpixel-settings-error{color:#c32525}.shortpixel-key-valid{font-weight:700}.shortpixel-key-valid .dashicons-yes:before{font-size:2em;line-height:25px;color:#3485ba;margin-left:-20px}.shortpixel-compression .shortpixel-compression-options{color:#999}.shortpixel-compression strong{line-height:22px}.shortpixel-compression .shortpixel-compression-options{display:inline-block}.shortpixel-compression label{width:158px;margin:0 -2px;background-color:#e2faff;font-weight:700;display:inline-block}.shortpixel-compression label span{text-align:center;font-size:18px;padding:8px 0;display:block}.shortpixel-compression label input{display:none}.shortpixel-compression input:checked+span{background-color:#0085ba;color:#f7f7f7}.shortpixel-compression .shortpixel-radio-info{min-height:60px}article.sp-tabs{position:relative;display:block;width:100%;margin:2em auto}article.sp-tabs section{position:absolute;display:block;top:1.8em;left:0;width:100%;max-width:100%;box-sizing:border-box;padding:10px 20px;z-index:0}article.sp-tabs section.sel-tab{box-shadow:0 3px 3px rgba(0,0,0,.1)}article.sp-tabs section .wp-shortpixel-tab-content{visibility:hidden}article.sp-tabs section.sel-tab .wp-shortpixel-tab-content{visibility:visible!important}article.sp-tabs section:first-child{z-index:1}article.sp-tabs section h2 a:focus,article.sp-tabs section#tab-resources a:focus{box-shadow:none;outline:0}article.sp-tabs section.sel-tab,article.sp-tabs section.sel-tab h2{color:#333;background-color:#fff;z-index:2}#tab-stats .sp-bulk-summary{position:absolute;right:0;top:0;z-index:100}.deliverWebpAlteringTypes,.deliverWebpSettings,.deliverWebpTypes{display:none}.deliverWebpTypes .sp-notice{color:red}.deliverWebpSettings{margin:16px 0}.deliverWebpSettings input:disabled+label{color:#818181}.deliverWebpAlteringTypes,.deliverWebpTypes{margin:16px 0 16px 16px}#png2jpg:not(:checked)~#png2jpgForce,#png2jpg:not(:checked)~label[for=png2jpgForce]{display:none}article.sp-tabs section #createWebp:checked~.deliverWebpSettings,article.sp-tabs section #deliverWebp:checked~.deliverWebpTypes,article.sp-tabs section #deliverWebpAltered:checked~.deliverWebpAlteringTypes{display:block}.shortpixel-help-link span.dashicons{text-decoration:none;margin-top:-1px}@media(min-width:1000px){section#tab-resources .col-md-6{display:inline-block;width:45%}}@media(max-width:999px){section#tab-resources .col-sm-12{display:inline-block;width:100%}}section#tab-resources .text-center{text-align:center}section#tab-resources p{font-size:16px}.wrap.short-pixel-bulk-page{margin-right:0}.sp-container{overflow:hidden;display:block;width:100%}.sp-floating-block{overflow:hidden;display:inline-block;float:left;margin-right:1.1%!important}.sp-full-width{width:98.8%;box-sizing:border-box}.sp-double-width{width:65.52%;box-sizing:border-box}.sp-single-width{width:32.23%;box-sizing:border-box}@media(max-width:1759px){.sp-floating-block{margin-right:1.3%!important}.sp-double-width,.sp-full-width{width:98.65%}.sp-single-width{width:48.7%}}@media(max-width:1249px){.sp-floating-block{margin-right:2%!important}.sp-double-width,.sp-full-width,.sp-single-width{width:97%}}.sp-tabs h2:before{content:none}.sp-column-actions-template+.sp-column-info{display:none}#wpadminbar .shortpixel-toolbar-processing .cssload-container{width:100%;height:24px;text-align:center;position:absolute;top:0;left:-1px}#wpadminbar .shortpixel-toolbar-processing.shortpixel-alert .cssload-container,#wpadminbar .shortpixel-toolbar-processing.shortpixel-quota-exceeded .cssload-container{display:none}#wpadminbar .shortpixel-toolbar-processing .cssload-speeding-wheel{width:24px;height:24px;opacity:.7;margin:0 auto;border:4px solid #1cbfcb;border-radius:50%;border-left-color:transparent;animation:cssload-spin 2s infinite linear;-o-animation:cssload-spin 2s infinite linear;-ms-animation:cssload-spin 2s infinite linear;-webkit-animation:cssload-spin 2s infinite linear;-moz-animation:cssload-spin 2s infinite linear}@keyframes cssload-spin{100%{transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes cssload-spin{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes cssload-spin{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes cssload-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes cssload-spin{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}
res/js/shortpixel.js CHANGED
@@ -1019,7 +1019,7 @@ function checkBulkProcessingCallApi(){
1019
  ShortPixel.otherMediaUpdateActions(id, ['quota','view']);
1020
  break;
1021
  case ShortPixel.STATUS_FAIL:
1022
- setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('" + id + "', false)\">"
1023
  + _spTr.retry + "</a>");
1024
  showToolBarAlert(ShortPixel.STATUS_FAIL, data["Message"], id);
1025
  if(isBulkPage) {
1019
  ShortPixel.otherMediaUpdateActions(id, ['quota','view']);
1020
  break;
1021
  case ShortPixel.STATUS_FAIL:
1022
+ setCellMessage(id, data["Message"], "<a class='button button-smaller button-primary' href=\"javascript:manualOptimization('" + id + "', true)\">"
1023
  + _spTr.retry + "</a>");
1024
  showToolBarAlert(ShortPixel.STATUS_FAIL, data["Message"], id);
1025
  if(isBulkPage) {
shortpixel-plugin.php CHANGED
@@ -10,7 +10,9 @@ use ShortPixel\Notices\NoticeController as Notices;
10
  */
11
  class ShortPixelPlugin
12
  {
13
- static private $instance;
 
 
14
  private $paths = array('class', 'class/controller', 'class/external'); // classes that are autoloaded
15
 
16
  protected $is_noheaders = false;
@@ -18,17 +20,92 @@ class ShortPixelPlugin
18
  protected $plugin_path;
19
  protected $plugin_url;
20
 
 
 
 
 
 
21
  public function __construct()
22
  {
23
  $this->plugin_path = plugin_dir_path(SHORTPIXEL_PLUGIN_FILE);
24
  $this->plugin_url = plugin_dir_url(SHORTPIXEL_PLUGIN_FILE);
25
 
26
- $this->initRuntime();
27
  $this->initHooks();
28
 
 
 
 
 
 
 
 
 
 
 
29
  if(isset($_REQUEST['noheader'])) {
30
  $this->is_noheaders = true;
31
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
33
 
34
  /** Create instance. This should not be needed to call anywhere else than main plugin file **/
@@ -36,9 +113,10 @@ class ShortPixelPlugin
36
  {
37
  if (is_null(self::$instance))
38
  {
39
- self::$instance = new shortPixelPlugin();
40
  }
41
  return self::$instance;
 
42
  }
43
 
44
  /** Init Runtime. Loads all classes. */
@@ -73,17 +151,26 @@ class ShortPixelPlugin
73
  public function initHooks()
74
  {
75
  add_action('admin_menu', array($this,'admin_pages'));
76
- add_action('admin_enqueue_scripts', array($this, 'admin_scripts'));
77
  add_action('admin_notices', array($this, 'admin_notices')); // notices occured before page load
78
  add_action('admin_footer', array($this, 'admin_notices')); // called in views.
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. */
@@ -168,8 +255,6 @@ class ShortPixelPlugin
168
  public function route()
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.
@@ -193,7 +278,7 @@ class ShortPixelPlugin
193
  if ($controller !== false)
194
  {
195
  $c = new $controller();
196
- $c->setShortPixel($shortPixelPluginInstance);
197
  $c->setControllerURL($url);
198
  if (method_exists($c, $action))
199
  $c->$action();
@@ -205,4 +290,103 @@ class ShortPixelPlugin
205
  }
206
  }
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  } // class plugin
10
  */
11
  class ShortPixelPlugin
12
  {
13
+ private static $instance;
14
+ protected static $modelsLoaded = array(); // don't require twice, limit amount of require looksups..
15
+
16
  private $paths = array('class', 'class/controller', 'class/external'); // classes that are autoloaded
17
 
18
  protected $is_noheaders = false;
20
  protected $plugin_path;
21
  protected $plugin_url;
22
 
23
+ protected $shortPixel; // shortpixel megaclass
24
+ protected $settings; // settings object.
25
+
26
+ protected $admin_pages; // admin page hooks.
27
+
28
  public function __construct()
29
  {
30
  $this->plugin_path = plugin_dir_path(SHORTPIXEL_PLUGIN_FILE);
31
  $this->plugin_url = plugin_dir_url(SHORTPIXEL_PLUGIN_FILE);
32
 
33
+ $this->initRuntime(); // require controllers, and other needed classes
34
  $this->initHooks();
35
 
36
+ add_action('plugins_loaded', array($this, 'init'), 5); // early as possible init.
37
+ }
38
+
39
+ /*
40
+ * Init the plugin after plugins_loaded hook. All of WP is there, all plugins.
41
+ * This can't be loaded on construct time, because of model Loaders etc, with would result in loop.
42
+ *
43
+ */
44
+ public function init()
45
+ {
46
  if(isset($_REQUEST['noheader'])) {
47
  $this->is_noheaders = true;
48
  }
49
+
50
+ // @todo Transitionary init for the time being, since plugin init functionality is still split between.
51
+ global $shortPixelPluginInstance;
52
+ $shortPixelPluginInstance = new \wpShortPixel();
53
+ $this->shortPixel = $shortPixelPluginInstance;
54
+
55
+ $front = new frontController();
56
+ $admin = adminController::getInstance();
57
+
58
+
59
+ if ($this->settings()->autoMediaLibrary)
60
+ {
61
+ // compat filter to shortcircuit this in cases. (see external - visualcomposer)
62
+ if (apply_filters('shortpixel/init/automedialibrary', true))
63
+ {
64
+ // $autoPng2Jpg = get_option('wp-short-pixel-png2jpg');
65
+ //$autoMediaLibrary = get_option('wp-short-pixel-auto-media-library');
66
+
67
+ if($this->settings()->autoMediaLibrary && $this->settings()->png2jpg) {
68
+ add_action( 'wp_handle_upload', array($admin,'handlePng2JpgHook'));
69
+ // @todo Document what plugin does mpp
70
+ add_action( 'mpp_handle_upload', array($admin,'handlePng2JpgHook'));
71
+ }
72
+ add_action('wp_handle_replace', array($admin,'handleReplaceHook'));
73
+
74
+ if($this->settings()->autoMediaLibrary) {
75
+
76
+ add_filter( 'wp_generate_attachment_metadata', array($admin,'handleImageUploadHook'), 10, 2 );
77
+ // @todo Document what plugin does mpp
78
+ add_filter( 'mpp_generate_metadata', array($admin,'handleImageUploadHook'), 10, 2 );
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /** Function to get plugin settings
85
+ *
86
+ * @return SettingsModel The settings model object.
87
+ */
88
+ public function settings()
89
+ {
90
+ if (is_null($this->settings))
91
+ $this->settings = new \WPShortPixelSettings();
92
+
93
+ return $this->settings;
94
+ }
95
+
96
+ /** Function to get all enviromental variables
97
+ *
98
+ * @return EnvironmentModel
99
+ */
100
+ public function env()
101
+ {
102
+ $this->loadModel('environment');
103
+ return EnvironmentModel::getInstance();
104
+ }
105
+
106
+ public function fileSystem()
107
+ {
108
+ return new \ShortPixel\FileSystemController();
109
  }
110
 
111
  /** Create instance. This should not be needed to call anywhere else than main plugin file **/
113
  {
114
  if (is_null(self::$instance))
115
  {
116
+ self::$instance = new ShortPixelPlugin();
117
  }
118
  return self::$instance;
119
+
120
  }
121
 
122
  /** Init Runtime. Loads all classes. */
151
  public function initHooks()
152
  {
153
  add_action('admin_menu', array($this,'admin_pages'));
154
+ add_action('admin_enqueue_scripts', array($this, 'admin_scripts')); // admin scripts
155
  add_action('admin_notices', array($this, 'admin_notices')); // notices occured before page load
156
  add_action('admin_footer', array($this, 'admin_notices')); // called in views.
 
157
  }
158
 
159
  /** Hook in our admin pages */
160
  public function admin_pages()
161
  {
162
+ $admin_pages = array();
163
  // settings page
164
+ $admin_pages[] = add_options_page( __('ShortPixel Settings','shortpixel-image-optimiser'), 'ShortPixel', 'manage_options', 'wp-shortpixel-settings', array($this, 'route'));
165
+
166
+ if($this->shortPixel->getSpMetaDao()->hasFoldersTable() && count($this->shortPixel->getSpMetaDao()->getFolders())) {
167
+ /*translators: title and menu name for the Other media page*/
168
+ $admin_pages[] = add_media_page( __('Other Media Optimized by ShortPixel','shortpixel-image-optimiser'), __('Other Media','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-custom', array( $this->shortPixel, 'listCustomMedia' ) );
169
+ }
170
+ /*translators: title and menu name for the Bulk Processing page*/
171
+ $admin_pages[] = add_media_page( __('ShortPixel Bulk Process','shortpixel-image-optimiser'), __('Bulk ShortPixel','shortpixel-image-optimiser'), 'edit_others_posts', 'wp-short-pixel-bulk', array( $this->shortPixel, 'bulkProcess' ) );
172
+
173
+ $this->admin_pages = $admin_pages;
174
  }
175
 
176
  /** PluginRunTime. Items that should be initialized *only* when doing our pages and territory. */
255
  public function route()
256
  {
257
  global $plugin_page;
 
 
258
  $this->initPluginRunTime();
259
 
260
  $default_action = 'load'; // generic action on controller.
278
  if ($controller !== false)
279
  {
280
  $c = new $controller();
281
+ $c->setShortPixel($this->shortPixel);
282
  $c->setControllerURL($url);
283
  if (method_exists($c, $action))
284
  $c->$action();
290
  }
291
  }
292
 
293
+ /** Loads the Model Data Structure upon request
294
+ *
295
+ * @param string $name Name of the model
296
+ */
297
+ public function loadModel($name){
298
+ $path = \ShortPixelTools::getPluginPath() . 'class/model/' . $name . '_model.php';
299
+
300
+ if (! in_array($name, self::$modelsLoaded))
301
+ {
302
+ self::$modelsLoaded[] = $name;
303
+ if(file_exists($path)){
304
+ require_once($path);
305
+ }
306
+ else {
307
+ Log::addError("Model $name could not be found");
308
+ }
309
+ }
310
+ }
311
+
312
+ // Get the plugin URL, based on real URL.
313
+ public function plugin_url($urlpath = '')
314
+ {
315
+ $url = trailingslashit($this->plugin_url);
316
+ if (strlen($urlpath) > 0)
317
+ $url .= $urlpath;
318
+ return $url;
319
+ }
320
+
321
+ // Get the plugin path.
322
+ public function plugin_path($path = '')
323
+ {
324
+ $plugin_path = trailingslashit($this->plugin_path);
325
+ if (strlen($path) > 0)
326
+ $plugin_path .= $path;
327
+
328
+ return $plugin_path;
329
+ }
330
+
331
+ // Get the ShortPixel Object.
332
+ public function getShortPixel()
333
+ {
334
+ return $this->shortPixel;
335
+ }
336
+
337
+ /** Returns defined admin page hooks. Internal use - check states via environmentmodel
338
+ * @returns Array
339
+ */
340
+ public function get_admin_pages()
341
+ {
342
+ return $this->admin_pages;
343
+ }
344
+
345
+ public static function activatePlugin()
346
+ {
347
+ self::deactivatePlugin();
348
+ if(SHORTPIXEL_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
349
+ \WPShortPixelSettings::debugResetOptions();
350
+ $settings = new \WPShortPixelSettings();
351
+ $spMetaDao = new \ShortPixelCustomMetaDao(new \WpShortPixelDb(), $settings->excludePatterns);
352
+ $spMetaDao->dropTables();
353
+ }
354
+
355
+ $env = wpSPIO()->env();
356
+
357
+ if(\WPShortPixelSettings::getOpt('deliverWebp') == 3 && ! $env->is_nginx) {
358
+ self::alterHtaccess(); //add the htaccess lines
359
+ }
360
+ \WPShortPixelSettings::onActivate();
361
+ }
362
+
363
+ public static function deactivatePlugin()
364
+ {
365
+ \ShortPixelQueue::resetBulk();
366
+ (! defined('SHORTPIXEL_NOFLOCK')) ? \ShortPixelQueue::resetPrio() : \ShortPixelQueueDB::resetPrio();
367
+ \WPShortPixelSettings::onDeactivate();
368
+
369
+ //$settingsControl = new \ShortPixel\SettingsController();
370
+ $env = wpSPIO()->env();
371
+
372
+ if (! $env->is_nginx)
373
+ \WpShortPixel::alterHtaccess(true);
374
+
375
+ // save remove.
376
+ $fs = new FileSystemController();
377
+ $log = $fs->getFile(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
378
+ if ($log->exists())
379
+ $log->delete();
380
+ // @unlink(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
381
+ }
382
+
383
+ public static function uninstallPlugin()
384
+ {
385
+ $settings = new \WPShortPixelSettings();
386
+ if($settings->removeSettingsOnDeletePlugin == 1) {
387
+ \WPShortPixelSettings::debugResetOptions();
388
+ insert_with_markers( get_home_path() . '.htaccess', 'ShortPixelWebp', '');
389
+ }
390
+ }
391
+
392
  } // class plugin
shortpixel_api.php CHANGED
@@ -113,7 +113,7 @@ class ShortPixelAPI {
113
  }
114
 
115
  // WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
116
- $URLs = apply_filters('shortpixel_image_urls', $URLs, $itemHandler->getId()) ;
117
 
118
  $requestParameters = array(
119
  'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
@@ -135,8 +135,8 @@ class ShortPixelAPI {
135
  }
136
 
137
  //WpShortPixel::log("ShortPixel API Request Settings: " . json_encode($requestParameters));
138
- //Log::addDebug('ShortPixel API Request');
139
  $response = wp_remote_post($this->_apiEndPoint, $this->prepareRequest($requestParameters, $Blocking) );
 
140
 
141
  //WpShortPixel::log('RESPONSE: ' . json_encode($response));
142
 
@@ -247,6 +247,7 @@ class ShortPixelAPI {
247
  $compressionType = $meta->getCompressionType() !== null ? $meta->getCompressionType() : $this->_settings->compressionType;
248
  $response = $this->doRequests($URLs, true, $itemHandler, $compressionType);//send requests to API
249
 
 
250
  //die($response['body']);
251
 
252
  if($response['response']['code'] != 200) {//response <> 200 -> there was an error apparently?
@@ -467,12 +468,13 @@ class ShortPixelAPI {
467
  return array("Status" => self::STATUS_SUCCESS);
468
  }
469
 
470
- Log::addDebug('Backing The Up', array($mainPath, $PATHs));
471
 
472
  //$fullSubDir = str_replace(wp_normalize_path(get_home_path()), "", wp_normalize_path(dirname($itemHandler->getMeta()->getPath()))) . '/';
473
  //$SubDir = ShortPixelMetaFacade::returnSubDir($itemHandler->getMeta()->getPath(), $itemHandler->getType());
474
  $fullSubDir = ShortPixelMetaFacade::returnSubDir($mainPath);
475
  $source = $PATHs; //array with final paths for these files
 
476
 
477
  if( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && ! ShortPixelFolder::createBackUpFolder() ) {//creates backup folder if it doesn't exist
478
  Log::addWarn('Backup folder does not exist and it cannot be created');
@@ -489,16 +491,20 @@ class ShortPixelAPI {
489
  //now that we have original files and where we should back them up we attempt to do just that
490
  if(is_writable(SHORTPIXEL_BACKUP_FOLDER))
491
  {
492
-
493
  foreach ( $destination as $fileID => $filePATH )
494
  {
495
- if ( !file_exists($filePATH) )
 
 
496
  {
497
- if ( !@copy($source[$fileID], $filePATH) )
 
 
498
  {//file couldn't be saved in backup folder
499
  $msg = sprintf(__('Cannot save file <i>%s</i> in backup directory','shortpixel-image-optimiser'),self::MB_basename($source[$fileID]));
500
  return array("Status" => self::STATUS_FAIL, "Message" => $msg);
501
  }
 
502
  }
503
  }
504
  return array("Status" => self::STATUS_SUCCESS);
@@ -828,6 +834,7 @@ class ShortPixelAPI {
828
  /**
829
  * @param $archive
830
  * @param $tempFiles
 
831
  */
832
  protected static function cleanupTemporaryFiles($archive, $tempFiles)
833
  {
@@ -835,8 +842,16 @@ class ShortPixelAPI {
835
  ShortpixelFolder::deleteFolder($archive['Path']);
836
  } else {
837
  if (!empty($tempFiles) && is_array($tempFiles)) {
 
838
  foreach ($tempFiles as $tmpFile) {
839
- @unlink($tmpFile["Message"]);
 
 
 
 
 
 
 
840
  }
841
  }
842
  }
113
  }
114
 
115
  // WpShortPixel::log("DO REQUESTS for META: " . json_encode($itemHandler->getRawMeta()) . " STACK: " . json_encode(debug_backtrace()));
116
+ $URLs = apply_filters('shortpixel_image_urls', $URLs, $itemHandler->getId()) ;
117
 
118
  $requestParameters = array(
119
  'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
135
  }
136
 
137
  //WpShortPixel::log("ShortPixel API Request Settings: " . json_encode($requestParameters));
 
138
  $response = wp_remote_post($this->_apiEndPoint, $this->prepareRequest($requestParameters, $Blocking) );
139
+ Log::addDebug('ShortPixel API Request sent', $requestParameters);
140
 
141
  //WpShortPixel::log('RESPONSE: ' . json_encode($response));
142
 
247
  $compressionType = $meta->getCompressionType() !== null ? $meta->getCompressionType() : $this->_settings->compressionType;
248
  $response = $this->doRequests($URLs, true, $itemHandler, $compressionType);//send requests to API
249
 
250
+
251
  //die($response['body']);
252
 
253
  if($response['response']['code'] != 200) {//response <> 200 -> there was an error apparently?
468
  return array("Status" => self::STATUS_SUCCESS);
469
  }
470
 
471
+ //Log::addDebug('Backing The Up', array($mainPath, $PATHs));
472
 
473
  //$fullSubDir = str_replace(wp_normalize_path(get_home_path()), "", wp_normalize_path(dirname($itemHandler->getMeta()->getPath()))) . '/';
474
  //$SubDir = ShortPixelMetaFacade::returnSubDir($itemHandler->getMeta()->getPath(), $itemHandler->getType());
475
  $fullSubDir = ShortPixelMetaFacade::returnSubDir($mainPath);
476
  $source = $PATHs; //array with final paths for these files
477
+ $fs = \wpSPIO()->filesystem();
478
 
479
  if( !file_exists(SHORTPIXEL_BACKUP_FOLDER) && ! ShortPixelFolder::createBackUpFolder() ) {//creates backup folder if it doesn't exist
480
  Log::addWarn('Backup folder does not exist and it cannot be created');
491
  //now that we have original files and where we should back them up we attempt to do just that
492
  if(is_writable(SHORTPIXEL_BACKUP_FOLDER))
493
  {
 
494
  foreach ( $destination as $fileID => $filePATH )
495
  {
496
+ $destination_file = $fs->getFile($filePATH);
497
+
498
+ if ( ! $destination_file->exists() )
499
  {
500
+ $source_file = $fs->getFile($source[$fileID]);
501
+ $result = $source_file->copy($destination_file);
502
+ if ( ! $result )
503
  {//file couldn't be saved in backup folder
504
  $msg = sprintf(__('Cannot save file <i>%s</i> in backup directory','shortpixel-image-optimiser'),self::MB_basename($source[$fileID]));
505
  return array("Status" => self::STATUS_FAIL, "Message" => $msg);
506
  }
507
+
508
  }
509
  }
510
  return array("Status" => self::STATUS_SUCCESS);
834
  /**
835
  * @param $archive
836
  * @param $tempFiles
837
+ * @todo Move to FS-controller
838
  */
839
  protected static function cleanupTemporaryFiles($archive, $tempFiles)
840
  {
842
  ShortpixelFolder::deleteFolder($archive['Path']);
843
  } else {
844
  if (!empty($tempFiles) && is_array($tempFiles)) {
845
+
846
  foreach ($tempFiles as $tmpFile) {
847
+ $filepath = isset($tmpFile['Message']) ? $tmpFile['Message'] : false;
848
+ if ($filepath)
849
+ {
850
+ $file = \wpSPIO()->filesystem()->getFile($filepath);
851
+ if ($file->exists())
852
+ $file->delete();
853
+ //@unlink($tmpFile["Message"]);
854
+ }
855
  }
856
  }
857
  }
wp-shortpixel-req.php CHANGED
@@ -7,27 +7,24 @@ if(defined('SHORTPIXEL_DEBUG') && SHORTPIXEL_DEBUG === true) {
7
  } */
8
 
9
  // Debug. Hook as early as possible.
10
- require_once('class/controller/controller.php');
11
  //require_once('class/controller/debug.php');
12
  //require_once('class/model/shortpixel-debug.php');
13
 
14
  // @todo wp-shortpixel-settings which depends on this model should be called when needed; in the model/ directory. That will be some work, so for now here.
15
- require_once('class/shortpixel-model.php');
16
 
17
  //use ShortPixel\DebugItem as DebugItem;
18
- use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
19
 
20
- if (! defined('SHORTPIXEL_DEBUG'))
21
  {
22
  define('SHORTPIXEL_DEBUG', false);
23
- }
24
-
25
- Log::addDebug('Plugin Req Init');
26
-
27
 
28
  // [BS] New plugin runtime.
29
- require_once('shortpixel-plugin.php'); // loads runtime and needed classes.
30
- new Shortpixel\ShortPixelPlugin();
31
 
32
  // @todo Temporary until main plugin file will receive it's unclutter. Require the things loaded by new plugin main
33
  /*if (! class_exists('ShortPixel\ShortPixelPlugin'))
7
  } */
8
 
9
  // Debug. Hook as early as possible.
10
+ //require_once('class/controller/controller.php');
11
  //require_once('class/controller/debug.php');
12
  //require_once('class/model/shortpixel-debug.php');
13
 
14
  // @todo wp-shortpixel-settings which depends on this model should be called when needed; in the model/ directory. That will be some work, so for now here.
15
+ //require_once('class/shortpixel-model.php');
16
 
17
  //use ShortPixel\DebugItem as DebugItem;
18
+ //use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
19
 
20
+ /*if (! defined('SHORTPIXEL_DEBUG'))
21
  {
22
  define('SHORTPIXEL_DEBUG', false);
23
+ } */
 
 
 
24
 
25
  // [BS] New plugin runtime.
26
+ //require_once('shortpixel-plugin.php'); // loads runtime and needed classes.
27
+ //new Shortpixel\ShortPixelPlugin();
28
 
29
  // @todo Temporary until main plugin file will receive it's unclutter. Require the things loaded by new plugin main
30
  /*if (! class_exists('ShortPixel\ShortPixelPlugin'))
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.6
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
@@ -19,7 +19,7 @@ define('SHORTPIXEL_PLUGIN_DIR', __DIR__);
19
 
20
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
21
 
22
- define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.14.6");
23
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
24
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
25
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
@@ -40,13 +40,14 @@ define('SHORTPIXEL_API', 'api.shortpixel.com');
40
  $max_exec = intval(ini_get('max_execution_time'));
41
  if ($max_exec === 0) // max execution time of zero means infinite. Quantify.
42
  $max_exec = 60;
 
 
43
  define('SHORTPIXEL_MAX_EXECUTION_TIME', $max_exec);
44
 
45
  // ** @todo For what is this needed? */
46
  //require_once(ABSPATH . 'wp-admin/includes/file.php');
47
  require_once(SHORTPIXEL_PLUGIN_DIR . '/build/shortpixel/autoload.php');
48
 
49
-
50
  $sp__uploads = wp_upload_dir();
51
  define('SHORTPIXEL_UPLOADS_BASE', (file_exists($sp__uploads['basedir']) ? '' : ABSPATH) . $sp__uploads['basedir'] );
52
  //define('SHORTPIXEL_UPLOADS_URL', is_main_site() ? $sp__uploads['baseurl'] : dirname(dirname($sp__uploads['baseurl'])));
@@ -69,203 +70,35 @@ else
69
  define('SHORTPIXEL_MAX_EXECUTION_TIME2', 2 );
70
  define("SHORTPIXEL_MAX_RESULTS_QUERY", 30);
71
  //define("SHORTPIXEL_NOFLOCK", true); // don't use flock queue, can cause instability.
 
72
 
73
- function shortpixelInit() {
74
- global $shortPixelPluginInstance;
75
- //limit to certain admin pages if function available
76
- $loadOnThisPage = function_exists('get_current_screen');
77
- if($loadOnThisPage) {
78
- $screen = get_current_screen();
79
- if(is_object($screen) && !in_array($screen->id, array('upload', 'edit', 'edit-tags', 'post-new', 'post'))) {
80
- return;
81
- }
82
- }
83
- $isAjaxButNotSP = false; //defined( 'DOING_AJAX' ) && DOING_AJAX && !(isset($_REQUEST['action']) && (strpos($_REQUEST['action'], 'shortpixel_') === 0));
84
- if (!isset($shortPixelPluginInstance)
85
- && ( (shortPixelCheckQueue() && get_option('wp-short-pixel-front-bootstrap'))
86
- || is_admin() && !$isAjaxButNotSP
87
- && (function_exists("is_user_logged_in") && is_user_logged_in()) //is admin, is logged in - :) seems funny but it's not, ajax scripts are admin even if no admin is logged in.
88
- && ( current_user_can( 'manage_options' )
89
- || current_user_can( 'upload_files' )
90
- || current_user_can( 'edit_posts' )
91
- )
92
- )
93
- )
94
- {
95
- require_once('wp-shortpixel-req.php');
96
-
97
- $shortPixelPluginInstance = new WPShortPixel;
98
- }
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
-
110
- /**
111
- * this is hooked into wp_generate_attachment_metadata
112
- * @param $meta
113
- * @param null $ID
114
- * @return WPShortPixel the instance
115
- */
116
- function shortPixelHandleImageUploadHook($meta, $ID = null) {
117
- global $shortPixelPluginInstance;
118
- if(!isset($shortPixelPluginInstance)) {
119
- require_once('wp-shortpixel-req.php');
120
- $shortPixelPluginInstance = new WPShortPixel;
121
- }
122
- return $shortPixelPluginInstance->handleMediaLibraryImageUpload($meta, $ID);
123
- }
124
-
125
- function shortPixelReplaceHook($params) {
126
- if(isset($params['post_id'])) { //integration with EnableMediaReplace - that's an upload for replacing an existing ID
127
- global $shortPixelPluginInstance;
128
- if (!isset($shortPixelPluginInstance)) {
129
- require_once('wp-shortpixel-req.php');
130
- $shortPixelPluginInstance = new WPShortPixel;
131
- }
132
- $itemHandler = $shortPixelPluginInstance->onDeleteImage($params['post_id']);
133
- $itemHandler->deleteAllSPMeta();
134
- }
135
- }
136
-
137
- function shortPixelPng2JpgHook($params) {
138
- global $shortPixelPluginInstance;
139
- if(!isset($shortPixelPluginInstance)) {
140
- require_once('wp-shortpixel-req.php');
141
- $shortPixelPluginInstance = new WPShortPixel;
142
- }
143
- return $shortPixelPluginInstance->convertPng2Jpg($params);
144
- }
145
-
146
- function shortPixelNggAdd($image) {
147
- global $shortPixelPluginInstance;
148
- if(!isset($shortPixelPluginInstance)) {
149
- require_once('wp-shortpixel-req.php');
150
- $shortPixelPluginInstance = new WPShortPixel;
151
- }
152
- $shortPixelPluginInstance->handleNextGenImageUpload($image);
153
- }
154
-
155
- function shortPixelActivatePlugin () {
156
- require_once('wp-shortpixel-req.php');
157
- WPShortPixel::shortPixelActivatePlugin();
158
- }
159
-
160
- function shortPixelDeactivatePlugin () {
161
- require_once('wp-shortpixel-req.php');
162
- WPShortPixel::shortPixelDeactivatePlugin();
163
- }
164
-
165
- function shortPixelUninstallPlugin () {
166
- require_once('wp-shortpixel-req.php');
167
- WPShortPixel::shortPixelUninstallPlugin();
168
- }
169
-
170
- //Picture generation, hooked on the_content filter
171
- function shortPixelConvertImgToPictureAddWebp($content) {
172
- if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
173
- //for AMP pages the <picture> tag is not allowed
174
- return $content . (isset($_GET['SHORTPIXEL_DEBUG']) ? '<!-- SPDBG is AMP -->' : '');
175
- }
176
- require_once('wp-shortpixel-req.php');
177
- require_once('class/front/img-to-picture-webp.php');
178
-
179
- return ShortPixelImgToPictureWebp::convert($content);// . "<!-- PICTURE TAGS BY SHORTPIXEL -->";
180
- }
181
- function shortPixelAddPictureJs() {
182
- // Don't do anything with the RSS feed.
183
- if ( is_feed() || is_admin() ) { return; }
184
-
185
- echo '<script>'
186
- . 'var spPicTest = document.createElement( "picture" );'
187
- . 'if(!window.HTMLPictureElement && document.addEventListener) {'
188
- . 'window.addEventListener("DOMContentLoaded", function() {'
189
- . 'var scriptTag = document.createElement("script");'
190
- . 'scriptTag.src = "' . plugins_url('/res/js/picturefill.min.js', __FILE__) . '";'
191
- . 'document.body.appendChild(scriptTag);'
192
- . '});'
193
- . '}'
194
- . '</script>';
195
- }
196
-
197
- add_filter( 'gform_save_field_value', 'shortPixelGravityForms', 10, 5 );
198
-
199
- function shortPixelGravityForms( $value, $lead, $field, $form ) {
200
- global $shortPixelPluginInstance;
201
- if($field->type == 'post_image') {
202
- require_once('wp-shortpixel-req.php');
203
- $shortPixelPluginInstance = new WPShortPixel;
204
- $shortPixelPluginInstance->handleGravityFormsImageField($value);
205
- }
206
- return $value;
207
  }
208
 
209
- function shortPixelInitOB() {
210
- if(!is_admin() || (function_exists("wp_doing_ajax") && wp_doing_ajax()) || (defined( 'DOING_AJAX' ) && DOING_AJAX)) {
211
- ob_start('shortPixelConvertImgToPictureAddWebp');
212
- }
213
- }
214
 
215
- function shortPixelIsPluginActive($plugin) {
216
- $activePlugins = apply_filters( 'active_plugins', get_option( 'active_plugins', array()));
217
- if ( is_multisite() ) {
218
- $activePlugins = array_merge($activePlugins, get_site_option( 'active_sitewide_plugins'));
219
- }
220
- return in_array( $plugin, $activePlugins);
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
  // @todo Better solution for pre-runtime inclusions of externals.
229
- require_once('class/external/flywheel.php'); // check if SP runs on flywheel
230
- require_once('class/external/wp-offload-media.php');
231
 
 
232
 
233
- $option = get_option('wp-short-pixel-create-webp-markup');
234
- if ( $option ) {
235
- if(shortPixelIsPluginActive('shortpixel-adaptive-images/short-pixel-ai.php')) {
236
- set_transient("shortpixel_thrown_notice", array('when' => 'spai', 'extra' => __('Please deactivate the ShortPixel Image Optimizer\'s
237
- <a href="options-general.php?page=wp-shortpixel-settings&part=adv-settings">Deliver WebP using PICTURE tag</a>
238
- option when the ShortPixel Adaptive Images plugin is active.','shortpixel-image-optimiser')), 1800);
239
- }
240
- elseif( $option == 1 ){
241
- add_action( 'wp_head', 'shortPixelAddPictureJs'); // adds polyfill JS to the header
242
- add_action( 'init', 'shortPixelInitOB', 1 ); // start output buffer to capture content
243
- } elseif ($option == 2){
244
- add_filter( 'the_content', 'shortPixelConvertImgToPictureAddWebp', 10000 ); // priority big, so it will be executed last
245
- add_filter( 'the_excerpt', 'shortPixelConvertImgToPictureAddWebp', 10000 );
246
- add_filter( 'post_thumbnail_html', 'shortPixelConvertImgToPictureAddWebp');
247
- }
248
- // add_action( 'wp_enqueue_scripts', 'spAddPicturefillJs' );
249
- }
250
-
251
- if ( !function_exists( 'vc_action' ) || vc_action() !== 'vc_inline' ) { //handle incompatibility with Visual Composer
252
- add_action( 'init', 'shortpixelInit');
253
- add_action('ngg_added_new_image', 'shortPixelNggAdd');
254
-
255
- $autoPng2Jpg = get_option('wp-short-pixel-png2jpg');
256
- $autoMediaLibrary = get_option('wp-short-pixel-auto-media-library');
257
- if($autoPng2Jpg && $autoMediaLibrary) {
258
- add_action( 'wp_handle_upload', 'shortPixelPng2JpgHook');
259
- add_action( 'mpp_handle_upload', 'shortPixelPng2JpgHook');
260
- }
261
- add_action('wp_handle_replace', 'shortPixelReplaceHook');
262
- if($autoMediaLibrary) {
263
- add_filter( 'wp_generate_attachment_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
264
- add_filter( 'mpp_generate_metadata', 'shortPixelHandleImageUploadHook', 10, 2 );
265
- }
266
-
267
- register_activation_hook( __FILE__, 'shortPixelActivatePlugin' );
268
- register_deactivation_hook( __FILE__, 'shortPixelDeactivatePlugin' );
269
- register_uninstall_hook(__FILE__, 'shortPixelUninstallPlugin');
270
- }
271
- ?>
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.15.0
7
  * Author: ShortPixel
8
  * Author URI: https://shortpixel.com
9
  * Text Domain: shortpixel-image-optimiser
19
 
20
  //define('SHORTPIXEL_AFFILIATE_CODE', '');
21
 
22
+ define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "4.15.0");
23
  define('SHORTPIXEL_MAX_TIMEOUT', 10);
24
  define('SHORTPIXEL_VALIDATE_MAX_TIMEOUT', 15);
25
  define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
40
  $max_exec = intval(ini_get('max_execution_time'));
41
  if ($max_exec === 0) // max execution time of zero means infinite. Quantify.
42
  $max_exec = 60;
43
+ elseif($max_exec < 0) // some hosts like to set negative figures on this. Ignore that.
44
+ $max_exec = 30;
45
  define('SHORTPIXEL_MAX_EXECUTION_TIME', $max_exec);
46
 
47
  // ** @todo For what is this needed? */
48
  //require_once(ABSPATH . 'wp-admin/includes/file.php');
49
  require_once(SHORTPIXEL_PLUGIN_DIR . '/build/shortpixel/autoload.php');
50
 
 
51
  $sp__uploads = wp_upload_dir();
52
  define('SHORTPIXEL_UPLOADS_BASE', (file_exists($sp__uploads['basedir']) ? '' : ABSPATH) . $sp__uploads['basedir'] );
53
  //define('SHORTPIXEL_UPLOADS_URL', is_main_site() ? $sp__uploads['baseurl'] : dirname(dirname($sp__uploads['baseurl'])));
70
  define('SHORTPIXEL_MAX_EXECUTION_TIME2', 2 );
71
  define("SHORTPIXEL_MAX_RESULTS_QUERY", 30);
72
  //define("SHORTPIXEL_NOFLOCK", true); // don't use flock queue, can cause instability.
73
+ //define("SHORTPIXEL_EXPERIMENTAL_SECURICACHE", true); // tries to add timestamps to URLS, to prevent hitting the cache.
74
 
75
+ /* Function to reach core function of ShortPixel
76
+ * Use to get plugin url, plugin path, or certain core controllers
77
+ */
78
+ function wpSPIO()
79
+ {
80
+ return \ShortPixel\ShortPixelPlugin::getInstance();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
 
83
+ // [BS] Start runtime here
84
+ require_once(SHORTPIXEL_PLUGIN_DIR . '/wp-shortpixel-req.php'); // @todo should be incorporated here.
85
+ require_once(SHORTPIXEL_PLUGIN_DIR . '/class/controller/controller.php');
86
+ require_once(SHORTPIXEL_PLUGIN_DIR . '/class/shortpixel-model.php');
87
+ require_once(SHORTPIXEL_PLUGIN_DIR . '/shortpixel-plugin.php'); // loads runtime and needed classes.
88
 
89
+ if (! defined('SHORTPIXEL_DEBUG'))
90
+ {
91
+ define('SHORTPIXEL_DEBUG', false);
 
 
 
92
  }
 
 
93
  $log = ShortPixel\ShortPixelLogger\ShortPixelLogger::getInstance();
94
  $log->setLogPath(SHORTPIXEL_BACKUP_FOLDER . "/shortpixel_log");
95
 
96
  // Pre-Runtime Checks
97
  // @todo Better solution for pre-runtime inclusions of externals.
98
+ // Should not be required here. wpspio initruntime loads externals
 
99
 
100
+ wpSPIO(); // let's go!
101
 
102
+ register_activation_hook( __FILE__, array('\ShortPixel\ShortPixelPlugin','activatePlugin') );
103
+ register_deactivation_hook( __FILE__, array('\ShortPixel\ShortPixelPlugin','deactivatePlugin') );
104
+ register_uninstall_hook(__FILE__, array('\ShortPixel\ShortPixelPlugin','uninstallPlugin') );