WebP Express - Version 0.22.0

Version Description

(released: 09 Nov 2021) * Added option to disable creating log files and button to do delete them. Thanks to Amit Sonkhiya and many others for suggesting this. * WebP Express now prevents serving webps when they are bigger than the source. Added option for it (default is to prevent) * You can now prevent that a certain image is redirected to webp by appending "?original" to the URL. Also works for Alter HTML. * You can now prevent that a certain image is redirected to webp by placing a dummy ".do-not-convert" file next to the original. * The conversion manager now uses the new "?original" escape hatch to avoid that the original image is being redirected. Beware that if you are on Nginx and have created rewrite rules, you will need to implement the same escape hatch to avoid the "Original" image to be redirected to webp. However, in a future update, I will probably implement a PHP script for serving the original so you can also just wait. * Bugfix: Alter HTML could in some rare cases produce invalid HTML. For more info, see the closed issues on the webp-express 0.22 milestone * Bugfix: The "Enable redirection to converter" functionality was too greedy. It did not only redirect jpegs and pngs, but any file request. The bug occurred when Destination folder was set to "Image roots". The fix updates the .htaccess files if neccessary. * Minor bugfix: Symlinking the webp-express plugin folder would break "Redirection to converter" functionality

For more info, see the closed issues on the webp-express 0.22 milestone

Download this release

Release Info

Developer rosell.dk
Plugin Icon 128x128 WebP Express
Version 0.22.0
Comparing to
See all releases

Code changes from version 0.21.1 to 0.22.0

README.md CHANGED
@@ -664,28 +664,21 @@ The following lazy load plugins/frameworks has been tested and works with *WebP
664
  I have only tested the above in *Varied image responses* mode, but it should also work in *CDN friendly* mode. Both *Alter HTML* options have been designed to work with standard lazy load attributes.
665
 
666
  ### Can I make an exceptions for some images?
667
- There can be instances where you actually need to serve a jpeg or png. For example if you are demonstrating how a jpeg looks using some compression settings. It is possible to bypass both the redirection and the HTML altering for certain images. Here is how:
668
 
669
- *Alter HTML*
670
- Alter HTML is programmed not to substitute image URLs with query strings (better safe than sorry). You can exploit that and simply add ie ?original to the image URLs in question.
 
671
 
672
- *Redirection*
673
- To bypass the *redirection*, you can add the following in the `.htaccess` where *WebP Express* has placed its rules (this is usually in the `wp-content` folder). The rules needs to be added *above* the rules inserted by *WebP Express*.
674
 
 
 
675
  ```
676
- RewriteCond %{QUERY_STRING} original
677
- RewriteCond %{REQUEST_FILENAME} -f
678
- RewriteRule . - [L]
679
  ```
680
- With those rules in place, you can add "?original" to the URLs of those images that you want to keep serving as jpg / png.
681
 
