update
This commit is contained in:
1150
wp-content/plugins/google-site-kit/includes/Modules/AdSense.php
Normal file
1150
wp-content/plugins/google-site-kit/includes/Modules/AdSense.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\AMP_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_AMP_Tag;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Class for AMP tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class AMP_Tag extends Module_AMP_Tag {
|
||||
|
||||
use Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Internal flag for whether the AdSense tag has been printed.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @var bool
|
||||
*/
|
||||
private $adsense_tag_printed = false;
|
||||
|
||||
/**
|
||||
* Web Story Ad Slot ID.
|
||||
*
|
||||
* @since 1.27.0
|
||||
* @var string
|
||||
*/
|
||||
private $story_ad_slot_id = '';
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
public function register() {
|
||||
if ( is_singular( 'web-story' ) ) {
|
||||
// If Web Stories are enabled, render the auto ads code.
|
||||
add_action( 'web_stories_print_analytics', $this->get_method_proxy( 'render_story_auto_ads' ) );
|
||||
} else {
|
||||
// For AMP Native and Transitional (if `wp_body_open` supported).
|
||||
add_action( 'wp_body_open', $this->get_method_proxy( 'render' ), -9999 );
|
||||
// For AMP Native and Transitional (as fallback).
|
||||
add_filter( 'the_content', $this->get_method_proxy( 'amp_content_add_auto_ads' ) );
|
||||
// For AMP Reader (if `amp_post_template_body_open` supported).
|
||||
add_action( 'amp_post_template_body_open', $this->get_method_proxy( 'render' ), -9999 );
|
||||
// For AMP Reader (as fallback).
|
||||
add_action( 'amp_post_template_footer', $this->get_method_proxy( 'render' ), -9999 );
|
||||
|
||||
// Load amp-auto-ads component for AMP Reader.
|
||||
$this->enqueue_amp_reader_component_script( 'amp-auto-ads', 'https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js' );
|
||||
}
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attributes for amp-story-auto-ads and amp-auto-ads tags.
|
||||
*
|
||||
* @since 1.39.0
|
||||
*
|
||||
* @param string $type Whether it's for web stories. Can be `web-story` or ``.
|
||||
* @return array Filtered $options.
|
||||
*/
|
||||
private function get_auto_ads_attributes( $type = '' ) {
|
||||
$options = array(
|
||||
'ad-client' => $this->tag_id,
|
||||
);
|
||||
|
||||
if ( 'web-story' === $type && ! empty( $this->story_ad_slot_id ) ) {
|
||||
$options['ad-slot'] = $this->story_ad_slot_id;
|
||||
}
|
||||
|
||||
$filtered_options = 'web-story' === $type
|
||||
? apply_filters( 'googlesitekit_amp_story_auto_ads_attributes', $options, $this->tag_id, $this->story_ad_slot_id )
|
||||
: apply_filters( 'googlesitekit_amp_auto_ads_attributes', $options, $this->tag_id, $this->story_ad_slot_id );
|
||||
|
||||
if ( is_array( $filtered_options ) && ! empty( $filtered_options ) ) {
|
||||
$options = $filtered_options;
|
||||
$options['ad-client'] = $this->tag_id;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the <amp-auto-ads> tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function render() {
|
||||
if ( $this->adsense_tag_printed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->adsense_tag_printed = true;
|
||||
|
||||
$attributes = '';
|
||||
foreach ( $this->get_auto_ads_attributes() as $amp_auto_ads_opt_key => $amp_auto_ads_opt_value ) {
|
||||
$attributes .= sprintf( ' data-%s="%s"', esc_attr( $amp_auto_ads_opt_key ), esc_attr( $amp_auto_ads_opt_value ) );
|
||||
}
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
printf(
|
||||
'<amp-auto-ads type="adsense" %s%s></amp-auto-ads>',
|
||||
$attributes, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$this->get_tag_blocked_on_consent_attribute() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the AMP auto ads tag if opted in.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param string $content The page content.
|
||||
* @return string Filtered $content.
|
||||
*/
|
||||
private function amp_content_add_auto_ads( $content ) {
|
||||
// Only run for the primary application of the `the_content` filter.
|
||||
if ( $this->adsense_tag_printed || ! in_the_loop() ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$this->adsense_tag_printed = true;
|
||||
|
||||
$snippet_comment_begin = sprintf( "\n<!-- %s -->\n", esc_html__( 'Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
$snippet_comment_end = sprintf( "\n<!-- %s -->\n", esc_html__( 'End Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
$tag = sprintf(
|
||||
'<amp-auto-ads type="adsense" data-ad-client="%s"%s></amp-auto-ads>',
|
||||
esc_attr( $this->tag_id ),
|
||||
$this->get_tag_blocked_on_consent_attribute()
|
||||
);
|
||||
|
||||
return $snippet_comment_begin . $tag . $snippet_comment_end . $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Web Story Ad Slot ID
|
||||
*
|
||||
* @since 1.27.0
|
||||
*
|
||||
* @param string $ad_slot_id The Ad Slot ID.
|
||||
*/
|
||||
public function set_story_ad_slot_id( $ad_slot_id ) {
|
||||
$this->story_ad_slot_id = $ad_slot_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the AMP Web Story auto ads code if enabled.
|
||||
*
|
||||
* @since 1.27.0
|
||||
*/
|
||||
private function render_story_auto_ads() {
|
||||
$config = array(
|
||||
'ad-attributes' => array(
|
||||
'type' => 'adsense',
|
||||
),
|
||||
);
|
||||
|
||||
$attributes = array();
|
||||
foreach ( $this->get_auto_ads_attributes( 'web-story' ) as $key => $value ) {
|
||||
$attributes[ 'data-' . $key ] = $value;
|
||||
}
|
||||
|
||||
$config['ad-attributes'] = array_merge( $config['ad-attributes'], $attributes );
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
printf( '<amp-story-auto-ads><script type="application/json">%s</script></amp-story-auto-ads>', wp_json_encode( $config ) );
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google AdSense AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Ad_Blocking_Recovery_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Storage\Setting;
|
||||
|
||||
/**
|
||||
* Class for AdSense Ad blocking recovery Tag.
|
||||
*
|
||||
* @since 1.104.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Ad_Blocking_Recovery_Tag extends Setting {
|
||||
|
||||
const OPTION = 'googlesitekit_adsense_ad_blocking_recovery_tag';
|
||||
|
||||
/**
|
||||
* Gets ad blocking recovery tag.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*
|
||||
* @return array Array with tag and error protection code.
|
||||
*/
|
||||
public function get() {
|
||||
$option = parent::get();
|
||||
|
||||
if ( ! $this->is_valid_tag_object( $option ) ) {
|
||||
return $this->get_default();
|
||||
}
|
||||
|
||||
return $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ad blocking recovery tag.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*
|
||||
* @param array $value Array with tag and error protection code.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function set( $value ) {
|
||||
if ( ! $this->is_valid_tag_object( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::set( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected value type.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*
|
||||
* @return string The type name.
|
||||
*/
|
||||
protected function get_type() {
|
||||
return 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'tag' => '',
|
||||
'error_protection_code' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given value is a valid tag object.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*
|
||||
* @param mixed $tag Tag object.
|
||||
*
|
||||
* @return bool TRUE if valid, otherwise FALSE.
|
||||
*/
|
||||
private function is_valid_tag_object( $tag ) {
|
||||
return is_array( $tag ) && isset( $tag['tag'] ) && isset( $tag['error_protection_code'] ) && is_string( $tag['tag'] ) && is_string( $tag['error_protection_code'] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Ad_Blocking_Recovery_Tag_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard;
|
||||
|
||||
/**
|
||||
* Class for the AdSense Ad Blocking Recovery tag guard.
|
||||
*
|
||||
* @since 1.105.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Ad_Blocking_Recovery_Tag_Guard extends Module_Tag_Guard {
|
||||
|
||||
/**
|
||||
* Determines whether the guarded tag can be activated or not.
|
||||
*
|
||||
* @since 1.105.0
|
||||
*
|
||||
* @return bool TRUE if guarded tag can be activated, otherwise FALSE or an error.
|
||||
*/
|
||||
public function can_activate() {
|
||||
$settings = $this->settings->get();
|
||||
|
||||
return ! empty( $settings['adBlockingRecoverySetupStatus'] ) && $settings['useAdBlockingRecoverySnippet'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Ad_Blocking_Recovery_Web_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Tags\Tag;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_DNS_Prefetch_Trait;
|
||||
|
||||
/**
|
||||
* Class for Ad Blocking Recovery tag.
|
||||
*
|
||||
* @since 1.105.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Ad_Blocking_Recovery_Web_Tag extends Tag {
|
||||
|
||||
use Method_Proxy_Trait;
|
||||
use Tag_With_DNS_Prefetch_Trait;
|
||||
|
||||
/**
|
||||
* Ad_Blocking_Recovery_Tag instance.
|
||||
*
|
||||
* @since 1.105.0
|
||||
* @var Ad_Blocking_Recovery_Tag
|
||||
*/
|
||||
protected $ad_blocking_recovery_tag;
|
||||
|
||||
/**
|
||||
* Use Error Protection Snippet.
|
||||
*
|
||||
* @since 1.105.0
|
||||
* @var bool
|
||||
*/
|
||||
protected $use_error_protection_snippet;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.105.0
|
||||
*
|
||||
* @param Ad_Blocking_Recovery_Tag $ad_blocking_recovery_tag Ad_Blocking_Recovery_Tag instance.
|
||||
* @param bool $use_error_protection_snippet Use Error Protection Snippet.
|
||||
*/
|
||||
public function __construct( Ad_Blocking_Recovery_Tag $ad_blocking_recovery_tag, $use_error_protection_snippet ) {
|
||||
$this->ad_blocking_recovery_tag = $ad_blocking_recovery_tag;
|
||||
$this->use_error_protection_snippet = $use_error_protection_snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.105.0
|
||||
*/
|
||||
public function register() {
|
||||
add_action( 'wp_head', $this->get_method_proxy_once( 'render' ) );
|
||||
|
||||
add_filter(
|
||||
'wp_resource_hints',
|
||||
$this->get_dns_prefetch_hints_callback( '//fundingchoicesmessages.google.com' ),
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the AdSense script tag.
|
||||
*
|
||||
* @since 1.105.0
|
||||
*/
|
||||
protected function render() {
|
||||
$tags = $this->ad_blocking_recovery_tag->get();
|
||||
|
||||
if ( empty( $tags['tag'] ) || empty( $tags['error_protection_code'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google AdSense Ad Blocking Recovery snippet added by Site Kit', 'google-site-kit' ) );
|
||||
echo $tags['tag']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google AdSense Ad Blocking Recovery snippet added by Site Kit', 'google-site-kit' ) );
|
||||
if ( $this->use_error_protection_snippet ) {
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google AdSense Ad Blocking Recovery Error Protection snippet added by Site Kit', 'google-site-kit' ) );
|
||||
echo $tags['error_protection_code']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google AdSense Ad Blocking Recovery Error Protection snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Auto_Ad_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard;
|
||||
|
||||
/**
|
||||
* Tag guard class for the AdSense module that blocks the tag placement if it is disabled for a certain user group.
|
||||
*
|
||||
* @since 1.39.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Auto_Ad_Guard extends Module_Tag_Guard {
|
||||
|
||||
/**
|
||||
* Determines whether the guarded tag can be activated or not.
|
||||
*
|
||||
* @since 1.39.0
|
||||
*
|
||||
* @return bool TRUE if guarded tag can be activated, otherwise FALSE.
|
||||
*/
|
||||
public function can_activate() {
|
||||
$settings = $this->settings->get();
|
||||
if ( ! isset( $settings['autoAdsDisabled'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
( in_array( 'loggedinUsers', $settings['autoAdsDisabled'], true ) && is_user_logged_in() ) ||
|
||||
( in_array( 'contentCreators', $settings['autoAdsDisabled'], true ) && current_user_can( 'edit_posts' ) )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Legacy_Keys_Trait;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Interface;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Trait;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_ViewOnly_Keys_Interface;
|
||||
|
||||
/**
|
||||
* Class for AdSense settings.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Settings extends Module_Settings implements Setting_With_Owned_Keys_Interface, Setting_With_ViewOnly_Keys_Interface {
|
||||
use Setting_With_Legacy_Keys_Trait, Setting_With_Owned_Keys_Trait;
|
||||
|
||||
const OPTION = 'googlesitekit_adsense_settings';
|
||||
|
||||
/**
|
||||
* Various ad blocking recovery setup statuses.
|
||||
*/
|
||||
const AD_BLOCKING_RECOVERY_SETUP_STATUS_TAG_PLACED = 'tag-placed';
|
||||
const AD_BLOCKING_RECOVERY_SETUP_STATUS_SETUP_CONFIRMED = 'setup-confirmed';
|
||||
|
||||
/**
|
||||
* Legacy account statuses to be migrated on-the-fly.
|
||||
*
|
||||
* @since 1.9.0
|
||||
* @var array
|
||||
*/
|
||||
protected $legacy_account_statuses = array(
|
||||
'account-connected' => array(
|
||||
'accountStatus' => 'approved',
|
||||
'siteStatus' => 'added',
|
||||
),
|
||||
'account-connected-nonmatching' => array(
|
||||
'accountStatus' => 'approved',
|
||||
'siteStatus' => 'added',
|
||||
),
|
||||
'account-connected-no-data' => array(
|
||||
'accountStatus' => 'approved',
|
||||
'siteStatus' => 'added',
|
||||
),
|
||||
'account-pending-review' => array(
|
||||
'accountStatus' => 'approved',
|
||||
'siteStatus' => 'none',
|
||||
),
|
||||
'account-required-action' => array(
|
||||
'accountStatus' => 'no-client',
|
||||
),
|
||||
'disapproved-account-afc' => array(
|
||||
'accountStatus' => 'no-client',
|
||||
),
|
||||
'ads-display-pending' => array(
|
||||
'accountStatus' => 'pending',
|
||||
),
|
||||
'disapproved-account' => array(
|
||||
'accountStatus' => 'disapproved',
|
||||
),
|
||||
'no-account' => array(
|
||||
'accountStatus' => 'none',
|
||||
),
|
||||
'no-account-tag-found' => array(
|
||||
'accountStatus' => 'none',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Registers the setting in WordPress.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register() {
|
||||
parent::register();
|
||||
|
||||
$this->register_legacy_keys_migration(
|
||||
array(
|
||||
'account_id' => 'accountID',
|
||||
'accountId' => 'accountID',
|
||||
'account_status' => 'accountStatus',
|
||||
'adsenseTagEnabled' => 'useSnippet',
|
||||
'client_id' => 'clientID',
|
||||
'clientId' => 'clientID',
|
||||
'setup_complete' => 'setupComplete',
|
||||
)
|
||||
);
|
||||
|
||||
$this->register_owned_keys();
|
||||
|
||||
add_filter(
|
||||
'option_' . self::OPTION,
|
||||
function ( $option ) {
|
||||
/**
|
||||
* Filters the AdSense account ID to use.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $account_id Empty by default, will fall back to the option value if not set.
|
||||
*/
|
||||
$account_id = apply_filters( 'googlesitekit_adsense_account_id', '' );
|
||||
|
||||
if ( $account_id ) {
|
||||
$option['accountID'] = $account_id;
|
||||
}
|
||||
|
||||
// Migrate legacy account statuses (now split into account status and site status).
|
||||
if ( ! empty( $option['accountStatus'] ) && isset( $this->legacy_account_statuses[ $option['accountStatus'] ] ) ) {
|
||||
foreach ( $this->legacy_account_statuses[ $option['accountStatus'] ] as $key => $value ) {
|
||||
$option[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Migration of legacy setting.
|
||||
if ( ! empty( $option['setupComplete'] ) ) {
|
||||
$option['accountSetupComplete'] = $option['setupComplete'];
|
||||
$option['siteSetupComplete'] = $option['setupComplete'];
|
||||
}
|
||||
unset( $option['setupComplete'] );
|
||||
|
||||
return $option;
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'pre_update_option_' . self::OPTION,
|
||||
function ( $value, $old_value ) {
|
||||
if ( isset( $old_value['setupCompletedTimestamp'] ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( ! empty( $old_value['accountStatus'] ) && ! empty( $old_value['siteStatus'] ) && 'ready' === $old_value['accountStatus'] && 'ready' === $old_value['siteStatus'] ) {
|
||||
$value['setupCompletedTimestamp'] = strtotime( '-1 month' );
|
||||
} elseif ( ! empty( $value['accountStatus'] ) && ! empty( $value['siteStatus'] ) && 'ready' === $value['accountStatus'] && 'ready' === $value['siteStatus'] ) {
|
||||
$value['setupCompletedTimestamp'] = time();
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for owned settings.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*
|
||||
* @return array An array of keys for owned settings.
|
||||
*/
|
||||
public function get_owned_keys() {
|
||||
return array(
|
||||
'accountID',
|
||||
'clientID',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for view-only settings.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*
|
||||
* @return array An array of keys for view-only settings.
|
||||
*/
|
||||
public function get_view_only_keys() {
|
||||
return array( 'accountID' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @since 1.102.0 Added settings for the Ad Blocking Recovery feature.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'ownerID' => 0,
|
||||
'accountID' => '',
|
||||
'autoAdsDisabled' => array(),
|
||||
'clientID' => '',
|
||||
'accountStatus' => '',
|
||||
'siteStatus' => '',
|
||||
'accountSetupComplete' => false,
|
||||
'siteSetupComplete' => false,
|
||||
'useSnippet' => true,
|
||||
'webStoriesAdUnit' => '',
|
||||
'setupCompletedTimestamp' => null,
|
||||
'useAdBlockingRecoverySnippet' => false,
|
||||
'useAdBlockingRecoveryErrorSnippet' => false,
|
||||
'adBlockingRecoverySetupStatus' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the callback for sanitizing the setting's value before saving.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @return callable|null
|
||||
*/
|
||||
protected function get_sanitize_callback() {
|
||||
return function( $option ) {
|
||||
if ( is_array( $option ) ) {
|
||||
if ( isset( $option['accountSetupComplete'] ) ) {
|
||||
$option['accountSetupComplete'] = (bool) $option['accountSetupComplete'];
|
||||
}
|
||||
if ( isset( $option['siteStatusComplete'] ) ) {
|
||||
$option['siteStatusComplete'] = (bool) $option['siteStatusComplete'];
|
||||
}
|
||||
if ( isset( $option['useSnippet'] ) ) {
|
||||
$option['useSnippet'] = (bool) $option['useSnippet'];
|
||||
}
|
||||
if ( isset( $option['autoAdsDisabled'] ) ) {
|
||||
$option['autoAdsDisabled'] = (array) $option['autoAdsDisabled'];
|
||||
}
|
||||
|
||||
if ( isset( $option['useAdBlockingRecoverySnippet'] ) ) {
|
||||
$option['useAdBlockingRecoverySnippet'] = (bool) $option['useAdBlockingRecoverySnippet'];
|
||||
}
|
||||
if ( isset( $option['useAdBlockingRecoveryErrorSnippet'] ) ) {
|
||||
$option['useAdBlockingRecoveryErrorSnippet'] = (bool) $option['useAdBlockingRecoveryErrorSnippet'];
|
||||
}
|
||||
if (
|
||||
isset( $option['adBlockingRecoverySetupStatus'] ) &&
|
||||
! in_array(
|
||||
$option['adBlockingRecoverySetupStatus'],
|
||||
array(
|
||||
'',
|
||||
self::AD_BLOCKING_RECOVERY_SETUP_STATUS_TAG_PLACED,
|
||||
self::AD_BLOCKING_RECOVERY_SETUP_STATUS_SETUP_CONFIRMED,
|
||||
),
|
||||
true
|
||||
)
|
||||
) {
|
||||
$option['adBlockingRecoverySetupStatus'] = $this->get()['adBlockingRecoverySetupStatus'];
|
||||
}
|
||||
}
|
||||
return $option;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Tag_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard;
|
||||
|
||||
/**
|
||||
* Class for the AdSense tag guard.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Guard extends Module_Tag_Guard {
|
||||
|
||||
/**
|
||||
* Determines whether the guarded tag can be activated or not.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @since 1.30.0 Update to return FALSE on 404 pages deliberately.
|
||||
* @since 1.105.0 Extract the check for 404 pages to dedicated Guard.
|
||||
*
|
||||
* @return bool|WP_Error TRUE if guarded tag can be activated, otherwise FALSE or an error.
|
||||
*/
|
||||
public function can_activate() {
|
||||
$settings = $this->settings->get();
|
||||
|
||||
// For web stories, the tag must only be rendered if a story-specific ad unit is provided.
|
||||
if ( is_singular( 'web-story' ) && empty( $settings['webStoriesAdUnit'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! empty( $settings['useSnippet'] ) && ! empty( $settings['clientID'] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\AdSense\Tag_Matchers
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules\AdSense
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
use Google\Site_Kit\Core\Tags\Tag_Matchers_Interface;
|
||||
|
||||
/**
|
||||
* Class for Tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Matchers extends Module_Tag_Matchers implements Tag_Matchers_Interface {
|
||||
|
||||
/**
|
||||
* Holds array of regex tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @return array Array of regex matchers.
|
||||
*/
|
||||
public function regex_matchers() {
|
||||
return array(
|
||||
// Detect google_ad_client.
|
||||
"/google_ad_client: ?[\"|'](.*?)[\"|']/",
|
||||
|
||||
// Detect old style auto-ads tags.
|
||||
'/<(?:script|amp-auto-ads) [^>]*data-ad-client="([^"]+)"/',
|
||||
|
||||
// Detect new style auto-ads tags.
|
||||
'/<(?:script|amp-auto-ads)[^>]*src="[^"]*\\?client=(ca-pub-[^"]+)"[^>]*>/',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\AdSense\Web_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\AdSense
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\AdSense;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Web_Tag;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_DNS_Prefetch_Trait;
|
||||
use Google\Site_Kit\Core\Util\BC_Functions;
|
||||
|
||||
/**
|
||||
* Class for Web tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Web_Tag extends Module_Web_Tag {
|
||||
|
||||
use Method_Proxy_Trait, Tag_With_DNS_Prefetch_Trait;
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
public function register() {
|
||||
add_action( 'wp_head', $this->get_method_proxy_once( 'render' ) );
|
||||
|
||||
add_filter(
|
||||
'wp_resource_hints',
|
||||
$this->get_dns_prefetch_hints_callback( '//pagead2.googlesyndication.com' ),
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the AdSense script tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function render() {
|
||||
// If we haven't completed the account connection yet, we still insert the AdSense tag
|
||||
// because it is required for account verification.
|
||||
|
||||
$adsense_script_src = sprintf(
|
||||
'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=%s&host=%s',
|
||||
esc_attr( $this->tag_id ), // Site owner's web property code.
|
||||
'ca-host-pub-2644536267352236' // SiteKit's web property code.
|
||||
);
|
||||
|
||||
$adsense_script_attributes = array(
|
||||
'async' => true,
|
||||
'src' => $adsense_script_src,
|
||||
'crossorigin' => 'anonymous',
|
||||
);
|
||||
|
||||
$adsense_attributes = $this->get_tag_blocked_on_consent_attribute_array();
|
||||
|
||||
$auto_ads_opt = array();
|
||||
|
||||
$auto_ads_opt_filtered = apply_filters( 'googlesitekit_auto_ads_opt', $auto_ads_opt, $this->tag_id );
|
||||
|
||||
if ( is_array( $auto_ads_opt_filtered ) && ! empty( $auto_ads_opt_filtered ) ) {
|
||||
$strip_attributes = array(
|
||||
'google_ad_client' => '',
|
||||
'enable_page_level_ads' => '',
|
||||
);
|
||||
|
||||
$auto_ads_opt_filtered = array_diff_key( $auto_ads_opt_filtered, $strip_attributes );
|
||||
|
||||
$auto_ads_opt_sanitized = array();
|
||||
|
||||
foreach ( $auto_ads_opt_filtered as $key => $value ) {
|
||||
$new_key = 'data-';
|
||||
$new_key .= str_replace( '_', '-', $key );
|
||||
|
||||
$auto_ads_opt_sanitized[ $new_key ] = $value;
|
||||
}
|
||||
|
||||
$adsense_attributes = array_merge( $adsense_attributes, $auto_ads_opt_sanitized );
|
||||
}
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google AdSense snippet added by Site Kit', 'google-site-kit' ) );
|
||||
BC_Functions::wp_print_script_tag( array_merge( $adsense_script_attributes, $adsense_attributes ) );
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google AdSense snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
|
||||
}
|
||||
330
wp-content/plugins/google-site-kit/includes/Modules/Ads.php
Normal file
330
wp-content/plugins/google-site-kit/includes/Modules/Ads.php
Normal file
@@ -0,0 +1,330 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Assets\Asset;
|
||||
use Google\Site_Kit\Core\Assets\Script;
|
||||
use Google\Site_Kit\Core\Assets\Script_Data;
|
||||
use Google\Site_Kit\Core\Modules\Module;
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Debug_Fields;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Deactivation;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Tag;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Tag_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\Site_Health\Debug_Data;
|
||||
use Google\Site_Kit\Modules\Ads\PAX_Config;
|
||||
use Google\Site_Kit\Modules\Ads\Settings;
|
||||
use Google\Site_Kit\Modules\Ads\Has_Tag_Guard;
|
||||
use Google\Site_Kit\Modules\Ads\Tag_Matchers;
|
||||
use Google\Site_Kit\Modules\Ads\Web_Tag;
|
||||
use Google\Site_Kit\Core\Tags\Guards\Tag_Environment_Type_Guard;
|
||||
use Google\Site_Kit\Core\Tags\Guards\Tag_Verify_Guard;
|
||||
use Google\Site_Kit\Core\Util\Feature_Flags;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Util\URL;
|
||||
use Google\Site_Kit\Modules\Ads\AMP_Tag;
|
||||
|
||||
/**
|
||||
* Class representing the Ads module.
|
||||
*
|
||||
* @since 1.121.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Ads extends Module implements Module_With_Assets, Module_With_Debug_Fields, Module_With_Scopes, Module_With_Settings, Module_With_Tag, Module_With_Deactivation {
|
||||
use Module_With_Assets_Trait;
|
||||
use Module_With_Scopes_Trait;
|
||||
use Module_With_Settings_Trait;
|
||||
use Module_With_Tag_Trait;
|
||||
use Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Module slug name.
|
||||
*/
|
||||
const MODULE_SLUG = 'ads';
|
||||
|
||||
const SCOPE = 'https://www.googleapis.com/auth/adwords';
|
||||
const SUPPORT_CONTENT_SCOPE = 'https://www.googleapis.com/auth/supportcontent';
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.121.0
|
||||
*/
|
||||
public function register() {
|
||||
$this->register_scopes_hook();
|
||||
// Ads tag placement logic.
|
||||
add_action( 'template_redirect', array( $this, 'register_tag' ) );
|
||||
|
||||
add_filter( 'googlesitekit_inline_modules_data', $this->get_method_proxy( 'inline_modules_data' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's assets to register.
|
||||
*
|
||||
* @since 1.122.0
|
||||
* @since 1.126.0 Added PAX assets.
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
protected function setup_assets() {
|
||||
$base_url = $this->context->url( 'dist/assets/' );
|
||||
|
||||
$assets = array(
|
||||
new Script(
|
||||
'googlesitekit-modules-ads',
|
||||
array(
|
||||
'src' => $base_url . 'js/googlesitekit-modules-ads.js',
|
||||
'dependencies' => array(
|
||||
'googlesitekit-vendor',
|
||||
'googlesitekit-api',
|
||||
'googlesitekit-data',
|
||||
'googlesitekit-modules',
|
||||
'googlesitekit-datastore-site',
|
||||
'googlesitekit-datastore-user',
|
||||
'googlesitekit-components',
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
if ( Feature_Flags::enabled( 'adsPax' ) ) {
|
||||
$input = $this->context->input();
|
||||
$is_googlesitekit_dashboard = 'googlesitekit-dashboard' === $input->filter( INPUT_GET, 'page' );
|
||||
$is_ads_slug = 'ads' === $input->filter( INPUT_GET, 'slug' );
|
||||
$is_re_auth = $input->filter( INPUT_GET, 'reAuth' );
|
||||
|
||||
$assets[] = new Script_Data(
|
||||
'googlesitekit-ads-pax-config',
|
||||
array(
|
||||
'global' => '_googlesitekitPAXConfig',
|
||||
'data_callback' => function() {
|
||||
if ( ! current_user_can( Permissions::VIEW_AUTHENTICATED_DASHBOARD ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$config = new PAX_Config( $this->context, $this->authentication->token() );
|
||||
|
||||
return $config->get();
|
||||
},
|
||||
)
|
||||
);
|
||||
// Integrator should be included if either Ads module is connected already,
|
||||
// or we are on the Ads module setup screen.
|
||||
if (
|
||||
current_user_can( Permissions::VIEW_AUTHENTICATED_DASHBOARD ) &&
|
||||
(
|
||||
// Integrator should be included if either:
|
||||
// The Ads module is already connected.
|
||||
$this->is_connected() ||
|
||||
// Or the user is on the Ads module setup screen.
|
||||
is_admin() && $is_googlesitekit_dashboard && $is_ads_slug && $is_re_auth
|
||||
)
|
||||
) {
|
||||
$assets[] = new Script(
|
||||
'googlesitekit-ads-pax-integrator',
|
||||
array(
|
||||
// When updating, mirror the fixed version for google-pax-sdk in package.json.
|
||||
'src' => 'https://www.gstatic.com/pax/1.0.8/pax_integrator.js',
|
||||
'execution' => 'async',
|
||||
'dependencies' => array(
|
||||
'googlesitekit-ads-pax-config',
|
||||
'googlesitekit-modules-data',
|
||||
),
|
||||
'version' => null,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates module data to pass to JS via _googlesitekitModulesData.
|
||||
*
|
||||
* @since 1.126.0
|
||||
*
|
||||
* @param array $modules_data Inline modules data.
|
||||
* @return array Inline modules data.
|
||||
*/
|
||||
private function inline_modules_data( $modules_data ) {
|
||||
if ( $this->is_connected() && Feature_Flags::enabled( 'adsPax' ) ) {
|
||||
// Add the data under the `ads` key to make it clear it's scoped to this module.
|
||||
$modules_data['ads'] = array(
|
||||
'supportedConversionEvents' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
return $modules_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets required Google OAuth scopes for the module.
|
||||
*
|
||||
* @since 1.126.0
|
||||
*
|
||||
* @return array List of Google OAuth scopes.
|
||||
*/
|
||||
public function get_scopes() {
|
||||
if ( Feature_Flags::enabled( 'adsPax' ) ) {
|
||||
$granted_scopes = $this->authentication->get_oauth_client()->get_granted_scopes();
|
||||
$options = $this->get_settings()->get();
|
||||
|
||||
if ( in_array( self::SCOPE, $granted_scopes, true ) || ! empty( $options['extCustomerID'] ) ) {
|
||||
return array( self::SCOPE, self::SUPPORT_CONTENT_SCOPE );
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up information about the module.
|
||||
*
|
||||
* @since 1.121.0
|
||||
*
|
||||
* @return array Associative array of module info.
|
||||
*/
|
||||
protected function setup_info() {
|
||||
return array(
|
||||
'slug' => 'ads',
|
||||
'name' => _x( 'Ads', 'Service name', 'google-site-kit' ),
|
||||
'description' => __( 'Track conversions for your existing Google Ads campaigns', 'google-site-kit' ),
|
||||
'order' => 1,
|
||||
'homepage' => __( 'https://google.com/ads', 'google-site-kit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's settings instance.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*
|
||||
* @return Module_Settings
|
||||
*/
|
||||
protected function setup_settings() {
|
||||
return new Settings( $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module is connected.
|
||||
*
|
||||
* A module being connected means that all steps required as part of its activation are completed.
|
||||
*
|
||||
* @since 1.122.0
|
||||
* @since 1.127.0 Add additional check to account for paxConversionID and extCustomerID as well when feature flag is enabled.
|
||||
*
|
||||
* @return bool True if module is connected, false otherwise.
|
||||
*/
|
||||
public function is_connected() {
|
||||
$options = $this->get_settings()->get();
|
||||
|
||||
if ( Feature_Flags::enabled( 'adsPax' ) ) {
|
||||
if ( empty( $options['conversionID'] ) && empty( $options['paxConversionID'] ) && empty( $options['extCustomerID'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::is_connected();
|
||||
}
|
||||
|
||||
if ( empty( $options['conversionID'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::is_connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up when the module is deactivated.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*/
|
||||
public function on_deactivation() {
|
||||
$this->get_settings()->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Ads tag.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*/
|
||||
public function register_tag() {
|
||||
$ads_conversion_id = $this->get_settings()->get()['conversionID'];
|
||||
$pax_conversion_id = $this->get_settings()->get()['paxConversionID'];
|
||||
|
||||
// The PAX-supplied Conversion ID should take precedence over the
|
||||
// user-supplied one, if both exist.
|
||||
if ( Feature_Flags::enabled( 'adsPax' ) && ! empty( $pax_conversion_id ) ) {
|
||||
$ads_conversion_id = $pax_conversion_id;
|
||||
}
|
||||
|
||||
$tag = $this->context->is_amp()
|
||||
? new AMP_Tag( $ads_conversion_id, self::MODULE_SLUG )
|
||||
: new Web_Tag( $ads_conversion_id, self::MODULE_SLUG );
|
||||
|
||||
if ( $tag->is_tag_blocked() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tag->use_guard( new Tag_Verify_Guard( $this->context->input() ) );
|
||||
$tag->use_guard( new Has_Tag_Guard( $ads_conversion_id ) );
|
||||
$tag->use_guard( new Tag_Environment_Type_Guard() );
|
||||
|
||||
if ( ! $tag->can_register() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$home_domain = URL::parse( $this->context->get_canonical_home_url(), PHP_URL_HOST );
|
||||
$tag->set_home_domain( $home_domain );
|
||||
|
||||
$tag->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of debug field definitions.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return array An array of all debug fields.
|
||||
*/
|
||||
public function get_debug_fields() {
|
||||
$settings = $this->get_settings()->get();
|
||||
|
||||
return array(
|
||||
'ads_conversion_tracking_id' => array(
|
||||
'label' => __( 'Ads Conversion Tracking ID', 'google-site-kit' ),
|
||||
'value' => $settings['conversionID'],
|
||||
'debug' => Debug_Data::redact_debug_value( $settings['conversionID'] ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Module_Tag_Matchers instance.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return Module_Tag_Matchers Module_Tag_Matchers instance.
|
||||
*/
|
||||
public function get_tag_matchers() {
|
||||
return new Tag_Matchers();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads\AMP_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Ads
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Ads;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_AMP_Tag;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_Linker_Interface;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_Linker_Trait;
|
||||
|
||||
/**
|
||||
* Class for AMP tag.
|
||||
*
|
||||
* @since 1.125.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class AMP_Tag extends Module_AMP_Tag implements Tag_With_Linker_Interface {
|
||||
|
||||
use Method_Proxy_Trait, Tag_With_Linker_Trait;
|
||||
|
||||
/**
|
||||
* Sets the current home domain.
|
||||
*
|
||||
* @since 1.125.0
|
||||
*
|
||||
* @param string $domain Domain name.
|
||||
*/
|
||||
public function set_home_domain( $domain ) {
|
||||
$this->home_domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.125.0
|
||||
*/
|
||||
public function register() {
|
||||
$render = $this->get_method_proxy_once( 'render' );
|
||||
|
||||
// Which actions are run depends on the version of the AMP Plugin
|
||||
// (https://amp-wp.org/) available. Version >=1.3 exposes a
|
||||
// new, `amp_print_analytics` action.
|
||||
// For all AMP modes, AMP plugin version >=1.3.
|
||||
add_action( 'amp_print_analytics', $render );
|
||||
// For AMP Standard and Transitional, AMP plugin version <1.3.
|
||||
add_action( 'wp_footer', $render, 20 );
|
||||
// For AMP Reader, AMP plugin version <1.3.
|
||||
add_action( 'amp_post_template_footer', $render, 20 );
|
||||
// For Web Stories plugin.
|
||||
add_action( 'web_stories_print_analytics', $render );
|
||||
|
||||
// Load amp-analytics component for AMP Reader.
|
||||
$this->enqueue_amp_reader_component_script( 'amp-analytics', 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js' );
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs gtag <amp-analytics> tag.
|
||||
*
|
||||
* @since 1.125.0
|
||||
*/
|
||||
protected function render() {
|
||||
$config = $this->get_tag_config();
|
||||
|
||||
$gtag_amp_opt = array(
|
||||
'optoutElementId' => '__gaOptOutExtension',
|
||||
'vars' => array(
|
||||
'gtag_id' => $this->tag_id,
|
||||
'config' => $config,
|
||||
),
|
||||
);
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google Ads AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
printf(
|
||||
'<amp-analytics type="gtag" data-credentials="include"%s><script type="application/json">%s</script></amp-analytics>',
|
||||
$this->get_tag_blocked_on_consent_attribute(), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
wp_json_encode( $gtag_amp_opt )
|
||||
);
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google Ads AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag config as used in the gtag data vars.
|
||||
*
|
||||
* @since 1.125.0
|
||||
*
|
||||
* @return array Tag configuration.
|
||||
*/
|
||||
protected function get_tag_config() {
|
||||
$config = array(
|
||||
$this->tag_id => array(
|
||||
'groups' => 'default',
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_linker_to_tag_config( $config );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads\Has_Tag_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Ads
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Ads;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard;
|
||||
|
||||
/**
|
||||
* Class for the Ads tag guard.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @since 1.128.0 Renamed class to be specific to presence of web tag.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Has_Tag_Guard extends Module_Tag_Guard {
|
||||
/**
|
||||
* Modules tag_id value.
|
||||
*
|
||||
* @since 1.128.0
|
||||
*
|
||||
* @var String
|
||||
*/
|
||||
protected $tag_id;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.128.0
|
||||
*
|
||||
* @param string $tag_id Modules web tag string value.
|
||||
*/
|
||||
public function __construct( $tag_id = '' ) {
|
||||
$this->tag_id = $tag_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the guarded tag can be activated or not.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @since 1.128.0 Updated logic to check modules tag_id value..
|
||||
*
|
||||
* @return bool TRUE if guarded tag can be activated, otherwise FALSE or an error.
|
||||
*/
|
||||
public function can_activate() {
|
||||
return ! empty( $this->tag_id );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads\PAX_Config
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Ads;
|
||||
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Core\Authentication\Token;
|
||||
|
||||
/**
|
||||
* Class representing PAX configuration.
|
||||
*
|
||||
* @since 1.128.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class PAX_Config {
|
||||
|
||||
/**
|
||||
* Context instance.
|
||||
*
|
||||
* @since 1.128.0
|
||||
* @var Context
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* Token instance.
|
||||
*
|
||||
* @since 1.128.0
|
||||
* @var Token
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.128.0
|
||||
*
|
||||
* @param Context $context Context instance.
|
||||
* @param Token $token Token instance.
|
||||
*/
|
||||
public function __construct( Context $context, Token $token ) {
|
||||
$this->context = $context;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration data.
|
||||
*
|
||||
* @since 1.128.0
|
||||
* @return array
|
||||
*/
|
||||
public function get() {
|
||||
$token = $this->token->get();
|
||||
|
||||
return array(
|
||||
'authAccess' => array(
|
||||
'oauthTokenAccess' => array(
|
||||
'token' => $token['access_token'] ?? '',
|
||||
),
|
||||
),
|
||||
'locale' => substr( $this->context->get_locale( 'user' ), 0, 2 ),
|
||||
'debuggingConfig' => array(
|
||||
'env' => $this->get_env(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment configuration.
|
||||
*
|
||||
* @since 1.128.0
|
||||
* @return string
|
||||
*/
|
||||
protected function get_env() {
|
||||
$allowed = array( 'PROD', 'QA_PROD' );
|
||||
|
||||
if ( defined( 'GOOGLESITEKIT_PAX_ENV' ) && in_array( GOOGLESITEKIT_PAX_ENV, $allowed, true ) ) {
|
||||
return GOOGLESITEKIT_PAX_ENV;
|
||||
}
|
||||
|
||||
return 'PROD';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads\Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Ads
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Ads;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Interface;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Trait;
|
||||
|
||||
/**
|
||||
* Class for Ads settings.
|
||||
*
|
||||
* @since 1.122.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Settings extends Module_Settings implements Setting_With_Owned_Keys_Interface {
|
||||
use Setting_With_Owned_Keys_Trait;
|
||||
|
||||
const OPTION = 'googlesitekit_ads_settings';
|
||||
|
||||
/**
|
||||
* Registers the setting in WordPress.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*/
|
||||
public function register() {
|
||||
parent::register();
|
||||
|
||||
$this->register_owned_keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.122.0
|
||||
* @since 1.126.0 Added new settings fields for PAX.
|
||||
*
|
||||
* @return array An array of default settings values.
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'conversionID' => '',
|
||||
'paxConversionID' => '',
|
||||
'extCustomerID' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for owned settings.
|
||||
*
|
||||
* @since 1.122.0
|
||||
* @since 1.126.0 Added new settings fields for PAX.
|
||||
*
|
||||
* @return array An array of keys for owned settings.
|
||||
*/
|
||||
public function get_owned_keys() {
|
||||
return array(
|
||||
'conversionID',
|
||||
'paxConversionID',
|
||||
'extCustomerID',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads\Tag_Matchers
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules\Ads
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Ads;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
use Google\Site_Kit\Core\Tags\Tag_Matchers_Interface;
|
||||
|
||||
/**
|
||||
* Class for Tag matchers.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Matchers extends Module_Tag_Matchers implements Tag_Matchers_Interface {
|
||||
|
||||
/**
|
||||
* Holds array of regex tag matchers.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return array Array of regex matchers.
|
||||
*/
|
||||
public function regex_matchers() {
|
||||
return array(
|
||||
"/gtag\\s*\\(\\s*['|\"]config['|\"]\\s*,\\s*['|\"](AW-[0-9]+)['|\"]\\s*\\)/i",
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Ads\Web_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Ads
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Ads;
|
||||
|
||||
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_Linker_Interface;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Class for Web tag.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Web_Tag extends Module_Web_Tag implements Tag_With_Linker_Interface {
|
||||
|
||||
use Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Sets the current home domain.
|
||||
*
|
||||
* @since 1.125.0
|
||||
*
|
||||
* @param string $domain Domain name.
|
||||
*/
|
||||
public function set_home_domain( $domain ) {
|
||||
$this->home_domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*/
|
||||
public function register() {
|
||||
// Set a lower priority here to let Analytics sets up its tag first.
|
||||
add_action(
|
||||
'googlesitekit_setup_gtag',
|
||||
$this->get_method_proxy( 'setup_gtag' ),
|
||||
20
|
||||
);
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs gtag snippet.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*/
|
||||
protected function render() {
|
||||
// Do nothing, gtag script is enqueued.
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures gtag script.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @param GTag $gtag GTag instance.
|
||||
*/
|
||||
protected function setup_gtag( $gtag ) {
|
||||
$gtag->add_tag( $this->tag_id );
|
||||
|
||||
$filter_google_gtagjs = function ( $tag, $handle ) {
|
||||
if ( GTag::HANDLE !== $handle ) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
// Retain this comment for detection of Site Kit placed tag.
|
||||
$snippet_comment = sprintf( "\n<!-- %s -->\n", esc_html__( 'Google Ads snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
return $snippet_comment . $tag;
|
||||
};
|
||||
|
||||
add_filter( 'script_loader_tag', $filter_google_gtagjs, 10, 2 );
|
||||
}
|
||||
}
|
||||
2393
wp-content/plugins/google-site-kit/includes/Modules/Analytics_4.php
Normal file
2393
wp-content/plugins/google-site-kit/includes/Modules/Analytics_4.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\AMP_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_AMP_Tag;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_Linker_Interface;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_Linker_Trait;
|
||||
|
||||
/**
|
||||
* Class for AMP tag.
|
||||
*
|
||||
* @since 1.104.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class AMP_Tag extends Module_AMP_Tag implements Tag_Interface, Tag_With_Linker_Interface {
|
||||
|
||||
use Method_Proxy_Trait, Tag_With_Linker_Trait;
|
||||
|
||||
/**
|
||||
* Custom dimensions data.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @var array
|
||||
*/
|
||||
private $custom_dimensions;
|
||||
|
||||
/**
|
||||
* Ads conversion ID.
|
||||
*
|
||||
* @since 1.118.0
|
||||
* @var string
|
||||
*/
|
||||
private $ads_conversion_id;
|
||||
|
||||
/**
|
||||
* Sets the current home domain.
|
||||
*
|
||||
* @since 1.118.0
|
||||
*
|
||||
* @param string $domain Domain name.
|
||||
*/
|
||||
public function set_home_domain( $domain ) {
|
||||
$this->home_domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ads conversion ID.
|
||||
*
|
||||
* @since 1.32.0
|
||||
*
|
||||
* @param string $ads_conversion_id Ads ID.
|
||||
*/
|
||||
public function set_ads_conversion_id( $ads_conversion_id ) {
|
||||
$this->ads_conversion_id = $ads_conversion_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom dimensions data.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimensions Custom dimensions data.
|
||||
*/
|
||||
public function set_custom_dimensions( $custom_dimensions ) {
|
||||
$this->custom_dimensions = $custom_dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*/
|
||||
public function register() {
|
||||
$render = $this->get_method_proxy_once( 'render' );
|
||||
|
||||
// Which actions are run depends on the version of the AMP Plugin
|
||||
// (https://amp-wp.org/) available. Version >=1.3 exposes a
|
||||
// new, `amp_print_analytics` action.
|
||||
// For all AMP modes, AMP plugin version >=1.3.
|
||||
add_action( 'amp_print_analytics', $render );
|
||||
// For AMP Standard and Transitional, AMP plugin version <1.3.
|
||||
add_action( 'wp_footer', $render, 20 );
|
||||
// For AMP Reader, AMP plugin version <1.3.
|
||||
add_action( 'amp_post_template_footer', $render, 20 );
|
||||
// For Web Stories plugin.
|
||||
add_action( 'web_stories_print_analytics', $render );
|
||||
|
||||
// Load amp-analytics component for AMP Reader.
|
||||
$this->enqueue_amp_reader_component_script( 'amp-analytics', 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js' );
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs gtag <amp-analytics> tag.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*/
|
||||
protected function render() {
|
||||
$config = $this->get_tag_config();
|
||||
|
||||
if ( ! empty( $this->ads_conversion_id ) ) {
|
||||
$config[ $this->ads_conversion_id ] = array(
|
||||
'groups' => 'default',
|
||||
);
|
||||
}
|
||||
|
||||
$gtag_amp_opt = array(
|
||||
'optoutElementId' => '__gaOptOutExtension',
|
||||
'vars' => array(
|
||||
'gtag_id' => $this->tag_id,
|
||||
'config' => $config,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the gtag configuration options for the amp-analytics tag.
|
||||
*
|
||||
* You can use the {@see 'googlesitekit_gtag_opt'} filter to do the same for gtag in non-AMP.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @see https://developers.google.com/gtagjs/devguide/amp
|
||||
*
|
||||
* @param array $gtag_amp_opt gtag config options for AMP.
|
||||
*/
|
||||
$gtag_amp_opt_filtered = apply_filters( 'googlesitekit_amp_gtag_opt', $gtag_amp_opt );
|
||||
|
||||
// Ensure gtag_id is set to the correct value.
|
||||
if ( ! is_array( $gtag_amp_opt_filtered ) ) {
|
||||
$gtag_amp_opt_filtered = $gtag_amp_opt;
|
||||
}
|
||||
|
||||
if ( ! isset( $gtag_amp_opt_filtered['vars'] ) || ! is_array( $gtag_amp_opt_filtered['vars'] ) ) {
|
||||
$gtag_amp_opt_filtered['vars'] = $gtag_amp_opt['vars'];
|
||||
}
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google Analytics AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
printf(
|
||||
'<amp-analytics type="gtag" data-credentials="include"%s><script type="application/json">%s</script></amp-analytics>',
|
||||
$this->get_tag_blocked_on_consent_attribute(), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
wp_json_encode( $gtag_amp_opt_filtered )
|
||||
);
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google Analytics AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends gtag vars config with the GA4 tag config.
|
||||
*
|
||||
* @since 1.104.0
|
||||
*
|
||||
* @param array $opt AMP gtag config.
|
||||
* @return array
|
||||
*/
|
||||
protected function extend_gtag_opt( $opt ) {
|
||||
$opt['vars']['config'] = array_merge(
|
||||
$opt['vars']['config'],
|
||||
$this->get_tag_config()
|
||||
);
|
||||
// `gtag_id` isn't used in a multi-destination configuration.
|
||||
// See https://developers.google.com/analytics/devguides/collection/amp-analytics/#sending_data_to_multiple_destinations.
|
||||
unset( $opt['vars']['gtag_id'] );
|
||||
|
||||
return $opt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag config as used in the gtag data vars.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @return array Tag configuration.
|
||||
*/
|
||||
protected function get_tag_config() {
|
||||
$config = array(
|
||||
$this->tag_id => array(
|
||||
'groups' => 'default',
|
||||
),
|
||||
);
|
||||
|
||||
if ( ! empty( $this->custom_dimensions ) ) {
|
||||
$config[ $this->tag_id ] = array_merge(
|
||||
$config[ $this->tag_id ],
|
||||
$this->custom_dimensions
|
||||
);
|
||||
}
|
||||
|
||||
return $this->add_linker_to_tag_config( $config );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Account_Ticket
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
/**
|
||||
* Class representing an account ticket for Analytics 4 account provisioning with associated parameters.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Account_Ticket {
|
||||
|
||||
/**
|
||||
* Account ticket ID.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Property name.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @var string
|
||||
*/
|
||||
protected $property_name;
|
||||
|
||||
/**
|
||||
* Data stream name.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @var string
|
||||
*/
|
||||
protected $data_stream_name;
|
||||
|
||||
/**
|
||||
* Timezone.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @var string
|
||||
*/
|
||||
protected $timezone;
|
||||
|
||||
/**
|
||||
* Whether or not enhanced measurement should be enabled.
|
||||
*
|
||||
* @since 1.111.0
|
||||
* @var boolean
|
||||
*/
|
||||
protected $enhanced_measurement_stream_enabled;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param array $data Data to hydrate properties with.
|
||||
*/
|
||||
public function __construct( $data = null ) {
|
||||
if ( ! is_array( $data ) ) {
|
||||
return;
|
||||
}
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( property_exists( $this, $key ) ) {
|
||||
$this->{"set_$key"}( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the account ticket ID.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the account ticket ID.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param string $id Account ticket ID.
|
||||
*/
|
||||
public function set_id( $id ) {
|
||||
$this->id = (string) $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property name.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_property_name() {
|
||||
return $this->property_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property name.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param string $property_name Property name.
|
||||
*/
|
||||
public function set_property_name( $property_name ) {
|
||||
$this->property_name = (string) $property_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data stream name.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_data_stream_name() {
|
||||
return $this->data_stream_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data stream name.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param string $data_stream_name Data stream name.
|
||||
*/
|
||||
public function set_data_stream_name( $data_stream_name ) {
|
||||
$this->data_stream_name = (string) $data_stream_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_timezone() {
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param string $timezone Timezone.
|
||||
*/
|
||||
public function set_timezone( $timezone ) {
|
||||
$this->timezone = (string) $timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enabled state of enhanced measurement for the data stream.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @return bool $enabled Enabled state.
|
||||
*/
|
||||
public function get_enhanced_measurement_stream_enabled() {
|
||||
return $this->enhanced_measurement_stream_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled state of enhanced measurement for the data stream.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @param bool $enabled Enabled state.
|
||||
*/
|
||||
public function set_enhanced_measurement_stream_enabled( $enabled ) {
|
||||
$this->enhanced_measurement_stream_enabled = (bool) $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array representation of the instance values.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array() {
|
||||
return get_object_vars( $this );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Event_List;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Script_Injector;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\AMP_Config_Injector;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Event_List_Registry;
|
||||
use Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
/**
|
||||
* Class for Google Analytics Advanced Event Tracking.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.121.0 Migrated from the Analytics (UA) namespace.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Advanced_Tracking {
|
||||
|
||||
/**
|
||||
* Plugin context.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var Context
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Map of events to be tracked.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var array Map of Event instances, keyed by their unique ID.
|
||||
*/
|
||||
private $events;
|
||||
|
||||
/**
|
||||
* Main class event list registry instance.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var Event_List_Registry
|
||||
*/
|
||||
private $event_list_registry;
|
||||
|
||||
/**
|
||||
* Advanced_Tracking constructor.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param Context $context Plugin context.
|
||||
*/
|
||||
public function __construct( Context $context ) {
|
||||
$this->context = $context;
|
||||
$this->event_list_registry = new Event_List_Registry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.118.0 Renamed hooks to target Analytics 4 module.
|
||||
*/
|
||||
public function register() {
|
||||
$slug_name = Analytics_4::MODULE_SLUG;
|
||||
|
||||
add_action(
|
||||
"googlesitekit_{$slug_name}_init_tag",
|
||||
function() {
|
||||
$this->register_event_lists();
|
||||
add_action(
|
||||
'wp_footer',
|
||||
function() {
|
||||
$this->set_up_advanced_tracking();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
add_action(
|
||||
"googlesitekit_{$slug_name}_init_tag_amp",
|
||||
function() {
|
||||
$this->register_event_lists();
|
||||
add_filter(
|
||||
'googlesitekit_amp_gtag_opt',
|
||||
function( $gtag_amp_opt ) {
|
||||
return $this->set_up_advanced_tracking_amp( $gtag_amp_opt );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of unique events.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @return array Map of Event instances, keyed by their unique ID.
|
||||
*/
|
||||
public function get_events() {
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects javascript to track active events.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*/
|
||||
private function set_up_advanced_tracking() {
|
||||
$this->compile_events();
|
||||
( new Script_Injector( $this->context ) )->inject_event_script( $this->events );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds triggers to AMP configuration.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param array $gtag_amp_opt gtag config options for AMP.
|
||||
* @return array Filtered $gtag_amp_opt.
|
||||
*/
|
||||
private function set_up_advanced_tracking_amp( $gtag_amp_opt ) {
|
||||
$this->compile_events();
|
||||
return ( new AMP_Config_Injector() )->inject_event_configurations( $gtag_amp_opt, $this->events );
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates and registers event lists.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*/
|
||||
private function register_event_lists() {
|
||||
/**
|
||||
* Fires when the Advanced_Tracking class is ready to receive event lists.
|
||||
*
|
||||
* This means that Advanced_Tracking class stores the event lists in the Event_List_Registry instance.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param Event_List_Registry $event_list_registry
|
||||
*/
|
||||
do_action( 'googlesitekit_analytics_register_event_lists', $this->event_list_registry );
|
||||
|
||||
foreach ( $this->event_list_registry->get_lists() as $event_list ) {
|
||||
$event_list->register();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the list of Event objects.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*/
|
||||
private function compile_events() {
|
||||
$this->events = array_reduce(
|
||||
$this->event_list_registry->get_lists(),
|
||||
function ( $events, Event_List $event_list ) {
|
||||
return array_merge( $events, $event_list->get_events() );
|
||||
},
|
||||
array()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\AMP_Config_Injector
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking;
|
||||
|
||||
/**
|
||||
* Class for injecting JavaScript based on the registered event configurations.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class AMP_Config_Injector {
|
||||
|
||||
/**
|
||||
* Creates list of measurement event configurations and javascript to inject.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.121.0 Migrated from the Analytics (UA) namespace.
|
||||
*
|
||||
* @param array $gtag_amp_opt gtag config options for AMP.
|
||||
* @param array $events The map of Event objects, keyed by their unique ID.
|
||||
* @return array Filtered $gtag_amp_opt.
|
||||
*/
|
||||
public function inject_event_configurations( $gtag_amp_opt, $events ) {
|
||||
if ( empty( $events ) ) {
|
||||
return $gtag_amp_opt;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'triggers', $gtag_amp_opt ) ) {
|
||||
$gtag_amp_opt['triggers'] = array();
|
||||
}
|
||||
|
||||
foreach ( $events as $amp_trigger_key => $event ) {
|
||||
$event_config = $event->get_config();
|
||||
|
||||
$amp_trigger = array();
|
||||
if ( 'DOMContentLoaded' === $event_config['on'] ) {
|
||||
$amp_trigger['on'] = 'visible';
|
||||
} else {
|
||||
$amp_trigger['on'] = $event_config['on'];
|
||||
$amp_trigger['selector'] = $event_config['selector'];
|
||||
}
|
||||
|
||||
$amp_trigger['vars'] = array();
|
||||
$amp_trigger['vars']['event_name'] = $event_config['action'];
|
||||
if ( is_array( $event_config['metadata'] ) ) {
|
||||
foreach ( $event_config['metadata'] as $key => $value ) {
|
||||
$amp_trigger['vars'][ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$gtag_amp_opt['triggers'][ $amp_trigger_key ] = $amp_trigger;
|
||||
}
|
||||
|
||||
return $gtag_amp_opt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Event
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class for representing a single tracking event that Advanced_Tracking tracks.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.121.0 Migrated from the Analytics (UA) namespace.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Event implements \JsonSerializable {
|
||||
|
||||
/**
|
||||
* The measurement event's configuration.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var array
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Event constructor.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param array $config {
|
||||
* The event's configuration.
|
||||
*
|
||||
* @type string $action Required. The event action / event name to send.
|
||||
* @type string $on Required. The DOM event to send the event for.
|
||||
* @type string $selector Required, unless $on is 'DOMContentLoaded'. The DOM selector on which to listen
|
||||
* to the $on event.
|
||||
* @type array|null $metadata Optional. Associative array of event metadata to send, such as 'event_category',
|
||||
* 'event_label' etc, or null to not send any extra event data.
|
||||
* }
|
||||
* @throws Exception Thrown when config param is undefined.
|
||||
*/
|
||||
public function __construct( $config ) {
|
||||
$this->config = $this->validate_config( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative event containing the event attributes.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @return array The configuration in JSON-serializable format.
|
||||
*/
|
||||
public function jsonSerialize() {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the measurement event configuration.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @return array The config.
|
||||
*/
|
||||
public function get_config() {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the configuration keys and value types.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param array $config The event's configuration.
|
||||
* @return array The event's configuration.
|
||||
* @throws Exception Thrown when invalid keys or value type.
|
||||
*/
|
||||
private function validate_config( $config ) {
|
||||
$valid_keys = array(
|
||||
'action',
|
||||
'selector',
|
||||
'on',
|
||||
'metadata',
|
||||
);
|
||||
foreach ( $config as $key => $value ) {
|
||||
if ( ! in_array( $key, $valid_keys, true ) ) {
|
||||
throw new Exception( 'Invalid configuration parameter: ' . $key );
|
||||
}
|
||||
}
|
||||
if ( ! array_key_exists( 'metadata', $config ) ) {
|
||||
$config['metadata'] = null;
|
||||
}
|
||||
if ( array_key_exists( 'on', $config ) && 'DOMContentLoaded' === $config['on'] ) {
|
||||
$config['selector'] = '';
|
||||
}
|
||||
foreach ( $valid_keys as $key ) {
|
||||
if ( ! array_key_exists( $key, $config ) ) {
|
||||
throw new Exception( 'Missed configuration parameter: ' . $key );
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Event_List
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking;
|
||||
|
||||
/**
|
||||
* Base class representing a tracking event list.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.121.0 Migrated from the Analytics (UA) namespace.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
abstract class Event_List {
|
||||
|
||||
/**
|
||||
* Container for events.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var array Map of events for this list, keyed by their unique ID.
|
||||
*/
|
||||
private $events = array();
|
||||
|
||||
/**
|
||||
* Adds events or registers WordPress hook callbacks to add events.
|
||||
*
|
||||
* Children classes should extend this to add their events, either generically or by dynamically collecting
|
||||
* metadata through WordPress hooks.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*/
|
||||
abstract public function register();
|
||||
|
||||
/**
|
||||
* Adds a measurement event to the measurement events array.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param Event $event The measurement event object.
|
||||
*/
|
||||
protected function add_event( Event $event ) {
|
||||
$hash = md5( wp_json_encode( $event ) );
|
||||
$this->events[ $hash ] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the measurement events array.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @return array The map of events for this list, keyed by their unique ID.
|
||||
*/
|
||||
public function get_events() {
|
||||
return $this->events;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Event_List_Registry
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking;
|
||||
|
||||
/**
|
||||
* Class for registering third party event lists.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.121.0 Migrated from the Analytics (UA) namespace.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Event_List_Registry {
|
||||
|
||||
/**
|
||||
* The list of registered event lists.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var Event_List[]
|
||||
*/
|
||||
private $event_lists = array();
|
||||
|
||||
/**
|
||||
* Registers an event list.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param Event_List $event_list The event list to be registered.
|
||||
*/
|
||||
public function register_list( Event_List $event_list ) {
|
||||
$this->event_lists[] = $event_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of registered event lists.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @return Event_List[] The list of registered event lists.
|
||||
*/
|
||||
public function get_lists() {
|
||||
return $this->event_lists;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking\Script_Injector
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking;
|
||||
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Core\Assets\Manifest;
|
||||
use Google\Site_Kit\Core\Util\BC_Functions;
|
||||
|
||||
/**
|
||||
* Class for injecting JavaScript based on the registered event configurations.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @since 1.121.0 Migrated from the Analytics (UA) namespace.
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Script_Injector {
|
||||
|
||||
/**
|
||||
* Plugin context.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
* @var Context
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param Context $context Plugin context.
|
||||
*/
|
||||
public function __construct( Context $context ) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates list of measurement event configurations and javascript to inject.
|
||||
*
|
||||
* @since 1.18.0.
|
||||
*
|
||||
* @param array $events The map of Event objects, keyed by their unique ID.
|
||||
*/
|
||||
public function inject_event_script( $events ) {
|
||||
if ( empty( $events ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
list( $filename ) = Manifest::get( 'analytics-advanced-tracking' );
|
||||
if ( ! $filename ) {
|
||||
// Get file contents of script and add it to the page, injecting event configurations into it.
|
||||
$filename = 'analytics-advanced-tracking.js';
|
||||
|
||||
}
|
||||
|
||||
$script_path = $this->context->path( "dist/assets/js/{$filename}" );
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions, WordPressVIPMinimum.Performance.FetchingRemoteData
|
||||
$script_content = file_get_contents( $script_path );
|
||||
if ( ! $script_content ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data_var = sprintf(
|
||||
'var _googlesitekitAnalyticsTrackingData = %s;',
|
||||
wp_json_encode( array_values( $events ) )
|
||||
);
|
||||
BC_Functions::wp_print_inline_script_tag( $data_var . "\n" . $script_content );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Audience_Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Storage\User_Setting;
|
||||
use Google\Site_Kit\Core\Util\Sanitize;
|
||||
|
||||
/**
|
||||
* Class for Analytics 4 audience settings.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Audience_Settings extends User_Setting {
|
||||
|
||||
/**
|
||||
* The user option name for audience setting.
|
||||
*/
|
||||
const OPTION = 'googlesitekit_audience_settings';
|
||||
|
||||
/**
|
||||
* Gets the expected value type.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return string The type name.
|
||||
*/
|
||||
protected function get_type() {
|
||||
return 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return array The default value.
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'configuredAudiences' => null,
|
||||
'isAudienceSegmentationWidgetHidden' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges an array of settings to update.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @param array $partial Partial settings array to save.
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function merge( array $partial ) {
|
||||
$settings = $this->get();
|
||||
$partial = array_filter(
|
||||
$partial,
|
||||
function ( $value ) {
|
||||
return null !== $value;
|
||||
}
|
||||
);
|
||||
|
||||
$allowed_settings = array(
|
||||
'configuredAudiences' => true,
|
||||
'isAudienceSegmentationWidgetHidden' => true,
|
||||
);
|
||||
|
||||
$updated = array_intersect_key( $partial, $allowed_settings );
|
||||
|
||||
return $this->set( array_merge( $settings, $updated ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the callback for sanitizing the setting's value before saving.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return callable Sanitize callback.
|
||||
*/
|
||||
protected function get_sanitize_callback() {
|
||||
return function ( $settings ) {
|
||||
if ( ! is_array( $settings ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$sanitized_settings = array();
|
||||
|
||||
if ( isset( $settings['configuredAudiences'] ) ) {
|
||||
$sanitized_settings['configuredAudiences'] = Sanitize::sanitize_string_list( $settings['configuredAudiences'] );
|
||||
}
|
||||
|
||||
if ( isset( $settings['isAudienceSegmentationWidgetHidden'] ) ) {
|
||||
$sanitized_settings['isAudienceSegmentationWidgetHidden'] = false !== $settings['isAudienceSegmentationWidgetHidden'];
|
||||
}
|
||||
|
||||
return $sanitized_settings;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Custom_Dimensions_Data_Available
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Storage\Transients;
|
||||
|
||||
/**
|
||||
* Class for updating Analytics 4 custom dimension data availability state.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Custom_Dimensions_Data_Available {
|
||||
|
||||
/**
|
||||
* List of valid custom dimension slugs.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @var array
|
||||
*/
|
||||
const CUSTOM_DIMENSION_SLUGS = array(
|
||||
'googlesitekit_post_date',
|
||||
'googlesitekit_post_author',
|
||||
'googlesitekit_post_categories',
|
||||
'googlesitekit_post_type',
|
||||
);
|
||||
|
||||
/**
|
||||
* Transients instance.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @var Transients
|
||||
*/
|
||||
protected $transients;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param Transients $transients Transients instance.
|
||||
*/
|
||||
public function __construct( Transients $transients ) {
|
||||
$this->transients = $transients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data available transient name for the custom dimension.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimension Custom dimension slug.
|
||||
* @return string Data available transient name.
|
||||
*/
|
||||
protected function get_data_available_transient_name( $custom_dimension ) {
|
||||
return "googlesitekit_custom_dimension_{$custom_dimension}_data_available";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data availability for all custom dimensions.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @return array Associative array of custom dimension names and their data availability state.
|
||||
*/
|
||||
public function get_data_availability() {
|
||||
return array_reduce(
|
||||
self::CUSTOM_DIMENSION_SLUGS,
|
||||
function ( $data_availability, $custom_dimension ) {
|
||||
$data_availability[ $custom_dimension ] = $this->is_data_available( $custom_dimension );
|
||||
return $data_availability;
|
||||
},
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data is available for the custom dimension.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimension Custom dimension slug.
|
||||
* @return bool True if data is available, false otherwise.
|
||||
*/
|
||||
protected function is_data_available( $custom_dimension ) {
|
||||
return (bool) $this->transients->get( $this->get_data_available_transient_name( $custom_dimension ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data available state for the custom dimension.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimension Custom dimension slug.
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function set_data_available( $custom_dimension ) {
|
||||
return $this->transients->set( $this->get_data_available_transient_name( $custom_dimension ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the data available state for all custom dimensions.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @since 1.114.0 Added optional $custom_dimensions parameter.
|
||||
*
|
||||
* @param array $custom_dimensions Optional. List of custom dimension slugs to reset.
|
||||
*/
|
||||
public function reset_data_available(
|
||||
$custom_dimensions = self::CUSTOM_DIMENSION_SLUGS
|
||||
) {
|
||||
foreach ( $custom_dimensions as $custom_dimension ) {
|
||||
$this->transients->delete( $this->get_data_available_transient_name( $custom_dimension ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the custom dimension is valid.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimension Custom dimension slug.
|
||||
* @return bool True if valid, false otherwise.
|
||||
*/
|
||||
public function is_valid_custom_dimension( $custom_dimension ) {
|
||||
return in_array( $custom_dimension, self::CUSTOM_DIMENSION_SLUGS, true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Class AccountProvisioningService
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin;
|
||||
use Google\Site_Kit_Dependencies\Google_Client;
|
||||
|
||||
/**
|
||||
* Class for Analytics account provisioning service of the GoogleAnalytics Admin API.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class AccountProvisioningService extends GoogleAnalyticsAdmin {
|
||||
|
||||
/**
|
||||
* Accounts resource instance.
|
||||
*
|
||||
* @var AccountsResource
|
||||
*/
|
||||
public $accounts;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param Google_Client $client The client used to deliver requests.
|
||||
* @param string $rootUrl The root URL used for requests to the service.
|
||||
*/
|
||||
public function __construct( Google_Client $client, $rootUrl = null ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
parent::__construct( $client, $rootUrl ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
|
||||
$this->accounts = new AccountsResource(
|
||||
$this,
|
||||
$this->serviceName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
'accounts',
|
||||
array(
|
||||
'methods' => array(
|
||||
'provisionAccountTicket' => array(
|
||||
'path' => 'v1beta/accounts:provisionAccountTicket',
|
||||
'httpMethod' => 'POST',
|
||||
'parameters' => array(),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Class AccountsResource
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaProvisionAccountTicketResponse;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\Resource;
|
||||
|
||||
/**
|
||||
* Class for representing the Accounts resource of the GoogleAnalytics Admin API for provisioning.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class AccountsResource extends Resource {
|
||||
|
||||
/**
|
||||
* Requests a ticket for creating an account.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param Proxy_GoogleAnalyticsAdminProvisionAccountTicketRequest $post_body The post body to send.
|
||||
* @param array $opt_params Optional parameters.
|
||||
* @return GoogleAnalyticsAdminV1betaProvisionAccountTicketResponse
|
||||
*/
|
||||
public function provisionAccountTicket( Proxy_GoogleAnalyticsAdminProvisionAccountTicketRequest $post_body, $opt_params = array() ) {
|
||||
$params = array( 'postBody' => $post_body );
|
||||
$params = array_merge( $params, $opt_params );
|
||||
|
||||
return $this->call(
|
||||
'provisionAccountTicket',
|
||||
array( $params ),
|
||||
GoogleAnalyticsAdminV1betaProvisionAccountTicketResponse::class
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// phpcs:ignoreFile
|
||||
|
||||
// Suppress coding standards checks for this file.
|
||||
// Reason: This file is a copy of the `GoogleAnalyticsAdminV1alphaEnhancedMeasurementSettings` class
|
||||
// from the Google API PHP Client library with a slight modification.
|
||||
|
||||
/**
|
||||
* Class EnhancedMeasurementSettingsModel
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
/**
|
||||
* The EnhancedMeasurementSettingsModel class.
|
||||
*/
|
||||
class EnhancedMeasurementSettingsModel extends \Google\Site_Kit_Dependencies\Google\Model {
|
||||
public $fileDownloadsEnabled;
|
||||
public $name;
|
||||
public $outboundClicksEnabled;
|
||||
public $pageChangesEnabled;
|
||||
public $scrollsEnabled;
|
||||
public $searchQueryParameter;
|
||||
public $siteSearchEnabled;
|
||||
public $streamEnabled;
|
||||
public $uriQueryParameter;
|
||||
public $videoEngagementEnabled;
|
||||
public function setFileDownloadsEnabled( $fileDownloadsEnabled ) {
|
||||
$this->fileDownloadsEnabled = $fileDownloadsEnabled;
|
||||
}
|
||||
public function getFileDownloadsEnabled() {
|
||||
return $this->fileDownloadsEnabled;
|
||||
}
|
||||
public function setName( $name ) {
|
||||
$this->name = $name;
|
||||
}
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
public function setOutboundClicksEnabled( $outboundClicksEnabled ) {
|
||||
$this->outboundClicksEnabled = $outboundClicksEnabled;
|
||||
}
|
||||
public function getOutboundClicksEnabled() {
|
||||
return $this->outboundClicksEnabled;
|
||||
}
|
||||
public function setPageChangesEnabled( $pageChangesEnabled ) {
|
||||
$this->pageChangesEnabled = $pageChangesEnabled;
|
||||
}
|
||||
public function getPageChangesEnabled() {
|
||||
return $this->pageChangesEnabled;
|
||||
}
|
||||
public function setScrollsEnabled( $scrollsEnabled ) {
|
||||
$this->scrollsEnabled = $scrollsEnabled;
|
||||
}
|
||||
public function getScrollsEnabled() {
|
||||
return $this->scrollsEnabled;
|
||||
}
|
||||
public function setSearchQueryParameter( $searchQueryParameter ) {
|
||||
$this->searchQueryParameter = $searchQueryParameter;
|
||||
}
|
||||
public function getSearchQueryParameter() {
|
||||
return $this->searchQueryParameter;
|
||||
}
|
||||
public function setSiteSearchEnabled( $siteSearchEnabled ) {
|
||||
$this->siteSearchEnabled = $siteSearchEnabled;
|
||||
}
|
||||
public function getSiteSearchEnabled() {
|
||||
return $this->siteSearchEnabled;
|
||||
}
|
||||
public function setStreamEnabled( $streamEnabled ) {
|
||||
$this->streamEnabled = $streamEnabled;
|
||||
}
|
||||
public function getStreamEnabled() {
|
||||
return $this->streamEnabled;
|
||||
}
|
||||
public function setUriQueryParameter( $uriQueryParameter ) {
|
||||
$this->uriQueryParameter = $uriQueryParameter;
|
||||
}
|
||||
public function getUriQueryParameter() {
|
||||
return $this->uriQueryParameter;
|
||||
}
|
||||
public function setVideoEngagementEnabled( $videoEngagementEnabled ) {
|
||||
$this->videoEngagementEnabled = $videoEngagementEnabled;
|
||||
}
|
||||
public function getVideoEngagementEnabled() {
|
||||
return $this->videoEngagementEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Class PropertiesAdSenseLinksService
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin;
|
||||
use Google\Site_Kit_Dependencies\Google_Client;
|
||||
use Google\Site_Kit_Dependencies\Google_Service_GoogleAnalyticsAdmin_PropertiesAdSenseLinks_Resource as PropertiesAdSenseLinksResource;
|
||||
|
||||
/**
|
||||
* Class for managing GA4 AdSense Links.
|
||||
*
|
||||
* @since 1.119.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class PropertiesAdSenseLinksService extends GoogleAnalyticsAdmin {
|
||||
|
||||
/**
|
||||
* PropertiesAdSenseLinksResource instance.
|
||||
*
|
||||
* @var PropertiesAdSenseLinksResource
|
||||
*/
|
||||
public $properties_adSenseLinks; // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @param Google_Client $client The client used to deliver requests.
|
||||
* @param string $rootUrl The root URL used for requests to the service.
|
||||
*/
|
||||
public function __construct( Google_Client $client, $rootUrl = null ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
parent::__construct( $client, $rootUrl ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
$this->version = 'v1alpha';
|
||||
|
||||
// phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
$this->properties_adSenseLinks = new PropertiesAdSenseLinksResource(
|
||||
$this,
|
||||
$this->serviceName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
'adSenseLinks',
|
||||
array(
|
||||
'methods' => array(
|
||||
'create' => array(
|
||||
'path' => 'v1alpha/{+parent}/adSenseLinks',
|
||||
'httpMethod' => 'POST',
|
||||
'parameters' => array(
|
||||
'parent' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'delete' => array(
|
||||
'path' => 'v1alpha/{+name}',
|
||||
'httpMethod' => 'DELETE',
|
||||
'parameters' => array(
|
||||
'name' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'get' => array(
|
||||
'path' => 'v1alpha/{+name}',
|
||||
'httpMethod' => 'GET',
|
||||
'parameters' => array(
|
||||
'name' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'list' => array(
|
||||
'path' => 'v1alpha/{+parent}/adSenseLinks',
|
||||
'httpMethod' => 'GET',
|
||||
'parameters' => array(
|
||||
'parent' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
'pageSize' => array(
|
||||
'location' => 'query',
|
||||
'type' => 'integer',
|
||||
),
|
||||
'pageToken' => array(
|
||||
'location' => 'query',
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Class PropertiesAudiencesService
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\Resource\PropertiesAudiences as PropertiesAudiences;
|
||||
use Google\Site_Kit_Dependencies\Google_Client;
|
||||
|
||||
/**
|
||||
* Class for managing GA4 audiences.
|
||||
*
|
||||
* @since 1.120.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class PropertiesAudiencesService extends GoogleAnalyticsAdmin {
|
||||
|
||||
/**
|
||||
* PropertiesAudiences instance.
|
||||
*
|
||||
* @var PropertiesAudiences
|
||||
*/
|
||||
public $properties_audiences;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.120.0
|
||||
*
|
||||
* @param Google_Client $client The client used to deliver requests.
|
||||
* @param string $rootUrl The root URL used for requests to the service.
|
||||
*/
|
||||
public function __construct( Google_Client $client, $rootUrl = null ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
parent::__construct( $client, $rootUrl ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
$this->version = 'v1alpha';
|
||||
|
||||
$this->properties_audiences = new PropertiesAudiences(
|
||||
$this,
|
||||
$this->serviceName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
'audiences',
|
||||
array(
|
||||
'methods' => array(
|
||||
'create' => array(
|
||||
'path' => 'v1alpha/{+parent}/audiences',
|
||||
'httpMethod' => 'POST',
|
||||
'parameters' => array(
|
||||
'parent' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'list' => array(
|
||||
'path' => 'v1alpha/{+parent}/audiences',
|
||||
'httpMethod' => 'GET',
|
||||
'parameters' => array(
|
||||
'parent' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
'pageSize' => array(
|
||||
'location' => 'query',
|
||||
'type' => 'integer',
|
||||
),
|
||||
'pageToken' => array(
|
||||
'location' => 'query',
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Class PropertiesEnhancedMeasurementResource
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin\EnhancedMeasurementSettingsModel;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\Resource;
|
||||
|
||||
/**
|
||||
* The "enhancedMeasurementSettings" collection of methods.
|
||||
*/
|
||||
class PropertiesEnhancedMeasurementResource extends Resource {
|
||||
|
||||
/**
|
||||
* Returns the singleton enhanced measurement settings for this web stream. Note
|
||||
* that the stream must enable enhanced measurement for these settings to take
|
||||
* effect. (webDataStreams.getEnhancedMeasurementSettings)
|
||||
*
|
||||
* @since 1.110.0
|
||||
*
|
||||
* @param string $name Required. The name of the settings to lookup. Format: properties/{property_id}/webDataStreams/{stream_id}/enhancedMeasurementSettings
|
||||
* Example: "properties/1000/webDataStreams/2000/enhancedMeasurementSettings".
|
||||
* @param array $opt_params Optional parameters.
|
||||
* @return EnhancedMeasurementSettingsModel
|
||||
*/
|
||||
public function getEnhancedMeasurementSettings( $name, $opt_params = array() ) {
|
||||
$params = array( 'name' => $name );
|
||||
$params = array_merge( $params, $opt_params );
|
||||
return $this->call( 'getEnhancedMeasurementSettings', array( $params ), EnhancedMeasurementSettingsModel::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the singleton enhanced measurement settings for this web stream. Note
|
||||
* that the stream must enable enhanced measurement for these settings to take
|
||||
* effect. (webDataStreams.updateEnhancedMeasurementSettings)
|
||||
*
|
||||
* @param string $name Output only. Resource name of this Data Stream. Format: properties/{property_id}/webDataStreams/{stream_id}/enhancedMeasurementSettings
|
||||
* Example: "properties/1000/webDataStreams/2000/enhancedMeasurementSettings".
|
||||
* @param EnhancedMeasurementSettingsModel $post_body The body of the request.
|
||||
* @param array $opt_params Optional parameters.
|
||||
*
|
||||
* @opt_param string updateMask Required. The list of fields to be updated.
|
||||
* Field names must be in snake case (e.g., "field_to_update"). Omitted fields
|
||||
* will not be updated. To replace the entire entity, use one path with the
|
||||
* string "*" to match all fields.
|
||||
* @return EnhancedMeasurementSettingsModel
|
||||
*/
|
||||
public function updateEnhancedMeasurementSettings( $name, EnhancedMeasurementSettingsModel $post_body, $opt_params = array() ) {
|
||||
$params = array(
|
||||
'name' => $name,
|
||||
'postBody' => $post_body,
|
||||
);
|
||||
$params = array_merge( $params, $opt_params );
|
||||
return $this->call( 'updateEnhancedMeasurementSettings', array( $params ), EnhancedMeasurementSettingsModel::class );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Class PropertiesEnhancedMeasurementService
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin;
|
||||
use Google\Site_Kit_Dependencies\Google_Client;
|
||||
|
||||
/**
|
||||
* Class for managing GA4 datastream enhanced measurement settings.
|
||||
*
|
||||
* @since 1.110.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class PropertiesEnhancedMeasurementService extends GoogleAnalyticsAdmin {
|
||||
|
||||
/**
|
||||
* PropertiesEnhancedMeasurementResource instance.
|
||||
*
|
||||
* @var PropertiesEnhancedMeasurementResource
|
||||
*/
|
||||
public $properties_enhancedMeasurements; // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.110.0
|
||||
*
|
||||
* @param Google_Client $client The client used to deliver requests.
|
||||
* @param string $rootUrl The root URL used for requests to the service.
|
||||
*/
|
||||
public function __construct( Google_Client $client, $rootUrl = null ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
parent::__construct( $client, $rootUrl ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
$this->version = 'v1alpha';
|
||||
|
||||
// phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
$this->properties_enhancedMeasurements = new PropertiesEnhancedMeasurementResource(
|
||||
$this,
|
||||
$this->serviceName, // phpcs:ignore WordPress.NamingConventions.ValidVariableName
|
||||
'enhancedMeasurements',
|
||||
array(
|
||||
'methods' => array(
|
||||
'getEnhancedMeasurementSettings' => array(
|
||||
'path' => 'v1alpha/{+name}',
|
||||
'httpMethod' => 'GET',
|
||||
'parameters' => array(
|
||||
'name' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'updateEnhancedMeasurementSettings' => array(
|
||||
'path' => 'v1alpha/{+name}',
|
||||
'httpMethod' => 'PATCH',
|
||||
'parameters' => array(
|
||||
'name' => array(
|
||||
'location' => 'path',
|
||||
'type' => 'string',
|
||||
'required' => true,
|
||||
),
|
||||
'updateMask' => array(
|
||||
'location' => 'query',
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Proxy_GoogleAnalyticsAdminProvisionAccountTicketRequest
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaProvisionAccountTicketRequest;
|
||||
|
||||
/**
|
||||
* Class for representing a proxied account ticket provisioning request body.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Proxy_GoogleAnalyticsAdminProvisionAccountTicketRequest extends GoogleAnalyticsAdminV1betaProvisionAccountTicketRequest {
|
||||
/**
|
||||
* The site ID.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @var string
|
||||
*/
|
||||
public $site_id = '';
|
||||
|
||||
/**
|
||||
* The site secret.
|
||||
*
|
||||
* @since 1.98.0
|
||||
* @var string
|
||||
*/
|
||||
public $site_secret = '';
|
||||
|
||||
/**
|
||||
* Gets the site ID.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*/
|
||||
public function getSiteId() {
|
||||
return $this->site_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the site ID.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param string $id The site id.
|
||||
*/
|
||||
public function setSiteId( $id ) {
|
||||
$this->site_id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the site secret.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*/
|
||||
public function getSiteSecret() {
|
||||
return $this->site_secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the site secret.
|
||||
*
|
||||
* @since 1.98.0
|
||||
*
|
||||
* @param string $secret The site secret.
|
||||
*/
|
||||
public function setSiteSecret( $secret ) {
|
||||
$this->site_secret = $secret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Core\Util\Date;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\DateRange as Google_Service_AnalyticsData_DateRange;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Dimension as Google_Service_AnalyticsData_Dimension;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\DimensionOrderBy as Google_Service_AnalyticsData_DimensionOrderBy;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\MetricOrderBy as Google_Service_AnalyticsData_MetricOrderBy;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\OrderBy as Google_Service_AnalyticsData_OrderBy;
|
||||
|
||||
/**
|
||||
* The base class for Analytics 4 reports.
|
||||
*
|
||||
* @since 1.99.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Report {
|
||||
|
||||
/**
|
||||
* Plugin context.
|
||||
*
|
||||
* @since 1.99.0
|
||||
* @var Context
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Context $context Plugin context.
|
||||
*/
|
||||
public function __construct( Context $context ) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses report dimensions received in the request params.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return Google_Service_AnalyticsData_Dimension[] An array of AnalyticsData Dimension objects.
|
||||
*/
|
||||
protected function parse_dimensions( Data_Request $data ) {
|
||||
$dimensions = $data['dimensions'];
|
||||
if ( empty( $dimensions ) || ( ! is_string( $dimensions ) && ! is_array( $dimensions ) ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( is_string( $dimensions ) ) {
|
||||
$dimensions = explode( ',', $dimensions );
|
||||
} elseif ( is_array( $dimensions ) && ! wp_is_numeric_array( $dimensions ) ) { // If single object is passed.
|
||||
$dimensions = array( $dimensions );
|
||||
}
|
||||
|
||||
$dimensions = array_filter(
|
||||
array_map(
|
||||
function ( $dimension_def ) {
|
||||
$dimension = new Google_Service_AnalyticsData_Dimension();
|
||||
|
||||
if ( is_string( $dimension_def ) ) {
|
||||
$dimension->setName( $dimension_def );
|
||||
} elseif ( is_array( $dimension_def ) && ! empty( $dimension_def['name'] ) ) {
|
||||
$dimension->setName( $dimension_def['name'] );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $dimension;
|
||||
},
|
||||
array_filter( $dimensions )
|
||||
)
|
||||
);
|
||||
|
||||
return $dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses report date ranges received in the request params.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return Google_Service_AnalyticsData_DateRange[] An array of AnalyticsData DateRange objects.
|
||||
*/
|
||||
protected function parse_dateranges( Data_Request $data ) {
|
||||
$date_ranges = array();
|
||||
$start_date = $data['startDate'];
|
||||
$end_date = $data['endDate'];
|
||||
if ( strtotime( $start_date ) && strtotime( $end_date ) ) {
|
||||
$compare_start_date = $data['compareStartDate'];
|
||||
$compare_end_date = $data['compareEndDate'];
|
||||
$date_ranges[] = array( $start_date, $end_date );
|
||||
|
||||
// When using multiple date ranges, it changes the structure of the response:
|
||||
// Aggregate properties (minimum, maximum, totals) will have an entry per date range.
|
||||
// The rows property will have additional row entries for each date range.
|
||||
if ( strtotime( $compare_start_date ) && strtotime( $compare_end_date ) ) {
|
||||
$date_ranges[] = array( $compare_start_date, $compare_end_date );
|
||||
}
|
||||
} else {
|
||||
// Default the date range to the last 28 days.
|
||||
$date_ranges[] = Date::parse_date_range( 'last-28-days', 1 );
|
||||
}
|
||||
|
||||
$date_ranges = array_map(
|
||||
function ( $date_range ) {
|
||||
list ( $start_date, $end_date ) = $date_range;
|
||||
$date_range = new Google_Service_AnalyticsData_DateRange();
|
||||
$date_range->setStartDate( $start_date );
|
||||
$date_range->setEndDate( $end_date );
|
||||
|
||||
return $date_range;
|
||||
},
|
||||
$date_ranges
|
||||
);
|
||||
|
||||
return $date_ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the orderby value of the data request into an array of AnalyticsData OrderBy object instances.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return Google_Service_AnalyticsData_OrderBy[] An array of AnalyticsData OrderBy objects.
|
||||
*/
|
||||
protected function parse_orderby( Data_Request $data ) {
|
||||
$orderby = $data['orderby'];
|
||||
if ( empty( $orderby ) || ! is_array( $orderby ) || ! wp_is_numeric_array( $orderby ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$results = array_map(
|
||||
function ( $order_def ) {
|
||||
$order_by = new Google_Service_AnalyticsData_OrderBy();
|
||||
$order_by->setDesc( ! empty( $order_def['desc'] ) );
|
||||
|
||||
if ( isset( $order_def['metric'] ) && isset( $order_def['metric']['metricName'] ) ) {
|
||||
$metric_order_by = new Google_Service_AnalyticsData_MetricOrderBy();
|
||||
$metric_order_by->setMetricName( $order_def['metric']['metricName'] );
|
||||
$order_by->setMetric( $metric_order_by );
|
||||
} elseif ( isset( $order_def['dimension'] ) && isset( $order_def['dimension']['dimensionName'] ) ) {
|
||||
$dimension_order_by = new Google_Service_AnalyticsData_DimensionOrderBy();
|
||||
$dimension_order_by->setDimensionName( $order_def['dimension']['dimensionName'] );
|
||||
$order_by->setDimension( $dimension_order_by );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $order_by;
|
||||
},
|
||||
$orderby
|
||||
);
|
||||
|
||||
$results = array_filter( $results );
|
||||
$results = array_values( $results );
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter\Filter
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
|
||||
|
||||
/**
|
||||
* Interface for a dimension filter class.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*/
|
||||
interface Filter {
|
||||
|
||||
/**
|
||||
* Converts the dimension filter into the GA4 compatible dimension filter expression.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*
|
||||
* @param string $dimension_name The dimension name.
|
||||
* @param mixed $dimension_value The dimension filter value.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
public function parse_filter_expression( $dimension_name, $dimension_value );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter\In_List_Filter
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Filter as Google_Service_AnalyticsData_Filter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\InListFilter as Google_Service_AnalyticsData_InListFilter;
|
||||
|
||||
/**
|
||||
* Class for parsing the dimension in-list filter.
|
||||
*
|
||||
* @since 1.106.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class In_List_Filter implements Filter {
|
||||
|
||||
/**
|
||||
* Converts the dimension filter into the GA4 compatible dimension filter expression.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*
|
||||
* @param string $dimension_name The dimension name.
|
||||
* @param mixed $dimension_value The dimension filter value.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
public function parse_filter_expression( $dimension_name, $dimension_value ) {
|
||||
$in_list_filter = new Google_Service_AnalyticsData_InListFilter();
|
||||
$in_list_filter->setValues( $dimension_value );
|
||||
|
||||
$filter = new Google_Service_AnalyticsData_Filter();
|
||||
$filter->setFieldName( $dimension_name );
|
||||
$filter->setInListFilter( $in_list_filter );
|
||||
|
||||
$expression = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$expression->setFilter( $filter );
|
||||
|
||||
return $expression;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter\String_Filter
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Filter as Google_Service_AnalyticsData_Filter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpressionList as Google_Service_AnalyticsData_FilterExpressionList;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\StringFilter as Google_Service_AnalyticsData_StringFilter;
|
||||
|
||||
/**
|
||||
* Class for parsing the dimension string filter.
|
||||
*
|
||||
* @since 1.106.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class String_Filter implements Filter {
|
||||
|
||||
/**
|
||||
* Converts the dimension filter into the GA4 compatible dimension filter expression.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*
|
||||
* @param string $dimension_name The dimension name.
|
||||
* @param mixed $dimension_value The dimension filter value.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
public function parse_filter_expression( $dimension_name, $dimension_value ) {
|
||||
$match_type = isset( $dimension_value['matchType'] )
|
||||
? $dimension_value['matchType']
|
||||
: 'EXACT';
|
||||
|
||||
$filter_value = isset( $dimension_value['value'] )
|
||||
? $dimension_value['value']
|
||||
: $dimension_value;
|
||||
|
||||
// If there are many values for this filter, then it means that we want to find
|
||||
// rows where dimension are included in the list of provided values. In this case,
|
||||
// we need to create a nested filter expression that contains separate string filters
|
||||
// for each item in the list and combined into the "OR" group.
|
||||
if ( is_array( $filter_value ) ) {
|
||||
$expressions = array();
|
||||
foreach ( $filter_value as $value ) {
|
||||
$expressions[] = $this->compose_individual_filter_expression(
|
||||
$dimension_name,
|
||||
$match_type,
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
$expression_list = new Google_Service_AnalyticsData_FilterExpressionList();
|
||||
$expression_list->setExpressions( $expressions );
|
||||
|
||||
$filter_expression = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$filter_expression->setOrGroup( $expression_list );
|
||||
|
||||
return $filter_expression;
|
||||
}
|
||||
|
||||
// If we have a single value for the filter, then we should use just a single
|
||||
// string filter expression and there is no need to create a nested one.
|
||||
return $this->compose_individual_filter_expression(
|
||||
$dimension_name,
|
||||
$match_type,
|
||||
$filter_value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes individual filter expression and returns it.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*
|
||||
* @param string $dimension_name The dimension name.
|
||||
* @param string $match_type The dimension filter match type.
|
||||
* @param mixed $value The dimension filter value.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
protected function compose_individual_filter_expression( $dimension_name, $match_type, $value ) {
|
||||
$string_filter = new Google_Service_AnalyticsData_StringFilter();
|
||||
$string_filter->setMatchType( $match_type );
|
||||
$string_filter->setValue( $value );
|
||||
|
||||
$filter = new Google_Service_AnalyticsData_Filter();
|
||||
$filter->setFieldName( $dimension_name );
|
||||
$filter->setStringFilter( $string_filter );
|
||||
|
||||
$filter_expression = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$filter_expression->setFilter( $filter );
|
||||
|
||||
return $filter_expression;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Metric_Filter\Between_Filter
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report\Metric_Filter
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report\Filters;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Filter as Google_Service_AnalyticsData_Filter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\BetweenFilter as Google_Service_AnalyticsData_BetweenFilter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\NumericValue as NumericValue;
|
||||
|
||||
/**
|
||||
* Class for parsing the metric between filter.
|
||||
*
|
||||
* @since 1.111.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Between_Filter {
|
||||
|
||||
/**
|
||||
* Converts the metric filter into the GA4 compatible metric filter expression.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @param string $metric_name The metric name.
|
||||
* @param integer $from_value The filter from value.
|
||||
* @param integer $to_value The filter to value.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
public function parse_filter_expression( $metric_name, $from_value, $to_value ) {
|
||||
$numeric_from_value = new NumericValue();
|
||||
$numeric_from_value->setInt64Value( $from_value );
|
||||
|
||||
$numeric_to_value = new NumericValue();
|
||||
$numeric_to_value->setInt64Value( $to_value );
|
||||
|
||||
$between_filter = new Google_Service_AnalyticsData_BetweenFilter();
|
||||
$between_filter->setFromValue( $numeric_from_value );
|
||||
$between_filter->setToValue( $numeric_to_value );
|
||||
|
||||
$filter = new Google_Service_AnalyticsData_Filter();
|
||||
$filter->setFieldName( $metric_name );
|
||||
$filter->setBetweenFilter( $between_filter );
|
||||
|
||||
$expression = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$expression->setFilter( $filter );
|
||||
|
||||
return $expression;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Metric_Filter\Numeric_Filter
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report\Metric_Filter
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report\Filters;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Filter as Google_Service_AnalyticsData_Filter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\NumericFilter as Google_Service_AnalyticsData_NumericFilter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\NumericValue as NumericValue;
|
||||
|
||||
/**
|
||||
* Class for parsing the metric numeric filter.
|
||||
*
|
||||
* @since 1.111.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Numeric_Filter {
|
||||
|
||||
/**
|
||||
* Converts the metric filter into the GA4 compatible metric filter expression.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @param string $metric_name The metric name.
|
||||
* @param string $operation The filter operation.
|
||||
* @param integer $value The filter value.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
public function parse_filter_expression( $metric_name, $operation, $value ) {
|
||||
$numeric_value = new NumericValue();
|
||||
$numeric_value->setInt64Value( $value );
|
||||
|
||||
$numeric_filter = new Google_Service_AnalyticsData_NumericFilter();
|
||||
$numeric_filter->setOperation( $operation );
|
||||
$numeric_filter->setValue( $numeric_value );
|
||||
|
||||
$filter = new Google_Service_AnalyticsData_Filter();
|
||||
$filter->setFieldName( $metric_name );
|
||||
$filter->setNumericFilter( $numeric_filter );
|
||||
|
||||
$expression = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$expression->setFilter( $filter );
|
||||
|
||||
return $expression;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Request
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report;
|
||||
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Core\Validation\Exception\Invalid_Report_Dimensions_Exception;
|
||||
use Google\Site_Kit\Core\Validation\Exception\Invalid_Report_Metrics_Exception;
|
||||
use Google\Site_Kit\Core\Util\URL;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Report;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter\In_List_Filter;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Report\Dimension_Filter\String_Filter;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Report\Filters\Numeric_Filter;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Report\Filters\Between_Filter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\DateRange as Google_Service_AnalyticsData_DateRange;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Dimension as Google_Service_AnalyticsData_Dimension;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\FilterExpressionList as Google_Service_AnalyticsData_FilterExpressionList;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\RunReportRequest as Google_Service_AnalyticsData_RunReportRequest;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Metric as Google_Service_AnalyticsData_Metric;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class for Analytics 4 report requests.
|
||||
*
|
||||
* @since 1.99.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Request extends Report {
|
||||
|
||||
/**
|
||||
* Creates and executes a new Analytics 4 report request.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @param bool $is_shared_request Determines whether the current request is shared or not.
|
||||
* @return RequestInterface|WP_Error Request object on success, or WP_Error on failure.
|
||||
*/
|
||||
public function create_request( Data_Request $data, $is_shared_request ) {
|
||||
$request = new Google_Service_AnalyticsData_RunReportRequest();
|
||||
$request->setKeepEmptyRows( true );
|
||||
$request->setMetricAggregations( array( 'TOTAL', 'MINIMUM', 'MAXIMUM' ) );
|
||||
|
||||
if ( ! empty( $data['limit'] ) ) {
|
||||
$request->setLimit( $data['limit'] );
|
||||
}
|
||||
|
||||
$dimensions = $this->parse_dimensions( $data );
|
||||
if ( ! empty( $dimensions ) ) {
|
||||
if ( $is_shared_request ) {
|
||||
try {
|
||||
$this->validate_shared_dimensions( $dimensions );
|
||||
} catch ( Invalid_Report_Dimensions_Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'invalid_analytics_4_report_dimensions',
|
||||
$exception->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$request->setDimensions( (array) $dimensions );
|
||||
}
|
||||
|
||||
$dimension_filters = $this->parse_dimension_filters( $data );
|
||||
$request->setDimensionFilter( $dimension_filters );
|
||||
|
||||
$metric_filters = $this->parse_metric_filters( $data );
|
||||
if ( ! empty( $metric_filters ) ) {
|
||||
$request->setMetricFilter( $metric_filters );
|
||||
}
|
||||
|
||||
$date_ranges = $this->parse_dateranges( $data );
|
||||
$request->setDateRanges( $date_ranges );
|
||||
|
||||
$metrics = $data['metrics'];
|
||||
if ( is_string( $metrics ) || is_array( $metrics ) ) {
|
||||
if ( is_string( $metrics ) ) {
|
||||
$metrics = explode( ',', $data['metrics'] );
|
||||
} elseif ( is_array( $metrics ) && ! wp_is_numeric_array( $metrics ) ) { // If single object is passed.
|
||||
$metrics = array( $metrics );
|
||||
}
|
||||
|
||||
$metrics = array_filter(
|
||||
array_map(
|
||||
function ( $metric_def ) {
|
||||
$metric = new Google_Service_AnalyticsData_Metric();
|
||||
|
||||
if ( is_string( $metric_def ) ) {
|
||||
$metric->setName( $metric_def );
|
||||
} elseif ( is_array( $metric_def ) ) {
|
||||
$metric->setName( $metric_def['name'] );
|
||||
if ( ! empty( $metric_def['expression'] ) ) {
|
||||
$metric->setExpression( $metric_def['expression'] );
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $metric;
|
||||
},
|
||||
$metrics
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $metrics ) ) {
|
||||
try {
|
||||
$this->validate_metrics( $metrics );
|
||||
} catch ( Invalid_Report_Metrics_Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'invalid_analytics_4_report_metrics',
|
||||
$exception->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
if ( $is_shared_request ) {
|
||||
try {
|
||||
$this->validate_shared_metrics( $metrics );
|
||||
} catch ( Invalid_Report_Metrics_Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'invalid_analytics_4_report_metrics',
|
||||
$exception->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$request->setMetrics( $metrics );
|
||||
}
|
||||
}
|
||||
|
||||
// Order by.
|
||||
$orderby = $this->parse_orderby( $data );
|
||||
if ( ! empty( $orderby ) ) {
|
||||
$request->setOrderBys( $orderby );
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given metrics for a report.
|
||||
*
|
||||
* Metrics must have valid names, matching the regular expression ^[a-zA-Z0-9_]+$ in keeping with the GA4 API.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Google_Service_AnalyticsData_Metric[] $metrics The metrics to validate.
|
||||
* @throws Invalid_Report_Metrics_Exception Thrown if the metrics are invalid.
|
||||
*/
|
||||
protected function validate_metrics( $metrics ) {
|
||||
$valid_name_expression = '^[a-zA-Z0-9_]+$';
|
||||
|
||||
$invalid_metrics = array_map(
|
||||
function ( $metric ) {
|
||||
return $metric->getName();
|
||||
},
|
||||
array_filter(
|
||||
$metrics,
|
||||
function ( $metric ) use ( $valid_name_expression ) {
|
||||
return ! preg_match( "#$valid_name_expression#", $metric->getName() );
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if ( count( $invalid_metrics ) > 0 ) {
|
||||
$message = count( $invalid_metrics ) > 1 ? sprintf(
|
||||
/* translators: 1: the regular expression for a valid name, 2: a comma separated list of the invalid metrics. */
|
||||
__(
|
||||
'Metric names should match the expression %1$s: %2$s',
|
||||
'google-site-kit'
|
||||
),
|
||||
$valid_name_expression,
|
||||
join(
|
||||
/* translators: used between list items, there is a space after the comma. */
|
||||
__( ', ', 'google-site-kit' ),
|
||||
$invalid_metrics
|
||||
)
|
||||
) : sprintf(
|
||||
/* translators: 1: the regular expression for a valid name, 2: the invalid metric. */
|
||||
__(
|
||||
'Metric name should match the expression %1$s: %2$s',
|
||||
'google-site-kit'
|
||||
),
|
||||
$valid_name_expression,
|
||||
$invalid_metrics[0]
|
||||
);
|
||||
|
||||
throw new Invalid_Report_Metrics_Exception( $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the report metrics for a shared request.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Google_Service_AnalyticsData_Metric[] $metrics The metrics to validate.
|
||||
* @throws Invalid_Report_Metrics_Exception Thrown if the metrics are invalid.
|
||||
*/
|
||||
protected function validate_shared_metrics( $metrics ) {
|
||||
$valid_metrics = apply_filters(
|
||||
'googlesitekit_shareable_analytics_4_metrics',
|
||||
array(
|
||||
'activeUsers',
|
||||
'averageSessionDuration',
|
||||
'bounceRate',
|
||||
'conversions',
|
||||
'engagedSessions',
|
||||
'engagementRate',
|
||||
'screenPageViews',
|
||||
'screenPageViewsPerSession',
|
||||
'sessions',
|
||||
'sessionConversionRate',
|
||||
'sessionsPerUser',
|
||||
'totalAdRevenue',
|
||||
'totalUsers',
|
||||
)
|
||||
);
|
||||
|
||||
$invalid_metrics = array_diff(
|
||||
array_map(
|
||||
function ( $metric ) {
|
||||
// If there is an expression, it means the name is there as an alias, otherwise the name should be a valid metric name.
|
||||
// Therefore, the expression takes precedence to the name for the purpose of allow-list validation.
|
||||
return ! empty( $metric->getExpression() ) ? $metric->getExpression() : $metric->getName();
|
||||
},
|
||||
$metrics
|
||||
),
|
||||
$valid_metrics
|
||||
);
|
||||
|
||||
if ( count( $invalid_metrics ) > 0 ) {
|
||||
$message = count( $invalid_metrics ) > 1 ? sprintf(
|
||||
/* translators: %s: is replaced with a comma separated list of the invalid metrics. */
|
||||
__(
|
||||
'Unsupported metrics requested: %s',
|
||||
'google-site-kit'
|
||||
),
|
||||
join(
|
||||
/* translators: used between list items, there is a space after the comma. */
|
||||
__( ', ', 'google-site-kit' ),
|
||||
$invalid_metrics
|
||||
)
|
||||
) : sprintf(
|
||||
/* translators: %s: is replaced with the invalid metric. */
|
||||
__(
|
||||
'Unsupported metric requested: %s',
|
||||
'google-site-kit'
|
||||
),
|
||||
$invalid_metrics[0]
|
||||
);
|
||||
|
||||
throw new Invalid_Report_Metrics_Exception( $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the report dimensions for a shared request.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Google_Service_AnalyticsData_Dimension[] $dimensions The dimensions to validate.
|
||||
* @throws Invalid_Report_Dimensions_Exception Thrown if the dimensions are invalid.
|
||||
*/
|
||||
protected function validate_shared_dimensions( $dimensions ) {
|
||||
$valid_dimensions = apply_filters(
|
||||
'googlesitekit_shareable_analytics_4_dimensions',
|
||||
array(
|
||||
'adSourceName',
|
||||
'city',
|
||||
'country',
|
||||
'date',
|
||||
'deviceCategory',
|
||||
'newVsReturning',
|
||||
'pagePath',
|
||||
'pageTitle',
|
||||
'sessionDefaultChannelGroup',
|
||||
'sessionDefaultChannelGrouping',
|
||||
'customEvent:googlesitekit_post_author',
|
||||
'customEvent:googlesitekit_post_categories',
|
||||
'customEvent:googlesitekit_post_date',
|
||||
'customEvent:googlesitekit_post_type',
|
||||
)
|
||||
);
|
||||
|
||||
$invalid_dimensions = array_diff(
|
||||
array_map(
|
||||
function ( $dimension ) {
|
||||
return $dimension->getName();
|
||||
},
|
||||
$dimensions
|
||||
),
|
||||
$valid_dimensions
|
||||
);
|
||||
|
||||
if ( count( $invalid_dimensions ) > 0 ) {
|
||||
$message = count( $invalid_dimensions ) > 1 ? sprintf(
|
||||
/* translators: %s: is replaced with a comma separated list of the invalid dimensions. */
|
||||
__(
|
||||
'Unsupported dimensions requested: %s',
|
||||
'google-site-kit'
|
||||
),
|
||||
join(
|
||||
/* translators: used between list items, there is a space after the comma. */
|
||||
__( ', ', 'google-site-kit' ),
|
||||
$invalid_dimensions
|
||||
)
|
||||
) : sprintf(
|
||||
/* translators: %s: is replaced with the invalid dimension. */
|
||||
__(
|
||||
'Unsupported dimension requested: %s',
|
||||
'google-site-kit'
|
||||
),
|
||||
$invalid_dimensions[0]
|
||||
);
|
||||
|
||||
throw new Invalid_Report_Dimensions_Exception( $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses dimension filters and returns a filter expression that should be added to the report request.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression to use with the report request.
|
||||
*/
|
||||
protected function parse_dimension_filters( Data_Request $data ) {
|
||||
$expressions = array();
|
||||
|
||||
$reference_url = trim( $this->context->get_reference_site_url(), '/' );
|
||||
$hostnames = URL::permute_site_hosts( URL::parse( $reference_url, PHP_URL_HOST ) );
|
||||
$expressions[] = $this->parse_dimension_filter( 'hostName', $hostnames );
|
||||
|
||||
if ( ! empty( $data['url'] ) ) {
|
||||
$url = str_replace( $reference_url, '', esc_url_raw( $data['url'] ) );
|
||||
$expressions[] = $this->parse_dimension_filter( 'pagePath', $url );
|
||||
}
|
||||
|
||||
if ( is_array( $data['dimensionFilters'] ) ) {
|
||||
foreach ( $data['dimensionFilters'] as $key => $value ) {
|
||||
$expressions[] = $this->parse_dimension_filter( $key, $value );
|
||||
}
|
||||
}
|
||||
|
||||
$filter_expression_list = new Google_Service_AnalyticsData_FilterExpressionList();
|
||||
$filter_expression_list->setExpressions( array_filter( $expressions ) );
|
||||
|
||||
$dimension_filters = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$dimension_filters->setAndGroup( $filter_expression_list );
|
||||
|
||||
return $dimension_filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns a single dimension filter.
|
||||
*
|
||||
* @since 1.106.0
|
||||
*
|
||||
* @param string $dimension_name The dimension name.
|
||||
* @param mixed $dimension_value The dimension fileter settings.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
protected function parse_dimension_filter( $dimension_name, $dimension_value ) {
|
||||
// Use the string filter type by default.
|
||||
$filter_type = 'stringFilter';
|
||||
if ( isset( $dimension_value['filterType'] ) ) {
|
||||
// If the filterType property is provided, use the explicit filter type then.
|
||||
$filter_type = $dimension_value['filterType'];
|
||||
} elseif ( wp_is_numeric_array( $dimension_value ) ) {
|
||||
// Otherwise, if the dimension has a numeric array of values, we should fall
|
||||
// back to the "in list" filter type.
|
||||
$filter_type = 'inListFilter';
|
||||
}
|
||||
|
||||
if ( 'stringFilter' === $filter_type ) {
|
||||
$filter_class = String_Filter::class;
|
||||
} elseif ( 'inListFilter' === $filter_type ) {
|
||||
$filter_class = In_List_Filter::class;
|
||||
// Ensure that the 'inListFilter' is provided a flat array of values.
|
||||
// Extract the actual values from the 'value' key if present.
|
||||
if ( isset( $dimension_value['value'] ) ) {
|
||||
$dimension_value = $dimension_value['value'];
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filter = new $filter_class();
|
||||
$filter_expression = $filter->parse_filter_expression( $dimension_name, $dimension_value );
|
||||
|
||||
if ( ! empty( $dimension_value['notExpression'] ) ) {
|
||||
$not_filter_expression = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$not_filter_expression->setNotExpression( $filter_expression );
|
||||
return $not_filter_expression;
|
||||
}
|
||||
|
||||
return $filter_expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses metric filters and returns a filter expression that should be added to the report request.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression to use with the report request.
|
||||
*/
|
||||
protected function parse_metric_filters( Data_Request $data ) {
|
||||
$expressions = array();
|
||||
|
||||
if ( is_array( $data['metricFilters'] ) ) {
|
||||
foreach ( $data['metricFilters'] as $key => $value ) {
|
||||
$expressions[] = $this->parse_metric_filter( $key, $value );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $expressions ) ) {
|
||||
$filter_expression_list = new Google_Service_AnalyticsData_FilterExpressionList();
|
||||
$filter_expression_list->setExpressions( array_filter( $expressions ) );
|
||||
|
||||
$metric_filters = new Google_Service_AnalyticsData_FilterExpression();
|
||||
$metric_filters->setAndGroup( $filter_expression_list );
|
||||
|
||||
return $metric_filters;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns a single metric filter.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @param string $metric_name The metric name.
|
||||
* @param mixed $metric_value The metric filter settings.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
protected function parse_metric_filter( $metric_name, $metric_value ) {
|
||||
// Use the numeric filter type by default.
|
||||
$filter_type = 'numericFilter';
|
||||
if ( isset( $metric_value['filterType'] ) ) {
|
||||
// If the filterType property is provided, use the explicit filter type then.
|
||||
$filter_type = $metric_value['filterType'];
|
||||
}
|
||||
|
||||
if ( 'numericFilter' === $filter_type ) {
|
||||
if ( ! isset( $metric_value['operation'] ) || ! isset( $metric_value['value'] ) ) {
|
||||
return null;
|
||||
}
|
||||
if ( ! isset( $metric_value['value']['int64Value'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filter = new Numeric_Filter();
|
||||
|
||||
} elseif ( 'betweenFilter' === $filter_type ) {
|
||||
if ( ! isset( $metric_value['from_value'] ) || ! isset( $metric_value['to_value'] ) ) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
! isset( $metric_value['from_value']['int64Value'] ) ||
|
||||
! isset( $metric_value['to_value']['int64Value'] )
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filter = new Between_Filter();
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filter_expression = $this->get_metric_filter_expression(
|
||||
$filter,
|
||||
$metric_name,
|
||||
$metric_value
|
||||
);
|
||||
|
||||
return $filter_expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns correct filter expression instance based on the metric filter instance.
|
||||
*
|
||||
* @since 1.111.0
|
||||
*
|
||||
* @param Numeric_Filter|Between_Filter $filter The metric filter instance.
|
||||
* @param string $metric_name The metric name.
|
||||
* @param mixed $metric_value The metric filter settings.
|
||||
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
|
||||
*/
|
||||
protected function get_metric_filter_expression( $filter, $metric_name, $metric_value ) {
|
||||
if ( $filter instanceof Numeric_Filter ) {
|
||||
$value = $metric_value['value']['int64Value'];
|
||||
|
||||
$filter_expression = $filter->parse_filter_expression(
|
||||
$metric_name,
|
||||
$metric_value['operation'],
|
||||
$value
|
||||
);
|
||||
|
||||
} elseif ( $filter instanceof Between_Filter ) {
|
||||
$from_value = $metric_value['from_value']['int64Value'];
|
||||
$to_value = $metric_value['to_value']['int64Value'];
|
||||
|
||||
$filter_expression = $filter->parse_filter_expression(
|
||||
$metric_name,
|
||||
$from_value,
|
||||
$to_value
|
||||
);
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $filter_expression;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Response
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report;
|
||||
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Modules\Analytics_4\Report;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\DateRange as Google_Service_AnalyticsData_DateRange;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Row as Google_Service_AnalyticsData_Row;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\RunReportResponse as Google_Service_AnalyticsData_RunReportResponse;
|
||||
|
||||
/**
|
||||
* Class for Analytics 4 report responses.
|
||||
*
|
||||
* @since 1.99.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Response extends Report {
|
||||
|
||||
use Row_Trait;
|
||||
|
||||
/**
|
||||
* Parses the report response, and pads the report data with zero-data rows where rows are missing. This only applies for reports which request a single `date` dimension.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @param Google_Service_AnalyticsData_RunReportResponse $response Request response.
|
||||
* @return mixed Parsed response data on success, or WP_Error on failure.
|
||||
*/
|
||||
public function parse_response( Data_Request $data, $response ) {
|
||||
// Return early if the response is not of the expected type.
|
||||
if ( ! $response instanceof Google_Service_AnalyticsData_RunReportResponse ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Get report dimensions and return early if there is either more than one dimension or
|
||||
// the only dimension is not "date".
|
||||
$dimensions = $this->parse_dimensions( $data );
|
||||
if ( count( $dimensions ) !== 1 || $dimensions[0]->getName() !== 'date' ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Get date ranges and return early if there are no date ranges for this report.
|
||||
$date_ranges = $this->get_sorted_dateranges( $data );
|
||||
if ( empty( $date_ranges ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Get all available dates in the report.
|
||||
$existing_rows = array();
|
||||
foreach ( $response->getRows() as $row ) {
|
||||
$dimension_values = $row->getDimensionValues();
|
||||
|
||||
$range = 'date_range_0';
|
||||
if ( count( $dimension_values ) > 1 ) {
|
||||
// Considering this code will only be run when we are requesting a single dimension, `date`,
|
||||
// the implication is that the row will _only_ have an additional dimension when multiple
|
||||
// date ranges are requested.
|
||||
//
|
||||
// In this scenario, the dimension at index 1 will have a value of `date_range_{i}`, where
|
||||
// `i` is the zero-based index of the date range.
|
||||
$range = $dimension_values[1]->getValue();
|
||||
}
|
||||
|
||||
$range = str_replace( 'date_range_', '', $range );
|
||||
$date = $dimension_values[0]->getValue();
|
||||
$key = self::get_response_row_key( $date, is_numeric( $range ) ? $range : false );
|
||||
|
||||
$existing_rows[ $key ] = $row;
|
||||
}
|
||||
|
||||
$metric_headers = $response->getMetricHeaders();
|
||||
$ranges_count = count( $date_ranges );
|
||||
$multiple_ranges = $ranges_count > 1;
|
||||
$rows = array();
|
||||
|
||||
// Add rows for the current date for each date range.
|
||||
self::iterate_date_ranges(
|
||||
$date_ranges,
|
||||
function( $date ) use ( &$rows, $existing_rows, $ranges_count, $metric_headers, $multiple_ranges ) {
|
||||
for ( $i = 0; $i < $ranges_count; $i++ ) {
|
||||
// Copy the existing row if it is available, otherwise create a new zero-value row.
|
||||
$key = self::get_response_row_key( $date, $i );
|
||||
$rows[ $key ] = isset( $existing_rows[ $key ] )
|
||||
? $existing_rows[ $key ]
|
||||
: $this->create_report_row( $metric_headers, $date, $multiple_ranges ? $i : false );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// If we have the same number of rows as in the response at the moment, then
|
||||
// we can return the response without setting the new rows back into the response.
|
||||
$new_rows_count = count( $rows );
|
||||
if ( $new_rows_count <= $response->getRowCount() ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// If we have multiple date ranges, we need to sort rows to have them in
|
||||
// the correct order.
|
||||
if ( $multiple_ranges ) {
|
||||
$rows = self::sort_response_rows( $rows, $date_ranges );
|
||||
}
|
||||
|
||||
// Set updated rows back to the response object.
|
||||
$response->setRows( array_values( $rows ) );
|
||||
$response->setRowCount( $new_rows_count );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response row key composed from the date and the date range index values.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param string $date The date of the row to return key for.
|
||||
* @param int|bool $date_range_index The date range index, or FALSE if no index is available.
|
||||
* @return string The row key.
|
||||
*/
|
||||
protected static function get_response_row_key( $date, $date_range_index ) {
|
||||
return "{$date}_{$date_range_index}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sorted and filtered date ranges received in the request params. All corrupted date ranges
|
||||
* are ignored and not included in the returning list.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return Google_Service_AnalyticsData_DateRange[] An array of AnalyticsData DateRange objects.
|
||||
*/
|
||||
protected function get_sorted_dateranges( Data_Request $data ) {
|
||||
$date_ranges = $this->parse_dateranges( $data );
|
||||
if ( empty( $date_ranges ) ) {
|
||||
return $date_ranges;
|
||||
}
|
||||
|
||||
// Filter out all corrupted date ranges.
|
||||
$date_ranges = array_filter(
|
||||
$date_ranges,
|
||||
function( $range ) {
|
||||
$start = strtotime( $range->getStartDate() );
|
||||
$end = strtotime( $range->getEndDate() );
|
||||
return ! empty( $start ) && ! empty( $end );
|
||||
}
|
||||
);
|
||||
|
||||
// Sort date ranges preserving keys to have the oldest date range at the beginning and
|
||||
// the latest date range at the end.
|
||||
uasort(
|
||||
$date_ranges,
|
||||
function( $a, $b ) {
|
||||
$a_start = strtotime( $a->getStartDate() );
|
||||
$b_start = strtotime( $b->getStartDate() );
|
||||
return $a_start - $b_start;
|
||||
}
|
||||
);
|
||||
|
||||
return $date_ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts response rows using the algorithm similar to the one that Analytics 4 uses internally
|
||||
* and returns sorted rows.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Google_Service_AnalyticsData_Row[] $rows The current report rows.
|
||||
* @param Google_Service_AnalyticsData_DateRange[] $date_ranges The report date ranges.
|
||||
* @return Google_Service_AnalyticsData_Row[] Sorted rows.
|
||||
*/
|
||||
protected static function sort_response_rows( $rows, $date_ranges ) {
|
||||
$sorted_rows = array();
|
||||
$ranges_count = count( $date_ranges );
|
||||
|
||||
self::iterate_date_ranges(
|
||||
$date_ranges,
|
||||
function( $date, $range_index ) use ( &$sorted_rows, $ranges_count, $rows ) {
|
||||
// First take the main date range row.
|
||||
$key = self::get_response_row_key( $date, $range_index );
|
||||
$sorted_rows[ $key ] = $rows[ $key ];
|
||||
|
||||
// Then take all remaining rows.
|
||||
for ( $i = 0; $i < $ranges_count; $i++ ) {
|
||||
if ( $i !== $range_index ) {
|
||||
$key = self::get_response_row_key( $date, $i );
|
||||
$sorted_rows[ $key ] = $rows[ $key ];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return $sorted_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the date ranges and calls callback for each date in each range.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Google_Service_AnalyticsData_DateRange[] $date_ranges The report date ranges.
|
||||
* @param callable $callback The callback to execute for each date.
|
||||
*/
|
||||
protected static function iterate_date_ranges( $date_ranges, $callback ) {
|
||||
foreach ( $date_ranges as $date_range_index => $date_range ) {
|
||||
$now = strtotime( $date_range->getStartDate() );
|
||||
$end = strtotime( $date_range->getEndDate() );
|
||||
|
||||
do {
|
||||
call_user_func(
|
||||
$callback,
|
||||
gmdate( 'Ymd', $now ),
|
||||
$date_range_index
|
||||
);
|
||||
|
||||
$now += DAY_IN_SECONDS;
|
||||
} while ( $now <= $end );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Report\Row_Trait
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4\Report
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4\Report;
|
||||
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\DimensionValue as Google_Service_AnalyticsData_DimensionValue;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\MetricHeader as Google_Service_AnalyticsData_MetricHeader;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\MetricValue as Google_Service_AnalyticsData_MetricValue;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Row as Google_Service_AnalyticsData_Row;
|
||||
|
||||
/**
|
||||
* A trait that adds a helper method to create report rows.
|
||||
*
|
||||
* @since 1.99.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
trait Row_Trait {
|
||||
|
||||
/**
|
||||
* Creates and returns a new zero-value row for provided date and metrics.
|
||||
*
|
||||
* @since 1.99.0
|
||||
*
|
||||
* @param Google_Service_AnalyticsData_MetricHeader[] $metric_headers Metric headers from the report response.
|
||||
* @param string $current_date The current date to create a zero-value row for.
|
||||
* @param int|bool $date_range_index The date range index for the current date.
|
||||
* @param string $default_value The default value to use for metric values in the row.
|
||||
* @return Google_Service_AnalyticsData_Row A new zero-value row instance.
|
||||
*/
|
||||
protected function create_report_row( $metric_headers, $current_date, $date_range_index, $default_value = '0' ) {
|
||||
$dimension_values = array();
|
||||
|
||||
$current_date_dimension_value = new Google_Service_AnalyticsData_DimensionValue();
|
||||
$current_date_dimension_value->setValue( $current_date );
|
||||
$dimension_values[] = $current_date_dimension_value;
|
||||
|
||||
// If we have multiple date ranges, we need to add "date_range_{i}" index to dimension values.
|
||||
if ( false !== $date_range_index ) {
|
||||
$date_range_dimension_value = new Google_Service_AnalyticsData_DimensionValue();
|
||||
$date_range_dimension_value->setValue( "date_range_{$date_range_index}" );
|
||||
$dimension_values[] = $date_range_dimension_value;
|
||||
}
|
||||
|
||||
$metric_values = array();
|
||||
foreach ( $metric_headers as $metric_header ) {
|
||||
$metric_value = new Google_Service_AnalyticsData_MetricValue();
|
||||
$metric_value->setValue( $default_value );
|
||||
|
||||
$metric_values[] = $metric_value;
|
||||
}
|
||||
|
||||
$row = new Google_Service_AnalyticsData_Row();
|
||||
$row->setDimensionValues( $dimension_values );
|
||||
$row->setMetricValues( $metric_values );
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Resource_Data_Availability_Date
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Storage\Transients;
|
||||
|
||||
/**
|
||||
* Class for managing Analytics 4 resource data availability date.
|
||||
*
|
||||
* @since 1.127.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Resource_Data_Availability_Date {
|
||||
|
||||
/**
|
||||
* List of valid custom dimension slugs.
|
||||
*
|
||||
* @since 1.127.0
|
||||
* @var array
|
||||
*/
|
||||
const CUSTOM_DIMENSION_SLUGS = array(
|
||||
'googlesitekit_post_type',
|
||||
);
|
||||
|
||||
const RESOURCE_TYPE_AUDIENCE = 'audience';
|
||||
const RESOURCE_TYPE_CUSTOM_DIMENSION = 'customDimension';
|
||||
const RESOURCE_TYPE_PROPERTY = 'property';
|
||||
|
||||
/**
|
||||
* Transients instance.
|
||||
*
|
||||
* @since 1.127.0
|
||||
* @var Transients
|
||||
*/
|
||||
protected $transients;
|
||||
|
||||
/**
|
||||
* Module settings.
|
||||
*
|
||||
* @since 1.127.0
|
||||
* @var Module_Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param Transients $transients Transients instance.
|
||||
* @param Module_Settings $settings Module settings instance.
|
||||
*/
|
||||
public function __construct( Transients $transients, Module_Settings $settings ) {
|
||||
$this->transients = $transients;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data availability date for the given resource.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param string $resource_slug Resource slug.
|
||||
* @param string $resource_type Resource type.
|
||||
* @return int Data availability date in YYYYMMDD format on success, 0 otherwise.
|
||||
*/
|
||||
public function get_resource_date( $resource_slug, $resource_type ) {
|
||||
return (int) $this->transients->get( $this->get_resource_transient_name( $resource_slug, $resource_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data availability date for the given resource.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param string $resource_slug Resource slug.
|
||||
* @param string $resource_type Resource type.
|
||||
* @param int $date Data availability date.
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function set_resource_date( $resource_slug, $resource_type, $date ) {
|
||||
return $this->transients->set( $this->get_resource_transient_name( $resource_slug, $resource_type ), $date );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the data availability date for the given resource.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param string $resource_slug Resource slug.
|
||||
* @param string $resource_type Resource type.
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function reset_resource_date( $resource_slug, $resource_type ) {
|
||||
return $this->transients->delete( $this->get_resource_transient_name( $resource_slug, $resource_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data availability dates for all resources.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @return array Associative array of resource names and their data availability date.
|
||||
*/
|
||||
public function get_all_resource_dates() {
|
||||
$property_id = $this->get_property_id();
|
||||
$available_audiences = $this->get_available_audience_resource_names();
|
||||
|
||||
return array_map(
|
||||
// Filter out falsy values (0) from every resource's data availability dates.
|
||||
fn( $data_availability_dates ) => array_filter( $data_availability_dates ),
|
||||
array(
|
||||
// Get data availability dates for the available audiences.
|
||||
self::RESOURCE_TYPE_AUDIENCE => array_reduce(
|
||||
$available_audiences,
|
||||
function ( $audience_data_availability_dates, $audience ) {
|
||||
$audience_data_availability_dates[ $audience ] = $this->get_resource_date( $audience, self::RESOURCE_TYPE_AUDIENCE );
|
||||
return $audience_data_availability_dates;
|
||||
},
|
||||
array()
|
||||
),
|
||||
// Get data availability dates for the custom dimensions.
|
||||
self::RESOURCE_TYPE_CUSTOM_DIMENSION => array_reduce(
|
||||
self::CUSTOM_DIMENSION_SLUGS,
|
||||
function ( $custom_dimension_data_availability_dates, $custom_dimension ) {
|
||||
$custom_dimension_data_availability_dates[ $custom_dimension ] = $this->get_resource_date( $custom_dimension, self::RESOURCE_TYPE_CUSTOM_DIMENSION );
|
||||
return $custom_dimension_data_availability_dates;
|
||||
},
|
||||
array()
|
||||
),
|
||||
// Get data availability date for the current property.
|
||||
self::RESOURCE_TYPE_PROPERTY => array(
|
||||
$property_id => $this->get_resource_date(
|
||||
$property_id,
|
||||
self::RESOURCE_TYPE_PROPERTY
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the data availability date for all resources.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param array/null $available_audience_names Optional. List of available audience resource names. If not provided, it will be fetched from settings.
|
||||
* @param string/null $property_id Optional. Property ID. If not provided, it will be fetched from settings.
|
||||
*/
|
||||
public function reset_all_resource_dates( $available_audience_names = null, $property_id = null ) {
|
||||
|
||||
foreach ( self::CUSTOM_DIMENSION_SLUGS as $custom_dimension ) {
|
||||
$this->reset_resource_date( $custom_dimension, self::RESOURCE_TYPE_CUSTOM_DIMENSION );
|
||||
}
|
||||
|
||||
$available_audience_names = $available_audience_names ?: $this->get_available_audience_resource_names();
|
||||
|
||||
foreach ( $available_audience_names as $audience_name ) {
|
||||
$this->reset_resource_date( $audience_name, self::RESOURCE_TYPE_AUDIENCE );
|
||||
}
|
||||
|
||||
$property_id = $property_id ?: $this->get_property_id();
|
||||
|
||||
$this->reset_resource_date( $property_id, self::RESOURCE_TYPE_PROPERTY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given resource type is valid.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param string $resource_type Resource type.
|
||||
* @return bool True if valid, false otherwise.
|
||||
*/
|
||||
public function is_valid_resource_type( $resource_type ) {
|
||||
return in_array( $resource_type, array( self::RESOURCE_TYPE_AUDIENCE, self::RESOURCE_TYPE_CUSTOM_DIMENSION, self::RESOURCE_TYPE_PROPERTY ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given resource slug is valid.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param string $resource_slug Resource slug.
|
||||
* @param string $resource_type Resource type.
|
||||
* @return bool True if valid, false otherwise.
|
||||
*/
|
||||
public function is_valid_resource_slug( $resource_slug, $resource_type ) {
|
||||
switch ( $resource_type ) {
|
||||
case self::RESOURCE_TYPE_AUDIENCE:
|
||||
return in_array( $resource_slug, $this->get_available_audience_resource_names(), true );
|
||||
case self::RESOURCE_TYPE_CUSTOM_DIMENSION:
|
||||
return in_array( $resource_slug, self::CUSTOM_DIMENSION_SLUGS, true );
|
||||
case self::RESOURCE_TYPE_PROPERTY:
|
||||
return $resource_slug === $this->get_property_id();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data available date transient name for the given resource.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @param string $resource_slug Resource slug.
|
||||
* @param string $resource_type Resource type.
|
||||
* @return string Data available date transient name.
|
||||
*/
|
||||
protected function get_resource_transient_name( $resource_slug, $resource_type ) {
|
||||
return "googlesitekit_{$resource_type}_{$resource_slug}_data_availability_date";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets available audience resource names.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @return array List of available audience resource names.
|
||||
*/
|
||||
private function get_available_audience_resource_names() {
|
||||
$settings = $this->settings->get();
|
||||
$available_audiences = $settings['availableAudiences'] ?? array();
|
||||
|
||||
return array_map(
|
||||
function ( $audience ) {
|
||||
return $audience['name'];
|
||||
},
|
||||
$available_audiences
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property ID from settings instance.
|
||||
*
|
||||
* @since 1.127.0
|
||||
*
|
||||
* @return string Property ID.
|
||||
*/
|
||||
private function get_property_id() {
|
||||
return $this->settings->get()['propertyID'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Interface;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Trait;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_ViewOnly_Keys_Interface;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Class for Analytics 4 settings.
|
||||
*
|
||||
* @since 1.30.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Settings extends Module_Settings implements Setting_With_Owned_Keys_Interface, Setting_With_ViewOnly_Keys_Interface {
|
||||
|
||||
use Setting_With_Owned_Keys_Trait;
|
||||
use Method_Proxy_Trait;
|
||||
|
||||
const OPTION = 'googlesitekit_analytics-4_settings';
|
||||
|
||||
/**
|
||||
* Registers the setting in WordPress.
|
||||
*
|
||||
* @since 1.30.0
|
||||
*/
|
||||
public function register() {
|
||||
parent::register();
|
||||
|
||||
$this->register_owned_keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for owned settings.
|
||||
*
|
||||
* @since 1.30.0
|
||||
*
|
||||
* @return array An array of keys for owned settings.
|
||||
*/
|
||||
public function get_owned_keys() {
|
||||
return array(
|
||||
'accountID',
|
||||
'propertyID',
|
||||
'webDataStreamID',
|
||||
'measurementID',
|
||||
'googleTagID',
|
||||
'googleTagAccountID',
|
||||
'googleTagContainerID',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for view-only settings.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @return array An array of keys for view-only settings.
|
||||
*/
|
||||
public function get_view_only_keys() {
|
||||
return array( 'availableCustomDimensions', 'adSenseLinked' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.30.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'ownerID' => 0,
|
||||
'accountID' => '',
|
||||
'adsConversionID' => '',
|
||||
'propertyID' => '',
|
||||
'webDataStreamID' => '',
|
||||
'measurementID' => '',
|
||||
'trackingDisabled' => array( 'loggedinUsers' ),
|
||||
'useSnippet' => true,
|
||||
'googleTagID' => '',
|
||||
'googleTagAccountID' => '',
|
||||
'googleTagContainerID' => '',
|
||||
'googleTagContainerDestinationIDs' => null,
|
||||
'googleTagLastSyncedAtMs' => 0,
|
||||
'availableCustomDimensions' => null,
|
||||
'propertyCreateTime' => 0,
|
||||
'adSenseLinked' => false,
|
||||
'adSenseLinkedLastSyncedAt' => 0,
|
||||
'adsConversionIDMigratedAtMs' => 0,
|
||||
'adsLinked' => false,
|
||||
'adsLinkedLastSyncedAt' => 0,
|
||||
'availableAudiences' => null,
|
||||
'availableAudiencesLastSyncedAt' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the callback for sanitizing the setting's value before saving.
|
||||
*
|
||||
* @since 1.30.0
|
||||
*
|
||||
* @return callable|null
|
||||
*/
|
||||
protected function get_sanitize_callback() {
|
||||
return function( $option ) {
|
||||
if ( is_array( $option ) ) {
|
||||
if ( isset( $option['useSnippet'] ) ) {
|
||||
$option['useSnippet'] = (bool) $option['useSnippet'];
|
||||
}
|
||||
if ( isset( $option['googleTagID'] ) ) {
|
||||
if ( ! preg_match( '/^(G|GT|AW)-[a-zA-Z0-9]+$/', $option['googleTagID'] ) ) {
|
||||
$option['googleTagID'] = '';
|
||||
}
|
||||
}
|
||||
if ( isset( $option['trackingDisabled'] ) ) {
|
||||
// Prevent other options from being saved if 'loggedinUsers' is selected.
|
||||
if ( in_array( 'loggedinUsers', $option['trackingDisabled'], true ) ) {
|
||||
$option['trackingDisabled'] = array( 'loggedinUsers' );
|
||||
} else {
|
||||
$option['trackingDisabled'] = (array) $option['trackingDisabled'];
|
||||
}
|
||||
}
|
||||
|
||||
$numeric_properties = array( 'googleTagAccountID', 'googleTagContainerID' );
|
||||
foreach ( $numeric_properties as $numeric_property ) {
|
||||
if ( isset( $option[ $numeric_property ] ) ) {
|
||||
if ( ! is_numeric( $option[ $numeric_property ] ) || ! $option[ $numeric_property ] > 0 ) {
|
||||
$option[ $numeric_property ] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['googleTagContainerDestinationIDs'] ) ) {
|
||||
if ( ! is_array( $option['googleTagContainerDestinationIDs'] ) ) {
|
||||
$option['googleTagContainerDestinationIDs'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['availableCustomDimensions'] ) ) {
|
||||
if ( is_array( $option['availableCustomDimensions'] ) ) {
|
||||
$valid_dimensions = array_filter(
|
||||
$option['availableCustomDimensions'],
|
||||
function( $dimension ) {
|
||||
return is_string( $dimension ) && strpos( $dimension, 'googlesitekit_' ) === 0;
|
||||
}
|
||||
);
|
||||
|
||||
$option['availableCustomDimensions'] = array_values( $valid_dimensions );
|
||||
} else {
|
||||
$option['availableCustomDimensions'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['adSenseLinked'] ) ) {
|
||||
$option['adSenseLinked'] = (bool) $option['adSenseLinked'];
|
||||
}
|
||||
|
||||
if ( isset( $option['adSenseLinkedLastSyncedAt'] ) ) {
|
||||
if ( ! is_int( $option['adSenseLinkedLastSyncedAt'] ) ) {
|
||||
$option['adSenseLinkedLastSyncedAt'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['adsConversionIDMigratedAtMs'] ) ) {
|
||||
if ( ! is_int( $option['adsConversionIDMigratedAtMs'] ) ) {
|
||||
$option['adsConversionIDMigratedAtMs'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['adsLinked'] ) ) {
|
||||
$option['adsLinked'] = (bool) $option['adsLinked'];
|
||||
}
|
||||
|
||||
if ( isset( $option['adsLinkedLastSyncedAt'] ) ) {
|
||||
if ( ! is_int( $option['adsLinkedLastSyncedAt'] ) ) {
|
||||
$option['adsLinkedLastSyncedAt'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['availableAudiences'] ) ) {
|
||||
if ( ! is_array( $option['availableAudiences'] ) ) {
|
||||
$option['availableAudiences'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $option['availableAudiencesLastSyncedAt'] ) ) {
|
||||
if ( ! is_int( $option['availableAudiencesLastSyncedAt'] ) ) {
|
||||
$option['availableAudiencesLastSyncedAt'] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $option;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Synchronize_AdSenseLinked
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\Storage\Options;
|
||||
use Google\Site_Kit\Core\Storage\User_Options;
|
||||
use Google\Site_Kit\Modules\Adsense;
|
||||
use Google\Site_Kit\Modules\Analytics_4;
|
||||
use Google\Site_Kit\Modules\AdSense\Settings as Adsense_Settings;
|
||||
|
||||
/**
|
||||
* The base class for Synchronizing the adSenseLinked status.
|
||||
*
|
||||
* @since 1.123.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Synchronize_AdSenseLinked {
|
||||
const CRON_SYNCHRONIZE_ADSENSE_LINKED = 'googlesitekit_cron_synchronize_adsense_linked_data';
|
||||
|
||||
/**
|
||||
* Analytics_4 instance.
|
||||
*
|
||||
* @since 1.123.0
|
||||
* @var Analytics_4
|
||||
*/
|
||||
protected $analytics_4;
|
||||
|
||||
/**
|
||||
* User_Options instance.
|
||||
*
|
||||
* @since 1.123.0
|
||||
* @var User_Options
|
||||
*/
|
||||
protected $user_options;
|
||||
|
||||
/**
|
||||
* Options instance.
|
||||
*
|
||||
* @since 1.123.0
|
||||
* @var Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.123.0
|
||||
*
|
||||
* @param Analytics_4 $analytics_4 Analytics 4 instance.
|
||||
* @param User_Options $user_options User_Options instance.
|
||||
* @param Options $options Options instance.
|
||||
*/
|
||||
public function __construct( Analytics_4 $analytics_4, User_Options $user_options, Options $options ) {
|
||||
$this->analytics_4 = $analytics_4;
|
||||
$this->user_options = $user_options;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.123.0
|
||||
*/
|
||||
public function register() {
|
||||
add_action(
|
||||
self::CRON_SYNCHRONIZE_ADSENSE_LINKED,
|
||||
function() {
|
||||
$this->synchronize_adsense_linked_data();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron callback for synchronizing the adsense linked data.
|
||||
*
|
||||
* @since 1.123.0
|
||||
*/
|
||||
protected function synchronize_adsense_linked_data() {
|
||||
$owner_id = $this->analytics_4->get_owner_id();
|
||||
$restore_user = $this->user_options->switch_user( $owner_id );
|
||||
|
||||
if ( user_can( $owner_id, Permissions::VIEW_AUTHENTICATED_DASHBOARD ) ) {
|
||||
$this->synchronize_adsense_linked_status();
|
||||
}
|
||||
|
||||
$restore_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules single cron which will synchronize the adSenseLinked status.
|
||||
*
|
||||
* @since 1.123.0
|
||||
*/
|
||||
public function maybe_schedule_synchronize_adsense_linked() {
|
||||
$analytics_4_connected = apply_filters( 'googlesitekit_is_module_connected', false, Analytics_4::MODULE_SLUG );
|
||||
$adsense_connected = apply_filters( 'googlesitekit_is_module_connected', false, AdSense::MODULE_SLUG );
|
||||
$cron_already_scheduled = wp_next_scheduled( self::CRON_SYNCHRONIZE_ADSENSE_LINKED );
|
||||
|
||||
if ( $analytics_4_connected && $adsense_connected && ! $cron_already_scheduled ) {
|
||||
wp_schedule_single_event(
|
||||
// Schedule the task to run in 24 hours.
|
||||
time() + ( DAY_IN_SECONDS ),
|
||||
self::CRON_SYNCHRONIZE_ADSENSE_LINKED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize the AdSenseLinked status.
|
||||
*
|
||||
* @since 1.123.0
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function synchronize_adsense_linked_status() {
|
||||
$settings_ga4 = $this->analytics_4->get_settings()->get();
|
||||
$property_id = $settings_ga4['propertyID'];
|
||||
$property_adsense_links = $this->analytics_4->get_data( 'adsense-links', array( 'propertyID' => $property_id ) );
|
||||
$current_adsense_options = ( new AdSense_Settings( $this->options ) )->get();
|
||||
$current_adsense_client_id = ! empty( $current_adsense_options['clientID'] ) ? $current_adsense_options['clientID'] : '';
|
||||
|
||||
if ( is_wp_error( $property_adsense_links ) || empty( $property_adsense_links ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$found_adsense_linked_for_client_id = false;
|
||||
|
||||
// Iterate over returned AdSense links and set true if one is found
|
||||
// matching the same client ID.
|
||||
foreach ( $property_adsense_links as $property_adsense_link ) {
|
||||
if ( $current_adsense_client_id === $property_adsense_link['adClientCode'] ) {
|
||||
$found_adsense_linked_for_client_id = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the AdSenseLinked status and timestamp.
|
||||
$this->analytics_4->get_settings()->merge(
|
||||
array(
|
||||
'adSenseLinked' => $found_adsense_linked_for_client_id,
|
||||
'adSenseLinkedLastSyncedAt' => time(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Synchronize_AdsLinked
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\Storage\User_Options;
|
||||
use Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
/**
|
||||
* The base class for Synchronizing the adsLinked status.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Synchronize_AdsLinked {
|
||||
const CRON_SYNCHRONIZE_ADS_LINKED = 'googlesitekit_cron_synchronize_ads_linked_data';
|
||||
|
||||
/**
|
||||
* Analytics_4 instance.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @var Analytics_4
|
||||
*/
|
||||
protected $analytics_4;
|
||||
|
||||
/**
|
||||
* User_Options instance.
|
||||
*
|
||||
* @since 1.124.0
|
||||
* @var User_Options
|
||||
*/
|
||||
protected $user_options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @param Analytics_4 $analytics_4 Analytics 4 instance.
|
||||
* @param User_Options $user_options User_Options instance.
|
||||
*/
|
||||
public function __construct( Analytics_4 $analytics_4, User_Options $user_options ) {
|
||||
$this->analytics_4 = $analytics_4;
|
||||
$this->user_options = $user_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*/
|
||||
public function register() {
|
||||
add_action(
|
||||
self::CRON_SYNCHRONIZE_ADS_LINKED,
|
||||
function() {
|
||||
$this->synchronize_ads_linked_data();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron callback for synchronizing the ads linked data.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*/
|
||||
protected function synchronize_ads_linked_data() {
|
||||
$owner_id = $this->analytics_4->get_owner_id();
|
||||
$restore_user = $this->user_options->switch_user( $owner_id );
|
||||
|
||||
if ( user_can( $owner_id, Permissions::VIEW_AUTHENTICATED_DASHBOARD ) ) {
|
||||
$this->synchronize_ads_linked_status();
|
||||
}
|
||||
|
||||
$restore_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize the adsLinked status.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function synchronize_ads_linked_status() {
|
||||
$settings_ga4 = $this->analytics_4->get_settings()->get();
|
||||
$property_id = $settings_ga4['propertyID'];
|
||||
$property_ads_links = $this->analytics_4->get_data(
|
||||
'ads-links',
|
||||
array( 'propertyID' => $property_id )
|
||||
);
|
||||
|
||||
if ( is_wp_error( $property_ads_links ) || ! is_array( $property_ads_links ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update the adsLinked status and timestamp.
|
||||
$this->analytics_4->get_settings()->merge(
|
||||
array(
|
||||
'adsLinked' => ! empty( $property_ads_links ),
|
||||
'adsLinkedLastSyncedAt' => time(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules single cron which will synchronize the adsLinked status.
|
||||
*
|
||||
* @since 1.124.0
|
||||
*/
|
||||
public function maybe_schedule_synchronize_ads_linked() {
|
||||
$analytics_4_connected = $this->analytics_4->is_connected();
|
||||
$cron_already_scheduled = wp_next_scheduled( self::CRON_SYNCHRONIZE_ADS_LINKED );
|
||||
|
||||
if ( $analytics_4_connected && ! $cron_already_scheduled ) {
|
||||
wp_schedule_single_event(
|
||||
// Schedule the task to run in 24 hours.
|
||||
time() + ( DAY_IN_SECONDS ),
|
||||
self::CRON_SYNCHRONIZE_ADS_LINKED
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Synchronize_Property
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\Storage\User_Options;
|
||||
use Google\Site_Kit\Modules\Analytics_4;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaProperty;
|
||||
|
||||
/**
|
||||
* The base class for Synchronizing the Analytics 4 property.
|
||||
*
|
||||
* @since 1.116.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Synchronize_Property {
|
||||
|
||||
const CRON_SYNCHRONIZE_PROPERTY = 'googlesitekit_cron_synchronize_property_data';
|
||||
|
||||
/**
|
||||
* Analytics_4 instance.
|
||||
*
|
||||
* @since 1.116.0
|
||||
* @var Analytics_4
|
||||
*/
|
||||
protected $analytics_4;
|
||||
|
||||
/**
|
||||
* User_Options instance.
|
||||
*
|
||||
* @since 1.116.0
|
||||
* @var User_Options
|
||||
*/
|
||||
protected $user_options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*
|
||||
* @param Analytics_4 $analytics_4 Analytics 4 instance.
|
||||
* @param User_Options $user_options User_Options instance.
|
||||
*/
|
||||
public function __construct( Analytics_4 $analytics_4, User_Options $user_options ) {
|
||||
$this->analytics_4 = $analytics_4;
|
||||
$this->user_options = $user_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*/
|
||||
public function register() {
|
||||
|
||||
add_action(
|
||||
self::CRON_SYNCHRONIZE_PROPERTY,
|
||||
function() {
|
||||
$this->synchronize_property_data();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron callback for synchronizing the property.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*/
|
||||
protected function synchronize_property_data() {
|
||||
$owner_id = $this->analytics_4->get_owner_id();
|
||||
$restore_user = $this->user_options->switch_user( $owner_id );
|
||||
|
||||
if ( user_can( $owner_id, Permissions::VIEW_AUTHENTICATED_DASHBOARD ) ) {
|
||||
$property = $this->retrieve_property();
|
||||
|
||||
$this->synchronize_property_create_time( $property );
|
||||
}
|
||||
|
||||
$restore_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules single cron which will synchronize the property data.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*/
|
||||
public function maybe_schedule_synchronize_property() {
|
||||
$settings = $this->analytics_4->get_settings()->get();
|
||||
|
||||
$create_time_has_value = (bool) $settings['propertyCreateTime'];
|
||||
$analytics_4_connected = $this->analytics_4->is_connected();
|
||||
$cron_already_scheduled = wp_next_scheduled( self::CRON_SYNCHRONIZE_PROPERTY );
|
||||
|
||||
if ( ! $create_time_has_value && $analytics_4_connected && ! $cron_already_scheduled ) {
|
||||
wp_schedule_single_event(
|
||||
// Schedule the task to run in 30 minutes.
|
||||
time() + ( 30 * MINUTE_IN_SECONDS ),
|
||||
self::CRON_SYNCHRONIZE_PROPERTY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Analytics 4 property.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*
|
||||
* @return GoogleAnalyticsAdminV1betaProperty|null $property Analytics 4 property object, or null if property is not found.
|
||||
*/
|
||||
protected function retrieve_property() {
|
||||
$settings = $this->analytics_4->get_settings()->get();
|
||||
|
||||
$property_id = $settings['propertyID'];
|
||||
$property = $this->analytics_4->get_data( 'property', array( 'propertyID' => $property_id ) );
|
||||
|
||||
if ( is_wp_error( $property ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize the property create time data.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*
|
||||
* @param GoogleAnalyticsAdminV1betaProperty|null $property Analytics 4 property object, or null if property is not found.
|
||||
*/
|
||||
protected function synchronize_property_create_time( $property ) {
|
||||
if ( ! $property ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$create_time_ms = self::convert_time_to_unix_ms( $property->createTime ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
|
||||
$this->analytics_4->get_settings()->merge(
|
||||
array(
|
||||
'propertyCreateTime' => $create_time_ms,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to Unix timestamp and then to milliseconds.
|
||||
*
|
||||
* @since 1.116.0
|
||||
*
|
||||
* @param string $date_time Date in date-time format.
|
||||
*/
|
||||
public static function convert_time_to_unix_ms( $date_time ) {
|
||||
$date_time_object = new \DateTime( $date_time, new \DateTimeZone( 'UTC' ) );
|
||||
|
||||
return (int) ( $date_time_object->getTimestamp() * 1000 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Tag_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard;
|
||||
|
||||
/**
|
||||
* Class for the Analytics 4 tag guard.
|
||||
*
|
||||
* @since 1.31.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Guard extends Module_Tag_Guard {
|
||||
|
||||
/**
|
||||
* Determines whether the guarded tag can be activated or not.
|
||||
*
|
||||
* @since 1.31.0
|
||||
*
|
||||
* @return bool|WP_Error TRUE if guarded tag can be activated, otherwise FALSE or an error.
|
||||
*/
|
||||
public function can_activate() {
|
||||
$settings = $this->settings->get();
|
||||
return ! empty( $settings['useSnippet'] ) && ! empty( $settings['measurementID'] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Tag_Interface
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2023 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Analytics_4;
|
||||
|
||||
/**
|
||||
* Interface for an Analytics 4 tag.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
interface Tag_Interface {
|
||||
/**
|
||||
* Sets the ads conversion ID.
|
||||
*
|
||||
* @since 1.118.0
|
||||
*
|
||||
* @param string $ads_conversion_id Ads ID.
|
||||
*/
|
||||
public function set_ads_conversion_id( $ads_conversion_id );
|
||||
|
||||
/**
|
||||
* Sets custom dimensions data.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimensions Custom dimensions data.
|
||||
*/
|
||||
public function set_custom_dimensions( $custom_dimensions );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Analytics_4\Tag_Matchers
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules\Analytics_4
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Analytics_4;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
use Google\Site_Kit\Core\Tags\Tag_Matchers_Interface;
|
||||
|
||||
/**
|
||||
* Class for Tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Matchers extends Module_Tag_Matchers implements Tag_Matchers_Interface {
|
||||
|
||||
/**
|
||||
* Holds array of regex tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @return array Array of regex matchers.
|
||||
*/
|
||||
public function regex_matchers() {
|
||||
$tag_matchers = array(
|
||||
"/__gaTracker\s*\(\s*['|\"]create['|\"]\s*,\s*['|\"](G-[a-zA-Z0-9]+)['|\"]\, ?['|\"]auto['|\"]\s*\)/i",
|
||||
"/_gaq\.push\s*\(\s*\[\s*['|\"][^_]*_setAccount['|\"]\s*,\s*['|\"](G-[a-zA-Z0-9]+)['|\"]\s*],?\s*\)/i",
|
||||
'/<amp-analytics\s+[^>]*type="gtag"[^>]*>[^<]*<script\s+type="application\/json">[^<]*"gtag_id"\s*:\s*"(G-[a-zA-Z0-9]+)"/i',
|
||||
'/<amp-analytics\s+[^>]*type="googleanalytics"[^>]*>[^<]*<script\s+type="application\/json">[^<]*"account"\s*:\s*"(G-[a-zA-Z0-9]+)"/i',
|
||||
);
|
||||
|
||||
$subdomains = array( '', 'www\\.' );
|
||||
foreach ( $subdomains as $subdomain ) {
|
||||
$tag_matchers[] = "/<script\\s+[^>]*src=['|\"]https?:\\/\\/" . $subdomain . "googletagmanager\\.com\\/gtag\\/js\\?id=(G-[a-zA-Z0-9]+)['|\"][^>]*><\\/script>/i";
|
||||
$tag_matchers[] = "/<script\\s+[^>]*src=['|\"]https?:\/\/" . $subdomain . "googletagmanager\\.com\\/gtag\\/js\\?id=(G-[a-zA-Z0-9]+)['|\"][^\\/]*\/>/i";
|
||||
}
|
||||
|
||||
$funcs = array( '__gaTracker', 'ga', 'gtag' );
|
||||
foreach ( $funcs as $func ) {
|
||||
$tag_matchers[] = "/$func\\s*\\(\\s*['|\"]create['|\"]\\s*,\\s*['|\"](G-[a-zA-Z0-9]+)['|\"]\\,\\s*['|\"]auto['|\"]\\s*\\)/i";
|
||||
$tag_matchers[] = "/$func\\s*\\(\\s*['|\"]config['|\"]\\s*,\\s*['|\"](G-[a-zA-Z0-9]+)['|\"]\\s*\\)/i";
|
||||
$tag_matchers[] = "/$func\\s*\\(\\s*['|\"]config['|\"]\\s*,\\s*['|\"](GT-[a-zA-Z0-9]+)['|\"]\\s*\\)/i";
|
||||
}
|
||||
|
||||
return $tag_matchers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Analytics_4\Web_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Analytics_4
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
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\Tags\Tag_With_Linker_Trait;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_Linker_Interface;
|
||||
|
||||
/**
|
||||
* Class for Web tag.
|
||||
*
|
||||
* @since 1.31.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Web_Tag extends Module_Web_Tag implements Tag_Interface, Tag_With_Linker_Interface {
|
||||
|
||||
use Method_Proxy_Trait, Tag_With_DNS_Prefetch_Trait, Tag_With_Linker_Trait;
|
||||
|
||||
/**
|
||||
* Custom dimensions data.
|
||||
*
|
||||
* @since 1.113.0
|
||||
* @var array
|
||||
*/
|
||||
private $custom_dimensions;
|
||||
|
||||
/**
|
||||
* Ads conversion ID.
|
||||
*
|
||||
* @since 1.32.0
|
||||
* @var string
|
||||
*/
|
||||
private $ads_conversion_id;
|
||||
|
||||
/**
|
||||
* Sets custom dimensions data.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @param string $custom_dimensions Custom dimensions data.
|
||||
*/
|
||||
public function set_custom_dimensions( $custom_dimensions ) {
|
||||
$this->custom_dimensions = $custom_dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current home domain.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param string $domain Domain name.
|
||||
*/
|
||||
public function set_home_domain( $domain ) {
|
||||
$this->home_domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ads conversion ID.
|
||||
*
|
||||
* @since 1.32.0
|
||||
*
|
||||
* @param string $ads_conversion_id Ads ID.
|
||||
*/
|
||||
public function set_ads_conversion_id( $ads_conversion_id ) {
|
||||
$this->ads_conversion_id = $ads_conversion_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets args to use if blocked_on_consent is deprecated.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*
|
||||
* @return array args to pass to apply_filters_deprecated if deprecated ($version, $replacement, $message)
|
||||
*/
|
||||
protected function get_tag_blocked_on_consent_deprecated_args() {
|
||||
return array(
|
||||
'1.122.0', // Deprecated in this version.
|
||||
'',
|
||||
__( 'Please use the Consent Mode feature instead.', 'google-site-kit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.31.0
|
||||
*/
|
||||
public function register() {
|
||||
add_action( 'googlesitekit_setup_gtag', $this->get_method_proxy( 'setup_gtag' ) );
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs gtag snippet.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function render() {
|
||||
// Do nothing, gtag script is enqueued.
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures gtag script.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @since 1.124.0 Renamed and refactored to use new GTag infrastructure.
|
||||
*
|
||||
* @param GTag $gtag GTag instance.
|
||||
*/
|
||||
protected function setup_gtag( GTag $gtag ) {
|
||||
$gtag_opt = $this->get_tag_config();
|
||||
|
||||
/**
|
||||
* Filters the gtag configuration options for the Analytics snippet.
|
||||
*
|
||||
* You can use the {@see 'googlesitekit_amp_gtag_opt'} filter to do the same for gtag in AMP.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @see https://developers.google.com/gtagjs/devguide/configure
|
||||
*
|
||||
* @param array $gtag_opt gtag config options.
|
||||
*/
|
||||
$gtag_opt = apply_filters( 'googlesitekit_gtag_opt', $gtag_opt );
|
||||
|
||||
if ( ! empty( $gtag_opt['linker'] ) ) {
|
||||
$gtag->add_command( 'set', array( 'linker', $gtag_opt['linker'] ) );
|
||||
|
||||
unset( $gtag_opt['linker'] );
|
||||
}
|
||||
|
||||
$gtag->add_tag( $this->tag_id, $gtag_opt );
|
||||
|
||||
// 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 ( $gtag ) {
|
||||
if ( GTag::HANDLE !== $handle ) {
|
||||
return $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' ) );
|
||||
|
||||
$block_on_consent_attrs = $this->get_tag_blocked_on_consent_attribute();
|
||||
|
||||
if ( $block_on_consent_attrs ) {
|
||||
$gtag_src = $gtag->get_gtag_src();
|
||||
|
||||
$tag = $this->add_legacy_block_on_consent_attributes( $tag, $gtag_src, $block_on_consent_attrs );
|
||||
}
|
||||
|
||||
return $snippet_comment . $tag;
|
||||
};
|
||||
|
||||
add_filter( 'script_loader_tag', $filter_google_gtagjs, 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag config as used in the gtag data vars.
|
||||
*
|
||||
* @since 1.113.0
|
||||
*
|
||||
* @return array Tag configuration.
|
||||
*/
|
||||
protected function get_tag_config() {
|
||||
$config = array();
|
||||
|
||||
if ( ! empty( $this->custom_dimensions ) ) {
|
||||
$config = array_merge( $config, $this->custom_dimensions );
|
||||
}
|
||||
|
||||
return $this->add_linker_to_tag_config( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds HTML attributes to the gtag script tag to block it until user consent is granted.
|
||||
*
|
||||
* This mechanism for blocking the tag is deprecated and the Consent Mode feature should be used instead.
|
||||
*
|
||||
* @since 1.122.0
|
||||
*
|
||||
* @param string $tag The script tag.
|
||||
* @param string $gtag_src The gtag script source URL.
|
||||
* @param string $block_on_consent_attrs The attributes to add to the script tag to block it until user consent is granted.
|
||||
* @return string The script tag with the added attributes.
|
||||
*/
|
||||
protected function add_legacy_block_on_consent_attributes( $tag, $gtag_src, $block_on_consent_attrs ) {
|
||||
return 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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\PageSpeed_Insights
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Assets\Script;
|
||||
use Google\Site_Kit\Core\Modules\Module;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Deactivation;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Owner;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Owner_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait;
|
||||
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
|
||||
use Google\Site_Kit\Core\Authentication\Clients\Google_Site_Kit_Client;
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Modules\PageSpeed_Insights\Settings;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\PagespeedInsights as Google_Service_PagespeedInsights;
|
||||
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class representing the PageSpeed Insights module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class PageSpeed_Insights extends Module
|
||||
implements Module_With_Scopes, Module_With_Assets, Module_With_Deactivation, Module_With_Settings, Module_With_Owner {
|
||||
use Module_With_Scopes_Trait, Module_With_Assets_Trait, Module_With_Settings_Trait, Module_With_Owner_Trait;
|
||||
|
||||
/**
|
||||
* Module slug name.
|
||||
*/
|
||||
const MODULE_SLUG = 'pagespeed-insights';
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register() {}
|
||||
|
||||
/**
|
||||
* Cleans up when the module is deactivated.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function on_deactivation() {
|
||||
$this->get_settings()->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of datapoint to definition data for each.
|
||||
*
|
||||
* @since 1.12.0
|
||||
*
|
||||
* @return array Map of datapoints to their definitions.
|
||||
*/
|
||||
protected function get_datapoint_definitions() {
|
||||
return array(
|
||||
'GET:pagespeed' => array(
|
||||
'service' => 'pagespeedonline',
|
||||
'shareable' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure.
|
||||
*
|
||||
* @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist.
|
||||
*/
|
||||
protected function create_data_request( Data_Request $data ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
case 'GET:pagespeed':
|
||||
if ( empty( $data['strategy'] ) ) {
|
||||
return new WP_Error(
|
||||
'missing_required_param',
|
||||
sprintf(
|
||||
/* translators: %s: Missing parameter name */
|
||||
__( 'Request parameter is empty: %s.', 'google-site-kit' ),
|
||||
'strategy'
|
||||
),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$valid_strategies = array( 'mobile', 'desktop' );
|
||||
|
||||
if ( ! in_array( $data['strategy'], $valid_strategies, true ) ) {
|
||||
return new WP_Error(
|
||||
'invalid_param',
|
||||
sprintf(
|
||||
/* translators: 1: Invalid parameter name, 2: list of valid values */
|
||||
__( 'Request parameter %1$s is not one of %2$s', 'google-site-kit' ),
|
||||
'strategy',
|
||||
implode( ', ', $valid_strategies )
|
||||
),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $data['url'] ) ) {
|
||||
$page_url = $data['url'];
|
||||
} else {
|
||||
$page_url = $this->context->get_reference_site_url();
|
||||
}
|
||||
|
||||
$service = $this->get_service( 'pagespeedonline' );
|
||||
|
||||
return $service->pagespeedapi->runpagespeed(
|
||||
$page_url,
|
||||
array(
|
||||
'locale' => $this->context->get_locale( 'site', 'language-code' ),
|
||||
'strategy' => $data['strategy'],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return parent::create_data_request( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's assets to register.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
protected function setup_assets() {
|
||||
$base_url = $this->context->url( 'dist/assets/' );
|
||||
|
||||
return array(
|
||||
new Script(
|
||||
'googlesitekit-modules-pagespeed-insights',
|
||||
array(
|
||||
'src' => $base_url . 'js/googlesitekit-modules-pagespeed-insights.js',
|
||||
'dependencies' => array(
|
||||
'googlesitekit-vendor',
|
||||
'googlesitekit-api',
|
||||
'googlesitekit-data',
|
||||
'googlesitekit-modules',
|
||||
'googlesitekit-datastore-site',
|
||||
'googlesitekit-components',
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up information about the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array Associative array of module info.
|
||||
*/
|
||||
protected function setup_info() {
|
||||
return array(
|
||||
'slug' => 'pagespeed-insights',
|
||||
'name' => _x( 'PageSpeed Insights', 'Service name', 'google-site-kit' ),
|
||||
'description' => __( 'Google PageSpeed Insights gives you metrics about performance, accessibility, SEO and PWA', 'google-site-kit' ),
|
||||
'order' => 4,
|
||||
'homepage' => __( 'https://pagespeed.web.dev', 'google-site-kit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's settings instance.
|
||||
*
|
||||
* @since 1.49.0
|
||||
*
|
||||
* @return Module_Settings
|
||||
*/
|
||||
protected function setup_settings() {
|
||||
return new Settings( $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the Google services the module should use.
|
||||
*
|
||||
* This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested
|
||||
* for the first time.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0 Now requires Google_Site_Kit_Client instance.
|
||||
*
|
||||
* @param Google_Site_Kit_Client $client Google client instance.
|
||||
* @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an
|
||||
* instance of Google_Service.
|
||||
*/
|
||||
protected function setup_services( Google_Site_Kit_Client $client ) {
|
||||
return array(
|
||||
'pagespeedonline' => new Google_Service_PagespeedInsights( $client ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets required Google OAuth scopes for the module.
|
||||
*
|
||||
* @return array List of Google OAuth scopes.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function get_scopes() {
|
||||
return array(
|
||||
'openid',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\PageSpeed_Insights\Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\PageSpeed_Insights
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\PageSpeed_Insights;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
|
||||
/**
|
||||
* Class for PageSpeed Insights settings.
|
||||
*
|
||||
* @since 1.49.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Settings extends Module_Settings {
|
||||
|
||||
const OPTION = 'googlesitekit_pagespeed-insights_settings';
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.49.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array( 'ownerID' => 0 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Search_Console
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Assets\Script;
|
||||
use Google\Site_Kit\Core\Authentication\Clients\Google_Site_Kit_Client;
|
||||
use Google\Site_Kit\Core\Modules\Module;
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Debug_Fields;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Owner;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Owner_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Service_Entity;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Data_Available_State;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Data_Available_State_Trait;
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
|
||||
use Google\Site_Kit\Core\Util\Date;
|
||||
use Google\Site_Kit\Core\Util\Google_URL_Matcher_Trait;
|
||||
use Google\Site_Kit\Core\Util\Google_URL_Normalizer;
|
||||
use Google\Site_Kit\Core\Util\Sort;
|
||||
use Google\Site_Kit\Modules\Search_Console\Settings;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\Exception as Google_Service_Exception;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SearchConsole as Google_Service_SearchConsole;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SearchConsole\SitesListResponse as Google_Service_SearchConsole_SitesListResponse;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SearchConsole\WmxSite as Google_Service_SearchConsole_WmxSite;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SearchConsole\SearchAnalyticsQueryRequest as Google_Service_SearchConsole_SearchAnalyticsQueryRequest;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SearchConsole\ApiDimensionFilter as Google_Service_SearchConsole_ApiDimensionFilter;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SearchConsole\ApiDimensionFilterGroup as Google_Service_SearchConsole_ApiDimensionFilterGroup;
|
||||
use Google\Site_Kit_Dependencies\Psr\Http\Message\ResponseInterface;
|
||||
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface;
|
||||
use WP_Error;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class representing the Search Console module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Search_Console extends Module
|
||||
implements Module_With_Scopes, Module_With_Settings, Module_With_Assets, Module_With_Debug_Fields, Module_With_Owner, Module_With_Service_Entity, Module_With_Data_Available_State {
|
||||
use Module_With_Scopes_Trait, Module_With_Settings_Trait, Google_URL_Matcher_Trait, Module_With_Assets_Trait, Module_With_Owner_Trait, Module_With_Data_Available_State_Trait;
|
||||
|
||||
/**
|
||||
* Module slug name.
|
||||
*/
|
||||
const MODULE_SLUG = 'search-console';
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register() {
|
||||
$this->register_scopes_hook();
|
||||
|
||||
// Detect and store Search Console property when receiving token for the first time.
|
||||
add_action(
|
||||
'googlesitekit_authorize_user',
|
||||
function( array $token_response ) {
|
||||
if ( ! current_user_can( Permissions::SETUP ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the response includes the Search Console property, set that.
|
||||
// But only if it is being set for the first time or if Search Console
|
||||
// has no owner or the current user is the owner.
|
||||
if ( ! empty( $token_response['search_console_property'] ) &&
|
||||
( empty( $this->get_property_id() ) || ( in_array( $this->get_owner_id(), array( 0, get_current_user_id() ), true ) ) ) ) {
|
||||
$this->get_settings()->merge(
|
||||
array( 'propertyID' => $token_response['search_console_property'] )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise try to detect if there isn't one set already.
|
||||
$property_id = $this->get_property_id() ?: $this->detect_property_id();
|
||||
if ( ! $property_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->get_settings()->merge(
|
||||
array( 'propertyID' => $property_id )
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// Ensure that the data available state is reset when the property changes.
|
||||
$this->get_settings()->on_change(
|
||||
function( $old_value, $new_value ) {
|
||||
if (
|
||||
is_array( $old_value ) &&
|
||||
is_array( $new_value ) &&
|
||||
isset( array_diff_assoc( $new_value, $old_value )['propertyID'] )
|
||||
) {
|
||||
$this->reset_data_available();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Ensure that a Search Console property must be set at all times.
|
||||
add_filter(
|
||||
'googlesitekit_setup_complete',
|
||||
function( $complete ) {
|
||||
if ( ! $complete ) {
|
||||
return $complete;
|
||||
}
|
||||
|
||||
return (bool) $this->get_property_id();
|
||||
}
|
||||
);
|
||||
|
||||
// Provide Search Console property information to JavaScript.
|
||||
add_filter(
|
||||
'googlesitekit_setup_data',
|
||||
function ( $data ) {
|
||||
$data['hasSearchConsoleProperty'] = (bool) $this->get_property_id();
|
||||
|
||||
return $data;
|
||||
},
|
||||
11
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets required Google OAuth scopes for the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array List of Google OAuth scopes.
|
||||
*/
|
||||
public function get_scopes() {
|
||||
return array(
|
||||
'https://www.googleapis.com/auth/webmasters', // The scope for the Search Console remains the legacy webmasters scope.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of debug field definitions.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_debug_fields() {
|
||||
return array(
|
||||
'search_console_property' => array(
|
||||
'label' => __( 'Search Console property', 'google-site-kit' ),
|
||||
'value' => $this->get_property_id(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of datapoint to definition data for each.
|
||||
*
|
||||
* @since 1.12.0
|
||||
*
|
||||
* @return array Map of datapoints to their definitions.
|
||||
*/
|
||||
protected function get_datapoint_definitions() {
|
||||
return array(
|
||||
'GET:matched-sites' => array( 'service' => 'searchconsole' ),
|
||||
'GET:searchanalytics' => array(
|
||||
'service' => 'searchconsole',
|
||||
'shareable' => true,
|
||||
),
|
||||
'POST:site' => array( 'service' => 'searchconsole' ),
|
||||
'GET:sites' => array( 'service' => 'searchconsole' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure.
|
||||
*
|
||||
* @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist.
|
||||
*/
|
||||
protected function create_data_request( Data_Request $data ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
case 'GET:matched-sites':
|
||||
return $this->get_searchconsole_service()->sites->listSites();
|
||||
case 'GET:searchanalytics':
|
||||
$start_date = $data['startDate'];
|
||||
$end_date = $data['endDate'];
|
||||
if ( ! strtotime( $start_date ) || ! strtotime( $end_date ) ) {
|
||||
list ( $start_date, $end_date ) = Date::parse_date_range( 'last-28-days', 1, 1 );
|
||||
}
|
||||
|
||||
$data_request = array(
|
||||
'start_date' => $start_date,
|
||||
'end_date' => $end_date,
|
||||
);
|
||||
|
||||
if ( ! empty( $data['url'] ) ) {
|
||||
$data_request['page'] = ( new Google_URL_Normalizer() )->normalize_url( $data['url'] );
|
||||
}
|
||||
|
||||
if ( isset( $data['limit'] ) ) {
|
||||
$data_request['row_limit'] = $data['limit'];
|
||||
}
|
||||
|
||||
$dimensions = $this->parse_string_list( $data['dimensions'] );
|
||||
if ( is_array( $dimensions ) && ! empty( $dimensions ) ) {
|
||||
$data_request['dimensions'] = $dimensions;
|
||||
}
|
||||
|
||||
return $this->create_search_analytics_data_request( $data_request );
|
||||
case 'POST:site':
|
||||
if ( empty( $data['siteURL'] ) ) {
|
||||
return new WP_Error(
|
||||
'missing_required_param',
|
||||
/* translators: %s: Missing parameter name */
|
||||
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'siteURL' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$url_normalizer = new Google_URL_Normalizer();
|
||||
|
||||
$site_url = $data['siteURL'];
|
||||
if ( 0 === strpos( $site_url, 'sc-domain:' ) ) { // Domain property.
|
||||
$site_url = 'sc-domain:' . $url_normalizer->normalize_url( str_replace( 'sc-domain:', '', $site_url, 1 ) );
|
||||
} else { // URL property.
|
||||
$site_url = $url_normalizer->normalize_url( trailingslashit( $site_url ) );
|
||||
}
|
||||
|
||||
return function () use ( $site_url ) {
|
||||
$restore_defer = $this->with_client_defer( false );
|
||||
|
||||
try {
|
||||
// If the site does not exist in the account, an exception will be thrown.
|
||||
$site = $this->get_searchconsole_service()->sites->get( $site_url );
|
||||
} catch ( Google_Service_Exception $exception ) {
|
||||
// If we got here, the site does not exist in the account, so we will add it.
|
||||
/* @var ResponseInterface $response Response object. */
|
||||
$response = $this->get_searchconsole_service()->sites->add( $site_url );
|
||||
|
||||
if ( 204 !== $response->getStatusCode() ) {
|
||||
return new WP_Error(
|
||||
'failed_to_add_site_to_search_console',
|
||||
__( 'Error adding the site to Search Console.', 'google-site-kit' ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch the site again now that it exists.
|
||||
$site = $this->get_searchconsole_service()->sites->get( $site_url );
|
||||
}
|
||||
|
||||
$restore_defer();
|
||||
$this->get_settings()->merge( array( 'propertyID' => $site_url ) );
|
||||
|
||||
return array(
|
||||
'siteURL' => $site->getSiteUrl(),
|
||||
'permissionLevel' => $site->getPermissionLevel(),
|
||||
);
|
||||
};
|
||||
case 'GET:sites':
|
||||
return $this->get_searchconsole_service()->sites->listSites();
|
||||
}
|
||||
|
||||
return parent::create_data_request( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a response for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @param mixed $response Request response.
|
||||
*
|
||||
* @return mixed Parsed response data on success, or WP_Error on failure.
|
||||
*/
|
||||
protected function parse_data_response( Data_Request $data, $response ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
case 'GET:matched-sites':
|
||||
/* @var Google_Service_SearchConsole_SitesListResponse $response Response object. */
|
||||
$entries = Sort::case_insensitive_list_sort(
|
||||
$this->map_sites( (array) $response->getSiteEntry() ),
|
||||
'name'
|
||||
);
|
||||
$strict = filter_var( $data['strict'], FILTER_VALIDATE_BOOLEAN );
|
||||
$current_url = $this->context->get_reference_site_url();
|
||||
if ( ! $strict ) {
|
||||
$current_url = untrailingslashit( $current_url );
|
||||
$current_url = $this->strip_url_scheme( $current_url );
|
||||
$current_url = $this->strip_domain_www( $current_url );
|
||||
}
|
||||
|
||||
$sufficient_permission_levels = array(
|
||||
'siteRestrictedUser',
|
||||
'siteOwner',
|
||||
'siteFullUser',
|
||||
);
|
||||
|
||||
return array_values(
|
||||
array_filter(
|
||||
$entries,
|
||||
function ( array $entry ) use ( $current_url, $sufficient_permission_levels, $strict ) {
|
||||
if ( 0 === strpos( $entry['siteURL'], 'sc-domain:' ) ) {
|
||||
$match = $this->is_domain_match( substr( $entry['siteURL'], strlen( 'sc-domain:' ) ), $current_url );
|
||||
} else {
|
||||
$site_url = untrailingslashit( $entry['siteURL'] );
|
||||
if ( ! $strict ) {
|
||||
$site_url = $this->strip_url_scheme( $site_url );
|
||||
$site_url = $this->strip_domain_www( $site_url );
|
||||
}
|
||||
|
||||
$match = $this->is_url_match( $site_url, $current_url );
|
||||
}
|
||||
return $match && in_array( $entry['permissionLevel'], $sufficient_permission_levels, true );
|
||||
}
|
||||
)
|
||||
);
|
||||
case 'GET:searchanalytics':
|
||||
return $response->getRows();
|
||||
case 'GET:sites':
|
||||
/* @var Google_Service_SearchConsole_SitesListResponse $response Response object. */
|
||||
return $this->map_sites( (array) $response->getSiteEntry() );
|
||||
}
|
||||
|
||||
return parent::parse_data_response( $data, $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Site model objects to associative arrays used for API responses.
|
||||
*
|
||||
* @param array $sites Site objects.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function map_sites( $sites ) {
|
||||
return array_map(
|
||||
function ( Google_Service_SearchConsole_WmxSite $site ) {
|
||||
return array(
|
||||
'siteURL' => $site->getSiteUrl(),
|
||||
'permissionLevel' => $site->getPermissionLevel(),
|
||||
);
|
||||
},
|
||||
$sites
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Search Console analytics request for the current site and given arguments.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. Additional arguments.
|
||||
*
|
||||
* @type array $dimensions List of request dimensions. Default empty array.
|
||||
* @type string $start_date Start date in 'Y-m-d' format. Default empty string.
|
||||
* @type string $end_date End date in 'Y-m-d' format. Default empty string.
|
||||
* @type string $page Specific page URL to filter by. Default empty string.
|
||||
* @type int $row_limit Limit of rows to return. Default 1000.
|
||||
* }
|
||||
* @return RequestInterface Search Console analytics request instance.
|
||||
*/
|
||||
protected function create_search_analytics_data_request( array $args = array() ) {
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'dimensions' => array(),
|
||||
'start_date' => '',
|
||||
'end_date' => '',
|
||||
'page' => '',
|
||||
'row_limit' => 1000,
|
||||
)
|
||||
);
|
||||
|
||||
$property_id = $this->get_property_id();
|
||||
|
||||
$request = new Google_Service_SearchConsole_SearchAnalyticsQueryRequest();
|
||||
if ( ! empty( $args['dimensions'] ) ) {
|
||||
$request->setDimensions( (array) $args['dimensions'] );
|
||||
}
|
||||
if ( ! empty( $args['start_date'] ) ) {
|
||||
$request->setStartDate( $args['start_date'] );
|
||||
}
|
||||
if ( ! empty( $args['end_date'] ) ) {
|
||||
$request->setEndDate( $args['end_date'] );
|
||||
}
|
||||
|
||||
$request->setDataState( 'all' );
|
||||
|
||||
$filters = array();
|
||||
|
||||
// If domain property, limit data to URLs that are part of the current site.
|
||||
if ( 0 === strpos( $property_id, 'sc-domain:' ) ) {
|
||||
$scope_site_filter = new Google_Service_SearchConsole_ApiDimensionFilter();
|
||||
$scope_site_filter->setDimension( 'page' );
|
||||
$scope_site_filter->setOperator( 'contains' );
|
||||
$scope_site_filter->setExpression( esc_url_raw( $this->context->get_reference_site_url() ) );
|
||||
$filters[] = $scope_site_filter;
|
||||
}
|
||||
|
||||
// If specific URL requested, limit data to that URL.
|
||||
if ( ! empty( $args['page'] ) ) {
|
||||
$single_url_filter = new Google_Service_SearchConsole_ApiDimensionFilter();
|
||||
$single_url_filter->setDimension( 'page' );
|
||||
$single_url_filter->setOperator( 'equals' );
|
||||
$single_url_filter->setExpression( rawurldecode( esc_url_raw( $args['page'] ) ) );
|
||||
$filters[] = $single_url_filter;
|
||||
}
|
||||
|
||||
// If there are relevant filters, add them to the request.
|
||||
if ( ! empty( $filters ) ) {
|
||||
$filter_group = new Google_Service_SearchConsole_ApiDimensionFilterGroup();
|
||||
$filter_group->setGroupType( 'and' );
|
||||
$filter_group->setFilters( $filters );
|
||||
$request->setDimensionFilterGroups( array( $filter_group ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['row_limit'] ) ) {
|
||||
$request->setRowLimit( $args['row_limit'] );
|
||||
}
|
||||
|
||||
return $this->get_searchconsole_service()
|
||||
->searchanalytics
|
||||
->query( $property_id, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property ID.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @return string Property ID URL if set, or empty string.
|
||||
*/
|
||||
protected function get_property_id() {
|
||||
$option = $this->get_settings()->get();
|
||||
|
||||
return $option['propertyID'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the property ID to use for this site.
|
||||
*
|
||||
* This method runs a Search Console API request. The determined ID should therefore be stored and accessed through
|
||||
* {@see Search_Console::get_property_id()} instead.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @return string Property ID, or empty string if none found.
|
||||
*/
|
||||
protected function detect_property_id() {
|
||||
$properties = $this->get_data( 'matched-sites', array( 'strict' => 'yes' ) );
|
||||
if ( is_wp_error( $properties ) || ! $properties ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// If there are multiple, prefer URL property over domain property.
|
||||
if ( count( $properties ) > 1 ) {
|
||||
$url_properties = array_filter(
|
||||
$properties,
|
||||
function( $property ) {
|
||||
return 0 !== strpos( $property['siteURL'], 'sc-domain:' );
|
||||
}
|
||||
);
|
||||
if ( count( $url_properties ) > 0 ) {
|
||||
$properties = $url_properties;
|
||||
}
|
||||
}
|
||||
|
||||
$property = array_shift( $properties );
|
||||
return $property['siteURL'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up information about the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array Associative array of module info.
|
||||
*/
|
||||
protected function setup_info() {
|
||||
return array(
|
||||
'slug' => 'search-console',
|
||||
'name' => _x( 'Search Console', 'Service name', 'google-site-kit' ),
|
||||
'description' => __( 'Google Search Console and helps you understand how Google views your site and optimize its performance in search results.', 'google-site-kit' ),
|
||||
'order' => 1,
|
||||
'homepage' => __( 'https://search.google.com/search-console', 'google-site-kit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured SearchConsole service instance.
|
||||
*
|
||||
* @since 1.25.0
|
||||
*
|
||||
* @return Google_Service_SearchConsole The Search Console API service.
|
||||
*/
|
||||
private function get_searchconsole_service() {
|
||||
return $this->get_service( 'searchconsole' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the Google services the module should use.
|
||||
*
|
||||
* This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested
|
||||
* for the first time.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0 Now requires Google_Site_Kit_Client instance.
|
||||
*
|
||||
* @param Google_Site_Kit_Client $client Google client instance.
|
||||
* @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an
|
||||
* instance of Google_Service.
|
||||
*/
|
||||
protected function setup_services( Google_Site_Kit_Client $client ) {
|
||||
return array(
|
||||
'searchconsole' => new Google_Service_SearchConsole( $client ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's settings instance.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @return Module_Settings
|
||||
*/
|
||||
protected function setup_settings() {
|
||||
return new Settings( $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's assets to register.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
protected function setup_assets() {
|
||||
$base_url = $this->context->url( 'dist/assets/' );
|
||||
|
||||
return array(
|
||||
new Script(
|
||||
'googlesitekit-modules-search-console',
|
||||
array(
|
||||
'src' => $base_url . 'js/googlesitekit-modules-search-console.js',
|
||||
'dependencies' => array(
|
||||
'googlesitekit-vendor',
|
||||
'googlesitekit-api',
|
||||
'googlesitekit-data',
|
||||
'googlesitekit-datastore-user',
|
||||
'googlesitekit-modules',
|
||||
'googlesitekit-components',
|
||||
'googlesitekit-modules-data',
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE to indicate that this module should be always active.
|
||||
*
|
||||
* @since 1.49.0
|
||||
*
|
||||
* @return bool Returns `true` indicating that this module should be activated all the time.
|
||||
*/
|
||||
public static function is_force_active() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has access to the current configured service entity.
|
||||
*
|
||||
* @since 1.70.0
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public function check_service_entity_access() {
|
||||
$data_request = array(
|
||||
'start_date' => gmdate( 'Y-m-d' ),
|
||||
'end_date' => gmdate( 'Y-m-d' ),
|
||||
'row_limit' => 1,
|
||||
);
|
||||
|
||||
try {
|
||||
$this->create_search_analytics_data_request( $data_request );
|
||||
} catch ( Exception $e ) {
|
||||
if ( $e->getCode() === 403 ) {
|
||||
return false;
|
||||
}
|
||||
return $this->exception_to_error( $e );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Search_Console\Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Search_Console
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Search_Console;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Interface;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Trait;
|
||||
|
||||
/**
|
||||
* Class for Search Console settings.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Settings extends Module_Settings implements Setting_With_Owned_Keys_Interface {
|
||||
use Setting_With_Owned_Keys_Trait;
|
||||
|
||||
const OPTION = 'googlesitekit_search-console_settings';
|
||||
|
||||
/**
|
||||
* Registers the setting in WordPress.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function register() {
|
||||
parent::register();
|
||||
|
||||
$this->register_owned_keys();
|
||||
|
||||
// Backwards compatibility with previous dedicated option.
|
||||
add_filter(
|
||||
'default_option_' . self::OPTION,
|
||||
function ( $default ) {
|
||||
$default['propertyID'] = $this->options->get( 'googlesitekit_search_console_property' ) ?: '';
|
||||
|
||||
return $default;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'propertyID' => '',
|
||||
'ownerID' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for owned settings.
|
||||
*
|
||||
* @since 1.31.0
|
||||
*
|
||||
* @return array An array of keys for owned settings.
|
||||
*/
|
||||
public function get_owned_keys() {
|
||||
return array(
|
||||
'propertyID',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Site_Verification
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules;
|
||||
|
||||
use Google\Site_Kit\Core\Authentication\Clients\Google_Site_Kit_Client;
|
||||
use Google\Site_Kit\Core\Authentication\Verification;
|
||||
use Google\Site_Kit\Core\Authentication\Verification_File;
|
||||
use Google\Site_Kit\Core\Authentication\Verification_Meta;
|
||||
use Google\Site_Kit\Core\Modules\Module;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait;
|
||||
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
|
||||
use Google\Site_Kit\Core\Permissions\Permissions;
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Core\Util\Exit_Handler;
|
||||
use Google\Site_Kit\Core\Util\Google_URL_Matcher_Trait;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Util\URL;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\Exception as Google_Service_Exception;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SiteVerification as Google_Service_SiteVerification;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SiteVerification\SiteVerificationWebResourceGettokenRequest as Google_Service_SiteVerification_SiteVerificationWebResourceGettokenRequest;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SiteVerification\SiteVerificationWebResourceGettokenRequestSite as Google_Service_SiteVerification_SiteVerificationWebResourceGettokenRequestSite;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SiteVerification\SiteVerificationWebResourceResource as Google_Service_SiteVerification_SiteVerificationWebResourceResource;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\SiteVerification\SiteVerificationWebResourceResourceSite as Google_Service_SiteVerification_SiteVerificationWebResourceResourceSite;
|
||||
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface;
|
||||
use WP_Error;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class representing the Site Verification module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Site_Verification extends Module implements Module_With_Scopes {
|
||||
use Method_Proxy_Trait;
|
||||
use Module_With_Scopes_Trait;
|
||||
use Google_URL_Matcher_Trait;
|
||||
|
||||
/**
|
||||
* Module slug name.
|
||||
*/
|
||||
const MODULE_SLUG = 'site-verification';
|
||||
|
||||
/**
|
||||
* Meta site verification type.
|
||||
*/
|
||||
const VERIFICATION_TYPE_META = 'META';
|
||||
|
||||
/**
|
||||
* File site verification type.
|
||||
*/
|
||||
const VERIFICATION_TYPE_FILE = 'FILE';
|
||||
|
||||
/**
|
||||
* Verification meta tag cache key.
|
||||
*/
|
||||
const TRANSIENT_VERIFICATION_META_TAGS = 'googlesitekit_verification_meta_tags';
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register() {
|
||||
$this->register_scopes_hook();
|
||||
|
||||
add_action(
|
||||
'googlesitekit_verify_site_ownership',
|
||||
$this->get_method_proxy( 'handle_verification_token' ),
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
$print_site_verification_meta = function() {
|
||||
$this->print_site_verification_meta();
|
||||
};
|
||||
|
||||
add_action( 'wp_head', $print_site_verification_meta );
|
||||
add_action( 'login_head', $print_site_verification_meta );
|
||||
|
||||
add_action(
|
||||
'googlesitekit_authorize_user',
|
||||
function() {
|
||||
if ( ! $this->authentication->credentials()->using_proxy() ) {
|
||||
return;
|
||||
}
|
||||
$this->user_options->set( Verification::OPTION, 'verified' );
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
function () {
|
||||
$request_uri = $this->context->input()->filter( INPUT_SERVER, 'REQUEST_URI' );
|
||||
$request_method = $this->context->input()->filter( INPUT_SERVER, 'REQUEST_METHOD' );
|
||||
|
||||
if (
|
||||
( $request_uri && $request_method )
|
||||
&& 'GET' === strtoupper( $request_method )
|
||||
&& preg_match( '/^\/google(?P<token>[a-z0-9]+)\.html$/', $request_uri, $matches )
|
||||
) {
|
||||
$this->serve_verification_file( $matches['token'] );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$clear_verification_meta_cache = function ( $meta_id, $object_id, $meta_key ) {
|
||||
if ( $this->user_options->get_meta_key( Verification_Meta::OPTION ) === $meta_key ) {
|
||||
$this->transients->delete( self::TRANSIENT_VERIFICATION_META_TAGS );
|
||||
}
|
||||
};
|
||||
add_action( 'added_user_meta', $clear_verification_meta_cache, 10, 3 );
|
||||
add_action( 'updated_user_meta', $clear_verification_meta_cache, 10, 3 );
|
||||
add_action( 'deleted_user_meta', $clear_verification_meta_cache, 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets required Google OAuth scopes for the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array List of Google OAuth scopes.
|
||||
*/
|
||||
public function get_scopes() {
|
||||
return array(
|
||||
'https://www.googleapis.com/auth/siteverification',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of datapoint to definition data for each.
|
||||
*
|
||||
* @since 1.12.0
|
||||
*
|
||||
* @return array Map of datapoints to their definitions.
|
||||
*/
|
||||
protected function get_datapoint_definitions() {
|
||||
return array(
|
||||
'GET:verification' => array( 'service' => 'siteverification' ),
|
||||
'POST:verification' => array( 'service' => 'siteverification' ),
|
||||
'GET:verification-token' => array( 'service' => 'siteverification' ),
|
||||
'GET:verified-sites' => array( 'service' => 'siteverification' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure.
|
||||
*
|
||||
* @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist.
|
||||
*/
|
||||
protected function create_data_request( Data_Request $data ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
case 'GET:verification':
|
||||
return $this->get_siteverification_service()->webResource->listWebResource();
|
||||
case 'POST:verification':
|
||||
if ( ! isset( $data['siteURL'] ) ) {
|
||||
/* translators: %s: Missing parameter name */
|
||||
return new WP_Error( 'missing_required_param', sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'siteURL' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
return function() use ( $data ) {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
if ( ! $current_user || ! $current_user->exists() ) {
|
||||
return new WP_Error( 'unknown_user', __( 'Unknown user.', 'google-site-kit' ) );
|
||||
}
|
||||
|
||||
$site = $this->get_data( 'verification', $data );
|
||||
|
||||
if ( is_wp_error( $site ) ) {
|
||||
return $site;
|
||||
}
|
||||
|
||||
$sites = array();
|
||||
|
||||
if ( ! empty( $site['verified'] ) ) {
|
||||
$this->authentication->verification()->set( true );
|
||||
|
||||
return $site;
|
||||
} else {
|
||||
$token = $this->get_data( 'verification-token', $data );
|
||||
|
||||
if ( is_wp_error( $token ) ) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
$this->authentication->verification_meta()->set( $token['token'] );
|
||||
|
||||
$restore_defer = $this->with_client_defer( false );
|
||||
$errors = new WP_Error();
|
||||
|
||||
foreach ( URL::permute_site_url( $data['siteURL'] ) as $url ) {
|
||||
$site = new Google_Service_SiteVerification_SiteVerificationWebResourceResourceSite();
|
||||
$site->setType( 'SITE' );
|
||||
$site->setIdentifier( $url );
|
||||
$resource = new Google_Service_SiteVerification_SiteVerificationWebResourceResource();
|
||||
$resource->setSite( $site );
|
||||
|
||||
try {
|
||||
$sites[] = $this->get_siteverification_service()->webResource->insert( 'META', $resource );
|
||||
} catch ( Google_Service_Exception $e ) {
|
||||
$messages = wp_list_pluck( $e->getErrors(), 'message' );
|
||||
$message = array_shift( $messages );
|
||||
|
||||
$errors->add( $e->getCode(), $message, array( 'url' => $url ) );
|
||||
} catch ( Exception $e ) {
|
||||
$errors->add( $e->getCode(), $e->getMessage(), array( 'url' => $url ) );
|
||||
}
|
||||
}
|
||||
|
||||
$restore_defer();
|
||||
|
||||
if ( empty( $sites ) ) {
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
$this->authentication->verification()->set( true );
|
||||
|
||||
try {
|
||||
$verification = $this->get_siteverification_service()->webResource->get( $data['siteURL'] );
|
||||
} catch ( Google_Service_Exception $e ) {
|
||||
$verification = array_shift( $sites );
|
||||
}
|
||||
|
||||
return array(
|
||||
'identifier' => $verification->getSite()->getIdentifier(),
|
||||
'type' => $verification->getSite()->getType(),
|
||||
'verified' => true,
|
||||
);
|
||||
};
|
||||
case 'GET:verification-token':
|
||||
$existing_token = $this->authentication->verification_meta()->get();
|
||||
|
||||
if ( ! empty( $existing_token ) ) {
|
||||
return function() use ( $existing_token ) {
|
||||
return array(
|
||||
'method' => 'META',
|
||||
'token' => $existing_token,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
$current_url = ! empty( $data['siteURL'] ) ? $data['siteURL'] : $this->context->get_reference_site_url();
|
||||
$site = new Google_Service_SiteVerification_SiteVerificationWebResourceGettokenRequestSite();
|
||||
$site->setIdentifier( $current_url );
|
||||
$site->setType( 'SITE' );
|
||||
$request = new Google_Service_SiteVerification_SiteVerificationWebResourceGettokenRequest();
|
||||
$request->setSite( $site );
|
||||
$request->setVerificationMethod( 'META' );
|
||||
|
||||
return $this->get_siteverification_service()->webResource->getToken( $request );
|
||||
case 'GET:verified-sites':
|
||||
return $this->get_siteverification_service()->webResource->listWebResource();
|
||||
}
|
||||
|
||||
return parent::create_data_request( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a response for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @param mixed $response Request response.
|
||||
*
|
||||
* @return mixed Parsed response data on success, or WP_Error on failure.
|
||||
*/
|
||||
protected function parse_data_response( Data_Request $data, $response ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
case 'GET:verification':
|
||||
if ( $data['siteURL'] ) {
|
||||
$current_url = $data['siteURL'];
|
||||
} else {
|
||||
$current_url = $this->context->get_reference_site_url();
|
||||
}
|
||||
|
||||
$items = $response->getItems();
|
||||
foreach ( $items as $item ) {
|
||||
$site = $item->getSite();
|
||||
|
||||
$match = false;
|
||||
if ( 'INET_DOMAIN' === $site->getType() ) {
|
||||
$match = $this->is_domain_match( $site->getIdentifier(), $current_url );
|
||||
} elseif ( 'SITE' === $site->getType() ) {
|
||||
$match = $this->is_url_match( $site->getIdentifier(), $current_url );
|
||||
}
|
||||
|
||||
if ( $match ) {
|
||||
return array(
|
||||
'identifier' => $site->getIdentifier(),
|
||||
'type' => $site->getType(),
|
||||
'verified' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'identifier' => $current_url,
|
||||
'type' => 'SITE',
|
||||
'verified' => false,
|
||||
);
|
||||
case 'GET:verification-token':
|
||||
if ( is_array( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return array(
|
||||
'method' => $response->getMethod(),
|
||||
'token' => $response->getToken(),
|
||||
);
|
||||
case 'GET:verified-sites':
|
||||
$items = $response->getItems();
|
||||
$data = array();
|
||||
|
||||
foreach ( $items as $item ) {
|
||||
$site = $item->getSite();
|
||||
$data[ $item->getId() ] = array(
|
||||
'identifier' => $site->getIdentifier(),
|
||||
'type' => $site->getType(),
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return parent::parse_data_response( $data, $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up information about the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array Associative array of module info.
|
||||
*/
|
||||
protected function setup_info() {
|
||||
return array(
|
||||
'slug' => 'site-verification',
|
||||
'name' => _x( 'Site Verification', 'Service name', 'google-site-kit' ),
|
||||
'description' => __( 'Google Site Verification allows you to manage ownership of your site.', 'google-site-kit' ),
|
||||
'order' => 0,
|
||||
'homepage' => __( 'https://www.google.com/webmasters/verification/home', 'google-site-kit' ),
|
||||
'internal' => true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured siteverification service instance.
|
||||
*
|
||||
* @return Google_Service_SiteVerification The Site Verification API service.
|
||||
*/
|
||||
private function get_siteverification_service() {
|
||||
return $this->get_service( 'siteverification' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the Google services the module should use.
|
||||
*
|
||||
* This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested
|
||||
* for the first time.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0 Now requires Google_Site_Kit_Client instance.
|
||||
*
|
||||
* @param Google_Site_Kit_Client $client Google client instance.
|
||||
* @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an
|
||||
* instance of Google_Service.
|
||||
*/
|
||||
protected function setup_services( Google_Site_Kit_Client $client ) {
|
||||
return array(
|
||||
'siteverification' => new Google_Service_SiteVerification( $client ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles receiving a verification token for a user by the authentication proxy.
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @since 1.1.2 Runs on `admin_action_googlesitekit_proxy_setup` and no longer redirects directly.
|
||||
* @since 1.48.0 Token and method are now passed as arguments.
|
||||
* @since 1.49.0 No longer uses the `googlesitekit_proxy_setup_url_params` filter to set the `verify` and `verification_method` query params.
|
||||
*
|
||||
* @param string $token Verification token.
|
||||
* @param string $method Verification method type.
|
||||
*/
|
||||
private function handle_verification_token( $token, $method ) {
|
||||
switch ( $method ) {
|
||||
case self::VERIFICATION_TYPE_FILE:
|
||||
$this->authentication->verification_file()->set( $token );
|
||||
break;
|
||||
case self::VERIFICATION_TYPE_META:
|
||||
$this->authentication->verification_meta()->set( $token );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints site verification meta in wp_head().
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
private function print_site_verification_meta() {
|
||||
// Get verification meta tags for all users.
|
||||
$verification_tags = $this->get_all_verification_tags();
|
||||
$allowed_html = array(
|
||||
'meta' => array(
|
||||
'name' => array(),
|
||||
'content' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $verification_tags as $verification_tag ) {
|
||||
$verification_tag = html_entity_decode( $verification_tag );
|
||||
|
||||
if ( 0 !== strpos( $verification_tag, '<meta ' ) ) {
|
||||
$verification_tag = '<meta name="google-site-verification" content="' . esc_attr( $verification_tag ) . '">';
|
||||
}
|
||||
|
||||
echo wp_kses( $verification_tag, $allowed_html );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all available verification tags for all users.
|
||||
*
|
||||
* This is a special method needed for printing all meta tags in the frontend.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*
|
||||
* @return array List of verification meta tags.
|
||||
*/
|
||||
private function get_all_verification_tags() {
|
||||
global $wpdb;
|
||||
|
||||
$meta_tags = $this->transients->get( self::TRANSIENT_VERIFICATION_META_TAGS );
|
||||
|
||||
if ( ! is_array( $meta_tags ) ) {
|
||||
$meta_key = $this->user_options->get_meta_key( Verification_Meta::OPTION );
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||
$meta_tags = $wpdb->get_col(
|
||||
$wpdb->prepare( "SELECT DISTINCT meta_value FROM {$wpdb->usermeta} WHERE meta_key = %s", $meta_key )
|
||||
);
|
||||
$this->transients->set( self::TRANSIENT_VERIFICATION_META_TAGS, $meta_tags );
|
||||
}
|
||||
|
||||
return array_filter( $meta_tags );
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves the verification file response.
|
||||
*
|
||||
* @param string $verification_token Token portion of verification.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
private function serve_verification_file( $verification_token ) {
|
||||
$user_ids = ( new \WP_User_Query(
|
||||
array(
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_key' => $this->user_options->get_meta_key( Verification_File::OPTION ),
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
'meta_value' => $verification_token,
|
||||
'fields' => 'id',
|
||||
'number' => 1,
|
||||
)
|
||||
) )->get_results();
|
||||
|
||||
$user_id = array_shift( $user_ids ) ?: 0;
|
||||
|
||||
if ( $user_id && user_can( $user_id, Permissions::SETUP ) ) {
|
||||
printf( 'google-site-verification: google%s.html', esc_html( $verification_token ) );
|
||||
( new Exit_Handler() )->invoke();
|
||||
}
|
||||
|
||||
// If the user does not have the necessary permissions then let the request pass through.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns TRUE to indicate that this module should be always active.
|
||||
*
|
||||
* @since 1.49.0
|
||||
*
|
||||
* @return bool Returns `true` indicating that this module should be activated all the time.
|
||||
*/
|
||||
public static function is_force_active() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,613 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Tag_Manager
|
||||
*
|
||||
* @package Google\Site_Kit
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules;
|
||||
|
||||
use Exception;
|
||||
use Google\Site_Kit\Context;
|
||||
use Google\Site_Kit\Core\Assets\Asset;
|
||||
use Google\Site_Kit\Core\Assets\Script;
|
||||
use Google\Site_Kit\Core\Authentication\Clients\Google_Site_Kit_Client;
|
||||
use Google\Site_Kit\Core\Modules\Module;
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Assets_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Deactivation;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Debug_Fields;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Owner;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Owner_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Service_Entity;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Settings_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Tag;
|
||||
use Google\Site_Kit\Core\Modules\Module_With_Tag_Trait;
|
||||
use Google\Site_Kit\Core\Modules\Tag_Manager\Tag_Matchers;
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
use Google\Site_Kit\Core\REST_API\Data_Request;
|
||||
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
|
||||
use Google\Site_Kit\Core\Tags\Guards\Tag_Environment_Type_Guard;
|
||||
use Google\Site_Kit\Core\Tags\Guards\Tag_Verify_Guard;
|
||||
use Google\Site_Kit\Core\Site_Health\Debug_Data;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Util\Sort;
|
||||
use Google\Site_Kit\Core\Util\URL;
|
||||
use Google\Site_Kit\Modules\Tag_Manager\AMP_Tag;
|
||||
use Google\Site_Kit\Modules\Tag_Manager\Settings;
|
||||
use Google\Site_Kit\Modules\Tag_Manager\Tag_Guard;
|
||||
use Google\Site_Kit\Modules\Tag_Manager\Web_Tag;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\TagManager as Google_Service_TagManager;
|
||||
use Google\Site_Kit_Dependencies\Google\Service\TagManager\Container as Google_Service_TagManager_Container;
|
||||
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class representing the Tag Manager module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
final class Tag_Manager extends Module
|
||||
implements Module_With_Scopes, Module_With_Settings, Module_With_Assets, Module_With_Debug_Fields, Module_With_Owner, Module_With_Service_Entity, Module_With_Deactivation, Module_With_Tag {
|
||||
use Method_Proxy_Trait;
|
||||
use Module_With_Assets_Trait;
|
||||
use Module_With_Owner_Trait;
|
||||
use Module_With_Scopes_Trait;
|
||||
use Module_With_Settings_Trait;
|
||||
use Module_With_Tag_Trait;
|
||||
|
||||
/**
|
||||
* Module slug name.
|
||||
*/
|
||||
const MODULE_SLUG = 'tagmanager';
|
||||
|
||||
/**
|
||||
* Container usage context for web.
|
||||
*/
|
||||
const USAGE_CONTEXT_WEB = 'web';
|
||||
|
||||
/**
|
||||
* Container usage context for AMP.
|
||||
*/
|
||||
const USAGE_CONTEXT_AMP = 'amp';
|
||||
|
||||
/**
|
||||
* Map of container usageContext to option key for containerID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $context_map = array(
|
||||
self::USAGE_CONTEXT_WEB => 'containerID',
|
||||
self::USAGE_CONTEXT_AMP => 'ampContainerID',
|
||||
);
|
||||
|
||||
/**
|
||||
* Registers functionality through WordPress hooks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function register() {
|
||||
$this->register_scopes_hook();
|
||||
|
||||
// Tag Manager tag placement logic.
|
||||
add_action( 'template_redirect', array( $this, 'register_tag' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets required Google OAuth scopes for the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array List of Google OAuth scopes.
|
||||
*/
|
||||
public function get_scopes() {
|
||||
return array(
|
||||
'https://www.googleapis.com/auth/tagmanager.readonly',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the module is connected.
|
||||
*
|
||||
* A module being connected means that all steps required as part of its activation are completed.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return bool True if module is connected, false otherwise.
|
||||
*/
|
||||
public function is_connected() {
|
||||
$settings = $this->get_settings()->get();
|
||||
$amp_mode = $this->context->get_amp_mode();
|
||||
|
||||
switch ( $amp_mode ) {
|
||||
case Context::AMP_MODE_PRIMARY:
|
||||
$container_ids = array( $settings['ampContainerID'] );
|
||||
break;
|
||||
case Context::AMP_MODE_SECONDARY:
|
||||
$container_ids = array( $settings['containerID'], $settings['ampContainerID'] );
|
||||
break;
|
||||
default:
|
||||
$container_ids = array( $settings['containerID'] );
|
||||
}
|
||||
|
||||
$container_id_errors = array_filter(
|
||||
$container_ids,
|
||||
function( $container_id ) {
|
||||
return ! $container_id;
|
||||
}
|
||||
);
|
||||
|
||||
if ( ! empty( $container_id_errors ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::is_connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up when the module is deactivated.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function on_deactivation() {
|
||||
$this->get_settings()->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of debug field definitions.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_debug_fields() {
|
||||
$settings = $this->get_settings()->get();
|
||||
|
||||
return array(
|
||||
'tagmanager_account_id' => array(
|
||||
'label' => __( 'Tag Manager account ID', 'google-site-kit' ),
|
||||
'value' => $settings['accountID'],
|
||||
'debug' => Debug_Data::redact_debug_value( $settings['accountID'] ),
|
||||
),
|
||||
'tagmanager_container_id' => array(
|
||||
'label' => __( 'Tag Manager container ID', 'google-site-kit' ),
|
||||
'value' => $settings['containerID'],
|
||||
'debug' => Debug_Data::redact_debug_value( $settings['containerID'], 7 ),
|
||||
),
|
||||
'tagmanager_amp_container_id' => array(
|
||||
'label' => __( 'Tag Manager AMP container ID', 'google-site-kit' ),
|
||||
'value' => $settings['ampContainerID'],
|
||||
'debug' => Debug_Data::redact_debug_value( $settings['ampContainerID'], 7 ),
|
||||
),
|
||||
'tagmanager_use_snippet' => array(
|
||||
'label' => __( 'Tag Manager snippet placed', 'google-site-kit' ),
|
||||
'value' => $settings['useSnippet'] ? __( 'Yes', 'google-site-kit' ) : __( 'No', 'google-site-kit' ),
|
||||
'debug' => $settings['useSnippet'] ? 'yes' : 'no',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a string to be used for a container name.
|
||||
*
|
||||
* @since 1.0.4
|
||||
*
|
||||
* @param string $name String to sanitize.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize_container_name( $name ) {
|
||||
// Remove any leading or trailing whitespace.
|
||||
$name = trim( $name );
|
||||
// Must not start with an underscore.
|
||||
$name = ltrim( $name, '_' );
|
||||
// Decode entities for special characters so that they are stripped properly.
|
||||
$name = wp_specialchars_decode( $name, ENT_QUOTES );
|
||||
// Convert accents to basic characters to prevent them from being stripped.
|
||||
$name = remove_accents( $name );
|
||||
// Strip all non-simple characters.
|
||||
$name = preg_replace( '/[^a-zA-Z0-9_., -]/', '', $name );
|
||||
// Collapse multiple whitespaces.
|
||||
$name = preg_replace( '/\s+/', ' ', $name );
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of datapoint to definition data for each.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @return array Map of datapoints to their definitions.
|
||||
*/
|
||||
protected function get_datapoint_definitions() {
|
||||
return array(
|
||||
'GET:accounts' => array( 'service' => 'tagmanager' ),
|
||||
'GET:accounts-containers' => array( 'service' => 'tagmanager' ),
|
||||
'GET:containers' => array( 'service' => 'tagmanager' ),
|
||||
'POST:create-container' => array(
|
||||
'service' => 'tagmanager',
|
||||
'scopes' => array( 'https://www.googleapis.com/auth/tagmanager.edit.containers' ),
|
||||
'request_scopes_message' => __( 'Additional permissions are required to create a new Tag Manager container on your behalf.', 'google-site-kit' ),
|
||||
),
|
||||
'GET:live-container-version' => array( 'service' => 'tagmanager' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure.
|
||||
*
|
||||
* @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist.
|
||||
*/
|
||||
protected function create_data_request( Data_Request $data ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
// Intentional fallthrough.
|
||||
case 'GET:accounts':
|
||||
case 'GET:accounts-containers':
|
||||
return $this->get_tagmanager_service()->accounts->listAccounts();
|
||||
case 'GET:containers':
|
||||
if ( ! isset( $data['accountID'] ) ) {
|
||||
/* translators: %s: Missing parameter name */
|
||||
return new WP_Error( 'missing_required_param', sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ), array( 'status' => 400 ) );
|
||||
}
|
||||
return $this->get_tagmanager_service()->accounts_containers->listAccountsContainers( "accounts/{$data['accountID']}" );
|
||||
case 'POST:create-container':
|
||||
if ( ! isset( $data['accountID'] ) ) {
|
||||
return new WP_Error(
|
||||
'missing_required_param',
|
||||
/* translators: %s: Missing parameter name */
|
||||
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$usage_context = $data['usageContext'] ?: array( self::USAGE_CONTEXT_WEB, self::USAGE_CONTEXT_AMP );
|
||||
|
||||
if ( empty( $this->context_map[ $usage_context ] ) ) {
|
||||
return new WP_Error(
|
||||
'invalid_param',
|
||||
sprintf(
|
||||
/* translators: 1: Invalid parameter name, 2: list of valid values */
|
||||
__( 'Request parameter %1$s is not one of %2$s', 'google-site-kit' ),
|
||||
'usageContext',
|
||||
implode( ', ', array_keys( $this->context_map ) )
|
||||
),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$account_id = $data['accountID'];
|
||||
|
||||
if ( $data['name'] ) {
|
||||
$container_name = $data['name'];
|
||||
} else {
|
||||
// Use site name for container, fallback to domain of reference URL.
|
||||
$container_name = get_bloginfo( 'name' ) ?: URL::parse( $this->context->get_reference_site_url(), PHP_URL_HOST );
|
||||
// Prevent naming conflict (Tag Manager does not allow more than one with same name).
|
||||
if ( self::USAGE_CONTEXT_AMP === $usage_context ) {
|
||||
$container_name .= ' AMP';
|
||||
}
|
||||
}
|
||||
|
||||
$container = new Google_Service_TagManager_Container();
|
||||
$container->setName( self::sanitize_container_name( $container_name ) );
|
||||
$container->setUsageContext( (array) $usage_context );
|
||||
|
||||
return $this->get_tagmanager_service()->accounts_containers->create( "accounts/{$account_id}", $container );
|
||||
case 'GET:live-container-version':
|
||||
if ( ! isset( $data['accountID'] ) ) {
|
||||
return new WP_Error(
|
||||
'missing_required_param',
|
||||
/* translators: %s: Missing parameter name */
|
||||
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
if ( ! isset( $data['internalContainerID'] ) ) {
|
||||
return new WP_Error(
|
||||
'missing_required_param',
|
||||
/* translators: %s: Missing parameter name */
|
||||
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'internalContainerID' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
return $this->get_tagmanager_service()->accounts_containers_versions->live(
|
||||
"accounts/{$data['accountID']}/containers/{$data['internalContainerID']}"
|
||||
);
|
||||
}
|
||||
|
||||
return parent::create_data_request( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GTM Container.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $account_id The account ID.
|
||||
* @param string|array $usage_context The container usage context(s).
|
||||
*
|
||||
* @return string Container public ID.
|
||||
* @throws Exception Throws an exception if raised during container creation.
|
||||
*/
|
||||
protected function create_container( $account_id, $usage_context = self::USAGE_CONTEXT_WEB ) {
|
||||
$restore_defer = $this->with_client_defer( false );
|
||||
|
||||
// Use site name for container, fallback to domain of reference URL.
|
||||
$container_name = get_bloginfo( 'name' ) ?: URL::parse( $this->context->get_reference_site_url(), PHP_URL_HOST );
|
||||
// Prevent naming conflict (Tag Manager does not allow more than one with same name).
|
||||
if ( self::USAGE_CONTEXT_AMP === $usage_context ) {
|
||||
$container_name .= ' AMP';
|
||||
}
|
||||
$container_name = self::sanitize_container_name( $container_name );
|
||||
|
||||
$container = new Google_Service_TagManager_Container();
|
||||
$container->setName( $container_name );
|
||||
$container->setUsageContext( (array) $usage_context );
|
||||
|
||||
try {
|
||||
$new_container = $this->get_tagmanager_service()->accounts_containers->create( "accounts/{$account_id}", $container );
|
||||
} catch ( Exception $exception ) {
|
||||
$restore_defer();
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$restore_defer();
|
||||
|
||||
return $new_container->getPublicId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a response for the given datapoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param Data_Request $data Data request object.
|
||||
* @param mixed $response Request response.
|
||||
*
|
||||
* @return mixed Parsed response data on success, or WP_Error on failure.
|
||||
*/
|
||||
protected function parse_data_response( Data_Request $data, $response ) {
|
||||
switch ( "{$data->method}:{$data->datapoint}" ) {
|
||||
case 'GET:accounts':
|
||||
/* @var Google_Service_TagManager_ListAccountsResponse $response List accounts response. */
|
||||
return Sort::case_insensitive_list_sort(
|
||||
$response->getAccount(),
|
||||
'name'
|
||||
);
|
||||
case 'GET:accounts-containers':
|
||||
/* @var Google_Service_TagManager_ListAccountsResponse $response List accounts response. */
|
||||
$accounts = Sort::case_insensitive_list_sort(
|
||||
$response->getAccount(),
|
||||
'name'
|
||||
);
|
||||
$response = array(
|
||||
// TODO: Parse this response to a regular array.
|
||||
'accounts' => $accounts,
|
||||
'containers' => array(),
|
||||
);
|
||||
if ( 0 === count( $response['accounts'] ) ) {
|
||||
return $response;
|
||||
}
|
||||
if ( $data['accountID'] ) {
|
||||
$account_id = $data['accountID'];
|
||||
} else {
|
||||
$account_id = $response['accounts'][0]->getAccountId();
|
||||
}
|
||||
|
||||
$containers = $this->get_data(
|
||||
'containers',
|
||||
array(
|
||||
'accountID' => $account_id,
|
||||
'usageContext' => $data['usageContext'] ?: self::USAGE_CONTEXT_WEB,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $containers ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return array_merge( $response, compact( 'containers' ) );
|
||||
case 'GET:containers':
|
||||
/* @var Google_Service_TagManager_ListContainersResponse $response Response object. */
|
||||
$usage_context = $data['usageContext'] ?: array( self::USAGE_CONTEXT_WEB, self::USAGE_CONTEXT_AMP );
|
||||
/* @var Google_Service_TagManager_Container[] $containers Filtered containers. */
|
||||
$containers = array_filter(
|
||||
(array) $response->getContainer(),
|
||||
function ( Google_Service_TagManager_Container $container ) use ( $usage_context ) {
|
||||
return array_intersect( (array) $usage_context, $container->getUsageContext() );
|
||||
}
|
||||
);
|
||||
|
||||
return Sort::case_insensitive_list_sort(
|
||||
array_values( $containers ),
|
||||
'name'
|
||||
);
|
||||
}
|
||||
|
||||
return parent::parse_data_response( $data, $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured TagManager service instance.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return Google_Service_TagManager instance.
|
||||
* @throws Exception Thrown if the module did not correctly set up the service.
|
||||
*/
|
||||
private function get_tagmanager_service() {
|
||||
return $this->get_service( 'tagmanager' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up information about the module.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return array Associative array of module info.
|
||||
*/
|
||||
protected function setup_info() {
|
||||
return array(
|
||||
'slug' => self::MODULE_SLUG,
|
||||
'name' => _x( 'Tag Manager', 'Service name', 'google-site-kit' ),
|
||||
'description' => __( 'Tag Manager creates an easy to manage way to create tags on your site without updating code', 'google-site-kit' ),
|
||||
'order' => 6,
|
||||
'homepage' => __( 'https://tagmanager.google.com/', 'google-site-kit' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the Google services the module should use.
|
||||
*
|
||||
* This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested
|
||||
* for the first time.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0 Now requires Google_Site_Kit_Client instance.
|
||||
*
|
||||
* @param Google_Site_Kit_Client $client Google client instance.
|
||||
* @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an
|
||||
* instance of Google_Service.
|
||||
*/
|
||||
protected function setup_services( Google_Site_Kit_Client $client ) {
|
||||
return array(
|
||||
'tagmanager' => new Google_Service_TagManager( $client ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's settings instance.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return Module_Settings
|
||||
*/
|
||||
protected function setup_settings() {
|
||||
return new Settings( $this->options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the module's assets to register.
|
||||
*
|
||||
* @since 1.11.0
|
||||
*
|
||||
* @return Asset[] List of Asset objects.
|
||||
*/
|
||||
protected function setup_assets() {
|
||||
$base_url = $this->context->url( 'dist/assets/' );
|
||||
|
||||
$dependencies = array(
|
||||
'googlesitekit-api',
|
||||
'googlesitekit-data',
|
||||
'googlesitekit-datastore-site',
|
||||
'googlesitekit-modules',
|
||||
'googlesitekit-vendor',
|
||||
'googlesitekit-components',
|
||||
);
|
||||
|
||||
$analytics_exists = apply_filters( 'googlesitekit_module_exists', false, 'analytics-4' );
|
||||
|
||||
// Note that the Tag Manager bundle will make use of the Analytics bundle if it's available,
|
||||
// but can also function without it, hence the conditional include of the Analytics bundle here.
|
||||
if ( $analytics_exists ) {
|
||||
$dependencies[] = 'googlesitekit-modules-analytics-4';
|
||||
}
|
||||
|
||||
return array(
|
||||
new Script(
|
||||
'googlesitekit-modules-tagmanager',
|
||||
array(
|
||||
'src' => $base_url . 'js/googlesitekit-modules-tagmanager.js',
|
||||
'dependencies' => $dependencies,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Tag Manager tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @since 1.119.0 Made method public.
|
||||
*/
|
||||
public function register_tag() {
|
||||
$is_amp = $this->context->is_amp();
|
||||
$module_settings = $this->get_settings();
|
||||
$settings = $module_settings->get();
|
||||
|
||||
$tag = $is_amp
|
||||
? new AMP_Tag( $settings['ampContainerID'], self::MODULE_SLUG )
|
||||
: new Web_Tag( $settings['containerID'], self::MODULE_SLUG );
|
||||
|
||||
if ( ! $tag->is_tag_blocked() ) {
|
||||
$tag->use_guard( new Tag_Verify_Guard( $this->context->input() ) );
|
||||
$tag->use_guard( new Tag_Guard( $module_settings, $is_amp ) );
|
||||
$tag->use_guard( new Tag_Environment_Type_Guard() );
|
||||
|
||||
if ( $tag->can_register() ) {
|
||||
$tag->register();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Module_Tag_Matchers instance.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @return Module_Tag_Matchers Module_Tag_Matchers instance.
|
||||
*/
|
||||
public function get_tag_matchers() {
|
||||
return new Tag_Matchers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has access to the current configured service entity.
|
||||
*
|
||||
* @since 1.77.0
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public function check_service_entity_access() {
|
||||
$is_amp_mode = in_array( $this->context->get_amp_mode(), array( Context::AMP_MODE_PRIMARY, Context::AMP_MODE_SECONDARY ), true );
|
||||
|
||||
$settings = $this->get_settings()->get();
|
||||
$account_id = $settings['accountID'];
|
||||
|
||||
$configured_containers = $is_amp_mode ? array( $settings['containerID'], $settings['ampContainerID'] ) : array( $settings['containerID'] );
|
||||
|
||||
try {
|
||||
$containers = $this->get_tagmanager_service()->accounts_containers->listAccountsContainers( "accounts/{$account_id}" );
|
||||
} catch ( Exception $e ) {
|
||||
if ( $e->getCode() === 404 ) {
|
||||
return false;
|
||||
}
|
||||
return $this->exception_to_error( $e );
|
||||
}
|
||||
|
||||
$all_containers = array_map(
|
||||
function( $container ) {
|
||||
return $container->getPublicId();
|
||||
},
|
||||
$containers->getContainer()
|
||||
);
|
||||
|
||||
return empty( array_diff( $configured_containers, $all_containers ) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Tag_Manager\AMP_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Tag_Manager
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Tag_Manager;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_AMP_Tag;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Class for AMP tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class AMP_Tag extends Module_AMP_Tag {
|
||||
|
||||
use Method_Proxy_Trait;
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
public function register() {
|
||||
$render = $this->get_method_proxy_once( 'render' );
|
||||
|
||||
// Which actions are run depends on the version of the AMP Plugin
|
||||
// (https://amp-wp.org/) available. Version >=1.3 exposes a
|
||||
// new, `amp_print_analytics` action.
|
||||
// For all AMP modes, AMP plugin version >=1.3.
|
||||
add_action( 'amp_print_analytics', $render );
|
||||
// For AMP Standard and Transitional, AMP plugin version <1.3.
|
||||
add_action( 'wp_footer', $render, 20 );
|
||||
// For AMP Reader, AMP plugin version <1.3.
|
||||
add_action( 'amp_post_template_footer', $render, 20 );
|
||||
// For Web Stories plugin.
|
||||
add_action( 'web_stories_print_analytics', $render );
|
||||
|
||||
// Load amp-analytics component for AMP Reader.
|
||||
$this->enqueue_amp_reader_component_script( 'amp-analytics', 'https://cdn.ampproject.org/v0/amp-analytics-0.1.js' );
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs Tag Manager <amp-analytics> tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function render() {
|
||||
// Add the optoutElementId for compatibility with our Analytics opt-out mechanism.
|
||||
// This configuration object will be merged with the configuration object returned
|
||||
// by the `config` attribute URL.
|
||||
$gtm_amp_opt = array(
|
||||
'optoutElementId' => '__gaOptOutExtension',
|
||||
);
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google Tag Manager AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
|
||||
printf(
|
||||
'<amp-analytics config="%s" data-credentials="include"%s><script type="application/json">%s</script></amp-analytics>',
|
||||
esc_url( 'https://www.googletagmanager.com/amp.json?id=' . rawurlencode( $this->tag_id ) ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$this->get_tag_blocked_on_consent_attribute(), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
wp_json_encode( $gtm_amp_opt )
|
||||
);
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google Tag Manager AMP snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Tag_Manager\Settings
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Tag_Manager
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Tag_Manager;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Legacy_Keys_Trait;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Interface;
|
||||
use Google\Site_Kit\Core\Storage\Setting_With_Owned_Keys_Trait;
|
||||
|
||||
/**
|
||||
* Class for Tag Manager settings.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Settings extends Module_Settings implements Setting_With_Owned_Keys_Interface {
|
||||
use Setting_With_Legacy_Keys_Trait, Setting_With_Owned_Keys_Trait;
|
||||
|
||||
const OPTION = 'googlesitekit_tagmanager_settings';
|
||||
|
||||
/**
|
||||
* Registers the setting in WordPress.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function register() {
|
||||
parent::register();
|
||||
|
||||
$this->register_legacy_keys_migration(
|
||||
array(
|
||||
'account_id' => 'accountID',
|
||||
'accountId' => 'accountID',
|
||||
'container_id' => 'containerID',
|
||||
'containerId' => 'containerID',
|
||||
)
|
||||
);
|
||||
|
||||
$this->register_owned_keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keys for owned settings.
|
||||
*
|
||||
* @since 1.16.0
|
||||
*
|
||||
* @return array An array of keys for owned settings.
|
||||
*/
|
||||
public function get_owned_keys() {
|
||||
return array(
|
||||
'accountID',
|
||||
'ampContainerID',
|
||||
'containerID',
|
||||
'internalAMPContainerID',
|
||||
'internalContainerID',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array(
|
||||
'ownerID' => 0,
|
||||
'accountID' => '',
|
||||
'ampContainerID' => '',
|
||||
'containerID' => '',
|
||||
'internalContainerID' => '',
|
||||
'internalAMPContainerID' => '',
|
||||
'useSnippet' => true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the callback for sanitizing the setting's value before saving.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @return callable|null
|
||||
*/
|
||||
protected function get_sanitize_callback() {
|
||||
return function( $option ) {
|
||||
if ( is_array( $option ) ) {
|
||||
if ( isset( $option['useSnippet'] ) ) {
|
||||
$option['useSnippet'] = (bool) $option['useSnippet'];
|
||||
}
|
||||
}
|
||||
return $option;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Tag_Manager\Tag_Guard
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Tag_Manager
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Tag_Manager;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Module_Settings;
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Guard;
|
||||
|
||||
/**
|
||||
* Class for the Tag Manager tag guard.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Guard extends Module_Tag_Guard {
|
||||
|
||||
/**
|
||||
* Determines AMP mode.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_amp;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @param Module_Settings $settings Module settings.
|
||||
* @param bool $is_amp AMP mode.
|
||||
*/
|
||||
public function __construct( Module_Settings $settings, $is_amp ) {
|
||||
parent::__construct( $settings );
|
||||
$this->is_amp = $is_amp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the guarded tag can be activated or not.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*
|
||||
* @return bool|WP_Error TRUE if guarded tag can be activated, otherwise FALSE or an error.
|
||||
*/
|
||||
public function can_activate() {
|
||||
$settings = $this->settings->get();
|
||||
$container_id = $this->is_amp ? $settings['ampContainerID'] : $settings['containerID'];
|
||||
return ! empty( $settings['useSnippet'] ) && ! empty( $container_id );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Core\Modules\Tag_Manager\Tag_Matchers
|
||||
*
|
||||
* @package Google\Site_Kit\Core\Modules\Tag_Manager
|
||||
* @copyright 2024 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Core\Modules\Tag_Manager;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
|
||||
use Google\Site_Kit\Core\Tags\Tag_Matchers_Interface;
|
||||
|
||||
/**
|
||||
* Class for Tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Tag_Matchers extends Module_Tag_Matchers implements Tag_Matchers_Interface {
|
||||
|
||||
/**
|
||||
* Holds array of regex tag matchers.
|
||||
*
|
||||
* @since 1.119.0
|
||||
*
|
||||
* @return array Array of regex matchers.
|
||||
*/
|
||||
public function regex_matchers() {
|
||||
return array(
|
||||
// Detect injection script (Google provided code, duracelltomi-google-tag-manager, metronet-tag-manager (uses user-provided)).
|
||||
"/<script[^>]*>[^>]+?www.googletagmanager.com\/gtm[^>]+?['|\"](GTM-[0-9A-Z]+)['|\"]/",
|
||||
|
||||
// Detect gtm.js script calls.
|
||||
"/<script[^>]*src=['|\"]https:\/\/www.googletagmanager.com\/gtm.js\?id=(GTM-[0-9A-Z]+)['|\"]/",
|
||||
|
||||
// Detect iframe version for no-js.
|
||||
"/<script[^>]*src=['|\"]https:\/\/www.googletagmanager.com\/ns.html\?id=(GTM-[0-9A-Z]+)['|\"]/",
|
||||
|
||||
// Detect amp tag.
|
||||
"/<amp-analytics [^>]*config=['|\"]https:\/\/www.googletagmanager.com\/amp.json\?id=(GTM-[0-9A-Z]+)['|\"]/",
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Google\Site_Kit\Modules\Tag_Manager\Web_Tag
|
||||
*
|
||||
* @package Google\Site_Kit\Modules\Tag_Manager
|
||||
* @copyright 2021 Google LLC
|
||||
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://sitekit.withgoogle.com
|
||||
*/
|
||||
|
||||
namespace Google\Site_Kit\Modules\Tag_Manager;
|
||||
|
||||
use Google\Site_Kit\Core\Modules\Tags\Module_Web_Tag;
|
||||
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
|
||||
use Google\Site_Kit\Core\Tags\Tag_With_DNS_Prefetch_Trait;
|
||||
use Google\Site_Kit\Core\Util\BC_Functions;
|
||||
|
||||
/**
|
||||
* Class for Web tag.
|
||||
*
|
||||
* @since 1.24.0
|
||||
* @access private
|
||||
* @ignore
|
||||
*/
|
||||
class Web_Tag extends Module_Web_Tag {
|
||||
|
||||
use Method_Proxy_Trait, Tag_With_DNS_Prefetch_Trait;
|
||||
|
||||
/**
|
||||
* Registers tag hooks.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
public function register() {
|
||||
$render_no_js = $this->get_method_proxy_once( 'render_no_js' );
|
||||
|
||||
add_action( 'wp_head', $this->get_method_proxy( 'render' ) );
|
||||
// For non-AMP (if `wp_body_open` supported).
|
||||
add_action( 'wp_body_open', $render_no_js, -9999 );
|
||||
// For non-AMP (as fallback).
|
||||
add_action( 'wp_footer', $render_no_js );
|
||||
|
||||
add_filter(
|
||||
'wp_resource_hints',
|
||||
$this->get_dns_prefetch_hints_callback( '//www.googletagmanager.com' ),
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
$this->do_init_tag_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs Tag Manager script.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
protected function render() {
|
||||
|
||||
$tag_manager_inline_script = sprintf(
|
||||
"
|
||||
( function( w, d, s, l, i ) {
|
||||
w[l] = w[l] || [];
|
||||
w[l].push( {'gtm.start': new Date().getTime(), event: 'gtm.js'} );
|
||||
var f = d.getElementsByTagName( s )[0],
|
||||
j = d.createElement( s ), dl = l != 'dataLayer' ? '&l=' + l : '';
|
||||
j.async = true;
|
||||
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
|
||||
f.parentNode.insertBefore( j, f );
|
||||
} )( window, document, 'script', 'dataLayer', '%s' );
|
||||
",
|
||||
esc_js( $this->tag_id )
|
||||
);
|
||||
|
||||
$tag_manager_consent_attribute = $this->get_tag_blocked_on_consent_attribute_array();
|
||||
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'Google Tag Manager snippet added by Site Kit', 'google-site-kit' ) );
|
||||
BC_Functions::wp_print_inline_script_tag( $tag_manager_inline_script, $tag_manager_consent_attribute );
|
||||
printf( "\n<!-- %s -->\n", esc_html__( 'End Google Tag Manager snippet added by Site Kit', 'google-site-kit' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs Tag Manager iframe for when the browser has JavaScript disabled.
|
||||
*
|
||||
* @since 1.24.0
|
||||
*/
|
||||
private function render_no_js() {
|
||||
// Consent-based blocking requires JS to be enabled so we need to bail here if present.
|
||||
if ( $this->get_tag_blocked_on_consent_attribute() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iframe_src = 'https://www.googletagmanager.com/ns.html?id=' . rawurlencode( $this->tag_id );
|
||||
|
||||
?>
|
||||
<!-- <?php esc_html_e( 'Google Tag Manager (noscript) snippet added by Site Kit', 'google-site-kit' ); ?> -->
|
||||
<noscript>
|
||||
<iframe src="<?php echo esc_url( $iframe_src ); ?>" height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||
</noscript>
|
||||
<!-- <?php esc_html_e( 'End Google Tag Manager (noscript) snippet added by Site Kit', 'google-site-kit' ); ?> -->
|
||||
<?php
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user