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 +560 -150 2.5.7 → 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,44 +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 - 'watchers' => '',
57 - 'quality' => 'auto',
58 - 'wm_id' => - 1,
59 - 'wm_opacity' => 1,
60 - 'wm_position' => Optml_Resize::GRAVITY_SOUTH_EAST,
61 - 'wm_x' => 0,
62 - 'wm_y' => 0,
63 - 'wm_scale' => 0,
64 - 'image_replacer' => 'enabled',
65 - 'img_to_video' => 'disabled',
66 - 'css_minify' => 'enabled',
67 - 'js_minify' => 'disabled',
68 - 'report_script' => 'disabled',
69 - 'native_lazyload' => 'disabled',
70 -
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',
71 108 ];
72 109 /**
73 110 * Option key.
74 111 *
@@ -79,30 +116,52 @@
79 116 * Holds all options from db.
80 117 *
81 118 * @var array All options.
82 119 */
83 - private $options;
120 + private static $options;
84 121
85 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 + /**
86 130 * Optml_Settings constructor.
87 131 */
88 132 public function __construct() {
133 + $this->default_schema['cloud_sites'] = $this->get_cloud_sites_whitelist_default();
134 +
89 135 $this->namespace = OPTML_NAMESPACE . '_settings';
90 136 $this->default_schema = apply_filters( 'optml_default_settings', $this->default_schema );
91 - $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 );
92 138
93 139 if ( defined( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && $this->to_boolean( constant( 'OPTIML_ENABLED_MU' ) ) && constant( 'OPTIML_MU_SITE_ID' ) ) {
94 140 switch_to_blog( constant( 'OPTIML_MU_SITE_ID' ) );
95 - $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 );
96 142 restore_current_blog();
97 143 }
98 144
99 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 +
100 159 foreach ( self::$whitelisted_settings as $key => $type ) {
101 160 $env_key = 'OPTIML_' . strtoupper( $key );
102 161 if ( defined( $env_key ) && constant( $env_key ) ) {
103 162 $value = constant( $env_key );
104 - if ( $type === 'bool' && ( $value === '' || ! in_array(
163 + if ( $type === 'bool' && ( (string) $value === '' || ! in_array(
105 164 $value,
106 165 [
107 166 'on',
108 167 'off',
@@ -111,13 +170,13 @@
111 170 ) ) ) {
112 171 continue;
113 172 }
114 173
115 - if ( $type === 'int' && ( $value === '' || (int) $value > 100 || (int) $value < 0 ) ) {
174 + if ( $type === 'int' && ( (string) $value === '' || (int) $value > 100 || (int) $value < 0 ) ) {
116 175 continue;
117 176 }
118 - $sanitized_value = ( $type === 'bool' ) ? ( $value === 'on' ? 'enabled' : 'disabled' ) : (int) $value;
119 - $this->options[ $key ] = $sanitized_value;
177 + $sanitized_value = ( $type === 'bool' ) ? ( $value === 'on' ? 'enabled' : 'disabled' ) : (int) $value;
178 + self::$options[ $key ] = $sanitized_value;
120 179 }
121 180 }
122 181 }
123 182 }
@@ -122,48 +181,66 @@
122 181 }
123 182 }
124 183
125 184 /**
126 - * Return filter definitions.
185 + * Check if the user is connected to Optimole.
127 186 *
128 - * @return mixed|null Filter values.
187 + * @return bool Connection status.
129 188 */
130 - 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 + }
131 197
132 - return $this->get( 'watchers' );
198 + return true;
199 + }
133 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 ] : '';
134 214 }
135 215
136 216 /**
137 - * Return filter definitions.
217 + * Check if key is allowed.
138 218 *
139 - * @return mixed|null Filter values.
219 + * @param string $key Is key allowed or not.
220 + *
221 + * @return bool Is key allowed or not.
140 222 */
141 - public function get_filters() {
223 + private function is_allowed( $key ) {
224 + return isset( $this->default_schema[ $key ] );
225 + }
142 226
143 - $filters = $this->get( 'filters' );
144 - if ( ! isset( $filters[ self::FILTER_TYPE_LAZYLOAD ] ) ) {
145 - $filters[ self::FILTER_TYPE_LAZYLOAD ] = [];
146 - }
147 - if ( ! isset( $filters[ self::FILTER_TYPE_OPTIMIZE ] ) ) {
148 - $filters[ self::FILTER_TYPE_OPTIMIZE ] = [];
149 - }
150 - foreach ( $filters as $filter_key => $filter_rules ) {
151 - if ( ! isset( $filter_rules[ self::FILTER_EXT ] ) ) {
152 - $filters[ $filter_key ][ self::FILTER_EXT ] = [];
153 - }
154 - if ( ! isset( $filter_rules[ self::FILTER_FILENAME ] ) ) {
155 - $filters[ $filter_key ][ self::FILTER_FILENAME ] = [];
156 - }
157 - if ( ! isset( $filter_rules[ self::FILTER_URL ] ) ) {
158 - $filters[ $filter_key ][ self::FILTER_URL ] = [];
159 - }
160 - if ( ! isset( $filter_rules[ self::FILTER_CLASS ] ) ) {
161 - $filters[ $filter_key ][ self::FILTER_CLASS ] = [];
162 - }
163 - }
227 + /**
228 + * Auto connect action.
229 + */
230 + public function auto_connect() {
164 231
165 - 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;
166 243 }
167 244
168 245 /**
169 246 * Process settings.
@@ -172,9 +249,11 @@
172 249 *
173 250 * @return array
174 251 */
175 252 public function parse_settings( $new_settings ) {
176 - $sanitized = [];
253 + $sanitized = [];
254 + $sanitized_value = '';
255 +
177 256 foreach ( $new_settings as $key => $value ) {
178 257 switch ( $key ) {
179 258 case 'admin_bar_item':
180 259 case 'lazyload':
@@ -183,31 +262,76 @@
183 262 case 'cdn':
184 263 case 'network_optimization':
185 264 case 'lazyload_placeholder':
186 265 case 'retina_images':
266 + case 'limit_dimensions':
187 267 case 'resize_smart':
188 268 case 'bg_replacer':
189 269 case 'video_lazyload':
190 - case 'report_script':
270 + case 'avif':
271 + case 'offload_media':
272 + case 'cloud_images':
273 + case 'autoquality':
191 274 case 'img_to_video':
192 275 case 'css_minify':
193 276 case 'js_minify':
194 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':
195 286 $sanitized_value = $this->to_map_values( $value, [ 'enabled', 'disabled' ], 'enabled' );
196 287 break;
197 - case 'max_width':
198 - case 'max_height':
288 + case 'offload_limit':
289 + $sanitized_value = absint( $value );
290 + break;
291 + case 'limit_height':
292 + case 'limit_width':
199 293 $sanitized_value = $this->to_bound_integer( $value, 100, 5000 );
200 294 break;
201 295 case 'quality':
202 - $sanitized_value = $this->to_bound_integer( $value, 1, 100 );
296 + if ( 'mauto' !== $value ) {
297 + $sanitized_value = $this->to_bound_integer( $value, 50, 100 );
298 + }
203 299 break;
204 300 case 'wm_id':
205 301 $sanitized_value = intval( $value );
206 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':
207 308 case 'cache_buster':
208 - $sanitized_value = is_string( $value ) ? $value : '';
309 + $sanitized_value = is_string( $value ) ? sanitize_text_field( $value ) : '';
209 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;
317 + case 'cloud_sites':
318 + $current_sites = $this->get( 'cloud_sites' );
319 + $sanitized_value = array_replace_recursive( $current_sites, $value );
320 + if ( isset( $value['all'] ) && $value['all'] === 'true' ) {
321 + $sanitized_value = [ 'all' => 'true' ];
322 + }
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;
210 334 case 'filters':
211 335 $current_filters = $this->get_filters();
212 336 $sanitized_value = array_replace_recursive( $current_filters, $value );
213 337 // Remove falsy vars.
@@ -222,10 +346,15 @@
222 346 }
223 347 }
224 348 break;
225 349 case 'watchers':
226 - $sanitized_value = $value;
350 + case 'placeholder_color':
351 + case 'transfer_status':
352 + $sanitized_value = sanitize_text_field( $value );
227 353 break;
354 + case 'skip_lazyload_images':
355 + $sanitized_value = $this->to_bound_integer( $value, 0, 100 );
356 + break;
228 357 case 'wm_opacity':
229 358 case 'wm_scale':
230 359 case 'wm_x':
231 360 case 'wm_y':
@@ -234,21 +363,24 @@
234 363 case 'wm_position':
235 364 $sanitized_value = $this->to_map_values(
236 365 $value,
237 366 [
238 - Optml_Resize::GRAVITY_NORTH,
239 - Optml_Resize::GRAVITY_NORTH_EAST,
240 - Optml_Resize::GRAVITY_NORTH_WEST,
241 - Optml_Resize::GRAVITY_CENTER,
242 - Optml_Resize::GRAVITY_EAST,
243 - Optml_Resize::GRAVITY_WEST,
244 - Optml_Resize::GRAVITY_SOUTH_EAST,
245 - Optml_Resize::GRAVITY_SOUTH,
246 - 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,
247 376 ],
248 - Optml_Resize::GRAVITY_SOUTH_EAST
377 + Position::SOUTH_EAST
249 378 );
250 379 break;
380 + case 'badge_position':
381 + $sanitized_value = $this->to_map_values( $value, [ 'left', 'right' ], 'right' );
382 + break;
251 383 default:
252 384 $sanitized_value = '';
253 385 break;
254 386
@@ -261,76 +393,142 @@
261 393 return $sanitized;
262 394 }
263 395
264 396 /**
265 - * Update settings.
397 + * Return filter definitions.
266 398 *
267 - * @param string $key Settings key.
268 - * @param mixed $value Settings value.
269 - *
270 - * @return bool Update result.
399 + * @return mixed|null Filter values.
271 400 */
272 - public function update( $key, $value ) {
273 - if ( ! $this->is_allowed( $key ) ) {
274 - 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 ] = [];
275 406 }
276 - // If we try to update from a website which is not the main OPTML blog, bail.
277 - if ( defined( 'OPTIML_ENABLED_MU' ) && constant( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && constant( 'OPTIML_MU_SITE_ID' ) &&
278 - intval( constant( 'OPTIML_MU_SITE_ID' ) ) !== get_current_blog_id()
279 - ) {
280 - return false;
407 + if ( ! isset( $filters[ self::FILTER_TYPE_OPTIMIZE ] ) ) {
408 + $filters[ self::FILTER_TYPE_OPTIMIZE ] = [];
281 409 }
282 - $opt = $this->options;
283 - $opt[ $key ] = $value;
284 - $update = update_option( $this->namespace, $opt, false );
285 - if ( $update ) {
286 - $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 + }
287 426 }
288 427
289 - return $update;
428 + return $filters;
290 429 }
291 430
292 431 /**
293 - * Check if key is allowed.
432 + * Update frontend banner setting from remote.
294 433 *
295 - * @param string $key Is key allowed or not.
434 + * @param bool $value Value.
296 435 *
297 - * @return bool Is key allowed or not.
436 + * @return bool
298 437 */
299 - private function is_allowed( $key ) {
300 - 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;
301 454 }
302 455
303 456 /**
304 - * Check if the user is connected to Optimole.
457 + * Update settings.
305 458 *
306 - * @return bool Connection status.
459 + * @param string $key Settings key.
460 + * @param mixed $value Settings value.
461 + *
462 + * @return bool Update result.
307 463 */
308 - public function is_connected() {
309 - $service_data = $this->get( 'service_data' );
310 - if ( ! isset( $service_data['cdn_key'] ) ) {
464 + public function update( $key, $value ) {
465 + if ( ! $this->is_allowed( $key ) ) {
311 466 return false;
312 467 }
313 - if ( empty( $service_data ['cdn_key'] ) || empty( $service_data['cdn_secret'] ) ) {
468 +
469 + if ( ! $this->is_main_mu_site() ) {
314 470 return false;
315 471 }
472 + $opt = self::$options;
316 473
317 - 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;
318 515 }
319 516
320 517 /**
321 - * Get setting value by key.
518 + * Check that we're on the main OPTML blog.
322 519 *
323 - * @param string $key Key to search against.
324 - *
325 - * @return mixed|null Setting value.
520 + * @return bool
326 521 */
327 - public function get( $key ) {
328 - if ( ! $this->is_allowed( $key ) ) {
329 - 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;
330 528 }
331 529
332 - return isset( $this->options[ $key ] ) ? $this->options[ $key ] : '';
530 + return true;
333 531 }
334 532
335 533 /**
336 534 * Return site settings.
@@ -337,33 +535,64 @@
337 535 *
338 536 * @return array Site settings.
339 537 */
340 538 public function get_site_settings() {
539 + if ( self::$site_settings_cache !== null ) {
540 + return self::$site_settings_cache;
541 + }
341 542
342 - return [
343 - 'quality' => $this->get_quality(),
344 - 'admin_bar_item' => $this->get( 'admin_bar_item' ),
345 - 'lazyload' => $this->get( 'lazyload' ),
346 - 'network_optimization' => $this->get( 'network_optimization' ),
347 - 'retina_images' => $this->get( 'retina_images' ),
348 - 'lazyload_placeholder' => $this->get( 'lazyload_placeholder' ),
349 - 'bg_replacer' => $this->get( 'bg_replacer' ),
350 - 'video_lazyload' => $this->get( 'video_lazyload' ),
351 - 'resize_smart' => $this->get( 'resize_smart' ),
352 - 'image_replacer' => $this->get( 'image_replacer' ),
353 - 'cdn' => $this->get( 'cdn' ),
354 - 'max_width' => $this->get( 'max_width' ),
355 - 'max_height' => $this->get( 'max_height' ),
356 - 'filters' => $this->get_filters(),
357 - 'watchers' => $this->get_watchers(),
358 - 'watermark' => $this->get_watermark(),
359 - 'img_to_video' => $this->get( 'img_to_video' ),
360 - 'scale' => $this->get( 'scale' ),
361 - 'css_minify' => $this->get( 'css_minify' ),
362 - 'js_minify' => $this->get( 'js_minify' ),
363 - 'native_lazyload' => $this->get( 'native_lazyload' ),
364 - 'report_script' => $this->get( 'report_script' ),
543 + $service_data = $this->get( 'service_data' );
544 + $whitelist = [];
545 + if ( isset( $service_data['whitelist'] ) ) {
546 + $whitelist = $service_data['whitelist'];
547 + }
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' ),
365 593 ];
594 + return self::$site_settings_cache;
366 595 }
367 596
368 597 /**
369 598 * Return quality factor.
@@ -371,8 +600,11 @@
371 600 * @return string Quality factor.
372 601 */
373 602 public function get_quality() {
374 603 $quality = $this->get( 'quality' );
604 + if ( $this->get( 'autoquality' ) === 'enabled' ) {
605 + return 'mauto';
606 + }
375 607 // Legacy compat.
376 608 if ( $quality === 'low' ) {
377 609 return 'high_c';
378 610 }
@@ -387,8 +619,18 @@
387 619 return $quality;
388 620 }
389 621
390 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 + /**
391 633 * Return an watermark array.
392 634 *
393 635 * @return array
394 636 */
@@ -412,8 +654,34 @@
412 654 return $this->get( 'resize_smart' ) === 'enabled';
413 655 }
414 656
415 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 + /**
416 684 * Get numeric quality used by the service.
417 685 *
418 686 * @return int Numeric quality.
419 687 */
@@ -436,8 +704,9 @@
436 704 }
437 705 if ( isset( $service_data['status'] ) && $service_data['status'] === 'inactive' ) {
438 706 return false;
439 707 }
708 +
440 709 return $this->to_boolean( $status );
441 710 }
442 711
443 712 /**
@@ -471,16 +740,21 @@
471 740 $service_data = $this->get( 'service_data' );
472 741 if ( ! isset( $service_data['cdn_key'] ) ) {
473 742 return '';
474 743 }
744 +
475 745 if ( defined( 'OPTML_CUSTOM_DOMAIN' ) && constant( 'OPTML_CUSTOM_DOMAIN' ) ) {
476 746 return parse_url( strtolower( OPTML_CUSTOM_DOMAIN ), PHP_URL_HOST );
477 747 }
478 748
749 + if ( isset( $service_data['is_cname_assigned'] ) && $service_data['is_cname_assigned'] === 'yes' && ! empty( $service_data['domain'] ) ) {
750 + return strtolower( $service_data['domain'] );
751 + }
752 +
479 753 return sprintf(
480 754 '%s.%s',
481 755 strtolower( $service_data['cdn_key'] ),
482 - 'i.optimole.com'
756 + Optml_Config::$base_domain
483 757 );
484 758 }
485 759
486 760 /**
@@ -488,16 +762,152 @@
488 762 *
489 763 * @return bool Reset action status.
490 764 */
491 765 public function reset() {
492 - $reset_schema = $this->default_schema;
493 - $reset_schema['filters'] = $this->options['filters'];
766 + $reset_schema = $this->default_schema;
767 + $reset_schema['filters'] = self::$options['filters'];
494 768
495 769 $update = update_option( $this->namespace, $reset_schema );
496 770 if ( $update ) {
497 - $this->options = $reset_schema;
771 + self::$options = $reset_schema;
772 + $this->set_settings_cache( 'filters', $reset_schema['filters'] );
498 773 }
774 + wp_unschedule_hook( Optml_Admin::SYNC_CRON );
775 + wp_unschedule_hook( Optml_Admin::ENRICH_CRON );
499 776
500 777 return $update;
501 778 }
502 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 + }
503 913 }