Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,178 @@
<?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;
use Tag_With_Linker_Trait;
/**
* Custom dimensions data.
*
* @since 1.113.0
* @var array
*/
private $custom_dimensions;
/**
* 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 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();
$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 );
}
}

View File

@@ -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 );
}
}

View File

@@ -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()
);
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,111 @@
<?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.
*/
#[\ReturnTypeWillChange]
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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 );
}
}

View File

@@ -0,0 +1,140 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Audience_Settings
*
* @package Google\Site_Kit\Modules\Analytics_4
* @copyright 2025 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\Setting;
use Google\Site_Kit\Core\Storage\Setting_With_ViewOnly_Keys_Interface;
/**
* Class for Audience_Settings.
*
* @since 1.148.0
* @access private
* @ignore
*/
class Audience_Settings extends Setting implements Setting_With_ViewOnly_Keys_Interface {
/**
* The option name for this setting.
*/
const OPTION = 'googlesitekit_analytics-4_audience_settings';
/**
* Gets the default value for settings.
*
* @since 1.148.0
*
* @return mixed The default value.
*/
public function get_default() {
return array(
'availableAudiences' => null,
'availableAudiencesLastSyncedAt' => 0,
'audienceSegmentationSetupCompletedBy' => null,
);
}
/**
* Gets the type of the setting.
*
* @since 1.148.0
*
* @return string The type of the setting.
*/
public function get_type() {
return 'array';
}
/**
* Gets the callback for sanitizing the setting's value before saving.
*
* @since 1.148.0
*
* @return callable|null
*/
protected function get_sanitize_callback() {
return function ( $option ) {
return $this->sanitize( $option );
};
}
/**
* Gets the view-only keys for the setting.
*
* @since 1.148.0
*
* @return array List of view-only keys.
*/
public function get_view_only_keys() {
return array(
'availableAudiences',
'audienceSegmentationSetupCompletedBy',
);
}
/**
* Merges the given settings with the existing ones. It will keep the old settings
* value for the properties that are not present in the given settings.
*
* @since 1.148.0
*
* @param array $settings The settings to merge.
*
* @return array The merged settings.
*/
public function merge( $settings ) {
$existing_settings = $this->get();
$updated_settings = array_merge( $existing_settings, $settings );
$this->set( $updated_settings );
return $updated_settings;
}
/**
* Sanitizes the settings.
*
* @since 1.148.0
*
* @param array $option The option to sanitize.
*
* @return array The sanitized settings.
*/
private function sanitize( $option ) {
$new_option = $this->get();
if ( isset( $option['availableAudiences'] ) ) {
if ( is_array( $option['availableAudiences'] ) ) {
$new_option['availableAudiences'] = $option['availableAudiences'];
} else {
$new_option['availableAudiences'] = null;
}
}
if ( isset( $option['availableAudiencesLastSyncedAt'] ) ) {
if ( is_int( $option['availableAudiencesLastSyncedAt'] ) ) {
$new_option['availableAudiencesLastSyncedAt'] = $option['availableAudiencesLastSyncedAt'];
} else {
$new_option['availableAudiencesLastSyncedAt'] = 0;
}
}
if ( isset( $option['audienceSegmentationSetupCompletedBy'] ) ) {
if ( is_int( $option['audienceSegmentationSetupCompletedBy'] ) ) {
$new_option['audienceSegmentationSetupCompletedBy'] = $option['audienceSegmentationSetupCompletedBy'];
} else {
$new_option['audienceSegmentationSetupCompletedBy'] = null;
}
}
return $new_option;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Cron
*
* @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\Conversion_Reporting;
/**
* Class providing cron implementation for conversion reporting.
*
* @since 1.135.0
* @access private
* @ignore
*/
class Conversion_Reporting_Cron {
const CRON_ACTION = 'googlesitekit_cron_conversion_reporting_events';
/**
* Cron callback reference.
*
* @var callable
*/
private $cron_callback;
/**
* Constructor.
*
* @since 1.135.0
*
* @param callable $callback Function to call on the cron action.
*/
public function __construct( callable $callback ) {
$this->cron_callback = $callback;
}
/**
* Registers functionality through WordPress hooks.
*
* @since 1.133.0
*/
public function register() {
add_action( self::CRON_ACTION, $this->cron_callback );
}
/**
* Schedules cron if not already set.
*
* @since 1.135.0
*/
public function maybe_schedule_cron() {
if ( ! wp_next_scheduled( self::CRON_ACTION ) && ! wp_installing() ) {
wp_schedule_single_event( time() + DAY_IN_SECONDS, self::CRON_ACTION );
}
}
}

View File

@@ -0,0 +1,204 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Events_Sync
*
* @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\Conversion_Reporting;
use Google\Site_Kit\Modules\Analytics_4;
use Google\Site_Kit\Modules\Analytics_4\Settings;
use Google\Site_Kit\Core\Storage\Transients;
/**
* Class providing report implementation for available events for conversion reporting.
*
* @since 1.135.0
* @access private
* @ignore
*/
class Conversion_Reporting_Events_Sync {
/**
* The detected events transient name.
*/
public const DETECTED_EVENTS_TRANSIENT = 'googlesitekit_conversion_reporting_detected_events';
/**
* The lost events transient name.
*/
public const LOST_EVENTS_TRANSIENT = 'googlesitekit_conversion_reporting_lost_events';
const EVENT_NAMES = array(
'add_to_cart',
'purchase',
'submit_lead_form',
'generate_lead',
'contact',
);
/**
* Settings instance.
*
* @var Settings
*/
private $settings;
/**
* Analytics_4 instance.
*
* @var Analytics_4
*/
private $analytics;
/**
* Conversion_Reporting_New_Badge_Events_Sync instance.
*
* @var Conversion_Reporting_New_Badge_Events_Sync
*/
private $new_badge_events_sync;
/**
* Transients instance.
*
* @since 1.139.0
* @var Transients
*/
protected $transients;
/**
* Constructor.
*
* @since 1.135.0
* @since 1.139.0 Added $context param to constructor.
* @since 1.144.0 Added $transients and $new_badge_events_sync params to constructor, and removed $context.
*
* @param Settings $settings Settings module settings instance.
* @param Transients $transients Transients instance.
* @param Analytics_4 $analytics Analytics 4 module instance.
* @param Conversion_Reporting_New_Badge_Events_Sync $new_badge_events_sync Conversion_Reporting_New_Badge_Events_Sync instance.
*/
public function __construct(
Settings $settings,
Transients $transients,
Analytics_4 $analytics,
Conversion_Reporting_New_Badge_Events_Sync $new_badge_events_sync
) {
$this->settings = $settings;
$this->transients = $transients;
$this->analytics = $analytics;
$this->new_badge_events_sync = $new_badge_events_sync;
}
/**
* Syncs detected events into settings.
*
* @since 1.135.0
*/
public function sync_detected_events() {
$report = $this->get_report();
$detected_events = array();
if ( is_wp_error( $report ) ) {
return;
}
// Get current stored detected events.
$settings = $this->settings->get();
$saved_detected_events = isset( $settings['detectedEvents'] ) ? $settings['detectedEvents'] : array();
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
if ( empty( $report->rowCount ) ) {
$this->settings->merge( array( 'detectedEvents' => array() ) );
$this->transients->delete( self::DETECTED_EVENTS_TRANSIENT );
if ( ! empty( $saved_detected_events ) ) {
$this->transients->set( self::LOST_EVENTS_TRANSIENT, $saved_detected_events );
}
return;
}
foreach ( $report->rows as $row ) {
$detected_events[] = $row['dimensionValues'][0]['value'];
}
$settings_partial = array( 'detectedEvents' => $detected_events );
$this->maybe_update_new_and_lost_events(
$detected_events,
$saved_detected_events,
$settings_partial
);
$this->settings->merge( $settings_partial );
}
/**
* Saves new and lost events transients.
*
* @since 1.144.0
*
* @param array $detected_events Currently detected events array.
* @param array $saved_detected_events Previously saved detected events array.
* @param array $settings_partial Analaytics settings partial.
*/
protected function maybe_update_new_and_lost_events( $detected_events, $saved_detected_events, &$settings_partial ) {
$new_events = array_diff( $detected_events, $saved_detected_events );
$lost_events = array_diff( $saved_detected_events, $detected_events );
if ( ! empty( $new_events ) ) {
$this->transients->set( self::DETECTED_EVENTS_TRANSIENT, array_values( $new_events ) );
$this->new_badge_events_sync->sync_new_badge_events( $new_events );
$settings_partial['newConversionEventsLastUpdateAt'] = time();
// Remove new events from lost events if present.
$saved_lost_events = $this->transients->get( self::LOST_EVENTS_TRANSIENT );
if ( $saved_lost_events ) {
$filtered_lost_events = array_diff( $saved_lost_events, $new_events );
$lost_events = array_merge( $lost_events, $filtered_lost_events );
}
}
if ( ! empty( $lost_events ) ) {
$this->transients->set( self::LOST_EVENTS_TRANSIENT, array_values( $lost_events ) );
$settings_partial['lostConversionEventsLastUpdateAt'] = time();
}
if ( empty( $saved_detected_events ) ) {
$this->transients->set( self::DETECTED_EVENTS_TRANSIENT, $detected_events );
}
}
/**
* Retrieves the GA4 report for filtered events.
*
* @since 1.135.0
*/
protected function get_report() {
$options = array(
// The 'metrics' parameter is required. 'eventCount' is used to ensure the request succeeds.
'metrics' => array( array( 'name' => 'eventCount' ) ),
'dimensions' => array(
array(
'name' => 'eventName',
),
),
'startDate' => gmdate( 'Y-m-d', strtotime( '-90 days' ) ),
'endDate' => gmdate( 'Y-m-d', strtotime( '-1 day' ) ),
'dimensionFilters' => array(
'eventName' => array(
'filterType' => 'inListFilter',
'value' => self::EVENT_NAMES,
),
),
'limit' => '20',
);
return $this->analytics->get_data( 'report', $options );
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_New_Badge_Events_Sync
*
* @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\Conversion_Reporting;
use Google\Site_Kit\Core\Storage\Transients;
/**
* Class providing implementation of "new" badge for detected conversion reporting events.
*
* @since 1.144.0
* @access private
* @ignore
*/
class Conversion_Reporting_New_Badge_Events_Sync {
/**
* The detected events transient name.
*/
public const NEW_EVENTS_BADGE_TRANSIENT = 'googlesitekit_conversion_reporting_new_badge_events';
/**
* The skip new badge events transient name.
*/
public const SKIP_NEW_BADGE_TRANSIENT = 'googlesitekit_conversion_reporting_skip_new_badge_events';
/**
* Transients instance.
*
* @since 1.144.0
* @var Transients
*/
protected $transients;
/**
* Constructor.
*
* @since 1.144.0
*
* @param Transients $transients Transients instance.
*/
public function __construct(
Transients $transients
) {
$this->transients = $transients;
}
/**
* Saves new events badge to the expirable items.
*
* @since 1.144.0
*
* @param array $new_events New events array.
*/
public function sync_new_badge_events( $new_events ) {
$skip_events_badge = $this->transients->get( self::SKIP_NEW_BADGE_TRANSIENT );
if ( $skip_events_badge ) {
$this->transients->delete( self::SKIP_NEW_BADGE_TRANSIENT );
return;
}
$new_events_badge = $this->transients->get( self::NEW_EVENTS_BADGE_TRANSIENT );
$save_new_badge_transient = fn( $events ) => $this->transients->set(
self::NEW_EVENTS_BADGE_TRANSIENT,
array(
'created_at' => time(),
'events' => $events,
),
7 * DAY_IN_SECONDS
);
if ( ! $new_events_badge ) {
$save_new_badge_transient( $new_events );
return;
}
$new_events_badge_elapsed_time = time() - $new_events_badge['created_at'];
// If the transient existed for 3 days or less, prevent scenarios where
// a new event is detected shortly after (within 1-3 days) the previous events.
// This avoids shortening the "new badge" time for previous events.
// Instead, we merge the new events with the previous ones to ensure the user sees all of them.
if ( $new_events_badge_elapsed_time > ( 3 * DAY_IN_SECONDS ) ) {
$save_new_badge_transient( $new_events );
return;
}
$events = array_merge( $new_events_badge['events'], $new_events );
$save_new_badge_transient( $events );
}
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Provider
*
* @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\Conversion_Reporting;
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Storage\Transients;
use Google\Site_Kit\Core\Storage\User_Options;
use Google\Site_Kit\Modules\Analytics_4;
use Google\Site_Kit\Modules\Analytics_4\Settings;
/**
* Class providing the integration of conversion reporting.
*
* @since 1.135.0
* @access private
* @ignore
*/
class Conversion_Reporting_Provider {
/**
* User_Options instance.
*
* @var User_Options
*/
private $user_options;
/**
* Analytics_4 instance.
*
* @var Analytics_4
*/
private $analytics;
/**
* Conversion_Reporting_Cron instance.
*
* @var Conversion_Reporting_Cron
*/
private Conversion_Reporting_Cron $cron;
/**
* Conversion_Reporting_Events_Sync instance.
*
* @var Conversion_Reporting_Events_Sync
*/
private Conversion_Reporting_Events_Sync $events_sync;
/**
* Constructor.
*
* @since 1.135.0
* @since 1.139.0 Added Context to constructor.
*
* @param Context $context Plugin context.
* @param Settings $settings Settings instance.
* @param User_Options $user_options User_Options instance.
* @param Analytics_4 $analytics analytics_4 instance.
*/
public function __construct(
Context $context,
Settings $settings,
User_Options $user_options,
Analytics_4 $analytics
) {
$this->user_options = $user_options;
$this->analytics = $analytics;
$transients = new Transients( $context );
$new_badge_events_sync = new Conversion_Reporting_New_Badge_Events_Sync( $transients );
$this->events_sync = new Conversion_Reporting_Events_Sync(
$settings,
$transients,
$this->analytics,
$new_badge_events_sync
);
$this->cron = new Conversion_Reporting_Cron( fn() => $this->cron_callback() );
}
/**
* Registers functionality through WordPress hooks.
*
* @since 1.135.0
*/
public function register() {
$this->cron->register();
add_action( 'load-toplevel_page_googlesitekit-dashboard', fn () => $this->on_dashboard_load() );
}
/**
* Handles the googlesitekit-dashboard page load callback.
*
* @since 1.135.0
*/
protected function on_dashboard_load() {
$this->cron->maybe_schedule_cron();
}
/**
* Handles the cron callback.
*
* @since 1.135.0
*/
protected function cron_callback() {
$owner_id = $this->analytics->get_owner_id();
$restore_user = $this->user_options->switch_user( $owner_id );
$this->events_sync->sync_detected_events();
$restore_user();
}
}

View File

@@ -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 );
}
}

View File

@@ -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(),
),
),
)
);
}
}

View File

@@ -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
);
}
}

View File

@@ -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;
}
}

View File

@@ -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',
),
),
),
),
)
);
}
}

View File

@@ -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;
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',
),
),
),
),
)
);
}
}

View File

@@ -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 );
}
}

View File

@@ -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',
),
),
),
),
)
);
}
}

View File

@@ -0,0 +1,106 @@
<?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 = '';
/**
* The state of the show progress flag.
*
* @since 1.165.0
* @var bool
*/
public $show_progress = false;
/**
* 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;
}
/**
* Sets the show progress flag.
*
* @since 1.165.0
*
* @param bool $show_progress The show progress flag.
*/
public function setShowProgress( $show_progress ) {
$this->show_progress = $show_progress;
}
/**
* Gets the show progress flag.
*
* @since 1.165.0
*/
public function getShowProgress() {
return $this->show_progress;
}
}

View File

@@ -0,0 +1,46 @@
<?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\Modules\Analytics_4\Report\ReportParsers;
/**
* The base class for Analytics 4 reports.
*
* @since 1.99.0
* @access private
* @ignore
*/
class Report extends ReportParsers {
/**
* 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;
}
// NOTE: The majority of this classes logic has been abstracted to
// ReportParsers which contains the methods for the Report class.
}

View File

@@ -0,0 +1,57 @@
<?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;
/**
* 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;
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Report\Filters\Empty_Filter
*
* @package Google\Site_Kit\Modules\Analytics_4\Report\Filters
* @copyright 2025 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\EmptyFilter as Google_Service_AnalyticsData_EmptyFilter;
/**
* Class for parsing the empty filter.
*
* @since 1.147.0
* @access private
* @ignore
*/
class Empty_Filter implements Filter {
/**
* Parses the empty filter.
*
* @since 1.147.0
* @param string $name The filter field name.
* @param string $value The filter value (not used).
*
* @return Google_Service_AnalyticsData_FilterExpression The filter expression.
*/
public function parse_filter_expression( $name, $value ) {
$empty_filter = new Google_Service_AnalyticsData_EmptyFilter();
$filter = new Google_Service_AnalyticsData_Filter();
$filter->setFieldName( $name );
$filter->setEmptyFilter( $empty_filter );
$expression = new Google_Service_AnalyticsData_FilterExpression();
$expression->setFilter( $filter );
return $expression;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Report\Filter
*
* @package Google\Site_Kit\Modules\Analytics_4\Report\Filters
* @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\FilterExpression as Google_Service_AnalyticsData_FilterExpression;
/**
* Interface for a filter class.
*
* @since 1.106.0
* @since 1.147.0 Moved from `Analytics_4\Report\Dimension_Filters` to `Analytics_4\Report\Filters` for use with both dimensions and metrics.
*/
interface Filter {
/**
* Converts the filter into the GA4 compatible filter expression.
*
* @since 1.106.0
*
* @param string $name Filter name.
* @param mixed $value Filter value.
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
*/
public function parse_filter_expression( $name, $value );
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Report\In_List_Filter
*
* @package Google\Site_Kit\Modules\Analytics_4\Report\Filters
* @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\InListFilter as Google_Service_AnalyticsData_InListFilter;
/**
* Class for parsing the in-list filter.
*
* @since 1.106.0
* @since 1.147.0 Moved from `Analytics_4\Report\Dimension_Filters` to `Analytics_4\Report\Filters` for use with both dimensions and metrics.
* @access private
* @ignore
*/
class In_List_Filter implements Filter {
/**
* Converts the filter into the GA4 compatible filter expression.
*
* @since 1.106.0
*
* @param string $name The filter field name.
* @param mixed $value The filter value.
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
*/
public function parse_filter_expression( $name, $value ) {
$in_list_filter = new Google_Service_AnalyticsData_InListFilter();
$in_list_filter->setValues( $value );
$filter = new Google_Service_AnalyticsData_Filter();
$filter->setFieldName( $name );
$filter->setInListFilter( $in_list_filter );
$expression = new Google_Service_AnalyticsData_FilterExpression();
$expression->setFilter( $filter );
return $expression;
}
}

View File

@@ -0,0 +1,54 @@
<?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;
/**
* 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;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Report\String_Filter
*
* @package Google\Site_Kit\Modules\Analytics_4\Report\Filters
* @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\FilterExpressionList as Google_Service_AnalyticsData_FilterExpressionList;
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\StringFilter as Google_Service_AnalyticsData_StringFilter;
/**
* Class for parsing the string filter.
*
* @since 1.106.0
* @since 1.147.0 Moved from `Analytics_4\Report\Dimension_Filters` to `Analytics_4\Report\Filters` for use with both dimensions and metrics.
* @access private
* @ignore
*/
class String_Filter implements Filter {
/**
* Converts the filter into the GA4 compatible filter expression.
*
* @since 1.106.0
*
* @param string $name The filter field name.
* @param mixed $value The filter value.
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
*/
public function parse_filter_expression( $name, $value ) {
$match_type = isset( $value['matchType'] )
? $value['matchType']
: 'EXACT';
$filter_value = isset( $value['value'] )
? $value['value']
: $value;
// If there are many values for this filter, then it means that we want to find
// rows where values 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(
$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(
$name,
$match_type,
$filter_value
);
}
/**
* Composes individual filter expression and returns it.
*
* @since 1.106.0
*
* @param string $name Filter name.
* @param string $match_type Filter match type.
* @param mixed $value Filter value.
* @return Google_Service_AnalyticsData_FilterExpression The filter expression instance.
*/
protected function compose_individual_filter_expression( $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( $name );
$filter->setStringFilter( $string_filter );
$filter_expression = new Google_Service_AnalyticsData_FilterExpression();
$filter_expression->setFilter( $filter );
return $filter_expression;
}
}

View File

@@ -0,0 +1,173 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Report\ReportParsers
*
* @package Google\Site_Kit\Modules\Analytics_4\Report
* @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\Report;
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;
/**
* A class with helper methods to parse report properties
*
* @since 1.130.0
* @access private
* @ignore
*/
class ReportParsers {
/**
* Parses report dimensions received in the request params.
*
* @since 1.99.0
* @since 1.130.0 Moved into `ReportParsers` for shared used (originally between `Report` and `PivotReport`). `PivotReport` has since been removed.
*
* @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
* @since 1.130.0 Moved into `ReportParsers` for shared used (originally between `Report` and `PivotReport`). `PivotReport` has since been removed.
* @since 1.157.0 Added support for dateRangeName and compareDateRangeName parameters.
*
* @param Data_Request $data Data request object.
* @return Google_Service_AnalyticsData_DateRange[] An array of AnalyticsData DateRange objects.
*/
public 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 );
}
// Get date range names if provided.
$date_range_name = $data['dateRangeName'] ?? '';
$compare_date_range_name = $data['compareDateRangeName'] ?? '';
$date_ranges = array_map(
function ( $date_range, $index ) use ( $date_range_name, $compare_date_range_name ) {
list ( $start_date, $end_date ) = $date_range;
$date_range_obj = new Google_Service_AnalyticsData_DateRange();
$date_range_obj->setStartDate( $start_date );
$date_range_obj->setEndDate( $end_date );
// Set date range names if provided.
if ( 0 === $index && ! empty( $date_range_name ) ) {
$date_range_obj->setName( $date_range_name );
} elseif ( 1 === $index && ! empty( $compare_date_range_name ) ) {
$date_range_obj->setName( $compare_date_range_name );
}
return $date_range_obj;
},
$date_ranges,
array_keys( $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
* @since 1.130.0 Moved into `ReportParsers` for shared used (originally between `Report` and `PivotReport`). `PivotReport` has since been removed.
*
* @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;
}
}

View File

@@ -0,0 +1,73 @@
<?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\Modules\Analytics_4\Report;
use Google\Site_Kit\Modules\Analytics_4\Report\RequestHelpers;
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\RunReportRequest as Google_Service_AnalyticsData_RunReportRequest;
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_helpers = new RequestHelpers( $this->context );
$request = new Google_Service_AnalyticsData_RunReportRequest();
$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 {
$request_helpers->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 );
}
$request = $request_helpers->shared_create_request( $data, $request, $is_shared_request );
$orderby = $this->parse_orderby( $data );
if ( ! empty( $orderby ) ) {
$request->setOrderBys( $orderby );
}
return $request;
}
}

