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