Skip to content

Commit

Permalink
Decouple GTag rendering from the Analytics module.
Browse files Browse the repository at this point in the history
  • Loading branch information
techanvil committed Feb 19, 2024
1 parent 70f7b9c commit 82d9f94
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 77 deletions.
104 changes: 104 additions & 0 deletions includes/Core/Tags/GTag.php
@@ -0,0 +1,104 @@
<?php

namespace Google\Site_Kit\Core\Tags;

use Google\Site_Kit\Core\Util\Method_Proxy_Trait;

class GTag {
use Method_Proxy_Trait;

const HANDLE = 'google_gtagjs';

private $tags = array();
private $commands = array();

public function register() {
add_action( 'wp_enqueue_scripts', $this->get_method_proxy( 'enqueue_gtag_script' ), 20 );

add_filter(
'wp_resource_hints',
function ( $urls, $relation_type ) {
if ( 'dns-prefetch' === $relation_type ) {
$urls[] = '//www.googletagmanager.com';
}

return $urls;
},
10,
2
);
}

public function add_tag( $tag_id, $config = array() ) {
$this->tags[] = array(
'tag_id' => $tag_id,
'config' => $config,
);
}

public function add_command( $command, ...$parameters ) {
$this->commands[] = array(
'command' => $command,
'parameters' => $parameters,
);
}

protected function enqueue_gtag_script() {
// $this->tags and $this->commands will be populated via this action's handlers.
do_action( 'googlesitekit_setup_gtag', $this );

if ( empty( $this->tags ) ) {
return;
}

// Load the GTag scripts using the first tag ID - it doesn't matter which is used, all registered tags will be setup with a
// config command regardless of which is used to load the source.
$gtag_src = 'https://www.googletagmanager.com/gtag/js?id=' . rawurlencode( $this->tags[0]['tag_id'] );

// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_enqueue_script( self::HANDLE, $gtag_src, false, null, false );
wp_script_add_data( self::HANDLE, 'script_execution', 'async' );
wp_add_inline_script( self::HANDLE, 'window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}' );
wp_add_inline_script( self::HANDLE, 'gtag("js", new Date());' );
wp_add_inline_script( self::HANDLE, 'gtag("set", "developer_id.dZTNiMT", true);' ); // Site Kit developer ID.

foreach ( $this->tags as $tag ) {
wp_add_inline_script( self::HANDLE, $this->get_gtag_call_for_tag( $tag ) );
}

foreach ( $this->commands as $command ) {
wp_add_inline_script( self::HANDLE, $this->get_gtag_call_for_command( $command ) );
}

$filter_google_gtagjs = function ( $tag, $handle ) {
if ( self::HANDLE !== $handle ) {
return $tag;
}

$snippet_comment_begin = sprintf( "\n<!-- %s -->\n", esc_html__( 'Google tag (gtag.js)snippet added by Site Kit', 'google-site-kit' ) );
$snippet_comment_end = sprintf( "\n<!-- %s -->\n", esc_html__( 'End Google tag (gtag.js) snippet added by Site Kit', 'google-site-kit' ) );

return $snippet_comment_begin . $tag . $snippet_comment_end;
};

add_filter( 'script_loader_tag', $filter_google_gtagjs, 20, 2 );
}

protected function get_gtag_call_for_tag( $tag ) {
return empty( $tag['config'] )
? sprintf( 'gtag("config", "%s");', esc_js( $tag['tag_id'] ) )
: sprintf( 'gtag("config", "%s", %s);', esc_js( $tag['tag_id'] ), wp_json_encode( $tag['config'] ) );
}

protected function get_gtag_call_for_command( $command ) {
$gtag_args = array_merge( array( $command['command'] ), $command['parameters'] );
$gtag_args = array_map(
function( $arg ) {
return wp_json_encode( $arg );
},
$gtag_args
);

return sprintf( 'gtag(%s);', implode( ',', $gtag_args ) );
}
}
98 changes: 21 additions & 77 deletions includes/Modules/Analytics_4/Web_Tag.php
Expand Up @@ -11,6 +11,7 @@
namespace Google\Site_Kit\Modules\Analytics_4;

use Google\Site_Kit\Core\Modules\Tags\Module_Web_Tag;
use Google\Site_Kit\Core\Tags\GTag;
use Google\Site_Kit\Core\Tags\Tag_With_DNS_Prefetch_Trait;
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;

Expand Down Expand Up @@ -88,13 +89,8 @@ public function set_ads_conversion_id( $ads_conversion_id ) {
* @since 1.31.0
*/
public function register() {
add_action( 'wp_enqueue_scripts', $this->get_method_proxy( 'enqueue_gtag_script' ), 20 );
add_filter(
'wp_resource_hints',
$this->get_dns_prefetch_hints_callback( '//www.googletagmanager.com' ),
10,
2
);
add_action( 'googlesitekit_setup_gtag', $this->get_method_proxy( 'setup_gtag' ) );

$this->do_init_tag_action();
}

Expand All @@ -108,18 +104,15 @@ protected function render() {
}

/**
* Enqueues gtag script.
* Configures gtag script.
*
* @since 1.24.0
* @since n.e.x.t Changed name and refactored.
*
* @param GTag $gtag GTag instance.
*/
protected function enqueue_gtag_script() {
protected function setup_gtag( $gtag ) {
$gtag_opt = $this->get_tag_config();
$gtag_src = 'https://www.googletagmanager.com/gtag/js?id=' . rawurlencode( $this->tag_id );

// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_enqueue_script( 'google_gtagjs', $gtag_src, false, null, false );
wp_script_add_data( 'google_gtagjs', 'script_execution', 'async' );
wp_add_inline_script( 'google_gtagjs', 'window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}' );

/**
* Filters the gtag configuration options for the Analytics snippet.
Expand All @@ -135,81 +128,32 @@ protected function enqueue_gtag_script() {
$gtag_opt = apply_filters( 'googlesitekit_gtag_opt', $gtag_opt );

if ( ! empty( $gtag_opt['linker'] ) ) {
$linker = wp_json_encode( $gtag_opt['linker'] );
$linker = sprintf( "gtag('set', 'linker', %s );", $linker );
wp_add_inline_script( 'google_gtagjs', $linker );
}
$gtag->add_command( 'set', 'linker', $gtag_opt['linker'] );

unset( $gtag_opt['linker'] );

wp_add_inline_script( 'google_gtagjs', 'gtag("js", new Date());' );
wp_add_inline_script( 'google_gtagjs', 'gtag("set", "developer_id.dZTNiMT", true);' ); // Site Kit developer ID.
unset( $gtag_opt['linker'] );
}

$this->add_inline_config( $this->tag_id, $gtag_opt );
$this->add_inline_ads_conversion_id_config();
$gtag->add_tag( $this->tag_id, $gtag_opt );

$block_on_consent_attrs = $this->get_tag_blocked_on_consent_attribute();
// TODO: Lift this out to the Ads module when it's ready.
if ( $this->ads_conversion_id ) {
$gtag->add_tag( $this->ads_conversion_id );
}

$filter_google_gtagjs = function ( $tag, $handle ) use ( $block_on_consent_attrs, $gtag_src ) {
if ( 'google_gtagjs' !== $handle ) {
$filter_google_gtagjs = function ( $tag, $handle ) {
if ( GTag::HANDLE !== $handle ) {
return $tag;
}

$snippet_comment_begin = sprintf( "\n<!-- %s -->\n", esc_html__( 'Google Analytics snippet added by Site Kit', 'google-site-kit' ) );
$snippet_comment_end = sprintf( "\n<!-- %s -->\n", esc_html__( 'End Google Analytics snippet added by Site Kit', 'google-site-kit' ) );

if ( $block_on_consent_attrs ) {
$tag = str_replace(
array(
"<script src='$gtag_src'", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script src=\"$gtag_src\"", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script type='text/javascript' src='$gtag_src'", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script type=\"text/javascript\" src=\"$gtag_src\"", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
),
array( // `type` attribute intentionally excluded in replacements.
"<script{$block_on_consent_attrs} src='$gtag_src'", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script{$block_on_consent_attrs} src=\"$gtag_src\"", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script{$block_on_consent_attrs} src='$gtag_src'", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script{$block_on_consent_attrs} src=\"$gtag_src\"", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
),
$tag
);

}
// Retain this comment for detection of Site Kit placed tag.
$snippet_comment = sprintf( "\n<!-- %s -->\n", esc_html__( 'Google Analytics snippet added by Site Kit', 'google-site-kit' ) );

return $snippet_comment_begin . $tag . $snippet_comment_end;
return $snippet_comment . $tag;
};

add_filter( 'script_loader_tag', $filter_google_gtagjs, 10, 2 );
}

/**
* Adds an inline script to configure ads conversion tracking.
*
* @since 1.32.0
*/
protected function add_inline_ads_conversion_id_config() {
if ( $this->ads_conversion_id ) {
$this->add_inline_config( $this->ads_conversion_id, array() );
}
}

/**
* Adds an inline script to configure provided tag including configuration options.
*
* @since 1.113.0
*
* @param string $tag_id The tag ID to add config for.
* @param array $gtag_opt The gtag configuration.
*/
protected function add_inline_config( $tag_id, $gtag_opt ) {
$config = ! empty( $gtag_opt )
? sprintf( 'gtag("config", "%s", %s);', esc_js( $tag_id ), wp_json_encode( $gtag_opt ) )
: sprintf( 'gtag("config", "%s");', esc_js( $tag_id ) );

wp_add_inline_script( 'google_gtagjs', $config );
}

/**
* Gets the tag config as used in the gtag data vars.
*
Expand Down
1 change: 1 addition & 0 deletions includes/Plugin.php
Expand Up @@ -213,6 +213,7 @@ function() use ( $options, $activation_flag ) {
( new Core\Dashboard_Sharing\Dashboard_Sharing( $this->context, $user_options ) )->register();
( new Core\Key_Metrics\Key_Metrics( $this->context, $user_options, $options ) )->register();
( new Core\Prompts\Prompts( $this->context, $user_options ) )->register();
( new Core\Tags\GTag( $this->context ) )->register();

// If a login is happening (runs after 'init'), update current user in dependency chain.
add_action(
Expand Down

0 comments on commit 82d9f94

Please sign in to comment.