View File

@@ -0,0 +1,537 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Report\SharedRequestHelpers
*
* @package Google\Site_Kit\Modules\Analytics_4\Report
* @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\Report;
use Google\Site_Kit\Context;
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\Filters\Empty_Filter;
use Google\Site_Kit\Modules\Analytics_4\Report\Filters\In_List_Filter;
use Google\Site_Kit\Modules\Analytics_4\Report\Filters\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\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;
/**
* A class containing shared methods for creating AnalyticsData Report requests.
*
* @since 1.130.0
* @access private
* @ignore
*/
class RequestHelpers {
/**
* Plugin context.
*
* @since 1.130.0
* @var Context
*/
private $context;
/**
* Constructs a new instance of the class.
*
* @param Context $context Plugin context.
*/
public function __construct( $context ) {
$this->context = $context;
}
/**
* Builds a Analytics Data Report request's shared properties.
*
* @since 1.130.0
*
* @param Data_Request $data Data request object.
* @param Google_Service_AnalyticsData_RunReportRequest $request The report request object.
* @param bool $is_shared_request Determines whether the current request is shared or not.
* @return Google_Service_AnalyticsData_RunReportRequest The report request object.
*/
public function shared_create_request( Data_Request $data, $request, $is_shared_request = false ) {
$keep_empty_rows = is_array( $data->data ) && array_key_exists( 'keepEmptyRows', $data->data ) ? filter_var( $data->data['keepEmptyRows'], FILTER_VALIDATE_BOOLEAN ) : true;
$request->setKeepEmptyRows( $keep_empty_rows );
$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 );
}
$report_parsers = new ReportParsers();
$date_ranges = $report_parsers->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 );
}
}
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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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',
'addToCarts',
'averageSessionDuration',
'bounceRate',
'keyEvents',
'ecommercePurchases',
'engagedSessions',
'engagementRate',
'eventCount',
'screenPageViews',
'screenPageViewsPerSession',
'sessions',
'sessionKeyEventRate',
'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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @param Google_Service_AnalyticsData_Dimension[] $dimensions The dimensions to validate.
* @throws Invalid_Report_Dimensions_Exception Thrown if the dimensions are invalid.
*/
public function validate_shared_dimensions( $dimensions ) {
$valid_dimensions = apply_filters(
'googlesitekit_shareable_analytics_4_dimensions',
array(
'audienceResourceName',
'adSourceName',
'city',
'country',
'date',
'deviceCategory',
'eventName',
'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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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'];
}
} elseif ( 'emptyFilter' === $filter_type ) {
$filter_class = Empty_Filter::class;
} 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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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
* @since 1.130.0 Moved into RequestHelpers for shared use in reports.
*
* @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;
}
}

