Skip to content
Snippets Groups Projects
Commit 78f15415 authored by Stefan Borchert's avatar Stefan Borchert Committed by Florent Torregrosa
Browse files

Issue #2978703 by stBorchert, ricovandevin, Grimreaper, somersoft, zcht,...

Issue #2978703 by stBorchert, ricovandevin, Grimreaper, somersoft, zcht, tsteur, pefferen: Support Matomo Tag Manager. Fix PHPCS, Composer normalize, some PHPMD, PHPCS Fixer.
parent 20cae770
No related branches found
No related tags found
No related merge requests found
Showing
with 584 additions and 9 deletions
......@@ -86,7 +86,7 @@ at https://matomo.org/docs/javascript-tracking/#toc-custom-variables.
You can include additional JavaScript snippets in the custom javascript
code textarea. These can be found on various blog posts, or on the
official Matomo pages. Support is not provided for any customisations
official Matomo pages. Support is not provided for any customizations
you include.
To speed up page loading you may also cache the Matomo "matomo.js"
......
{
"name": "drupal/matomo",
"type": "drupal-module",
"description": "Adds Matomo javascript tracking code to all your site's pages.",
"license": "GPL-2.0-or-later",
"conflict": {
"drupal/csp": "<1.12"
},
"type": "drupal-module",
"require-dev": {
"drupal/csp": "~1.12",
"drupal/php": "~1.1",
"drupal/token": "~1.9",
"drupal/csp": "~1.12"
"drupal/token": "~1.9"
},
"conflict": {
"drupal/csp": "<1.12"
}
}
......@@ -112,6 +112,9 @@ matomo.settings:
translation_set:
type: boolean
label: 'Track translation sets as one unit'
disable_tracking:
type: boolean
label: 'Disable tracking'
cache:
type: boolean
label: 'Locally cache tracking code file'
......
......@@ -89,3 +89,13 @@ function matomo_update_8101() {
$config->set('privacy.disablecookies', FALSE);
$config->save(TRUE);
}
/**
* Add new disable tracking setting to Matomo default configuration.
*/
function matomo_update_8102() {
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('matomo.settings');
$config->set('disable_tracking', FALSE);
$config->save(TRUE);
}
......@@ -47,7 +47,12 @@ function matomo_page_attachments(array &$page) {
$request = \Drupal::request();
// Add module cache tags.
$page['#cache']['tags'] = Cache::mergeTags(isset($page['#cache']['tags']) ? $page['#cache']['tags'] : [], $config->getCacheTags());
$page['#cache']['tags'] = Cache::mergeTags($page['#cache']['tags'] ?? [], $config->getCacheTags());
if ($config->get('disable_tracking')) {
// Tracking is disabled in favor of other methods (e.g. Tag Manager).
return;
}
// Get page http status code for visibility filtering.
$status = NULL;
......@@ -436,7 +441,7 @@ function matomo_form_user_form_alter(&$form, FormStateInterface $form_state) {
'#type' => 'checkbox',
'#title' => t('Enable user tracking'),
'#description' => $description,
'#default_value' => isset($account_data_matomo['user_account_users']) ? $account_data_matomo['user_account_users'] : ($visibility_users == 1),
'#default_value' => $account_data_matomo['user_account_users'] ?? ($visibility_users == 1),
'#disabled' => $disabled,
];
......
container_location: ''
matomo_tagmanager.settings:
type: config_object
label: 'Matomo Tag Manager settings'
mapping:
container_location:
type: string
label: 'Container file location'
matomo_tagmanager.container.*:
type: config_entity
label: 'Container settings'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
weight:
type: integer
label: 'Weight'
container_id:
type: string
label: 'Container ID'
name: 'Matomo Analytics Tag Manager'
type: module
description: 'Matomo Tag Manager integration.'
core_version_requirement: ^9.2 || ^10
package: 'Statistics'
configure: entity.matomo_tagmanager_container.collection
dependencies:
- matomo:matomo
entity.matomo_tagmanager_container.add_form:
title: 'Add container'
route_name: entity.matomo_tagmanager_container.add_form
appears_on:
- entity.matomo_tagmanager_container.collection
matomo_tagmanager.settings_form:
title: 'Matomo Tag Manager settings'
route_name: matomo_tagmanager.settings_form
parent: matomo.admin_settings_form
entity.matomo_tagmanager_container.collection:
title: 'Matomo Tag Manager containers'
description: 'Configure the website integration with Matomo Tag Manager.'
route_name: entity.matomo_tagmanager_container.collection
parent: matomo.admin_settings_form
matomo_tagmanager.settings_tab:
title: 'Tag Manager settings'
route_name: matomo_tagmanager.settings_form
base_route: matomo.admin_settings_form
matomo_tagmanager.container_list_tab:
title: 'Tag Manager containers'
route_name: entity.matomo_tagmanager_container.collection
base_route: matomo.admin_settings_form
<?php
/**
* @file
* Hook implementations for Matomo Tag Manager.
*/
declare(strict_types = 1);
use Drupal\Core\Cache\Cache;
use Drupal\matomo\Component\Render\MatomoJavaScriptSnippet;
/**
* Implements hook_page_attachments().
*/
function matomo_tagmanager_page_attachments(array &$page) {
$account = \Drupal::currentUser();
$config = \Drupal::config('matomo.settings');
$tagmanager_config = \Drupal::config('matomo_tagmanager.settings');
/** @var Drupal\matomo_tagmanager\ContainerStorageInterface $container_storage */
$container_storage = \Drupal::entityTypeManager()->getStorage('matomo_tagmanager_container');
$containers = $container_storage->loadEnabled();
if (empty($containers)) {
return;
}
$request = \Drupal::request();
// Get page http status code for visibility filtering.
$status = NULL;
$exception = $request->attributes->get('exception');
if ($exception) {
$status = $exception->getStatusCode();
}
$trackable_status_codes = [
// "Forbidden" status code.
'403',
// "Not Found" status code.
'404',
];
if (!(_matomo_visibility_pages() || in_array($status, $trackable_status_codes)) || !_matomo_visibility_user($account)) {
// Stop early.
return;
}
$url_http = $config->get('url_http');
$url_https = $config->get('url_https');
$container_location = $tagmanager_config->get('container_location') ?? '';
$matomo_url = empty($url_https) ? $url_http : $url_https;
if (empty($matomo_url)) {
// This should not happen.
return;
}
// Build tracker code.
// @see https://developer.matomo.org/guides/tagmanager/integration-plugin
$script = 'var _mtm = window._mtm = window._mtm || [];';
$script .= '_mtm.push({"mtm.startTime": (new Date().getTime()), "event": "mtm.Start"});';
$script .= 'var d = document, g = d.createElement("script"), s = d.getElementsByTagName("script")[0];';
$script .= 'g.type = "text/javascript";';
$script .= 'g.async = true;';
$script .= 'g.src="${MATOMOURL}/${CONTAINERLOCATION}container_${CONTAINERID}.js";';
$script .= 's.parentNode.insertBefore(g, s);';
$cache_tags = $page['#cache']['tags'] ?? [];
foreach ($containers as $container) {
// Add cache tags per container.
$cache_tags = Cache::mergeTags($cache_tags, $config->getCacheTags());
// Build script.
$replacements = [
'${MATOMOURL}' => rtrim($matomo_url, '/'),
'${CONTAINERLOCATION}' => ltrim($container_location, '/'),
'${CONTAINERID}' => $container->containerId(),
];
$container_script = strtr($script, $replacements);
$page['#attached']['html_head'][] = [
[
'#tag' => 'script',
'#value' => new MatomoJavaScriptSnippet($container_script),
],
'matomo_tagmanager_tracking_script__' . $container->id(),
];
}
// Set cache tags.
$page['#cache']['tags'] = $cache_tags;
}
administer matomo tag manager:
title: 'Administer Matomo Tag Manager'
description: 'Configure the website integration with Matomo Tag Manager.'
matomo_tagmanager.settings_form:
path: '/admin/config/system/matomo/tagmanager/settings'
defaults:
_title: 'Matomo Tag Manager settings'
_form: '\Drupal\matomo_tagmanager\Form\SettingsForm'
requirements:
_permission: 'administer matomo tag manager'
# Container management.
entity.matomo_tagmanager_container.collection:
path: '/admin/config/system/matomo/tagmanager'
defaults:
_entity_list: 'matomo_tagmanager_container'
_title: 'Matomo Tag Manager containers'
requirements:
_permission: 'administer matomo tag manager'
entity.matomo_tagmanager_container.add_form:
path: '/admin/config/system/matomo/tagmanager/add'
defaults:
_entity_form: 'matomo_tagmanager_container'
_title_callback: '\Drupal\matomo_tagmanager\Controller\ContainerController::addTitle'
entity_type_id: 'matomo_tagmanager_container'
requirements:
_permission: 'administer matomo tag manager'
entity.matomo_tagmanager_container.enable:
path: '/admin/config/system/matomo/tagmanager/manage/{matomo_tagmanager_container}/enable'
defaults:
_controller: '\Drupal\matomo_tagmanager\Controller\ContainerController::enable'
entity_type: 'matomo_tagmanager_container'
requirements:
_permission: 'administer matomo tag manager'
entity.matomo_tagmanager_container.disable:
path: '/admin/config/system/matomo/tagmanager/manage/{matomo_tagmanager_container}/disable'
defaults:
_controller: '\Drupal\matomo_tagmanager\Controller\ContainerController::disable'
entity_type: 'matomo_tagmanager_container'
requirements:
_permission: 'administer matomo tag manager'
entity.matomo_tagmanager_container.edit_form:
path: '/admin/config/system/matomo/tagmanager/manage/{matomo_tagmanager_container}'
defaults:
_entity_form: matomo_tagmanager_container
_title_callback: '\Drupal\matomo_tagmanager\Controller\ContainerController::editTitle'
requirements:
_permission: 'administer matomo tag manager'
entity.matomo_tagmanager_container.delete_form:
path: '/admin/config/system/matomo/tagmanager/manage/{matomo_tagmanager_container}/delete'
defaults:
_entity_form: 'matomo_tagmanager_container.delete'
requirements:
_permission: 'administer matomo tag manager'
<?php
declare(strict_types = 1);
namespace Drupal\matomo_tagmanager;
use Drupal\Core\Config\Entity\DraggableListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\matomo_tagmanager\Entity\ContainerInterface;
/**
* Defines a listing of Matomo Tag Manager container configuration entities.
*
* @see \Drupal\matomo_tagmanager\Entity\Container
*/
class ContainerListBuilder extends DraggableListBuilder {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
protected $entitiesKey = 'containers';
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header = [];
$header['label'] = $this->t('Label');
$header['entity_id'] = $this->t('Machine name');
$header['container_id'] = $this->t('Container ID');
$header['status'] = $this->t('Status');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
\assert($entity instanceof ContainerInterface);
$row = [];
$row['label'] = $entity->label();
$row['entity_id'] = ['#markup' => $entity->id()];
$row['container_id'] = ['#markup' => $entity->containerId()];
$row['status'] = ['#markup' => $entity->status() ? $this->t('enabled') : $this->t('disabled')];
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
public function getFormId(): string {
return 'matomo_tagmanager_container_list';
}
}
<?php
declare(strict_types = 1);
namespace Drupal\matomo_tagmanager;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
/**
* Defines the Matomo Tag Manager container storage handler class.
*/
class ContainerStorage extends ConfigEntityStorage implements ContainerStorageInterface {
/**
* {@inheritdoc}
*/
public function loadAll(): array {
$query = $this->getQuery()
->sort('weight')
->sort('id');
$ids = $query->execute();
return $this->loadMultiple($ids);
}
/**
* {@inheritdoc}
*/
public function loadEnabled(): array {
$query = $this->getQuery()
->condition('status', TRUE)
->sort('weight')
->sort('id');
$ids = $query->execute();
return $this->loadMultiple($ids);
}
}
<?php
declare(strict_types = 1);
namespace Drupal\matomo_tagmanager;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
/**
* Interface for Matomo Tag Manager container storage handler.
*/
interface ContainerStorageInterface extends ConfigEntityStorageInterface {
/**
* Load all defined containers.
*
* @return \Drupal\matomo_tagmanager\Entity\ContainerInterface[]
* List of all containers ordered by weight, id.
*/
public function loadAll(): array;
/**
* Load all enabled containers.
*
* @return \Drupal\matomo_tagmanager\Entity\ContainerInterface[]
* List of enabled containers ordered by weight, id.
*/
public function loadEnabled(): array;
}
<?php
declare(strict_types = 1);
namespace Drupal\matomo_tagmanager\Controller;
use Drupal\Core\Entity\Controller\EntityController;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\matomo_tagmanager\Entity\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Controller for the Matomo Tag Manager container configuration entity type.
*/
class ContainerController extends EntityController {
/**
* Route title callback.
*
* @param string $entity_type_id
* The entity type ID.
*
* @return string
* The title for the add entity page.
*/
public function addTitle($entity_type_id) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
return $this->t('Add @entity-type', ['@entity-type' => $entity_type->getSingularLabel()]);
}
/**
* Route title callback.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param \Drupal\Core\Entity\EntityInterface|null $_entity
* (optional) An entity, passed in directly from the request attributes.
*
* @return string|null
* The title for the entity edit page, if an entity was found.
*/
public function editTitle(RouteMatchInterface $route_match, ?EntityInterface $_entity = NULL) {
$entity = $this->doGetEntity($route_match, $_entity);
if ($entity) {
return $this->t('Edit %label container', ['%label' => $entity->label()]);
}
}
/**
* Enables a Container object.
*
* @param \Drupal\matomo_tagmanager\Entity\ContainerInterface $matomo_tagmanager_container
* The Container object to enable.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect response to the matomo_tagmanager_container listing page.
*
* @todo The parameter name must match that used in routing.yml although the
* documentation suggests otherwise.
*/
public function enable(ContainerInterface $matomo_tagmanager_container) {
$matomo_tagmanager_container->enable()->save();
return new RedirectResponse($matomo_tagmanager_container->toUrl('collection', ['absolute' => TRUE])->toString());
}
/**
* Disables a Container object.
*
* @param \Drupal\matomo_tagmanager\Entity\ContainerInterface $matomo_tagmanager_container
* The Container object to disable.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect response to the matomo_tagmanager_container listing page.
*/
public function disable(ContainerInterface $matomo_tagmanager_container) {
$matomo_tagmanager_container->disable()->save();
return new RedirectResponse($matomo_tagmanager_container->toUrl('collection', ['absolute' => TRUE])->toString());
}
}
<?php
declare(strict_types = 1);
namespace Drupal\matomo_tagmanager\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Defines the Matomo Tag Manager container configuration entity.
*
* @ConfigEntityType(
* id = "matomo_tagmanager_container",
* label = @Translation("Container"),
* label_singular = @Translation("container"),
* label_plural = @Translation("containers"),
* label_collection = @Translation("Containers"),
* handlers = {
* "storage": "Drupal\matomo_tagmanager\ContainerStorage",
* "list_builder": "Drupal\matomo_tagmanager\ContainerListBuilder",
* "form": {
* "default": "Drupal\matomo_tagmanager\Form\ContainerForm",
* "delete": "Drupal\Core\Entity\EntityDeleteForm"
* }
* },
* admin_permission = "administer matomo tag manager",
* config_prefix = "container",
* entity_keys = {
* "id": "id",
* "label": "label",
* "weight": "weight",
* "status": "status"
* },
* config_export = {
* "id",
* "label",
* "weight",
* "container_id",
* },
* links = {
* "add-form": "/admin/config/system/matomo-tagmanager/add",
* "edit-form": "/admin/config/system/matomo-tagmanager/manage/{matomo_tagmanager_container}",
* "delete-form": "/admin/config/system/matomo-tagmanager/manage/{matomo_tagmanager_container}/delete",
* "enable": "/admin/config/system/matomo-tagmanager/manage/{matomo_tagmanager_container}/enable",
* "disable": "/admin/config/system/matomo-tagmanager/manage/{matomo_tagmanager_container}/disable",
* "collection": "/admin/config/system/matomo-tagmanager",
* }
* )
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class Container extends ConfigEntityBase implements ContainerInterface {
use StringTranslationTrait;
/**
* The machine name for the configuration entity.
*
* @var string
*/
protected $id;
/**
* The human-readable name of the configuration entity.
*
* @var string
*/
public $label;
/**
* The weight of the configuration entity.
*
* @var int
*/
public $weight = 0;
/**
* The Matomo Tag Manager container id.
*
* @var string
*/
public $container_id;
/**
* {@inheritdoc}
*/
public function containerId(): string {
return $this->container_id;
}
/**
* {@inheritdoc}
*/
public function weight(): int {
return $this->weight;
}
/**
* Returns a cleansed variable.
*
* @param string $variable
* The variable name.
*
* @return string
* The cleansed variable.
*/
public function variableClean($variable) {
return \trim(\json_encode($this->get($variable)), '"');
}
}
<?php
declare(strict_types = 1);
namespace Drupal\matomo_tagmanager\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Interface for Matomo Tag Manager container.
*/
interface ContainerInterface extends ConfigEntityInterface {
/**
* Get the ID of the container.
*
* @return string
* The configured container ID.
*/
public function containerId(): string;
/**
* Get the container's weight.
*
* @return int
* The weight of the container.
*/
public function weight(): int;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment