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