View File

@@ -0,0 +1,235 @@
<?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, $date_ranges, $ranges_count, $metric_headers, $multiple_ranges ) {
for ( $i = 0; $i < $ranges_count; $i++ ) {
$date_range_name = $date_ranges[ $i ]->getName();
if ( empty( $date_range_name ) ) {
$date_range_name = $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 ? $date_range_name : 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 );
}
}
}

View File

@@ -0,0 +1,70 @@
<?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(
is_numeric( $date_range_index )
? "date_range_{$date_range_index}"
: $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;
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* Class Google\Site_Kit\Modules\Analytics_4\Reset_Audiences
*
* @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\Dismissals\Dismissed_Items;
use Google\Site_Kit\Core\Prompts\Dismissed_Prompts;
use Google\Site_Kit\Core\Storage\User_Options;
use Google\Site_Kit\Core\User\Audience_Settings;
use Google\Site_Kit\Modules\Analytics_4;
/**
* Class to reset Audience Segmentation Settings across multiple users.
*
* @since 1.137.0
* @access private
* @ignore
*/
class Reset_Audiences {
/**
* User_Options instance.
*
* @since 1.137.0
* @var User_Options
*/
protected $user_options;
/**
* Dismissed_Prompts instance.
*
* @since 1.137.0
* @var Dismissed_Prompts
*/
protected $dismissed_prompts;
/**
* Dismissed_Items instance.
*
* @since 1.137.0
* @var Dismissed_Items
*/
protected $dismissed_items;
/**
* Audience Settings instance.
*
* @since 1.137.0
* @var Audience_Settings
*/
protected $audience_settings;
const AUDIENCE_SEGMENTATION_DISMISSED_PROMPTS = array( 'audience_segmentation_setup_cta-notification' );
const AUDIENCE_SEGMENTATION_DISMISSED_ITEMS = array(
'audience-segmentation-add-group-notice',
'setup-success-notification-audiences',
'settings_visitor_groups_setup_success_notification',
'audience-segmentation-no-audiences-banner',
'audience-tile-*',
);
/**
* Constructor.
*
* @since 1.137.0
*
* @param User_Options $user_options User option API.
*/
public function __construct( ?User_Options $user_options = null ) {
$this->user_options = $user_options;
$this->dismissed_prompts = new Dismissed_Prompts( $this->user_options );
$this->dismissed_items = new Dismissed_Items( $this->user_options );
$this->audience_settings = new Audience_Settings( $this->user_options );
}
/**
* Reset audience specific settings for all SK users.
*
* @since 1.137.0
*/
public function reset_audience_data() {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
$users = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT user_id
FROM $wpdb->usermeta
WHERE meta_key IN (%s, %s)
LIMIT 100 -- Arbitrary limit to avoid unbounded user iteration.",
$this->user_options->get_meta_key( Dismissed_Items::OPTION ),
$this->user_options->get_meta_key( Dismissed_Prompts::OPTION ),
)
);
if ( $users ) {
$backup_user_id = $this->user_options->get_user_id();
foreach ( $users as $user_id ) {
$this->user_options->switch_user( $user_id );
// Remove Audience Segmentation specific dismissed prompts.
foreach ( self::AUDIENCE_SEGMENTATION_DISMISSED_PROMPTS as $prompt ) {
$this->dismissed_prompts->remove( $prompt );
}
// Remove Audience Segmentation specific dismissed items.
foreach ( self::AUDIENCE_SEGMENTATION_DISMISSED_ITEMS as $item ) {
// Support wildcard matches, in order to delete all dismissed items prefixed with audience-tile-*.
if ( strpos( $item, '*' ) !== false ) {
$dismissed_items = $this->dismissed_items->get();
foreach ( array_keys( $dismissed_items ) as $existing_item ) {
if ( str_starts_with( $existing_item, rtrim( $item, '*' ) ) ) {
$this->dismissed_items->remove( $existing_item );
}
}
} else {
// For non-wildcard items, remove them directly.
$this->dismissed_items->remove( $item );
}
}
// Reset the user's audience settings.
if ( $this->audience_settings->has() ) {
$this->audience_settings->merge(
array(
'configuredAudiences' => null,
'didSetAudiences' => false,
),
);
}
}
// Restore original user.
$this->user_options->switch_user( $backup_user_id );
}
}
}

