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 +527 -176 3.1.3 → trunk View file →
@@ -1,17 +1,23 @@
1 1 <?php
2 2
3 +use Optimole\Sdk\ValueObject\Position;
4 +
3 5 /**
4 6 * Class Optml_Settings.
5 7 */
6 8 class Optml_Settings {
7 9 use Optml_Normalizer;
10 +
8 11 const FILTER_EXT = 'extension';
9 12 const FILTER_URL = 'page_url';
13 + const FILTER_URL_MATCH = 'page_url_match';
10 14 const FILTER_FILENAME = 'filename';
11 15 const FILTER_CLASS = 'class';
12 16 const FILTER_TYPE_LAZYLOAD = 'lazyload';
13 17 const FILTER_TYPE_OPTIMIZE = 'optimize';
18 + const OPTML_USER_EMAIL = 'optml_user_email';
19 + const INDIVIDUAL_CACHE_TOKENS_KEY = '_optml_cache_tokens_individual';
14 20 /**
15 21 * Holds an array of possible settings to alter via wp cli or wp-config constants.
16 22 *
17 23 * @var array Whitelisted settings.
@@ -21,8 +27,9 @@
21 27 'quality' => 'int',
22 28 'lazyload' => 'bool',
23 29 'lazyload_placeholder' => 'bool',
24 30 'network_optimization' => 'bool',
31 + 'autoquality' => 'bool',
25 32 'img_to_video' => 'bool',
26 33 'resize_smart' => 'bool',
27 34 'retina_images' => 'bool',
28 35 'native_lazyload' => 'bool',
@@ -31,48 +38,74 @@
31 38 'scale' => 'bool',
32 39 'cdn' => 'bool',
33 40 ];
34 41 /**
42 + * Holds the status of the auto connect hook.
43 + *
44 + * @var boolean Whether or not the auto connect action is hooked.
45 + */
46 + private static $auto_connect_hooked = false;
47 +
48 + /**
35 49 * Default settings schema.
36 50 *
37 51 * @var array Settings schema.
38 52 */
39 53 private $default_schema = [
40 - 'api_key' => '',
41 - 'service_data' => '',
42 - 'cache_buster' => '',
43 - 'cdn' => 'disabled',
44 - 'max_height' => 1500,
45 - 'max_width' => 2000,
46 - 'admin_bar_item' => 'enabled',
47 - 'lazyload' => 'disabled',
48 - 'scale' => 'disabled',
49 - 'network_optimization' => 'disabled',
50 - 'lazyload_placeholder' => 'disabled',
51 - 'bg_replacer' => 'enabled',
52 - 'video_lazyload' => 'disabled',
53 - 'retina_images' => 'disabled',
54 - 'resize_smart' => 'disabled',
55 - 'filters' => [],
56 - 'cloud_sites' => [ 'all' => 'true' ],
57 - 'watchers' => '',
58 - 'quality' => 'auto',
59 - 'wm_id' => - 1,
60 - 'wm_opacity' => 1,
61 - 'wm_position' => Optml_Resize::GRAVITY_SOUTH_EAST,
62 - 'wm_x' => 0,
63 - 'wm_y' => 0,
64 - 'wm_scale' => 0,
65 - 'image_replacer' => 'enabled',
66 - 'img_to_video' => 'disabled',
67 - 'css_minify' => 'enabled',
68 - 'js_minify' => 'disabled',
69 - 'report_script' => 'disabled',
70 - 'native_lazyload' => 'disabled',
71 - 'offload_media' => 'disabled',
72 - 'cloud_images' => 'disabled',
73 - 'skip_lazyload_images' => 3,
74 -
54 + 'api_key' => '',
55 + 'service_data' => '',
56 + 'cache_buster' => '',
57 + 'cache_buster_assets' => '',
58 + 'cache_buster_images' => '',
59 + 'cdn' => 'disabled',
60 + 'admin_bar_item' => 'enabled',
61 + 'lazyload' => 'disabled',
62 + 'scale' => 'disabled', // Due to legacy reasons the disabled state means that the scale is enabled and the enabled state means that the scale is disabled.
63 + 'network_optimization' => 'enabled',
64 + 'lazyload_placeholder' => 'enabled',
65 + 'bg_replacer' => 'enabled',
66 + 'video_lazyload' => 'enabled',
67 + 'retina_images' => 'disabled',
68 + 'lazyload_type' => 'fixed|viewport',
69 + 'limit_dimensions' => 'enabled',
70 + 'limit_height' => 4000,
71 + 'limit_width' => 1920,
72 + 'resize_smart' => 'disabled',
73 + 'no_script' => 'disabled',
74 + 'filters' => [],
75 + 'compression_mode' => 'custom',
76 + 'cloud_sites' => [ 'all' => 'true' ],
77 + 'watchers' => '',
78 + 'quality' => 80,
79 + 'wm_id' => - 1,
80 + 'wm_opacity' => 1,
81 + 'wm_position' => Position::SOUTH_EAST,
82 + 'wm_x' => 0,
83 + 'wm_y' => 0,
84 + 'wm_scale' => 0,
85 + 'image_replacer' => 'enabled',
86 + 'img_to_video' => 'disabled',
87 + 'css_minify' => 'enabled',
88 + 'js_minify' => 'disabled',
89 + 'avif' => 'enabled', // legacy setting, is no longer used in the UI
90 + 'autoquality' => 'enabled',
91 + 'native_lazyload' => 'disabled',
92 + 'offload_media' => 'disabled',
93 + 'transfer_status' => 'disabled',
94 + 'cloud_images' => 'enabled',
95 + 'strip_metadata' => 'enabled',
96 + 'skip_lazyload_images' => 2,
97 + 'defined_image_sizes' => [],
98 + 'banner_frontend' => 'disabled',
99 + 'offloading_status' => 'disabled',
100 + 'rollback_status' => 'disabled',
101 + 'best_format' => 'enabled',
102 + 'offload_limit_reached' => 'disabled',
103 + 'offload_limit' => 50000,
104 + 'placeholder_color' => '',
105 + 'show_offload_finish_notice' => '',
106 + 'show_badge_icon' => 'disabled',
107 + 'badge_position' => 'left',
75 108 ];
76 109 /**
77 110 * Option key.
78 111 *
@@ -83,27 +116,30 @@
83 116 * Holds all options from db.
84 117 *
85 118 * @var array All options.
86 119 */
87 - private $options;
120 + private static $options;
121 +
88 122 /**
89 - * Holds the status of the auto connect hook.
123 + * Cache for site settings to avoid multiple calls to get_option on the same request.
90 124 *
91 - * @var boolean Whether or not the auto connect action is hooked.
125 + * @var array<string, mixed>|null Cached site settings.
92 126 */
93 - private static $auto_connect_hooked = false;
127 + private static $site_settings_cache = null;
94 128
95 129 /**
96 130 * Optml_Settings constructor.
97 131 */
98 132 public function __construct() {
133 + $this->default_schema['cloud_sites'] = $this->get_cloud_sites_whitelist_default();
134 +
99 135 $this->namespace = OPTML_NAMESPACE . '_settings';
100 136 $this->default_schema = apply_filters( 'optml_default_settings', $this->default_schema );
101 - $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 );
102 138
103 139 if ( defined( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && $this->to_boolean( constant( 'OPTIML_ENABLED_MU' ) ) && constant( 'OPTIML_MU_SITE_ID' ) ) {
104 140 switch_to_blog( constant( 'OPTIML_MU_SITE_ID' ) );
105 - $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 );
106 142 restore_current_blog();
107 143 }
108 144
109 145 if ( defined( 'OPTIML_USE_ENV' ) && constant( 'OPTIML_USE_ENV' ) && $this->to_boolean( constant( 'OPTIML_USE_ENV' ) ) ) {
@@ -114,9 +150,9 @@
114 150 if ( ! $this->is_connected() && ! self::$auto_connect_hooked ) {
115 151 self::$auto_connect_hooked = true;
116 152 add_action(
117 153 'plugins_loaded',
118 - [$this, 'auto_connect']
154 + [ $this, 'auto_connect' ]
119 155 );
120 156 }
121 157 }
122 158
@@ -123,9 +159,9 @@
123 159 foreach ( self::$whitelisted_settings as $key => $type ) {
124 160 $env_key = 'OPTIML_' . strtoupper( $key );
125 161 if ( defined( $env_key ) && constant( $env_key ) ) {
126 162 $value = constant( $env_key );
127 - if ( $type === 'bool' && ( $value === '' || ! in_array(
163 + if ( $type === 'bool' && ( (string) $value === '' || ! in_array(
128 164 $value,
129 165 [
130 166 'on',
131 167 'off',
@@ -134,69 +170,77 @@
134 170 ) ) ) {
135 171 continue;
136 172 }
137 173
138 - if ( $type === 'int' && ( $value === '' || (int) $value > 100 || (int) $value < 0 ) ) {
174 + if ( $type === 'int' && ( (string) $value === '' || (int) $value > 100 || (int) $value < 0 ) ) {
139 175 continue;
140 176 }
141 - $sanitized_value = ( $type === 'bool' ) ? ( $value === 'on' ? 'enabled' : 'disabled' ) : (int) $value;
142 - $this->options[ $key ] = $sanitized_value;
177 + $sanitized_value = ( $type === 'bool' ) ? ( $value === 'on' ? 'enabled' : 'disabled' ) : (int) $value;
178 + self::$options[ $key ] = $sanitized_value;
143 179 }
144 180 }
145 181 }
146 182 }
183 +
147 184 /**
148 - * Auto connect action.
185 + * Check if the user is connected to Optimole.
186 + *
187 + * @return bool Connection status.
149 188 */
150 - public function auto_connect() {
151 - $request = new WP_REST_Request( 'POST' );
152 - $request->set_param( 'api_key', constant( 'OPTIML_API_KEY' ) );
153 - Optml_Main::instance()->rest->connect( $request );
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 + }
154 197
155 - remove_action( 'plugins_loaded', [ $this, 'auto_connect' ] );
156 - self::$auto_connect_hooked = false;
198 + return true;
157 199 }
200 +
158 201 /**
159 - * Return filter definitions.
202 + * Get setting value by key.
160 203 *
161 - * @return mixed|null Filter values.
204 + * @param string $key Key to search against.
205 + *
206 + * @return mixed|null Setting value.
162 207 */
163 - public function get_watchers() {
208 + public function get( $key ) {
209 + if ( ! $this->is_allowed( $key ) ) {
210 + return null;
211 + }
164 212
165 - return $this->get( 'watchers' );
213 + return isset( self::$options[ $key ] ) ? self::$options[ $key ] : '';
214 + }
166 215
216 + /**
217 + * Check if key is allowed.
218 + *
219 + * @param string $key Is key allowed or not.
220 + *
221 + * @return bool Is key allowed or not.
222 + */
223 + private function is_allowed( $key ) {
224 + return isset( $this->default_schema[ $key ] );
167 225 }
168 226
169 227 /**
170 - * Return filter definitions.
171 - *
172 - * @return mixed|null Filter values.
228 + * Auto connect action.
173 229 */
174 - public function get_filters() {
230 + public function auto_connect() {
175 231
176 - $filters = $this->get( 'filters' );
177 - if ( ! isset( $filters[ self::FILTER_TYPE_LAZYLOAD ] ) ) {
178 - $filters[ self::FILTER_TYPE_LAZYLOAD ] = [];
179 - }
180 - if ( ! isset( $filters[ self::FILTER_TYPE_OPTIMIZE ] ) ) {
181 - $filters[ self::FILTER_TYPE_OPTIMIZE ] = [];
182 - }
183 - foreach ( $filters as $filter_key => $filter_rules ) {
184 - if ( ! isset( $filter_rules[ self::FILTER_EXT ] ) ) {
185 - $filters[ $filter_key ][ self::FILTER_EXT ] = [];
186 - }
187 - if ( ! isset( $filter_rules[ self::FILTER_FILENAME ] ) ) {
188 - $filters[ $filter_key ][ self::FILTER_FILENAME ] = [];
189 - }
190 - if ( ! isset( $filter_rules[ self::FILTER_URL ] ) ) {
191 - $filters[ $filter_key ][ self::FILTER_URL ] = [];
192 - }
193 - if ( ! isset( $filter_rules[ self::FILTER_CLASS ] ) ) {
194 - $filters[ $filter_key ][ self::FILTER_CLASS ] = [];
195 - }
196 - }
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 );
197 240
198 - return $filters;
241 + remove_action( 'plugins_loaded', [ $this, 'auto_connect' ] );
242 + self::$auto_connect_hooked = false;
199 243 }
200 244
201 245 /**
202 246 * Process settings.
@@ -205,9 +249,11 @@
205 249 *
206 250 * @return array
207 251 */
208 252 public function parse_settings( $new_settings ) {
209 - $sanitized = [];
253 + $sanitized = [];
254 + $sanitized_value = '';
255 +
210 256 foreach ( $new_settings as $key => $value ) {
211 257 switch ( $key ) {
212 258 case 'admin_bar_item':
213 259 case 'lazyload':
@@ -216,40 +262,76 @@
216 262 case 'cdn':
217 263 case 'network_optimization':
218 264 case 'lazyload_placeholder':
219 265 case 'retina_images':
266 + case 'limit_dimensions':
220 267 case 'resize_smart':
221 268 case 'bg_replacer':
222 269 case 'video_lazyload':
223 - case 'report_script':
270 + case 'avif':
224 271 case 'offload_media':
225 272 case 'cloud_images':
273 + case 'autoquality':
226 274 case 'img_to_video':
227 275 case 'css_minify':
228 276 case 'js_minify':
229 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':
230 286 $sanitized_value = $this->to_map_values( $value, [ 'enabled', 'disabled' ], 'enabled' );
231 287 break;
232 - case 'max_width':
233 - case 'max_height':
288 + case 'offload_limit':
289 + $sanitized_value = absint( $value );
290 + break;
291 + case 'limit_height':
292 + case 'limit_width':
234 293 $sanitized_value = $this->to_bound_integer( $value, 100, 5000 );
235 294 break;
236 295 case 'quality':
237 - $sanitized_value = $this->to_bound_integer( $value, 1, 100 );
296 + if ( 'mauto' !== $value ) {
297 + $sanitized_value = $this->to_bound_integer( $value, 50, 100 );
298 + }
238 299 break;
239 300 case 'wm_id':
240 301 $sanitized_value = intval( $value );
241 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':
242 308 case 'cache_buster':
243 - $sanitized_value = is_string( $value ) ? $value : '';
309 + $sanitized_value = is_string( $value ) ? sanitize_text_field( $value ) : '';
244 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;
245 317 case 'cloud_sites':
246 - $current_sites = $this->get( 'cloud_sites' );
318 + $current_sites = $this->get( 'cloud_sites' );
247 319 $sanitized_value = array_replace_recursive( $current_sites, $value );
248 320 if ( isset( $value['all'] ) && $value['all'] === 'true' ) {
249 321 $sanitized_value = [ 'all' => 'true' ];
250 322 }
251 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;
252 334 case 'filters':
253 335 $current_filters = $this->get_filters();
254 336 $sanitized_value = array_replace_recursive( $current_filters, $value );
255 337 // Remove falsy vars.
@@ -264,9 +346,11 @@
264 346 }
265 347 }
266 348 break;
267 349 case 'watchers':
268 - $sanitized_value = $value;
350 + case 'placeholder_color':
351 + case 'transfer_status':
352 + $sanitized_value = sanitize_text_field( $value );
269 353 break;
270 354 case 'skip_lazyload_images':
271 355 $sanitized_value = $this->to_bound_integer( $value, 0, 100 );
272 356 break;
@@ -279,21 +363,24 @@
279 363 case 'wm_position':
280 364 $sanitized_value = $this->to_map_values(
281 365 $value,
282 366 [
283 - Optml_Resize::GRAVITY_NORTH,
284 - Optml_Resize::GRAVITY_NORTH_EAST,
285 - Optml_Resize::GRAVITY_NORTH_WEST,
286 - Optml_Resize::GRAVITY_CENTER,
287 - Optml_Resize::GRAVITY_EAST,
288 - Optml_Resize::GRAVITY_WEST,
289 - Optml_Resize::GRAVITY_SOUTH_EAST,
290 - Optml_Resize::GRAVITY_SOUTH,
291 - 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,
292 376 ],
293 - Optml_Resize::GRAVITY_SOUTH_EAST
377 + Position::SOUTH_EAST
294 378 );
295 379 break;
380 + case 'badge_position':
381 + $sanitized_value = $this->to_map_values( $value, [ 'left', 'right' ], 'right' );
382 + break;
296 383 default:
297 384 $sanitized_value = '';
298 385 break;
299 386
@@ -306,76 +393,142 @@
306 393 return $sanitized;
307 394 }
308 395
309 396 /**
310 - * Update settings.
397 + * Return filter definitions.
311 398 *
312 - * @param string $key Settings key.
313 - * @param mixed $value Settings value.
314 - *
315 - * @return bool Update result.
399 + * @return mixed|null Filter values.
316 400 */
317 - public function update( $key, $value ) {
318 - if ( ! $this->is_allowed( $key ) ) {
319 - 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 ] = [];
320 406 }
321 - // If we try to update from a website which is not the main OPTML blog, bail.
322 - if ( defined( 'OPTIML_ENABLED_MU' ) && constant( 'OPTIML_ENABLED_MU' ) && defined( 'OPTIML_MU_SITE_ID' ) && constant( 'OPTIML_MU_SITE_ID' ) &&
323 - intval( constant( 'OPTIML_MU_SITE_ID' ) ) !== get_current_blog_id()
324 - ) {
325 - return false;
407 + if ( ! isset( $filters[ self::FILTER_TYPE_OPTIMIZE ] ) ) {
408 + $filters[ self::FILTER_TYPE_OPTIMIZE ] = [];
326 409 }
327 - $opt = $this->options;
328 - $opt[ $key ] = $value;
329 - $update = update_option( $this->namespace, $opt, false );
330 - if ( $update ) {
331 - $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 + }
332 426 }
333 427
334 - return $update;
428 + return $filters;
335 429 }
336 430
337 431 /**
338 - * Check if key is allowed.
432 + * Update frontend banner setting from remote.
339 433 *
340 - * @param string $key Is key allowed or not.
434 + * @param bool $value Value.
341 435 *
342 - * @return bool Is key allowed or not.
436 + * @return bool
343 437 */
344 - private function is_allowed( $key ) {
345 - 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;
346 454 }
347 455
348 456 /**
349 - * Check if the user is connected to Optimole.
457 + * Update settings.
350 458 *
351 - * @return bool Connection status.
459 + * @param string $key Settings key.
460 + * @param mixed $value Settings value.
461 + *
462 + * @return bool Update result.
352 463 */
353 - public function is_connected() {
354 - $service_data = $this->get( 'service_data' );
355 - if ( ! isset( $service_data['cdn_key'] ) ) {
464 + public function update( $key, $value ) {
465 + if ( ! $this->is_allowed( $key ) ) {
356 466 return false;
357 467 }
358 - if ( empty( $service_data ['cdn_key'] ) || empty( $service_data['cdn_secret'] ) ) {
468 +
469 + if ( ! $this->is_main_mu_site() ) {
359 470 return false;
360 471 }
472 + $opt = self::$options;
361 473
362 - 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;
363 515 }
364 516
365 517 /**
366 - * Get setting value by key.
518 + * Check that we're on the main OPTML blog.
367 519 *
368 - * @param string $key Key to search against.
369 - *
370 - * @return mixed|null Setting value.
520 + * @return bool
371 521 */
372 - public function get( $key ) {
373 - if ( ! $this->is_allowed( $key ) ) {
374 - 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;
375 528 }
376 529
377 - return isset( $this->options[ $key ] ) ? $this->options[ $key ] : '';
530 + return true;
378 531 }
379 532
380 533 /**
381 534 * Return site settings.
@@ -382,42 +535,64 @@
382 535 *
383 536 * @return array Site settings.
384 537 */
385 538 public function get_site_settings() {
539 + if ( self::$site_settings_cache !== null ) {
540 + return self::$site_settings_cache;
541 + }
542 +
386 543 $service_data = $this->get( 'service_data' );
387 - $whitelist = [];
544 + $whitelist = [];
388 545 if ( isset( $service_data['whitelist'] ) ) {
389 546 $whitelist = $service_data['whitelist'];
390 547 }
391 - return [
392 - 'quality' => $this->get_quality(),
393 - 'admin_bar_item' => $this->get( 'admin_bar_item' ),
394 - 'lazyload' => $this->get( 'lazyload' ),
395 - 'network_optimization' => $this->get( 'network_optimization' ),
396 - 'retina_images' => $this->get( 'retina_images' ),
397 - 'lazyload_placeholder' => $this->get( 'lazyload_placeholder' ),
398 - 'skip_lazyload_images' => $this->get( 'skip_lazyload_images' ),
399 - 'bg_replacer' => $this->get( 'bg_replacer' ),
400 - 'video_lazyload' => $this->get( 'video_lazyload' ),
401 - 'resize_smart' => $this->get( 'resize_smart' ),
402 - 'image_replacer' => $this->get( 'image_replacer' ),
403 - 'cdn' => $this->get( 'cdn' ),
404 - 'max_width' => $this->get( 'max_width' ),
405 - 'max_height' => $this->get( 'max_height' ),
406 - 'filters' => $this->get_filters(),
407 - 'cloud_sites' => $this->get( 'cloud_sites' ),
408 - 'watchers' => $this->get_watchers(),
409 - 'watermark' => $this->get_watermark(),
410 - 'img_to_video' => $this->get( 'img_to_video' ),
411 - 'scale' => $this->get( 'scale' ),
412 - 'css_minify' => $this->get( 'css_minify' ),
413 - 'js_minify' => $this->get( 'js_minify' ),
414 - 'native_lazyload' => $this->get( 'native_lazyload' ),
415 - 'report_script' => $this->get( 'report_script' ),
416 - 'offload_media' => $this->get( 'offload_media' ),
417 - 'cloud_images' => $this->get( 'cloud_images' ),
418 - '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' ),
419 593 ];
594 + return self::$site_settings_cache;
420 595 }
421 596
422 597 /**
423 598 * Return quality factor.
@@ -425,8 +600,11 @@
425 600 * @return string Quality factor.
426 601 */
427 602 public function get_quality() {
428 603 $quality = $this->get( 'quality' );
604 + if ( $this->get( 'autoquality' ) === 'enabled' ) {
605 + return 'mauto';
606 + }
429 607 // Legacy compat.
430 608 if ( $quality === 'low' ) {
431 609 return 'high_c';
432 610 }
@@ -441,8 +619,18 @@
441 619 return $quality;
442 620 }
443 621
444 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 + /**
445 633 * Return an watermark array.
446 634 *
447 635 * @return array
448 636 */
@@ -466,8 +654,34 @@
466 654 return $this->get( 'resize_smart' ) === 'enabled';
467 655 }
468 656
469 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 + /**
470 684 * Get numeric quality used by the service.
471 685 *
472 686 * @return int Numeric quality.
473 687 */
@@ -490,8 +704,9 @@
490 704 }
491 705 if ( isset( $service_data['status'] ) && $service_data['status'] === 'inactive' ) {
492 706 return false;
493 707 }
708 +
494 709 return $this->to_boolean( $status );
495 710 }
496 711
497 712 /**
@@ -526,16 +741,16 @@
526 741 if ( ! isset( $service_data['cdn_key'] ) ) {
527 742 return '';
528 743 }
529 744
745 + if ( defined( 'OPTML_CUSTOM_DOMAIN' ) && constant( 'OPTML_CUSTOM_DOMAIN' ) ) {
746 + return parse_url( strtolower( OPTML_CUSTOM_DOMAIN ), PHP_URL_HOST );
747 + }
748 +
530 749 if ( isset( $service_data['is_cname_assigned'] ) && $service_data['is_cname_assigned'] === 'yes' && ! empty( $service_data['domain'] ) ) {
531 750 return strtolower( $service_data['domain'] );
532 751 }
533 752
534 - if ( defined( 'OPTML_CUSTOM_DOMAIN' ) && constant( 'OPTML_CUSTOM_DOMAIN' ) ) {
535 - return parse_url( strtolower( OPTML_CUSTOM_DOMAIN ), PHP_URL_HOST );
536 - }
537 -
538 753 return sprintf(
539 754 '%s.%s',
540 755 strtolower( $service_data['cdn_key'] ),
541 756 Optml_Config::$base_domain
@@ -547,16 +762,152 @@
547 762 *
548 763 * @return bool Reset action status.
549 764 */
550 765 public function reset() {
551 - $reset_schema = $this->default_schema;
552 - $reset_schema['filters'] = $this->options['filters'];
766 + $reset_schema = $this->default_schema;
767 + $reset_schema['filters'] = self::$options['filters'];
553 768
554 769 $update = update_option( $this->namespace, $reset_schema );
555 770 if ( $update ) {
556 - $this->options = $reset_schema;
771 + self::$options = $reset_schema;
772 + $this->set_settings_cache( 'filters', $reset_schema['filters'] );
557 773 }
774 + wp_unschedule_hook( Optml_Admin::SYNC_CRON );
775 + wp_unschedule_hook( Optml_Admin::ENRICH_CRON );
558 776
559 777 return $update;
560 778 }
561 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 + }
562 913 }