682
- Alternatively, you can specify the filenames individually in the `.htaccess`:
683
-
684
- ```
685
- RewriteRule ^uploads/2019/02/example-of-jpg-compressed-to-80\.jpg - [L]
686
- RewriteRule ^uploads/2019/02/image2\.jpg - [L]
687
- RewriteRule . - [L]
688
- ```
689
  If you got any further questions, look at, or comment on [this topic](https://wordpress.org/support/topic/can-i-make-an-exception-for-specific-post-image/)
690
 
691
  ### Alter HTML only replaces some of the images
664
  I have only tested the above in *Varied image responses* mode, but it should also work in *CDN friendly* mode. Both *Alter HTML* options have been designed to work with standard lazy load attributes.
665
 
666
  ### Can I make an exceptions for some images?
667
+ There can be instances where you actually need to serve a jpeg or png. For example if you are demonstrating how a jpeg looks using some compression settings.
668
 
669
+ If you want an image to be served in the original format (jpeg og png), do one of the following things:
670
+ - Add "?original" to the image url.
671
+ - Place an empty file in the same folder as the jpeg/png. The file name must be the same as the jpeg/png with ".do-not-convert" appended
672
 
673
+ Doing this will bypass redirection to webp and also prevent Alter HTML to use the webp instead of the original.
 
674
 
675
+ *Bypassing for an entire folder*
676
+ To bypass redirection for an entire folder, you can put something like this into your index `.htaccess`:
677
  ```
678
+ RewriteRule ^wp-content/uploads/2021/06/ - [L]
 
 
679
  ```
680
+ PS: If *WebP Express* has placed rules in that .htaccess, you need to place the rule *above* the rules inserted by *WebP Express*
681
 
 
 
 
 
 
 
 
682
  If you got any further questions, look at, or comment on [this topic](https://wordpress.org/support/topic/can-i-make-an-exception-for-specific-post-image/)
683
 
684
  ### Alter HTML only replaces some of the images
README.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://ko-fi.com/rosell
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.8
7
- Stable tag: 0.21.1
8
  Requires PHP: 5.6
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
@@ -175,10 +175,9 @@ Bread on the table don't come for free, even though this plugin does, and always
175
  * Ruben Solvang
176
 
177
  **Persons who recently contributed with [ko-fi](https://ko-fi.com/rosell) - Thanks!**
 
178
  * 29 Aug: Pawa Tecnologia
179
  * 29 Jul: Brian Laursen
180
- * 24 Jul: Hans Konings
181
- * 8 Jul: Raj
182
 
183
  **Persons who contributed with extra generously amounts of coffee / lifetime backing (>30$) - thanks!:**
184
 
@@ -719,28 +718,21 @@ The following lazy load plugins/frameworks has been tested and works with *WebP
719
  I have only tested the above in *Varied image responses* mode, but it should also work in *CDN friendly* mode. Both *Alter HTML* options have been designed to work with standard lazy load attributes.
720
 
721
  = Can I make an exceptions for some images? =
722
- There can be instances where you actually need to serve a jpeg or png. For example if you are demonstrating how a jpeg looks using some compression settings. It is possible to bypass both the redirection and the HTML altering for certain images. Here is how:
723
 
724
- *Alter HTML*
725
- Alter HTML is programmed not to substitute image URLs with query strings (better safe than sorry). You can exploit that and simply add ie ?original to the image URLs in question.
 
726
 
727
- *Redirection*
728
- To bypass the *redirection*, you can add the following in the `.htaccess` where *WebP Express* has placed its rules (this is usually in the `wp-content` folder). The rules needs to be added *above* the rules inserted by *WebP Express*.
729
 
 
 
730
  `
731
- RewriteCond %{QUERY_STRING} original
732
- RewriteCond %{REQUEST_FILENAME} -f
733
- RewriteRule . - [L]
734
  `
735
- With those rules in place, you can add "?original" to the URLs of those images that you want to keep serving as jpg / png.
736
 
737
- Alternatively, you can specify the filenames individually in the `.htaccess`:
738
-
739
- `
740
- RewriteRule ^uploads/2019/02/example-of-jpg-compressed-to-80\.jpg - [L]
741
- RewriteRule ^uploads/2019/02/image2\.jpg - [L]
742
- RewriteRule . - [L]
743
- `
744
  If you got any further questions, look at, or comment on [this topic](https://wordpress.org/support/topic/can-i-make-an-exception-for-specific-post-image/)
745
 
746
  = Update failed and cannot reinstall =
@@ -762,7 +754,7 @@ No schedule. I move forward as time allows. I currently spend a lot of time answ
762
  Right now I am focusing on the File Manager. I would like to add possibility for converting, bulk converting, viewing conversion logs, viewing stats, etc.
763
 
764
  Here are other things in pipeline:
765
- - Excluding certain files and folders.
766
  - Supporting Save-Data header in Varied Image Responses mode (send extra compressed images to clients who wants to use as little bandwidth as possible).
767
  - Displaying rules for NGINX.
768
  - Allow webp for all browsers using [this javascript library](http://libwebpjs.hohenlimburg.org/v0.6.0/). Unfortunately, the javascript library does not (currently) support srcset attributes, which is why I moved this item down the priority list. We need srcset to be supported for the feature to be useful.
@@ -783,6 +775,20 @@ If you want to make sure that my coffee supplies don't run dry, you can even buy
783
 
784
  == Changelog ==
785
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
  = 0.21.1 =
787
  *(released: 27 Oct 2021)*
788
  * Bugfix: File manager could not handle many images. It now loads tree branches on need basis instead of the complete tree in one go
@@ -822,6 +828,9 @@ For older releases, check out changelog.txt
822
 
823
  == Upgrade Notice ==
824
 
 
 
 
825
  = 0.21.1 =
826
  * Two bug fixes
827
 
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.8
7
+ Stable tag: 0.22.0
8
  Requires PHP: 5.6
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
175
  * Ruben Solvang
176
 
177
  **Persons who recently contributed with [ko-fi](https://ko-fi.com/rosell) - Thanks!**
178
+ * 26 Oct: Anonymous
179
  * 29 Aug: Pawa Tecnologia
180
  * 29 Jul: Brian Laursen
 
 
181
 
182
  **Persons who contributed with extra generously amounts of coffee / lifetime backing (>30$) - thanks!:**
183
 
718
  I have only tested the above in *Varied image responses* mode, but it should also work in *CDN friendly* mode. Both *Alter HTML* options have been designed to work with standard lazy load attributes.
719
 
720
  = Can I make an exceptions for some images? =
721
+ There can be instances where you actually need to serve a jpeg or png. For example if you are demonstrating how a jpeg looks using some compression settings.
722
 
723
+ If you want an image to be served in the original format (jpeg og png), do one of the following things:
724
+ - Add "?original" to the image url.
725
+ - Place an empty file in the same folder as the jpeg/png. The file name must be the same as the jpeg/png with ".do-not-convert" appended
726
 
727
+ Doing this will bypass redirection to webp and also prevent Alter HTML to use the webp instead of the original.
 
728
 
729
+ *Bypassing for an entire folder*
730
+ To bypass redirection for an entire folder, you can put something like this into your root .htaccess:
731
  `
732
+ RewriteRule ^uploads/2021/06/ - [L]
 
 
733
  `
734
+ PS: If *WebP Express* has placed rules in that .htaccess, you need to place the rule *above* the rules inserted by *WebP Express*
735
 
 
 
 
 
 
 
 
736
  If you got any further questions, look at, or comment on [this topic](https://wordpress.org/support/topic/can-i-make-an-exception-for-specific-post-image/)
737
 
738
  = Update failed and cannot reinstall =
754
  Right now I am focusing on the File Manager. I would like to add possibility for converting, bulk converting, viewing conversion logs, viewing stats, etc.
755
 
756
  Here are other things in pipeline:
757
+ - Excluding certain files and folders.
758
  - Supporting Save-Data header in Varied Image Responses mode (send extra compressed images to clients who wants to use as little bandwidth as possible).
759
  - Displaying rules for NGINX.
760
  - Allow webp for all browsers using [this javascript library](http://libwebpjs.hohenlimburg.org/v0.6.0/). Unfortunately, the javascript library does not (currently) support srcset attributes, which is why I moved this item down the priority list. We need srcset to be supported for the feature to be useful.
775
 
776
  == Changelog ==
777
 
778
+ = 0.22.0 =
779
+ *(released: 09 Nov 2021)*
780
+ * Added option to disable creating log files and button to do delete them. Thanks to Amit Sonkhiya and many others for suggesting this.
781
+ * WebP Express now prevents serving webps when they are bigger than the source. Added option for it (default is to prevent)
782
+ * You can now prevent that a certain image is redirected to webp by appending "?original" to the URL. Also works for Alter HTML.
783
+ * You can now prevent that a certain image is redirected to webp by placing a dummy ".do-not-convert" file next to the original.
784
+ * The conversion manager now uses the new "?original" escape hatch to avoid that the original image is being redirected. Beware that if you are on Nginx and have created rewrite rules, you will need to implement the same escape hatch to avoid the "Original" image to be redirected to webp. However, in a future update, I will probably implement a PHP script for serving the original so you can also just wait.
785
+ * Bugfix: Alter HTML could in some [rare cases](https://github.com/rosell-dk/webp-express/issues/528) produce invalid HTML.
786
+ For more info, see the closed issues on the [webp-express 0.22 milestone](https://github.com/rosell-dk/webp-express/milestone/13?closed=1)
787
+ * Bugfix: The "Enable redirection to converter" functionality was too greedy. It did not only redirect jpegs and pngs, but any file request. The bug occurred when Destination folder was set to "Image roots". The fix updates the .htaccess files if neccessary.
788
+ * Minor bugfix: Symlinking the webp-express plugin folder would break "Redirection to converter" functionality
789
+
790
+ For more info, see the closed issues on the [webp-express 0.22 milestone](https://github.com/rosell-dk/webp-express/milestone/13?closed=1)
791
+
792
  = 0.21.1 =
793
  *(released: 27 Oct 2021)*
794
  * Bugfix: File manager could not handle many images. It now loads tree branches on need basis instead of the complete tree in one go
828
 
829
  == Upgrade Notice ==
830
 
831
+ = 0.22.0 =
832
+ * Various improvements and bug fixes
833
+
834
  = 0.21.1 =
835
  * Two bug fixes
836
 
changelog.txt CHANGED
@@ -1,3 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = 0.21.1 =
2
  *(released: 27 Oct 2021)*
3
  * Bugfix: File manager could not handle many images. It now loads tree branches on need basis instead of the complete tree in one go
1
+ = 0.22.0 =
2
+ *(released: 09 Nov 2021)*
3
+ * Added option to disable creating log files and button to do delete them. Thanks to Amit Sonkhiya and many others for suggesting this.
4
+ * WebP Express now prevents serving webps when they are bigger than the source. Added option for it (default is to prevent)
5
+ * You can now prevent that a certain image is redirected to webp by appending "?original" to the URL. Also works for Alter HTML.
6
+ * You can now prevent that a certain image is redirected to webp by placing a dummy ".do-not-convert" file next to the original.
7
+ * The conversion manager now uses the new "?original" escape hatch to avoid that the original image is being redirected. Beware that if you are on Nginx and have created rewrite rules, you will need to implement the same escape hatch to avoid the "Original" image to be redirected to webp. However, in a future update, I will probably implement a PHP script for serving the original so you can also just wait.
8
+ * Bugfix: Alter HTML could in some [rare cases](https://github.com/rosell-dk/webp-express/issues/528) produce invalid HTML.
9
+ For more info, see the closed issues on the [webp-express 0.22 milestone](https://github.com/rosell-dk/webp-express/milestone/13?closed=1)
10
+ * Bugfix: The "Enable redirection to converter" functionality was too greedy. It did not only redirect jpegs and pngs, but any file request. The bug occurred when Destination folder was set to "Image roots". The fix updates the .htaccess files if neccessary.
11
+ * Minor bugfix: Symlinking the webp-express plugin folder would break "Redirection to converter" functionality
12
+
13
+ For more info, see the closed issues on the [webp-express 0.22 milestone](https://github.com/rosell-dk/webp-express/milestone/13?closed=1)
14
+
15
  = 0.21.1 =
16
  *(released: 27 Oct 2021)*
17
  * Bugfix: File manager could not handle many images. It now loads tree branches on need basis instead of the complete tree in one go
docs/publishing.md CHANGED
@@ -114,12 +114,12 @@ svn status | grep '^!' | awk '{print $2}' | xargs svn delete --force (t
114
  Then add a new tag
115
  ```
116
  cd svn
117
- svn cp trunk tags/0.21.0 (this will copy trunk into a new tag)
118
  ```
119
 
120
  And commit!
121
  ```
122
- svn ci -m '0.21.0'
123
  ```
124
 
125
 
114
  Then add a new tag
115
  ```
116
  cd svn
117
+ svn cp trunk tags/0.21.1 (this will copy trunk into a new tag)
118
  ```
119
 
120
  And commit!
121
  ```
122
+ svn ci -m '0.21.1'
123
  ```
124
 
125
 
lib/classes/AdminInit.php CHANGED
@@ -25,7 +25,7 @@ class AdminInit
25
  public static function runMigrationIfNeeded()
26
  {
27
  // When an update requires a migration, the number should be increased
28
- define('WEBPEXPRESS_MIGRATION_VERSION', '13');
29
 
30
  if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) {
31
  // run migration logic
@@ -33,7 +33,7 @@ class AdminInit
33
  }
34
 
35
  // uncomment next line to test-run a migration
36
- //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate13.php';
37
  }
38
 
39
  public static function pageNowIs($pageId)
@@ -80,6 +80,7 @@ class AdminInit
80
  add_action('wp_ajax_convert_file', array('\WebPExpress\Convert', 'processAjaxConvertFile'));
81
  add_action('wp_ajax_webpexpress_view_log', array('\WebPExpress\ConvertLog', 'processAjaxViewLog'));
82
  add_action('wp_ajax_webpexpress_purge_cache', array('\WebPExpress\CachePurge', 'processAjaxPurgeCache'));
 
83
  add_action('wp_ajax_webpexpress_dismiss_message', array('\WebPExpress\DismissableMessages', 'processAjaxDismissMessage'));
84
  add_action('wp_ajax_webpexpress_dismiss_global_message', array('\WebPExpress\DismissableGlobalMessages', 'processAjaxDismissGlobalMessage'));
85
  add_action('wp_ajax_webpexpress_self_test', array('\WebPExpress\SelfTest', 'processAjax'));
@@ -103,11 +104,20 @@ class AdminInit
103
  register_deactivation_hook(WEBPEXPRESS_PLUGIN, array('\WebPExpress\PluginDeactivate', 'deactivate'));
104
  register_uninstall_hook(WEBPEXPRESS_PLUGIN, array('\WebPExpress\PluginUninstall', 'uninstall'));
105
 
 
 
 
 
 
106
  // Some hooks must be registered AFTER admin_init...
107
  add_action("admin_init", array('\WebPExpress\AdminInit', 'addHooksAfterAdminInit'));
108
 
109
  // Run migration AFTER admin_init hook (important, as insert_with_markers injection otherwise fails, see #394)
 
110
  // PS: Unfortunately Message::addMessage doesnt print until next load now, we should look into that.
 
 
 
111
  add_action("admin_init", array('\WebPExpress\AdminInit', 'runMigrationIfNeeded'));
112
 
113
  add_action("admin_notices", array('\WebPExpress\DismissableGlobalMessages', 'printMessages'));
25
  public static function runMigrationIfNeeded()
26
  {
27
  // When an update requires a migration, the number should be increased
28
+ define('WEBPEXPRESS_MIGRATION_VERSION', '14');
29
 
30
  if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) {
31
  // run migration logic
33
  }
34
 
35
  // uncomment next line to test-run a migration
36
+ //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate14.php';
37
  }
38
 
39
  public static function pageNowIs($pageId)
80
  add_action('wp_ajax_convert_file', array('\WebPExpress\Convert', 'processAjaxConvertFile'));
81
  add_action('wp_ajax_webpexpress_view_log', array('\WebPExpress\ConvertLog', 'processAjaxViewLog'));
82
  add_action('wp_ajax_webpexpress_purge_cache', array('\WebPExpress\CachePurge', 'processAjaxPurgeCache'));
83
+ add_action('wp_ajax_webpexpress_purge_log', array('\WebPExpress\LogPurge', 'processAjaxPurgeLog'));
84
  add_action('wp_ajax_webpexpress_dismiss_message', array('\WebPExpress\DismissableMessages', 'processAjaxDismissMessage'));
85
  add_action('wp_ajax_webpexpress_dismiss_global_message', array('\WebPExpress\DismissableGlobalMessages', 'processAjaxDismissGlobalMessage'));
86
  add_action('wp_ajax_webpexpress_self_test', array('\WebPExpress\SelfTest', 'processAjax'));
104
  register_deactivation_hook(WEBPEXPRESS_PLUGIN, array('\WebPExpress\PluginDeactivate', 'deactivate'));
105
  register_uninstall_hook(WEBPEXPRESS_PLUGIN, array('\WebPExpress\PluginUninstall', 'uninstall'));
106
 
107
+ /*$start = microtime(true);
108
+ BiggerThanSourceDummyFilesBulk::updateStatus(Config::loadConfig());
109
+ echo microtime(true) - $start;*/
110
+
111
+
112
  // Some hooks must be registered AFTER admin_init...
113
  add_action("admin_init", array('\WebPExpress\AdminInit', 'addHooksAfterAdminInit'));
114
 
115
  // Run migration AFTER admin_init hook (important, as insert_with_markers injection otherwise fails, see #394)
116
+ // PS: "plugins_loaded" is to early, as insert_with_markers fails.
117
  // PS: Unfortunately Message::addMessage doesnt print until next load now, we should look into that.
118
+ // PPS: It does run. It must be the Option that does not react
119
+ //add_action("admin_init", array('\WebPExpress\AdminInit', 'runMigrationIfNeeded'));
120
+
121
  add_action("admin_init", array('\WebPExpress\AdminInit', 'runMigrationIfNeeded'));
122
 
123
  add_action("admin_notices", array('\WebPExpress\DismissableGlobalMessages', 'printMessages'));
lib/classes/AlterHtmlHelper.php CHANGED
@@ -43,7 +43,9 @@ class AlterHtmlHelper
43
  public static function getOptions() {
44
  if (!isset(self::$options)) {
45
  self::$options = json_decode(Option::getOption('webp-express-alter-html-options', null), true);
46
-
 
 
47
  // Set scope if it isn't there (it wasn't cached until 0.17.5)
48
  if (!isset(self::$options['scope'])) {
49
  $config = Config::loadConfig();
@@ -151,12 +153,13 @@ class AlterHtmlHelper
151
 
152
 
153
  /**
154
- * Get url for webp from source url, (if ), given a certain baseUrl / baseDir.
155
- * Base can for example be uploads or wp-content.
156
  *
157
- * returns false
158
- * - if no source file found in that base
159
- * - or source file is found but webp file isn't there and the `only-for-webps-that-exists` option is set
 
160
  *
161
  * @param string $sourceUrl Url of source image (ie http://example.com/wp-content/image.jpg)
162
  * @param string $rootId Id (created in Config::updateAutoloadedOptions). Ie "uploads", "content" or any image root id
@@ -216,6 +219,20 @@ class AlterHtmlHelper
216
  return false;
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  $destUrl = $destinationRoot['url'] . '/' . $relPathFromImageRootToDest;
220
 
221
  // Fix scheme (use same as source)
43
  public static function getOptions() {
44
  if (!isset(self::$options)) {
45
  self::$options = json_decode(Option::getOption('webp-express-alter-html-options', null), true);
46
+ if (!isset(self::$options['prevent-using-webps-larger-than-original'])) {
47
+ self::$options['prevent-using-webps-larger-than-original'] = true;
48
+ }
49
  // Set scope if it isn't there (it wasn't cached until 0.17.5)
50
  if (!isset(self::$options['scope'])) {
51
  $config = Config::loadConfig();
153
 
154
 
155
  /**
156
+ * Get url for webp from source url, (if ), given a certain baseUrl / baseDir.
157
+ * Base can for example be uploads or wp-content.
158
  *
159
+ * returns false:
160
+ * - if no source file found in that base
161
+ * - if source file is found but webp file isn't there and the `only-for-webps-that-exists` option is set
162
+ * - if webp is marked as bigger than source
163
  *
164
  * @param string $sourceUrl Url of source image (ie http://example.com/wp-content/image.jpg)
165
  * @param string $rootId Id (created in Config::updateAutoloadedOptions). Ie "uploads", "content" or any image root id
219
  return false;
220
  }
221
 
222
+ // check if webp is marked as bigger than source
223
+ /*
224
+ $biggerThanSourcePath = Paths::getBiggerThanSourceDirAbs() . '/' . $rootId . '/' . $relPathFromImageRootToDest;
225
+ if (@file_exists($biggerThanSourcePath)) {
226
+ return false;
227
+ }*/
228
+
229
+ // check if webp is larger than original
230
+ if (self::$options['prevent-using-webps-larger-than-original']) {
231
+ if (BiggerThanSource::bigger($srcPathAbs, $destPathAbs)) {
232
+ return false;
233
+ }
234
+ }
235
+
236
  $destUrl = $destinationRoot['url'] . '/' . $relPathFromImageRootToDest;
237
 
238
  // Fix scheme (use same as source)
lib/classes/BiggerThanSource.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ This class is made to not be dependent on Wordpress functions and must be kept like that.
5
+ It is used by webp-on-demand.php. It is also used for bulk conversion.
6
+ */
7
+ namespace WebPExpress;
8
+
9
+
10
+ class BiggerThanSource
11
+ {
12
+ /**
13
+ * Check if webp is bigger than original.
14
+ *
15
+ * @return boolean|null True if it is bigger than original, false if not. NULL if it cannot be determined
16
+ */
17
+ public static function bigger($source, $destination)
18
+ {
19
+ /*
20
+ if ((!@file_exists($source)) || (!@file_exists($destination) {
21
+ return null;
22
+ }*/
23
+ $filesizeDestination = @filesize($destination);
24
+ $filesizeSource = @filesize($source);
25
+
26
+ // sizes are FALSE on failure (ie if file does not exists)
27
+ if (($filesizeDestination === false) || ($filesizeDestination === false)) {
28
+ return null;
29
+ }
30
+
31
+ return ($filesizeDestination > $filesizeSource);
32
+ }
33
+ }
lib/classes/BiggerThanSourceDummyFiles.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ This class is made to not be dependent on Wordpress functions and must be kept like that.
5
+ It is used by webp-on-demand.php. It is also used for bulk conversion.
6
+ */
7
+ namespace WebPExpress;
8
+
9
+
10
+ class BiggerThanSourceDummyFiles
11
+ {
12
+
13
+
14
+ /**
15
+ * Create the directory for log files and put a .htaccess file into it, which prevents
16
+ * it to be viewed from the outside (not that it contains any sensitive information btw, but for good measure).
17
+ *
18
+ * @param string $logDir The folder where log files are kept
19
+ *
20
+ * @return boolean Whether it was created successfully or not.
21
+ *
22
+ */
23
+ private static function createBiggerThanSourceBaseDir($dir)
24
+ {
25
+ if (!is_dir($dir)) {
26
+ @mkdir($dir, 0775, true);
27
+ @chmod($dir, 0775);
28
+ @file_put_contents(rtrim($dir . '/') . '/.htaccess', <<<APACHE
29
+ <IfModule mod_authz_core.c>
30
+ Require all denied
31
+ </IfModule>
32
+ <IfModule !mod_authz_core.c>
33
+ Order deny,allow
34
+ Deny from all
35
+ </IfModule>
36
+ APACHE
37
+ );
38
+ @chmod($dir . '/.htaccess', 0664);
39
+ }
40
+ return is_dir($dir);
41
+ }
42
+
43
+ public static function pathToDummyFile($source, $basedir, $imageRoots, $destinationFolder, $destinationExt)
44
+ {
45
+ $sourceResolved = realpath($source);
46
+
47
+ // Check roots until we (hopefully) get a match.
48
+ // (that is: find a root which the source is inside)
49
+ foreach ($imageRoots->getArray() as $i => $imageRoot) {
50
+ $rootPath = $imageRoot->getAbsPath();
51
+
52
+ // We can assume that $rootPath is resolvable using realpath (it ought to exist and be within open_basedir for WP to function)
53
+ // We can also assume that $source is resolvable (it ought to exist and within open_basedir)
54
+ // So: Resolve both! and test if the resolved source begins with the resolved rootPath.
55
+ if (strpos($sourceResolved, realpath($rootPath)) !== false) {
56
+ $relPath = substr($sourceResolved, strlen(realpath($rootPath)) + 1);
57
+ $relPath = ConvertHelperIndependent::appendOrSetExtension($relPath, $destinationFolder, $destinationExt, false);
58
+
59
+ return $basedir . '/' . $imageRoot->id . '/' . $relPath;
60
+ break;
61
+ }
62
+ }
63
+ return false;
64
+ }
65
+
66
+ public static function pathToDummyFileRootAndRelKnown($source, $basedir, $rootId, $destinationFolder, $destinationExt)
67
+ {
68
+ }
69
+
70
+ /**
71
+ * Check if webp is bigger than original.
72
+ *
73
+ * @return boolean|null True if it is bigger than original, false if not. NULL if it cannot be determined
74
+ */
75
+ public static function bigger($source, $destination)
76
+ {
77
+ /*
78
+ if ((!@file_exists($source)) || (!@file_exists($destination) {
79
+ return null;
80
+ }*/
81
+ $filesizeDestination = @filesize($destination);
82
+ $filesizeSource = @filesize($source);
83
+
84
+ // sizes are FALSE on failure (ie if file does not exists)
85
+ if (($filesizeDestination === false) || ($filesizeDestination === false)) {
86
+ return null;
87
+ }
88
+
89
+ return ($filesizeDestination > $filesizeSource);
90
+ }
91
+
92
+ /**
93
+ * Update the status for a single image (when rootId is unknown)
94
+ *
95
+ * Checks if webp is bigger than original. If it is, a dummy file is placed. Otherwise, it is
96
+ * removed (if exists)
97
+ *
98
+ * @param string $source Path to the source file that was converted
99
+ *
100
+ *
101
+ */
102
+ public static function updateStatus($source, $destination, $webExpressContentDirAbs, $imageRoots, $destinationFolder, $destinationExt)
103
+ {
104
+ $basedir = $webExpressContentDirAbs . '/webp-images-bigger-than-source';
105
+ if (!file_exists($basedir)) {
106
+ self::createBiggerThanSourceBaseDir($basedir);
107
+ }
108
+ $bigWebP = BiggerThanSource::bigger($source, $destination);
109
+
110
+ $file = self::pathToDummyFile($source, $basedir, $imageRoots, $destinationFolder, $destinationExt);
111
+ if ($file === false) {
112
+ return;
113
+ }
114
+
115
+ if ($bigWebP === true) {
116
+ // place dummy file, which marks that webp is bigger than source
117
+
118
+ $folder = @dirname($file);
119
+ if (!@file_exists($folder)) {
120
+ mkdir($folder, 0777, true);
121
+ }
122
+ if (@file_exists($folder)) {
123
+ file_put_contents($file, '');
124
+ }
125
+
126
+ } else {
127
+ // remove dummy file (if exists)
128
+ if (@file_exists($file)) {
129
+ @unlink($file);
130
+ }
131
+ }
132
+
133
+ }
134
+
135
+ }
lib/classes/BiggerThanSourceDummyFilesBulk.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+
6
+ class BiggerThanSourceDummyFilesBulk
7
+ {
8
+
9
+ private static $settings;
10
+
11
+ /**
12
+ * Update the status for a all images.
13
+ *
14
+ */
15
+ public static function updateStatus($config = null)
16
+ {
17
+ if (is_null($config)) {
18
+ $config = Config::loadConfigAndFix(false);
19
+ }
20
+ self::$settings = [
21
+ 'ext' => $config['destination-extension'],
22
+ 'destination-folder' => $config['destination-folder'], /* hm, "destination-folder" is a bad name... */
23
+ 'webExpressContentDirAbs' => Paths::getWebPExpressContentDirAbs(),
24
+ 'uploadDirAbs' => Paths::getUploadDirAbs(),
25
+ 'useDocRootForStructuringCacheDir' => (($config['destination-structure'] == 'doc-root') && (Paths::canUseDocRootForStructuringCacheDir())),
26
+ //'imageRoots' => new ImageRoots(Paths::getImageRootsDefForSelectedIds($config['scope'])), // (Paths::getImageRootsDef()
27
+ 'imageRoots' => new ImageRoots(Paths::getImageRootsDefForSelectedIds(Paths::getImageRootIds())), // (Paths::getImageRootsDef()
28
+ 'image-types' => $config['image-types'],
29
+ ];
30
+
31
+
32
+ //$rootIds = Paths::filterOutSubRoots($config['scope']);
33
+
34
+ // We want to update status on ALL root dirs (so we don't have to re-run when user changes scope)
35
+ $rootIds = Paths::filterOutSubRoots(Paths::getImageRootIds());
36
+ //$rootIds = ['uploads'];
37
+ //$rootIds = ['uploads', 'themes'];
38
+
39
+ foreach ($rootIds as $rootId) {
40
+ self::updateStatusForRoot($rootId);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Pre-requirement: self::$settings is set.
46
+ *
47
+ * Idea for improvement: Traverse destination dirs instead. This will be quicker, as there will not be
48
+ * as many images (unless all have been converted), and not as many folders (non-image folders will not be present.
49
+ * however, index does not take too long to traverse, even though it has many non-image folders, so it will only
50
+ * be a problem if there are plugins or themes with extremely many folders).
51
+ */
52
+ private static function updateStatusForRoot($rootId, $dir = '')
53
+ {
54
+ if ($dir == '') {
55
+ $dir = Paths::getAbsDirById($rootId);
56
+ }
57
+
58
+ // Canonicalize because dir might contain "/./", which causes file_exists to fail (#222)
59
+ $dir = PathHelper::canonicalize($dir);
60
+
61
+ if (!@file_exists($dir) || !@is_dir($dir)) {
62
+ return [];
63
+ }
64
+
65
+ $fileIterator = new \FilesystemIterator($dir);
66
+
67
+ $results = [];
68
+
69
+ while ($fileIterator->valid()) {
70
+ $filename = $fileIterator->getFilename();
71
+
72
+ if (($filename != ".") && ($filename != "..")) {
73
+ if (@is_dir($dir . "/" . $filename)) {
74
+ $newDir = $dir . "/" . $filename;
75
+
76
+ // The new dir might have its own root id
77
+ $newRootId = Paths::findImageRootOfPath($newDir, Paths::getImageRootIds());
78
+ //echo $newRootId . ': ' . $newDir . "\n";
79
+ self::updateStatusForRoot($newRootId, $newDir);
80
+ } else {
81
+ // its a file - check if its a valid image type (jpeg or png)
82
+ $regex = '#\.(jpe?g|png)$#';
83
+ if (preg_match($regex, $filename)) {
84
+
85
+ $source = $dir . "/" . $filename;
86
+
87
+ $destination = ConvertHelperIndependent::getDestination(
88
+ $source,
89
+ self::$settings['destination-folder'],
90
+ self::$settings['ext'],
91
+ self::$settings['webExpressContentDirAbs'],
92
+ self::$settings['uploadDirAbs'],
93
+ self::$settings['useDocRootForStructuringCacheDir'],
94
+ self::$settings['imageRoots'],
95
+ //$rootId
96
+
97
+ );
98
+ $webpExists = @file_exists($destination);
99
+
100
+ //echo ($webpExists ? 'YES' : 'NO') . ' ' . $rootId . ': ' . $source . "\n";
101
+
102
+ BiggerThanSourceDummyFiles::updateStatus(
103
+ $source,
104
+ $destination,
105
+ self::$settings['webExpressContentDirAbs'],
106
+ self::$settings['imageRoots'],
107
+ self::$settings['destination-folder'],
108
+ self::$settings['ext'],
109
+ // TODO: send rootId so the function doesn't need to try all
110
+ // $rootId,
111
+ );
112
+
113
+ }
114
+ }
115
+ }
116
+ $fileIterator->next();
117
+ }
118
+ return $results;
119
+ }
120
+ }
lib/classes/CachePurge.php CHANGED
@@ -38,6 +38,10 @@ class CachePurge
38
  $numFailed += $f;
39
  }
40
 
 
 
 
 
41
 
42
  return [
43
  'delete-count' => $numDeleted,
38
  $numFailed += $f;
39
  }
40
 
41
+ // Now, purge dummy files too
42
+ $dir = Paths::getBiggerThanSourceDirAbs();
43
+ self::purgeWebPFilesInDir($dir, $filter, $config);
44
+ FileHelper::removeEmptySubFolders($dir);
45
 
46
  return [
47
  'delete-count' => $numDeleted,
lib/classes/Config.php CHANGED
@@ -36,6 +36,8 @@ class Config
36
  'cache-control-max-age' => 'one-week',
37
  'cache-control-public' => false,
38
  'scope' => ['themes', 'uploads'],
 
 
39
 
40
  // redirection rules
41
  'enable-redirection-to-converter' => true,
@@ -155,6 +157,12 @@ class Config
155
  return $config;
156
  }
157
 
 
 
 
 
 
 
158
  public static function fix($config, $checkQualityDetection = true)
159
  {
160
  if ($config === false) {
@@ -412,6 +420,7 @@ class Config
412
  $obj['destination-structure'] = $config['destination-structure'];
413
  $obj['scope'] = $config['scope'];
414
  $obj['image-types'] = $config['image-types']; // 0=none,1=jpg, 2=png, 3=both
 
415
 
416
  Option::updateOption(
417
  'webp-express-alter-html-options',
@@ -420,6 +429,9 @@ class Config
420
  );
421
  }
422
 
 
 
 
423
  public static function saveConfigurationFile($config)
424
  {
425
  $config['paths-used-in-htaccess'] = [
@@ -580,6 +592,7 @@ class Config
580
  // WOD options
581
  // -------------
582
  $wod = [
 
583
  'enable-redirection-to-converter' => $config['enable-redirection-to-converter'],
584
  'enable-redirection-to-webp-realizer' => $config['enable-redirection-to-webp-realizer'],
585
  'base-htaccess-on-these-capability-tests' => $config['base-htaccess-on-these-capability-tests'],
@@ -638,13 +651,46 @@ class Config
638
  return (self::saveWodOptionsFile($options));
639
  }
640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  /**
642
  *
643
  * $rewriteRulesNeedsUpdate:
644
  */
645
  public static function saveConfigurationAndHTAccess($config, $forceRuleUpdating = false)
646
  {
647
-
648
  // Important to do this check before saving config, because the method
649
  // compares against existing config.
650
 
36
  'cache-control-max-age' => 'one-week',
37
  'cache-control-public' => false,
38
  'scope' => ['themes', 'uploads'],
39
+ 'enable-logging' => false,
40
+ 'prevent-using-webps-larger-than-original' => true,
41
 
42
  // redirection rules
43
  'enable-redirection-to-converter' => true,
157
  return $config;
158
  }
159
 
160
+ /**
161
+ * Fix config.
162
+ *
163
+ * Among other things, the config is merged with default config, to ensure all options are present
164
+ *
165
+ */
166
  public static function fix($config, $checkQualityDetection = true)
167
  {
168
  if ($config === false) {
420
  $obj['destination-structure'] = $config['destination-structure'];
421
  $obj['scope'] = $config['scope'];
422
  $obj['image-types'] = $config['image-types']; // 0=none,1=jpg, 2=png, 3=both
423
+ $obj['prevent-using-webps-larger-than-original'] = $config['prevent-using-webps-larger-than-original'];
424
 
425
  Option::updateOption(
426
  'webp-express-alter-html-options',
429
  );
430
  }
431
 
432
+ /**
433
+ * Save configuration file. Also updates autoloaded options (such as alter html options)
434
+ */
435
  public static function saveConfigurationFile($config)
436
  {
437
  $config['paths-used-in-htaccess'] = [
592
  // WOD options
593
  // -------------
594
  $wod = [
595
+ 'enable-logging' => $config['enable-logging'],
596
  'enable-redirection-to-converter' => $config['enable-redirection-to-converter'],
597
  'enable-redirection-to-webp-realizer' => $config['enable-redirection-to-webp-realizer'],
598
  'base-htaccess-on-these-capability-tests' => $config['base-htaccess-on-these-capability-tests'],
651
  return (self::saveWodOptionsFile($options));
652
  }
653
 
654
+ /**
655
+ * Regenerate config and .htaccess files
656
+ *
657
+ * It will only happen if configuration file exists. So the method is meant for updating - ie upon migration.
658
+ * It updates:
659
+ * - config files (both) - and ensures that capability tests have been run
660
+ * - autoloaded options (such as alter html options)
661
+ * - .htaccess files (all)
662
+ */
663
+ public static function regenerateConfigAndHtaccessFiles() {
664
+ self::regenerateConfig(true);
665
+ }
666
+
667
+ /**
668
+ * Regenerate config and .htaccess files
669
+ *
670
+ * It will only happen if configuration file exists. So the method is meant for updating - ie upon migration.
671
+ * It updates:
672
+ * - config files (both) - and ensures that capability tests have been run
673
+ * - autoloaded options (such as alter html options)
674
+ * - .htaccess files - but only if needed due to configuration changes
675
+ */
676
+ public static function regenerateConfig($forceRuleUpdating = false) {
677
+ if (!self::isConfigFileThere()) {
678
+ return;
679
+ }
680
+ $config = self::loadConfig();
681
+ $config = self::fix($config, false); // fix. We do not need examining if quality detection is working
682
+ if ($config === false) {
683
+ return;
684
+ }
685
+ self::saveConfigurationAndHTAccess($config, $forceRuleUpdating);
686
+ }
687
+
688
  /**
689
  *
690
  * $rewriteRulesNeedsUpdate:
691
  */
692
  public static function saveConfigurationAndHTAccess($config, $forceRuleUpdating = false)
693
  {
 
694
  // Important to do this check before saving config, because the method
695
  // compares against existing config.
696
 
lib/classes/Convert.php CHANGED
@@ -32,6 +32,24 @@ class Convert
32
  );
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  public static function convertFile($source, $config = null, $convertOptions = null, $converter = null)
36
  {
37
  try {
@@ -84,7 +102,11 @@ class Convert
84
  // Check log dir
85
  // -------------------------------
86
  $checking = 'conversion log dir';
87
- $logDir = SanityCheck::absPath(Paths::getWebPExpressContentDirAbs() . '/log');
 
 
 
 
88
 
89
 
90
  } catch (\Exception $e) {
@@ -113,6 +135,7 @@ class Convert
113
  }
114
  //}
115
 
 
116
 
117
  if ($result['success'] === true) {
118
  $result['filesize-original'] = @filesize($source);
32
  );
33
  }
34
 
35
+ public static function updateBiggerThanOriginalMark($source, $destination = null, &$config = null)
36
+ {
37
+ if (is_null($config)) {
38
+ $config = Config::loadConfigAndFix();
39
+ }
40
+ if (is_null($destination)) {
41
+ $destination = self::getDestination($config);
42
+ }
43
+ BiggerThanSourceDummyFiles::updateStatus(
44
+ $source,
45
+ $destination,
46
+ Paths::getWebPExpressContentDirAbs(),
47
+ new ImageRoots(Paths::getImageRootsDef()),
48
+ $config['destination-folder'],
49
+ $config['destination-extension']
50
+ );
51
+ }
52
+
53
  public static function convertFile($source, $config = null, $convertOptions = null, $converter = null)
54
  {
55
  try {
102
  // Check log dir
103
  // -------------------------------
104
  $checking = 'conversion log dir';
105
+ if (isset($config['enable-logging']) && $config['enable-logging']) {
106
+ $logDir = SanityCheck::absPath(Paths::getWebPExpressContentDirAbs() . '/log');
107
+ } else {
108
+ $logDir = null;
109
+ }
110
 
111
 
112
  } catch (\Exception $e) {
135
  }
136
  //}
137
 
138
+ self::updateBiggerThanOriginalMark($source, $destination, $config);
139
 
140
  if ($result['success'] === true) {
141
  $result['filesize-original'] = @filesize($source);
lib/classes/ConvertHelperIndependent.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  /*
4
  This class is made to not be dependent on Wordpress functions and must be kept like that.
5
- It is used by webp-on-demand.php, which does not register an auto loader. It is also used for bulk conversion.
6
  */
7
  namespace WebPExpress;
8
 
@@ -77,8 +77,8 @@ class ConvertHelperIndependent
77
  /**
78
  * Get destination path corresponding to the source path given (and some configurations)
79
  *
80
- * If for example Operation mode is set to "mingled" and extension is set to "Append .webp",
81
- * the result of finding the destination path that corresponds to "/path/to/logo.jpg" will be "/path/to/logo.jpg.webp".
82
  *
83
  * @param string $source Path to source file
84
  * @param string $destinationFolder 'mingled' or 'separate'
@@ -571,7 +571,7 @@ APACHE
571
  $text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
572
 
573
  // TODO: Put version number somewhere else. Ie \WebPExpress\VersionNumber::version
574
- $text = 'WebP Express 0.21.1. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
575
 
576
  $logFile = self::getLogFilename($source, $logDir);
577
 
@@ -596,10 +596,10 @@ APACHE
596
  * @param string $source Full path to the source file that was converted.
597
  * @param string $destination Full path to the destination file (may exist or not).
598
  * @param array $convertOptions Conversion options.
599
- * @param string $logDir The folder where log files are kept.
600
  * @param string $converter (optional) Set it to convert with a specific converter.
601
  */
602
- public static function convert($source, $destination, $convertOptions, $logDir, $converter = null) {
603
  include_once __DIR__ . '/../../vendor/autoload.php';
604
 
605
  // At this point, everything has already been checked for sanity. But for good meassure, lets
@@ -620,7 +620,9 @@ APACHE
620
 
621
  // Check that log path is sane and inside document root
622
  // -------------------------------------------------------
623
- $logDir = SanityCheck::absPathIsInDocRoot($logDir);
 
 
624
 
625
 
626
  // PS: No need to check $logMsgTop. Log files are markdown and stored as ".md". They can do no harm.
@@ -658,7 +660,9 @@ APACHE
658
  //$msg = 'oh no';
659
  }
660
 
661
- self::saveLog($source, $logDir, $logger->getMarkDown("\n\r"), 'Conversion triggered using bulk conversion');
 
 
662
 
663
  return [
664
  'success' => $success,
@@ -672,7 +676,7 @@ APACHE
672
  * Serve a converted file (if it does not already exist, a conversion is triggered - all handled in webp-convert).
673
  *
674
  */
675
- public static function serveConverted($source, $destination, $serveOptions, $logDir, $logMsgTop = '')
676
  {
677
  include_once __DIR__ . '/../../vendor/autoload.php';
678
 
@@ -697,8 +701,9 @@ APACHE
697
  // Check that log path is sane
698
  // -------------------------------------------------------
699
  //$logDir = SanityCheck::absPathIsInDocRoot($logDir);
700
- $logDir = SanityCheck::absPath($logDir);
701
-
 
702
 
703
  // PS: No need to check $logMsgTop. Log files are markdown and stored as ".md". They can do no harm.
704
 
@@ -712,10 +717,11 @@ APACHE
712
 
713
  $convertLogger = new BufferLogger();
714
  WebPConvert::serveConverted($source, $destination, $serveOptions, null, $convertLogger);
715
- $convertLog = $convertLogger->getMarkDown("\n\r");
716
- if ($convertLog != '') {
717
- self::saveLog($source, $logDir, $convertLog, $logMsgTop);
 
 
718
  }
719
-
720
  }
721
  }
2
 
3
  /*
4
  This class is made to not be dependent on Wordpress functions and must be kept like that.
5
+ It is used by webp-on-demand.php. It is also used for bulk conversion.
6
  */
7
  namespace WebPExpress;
8
 
77
  /**
78
  * Get destination path corresponding to the source path given (and some configurations)
79
  *
80
+ * If for example Operation mode is set to "mingled" and extension is set to "Append .webp",
81
+ * the result of finding the destination path that corresponds to "/path/to/logo.jpg" will be "/path/to/logo.jpg.webp".
82
  *
83
  * @param string $source Path to source file
84
  * @param string $destinationFolder 'mingled' or 'separate'
571
  $text = preg_replace('#' . preg_quote($_SERVER["DOCUMENT_ROOT"]) . '#', '[doc-root]', $text);
572
 
573
  // TODO: Put version number somewhere else. Ie \WebPExpress\VersionNumber::version
574
+ $text = 'WebP Express 0.22.0. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;
575
 
576
  $logFile = self::getLogFilename($source, $logDir);
577
 
596
  * @param string $source Full path to the source file that was converted.
597
  * @param string $destination Full path to the destination file (may exist or not).
598
  * @param array $convertOptions Conversion options.
599
+ * @param string $logDir The folder where log files are kept or null for no logging
600
  * @param string $converter (optional) Set it to convert with a specific converter.
601
  */
602
+ public static function convert($source, $destination, $convertOptions, $logDir = null, $converter = null) {
603
  include_once __DIR__ . '/../../vendor/autoload.php';
604
 
605
  // At this point, everything has already been checked for sanity. But for good meassure, lets
620
 
621
  // Check that log path is sane and inside document root
622
  // -------------------------------------------------------
623
+ if (!is_null($logDir)) {
624
+ $logDir = SanityCheck::absPathIsInDocRoot($logDir);
625
+ }
626
 
627
 
628
  // PS: No need to check $logMsgTop. Log files are markdown and stored as ".md". They can do no harm.
660
  //$msg = 'oh no';
661
  }
662
 
663
+ if (!is_null($logDir)) {
664
+ self::saveLog($source, $logDir, $logger->getMarkDown("\n\r"), 'Conversion triggered using bulk conversion');
665
+ }
666
 
667
  return [
668
  'success' => $success,
676
  * Serve a converted file (if it does not already exist, a conversion is triggered - all handled in webp-convert).
677
  *
678
  */
679
+ public static function serveConverted($source, $destination, $serveOptions, $logDir = null, $logMsgTop = '')
680
  {
681
  include_once __DIR__ . '/../../vendor/autoload.php';
682
 
701
  // Check that log path is sane
702
  // -------------------------------------------------------
703
  //$logDir = SanityCheck::absPathIsInDocRoot($logDir);
704
+ if ($logDir != null) {
705
+ $logDir = SanityCheck::absPath($logDir);
706
+ }
707
 
708
  // PS: No need to check $logMsgTop. Log files are markdown and stored as ".md". They can do no harm.
709
 
717
 
718
  $convertLogger = new BufferLogger();
719
  WebPConvert::serveConverted($source, $destination, $serveOptions, null, $convertLogger);
720
+ if (!is_null($logDir)) {
721
+ $convertLog = $convertLogger->getMarkDown("\n\r");
722
+ if ($convertLog != '') {
723
+ self::saveLog($source, $logDir, $convertLog, $logMsgTop);
724
+ }
725
  }
 
726
  }
727
  }
lib/classes/HTAccessRules.php CHANGED
@@ -74,6 +74,25 @@ class HTAccessRules
74
  return false;
75
  }
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  /**
78
  * Decides if .htaccess rules needs to be updated.
79
  *
@@ -122,7 +141,8 @@ class HTAccessRules
122
  'destination-folder' => 'separate',
123
  'destination-extension' => 'append',
124
  'destination-structure' => 'doc-root',
125
- 'scope' => ['themes', 'uploads']
 
126
  ];
127
 
128
  // If one of the props have changed, we need to update.
@@ -277,7 +297,6 @@ class HTAccessRules
277
  {
278
  $rules = '';
279
 
280
-
281
  if (self::$mingled) {
282
  // TODO:
283
  // Only write mingled rules for "uploads" dir.
@@ -440,6 +459,8 @@ class HTAccessRules
440
  $params[] = "wp-content=" . Paths::getContentDirRel();
441
  }
442
 
 
 
443
  $rewriteRuleStart = '^/?(.+)';
444
  $rules .= " RewriteRule " . $rewriteRuleStart . "\.(webp)$ " .
445
  "/" . self::getWebPRealizerUrlPath() .
@@ -458,7 +479,8 @@ class HTAccessRules
458
  $flags = [];
459
 
460
  if (!self::$passThroughEnvVarDefinitelyUnavailable) {
461
- $flags[] = 'E=WE_WP_CONTENT_REL_TO_PLUGIN_DIR:' . Paths::getContentDirRelToPluginDir();
 
462
  $flags[] = 'E=WE_DESTINATION_REL_HTACCESS:$0';
463
  $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw either be "uploads" or "cache"
464
  }
@@ -466,7 +488,8 @@ class HTAccessRules
466
  $flags[] = 'L';
467
 
468
  if (!self::$passThroughEnvVarDefinitelyAvailable) {
469
- $params[] = 'xwp-content-rel-to-plugin-dir=x' . Paths::getContentDirRelToPluginDir();
 
470
  $params[] = 'xdestination-rel-htaccess=x$0';
471
  $params[] = 'htaccess-id=' . self::$htaccessDir;
472
  }
@@ -643,9 +666,9 @@ class HTAccessRules
643
 
644
  /*
645
  Create something like this:
646
-
647
  RewriteCond %{REQUEST_FILENAME} -f
648
- RewriteRule (?i).*(\.jpe?g|\.png)$ /plugins-moved/webp-express/wod/webp-on-demand.php [E=WE_WP_CONTENT_REL_TO_PLUGIN_DIR:../../we0-content,E=WE_SOURCE_REL_HTACCESS:$0,E=WE_HTACCESS_ID:themes,NC,L]
 
649
  */
650
 
651
  // Making sure the source exists
@@ -655,7 +678,8 @@ class HTAccessRules
655
  $flags = [];
656
 
657
  if (!self::$passThroughEnvVarDefinitelyUnavailable) {
658
- $flags[] = 'E=WE_WP_CONTENT_REL_TO_PLUGIN_DIR:' . Paths::getContentDirRelToPluginDir();
 
659
  $flags[] = 'E=WE_SOURCE_REL_HTACCESS:$0';
660
  $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw be one of the image roots. It will not be "cache"
661
  }
@@ -663,16 +687,25 @@ class HTAccessRules
663
  $flags[] = 'L';
664
 
665
  if (!self::$passThroughEnvVarDefinitelyAvailable) {
666
- $params[] = 'xwp-content-rel-to-plugin-dir=x' . Paths::getContentDirRelToPluginDir();
 
667
  $params[] = 'xsource-rel-htaccess=x$0';
668
  $params[] = 'htaccess-id=' . self::$htaccessDir;
669
  }
670
 
 
 
 
 
 
 
 
671
  // self::$appendWebP cannot be used, we need the following in order for
672
  // it to work for uploads in: Mingled, "Set to WebP", "Image roots".
673
  // TODO! Will it work for ie theme images?
674
  // - well, it should, because the script is passed $0. Not matching the ".png" part of the filename
675
  // only means it is a bit more greedy than it has to
 
676
  $appendWebP = !(self::$config['destination-extension'] == 'set');
677
 
678
  $rules .= " RewriteRule (?i).*" . ($appendWebP ? "(" . self::$fileExtIncludingDot . ")" : "") . "$ " .
@@ -680,7 +713,7 @@ class HTAccessRules
680
  (count($params) > 0 ? "?" . implode('&', $params) : "") .
681
  " [" . implode(',', $flags) . "]\n";
682
 
683
-
684
  /*
685
  */
686
 
@@ -849,6 +882,10 @@ class HTAccessRules
849
  self::$modHeaderDefinitelyUnavailable = ($capTests['modHeaderWorking'] === false);
850
  self::$passThroughHeaderDefinitelyUnavailable = ($capTests['passThroughHeaderWorking'] === false);
851
  self::$passThroughHeaderDefinitelyAvailable = ($capTests['passThroughHeaderWorking'] === true);
 
 
 
 
852
  self::$canDefinitelyRunTestScriptInWOD = ($capTests['canRunTestScriptInWOD'] === true);
853
  self::$canDefinitelyRunTestScriptInWOD2 = ($capTests['canRunTestScriptInWOD2'] === true);
854
 
@@ -1016,6 +1053,40 @@ class HTAccessRules
1016
  $rules .= "<IfModule mod_rewrite.c>\n" .
1017
  " RewriteEngine On\n\n";
1018
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1019
  if (self::$config['redirect-to-existing-in-htaccess']) {
1020
  $rules .= self::redirectToExistingRules();
1021
  }
74
  return false;
75
  }
76
 
77
+ /**
78
+ *
79
+ * Note that server variables are only allowed some places in the .htaccess.
80
+ * It is for example not allowed in CondPattern so something like this will not work:
81
+ * RewriteCond %{REQUEST_FILENAME} (?i)(%{DOCUMENT_ROOT}/wordpress/wp-content/themes/)(.*)(\.jpe?g|\.png)$
82
+ */
83
+ private static function replaceDocRootWithApacheTokenIfDocRootAvailable($absPath)
84
+ {
85
+ // TODO: I would like to test this thoroughly before using so we do nothing now:
86
+ return $absPath;
87
+
88
+ if (PathHelper::isDocRootAvailable()) {
89
+ if (strpos($absPath, $_SERVER['DOCUMENT_ROOT']) === 0) {
90
+ return "%{DOCUMENT_ROOT}" . substr($absPath, strlen($_SERVER['DOCUMENT_ROOT']));
91
+ }
92
+ }
93
+ return $absPath;
94
+ }
95
+
96
  /**
97
  * Decides if .htaccess rules needs to be updated.
98
  *
141
  'destination-folder' => 'separate',
142
  'destination-extension' => 'append',
143
  'destination-structure' => 'doc-root',
144
+ 'scope' => ['themes', 'uploads'],
145
+ 'prevent-using-webps-larger-than-original' => true,
146
  ];
147
 
148
  // If one of the props have changed, we need to update.
297
  {
298
  $rules = '';
299
 
 
300
  if (self::$mingled) {
301
  // TODO:
302
  // Only write mingled rules for "uploads" dir.
459
  $params[] = "wp-content=" . Paths::getContentDirRel();
460
  }
461
 
462
+ // When matching from the beginning (^), we need the "/?" in order to make it work on litespeed too.
463
+ // Here is why: https://openlitespeed.org/kb/apache-rewrite-rules-in-openlitespeed/
464
  $rewriteRuleStart = '^/?(.+)';
465
  $rules .= " RewriteRule " . $rewriteRuleStart . "\.(webp)$ " .
466
  "/" . self::getWebPRealizerUrlPath() .
479
  $flags = [];
480
 
481
  if (!self::$passThroughEnvVarDefinitelyUnavailable) {
482
+ //$flags[] = 'E=WE_WP_CONTENT_REL_TO_PLUGIN_DIR:' . Paths::getContentDirRelToPluginDir();
483
+ $flags[] = 'E=WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR:' . Paths::getContentDirRelToWebPExpressPluginDir();
484
  $flags[] = 'E=WE_DESTINATION_REL_HTACCESS:$0';
485
  $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw either be "uploads" or "cache"
486
  }
488
  $flags[] = 'L';
489
 
490
  if (!self::$passThroughEnvVarDefinitelyAvailable) {
491
+ //$params[] = 'xwp-content-rel-to-plugin-dir=x' . Paths::getContentDirRelToPluginDir();
492
+ $params[] = 'xwp-content-rel-to-we-plugin-dir=x' . Paths::getContentDirRelToWebPExpressPluginDir();
493
  $params[] = 'xdestination-rel-htaccess=x$0';
494
  $params[] = 'htaccess-id=' . self::$htaccessDir;
495
  }
666
 
667
  /*
668
  Create something like this:
 
669
  RewriteCond %{REQUEST_FILENAME} -f
670
+ RewriteCond %{REQUEST_FILENAME} (?i)(.*)(\.jpe?g|\.png)$
671
+ RewriteRule (?i).*$ /wordpress/wp-content/plugins/webp-express/wod/webp-on-demand.php [E=WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR:../..,E=WE_SOURCE_REL_HTACCESS:$0,E=WE_HTACCESS_ID:uploads,NC,L]
672
  */
673
 
674
  // Making sure the source exists
678
  $flags = [];
679
 
680
  if (!self::$passThroughEnvVarDefinitelyUnavailable) {
681
+ //$flags[] = 'E=WE_WP_CONTENT_REL_TO_PLUGIN_DIR:' . Paths::getContentDirRelToPluginDir();
682
+ $flags[] = 'E=WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR:' . Paths::getContentDirRelToWebPExpressPluginDir();
683
  $flags[] = 'E=WE_SOURCE_REL_HTACCESS:$0';
684
  $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw be one of the image roots. It will not be "cache"
685
  }
687
  $flags[] = 'L';
688
 
689
  if (!self::$passThroughEnvVarDefinitelyAvailable) {
690
+ //$params[] = 'xwp-content-rel-to-plugin-dir=x' . Paths::getContentDirRelToPluginDir();
691
+ $params[] = 'xwp-content-rel-to-we-plugin-dir=x' . Paths::getContentDirRelToWebPExpressPluginDir();
692
  $params[] = 'xsource-rel-htaccess=x$0';
693
  $params[] = 'htaccess-id=' . self::$htaccessDir;
694
  }
695
 
696
+ $rules .= " RewriteCond %{REQUEST_FILENAME} (?i)(.*)(" . self::$fileExtIncludingDot . ")$\n";
697
+
698
+ $rules .= " RewriteRule (?i).*$ " .
699
+ "/" . self::getWodUrlPath() .
700
+ (count($params) > 0 ? "?" . implode('&', $params) : "") .
701
+ " [" . implode(',', $flags) . "]\n";
702
+
703
  // self::$appendWebP cannot be used, we need the following in order for
704
  // it to work for uploads in: Mingled, "Set to WebP", "Image roots".
705
  // TODO! Will it work for ie theme images?
706
  // - well, it should, because the script is passed $0. Not matching the ".png" part of the filename
707
  // only means it is a bit more greedy than it has to
708
+ /*
709
  $appendWebP = !(self::$config['destination-extension'] == 'set');
710
 
711
  $rules .= " RewriteRule (?i).*" . ($appendWebP ? "(" . self::$fileExtIncludingDot . ")" : "") . "$ " .
713
  (count($params) > 0 ? "?" . implode('&', $params) : "") .
714
  " [" . implode(',', $flags) . "]\n";
715
 
716
+ */
717
  /*
718
  */
719
 
882
  self::$modHeaderDefinitelyUnavailable = ($capTests['modHeaderWorking'] === false);
883
  self::$passThroughHeaderDefinitelyUnavailable = ($capTests['passThroughHeaderWorking'] === false);
884
  self::$passThroughHeaderDefinitelyAvailable = ($capTests['passThroughHeaderWorking'] === true);
885
+
886
+ self::$passThroughEnvVarDefinitelyUnavailable = ($capTests['passThroughEnvWorking'] === false);
887
+ self::$passThroughEnvVarDefinitelyAvailable = ($capTests['passThroughEnvWorking'] === true);
888
+
889
  self::$canDefinitelyRunTestScriptInWOD = ($capTests['canRunTestScriptInWOD'] === true);
890
  self::$canDefinitelyRunTestScriptInWOD2 = ($capTests['canRunTestScriptInWOD2'] === true);
891
 
1053
  $rules .= "<IfModule mod_rewrite.c>\n" .
1054
  " RewriteEngine On\n\n";
1055
 
1056
+ $rules .= " # Escape hatch #1: Adding ?original to an url can be used to bypass redirection\n";
1057
+ $rules .= " RewriteCond %{QUERY_STRING} original$\n";
1058
+ $rules .= " RewriteCond %{REQUEST_FILENAME} -f\n";
1059
+ $rules .= " RewriteRule . - [L]\n\n";
1060
+
1061
+ $rules .= " # Escape hatch #2: Placing an empty file in the same folder as the jpeg/png which has same file name, but \".do-not-convert\" appended will bypass redirection\n";
1062
+ $rules .= " RewriteCond %{REQUEST_FILENAME} (?i)(.*)(\.jpe?g|\.png)$\n";
1063
+ $rules .= " RewriteCond %1%2\.do-not-convert -f\n";
1064
+ $rules .= " RewriteRule . - [L]\n\n";
1065
+
1066
+ if ($config['prevent-using-webps-larger-than-original']) {
1067
+ $rules .= " # Avoid redirecting to webp files that are bigger than the original\n";
1068
+ $rules .= " RewriteCond %{REQUEST_FILENAME} -f\n";
1069
+
1070
+ // Find relative path of source (accessible as %2%3)
1071
+ $rules .= " RewriteCond %{REQUEST_FILENAME} (?i)(" . self::$htaccessDirAbs . "/)(.*)(" .self::$fileExtIncludingDot . ")$\n";
1072
+
1073
+ // Check if dummy file exists
1074
+ $cacheDirForThisRoot = PathHelper::fixAbsPathToUseUnresolvedDocRoot(
1075
+ Paths::getBiggerThanSourceDirAbs() . '/' . self::$htaccessDir
1076
+ );
1077
+ $rules .= " RewriteCond " .
1078
+ self::replaceDocRootWithApacheTokenIfDocRootAvailable($cacheDirForThisRoot) .
1079
+ "/%2%3.webp -f\n";
1080
+
1081
+ $rules .= " RewriteRule . - [L]\n\n";
1082
+
1083
+ }
1084
+
1085
+ // In the future, we could let user add exeptions in UI. Also for folders
1086
+ // in order to make this work for folders, we will need to update the .htaccess
1087
+ // and list the exceptions here with rules like this:
1088
+ // RewriteRule ^uploads/2021/06/ - [L]
1089
+
1090
  if (self::$config['redirect-to-existing-in-htaccess']) {
1091
  $rules .= self::redirectToExistingRules();
1092
  }
lib/classes/HandleDeleteFileHook.php CHANGED
@@ -20,12 +20,16 @@ class HandleDeleteFileHook
20
  return $filename;
21
  }
22
 
23
- $destination = Convert::getDestination($filename);
 
24
  if (@file_exists($destination)) {
25
- if (!@unlink($destination)) {
 
 
26
  error_log('WebP Express failed deleting webp:' . $destination);
27
- }
28
  }
 
29
  return $filename;
30
  }
31
  }
20
  return $filename;
21
  }
22
 
23
+ $config = Config::loadConfigAndFix();
24
+ $destination = Convert::getDestination($filename, $config);
25
  if (@file_exists($destination)) {
26
+ if (@unlink($destination)) {
27
+ Convert::updateBiggerThanOriginalMark($filename, $destination, $config);
28
+ } else {
29
  error_log('WebP Express failed deleting webp:' . $destination);
30
+ }
31
  }
32
+
33
  return $filename;
34
  }
35
  }
lib/classes/LogPurge.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ class LogPurge
6
+ {
7
+
8
+ /**
9
+ * - Removes cache dir
10
+ * - Removes all files with ".webp" extension in upload dir (if set to mingled)
11
+ */
12
+ public static function purge()
13
+ {
14
+ DismissableMessages::dismissMessage('0.14.0/suggest-wipe-because-lossless');
15
+
16
+ $filter = [
17
+ 'only-png' => $onlyPng,
18
+ 'only-with-corresponding-original' => false
19
+ ];
20
+
21
+ $numDeleted = 0;
22
+ $numFailed = 0;
23
+
24
+ $dir = Paths::getLogDirAbs();
25
+ list($numDeleted, $numFailed) = self::purgeLogFilesInDir($dir);
26
+ FileHelper::removeEmptySubFolders($dir);
27
+
28
+ return [
29
+ 'delete-count' => $numDeleted,
30
+ 'fail-count' => $numFailed
31
+ ];
32
+
33
+ //$successInRemovingCacheDir = FileHelper::rrmdir(Paths::getCacheDirAbs());
34
+
35
+ }
36
+
37
+
38
+ /**
39
+ * Purge log files in a dir
40
+ *
41
+ * @return [num files deleted, num files failed to delete]
42
+ */
43
+ private static function purgeLogFilesInDir($dir)
44
+ {
45
+ if (!@file_exists($dir) || !@is_dir($dir)) {
46
+ return [0, 0];
47
+ }
48
+
49
+ $numFilesDeleted = 0;
50
+ $numFilesFailedDeleting = 0;
51
+
52
+ $fileIterator = new \FilesystemIterator($dir);
53
+ while ($fileIterator->valid()) {
54
+ $filename = $fileIterator->getFilename();
55
+
56
+ if (($filename != ".") && ($filename != "..")) {
57
+
58
+ if (@is_dir($dir . "/" . $filename)) {
59
+ list($r1, $r2) = self::purgeLogFilesInDir($dir . "/" . $filename);
60
+ $numFilesDeleted += $r1;
61
+ $numFilesFailedDeleting += $r2;
62
+ } else {
63
+
64
+ // its a file
65
+ // Run through filters, which each may set "skipThis" to true
66
+
67
+ $skipThis = false;
68
+
69
+ // filter: It must have ".md" extension
70
+ if (!$skipThis && !preg_match('#\.md$#', $filename)) {
71
+ $skipThis = true;
72
+ }
73
+
74
+ if (!$skipThis) {
75
+ if (@unlink($dir . "/" . $filename)) {
76
+ $numFilesDeleted++;
77
+ } else {
78
+ $numFilesFailedDeleting++;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ $fileIterator->next();
84
+ }
85
+ return [$numFilesDeleted, $numFilesFailedDeleting];
86
+ }
87
+
88
+ public static function processAjaxPurgeLog()
89
+ {
90
+
91
+ if (!check_ajax_referer('webpexpress-ajax-purge-log-nonce', 'nonce', false)) {
92
+ wp_send_json_error('The security nonce has expired. You need to reload the settings page (press F5) and try again)');
93
+ wp_die();
94
+ }
95
+ $result = self::purge($config);
96
+ echo json_encode($result, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
97
+ wp_die();
98
+ }
99
+ }
lib/classes/PathHelper.php CHANGED
@@ -7,7 +7,9 @@ class PathHelper
7
 
8
  public static function isDocRootAvailable() {
9
 
10
- //return false;
 
 
11
 
12
  if (!isset($_SERVER['DOCUMENT_ROOT'])) {
13
  return false;
@@ -43,6 +45,17 @@ class PathHelper
43
  );
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
46
  public static function fixAbsPathToUseUnresolvedDocRoot($absPath) {
47
  if (self::isDocRootAvailableAndResolvable()) {
48
  if (strpos($absPath, realpath($_SERVER['DOCUMENT_ROOT'])) === 0) {
7
 
8
  public static function isDocRootAvailable() {
9
 
10
+ // BTW:
11
+ // Note that DOCUMENT_ROOT does not end with trailing slash on old litespeed servers:
12
+ // https://www.litespeedtech.com/support/forum/threads/document_root-trailing-slash.5304/
13
 
14
  if (!isset($_SERVER['DOCUMENT_ROOT'])) {
15
  return false;
45
  );
46
  }
47
 
48
+ /**
49
+ * When the rewrite rules are using the absolute dir, the rewrite rules does not work if that dir
50
+ * is outside document root. This poses a problem if some part of the document root has been symlinked.
51
+ *
52
+ * This method "unresolves" the document root part of a dir.
53
+ * That is: It takes an absolute url, looks to see if it begins with the resolved document root.
54
+ * In case it does, it replaces the resolved document root with the unresolved document root.
55
+ *
56
+ * Unfortunately we can only unresolve when document root is available and resolvable.
57
+ * - which is sad, because the image-roots was introduced in order to get it to work on setups
58
+ */
59
  public static function fixAbsPathToUseUnresolvedDocRoot($absPath) {
60
  if (self::isDocRootAvailableAndResolvable()) {
61
  if (strpos($absPath, realpath($_SERVER['DOCUMENT_ROOT'])) === 0) {
lib/classes/Paths.php CHANGED
@@ -163,6 +163,12 @@ class Paths
163
  return ['uploads', 'themes', 'plugins', 'wp-content', 'index'];
164
  }
165
 
 
 
 
 
 
 
166
  public static function findImageRootOfPath($path, $rootIdsToSearch) {
167
  foreach ($rootIdsToSearch as $rootId) {
168
  if (PathHelper::isPathWithinExistingDirPath($path, self::getAbsDirById($rootId))) {
@@ -339,6 +345,10 @@ class Paths
339
  {
340
  return PathHelper::getRelDir(self::getPluginDirAbs(), self::getContentDirAbs());
341
  }
 
 
 
 
342
 
343
 
344
  public static function isWPContentDirMoved()
@@ -493,6 +503,13 @@ APACHE
493
  return self::getWebPExpressContentDirAbs() . '/log';
494
  }
495
 
 
 
 
 
 
 
 
496
  // ------------ Plugin Dir (all plugins) -------------
497
 
498
  public static function getPluginDirAbs()
163
  return ['uploads', 'themes', 'plugins', 'wp-content', 'index'];
164
  }
165
 
166
+ /**
167
+ * Find which rootId a path belongs to.
168
+ *
169
+ * Note: If the root ids passed are ordered the way getImageRootIds() returns them, the root id
170
+ * returned will be the "deepest"
171
+ */
172
  public static function findImageRootOfPath($path, $rootIdsToSearch) {
173
  foreach ($rootIdsToSearch as $rootId) {
174
  if (PathHelper::isPathWithinExistingDirPath($path, self::getAbsDirById($rootId))) {
345
  {
346
  return PathHelper::getRelDir(self::getPluginDirAbs(), self::getContentDirAbs());
347
  }
348
+ public static function getContentDirRelToWebPExpressPluginDir()
349
+ {
350
+ return PathHelper::getRelDir(self::getWebPExpressPluginDirAbs(), self::getContentDirAbs());
351
+ }
352
 
353
 
354
  public static function isWPContentDirMoved()
503
  return self::getWebPExpressContentDirAbs() . '/log';
504
  }
505
 
506
+ // ------------ Bigger-than-source dir -------------
507
+
508
+ public static function getBiggerThanSourceDirAbs()
509
+ {
510
+ return self::getWebPExpressContentDirAbs() . '/webp-images-bigger-than-source';
511
+ }
512
+
513
  // ------------ Plugin Dir (all plugins) -------------
514
 
515
  public static function getPluginDirAbs()
lib/classes/WCFMApi.php CHANGED
@@ -209,10 +209,11 @@ class WCFMApi
209
  $rootId = array_shift($pathTokens); // Shift off the first item, which is the scope
210
  $relPath = implode('/', $pathTokens);
211
  $config = Config::loadConfigAndFix();
212
- $rootIds = Paths::filterOutSubRoots($config['scope']);
213
  if (!in_array($rootId, $rootIds)) {
214
- throw new \Exception('Invalid scope');
215
- }
 
216
 
217
  $absPath = Paths::getAbsDirById($rootId) . '/' . $relPath;
218
  //absPathExistsAndIsFile
@@ -222,7 +223,7 @@ class WCFMApi
222
  //'filename' => $absPath,
223
  //'abspath' => $absPath,
224
  'size' => filesize($absPath),
225
- 'url' => Paths::getUrlById($rootId) . '/' . $relPath,
226
  ]
227
  ];
228
 
@@ -249,7 +250,7 @@ class WCFMApi
249
  $result['converted'] = [
250
  'abspath' => $absPathDest,
251
  'size' => filesize($absPathDest),
252
- 'url' => $destinationUrl,
253
  'log' => ''
254
  ];
255
  }
@@ -315,9 +316,9 @@ class WCFMApi
315
  }
316
 
317
  $config = Config::loadConfigAndFix();
318
- $rootIds = Paths::filterOutSubRoots($config['scope']);
319
-
320
  if ($path == '/') {
 
321
  $result = ['children'=>[]];
322
  foreach ($rootIds as $rootId) {
323
  $result['children'][] = [
@@ -354,8 +355,8 @@ class WCFMApi
354
 
355
  //throw new \Exception('Invalid rootId' . print_r($listOptions));
356
 
357
-
358
  $list = BulkConvert::getListRecursively($relPath, $listOptions);
 
359
  return ['children' => $list];
360
  }
361
 
209
  $rootId = array_shift($pathTokens); // Shift off the first item, which is the scope
210
  $relPath = implode('/', $pathTokens);
211
  $config = Config::loadConfigAndFix();
212
+ /*$rootIds = Paths::filterOutSubRoots($config['scope']);
213
  if (!in_array($rootId, $rootIds)) {
214
+ throw new \Exception('Invalid scope (have you perhaps changed the scope setting after igniting the file manager?)');
215
+ }*/
216
+ $rootIds = $rootIds = Paths::getImageRootIds();
217
 
218
  $absPath = Paths::getAbsDirById($rootId) . '/' . $relPath;
219
  //absPathExistsAndIsFile
223
  //'filename' => $absPath,
224
  //'abspath' => $absPath,
225
  'size' => filesize($absPath),
226
+ 'url' => Paths::getUrlById($rootId) . '/' . $relPath . '?' . SelfTestHelper::randomDigitsAndLetters(8) . '&original',
227
  ]
228
  ];
229
 
250
  $result['converted'] = [
251
  'abspath' => $absPathDest,
252
  'size' => filesize($absPathDest),
253
+ 'url' => $destinationUrl . '?' . SelfTestHelper::randomDigitsAndLetters(8),
254
  'log' => ''
255
  ];
256
  }
316
  }
317
 
318
  $config = Config::loadConfigAndFix();
319
+ $rootIds = Paths::getImageRootIds();
 
320
  if ($path == '/') {
321
+ $rootIds = Paths::filterOutSubRoots($config['scope']);
322
  $result = ['children'=>[]];
323
  foreach ($rootIds as $rootId) {
324
  $result['children'][] = [
355
 
356
  //throw new \Exception('Invalid rootId' . print_r($listOptions));
357
 
 
358
  $list = BulkConvert::getListRecursively($relPath, $listOptions);
359
+
360
  return ['children' => $list];
361
  }
362
 
lib/classes/WebPOnDemand.php CHANGED
@@ -242,14 +242,26 @@ class WebPOnDemand extends WodConfigLoader
242
  }
243
  }
244
 
 
 
 
245
  ConvertHelperIndependent::serveConverted(
246
  $source,
247
  $destination,
248
  $serveOptions,
249
- self::$webExpressContentDirAbs . '/log',
250
  'Conversion triggered with the conversion script (wod/webp-on-demand.php)'
251
  );
252
 
 
 
 
 
 
 
 
 
 
253
  self::fixConfigIfEwwwDiscoveredNonFunctionalApiKeys();
254
  }
255
 
242
  }
243
  }
244
 
245
+ $loggingEnabled = (isset($wodOptions['enable-logging']) ? $wodOptions['enable-logging'] : true);
246
+ $logDir = ($loggingEnabled ? self::$webExpressContentDirAbs . '/log' : null);
247
+
248
  ConvertHelperIndependent::serveConverted(
249
  $source,
250
  $destination,
251
  $serveOptions,
252
+ $logDir,
253
  'Conversion triggered with the conversion script (wod/webp-on-demand.php)'
254
  );
255
 
256
+ BiggerThanSourceDummyFiles::updateStatus(
257
+ $source,
258
+ $destination,
259
+ self::$webExpressContentDirAbs,
260
+ self::getImageRootsDef(),
261
+ $wodOptions['destination-folder'],
262
+ $wodOptions['destination-extension']
263
+ );
264
+
265
  self::fixConfigIfEwwwDiscoveredNonFunctionalApiKeys();
266
  }
267
 
lib/classes/WebPRealizer.php CHANGED
@@ -239,14 +239,26 @@ class WebPRealizer extends WodConfigLoader
239
  $serveOptions['fail-when-fail-fails'] = '404';
240
  $serveOptions['serve-image']['headers']['vary-accept'] = false;
241
 
 
 
 
242
  ConvertHelperIndependent::serveConverted(
243
  $source,
244
  $destination,
245
  $serveOptions,
246
- self::$webExpressContentDirAbs . '/log',
247
  'Conversion triggered with the conversion script (wod/webp-realizer.php)'
248
  );
249
 
 
 
 
 
 
 
 
 
 
250
  self::fixConfigIfEwwwDiscoveredNonFunctionalApiKeys();
251
  }
252
 
239
  $serveOptions['fail-when-fail-fails'] = '404';
240
  $serveOptions['serve-image']['headers']['vary-accept'] = false;
241
 
242
+ $loggingEnabled = (isset($wodOptions['enable-logging']) ? $wodOptions['enable-logging'] : true);
243
+ $logDir = ($loggingEnabled ? self::$webExpressContentDirAbs . '/log' : null);
244
+
245
  ConvertHelperIndependent::serveConverted(
246
  $source,
247
  $destination,
248
  $serveOptions,
249
+ $logDir,
250
  'Conversion triggered with the conversion script (wod/webp-realizer.php)'
251
  );
252
 
253
+ BiggerThanSourceDummyFiles::updateStatus(
254
+ $source,
255
+ $destination,
256
+ self::$webExpressContentDirAbs,
257
+ self::getImageRootsDef(),
258
+ $wodOptions['destination-folder'],
259
+ $wodOptions['destination-extension']
260
+ );
261
+
262
  self::fixConfigIfEwwwDiscoveredNonFunctionalApiKeys();
263
  }
264
 
lib/classes/WodConfigLoader.php CHANGED
@@ -119,31 +119,48 @@ class WodConfigLoader
119
  protected static function getWebPExpressContentDirNoDocRoot() {
120
  // Check wp-content
121
  // ----------------------
122
- self::$checking = 'path to wp-content dir';
123
 
124
- // Passed in env variable?
125
- $wpContentDirRelToPluginDir = self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_PLUGIN_DIR');
126
- if ($wpContentDirRelToPluginDir === false) {
 
127
  // Passed in QS?
128
- if (isset($_GET['xwp-content-rel-to-plugin-dir'])) {
129
-
130
- $xwpContentDirRelToPluginDir = SanityCheck::noControlChars($_GET['xwp-content-rel-to-plugin-dir']);
131
- $wpContentDirRelToPluginDir = SanityCheck::pathDirectoryTraversalAllowed(substr($xwpContentDirRelToPluginDir, 1));
 
132
 
133
- } else {
134
- throw new \Exception('Path to wp-content was not received');
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
 
136
  }
137
 
 
138
  // Check WebP Express content dir
139
  // ---------------------------------
140
  self::$checking = 'WebP Express content dir';
141
- // echo 'dir:' . $wpContentDirRelToPluginDir . '<br>'; exit;
142
 
143
- $pathToPluginDir = dirname(dirname(dirname(__DIR__)));
144
- //echo $pathToPluginDir; exit;
145
 
146
- $webExpressContentDirAbs = SanityCheck::pathDirectoryTraversalAllowed($pathToPluginDir . '/' . $wpContentDirRelToPluginDir . '/webp-express');
 
147
  //echo $webExpressContentDirAbs; exit;
148
  if (@!file_exists($webExpressContentDirAbs)) {
149
  throw new \Exception('Dir not found');
@@ -166,6 +183,8 @@ class WodConfigLoader
166
  protected static function loadConfig() {
167
 
168
  $usingDocRoot = !(
 
 
169
  isset($_GET['xwp-content-rel-to-plugin-dir']) ||
170
  self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_PLUGIN_DIR')
171
  );
119
  protected static function getWebPExpressContentDirNoDocRoot() {
120
  // Check wp-content
121
  // ----------------------
122
+ self::$checking = 'relative path between webp-express plugin dir and wp-content dir';
123
 
124
+ // From v0.22.0, we pass relative to webp-express dir rather than to the general plugin dir.
125
+ // - this allows symlinking the webp-express dir.
126
+ $wpContentDirRelToWEPluginDir = self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR');
127
+ if (!$wpContentDirRelToWEPluginDir) {
128
  // Passed in QS?
129
+ if (isset($_GET['xwp-content-rel-to-we-plugin-dir'])) {
130
+ $xwpContentDirRelToWEPluginDir = SanityCheck::noControlChars($_GET['xwp-content-rel-to-we-plugin-dir']);
131
+ $wpContentDirRelToWEPluginDir = SanityCheck::pathDirectoryTraversalAllowed(substr($xwpContentDirRelToWEPluginDir, 1));
132
+ }
133
+ }
134
 
135
+ // Old .htaccess rules from before 0.22.0 passed relative path to general plugin dir.
136
+ // these rules must still be supported, which is what we do here:
137
+ if (!$wpContentDirRelToWEPluginDir) {
138
+ self::$checking = 'relative path between plugin dir and wp-content dir';
139
+
140
+ $wpContentDirRelToPluginDir = self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_PLUGIN_DIR');
141
+ if ($wpContentDirRelToPluginDir === false) {
142
+ // Passed in QS?
143
+ if (isset($_GET['xwp-content-rel-to-plugin-dir'])) {
144
+ $xwpContentDirRelToPluginDir = SanityCheck::noControlChars($_GET['xwp-content-rel-to-plugin-dir']);
145
+ $wpContentDirRelToPluginDir = SanityCheck::pathDirectoryTraversalAllowed(substr($xwpContentDirRelToPluginDir, 1));
146
+
147
+ } else {
148
+ throw new \Exception('Path to wp-content was not received in any way');
149
+ }
150
  }
151
+ $wpContentDirRelToWEPluginDir = $wpContentDirRelToPluginDir . '/webp-express';
152
  }
153
 
154
+
155
  // Check WebP Express content dir
156
  // ---------------------------------
157
  self::$checking = 'WebP Express content dir';
 
158
 
159
+ $pathToWEPluginDir = dirname(dirname(__DIR__));
160
+ $webExpressContentDirAbs = SanityCheck::pathDirectoryTraversalAllowed($pathToWEPluginDir . '/' . $wpContentDirRelToWEPluginDir . '/webp-express');
161
 
162
+ //$pathToPluginDir = dirname(dirname(dirname(__DIR__)));
163
+ //$webExpressContentDirAbs = SanityCheck::pathDirectoryTraversalAllowed($pathToPluginDir . '/' . $wpContentDirRelToPluginDir . '/webp-express');
164
  //echo $webExpressContentDirAbs; exit;
165
  if (@!file_exists($webExpressContentDirAbs)) {
166
  throw new \Exception('Dir not found');
183
  protected static function loadConfig() {
184
 
185
  $usingDocRoot = !(
186
+ isset($_GET['xwp-content-rel-to-we-plugin-dir']) ||
187
+ self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR') ||
188
  isset($_GET['xwp-content-rel-to-plugin-dir']) ||
189
  self::getEnvPassedInRewriteRule('WE_WP_CONTENT_REL_TO_PLUGIN_DIR')
190
  );
lib/migrate/migrate14.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebPExpress;
4
+
5
+ use \WebPExpress\Config;
6
+ use \WebPExpress\Messenger;
7
+ use \WebPExpress\Option;
8
+
9
+ function webpexpress_migrate14() {
10
+
11
+ // Update migrate version right away to minimize risk of running the update twice in a multithreaded environment
12
+ Option::updateOption('webp-express-migration-version', '14');
13
+
14
+ $config = Config::loadConfigAndFix(false); // false means we do not need the check if quality detection is supported
15
+ if (($config['enable-redirection-to-converter']) || ($config['redirect-to-existing-in-htaccess'])) {
16
+
17
+ // We need to regenerate .htaccess files in case redirection to webp is enabled. Two reasons:
18
+ // 1: WebP On Demand rules needs fixing (#520)
19
+ // 2: The new escape hatch (#522), which is needed for the File Manager (#521)
20
+ wp_schedule_single_event(time() + 10, 'webp_express_task_regenerate_config_and_htaccess');
21
+ } else {
22
+ /*
23
+ if (isset($config['alter-html']) && $config['alter-html']['enabled']) {
24
+ // Schedule to regenate config, because we need to update autoloaded options in order to
25
+ // autoload the new alter-html/prevent-using-webps-larger-than-original option
26
+ // (hm, actually it defaults to true, so it should be neccessary...)
27
+ wp_schedule_single_event(time() + 10, 'webp_express_task_regenerate_config');
28
+ }*/
29
+ }
30
+
31
+ // Schedule bulk update dummy files
32
+ wp_schedule_single_event(time() + 30, 'webp_express_task_bulk_update_dummy_files');
33
+
34
+ }
35
+
36
+ webpexpress_migrate14();
lib/options/enqueue_scripts.php CHANGED
@@ -5,7 +5,7 @@ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
5
  use \WebPExpress\Paths;
6
  use \WebPExpress\Config;
7
 
8
- $ver = '7'; // note: Minimum 1
9
  $jsDir = 'js/0.19.0'; // We change dir when it is critical that no-one gets the cached version (there is a plugin that strips version strings out there...)
10
 
11
  if (!function_exists('webp_express_add_inline_script')) {
@@ -102,6 +102,10 @@ if (!(isset($config['operation-mode']) && ($config['operation-mode'] == 'no-conv
102
  wp_register_script('purgecache', plugins_url($jsDir . '/purge-cache.js', __FILE__), [], $ver);
103
  wp_enqueue_script('purgecache');
104
 
 
 
 
 
105
  }
106
 
107
  //wp_register_script('api_keys', plugins_url($jsDir . 'api-keys.js', __FILE__), ['daspopup'], '0.7.0-dev8');
@@ -115,6 +119,7 @@ $javascriptVars = [
115
  'convert' => wp_create_nonce('webpexpress-ajax-convert-nonce'),
116
  'list-unconverted-files' => wp_create_nonce('webpexpress-ajax-list-unconverted-files-nonce'),
117
  'purge-cache' => wp_create_nonce('webpexpress-ajax-purge-cache-nonce'),
 
118
  'view-log' => wp_create_nonce('webpexpress-ajax-view-log-nonce'),
119
  'self-test' => wp_create_nonce('webpexpress-ajax-self-test-nonce'),
120
  ],
5
  use \WebPExpress\Paths;
6
  use \WebPExpress\Config;
7
 
8
+ $ver = '8'; // note: Minimum 1
9
  $jsDir = 'js/0.19.0'; // We change dir when it is critical that no-one gets the cached version (there is a plugin that strips version strings out there...)
10
 
11
  if (!function_exists('webp_express_add_inline_script')) {
102
  wp_register_script('purgecache', plugins_url($jsDir . '/purge-cache.js', __FILE__), [], $ver);
103
  wp_enqueue_script('purgecache');
104
 
105
+ // purge log
106
+ wp_register_script('purgelog', plugins_url($jsDir . '/purge-log.js', __FILE__), [], $ver);
107
+ wp_enqueue_script('purgelog');
108
+
109
  }
110
 
111
  //wp_register_script('api_keys', plugins_url($jsDir . 'api-keys.js', __FILE__), ['daspopup'], '0.7.0-dev8');
119
  'convert' => wp_create_nonce('webpexpress-ajax-convert-nonce'),
120
  'list-unconverted-files' => wp_create_nonce('webpexpress-ajax-list-unconverted-files-nonce'),
121
  'purge-cache' => wp_create_nonce('webpexpress-ajax-purge-cache-nonce'),
122
+ 'purge-log' => wp_create_nonce('webpexpress-ajax-purge-log-nonce'),
123
  'view-log' => wp_create_nonce('webpexpress-ajax-view-log-nonce'),
124
  'self-test' => wp_create_nonce('webpexpress-ajax-self-test-nonce'),
125
  ],
lib/options/js/0.19.0/bulk-convert.js CHANGED
@@ -279,7 +279,7 @@ function convertNextInBulkQueue() {
279
  var group = bulkInfo.groups[bulkInfo.groupPointer];
280
  var filename = group.files[bulkInfo.filePointer];
281
 
282
- var result = JSON.parse(response);
283
 
284
  //console.log(result);
285
 
279
  var group = bulkInfo.groups[bulkInfo.groupPointer];
280
  var filename = group.files[bulkInfo.filePointer];
281
 
282
+ var result = JSON.parse(response); // TODO: An parse error has been experienced (perhaps when token expired?)
283
 
284
  //console.log(result);
285
 
lib/options/js/0.19.0/purge-log.js ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ function openDeleteLogFilesPopup() {
3
+ var html = '';
4
+ html += '<p><b>Are you sure you want to do that?</b></p>';
5
+ html += '<p>' +
6
+ 'The log files contain information about the conversion settings used for each webp and the libraries ' +
7
+ 'used, and the versions. This information will be visible in the conversion manager in a not too far future. ' +
8
+ 'The information might also be used to notify you if the libraries / version of a library you have is ' +
9
+ 'significantly better than the one used for the conversion. ' +
10
+ 'If you delete the log files, you will not benefit from this functionality. ' +
11
+ '</p>';
12
+
13
+ html += '<p>' +
14
+ 'The log files are btw located in <i>wp-content/webp-express/log/</i>, if you want to have a closer look.' +
15
+ '</p>';
16
+ //html += '<p>In a not too far future, the log files will be used in the conversion manager.</p>'
17
+ //html += 'They could become handy.</p>'
18
+ /*
19
+ html += '<p>This action cannot be reversed. Your log files will be gone. '
20
+ html += 'Dead. Completely. Forever. '
21
+ html += '(Unless of course you have a backup. Or, of course, there are ways of recovery... Anyway...). '
22
+ html += 'Ok, sorry for the babbeling. The dialog seemed bare without text.</p>';*/
23
+ html += '<button onclick="purgeLog()" class="button button-secondary" type="button">Yes, delete!</button>';
24
+
25
+ document.getElementById('purgelogcontent').innerHTML = '<div>' + html + '</div>';
26
+ tb_show('Delete all log Files?', '#TB_inline?inlineId=purgelogpopup&height=320&width=450');
27
+
28
+ }
29
+
30
+ function closePurgeLogDialog() {
31
+ tb_remove();
32
+ }
33
+
34
+ function purgeLog() {
35
+ var data = {
36
+ 'action': 'webpexpress_purge_log',
37
+ 'nonce' : window.webpExpress['ajax-nonces']['purge-log'],
38
+ };
39
+ jQuery.post(ajaxurl, data, function(response) {
40
+ if ((typeof response == 'object') && (response['success'] == false)) {
41
+ if (response['data'] && ((typeof response['data']) == 'string')) {
42
+ alert(response['data']);
43
+ } else {
44
+ alert('Something failed');
45
+ }
46
+ return;
47
+ }
48
+
49
+ var result = JSON.parse(response);
50
+ //console.log(result);
51
+
52
+ var html = '<div><p>';
53
+
54
+ if (result['fail-count'] == 0) {
55
+ if (result['delete-count'] == 0) {
56
+ html += 'No log files were found, so none was deleted.';
57
+ } else {
58
+ html += 'Successfully deleted ' + result['delete-count'] + ' log files';
59
+ }
60
+ } else {
61
+ if (result['delete-count'] == 0) {
62
+ html += 'Failed deleting ' + result['fail-count'] + ' log files. None was deleted, in fact.';
63
+ } else {
64
+ html += 'Deleted ' + result['delete-count'] + ' log files. However, failed deleting ' + result['fail-count'] + ' log files.';
65
+ }
66
+ }
67
+ html += '</p>';
68
+ html += '<button onclick="closePurgeLogDialog()" class="button button-secondary" type="button">Ok</button>';
69
+ html += '</div>';
70
+
71
+ document.getElementById('purgelogcontent').innerHTML = html;
72
+
73
+ });
74
+ }
lib/options/options/conversion-options/conversion-options.inc CHANGED
@@ -10,6 +10,7 @@
10
  include_once 'metadata.inc';
11
  include_once 'converters.inc';
12
  include_once 'convert-on-upload.inc';
 
13
  include_once 'bulk-convert.inc';
14
  ?>
15
  </tbody>
10
  include_once 'metadata.inc';
11
  include_once 'converters.inc';
12
  include_once 'convert-on-upload.inc';
13
+ include_once 'logging.inc';
14
  include_once 'bulk-convert.inc';
15
  ?>
16
  </tbody>
lib/options/options/conversion-options/logging.inc ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <tr>
2
+ <th scope="row">
3
+ Enable logging
4
+ <?php
5
+ echo helpIcon(
6
+ '<p>Store conversion results in log files. ' .
7
+ 'This can be useful in order to find out what went wrong in case a conversion failed or ' .
8
+ 'the result is poor quality. ' .
9
+ 'The log files reside in <i>wp-content/webp-express/log/conversions/</i>. ' .
10
+ 'In not too far a future, the conversion logs will be accessible in the File Manager too.' .
11
+ '</p>'
12
+ );
13
+ ?>
14
+ </th>
15
+ <td>
16
+ <input type="checkbox" id="enable_logging" name="enable-logging" value="true" <?php echo ($config['enable-logging'] ? 'checked="checked"' : '') ?> >
17
+ &nbsp;
18
+ <button onclick="openDeleteLogFilesPopup()" class="button button-secondary" type="button">Delete log files</button>
19
+ <div id="purgelogpopup" style="display:none;">
20
+ <div id="purgelogcontent"></div>
21
+ </div>
22
+
23
+ </td>
24
+ </tr>
lib/options/options/general/destination-extension.inc CHANGED
@@ -40,8 +40,8 @@
40
  <?php
41
  if ($config['operation-mode'] == 'no-conversion') {
42
  webpexpress_radioButtons('destination-extension', $config['destination-extension'], [
43
- 'append' => 'Original extension is kept (ie "image.jpg.webp")',
44
- 'set' => 'Original extension is replaced (ie "image.webp")',
45
  ], [
46
  'append' => 'Original extension is kept and ".webp" is appended. ',
47
  'set' => 'Original extension is replaced with ".webp".'
40
  <?php
41
  if ($config['operation-mode'] == 'no-conversion') {
42
  webpexpress_radioButtons('destination-extension', $config['destination-extension'], [
43
+ 'append' => 'Appended ".webp" (ie "image.jpg.webp")',
44
+ 'set' => 'Replaced extension (ie "image.webp")',
45
  ], [
46
  'append' => 'Original extension is kept and ".webp" is appended. ',
47
  'set' => 'Original extension is replaced with ".webp".'
lib/options/options/general/general.inc CHANGED
@@ -28,6 +28,8 @@
28
  include_once 'cache-control.inc';
29
  }
30
 
 
 
31
  ?>
32
  </tbody>
33
  </table>
28
  include_once 'cache-control.inc';
29
  }
30
 
31
+ include_once 'prevent-using-webps-larger-than-original.inc';
32
+
33
  ?>
34
  </tbody>
35
  </table>
lib/options/options/general/prevent-using-webps-larger-than-original.inc ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <tr>
2
+ <th scope="row">Prevent using webps larger than original<?php
3
+ echo helpIcon(
4
+ '<p>For some images, the converted webp might turns out bigger than the original. ' .
5
+ 'These are always kept on disk. However, with this option, you can choose whether they should be used or not. ' .
6
+ 'If you are motivated by limiting bandwidth usage and having a fast website, keep this option enabled. ' .
7
+ 'If you are more concerned about SEO and a penalty for serving jpegs and pngs rather than webps, disable it.' .
8
+ '</p>' .
9
+ '<p>The option is used both when generating .htaccess rules and in Alter HTML.</p>'
10
+ );
11
+ ?></th>
12
+ <td>
13
+ <input type="checkbox" id="prevent_using_webps_larger_than_original" name="prevent-using-webps-larger-than-original" value="true" <?php echo ($config['prevent-using-webps-larger-than-original'] ? 'checked="checked"' : '') ?> >
14
+ </td>
15
+ </tr>
lib/options/submit.php CHANGED
@@ -381,6 +381,7 @@ $sanitized = [
381
  'private',
382
  ]),
383
  'cache-control-custom' => webpexpress_getSanitizedCacheControlHeader('cache-control-custom'),
 
384
 
385
 
386
  // Redirection rules
@@ -427,6 +428,7 @@ $sanitized = [
427
  ]),
428
  'alpha-quality' => webpexpress_getSanitizedQuality('alpha-quality', 80),
429
  'convert-on-upload' => isset($_POST['convert-on-upload']),
 
430
  'converters' => webpexpress_getSanitizedConverters(),
431
 
432
 
@@ -505,6 +507,8 @@ switch ($sanitized['cache-control']) {
505
  $config['cache-control-custom'] = $sanitized['cache-control-custom'];
506
  break;
507
  }
 
 
508
 
509
  // Alter HTML
510
  $config['alter-html'] = [];
@@ -565,6 +569,7 @@ if ($sanitized['operation-mode'] != 'no-conversion') {
565
 
566
  // Other conversion options
567
  $config['convert-on-upload'] = $sanitized['convert-on-upload'];
 
568
 
569
 
570
  // Web Service
@@ -679,6 +684,12 @@ if ($sanitized['operation-mode'] != $sanitized['change-operation-mode']) {
679
  // the redirect-to-existing-in-htaccess option
680
  $config['redirect-to-existing-in-htaccess'] = true;
681
  }
 
 
 
 
 
 
682
  }
683
 
684
  // If we are going to save .htaccess, run and store capability tests first
381
  'private',
382
  ]),
383
  'cache-control-custom' => webpexpress_getSanitizedCacheControlHeader('cache-control-custom'),
384
+ 'prevent-using-webps-larger-than-original' => isset($_POST['prevent-using-webps-larger-than-original']),
385
 
386
 
387
  // Redirection rules
428
  ]),
429
  'alpha-quality' => webpexpress_getSanitizedQuality('alpha-quality', 80),
430
  'convert-on-upload' => isset($_POST['convert-on-upload']),
431
+ 'enable-logging' => isset($_POST['enable-logging']),
432
  'converters' => webpexpress_getSanitizedConverters(),
433
 
434
 
507
  $config['cache-control-custom'] = $sanitized['cache-control-custom'];
508
  break;
509
  }
510
+ $config['prevent-using-webps-larger-than-original'] = $sanitized['prevent-using-webps-larger-than-original'];
511
+
512
 
513
  // Alter HTML
514
  $config['alter-html'] = [];
569
 
570
  // Other conversion options
571
  $config['convert-on-upload'] = $sanitized['convert-on-upload'];
572
+ $config['enable-logging'] = $sanitized['enable-logging'];
573
 
574
 
575
  // Web Service
684
  // the redirect-to-existing-in-htaccess option
685
  $config['redirect-to-existing-in-htaccess'] = true;
686
  }
687
+
688
+ if ($config['operation-mode'] == 'no-conversion') {
689
+ // No conversion probably means that there are webps in the system not generated by
690
+ // webp express. Schedule a task to mark those that are bigger than originals
691
+ wp_schedule_single_event(time() + 30, 'webp_express_task_bulk_update_dummy_files');
692
+ }
693
  }
694
 
695
  // If we are going to save .htaccess, run and store capability tests first
vendor/composer/installed.json CHANGED
@@ -156,17 +156,17 @@
156
  },
157
  {
158
  "name": "rosell-dk/dom-util-for-webp",
159
- "version": "0.4.1",
160
- "version_normalized": "0.4.1.0",
161
  "source": {
162
  "type": "git",
163
  "url": "https://github.com/rosell-dk/dom-util-for-webp.git",
164
- "reference": "f06a38fe0950e2ca0e5137a6ecd44e93d9b6b2d9"
165
  },
166
  "dist": {
167
  "type": "zip",
168
- "url": "https://api.github.com/repos/rosell-dk/dom-util-for-webp/zipball/f06a38fe0950e2ca0e5137a6ecd44e93d9b6b2d9",
169
- "reference": "f06a38fe0950e2ca0e5137a6ecd44e93d9b6b2d9",
170
  "shasum": ""
171
  },
172
  "require-dev": {
@@ -174,7 +174,7 @@
174
  "phpunit/phpunit": "^9.3",
175
  "squizlabs/php_codesniffer": "3.*"
176
  },
177
- "time": "2021-06-23T17:58:50+00:00",
178
  "type": "library",
179
  "extra": {
180
  "scripts-descriptions": {
@@ -212,7 +212,7 @@
212
  ],
213
  "support": {
214
  "issues": "https://github.com/rosell-dk/dom-util-for-webp/issues",
215
- "source": "https://github.com/rosell-dk/dom-util-for-webp/tree/0.4.1"
216
  },
217
  "funding": [
218
  {
156
  },
157
  {
158
  "name": "rosell-dk/dom-util-for-webp",
159
+ "version": "0.4.2",
160
+ "version_normalized": "0.4.2.0",
161
  "source": {
162
  "type": "git",
163
  "url": "https://github.com/rosell-dk/dom-util-for-webp.git",
164
+ "reference": "f33515fbb90067cb982dc3109d7193f27ab97619"
165
  },
166
  "dist": {
167
  "type": "zip",
168
+ "url": "https://api.github.com/repos/rosell-dk/dom-util-for-webp/zipball/f33515fbb90067cb982dc3109d7193f27ab97619",
169
+ "reference": "f33515fbb90067cb982dc3109d7193f27ab97619",
170
  "shasum": ""
171
  },
172
  "require-dev": {
174
  "phpunit/phpunit": "^9.3",
175
  "squizlabs/php_codesniffer": "3.*"
176
  },
177
+ "time": "2021-11-04T11:13:34+00:00",
178
  "type": "library",
179
  "extra": {
180
  "scripts-descriptions": {
212
  ],
213
  "support": {
214
  "issues": "https://github.com/rosell-dk/dom-util-for-webp/issues",
215
+ "source": "https://github.com/rosell-dk/dom-util-for-webp/tree/0.4.2"
216
  },
217
  "funding": [
218
  {
vendor/composer/installed.php CHANGED
@@ -5,7 +5,7 @@
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
- 'reference' => 'b010c2c57ab39a7ca057ff68360803b6fb940428',
9
  'name' => 'rosell-dk/webp-express',
10
  'dev' => true,
11
  ),
@@ -20,12 +20,12 @@
20
  'dev_requirement' => false,
21
  ),
22
  'rosell-dk/dom-util-for-webp' => array(
23
- 'pretty_version' => '0.4.1',
24
- 'version' => '0.4.1.0',
25
  'type' => 'library',
26
  'install_path' => __DIR__ . '/../rosell-dk/dom-util-for-webp',
27
  'aliases' => array(),
28
- 'reference' => 'f06a38fe0950e2ca0e5137a6ecd44e93d9b6b2d9',
29
  'dev_requirement' => false,
30
  ),
31
  'rosell-dk/htaccess-capability-tester' => array(
@@ -70,7 +70,7 @@
70
  'type' => 'wordpress-plugin',
71
  'install_path' => __DIR__ . '/../../',
72
  'aliases' => array(),
73
- 'reference' => 'b010c2c57ab39a7ca057ff68360803b6fb940428',
74
  'dev_requirement' => false,
75
  ),
76
  'roundcube/plugin-installer' => array(
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
+ 'reference' => 'f5035d6408254d4070e9edfb7b2b6348753d4e83',
9
  'name' => 'rosell-dk/webp-express',
10
  'dev' => true,
11
  ),
20
  'dev_requirement' => false,
21
  ),
22
  'rosell-dk/dom-util-for-webp' => array(
23
+ 'pretty_version' => '0.4.2',
24
+ 'version' => '0.4.2.0',
25
  'type' => 'library',
26
  'install_path' => __DIR__ . '/../rosell-dk/dom-util-for-webp',
27
  'aliases' => array(),
28
+ 'reference' => 'f33515fbb90067cb982dc3109d7193f27ab97619',
29
  'dev_requirement' => false,
30
  ),
31
  'rosell-dk/htaccess-capability-tester' => array(
70
  'type' => 'wordpress-plugin',
71
  'install_path' => __DIR__ . '/../../',
72
  'aliases' => array(),
73
+ 'reference' => 'f5035d6408254d4070e9edfb7b2b6348753d4e83',
74
  'dev_requirement' => false,
75
  ),
76
  'roundcube/plugin-installer' => array(
vendor/rosell-dk/dom-util-for-webp/README.md CHANGED
@@ -2,7 +2,7 @@
2
 
3
  [![Latest Stable Version](https://img.shields.io/packagist/v/rosell-dk/dom-util-for-webp.svg?style=flat-square)](https://packagist.org/packages/rosell-dk/dom-util-for-webp)
4
  [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%205.6-8892BF.svg?style=flat-square)](https://php.net)
5
- [![Build Status](https://img.shields.io/travis/com/rosell-dk/dom-util-for-webp/master.svg?style=flat-square)](https://travis-ci.com/github/rosell-dk/dom-util-for-webp)
6
  [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/rosell-dk/dom-util-for-webp.svg?style=flat-square)](https://scrutinizer-ci.com/g/rosell-dk/dom-util-for-webp/code-structure/master)
7
  [![Quality Score](https://img.shields.io/scrutinizer/g/rosell-dk/dom-util-for-webp.svg?style=flat-square)](https://scrutinizer-ci.com/g/rosell-dk/dom-util-for-webp/)
8
  [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/rosell-dk/dom-util-for-webp/blob/master/LICENSE)
2
 
3
  [![Latest Stable Version](https://img.shields.io/packagist/v/rosell-dk/dom-util-for-webp.svg?style=flat-square)](https://packagist.org/packages/rosell-dk/dom-util-for-webp)
4
  [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%205.6-8892BF.svg?style=flat-square)](https://php.net)
5
+ [![Build Status](https://img.shields.io/github/workflow/status/rosell-dk/dom-util-for-webp/PHP?style=flat-square)](https://github.com/rosell-dk/dom-util-for-webp/actions/workflows/php.yml)
6
  [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/rosell-dk/dom-util-for-webp.svg?style=flat-square)](https://scrutinizer-ci.com/g/rosell-dk/dom-util-for-webp/code-structure/master)
7
  [![Quality Score](https://img.shields.io/scrutinizer/g/rosell-dk/dom-util-for-webp.svg?style=flat-square)](https://scrutinizer-ci.com/g/rosell-dk/dom-util-for-webp/)
8
  [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/rosell-dk/dom-util-for-webp/blob/master/LICENSE)
vendor/rosell-dk/dom-util-for-webp/src/PictureTags.php CHANGED
@@ -84,6 +84,33 @@ class PictureTags
84
  );
85
  }
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  private static function getAttributes($html)
88
  {
89
  if (function_exists("mb_convert_encoding")) {
@@ -100,6 +127,10 @@ class PictureTags
100
  return $attributes;
101
  } else {
102
  //$dom = HtmlDomParser::str_get_html($html, false, false, 'UTF-8', false);
 
 
 
 
103
  $dom = str_get_html($html, false, false, 'UTF-8', false);
104
  if ($dom !== false) {
105
  $elems = $dom->find('img,IMG');
@@ -151,14 +182,24 @@ class PictureTags
151
  $srcsetInfo = self::lazyGet($imgAttributes, 'srcset');
152
  $sizesInfo = self::lazyGet($imgAttributes, 'sizes');
153
 
 
 
 
 
 
 
 
 
154
  // add the exclude class so if this content is processed again in other filter,
155
  // the img is not converted again in picture
156
  $imgAttributes['class'] = (isset($imgAttributes['class']) ? $imgAttributes['class'] . " " : "") .
157
  "webpexpress-processed";
158
 
159
- $srcsetWebP = '';
160
- if ($srcsetInfo['value']) {
161
- $srcsetArr = explode(', ', $srcsetInfo["value"]);
 
 
162
  $srcsetArrWebP = [];
163
  foreach ($srcsetArr as $i => $srcSetEntry) {
164
  // $srcSetEntry is ie "http://example.com/image.jpg 520w"
@@ -167,26 +208,58 @@ class PictureTags
167
  $width = null;
168
  if ($result && count($result) >= 2) {
169
  list($src, $width) = $result;
 
170
  }
171
 
172
  $webpUrl = $this->replaceUrlOr($src, false);
173
  if ($webpUrl !== false) {
174
- $srcsetArrWebP[] = $webpUrl . (isset($width) ? ' ' . $width : '');
 
 
 
175
  }
176
  }
177
- $srcsetWebP = implode(', ', $srcsetArrWebP);
178
- if (strlen($srcsetWebP) == 0) {
179
- // We have no webps for you, so no reason to create <picture> tag
 
 
 
 
180
  return $imgTag;
181
  }
182
- $sizesAttr = ($sizesInfo['value'] ? (' ' . $sizesInfo['attrName'] . '="' . $sizesInfo['value'] . '"') : '');
183
- $sourceSrcAttrName = $srcsetInfo['attrName'];
184
- if ($sourceSrcAttrName == 'src') {
185
- // "src" isn't allowed in <source> tag with <picture> tag as parent.
186
- $sourceSrcAttrName = 'srcset';
 
 
187
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  return '<picture>'
189
- . '<source ' . $sourceSrcAttrName . '="' . $srcsetWebP . '"' . $sizesAttr . ' type="image/webp">'
190
  . '<img' . self::createAttributes($imgAttributes) . '>'
191
  . '</picture>';
192
  } else {
@@ -206,7 +279,7 @@ class PictureTags
206
  . '<source ' . $sourceSrcAttrName . '="' . $srcWebP . '" type="image/webp">'
207
  . '<img' . self::createAttributes($imgAttributes) . '>'
208
  . '</picture>';
209
- }
210
  }
211
 
212
  /*
@@ -251,9 +324,6 @@ class PictureTags
251
  /* Main replacer function */
252
  public static function replace($html)
253
  {
254
- if (!function_exists('str_get_html')) {
255
- require_once __DIR__ . '/../src-vendor/simple_html_dom/simple_html_dom.inc';
256
- }
257
  $pt = new static();
258
  return $pt->replaceHtml($html);
259
  }
84
  );
85
  }
86
 
87
+ /**
88
+ * Look for attribute such as "src", but also with prefixes such as "data-lazy-src" and "data-src"
89
+ *
90
+ * @param array $attributes an array of all attributes for the element
91
+ * @param string $attrName ie "src", "srcset" or "sizes"
92
+ *
93
+ * @return array an array with "value" key and "attrName" key. ("value" is the value of the attribute and
94
+ * "attrName" is the name of the attribute used)
95
+ *
96
+ */
97
+ private static function findAttributesWithNameOrPrefixed($attributes, $attrName)
98
+ {
99
+ $tryThesePrefixes = ['', 'data-lazy-', 'data-'];
100
+ $result = [];
101
+ foreach ($tryThesePrefixes as $prefix) {
102
+ $name = $prefix . $attrName;
103
+ if (isset($attributes[$name]) && strlen($attributes[$name])) {
104
+ /*$result[] = [
105
+ 'value' => trim($attributes[$name]),
106
+ 'attrName' => $name,
107
+ ];*/
108
+ $result[$name] = trim($attributes[$name]);
109
+ }
110
+ }
111
+ return $result;
112
+ }
113
+
114
  private static function getAttributes($html)
115
  {
116
  if (function_exists("mb_convert_encoding")) {
127
  return $attributes;
128
  } else {
129
  //$dom = HtmlDomParser::str_get_html($html, false, false, 'UTF-8', false);
130
+ if (!function_exists('str_get_html')) {
131
+ require_once __DIR__ . '/../src-vendor/simple_html_dom/simple_html_dom.inc';
132
+ }
133
+
134
  $dom = str_get_html($html, false, false, 'UTF-8', false);
135
  if ($dom !== false) {
136
  $elems = $dom->find('img,IMG');
182
  $srcsetInfo = self::lazyGet($imgAttributes, 'srcset');
183
  $sizesInfo = self::lazyGet($imgAttributes, 'sizes');
184
 
185
+ $srcSetAttributes = self::findAttributesWithNameOrPrefixed($imgAttributes, 'srcset');
186
+ $srcAttributes = self::findAttributesWithNameOrPrefixed($imgAttributes, 'src');
187
+
188
+ if ((!isset($srcSetAttributes['srcset'])) && (!isset($srcAttributes['src']))) {
189
+ // better not mess with this html...
190
+ return $imgTag;
191
+ }
192
+
193
  // add the exclude class so if this content is processed again in other filter,
194
  // the img is not converted again in picture
195
  $imgAttributes['class'] = (isset($imgAttributes['class']) ? $imgAttributes['class'] . " " : "") .
196
  "webpexpress-processed";
197
 
198
+ // Process srcset (also data-srcset etc)
199
+ $atLeastOneWebp = false;
200
+ $sourceTagAttributes = [];
201
+ foreach ($srcSetAttributes as $attrName => $attrValue) {
202
+ $srcsetArr = explode(', ', $attrValue);
203
  $srcsetArrWebP = [];
204
  foreach ($srcsetArr as $i => $srcSetEntry) {
205
  // $srcSetEntry is ie "http://example.com/image.jpg 520w"
208
  $width = null;
209
  if ($result && count($result) >= 2) {
210
  list($src, $width) = $result;
211
+
212
  }
213
 
214
  $webpUrl = $this->replaceUrlOr($src, false);
215
  if ($webpUrl !== false) {
216
+ if (substr($src, 0, 5) != 'data:') {
217
+ $atLeastOneWebp = true;
218
+ $srcsetArrWebP[] = $webpUrl . (isset($width) ? ' ' . $width : '');
219
+ }
220
  }
221
  }
222
+ $sourceTagAttributes[$attrName] = implode(', ', $srcsetArrWebP);
223
+ }
224
+
225
+ foreach ($srcAttributes as $attrName => $attrValue) {
226
+
227
+ if (substr($attrValue, 0, 5) == 'data:') {
228
+ // ignore tags with data urls, such as <img src="data:...
229
  return $imgTag;
230
  }
231
+ // Make sure not to override existing srcset with src
232
+ if (!isset($sourceTagAttributes[$attrName . 'set'])) {
233
+ $srcWebP = $this->replaceUrlOr($attrValue, false);
234
+ if ($srcWebP !== false) {
235
+ $atLeastOneWebp = true;
236
+ }
237
+ $sourceTagAttributes[$attrName . 'set'] = $srcWebP;
238
  }
239
+ }
240
+
241
+ if ($sizesInfo['value']) {
242
+ $sourceTagAttributes[$sizesInfo['attrName']] = $sizesInfo['value'];
243
+ }
244
+
245
+ if (!$atLeastOneWebp) {
246
+ // We have no webps for you, so no reason to create <picture> tag
247
+ return $imgTag;
248
+ }
249
+
250
+ return '<picture>'
251
+ . '<source' . self::createAttributes($sourceTagAttributes) . ' type="image/webp">'
252
+ . '<img' . self::createAttributes($imgAttributes) . '>'
253
+ . '</picture>';
254
+
255
+ /*
256
+ //if ($srcsetInfo['value']) {
257
+ if (isset($srcSetAttributes['srcset'])) {
258
+ $sourceTagAttributes = $srcSetAttributes;
259
+
260
+
261
  return '<picture>'
262
+ . '<source' . self::createAttributes($sourceTagAttributes) . ' type="image/webp">'
263
  . '<img' . self::createAttributes($imgAttributes) . '>'
264
  . '</picture>';
265
  } else {
279
  . '<source ' . $sourceSrcAttrName . '="' . $srcWebP . '" type="image/webp">'
280
  . '<img' . self::createAttributes($imgAttributes) . '>'
281
  . '</picture>';
282
+ }*/
283
  }
284
 
285
  /*
324
  /* Main replacer function */
325
  public static function replace($html)
326
  {
 
 
 
327
  $pt = new static();
328
  return $pt->replaceHtml($html);
329
  }
webp-express.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: WebP Express
4
  * Plugin URI: https://github.com/rosell-dk/webp-express
5
  * Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
6
- * Version: 0.21.1
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
@@ -61,3 +61,8 @@ add_filter('image_make_intermediate_size', array('\WebPExpress\HandleUploadHooks
61
  add_filter('wp_delete_file', array('\WebPExpress\HandleDeleteFileHook', 'deleteAssociatedWebP'), 10, 2);
62
 
63
  //add_action( 'template_redirect', 'webp_express_template_redirect' );
 
 
 
 
 
3
  * Plugin Name: WebP Express
4
  * Plugin URI: https://github.com/rosell-dk/webp-express
5
  * Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
6
+ * Version: 0.22.0
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
61
  add_filter('wp_delete_file', array('\WebPExpress\HandleDeleteFileHook', 'deleteAssociatedWebP'), 10, 2);
62
 
63
  //add_action( 'template_redirect', 'webp_express_template_redirect' );
64
+
65
+ // Add hooks for tasks that might be scheduled for wp_cron
66
+ add_action('webp_express_task_bulk_update_dummy_files', array('\WebPExpress\BiggerThanSourceDummyFilesBulk', 'updateStatus'), 10, 0);
67
+ add_action('webp_express_task_regenerate_config', array('\WebPExpress\Config', 'regenerateConfig'), 10, 0);
68
+ add_action('webp_express_task_regenerate_config_and_htaccess', array('\WebPExpress\Config', 'regenerateConfigAndHtaccessFiles'), 10, 0);