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 / matomo.php
matomo Last commit date
app 3 days ago assets 3 days ago classes 3 days ago config 3 months ago languages 6 years ago misc 6 months ago node_modules 4 years ago plugins 3 days ago .htaccess 6 months ago LEGALNOTICE 2 years ago LICENSE 6 years ago matomo.php 3 days ago readme.txt 3 days ago shared.php 3 years ago uninstall.php 6 years ago wdio.conf.uninstall.ts 1 year ago
matomo.php
319 lines
1 <?php
2 /**
3 * Plugin Name: Matomo Analytics - Ethical Stats. Powerful Insights.
4 * Description: Privacy friendly, GDPR compliant and self-hosted. Matomo is the #1 Google Analytics alternative that gives you control of your data. Free and secure.
5 * Author: Matomo
6 * Author URI: https://matomo.org
7 * Version: 5.13.0
8 * Requires at least: 4.8
9 * Requires PHP: 7.2.5
10 * Domain Path: /languages
11 * WC requires at least: 2.4.0
12 * WC tested up to: 11.0.1
13 *
14 * Matomo - free/libre analytics platform
15 *
16 * @link https://matomo.org
17 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
18 * @package matomo
19 * phpcs:disable WordPress.Security.ValidatedSanitizedInput
20 * phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
21 * phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
22 */
23 if ( ! defined( 'ABSPATH' ) ) {
24 exit; // if accessed directly
25 }
26
27 if ( ! defined( 'MATOMO_ANALYTICS_FILE' ) ) {
28 define( 'MATOMO_ANALYTICS_FILE', __FILE__ );
29 }
30
31 if ( ! defined( 'MATOMO_MARKETPLACE_PLUGIN_NAME' ) ) {
32 define( 'MATOMO_MARKETPLACE_PLUGIN_NAME', 'matomo-marketplace-for-wordpress/matomo-marketplace-for-wordpress.php' );
33 }
34
35 $GLOBALS['MATOMO_PLUGINS_ENABLED'] = array();
36
37 /** MATOMO_PLUGIN_FILES => used to check for updates etc */
38 $GLOBALS['MATOMO_PLUGIN_FILES'] = array( MATOMO_ANALYTICS_FILE );
39
40 add_action(
41 'init',
42 function () {
43 load_plugin_textdomain( 'matomo', false, basename( __DIR__ ) . '/languages' );
44 }
45 );
46
47 function matomo_has_compatible_content_dir() {
48 if ( ! empty( $_SERVER['MATOMO_WP_ROOT_PATH'] )
49 && file_exists( rtrim( $_SERVER['MATOMO_WP_ROOT_PATH'], '/' ) . '/wp-load.php' ) ) {
50 return true;
51 }
52
53 if ( ! defined( 'WP_CONTENT_DIR' ) ) {
54 return false;
55 }
56
57 $content_dir = rtrim( rtrim( WP_CONTENT_DIR, '/' ), DIRECTORY_SEPARATOR );
58 $content_dir = wp_normalize_path( $content_dir );
59 $abs_path = wp_normalize_path( ABSPATH );
60
61 $abs_paths = array(
62 $abs_path . 'wp-content',
63 $abs_path . '/wp-content',
64 $abs_path . DIRECTORY_SEPARATOR . 'wp-content',
65 );
66
67 if ( in_array( $content_dir, $abs_paths, true ) ) {
68 return true;
69 }
70
71 $wpload_base = '../../../wp-load.php';
72 $wpload_full = __DIR__ . '/' . $wpload_base;
73 if ( file_exists( $wpload_full ) && is_readable( $wpload_full ) ) {
74 return true;
75 } elseif ( realpath( $wpload_full ) && file_exists( realpath( $wpload_full ) ) && is_readable( realpath( $wpload_full ) ) ) {
76 return true;
77 } elseif ( ! empty( $_SERVER['SCRIPT_FILENAME'] ) && file_exists( $_SERVER['SCRIPT_FILENAME'] ) ) {
78 // seems symlinked... eg the wp-content dir or wp-content/plugins dir is symlinked from some very much other place...
79 $wpload_full = dirname( $_SERVER['SCRIPT_FILENAME'] ) . '/' . $wpload_base;
80 if ( file_exists( $wpload_full ) ) {
81 return true;
82 } elseif ( realpath( $wpload_full ) && file_exists( realpath( $wpload_full ) ) ) {
83 return true;
84 } elseif ( file_exists( dirname( $_SERVER['SCRIPT_FILENAME'] ) ) . '/wp-load.php' ) {
85 return true;
86 }
87 }
88
89 // look in plugins directory if there is a config file for us
90 $wpload_config = __DIR__ . '/../matomo.wpload_dir.php';
91 if ( file_exists( $wpload_config ) && is_readable( $wpload_config ) ) {
92 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
93 $content = @file_get_contents( $wpload_config ); // we do not include that file for security reasons
94 if ( ! empty( $content ) ) {
95 $content = str_replace( array( '<?php', 'exit;' ), '', $content );
96 $content = preg_replace( '/\s/', '', $content );
97 $content = trim( ltrim( trim( $content ), '#' ) ); // the path may be commented out # /abs/path
98 if ( strpos( $content, DIRECTORY_SEPARATOR ) === 0 ) {
99 $wpload_file = rtrim( $content, DIRECTORY_SEPARATOR ) . '/wp-load.php';
100 return file_exists( $wpload_file ) && is_readable( $wpload_file );
101 }
102 }
103 }
104
105 return false;
106 }
107
108 function matomo_header_icon( $full = false ) {
109 $class = 'matomo-header-icon';
110 $file = 'logo.png';
111 if ( $full ) {
112 $file = 'logo-full.png';
113 $class .= '-full';
114 }
115 echo '<img height="32" src="' . esc_url( plugins_url( 'assets/img/' . $file, MATOMO_ANALYTICS_FILE ) . '?v=' . rawurlencode( matomo_get_asset_version() ) ) . '" class="' . esc_attr( $class ) . '">';
116 }
117
118 function matomo_is_app_request() {
119 return ! empty( $_SERVER['SCRIPT_NAME'] )
120 && ( substr( $_SERVER['SCRIPT_NAME'], - 1 * strlen( 'matomo/app/index.php' ) ) === 'matomo/app/index.php' );
121 }
122
123 function matomo_has_tag_manager() {
124 if ( defined( 'MATOMO_ENABLE_TAG_MANAGER' ) ) {
125 return ! empty( MATOMO_ENABLE_TAG_MANAGER );
126 }
127
128 $is_multisite = function_exists( 'is_multisite' ) && is_multisite();
129 if ( $is_multisite ) {
130 return false;
131 }
132
133 return true;
134 }
135
136 function matomo_anonymize_value( $value ) {
137 if ( is_string( $value ) && ! empty( $value ) ) {
138 $values_to_anonymize = array(
139 ABSPATH => '$abs_path/',
140 str_replace( '/', '\/', ABSPATH ) => '$abs_path\/',
141 str_replace( '/', '\\', ABSPATH ) => '$abs_path\/',
142 WP_CONTENT_DIR => '$WP_CONTENT_DIR/',
143 str_replace( '/', '\\', WP_CONTENT_DIR ) => '$WP_CONTENT_DIR\\',
144 home_url() => '$home_url',
145 site_url() => '$site_url',
146 DB_PASSWORD => '$DB_PASSWORD',
147 DB_USER => '$DB_USER',
148 DB_HOST => '$DB_HOST',
149 DB_NAME => '$DB_NAME',
150 );
151 $keys = array( 'AUTH_KEY', 'SECURE_AUTH_KEY', 'LOGGED_IN_KEY', 'AUTH_SALT', 'NONCE_KEY', 'SECURE_AUTH_SALT', 'LOGGED_IN_SALT', 'NONCE_SALT' );
152 foreach ( $keys as $key ) {
153 if ( defined( $key ) ) {
154 $const_value = constant( $key );
155 if ( ! empty( $const_value ) && is_string( $const_value ) && strlen( $key ) > 3 ) {
156 $values_to_anonymize[ $const_value ] = '$' . $key;
157 }
158 }
159 }
160 foreach ( $values_to_anonymize as $search => $replace ) {
161 if ( $search ) {
162 $value = str_replace( $search, $replace, $value );
163 }
164 }
165 // replace anything like token_auth etc or md5 or sha1 ...
166 $value = preg_replace( '/[[:xdigit:]]{31,80}/', 'TOKEN_REPLACED', $value );
167 }
168
169 return $value;
170 }
171
172 $GLOBALS['MATOMO_MARKETPLACE_PLUGINS'] = array();
173
174 function matomo_rel_path( $to_dir, $from_dir ) {
175 $to_dir_parts = array_values( array_filter( explode( DIRECTORY_SEPARATOR, $to_dir ) ) );
176 $from_dir_parts = array_values( array_filter( explode( DIRECTORY_SEPARATOR, $from_dir ) ) );
177
178 $to_index = 0;
179 $from_index = 0;
180
181 $to_dir_segment_count = count( $to_dir_parts );
182 $from_dir_segment_count = count( $from_dir_parts );
183
184 // skip over common parts of $to_dir and $from_dir
185 for ( ; $to_index < $to_dir_segment_count && $from_index < $from_dir_segment_count && $to_dir_parts[ $to_index ] === $from_dir_parts[ $from_index ]; ++$to_index, ++$from_index );
186
187 // ascend from $to_dir to common root it has with $from_dir
188 $relative_path = str_repeat( '..' . DIRECTORY_SEPARATOR, count( $from_dir_parts ) - $from_index );
189
190 // descend from common root to target in rest of $to_dir
191 $rest = array_slice( $to_dir_parts, $to_index );
192 if ( ! empty( $rest ) ) {
193 $relative_path = $relative_path . implode( DIRECTORY_SEPARATOR, $rest );
194 }
195
196 return $relative_path;
197 }
198
199 function matomo_is_plugin_compatible( $wp_plugin_file ) {
200 require_once __DIR__ . '/app/core/Version.php';
201
202 $plugin_manifest_path = dirname( $wp_plugin_file ) . '/plugin.json';
203 clearstatcache( false, $plugin_manifest_path );
204
205 if ( ! is_file( $plugin_manifest_path )
206 || ! is_readable( $plugin_manifest_path )
207 ) {
208 return false;
209 }
210
211 $modified_time = filemtime( $plugin_manifest_path );
212 if ( false === $modified_time ) {
213 return false;
214 }
215
216 $cache_key = 'matomo_plugin_compatible_' . basename( $wp_plugin_file ) . '_' . \Piwik\Version::VERSION . '_' . $modified_time;
217 $cache_value = get_transient( $cache_key );
218 if ( false === $cache_value ) {
219 // assume the plugin is not compatible in case the below code fails.
220 // this way, the next request will work rather than trigger the same
221 // error.
222 $one_day = 24 * 60 * 60;
223 set_transient( $cache_key, 0, $one_day );
224
225 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
226 $plugin_manifest = file_get_contents( $plugin_manifest_path );
227 $plugin_manifest = json_decode( $plugin_manifest, true );
228 if ( empty( $plugin_manifest['require']['matomo'] )
229 && empty( $plugin_manifest['require']['piwik'] )
230 ) {
231 return false;
232 }
233
234 $core_requirement = isset( $plugin_manifest['require']['matomo'] )
235 ? $plugin_manifest['require']['matomo']
236 : $plugin_manifest['require']['piwik'];
237
238 require_once __DIR__ . '/app/vendor/autoload.php';
239
240 $dependency = new \Piwik\Plugin\Dependency();
241 $missing_dependencies = $dependency->getMissingDependencies( [ 'matomo' => $core_requirement ] );
242
243 $is_compatible = empty( $missing_dependencies );
244 $cache_value = (int) $is_compatible;
245
246 $two_months = 60 * 60 * 24 * 60;
247 set_transient( $cache_key, $cache_value, $two_months );
248 }
249
250 return 1 === (int) $cache_value;
251 }
252
253 function matomo_filter_incompatible_plugins( &$plugin_list ) {
254 if ( empty( $GLOBALS['MATOMO_MARKETPLACE_PLUGINS'] ) ) {
255 return;
256 }
257
258 $incompatible_plugins = [];
259 foreach ( $GLOBALS['MATOMO_MARKETPLACE_PLUGINS'] as $wp_plugin_file ) {
260 if ( matomo_is_plugin_compatible( $wp_plugin_file ) ) {
261 continue;
262 }
263
264 $plugin_name = basename( dirname( $wp_plugin_file ) );
265 $incompatible_plugins[] = $plugin_name;
266 }
267
268 $plugin_list = array_values( array_diff( $plugin_list, $incompatible_plugins ) );
269 }
270
271 function matomo_add_plugin( $plugins_directory, $wp_plugin_file, $is_marketplace_plugin = false ) {
272 if ( ! in_array( $wp_plugin_file, $GLOBALS['MATOMO_PLUGIN_FILES'], true ) ) {
273 $GLOBALS['MATOMO_PLUGIN_FILES'][] = $wp_plugin_file;
274 }
275
276 if ( empty( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) ) {
277 $GLOBALS['MATOMO_PLUGIN_DIRS'] = array();
278 }
279
280 if ( $is_marketplace_plugin && dirname( $wp_plugin_file ) === $plugins_directory ) {
281 $GLOBALS['MATOMO_MARKETPLACE_PLUGINS'][] = $wp_plugin_file;
282 }
283
284 $GLOBALS['MATOMO_PLUGINS_ENABLED'][] = basename( $plugins_directory );
285 $root_dir = dirname( $plugins_directory );
286 foreach ( $GLOBALS['MATOMO_PLUGIN_DIRS'] as $path ) {
287 if ( $path['pluginsPathAbsolute'] === $root_dir ) {
288 return; // already added
289 }
290 }
291
292 $matomo_dir = __DIR__ . DIRECTORY_SEPARATOR . 'app';
293 $webroot_dir = matomo_rel_path( $root_dir, $matomo_dir );
294
295 $GLOBALS['MATOMO_PLUGIN_DIRS'][] = array(
296 'pluginsPathAbsolute' => $root_dir,
297 'webrootDirRelativeToMatomo' => $webroot_dir,
298 );
299 }
300
301 function matomo_get_asset_version() {
302 $version = \WpMatomo::VERSION;
303 $version = apply_filters( 'matomo_asset_version', $version );
304 return $version;
305 }
306
307 if ( matomo_is_app_request() || ! empty( $GLOBALS['MATOMO_LOADED_DIRECTLY'] ) ) {
308 // prevent layout being broken when thegem theme is used. their lazy items class causes the reporting UI to not appear
309 // because it creates a JS error because of escaping " too often. only breaks when " Activate image loading optimization (for desktops)"
310 // is enabled in the general theme settings
311 add_filter( 'thegem_lazy_items_need_process_content', '__return_false', 99999999, $args = 0 );
312 }
313
314 require_once __DIR__ . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . 'WpMatomo.php';
315 require 'shared.php';
316 matomo_add_plugin( __DIR__ . '/plugins/WordPress', MATOMO_ANALYTICS_FILE );
317
318 new WpMatomo();
319