function matomo_page_alter in Matomo Analytics 7.2
Implements hook_page_alter() to insert JavaScript to the appropriate scope/region of the page.
File
- ./
matomo.module, line 91 - Drupal Module: Matomo
Code
function matomo_page_alter(&$page) {
global $user;
$id = variable_get('matomo_site_id', '');
// Get page status code for visibility filtering.
$status = drupal_get_http_header('Status');
$trackable_status_codes = array(
'403 Forbidden',
'404 Not Found',
);
// 1. Check if the matomo account number has a value.
// 2. Track page views based on visibility value.
// 3. Check if we should track the currently active user's role.
if (preg_match('/^\\d{1,}$/', $id) && (_matomo_visibility_pages() || in_array($status, $trackable_status_codes)) && _matomo_visibility_user($user)) {
$url_http = variable_get('matomo_url_http', '');
$url_https = variable_get('matomo_url_https', '');
$scope = variable_get('matomo_js_scope', 'header');
$set_custom_url = '';
$set_document_title = '';
// Add link tracking.
$link_settings = array();
$link_settings['trackMailto'] = variable_get('matomo_trackmailto', 1);
if (module_exists('colorbox') && ($track_colorbox = variable_get('matomo_trackcolorbox', 1))) {
$link_settings['trackColorbox'] = $track_colorbox;
}
drupal_add_js(array(
'matomo' => $link_settings,
), 'setting');
drupal_add_js(drupal_get_path('module', 'matomo') . '/matomo.js');
// Matomo can show a tree view of page titles that represents the site structure
// if setDocumentTitle() provides the page titles as a "/" delimited list.
// This may makes it easier to browse through the statistics of page titles
// on larger sites.
if (variable_get('matomo_page_title_hierarchy', FALSE) == TRUE) {
$titles = _matomo_get_hierarchy_titles();
if (variable_get('matomo_page_title_hierarchy_exclude_home', TRUE)) {
// Remove the "Home" item from the titles to flatten the tree view.
array_shift($titles);
}
// Remove all empty titles.
$titles = array_filter($titles);
if (!empty($titles)) {
// Encode title, at least to keep "/" intact.
$titles = array_map('rawurlencode', $titles);
$set_document_title = drupal_json_encode(implode('/', $titles));
}
}
// Add messages tracking.
$message_events = '';
if ($message_types = variable_get('matomo_trackmessages', array())) {
$message_types = array_values(array_filter($message_types));
$status_heading = array(
'status' => t('Status message'),
'warning' => t('Warning message'),
'error' => t('Error message'),
);
foreach (drupal_get_messages(NULL, FALSE) as $type => $messages) {
// Track only the selected message types.
if (in_array($type, $message_types)) {
foreach ($messages as $message) {
$message_events .= '_paq.push(["trackEvent", ' . drupal_json_encode(t('Messages')) . ', ' . drupal_json_encode($status_heading[$type]) . ', ' . drupal_json_encode(strip_tags($message)) . ']);';
}
}
}
}
// If this node is a translation of another node, pass the original
// node instead.
if (module_exists('translation') && variable_get('matomo_translation_set', 0)) {
// Check we have a node object, it supports translation, and its
// translated node ID (tnid) doesn't match its own node ID.
$node = menu_get_object();
if ($node && translation_supported_type($node->type) && !empty($node->tnid) && $node->tnid != $node->nid) {
$source_node = node_load($node->tnid);
$languages = language_list();
$set_custom_url = drupal_json_encode(url('node/' . $source_node->nid, array(
'language' => $languages[$source_node->language],
'absolute' => TRUE,
)));
}
}
// Track access denied (403) and file not found (404) pages.
if ($status == '403 Forbidden') {
$set_document_title = '"403/URL = " + encodeURIComponent(document.location.pathname+document.location.search) + "/From = " + encodeURIComponent(document.referrer)';
}
elseif ($status == '404 Not Found') {
$set_document_title = '"404/URL = " + encodeURIComponent(document.location.pathname+document.location.search) + "/From = " + encodeURIComponent(document.referrer)';
}
// #2693595: User has entered an invalid login and clicked on forgot
// password link. This link contains the username or email address and may
// get send to Matomo if we do not override it. Override only if 'name'
// query param exists. Last custom url condition, this need to win.
//
// URLs to protect are:
// - user/password?name=username
// - user/password?name=foo@example.com
if (arg(0) == 'user' && arg(1) == 'password' && array_key_exists('name', drupal_get_query_parameters())) {
$set_custom_url = drupal_json_encode(url('user/password'));
}
// Add custom variables.
$matomo_custom_vars = variable_get('matomo_custom_var', array());
$custom_variable = '';
for ($i = 1; $i < 6; $i++) {
$custom_var_name = !empty($matomo_custom_vars['slots'][$i]['name']) ? $matomo_custom_vars['slots'][$i]['name'] : '';
if (!empty($custom_var_name)) {
$custom_var_value = !empty($matomo_custom_vars['slots'][$i]['value']) ? $matomo_custom_vars['slots'][$i]['value'] : '';
$custom_var_scope = !empty($matomo_custom_vars['slots'][$i]['scope']) ? $matomo_custom_vars['slots'][$i]['scope'] : 'visit';
$types = array();
$node = menu_get_object();
if (is_object($node)) {
$types += array(
'node' => $node,
);
}
$custom_var_name = token_replace($custom_var_name, $types, array(
'clear' => TRUE,
));
$custom_var_value = token_replace($custom_var_value, $types, array(
'clear' => TRUE,
));
// Suppress empty custom names and/or variables.
if (!drupal_strlen(trim($custom_var_name)) || !drupal_strlen(trim($custom_var_value))) {
continue;
}
// Custom variables names and values are limited to 200 characters in
// length. It is recommended to store values that are as small as
// possible to ensure that the Matomo Tracking request URL doesn't go
// over the URL limit for the webserver or browser.
$custom_var_name = rtrim(substr($custom_var_name, 0, 200));
$custom_var_value = rtrim(substr($custom_var_value, 0, 200));
$custom_var_name = drupal_json_encode($custom_var_name);
$custom_var_value = drupal_json_encode($custom_var_value);
$custom_var_scope = drupal_json_encode($custom_var_scope);
$custom_variable .= "_paq.push(['setCustomVariable', {$i}, {$custom_var_name}, {$custom_var_value}, {$custom_var_scope}]);";
}
}
// Add any custom code snippets if specified.
$codesnippet_before = variable_get('matomo_codesnippet_before', '');
$codesnippet_after = variable_get('matomo_codesnippet_after', '');
// Build tracker code. See https://matomo.org/docs/javascript-tracking/#toc-asynchronous-tracking
$script = 'var _paq = _paq || [];';
$script .= '(function(){';
$script .= 'var u=(("https:" == document.location.protocol) ? "' . check_url($url_https) . '" : "' . check_url($url_http) . '");';
$script .= '_paq.push(["setSiteId", ' . drupal_json_encode(variable_get('matomo_site_id', '')) . ']);';
$script .= '_paq.push(["setTrackerUrl", u+"matomo.php"]);';
// Track logged in users across all devices.
if (variable_get('matomo_trackuserid', 0) && user_is_logged_in()) {
// The USER_ID value should be a unique, persistent, and non-personally
// identifiable string identifier that represents a user or signed-in
// account across devices.
$script .= '_paq.push(["setUserId", ' . drupal_json_encode(matomo_user_id_hash($user->uid)) . ']);';
}
// Set custom url.
if (!empty($set_custom_url)) {
$script .= '_paq.push(["setCustomUrl", ' . $set_custom_url . ']);';
}
// Set custom document title.
if (!empty($set_document_title)) {
$script .= '_paq.push(["setDocumentTitle", ' . $set_document_title . ']);';
}
// Custom file download extensions.
if (variable_get('matomo_track', 1) && !(variable_get('matomo_trackfiles_extensions', MATOMO_TRACKFILES_EXTENSIONS) == MATOMO_TRACKFILES_EXTENSIONS)) {
$script .= '_paq.push(["setDownloadExtensions", ' . drupal_json_encode(variable_get('matomo_trackfiles_extensions', MATOMO_TRACKFILES_EXTENSIONS)) . ']);';
}
// Disable tracking for visitors who have opted out from tracking via DNT (Do-Not-Track) header.
if (variable_get('matomo_privacy_donottrack', 1)) {
$script .= '_paq.push(["setDoNotTrack", 1]);';
}
// Disable Cookies.
if (variable_get('matomo_privacy_disable_cookies', 0)) {
$script .= '_paq.push(["disableCookies"]);';
}
// Domain tracking type.
global $cookie_domain;
$domain_mode = variable_get('matomo_domain_mode', 0);
// Per RFC 2109, cookie domains must contain at least one dot other than the
// first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
if ($domain_mode == 1 && count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
$script .= '_paq.push(["setCookieDomain", ' . drupal_json_encode($cookie_domain) . ']);';
}
// Ordering $custom_variable before $codesnippet_before allows users to add
// custom code snippets that may use deleteCustomVariable() and/or getCustomVariable().
if (!empty($custom_variable)) {
$script .= $custom_variable;
}
if (!empty($codesnippet_before)) {
$script .= $codesnippet_before;
}
// Site search tracking support.
// NOTE: It's recommended not to call trackPageView() on the Site Search Result page.
if (module_exists('search') && variable_get('matomo_site_search', FALSE) && arg(0) == 'search' && ($keys = matomo_search_get_keys())) {
// Parameters:
// 1. Search keyword searched for. Example: "Banana"
// 2. Search category selected in your search engine. If you do not need
// this, set to false. Example: "Organic Food"
// 3. Number of results on the Search results page. Zero indicates a
// 'No Result Search Keyword'. Set to false if you don't know.
//
// hook_preprocess_search_results() is not executed if search result is
// empty. Make sure the counter is set to 0 if there are no results.
$script .= '_paq.push(["trackSiteSearch", ' . drupal_json_encode($keys) . ', false, (window.matomo_search_results) ? window.matomo_search_results : 0]);';
}
else {
$script .= '_paq.push(["trackPageView"]);';
}
// Add link tracking.
if (variable_get('matomo_track', 1)) {
// Disable tracking of links with ".no-tracking" and ".colorbox" classes.
$ignore_classes = array(
'no-tracking',
'colorbox',
);
// Disable the download & outlink tracking for specific CSS classes.
// Custom code snippets with 'setIgnoreClasses' will override the value.
// https://developer.matomo.org/api-reference/tracking-javascript#disable-the-download-amp-outlink-tracking-for-specific-css-classes
$script .= '_paq.push(["setIgnoreClasses", ' . drupal_json_encode($ignore_classes) . ']);';
// Enable download & outlink link tracking.
$script .= '_paq.push(["enableLinkTracking"]);';
}
if (!empty($message_events)) {
$script .= $message_events;
}
if (!empty($codesnippet_after)) {
$script .= $codesnippet_after;
}
$script .= 'var d=document,';
$script .= 'g=d.createElement("script"),';
$script .= 's=d.getElementsByTagName("script")[0];';
$script .= 'g.type="text/javascript";';
$script .= 'g.defer=true;';
$script .= 'g.async=true;';
// Should a local cached copy of the tracking code be used?
if (variable_get('matomo_cache', 0) && ($url = _matomo_cache($url_http . 'matomo.js'))) {
// A dummy query-string is added to filenames, to gain control over
// browser-caching. The string changes on every update or full cache
// flush, forcing browsers to load a new copy of the files, as the
// URL changed.
$query_string = '?' . variable_get('css_js_query_string', '0');
$script .= 'g.src="' . $url . $query_string . '";';
}
else {
$script .= 'g.src=u+"matomo.js";';
}
$script .= 's.parentNode.insertBefore(g,s);';
$script .= '})();';
// Add tracker code to scope.
drupal_add_js($script, array(
'scope' => $scope,
'type' => 'inline',
));
}
}