View File

@@ -0,0 +1,266 @@
<?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\Options_Interface;
use Google\Site_Kit\Core\Storage\Transients;
use Google\Site_Kit\Core\Util\Feature_Flags;
use Google\Site_Kit\Modules\Analytics_4\Audience_Settings;
/**
* 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;
/**
* Options instance.
*
* @since 1.151.0
* @var Audience_Settings
*/
protected $audience_settings;
/**
* Constructor.
*
* @since 1.127.0
*
* @param Transients $transients Transients instance.
* @param Module_Settings $settings Module settings instance.
* @param Audience_Settings $audience_settings Audience_Settings instance.
*/
public function __construct( Transients $transients, Module_Settings $settings, Audience_Settings $audience_settings ) {
$this->transients = $transients;
$this->settings = $settings;
$this->audience_settings = $audience_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() {
$available_audiences = $this->audience_settings->get();
$available_audiences = $available_audiences['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'];
}
}

View File

@@ -0,0 +1,216 @@
<?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
*/
// phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
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',
'detectedEvents',
'newConversionEventsLastUpdateAt',
'lostConversionEventsLastUpdateAt',
);
}
/**
* 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,
'detectedEvents' => array(),
'newConversionEventsLastUpdateAt' => 0,
'lostConversionEventsLastUpdateAt' => 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['newConversionEventsLastUpdateAt'] ) ) {
if ( ! is_int( $option['newConversionEventsLastUpdateAt'] ) ) {
$option['newConversionEventsLastUpdateAt'] = 0;
}
}
if ( isset( $option['lostConversionEventsLastUpdateAt'] ) ) {
if ( ! is_int( $option['lostConversionEventsLastUpdateAt'] ) ) {
$option['lostConversionEventsLastUpdateAt'] = 0;
}
}
}
return $option;
};
}
}

View File

@@ -0,0 +1,161 @@
<?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
* @since 1.130.0 Added check for property ID, so it can return early if property ID is not set.
*/
protected function synchronize_adsense_linked_data() {
$owner_id = $this->analytics_4->get_owner_id();
$restore_user = $this->user_options->switch_user( $owner_id );
$settings_ga4 = $this->analytics_4->get_settings()->get();
if ( empty( $settings_ga4['propertyID'] ) ) {
return;
}
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(),
)
);
}
}

