google_tag-8.x-1.x-dev/google_tag.module
google_tag.module
<?php
/**
* @file
* Provides google_tag hook implementations.
*/
declare(strict_types=1);
/**
* @file
* Integration between Drupal and Google Tag.
*/
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface;
use Drupal\commerce_product\Entity\ProductInterface;
use Drupal\commerce_product\ProductVariationStorageInterface;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\search\SearchPageInterface;
/**
* Implements hook_theme().
*/
function google_tag_theme() {
return [
'google_tag_gtm_iframe' => [
'variables' => [
'url' => NULL,
],
],
];
}
/**
* Implements hook_module_implements_alter().
*/
function google_tag_module_implements_alter(&$implementations, $hook) {
if ($hook === 'page_attachments') {
// Ensure this module's implementation of the `page_attachments` hook runs
// last to capture all events.
$group = $implementations['google_tag'];
unset($implementations['google_tag']);
$implementations['google_tag'] = $group;
// Unset google_analytics' hook implementation for page_attachments.
if (isset($implementations['google_analytics'])) {
unset($implementations['google_analytics']);
}
}
}
/**
* Implements hook_page_top().
*/
function google_tag_page_top(array &$page) {
$definition = \Drupal::entityTypeManager()->getDefinition('google_tag_container');
$cacheable_metadata = new CacheableMetadata();
$cacheable_metadata->addCacheTags($definition->getListCacheTags());
/** @var \Drupal\google_tag\Entity\TagContainer|null $config */
$config = \Drupal::service('google_tag.tag_container_resolver')->resolve();
if ($config === NULL || $config->getGtmId() === '') {
$cacheable_metadata->applyTo($page);
return;
}
$cacheable_metadata->addCacheableDependency($config);
$cacheable_metadata->applyTo($page);
$gtm_ids = $config->getGtmIds();
foreach ($gtm_ids as $gtm_id) {
$adv_settings = $config->getGtmSettings($gtm_id);
if ($gtm_id !== '') {
$query_params = [
'id' => $gtm_id,
];
if ($adv_settings['include_environment']) {
// Gather data.
$query_params['gtm_auth'] = $adv_settings['environment_token'];
$query_params['gtm_preview'] = $adv_settings['environment_id'];
$query_params['gtm_cookies_win'] = 'x';
}
$page['google_tag_gtm_iframe'][] = [
'#theme' => 'google_tag_gtm_iframe',
'#url' => Url::fromUri('https://www.googletagmanager.com/ns.html', ['query' => $query_params]),
];
}
}
}
/**
* Implements hook_page_attachments().
*/
function google_tag_page_attachments(array &$attachments) {
$definition = \Drupal::entityTypeManager()->getDefinition('google_tag_container');
$cacheable_metadata = CacheableMetadata::createFromRenderArray($attachments);
$cacheable_metadata->addCacheTags($definition->getListCacheTags());
/** @var \Drupal\google_tag\Entity\TagContainer|null $config */
$config = \Drupal::service('google_tag.tag_container_resolver')->resolve();
if ($config === NULL) {
$cacheable_metadata->applyTo($attachments);
return;
}
$cacheable_metadata->addCacheableDependency($config);
$cacheable_metadata->applyTo($attachments);
// @todo Put this data into their own respective methods?
// GTM JS embed.
if ($config->getGtmIds() !== []) {
$attachments['#attached']['library'][] = 'google_tag/gtm';
$gtm = [
'tagIds' => $config->getGtmIds(),
];
$settings = $config->getGtmSettings();
$gtm['settings'] = $settings;
if (isset($settings['include_classes']) && $settings['include_classes'] === TRUE) {
$gtm['settings']['allowlist_classes'] = explode(PHP_EOL, $settings['allowlist_classes']);
$gtm['settings']['blocklist_classes'] = explode(PHP_EOL, $settings['blocklist_classes']);
}
$attachments['#attached']['drupalSettings']['gtm'] = $gtm;
}
// ^ returns the config which is active and the main tag ID.
// @todo if no config, only send events to datalayer.
$attachments['#attached']['library'][] = 'google_tag/gtag';
$attachments['#attached']['library'][] = 'google_tag/gtag.ajax';
$attachments['#attached']['drupalSettings']['gtag'] = [
'tagId' => $config->getDefaultTagId(),
'otherIds' => $config->getAdditionalIds(),
'consentMode' => $config->getConsentMode(),
'events' => [],
'additionalConfigInfo' => \Drupal::service('google_tag.dimensions_metrics_processor')->getValues($config),
];
$collector = \Drupal::getContainer()->get('google_tag.event_collector');
foreach ($collector->getEvents() as $event) {
$attachments['#attached']['drupalSettings']['gtag']['events'][] = [
'name' => $event->getName(),
'data' => $event->getData(),
];
if ($event instanceof CacheableDependencyInterface) {
$cacheable_metadata->addCacheableDependency($event);
}
}
$cacheable_metadata->applyTo($attachments);
}
/**
* Implements hook_help().
*/
function google_tag_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.google_tag':
case 'google_tag.settings_form':
return t('<a href=":url">Google Tag</a> is a free service (registration required) to manage the insertion of tags for capturing website analytics.', [':url' => 'https://tagmanager.google.com/']);
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function google_tag_form_user_login_form_alter(&$form, FormStateInterface $form_state) {
$form['#submit'][] = '_google_tag_user_login_form_event';
}
/**
* Adds login event to the collector.
*/
function _google_tag_user_login_form_event(&$form, FormStateInterface $form_state) {
\Drupal::service('google_tag.event_collector')->addEvent('login');
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function google_tag_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['actions']['submit']['#submit'][] = '_google_tag_user_register_form_event';
}
/**
* Adds sign up event to the collector.
*/
function _google_tag_user_register_form_event(&$form, FormStateInterface $form_state): void {
\Drupal::service('google_tag.event_collector')->addEvent('sign_up');
}
/**
* Implements hook_form_google_analytics_admin_settings_alter().
*/
function google_tag_form_google_analytics_admin_settings_alter(&$form, FormStateInterface $form_state, $form_id) {
\Drupal::messenger()->addWarning(t('Google Analytics functionality has been rolled into <a href=":url">Google Tag</a>. You should disable this module.', [':url' => Url::fromRoute('entity.google_tag_container.single_form')->toString()]));
$form['actions']['submit']['#disabled'] = TRUE;
$form['actions']['submit']['#value'] = t('Save configuration (disabled)');
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function google_tag_form_commerce_checkout_flow_alter(&$form, FormStateInterface $form_state, $form_id) {
$form_object = $form_state->getFormObject();
assert($form_object instanceof CheckoutFlowInterface);
$step_id = $form['#step_id'];
$collector = \Drupal::getContainer()->get('google_tag.event_collector');
assert($collector !== NULL);
if ($form_object->getPreviousStepId($step_id) === NULL) {
$collector->addEvent('commerce_begin_checkout', [
'order' => $form_object->getOrder(),
]);
}
if ($step_id === 'complete') {
$collector->addEvent('commerce_purchase', [
'order' => $form_object->getOrder(),
]);
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function google_tag_preprocess_item_list__search_results(&$variables) {
$search_page = \Drupal::routeMatch()->getParameter('entity');
if ($search_page instanceof SearchPageInterface) {
$plugin = $search_page->getPlugin();
if ($plugin->isSearchExecutable()) {
\Drupal::service('google_tag.event_collector')->addEvent('search', [
'search_term' => $plugin->getKeywords(),
]);
}
}
}
/**
* Implements hook_ENTITY_TYPE_view().
*/
function google_tag_commerce_product_view(array &$build, ProductInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
// Workaround to prevent sending an event if the add to cart form is
// causing a render, or if a product has no published variations.
if (!\Drupal::request()->isMethodSafe() || !$entity->hasVariations()) {
return;
}
if ($view_mode === 'full') {
$product_variation_storage = \Drupal::entityTypeManager()->getStorage('commerce_product_variation');
assert($product_variation_storage instanceof ProductVariationStorageInterface);
$variation = $product_variation_storage->loadFromContext($entity);
if ($variation !== NULL) {
\Drupal::service('google_tag.event_collector')->addEvent('commerce_view_item', [
'item' => $variation,
]);
}
}
}
