settings = $settings;
$this->logger = new Logger();
}
public static function get_disable_cookies_partial() {
// if ecommerce tracking is enabled, disableCookies can be added to _paq multiple times
// (since ecommerce tracking methods can be called before the main tracking JS in some situations).
// piwik.js complains if the initial _paq array has more than one of the same method, so
// we only add it if it's not there to begin with.
return 'if (!window._paq.find || !window._paq.find(function (m) { return m[0] === "disableCookies"; })) {
window._paq.push(["disableCookies"]);
}';
}
public function register_hooks() {
add_action( 'matomo_site_synced', [ $this, 'update_tracking_code' ], $prio = 10, $args = 0 );
add_action( 'matomo_tracking_settings_changed', [ $this, 'update_tracking_code' ], $prio = 10, $args = 0 );
}
public function update_tracking_code() {
if ( $this->settings->is_current_tracking_code()
&& $this->settings->get_option( 'tracking_code' ) ) {
return false;
}
$track_mode = $this->settings->get_global_option( 'track_mode' );
if ( ! $this->settings->is_tracking_enabled()
|| TrackingSettings::TRACK_MODE_MANUALLY === $track_mode ) {
return false;
}
$blod_id = get_current_blog_id();
$idsite = Site::get_matomo_site_id( $blod_id );
if ( ! $idsite ) {
$this->logger->log( 'Not found related idSite for blog ' . get_current_blog_id() );
return false;
}
if ( TrackingSettings::TRACK_MODE_DEFAULT === $track_mode ) {
$result = $this->prepare_tracking_code( $idsite );
if ( ! $this->settings->get_global_option( 'track_noscript' ) ) {
$result['noscript'] = '';
}
} elseif ( TrackingSettings::TRACK_MODE_TAGMANAGER === $track_mode && matomo_has_tag_manager() ) {
$result = $this->prepare_tagmanger_code( $this->settings, $this->logger );
} else {
$result = [
'script' => '',
'noscript' => '',
];
}
if ( ! empty( $result['script'] ) ) {
$this->settings->set_option( 'tracking_code', $result['script'] );
$this->settings->set_option( 'noscript_code', $result['noscript'] );
}
$this->settings->set_option( Settings::OPTION_LAST_TRACKING_CODE_UPDATE, time() );
$this->settings->save();
return $result;
}
public function get_noscript_code() {
$this->update_tracking_code();
return $this->settings->get_noscript_tracking_code();
}
public function get_tracking_code() {
$this->update_tracking_code();
$tracking_code = $this->settings->get_js_tracking_code();
if ( $this->settings->track_user_id_enabled() ) {
$tracking_code = $this->apply_user_tracking( $tracking_code );
}
if ( $this->settings->track_404_enabled() && is_404() ) {
$tracking_code = $this->apply_404_changes( $tracking_code );
}
if ( $this->settings->track_search_enabled() ) {
$tracking_code = $this->apply_search_changes( $tracking_code );
}
return $tracking_code;
}
/**
* @param Settings $settings
* @param Logger $logger
*
* @return array
*/
private function prepare_tagmanger_code( $settings, $logger ) {
$logger->log( 'Apply tag manager code changes:' );
$container_ids = $settings->get_global_option( 'tagmanger_container_ids' );
$code = '';
if ( ! empty( $container_ids ) && is_array( $container_ids ) ) {
$paths = new Paths();
$upload_url = $paths->get_upload_base_url();
foreach ( $container_ids as $container_id => $enabled ) {
if ( $enabled
&& ctype_alnum( $container_id )
&& strlen( $container_id ) <= 16 ) {
$container_url = $upload_url . '/container_' . rawurlencode( $container_id ) . '.js';
$data_cf_async = '';
if ( $settings->get_global_option( 'track_datacfasync' ) ) {
$data_cf_async = 'data-cfasync="false"';
}
if ( $settings->get_global_option( 'force_protocol' ) === 'https' ) {
$container_url = preg_replace( '(^http://)', 'https://', $container_url );
}
$code .= '
';
}
}
}
$code .= '';
return [
'script' => $code,
'noscript' => '',
];
}
public function get_tracker_endpoint() {
$paths = new Paths();
if ( $this->settings->get_global_option( 'track_api_endpoint' ) === 'restapi' ) {
$tracker_endpoint = $paths->get_tracker_api_rest_api_endpoint();
} else {
$tracker_endpoint = $paths->get_tracker_api_url_in_matomo_dir();
}
if ( $this->settings->get_global_option( 'force_protocol' ) === 'https' ) {
$tracker_endpoint = preg_replace( '(^http://)', 'https://', $tracker_endpoint );
} else {
$tracker_endpoint = preg_replace( '(^https?://)', '//', $tracker_endpoint );
}
return $tracker_endpoint;
}
public function get_js_endpoint() {
$paths = new Paths();
if ( $this->settings->get_global_option( 'track_js_endpoint' ) === 'restapi' ) {
$js_endpoint = $paths->get_js_tracker_rest_api_endpoint();
} elseif ( $this->settings->get_global_option( 'track_js_endpoint' ) === 'plugin' ) {
$js_endpoint = plugins_url( 'app/matomo.js', MATOMO_ANALYTICS_FILE );
} else {
$js_endpoint = $paths->get_js_tracker_url_in_matomo_dir();
}
if ( $this->settings->get_global_option( 'force_protocol' ) === 'https' ) {
$js_endpoint = preg_replace( '(^http://)', 'https://', $js_endpoint );
} else {
$js_endpoint = preg_replace( '(^https?://)', '//', $js_endpoint );
}
return $js_endpoint;
}
/**
* @param int|string $idsite
*
* @return array
*/
public function prepare_tracking_code( $idsite ) {
$log_level = is_admin() ? Logger::LEVEL_DEBUG : Logger::LEVEL_INFO;
$this->logger->log( 'Apply tracking code changes:', $log_level );
$tracker_endpoint = $this->get_tracker_endpoint();
$js_endpoint = $this->get_js_endpoint();
$options = [];
if ( $this->settings->get_global_option( 'set_download_extensions' ) ) {
$options[] = "_paq.push(['setDownloadExtensions', " . wp_json_encode( $this->settings->get_global_option( 'set_download_extensions' ) ) . ']);';
}
if ( $this->settings->get_global_option( 'add_download_extensions' ) ) {
$options[] = "_paq.push(['addDownloadExtensions', " . wp_json_encode( $this->settings->get_global_option( 'add_download_extensions' ) ) . ']);';
}
if ( $this->settings->get_global_option( 'set_download_classes' ) ) {
$options[] = "_paq.push(['setDownloadClasses', " . wp_json_encode( $this->settings->get_global_option( 'set_download_classes' ) ) . ']);';
}
if ( $this->settings->get_global_option( 'set_link_classes' ) ) {
$options[] = "_paq.push(['setLinkClasses', " . wp_json_encode( $this->settings->get_global_option( 'set_link_classes' ) ) . ']);';
}
if ( $this->settings->get_global_option( 'disable_cookies' ) ) {
$options[] = self::get_disable_cookies_partial();
}
if ( $this->settings->get_global_option( 'track_crossdomain_linking' ) ) {
$options[] = "_paq.push(['enableCrossDomainLinking']);";
}
if ( $this->settings->get_global_option( 'track_jserrors' ) ) {
$options[] = "_paq.push(['enableJSErrorTracking']);";
}
$cookie_domain = $this->settings->get_tracking_cookie_domain();
if ( ! empty( $cookie_domain ) ) {
$options[] = '_paq.push(["setCookieDomain", ' . wp_json_encode( $cookie_domain ) . ']);';
}
$track_across_alias = $this->settings->get_global_option( 'track_across_alias' );
if ( $track_across_alias ) {
// todo detect more hosts such as when using WPML etc
$hosts = [ wp_parse_url( home_url(), PHP_URL_HOST ) ];
$hosts = array_filter( $hosts );
$hosts = array_map(
function ( $host ) {
return '*.' . $host;
},
$hosts
);
if ( ! empty( $hosts ) ) {
$options[] = '_paq.push(["setDomains", ' . wp_json_encode( $hosts ) . ']);';
}
}
if ( $this->settings->get_global_option( 'force_post' ) ) {
$options[] = "_paq.push(['setRequestMethod', 'POST']);";
}
$cookie_consent = new CookieConsent();
$cookie_consent_option = $cookie_consent->get_tracking_consent_option( $this->settings->get_global_option( 'cookie_consent' ) );
// for unit test cases
if ( ! empty( $cookie_consent_option ) ) {
$options[] = $cookie_consent_option;
}
if ( $this->settings->get_global_option( 'limit_cookies' ) ) {
$options[] = "_paq.push(['setVisitorCookieTimeout', " . wp_json_encode( $this->settings->get_global_option( 'limit_cookies_visitor' ) ) . ']);';
$options[] = "_paq.push(['setSessionCookieTimeout', " . wp_json_encode( $this->settings->get_global_option( 'limit_cookies_session' ) ) . ']);';
$options[] = "_paq.push(['setReferralCookieTimeout', " . wp_json_encode( $this->settings->get_global_option( 'limit_cookies_referral' ) ) . ']);';
}
if ( $this->settings->get_global_option( 'track_content' ) === 'all' ) {
$options[] = "_paq.push(['trackAllContentImpressions']);";
} elseif ( $this->settings->get_global_option( 'track_content' ) === 'visible' ) {
$options[] = "_paq.push(['trackVisibleContentImpressions']);";
}
if ( (int) $this->settings->get_global_option( 'track_heartbeat' ) > 0 ) {
$options[] = "_paq.push(['enableHeartBeatTimer', " . intval( $this->settings->get_global_option( 'track_heartbeat' ) ) . ']);';
}
$data_cf_async = '';
$data_of_async_option = [];
if ( $this->settings->get_global_option( 'track_datacfasync' ) ) {
$data_cf_async = 'data-cfasync="false"';
$data_of_async_option['data-cfasync'] = 'false';
}
$script = "var _paq = window._paq = window._paq || [];\n";
$script .= implode( "\n", $options );
$script .= self::TRACKPAGEVIEW;
$script .= "_paq.push(['enableLinkTracking']);_paq.push(['alwaysUseSendBeacon']);";
$script .= "_paq.push(['setTrackerUrl', " . wp_json_encode( $tracker_endpoint ) . ']);';
$script .= "_paq.push(['setSiteId', '" . intval( $idsite ) . "']);";
$script .= "var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.src=" . wp_json_encode( $js_endpoint ) . '; s.parentNode.insertBefore(g,s);';
if ( function_exists( 'wp_get_inline_script_tag' ) ) {
$script = wp_get_inline_script_tag(
$script,
$data_of_async_option
);
} else {
/*
* method wp_get_inline_script_tag add a line feed.
* to get the unit tests pass, we add a line feed when not using the method
*/
$script = '\n";
}
$script = '' . $script . '';
$no_script = '';
$script = apply_filters( 'matomo_tracking_code_script', $script, $idsite );
$script = apply_filters( 'matomo_tracking_code_noscript', $script, $idsite );
$this->logger->log( 'Finished tracking code: ' . $script, $log_level );
$this->logger->log( 'Finished noscript code: ' . $no_script, $log_level );
return [
'script' => $script,
'noscript' => $no_script,
];
}
private function apply_404_changes( $tracking_code ) {
$this->logger->log( 'Apply 404 tracking changes. Blog ID: ' . get_current_blog_id() );
$code = "_paq.push(['setDocumentTitle', '404/URL = '+String(document.location.pathname+document.location.search).replace(/\//g,'%2f') + '/From = ' + String(document.referrer).replace(/\//g,'%2f')]);";
$tracking_code = str_replace( self::TRACKPAGEVIEW, $code . self::TRACKPAGEVIEW, $tracking_code );
$tracking_code = str_replace( self::MTM_INIT, $code . self::MTM_INIT, $tracking_code );
return $tracking_code;
}
private function apply_search_changes( $tracking_code ) {
$this->logger->log( 'Apply search tracking changes. Blog ID: ' . get_current_blog_id() );
$obj_search = new WP_Query( 's=' . get_search_query() . '&showposts=-1' );
$int_result_count = $obj_search->post_count;
$code = "window._paq = window._paq || []; window._paq.push(['trackSiteSearch','" . get_search_query() . "', false, " . $int_result_count . "]);\n";
$tracking_code = str_replace( self::TRACKPAGEVIEW, $code . self::TRACKPAGEVIEW, $tracking_code );
$tracking_code = str_replace( self::MTM_INIT, $code . self::MTM_INIT, $tracking_code );
return $tracking_code;
}
private function apply_user_tracking( $tracking_code ) {
$user_id_to_track = null;
if ( is_user_logged_in() ) {
// Get the User ID Admin option, and the current user's data
$uid_from = $this->settings->get_global_option( 'track_user_id' );
$current_user = wp_get_current_user(); // current user
// Get the user ID based on the admin setting
if ( 'uid' === $uid_from ) {
$user_id_to_track = $current_user->ID;
} elseif ( 'email' === $uid_from ) {
$user_id_to_track = $current_user->user_email;
} elseif ( 'username' === $uid_from ) {
$user_id_to_track = $current_user->user_login;
} elseif ( 'displayname' === $uid_from ) {
$user_id_to_track = $current_user->display_name;
}
}
$user_id_to_track = apply_filters( 'matomo_tracking_user_id', $user_id_to_track );
// Check we got a User ID to track, and track it
if ( isset( $user_id_to_track ) && ! empty( $user_id_to_track ) ) {
$code = "window._paq = window._paq || []; window._paq.push(['setUserId', '" . esc_js( $user_id_to_track ) . "']);\n";
$tracking_code = str_replace( self::TRACKPAGEVIEW, $code . self::TRACKPAGEVIEW, $tracking_code );
$tracking_code = str_replace( self::MTM_INIT, $code . self::MTM_INIT, $tracking_code );
}
return $tracking_code;
}
}