WebP Express - Version 0.8.0

Version Description

  • New conversion method, which calls imagick binary directly. This will make WebP express work out of the box on more systems
  • Made sure not to trigger LFI warning i Wordfence (to activate, click the force .htaccess button)
  • Imagick can now be configured to set quality to auto on systems where the auto option isn't generally available
  • Added Last-Modified header to images. This makes image caching work better
  • On some systems, converted files where stored in ie ..doc-rootwp-content.. rather than ..doc-root/wp-content... This is fixed, a clean-up script corrects the file structure upon upgrade.
  • Added condition in .htaccess that checks that source file exists before handing over to converter

For more info, see the closed issues on the 0.8.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/issues?q=is%3Aclosed+milestone%3A0.8.0

Download this release

Release Info

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

Code changes from version 0.7.2 to 0.8.0

BACKERS.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ There are no backers yet. Become the first, by backing me up financially at patreon.com.
2
+ Your name will then appear here.
3
+
4
+ My page at patreon: https://www.patreon.com/rosell
5
+
6
+
7
+ ## Generous backers via Patron
8
+
9
+ There are no generous backers yet. [Be the first!](https://www.patreon.com/rosell)
10
+
11
+ Generous backers will get their names listed here, along with a message - max 100 chars, and it may contain one link (the link url does not count any chars).
12
+
13
+ Examples:
14
+
15
+ | Name | Date | Message (70 chars limit, one link allowed) |
16
+ | --------------------- | ---------- | ----------------------------------------------------------------------- |
17
+ | John Doe | 2018-11-23 | I have a similar plugin. [Check it out!](https://example.com/plugin)|
18
+ | John Doe 2 | 2018-11-23 | I do in fact sell shoes - with WebP Express logo! [Check it out!]((https://printed-shoes.com/) |
19
+
20
+ <small>
21
+ I reserve the right to disallow inappropriate messages and links. No xxx sites or anything freaky or fishy, please. You may however advertise non-freaky-or-fishy things, if you wish. Just remember the audience. No point in trying to sell shoes here</small>
22
+
23
+
24
+ ## Backers via Patron
25
+
26
+ There are no backers yet. [Be the first!](https://www.patreon.com/rosell)
27
+
28
+ Backers will get their names listed here, along with a message (max 70 chars, plain text only).
29
+
30
+ Examples:
31
+
32
+ | Name | Date | Message |
33
+ | --------------------- | ---------- | ----------------------------------------------------------------------- |
34
+ | John Doe | 2018-11-23 | Your children shouldn't just eat bananas. Buy some oranges too! |
35
+ | John Doe 2 | 2018-11-23 | Perhaps you could work on multisite support? |
36
+ | John Doe 3 | 2018-11-23 | Thank you. Your plugin changed my life! |
37
+
38
+ <small>I reserve the right to disallow inappropriate messages. No Trump hooting or bashing here, please. And don't be aggressive, obscene or anything unpleasant. But I don't have to point that out, do I?</small>
README.md CHANGED
@@ -12,11 +12,11 @@ The plugin basically routes jpeg/png images to an image converter, or - if the i
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
  #### Benefits
15
- - Much faster load time for images in blink based browsers such as Chrome and Opera (overall accounting for ~73% of all traffic, and ~78% of mobile browsing trafic, according to [caniuse.com](https://caniuse.com/webp)).
16
- - 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.
17
  - Better user experience (whether performance goes from terrible to bad, or from good to impressive, it is a benefit)
18
  - Better ranking in Google searches (performance is taken into account by Google)
19
  - 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).
 
20
 
21
 
22
  ## Installation
@@ -72,16 +72,63 @@ The redirect rules created in *.htaccess* are pointing to a PHP script. If you h
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.
73
 
74
  *Note:*
75
- The plugin has not been tested in multisite configurations. It's on the roadmap...
76
 
77
 
78
  ## Limitations
79
 
80
- * The plugin does not work on Microsoft IIS server
81
  * The plugin has not been tested with multisite installation
82
 
83
  ## Frequently Asked Questions
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  ### Why do I not see the option to set WebP quality to auto?
86
  The option will only display, if your system is able to detect jpeg qualities. To make your server capable to do that, install *Imagick* or *Gmagick*
87
 
@@ -90,15 +137,18 @@ Chances are that the default setting of your CDN is not to forward any headers t
90
 
91
  The plugin takes care of setting the "Vary" HTTP header to "Accept" when routing WebP images. When the CDN sees this, it knows that the response varies, depending on the "Accept" header. The CDN is thus instructed not to cache the response on URL only, but also on the "Accept" header. This means that it will store an image for every accept header it meets. Luckily, there are (not that many variants for images)[https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values#Values_for_an_image], so it is not an issue.
92
 
93
- ### How do I donate?
94
- Putting this question in the "frequently" asked questions section is of course some mixture of humour, sarcasm and wishful thinking. In case there really is someone out there wanting to donate, you can simply write to me, and we can arrange. My contact information is available here https://www.bitwise-it.dk/contact. I have paypal, mobilepay and of course an ordinary bank account.
95
-
96
- ## Changes in 0.7.0
97
- This version added option to provide conversion service to other sites!
 
 
98
 
99
- For more info, see the closed issues on the 0.7.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/issues?q=is%3Aclosed+milestone%3A0.7.0
100
 
101
- ## Silly dance-ware
102
- If you enjoy this software, feel free to express yourself now through bodily movements. Don't - be - shy!
103
 
104
- *No animals were harmed and no women were sexually harassed either during the production of this plugin*
 
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
  #### Benefits
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 ~78% 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)).
20
 
21
 
22
  ## Installation
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.
73
 
74
  *Note:*
75
+ The plugin has not been tested in multisite configurations.
76
 
77
 
78
  ## Limitations
79
 
80
+ * The plugin does not work on Microsoft IIS server, nor in WAMP
81
  * The plugin has not been tested with multisite installation
82
 
83
  ## Frequently Asked Questions
84
 
85
+ ### How do I verify that the plugin is working?
86
+ See the "Verifying that it works section"
87
+
88
+ ### No conversions methods are working out of the box
89
+ Don't fret - you have options!
90
+
91
+ - If you a controlling another WordPress site (where the local conversion methods DO work), you can set up WebP Express there, and then connect to it by configuring the “Remote WebP Express” conversion method.
92
+ - You can also setup the ewww conversion method. To use it, you need to purchase an api key. They do not charge credits for webp conversions, so all you ever have to pay is the one dollar start-up fee 🙂 (unless they change their pricing – I have no control over that). You can buy an api key here: https://ewww.io/plans/
93
+ - If you are up to it, you can try to get one of the local converters working. Check out [this page](https://github.com/rosell-dk/webp-convert/wiki/Meeting-the-requirements-of-the-converters) on the webp-convert wiki
94
+ - Finally, if you have access to a server and are comfortable with installing projects with composer, you can install [webp-convert-cloud-service](https://github.com/rosell-dk/webp-convert-cloud-service). It's open source.
95
+
96
+ ### It doesn't work - Although test conversions work, it still serves jpeg images.
97
+ Actually, you might be mistaking, so first, make sure that you didn't make the very common mistake of thinking that something with the URL *example.com/image.jpg* must be a jpeg image. The plugin serves webp images on same URL as the original (unconverted) images, so do not let appearances fool you! Confused? See next FAQ item.
98
+
99
+ Assuming that you have inspected the *content type* header, and it doesn't show "image/webp", please make sure that:
100
+ 1) You tested with a browser that supports webp (such as Chrome)
101
+ 2) The image URL you are looking at are not pointing to another server (such as gravatar.com)
102
+
103
+ Assuming that all above is in place, please look at the response headers to see if there is a *X-WebP-Convert-Status* header. If there isn't, well, then it seems that the problem is that the image request isn't handed over to WebP Express. Reasons for that can be:
104
+
105
+ - You are on NGINX (or an Apache/Nginx combination). NGINX requires special attention, please look at that FAQ item
106
+ - You are on WAMP. Please look at that FAQ item
107
+
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 asure 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.
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 requieres manually inserting redirection rules in the NGINX configuration file (nginx.conf). For standard wordpress installations, the following rules should work:
117
+
118
+ ```
119
+ if ($http_accept ~* “webp”){
120
+ rewrite ^/(.*).(jpe?g|png)$ /wp-content/plugins/webp-express/wod/webp-on-demand.php?source=$document_root$request_uri&wp-content=wp-content&%1 break;
121
+ }
122
+ ```
123
+ However, the location of the wp-content folder and the plugins folder can be customized with Wordpress. In that case, the above rule must be changed accordingly.
124
+
125
+ I'd like to make the plugin print the NGINX rules that needs to be inserted, when running on NGINX / NGINX based setup (ie [OpenResty](https://openresty.org/en/)). Please help make that possible by [contributing](https://www.patreon.com/rosell).
126
+
127
+ Discussion on this topic here: https://wordpress.org/support/topic/nginx-rewrite-rules-4/
128
+
129
+ ### I am on a WAMP stack
130
+ It has been reported that WebP Express *almost* works on WAMP stack (Windows, Apache, MySQL, PHP). I'd love to debug this, but do not own a Windows server or access to one... Can you help?
131
+
132
  ### Why do I not see the option to set WebP quality to auto?
133
  The option will only display, if your system is able to detect jpeg qualities. To make your server capable to do that, install *Imagick* or *Gmagick*
134
 
137
 
138
  The plugin takes care of setting the "Vary" HTTP header to "Accept" when routing WebP images. When the CDN sees this, it knows that the response varies, depending on the "Accept" header. The CDN is thus instructed not to cache the response on URL only, but also on the "Accept" header. This means that it will store an image for every accept header it meets. Luckily, there are (not that many variants for images)[https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values#Values_for_an_image], so it is not an issue.
139
 
140
+ ## Changes in 0.8.0
141
+ - New conversion method, which calls imagick binary directly. This will make WebP express work out of the box on more systems
142
+ - Made sure not to trigger LFI warning i Wordfence (to activate, click the force .htaccess button)
143
+ - Imagick can now be configured to set quality to auto (but only when the "auto" option isn't generally available)
144
+ - Added Last-Modified header to images. This makes image caching work better
145
+ - On some systems, converted files where stored in ie *..doc-rootwp-content..* rather than *..doc-root/wp-content..*. This is fixed, a clean-up script corrects the file structure upon upgrade.
146
+ - Added condition in .htaccess that checks that source file exists before handing over to converter
147
 
148
+ For more info, see the closed issues on the 0.8.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/issues?q=is%3Aclosed+milestone%3A0.8.0
149
 
150
+ ## Supporting WebP Express
151
+ Bread on the table don't come for free, even though this plugin does, and always will. I enjoy developing this, and supporting you guys, but I kind of need the bread too. Please make it possible for me to continue putting effort into this plugin:
152
 
153
+ - [Buy me a Coffee](https://ko-fi.com/rosell)
154
+ - [Become a backer or sponsor on Patreon](https://www.patreon.com/rosell).
README.txt CHANGED
@@ -1,10 +1,10 @@
1
  === WebP Express ===
2
  Contributors: rosell.dk
3
- Donate link: https://www.patreon.com/rosell
4
  Tags: webp, images, performance
5
  Requires at least: 4.0
6
  Tested up to: 5.0
7
- Stable tag: 0.7.2
8
  Requires PHP: 5.6
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
@@ -72,7 +72,9 @@ The plugin has not been tested in multisite configurations. It's on the roadmap.
72
  == Supporting WebP Express ==
73
  Bread on the table don't come for free, even though this plugin does, and always will. I enjoy developing this, and supporting you guys, but I kind of need the bread too. Please make it possible for me to continue wasting time on this plugin:
74
 
75
- *[Become a backer or sponsor on Patreon](https://www.patreon.com/rosell)*.
 
 
76
 
77
  == Frequently Asked Questions ==
78
 
@@ -148,9 +150,8 @@ The plugin takes care of setting the "Vary" HTTP header to "Accept" when routing
148
  https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values#Values_for_an_image
149
  - so it is not an issue.
150
 
151
- = How do I donate? =
152
- Putting this question in the "frequently" asked questions section is of course some mixture of humour, sarcasm and wishful thinking. In case there really is someone out there wanting to donate, you can simply write to me, and we can arrange. My contact information is available here https://www.bitwise-it.dk/contact. I have paypal, mobilepay and of course an ordinary bank account.
153
-
154
 
155
  == Screenshots ==
156
 
@@ -158,6 +159,16 @@ Putting this question in the "frequently" asked questions section is of course s
158
 
159
  == Changelog ==
160
 
 
 
 
 
 
 
 
 
 
 
161
  = 0.7.2 =
162
  Fixed a critical bug which generated an error message which caused corrupt images. It was not the bug itself, but the error message it generated, that caused the images to be corrupted. It only happened when debugging was enabled in php.ini
163
 
@@ -213,5 +224,5 @@ For older releases, check out changelog.txt
213
 
214
  == Upgrade Notice ==
215
 
216
- = 0.7.1 =
217
- This version added option to provide conversion service to other sites!
1
  === WebP Express ===
2
  Contributors: rosell.dk
3
+ 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.0
8
  Requires PHP: 5.6
9
  License: GPLv3
10
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
72
  == Supporting WebP Express ==
73
  Bread on the table don't come for free, even though this plugin does, and always will. I enjoy developing this, and supporting you guys, but I kind of need the bread too. Please make it possible for me to continue wasting time on this plugin:
74
 
75
+ * [Buy me a Coffee](https://ko-fi.com/rosell)
76
+ * [Become a backer or sponsor on Patreon](https://www.patreon.com/rosell)
77
+
78
 
79
  == Frequently Asked Questions ==
80
 
150
  https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values#Values_for_an_image
151
  - so it is not an issue.
152
 
153
+ = How do I buy you a cup of coffee? =
154
+ Easy enough! - [Go here!](https://ko-fi.com/rosell)
 
155
 
156
  == Screenshots ==
157
 
159
 
160
  == Changelog ==
161
 
162
+ = 0.8.0 =
163
+ * New conversion method, which calls imagick binary directly. This will make WebP express work out of the box on more systems
164
+ * Made sure not to trigger LFI warning i Wordfence (to activate, click the force .htaccess button)
165
+ * Imagick can now be configured to set quality to auto on systems where the auto option isn't generally available
166
+ * Added Last-Modified header to images. This makes image caching work better
167
+ * On some systems, converted files where stored in ie *..doc-rootwp-content..* rather than *..doc-root/wp-content..*. This is fixed, a clean-up script corrects the file structure upon upgrade.
168
+ * Added condition in .htaccess that checks that source file exists before handing over to converter
169
+
170
+ For more info, see the closed issues on the 0.8.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/issues?q=is%3Aclosed+milestone%3A0.8.0
171
+
172
  = 0.7.2 =
173
  Fixed a critical bug which generated an error message which caused corrupt images. It was not the bug itself, but the error message it generated, that caused the images to be corrupted. It only happened when debugging was enabled in php.ini
174
 
224
 
225
  == Upgrade Notice ==
226
 
227
+ = 0.8.0 =
228
+ New converter and miscellaneous improvements
changelog.txt CHANGED
@@ -1,3 +1,13 @@
 
 
 
 
 
 
 
 
 
 
1
  = 0.7.2 =
2
  Fixed a critical bug which generated an error message which caused corrupt images. It was not the bug itself, but the error message it generated, that caused the images to be corrupted. It only happened when debugging was enabled in php.ini
3
 
1
+ = 0.8.0 =
2
+ * New conversion method, which calls imagick binary directly. This will make WebP express work out of the box on more systems
3
+ * Made sure not to trigger LFI warning i Wordfence (to activate, click the force .htaccess button)
4
+ * Imagick can now be configured to set quality to auto on systems where the auto option isn't generally available
5
+ * Added Last-Modified header to images. This makes image caching work better
6
+ * Added condition in .htaccess that checks that source file exists before handing over to converter
7
+ * On some systems, converted files where stored in ie *..doc-rootwp-content..* rather than *..doc-root/wp-content..*. This is fixed, a clean-up script corrects the file structure upon upgrade.
8
+
9
+ For more info, see the closed issues on the 0.8.0 milestone on the github repository: https://github.com/rosell-dk/webp-express/issues?q=is%3Aclosed+milestone%3A0.8.0
10
+
11
  = 0.7.2 =
12
  Fixed a critical bug which generated an error message which caused corrupt images. It was not the bug itself, but the error message it generated, that caused the images to be corrupted. It only happened when debugging was enabled in php.ini
13
 
docs/development.md ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ The plugin published on github now uses composer. I still include the vendor files on the svn (the Wordpress "codex").
2
+
3
+ For the few, who might be using this thing here on github: To get the dependent libraries, you need to cd into the plugin dir and run `composer update`. This gets you the latest dev releases of "webp-convert" and "webp-convert-cloud-service" on github. You must change the composer.json if you rather want the latest stable release (remove the repositories and change the minimum requirements). On the svn, I simply rsync everything, including the vendor folder, before committing.
lib/admin.php CHANGED
@@ -2,13 +2,15 @@
2
  use \WebPExpress\State;
3
 
4
  // When an update requires a migration, the number should be increased
5
- define('WEBPEXPRESS_MIGRATION_VERSION', '2');
6
 
7
  if (WEBPEXPRESS_MIGRATION_VERSION != get_option('webp-express-migration-version', 0)) {
8
  // run migration logic
9
  include __DIR__ . '/migrate/migrate.php';
10
  }
11
 
 
 
12
  // uncomment next line to debug an error during activation
13
  //include __DIR__ . "/debug.php";
14
 
@@ -45,72 +47,7 @@ register_uninstall_hook(WEBPEXPRESS_PLUGIN, 'webp_express_uninstall');
45
  add_filter('plugin_action_links_' . plugin_basename(WEBPEXPRESS_PLUGIN), function ( $links ) {
46
  $mylinks = array(
47
  '<a href="' . admin_url( 'options-general.php?page=webp_express_settings_page' ) . '">Settings</a>',
 
48
  );
49
  return array_merge($links, $mylinks);
50
  });
51
-
52
- add_action('wp_ajax_webpexpress_start_listening', 'webpexpress_start_listening');
53
- function webpexpress_start_listening() {
54
- include_once __DIR__ . '/classes/State.php';
55
- State::setState('listening', true);
56
- State::setState('request', null);
57
- wp_die();
58
- }
59
-
60
- add_action('wp_ajax_webpexpress_stop_listening', 'webpexpress_stop_listening');
61
- function webpexpress_stop_listening() {
62
- include_once __DIR__ . '/classes/State.php';
63
- State::setState('listening', false);
64
- State::setState('request', null);
65
- wp_die();
66
- }
67
-
68
- add_action('wp_ajax_webpexpress_get_request', 'webpexpress_get_request');
69
- function webpexpress_get_request() {
70
- include_once __DIR__ . '/classes/State.php';
71
- echo json_encode(State::getState('request', null), JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
72
- wp_die();
73
- }
74
-
75
- add_action('wp_ajax_webpexpress_request_access', 'webpexpress_request_access');
76
- function webpexpress_request_access() {
77
-
78
- $ch = curl_init();
79
-
80
- curl_setopt_array(
81
- $ch,
82
- [
83
- CURLOPT_URL => 'http://we0/wordpress/webp-express-server',
84
- CURLOPT_HTTPHEADER => [
85
- 'User-Agent: WebPConvert',
86
- ],
87
- CURLOPT_POSTFIELDS => [
88
- 'action' => 'request-access',
89
- 'label' => 'test',
90
- 'key' => 'test-key'
91
- ],
92
- CURLOPT_RETURNTRANSFER => true,
93
- CURLOPT_HEADER => false,
94
- CURLOPT_SSL_VERIFYPEER => false
95
- ]
96
- );
97
- $response = curl_exec($ch);
98
- if (curl_errno($ch)) {
99
- }
100
-
101
- $returnObj = [
102
- 'success' => true
103
- ];
104
- echo json_encode($returnObj, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
105
-
106
- wp_die();
107
- }
108
- /*
109
- add_action('wp_ajax_webpexpress_accept_request', 'webpexpress_accept_request');
110
- function webpexpress_accept_request() {
111
- include_once __DIR__ . '/classes/State.php';
112
-
113
- State::setState('listening', true);
114
- State::setState('request', null);
115
- wp_die();
116
- }*/
2
  use \WebPExpress\State;
3
 
4
  // When an update requires a migration, the number should be increased
5
+ define('WEBPEXPRESS_MIGRATION_VERSION', '3');
6
 
7
  if (WEBPEXPRESS_MIGRATION_VERSION != get_option('webp-express-migration-version', 0)) {
8
  // run migration logic
9
  include __DIR__ . '/migrate/migrate.php';
10
  }
11
 
12
+ // include __DIR__ . '/migrate/migrate3.php'; // test-running a migration
13
+
14
  // uncomment next line to debug an error during activation
15
  //include __DIR__ . "/debug.php";
16
 
47
  add_filter('plugin_action_links_' . plugin_basename(WEBPEXPRESS_PLUGIN), function ( $links ) {
48
  $mylinks = array(
49
  '<a href="' . admin_url( 'options-general.php?page=webp_express_settings_page' ) . '">Settings</a>',
50
+ '<a href="https://ko-fi.com/rosell" target="_blank">Buy the maintainer a cup of coffee</a>',
51
  );
52
  return array_merge($links, $mylinks);
53
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/classes/Config.php CHANGED
@@ -106,6 +106,9 @@ class Config
106
  unset ($c['id']);
107
  unset($c['working']);
108
  unset($c['error']);
 
 
 
109
  if (!isset($c['options'])) {
110
  $c = $c['converter'];
111
  }
106
  unset ($c['id']);
107
  unset($c['working']);
108
  unset($c['error']);
109
+ if (isset($c['options']['quality']) && ($c['options']['quality'] == 'inherit')) {
110
+ unset ($c['options']['quality']);
111
+ }
112
  if (!isset($c['options'])) {
113
  $c = $c['converter'];
114
  }
lib/classes/ConvertersHelper.php CHANGED
@@ -7,7 +7,7 @@ class ConvertersHelper
7
  public static $defaultConverters = [
8
  ['converter' => 'gd', 'options' => ['skip-pngs' => true]],
9
  ['converter' => 'cwebp', 'options' => [
10
- 'use-nice' => false,
11
  'try-common-system-paths' => true,
12
  'try-supplied-binary-for-os' => true,
13
  'method' => 6,
@@ -17,8 +17,11 @@ class ConvertersHelper
17
  ]],
18
  ['converter' => 'imagick'],
19
  ['converter' => 'gmagick'],
20
- ['converter' => 'wpc', 'options' => ['quality' => 'auto']], // we should not set api-version default - it is handled in the javascript
21
  ['converter' => 'ewww'],
 
 
 
22
  ];
23
 
24
  public static function getDefaultConverterNames()
7
  public static $defaultConverters = [
8
  ['converter' => 'gd', 'options' => ['skip-pngs' => true]],
9
  ['converter' => 'cwebp', 'options' => [
10
+ 'use-nice' => true,
11
  'try-common-system-paths' => true,
12
  'try-supplied-binary-for-os' => true,
13
  'method' => 6,
17
  ]],
18
  ['converter' => 'imagick'],
19
  ['converter' => 'gmagick'],
20
+ ['converter' => 'wpc'], // we should not set api-version default - it is handled in the javascript
21
  ['converter' => 'ewww'],
22
+ ['converter' => 'imagickbinary', 'options' => [
23
+ 'use-nice' => true,
24
+ ]],
25
  ];
26
 
27
  public static function getDefaultConverterNames()
lib/classes/FileHelper.php CHANGED
@@ -160,4 +160,27 @@ class FileHelper
160
  }
161
  return $return;
162
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
160
  }
161
  return $return;
162
  }
163
+
164
+
165
+ /* Remove dir and files in it recursively.
166
+ No warnings
167
+ returns $success
168
+ */
169
+ public static function rrmdir($dir) {
170
+ if (@is_dir($dir)) {
171
+ $objects = @scandir($dir);
172
+ foreach ($objects as $object) {
173
+ if ($object != "." && $object != "..") {
174
+ if (@is_dir($dir . "/" . $object))
175
+ self::rrmdir($dir . "/" . $object);
176
+ else
177
+ @unlink($dir . "/" . $object);
178
+ }
179
+ }
180
+ return @rmdir($dir);
181
+ } else {
182
+ return false;
183
+ }
184
+ }
185
+
186
  }
lib/classes/HTAccess.php CHANGED
@@ -60,12 +60,13 @@ class HTAccess
60
 
61
  $rules .= " # Redirect images to webp-on-demand.php (if browser supports webp)\n";
62
  $rules .= " RewriteCond %{HTTP_ACCEPT} image/webp\n";
 
63
  if ($config['forward-query-string']) {
64
  $rules .= " RewriteCond %{QUERY_STRING} (.*)\n";
65
  }
66
  $rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ " .
67
  "/" . Paths::getWodUrlPath() .
68
- "?source=%{SCRIPT_FILENAME}" .
69
  "&wp-content=" . Paths::getWPContentDirRel() .
70
  ($config['forward-query-string'] ? '&%1' : '') .
71
  " [NC,L]\n";
60
 
61
  $rules .= " # Redirect images to webp-on-demand.php (if browser supports webp)\n";
62
  $rules .= " RewriteCond %{HTTP_ACCEPT} image/webp\n";
63
+ $rules .= " RewriteCond %{REQUEST_FILENAME} -f\n";
64
  if ($config['forward-query-string']) {
65
  $rules .= " RewriteCond %{QUERY_STRING} (.*)\n";
66
  }
67
  $rules .= " RewriteRule ^(.*)\.(" . $fileExt . ")$ " .
68
  "/" . Paths::getWodUrlPath() .
69
+ "?xsource=x%{SCRIPT_FILENAME}" .
70
  "&wp-content=" . Paths::getWPContentDirRel() .
71
  ($config['forward-query-string'] ? '&%1' : '') .
72
  " [NC,L]\n";
lib/classes/Paths.php CHANGED
@@ -115,18 +115,21 @@ class Paths
115
  }
116
 
117
  // ------------ Upload Dir -------------
118
- // (can be moved out of wp-content dir. But not (rarely? - I suppose someone could enter dots) out of abspath)
 
 
 
 
119
 
 
120
  public static function getUploadDirAbs()
121
  {
122
- // Pst: When supporting multisite, we could use wp_upload_dir instead
123
- // https://developer.wordpress.org/reference/functions/wp_upload_dir/
124
  if ( defined( 'UPLOADS' ) ) {
125
  return ABSPATH . rtrim(UPLOADS, '/');
126
  } else {
127
  return self::getWPContentDirAbs() . '/uploads';
128
  }
129
- }
130
 
131
  public static function isUploadDirMovedOutOfWPContentDir()
132
  {
@@ -313,7 +316,8 @@ APACHE
313
  ],
314
  'filePaths' => [
315
  'webpExpressRoot' => self::getWebPExpressPluginDirAbs(),
316
- 'destinationRoot' => self::getCacheDirAbs()
 
317
  ]
318
  ];
319
  }
115
  }
116
 
117
  // ------------ Upload Dir -------------
118
+ public static function getUploadDirAbs()
119
+ {
120
+ $upload_dir = wp_upload_dir(null, false);
121
+ return $upload_dir['basedir'];
122
+ }
123
 
124
+ /*
125
  public static function getUploadDirAbs()
126
  {
 
 
127
  if ( defined( 'UPLOADS' ) ) {
128
  return ABSPATH . rtrim(UPLOADS, '/');
129
  } else {
130
  return self::getWPContentDirAbs() . '/uploads';
131
  }
132
+ }*/
133
 
134
  public static function isUploadDirMovedOutOfWPContentDir()
135
  {
316
  ],
317
  'filePaths' => [
318
  'webpExpressRoot' => self::getWebPExpressPluginDirAbs(),
319
+ 'destinationRoot' => self::getCacheDirAbs(),
320
+ 'configRelToDocRoot' => self::getConfigDirRel()
321
  ]
322
  ];
323
  }
lib/migrate/migrate.php CHANGED
@@ -3,7 +3,6 @@
3
  include_once __DIR__ . '/../classes/State.php';
4
  use \WebPExpress\State;
5
 
6
-
7
  /*
8
  In 0.4.0, we had a 'webp-express-configured' option.
9
  As long as there are still users on 0.4 or below, we must do the following:
@@ -42,10 +41,10 @@ if (!(State::getState('configured', false))) {
42
  include __DIR__ . '/migrate2.php';
43
  }
44
 
45
- /*
46
  // We make sure to grab the option again - it might have been changed in the migration above
47
  if (intval(get_option('webp-express-migration-version', 0)) == 2) {
48
- // run migration 2
49
  include __DIR__ . '/migrate3.php';
50
- }*/
51
  }
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:
41
  include __DIR__ . '/migrate2.php';
42
  }
43
 
44
+
45
  // We make sure to grab the option again - it might have been changed in the migration above
46
  if (intval(get_option('webp-express-migration-version', 0)) == 2) {
47
+ // run migration 3
48
  include __DIR__ . '/migrate3.php';
49
+ }
50
  }
lib/migrate/migrate3.php CHANGED
@@ -1,66 +1,127 @@
1
  <?php
2
- /*
3
  namespace WebPExpress;
4
 
5
- include_once __DIR__ . '/../classes/Config.php';
6
- use \WebPExpress\Config;
 
7
 
8
  include_once __DIR__ . '/../classes/Paths.php';
9
  use \WebPExpress\Paths;
10
 
 
 
 
11
  include_once __DIR__ . '/../classes/Messenger.php';
12
  use \WebPExpress\Messenger;
13
 
14
- include_once __DIR__ . '/../classes/TestRun.php';
15
- use \WebPExpress\TestRun;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  function webpexpress_migrate3() {
18
 
19
- $changedSomething = false;
20
- $config = Config::loadConfig();
21
- if ($config !== false) {
22
- if (isset($config['converters'])) {
23
- foreach ($config['converters'] as &$converter) {
24
- if (isset($converter['converter']) && ($converter['converter'] == 'wpc') && (isset($converter['options']))) {
25
- if (isset($converter['options']['secret'])) {
26
- $converter['options']['api-key'] = $converter['options']['secret'];
27
- unset($converter['options']['secret']);
28
- $converter['options']['api-version'] = 0;
29
- } else {
30
- $converter['options']['api-version'] = 1;
31
- }
32
-
33
- unset($converter['options']['url-2']);
34
- unset($converter['options']['secret-2']);
35
-
36
- $changedSomething = true;
37
-
38
- }
39
  }
40
- if ($changedSomething) {
41
- if (Config::saveConfigurationFileAndWodOptions($config)) {
42
- Messenger::addMessage(
43
- 'info',
44
- 'WebP Express successfully migrated configuration file to 0.7.0 format'
45
- );
46
- } else {
47
- Messenger::addMessage(
48
- 'warning',
49
- 'WebP Express could not migrated configuration files to 0.7.0 format, because it failed saving the files. ' .
50
- 'If you use the wpc converter, you should change the configuration files manually (located in wp-content/webp-express/config). ' .
51
- 'You should change "secret" to "api-key"'
52
- );
53
- return;
54
- }
55
  }
56
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
59
- // PSST: When creating new migration files, remember to update WEBPEXPRESS_MIGRATION_VERSION in admin.php
60
 
 
61
  update_option('webp-express-migration-version', '3');
62
 
63
  }
64
 
65
  webpexpress_migrate3();
66
- */
1
  <?php
2
+
3
  namespace WebPExpress;
4
 
5
+
6
+ include_once __DIR__ . '/../classes/FileHelper.php';
7
+ use \WebPExpress\FileHelper;
8
 
9
  include_once __DIR__ . '/../classes/Paths.php';
10
  use \WebPExpress\Paths;
11
 
12
+ include_once __DIR__ . '/../classes/PathHelper.php';
13
+ use \WebPExpress\PathHelper;
14
+
15
  include_once __DIR__ . '/../classes/Messenger.php';
16
  use \WebPExpress\Messenger;
17
 
18
+
19
+ if ( ! function_exists('webp_express_glob_recursive'))
20
+ {
21
+ // Does not support flag GLOB_BRACE
22
+
23
+ function webp_express_glob_recursive($pattern, $flags = 0)
24
+ {
25
+ $files = glob($pattern, $flags);
26
+
27
+ foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
28
+ {
29
+ $files = array_merge($files, webp_express_glob_recursive($dir.'/'.basename($pattern), $flags));
30
+ }
31
+
32
+ return $files;
33
+ }
34
+ }
35
 
36
  function webpexpress_migrate3() {
37
 
38
+ $dirs = glob(PathHelper::canonicalize(Paths::getCacheDirAbs()) . '/doc-root*');
39
+
40
+ $movedAtLeastOneFile = false;
41
+ $failedMovingAtLeastOneFile = false;
42
+ $atLeastOneFileMustBeMoved = false;
43
+ $failedRemovingAtLeastOneDir = false;
44
+
45
+ foreach ($dirs as $dir) {
46
+ if (preg_match('/\/doc-root$/i', $dir)) {
47
+ // do not process the "doc-root" dir
48
+ continue;
49
+ }
50
+
51
+ $files = webp_express_glob_recursive($dir . '/*.webp');
52
+ foreach ($files as $file) {
53
+ $atLeastOneFileMustBeMoved = true;
54
+ $newName = preg_replace('/\/doc-root(.*)$/', '/doc-root/$1', $file);
55
+ $dirName = FileHelper::dirName($newName);
56
+ if (!file_exists($dirName)) {
57
+ mkdir($dirName, 0775, true);
58
  }
59
+ if (@rename($file, $newName)) {
60
+ $movedAtLeastOneFile = true;
61
+ } else {
62
+ $failedMovingAtLeastOneFile = true;
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
  }
65
+
66
+ if (!FileHelper::rrmdir($dir)) {
67
+ $failedRemovingAtLeastOneDir = true;
68
+ }
69
+ }
70
+
71
+
72
+ if ($atLeastOneFileMustBeMoved) {
73
+ if ($movedAtLeastOneFile && !$failedMovingAtLeastOneFile && !$failedRemovingAtLeastOneDir) {
74
+ Messenger::addMessage(
75
+ 'info',
76
+ 'Successfully fixed cache directory structure. Dont know what its all about? Never mind, all is okay.'
77
+ );
78
+ } else {
79
+ if ($failedRemovingAtLeastOneDir) {
80
+ Messenger::addMessage(
81
+ 'warning',
82
+ 'A minor bug caused the cache directory structure to be wrong on your system ' .
83
+ '(<a href="https://github.com/rosell-dk/webp-express/issues/96" target="_blank">issue #96</a>). ' .
84
+ 'The bug has been fixed, but unfortunately the file permissions does not allow WebP Convert to clean up the file structure. ' .
85
+ 'To clean up manually, delete all folders in your wp-content/webp-express/webp-images folder beginning with "doc-root" (but not the "doc-root" folder itself)'
86
+ );
87
+ }
88
+ }
89
+ }
90
+
91
+ // Show "Whats new" message.
92
+ // We test the version, because we do not want a whole lot of "whats new" messages
93
+ // to show when updating many versions in one go. Just the recent, please.
94
+ if (WEBPEXPRESS_MIGRATION_VERSION == '3') {
95
+ Messenger::addMessage(
96
+ 'info',
97
+ '<i>New in WebP Express 0.8.0:</i>' .
98
+ '<ul style="list-style-type:disc;margin-left:20px">' .
99
+ '<li>New conversion method, which calls imagick binary directly</li>' .
100
+ '<li>Made sure not to trigger LFI warning i Wordfence (to activate, click the force .htaccess button)</li>' .
101
+ "<li>Imagick can now be configured to set quality to auto on systems where the auto option isn't generally available</li>" .
102
+ '<li><a href="https://github.com/rosell-dk/webp-express/issues?q=is%3Aclosed+milestone%3A0.8.0">and more...</a></li>' .
103
+ '</ul>' .
104
+ '</ul>' .
105
+ '<br><i>Roadmap / wishlist:</i>' .
106
+ '<ul style="list-style-type:disc;margin-left:20px">' .
107
+ '<li>Rule in .htaccess to serve already converted images immediately (optional)</li>' .
108
+ '<li>Better NGINX support (print rules that needs to be manually inserted in nginx.conf)</li>' .
109
+ '<li>Diagnose button</li>' .
110
+ '<li>A file explorer for viewing converted images, reconverting them, and seeing them side by side with the original</li>' .
111
+ '<li>IIS support, WAMP support, Multisite support</li>' .
112
+ '<li><a href="https://github.com/rosell-dk/webp-express/issues">and more...</a></li>' .
113
+ '</ul>' .
114
+ '<b>Please help me making this happen faster / happen at all by donating even a small sum. ' .
115
+ '<a href="https://ko-fi.com/rosell" target="_blank" >Buy me a coffee</a>, ' .
116
+ 'or support me on <a href="http://www.patreon.com/rosell" target="_blank" >patreon.com</a>' .
117
+ '</b>'
118
+ );
119
  }
120
 
 
121
 
122
+ // PSST: When creating new migration files, remember to update WEBPEXPRESS_MIGRATION_VERSION in admin.php
123
  update_option('webp-express-migration-version', '3');
124
 
125
  }
126
 
127
  webpexpress_migrate3();
 
lib/options/converter-options/cwebp.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="cwebp" style="display:none;">
2
+ <div class="cwebp converter-options">
3
+ <h3>cweb options</h3>
4
+ <div>
5
+ <label for="cwebp_use_nice">Use nice</label>
6
+ <input type="checkbox" id="cwebp_use_nice">
7
+ <br>Enabling this option saves system resources at the cost of slightly slower conversion
8
+ </div>
9
+ <div>
10
+ <label for="cwebp_try_common_system_paths">Try to execute cweb binary at common locations</label>
11
+ <input type="checkbox" id="cwebp_try_common_system_paths">
12
+ <br>If checked, we will look for binaries in common locations, such as <i>/usr/bin/cwebp</i>
13
+ </div>
14
+ <div>
15
+ <label for="cwebp_try_common_system_paths">Try precompiled cwebp</label>
16
+ <input type="checkbox" id="cwebp_try_supplied_binary">
17
+ <br>This plugin ships with precompiled cweb binaries for different platforms. If checked, and we have a precompiled binary for your OS, we will try to exectute it
18
+ </div>
19
+ <div>
20
+ <label for="cwebp_method">Method (0-6)</label>
21
+ <input type="text" size="2" id="cwebp_method">
22
+ <br>This parameter controls the trade off between encoding speed and the compressed file size and quality.
23
+ Possible values range from 0 to 6. 0 is fastest. 6 results in best quality.
24
+ </div>
25
+ <div>
26
+ <label for="cwebp_set_size">Set size option (and ignore quality option)</label>
27
+ <input type="checkbox" id="cwebp_set_size">
28
+ <br>This option activates the size option below.
29
+ <?php
30
+ if ($canDetectQuality) {
31
+ echo 'As you have quality detection working on your server, it is probably best to use that, rather ';
32
+ echo 'than the "size" option. Using the size option takes more ressources (it takes about 2.5 times ';
33
+ echo 'longer for cwebp to do a a conversion with the size option than the quality option). Long ';
34
+ echo 'story short, you should probably <i>not</i> activate the size option.';
35
+ } else {
36
+ echo 'As you do not have quality detection working on your server, it is probably a good ';
37
+ echo 'idea to use the size option to avoid making conversions with a higher quality setting ';
38
+ echo 'than the source image. ';
39
+ echo 'Beware, though, that cwebp takes about 2.5 times longer to do a a conversion with the size option set.';
40
+ }
41
+ ?>
42
+ </div>
43
+ <div>
44
+ <label for="cwebp_size_in_percentage">Size (in percentage of source)</label>
45
+ <input type="text" size="2" id="cwebp_size_in_percentage">
46
+ <br>Set the cwebp should aim for, in percentage of the original.
47
+ Usually cwebp can reduce to ~45% of original without loosing quality.
48
+ </div>
49
+ <div>
50
+ <label for="cwebp_command_line_options">Extra command line options</label><br>
51
+ <input type="text" size="40" id="cwebp_command_line_options" style="width:100%">
52
+ <br>This allows you to set any parameter available for cwebp in the same way as
53
+ you would do when executing <i>cwebp</i>. As a syntax example, you could ie. set it to
54
+ "-low_memory -af -f 50 -sharpness 0 -mt -crop 10 10 40 40" (do not include the quotes).
55
+ Read more about all the available parameters in
56
+ <a target="_blank" href="https://developers.google.com/speed/webp/docs/cwebp">the docs</a>
57
+ </div>
58
+ <br>
59
+ <?php webp_express_printUpdateButtons() ?>
60
+ </div>
61
+ </div>
lib/options/converter-options/ewww.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="ewww" style="display:none;">
2
+ <div class="ewww converter-options">
3
+ <h3>Ewww</h3>
4
+ <p>
5
+ ewww is a cloud service for converting images.
6
+ To use it, you need to purchase a key <a target="_blank" href="https://ewww.io/plans/">here</a>.
7
+ They do not charge credits for webp conversions, so all you ever have to pay is the one dollar start-up fee :)
8
+ </p>
9
+ <h3>Options</h3>
10
+ <div>
11
+ <label for="ewww_key">Key</label>
12
+ <input type="text" id="ewww_key" placeholder="Your API key here">
13
+ </div>
14
+ <br>
15
+ <h4>Fallback (optional)</h4>
16
+ <div>
17
+ <label for="ewww_key_2">key</label>
18
+ <input type="text" id="ewww_key_2" placeholder="In case the first one expires...">
19
+ </div>
20
+ <br>
21
+ <?php webp_express_printUpdateButtons() ?>
22
+ </div>
23
+ </div>
lib/options/converter-options/gd.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="gd" style="display:none;">
2
+ <div class="gd converter-options">
3
+ <h3>Gd options</h3>
4
+ <div>
5
+ <label for="gd_skip_pngs">Skip PNGs</label>
6
+ <input type="checkbox" id="gd_skip_pngs">
7
+ <br>Gd is not suited for converting PNGs into webp. &ndash;
8
+ The filesize is generally much larger than the original.
9
+ For this reason, the converter defaults to skip PNG's.
10
+ </div>
11
+ <br>
12
+ <?php webp_express_printUpdateButtons() ?>
13
+ </div>
14
+ </div>
lib/options/converter-options/imagick.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="imagick" style="display:none;">
2
+ <div class="imagick converter-options">
3
+ <h3>Imagick options</h3>
4
+ <?php
5
+ if ($canDetectQuality) {
6
+ echo '<div class="info">imagick has no special options.</div>';
7
+ } else {
8
+ echo '<br>';
9
+ printAutoQualityOptionForConverter('imagick');
10
+ }
11
+ ?>
12
+ <!--
13
+ <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
14
+ -->
15
+ <!-- <a href="javascript: tb_remove();">close</a> -->
16
+ </div>
17
+ </div>
lib/options/converter-options/imagickbinary.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="imagickbinary" style="display:none;">
2
+ <div class="imagickbinary converter-options">
3
+
4
+ <h3>Imagick binary options</h3>
5
+ <p>This conversion method works by executing imagick binary (the 'convert' command).</p>
6
+
7
+ <div>
8
+ <label for="imagickbinary_use_nice">Use nice</label>
9
+ <input type="checkbox" id="imagickbinary_use_nice">
10
+ <br>Enabling this option saves system resources at the cost of slightly slower conversion
11
+ </div>
12
+
13
+ <?php
14
+ if (!$canDetectQuality) {
15
+ printAutoQualityOptionForConverter('imagickbinary');
16
+ }
17
+ ?>
18
+ <!--
19
+ <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
20
+ -->
21
+ <!-- <a href="javascript: tb_remove();">close</a> -->
22
+ <?php webp_express_printUpdateButtons() ?>
23
+ </div>
24
+ </div>
lib/options/converter-options/wpc.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="wpc" style="display:none;">
2
+ <div class="wpc converter-options">
3
+ <h3>Remote WebP Express</h3>
4
+ Use a WebP Express installed on another Wordpress site to convert. Remote WepP Express is based
5
+ on <a href="https://github.com/rosell-dk/webp-convert-cloud-service" target="blank">WPC</a>,
6
+ and you can use it to connect to WPC as well.
7
+
8
+ <?php
9
+ if ((!extension_loaded('curl')) || (!function_exists('curl_init'))) {
10
+ echo '<p><b style="color:red">Your server does not have curl installed. Curl is required!</b></p>';
11
+ }
12
+ ?>
13
+
14
+ <h3>Options</h3>
15
+ <!--
16
+ <div>
17
+ <label for="wpc_web_services">Web Services</label>
18
+ <div style="display:inline-block">
19
+ <div id="wpc_web_services_div"></div>
20
+ <button type="button" id="wpc_web_services_request" onclick="openWpcConnectPopup()" class="button button-secondary" >Add web service</button>
21
+ </div>
22
+ </div>
23
+ -->
24
+
25
+ <div id="wpc_api_version_div">
26
+ <label for="wpc_api_version">
27
+ Api version
28
+ <?php echo helpIcon('Select 1, if connecting to a remote webp-express. Api 0 was never used with this plugin, and should only be used to connect to webp-convert-cloud-service v.0.1 instances'); ?>
29
+ </label>
30
+ <select id="wpc_api_version" onchange="wpcApiVersionChanged()">
31
+ <option value="0">0</option>
32
+ <option value="1">1</option>
33
+ </select>
34
+ </div>
35
+
36
+ <div>
37
+ <label for="wpc_url">
38
+ URL
39
+ <?php echo helpIcon('The endpoint of the web service. Copy it from the remote setup.'); ?>
40
+ </label>
41
+ <input type="text" id="wpc_url" placeholder="Url to your Remote WebP Express">
42
+ </div>
43
+
44
+ <div id="wpc_secret_div">
45
+ <label for="wpc_secret">
46
+ Secret
47
+ <?php echo helpIcon('Must match the one set up in webp-convert-cloud-service v0.1'); ?>
48
+ </label>
49
+ <input type="text" id="wpc_secret" placeholder="">
50
+ </div>
51
+
52
+ <div id="wpc_api_key_div">
53
+ <label id="wpc_api_key_label_1" for="wpc_api_key">
54
+ Secret
55
+ <?php echo helpIcon('The secret set up on the wpc server. Copy that.'); ?>
56
+ </label>
57
+ <label id="wpc_api_key_label_2" for="wpc_api_key">
58
+ Api key
59
+ <?php echo helpIcon('The API key is set up on the remote. Copy that.'); ?>
60
+ </label>
61
+ <input id="wpc_new_api_key" type="password">
62
+ <a id="wpc_change_api_key" href="javascript:wpcChangeApiKey()">
63
+ Click to change
64
+ </a>
65
+ </div>
66
+
67
+ <div id="wpc_crypt_api_key_in_transfer_div">
68
+ <label for="wpc_crypt_api_key_in_transfer">
69
+ Crypt api key in transfer?
70
+ <?php echo helpIcon('If checked, the api key will be crypted in requests. Crypting the api-key protects it from being stolen during transfer.'); ?>
71
+ </label>
72
+ <input id="wpc_crypt_api_key_in_transfer" type="checkbox">
73
+ </div>
74
+
75
+ <?php
76
+ if (!$canDetectQuality) {
77
+ printAutoQualityOptionForConverter('wpc');
78
+ }
79
+ ?>
80
+
81
+ <p>
82
+ <b>Psst. The IP of your website is: <?php echo $_SERVER['SERVER_ADDR']; ?>.</b>
83
+ </p>
84
+ <?php webp_express_printUpdateButtons() ?>
85
+ </div>
86
+ </div>
lib/options/css/webp-express-options-page.css CHANGED
@@ -77,8 +77,8 @@
77
  #converters li a {
78
  cursor: pointer;
79
  }
80
- #converters li[data-id='gmagick'] a.configure-converter,
81
- #converters li[data-id='imagick'] a.configure-converter {
82
  visibility: hidden;
83
  }
84
 
@@ -201,6 +201,10 @@
201
  .converter-options button {
202
  margin-top: 15px;
203
  }
 
 
 
 
204
  .converter-options div {
205
  margin-bottom: 15px;
206
  }
77
  #converters li a {
78
  cursor: pointer;
79
  }
80
+
81
+ #converters li[data-id='gmagick'] a.configure-converter {
82
  visibility: hidden;
83
  }
84
 
201
  .converter-options button {
202
  margin-top: 15px;
203
  }
204
+ .converter-options button.button-primary {
205
+ margin-right: 10px;
206
+ }
207
+
208
  .converter-options div {
209
  margin-bottom: 15px;
210
  }
lib/options/enqueue_scripts.php CHANGED
@@ -9,7 +9,7 @@ wp_enqueue_script('sortable');
9
  wp_register_script('daspopup', plugins_url('js/das-popup.js', __FILE__), [], '0.7.0-dev5');
10
  wp_enqueue_script('daspopup');
11
 
12
- wp_register_script('converters', plugins_url('js/converters.js', __FILE__), ['sortable','daspopup'], '0.7.0-dev24');
13
  wp_enqueue_script('converters');
14
 
15
  wp_register_script('whitelist', plugins_url('js/whitelist.js', __FILE__), ['daspopup'], '0.7.0-dev15');
@@ -29,7 +29,7 @@ if (function_exists('wp_add_inline_script')) {
29
  }
30
 
31
  // Register styles
32
- wp_register_style('webp-express-options-page-css', plugins_url('css/webp-express-options-page.css', __FILE__), null, '0.7.0-dev6');
33
  wp_enqueue_style('webp-express-options-page-css');
34
 
35
  wp_register_style('das-popup-css', plugins_url('css/das-popup.css', __FILE__), null, '0.7.0-dev5');
9
  wp_register_script('daspopup', plugins_url('js/das-popup.js', __FILE__), [], '0.7.0-dev5');
10
  wp_enqueue_script('daspopup');
11
 
12
+ wp_register_script('converters', plugins_url('js/converters.js', __FILE__), ['sortable','daspopup'], '0.8.0-dev7');
13
  wp_enqueue_script('converters');
14
 
15
  wp_register_script('whitelist', plugins_url('js/whitelist.js', __FILE__), ['daspopup'], '0.7.0-dev15');
29
  }
30
 
31
  // Register styles
32
+ wp_register_style('webp-express-options-page-css', plugins_url('css/webp-express-options-page.css', __FILE__), null, '0.8.0-dev4');
33
  wp_enqueue_style('webp-express-options-page-css');
34
 
35
  wp_register_style('das-popup-css', plugins_url('css/das-popup.css', __FILE__), null, '0.7.0-dev5');
lib/options/js/converters.js CHANGED
@@ -55,6 +55,7 @@ function getConversionMethodDescription(converterId) {
55
  'gd': 'Gd extension',
56
  'imagick': 'Imagick extension',
57
  'gmagick': 'Gmagick extension',
 
58
  };
59
  if (descriptions[converterId]) {
60
  return descriptions[converterId];
@@ -240,6 +241,13 @@ function configureConverter(id) {
240
  var converter = window.convertersMap[id];
241
  window.currentlyEditing = id;
242
 
 
 
 
 
 
 
 
243
  switch (converter['converter']) {
244
  case 'ewww':
245
  document.getElementById('ewww_key').value = getConverterOption(converter, 'key', '');
@@ -312,13 +320,6 @@ function configureConverter(id) {
312
 
313
  //wpcUpdateWebServicesHTML();
314
 
315
- var q = getConverterOption(converter, 'quality', 'not_set');
316
- if (document.getElementById('wpc_quality')) {
317
- document.getElementById('wpc_quality').value = q;
318
- document.getElementById('wpc_max_quality_div').style['display'] = (q == 'auto' ? 'block' : 'none');
319
- document.getElementById('wpc_max_quality').value = getConverterOption(converter, 'max-quality', 85);
320
- }
321
-
322
  break;
323
  case 'gd':
324
  document.getElementById('gd_skip_pngs').checked = getConverterOption(converter, 'skip-pngs', '');
@@ -332,13 +333,31 @@ function configureConverter(id) {
332
  document.getElementById('cwebp_size_in_percentage').value = getConverterOption(converter, 'size-in-percentage', '');
333
  document.getElementById('cwebp_command_line_options').value = getConverterOption(converter, 'command-line-options', '');
334
  break;
335
-
 
 
336
  }
337
  tb_show("Configure " + converter['id'] + ' converter', '#TB_inline?inlineId=' + converter['converter']);
338
  }
339
 
340
  function updateConverterOptions() {
341
- var converter = window.convertersMap[window.currentlyEditing];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
  switch (converter['converter']) {
344
  case 'ewww':
@@ -370,20 +389,6 @@ function updateConverterOptions() {
370
  deleteConverterOption(converter, 'new-api-key');
371
  }
372
 
373
-
374
- if (document.getElementById('wpc_quality')) {
375
- var q = document.getElementById('wpc_quality').value;
376
- if (q == 'auto') {
377
- setConverterOption(converter, 'quality', 'auto');
378
- setConverterOption(converter, 'max-quality', document.getElementById('wpc_max_quality').value);
379
- } else {
380
- delete converter['options']['quality'];
381
- delete converter['options']['max-quality'];
382
- }
383
- } else {
384
- delete converter['options']['quality'];
385
- delete converter['options']['max-quality'];
386
- }
387
  break;
388
  case 'gd':
389
  setConverterOption(converter, 'skip-pngs', document.getElementById('gd_skip_pngs').checked);
@@ -397,11 +402,27 @@ function updateConverterOptions() {
397
  setConverterOption(converter, 'size-in-percentage', document.getElementById('cwebp_size_in_percentage').value);
398
  setConverterOption(converter, 'command-line-options', document.getElementById('cwebp_command_line_options').value);
399
  break;
 
 
 
400
  }
401
  updateInputValue();
402
  tb_remove();
 
 
 
 
403
  document.getElementById('webpexpress_settings').submit();
404
  }
 
 
 
 
 
 
 
 
 
405
 
406
  function testConverter(id) {
407
  //alert('h' + id);
@@ -418,13 +439,14 @@ function testConverter(id) {
418
 
419
  // test images here: http://nottinghamtec.co.uk/~aer/TestPatterns/1080/
420
  filename = 'test.jpg';
421
- filename = 'stones.jpg';
422
  filename = 'architecture2.jpg';
423
  filename = 'test1.png';
424
- filename = 'focus.jpg';
425
 
426
- url += '?source=' + paths['webpExpressRoot'] + '/test/' + filename;
427
- url += '&destination=' + paths['destinationRoot'] + '/test-conversions/' + filename + '.webp';
 
428
  url += '&converter=' + converter['converter'];
429
  if (document.getElementById('max_quality')) {
430
  url += '&max-quality=' + document.getElementById('max_quality').value;
@@ -479,9 +501,9 @@ function activateConverter(id) {
479
  /* WPC */
480
  /* ------------- */
481
 
482
- function wpcQualityChanged() {
483
- var q = document.getElementById('wpc_quality').value;
484
- document.getElementById('wpc_max_quality_div').style['display'] = (q == 'auto' ? 'block' : 'none');
485
  }
486
 
487
  function wpcShowAwaitingApprovalPopup() {
55
  'gd': 'Gd extension',
56
  'imagick': 'Imagick extension',
57
  'gmagick': 'Gmagick extension',
58
+ 'imagickbinary': 'Imagick binary'
59
  };
60
  if (descriptions[converterId]) {
61
  return descriptions[converterId];
241
  var converter = window.convertersMap[id];
242
  window.currentlyEditing = id;
243
 
244
+ var q = getConverterOption(converter, 'quality', 'auto');
245
+ if (document.getElementById(id + '_quality')) {
246
+ document.getElementById(id + '_quality').value = q;
247
+ document.getElementById(id + '_max_quality_div').style['display'] = (q == 'auto' ? 'block' : 'none');
248
+ document.getElementById(id + '_max_quality').value = getConverterOption(converter, 'max-quality', 85);
249
+ }
250
+
251
  switch (converter['converter']) {
252
  case 'ewww':
253
  document.getElementById('ewww_key').value = getConverterOption(converter, 'key', '');
320
 
321
  //wpcUpdateWebServicesHTML();
322
 
 
 
 
 
 
 
 
323
  break;
324
  case 'gd':
325
  document.getElementById('gd_skip_pngs').checked = getConverterOption(converter, 'skip-pngs', '');
333
  document.getElementById('cwebp_size_in_percentage').value = getConverterOption(converter, 'size-in-percentage', '');
334
  document.getElementById('cwebp_command_line_options').value = getConverterOption(converter, 'command-line-options', '');
335
  break;
336
+ case 'imagickbinary':
337
+ document.getElementById('imagickbinary_use_nice').checked = getConverterOption(converter, 'use-nice', '');
338
+ break;
339
  }
340
  tb_show("Configure " + converter['id'] + ' converter', '#TB_inline?inlineId=' + converter['converter']);
341
  }
342
 
343
  function updateConverterOptions() {
344
+ var id = window.currentlyEditing;
345
+ var converter = window.convertersMap[id];
346
+
347
+ if (document.getElementById(id + '_quality')) {
348
+ var q = document.getElementById(id + '_quality').value;
349
+ if (q == 'auto') {
350
+ setConverterOption(converter, 'quality', 'auto');
351
+ setConverterOption(converter, 'max-quality', document.getElementById(id + '_max_quality').value);
352
+ } else {
353
+ //delete converter['options']['quality'];
354
+ setConverterOption(converter, 'quality', 'inherit');
355
+ delete converter['options']['max-quality'];
356
+ }
357
+ } else {
358
+ delete converter['options']['quality'];
359
+ delete converter['options']['max-quality'];
360
+ }
361
 
362
  switch (converter['converter']) {
363
  case 'ewww':
389
  deleteConverterOption(converter, 'new-api-key');
390
  }
391
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  break;
393
  case 'gd':
394
  setConverterOption(converter, 'skip-pngs', document.getElementById('gd_skip_pngs').checked);
402
  setConverterOption(converter, 'size-in-percentage', document.getElementById('cwebp_size_in_percentage').value);
403
  setConverterOption(converter, 'command-line-options', document.getElementById('cwebp_command_line_options').value);
404
  break;
405
+ case 'imagickbinary':
406
+ setConverterOption(converter, 'use-nice', document.getElementById('imagickbinary_use_nice').checked);
407
+ break;
408
  }
409
  updateInputValue();
410
  tb_remove();
411
+ }
412
+
413
+ function updateConverterOptionsAndSave() {
414
+ updateConverterOptions();
415
  document.getElementById('webpexpress_settings').submit();
416
  }
417
+ /** Encode path before adding to querystring.
418
+ * Paths in querystring triggers LFI warning in Wordfence.
419
+ * By encoding it, Wordpfence will not detect our misdeed!
420
+ *
421
+ * see https://github.com/rosell-dk/webp-express/issues/87
422
+ */
423
+ function encodePathforQS(path) {
424
+ return path.replace('/', '**');
425
+ }
426
 
427
  function testConverter(id) {
428
  //alert('h' + id);
439
 
440
  // test images here: http://nottinghamtec.co.uk/~aer/TestPatterns/1080/
441
  filename = 'test.jpg';
442
+ /* filename = 'stones.jpg';
443
  filename = 'architecture2.jpg';
444
  filename = 'test1.png';
445
+ filename = 'focus.jpg';*/
446
 
447
+ url += '?source=' + encodePathforQS(paths['webpExpressRoot'] + '/test/' + filename);
448
+ url += '&configDirRel=' + encodePathforQS(paths['configRelToDocRoot']);
449
+ url += '&destination=' + encodePathforQS(paths['destinationRoot'] + '/test-conversions/' + filename + '.webp');
450
  url += '&converter=' + converter['converter'];
451
  if (document.getElementById('max_quality')) {
452
  url += '&max-quality=' + document.getElementById('max_quality').value;
501
  /* WPC */
502
  /* ------------- */
503
 
504
+ function converterQualityChanged(converterId) {
505
+ var q = document.getElementById(converterId + '_quality').value;
506
+ document.getElementById(converterId + '_max_quality_div').style['display'] = (q == 'auto' ? 'block' : 'none');
507
  }
508
 
509
  function wpcShowAwaitingApprovalPopup() {
lib/options/page-messages.php CHANGED
@@ -10,6 +10,8 @@ use \WebPExpress\FileHelper;
10
 
11
  //include __DIR__ . "/page-welcome.php";
12
 
 
 
13
  if ((!State::getState('configured', false))) {
14
  include __DIR__ . "/page-welcome.php";
15
  }
10
 
11
  //include __DIR__ . "/page-welcome.php";
12
 
13
+ //echo 'display errors:' . ini_get('display_errors');
14
+
15
  if ((!State::getState('configured', false))) {
16
  include __DIR__ . "/page-welcome.php";
17
  }
lib/options/page.php CHANGED
@@ -43,6 +43,29 @@ function webpexpress_converterName($converterId) {
43
  return $converterId;
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  //update_option('webp-express-migration-version', '1');
47
 
48
  // Test converters
@@ -59,7 +82,6 @@ if ($testResult) {
59
  );
60
  }
61
 
62
- $canDetectQuality = TestRun::isLocalQualityDetectionWorking();
63
 
64
  include __DIR__ . "/page-messages.php";
65
 
@@ -309,8 +331,10 @@ if ($canDetectQuality) {
309
  echo helpIcon('All converted images will be encoded with this quality');
310
  } else {
311
  echo helpIcon('All converted images will be encoded with this quality. ' .
312
- 'For the Remote WebP Express converter, you will however have the option to use override this, and use ' .
313
- '"auto". If you install imagick or gmagick, quality can have "auto" for all convertion methods. '
 
 
314
  );
315
  }
316
  echo '</th><td>';
@@ -375,298 +399,23 @@ echo '<a target="_blank" href="https://github.com/rosell-dk/webp-convert/blob/ma
375
  // https://github.com/RubaXa/Sortable
376
 
377
  // Empty list of converters. The list will be populated by the javascript
378
- echo '<ul id="converters" style="margin-top: -13px"></ul>';
379
- ?>
380
- <div id="cwebp" style="display:none;">
381
- <div class="cwebp converter-options">
382
- <h3>cweb options</h3>
383
- <div>
384
- <label for="cwebp_use_nice">Use nice</label>
385
- <input type="checkbox" id="cwebp_use_nice">
386
- <br>Enabling this option saves system resources at the cost of slightly slower conversion
387
- </div>
388
- <div>
389
- <label for="cwebp_try_common_system_paths">Try to execute cweb binary at common locations</label>
390
- <input type="checkbox" id="cwebp_try_common_system_paths">
391
- <br>If checked, we will look for binaries in common locations, such as <i>/usr/bin/cwebp</i>
392
- </div>
393
- <div>
394
- <label for="cwebp_try_common_system_paths">Try precompiled cwebp</label>
395
- <input type="checkbox" id="cwebp_try_supplied_binary">
396
- <br>This plugin ships with precompiled cweb binaries for different platforms. If checked, and we have a precompiled binary for your OS, we will try to exectute it
397
- </div>
398
- <div>
399
- <label for="cwebp_method">Method (0-6)</label>
400
- <input type="text" size="2" id="cwebp_method">
401
- <br>This parameter controls the trade off between encoding speed and the compressed file size and quality.
402
- Possible values range from 0 to 6. 0 is fastest. 6 results in best quality.
403
- </div>
404
- <div>
405
- <label for="cwebp_set_size">Set size option (and ignore quality option)</label>
406
- <input type="checkbox" id="cwebp_set_size">
407
- <br>This option activates the size option below.
408
- <?php
409
- if ($canDetectQuality) {
410
- echo 'As you have quality detection working on your server, it is probably best to use that, rather ';
411
- echo 'than the "size" option. Using the size option takes more ressources (it takes about 2.5 times ';
412
- echo 'longer for cwebp to do a a conversion with the size option than the quality option). Long ';
413
- echo 'story short, you should probably <i>not</i> activate the size option.';
414
- } else {
415
- echo 'As you do not have quality detection working on your server, it is probably a good ';
416
- echo 'idea to use the size option to avoid making conversions with a higher quality setting ';
417
- echo 'than the source image. ';
418
- echo 'Beware, though, that cwebp takes about 2.5 times longer to do a a conversion with the size option set.';
419
- }
420
- ?>
421
- </div>
422
- <div>
423
- <label for="cwebp_size_in_percentage">Size (in percentage of source)</label>
424
- <input type="text" size="2" id="cwebp_size_in_percentage">
425
- <br>Set the cwebp should aim for, in percentage of the original.
426
- Usually cwebp can reduce to ~45% of original without loosing quality.
427
- </div>
428
- <div>
429
- <label for="cwebp_command_line_options">Extra command line options</label><br>
430
- <input type="text" size="40" id="cwebp_command_line_options" style="width:100%">
431
- <br>This allows you to set any parameter available for cwebp in the same way as
432
- you would do when executing <i>cwebp</i>. As a syntax example, you could ie. set it to
433
- "-low_memory -af -f 50 -sharpness 0 -mt -crop 10 10 40 40" (do not include the quotes).
434
- Read more about all the available parameters in
435
- <a target="_blank" href="https://developers.google.com/speed/webp/docs/cwebp">the docs</a>
436
- </div>
437
- <br>
438
- <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update and save settings</button>
439
- <!-- <a href="javascript: tb_remove();">close</a> -->
440
- </div>
441
- </div>
442
- <div id="gd" style="display:none;">
443
- <div class="gd converter-options">
444
- <h3>Gd options</h3>
445
- <div>
446
- <label for="gd_skip_pngs">Skip PNGs</label>
447
- <input type="checkbox" id="gd_skip_pngs">
448
- <br>Gd is not suited for converting PNGs into webp. &ndash;
449
- The filesize is generally much larger than the original.
450
- For this reason, the converter defaults to skip PNG's.
451
- </div>
452
- <br>
453
- <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update and save settings</button>
454
- <!-- <a href="javascript: tb_remove();">close</a> -->
455
- </div>
456
- </div>
457
- <div id="imagick" style="display:none;">
458
- <div class="imagick converter-options">
459
- <h3>Imagick options</h3>
460
- <div class="info">
461
- imagick has no special options.
462
- </div>
463
- <br>
464
- <!--
465
- <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update</button>
466
- -->
467
- <!-- <a href="javascript: tb_remove();">close</a> -->
468
- </div>
469
- </div>
470
- <div id="ewww" style="display:none;">
471
- <div class="ewww converter-options">
472
- <h3>Ewww</h3>
473
- <p>
474
- ewww is a cloud service for converting images.
475
- To use it, you need to purchase a key <a target="_blank" href="https://ewww.io/plans/">here</a>.
476
- They do not charge credits for webp conversions, so all you ever have to pay is the one dollar start-up fee :)
477
- </p>
478
- <h3>Options</h3>
479
- <div>
480
- <label for="ewww_key">Key</label>
481
- <input type="text" id="ewww_key" placeholder="Your API key here">
482
- </div>
483
- <br>
484
- <h4>Fallback (optional)</h4>
485
- <div>
486
- <label for="ewww_key_2">key</label>
487
- <input type="text" id="ewww_key_2" placeholder="In case the first one expires...">
488
- </div>
489
- <br>
490
- <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update and save settings</button>
491
- <!-- <a href="javascript: tb_remove();">close</a> -->
492
- </div>
493
- </div>
494
- <!--
495
- <div id="wpc_successfully_connected_popup" class="das-popup">
496
- <h3>Your request has been approved</h3>
497
- All you need now is to save settings (both places)
498
- <button onclick="closeDasPopup()" class="button button-primary" type="button" style="position:absolute; bottom:20px">Close</button>
499
- </div>
500
- <div id="wpc_awaiting_approval_popup" class="das-popup">
501
- <h3>Avaiting approval<span class="animated-dots">...</span></h3>
502
- In the remote WebP Express settings, the screen should now show "Incoming request".
503
- Click the "Grant access" button there, and then return here.
504
- </div>
505
- <div id="wpc_connect_popup" class="das-popup">
506
- <h3>Request access to web service</h3>
507
- <div style="font-size:90%">
508
- Before requesting access, the website you want to request access to must be <i>listening</i>
509
- for requests. If you control that website, open a new tab and do the following. Otherwise,
510
- make sure the admin does the following:
511
- <ol>
512
- <li>Log in to the Wordpress site you want to connect to.</li>
513
- <li>In WebP Express settings, make sure that "enable web service?" is checked.</li>
514
- <li>Click "+ Authorize new website"</li>
515
- <li>An URL will display, which you must copy to the field below:</li>
516
- </ol>
517
- Paste URL here:<br>
518
- <input id="wpc_request_access_url" style="width:100%">
519
- </div>
520
- <div style="position:absolute; bottom:20px; line-height:28px">
521
- <button onclick="wpcRequestAccess()" class="button button-primary" type="button">Request access</button>
522
- &nbsp;or&nbsp;
523
- <button onclick="wpcAddManually()" class="button button-secondary" type="button">Add manually</button>
524
- </div>
525
- </div>
526
- <div id="wpc_properties_popup" class="das-popup">
527
- <h3 class="hide-in-edit">Add connection to web service</h3>
528
- <h3 class="hide-in-add">Edit connection to web service</h3>
529
- <input type="hidden" id="wpc_i">
530
- <div>
531
- <label for="wpc_label">
532
- Label
533
- <?php echo helpIcon('The label is purely for your own reference'); ?>
534
- </label>
535
- <input id="wpc_label" type="text">
536
- </div>
537
- <div>
538
- <label for="wpc_url">
539
- URL
540
- <?php echo helpIcon('The endpoint of the web service.'); ?>
541
- </label>
542
- <input id="wpc_url" type="text">
543
- </div>
544
- <div>
545
- <label for="wpc_api_key">
546
- Api key
547
- <?php echo helpIcon('The API key is set up on the remote. Copy that.'); ?>
548
- </label>
549
- <input id="wpc_api_key" type="password" class="hide-in-edit">
550
- <a href="javascript:wpcChangeApiKey()" class="hide-in-add" style="display:inline-block;line-height:34px">Change api key</a>
551
- </div>
552
- <div>
553
- <label for="wpc_crypt_api_key_in_transfer">
554
- Crypt api key in transfer?
555
- <?php echo helpIcon('If checked, the api key will be crypted in requests. Crypting the api-key protects it from being stolen during transfer.'); ?>
556
- </label>
557
- <input id="wpc_crypt_api_key_in_transfer" type="checkbox">
558
- </div>
559
- <button id="wpc_properties_add_button" onclick="wpcAddEntry()" class="hide-in-edit button button-primary" type="button" style="position:absolute; bottom:20px">
560
- Add
561
- </button>
562
- <button id="wpc_properties_update_button" onclick="wpcUpdateEntry()" class="hide-in-add button button-primary" type="button" style="position:absolute; bottom:20px">
563
- Update
564
- </button>
565
- </div>
566
- -->
567
- <div id="wpc" style="display:none;">
568
- <div class="wpc converter-options">
569
- <h3>Remote WebP Express</h3>
570
- Use a WebP Express installed on another Wordpress site to convert. Remote WepP Express is based
571
- on <a href="https://github.com/rosell-dk/webp-convert-cloud-service" target="blank">WPC</a>,
572
- and you can use it to connect to WPC as well.
573
-
574
- <?php
575
- if ((!extension_loaded('curl')) || (!function_exists('curl_init'))) {
576
- echo '<p><b style="color:red">Your server does not have curl installed. Curl is required!</b></p>';
577
- }
578
- ?>
579
-
580
- <h3>Options</h3>
581
- <!--
582
- <div>
583
- <label for="wpc_web_services">Web Services</label>
584
- <div style="display:inline-block">
585
- <div id="wpc_web_services_div"></div>
586
- <button type="button" id="wpc_web_services_request" onclick="openWpcConnectPopup()" class="button button-secondary" >Add web service</button>
587
- </div>
588
- </div>
589
- -->
590
 
591
- <div id="wpc_api_version_div">
592
- <label for="wpc_api_version">
593
- Api version
594
- <?php echo helpIcon('Select 1, if connecting to a remote webp-express. Api 0 was never used with this plugin, and should only be used to connect to webp-convert-cloud-service v.0.1 instances'); ?>
595
- </label>
596
- <select id="wpc_api_version" onchange="wpcApiVersionChanged()">
597
- <option value="0">0</option>
598
- <option value="1">1</option>
599
- </select>
600
- </div>
601
 
602
- <div>
603
- <label for="wpc_url">
604
- URL
605
- <?php echo helpIcon('The endpoint of the web service. Copy it from the remote setup.'); ?>
606
- </label>
607
- <input type="text" id="wpc_url" placeholder="Url to your Remote WebP Express">
608
- </div>
609
-
610
- <div id="wpc_secret_div">
611
- <label for="wpc_secret">
612
- Secret
613
- <?php echo helpIcon('Must match the one set up in webp-convert-cloud-service v0.1'); ?>
614
- </label>
615
- <input type="text" id="wpc_secret" placeholder="">
616
- </div>
617
-
618
- <div id="wpc_api_key_div">
619
- <label id="wpc_api_key_label_1" for="wpc_api_key">
620
- Secret
621
- <?php echo helpIcon('The secret set up on the wpc server. Copy that.'); ?>
622
- </label>
623
- <label id="wpc_api_key_label_2" for="wpc_api_key">
624
- Api key
625
- <?php echo helpIcon('The API key is set up on the remote. Copy that.'); ?>
626
- </label>
627
- <input id="wpc_new_api_key" type="password">
628
- <a id="wpc_change_api_key" href="javascript:wpcChangeApiKey()">
629
- Click to change
630
- </a>
631
- </div>
632
-
633
- <div id="wpc_crypt_api_key_in_transfer_div">
634
- <label for="wpc_crypt_api_key_in_transfer">
635
- Crypt api key in transfer?
636
- <?php echo helpIcon('If checked, the api key will be crypted in requests. Crypting the api-key protects it from being stolen during transfer.'); ?>
637
- </label>
638
- <input id="wpc_crypt_api_key_in_transfer" type="checkbox">
639
- </div>
640
-
641
- <?php
642
- if (!$canDetectQuality) { ?>
643
- <div>
644
- <label for="wpc_quality">
645
- Quality
646
- <?php echo helpIcon('If "Auto" is selected, the converted image will get same quality as source. Auto is recommended!'); ?>
647
- </label>
648
- <!--
649
- Your server cannot detect quality of jpeg files. But you can have the cloud server do it for you
650
- (provided that <i>it</i> can) -->
651
- <select id="wpc_quality" onchange="wpcQualityChanged()">
652
- <option value="not_set">Use global settings</option>
653
- <option value="auto">Auto</option>
654
- </select>
655
- </div>
656
- <div id="wpc_max_quality_div">
657
- <label>
658
- Max quality
659
- <?php echo helpIcon('Enter number (0-100). Converted images will be encoded with same quality as the source image, but not more than this setting'); ?>
660
- </label>
661
- <input type="text" size=3 id="wpc_max_quality">
662
- </div>
663
- <?php } ?>
664
- <p>
665
- <b>Psst. The IP of your website is: <?php echo $_SERVER['SERVER_ADDR']; ?>.</b>
666
- </p>
667
- <button onclick="updateConverterOptions()" class="button button-primary" type="button">Update and save settings</button>
668
- </div>
669
- </div>
670
  </td></tr>
671
  <?php
672
 
43
  return $converterId;
44
  }
45
 
46
+ $canDetectQuality = TestRun::isLocalQualityDetectionWorking();
47
+
48
+ function printAutoQualityOptionForConverter($converterId) {
49
+ ?>
50
+ <div>
51
+ <label for="<?php echo $converterId; ?>_quality">
52
+ Quality
53
+ <?php echo helpIcon('If "Auto" is selected, the converted image will get same quality as source. Auto is recommended!'); ?>
54
+ </label>
55
+ <select id="<?php echo $converterId; ?>_quality" onchange="converterQualityChanged('<?php echo $converterId; ?>')">
56
+ <option value="inherit">Use global settings</option>
57
+ <option value="auto">Auto</option>
58
+ </select>
59
+ </div>
60
+ <div id="<?php echo $converterId; ?>_max_quality_div">
61
+ <label>
62
+ Max quality
63
+ <?php echo helpIcon('Enter number (0-100). Converted images will be encoded with same quality as the source image, but not more than this setting'); ?>
64
+ </label>
65
+ <input type="text" size=3 id="<?php echo $converterId; ?>_max_quality">
66
+ </div>
67
+ <?php
68
+ }
69
  //update_option('webp-express-migration-version', '1');
70
 
71
  // Test converters
82
  );
83
  }
84
 
 
85
 
86
  include __DIR__ . "/page-messages.php";
87
 
331
  echo helpIcon('All converted images will be encoded with this quality');
332
  } else {
333
  echo helpIcon('All converted images will be encoded with this quality. ' .
334
+ 'For Remote WebP Express and Imagick, you however have the option to use override this, and use ' .
335
+ '"auto". With some setup, you can get quality detection working and you will then be able to set ' .
336
+ 'quality to "auto" generally. For that you either need to get the imagick extension running ' .
337
+ '(PECL >= 2.2.2) or exec() rights and either imagick or gmagick installed.'
338
  );
339
  }
340
  echo '</th><td>';
399
  // https://github.com/RubaXa/Sortable
400
 
401
  // Empty list of converters. The list will be populated by the javascript
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
 
403
+ function webp_express_printUpdateButtons() {
404
+ ?>
405
+ <button onclick="updateConverterOptionsAndSave()" class="button button-primary" type="button">Update and save settings</button>
406
+ <button onclick="updateConverterOptions()" class="button button-secondary" type="button">Update, but do not save yet</button>
407
+ <?php
408
+ //echo '<a href="javascript: tb_remove();">close</a>';
409
+ }
410
+ echo '<ul id="converters" style="margin-top: -13px"></ul>';
 
 
411
 
412
+ include 'converter-options/cwebp.php';
413
+ include 'converter-options/gd.php';
414
+ include 'converter-options/imagick.php';
415
+ include 'converter-options/ewww.php';
416
+ include 'converter-options/wpc.php';
417
+ include 'converter-options/imagickbinary.php';
418
+ ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  </td></tr>
420
  <?php
421
 
lib/options/submit.php CHANGED
@@ -124,6 +124,9 @@ if ($auto) {
124
  $config['quality-specific'] = webp_express_sanitize_quality_field($_POST['quality-specific']);
125
  }
126
 
 
 
 
127
  // remove id's
128
  foreach ($config['converters'] as &$converter) {
129
  unset ($converter['id']);
@@ -175,10 +178,10 @@ foreach ($config['converters'] as &$converter) {
175
  } else {
176
  $converter['options']['api-key'] = $existingWpcApiKey;
177
  }
178
-
179
  }
180
  }
181
 
 
182
  // create password hashes for new passwords
183
  /*
184
  foreach ($config['server']['whitelist'] as &$entry) {
124
  $config['quality-specific'] = webp_express_sanitize_quality_field($_POST['quality-specific']);
125
  }
126
 
127
+ //echo '<pre>' . print_r($config['converters'], true) . '</pre>';
128
+ //die;
129
+
130
  // remove id's
131
  foreach ($config['converters'] as &$converter) {
132
  unset ($converter['id']);
178
  } else {
179
  $converter['options']['api-key'] = $existingWpcApiKey;
180
  }
 
181
  }
182
  }
183
 
184
+
185
  // create password hashes for new passwords
186
  /*
187
  foreach ($config['server']['whitelist'] as &$entry) {
test/test-run.php CHANGED
@@ -5,6 +5,7 @@ if (isset($_GET['stream-webp-image'])) {
5
  if (@readfile($_GET['stream-webp-image']) === false) {
6
  // ...
7
  }
 
8
  }
9
 
10
  error_reporting(E_ALL);
@@ -42,9 +43,19 @@ use WebPConvert\Loggers\EchoLogger;
42
  <body style="">
43
 
44
  <?php
 
 
 
 
 
 
 
 
 
 
45
  //WebPConvertAndServe::convertAndReport($source, $destination, $options);use WebPConvert\Loggers\EchoLogger;
46
- $source = $_GET['source'];
47
- $destination = $_GET['destination'];
48
  $converter = $_GET['converter'];
49
 
50
  if (isset($_GET['max-quality'])) {
@@ -74,7 +85,7 @@ function getConverterOptionsFromQueryString($converter)
74
  // Set options
75
  $options = [];
76
  foreach ($availOptions as $optionName => $optionType) {
77
- //echo $optionName . '<br>';
78
  switch ($optionType) {
79
  case 'string':
80
  if (isset($_GET[$optionName])) {
@@ -93,6 +104,47 @@ function getConverterOptionsFromQueryString($converter)
93
  break;
94
  }
95
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  return $options;
97
  }
98
  $options['converters'] = [[
@@ -100,6 +152,10 @@ $options['converters'] = [[
100
  'options' => getConverterOptionsFromQueryString($converter)
101
  ]];
102
 
 
 
 
 
103
  //echo '<pre>' . print_r($_GET, true) . '</pre>';
104
  //echo '<pre>' . print_r($options, true) . '</pre>';
105
 
5
  if (@readfile($_GET['stream-webp-image']) === false) {
6
  // ...
7
  }
8
+ exit;
9
  }
10
 
11
  error_reporting(E_ALL);
43
  <body style="">
44
 
45
  <?php
46
+
47
+ /**
48
+ * Paths passed in query string were encoded, to avoid triggering LFI warning in Wordfence
49
+ * (encoding is done in converters.js)
50
+ * see https://github.com/rosell-dk/webp-express/issues/87
51
+ */
52
+ function decodePathInQS($encodedPath) {
53
+ return preg_replace('/\*\*/', '/', $encodedPath);
54
+ }
55
+
56
  //WebPConvertAndServe::convertAndReport($source, $destination, $options);use WebPConvert\Loggers\EchoLogger;
57
+ $source = decodePathInQS($_GET['source']);
58
+ $destination = decodePathInQS($_GET['destination']);
59
  $converter = $_GET['converter'];
60
 
61
  if (isset($_GET['max-quality'])) {
85
  // Set options
86
  $options = [];
87
  foreach ($availOptions as $optionName => $optionType) {
88
+ //echo $optionName . ':' . $optionType . '<br>';
89
  switch ($optionType) {
90
  case 'string':
91
  if (isset($_GET[$optionName])) {
104
  break;
105
  }
106
  }
107
+
108
+ if ($converter == 'wpc') {
109
+
110
+ // Handle api key.
111
+ // If it has been modified on the options page, it is passed as 'new-api-key'.
112
+ // If it has not been modified, it is not passed at all!
113
+ // - in that case, we must load it from the config file.
114
+
115
+ if (isset($_GET['new-api-key'])) {
116
+ $options['api-key'] = $_GET['new-api-key'];
117
+ } elseif (isset($_GET['configDirRel'])) {
118
+
119
+ // Fetch api-key from configuration file.
120
+ $configFilename = $_SERVER['DOCUMENT_ROOT'] . '/' . decodePathInQS($_GET['configDirRel']) . '/config.json';
121
+
122
+ if (file_exists($configFilename)) {
123
+
124
+ $handle = @fopen($configFilename, "r");
125
+ $json = fread($handle, filesize($configFilename));
126
+ fclose($handle);
127
+
128
+ $config = json_decode($json, true);
129
+ if ($config) {
130
+ foreach ($config['converters'] as $converter) {
131
+ if ($converter['converter'] == 'wpc') {
132
+ //print_r($converter);
133
+ if (isset($converter['options']['api-key'])) {
134
+ $options['api-key'] = $converter['options']['api-key'];
135
+ //echo 'api-key:' . $converter['options']['api-key'] . '<br>';
136
+ //print_r($options);
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ if (!isset($options['api-key'])) {
144
+ echo '<p style="color:red">Warning: No Api key is set</p>';
145
+ }
146
+ }
147
+
148
  return $options;
149
  }
150
  $options['converters'] = [[
152
  'options' => getConverterOptionsFromQueryString($converter)
153
  ]];
154
 
155
+
156
+
157
+
158
+
159
  //echo '<pre>' . print_r($_GET, true) . '</pre>';
160
  //echo '<pre>' . print_r($options, true) . '</pre>';
161
 
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.2
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
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.8.0
7
  * Author: Bjørn Rosell
8
  * Author URI: https://www.bitwise-it.dk
9
  * License: GPL2
wod/webp-on-demand.php CHANGED
@@ -1,5 +1,8 @@
1
  <?php
2
 
 
 
 
3
  //require 'webp-on-demand-1.inc';
4
  require '../vendor/rosell-dk/webp-convert/build/webp-on-demand-1.inc';
5
  //require '../vendor/autoload.php';
@@ -21,7 +24,6 @@ $options = json_decode($json, true);
21
  $options['require-for-conversion'] = 'webp-on-demand-2.inc';
22
  //$options['require-for-conversion'] = '../../../autoload.php';
23
 
24
- $gmagickHere = false;
25
  foreach ($options['converters'] as &$converter) {
26
  if (isset($converter['converter'])) {
27
  $converterId = $converter['converter'];
@@ -31,12 +33,6 @@ foreach ($options['converters'] as &$converter) {
31
  if ($converterId == 'cwebp') {
32
  $converter['options']['rel-path-to-precompiled-binaries'] = '../src/Converters/Binaries';
33
  }
34
- if ($converterId == 'gmagick') {
35
- $gmagickHere = true;
36
- }
37
- }
38
- if (!$gmagickHere) {
39
- // $options['converters'][] = 'gmagick';
40
  }
41
 
42
  if ($options['forward-query-string']) {
@@ -47,26 +43,37 @@ if ($options['forward-query-string']) {
47
  $options['reconvert'] = true;
48
  }
49
  }
50
- $source = $_GET['source'];
 
 
 
 
 
51
 
52
  // Calculate destination
53
- $applicationRoot = $_SERVER["DOCUMENT_ROOT"];
54
  $imageRoot = $contentDirAbs . '/webp-images';
55
 
56
- if (substr($source, 0, strlen($applicationRoot)) === $applicationRoot) {
57
- // Source file is residing inside document root.
58
- // We can store relative to that.
59
- $sourceRel = substr($source, strlen($applicationRoot));
60
- $destination = $imageRoot . '/doc-root' . $sourceRel . '.webp';
 
 
 
 
61
  } else {
62
  // Source file is residing outside document root.
63
  // we must add complete path to structure
64
  $destination = $imageRoot . '/abs' . $source . '.webp';
65
  }
66
- //$destination = $imageRoot . $source . '.webp';
 
 
 
 
 
67
 
68
 
69
- //echo $source . '<br>';
70
- //echo $destination . '<br>';
71
- //echo $sourceRel;
72
  WebPConvert::convertAndServe($source, $destination, $options);
1
  <?php
2
 
3
+ //echo 'display errors:' . ini_get('display_errors');
4
+ //exit;
5
+
6
  //require 'webp-on-demand-1.inc';
7
  require '../vendor/rosell-dk/webp-convert/build/webp-on-demand-1.inc';
8
  //require '../vendor/autoload.php';
24
  $options['require-for-conversion'] = 'webp-on-demand-2.inc';
25
  //$options['require-for-conversion'] = '../../../autoload.php';
26
 
 
27
  foreach ($options['converters'] as &$converter) {
28
  if (isset($converter['converter'])) {
29
  $converterId = $converter['converter'];
33
  if ($converterId == 'cwebp') {
34
  $converter['options']['rel-path-to-precompiled-binaries'] = '../src/Converters/Binaries';
35
  }
 
 
 
 
 
 
36
  }
37
 
38
  if ($options['forward-query-string']) {
43
  $options['reconvert'] = true;
44
  }
45
  }
46
+
47
+ if (isset($_GET['source'])) {
48
+ $source = $_GET['source'];
49
+ } elseif (isset($_GET['xsource'])) {
50
+ $source = substr($_GET['xsource'], 1);
51
+ }
52
 
53
  // Calculate destination
54
+ $docRoot = rtrim($_SERVER["DOCUMENT_ROOT"], '/');
55
  $imageRoot = $contentDirAbs . '/webp-images';
56
 
57
+ // Check if source is residing inside document root.
58
+ // (it is, if path starts with document root + '/')
59
+ if (substr($source, 0, strlen($docRoot) + 1) === $docRoot . '/') {
60
+
61
+ // We store relative to document root.
62
+ // "Eat" the left part off the source parameter which contains the document root.
63
+ // and also eat the slash (+1)
64
+ $sourceRel = substr($source, strlen($docRoot) + 1);
65
+ $destination = $imageRoot . '/doc-root/' . $sourceRel . '.webp';
66
  } else {
67
  // Source file is residing outside document root.
68
  // we must add complete path to structure
69
  $destination = $imageRoot . '/abs' . $source . '.webp';
70
  }
71
+
72
+ // If we wanted webp images to be located in same folder, with ie ".jpg.webp" extension:
73
+ // $destination = $source . '.webp';
74
+
75
+ // If we wanted webp images to be located in same folder, with ".webp" extension:
76
+ // $destination = preg_replace('/\.(jpg|jpeg|png)$/', '.webp', $source);
77
 
78
 
 
 
 
79
  WebPConvert::convertAndServe($source, $destination, $options);