✅ Fixed bulk conversion getting stuck on missing files ✅ Added robust error handling and timeout protection ✅ Improved JavaScript response parsing ✅ Added file existence validation ✅ Fixed missing PHP class imports ✅ Added comprehensive try-catch error recovery 🔧 Key fixes: - File existence checks before conversion attempts - 30-second timeout protection per file - Graceful handling of 500 errors and JSON parsing issues - Automatic continuation to next file on failures - Cache busting for JavaScript updates 🎯 Result: Bulk conversion now completes successfully even with missing files 🚀 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
808 lines
29 KiB
PHP
808 lines
29 KiB
PHP
<?php
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
|
|
use \WebPExpress\CacheMover;
|
|
use \WebPExpress\Config;
|
|
use \WebPExpress\ConvertersHelper;
|
|
use \WebPExpress\DismissableMessages;
|
|
use \WebPExpress\HTAccess;
|
|
use \WebPExpress\HTAccessRules;
|
|
use \WebPExpress\Messenger;
|
|
use \WebPExpress\PathHelper;
|
|
use \WebPExpress\Paths;
|
|
|
|
// TODO: Move this code to a class
|
|
|
|
check_admin_referer('webpexpress-save-settings-nonce');
|
|
|
|
DismissableMessages::dismissMessage('0.14.0/say-hello-to-vips');
|
|
|
|
DismissableMessages::dismissMessage('0.15.0/new-scope-setting-no-uploads');
|
|
DismissableMessages::dismissMessage('0.15.0/new-scope-setting-index');
|
|
DismissableMessages::dismissMessage('0.15.0/new-scope-setting-content');
|
|
DismissableMessages::dismissMessage('0.15.1/problems-with-mingled-set');
|
|
|
|
/*
|
|
--------------------------------
|
|
Custom functions for sanitizing
|
|
--------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Get sanitized text (NUL removed too)
|
|
*
|
|
* General purpose for getting textual values from $_POST.
|
|
* If the POST value is not set, the fallback is returned
|
|
*
|
|
* For sanitizing, the wordpress function "sanitize_text_field" is used. However, before doing that, we
|
|
* remove any NUL characters. NUL characters can be used to trick input validation, so we better get rid of those
|
|
* right away
|
|
*
|
|
* @param string $keyInPOST key in $_POST
|
|
* @param int $fallback value to return if the POST does not match any in the set, or it is not send at all
|
|
* @param array $acceptableValues the set of values that we have to choose between
|
|
*
|
|
* @return string sanitized text, or fallback if value isn't set
|
|
*/
|
|
function webpexpress_getSanitizedText($keyInPOST, $fallbackValue = '') {
|
|
if (!isset($_POST[$keyInPOST])) {
|
|
return $fallbackValue;
|
|
}
|
|
$value = $_POST[$keyInPOST];
|
|
|
|
// Keep in mind checking for NUL when dealing with user input
|
|
// see https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP
|
|
$value = str_replace(chr(0), '', $value);
|
|
|
|
return sanitize_text_field($value);
|
|
}
|
|
|
|
/**
|
|
* Get sanitized value from a set of values.
|
|
*
|
|
* Only allows values in the set given. If the value does not match, the fallback will be returned.
|
|
*
|
|
* @param string $keyInPOST key in $_POST
|
|
* @param int $fallback value to return if the POST does not match any in the set, or it is not send at all
|
|
* @param array $acceptableValues the set of values that we have to choose between
|
|
*
|
|
* @return mixed one of the items in the set - or fallback (which is usually also one in the set)
|
|
*/
|
|
function webpexpress_getSanitizedChooseFromSet($keyInPOST, $fallbackValue, $acceptableValues) {
|
|
$value = webpexpress_getSanitizedText($keyInPOST, $fallbackValue);
|
|
if (in_array($value, $acceptableValues)) {
|
|
return $value;
|
|
}
|
|
return $fallbackValue;
|
|
}
|
|
|
|
function webpexpress_getSanitizedCacheControlHeader($keyInPOST) {
|
|
$value = webpexpress_getSanitizedText($keyInPOST);
|
|
|
|
// Example of valid header: "public, max-age=31536000, stale-while-revalidate=604800, stale-if-error=604800"
|
|
$value = strtolower($value);
|
|
return preg_replace('#[^a-z0-9=,\s_\-]#', '', $value);
|
|
}
|
|
|
|
/**
|
|
* Get sanitized integer
|
|
*
|
|
* @param string $keyInPOST key in $_POST
|
|
* @param int $fallback fallback in case nothing in POST or if we cannot parse it as int
|
|
*
|
|
* @return int the sanitized int value.
|
|
*/
|
|
function webpexpress_getSanitizedInt($keyInPOST, $fallback=0) {
|
|
$value = webpexpress_getSanitizedText($keyInPOST, strval($fallback));
|
|
|
|
// strip anything after and including comma
|
|
$value = preg_replace('#[\.\,].*#', '', $value);
|
|
|
|
// remove anything but digits
|
|
$value = preg_replace('#[^0-9]#', '', $value);
|
|
|
|
if ($value == '') {
|
|
return $fallback;
|
|
}
|
|
|
|
return intval($value);
|
|
}
|
|
|
|
/**
|
|
* Get sanitized quality (0-100).
|
|
*
|
|
* @param string $keyInPOST key in $_POST
|
|
*
|
|
* @return int quality (0-100)
|
|
*/
|
|
function webpexpress_getSanitizedQuality($keyInPOST, $fallback = 75) {
|
|
$q = webpexpress_getSanitizedInt($keyInPOST, $fallback);
|
|
// return value between 0-100
|
|
return max(0, min($q, 100));
|
|
}
|
|
|
|
function webpexpress_getSanitizedScope() {
|
|
$scopeText = webpexpress_getSanitizedText('scope');
|
|
if ($scopeText == '') {
|
|
$scopeText = 'uploads';
|
|
}
|
|
$scope = explode(',', $scopeText);
|
|
$allowed = Paths::getImageRootIds();
|
|
$result = [];
|
|
foreach ($scope as $imageRootId) {
|
|
if (in_array($imageRootId, $allowed)) {
|
|
$result[] = $imageRootId;
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get sanitized whitelist
|
|
*
|
|
* @return array Sanitized array of the whitelist json array received in $_POST
|
|
*/
|
|
function webpexpress_getSanitizedWhitelist() {
|
|
$whitelistPosted = (isset($_POST['whitelist']) ? $_POST['whitelist'] : '[]');
|
|
|
|
$whitelistPosted = json_decode(wp_unslash($whitelistPosted), true);
|
|
// TODO: check for json decode error
|
|
|
|
$whitelistSanitized = [];
|
|
|
|
// Sanitize whitelist
|
|
foreach ($whitelistPosted as $whitelist) {
|
|
if (
|
|
isset($whitelist['label']) &&
|
|
isset($whitelist['ip'])
|
|
// note: api-key is not neccessarily set
|
|
) {
|
|
$obj = [
|
|
'label' => sanitize_text_field($whitelist['label']),
|
|
'ip' => sanitize_text_field($whitelist['ip']),
|
|
];
|
|
if (isset($whitelist['new-api-key'])) {
|
|
$obj['new-api-key'] = sanitize_text_field($whitelist['new-api-key']);
|
|
}
|
|
if (isset($whitelist['uid'])) {
|
|
$obj['uid'] = sanitize_text_field($whitelist['uid']);
|
|
}
|
|
if (isset($whitelist['require-api-key-to-be-crypted-in-transfer'])) {
|
|
$obj['require-api-key-to-be-crypted-in-transfer'] = ($whitelist['require-api-key-to-be-crypted-in-transfer'] === true);
|
|
}
|
|
|
|
$whitelistSanitized[] = $obj;
|
|
}
|
|
}
|
|
return $whitelistSanitized;
|
|
}
|
|
|
|
/**
|
|
* Get sanitized converters.
|
|
*
|
|
* @return array Sanitized array of the converters json array received in $_POST
|
|
*/
|
|
function webpexpress_getSanitizedConverters() {
|
|
$convertersPosted = (isset($_POST['converters']) ? $_POST['converters'] : '[]');
|
|
$convertersPosted = json_decode(wp_unslash($convertersPosted), true); // holy moly! Wordpress automatically adds slashes to the global POST vars- https://stackoverflow.com/questions/2496455/why-are-post-variables-getting-escaped-in-php
|
|
|
|
$convertersSanitized = [];
|
|
|
|
// Get list of possible converter ids.
|
|
$availableConverterIDs = ConvertersHelper::getDefaultConverterNames();
|
|
|
|
// Add converters one at the time.
|
|
foreach ($convertersPosted as $unsanitizedConverter) {
|
|
if (!isset($unsanitizedConverter['converter'])) {
|
|
continue;
|
|
}
|
|
|
|
// Only add converter if its ID is a known converter.
|
|
if (!in_array($unsanitizedConverter['converter'], $availableConverterIDs)) {
|
|
continue;
|
|
}
|
|
|
|
$sanitizedConverter = [];
|
|
$sanitizedConverter['converter'] = $unsanitizedConverter['converter'];
|
|
|
|
// Sanitize and add expected fields ("options", "working", "deactivated" and "error")
|
|
|
|
// "options"
|
|
if (isset($unsanitizedConverter['options'])) {
|
|
$sanitizedConverter['options'] = [];
|
|
|
|
// Sanitize all (string) options individually
|
|
foreach ($unsanitizedConverter['options'] as $optionName => $unsanitizedOptionValue) {
|
|
|
|
$acceptedOptions = [
|
|
// vips
|
|
'smart-subsample' => 'boolean',
|
|
'preset' => 'string',
|
|
|
|
// gd
|
|
'skip-pngs' => 'boolean',
|
|
|
|
// in multiple
|
|
"use-nice" => 'boolean',
|
|
|
|
// cwebp
|
|
"try-common-system-paths" => 'boolean',
|
|
"try-supplied-binary-for-os" => 'boolean',
|
|
"skip-these-precompiled-binaries" => 'string',
|
|
"method" => 'integer', // 0-6,
|
|
"size-in-percentage" => 'integer', // 0-100
|
|
"low-memory" => 'boolean',
|
|
"command-line-options" => 'string', // webp-convert takes care of sanitizing this very carefully!
|
|
"set-size" => 'boolean',
|
|
|
|
// wpc
|
|
"api-url" => 'string',
|
|
"api-version" => 'integer',
|
|
"crypt-api-key-in-transfer" => 'boolean',
|
|
"new-api-key" => 'string',
|
|
|
|
//ewww
|
|
"api-key" => 'string',
|
|
"api-key-2" => 'string',
|
|
];
|
|
|
|
// check that it is an accepted option name
|
|
if (!isset($acceptedOptions[$optionName])) {
|
|
continue;
|
|
}
|
|
|
|
// check that type is as expected
|
|
$expectedType = $acceptedOptions[$optionName];
|
|
if (gettype($unsanitizedOptionValue) != $expectedType) {
|
|
continue;
|
|
}
|
|
if ($expectedType == 'string') {
|
|
$sanitizedOptionValue = sanitize_text_field($unsanitizedOptionValue);
|
|
} else {
|
|
// integer and boolean are completely safe!
|
|
$sanitizedOptionValue = $unsanitizedOptionValue;
|
|
}
|
|
if (($optionName == "size-in-percentage") && ($sanitizedOptionValue == '')) {
|
|
continue;
|
|
}
|
|
$sanitizedConverter['options'][$optionName] = $sanitizedOptionValue;
|
|
|
|
}
|
|
}
|
|
|
|
// "working" (bool)
|
|
if (isset($unsanitizedConverter['working'])) {
|
|
$sanitizedConverter['working'] = ($unsanitizedConverter['working'] === true);
|
|
}
|
|
|
|
// "deactivated" (bool)
|
|
if (isset($unsanitizedConverter['deactivated'])) {
|
|
$sanitizedConverter['deactivated'] = ($unsanitizedConverter['deactivated'] === true);
|
|
}
|
|
|
|
$convertersSanitized[] = $sanitizedConverter;
|
|
}
|
|
|
|
return $convertersSanitized;
|
|
}
|
|
|
|
/**
|
|
* Get sanitized converters.
|
|
*
|
|
* @return array Sanitized array of the converters json array received in $_POST
|
|
*/
|
|
function webpexpress_getSanitizedAlterHtmlHostnameAliases() {
|
|
$index = 0;
|
|
|
|
$result = [];
|
|
while (isset($_POST['alter-html-hostname-alias-' . $index])) {
|
|
$alias = webpexpress_getSanitizedText('alter-html-hostname-alias-' . $index, '');
|
|
$alias = preg_replace('#^https?\\:\\/\\/#', '', $alias);
|
|
//$alias .= 'hm';
|
|
if ($alias != '') {
|
|
$result[] = $alias;
|
|
}
|
|
$index++;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/*
|
|
------------------------------------------------------
|
|
|
|
Create a sanitized object from the POST data
|
|
It reflects the POST data - it has same keys and values - except that the values have been sanitized.
|
|
|
|
After this, there must be no more references to $_POST
|
|
------------------------------------------------------
|
|
*/
|
|
|
|
|
|
// Sanitizing
|
|
$sanitized = [
|
|
// Force htaccess rules
|
|
'force' => isset($_POST['force']),
|
|
|
|
|
|
// Operation mode
|
|
// --------------
|
|
// Note that "operation-mode" is actually the old mode. The new mode is posted in "change-operation-mode"
|
|
'operation-mode' => webpexpress_getSanitizedChooseFromSet('operation-mode', 'varied-image-responses', [
|
|
'varied-image-responses',
|
|
'cdn-friendly',
|
|
'no-conversion',
|
|
'tweaked'
|
|
]),
|
|
'change-operation-mode' => webpexpress_getSanitizedChooseFromSet('change-operation-mode', 'varied-image-responses', [
|
|
'varied-image-responses',
|
|
'cdn-friendly',
|
|
'no-conversion',
|
|
'tweaked'
|
|
]),
|
|
|
|
|
|
// General
|
|
// --------
|
|
'image-types' => intval(webpexpress_getSanitizedChooseFromSet('image-types', '3', [
|
|
'0',
|
|
'1',
|
|
'2',
|
|
'3'
|
|
])),
|
|
'scope' => webpexpress_getSanitizedScope(),
|
|
'destination-folder' => webpexpress_getSanitizedChooseFromSet('destination-folder', 'separate', [
|
|
'separate',
|
|
'mingled',
|
|
]),
|
|
'destination-extension' => webpexpress_getSanitizedChooseFromSet('destination-extension', 'append', [
|
|
'append',
|
|
'set',
|
|
]),
|
|
'destination-structure' => webpexpress_getSanitizedChooseFromSet('destination-structure', 'doc-root', [
|
|
'doc-root',
|
|
'image-roots',
|
|
]),
|
|
'cache-control' => webpexpress_getSanitizedChooseFromSet('cache-control', 'no-header', [
|
|
'no-header',
|
|
'set',
|
|
'custom'
|
|
]),
|
|
'cache-control-max-age' => webpexpress_getSanitizedChooseFromSet('cache-control-max-age', 'one-hour', [
|
|
'one-second',
|
|
'one-minute',
|
|
'one-hour',
|
|
'one-day',
|
|
'one-week',
|
|
'one-month',
|
|
'one-year',
|
|
]),
|
|
'cache-control-public' => webpexpress_getSanitizedChooseFromSet('cache-control-public', 'public', [
|
|
'public',
|
|
'private',
|
|
]),
|
|
'cache-control-custom' => webpexpress_getSanitizedCacheControlHeader('cache-control-custom'),
|
|
'prevent-using-webps-larger-than-original' => isset($_POST['prevent-using-webps-larger-than-original']),
|
|
|
|
|
|
// Redirection rules
|
|
// -----------------
|
|
'redirect-to-existing-in-htaccess' => isset($_POST['redirect-to-existing-in-htaccess']),
|
|
'enable-redirection-to-converter' => isset($_POST['enable-redirection-to-converter']),
|
|
'only-redirect-to-converter-for-webp-enabled-browsers' => isset($_POST['only-redirect-to-converter-for-webp-enabled-browsers']),
|
|
'only-redirect-to-converter-on-cache-miss' => isset($_POST['only-redirect-to-converter-on-cache-miss']),
|
|
'do-not-pass-source-in-query-string' => isset($_POST['do-not-pass-source-in-query-string']),
|
|
'enable-redirection-to-webp-realizer' => isset($_POST['enable-redirection-to-webp-realizer']),
|
|
|
|
|
|
// Conversion options
|
|
// ------------------
|
|
'metadata' => webpexpress_getSanitizedChooseFromSet('metadata', 'none', [
|
|
'none',
|
|
'all'
|
|
]),
|
|
'jpeg-encoding' => webpexpress_getSanitizedChooseFromSet('jpeg-encoding', 'auto', [
|
|
'lossy',
|
|
'auto'
|
|
]),
|
|
'jpeg-enable-near-lossless' => webpexpress_getSanitizedChooseFromSet('jpeg-enable-near-lossless', 'on', [
|
|
'on',
|
|
'off'
|
|
]),
|
|
'quality-auto' => webpexpress_getSanitizedChooseFromSet('quality-auto', 'auto_on', [
|
|
'auto_on',
|
|
'auto_off'
|
|
]),
|
|
'max-quality' => webpexpress_getSanitizedQuality('max-quality', 80),
|
|
'jpeg-near-lossless' => webpexpress_getSanitizedQuality('jpeg-near-lossless', 60),
|
|
'quality-specific' => webpexpress_getSanitizedQuality('quality-specific', 70),
|
|
'quality-fallback' => webpexpress_getSanitizedQuality('quality-fallback', 70),
|
|
'png-near-lossless' => webpexpress_getSanitizedQuality('png-near-lossless', 60),
|
|
'png-enable-near-lossless' => webpexpress_getSanitizedChooseFromSet('png-enable-near-lossless', 'on', [
|
|
'on',
|
|
'off'
|
|
]),
|
|
'png-quality' => webpexpress_getSanitizedQuality('png-quality', 85),
|
|
'png-encoding' => webpexpress_getSanitizedChooseFromSet('png-encoding', 'auto', [
|
|
'lossless',
|
|
'auto'
|
|
]),
|
|
'alpha-quality' => webpexpress_getSanitizedQuality('alpha-quality', 80),
|
|
'convert-on-upload' => isset($_POST['convert-on-upload']),
|
|
'enable-logging' => isset($_POST['enable-logging']),
|
|
'converters' => webpexpress_getSanitizedConverters(),
|
|
|
|
|
|
// Serve options
|
|
// ---------------
|
|
'fail' => webpexpress_getSanitizedChooseFromSet('fail', 'original', [
|
|
'original',
|
|
'404',
|
|
'report'
|
|
]),
|
|
'success-response' => webpexpress_getSanitizedChooseFromSet('success-response', 'original', [
|
|
'original',
|
|
'converted',
|
|
]),
|
|
|
|
|
|
// Alter html
|
|
// ----------
|
|
'alter-html-enabled' => isset($_POST['alter-html-enabled']),
|
|
'alter-html-only-for-webp-enabled-browsers' => isset($_POST['alter-html-only-for-webp-enabled-browsers']),
|
|
'alter-html-add-picturefill-js' => isset($_POST['alter-html-add-picturefill-js']),
|
|
'alter-html-for-webps-that-has-yet-to-exist' => isset($_POST['alter-html-for-webps-that-has-yet-to-exist']),
|
|
'alter-html-replacement' => webpexpress_getSanitizedChooseFromSet('alter-html-replacement', 'picture', [
|
|
'picture',
|
|
'url'
|
|
]),
|
|
'alter-html-hooks' => webpexpress_getSanitizedChooseFromSet('alter-html-hooks', 'content-hooks', [
|
|
'content-hooks',
|
|
'ob'
|
|
]),
|
|
'alter-html-hostname-aliases' => webpexpress_getSanitizedAlterHtmlHostnameAliases(),
|
|
|
|
|
|
// Web service
|
|
// ------------
|
|
'web-service-enabled' => isset($_POST['web-service-enabled']),
|
|
'whitelist' => webpexpress_getSanitizedWhitelist(),
|
|
|
|
];
|
|
|
|
if (!Paths::canUseDocRootForRelPaths()) {
|
|
$sanitized['destination-structure'] = 'image-roots';
|
|
}
|
|
|
|
/*
|
|
------------------------------------------------------
|
|
|
|
Lets begin working on the data.
|
|
Remember: Use $sanitized instead of $_POST
|
|
|
|
------------------------------------------------------
|
|
*/
|
|
|
|
$config = Config::loadConfigAndFix(false); // false, because we do not need to test if quality detection is working
|
|
$oldConfig = $config;
|
|
|
|
// Set options that are available in all operation modes
|
|
$config = array_merge($config, [
|
|
'operation-mode' => $sanitized['operation-mode'],
|
|
|
|
'scope' => $sanitized['scope'],
|
|
'image-types' => $sanitized['image-types'],
|
|
'forward-query-string' => true,
|
|
]);
|
|
|
|
// Set options that are available in ALL operation modes
|
|
$config['cache-control'] = $sanitized['cache-control'];
|
|
switch ($sanitized['cache-control']) {
|
|
case 'no-header':
|
|
break;
|
|
case 'set':
|
|
$config['cache-control-max-age'] = $sanitized['cache-control-max-age'];
|
|
$config['cache-control-public'] = ($sanitized['cache-control-public'] == 'public');
|
|
break;
|
|
case 'custom':
|
|
$config['cache-control-custom'] = $sanitized['cache-control-custom'];
|
|
break;
|
|
}
|
|
$config['prevent-using-webps-larger-than-original'] = $sanitized['prevent-using-webps-larger-than-original'];
|
|
|
|
|
|
// Alter HTML
|
|
$config['alter-html'] = [];
|
|
$config['alter-html']['enabled'] = $sanitized['alter-html-enabled'];
|
|
if ($sanitized['alter-html-replacement'] == 'url') {
|
|
$config['alter-html']['only-for-webp-enabled-browsers'] = $sanitized['alter-html-only-for-webp-enabled-browsers'];
|
|
} else {
|
|
$config['alter-html']['only-for-webp-enabled-browsers'] = false;
|
|
}
|
|
if ($sanitized['alter-html-replacement'] == 'picture') {
|
|
$config['alter-html']['alter-html-add-picturefill-js'] = $sanitized['alter-html-add-picturefill-js'];
|
|
}
|
|
if ($sanitized['operation-mode'] != 'no-conversion') {
|
|
$config['alter-html']['only-for-webps-that-exists'] = (!$sanitized['alter-html-for-webps-that-has-yet-to-exist']);
|
|
} else {
|
|
$config['alter-html']['only-for-webps-that-exists'] = true;
|
|
}
|
|
|
|
$config['alter-html']['replacement'] = $sanitized['alter-html-replacement'];
|
|
$config['alter-html']['hooks'] = $sanitized['alter-html-hooks'];
|
|
$config['alter-html']['hostname-aliases'] = $sanitized['alter-html-hostname-aliases'];
|
|
|
|
|
|
// Set options that are available in all operation modes, except the "no-conversion" mode
|
|
if ($sanitized['operation-mode'] != 'no-conversion') {
|
|
|
|
$config['enable-redirection-to-webp-realizer'] = $sanitized['enable-redirection-to-webp-realizer'];
|
|
|
|
// Metadata
|
|
// --------
|
|
$config['metadata'] = $sanitized['metadata'];
|
|
|
|
// Jpeg
|
|
// --------
|
|
$config['jpeg-encoding'] = $sanitized['jpeg-encoding'];
|
|
|
|
$auto = ($sanitized['quality-auto'] == 'auto_on');
|
|
$config['quality-auto'] = $auto;
|
|
if ($auto) {
|
|
$config['max-quality'] = $sanitized['max-quality'];
|
|
$config['quality-specific'] = $sanitized['quality-fallback'];
|
|
} else {
|
|
$config['max-quality'] = 80;
|
|
$config['quality-specific'] = $sanitized['quality-specific'];
|
|
}
|
|
|
|
$config['jpeg-enable-near-lossless'] = ($sanitized['jpeg-enable-near-lossless'] == 'on');
|
|
$config['jpeg-near-lossless'] = $sanitized['jpeg-near-lossless'];
|
|
|
|
|
|
// Png
|
|
// --------
|
|
$config['png-encoding'] = $sanitized['png-encoding'];
|
|
$config['png-quality'] = $sanitized['png-quality'];
|
|
$config['png-enable-near-lossless'] = ($sanitized['png-enable-near-lossless'] == 'on');
|
|
$config['png-near-lossless'] = $sanitized['png-near-lossless'];
|
|
$config['alpha-quality'] = $sanitized['alpha-quality'];
|
|
|
|
// Other conversion options
|
|
$config['convert-on-upload'] = $sanitized['convert-on-upload'];
|
|
$config['enable-logging'] = $sanitized['enable-logging'];
|
|
|
|
|
|
// Web Service
|
|
// -------------
|
|
|
|
$config['web-service'] = [
|
|
'enabled' => $sanitized['web-service-enabled'],
|
|
'whitelist' => $sanitized['whitelist']
|
|
];
|
|
|
|
// Set existing api keys in web service (we removed them from the json array, for security purposes)
|
|
if (isset($oldConfig['web-service']['whitelist'])) {
|
|
foreach ($oldConfig['web-service']['whitelist'] as $existingWhitelistEntry) {
|
|
foreach ($config['web-service']['whitelist'] as &$whitelistEntry) {
|
|
if ($whitelistEntry['uid'] == $existingWhitelistEntry['uid']) {
|
|
$whitelistEntry['api-key'] = $existingWhitelistEntry['api-key'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set changed api keys
|
|
foreach ($config['web-service']['whitelist'] as &$whitelistEntry) {
|
|
if (!empty($whitelistEntry['new-api-key'])) {
|
|
$whitelistEntry['api-key'] = $whitelistEntry['new-api-key'];
|
|
unset($whitelistEntry['new-api-key']);
|
|
}
|
|
}
|
|
|
|
// Converters
|
|
// -------------
|
|
|
|
$config['converters'] = $sanitized['converters'];
|
|
|
|
// remove converter ids
|
|
foreach ($config['converters'] as &$converter) {
|
|
unset ($converter['id']);
|
|
}
|
|
|
|
// Get existing wpc api key from old config
|
|
$existingWpcApiKey = '';
|
|
foreach ($oldConfig['converters'] as &$converter) {
|
|
if (isset($converter['converter']) && ($converter['converter'] == 'wpc')) {
|
|
if (isset($converter['options']['api-key'])) {
|
|
$existingWpcApiKey = $converter['options']['api-key'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set wpc api key in new config
|
|
// - either to the existing, or to a new
|
|
foreach ($config['converters'] as &$converter) {
|
|
if (isset($converter['converter']) && ($converter['converter'] == 'wpc')) {
|
|
unset($converter['options']['_api-key-non-empty']);
|
|
if (isset($converter['options']['new-api-key'])) {
|
|
$converter['options']['api-key'] = $converter['options']['new-api-key'];
|
|
unset($converter['options']['new-api-key']);
|
|
} else {
|
|
$converter['options']['api-key'] = $existingWpcApiKey;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$config['destination-structure'] = $sanitized['destination-structure'];
|
|
|
|
switch ($sanitized['operation-mode']) {
|
|
case 'varied-image-responses':
|
|
$config = array_merge($config, [
|
|
'redirect-to-existing-in-htaccess' => $sanitized['redirect-to-existing-in-htaccess'],
|
|
'destination-folder' => $sanitized['destination-folder'],
|
|
'destination-extension' => (($sanitized['destination-folder'] == 'mingled') ? $sanitized['destination-extension'] : 'append'),
|
|
'enable-redirection-to-converter' => $sanitized['enable-redirection-to-converter'],
|
|
]);
|
|
break;
|
|
case 'cdn-friendly':
|
|
$config = array_merge($config, [
|
|
'destination-folder' => $sanitized['destination-folder'],
|
|
'destination-extension' => (($sanitized['destination-folder'] == 'mingled') ? $sanitized['destination-extension'] : 'append'),
|
|
'enable-redirection-to-converter' => $sanitized['enable-redirection-to-converter'], // PS: its called "autoconvert" in this mode
|
|
]);
|
|
break;
|
|
case 'no-conversion':
|
|
$config = array_merge($config, [
|
|
'redirect-to-existing-in-htaccess' => $sanitized['redirect-to-existing-in-htaccess'],
|
|
'destination-extension' => $sanitized['destination-extension'],
|
|
]);
|
|
break;
|
|
case 'tweaked':
|
|
$config = array_merge($config, [
|
|
'enable-redirection-to-converter' => $sanitized['enable-redirection-to-converter'],
|
|
'only-redirect-to-converter-for-webp-enabled-browsers' => $sanitized['only-redirect-to-converter-for-webp-enabled-browsers'],
|
|
'only-redirect-to-converter-on-cache-miss' => $sanitized['only-redirect-to-converter-on-cache-miss'],
|
|
'do-not-pass-source-in-query-string' => $sanitized['do-not-pass-source-in-query-string'],
|
|
'redirect-to-existing-in-htaccess' => $sanitized['redirect-to-existing-in-htaccess'],
|
|
'destination-folder' => $sanitized['destination-folder'],
|
|
'destination-extension' => (($sanitized['destination-folder'] == 'mingled') ? $sanitized['destination-extension'] : 'append'),
|
|
'fail' => $sanitized['fail'],
|
|
'success-response' => $sanitized['success-response'],
|
|
]);
|
|
break;
|
|
}
|
|
|
|
if ($sanitized['operation-mode'] != $sanitized['change-operation-mode']) {
|
|
|
|
// Operation mode changed!
|
|
$config['operation-mode'] = $sanitized['change-operation-mode'];
|
|
$config = Config::applyOperationMode($config);
|
|
|
|
if ($config['operation-mode'] == 'varied-image-responses') {
|
|
// changing to "varied image responses" mode should enable
|
|
// the redirect-to-existing-in-htaccess option
|
|
$config['redirect-to-existing-in-htaccess'] = true;
|
|
}
|
|
|
|
if ($config['operation-mode'] == 'no-conversion') {
|
|
// No conversion probably means that there are webps in the system not generated by
|
|
// webp express. Schedule a task to mark those that are bigger than originals
|
|
wp_schedule_single_event(time() + 30, 'webp_express_task_bulk_update_dummy_files');
|
|
}
|
|
}
|
|
|
|
// If we are going to save .htaccess, run and store capability tests first
|
|
// (we should only store results when .htaccess is updated as well)
|
|
if ($sanitized['force'] || HTAccessRules::doesRewriteRulesNeedUpdate($config)) {
|
|
Config::runAndStoreCapabilityTests($config);
|
|
}
|
|
|
|
|
|
$config['environment-when-config-was-saved'] = [
|
|
'doc-root-available' => PathHelper::isDocRootAvailable(),
|
|
'doc-root-resolvable' => PathHelper::isDocRootAvailableAndResolvable(),
|
|
'doc-root-usable-for-structuring' => Paths::canUseDocRootForRelPaths(),
|
|
'image-roots' => Paths::getImageRootsDef(),
|
|
'document-root' => null,
|
|
];
|
|
|
|
if (PathHelper::isDocRootAvailable()) {
|
|
$config['document-root'] = $_SERVER['DOCUMENT_ROOT'];
|
|
}
|
|
|
|
// SAVE!
|
|
// -----
|
|
$result = Config::saveConfigurationAndHTAccess($config, $sanitized['force']);
|
|
|
|
// Handle results
|
|
// ---------------
|
|
|
|
if (!$result['saved-both-config']) {
|
|
if (!$result['saved-main-config']) {
|
|
Messenger::addMessage(
|
|
'error',
|
|
'Failed saving configuration file.<br>' .
|
|
'Current file permissions are preventing WebP Express to save configuration to: "' . Paths::getConfigFileName() . '"'
|
|
);
|
|
} else {
|
|
Messenger::addMessage(
|
|
'error',
|
|
'Failed saving options file. Check file permissions<br>' .
|
|
'Tried to save to: "' . Paths::getWodOptionsFileName() . '"'
|
|
);
|
|
|
|
}
|
|
} else {
|
|
$changeFolder = ($config['destination-folder'] != $oldConfig['destination-folder']);
|
|
$changeExtension = ($config['destination-extension'] != $oldConfig['destination-extension']);
|
|
$changeStructure = ($config['destination-structure'] != $oldConfig['destination-structure']);
|
|
|
|
if ($changeFolder || $changeExtension || $changeStructure) {
|
|
|
|
$relocate = $changeFolder || $changeStructure;
|
|
$rename = $changeExtension;
|
|
|
|
$actionPastTense = '';
|
|
if ($rename && $relocate) {
|
|
$actionPastTense = 'relocated and renamed';
|
|
$actionPresentTense = 'relocate and rename';
|
|
} else {
|
|
if ($rename) {
|
|
$actionPastTense = 'renamed';
|
|
$actionPresentTense = 'rename';
|
|
} else {
|
|
$actionPastTense = 'relocated';
|
|
$actionPresentTense = 'relocate';
|
|
}
|
|
}
|
|
|
|
list($numFilesMoved, $numFilesFailedMoving) = CacheMover::move($config, $oldConfig);
|
|
if ($numFilesFailedMoving == 0) {
|
|
if ($numFilesMoved == 0) {
|
|
Messenger::addMessage(
|
|
'notice',
|
|
'No cached webp files needed to be ' . $actionPastTense
|
|
);
|
|
|
|
} else {
|
|
Messenger::addMessage(
|
|
'success',
|
|
'The webp files was ' . $actionPastTense . ' (' . $actionPastTense . ' ' . $numFilesMoved . ' images)'
|
|
);
|
|
}
|
|
} else {
|
|
if ($numFilesMoved == 0) {
|
|
Messenger::addMessage(
|
|
'warning',
|
|
'No webp files could not be ' . $actionPastTense . ' (failed to ' . $actionPresentTense . ' ' . $numFilesFailedMoving . ' images)'
|
|
);
|
|
} else {
|
|
Messenger::addMessage(
|
|
'warning',
|
|
'Some webp files could not be ' . $actionPastTense . ' (failed to ' . $actionPresentTense . ' ' . $numFilesFailedMoving . ' images, but successfully ' . $actionPastTense . ' ' . $numFilesMoved . ' images)'
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$result['rules-needed-update']) {
|
|
Messenger::addMessage(
|
|
'success',
|
|
'Configuration saved. Rewrite rules did not need to be updated. ' . HTAccess::testLinks($config)
|
|
);
|
|
} else {
|
|
Messenger::addMessage(
|
|
'success',
|
|
'Configuration was saved.'
|
|
);
|
|
HTAccess::showSaveRulesMessages($result['htaccess-result']);
|
|
}
|
|
}
|
|
|
|
wp_redirect(Paths::getSettingsUrl());
|
|
|
|
exit();
|