PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.13.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.13.0
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / classes / WpMatomo / Settings.php
matomo / classes / WpMatomo Last commit date
Admin 4 days ago Commands 4 days ago Db 4 days ago Ecommerce 1 month ago Report 1 month ago Site 4 days ago TrackingCode 4 days ago Updater 4 years ago User 4 days ago Workarounds 2 years ago WpStatistics 1 month ago views 1 month ago AIBotTracking.php 1 month ago API.php 1 month ago Access.php 4 days ago AjaxTracker.php 6 months ago Annotations.php 1 month ago Bootstrap.php 1 year ago Capabilities.php 4 days ago Compatibility.php 1 month ago Email.php 1 month ago ErrorNotice.php 1 month ago Feature.php 4 months ago Installer.php 1 month ago Logger.php 1 year ago MinimumRequirements.php 4 days ago MinimumRequirementsNotice.php 4 days ago MinimumRequirementsUpdateGuard.php 4 days ago OptOut.php 3 months ago Paths.php 1 month ago PluginActionLinks.php 4 months ago PluginAdminOverrides.php 1 month ago PluginInit.php 1 month ago PrivacyBadge.php 4 years ago RedirectOnActivation.php 4 months ago Referral.php 4 months ago Request.php 4 days ago Roles.php 4 months ago ScheduledTasks.php 1 month ago Settings.php 4 days ago Site.php 4 days ago TrackingCode.php 1 month ago Uninstaller.php 4 days ago Updater.php 1 month ago User.php 4 days ago
Settings.php
710 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 * @package matomo
8 * Code Based on
9 * @author Andr&eacute; Br&auml;kling
10 * https://github.com/braekling/matomo
11 *
12 */
13
14 namespace WpMatomo;
15
16 use Piwik\CliMulti\Process;
17 use Piwik\Tracker\Cache;
18 use WpMatomo\Admin\CookieConsent;
19 use WpMatomo\Admin\TrackingSettings;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit; // if accessed directly
23 }
24
25 class Settings {
26 const OPTION_PREFIX = 'matomo-';
27 const GLOBAL_OPTION_PREFIX = 'matomo_global-';
28 const OPTION = 'matomo-option';
29 const OPTION_GLOBAL = 'matomo-global-option';
30 const OPTION_KEY_CAPS_ACCESS = 'caps_access';
31 const OPTION_KEY_STEALTH = 'caps_tracking';
32 const OPTION_LAST_TRACKING_SETTINGS_CHANGE = 'last_tracking_settings_update';
33 const OPTION_LAST_TRACKING_CODE_UPDATE = 'last_tracking_code_update';
34 const SHOW_GET_STARTED_PAGE = 'show_get_started_page';
35 const DELETE_ALL_DATA_ON_UNINSTALL = 'delete_all_data_uninstall';
36 const SITE_CURRENCY = 'site_currency';
37 const NETWORK_CONFIG_OPTIONS = 'config_options';
38 const DISABLE_ASYNC_ARCHIVING_OPTION_NAME = 'matomo_disable_async_archiving';
39 const USE_SESSION_VISITOR_ID_OPTION_NAME = 'use_session_visitor_id';
40 const SERVER_SIDE_TRACKING_DELAY_SECS = 'server_side_tracking_delay_secs';
41 const GLOBAL_USER_AGENT_EXCLUSIONS = 'global_user_agent_exclusions';
42 const TRACK_AI_BOTS = 'track_ai_bots';
43 const TRACK_AI_BOTS_USING_ESI = 'track_ai_bots_using_esi';
44
45 // NOTE: this is not a setting value, but is stored with setting values to avoid
46 // adding an extra get_option call to every WordPress backoffice request.
47 const INSTANCE_COMPONENTS_INSTALLED = 'instance-components-installed';
48
49 public static $is_doing_action_tracking_related = false;
50 /**
51 * @internal tests only
52 * @var bool
53 */
54 public $force_disable_addhandler = false;
55
56 /**
57 * Tests only
58 *
59 * @ignore
60 * @var bool
61 */
62 private $assume_is_network_enabled_in_tests = false;
63
64 /**
65 * Register default configuration set
66 *
67 * (public for tests)
68 *
69 * @var array
70 */
71 public $default_global_settings = [
72 // Plugin settings
73 'last_settings_update' => 0,
74 self::OPTION_LAST_TRACKING_SETTINGS_CHANGE => 0,
75 self::OPTION_KEY_STEALTH => [],
76 self::OPTION_KEY_CAPS_ACCESS => [],
77 self::NETWORK_CONFIG_OPTIONS => [],
78 self::DELETE_ALL_DATA_ON_UNINSTALL => true,
79 self::SITE_CURRENCY => 'USD',
80 // User settings: Stats configuration
81 // User settings: Tracking configuration
82 'track_mode' => 'disabled',
83 'track_js_endpoint' => 'default',
84 'track_api_endpoint' => 'default',
85 'track_codeposition' => 'footer',
86 'track_noscript' => false,
87 'track_content' => 'disabled',
88 'track_ecommerce' => true,
89 'track_search' => false,
90 'track_404' => false,
91 self::TRACK_AI_BOTS => false,
92 self::TRACK_AI_BOTS_USING_ESI => false,
93 'tagmanger_container_ids' => [],
94 'add_post_annotations' => [],
95 'add_customvars_box' => false,
96 'js_manually' => '',
97 'noscript_manually' => '',
98 'add_download_extensions' => '',
99 'set_download_extensions' => '',
100 'set_link_classes' => '',
101 'set_download_classes' => '',
102 'core_version' => '',
103 'version_history' => [],
104 'mail_history' => [],
105 'disable_cookies' => false,
106 'cookie_consent' => CookieConsent::REQUIRE_NONE,
107 'force_post' => false,
108 'limit_cookies' => false,
109 'limit_cookies_visitor' => 34186669, // Matomo default 13 months
110 'limit_cookies_session' => 1800, // Matomo default 30 minutes
111 'limit_cookies_referral' => 15778463, // Matomo default 6 months
112 'track_admin' => false,
113 'track_across' => false,
114 'track_across_alias' => false,
115 'track_crossdomain_linking' => false,
116 'track_feed' => false,
117 'track_feed_addcampaign' => false,
118 'track_feed_campaign' => 'feed',
119 'track_heartbeat' => 0,
120 'track_user_id' => 'disabled',
121 'track_datacfasync' => false,
122 'track_jserrors' => false,
123 'force_protocol' => 'disabled',
124 'maxmind_license_key' => '',
125 self::SHOW_GET_STARTED_PAGE => 1,
126 self::DISABLE_ASYNC_ARCHIVING_OPTION_NAME => false,
127 self::GLOBAL_USER_AGENT_EXCLUSIONS => null,
128 ];
129
130 /**
131 * Settings stored per blog
132 *
133 * @var array
134 */
135 private $default_blog_settings = [
136 'noscript_code' => '',
137 'tracking_code' => '',
138 self::OPTION_LAST_TRACKING_CODE_UPDATE => 0,
139 self::USE_SESSION_VISITOR_ID_OPTION_NAME => false,
140 self::SERVER_SIDE_TRACKING_DELAY_SECS => 180,
141 ];
142
143 private $global_settings = [];
144 private $blog_settings = [];
145
146 /**
147 * The blog ID the cached settings were loaded for. Long-lived Settings instances can
148 * be used across switch_to_blog() calls, cached values must be reloaded when a blog
149 * changes.
150 *
151 * @var int|null
152 */
153 private $loaded_for_blog_id = null;
154
155 /**
156 * Whether the cached global settings were read from the network wide site option rather
157 * than from this blog's own option. Recorded when they are loaded instead of re-derived
158 * during a reload, to avoid invoking WP option filters while reloading settings.
159 *
160 * @var bool
161 */
162 private $global_settings_are_network_wide = false;
163
164 /**
165 * @var string[]
166 */
167 private $global_settings_changed = [];
168
169 /**
170 * @var string[]
171 */
172 private $blog_settings_changed = [];
173
174 /**
175 * Settings whose value is a secret. Their values are never written to the debug log.
176 *
177 * Matching whole keys also means a secret nested inside an array valued setting cannot be
178 * hidden this way: the whole array is json encoded into the log.
179 *
180 * @var string[]
181 */
182 private static $sensitive_settings = [
183 'maxmind_license_key',
184 ];
185
186 /**
187 * @var Logger
188 */
189 private $logger;
190
191 /**
192 * Constructor class to prepare settings manager
193 *
194 */
195 public function __construct() {
196 $this->logger = new Logger();
197
198 $this->init_settings();
199 }
200
201 public function init_settings() {
202 // set and cleared before the option reads below, which all run filters that can
203 // reach back in here through WpMatomo::$settings. such a re-entrant call must not find
204 // a blog mismatch, or it would start the load over again recursing forever
205 $this->loaded_for_blog_id = get_current_blog_id();
206
207 // a re-entrant call must also not reference values that were changed for the previously
208 // loaded blog
209 $this->global_settings_changed = [];
210 $this->blog_settings_changed = [];
211 $this->global_settings = [];
212 $this->blog_settings = [];
213
214 $this->global_settings_are_network_wide = $this->is_network_enabled();
215
216 if ( $this->global_settings_are_network_wide ) {
217 $global_settings = get_site_option( self::OPTION_GLOBAL, [] );
218 } else {
219 $global_settings = get_option( self::OPTION_GLOBAL, [] );
220 }
221
222 if ( ! empty( $global_settings ) && is_array( $global_settings ) ) {
223 $this->global_settings = $global_settings;
224 }
225
226 $this->load_blog_settings();
227 }
228
229 public function get_customised_global_settings() {
230 $this->reload_if_blog_switched();
231
232 $custom_settings = [];
233
234 foreach ( $this->global_settings as $key => $val ) {
235 if ( isset( $this->default_global_settings[ $key ] )
236 // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual
237 && $this->default_global_settings[ $key ] != $val ) {
238 $custom_settings[ $key ] = $val;
239 }
240 }
241
242 return $custom_settings;
243 }
244
245 public function uninstall() {
246 Uninstaller::uninstall_options( self::OPTION_PREFIX );
247 Uninstaller::uninstall_options( self::GLOBAL_OPTION_PREFIX );
248 Uninstaller::uninstall_site_meta( self::GLOBAL_OPTION_PREFIX );
249 $this->init_settings();
250 }
251
252 public function is_multisite() {
253 return function_exists( 'is_multisite' ) && is_multisite();
254 }
255
256 /**
257 * @api
258 */
259 public function is_network_enabled() {
260 if ( $this->assume_is_network_enabled_in_tests ) {
261 return true;
262 }
263
264 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
265 require_once ABSPATH . 'wp-admin/includes/plugin.php';
266 }
267
268 return is_plugin_active_for_network( 'matomo/matomo.php' );
269 }
270
271 /**
272 * Save all settings as WordPress options.
273 *
274 * Note: save() should be called immediately after one or more set_option/set_global_option
275 * calls.
276 */
277 public function save() {
278 $this->reload_if_blog_switched();
279
280 if ( empty( $this->global_settings_changed ) && empty( $this->blog_settings_changed ) ) {
281 $this->logger->log( 'No settings changed yet' );
282 return;
283 }
284
285 $this->logger->log( 'Save settings' );
286
287 if ( ! empty( $this->global_settings_changed ) ) {
288 if ( $this->global_settings_are_network_wide ) {
289 update_site_option( self::OPTION_GLOBAL, $this->global_settings );
290 } else {
291 update_option( self::OPTION_GLOBAL, $this->global_settings );
292 }
293 }
294
295 if ( ! empty( $this->blog_settings_changed ) ) {
296 update_option( self::OPTION, $this->blog_settings );
297 }
298
299 $keys_changed = array_values(
300 array_unique( array_merge( $this->global_settings_changed, $this->blog_settings_changed ) )
301 );
302
303 $this->global_settings_changed = [];
304 $this->blog_settings_changed = [];
305
306 foreach ( $keys_changed as $key_changed ) {
307 if ( self::GLOBAL_USER_AGENT_EXCLUSIONS === $key_changed ) {
308 Bootstrap::do_bootstrap();
309 Cache::clearCacheGeneral();
310 }
311
312 do_action( 'matomo_setting_change_' . $key_changed );
313 }
314 }
315
316 /**
317 * Get a global option's value
318 *
319 * @param string $key
320 * option key
321 *
322 * @return string|array option value
323 * @api
324 */
325 public function get_global_option( $key ) {
326 $this->reload_if_blog_switched();
327
328 if ( isset( $this->global_settings[ $key ] ) ) {
329 return $this->global_settings[ $key ];
330 }
331
332 if ( isset( $this->default_global_settings[ $key ] ) ) {
333 return $this->default_global_settings[ $key ];
334 }
335 }
336
337 /**
338 * Get an option's value related to a specific blog
339 *
340 * @param string $key option key
341 *
342 * @return string|array
343 * @api
344 */
345 public function get_option( $key ) {
346 $this->reload_if_blog_switched();
347
348 if ( isset( $this->blog_settings[ $key ] ) ) {
349 return $this->blog_settings[ $key ];
350 }
351
352 if ( isset( $this->default_blog_settings[ $key ] ) ) {
353 return $this->default_blog_settings[ $key ];
354 }
355 }
356
357 private function convert_type( $value, $type ) {
358 if ( 'array' === $type && empty( $value ) ) {
359 $value = []; // prevent eg converting '' to array('')
360 } else {
361 settype( $value, $type );
362 }
363
364 return $value;
365 }
366
367 /**
368 * Set a global option's value
369 *
370 * @param string $key option key
371 * @param string|array $value new option value
372 */
373 public function set_global_option( $key, $value ) {
374 $this->reload_if_blog_switched();
375
376 if ( isset( $this->default_global_settings[ $key ] ) ) {
377 $type = gettype( $this->default_global_settings[ $key ] );
378 $value = $this->convert_type( $value, $type );
379 }
380
381 if ( ! isset( $this->global_settings[ $key ] )
382 || $this->global_settings[ $key ] !== $value
383 ) {
384 $this->global_settings_changed[] = $key;
385 $this->logger->log( 'Changed global option ' . $key . ': ' . $this->loggable_value( $key, $value ) );
386
387 $this->global_settings[ $key ] = $value;
388 }
389 }
390
391 /**
392 * Set an option's value related to a specific blog
393 *
394 * @param string $key option key
395 * @param string $value new option value
396 */
397 public function set_option( $key, $value ) {
398 $this->reload_if_blog_switched();
399
400 if ( isset( $this->default_blog_settings[ $key ] ) ) {
401 $type = gettype( $this->default_blog_settings[ $key ] );
402 $value = $this->convert_type( $value, $type );
403 }
404
405 if ( ! isset( $this->blog_settings[ $key ] )
406 || $this->blog_settings[ $key ] !== $value ) {
407 $this->blog_settings_changed[] = $key;
408 $this->logger->log( 'Changed option ' . $key . ': ' . $this->loggable_value( $key, $value ) );
409 $this->blog_settings[ $key ] = $value;
410 }
411 }
412
413 /**
414 * Values of some settings are secrets and must not be written to the debug log.
415 *
416 * @param string $key
417 * @param mixed $value
418 *
419 * @return string
420 */
421 private function loggable_value( $key, $value ) {
422 if ( in_array( $key, self::$sensitive_settings, true ) ) {
423 return '(value hidden)';
424 }
425
426 if ( is_array( $value ) || is_object( $value ) ) {
427 $encoded = wp_json_encode( $value );
428
429 // wp_json_encode() returns false eg when the value nests deeper than it can encode
430 return false === $encoded ? '(value could not be encoded)' : $encoded;
431 }
432
433 return (string) $value;
434 }
435
436 /**
437 * @param array $values
438 *
439 * @api
440 */
441 public function apply_tracking_related_changes( $values ) {
442 $this->set_global_option( self::OPTION_LAST_TRACKING_SETTINGS_CHANGE, time() );
443
444 $this->apply_changes( $values );
445
446 if ( ! self::$is_doing_action_tracking_related ) {
447 // prevent recurison if any plugin was listening to this event and calling this method again
448 self::$is_doing_action_tracking_related = true;
449 do_action( 'matomo_tracking_settings_changed', $this, $values );
450 self::$is_doing_action_tracking_related = false;
451 }
452 }
453
454 public function should_disable_addhandler() {
455 if ( $this->force_disable_addhandler ) {
456 return true;
457 }
458
459 return defined( 'MATOMO_DISABLE_ADDHANDLER' ) && MATOMO_DISABLE_ADDHANDLER;
460 }
461
462 /**
463 * Apply new configuration
464 *
465 * @param array $settings
466 * new configuration set
467 *
468 * @api
469 */
470 public function apply_changes( $settings ) {
471 $this->logger->log( 'Apply changed settings:' );
472 foreach ( $this->default_global_settings as $key => $val ) {
473 if ( isset( $settings[ $key ] ) ) {
474 $this->set_global_option( $key, $settings[ $key ] );
475 }
476 }
477 foreach ( $this->default_blog_settings as $key => $val ) {
478 if ( isset( $settings[ $key ] ) ) {
479 $this->set_option( $key, $settings[ $key ] );
480 }
481 }
482 $this->set_global_option( 'last_settings_update', time() );
483
484 if ( $this->should_save_tracking_code_across_sites() ) {
485 // special case for when the same tracking code needs to be used across all instances.
486 if ( isset( $settings['tracking_code'] ) ) {
487 $this->set_global_option( 'js_manually', $this->get_option( 'tracking_code' ) );
488 }
489 if ( isset( $settings['noscript_code'] ) ) {
490 $this->set_global_option( 'noscript_manually', $this->get_option( 'noscript_code' ) );
491 }
492 }
493
494 $this->save();
495 }
496
497 private function should_save_tracking_code_across_sites() {
498 return $this->global_settings_are_network_wide
499 && $this->get_global_option( 'track_mode' ) === TrackingSettings::TRACK_MODE_MANUALLY;
500 }
501
502 public function get_js_tracking_code() {
503 if ( $this->should_save_tracking_code_across_sites() ) {
504 return $this->get_global_option( 'js_manually' );
505 }
506
507 return $this->get_option( 'tracking_code' );
508 }
509
510 public function get_noscript_tracking_code() {
511 if ( $this->should_save_tracking_code_across_sites() ) {
512 return $this->get_global_option( 'noscript_manually' );
513 }
514
515 return $this->get_option( 'noscript_code' );
516 }
517
518 public function is_cross_domain_linking_enabled() {
519 return $this->get_global_option( 'track_crossdomain_linking' );
520 }
521
522 public function should_delete_all_data_on_uninstall() {
523 if ( defined( 'MATOMO_REMOVE_ALL_DATA' ) ) {
524 return (bool) MATOMO_REMOVE_ALL_DATA;
525 }
526
527 return (bool) $this->get_global_option( self::DELETE_ALL_DATA_ON_UNINSTALL );
528 }
529
530 /**
531 * Check if feed tracking is enabled
532 *
533 * @return boolean Is feed tracking enabled?
534 */
535 public function is_track_feed() {
536 return $this->get_global_option( 'track_feed' );
537 }
538
539 /**
540 * Check if admin tracking is enabled
541 *
542 * @return boolean Is admin tracking enabled?
543 */
544 public function is_admin_tracking_enabled() {
545 return $this->get_global_option( 'track_admin' ) && is_admin();
546 }
547
548 public function is_current_tracking_code() {
549 $last_tracking_code_update = $this->get_option( self::OPTION_LAST_TRACKING_CODE_UPDATE );
550 $last_tracking_settings_update = $this->get_global_option( self::OPTION_LAST_TRACKING_SETTINGS_CHANGE );
551
552 return $last_tracking_code_update && $last_tracking_code_update > $last_tracking_settings_update;
553 }
554
555 /**
556 * Check if feed permalinks get a campaign parameter
557 *
558 * @return boolean Add campaign parameter to feed permalinks?
559 */
560 public function is_add_feed_campaign() {
561 return $this->get_global_option( 'track_feed_addcampaign' );
562 }
563
564 public function is_tracking_enabled() {
565 return $this->get_global_option( 'track_mode' ) !== 'disabled';
566 }
567
568 /**
569 * Whether the tracking code of the current blog is generated by the plugin, as opposed to
570 * being entered manually or not used at all.
571 *
572 * @return bool
573 */
574 public function is_tracking_code_autogenerated() {
575 return $this->is_tracking_enabled()
576 && TrackingSettings::TRACK_MODE_MANUALLY !== $this->get_global_option( 'track_mode' );
577 }
578
579 /**
580 * Check if noscript code insertion is enabled
581 *
582 * @return boolean Insert noscript code?
583 */
584 public function is_add_no_script_code() {
585 return $this->get_global_option( 'track_noscript' );
586 }
587
588 public function get_tracking_code_position() {
589 return $this->get_global_option( 'track_codeposition' );
590 }
591
592 public function track_404_enabled() {
593 return $this->get_global_option( 'track_404' );
594 }
595
596 public function track_user_id_enabled() {
597 return $this->get_global_option( 'track_user_id' ) !== 'disabled';
598 }
599
600 public function track_search_enabled() {
601 return ( is_search() && $this->get_global_option( 'track_search' ) );
602 }
603
604 public function set_assume_is_network_enabled_in_tests( $network_enabled = true ) {
605 $this->assume_is_network_enabled_in_tests = $network_enabled;
606 $this->global_settings_are_network_wide = $network_enabled;
607 }
608
609 public function is_async_archiving_supported() {
610 return Process::isSupported() && ( ! defined( 'MATOMO_SUPPORT_ASYNC_ARCHIVING' ) || MATOMO_SUPPORT_ASYNC_ARCHIVING );
611 }
612
613 public function is_async_archiving_disabled_by_option() {
614 return (bool) $this->get_global_option( self::DISABLE_ASYNC_ARCHIVING_OPTION_NAME );
615 }
616
617 public function is_ai_bot_tracking_enabled() {
618 return (bool) $this->get_global_option( self::TRACK_AI_BOTS );
619 }
620
621 public function is_tracking_ai_bots_via_esi_includes() {
622 return (bool) $this->get_global_option( self::TRACK_AI_BOTS_USING_ESI );
623 }
624
625 public function get_matomo_major_version() {
626 $core_version = $this->get_global_option( 'core_version' );
627 $core_version = isset( $core_version ) ? $core_version : '';
628
629 $parts = explode( '.', $core_version );
630 if ( empty( $parts ) ) {
631 return 0;
632 }
633
634 return (int) $parts[0];
635 }
636
637 public function set_global_user_agent_exclusions( $user_agents ) {
638 $this->set_global_option( self::GLOBAL_USER_AGENT_EXCLUSIONS, $user_agents );
639 }
640
641 public function get_global_user_agent_exclusions() {
642 $user_agents = $this->get_global_option( self::GLOBAL_USER_AGENT_EXCLUSIONS );
643 if ( ! is_array( $user_agents ) ) {
644 // only bootstrap if we can't access the SitesManager API.
645 // if we always bootstrap, it is possible to try initializing the FrontController before Matomo
646 // installation completes, which will fail.
647 if ( ! class_exists( \Piwik\Plugins\SitesManager\API::class ) ) {
648 Bootstrap::do_bootstrap();
649 }
650
651 $user_agents = \Piwik\Plugins\SitesManager\API::getInstance()->getExcludedUserAgentsGlobal();
652 $user_agents = explode( ',', $user_agents );
653 }
654 return $user_agents;
655 }
656
657 public function is_track_via_esi_enabled() {
658 return ( (bool) $this->get_global_option( 'track_ai_bots_using_esi' ) ) === true;
659 }
660
661 private function load_blog_settings() {
662 // set and cleared before the option read below for the same reasons as in
663 // init_settings(), see there
664 $this->loaded_for_blog_id = get_current_blog_id();
665 $this->blog_settings = [];
666 $this->blog_settings_changed = [];
667
668 $settings = get_option( self::OPTION, [] );
669 if ( ! is_array( $settings ) ) {
670 $settings = [];
671 }
672
673 $this->blog_settings = $settings;
674 }
675
676 /**
677 * Reload cached settings if the current blog changed since they were loaded (eg, via
678 * switch_to_blog()). Cached per-blog settings must not be served for, or saved to, a
679 * different blog. Pending unsaved per-blog changes are discarded.
680 */
681 private function reload_if_blog_switched() {
682 if ( ! $this->is_multisite()
683 || get_current_blog_id() === $this->loaded_for_blog_id
684 ) {
685 return;
686 }
687
688 $discarded = $this->blog_settings_changed;
689
690 if ( $this->global_settings_are_network_wide ) {
691 // they live in a network wide site option, so neither they nor unsaved changes to
692 // them are affected by the blog switch, and both are left alone
693 $this->load_blog_settings();
694 } else {
695 // without network activation the global settings are stored per blog too
696 $discarded = array_merge( $this->global_settings_changed, $discarded );
697
698 $this->init_settings();
699 }
700
701 if ( ! empty( $discarded ) ) {
702 // the blog was switched in code before the settings were save()'d
703 $this->logger->log(
704 'Unsaved Matomo setting changes were discarded after a WP blog switch: '
705 . implode( ', ', array_values( array_unique( $discarded ) ) )
706 );
707 }
708 }
709 }
710