Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Audience Reset class to reset Audience settings across all users on Analytics property change. #9328

Merged
14 changes: 13 additions & 1 deletion includes/Modules/Analytics_4.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Cron;
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Events_Sync;
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Provider;
use Google\Site_Kit\Modules\Analytics_4\Reset_Audiences;
use stdClass;
use WP_Error;

Expand Down Expand Up @@ -152,6 +153,14 @@ final class Analytics_4 extends Module implements Module_With_Scopes, Module_Wit
*/
protected $custom_dimensions_data_available;

/**
* Reset_Audiences instance.
*
* @since n.e.x.t
* @var Reset_Audiences
*/
protected $reset_audiences;

/**
* Resource_Data_Availability_Date instance.
*
Expand Down Expand Up @@ -180,6 +189,7 @@ public function __construct(
) {
parent::__construct( $context, $options, $user_options, $authentication, $assets );
$this->custom_dimensions_data_available = new Custom_Dimensions_Data_Available( $this->transients );
$this->reset_audiences = new Reset_Audiences( $this->user_options, $this );
$this->resource_data_availability_date = new Resource_Data_Availability_Date( $this->transients, $this->get_settings() );
}

Expand Down Expand Up @@ -222,6 +232,8 @@ public function register() {

( new Advanced_Tracking( $this->context ) )->register();

$this->reset_audiences->register();

add_action( 'admin_init', array( $synchronize_property, 'maybe_schedule_synchronize_property' ) );
add_action( 'admin_init', array( $synchronize_adsense_linked, 'maybe_schedule_synchronize_adsense_linked' ) );
add_action( 'load-toplevel_page_googlesitekit-dashboard', array( $synchronize_ads_linked, 'maybe_schedule_synchronize_ads_linked' ) );
Expand Down Expand Up @@ -288,7 +300,6 @@ function ( $audience ) {
'adsLinked' => false,
'adsLinkedLastSyncedAt' => 0,
'detectedEvents' => array(),
'availableAudiencesLastSyncedAt' => 0,
)
);

Expand Down Expand Up @@ -454,6 +465,7 @@ public function on_deactivation() {
$this->get_settings()->delete();
$this->reset_data_available();
$this->custom_dimensions_data_available->reset_data_available();
$this->reset_audiences->reset_audience_data();
}

/**
Expand Down
175 changes: 175 additions & 0 deletions includes/Modules/Analytics_4/Reset_Audiences.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<?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 n.e.x.t
* @access private
* @ignore
*/
class Reset_Audiences {

/**
* User_Options instance.
*
* @since n.e.x.t
* @var User_Options
*/
protected $user_options;

/**
* Dismissed_Prompts instance.
*
* @since n.e.x.t
* @var Dismissed_Prompts
*/
protected $dismissed_prompts;

/**
* Dismissed_Items instance.
*
* @since n.e.x.t
* @var Dismissed_Items
*/
protected $dismissed_items;

/**
* Analytics_4 instance.
*
* @since n.e.x.t
* @var Analytics_4
*/
protected $analytics;

/**
* Audience Settings instance.
*
* @since n.e.x.t
* @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',
'audience_segmentation_setup_success_notification',
'settings_visitor_groups_setup_success_notification',
'audience-segmentation-no-audiences-banner',
'audience-tile-*',
);

/**
* Constructor.
*
* @since n.e.x.t
*
* @param User_Options $user_options User option API.
* @param Analytics_4 $analytics Analytics_4 instance.
*/
public function __construct( User_Options $user_options = null, Analytics_4 $analytics ) {
$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 );
$this->analytics = $analytics;
}

/**
* Register on change actions.
*
* @since n.e.x.t
*/
public function register() {
$this->analytics->get_settings()->on_change(
function ( $old_value, $new_value ) {
// Reset Audience specific settings, only when the Analytics propertyID changes.
if ( $old_value['propertyID'] !== $new_value['propertyID'] ) {
$this->reset_audience_data();
}
}
);
}

/**
* Reset audience specific settings for all SK users.
*
* @since n.e.x.t
*/
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 users audience settings, such as configured audiences.
$this->audience_settings->delete();
}

// Restore original user.
$this->user_options->switch_user( $backup_user_id );
}

// Reset the main Analytics Module, Audience Segmentation settings.
$this->analytics->get_settings()->merge(
array(
'availableAudiences' => null,
'availableAudiencesLastSyncedAt' => 0,
'audienceSegmentationSetupComplete' => false,
)
);
nfmohit marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading