Version Description
- Alter HTML to point to webp files (choose between picture tags or simply altering all image urls)
- Convert non-existing webp-files upon request (means you can reference the converted webp files before they are actually converted!)
For more info, see the closed issues on the 0.11.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/14?closed=1
Download this release
Release Info
Developer | rosell.dk |
Plugin | WebP Express |
Version | 0.11.0 |
Comparing to | |
See all releases |
Code changes from version 0.10.0 to 0.11.0
- README.md +133 -46
- README.txt +106 -48
- changelog.txt +6 -0
- composer.json +25 -3
- docs/regex.md +154 -0
- js/picturefill.min.js +5 -0
- lib/admin.php +3 -1
- lib/alter-html.php +75 -0
- lib/classes/AlterHtmlHelper.php +257 -0
- lib/classes/AlterHtmlImageUrls.php +83 -0
- lib/classes/AlterHtmlInit.php +126 -0
- lib/classes/AlterHtmlPicture.php +18 -0
- lib/classes/CacheMover.php +36 -5
- lib/classes/Config.php +90 -15
- lib/classes/FileHelper.php +52 -2
- lib/classes/HTAccess.php +131 -66
- lib/classes/Messenger.php +7 -1
- lib/classes/Paths.php +45 -15
- lib/classes/TestRun.php +2 -2
- lib/migrate/migrate.php +29 -16
- lib/migrate/migrate5.php +55 -0
- lib/options/css/webp-express-options-page.css +24 -2
- lib/options/enqueue_scripts.php +1 -1
- lib/options/js/page.js +35 -1
- lib/options/options/alter-html/alter-html-options.inc +98 -0
- lib/options/options/alter-html/alter-html.inc +19 -0
- lib/options/options/conversion-options/conversion-options.inc +1 -1
- lib/options/options/conversion-options/destination-extension.inc +24 -15
- lib/options/options/conversion-options/destination-folder.inc +24 -13
- lib/options/options/operation-mode.inc +24 -26
- lib/options/options/redirection-rules/enable-redirection-to-converter.inc +17 -3
- lib/options/options/redirection-rules/enable-redirection-to-webp-realizer.inc +26 -0
- lib/options/options/redirection-rules/image-types.inc +2 -2
- lib/options/options/redirection-rules/only-redirect-to-converter-on-cache-miss.inc +9 -1
- lib/options/options/redirection-rules/redirection-rules.inc +4 -2
- lib/options/options/serve-options/serve-options.inc +1 -1
- lib/options/page-messages.php +2 -2
- lib/options/page-welcome.php +1 -1
- lib/options/page.php +60 -2
- lib/options/submit.php +41 -17
- lib/uninstall.php +1 -1
- test/very-small.jpg +0 -0
- tests/BananaTest.php +24 -0
- webp-express.php +7 -1
- wod/.htaccess +1 -1
- wod/.webp +0 -0
- wod/webp-on-demand.php +31 -17
- wod/webp-realizer.php +215 -0
README.md
CHANGED
@@ -1,22 +1,35 @@
|
|
1 |
Â
# WebP Express
|
2 |
Â
|
3 |
-
Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP.
|
4 |
Â
|
5 |
Â
The plugin is available on the Wordpress codex ([here](https://wordpress.org/plugins/webp-express/)), and developed on github ([here](https://github.com/rosell-dk/webp-express/)).
|
6 |
Â
|
Â
|
|
Â
|
|
7 |
Â
## Description
|
8 |
Â
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
|
9 |
Â
|
10 |
-
The
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
11 |
Â
|
12 |
Â
The plugin builds on [WebPConvert](https://github.com/rosell-dk/webp-convert) and its "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/webp-on-demand/webp-on-demand.md)
|
13 |
Â
|
14 |
-
|
15 |
Â
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
|
16 |
Â
- Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
|
17 |
Â
- Better ranking in Google searches (performance is taken into account by Google)
|
18 |
Â
- Less bandwidth consumption - makes a huge difference in the parts of the world where the internet is slow and costly (you know, ~80% of the world population lives under these circumstances).
|
19 |
-
- Currently ~73% of all traffic, and ~
|
20 |
Â
|
21 |
Â
|
22 |
Â
## Installation
|
@@ -29,6 +42,22 @@ The plugin builds on [WebPConvert](https://github.com/rosell-dk/webp-convert) an
|
|
29 |
Â
### Configuring
|
30 |
Â
You configure the plugin in *Settings > WebP Express*.
|
31 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
32 |
Â
#### Conversion methods
|
33 |
Â
WebP Express has a bunch of methods available for converting images: Executing cwebp binary, Gd extension, Imagick extension, ewww cloud converter and remote WebP express. Each requires *something*. In many cases, one of the conversion methods will be available. You can quickly identify which converters are working - there is a green icon next to them. Hovering conversion methods that are not working will show you what is wrong.
|
34 |
Â
|
@@ -51,7 +80,7 @@ Once, you have a converter, that works, when you click the "test"-button, you ar
|
|
51 |
Â
|
52 |
Â
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. It could also be that your images are handled by nginx.
|
53 |
Â
|
54 |
-
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:
|
55 |
Â
|
56 |
Â
- Open the page in Google Chrome
|
57 |
Â
- Right-click the page and choose "Inspect"
|
@@ -66,7 +95,7 @@ You can also append `?debug` after any image url, in order to run a conversion,
|
|
66 |
Â
### Notes
|
67 |
Â
|
68 |
Â
*Note:*
|
69 |
-
The redirect rules created in *.htaccess* are pointing to a PHP 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
|
70 |
Â
|
71 |
Â
*Note:*
|
72 |
Â
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.
|
@@ -108,12 +137,12 @@ Assuming that all above is in place, please look at the response headers to see
|
|
108 |
Â
I shall write more on this FAQ item... Stay tuned.
|
109 |
Â
|
110 |
Â
### How can a webp image be served on an URL ending with "jpg"?
|
111 |
-
Easy enough. Browsers looks at the *content type* header rather than the URL to determine what it is that it gets. So, although it can be confusing that the resource at *example.com/image.jpg* is a webp image, rest
|
112 |
Â
|
113 |
Â
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
|
114 |
Â
|
115 |
Â
### I am on NGINX / OpenResty
|
116 |
-
It is possible to make WebP Express work on NGINX, but it
|
117 |
Â
|
118 |
Â
```
|
119 |
Â
if ($http_accept ~* "webp"){
|
@@ -158,7 +187,7 @@ Well, there is no point in having the "Speed up image load times" enabled togeth
|
|
158 |
Â
|
159 |
Â
But there is a case for using WebP Express rather than Jetpacks "Speed up image load times" feature:
|
160 |
Â
|
161 |
-
Jetpack has the same drawback as the *
|
162 |
Â
|
163 |
Â
Pro Jetpack:
|
164 |
Â
- It is a free CDN which serves webp out of the box.
|
@@ -183,12 +212,64 @@ If you have the *Imagick*, the *Imagick binary* or the *Remote WebP Express* con
|
|
183 |
Â
|
184 |
Â
Note: If you experience that the general auto option doesn't show, even though the above-mentioned requirements should be in order, check out [this support-thread](https://wordpress.org/support/topic/still-no-auto-option/).
|
185 |
Â
|
186 |
-
### How do I
|
187 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
188 |
Â
|
189 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
190 |
Â
|
191 |
-
In the 0.10.0 release, you will have another (and better) option, as [described here](https://github.com/rosell-dk/webp-express/issues/133)
|
192 |
Â
|
193 |
Â
### I am on Cloudflare
|
194 |
Â
Without configuration, Cloudflare will not maintain separate caches for jpegs and webp; all browsers will get jpeg. To make Cloudflare cache not only by URL, but also by header, you need to use the [Custom Cache Key](https://support.cloudflare.com/hc/en-us/articles/115004290387) page rule, and add *Header content* to make separate caches depending on the *Accept* request header.
|
@@ -206,28 +287,26 @@ To make *WebP Express* work on a free Cloudflare account, you have the following
|
|
206 |
Â
### WebP Express / ShortPixel setup
|
207 |
Â
Here is a recipe for using WebP Express together with ShortPixel, such that WebP Express generates the webp's, and ShortPixel only is used to create `<picture>` tags, when it detects a webp image in the same folder as an original.
|
208 |
Â
|
209 |
-
|
210 |
-
1. You are using a CDN which cannot be configured to work with the Standard WebP Express setup.
|
211 |
-
2. You think it is problematic that when a user saves an image, it has the jpg extension, even though it is a webp image.
|
212 |
Â
|
213 |
Â
You need:
|
214 |
Â
1 x WebP Express
|
215 |
Â
1 x ShortPixel
|
216 |
Â
|
217 |
-
|
218 |
-
If you
|
219 |
Â
- Open WebP Express options
|
220 |
-
- Switch to *
|
221 |
Â
- Set *File extension* to "Set to .webp"
|
222 |
-
- Make sure the *
|
223 |
Â
|
224 |
Â
If you want to *ShortPixel* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
225 |
Â
- Open WebP Express options
|
226 |
-
- Switch to *
|
227 |
Â
- Set *Destination folder* to "Mingled"
|
228 |
Â
- Set *File extension* to "Set to .webp"
|
229 |
Â
|
230 |
-
|
231 |
Â
- Install [ShortPixel](https://wordpress.org/plugins/shortpixel-image-optimiser/) the usual way
|
232 |
Â
- Get an API key and enter it on the options page.
|
233 |
Â
- In *Advanced*, enable the following options:
|
@@ -238,13 +317,13 @@ If you want to *ShortPixel* to create <picture> tags but still want the magic to
|
|
238 |
Â
- *Automatically optimize images added by users in front end.*
|
239 |
Â
- *Automatically optimize Media Library items after they are uploaded (recommended).*
|
240 |
Â
|
241 |
-
|
242 |
Â
As there are presumably no webps generated yet, ShortPixel will not generate `<picture>` tags on the first visit. However, the images that are referenced causes the WebP Express *Auto convert* feature to kick in and generate webp images for each image on that page.
|
243 |
Â
|
244 |
-
|
245 |
Â
As *WebP Express* have generated webps in the same folder as the originals, *ShortPixel* detects these, and you should see `<picture>` tags which references the webp's.
|
246 |
Â
|
247 |
-
|
248 |
Â
Cache Enabler has the advantage over ShortPixel that the HTML structure remains the same. With ShortPixel, image tags are wrapped in a `<picture>` tag structure, and by doing that, there is a risk of breaking styles.
|
249 |
Â
|
250 |
Â
Further, Cache Enabler *caches* the HTML. This is good for performance. However, this also locks you to using that plugin for caching. With ShortPixel, you can keep using your favourite caching plugin.
|
@@ -252,35 +331,39 @@ Further, Cache Enabler *caches* the HTML. This is good for performance. However,
|
|
252 |
Â
Cache Enabler will not work if you are caching HTML on a CDN, because the HTML varies depending on the *Accept* header and it doesn't signal this with a Vary:Accept header. You could however add that manually. ShortPixel does not have that issue, as the HTML is the same for all.
|
253 |
Â
|
254 |
Â
### WebP Express / Cache Enabler setup
|
255 |
-
The WebP Express / Cache Enabler setup is quite potent and very CDN-friendly. Cache Enabler is used for generating and caching two versions of the HTML (one for webp-enabled browsers and one for webp-disabled browsers)
|
256 |
Â
|
257 |
Â
The reason for doing this could be:
|
258 |
-
1. You are using a CDN which cannot be configured to work
|
259 |
-
2. You
|
Â
|
|
260 |
Â
|
261 |
Â
You need:
|
262 |
Â
1 x WebP Express
|
263 |
Â
1 x Cache Enabler
|
264 |
Â
|
265 |
-
|
266 |
-
If you
|
267 |
Â
- Open WebP Express options
|
268 |
-
- Switch to *
|
269 |
Â
- Set *File extension* to "Set to .webp"
|
270 |
-
-
|
Â
|
|
Â
|
|
271 |
Â
|
272 |
Â
If you want to *Cache Enabler* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
273 |
Â
- Open WebP Express options
|
274 |
-
- Switch to *
|
275 |
Â
- Set *Destination folder* to "Mingled"
|
276 |
Â
- Set *File extension* to "Set to .webp"
|
Â
|
|
277 |
Â
|
278 |
-
|
279 |
Â
- Open the options
|
280 |
Â
- Enable of the *Create an additional cached version for WebP image support* option
|
281 |
Â
|
282 |
-
|
283 |
-
*WebP Express* creates *webp* images on need basis. It needs page visits in order to do the
|
284 |
Â
|
285 |
Â
```
|
286 |
Â
wget -e robots=off -r -np -w 2 http://www.example.com
|
@@ -291,24 +374,18 @@ wget -e robots=off -r -np -w 2 http://www.example.com
|
|
291 |
Â
`-np` (no-parent) makes wget stay within the boundaries (doesn't go into parent folders)
|
292 |
Â
`w 2` Waits two seconds between each request, in order not to stress the server
|
293 |
Â
|
294 |
-
|
295 |
Â
Click the "Clear Cache" button in the top right corner in order to clear the Cache Enabler cache.
|
296 |
Â
|
297 |
-
|
298 |
Â
When visiting a page with images on, different HTML will be served to browsers, depending on whether they support webp or not.
|
299 |
Â
|
300 |
Â
In a webp-enabled browser, the HTML may look like this: `<img src="image.webp">`, while in a non-webp enabled browser, it looks like this: `<img src="image.jpg">`
|
301 |
Â
|
302 |
Â
|
303 |
-
|
304 |
Â
*Cache Enabler* provides some rewrite rules that redirects to the cached file directly in the `.htaccess`, bypassing PHP entirely. Their plugin doesn't do that for you, so you will have to do it manually in order to get the best performance. The rules are in the "Advanced configuration" section on [this page](https://www.keycdn.com/support/wordpress-cache-enabler-plugin).
|
305 |
Â
|
306 |
-
#### ShortPixel or Cache Enabler ?
|
307 |
-
Cache Enabler has the advantage over ShortPixel that the HTML structure remains the same. With ShortPixel, image tags are wrapped in a `<picture>` tag structure, and by doing that, there is a risk of breaking styles.
|
308 |
-
|
309 |
-
Further, Cache Enabler *caches* the HTML. This is good for performance. However, this also locks you to using that plugin for caching. With ShortPixel, you can keep using your favourite caching plugin.
|
310 |
-
|
311 |
-
Cache Enabler will not work if you are caching HTML on a CDN, because the HTML varies depending on the *Accept* header and it doesn't signal this with a Vary:Accept header. You could however add that manually. ShortPixel does not have that issue, as the HTML is the same for all.
|
312 |
Â
|
313 |
Â
### Does it work with lazy loaded images?
|
314 |
Â
No plugins/frameworks has yet been discovered, which does not work with *WebP Express*.
|
@@ -319,13 +396,23 @@ The following lazy load plugins/frameworks has been tested and works with *WebP
|
|
319 |
Â
- [BJ Lazy Load](https://da.wordpress.org/plugins/bj-lazy-load/)
|
320 |
Â
- [Owl Carousel 2](https://owlcarousel2.github.io/OwlCarousel2/)
|
321 |
Â
|
Â
|
|
Â
|
|
322 |
Â
### When is feature X coming? / Roadmap
|
323 |
Â
No schedule. I move forward as time allows. I currently spend a lot of time answering questions in the support forum. If someone would be nice and help out answering questions here, it would allow me to spend that time developing. Also, donations would allow me to turn down some of the more boring requests from my customers, and speed things up here.
|
324 |
Â
|
325 |
-
Here are my current plans ahead: The 0.
|
326 |
Â
|
327 |
Â
If you wish to affect priorities, it is certainly possible. You can try to argue your case in the forum or you can simply let the money do the talking. By donating as little as a cup of coffee on [ko-fi.com/rosell](https://ko-fi.com/rosell), you can leave a wish. I shall take these wishes into account when prioritizing between new features.
|
328 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
329 |
Â
|
330 |
Â
## Changes in 0.9.0
|
331 |
Â
- Optionally make .htaccess redirect directly to existing webp (improves performance)
|
1 |
Â
# WebP Express
|
2 |
Â
|
3 |
+
Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP.
|
4 |
Â
|
5 |
Â
The plugin is available on the Wordpress codex ([here](https://wordpress.org/plugins/webp-express/)), and developed on github ([here](https://github.com/rosell-dk/webp-express/)).
|
6 |
Â
|
7 |
+
Note that to use the version on github, you need to *cd* into the plugin folder and run `composer update`.
|
8 |
+
|
9 |
Â
## Description
|
10 |
Â
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
|
11 |
Â
|
12 |
+
### The image converter
|
13 |
+
The plugin uses the [WebP Convert](https://github.com/rosell-dk/webp-convert) library to convert images to webp. *WebP Convert* is able to convert images using multiple methods. There are the "local" conversion methods: `cwebp`, `gd`, `imagick`. If none of these works on your host, there are the cloud alternatives: `ewww` (paid) or connecting to a Wordpress site where you got WebP Express installed and you enabled the "web service" functionality.
|
14 |
+
|
15 |
+
### The "Serving webp to browsers that supports it" part.
|
16 |
+
|
17 |
+
The plugin supports different ways of delivering webps to browsers that supports it:
|
18 |
+
|
19 |
+
1. By routing jpeg/png images to the corresponding webp - or to the image converter if the image hasn't been converted yet.
|
20 |
+
2. By altering the HTML, replacing image tags with *picture* tags. Missing webps are auto generated upon visit.
|
21 |
+
3. By altering the HTML, replacing image URLs so all points to webp. The replacements only being made for browsers that supports webp. Again, missing webps are auto generated upon visit.
|
22 |
+
4. In combination with *Cache Enabler*, the same as above can be achieved, but with page caching.
|
23 |
+
5. You can also deliver webp to *all* browsers and add the [webpjs](http://webpjs.appspot.com) javascript, which provides webp support for browsers that doesn't support webp natively. You currently have to add the javascript yourself, but I expect to add an option for it in the next release.
|
24 |
Â
|
25 |
Â
The plugin builds on [WebPConvert](https://github.com/rosell-dk/webp-convert) and its "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/webp-on-demand/webp-on-demand.md)
|
26 |
Â
|
27 |
+
### Benefits
|
28 |
Â
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
|
29 |
Â
- Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
|
30 |
Â
- Better ranking in Google searches (performance is taken into account by Google)
|
31 |
Â
- Less bandwidth consumption - makes a huge difference in the parts of the world where the internet is slow and costly (you know, ~80% of the world population lives under these circumstances).
|
32 |
+
- Currently ~73% of all traffic, and ~79% of mobile browsing traffic are done with browsers supporting webp. With Mozilla and Microsoft [finally on board](https://medium.com/@richard_90141/webp-image-support-an-8-year-saga-7aa2bedb8d02), these numbers are bound to increase. Check current numbers on [caniuse.com](https://caniuse.com/webp)).
|
33 |
Â
|
34 |
Â
|
35 |
Â
## Installation
|
42 |
Â
### Configuring
|
43 |
Â
You configure the plugin in *Settings > WebP Express*.
|
44 |
Â
|
45 |
+
#### Operation modes
|
46 |
+
As sort of a main switch, you can choose between the following modes of operation:
|
47 |
+
|
48 |
+
*Varied image responses*:
|
49 |
+
WebP Express creates redirection rules for images, such that a request for a jpeg will result in a webp – but only if the request comes from a webp-enabled browser. If a webp already exists, it is served immediately. Otherwise it is converted and then served. Note that not all CDN's handles varied responses well.
|
50 |
+
|
51 |
+
*CDN friendly*:
|
52 |
+
In "CDN friendly" mode, a jpeg is always served as a jpeg. Instead of varying the image response, WebP Express alters the HTML for webp usage.
|
53 |
+
|
54 |
+
*Just redirect*:
|
55 |
+
In "just redirect" mode, WebP Express is used just for redirecting jpeg and pngs to existing webp images in the same folder. So in this mode, WebP express will not do any converting. It may be that you use another plugin for that, or that you converted the images off-line and uploaded them manually.
|
56 |
+
|
57 |
+
*Tweaked*:
|
58 |
+
Here you have all options available.
|
59 |
+
|
60 |
+
|
61 |
Â
#### Conversion methods
|
62 |
Â
WebP Express has a bunch of methods available for converting images: Executing cwebp binary, Gd extension, Imagick extension, ewww cloud converter and remote WebP express. Each requires *something*. In many cases, one of the conversion methods will be available. You can quickly identify which converters are working - there is a green icon next to them. Hovering conversion methods that are not working will show you what is wrong.
|
63 |
Â
|
80 |
Â
|
81 |
Â
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. It could also be that your images are handled by nginx.
|
82 |
Â
|
83 |
+
Note that the plugin does not change any HTML (unless you enabled the *Alter HTML* option). 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:
|
84 |
Â
|
85 |
Â
- Open the page in Google Chrome
|
86 |
Â
- Right-click the page and choose "Inspect"
|
95 |
Â
### Notes
|
96 |
Â
|
97 |
Â
*Note:*
|
98 |
+
The redirect rules created in *.htaccess* are pointing to a PHP 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 a rare situation, WebP Express are not using any resources monitoring this. However, it will do the check when you visit the settings page.
|
99 |
Â
|
100 |
Â
*Note:*
|
101 |
Â
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.
|
137 |
Â
I shall write more on this FAQ item... Stay tuned.
|
138 |
Â
|
139 |
Â
### How can a webp image be served on an URL ending with "jpg"?
|
140 |
+
Easy enough. Browsers looks at the *content type* header rather than the URL to determine what it is that it gets. So, although it can be confusing that the resource at *example.com/image.jpg* is a webp image, rest assured that the browsers are not confused. To determine if the plugin is working, you must therefore examine the *content type* response header rather than the URL. See the "How do I verify that the plugin is working?" Faq item.
|
141 |
Â
|
142 |
Â
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
|
143 |
Â
|
144 |
Â
### I am on NGINX / OpenResty
|
145 |
+
It is possible to make WebP Express work on NGINX, but it requires manually inserting redirection rules in the NGINX configuration file (nginx.conf or the configuration file for the site, found in `/etc/nginx/sites-available`). On most setups the following rules should work:
|
146 |
Â
|
147 |
Â
```
|
148 |
Â
if ($http_accept ~* "webp"){
|
187 |
Â
|
188 |
Â
But there is a case for using WebP Express rather than Jetpacks "Speed up image load times" feature:
|
189 |
Â
|
190 |
+
Jetpack has the same drawback as the *Varied image responses* operation mode: If a user downloads the file, there will be a mismatch between the file extension and the image type (the file is ie called "logo.jpg", but it is really a webp image). I don't think that is a big issue, but for those who do, WebP Express might still be for you, even though you have Jetpack. And that is because WebP Express can be set up just to generate webp's, without doing the internal redirection to webp (will be possible from version 0.10.0). You can then for example use the [Cache Enabler](https://wordpress.org/plugins/cache-enabler/) plugin, which is able to generate and cache two versions of each page. One for browsers that accepts webp and one for those that don't. In the HTML for webp-enabled browsers, the images points directly to the webp files.
|
191 |
Â
|
192 |
Â
Pro Jetpack:
|
193 |
Â
- It is a free CDN which serves webp out of the box.
|
212 |
Â
|
213 |
Â
Note: If you experience that the general auto option doesn't show, even though the above-mentioned requirements should be in order, check out [this support-thread](https://wordpress.org/support/topic/still-no-auto-option/).
|
214 |
Â
|
215 |
+
### How do I configure my CDN ("Varied image responses" mode)?
|
216 |
+
In *Varied image responses* operation mode, the image responses *varies* depending on whether the browser supports webp or not (which browsers signals in the *Accept* header). Some CDN's support this out of the box, others requires some configuration and others doesn't support it at all.
|
217 |
+
|
218 |
+
For a CDN to cooperate, it needs to
|
219 |
+
1) forward the *Accept* header and
|
220 |
+
2) Honour the Vary:Accept response header.
|
221 |
+
|
222 |
+
You can also make it "work" on some CDN's by bypassing cache for images. But I rather suggest that you try out the *CDN friendly* mode (see next FAQ item)
|
223 |
+
|
224 |
+
#### Status of some CDN's
|
225 |
+
|
226 |
+
- *KeyCDN*: Does not support varied image responses. I have added a feature request [here](https://community.keycdn.com/t/support-vary-accept-header-for-conditional-webp/1864). You can give it a +1 if you like!
|
227 |
+
- *Cloudflare*: See the "I am on Cloudflare" item
|
228 |
+
- *Cloudfront*: Works, but needs to be configured to forward the accept header. Go to *Distribution settings*, find the *Behavior tab*, select the Behavior and click the Edit button. Choose *Whitelist* from *Forward Headers* and then add the "Accept" header to the whitelist.
|
229 |
+
|
230 |
+
I shall add more to the list. You are welcome to help out [here](https://wordpress.org/support/topic/which-cdns-works-in-standard-mode/).
|
231 |
+
|
232 |
+
### How do I make it work with CDN? ("CDN friendly" mode)
|
233 |
+
In *CDN friendly* mode, there is no trickery with varied image responses, so no special attention is required *on the CDN*.
|
234 |
+
|
235 |
+
However, there are other pitfalls.
|
236 |
+
|
237 |
+
The thing is that, unless you have the whole site on a CDN, you are probably using a plugin that *alters the HTML* in order to point your static assets to the CDN. If you have enabled the "Alter HTML" in WebP Express, it means that you now have *two alterations* on the image URLs!
|
238 |
+
|
239 |
+
How will that play out?
|
240 |
Â
|
241 |
+
Well, if *WebP Express* gets to alter the HTML *after* the image URLs have been altered to point to a CDN, we have trouble. WebP Express does not alter external images but the URLs are now external.
|
242 |
+
|
243 |
+
However, if *WebP Express* gets to alter the HTML *before* the other plugin, things will work fine.
|
244 |
+
|
245 |
+
So it is important that *WebP Express* gets there first.
|
246 |
+
|
247 |
+
*The good news is that WebP Express does get there first on all the plugins I have tested.*
|
248 |
+
|
249 |
+
But what can you do if it doesn't?
|
250 |
+
|
251 |
+
Firstly, you have an option in WebP Express to select between:
|
252 |
+
1. Use content filtering hooks (the_content, the_excerpt, etc)
|
253 |
+
2. The complete page (using output buffering)
|
254 |
+
|
255 |
+
The content filtering hooks gets to process the content before output buffering does. So in case output buffering isn't early enough for you, choose the content filtering hooks.
|
256 |
+
|
257 |
+
There is a risk that you CDN plugin also uses content filtering hooks. I haven't encountered any though. But if there is any out there that does, chances are that they get to process the content before WebP Express, because I have set the priority of these hooks quite high (10000). The reasoning behind this is to that we want to replace images that might be inserted using the same hook (for example, a theme might use *the_content* filter to insert the featured image). If you do encounter a plugin for changing URLs for CDN which uses the content filtering hooks, you are currently out of luck. Let me know, so I can fix that (ie. by making the priority configurable)
|
258 |
+
|
259 |
+
Here are a list of some plugins for CDN and when they process the HTML:
|
260 |
+
|
261 |
+
| Plugin | Method | Hook(s) | Priority
|
262 |
+
| ----------------- | ------------------ | ------------------------------------------------ | ---------------
|
263 |
+
| BunnyCDN | Output buffering | template_redirect | default (10)
|
264 |
+
| CDN enabler | Output buffering | template_redirect | default (10)
|
265 |
+
| Jetpack | content filtering | the_content, etc | the_content: 10
|
266 |
+
| W3 Total Cache | Output buffering | no hooks. Buffering is started before hooks |
|
267 |
+
| WP Fastest Cache | Output buffering | no hooks. Buffering is started before hooks |
|
268 |
+
| WP Super Cache | Output buffering | init | default (10)
|
269 |
+
|
270 |
+
|
271 |
+
With output buffering the plugin that starts the output buffering first gets to process the output last. So WebP Express starts as late as possible, which is on the `template_redirect` hook, with priority 10000 (higher means later). This is later than the `init` hook, which is again later than the `no hooks`.
|
272 |
Â
|
Â
|
|
273 |
Â
|
274 |
Â
### I am on Cloudflare
|
275 |
Â
Without configuration, Cloudflare will not maintain separate caches for jpegs and webp; all browsers will get jpeg. To make Cloudflare cache not only by URL, but also by header, you need to use the [Custom Cache Key](https://support.cloudflare.com/hc/en-us/articles/115004290387) page rule, and add *Header content* to make separate caches depending on the *Accept* request header.
|
287 |
Â
### WebP Express / ShortPixel setup
|
288 |
Â
Here is a recipe for using WebP Express together with ShortPixel, such that WebP Express generates the webp's, and ShortPixel only is used to create `<picture>` tags, when it detects a webp image in the same folder as an original.
|
289 |
Â
|
290 |
+
**There is really no need to do this anymore, because WebP Express is now capable of replacing img tags with picture tags (check out the Alter HTML option)**
|
Â
|
|
Â
|
|
291 |
Â
|
292 |
Â
You need:
|
293 |
Â
1 x WebP Express
|
294 |
Â
1 x ShortPixel
|
295 |
Â
|
296 |
+
*1. Setup WebP Express*
|
297 |
+
If you do not want to use serve varied images:
|
298 |
Â
- Open WebP Express options
|
299 |
+
- Switch to *CDN friendly* mode.
|
300 |
Â
- Set *File extension* to "Set to .webp"
|
301 |
+
- Make sure the *Convert non-existing webp-files upon request to original image* option is enabled
|
302 |
Â
|
303 |
Â
If you want to *ShortPixel* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
304 |
Â
- Open WebP Express options
|
305 |
+
- Switch to *Varied image responses* mode.
|
306 |
Â
- Set *Destination folder* to "Mingled"
|
307 |
Â
- Set *File extension* to "Set to .webp"
|
308 |
Â
|
309 |
+
*2. Setup ShortPixel*
|
310 |
Â
- Install [ShortPixel](https://wordpress.org/plugins/shortpixel-image-optimiser/) the usual way
|
311 |
Â
- Get an API key and enter it on the options page.
|
312 |
Â
- In *Advanced*, enable the following options:
|
317 |
Â
- *Automatically optimize images added by users in front end.*
|
318 |
Â
- *Automatically optimize Media Library items after they are uploaded (recommended).*
|
319 |
Â
|
320 |
+
*3. Visit a page*
|
321 |
Â
As there are presumably no webps generated yet, ShortPixel will not generate `<picture>` tags on the first visit. However, the images that are referenced causes the WebP Express *Auto convert* feature to kick in and generate webp images for each image on that page.
|
322 |
Â
|
323 |
+
*4. Visit the page again*
|
324 |
Â
As *WebP Express* have generated webps in the same folder as the originals, *ShortPixel* detects these, and you should see `<picture>` tags which references the webp's.
|
325 |
Â
|
326 |
+
*ShortPixel or Cache Enabler ?*
|
327 |
Â
Cache Enabler has the advantage over ShortPixel that the HTML structure remains the same. With ShortPixel, image tags are wrapped in a `<picture>` tag structure, and by doing that, there is a risk of breaking styles.
|
328 |
Â
|
329 |
Â
Further, Cache Enabler *caches* the HTML. This is good for performance. However, this also locks you to using that plugin for caching. With ShortPixel, you can keep using your favourite caching plugin.
|
331 |
Â
Cache Enabler will not work if you are caching HTML on a CDN, because the HTML varies depending on the *Accept* header and it doesn't signal this with a Vary:Accept header. You could however add that manually. ShortPixel does not have that issue, as the HTML is the same for all.
|
332 |
Â
|
333 |
Â
### WebP Express / Cache Enabler setup
|
334 |
+
The WebP Express / Cache Enabler setup is quite potent and very CDN-friendly. *Cache Enabler* is used for generating *and caching* two versions of the HTML (one for webp-enabled browsers and one for webp-disabled browsers)
|
335 |
Â
|
336 |
Â
The reason for doing this could be:
|
337 |
+
1. You are using a CDN which cannot be configured to work in the "Varied image responses" mode.
|
338 |
+
2. You could tweak your CDN to work in the "Varied image responses" mode, but you would have to do it by using the entire Accept header as key. Doing that would increase the risk of cache MISS, and you therefore decided that do not want to do that.
|
339 |
+
3. You think it is problematic that when a user saves an image, it has the jpg extension, even though it is a webp image.
|
340 |
Â
|
341 |
Â
You need:
|
342 |
Â
1 x WebP Express
|
343 |
Â
1 x Cache Enabler
|
344 |
Â
|
345 |
+
*1. Setup WebP Express*
|
346 |
+
If you do not want to use serve varied images:
|
347 |
Â
- Open WebP Express options
|
348 |
+
- Switch to *CDN friendly* mode.
|
349 |
Â
- Set *File extension* to "Set to .webp"
|
350 |
+
- Enable *Alter HTML* and select *Replace image URLs*. It is not absolutely neccessary, as Cache Enabler also alters HTML - but there are several reasons to do it. Firstly, *Cache Enabler* doesn't get as many URLs replaced as we do. WebP Express for example also replaces background urls in inline styles. Secondly, *Cache enabler* has [problems in edge cases](https://regexr.com/46isf). Thirdly, WebP Express can be configured to alter HTML to point to corresponding webp images, *before they even exists* which can be used in conjunction with the the *Convert non-existing webp-files upon request* option. And this is smart, because then you don't have trouble with *Cache Enabler* caching HTML which references the original images due to that some images hasn't been converted yet.
|
351 |
+
- If you enabled *Alter HTML*, also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*
|
352 |
+
- If you did not enable *Alter HTML*, enable *Convert non-existing webp-files upon request to original image*
|
353 |
Â
|
354 |
Â
If you want to *Cache Enabler* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
355 |
Â
- Open WebP Express options
|
356 |
+
- Switch to *Varied image responses* mode.
|
357 |
Â
- Set *Destination folder* to "Mingled"
|
358 |
Â
- Set *File extension* to "Set to .webp"
|
359 |
+
- I suggest you enable *Alter HTML* and select *Replace image URLs*. And also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*.
|
360 |
Â
|
361 |
+
*2. Setup Cache Enabler*
|
362 |
Â
- Open the options
|
363 |
Â
- Enable of the *Create an additional cached version for WebP image support* option
|
364 |
Â
|
365 |
+
*3. If you did not enable Alter HTML and Reference webps that hasn't been converted yet: Let rise in a warm place until doubled*
|
366 |
+
*WebP Express* creates *webp* images on need basis. It needs page visits in order to do the conversions . Bulk conversion is on the roadmap, but until then, you need to visit all pages of relevance. You can either do it manually, let your visitors do it (that is: wait a bit), or, if you are on linux, you can use `wget` to grab your website:
|
367 |
Â
|
368 |
Â
```
|
369 |
Â
wget -e robots=off -r -np -w 2 http://www.example.com
|
374 |
Â
`-np` (no-parent) makes wget stay within the boundaries (doesn't go into parent folders)
|
375 |
Â
`w 2` Waits two seconds between each request, in order not to stress the server
|
376 |
Â
|
377 |
+
*4. Clear the Cache Enabler cache.*
|
378 |
Â
Click the "Clear Cache" button in the top right corner in order to clear the Cache Enabler cache.
|
379 |
Â
|
380 |
+
*5. Inspect the HTML*
|
381 |
Â
When visiting a page with images on, different HTML will be served to browsers, depending on whether they support webp or not.
|
382 |
Â
|
383 |
Â
In a webp-enabled browser, the HTML may look like this: `<img src="image.webp">`, while in a non-webp enabled browser, it looks like this: `<img src="image.jpg">`
|
384 |
Â
|
385 |
Â
|
386 |
+
*6. Optionally add Cache Enabler rewrite rules in your .htaccess*
|
387 |
Â
*Cache Enabler* provides some rewrite rules that redirects to the cached file directly in the `.htaccess`, bypassing PHP entirely. Their plugin doesn't do that for you, so you will have to do it manually in order to get the best performance. The rules are in the "Advanced configuration" section on [this page](https://www.keycdn.com/support/wordpress-cache-enabler-plugin).
|
388 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
389 |
Â
|
390 |
Â
### Does it work with lazy loaded images?
|
391 |
Â
No plugins/frameworks has yet been discovered, which does not work with *WebP Express*.
|
396 |
Â
- [BJ Lazy Load](https://da.wordpress.org/plugins/bj-lazy-load/)
|
397 |
Â
- [Owl Carousel 2](https://owlcarousel2.github.io/OwlCarousel2/)
|
398 |
Â
|
399 |
+
I have only tested the above in *Varied image responses* mode, but it should also work in *CDN friendly* mode. Both *Alter HTML* options have been designed to work with standard lazy load attributes.
|
400 |
+
|
401 |
Â
### When is feature X coming? / Roadmap
|
402 |
Â
No schedule. I move forward as time allows. I currently spend a lot of time answering questions in the support forum. If someone would be nice and help out answering questions here, it would allow me to spend that time developing. Also, donations would allow me to turn down some of the more boring requests from my customers, and speed things up here.
|
403 |
Â
|
404 |
+
Here are my current plans ahead: The 0.12 release will allow webp for all browsers! - using [this wonderful javascript library](https://webpjs.appspot.com/). The 0.13 release will probably be multisite support, as this has been requested by many. 0.14 might be adding some diagnose tool – this should release some time spend in the forum. 0.54 could be focused on PNG. 0.16 might be displaying rules for NGINX. 0.17 might be supporting Save-Data header (send extra compressed images to clients who wants to use as little bandwidth as possible). 0.18 might be a file manager-like interface for inspecting generated webp files. 0.19 might be WAMP support. The current milestones, their subtasks and their progress can be viewed here: https://github.com/rosell-dk/webp-express/milestones
|
405 |
Â
|
406 |
Â
If you wish to affect priorities, it is certainly possible. You can try to argue your case in the forum or you can simply let the money do the talking. By donating as little as a cup of coffee on [ko-fi.com/rosell](https://ko-fi.com/rosell), you can leave a wish. I shall take these wishes into account when prioritizing between new features.
|
407 |
Â
|
408 |
+
## Changes in 0.11.0
|
409 |
+
- Alter HTML to point to webp files (choose between picture tags or simply altering all image urls)
|
410 |
+
- Convert non-existing webp-files upon request (means you can reference the converted webp files before they are actually converted!)
|
411 |
+
|
412 |
+
## Changes in 0.10.0
|
413 |
+
- Introduced "Operation modes" in order to keep setting screens simple but still allow tweaking
|
414 |
+
- WebP Express can now be used in conjunction with Cache Enabler and ShortPixel
|
415 |
+
- Cache-Control header is now added in *.htaccess*, when redirecting directly to existing webp
|
416 |
Â
|
417 |
Â
## Changes in 0.9.0
|
418 |
Â
- Optionally make .htaccess redirect directly to existing webp (improves performance)
|
README.txt
CHANGED
@@ -4,27 +4,38 @@ Donate link: https://ko-fi.com/rosell
|
|
4 |
Â
Tags: webp, images, performance
|
5 |
Â
Requires at least: 4.0
|
6 |
Â
Tested up to: 5.0
|
7 |
-
Stable tag: 0.
|
8 |
Â
Requires PHP: 5.6
|
9 |
Â
License: GPLv3
|
10 |
Â
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
11 |
Â
|
12 |
-
Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP.
|
13 |
Â
|
14 |
Â
== Description ==
|
15 |
Â
|
16 |
Â
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
|
17 |
Â
|
18 |
-
The
|
Â
|
|
19 |
Â
|
20 |
-
The
|
21 |
Â
|
22 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
23 |
Â
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
|
24 |
Â
- Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
|
25 |
Â
- Better ranking in Google searches (performance is taken into account by Google)
|
26 |
-
- Less bandwidth consumption - makes a difference
|
27 |
-
- Currently ~73% of all traffic, and ~
|
28 |
Â
|
29 |
Â
== Installation ==
|
30 |
Â
|
@@ -36,6 +47,21 @@ The plugin is developed on [github](https://github.com/rosell-dk/webp-express/).
|
|
36 |
Â
### Configuring
|
37 |
Â
You configure the plugin in *Settings > WebP Express*.
|
38 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
39 |
Â
#### Conversion methods
|
40 |
Â
WebP Express has a bunch of methods available for converting images: Executing cwebp binary, Gd extension, Imagick extension, ewww cloud converter and remote WebP express. Each requires *something*. In many cases, one of the conversion methods will be available. You can quickly identify which converters are working - there is a green icon next to them. Hovering conversion methods that are not working will show you what is wrong.
|
41 |
Â
|
@@ -56,7 +82,7 @@ If you do not the "auto" option available:
|
|
56 |
Â
### Notes
|
57 |
Â
|
58 |
Â
*Note:*
|
59 |
-
The redirect rules created in *.htaccess* are pointing to a PHP 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
|
60 |
Â
|
61 |
Â
*Note:*
|
62 |
Â
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.
|
@@ -83,7 +109,7 @@ Once, you have a converter, that works, when you click the "test"-button, you ar
|
|
83 |
Â
|
84 |
Â
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. It could also be that your images are handled by nginx.
|
85 |
Â
|
86 |
-
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:
|
87 |
Â
|
88 |
Â
- Open the page in Google Chrome
|
89 |
Â
- Right-click the page and choose "Inspect"
|
@@ -119,12 +145,12 @@ I shall write more on this FAQ item... Stay tuned.
|
|
119 |
Â
|
120 |
Â
|
121 |
Â
= How can a webp image be served on an URL ending with "jpg"? =
|
122 |
-
Easy enough. Browsers looks at the *content type* header rather than the URL to determine what it is that it gets. So, although it can be confusing that the resource at *example.com/image.jpg* is a webp image, rest
|
123 |
Â
|
124 |
Â
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
|
125 |
Â
|
126 |
Â
= I am on NGINX / OpenResty =
|
127 |
-
It is possible to make WebP Express work on NGINX, but it
|
128 |
Â
|
129 |
Â
```
|
130 |
Â
if ($http_accept ~* "webp"){
|
@@ -194,35 +220,62 @@ If you have the *Imagick*, the *Imagick binary* or the *Remote WebP Express* con
|
|
194 |
Â
|
195 |
Â
Note: If you experience that the general auto option doesn't show, even though the above-mentioned requirements should be in order, check out [this support-thread](https://wordpress.org/support/topic/still-no-auto-option/).
|
196 |
Â
|
197 |
-
= How do I configure my CDN
|
198 |
-
In *
|
199 |
Â
|
200 |
Â
For a CDN to cooperate, it needs to
|
201 |
Â
1) forward the *Accept* header and
|
202 |
Â
2) Honour the Vary:Accept response header.
|
203 |
Â
|
204 |
-
You can also make it "work" on some CDN's by bypassing cache for images. But I rather suggest that you try out the *
|
205 |
Â
|
206 |
Â
*Status of some CDN's*:
|
207 |
Â
|
208 |
-
- *KeyCDN*: Does not support varied responses. I have added a feature request [here](https://community.keycdn.com/t/support-vary-accept-header-for-conditional-webp/1864). You can give it a +1 if you like!
|
209 |
Â
- *Cloudflare*: See the "I am on Cloudflare" item
|
210 |
Â
- *Cloudfront*: Works, but needs to be configured to forward the accept header. Go to *Distribution settings*, find the *Behavior tab*, select the Behavior and click the Edit button. Choose *Whitelist* from *Forward Headers* and then add the "Accept" header to the whitelist.
|
211 |
Â
|
212 |
Â
I shall add more to the list. You are welcome to help out [here](https://wordpress.org/support/topic/which-cdns-works-in-standard-mode/).
|
213 |
Â
|
214 |
-
|
215 |
-
In *
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
216 |
Â
|
217 |
-
|
218 |
Â
|
219 |
-
|
Â
|
|
Â
|
|
220 |
Â
|
221 |
-
|
222 |
Â
|
223 |
-
But
|
224 |
Â
|
225 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
226 |
Â
|
227 |
Â
= I am on Cloudflare =
|
228 |
Â
Without configuration, Cloudflare will not maintain separate caches for jpegs and webp; all browsers will get jpeg. To make Cloudflare cache not only by URL, but also by header, you need to use the [Custom Cache Key](https://support.cloudflare.com/hc/en-us/articles/115004290387) page rule, and add *Header content* to make separate caches depending on the *Accept* request header.
|
@@ -240,24 +293,22 @@ To make *WebP Express* work on a free Cloudflare account, you have the following
|
|
240 |
Â
### WebP Express / ShortPixel setup
|
241 |
Â
Here is a recipe for using WebP Express together with ShortPixel, such that WebP Express generates the webp's, and ShortPixel only is used to create `<picture>` tags, when it detects a webp image in the same folder as an original.
|
242 |
Â
|
243 |
-
|
244 |
-
1. You are using a CDN which cannot be configured to work with the Standard WebP Express setup.
|
245 |
-
2. You think it is problematic that when a user saves an image, it has the jpg extension, even though it is a webp image.
|
246 |
Â
|
247 |
Â
You need:
|
248 |
Â
1 x WebP Express
|
249 |
Â
1 x ShortPixel
|
250 |
Â
|
251 |
Â
*1. Setup WebP Express*
|
252 |
-
If you
|
253 |
Â
- Open WebP Express options
|
254 |
-
- Switch to *
|
255 |
Â
- Set *File extension* to "Set to .webp"
|
256 |
-
- Make sure the *
|
257 |
Â
|
258 |
Â
If you want to *ShortPixel* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
259 |
Â
- Open WebP Express options
|
260 |
-
- Switch to *
|
261 |
Â
- Set *Destination folder* to "Mingled"
|
262 |
Â
- Set *File extension* to "Set to .webp"
|
263 |
Â
|
@@ -286,36 +337,39 @@ Further, Cache Enabler *caches* the HTML. This is good for performance. However,
|
|
286 |
Â
Cache Enabler will not work if you are caching HTML on a CDN, because the HTML varies depending on the *Accept* header and it doesn't signal this with a Vary:Accept header. You could however add that manually. ShortPixel does not have that issue, as the HTML is the same for all.
|
287 |
Â
|
288 |
Â
### WebP Express / Cache Enabler setup
|
289 |
-
The WebP Express / Cache Enabler setup is quite potent and very CDN-friendly. Cache Enabler is used for generating and caching two versions of the HTML (one for webp-enabled browsers and one for webp-disabled browsers)
|
290 |
Â
|
291 |
Â
The reason for doing this could be:
|
292 |
-
1. You are using a CDN which cannot be configured to work
|
293 |
-
2. You
|
Â
|
|
294 |
Â
|
295 |
Â
You need:
|
296 |
Â
1 x WebP Express
|
297 |
Â
1 x Cache Enabler
|
298 |
Â
|
299 |
Â
*1. Setup WebP Express*
|
300 |
-
If you
|
301 |
Â
- Open WebP Express options
|
302 |
-
- Switch to *
|
303 |
Â
- Set *File extension* to "Set to .webp"
|
304 |
-
-
|
Â
|
|
Â
|
|
305 |
Â
|
306 |
Â
If you want to *Cache Enabler* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
307 |
Â
- Open WebP Express options
|
308 |
-
- Switch to *
|
309 |
Â
- Set *Destination folder* to "Mingled"
|
310 |
Â
- Set *File extension* to "Set to .webp"
|
Â
|
|
311 |
Â
|
312 |
Â
*2. Setup Cache Enabler*
|
313 |
Â
- Open the options
|
314 |
Â
- Enable of the *Create an additional cached version for WebP image support* option
|
315 |
Â
|
316 |
-
|
317 |
-
*
|
318 |
-
*WebP Express* creates *webp* images on need basis. It needs page visits in order to do the convertions . Bulk conversion is on the roadmap, but until then, you need to visit all pages of relevance. You can either do it manually, let your visitors do it (that is: wait a bit), or, if you are on linux, you can use `wget` to grab your website:
|
319 |
Â
|
320 |
Â
```
|
321 |
Â
wget -e robots=off -r -np -w 2 http://www.example.com
|
@@ -338,15 +392,8 @@ In a webp-enabled browser, the HTML may look like this: `<img src="image.webp">`
|
|
338 |
Â
*6. Optionally add Cache Enabler rewrite rules in your .htaccess*
|
339 |
Â
*Cache Enabler* provides some rewrite rules that redirects to the cached file directly in the `.htaccess`, bypassing PHP entirely. Their plugin doesn't do that for you, so you will have to do it manually in order to get the best performance. The rules are in the "Advanced configuration" section on [this page](https://www.keycdn.com/support/wordpress-cache-enabler-plugin).
|
340 |
Â
|
341 |
-
*ShortPixel or Cache Enabler ?*
|
342 |
-
Cache Enabler has the advantage over ShortPixel that the HTML structure remains the same. With ShortPixel, image tags are wrapped in a `<picture>` tag structure, and by doing that, there is a risk of breaking styles.
|
343 |
-
|
344 |
-
Further, Cache Enabler *caches* the HTML. This is good for performance. However, this also locks you to using that plugin for caching. With ShortPixel, you can keep using your favourite caching plugin.
|
345 |
-
|
346 |
-
Cache Enabler will not work if you are caching HTML on a CDN, because the HTML varies depending on the *Accept* header and it doesn't signal this with a Vary:Accept header. You could however add that manually. ShortPixel does not have that issue, as the HTML is the same for all.
|
347 |
-
|
348 |
Â
= Does it work with lazy loaded images? =
|
349 |
-
No plugins/frameworks has yet been discovered, which does not work with *WebP Express
|
350 |
Â
|
351 |
Â
The most common way of lazy-loading is by setting a *data-src* attribute on the image and let javascript use that value for setten the *src* attribute. That method works, as the image request, seen from the server side, is indistinguishable from any other image request. It could however be that some obscure lazy load implementation would load the image with an XHR request. In that case, the *Accept* header will not contain 'image/webp', but '*/*', and a jpeg will be served, even though the browser supports webp.
|
352 |
Â
|
@@ -354,10 +401,12 @@ The following lazy load plugins/frameworks has been tested and works with *WebP
|
|
354 |
Â
- [BJ Lazy Load](https://da.wordpress.org/plugins/bj-lazy-load/)
|
355 |
Â
- [Owl Carousel 2](https://owlcarousel2.github.io/OwlCarousel2/)
|
356 |
Â
|
Â
|
|
Â
|
|
357 |
Â
= When is feature X coming? / Roadmap =
|
358 |
Â
No schedule. I move forward as time allows. I currently spend a lot of time answering questions in the support forum. If someone would be nice and help out answering questions here, it would allow me to spend that time developing. Also, donations would allow me to turn down some of the more boring requests from my customers, and speed things up here.
|
359 |
Â
|
360 |
-
Here are my current plans ahead: The 0.
|
361 |
Â
|
362 |
Â
If you wish to affect priorities, it is certainly possible. You can try to argue your case in the forum or you can simply let the money do the talking. By donating as little as a cup of coffee on [ko-fi.com/rosell](https://ko-fi.com/rosell), you can leave a wish. I shall take these wishes into account when prioritizing between new features.
|
363 |
Â
|
@@ -370,6 +419,12 @@ Easy enough! - [Go here!](https://ko-fi.com/rosell). Or [here](https://buymeacof
|
|
370 |
Â
|
371 |
Â
== Changelog ==
|
372 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
373 |
Â
= 0.10.0 =
|
374 |
Â
* Introduced "Operation modes" in order to keep setting screens simple but still allow tweaking
|
375 |
Â
* WebP Express can now be used in conjunction with Cache Enabler and ShortPixel
|
@@ -457,6 +512,9 @@ For older releases, check out changelog.txt
|
|
457 |
Â
|
458 |
Â
== Upgrade Notice ==
|
459 |
Â
|
Â
|
|
Â
|
|
Â
|
|
460 |
Â
= 0.10.0 =
|
461 |
Â
WebP Express can now be used in conjunction with Cache Enabler and ShortPixel. Also introduced "Operation modes" in order to keep setting screens simple but still allow tweaking.
|
462 |
Â
|
4 |
Â
Tags: webp, images, performance
|
5 |
Â
Requires at least: 4.0
|
6 |
Â
Tested up to: 5.0
|
7 |
+
Stable tag: 0.11.0
|
8 |
Â
Requires PHP: 5.6
|
9 |
Â
License: GPLv3
|
10 |
Â
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
11 |
Â
|
12 |
+
Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP.
|
13 |
Â
|
14 |
Â
== Description ==
|
15 |
Â
|
16 |
Â
Almost 4 out of 5 mobile users use a browser that is able to display webp images. Yet, on most websites, they are served jpeg images, which are typically double the size of webp images for a given quality. What a waste of bandwidth! This plugin was created to help remedy that situation. With little effort, Wordpress admins can have their site serving autogenerated webp images to browsers that supports it, while still serving jpeg and png files to browsers that does not support webp.
|
17 |
Â
|
18 |
+
### The image converter
|
19 |
+
The plugin uses the [WebP Convert](https://github.com/rosell-dk/webp-convert) library to convert images to webp. *WebP Convert* is able to convert images using multiple methods. There are the "local" conversion methods: `cwebp`, `gd`, `imagick`. If none of these works on your host, there are the cloud alternatives: `ewww` (paid) or connecting to a Wordpress site where you got WebP Express installed and you enabled the "web service" functionality.
|
20 |
Â
|
21 |
+
### The "Serving webp to browsers that supports it" part.
|
22 |
Â
|
23 |
+
The plugin supports different ways of delivering webps to browsers that supports it:
|
24 |
+
|
25 |
+
1. By routing jpeg/png images to the corresponding webp - or to the image converter if the image hasn't been converted yet.
|
26 |
+
2. By altering the HTML, replacing image tags with *picture* tags. Missing webps are auto generated upon visit.
|
27 |
+
3. By altering the HTML, replacing image URLs so all points to webp. The replacements only being made for browsers that supports webp. Again, missing webps are auto generated upon visit.
|
28 |
+
4. In combination with *Cache Enabler*, the same as above can be achieved, but with page caching.
|
29 |
+
5. You can also deliver webp to *all* browsers and add the [webpjs](http://webpjs.appspot.com) javascript, which provides webp support for browsers that doesn't support webp natively. You currently have to add the javascript yourself, but I expect to add an option for it in the next release.
|
30 |
+
|
31 |
+
The plugin builds on [WebPConvert](https://github.com/rosell-dk/webp-convert) and its "WebP On Demand" solution described [here](https://github.com/rosell-dk/webp-convert/blob/master/docs/webp-on-demand/webp-on-demand.md)
|
32 |
+
|
33 |
+
### Benefits
|
34 |
Â
- Much faster load time for images in browsers that supports webp. The converted images are typically *less than half the size* (for jpeg), while maintaining the same quality. Bear in mind that for most web sites, images are responsible for the largest part of the waiting time.
|
35 |
Â
- Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
|
36 |
Â
- Better ranking in Google searches (performance is taken into account by Google)
|
37 |
+
- Less bandwidth consumption - makes a huge difference in the parts of the world where the internet is slow and costly (you know, ~80% of the world population lives under these circumstances).
|
38 |
+
- Currently ~73% of all traffic, and ~79% of mobile browsing traffic are done with browsers supporting webp. With Mozilla and Microsoft [finally on board](https://medium.com/@richard_90141/webp-image-support-an-8-year-saga-7aa2bedb8d02), these numbers are bound to increase. Check current numbers on [caniuse.com](https://caniuse.com/webp)).
|
39 |
Â
|
40 |
Â
== Installation ==
|
41 |
Â
|
47 |
Â
### Configuring
|
48 |
Â
You configure the plugin in *Settings > WebP Express*.
|
49 |
Â
|
50 |
+
#### Operation modes
|
51 |
+
As sort of a main switch, you can choose between the following modes of operation:
|
52 |
+
|
53 |
+
*Varied image responses*:
|
54 |
+
WebP Express creates redirection rules for images, such that a request for a jpeg will result in a webp – but only if the request comes from a webp-enabled browser. If a webp already exists, it is served immediately. Otherwise it is converted and then served. Note that not all CDN's handles varied responses well.
|
55 |
+
|
56 |
+
*CDN friendly*:
|
57 |
+
In "CDN friendly" mode, a jpeg is always served as a jpeg. Instead of varying the image response, WebP Express alters the HTML for webp usage.
|
58 |
+
|
59 |
+
*Just redirect*:
|
60 |
+
In "just redirect" mode, WebP Express is used just for redirecting jpeg and pngs to existing webp images in the same folder. So in this mode, WebP express will not do any converting. It may be that you use another plugin for that, or that you converted the images off-line and uploaded them manually.
|
61 |
+
|
62 |
+
*Tweaked*:
|
63 |
+
Here you have all options available.
|
64 |
+
|
65 |
Â
#### Conversion methods
|
66 |
Â
WebP Express has a bunch of methods available for converting images: Executing cwebp binary, Gd extension, Imagick extension, ewww cloud converter and remote WebP express. Each requires *something*. In many cases, one of the conversion methods will be available. You can quickly identify which converters are working - there is a green icon next to them. Hovering conversion methods that are not working will show you what is wrong.
|
67 |
Â
|
82 |
Â
### Notes
|
83 |
Â
|
84 |
Â
*Note:*
|
85 |
+
The redirect rules created in *.htaccess* are pointing to a PHP 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 a rare situation, WebP Express are not using any resources monitoring this. However, it will do the check when you visit the settings page.
|
86 |
Â
|
87 |
Â
*Note:*
|
88 |
Â
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.
|
109 |
Â
|
110 |
Â
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. It could also be that your images are handled by nginx.
|
111 |
Â
|
112 |
+
Note that the plugin does not change any HTML (unless you enabled the *Alter HTML* option). 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:
|
113 |
Â
|
114 |
Â
- Open the page in Google Chrome
|
115 |
Â
- Right-click the page and choose "Inspect"
|
145 |
Â
|
146 |
Â
|
147 |
Â
= How can a webp image be served on an URL ending with "jpg"? =
|
148 |
+
Easy enough. Browsers looks at the *content type* header rather than the URL to determine what it is that it gets. So, although it can be confusing that the resource at *example.com/image.jpg* is a webp image, rest assured that the browsers are not confused. To determine if the plugin is working, you must therefore examine the *content type* response header rather than the URL. See the "How do I verify that the plugin is working?" Faq item.
|
149 |
Â
|
150 |
Â
I am btw considering making an option to have the plugin redirect to the webp instead of serving immediately. That would remove the apparent mismatch between file extension and content type header. However, the cost of doing that will be an extra request for each image, which means extra time and worse performance. I believe you'd be ill advised to use that option, so I guess I will not implement it. But perhaps you have good reasons to use it? If you do, please let me know!
|
151 |
Â
|
152 |
Â
= I am on NGINX / OpenResty =
|
153 |
+
It is possible to make WebP Express work on NGINX, but it requires manually inserting redirection rules in the NGINX configuration file (nginx.conf or the configuration file for the site, found in `/etc/nginx/sites-available`). On most setups the following rules should work:
|
154 |
Â
|
155 |
Â
```
|
156 |
Â
if ($http_accept ~* "webp"){
|
220 |
Â
|
221 |
Â
Note: If you experience that the general auto option doesn't show, even though the above-mentioned requirements should be in order, check out [this support-thread](https://wordpress.org/support/topic/still-no-auto-option/).
|
222 |
Â
|
223 |
+
= How do I configure my CDN in "Varied image responses" operation mode? =
|
224 |
+
In *Varied image responses* operation mode, the image responses *varies* depending on whether the browser supports webp or not (which browsers signals in the *Accept* header). Some CDN's support this out of the box, others requires some configuration and others doesn't support it at all.
|
225 |
Â
|
226 |
Â
For a CDN to cooperate, it needs to
|
227 |
Â
1) forward the *Accept* header and
|
228 |
Â
2) Honour the Vary:Accept response header.
|
229 |
Â
|
230 |
+
You can also make it "work" on some CDN's by bypassing cache for images. But I rather suggest that you try out the *CDN friendly* mode (see next FAQ item)
|
231 |
Â
|
232 |
Â
*Status of some CDN's*:
|
233 |
Â
|
234 |
+
- *KeyCDN*: Does not support varied image responses. I have added a feature request [here](https://community.keycdn.com/t/support-vary-accept-header-for-conditional-webp/1864). You can give it a +1 if you like!
|
235 |
Â
- *Cloudflare*: See the "I am on Cloudflare" item
|
236 |
Â
- *Cloudfront*: Works, but needs to be configured to forward the accept header. Go to *Distribution settings*, find the *Behavior tab*, select the Behavior and click the Edit button. Choose *Whitelist* from *Forward Headers* and then add the "Accept" header to the whitelist.
|
237 |
Â
|
238 |
Â
I shall add more to the list. You are welcome to help out [here](https://wordpress.org/support/topic/which-cdns-works-in-standard-mode/).
|
239 |
Â
|
240 |
+
### How do I make it work with CDN? ("CDN friendly" mode)
|
241 |
+
In *CDN friendly* mode, there is no trickery with varied image responses, so no special attention is required *on the CDN*.
|
242 |
+
|
243 |
+
However, there are other pitfalls.
|
244 |
+
|
245 |
+
The thing is that, unless you have the whole site on a CDN, you are probably using a plugin that *alters the HTML* in order to point your static assets to the CDN. If you have enabled the "Alter HTML" in WebP Express, it means that you now have *two alterations* on the image URLs!
|
246 |
+
|
247 |
+
How will that play out?
|
248 |
+
|
249 |
+
Well, if *WebP Express* gets to alter the HTML *after* the image URLs have been altered to point to a CDN, we have trouble. WebP Express does not alter external images but the URLs are now external.
|
250 |
+
|
251 |
+
However, if *WebP Express* gets to alter the HTML *before* the other plugin, things will work fine.
|
252 |
+
|
253 |
+
So it is important that *WebP Express* gets there first.
|
254 |
+
|
255 |
+
*The good news is that WebP Express does get there first on all the plugins I have tested.*
|
256 |
Â
|
257 |
+
But what can you do if it doesn't?
|
258 |
Â
|
259 |
+
Firstly, you have an option in WebP Express to select between:
|
260 |
+
1. Use content filtering hooks (the_content, the_excerpt, etc)
|
261 |
+
2. The complete page (using output buffering)
|
262 |
Â
|
263 |
+
The content filtering hooks gets to process the content before output buffering does. So in case output buffering isn't early enough for you, choose the content filtering hooks.
|
264 |
Â
|
265 |
+
There is a risk that you CDN plugin also uses content filtering hooks. I haven't encountered any though. But if there is any out there that does, chances are that they get to process the content before WebP Express, because I have set the priority of these hooks quite high (10000). The reasoning behind this is to that we want to replace images that might be inserted using the same hook (for example, a theme might use *the_content* filter to insert the featured image). If you do encounter a plugin for changing URLs for CDN which uses the content filtering hooks, you are currently out of luck. Let me know, so I can fix that (ie. by making the priority configurable)
|
266 |
Â
|
267 |
+
Here are a list of some plugins for CDN and when they process the HTML:
|
268 |
+
|
269 |
+
| Plugin | Method | Hook(s) | Priority
|
270 |
+
| ----------------- | ------------------ | ------------------------------------------------ | ---------------
|
271 |
+
| BunnyCDN | Output buffering | template_redirect | default (10)
|
272 |
+
| CDN enabler | Output buffering | template_redirect | default (10)
|
273 |
+
| Jetpack | content filtering | the_content, etc | the_content: 10
|
274 |
+
| W3 Total Cache | Output buffering | no hooks. Buffering is started before hooks |
|
275 |
+
| WP Fastest Cache | Output buffering | no hooks. Buffering is started before hooks |
|
276 |
+
| WP Super Cache | Output buffering | init | default (10)
|
277 |
+
|
278 |
+
With output buffering the plugin that starts the output buffering first gets to process the output last. So WebP Express starts as late as possible, which is on the `template_redirect` hook, with priority 10000 (higher means later). This is later than the `init` hook, which is again later than the `no hooks`.
|
279 |
Â
|
280 |
Â
= I am on Cloudflare =
|
281 |
Â
Without configuration, Cloudflare will not maintain separate caches for jpegs and webp; all browsers will get jpeg. To make Cloudflare cache not only by URL, but also by header, you need to use the [Custom Cache Key](https://support.cloudflare.com/hc/en-us/articles/115004290387) page rule, and add *Header content* to make separate caches depending on the *Accept* request header.
|
293 |
Â
### WebP Express / ShortPixel setup
|
294 |
Â
Here is a recipe for using WebP Express together with ShortPixel, such that WebP Express generates the webp's, and ShortPixel only is used to create `<picture>` tags, when it detects a webp image in the same folder as an original.
|
295 |
Â
|
296 |
+
**There is really no need to do this anymore, because WebP Express is now capable of replacing img tags with picture tags (check out the Alter HTML option)**
|
Â
|
|
Â
|
|
297 |
Â
|
298 |
Â
You need:
|
299 |
Â
1 x WebP Express
|
300 |
Â
1 x ShortPixel
|
301 |
Â
|
302 |
Â
*1. Setup WebP Express*
|
303 |
+
If you do not want to use serve varied images:
|
304 |
Â
- Open WebP Express options
|
305 |
+
- Switch to *CDN friendly* mode.
|
306 |
Â
- Set *File extension* to "Set to .webp"
|
307 |
+
- Make sure the *Convert non-existing webp-files upon request to original image* option is enabled
|
308 |
Â
|
309 |
Â
If you want to *ShortPixel* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
310 |
Â
- Open WebP Express options
|
311 |
+
- Switch to *Varied image responses* mode.
|
312 |
Â
- Set *Destination folder* to "Mingled"
|
313 |
Â
- Set *File extension* to "Set to .webp"
|
314 |
Â
|
337 |
Â
Cache Enabler will not work if you are caching HTML on a CDN, because the HTML varies depending on the *Accept* header and it doesn't signal this with a Vary:Accept header. You could however add that manually. ShortPixel does not have that issue, as the HTML is the same for all.
|
338 |
Â
|
339 |
Â
### WebP Express / Cache Enabler setup
|
340 |
+
The WebP Express / Cache Enabler setup is quite potent and very CDN-friendly. *Cache Enabler* is used for generating *and caching* two versions of the HTML (one for webp-enabled browsers and one for webp-disabled browsers)
|
341 |
Â
|
342 |
Â
The reason for doing this could be:
|
343 |
+
1. You are using a CDN which cannot be configured to work in the "Varied image responses" mode.
|
344 |
+
2. You could tweak your CDN to work in the "Varied image responses" mode, but you would have to do it by using the entire Accept header as key. Doing that would increase the risk of cache MISS, and you therefore decided that do not want to do that.
|
345 |
+
3. You think it is problematic that when a user saves an image, it has the jpg extension, even though it is a webp image.
|
346 |
Â
|
347 |
Â
You need:
|
348 |
Â
1 x WebP Express
|
349 |
Â
1 x Cache Enabler
|
350 |
Â
|
351 |
Â
*1. Setup WebP Express*
|
352 |
+
If you do not want to use serve varied images:
|
353 |
Â
- Open WebP Express options
|
354 |
+
- Switch to *CDN friendly* mode.
|
355 |
Â
- Set *File extension* to "Set to .webp"
|
356 |
+
- Enable *Alter HTML* and select *Replace image URLs*. It is not absolutely neccessary, as Cache Enabler also alters HTML - but there are several reasons to do it. Firstly, *Cache Enabler* doesn't get as many URLs replaced as we do. WebP Express for example also replaces background urls in inline styles. Secondly, *Cache enabler* has [problems in edge cases](https://regexr.com/46isf). Thirdly, WebP Express can be configured to alter HTML to point to corresponding webp images, *before they even exists* which can be used in conjunction with the the *Convert non-existing webp-files upon request* option. And this is smart, because then you don't have trouble with *Cache Enabler* caching HTML which references the original images due to that some images hasn't been converted yet.
|
357 |
+
- If you enabled *Alter HTML*, also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*
|
358 |
+
- If you did not enable *Alter HTML*, enable *Convert non-existing webp-files upon request to original image*
|
359 |
Â
|
360 |
Â
If you want to *Cache Enabler* to create <picture> tags but still want the magic to work on other images (such as images are referenced from CSS or javascript):
|
361 |
Â
- Open WebP Express options
|
362 |
+
- Switch to *Varied image responses* mode.
|
363 |
Â
- Set *Destination folder* to "Mingled"
|
364 |
Â
- Set *File extension* to "Set to .webp"
|
365 |
+
- I suggest you enable *Alter HTML* and select *Replace image URLs*. And also enable *Reference webps that hasn't been converted yet* and *Convert non-existing webp-files upon request*.
|
366 |
Â
|
367 |
Â
*2. Setup Cache Enabler*
|
368 |
Â
- Open the options
|
369 |
Â
- Enable of the *Create an additional cached version for WebP image support* option
|
370 |
Â
|
371 |
+
*3. If you did not enable Alter HTML and Reference webps that hasn't been converted yet: Let rise in a warm place until doubled*
|
372 |
+
*WebP Express* creates *webp* images on need basis. It needs page visits in order to do the conversions . Bulk conversion is on the roadmap, but until then, you need to visit all pages of relevance. You can either do it manually, let your visitors do it (that is: wait a bit), or, if you are on linux, you can use `wget` to grab your website:
|
Â
|
|
373 |
Â
|
374 |
Â
```
|
375 |
Â
wget -e robots=off -r -np -w 2 http://www.example.com
|
392 |
Â
*6. Optionally add Cache Enabler rewrite rules in your .htaccess*
|
393 |
Â
*Cache Enabler* provides some rewrite rules that redirects to the cached file directly in the `.htaccess`, bypassing PHP entirely. Their plugin doesn't do that for you, so you will have to do it manually in order to get the best performance. The rules are in the "Advanced configuration" section on [this page](https://www.keycdn.com/support/wordpress-cache-enabler-plugin).
|
394 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
395 |
Â
= Does it work with lazy loaded images? =
|
396 |
+
No plugins/frameworks has yet been discovered, which does not work with *WebP Express*.
|
397 |
Â
|
398 |
Â
The most common way of lazy-loading is by setting a *data-src* attribute on the image and let javascript use that value for setten the *src* attribute. That method works, as the image request, seen from the server side, is indistinguishable from any other image request. It could however be that some obscure lazy load implementation would load the image with an XHR request. In that case, the *Accept* header will not contain 'image/webp', but '*/*', and a jpeg will be served, even though the browser supports webp.
|
399 |
Â
|
401 |
Â
- [BJ Lazy Load](https://da.wordpress.org/plugins/bj-lazy-load/)
|
402 |
Â
- [Owl Carousel 2](https://owlcarousel2.github.io/OwlCarousel2/)
|
403 |
Â
|
404 |
+
I have only tested the above in *Varied image responses* mode, but it should also work in *CDN friendly* mode. Both *Alter HTML* options have been designed to work with standard lazy load attributes.
|
405 |
+
|
406 |
Â
= When is feature X coming? / Roadmap =
|
407 |
Â
No schedule. I move forward as time allows. I currently spend a lot of time answering questions in the support forum. If someone would be nice and help out answering questions here, it would allow me to spend that time developing. Also, donations would allow me to turn down some of the more boring requests from my customers, and speed things up here.
|
408 |
Â
|
409 |
+
Here are my current plans ahead: The 0.12 release will allow webp for all browsers! - using [this wonderful javascript library](https://webpjs.appspot.com/). The 0.13 release will probably be multisite support, as this has been requested by many. 0.14 might be adding some diagnose tool – this should release some time spend in the forum. 0.54 could be focused on PNG. 0.16 might be displaying rules for NGINX. 0.17 might be supporting Save-Data header (send extra compressed images to clients who wants to use as little bandwidth as possible). 0.18 might be a file manager-like interface for inspecting generated webp files. 0.19 might be WAMP support. The current milestones, their subtasks and their progress can be viewed here: https://github.com/rosell-dk/webp-express/milestones
|
410 |
Â
|
411 |
Â
If you wish to affect priorities, it is certainly possible. You can try to argue your case in the forum or you can simply let the money do the talking. By donating as little as a cup of coffee on [ko-fi.com/rosell](https://ko-fi.com/rosell), you can leave a wish. I shall take these wishes into account when prioritizing between new features.
|
412 |
Â
|
419 |
Â
|
420 |
Â
== Changelog ==
|
421 |
Â
|
422 |
+
= 0.11.0 =
|
423 |
+
* Alter HTML to point to webp files (choose between picture tags or simply altering all image urls)
|
424 |
+
* Convert non-existing webp-files upon request (means you can reference the converted webp files before they are actually converted!)
|
425 |
+
|
426 |
+
For more info, see the closed issues on the 0.11.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/14?closed=1
|
427 |
+
|
428 |
Â
= 0.10.0 =
|
429 |
Â
* Introduced "Operation modes" in order to keep setting screens simple but still allow tweaking
|
430 |
Â
* WebP Express can now be used in conjunction with Cache Enabler and ShortPixel
|
512 |
Â
|
513 |
Â
== Upgrade Notice ==
|
514 |
Â
|
515 |
+
= 0.11.0 =
|
516 |
+
WebP Express can now alter HTML to either point to webp images directly or by using the picture tag syntax. Also, non-existing webp-files can be converted upon request (means you can reference the converted webp files before they are actually converted!)
|
517 |
+
|
518 |
Â
= 0.10.0 =
|
519 |
Â
WebP Express can now be used in conjunction with Cache Enabler and ShortPixel. Also introduced "Operation modes" in order to keep setting screens simple but still allow tweaking.
|
520 |
Â
|
changelog.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1 |
Â
= 0.10.0 =
|
2 |
Â
* Introduced "Operation modes" in order to keep setting screens simple but still allow tweaking
|
3 |
Â
* WebP Express can now be used in conjunction with Cache Enabler and ShortPixel
|
1 |
+
= 0.11.0 =
|
2 |
+
* Alter HTML to point to webp files (choose between picture tags or simply altering all image urls)
|
3 |
+
* Convert non-existing webp-files upon request (means you can reference the converted webp files before they are actually converted!)
|
4 |
+
|
5 |
+
For more info, see the closed issues on the 0.11.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/milestone/14?closed=1
|
6 |
+
|
7 |
Â
= 0.10.0 =
|
8 |
Â
* Introduced "Operation modes" in order to keep setting screens simple but still allow tweaking
|
9 |
Â
* WebP Express can now be used in conjunction with Cache Enabler and ShortPixel
|
composer.json
CHANGED
@@ -1,5 +1,9 @@
|
|
1 |
Â
{
|
2 |
-
"
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
3 |
Â
"repositories": [
|
4 |
Â
{
|
5 |
Â
"type": "vcs",
|
@@ -12,6 +16,24 @@
|
|
12 |
Â
],
|
13 |
Â
"require": {
|
14 |
Â
"rosell-dk/webp-convert": "dev-master",
|
15 |
-
"rosell-dk/webp-convert-cloud-service": "dev-master"
|
16 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
17 |
Â
}
|
1 |
Â
{
|
2 |
+
"name": "rosell-dk/webp-express",
|
3 |
+
"description": "WebP for the masses",
|
4 |
+
"type": "project",
|
5 |
+
"license": "MIT",
|
6 |
+
"minimum-stability": "stable",
|
7 |
Â
"repositories": [
|
8 |
Â
{
|
9 |
Â
"type": "vcs",
|
16 |
Â
],
|
17 |
Â
"require": {
|
18 |
Â
"rosell-dk/webp-convert": "dev-master",
|
19 |
+
"rosell-dk/webp-convert-cloud-service": "dev-master",
|
20 |
+
"rosell-dk/dom-util-for-webp": "^0.2.0"
|
21 |
+
},
|
22 |
+
"require-dev": {
|
23 |
+
"phpunit/phpunit": "^8.0@dev"
|
24 |
+
},
|
25 |
+
"scripts": {
|
26 |
+
"ci": [
|
27 |
+
"@test",
|
28 |
+
"@composer validate --no-check-all --strict"
|
29 |
+
],
|
30 |
+
"test": "phpunit tests"
|
31 |
+
},
|
32 |
+
"authors": [
|
33 |
+
{
|
34 |
+
"name": "Bjørn Rosell",
|
35 |
+
"homepage": "https://www.bitwise-it.dk/contact",
|
36 |
+
"role": "Project Author"
|
37 |
+
}
|
38 |
+
]
|
39 |
Â
}
|
docs/regex.md
ADDED
@@ -0,0 +1,154 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
Pattern used for image urls:
|
2 |
+
in attributes: https://regexr.com/46jat
|
3 |
+
in css: https://regexr.com/46jcg
|
4 |
+
|
5 |
+
In case reqexr.com should be down, here are the content:
|
6 |
+
|
7 |
+
|
8 |
+
# in attributes
|
9 |
+
|
10 |
+
*pattern:*
|
11 |
+
(?<=(?:<(img|source|input|iframe)[^>]*\s+(src|srcset|data-[^=]*)\s*=\s*[\"\']?))(?:[^\"\'>]+)(\.png|\.jp[e]?g)(\s\d+w)?(?=\/?[\"\'\s\>])
|
12 |
+
|
13 |
+
*text:*
|
14 |
+
Notice: The pattern is meant for PHP and contains syntax which only works in some browsers. It works in Chrome. Not in Firefox.
|
15 |
+
|
16 |
+
The following should produce matches:
|
17 |
+
<img src="header.jpg">
|
18 |
+
<img src="/header.jpg">
|
19 |
+
<img src="http://example.com/header.jpeg" alt="">
|
20 |
+
<img src="http://example.com/header.jpg">
|
21 |
+
<img src="http://example.com/header.jpg"/>
|
22 |
+
<img src = "http://example.com/header.jpg">
|
23 |
+
<img src=http://example.com/header.jpg alt="">
|
24 |
+
<img src=http://example.com/header.jpg>
|
25 |
+
<img src=http://example.com/header.jpg alt="hello">
|
26 |
+
<img src=http://example.com/header.jpg />
|
27 |
+
<img src=http://example.com/header_.jpg/>
|
28 |
+
<picture><source src="http://example.com/header.jpg"><img src="http://example.com/header.jpg"></picture>
|
29 |
+
<input type="image" src="http://example.com/flamingo.jpg">
|
30 |
+
<iframe src="http://example.com/image.jpg"></iframe>
|
31 |
+
|
32 |
+
|
33 |
+
|
34 |
+
In srcset, the whole attribute must be matched
|
35 |
+
<img src="http://example.com/header.jpg" srcset="http://example.com/header.jpg 1000w">
|
36 |
+
<img src="http://example.com/header.jpg" srcset="http://example.com/header.jpg 1000w,http://example.com/header.jpg 1000w, http://example.com/header.jpg 2000w">
|
37 |
+
<img src="http://example.com/header.jpg" srcset="http://example.com/header-150x150.jpg 500w,http://example.com/header.jpg-300x300.jpg" sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px" alt="" width="100" height="100">
|
38 |
+
|
39 |
+
Common lazy load attributes are matched:
|
40 |
+
<img data-cvpsrc="http://example.com/header.jpg">
|
41 |
+
<img data-cvpset="http://example.com/header.jpg">
|
42 |
+
<img data-thumb="http://example.com/header.jpg">
|
43 |
+
<img data-bg-url="http://example.com/header.jpg">
|
44 |
+
<img data-large_image="http://example.com/header.jpg">
|
45 |
+
<img data-lazyload="http://example.com/header.jpg">
|
46 |
+
<img data-source-url="http://example.com/header.jpg">
|
47 |
+
<img data-srcsmall="http://example.com/header.jpg">
|
48 |
+
<img data-srclarge="http://example.com/header.jpg">
|
49 |
+
<img data-srcfull="http://example.com/header.jpg">
|
50 |
+
<img data-slide-img="http://example.com/header.jpg">
|
51 |
+
<img data-lazy-original="http://example.com/header.jpg">
|
52 |
+
|
53 |
+
|
54 |
+
The following should NOT produce matches:
|
55 |
+
-----------------------------------------
|
56 |
+
|
57 |
+
Ignore URLs with query string:
|
58 |
+
<img src="http://example.com/header.jpg?width=200">
|
59 |
+
|
60 |
+
<img src="http://example.com/tegning.jpg.webp" alt="">
|
61 |
+
<img src="http://example.com/tegning.jpglidilo" alt="">
|
62 |
+
<img src="http://example.com/header.jpg/hi-res">
|
63 |
+
<img src=http://example.com/header.gif alt=nice-jpg>
|
64 |
+
<img src="http://example.com/tegning.webp" alt="">
|
65 |
+
src="http://example.com/header.jpeg"
|
66 |
+
<article data-src="http://example.com/header.jpg" />
|
67 |
+
<img><script src="http://example.com/script.js?preload=image.jpg">
|
68 |
+
|
69 |
+
|
70 |
+
I use another pattern for matching image urls in styles: https://regexr.com/46jcg
|
71 |
+
|
72 |
+
It matches stuff like this:
|
73 |
+
<div style="background-image: url('http://example.com/image.png'), url("/image2.jpeg", url(http://example.com/image3.jpg);"></div>
|
74 |
+
<div style="background: url ("http://example.com/image2.jpg")"></div>
|
75 |
+
<style>#myphoto {background: url("http://example.com/image2.jpg")}</style>
|
76 |
+
|
77 |
+
I have another pattern where we allow QS here: https://regexr.com/46ivi
|
78 |
+
|
79 |
+
PS: The rules are used for the WebP Express plugin for Wordpress
|
80 |
+
|
81 |
+
PPS: This regex is used in WPFastestCache (not just images)
|
82 |
+
// $content = preg_replace_callback("/(srcset|src|href|data-cvpsrc|data-cvpset|data-thumb|data-bg-url|data-large_image|data-lazyload|data-source-url|data-srcsmall|data-srclarge|data-srcfull|data-slide-img|data-lazy-original)\s{0,2}\=[\'\"]([^\'\"]+)[\'\"]/i", array($this, 'cdn_replace_urls'), $content);
|
83 |
+
|
84 |
+
PPPS:
|
85 |
+
As we are limiting to a few tags (img, source, input, etc), and only match image urls ending with (png|jpe?g), I deem it ok to match in ANY "data-" attribute.
|
86 |
+
But if you want to limit it to attributes that smells like they are used for images you can do this:
|
87 |
+
(src|srcset|data-[^=]*(lazy|small|slide|img|large|src|thumb|source|set|bg-url)[^=]*)
|
88 |
+
That will catch the following known and more: data-cvpsrc|data-cvpset|data-thumb|data-bg-url|data-large_image|data-lazyload|data-source-url|data-srcsmall|data-srclarge|data-srcfull|data-slide-img|data-lazy-original
|
89 |
+
|
90 |
+
|
91 |
+
# in style
|
92 |
+
|
93 |
+
*pattern:*
|
94 |
+
((?<=(?:((style\s*=)|(\<\s*style)).*background(-image)?\s*:\s*url\s*\([\"\']?)|(((style\s*=)|(\<\s*style)).*url.*,\s*url\([\"\']?))[^\"\']*\.(jpe?g|png))(?=[\"\'\s\>)])
|
95 |
+
|
96 |
+
*text:*
|
97 |
+
Notice: The pattern is meant for PHP and contains syntax which only works in some browsers. It works in Chrome. Not in Firefox.
|
98 |
+
|
99 |
+
The following should produce matches:
|
100 |
+
|
101 |
+
<style>#myphoto {background: url("http://example.com/image2.jpg")}</style>
|
102 |
+
<div style="background-image: url('http://example.com/image.png'), url("/image2.jpeg"), url(http://example.com/image3.jpg);"></div>
|
103 |
+
<div style="background: url ("http://example.com/image2.jpg")"></div>
|
104 |
+
<style>#myphoto {background: url("http://example.com/image2.jpg"), url("image2.jpeg"}</style>
|
105 |
+
|
106 |
+
Not these:
|
107 |
+
----------
|
108 |
+
|
109 |
+
GIFs are disallowed:
|
110 |
+
<div style="background-image: url("http://example.com/image.gif"), url("http://example.com/image2.gif", url("image3.gif");"></div>
|
111 |
+
|
112 |
+
Querystrings are disallowed:
|
113 |
+
<div style="background-image: url('http://example.com/image.jpg?no-qs!')"></div>
|
114 |
+
|
115 |
+
HTML attributes disallowed:
|
116 |
+
<img src="header.jpg">
|
117 |
+
|
118 |
+
Go with style: background: url("http://example.com/image2.jpg")
|
119 |
+
|
120 |
+
|
121 |
+
And none of this either:
|
122 |
+
|
123 |
+
<div style="background-image: url('http://example.com/image.jpgelegi')"></div>
|
124 |
+
<img src="header.jpg">
|
125 |
+
<img src="/header.jpg">
|
126 |
+
<img src="http://example.com/header.jpeg" alt="">
|
127 |
+
<img src="http://example.com/header.jpg">
|
128 |
+
<img src="http://example.com/header.jpg"/>
|
129 |
+
<img src = "http://example.com/header.jpg">
|
130 |
+
<img src=http://example.com/header.jpg alt="">
|
131 |
+
<img src=http://example.com/header.jpg>
|
132 |
+
<img src=http://example.com/header.jpg alt="hello">
|
133 |
+
<img src=http://example.com/header.jpg />
|
134 |
+
<img src=http://example.com/header_.jpg/>
|
135 |
+
<picture><source src="http://example.com/header.jpg"><img src="http://example.com/header.jpg"></picture>
|
136 |
+
<input type="image" src="http://example.com/flamingo.jpg">
|
137 |
+
<iframe src="http://example.com/image.jpg"></iframe>
|
138 |
+
|
139 |
+
<img src="http://example.com/header.jpg" srcset="http://example.com/header.jpg 1000w">
|
140 |
+
<img src="http://example.com/header.jpg" srcset="http://example.com/header.jpg 1000w,http://example.com/header.jpg 1000w, http://example.com/header.jpg 2000w">
|
141 |
+
<img src="http://example.com/header.jpg" srcset="http://example.com/header-150x150.jpg 500w,http://example.com/header.jpg-300x300.jpg" sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px" alt="" width="100" height="100">
|
142 |
+
|
143 |
+
<img src="http://example.com/tegning.jpg.webp" alt="">
|
144 |
+
<img src="http://example.com/tegning.jpglidilo" alt="">
|
145 |
+
<img src="http://example.com/header.jpg/hi-res">
|
146 |
+
<img src=http://example.com/header.gif alt=nice-jpg>
|
147 |
+
<img src="http://example.com/tegning.webp" alt="">
|
148 |
+
src="http://example.com/header.jpeg"
|
149 |
+
<article data-src="http://example.com/header.jpg" />
|
150 |
+
<img><script src="http://example.com/script.js?preload=image.jpg">
|
151 |
+
|
152 |
+
|
153 |
+
I use another pattern for matching image urls in HTML attributes:
|
154 |
+
https://regexr.com/46jat
|
js/picturefill.min.js
ADDED
@@ -0,0 +1,5 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
/*! picturefill - v3.0.2 - 2016-02-12
|
2 |
+
* https://scottjehl.github.io/picturefill/
|
3 |
+
* Copyright (c) 2016 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT
|
4 |
+
*/
|
5 |
+
!function(a){var b=navigator.userAgent;a.HTMLPictureElement&&/ecko/.test(b)&&b.match(/rv\:(\d+)/)&&RegExp.$1<45&&addEventListener("resize",function(){var b,c=document.createElement("source"),d=function(a){var b,d,e=a.parentNode;"PICTURE"===e.nodeName.toUpperCase()?(b=c.cloneNode(),e.insertBefore(b,e.firstElementChild),setTimeout(function(){e.removeChild(b)})):(!a._pfLastSize||a.offsetWidth>a._pfLastSize)&&(a._pfLastSize=a.offsetWidth,d=a.sizes,a.sizes+=",100vw",setTimeout(function(){a.sizes=d}))},e=function(){var a,b=document.querySelectorAll("picture > img, img[srcset][sizes]");for(a=0;a<b.length;a++)d(b[a])},f=function(){clearTimeout(b),b=setTimeout(e,99)},g=a.matchMedia&&matchMedia("(orientation: landscape)"),h=function(){f(),g&&g.addListener&&g.addListener(f)};return c.srcset="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",/^[c|i]|d$/.test(document.readyState||"")?h():document.addEventListener("DOMContentLoaded",h),f}())}(window),function(a,b,c){"use strict";function d(a){return" "===a||" "===a||"\n"===a||"\f"===a||"\r"===a}function e(b,c){var d=new a.Image;return d.onerror=function(){A[b]=!1,ba()},d.onload=function(){A[b]=1===d.width,ba()},d.src=c,"pending"}function f(){M=!1,P=a.devicePixelRatio,N={},O={},s.DPR=P||1,Q.width=Math.max(a.innerWidth||0,z.clientWidth),Q.height=Math.max(a.innerHeight||0,z.clientHeight),Q.vw=Q.width/100,Q.vh=Q.height/100,r=[Q.height,Q.width,P].join("-"),Q.em=s.getEmValue(),Q.rem=Q.em}function g(a,b,c,d){var e,f,g,h;return"saveData"===B.algorithm?a>2.7?h=c+1:(f=b-c,e=Math.pow(a-.6,1.5),g=f*e,d&&(g+=.1*e),h=a+g):h=c>1?Math.sqrt(a*b):a,h>c}function h(a){var b,c=s.getSet(a),d=!1;"pending"!==c&&(d=r,c&&(b=s.setRes(c),s.applySetCandidate(b,a))),a[s.ns].evaled=d}function i(a,b){return a.res-b.res}function j(a,b,c){var d;return!c&&b&&(c=a[s.ns].sets,c=c&&c[c.length-1]),d=k(b,c),d&&(b=s.makeUrl(b),a[s.ns].curSrc=b,a[s.ns].curCan=d,d.res||aa(d,d.set.sizes)),d}function k(a,b){var c,d,e;if(a&&b)for(e=s.parseSet(b),a=s.makeUrl(a),c=0;c<e.length;c++)if(a===s.makeUrl(e[c].url)){d=e[c];break}return d}function l(a,b){var c,d,e,f,g=a.getElementsByTagName("source");for(c=0,d=g.length;d>c;c++)e=g[c],e[s.ns]=!0,f=e.getAttribute("srcset"),f&&b.push({srcset:f,media:e.getAttribute("media"),type:e.getAttribute("type"),sizes:e.getAttribute("sizes")})}function m(a,b){function c(b){var c,d=b.exec(a.substring(m));return d?(c=d[0],m+=c.length,c):void 0}function e(){var a,c,d,e,f,i,j,k,l,m=!1,o={};for(e=0;e<h.length;e++)f=h[e],i=f[f.length-1],j=f.substring(0,f.length-1),k=parseInt(j,10),l=parseFloat(j),X.test(j)&&"w"===i?((a||c)&&(m=!0),0===k?m=!0:a=k):Y.test(j)&&"x"===i?((a||c||d)&&(m=!0),0>l?m=!0:c=l):X.test(j)&&"h"===i?((d||c)&&(m=!0),0===k?m=!0:d=k):m=!0;m||(o.url=g,a&&(o.w=a),c&&(o.d=c),d&&(o.h=d),d||c||a||(o.d=1),1===o.d&&(b.has1x=!0),o.set=b,n.push(o))}function f(){for(c(T),i="",j="in descriptor";;){if(k=a.charAt(m),"in descriptor"===j)if(d(k))i&&(h.push(i),i="",j="after descriptor");else{if(","===k)return m+=1,i&&h.push(i),void e();if("("===k)i+=k,j="in parens";else{if(""===k)return i&&h.push(i),void e();i+=k}}else if("in parens"===j)if(")"===k)i+=k,j="in descriptor";else{if(""===k)return h.push(i),void e();i+=k}else if("after descriptor"===j)if(d(k));else{if(""===k)return void e();j="in descriptor",m-=1}m+=1}}for(var g,h,i,j,k,l=a.length,m=0,n=[];;){if(c(U),m>=l)return n;g=c(V),h=[],","===g.slice(-1)?(g=g.replace(W,""),e()):f()}}function n(a){function b(a){function b(){f&&(g.push(f),f="")}function c(){g[0]&&(h.push(g),g=[])}for(var e,f="",g=[],h=[],i=0,j=0,k=!1;;){if(e=a.charAt(j),""===e)return b(),c(),h;if(k){if("*"===e&&"/"===a[j+1]){k=!1,j+=2,b();continue}j+=1}else{if(d(e)){if(a.charAt(j-1)&&d(a.charAt(j-1))||!f){j+=1;continue}if(0===i){b(),j+=1;continue}e=" "}else if("("===e)i+=1;else if(")"===e)i-=1;else{if(","===e){b(),c(),j+=1;continue}if("/"===e&&"*"===a.charAt(j+1)){k=!0,j+=2;continue}}f+=e,j+=1}}}function c(a){return k.test(a)&&parseFloat(a)>=0?!0:l.test(a)?!0:"0"===a||"-0"===a||"+0"===a?!0:!1}var e,f,g,h,i,j,k=/^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i,l=/^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i;for(f=b(a),g=f.length,e=0;g>e;e++)if(h=f[e],i=h[h.length-1],c(i)){if(j=i,h.pop(),0===h.length)return j;if(h=h.join(" "),s.matchesMedia(h))return j}return"100vw"}b.createElement("picture");var o,p,q,r,s={},t=!1,u=function(){},v=b.createElement("img"),w=v.getAttribute,x=v.setAttribute,y=v.removeAttribute,z=b.documentElement,A={},B={algorithm:""},C="data-pfsrc",D=C+"set",E=navigator.userAgent,F=/rident/.test(E)||/ecko/.test(E)&&E.match(/rv\:(\d+)/)&&RegExp.$1>35,G="currentSrc",H=/\s+\+?\d+(e\d+)?w/,I=/(\([^)]+\))?\s*(.+)/,J=a.picturefillCFG,K="position:absolute;left:0;visibility:hidden;display:block;padding:0;border:none;font-size:1em;width:1em;overflow:hidden;clip:rect(0px, 0px, 0px, 0px)",L="font-size:100%!important;",M=!0,N={},O={},P=a.devicePixelRatio,Q={px:1,"in":96},R=b.createElement("a"),S=!1,T=/^[ \t\n\r\u000c]+/,U=/^[, \t\n\r\u000c]+/,V=/^[^ \t\n\r\u000c]+/,W=/[,]+$/,X=/^\d+$/,Y=/^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/,Z=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)},$=function(a){var b={};return function(c){return c in b||(b[c]=a(c)),b[c]}},_=function(){var a=/^([\d\.]+)(em|vw|px)$/,b=function(){for(var a=arguments,b=0,c=a[0];++b in a;)c=c.replace(a[b],a[++b]);return c},c=$(function(a){return"return "+b((a||"").toLowerCase(),/\band\b/g,"&&",/,/g,"||",/min-([a-z-\s]+):/g,"e.$1>=",/max-([a-z-\s]+):/g,"e.$1<=",/calc([^)]+)/g,"($1)",/(\d+[\.]*[\d]*)([a-z]+)/g,"($1 * e.$2)",/^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/gi,"")+";"});return function(b,d){var e;if(!(b in N))if(N[b]=!1,d&&(e=b.match(a)))N[b]=e[1]*Q[e[2]];else try{N[b]=new Function("e",c(b))(Q)}catch(f){}return N[b]}}(),aa=function(a,b){return a.w?(a.cWidth=s.calcListLength(b||"100vw"),a.res=a.w/a.cWidth):a.res=a.d,a},ba=function(a){if(t){var c,d,e,f=a||{};if(f.elements&&1===f.elements.nodeType&&("IMG"===f.elements.nodeName.toUpperCase()?f.elements=[f.elements]:(f.context=f.elements,f.elements=null)),c=f.elements||s.qsa(f.context||b,f.reevaluate||f.reselect?s.sel:s.selShort),e=c.length){for(s.setupRun(f),S=!0,d=0;e>d;d++)s.fillImg(c[d],f);s.teardownRun(f)}}};o=a.console&&console.warn?function(a){console.warn(a)}:u,G in v||(G="src"),A["image/jpeg"]=!0,A["image/gif"]=!0,A["image/png"]=!0,A["image/svg+xml"]=b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1"),s.ns=("pf"+(new Date).getTime()).substr(0,9),s.supSrcset="srcset"in v,s.supSizes="sizes"in v,s.supPicture=!!a.HTMLPictureElement,s.supSrcset&&s.supPicture&&!s.supSizes&&!function(a){v.srcset="data:,a",a.src="data:,a",s.supSrcset=v.complete===a.complete,s.supPicture=s.supSrcset&&s.supPicture}(b.createElement("img")),s.supSrcset&&!s.supSizes?!function(){var a="data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw==",c="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",d=b.createElement("img"),e=function(){var a=d.width;2===a&&(s.supSizes=!0),q=s.supSrcset&&!s.supSizes,t=!0,setTimeout(ba)};d.onload=e,d.onerror=e,d.setAttribute("sizes","9px"),d.srcset=c+" 1w,"+a+" 9w",d.src=c}():t=!0,s.selShort="picture>img,img[srcset]",s.sel=s.selShort,s.cfg=B,s.DPR=P||1,s.u=Q,s.types=A,s.setSize=u,s.makeUrl=$(function(a){return R.href=a,R.href}),s.qsa=function(a,b){return"querySelector"in a?a.querySelectorAll(b):[]},s.matchesMedia=function(){return a.matchMedia&&(matchMedia("(min-width: 0.1em)")||{}).matches?s.matchesMedia=function(a){return!a||matchMedia(a).matches}:s.matchesMedia=s.mMQ,s.matchesMedia.apply(this,arguments)},s.mMQ=function(a){return a?_(a):!0},s.calcLength=function(a){var b=_(a,!0)||!1;return 0>b&&(b=!1),b},s.supportsType=function(a){return a?A[a]:!0},s.parseSize=$(function(a){var b=(a||"").match(I);return{media:b&&b[1],length:b&&b[2]}}),s.parseSet=function(a){return a.cands||(a.cands=m(a.srcset,a)),a.cands},s.getEmValue=function(){var a;if(!p&&(a=b.body)){var c=b.createElement("div"),d=z.style.cssText,e=a.style.cssText;c.style.cssText=K,z.style.cssText=L,a.style.cssText=L,a.appendChild(c),p=c.offsetWidth,a.removeChild(c),p=parseFloat(p,10),z.style.cssText=d,a.style.cssText=e}return p||16},s.calcListLength=function(a){if(!(a in O)||B.uT){var b=s.calcLength(n(a));O[a]=b?b:Q.width}return O[a]},s.setRes=function(a){var b;if(a){b=s.parseSet(a);for(var c=0,d=b.length;d>c;c++)aa(b[c],a.sizes)}return b},s.setRes.res=aa,s.applySetCandidate=function(a,b){if(a.length){var c,d,e,f,h,k,l,m,n,o=b[s.ns],p=s.DPR;if(k=o.curSrc||b[G],l=o.curCan||j(b,k,a[0].set),l&&l.set===a[0].set&&(n=F&&!b.complete&&l.res-.1>p,n||(l.cached=!0,l.res>=p&&(h=l))),!h)for(a.sort(i),f=a.length,h=a[f-1],d=0;f>d;d++)if(c=a[d],c.res>=p){e=d-1,h=a[e]&&(n||k!==s.makeUrl(c.url))&&g(a[e].res,c.res,p,a[e].cached)?a[e]:c;break}h&&(m=s.makeUrl(h.url),o.curSrc=m,o.curCan=h,m!==k&&s.setSrc(b,h),s.setSize(b))}},s.setSrc=function(a,b){var c;a.src=b.url,"image/svg+xml"===b.set.type&&(c=a.style.width,a.style.width=a.offsetWidth+1+"px",a.offsetWidth+1&&(a.style.width=c))},s.getSet=function(a){var b,c,d,e=!1,f=a[s.ns].sets;for(b=0;b<f.length&&!e;b++)if(c=f[b],c.srcset&&s.matchesMedia(c.media)&&(d=s.supportsType(c.type))){"pending"===d&&(c=d),e=c;break}return e},s.parseSets=function(a,b,d){var e,f,g,h,i=b&&"PICTURE"===b.nodeName.toUpperCase(),j=a[s.ns];(j.src===c||d.src)&&(j.src=w.call(a,"src"),j.src?x.call(a,C,j.src):y.call(a,C)),(j.srcset===c||d.srcset||!s.supSrcset||a.srcset)&&(e=w.call(a,"srcset"),j.srcset=e,h=!0),j.sets=[],i&&(j.pic=!0,l(b,j.sets)),j.srcset?(f={srcset:j.srcset,sizes:w.call(a,"sizes")},j.sets.push(f),g=(q||j.src)&&H.test(j.srcset||""),g||!j.src||k(j.src,f)||f.has1x||(f.srcset+=", "+j.src,f.cands.push({url:j.src,d:1,set:f}))):j.src&&j.sets.push({srcset:j.src,sizes:null}),j.curCan=null,j.curSrc=c,j.supported=!(i||f&&!s.supSrcset||g&&!s.supSizes),h&&s.supSrcset&&!j.supported&&(e?(x.call(a,D,e),a.srcset=""):y.call(a,D)),j.supported&&!j.srcset&&(!j.src&&a.src||a.src!==s.makeUrl(j.src))&&(null===j.src?a.removeAttribute("src"):a.src=j.src),j.parsed=!0},s.fillImg=function(a,b){var c,d=b.reselect||b.reevaluate;a[s.ns]||(a[s.ns]={}),c=a[s.ns],(d||c.evaled!==r)&&((!c.parsed||b.reevaluate)&&s.parseSets(a,a.parentNode,b),c.supported?c.evaled=r:h(a))},s.setupRun=function(){(!S||M||P!==a.devicePixelRatio)&&f()},s.supPicture?(ba=u,s.fillImg=u):!function(){var c,d=a.attachEvent?/d$|^c/:/d$|^c|^i/,e=function(){var a=b.readyState||"";f=setTimeout(e,"loading"===a?200:999),b.body&&(s.fillImgs(),c=c||d.test(a),c&&clearTimeout(f))},f=setTimeout(e,b.body?9:99),g=function(a,b){var c,d,e=function(){var f=new Date-d;b>f?c=setTimeout(e,b-f):(c=null,a())};return function(){d=new Date,c||(c=setTimeout(e,b))}},h=z.clientHeight,i=function(){M=Math.max(a.innerWidth||0,z.clientWidth)!==Q.width||z.clientHeight!==h,h=z.clientHeight,M&&s.fillImgs()};Z(a,"resize",g(i,99)),Z(b,"readystatechange",e)}(),s.picturefill=ba,s.fillImgs=ba,s.teardownRun=u,ba._=s,a.picturefillCFG={pf:s,push:function(a){var b=a.shift();"function"==typeof s[b]?s[b].apply(s,a):(B[b]=a[0],S&&s.fillImgs({reselect:!0}))}};for(;J&&J.length;)a.picturefillCFG.push(J.shift());a.picturefill=ba,"object"==typeof module&&"object"==typeof module.exports?module.exports=ba:"function"==typeof define&&define.amd&&define("picturefill",function(){return ba}),s.supPicture||(A["image/webp"]=e("image/webp","data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA=="))}(window,document);
|
lib/admin.php
CHANGED
@@ -2,7 +2,9 @@
|
|
2 |
Â
use \WebPExpress\State;
|
3 |
Â
|
4 |
Â
// When an update requires a migration, the number should be increased
|
5 |
-
define('WEBPEXPRESS_MIGRATION_VERSION', '
|
Â
|
|
Â
|
|
6 |
Â
|
7 |
Â
if (WEBPEXPRESS_MIGRATION_VERSION != get_option('webp-express-migration-version', 0)) {
|
8 |
Â
// run migration logic
|
2 |
Â
use \WebPExpress\State;
|
3 |
Â
|
4 |
Â
// When an update requires a migration, the number should be increased
|
5 |
+
define('WEBPEXPRESS_MIGRATION_VERSION', '5');
|
6 |
+
|
7 |
+
//update_option('webp-express-migration-version', '4');
|
8 |
Â
|
9 |
Â
if (WEBPEXPRESS_MIGRATION_VERSION != get_option('webp-express-migration-version', 0)) {
|
10 |
Â
// run migration logic
|
lib/alter-html.php
ADDED
@@ -0,0 +1,75 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
function webPExpressAlterHtml($content) {
|
4 |
+
// Don't do anything with the RSS feed.
|
5 |
+
if (is_feed()) {
|
6 |
+
return $content;
|
7 |
+
}
|
8 |
+
|
9 |
+
if (get_option('webp-express-alter-html-replacement') == 'picture') {
|
10 |
+
if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
|
11 |
+
//for AMP pages the <picture> tag is not allowed
|
12 |
+
return $content;
|
13 |
+
}
|
14 |
+
/*if (is_admin() ) {
|
15 |
+
return $content;
|
16 |
+
}*/
|
17 |
+
require_once __DIR__ . '/classes/AlterHtmlPicture.php';
|
18 |
+
return \WebPExpress\AlterHtmlPicture::alter($content);
|
19 |
+
} else {
|
20 |
+
require_once __DIR__ . '/classes/AlterHtmlImageUrls.php';
|
21 |
+
return \WebPExpress\AlterHtmlImageUrls::alter($content);
|
22 |
+
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
function webpExpressOutputBuffer() {
|
27 |
+
if (!is_admin() || (function_exists("wp_doing_ajax") && wp_doing_ajax()) || (defined( 'DOING_AJAX' ) && DOING_AJAX)) {
|
28 |
+
ob_start('webPExpressAlterHtml');
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
function webpExpressAddPictureJs() {
|
33 |
+
// Don't do anything with the RSS feed.
|
34 |
+
// - and no need for PictureJs in the admin
|
35 |
+
if ( is_feed() || is_admin() ) { return; }
|
36 |
+
|
37 |
+
echo '<script>'
|
38 |
+
. 'document.createElement( "picture" );'
|
39 |
+
. 'if(!window.HTMLPictureElement && document.addEventListener) {'
|
40 |
+
. 'window.addEventListener("DOMContentLoaded", function() {'
|
41 |
+
. 'var s = document.createElement("script");'
|
42 |
+
. 's.src = "' . plugins_url('/js/picturefill.min.js', __FILE__) . '";'
|
43 |
+
. 'document.body.appendChild(s);'
|
44 |
+
. '});'
|
45 |
+
. '}'
|
46 |
+
. '</script>';
|
47 |
+
}
|
48 |
+
|
49 |
+
|
50 |
+
if (get_option('webp-express-alter-html-replacement') == 'picture') {
|
51 |
+
add_action( 'wp_head', 'webpExpressAddPictureJs');
|
52 |
+
}
|
53 |
+
|
54 |
+
if (get_option('webp-express-alter-html-hooks', 'ob') == 'ob') {
|
55 |
+
/* TODO:
|
56 |
+
Which hook should we use, and should we make it optional?
|
57 |
+
- Cache enabler uses 'template_redirect'
|
58 |
+
- ShortPixes uses 'init'
|
59 |
+
|
60 |
+
We go with template_redirect now, because it is the "innermost".
|
61 |
+
This lowers the risk of problems with plugins used rewriting URLs to point to CDN.
|
62 |
+
(We need to process the output *before* the other plugin has rewritten the URLs,
|
63 |
+
if the "Only for webps that exists" feature is enabled)
|
64 |
+
*/
|
65 |
+
add_action( 'init', 'webpExpressOutputBuffer', 1 );
|
66 |
+
//add_action( 'template_redirect', 'webpExpressOutputBuffer', 1 );
|
67 |
+
|
68 |
+
} else {
|
69 |
+
add_filter( 'the_content', 'webPExpressAlterHtml', 10000 ); // priority big, so it will be executed last
|
70 |
+
add_filter( 'the_excerpt', 'webPExpressAlterHtml', 10000 );
|
71 |
+
add_filter( 'post_thumbnail_html', 'webPExpressAlterHtml');
|
72 |
+
}
|
73 |
+
|
74 |
+
//echo wp_doing_ajax() ? 'ajax' : 'no ajax'; exit;
|
75 |
+
//echo is_feed() ? 'feed' : 'no feed'; exit;
|
lib/classes/AlterHtmlHelper.php
ADDED
@@ -0,0 +1,257 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
//use AlterHtmlInit;
|
6 |
+
|
7 |
+
include_once "Paths.php";
|
8 |
+
use \WebPExpress\Paths;
|
9 |
+
|
10 |
+
include_once "PathHelper.php";
|
11 |
+
use \WebPExpress\PathHelper;
|
12 |
+
|
13 |
+
class AlterHtmlHelper
|
14 |
+
{
|
15 |
+
|
16 |
+
public static $options;
|
17 |
+
/*
|
18 |
+
public static function hasWebP($src)
|
19 |
+
{
|
20 |
+
return true;
|
21 |
+
}
|
22 |
+
|
23 |
+
public static function inUploadDir($src)
|
24 |
+
{
|
25 |
+
$upload_dir = wp_upload_dir();
|
26 |
+
$src_url = parse_url($upload_dir['baseurl']);
|
27 |
+
$upload_path = $src_url['path'];
|
28 |
+
|
29 |
+
return (strpos($src, $upload_path) !== false );
|
30 |
+
|
31 |
+
}
|
32 |
+
|
33 |
+
public static function checkSrc($src)
|
34 |
+
{
|
35 |
+
self::$options = \WebPExpress\AlterHtmlInit::self::$options();
|
36 |
+
|
37 |
+
|
38 |
+
if (self::$options['destination-folder'] == 'mingled') {
|
39 |
+
|
40 |
+
}
|
41 |
+
}
|
42 |
+
*/
|
43 |
+
/**
|
44 |
+
* Gets relative path between a base url and another.
|
45 |
+
* Returns false if the url isn't a subpath
|
46 |
+
*
|
47 |
+
* @param $imageUrl (ie "http://example.com/wp-content/image.jpg")
|
48 |
+
* @param $baseUrl (ie "http://example.com/wp-content")
|
49 |
+
* @return path or false (ie "/image.jpg")
|
50 |
+
*/
|
51 |
+
public static function getRelUrlPath($imageUrl, $baseUrl)
|
52 |
+
{
|
53 |
+
|
54 |
+
$baseUrlComponents = parse_url($baseUrl);
|
55 |
+
/* ie:
|
56 |
+
(
|
57 |
+
[scheme] => http
|
58 |
+
[host] => we0
|
59 |
+
[path] => /wordpress/uploads-moved
|
60 |
+
)*/
|
61 |
+
|
62 |
+
$imageUrlComponents = parse_url($imageUrl);
|
63 |
+
/* ie:
|
64 |
+
(
|
65 |
+
[scheme] => http
|
66 |
+
[host] => we0
|
67 |
+
[path] => /wordpress/uploads-moved/logo.jpg
|
68 |
+
)*/
|
69 |
+
if ($baseUrlComponents['host'] != $imageUrlComponents['host']) {
|
70 |
+
return false;
|
71 |
+
}
|
72 |
+
|
73 |
+
// Check if path begins with base path
|
74 |
+
if (strpos($imageUrlComponents['path'], $baseUrlComponents['path']) !== 0) {
|
75 |
+
return false;
|
76 |
+
}
|
77 |
+
|
78 |
+
// Remove base path from path (we know it begins with basepath, from previous check)
|
79 |
+
return substr($imageUrlComponents['path'], strlen($baseUrlComponents['path']));
|
80 |
+
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Looks if $imageUrl is rooted in $baseUrl and if the file is there
|
85 |
+
*
|
86 |
+
*
|
87 |
+
* @param $imageUrl (ie http://example.com/wp-content/image.jpg)
|
88 |
+
* @param $baseUrl (ie http://example.com/wp-content)
|
89 |
+
* @param $baseDir (ie /var/www/example.com/wp-content)
|
90 |
+
*/
|
91 |
+
public static function isImageUrlHere($imageUrl, $baseUrl, $baseDir)
|
92 |
+
{
|
93 |
+
|
94 |
+
$srcPathRel = self::getRelUrlPath($imageUrl, $baseUrl);
|
95 |
+
|
96 |
+
if ($srcPathRel === false) {
|
97 |
+
return false;
|
98 |
+
}
|
99 |
+
|
100 |
+
// Calculate file path to src
|
101 |
+
$srcPathAbs = $baseDir . $srcPathRel;
|
102 |
+
//return 'dyt:' . $srcPathAbs;
|
103 |
+
|
104 |
+
// Check that src file exists
|
105 |
+
if (!@file_exists($srcPathAbs)) {
|
106 |
+
return false;
|
107 |
+
}
|
108 |
+
|
109 |
+
return true;
|
110 |
+
|
111 |
+
}
|
112 |
+
|
113 |
+
|
114 |
+
public static function isSourceInUpload($src)
|
115 |
+
{
|
116 |
+
/* $src is ie http://we0/wp-content-moved/themes/twentyseventeen/assets/images/header.jpg */
|
117 |
+
|
118 |
+
$uploadDir = wp_upload_dir();
|
119 |
+
/* ie:
|
120 |
+
|
121 |
+
[path] => /var/www/webp-express-tests/we0/wordpress/uploads-moved
|
122 |
+
[url] => http://we0/wordpress/uploads-moved
|
123 |
+
[subdir] =>
|
124 |
+
[basedir] => /var/www/webp-express-tests/we0/wordpress/uploads-moved
|
125 |
+
[baseurl] => http://we0/wordpress/uploads-moved
|
126 |
+
[error] =>
|
127 |
+
*/
|
128 |
+
|
129 |
+
return self::isImageUrlHere($src, $uploadDir['baseurl'], $uploadDir['basedir']);
|
130 |
+
}
|
131 |
+
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Get url for webp, given a certain baseUrl / baseDir.
|
135 |
+
* Base can for example be uploads or wp-content.
|
136 |
+
*
|
137 |
+
* returns false
|
138 |
+
* - if no source file found in that base
|
139 |
+
* - or webp file isn't there and the `only-for-webps-that-exists` option is set
|
140 |
+
*
|
141 |
+
* @param $imageUrl (ie http://example.com/wp-content/image.jpg)
|
142 |
+
* @param $baseUrl (ie http://example.com/wp-content)
|
143 |
+
* @param $baseDir (ie /var/www/example.com/wp-content)
|
144 |
+
*/
|
145 |
+
private static function getWebPUrlInBase($sourceUrl, $baseUrl, $baseDir)
|
146 |
+
{
|
147 |
+
|
148 |
+
$srcPathRel = self::getRelUrlPath($sourceUrl, $baseUrl);
|
149 |
+
|
150 |
+
if ($srcPathRel === false) {
|
151 |
+
return false;
|
152 |
+
}
|
153 |
+
|
154 |
+
// Calculate file path to src
|
155 |
+
$srcPathAbs = $baseDir . $srcPathRel;
|
156 |
+
|
157 |
+
// Check that src file exists
|
158 |
+
if (!@file_exists($srcPathAbs)) {
|
159 |
+
return false;
|
160 |
+
}
|
161 |
+
|
162 |
+
|
163 |
+
// Calculate $destPathAbs and $destUrl
|
164 |
+
// -------------------------------------
|
165 |
+
$inUpload = self::isSourceInUpload($sourceUrl);
|
166 |
+
|
167 |
+
if ((self::$options['destination-folder'] == 'mingled') && $inUpload) {
|
168 |
+
// mingled
|
169 |
+
if (self::$options['destination-extension'] == 'append') {
|
170 |
+
$destPathAbs = $srcPathAbs . '.webp';
|
171 |
+
$destUrl = $sourceUrl . '.webp';
|
172 |
+
} else {
|
173 |
+
$destPathAbs = preg_replace('/\\.(png|jpe?g)$/', '', $srcPathAbs) . '.webp';
|
174 |
+
$destUrl = preg_replace('/\\.(png|jpe?g)$/', '', $sourceUrl) . '.webp';
|
175 |
+
}
|
176 |
+
} else {
|
177 |
+
// separate (images that are not in upload are always put in separate)
|
178 |
+
|
179 |
+
$relPathFromDocRoot = '/webp-express/webp-images/doc-root/';
|
180 |
+
$relPathFromDocRoot .= PathHelper::getRelDir(realpath($_SERVER['DOCUMENT_ROOT']), $baseDir) . $srcPathRel;
|
181 |
+
|
182 |
+
list ($contentDirAbs, $contentUrl) = self::$options['bases']['content'];
|
183 |
+
|
184 |
+
$destPathAbs = $contentDirAbs . $relPathFromDocRoot . '.webp';
|
185 |
+
$destUrl = $contentUrl . $relPathFromDocRoot . '.webp';
|
186 |
+
}
|
187 |
+
|
188 |
+
$webpMustExist = self::$options['only-for-webps-that-exists'];
|
189 |
+
if ($webpMustExist && (!@file_exists($destPathAbs))) {
|
190 |
+
return false;
|
191 |
+
}
|
192 |
+
return $destUrl;
|
193 |
+
|
194 |
+
}
|
195 |
+
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Get url for webp
|
199 |
+
* returns second argument if no webp
|
200 |
+
*
|
201 |
+
* @param $imageUrl (ie http://example.com/wp-content/image.jpg)
|
202 |
+
* @param $baseUrl (ie http://example.com/wp-content)
|
203 |
+
* @param $baseDir (ie /var/www/example.com/wp-content)
|
204 |
+
*/
|
205 |
+
public static function getWebPUrl($sourceUrl, $returnValueOnFail)
|
206 |
+
{
|
207 |
+
if (!isset(self::$options)) {
|
208 |
+
self::$options = json_decode(get_option('webp-express-alter-html-options', null), true);
|
209 |
+
}
|
210 |
+
|
211 |
+
|
212 |
+
// Currently we do not handle relative urls - so we skip
|
213 |
+
if (!preg_match('#^https?://#', $sourceUrl)) {
|
214 |
+
return $returnValueOnFail;
|
215 |
+
}
|
216 |
+
|
217 |
+
switch (self::$options['image-types']) {
|
218 |
+
case 0:
|
219 |
+
return $returnValueOnFail;
|
220 |
+
case 1:
|
221 |
+
if (!preg_match('#(jpe?g)$#', $sourceUrl)) {
|
222 |
+
return $returnValueOnFail;
|
223 |
+
}
|
224 |
+
break;
|
225 |
+
case 2:
|
226 |
+
if (!preg_match('#(png)$#', $sourceUrl)) {
|
227 |
+
return $returnValueOnFail;
|
228 |
+
}
|
229 |
+
break;
|
230 |
+
case 3:
|
231 |
+
if (!preg_match('#(jpe?g|png)$#', $sourceUrl)) {
|
232 |
+
return $returnValueOnFail;
|
233 |
+
}
|
234 |
+
break;
|
235 |
+
}
|
236 |
+
|
237 |
+
if ((self::$options['only-for-webp-enabled-browsers']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') === false)) {
|
238 |
+
return $returnValueOnFail;
|
239 |
+
}
|
240 |
+
|
241 |
+
foreach (self::$options['bases'] as $id => list($baseDir, $baseUrl)) {
|
242 |
+
|
243 |
+
$result = self::getWebPUrlInBase($sourceUrl, $baseUrl, $baseDir);
|
244 |
+
if ($result !== false) {
|
245 |
+
return $result;
|
246 |
+
}
|
247 |
+
}
|
248 |
+
return $returnValueOnFail;
|
249 |
+
}
|
250 |
+
|
251 |
+
/*
|
252 |
+
public static function getWebPUrlOrSame($sourceUrl, $returnValueOnFail)
|
253 |
+
{
|
254 |
+
return self::getWebPUrl($sourceUrl, $sourceUrl);
|
255 |
+
}*/
|
256 |
+
|
257 |
+
}
|
lib/classes/AlterHtmlImageUrls.php
ADDED
@@ -0,0 +1,83 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "Paths.php";
|
6 |
+
use \WebPExpress\Paths;
|
7 |
+
|
8 |
+
use \WebPExpress\AlterHtmlInit;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class AlterHtmlImageUrls - convert image urls to webp
|
12 |
+
* Based this code on code from the Cache Enabler plugin
|
13 |
+
*/
|
14 |
+
|
15 |
+
use \WebPExpress\AlterHtmlHelper;
|
16 |
+
//use \WebPExpress\ImageUrlsReplacer;
|
17 |
+
use DOMUtilForWebP\ImageUrlReplacer;
|
18 |
+
|
19 |
+
class AlterHtmlImageUrls extends ImageUrlReplacer
|
20 |
+
{
|
21 |
+
public function replaceUrl($url) {
|
22 |
+
return AlterHtmlHelper::getWebPUrl($url, null);
|
23 |
+
}
|
24 |
+
|
25 |
+
public function attributeFilter($attrName) {
|
26 |
+
// Allow "src", "srcset" and data-attributes that smells like they are used for images
|
27 |
+
// The following rule matches all attributes used for lazy loading images that we know of
|
28 |
+
return preg_match('#^(src|srcset|(data-[^=]*(lazy|small|slide|img|large|src|thumb|source|set|bg-url)[^=]*))$#i', $attrName);
|
29 |
+
|
30 |
+
// If you want to limit it further, only allowing attributes known to be used for lazy load,
|
31 |
+
// use the following regex instead:
|
32 |
+
//return preg_match('#^(src|srcset|data-(src|srcset|cvpsrc|cvpset|thumb|bg-url|large_image|lazyload|source-url|srcsmall|srclarge|srcfull|slide-img|lazy-original))$#i', $attrName);
|
33 |
+
}
|
34 |
+
|
35 |
+
/*
|
36 |
+
public static function alter($content) {
|
37 |
+
require_once "AlterHtmlHelper.php";
|
38 |
+
|
39 |
+
|
40 |
+
//Find image urls in HTML attributes.
|
41 |
+
//- Ignore URLs with query string
|
42 |
+
//- Matches src, src-set and data-attributes in a limited set of tags
|
43 |
+
//- Only jpeg, jpg or png (NO GIFs)
|
44 |
+
//- Both relative and absolute URLs are matched
|
45 |
+
//- tag name is returned in $1, url in $&
|
46 |
+
|
47 |
+
//Pattern for attributes can be tested here: https://regexr.com/46jat
|
48 |
+
//PS: Based on regex found in Cache Enabler 1.3.2: https://regexr.com/46isf
|
49 |
+
|
50 |
+
//TODO: Dont match PNG, unless choosen
|
51 |
+
$regex_rule = '#(?<=(?:<(img|source|input|iframe)[^>]*\s+(src|srcset|data-[^=]*)\s*=\s*[\"\']?))(?:[^\"\'>]+)(\.png|\.jp[e]?g)(\s\d+w)?(?=\/?[\"\'\s\>])#';
|
52 |
+
return preg_replace_callback($regex_rule, 'self::replaceCallback', $content);
|
53 |
+
|
54 |
+
// TODO: css too. I already got the regex ready: https://regexr.com/46jcg
|
55 |
+
|
56 |
+
|
57 |
+
}
|
58 |
+
|
59 |
+
/*
|
60 |
+
private static function replaceCallback($match) {
|
61 |
+
list($attrValue, $attrName) = $match;
|
62 |
+
|
63 |
+
// A data attribute contain a srcset or single url.
|
64 |
+
// We should probably examine the attrValue further. For now, we however just use the attrName as a clue.
|
65 |
+
// If it contains "set", we handle it as a srcset. This takes care of both "data-cvpset" and "srcset"
|
66 |
+
if (strpos($attrName, 'set') > 0) {
|
67 |
+
$srcsetArr = explode(',', $attrValue);
|
68 |
+
foreach ($srcsetArr as $i => $srcSetEntry) {
|
69 |
+
// $srcSetEntry is ie "http://example.com/image.jpg 520w"
|
70 |
+
list($src, $width) = preg_split('/\s+/', trim($srcSetEntry)); // $width might not be set, but thats ok.
|
71 |
+
|
72 |
+
$webpUrl = \WebPExpress\AlterHtmlHelper::getWebPUrl($src, false);
|
73 |
+
if ($webpUrl !== false) {
|
74 |
+
$srcsetArr[$i] = $webpUrl . (isset($width) ? ' ' . $width : '');
|
75 |
+
}
|
76 |
+
}
|
77 |
+
return implode(', ', $srcsetArr);
|
78 |
+
} else {
|
79 |
+
return \WebPExpress\AlterHtmlHelper::getWebPUrl($attrValue, $attrValue);
|
80 |
+
}
|
81 |
+
}*/
|
82 |
+
|
83 |
+
}
|
lib/classes/AlterHtmlInit.php
ADDED
@@ -0,0 +1,126 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
include_once "AlterHtmlHelper.php";
|
6 |
+
use AlterHtmlHelper;
|
7 |
+
|
8 |
+
class AlterHtmlInit
|
9 |
+
{
|
10 |
+
public static $options = null;
|
11 |
+
|
12 |
+
public static function startOutputBuffer()
|
13 |
+
{
|
14 |
+
if (!is_admin() || (function_exists("wp_doing_ajax") && wp_doing_ajax()) || (defined( 'DOING_AJAX' ) && DOING_AJAX)) {
|
15 |
+
ob_start('self::alterHtml');
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
public static function alterHtml($content)
|
20 |
+
{
|
21 |
+
// Don't do anything with the RSS feed.
|
22 |
+
if (is_feed()) {
|
23 |
+
return $content;
|
24 |
+
}
|
25 |
+
|
26 |
+
if (get_option('webp-express-alter-html-replacement') == 'picture') {
|
27 |
+
if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
|
28 |
+
//for AMP pages the <picture> tag is not allowed
|
29 |
+
return $content;
|
30 |
+
}
|
31 |
+
/*if (is_admin() ) {
|
32 |
+
return $content;
|
33 |
+
}*/
|
34 |
+
}
|
35 |
+
|
36 |
+
if (!isset(self::$options)) {
|
37 |
+
self::$options = json_decode(get_option('webp-express-alter-html-options', null), true);
|
38 |
+
//AlterHtmlHelper::$options = self::$options;
|
39 |
+
}
|
40 |
+
|
41 |
+
if (self::$options == null) {
|
42 |
+
return $content;
|
43 |
+
}
|
44 |
+
|
45 |
+
if (get_option('webp-express-alter-html-replacement') == 'picture') {
|
46 |
+
require_once __DIR__ . "../../../vendor/autoload.php";
|
47 |
+
require_once __DIR__ . '/AlterHtmlHelper.php';
|
48 |
+
require_once __DIR__ . '/AlterHtmlPicture.php';
|
49 |
+
return \WebPExpress\AlterHtmlPicture::replace($content);
|
50 |
+
} else {
|
51 |
+
require_once __DIR__ . "../../../vendor/autoload.php";
|
52 |
+
require_once __DIR__ . '/AlterHtmlHelper.php';
|
53 |
+
require_once __DIR__ . '/AlterHtmlImageUrls.php';
|
54 |
+
return \WebPExpress\AlterHtmlImageUrls::replace($content);
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
public static function addPictureJs()
|
59 |
+
{
|
60 |
+
// Don't do anything with the RSS feed.
|
61 |
+
// - and no need for PictureJs in the admin
|
62 |
+
if ( is_feed() || is_admin() ) { return; }
|
63 |
+
|
64 |
+
echo '<script>'
|
65 |
+
. 'document.createElement( "picture" );'
|
66 |
+
. 'if(!window.HTMLPictureElement && document.addEventListener) {'
|
67 |
+
. 'window.addEventListener("DOMContentLoaded", function() {'
|
68 |
+
. 'var s = document.createElement("script");'
|
69 |
+
. 's.src = "' . plugins_url('/js/picturefill.min.js', __FILE__) . '";'
|
70 |
+
. 'document.body.appendChild(s);'
|
71 |
+
. '});'
|
72 |
+
. '}'
|
73 |
+
. '</script>';
|
74 |
+
}
|
75 |
+
|
76 |
+
public static function setHooks() {
|
77 |
+
|
78 |
+
if (get_option('webp-express-alter-html-replacement') == 'picture') {
|
79 |
+
// add_action( 'wp_head', '\\WebPExpress\\AlterHtmlInit::addPictureJs');
|
80 |
+
}
|
81 |
+
|
82 |
+
if (get_option('webp-express-alter-html-hooks', 'ob') == 'ob') {
|
83 |
+
/* TODO:
|
84 |
+
Which hook should we use, and should we make it optional?
|
85 |
+
- Cache enabler uses 'template_redirect'
|
86 |
+
- ShortPixes uses 'init'
|
87 |
+
|
88 |
+
We go with template_redirect now, because it is the "innermost".
|
89 |
+
This lowers the risk of problems with plugins used rewriting URLs to point to CDN.
|
90 |
+
(We need to process the output *before* the other plugin has rewritten the URLs,
|
91 |
+
if the "Only for webps that exists" feature is enabled)
|
92 |
+
*/
|
93 |
+
add_action( 'init', '\\WebPExpress\\AlterHtmlInit::startOutputBuffer', 1 );
|
94 |
+
add_action( 'template_redirect', '\\WebPExpress\\AlterHtmlInit::startOutputBuffer', 10000 );
|
95 |
+
|
96 |
+
} else {
|
97 |
+
add_filter( 'the_content', '\\WebPExpress\\AlterHtmlInit::alterHtml', 99999 ); // priority big, so it will be executed last
|
98 |
+
add_filter( 'the_excerpt', '\\WebPExpress\\AlterHtmlInit::alterHtml', 99999 );
|
99 |
+
add_filter( 'post_thumbnail_html', '\\WebPExpress\\AlterHtmlInit::alterHtml', 99999);
|
100 |
+
|
101 |
+
|
102 |
+
/*
|
103 |
+
TODO:
|
104 |
+
check out these hooks (used by Jecpack, in class.photon.php)
|
105 |
+
|
106 |
+
// Images in post content and galleries
|
107 |
+
add_filter( 'the_content', array( __CLASS__, 'filter_the_content' ), 999999 );
|
108 |
+
add_filter( 'get_post_galleries', array( __CLASS__, 'filter_the_galleries' ), 999999 );
|
109 |
+
add_filter( 'widget_media_image_instance', array( __CLASS__, 'filter_the_image_widget' ), 999999 );
|
110 |
+
|
111 |
+
// Core image retrieval
|
112 |
+
add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 10, 3 );
|
113 |
+
add_filter( 'rest_request_before_callbacks', array( $this, 'should_rest_photon_image_downsize' ), 10, 3 );
|
114 |
+
add_filter( 'rest_request_after_callbacks', array( $this, 'cleanup_rest_photon_image_downsize' ) );
|
115 |
+
|
116 |
+
// Responsive image srcset substitution
|
117 |
+
add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_srcset_array' ), 10, 5 );
|
118 |
+
add_filter( 'wp_calculate_image_sizes', array( $this, 'filter_sizes' ), 1, 2 ); // Early so themes can still easily filter.
|
119 |
+
|
120 |
+
// Helpers for maniuplated images
|
121 |
+
add_action( 'wp_enqueue_scripts', array( $this, 'action_wp_enqueue_scripts' ), 9 );
|
122 |
+
*/
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
}
|
lib/classes/AlterHtmlPicture.php
ADDED
@@ -0,0 +1,18 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class AlterHtmlPicture - convert an <img> tag to a <picture> tag and add the webp versions of the images
|
7 |
+
* Based this code on code from the ShortPixel plugin, which used code from Responsify WP plugin
|
8 |
+
*/
|
9 |
+
|
10 |
+
use \WebPExpress\AlterHtmlHelper;
|
11 |
+
use DOMUtilForWebP\PictureTags;
|
12 |
+
|
13 |
+
class AlterHtmlPicture extends PictureTags
|
14 |
+
{
|
15 |
+
public function replaceUrl($url) {
|
16 |
+
return AlterHtmlHelper::getWebPUrl($url, null);
|
17 |
+
}
|
18 |
+
}
|
lib/classes/CacheMover.php
CHANGED
@@ -8,7 +8,6 @@ use \WebPExpress\FileHelper;
|
|
8 |
Â
include_once "Paths.php";
|
9 |
Â
use \WebPExpress\Paths;
|
10 |
Â
|
11 |
-
|
12 |
Â
class CacheMover
|
13 |
Â
{
|
14 |
Â
|
@@ -22,6 +21,33 @@ class CacheMover
|
|
22 |
Â
}
|
23 |
Â
}
|
24 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
25 |
Â
/**
|
26 |
Â
* Move cache because of change in options.
|
27 |
Â
* Only move the upload folder
|
@@ -52,7 +78,11 @@ class CacheMover
|
|
52 |
Â
echo 'ext:' . $fromExt . ' => ' . $toExt . '<br>';
|
53 |
Â
echo '</pre>';*/
|
54 |
Â
|
55 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
56 |
Â
//self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
|
57 |
Â
}
|
58 |
Â
|
@@ -81,10 +111,10 @@ class CacheMover
|
|
81 |
Â
$filename = $fileIterator->getFilename();
|
82 |
Â
|
83 |
Â
if (($filename != ".") && ($filename != "..")) {
|
84 |
-
//$filePerm = FileHelper::filePermWithFallback($
|
85 |
Â
|
86 |
Â
if (@is_dir($fromDir . "/" . $filename)) {
|
87 |
-
list($r1, $r2) = self::moveRecursively($fromDir . "/" . $filename, $toDir . "/" . $filename, $srcDir . "/" . $filename, $fromExt, $toExt
|
88 |
Â
$numFilesMoved += $r1;
|
89 |
Â
$numFilesFailedMoving += $r2;
|
90 |
Â
|
@@ -136,7 +166,8 @@ class CacheMover
|
|
136 |
Â
|
137 |
Â
if ($newFilename !== null) {
|
138 |
Â
//echo 'moving to: ' . $toDir . '/' .$newFilename . "<br>";
|
139 |
-
|
Â
|
|
140 |
Â
$numFilesMoved++;
|
141 |
Â
} else {
|
142 |
Â
$numFilesFailedMoving++;
|
8 |
Â
include_once "Paths.php";
|
9 |
Â
use \WebPExpress\Paths;
|
10 |
Â
|
Â
|
|
11 |
Â
class CacheMover
|
12 |
Â
{
|
13 |
Â
|
21 |
Â
}
|
22 |
Â
}
|
23 |
Â
|
24 |
+
/**
|
25 |
+
* Sets permission, uid and gid of all subfolders/files of a dir to same as the dir
|
26 |
+
* (but for files, do not set executable flag)
|
27 |
+
*/
|
28 |
+
public static function chmodFixSubDirs($dir, $alsoSetOnDirs)
|
29 |
+
{
|
30 |
+
$dirPerm = FileHelper::filePermWithFallback($dir, 0775);
|
31 |
+
$filePerm = $dirPerm & 0666; // set executable flags to 0
|
32 |
+
/*echo 'dir:' . $dir . "\n";
|
33 |
+
echo 'Dir perm:' . FileHelper::humanReadableFilePerm($dirPerm) . "\n";
|
34 |
+
echo 'File perm:' . FileHelper::humanReadableFilePerm($filePerm) . "\n";*/
|
35 |
+
//return;
|
36 |
+
|
37 |
+
$stat = @stat($dir);
|
38 |
+
$uid = null;
|
39 |
+
$gid = null;
|
40 |
+
if ($stat !== false) {
|
41 |
+
if (isset($stat['uid'])) {
|
42 |
+
$uid = $stat['uid'];
|
43 |
+
}
|
44 |
+
if (isset($stat['gid'])) {
|
45 |
+
$uid = $stat['gid'];
|
46 |
+
}
|
47 |
+
}
|
48 |
+
FileHelper::chmod_r($dir, $dirPerm, $filePerm, $uid, $gid, '#\.webp$#', ($alsoSetOnDirs ? null : '#^$#'));
|
49 |
+
}
|
50 |
+
|
51 |
Â
/**
|
52 |
Â
* Move cache because of change in options.
|
53 |
Â
* Only move the upload folder
|
78 |
Â
echo 'ext:' . $fromExt . ' => ' . $toExt . '<br>';
|
79 |
Â
echo '</pre>';*/
|
80 |
Â
|
81 |
+
|
82 |
+
$result = self::moveRecursively($fromDir, $toDir, $srcDir, $fromExt, $toExt);
|
83 |
+
self::chmodFixSubDirs($toDir, ($newConfig['destination-folder'] == 'separate'));
|
84 |
+
|
85 |
+
return $result;
|
86 |
Â
//self::moveRecursively($toDir, $fromDir, $srcDir, $fromExt, $toExt);
|
87 |
Â
}
|
88 |
Â
|
111 |
Â
$filename = $fileIterator->getFilename();
|
112 |
Â
|
113 |
Â
if (($filename != ".") && ($filename != "..")) {
|
114 |
+
//$filePerm = FileHelper::filePermWithFallback($filename, 0777);
|
115 |
Â
|
116 |
Â
if (@is_dir($fromDir . "/" . $filename)) {
|
117 |
+
list($r1, $r2) = self::moveRecursively($fromDir . "/" . $filename, $toDir . "/" . $filename, $srcDir . "/" . $filename, $fromExt, $toExt);
|
118 |
Â
$numFilesMoved += $r1;
|
119 |
Â
$numFilesFailedMoving += $r2;
|
120 |
Â
|
166 |
Â
|
167 |
Â
if ($newFilename !== null) {
|
168 |
Â
//echo 'moving to: ' . $toDir . '/' .$newFilename . "<br>";
|
169 |
+
$toFilename = $toDir . "/" . $newFilename;
|
170 |
+
if (@rename($fromDir . "/" . $filename, $toFilename)) {
|
171 |
Â
$numFilesMoved++;
|
172 |
Â
} else {
|
173 |
Â
$numFilesFailedMoving++;
|
lib/classes/Config.php
CHANGED
@@ -45,7 +45,7 @@ class Config
|
|
45 |
Â
|
46 |
Â
public static function saveJSONOptions($filename, $obj)
|
47 |
Â
{
|
48 |
-
$result = @file_put_contents($filename, json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK));
|
49 |
Â
/*if ($result === false) {
|
50 |
Â
echo 'COULD NOT' . $filename;
|
51 |
Â
}*/
|
@@ -67,7 +67,7 @@ class Config
|
|
67 |
Â
|
68 |
Â
return [
|
69 |
Â
|
70 |
-
'operation-mode' => '
|
71 |
Â
|
72 |
Â
// redirection rules
|
73 |
Â
'enable-redirection-to-converter' => true,
|
@@ -77,6 +77,7 @@ class Config
|
|
77 |
Â
'do-not-pass-source-in-query-string' => false,
|
78 |
Â
'redirect-to-existing-in-htaccess' => true,
|
79 |
Â
'forward-query-string' => false,
|
Â
|
|
80 |
Â
|
81 |
Â
// conversion options
|
82 |
Â
'converters' => [],
|
@@ -95,6 +96,15 @@ class Config
|
|
95 |
Â
'fail' => 'original',
|
96 |
Â
'success-response' => 'converted',
|
97 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
98 |
Â
// web service
|
99 |
Â
'web-service' => [
|
100 |
Â
'enabled' => false,
|
@@ -120,10 +130,10 @@ class Config
|
|
120 |
Â
public static function applyOperationMode($config)
|
121 |
Â
{
|
122 |
Â
if (!isset($config['operation-mode'])) {
|
123 |
-
$config['operation-mode'] = '
|
124 |
Â
}
|
125 |
Â
|
126 |
-
if ($config['operation-mode'] == '
|
127 |
Â
$config = array_merge($config, [
|
128 |
Â
'enable-redirection-to-converter' => true,
|
129 |
Â
'only-redirect-to-converter-for-webp-enabled-browsers' => true,
|
@@ -133,13 +143,12 @@ class Config
|
|
133 |
Â
'fail' => 'original',
|
134 |
Â
'success-response' => 'converted',
|
135 |
Â
]);
|
136 |
-
} elseif ($config['operation-mode'] == '
|
137 |
Â
$config = array_merge($config, [
|
138 |
Â
'only-redirect-to-converter-for-webp-enabled-browsers' => false,
|
139 |
Â
'only-redirect-to-converter-on-cache-miss' => true,
|
140 |
Â
'do-not-pass-source-in-query-string' => true,
|
141 |
Â
'redirect-to-existing-in-htaccess' => false,
|
142 |
-
'destination-folder' => 'mingled',
|
143 |
Â
'fail' => 'original',
|
144 |
Â
'success-response' => 'original',
|
145 |
Â
]);
|
@@ -150,19 +159,15 @@ class Config
|
|
150 |
Â
$config = array_merge($config, [
|
151 |
Â
'enable-redirection-to-converter' => false,
|
152 |
Â
'destination-folder' => 'mingled',
|
Â
|
|
153 |
Â
]);
|
154 |
Â
}
|
155 |
Â
|
156 |
Â
return $config;
|
157 |
Â
}
|
158 |
Â
|
159 |
-
|
160 |
-
* Loads Config (if available), fills in the rest with defaults
|
161 |
-
* also applies operation mode.
|
162 |
-
*/
|
163 |
-
public static function loadConfigAndFix($checkQualityDetection = true)
|
164 |
Â
{
|
165 |
-
$config = Config::loadConfig();
|
166 |
Â
if ($config === false) {
|
167 |
Â
$config = self::getDefaultConfig(!$checkQualityDetection);
|
168 |
Â
} else {
|
@@ -174,7 +179,10 @@ class Config
|
|
174 |
Â
}
|
175 |
Â
}
|
176 |
Â
}
|
177 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
178 |
Â
}
|
179 |
Â
|
180 |
Â
$config = self::applyOperationMode($config);
|
@@ -182,8 +190,8 @@ class Config
|
|
182 |
Â
if (!isset($config['web-service'])) {
|
183 |
Â
$config['web-service'] = [];
|
184 |
Â
}
|
185 |
-
if (
|
186 |
-
$config['
|
187 |
Â
}
|
188 |
Â
|
189 |
Â
if ($config['converters'] == null) {
|
@@ -246,6 +254,15 @@ class Config
|
|
246 |
Â
return $config;
|
247 |
Â
}
|
248 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
249 |
Â
|
250 |
Â
public static $configForOptionsPage = null; // cache the result (called twice, - also in enqueue_scripts)
|
251 |
Â
public static function getConfigForOptionsPage()
|
@@ -347,6 +364,43 @@ class Config
|
|
347 |
Â
return self::loadJSONOptions(Paths::getWodOptionsFileName());
|
348 |
Â
}
|
349 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
350 |
Â
public static function saveConfigurationFile($config)
|
351 |
Â
{
|
352 |
Â
$config['paths-used-in-htaccess'] = [
|
@@ -359,7 +413,9 @@ class Config
|
|
359 |
Â
$success = self::saveJSONOptions(Paths::getConfigFileName(), $config);
|
360 |
Â
if ($success) {
|
361 |
Â
State::setState('configured', true);
|
Â
|
|
362 |
Â
}
|
Â
|
|
363 |
Â
return $success;
|
364 |
Â
}
|
365 |
Â
return false;
|
@@ -376,6 +432,7 @@ class Config
|
|
376 |
Â
$public = (isset($config['cache-control-public']) ? $config['cache-control-public'] : true);
|
377 |
Â
$maxAge = (isset($config['cache-control-max-age']) ? $config['cache-control-max-age'] : $cacheControl);
|
378 |
Â
$maxAgeOptions = [
|
Â
|
|
379 |
Â
'one-second' => 'max-age=1',
|
380 |
Â
'one-minute' => 'max-age=60',
|
381 |
Â
'one-hour' => 'max-age=3600',
|
@@ -439,12 +496,30 @@ class Config
|
|
439 |
Â
unset($options['cache-control-custom']);
|
440 |
Â
unset($options['cache-control-public']);
|
441 |
Â
unset($options['cache-control-max-age']);
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
442 |
Â
|
443 |
Â
|
444 |
Â
//unset($options['forward-query-string']); // It is used in webp-on-demand.php, so do not unset!
|
445 |
Â
unset($options['do-not-pass-source-in-query-string']);
|
446 |
Â
unset($options['redirect-to-existing-in-htaccess']);
|
447 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
448 |
Â
return $options;
|
449 |
Â
}
|
450 |
Â
|
45 |
Â
|
46 |
Â
public static function saveJSONOptions($filename, $obj)
|
47 |
Â
{
|
48 |
+
$result = @file_put_contents($filename, json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT));
|
49 |
Â
/*if ($result === false) {
|
50 |
Â
echo 'COULD NOT' . $filename;
|
51 |
Â
}*/
|
67 |
Â
|
68 |
Â
return [
|
69 |
Â
|
70 |
+
'operation-mode' => 'varied-responses',
|
71 |
Â
|
72 |
Â
// redirection rules
|
73 |
Â
'enable-redirection-to-converter' => true,
|
77 |
Â
'do-not-pass-source-in-query-string' => false,
|
78 |
Â
'redirect-to-existing-in-htaccess' => true,
|
79 |
Â
'forward-query-string' => false,
|
80 |
+
'enable-redirection-to-webp-realizer' => true,
|
81 |
Â
|
82 |
Â
// conversion options
|
83 |
Â
'converters' => [],
|
96 |
Â
'fail' => 'original',
|
97 |
Â
'success-response' => 'converted',
|
98 |
Â
|
99 |
+
// alter html options
|
100 |
+
'alter-html' => [
|
101 |
+
'enabled' => false,
|
102 |
+
'replacement' => 'picture', // "picture" or "url"
|
103 |
+
'hooks' => 'ob', // "content-hooks" or "ob"
|
104 |
+
'only-for-webp-enabled-browsers' => false, // If true, there will be two HTML versions of each page
|
105 |
+
'only-for-webps-that-exists' => false,
|
106 |
+
],
|
107 |
+
|
108 |
Â
// web service
|
109 |
Â
'web-service' => [
|
110 |
Â
'enabled' => false,
|
130 |
Â
public static function applyOperationMode($config)
|
131 |
Â
{
|
132 |
Â
if (!isset($config['operation-mode'])) {
|
133 |
+
$config['operation-mode'] = 'varied-responses';
|
134 |
Â
}
|
135 |
Â
|
136 |
+
if ($config['operation-mode'] == 'varied-responses') {
|
137 |
Â
$config = array_merge($config, [
|
138 |
Â
'enable-redirection-to-converter' => true,
|
139 |
Â
'only-redirect-to-converter-for-webp-enabled-browsers' => true,
|
143 |
Â
'fail' => 'original',
|
144 |
Â
'success-response' => 'converted',
|
145 |
Â
]);
|
146 |
+
} elseif ($config['operation-mode'] == 'no-varied-responses') {
|
147 |
Â
$config = array_merge($config, [
|
148 |
Â
'only-redirect-to-converter-for-webp-enabled-browsers' => false,
|
149 |
Â
'only-redirect-to-converter-on-cache-miss' => true,
|
150 |
Â
'do-not-pass-source-in-query-string' => true,
|
151 |
Â
'redirect-to-existing-in-htaccess' => false,
|
Â
|
|
152 |
Â
'fail' => 'original',
|
153 |
Â
'success-response' => 'original',
|
154 |
Â
]);
|
159 |
Â
$config = array_merge($config, [
|
160 |
Â
'enable-redirection-to-converter' => false,
|
161 |
Â
'destination-folder' => 'mingled',
|
162 |
+
'enable-redirection-to-webp-realizer' => false,
|
163 |
Â
]);
|
164 |
Â
}
|
165 |
Â
|
166 |
Â
return $config;
|
167 |
Â
}
|
168 |
Â
|
169 |
+
public static function fix($config, $checkQualityDetection = true)
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
170 |
Â
{
|
Â
|
|
171 |
Â
if ($config === false) {
|
172 |
Â
$config = self::getDefaultConfig(!$checkQualityDetection);
|
173 |
Â
} else {
|
179 |
Â
}
|
180 |
Â
}
|
181 |
Â
}
|
182 |
+
$defaultConfig = self::getDefaultConfig(true);
|
183 |
+
$config = array_merge($defaultConfig, $config);
|
184 |
+
|
185 |
+
$config['alter-html'] = array_replace_recursive($defaultConfig['alter-html'], $config['alter-html']);
|
186 |
Â
}
|
187 |
Â
|
188 |
Â
$config = self::applyOperationMode($config);
|
190 |
Â
if (!isset($config['web-service'])) {
|
191 |
Â
$config['web-service'] = [];
|
192 |
Â
}
|
193 |
+
if (($config['cache-control'] == 'set') && ($config['cache-control-max-age'] == '')) {
|
194 |
+
$config['cache-control-max-age'] = 'one-week';
|
195 |
Â
}
|
196 |
Â
|
197 |
Â
if ($config['converters'] == null) {
|
254 |
Â
return $config;
|
255 |
Â
}
|
256 |
Â
|
257 |
+
/**
|
258 |
+
* Loads Config (if available), fills in the rest with defaults
|
259 |
+
* also applies operation mode.
|
260 |
+
*/
|
261 |
+
public static function loadConfigAndFix($checkQualityDetection = true)
|
262 |
+
{
|
263 |
+
return self::fix(Config::loadConfig(), $checkQualityDetection);
|
264 |
+
}
|
265 |
+
|
266 |
Â
|
267 |
Â
public static $configForOptionsPage = null; // cache the result (called twice, - also in enqueue_scripts)
|
268 |
Â
public static function getConfigForOptionsPage()
|
364 |
Â
return self::loadJSONOptions(Paths::getWodOptionsFileName());
|
365 |
Â
}
|
366 |
Â
|
367 |
+
/**
|
368 |
+
* Some of the options in config needs to be quickly accessible
|
369 |
+
* These are stored in wordpress autoloaded options
|
370 |
+
*/
|
371 |
+
public static function updateAutoloadedOptions($config)
|
372 |
+
{
|
373 |
+
$config = self::fix($config, false);
|
374 |
+
|
375 |
+
update_option('webp-express-alter-html', $config['alter-html']['enabled'], true);
|
376 |
+
update_option('webp-express-alter-html-hooks', $config['alter-html']['hooks'], true);
|
377 |
+
update_option('webp-express-alter-html-replacement', $config['alter-html']['replacement'], true);
|
378 |
+
|
379 |
+
//update_option('webp-express-alter-html', $config['alter-html']['enabled'], true);
|
380 |
+
|
381 |
+
$obj = $config['alter-html'];
|
382 |
+
unset($obj['enabled']);
|
383 |
+
$obj['destination-folder'] = $config['destination-folder'];
|
384 |
+
$obj['destination-extension'] = $config['destination-extension'];
|
385 |
+
$obj['bases'] = [
|
386 |
+
'uploads' => [
|
387 |
+
Paths::getUploadDirAbs(),
|
388 |
+
Paths::getUploadUrl()
|
389 |
+
],
|
390 |
+
'content' => [
|
391 |
+
Paths::getContentDirAbs(),
|
392 |
+
Paths::getContentUrl()
|
393 |
+
],
|
394 |
+
];
|
395 |
+
$obj['image-types'] = $config['image-types']; // 0=none,1=jpg, 2=png, 3=both
|
396 |
+
|
397 |
+
update_option(
|
398 |
+
'webp-express-alter-html-options',
|
399 |
+
json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK),
|
400 |
+
true
|
401 |
+
);
|
402 |
+
}
|
403 |
+
|
404 |
Â
public static function saveConfigurationFile($config)
|
405 |
Â
{
|
406 |
Â
$config['paths-used-in-htaccess'] = [
|
413 |
Â
$success = self::saveJSONOptions(Paths::getConfigFileName(), $config);
|
414 |
Â
if ($success) {
|
415 |
Â
State::setState('configured', true);
|
416 |
+
self::updateAutoloadedOptions($config);
|
417 |
Â
}
|
418 |
+
|
419 |
Â
return $success;
|
420 |
Â
}
|
421 |
Â
return false;
|
432 |
Â
$public = (isset($config['cache-control-public']) ? $config['cache-control-public'] : true);
|
433 |
Â
$maxAge = (isset($config['cache-control-max-age']) ? $config['cache-control-max-age'] : $cacheControl);
|
434 |
Â
$maxAgeOptions = [
|
435 |
+
'' => 'max-age=604800', // it has happened, but I don't think it can happen again...
|
436 |
Â
'one-second' => 'max-age=1',
|
437 |
Â
'one-minute' => 'max-age=60',
|
438 |
Â
'one-hour' => 'max-age=3600',
|
496 |
Â
unset($options['cache-control-custom']);
|
497 |
Â
unset($options['cache-control-public']);
|
498 |
Â
unset($options['cache-control-max-age']);
|
499 |
+
unset($options['paths-used-in-htaccess']);
|
500 |
+
unset($options['web-service']);
|
501 |
+
unset($options['alter-html']);
|
502 |
+
unset($options['enable-redirection-to-converter']);
|
503 |
+
unset($options['operation-mode']);
|
504 |
+
unset($options['only-redirect-to-converter-for-webp-enabled-browsers']);
|
505 |
+
unset($options['only-redirect-to-converter-on-cache-miss']);
|
506 |
+
unset($options['enable-redirection-to-webp-realizer']);
|
507 |
+
|
508 |
+
|
509 |
+
//unset($options['']);
|
510 |
+
//unset($options['']);
|
511 |
+
//unset($options['']);
|
512 |
+
|
513 |
Â
|
514 |
Â
|
515 |
Â
//unset($options['forward-query-string']); // It is used in webp-on-demand.php, so do not unset!
|
516 |
Â
unset($options['do-not-pass-source-in-query-string']);
|
517 |
Â
unset($options['redirect-to-existing-in-htaccess']);
|
518 |
Â
|
519 |
+
$options['paths'] = [
|
520 |
+
'uploadDirRel' => Paths::getUploadDirRel()
|
521 |
+
];
|
522 |
+
|
523 |
Â
return $options;
|
524 |
Â
}
|
525 |
Â
|
lib/classes/FileHelper.php
CHANGED
@@ -33,7 +33,7 @@ class FileHelper
|
|
33 |
Â
* If failure, it returns $fallback
|
34 |
Â
*/
|
35 |
Â
public static function filePermWithFallback($filename, $fallback) {
|
36 |
-
$perm = self::filePerm();
|
37 |
Â
if ($perm === false) {
|
38 |
Â
return $fallback;
|
39 |
Â
}
|
@@ -45,7 +45,7 @@ class FileHelper
|
|
45 |
Â
}
|
46 |
Â
|
47 |
Â
public static function humanReadableFilePermOfFile($filename) {
|
48 |
-
return self::
|
49 |
Â
}
|
50 |
Â
|
51 |
Â
/**
|
@@ -72,6 +72,56 @@ class FileHelper
|
|
72 |
Â
return false;
|
73 |
Â
}
|
74 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
75 |
Â
/**
|
76 |
Â
* Create a dir using same permissions as parent.
|
77 |
Â
* If
|
33 |
Â
* If failure, it returns $fallback
|
34 |
Â
*/
|
35 |
Â
public static function filePermWithFallback($filename, $fallback) {
|
36 |
+
$perm = self::filePerm($filename);
|
37 |
Â
if ($perm === false) {
|
38 |
Â
return $fallback;
|
39 |
Â
}
|
45 |
Â
}
|
46 |
Â
|
47 |
Â
public static function humanReadableFilePermOfFile($filename) {
|
48 |
+
return self::humanReadableFilePerm(self::filePerm($filename));
|
49 |
Â
}
|
50 |
Â
|
51 |
Â
/**
|
72 |
Â
return false;
|
73 |
Â
}
|
74 |
Â
|
75 |
+
public static function chmod_r($dir, $dirPerm = null, $filePerm = null, $uid = null, $gid = null, $regexFileMatchPattern = null, $regexDirMatchPattern = null) {
|
76 |
+
$fileIterator = new \FilesystemIterator($dir);
|
77 |
+
|
78 |
+
while ($fileIterator->valid()) {
|
79 |
+
$filename = $fileIterator->getFilename();
|
80 |
+
$filepath = $dir . "/" . $filename;
|
81 |
+
|
82 |
+
// echo $filepath . "\n";
|
83 |
+
|
84 |
+
$isDir = @is_dir($filepath);
|
85 |
+
|
86 |
+
if ((!$isDir && (is_null($regexFileMatchPattern) || preg_match($regexFileMatchPattern, $filename))) ||
|
87 |
+
($isDir && (is_null($regexDirMatchPattern) || preg_match($regexDirMatchPattern, $filename)))) {
|
88 |
+
// chmod
|
89 |
+
if ($isDir) {
|
90 |
+
if (!is_null($dirPerm)) {
|
91 |
+
self::chmod($filepath, $dirPerm);
|
92 |
+
//echo '. chmod dir to:' . self::humanReadableFilePerm($dirPerm) . '. result:' . self::humanReadableFilePermOfFile($filepath) . "\n";
|
93 |
+
}
|
94 |
+
} else {
|
95 |
+
if (!is_null($filePerm)) {
|
96 |
+
self::chmod($filepath, $filePerm);
|
97 |
+
//echo '. chmod file to:' . self::humanReadableFilePerm($filePerm) . '. result:' . self::humanReadableFilePermOfFile($filepath) . "\n";
|
98 |
+
}
|
99 |
+
|
100 |
+
}
|
101 |
+
|
102 |
+
// chown
|
103 |
+
if (!is_null($uid)) {
|
104 |
+
@chown($filepath, $uid);
|
105 |
+
}
|
106 |
+
|
107 |
+
// chgrp
|
108 |
+
if (!is_null($gid)) {
|
109 |
+
@chgrp($filepath, $gid);
|
110 |
+
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
// recurse
|
115 |
+
if ($isDir) {
|
116 |
+
self::chmod_r($filepath, $dirPerm, $filePerm, $uid, $gid, $regexFileMatchPattern, $regexDirMatchPattern);
|
117 |
+
}
|
118 |
+
|
119 |
+
// next!
|
120 |
+
$fileIterator->next();
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
|
125 |
Â
/**
|
126 |
Â
* Create a dir using same permissions as parent.
|
127 |
Â
* If
|
lib/classes/HTAccess.php
CHANGED
@@ -36,8 +36,8 @@ class HTAccess
|
|
36 |
Â
];
|
37 |
Â
$config = array_merge($defaults, $config);
|
38 |
Â
|
39 |
-
if ((!$config['enable-redirection-to-converter']) && (!$config['redirect-to-existing-in-htaccess'])) {
|
40 |
-
return '# WebP Express does not need to write any rules (it has not been set up to redirect to converter, nor to existing webp)';
|
41 |
Â
}
|
42 |
Â
|
43 |
Â
/* Calculate $fileExt */
|
@@ -55,9 +55,79 @@ class HTAccess
|
|
55 |
Â
return '# WebP Express disabled (no image types have been choosen to be converted/redirected)';
|
56 |
Â
}
|
57 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
58 |
Â
/* Build rules */
|
59 |
Â
$rules = '';
|
60 |
Â
|
Â
|
|
61 |
Â
/*
|
62 |
Â
// The next line sets an environment variable.
|
63 |
Â
// On the options page, we verify if this is set to diagnose if "AllowOverride None" is presented in 'httpd.conf'
|
@@ -85,7 +155,7 @@ class HTAccess
|
|
85 |
Â
$htaccessDirRel = Paths::getUploadDirRel();
|
86 |
Â
break;
|
87 |
Â
case 'wp-content':
|
88 |
-
$htaccessDirRel = Paths::
|
89 |
Â
break;
|
90 |
Â
}
|
91 |
Â
|
@@ -96,7 +166,7 @@ class HTAccess
|
|
96 |
Â
// TODO: It seems $pathToExisting needs to be adjusted, depending on where the .htaccess is located
|
97 |
Â
// Ie, if plugin folder has been moved out of ABSPATH, we should ie set
|
98 |
Â
// $pathToExisting to 'doc-root/plugins-moved/'
|
99 |
-
// to get: RewriteRule ^\/?(.*)\.(jpe?g)$ /wp-content-moved/webp-express/webp-images/doc-root/plugins-moved/$1.$2.webp [NC,T=image/webp,
|
100 |
Â
|
101 |
Â
// https://stackoverflow.com/questions/34124819/mod-rewrite-set-custom-header-through-htaccess
|
102 |
Â
$mingled = ($config['destination-folder'] == 'mingled');
|
@@ -108,10 +178,10 @@ class HTAccess
|
|
108 |
Â
|
109 |
Â
if ($config['destination-extension'] == 'append') {
|
110 |
Â
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $htaccessDirRel . "/$1.$2.webp -f\n";
|
111 |
-
$rules .= " RewriteRule ^(.+)\.(" . $fileExt . ")$ $1.$2.webp [T=image/webp,
|
112 |
Â
} else {
|
113 |
Â
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $htaccessDirRel . "/$1.webp -f\n";
|
114 |
-
$rules .= " RewriteRule ^(.+)\.(" . $fileExt . ")$ $1.webp [T=image/webp,
|
115 |
Â
}
|
116 |
Â
}
|
117 |
Â
|
@@ -119,66 +189,61 @@ class HTAccess
|
|
119 |
Â
$rules .= " RewriteCond %{HTTP_ACCEPT} image/webp\n";
|
120 |
Â
$rules .= " RewriteCond %{REQUEST_FILENAME} -f\n";
|
121 |
Â
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $cacheDirRel . "/" . $htaccessDirRel . "/$1.$2.webp -f\n";
|
122 |
-
$rules .= " RewriteRule ^\/?(.*)\.(" . $fileExt . ")$ /" . $cacheDirRel . "/" . $htaccessDirRel . "/$1.$2.webp [NC,T=image/webp,
|
123 |
-
|
124 |
-
$cacheControlHeader = Config::getCacheControlHeader($config);
|
125 |
-
if ($cacheControlHeader != '') {
|
126 |
-
|
127 |
-
// Add Cache-Control header for webp files (this requires mod_headers)
|
128 |
-
// $rules .= "\n";
|
129 |
-
$rules .= " # Set Cache-Control header so these direct redirections also get cached\n";
|
130 |
-
$rules .= " <IfModule mod_headers.c>\n";
|
131 |
-
$rules .= " <FilesMatch \"\.webp$\">\n";
|
132 |
-
$rules .= " Header set Cache-Control \"" . $cacheControlHeader . "\"\n";
|
133 |
-
$rules .= " </FilesMatch>\n";
|
134 |
-
$rules .= " </IfModule>\n\n";
|
135 |
-
|
136 |
-
// Fall back to mod_expires if mod_headers is unavailable
|
137 |
-
$cacheControl = $config['cache-control'];
|
138 |
-
if ($cacheControl == 'custom') {
|
139 |
-
$expires = '';
|
140 |
-
|
141 |
-
// Do not add Expire header if private is set
|
142 |
-
// - because then the user don't want caching in proxies / CDNs.
|
143 |
-
// the Expires header doesn't differentiate between private/public
|
144 |
-
if (!(preg_match('/private/', $config['cache-control-custom']))) {
|
145 |
-
if (preg_match('/max-age=(\d+)/', $config['cache-control-custom'], $matches)) {
|
146 |
-
if (isset($matches[1])) {
|
147 |
-
$expires = $matches[1] . ' seconds';
|
148 |
-
}
|
149 |
-
}
|
150 |
-
}
|
151 |
Â
|
152 |
-
|
153 |
-
$expires = '';
|
154 |
-
} elseif ($cacheControl == 'set') {
|
155 |
-
if ($config['cache-control-public']) {
|
156 |
-
$cacheControlOptions = [
|
157 |
-
'no-header' => '',
|
158 |
-
'one-second' => '1 seconds',
|
159 |
-
'one-minute' => '1 minutes',
|
160 |
-
'one-hour' => '1 hours',
|
161 |
-
'one-day' => '1 days',
|
162 |
-
'one-week' => '1 weeks',
|
163 |
-
'one-month' => '1 months',
|
164 |
-
'one-year' => '1 years',
|
165 |
-
];
|
166 |
-
$expires = $cacheControlOptions[$config['cache-control-max-age']];
|
167 |
-
}
|
168 |
-
}
|
169 |
Â
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
179 |
Â
}
|
Â
|
|
Â
|
|
180 |
Â
}
|
181 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
182 |
Â
}
|
183 |
Â
|
184 |
Â
if ($config['enable-redirection-to-converter']) {
|
@@ -201,7 +266,7 @@ class HTAccess
|
|
201 |
Â
|
202 |
Â
|
203 |
Â
if (!$passSourceInQS) {
|
204 |
-
$rules .= " # Pass REQUEST_FILENAME to
|
205 |
Â
$rules .= $basicConditions;
|
206 |
Â
$rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ - [E=REQFN:%{REQUEST_FILENAME}]\n" .
|
207 |
Â
" <IfModule mod_headers.c>\n" .
|
@@ -235,7 +300,7 @@ class HTAccess
|
|
235 |
Â
$rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ " .
|
236 |
Â
"/" . Paths::getWodUrlPath() .
|
237 |
Â
($passSourceInQS ? "?xsource=x%{SCRIPT_FILENAME}&" : "?") .
|
238 |
-
"wp-content=" . Paths::
|
239 |
Â
($config['forward-query-string'] ? '&%1' : '') .
|
240 |
Â
" [NC,L]\n"; // E=WOD:1
|
241 |
Â
|
@@ -405,7 +470,7 @@ class HTAccess
|
|
405 |
Â
|
406 |
Â
public static function whichHTAccessDirIsThis($dir) {
|
407 |
Â
switch ($dir) {
|
408 |
-
case Paths::
|
409 |
Â
return 'wp-content';
|
410 |
Â
case Paths::getIndexDirAbs():
|
411 |
Â
return 'index';
|
@@ -544,7 +609,7 @@ class HTAccess
|
|
544 |
Â
//return self::saveHTAccessRules('# Plugin is deactivated');
|
545 |
Â
$indexDir = Paths::getIndexDirAbs();
|
546 |
Â
$homeDir = Paths::getHomeDirAbs();
|
547 |
-
$wpContentDir = Paths::
|
548 |
Â
$pluginDir = Paths::getPluginDirAbs();
|
549 |
Â
$uploadDir = Paths::getUploadDirAbs();
|
550 |
Â
|
@@ -619,7 +684,7 @@ class HTAccess
|
|
619 |
Â
list($minRequired, $pluginToo, $uploadToo) = self::getHTAccessDirRequirements();
|
620 |
Â
|
621 |
Â
$rules = HTAccess::generateHTAccessRulesFromConfigObj($config, 'wp-content');
|
622 |
-
$wpContentDir = Paths::
|
623 |
Â
$wpContentFailed = !(HTAccess::saveHTAccessRulesToFile($wpContentDir . '/.htaccess', $rules, true));
|
624 |
Â
|
625 |
Â
$overidingRulesInWpContentWarning = false;
|
36 |
Â
];
|
37 |
Â
$config = array_merge($defaults, $config);
|
38 |
Â
|
39 |
+
if ((!$config['enable-redirection-to-converter']) && (!$config['redirect-to-existing-in-htaccess']) && (!$config['enable-redirection-to-webp-realizer'])) {
|
40 |
+
return '# WebP Express does not need to write any rules (it has not been set up to redirect to converter, nor to existing webp, and the "convert non-existing webp-files upon request" option has not been enabled)';
|
41 |
Â
}
|
42 |
Â
|
43 |
Â
/* Calculate $fileExt */
|
55 |
Â
return '# WebP Express disabled (no image types have been choosen to be converted/redirected)';
|
56 |
Â
}
|
57 |
Â
|
58 |
+
|
59 |
+
// Build cache control rules
|
60 |
+
$ccRules = '';
|
61 |
+
$cacheControlHeader = Config::getCacheControlHeader($config);
|
62 |
+
if ($cacheControlHeader != '') {
|
63 |
+
|
64 |
+
if ($config['redirect-to-existing-in-htaccess']) {
|
65 |
+
$ccRules .= " # Set Cache-Control header so these direct redirections also get the header set\n";
|
66 |
+
if ($config['enable-redirection-to-webp-realizer']) {
|
67 |
+
$ccRules .= " # (and also webp-realizer.php)\n";
|
68 |
+
}
|
69 |
+
} else {
|
70 |
+
if ($config['enable-redirection-to-webp-realizer']) {
|
71 |
+
$ccRules .= " # Set Cache-Control header for requests to webp images\n";
|
72 |
+
}
|
73 |
+
}
|
74 |
+
$ccRules .= " <IfModule mod_headers.c>\n";
|
75 |
+
$ccRules .= " <FilesMatch \"\.webp$\">\n";
|
76 |
+
$ccRules .= " Header set Cache-Control \"" . $cacheControlHeader . "\"\n";
|
77 |
+
$ccRules .= " </FilesMatch>\n";
|
78 |
+
$ccRules .= " </IfModule>\n\n";
|
79 |
+
|
80 |
+
// Fall back to mod_expires if mod_headers is unavailable
|
81 |
+
$cacheControl = $config['cache-control'];
|
82 |
+
if ($cacheControl == 'custom') {
|
83 |
+
$expires = '';
|
84 |
+
|
85 |
+
// Do not add Expire header if private is set
|
86 |
+
// - because then the user don't want caching in proxies / CDNs.
|
87 |
+
// the Expires header doesn't differentiate between private/public
|
88 |
+
if (!(preg_match('/private/', $config['cache-control-custom']))) {
|
89 |
+
if (preg_match('/max-age=(\d+)/', $config['cache-control-custom'], $matches)) {
|
90 |
+
if (isset($matches[1])) {
|
91 |
+
$expires = $matches[1] . ' seconds';
|
92 |
+
}
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
} elseif ($cacheControl == 'no-header') {
|
97 |
+
$expires = '';
|
98 |
+
} elseif ($cacheControl == 'set') {
|
99 |
+
if ($config['cache-control-public']) {
|
100 |
+
$cacheControlOptions = [
|
101 |
+
'no-header' => '',
|
102 |
+
'one-second' => '1 seconds',
|
103 |
+
'one-minute' => '1 minutes',
|
104 |
+
'one-hour' => '1 hours',
|
105 |
+
'one-day' => '1 days',
|
106 |
+
'one-week' => '1 weeks',
|
107 |
+
'one-month' => '1 months',
|
108 |
+
'one-year' => '1 years',
|
109 |
+
];
|
110 |
+
$expires = $cacheControlOptions[$config['cache-control-max-age']];
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
if ($expires != '') {
|
115 |
+
// in case mod_headers is missing, try mod_expires
|
116 |
+
$ccRules .= " # Fall back to mod_expires if mod_headers is unavailable\n";
|
117 |
+
$ccRules .= " <IfModule !mod_headers.c>\n";
|
118 |
+
$ccRules .= " <IfModule mod_expires.c>\n";
|
119 |
+
$ccRules .= " ExpiresActive On\n";
|
120 |
+
$ccRules .= " ExpiresByType image/webp \"access plus " . $expires . "\"\n";
|
121 |
+
$ccRules .= " </IfModule>\n";
|
122 |
+
$ccRules .= " </IfModule>\n\n";
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
|
127 |
Â
/* Build rules */
|
128 |
Â
$rules = '';
|
129 |
Â
|
130 |
+
|
131 |
Â
/*
|
132 |
Â
// The next line sets an environment variable.
|
133 |
Â
// On the options page, we verify if this is set to diagnose if "AllowOverride None" is presented in 'httpd.conf'
|
155 |
Â
$htaccessDirRel = Paths::getUploadDirRel();
|
156 |
Â
break;
|
157 |
Â
case 'wp-content':
|
158 |
+
$htaccessDirRel = Paths::getContentDirRel();
|
159 |
Â
break;
|
160 |
Â
}
|
161 |
Â
|
166 |
Â
// TODO: It seems $pathToExisting needs to be adjusted, depending on where the .htaccess is located
|
167 |
Â
// Ie, if plugin folder has been moved out of ABSPATH, we should ie set
|
168 |
Â
// $pathToExisting to 'doc-root/plugins-moved/'
|
169 |
+
// to get: RewriteRule ^\/?(.*)\.(jpe?g)$ /wp-content-moved/webp-express/webp-images/doc-root/plugins-moved/$1.$2.webp [NC,T=image/webp,E=WEBPACCEPT:1,E=EXISTING:1,L]
|
170 |
Â
|
171 |
Â
// https://stackoverflow.com/questions/34124819/mod-rewrite-set-custom-header-through-htaccess
|
172 |
Â
$mingled = ($config['destination-folder'] == 'mingled');
|
178 |
Â
|
179 |
Â
if ($config['destination-extension'] == 'append') {
|
180 |
Â
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $htaccessDirRel . "/$1.$2.webp -f\n";
|
181 |
+
$rules .= " RewriteRule ^(.+)\.(" . $fileExt . ")$ $1.$2.webp [T=image/webp,E=EXISTING:1,L]\n\n";
|
182 |
Â
} else {
|
183 |
Â
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $htaccessDirRel . "/$1.webp -f\n";
|
184 |
+
$rules .= " RewriteRule ^(.+)\.(" . $fileExt . ")$ $1.webp [T=image/webp,E=EXISTING:1,L]\n\n";
|
185 |
Â
}
|
186 |
Â
}
|
187 |
Â
|
189 |
Â
$rules .= " RewriteCond %{HTTP_ACCEPT} image/webp\n";
|
190 |
Â
$rules .= " RewriteCond %{REQUEST_FILENAME} -f\n";
|
191 |
Â
$rules .= " RewriteCond %{DOCUMENT_ROOT}/" . $cacheDirRel . "/" . $htaccessDirRel . "/$1.$2.webp -f\n";
|
192 |
+
$rules .= " RewriteRule ^\/?(.*)\.(" . $fileExt . ")$ /" . $cacheDirRel . "/" . $htaccessDirRel . "/$1.$2.webp [NC,T=image/webp,E=EXISTING:1,L]\n\n";
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
193 |
Â
|
194 |
+
$rules .= $ccRules;
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
195 |
Â
|
196 |
+
}
|
197 |
+
|
198 |
+
if ($config['enable-redirection-to-webp-realizer']) {
|
199 |
+
/*
|
200 |
+
# Pass REQUEST_FILENAME to PHP in request header
|
201 |
+
RewriteCond %{REQUEST_FILENAME} !-f
|
202 |
+
RewriteCond %{DOCUMENT_ROOT}/wordpress/uploads-moved/$1 -f
|
203 |
+
RewriteRule ^(.*)\.(webp)$ - [E=REQFN:%{REQUEST_FILENAME}]
|
204 |
+
<IfModule mod_headers.c>
|
205 |
+
RequestHeader set REQFN "%{REQFN}e" env=REQFN
|
206 |
+
</IfModule>
|
207 |
+
|
208 |
+
# WebP Realizer: Redirect non-existing webp images to converter when a corresponding jpeg/png is found
|
209 |
+
RewriteCond %{REQUEST_FILENAME} !-f
|
210 |
+
RewriteCond %{DOCUMENT_ROOT}/wordpress/uploads-moved/$1 -f
|
211 |
+
RewriteRule ^(.*)\.(webp)$ /plugins-moved/webp-express/wod/webp-realizer.php?wp-content=wp-content-moved [NC,L]
|
212 |
+
*/
|
213 |
+
$passSourceInQSRealizer = $passSourceInQS;
|
214 |
+
|
215 |
+
$basicConditionsRealizer = '';
|
216 |
+
$basicConditionsRealizer .= " RewriteCond %{REQUEST_FILENAME} !-f\n";
|
217 |
+
if ($mingled) {
|
218 |
+
if ($config['destination-extension'] == 'append') {
|
219 |
+
$basicConditionsRealizer .= " RewriteCond %{DOCUMENT_ROOT}/" . $htaccessDirRel . "/$1 -f\n";
|
220 |
+
} else {
|
221 |
+
//$basicConditionsRealizer .= " RewriteCond %{DOCUMENT_ROOT}/" . $htaccessDirRel . "/$1.webp !-f\n";
|
222 |
Â
}
|
223 |
+
} else {
|
224 |
+
//$basicConditionsRealizer .= " RewriteCond %{DOCUMENT_ROOT}/" . $cacheDirRel . "/" . $htaccessDirRel . "/$1.$2.webp !-f\n";
|
225 |
Â
}
|
226 |
Â
|
227 |
+
|
228 |
+
$rules .= " # Pass REQUEST_FILENAME to webp-realizer.php in request header\n";
|
229 |
+
$rules .= $basicConditionsRealizer;
|
230 |
+
$rules .= " RewriteRule ^(.*)\.(webp)$ - [E=REQFN:%{REQUEST_FILENAME}]\n" .
|
231 |
+
" <IfModule mod_headers.c>\n" .
|
232 |
+
" RequestHeader set REQFN \"%{REQFN}e\" env=REQFN\n" .
|
233 |
+
" </IfModule>\n\n";
|
234 |
+
|
235 |
+
$rules .= " # WebP Realizer: Redirect non-existing webp images to webp-realizer.php, which will locate corresponding jpg/png, convert it, and deliver the webp (if possible) \n";
|
236 |
+
$rules .= $basicConditionsRealizer;
|
237 |
+
|
238 |
+
$rules .= " RewriteRule ^(.*)\.(webp)$ " .
|
239 |
+
"/" . Paths::getWebPRealizerUrlPath() .
|
240 |
+
($passSourceInQSRealizer ? "?xdestination=x%{SCRIPT_FILENAME}&" : "?") .
|
241 |
+
"wp-content=" . Paths::getContentDirRel() .
|
242 |
+
" [NC,L]\n\n"; // E=WOD:1
|
243 |
+
|
244 |
+
if (!$config['redirect-to-existing-in-htaccess']) {
|
245 |
+
$rules .= $ccRules;
|
246 |
+
}
|
247 |
Â
}
|
248 |
Â
|
249 |
Â
if ($config['enable-redirection-to-converter']) {
|
266 |
Â
|
267 |
Â
|
268 |
Â
if (!$passSourceInQS) {
|
269 |
+
$rules .= " # Pass REQUEST_FILENAME to webp-on-demand.php in request header\n";
|
270 |
Â
$rules .= $basicConditions;
|
271 |
Â
$rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ - [E=REQFN:%{REQUEST_FILENAME}]\n" .
|
272 |
Â
" <IfModule mod_headers.c>\n" .
|
300 |
Â
$rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ " .
|
301 |
Â
"/" . Paths::getWodUrlPath() .
|
302 |
Â
($passSourceInQS ? "?xsource=x%{SCRIPT_FILENAME}&" : "?") .
|
303 |
+
"wp-content=" . Paths::getContentDirRel() .
|
304 |
Â
($config['forward-query-string'] ? '&%1' : '') .
|
305 |
Â
" [NC,L]\n"; // E=WOD:1
|
306 |
Â
|
470 |
Â
|
471 |
Â
public static function whichHTAccessDirIsThis($dir) {
|
472 |
Â
switch ($dir) {
|
473 |
+
case Paths::getContentDirAbs():
|
474 |
Â
return 'wp-content';
|
475 |
Â
case Paths::getIndexDirAbs():
|
476 |
Â
return 'index';
|
609 |
Â
//return self::saveHTAccessRules('# Plugin is deactivated');
|
610 |
Â
$indexDir = Paths::getIndexDirAbs();
|
611 |
Â
$homeDir = Paths::getHomeDirAbs();
|
612 |
+
$wpContentDir = Paths::getContentDirAbs();
|
613 |
Â
$pluginDir = Paths::getPluginDirAbs();
|
614 |
Â
$uploadDir = Paths::getUploadDirAbs();
|
615 |
Â
|
684 |
Â
list($minRequired, $pluginToo, $uploadToo) = self::getHTAccessDirRequirements();
|
685 |
Â
|
686 |
Â
$rules = HTAccess::generateHTAccessRulesFromConfigObj($config, 'wp-content');
|
687 |
+
$wpContentDir = Paths::getContentDirAbs();
|
688 |
Â
$wpContentFailed = !(HTAccess::saveHTAccessRulesToFile($wpContentDir . '/.htaccess', $rules, true));
|
689 |
Â
|
690 |
Â
$overidingRulesInWpContentWarning = false;
|
lib/classes/Messenger.php
CHANGED
@@ -19,8 +19,14 @@ class Messenger
|
|
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 |
Â
}
|
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 |
Â
$pendingMessages = State::getState('pendingMessages', []);
|
23 |
+
|
24 |
+
// Ensure we do not add a message that is already pending.
|
25 |
+
foreach ($pendingMessages as $i => $entry) {
|
26 |
+
if ($entry['message'] == $msg) {
|
27 |
+
return;
|
28 |
+
}
|
29 |
+
}
|
30 |
Â
$pendingMessages[] = ['level' => $level, 'message' => $msg];
|
31 |
Â
State::setState('pendingMessages', $pendingMessages);
|
32 |
Â
}
|
lib/classes/Paths.php
CHANGED
@@ -116,42 +116,42 @@ class Paths
|
|
116 |
Â
}
|
117 |
Â
|
118 |
Â
// ------------ WP Content Dir -------------
|
119 |
-
public static function
|
120 |
Â
{
|
121 |
Â
return self::getAbsDir(WP_CONTENT_DIR);
|
122 |
Â
}
|
123 |
-
public static function
|
124 |
Â
{
|
125 |
-
return self::getRelDir(self::
|
126 |
Â
}
|
127 |
Â
|
128 |
Â
public static function isWPContentDirMoved()
|
129 |
Â
{
|
130 |
-
return (self::
|
131 |
Â
}
|
132 |
Â
|
133 |
Â
public static function isWPContentDirMovedOutOfAbsPath()
|
134 |
Â
{
|
135 |
-
return !(self::isDirInsideDir(self::
|
136 |
Â
}
|
137 |
Â
|
138 |
Â
|
139 |
Â
// ------------ Content Dir -------------
|
140 |
Â
// (the "webp-express" directory inside wp-content)
|
141 |
Â
|
142 |
-
public static function
|
143 |
Â
{
|
144 |
-
return self::
|
145 |
Â
}
|
146 |
Â
|
147 |
-
public static function
|
148 |
Â
{
|
149 |
-
return self::getRelDir(self::
|
150 |
Â
}
|
151 |
Â
|
152 |
Â
public static function createContentDirIfMissing()
|
153 |
Â
{
|
154 |
-
return self::createDirIfMissing(self::
|
155 |
Â
}
|
156 |
Â
|
157 |
Â
// ------------ Upload Dir -------------
|
@@ -171,13 +171,13 @@ class Paths
|
|
171 |
Â
if ( defined( 'UPLOADS' ) ) {
|
172 |
Â
return ABSPATH . rtrim(UPLOADS, '/');
|
173 |
Â
} else {
|
174 |
-
return self::
|
175 |
Â
}
|
176 |
Â
}*/
|
177 |
Â
|
178 |
Â
public static function isUploadDirMovedOutOfWPContentDir()
|
179 |
Â
{
|
180 |
-
return !(self::isDirInsideDir(self::getUploadDirAbs(), self::
|
181 |
Â
}
|
182 |
Â
|
183 |
Â
public static function isUploadDirMovedOutOfAbsPath()
|
@@ -189,7 +189,7 @@ class Paths
|
|
189 |
Â
|
190 |
Â
public static function getConfigDirAbs()
|
191 |
Â
{
|
192 |
-
return self::
|
193 |
Â
}
|
194 |
Â
|
195 |
Â
public static function getConfigDirRel()
|
@@ -235,7 +235,7 @@ APACHE
|
|
235 |
Â
|
236 |
Â
public static function getCacheDirAbs()
|
237 |
Â
{
|
238 |
-
return self::
|
239 |
Â
}
|
240 |
Â
|
241 |
Â
public static function getCacheDirRel()
|
@@ -267,7 +267,7 @@ APACHE
|
|
267 |
Â
|
268 |
Â
public static function isPluginDirMovedOutOfWpContent()
|
269 |
Â
{
|
270 |
-
return !(self::isDirInsideDir(self::getPluginDirAbs(), self::
|
271 |
Â
}
|
272 |
Â
|
273 |
Â
// ------------ WebP Express Plugin Dir -------------
|
@@ -321,6 +321,31 @@ APACHE
|
|
321 |
Â
return self::getUrlPathFromUrl(self::getHomeUrl());
|
322 |
Â
}
|
323 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
324 |
Â
/**
|
325 |
Â
* 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)
|
326 |
Â
*/
|
@@ -339,6 +364,11 @@ APACHE
|
|
339 |
Â
return self::getPluginUrlPath() . '/wod/webp-on-demand.php';
|
340 |
Â
}
|
341 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
342 |
Â
public static function getWebServiceUrl()
|
343 |
Â
{
|
344 |
Â
//return self::getPluginUrl() . '/wpc.php';
|
116 |
Â
}
|
117 |
Â
|
118 |
Â
// ------------ WP Content Dir -------------
|
119 |
+
public static function getContentDirAbs()
|
120 |
Â
{
|
121 |
Â
return self::getAbsDir(WP_CONTENT_DIR);
|
122 |
Â
}
|
123 |
+
public static function getContentDirRel()
|
124 |
Â
{
|
125 |
+
return self::getRelDir(self::getContentDirAbs());
|
126 |
Â
}
|
127 |
Â
|
128 |
Â
public static function isWPContentDirMoved()
|
129 |
Â
{
|
130 |
+
return (self::getContentDirAbs() != (ABSPATH . 'wp-content'));
|
131 |
Â
}
|
132 |
Â
|
133 |
Â
public static function isWPContentDirMovedOutOfAbsPath()
|
134 |
Â
{
|
135 |
+
return !(self::isDirInsideDir(self::getContentDirAbs(), ABSPATH));
|
136 |
Â
}
|
137 |
Â
|
138 |
Â
|
139 |
Â
// ------------ Content Dir -------------
|
140 |
Â
// (the "webp-express" directory inside wp-content)
|
141 |
Â
|
142 |
+
public static function getWebPExpressContentDirAbs()
|
143 |
Â
{
|
144 |
+
return self::getContentDirAbs() . '/webp-express';
|
145 |
Â
}
|
146 |
Â
|
147 |
+
public static function getWebPExpressContentDirRel()
|
148 |
Â
{
|
149 |
+
return self::getRelDir(self::getWebPExpressContentDirAbs());
|
150 |
Â
}
|
151 |
Â
|
152 |
Â
public static function createContentDirIfMissing()
|
153 |
Â
{
|
154 |
+
return self::createDirIfMissing(self::getWebPExpressContentDirAbs());
|
155 |
Â
}
|
156 |
Â
|
157 |
Â
// ------------ Upload Dir -------------
|
171 |
Â
if ( defined( 'UPLOADS' ) ) {
|
172 |
Â
return ABSPATH . rtrim(UPLOADS, '/');
|
173 |
Â
} else {
|
174 |
+
return self::getContentDirAbs() . '/uploads';
|
175 |
Â
}
|
176 |
Â
}*/
|
177 |
Â
|
178 |
Â
public static function isUploadDirMovedOutOfWPContentDir()
|
179 |
Â
{
|
180 |
+
return !(self::isDirInsideDir(self::getUploadDirAbs(), self::getContentDirAbs()));
|
181 |
Â
}
|
182 |
Â
|
183 |
Â
public static function isUploadDirMovedOutOfAbsPath()
|
189 |
Â
|
190 |
Â
public static function getConfigDirAbs()
|
191 |
Â
{
|
192 |
+
return self::getWebPExpressContentDirAbs() . '/config';
|
193 |
Â
}
|
194 |
Â
|
195 |
Â
public static function getConfigDirRel()
|
235 |
Â
|
236 |
Â
public static function getCacheDirAbs()
|
237 |
Â
{
|
238 |
+
return self::getWebPExpressContentDirAbs() . '/webp-images';
|
239 |
Â
}
|
240 |
Â
|
241 |
Â
public static function getCacheDirRel()
|
267 |
Â
|
268 |
Â
public static function isPluginDirMovedOutOfWpContent()
|
269 |
Â
{
|
270 |
+
return !(self::isDirInsideDir(self::getPluginDirAbs(), self::getContentDirAbs()));
|
271 |
Â
}
|
272 |
Â
|
273 |
Â
// ------------ WebP Express Plugin Dir -------------
|
321 |
Â
return self::getUrlPathFromUrl(self::getHomeUrl());
|
322 |
Â
}
|
323 |
Â
|
324 |
+
|
325 |
+
public static function getUploadUrl()
|
326 |
+
{
|
327 |
+
$uploadDir = wp_upload_dir(null, false);
|
328 |
+
return untrailingslashit($uploadDir['baseurl']);
|
329 |
+
}
|
330 |
+
|
331 |
+
public static function getUploadUrlPath()
|
332 |
+
{
|
333 |
+
return self::getUrlPathFromUrl(self::getUploadUrl());
|
334 |
+
}
|
335 |
+
|
336 |
+
public static function getContentUrl()
|
337 |
+
{
|
338 |
+
return untrailingslashit(content_url());
|
339 |
+
}
|
340 |
+
|
341 |
+
public static function getContentUrlPath()
|
342 |
+
{
|
343 |
+
return self::getUrlPathFromUrl(self::getContentUrl());
|
344 |
+
}
|
345 |
+
|
346 |
+
|
347 |
+
|
348 |
+
|
349 |
Â
/**
|
350 |
Â
* 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)
|
351 |
Â
*/
|
364 |
Â
return self::getPluginUrlPath() . '/wod/webp-on-demand.php';
|
365 |
Â
}
|
366 |
Â
|
367 |
+
public static function getWebPRealizerUrlPath()
|
368 |
+
{
|
369 |
+
return self::getPluginUrlPath() . '/wod/webp-realizer.php';
|
370 |
+
}
|
371 |
+
|
372 |
Â
public static function getWebServiceUrl()
|
373 |
Â
{
|
374 |
Â
//return self::getPluginUrl() . '/wpc.php';
|
lib/classes/TestRun.php
CHANGED
@@ -39,9 +39,9 @@ class TestRun
|
|
39 |
Â
$source = Paths::getWebPExpressPluginDirAbs() . '/test/small-q61.jpg';
|
40 |
Â
$destination = Paths::getUploadDirAbs() . '/webp-express-test-conversion.webp';
|
41 |
Â
if (!FileHelper::canCreateFile($destination)) {
|
42 |
-
$destination = Paths::
|
43 |
Â
}
|
44 |
-
if (!FileHelper::canCreateFile($destination)) {
|
45 |
Â
self::$converterStatus = false; // // cache the result
|
46 |
Â
return false;
|
47 |
Â
}
|
39 |
Â
$source = Paths::getWebPExpressPluginDirAbs() . '/test/small-q61.jpg';
|
40 |
Â
$destination = Paths::getUploadDirAbs() . '/webp-express-test-conversion.webp';
|
41 |
Â
if (!FileHelper::canCreateFile($destination)) {
|
42 |
+
$destination = Paths::getContentDirAbs() . '/webp-express-test-conversion.webp';
|
43 |
Â
}
|
44 |
+
if (!FileHelper::canCreateFile($destination)) {
|
45 |
Â
self::$converterStatus = false; // // cache the result
|
46 |
Â
return false;
|
47 |
Â
}
|
lib/migrate/migrate.php
CHANGED
@@ -3,6 +3,11 @@
|
|
3 |
Â
include_once __DIR__ . '/../classes/State.php';
|
4 |
Â
use \WebPExpress\State;
|
5 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
6 |
Â
/*
|
7 |
Â
In 0.4.0, we had a 'webp-express-configured' option.
|
8 |
Â
As long as there are still users on 0.4 or below, we must do the following:
|
@@ -39,24 +44,32 @@ if (!(State::getState('configured', false))) {
|
|
39 |
Â
}
|
40 |
Â
|
41 |
Â
}
|
42 |
-
|
43 |
-
if (intval(get_option('webp-express-migration-version', 0)) == 0) {
|
44 |
-
// run migration 1
|
45 |
-
// It must take care of updating migration-version to 1, - if successful.
|
46 |
-
include __DIR__ . '/migrate1.php';
|
47 |
-
}
|
48 |
Â
|
49 |
-
// We make sure to grab the option again - it might have been changed in the migration above
|
50 |
-
if (intval(get_option('webp-express-migration-version', 0)) == 1) {
|
51 |
-
// run migration 2
|
52 |
-
include __DIR__ . '/migrate2.php';
|
53 |
-
}
|
54 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
55 |
Â
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
Â
|
|
Â
|
|
Â
|
|
60 |
Â
}
|
61 |
-
*/
|
62 |
Â
}
|
Â
|
|
Â
|
|
Â
|
3 |
Â
include_once __DIR__ . '/../classes/State.php';
|
4 |
Â
use \WebPExpress\State;
|
5 |
Â
|
6 |
+
use \WebPConvert\Converters\Ewww;
|
7 |
+
use \WebPExpress\Messenger;
|
8 |
+
use \WebPExpress\Config;
|
9 |
+
|
10 |
+
|
11 |
Â
/*
|
12 |
Â
In 0.4.0, we had a 'webp-express-configured' option.
|
13 |
Â
As long as there are still users on 0.4 or below, we must do the following:
|
44 |
Â
}
|
45 |
Â
|
46 |
Â
}
|
47 |
+
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
48 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
49 |
Â
|
50 |
+
function webpexpress_keepEwwwSubscriptionAlive() {
|
51 |
+
include_once __DIR__ . '/../../vendor/autoload.php';
|
52 |
+
include_once __DIR__ . '/../classes/Messenger.php';
|
53 |
+
include_once __DIR__ . '/../classes/Config.php';
|
54 |
+
|
55 |
+
$config = Config::loadConfigAndFix(false);
|
56 |
+
foreach ($config['converters'] as $i => $converter) {
|
57 |
+
if (
|
58 |
+
($converter['converter'] == 'ewww') &&
|
59 |
+
(!(isset($converter['deactivated'])) || (!$converter['deactivated'])) &&
|
60 |
+
(isset($converter['options']['key'])))
|
61 |
+
{
|
62 |
+
$ewwwConvertResult = Ewww::keepSubscriptionAlive(__DIR__ . '/../../test/very-small.jpg', $converter['options']['key']);
|
63 |
Â
|
64 |
+
if ($ewwwConvertResult === true) {
|
65 |
+
Messenger::addMessage(
|
66 |
+
'info',
|
67 |
+
'Successfully optimized regular jpg with <i>ewww</i> converter in order to keep the subscription alive'
|
68 |
+
);
|
69 |
+
}
|
70 |
+
}
|
71 |
Â
}
|
Â
|
|
72 |
Â
}
|
73 |
+
|
74 |
+
// create
|
75 |
+
webpexpress_keepEwwwSubscriptionAlive();
|
lib/migrate/migrate5.php
ADDED
@@ -0,0 +1,55 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WebPExpress;
|
4 |
+
|
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/CacheMover.php';
|
13 |
+
use \WebPExpress\CacheMover;
|
14 |
+
|
15 |
+
function webpexpress_migrate5() {
|
16 |
+
|
17 |
+
// Regenerate configuration file and wod-options.json.
|
18 |
+
|
19 |
+
// By regenerating the config, we ensure that Config::updateAutoloadedOptions() is called,
|
20 |
+
// By regenerating wod-options.json, we ensure that the new "paths" option is there, which is required in "mingled" mode for
|
21 |
+
// determining if an image resides in the uploads folder or not.
|
22 |
+
|
23 |
+
$config = Config::loadConfigAndFix();
|
24 |
+
if ($config['operation-mode'] == 'just-convert') {
|
25 |
+
$config['operation-mode'] = 'no-varied-responses';
|
26 |
+
}
|
27 |
+
if ($config['operation-mode'] == 'standard') {
|
28 |
+
$config['operation-mode'] = 'varied-responses';
|
29 |
+
}
|
30 |
+
|
31 |
+
if (Config::saveConfigurationFileAndWodOptions($config)) {
|
32 |
+
|
33 |
+
// Moving destination in v0.10 might have created bad permissions. - so lets fix the permissions
|
34 |
+
//CacheMover::chmodFixSubDirs(CacheMover::getUploadFolder($config['destination-folder']));
|
35 |
+
CacheMover::chmodFixSubDirs(Paths::getCacheDirAbs(), true);
|
36 |
+
CacheMover::chmodFixSubDirs(Paths::getUploadDirAbs(), false);
|
37 |
+
|
38 |
+
Messenger::addMessage(
|
39 |
+
'info',
|
40 |
+
'Successfully migrated webp express options for 0.11+'
|
41 |
+
);
|
42 |
+
|
43 |
+
// PSST: When creating new migration files, remember to update WEBPEXPRESS_MIGRATION_VERSION in admin.php
|
44 |
+
update_option('webp-express-migration-version', '5');
|
45 |
+
|
46 |
+
} else {
|
47 |
+
Messenger::addMessage(
|
48 |
+
'error',
|
49 |
+
'Failed migrating webp express options to 0.11+. Probably you need to grant write permissions in your wp-content folder.'
|
50 |
+
);
|
51 |
+
}
|
52 |
+
|
53 |
+
}
|
54 |
+
|
55 |
+
webpexpress_migrate5();
|
lib/options/css/webp-express-options-page.css
CHANGED
@@ -153,12 +153,17 @@
|
|
153 |
Â
/*left: 22px;
|
154 |
Â
top: 20px;*/
|
155 |
Â
margin-top: -5px;
|
156 |
-
font-size: 12px;
|
157 |
Â
/*white-space: nowrap;*/
|
158 |
-
color: #000;
|
159 |
Â
min-width: 150px;
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
160 |
Â
font-weight: normal;
|
161 |
Â
text-align: left;
|
Â
|
|
Â
|
|
Â
|
|
162 |
Â
}
|
163 |
Â
|
164 |
Â
.help .popup.narrow {
|
@@ -173,6 +178,9 @@
|
|
173 |
Â
.help .popup.wider {
|
174 |
Â
width: 450px;
|
175 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
176 |
Â
@media (max-width: 500px) {
|
177 |
Â
.help .popup {
|
178 |
Â
max-width: 380px;
|
@@ -252,6 +260,12 @@
|
|
252 |
Â
vertical-align: top;
|
253 |
Â
font-family: sans-serif;
|
254 |
Â
position: relative;
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
255 |
Â
}
|
256 |
Â
.help.no-margin-left {
|
257 |
Â
margin-left: 0px;
|
@@ -409,3 +423,11 @@
|
|
409 |
Â
#webpexpress_settings .block.buttons table {
|
410 |
Â
float: right;
|
411 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
153 |
Â
/*left: 22px;
|
154 |
Â
top: 20px;*/
|
155 |
Â
margin-top: -5px;
|
Â
|
|
156 |
Â
/*white-space: nowrap;*/
|
Â
|
|
157 |
Â
min-width: 150px;
|
158 |
+
}
|
159 |
+
|
160 |
+
.popup,
|
161 |
+
.popup p {
|
162 |
Â
font-weight: normal;
|
163 |
Â
text-align: left;
|
164 |
+
line-height: 1.5;
|
165 |
+
font-size: 14px;
|
166 |
+
color: #000;
|
167 |
Â
}
|
168 |
Â
|
169 |
Â
.help .popup.narrow {
|
178 |
Â
.help .popup.wider {
|
179 |
Â
width: 450px;
|
180 |
Â
}
|
181 |
+
.help .popup.widest {
|
182 |
+
width: 600px;
|
183 |
+
}
|
184 |
Â
@media (max-width: 500px) {
|
185 |
Â
.help .popup {
|
186 |
Â
max-width: 380px;
|
260 |
Â
vertical-align: top;
|
261 |
Â
font-family: sans-serif;
|
262 |
Â
position: relative;
|
263 |
+
font-style: normal;
|
264 |
+
}
|
265 |
+
.help { /* new look! */
|
266 |
+
background-color: transparent;
|
267 |
+
border: 1px solid #999;
|
268 |
+
color: #333;
|
269 |
Â
}
|
270 |
Â
.help.no-margin-left {
|
271 |
Â
margin-left: 0px;
|
423 |
Â
#webpexpress_settings .block.buttons table {
|
424 |
Â
float: right;
|
425 |
Â
}
|
426 |
+
|
427 |
+
#alter_html_options_div > div > label {
|
428 |
+
font-style: italic;
|
429 |
+
}
|
430 |
+
|
431 |
+
#alter_html_options_div > div {
|
432 |
+
margin-top: 15px;
|
433 |
+
}
|
lib/options/enqueue_scripts.php
CHANGED
@@ -6,7 +6,7 @@ use \WebPExpress\Paths;
|
|
6 |
Â
include_once __DIR__ . '/../classes/Config.php';
|
7 |
Â
use \WebPExpress\Config;
|
8 |
Â
|
9 |
-
$version = '0.
|
10 |
Â
|
11 |
Â
function webp_express_add_inline_script($id, $script, $position) {
|
12 |
Â
if (function_exists('wp_add_inline_script')) {
|
6 |
Â
include_once __DIR__ . '/../classes/Config.php';
|
7 |
Â
use \WebPExpress\Config;
|
8 |
Â
|
9 |
+
$version = '0.11.0-dev1';
|
10 |
Â
|
11 |
Â
function webp_express_add_inline_script($id, $script, $position) {
|
12 |
Â
if (function_exists('wp_add_inline_script')) {
|
lib/options/js/page.js
CHANGED
@@ -4,13 +4,28 @@ function setOptionVisibility(elmId, show) {
|
|
4 |
Â
return;
|
5 |
Â
}
|
6 |
Â
if (show) {
|
7 |
-
elm.style['visibility'] = '
|
8 |
Â
elm.style['position'] = 'static';
|
9 |
Â
} else {
|
10 |
Â
elm.style['visibility'] = 'hidden';
|
11 |
Â
elm.style['position'] = 'absolute';
|
12 |
Â
}
|
13 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
14 |
Â
function updateCacheControlCustomVisibility() {
|
15 |
Â
|
16 |
Â
if (document.getElementById('cache_control_select') == null) {
|
@@ -93,6 +108,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
93 |
Â
updateCacheControlCustomVisibility();
|
94 |
Â
updateQualityVisibility();
|
95 |
Â
updateServerSettingsVisibility();
|
Â
|
|
Â
|
|
96 |
Â
updateDestinationExtensionVisibility();
|
97 |
Â
|
98 |
Â
if (document.getElementById('cache_control_select')) {
|
@@ -119,6 +136,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
119 |
Â
});
|
120 |
Â
}
|
121 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
122 |
Â
document.getElementById('change_operation_mode').addEventListener('change', function() {
|
123 |
Â
var msg;
|
124 |
Â
if (document.getElementById('operation_mode').value == 'tweaked') {
|
4 |
Â
return;
|
5 |
Â
}
|
6 |
Â
if (show) {
|
7 |
+
elm.style['visibility'] = 'inherit';
|
8 |
Â
elm.style['position'] = 'static';
|
9 |
Â
} else {
|
10 |
Â
elm.style['visibility'] = 'hidden';
|
11 |
Â
elm.style['position'] = 'absolute';
|
12 |
Â
}
|
13 |
Â
}
|
14 |
+
|
15 |
+
function updateAlterHTMLVisibility() {
|
16 |
+
if (document.getElementById('alter_html_enabled')) {
|
17 |
+
setOptionVisibility('alter_html_options_div', document.getElementById('alter_html_enabled').checked);
|
18 |
+
}
|
19 |
+
}
|
20 |
+
|
21 |
+
function updateAlterHTMLReplaceVisibility() {
|
22 |
+
if (document.getElementById('alter_html_replacement_url')) {
|
23 |
+
setOptionVisibility('alter_html_url_options_div', document.getElementById('alter_html_replacement_url').checked);
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
|
28 |
+
|
29 |
Â
function updateCacheControlCustomVisibility() {
|
30 |
Â
|
31 |
Â
if (document.getElementById('cache_control_select') == null) {
|
108 |
Â
updateCacheControlCustomVisibility();
|
109 |
Â
updateQualityVisibility();
|
110 |
Â
updateServerSettingsVisibility();
|
111 |
+
updateAlterHTMLVisibility();
|
112 |
+
updateAlterHTMLReplaceVisibility();
|
113 |
Â
updateDestinationExtensionVisibility();
|
114 |
Â
|
115 |
Â
if (document.getElementById('cache_control_select')) {
|
136 |
Â
});
|
137 |
Â
}
|
138 |
Â
|
139 |
+
if (document.getElementById('alter_html_enabled')) {
|
140 |
+
document.getElementById('alter_html_enabled').addEventListener('change', function() {
|
141 |
+
updateAlterHTMLVisibility();
|
142 |
+
});
|
143 |
+
}
|
144 |
+
|
145 |
+
if (document.getElementById('alter_html_replacement_url')) {
|
146 |
+
document.getElementById('alter_html_replacement_url').addEventListener('change', function() {
|
147 |
+
updateAlterHTMLReplaceVisibility();
|
148 |
+
});
|
149 |
+
document.getElementById('alter_html_replacement_picture').addEventListener('change', function() {
|
150 |
+
updateAlterHTMLReplaceVisibility();
|
151 |
+
});
|
152 |
+
}
|
153 |
+
|
154 |
+
|
155 |
+
|
156 |
Â
document.getElementById('change_operation_mode').addEventListener('change', function() {
|
157 |
Â
var msg;
|
158 |
Â
if (document.getElementById('operation_mode').value == 'tweaked') {
|
lib/options/options/alter-html/alter-html-options.inc
ADDED
@@ -0,0 +1,98 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<tr>
|
2 |
+
<th scope="row">Alter HTML? <span style="color:darkorange">(beta)</span><?php echo helpIcon(
|
3 |
+
'<p>Alter HTML to either use picture tag syntax for pointing to webp versions or point directly to webps.</p>' .
|
4 |
+
'<p>The feature is new in 0.11.0. It is marked as beta because of that, but it has been tested on many setups and with ' .
|
5 |
+
'many test cases, so it should be quite stable.</p>' .
|
6 |
+
(($config['operation-mode'] == 'varied-responses') ?
|
7 |
+
'<p>You do not need to enable this in <i>Varied image responses</i> operation mode, but enabling it has some benefits: ' .
|
8 |
+
'Caching is improved if you are on a CDN as the webp images that are requested directly does not need to be keyed by the Accept header. ' .
|
9 |
+
'Also, when a user downloads an image, it will have the correct extension.</p>' : '')
|
10 |
+
);
|
11 |
+
?>
|
12 |
+
</th>
|
13 |
+
<td>
|
14 |
+
<input
|
15 |
+
id="alter_html_enabled"
|
16 |
+
name="alter-html-enabled"
|
17 |
+
<?php echo ($config['alter-html']['enabled'] ? 'checked="checked"' : '') ?>
|
18 |
+
value="true"
|
19 |
+
type="checkbox"
|
20 |
+
>
|
21 |
+
<div id="alter_html_options_div">
|
22 |
+
<div>
|
23 |
+
<label>What to replace:</label>
|
24 |
+
<?php
|
25 |
+
webpexpress_radioButtons('alter-html-replacement', $config['alter-html']['replacement'], [
|
26 |
+
'picture' => 'Replace <img> tags with <picture> tags, adding the webp to srcset.',
|
27 |
+
'url' => 'Replace image URLs',
|
28 |
+
], [
|
29 |
+
'picture' => '<p>"Picture tag" replaces <img> tags with <picture> tags and adds the webp as an extra source. ' .
|
30 |
+
'This effectively points webp-enabled browsers to the webp variant and other browsers to ' .
|
31 |
+
'the original image. It also adds a script that dynamically loads picturefill.js in browsers ' .
|
32 |
+
'that doesnt support the picture tag.</p>' .
|
33 |
+
'<p><em>Beware that this structural change may affect styling!</em></p>' .
|
34 |
+
'<p>PS: This functionality is handled by ' .
|
35 |
+
'<a target="_blank" href="https://github.com/rosell-dk/dom-util-for-webp">this external library</a>' .
|
36 |
+
' (I have pushed the code to an external library so it can be used by other projects besides this plugin)</p>',
|
37 |
+
'url' => '<p>"Image URLs" replaces the image URLs to point to ' .
|
38 |
+
'the webp <i>rather than</i> the original. Handles src, srcset, common lazy-load attributes and even ' .
|
39 |
+
'inline styles</p>' .
|
40 |
+
'<p>Note that you will have to do something for the browsers that does not support webp. ' .
|
41 |
+
'For example, you can enable the <i>Only do the replacements in webp enabled browsers</i> option, which ' .
|
42 |
+
'will show up when you enable this option. ' .
|
43 |
+
'Or you can add the <a href="https://webpjs.appspot.com/">webpjs</a> javascript library. ' .
|
44 |
+
'In the next release, you will be able to add the webjs library with this plugin. ' .
|
45 |
+
'<p>PS: This replace functionality is handled by ' .
|
46 |
+
'<a target="_blank" href="https://github.com/rosell-dk/dom-util-for-webp">this external library</a>' .
|
47 |
+
', created for the purpose.</p>',
|
48 |
+
]
|
49 |
+
);
|
50 |
+
?>
|
51 |
+
<div id="alter_html_url_options_div" style="margin-left:39px; margin-top: -5px;">
|
52 |
+
<?php
|
53 |
+
webpexpress_checkbox(
|
54 |
+
'alter-html-only-for-webp-enabled-browsers',
|
55 |
+
$config['alter-html']['only-for-webp-enabled-browsers'],
|
56 |
+
'Only do the replacements in webp enabled browsers',
|
57 |
+
'If you enable this option, the replacements will only be made, when the request is from ' .
|
58 |
+
'a browser that supports webp. Note that this will not play well with plugins that caches ' .
|
59 |
+
'the page. Instead of enabling this option, you should consider using the Cache Enabler plugin, ' .
|
60 |
+
'which does the same, but with page caching.'
|
61 |
+
);
|
62 |
+
?>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
<div>
|
66 |
+
<div style="margin-left: 11px">
|
67 |
+
<?php
|
68 |
+
webpexpress_checkbox(
|
69 |
+
'alter-html-for-webps-that-has-yet-to-exist',
|
70 |
+
(!$config['alter-html']['only-for-webps-that-exists']),
|
71 |
+
'Reference webps that hasn\'t been converted yet',
|
72 |
+
'If you enable this option, there will be references to webp files that doesnt exist yet. ' .
|
73 |
+
'And that will be ok! - Just make sure to enable the option to convert missing webp files ' .
|
74 |
+
'upon request'
|
75 |
+
);
|
76 |
+
?>
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
+
<div>
|
80 |
+
<label>Where to replace: <?php echo helpIcon(
|
81 |
+
'<p></p>',
|
82 |
+
'no-margin-left set-margin-right');
|
83 |
+
?></label>
|
84 |
+
|
85 |
+
<?php
|
86 |
+
webpexpress_radioButtons('alter-html-hooks', $config['alter-html']['hooks'], [
|
87 |
+
'content-hooks' => 'Use content filtering hooks (the_content, the_excerpt, etc)',
|
88 |
+
'ob' => 'The complete page (using output buffering)</em>',
|
89 |
+
], [
|
90 |
+
]
|
91 |
+
);
|
92 |
+
?>
|
93 |
+
</div>
|
94 |
+
|
95 |
+
</div>
|
96 |
+
|
97 |
+
</td>
|
98 |
+
</tr>
|
lib/options/options/alter-html/alter-html.inc
ADDED
@@ -0,0 +1,19 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php if ($config['operation-mode'] == 'tweaked') : ?>
|
2 |
+
<fieldset class="block">
|
3 |
+
<h3>Alter HTML</h3>
|
4 |
+
<table class="form-table">
|
5 |
+
<tbody>
|
6 |
+
<?php
|
7 |
+
include_once 'alter-html-options.inc';
|
8 |
+
?>
|
9 |
+
</tbody>
|
10 |
+
</table>
|
11 |
+
</fieldset>
|
12 |
+
<?php
|
13 |
+
else:
|
14 |
+
|
15 |
+
if ($config['operation-mode'] != 'just-redirect') {
|
16 |
+
include_once 'alter-html-options.inc';
|
17 |
+
}
|
18 |
+
endif;
|
19 |
+
?>
|
lib/options/options/conversion-options/conversion-options.inc
CHANGED
@@ -20,7 +20,7 @@ else:
|
|
20 |
Â
include_once 'metadata.inc';
|
21 |
Â
include_once 'converters.inc';
|
22 |
Â
|
23 |
-
if ($config['operation-mode'] == '
|
24 |
Â
include_once 'destination-folder.inc';
|
25 |
Â
}
|
26 |
Â
include_once 'destination-extension.inc';
|
20 |
Â
include_once 'metadata.inc';
|
21 |
Â
include_once 'converters.inc';
|
22 |
Â
|
23 |
+
if (($config['operation-mode'] == 'varied-responses') || ($config['operation-mode'] == 'no-varied-responses')) {
|
24 |
Â
include_once 'destination-folder.inc';
|
25 |
Â
}
|
26 |
Â
include_once 'destination-extension.inc';
|
lib/options/options/conversion-options/destination-extension.inc
CHANGED
@@ -1,15 +1,24 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<tr id="destination_extension_row"><th scope="row">File extension<?php
|
2 |
+
echo helpIcon(
|
3 |
+
'<p>Controls the filename of the converted file.</p>' .
|
4 |
+
'<p>The "Append" option result in file names such as "image.png.webp". ' .
|
5 |
+
'The "Set" option results in file names such as "image.webp". ' .
|
6 |
+
'Note that if you choose "Set", it will be a problem if you ie both have a logo.jpg and a logo.png in the same folder. ' .
|
7 |
+
'If you are using WebP Express together with <a target="blank" href="https://da.wordpress.org/plugins/cache-enabler/">Cache enabler</a> ' .
|
8 |
+
'or <a target="_blank" href="https://wordpress.org/plugins/shortpixel-image-optimiser/">Shortpixel</a>, set this option to Set"</p>' .
|
9 |
+
(($config['operation-mode'] == 'no-varied-responses') ? '<p>In this mode, the webp files will be stored in the same folder as the originals, except for images that are not inside the uploads folder (these are stored in wp-content/webp-express/webp-images/doc-root).</p>' : '') .
|
10 |
+
'<p>Changing this option will cause existing webp images to be renamed (only those in the upload folder, and only those that has a ' .
|
11 |
+
'corresponding source image)</p>'
|
12 |
+
);?>
|
13 |
+
</th>
|
14 |
+
<td>
|
15 |
+
<select name="destination-extension">
|
16 |
+
<?php
|
17 |
+
webpexpress_selectBoxOptions($config['destination-extension'], [
|
18 |
+
'append' => 'Append ".webp"',
|
19 |
+
'set' => 'Set to ".webp"',
|
20 |
+
]);
|
21 |
+
?>
|
22 |
+
</select>
|
23 |
+
</td>
|
24 |
+
</tr>
|
lib/options/options/conversion-options/destination-folder.inc
CHANGED
@@ -1,15 +1,26 @@
|
|
1 |
-
<?php
|
2 |
-
// Image types
|
3 |
-
// ------------
|
4 |
-
echo '<tr><th scope="row">Destination folder';
|
5 |
-
echo helpIcon('<p>If "In separate folder" is selected, the webp images will be saved to a common root (wp-content/webp-express/webp-images/doc-root), mirroring the folder structure. If on the other hand, "Mingled" is selected, the converter will try to save the webp image in the same folder as the original. If that fails (ie for theme or plugin images), the image will be saved in the separate folder. If you are using WebP Express together with <a target="blank" href="https://da.wordpress.org/plugins/cache-enabler/">Cache enabler</a> or <a target="_blank" href="https://wordpress.org/plugins/shortpixel-image-optimiser/">Shortpixel</a>, set this option to "mingled"</p><p>Changing this option will cause existing webp images to be moved (only those in the upload folder, and only those that has a corresponding source image)</p>');
|
6 |
-
echo '</th><td>';
|
7 |
Â
|
8 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
9 |
Â
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
1 |
Â
|
2 |
+
<tr>
|
3 |
+
<th scope="row">Destination folder<?php
|
4 |
+
echo helpIcon('<p>' .
|
5 |
+
'<span style="text-decoration:underline">Mingled:</span><br>' .
|
6 |
+
'When "Mingled" is selected, the webp images will be put in the same folder as the original <i>but only for images in the uploads folder</i>. ' .
|
7 |
+
'Other images, such as theme images are stored separately.<br>' .
|
8 |
+
'If you are using WebP Express together with <a target="blank" href="https://da.wordpress.org/plugins/cache-enabler/">Cache enabler</a> or ' .
|
9 |
+
'<a target="_blank" href="https://wordpress.org/plugins/shortpixel-image-optimiser/">Shortpixel</a>, choose this option<br><br>' .
|
10 |
Â
|
11 |
+
'<span style="text-decoration:underline">In separate folder:</span><br>' .
|
12 |
+
'Images are stored in a separate folder ' .
|
13 |
+
'(wp-content/webp-express/webp-images/doc-root).' .
|
14 |
+
'<p><i>Note: Changing this option will cause existing webp images to be moved</i></p>');
|
15 |
+
?></th>
|
16 |
+
<td>
|
17 |
+
<select name="destination-folder" id="destination_folder">
|
18 |
+
<?php
|
19 |
+
webpexpress_selectBoxOptions($config['destination-folder'], [
|
20 |
+
'separate' => 'In separate folder',
|
21 |
+
'mingled' => 'Mingled',
|
22 |
+
]);
|
23 |
+
?>
|
24 |
+
</select>
|
25 |
+
</td>
|
26 |
+
</tr>
|
lib/options/options/operation-mode.inc
CHANGED
@@ -2,54 +2,52 @@
|
|
2 |
Â
$operationMode = $config['operation-mode'];
|
3 |
Â
?>
|
4 |
Â
<fieldset class="block">
|
5 |
-
<h3>Operation mode: <?php echo helpIcon(
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
6 |
Â
<input type="hidden" name="operation-mode" id="operation_mode" value="<?php echo $operationMode ?>">
|
7 |
Â
<select name="change-operation-mode" id="change_operation_mode">
|
8 |
-
<option value="
|
9 |
-
<option value="
|
10 |
Â
<option value="just-redirect"<?php if ($operationMode == 'just-redirect') echo ' selected'?>>Just redirect</option>
|
11 |
Â
<option value="tweaked"<?php if ($operationMode == 'tweaked') echo ' selected'?>>Tweaked</option>
|
12 |
Â
</select></h3>
|
13 |
Â
|
14 |
-
<?php if ($config['operation-mode'] == '
|
15 |
Â
<p><div>
|
16 |
-
<i>In
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
which generates the webp. On subsequent requests, the image is redirected (again, internally) directly
|
22 |
-
to the webp, by means of a rule in the .htaccess.</i>
|
23 |
-
</div>
|
24 |
-
</div>
|
25 |
Â
</div></p>
|
26 |
Â
<?php endif; ?>
|
27 |
-
<?php if ($config['operation-mode'] == '
|
28 |
Â
<p><div>
|
29 |
-
<i>In "
|
30 |
-
|
31 |
Â
<div class="help">?<div class="popup">
|
32 |
Â
<p>
|
33 |
-
Benefits of
|
34 |
Â
<ol>
|
35 |
Â
<li>CDN's will not have to be set up to forward the Accept header</li>
|
Â
|
|
36 |
Â
<li>If a user downloads an image, it will not have wrong file extension</li>
|
37 |
Â
</ol>
|
38 |
-
PS:
|
39 |
Â
</p>
|
40 |
Â
<p>
|
41 |
-
PPS: Images refererenced from CSS or added dynamically with javascript will not be served as webp in this mode.
|
42 |
Â
</p>
|
43 |
Â
</div>
|
44 |
Â
</div>
|
45 |
Â
<br><br>
|
46 |
-
<i>
|
47 |
-
<div class="help">?<div class="popup">
|
48 |
-
<em>The auto-convertion works this way: When an image is requested, a rule in the .htaccess detects if
|
49 |
-
that image has been converted. If not, the request is redirected to the converter, which creates the
|
50 |
-
webp and returns <em>the original</em> image</em>
|
51 |
-
</div>
|
52 |
-
</div>
|
53 |
Â
</div></p>
|
54 |
Â
<?php endif; ?>
|
55 |
Â
<?php if ($config['operation-mode'] == 'just-redirect') : ?>
|
2 |
Â
$operationMode = $config['operation-mode'];
|
3 |
Â
?>
|
4 |
Â
<fieldset class="block">
|
5 |
+
<h3>Operation mode: <?php echo helpIcon(
|
6 |
+
'<p>Think of the operation modes as presets that matches normal use-cases. ' .
|
7 |
+
'Usually you want to stick with <i>Varied image responses</i> or perhaps <i>CDN friendly</i>. ' .
|
8 |
+
'The Tweaked mode has no presets. That is: Here, you can set all options.</p>' .
|
9 |
+
'<p>Changing from ie. "Varied image responses" mode to "Tweaked" mode enables you to see the underlying options for that mode (and tweak them). ' .
|
10 |
+
'Changing back will override the tweaks (you will lose them).</p>' .
|
11 |
+
'<p>You will never loose your converter configurations by changing mode</p>');
|
12 |
+
?>
|
13 |
Â
<input type="hidden" name="operation-mode" id="operation_mode" value="<?php echo $operationMode ?>">
|
14 |
Â
<select name="change-operation-mode" id="change_operation_mode">
|
15 |
+
<option value="varied-responses"<?php if ($operationMode == 'varied-responses') echo ' selected'?>>Varied image responses</option>
|
16 |
+
<option value="no-varied-responses"<?php if ($operationMode == 'no-varied-responses') echo ' selected'?>>CDN friendly</option>
|
17 |
Â
<option value="just-redirect"<?php if ($operationMode == 'just-redirect') echo ' selected'?>>Just redirect</option>
|
18 |
Â
<option value="tweaked"<?php if ($operationMode == 'tweaked') echo ' selected'?>>Tweaked</option>
|
19 |
Â
</select></h3>
|
20 |
Â
|
21 |
+
<?php if ($config['operation-mode'] == 'varied-responses') : ?>
|
22 |
Â
<p><div>
|
23 |
+
<i>In the "Varied image responses" mode, WebP Express creates redirection rules for images, such that a request for a jpeg will
|
24 |
+
result in a webp – but only if the request comes from a webp-enabled browser.
|
25 |
+
If a webp already exists, it is served immediately. Otherwise it is converted and then served.
|
26 |
+
Note that not all CDN's handles varied responses well (see FAQ).
|
27 |
+
</i>
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
28 |
Â
</div></p>
|
29 |
Â
<?php endif; ?>
|
30 |
+
<?php if ($config['operation-mode'] == 'no-varied-responses') : ?>
|
31 |
Â
<p><div>
|
32 |
+
<i>In "CDN friendly" mode, a jpeg is always served as a jpeg.
|
33 |
+
Instead of varying the image response, WebP Express alters the HTML for webp usage.</i>
|
34 |
Â
<div class="help">?<div class="popup">
|
35 |
Â
<p>
|
36 |
+
Benefits of not varying image responses:
|
37 |
Â
<ol>
|
38 |
Â
<li>CDN's will not have to be set up to forward the Accept header</li>
|
39 |
+
<li>Better HIT ratio on CDNs, if the CDN had to be configured to vary on the whole Accept header.</li>
|
40 |
Â
<li>If a user downloads an image, it will not have wrong file extension</li>
|
41 |
Â
</ol>
|
42 |
+
PS: This mode also works great with <a target="_blank" href="https://da.wordpress.org/plugins/cache-enabler/">Cache Enabler</a>. Instructions are found in the FAQ.
|
43 |
Â
</p>
|
44 |
Â
<p>
|
45 |
+
PPS: Images refererenced from external CSS or added dynamically with javascript will not be served as webp in this mode.
|
46 |
Â
</p>
|
47 |
Â
</div>
|
48 |
Â
</div>
|
49 |
Â
<br><br>
|
50 |
+
<i>A couple of options are available for automatically triggering webp conversion.</i>
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
51 |
Â
</div></p>
|
52 |
Â
<?php endif; ?>
|
53 |
Â
<?php if ($config['operation-mode'] == 'just-redirect') : ?>
|
lib/options/options/redirection-rules/enable-redirection-to-converter.inc
CHANGED
@@ -1,9 +1,23 @@
|
|
1 |
Â
<tr>
|
2 |
Â
<th scope="row">
|
3 |
-
<?php if ($config['operation-mode'] == '
|
4 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
5 |
Â
<?php else: ?>
|
6 |
-
Enable redirection to converter?<?php echo helpIcon('This will add rules in the .htaccess that redirects to
|
7 |
Â
<?php endif; ?>
|
8 |
Â
</th>
|
9 |
Â
<td>
|
1 |
Â
<tr>
|
2 |
Â
<th scope="row">
|
3 |
+
<?php if ($config['operation-mode'] == 'no-varied-responses'): ?>
|
4 |
+
Convert non-existing webp-files upon request to original image <span style="color:darkorange">(almost obsolete)<?php
|
5 |
+
echo helpIcon(
|
6 |
+
//'<p><em>The auto-convertion works this way: When an image is requested, a rule in the .htaccess detects if that image has been converted. If not, the request is redirected to the converter, which creates the webp and returns <em>the original</em> image</em></p>
|
7 |
+
'<p>This works the following way:' .
|
8 |
+
'<ol>' .
|
9 |
+
'<li>WebP adds rules in the <i>.htaccess</i> that redirects requests for jpg/png images to the converter, <i>when no corresponding webp image is found</i></li>' .
|
10 |
+
'<li>The converter creates the webp image, the jpg/png is served</li>' .
|
11 |
+
'<li>The jpg/png file is served</li>' .
|
12 |
+
'</ol>' .
|
13 |
+
'<p>This only happens once per image. The next time the jpg/png is requested, ' .
|
14 |
+
'the rule will not trigger because it now detects a corresponding webp</p>' .
|
15 |
+
'<p>Note: After the introduction of the <i>Convert non-existing webp-files upon request?</i> option, ' .
|
16 |
+
'you probably will not need this option. There are however rare cases, where it could be useful. ' .
|
17 |
+
'<b>From the next release on on, this option will only be available in the <i>Tweaked</i> operation mode</b></p>'
|
18 |
+
); ?>
|
19 |
Â
<?php else: ?>
|
20 |
+
Enable redirection to converter?<?php echo helpIcon('This will add rules in the .htaccess that redirects images (jpg/png) to the script'); ?>
|
21 |
Â
<?php endif; ?>
|
22 |
Â
</th>
|
23 |
Â
<td>
|
lib/options/options/redirection-rules/enable-redirection-to-webp-realizer.inc
ADDED
@@ -0,0 +1,26 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<tr>
|
2 |
+
<th scope="row">
|
3 |
+
Convert non-existing webp-files upon request <span style="color:darkorange">(beta)</span>
|
4 |
+
<?php echo helpIcon(
|
5 |
+
'<p>Have ie. "image.jpg.webp" automatically generated from "image.jpg" the first time the webp is requested.</p>' .
|
6 |
+
'<p>This works the following way:' .
|
7 |
+
'<ol>' .
|
8 |
+
'<li>WebP adds rules in the <i>.htaccess</i> that redirects requests for non-existing webp files to <i>webp-realizer.php</i></li>' .
|
9 |
+
'<li><i>webp-realizer.php</i> looks for a corresponding jpg/png. If found, it is converted and saved, so the webp will be directly available the next time it is requested – and it tells the browser to fetch the same URL again (302 redirect to same location). In case no corresponding jpg/png is found, a 404 is returned</li>' .
|
10 |
+
'</ol>' .
|
11 |
+
'</p>' .
|
12 |
+
'This only happens once per image. The next time the webp is requested, the rule will not trigger because the webp now exists'
|
13 |
+
// '<p>This feature allows you to reference webp images before they actually exists. You can ie write:' .
|
14 |
+
// "<pre><picture>\n <source srcset=\"image.jpg.webp\" type=\"image/webp\" />\n <img src=\"image.jpg\" /></picture>"
|
15 |
+
|
16 |
+
); ?>
|
17 |
+
</th>
|
18 |
+
<td>
|
19 |
+
<input
|
20 |
+
name="enable-redirection-to-webp-realizer"
|
21 |
+
<?php echo ($config['enable-redirection-to-webp-realizer'] ? 'checked="checked"' : '') ?>
|
22 |
+
value="true"
|
23 |
+
type="checkbox"
|
24 |
+
>
|
25 |
+
</td>
|
26 |
+
</tr>
|
lib/options/options/redirection-rules/image-types.inc
CHANGED
@@ -3,10 +3,10 @@
|
|
3 |
Â
// ------------
|
4 |
Â
echo '<tr><th scope="row">';
|
5 |
Â
switch ($config['operation-mode']) {
|
6 |
-
case '
|
7 |
Â
echo 'Image types to work on';
|
8 |
Â
break;
|
9 |
-
case '
|
10 |
Â
echo 'Image types to convert';
|
11 |
Â
break;
|
12 |
Â
case 'just-redirect':
|
3 |
Â
// ------------
|
4 |
Â
echo '<tr><th scope="row">';
|
5 |
Â
switch ($config['operation-mode']) {
|
6 |
+
case 'varied-responses':
|
7 |
Â
echo 'Image types to work on';
|
8 |
Â
break;
|
9 |
+
case 'no-varied-responses':
|
10 |
Â
echo 'Image types to convert';
|
11 |
Â
break;
|
12 |
Â
case 'just-redirect':
|
lib/options/options/redirection-rules/only-redirect-to-converter-on-cache-miss.inc
CHANGED
@@ -1,6 +1,14 @@
|
|
1 |
Â
<tr>
|
2 |
Â
<th scope="row">
|
3 |
-
Only redirect to converter if no webp
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
4 |
Â
</th>
|
5 |
Â
<td>
|
6 |
Â
<input type="checkbox" name="only-redirect-to-converter-on-cache-miss" value="true" <?php echo ($config['only-redirect-to-converter-on-cache-miss'] ? 'checked="checked"' : '') ?> >
|
1 |
Â
<tr>
|
2 |
Â
<th scope="row">
|
3 |
+
Only redirect to converter if no webp exists <?php
|
4 |
+
echo helpIcon(
|
5 |
+
'<p>This extra condition is not needed if you enabled the ' .
|
6 |
+
'<i>Redirect directly to converted image when available</i> option.</p>' .
|
7 |
+
'<p>The option was created in order to make it possible to achieve the functionality behind the ' .
|
8 |
+
'<i>Convert non-existing webp-files upon request to original image?</i> option found in the ' .
|
9 |
+
'"CDN friendly" operation mode.</p>'
|
10 |
+
);
|
11 |
+
?>
|
12 |
Â
</th>
|
13 |
Â
<td>
|
14 |
Â
<input type="checkbox" name="only-redirect-to-converter-on-cache-miss" value="true" <?php echo ($config['only-redirect-to-converter-on-cache-miss'] ? 'checked="checked"' : '') ?> >
|
lib/options/options/redirection-rules/redirection-rules.inc
CHANGED
@@ -11,13 +11,14 @@
|
|
11 |
Â
include_once 'do-not-pass-source-path-in-query-string.inc';
|
12 |
Â
include_once 'only-redirect-to-converter-on-cache-miss.inc';
|
13 |
Â
include_once 'redirect-to-existing.inc';
|
Â
|
|
14 |
Â
?>
|
15 |
Â
</tbody>
|
16 |
Â
</table>
|
17 |
Â
</fieldset>
|
18 |
Â
<?php
|
19 |
Â
else:
|
20 |
-
if ($config['operation-mode'] == '
|
21 |
Â
// ps: we call it "auto convert", when in this mode
|
22 |
Â
// PPS: we include it directly in page.php now.
|
23 |
Â
//include_once 'enable-redirection-to-converter.inc';
|
@@ -25,8 +26,9 @@ else:
|
|
25 |
Â
//include_once 'only-redirect-to-converter-on-cache-miss.inc';
|
26 |
Â
|
27 |
Â
}
|
28 |
-
if ($config['operation-mode'] == '
|
29 |
Â
include_once 'redirect-to-existing.inc';
|
Â
|
|
30 |
Â
}
|
31 |
Â
include_once 'image-types.inc';
|
32 |
Â
endif;
|
11 |
Â
include_once 'do-not-pass-source-path-in-query-string.inc';
|
12 |
Â
include_once 'only-redirect-to-converter-on-cache-miss.inc';
|
13 |
Â
include_once 'redirect-to-existing.inc';
|
14 |
+
include_once 'enable-redirection-to-webp-realizer.inc';
|
15 |
Â
?>
|
16 |
Â
</tbody>
|
17 |
Â
</table>
|
18 |
Â
</fieldset>
|
19 |
Â
<?php
|
20 |
Â
else:
|
21 |
+
if ($config['operation-mode'] == 'no-varied-responses') {
|
22 |
Â
// ps: we call it "auto convert", when in this mode
|
23 |
Â
// PPS: we include it directly in page.php now.
|
24 |
Â
//include_once 'enable-redirection-to-converter.inc';
|
26 |
Â
//include_once 'only-redirect-to-converter-on-cache-miss.inc';
|
27 |
Â
|
28 |
Â
}
|
29 |
+
if ($config['operation-mode'] == 'varied-responses') {
|
30 |
Â
include_once 'redirect-to-existing.inc';
|
31 |
+
//include_once 'enable-redirection-to-webp-realizer.inc';
|
32 |
Â
}
|
33 |
Â
include_once 'image-types.inc';
|
34 |
Â
endif;
|
lib/options/options/serve-options/serve-options.inc
CHANGED
@@ -14,7 +14,7 @@
|
|
14 |
Â
</fieldset>
|
15 |
Â
<?php
|
16 |
Â
else:
|
17 |
-
if ($config['operation-mode'] != '
|
18 |
Â
include_once 'cache-control.inc';
|
19 |
Â
}
|
20 |
Â
endif;
|
14 |
Â
</fieldset>
|
15 |
Â
<?php
|
16 |
Â
else:
|
17 |
+
if ($config['operation-mode'] != 'no-varied-responses') {
|
18 |
Â
include_once 'cache-control.inc';
|
19 |
Â
}
|
20 |
Â
endif;
|
lib/options/page-messages.php
CHANGED
@@ -40,7 +40,7 @@ if (!Paths::createContentDirIfMissing()) {
|
|
40 |
Â
Messenger::printMessage(
|
41 |
Â
'error',
|
42 |
Â
'WebP Express needs to create a directory "webp-express" under your wp-content folder, but does not have permission to do so.<br>' .
|
43 |
-
'Please create the folder manually, or change the file permissions of your wp-content folder (failed to create this folder: ' . Paths::
|
44 |
Â
);
|
45 |
Â
} else {
|
46 |
Â
if (!Paths::createConfigDirIfMissing()) {
|
@@ -79,7 +79,7 @@ if (Config::isConfigFileThere()) {
|
|
79 |
Â
|
80 |
Â
if (
|
81 |
Â
HTAccess::haveWeRulesInThisHTAccessBestGuess(Paths::getIndexDirAbs() . '/.htaccess') &&
|
82 |
-
HTAccess::haveWeRulesInThisHTAccessBestGuess(Paths::
|
83 |
Â
) {
|
84 |
Â
if (!HTAccess::saveHTAccessRulesToFile(Paths::getIndexDirAbs() . '/.htaccess', '# WebP Express has placed its rules in your wp-content dir. Go there.', false)) {
|
85 |
Â
Messenger::printMessage(
|
40 |
Â
Messenger::printMessage(
|
41 |
Â
'error',
|
42 |
Â
'WebP Express needs to create a directory "webp-express" under your wp-content folder, but does not have permission to do so.<br>' .
|
43 |
+
'Please create the folder manually, or change the file permissions of your wp-content folder (failed to create this folder: ' . Paths::getWebPExpressContentDirAbs() . ')'
|
44 |
Â
);
|
45 |
Â
} else {
|
46 |
Â
if (!Paths::createConfigDirIfMissing()) {
|
79 |
Â
|
80 |
Â
if (
|
81 |
Â
HTAccess::haveWeRulesInThisHTAccessBestGuess(Paths::getIndexDirAbs() . '/.htaccess') &&
|
82 |
+
HTAccess::haveWeRulesInThisHTAccessBestGuess(Paths::getContentDirAbs() . '/.htaccess')
|
83 |
Â
) {
|
84 |
Â
if (!HTAccess::saveHTAccessRulesToFile(Paths::getIndexDirAbs() . '/.htaccess', '# WebP Express has placed its rules in your wp-content dir. Go there.', false)) {
|
85 |
Â
Messenger::printMessage(
|
lib/options/page-welcome.php
CHANGED
@@ -9,7 +9,7 @@ use \WebPExpress\PlatformInfo;
|
|
9 |
Â
|
10 |
Â
$indexDir = Paths::getIndexDirAbs();
|
11 |
Â
$homeDir = Paths::getHomeDirAbs();
|
12 |
-
$wpContentDir = Paths::
|
13 |
Â
$pluginDir = Paths::getPluginDirAbs();
|
14 |
Â
$uploadDir = Paths::getUploadDirAbs();
|
15 |
Â
|
9 |
Â
|
10 |
Â
$indexDir = Paths::getIndexDirAbs();
|
11 |
Â
$homeDir = Paths::getHomeDirAbs();
|
12 |
+
$wpContentDir = Paths::getContentDirAbs();
|
13 |
Â
$pluginDir = Paths::getPluginDirAbs();
|
14 |
Â
$uploadDir = Paths::getUploadDirAbs();
|
15 |
Â
|
lib/options/page.php
CHANGED
@@ -96,6 +96,9 @@ $webpexpress_settings_nonce = wp_create_nonce('webpexpress_settings_nonce');
|
|
96 |
Â
?>
|
97 |
Â
|
98 |
Â
<?php
|
Â
|
|
Â
|
|
Â
|
|
99 |
Â
|
100 |
Â
echo '<form id="webpexpress_settings" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" method="post" >';
|
101 |
Â
?>
|
@@ -118,7 +121,11 @@ function helpIcon($text, $customClass = '') {
|
|
118 |
Â
}
|
119 |
Â
if (strlen($text) > 150) {
|
120 |
Â
if (strlen($text) > 300) {
|
121 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
122 |
Â
} else {
|
123 |
Â
$className = 'wide';
|
124 |
Â
}
|
@@ -134,6 +141,46 @@ function webpexpress_selectBoxOptions($selected, $options) {
|
|
134 |
Â
}
|
135 |
Â
}
|
136 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
137 |
Â
include_once 'options/operation-mode.inc';
|
138 |
Â
|
139 |
Â
if ($config['operation-mode'] != 'tweaked') {
|
@@ -150,11 +197,22 @@ if ($config['operation-mode'] == 'just-redirect') {
|
|
150 |
Â
}
|
151 |
Â
include_once 'options/serve-options/serve-options.inc';
|
152 |
Â
|
153 |
-
if ($config['operation-mode']
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
154 |
Â
// ps: we call it "auto convert", when in this mode
|
155 |
Â
include_once 'options/redirection-rules/enable-redirection-to-converter.inc';
|
156 |
Â
}
|
157 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
158 |
Â
if ($config['operation-mode'] != 'just-redirect') {
|
159 |
Â
include_once 'options/web-service-options/web-service-options.inc';
|
160 |
Â
}
|
96 |
Â
?>
|
97 |
Â
|
98 |
Â
<?php
|
99 |
+
//echo get_theme_root_uri();
|
100 |
+
|
101 |
+
include_once __DIR__ . '/../classes/AlterHtmlHelper.php';
|
102 |
Â
|
103 |
Â
echo '<form id="webpexpress_settings" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" method="post" >';
|
104 |
Â
?>
|
121 |
Â
}
|
122 |
Â
if (strlen($text) > 150) {
|
123 |
Â
if (strlen($text) > 300) {
|
124 |
+
if (strlen($text) > 500) {
|
125 |
+
$className = 'widest';
|
126 |
+
} else {
|
127 |
+
$className = 'wider';
|
128 |
+
}
|
129 |
Â
} else {
|
130 |
Â
$className = 'wide';
|
131 |
Â
}
|
141 |
Â
}
|
142 |
Â
}
|
143 |
Â
|
144 |
+
function webpexpress_radioButtons($optionName, $selected, $options, $helpTexts = []) {
|
145 |
+
echo '<ul style="margin-left: 20px; margin-top: 5px">';
|
146 |
+
foreach ($options as $optionValue => $text) {
|
147 |
+
$id = str_replace('-', '_', $optionName . '_' . $optionValue);
|
148 |
+
echo '<li>';
|
149 |
+
echo '<input type="radio" id="' . $id . '"';
|
150 |
+
if ($optionValue == $selected) {
|
151 |
+
echo ' checked="checked"';
|
152 |
+
}
|
153 |
+
echo ' name="' . $optionName . '" value="' . $optionValue . '" style="margin-right: 10px">';
|
154 |
+
echo '<label for="' . $id . '">';
|
155 |
+
echo $text;
|
156 |
+
if (isset($helpTexts[$optionValue])) {
|
157 |
+
echo helpIcon($helpTexts[$optionValue]);
|
158 |
+
}
|
159 |
+
echo '</label>';
|
160 |
+
echo '</li>';
|
161 |
+
}
|
162 |
+
echo '</ul>';
|
163 |
+
}
|
164 |
+
|
165 |
+
function webpexpress_checkbox($optionName, $checked, $label, $helpText = '') {
|
166 |
+
$id = str_replace('-', '_', $optionName);
|
167 |
+
echo '<div style="margin:10px 0 0 10px;">';
|
168 |
+
echo '<input value="true" type="checkbox" style="margin-right: 10px" ';
|
169 |
+
echo 'name="' . $optionName . '"';
|
170 |
+
echo 'id="' . $id . '"';
|
171 |
+
if ($checked) {
|
172 |
+
echo ' checked="checked"';
|
173 |
+
}
|
174 |
+
echo '>';
|
175 |
+
echo '<label for="' . $id . '">';
|
176 |
+
echo $label . '</label>';
|
177 |
+
if ($helpText != '') {
|
178 |
+
echo helpIcon($helpText);
|
179 |
+
}
|
180 |
+
echo '</div>';
|
181 |
+
|
182 |
+
}
|
183 |
+
|
184 |
Â
include_once 'options/operation-mode.inc';
|
185 |
Â
|
186 |
Â
if ($config['operation-mode'] != 'tweaked') {
|
197 |
Â
}
|
198 |
Â
include_once 'options/serve-options/serve-options.inc';
|
199 |
Â
|
200 |
+
if ($config['operation-mode'] != 'just-redirect') {
|
201 |
+
include_once 'options/alter-html/alter-html.inc';
|
202 |
+
}
|
203 |
+
|
204 |
+
if ($config['operation-mode'] == 'no-varied-responses') {
|
205 |
+
include_once 'options/redirection-rules/enable-redirection-to-webp-realizer.inc';
|
206 |
+
|
207 |
Â
// ps: we call it "auto convert", when in this mode
|
208 |
Â
include_once 'options/redirection-rules/enable-redirection-to-converter.inc';
|
209 |
Â
}
|
210 |
Â
|
211 |
+
if ($config['operation-mode'] == 'varied-responses') {
|
212 |
+
include_once 'options/redirection-rules/enable-redirection-to-webp-realizer.inc';
|
213 |
+
}
|
214 |
+
|
215 |
+
|
216 |
Â
if ($config['operation-mode'] != 'just-redirect') {
|
217 |
Â
include_once 'options/web-service-options/web-service-options.inc';
|
218 |
Â
}
|
lib/options/submit.php
CHANGED
@@ -37,27 +37,34 @@ $config = array_merge($config, [
|
|
37 |
Â
'image-types' => sanitize_text_field($_POST['image-types']),
|
38 |
Â
'forward-query-string' => true,
|
39 |
Â
|
40 |
-
// serve options
|
41 |
-
'cache-control' => sanitize_text_field($_POST['cache-control']),
|
42 |
-
'cache-control-custom' => sanitize_text_field($_POST['cache-control-custom']),
|
43 |
Â
]);
|
44 |
Â
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
56 |
Â
}
|
57 |
Â
|
Â
|
|
58 |
Â
// Set options that are available in all operation modes, except the "just-redirect" mode
|
59 |
Â
if ($_POST['operation-mode'] != 'just-redirect') {
|
60 |
Â
|
Â
|
|
Â
|
|
Â
|
|
61 |
Â
// Metadata
|
62 |
Â
// --------
|
63 |
Â
$config['metadata'] = sanitize_text_field($_POST['metadata']);
|
@@ -135,17 +142,34 @@ if ($_POST['operation-mode'] != 'just-redirect') {
|
|
135 |
Â
}
|
136 |
Â
}
|
137 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
138 |
Â
}
|
139 |
Â
|
140 |
Â
switch ($_POST['operation-mode']) {
|
141 |
-
case '
|
142 |
Â
$config = array_merge($config, [
|
143 |
Â
'redirect-to-existing-in-htaccess' => isset($_POST['redirect-to-existing-in-htaccess']),
|
Â
|
|
Â
|
|
144 |
Â
]);
|
145 |
Â
break;
|
146 |
-
case '
|
147 |
Â
$config = array_merge($config, [
|
148 |
-
'destination-
|
Â
|
|
149 |
Â
'enable-redirection-to-converter' => isset($_POST['enable-redirection-to-converter']), // PS: its called "autoconvert" in this mode
|
150 |
Â
]);
|
151 |
Â
break;
|
37 |
Â
'image-types' => sanitize_text_field($_POST['image-types']),
|
38 |
Â
'forward-query-string' => true,
|
39 |
Â
|
Â
|
|
Â
|
|
Â
|
|
40 |
Â
]);
|
41 |
Â
|
42 |
+
// Set options that are available in all operation modes, except the "CDN friendly" mode
|
43 |
+
if ($_POST['operation-mode'] != 'no-varied-responses') {
|
44 |
+
|
45 |
+
$cacheControl = sanitize_text_field($_POST['cache-control']);
|
46 |
+
$config['cache-control'] = $cacheControl;
|
47 |
+
|
48 |
+
switch ($cacheControl) {
|
49 |
+
case 'no-header':
|
50 |
+
break;
|
51 |
+
case 'set':
|
52 |
+
$config['cache-control-max-age'] = sanitize_text_field($_POST['cache-control-max-age']);
|
53 |
+
$config['cache-control-public'] = (sanitize_text_field($_POST['cache-control-public']) == 'public');
|
54 |
+
break;
|
55 |
+
case 'custom':
|
56 |
+
$config['cache-control-custom'] = sanitize_text_field($_POST['cache-control-custom']);
|
57 |
+
break;
|
58 |
+
}
|
59 |
Â
}
|
60 |
Â
|
61 |
+
|
62 |
Â
// Set options that are available in all operation modes, except the "just-redirect" mode
|
63 |
Â
if ($_POST['operation-mode'] != 'just-redirect') {
|
64 |
Â
|
65 |
+
$config['enable-redirection-to-webp-realizer'] = isset($_POST['enable-redirection-to-webp-realizer']);
|
66 |
+
|
67 |
+
|
68 |
Â
// Metadata
|
69 |
Â
// --------
|
70 |
Â
$config['metadata'] = sanitize_text_field($_POST['metadata']);
|
142 |
Â
}
|
143 |
Â
}
|
144 |
Â
}
|
145 |
+
|
146 |
+
// Alter HTML
|
147 |
+
|
148 |
+
$config['alter-html'] = [];
|
149 |
+
$config['alter-html']['enabled'] = isset($_POST['alter-html-enabled']);
|
150 |
+
if ($_POST['alter-html-replacement'] == 'url') {
|
151 |
+
$config['alter-html']['only-for-webp-enabled-browsers'] = isset($_POST['alter-html-only-for-webp-enabled-browsers']);
|
152 |
+
} else {
|
153 |
+
$config['alter-html']['only-for-webp-enabled-browsers'] = false;
|
154 |
+
}
|
155 |
+
$config['alter-html']['only-for-webps-that-exists'] = (!isset($_POST['alter-html-for-webps-that-has-yet-to-exist']));
|
156 |
+
$config['alter-html']['replacement'] = $_POST['alter-html-replacement'];
|
157 |
+
$config['alter-html']['hooks'] = $_POST['alter-html-hooks'];
|
158 |
+
|
159 |
Â
}
|
160 |
Â
|
161 |
Â
switch ($_POST['operation-mode']) {
|
162 |
+
case 'varied-responses':
|
163 |
Â
$config = array_merge($config, [
|
164 |
Â
'redirect-to-existing-in-htaccess' => isset($_POST['redirect-to-existing-in-htaccess']),
|
165 |
+
'destination-folder' => $_POST['destination-folder'],
|
166 |
+
'destination-extension' => (($_POST['destination-folder'] == 'mingled') ? $_POST['destination-extension'] : 'append'),
|
167 |
Â
]);
|
168 |
Â
break;
|
169 |
+
case 'no-varied-responses':
|
170 |
Â
$config = array_merge($config, [
|
171 |
+
'destination-folder' => $_POST['destination-folder'],
|
172 |
+
'destination-extension' => (($_POST['destination-folder'] == 'mingled') ? $_POST['destination-extension'] : 'append'),
|
173 |
Â
'enable-redirection-to-converter' => isset($_POST['enable-redirection-to-converter']), // PS: its called "autoconvert" in this mode
|
174 |
Â
]);
|
175 |
Â
break;
|
lib/uninstall.php
CHANGED
@@ -36,4 +36,4 @@ foreach ($optionsToDelete as $i => $optionName) {
|
|
36 |
Â
}
|
37 |
Â
|
38 |
Â
// remove content dir (config plus images)
|
39 |
-
webpexpress_rrmdir(Paths::
|
36 |
Â
}
|
37 |
Â
|
38 |
Â
// remove content dir (config plus images)
|
39 |
+
webpexpress_rrmdir(Paths::getWebPExpressContentDirAbs());
|
test/very-small.jpg
ADDED
Binary file
|
tests/BananaTest.php
ADDED
@@ -0,0 +1,24 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* WebPConvert - Convert JPEG & PNG to WebP with PHP
|
5 |
+
*
|
6 |
+
* @link https://github.com/rosell-dk/webp-convert
|
7 |
+
* @license MIT
|
8 |
+
*/
|
9 |
+
|
10 |
+
namespace WebPExpressTests;
|
11 |
+
|
12 |
+
//use WebPConvert\WebPConvert;
|
13 |
+
use PHPUnit\Framework\TestCase;
|
14 |
+
use Sunra\PhpSimple\HtmlDomParser;
|
15 |
+
|
16 |
+
class BananaTest extends TestCase
|
17 |
+
{
|
18 |
+
|
19 |
+
public function testPotatoPotato()
|
20 |
+
{
|
21 |
+
$this->assertEquals('potato', 'potato');
|
22 |
+
}
|
23 |
+
|
24 |
+
}
|
webp-express.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Â
* Plugin Name: WebP Express
|
4 |
Â
* Plugin URI: https://github.com/rosell-dk/webp-express
|
5 |
Â
* Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
|
6 |
-
* Version: 0.
|
7 |
Â
* Author: Bjørn Rosell
|
8 |
Â
* Author URI: https://www.bitwise-it.dk
|
9 |
Â
* License: GPL2
|
@@ -36,4 +36,10 @@ function webp_express_process_post() {
|
|
36 |
Â
}
|
37 |
Â
add_action( 'init', 'webp_express_process_post' );
|
38 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
39 |
Â
//add_action( 'template_redirect', 'webp_express_template_redirect' );
|
3 |
Â
* Plugin Name: WebP Express
|
4 |
Â
* Plugin URI: https://github.com/rosell-dk/webp-express
|
5 |
Â
* Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc).
|
6 |
+
* Version: 0.11.0
|
7 |
Â
* Author: Bjørn Rosell
|
8 |
Â
* Author URI: https://www.bitwise-it.dk
|
9 |
Â
* License: GPL2
|
36 |
Â
}
|
37 |
Â
add_action( 'init', 'webp_express_process_post' );
|
38 |
Â
|
39 |
+
if (get_option('webp-express-alter-html', false)) {
|
40 |
+
require_once __DIR__ . '/lib/classes/AlterHtmlInit.php';
|
41 |
+
\WebPExpress\AlterHtmlInit::setHooks();
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
Â
//add_action( 'template_redirect', 'webp_express_template_redirect' );
|
wod/.htaccess
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
<FilesMatch "webp-on-demand\.php$">
|
2 |
Â
<IfModule !mod_authz_core.c>
|
3 |
Â
Order deny,allow
|
4 |
Â
Allow from all
|
1 |
+
<FilesMatch "(webp-on-demand|webp-realizer)\.php$">
|
2 |
Â
<IfModule !mod_authz_core.c>
|
3 |
Â
Order deny,allow
|
4 |
Â
Allow from all
|
wod/.webp
DELETED
Binary file
|
wod/webp-on-demand.php
CHANGED
@@ -87,34 +87,48 @@ $allowInHeader = true; // todo: implement setting
|
|
87 |
Â
$source = getSource($allowInQS, $allowInHeader);
|
88 |
Â
//$source = getSource(false, false);
|
89 |
Â
|
Â
|
|
Â
|
|
90 |
Â
if (!file_exists($source)) {
|
91 |
Â
header('X-WebP-Express-Error: Source file not found!', true);
|
92 |
Â
echo 'Source file not found!';
|
93 |
Â
exit;
|
94 |
Â
}
|
95 |
Â
|
96 |
-
//
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
97 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
98 |
Â
|
Â
|
|
Â
|
|
99 |
Â
|
100 |
-
|
101 |
-
//
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
// Test if source folder is writable.
|
106 |
-
// We will only store "mingled", if it is.
|
107 |
-
$sourceFolder = preg_replace('/\\/[^\\/]*$/', '', $source);
|
108 |
-
if (@is_writable($sourceFolder) && @is_executable($sourceFolder)) {
|
109 |
-
$storeMingled = true;
|
110 |
-
} else {
|
111 |
-
header('X-WebP-Express-Notice: Cannot save file in same directory as source, falling back to separate folder', true);
|
112 |
-
if (isset($_GET['debug'])) {
|
113 |
-
echo 'Notice: Cannot save file in same directory as source, falling back to separate folder<br><br>';
|
114 |
-
}
|
115 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
116 |
Â
}
|
117 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
118 |
Â
if (isset($options['destination-extension']) && ($options['destination-extension'] == 'append')) {
|
119 |
Â
$destination = $source . '.webp';
|
120 |
Â
} else {
|
87 |
Â
$source = getSource($allowInQS, $allowInHeader);
|
88 |
Â
//$source = getSource(false, false);
|
89 |
Â
|
90 |
+
//echo $source; exit;
|
91 |
+
|
92 |
Â
if (!file_exists($source)) {
|
93 |
Â
header('X-WebP-Express-Error: Source file not found!', true);
|
94 |
Â
echo 'Source file not found!';
|
95 |
Â
exit;
|
96 |
Â
}
|
97 |
Â
|
98 |
+
// Determine if we should store mingled or not
|
99 |
+
function storeMingled() {
|
100 |
+
global $options;
|
101 |
+
global $source;
|
102 |
+
global $docRoot;
|
103 |
Â
|
104 |
+
$destinationOptionSetToMingled = (isset($options['destination-folder']) && ($options['destination-folder'] == 'mingled'));
|
105 |
+
if (!$destinationOptionSetToMingled) {
|
106 |
+
return false;
|
107 |
+
}
|
108 |
Â
|
109 |
+
// Option is set for mingled.
|
110 |
+
// But we will only store "mingled", for images in upload folder
|
111 |
Â
|
112 |
+
if (!isset($options['paths']['uploadDirRel'])) {
|
113 |
+
// Hm, we dont know the upload dir, as the configuration hasn't been regenerated.
|
114 |
+
// This should not happen because configuration file is saved upon migration to 0.11
|
115 |
+
// So we can do this wild guess:
|
116 |
+
return preg_match('/\\/uploads\\//', $source);
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
117 |
Â
}
|
118 |
+
|
119 |
+
$uploadDirAbs = $docRoot . '/' . $options['paths']['uploadDirRel'];
|
120 |
+
if (strpos($source, $uploadDirAbs) === 0) {
|
121 |
+
// We are in upload folder
|
122 |
+
return true;
|
123 |
+
}
|
124 |
+
return false;
|
125 |
Â
}
|
126 |
+
|
127 |
+
|
128 |
+
// Calculate $destination
|
129 |
+
// ----------------------
|
130 |
+
|
131 |
+
if (storeMingled($options)) {
|
132 |
Â
if (isset($options['destination-extension']) && ($options['destination-extension'] == 'append')) {
|
133 |
Â
$destination = $source . '.webp';
|
134 |
Â
} else {
|
wod/webp-realizer.php
ADDED
@@ -0,0 +1,215 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// https://www.askapache.com/htaccess/crazy-advanced-mod_rewrite-tutorial/#Decoding_Mod_Rewrite_Variables
|
3 |
+
|
4 |
+
ini_set('display_errors', 1);
|
5 |
+
error_reporting(E_ALL);
|
6 |
+
|
7 |
+
//echo 'display errors:' . ini_get('display_errors');
|
8 |
+
//exit;
|
9 |
+
|
10 |
+
//require 'webp-on-demand-1.inc';
|
11 |
+
//require '../vendor/autoload.php';
|
12 |
+
|
13 |
+
//print_r($_GET); exit;
|
14 |
+
|
15 |
+
use \WebPConvert\WebPConvert;
|
16 |
+
use \WebPConvert\ServeExistingOrHandOver;
|
17 |
+
|
18 |
+
function loadConfig($configFilename) {
|
19 |
+
if (!file_exists($configFilename)) {
|
20 |
+
header('X-WebP-Express-Error: Configuration file not found!', true);
|
21 |
+
echo 'Configuration file not found!';
|
22 |
+
//WebPConvert::convertAndServe($source, $destination, []);
|
23 |
+
exit;
|
24 |
+
}
|
25 |
+
|
26 |
+
// TODO: Handle read error / json error
|
27 |
+
$handle = @fopen($configFilename, "r");
|
28 |
+
$json = fread($handle, filesize($configFilename));
|
29 |
+
fclose($handle);
|
30 |
+
return json_decode($json, true);
|
31 |
+
}
|
32 |
+
|
33 |
+
function getDestinationRealPath($dest) {
|
34 |
+
//echo $_SERVER["DOCUMENT_ROOT"] . '<br>' . $dest . '<br>';
|
35 |
+
if (strpos($dest, $_SERVER["DOCUMENT_ROOT"]) === 0) {
|
36 |
+
return realpath($_SERVER["DOCUMENT_ROOT"]) . substr($dest, strlen($_SERVER["DOCUMENT_ROOT"]));
|
37 |
+
} else {
|
38 |
+
return $dest;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
function getDestination($allowInQS, $allowInHeader) {
|
43 |
+
// First check if it is in an environment variable - thats the safest way
|
44 |
+
foreach ($_SERVER as $key => $item) {
|
45 |
+
if (substr($key, -14) == 'REDIRECT_REQFN') {
|
46 |
+
return getDestinationRealPath($item);
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
if ($allowInHeader) {
|
51 |
+
if (isset($_SERVER['HTTP_REQFN'])) {
|
52 |
+
//echo 'dest:' . $_SERVER['HTTP_REQFN'];
|
53 |
+
return getDestinationRealPath($_SERVER['HTTP_REQFN']);
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
// Last resort is to use $_SERVER['REQUEST_URI'], well knowing that it does not give the
|
58 |
+
// correct result in all setups (ie "folder method 1")
|
59 |
+
$requestUriNoQS = explode('?', $_SERVER['REQUEST_URI'])[0];
|
60 |
+
$docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
|
61 |
+
$source = $docRoot . urldecode($requestUriNoQS);
|
62 |
+
if (@file_exists($source)) {
|
63 |
+
return $source;
|
64 |
+
}
|
65 |
+
|
66 |
+
header('X-WebP-Express-Error: None of the available methods for locating destination file works', true);
|
67 |
+
echo 'None of the available methods for locating destination file works!';
|
68 |
+
if (!$allowInHeader) {
|
69 |
+
echo '<br>Have you tried allowing destination to be passed as a request header?';
|
70 |
+
}
|
71 |
+
if (!$allowInQS) {
|
72 |
+
echo '<br>Have you tried allowing destination to be passed in querystring?';
|
73 |
+
}
|
74 |
+
exit;
|
75 |
+
}
|
76 |
+
|
77 |
+
$docRoot = rtrim(realpath($_SERVER["DOCUMENT_ROOT"]), '/');
|
78 |
+
$wpContentDirRel = (isset($_GET['wp-content']) ? $_GET['wp-content'] : 'wp-content');
|
79 |
+
$webExpressContentDirRel = $wpContentDirRel . '/webp-express';
|
80 |
+
$webExpressContentDirAbs = $docRoot . '/' . $webExpressContentDirRel;
|
81 |
+
$configFilename = $webExpressContentDirAbs . '/config/wod-options.json';
|
82 |
+
|
83 |
+
$options = loadConfig($configFilename);
|
84 |
+
|
85 |
+
$allowInQS = !(isset($options['do-not-pass-source-in-query-string']) && $options['do-not-pass-source-in-query-string']);
|
86 |
+
$allowInHeader = true; // todo: implement setting
|
87 |
+
$destination = getDestination($allowInQS, $allowInHeader);
|
88 |
+
//$destination = getDestination(false, false);
|
89 |
+
|
90 |
+
//echo $destination; exit;
|
91 |
+
|
92 |
+
// Try to find source in same folder.
|
93 |
+
// Return false on failure
|
94 |
+
function findSourceMingled() {
|
95 |
+
global $options;
|
96 |
+
global $destination;
|
97 |
+
if (isset($options['destination-extension']) && ($options['destination-extension'] == 'append')) {
|
98 |
+
$source = preg_replace('/\\.(webp)$/', '', $destination);
|
99 |
+
} else {
|
100 |
+
$source = preg_replace('/\\.webp$/', '.jpg', $destination);
|
101 |
+
if (!@file_exists($source)) {
|
102 |
+
$source = preg_replace('/\\.webp$/', '.jpeg', $destination);
|
103 |
+
}
|
104 |
+
if (!@file_exists($source)) {
|
105 |
+
$source = preg_replace('/\\.webp$/', '.png', $destination);
|
106 |
+
}
|
107 |
+
}
|
108 |
+
if (!@file_exists($source)) {
|
109 |
+
return false;
|
110 |
+
}
|
111 |
+
return $source;
|
112 |
+
}
|
113 |
+
|
114 |
+
function findSourceSeparate() {
|
115 |
+
global $options;
|
116 |
+
global $destination;
|
117 |
+
global $webExpressContentDirAbs;
|
118 |
+
global $docRoot;
|
119 |
+
|
120 |
+
$imageRoot = $webExpressContentDirAbs . '/webp-images';
|
121 |
+
|
122 |
+
// Check if destination is residing inside "doc-root" folder
|
123 |
+
if (strpos($destination, $imageRoot . '/doc-root/') === 0) {
|
124 |
+
|
125 |
+
$imageRoot .= '/doc-root';
|
126 |
+
// "Eat" the left part off the $destination parameter. $destination is for example:
|
127 |
+
// "/var/www/webp-express-tests/we0/wp-content-moved/webp-express/webp-images/doc-root/wordpress/uploads-moved/2018/12/tegning5-300x265.jpg.webp"
|
128 |
+
// We also eat the slash (+1)
|
129 |
+
$sourceRel = substr($destination, strlen($imageRoot) + 1);
|
130 |
+
|
131 |
+
$source = $docRoot . '/' . $sourceRel;
|
132 |
+
$source = preg_replace('/\\.(webp)$/', '', $source);
|
133 |
+
} else {
|
134 |
+
$imageRoot .= '/abs';
|
135 |
+
$sourceRel = substr($destination, strlen($imageRoot) + 1);
|
136 |
+
$source = $sourceRel;
|
137 |
+
$source = preg_replace('/\\.(webp)$/', '', $source);
|
138 |
+
}
|
139 |
+
|
140 |
+
if (!@file_exists($source)) {
|
141 |
+
return false;
|
142 |
+
}
|
143 |
+
return $source;
|
144 |
+
}
|
145 |
+
|
146 |
+
|
147 |
+
$mingled = (isset($options['destination-folder']) && ($options['destination-folder'] == 'mingled'));
|
148 |
+
|
149 |
+
if ($mingled) {
|
150 |
+
$source = findSourceMingled();
|
151 |
+
if ($source === false) {
|
152 |
+
$source = findSourceSeparate();
|
153 |
+
}
|
154 |
+
} else {
|
155 |
+
$source = findSourceSeparate();
|
156 |
+
}
|
157 |
+
|
158 |
+
if ($source === false) {
|
159 |
+
header('X-WebP-Express-Error: webp-realizer.php could not find an existing jpg or png that corresponds to the webp requested', true);
|
160 |
+
|
161 |
+
$protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
|
162 |
+
header($protocol . " 404 Not Found");
|
163 |
+
//echo '<p>webp-realizer.php could not find an existing jpg or png that corresponds to the webp requested!</p>';
|
164 |
+
//echo 'destination requested:<br><i>' . $destination . '</i>';
|
165 |
+
}
|
166 |
+
|
167 |
+
|
168 |
+
|
169 |
+
|
170 |
+
//echo $destination; exit;
|
171 |
+
|
172 |
+
|
173 |
+
//echo '<pre>' . print_r($options, true) . '</pre>';
|
174 |
+
//exit;
|
175 |
+
|
176 |
+
foreach ($options['converters'] as &$converter) {
|
177 |
+
if (isset($converter['converter'])) {
|
178 |
+
$converterId = $converter['converter'];
|
179 |
+
} else {
|
180 |
+
$converterId = $converter;
|
181 |
+
}
|
182 |
+
if ($converterId == 'cwebp') {
|
183 |
+
$converter['options']['rel-path-to-precompiled-binaries'] = '../src/Converters/Binaries';
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
if ($options['forward-query-string']) {
|
188 |
+
if (isset($_GET['debug'])) {
|
189 |
+
$options['show-report'] = true;
|
190 |
+
}
|
191 |
+
if (isset($_GET['reconvert'])) {
|
192 |
+
$options['reconvert'] = true;
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
$options['require-for-conversion'] = 'webp-on-demand-2.inc';
|
197 |
+
//$options['require-for-conversion'] = '../../../autoload.php';
|
198 |
+
|
199 |
+
$options['add-vary-header'] = false;
|
200 |
+
$options['fail'] = '404';
|
201 |
+
$options['critical-fail'] = '404';
|
202 |
+
//$options['show-report'] = true;
|
203 |
+
|
204 |
+
function aboutToServeImageCallBack($servingWhat, $whyServingThis, $obj) {
|
205 |
+
// Redirect to same location.
|
206 |
+
header('Location: ?fresh' , 302);
|
207 |
+
return false; // tell webp-convert not to serve!
|
208 |
+
}
|
209 |
+
|
210 |
+
$options['aboutToServeImageCallBack'] = 'aboutToServeImageCallBack';
|
211 |
+
|
212 |
+
include_once '../vendor/rosell-dk/webp-convert/build/webp-on-demand-1.inc';
|
213 |
+
WebPConvert::convertAndServe($source, $destination, $options);
|
214 |
+
|
215 |
+
//echo "<pre>source: $source \ndestination: $destination \n\noptions:" . print_r($options, true) . '</pre>'; exit;
|