PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.6.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.6.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 7 months ago Commands 2 years ago Db 1 year ago Ecommerce 10 months ago Report 1 year ago Site 2 years ago TrackingCode 1 year ago Updater 4 years ago User 7 months ago Workarounds 2 years ago WpStatistics 7 months ago views 4 years ago API.php 1 year ago Access.php 4 years ago AjaxTracker.php 7 months ago Annotations.php 4 years ago Bootstrap.php 10 months ago Capabilities.php 4 years ago Compatibility.php 2 years ago Email.php 2 years ago ErrorNotice.php 2 years ago Installer.php 1 year ago Logger.php 1 year ago OptOut.php 1 year ago Paths.php 2 years ago PluginAdminOverrides.php 2 years ago PrivacyBadge.php 4 years ago RedirectOnActivation.php 4 years ago Referral.php 2 years ago Roles.php 10 months ago ScheduledTasks.php 1 year ago Settings.php 7 months ago Site.php 3 years ago TrackingCode.php 1 year ago Uninstaller.php 1 year ago Updater.php 10 months ago User.php 4 years ago
Settings.php
539 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
43 // NOTE: this is not a setting value, but is stored with setting values to avoid
44 // adding an extra get_option call to every WordPress backoffice request.
45 const INSTANCE_COMPONENTS_INSTALLED = 'instance-components-installed';
46
47 public static $is_doing_action_tracking_related = false;
48 /**
49 * @internal tests only
50 * @var bool
51 */
52 public $force_disable_addhandler = false;
53
54 /**
55 * Tests only
56 *
57 * @ignore
58 * @var bool
59 */
60 private $assume_is_network_enabled_in_tests = false;
61
62 /**
63 * Register default configuration set
64 *
65 * (public for tests)
66 *
67 * @var array
68 */
69 public $default_global_settings = [
70 // Plugin settings
71 'last_settings_update' => 0,
72 self::OPTION_LAST_TRACKING_SETTINGS_CHANGE => 0,
73 self::OPTION_KEY_STEALTH => [],
74 self::OPTION_KEY_CAPS_ACCESS => [],
75 self::NETWORK_CONFIG_OPTIONS => [],
76 self::DELETE_ALL_DATA_ON_UNINSTALL => true,
77 self::SITE_CURRENCY => 'USD',
78 // User settings: Stats configuration
79 // User settings: Tracking configuration
80 'track_mode' => 'disabled',
81 'track_js_endpoint' => 'default',
82 'track_api_endpoint' => 'default',
83 'track_codeposition' => 'footer',
84 'track_noscript' => false,
85 'track_content' => 'disabled',
86 'track_ecommerce' => true,
87 'track_search' => false,
88 'track_404' => false,
89 'tagmanger_container_ids' => [],
90 'add_post_annotations' => [],
91 'add_customvars_box' => false,
92 'js_manually' => '',
93 'noscript_manually' => '',
94 'add_download_extensions' => '',
95 'set_download_extensions' => '',
96 'set_link_classes' => '',
97 'set_download_classes' => '',
98 'core_version' => '',
99 'version_history' => [],
100 'mail_history' => [],
101 'disable_cookies' => false,
102 'cookie_consent' => CookieConsent::REQUIRE_NONE,
103 'force_post' => false,
104 'limit_cookies' => false,
105 'limit_cookies_visitor' => 34186669, // Matomo default 13 months
106 'limit_cookies_session' => 1800, // Matomo default 30 minutes
107 'limit_cookies_referral' => 15778463, // Matomo default 6 months
108 'track_admin' => false,
109 'track_across' => false,
110 'track_across_alias' => false,
111 'track_crossdomain_linking' => false,
112 'track_feed' => false,
113 'track_feed_addcampaign' => false,
114 'track_feed_campaign' => 'feed',
115 'track_heartbeat' => 0,
116 'track_user_id' => 'disabled',
117 'track_datacfasync' => false,
118 'track_jserrors' => false,
119 'force_protocol' => 'disabled',
120 'maxmind_license_key' => '',
121 self::SHOW_GET_STARTED_PAGE => 1,
122 self::DISABLE_ASYNC_ARCHIVING_OPTION_NAME => false,
123 self::GLOBAL_USER_AGENT_EXCLUSIONS => null,
124 ];
125
126 /**
127 * Settings stored per blog
128 *
129 * @var array
130 */
131 private $default_blog_settings = [
132 'noscript_code' => '',
133 'tracking_code' => '',
134 self::OPTION_LAST_TRACKING_CODE_UPDATE => 0,
135 self::USE_SESSION_VISITOR_ID_OPTION_NAME => false,
136 self::SERVER_SIDE_TRACKING_DELAY_SECS => 180,
137 ];
138
139 private $global_settings = [];
140 private $blog_settings = [];
141
142 private $settings_changed = [];
143
144 /**
145 * @var Logger
146 */
147 private $logger;
148
149 /**
150 * Constructor class to prepare settings manager
151 *
152 */
153 public function __construct() {
154 $this->logger = new Logger();
155
156 $this->init_settings();
157 }
158
159 public function init_settings() {
160 $this->settings_changed = [];
161 $this->global_settings = [];
162 $this->blog_settings = [];
163
164 if ( $this->is_network_enabled() ) {
165 $global_settings = get_site_option( self::OPTION_GLOBAL, [] );
166 } else {
167 $global_settings = get_option( self::OPTION_GLOBAL, [] );
168 }
169
170 if ( ! empty( $global_settings ) && is_array( $global_settings ) ) {
171 $this->global_settings = $global_settings;
172 }
173
174 $settings = get_option( self::OPTION, [] );
175
176 if ( ! empty( $settings ) && is_array( $settings ) ) {
177 $this->blog_settings = $settings;
178 }
179 }
180
181 public function get_customised_global_settings() {
182 $custom_settings = [];
183
184 foreach ( $this->global_settings as $key => $val ) {
185 if ( isset( $this->default_global_settings[ $key ] )
186 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
187 && $this->default_global_settings[ $key ] != $val ) {
188 $custom_settings[ $key ] = $val;
189 }
190 }
191
192 return $custom_settings;
193 }
194
195 public function uninstall() {
196 Uninstaller::uninstall_options( self::OPTION_PREFIX );
197 Uninstaller::uninstall_options( self::GLOBAL_OPTION_PREFIX );
198 Uninstaller::uninstall_site_meta( self::GLOBAL_OPTION_PREFIX );
199 $this->init_settings();
200 }
201
202 public function is_multisite() {
203 return function_exists( 'is_multisite' ) && is_multisite();
204 }
205
206 /**
207 * @api
208 */
209 public function is_network_enabled() {
210 if ( $this->assume_is_network_enabled_in_tests ) {
211 return true;
212 }
213
214 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
215 require_once ABSPATH . 'wp-admin/includes/plugin.php';
216 }
217
218 return is_plugin_active_for_network( 'matomo/matomo.php' );
219 }
220
221 /**
222 * Save all settings as WordPress options
223 */
224 public function save() {
225 if ( empty( $this->settings_changed ) ) {
226 $this->logger->log( 'No settings changed yet' );
227
228 return;
229 }
230
231 $this->logger->log( 'Save settings' );
232
233 if ( $this->is_network_enabled() ) {
234 update_site_option( self::OPTION_GLOBAL, $this->global_settings );
235 } else {
236 update_option( self::OPTION_GLOBAL, $this->global_settings );
237 }
238
239 update_option( self::OPTION, $this->blog_settings );
240
241 $keys_changed = array_values( array_unique( $this->settings_changed ) );
242 $this->settings_changed = [];
243
244 foreach ( $keys_changed as $key_changed ) {
245 if ( self::GLOBAL_USER_AGENT_EXCLUSIONS === $key_changed ) {
246 Bootstrap::do_bootstrap();
247 Cache::clearCacheGeneral();
248 }
249
250 do_action( 'matomo_setting_change_' . $key_changed );
251 }
252 }
253
254 /**
255 * Get a global option's value
256 *
257 * @param string $key
258 * option key
259 *
260 * @return string|array option value
261 * @api
262 */
263 public function get_global_option( $key ) {
264 if ( isset( $this->global_settings[ $key ] ) ) {
265 return $this->global_settings[ $key ];
266 }
267
268 if ( isset( $this->default_global_settings[ $key ] ) ) {
269 return $this->default_global_settings[ $key ];
270 }
271 }
272
273 /**
274 * Get an option's value related to a specific blog
275 *
276 * @param string $key option key
277 *
278 * @return string|array
279 * @api
280 */
281 public function get_option( $key ) {
282 if ( isset( $this->blog_settings[ $key ] ) ) {
283 return $this->blog_settings[ $key ];
284 }
285
286 if ( isset( $this->default_blog_settings[ $key ] ) ) {
287 return $this->default_blog_settings[ $key ];
288 }
289 }
290
291 private function convert_type( $value, $type ) {
292 if ( 'array' === $type && empty( $value ) ) {
293 $value = []; // prevent eg converting '' to array('')
294 } else {
295 settype( $value, $type );
296 }
297
298 return $value;
299 }
300
301 /**
302 * Set a global option's value
303 *
304 * @param string $key option key
305 * @param string|array $value new option value
306 */
307 public function set_global_option( $key, $value ) {
308 if ( isset( $this->default_global_settings[ $key ] ) ) {
309 $type = gettype( $this->default_global_settings[ $key ] );
310 $value = $this->convert_type( $value, $type );
311 }
312
313 if ( ! isset( $this->global_settings[ $key ] )
314 || $this->global_settings[ $key ] !== $value ) {
315 $this->settings_changed[] = $key;
316 $this->logger->log( 'Changed global option ' . $key . ': ' . ( is_array( $value ) ? wp_json_encode( $value ) : $value ) );
317
318 $this->global_settings[ $key ] = $value;
319 }
320 }
321
322 /**
323 * Set an option's value related to a specific blog
324 *
325 * @param string $key option key
326 * @param string $value new option value
327 */
328 public function set_option( $key, $value ) {
329 if ( isset( $this->default_blog_settings[ $key ] ) ) {
330 $type = gettype( $this->default_blog_settings[ $key ] );
331 $value = $this->convert_type( $value, $type );
332 }
333
334 if ( ! isset( $this->blog_settings[ $key ] )
335 || $this->blog_settings[ $key ] !== $value ) {
336 $this->settings_changed[] = $key;
337 $this->logger->log( 'Changed option ' . $key . ': ' . $value );
338 $this->blog_settings[ $key ] = $value;
339 }
340 }
341
342 /**
343 * @param array $values
344 *
345 * @api
346 */
347 public function apply_tracking_related_changes( $values ) {
348 $this->set_global_option( self::OPTION_LAST_TRACKING_SETTINGS_CHANGE, time() );
349
350 $this->apply_changes( $values );
351
352 if ( ! self::$is_doing_action_tracking_related ) {
353 // prevent recurison if any plugin was listening to this event and calling this method again
354 self::$is_doing_action_tracking_related = true;
355 do_action( 'matomo_tracking_settings_changed', $this, $values );
356 self::$is_doing_action_tracking_related = false;
357 }
358 }
359
360 public function should_disable_addhandler() {
361 if ( $this->force_disable_addhandler ) {
362 return true;
363 }
364
365 return defined( 'MATOMO_DISABLE_ADDHANDLER' ) && MATOMO_DISABLE_ADDHANDLER;
366 }
367
368 /**
369 * Apply new configuration
370 *
371 * @param array $settings
372 * new configuration set
373 *
374 * @api
375 */
376 public function apply_changes( $settings ) {
377 $this->logger->log( 'Apply changed settings:' );
378 foreach ( $this->default_global_settings as $key => $val ) {
379 if ( isset( $settings[ $key ] ) ) {
380 $this->set_global_option( $key, $settings[ $key ] );
381 }
382 }
383 foreach ( $this->default_blog_settings as $key => $val ) {
384 if ( isset( $settings[ $key ] ) ) {
385 $this->set_option( $key, $settings[ $key ] );
386 }
387 }
388 $this->set_global_option( 'last_settings_update', time() );
389
390 if ( $this->should_save_tracking_code_across_sites() ) {
391 // special case for when the same tracking code needs to be used across all instances
392 $this->set_global_option( 'js_manually', $this->get_option( 'tracking_code' ) );
393 $this->set_global_option( 'noscript_manually', $this->get_option( 'noscript_code' ) );
394 }
395
396 $this->save();
397 }
398
399 private function should_save_tracking_code_across_sites() {
400 return $this->is_network_enabled()
401 && $this->get_global_option( 'track_mode' ) === TrackingSettings::TRACK_MODE_MANUALLY;
402 }
403
404 public function get_js_tracking_code() {
405 if ( $this->should_save_tracking_code_across_sites() ) {
406 return $this->get_global_option( 'js_manually' );
407 }
408
409 return $this->get_option( 'tracking_code' );
410 }
411
412 public function get_noscript_tracking_code() {
413 if ( $this->should_save_tracking_code_across_sites() ) {
414 return $this->get_global_option( 'noscript_manually' );
415 }
416
417 return $this->get_option( 'noscript_code' );
418 }
419
420 public function is_cross_domain_linking_enabled() {
421 return $this->get_global_option( 'track_crossdomain_linking' );
422 }
423
424 public function should_delete_all_data_on_uninstall() {
425 if ( defined( 'MATOMO_REMOVE_ALL_DATA' ) ) {
426 return (bool) MATOMO_REMOVE_ALL_DATA;
427 }
428
429 return (bool) $this->get_global_option( self::DELETE_ALL_DATA_ON_UNINSTALL );
430 }
431
432 /**
433 * Check if feed tracking is enabled
434 *
435 * @return boolean Is feed tracking enabled?
436 */
437 public function is_track_feed() {
438 return $this->get_global_option( 'track_feed' );
439 }
440
441 /**
442 * Check if admin tracking is enabled
443 *
444 * @return boolean Is admin tracking enabled?
445 */
446 public function is_admin_tracking_enabled() {
447 return $this->get_global_option( 'track_admin' ) && is_admin();
448 }
449
450 public function is_current_tracking_code() {
451 $last_tracking_code_update = $this->get_option( self::OPTION_LAST_TRACKING_CODE_UPDATE );
452 $last_tracking_settings_update = $this->get_global_option( self::OPTION_LAST_TRACKING_SETTINGS_CHANGE );
453
454 return $last_tracking_code_update && $last_tracking_code_update > $last_tracking_settings_update;
455 }
456
457 /**
458 * Check if feed permalinks get a campaign parameter
459 *
460 * @return boolean Add campaign parameter to feed permalinks?
461 */
462 public function is_add_feed_campaign() {
463 return $this->get_global_option( 'track_feed_addcampaign' );
464 }
465
466 public function is_tracking_enabled() {
467 return $this->get_global_option( 'track_mode' ) !== 'disabled';
468 }
469
470 /**
471 * Check if noscript code insertion is enabled
472 *
473 * @return boolean Insert noscript code?
474 */
475 public function is_add_no_script_code() {
476 return $this->get_global_option( 'track_noscript' );
477 }
478
479 public function get_tracking_code_position() {
480 return $this->get_global_option( 'track_codeposition' );
481 }
482
483 public function track_404_enabled() {
484 return $this->get_global_option( 'track_404' );
485 }
486
487 public function track_user_id_enabled() {
488 return $this->get_global_option( 'track_user_id' ) !== 'disabled';
489 }
490
491 public function track_search_enabled() {
492 return ( is_search() && $this->get_global_option( 'track_search' ) );
493 }
494
495 public function set_assume_is_network_enabled_in_tests( $network_enabled = true ) {
496 $this->assume_is_network_enabled_in_tests = $network_enabled;
497 }
498
499 public function is_async_archiving_supported() {
500 return Process::isSupported() && ( ! defined( 'MATOMO_SUPPORT_ASYNC_ARCHIVING' ) || MATOMO_SUPPORT_ASYNC_ARCHIVING );
501 }
502
503 public function is_async_archiving_disabled_by_option() {
504 return (bool) $this->get_global_option( self::DISABLE_ASYNC_ARCHIVING_OPTION_NAME );
505 }
506
507 public function get_matomo_major_version() {
508 $core_version = $this->get_global_option( 'core_version' );
509 $core_version = isset( $core_version ) ? $core_version : '';
510
511 $parts = explode( '.', $core_version );
512 if ( empty( $parts ) ) {
513 return 0;
514 }
515
516 return (int) $parts[0];
517 }
518
519 public function set_global_user_agent_exclusions( $user_agents ) {
520 $this->set_global_option( self::GLOBAL_USER_AGENT_EXCLUSIONS, $user_agents );
521 }
522
523 public function get_global_user_agent_exclusions() {
524 $user_agents = $this->get_global_option( self::GLOBAL_USER_AGENT_EXCLUSIONS );
525 if ( ! is_array( $user_agents ) ) {
526 // only bootstrap if we can't access the SitesManager API.
527 // if we always bootstrap, it is possible to try initializing the FrontController before Matomo
528 // installation completes, which will fail.
529 if ( ! class_exists( \Piwik\Plugins\SitesManager\API::class ) ) {
530 Bootstrap::do_bootstrap();
531 }
532
533 $user_agents = \Piwik\Plugins\SitesManager\API::getInstance()->getExcludedUserAgentsGlobal();
534 $user_agents = explode( ',', $user_agents );
535 }
536 return $user_agents;
537 }
538 }
539