View File

@@ -0,0 +1,137 @@
<?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\Ads;
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() {
$ads_connected = apply_filters( 'googlesitekit_is_module_connected', false, Ads::MODULE_SLUG );
if ( $ads_connected ) {
return;
}
$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(
time() + ( WEEK_IN_SECONDS ),
self::CRON_SYNCHRONIZE_ADS_LINKED
);
}
}
}

View File

@@ -0,0 +1,171 @@
<?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'];
$has_property_access = $this->analytics_4->has_property_access( $property_id );
if ( is_wp_error( $has_property_access ) || ! $has_property_access ) {
return null;
}
$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 );
}
}

View File

@@ -0,0 +1,35 @@
<?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'] );
}
}

View File

@@ -0,0 +1,29 @@
<?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 custom dimensions data.
*
* @since 1.113.0
*
* @param string $custom_dimensions Custom dimensions data.
*/
public function set_custom_dimensions( $custom_dimensions );
}

View File

@@ -0,0 +1,55 @@
<?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;
}
}

View File

@@ -0,0 +1,197 @@
<?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_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;
use Tag_With_Linker_Trait;
/**
* Custom dimensions data.
*
* @since 1.113.0
* @var array
*/
private $custom_dimensions;
/**
* 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;
}
/**
* 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' ) );
add_filter( 'script_loader_tag', $this->get_method_proxy( 'filter_tag_output' ), 10, 2 );
$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 );
}
/**
* Filters output of tag HTML.
*
* @param string $tag Tag HTML.
* @param string $handle WP script handle of given tag.
* @return string
*/
protected function filter_tag_output( $tag, $handle ) {
// The tag will either have its own handle or use the common GTag handle, not both.
if ( GTag::get_handle_for_tag( $this->tag_id ) !== $handle && GTag::HANDLE !== $handle ) {
return $tag;
}
// Retain this comment for detection of Site Kit placed tag.
$snippet_comment = sprintf( "<!-- %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 ) {
$tag = $this->add_legacy_block_on_consent_attributes( $tag, $block_on_consent_attrs );
}
return $snippet_comment . $tag;
}
/**
* 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
* @since 1.158.0 Remove src from signature & replacement.
*
* @param string $tag The script tag.
* @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, $block_on_consent_attrs ) {
return str_replace(
array(
'<script src=', // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
"<script type='text/javascript' src=", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
'<script type="text/javascript" src=', // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
),
// `type` attribute intentionally excluded in replacements.
"<script{$block_on_consent_attrs} src=", // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
$tag
);
}
}