PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / trunk
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization vtrunk
4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 All 135 releases
← All changes | inc/settings.php +544 -164 3.1.0 → trunk View file →
@@ -1,17 +1,23 @@
1 1 <?php
2 2
3 +use Optimole\Sdk\ValueObject\Position;
4 +
3 5 /**
4 6 * Class Optml_Settings.
5 7 */
6 8 class Optml_Settings {
7 9 use Optml_Normalizer;
10 +
8 11 const FILTER_EXT = 'extension';
9 12 const FILTER_URL = 'page_url';
13 + const FILTER_URL_MATCH = 'page_url_match';
10 14 const FILTER_FILENAME = 'filename';
11 15 const FILTER_CLASS = 'class';
12 16 const FILTER_TYPE_LAZYLOAD = 'lazyload';
13 17 const FILTER_TYPE_OPTIMIZE = 'optimize';
18 + const OPTML_USER_EMAIL = 'optml_user_email';
19 + const INDIVIDUAL_CACHE_TOKENS_KEY = '_optml_cache_tokens_individual';
14 20 /**
15 21 * Holds an array of possible settings to alter via wp cli or wp-config constants.
16 22 *
17 23 * @var array Whitelisted settings.
@@ -21,8 +27,9 @@
21 27 'quality' => 'int',
22 28 'lazyload' => 'bool',
23 29 'lazyload_placeholder' => 'bool',
24 30 'network_optimization' => 'bool',
31 + 'autoquality' => 'bool',
25 32 'img_to_video' => 'bool',
26 33 'resize_smart' => 'bool',
27 34 'retina_images' => 'bool',
28 35 'native_lazyload' => 'bool',
@@ -31,48 +38,74 @@
31 38 'scale' => 'bool',
32 39 'cdn' => 'bool',
33 40 ];
34 41 /**
42 + * Holds the status of the auto connect hook.
43 + *
44 + * @var boolean Whether or not the auto connect action is hooked.
45 + */
46 + private static $auto_connect_hooked = false;
47 +
48 + /**
35 49 * Default settings schema.
36 50 *
37 51 * @var array Settings schema.
38 52 */
39 53 private $default_schema = [
40 - 'api_key' => '',
41 - 'service_data' => '',
42 - 'cache_buster' => '',
43 - 'cdn' => 'disabled',
44 - 'max_height' => 1500,
45 - 'max_width' => 2000,
46 - 'admin_bar_item' => 'enabled',
47 - 'lazyload' => 'disabled',
48 - 'scale' => 'disabled',
49 - 'network_optimization' => 'disabled',
50 - 'lazyload_placeholder' => 'disabled',
51 - 'bg_replacer' => 'enabled',
52 - 'video_lazyload' => 'disabled',
53 - 'retina_images' => 'disabled',
54 - 'resize_smart' => 'disabled',
55 - 'filters' => [],
56 - 'cloud_sites' => [ 'all' => 'true' ],
57 - 'watchers' => '',
58 - 'quality' => 'auto',
59 - 'wm_id' => - 1,
60 - 'wm_opacity' => 1,
61 - 'wm_position' => Optml_Resize::GRAVITY_SOUTH_EAST,
62 - 'wm_x' => 0,
63 - 'wm_y' => 0,
64 - 'wm_scale' => 0,
65 - 'image_replacer' => 'enabled',
66 - 'img_to_video' => 'disabled',
67 - 'css_minify' => 'enabled',
68 - 'js_minify' => 'disabled',
69 - 'report_script' => 'disabled',
70 - 'native_lazyload' => 'disabled',
71 - 'offload_media' => 'disabled',
72 - 'cloud_images' => 'disabled',
73 - 'skip_lazyload_images' => 3,
74 -
54 + 'api_key' => '',
55 + 'service_data' => '',
56 + 'cache_buster' => '',
57 + 'cache_buster_assets' => '',
58 + 'cache_buster_images' => '',
59 + 'cdn' => 'disabled',
60 + 'admin_bar_item' => 'enabled',
61 + 'lazyload' => 'disabled',
62 + 'scale' => 'disabled', // Due to legacy reasons the disabled state means that the scale is enabled and the enabled state means that the scale is disabled.
63 + 'network_optimization' => 'enabled',
64 + 'lazyload_placeholder' => 'enabled',
65 + 'bg_replacer' => 'enabled',
66 + 'video_lazyload' => 'enabled',
67 + 'retina_images' => 'disabled',
68 + 'lazyload_type' => 'fixed|viewport',
69 + 'limit_dimensions' => 'enabled',
70 + 'limit_height' => 4000,
71 + 'limit_width' => 1920,
72 + 'resize_smart' => 'disabled',
73 + 'no_script' => 'disabled',
74 + 'filters' => [],
75 + 'compression_mode' => 'custom',
76 + 'cloud_sites' => [ 'all' => 'true' ],
77 + 'watchers' => '',
78 + 'quality' => 80,
79 + 'wm_id' => - 1,
80 + 'wm_opacity' => 1,
81 + 'wm_position' => Position::SOUTH_EAST,
82 + 'wm_x' => 0,
83 + 'wm_y' => 0,
84 + 'wm_scale' => 0,
85 + 'image_replacer' => 'enabled',
86 + 'img_to_video' => 'disabled',
87 + 'css_minify' => 'enabled',
88 + 'js_minify' => 'disabled',
89 + 'avif' => 'enabled', // legacy setting, is no longer used in the UI
90 + 'autoquality' => 'enabled',
91 + 'native_lazyload' => 'disabled',
92 + 'offload_media' => 'disabled',
93 + 'transfer_status' => 'disabled',
94 + 'cloud_images' => 'enabled',
95 + 'strip_metadata' => 'enabled',
96 + 'skip_lazyload_images' => 2,
97 + 'defined_image_sizes' => [],
98 + 'banner_frontend' => 'disabled',
99 + 'offloading_status' => 'disabled',
100 + 'rollback_status' => 'disabled',
101 + 'best_format' => 'enabled',
102 + 'offload_limit_reached' => 'disabled',
103 + 'offload_limit' => 50000,
104 + 'placeholder_color' => '',
105 + 'show_offload_finish_notice' => '',
106 + 'show_badge_icon' => 'disabled',
107 + 'badge_position' => 'left',
75 108 ];
76 109 /**
77 110 * Option key.
78 111 *
@@ -83,30 +116,52 @@
83 116 * Holds all options from db.
84 117 *
85 118 * @var array All options.
86 119 */
87 - private $options;
120 + private static $options;
88 121
89 122 /**
123 + * Cache for site settings to avoid multiple calls to get_option on the same request.
124 + *
125 + * @var array<string, mixed>|null Cached site settings.
126 + */
127 + private static $site_settings_cache = null;
128 +
129 + /**
90 130 * Optml_Settings constructor.
91 131 */
92 132 public function __construct() {
133 + $this->default_schema['cloud_sites'] = $this->get_cloud_sites_whitelist_default();
134 +
93 135 $this->namespace = OPTML_NAMESPACE . '_settings';
94 136 $this->default_schema = apply_filters( 'optml_default_settings', $this->default_schema );
95 - $this->options = wp_parse_args( get_option( $this->namespace, $this->default_schema ), $this->default_schema );
137 + self::$options = wp_parse_args( get_option( $this->namespace, $this->default_schema ), $this->default_schema );
96 138
97 139 if ( defined( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && $this->to_boolean( constant( 'OPTIML_ENABLED_MU' ) ) && constant( 'OPTIML_MU_SITE_ID' ) ) {
98 140 switch_to_blog( constant( 'OPTIML_MU_SITE_ID' ) );
99 - $this->options = wp_parse_args( get_option( $this->namespace, $this->default_schema ), $this->default_schema );
141 + self::$options = wp_parse_args( get_option( $this->namespace, $this->default_schema ), $this->default_schema );
100 142 restore_current_blog();
101 143 }
102 144
103 145 if ( defined( 'OPTIML_USE_ENV' ) && constant( 'OPTIML_USE_ENV' ) && $this->to_boolean( constant( 'OPTIML_USE_ENV' ) ) ) {
146 +
147 + if ( defined( 'OPTIML_API_KEY' )
148 + && constant( 'OPTIML_API_KEY' ) !== ''
149 + ) {
150 + if ( ! $this->is_connected() && ! self::$auto_connect_hooked ) {
151 + self::$auto_connect_hooked = true;
152 + add_action(
153 + 'plugins_loaded',
154 + [ $this, 'auto_connect' ]
155 + );
156 + }
157 + }
158 +
104 159 foreach ( self::$whitelisted_settings as $key => $type ) {
105 160 $env_key = 'OPTIML_' . strtoupper( $key );
106 161 if ( defined( $env_key ) && constant( $env_key ) ) {
107 162 $value = constant( $env_key );
108 - if ( $type === 'bool' && ( $value === '' || ! in_array(
163 + if ( $type === 'bool' && ( (string) $value === '' || ! in_array(
109 164 $value,
110 165 [
111 166 'on',
112 167 'off',
@@ -115,13 +170,13 @@
115 170 ) ) ) {
116 171 continue;
117 172 }
118 173
119 - if ( $type === 'int' && ( $value === '' || (int) $value > 100 || (int) $value < 0 ) ) {
174 + if ( $type === 'int' && ( (string) $value === '' || (int) $value > 100 || (int) $value < 0 ) ) {
120 175 continue;
121 176 }
122 - $sanitized_value = ( $type === 'bool' ) ? ( $value === 'on' ? 'enabled' : 'disabled' ) : (int) $value;
123 - $this->options[ $key ] = $sanitized_value;
177 + $sanitized_value = ( $type === 'bool' ) ? ( $value === 'on' ? 'enabled' : 'disabled' ) : (int) $value;
178 + self::$options[ $key ] = $sanitized_value;
124 179 }
125 180 }
126 181 }
127 182 }
@@ -126,48 +181,66 @@
126 181 }
127 182 }
128 183
129 184 /**
130 - * Return filter definitions.
185 + * Check if the user is connected to Optimole.
131 186 *
132 - * @return mixed|null Filter values.
187 + * @return bool Connection status.
133 188 */
134 - public function get_watchers() {
189 + public function is_connected() {
190 + $service_data = $this->get( 'service_data' );
191 + if ( ! isset( $service_data['cdn_key'] ) ) {
192 + return false;
193 + }
194 + if ( empty( $service_data ['cdn_key'] ) || empty( $service_data['cdn_secret'] ) ) {
195 + return false;
196 + }
135 197
136 - return $this->get( 'watchers' );
198 + return true;
199 + }
137 200
201 + /**
202 + * Get setting value by key.
203 + *
204 + * @param string $key Key to search against.
205 + *
206 + * @return mixed|null Setting value.
207 + */
208 + public function get( $key ) {
209 + if ( ! $this->is_allowed( $key ) ) {
210 + return null;
211 + }
212 +
213 + return isset( self::$options[ $key ] ) ? self::$options[ $key ] : '';
138 214 }
139 215
140 216 /**
141 - * Return filter definitions.
217 + * Check if key is allowed.
142 218 *
143 - * @return mixed|null Filter values.
219 + * @param string $key Is key allowed or not.
220 + *
221 + * @return bool Is key allowed or not.
144 222 */
145 - public function get_filters() {
223 + private function is_allowed( $key ) {
224 + return isset( $this->default_schema[ $key ] );
225 + }
146 226
147 - $filters = $this->get( 'filters' );
148 - if ( ! isset( $filters[ self::FILTER_TYPE_LAZYLOAD ] ) ) {
149 - $filters[ self::FILTER_TYPE_LAZYLOAD ] = [];
150 - }
151 - if ( ! isset( $filters[ self::FILTER_TYPE_OPTIMIZE ] ) ) {
152 - $filters[ self::FILTER_TYPE_OPTIMIZE ] = [];
153 - }
154 - foreach ( $filters as $filter_key => $filter_rules ) {
155 - if ( ! isset( $filter_rules[ self::FILTER_EXT ] ) ) {
156 - $filters[ $filter_key ][ self::FILTER_EXT ] = [];
157 - }
158 - if ( ! isset( $filter_rules[ self::FILTER_FILENAME ] ) ) {
159 - $filters[ $filter_key ][ self::FILTER_FILENAME ] = [];
160 - }
161 - if ( ! isset( $filter_rules[ self::FILTER_URL ] ) ) {
162 - $filters[ $filter_key ][ self::FILTER_URL ] = [];
163 - }
164 - if ( ! isset( $filter_rules[ self::FILTER_CLASS ] ) ) {
165 - $filters[ $filter_key ][ self::FILTER_CLASS ] = [];
166 - }
167 - }
227 + /**
228 + * Auto connect action.
229 + */
230 + public function auto_connect() {
168 231
169 - return $filters;
232 + /**
233 + * Connect rest request.
234 + *
235 + * @var WP_REST_Request<array{api_key: string, application?: string}>
236 + */
237 + $request = new WP_REST_Request( 'POST' );
238 + $request->set_param( 'api_key', constant( 'OPTIML_API_KEY' ) );
239 + Optml_Main::instance()->rest->connect( $request );
240 +
241 + remove_action( 'plugins_loaded', [ $this, 'auto_connect' ] );
242 + self::$auto_connect_hooked = false;
170 243 }
171 244
172 245 /**
173 246 * Process settings.
@@ -176,9 +249,11 @@
176 249 *
177 250 * @return array
178 251 */
179 252 public function parse_settings( $new_settings ) {
180 - $sanitized = [];
253 + $sanitized = [];
254 + $sanitized_value = '';
255 +
181 256 foreach ( $new_settings as $key => $value ) {
182 257 switch ( $key ) {
183 258 case 'admin_bar_item':
184 259 case 'lazyload':
@@ -187,40 +262,76 @@
187 262 case 'cdn':
188 263 case 'network_optimization':
189 264 case 'lazyload_placeholder':
190 265 case 'retina_images':
266 + case 'limit_dimensions':
191 267 case 'resize_smart':
192 268 case 'bg_replacer':
193 269 case 'video_lazyload':
194 - case 'report_script':
270 + case 'avif':
195 271 case 'offload_media':
196 272 case 'cloud_images':
273 + case 'autoquality':
197 274 case 'img_to_video':
198 275 case 'css_minify':
199 276 case 'js_minify':
200 277 case 'native_lazyload':
278 + case 'strip_metadata':
279 + case 'no_script':
280 + case 'banner_frontend':
281 + case 'offloading_status':
282 + case 'rollback_status':
283 + case 'best_format':
284 + case 'offload_limit_reached':
285 + case 'show_badge_icon':
201 286 $sanitized_value = $this->to_map_values( $value, [ 'enabled', 'disabled' ], 'enabled' );
202 287 break;
203 - case 'max_width':
204 - case 'max_height':
288 + case 'offload_limit':
289 + $sanitized_value = absint( $value );
290 + break;
291 + case 'limit_height':
292 + case 'limit_width':
205 293 $sanitized_value = $this->to_bound_integer( $value, 100, 5000 );
206 294 break;
207 295 case 'quality':
208 - $sanitized_value = $this->to_bound_integer( $value, 1, 100 );
296 + if ( 'mauto' !== $value ) {
297 + $sanitized_value = $this->to_bound_integer( $value, 50, 100 );
298 + }
209 299 break;
210 300 case 'wm_id':
211 301 $sanitized_value = intval( $value );
212 302 break;
303 + case 'show_offload_finish_notice':
304 + $sanitized_value = $this->to_map_values( $value, [ 'offload', 'rollback' ], '' );
305 + break;
306 + case 'cache_buster_assets':
307 + case 'cache_buster_images':
213 308 case 'cache_buster':
214 - $sanitized_value = is_string( $value ) ? $value : '';
309 + $sanitized_value = is_string( $value ) ? sanitize_text_field( $value ) : '';
215 310 break;
311 + case 'lazyload_type':
312 + $sanitized_value = $this->to_map_values( $value, [ 'fixed', 'viewport', 'all', 'fixed|viewport' ], 'fixed' );
313 + break;
314 + case 'compression_mode':
315 + $sanitized_value = $this->to_map_values( $value, [ 'speed_optimized', 'quality_optimized', 'custom' ], 'custom' );
316 + break;
216 317 case 'cloud_sites':
217 - $current_sites = $this->get( 'cloud_sites' );
318 + $current_sites = $this->get( 'cloud_sites' );
218 319 $sanitized_value = array_replace_recursive( $current_sites, $value );
219 320 if ( isset( $value['all'] ) && $value['all'] === 'true' ) {
220 321 $sanitized_value = [ 'all' => 'true' ];
221 322 }
222 323 break;
324 + case 'defined_image_sizes':
325 + $current_sizes = $this->get( 'defined_image_sizes' );
326 + foreach ( $value as $size_name => $size_value ) {
327 + if ( $size_value === 'remove' ) {
328 + unset( $current_sizes[ $size_name ] );
329 + unset( $value[ $size_name ] );
330 + }
331 + }
332 + $sanitized_value = array_replace_recursive( $current_sizes, $value );
333 + break;
223 334 case 'filters':
224 335 $current_filters = $this->get_filters();
225 336 $sanitized_value = array_replace_recursive( $current_filters, $value );
226 337 // Remove falsy vars.
@@ -235,9 +346,11 @@
235 346 }
236 347 }
237 348 break;
238 349 case 'watchers':
239 - $sanitized_value = $value;
350 + case 'placeholder_color':
351 + case 'transfer_status':
352 + $sanitized_value = sanitize_text_field( $value );
240 353 break;
241 354 case 'skip_lazyload_images':
242 355 $sanitized_value = $this->to_bound_integer( $value, 0, 100 );
243 356 break;
@@ -250,21 +363,24 @@
250 363 case 'wm_position':
251 364 $sanitized_value = $this->to_map_values(
252 365 $value,
253 366 [
254 - Optml_Resize::GRAVITY_NORTH,
255 - Optml_Resize::GRAVITY_NORTH_EAST,
256 - Optml_Resize::GRAVITY_NORTH_WEST,
257 - Optml_Resize::GRAVITY_CENTER,
258 - Optml_Resize::GRAVITY_EAST,
259 - Optml_Resize::GRAVITY_WEST,
260 - Optml_Resize::GRAVITY_SOUTH_EAST,
261 - Optml_Resize::GRAVITY_SOUTH,
262 - Optml_Resize::GRAVITY_SOUTH_WEST,
367 + Position::NORTH,
368 + Position::NORTH_EAST,
369 + Position::NORTH_WEST,
370 + Position::CENTER,
371 + Position::EAST,
372 + Position::WEST,
373 + Position::SOUTH_EAST,
374 + Position::SOUTH,
375 + Position::SOUTH_WEST,
263 376 ],
264 - Optml_Resize::GRAVITY_SOUTH_EAST
377 + Position::SOUTH_EAST
265 378 );
266 379 break;
380 + case 'badge_position':
381 + $sanitized_value = $this->to_map_values( $value, [ 'left', 'right' ], 'right' );
382 + break;
267 383 default:
268 384 $sanitized_value = '';
269 385 break;
270 386
@@ -277,76 +393,142 @@
277 393 return $sanitized;
278 394 }
279 395
280 396 /**
281 - * Update settings.
397 + * Return filter definitions.
282 398 *
283 - * @param string $key Settings key.
284 - * @param mixed $value Settings value.
285 - *
286 - * @return bool Update result.
399 + * @return mixed|null Filter values.
287 400 */
288 - public function update( $key, $value ) {
289 - if ( ! $this->is_allowed( $key ) ) {
290 - return false;
401 + public function get_filters() {
402 +
403 + $filters = $this->get( 'filters' );
404 + if ( ! isset( $filters[ self::FILTER_TYPE_LAZYLOAD ] ) ) {
405 + $filters[ self::FILTER_TYPE_LAZYLOAD ] = [];
291 406 }
292 - // If we try to update from a website which is not the main OPTML blog, bail.
293 - if ( defined( 'OPTIML_ENABLED_MU' ) && constant( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && constant( 'OPTIML_MU_SITE_ID' ) &&
294 - intval( constant( 'OPTIML_MU_SITE_ID' ) ) !== get_current_blog_id()
295 - ) {
296 - return false;
407 + if ( ! isset( $filters[ self::FILTER_TYPE_OPTIMIZE ] ) ) {
408 + $filters[ self::FILTER_TYPE_OPTIMIZE ] = [];
297 409 }
298 - $opt = $this->options;
299 - $opt[ $key ] = $value;
300 - $update = update_option( $this->namespace, $opt, false );
301 - if ( $update ) {
302 - $this->options = $opt;
410 + foreach ( $filters as $filter_key => $filter_rules ) {
411 + if ( ! isset( $filter_rules[ self::FILTER_EXT ] ) ) {
412 + $filters[ $filter_key ][ self::FILTER_EXT ] = [];
413 + }
414 + if ( ! isset( $filter_rules[ self::FILTER_FILENAME ] ) ) {
415 + $filters[ $filter_key ][ self::FILTER_FILENAME ] = [];
416 + }
417 + if ( ! isset( $filter_rules[ self::FILTER_URL ] ) ) {
418 + $filters[ $filter_key ][ self::FILTER_URL ] = [];
419 + }
420 + if ( ! isset( $filter_rules[ self::FILTER_URL_MATCH ] ) ) {
421 + $filters[ $filter_key ][ self::FILTER_URL_MATCH ] = [];
422 + }
423 + if ( ! isset( $filter_rules[ self::FILTER_CLASS ] ) ) {
424 + $filters[ $filter_key ][ self::FILTER_CLASS ] = [];
425 + }
303 426 }
304 427
305 - return $update;
428 + return $filters;
306 429 }
307 430
308 431 /**
309 - * Check if key is allowed.
432 + * Update frontend banner setting from remote.
310 433 *
311 - * @param string $key Is key allowed or not.
434 + * @param bool $value Value.
312 435 *
313 - * @return bool Is key allowed or not.
436 + * @return bool
314 437 */
315 - private function is_allowed( $key ) {
316 - return isset( $this->default_schema[ $key ] );
438 + public function update_frontend_banner_from_remote( $value ) {
439 + if ( ! $this->is_main_mu_site() ) {
440 + return false;
441 + }
442 +
443 + $opts = self::$options;
444 + $opts['banner_frontend'] = $value ? 'enabled' : 'disabled';
445 +
446 + $update = update_option( $this->namespace, $opts, false );
447 +
448 + if ( $update ) {
449 + self::$options = $opts;
450 + $this->set_settings_cache( 'banner_frontend', $opts['banner_frontend'] );
451 + }
452 +
453 + return $update;
317 454 }
318 455
319 456 /**
320 - * Check if the user is connected to Optimole.
457 + * Update settings.
321 458 *
322 - * @return bool Connection status.
459 + * @param string $key Settings key.
460 + * @param mixed $value Settings value.
461 + *
462 + * @return bool Update result.
323 463 */
324 - public function is_connected() {
325 - $service_data = $this->get( 'service_data' );
326 - if ( ! isset( $service_data['cdn_key'] ) ) {
464 + public function update( $key, $value ) {
465 + if ( ! $this->is_allowed( $key ) ) {
327 466 return false;
328 467 }
329 - if ( empty( $service_data ['cdn_key'] ) || empty( $service_data['cdn_secret'] ) ) {
468 +
469 + if ( ! $this->is_main_mu_site() ) {
330 470 return false;
331 471 }
472 + $opt = self::$options;
332 473
333 - return true;
474 + if ( $key === 'banner_frontend' ) {
475 + $api = new Optml_Api();
476 + $service_data = $this->get( 'service_data' );
477 + $application = isset( $service_data['cdn_key'] ) ? $service_data['cdn_key'] : '';
478 + $response = $api->update_extra_visits( $opt['api_key'], $value, $application );
479 + }
480 +
481 + $opt[ $key ] = $value;
482 +
483 + $update = update_option( $this->namespace, $opt, false );
484 +
485 + if ( $update ) {
486 + self::$options = $opt;
487 + $this->set_settings_cache( $key, $value );
488 + }
489 + if ( apply_filters( 'optml_dont_trigger_settings_updated', false ) === false ) {
490 + /**
491 + * Fires when Optimole settings are updated.
492 + *
493 + * This action is triggered after settings have been successfully updated
494 + * and provides an opportunity for other plugins or themes to react to
495 + * Optimole configuration changes.
496 + */
497 + do_action( 'optml_settings_updated' );
498 + static $cleared = false;
499 + if ( $cleared ) {
500 + return $update;
501 + }
502 + $cleared = true;
503 + /**
504 + * Fires when Optimole cache needs to be cleared after settings update.
505 + *
506 + * This action is triggered after settings are updated to ensure that
507 + * any cached data is refreshed with the new configuration.
508 + *
509 + * @param bool|string $location Whether to clear the cache globally or just for a particular request.
510 + * You can pass a string with the URL to clear the cache for a particular URL only.
511 + */
512 + do_action( 'optml_clear_cache', true );
513 + }
514 + return $update;
334 515 }
335 516
336 517 /**
337 - * Get setting value by key.
518 + * Check that we're on the main OPTML blog.
338 519 *
339 - * @param string $key Key to search against.
340 - *
341 - * @return mixed|null Setting value.
520 + * @return bool
342 521 */
343 - public function get( $key ) {
344 - if ( ! $this->is_allowed( $key ) ) {
345 - return null;
522 + private function is_main_mu_site() {
523 + // If we try to update from a website which is not the main OPTML blog, bail.
524 + if ( defined( 'OPTIML_ENABLED_MU' ) && constant( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && constant( 'OPTIML_MU_SITE_ID' ) &&
525 + intval( constant( 'OPTIML_MU_SITE_ID' ) ) !== get_current_blog_id()
526 + ) {
527 + return false;
346 528 }
347 529
348 - return isset( $this->options[ $key ] ) ? $this->options[ $key ] : '';
530 + return true;
349 531 }
350 532
351 533 /**
352 534 * Return site settings.
@@ -353,42 +535,64 @@
353 535 *
354 536 * @return array Site settings.
355 537 */
356 538 public function get_site_settings() {
539 + if ( self::$site_settings_cache !== null ) {
540 + return self::$site_settings_cache;
541 + }
542 +
357 543 $service_data = $this->get( 'service_data' );
358 - $whitelist = [];
544 + $whitelist = [];
359 545 if ( isset( $service_data['whitelist'] ) ) {
360 546 $whitelist = $service_data['whitelist'];
361 547 }
362 - return [
363 - 'quality' => $this->get_quality(),
364 - 'admin_bar_item' => $this->get( 'admin_bar_item' ),
365 - 'lazyload' => $this->get( 'lazyload' ),
366 - 'network_optimization' => $this->get( 'network_optimization' ),
367 - 'retina_images' => $this->get( 'retina_images' ),
368 - 'lazyload_placeholder' => $this->get( 'lazyload_placeholder' ),
369 - 'skip_lazyload_images' => $this->get( 'skip_lazyload_images' ),
370 - 'bg_replacer' => $this->get( 'bg_replacer' ),
371 - 'video_lazyload' => $this->get( 'video_lazyload' ),
372 - 'resize_smart' => $this->get( 'resize_smart' ),
373 - 'image_replacer' => $this->get( 'image_replacer' ),
374 - 'cdn' => $this->get( 'cdn' ),
375 - 'max_width' => $this->get( 'max_width' ),
376 - 'max_height' => $this->get( 'max_height' ),
377 - 'filters' => $this->get_filters(),
378 - 'cloud_sites' => $this->get( 'cloud_sites' ),
379 - 'watchers' => $this->get_watchers(),
380 - 'watermark' => $this->get_watermark(),
381 - 'img_to_video' => $this->get( 'img_to_video' ),
382 - 'scale' => $this->get( 'scale' ),
383 - 'css_minify' => $this->get( 'css_minify' ),
384 - 'js_minify' => $this->get( 'js_minify' ),
385 - 'native_lazyload' => $this->get( 'native_lazyload' ),
386 - 'report_script' => $this->get( 'report_script' ),
387 - 'offload_media' => $this->get( 'offload_media' ),
388 - 'cloud_images' => $this->get( 'cloud_images' ),
389 - 'whitelist_domains' => $whitelist,
548 +
549 + self::$site_settings_cache = [
550 + 'quality' => $this->get_quality(),
551 + 'admin_bar_item' => $this->get( 'admin_bar_item' ),
552 + 'lazyload' => $this->get( 'lazyload' ),
553 + 'network_optimization' => $this->get( 'network_optimization' ),
554 + 'retina_images' => $this->get( 'retina_images' ),
555 + 'limit_dimensions' => $this->get( 'limit_dimensions' ),
556 + 'limit_height' => $this->get( 'limit_height' ),
557 + 'limit_width' => $this->get( 'limit_width' ),
558 + 'lazyload_placeholder' => $this->get( 'lazyload_placeholder' ),
559 + 'skip_lazyload_images' => $this->get( 'skip_lazyload_images' ),
560 + 'bg_replacer' => $this->get( 'bg_replacer' ),
561 + 'video_lazyload' => $this->get( 'video_lazyload' ),
562 + 'resize_smart' => $this->get( 'resize_smart' ),
563 + 'no_script' => $this->get( 'no_script' ),
564 + 'lazyload_type' => $this->get( 'lazyload_type' ),
565 + 'compression_mode' => $this->get( 'compression_mode' ),
566 + 'image_replacer' => $this->get( 'image_replacer' ),
567 + 'cdn' => $this->get( 'cdn' ),
568 + 'filters' => $this->get_filters(),
569 + 'cloud_sites' => $this->get( 'cloud_sites' ),
570 + 'defined_image_sizes' => $this->get( 'defined_image_sizes' ),
571 + 'watchers' => $this->get_watchers(),
572 + 'watermark' => $this->get_watermark(),
573 + 'img_to_video' => $this->get( 'img_to_video' ), // @deprecated
574 + 'scale' => $this->get( 'scale' ),
575 + 'css_minify' => $this->get( 'css_minify' ),
576 + 'js_minify' => $this->get( 'js_minify' ),
577 + 'native_lazyload' => $this->get( 'native_lazyload' ),
578 + 'avif' => $this->get( 'avif' ),
579 + 'autoquality' => $this->get( 'autoquality' ),
580 + 'offload_media' => $this->get( 'offload_media' ),
581 + 'cloud_images' => $this->get( 'cloud_images' ),
582 + 'strip_metadata' => $this->get( 'strip_metadata' ),
583 + 'whitelist_domains' => $whitelist,
584 + 'banner_frontend' => $this->get( 'banner_frontend' ),
585 + 'offloading_status' => $this->get( 'offloading_status' ),
586 + 'rollback_status' => $this->get( 'rollback_status' ),
587 + 'best_format' => $this->get( 'best_format' ),
588 + 'offload_limit_reached' => $this->get( 'offload_limit_reached' ),
589 + 'placeholder_color' => $this->get( 'placeholder_color' ),
590 + 'show_offload_finish_notice' => $this->get( 'show_offload_finish_notice' ),
591 + 'show_badge_icon' => $this->get( 'show_badge_icon' ),
592 + 'badge_position' => $this->get( 'badge_position' ),
390 593 ];
594 + return self::$site_settings_cache;
391 595 }
392 596
393 597 /**
394 598 * Return quality factor.
@@ -396,8 +600,11 @@
396 600 * @return string Quality factor.
397 601 */
398 602 public function get_quality() {
399 603 $quality = $this->get( 'quality' );
604 + if ( $this->get( 'autoquality' ) === 'enabled' ) {
605 + return 'mauto';
606 + }
400 607 // Legacy compat.
401 608 if ( $quality === 'low' ) {
402 609 return 'high_c';
403 610 }
@@ -412,8 +619,18 @@
412 619 return $quality;
413 620 }
414 621
415 622 /**
623 + * Return filter definitions.
624 + *
625 + * @return mixed|null Filter values.
626 + */
627 + public function get_watchers() {
628 +
629 + return $this->get( 'watchers' );
630 + }
631 +
632 + /**
416 633 * Return an watermark array.
417 634 *
418 635 * @return array
419 636 */
@@ -437,8 +654,34 @@
437 654 return $this->get( 'resize_smart' ) === 'enabled';
438 655 }
439 656
440 657 /**
658 + * Check if best format is enabled.
659 + *
660 + * @return bool
661 + */
662 + public function is_best_format() {
663 + return $this->get( 'best_format' ) === 'enabled';
664 + }
665 + /**
666 + * Check if scale is enabled.
667 + *
668 + * @return bool Scale enabled
669 + */
670 + public function is_scale_enabled() {
671 + return $this->get( 'scale' ) === 'disabled'; // Due to legacy reasons the disabled state means that the scale is enabled and the enabled state means that the scale is disabled.
672 + }
673 +
674 + /**
675 + * Check if offload limit was reached.
676 + *
677 + * @return bool
678 + */
679 + public function is_offload_limit_reached() {
680 + return $this->get( 'offload_limit_reached' ) === 'enabled';
681 + }
682 +
683 + /**
441 684 * Get numeric quality used by the service.
442 685 *
443 686 * @return int Numeric quality.
444 687 */
@@ -461,8 +704,9 @@
461 704 }
462 705 if ( isset( $service_data['status'] ) && $service_data['status'] === 'inactive' ) {
463 706 return false;
464 707 }
708 +
465 709 return $this->to_boolean( $status );
466 710 }
467 711
468 712 /**
@@ -497,16 +741,16 @@
497 741 if ( ! isset( $service_data['cdn_key'] ) ) {
498 742 return '';
499 743 }
500 744
745 + if ( defined( 'OPTML_CUSTOM_DOMAIN' ) && constant( 'OPTML_CUSTOM_DOMAIN' ) ) {
746 + return parse_url( strtolower( OPTML_CUSTOM_DOMAIN ), PHP_URL_HOST );
747 + }
748 +
501 749 if ( isset( $service_data['is_cname_assigned'] ) && $service_data['is_cname_assigned'] === 'yes' && ! empty( $service_data['domain'] ) ) {
502 750 return strtolower( $service_data['domain'] );
503 751 }
504 752
505 - if ( defined( 'OPTML_CUSTOM_DOMAIN' ) && constant( 'OPTML_CUSTOM_DOMAIN' ) ) {
506 - return parse_url( strtolower( OPTML_CUSTOM_DOMAIN ), PHP_URL_HOST );
507 - }
508 -
509 753 return sprintf(
510 754 '%s.%s',
511 755 strtolower( $service_data['cdn_key'] ),
512 756 Optml_Config::$base_domain
@@ -518,16 +762,152 @@
518 762 *
519 763 * @return bool Reset action status.
520 764 */
521 765 public function reset() {
522 - $reset_schema = $this->default_schema;
523 - $reset_schema['filters'] = $this->options['filters'];
766 + $reset_schema = $this->default_schema;
767 + $reset_schema['filters'] = self::$options['filters'];
524 768
525 769 $update = update_option( $this->namespace, $reset_schema );
526 770 if ( $update ) {
527 - $this->options = $reset_schema;
771 + self::$options = $reset_schema;
772 + $this->set_settings_cache( 'filters', $reset_schema['filters'] );
528 773 }
774 + wp_unschedule_hook( Optml_Admin::SYNC_CRON );
775 + wp_unschedule_hook( Optml_Admin::ENRICH_CRON );
529 776
530 777 return $update;
531 778 }
532 779
780 + /**
781 + * Get raw settings value.
782 + *
783 + * @return array
784 + */
785 + public function get_raw_settings() {
786 + $raw_settings = get_option( $this->namespace, [] );
787 +
788 + return is_array( $raw_settings ) ? $raw_settings : [];
789 + }
790 +
791 +
792 + /**
793 + * Clear cache.
794 + *
795 + * @param string $type Cache type.
796 + *
797 + * @return string|WP_Error
798 + */
799 + public function clear_cache( $type = '' ) {
800 + $token = $this->get( 'cache_buster' );
801 + $token_images = $this->get( 'cache_buster_images' );
802 +
803 + // here is an individual cache tokens
804 + $individual = get_transient( self::INDIVIDUAL_CACHE_TOKENS_KEY ) ?: [];
805 + if ( ( empty( $type ) || $type === 'images' ) ) {
806 + if ( ! empty( $token_images ) ) {
807 + $token = $token_images;
808 + }
809 + } elseif ( $type === 'assets' ) {
810 + $token = $this->get( 'cache_buster_assets' );
811 + } else {
812 + $token = $individual[ crc32( $type ) ] ?? $token_images ?: $token;
813 + }
814 +
815 + $request = new Optml_Api();
816 + $data = $request->get_cache_token( $token, $type );
817 +
818 + if ( $data === false || is_wp_error( $data ) || empty( $data ) || ! isset( $data['token'] ) ) {
819 + $extra = '';
820 +
821 + if ( is_wp_error( $data ) ) {
822 + /**
823 + * Error from api.
824 + *
825 + * @var WP_Error $data Error object.
826 + */
827 + $extra = sprintf( /* translators: Error details */ __( '. ERROR details: %s', 'optimole-wp' ), $data->get_error_message() );
828 + }
829 +
830 + return new WP_Error( 'optimole_cache_buster_error', __( 'Can not get new token from Optimole service', 'optimole-wp' ) . $extra );
831 + }
832 +
833 + if ( empty( $type ) || $type === 'images' ) {
834 + set_transient( 'optml_cache_lock', 'yes', 5 * MINUTE_IN_SECONDS );
835 + $this->update( 'cache_buster_images', $data['token'] );
836 + // we delete individual cache tokens since this is a global cache clear.
837 + delete_transient( self::INDIVIDUAL_CACHE_TOKENS_KEY );
838 + } elseif ( $type === 'assets' ) {
839 + set_transient( 'optml_cache_lock_assets', 'yes', 5 * MINUTE_IN_SECONDS );
840 + $this->update( 'cache_buster_assets', $data['token'] );
841 + } else {
842 + $individual[ crc32( $type ) ] = $data['token'];
843 + set_transient( self::INDIVIDUAL_CACHE_TOKENS_KEY, $individual, 6 * HOUR_IN_SECONDS );
844 + }
845 +
846 + return $data['token'];
847 + }
848 +
849 + /**
850 + * Check if lazyload type is viewport.
851 + *
852 + * @return bool
853 + */
854 + public function is_lazyload_type_viewport() {
855 + return false !== strpos( $this->get( 'lazyload_type' ), 'viewport' );
856 + }
857 +
858 + /**
859 + * Check if lazyload type is all.
860 + *
861 + * @return bool
862 + */
863 + public function is_lazyload_type_all() {
864 + return $this->get( 'lazyload_type' ) === 'all';
865 + }
866 +
867 + /**
868 + * Check if lazyload type is fixed.
869 + *
870 + * @return bool
871 + */
872 + public function is_lazyload_type_fixed() {
873 + return false !== strpos( $this->get( 'lazyload_type' ), 'fixed' );
874 + }
875 + /**
876 + * Utility to check if offload is enabled.
877 + *
878 + * @return bool
879 + */
880 + public function is_offload_enabled() {
881 + return $this->get( 'offload_media' ) === 'enabled' || $this->get( 'rollback_status' ) !== 'disabled';
882 + }
883 +
884 + /**
885 + * Get cloud sites whitelist for current domain only.
886 + *
887 + * @return array
888 + */
889 + public function get_cloud_sites_whitelist_default() {
890 + $site_url = get_site_url();
891 +
892 + $site_url = preg_replace( '/^https?:\/\//', '', $site_url );
893 + $site_url = trim( $site_url, '/' );
894 +
895 + return [
896 + 'all' => 'false',
897 + $site_url => 'true',
898 + ];
899 + }
900 +
901 + /**
902 + * Set settings cache value.
903 + *
904 + * @param string $key Cache key.
905 + * @param mixed $value Cache value.
906 + * @return void
907 + */
908 + private function set_settings_cache( $key, $value ) {
909 + if ( self::$site_settings_cache !== null && isset( self::$site_settings_cache[ $key ] ) ) {
910 + self::$site_settings_cache[ $key ] = $value;
911 + }
912 + }
533 913 }