PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.4.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.4.1
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 / TrackingCode / TrackingCodeGenerator.php
matomo / classes / WpMatomo / TrackingCode Last commit date
TrackingCodeGenerator.php 5 years ago
TrackingCodeGenerator.php
390 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 */
9
10 namespace WpMatomo\TrackingCode;
11
12 use WpMatomo\Admin\TrackingSettings;
13 use WpMatomo\Admin\CookieConsent;
14 use WpMatomo\Logger;
15 use WpMatomo\Paths;
16 use WpMatomo\Settings;
17 use WpMatomo\Site;
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit; // if accessed directly
21 }
22
23 class TrackingCodeGenerator {
24
25 const TRACKPAGEVIEW = "_paq.push(['trackPageView']);";
26 const MTM_INIT = 'var _mtm = _mtm || [];';
27
28 /**
29 * @var Settings
30 */
31 private $settings;
32
33 /**
34 * @var Logger
35 */
36 private $logger;
37
38 /**
39 * @param Settings $settings
40 */
41 public function __construct( $settings ) {
42 $this->settings = $settings;
43 $this->logger = new Logger();
44 }
45
46 public function register_hooks() {
47 add_action( 'matomo_site_synced', array( $this, 'update_tracking_code' ), $prio = 10, $args = 0 );
48 add_action( 'matomo_tracking_settings_changed', array( $this, 'update_tracking_code' ), $prio = 10, $args = 0 );
49 }
50
51 public function update_tracking_code() {
52 if ( $this->settings->is_current_tracking_code()
53 && $this->settings->get_option( 'tracking_code' ) ) {
54 return false;
55 }
56
57 $track_mode = $this->settings->get_global_option( 'track_mode' );
58
59 if ( ! $this->settings->is_tracking_enabled()
60 || $track_mode == TrackingSettings::TRACK_MODE_MANUALLY ) {
61 return false;
62 }
63
64 $blod_id = get_current_blog_id();
65 $idsite = Site::get_matomo_site_id( $blod_id );
66
67 if ( ! $idsite ) {
68 $this->logger->log( 'Not found related idSite for blog ' . get_current_blog_id() );
69
70 return false;
71 }
72
73 if ( TrackingSettings::TRACK_MODE_DEFAULT === $track_mode ) {
74 $result = $this->prepare_tracking_code( $idsite );
75
76 if ( ! $this->settings->get_global_option( 'track_noscript' ) ) {
77 $result['noscript'] = '';
78 }
79 } elseif ( TrackingSettings::TRACK_MODE_TAGMANAGER === $track_mode && matomo_has_tag_manager() ) {
80 $result = $this->prepare_tagmanger_code( $this->settings, $this->logger );
81 } else {
82 $result = array(
83 'script' => '<!-- Matomo: no supported track_mode selected -->',
84 'noscript' => '',
85 );
86 }
87
88 if ( ! empty( $result['script'] ) ) {
89 $this->settings->set_option( 'tracking_code', $result['script'] );
90 $this->settings->set_option( 'noscript_code', $result['noscript'] );
91 }
92
93 $this->settings->set_option( Settings::OPTION_LAST_TRACKING_CODE_UPDATE, time() );
94 $this->settings->save();
95
96 return $result;
97 }
98
99 public function get_noscript_code() {
100 $this->update_tracking_code();
101
102 return $this->settings->get_noscript_tracking_code();
103 }
104
105 public function get_tracking_code() {
106 $this->update_tracking_code();
107
108 $tracking_code = $this->settings->get_js_tracking_code();
109
110 if ( $this->settings->track_user_id_enabled() ) {
111 $tracking_code = $this->apply_user_tracking( $tracking_code );
112 }
113 if ( $this->settings->track_404_enabled() && is_404() ) {
114 $tracking_code = $this->apply_404_changes( $tracking_code );
115 }
116 if ( $this->settings->track_search_enabled() ) {
117 $tracking_code = $this->apply_search_changes( $tracking_code );
118 }
119
120 return $tracking_code;
121 }
122
123 /**
124 * @param Settings $settings
125 * @param Logger $logger
126 *
127 * @return array
128 */
129 private function prepare_tagmanger_code( $settings, $logger ) {
130 $logger->log( 'Apply tag manager code changes:' );
131
132 $container_ids = $settings->get_global_option( 'tagmanger_container_ids' );
133
134 $code = '<!-- Matomo Tag Manager -->';
135
136 if ( ! empty( $container_ids ) && is_array( $container_ids ) ) {
137 $paths = new Paths();
138 $upload_url = $paths->get_upload_base_url();
139
140 foreach ( $container_ids as $container_id => $enabled ) {
141 if ( $enabled
142 && ctype_alnum( $container_id )
143 && strlen( $container_id ) <= 16 ) {
144 $container_url = $upload_url . '/container_' . urlencode( $container_id ) . '.js';
145
146 $data_cf_async = '';
147 if ( $settings->get_global_option( 'track_datacfasync' ) ) {
148 $data_cf_async = 'data-cfasync="false"';
149 }
150
151 if ( $settings->get_global_option( 'force_protocol' ) == 'https' ) {
152 $container_url = preg_replace( '(^http://)', 'https://', $container_url );
153 }
154
155 $code .= '
156 <script ' . $data_cf_async . '>
157 ' . self::MTM_INIT . '
158 _mtm.push({\'mtm.startTime\': (new Date().getTime()), \'event\': \'mtm.Start\'});
159 var d=document, g=d.createElement(\'script\'), s=d.getElementsByTagName(\'script\')[0];
160 g.type=\'text/javascript\'; g.async=true; g.src="' . $container_url . '"; s.parentNode.insertBefore(g,s);
161 </script>';
162 }
163 }
164 }
165
166 $code .= '<!-- End Matomo Tag Manager -->';
167
168 return array(
169 'script' => $code,
170 'noscript' => '',
171 );
172 }
173
174 public function get_tracker_endpoint() {
175 $paths = new Paths();
176
177 if ( $this->settings->get_global_option( 'track_api_endpoint' ) === 'restapi' ) {
178 $tracker_endpoint = $paths->get_tracker_api_rest_api_endpoint();
179 } else {
180 $tracker_endpoint = $paths->get_tracker_api_url_in_matomo_dir();
181 }
182
183 if ( $this->settings->get_global_option( 'force_protocol' ) === 'https' ) {
184 $tracker_endpoint = preg_replace( '(^http://)', 'https://', $tracker_endpoint );
185 } else {
186 $tracker_endpoint = preg_replace( '(^https?://)', '//', $tracker_endpoint );
187 }
188
189 return $tracker_endpoint;
190 }
191
192 public function get_js_endpoint() {
193 $paths = new Paths();
194 if ( $this->settings->get_global_option( 'track_js_endpoint' ) === 'restapi' ) {
195 $js_endpoint = $paths->get_js_tracker_rest_api_endpoint();
196 } elseif ( $this->settings->get_global_option( 'track_js_endpoint' ) === 'plugin' ) {
197 $js_endpoint = plugins_url( 'app/matomo.js', MATOMO_ANALYTICS_FILE );;
198 } else {
199 $js_endpoint = $paths->get_js_tracker_url_in_matomo_dir();
200 }
201
202 if ( $this->settings->get_global_option( 'force_protocol' ) === 'https' ) {
203 $js_endpoint = preg_replace( '(^http://)', 'https://', $js_endpoint );
204 } else {
205 $js_endpoint = preg_replace( '(^https?://)', '//', $js_endpoint );
206 }
207
208 return $js_endpoint;
209 }
210
211 /**
212 * @param $idsite
213 *
214 * @return array
215 */
216 public function prepare_tracking_code( $idsite ) {
217 $logLevel = is_admin() ? Logger::LEVEL_DEBUG : Logger::LEVEL_INFO;
218
219 $this->logger->log( 'Apply tracking code changes:', $logLevel );
220
221 $tracker_endpoint = $this->get_tracker_endpoint();
222 $js_endpoint = $this->get_js_endpoint();
223
224 $options = array();
225
226 if ( $this->settings->get_global_option( 'set_download_extensions' ) ) {
227 $options[] = "_paq.push(['setDownloadExtensions', " . wp_json_encode( $this->settings->get_global_option( 'set_download_extensions' ) ) . ']);';
228 }
229 if ( $this->settings->get_global_option( 'add_download_extensions' ) ) {
230 $options[] = "_paq.push(['addDownloadExtensions', " . wp_json_encode( $this->settings->get_global_option( 'add_download_extensions' ) ) . ']);';
231 }
232 if ( $this->settings->get_global_option( 'set_download_classes' ) ) {
233 $options[] = "_paq.push(['setDownloadClasses', " . wp_json_encode( $this->settings->get_global_option( 'set_download_classes' ) ) . ']);';
234 }
235 if ( $this->settings->get_global_option( 'set_link_classes' ) ) {
236 $options[] = "_paq.push(['setLinkClasses', " . wp_json_encode( $this->settings->get_global_option( 'set_link_classes' ) ) . ']);';
237 }
238 if ( $this->settings->get_global_option( 'disable_cookies' ) ) {
239 $options[] = "_paq.push(['disableCookies']);";
240 }
241 if ( $this->settings->get_global_option( 'track_crossdomain_linking' ) ) {
242 $options[] = "_paq.push(['enableCrossDomainLinking']);";
243 }
244 if ( $this->settings->get_global_option( 'track_jserrors' ) ) {
245 $options[] = "_paq.push(['enableJSErrorTracking']);";
246 }
247
248 $cookie_domain = $this->settings->get_tracking_cookie_domain();
249 if ( ! empty( $cookie_domain ) ) {
250 $options[] = '_paq.push(["setCookieDomain", ' . wp_json_encode( $cookie_domain ) . ']);';
251 }
252
253 $track_across_alias = $this->settings->get_global_option( 'track_across_alias' );
254
255 if ( $track_across_alias ) {
256 // todo detect more hosts such as when using WPML etc
257 $hosts = array( @parse_url( home_url(), PHP_URL_HOST ) );
258 $hosts = array_filter( $hosts );
259 $hosts = array_map(
260 function ( $host ) {
261 return '*.' . $host;
262 },
263 $hosts
264 );
265 if ( ! empty( $hosts ) ) {
266 $options[] = '_paq.push(["setDomains", ' . wp_json_encode( $hosts ) . ']);';
267 }
268 }
269 if ( $this->settings->get_global_option( 'force_post' ) ) {
270 $options[] = "_paq.push(['setRequestMethod', 'POST']);";
271 }
272
273 $cookieConsent = new CookieConsent();
274 $cookie_consent_option = $cookieConsent->get_tracking_consent_option( $this->settings->get_global_option( 'cookie_consent' ) );
275 // for unit test cases
276 if ( ! empty( $cookie_consent_option ) ) {
277 $options[] = $cookie_consent_option;
278 }
279
280 if ( $this->settings->get_global_option( 'limit_cookies' ) ) {
281 $options[] = "_paq.push(['setVisitorCookieTimeout', " . wp_json_encode( $this->settings->get_global_option( 'limit_cookies_visitor' ) ) . ']);';
282 $options[] = "_paq.push(['setSessionCookieTimeout', " . wp_json_encode( $this->settings->get_global_option( 'limit_cookies_session' ) ) . ']);';
283 $options[] = "_paq.push(['setReferralCookieTimeout', " . wp_json_encode( $this->settings->get_global_option( 'limit_cookies_referral' ) ) . ']);';
284 }
285 if ( $this->settings->get_global_option( 'track_content' ) === 'all' ) {
286 $options[] = "_paq.push(['trackAllContentImpressions']);";
287 } elseif ( $this->settings->get_global_option( 'track_content' ) === 'visible' ) {
288 $options[] = "_paq.push(['trackVisibleContentImpressions']);";
289 }
290 if ( (int) $this->settings->get_global_option( 'track_heartbeat' ) > 0 ) {
291 $options[] = "_paq.push(['enableHeartBeatTimer', " . intval( $this->settings->get_global_option( 'track_heartbeat' ) ) . ']);';
292 }
293
294 $data_cf_async = '';
295 $data_of_async_option = [];
296 if ( $this->settings->get_global_option( 'track_datacfasync' ) ) {
297 $data_cf_async = 'data-cfasync="false"';
298 $data_of_async_option['data-cfasync'] = "false";
299 }
300
301 $script = "var _paq = window._paq = window._paq || [];\n";
302 $script .= implode( "\n", $options );
303 $script .= self::TRACKPAGEVIEW;
304 $script .= "_paq.push(['enableLinkTracking']);_paq.push(['alwaysUseSendBeacon']);";
305 $script .= "_paq.push(['setTrackerUrl', " . wp_json_encode( $tracker_endpoint ) . ']);';
306 $script .= "_paq.push(['setSiteId', '" . intval( $idsite ) . "']);";
307 $script .= "var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
308 g.type='text/javascript'; g.async=true; g.src=" . wp_json_encode( $js_endpoint ) . '; s.parentNode.insertBefore(g,s);';
309
310 if ( function_exists( 'wp_get_inline_script_tag' ) ) {
311 $script = wp_get_inline_script_tag(
312 $script,
313 $data_of_async_option
314 );
315 } else {
316 /*
317 * method wp_get_inline_script_tag add a line feed.
318 * to get the unit tests pass, we add a line feed when not using the method
319 */
320 $script = '<script ' . $data_cf_async . ">\n" . $script . "\n</script>\n";
321 }
322
323 $script = '<!-- Matomo -->'.$script.'<!-- End Matomo Code -->';
324
325 $no_script = '<noscript><p><img referrerpolicy="no-referrer-when-downgrade" src="' . esc_url( $tracker_endpoint ) . '?idsite=' . intval( $idsite ) . '&amp;rec=1" style="border:0;" alt="" /></p></noscript>';
326
327 $script = apply_filters( 'matomo_tracking_code_script', $script, $idsite );
328 $script = apply_filters( 'matomo_tracking_code_noscript', $script, $idsite );
329
330 $this->logger->log( 'Finished tracking code: ' . $script, $logLevel );
331 $this->logger->log( 'Finished noscript code: ' . $no_script, $logLevel);
332
333 return array(
334 'script' => $script,
335 'noscript' => $no_script,
336 );
337 }
338
339 private function apply_404_changes( $tracking_code ) {
340 $this->logger->log( 'Apply 404 tracking changes. Blog ID: ' . get_current_blog_id() );
341
342 $code = "_paq.push(['setDocumentTitle', '404/URL = '+String(document.location.pathname+document.location.search).replace(/\//g,'%2f') + '/From = ' + String(document.referrer).replace(/\//g,'%2f')]);";
343 $tracking_code = str_replace( self::TRACKPAGEVIEW, $code . self::TRACKPAGEVIEW, $tracking_code );
344 $tracking_code = str_replace( self::MTM_INIT, $code . self::MTM_INIT, $tracking_code );
345
346 return $tracking_code;
347 }
348
349 private function apply_search_changes( $tracking_code ) {
350 $this->logger->log( 'Apply search tracking changes. Blog ID: ' . get_current_blog_id() );
351 $obj_search = new \WP_Query( 's=' . get_search_query() . '&showposts=-1' );
352 $int_result_count = $obj_search->post_count;
353
354 $code = "window._paq = window._paq || []; window._paq.push(['trackSiteSearch','" . get_search_query() . "', false, " . $int_result_count . "]);\n";
355 $tracking_code = str_replace( self::TRACKPAGEVIEW, $code . self::TRACKPAGEVIEW, $tracking_code );
356 $tracking_code = str_replace( self::MTM_INIT, $code . self::MTM_INIT, $tracking_code );
357
358 return $tracking_code;
359 }
360
361 private function apply_user_tracking( $tracking_code ) {
362 $user_id_to_track = null;
363 if ( \is_user_logged_in() ) {
364 // Get the User ID Admin option, and the current user's data
365 $uid_from = $this->settings->get_global_option( 'track_user_id' );
366 $current_user = wp_get_current_user(); // current user
367 // Get the user ID based on the admin setting
368 if ( 'uid' === $uid_from ) {
369 $user_id_to_track = $current_user->ID;
370 } elseif ( 'email' === $uid_from ) {
371 $user_id_to_track = $current_user->user_email;
372 } elseif ( 'username' === $uid_from ) {
373 $user_id_to_track = $current_user->user_login;
374 } elseif ( 'displayname' === $uid_from ) {
375 $user_id_to_track = $current_user->display_name;
376 }
377 }
378 $user_id_to_track = apply_filters( 'matomo_tracking_user_id', $user_id_to_track );
379 // Check we got a User ID to track, and track it
380 if ( isset( $user_id_to_track ) && ! empty( $user_id_to_track ) ) {
381 $code = "window._paq = window._paq || []; window._paq.push(['setUserId', '" . esc_js( $user_id_to_track ) . "']);\n";
382 $tracking_code = str_replace( self::TRACKPAGEVIEW, $code . self::TRACKPAGEVIEW, $tracking_code );
383 $tracking_code = str_replace( self::MTM_INIT, $code . self::MTM_INIT, $tracking_code );
384 }
385
386 return $tracking_code;
387 }
388
389 }
390