Version Description
This version works on many more setups than the previous. Also uses less resources and handles when images are changed.
- Configuration is now stored in a separate configuration file instead of storing directly in the .htaccess file and passing it on via query string. When updating, these settings are migrated automatically.
- Handles setups where Wordpress has been given its own directory (both methods mentioned here)
- Handles setups where wp-content has been moved, even out of Wordpress root.
- Handles setups where Uploads folder has been moved, even out of wp-content.
- Handles setups where Plugins folder has been moved, even out of wp-content or out of Wordpress root
- Is not as likely to be subject to firewalls blocking requests (in 0.4.0, we passed all options in a querystring, and that could trigger firewalls under some circumstances)
- Is not as likely to be subject to rewrite rules from other plugins interfering. WebP Express now stores the .htaccess in the wp-content folder (if you allow it). As this is deeper than the root folder, the rules in here takes precedence over rules in the main .htaccess
- The .htaccess now passes the complete absulute path to the source file instead of a relative path. This is a less error-prone method.
- Reconverts the webp, if source image has changed
- Now runs on version 1.0.0 of WebP On Demand. Previously ran on 0.3.0
- Now takes care of only loading the PHP classes when needed in order not to slow down your Wordpress. The frontend will only need to process four lines of code. The backend footprint is also quite small now (80 lines of code of hooks)
- Now works in Wordpress 4.0 - 4.6.
- Added cache-breaking tokens to image test links
- Denies deactivation if rewrite rules could not be removed
- Refactored thoroughly
- More helpful texts.
- Extensive testing. Tested on Wordpress 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8 and 4.9. Tested with PHP 5.6, PHP 7.0 and PHP 7.1. Tested on Apache and LiteSpeed. Tested when missing various write permissions. Tested migration. Tested when installed in root, in subfolder, when Wordpress has its own directory (both methods), when wp-content is moved out of Wordpress directory, when plugins is moved out of Wordpress directory, when both of them are moved and when uploads have been moved.
For more info, see the closed issues on the 0.5.0 milestone on our github repository: https://github.com/rosell-dk/webp-express/milestone/2?closed=1
=
Download this release
Release Info
Developer | rosell.dk |
Plugin | WebP Express |
Version | 0.5.0 |
Comparing to | |
See all releases |
Code changes from version 0.4.0 to 0.5.0
- README.md +29 -11
- README.txt +34 -28
- changelog.txt +11 -0
- lib/activate-first-time.php +65 -0
- lib/activate-hook.php +11 -0
- lib/activate.php +0 -88
- lib/admin.php +49 -0
- lib/classes/Actions.php +46 -0
- lib/classes/Config.php +165 -0
- lib/classes/FileHelper.php +163 -0
- lib/classes/HTAccess.php +458 -0
- lib/classes/Messenger.php +87 -0
- lib/classes/PathHelper.php +119 -0
- lib/classes/Paths.php +311 -0
- lib/classes/PlatformInfo.php +65 -0
- lib/classes/State.php +42 -0
- lib/deactivate.php +32 -6
- lib/debug.php +12 -0
- lib/helpers.php +0 -311
- lib/message.php +0 -131
- lib/migrate/migrate.php +47 -0
- lib/migrate/migrate1.php +205 -0
- lib/options.php +0 -498
- {css → lib/options/css}/webp-express-options-page.css +12 -2
- lib/options/enqueue_scripts.php +32 -0
- {images → lib/options/images}/drag-reorder.svg +0 -0
- {js → lib/options/js}/sortable.min.js +0 -0
- {js → lib/options/js}/webp-express-options-page.js +5 -46
- lib/options/options-hooks.php +29 -0
- lib/options/page-messages.php +87 -0
- lib/options/page-welcome.php +126 -0
- lib/options/page.php +306 -0
- lib/options/submit.php +177 -0
- lib/reactivate.php +68 -0
- lib/uninstall.php +39 -0
- test-run.php +0 -260
- test/test-run.php +137 -0
- vendor/require-webp-convert-and-serve.php +0 -4
- vendor/require-webp-convert.php +0 -20
- vendor/require-webp-on-demand.php +0 -3
- vendor/webp-convert-and-serve/BufferLogger.php +0 -57
- vendor/webp-convert-and-serve/WebPConvertAndServe.php +0 -254
- vendor/webp-convert/Converters/ConverterHelper.php +0 -260
- vendor/webp-convert/Converters/Cwebp.php +0 -259
- vendor/webp-convert/Converters/Ewww.php +0 -197
- vendor/webp-convert/Converters/Exceptions/ConversionDeclinedException.php +0 -10
- vendor/webp-convert/Converters/Exceptions/ConverterFailedException.php +0 -10
- vendor/webp-convert/Converters/Exceptions/ConverterNotOperationalException.php +0 -10
- vendor/webp-convert/Converters/Gd.php +0 -83
- vendor/webp-convert/Converters/Imagick.php +0 -76
- vendor/webp-convert/Converters/Wpc.php +0 -171
- vendor/webp-convert/Exceptions/ConverterNotFoundException.php +0 -10
- vendor/webp-convert/Exceptions/CreateDestinationFileException.php +0 -10
- vendor/webp-convert/Exceptions/CreateDestinationFolderException.php +0 -10
- vendor/webp-convert/Exceptions/InvalidFileExtensionException.php +0 -10
- vendor/webp-convert/Exceptions/TargetNotFoundException.php +0 -10
- vendor/webp-convert/Exceptions/WebPConvertBaseException.php +0 -7
- vendor/webp-convert/Loggers/BaseLogger.php +0 -26
- vendor/webp-convert/Loggers/EchoLogger.php +0 -22
- vendor/webp-convert/Loggers/VoidLogger.php +0 -14
- vendor/webp-convert/WebPConvert.php +0 -108
- vendor/webp-convert/require-all.inc +0 -20
- vendor/webp-on-demand/WebPOnDemand.php +0 -345
- webp-express.php +3 -101
- webp-on-demand.php +0 -17
- {vendor/webp-convert/Converters → wod}/Binaries/cwebp-fbsd +0 -0
- {vendor/webp-convert/Converters → wod}/Binaries/cwebp-linux +0 -0
- {vendor/webp-convert/Converters → wod}/Binaries/cwebp-mac12 +0 -0
- {vendor/webp-convert/Converters → wod}/Binaries/cwebp-sol +0 -0
- {vendor/webp-convert/Converters → wod}/Binaries/cwebp.exe +0 -0
- wod/webp-convert-and-serve.inc +1615 -0
- wod/webp-on-demand.inc +135 -0
- wod/webp-on-demand.php +50 -0
README.md
CHANGED
@@ -35,9 +35,9 @@ These different converting methods are called *converters*.
|
|
35 |
The best converter is *cwebp*. So the first thing you should do is test whether the cwebp converter is working. Simply click "test" next to the converter. If it doesn't work, you can disable that converter (or try to make it work, by changing the server setup).
|
36 |
The next converter to try is *wpc*, which is equally good as cwebp in terms of quality / filesize ratio, but which is slower. wpc is an open source cloud service, which you will have to install on some other server. If this is too much work, continue to the next converter. In the converter settings, you can read about the individual converters. You can also head to the WebPConvert readme for more information.
|
37 |
|
38 |
-
Once, you have a converter, that works, when you click the "test"-button, you are ready to test the whole stack, and the rewrite rules. To do this, first make sure to select something other than "Do not convert any images!" in *Image types to convert*. Next, click "Save settings". This will save settings, as well as update the
|
39 |
|
40 |
-
If you are working in a browser that supports webp (ie Google Chrome), you will see a link "Convert test image (show debug)"
|
41 |
|
42 |
Note that the plugin does not change any HTML. In the HTML the image src is still set to ie "example.jpg". To verify that the plugin is working (without clicking the test button), do the following:
|
43 |
|
@@ -47,17 +47,17 @@ Note that the plugin does not change any HTML. In the HTML the image src is stil
|
|
47 |
- Reload the page
|
48 |
- Find a jpeg or png image in the list. In the "type" column, it should say "webp"
|
49 |
|
50 |
-
In order to test that the image is not being reconverted every time, look at the Response headers of the image. There should be a "X-WebP-On-Demand" header. It should say
|
51 |
|
52 |
You can also append `?debug` after any image url, in order to run a conversion, and see the conversion report. Btw: If you append `?reconvert` after an image url, you will force a reconversion of the image.
|
53 |
|
54 |
### Notes
|
55 |
|
56 |
*Note:*
|
57 |
-
The redirect rules created in
|
58 |
|
59 |
*Note:*
|
60 |
-
Do not simply remove the plugin without deactivating it first. Deactivation takes care of removing the rules in the
|
61 |
|
62 |
*Note:*
|
63 |
The plugin has not been tested in multisite configurations. It's on the roadmap!
|
@@ -67,12 +67,7 @@ The plugin has not been tested in multisite configurations. It's on the roadmap!
|
|
67 |
|
68 |
* The plugin does not work on Microsoft IIS server
|
69 |
* The plugin has not been tested with multisite installation (it is on the roadmap!).
|
70 |
-
*
|
71 |
-
* The plugin has not been tested in all possible Wordpress configurations. It has been tested in the following configurations: root install, subdir install, and subdir install with redirect from root (described as method 1 (here)[https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory]).
|
72 |
-
* There might be compatability issues with other plugins. For example .htaccess rules from other plugins might interfere.
|
73 |
-
|
74 |
-
## Known compatability issues
|
75 |
-
* W3TotalCache: When CDN is enabled, W3TotalCache creates some .htaccess rules which interferes when they appear before the rules created by this plugin. You can move them by manually editing the .htaccess file
|
76 |
|
77 |
## Frequently Asked Questions
|
78 |
|
@@ -87,6 +82,29 @@ The plugin takes care of setting the "Vary" HTTP header to "Accept" when routing
|
|
87 |
### How do I donate?
|
88 |
Putting this question in the "frequently" asked questions section is of course some mixture of humour, sarcasm and wishful thinking. In case there really is someone out there wanting to donate, you can simply write to me, and we can arrange. My contact information is available here https://www.bitwise-it.dk/contact. I have paypal and of course an ordinary bank account.
|
89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
# Roadmap
|
91 |
|
92 |
* Share converter with other sites. Optionally provide conversion service for other sites, which will be able to connect to it using the "wpc" converter.
|
35 |
The best converter is *cwebp*. So the first thing you should do is test whether the cwebp converter is working. Simply click "test" next to the converter. If it doesn't work, you can disable that converter (or try to make it work, by changing the server setup).
|
36 |
The next converter to try is *wpc*, which is equally good as cwebp in terms of quality / filesize ratio, but which is slower. wpc is an open source cloud service, which you will have to install on some other server. If this is too much work, continue to the next converter. In the converter settings, you can read about the individual converters. You can also head to the WebPConvert readme for more information.
|
37 |
|
38 |
+
Once, you have a converter, that works, when you click the "test"-button, you are ready to test the whole stack, and the rewrite rules. To do this, first make sure to select something other than "Do not convert any images!" in *Image types to convert*. Next, click "Save settings". This will save settings, as well as update the *.htaccess*.
|
39 |
|
40 |
+
If you are working in a browser that supports webp (ie Google Chrome), you will see a link "Convert test image (show debug)" after a successful save. Click that to test if it works. The screen should show a textual report of the conversion process. If it shows an image, it means that the *.htaccess* redirection isn't working. It may be that your server just needs some time. Some servers has set up caching.
|
41 |
|
42 |
Note that the plugin does not change any HTML. In the HTML the image src is still set to ie "example.jpg". To verify that the plugin is working (without clicking the test button), do the following:
|
43 |
|
47 |
- Reload the page
|
48 |
- Find a jpeg or png image in the list. In the "type" column, it should say "webp"
|
49 |
|
50 |
+
In order to test that the image is not being reconverted every time, look at the Response headers of the image. There should be a "X-WebP-On-Demand" header. It should say "Converting image (handed over to WebPConvertAndServe)" the first time, but "Serving existing converted image" on subsequent requests (WebP-Express is based upon [WebP On Demand](https://github.com/rosell-dk/webp-on-demand)). When routed to image converter, there should also be some headers beginning with "X-WebP-Convert-And-Serve", which reveals information about the conversion.
|
51 |
|
52 |
You can also append `?debug` after any image url, in order to run a conversion, and see the conversion report. Btw: If you append `?reconvert` after an image url, you will force a reconversion of the image.
|
53 |
|
54 |
### Notes
|
55 |
|
56 |
*Note:*
|
57 |
+
The redirect rules created in *.htaccess* are pointing to the WebP on demand script. If you happen to change the url path of your plugins, the rules will have to be updated. The *.htaccess* also passes the path to wp-content (relative to document root) to the script, so the script knows where to find its configuration and where to store converted images. So again, if you move the wp-content folder, or perhaps moves Wordpress to a subfolder, the rules will have to be updated. As moving these things around is is a rare situation, WebP Express are not using any resources monitoring this. However, it will do the check when you visit the settings page.
|
58 |
|
59 |
*Note:*
|
60 |
+
Do not simply remove the plugin without deactivating it first. Deactivation takes care of removing the rules in the *.htaccess* file. With the rules there, but converter gone, your Google Chrome visitors will not see any jpeg images.
|
61 |
|
62 |
*Note:*
|
63 |
The plugin has not been tested in multisite configurations. It's on the roadmap!
|
67 |
|
68 |
* The plugin does not work on Microsoft IIS server
|
69 |
* The plugin has not been tested with multisite installation (it is on the roadmap!).
|
70 |
+
* There might be compatability issues with other plugins. For example *.htaccess* rules from other plugins might interfere.
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
## Frequently Asked Questions
|
73 |
|
82 |
### How do I donate?
|
83 |
Putting this question in the "frequently" asked questions section is of course some mixture of humour, sarcasm and wishful thinking. In case there really is someone out there wanting to donate, you can simply write to me, and we can arrange. My contact information is available here https://www.bitwise-it.dk/contact. I have paypal and of course an ordinary bank account.
|
84 |
|
85 |
+
## Changes in 0.5.0
|
86 |
+
This version works on many more setups than the previous. Also uses less resources and handles when images are changed.
|
87 |
+
|
88 |
+
* Configuration is now stored in a separate configuration file instead of storing directly in the *.htaccess* file and passing it on via query string. When updating, these settings are migrated automatically.
|
89 |
+
* Handles setups where Wordpress has been given its own directory (both methods mentioned [here](https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory))
|
90 |
+
* Handles setups where *wp-content* has been moved, even out of Wordpress root.
|
91 |
+
* Handles setups where Uploads folder has been moved, even out of *wp-content*.
|
92 |
+
* Handles setups where Plugins folder has been moved, even out of *wp-content* or out of Wordpress root
|
93 |
+
* Is not as likely to be subject to firewalls blocking requests (in 0.4.0, we passed all options in a querystring, and that could trigger firewalls under some circumstances)
|
94 |
+
* Is not as likely to be subject to rewrite rules from other plugins interfering. WebP Express now stores the .htaccess in the wp-content folder (if you allow it). As this is deeper than the root folder, the rules in here takes precedence over rules in the main *.htaccess*
|
95 |
+
* The *.htaccess* now passes the complete absulute path to the source file instead of a relative path. This is a less error-prone method.
|
96 |
+
* Reconverts the webp, if source image has changed
|
97 |
+
* Now runs on version 1.0.0 of [WebP On Demand](https://github.com/rosell-dk/webp-on-demand). Previously ran on 0.3.0
|
98 |
+
* Now takes care of only loading the PHP classes when needed in order not to slow down your Wordpress. The frontend will only need to process four lines of code. The backend footprint is also quite small now (80 lines of code of hooks)
|
99 |
+
* Now works in Wordpress 4.0 - 4.6.
|
100 |
+
* Added cache-breaking tokens to image test links
|
101 |
+
* Denies deactivation if rewrite rules could not be removed
|
102 |
+
* Refactored thoroughly
|
103 |
+
* More helpful texts.
|
104 |
+
* Extensive testing. Tested on Wordpress 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8 and 4.9. Tested with PHP 5.6, PHP 7.0 and PHP 7.1. Tested on Apache and LiteSpeed. Tested when missing various write permissions. Tested migration. Tested when installed in root, in subfolder, when Wordpress has its own directory (both methods), when wp-content is moved out of Wordpress directory, when plugins is moved out of Wordpress directory, when both of them are moved and when uploads have been moved.
|
105 |
+
|
106 |
+
For more info, see the closed issues on the 0.5.0 milestone on our github repository: https://github.com/rosell-dk/webp-express/milestone/2?closed=1
|
107 |
+
|
108 |
# Roadmap
|
109 |
|
110 |
* Share converter with other sites. Optionally provide conversion service for other sites, which will be able to connect to it using the "wpc" converter.
|
README.txt
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
Contributors: rosell.dk
|
3 |
Donate link: https://www.bitwise-it.dk/contact
|
4 |
Tags: webp, images, performance
|
5 |
-
Requires at least: 4.
|
6 |
Tested up to: 4.9
|
7 |
-
Stable tag: 0.
|
8 |
-
Requires PHP: 5.
|
9 |
License: GPLv3
|
10 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
11 |
|
@@ -45,9 +45,9 @@ The best converter is cwebp. So the first thing you should do is test whether th
|
|
45 |
|
46 |
The next converter to try is wpc, which is equally good as cwebp in terms of quality / filesize ratio, but which is slower. wpc is an open source cloud service, which you will have to install on some other server. It is btw on the roadmap to have this plugin provide the same service. If installing wpc too much work, continue to the next converter. I will not go through all of them here; you can learn more by clicking on the "configure" button on a converter. You can also head to the WebPConvert project for more information.
|
47 |
|
48 |
-
Once, you have a converter, that works, when you click the "test"-button, you are ready to test the whole stack, and the rewrite rules. To do this, first make sure to select something other than "Do not convert any images!" in "Image types to convert". Next, click "Save settings". This will save settings, as well as update the
|
49 |
|
50 |
-
If you are working in a browser that supports webp (ie Google Chrome), you will see a link "Convert test image (show debug)"
|
51 |
|
52 |
Note that the plugin does not change any HTML. In the HTML the image src is still set to ie "example.jpg". To verify that the plugin is working (without clicking the test button), do the following:
|
53 |
|
@@ -57,7 +57,7 @@ Note that the plugin does not change any HTML. In the HTML the image src is stil
|
|
57 |
- Reload the page
|
58 |
- Find a jpeg or png image in the list. In the "type" column, it should say "webp"
|
59 |
|
60 |
-
In order to test that the image is not being reconverted every time, look at the Response headers of the image. There should be a "X-WebP-On-Demand" header. It should say
|
61 |
|
62 |
You can also append ?debug after any image url, in order to run a conversion, and see the conversion report.
|
63 |
Btw: If you append ?reconvert after an image url, you will force a reconversion of the image.
|
@@ -65,24 +65,19 @@ Btw: If you append ?reconvert after an image url, you will force a reconversion
|
|
65 |
### Notes
|
66 |
|
67 |
*Note:*
|
68 |
-
The redirect rules created in
|
69 |
|
70 |
*Note:*
|
71 |
-
Do not simply remove the plugin without deactivating it first. Deactivation takes care of removing the rules in the
|
72 |
|
73 |
*Note:*
|
74 |
The plugin has not been tested in multisite configurations. It's on the roadmap!
|
75 |
|
76 |
-
== Screenshots ==
|
77 |
-
|
78 |
-
|
79 |
== Limitations ==
|
80 |
|
81 |
* The plugin does not work on Microsoft IIS server
|
82 |
* The plugin has not been tested with multisite installation (it is on the roadmap!).
|
83 |
-
*
|
84 |
-
* The plugin has not been tested in all possible Wordpress configurations. It has been tested in the following configurations: root install, subdir install, and subdir install with redirect from root (described as method 1 (here)[https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory]).
|
85 |
-
* There might be compatability issues with other plugins. For example .htaccess rules from other plugins might interfere.
|
86 |
|
87 |
== Frequently Asked Questions ==
|
88 |
|
@@ -105,24 +100,35 @@ Putting this question in the "frequently" asked questions section is of course s
|
|
105 |
|
106 |
== Changelog ==
|
107 |
|
108 |
-
= 0.
|
109 |
-
|
110 |
-
|
111 |
-
*
|
112 |
-
*
|
113 |
-
*
|
114 |
-
*
|
115 |
-
|
116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
|
118 |
== Upgrade Notice ==
|
119 |
|
120 |
-
= 0.
|
121 |
-
This version
|
122 |
|
123 |
== Roadmap ==
|
124 |
|
125 |
* Share converter with other sites. Optionally provide conversion service for other sites, which will be able to connect to it using the "wpc" converter.
|
126 |
-
*
|
127 |
-
* Display whether the server is able to detect quality of jpegs or not
|
128 |
-
* Make the fallback quality configurable (the quality to use, when quality of source file cannot be determined)
|
2 |
Contributors: rosell.dk
|
3 |
Donate link: https://www.bitwise-it.dk/contact
|
4 |
Tags: webp, images, performance
|
5 |
+
Requires at least: 4.0
|
6 |
Tested up to: 4.9
|
7 |
+
Stable tag: 0.5.0
|
8 |
+
Requires PHP: 5.6
|
9 |
License: GPLv3
|
10 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
11 |
|
45 |
|
46 |
The next converter to try is wpc, which is equally good as cwebp in terms of quality / filesize ratio, but which is slower. wpc is an open source cloud service, which you will have to install on some other server. It is btw on the roadmap to have this plugin provide the same service. If installing wpc too much work, continue to the next converter. I will not go through all of them here; you can learn more by clicking on the "configure" button on a converter. You can also head to the WebPConvert project for more information.
|
47 |
|
48 |
+
Once, you have a converter, that works, when you click the "test"-button, you are ready to test the whole stack, and the rewrite rules. To do this, first make sure to select something other than "Do not convert any images!" in "Image types to convert". Next, click "Save settings". This will save settings, as well as update the *.htaccess*.
|
49 |
|
50 |
+
If you are working in a browser that supports webp (ie Google Chrome), you will see a link "Convert test image (show debug)" after a successful save. Click that to test if it works. The screen should show a textual report of the conversion process. If it shows an image, it means that the *.htaccess* redirection isn't working. It may be that your server just needs some time. Some servers has set up caching.
|
51 |
|
52 |
Note that the plugin does not change any HTML. In the HTML the image src is still set to ie "example.jpg". To verify that the plugin is working (without clicking the test button), do the following:
|
53 |
|
57 |
- Reload the page
|
58 |
- Find a jpeg or png image in the list. In the "type" column, it should say "webp"
|
59 |
|
60 |
+
In order to test that the image is not being reconverted every time, look at the Response headers of the image. There should be a "X-WebP-On-Demand" header. It should say "Converting image (handed over to WebPConvertAndServe)" the first time, but "Serving existing converted image" on subsequent requests (WebP-Express is based upon [WebP On Demand](https://github.com/rosell-dk/webp-on-demand)). When routed to image converter, there should also be some headers beginning with "X-WebP-Convert-And-Serve", which reveals information about the conversion.
|
61 |
|
62 |
You can also append ?debug after any image url, in order to run a conversion, and see the conversion report.
|
63 |
Btw: If you append ?reconvert after an image url, you will force a reconversion of the image.
|
65 |
### Notes
|
66 |
|
67 |
*Note:*
|
68 |
+
The redirect rules created in *.htaccess* are pointing to the WebP on demand script. If you happen to change the url path of your plugins, the rules will have to be updated. The *.htaccess* also passes the path to wp-content (relative to document root) to the script, so the script knows where to find its configuration and where to store converted images. So again, if you move the wp-content folder, or perhaps moves Wordpress to a subfolder, the rules will have to be updated. As moving these things around is is a rare situation, WebP Express are not using any resources monitoring this. However, it will do the check when you visit the settings page.
|
69 |
|
70 |
*Note:*
|
71 |
+
Do not simply remove the plugin without deactivating it first. Deactivation takes care of removing the rules in the *.htaccess* file. With the rules there, but converter gone, your Google Chrome visitors will not see any jpeg images.
|
72 |
|
73 |
*Note:*
|
74 |
The plugin has not been tested in multisite configurations. It's on the roadmap!
|
75 |
|
|
|
|
|
|
|
76 |
== Limitations ==
|
77 |
|
78 |
* The plugin does not work on Microsoft IIS server
|
79 |
* The plugin has not been tested with multisite installation (it is on the roadmap!).
|
80 |
+
* There might be compatability issues with other plugins. For example .htaccess rules from other plugins might interfere. Please report if you discover any problems!
|
|
|
|
|
81 |
|
82 |
== Frequently Asked Questions ==
|
83 |
|
100 |
|
101 |
== Changelog ==
|
102 |
|
103 |
+
= 0.5.0 =
|
104 |
+
This version works on many more setups than the previous. Also uses less resources and handles when images are changed.
|
105 |
+
|
106 |
+
* Configuration is now stored in a separate configuration file instead of storing directly in the *.htaccess* file and passing it on via query string. When updating, these settings are migrated automatically.
|
107 |
+
* Handles setups where Wordpress has been given its own directory (both methods mentioned [here](https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory))
|
108 |
+
* Handles setups where *wp-content* has been moved, even out of Wordpress root.
|
109 |
+
* Handles setups where Uploads folder has been moved, even out of *wp-content*.
|
110 |
+
* Handles setups where Plugins folder has been moved, even out of *wp-content* or out of Wordpress root
|
111 |
+
* Is not as likely to be subject to firewalls blocking requests (in 0.4.0, we passed all options in a querystring, and that could trigger firewalls under some circumstances)
|
112 |
+
* Is not as likely to be subject to rewrite rules from other plugins interfering. WebP Express now stores the .htaccess in the wp-content folder (if you allow it). As this is deeper than the root folder, the rules in here takes precedence over rules in the main *.htaccess*
|
113 |
+
* The *.htaccess* now passes the complete absulute path to the source file instead of a relative path. This is a less error-prone method.
|
114 |
+
* Reconverts the webp, if source image has changed
|
115 |
+
* Now runs on version 1.0.0 of [WebP On Demand](https://github.com/rosell-dk/webp-on-demand). Previously ran on 0.3.0
|
116 |
+
* Now takes care of only loading the PHP classes when needed in order not to slow down your Wordpress. The frontend will only need to process four lines of code. The backend footprint is also quite small now (80 lines of code of hooks)
|
117 |
+
* Now works in Wordpress 4.0 - 4.6.
|
118 |
+
* Added cache-breaking tokens to image test links
|
119 |
+
* Denies deactivation if rewrite rules could not be removed
|
120 |
+
* Refactored thoroughly
|
121 |
+
* More helpful texts.
|
122 |
+
* Extensive testing. Tested on Wordpress 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8 and 4.9. Tested with PHP 5.6, PHP 7.0 and PHP 7.1. Tested on Apache and LiteSpeed. Tested when missing various write permissions. Tested migration. Tested when installed in root, in subfolder, when Wordpress has its own directory (both methods), when wp-content is moved out of Wordpress directory, when plugins is moved out of Wordpress directory, when both of them are moved and when uploads have been moved.
|
123 |
+
|
124 |
+
For more info, see the closed issues on the 0.5.0 milestone on our github repository: https://github.com/rosell-dk/webp-express/milestone/2?closed=1
|
125 |
|
126 |
== Upgrade Notice ==
|
127 |
|
128 |
+
= 0.5.0 =
|
129 |
+
This version is a leap forward regarding stability. Also uses less resources and handles when images are changed.
|
130 |
|
131 |
== Roadmap ==
|
132 |
|
133 |
* Share converter with other sites. Optionally provide conversion service for other sites, which will be able to connect to it using the "wpc" converter.
|
134 |
+
* Support multisite setups
|
|
|
|
changelog.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
= 0.3.1 =
|
2 |
* The "Only jpeg" setting wasn't respected in 0.3.0. It now works again
|
3 |
|
1 |
+
= 0.4.0 =
|
2 |
+
This version fixes some misbehaviours and provides new http headers with info about the conversion process.
|
3 |
+
|
4 |
+
* Fixed bug: .htaccess was not updated every time the settings was saved.
|
5 |
+
* Fixed bug: The plugin generated error upon activation.
|
6 |
+
* Now produces X-WebP-Convert-And-Serve headers with info about the conversion - useful for validating that converter receives the expected arguments and executes correctly.
|
7 |
+
* WebPExpress options are now removed when plugin is uninstalled.
|
8 |
+
* No longer generates .htaccess rules on install. The user now has to actively go to Web Express setting and save first
|
9 |
+
* Added a "first time" message on options page and a reactivation message
|
10 |
+
For more info, see the closed issues on the github repository: https://github.com/rosell-dk/webp-express/milestone/1?closed=1
|
11 |
+
|
12 |
= 0.3.1 =
|
13 |
* The "Only jpeg" setting wasn't respected in 0.3.0. It now works again
|
14 |
|
lib/activate-first-time.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/classes/Actions.php';
|
4 |
+
use \WebPExpress\Actions;
|
5 |
+
|
6 |
+
include_once __DIR__ . '/classes/Config.php';
|
7 |
+
use \WebPExpress\Config;
|
8 |
+
|
9 |
+
include_once __DIR__ . '/classes/Messenger.php';
|
10 |
+
use \WebPExpress\Messenger;
|
11 |
+
|
12 |
+
include_once __DIR__ . '/classes/PlatformInfo.php';
|
13 |
+
use \WebPExpress\PlatformInfo;
|
14 |
+
|
15 |
+
include_once __DIR__ . '/classes/State.php';
|
16 |
+
use \WebPExpress\State;
|
17 |
+
|
18 |
+
// First check basic requirements.
|
19 |
+
// -------------------------------
|
20 |
+
|
21 |
+
if (PlatformInfo::isMicrosoftIis()) {
|
22 |
+
Messenger::addMessage('error', 'You are on Microsof IIS server. The plugin does not work on IIS (yet). The plugin has been <i>deactivated</i> again!');
|
23 |
+
Actions::procastinate('deactivate');
|
24 |
+
|
25 |
+
// Well, that was it.
|
26 |
+
return;
|
27 |
+
}
|
28 |
+
|
29 |
+
|
30 |
+
if ( is_multisite() ) {
|
31 |
+
Messenger::addMessage('error', 'You are on multisite. It is not supported yet. BUT IT IS ON THE ROADMAP! Stay tuned! The plugin has been <i>deactivated</i> again!');
|
32 |
+
Actions::procastinate('deactivate');
|
33 |
+
return;
|
34 |
+
}
|
35 |
+
|
36 |
+
if (!version_compare(PHP_VERSION, '5.5.0', '>=')) {
|
37 |
+
//$msg = sprintf(__( 'You are on a very old version of PHP (%s). WebP Express may not work as intended.', 'webp-express' ), phpversion());
|
38 |
+
Messenger::addMessage(
|
39 |
+
'warning',
|
40 |
+
'You are on a very old version of PHP. WebP Express may not work correctly. Your PHP version:' . phpversion()
|
41 |
+
);
|
42 |
+
return;
|
43 |
+
}
|
44 |
+
|
45 |
+
// Next issue warnings, if any
|
46 |
+
// -------------------------------
|
47 |
+
|
48 |
+
if (PlatformInfo::isApache() || PlatformInfo::isLiteSpeed()) {
|
49 |
+
// all is well.
|
50 |
+
} else {
|
51 |
+
Messenger::addMessage(
|
52 |
+
'warning',
|
53 |
+
'You are not on Apache server, nor on LiteSpeed. WebP Express only works out of the box on Apache and LiteSpeed.<br>' .
|
54 |
+
'But you may get it to work. WebP Express will print you rewrite rules for Apache. You could try to configure your server to do similar routing.<br>' .
|
55 |
+
'Btw: your server is: ' . $_SERVER['SERVER_SOFTWARE']
|
56 |
+
);
|
57 |
+
}
|
58 |
+
|
59 |
+
// Welcome!
|
60 |
+
// -------------------------------
|
61 |
+
Messenger::addMessage(
|
62 |
+
'info',
|
63 |
+
'WebP Express was installed successfully. To start using it, you must ' .
|
64 |
+
'<a href="options-general.php?page=webp_express_settings_page">configure it here</a>.'
|
65 |
+
);
|
lib/activate-hook.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/classes/State.php';
|
4 |
+
use \WebPExpress\State;
|
5 |
+
|
6 |
+
// Test if plugin is activated for the first time, or simply reactivated
|
7 |
+
if (State::getState('configured', false)) {
|
8 |
+
include __DIR__ . "/reactivate.php";
|
9 |
+
} else {
|
10 |
+
include __DIR__ . "/activate-first-time.php";
|
11 |
+
}
|
lib/activate.php
DELETED
@@ -1,88 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
include_once( plugin_dir_path( __FILE__ ) . 'helpers.php');
|
4 |
-
|
5 |
-
class WebPExpressActivate {
|
6 |
-
|
7 |
-
|
8 |
-
public static function activate() {
|
9 |
-
|
10 |
-
update_option( 'webp-express-message-pending', true, false );
|
11 |
-
|
12 |
-
update_option( 'webp-express-just-activated', true, false );
|
13 |
-
|
14 |
-
|
15 |
-
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
|
16 |
-
|
17 |
-
$server_is_microsoft_iis = ( strpos( $server, 'microsoft-iis') !== false );
|
18 |
-
if ($server_is_microsoft_iis) {
|
19 |
-
update_option( 'webp-express-microsoft-iis', true, false );
|
20 |
-
update_option( 'webp-express-deactivate', true, false );
|
21 |
-
return;
|
22 |
-
}
|
23 |
-
|
24 |
-
|
25 |
-
$server_is_litespeed = ( strpos( $server, 'litespeed') !== false );
|
26 |
-
$server_is_apache = ( strpos( $server, 'apache') !== false );
|
27 |
-
|
28 |
-
if ($server_is_litespeed || $server_is_apache) {
|
29 |
-
// all is well.
|
30 |
-
} else {
|
31 |
-
update_option( 'webp-express-not-apache-nor-litespeed', true, false );
|
32 |
-
}
|
33 |
-
|
34 |
-
|
35 |
-
if ( is_multisite() ) {
|
36 |
-
update_option( 'webp-express-no-multisite', true, false );
|
37 |
-
update_option( 'webp-express-deactivate', true, false );
|
38 |
-
return;
|
39 |
-
}
|
40 |
-
|
41 |
-
if (!version_compare(PHP_VERSION, '5.5.0', '>=')) {
|
42 |
-
update_option( 'webp-express-php-too-old', true, false );
|
43 |
-
//update_option( 'webp-express-deactivate', true, false );
|
44 |
-
return;
|
45 |
-
}
|
46 |
-
|
47 |
-
|
48 |
-
// Create upload dir
|
49 |
-
$urlsAndPaths = WebPExpressHelpers::calculateUrlsAndPaths();
|
50 |
-
$ourUploadDir = $urlsAndPaths['filePaths']['destinationRoot'];
|
51 |
-
|
52 |
-
if ( ! file_exists( $ourUploadDir ) ) {
|
53 |
-
wp_mkdir_p( $ourUploadDir );
|
54 |
-
}
|
55 |
-
if ( ! file_exists( $ourUploadDir ) ) {
|
56 |
-
update_option( 'webp-express-failed-creating-upload-dir', true, false );
|
57 |
-
update_option( 'webp-express-deactivate', true, false );
|
58 |
-
return;
|
59 |
-
}
|
60 |
-
|
61 |
-
if (!empty(get_option('webp-express-configured'))) {
|
62 |
-
|
63 |
-
// The plugin has been reactivated.
|
64 |
-
// We must regenerate the .htaccess rules.
|
65 |
-
$rules = WebPExpressHelpers::generateHTAccessRules();
|
66 |
-
WebPExpressHelpers::insertHTAccessRules($rules);
|
67 |
-
|
68 |
-
} else {
|
69 |
-
// WebP Express has not been configured yet.
|
70 |
-
|
71 |
-
// Should we perhaps write to .htaccess, in order to determine if there is a permission problem or not ?
|
72 |
-
// like this:
|
73 |
-
/*
|
74 |
-
if (WebPExpressHelpers::doInsertHTAccessRules('# WebP Express has not been configured yet, so here are no rules yet.')) {
|
75 |
-
|
76 |
-
} else {
|
77 |
-
update_option('webp-express-failed-inserting-rules', true, false);
|
78 |
-
}*/
|
79 |
-
|
80 |
-
|
81 |
-
}
|
82 |
-
|
83 |
-
|
84 |
-
}
|
85 |
-
|
86 |
-
}
|
87 |
-
|
88 |
-
WebPExpressActivate::activate();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/admin.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// When an update requires a migration, the number should be increased
|
4 |
+
define('WEBPEXPRESS_MIGRATION_VERSION', '1');
|
5 |
+
|
6 |
+
if (WEBPEXPRESS_MIGRATION_VERSION != get_option('webp-express-migration-version', 0)) {
|
7 |
+
// run migration logic
|
8 |
+
include __DIR__ . '/migrate/migrate.php';
|
9 |
+
}
|
10 |
+
|
11 |
+
// uncomment next line to debug an error during activation
|
12 |
+
//include __DIR__ . "/debug.php";
|
13 |
+
|
14 |
+
include __DIR__ . '/options/options-hooks.php';
|
15 |
+
|
16 |
+
register_activation_hook(WEBPEXPRESS_PLUGIN, function () {
|
17 |
+
include __DIR__ . '/activate-hook.php';
|
18 |
+
});
|
19 |
+
|
20 |
+
register_deactivation_hook(WEBPEXPRESS_PLUGIN, function () {
|
21 |
+
include __DIR__ . '/deactivate.php';
|
22 |
+
});
|
23 |
+
|
24 |
+
if (get_option('webp-express-messages-pending')) {
|
25 |
+
include_once __DIR__ . '/classes/Messenger.php';
|
26 |
+
add_action( 'admin_notices', function() {
|
27 |
+
\WebPExpress\Messenger::printPendingMessages();
|
28 |
+
});
|
29 |
+
}
|
30 |
+
if (get_option('webp-express-actions-pending')) {
|
31 |
+
include_once __DIR__ . '/classes/Actions.php';
|
32 |
+
\WebPExpress\Actions::processQueuedActions();
|
33 |
+
}
|
34 |
+
|
35 |
+
function webp_express_uninstall() {
|
36 |
+
include __DIR__ . '/uninstall.php';
|
37 |
+
}
|
38 |
+
|
39 |
+
// interestingly, I get "Serialization of 'Closure' is not allowed" if I pass anonymous function
|
40 |
+
// ... perhaps we should not do that in the other hooks either.
|
41 |
+
register_uninstall_hook(WEBPEXPRESS_PLUGIN, 'webp_express_uninstall');
|
42 |
+
|
43 |
+
// Add settings link on the plugins page
|
44 |
+
add_filter('plugin_action_links_' . plugin_basename(WEBPEXPRESS_PLUGIN), function ( $links ) {
|
45 |
+
$mylinks = array(
|
46 |
+
'<a href="' . admin_url( 'options-general.php?page=webp_express_settings_page' ) . '">Settings</a>',
|
47 |
+
);
|
48 |
+
return array_merge( $links, $mylinks );
|
49 |
+
});
|
lib/classes/Actions.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "State.php";
|
6 |
+
use \WebPExpress\State;
|
7 |
+
|
8 |
+
/**
|
9 |
+
*
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Actions
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* $action: identifier
|
16 |
+
*/
|
17 |
+
public static function procastinate($action) {
|
18 |
+
update_option('webp-express-actions-pending', true, true);
|
19 |
+
|
20 |
+
$pendingActions = State::getState('pendingActions', []);
|
21 |
+
$pendingActions[] = $action;
|
22 |
+
State::setState('pendingActions', $pendingActions);
|
23 |
+
}
|
24 |
+
|
25 |
+
public static function takeAction($action) {
|
26 |
+
switch ($action) {
|
27 |
+
case 'deactivate':
|
28 |
+
add_action('admin_init', function () {
|
29 |
+
deactivate_plugins(plugin_basename(WEBPEXPRESS_PLUGIN));
|
30 |
+
});
|
31 |
+
break;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
public static function processQueuedActions() {
|
36 |
+
$actions = State::getState('pendingActions', []);
|
37 |
+
|
38 |
+
foreach ($actions as $action) {
|
39 |
+
self::takeAction($action);
|
40 |
+
}
|
41 |
+
|
42 |
+
State::setState('pendingActions', []);
|
43 |
+
update_option('webp-express-actions-pending', false, true);
|
44 |
+
|
45 |
+
}
|
46 |
+
}
|
lib/classes/Config.php
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "FileHelper.php";
|
6 |
+
use \WebPExpress\FileHelper;
|
7 |
+
|
8 |
+
include_once "HTAccess.php";
|
9 |
+
use \WebPExpress\HTAccess;
|
10 |
+
|
11 |
+
include_once "Messenger.php";
|
12 |
+
use \WebPExpress\Messenger;
|
13 |
+
|
14 |
+
include_once "Paths.php";
|
15 |
+
use \WebPExpress\Paths;
|
16 |
+
|
17 |
+
include_once "State.php";
|
18 |
+
use \WebPExpress\State;
|
19 |
+
|
20 |
+
class Config
|
21 |
+
{
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Return object or false, if config file does not exist, or read error
|
25 |
+
*/
|
26 |
+
public static function loadJSONOptions($filename)
|
27 |
+
{
|
28 |
+
$json = FileHelper::loadFile($filename);
|
29 |
+
if ($json === false) {
|
30 |
+
return false;
|
31 |
+
}
|
32 |
+
|
33 |
+
$options = json_decode($json, true);
|
34 |
+
if ($options === null) {
|
35 |
+
return false;
|
36 |
+
}
|
37 |
+
return $options;
|
38 |
+
}
|
39 |
+
|
40 |
+
public static function saveJSONOptions($filename, $obj)
|
41 |
+
{
|
42 |
+
$result = @file_put_contents($filename, json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK));
|
43 |
+
/*if ($result === false) {
|
44 |
+
echo 'COULD NOT' . $filename;
|
45 |
+
}*/
|
46 |
+
return ($result !== false);
|
47 |
+
}
|
48 |
+
|
49 |
+
|
50 |
+
public static function loadConfig()
|
51 |
+
{
|
52 |
+
return self::loadJSONOptions(Paths::getConfigFileName());
|
53 |
+
}
|
54 |
+
|
55 |
+
public static function isConfigFileThere()
|
56 |
+
{
|
57 |
+
return (FileHelper::fileExists(Paths::getConfigFileName()));
|
58 |
+
}
|
59 |
+
|
60 |
+
public static function isConfigFileThereAndOk()
|
61 |
+
{
|
62 |
+
return (self::loadConfig() !== false);
|
63 |
+
}
|
64 |
+
|
65 |
+
public static function loadWodOptions()
|
66 |
+
{
|
67 |
+
return self::loadJSONOptions(Paths::getWodOptionsFileName());
|
68 |
+
}
|
69 |
+
|
70 |
+
public static function saveConfigurationFile($config)
|
71 |
+
{
|
72 |
+
$config['paths-used-in-htaccess'] = [
|
73 |
+
'existing' => Paths::getPathToExisting(),
|
74 |
+
'wod-url-path' => Paths::getWodUrlPath(),
|
75 |
+
'config-dir-rel' => Paths::getConfigDirRel()
|
76 |
+
];
|
77 |
+
|
78 |
+
if (Paths::createConfigDirIfMissing()) {
|
79 |
+
$success = self::saveJSONOptions(Paths::getConfigFileName(), $config);
|
80 |
+
if ($success) {
|
81 |
+
State::setState('configured', true);
|
82 |
+
}
|
83 |
+
return $success;
|
84 |
+
}
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
|
88 |
+
public static function generateWodOptionsFromConfigObj($config)
|
89 |
+
{
|
90 |
+
$options = $config;
|
91 |
+
$options['converters'] = [];
|
92 |
+
foreach ($config['converters'] as $converter) {
|
93 |
+
if (isset($converter['deactivated'])) continue;
|
94 |
+
|
95 |
+
$options['converters'][] = $converter;
|
96 |
+
}
|
97 |
+
foreach ($options['converters'] as &$c) {
|
98 |
+
unset ($c['id']);
|
99 |
+
if (!isset($c['options'])) {
|
100 |
+
$c = $c['converter'];
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
unset($options['image-types']);
|
105 |
+
return $options;
|
106 |
+
}
|
107 |
+
|
108 |
+
public static function saveWodOptionsFile($options)
|
109 |
+
{
|
110 |
+
if (Paths::createConfigDirIfMissing()) {
|
111 |
+
return self::saveJSONOptions(Paths::getWodOptionsFileName(), $options);
|
112 |
+
}
|
113 |
+
return false;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
*
|
118 |
+
* $rewriteRulesNeedsUpdate:
|
119 |
+
*/
|
120 |
+
public static function saveConfigurationAndHTAccess($config, $forceRuleUpdating = false)
|
121 |
+
{
|
122 |
+
// Important to do this check before saving config, because the method
|
123 |
+
// compares against existing config.
|
124 |
+
|
125 |
+
if ($forceRuleUpdating) {
|
126 |
+
$rewriteRulesNeedsUpdate = true;
|
127 |
+
} else {
|
128 |
+
$rewriteRulesNeedsUpdate = HTAccess::doesRewriteRulesNeedUpdate($config);
|
129 |
+
}
|
130 |
+
|
131 |
+
if (self::saveConfigurationFile($config)) {
|
132 |
+
$options = self::generateWodOptionsFromConfigObj($config);
|
133 |
+
if (self::saveWodOptionsFile($options)) {
|
134 |
+
if ($rewriteRulesNeedsUpdate) {
|
135 |
+
$rulesResult = HTAccess::saveRules($config);
|
136 |
+
return [
|
137 |
+
'saved-both-config' => true,
|
138 |
+
'saved-main-config' => true,
|
139 |
+
'rules-needed-update' => true,
|
140 |
+
'htaccess-result' => $rulesResult
|
141 |
+
];
|
142 |
+
}
|
143 |
+
else {
|
144 |
+
$rulesResult = HTAccess::saveRules($config);
|
145 |
+
return [
|
146 |
+
'saved-both-config' => true,
|
147 |
+
'saved-main-config' => true,
|
148 |
+
'rules-needed-update' => false,
|
149 |
+
'htaccess-result' => $rulesResult
|
150 |
+
];
|
151 |
+
}
|
152 |
+
} else {
|
153 |
+
return [
|
154 |
+
'saved-both-config' => false,
|
155 |
+
'saved-main-config' => true,
|
156 |
+
];
|
157 |
+
}
|
158 |
+
} else {
|
159 |
+
return [
|
160 |
+
'saved-both-config' => false,
|
161 |
+
'saved-main-config' => false,
|
162 |
+
];
|
163 |
+
}
|
164 |
+
}
|
165 |
+
}
|
lib/classes/FileHelper.php
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
class FileHelper
|
6 |
+
{
|
7 |
+
|
8 |
+
public static function fileExists($filename) {
|
9 |
+
return @file_exists($filename);
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Get file permission of a file (integer). Only get the last part, ie 0644
|
14 |
+
* If failure, it returns false
|
15 |
+
*/
|
16 |
+
public static function filePerm($filename) {
|
17 |
+
if (!self::fileExists($filename)) {
|
18 |
+
return false;
|
19 |
+
}
|
20 |
+
|
21 |
+
// fileperms can still fail. In that case, it returns false
|
22 |
+
$perm = @fileperms($filename);
|
23 |
+
if ($perm === false) {
|
24 |
+
return false;
|
25 |
+
}
|
26 |
+
|
27 |
+
return octdec(substr(decoct($perm), -4));
|
28 |
+
}
|
29 |
+
|
30 |
+
public static function humanReadableFilePerm($mode) {
|
31 |
+
return substr(decoct($mode), -4);
|
32 |
+
}
|
33 |
+
|
34 |
+
public static function humanReadableFilePermOfFile($filename) {
|
35 |
+
return self::readableFilePerm(self::filePerm($filename));
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* As the return value of the PHP function isn't reliable,
|
40 |
+
* we have our own chmod.
|
41 |
+
*/
|
42 |
+
public static function chmod($filename, $mode) {
|
43 |
+
// In case someone carelessly passed the result of a filePerm call, which was false:
|
44 |
+
if ($mode === false) {
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
$existingPermission = self::filePerm($filename);
|
48 |
+
if ($mode === $existingPermission) {
|
49 |
+
return true;
|
50 |
+
}
|
51 |
+
if (@chmod($filename, $mode)) {
|
52 |
+
// in some cases chmod returns true, even though it did not succeed!
|
53 |
+
// - so we test if our operation had the desired effect.
|
54 |
+
if (self::filePerm($filename) !== $mode) {
|
55 |
+
return false;
|
56 |
+
}
|
57 |
+
return true;
|
58 |
+
}
|
59 |
+
return false;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Get directory part of filename.
|
64 |
+
* Ie '/var/www/.htaccess' => '/var/www'
|
65 |
+
* Also works with backslashes
|
66 |
+
*/
|
67 |
+
public static function dirName($filename) {
|
68 |
+
return preg_replace('/[\/\\\\][^\/\\\\]*$/', '', $filename);
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Determines if a file can be created.
|
73 |
+
* BEWARE: It requires that the containing folder already exists
|
74 |
+
*/
|
75 |
+
public static function canCreateFile($filename) {
|
76 |
+
$dirName = self::dirName($filename);
|
77 |
+
if (!@file_exists($dirName)) {
|
78 |
+
return false;
|
79 |
+
}
|
80 |
+
if (@is_writable($dirName) && @is_executable($dirName)) {
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
|
84 |
+
$existingPermission = self::filePerm($dirName);
|
85 |
+
|
86 |
+
// we need to make sure we got the existing permission, so we can revert correctly later
|
87 |
+
if ($existingPermission !== false) {
|
88 |
+
if (self::chmod($dirName, 0775)) {
|
89 |
+
// change back
|
90 |
+
self::chmod($filename, $existingPermission);
|
91 |
+
return true;
|
92 |
+
}
|
93 |
+
}
|
94 |
+
return false;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Note: Do not use for directories
|
99 |
+
*/
|
100 |
+
public static function canEditFile($filename) {
|
101 |
+
if (!@file_exists($filename)) {
|
102 |
+
return false;
|
103 |
+
}
|
104 |
+
if (@is_writable($filename) && @is_readable($filename)) {
|
105 |
+
return true;
|
106 |
+
}
|
107 |
+
|
108 |
+
// As a last desperate try, lets see if we can give ourself write permissions.
|
109 |
+
// If possible, then it will also be possible when actually writing
|
110 |
+
$existingPermission = self::filePerm($filename);
|
111 |
+
|
112 |
+
// we need to make sure we got the existing permission, so we can revert correctly later
|
113 |
+
if ($existingPermission !== false) {
|
114 |
+
if (self::chmod($filename, 0664)) {
|
115 |
+
// change back
|
116 |
+
self::chmod($filename, $existingPermission);
|
117 |
+
return true;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
return false;
|
121 |
+
|
122 |
+
// Idea: Perhaps we should also try to actually open the file for writing?
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
public static function canEditOrCreateFileHere($filename) {
|
127 |
+
if (@file_exists($filename)) {
|
128 |
+
return self::canEditFile($filename);
|
129 |
+
} else {
|
130 |
+
return self::canCreateFile($filename);
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Try to read from a file. Tries hard.
|
136 |
+
* Returns content, or false if read error.
|
137 |
+
*/
|
138 |
+
public static function loadFile($filename) {
|
139 |
+
$changedPermission = false;
|
140 |
+
if (!@is_readable($filename)) {
|
141 |
+
$existingPermission = self::filePerm($filename);
|
142 |
+
|
143 |
+
// we need to make sure we got the existing permission, so we can revert correctly later
|
144 |
+
if ($existingPermission !== false) {
|
145 |
+
$changedPermission = self::chmod($filename, 0664);
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
$return = false;
|
150 |
+
$handle = @fopen($filename, "r");
|
151 |
+
if ($handle !== false) {
|
152 |
+
// Return value is either file content or false
|
153 |
+
$return = @fread($handle, filesize($filename));
|
154 |
+
fclose($handle);
|
155 |
+
}
|
156 |
+
|
157 |
+
if ($changedPermission) {
|
158 |
+
// change back
|
159 |
+
self::chmod($filename, $existingPermission);
|
160 |
+
}
|
161 |
+
return $return;
|
162 |
+
}
|
163 |
+
}
|
lib/classes/HTAccess.php
ADDED
@@ -0,0 +1,458 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "Config.php";
|
6 |
+
use \WebPExpress\Config;
|
7 |
+
|
8 |
+
include_once "FileHelper.php";
|
9 |
+
use \WebPExpress\FileHelper;
|
10 |
+
|
11 |
+
include_once "Paths.php";
|
12 |
+
use \WebPExpress\Paths;
|
13 |
+
|
14 |
+
include_once "State.php";
|
15 |
+
use \WebPExpress\State;
|
16 |
+
|
17 |
+
class HTAccess
|
18 |
+
{
|
19 |
+
|
20 |
+
public static function generateHTAccessRulesFromConfigObj($config)
|
21 |
+
{
|
22 |
+
|
23 |
+
/* Calculate $fileExt */
|
24 |
+
$imageTypes = $config['image-types'];
|
25 |
+
$fileExtensions = [];
|
26 |
+
if ($imageTypes & 1) {
|
27 |
+
$fileExtensions[] = 'jpe?g';
|
28 |
+
}
|
29 |
+
if ($imageTypes & 2) {
|
30 |
+
$fileExtensions[] = 'png';
|
31 |
+
}
|
32 |
+
$fileExt = implode('|', $fileExtensions);
|
33 |
+
|
34 |
+
if ($imageTypes == 0) {
|
35 |
+
return '# WebP Express disabled (no image types have been choosen to be converted)';
|
36 |
+
}
|
37 |
+
/* Build rules */
|
38 |
+
$rules = '';
|
39 |
+
|
40 |
+
// The next line sets an environment variable.
|
41 |
+
// On the options page, we verify if this is set to diagnose if "AllowOverride None" is presented in 'httpd.conf'
|
42 |
+
//$rules .= "# The following SetEnv allows to diagnose if .htaccess files are turned off\n";
|
43 |
+
//$rules .= "SetEnv HTACCESS on\n\n";
|
44 |
+
|
45 |
+
$rules .= "<IfModule mod_rewrite.c>\n" .
|
46 |
+
" RewriteEngine On\n\n";
|
47 |
+
|
48 |
+
$pathToExisting = Paths::getPathToExisting();
|
49 |
+
|
50 |
+
/*
|
51 |
+
// TODO: handle when wp-content is outside document root.
|
52 |
+
// TODO: this should be made optional
|
53 |
+
if (true) {
|
54 |
+
# Redirect to existing converted image (under appropriate circumstances)
|
55 |
+
$rules .= " RewriteCond %{HTTP_ACCEPT} image/webp\n";
|
56 |
+
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $pathToExisting . "/$1.$2.webp -f\n";
|
57 |
+
$rules .= " RewriteRule ^\/?(.*)\.(" . $fileExt . ")$ /" . $pathToExisting . "/$1.$2.webp [NC,T=image/webp,QSD,L]\n\n";
|
58 |
+
}*/
|
59 |
+
|
60 |
+
|
61 |
+
$rules .= " # Redirect images to webp-on-demand.php (if browser supports webp)\n";
|
62 |
+
$rules .= " RewriteCond %{HTTP_ACCEPT} image/webp\n";
|
63 |
+
if ($config['forward-query-string']) {
|
64 |
+
$rules .= " RewriteCond %{QUERY_STRING} (.*)\n";
|
65 |
+
}
|
66 |
+
$rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ " .
|
67 |
+
"/" . Paths::getWodUrlPath() .
|
68 |
+
"?source=%{SCRIPT_FILENAME}" .
|
69 |
+
"&wp-content=" . Paths::getWPContentDirRel() .
|
70 |
+
($config['forward-query-string'] ? '&%1' : '') .
|
71 |
+
" [NC,L]\n";
|
72 |
+
|
73 |
+
$rules .="</IfModule>\n" .
|
74 |
+
"AddType image/webp .webp\n";
|
75 |
+
|
76 |
+
return $rules;
|
77 |
+
}
|
78 |
+
|
79 |
+
public static function generateHTAccessRulesFromConfigFile() {
|
80 |
+
if (Config::isConfigFileThereAndOk()) {
|
81 |
+
return self::generateHTAccessRulesFromConfigObj(Config::loadConfig());
|
82 |
+
} else {
|
83 |
+
return false;
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
public static function arePathsUsedInHTAccessOutdated() {
|
88 |
+
if (!Config::isConfigFileThere()) {
|
89 |
+
// this properly means that rewrite rules have never been generated
|
90 |
+
return false;
|
91 |
+
}
|
92 |
+
|
93 |
+
$pathsGoingToBeUsedInHtaccess = [
|
94 |
+
'existing' => Paths::getPathToExisting(),
|
95 |
+
'wod-url-path' => Paths::getWodUrlPath(),
|
96 |
+
'config-dir-rel' => Paths::getConfigDirRel()
|
97 |
+
];
|
98 |
+
|
99 |
+
$config = Config::loadConfig();
|
100 |
+
if ($config === false) {
|
101 |
+
// corrupt or not readable
|
102 |
+
return true;
|
103 |
+
}
|
104 |
+
|
105 |
+
foreach ($config['paths-used-in-htaccess'] as $prop => $value) {
|
106 |
+
if ($value != $pathsGoingToBeUsedInHtaccess[$prop]) {
|
107 |
+
return true;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
public static function doesRewriteRulesNeedUpdate($newConfig) {
|
113 |
+
if (!Config::isConfigFileThere()) {
|
114 |
+
// this properly means that rewrite rules have never been generated
|
115 |
+
return true;
|
116 |
+
}
|
117 |
+
|
118 |
+
$oldConfig = Config::loadConfig();
|
119 |
+
if ($oldConfig === false) {
|
120 |
+
// corrupt or not readable
|
121 |
+
return true;
|
122 |
+
}
|
123 |
+
|
124 |
+
$propsToCompare = ['forward-query-string', 'image-types'];
|
125 |
+
|
126 |
+
|
127 |
+
foreach ($propsToCompare as $prop) {
|
128 |
+
if ($newConfig[$prop] != $oldConfig[$prop]) {
|
129 |
+
return true;
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
if (!isset($oldConfig['paths-used-in-htaccess'])) {
|
134 |
+
return true;
|
135 |
+
}
|
136 |
+
|
137 |
+
return self::arePathsUsedInHTAccessOutdated();
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Must be parsed ie "wp-content", "index", etc. Not real dirs
|
142 |
+
*/
|
143 |
+
public static function addToActiveHTAccessDirsArray($whichDir)
|
144 |
+
{
|
145 |
+
$activeHtaccessDirs = State::getState('active-htaccess-dirs', []);
|
146 |
+
if (!in_array($whichDir, $activeHtaccessDirs)) {
|
147 |
+
$activeHtaccessDirs[] = $whichDir;
|
148 |
+
State::setState('active-htaccess-dirs', array_values($activeHtaccessDirs));
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
public static function removeFromActiveHTAccessDirsArray($whichDir)
|
153 |
+
{
|
154 |
+
$activeHtaccessDirs = State::getState('active-htaccess-dirs', []);
|
155 |
+
if (in_array($whichDir, $activeHtaccessDirs)) {
|
156 |
+
$activeHtaccessDirs = array_diff($activeHtaccessDirs, [$whichDir]);
|
157 |
+
State::setState('active-htaccess-dirs', array_values($activeHtaccessDirs));
|
158 |
+
}
|
159 |
+
}
|
160 |
+
|
161 |
+
public static function isInActiveHTAccessDirsArray($whichDir)
|
162 |
+
{
|
163 |
+
$activeHtaccessDirs = State::getState('active-htaccess-dirs', []);
|
164 |
+
return (in_array($whichDir, $activeHtaccessDirs));
|
165 |
+
}
|
166 |
+
|
167 |
+
public static function whichHTAccessDirIsThis($dir) {
|
168 |
+
switch ($dir) {
|
169 |
+
case Paths::getWPContentDirAbs():
|
170 |
+
return 'wp-content';
|
171 |
+
case Paths::getIndexDirAbs():
|
172 |
+
return 'index';
|
173 |
+
case Paths::getHomeDirAbs():
|
174 |
+
return 'home';
|
175 |
+
case Paths::getPluginDirAbs():
|
176 |
+
return 'plugins';
|
177 |
+
case Paths::getUploadDirAbs():
|
178 |
+
return 'uploads';
|
179 |
+
}
|
180 |
+
return '';
|
181 |
+
}
|
182 |
+
|
183 |
+
public static function hasRecordOfSavingHTAccessToDir($dir) {
|
184 |
+
$whichDir = self::whichHTAccessDirIsThis($dir);
|
185 |
+
if ($whichDir != '') {
|
186 |
+
return self::isInActiveHTAccessDirsArray($whichDir);
|
187 |
+
}
|
188 |
+
return false;
|
189 |
+
}
|
190 |
+
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Sneak peak into .htaccess to see if we have rules in it
|
194 |
+
* This may not be possible.
|
195 |
+
* Return true, false, or null if we just can't tell
|
196 |
+
*/
|
197 |
+
public static function haveWeRulesInThisHTAccess($filename) {
|
198 |
+
if (FileHelper::fileExists($filename)) {
|
199 |
+
$content = FileHelper::loadFile($filename);
|
200 |
+
if ($content === false) {
|
201 |
+
return null;
|
202 |
+
}
|
203 |
+
return (strpos($content, '# Redirect images to webp-on-demand.php') != false);
|
204 |
+
} else {
|
205 |
+
// the .htaccess isn't even there. So there are no rules.
|
206 |
+
return false;
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
public static function haveWeRulesInThisHTAccessBestGuess($filename)
|
211 |
+
{
|
212 |
+
// First try to sneak peak. May return null if it cannot be determined.
|
213 |
+
$result = self::haveWeRulesInThisHTAccess($filename);
|
214 |
+
if ($result === true) {
|
215 |
+
return true;
|
216 |
+
}
|
217 |
+
if ($result === null) {
|
218 |
+
// We were not allowed to sneak-peak.
|
219 |
+
// Well, good thing that we stored successful .htaccess write locations ;)
|
220 |
+
// If we recorded a successful write, then we assume there are still rules there
|
221 |
+
$dir = FileHelper::dirName($filename);
|
222 |
+
return self::hasRecordOfSavingHTAccessToDir($dir);
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
public static function saveHTAccessRulesToFile($filename, $rules, $createIfMissing = false) {
|
227 |
+
if (!@file_exists($filename)) {
|
228 |
+
if (!$createIfMissing) {
|
229 |
+
return false;
|
230 |
+
}
|
231 |
+
// insert_with_markers will create file if it doesn't exist, so we can continue...
|
232 |
+
}
|
233 |
+
|
234 |
+
$existingFilePermission = null;
|
235 |
+
$existingDirPermission = null;
|
236 |
+
|
237 |
+
// Try to make .htaccess writable if its not
|
238 |
+
if (@file_exists($filename)) {
|
239 |
+
if (!@is_writable($filename)) {
|
240 |
+
$existingFilePermission = FileHelper::filePerm($filename);
|
241 |
+
@chmod($filename, 0664); // chmod may fail, we know...
|
242 |
+
}
|
243 |
+
} else {
|
244 |
+
$dir = FileHelper::dirName($filename);
|
245 |
+
if (!@is_writable($dir)) {
|
246 |
+
$existingDirPermission = FileHelper::filePerm($dir);
|
247 |
+
@chmod($dir, 0775);
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
/* Add rules to .htaccess */
|
252 |
+
if (!function_exists('insert_with_markers')) {
|
253 |
+
require_once ABSPATH . 'wp-admin/includes/misc.php';
|
254 |
+
}
|
255 |
+
|
256 |
+
// Convert to array, because string version has bugs in Wordpress 4.3
|
257 |
+
$rules = explode("\n", $rules);
|
258 |
+
$success = insert_with_markers($filename, 'WebP Express', $rules);
|
259 |
+
|
260 |
+
// Revert file or dir permissions
|
261 |
+
if (!is_null($existingFilePermission)) {
|
262 |
+
@chmod($filename, $existingFilePermission);
|
263 |
+
}
|
264 |
+
if (!is_null($existingDirPermission)) {
|
265 |
+
@chmod($dir, $existingDirPermission);
|
266 |
+
}
|
267 |
+
|
268 |
+
if ($success) {
|
269 |
+
State::setState('htaccess-rules-saved-at-some-point', true);
|
270 |
+
|
271 |
+
$containsRules = (strpos(implode('',$rules), '# Redirect images to webp-on-demand.php') != false);
|
272 |
+
|
273 |
+
$dir = FileHelper::dirName($filename);
|
274 |
+
$whichDir = self::whichHTAccessDirIsThis($dir);
|
275 |
+
if ($whichDir != '') {
|
276 |
+
if ($containsRules) {
|
277 |
+
self::addToActiveHTAccessDirsArray($whichDir);
|
278 |
+
} else {
|
279 |
+
self::removeFromActiveHTAccessDirsArray($whichDir);
|
280 |
+
}
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
return $success;
|
285 |
+
}
|
286 |
+
|
287 |
+
public static function saveHTAccessRulesToFirstWritableHTAccessDir($dirs, $rules)
|
288 |
+
{
|
289 |
+
foreach ($dirs as $dir) {
|
290 |
+
if (self::saveHTAccessRulesToFile($dir . '/.htaccess', $rules, true)) {
|
291 |
+
return $dir;
|
292 |
+
}
|
293 |
+
}
|
294 |
+
return false;
|
295 |
+
}
|
296 |
+
|
297 |
+
|
298 |
+
/**
|
299 |
+
* Try to deactivate all .htaccess rules.
|
300 |
+
* If success, we return true.
|
301 |
+
* If we fail, we return an array of filenames that have problems
|
302 |
+
*/
|
303 |
+
public static function deactivateHTAccessRules() {
|
304 |
+
//return self::saveHTAccessRules('# Plugin is deactivated');
|
305 |
+
$indexDir = Paths::getIndexDirAbs();
|
306 |
+
$homeDir = Paths::getHomeDirAbs();
|
307 |
+
$wpContentDir = Paths::getWPContentDirAbs();
|
308 |
+
$pluginDir = Paths::getPluginDirAbs();
|
309 |
+
$uploadDir = Paths::getUploadDirAbs();
|
310 |
+
|
311 |
+
$dirsToClean = [$indexDir, $homeDir, $wpContentDir, $pluginDir, $uploadDir];
|
312 |
+
|
313 |
+
$failures = [];
|
314 |
+
|
315 |
+
foreach ($dirsToClean as $dir) {
|
316 |
+
$filename = $dir . '/.htaccess';
|
317 |
+
if (!FileHelper::fileExists($filename)) {
|
318 |
+
continue;
|
319 |
+
} else {
|
320 |
+
if (self::haveWeRulesInThisHTAccessBestGuess($filename)) {
|
321 |
+
if (!self::saveHTAccessRulesToFile($filename, '# Plugin is deactivated', false)) {
|
322 |
+
$failures[] = $filename;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
}
|
326 |
+
}
|
327 |
+
if (count($failures) == 0) {
|
328 |
+
return true;
|
329 |
+
}
|
330 |
+
return $failures;
|
331 |
+
}
|
332 |
+
|
333 |
+
public static function testLinks($config) {
|
334 |
+
if (isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false )) {
|
335 |
+
if ($config['image-types'] != 0) {
|
336 |
+
$webpExpressRoot = Paths::getPluginUrlPath();
|
337 |
+
return '<br>' .
|
338 |
+
'<a href="/' . $webpExpressRoot . '/test/test.jpg?debug&time=' . time() . '" target="_blank">Convert test image (show debug)</a><br>' .
|
339 |
+
'<a href="/' . $webpExpressRoot . '/test/test.jpg?' . time() . '" target="_blank">Convert test image</a><br>';
|
340 |
+
}
|
341 |
+
}
|
342 |
+
return '';
|
343 |
+
}
|
344 |
+
|
345 |
+
|
346 |
+
public static function getHTAccessDirRequirements() {
|
347 |
+
$minRequired = 'index';
|
348 |
+
if (Paths::isWPContentDirMovedOutOfAbsPath()) {
|
349 |
+
$minRequired = 'wp-content';
|
350 |
+
$pluginToo = Paths::isPluginDirMovedOutOfWpContent() ? 'yes' : 'no';
|
351 |
+
$uploadToo = Paths::isUploadDirMovedOutOfWPContentDir() ? 'yes' : 'no';
|
352 |
+
} else {
|
353 |
+
// plugin requirement depends...
|
354 |
+
// - if user grants access to 'index', the requirement is Paths::isPluginDirMovedOutOfAbsPath()
|
355 |
+
// - if user grants access to 'wp-content', the requirement is Paths::isPluginDirMovedOutOfWpContent()
|
356 |
+
$pluginToo = 'depends';
|
357 |
+
|
358 |
+
// plugin requirement depends...
|
359 |
+
// - if user grants access to 'index', we should be fine, as UPLOADS is always in ABSPATH.
|
360 |
+
// - if user grants access to 'wp-content', the requirement is Paths::isUploadDirMovedOutOfWPContentDir()
|
361 |
+
$uploadToo = 'depends';
|
362 |
+
}
|
363 |
+
|
364 |
+
return [
|
365 |
+
$minRequired,
|
366 |
+
$pluginToo, // 'yes', 'no' or 'depends'
|
367 |
+
$uploadToo
|
368 |
+
];
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Try to save the rules.
|
373 |
+
* Returns many details
|
374 |
+
*/
|
375 |
+
public static function saveRules($config) {
|
376 |
+
|
377 |
+
$rules = HTAccess::generateHTAccessRulesFromConfigObj($config);
|
378 |
+
|
379 |
+
list($minRequired, $pluginToo, $uploadToo) = self::getHTAccessDirRequirements();
|
380 |
+
|
381 |
+
$indexDir = Paths::getIndexDirAbs();
|
382 |
+
$wpContentDir = Paths::getWPContentDirAbs();
|
383 |
+
|
384 |
+
$acceptableDirs = [
|
385 |
+
$wpContentDir
|
386 |
+
];
|
387 |
+
if ($minRequired == 'index') {
|
388 |
+
$acceptableDirs[] = $indexDir;
|
389 |
+
}
|
390 |
+
|
391 |
+
$overidingRulesInWpContentWarning = false;
|
392 |
+
$result = HTAccess::saveHTAccessRulesToFirstWritableHTAccessDir($acceptableDirs, $rules);
|
393 |
+
if ($result == $wpContentDir) {
|
394 |
+
$mainResult = 'wp-content';
|
395 |
+
//if (self::haveWeRulesInThisHTAccessBestGuess($indexDir . '/.htaccess')) {
|
396 |
+
HTAccess::saveHTAccessRulesToFile($indexDir . '/.htaccess', '# WebP Express has placed its rules in your wp-content dir. Go there.', false);
|
397 |
+
//}
|
398 |
+
} elseif ($result == $indexDir) {
|
399 |
+
$mainResult = 'index';
|
400 |
+
$overidingRulesInWpContentWarning = self::haveWeRulesInThisHTAccessBestGuess($wpContentDir . '/.htaccess');
|
401 |
+
} elseif ($result === false) {
|
402 |
+
$mainResult = 'failed';
|
403 |
+
}
|
404 |
+
|
405 |
+
/* plugin */
|
406 |
+
if ($pluginToo == 'depends') {
|
407 |
+
if ($mainResult == 'wp-content') {
|
408 |
+
$pluginToo = (Paths::isPluginDirMovedOutOfWpContent() ? 'yes' : 'no');
|
409 |
+
} elseif ($mainResult == 'index') {
|
410 |
+
$pluginToo = (Paths::isPluginDirMovedOutOfAbsPath() ? 'yes' : 'no');
|
411 |
+
} else {
|
412 |
+
// $result must be false. So $pluginToo should still be 'depends'
|
413 |
+
}
|
414 |
+
}
|
415 |
+
$pluginFailed = false;
|
416 |
+
$pluginFailedBadly = true;
|
417 |
+
if ($pluginToo == 'yes') {
|
418 |
+
$pluginDir = Paths::getPluginDirAbs();
|
419 |
+
$pluginFailed = !(HTAccess::saveHTAccessRulesToFile($pluginDir . '/.htaccess', $rules, true));
|
420 |
+
if ($pluginFailed) {
|
421 |
+
$pluginFailedBadly = self::haveWeRulesInThisHTAccessBestGuess($pluginDir . '/.htaccess');
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
/* upload */
|
426 |
+
if ($uploadToo == 'depends') {
|
427 |
+
if ($mainResult == 'wp-content') {
|
428 |
+
$uploadToo = (Paths::isUploadDirMovedOutOfWPContentDir() ? 'yes' : 'no');
|
429 |
+
} elseif ($mainResult == 'index') {
|
430 |
+
$uploadToo = (Paths::isUploadDirMovedOutOfAbsPath() ? 'yes' : 'no');
|
431 |
+
} else {
|
432 |
+
// $result must be false. So $uploadToo should still be 'depends'
|
433 |
+
}
|
434 |
+
}
|
435 |
+
$uploadFailed = false;
|
436 |
+
$uploadFailedBadly = true;
|
437 |
+
if ($uploadToo == 'yes') {
|
438 |
+
$uploadDir = Paths::getUploadDirAbs();
|
439 |
+
$uploadFailed = !(HTAccess::saveHTAccessRulesToFile($uploadDir . '/.htaccess', $rules, true));
|
440 |
+
if ($uploadFailed) {
|
441 |
+
$uploadFailedBadly = self::haveWeRulesInThisHTAccessBestGuess($uploadDir . '/.htaccess');
|
442 |
+
}
|
443 |
+
}
|
444 |
+
|
445 |
+
return [
|
446 |
+
'mainResult' => $mainResult, // 'index', 'wp-content' or 'failed'
|
447 |
+
'minRequired' => $minRequired, // 'index' or 'wp-content'
|
448 |
+
'overidingRulesInWpContentWarning' => $overidingRulesInWpContentWarning, // true if main result is 'index' but we cannot remove those in wp-content
|
449 |
+
'rules' => $rules, // The rules we generated
|
450 |
+
'pluginToo' => $pluginToo, // 'yes', 'no' or 'depends'
|
451 |
+
'pluginFailed' => $pluginFailed, // true if failed to write to plugin folder (it only tries that, if pluginToo == 'yes')
|
452 |
+
'pluginFailedBadly' => $pluginFailedBadly, // true if plugin failed AND it seems we have rewrite rules there
|
453 |
+
'uploadToo' => $uploadToo, // 'yes', 'no' or 'depends'
|
454 |
+
'uploadFailed' => $uploadFailed,
|
455 |
+
'uploadFailedBadly' => $uploadFailedBadly,
|
456 |
+
];
|
457 |
+
}
|
458 |
+
}
|
lib/classes/Messenger.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "State.php";
|
6 |
+
use \WebPExpress\State;
|
7 |
+
|
8 |
+
class Messenger
|
9 |
+
{
|
10 |
+
private static $printedStyles = false;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* $level: info | success | warning | error
|
14 |
+
* $msg: the message (not translated)
|
15 |
+
*
|
16 |
+
* Hm... we should add some sprintf-like support
|
17 |
+
* $msg = sprintf(__( 'You are on a very old version of PHP (%s). WebP Express may not work as intended.', 'webp-express' ), phpversion());
|
18 |
+
*/
|
19 |
+
public static function addMessage($level, $msg) {
|
20 |
+
|
21 |
+
update_option('webp-express-messages-pending', true, true); // We want this option to be autoloaded
|
22 |
+
|
23 |
+
$pendingMessages = State::getState('pendingMessages', []);
|
24 |
+
$pendingMessages[] = ['level' => $level, 'message' => $msg];
|
25 |
+
State::setState('pendingMessages', $pendingMessages);
|
26 |
+
}
|
27 |
+
|
28 |
+
public static function printMessage($level, $msg) {
|
29 |
+
if (!(self::$printedStyles)) {
|
30 |
+
global $wp_version;
|
31 |
+
if (floatval(substr($wp_version, 0, 3)) < 4.1) {
|
32 |
+
// Actually, I don't know precisely what version the styles were introduced.
|
33 |
+
// They are there in 4.1. They are not there in 4.0
|
34 |
+
self::printMessageStylesForOldWordpress();
|
35 |
+
}
|
36 |
+
self::$printedStyles = true;
|
37 |
+
}
|
38 |
+
|
39 |
+
//$msg = __( $msg, 'webp-express'); // uncommented. We should add some sprintf-like functionality before making the plugin translatable
|
40 |
+
printf(
|
41 |
+
'<div class="%1$s"><p>%2$s</p></div>',
|
42 |
+
esc_attr('notice notice-' . $level . ' is-dismissible'),
|
43 |
+
$msg
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
+
private static function printMessageStylesForOldWordpress() {
|
48 |
+
?>
|
49 |
+
<style>
|
50 |
+
/* In Older Wordpress (ie 4.0), .notice is not declared */
|
51 |
+
.notice {
|
52 |
+
background: #fff;
|
53 |
+
border-left: 4px solid #fff;
|
54 |
+
-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
55 |
+
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
|
56 |
+
margin: 10px 15px 2px 2px;
|
57 |
+
padding: 1px 12px;
|
58 |
+
}
|
59 |
+
.notice-error {
|
60 |
+
border-left-color: #dc3232;
|
61 |
+
}
|
62 |
+
.notice-success {
|
63 |
+
border-left-color: #46b450;
|
64 |
+
}
|
65 |
+
.notice-info {
|
66 |
+
border-left-color: #00a0d2;
|
67 |
+
}
|
68 |
+
.notice-warning {
|
69 |
+
border-left-color: #ffb900;
|
70 |
+
}
|
71 |
+
</style>
|
72 |
+
<?php
|
73 |
+
}
|
74 |
+
|
75 |
+
public static function printPendingMessages() {
|
76 |
+
|
77 |
+
|
78 |
+
$messages = State::getState('pendingMessages', []);
|
79 |
+
|
80 |
+
foreach ($messages as $message) {
|
81 |
+
self::printMessage($message['level'], $message['message']);
|
82 |
+
}
|
83 |
+
|
84 |
+
State::setState('pendingMessages', []);
|
85 |
+
update_option('webp-express-messages-pending', false, true);
|
86 |
+
}
|
87 |
+
}
|
lib/classes/PathHelper.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
class PathHelper
|
6 |
+
{
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Replace double slash with single slash. ie '/var//www/' => '/var/www/'
|
10 |
+
* This allows you to lazely concatenate paths with '/' and then call this method to clean up afterwards.
|
11 |
+
* Also removes triple slash etc.
|
12 |
+
*/
|
13 |
+
public static function fixDoubleSlash($str)
|
14 |
+
{
|
15 |
+
return preg_replace('/\/\/+/', '/', $str);
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Remove trailing slash, if any
|
20 |
+
*/
|
21 |
+
public static function untrailSlash($str)
|
22 |
+
{
|
23 |
+
return rtrim($str, '/');
|
24 |
+
//return preg_replace('/\/$/', '', $str);
|
25 |
+
}
|
26 |
+
|
27 |
+
// Canonicalize a path by resolving '../' and './'
|
28 |
+
// Got it from a comment here: http://php.net/manual/en/function.realpath.php
|
29 |
+
// But fixed it (it could not handle './../')
|
30 |
+
public static function canonicalize($path) {
|
31 |
+
$parts = explode('/', $path);
|
32 |
+
|
33 |
+
// Remove parts containing just '.' (and the empty holes afterwards)
|
34 |
+
$parts = array_values(array_filter($parts, function($var) {
|
35 |
+
return ($var != '.');
|
36 |
+
}));
|
37 |
+
|
38 |
+
// Remove parts containing '..' and the preceding
|
39 |
+
$keys = array_keys($parts, '..');
|
40 |
+
foreach($keys as $keypos => $key) {
|
41 |
+
array_splice($parts, $key - ($keypos * 2 + 1), 2);
|
42 |
+
}
|
43 |
+
return implode('/', $parts);
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Returns absolute path from a relative path and root
|
48 |
+
* The result is canonicalized (dots and double-dots are resolved)
|
49 |
+
*
|
50 |
+
* @param $path Absolute path or relative path
|
51 |
+
* @param $root What the path is relative to, if its relative
|
52 |
+
*/
|
53 |
+
public static function relPathToAbsPath($path, $root)
|
54 |
+
{
|
55 |
+
return self::canonicalize(self::fixDoubleSlash($root . '/' . $path));
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* isAbsPath
|
60 |
+
* If path starts with '/', it is considered an absolute path (no Windows support)
|
61 |
+
*
|
62 |
+
* @param $path Path to inspect
|
63 |
+
*/
|
64 |
+
public static function isAbsPath($path)
|
65 |
+
{
|
66 |
+
return (substr($path, 0, 1) == '/');
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Returns absolute path from a path which can either be absolute or relative to second argument.
|
71 |
+
* If path starts with '/', it is considered an absolute path.
|
72 |
+
* The result is canonicalized (dots and double-dots are resolved)
|
73 |
+
*
|
74 |
+
* @param $path Absolute path or relative path
|
75 |
+
* @param $root What the path is relative to, if its relative
|
76 |
+
*/
|
77 |
+
public static function pathToAbsPath($path, $root)
|
78 |
+
{
|
79 |
+
if (self::isAbsPath($path)) {
|
80 |
+
// path is already absolute
|
81 |
+
return $path;
|
82 |
+
} else {
|
83 |
+
return self::relPathToAbsPath($path, $root);
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Get relative path between two absolute paths
|
89 |
+
* Examples:
|
90 |
+
* from '/var/www' to 'var/ddd'. Result: '../ddd'
|
91 |
+
* from '/var/www' to 'var/www/images'. Result: 'images'
|
92 |
+
* from '/var/www' to 'var/www'. Result: '.'
|
93 |
+
*/
|
94 |
+
public static function getRelDir($fromPath, $toPath)
|
95 |
+
{
|
96 |
+
$fromDirParts = explode('/', str_replace('\\', '/', self::canonicalize(self::untrailSlash($fromPath))));
|
97 |
+
$toDirParts = explode('/', str_replace('\\', '/', self::canonicalize(self::untrailSlash($toPath))));
|
98 |
+
$i = 0;
|
99 |
+
while (($i < count($fromDirParts)) && ($i < count($toDirParts)) && ($fromDirParts[$i] == $toDirParts[$i])) {
|
100 |
+
$i++;
|
101 |
+
}
|
102 |
+
$rel = "";
|
103 |
+
for ($j = $i; $j < count($fromDirParts); $j++) {
|
104 |
+
$rel .= "../";
|
105 |
+
}
|
106 |
+
|
107 |
+
for ($j = $i; $j < count($toDirParts); $j++) {
|
108 |
+
$rel .= $toDirParts[$j];
|
109 |
+
if ($j < count($toDirParts)-1) {
|
110 |
+
$rel .= '/';
|
111 |
+
}
|
112 |
+
}
|
113 |
+
if ($rel == '') {
|
114 |
+
$rel = '.';
|
115 |
+
}
|
116 |
+
return $rel;
|
117 |
+
}
|
118 |
+
|
119 |
+
}
|
lib/classes/Paths.php
ADDED
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "PathHelper.php";
|
6 |
+
use \WebPExpress\PathHelper;
|
7 |
+
|
8 |
+
include_once "FileHelper.php";
|
9 |
+
use \WebPExpress\FileHelper;
|
10 |
+
|
11 |
+
class Paths
|
12 |
+
{
|
13 |
+
|
14 |
+
public static function createDirIfMissing($dir)
|
15 |
+
{
|
16 |
+
if (!file_exists($dir)) {
|
17 |
+
wp_mkdir_p($dir);
|
18 |
+
}
|
19 |
+
return file_exists($dir);
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Find out if $dir1 is inside - or equal to - $dir2
|
24 |
+
*/
|
25 |
+
public static function isDirInsideDir($dir1, $dir2)
|
26 |
+
{
|
27 |
+
$rel = PathHelper::getRelDir($dir2, $dir1);
|
28 |
+
return (substr($rel, 0, 3) != '../');
|
29 |
+
}
|
30 |
+
|
31 |
+
// ------------ Home Dir -------------
|
32 |
+
|
33 |
+
public static function getHomeDirAbs()
|
34 |
+
{
|
35 |
+
if (!function_exists('get_home_path')) {
|
36 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
37 |
+
}
|
38 |
+
return rtrim(get_home_path(), '/');
|
39 |
+
}
|
40 |
+
|
41 |
+
public static function getHomeDirRel()
|
42 |
+
{
|
43 |
+
return PathHelper::getRelDir($_SERVER['DOCUMENT_ROOT'], self::getHomeDirAbs());
|
44 |
+
}
|
45 |
+
|
46 |
+
// ------------ Index Dir -------------
|
47 |
+
// (The Wordpress installation dir)
|
48 |
+
|
49 |
+
public static function getIndexDirAbs()
|
50 |
+
{
|
51 |
+
return rtrim(ABSPATH, '/');
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
// ------------ .htaccess dir -------------
|
56 |
+
// (directory containing the relevant .htaccess)
|
57 |
+
// (see https://github.com/rosell-dk/webp-express/issues/36)
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
public static function canWriteHTAccessRulesHere($dirName) {
|
62 |
+
return FileHelper::canEditOrCreateFileHere($dirName . '/.htaccess');
|
63 |
+
}
|
64 |
+
|
65 |
+
public static function returnFirstWritableHTAccessDir($dirs)
|
66 |
+
{
|
67 |
+
foreach ($dirs as $dir) {
|
68 |
+
if (self::canWriteHTAccessRulesHere($dir)) {
|
69 |
+
return $dir;
|
70 |
+
}
|
71 |
+
}
|
72 |
+
return false;
|
73 |
+
}
|
74 |
+
|
75 |
+
// ------------ WP Content Dir -------------
|
76 |
+
public static function getWPContentDirAbs()
|
77 |
+
{
|
78 |
+
return rtrim(WP_CONTENT_DIR, '/');
|
79 |
+
}
|
80 |
+
public static function getWPContentDirRel()
|
81 |
+
{
|
82 |
+
return PathHelper::getRelDir($_SERVER['DOCUMENT_ROOT'], self::getWPContentDirAbs());
|
83 |
+
}
|
84 |
+
|
85 |
+
public static function isWPContentDirMoved()
|
86 |
+
{
|
87 |
+
return (self::getWPContentDirAbs() != (ABSPATH . 'wp-content'));
|
88 |
+
}
|
89 |
+
|
90 |
+
public static function isWPContentDirMovedOutOfAbsPath()
|
91 |
+
{
|
92 |
+
return !(self::isDirInsideDir(self::getWPContentDirAbs(), ABSPATH));
|
93 |
+
}
|
94 |
+
|
95 |
+
|
96 |
+
// ------------ Content Dir -------------
|
97 |
+
// (the "webp-express" directory inside wp-content)
|
98 |
+
|
99 |
+
public static function getContentDirAbs()
|
100 |
+
{
|
101 |
+
return rtrim(WP_CONTENT_DIR, '/') . '/webp-express';
|
102 |
+
}
|
103 |
+
|
104 |
+
public static function getContentDirRel()
|
105 |
+
{
|
106 |
+
return PathHelper::getRelDir($_SERVER['DOCUMENT_ROOT'], self::getContentDirAbs());
|
107 |
+
}
|
108 |
+
|
109 |
+
public static function createContentDirIfMissing()
|
110 |
+
{
|
111 |
+
return self::createDirIfMissing(self::getContentDirAbs());
|
112 |
+
}
|
113 |
+
|
114 |
+
// ------------ Upload Dir -------------
|
115 |
+
// (can be moved out of wp-content dir. But not (rarely? - I suppose someone could enter dots) out of abspath)
|
116 |
+
|
117 |
+
public static function getUploadDirAbs()
|
118 |
+
{
|
119 |
+
// Pst: When supporting multisite, we could use wp_upload_dir instead
|
120 |
+
// https://developer.wordpress.org/reference/functions/wp_upload_dir/
|
121 |
+
if ( defined( 'UPLOADS' ) ) {
|
122 |
+
return ABSPATH . rtrim(UPLOADS, '/');
|
123 |
+
} else {
|
124 |
+
return self::getWPContentDirAbs() . '/uploads';
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
public static function isUploadDirMovedOutOfWPContentDir()
|
129 |
+
{
|
130 |
+
return !(self::isDirInsideDir(self::getUploadDirAbs(), self::getWPContentDirAbs()));
|
131 |
+
}
|
132 |
+
|
133 |
+
public static function isUploadDirMovedOutOfAbsPath()
|
134 |
+
{
|
135 |
+
return !(self::isDirInsideDir(self::getUploadDirAbs(), ABSPATH));
|
136 |
+
}
|
137 |
+
|
138 |
+
// ------------ Config Dir -------------
|
139 |
+
|
140 |
+
public static function getConfigDirAbs()
|
141 |
+
{
|
142 |
+
return self::getContentDirAbs() . '/config';
|
143 |
+
}
|
144 |
+
|
145 |
+
public static function getConfigDirRel()
|
146 |
+
{
|
147 |
+
return PathHelper::getRelDir($_SERVER['DOCUMENT_ROOT'], self::getConfigDirAbs());
|
148 |
+
}
|
149 |
+
|
150 |
+
public static function createConfigDirIfMissing()
|
151 |
+
{
|
152 |
+
$configDir = self::getConfigDirAbs();
|
153 |
+
// Using code from Wordfence bootstrap.php...
|
154 |
+
// Why not simply use wp_mkdir_p ? - it sets the permissions to same as parent. Isn't that better?
|
155 |
+
// or perhaps not... - Because we need write permissions in the config dir.
|
156 |
+
if (!is_dir($configDir)) {
|
157 |
+
@mkdir($configDir, 0775);
|
158 |
+
@chmod($configDir, 0775);
|
159 |
+
@file_put_contents(rtrim($configDir . '/') . '/.htaccess', <<<APACHE
|
160 |
+
<IfModule mod_authz_core.c>
|
161 |
+
Require all denied
|
162 |
+
</IfModule>
|
163 |
+
<IfModule !mod_authz_core.c>
|
164 |
+
Order deny,allow
|
165 |
+
Deny from all
|
166 |
+
</IfModule>
|
167 |
+
APACHE
|
168 |
+
);
|
169 |
+
@chmod($configDir . '/.htaccess', 0664);
|
170 |
+
}
|
171 |
+
return is_dir($configDir);
|
172 |
+
}
|
173 |
+
|
174 |
+
public static function getConfigFileName()
|
175 |
+
{
|
176 |
+
return self::getConfigDirAbs() . '/config.json';
|
177 |
+
}
|
178 |
+
|
179 |
+
public static function getWodOptionsFileName()
|
180 |
+
{
|
181 |
+
return self::getConfigDirAbs() . '/wod-options.json';
|
182 |
+
}
|
183 |
+
|
184 |
+
// ------------ Cache Dir -------------
|
185 |
+
|
186 |
+
public static function getCacheDirAbs()
|
187 |
+
{
|
188 |
+
return self::getContentDirAbs() . '/webp-images';
|
189 |
+
}
|
190 |
+
|
191 |
+
public static function getCacheDirRel()
|
192 |
+
{
|
193 |
+
return PathHelper::getRelDir($_SERVER['DOCUMENT_ROOT'], self::getCacheDirAbs());
|
194 |
+
}
|
195 |
+
|
196 |
+
public static function createCacheDirIfMissing()
|
197 |
+
{
|
198 |
+
return self::createDirIfMissing(self::getCacheDirAbs());
|
199 |
+
}
|
200 |
+
|
201 |
+
// ------------ Plugin Dir (all plugins) -------------
|
202 |
+
|
203 |
+
public static function getPluginDirAbs()
|
204 |
+
{
|
205 |
+
return untrailingslashit(WP_PLUGIN_DIR);
|
206 |
+
}
|
207 |
+
|
208 |
+
public static function isPluginDirMovedOutOfAbsPath()
|
209 |
+
{
|
210 |
+
return !(self::isDirInsideDir(self::getPluginDirAbs(), ABSPATH));
|
211 |
+
}
|
212 |
+
|
213 |
+
public static function isPluginDirMovedOutOfWpContent()
|
214 |
+
{
|
215 |
+
return !(self::isDirInsideDir(self::getPluginDirAbs(), self::getWPContentDirAbs()));
|
216 |
+
}
|
217 |
+
|
218 |
+
// ------------ WebP Express Plugin Dir -------------
|
219 |
+
|
220 |
+
public static function getWebPExpressPluginDirAbs()
|
221 |
+
{
|
222 |
+
return untrailingslashit(WEBPEXPRESS_PLUGIN_DIR);
|
223 |
+
}
|
224 |
+
|
225 |
+
|
226 |
+
// ------------------------------------
|
227 |
+
// --------- Url paths ----------
|
228 |
+
// ------------------------------------
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Get url path (relative to domain) from absolute url.
|
232 |
+
* Ie: "http://example.com/blog" => "blog"
|
233 |
+
* Btw: By "url path" we shall always mean relative to domain
|
234 |
+
* By "url" we shall always mean complete URL (with domain and everything)
|
235 |
+
* (or at least something that starts with it...)
|
236 |
+
*
|
237 |
+
* Also note that in this library, we never returns trailing or leading slashes.
|
238 |
+
*/
|
239 |
+
public static function getUrlPathFromUrl($url)
|
240 |
+
{
|
241 |
+
$parsed = parse_url($url);
|
242 |
+
if (!isset($parsed['path'])) {
|
243 |
+
return '';
|
244 |
+
}
|
245 |
+
if (is_null($parsed['path'])) {
|
246 |
+
return '';
|
247 |
+
}
|
248 |
+
$path = untrailingslashit($parsed['path']);
|
249 |
+
return ltrim($path, '/\\');
|
250 |
+
}
|
251 |
+
|
252 |
+
// Get complete home url (no trailing slash). Ie: "http://example.com/blog"
|
253 |
+
public static function getHomeUrl()
|
254 |
+
{
|
255 |
+
if (!function_exists('get_home_url')) {
|
256 |
+
// silence is golden?
|
257 |
+
}
|
258 |
+
return untrailingslashit(home_url());
|
259 |
+
}
|
260 |
+
|
261 |
+
/** Get home url, relative to domain. Ie "" or "blog"
|
262 |
+
* If home url is for example http://example.com/blog/, the result is "blog"
|
263 |
+
*/
|
264 |
+
public static function getHomeUrlPath()
|
265 |
+
{
|
266 |
+
return self::getUrlPathFromUrl(self::getHomeUrl());
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* Get Url to plugin (this is in fact an incomplete URL, you need to append ie '/webp-on-demand.php' to get a full URL)
|
271 |
+
*/
|
272 |
+
public static function getPluginUrl()
|
273 |
+
{
|
274 |
+
return untrailingslashit(plugins_url('', WEBPEXPRESS_PLUGIN));
|
275 |
+
}
|
276 |
+
|
277 |
+
public static function getPluginUrlPath()
|
278 |
+
{
|
279 |
+
return self::getUrlPathFromUrl(self::getPluginUrl());
|
280 |
+
}
|
281 |
+
|
282 |
+
public static function getWodUrlPath()
|
283 |
+
{
|
284 |
+
return self::getPluginUrlPath() . '/wod/webp-on-demand.php';
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Calculate path to existing image, excluding
|
289 |
+
* (relative to document root)
|
290 |
+
* Ie: "/webp-express-test/wordpress/wp-content/webp-express/webp-images/webp-express-test/wordpress/"
|
291 |
+
* This is needed for the .htaccess
|
292 |
+
*/
|
293 |
+
public static function getPathToExisting()
|
294 |
+
{
|
295 |
+
return self::getCacheDirRel() . '/' . self::getHomeDirRel();
|
296 |
+
}
|
297 |
+
|
298 |
+
public static function getUrlsAndPathsForTheJavascript()
|
299 |
+
{
|
300 |
+
return [
|
301 |
+
'urls' => [
|
302 |
+
'webpExpressRoot' => self::getPluginUrlPath(),
|
303 |
+
],
|
304 |
+
'filePaths' => [
|
305 |
+
'webpExpressRoot' => self::getWebPExpressPluginDirAbs(),
|
306 |
+
'destinationRoot' => self::getCacheDirAbs()
|
307 |
+
]
|
308 |
+
];
|
309 |
+
}
|
310 |
+
|
311 |
+
}
|
lib/classes/PlatformInfo.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
class PlatformInfo
|
6 |
+
{
|
7 |
+
|
8 |
+
public static function isMicrosoftIis()
|
9 |
+
{
|
10 |
+
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
|
11 |
+
return ( strpos( $server, 'microsoft-iis') !== false );
|
12 |
+
}
|
13 |
+
|
14 |
+
public static function isApache()
|
15 |
+
{
|
16 |
+
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
|
17 |
+
return ( strpos( $server, 'apache') !== false );
|
18 |
+
}
|
19 |
+
|
20 |
+
public static function isLiteSpeed()
|
21 |
+
{
|
22 |
+
$server = strtolower($_SERVER['SERVER_SOFTWARE']);
|
23 |
+
return ( strpos( $server, 'litespeed') !== false );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* It is not always possible to determine if apache has a given module...
|
28 |
+
* We shall not fool anyone into thinking otherwise by providing a "got" method like Wordpress does...
|
29 |
+
*/
|
30 |
+
public static function definitelyNotGotApacheModule($mod)
|
31 |
+
{
|
32 |
+
if (function_exists( 'apache_get_modules')) {
|
33 |
+
$mods = apache_get_modules();
|
34 |
+
if (!in_array($mod, $mods)) {
|
35 |
+
return true;
|
36 |
+
}
|
37 |
+
}
|
38 |
+
// TODO: Perhaps also try looking at phpinfo, like Wordpress does in apache_mod_loaded
|
39 |
+
|
40 |
+
return false;
|
41 |
+
}
|
42 |
+
|
43 |
+
public static function definitelyGotApacheModule($mod)
|
44 |
+
{
|
45 |
+
if (function_exists( 'apache_get_modules')) {
|
46 |
+
$mods = apache_get_modules();
|
47 |
+
if (in_array($mod, $mods)) {
|
48 |
+
return true;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
return false;
|
52 |
+
}
|
53 |
+
|
54 |
+
public static function definitelyNotGotModRewrite()
|
55 |
+
{
|
56 |
+
return self::definitelyNotGotApacheModule('mod_rewrite');
|
57 |
+
}
|
58 |
+
|
59 |
+
public static function definitelyGotModEnv()
|
60 |
+
{
|
61 |
+
return self::definitelyGotApacheModule('mod_env');
|
62 |
+
}
|
63 |
+
|
64 |
+
|
65 |
+
}
|
lib/classes/State.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Store state in db
|
7 |
+
* We are using update_option WITHOUT autoloading.
|
8 |
+
* So this class is not intended for storing stuff that is needed on every page load.
|
9 |
+
* For such things, use update_option / get_option directly
|
10 |
+
*/
|
11 |
+
|
12 |
+
class State
|
13 |
+
{
|
14 |
+
|
15 |
+
public static function getStateObj() {
|
16 |
+
// TODO: cache
|
17 |
+
$json = get_option('webp-express-state', '[]');
|
18 |
+
return json_decode($json, true);
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Return state by key. Returns supplied default if key doesn't exist, or state object is corrupt
|
23 |
+
*/
|
24 |
+
public static function getState($key, $default = null) {
|
25 |
+
$obj = self::getStateObj();
|
26 |
+
if ($obj != false) {
|
27 |
+
if (isset($obj[$key])) {
|
28 |
+
return $obj[$key];
|
29 |
+
}
|
30 |
+
}
|
31 |
+
return $default;
|
32 |
+
}
|
33 |
+
|
34 |
+
public static function setState($key, $value) {
|
35 |
+
$currentStateObj = self::getStateObj();
|
36 |
+
$currentStateObj[$key] = $value;
|
37 |
+
$json = json_encode($currentStateObj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
|
38 |
+
|
39 |
+
// Store in db. No autoloading.
|
40 |
+
update_option('webp-express-state', $json, false);
|
41 |
+
}
|
42 |
+
}
|
lib/deactivate.php
CHANGED
@@ -1,11 +1,37 @@
|
|
1 |
<?php
|
2 |
-
include_once( plugin_dir_path( __FILE__ ) . 'helpers.php');
|
3 |
|
4 |
-
|
|
|
5 |
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
}
|
10 |
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<?php
|
|
|
2 |
|
3 |
+
include_once __DIR__ . '/classes/Actions.php';
|
4 |
+
use \WebPExpress\Actions;
|
5 |
|
6 |
+
/*include_once __DIR__ . '/classes/Config.php';
|
7 |
+
use \WebPExpress\Config;*/
|
8 |
+
|
9 |
+
include_once __DIR__ . '/classes/HTAccess.php';
|
10 |
+
use \WebPExpress\HTAccess;
|
11 |
+
|
12 |
+
include_once __DIR__ . '/classes/Messenger.php';
|
13 |
+
use \WebPExpress\Messenger;
|
14 |
+
|
15 |
+
include_once __DIR__ . '/classes/Paths.php';
|
16 |
+
use \WebPExpress\Paths;
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
function webpexpress_deny_deactivate($msg) {
|
21 |
+
Messenger::addMessage(
|
22 |
+
'error',
|
23 |
+
$msg
|
24 |
+
);
|
25 |
+
wp_redirect( $_SERVER['HTTP_REFERER']);
|
26 |
+
exit;
|
27 |
}
|
28 |
|
29 |
+
$result = HTAccess::deactivateHTAccessRules();
|
30 |
+
if ($result !== true) {
|
31 |
+
// Oh no. We failed removing the rules
|
32 |
+
$msg = "<b>Sorry, can't let you disable WebP Express!</b><br>" .
|
33 |
+
'There are rewrite rules in the <i>.htaccess</i> that could not be removed. If these are not removed, it would break all images.<br>' .
|
34 |
+
'Please make your <i>.htaccess</i> writable and then try to disable WebPExpress again.<br>Alternatively, remove the rules manually in your <i>.htaccess</i> file and try disabling again.' .
|
35 |
+
'<br>It concerns the following files:<br>' . implode('<br>', $result);
|
36 |
+
webpexpress_deny_deactivate($msg);
|
37 |
+
}
|
lib/debug.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
function webpexpress_activated() {
|
5 |
+
update_option( 'webp-express-activation-error', ob_get_contents() );
|
6 |
+
}
|
7 |
+
add_action( 'activated_plugin', 'webpexpress_activated' );
|
8 |
+
if (!empty(get_option('webp-express-activation-error'))) {
|
9 |
+
add_filter( 'admin_footer_text', function() {
|
10 |
+
return 'Activation error:' . get_option('webp-express-activation-error');
|
11 |
+
});
|
12 |
+
}
|
lib/helpers.php
DELETED
@@ -1,311 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class WebPExpressHelpers
|
4 |
-
{
|
5 |
-
|
6 |
-
public static function calculateUrlsAndPaths()
|
7 |
-
{
|
8 |
-
// Calculate URL's
|
9 |
-
$upload_dir = wp_upload_dir();
|
10 |
-
$destinationRootUrlPathAbs = $upload_dir['baseurl'] . '/' . 'webp-express';
|
11 |
-
|
12 |
-
// Calculate url path to directory of image converter
|
13 |
-
$pluginUrlAbs = plugins_url('', WEBPEXPRESS_PLUGIN);
|
14 |
-
|
15 |
-
$converterUrlPath = parse_url($pluginUrlAbs)['path'];
|
16 |
-
|
17 |
-
// Calculate destination root url path
|
18 |
-
$destinationRootUrlPath = parse_url($destinationRootUrlPathAbs)['path'];
|
19 |
-
|
20 |
-
// Calculate Wordpress url path
|
21 |
-
// If site for example is accessed example.com/blog/, then the url path is "blog"
|
22 |
-
$wpUrlPath = trailingslashit(parse_url(site_url())['path']); // ie "/blog/" or "/"
|
23 |
-
|
24 |
-
$converterUrlPathRelativeToSiteUrl = WebPExpressHelpers::get_rel_dir(untrailingslashit($wpUrlPath), $converterUrlPath);
|
25 |
-
$siteUrlPathRelativeToConverterPath = WebPExpressHelpers::get_rel_dir($converterUrlPath, untrailingslashit($wpUrlPath));
|
26 |
-
|
27 |
-
|
28 |
-
// Calculate file dirs
|
29 |
-
// --------------------
|
30 |
-
|
31 |
-
$destinationRoot = trailingslashit($upload_dir['basedir']) . 'webp-express';
|
32 |
-
|
33 |
-
$WebExpressRoot = untrailingslashit(WEBPEXPRESS_PLUGIN_DIR);
|
34 |
-
|
35 |
-
$destinationRootRelativeToWebExpressRoot = untrailingslashit(WebPExpressHelpers::get_rel_dir($WebExpressRoot, $destinationRoot));
|
36 |
-
|
37 |
-
// $destinationRoot is ie '/webp-express-test/wordpress/wp-content/uploads/webp-express/'
|
38 |
-
// $wordpressRoot is ie '/mnt/Work/playground/webp-express-test/wordpress/'
|
39 |
-
$wordpressRoot = untrailingslashit(ABSPATH);
|
40 |
-
$destinationRootRelativeToWordpressRoot = untrailingslashit(WebPExpressHelpers::get_rel_dir($wordpressRoot . '/', $destinationRoot));
|
41 |
-
|
42 |
-
return [
|
43 |
-
'urls' => [
|
44 |
-
'webpExpressRoot' => untrailingslashit($converterUrlPath),
|
45 |
-
'convert' => untrailingslashit($converterUrlPath) . 'convert.php',
|
46 |
-
'destinationRoot' => untrailingslashit($destinationRootUrlPath),
|
47 |
-
'wpRoot' => untrailingslashit($wpUrlPath), // ie "/blog" or ""
|
48 |
-
'converterUrlPathRelativeToSiteUrl' => $converterUrlPathRelativeToSiteUrl,
|
49 |
-
'siteUrlPathRelativeToConverterPath' => $siteUrlPathRelativeToConverterPath
|
50 |
-
],
|
51 |
-
'filePaths' => [
|
52 |
-
'wordpressRoot' => $wordpressRoot,
|
53 |
-
'destinationRoot' => $destinationRoot,
|
54 |
-
'webpExpressRoot' => $WebExpressRoot,
|
55 |
-
'destinationRootRelativeToWebExpressRoot' => $destinationRootRelativeToWebExpressRoot,
|
56 |
-
'destinationRootRelativeToWordpressRoot' => $destinationRootRelativeToWordpressRoot
|
57 |
-
],
|
58 |
-
// TODO: read up on this, and make complete tests
|
59 |
-
// https://wordpress.stackexchange.com/questions/188448/whats-the-difference-between-get-home-path-and-abspath
|
60 |
-
'pathsForHtaccess' => [
|
61 |
-
//$basePath, $destinationRoot, $scriptPath
|
62 |
-
'basePath' => untrailingslashit(WebPExpressHelpers::get_rel_dir($_SERVER['DOCUMENT_ROOT'], untrailingslashit(ABSPATH))),
|
63 |
-
'destinationRoot' => $destinationRootRelativeToWordpressRoot, // Where to place converted files, relative to the base path.
|
64 |
-
'scriptPath' => untrailingslashit($converterUrlPathRelativeToSiteUrl),
|
65 |
-
|
66 |
-
//'abspath' => ABSPATH,
|
67 |
-
//'dr' => $_SERVER['DOCUMENT_ROOT'],
|
68 |
-
//'bp' => str_replace($_SERVER['DOCUMENT_ROOT'] . '/', '', untrailingslashit(ABSPATH)),
|
69 |
-
]
|
70 |
-
];
|
71 |
-
}
|
72 |
-
|
73 |
-
public static function generateHTAccessRules()
|
74 |
-
{
|
75 |
-
//global $wpdb;
|
76 |
-
//$hasWebPExpressOptionBeenSaved = ($wpdb->get_row( "SELECT * FROM $wpdb->options WHERE option_name = 'webp_express_converters'" ) !== null);
|
77 |
-
//if (!$hasWebPExpressOptionBeenSaved) {
|
78 |
-
|
79 |
-
if (empty(get_option('webp-express-configured')) || empty(get_option('webp_express_converters'))) {
|
80 |
-
// This should not happen, because generateHTAccessRules should not be called at this stage.
|
81 |
-
// But if it did happen anyway, better to exit with a comment than failing totally.
|
82 |
-
return '# Cannot generate the htaccess rules yet. - WebP Express has not been configured yet.';
|
83 |
-
}
|
84 |
-
$options = '';
|
85 |
-
$options .= '&max-quality=' . get_option('webp_express_max_quality', '85');
|
86 |
-
//$options .= '&method=' . get_option('webp_express_method');
|
87 |
-
$options .= '&fail=' . get_option('webp_express_failure_response', 'original');
|
88 |
-
$options .= '&critical-fail=report';
|
89 |
-
|
90 |
-
$converters_and_options = json_decode(get_option('webp_express_converters'), true);
|
91 |
-
|
92 |
-
$converter_options = '';
|
93 |
-
foreach ($converters_and_options as $converter) {
|
94 |
-
if (isset($converter['deactivated'])) continue;
|
95 |
-
$converters[] = $converter['converter'];
|
96 |
-
if (isset($converter['options'])) {
|
97 |
-
foreach ($converter['options'] as $converter_option => $converter_value) {
|
98 |
-
$converter_options .= '&' . $converter['id'] . '-' . $converter_option . '=' . $converter_value;
|
99 |
-
}
|
100 |
-
};
|
101 |
-
}
|
102 |
-
$options .= '&converters=' . implode(',', $converters);
|
103 |
-
$options .= $converter_options;
|
104 |
-
|
105 |
-
$urlsAndPaths = WebPExpressHelpers::calculateUrlsAndPaths();
|
106 |
-
$urls = $urlsAndPaths['urls'];
|
107 |
-
$filePaths = $urlsAndPaths['filePaths'];
|
108 |
-
|
109 |
-
$imageTypes = get_option('webp_express_image_types_to_convert', 1);
|
110 |
-
$fileExtensions = [];
|
111 |
-
if ($imageTypes & 1) {
|
112 |
-
$fileExtensions[] = 'jpe?g';
|
113 |
-
}
|
114 |
-
if ($imageTypes & 2) {
|
115 |
-
$fileExtensions[] = 'png';
|
116 |
-
}
|
117 |
-
$fileExt = implode('|', $fileExtensions);
|
118 |
-
|
119 |
-
$paths = $urlsAndPaths['pathsForHtaccess'];
|
120 |
-
return self::generateHTAccessRules2($fileExt, $paths['basePath'], $paths['destinationRoot'], $paths['scriptPath'], $options);
|
121 |
-
}
|
122 |
-
/*
|
123 |
-
if ($imageTypes == 0) {
|
124 |
-
$rules = '# Configured not to convert anything!';
|
125 |
-
//$rules .= 'php_value include_path ".:/usr/local/lib/php:/your/dir"';
|
126 |
-
$rules .= 'php_value include_path ".:/usr/local/lib/php:/hsphere/local/home/z84733/mingo.net/wp-content/plugins/webp-express/vendor/webp-convert/Converters/Binaries"';
|
127 |
-
} else {
|
128 |
-
$rules = "<IfModule mod_rewrite.c>\n" .
|
129 |
-
|
130 |
-
" RewriteEngine On\n\n" .
|
131 |
-
" # Redirect to existing converted image (under appropriate circumstances)\n" .
|
132 |
-
" RewriteCond %{HTTP_ACCEPT} image/webp\n" .
|
133 |
-
" RewriteCond %{QUERY_STRING} !((^reconvert.*)|(^debug.*))\n" .
|
134 |
-
" RewriteCond %{DOCUMENT_ROOT}" . $urls['destinationRoot'] . "/$1.$2.webp -f\n" .
|
135 |
-
" RewriteRule ^\\/?(.*)\.(" . $fileExt . ")$ " . $urls['destinationRoot'] . "/$1.$2.webp [NC,T=image/webp,E=webpaccept:1,E=WEBPEXISTING:1,QSD]\n\n" .
|
136 |
-
" # Redirect to image converter (under appropriate circumstances)\n" .
|
137 |
-
" RewriteCond %{HTTP_ACCEPT} image/webp\n" .
|
138 |
-
" RewriteCond %{QUERY_STRING} (^reconvert.*)|(^debug.*) [OR]\n" .
|
139 |
-
" RewriteCond %{DOCUMENT_ROOT}" . $urls['destinationRoot'] . "/$1.$2.webp !-f\n" .
|
140 |
-
" RewriteCond %{QUERY_STRING} (.*)\n" .
|
141 |
-
//" RewriteRule ^\\/?(.*)\.(" . $fileExt . ")$ " . $urls['converterUrlPathRelativeToSiteUrl'] . "convert.php?source=" . $urls['siteUrlPathRelativeToConverterPath'] . "$1.$2&destination-root=" . $filePaths['destinationRootRelativeToWebExpressRoot'] . $options . "&%1 [NC,E=accept:1]\n" .
|
142 |
-
" RewriteRule ^\\/?(.*)\.(" . $fileExt . ")$ " . $urls['converterUrlPathRelativeToSiteUrl'] . "convert.php?htaccess-path=" . $filePaths['wordpressRoot'] . '&destination-root-rel-to-htaccess-path=' . $filePaths['destinationRootRelativeToWordpressRoot'] . '&source-rel-to-htaccess-path=$1.$2' . $options . "&%1 [NC,E=webpaccept:1,E=WEBPNEW]\n" .
|
143 |
-
|
144 |
-
"</IfModule>\n" .
|
145 |
-
|
146 |
-
"<IfModule mod_headers.c>\n" .
|
147 |
-
" # Apache appends \"REDIRECT_\" in front of the environment variables, but LiteSpeed does not\n" .
|
148 |
-
" # These next three lines are for Apache, in order to set environment variables without \"REDIRECT_\"\n" .
|
149 |
-
" SetEnvIf REDIRECT_WEBPACCEPT 1 WEBPACCEPT=1\n" .
|
150 |
-
" SetEnvIf REDIRECT_WEBPEXISTING 1 WEBPEXISTING=1\n" .
|
151 |
-
" SetEnvIf REDIRECT_WEBPNEW 1 WEBPNEW=1\n\n" .
|
152 |
-
|
153 |
-
" # Make CDN caching possible." .
|
154 |
-
" Header append Vary Accept env=WEBPACCEPT\n\n" .
|
155 |
-
|
156 |
-
" # Add headers for debugging\n" .
|
157 |
-
" Header append X-WebP-Express \"Routed to existing converted image\" env=WEBPEXISTING\n" .
|
158 |
-
" Header append X-WebP-Express \"Routed to image converter\" env=WEBPNEW\n" .
|
159 |
-
"</IfModule>\n\n" .
|
160 |
-
"AddType image/webp .webp\n";
|
161 |
-
}
|
162 |
-
return $rules;
|
163 |
-
}
|
164 |
-
*/
|
165 |
-
/**
|
166 |
-
Create rewrite rules for WebP On Demand.
|
167 |
-
|
168 |
-
@param $fileExt To convert both jpegs and pngs, use "jpe?g|png". To disable converting, use ""
|
169 |
-
@param $basePath Path of the .htaccess relative to document root. Ie "." or "my-sub-site"
|
170 |
-
@param $destinationRoot Where to place converted files, relative to the base path.
|
171 |
-
@param $scriptPath Url path to webp-on-demand.php, relative to the base directory.
|
172 |
-
@param $options String of options. If not empty, it must start with "&". Ie "&converters=cwebp,gd&quality=auto"
|
173 |
-
|
174 |
-
Note: None of the paths supplied may start or end with a forward slash.
|
175 |
-
*/
|
176 |
-
private static function generateHTAccessRules2($fileExt, $basePath, $destinationRoot, $scriptPath, $options)
|
177 |
-
{
|
178 |
-
if ($basePath == '') {
|
179 |
-
//$basePath = '.';
|
180 |
-
}
|
181 |
-
$rules = '';
|
182 |
-
if ($fileExt == '') {
|
183 |
-
$rules .= '# Configured not to convert anything!';
|
184 |
-
} else {
|
185 |
-
$rules .= "<IfModule mod_rewrite.c>\n" .
|
186 |
-
|
187 |
-
" RewriteEngine On\n\n" .
|
188 |
-
|
189 |
-
" # Redirect to existing converted image (under appropriate circumstances)\n" .
|
190 |
-
" RewriteCond %{HTTP_ACCEPT} image/webp\n" .
|
191 |
-
" RewriteCond %{QUERY_STRING} !((^reconvert.*)|(^debug.*))\n" .
|
192 |
-
" RewriteCond %{DOCUMENT_ROOT}/" . $basePath . "/" . $destinationRoot . "/$1.$2.webp -f\n" .
|
193 |
-
" RewriteRule ^\/?(.*)\.(jpe?g|png)$ /" . $basePath . "/" . $destinationRoot . "/$1.$2.webp [NC,T=image/webp,E=WEBPACCEPT:1,E=WEBPEXISTING:1,QSD]\n\n" .
|
194 |
-
|
195 |
-
" # Redirect to converter (under appropriate circumstances)\n" .
|
196 |
-
" RewriteCond %{HTTP_ACCEPT} image/webp\n" .
|
197 |
-
" RewriteCond %{QUERY_STRING} (^reconvert.*)|(^debug.*) [OR]\n" .
|
198 |
-
" RewriteCond %{DOCUMENT_ROOT}/" . $basePath . "/" . $destinationRoot . "/$1.$2.webp !-f\n" .
|
199 |
-
" RewriteCond %{QUERY_STRING} (.*)\n" .
|
200 |
-
" RewriteRule ^\/?(.*)\.(" . $fileExt . ")$ " . $scriptPath . "/webp-on-demand.php?base-path=" . $basePath . "&destination-root=" . $destinationRoot . "&source=$1.$2" . $options . "&%1 [NC,E=WEBPACCEPT:1,E=WEBPNEW:1]\n" .
|
201 |
-
"</IfModule>\n\n" .
|
202 |
-
|
203 |
-
"<IfModule mod_headers.c>\n" .
|
204 |
-
" # Apache appends \"REDIRECT_\" in front of the environment variables, but LiteSpeed does not\n" .
|
205 |
-
" # These next three lines are for Apache, in order to set environment variables without \"REDIRECT_\"\n" .
|
206 |
-
" SetEnvIf REDIRECT_WEBPACCEPT 1 WEBPACCEPT=1\n" .
|
207 |
-
" SetEnvIf REDIRECT_WEBPEXISTING 1 WEBPEXISTING=1\n" .
|
208 |
-
" SetEnvIf REDIRECT_WEBPNEW 1 WEBPNEW=1\n\n" .
|
209 |
-
|
210 |
-
" # Make CDN caching possible.\n" .
|
211 |
-
" Header append Vary Accept env=WEBPACCEPT\n\n" .
|
212 |
-
|
213 |
-
" # Add headers for debugging\n" .
|
214 |
-
" Header append X-WebP-On-Demand \"Routed to existing converted image\" env=WEBPEXISTING\n" .
|
215 |
-
" Header append X-WebP-On-Demand \"Routed to image converter\" env=WEBPNEW\n" .
|
216 |
-
"</IfModule>\n\n" .
|
217 |
-
"AddType image/webp .webp\n";
|
218 |
-
}
|
219 |
-
return $rules;
|
220 |
-
}
|
221 |
-
|
222 |
-
// Insert .htaccess rules.
|
223 |
-
// @return (bool) True if successful, false if not.
|
224 |
-
public static function doInsertHTAccessRules($rules) {
|
225 |
-
if (!function_exists('get_home_path')) {
|
226 |
-
require_once ABSPATH . 'wp-admin/includes/file.php';
|
227 |
-
}
|
228 |
-
$root_path = get_home_path();
|
229 |
-
|
230 |
-
if (!file_exists($root_path . '.htaccess')) {
|
231 |
-
return false;
|
232 |
-
}
|
233 |
-
|
234 |
-
$file_existing_permission = '';
|
235 |
-
|
236 |
-
// Try to make .htaccess writable if its not
|
237 |
-
if (file_exists($root_path . '.htaccess') && !is_writable($root_path . '.htaccess')) {
|
238 |
-
// Store existing permissions, so we can revert later
|
239 |
-
$file_existing_permission = octdec(substr(decoct(fileperms($root_path . '.htaccess')), -4));
|
240 |
-
|
241 |
-
// Try to chmod.
|
242 |
-
// It may fail, but we can ignore that. If it fails, insert_with_markers will also fail
|
243 |
-
chmod($root_path . '.htaccess', 0550);
|
244 |
-
}
|
245 |
-
|
246 |
-
|
247 |
-
/* Add rules to .htaccess */
|
248 |
-
if (!function_exists('insert_with_markers')) {
|
249 |
-
require_once ABSPATH . 'wp-admin/includes/misc.php';
|
250 |
-
}
|
251 |
-
if (!insert_with_markers($root_path . '.htaccess', 'WebP Express', $rules)) {
|
252 |
-
return false;
|
253 |
-
}
|
254 |
-
else {
|
255 |
-
/* Revert File Permission */
|
256 |
-
if (!empty($file_existing_permission)) {
|
257 |
-
chmod($root_path . '.htaccess', $file_existing_permission);
|
258 |
-
}
|
259 |
-
return true;
|
260 |
-
}
|
261 |
-
|
262 |
-
}
|
263 |
-
|
264 |
-
public static function insertHTAccessRules($rules)
|
265 |
-
{
|
266 |
-
if (self::doInsertHTAccessRules($rules)) {
|
267 |
-
update_option('webp-express-message-pending', true, false );
|
268 |
-
update_option('webp-express-inserted-rules-ok', true, false);
|
269 |
-
} else {
|
270 |
-
update_option('webp-express-failed-inserting-rules', true, false);
|
271 |
-
update_option('webp-express-deactivate', true, false);
|
272 |
-
}
|
273 |
-
}
|
274 |
-
|
275 |
-
/* Get relative path between one dir and the other.
|
276 |
-
ie
|
277 |
-
from: /var/www/wordpress/wp-content/plugins/webp-express
|
278 |
-
to: /var/www/wordpress/wp-content/uploads
|
279 |
-
result: ../../uploads/
|
280 |
-
|
281 |
-
or
|
282 |
-
from: /mnt/Work/playground/webp-express-test/wordpress/
|
283 |
-
to: /mnt/Work/playground/webp-express-test/wordpress/wp-content/uploads/webp-express
|
284 |
-
result: wp-content/uploads/webp-express
|
285 |
-
|
286 |
-
*/
|
287 |
-
public static function get_rel_dir($from_dir, $to_dir) {
|
288 |
-
$from_dir = untrailingslashit($from_dir);
|
289 |
-
$to_dir = untrailingslashit($to_dir);
|
290 |
-
|
291 |
-
$from_dir_parts = explode('/', str_replace( '\\', '/', $from_dir ));
|
292 |
-
$to_dir_parts = explode('/', str_replace( '\\', '/', $to_dir ));
|
293 |
-
$i = 0;
|
294 |
-
while (($i < count($from_dir_parts)) && ($i < count($to_dir_parts)) && ($from_dir_parts[$i] == $to_dir_parts[$i])) {
|
295 |
-
$i++;
|
296 |
-
}
|
297 |
-
$rel = "";
|
298 |
-
for ($j = $i; $j < count($from_dir_parts); $j++) {
|
299 |
-
$rel .= "../";
|
300 |
-
}
|
301 |
-
|
302 |
-
for ($j = $i; $j < count($to_dir_parts); $j++) {
|
303 |
-
$rel .= $to_dir_parts[$j] . '/';
|
304 |
-
}
|
305 |
-
if ($rel == '') {
|
306 |
-
$rel = '.';
|
307 |
-
}
|
308 |
-
return $rel;
|
309 |
-
}
|
310 |
-
|
311 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/message.php
DELETED
@@ -1,131 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
delete_option( 'webp-express-message-pending');
|
4 |
-
|
5 |
-
if ( get_option( 'webp-express-deactivate' ) ) {
|
6 |
-
add_action( 'admin_notices', function() {
|
7 |
-
printf(
|
8 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
9 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
10 |
-
__( 'Plugin <b>deactivated</b>' )
|
11 |
-
);
|
12 |
-
});
|
13 |
-
}
|
14 |
-
|
15 |
-
add_action( 'admin_notices', function() {
|
16 |
-
// Possible classes:
|
17 |
-
// notice-warning, notice-error, notice-warning, notice-success, or notice-info
|
18 |
-
// - add is-dismissible
|
19 |
-
|
20 |
-
if ( get_option( 'webp-express-inserted-rules-ok' ) ) {
|
21 |
-
delete_option( 'webp-express-inserted-rules-ok');
|
22 |
-
printf(
|
23 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
24 |
-
esc_attr( 'notice notice-success is-dismissible' ),
|
25 |
-
esc_html( __( 'WebP Express updated .htaccess', 'webp-express' ) )
|
26 |
-
);
|
27 |
-
}
|
28 |
-
|
29 |
-
|
30 |
-
if ( get_option( 'webp-express-just-activated' ) ) {
|
31 |
-
delete_option( 'webp-express-just-activated');
|
32 |
-
|
33 |
-
if ( get_option( 'webp-express-microsoft-iis' ) ) {
|
34 |
-
delete_option( 'webp-express-microsoft-iis');
|
35 |
-
printf(
|
36 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
37 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
38 |
-
esc_html( __( 'You are on Microsof IIS server. The plugin does not work on IIS', 'webp-express' ) )
|
39 |
-
);
|
40 |
-
return;
|
41 |
-
} else if ( get_option( 'webp-express-not-apache-nor-litespeed' ) ) {
|
42 |
-
delete_option( 'webp-express-not-apache-nor-litespeed');
|
43 |
-
printf(
|
44 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
45 |
-
esc_attr( 'notice notice-warning is-dismissible' ),
|
46 |
-
esc_html( __( 'You are not on Apache server, nor on LiteSpeed. WebP Express has only been tested on Apache and LiteSpeed - continue at own risk (but please tell me if it works!). Your server is: ' . $_SERVER['SERVER_SOFTWARE'], 'webp-express' ) )
|
47 |
-
);
|
48 |
-
}
|
49 |
-
|
50 |
-
if ( get_option( 'webp-express-no-multisite' ) ) {
|
51 |
-
delete_option( 'webp-express-no-multisite');
|
52 |
-
printf(
|
53 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
54 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
55 |
-
esc_html( __( 'Sorry, WebP Express does not support multisite websites (yet). A donation might resolve the issue ;-)', 'webp-express' ) )
|
56 |
-
);
|
57 |
-
return;
|
58 |
-
}
|
59 |
-
|
60 |
-
if (get_option( 'webp-express-php-too-old' ) ) {
|
61 |
-
delete_option( 'webp-express-php-too-old');
|
62 |
-
printf(
|
63 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
64 |
-
esc_attr( 'notice notice-warning is-dismissible' ),
|
65 |
-
esc_html( sprintf(__( 'You are on a very old version of PHP (%s). WebP Express may not work as intended.', 'webp-express' ), phpversion() ) )
|
66 |
-
);
|
67 |
-
return;
|
68 |
-
}
|
69 |
-
|
70 |
-
if ( get_option( 'webp-express-failed-creating-upload-dir' ) ) {
|
71 |
-
delete_option( 'webp-express-failed-creating-upload-dir');
|
72 |
-
printf(
|
73 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
74 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
75 |
-
//esc_html( __( 'WebP Express could not create a subfolder in your upload folder. Check your file permissions', 'webp-express' ) )
|
76 |
-
'<b>WebP Express could not create a subfolder in your upload folder</b>. Check your file permissions. <a target="_blank" href="https://github.com/rosell-dk/webp-express/wiki/Error-messages-and-warnings#webp-express-could-not-create-a-subfolder-in-your-upload-folder">Click here</a> for more information.'
|
77 |
-
);
|
78 |
-
return;
|
79 |
-
}
|
80 |
-
|
81 |
-
/*
|
82 |
-
if ( get_option( 'webp-express-htaccess-not-writable' ) ) {
|
83 |
-
delete_option( 'webp-express-htaccess-not-writable');
|
84 |
-
printf(
|
85 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
86 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
87 |
-
'<b>.htaccess is not writable</b>. The plugin has been disabled. To fix this, make .htaccess writable and try activating the plugin again. <a target="_blank" href="https://github.com/rosell-dk/webp-express/wiki/Error-messages-and-warnings#htaccess-is-not-writable">Click here</a> for more information.'
|
88 |
-
//esc_html( __( '.htaccess is not writable. As WebP Express needs to write its redirection rules to .htaccess in order to function, the plugin has been disabled. To fix this, make .htaccess writable and try activating the plugin again. The file is located in the root of your wordpress installation.', 'webp-express' ) )
|
89 |
-
);
|
90 |
-
return;
|
91 |
-
}
|
92 |
-
|
93 |
-
if ( get_option( 'webp-express-failed-inserting-rules' ) ) {
|
94 |
-
delete_option( 'webp-express-failed-inserting-rules');
|
95 |
-
printf(
|
96 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
97 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
98 |
-
esc_html( __( 'WebP Express failed writing rules to .htaccess. The file is writable, but something went wrong writing to it anyway. More precicely, the Wordpress function "insert_with_markers" returned false. The .htaccess rules are needed for this plugin to function, and the plugin has therefore been deactivated.', 'webp-express' ) )
|
99 |
-
);
|
100 |
-
return;
|
101 |
-
}*/
|
102 |
-
|
103 |
-
if ( get_option( 'webp-express-failed-inserting-rules' ) ) {
|
104 |
-
delete_option( 'webp-express-failed-inserting-rules');
|
105 |
-
printf(
|
106 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
107 |
-
esc_attr( 'notice notice-error is-dismissible' ),
|
108 |
-
'<b>.htaccess is not writable</b>. To fix this, make .htaccess writable and try activating the plugin again. <a target="_blank" href="https://github.com/rosell-dk/webp-express/wiki/Error-messages-and-warnings#htaccess-is-not-writable">Click here</a> for more information.'
|
109 |
-
);
|
110 |
-
return;
|
111 |
-
}
|
112 |
-
|
113 |
-
if (empty(get_option('webp-express-configured'))) {
|
114 |
-
printf(
|
115 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
116 |
-
esc_attr( 'notice notice-info is-dismissible' ),
|
117 |
-
'WebP Express was installed successfully. To start using it, you must <a href="options-general.php?page=webp_express_settings_page">configure it here</a>.'
|
118 |
-
);
|
119 |
-
} else {
|
120 |
-
printf(
|
121 |
-
'<div class="%1$s"><p>%2$s</p></div>',
|
122 |
-
esc_attr( 'notice notice-info is-dismissible' ),
|
123 |
-
'WebP Express reactivated successfully.<br>The image redirections should be in effect again (you should see a "WebP Express updated .htaccess" message above this...)<br><br>Just a quick reminder: If you at some point change the upload directory or move Wordpress, you will have to regenerate the .htaccess.<br>You do that by changing the configuration <a href="options-general.php?page=webp_express_settings_page">(here)</a>'
|
124 |
-
);
|
125 |
-
}
|
126 |
-
}
|
127 |
-
});
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/migrate/migrate.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/../classes/State.php';
|
4 |
+
use \WebPExpress\State;
|
5 |
+
|
6 |
+
|
7 |
+
/*
|
8 |
+
In 0.4.0, we had a 'webp-express-configured' option.
|
9 |
+
As long as there are still users on 0.4 or below, we must do the following:
|
10 |
+
*/
|
11 |
+
if (get_option('webp-express-configured', false)) {
|
12 |
+
State::setState('configured', true);
|
13 |
+
}
|
14 |
+
|
15 |
+
/*
|
16 |
+
In 0.1, we did not have the 'webp-express-configured' option.
|
17 |
+
To determine if WebP Express was configured in 0.1, we can test the (now obsolete) webp_express_converters option
|
18 |
+
As long as there are still users on 0.1, we must do the following:
|
19 |
+
*/
|
20 |
+
if (!get_option('webp-express-configured', false)) {
|
21 |
+
if (!is_null(get_option('webp_express_converters', null))) {
|
22 |
+
State::setState('configured', true);
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
|
27 |
+
if (!(State::getState('configured', false))) {
|
28 |
+
// Options has never has been saved, so no migration is needed.
|
29 |
+
// We can set migrate-version to current
|
30 |
+
update_option('webp-express-migration-version', WEBPEXPRESS_MIGRATION_VERSION);
|
31 |
+
} else {
|
32 |
+
|
33 |
+
if (intval(get_option('webp-express-migration-version', 0)) == 0) {
|
34 |
+
// run migration 1
|
35 |
+
// It must take care of updating migration-version to 1, - if successful.
|
36 |
+
include __DIR__ . '/migrate1.php';
|
37 |
+
}
|
38 |
+
|
39 |
+
// When a new version needs a new migration, uncomment this:
|
40 |
+
// (make sure to grab the option again - it might have been changed in the migration above)
|
41 |
+
/*
|
42 |
+
if (intval(get_option('webp-express-migration-version', 0)) == 1) {
|
43 |
+
// run migration 2
|
44 |
+
include __DIR__ . '/migrate2.php';
|
45 |
+
}
|
46 |
+
*/
|
47 |
+
}
|
lib/migrate/migrate1.php
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once __DIR__ . '/../classes/Config.php';
|
6 |
+
use \WebPExpress\Config;
|
7 |
+
|
8 |
+
include_once __DIR__ . '/../classes/HTAccess.php';
|
9 |
+
use \WebPExpress\HTAccess;
|
10 |
+
|
11 |
+
include_once __DIR__ . '/../classes/Paths.php';
|
12 |
+
use \WebPExpress\Paths;
|
13 |
+
|
14 |
+
include_once __DIR__ . '/../classes/Messenger.php';
|
15 |
+
use \WebPExpress\Messenger;
|
16 |
+
|
17 |
+
//Messenger::addMessage('info', 'migration:' . get_option('webp-express-migration-version', 'not set'));
|
18 |
+
|
19 |
+
// On successful migration:
|
20 |
+
// update_option('webp-express-migration-version', '1', true);
|
21 |
+
|
22 |
+
function webp_express_migrate1_createFolders()
|
23 |
+
{
|
24 |
+
if (!Paths::createContentDirIfMissing()) {
|
25 |
+
Messenger::printMessage(
|
26 |
+
'error',
|
27 |
+
'For migration to 0.5.0, WebP Express needs to create a directory "webp-express" under your wp-content folder, but does not have permission to do so.<br>' .
|
28 |
+
'Please create the folder manually, or change the file permissions of your wp-content folder.'
|
29 |
+
);
|
30 |
+
return false;
|
31 |
+
} else {
|
32 |
+
if (!Paths::createConfigDirIfMissing()) {
|
33 |
+
Messenger::printMessage(
|
34 |
+
'error',
|
35 |
+
'For migration to 0.5.0, WebP Express needs to create a directory "webp-express/config" under your wp-content folder, but does not have permission to do so.<br>' .
|
36 |
+
'Please create the folder manually, or change the file permissions.'
|
37 |
+
);
|
38 |
+
return false;
|
39 |
+
}
|
40 |
+
|
41 |
+
|
42 |
+
if (!Paths::createCacheDirIfMissing()) {
|
43 |
+
Messenger::printMessage(
|
44 |
+
'error',
|
45 |
+
'For migration to 0.5.0, WebP Express needs to create a directory "webp-express/webp-images" under your wp-content folder, but does not have permission to do so.<br>' .
|
46 |
+
'Please create the folder manually, or change the file permissions.'
|
47 |
+
);
|
48 |
+
return false;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
return true;
|
52 |
+
}
|
53 |
+
|
54 |
+
function webp_express_migrate1_createDummyConfigFiles()
|
55 |
+
{
|
56 |
+
// TODO...
|
57 |
+
return true;
|
58 |
+
}
|
59 |
+
|
60 |
+
function webpexpress_migrate1_migrateOptions()
|
61 |
+
{
|
62 |
+
$converters = json_decode(get_option('webp_express_converters', '[]'), true);
|
63 |
+
foreach ($converters as &$converter) {
|
64 |
+
unset ($converter['id']);
|
65 |
+
}
|
66 |
+
|
67 |
+
$options = [
|
68 |
+
'image-types' => intval(get_option('webp_express_image_types_to_convert', 1)),
|
69 |
+
'max-quality' => intval(get_option('webp_express_max_quality', 80)),
|
70 |
+
'fail' => get_option('webp_express_failure_response', 'original'),
|
71 |
+
'converters' => $converters,
|
72 |
+
'forward-query-string' => true
|
73 |
+
];
|
74 |
+
if ($options['max-quality'] == 0) {
|
75 |
+
$options['max-quality'] = 80;
|
76 |
+
if ($options['image-types'] == 0) {
|
77 |
+
$options['image-types'] = 1;
|
78 |
+
}
|
79 |
+
}
|
80 |
+
if ($options['converters'] == null) {
|
81 |
+
$options['converters'] = [];
|
82 |
+
}
|
83 |
+
|
84 |
+
// TODO: Save
|
85 |
+
//Messenger::addMessage('info', 'Options: <pre>' . print_r($options, true) . '</pre>');
|
86 |
+
// $htaccessExists = Config::doesHTAccessExists();
|
87 |
+
|
88 |
+
$config = $options;
|
89 |
+
|
90 |
+
//$htaccessExists = Config::doesHTAccessExists();
|
91 |
+
$rules = HTAccess::generateHTAccessRulesFromConfigObj($config);
|
92 |
+
|
93 |
+
if (Config::saveConfigurationFile($config)) {
|
94 |
+
$options = Config::generateWodOptionsFromConfigObj($config);
|
95 |
+
if (Config::saveWodOptionsFile($options)) {
|
96 |
+
|
97 |
+
//Config::saveConfigurationAndHTAccessFilesWithMessages($config, 'migrate');
|
98 |
+
$rulesResult = HTAccess::saveRules($config);
|
99 |
+
/*
|
100 |
+
'mainResult' // 'index', 'wp-content' or 'failed'
|
101 |
+
'minRequired' // 'index' or 'wp-content'
|
102 |
+
'pluginToo' // 'yes', 'no' or 'depends'
|
103 |
+
'pluginFailed' // true if failed to write to plugin folder (it only tries that, if pluginToo == 'yes')
|
104 |
+
'pluginFailedBadly' // true if plugin failed AND it seems we have rewrite rules there
|
105 |
+
'overidingRulesInWpContentWarning' // true if main result is 'index' but we cannot remove those in wp-content
|
106 |
+
'rules' // the rules that were generated
|
107 |
+
*/
|
108 |
+
$mainResult = $rulesResult['mainResult'];
|
109 |
+
$rules = $rulesResult['rules'];
|
110 |
+
|
111 |
+
if ($mainResult != 'failed') {
|
112 |
+
Messenger::addMessage(
|
113 |
+
'success',
|
114 |
+
'WebP Express has successfully migrated its configuration and updated the rewrite rules'
|
115 |
+
);
|
116 |
+
} else {
|
117 |
+
Messenger::addMessage(
|
118 |
+
'warning',
|
119 |
+
'WebP Express has successfully migrated its configuration.' .
|
120 |
+
'However, WebP Express could not update the rewrite rules<br>' .
|
121 |
+
'You need to change some permissions. Head to the ' .
|
122 |
+
'<a href="options-general.php?page=webp_express_settings_page">settings page</a> ' .
|
123 |
+
'and try to save the settings there (it will provide more information about the problem)'
|
124 |
+
);
|
125 |
+
}
|
126 |
+
} else {
|
127 |
+
Messenger::addMessage(
|
128 |
+
'error',
|
129 |
+
'For migration to 0.5.0, WebP Express failed saving options file. ' .
|
130 |
+
'You must grant us write access to your wp-config folder.<br>' .
|
131 |
+
'Tried to save to: "' . Paths::getWodOptionsFileName() . '"' .
|
132 |
+
'Fix the file permissions and reload<br>'
|
133 |
+
);
|
134 |
+
return false;
|
135 |
+
}
|
136 |
+
} else {
|
137 |
+
Messenger::addMessage(
|
138 |
+
'error',
|
139 |
+
'For migration to 0.5.0, WebP Express failed saving configuration file.<br>' .
|
140 |
+
'You must grant us write access to your wp-config folder.<br>' .
|
141 |
+
'Tried to save to: "' . Paths::getConfigFileName() . '"' .
|
142 |
+
'Fix the file permissions and reload<br>'
|
143 |
+
);
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
|
147 |
+
//saveConfigurationFile
|
148 |
+
//return $options;
|
149 |
+
return true;
|
150 |
+
}
|
151 |
+
|
152 |
+
function webpexpress_migrate1_deleteOldOptions() {
|
153 |
+
$optionsToDelete = [
|
154 |
+
'webp_express_max_quality',
|
155 |
+
'webp_express_image_types_to_convert',
|
156 |
+
'webp_express_failure_response',
|
157 |
+
'webp_express_converters',
|
158 |
+
'webp-express-inserted-rules-ok',
|
159 |
+
'webp-express-configured',
|
160 |
+
'webp-express-pending-messages',
|
161 |
+
'webp-express-just-activated',
|
162 |
+
'webp-express-message-pending',
|
163 |
+
'webp-express-failed-inserting-rules',
|
164 |
+
'webp-express-deactivate',
|
165 |
+
'webp_express_fail_action',
|
166 |
+
'webp_express_method',
|
167 |
+
'webp_express_quality'
|
168 |
+
|
169 |
+
];
|
170 |
+
foreach ($optionsToDelete as $i => $optionName) {
|
171 |
+
delete_option($optionName);
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
/* helper. Remove dir recursively. No warnings - fails silently */
|
176 |
+
function webpexpress_migrate1_rrmdir($dir) {
|
177 |
+
if (@is_dir($dir)) {
|
178 |
+
$objects = @scandir($dir);
|
179 |
+
foreach ($objects as $object) {
|
180 |
+
if ($object != "." && $object != "..") {
|
181 |
+
if (@is_dir($dir."/".$object))
|
182 |
+
webpexpress_migrate1_rrmdir($dir."/".$object);
|
183 |
+
else
|
184 |
+
@unlink($dir."/".$object);
|
185 |
+
}
|
186 |
+
}
|
187 |
+
@rmdir($dir);
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
function webpexpress_migrate1_deleteOldWebPImages() {
|
192 |
+
$upload_dir = wp_upload_dir();
|
193 |
+
$destinationRoot = trailingslashit($upload_dir['basedir']) . 'webp-express';
|
194 |
+
webpexpress_migrate1_rrmdir($destinationRoot);
|
195 |
+
}
|
196 |
+
|
197 |
+
if (webp_express_migrate1_createFolders()) {
|
198 |
+
if (webp_express_migrate1_createDummyConfigFiles()) {
|
199 |
+
if (webpexpress_migrate1_migrateOptions()) {
|
200 |
+
webpexpress_migrate1_deleteOldOptions();
|
201 |
+
webpexpress_migrate1_deleteOldWebPImages();
|
202 |
+
update_option('webp-express-migration-version', '1');
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
lib/options.php
DELETED
@@ -1,498 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// Maybe go away from using Settings API ?
|
4 |
-
// https://wpshout.com/wordpress-options-page/
|
5 |
-
|
6 |
-
|
7 |
-
include_once 'helpers.php';
|
8 |
-
|
9 |
-
/*
|
10 |
-
These lines should enable us to know whether quality can be detected.
|
11 |
-
But needs testing...
|
12 |
-
require WEBPEXPRESS_PLUGIN_DIR . '/vendor/require-webp-convert.php';
|
13 |
-
$detectedQualityOfTestJpg = \WebPConvert\Converters\ConverterHelper::detectQualityOfJpg(WEBPEXPRESS_PLUGIN_DIR . '/test/focus.jpg');
|
14 |
-
$canDetectQualityOfJpegs = ($detectedQualityOfTestJpg == 100);
|
15 |
-
*/
|
16 |
-
|
17 |
-
add_action('admin_enqueue_scripts', function () {
|
18 |
-
// https://github.com/RubaXa/Sortable
|
19 |
-
|
20 |
-
wp_register_script('sortable', plugins_url('../js/sortable.min.js', __FILE__), [], '1.9.0');
|
21 |
-
wp_enqueue_script('sortable');
|
22 |
-
|
23 |
-
wp_register_script(
|
24 |
-
'webp-express-options-page',
|
25 |
-
plugins_url('../js/webp-express-options-page.js', __FILE__),
|
26 |
-
['sortable'],
|
27 |
-
'0.2.0'
|
28 |
-
);
|
29 |
-
wp_enqueue_script('webp-express-options-page');
|
30 |
-
|
31 |
-
wp_add_inline_script('webp-express-options-page', 'window.webpExpressPaths = ' . json_encode(WebPExpressHelpers::calculateUrlsAndPaths()) . ';');
|
32 |
-
//wp_add_inline_script('webp-express-options-page', 'window.converters = [{"converter":"imagick","id":"imagick"},{"converter":"cwebp","id":"cwebp"},{"converter":"gd","id":"gd"}];');
|
33 |
-
|
34 |
-
//wp_add_inline_script('webp-express-options-page', 'window.converters = [{"converter":"imagick","id":"imagick"},{"converter":"cwebp","id":"cwebp"},{"converter":"gd","id":"gd"}];');
|
35 |
-
// ,{"converter":"wpc","options":{"url":"http://","secret":"banana"},"id":"wpc"}
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
wp_register_style(
|
40 |
-
'webp-express-options-page-css',
|
41 |
-
plugins_url('../css/webp-express-options-page.css', __FILE__),
|
42 |
-
null,
|
43 |
-
'0.2.0'
|
44 |
-
);
|
45 |
-
wp_enqueue_style('webp-express-options-page-css');
|
46 |
-
|
47 |
-
add_thickbox();
|
48 |
-
});
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
add_action('admin_init', 'webp_express_option_group_init');
|
53 |
-
|
54 |
-
|
55 |
-
function webp_express_option_group_init()
|
56 |
-
{
|
57 |
-
register_setting(
|
58 |
-
'webp_express_option_group', // A settings group name. Must exist prior to the register_setting call. This must match the group name in settings_fields()
|
59 |
-
'webp_express_max_quality', //The name of an option to sanitize and save.
|
60 |
-
[
|
61 |
-
'type' => 'integer',
|
62 |
-
'default' => '85',
|
63 |
-
'sanitize_callback' => 'sanitize_text_field',
|
64 |
-
]
|
65 |
-
);
|
66 |
-
register_setting(
|
67 |
-
'webp_express_option_group',
|
68 |
-
'webp_express_image_types_to_convert',
|
69 |
-
[
|
70 |
-
'type' => 'integer',
|
71 |
-
'default' => '1',
|
72 |
-
'sanitize_callback' => 'sanitize_text_field',
|
73 |
-
]
|
74 |
-
);
|
75 |
-
/*
|
76 |
-
register_setting(
|
77 |
-
'webp_express_option_group',
|
78 |
-
'webp_express_method',
|
79 |
-
[
|
80 |
-
'type' => 'integer',
|
81 |
-
'default' => '6',
|
82 |
-
'sanitize_callback' => 'sanitize_text_field',
|
83 |
-
]
|
84 |
-
);*/
|
85 |
-
register_setting(
|
86 |
-
'webp_express_option_group',
|
87 |
-
'webp_express_failure_response',
|
88 |
-
[
|
89 |
-
'type' => 'string',
|
90 |
-
'default' => 'original',
|
91 |
-
'sanitize_callback' => 'sanitize_text_field',
|
92 |
-
]
|
93 |
-
);
|
94 |
-
register_setting(
|
95 |
-
'webp_express_option_group',
|
96 |
-
'webp_express_converters',
|
97 |
-
[
|
98 |
-
'type' => 'string',
|
99 |
-
// TODO: test on new installation
|
100 |
-
'default' => '[{"converter":"cwebp","id":"cwebp"},{"converter":"wpc","id":"wpc"},{"converter":"gd","id":"gd"},{"converter":"imagick","id":"imagick"}]',
|
101 |
-
//'sanitize_callback' => 'sanitize_text_field',
|
102 |
-
]
|
103 |
-
);
|
104 |
-
|
105 |
-
|
106 |
-
add_settings_section('webp_express_conversion_options_section', 'Conversion options', function () {
|
107 |
-
//echo 'here you set conversion options';
|
108 |
-
}, 'webp_express_settings_page');
|
109 |
-
|
110 |
-
add_settings_field('webp_express_image_types_to_convert_id', 'Image types to convert', function () {
|
111 |
-
// bitmask
|
112 |
-
// 1: JPEGs
|
113 |
-
// 2: PNG's
|
114 |
-
// Converting only jpegs is thus "1"
|
115 |
-
// Converting both jpegs and pngs is (1+2) = 3
|
116 |
-
$imageTypes = get_option('webp_express_image_types_to_convert');
|
117 |
-
|
118 |
-
echo '<select name="webp_express_image_types_to_convert">';
|
119 |
-
echo '<option value="0"' . ($imageTypes == 0 ? ' selected' : '') . '>Do not convert any images!</option>';
|
120 |
-
echo '<option value="1"' . ($imageTypes == 1 ? ' selected' : '') . '>Only convert jpegs</option>';
|
121 |
-
echo '<option value="3"' . ($imageTypes == 3 ? ' selected' : '') . '>Convert both jpegs and pngs</option>';
|
122 |
-
echo '</select>';
|
123 |
-
|
124 |
-
//echo '<input type="checkbox" ' . ($types == 1 ? ' checked=checked' : '') . '>';
|
125 |
-
|
126 |
-
}, 'webp_express_settings_page', 'webp_express_conversion_options_section');
|
127 |
-
|
128 |
-
/*
|
129 |
-
add_settings_field('webp_express_method_id', 'Method (0-6)', function () {
|
130 |
-
$method = get_option('webp_express_method');
|
131 |
-
echo "<input type='text' name='webp_express_method' value='" . $method . "' />";
|
132 |
-
echo '<p>When higher values are used, the encoder will spend more time inspecting additional encoding possibilities and decide on the quality gain. Supported by cwebp, wpc and imagick</p>';
|
133 |
-
}, 'webp_express_settings_page', 'webp_express_conversion_options_section');
|
134 |
-
*/
|
135 |
-
|
136 |
-
add_settings_field('webp_express_failure_response', 'Response on failure', function () {
|
137 |
-
$failureResponse = get_option('webp_express_failure_response');
|
138 |
-
echo '<select name="webp_express_failure_response">';
|
139 |
-
echo '<option value="original"' . ($failureResponse == 'original' ? ' selected' : '') . '>Original image</option>';
|
140 |
-
echo '<option value="404"' . ($failureResponse == '404' ? ' selected' : '') . '>404</option>';
|
141 |
-
echo '<option value="report"' . ($failureResponse == 'report' ? ' selected' : '') . '>Error report (in plain text)</option>';
|
142 |
-
echo '<option value="report-as-image"' . ($failureResponse == 'report-as-image' ? ' selected' : '') . '>Error report as image</option>';
|
143 |
-
echo '</select>';
|
144 |
-
echo '<p>Determines what the converter should serve, in case the image conversion should fail. For production servers, recommended value is "Original image". For development servers, choose anything you like, but that</p>';
|
145 |
-
}, 'webp_express_settings_page', 'webp_express_conversion_options_section');
|
146 |
-
|
147 |
-
add_settings_field('webp_express_max_quality_id', 'Max quality (0-100)', function () {
|
148 |
-
echo "<input type='text' name='webp_express_max_quality' value='" . get_option('webp_express_max_quality') . "' />";
|
149 |
-
echo '<p>Converted jpeg images will get same quality as original, but not more than this setting. 85 is recommended for most websites.</p>';
|
150 |
-
}, 'webp_express_settings_page', 'webp_express_conversion_options_section');
|
151 |
-
|
152 |
-
|
153 |
-
/*
|
154 |
-
public static $CONVERTED_IMAGE = 1;
|
155 |
-
public static $ORIGINAL = -1;
|
156 |
-
public static $HTTP_404 = -2;
|
157 |
-
public static $REPORT_AS_IMAGE = -3;
|
158 |
-
public static $REPORT = -4;*/
|
159 |
-
|
160 |
-
add_settings_field('webp_express_converters', '', function () {
|
161 |
-
$converters = get_option('webp_express_converters');
|
162 |
-
echo '<script>window.converters = ' . get_option('webp_express_converters') . '</script>';
|
163 |
-
|
164 |
-
echo "<input type='text' name='webp_express_converters' value='' />";
|
165 |
-
}, 'webp_express_settings_page', 'webp_express_conversion_options_section');
|
166 |
-
}
|
167 |
-
|
168 |
-
|
169 |
-
/* Settings Page Content */
|
170 |
-
function webp_express_settings_page_content()
|
171 |
-
{
|
172 |
-
if (!current_user_can('manage_options')) {
|
173 |
-
wp_die(__('You do not have sufficient permissions to access this page.', 'yasr'));
|
174 |
-
}
|
175 |
-
?>
|
176 |
-
<div class="wrap">
|
177 |
-
<h2>WebP Express Settings</h2>
|
178 |
-
|
179 |
-
<?php
|
180 |
-
|
181 |
-
global $wpdb;
|
182 |
-
//$hasWebPExpressOptionBeenSaved = ($wpdb->get_row( "SELECT * FROM $wpdb->options WHERE option_name = 'webp_express_converters'" ) !== null);
|
183 |
-
//if (!$hasWebPExpressOptionBeenSaved) {
|
184 |
-
if (empty(get_option('webp-express-configured'))) {
|
185 |
-
echo '<div style="background-color: #cfc; padding: 20px; border: 1px solid #ccc">';
|
186 |
-
echo '<h3>Welcome!<h3>';
|
187 |
-
echo '<p>The rewrite rules are not active yet. They will be activated the first time you click the "Save settings" button.</p>';
|
188 |
-
echo '<p>Before you do that, I suggest you find out which converters that works. Start from the top. Click "test" next to a converter to test it. Try also clicking the "configure" buttons</p>';
|
189 |
-
echo '</div>';
|
190 |
-
}
|
191 |
-
|
192 |
-
global $wpdb;
|
193 |
-
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}options WHERE option_id = 1", OBJECT );
|
194 |
-
?>
|
195 |
-
<form action="options.php" method="post">
|
196 |
-
<?php
|
197 |
-
settings_fields('webp_express_option_group');
|
198 |
-
do_settings_sections('webp_express_settings_page');
|
199 |
-
|
200 |
-
//print_r(get_option('plugin_error'));
|
201 |
-
|
202 |
-
//echo '<pre>' . print_r(WebPExpressHelpers::calculateUrlsAndPaths(), true) . '</pre>';
|
203 |
-
$localConverters = ['cwebp', 'imagick', 'gd'];
|
204 |
-
|
205 |
-
/*
|
206 |
-
$testResult = WebPExpressHelpers::testConverters($localConverters);
|
207 |
-
//print_r($testResult);
|
208 |
-
|
209 |
-
if ($testResult['numOperationalConverters'] == 0) {
|
210 |
-
echo 'Unfortunately, your server is currently not able to convert to webp files by itself. You will need to set up a cloud converter.<br><br>';
|
211 |
-
foreach ($testResult['results'] as $result) {
|
212 |
-
echo $result['converter'] . ':' . $result['message'] . '<br>';
|
213 |
-
}
|
214 |
-
} else {
|
215 |
-
//echo 'Your server is able to convert webp files by itself.';
|
216 |
-
}
|
217 |
-
if ($testResult['numOperationalConverters'] == 1) {
|
218 |
-
//
|
219 |
-
}
|
220 |
-
*/
|
221 |
-
$extraConverters = [
|
222 |
-
[
|
223 |
-
'converter' => 'ewww',
|
224 |
-
'options' => array(
|
225 |
-
'key' => 'your api key here',
|
226 |
-
),
|
227 |
-
]
|
228 |
-
];
|
229 |
-
|
230 |
-
$converters = [
|
231 |
-
[
|
232 |
-
'converter' => 'cwebp',
|
233 |
-
],
|
234 |
-
[
|
235 |
-
'converter' => 'imagick',
|
236 |
-
],
|
237 |
-
[
|
238 |
-
'converter' => 'gd',
|
239 |
-
],
|
240 |
-
[
|
241 |
-
'converter' => 'ewww',
|
242 |
-
'options' => [
|
243 |
-
'key' => 'your api key here',
|
244 |
-
],
|
245 |
-
]
|
246 |
-
];
|
247 |
-
|
248 |
-
/*
|
249 |
-
http://php.net/manual/en/function.set-include-path.php
|
250 |
-
|
251 |
-
|
252 |
-
//exec('/usr/sbin/getsebool -a', $output6, $returnCode5); // ok
|
253 |
-
//echo 'All se bools: ' . print_r($output6, true) . '. Return code:' . $returnCode5;
|
254 |
-
*/
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
echo '<h2>Converters</h2>';
|
261 |
-
$dragIcon = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="17px" height="17px" viewBox="0 0 100.000000 100.000000" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,100.000000) scale(0.100000,-0.100000)" fill="#444444" stroke="none"><path d="M415 920 l-80 -80 165 0 165 0 -80 80 c-44 44 -82 80 -85 80 -3 0 -41 -36 -85 -80z"/><path d="M0 695 l0 -45 500 0 500 0 0 45 0 45 -500 0 -500 0 0 -45z"/><path d="M0 500 l0 -40 500 0 500 0 0 40 0 40 -500 0 -500 0 0 -40z"/><path d="M0 305 l0 -45 500 0 500 0 0 45 0 45 -500 0 -500 0 0 -45z"/><path d="M418 78 l82 -83 82 83 83 82 -165 0 -165 0 83 -82z"/></g></svg>';
|
262 |
-
|
263 |
-
echo '<p><i>Drag to reorder. The converter on top will be used. Should it fail, the next will be used, etc</i></p>';
|
264 |
-
// https://github.com/RubaXa/Sortable
|
265 |
-
|
266 |
-
// Empty list of converters. The list will be populated by the javascript
|
267 |
-
echo '<ul id="converters"></ul>';
|
268 |
-
?>
|
269 |
-
<div id="cwebp" style="display:none;">
|
270 |
-
<div class="cwebp converter-options">
|
271 |
-
<h3>cwebp</h3>
|
272 |
-
<div class="info">
|
273 |
-
cwebp works by executing the cwebp binary from Google. This should normally be your first choice.
|
274 |
-
Its best in terms of quality, speed and options.
|
275 |
-
The only catch is that it requires the exec function to be enabled, and that the webserver user is
|
276 |
-
allowed to execute the cwebp binary (either at known system locations, or one of the precompiled binaries,
|
277 |
-
that comes with this library).
|
278 |
-
If you are on a shared host that doesn't allow that, the second best choice would probably be the wpc cloud converter.
|
279 |
-
</div>
|
280 |
-
<h3>cweb options</h3>
|
281 |
-
<div>
|
282 |
-
<label for="cwebp_use_nice">Use nice</label>
|
283 |
-
<input type="checkbox" id="cwebp_use_nice">
|
284 |
-
<br>Enabling "use nice" saves system resources at the cost of slightly slower conversion
|
285 |
-
|
286 |
-
</div>
|
287 |
-
<br>
|
288 |
-
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
289 |
-
<!-- <a href="javascript: tb_remove();">close</a> -->
|
290 |
-
</div>
|
291 |
-
</div>
|
292 |
-
<div id="gd" style="display:none;">
|
293 |
-
<div class="ewww converter-options">
|
294 |
-
<h3>Gd</h3>
|
295 |
-
<p>
|
296 |
-
The gd converter uses the Gd extension to do the conversion. It is per default placed below the cloud converters for two reasons.
|
297 |
-
Firstly, it does not seem to produce quite as good quality as cwebp.
|
298 |
-
Secondly, it provides no conversion options, besides quality.
|
299 |
-
The Gd extension is pretty common, so the main feature of this converter is that it <i>may</i> work out of the box.
|
300 |
-
This is in contrast to the cloud converters, which requires that the user does some setup.
|
301 |
-
</p>
|
302 |
-
<h3>Gd options</h3>
|
303 |
-
<div class="info">
|
304 |
-
Gd neither supports copying metadata nor exposes any WebP options. Lacking the option to set lossless encoding results in poor encoding of PNGs - the filesize is generally much larger than the original. For this reason, the converter defaults to skip PNG's.
|
305 |
-
</div>
|
306 |
-
<div>
|
307 |
-
<label for="gd_skip_pngs">Skip PNGs</label>
|
308 |
-
<input type="checkbox" id="gd_skip_pngs">
|
309 |
-
</div>
|
310 |
-
<br>
|
311 |
-
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
312 |
-
<!-- <a href="javascript: tb_remove();">close</a> -->
|
313 |
-
</div>
|
314 |
-
</div>
|
315 |
-
<div id="imagick" style="display:none;">
|
316 |
-
<div class="imagick converter-options">
|
317 |
-
<h3>Imagick</h3>
|
318 |
-
<p>
|
319 |
-
imagick would be your last choice. For some reason it produces conversions that are only marginally better than the originals.
|
320 |
-
See <a href="https://github.com/rosell-dk/webp-convert/issues/43" target="_blank">this issue</a>. But it is fast.
|
321 |
-
</p>
|
322 |
-
<h3>Imagick options</h3>
|
323 |
-
<div class="info">
|
324 |
-
imagick has no extra options.
|
325 |
-
</div>
|
326 |
-
<br>
|
327 |
-
<!--
|
328 |
-
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
329 |
-
-->
|
330 |
-
<!-- <a href="javascript: tb_remove();">close</a> -->
|
331 |
-
</div>
|
332 |
-
</div>
|
333 |
-
<div id="ewww" style="display:none;">
|
334 |
-
<div class="ewww converter-options">
|
335 |
-
<h3>Ewww</h3>
|
336 |
-
<p>
|
337 |
-
<a href="https://ewww.io/" target="_blank">ewww</a> is a cloud service.
|
338 |
-
It is a decent alternative for those who don't have the technical know-how to install wpc.
|
339 |
-
ewww is using cwebp to do the conversion, so quality is great.
|
340 |
-
ewww however only provides one conversion option (quality), and it does not support "auto"
|
341 |
-
quality (yet - I have requested the feature and the maintainer are considering it).
|
342 |
-
Also, it is not free. But very cheap. Like in almost free.
|
343 |
-
</p>
|
344 |
-
<h3>Ewww options</h3>
|
345 |
-
<div>
|
346 |
-
<label for="ewww_key">Key</label>
|
347 |
-
<input type="text" id="ewww_key" placeholder="Your API key here">
|
348 |
-
</div>
|
349 |
-
<br>
|
350 |
-
<h4>Fallback (optional)</h4>
|
351 |
-
<div>
|
352 |
-
<label for="ewww_key_2">key</label>
|
353 |
-
<input type="text" id="ewww_key_2" placeholder="In case the first one expires...">
|
354 |
-
</div>
|
355 |
-
<br>
|
356 |
-
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
357 |
-
<!-- <a href="javascript: tb_remove();">close</a> -->
|
358 |
-
</div>
|
359 |
-
</div>
|
360 |
-
<div id="wpc" style="display:none;">
|
361 |
-
<div class="wpc converter-options">
|
362 |
-
<h3>WebPConvert Cloud Service</h3>
|
363 |
-
wpc is an open source cloud converter based on <a href="https://github.com/rosell-dk/webp-convert" target="_blank">WebPConvert</a>
|
364 |
-
(this plugin also happens to be based on WebPConvert).
|
365 |
-
Conversions will of course be slower than cwebp, as images need to go back and forth to the cloud converter.
|
366 |
-
As images usually just needs to be converted once, the slower conversion
|
367 |
-
speed is probably acceptable. The conversion quality and options of wpc matches cwebp.
|
368 |
-
The only catch is that you will need to install the WPC library on a server (or have someone do it for you).
|
369 |
-
<a href="https://github.com/rosell-dk/webp-convert-cloud-service" target="blank">Visit WPC on github</a>.
|
370 |
-
If this is a problem, we suggest you turn to ewww.
|
371 |
-
(PS: A Wordpress plugin is planned, making it easier to set up a WPC instance. Or perhaps the functionality will even be part of this plugin)
|
372 |
-
|
373 |
-
<h3>Options for WebPConvert Cloud Service</h3>
|
374 |
-
<div>
|
375 |
-
<label for="wpc_url">URL</label>
|
376 |
-
<input type="text" id="wpc_url" placeholder="Url to your WPC instance">
|
377 |
-
</div>
|
378 |
-
|
379 |
-
<div>
|
380 |
-
<label for="wpc_secret">Secret</label>
|
381 |
-
<input type="text" id="wpc_secret" placeholder="Secret (must match secret on server side)">
|
382 |
-
</div>
|
383 |
-
<br>
|
384 |
-
<h4>Fallback (optional)</h4>
|
385 |
-
<p>In case the first is down, the fallback will be used.</p>
|
386 |
-
<div>
|
387 |
-
<label for="wpc_url_2">URL</label>
|
388 |
-
<input type="text" id="wpc_url_2" placeholder="Url to your other WPC instance">
|
389 |
-
</div>
|
390 |
-
<div>
|
391 |
-
<label for="wpc_secret_2">Secret</label>
|
392 |
-
<input type="text" id="wpc_secret_2" placeholder="Secret (must match secret on server side)">
|
393 |
-
</div>
|
394 |
-
<br>
|
395 |
-
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
396 |
-
</div>
|
397 |
-
</div>
|
398 |
-
<?php
|
399 |
-
//print_r($urls);
|
400 |
-
//echo $urls['urls']['webpExpressRoot'];
|
401 |
-
if (isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false )) {
|
402 |
-
echo 'Your browser supports webp... So you can test if everything works (including the redirect magic) - using these links:<br>';
|
403 |
-
$webpExpressRoot = WebPExpressHelpers::calculateUrlsAndPaths()['urls']['webpExpressRoot'];
|
404 |
-
echo '<a href="' . $webpExpressRoot . '/test/test.jpg" target="_blank">Convert test image</a><br>';
|
405 |
-
echo '<a href="' . $webpExpressRoot . '/test/test.jpg?debug" target="_blank">Convert test image (show debug)</a><br>';
|
406 |
-
}
|
407 |
-
?>
|
408 |
-
|
409 |
-
<!--
|
410 |
-
<div id="add-cloud-converter-id" style="display:none;">
|
411 |
-
<p>
|
412 |
-
Select cloud converter to add:
|
413 |
-
|
414 |
-
<button onclick="addConverter('ewww')" class="button button-primary" type="button">Add ewww converter</button>
|
415 |
-
</p>
|
416 |
-
</div>
|
417 |
-
<button class="button button-secondary" onclick="addConverterClick()" type="button">Add cloud converter</button>
|
418 |
-
-->
|
419 |
-
<?php
|
420 |
-
|
421 |
-
|
422 |
-
submit_button('Save settings');
|
423 |
-
?>
|
424 |
-
</form>
|
425 |
-
</div>
|
426 |
-
|
427 |
-
<?php
|
428 |
-
}
|
429 |
-
|
430 |
-
// This hook is invoked when a option is changed away from default value
|
431 |
-
add_action('added_option', function($option_name, $value) {
|
432 |
-
|
433 |
-
// Notice that we use underscore in "webp_express" for the configuration, but dash for other options (such as messages and state)
|
434 |
-
if (strpos($option_name, 'webp_express') === 0) {
|
435 |
-
|
436 |
-
// Store the fact that webp options has been changed.
|
437 |
-
// When nobody is using 0.3 or below, we can test on the existence of that option, instead of
|
438 |
-
// querying the database directly ($hasWebPExpressOptionBeenSaved)
|
439 |
-
add_option('webp-express-configured', true);
|
440 |
-
|
441 |
-
$rules = WebPExpressHelpers::generateHTAccessRules();
|
442 |
-
WebPExpressHelpers::insertHTAccessRules($rules);
|
443 |
-
}
|
444 |
-
}, 10, 3);
|
445 |
-
|
446 |
-
// This hook is invoked when a option is changed (but not when the old value is the same as its default value)
|
447 |
-
add_action('updated_option', function($option_name, $old_value, $value) {
|
448 |
-
|
449 |
-
// Notice that we use underscore in "webp_express" for the configuration, but dash for other options (such as messages and state)
|
450 |
-
if (strpos($option_name, 'webp_express') === 0) {
|
451 |
-
$rules = WebPExpressHelpers::generateHTAccessRules();
|
452 |
-
WebPExpressHelpers::insertHTAccessRules($rules);
|
453 |
-
}
|
454 |
-
}, 10, 3);
|
455 |
-
|
456 |
-
|
457 |
-
//End webp_express_settings_page_content
|
458 |
-
|
459 |
-
//include( plugin_dir_path( __FILE__ ) . 'lib/helpers.php');
|
460 |
-
|
461 |
-
//echo '<pre>rules:' . WebPExpressHelpers::generateHTAccessRules() . '</pre>';
|
462 |
-
|
463 |
-
/*
|
464 |
-
add_action('admin_menu', function () {
|
465 |
-
add_options_page('WebP Express', 'WebP Express', 'manage_options', 'webp-express', function () {
|
466 |
-
include(plugin_dir_path(__FILE__) . 'lib/options.php');
|
467 |
-
});
|
468 |
-
});
|
469 |
-
|
470 |
-
function d1() {
|
471 |
-
echo '<p>Main description of this section here.</p>';
|
472 |
-
}
|
473 |
-
function d2() {
|
474 |
-
$options = get_option('options');
|
475 |
-
echo "<input id='plugin_text_string' name='plugin_options[text_string]' size='40' type='text' value='{$options['text_string']}' />";
|
476 |
-
}
|
477 |
-
function plugin_options_validate($input) {
|
478 |
-
$newinput['text_string'] = trim($input['text_string']);
|
479 |
-
if (!preg_match('/^[a-z0-9]{32}$/i', $newinput['text_string'])) {
|
480 |
-
$newinput['text_string'] = '';
|
481 |
-
}
|
482 |
-
return $newinput;
|
483 |
-
}
|
484 |
-
|
485 |
-
add_action('admin_init', function () {
|
486 |
-
register_setting('general', 'quality', [
|
487 |
-
'type' => 'string',
|
488 |
-
'default' => '85'
|
489 |
-
]);
|
490 |
-
add_settings_field('plugin_text_string', 'Plugin Text Input', function () {
|
491 |
-
echo 'hello...';
|
492 |
-
}, 'webp-express');
|
493 |
-
|
494 |
-
//register_setting('webp_express_options', 'options', 'plugin_options_validate');
|
495 |
-
//add_settings_section('webp_express_main', 'Main Settings', d1, 'webp_express');
|
496 |
-
//add_settings_field('plugin_text_string', 'Plugin Text Input', d2, 'webp_express', 'webp_express_main');
|
497 |
-
});
|
498 |
-
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{css → lib/options/css}/webp-express-options-page.css
RENAMED
@@ -1,13 +1,23 @@
|
|
1 |
-
input[name=webp_express_converters] {
|
2 |
display: none;
|
3 |
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
}
|
5 |
|
|
|
|
|
|
|
6 |
/* remove the padding on the row with the hidden "webp_express_converters" setting.
|
7 |
it is the last row, we happen to know... */
|
|
|
8 |
.form-table tr:last-of-type > * {
|
9 |
padding: 0;
|
10 |
-
}
|
11 |
|
12 |
|
13 |
#converters li {
|
1 |
+
/*input[name=webp_express_converters] {
|
2 |
display: none;
|
3 |
width: 100%;
|
4 |
+
}*/
|
5 |
+
|
6 |
+
/* break long lines in pre (when we are showing .htaccess inside notice) */
|
7 |
+
.notice pre {
|
8 |
+
white-space: pre-wrap;
|
9 |
+
word-wrap: break-word;
|
10 |
}
|
11 |
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
/* remove the padding on the row with the hidden "webp_express_converters" setting.
|
16 |
it is the last row, we happen to know... */
|
17 |
+
/*
|
18 |
.form-table tr:last-of-type > * {
|
19 |
padding: 0;
|
20 |
+
}*/
|
21 |
|
22 |
|
23 |
#converters li {
|
lib/options/enqueue_scripts.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/../classes/Paths.php';
|
4 |
+
use \WebPExpress\Paths;
|
5 |
+
|
6 |
+
wp_register_script('sortable', plugins_url('js/sortable.min.js', __FILE__), [], '1.9.0');
|
7 |
+
wp_enqueue_script('sortable');
|
8 |
+
|
9 |
+
wp_register_script(
|
10 |
+
'webp-express-options-page',
|
11 |
+
plugins_url('js/webp-express-options-page.js', __FILE__),
|
12 |
+
['sortable'],
|
13 |
+
'0.5.1'
|
14 |
+
);
|
15 |
+
wp_enqueue_script('webp-express-options-page');
|
16 |
+
|
17 |
+
if (function_exists('wp_add_inline_script')) {
|
18 |
+
// wp_add_inline_script is available from Wordpress 4.5
|
19 |
+
wp_add_inline_script('webp-express-options-page', 'window.webpExpressPaths = ' . json_encode(Paths::getUrlsAndPathsForTheJavascript()) . ';');
|
20 |
+
} else {
|
21 |
+
echo '<script>window.webpExpressPaths = ' . json_encode(Paths::getUrlsAndPathsForTheJavascript()) . ';</script>';
|
22 |
+
}
|
23 |
+
|
24 |
+
wp_register_style(
|
25 |
+
'webp-express-options-page-css',
|
26 |
+
plugins_url('css/webp-express-options-page.css', __FILE__),
|
27 |
+
null,
|
28 |
+
'0.5.1'
|
29 |
+
);
|
30 |
+
wp_enqueue_style('webp-express-options-page-css');
|
31 |
+
|
32 |
+
add_thickbox();
|
{images → lib/options/images}/drag-reorder.svg
RENAMED
File without changes
|
{js → lib/options/js}/sortable.min.js
RENAMED
File without changes
|
{js → lib/options/js}/webp-express-options-page.js
RENAMED
@@ -1,34 +1,4 @@
|
|
1 |
|
2 |
-
/*window.converters = [
|
3 |
-
{
|
4 |
-
'converter': 'imagick',
|
5 |
-
},
|
6 |
-
{
|
7 |
-
'converter': 'cwebp',
|
8 |
-
},
|
9 |
-
{
|
10 |
-
'converter': 'gd',
|
11 |
-
},
|
12 |
-
{
|
13 |
-
'converter': 'ewww',
|
14 |
-
'options': {
|
15 |
-
'key': 'your api key here',
|
16 |
-
},
|
17 |
-
},
|
18 |
-
{
|
19 |
-
'converter': 'ewww',
|
20 |
-
'options': {
|
21 |
-
'key': 'your api key here 2',
|
22 |
-
},
|
23 |
-
},
|
24 |
-
{
|
25 |
-
'converter': 'wpc',
|
26 |
-
'options': {
|
27 |
-
'url': 'http://',
|
28 |
-
'secret': 'banana',
|
29 |
-
},
|
30 |
-
}
|
31 |
-
];*/
|
32 |
|
33 |
// Map of converters (are updated with updateConvertersMap)
|
34 |
window.convertersMap = {};
|
@@ -98,17 +68,6 @@ html += '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox=
|
|
98 |
html += '<a class="deactivate-converter btn" onclick=deactivateConverter(\'' + converter['id'] + '\')>deactivate</a>';
|
99 |
}
|
100 |
|
101 |
-
/*
|
102 |
-
switch (converter['converter']) {
|
103 |
-
case 'ewww':
|
104 |
-
case 'wpc':
|
105 |
-
html += '<a class="remove-converter btn" onclick=removeConverter(\'' + converter['id'] + '\')>remove</a>';
|
106 |
-
break;
|
107 |
-
case 'cwebp':
|
108 |
-
case 'imagick':
|
109 |
-
case 'gd':
|
110 |
-
break;
|
111 |
-
}*/
|
112 |
html += '</li>';
|
113 |
return html;
|
114 |
}
|
@@ -154,7 +113,7 @@ function reorderConverters(order) {
|
|
154 |
|
155 |
/* Update the hidden input containing all the data */
|
156 |
function updateInputValue() {
|
157 |
-
document.getElementsByName('
|
158 |
}
|
159 |
|
160 |
function setConvertersHTML() {
|
@@ -276,7 +235,8 @@ function testConverter(id) {
|
|
276 |
var urls = window.webpExpressPaths['urls'];
|
277 |
var paths = window.webpExpressPaths['filePaths'];
|
278 |
|
279 |
-
var url = urls['webpExpressRoot'] + '/test-run.php';
|
|
|
280 |
|
281 |
|
282 |
// test images here: http://nottinghamtec.co.uk/~aer/TestPatterns/1080/
|
@@ -287,10 +247,9 @@ function testConverter(id) {
|
|
287 |
filename = 'focus.jpg';
|
288 |
|
289 |
url += '?source=' + paths['webpExpressRoot'] + '/test/' + filename;
|
290 |
-
url += '&destination=' + paths['destinationRoot'] + '/' + filename + '.webp';
|
291 |
-
url += '&destinationUrl=' + urls['destinationRoot'] + '/' + filename + '.webp';
|
292 |
url += '&converter=' + converter['converter'];
|
293 |
-
url += '&max-quality=' + document.getElementsByName('
|
294 |
//url += '&method=' + document.getElementsByName('webp_express_method')[0].value;
|
295 |
|
296 |
if (converter.options) {
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
// Map of converters (are updated with updateConvertersMap)
|
4 |
window.convertersMap = {};
|
68 |
html += '<a class="deactivate-converter btn" onclick=deactivateConverter(\'' + converter['id'] + '\')>deactivate</a>';
|
69 |
}
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
html += '</li>';
|
72 |
return html;
|
73 |
}
|
113 |
|
114 |
/* Update the hidden input containing all the data */
|
115 |
function updateInputValue() {
|
116 |
+
document.getElementsByName('converters')[0].value = JSON.stringify(window.converters);
|
117 |
}
|
118 |
|
119 |
function setConvertersHTML() {
|
235 |
var urls = window.webpExpressPaths['urls'];
|
236 |
var paths = window.webpExpressPaths['filePaths'];
|
237 |
|
238 |
+
var url = '/' + urls['webpExpressRoot'] + '/test/test-run.php';
|
239 |
+
//alert(url);
|
240 |
|
241 |
|
242 |
// test images here: http://nottinghamtec.co.uk/~aer/TestPatterns/1080/
|
247 |
filename = 'focus.jpg';
|
248 |
|
249 |
url += '?source=' + paths['webpExpressRoot'] + '/test/' + filename;
|
250 |
+
url += '&destination=' + paths['destinationRoot'] + '/test-conversions/' + filename + '.webp';
|
|
|
251 |
url += '&converter=' + converter['converter'];
|
252 |
+
url += '&max-quality=' + document.getElementsByName('max-quality')[0].value;
|
253 |
//url += '&method=' + document.getElementsByName('webp_express_method')[0].value;
|
254 |
|
255 |
if (converter.options) {
|
lib/options/options-hooks.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
add_action( 'admin_menu', function() {
|
4 |
+
|
5 |
+
//Add Settings Page
|
6 |
+
add_options_page(
|
7 |
+
'WebP Express Settings', //Page Title
|
8 |
+
'WebP Express', //Menu Title
|
9 |
+
'manage_options', //capability
|
10 |
+
'webp_express_settings_page', //menu slug
|
11 |
+
'webp_express_settings_page_content' //The function to be called to output the content for this page.
|
12 |
+
);
|
13 |
+
});
|
14 |
+
|
15 |
+
add_action('admin_post_webpexpress_settings_submit', function() {
|
16 |
+
include __DIR__ . '/submit.php';
|
17 |
+
});
|
18 |
+
|
19 |
+
global $pagenow;
|
20 |
+
if (($pagenow == 'options-general.php') && ($_GET['page'] == 'webp_express_settings_page')) {
|
21 |
+
add_action('admin_enqueue_scripts', function () {
|
22 |
+
include __DIR__ . '/enqueue_scripts.php';
|
23 |
+
});
|
24 |
+
}
|
25 |
+
|
26 |
+
function webp_express_settings_page_content()
|
27 |
+
{
|
28 |
+
include __DIR__ . '/page.php';
|
29 |
+
}
|
lib/options/page-messages.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use \WebPExpress\Paths;
|
4 |
+
use \WebPExpress\HTAccess;
|
5 |
+
use \WebPExpress\Config;
|
6 |
+
use \WebPExpress\State;
|
7 |
+
use \WebPExpress\Messenger;
|
8 |
+
use \WebPExpress\PlatformInfo;
|
9 |
+
use \WebPExpress\FileHelper;
|
10 |
+
|
11 |
+
if ((!State::getState('configured', false))) {
|
12 |
+
include __DIR__ . "/page-welcome.php";
|
13 |
+
}
|
14 |
+
|
15 |
+
if (PlatformInfo::definitelyNotGotModRewrite()) {
|
16 |
+
Messenger::printMessage(
|
17 |
+
'error',
|
18 |
+
"Rewriting isn't enabled on your server. WebP Express cannot work without it. Tell your host or system administrator to enable the 'mod_rewrite' module. If you are on a shared host, chances are that mod_rewrite can be turned on in your control panel."
|
19 |
+
);
|
20 |
+
}
|
21 |
+
/*
|
22 |
+
if (Config::isConfigFileThereAndOk() ) { // && PlatformInfo::definitelyGotModEnv()
|
23 |
+
if (!isset($_SERVER['HTACCESS'])) {
|
24 |
+
Messenger::printMessage(
|
25 |
+
'warning',
|
26 |
+
"Using rewrite rules in <i>.htaccess</i> files seems to be disabled " .
|
27 |
+
"(The <i>AllowOverride</i> directive is probably set to <i>None</i>. " .
|
28 |
+
"It needs to be set to <i>All</i>, or at least <i>FileInfo</i> to allow rewrite rules in <i>.htaccess</i> files.)<br>" .
|
29 |
+
"Disabled <i>.htaccess</i> files is actually a good thing, both performance-wise and security-wise. <br> " .
|
30 |
+
"But it means you will have to insert the following rules into your apache configuration manually:" .
|
31 |
+
"<pre>" . htmlentities(print_r(Config::generateHTAccessRulesFromConfigFile(), true)) . "</pre>"
|
32 |
+
);
|
33 |
+
}
|
34 |
+
}*/
|
35 |
+
if (!Paths::createContentDirIfMissing()) {
|
36 |
+
Messenger::printMessage(
|
37 |
+
'error',
|
38 |
+
'WebP Express needs to create a directory "webp-express" under your wp-content folder, but does not have permission to do so.<br>' .
|
39 |
+
'Please create the folder manually, or change the file permissions of your wp-content folder.'
|
40 |
+
);
|
41 |
+
} else {
|
42 |
+
if (!Paths::createConfigDirIfMissing()) {
|
43 |
+
Messenger::printMessage(
|
44 |
+
'error',
|
45 |
+
'WebP Express needs to create a directory "webp-express/config" under your wp-content folder, but does not have permission to do so.<br>' .
|
46 |
+
'Please create the folder manually, or change the file permissions.'
|
47 |
+
);
|
48 |
+
}
|
49 |
+
|
50 |
+
if (!Paths::createCacheDirIfMissing()) {
|
51 |
+
Messenger::printMessage(
|
52 |
+
'error',
|
53 |
+
'WebP Express needs to create a directory "webp-express/webp-images" under your wp-content folder, but does not have permission to do so.<br>' .
|
54 |
+
'Please create the folder manually, or change the file permissions.'
|
55 |
+
);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
if (Config::isConfigFileThere()) {
|
60 |
+
if (!Config::isConfigFileThereAndOk()) {
|
61 |
+
Messenger::printMessage(
|
62 |
+
'warning',
|
63 |
+
'Warning: The configuration file is not ok! (cant be read, or not valid json).<br>' .
|
64 |
+
'file: "' . Paths::getConfigFileName() . '"'
|
65 |
+
);
|
66 |
+
} else {
|
67 |
+
if (HTAccess::arePathsUsedInHTAccessOutdated()) {
|
68 |
+
Messenger::printMessage(
|
69 |
+
'warning',
|
70 |
+
'Warning: Wordpress paths have changed since the last time the Rewrite Rules was generated. The rules needs updating! (click <i>Save settings</i> to do so)<br>'
|
71 |
+
);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
if (
|
77 |
+
HTAccess::haveWeRulesInThisHTAccessBestGuess(Paths::getIndexDirAbs() . '/.htaccess') &&
|
78 |
+
HTAccess::haveWeRulesInThisHTAccessBestGuess(Paths::getWPContentDirAbs() . '/.htaccess')
|
79 |
+
) {
|
80 |
+
if (!HTAccess::saveHTAccessRulesToFile(Paths::getIndexDirAbs() . '/.htaccess', '# WebP Express has placed its rules in your wp-content dir. Go there.', false)) {
|
81 |
+
Messenger::printMessage(
|
82 |
+
'warning',
|
83 |
+
'Warning: WebP Express have rules in both your wp-content folder and in your Wordpress folder.<br>' .
|
84 |
+
'Please remove those in the <i>.htaccess</i> in your Wordress folder manually, or let us handle it, by granting us write access'
|
85 |
+
);
|
86 |
+
}
|
87 |
+
}
|
lib/options/page-welcome.php
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use \WebPExpress\Paths;
|
4 |
+
use \WebPExpress\Config;
|
5 |
+
use \WebPExpress\State;
|
6 |
+
use \WebPExpress\Messenger;
|
7 |
+
use \WebPExpress\PlatformInfo;
|
8 |
+
use \WebPExpress\FileHelper;
|
9 |
+
|
10 |
+
$indexDir = Paths::getIndexDirAbs();
|
11 |
+
$homeDir = Paths::getHomeDirAbs();
|
12 |
+
$wpContentDir = Paths::getWPContentDirAbs();
|
13 |
+
$pluginDir = Paths::getPluginDirAbs();
|
14 |
+
$uploadDir = Paths::getUploadDirAbs();
|
15 |
+
|
16 |
+
echo '<div style="background-color: #cfc; padding: 20px; border: 1px solid #ccc; color: black">';
|
17 |
+
echo '<h3>Welcome!</h3>';
|
18 |
+
echo '<p>The rewrite rules are not active yet. They will be activated the first time you click the "Save settings" button.</p>';
|
19 |
+
echo '<p>Before you do that, I suggest you find out which converters that works. Start from the top. Click "test" next to a converter to test it. Try also clicking the "configure" buttons</p>';
|
20 |
+
|
21 |
+
|
22 |
+
if (Paths::isWPContentDirMovedOutOfAbsPath()) {
|
23 |
+
if (!Paths::canWriteHTAccessRulesHere($wpContentDir)) {
|
24 |
+
echo '<p><b>Oh, one more thing</b>. Unless you are going to put the rewrite rules into your configuration manually, ';
|
25 |
+
echo '<i>WebP Express</i> would be needing to store the rewrite rules in a <i>.htaccess</i> file in your <i>wp-content</i> directory ';
|
26 |
+
echo '(we need to store them there rather than in your root, because you have moved your wp-content folder out of the Wordpress root). ';
|
27 |
+
echo 'Please adjust the file permissions of your <i>wp-content</i> dir. ';
|
28 |
+
|
29 |
+
if (Paths::isPluginDirMovedOutOfWpContent()) {
|
30 |
+
echo '<br>But that is not all. Besides moving your wp-content dir, you have <i>also</i> moved your plugin dir... ';
|
31 |
+
echo 'If you want WebP-Express to work on the images delivered by your plugins, you must also grant write access to your plugin dir (you can revoke the access after we have written the rules).<br>';
|
32 |
+
}
|
33 |
+
echo 'You can reload this page aftewards, and this message should be gone</p>';
|
34 |
+
} else {
|
35 |
+
if (Paths::isPluginDirMovedOutOfWpContent()) {
|
36 |
+
echo '<p><b>Oh, one more thing</b>. I can see that your plugin dir has been moved out of your wp-content folder. ';
|
37 |
+
echo 'If you want WebP-Express to work on the images delivered by your plugins, you must grant write access to your ';
|
38 |
+
echo 'plugin dir (you can revoke the access after we have written the rules, but beware that the plugin may need ';
|
39 |
+
echo 'access rights again. Some of the options affects the .htaccess rules. And WebP Express also have to remove the rules if the plugin is disabled)';
|
40 |
+
}
|
41 |
+
}
|
42 |
+
if (Paths::isUploadDirMovedOutOfWPContentDir()) {
|
43 |
+
if (!Paths::canWriteHTAccessRulesHere($uploadDir)) {
|
44 |
+
echo '<p><b>Oh, one more thing</b>. We also need to write rules to your uploads dir (because you have moved it). ';
|
45 |
+
echo 'Please grant us write access to your ';
|
46 |
+
if (FileHelper::fileExists($uploadDir . '/.htaccess')) {
|
47 |
+
echo '<i>.htaccess</i> file in your upload dir';
|
48 |
+
} else {
|
49 |
+
echo 'upload dir, so we can plant an <i>.htaccess</i> there';
|
50 |
+
}
|
51 |
+
echo '. Your upload dir is: <i>' . $uploadDir . '</i>. ';
|
52 |
+
echo '- Or alternatively, you can leave it be and update the rules manually, whenever they need to be changed. ';
|
53 |
+
}
|
54 |
+
}
|
55 |
+
} else {
|
56 |
+
$firstWritable = Paths::returnFirstWritableHTAccessDir([$wpContentDir, $indexDir]);
|
57 |
+
if ($firstWritable === false) {
|
58 |
+
echo '<p><b>Oh, one more thing</b>. Unless you are going to put the rewrite rules into your configuration manually, ';
|
59 |
+
echo '<i>WebP Express</i> would be needing to store the rewrite rules in a <i>.htaccess</i> file. ';
|
60 |
+
echo 'However, your current file permissions does not allow that. ';
|
61 |
+
echo '<i>WebP Express</i> would prefer to put the rewrite rules into your <i>wp-content</i> folder, but ';
|
62 |
+
echo 'will store them in your main <i>.htaccess</i> file if it can write to that, but not your wp-content. ';
|
63 |
+
echo '(The preference for storing in wp-content is simply that it minimizes the risk of conflicts with rules from other plugins. ';
|
64 |
+
echo 'deeper <i>.htaccess</i> files takes precedence). ';
|
65 |
+
echo 'Anyway: Could you please adjust the file permissions of either your main <i>.htaccess</i> file or your wp-content dir?';
|
66 |
+
echo 'You can reload this page aftewards, and this message should be gone</p>';
|
67 |
+
} else {
|
68 |
+
if ($firstWritable != $wpContentDir) {
|
69 |
+
echo '<p>Oh, one more thing. Unless you are going to put the rewrite rules into your configuration manually, ';
|
70 |
+
echo '<i>WebP Express</i> would be needing to store the rewrite rules in a <i>.htaccess</i> file. ';
|
71 |
+
echo 'Your current file permissions <i>does</i> allow us to store rules in your main <i>.htaccess</i> file. ';
|
72 |
+
echo 'However, <i>WebP Express</i> would prefer to put the rewrite rules into your <i>wp-content</i> folder. ';
|
73 |
+
echo 'Putting them there will minimize the risk of conflict with rules from other plugins, as ';
|
74 |
+
echo 'deeper <i>.htaccess</i> files takes precedence. ';
|
75 |
+
echo 'If you would like the <i>.htaccess</i> file to be stored in your wp-content folder, please adjust your file permissions. ';
|
76 |
+
echo 'You can reload this page aftewards, and this message should be gone</p>';
|
77 |
+
}
|
78 |
+
}
|
79 |
+
if (Paths::isUploadDirMovedOutOfWPContentDir()) {
|
80 |
+
if (!Paths::canWriteHTAccessRulesHere($uploadDir)) {
|
81 |
+
echo '<p><b>Oh, one more thing</b>. We also need to write rules to your uploads dir (because you have moved it). ';
|
82 |
+
echo 'Please grant us write access to your ';
|
83 |
+
if (FileHelper::fileExists($uploadDir . '/.htaccess')) {
|
84 |
+
echo '<i>.htaccess</i> file in your upload dir';
|
85 |
+
} else {
|
86 |
+
echo 'upload dir, so we can plant an <i>.htaccess</i> there';
|
87 |
+
}
|
88 |
+
echo '. Your upload dir is: <i>' . $uploadDir . '</i>. ';
|
89 |
+
echo '- Or alternatively, you can leave it and update the rules manually, whenever they need to be changed. ';
|
90 |
+
}
|
91 |
+
}
|
92 |
+
if (Paths::isPluginDirMovedOutOfAbsPath()) {
|
93 |
+
if (!Paths::canWriteHTAccessRulesHere($pluginDir)) {
|
94 |
+
echo '<p>Oh, one more thing. I see you have moved your plugins dir out of your root. ';
|
95 |
+
echo 'If you want WebP-Express to work on the images delivered by your plugins, you must also grant write access ';
|
96 |
+
echo 'to your ';
|
97 |
+
if (FileHelper::fileExists($pluginDir . '/.htaccess')) {
|
98 |
+
echo '<i>.htaccess</i> file in your plugin dir';
|
99 |
+
} else {
|
100 |
+
echo 'plugin dir, so we can plant an <i>.htaccess</i> there';
|
101 |
+
}
|
102 |
+
echo ' (you can revoke the access after we have written the rules).';
|
103 |
+
echo '</p>';
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
|
108 |
+
/*
|
109 |
+
if(Paths::canWriteHTAccessRulesHere($wpContentDir)) {
|
110 |
+
|
111 |
+
if ($firstWritable === false) {
|
112 |
+
echo 'Actually, WebP Express does not have permission to write to your main <i>.htaccess</i> either. Please fix. Preferably ';
|
113 |
+
}
|
114 |
+
|
115 |
+
|
116 |
+
$firstWritable = Paths::returnFirstWritableHTAccessDir([$indexDir, $homeDir]);
|
117 |
+
if ($firstWritable === false) {
|
118 |
+
echo 'Actually, WebP Express does not have permission to write to your main <i>.htaccess</i> either. Please fix. Preferably ';
|
119 |
+
}
|
120 |
+
if(Paths::canWriteHTAccessRulesHere($wpContentDir)) {
|
121 |
+
echo '<i>WebP Express</i> however does have rights to write to your main <i>.htaccess</i>. It will work too - probably. But to minimize risk of conflict with rules from other plugins, I recommended you to adjust the file permissions to allow us to write to a <i>.htaccess</i> file in your <i>wp-content dir</i>';
|
122 |
+
}
|
123 |
+
echo '</p>';
|
124 |
+
}*/
|
125 |
+
|
126 |
+
echo '</div>';
|
lib/options/page.php
ADDED
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/../classes/Config.php';
|
4 |
+
use \WebPExpress\Config;
|
5 |
+
|
6 |
+
include_once __DIR__ . '/../classes/FileHelper.php';
|
7 |
+
use \WebPExpress\FileHelper;
|
8 |
+
|
9 |
+
include_once __DIR__ . '/../classes/HTAccess.php';
|
10 |
+
use \WebPExpress\HTAccess;
|
11 |
+
|
12 |
+
include_once __DIR__ . '/../classes/Messenger.php';
|
13 |
+
use \WebPExpress\Messenger;
|
14 |
+
|
15 |
+
include_once __DIR__ . '/../classes/Paths.php';
|
16 |
+
use \WebPExpress\Paths;
|
17 |
+
|
18 |
+
include_once __DIR__ . '/../classes/PlatformInfo.php';
|
19 |
+
use \WebPExpress\PlatformInfo;
|
20 |
+
|
21 |
+
include_once __DIR__ . '/../classes/State.php';
|
22 |
+
use \WebPExpress\State;
|
23 |
+
|
24 |
+
|
25 |
+
if (!current_user_can('manage_options')) {
|
26 |
+
wp_die('You do not have sufficient permissions to access this page.');
|
27 |
+
}
|
28 |
+
?>
|
29 |
+
<div class="wrap">
|
30 |
+
<h2>WebP Express Settings</h2>
|
31 |
+
|
32 |
+
<?php
|
33 |
+
include __DIR__ . "/page-messages.php";
|
34 |
+
|
35 |
+
/*
|
36 |
+
foreach (Paths::getHTAccessDirs() as $dir) {
|
37 |
+
echo $dir . ':' . (Paths::canWriteHTAccessRulesHere($dir) ? 'writable' : 'not writable') . '<br>';
|
38 |
+
//Paths::canWriteHTAccessRulesHere($dir);
|
39 |
+
}*/
|
40 |
+
|
41 |
+
|
42 |
+
$defaultConfig = [
|
43 |
+
'image-types' => 1,
|
44 |
+
'fail' => 'original',
|
45 |
+
'max-quality' => 80,
|
46 |
+
'converters' => [],
|
47 |
+
'forward-query-string' => true
|
48 |
+
];
|
49 |
+
|
50 |
+
$config = Config::loadConfig();
|
51 |
+
if (!$config) {
|
52 |
+
$config = [];
|
53 |
+
}
|
54 |
+
|
55 |
+
$config = array_merge($defaultConfig, $config);
|
56 |
+
if ($config['converters'] == null) {
|
57 |
+
$config['converters'] = [];
|
58 |
+
}
|
59 |
+
|
60 |
+
// Generate a custom nonce value.
|
61 |
+
$webpexpress_settings_nonce = wp_create_nonce('webpexpress_settings_nonce');
|
62 |
+
|
63 |
+
echo '<form action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" method="post" id="webpexpress_settings" >';
|
64 |
+
?>
|
65 |
+
<input type="hidden" name="action" value="webpexpress_settings_submit">
|
66 |
+
<input type="hidden" name="webpexpress_settings_nonce" value="<?php echo $webpexpress_settings_nonce ?>" />
|
67 |
+
|
68 |
+
<?php
|
69 |
+
|
70 |
+
echo '<table class="form-table"><tbody>';
|
71 |
+
|
72 |
+
// Image types
|
73 |
+
// ------------
|
74 |
+
echo '<tr><th scope="row">Image types to convert</th><td>';
|
75 |
+
|
76 |
+
// bitmask
|
77 |
+
// 1: JPEGs
|
78 |
+
// 2: PNG's
|
79 |
+
// Converting only jpegs is thus "1"
|
80 |
+
// Converting both jpegs and pngs is (1+2) = 3
|
81 |
+
//$imageTypes = get_option('webp_express_image_types_to_convert');
|
82 |
+
$imageTypes = $config['image-types'];
|
83 |
+
|
84 |
+
echo '<select name="image-types">';
|
85 |
+
echo '<option value="0"' . ($imageTypes == 0 ? ' selected' : '') . '>Do not convert any images!</option>';
|
86 |
+
echo '<option value="1"' . ($imageTypes == 1 ? ' selected' : '') . '>Only convert jpegs</option>';
|
87 |
+
echo '<option value="3"' . ($imageTypes == 3 ? ' selected' : '') . '>Convert both jpegs and pngs</option>';
|
88 |
+
echo '</select>';
|
89 |
+
|
90 |
+
echo '</td></tr>';
|
91 |
+
|
92 |
+
// Response on failure
|
93 |
+
// --------------------
|
94 |
+
echo '<tr><th scope="row">Response on failure</th><td>';
|
95 |
+
|
96 |
+
//$fail = get_option('webp_express_failure_response');
|
97 |
+
$fail = $config['fail'];
|
98 |
+
echo '<select name="fail">';
|
99 |
+
echo '<option value="original"' . ($fail == 'original' ? ' selected' : '') . '>Original image</option>';
|
100 |
+
echo '<option value="404"' . ($fail == '404' ? ' selected' : '') . '>404</option>';
|
101 |
+
echo '<option value="report"' . ($fail == 'report' ? ' selected' : '') . '>Error report (in plain text)</option>';
|
102 |
+
echo '<option value="report-as-image"' . ($fail == 'report-as-image' ? ' selected' : '') . '>Error report as image</option>';
|
103 |
+
echo '</select>';
|
104 |
+
echo '</td></tr>';
|
105 |
+
// echo '<tr><td colspan=2>Determines what the converter should serve, in case the image conversion should fail. For production servers, recommended value is "Original image". For development servers, choose anything you like, but that</td></tr>';
|
106 |
+
|
107 |
+
// Max quality
|
108 |
+
// --------------------
|
109 |
+
//$maxQuality = get_option('webp_express_max_quality');
|
110 |
+
$maxQuality = $config['max-quality'];
|
111 |
+
|
112 |
+
echo '<tr><th scope="row">Max quality (0-100)</th><td>';
|
113 |
+
echo '<input type="text" name="max-quality" value="' . $maxQuality . '">';
|
114 |
+
echo '</td></tr>';
|
115 |
+
// echo '<tr><td colspan=2><p>Converted jpeg images will get same quality as original, but not more than this setting. Something between 70-85 is recommended for most websites.</p></td></tr>';
|
116 |
+
|
117 |
+
// method
|
118 |
+
//echo '<p>When higher values are used, the encoder will spend more time inspecting additional encoding possibilities and decide on the quality gain. Supported by cwebp, wpc and imagick</p>';
|
119 |
+
|
120 |
+
echo '</tbody></table>';
|
121 |
+
|
122 |
+
// Converters
|
123 |
+
// --------------------
|
124 |
+
|
125 |
+
$converters = $config['converters'];
|
126 |
+
echo '<script>window.converters = ' . json_encode($converters) . '</script>';
|
127 |
+
echo "<input type='text' name='converters' value='' style='visibility:hidden' />";
|
128 |
+
|
129 |
+
// https://premium.wpmudev.org/blog/handling-form-submissions/
|
130 |
+
|
131 |
+
|
132 |
+
?>
|
133 |
+
<?php
|
134 |
+
/*
|
135 |
+
$localConverters = ['cwebp', 'imagick', 'gd'];
|
136 |
+
$testResult = WebPExpressHelpers::testConverters($localConverters);
|
137 |
+
//print_r($testResult);
|
138 |
+
|
139 |
+
if ($testResult['numOperationalConverters'] == 0) {
|
140 |
+
echo 'Unfortunately, your server is currently not able to convert to webp files by itself. You will need to set up a cloud converter.<br><br>';
|
141 |
+
foreach ($testResult['results'] as $result) {
|
142 |
+
echo $result['converter'] . ':' . $result['message'] . '<br>';
|
143 |
+
}
|
144 |
+
} else {
|
145 |
+
//echo 'Your server is able to convert webp files by itself.';
|
146 |
+
}
|
147 |
+
if ($testResult['numOperationalConverters'] == 1) {
|
148 |
+
//
|
149 |
+
}
|
150 |
+
*/
|
151 |
+
|
152 |
+
|
153 |
+
/*
|
154 |
+
http://php.net/manual/en/function.set-include-path.php
|
155 |
+
|
156 |
+
//exec('/usr/sbin/getsebool -a', $output6, $returnCode5); // ok
|
157 |
+
//echo 'All se bools: ' . print_r($output6, true) . '. Return code:' . $returnCode5;
|
158 |
+
*/
|
159 |
+
|
160 |
+
echo '<h2>Converters</h2>';
|
161 |
+
$dragIcon = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="17px" height="17px" viewBox="0 0 100.000000 100.000000" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,100.000000) scale(0.100000,-0.100000)" fill="#444444" stroke="none"><path d="M415 920 l-80 -80 165 0 165 0 -80 80 c-44 44 -82 80 -85 80 -3 0 -41 -36 -85 -80z"/><path d="M0 695 l0 -45 500 0 500 0 0 45 0 45 -500 0 -500 0 0 -45z"/><path d="M0 500 l0 -40 500 0 500 0 0 40 0 40 -500 0 -500 0 0 -40z"/><path d="M0 305 l0 -45 500 0 500 0 0 45 0 45 -500 0 -500 0 0 -45z"/><path d="M418 78 l82 -83 82 83 83 82 -165 0 -165 0 83 -82z"/></g></svg>';
|
162 |
+
|
163 |
+
echo '<p><i>Drag to reorder. The converter on top will be used. Should it fail, the next will be used, etc</i></p>';
|
164 |
+
// https://github.com/RubaXa/Sortable
|
165 |
+
|
166 |
+
// Empty list of converters. The list will be populated by the javascript
|
167 |
+
echo '<ul id="converters"></ul>';
|
168 |
+
?>
|
169 |
+
<div id="cwebp" style="display:none;">
|
170 |
+
<div class="cwebp converter-options">
|
171 |
+
<h3>cwebp</h3>
|
172 |
+
<div class="info">
|
173 |
+
cwebp works by executing the cwebp binary from Google. This should normally be your first choice.
|
174 |
+
Its best in terms of quality, speed and options.
|
175 |
+
The only catch is that it requires the exec function to be enabled, and that the webserver user is
|
176 |
+
allowed to execute the cwebp binary (either at known system locations, or one of the precompiled binaries,
|
177 |
+
that comes with this library).
|
178 |
+
If you are on a shared host that doesn't allow that, the second best choice would probably be the wpc cloud converter.
|
179 |
+
</div>
|
180 |
+
<h3>cweb options</h3>
|
181 |
+
<div>
|
182 |
+
<label for="cwebp_use_nice">Use nice</label>
|
183 |
+
<input type="checkbox" id="cwebp_use_nice">
|
184 |
+
<br>Enabling "use nice" saves system resources at the cost of slightly slower conversion
|
185 |
+
|
186 |
+
</div>
|
187 |
+
<br>
|
188 |
+
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
189 |
+
<!-- <a href="javascript: tb_remove();">close</a> -->
|
190 |
+
</div>
|
191 |
+
</div>
|
192 |
+
<div id="gd" style="display:none;">
|
193 |
+
<div class="ewww converter-options">
|
194 |
+
<h3>Gd</h3>
|
195 |
+
<p>
|
196 |
+
The gd converter uses the Gd extension to do the conversion. It is per default placed below the cloud converters for two reasons.
|
197 |
+
Firstly, it does not seem to produce quite as good quality as cwebp.
|
198 |
+
Secondly, it provides no conversion options, besides quality.
|
199 |
+
The Gd extension is pretty common, so the main feature of this converter is that it <i>may</i> work out of the box.
|
200 |
+
This is in contrast to the cloud converters, which requires that the user does some setup.
|
201 |
+
</p>
|
202 |
+
<h3>Gd options</h3>
|
203 |
+
<div class="info">
|
204 |
+
Gd neither supports copying metadata nor exposes any WebP options. Lacking the option to set lossless encoding results in poor encoding of PNGs - the filesize is generally much larger than the original. For this reason, the converter defaults to skip PNG's.
|
205 |
+
</div>
|
206 |
+
<div>
|
207 |
+
<label for="gd_skip_pngs">Skip PNGs</label>
|
208 |
+
<input type="checkbox" id="gd_skip_pngs">
|
209 |
+
</div>
|
210 |
+
<br>
|
211 |
+
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
212 |
+
<!-- <a href="javascript: tb_remove();">close</a> -->
|
213 |
+
</div>
|
214 |
+
</div>
|
215 |
+
<div id="imagick" style="display:none;">
|
216 |
+
<div class="imagick converter-options">
|
217 |
+
<h3>Imagick</h3>
|
218 |
+
<p>
|
219 |
+
imagick would be your last choice. For some reason it produces conversions that are only marginally better than the originals.
|
220 |
+
See <a href="https://github.com/rosell-dk/webp-convert/issues/43" target="_blank">this issue</a>. But it is fast.
|
221 |
+
</p>
|
222 |
+
<h3>Imagick options</h3>
|
223 |
+
<div class="info">
|
224 |
+
imagick has no extra options.
|
225 |
+
</div>
|
226 |
+
<br>
|
227 |
+
<!--
|
228 |
+
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
229 |
+
-->
|
230 |
+
<!-- <a href="javascript: tb_remove();">close</a> -->
|
231 |
+
</div>
|
232 |
+
</div>
|
233 |
+
<div id="ewww" style="display:none;">
|
234 |
+
<div class="ewww converter-options">
|
235 |
+
<h3>Ewww</h3>
|
236 |
+
<p>
|
237 |
+
<a href="https://ewww.io/" target="_blank">ewww</a> is a cloud service.
|
238 |
+
It is a decent alternative for those who don't have the technical know-how to install wpc.
|
239 |
+
ewww is using cwebp to do the conversion, so quality is great.
|
240 |
+
ewww however only provides one conversion option (quality), and it does not support "auto"
|
241 |
+
quality (yet - I have requested the feature and the maintainer are considering it).
|
242 |
+
Also, it is not free. But very cheap. Like in almost free.
|
243 |
+
</p>
|
244 |
+
<h3>Ewww options</h3>
|
245 |
+
<div>
|
246 |
+
<label for="ewww_key">Key</label>
|
247 |
+
<input type="text" id="ewww_key" placeholder="Your API key here">
|
248 |
+
</div>
|
249 |
+
<br>
|
250 |
+
<h4>Fallback (optional)</h4>
|
251 |
+
<div>
|
252 |
+
<label for="ewww_key_2">key</label>
|
253 |
+
<input type="text" id="ewww_key_2" placeholder="In case the first one expires...">
|
254 |
+
</div>
|
255 |
+
<br>
|
256 |
+
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
257 |
+
<!-- <a href="javascript: tb_remove();">close</a> -->
|
258 |
+
</div>
|
259 |
+
</div>
|
260 |
+
<div id="wpc" style="display:none;">
|
261 |
+
<div class="wpc converter-options">
|
262 |
+
<h3>WebPConvert Cloud Service</h3>
|
263 |
+
wpc is an open source cloud converter based on <a href="https://github.com/rosell-dk/webp-convert" target="_blank">WebPConvert</a>
|
264 |
+
(this plugin also happens to be based on WebPConvert).
|
265 |
+
Conversions will of course be slower than cwebp, as images need to go back and forth to the cloud converter.
|
266 |
+
As images usually just needs to be converted once, the slower conversion
|
267 |
+
speed is probably acceptable. The conversion quality and options of wpc matches cwebp.
|
268 |
+
The only catch is that you will need to install the WPC library on a server (or have someone do it for you).
|
269 |
+
<a href="https://github.com/rosell-dk/webp-convert-cloud-service" target="blank">Visit WPC on github</a>.
|
270 |
+
If this is a problem, we suggest you turn to ewww.
|
271 |
+
(PS: A Wordpress plugin is planned, making it easier to set up a WPC instance. Or perhaps the functionality will even be part of this plugin)
|
272 |
+
|
273 |
+
<h3>Options for WebPConvert Cloud Service</h3>
|
274 |
+
<div>
|
275 |
+
<label for="wpc_url">URL</label>
|
276 |
+
<input type="text" id="wpc_url" placeholder="Url to your WPC instance">
|
277 |
+
</div>
|
278 |
+
|
279 |
+
<div>
|
280 |
+
<label for="wpc_secret">Secret</label>
|
281 |
+
<input type="text" id="wpc_secret" placeholder="Secret (must match secret on server side)">
|
282 |
+
</div>
|
283 |
+
<br>
|
284 |
+
<h4>Fallback (optional)</h4>
|
285 |
+
<p>In case the first is down, the fallback will be used.</p>
|
286 |
+
<div>
|
287 |
+
<label for="wpc_url_2">URL</label>
|
288 |
+
<input type="text" id="wpc_url_2" placeholder="Url to your other WPC instance">
|
289 |
+
</div>
|
290 |
+
<div>
|
291 |
+
<label for="wpc_secret_2">Secret</label>
|
292 |
+
<input type="text" id="wpc_secret_2" placeholder="Secret (must match secret on server side)">
|
293 |
+
</div>
|
294 |
+
<br>
|
295 |
+
<button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
|
296 |
+
</div>
|
297 |
+
</div>
|
298 |
+
|
299 |
+
<table>
|
300 |
+
<tr>
|
301 |
+
<td style="padding-right:20px"><?php submit_button('Save settings'); ?></td>
|
302 |
+
<td><?php submit_button('Save settings and force new .htaccess rules', 'secondary', 'force'); ?></td>
|
303 |
+
</tr>
|
304 |
+
</table>
|
305 |
+
</form>
|
306 |
+
</div>
|
lib/options/submit.php
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/../classes/Config.php';
|
4 |
+
use \WebPExpress\Config;
|
5 |
+
|
6 |
+
|
7 |
+
include_once __DIR__ . '/../classes/HTAccess.php';
|
8 |
+
use \WebPExpress\HTAccess;
|
9 |
+
|
10 |
+
include_once __DIR__ . '/../classes/Messenger.php';
|
11 |
+
use \WebPExpress\Messenger;
|
12 |
+
|
13 |
+
include_once __DIR__ . '/../classes/Paths.php';
|
14 |
+
use \WebPExpress\Paths;
|
15 |
+
|
16 |
+
|
17 |
+
// https://premium.wpmudev.org/blog/handling-form-submissions/
|
18 |
+
// checkout https://codex.wordpress.org/Function_Reference/sanitize_meta
|
19 |
+
|
20 |
+
$config = [
|
21 |
+
'fail' => sanitize_text_field($_POST['fail']),
|
22 |
+
'max-quality' => sanitize_text_field($_POST['max-quality']),
|
23 |
+
'image-types' => sanitize_text_field($_POST['image-types']),
|
24 |
+
'converters' => json_decode(wp_unslash($_POST['converters']), true), // holy moly! - https://stackoverflow.com/questions/2496455/why-are-post-variables-getting-escaped-in-php
|
25 |
+
'forward-query-string' => true
|
26 |
+
];
|
27 |
+
|
28 |
+
// remove id's
|
29 |
+
foreach ($config['converters'] as &$converter) {
|
30 |
+
unset ($converter['id']);
|
31 |
+
}
|
32 |
+
|
33 |
+
$result = Config::saveConfigurationAndHTAccess($config, isset($_POST['force']));
|
34 |
+
|
35 |
+
/*
|
36 |
+
Messenger::addMessage(
|
37 |
+
'info',
|
38 |
+
isset($_POST['force']) ? 'force' : 'no-force' .
|
39 |
+
(HTAccess::doesRewriteRulesNeedUpdate($config) ? 'need' : 'no need')
|
40 |
+
);*/
|
41 |
+
|
42 |
+
/*
|
43 |
+
Messenger::addMessage(
|
44 |
+
'info',
|
45 |
+
'<pre>' . htmlentities(print_r($result, true)) . '</pre>'
|
46 |
+
);*/
|
47 |
+
|
48 |
+
if (!$result['saved-both-config']) {
|
49 |
+
if (!$result['saved-main-config']) {
|
50 |
+
Messenger::addMessage(
|
51 |
+
'error',
|
52 |
+
'Failed saving configuration file.<br>' .
|
53 |
+
'Current file permissions are preventing WebP Express to save configuration to: "' . Paths::getConfigFileName() . '"'
|
54 |
+
);
|
55 |
+
} else {
|
56 |
+
Messenger::addMessage(
|
57 |
+
'error',
|
58 |
+
'Failed saving options file. Check file permissions<br>' .
|
59 |
+
'Tried to save to: "' . Paths::getWodOptionsFileName() . '"'
|
60 |
+
);
|
61 |
+
|
62 |
+
}
|
63 |
+
} else {
|
64 |
+
if (!$result['rules-needed-update']) {
|
65 |
+
Messenger::addMessage(
|
66 |
+
'success',
|
67 |
+
'Configuration saved. Rewrite rules did not need to be updated. ' . HTAccess::testLinks($config)
|
68 |
+
);
|
69 |
+
} else {
|
70 |
+
$rulesResult = $result['htaccess-result'];
|
71 |
+
/*
|
72 |
+
'mainResult' // 'index', 'wp-content' or 'failed'
|
73 |
+
'minRequired' // 'index' or 'wp-content'
|
74 |
+
'pluginToo' // 'yes', 'no' or 'depends'
|
75 |
+
'uploadToo' // 'yes', 'no' or 'depends'
|
76 |
+
'overidingRulesInWpContentWarning' // true if main result is 'index' but we cannot remove those in wp-content
|
77 |
+
'rules' // the rules that were generated
|
78 |
+
'pluginFailed' // true if failed to write to plugin folder (it only tries that, if pluginToo == 'yes')
|
79 |
+
'pluginFailedBadly' // true if plugin failed AND it seems we have rewrite rules there
|
80 |
+
'uploadFailed' // true if failed to write to plugin folder (it only tries that, if pluginToo == 'yes')
|
81 |
+
'uploadFailedBadly' // true if plugin failed AND it seems we have rewrite rules there
|
82 |
+
*/
|
83 |
+
$mainResult = $rulesResult['mainResult'];
|
84 |
+
$rules = $rulesResult['rules'];
|
85 |
+
|
86 |
+
if ($mainResult == 'failed') {
|
87 |
+
if ($rulesResult['minRequired'] == 'wp-content') {
|
88 |
+
Messenger::addMessage(
|
89 |
+
'error',
|
90 |
+
'Configuration saved, but failed saving rewrite rules. ' .
|
91 |
+
'Please grant us write access to your <i>wp-content</i> dir (we need that, because you have moved <i>wp-content</i> out of the Wordpress dir) ' .
|
92 |
+
'- or, alternatively insert the following rules directly in that <i>.htaccess</i> file, or your Apache configuration:' .
|
93 |
+
'<pre>' . htmlentities(print_r($rules, true)) . '</pre>'
|
94 |
+
);
|
95 |
+
|
96 |
+
} else {
|
97 |
+
Messenger::addMessage(
|
98 |
+
'error',
|
99 |
+
'Configuration saved, but failed saving rewrite rules. ' .
|
100 |
+
'Please grant us write access to either write rules to an <i>.htaccess</i> in your <i>wp-content</i> dir (preferably), ' .
|
101 |
+
'or your main <i>.htaccess</i> file. ' .
|
102 |
+
'- or, alternatively insert the following rules directly in that <i>.htaccess</i> file, or your Apache configuration:' .
|
103 |
+
'<pre>' . htmlentities(print_r($rules, true)) . '</pre>'
|
104 |
+
);
|
105 |
+
}
|
106 |
+
} else {
|
107 |
+
$savedToPluginsToo = (($rulesResult['pluginToo'] == 'yes') && !($rulesResult['pluginFailed']));
|
108 |
+
$savedToUploadsToo = (($rulesResult['uploadToo'] == 'yes') && !($rulesResult['uploadFailed']));
|
109 |
+
|
110 |
+
Messenger::addMessage(
|
111 |
+
'success',
|
112 |
+
'Configuration saved. Rewrite rules were saved to your <i>.htaccess</i> in your <i>' . $mainResult . '</i> folder' .
|
113 |
+
(Paths::isWPContentDirMoved() ? ' (which you moved, btw)' : '') .
|
114 |
+
($savedToPluginsToo ? ' as well as in your <i>plugins</i> folder' : '') .
|
115 |
+
((Paths::isWPContentDirMoved() && $savedToPluginsToo) ? ' (you moved that as well!)' : '.') .
|
116 |
+
($savedToUploadsToo ? ' as well as in your <i>uploads</i> folder' : '') .
|
117 |
+
((Paths::isWPContentDirMoved() && $savedToUploadsToo) ? ' (you moved that as well!)' : '.') .
|
118 |
+
HTAccess::testLinks($config)
|
119 |
+
);
|
120 |
+
}
|
121 |
+
if ($rulesResult['mainResult'] == 'index') {
|
122 |
+
if ($rulesResult['overidingRulesInWpContentWarning']) {
|
123 |
+
Messenger::addMessage(
|
124 |
+
'warning',
|
125 |
+
'We have rewrite rules in the <i>wp-content</i> folder, which we cannot remove. ' .
|
126 |
+
'These are overriding those just saved. ' .
|
127 |
+
'Please change file permissions or remove the rules from the <i>.htaccess</i> file manually'
|
128 |
+
);
|
129 |
+
} else {
|
130 |
+
Messenger::addMessage(
|
131 |
+
'info',
|
132 |
+
'The rewrite rules are currently stored in your root. ' .
|
133 |
+
'WebP Express would prefer to store them in your wp-content folder, ' .
|
134 |
+
'but your current file permissions does not allow that.'
|
135 |
+
);
|
136 |
+
}
|
137 |
+
}
|
138 |
+
if ($rulesResult['pluginFailed']) {
|
139 |
+
if ($rulesResult['pluginFailedBadly']) {
|
140 |
+
Messenger::addMessage(
|
141 |
+
'warning',
|
142 |
+
'The <i>.htaccess</i> rules in your plugins folder could not be updated (no write access). ' .
|
143 |
+
'This is not so good, because we have rules there already...' .
|
144 |
+
'You should update them. Here they are: ' .
|
145 |
+
'<pre>' . htmlentities(print_r($rules, true)) . '</pre>'
|
146 |
+
);
|
147 |
+
} else {
|
148 |
+
Messenger::addMessage(
|
149 |
+
'info',
|
150 |
+
'<i>.htaccess</i> rules could not be written into your plugins folder. ' .
|
151 |
+
'Images stored in your plugins will not be converted to webp'
|
152 |
+
);
|
153 |
+
}
|
154 |
+
}
|
155 |
+
if ($rulesResult['uploadFailed']) {
|
156 |
+
if ($rulesResult['uploadFailedBadly']) {
|
157 |
+
Messenger::addMessage(
|
158 |
+
'error',
|
159 |
+
'The <i>.htaccess</i> rules in your uploads folder could not be updated (no write access). ' .
|
160 |
+
'This is not so good, because we have rules there already...' .
|
161 |
+
'You should update them. Here they are: ' .
|
162 |
+
'<pre>' . htmlentities(print_r($rules, true)) . '</pre>'
|
163 |
+
);
|
164 |
+
} else {
|
165 |
+
Messenger::addMessage(
|
166 |
+
'warning',
|
167 |
+
'<i>.htaccess</i> rules could not be written into your uploads folder (this is needed, because you have moved it outside your <i>wp-content</i> folder). ' .
|
168 |
+
'Please grant write permmissions to you uploads folder. Otherwise uploaded mages will not be converted to webp'
|
169 |
+
);
|
170 |
+
}
|
171 |
+
}
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
wp_redirect( $_SERVER['HTTP_REFERER']);
|
176 |
+
|
177 |
+
exit();
|
lib/reactivate.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
include_once __DIR__ . '/classes/Config.php';
|
4 |
+
use \WebPExpress\Config;
|
5 |
+
|
6 |
+
include_once __DIR__ . '/classes/HTAccess.php';
|
7 |
+
use \WebPExpress\HTAccess;
|
8 |
+
|
9 |
+
include_once __DIR__ . '/classes/Messenger.php';
|
10 |
+
use \WebPExpress\Messenger;
|
11 |
+
|
12 |
+
include_once __DIR__ . '/classes/Actions.php';
|
13 |
+
use \WebPExpress\Actions;
|
14 |
+
|
15 |
+
include_once __DIR__ . '/classes/PlatformInfo.php';
|
16 |
+
use \WebPExpress\PlatformInfo;
|
17 |
+
|
18 |
+
include_once __DIR__ . '/classes/State.php';
|
19 |
+
use \WebPExpress\State;
|
20 |
+
|
21 |
+
|
22 |
+
// The plugin has been reactivated.
|
23 |
+
// We must regenerate the .htaccess rules.
|
24 |
+
// (config dir and options and of course still there, no need to do anything about that)
|
25 |
+
|
26 |
+
// Messenger::addMessage('error', 'You are on Microsof IIS server. The plugin does not work on IIS (yet)');
|
27 |
+
//Actions::procastinate('deactivate');
|
28 |
+
|
29 |
+
$config = Config::loadConfig();
|
30 |
+
if ($config === false) {
|
31 |
+
Messenger::addMessage(
|
32 |
+
'error',
|
33 |
+
'The config file seems to have gone missing. You will need to reconfigure WebP Express <a href="options-general.php?page=webp_express_settings_page">(here)</a>.'
|
34 |
+
);
|
35 |
+
} else {
|
36 |
+
$rulesResult = HTAccess::saveRules($config);
|
37 |
+
/*
|
38 |
+
'mainResult' // 'index', 'wp-content' or 'failed'
|
39 |
+
'minRequired' // 'index' or 'wp-content'
|
40 |
+
'pluginToo' // 'yes', 'no' or 'depends'
|
41 |
+
'pluginFailed' // true if failed to write to plugin folder (it only tries that, if pluginToo == 'yes')
|
42 |
+
'pluginFailedBadly' // true if plugin failed AND it seems we have rewrite rules there
|
43 |
+
'overidingRulesInWpContentWarning' // true if main result is 'index' but we cannot remove those in wp-content
|
44 |
+
'rules' // the rules that were generated
|
45 |
+
*/
|
46 |
+
$mainResult = $rulesResult['mainResult'];
|
47 |
+
$rules = $rulesResult['rules'];
|
48 |
+
|
49 |
+
if ($mainResult != 'failed') {
|
50 |
+
Messenger::addMessage(
|
51 |
+
'success',
|
52 |
+
'WebP Express re-activated successfully.<br>' .
|
53 |
+
'The image redirections are in effect again.<br><br>' .
|
54 |
+
'Just a quick reminder: If you at some point change the upload directory or move Wordpress, the <i>.htaccess</i> will need to be regenerated.<br>' .
|
55 |
+
'You do that by re-saving the settings <a href="options-general.php?page=webp_express_settings_page">(here)</a>'
|
56 |
+
);
|
57 |
+
} else {
|
58 |
+
Messenger::addMessage(
|
59 |
+
'warning',
|
60 |
+
'WebP Express could not regenerate the rewrite rules<br>' .
|
61 |
+
'You need to change some permissions. Head to the ' .
|
62 |
+
'<a href="options-general.php?page=webp_express_settings_page">settings page</a> ' .
|
63 |
+
'and try to save the settings there (it will provide more information about the problem)'
|
64 |
+
);
|
65 |
+
|
66 |
+
}
|
67 |
+
|
68 |
+
}
|
lib/uninstall.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
//include_once __DIR__ . '/classes/Config.php';
|
4 |
+
//use \WebPExpress\Config;
|
5 |
+
|
6 |
+
include_once __DIR__ . '/classes/Paths.php';
|
7 |
+
use \WebPExpress\Paths;
|
8 |
+
|
9 |
+
|
10 |
+
/* helper. Remove dir recursively. No warnings - fails silently */
|
11 |
+
function webpexpress_rrmdir($dir) {
|
12 |
+
if (@is_dir($dir)) {
|
13 |
+
$objects = @scandir($dir);
|
14 |
+
foreach ($objects as $object) {
|
15 |
+
if ($object != "." && $object != "..") {
|
16 |
+
if (@is_dir($dir."/".$object))
|
17 |
+
webpexpress_rrmdir($dir."/".$object);
|
18 |
+
else
|
19 |
+
@unlink($dir."/".$object);
|
20 |
+
}
|
21 |
+
}
|
22 |
+
@rmdir($dir);
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
$optionsToDelete = [
|
27 |
+
'webp-express-messages-pending',
|
28 |
+
'webp-express-action-pending',
|
29 |
+
'webp-express-state',
|
30 |
+
'webp-express-version',
|
31 |
+
'webp-express-activation-error',
|
32 |
+
'webp-express-migration-version'
|
33 |
+
];
|
34 |
+
foreach ($optionsToDelete as $i => $optionName) {
|
35 |
+
delete_option($optionName);
|
36 |
+
}
|
37 |
+
|
38 |
+
// remove content dir (config plus images)
|
39 |
+
webpexpress_rrmdir(Paths::getContentDirAbs());
|
test-run.php
DELETED
@@ -1,260 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
//require 'webp-on-demand/vendor/autoload.php';
|
4 |
-
|
5 |
-
|
6 |
-
//require 'vendor/webp-convert-and-serve/autoload.php';
|
7 |
-
//require 'vendor/webp-convert/autoload.php';
|
8 |
-
|
9 |
-
error_reporting(E_ALL);
|
10 |
-
ini_set("display_errors", 1);
|
11 |
-
|
12 |
-
//require 'vendor/webp-convert/require-all.inc';
|
13 |
-
require 'vendor/require-webp-convert.php';
|
14 |
-
|
15 |
-
//use WebPConvertAndServe\WebPConvertAndServe;
|
16 |
-
use WebPConvert\WebPConvert;
|
17 |
-
use WebPConvert\Converters\ConverterHelper;
|
18 |
-
|
19 |
-
// TODO:
|
20 |
-
// Much of this file could be moved into the libraries.
|
21 |
-
// Ie:
|
22 |
-
// - Report (such as "trying gd", "successfully...", "file size (original)") could be part of WebPConvertAndServe
|
23 |
-
// ($REPORT_AS_IMAGE and $REPORT actions could show complete report, and convertAndReport too)
|
24 |
-
// - or even be part of WebPConvert - ie the log could be returned in a variable passed by reference.
|
25 |
-
//
|
26 |
-
|
27 |
-
$source = $_GET['source'];
|
28 |
-
$destination = $_GET['destination'];
|
29 |
-
$converter = $_GET['converter'];
|
30 |
-
|
31 |
-
if (isset($_GET['max-quality'])) {
|
32 |
-
$options['max-quality'] = intval($_GET['max-quality']);
|
33 |
-
}
|
34 |
-
|
35 |
-
if (isset($_GET['method'])) {
|
36 |
-
$options['method'] = intval($_GET['method']);
|
37 |
-
}
|
38 |
-
|
39 |
-
/*
|
40 |
-
switch ($converter) {
|
41 |
-
case 'ewww':
|
42 |
-
if (isset($_GET['key'])) {
|
43 |
-
$options['key'] = $_GET['key'];
|
44 |
-
}
|
45 |
-
break;
|
46 |
-
case 'cwebp':
|
47 |
-
if (isset($_GET['use-nice'])) {
|
48 |
-
$options['use-nice'] = boolval($_GET['use-nice'] == 'true');
|
49 |
-
}
|
50 |
-
break;
|
51 |
-
case 'gd':
|
52 |
-
if (isset($_GET['skip-pngs'])) {
|
53 |
-
$options['skip-pngs'] = boolval($_GET['skip-pngs'] == 'true');
|
54 |
-
}
|
55 |
-
break;
|
56 |
-
}*/
|
57 |
-
|
58 |
-
$converterClassName = 'WebPConvert\\Converters\\' . ucfirst($converter);
|
59 |
-
$availOptions = array_column($converterClassName::$extraOptions, 'type', 'name');
|
60 |
-
//print_r($availOptions);
|
61 |
-
|
62 |
-
$hasFallback = false;
|
63 |
-
$options2 = [];
|
64 |
-
foreach ($availOptions as $optionName => $optionType) {
|
65 |
-
if (isset($_GET[$optionName . '-2'])) {
|
66 |
-
if ($_GET[$optionName . '-2'] != '') {
|
67 |
-
$hasFallback = true;
|
68 |
-
$options2 = $options;
|
69 |
-
// echo 'value:' . $_GET[$optionName . '-2'];
|
70 |
-
break;
|
71 |
-
}
|
72 |
-
}
|
73 |
-
}
|
74 |
-
|
75 |
-
foreach ($availOptions as $optionName => $optionType) {
|
76 |
-
switch ($optionType) {
|
77 |
-
case 'string':
|
78 |
-
if (isset($_GET[$optionName])) {
|
79 |
-
$options[$optionName] = $_GET[$optionName];
|
80 |
-
}
|
81 |
-
if (isset($_GET[$optionName. '-2'])) {
|
82 |
-
$options2[$optionName] = $_GET[$optionName . '-2'];
|
83 |
-
} else {
|
84 |
-
if ($hasFallback) {
|
85 |
-
$options2[$optionName] = $options[$optionName];
|
86 |
-
}
|
87 |
-
}
|
88 |
-
break;
|
89 |
-
case 'boolean':
|
90 |
-
if (isset($_GET[$optionName])) {
|
91 |
-
$options[$optionName] = ($_GET[$optionName] == 'true');
|
92 |
-
}
|
93 |
-
break;
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
//echo '<pre>' . print_r($options, true) . '</pre>';
|
98 |
-
//echo '<pre>' . print_r($options2, true) . '</pre>';
|
99 |
-
|
100 |
-
//echo '';
|
101 |
-
?>
|
102 |
-
<html>
|
103 |
-
<head>
|
104 |
-
<style>
|
105 |
-
body {
|
106 |
-
padding:10px;
|
107 |
-
font-size: 17px;
|
108 |
-
}
|
109 |
-
p {
|
110 |
-
margin-top: 0;
|
111 |
-
}
|
112 |
-
label {
|
113 |
-
font-style: italic;
|
114 |
-
}
|
115 |
-
p.error-msg {
|
116 |
-
/*font-size: 20px;*/
|
117 |
-
}
|
118 |
-
h3 {color: red}
|
119 |
-
</style>
|
120 |
-
</head>
|
121 |
-
<body style="">
|
122 |
-
|
123 |
-
<?php
|
124 |
-
//echo '<p>source: ' . $source . '</p>';
|
125 |
-
//echo '<p>destination: ' . $destination . '</p>';
|
126 |
-
//echo '<p>converter: ' . $converter . '</p>';
|
127 |
-
//echo '</body></html>';
|
128 |
-
|
129 |
-
//WebPConvertAndServe::convertAndReport($source, $destination, $options);
|
130 |
-
|
131 |
-
function testRun($converter, $source, $destination, $options) {
|
132 |
-
$beginTime = microtime(true);
|
133 |
-
|
134 |
-
try {
|
135 |
-
ConverterHelper::runConverter($converter, $source, $destination, $options);
|
136 |
-
} catch (\WebPConvert\Exceptions\WebPConvertBaseException $e) {
|
137 |
-
$failure = $e->description;
|
138 |
-
$msg = $e->getMessage();
|
139 |
-
} catch (\Exception $e) {
|
140 |
-
$failure = 'Unancipated failure';
|
141 |
-
$msg = $e->getMessage();
|
142 |
-
}
|
143 |
-
|
144 |
-
$endTime = microtime(true);
|
145 |
-
$duration = $endTime - $beginTime;
|
146 |
-
|
147 |
-
if (isset($msg)) {
|
148 |
-
echo '<h3 class="error">Test conversion failed (in ' . round($duration * 1000) . ' ms)</h3>';
|
149 |
-
echo '<label>Problem:</label>';
|
150 |
-
echo '<p class="failure">' . $failure . '</p>';
|
151 |
-
echo '<label>Details:</label>';
|
152 |
-
echo '<p class="error-msg">' . $msg . '</p>';
|
153 |
-
} else {
|
154 |
-
echo '<p>Successfully converted test image in ' . round($duration * 1000) . ' ms</p>';
|
155 |
-
|
156 |
-
if (isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false )) {
|
157 |
-
echo '<img src="' . $_GET['destinationUrl'] . '" width=48%><br><br>';
|
158 |
-
}
|
159 |
-
if (filesize($source) < 10000) {
|
160 |
-
echo 'file size (original): ' . round(filesize($source)) . ' bytes<br>';
|
161 |
-
echo 'file size (converted): ' . round(filesize($destination)) . ' bytes<br>';
|
162 |
-
}
|
163 |
-
else {
|
164 |
-
echo 'file size (original): ' . round(filesize($source)/1000) . ' kb<br>';
|
165 |
-
echo 'file size (converted): ' . round(filesize($destination)/1000) . ' kb<br>';
|
166 |
-
}
|
167 |
-
}
|
168 |
-
}
|
169 |
-
|
170 |
-
testRun($converter, $source, $destination, $options);
|
171 |
-
|
172 |
-
if ($hasFallback) {
|
173 |
-
echo '<h2>Testing fallback</h2>';
|
174 |
-
testRun($converter, $source, $destination, $options2);
|
175 |
-
}
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
/*
|
181 |
-
$className = 'WebPConvert\\Converters\\' . ucfirst($converter);
|
182 |
-
|
183 |
-
if (!is_callable([$className, 'convert'])) {
|
184 |
-
echo 'Converter does not appear to exist!';
|
185 |
-
exit;
|
186 |
-
}
|
187 |
-
|
188 |
-
try {
|
189 |
-
call_user_func(
|
190 |
-
[$className, 'convert'],
|
191 |
-
$source,
|
192 |
-
$destination,
|
193 |
-
$options
|
194 |
-
);
|
195 |
-
} catch (\WebPConvert\Converters\Exceptions\ConverterNotOperationalException $e) {
|
196 |
-
// The converter is not operational.
|
197 |
-
$failure = 'The converter is not operational';
|
198 |
-
|
199 |
-
// TODO: We should show link to install instructions for the specific converter (WIKI)
|
200 |
-
|
201 |
-
$msg = $e->getMessage();
|
202 |
-
} catch (\WebPConvert\Converters\Exceptions\ConverterFailedException $e) {
|
203 |
-
$failure = 'The converter failed converting, although requirements seemed to be met';
|
204 |
-
$msg = $e->getMessage();
|
205 |
-
} catch (\WebPConvert\Converters\Exceptions\ConversionDeclinedException $e) {
|
206 |
-
$failure = 'The converter declined converting';
|
207 |
-
$msg = $e->getMessage();
|
208 |
-
} catch (\WebPConvert\Exceptions\InvalidFileExtensionException $e) {
|
209 |
-
$failure = 'The converter does not accept the file extension';
|
210 |
-
$msg = $e->getMessage();
|
211 |
-
} catch (\WebPConvert\Exceptions\TargetNotFoundException $e) {
|
212 |
-
$failure = 'The converter could not locate source file';
|
213 |
-
$msg = $e->getMessage();
|
214 |
-
} catch (\WebPConvert\Exceptions\CreateDestinationFolderException $e) {
|
215 |
-
$failure = 'The converter could not create destination folder. Check file permisions!';
|
216 |
-
$msg = $e->getMessage();
|
217 |
-
} catch (\WebPConvert\Exceptions\CreateDestinationFileException $e) {
|
218 |
-
$failure = 'The converter could not create destination file. Check file permisions!';
|
219 |
-
$msg = $e->getMessage();
|
220 |
-
} catch (\Exception $e) {
|
221 |
-
$failure = 'Unexpected failure';
|
222 |
-
$msg = $e->getMessage();
|
223 |
-
}
|
224 |
-
*/
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
/*
|
230 |
-
try {
|
231 |
-
$options = [
|
232 |
-
'converters' => [$converter]
|
233 |
-
];
|
234 |
-
$success = WebPConvert::convert($source, $destination, $options);
|
235 |
-
} catch (\Exception $e) {
|
236 |
-
$success = false;
|
237 |
-
$msg = $e->getMessage();
|
238 |
-
}
|
239 |
-
|
240 |
-
if ($success) {
|
241 |
-
$endTime = microtime(true);
|
242 |
-
|
243 |
-
$duration = $endTime - $beginTime;
|
244 |
-
echo '<p>Successfully converted test image in ' . round($duration * 1000) . ' ms</p>';
|
245 |
-
|
246 |
-
|
247 |
-
if (isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false )) {
|
248 |
-
echo '<img src="' . $_GET['destinationUrl'] . '" width=50%>';
|
249 |
-
}
|
250 |
-
} else {
|
251 |
-
echo 'Converter failed ';
|
252 |
-
echo $msg;
|
253 |
-
}
|
254 |
-
*/
|
255 |
-
/*
|
256 |
-
$status = WebPOnDemand::serve(__DIR__);
|
257 |
-
if ($status < 0) {
|
258 |
-
// Conversion failed.
|
259 |
-
// you could message your application about the problem here...
|
260 |
-
}*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test/test-run.php
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (isset($_GET['stream-webp-image'])) {
|
4 |
+
header('Content-type: image/webp');
|
5 |
+
if (@readfile($_GET['stream-webp-image']) === false) {
|
6 |
+
// ...
|
7 |
+
}
|
8 |
+
}
|
9 |
+
|
10 |
+
error_reporting(E_ALL);
|
11 |
+
ini_set("display_errors", 1);
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
require "../wod/webp-convert-and-serve.inc";
|
16 |
+
|
17 |
+
use WebPConvert\WebPConvert;
|
18 |
+
use WebPConvert\Loggers\EchoLogger;
|
19 |
+
//use WebPConvertAndServe\WebPConvertAndServe;
|
20 |
+
//use WebPConvert\Converters\ConverterHelper;
|
21 |
+
//use WebPConvertAndServe;
|
22 |
+
|
23 |
+
?>
|
24 |
+
<html>
|
25 |
+
<head>
|
26 |
+
<style>
|
27 |
+
body {
|
28 |
+
padding:10px;
|
29 |
+
font-size: 17px;
|
30 |
+
}
|
31 |
+
p {
|
32 |
+
margin-top: 0;
|
33 |
+
}
|
34 |
+
label {
|
35 |
+
font-style: italic;
|
36 |
+
}
|
37 |
+
p.error-msg {
|
38 |
+
/*font-size: 20px;*/
|
39 |
+
}
|
40 |
+
h3 {color: red}
|
41 |
+
</style>
|
42 |
+
</head>
|
43 |
+
<body style="">
|
44 |
+
|
45 |
+
<?php
|
46 |
+
//WebPConvertAndServe::convertAndReport($source, $destination, $options);use WebPConvert\Loggers\EchoLogger;
|
47 |
+
$source = $_GET['source'];
|
48 |
+
$destination = $_GET['destination'];
|
49 |
+
$converter = $_GET['converter'];
|
50 |
+
|
51 |
+
if (isset($_GET['max-quality'])) {
|
52 |
+
$options['max-quality'] = intval($_GET['max-quality']);
|
53 |
+
}
|
54 |
+
|
55 |
+
/*
|
56 |
+
if (isset($_GET['method'])) {
|
57 |
+
$options['method'] = intval($_GET['method']);
|
58 |
+
}*/
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Sets the options from the query string
|
62 |
+
* We make sure only to set those options that are declared by the converter
|
63 |
+
*/
|
64 |
+
function getConverterOptionsFromQueryString($converter)
|
65 |
+
{
|
66 |
+
|
67 |
+
// Get meta about the options that the converter supports
|
68 |
+
$converterClassName = 'WebPConvert\\Converters\\' . ucfirst($converter);
|
69 |
+
$availOptions = array_column($converterClassName::$extraOptions, 'type', 'name');
|
70 |
+
//print_r($availOptions);
|
71 |
+
|
72 |
+
// Set options
|
73 |
+
$options = [];
|
74 |
+
foreach ($availOptions as $optionName => $optionType) {
|
75 |
+
switch ($optionType) {
|
76 |
+
case 'string':
|
77 |
+
if (isset($_GET[$optionName])) {
|
78 |
+
$options[$optionName] = $_GET[$optionName];
|
79 |
+
}
|
80 |
+
break;
|
81 |
+
case 'boolean':
|
82 |
+
if (isset($_GET[$optionName])) {
|
83 |
+
$options[$optionName] = ($_GET[$optionName] == 'true');
|
84 |
+
}
|
85 |
+
break;
|
86 |
+
}
|
87 |
+
}
|
88 |
+
return $options;
|
89 |
+
}
|
90 |
+
$options['converters'] = [[
|
91 |
+
'converter' => $converter,
|
92 |
+
'options' => getConverterOptionsFromQueryString($converter)
|
93 |
+
]];
|
94 |
+
|
95 |
+
//echo '<pre>' . print_r($options, true) . '</pre>';
|
96 |
+
|
97 |
+
function testRun($converter, $source, $destination, $options) {
|
98 |
+
$beginTime = microtime(true);
|
99 |
+
|
100 |
+
try {
|
101 |
+
$success = WebPConvert::convert($source, $destination, $options, new EchoLogger());
|
102 |
+
} catch (\Exception $e) {
|
103 |
+
$msg = $e->getMessage();
|
104 |
+
}
|
105 |
+
|
106 |
+
$endTime = microtime(true);
|
107 |
+
$duration = $endTime - $beginTime;
|
108 |
+
|
109 |
+
if (!$success) {
|
110 |
+
echo '<h3 class="error">Test conversion failed (in ' . round($duration * 1000) . ' ms)</h3>';
|
111 |
+
|
112 |
+
if (isset($msg)) {
|
113 |
+
echo '<label>Problem:</label>';
|
114 |
+
//echo '<p class="failure">' . $failure . '</p>';
|
115 |
+
//echo '<label>Details:</label>';
|
116 |
+
echo '<p class="error-msg">' . $msg . '</p>';
|
117 |
+
}
|
118 |
+
} else {
|
119 |
+
echo '<p>Successfully converted test image in ' . round($duration * 1000) . ' ms</p>';
|
120 |
+
|
121 |
+
if (isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false )) {
|
122 |
+
//echo '<img src="' . $_GET['destinationUrl'] . '" width=48%><br><br>';
|
123 |
+
echo '<img src="?stream-webp-image=' . $destination . '" width=48%><br><br>';
|
124 |
+
|
125 |
+
}
|
126 |
+
if (filesize($source) < 10000) {
|
127 |
+
echo 'file size (original): ' . round(filesize($source)) . ' bytes<br>';
|
128 |
+
echo 'file size (converted): ' . round(filesize($destination)) . ' bytes<br>';
|
129 |
+
}
|
130 |
+
else {
|
131 |
+
echo 'file size (original): ' . round(filesize($source)/1000) . ' kb<br>';
|
132 |
+
echo 'file size (converted): ' . round(filesize($destination)/1000) . ' kb<br>';
|
133 |
+
}
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
testRun($converter, $source, $destination, $options);
|
vendor/require-webp-convert-and-serve.php
DELETED
@@ -1,4 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
require_once(__DIR__ . "/webp-convert-and-serve/../require-webp-convert.php");
|
3 |
-
require_once(__DIR__ . "/webp-convert-and-serve/BufferLogger.php");
|
4 |
-
require_once(__DIR__ . "/webp-convert-and-serve/WebPConvertAndServe.php");
|
|
|
|
|
|
|
|
vendor/require-webp-convert.php
DELETED
@@ -1,20 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
require_once(__DIR__ . "/webp-convert/Exceptions/WebPConvertBaseException.php");
|
3 |
-
require_once(__DIR__ . "/webp-convert/Loggers/BaseLogger.php");
|
4 |
-
require_once(__DIR__ . "/webp-convert/WebPConvert.php");
|
5 |
-
require_once(__DIR__ . "/webp-convert/Converters/ConverterHelper.php");
|
6 |
-
require_once(__DIR__ . "/webp-convert/Converters/Cwebp.php");
|
7 |
-
require_once(__DIR__ . "/webp-convert/Converters/Ewww.php");
|
8 |
-
require_once(__DIR__ . "/webp-convert/Converters/Gd.php");
|
9 |
-
require_once(__DIR__ . "/webp-convert/Converters/Imagick.php");
|
10 |
-
require_once(__DIR__ . "/webp-convert/Converters/Wpc.php");
|
11 |
-
require_once(__DIR__ . "/webp-convert/Exceptions/ConverterNotFoundException.php");
|
12 |
-
require_once(__DIR__ . "/webp-convert/Exceptions/CreateDestinationFileException.php");
|
13 |
-
require_once(__DIR__ . "/webp-convert/Exceptions/CreateDestinationFolderException.php");
|
14 |
-
require_once(__DIR__ . "/webp-convert/Exceptions/InvalidFileExtensionException.php");
|
15 |
-
require_once(__DIR__ . "/webp-convert/Exceptions/TargetNotFoundException.php");
|
16 |
-
require_once(__DIR__ . "/webp-convert/Converters/Exceptions/ConversionDeclinedException.php");
|
17 |
-
require_once(__DIR__ . "/webp-convert/Converters/Exceptions/ConverterFailedException.php");
|
18 |
-
require_once(__DIR__ . "/webp-convert/Converters/Exceptions/ConverterNotOperationalException.php");
|
19 |
-
require_once(__DIR__ . "/webp-convert/Loggers/EchoLogger.php");
|
20 |
-
require_once(__DIR__ . "/webp-convert/Loggers/VoidLogger.php");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/require-webp-on-demand.php
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
require_once(__DIR__ . "/webp-on-demand/../require-webp-convert-and-serve.php");
|
3 |
-
require_once(__DIR__ . "/webp-on-demand/WebPOnDemand.php");
|
|
|
|
|
|
vendor/webp-convert-and-serve/BufferLogger.php
DELETED
@@ -1,57 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvertAndServe;
|
4 |
-
use WebPConvert\Loggers\BaseLogger;
|
5 |
-
|
6 |
-
class BufferLogger extends BaseLogger
|
7 |
-
{
|
8 |
-
public $entries = array();
|
9 |
-
|
10 |
-
public function log($msg, $style = '')
|
11 |
-
{
|
12 |
-
$this->entries[] = [$msg, $style];
|
13 |
-
}
|
14 |
-
|
15 |
-
public function ln()
|
16 |
-
{
|
17 |
-
$this->entries[] = '';
|
18 |
-
}
|
19 |
-
|
20 |
-
public function getHtml()
|
21 |
-
{
|
22 |
-
$html = '';
|
23 |
-
foreach ($this->entries as $entry) {
|
24 |
-
if ($entry == '') {
|
25 |
-
$html .= '<br>';
|
26 |
-
} else {
|
27 |
-
list($msg, $style) = $entry;
|
28 |
-
|
29 |
-
if ($style == 'bold') {
|
30 |
-
$html .= '<b>' . $msg . '</b>';
|
31 |
-
} elseif ($style == 'italic') {
|
32 |
-
$html .= '<i>' . $msg . '</i>';
|
33 |
-
} else {
|
34 |
-
$html .= $msg;
|
35 |
-
}
|
36 |
-
}
|
37 |
-
}
|
38 |
-
return $html;
|
39 |
-
}
|
40 |
-
|
41 |
-
public function getText($newLineChar = ' ')
|
42 |
-
{
|
43 |
-
$text = '';
|
44 |
-
foreach ($this->entries as $entry) {
|
45 |
-
if ($entry == '') {
|
46 |
-
if (substr($text, -2) != '. ') {
|
47 |
-
$text .= '. ';
|
48 |
-
}
|
49 |
-
} else {
|
50 |
-
list($msg, $style) = $entry;
|
51 |
-
$text .= $msg;
|
52 |
-
}
|
53 |
-
}
|
54 |
-
|
55 |
-
return $text;
|
56 |
-
}
|
57 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert-and-serve/WebPConvertAndServe.php
DELETED
@@ -1,254 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace WebPConvertAndServe;
|
3 |
-
|
4 |
-
use WebPConvert\WebPConvert;
|
5 |
-
use WebPConvertAndServe\BufferLogger;
|
6 |
-
use WebPConvert\Converters\ConverterHelper;
|
7 |
-
//use WebPConvert\Loggers\EchoLogger;
|
8 |
-
|
9 |
-
class WebPConvertAndServe
|
10 |
-
{
|
11 |
-
public static $CONVERTED_IMAGE = 1;
|
12 |
-
public static $ORIGINAL = -1;
|
13 |
-
public static $HTTP_404 = -2;
|
14 |
-
public static $REPORT_AS_IMAGE = -3;
|
15 |
-
public static $REPORT = -4;
|
16 |
-
|
17 |
-
private static function serve404()
|
18 |
-
{
|
19 |
-
$protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
|
20 |
-
header($protocol . " 404 Not Found");
|
21 |
-
}
|
22 |
-
|
23 |
-
private static function serveOriginal($source)
|
24 |
-
{
|
25 |
-
// Prevent caching image
|
26 |
-
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
27 |
-
header("Cache-Control: post-check=0, pre-check=0", false);
|
28 |
-
header("Pragma: no-cache");
|
29 |
-
|
30 |
-
$arr = explode('.', $source);
|
31 |
-
$ext = array_pop($arr);
|
32 |
-
switch (strtolower($ext)) {
|
33 |
-
case 'jpg':
|
34 |
-
case 'jpeg':
|
35 |
-
header('Content-type: image/jpeg');
|
36 |
-
break;
|
37 |
-
case 'png':
|
38 |
-
header('Content-type: image/png');
|
39 |
-
break;
|
40 |
-
}
|
41 |
-
readfile($source);
|
42 |
-
}
|
43 |
-
|
44 |
-
private static function serveErrorMessageImage($msg)
|
45 |
-
{
|
46 |
-
// Generate image containing error message
|
47 |
-
header('Content-type: image/gif');
|
48 |
-
|
49 |
-
// Prevent caching image
|
50 |
-
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
51 |
-
header("Cache-Control: post-check=0, pre-check=0", false);
|
52 |
-
header("Pragma: no-cache");
|
53 |
-
|
54 |
-
$image = imagecreatetruecolor(620, 200);
|
55 |
-
imagestring($image, 1, 5, 5, $msg, imagecolorallocate($image, 233, 214, 291));
|
56 |
-
// echo imagewebp($image);
|
57 |
-
echo imagegif($image);
|
58 |
-
imagedestroy($image);
|
59 |
-
}
|
60 |
-
|
61 |
-
|
62 |
-
public static function convertAndServeImage($source, $destination, $options, $failAction, $criticalFailAction, $debug = false)
|
63 |
-
{
|
64 |
-
if ($debug) {
|
65 |
-
error_reporting(E_ALL);
|
66 |
-
ini_set('display_errors', 'On');
|
67 |
-
} else {
|
68 |
-
ini_set('display_errors', 'Off');
|
69 |
-
}
|
70 |
-
|
71 |
-
$criticalFail = false;
|
72 |
-
|
73 |
-
$success = false;
|
74 |
-
|
75 |
-
$bufferLogger = new BufferLogger();
|
76 |
-
|
77 |
-
try {
|
78 |
-
$success = WebPConvert::convert($source, $destination, $options, $bufferLogger);
|
79 |
-
|
80 |
-
if ($success) {
|
81 |
-
$status = 'Success';
|
82 |
-
$msg = 'Success';
|
83 |
-
} else {
|
84 |
-
$status = 'Failure (no converters are operational)';
|
85 |
-
$msg = 'No converters are operational';
|
86 |
-
}
|
87 |
-
} catch (\WebPConvert\Exceptions\InvalidFileExtensionException $e) {
|
88 |
-
$criticalFail = true;
|
89 |
-
$status = 'Failure (invalid file extension)';
|
90 |
-
$msg = $e->getMessage();
|
91 |
-
} catch (\WebPConvert\Exceptions\TargetNotFoundException $e) {
|
92 |
-
$criticalFail = true;
|
93 |
-
$status = 'Failure (target file not found)';
|
94 |
-
$msg = $e->getMessage();
|
95 |
-
} catch (\WebPConvert\Converters\Exceptions\ConverterFailedException $e) {
|
96 |
-
// No converters could convert the image. At least one converter failed, even though it appears to be operational
|
97 |
-
$status = 'Failure (no converters could convert the image)';
|
98 |
-
$msg = $e->getMessage();
|
99 |
-
} catch (\WebPConvert\Converters\Exceptions\ConversionDeclinedException $e) {
|
100 |
-
// (no converters could convert the image. At least one converter declined
|
101 |
-
$status = 'Failure (no converters could/wanted to convert the image)';
|
102 |
-
$msg = $e->getMessage();
|
103 |
-
} catch (\WebPConvert\Exceptions\ConverterNotFoundException $e) {
|
104 |
-
$status = 'Failure (a converter was not found!)';
|
105 |
-
$msg = $e->getMessage();
|
106 |
-
} catch (\WebPConvert\Exceptions\CreateDestinationFileException $e) {
|
107 |
-
$status = 'Failure (cannot create destination file)';
|
108 |
-
$msg = $e->getMessage();
|
109 |
-
} catch (\WebPConvert\Exceptions\CreateDestinationFolderException $e) {
|
110 |
-
$status = 'Failure (cannot create destination folder)';
|
111 |
-
$msg = $e->getMessage();
|
112 |
-
} catch (\Exception $e) {
|
113 |
-
$status = 'Failure (an unanticipated exception was thrown)';
|
114 |
-
$msg = $e->getMessage();
|
115 |
-
}
|
116 |
-
|
117 |
-
$optionsForPrint = [];
|
118 |
-
foreach (self::getPrintableOptions($options) as $optionName => $optionValue) {
|
119 |
-
if ($optionName == 'converters') {
|
120 |
-
$converterNames = [];
|
121 |
-
$extraConvertOptions = [];
|
122 |
-
foreach ($optionValue as $converter) {
|
123 |
-
if (is_array($converter)) {
|
124 |
-
$converterNames[] = $converter['converter'];
|
125 |
-
if (isset($converter['options'])) {
|
126 |
-
$extraConvertOptions[$converter['converter']] = $converter['options'];
|
127 |
-
}
|
128 |
-
} else {
|
129 |
-
$converterNames[] = $converter;
|
130 |
-
}
|
131 |
-
}
|
132 |
-
$optionsForPrint[] = 'converters:' . implode(',', $converterNames);
|
133 |
-
foreach ($extraConvertOptions as $converter => $extraOptions) {
|
134 |
-
$opt = [];
|
135 |
-
foreach ($extraOptions as $oName => $oValue) {
|
136 |
-
$opt[] = $oName . ':"' . $oValue . '"';
|
137 |
-
}
|
138 |
-
$optionsForPrint[] = $converter . ' options:(' . implode($opt, ', ') . ')';
|
139 |
-
}
|
140 |
-
} else {
|
141 |
-
$optionsForPrint[] = $optionName . ':' . $optionValue ;
|
142 |
-
}
|
143 |
-
|
144 |
-
}
|
145 |
-
|
146 |
-
header('X-WebP-Convert-And-Serve-Options:' . implode('. ', $optionsForPrint));
|
147 |
-
|
148 |
-
header('X-WebP-Convert-And-Serve-Status: ' . $status);
|
149 |
-
|
150 |
-
// Next line is commented out, because we need to be absolute sure that the details does not violate header syntax
|
151 |
-
// We could either try to filter it, or we could change WebPConvert, such that it only provides safe texts.
|
152 |
-
// header('X-WebP-Convert-And-Serve-Details: ' . $bufferLogger->getText());
|
153 |
-
|
154 |
-
if ($success) {
|
155 |
-
header('Content-type: image/webp');
|
156 |
-
// Should we add Content-Length header?
|
157 |
-
// header('Content-Length: ' . filesize($file));
|
158 |
-
readfile($destination);
|
159 |
-
return self::$CONVERTED_IMAGE;
|
160 |
-
} else {
|
161 |
-
$action = ($criticalFail ? $criticalFailAction : $failAction);
|
162 |
-
|
163 |
-
switch ($action) {
|
164 |
-
case WebPConvertAndServe::$ORIGINAL:
|
165 |
-
self::serveOriginal($source);
|
166 |
-
break;
|
167 |
-
case WebPConvertAndServe::$HTTP_404:
|
168 |
-
self::serve404();
|
169 |
-
break;
|
170 |
-
case WebPConvertAndServe::$REPORT_AS_IMAGE:
|
171 |
-
self::serveErrorMessageImage($status . '. ' . $msg);
|
172 |
-
break;
|
173 |
-
case WebPConvertAndServe::$REPORT:
|
174 |
-
echo '<h1>' . $status . '</h1>';
|
175 |
-
echo $msg;
|
176 |
-
echo '<p>This is how conversion process went:</p>' . $bufferLogger->getHtml();
|
177 |
-
break;
|
178 |
-
}
|
179 |
-
return $action;
|
180 |
-
}
|
181 |
-
}
|
182 |
-
|
183 |
-
/* Hides sensitive options */
|
184 |
-
private static function getPrintableOptions($options)
|
185 |
-
{
|
186 |
-
|
187 |
-
$printable_options = [];
|
188 |
-
|
189 |
-
// (psst: the is_callable check is needed in order to work with WebPConvert v1.0)
|
190 |
-
if (is_callable('ConverterHelper', 'getClassNameOfConverter')) {
|
191 |
-
|
192 |
-
$printable_options = $options;
|
193 |
-
if (isset($printable_options['converters'])) {
|
194 |
-
foreach ($printable_options['converters'] as &$converter) {
|
195 |
-
if (is_array($converter)) {
|
196 |
-
//echo '::' . $converter['converter'] . '<br>';
|
197 |
-
$className = ConverterHelper::getClassNameOfConverter($converter['converter']);
|
198 |
-
|
199 |
-
// (pstt: the isset check is needed in order to work with WebPConvert v1.0)
|
200 |
-
if (isset($className::$extraOptions)) {
|
201 |
-
foreach ($className::$extraOptions as $extraOption) {
|
202 |
-
if ($extraOption['sensitive']) {
|
203 |
-
if (isset($converter['options'][$extraOption['name']])) {
|
204 |
-
$converter['options'][$extraOption['name']] = '*******';
|
205 |
-
}
|
206 |
-
}
|
207 |
-
}
|
208 |
-
}
|
209 |
-
}
|
210 |
-
}
|
211 |
-
}
|
212 |
-
}
|
213 |
-
return $printable_options;
|
214 |
-
}
|
215 |
-
|
216 |
-
public static function convertAndReport($source, $destination, $options)
|
217 |
-
{
|
218 |
-
error_reporting(E_ALL);
|
219 |
-
ini_set('display_errors', 'On');
|
220 |
-
|
221 |
-
echo '<html><style>td {vertical-align: top} table {color: #666}</style>';
|
222 |
-
echo '<body><table>';
|
223 |
-
echo '<tr><td><i>source:</i></td><td>' . $source . '</td></tr>';
|
224 |
-
echo '<tr><td><i>destination:</i></td><td>' . $destination . '<td></tr>';
|
225 |
-
|
226 |
-
echo '<tr><td><i>options:</i></td><td>' . print_r(self::getPrintableOptions($options), true) . '</td></tr>';
|
227 |
-
echo '</table>';
|
228 |
-
|
229 |
-
// TODO:
|
230 |
-
// We could display warning if unknown options are set
|
231 |
-
// but that requires that WebPConvert also describes its general options
|
232 |
-
|
233 |
-
echo '<br>';
|
234 |
-
|
235 |
-
try {
|
236 |
-
$echoLogger = new \WebPConvert\Loggers\EchoLogger();
|
237 |
-
$success = WebPConvert::convert($source, $destination, $options, $echoLogger);
|
238 |
-
} catch (\Exception $e) {
|
239 |
-
$success = false;
|
240 |
-
|
241 |
-
$msg = $e->getMessage();
|
242 |
-
|
243 |
-
echo '<b>' . $msg . '</b>';
|
244 |
-
exit;
|
245 |
-
}
|
246 |
-
|
247 |
-
if ($success) {
|
248 |
-
//echo 'ok';
|
249 |
-
} else {
|
250 |
-
echo '<b>Conversion failed. None of the tried converters are operational</b>';
|
251 |
-
}
|
252 |
-
echo '</body></html>';
|
253 |
-
}
|
254 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/ConverterHelper.php
DELETED
@@ -1,260 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters;
|
4 |
-
|
5 |
-
//use WebPConvert\Converters\Cwebp;
|
6 |
-
|
7 |
-
use WebPConvert\Exceptions\ConverterNotFoundException;
|
8 |
-
use WebPConvert\Exceptions\CreateDestinationFileException;
|
9 |
-
use WebPConvert\Exceptions\CreateDestinationFolderException;
|
10 |
-
use WebPConvert\Exceptions\InvalidFileExtensionException;
|
11 |
-
use WebPConvert\Exceptions\TargetNotFoundException;
|
12 |
-
|
13 |
-
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
14 |
-
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
15 |
-
|
16 |
-
class ConverterHelper
|
17 |
-
{
|
18 |
-
public static $allowedExtensions = ['jpg', 'jpeg', 'png'];
|
19 |
-
|
20 |
-
public static $defaultOptions = [
|
21 |
-
'quality' => 'auto',
|
22 |
-
'max-quality' => 85,
|
23 |
-
'default-quality' => 80,
|
24 |
-
'metadata' => 'none',
|
25 |
-
'method' => 6,
|
26 |
-
'low-memory' => false,
|
27 |
-
'lossless' => false,
|
28 |
-
'converters' => ['cwebp', 'gd', 'imagick']
|
29 |
-
];
|
30 |
-
|
31 |
-
public static function mergeOptions($options, $extraOptions)
|
32 |
-
{
|
33 |
-
return $options;
|
34 |
-
}
|
35 |
-
|
36 |
-
public static function getClassNameOfConverter($converterId)
|
37 |
-
{
|
38 |
-
return 'WebPConvert\\Converters\\' . ucfirst($converterId);
|
39 |
-
}
|
40 |
-
|
41 |
-
|
42 |
-
/* Call the "convert" method on a converter, by id.
|
43 |
-
- but also prepares options (merges in the $extraOptions of the converter),
|
44 |
-
prepares destination folder, and runs some standard validations */
|
45 |
-
public static function runConverter($converterId, $source, $destination, $options = [], $prepareDestinationFolder = true, $logger = null)
|
46 |
-
{
|
47 |
-
if ($prepareDestinationFolder) {
|
48 |
-
ConverterHelper::prepareDestinationFolderAndRunCommonValidations($source, $destination);
|
49 |
-
}
|
50 |
-
|
51 |
-
if (!isset($logger)) {
|
52 |
-
$logger = new \WebPConvert\Loggers\VoidLogger();
|
53 |
-
}
|
54 |
-
|
55 |
-
$className = self::getClassNameOfConverter($converterId);
|
56 |
-
if (!is_callable([$className, 'convert'])) {
|
57 |
-
throw new ConverterNotFoundException();
|
58 |
-
}
|
59 |
-
|
60 |
-
// Prepare options.
|
61 |
-
// - Remove 'converters'
|
62 |
-
$defaultOptions = self::$defaultOptions;
|
63 |
-
unset($defaultOptions['converters']);
|
64 |
-
|
65 |
-
// - Merge defaults of the converters extra options into the standard default options.
|
66 |
-
$defaultOptions = array_merge($defaultOptions, array_column($className::$extraOptions, 'default', 'name'));
|
67 |
-
|
68 |
-
// - Merge $defaultOptions into provided options
|
69 |
-
$options = array_merge($defaultOptions, $options);
|
70 |
-
|
71 |
-
// Individual converters do not accept quality = auto. They need a number.
|
72 |
-
// Change $options['quality'] to number, based on quality of source and several settings
|
73 |
-
|
74 |
-
self::processQualityOption($source, $options, $logger);
|
75 |
-
|
76 |
-
call_user_func(
|
77 |
-
[$className, 'doConvert'],
|
78 |
-
$source,
|
79 |
-
$destination,
|
80 |
-
$options,
|
81 |
-
$logger
|
82 |
-
);
|
83 |
-
|
84 |
-
if (!file_exists($destination)) {
|
85 |
-
throw new ConverterFailedException('Destination file is not there');
|
86 |
-
}
|
87 |
-
}
|
88 |
-
|
89 |
-
/* Try to detect quality of jpeg.
|
90 |
-
If not possible, nothing is returned (null). Otherwise quality is returned (int)
|
91 |
-
*/
|
92 |
-
public static function detectQualityOfJpg($filename)
|
93 |
-
{
|
94 |
-
// Try Imagick extension
|
95 |
-
if (extension_loaded('imagick') && class_exists('Imagick')) {
|
96 |
-
$img = new Imagick($filename);
|
97 |
-
|
98 |
-
// The required function is available as from PECL imagick v2.2.2
|
99 |
-
if (method_exists($img, 'getImageCompressionQuality')) {
|
100 |
-
return $img->getImageCompressionQuality();
|
101 |
-
}
|
102 |
-
}
|
103 |
-
|
104 |
-
if (function_exists('shell_exec')) {
|
105 |
-
|
106 |
-
// Try Imagick
|
107 |
-
$quality = shell_exec("identify -format '%Q' " . $filename);
|
108 |
-
if ($quality) {
|
109 |
-
return intval($quality);
|
110 |
-
}
|
111 |
-
|
112 |
-
// Try GraphicsMagick
|
113 |
-
$quality = shell_exec("gm identify -format '%Q' " . $filename);
|
114 |
-
if ($quality) {
|
115 |
-
return intval($quality);
|
116 |
-
}
|
117 |
-
}
|
118 |
-
}
|
119 |
-
|
120 |
-
public static function processQualityOption($source, &$options, $logger)
|
121 |
-
{
|
122 |
-
if (isset($options['_calculated_quality'])) {
|
123 |
-
return;
|
124 |
-
}
|
125 |
-
if ($options['quality'] == 'auto') {
|
126 |
-
$q = self::detectQualityOfJpg($source);
|
127 |
-
//$logger->log('Quality set to auto... Quality of source: ');
|
128 |
-
if (!$q) {
|
129 |
-
$q = $options['default-quality'];
|
130 |
-
$logger->logLn('Quality of source could not be established (Imagick or GraphicsMagick is required) - Using default instead (' . $options['default-quality'] . ').');
|
131 |
-
|
132 |
-
// this allows the wpc converter to know
|
133 |
-
$options['_quality_could_not_be_detected'] = true;
|
134 |
-
} else {
|
135 |
-
if ($q > $options['max-quality']) {
|
136 |
-
$logger->log('Quality of source is ' . $q . '. This is higher than max-quality, so using that instead (' . $options['max-quality'] . ')');
|
137 |
-
} else {
|
138 |
-
$logger->log('Quality set to same as source: ' . $q);
|
139 |
-
}
|
140 |
-
}
|
141 |
-
$logger->ln();
|
142 |
-
$q = min($q, $options['max-quality']);
|
143 |
-
|
144 |
-
$options['_calculated_quality'] = $q;
|
145 |
-
//$logger->logLn('Using quality: ' . $options['quality']);
|
146 |
-
} else {
|
147 |
-
$logger->logLn('Quality: ' . $options['quality'] . '. Consider setting quality to "auto" instead. It is generally a better idea');
|
148 |
-
$options['_calculated_quality'] = $options['quality'];
|
149 |
-
}
|
150 |
-
$logger->ln();
|
151 |
-
}
|
152 |
-
|
153 |
-
|
154 |
-
public static function getExtension($filePath)
|
155 |
-
{
|
156 |
-
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
|
157 |
-
return strtolower($fileExtension);
|
158 |
-
}
|
159 |
-
|
160 |
-
// Throws an exception if the provided file doesn't exist
|
161 |
-
public static function isValidTarget($filePath)
|
162 |
-
{
|
163 |
-
if (!file_exists($filePath)) {
|
164 |
-
throw new TargetNotFoundException('File or directory not found: ' . $filePath);
|
165 |
-
}
|
166 |
-
|
167 |
-
return true;
|
168 |
-
}
|
169 |
-
|
170 |
-
// Throws an exception if the provided file's extension is invalid
|
171 |
-
public static function isAllowedExtension($filePath)
|
172 |
-
{
|
173 |
-
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
|
174 |
-
if (!in_array(strtolower($fileExtension), self::$allowedExtensions)) {
|
175 |
-
throw new InvalidFileExtensionException('Unsupported file extension: ' . $fileExtension);
|
176 |
-
}
|
177 |
-
|
178 |
-
return true;
|
179 |
-
}
|
180 |
-
|
181 |
-
// Creates folder in provided path & sets correct permissions
|
182 |
-
public static function createWritableFolder($filePath)
|
183 |
-
{
|
184 |
-
$folder = pathinfo($filePath, PATHINFO_DIRNAME);
|
185 |
-
if (!file_exists($folder)) {
|
186 |
-
// TODO: what if this is outside open basedir?
|
187 |
-
// see http://php.net/manual/en/ini.core.php#ini.open-basedir
|
188 |
-
|
189 |
-
// First, we have to figure out which permissions to set.
|
190 |
-
// We want same permissions as parent folder
|
191 |
-
// But which parent? - the parent to the first missing folder
|
192 |
-
|
193 |
-
$parentFolders = explode('/', $folder);
|
194 |
-
$poppedFolders = [];
|
195 |
-
|
196 |
-
while (!(file_exists(implode('/', $parentFolders))) && count($parentFolders) > 0) {
|
197 |
-
array_unshift($poppedFolders, array_pop($parentFolders));
|
198 |
-
}
|
199 |
-
|
200 |
-
// Retrieving permissions of closest existing folder
|
201 |
-
$closestExistingFolder = implode('/', $parentFolders);
|
202 |
-
$permissions = fileperms($closestExistingFolder) & 000777;
|
203 |
-
|
204 |
-
// Trying to create the given folder
|
205 |
-
// Notice: mkdir emits a warning on failure. It would be nice to suppress that, if possible
|
206 |
-
if (!mkdir($folder, $permissions, true)) {
|
207 |
-
throw new CreateDestinationFolderException('Failed creating folder: ' . $folder);
|
208 |
-
}
|
209 |
-
|
210 |
-
|
211 |
-
// `mkdir` doesn't respect permissions, so we have to `chmod` each created subfolder
|
212 |
-
foreach ($poppedFolders as $subfolder) {
|
213 |
-
$closestExistingFolder .= '/' . $subfolder;
|
214 |
-
// Setting directory permissions
|
215 |
-
chmod($folder, $permissions);
|
216 |
-
}
|
217 |
-
}
|
218 |
-
|
219 |
-
// Checks if there's a file in $filePath & if writing permissions are correct
|
220 |
-
if (file_exists($filePath) && !is_writable($filePath)) {
|
221 |
-
throw new CreateDestinationFileException('Cannot overwrite ' . basename($filePath) . ' - check file permissions.');
|
222 |
-
}
|
223 |
-
|
224 |
-
// There's either a rewritable file in $filePath or none at all.
|
225 |
-
// If there is, simply attempt to delete it
|
226 |
-
if (file_exists($filePath) && !unlink($filePath)) {
|
227 |
-
throw new CreateDestinationFileException('Existing file cannot be removed: ' . basename($filePath));
|
228 |
-
}
|
229 |
-
|
230 |
-
return true;
|
231 |
-
}
|
232 |
-
|
233 |
-
public static function prepareDestinationFolderAndRunCommonValidations($source, $destination)
|
234 |
-
{
|
235 |
-
self::isValidTarget($source);
|
236 |
-
self::isAllowedExtension($source);
|
237 |
-
self::createWritableFolder($destination);
|
238 |
-
}
|
239 |
-
|
240 |
-
public static function initCurlForConverter()
|
241 |
-
{
|
242 |
-
if (!extension_loaded('curl')) {
|
243 |
-
throw new ConverterNotOperationalException('Required cURL extension is not available.');
|
244 |
-
}
|
245 |
-
|
246 |
-
if (!function_exists('curl_init')) {
|
247 |
-
throw new ConverterNotOperationalException('Required url_init() function is not available.');
|
248 |
-
}
|
249 |
-
|
250 |
-
if (!function_exists('curl_file_create')) {
|
251 |
-
throw new ConverterNotOperationalException('Required curl_file_create() function is not available (requires PHP > 5.5).');
|
252 |
-
}
|
253 |
-
|
254 |
-
$ch = curl_init();
|
255 |
-
if (!$ch) {
|
256 |
-
throw new ConverterNotOperationalException('Could not initialise cURL.');
|
257 |
-
}
|
258 |
-
return $ch;
|
259 |
-
}
|
260 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Cwebp.php
DELETED
@@ -1,259 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters;
|
4 |
-
|
5 |
-
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
6 |
-
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
7 |
-
|
8 |
-
class Cwebp
|
9 |
-
{
|
10 |
-
public static $extraOptions = [
|
11 |
-
[
|
12 |
-
'name' => 'use-nice',
|
13 |
-
'type' => 'boolean',
|
14 |
-
'sensitive' => false,
|
15 |
-
'default' => false,
|
16 |
-
'required' => false
|
17 |
-
],
|
18 |
-
];
|
19 |
-
|
20 |
-
public static function convert($source, $destination, $options = [])
|
21 |
-
{
|
22 |
-
ConverterHelper::runConverter('cwebp', $source, $destination, $options, true);
|
23 |
-
}
|
24 |
-
|
25 |
-
// System paths to look for cwebp binary
|
26 |
-
private static $cwebpDefaultPaths = [
|
27 |
-
'/usr/bin/cwebp',
|
28 |
-
'/usr/local/bin/cwebp',
|
29 |
-
'/usr/gnu/bin/cwebp',
|
30 |
-
'/usr/syno/bin/cwebp'
|
31 |
-
];
|
32 |
-
|
33 |
-
// OS-specific binaries included in this library, along with hashes
|
34 |
-
private static $suppliedBinariesInfo = [
|
35 |
-
'WinNT' => [ 'cwebp.exe', '49e9cb98db30bfa27936933e6fd94d407e0386802cb192800d9fd824f6476873'],
|
36 |
-
'Darwin' => [ 'cwebp-mac12', 'a06a3ee436e375c89dbc1b0b2e8bd7729a55139ae072ed3f7bd2e07de0ebb379'],
|
37 |
-
'SunOS' => [ 'cwebp-sol', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f'],
|
38 |
-
'FreeBSD' => [ 'cwebp-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573'],
|
39 |
-
'Linux' => [ 'cwebp-linux', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568']
|
40 |
-
];
|
41 |
-
|
42 |
-
private static function escapeFilename($string)
|
43 |
-
{
|
44 |
-
// Escaping whitespace
|
45 |
-
$string = preg_replace('/\s/', '\\ ', $string);
|
46 |
-
|
47 |
-
// filter_var() is should normally be available, but it is not always
|
48 |
-
// - https://stackoverflow.com/questions/11735538/call-to-undefined-function-filter-var
|
49 |
-
if (function_exists('filter_var')) {
|
50 |
-
// Sanitize quotes
|
51 |
-
$string = filter_var($string, FILTER_SANITIZE_MAGIC_QUOTES);
|
52 |
-
|
53 |
-
// Stripping control characters
|
54 |
-
// see https://stackoverflow.com/questions/12769462/filter-flag-strip-low-vs-filter-flag-strip-high
|
55 |
-
$string = filter_var($string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
|
56 |
-
}
|
57 |
-
|
58 |
-
return $string;
|
59 |
-
}
|
60 |
-
|
61 |
-
// Checks if 'Nice' is available
|
62 |
-
private static function hasNiceSupport()
|
63 |
-
{
|
64 |
-
exec("nice 2>&1", $niceOutput);
|
65 |
-
|
66 |
-
if (is_array($niceOutput) && isset($niceOutput[0])) {
|
67 |
-
if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
|
68 |
-
/*
|
69 |
-
* Nice is available - default niceness (+10)
|
70 |
-
* https://www.lifewire.com/uses-of-commands-nice-renice-2201087
|
71 |
-
* https://www.computerhope.com/unix/unice.htm
|
72 |
-
*/
|
73 |
-
|
74 |
-
return true;
|
75 |
-
}
|
76 |
-
|
77 |
-
return false;
|
78 |
-
}
|
79 |
-
}
|
80 |
-
|
81 |
-
//
|
82 |
-
private static function executeBinary($binary, $commandOptions, $useNice, $logger)
|
83 |
-
{
|
84 |
-
$command = ($useNice ? 'nice ' : '') . $binary . ' ' . $commandOptions;
|
85 |
-
|
86 |
-
$logger->logLn('Trying to execute binary:' . $binary);
|
87 |
-
//$logger->logLn();
|
88 |
-
|
89 |
-
exec($command, $output, $returnCode);
|
90 |
-
|
91 |
-
switch ($returnCode) {
|
92 |
-
case 0:
|
93 |
-
$logger->logLn('Success!');
|
94 |
-
break;
|
95 |
-
case 126:
|
96 |
-
$logger->logLn('Permission denied. The user that the command was run with (' . shell_exec('whoami') . ') does not have permission to execute that binary.');
|
97 |
-
break;
|
98 |
-
case 127:
|
99 |
-
$logger->logLn('No binary found at that location');
|
100 |
-
break;
|
101 |
-
default:
|
102 |
-
$logger->logLn('Failed. Return code:' . $returnCode . '. See http://tldp.org/LDP/abs/html/exitcodes.html for failcodes');
|
103 |
-
}
|
104 |
-
return $returnCode;
|
105 |
-
}
|
106 |
-
|
107 |
-
// Although this method is public, do not call directly.
|
108 |
-
public static function doConvert($source, $destination, $options = [], $logger)
|
109 |
-
{
|
110 |
-
$errorMsg = '';
|
111 |
-
// Force lossless option to true for PNG images
|
112 |
-
if (ConverterHelper::getExtension($source) == 'png') {
|
113 |
-
$options['lossless'] = true;
|
114 |
-
}
|
115 |
-
|
116 |
-
if (!function_exists('exec')) {
|
117 |
-
throw new ConverterNotOperationalException('exec() is not enabled.');
|
118 |
-
}
|
119 |
-
|
120 |
-
/*
|
121 |
-
* Prepare cwebp options
|
122 |
-
*/
|
123 |
-
|
124 |
-
// Metadata (all, exif, icc, xmp or none (default))
|
125 |
-
// Comma-separated list of existing metadata to copy from input to output
|
126 |
-
$metadata = '-metadata ' . $options['metadata'];
|
127 |
-
|
128 |
-
// Image quality
|
129 |
-
$quality = '-q ' . $options['_calculated_quality'];
|
130 |
-
|
131 |
-
// Losless PNG conversion
|
132 |
-
$lossless = ($options['lossless'] ? '-lossless' : '');
|
133 |
-
|
134 |
-
// Built-in method option
|
135 |
-
$method = ' -m ' . strval($options['method']);
|
136 |
-
|
137 |
-
|
138 |
-
// TODO:
|
139 |
-
// Why not use -af ? (https://developers.google.com/speed/webp/docs/cwebp)
|
140 |
-
// Would it be possible get a quality similar to source?
|
141 |
-
// It seems so: "identify -format '%Q' yourimage.jpg" (https://stackoverflow.com/questions/2024947/is-it-possible-to-tell-the-quality-level-of-a-jpeg)
|
142 |
-
// -- With -jpeg_like option, or perhaps the -size option
|
143 |
-
|
144 |
-
// Built-in low memory option
|
145 |
-
$lowMemory = '';
|
146 |
-
if ($options['low-memory']) {
|
147 |
-
$lowMemory = '-low_memory';
|
148 |
-
}
|
149 |
-
|
150 |
-
$commandOptionsArray = [
|
151 |
-
$metadata = $metadata,
|
152 |
-
$quality = $quality,
|
153 |
-
$lossless = $lossless,
|
154 |
-
$method = $method,
|
155 |
-
$lowMemory = $lowMemory,
|
156 |
-
$input = self::escapeFilename($source),
|
157 |
-
$output = '-o ' . self::escapeFilename($destination),
|
158 |
-
$stderrRedirect = '2>&1'
|
159 |
-
];
|
160 |
-
|
161 |
-
$useNice = (($options['use-nice']) && self::hasNiceSupport()) ? true : false;
|
162 |
-
|
163 |
-
$commandOptions = implode(' ', $commandOptionsArray);
|
164 |
-
|
165 |
-
|
166 |
-
// Init with common system paths
|
167 |
-
$cwebpPathsToTest = self::$cwebpDefaultPaths;
|
168 |
-
|
169 |
-
// Remove paths that doesn't exist
|
170 |
-
$cwebpPathsToTest = array_filter($cwebpPathsToTest, function ($binary) {
|
171 |
-
//return file_exists($binary);
|
172 |
-
return @is_readable($binary);
|
173 |
-
});
|
174 |
-
|
175 |
-
// Try all common paths that exitst
|
176 |
-
$success = false;
|
177 |
-
foreach ($cwebpPathsToTest as $index => $binary) {
|
178 |
-
$success = (self::executeBinary($binary, $commandOptions, $useNice, $logger) == 0);
|
179 |
-
if ($success) {
|
180 |
-
break;
|
181 |
-
}
|
182 |
-
}
|
183 |
-
if (!$success) {
|
184 |
-
//$logger->logLn('');
|
185 |
-
if (count($cwebpPathsToTest) > 0) {
|
186 |
-
$errorMsg .= 'Found cwebp binaries at these locations: "' . implode('", "', $cwebpPathsToTest) . '". However, executing these failed. ';
|
187 |
-
} else {
|
188 |
-
$errorMsg .= 'Found no cwebp binaries in any common locations. ';
|
189 |
-
}
|
190 |
-
}
|
191 |
-
|
192 |
-
if (!$success) {
|
193 |
-
|
194 |
-
// Try supplied binary (if available for OS, and hash is correct)
|
195 |
-
if (isset(self::$suppliedBinariesInfo[PHP_OS])) {
|
196 |
-
$info = self::$suppliedBinariesInfo[PHP_OS];
|
197 |
-
|
198 |
-
$file = $info[0];
|
199 |
-
$hash = $info[1];
|
200 |
-
|
201 |
-
$binaryFile = __DIR__ . '/Binaries/' . $file;
|
202 |
-
|
203 |
-
// The file should exist, but may have been removed manually.
|
204 |
-
if (file_exists($binaryFile)) {
|
205 |
-
// File exists, now generate its hash
|
206 |
-
|
207 |
-
// hash_file() is normally available, but it is not always
|
208 |
-
// - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash
|
209 |
-
// If available, validate that hash is correct.
|
210 |
-
$proceedAfterHashCheck = true;
|
211 |
-
if (function_exists('hash_file')) {
|
212 |
-
$binaryHash = hash_file('sha256', $binaryFile);
|
213 |
-
|
214 |
-
if ($binaryHash != $hash) {
|
215 |
-
$errorMsg .= 'Binary checksum of supplied binary is invalid! Did you transfer with FTP, but not in binary mode? File:' . $binaryFile . '. Expected checksum: ' . $hash . ' Actual checksum:' . $binaryHash . '. ';
|
216 |
-
$proceedAfterHashCheck = false;
|
217 |
-
}
|
218 |
-
}
|
219 |
-
if ($proceedAfterHashCheck) {
|
220 |
-
$returnCode = self::executeBinary($binaryFile, $commandOptions, $useNice, $logger);
|
221 |
-
if ($returnCode == 0) {
|
222 |
-
$success = true;
|
223 |
-
} else {
|
224 |
-
$errorMsg .= 'Tried executing supplied binary (' . $binaryFile . '), but that failed too: ';
|
225 |
-
switch ($returnCode) {
|
226 |
-
case 126:
|
227 |
-
$errorMsg .= 'Permission denied (user "' . trim(shell_exec('whoami')) . '" does not have permission to execute the binary)';
|
228 |
-
break;
|
229 |
-
default:
|
230 |
-
$errorMsg .= 'Fail code: ' . $returnCode;
|
231 |
-
}
|
232 |
-
}
|
233 |
-
}
|
234 |
-
} else {
|
235 |
-
$errorMsg .= 'Supplied binary not found:' . $binaryFile;
|
236 |
-
}
|
237 |
-
} else {
|
238 |
-
$errorMsg .= 'No supplied binaries found for OS:' . PHP_OS;
|
239 |
-
}
|
240 |
-
}
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
// cwebp sets file permissions to 664 but instead ..
|
245 |
-
// .. $destination's parent folder's permissions should be used (except executable bits)
|
246 |
-
if ($success) {
|
247 |
-
$destinationParent = dirname($destination);
|
248 |
-
$fileStatistics = stat($destinationParent);
|
249 |
-
|
250 |
-
// Apply same permissions as parent folder but strip off the executable bits
|
251 |
-
$permissions = $fileStatistics['mode'] & 0000666;
|
252 |
-
chmod($destination, $permissions);
|
253 |
-
}
|
254 |
-
|
255 |
-
if (!$success) {
|
256 |
-
throw new ConverterNotOperationalException($errorMsg);
|
257 |
-
}
|
258 |
-
}
|
259 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Ewww.php
DELETED
@@ -1,197 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters;
|
4 |
-
|
5 |
-
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
6 |
-
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
7 |
-
|
8 |
-
class Ewww
|
9 |
-
{
|
10 |
-
public static $extraOptions = [
|
11 |
-
[
|
12 |
-
'name' => 'key',
|
13 |
-
'type' => 'string',
|
14 |
-
'sensitive' => true,
|
15 |
-
'default' => '',
|
16 |
-
'required' => true
|
17 |
-
],
|
18 |
-
];
|
19 |
-
|
20 |
-
public static function convert($source, $destination, $options = [])
|
21 |
-
{
|
22 |
-
ConverterHelper::runConverter('ewww', $source, $destination, $options, true);
|
23 |
-
}
|
24 |
-
|
25 |
-
// Although this method is public, do not call directly.
|
26 |
-
public static function doConvert($source, $destination, $options = [], $logger)
|
27 |
-
{
|
28 |
-
if ($options['key'] == '') {
|
29 |
-
throw new ConverterNotOperationalException('Missing API key.');
|
30 |
-
}
|
31 |
-
if (strlen($options['key']) < 20) {
|
32 |
-
throw new ConverterNotOperationalException('Key is invalid. Keys are supposed to be 32 characters long - your key is much shorter');
|
33 |
-
}
|
34 |
-
|
35 |
-
$keyStatus = self::getKeyStatus($options['key']);
|
36 |
-
switch ($keyStatus) {
|
37 |
-
case 'great':
|
38 |
-
break;
|
39 |
-
case 'exceeded':
|
40 |
-
throw new ConverterNotOperationalException('quota has exceeded');
|
41 |
-
break;
|
42 |
-
case 'invalid':
|
43 |
-
throw new ConverterNotOperationalException('key is invalid');
|
44 |
-
break;
|
45 |
-
}
|
46 |
-
|
47 |
-
$ch = ConverterHelper::initCurlForConverter();
|
48 |
-
|
49 |
-
$curlOptions = [
|
50 |
-
'api_key' => $options['key'],
|
51 |
-
'webp' => '1',
|
52 |
-
'file' => curl_file_create($source),
|
53 |
-
'domain' => $_SERVER['HTTP_HOST'],
|
54 |
-
'quality' => $options['_calculated_quality'],
|
55 |
-
'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
|
56 |
-
];
|
57 |
-
|
58 |
-
curl_setopt_array($ch, [
|
59 |
-
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
60 |
-
CURLOPT_HTTPHEADER => [
|
61 |
-
'User-Agent: WebPConvert',
|
62 |
-
'Accept: image/*'
|
63 |
-
],
|
64 |
-
CURLOPT_POSTFIELDS => $curlOptions,
|
65 |
-
CURLOPT_BINARYTRANSFER => true,
|
66 |
-
CURLOPT_RETURNTRANSFER => true,
|
67 |
-
CURLOPT_HEADER => false,
|
68 |
-
CURLOPT_SSL_VERIFYPEER => false
|
69 |
-
]);
|
70 |
-
|
71 |
-
$response = curl_exec($ch);
|
72 |
-
|
73 |
-
if (curl_errno($ch)) {
|
74 |
-
throw new ConverterNotOperationalException(curl_error($ch));
|
75 |
-
}
|
76 |
-
|
77 |
-
// The API does not always return images.
|
78 |
-
// For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
|
79 |
-
// Messages has a http content type of ie 'text/html; charset=UTF-8
|
80 |
-
// Images has application/octet-stream.
|
81 |
-
// So verify that we got an image back.
|
82 |
-
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
83 |
-
|
84 |
-
//echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
85 |
-
curl_close($ch);
|
86 |
-
|
87 |
-
/* May return this: {"error":"invalid","t":"exceeded"} */
|
88 |
-
$responseObj = json_decode($response);
|
89 |
-
if (isset($responseObj->error)) {
|
90 |
-
//echo 'error:' . $responseObj->error . '<br>';
|
91 |
-
//echo $response;
|
92 |
-
//self::blacklistKey($key);
|
93 |
-
//throw new ConverterNotOperationalException('The key is invalid. Blacklisted it!');
|
94 |
-
throw new ConverterNotOperationalException('The key is invalid');
|
95 |
-
}
|
96 |
-
|
97 |
-
throw new ConverterNotOperationalException('ewww api did not return an image. It could be that the key is invalid. Response: ' . $response);
|
98 |
-
}
|
99 |
-
|
100 |
-
// Not sure this can happen. So just in case
|
101 |
-
if ($response == '') {
|
102 |
-
throw new ConverterNotOperationalException('ewww api did not return anything');
|
103 |
-
}
|
104 |
-
|
105 |
-
$success = file_put_contents($destination, $response);
|
106 |
-
|
107 |
-
if (!$success) {
|
108 |
-
throw new ConverterFailedException('Error saving file');
|
109 |
-
}
|
110 |
-
}
|
111 |
-
|
112 |
-
/*
|
113 |
-
public static function blacklistKey($key)
|
114 |
-
{
|
115 |
-
}
|
116 |
-
|
117 |
-
public static function isKeyBlacklisted($key)
|
118 |
-
{
|
119 |
-
}*/
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Return "great", "exceeded" or "invalid"
|
123 |
-
*/
|
124 |
-
public static function getKeyStatus($key)
|
125 |
-
{
|
126 |
-
$ch = ConverterHelper::initCurlForConverter();
|
127 |
-
|
128 |
-
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
|
129 |
-
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
130 |
-
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
131 |
-
'api_key' => $key
|
132 |
-
]);
|
133 |
-
|
134 |
-
// The 403 forbidden is avoided with this line.
|
135 |
-
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)');
|
136 |
-
|
137 |
-
$response = curl_exec($ch);
|
138 |
-
// echo $response;
|
139 |
-
if (curl_errno($ch)) {
|
140 |
-
throw new \Exception(curl_error($ch));
|
141 |
-
}
|
142 |
-
curl_close($ch);
|
143 |
-
|
144 |
-
// Possible responses:
|
145 |
-
// “great” = verification successful
|
146 |
-
// “exceeded” = indicates a valid key with no remaining image credits.
|
147 |
-
// an empty response indicates that the key is not valid
|
148 |
-
|
149 |
-
if ($response == '') {
|
150 |
-
return 'invalid';
|
151 |
-
}
|
152 |
-
$responseObj = json_decode($response);
|
153 |
-
if (isset($responseObj->error)) {
|
154 |
-
if ($responseObj->error == 'invalid') {
|
155 |
-
return 'invalid';
|
156 |
-
} else {
|
157 |
-
throw new \Exception('Ewww returned unexpected error: ' . $response);
|
158 |
-
}
|
159 |
-
}
|
160 |
-
if (!isset($responseObj->status)) {
|
161 |
-
throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
|
162 |
-
}
|
163 |
-
switch ($responseObj->status) {
|
164 |
-
case 'great':
|
165 |
-
case 'exceeded':
|
166 |
-
return $responseObj->status;
|
167 |
-
}
|
168 |
-
throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
|
169 |
-
}
|
170 |
-
|
171 |
-
public static function isWorkingKey($key)
|
172 |
-
{
|
173 |
-
return (self::getKeyStatus($key) == 'great');
|
174 |
-
}
|
175 |
-
|
176 |
-
public static function isValidKey($key)
|
177 |
-
{
|
178 |
-
return (self::getKeyStatus($key) != 'invalid');
|
179 |
-
}
|
180 |
-
|
181 |
-
public static function getQuota($key)
|
182 |
-
{
|
183 |
-
$ch = ConverterHelper::initCurlForConverter();
|
184 |
-
|
185 |
-
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
|
186 |
-
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
187 |
-
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
188 |
-
'api_key' => $key
|
189 |
-
]);
|
190 |
-
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)');
|
191 |
-
|
192 |
-
$response = curl_exec($ch);
|
193 |
-
return $response; // ie -830 23. Seems to return empty for invalid keys
|
194 |
-
// or empty
|
195 |
-
//echo $response;
|
196 |
-
}
|
197 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Exceptions/ConversionDeclinedException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class ConversionDeclinedException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter declined converting';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Exceptions/ConverterFailedException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class ConverterFailedException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter failed converting, although requirements seemed to be met';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Exceptions/ConverterNotOperationalException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class ConverterNotOperationalException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter is not operational';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Gd.php
DELETED
@@ -1,83 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters;
|
4 |
-
|
5 |
-
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
6 |
-
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
7 |
-
use WebPConvert\Converters\Exceptions\ConversionDeclinedException;
|
8 |
-
|
9 |
-
use WebPConvert\Converters\ConverterHelper;
|
10 |
-
|
11 |
-
class Gd
|
12 |
-
{
|
13 |
-
public static $extraOptions = [
|
14 |
-
[
|
15 |
-
'name' => 'skip-pngs',
|
16 |
-
'type' => 'boolean',
|
17 |
-
'sensitive' => false,
|
18 |
-
'default' => true,
|
19 |
-
'required' => false
|
20 |
-
],
|
21 |
-
];
|
22 |
-
|
23 |
-
public static function convert($source, $destination, $options = [])
|
24 |
-
{
|
25 |
-
ConverterHelper::runConverter('gd', $source, $destination, $options, true);
|
26 |
-
}
|
27 |
-
|
28 |
-
// Although this method is public, do not call directly.
|
29 |
-
public static function doConvert($source, $destination, $options = [], $logger)
|
30 |
-
{
|
31 |
-
if (!extension_loaded('gd')) {
|
32 |
-
throw new ConverterNotOperationalException('Required GD extension is not available.');
|
33 |
-
}
|
34 |
-
|
35 |
-
if (!function_exists('imagewebp')) {
|
36 |
-
throw new ConverterNotOperationalException('Required imagewebp() function is not available.');
|
37 |
-
}
|
38 |
-
|
39 |
-
switch (ConverterHelper::getExtension($source)) {
|
40 |
-
case 'png':
|
41 |
-
if (!$options['skip-pngs']) {
|
42 |
-
if (!function_exists('imagecreatefrompng')) {
|
43 |
-
throw new ConverterNotOperationalException('Required imagecreatefrompng() function is not available.');
|
44 |
-
}
|
45 |
-
$image = imagecreatefrompng($source);
|
46 |
-
if (!$image) {
|
47 |
-
throw new ConverterFailedException('imagecreatefrompng("' . $source . '") failed');
|
48 |
-
}
|
49 |
-
} else {
|
50 |
-
throw new ConversionDeclinedException('PNG file skipped. GD is configured not to convert PNGs');
|
51 |
-
}
|
52 |
-
break;
|
53 |
-
default:
|
54 |
-
if (!function_exists('imagecreatefromjpeg')) {
|
55 |
-
throw new ConverterNotOperationalException('Required imagecreatefromjpeg() function is not available.');
|
56 |
-
}
|
57 |
-
$image = imagecreatefromjpeg($source);
|
58 |
-
if (!$image) {
|
59 |
-
throw new ConverterFailedException('imagecreatefromjpeg("' . $source . '") failed');
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
// Checks if either imagecreatefromjpeg() or imagecreatefrompng() returned false
|
64 |
-
|
65 |
-
$success = imagewebp($image, $destination, $options['_calculated_quality']);
|
66 |
-
|
67 |
-
if (!$success) {
|
68 |
-
throw new ConverterFailedException('Call to imagewebp() failed. Probably failed writing file');
|
69 |
-
}
|
70 |
-
|
71 |
-
/*
|
72 |
-
* This hack solves an `imagewebp` bug
|
73 |
-
* See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
|
74 |
-
*
|
75 |
-
*/
|
76 |
-
|
77 |
-
if (filesize($destination) % 2 == 1) {
|
78 |
-
file_put_contents($destination, "\0", FILE_APPEND);
|
79 |
-
}
|
80 |
-
|
81 |
-
imagedestroy($image);
|
82 |
-
}
|
83 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Imagick.php
DELETED
@@ -1,76 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters;
|
4 |
-
|
5 |
-
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
6 |
-
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
7 |
-
|
8 |
-
//use WebPConvert\Exceptions\TargetNotFoundException;
|
9 |
-
|
10 |
-
class Imagick
|
11 |
-
{
|
12 |
-
public static $extraOptions = [];
|
13 |
-
|
14 |
-
public static function convert($source, $destination, $options = [])
|
15 |
-
{
|
16 |
-
ConverterHelper::runConverter('imagick', $source, $destination, $options, true);
|
17 |
-
}
|
18 |
-
|
19 |
-
// Although this method is public, do not call directly.
|
20 |
-
public static function doConvert($source, $destination, $options = [], $logger)
|
21 |
-
{
|
22 |
-
if (!extension_loaded('imagick')) {
|
23 |
-
throw new ConverterNotOperationalException('Required iMagick extension is not available.');
|
24 |
-
}
|
25 |
-
|
26 |
-
if (!class_exists('Imagick')) {
|
27 |
-
throw new ConverterNotOperationalException('iMagick is installed, but not correctly. The class Imagick is not available');
|
28 |
-
}
|
29 |
-
|
30 |
-
$im = new \Imagick($source);
|
31 |
-
|
32 |
-
// Throws an exception if iMagick does not support WebP conversion
|
33 |
-
if (!in_array('WEBP', $im->queryFormats())) {
|
34 |
-
throw new ConverterNotOperationalException('iMagick was compiled without WebP support.');
|
35 |
-
}
|
36 |
-
|
37 |
-
$options = array_merge(ConverterHelper::$defaultOptions, $options);
|
38 |
-
|
39 |
-
// Force lossless option to true for PNG images
|
40 |
-
if (ConverterHelper::getExtension($source) == 'png') {
|
41 |
-
$options['lossless'] = true;
|
42 |
-
}
|
43 |
-
|
44 |
-
$im->setImageFormat('WEBP');
|
45 |
-
|
46 |
-
/*
|
47 |
-
* More about iMagick's WebP options:
|
48 |
-
* http://www.imagemagick.org/script/webp.php
|
49 |
-
* https://developers.google.com/speed/webp/docs/cwebp
|
50 |
-
* https://stackoverflow.com/questions/37711492/imagemagick-specific-webp-calls-in-php
|
51 |
-
*/
|
52 |
-
|
53 |
-
// TODO: We could easily support all webp options with a loop
|
54 |
-
$im->setOption('webp:method', strval($options['method']));
|
55 |
-
$im->setOption('webp:low-memory', strval($options['low-memory']));
|
56 |
-
$im->setOption('webp:lossless', strval($options['lossless']));
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
$im->setImageCompressionQuality($options['_calculated_quality']);
|
61 |
-
|
62 |
-
// TODO:
|
63 |
-
// Should we set alpha channel for PNG's like suggested here:
|
64 |
-
// https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php ??
|
65 |
-
// It seems that alpha channel works without... (at least I see completely transparerent pixels)
|
66 |
-
|
67 |
-
// TODO: Check out other iMagick methods, see http://php.net/manual/de/imagick.writeimage.php#114714
|
68 |
-
// 1. file_put_contents($destination, $im)
|
69 |
-
// 2. $im->writeImage($destination)
|
70 |
-
$success = $im->writeImageFile(fopen($destination, 'wb'));
|
71 |
-
|
72 |
-
if (!$success) {
|
73 |
-
throw new ConverterFailedException('Failed writing file');
|
74 |
-
}
|
75 |
-
}
|
76 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Converters/Wpc.php
DELETED
@@ -1,171 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Converters;
|
4 |
-
|
5 |
-
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
6 |
-
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
7 |
-
|
8 |
-
class Wpc
|
9 |
-
{
|
10 |
-
public static $extraOptions = [
|
11 |
-
[
|
12 |
-
'name' => 'secret',
|
13 |
-
'type' => 'string',
|
14 |
-
'sensitive' => true,
|
15 |
-
'default' => 'my dog is white',
|
16 |
-
'required' => true
|
17 |
-
],
|
18 |
-
[
|
19 |
-
'name' => 'url',
|
20 |
-
'type' => 'string',
|
21 |
-
'sensitive' => true,
|
22 |
-
'default' => '',
|
23 |
-
'required' => true
|
24 |
-
],
|
25 |
-
];
|
26 |
-
|
27 |
-
public static function convert($source, $destination, $options = [])
|
28 |
-
{
|
29 |
-
ConverterHelper::runConverter('wpc', $source, $destination, $options, true);
|
30 |
-
}
|
31 |
-
|
32 |
-
// Although this method is public, do not call directly.
|
33 |
-
public static function doConvert($source, $destination, $options = [], $logger)
|
34 |
-
{
|
35 |
-
if ($options['url'] == '') {
|
36 |
-
throw new ConverterNotOperationalException('Missing URL. You must install WebpConvertCloudService on a server, and supply url');
|
37 |
-
}
|
38 |
-
|
39 |
-
if (!extension_loaded('curl')) {
|
40 |
-
throw new ConverterNotOperationalException('Required cURL extension is not available.');
|
41 |
-
}
|
42 |
-
|
43 |
-
if (!function_exists('curl_init')) {
|
44 |
-
throw new ConverterNotOperationalException('Required url_init() function is not available.');
|
45 |
-
}
|
46 |
-
|
47 |
-
|
48 |
-
if (!function_exists('curl_file_create')) {
|
49 |
-
throw new ConverterNotOperationalException('Required curl_file_create() PHP function is not available (requires PHP > 5.5).');
|
50 |
-
}
|
51 |
-
|
52 |
-
if (!empty($options['secret'])) {
|
53 |
-
// if secret is set, we need md5() and md5_file() functions
|
54 |
-
if (!function_exists('md5')) {
|
55 |
-
throw new ConverterNotOperationalException('A secret has been set, which requires us to create a md5 hash from the secret and the file contents. But the required md5() PHP function is not available.');
|
56 |
-
}
|
57 |
-
if (!function_exists('md5_file')) {
|
58 |
-
throw new ConverterNotOperationalException('A secret has been set, which requires us to create a md5 hash from the secret and the file contents. But the required md5_file() PHP function is not available.');
|
59 |
-
}
|
60 |
-
}
|
61 |
-
|
62 |
-
// Got some code here:
|
63 |
-
// https://coderwall.com/p/v4ps1a/send-a-file-via-post-with-curl-and-php
|
64 |
-
|
65 |
-
$ch = curl_init();
|
66 |
-
if (!$ch) {
|
67 |
-
throw new ConverterNotOperationalException('Could not initialise cURL.');
|
68 |
-
}
|
69 |
-
|
70 |
-
$optionsToSend = $options;
|
71 |
-
|
72 |
-
if (isset($options['_quality_could_not_be_detected'])) {
|
73 |
-
// quality was set to "auto", but we could not meassure the quality of the jpeg locally
|
74 |
-
// Ask the cloud service to do it, rather than using what we came up with.
|
75 |
-
$optionsToSend['quality'] = 'auto';
|
76 |
-
} else {
|
77 |
-
$optionsToSend['quality'] = $options['_calculated_quality'];
|
78 |
-
}
|
79 |
-
|
80 |
-
unset($optionsToSend['converters']);
|
81 |
-
unset($optionsToSend['secret']);
|
82 |
-
unset($optionsToSend['_quality_could_not_be_detected']);
|
83 |
-
unset($optionsToSend['_calculated_quality']);
|
84 |
-
|
85 |
-
curl_setopt_array($ch, [
|
86 |
-
CURLOPT_URL => $options['url'],
|
87 |
-
CURLOPT_POST => 1,
|
88 |
-
CURLOPT_POSTFIELDS => [
|
89 |
-
'file' => curl_file_create($source),
|
90 |
-
'hash' => md5(md5_file($source) . $options['secret']),
|
91 |
-
'options' => json_encode($optionsToSend)
|
92 |
-
],
|
93 |
-
CURLOPT_BINARYTRANSFER => true,
|
94 |
-
CURLOPT_RETURNTRANSFER => true,
|
95 |
-
CURLOPT_HEADER => false,
|
96 |
-
CURLOPT_SSL_VERIFYPEER => false
|
97 |
-
]);
|
98 |
-
|
99 |
-
$response = curl_exec($ch);
|
100 |
-
if (curl_errno($ch)) {
|
101 |
-
throw new ConverterNotOperationalException('Curl error:' . curl_error($ch));
|
102 |
-
}
|
103 |
-
|
104 |
-
// Check if we got a 404
|
105 |
-
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
106 |
-
if ($httpCode == 404) {
|
107 |
-
curl_close($ch);
|
108 |
-
throw new ConverterFailedException('WPC was not found and the specified URL - we got a 404 response.');
|
109 |
-
}
|
110 |
-
|
111 |
-
// The WPC cloud service either returns an image or an error message
|
112 |
-
// Images has application/octet-stream.
|
113 |
-
// Verify that we got an image back.
|
114 |
-
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
115 |
-
curl_close($ch);
|
116 |
-
|
117 |
-
if (substr($response, 0, 1) == '{') {
|
118 |
-
$responseObj = json_decode($response, true);
|
119 |
-
if (isset($responseObj['errorCode'])) {
|
120 |
-
switch ($responseObj['errorCode']) {
|
121 |
-
case 0:
|
122 |
-
throw new ConverterFailedException('WPC reported problems with server setup: "' . $responseObj['errorMessage'] . '"');
|
123 |
-
case 1:
|
124 |
-
throw new ConverterFailedException('WPC denied us access to the service: "' . $responseObj['errorMessage'] . '"');
|
125 |
-
default:
|
126 |
-
throw new ConverterFailedException('WPC failed: "' . $responseObj['errorMessage'] . '"');
|
127 |
-
}
|
128 |
-
}
|
129 |
-
}
|
130 |
-
|
131 |
-
// WPC 0.1 returns 'failed![error messag]' when conversion fails. Handle that.
|
132 |
-
if (substr($response, 0, 7) == 'failed!') {
|
133 |
-
throw new ConverterFailedException('WPC failed converting image: "' . substr($response, 7) . '"');
|
134 |
-
}
|
135 |
-
|
136 |
-
$errorMsg = 'Error: Unexpected result. We did not receive an image. We received: "';
|
137 |
-
$errorMsg .= str_replace("\r", '', str_replace("\n", '', htmlentities(substr($response, 0, 400))));
|
138 |
-
throw new ConverterFailedException($errorMsg . '..."');
|
139 |
-
//throw new ConverterNotOperationalException($response);
|
140 |
-
}
|
141 |
-
|
142 |
-
$success = file_put_contents($destination, $response);
|
143 |
-
curl_close($ch);
|
144 |
-
|
145 |
-
if (!$success) {
|
146 |
-
throw new ConverterFailedException('Error saving file');
|
147 |
-
}
|
148 |
-
/*
|
149 |
-
$curlOptions = [
|
150 |
-
'api_key' => $options['key'],
|
151 |
-
'webp' => '1',
|
152 |
-
'file' => curl_file_create($source),
|
153 |
-
'domain' => $_SERVER['HTTP_HOST'],
|
154 |
-
'quality' => $options['quality'],
|
155 |
-
'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
|
156 |
-
];
|
157 |
-
|
158 |
-
curl_setopt_array($ch, [
|
159 |
-
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
160 |
-
CURLOPT_HTTPHEADER => [
|
161 |
-
'User-Agent: WebPConvert',
|
162 |
-
'Accept: image/*'
|
163 |
-
],
|
164 |
-
CURLOPT_POSTFIELDS => $curlOptions,
|
165 |
-
CURLOPT_BINARYTRANSFER => true,
|
166 |
-
CURLOPT_RETURNTRANSFER => true,
|
167 |
-
CURLOPT_HEADER => false,
|
168 |
-
CURLOPT_SSL_VERIFYPEER => false
|
169 |
-
]);*/
|
170 |
-
}
|
171 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Exceptions/ConverterNotFoundException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class ConverterNotFoundException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter does not exist.';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Exceptions/CreateDestinationFileException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class CreateDestinationFileException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter could not create destination file. Check file permisions!';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Exceptions/CreateDestinationFolderException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class CreateDestinationFolderException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter could not create destination folder. Check file permisions!';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Exceptions/InvalidFileExtensionException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class InvalidFileExtensionException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter does not accept the file extension';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Exceptions/TargetNotFoundException.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Exceptions;
|
4 |
-
|
5 |
-
use WebPConvert\Exceptions\WebPConvertBaseException;
|
6 |
-
|
7 |
-
class TargetNotFoundException extends WebPConvertBaseException
|
8 |
-
{
|
9 |
-
public $description = 'The converter could not locate source file';
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Exceptions/WebPConvertBaseException.php
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Exceptions;
|
4 |
-
|
5 |
-
class WebPConvertBaseException extends \Exception
|
6 |
-
{
|
7 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Loggers/BaseLogger.php
DELETED
@@ -1,26 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Loggers;
|
4 |
-
|
5 |
-
abstract class BaseLogger
|
6 |
-
{
|
7 |
-
/*
|
8 |
-
$msg: message to log
|
9 |
-
$style: null | bold | italic
|
10 |
-
*/
|
11 |
-
abstract public function log($msg, $style = '');
|
12 |
-
|
13 |
-
abstract public function ln();
|
14 |
-
|
15 |
-
public function logLn($msg, $style = '')
|
16 |
-
{
|
17 |
-
$this->log($msg, $style);
|
18 |
-
$this->ln();
|
19 |
-
}
|
20 |
-
|
21 |
-
public function logLnLn($msg, $style = '')
|
22 |
-
{
|
23 |
-
$this->logLn($msg, $style);
|
24 |
-
$this->ln();
|
25 |
-
}
|
26 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Loggers/EchoLogger.php
DELETED
@@ -1,22 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Loggers;
|
4 |
-
|
5 |
-
class EchoLogger extends BaseLogger
|
6 |
-
{
|
7 |
-
public function log($msg, $style = '')
|
8 |
-
{
|
9 |
-
if ($style == 'bold') {
|
10 |
-
echo '<b>' . $msg . '</b>';
|
11 |
-
} elseif ($style == 'italic') {
|
12 |
-
echo '<i>' . $msg . '</i>';
|
13 |
-
} else {
|
14 |
-
echo $msg;
|
15 |
-
}
|
16 |
-
}
|
17 |
-
|
18 |
-
public function ln()
|
19 |
-
{
|
20 |
-
echo '<br>';
|
21 |
-
}
|
22 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/Loggers/VoidLogger.php
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert\Loggers;
|
4 |
-
|
5 |
-
class VoidLogger extends BaseLogger
|
6 |
-
{
|
7 |
-
public function log($msg, $style = '')
|
8 |
-
{
|
9 |
-
}
|
10 |
-
|
11 |
-
public function ln()
|
12 |
-
{
|
13 |
-
}
|
14 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/WebPConvert.php
DELETED
@@ -1,108 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WebPConvert;
|
4 |
-
|
5 |
-
use WebPConvert\Converters\ConverterHelper;
|
6 |
-
|
7 |
-
class WebPConvert
|
8 |
-
{
|
9 |
-
|
10 |
-
/*
|
11 |
-
@param (string) $source: Absolute path to image to be converted (no backslashes). Image must be jpeg or png
|
12 |
-
@param (string) $destination: Absolute path (no backslashes)
|
13 |
-
@param (object) $options: Array of named options, such as 'quality' and 'metadata'
|
14 |
-
*/
|
15 |
-
public static function convert($source, $destination, $options = [], $logger = null)
|
16 |
-
{
|
17 |
-
if (!isset($logger)) {
|
18 |
-
$logger = new \WebPConvert\Loggers\VoidLogger();
|
19 |
-
}
|
20 |
-
ConverterHelper::prepareDestinationFolderAndRunCommonValidations($source, $destination);
|
21 |
-
|
22 |
-
$options = array_merge(ConverterHelper::$defaultOptions, $options);
|
23 |
-
|
24 |
-
ConverterHelper::processQualityOption($source, $options, $logger);
|
25 |
-
|
26 |
-
// Force lossless option to true for PNG images
|
27 |
-
if (ConverterHelper::getExtension($source) == 'png') {
|
28 |
-
$options['lossless'] = true;
|
29 |
-
}
|
30 |
-
|
31 |
-
$defaultConverterOptions = $options;
|
32 |
-
$defaultConverterOptions['converters'] = null;
|
33 |
-
|
34 |
-
$firstFailException = null;
|
35 |
-
|
36 |
-
foreach ($options['converters'] as $converter) {
|
37 |
-
if (is_array($converter)) {
|
38 |
-
$converterId = $converter['converter'];
|
39 |
-
$converterOptions = $converter['options'];
|
40 |
-
} else {
|
41 |
-
$converterId = $converter;
|
42 |
-
$converterOptions = [];
|
43 |
-
}
|
44 |
-
|
45 |
-
$converterOptions = array_merge($defaultConverterOptions, $converterOptions);
|
46 |
-
|
47 |
-
try {
|
48 |
-
$logger->logLn('Trying:' . $converterId, 'italic');
|
49 |
-
|
50 |
-
// If quality is different, we must recalculate
|
51 |
-
if ($converterOptions['quality'] != $defaultConverterOptions['quality']) {
|
52 |
-
unset($converterOptions['_calculated_quality']);
|
53 |
-
ConverterHelper::processQualityOption($source, $converterOptions, $logger);
|
54 |
-
}
|
55 |
-
|
56 |
-
ConverterHelper::runConverter($converterId, $source, $destination, $converterOptions, false, $logger);
|
57 |
-
|
58 |
-
// Still here? - well, we did it! - job is done.
|
59 |
-
$logger->logLn('ok', 'bold');
|
60 |
-
return true;
|
61 |
-
} catch (\WebPConvert\Converters\Exceptions\ConverterNotOperationalException $e) {
|
62 |
-
// $logger->logLnLn($e->description . ' : ' . $e->getMessage());
|
63 |
-
$logger->logLnLn($e->getMessage());
|
64 |
-
|
65 |
-
// The converter is not operational.
|
66 |
-
// Well, well, we will just have to try the next, then
|
67 |
-
} catch (\WebPConvert\Converters\Exceptions\ConverterFailedException $e) {
|
68 |
-
$logger->logLnLn($e->getMessage());
|
69 |
-
|
70 |
-
// Converter failed in an anticipated, yet somewhat surprising fashion.
|
71 |
-
// The converter seemed operational - requirements was in order - but it failed anyway.
|
72 |
-
// This is moderately bad.
|
73 |
-
// If some other converter can handle the conversion, we will let this one go.
|
74 |
-
// But if not, we shall throw the exception
|
75 |
-
|
76 |
-
if (!$firstFailException) {
|
77 |
-
$firstFailException = $e;
|
78 |
-
}
|
79 |
-
} catch (\WebPConvert\Converters\Exceptions\ConversionDeclinedException $e) {
|
80 |
-
$logger->logLnLn($e->getMessage());
|
81 |
-
|
82 |
-
// The converter declined.
|
83 |
-
// Gd is for example throwing this, when asked to convert a PNG, but configured not to
|
84 |
-
// We also possibly rethrow this, because it may have come as a surprise to the user
|
85 |
-
// who perhaps only tested jpg
|
86 |
-
if (!$firstFailException) {
|
87 |
-
$firstFailException = $e;
|
88 |
-
}
|
89 |
-
}
|
90 |
-
}
|
91 |
-
|
92 |
-
if ($firstFailException) {
|
93 |
-
// At least one converter failed or declined.
|
94 |
-
$logger->logLn('Conversion failed. None of the tried converters could convert the image', 'bold');
|
95 |
-
} else {
|
96 |
-
// All converters threw a ConverterNotOperationalException
|
97 |
-
$logger->logLn('Conversion failed. None of the tried converters are operational', 'bold');
|
98 |
-
}
|
99 |
-
|
100 |
-
// No converters could do the job.
|
101 |
-
// If one of them failed moderately bad, rethrow that exception.
|
102 |
-
if ($firstFailException) {
|
103 |
-
throw $firstFailException;
|
104 |
-
}
|
105 |
-
|
106 |
-
return false;
|
107 |
-
}
|
108 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-convert/require-all.inc
DELETED
@@ -1,20 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
require_once(__DIR__ . "/Exceptions/WebPConvertBaseException.php");
|
3 |
-
require_once(__DIR__ . "/Loggers/BaseLogger.php");
|
4 |
-
require_once(__DIR__ . "/WebPConvert.php");
|
5 |
-
require_once(__DIR__ . "/Converters/ConverterHelper.php");
|
6 |
-
require_once(__DIR__ . "/Converters/Cwebp.php");
|
7 |
-
require_once(__DIR__ . "/Converters/Ewww.php");
|
8 |
-
require_once(__DIR__ . "/Converters/Gd.php");
|
9 |
-
require_once(__DIR__ . "/Converters/Imagick.php");
|
10 |
-
require_once(__DIR__ . "/Converters/Wpc.php");
|
11 |
-
require_once(__DIR__ . "/Exceptions/ConverterNotFoundException.php");
|
12 |
-
require_once(__DIR__ . "/Exceptions/CreateDestinationFileException.php");
|
13 |
-
require_once(__DIR__ . "/Exceptions/CreateDestinationFolderException.php");
|
14 |
-
require_once(__DIR__ . "/Exceptions/InvalidFileExtensionException.php");
|
15 |
-
require_once(__DIR__ . "/Exceptions/TargetNotFoundException.php");
|
16 |
-
require_once(__DIR__ . "/Converters/Exceptions/ConversionDeclinedException.php");
|
17 |
-
require_once(__DIR__ . "/Converters/Exceptions/ConverterFailedException.php");
|
18 |
-
require_once(__DIR__ . "/Converters/Exceptions/ConverterNotOperationalException.php");
|
19 |
-
require_once(__DIR__ . "/Loggers/EchoLogger.php");
|
20 |
-
require_once(__DIR__ . "/Loggers/VoidLogger.php");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vendor/webp-on-demand/WebPOnDemand.php
DELETED
@@ -1,345 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/*
|
3 |
-
URL parameters:
|
4 |
-
|
5 |
-
base-path:
|
6 |
-
Sets the base path used for "source" and "destination-root" options.
|
7 |
-
Must be relative to document root, or absolute (not recommended)
|
8 |
-
When used in .htaccess, set it to the folder containing the .htaccess file, relative to document root.
|
9 |
-
If for example document root is /var/www/example.com/ and you have a subdirectory "wordpress", which you
|
10 |
-
want WebPOnDemand to work on, you should place .htaccess rules in the "wordpress" directory, and
|
11 |
-
your "base-path" will be "wordpress"
|
12 |
-
If not set, it defaults to be the path of webp-on-demand.php
|
13 |
-
|
14 |
-
source: Path to source file.
|
15 |
-
Path to source file, relative to 'base-path' option.
|
16 |
-
The final path is calculated like this:
|
17 |
-
[base-path] + [path to source file] + ".webp".
|
18 |
-
absolute path is depreciated, but supported for backwards compatability.
|
19 |
-
|
20 |
-
destination-root:
|
21 |
-
The path of where you want the converted files to reside, relative to the 'base-path' option.
|
22 |
-
If you want converted files to be put in the same folder as the originals, you can set destination-root to ".", or
|
23 |
-
leave it blank. If you on the other hand want all converted files to reside in their own folder, set the
|
24 |
-
destination-root to point to that folder. The converted files will be stored in a hierarchy that matches the source
|
25 |
-
files. With destination-root set to "webp-cache", the source file "images/2017/cool.jpg" will be stored at
|
26 |
-
"webp-cache/images/2017/cool.jpg.webp".
|
27 |
-
Double-dots in paths are allowed, ie "../webp-cache"
|
28 |
-
The final destination is calculated like this:
|
29 |
-
[base-path] + [destination-root] + [path to source file] + ".webp".
|
30 |
-
Default is "."
|
31 |
-
You can also supply an absolute path
|
32 |
-
|
33 |
-
quality (optional):
|
34 |
-
The quality of the generated WebP image, "auto" or 0-100. Defaults to "auto"
|
35 |
-
|
36 |
-
max-quality (optional):
|
37 |
-
The maximum quality. Only relevant when quality is set to "auto"
|
38 |
-
|
39 |
-
default-quality (optional):
|
40 |
-
Fallback value for quality, if it isn't possible to detect quality of jpeg. Only relevant when quality is set to "auto"
|
41 |
-
|
42 |
-
metadata (optional):
|
43 |
-
If set to "none", all metadata will be stripped
|
44 |
-
If set to "all", all metadata will be preserved
|
45 |
-
Note however that not all converters supports preserving metadata. cwebp supports it, imagewebp does not.
|
46 |
-
|
47 |
-
converters (optional):
|
48 |
-
Comma-separated list of converters. Ie. "cwebp,gd".
|
49 |
-
To pass options to the individual converters, see next.
|
50 |
-
Also, check out the WebPConvert docs
|
51 |
-
|
52 |
-
[converter-id]-[option-name] (optional):
|
53 |
-
This pattern is used for setting options on the individual converters.
|
54 |
-
Ie, in order to set the "key" option of the "ewww" converter, you pass "ewww-key".
|
55 |
-
|
56 |
-
[converter-id]-[n]-[option-name] (optional):
|
57 |
-
Use this pattern for targeting options of a converter, that are used multiple times. However, use the pattern above
|
58 |
-
for targeting the first occurence. `n` stands for the nth occurence of that converter in the `converters` option.
|
59 |
-
Example: `...&converters=cwebp,ewww,ewww,gd,ewww&ewww-key=xxx&ewww-2-key=yyy&ewww-3-key=zzz&gd-skip-pngs=1`
|
60 |
-
|
61 |
-
[converter-id]-[option-name]-[2] (optional):
|
62 |
-
This is an alternative, and simpler pattern than the above, for providing fallback for a single converter.
|
63 |
-
If WebPOnDemand detects that such an option is provided (ie ewww-key-2=yyy), it will automatically insert an extra
|
64 |
-
converter into the array (immidiately after), configured with the options with the '-2' postfix.
|
65 |
-
Example: `...&converters=cwebp,ewww,gd&ewww-key=xxx&ewww-key-2=yyy`
|
66 |
-
- will result in converter order: cwebp, ewww (with key=xxx), ewww (with key=yyy), gd
|
67 |
-
|
68 |
-
converters (optional):
|
69 |
-
Comma-separated list of converters. Ie. "cwebp,gd".
|
70 |
-
Passing options to the individual converters is done by passing options named like this:
|
71 |
-
[converter-name]-[option-name] (see below)
|
72 |
-
|
73 |
-
See WebPConvert documentation for more info
|
74 |
-
|
75 |
-
[converter]-[option-name] (optional):
|
76 |
-
Options for the converters can be passed as parameters with names like this: [converter]-[option-name].
|
77 |
-
Ie, in order to set the "key" option of the "ewww" converter, you pass "ewww-key".
|
78 |
-
|
79 |
-
If the same converter is going to be used with different configurations, you can add "-[n]" after the converter id.
|
80 |
-
Ie: ...&converters=ewww,ewww&ewww-key=xxx&ewww-2-key=yyy
|
81 |
-
|
82 |
-
See WebPConvert documentation for more info
|
83 |
-
|
84 |
-
debug (optional):
|
85 |
-
If set, a report will be served (as text) instead of an image
|
86 |
-
|
87 |
-
fail:
|
88 |
-
Default: "original"
|
89 |
-
What to serve if conversion fails
|
90 |
-
|
91 |
-
Possible values:
|
92 |
-
- "original": Serves the original image (source)
|
93 |
-
- "404": Serves a 404 header
|
94 |
-
- "report": Serves the error message as plain text
|
95 |
-
- "report-as-image": Serves the error message as an image
|
96 |
-
|
97 |
-
critical-fail:
|
98 |
-
Default: "report-as-image"
|
99 |
-
What to serve if conversion fails and source image is not available
|
100 |
-
|
101 |
-
Possible values:
|
102 |
-
- "404": Serves a 404 header
|
103 |
-
- "report": Serves the error message as plain text
|
104 |
-
- "report-as-image": Serves the error message as an image
|
105 |
-
|
106 |
-
*/
|
107 |
-
|
108 |
-
namespace WebPOnDemand;
|
109 |
-
|
110 |
-
use WebPConvertAndServe\WebPConvertAndServe;
|
111 |
-
use WebPConvert\WebPConvert;
|
112 |
-
use WebPConvert\Converters\ConverterHelper;
|
113 |
-
|
114 |
-
class WebPOnDemand
|
115 |
-
{
|
116 |
-
// transform options with '-2' postfix into new converters
|
117 |
-
// Idea: rename function to ie "transformFallbackOptionsIntoNewConverters"
|
118 |
-
private static function transformFallbackOptions($converters) {
|
119 |
-
foreach ($converters as $i => &$converter) {
|
120 |
-
$duplicateConverter = false;
|
121 |
-
foreach ($converter['options'] as $optionName => $optionValue) {
|
122 |
-
if (substr($optionName, -2) === '-2') {
|
123 |
-
$duplicateConverter = true;
|
124 |
-
break;
|
125 |
-
}
|
126 |
-
}
|
127 |
-
if ($duplicateConverter) {
|
128 |
-
$options2 = [];
|
129 |
-
foreach ($converter['options'] as $optionName => $optionValue) {
|
130 |
-
if (substr($optionName, -2) === '-2') {
|
131 |
-
$options2[substr($optionName, 0, -2)] = $optionValue;
|
132 |
-
unset($converter['options'][$optionName]);
|
133 |
-
}
|
134 |
-
}
|
135 |
-
array_splice($converters, $i+1, 0, [['converter' => $converter['converter'], 'options' => $options2]]);
|
136 |
-
}
|
137 |
-
}
|
138 |
-
return $converters;
|
139 |
-
}
|
140 |
-
|
141 |
-
private static function setOption(&$array, $parameterName, $optionName, $optionType)
|
142 |
-
{
|
143 |
-
if (!isset($_GET[$parameterName])) {
|
144 |
-
return;
|
145 |
-
}
|
146 |
-
switch ($optionType) {
|
147 |
-
case 'string':
|
148 |
-
//$options['converters'][$i]['options'][$optionName] = $_GET[$parameterName];
|
149 |
-
$array[$optionName] = $_GET[$parameterName];
|
150 |
-
break;
|
151 |
-
case 'boolean':
|
152 |
-
//$options['converters'][$i]['options'][$optionName] = ($_GET[$parameterName] == '1');
|
153 |
-
$array[$optionName] = ($_GET[$parameterName] == '1');
|
154 |
-
break;
|
155 |
-
}
|
156 |
-
}
|
157 |
-
private static function removeDoubleSlash($str)
|
158 |
-
{
|
159 |
-
return preg_replace('/\/\//', '/', $str);
|
160 |
-
}
|
161 |
-
private static function getRelDir($from_dir, $to_dir)
|
162 |
-
{
|
163 |
-
$fromDirParts = explode('/', str_replace('\\', '/', $from_dir));
|
164 |
-
$toDirParts = explode('/', str_replace('\\', '/', $to_dir));
|
165 |
-
$i = 0;
|
166 |
-
while (($i < count($fromDirParts)) && ($i < count($toDirParts)) && ($fromDirParts[$i] == $toDirParts[$i])) {
|
167 |
-
$i++;
|
168 |
-
}
|
169 |
-
$rel = "";
|
170 |
-
for ($j = $i; $j < count($fromDirParts); $j++) {
|
171 |
-
$rel .= "../";
|
172 |
-
}
|
173 |
-
|
174 |
-
for ($j = $i; $j < count($toDirParts); $j++) {
|
175 |
-
$rel .= $toDirParts[$j];
|
176 |
-
if ($j < count($toDirParts)-1) {
|
177 |
-
$rel .= '/';
|
178 |
-
}
|
179 |
-
}
|
180 |
-
return $rel;
|
181 |
-
}
|
182 |
-
|
183 |
-
public static function serve($scriptPath)
|
184 |
-
{
|
185 |
-
|
186 |
-
$debug = (isset($_GET['debug']) ? ($_GET['debug'] != 'no') : false);
|
187 |
-
|
188 |
-
//$source = $root . '/' . $_GET['source'];
|
189 |
-
|
190 |
-
if (!isset($_GET['base-path'])) {
|
191 |
-
$basePath = $scriptPath;
|
192 |
-
} else {
|
193 |
-
$basePath = $_GET['base-path'];
|
194 |
-
if ((substr($basePath, 0, 1) == '/')) {
|
195 |
-
} else {
|
196 |
-
$basePath = $_SERVER["DOCUMENT_ROOT"] . '/' . $basePath;
|
197 |
-
}
|
198 |
-
}
|
199 |
-
|
200 |
-
// Calculate $source and $sourceRelToBasePath (needed for calculating $destination)
|
201 |
-
$sourcePath = $_GET['source']; // this path includes filename
|
202 |
-
if ((substr($sourcePath, 0, 1) == '/')) {
|
203 |
-
$sourcePathAbs = $sourcePath;
|
204 |
-
$sourceRelToBasePath = self::getRelDir($basePath, $sourcePathAbs);
|
205 |
-
//echo $basePath . '<br>' . $sourcePathAbs . '<br>' . $sourceRelToBasePath . '<br><br>';
|
206 |
-
|
207 |
-
} else {
|
208 |
-
$sourceRelToBasePath = $sourcePath;
|
209 |
-
$sourcePathAbs = $basePath . '/' . $sourcePath;
|
210 |
-
}
|
211 |
-
$source = self::removeDoubleSlash($sourcePathAbs);
|
212 |
-
|
213 |
-
// Calculate $destination from destination-root and $basePath
|
214 |
-
if (!isset($_GET['destination-root'])) {
|
215 |
-
$destinationRoot = '.';
|
216 |
-
} else {
|
217 |
-
$destinationRoot = $_GET['destination-root'];
|
218 |
-
}
|
219 |
-
if ((substr($destinationRoot, 0, 1) == '/')) {
|
220 |
-
// absolute path - overrides basepath
|
221 |
-
$destinationRootAbs = $destinationRoot;
|
222 |
-
} else {
|
223 |
-
$destinationRootAbs = $basePath . '/' . $destinationRoot;
|
224 |
-
}
|
225 |
-
$destination = self::removeDoubleSlash($destinationRootAbs . '/' . $sourceRelToBasePath . '.webp');
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
$options = [];
|
231 |
-
|
232 |
-
// quality
|
233 |
-
if (isset($_GET['quality'])) {
|
234 |
-
if ($_GET['quality'] == 'auto') {
|
235 |
-
$options['quality'] = 'auto';
|
236 |
-
} else {
|
237 |
-
$options['quality'] = intval($_GET['quality']);
|
238 |
-
}
|
239 |
-
}
|
240 |
-
|
241 |
-
// max-quality
|
242 |
-
if (isset($_GET['max-quality'])) {
|
243 |
-
$options['max-quality'] = intval($_GET['max-quality']);
|
244 |
-
}
|
245 |
-
|
246 |
-
// default-quality
|
247 |
-
if (isset($_GET['default-quality'])) {
|
248 |
-
$options['default-quality'] = intval($_GET['default-quality']);
|
249 |
-
}
|
250 |
-
|
251 |
-
// method
|
252 |
-
if (isset($_GET['method'])) {
|
253 |
-
$options['method'] = $_GET['method'];
|
254 |
-
}
|
255 |
-
|
256 |
-
// metadata
|
257 |
-
if (isset($_GET['metadata'])) {
|
258 |
-
$options['metadata'] = $_GET['metadata'];
|
259 |
-
}
|
260 |
-
|
261 |
-
// converters
|
262 |
-
if (isset($_GET['converters'])) {
|
263 |
-
$conv = explode(',', $_GET['converters']);
|
264 |
-
$options['converters'] = [];
|
265 |
-
foreach ($conv as $i => $converter_name) {
|
266 |
-
$options['converters'][] = ['converter' => $converter_name, 'options' => []];
|
267 |
-
}
|
268 |
-
} else {
|
269 |
-
// Copy default converters.
|
270 |
-
// We need them in case some has options
|
271 |
-
foreach (ConverterHelper::$defaultOptions['converters'] as $i => $converter_name) {
|
272 |
-
$options['converters'][] = ['converter' => $converter_name, 'options' => []];
|
273 |
-
}
|
274 |
-
}
|
275 |
-
|
276 |
-
|
277 |
-
// Converter options
|
278 |
-
$counts = [];
|
279 |
-
foreach ($options['converters'] as $i => $converter_object) {
|
280 |
-
$converter = $converter_object['converter'];
|
281 |
-
//echo $i . ':' . $converter;
|
282 |
-
if (!isset($counts[$converter])) {
|
283 |
-
$counts[$converter] = 1;
|
284 |
-
$id = $converter;
|
285 |
-
}
|
286 |
-
else {
|
287 |
-
$counts[$converter]++;
|
288 |
-
}
|
289 |
-
|
290 |
-
$className = ConverterHelper::getClassNameOfConverter($converter);
|
291 |
-
$availOptions = array_column($className::$extraOptions, 'type', 'name');
|
292 |
-
//print_r($availOptions);
|
293 |
-
|
294 |
-
foreach ($availOptions as $optionName => $optionType) {
|
295 |
-
$parameterName = $converter . (($counts[$converter] > 1 ? '-' . $counts[$converter] : '')) . '-' . $optionName;
|
296 |
-
|
297 |
-
self::setOption($options['converters'][$i]['options'], $parameterName, $optionName, $optionType);
|
298 |
-
self::setOption($options['converters'][$i]['options'], $parameterName . '-2', $optionName . '-2', $optionType);
|
299 |
-
|
300 |
-
}
|
301 |
-
}
|
302 |
-
|
303 |
-
// transform options with '-2' postfix into new converters
|
304 |
-
$options['converters'] = self::transformFallbackOptions($options['converters']);
|
305 |
-
|
306 |
-
//echo '<pre>' . print_r($options, true) . '</pre>';
|
307 |
-
// Failure actions
|
308 |
-
$failCodes = [
|
309 |
-
"original" => WebPConvertAndServe::$ORIGINAL,
|
310 |
-
"404" => WebPConvertAndServe::$HTTP_404,
|
311 |
-
"report-as-image" => WebPConvertAndServe::$REPORT_AS_IMAGE,
|
312 |
-
"report" => WebPConvertAndServe::$REPORT,
|
313 |
-
];
|
314 |
-
|
315 |
-
$fail = 'original';
|
316 |
-
if (isset($_GET['fail'])) {
|
317 |
-
$fail = $_GET['fail'];
|
318 |
-
}
|
319 |
-
$fail = $failCodes[$fail];
|
320 |
-
|
321 |
-
$criticalFail = 'report';
|
322 |
-
if (isset($_GET['critical-fail'])) {
|
323 |
-
$criticalFail = $_GET['critical-fail'];
|
324 |
-
}
|
325 |
-
$criticalFail = $failCodes[$criticalFail];
|
326 |
-
|
327 |
-
if (!$debug) {
|
328 |
-
return WebPConvertAndServe::convertAndServeImage($source, $destination, $options, $fail, $criticalFail);
|
329 |
-
} else {
|
330 |
-
|
331 |
-
// TODO
|
332 |
-
// As we do not want to leak api keys, I have commented out the following.
|
333 |
-
/*
|
334 |
-
echo 'GET parameters:<br>';
|
335 |
-
foreach ($_GET as $key => $value) {
|
336 |
-
echo '<i>' . $key . '</i>: ' . htmlspecialchars($value) . '<br>';
|
337 |
-
}
|
338 |
-
echo '<br>';*/
|
339 |
-
|
340 |
-
//echo $_SERVER['DOCUMENT_ROOT'];
|
341 |
-
WebPConvertAndServe::convertAndReport($source, $destination, $options);
|
342 |
-
return 1;
|
343 |
-
}
|
344 |
-
}
|
345 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webp-express.php
CHANGED
@@ -3,117 +3,19 @@
|
|
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.
|
7 |
* Author: Bjørn Rosell
|
8 |
* Author URI: https://www.bitwise-it.dk
|
9 |
* License: GPL2
|
10 |
*/
|
11 |
|
12 |
-
|
13 |
/*
|
14 |
Note: Perhaps create a plugin page on my website?, ie https://www.bitwise-it.dk/software/wordpress/webp-express
|
15 |
*/
|
16 |
|
17 |
-
|
18 |
-
/*
|
19 |
-
// uncomment this block to debug an error during activation
|
20 |
-
function tl_save_error() {
|
21 |
-
update_option( 'webp-express-activation-error', ob_get_contents() );
|
22 |
-
}
|
23 |
-
add_action( 'activated_plugin', 'tl_save_error' );
|
24 |
-
if (!empty(get_option('plugin_error'))) {
|
25 |
-
add_filter( 'admin_footer_text', function() {
|
26 |
-
return 'Activation error:' . get_option('webp-express-activation-error');
|
27 |
-
});
|
28 |
-
}*/
|
29 |
-
|
30 |
define('WEBPEXPRESS_PLUGIN', __FILE__);
|
31 |
define('WEBPEXPRESS_PLUGIN_DIR', __DIR__);
|
32 |
|
33 |
-
if (
|
34 |
-
|
35 |
-
$hasWebPExpressOptionBeenSaved = ($wpdb->get_row( "SELECT * FROM $wpdb->options WHERE option_name = 'webp_express_converters'" ) !== null);
|
36 |
-
if ($hasWebPExpressOptionBeenSaved) {
|
37 |
-
// Store the fact that webp options has been changed.
|
38 |
-
// When nobody is using 0.3 or below, we can test on the existence of that option, instead of
|
39 |
-
// querying the database directly ($hasWebPExpressOptionBeenSaved)
|
40 |
-
add_option('webp-express-configured', true);
|
41 |
-
}
|
42 |
-
}
|
43 |
-
|
44 |
-
add_action( 'admin_menu', function() {
|
45 |
-
|
46 |
-
//Add Settings Page
|
47 |
-
add_options_page(
|
48 |
-
'WebP Express Settings', //Page Title
|
49 |
-
__( 'WebP Express', 'yasr' ), //Menu Title
|
50 |
-
'manage_options', //capability
|
51 |
-
'webp_express_settings_page', //menu slug
|
52 |
-
'webp_express_settings_page_content' //The function to be called to output the content for this page.
|
53 |
-
);
|
54 |
-
});
|
55 |
-
|
56 |
-
include(plugin_dir_path(__FILE__) . 'lib/options.php');
|
57 |
-
|
58 |
-
if (get_option('webp-express-htaccess-needs-updating')) {
|
59 |
-
delete_option('webp-express-htaccess-needs-updating');
|
60 |
-
//include(plugin_dir_path(__FILE__) . 'lib/helpers.php');
|
61 |
-
include_once 'lib/helpers.php';
|
62 |
-
|
63 |
-
$rules = WebPExpressHelpers::generateHTAccessRules();
|
64 |
-
WebPExpressHelpers::insertHTAccessRules($rules);
|
65 |
-
|
66 |
}
|
67 |
-
|
68 |
-
|
69 |
-
register_activation_hook(__FILE__, function () {
|
70 |
-
include(plugin_dir_path(__FILE__) . 'lib/activate.php');
|
71 |
-
});
|
72 |
-
|
73 |
-
register_deactivation_hook(__FILE__, function () {
|
74 |
-
include(plugin_dir_path(__FILE__) . 'lib/deactivate.php');
|
75 |
-
});
|
76 |
-
|
77 |
-
if (get_option('webp-express-message-pending')) {
|
78 |
-
include(plugin_dir_path(__FILE__) . 'lib/message.php');
|
79 |
-
}
|
80 |
-
|
81 |
-
if (get_option('webp-express-deactivate')) {
|
82 |
-
add_action('admin_init', function () {
|
83 |
-
deactivate_plugins(plugin_basename(__FILE__));
|
84 |
-
});
|
85 |
-
delete_option('webp-express-deactivate');
|
86 |
-
}
|
87 |
-
|
88 |
-
function webp_express_register_uninstall_hook() {
|
89 |
-
$optionsToDelete = [
|
90 |
-
'webp_express_max_quality',
|
91 |
-
'webp_express_image_types_to_convert',
|
92 |
-
'webp_express_failure_response',
|
93 |
-
'webp_express_converters',
|
94 |
-
'webp-express-inserted-rules-ok',
|
95 |
-
'webp-express-configured',
|
96 |
-
];
|
97 |
-
foreach ($optionsToDelete as $i => $optionName) {
|
98 |
-
delete_option($optionName);
|
99 |
-
}
|
100 |
-
/*
|
101 |
-
webp_express_fail_action
|
102 |
-
webp_express_method
|
103 |
-
webp_express_quality
|
104 |
-
*/
|
105 |
-
// Should we also call unregister_setting ?
|
106 |
-
}
|
107 |
-
|
108 |
-
// interestingly, I get "Serialization of 'Closure' is not allowed" if I pass anonymous function
|
109 |
-
// ... perhaps we should not do that in the other hooks either.
|
110 |
-
register_uninstall_hook( __FILE__, 'webp_express_register_uninstall_hook');
|
111 |
-
|
112 |
-
|
113 |
-
// Add settings link on the plugins page
|
114 |
-
add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), function ( $links ) {
|
115 |
-
$mylinks = array(
|
116 |
-
'<a href="' . admin_url( 'options-general.php?page=webp_express_settings_page' ) . '">Settings</a>',
|
117 |
-
);
|
118 |
-
return array_merge( $links, $mylinks );
|
119 |
-
});
|
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.5.0
|
7 |
* Author: Bjørn Rosell
|
8 |
* Author URI: https://www.bitwise-it.dk
|
9 |
* License: GPL2
|
10 |
*/
|
11 |
|
|
|
12 |
/*
|
13 |
Note: Perhaps create a plugin page on my website?, ie https://www.bitwise-it.dk/software/wordpress/webp-express
|
14 |
*/
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
define('WEBPEXPRESS_PLUGIN', __FILE__);
|
17 |
define('WEBPEXPRESS_PLUGIN_DIR', __DIR__);
|
18 |
|
19 |
+
if (is_admin()) {
|
20 |
+
include __DIR__ . '/lib/admin.php';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webp-on-demand.php
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
error_reporting(E_ALL);
|
4 |
-
ini_set('display_errors', 'On');
|
5 |
-
|
6 |
-
//require 'webp-on-demand/vendor/autoload.php';
|
7 |
-
//require 'vendor/webp-on-demand/autoload.php';
|
8 |
-
require 'vendor/require-webp-on-demand.php';
|
9 |
-
|
10 |
-
use WebPOnDemand\WebPOnDemand;
|
11 |
-
|
12 |
-
|
13 |
-
$status = WebPOnDemand::serve(__DIR__);
|
14 |
-
if ($status < 0) {
|
15 |
-
// Conversion failed.
|
16 |
-
// you could message your application about the problem here...
|
17 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{vendor/webp-convert/Converters → wod}/Binaries/cwebp-fbsd
RENAMED
File without changes
|
{vendor/webp-convert/Converters → wod}/Binaries/cwebp-linux
RENAMED
File without changes
|
{vendor/webp-convert/Converters → wod}/Binaries/cwebp-mac12
RENAMED
File without changes
|
{vendor/webp-convert/Converters → wod}/Binaries/cwebp-sol
RENAMED
File without changes
|
{vendor/webp-convert/Converters → wod}/Binaries/cwebp.exe
RENAMED
File without changes
|
wod/webp-convert-and-serve.inc
ADDED
@@ -0,0 +1,1615 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
namespace WebPConvert\Exceptions;
|
5 |
+
|
6 |
+
class WebPConvertBaseException extends \Exception
|
7 |
+
{
|
8 |
+
}
|
9 |
+
|
10 |
+
|
11 |
+
|
12 |
+
namespace WebPConvert\Loggers;
|
13 |
+
|
14 |
+
abstract class BaseLogger
|
15 |
+
{
|
16 |
+
/*
|
17 |
+
$msg: message to log
|
18 |
+
$style: null | bold | italic
|
19 |
+
*/
|
20 |
+
abstract public function log($msg, $style = '');
|
21 |
+
|
22 |
+
abstract public function ln();
|
23 |
+
|
24 |
+
public function logLn($msg, $style = '')
|
25 |
+
{
|
26 |
+
$this->log($msg, $style);
|
27 |
+
$this->ln();
|
28 |
+
}
|
29 |
+
|
30 |
+
public function logLnLn($msg, $style = '')
|
31 |
+
{
|
32 |
+
$this->logLn($msg, $style);
|
33 |
+
$this->ln();
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
namespace WebPConvert;
|
40 |
+
|
41 |
+
use WebPConvert\Converters\ConverterHelper;
|
42 |
+
|
43 |
+
class WebPConvert
|
44 |
+
{
|
45 |
+
|
46 |
+
/*
|
47 |
+
@param (string) $source: Absolute path to image to be converted (no backslashes). Image must be jpeg or png
|
48 |
+
@param (string) $destination: Absolute path (no backslashes)
|
49 |
+
@param (object) $options: Array of named options, such as 'quality' and 'metadata'
|
50 |
+
*/
|
51 |
+
public static function convert($source, $destination, $options = [], $logger = null)
|
52 |
+
{
|
53 |
+
if (!isset($logger)) {
|
54 |
+
$logger = new \WebPConvert\Loggers\VoidLogger();
|
55 |
+
}
|
56 |
+
ConverterHelper::prepareDestinationFolderAndRunCommonValidations($source, $destination);
|
57 |
+
|
58 |
+
$options = array_merge(ConverterHelper::$defaultOptions, $options);
|
59 |
+
|
60 |
+
ConverterHelper::processQualityOption($source, $options, $logger);
|
61 |
+
|
62 |
+
// Force lossless option to true for PNG images
|
63 |
+
if (ConverterHelper::getExtension($source) == 'png') {
|
64 |
+
$options['lossless'] = true;
|
65 |
+
}
|
66 |
+
|
67 |
+
$defaultConverterOptions = $options;
|
68 |
+
$defaultConverterOptions['converters'] = null;
|
69 |
+
|
70 |
+
$firstFailException = null;
|
71 |
+
|
72 |
+
foreach ($options['converters'] as $converter) {
|
73 |
+
if (is_array($converter)) {
|
74 |
+
$converterId = $converter['converter'];
|
75 |
+
$converterOptions = $converter['options'];
|
76 |
+
} else {
|
77 |
+
$converterId = $converter;
|
78 |
+
$converterOptions = [];
|
79 |
+
}
|
80 |
+
|
81 |
+
$converterOptions = array_merge($defaultConverterOptions, $converterOptions);
|
82 |
+
|
83 |
+
try {
|
84 |
+
$logger->logLn('Trying:' . $converterId, 'italic');
|
85 |
+
|
86 |
+
// If quality is different, we must recalculate
|
87 |
+
if ($converterOptions['quality'] != $defaultConverterOptions['quality']) {
|
88 |
+
unset($converterOptions['_calculated_quality']);
|
89 |
+
ConverterHelper::processQualityOption($source, $converterOptions, $logger);
|
90 |
+
}
|
91 |
+
|
92 |
+
ConverterHelper::runConverter($converterId, $source, $destination, $converterOptions, false, $logger);
|
93 |
+
|
94 |
+
// Still here? - well, we did it! - job is done.
|
95 |
+
$logger->logLn('ok', 'bold');
|
96 |
+
return true;
|
97 |
+
} catch (\WebPConvert\Converters\Exceptions\ConverterNotOperationalException $e) {
|
98 |
+
// $logger->logLnLn($e->description . ' : ' . $e->getMessage());
|
99 |
+
$logger->logLnLn($e->getMessage());
|
100 |
+
|
101 |
+
// The converter is not operational.
|
102 |
+
// Well, well, we will just have to try the next, then
|
103 |
+
} catch (\WebPConvert\Converters\Exceptions\ConverterFailedException $e) {
|
104 |
+
$logger->logLnLn($e->getMessage());
|
105 |
+
|
106 |
+
// Converter failed in an anticipated, yet somewhat surprising fashion.
|
107 |
+
// The converter seemed operational - requirements was in order - but it failed anyway.
|
108 |
+
// This is moderately bad.
|
109 |
+
// If some other converter can handle the conversion, we will let this one go.
|
110 |
+
// But if not, we shall throw the exception
|
111 |
+
|
112 |
+
if (!$firstFailException) {
|
113 |
+
$firstFailException = $e;
|
114 |
+
}
|
115 |
+
} catch (\WebPConvert\Converters\Exceptions\ConversionDeclinedException $e) {
|
116 |
+
$logger->logLnLn($e->getMessage());
|
117 |
+
|
118 |
+
// The converter declined.
|
119 |
+
// Gd is for example throwing this, when asked to convert a PNG, but configured not to
|
120 |
+
// We also possibly rethrow this, because it may have come as a surprise to the user
|
121 |
+
// who perhaps only tested jpg
|
122 |
+
if (!$firstFailException) {
|
123 |
+
$firstFailException = $e;
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
$logger->logLn('Conversion failed. None of the tried converters are operational', 'bold');
|
129 |
+
|
130 |
+
// No converters could do the job.
|
131 |
+
// If one of them failed moderately bad, rethrow that exception.
|
132 |
+
if ($firstFailException) {
|
133 |
+
throw $firstFailException;
|
134 |
+
}
|
135 |
+
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
|
141 |
+
|
142 |
+
namespace WebPConvert\Converters;
|
143 |
+
|
144 |
+
//use WebPConvert\Converters\Cwebp;
|
145 |
+
|
146 |
+
use WebPConvert\Exceptions\ConverterNotFoundException;
|
147 |
+
use WebPConvert\Exceptions\CreateDestinationFileException;
|
148 |
+
use WebPConvert\Exceptions\CreateDestinationFolderException;
|
149 |
+
use WebPConvert\Exceptions\InvalidFileExtensionException;
|
150 |
+
use WebPConvert\Exceptions\TargetNotFoundException;
|
151 |
+
|
152 |
+
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
153 |
+
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
154 |
+
|
155 |
+
class ConverterHelper
|
156 |
+
{
|
157 |
+
public static $allowedExtensions = ['jpg', 'jpeg', 'png'];
|
158 |
+
|
159 |
+
public static $defaultOptions = [
|
160 |
+
'quality' => 'auto',
|
161 |
+
'max-quality' => 85,
|
162 |
+
'default-quality' => 80,
|
163 |
+
'metadata' => 'none',
|
164 |
+
'method' => 6,
|
165 |
+
'low-memory' => false,
|
166 |
+
'lossless' => false,
|
167 |
+
'converters' => ['cwebp', 'gd', 'imagick']
|
168 |
+
];
|
169 |
+
|
170 |
+
public static function mergeOptions($options, $extraOptions)
|
171 |
+
{
|
172 |
+
return $options;
|
173 |
+
}
|
174 |
+
|
175 |
+
public static function getClassNameOfConverter($converterId)
|
176 |
+
{
|
177 |
+
return 'WebPConvert\\Converters\\' . ucfirst($converterId);
|
178 |
+
}
|
179 |
+
|
180 |
+
|
181 |
+
/* Call the "convert" method on a converter, by id.
|
182 |
+
- but also prepares options (merges in the $extraOptions of the converter),
|
183 |
+
prepares destination folder, and runs some standard validations */
|
184 |
+
public static function runConverter($converterId, $source, $destination, $options = [], $prepareDestinationFolder = true, $logger = null)
|
185 |
+
{
|
186 |
+
if ($prepareDestinationFolder) {
|
187 |
+
ConverterHelper::prepareDestinationFolderAndRunCommonValidations($source, $destination);
|
188 |
+
}
|
189 |
+
|
190 |
+
if (!isset($logger)) {
|
191 |
+
$logger = new \WebPConvert\Loggers\VoidLogger();
|
192 |
+
}
|
193 |
+
|
194 |
+
$className = self::getClassNameOfConverter($converterId);
|
195 |
+
if (!is_callable([$className, 'convert'])) {
|
196 |
+
throw new ConverterNotFoundException();
|
197 |
+
}
|
198 |
+
|
199 |
+
// Prepare options.
|
200 |
+
// - Remove 'converters'
|
201 |
+
$defaultOptions = self::$defaultOptions;
|
202 |
+
unset($defaultOptions['converters']);
|
203 |
+
|
204 |
+
// - Merge defaults of the converters extra options into the standard default options.
|
205 |
+
$defaultOptions = array_merge($defaultOptions, array_column($className::$extraOptions, 'default', 'name'));
|
206 |
+
|
207 |
+
// - Merge $defaultOptions into provided options
|
208 |
+
$options = array_merge($defaultOptions, $options);
|
209 |
+
|
210 |
+
// Individual converters do not accept quality = auto. They need a number.
|
211 |
+
// Change $options['quality'] to number, based on quality of source and several settings
|
212 |
+
|
213 |
+
self::processQualityOption($source, $options, $logger);
|
214 |
+
|
215 |
+
call_user_func(
|
216 |
+
[$className, 'doConvert'],
|
217 |
+
$source,
|
218 |
+
$destination,
|
219 |
+
$options,
|
220 |
+
$logger
|
221 |
+
);
|
222 |
+
|
223 |
+
if (!file_exists($destination)) {
|
224 |
+
throw new ConverterFailedException('Destination file is not there');
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
/* Try to detect quality of jpeg.
|
229 |
+
If not possible, nothing is returned (null). Otherwise quality is returned (int)
|
230 |
+
*/
|
231 |
+
public static function detectQualityOfJpg($filename)
|
232 |
+
{
|
233 |
+
// Try Imagick extension
|
234 |
+
if (extension_loaded('imagick') && class_exists('Imagick')) {
|
235 |
+
$img = new Imagick($filename);
|
236 |
+
|
237 |
+
// The required function is available as from PECL imagick v2.2.2
|
238 |
+
if (method_exists($img, 'getImageCompressionQuality')) {
|
239 |
+
return $img->getImageCompressionQuality();
|
240 |
+
}
|
241 |
+
}
|
242 |
+
|
243 |
+
if (function_exists('shell_exec')) {
|
244 |
+
|
245 |
+
// Try Imagick
|
246 |
+
$quality = shell_exec("identify -format '%Q' " . $filename);
|
247 |
+
if ($quality) {
|
248 |
+
return intval($quality);
|
249 |
+
}
|
250 |
+
|
251 |
+
// Try GraphicsMagick
|
252 |
+
$quality = shell_exec("gm identify -format '%Q' " . $filename);
|
253 |
+
if ($quality) {
|
254 |
+
return intval($quality);
|
255 |
+
}
|
256 |
+
}
|
257 |
+
}
|
258 |
+
|
259 |
+
public static function processQualityOption($source, &$options, $logger)
|
260 |
+
{
|
261 |
+
if (isset($options['_calculated_quality'])) {
|
262 |
+
return;
|
263 |
+
}
|
264 |
+
if ($options['quality'] == 'auto') {
|
265 |
+
$q = self::detectQualityOfJpg($source);
|
266 |
+
//$logger->log('Quality set to auto... Quality of source: ');
|
267 |
+
if (!$q) {
|
268 |
+
$q = $options['default-quality'];
|
269 |
+
$logger->logLn('Quality of source could not be established (Imagick or GraphicsMagick is required) - Using default instead (' . $options['default-quality'] . ').');
|
270 |
+
|
271 |
+
// this allows the wpc converter to know
|
272 |
+
$options['_quality_could_not_be_detected'] = true;
|
273 |
+
} else {
|
274 |
+
if ($q > $options['max-quality']) {
|
275 |
+
$logger->log('Quality of source is ' . $q . '. This is higher than max-quality, so using that instead (' . $options['max-quality'] . ')');
|
276 |
+
} else {
|
277 |
+
$logger->log('Quality set to same as source: ' . $q);
|
278 |
+
}
|
279 |
+
}
|
280 |
+
$logger->ln();
|
281 |
+
$q = min($q, $options['max-quality']);
|
282 |
+
|
283 |
+
$options['_calculated_quality'] = $q;
|
284 |
+
//$logger->logLn('Using quality: ' . $options['quality']);
|
285 |
+
} else {
|
286 |
+
$logger->logLn('Quality: ' . $options['quality'] . '. Consider setting quality to "auto" instead. It is generally a better idea');
|
287 |
+
$options['_calculated_quality'] = $options['quality'];
|
288 |
+
}
|
289 |
+
$logger->ln();
|
290 |
+
}
|
291 |
+
|
292 |
+
|
293 |
+
public static function getExtension($filePath)
|
294 |
+
{
|
295 |
+
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
|
296 |
+
return strtolower($fileExtension);
|
297 |
+
}
|
298 |
+
|
299 |
+
// Throws an exception if the provided file doesn't exist
|
300 |
+
public static function isValidTarget($filePath)
|
301 |
+
{
|
302 |
+
if (!file_exists($filePath)) {
|
303 |
+
throw new TargetNotFoundException('File or directory not found: ' . $filePath);
|
304 |
+
}
|
305 |
+
|
306 |
+
return true;
|
307 |
+
}
|
308 |
+
|
309 |
+
// Throws an exception if the provided file's extension is invalid
|
310 |
+
public static function isAllowedExtension($filePath)
|
311 |
+
{
|
312 |
+
$fileExtension = pathinfo($filePath, PATHINFO_EXTENSION);
|
313 |
+
if (!in_array(strtolower($fileExtension), self::$allowedExtensions)) {
|
314 |
+
throw new InvalidFileExtensionException('Unsupported file extension: ' . $fileExtension);
|
315 |
+
}
|
316 |
+
|
317 |
+
return true;
|
318 |
+
}
|
319 |
+
|
320 |
+
// Creates folder in provided path & sets correct permissions
|
321 |
+
public static function createWritableFolder($filePath)
|
322 |
+
{
|
323 |
+
$folder = pathinfo($filePath, PATHINFO_DIRNAME);
|
324 |
+
if (!file_exists($folder)) {
|
325 |
+
// TODO: what if this is outside open basedir?
|
326 |
+
// see http://php.net/manual/en/ini.core.php#ini.open-basedir
|
327 |
+
|
328 |
+
// First, we have to figure out which permissions to set.
|
329 |
+
// We want same permissions as parent folder
|
330 |
+
// But which parent? - the parent to the first missing folder
|
331 |
+
|
332 |
+
$parentFolders = explode('/', $folder);
|
333 |
+
$poppedFolders = [];
|
334 |
+
|
335 |
+
while (!(file_exists(implode('/', $parentFolders))) && count($parentFolders) > 0) {
|
336 |
+
array_unshift($poppedFolders, array_pop($parentFolders));
|
337 |
+
}
|
338 |
+
|
339 |
+
// Retrieving permissions of closest existing folder
|
340 |
+
$closestExistingFolder = implode('/', $parentFolders);
|
341 |
+
$permissions = fileperms($closestExistingFolder) & 000777;
|
342 |
+
|
343 |
+
// Trying to create the given folder
|
344 |
+
// Notice: mkdir emits a warning on failure. It would be nice to suppress that, if possible
|
345 |
+
if (!mkdir($folder, $permissions, true)) {
|
346 |
+
throw new CreateDestinationFolderException('Failed creating folder: ' . $folder);
|
347 |
+
}
|
348 |
+
|
349 |
+
|
350 |
+
// `mkdir` doesn't respect permissions, so we have to `chmod` each created subfolder
|
351 |
+
foreach ($poppedFolders as $subfolder) {
|
352 |
+
$closestExistingFolder .= '/' . $subfolder;
|
353 |
+
// Setting directory permissions
|
354 |
+
chmod($folder, $permissions);
|
355 |
+
}
|
356 |
+
}
|
357 |
+
|
358 |
+
// Checks if there's a file in $filePath & if writing permissions are correct
|
359 |
+
if (file_exists($filePath) && !is_writable($filePath)) {
|
360 |
+
throw new CreateDestinationFileException('Cannot overwrite ' . basename($filePath) . ' - check file permissions.');
|
361 |
+
}
|
362 |
+
|
363 |
+
// There's either a rewritable file in $filePath or none at all.
|
364 |
+
// If there is, simply attempt to delete it
|
365 |
+
if (file_exists($filePath) && !unlink($filePath)) {
|
366 |
+
throw new CreateDestinationFileException('Existing file cannot be removed: ' . basename($filePath));
|
367 |
+
}
|
368 |
+
|
369 |
+
return true;
|
370 |
+
}
|
371 |
+
|
372 |
+
public static function prepareDestinationFolderAndRunCommonValidations($source, $destination)
|
373 |
+
{
|
374 |
+
self::isValidTarget($source);
|
375 |
+
self::isAllowedExtension($source);
|
376 |
+
self::createWritableFolder($destination);
|
377 |
+
}
|
378 |
+
|
379 |
+
public static function initCurlForConverter()
|
380 |
+
{
|
381 |
+
if (!extension_loaded('curl')) {
|
382 |
+
throw new ConverterNotOperationalException('Required cURL extension is not available.');
|
383 |
+
}
|
384 |
+
|
385 |
+
if (!function_exists('curl_init')) {
|
386 |
+
throw new ConverterNotOperationalException('Required url_init() function is not available.');
|
387 |
+
}
|
388 |
+
|
389 |
+
if (!function_exists('curl_file_create')) {
|
390 |
+
throw new ConverterNotOperationalException('Required curl_file_create() function is not available (requires PHP > 5.5).');
|
391 |
+
}
|
392 |
+
|
393 |
+
$ch = curl_init();
|
394 |
+
if (!$ch) {
|
395 |
+
throw new ConverterNotOperationalException('Could not initialise cURL.');
|
396 |
+
}
|
397 |
+
return $ch;
|
398 |
+
}
|
399 |
+
}
|
400 |
+
|
401 |
+
|
402 |
+
|
403 |
+
namespace WebPConvert\Converters;
|
404 |
+
|
405 |
+
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
406 |
+
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
407 |
+
|
408 |
+
class Cwebp
|
409 |
+
{
|
410 |
+
public static $extraOptions = [
|
411 |
+
[
|
412 |
+
'name' => 'use-nice',
|
413 |
+
'type' => 'boolean',
|
414 |
+
'sensitive' => false,
|
415 |
+
'default' => false,
|
416 |
+
'required' => false
|
417 |
+
],
|
418 |
+
];
|
419 |
+
|
420 |
+
public static function convert($source, $destination, $options = [])
|
421 |
+
{
|
422 |
+
ConverterHelper::runConverter('cwebp', $source, $destination, $options, true);
|
423 |
+
}
|
424 |
+
|
425 |
+
// System paths to look for cwebp binary
|
426 |
+
private static $cwebpDefaultPaths = [
|
427 |
+
'/usr/bin/cwebp',
|
428 |
+
'/usr/local/bin/cwebp',
|
429 |
+
'/usr/gnu/bin/cwebp',
|
430 |
+
'/usr/syno/bin/cwebp'
|
431 |
+
];
|
432 |
+
|
433 |
+
// OS-specific binaries included in this library, along with hashes
|
434 |
+
private static $suppliedBinariesInfo = [
|
435 |
+
'WinNT' => [ 'cwebp.exe', '49e9cb98db30bfa27936933e6fd94d407e0386802cb192800d9fd824f6476873'],
|
436 |
+
'Darwin' => [ 'cwebp-mac12', 'a06a3ee436e375c89dbc1b0b2e8bd7729a55139ae072ed3f7bd2e07de0ebb379'],
|
437 |
+
'SunOS' => [ 'cwebp-sol', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f'],
|
438 |
+
'FreeBSD' => [ 'cwebp-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573'],
|
439 |
+
'Linux' => [ 'cwebp-linux', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568']
|
440 |
+
];
|
441 |
+
|
442 |
+
private static function escapeFilename($string)
|
443 |
+
{
|
444 |
+
// Escaping whitespaces & quotes
|
445 |
+
$string = preg_replace('/\s/', '\\ ', $string);
|
446 |
+
$string = filter_var($string, FILTER_SANITIZE_MAGIC_QUOTES);
|
447 |
+
|
448 |
+
// Stripping control characters
|
449 |
+
// see https://stackoverflow.com/questions/12769462/filter-flag-strip-low-vs-filter-flag-strip-high
|
450 |
+
$string = filter_var($string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
|
451 |
+
|
452 |
+
return $string;
|
453 |
+
}
|
454 |
+
|
455 |
+
// Checks if 'Nice' is available
|
456 |
+
private static function hasNiceSupport()
|
457 |
+
{
|
458 |
+
exec("nice 2>&1", $niceOutput);
|
459 |
+
|
460 |
+
if (is_array($niceOutput) && isset($niceOutput[0])) {
|
461 |
+
if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
|
462 |
+
/*
|
463 |
+
* Nice is available - default niceness (+10)
|
464 |
+
* https://www.lifewire.com/uses-of-commands-nice-renice-2201087
|
465 |
+
* https://www.computerhope.com/unix/unice.htm
|
466 |
+
*/
|
467 |
+
|
468 |
+
return true;
|
469 |
+
}
|
470 |
+
|
471 |
+
return false;
|
472 |
+
}
|
473 |
+
}
|
474 |
+
|
475 |
+
//
|
476 |
+
private static function executeBinary($binary, $commandOptions, $useNice, $logger)
|
477 |
+
{
|
478 |
+
$command = ($useNice ? 'nice ' : '') . $binary . ' ' . $commandOptions;
|
479 |
+
|
480 |
+
$logger->logLn('Trying to execute binary:' . $binary);
|
481 |
+
//$logger->logLn();
|
482 |
+
|
483 |
+
exec($command, $output, $returnCode);
|
484 |
+
|
485 |
+
switch ($returnCode) {
|
486 |
+
case 0:
|
487 |
+
$logger->logLn('Success!');
|
488 |
+
break;
|
489 |
+
case 126:
|
490 |
+
$logger->logLn('Permission denied. The user that the command was run with (' . shell_exec('whoami') . ') does not have permission to execute that binary.');
|
491 |
+
break;
|
492 |
+
case 127:
|
493 |
+
$logger->logLn('No binary found at that location');
|
494 |
+
break;
|
495 |
+
default:
|
496 |
+
$logger->logLn('Failed. Return code:' . $returnCode . '. See http://tldp.org/LDP/abs/html/exitcodes.html for failcodes');
|
497 |
+
}
|
498 |
+
return $returnCode;
|
499 |
+
}
|
500 |
+
|
501 |
+
// Although this method is public, do not call directly.
|
502 |
+
public static function doConvert($source, $destination, $options = [], $logger)
|
503 |
+
{
|
504 |
+
$errorMsg = '';
|
505 |
+
// Force lossless option to true for PNG images
|
506 |
+
if (ConverterHelper::getExtension($source) == 'png') {
|
507 |
+
$options['lossless'] = true;
|
508 |
+
}
|
509 |
+
|
510 |
+
if (!function_exists('exec')) {
|
511 |
+
throw new ConverterNotOperationalException('exec() is not enabled.');
|
512 |
+
}
|
513 |
+
|
514 |
+
/*
|
515 |
+
* Prepare cwebp options
|
516 |
+
*/
|
517 |
+
|
518 |
+
// Metadata (all, exif, icc, xmp or none (default))
|
519 |
+
// Comma-separated list of existing metadata to copy from input to output
|
520 |
+
$metadata = '-metadata ' . $options['metadata'];
|
521 |
+
|
522 |
+
// Image quality
|
523 |
+
$quality = '-q ' . $options['_calculated_quality'];
|
524 |
+
|
525 |
+
// Losless PNG conversion
|
526 |
+
$lossless = ($options['lossless'] ? '-lossless' : '');
|
527 |
+
|
528 |
+
// Built-in method option
|
529 |
+
$method = ' -m ' . strval($options['method']);
|
530 |
+
|
531 |
+
|
532 |
+
// TODO:
|
533 |
+
// Why not use -af ? (https://developers.google.com/speed/webp/docs/cwebp)
|
534 |
+
// Would it be possible get a quality similar to source?
|
535 |
+
// It seems so: "identify -format '%Q' yourimage.jpg" (https://stackoverflow.com/questions/2024947/is-it-possible-to-tell-the-quality-level-of-a-jpeg)
|
536 |
+
// -- With -jpeg_like option, or perhaps the -size option
|
537 |
+
|
538 |
+
// Built-in low memory option
|
539 |
+
$lowMemory = '';
|
540 |
+
if ($options['low-memory']) {
|
541 |
+
$lowMemory = '-low_memory';
|
542 |
+
}
|
543 |
+
|
544 |
+
$commandOptionsArray = [
|
545 |
+
$metadata = $metadata,
|
546 |
+
$quality = $quality,
|
547 |
+
$lossless = $lossless,
|
548 |
+
$method = $method,
|
549 |
+
$lowMemory = $lowMemory,
|
550 |
+
$input = self::escapeFilename($source),
|
551 |
+
$output = '-o ' . self::escapeFilename($destination),
|
552 |
+
$stderrRedirect = '2>&1'
|
553 |
+
];
|
554 |
+
|
555 |
+
$useNice = (($options['use-nice']) && self::hasNiceSupport()) ? true : false;
|
556 |
+
|
557 |
+
$commandOptions = implode(' ', $commandOptionsArray);
|
558 |
+
|
559 |
+
|
560 |
+
// Init with common system paths
|
561 |
+
$cwebpPathsToTest = self::$cwebpDefaultPaths;
|
562 |
+
|
563 |
+
// Remove paths that doesn't exist
|
564 |
+
$cwebpPathsToTest = array_filter($cwebpPathsToTest, function ($binary) {
|
565 |
+
//return file_exists($binary);
|
566 |
+
return @is_readable($binary);
|
567 |
+
});
|
568 |
+
|
569 |
+
// Try all common paths that exitst
|
570 |
+
$success = false;
|
571 |
+
foreach ($cwebpPathsToTest as $index => $binary) {
|
572 |
+
$success = (self::executeBinary($binary, $commandOptions, $useNice, $logger) == 0);
|
573 |
+
if ($success) {
|
574 |
+
break;
|
575 |
+
}
|
576 |
+
}
|
577 |
+
if (!$success) {
|
578 |
+
//$logger->logLn('');
|
579 |
+
if (count($cwebpPathsToTest) > 0) {
|
580 |
+
$errorMsg .= 'Found cwebp binaries at these locations: "' . implode('", "', $cwebpPathsToTest) . '". However, executing these failed. ';
|
581 |
+
} else {
|
582 |
+
$errorMsg .= 'Found no cwebp binaries in any common locations. ';
|
583 |
+
}
|
584 |
+
}
|
585 |
+
|
586 |
+
if (!$success) {
|
587 |
+
|
588 |
+
// Try supplied binary (if available for OS, and hash is correct)
|
589 |
+
if (isset(self::$suppliedBinariesInfo[PHP_OS])) {
|
590 |
+
$info = self::$suppliedBinariesInfo[PHP_OS];
|
591 |
+
|
592 |
+
$file = $info[0];
|
593 |
+
$hash = $info[1];
|
594 |
+
|
595 |
+
$binaryFile = __DIR__ . '/Binaries/' . $file;
|
596 |
+
|
597 |
+
// The file should exist, but may have been removed manually.
|
598 |
+
if (file_exists($binaryFile)) {
|
599 |
+
// File exists, now generate its hash
|
600 |
+
$binaryHash = hash_file('sha256', $binaryFile);
|
601 |
+
|
602 |
+
// Throw an exception if binary file checksum & deposited checksum do not match
|
603 |
+
if ($binaryHash != $hash) {
|
604 |
+
//throw new ConverterNotOperationalException('Binary checksum is invalid.');
|
605 |
+
$errorMsg .= 'Binary checksum of supplied binary is invalid! Did you transfer with FTP, but not in binary mode? File:' . $binaryFile . '. Expected checksum: ' . $hash . ' Actual checksum:' . $binaryHash . '. ';
|
606 |
+
} else {
|
607 |
+
$returnCode = self::executeBinary($binaryFile, $commandOptions, $useNice, $logger);
|
608 |
+
if ($returnCode == 0) {
|
609 |
+
$success = true;
|
610 |
+
} else {
|
611 |
+
$errorMsg .= 'Tried executing supplied binary (' . $binaryFile . '), but that failed too: ';
|
612 |
+
switch ($returnCode) {
|
613 |
+
case 126:
|
614 |
+
$errorMsg .= 'Permission denied (user "' . trim(shell_exec('whoami')) . '" does not have permission to execute the binary)';
|
615 |
+
break;
|
616 |
+
default:
|
617 |
+
$errorMsg .= 'Fail code: ' . $returnCode;
|
618 |
+
}
|
619 |
+
}
|
620 |
+
}
|
621 |
+
} else {
|
622 |
+
$errorMsg .= 'Supplied binary not found:' . $binaryFile;
|
623 |
+
}
|
624 |
+
} else {
|
625 |
+
$errorMsg .= 'No supplied binaries found for OS:' . PHP_OS;
|
626 |
+
}
|
627 |
+
}
|
628 |
+
|
629 |
+
|
630 |
+
|
631 |
+
// cwebp sets file permissions to 664 but instead ..
|
632 |
+
// .. $destination's parent folder's permissions should be used (except executable bits)
|
633 |
+
if ($success) {
|
634 |
+
$destinationParent = dirname($destination);
|
635 |
+
$fileStatistics = stat($destinationParent);
|
636 |
+
|
637 |
+
// Apply same permissions as parent folder but strip off the executable bits
|
638 |
+
$permissions = $fileStatistics['mode'] & 0000666;
|
639 |
+
chmod($destination, $permissions);
|
640 |
+
}
|
641 |
+
|
642 |
+
if (!$success) {
|
643 |
+
throw new ConverterNotOperationalException($errorMsg);
|
644 |
+
}
|
645 |
+
}
|
646 |
+
}
|
647 |
+
|
648 |
+
|
649 |
+
|
650 |
+
namespace WebPConvert\Converters;
|
651 |
+
|
652 |
+
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
653 |
+
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
654 |
+
|
655 |
+
class Ewww
|
656 |
+
{
|
657 |
+
public static $extraOptions = [
|
658 |
+
[
|
659 |
+
'name' => 'key',
|
660 |
+
'type' => 'string',
|
661 |
+
'sensitive' => true,
|
662 |
+
'default' => '',
|
663 |
+
'required' => true
|
664 |
+
],
|
665 |
+
];
|
666 |
+
|
667 |
+
public static function convert($source, $destination, $options = [])
|
668 |
+
{
|
669 |
+
ConverterHelper::runConverter('ewww', $source, $destination, $options, true);
|
670 |
+
}
|
671 |
+
|
672 |
+
// Although this method is public, do not call directly.
|
673 |
+
public static function doConvert($source, $destination, $options = [], $logger)
|
674 |
+
{
|
675 |
+
if ($options['key'] == '') {
|
676 |
+
throw new ConverterNotOperationalException('Missing API key.');
|
677 |
+
}
|
678 |
+
if (strlen($options['key']) < 20) {
|
679 |
+
throw new ConverterNotOperationalException('Key is invalid. Keys are supposed to be 32 characters long - your key is much shorter');
|
680 |
+
}
|
681 |
+
|
682 |
+
$keyStatus = self::getKeyStatus($options['key']);
|
683 |
+
switch ($keyStatus) {
|
684 |
+
case 'great':
|
685 |
+
break;
|
686 |
+
case 'exceeded':
|
687 |
+
throw new ConverterNotOperationalException('quota has exceeded');
|
688 |
+
break;
|
689 |
+
case 'invalid':
|
690 |
+
throw new ConverterNotOperationalException('key is invalid');
|
691 |
+
break;
|
692 |
+
}
|
693 |
+
|
694 |
+
$ch = ConverterHelper::initCurlForConverter();
|
695 |
+
|
696 |
+
$curlOptions = [
|
697 |
+
'api_key' => $options['key'],
|
698 |
+
'webp' => '1',
|
699 |
+
'file' => curl_file_create($source),
|
700 |
+
'domain' => $_SERVER['HTTP_HOST'],
|
701 |
+
'quality' => $options['_calculated_quality'],
|
702 |
+
'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
|
703 |
+
];
|
704 |
+
|
705 |
+
curl_setopt_array($ch, [
|
706 |
+
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
707 |
+
CURLOPT_HTTPHEADER => [
|
708 |
+
'User-Agent: WebPConvert',
|
709 |
+
'Accept: image/*'
|
710 |
+
],
|
711 |
+
CURLOPT_POSTFIELDS => $curlOptions,
|
712 |
+
CURLOPT_BINARYTRANSFER => true,
|
713 |
+
CURLOPT_RETURNTRANSFER => true,
|
714 |
+
CURLOPT_HEADER => false,
|
715 |
+
CURLOPT_SSL_VERIFYPEER => false
|
716 |
+
]);
|
717 |
+
|
718 |
+
$response = curl_exec($ch);
|
719 |
+
|
720 |
+
if (curl_errno($ch)) {
|
721 |
+
throw new ConverterNotOperationalException(curl_error($ch));
|
722 |
+
}
|
723 |
+
|
724 |
+
// The API does not always return images.
|
725 |
+
// For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
|
726 |
+
// Messages has a http content type of ie 'text/html; charset=UTF-8
|
727 |
+
// Images has application/octet-stream.
|
728 |
+
// So verify that we got an image back.
|
729 |
+
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
730 |
+
|
731 |
+
//echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
732 |
+
curl_close($ch);
|
733 |
+
|
734 |
+
/* May return this: {"error":"invalid","t":"exceeded"} */
|
735 |
+
$responseObj = json_decode($response);
|
736 |
+
if (isset($responseObj->error)) {
|
737 |
+
//echo 'error:' . $responseObj->error . '<br>';
|
738 |
+
//echo $response;
|
739 |
+
//self::blacklistKey($key);
|
740 |
+
//throw new ConverterNotOperationalException('The key is invalid. Blacklisted it!');
|
741 |
+
throw new ConverterNotOperationalException('The key is invalid');
|
742 |
+
}
|
743 |
+
|
744 |
+
throw new ConverterNotOperationalException('ewww api did not return an image. It could be that the key is invalid. Response: ' . $response);
|
745 |
+
}
|
746 |
+
|
747 |
+
// Not sure this can happen. So just in case
|
748 |
+
if ($response == '') {
|
749 |
+
throw new ConverterNotOperationalException('ewww api did not return anything');
|
750 |
+
}
|
751 |
+
|
752 |
+
$success = file_put_contents($destination, $response);
|
753 |
+
|
754 |
+
if (!$success) {
|
755 |
+
throw new ConverterFailedException('Error saving file');
|
756 |
+
}
|
757 |
+
}
|
758 |
+
|
759 |
+
/*
|
760 |
+
public static function blacklistKey($key)
|
761 |
+
{
|
762 |
+
}
|
763 |
+
|
764 |
+
public static function isKeyBlacklisted($key)
|
765 |
+
{
|
766 |
+
}*/
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Return "great", "exceeded" or "invalid"
|
770 |
+
*/
|
771 |
+
public static function getKeyStatus($key)
|
772 |
+
{
|
773 |
+
$ch = ConverterHelper::initCurlForConverter();
|
774 |
+
|
775 |
+
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
|
776 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
777 |
+
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
778 |
+
'api_key' => $key
|
779 |
+
]);
|
780 |
+
|
781 |
+
// The 403 forbidden is avoided with this line.
|
782 |
+
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)');
|
783 |
+
|
784 |
+
$response = curl_exec($ch);
|
785 |
+
// echo $response;
|
786 |
+
if (curl_errno($ch)) {
|
787 |
+
throw new \Exception(curl_error($ch));
|
788 |
+
}
|
789 |
+
curl_close($ch);
|
790 |
+
|
791 |
+
// Possible responses:
|
792 |
+
// “great” = verification successful
|
793 |
+
// “exceeded” = indicates a valid key with no remaining image credits.
|
794 |
+
// an empty response indicates that the key is not valid
|
795 |
+
|
796 |
+
if ($response == '') {
|
797 |
+
return 'invalid';
|
798 |
+
}
|
799 |
+
$responseObj = json_decode($response);
|
800 |
+
if (isset($responseObj->error)) {
|
801 |
+
if ($responseObj->error == 'invalid') {
|
802 |
+
return 'invalid';
|
803 |
+
} else {
|
804 |
+
throw new \Exception('Ewww returned unexpected error: ' . $response);
|
805 |
+
}
|
806 |
+
}
|
807 |
+
if (!isset($responseObj->status)) {
|
808 |
+
throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
|
809 |
+
}
|
810 |
+
switch ($responseObj->status) {
|
811 |
+
case 'great':
|
812 |
+
case 'exceeded':
|
813 |
+
return $responseObj->status;
|
814 |
+
}
|
815 |
+
throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
|
816 |
+
}
|
817 |
+
|
818 |
+
public static function isWorkingKey($key)
|
819 |
+
{
|
820 |
+
return (self::getKeyStatus($key) == 'great');
|
821 |
+
}
|
822 |
+
|
823 |
+
public static function isValidKey($key)
|
824 |
+
{
|
825 |
+
return (self::getKeyStatus($key) != 'invalid');
|
826 |
+
}
|
827 |
+
|
828 |
+
public static function getQuota($key)
|
829 |
+
{
|
830 |
+
$ch = ConverterHelper::initCurlForConverter();
|
831 |
+
|
832 |
+
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
|
833 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
834 |
+
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
835 |
+
'api_key' => $key
|
836 |
+
]);
|
837 |
+
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)');
|
838 |
+
|
839 |
+
$response = curl_exec($ch);
|
840 |
+
return $response; // ie -830 23. Seems to return empty for invalid keys
|
841 |
+
// or empty
|
842 |
+
//echo $response;
|
843 |
+
}
|
844 |
+
}
|
845 |
+
|
846 |
+
|
847 |
+
|
848 |
+
namespace WebPConvert\Converters;
|
849 |
+
|
850 |
+
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
851 |
+
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
852 |
+
use WebPConvert\Converters\Exceptions\ConversionDeclinedException;
|
853 |
+
|
854 |
+
use WebPConvert\Converters\ConverterHelper;
|
855 |
+
|
856 |
+
class Gd
|
857 |
+
{
|
858 |
+
public static $extraOptions = [
|
859 |
+
[
|
860 |
+
'name' => 'skip-pngs',
|
861 |
+
'type' => 'boolean',
|
862 |
+
'sensitive' => false,
|
863 |
+
'default' => true,
|
864 |
+
'required' => false
|
865 |
+
],
|
866 |
+
];
|
867 |
+
|
868 |
+
public static function convert($source, $destination, $options = [])
|
869 |
+
{
|
870 |
+
ConverterHelper::runConverter('gd', $source, $destination, $options, true);
|
871 |
+
}
|
872 |
+
|
873 |
+
// Although this method is public, do not call directly.
|
874 |
+
public static function doConvert($source, $destination, $options = [], $logger)
|
875 |
+
{
|
876 |
+
if (!extension_loaded('gd')) {
|
877 |
+
throw new ConverterNotOperationalException('Required GD extension is not available.');
|
878 |
+
}
|
879 |
+
|
880 |
+
if (!function_exists('imagewebp')) {
|
881 |
+
throw new ConverterNotOperationalException('Required imagewebp() function is not available.');
|
882 |
+
}
|
883 |
+
|
884 |
+
switch (ConverterHelper::getExtension($source)) {
|
885 |
+
case 'png':
|
886 |
+
if (!$options['skip-pngs']) {
|
887 |
+
if (!function_exists('imagecreatefrompng')) {
|
888 |
+
throw new ConverterNotOperationalException('Required imagecreatefrompng() function is not available.');
|
889 |
+
}
|
890 |
+
$image = imagecreatefrompng($source);
|
891 |
+
if (!$image) {
|
892 |
+
throw new ConverterFailedException('imagecreatefrompng("' . $source . '") failed');
|
893 |
+
}
|
894 |
+
} else {
|
895 |
+
throw new ConversionDeclinedException('PNG file skipped. GD is configured not to convert PNGs');
|
896 |
+
}
|
897 |
+
break;
|
898 |
+
default:
|
899 |
+
if (!function_exists('imagecreatefromjpeg')) {
|
900 |
+
throw new ConverterNotOperationalException('Required imagecreatefromjpeg() function is not available.');
|
901 |
+
}
|
902 |
+
$image = imagecreatefromjpeg($source);
|
903 |
+
if (!$image) {
|
904 |
+
throw new ConverterFailedException('imagecreatefromjpeg("' . $source . '") failed');
|
905 |
+
}
|
906 |
+
}
|
907 |
+
|
908 |
+
// Checks if either imagecreatefromjpeg() or imagecreatefrompng() returned false
|
909 |
+
|
910 |
+
$success = imagewebp($image, $destination, $options['_calculated_quality']);
|
911 |
+
|
912 |
+
if (!$success) {
|
913 |
+
throw new ConverterFailedException('Call to imagewebp() failed. Probably failed writing file');
|
914 |
+
}
|
915 |
+
|
916 |
+
/*
|
917 |
+
* This hack solves an `imagewebp` bug
|
918 |
+
* See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
|
919 |
+
*
|
920 |
+
*/
|
921 |
+
|
922 |
+
if (filesize($destination) % 2 == 1) {
|
923 |
+
file_put_contents($destination, "\0", FILE_APPEND);
|
924 |
+
}
|
925 |
+
|
926 |
+
imagedestroy($image);
|
927 |
+
}
|
928 |
+
}
|
929 |
+
|
930 |
+
|
931 |
+
|
932 |
+
namespace WebPConvert\Converters;
|
933 |
+
|
934 |
+
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
935 |
+
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
936 |
+
|
937 |
+
//use WebPConvert\Exceptions\TargetNotFoundException;
|
938 |
+
|
939 |
+
class Imagick
|
940 |
+
{
|
941 |
+
public static $extraOptions = [];
|
942 |
+
|
943 |
+
public static function convert($source, $destination, $options = [])
|
944 |
+
{
|
945 |
+
ConverterHelper::runConverter('imagick', $source, $destination, $options, true);
|
946 |
+
}
|
947 |
+
|
948 |
+
// Although this method is public, do not call directly.
|
949 |
+
public static function doConvert($source, $destination, $options = [], $logger)
|
950 |
+
{
|
951 |
+
if (!extension_loaded('imagick')) {
|
952 |
+
throw new ConverterNotOperationalException('Required iMagick extension is not available.');
|
953 |
+
}
|
954 |
+
|
955 |
+
if (!class_exists('Imagick')) {
|
956 |
+
throw new ConverterNotOperationalException('iMagick is installed, but not correctly. The class Imagick is not available');
|
957 |
+
}
|
958 |
+
|
959 |
+
$im = new \Imagick($source);
|
960 |
+
|
961 |
+
// Throws an exception if iMagick does not support WebP conversion
|
962 |
+
if (!in_array('WEBP', $im->queryFormats())) {
|
963 |
+
throw new ConverterNotOperationalException('iMagick was compiled without WebP support.');
|
964 |
+
}
|
965 |
+
|
966 |
+
$options = array_merge(ConverterHelper::$defaultOptions, $options);
|
967 |
+
|
968 |
+
// Force lossless option to true for PNG images
|
969 |
+
if (ConverterHelper::getExtension($source) == 'png') {
|
970 |
+
$options['lossless'] = true;
|
971 |
+
}
|
972 |
+
|
973 |
+
$im->setImageFormat('WEBP');
|
974 |
+
|
975 |
+
/*
|
976 |
+
* More about iMagick's WebP options:
|
977 |
+
* http://www.imagemagick.org/script/webp.php
|
978 |
+
* https://developers.google.com/speed/webp/docs/cwebp
|
979 |
+
* https://stackoverflow.com/questions/37711492/imagemagick-specific-webp-calls-in-php
|
980 |
+
*/
|
981 |
+
|
982 |
+
// TODO: We could easily support all webp options with a loop
|
983 |
+
$im->setOption('webp:method', strval($options['method']));
|
984 |
+
$im->setOption('webp:low-memory', strval($options['low-memory']));
|
985 |
+
$im->setOption('webp:lossless', strval($options['lossless']));
|
986 |
+
|
987 |
+
|
988 |
+
|
989 |
+
$im->setImageCompressionQuality($options['_calculated_quality']);
|
990 |
+
|
991 |
+
// TODO:
|
992 |
+
// Should we set alpha channel for PNG's like suggested here:
|
993 |
+
// https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php ??
|
994 |
+
// It seems that alpha channel works without... (at least I see completely transparerent pixels)
|
995 |
+
|
996 |
+
// TODO: Check out other iMagick methods, see http://php.net/manual/de/imagick.writeimage.php#114714
|
997 |
+
// 1. file_put_contents($destination, $im)
|
998 |
+
// 2. $im->writeImage($destination)
|
999 |
+
$success = $im->writeImageFile(fopen($destination, 'wb'));
|
1000 |
+
|
1001 |
+
if (!$success) {
|
1002 |
+
throw new ConverterFailedException('Failed writing file');
|
1003 |
+
}
|
1004 |
+
}
|
1005 |
+
}
|
1006 |
+
|
1007 |
+
|
1008 |
+
|
1009 |
+
namespace WebPConvert\Converters;
|
1010 |
+
|
1011 |
+
use WebPConvert\Converters\Exceptions\ConverterNotOperationalException;
|
1012 |
+
use WebPConvert\Converters\Exceptions\ConverterFailedException;
|
1013 |
+
|
1014 |
+
class Wpc
|
1015 |
+
{
|
1016 |
+
public static $extraOptions = [
|
1017 |
+
[
|
1018 |
+
'name' => 'secret',
|
1019 |
+
'type' => 'string',
|
1020 |
+
'sensitive' => true,
|
1021 |
+
'default' => 'my dog is white',
|
1022 |
+
'required' => true
|
1023 |
+
],
|
1024 |
+
[
|
1025 |
+
'name' => 'url',
|
1026 |
+
'type' => 'string',
|
1027 |
+
'sensitive' => true,
|
1028 |
+
'default' => '',
|
1029 |
+
'required' => true
|
1030 |
+
],
|
1031 |
+
];
|
1032 |
+
|
1033 |
+
public static function convert($source, $destination, $options = [])
|
1034 |
+
{
|
1035 |
+
ConverterHelper::runConverter('wpc', $source, $destination, $options, true);
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
// Although this method is public, do not call directly.
|
1039 |
+
public static function doConvert($source, $destination, $options = [], $logger)
|
1040 |
+
{
|
1041 |
+
if ($options['url'] == '') {
|
1042 |
+
throw new ConverterNotOperationalException('Missing URL. You must install WebpConvertCloudService on a server, and supply url');
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
if (!extension_loaded('curl')) {
|
1046 |
+
throw new ConverterNotOperationalException('Required cURL extension is not available.');
|
1047 |
+
}
|
1048 |
+
|
1049 |
+
if (!function_exists('curl_init')) {
|
1050 |
+
throw new ConverterNotOperationalException('Required url_init() function is not available.');
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
if (!function_exists('curl_file_create')) {
|
1054 |
+
throw new ConverterNotOperationalException('Required curl_file_create() function is not available (requires PHP > 5.5).');
|
1055 |
+
}
|
1056 |
+
|
1057 |
+
// Got some code here:
|
1058 |
+
// https://coderwall.com/p/v4ps1a/send-a-file-via-post-with-curl-and-php
|
1059 |
+
|
1060 |
+
$ch = curl_init();
|
1061 |
+
if (!$ch) {
|
1062 |
+
throw new ConverterNotOperationalException('Could not initialise cURL.');
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
$optionsToSend = $options;
|
1066 |
+
|
1067 |
+
if (isset($options['_quality_could_not_be_detected'])) {
|
1068 |
+
// quality was set to "auto", but we could not meassure the quality of the jpeg locally
|
1069 |
+
// Ask the cloud service to do it, rather than using what we came up with.
|
1070 |
+
$optionsToSend['quality'] = 'auto';
|
1071 |
+
} else {
|
1072 |
+
$optionsToSend['quality'] = $options['_calculated_quality'];
|
1073 |
+
}
|
1074 |
+
|
1075 |
+
unset($optionsToSend['converters']);
|
1076 |
+
unset($optionsToSend['secret']);
|
1077 |
+
unset($optionsToSend['_quality_could_not_be_detected']);
|
1078 |
+
unset($optionsToSend['_calculated_quality']);
|
1079 |
+
|
1080 |
+
curl_setopt_array($ch, [
|
1081 |
+
CURLOPT_URL => $options['url'],
|
1082 |
+
CURLOPT_POST => 1,
|
1083 |
+
CURLOPT_POSTFIELDS => [
|
1084 |
+
'file' => curl_file_create($source),
|
1085 |
+
'hash' => md5(md5_file($source) . $options['secret']),
|
1086 |
+
'options' => json_encode($optionsToSend)
|
1087 |
+
],
|
1088 |
+
CURLOPT_BINARYTRANSFER => true,
|
1089 |
+
CURLOPT_RETURNTRANSFER => true,
|
1090 |
+
CURLOPT_HEADER => false,
|
1091 |
+
CURLOPT_SSL_VERIFYPEER => false
|
1092 |
+
]);
|
1093 |
+
|
1094 |
+
$response = curl_exec($ch);
|
1095 |
+
|
1096 |
+
if (curl_errno($ch)) {
|
1097 |
+
throw new ConverterNotOperationalException(curl_error($ch));
|
1098 |
+
}
|
1099 |
+
|
1100 |
+
// The WPC cloud service either returns an image or an error message
|
1101 |
+
// Images has application/octet-stream.
|
1102 |
+
|
1103 |
+
// TODO: Check for 404 response, and handle that here
|
1104 |
+
|
1105 |
+
// Verify that we got an image back.
|
1106 |
+
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
1107 |
+
curl_close($ch);
|
1108 |
+
throw new ConverterFailedException($response);
|
1109 |
+
//throw new ConverterNotOperationalException($response);
|
1110 |
+
}
|
1111 |
+
|
1112 |
+
$success = file_put_contents($destination, $response);
|
1113 |
+
curl_close($ch);
|
1114 |
+
|
1115 |
+
if (!$success) {
|
1116 |
+
throw new ConverterFailedException('Error saving file');
|
1117 |
+
}
|
1118 |
+
/*
|
1119 |
+
$curlOptions = [
|
1120 |
+
'api_key' => $options['key'],
|
1121 |
+
'webp' => '1',
|
1122 |
+
'file' => curl_file_create($source),
|
1123 |
+
'domain' => $_SERVER['HTTP_HOST'],
|
1124 |
+
'quality' => $options['quality'],
|
1125 |
+
'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
|
1126 |
+
];
|
1127 |
+
|
1128 |
+
curl_setopt_array($ch, [
|
1129 |
+
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
1130 |
+
CURLOPT_HTTPHEADER => [
|
1131 |
+
'User-Agent: WebPConvert',
|
1132 |
+
'Accept: image/*'
|
1133 |
+
],
|
1134 |
+
CURLOPT_POSTFIELDS => $curlOptions,
|
1135 |
+
CURLOPT_BINARYTRANSFER => true,
|
1136 |
+
CURLOPT_RETURNTRANSFER => true,
|
1137 |
+
CURLOPT_HEADER => false,
|
1138 |
+
CURLOPT_SSL_VERIFYPEER => false
|
1139 |
+
]);*/
|
1140 |
+
}
|
1141 |
+
}
|
1142 |
+
|
1143 |
+
|
1144 |
+
|
1145 |
+
namespace WebPConvert\Exceptions;
|
1146 |
+
|
1147 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1148 |
+
|
1149 |
+
class ConverterNotFoundException extends WebPConvertBaseException
|
1150 |
+
{
|
1151 |
+
public $description = 'The converter does not exist.';
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
|
1155 |
+
|
1156 |
+
namespace WebPConvert\Exceptions;
|
1157 |
+
|
1158 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1159 |
+
|
1160 |
+
class CreateDestinationFileException extends WebPConvertBaseException
|
1161 |
+
{
|
1162 |
+
public $description = 'The converter could not create destination file. Check file permisions!';
|
1163 |
+
}
|
1164 |
+
|
1165 |
+
|
1166 |
+
|
1167 |
+
namespace WebPConvert\Exceptions;
|
1168 |
+
|
1169 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1170 |
+
|
1171 |
+
class CreateDestinationFolderException extends WebPConvertBaseException
|
1172 |
+
{
|
1173 |
+
public $description = 'The converter could not create destination folder. Check file permisions!';
|
1174 |
+
}
|
1175 |
+
|
1176 |
+
|
1177 |
+
|
1178 |
+
namespace WebPConvert\Exceptions;
|
1179 |
+
|
1180 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1181 |
+
|
1182 |
+
class InvalidFileExtensionException extends WebPConvertBaseException
|
1183 |
+
{
|
1184 |
+
public $description = 'The converter does not accept the file extension';
|
1185 |
+
}
|
1186 |
+
|
1187 |
+
|
1188 |
+
|
1189 |
+
namespace WebPConvert\Exceptions;
|
1190 |
+
|
1191 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1192 |
+
|
1193 |
+
class TargetNotFoundException extends WebPConvertBaseException
|
1194 |
+
{
|
1195 |
+
public $description = 'The converter could not locate source file';
|
1196 |
+
}
|
1197 |
+
|
1198 |
+
|
1199 |
+
|
1200 |
+
namespace WebPConvert\Converters\Exceptions;
|
1201 |
+
|
1202 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1203 |
+
|
1204 |
+
class ConversionDeclinedException extends WebPConvertBaseException
|
1205 |
+
{
|
1206 |
+
public $description = 'The converter declined converting';
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
|
1210 |
+
|
1211 |
+
namespace WebPConvert\Converters\Exceptions;
|
1212 |
+
|
1213 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1214 |
+
|
1215 |
+
class ConverterFailedException extends WebPConvertBaseException
|
1216 |
+
{
|
1217 |
+
public $description = 'The converter failed converting, although requirements seemed to be met';
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
|
1221 |
+
|
1222 |
+
namespace WebPConvert\Converters\Exceptions;
|
1223 |
+
|
1224 |
+
use WebPConvert\Exceptions\WebPConvertBaseException;
|
1225 |
+
|
1226 |
+
class ConverterNotOperationalException extends WebPConvertBaseException
|
1227 |
+
{
|
1228 |
+
public $description = 'The converter is not operational';
|
1229 |
+
}
|
1230 |
+
|
1231 |
+
|
1232 |
+
|
1233 |
+
namespace WebPConvert\Loggers;
|
1234 |
+
|
1235 |
+
class EchoLogger extends BaseLogger
|
1236 |
+
{
|
1237 |
+
public function log($msg, $style = '')
|
1238 |
+
{
|
1239 |
+
if ($style == 'bold') {
|
1240 |
+
echo '<b>' . $msg . '</b>';
|
1241 |
+
} elseif ($style == 'italic') {
|
1242 |
+
echo '<i>' . $msg . '</i>';
|
1243 |
+
} else {
|
1244 |
+
echo $msg;
|
1245 |
+
}
|
1246 |
+
}
|
1247 |
+
|
1248 |
+
public function ln()
|
1249 |
+
{
|
1250 |
+
echo '<br>';
|
1251 |
+
}
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
|
1255 |
+
|
1256 |
+
namespace WebPConvert\Loggers;
|
1257 |
+
|
1258 |
+
class VoidLogger extends BaseLogger
|
1259 |
+
{
|
1260 |
+
public function log($msg, $style = '')
|
1261 |
+
{
|
1262 |
+
}
|
1263 |
+
|
1264 |
+
public function ln()
|
1265 |
+
{
|
1266 |
+
}
|
1267 |
+
}
|
1268 |
+
|
1269 |
+
|
1270 |
+
|
1271 |
+
namespace WebPConvertAndServe;
|
1272 |
+
use WebPConvert\Loggers\BaseLogger;
|
1273 |
+
|
1274 |
+
class BufferLogger extends BaseLogger
|
1275 |
+
{
|
1276 |
+
public $entries = array();
|
1277 |
+
|
1278 |
+
public function log($msg, $style = '')
|
1279 |
+
{
|
1280 |
+
$this->entries[] = [$msg, $style];
|
1281 |
+
}
|
1282 |
+
|
1283 |
+
public function ln()
|
1284 |
+
{
|
1285 |
+
$this->entries[] = '';
|
1286 |
+
}
|
1287 |
+
|
1288 |
+
public function getHtml()
|
1289 |
+
{
|
1290 |
+
$html = '';
|
1291 |
+
foreach ($this->entries as $entry) {
|
1292 |
+
if ($entry == '') {
|
1293 |
+
$html .= '<br>';
|
1294 |
+
} else {
|
1295 |
+
list($msg, $style) = $entry;
|
1296 |
+
|
1297 |
+
if ($style == 'bold') {
|
1298 |
+
$html .= '<b>' . $msg . '</b>';
|
1299 |
+
} elseif ($style == 'italic') {
|
1300 |
+
$html .= '<i>' . $msg . '</i>';
|
1301 |
+
} else {
|
1302 |
+
$html .= $msg;
|
1303 |
+
}
|
1304 |
+
}
|
1305 |
+
}
|
1306 |
+
return $html;
|
1307 |
+
}
|
1308 |
+
|
1309 |
+
public function getText($newLineChar = ' ')
|
1310 |
+
{
|
1311 |
+
$text = '';
|
1312 |
+
foreach ($this->entries as $entry) {
|
1313 |
+
if ($entry == '') {
|
1314 |
+
if (substr($text, -2) != '. ') {
|
1315 |
+
$text .= '. ';
|
1316 |
+
}
|
1317 |
+
} else {
|
1318 |
+
list($msg, $style) = $entry;
|
1319 |
+
$text .= $msg;
|
1320 |
+
}
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
return $text;
|
1324 |
+
}
|
1325 |
+
}
|
1326 |
+
|
1327 |
+
|
1328 |
+
namespace WebPConvertAndServe;
|
1329 |
+
|
1330 |
+
use WebPConvert\WebPConvert;
|
1331 |
+
use WebPConvertAndServe\BufferLogger;
|
1332 |
+
use WebPConvert\Converters\ConverterHelper;
|
1333 |
+
//use WebPConvert\Loggers\EchoLogger;
|
1334 |
+
|
1335 |
+
class WebPConvertAndServe
|
1336 |
+
{
|
1337 |
+
public static $CONVERTED_IMAGE = 1;
|
1338 |
+
public static $ORIGINAL = -1;
|
1339 |
+
public static $HTTP_404 = -2;
|
1340 |
+
public static $REPORT_AS_IMAGE = -3;
|
1341 |
+
public static $REPORT = -4;
|
1342 |
+
|
1343 |
+
public static $defaultOptions = [
|
1344 |
+
'fail' => 'original',
|
1345 |
+
'critical-fail' => '404',
|
1346 |
+
];
|
1347 |
+
|
1348 |
+
private static function serve404()
|
1349 |
+
{
|
1350 |
+
$protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
|
1351 |
+
header($protocol . " 404 Not Found");
|
1352 |
+
}
|
1353 |
+
|
1354 |
+
private static function serveOriginal($source)
|
1355 |
+
{
|
1356 |
+
// Prevent caching image
|
1357 |
+
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
1358 |
+
header("Cache-Control: post-check=0, pre-check=0", false);
|
1359 |
+
header("Pragma: no-cache");
|
1360 |
+
|
1361 |
+
$arr = explode('.', $source);
|
1362 |
+
$ext = array_pop($arr);
|
1363 |
+
switch (strtolower($ext)) {
|
1364 |
+
case 'jpg':
|
1365 |
+
case 'jpeg':
|
1366 |
+
header('Content-type: image/jpeg');
|
1367 |
+
break;
|
1368 |
+
case 'png':
|
1369 |
+
header('Content-type: image/png');
|
1370 |
+
break;
|
1371 |
+
}
|
1372 |
+
readfile($source);
|
1373 |
+
}
|
1374 |
+
|
1375 |
+
private static function serveErrorMessageImage($msg)
|
1376 |
+
{
|
1377 |
+
// Generate image containing error message
|
1378 |
+
header('Content-type: image/gif');
|
1379 |
+
|
1380 |
+
// Prevent caching image
|
1381 |
+
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
1382 |
+
header("Cache-Control: post-check=0, pre-check=0", false);
|
1383 |
+
header("Pragma: no-cache");
|
1384 |
+
|
1385 |
+
$image = imagecreatetruecolor(620, 200);
|
1386 |
+
imagestring($image, 1, 5, 5, $msg, imagecolorallocate($image, 233, 214, 291));
|
1387 |
+
// echo imagewebp($image);
|
1388 |
+
echo imagegif($image);
|
1389 |
+
imagedestroy($image);
|
1390 |
+
}
|
1391 |
+
|
1392 |
+
/**
|
1393 |
+
* @depreciated
|
1394 |
+
*/
|
1395 |
+
public static function convertAndServeImage($source, $destination, $options, $failAction, $criticalFailAction, $debug = false)
|
1396 |
+
{
|
1397 |
+
if ($debug) {
|
1398 |
+
error_reporting(E_ALL);
|
1399 |
+
ini_set('display_errors', 'On');
|
1400 |
+
} else {
|
1401 |
+
ini_set('display_errors', 'Off');
|
1402 |
+
}
|
1403 |
+
|
1404 |
+
$options['fail'] = $failAction;
|
1405 |
+
$options['critical-fail'] = $criticalFailAction;
|
1406 |
+
|
1407 |
+
return self::convertAndServe($source, $destination, $options);
|
1408 |
+
}
|
1409 |
+
|
1410 |
+
/**
|
1411 |
+
* Main method
|
1412 |
+
*/
|
1413 |
+
public static function convertAndServe($source, $destination, $options)
|
1414 |
+
{
|
1415 |
+
$options = array_merge(self::$defaultOptions, $options);
|
1416 |
+
|
1417 |
+
$failCodes = [
|
1418 |
+
"original" => -1,
|
1419 |
+
"404" => -2,
|
1420 |
+
"report-as-image" => -3,
|
1421 |
+
"report" => -4,
|
1422 |
+
];
|
1423 |
+
|
1424 |
+
$failAction = $options['fail'];
|
1425 |
+
$criticalFailAction = $options['critical-fail'];
|
1426 |
+
|
1427 |
+
if (is_string($failAction)) {
|
1428 |
+
$failAction = $failCodes[$failAction];
|
1429 |
+
}
|
1430 |
+
if (is_string($criticalFailAction)) {
|
1431 |
+
$criticalFailAction = $failCodes[$criticalFailAction];
|
1432 |
+
}
|
1433 |
+
|
1434 |
+
$criticalFail = false;
|
1435 |
+
$success = false;
|
1436 |
+
$bufferLogger = new BufferLogger();
|
1437 |
+
|
1438 |
+
try {
|
1439 |
+
$success = WebPConvert::convert($source, $destination, $options, $bufferLogger);
|
1440 |
+
|
1441 |
+
if ($success) {
|
1442 |
+
$status = 'Success';
|
1443 |
+
$msg = 'Success';
|
1444 |
+
} else {
|
1445 |
+
$status = 'Failure (no converters are operational)';
|
1446 |
+
$msg = 'No converters are operational';
|
1447 |
+
}
|
1448 |
+
} catch (\WebPConvert\Exceptions\InvalidFileExtensionException $e) {
|
1449 |
+
$criticalFail = true;
|
1450 |
+
$status = 'Failure (invalid file extension)';
|
1451 |
+
$msg = $e->getMessage();
|
1452 |
+
} catch (\WebPConvert\Exceptions\TargetNotFoundException $e) {
|
1453 |
+
$criticalFail = true;
|
1454 |
+
$status = 'Failure (target file not found)';
|
1455 |
+
$msg = $e->getMessage();
|
1456 |
+
} catch (\WebPConvert\Converters\Exceptions\ConverterFailedException $e) {
|
1457 |
+
// No converters could convert the image. At least one converter failed, even though it appears to be operational
|
1458 |
+
$status = 'Failure (no converters could convert the image)';
|
1459 |
+
$msg = $e->getMessage();
|
1460 |
+
} catch (\WebPConvert\Converters\Exceptions\ConversionDeclinedException $e) {
|
1461 |
+
// (no converters could convert the image. At least one converter declined
|
1462 |
+
$status = 'Failure (no converters could/wanted to convert the image)';
|
1463 |
+
$msg = $e->getMessage();
|
1464 |
+
} catch (\WebPConvert\Exceptions\ConverterNotFoundException $e) {
|
1465 |
+
$status = 'Failure (a converter was not found!)';
|
1466 |
+
$msg = $e->getMessage();
|
1467 |
+
} catch (\WebPConvert\Exceptions\CreateDestinationFileException $e) {
|
1468 |
+
$status = 'Failure (cannot create destination file)';
|
1469 |
+
$msg = $e->getMessage();
|
1470 |
+
} catch (\WebPConvert\Exceptions\CreateDestinationFolderException $e) {
|
1471 |
+
$status = 'Failure (cannot create destination folder)';
|
1472 |
+
$msg = $e->getMessage();
|
1473 |
+
} catch (\Exception $e) {
|
1474 |
+
$status = 'Failure (an unanticipated exception was thrown)';
|
1475 |
+
$msg = $e->getMessage();
|
1476 |
+
}
|
1477 |
+
|
1478 |
+
$optionsForPrint = [];
|
1479 |
+
foreach (self::getPrintableOptions($options) as $optionName => $optionValue) {
|
1480 |
+
if ($optionName == 'converters') {
|
1481 |
+
$converterNames = [];
|
1482 |
+
$extraConvertOptions = [];
|
1483 |
+
foreach ($optionValue as $converter) {
|
1484 |
+
if (is_array($converter)) {
|
1485 |
+
$converterNames[] = $converter['converter'];
|
1486 |
+
if (isset($converter['options'])) {
|
1487 |
+
$extraConvertOptions[$converter['converter']] = $converter['options'];
|
1488 |
+
}
|
1489 |
+
} else {
|
1490 |
+
$converterNames[] = $converter;
|
1491 |
+
}
|
1492 |
+
}
|
1493 |
+
$optionsForPrint[] = 'converters:' . implode(',', $converterNames);
|
1494 |
+
foreach ($extraConvertOptions as $converter => $extraOptions) {
|
1495 |
+
$opt = [];
|
1496 |
+
foreach ($extraOptions as $oName => $oValue) {
|
1497 |
+
$opt[] = $oName . ':"' . $oValue . '"';
|
1498 |
+
}
|
1499 |
+
$optionsForPrint[] = $converter . ' options:(' . implode($opt, ', ') . ')';
|
1500 |
+
}
|
1501 |
+
} else {
|
1502 |
+
$optionsForPrint[] = $optionName . ':' . $optionValue ;
|
1503 |
+
}
|
1504 |
+
|
1505 |
+
}
|
1506 |
+
|
1507 |
+
header('X-WebP-Convert-And-Serve-Options:' . implode('. ', $optionsForPrint));
|
1508 |
+
|
1509 |
+
header('X-WebP-Convert-And-Serve-Status: ' . $status);
|
1510 |
+
|
1511 |
+
// Next line is commented out, because we need to be absolute sure that the details does not violate header syntax
|
1512 |
+
// We could either try to filter it, or we could change WebPConvert, such that it only provides safe texts.
|
1513 |
+
// header('X-WebP-Convert-And-Serve-Details: ' . $bufferLogger->getText());
|
1514 |
+
|
1515 |
+
if ($success) {
|
1516 |
+
header('Content-type: image/webp');
|
1517 |
+
// Should we add Content-Length header?
|
1518 |
+
// header('Content-Length: ' . filesize($file));
|
1519 |
+
readfile($destination);
|
1520 |
+
return self::$CONVERTED_IMAGE;
|
1521 |
+
} else {
|
1522 |
+
$action = ($criticalFail ? $criticalFailAction : $failAction);
|
1523 |
+
|
1524 |
+
switch ($action) {
|
1525 |
+
case WebPConvertAndServe::$ORIGINAL:
|
1526 |
+
self::serveOriginal($source);
|
1527 |
+
break;
|
1528 |
+
case WebPConvertAndServe::$HTTP_404:
|
1529 |
+
self::serve404();
|
1530 |
+
break;
|
1531 |
+
case WebPConvertAndServe::$REPORT_AS_IMAGE:
|
1532 |
+
self::serveErrorMessageImage($status . '. ' . $msg);
|
1533 |
+
break;
|
1534 |
+
case WebPConvertAndServe::$REPORT:
|
1535 |
+
echo '<h1>' . $status . '</h1>';
|
1536 |
+
echo $msg;
|
1537 |
+
echo '<p>This is how conversion process went:</p>' . $bufferLogger->getHtml();
|
1538 |
+
break;
|
1539 |
+
}
|
1540 |
+
return $action;
|
1541 |
+
}
|
1542 |
+
}
|
1543 |
+
|
1544 |
+
/* Hides sensitive options */
|
1545 |
+
private static function getPrintableOptions($options)
|
1546 |
+
{
|
1547 |
+
|
1548 |
+
$printable_options = [];
|
1549 |
+
|
1550 |
+
// (psst: the is_callable check is needed in order to work with WebPConvert v1.0)
|
1551 |
+
if (is_callable('ConverterHelper', 'getClassNameOfConverter')) {
|
1552 |
+
|
1553 |
+
$printable_options = $options;
|
1554 |
+
if (isset($printable_options['converters'])) {
|
1555 |
+
foreach ($printable_options['converters'] as &$converter) {
|
1556 |
+
if (is_array($converter)) {
|
1557 |
+
//echo '::' . $converter['converter'] . '<br>';
|
1558 |
+
$className = ConverterHelper::getClassNameOfConverter($converter['converter']);
|
1559 |
+
|
1560 |
+
// (pstt: the isset check is needed in order to work with WebPConvert v1.0)
|
1561 |
+
if (isset($className::$extraOptions)) {
|
1562 |
+
foreach ($className::$extraOptions as $extraOption) {
|
1563 |
+
if ($extraOption['sensitive']) {
|
1564 |
+
if (isset($converter['options'][$extraOption['name']])) {
|
1565 |
+
$converter['options'][$extraOption['name']] = '*******';
|
1566 |
+
}
|
1567 |
+
}
|
1568 |
+
}
|
1569 |
+
}
|
1570 |
+
}
|
1571 |
+
}
|
1572 |
+
}
|
1573 |
+
}
|
1574 |
+
return $printable_options;
|
1575 |
+
}
|
1576 |
+
|
1577 |
+
public static function convertAndReport($source, $destination, $options)
|
1578 |
+
{
|
1579 |
+
error_reporting(E_ALL);
|
1580 |
+
ini_set('display_errors', 'On');
|
1581 |
+
|
1582 |
+
echo '<html><style>td {vertical-align: top} table {color: #666}</style>';
|
1583 |
+
echo '<body><table>';
|
1584 |
+
echo '<tr><td><i>source:</i></td><td>' . $source . '</td></tr>';
|
1585 |
+
echo '<tr><td><i>destination:</i></td><td>' . $destination . '<td></tr>';
|
1586 |
+
|
1587 |
+
echo '<tr><td><i>options:</i></td><td>' . print_r(self::getPrintableOptions($options), true) . '</td></tr>';
|
1588 |
+
echo '</table>';
|
1589 |
+
|
1590 |
+
// TODO:
|
1591 |
+
// We could display warning if unknown options are set
|
1592 |
+
// but that requires that WebPConvert also describes its general options
|
1593 |
+
|
1594 |
+
echo '<br>';
|
1595 |
+
|
1596 |
+
try {
|
1597 |
+
$echoLogger = new \WebPConvert\Loggers\EchoLogger();
|
1598 |
+
$success = WebPConvert::convert($source, $destination, $options, $echoLogger);
|
1599 |
+
} catch (\Exception $e) {
|
1600 |
+
$success = false;
|
1601 |
+
|
1602 |
+
$msg = $e->getMessage();
|
1603 |
+
|
1604 |
+
echo '<b>' . $msg . '</b>';
|
1605 |
+
exit;
|
1606 |
+
}
|
1607 |
+
|
1608 |
+
if ($success) {
|
1609 |
+
//echo 'ok';
|
1610 |
+
} else {
|
1611 |
+
echo '<b>Conversion failed. None of the tried converters are operational</b>';
|
1612 |
+
}
|
1613 |
+
echo '</body></html>';
|
1614 |
+
}
|
1615 |
+
}
|
wod/webp-on-demand.inc
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
namespace WebPOnDemand;
|
5 |
+
|
6 |
+
class WebPOnDemand
|
7 |
+
{
|
8 |
+
public static $defaultOptions = [
|
9 |
+
'show-report' => false,
|
10 |
+
'reconvert' => false,
|
11 |
+
'original' => false,
|
12 |
+
'add-x-webp-on-demand-headers' => true,
|
13 |
+
'add-vary-header' => true,
|
14 |
+
];
|
15 |
+
|
16 |
+
private static function serveOriginal($source)
|
17 |
+
{
|
18 |
+
// Serve original image
|
19 |
+
$arr = explode('.', $source);
|
20 |
+
$ext = array_pop($arr);
|
21 |
+
switch (strtolower($ext)) {
|
22 |
+
case 'jpg':
|
23 |
+
case 'jpeg':
|
24 |
+
header('Content-type: image/jpeg');
|
25 |
+
break;
|
26 |
+
case 'png':
|
27 |
+
header('Content-type: image/png');
|
28 |
+
break;
|
29 |
+
}
|
30 |
+
if (@readfile($source) === false) {
|
31 |
+
header('X-WebP-On-Demand-Error: Could not read file');
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
private static function addWebPOnDemandHeader($text, $options)
|
36 |
+
{
|
37 |
+
if ($options['add-x-webp-on-demand-headers']) {
|
38 |
+
header('X-WebP-On-Demand: ' . $text);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
private static function addVaryHeader($options)
|
43 |
+
{
|
44 |
+
if ($options['add-vary-header']) {
|
45 |
+
header('Vary: Accept');
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
|
50 |
+
public static function serve($source, $destination, $options)
|
51 |
+
{
|
52 |
+
$options = array_merge(self::$defaultOptions, $options);
|
53 |
+
|
54 |
+
if (empty($source)) {
|
55 |
+
self::addWebPOnDemandHeader('Failed (Missing source argument)', $options);
|
56 |
+
}
|
57 |
+
if (empty($destination)) {
|
58 |
+
self::addWebPOnDemandHeader('Failed (Missing destination argument)', $options);
|
59 |
+
}
|
60 |
+
|
61 |
+
if ($options['show-report']) {
|
62 |
+
// Load WebPConvertAndServe (only when needed)
|
63 |
+
if (isset($options['require-for-conversion'])) {
|
64 |
+
require($options['require-for-conversion']);
|
65 |
+
}
|
66 |
+
|
67 |
+
self::addWebPOnDemandHeader('Reporting...', $options);
|
68 |
+
|
69 |
+
\WebPConvertAndServe\WebPConvertAndServe::convertAndReport($source, $destination, $options);
|
70 |
+
return;
|
71 |
+
}
|
72 |
+
|
73 |
+
if ($options['original']) {
|
74 |
+
self::addWebPOnDemandHeader('Serving original image (was explicitly told to)', $options);
|
75 |
+
self::serveOriginal($source);
|
76 |
+
}
|
77 |
+
if (file_exists($destination) && (!$options['reconvert'])) {
|
78 |
+
$timestampSource = filemtime($source);
|
79 |
+
$timestampDestination = filemtime($destination);
|
80 |
+
|
81 |
+
if (($timestampSource === false) &&
|
82 |
+
($timestampDestination !== false) &&
|
83 |
+
($timestampSource > $timestampDestination)) {
|
84 |
+
// It must be reconverted...
|
85 |
+
// will be done in a subsequent block...
|
86 |
+
} else {
|
87 |
+
$filesizeDestination = filesize($destination);
|
88 |
+
$filesizeSource = filesize($source);
|
89 |
+
|
90 |
+
// Serve original image, if the converted image is larger
|
91 |
+
if (($filesizeSource !== false) &&
|
92 |
+
($filesizeDestination !== false) &&
|
93 |
+
($filesizeDestination > $filesizeSource)) {
|
94 |
+
self::addWebPOnDemandHeader(
|
95 |
+
'Serving original image - because it is smaller than the converted!',
|
96 |
+
$options
|
97 |
+
);
|
98 |
+
self::addVaryHeader($options);
|
99 |
+
self::serveOriginal($source);
|
100 |
+
} else {
|
101 |
+
// Serve existing converted image
|
102 |
+
//echo $destination;
|
103 |
+
header('Content-type: image/webp');
|
104 |
+
self::addWebPOnDemandHeader('Serving existing converted image', $options);
|
105 |
+
self::addVaryHeader($options);
|
106 |
+
|
107 |
+
if (@readfile($destination) === false) {
|
108 |
+
header('X-WebP-On-Demand-Error: Could not read file');
|
109 |
+
}
|
110 |
+
}
|
111 |
+
return;
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
// We are still here... This means we should ignite the converter, possibly doing a reconversion
|
116 |
+
|
117 |
+
// Load WebPConvertAndServe (we only do that when it is needed)
|
118 |
+
if (isset($options['require-for-conversion'])) {
|
119 |
+
require($options['require-for-conversion']);
|
120 |
+
}
|
121 |
+
|
122 |
+
self::addWebPOnDemandHeader('Converting image (handed over to WebPConvertAndServe)', $options);
|
123 |
+
|
124 |
+
// We do not add "Vary Accept" header here, because WebPConvertAndServe will do that (if successful)
|
125 |
+
|
126 |
+
unset($options['show-report']);
|
127 |
+
unset($options['reconvert']);
|
128 |
+
unset($options['original']);
|
129 |
+
unset($options['add-x-webp-on-demand-headers']);
|
130 |
+
unset($options['require-for-conversion']);
|
131 |
+
|
132 |
+
|
133 |
+
\WebPConvertAndServe\WebPConvertAndServe::convertAndServe($source, $destination, $options);
|
134 |
+
}
|
135 |
+
}
|
wod/webp-on-demand.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require 'webp-on-demand.inc';
|
4 |
+
|
5 |
+
use \WebPOnDemand\WebPOnDemand;
|
6 |
+
|
7 |
+
$options = [];
|
8 |
+
|
9 |
+
$contentDirAbs = $_SERVER['DOCUMENT_ROOT'] . '/' . $_GET['wp-content'] . '/webp-express';
|
10 |
+
$configFilename = $contentDirAbs . '/config/wod-options.json';
|
11 |
+
$handle = @fopen($configFilename, "r");
|
12 |
+
$json = fread($handle, filesize($configFilename));
|
13 |
+
fclose($handle);
|
14 |
+
|
15 |
+
$options = json_decode($json, true);
|
16 |
+
//print_r($options);
|
17 |
+
|
18 |
+
$options['require-for-conversion'] = 'webp-convert-and-serve.inc';
|
19 |
+
|
20 |
+
if ($options['forward-query-string']) {
|
21 |
+
if (isset($_GET['debug'])) {
|
22 |
+
$options['show-report'] = true;
|
23 |
+
}
|
24 |
+
if (isset($_GET['reconvert'])) {
|
25 |
+
$options['reconvert'] = true;
|
26 |
+
}
|
27 |
+
}
|
28 |
+
$source = $_GET['source'];
|
29 |
+
|
30 |
+
// Calculate destination
|
31 |
+
$applicationRoot = $_SERVER["DOCUMENT_ROOT"];
|
32 |
+
$imageRoot = $contentDirAbs . '/webp-images';
|
33 |
+
|
34 |
+
if (substr($source, 0, strlen($applicationRoot)) === $applicationRoot) {
|
35 |
+
// Source file is residing inside document root.
|
36 |
+
// We can store relative to that.
|
37 |
+
$sourceRel = substr($source, strlen($applicationRoot));
|
38 |
+
$destination = $imageRoot . '/doc-root' . $sourceRel . '.webp';
|
39 |
+
} else {
|
40 |
+
// Source file is residing outside document root.
|
41 |
+
// we must add complete path to structure
|
42 |
+
$destination = $imageRoot . '/abs' . $source . '.webp';
|
43 |
+
}
|
44 |
+
//$destination = $imageRoot . $source . '.webp';
|
45 |
+
|
46 |
+
|
47 |
+
//echo $source . '<br>';
|
48 |
+
//echo $destination . '<br>';
|
49 |
+
//echo $sourceRel;
|
50 |
+
WebPOnDemand::serve($source, $destination, $options);
|