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