Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/google-site-kit/includes/Modules/Analytics_4.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* Class Google\Site_Kit\Modules\Analytics_4
4
+
*
5
+
* @package Google\Site_Kit
6
+
* @copyright 2021 Google LLC
7
+
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
8
+
* @link https://sitekit.withgoogle.com
9
+
*/
10
+
11
+
// phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
12
+
13
+
namespace Google\Site_Kit\Modules;
14
+
15
+
use Exception;
16
+
use Google\Site_Kit\Context;
17
+
use Google\Site_Kit\Core\Assets\Asset;
18
+
use Google\Site_Kit\Core\Assets\Assets;
19
+
use Google\Site_Kit\Core\Assets\Script;
20
+
use Google\Site_Kit\Core\Authentication\Authentication;
21
+
use Google\Site_Kit\Core\Authentication\Clients\Google_Site_Kit_Client;
22
+
use Google\Site_Kit\Core\Dismissals\Dismissed_Items;
23
+
use Google\Site_Kit\Core\Modules\Analytics_4\Tag_Matchers;
24
+
use Google\Site_Kit\Core\Modules\Module;
25
+
use Google\Site_Kit\Core\Modules\Module_Settings;
26
+
use Google\Site_Kit\Core\Modules\Module_With_Activation;
27
+
use Google\Site_Kit\Core\Modules\Module_With_Deactivation;
28
+
use Google\Site_Kit\Core\Modules\Module_With_Debug_Fields;
29
+
use Google\Site_Kit\Core\Modules\Module_With_Assets;
30
+
use Google\Site_Kit\Core\Modules\Module_With_Assets_Trait;
31
+
use Google\Site_Kit\Core\Modules\Module_With_Data_Available_State;
32
+
use Google\Site_Kit\Core\Modules\Module_With_Data_Available_State_Trait;
33
+
use Google\Site_Kit\Core\Modules\Module_With_Inline_Data;
34
+
use Google\Site_Kit\Core\Modules\Module_With_Inline_Data_Trait;
35
+
use Google\Site_Kit\Core\Modules\Module_With_Scopes;
36
+
use Google\Site_Kit\Core\Modules\Module_With_Scopes_Trait;
37
+
use Google\Site_Kit\Core\Modules\Module_With_Settings;
38
+
use Google\Site_Kit\Core\Modules\Module_With_Settings_Trait;
39
+
use Google\Site_Kit\Core\Modules\Module_With_Owner;
40
+
use Google\Site_Kit\Core\Modules\Module_With_Owner_Trait;
41
+
use Google\Site_Kit\Core\Modules\Module_With_Service_Entity;
42
+
use Google\Site_Kit\Core\Permissions\Permissions;
43
+
use Google\Site_Kit\Core\Modules\Module_With_Tag;
44
+
use Google\Site_Kit\Core\Modules\Module_With_Tag_Trait;
45
+
use Google\Site_Kit\Core\Modules\Tags\Module_Tag_Matchers;
46
+
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Datapoint_Exception;
47
+
use Google\Site_Kit\Core\REST_API\Data_Request;
48
+
use Google\Site_Kit\Core\REST_API\Exception\Invalid_Param_Exception;
49
+
use Google\Site_Kit\Core\REST_API\Exception\Missing_Required_Param_Exception;
50
+
use Google\Site_Kit\Core\Site_Health\Debug_Data;
51
+
use Google\Site_Kit\Core\Storage\Options;
52
+
use Google\Site_Kit\Core\Storage\User_Options;
53
+
use Google\Site_Kit\Core\Tags\Guards\Tag_Environment_Type_Guard;
54
+
use Google\Site_Kit\Core\Tags\Guards\Tag_Verify_Guard;
55
+
use Google\Site_Kit\Core\Util\BC_Functions;
56
+
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;
57
+
use Google\Site_Kit\Core\Util\Sort;
58
+
use Google\Site_Kit\Core\Util\URL;
59
+
use Google\Site_Kit\Modules\AdSense\Settings as AdSense_Settings;
60
+
use Google\Site_Kit\Modules\Analytics_4\Account_Ticket;
61
+
use Google\Site_Kit\Modules\Analytics_4\Advanced_Tracking;
62
+
use Google\Site_Kit\Modules\Analytics_4\AMP_Tag;
63
+
use Google\Site_Kit\Modules\Analytics_4\Custom_Dimensions_Data_Available;
64
+
use Google\Site_Kit\Modules\Analytics_4\Datapoints\Create_Account_Ticket;
65
+
use Google\Site_Kit\Modules\Analytics_4\Datapoints\Create_Property;
66
+
use Google\Site_Kit\Modules\Analytics_4\Datapoints\Create_Webdatastream;
67
+
use Google\Site_Kit\Modules\Analytics_4\Synchronize_Property;
68
+
use Google\Site_Kit\Modules\Analytics_4\Synchronize_AdSenseLinked;
69
+
use Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin\AccountProvisioningService;
70
+
use Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin\EnhancedMeasurementSettingsModel;
71
+
use Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin\PropertiesAdSenseLinksService;
72
+
use Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin\PropertiesAudiencesService;
73
+
use Google\Site_Kit\Modules\Analytics_4\GoogleAnalyticsAdmin\PropertiesEnhancedMeasurementService;
74
+
use Google\Site_Kit\Modules\Analytics_4\Report\Request as Analytics_4_Report_Request;
75
+
use Google\Site_Kit\Modules\Analytics_4\Report\Response as Analytics_4_Report_Response;
76
+
use Google\Site_Kit\Modules\Analytics_4\Resource_Data_Availability_Date;
77
+
use Google\Site_Kit\Modules\Analytics_4\Settings;
78
+
use Google\Site_Kit\Modules\Analytics_4\Synchronize_AdsLinked;
79
+
use Google\Site_Kit\Modules\Analytics_4\Tag_Guard;
80
+
use Google\Site_Kit\Modules\Analytics_4\Tag_Interface;
81
+
use Google\Site_Kit\Modules\Analytics_4\Web_Tag;
82
+
use Google\Site_Kit_Dependencies\Google\Model as Google_Model;
83
+
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData as Google_Service_AnalyticsData;
84
+
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\RunReportRequest as Google_Service_AnalyticsData_RunReportRequest;
85
+
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\DateRange as Google_Service_AnalyticsData_DateRange;
86
+
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Dimension as Google_Service_AnalyticsData_Dimension;
87
+
use Google\Site_Kit_Dependencies\Google\Service\AnalyticsData\Metric as Google_Service_AnalyticsData_Metric;
88
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin as Google_Service_GoogleAnalyticsAdmin;
89
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1alphaAudience;
90
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaCustomDimension;
91
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaDataStream;
92
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaDataStreamWebStreamData;
93
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaListDataStreamsResponse;
94
+
use Google\Site_Kit_Dependencies\Google\Service\GoogleAnalyticsAdmin\GoogleAnalyticsAdminV1betaProperty as Google_Service_GoogleAnalyticsAdmin_GoogleAnalyticsAdminV1betaProperty;
95
+
use Google\Site_Kit_Dependencies\Google\Service\TagManager as Google_Service_TagManager;
96
+
use Google\Site_Kit_Dependencies\Google_Service_TagManager_Container;
97
+
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface;
98
+
use Google\Site_Kit\Core\REST_API\REST_Routes;
99
+
use Google\Site_Kit\Core\Tracking\Feature_Metrics_Trait;
100
+
use Google\Site_Kit\Core\Tracking\Provides_Feature_Metrics;
101
+
use Google\Site_Kit\Core\Util\Feature_Flags;
102
+
use Google\Site_Kit\Modules\Analytics_4\Audience_Settings;
103
+
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Cron;
104
+
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Events_Sync;
105
+
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_New_Badge_Events_Sync;
106
+
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Provider;
107
+
use Google\Site_Kit\Modules\Analytics_4\Reset_Audiences;
108
+
use stdClass;
109
+
use WP_Error;
110
+
use WP_Post;
111
+
112
+
/**
113
+
* Class representing the Analytics 4 module.
114
+
*
115
+
* @since 1.30.0
116
+
* @access private
117
+
* @ignore
118
+
*/
119
+
final class Analytics_4 extends Module implements Module_With_Inline_Data, Module_With_Scopes, Module_With_Settings, Module_With_Debug_Fields, Module_With_Owner, Module_With_Assets, Module_With_Service_Entity, Module_With_Activation, Module_With_Deactivation, Module_With_Data_Available_State, Module_With_Tag, Provides_Feature_Metrics {
120
+
121
+
use Method_Proxy_Trait;
122
+
use Module_With_Assets_Trait;
123
+
use Module_With_Owner_Trait;
124
+
use Module_With_Scopes_Trait;
125
+
use Module_With_Settings_Trait;
126
+
use Module_With_Data_Available_State_Trait;
127
+
use Module_With_Tag_Trait;
128
+
use Module_With_Inline_Data_Trait;
129
+
use Feature_Metrics_Trait;
130
+
131
+
const PROVISION_ACCOUNT_TICKET_ID = 'googlesitekit_analytics_provision_account_ticket_id';
132
+
133
+
const READONLY_SCOPE = 'https://www.googleapis.com/auth/analytics.readonly';
134
+
const EDIT_SCOPE = 'https://www.googleapis.com/auth/analytics.edit';
135
+
136
+
/**
137
+
* Module slug name.
138
+
*/
139
+
const MODULE_SLUG = 'analytics-4';
140
+
141
+
/**
142
+
* Prefix used to fetch custom dimensions in reports.
143
+
*/
144
+
const CUSTOM_EVENT_PREFIX = 'customEvent:';
145
+
146
+
/**
147
+
* Custom dimensions tracked by Site Kit.
148
+
*/
149
+
const CUSTOM_DIMENSION_POST_AUTHOR = 'googlesitekit_post_author';
150
+
const CUSTOM_DIMENSION_POST_CATEGORIES = 'googlesitekit_post_categories';
151
+
152
+
/**
153
+
* Weights for audience types when sorting audiences in the selection panel
154
+
* and within the dashboard widget.
155
+
*/
156
+
const AUDIENCE_TYPE_SORT_ORDER = array(
157
+
'USER_AUDIENCE' => 0,
158
+
'SITE_KIT_AUDIENCE' => 1,
159
+
'DEFAULT_AUDIENCE' => 2,
160
+
);
161
+
162
+
/**
163
+
* Custom_Dimensions_Data_Available instance.
164
+
*
165
+
* @since 1.113.0
166
+
* @var Custom_Dimensions_Data_Available
167
+
*/
168
+
protected $custom_dimensions_data_available;
169
+
170
+
/**
171
+
* Reset_Audiences instance.
172
+
*
173
+
* @since 1.137.0
174
+
* @var Reset_Audiences
175
+
*/
176
+
protected $reset_audiences;
177
+
178
+
/**
179
+
* Resource_Data_Availability_Date instance.
180
+
*
181
+
* @since 1.127.0
182
+
* @var Resource_Data_Availability_Date
183
+
*/
184
+
protected $resource_data_availability_date;
185
+
186
+
/**
187
+
* Audience_Settings instance.
188
+
*
189
+
* @since 1.148.0
190
+
*
191
+
* @var Audience_Settings
192
+
*/
193
+
protected $audience_settings;
194
+
195
+
/**
196
+
* Constructor.
197
+
*
198
+
* @since 1.113.0
199
+
*
200
+
* @param Context $context Plugin context.
201
+
* @param Options $options Optional. Option API instance. Default is a new instance.
202
+
* @param User_Options $user_options Optional. User Option API instance. Default is a new instance.
203
+
* @param Authentication $authentication Optional. Authentication instance. Default is a new instance.
204
+
* @param Assets $assets Optional. Assets API instance. Default is a new instance.
205
+
*/
206
+
public function __construct(
207
+
Context $context,
208
+
?Options $options = null,
209
+
?User_Options $user_options = null,
210
+
?Authentication $authentication = null,
211
+
?Assets $assets = null
212
+
) {
213
+
parent::__construct( $context, $options, $user_options, $authentication, $assets );
214
+
$this->custom_dimensions_data_available = new Custom_Dimensions_Data_Available( $this->transients );
215
+
$this->reset_audiences = new Reset_Audiences( $this->user_options );
216
+
$this->audience_settings = new Audience_Settings( $this->options );
217
+
$this->resource_data_availability_date = new Resource_Data_Availability_Date( $this->transients, $this->get_settings(), $this->audience_settings );
218
+
}
219
+
220
+
/**
221
+
* Registers functionality through WordPress hooks.
222
+
*
223
+
* @since 1.30.0
224
+
* @since 1.101.0 Added a filter hook to add the required `https://www.googleapis.com/auth/tagmanager.readonly` scope for GTE support.
225
+
*/
226
+
public function register() {
227
+
$this->register_scopes_hook();
228
+
229
+
$this->register_inline_data();
230
+
231
+
$this->register_feature_metrics();
232
+
233
+
$synchronize_property = new Synchronize_Property(
234
+
$this,
235
+
$this->user_options
236
+
);
237
+
$synchronize_property->register();
238
+
239
+
$synchronize_adsense_linked = new Synchronize_AdSenseLinked(
240
+
$this,
241
+
$this->user_options,
242
+
$this->options
243
+
);
244
+
$synchronize_adsense_linked->register();
245
+
246
+
$synchronize_ads_linked = new Synchronize_AdsLinked(
247
+
$this,
248
+
$this->user_options
249
+
);
250
+
$synchronize_ads_linked->register();
251
+
252
+
$conversion_reporting_provider = new Conversion_Reporting_Provider(
253
+
$this->context,
254
+
$this->settings,
255
+
$this->user_options,
256
+
$this
257
+
);
258
+
$conversion_reporting_provider->register();
259
+
260
+
$this->audience_settings->register();
261
+
262
+
( new Advanced_Tracking( $this->context ) )->register();
263
+
264
+
add_action( 'admin_init', array( $synchronize_property, 'maybe_schedule_synchronize_property' ) );
265
+
add_action( 'admin_init', array( $synchronize_adsense_linked, 'maybe_schedule_synchronize_adsense_linked' ) );
266
+
add_action( 'load-toplevel_page_googlesitekit-dashboard', array( $synchronize_ads_linked, 'maybe_schedule_synchronize_ads_linked' ) );
267
+
add_action( 'admin_init', $this->get_method_proxy( 'handle_provisioning_callback' ) );
268
+
269
+
// For non-AMP and AMP.
270
+
add_action( 'wp_head', $this->get_method_proxy( 'print_tracking_opt_out' ), 0 );
271
+
// For Web Stories plugin.
272
+
add_action( 'web_stories_story_head', $this->get_method_proxy( 'print_tracking_opt_out' ), 0 );
273
+
274
+
// Analytics 4 tag placement logic.
275
+
add_action( 'template_redirect', array( $this, 'register_tag' ) );
276
+
277
+
$this->audience_settings->on_change(
278
+
function ( $old_value, $new_value ) {
279
+
// Ensure that the resource data availability dates for `availableAudiences` that no longer exist are reset.
280
+
$old_available_audiences = $old_value['availableAudiences'];
281
+
if ( $old_available_audiences ) {
282
+
$old_available_audience_names = array_map(
283
+
function ( $audience ) {
284
+
return $audience['name'];
285
+
},
286
+
$old_available_audiences
287
+
);
288
+
289
+
$new_available_audiences = $new_value['availableAudiences'] ?? array();
290
+
$new_available_audience_names = array_map(
291
+
function ( $audience ) {
292
+
return $audience['name'];
293
+
},
294
+
$new_available_audiences
295
+
);
296
+
297
+
$unavailable_audience_names = array_diff( $old_available_audience_names, $new_available_audience_names );
298
+
299
+
foreach ( $unavailable_audience_names as $unavailable_audience_name ) {
300
+
$this->resource_data_availability_date->reset_resource_date( $unavailable_audience_name, Resource_Data_Availability_Date::RESOURCE_TYPE_AUDIENCE );
301
+
}
302
+
}
303
+
}
304
+
);
305
+
306
+
$this->get_settings()->on_change(
307
+
function ( $old_value, $new_value ) {
308
+
// Ensure that the data available state is reset when the property ID or measurement ID changes.
309
+
if ( $old_value['propertyID'] !== $new_value['propertyID'] || $old_value['measurementID'] !== $new_value['measurementID'] ) {
310
+
$this->reset_data_available();
311
+
$this->custom_dimensions_data_available->reset_data_available();
312
+
313
+
$audience_settings = $this->audience_settings->get();
314
+
$available_audiences = $audience_settings['availableAudiences'] ?? array();
315
+
316
+
$available_audience_names = array_map(
317
+
function ( $audience ) {
318
+
return $audience['name'];
319
+
},
320
+
$available_audiences
321
+
);
322
+
323
+
$this->resource_data_availability_date->reset_all_resource_dates( $available_audience_names, $old_value['propertyID'] );
324
+
}
325
+
326
+
// Reset property specific settings when propertyID changes.
327
+
if ( $old_value['propertyID'] !== $new_value['propertyID'] ) {
328
+
$this->get_settings()->merge(
329
+
array(
330
+
'adSenseLinked' => false,
331
+
'adSenseLinkedLastSyncedAt' => 0,
332
+
'adsLinked' => false,
333
+
'adsLinkedLastSyncedAt' => 0,
334
+
'detectedEvents' => array(),
335
+
)
336
+
);
337
+
338
+
$this->audience_settings->delete();
339
+
340
+
if ( ! empty( $new_value['propertyID'] ) ) {
341
+
do_action( Synchronize_AdSenseLinked::CRON_SYNCHRONIZE_ADSENSE_LINKED );
342
+
343
+
// Reset event detection and new badge events.
344
+
$this->transients->delete( Conversion_Reporting_Events_Sync::DETECTED_EVENTS_TRANSIENT );
345
+
$this->transients->delete( Conversion_Reporting_Events_Sync::LOST_EVENTS_TRANSIENT );
346
+
$this->transients->delete( Conversion_Reporting_New_Badge_Events_Sync::NEW_EVENTS_BADGE_TRANSIENT );
347
+
348
+
$this->transients->set( Conversion_Reporting_New_Badge_Events_Sync::SKIP_NEW_BADGE_TRANSIENT, 1 );
349
+
350
+
do_action( Conversion_Reporting_Cron::CRON_ACTION );
351
+
}
352
+
353
+
// Reset audience specific settings.
354
+
$this->reset_audiences->reset_audience_data();
355
+
}
356
+
}
357
+
);
358
+
359
+
// Check if the property ID has changed and reset applicable settings to null.
360
+
//
361
+
// This is not done using the `get_settings()->merge` method because
362
+
// `Module_Settings::merge` doesn't support setting a value to `null`.
363
+
add_filter(
364
+
'pre_update_option_googlesitekit_analytics-4_settings',
365
+
function ( $new_value, $old_value ) {
366
+
if ( $new_value['propertyID'] !== $old_value['propertyID'] ) {
367
+
$new_value['availableCustomDimensions'] = null;
368
+
}
369
+
370
+
return $new_value;
371
+
},
372
+
10,
373
+
2
374
+
);
375
+
376
+
add_filter(
377
+
'googlesitekit_auth_scopes',
378
+
function ( array $scopes ) {
379
+
$oauth_client = $this->authentication->get_oauth_client();
380
+
381
+
$needs_tagmanager_scope = false;
382
+
383
+
$refined_scopes = $this->get_refined_scopes( $scopes );
384
+
385
+
if ( $oauth_client->has_sufficient_scopes(
386
+
array_merge(
387
+
$refined_scopes,
388
+
array(
389
+
'https://www.googleapis.com/auth/tagmanager.readonly',
390
+
),
391
+
)
392
+
) ) {
393
+
$needs_tagmanager_scope = true;
394
+
395
+
// Ensure the Tag Manager scope is not added as a required scope in the case where the user has
396
+
// granted the Analytics scope but not the Tag Manager scope, in order to allow the GTE-specific
397
+
// Unsatisfied Scopes notification to be displayed without the Additional Permissions Required
398
+
// modal also appearing.
399
+
} elseif ( ! $oauth_client->has_sufficient_scopes(
400
+
$refined_scopes
401
+
) ) {
402
+
$needs_tagmanager_scope = true;
403
+
}
404
+
405
+
if ( $needs_tagmanager_scope ) {
406
+
$refined_scopes[] = 'https://www.googleapis.com/auth/tagmanager.readonly';
407
+
}
408
+
409
+
return $refined_scopes;
410
+
}
411
+
);
412
+
413
+
add_filter( 'googlesitekit_allow_tracking_disabled', $this->get_method_proxy( 'filter_analytics_allow_tracking_disabled' ) );
414
+
415
+
// This hook adds the "Set up Google Analytics" step to the Site Kit
416
+
// setup flow.
417
+
//
418
+
// This filter is documented in
419
+
// Core\Authentication\Google_Proxy::get_metadata_fields.
420
+
add_filter(
421
+
'googlesitekit_proxy_setup_mode',
422
+
function ( $original_mode ) {
423
+
return ! $this->is_connected()
424
+
? 'analytics-step'
425
+
: $original_mode;
426
+
}
427
+
);
428
+
429
+
// Preload the path to avoid layout shift for audience setup CTA banner.
430
+
add_filter(
431
+
'googlesitekit_apifetch_preload_paths',
432
+
function ( $routes ) {
433
+
return array_merge(
434
+
$routes,
435
+
array(
436
+
'/' . REST_Routes::REST_ROOT . '/modules/analytics-4/data/audience-settings',
437
+
)
438
+
);
439
+
}
440
+
);
441
+
442
+
add_filter(
443
+
'googlesitekit_ads_measurement_connection_checks',
444
+
function ( $checks ) {
445
+
$checks[] = array( $this, 'check_ads_measurement_connection' );
446
+
return $checks;
447
+
},
448
+
20
449
+
);
450
+
}
451
+
452
+
/**
453
+
* Checks if the Analytics 4 module is connected and contributing to Ads measurement.
454
+
*
455
+
* Verifies connection status and settings to determine if Ads-related configurations
456
+
* (AdSense linked or Google Tag Container with AW- destination IDs) exist.
457
+
*
458
+
* @since 1.151.0
459
+
*
460
+
* @return bool True if Analytics 4 is connected and configured for Ads measurement; false otherwise.
461
+
*/
462
+
public function check_ads_measurement_connection() {
463
+
if ( ! $this->is_connected() ) {
464
+
return false;
465
+
}
466
+
$settings = $this->get_settings()->get();
467
+
468
+
if ( $settings['adsLinked'] ) {
469
+
return true;
470
+
}
471
+
472
+
foreach ( (array) $settings['googleTagContainerDestinationIDs'] as $destination_id ) {
473
+
if ( 0 === stripos( $destination_id, 'AW-' ) ) {
474
+
return true;
475
+
}
476
+
}
477
+
478
+
return false;
479
+
}
480
+
481
+
/**
482
+
* Gets required Google OAuth scopes for the module.
483
+
*
484
+
* @since 1.30.0
485
+
*
486
+
* @return array List of Google OAuth scopes.
487
+
*/
488
+
public function get_scopes() {
489
+
return array( self::READONLY_SCOPE );
490
+
}
491
+
492
+
/**
493
+
* Checks whether the module is connected.
494
+
*
495
+
* A module being connected means that all steps required as part of its activation are completed.
496
+
*
497
+
* @since 1.30.0
498
+
*
499
+
* @return bool True if module is connected, false otherwise.
500
+
*/
501
+
public function is_connected() {
502
+
$required_keys = array(
503
+
'accountID',
504
+
'propertyID',
505
+
'webDataStreamID',
506
+
'measurementID',
507
+
);
508
+
509
+
$options = $this->get_settings()->get();
510
+
foreach ( $required_keys as $required_key ) {
511
+
if ( empty( $options[ $required_key ] ) ) {
512
+
return false;
513
+
}
514
+
}
515
+
516
+
return parent::is_connected();
517
+
}
518
+
519
+
/**
520
+
* Cleans up when the module is activated.
521
+
*
522
+
* @since 1.107.0
523
+
*/
524
+
public function on_activation() {
525
+
$dismissed_items = new Dismissed_Items( $this->user_options );
526
+
$dismissed_items->remove( 'key-metrics-connect-ga4-cta-widget' );
527
+
}
528
+
529
+
/**
530
+
* Cleans up when the module is deactivated.
531
+
*
532
+
* @since 1.30.0
533
+
*/
534
+
public function on_deactivation() {
535
+
// We need to reset the resource data availability dates before deleting the settings.
536
+
// This is because the property ID and the audience resource names are pulled from settings.
537
+
$this->resource_data_availability_date->reset_all_resource_dates();
538
+
$this->get_settings()->delete();
539
+
$this->reset_data_available();
540
+
$this->custom_dimensions_data_available->reset_data_available();
541
+
$this->reset_audiences->reset_audience_data();
542
+
$this->audience_settings->delete();
543
+
}
544
+
545
+
/**
546
+
* Checks whether the AdSense module is connected.
547
+
*
548
+
* @since 1.121.0
549
+
*
550
+
* @return bool True if AdSense is connected, false otherwise.
551
+
*/
552
+
private function is_adsense_connected() {
553
+
$adsense_settings = ( new AdSense_Settings( $this->options ) )->get();
554
+
555
+
if ( empty( $adsense_settings['accountSetupComplete'] ) || empty( $adsense_settings['siteSetupComplete'] ) ) {
556
+
return false;
557
+
}
558
+
559
+
return true;
560
+
}
561
+
562
+
/**
563
+
* Gets an array of debug field definitions.
564
+
*
565
+
* @since 1.30.0
566
+
*
567
+
* @return array
568
+
*/
569
+
public function get_debug_fields() {
570
+
$settings = $this->get_settings()->get();
571
+
572
+
$debug_fields = array(
573
+
'analytics_4_account_id' => array(
574
+
'label' => __( 'Analytics: Account ID', 'google-site-kit' ),
575
+
'value' => $settings['accountID'],
576
+
'debug' => Debug_Data::redact_debug_value( $settings['accountID'] ),
577
+
),
578
+
'analytics_4_property_id' => array(
579
+
'label' => __( 'Analytics: Property ID', 'google-site-kit' ),
580
+
'value' => $settings['propertyID'],
581
+
'debug' => Debug_Data::redact_debug_value( $settings['propertyID'], 7 ),
582
+
),
583
+
'analytics_4_web_data_stream_id' => array(
584
+
'label' => __( 'Analytics: Web data stream ID', 'google-site-kit' ),
585
+
'value' => $settings['webDataStreamID'],
586
+
'debug' => Debug_Data::redact_debug_value( $settings['webDataStreamID'] ),
587
+
),
588
+
'analytics_4_measurement_id' => array(
589
+
'label' => __( 'Analytics: Measurement ID', 'google-site-kit' ),
590
+
'value' => $settings['measurementID'],
591
+
'debug' => Debug_Data::redact_debug_value( $settings['measurementID'] ),
592
+
),
593
+
'analytics_4_use_snippet' => array(
594
+
'label' => __( 'Analytics: Snippet placed', 'google-site-kit' ),
595
+
'value' => $settings['useSnippet'] ? __( 'Yes', 'google-site-kit' ) : __( 'No', 'google-site-kit' ),
596
+
'debug' => $settings['useSnippet'] ? 'yes' : 'no',
597
+
),
598
+
'analytics_4_available_custom_dimensions' => array(
599
+
'label' => __( 'Analytics: Available Custom Dimensions', 'google-site-kit' ),
600
+
'value' => empty( $settings['availableCustomDimensions'] )
601
+
? __( 'None', 'google-site-kit' )
602
+
: join(
603
+
/* translators: used between list items, there is a space after the comma */
604
+
__( ', ', 'google-site-kit' ),
605
+
$settings['availableCustomDimensions']
606
+
),
607
+
'debug' => empty( $settings['availableCustomDimensions'] )
608
+
? 'none'
609
+
: join( ', ', $settings['availableCustomDimensions'] ),
610
+
),
611
+
'analytics_4_ads_linked' => array(
612
+
'label' => __( 'Analytics: Ads Linked', 'google-site-kit' ),
613
+
'value' => $settings['adsLinked'] ? __( 'Connected', 'google-site-kit' ) : __( 'Not connected', 'google-site-kit' ),
614
+
'debug' => $settings['adsLinked'],
615
+
),
616
+
'analytics_4_ads_linked_last_synced_at' => array(
617
+
'label' => __( 'Analytics: Ads Linked Last Synced At', 'google-site-kit' ),
618
+
'value' => $settings['adsLinkedLastSyncedAt'] ? gmdate( 'Y-m-d H:i:s', $settings['adsLinkedLastSyncedAt'] ) : __( 'Never synced', 'google-site-kit' ),
619
+
'debug' => $settings['adsLinkedLastSyncedAt'],
620
+
),
621
+
);
622
+
623
+
if ( $this->is_adsense_connected() ) {
624
+
$debug_fields['analytics_4_adsense_linked'] = array(
625
+
'label' => __( 'Analytics: AdSense Linked', 'google-site-kit' ),
626
+
'value' => $settings['adSenseLinked'] ? __( 'Connected', 'google-site-kit' ) : __( 'Not connected', 'google-site-kit' ),
627
+
'debug' => Debug_Data::redact_debug_value( $settings['adSenseLinked'] ),
628
+
);
629
+
630
+
$debug_fields['analytics_4_adsense_linked_last_synced_at'] = array(
631
+
'label' => __( 'Analytics: AdSense Linked Last Synced At', 'google-site-kit' ),
632
+
'value' => $settings['adSenseLinkedLastSyncedAt'] ? gmdate( 'Y-m-d H:i:s', $settings['adSenseLinkedLastSyncedAt'] ) : __( 'Never synced', 'google-site-kit' ),
633
+
'debug' => Debug_Data::redact_debug_value( $settings['adSenseLinkedLastSyncedAt'] ),
634
+
);
635
+
}
636
+
637
+
// Return the SITE_KIT_AUDIENCE audiences.
638
+
$available_audiences = $this->audience_settings->get()['availableAudiences'] ?? array();
639
+
$site_kit_audiences = $this->get_site_kit_audiences( $available_audiences );
640
+
641
+
$debug_fields['analytics_4_site_kit_audiences'] = array(
642
+
'label' => __( 'Analytics: Site created audiences', 'google-site-kit' ),
643
+
'value' => empty( $site_kit_audiences )
644
+
? __( 'None', 'google-site-kit' )
645
+
: join(
646
+
/* translators: used between list items, there is a space after the comma */
647
+
__( ', ', 'google-site-kit' ),
648
+
$site_kit_audiences
649
+
),
650
+
'debug' => empty( $site_kit_audiences )
651
+
? 'none'
652
+
: join( ', ', $site_kit_audiences ),
653
+
);
654
+
655
+
return $debug_fields;
656
+
}
657
+
658
+
/**
659
+
* Gets an array of internal feature metrics.
660
+
*
661
+
* @since 1.163.0
662
+
*
663
+
* @return array
664
+
*/
665
+
public function get_feature_metrics() {
666
+
$settings = $this->get_settings()->get();
667
+
668
+
return array(
669
+
'audseg_setup_completed' => (bool) $this->audience_settings->get()['audienceSegmentationSetupCompletedBy'],
670
+
'audseg_audience_count' => count( $this->audience_settings->get()['availableAudiences'] ?? array() ),
671
+
'analytics_adsense_linked' => $this->is_adsense_connected() && $settings['adSenseLinked'],
672
+
);
673
+
}
674
+
675
+
/**
676
+
* Gets map of datapoint to definition data for each.
677
+
*
678
+
* @since 1.30.0
679
+
*
680
+
* @return array Map of datapoints to their definitions.
681
+
*/
682
+
protected function get_datapoint_definitions() {
683
+
$datapoints = array(
684
+
'GET:account-summaries' => array( 'service' => 'analyticsadmin' ),
685
+
'GET:accounts' => array( 'service' => 'analyticsadmin' ),
686
+
'GET:ads-links' => array( 'service' => 'analyticsadmin' ),
687
+
'GET:adsense-links' => array( 'service' => 'analyticsadsenselinks' ),
688
+
'GET:container-lookup' => array(
689
+
'service' => 'tagmanager',
690
+
'scopes' => array(
691
+
'https://www.googleapis.com/auth/tagmanager.readonly',
692
+
),
693
+
),
694
+
'GET:container-destinations' => array(
695
+
'service' => 'tagmanager',
696
+
'scopes' => array(
697
+
'https://www.googleapis.com/auth/tagmanager.readonly',
698
+
),
699
+
),
700
+
'GET:key-events' => array(
701
+
'service' => 'analyticsadmin',
702
+
'shareable' => true,
703
+
),
704
+
'POST:create-account-ticket' => new Create_Account_Ticket(
705
+
array(
706
+
'credentials' => $this->authentication->credentials()->get(),
707
+
'provisioning_redirect_uri' => $this->get_provisioning_redirect_uri(),
708
+
'service' => function () {
709
+
return $this->get_service( 'analyticsprovisioning' );
710
+
},
711
+
'scopes' => array( self::EDIT_SCOPE ),
712
+
'request_scopes_message' => __( 'You’ll need to grant Site Kit permission to create a new Analytics account on your behalf.', 'google-site-kit' ),
713
+
),
714
+
),
715
+
'GET:google-tag-settings' => array(
716
+
'service' => 'tagmanager',
717
+
'scopes' => array(
718
+
'https://www.googleapis.com/auth/tagmanager.readonly',
719
+
),
720
+
),
721
+
'POST:create-property' => new Create_Property(
722
+
array(
723
+
'reference_site_url' => $this->context->get_reference_site_url(),
724
+
'service' => function () {
725
+
return $this->get_service( 'analyticsadmin' );
726
+
},
727
+
'scopes' => array( self::EDIT_SCOPE ),
728
+
'request_scopes_message' => __( 'You’ll need to grant Site Kit permission to create a new Analytics property on your behalf.', 'google-site-kit' ),
729
+
)
730
+
),
731
+
'POST:create-webdatastream' => new Create_Webdatastream(
732
+
array(
733
+
'reference_site_url' => $this->context->get_reference_site_url(),
734
+
'service' => function () {
735
+
return $this->get_service( 'analyticsadmin' );
736
+
},
737
+
'scopes' => array( self::EDIT_SCOPE ),
738
+
'request_scopes_message' => __( 'You’ll need to grant Site Kit permission to create a new Analytics web data stream for this site on your behalf.', 'google-site-kit' ),
739
+
)
740
+
),
741
+
'GET:properties' => array( 'service' => 'analyticsadmin' ),
742
+
'GET:property' => array( 'service' => 'analyticsadmin' ),
743
+
'GET:has-property-access' => array( 'service' => 'analyticsdata' ),
744
+
'GET:report' => array(
745
+
'service' => 'analyticsdata',
746
+
'shareable' => true,
747
+
),
748
+
'GET:batch-report' => array(
749
+
'service' => 'analyticsdata',
750
+
'shareable' => true,
751
+
),
752
+
'GET:webdatastreams' => array( 'service' => 'analyticsadmin' ),
753
+
'GET:webdatastreams-batch' => array( 'service' => 'analyticsadmin' ),
754
+
'GET:enhanced-measurement-settings' => array( 'service' => 'analyticsenhancedmeasurement' ),
755
+
'POST:enhanced-measurement-settings' => array(
756
+
'service' => 'analyticsenhancedmeasurement',
757
+
'scopes' => array( self::EDIT_SCOPE ),
758
+
'request_scopes_message' => __( 'You’ll need to grant Site Kit permission to update enhanced measurement settings for this Analytics web data stream on your behalf.', 'google-site-kit' ),
759
+
),
760
+
'POST:create-custom-dimension' => array(
761
+
'service' => 'analyticsdata',
762
+
'scopes' => array( self::EDIT_SCOPE ),
763
+
'request_scopes_message' => __( 'You’ll need to grant Site Kit permission to create a new Analytics custom dimension on your behalf.', 'google-site-kit' ),
764
+
),
765
+
'POST:sync-custom-dimensions' => array(
766
+
'service' => 'analyticsadmin',
767
+
),
768
+
'POST:custom-dimension-data-available' => array(
769
+
'service' => '',
770
+
),
771
+
'POST:set-google-tag-id-mismatch' => array(
772
+
'service' => '',
773
+
),
774
+
'POST:set-is-web-data-stream-unavailable' => array(
775
+
'service' => '',
776
+
),
777
+
'POST:create-audience' => array(
778
+
'service' => 'analyticsaudiences',
779
+
'scopes' => array( self::EDIT_SCOPE ),
780
+
'request_scopes_message' => __( 'You’ll need to grant Site Kit permission to create new audiences for your Analytics property on your behalf.', 'google-site-kit' ),
781
+
),
782
+
'POST:save-resource-data-availability-date' => array(
783
+
'service' => '',
784
+
),
785
+
'POST:sync-audiences' => array(
786
+
'service' => 'analyticsaudiences',
787
+
'shareable' => true,
788
+
),
789
+
'GET:audience-settings' => array(
790
+
'service' => '',
791
+
'shareable' => true,
792
+
),
793
+
'POST:save-audience-settings' => array(
794
+
'service' => '',
795
+
),
796
+
);
797
+
798
+
return $datapoints;
799
+
}
800
+
801
+
/**
802
+
* Creates a new property for provided account.
803
+
*
804
+
* @since 1.35.0
805
+
* @since 1.98.0 Added `$options` parameter.
806
+
*
807
+
* @param string $account_id Account ID.
808
+
* @param array $options {
809
+
* Property options.
810
+
*
811
+
* @type string $displayName Display name.
812
+
* @type string $timezone Timezone.
813
+
* }
814
+
* @return Google_Service_GoogleAnalyticsAdmin_GoogleAnalyticsAdminV1betaProperty A new property.
815
+
*/
816
+
private function create_property( $account_id, $options = array() ) {
817
+
if ( ! empty( $options['displayName'] ) ) {
818
+
$display_name = sanitize_text_field( $options['displayName'] );
819
+
} else {
820
+
$display_name = URL::parse( $this->context->get_reference_site_url(), PHP_URL_HOST );
821
+
}
822
+
823
+
if ( ! empty( $options['timezone'] ) ) {
824
+
$timezone = $options['timezone'];
825
+
} else {
826
+
$timezone = get_option( 'timezone_string' ) ?: 'UTC';
827
+
}
828
+
829
+
$property = new Google_Service_GoogleAnalyticsAdmin_GoogleAnalyticsAdminV1betaProperty();
830
+
$property->setParent( self::normalize_account_id( $account_id ) );
831
+
$property->setDisplayName( $display_name );
832
+
$property->setTimeZone( $timezone );
833
+
834
+
return $this->get_service( 'analyticsadmin' )->properties->create( $property );
835
+
}
836
+
837
+
/**
838
+
* Creates a new web data stream for provided property.
839
+
*
840
+
* @since 1.35.0
841
+
* @since 1.98.0 Added `$options` parameter.
842
+
*
843
+
* @param string $property_id Property ID.
844
+
* @param array $options {
845
+
* Web data stream options.
846
+
*
847
+
* @type string $displayName Display name.
848
+
* }
849
+
* @return GoogleAnalyticsAdminV1betaDataStream A new web data stream.
850
+
*/
851
+
private function create_webdatastream( $property_id, $options = array() ) {
852
+
$site_url = $this->context->get_reference_site_url();
853
+
854
+
if ( ! empty( $options['displayName'] ) ) {
855
+
$display_name = sanitize_text_field( $options['displayName'] );
856
+
} else {
857
+
$display_name = URL::parse( $site_url, PHP_URL_HOST );
858
+
}
859
+
860
+
$data = new GoogleAnalyticsAdminV1betaDataStreamWebStreamData();
861
+
$data->setDefaultUri( $site_url );
862
+
863
+
$datastream = new GoogleAnalyticsAdminV1betaDataStream();
864
+
$datastream->setDisplayName( $display_name );
865
+
$datastream->setType( 'WEB_DATA_STREAM' );
866
+
$datastream->setWebStreamData( $data );
867
+
868
+
/* @var Google_Service_GoogleAnalyticsAdmin $analyticsadmin phpcs:ignore Squiz.PHP.CommentedOutCode.Found */
869
+
$analyticsadmin = $this->get_service( 'analyticsadmin' );
870
+
871
+
return $analyticsadmin
872
+
->properties_dataStreams // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
873
+
->create(
874
+
self::normalize_property_id( $property_id ),
875
+
$datastream
876
+
);
877
+
}
878
+
879
+
/**
880
+
* Outputs the user tracking opt-out script.
881
+
*
882
+
* This script opts out of all Google Analytics tracking, for all measurement IDs, regardless of implementation.
883
+
* E.g. via Tag Manager, etc.
884
+
*
885
+
* @since 1.5.0
886
+
* @since 1.121.0 Migrated from the Analytics (UA) class and adapted to only work for GA4 properties.
887
+
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-opt-out
888
+
*/
889
+
private function print_tracking_opt_out() {
890
+
$settings = $this->get_settings()->get();
891
+
$account_id = $settings['accountID'];
892
+
$property_id = $settings['propertyID'];
893
+
894
+
if ( ! $this->is_tracking_disabled() ) {
895
+
return;
896
+
}
897
+
898
+
if ( $this->context->is_amp() ) : ?>
899
+
<!-- <?php esc_html_e( 'Google Analytics AMP opt-out snippet added by Site Kit', 'google-site-kit' ); ?> -->
900
+
<meta name="ga-opt-out" content="" id="__gaOptOutExtension">
901
+
<!-- <?php esc_html_e( 'End Google Analytics AMP opt-out snippet added by Site Kit', 'google-site-kit' ); ?> -->
902
+
<?php else : ?>
903
+
<!-- <?php esc_html_e( 'Google Analytics opt-out snippet added by Site Kit', 'google-site-kit' ); ?> -->
904
+
<?php
905
+
// Opt-out should always use the measurement ID, even when using a GT tag.
906
+
$tag_id = $this->get_measurement_id();
907
+
if ( ! empty( $tag_id ) ) {
908
+
BC_Functions::wp_print_inline_script_tag( sprintf( 'window["ga-disable-%s"] = true;', esc_attr( $tag_id ) ) );
909
+
}
910
+
?>
911
+
<?php do_action( 'googlesitekit_analytics_tracking_opt_out', $property_id, $account_id ); ?>
912
+
<!-- <?php esc_html_e( 'End Google Analytics opt-out snippet added by Site Kit', 'google-site-kit' ); ?> -->
913
+
<?php
914
+
endif;
915
+
}
916
+
917
+
/**
918
+
* Checks whether or not tracking snippet should be contextually disabled for this request.
919
+
*
920
+
* @since 1.1.0
921
+
* @since 1.121.0 Migrated here from the Analytics (UA) class.
922
+
*
923
+
* @return bool
924
+
*/
925
+
protected function is_tracking_disabled() {
926
+
$settings = $this->get_settings()->get();
927
+
928
+
// This filter is documented in Tag_Manager::filter_analytics_allow_tracking_disabled.
929
+
if ( ! apply_filters( 'googlesitekit_allow_tracking_disabled', $settings['useSnippet'] ) ) {
930
+
return false;
931
+
}
932
+
933
+
$disable_logged_in_users = in_array( 'loggedinUsers', $settings['trackingDisabled'], true ) && is_user_logged_in();
934
+
$disable_content_creators = in_array( 'contentCreators', $settings['trackingDisabled'], true ) && current_user_can( 'edit_posts' );
935
+
936
+
$disabled = $disable_logged_in_users || $disable_content_creators;
937
+
938
+
/**
939
+
* Filters whether or not the Analytics tracking snippet is output for the current request.
940
+
*
941
+
* @since 1.1.0
942
+
*
943
+
* @param $disabled bool Whether to disable tracking or not.
944
+
*/
945
+
return (bool) apply_filters( 'googlesitekit_analytics_tracking_disabled', $disabled );
946
+
}
947
+
948
+
/**
949
+
* Handles the provisioning callback after the user completes the terms of service.
950
+
*
951
+
* @since 1.9.0
952
+
* @since 1.98.0 Extended to handle callback from Admin API (no UA entities).
953
+
* @since 1.121.0 Migrated method from original Analytics class to Analytics_4 class.
954
+
*/
955
+
protected function handle_provisioning_callback() {
956
+
if ( defined( 'WP_CLI' ) && WP_CLI ) {
957
+
return;
958
+
}
959
+
960
+
if ( ! current_user_can( Permissions::MANAGE_OPTIONS ) ) {
961
+
return;
962
+
}
963
+
964
+
$input = $this->context->input();
965
+
966
+
if ( ! $input->filter( INPUT_GET, 'gatoscallback' ) ) {
967
+
return;
968
+
}
969
+
970
+
// First check that the accountTicketId matches one stored for the user.
971
+
// This is always provided, even in the event of an error.
972
+
$account_ticket_id = htmlspecialchars( $input->filter( INPUT_GET, 'accountTicketId' ) );
973
+
// The create-account-ticket request stores the created account ticket in a transient before
974
+
// sending the user off to the terms of service page.
975
+
$account_ticket_transient_key = self::PROVISION_ACCOUNT_TICKET_ID . '::' . get_current_user_id();
976
+
$account_ticket_params = $this->transients->get( $account_ticket_transient_key );
977
+
$account_ticket = new Account_Ticket( $account_ticket_params );
978
+
979
+
// Backwards compat for previous storage type which stored ID only.
980
+
if ( is_scalar( $account_ticket_params ) ) {
981
+
$account_ticket->set_id( $account_ticket_params );
982
+
}
983
+
984
+
if ( $account_ticket->get_id() !== $account_ticket_id ) {
985
+
wp_safe_redirect(
986
+
$this->context->admin_url( 'dashboard', array( 'error_code' => 'account_ticket_id_mismatch' ) )
987
+
);
988
+
exit;
989
+
}
990
+
991
+
// At this point, the accountTicketId is a match and params are loaded, so we can safely delete the transient.
992
+
$this->transients->delete( $account_ticket_transient_key );
993
+
994
+
// Next, check for a returned error.
995
+
$error = $input->filter( INPUT_GET, 'error' );
996
+
if ( ! empty( $error ) ) {
997
+
wp_safe_redirect(
998
+
$this->context->admin_url( 'dashboard', array( 'error_code' => htmlspecialchars( $error ) ) )
999
+
);
1000
+
exit;
1001
+
}
1002
+
1003
+
$account_id = htmlspecialchars( $input->filter( INPUT_GET, 'accountId' ) );
1004
+
1005
+
if ( empty( $account_id ) ) {
1006
+
wp_safe_redirect(
1007
+
$this->context->admin_url( 'dashboard', array( 'error_code' => 'callback_missing_parameter' ) )
1008
+
);
1009
+
exit;
1010
+
}
1011
+
1012
+
$new_settings = array();
1013
+
1014
+
// At this point, account creation was successful.
1015
+
$new_settings['accountID'] = $account_id;
1016
+
1017
+
$this->get_settings()->merge( $new_settings );
1018
+
1019
+
$this->provision_property_webdatastream( $account_id, $account_ticket );
1020
+
1021
+
if ( Feature_Flags::enabled( 'setupFlowRefresh' ) ) {
1022
+
$show_progress = (bool) $input->filter( INPUT_GET, 'show_progress' );
1023
+
1024
+
wp_safe_redirect(
1025
+
$this->context->admin_url(
1026
+
'key-metrics-setup',
1027
+
array(
1028
+
'showProgress' => $show_progress ? 'true' : null,
1029
+
)
1030
+
)
1031
+
);
1032
+
exit;
1033
+
}
1034
+
1035
+
wp_safe_redirect(
1036
+
$this->context->admin_url(
1037
+
'dashboard',
1038
+
array(
1039
+
'notification' => 'authentication_success',
1040
+
'slug' => 'analytics-4',
1041
+
)
1042
+
)
1043
+
);
1044
+
exit;
1045
+
}
1046
+
1047
+
/**
1048
+
* Provisions new GA4 property and web data stream for provided account.
1049
+
*
1050
+
* @since 1.35.0
1051
+
* @since 1.98.0 Added $account_ticket.
1052
+
*
1053
+
* @param string $account_id Account ID.
1054
+
* @param Account_Ticket $account_ticket Account ticket instance.
1055
+
*/
1056
+
private function provision_property_webdatastream( $account_id, $account_ticket ) {
1057
+
// Reset the current GA4 settings.
1058
+
$this->get_settings()->merge(
1059
+
array(
1060
+
'propertyID' => '',
1061
+
'webDataStreamID' => '',
1062
+
'measurementID' => '',
1063
+
)
1064
+
);
1065
+
1066
+
$property = $this->create_property(
1067
+
$account_id,
1068
+
array(
1069
+
'displayName' => $account_ticket->get_property_name(),
1070
+
'timezone' => $account_ticket->get_timezone(),
1071
+
)
1072
+
);
1073
+
$property = self::filter_property_with_ids( $property );
1074
+
1075
+
if ( empty( $property->_id ) ) {
1076
+
return;
1077
+
}
1078
+
1079
+
$create_time = isset( $property->createTime ) ? $property->createTime : ''; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1080
+
$create_time_ms = 0;
1081
+
if ( $create_time ) {
1082
+
$create_time_ms = Synchronize_Property::convert_time_to_unix_ms( $create_time );
1083
+
}
1084
+
1085
+
$this->get_settings()->merge(
1086
+
array(
1087
+
'propertyID' => $property->_id,
1088
+
'propertyCreateTime' => $create_time_ms,
1089
+
)
1090
+
);
1091
+
1092
+
$web_datastream = $this->create_webdatastream(
1093
+
$property->_id,
1094
+
array(
1095
+
'displayName' => $account_ticket->get_data_stream_name(),
1096
+
)
1097
+
);
1098
+
$web_datastream = self::filter_webdatastream_with_ids( $web_datastream );
1099
+
1100
+
if ( empty( $web_datastream->_id ) ) {
1101
+
return;
1102
+
}
1103
+
1104
+
$measurement_id = $web_datastream->webStreamData->measurementId; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1105
+
1106
+
$this->get_settings()->merge(
1107
+
array(
1108
+
'webDataStreamID' => $web_datastream->_id,
1109
+
'measurementID' => $measurement_id,
1110
+
)
1111
+
);
1112
+
1113
+
if ( $account_ticket->get_enhanced_measurement_stream_enabled() ) {
1114
+
$this->set_data(
1115
+
'enhanced-measurement-settings',
1116
+
array(
1117
+
'propertyID' => $property->_id,
1118
+
'webDataStreamID' => $web_datastream->_id,
1119
+
'enhancedMeasurementSettings' => array(
1120
+
// We can hardcode this to `true` here due to the conditional invocation.
1121
+
'streamEnabled' => true,
1122
+
),
1123
+
)
1124
+
);
1125
+
}
1126
+
1127
+
$this->sync_google_tag_settings();
1128
+
}
1129
+
1130
+
/**
1131
+
* Syncs Google tag settings for the currently configured measurementID.
1132
+
*
1133
+
* @since 1.102.0
1134
+
*/
1135
+
protected function sync_google_tag_settings() {
1136
+
$settings = $this->get_settings();
1137
+
$measurement_id = $settings->get()['measurementID'];
1138
+
1139
+
if ( ! $measurement_id ) {
1140
+
return;
1141
+
}
1142
+
1143
+
$google_tag_settings = $this->get_data( 'google-tag-settings', array( 'measurementID' => $measurement_id ) );
1144
+
1145
+
if ( is_wp_error( $google_tag_settings ) ) {
1146
+
return;
1147
+
}
1148
+
1149
+
$settings->merge( $google_tag_settings );
1150
+
}
1151
+
1152
+
/**
1153
+
* Creates a request object for the given datapoint.
1154
+
*
1155
+
* @since 1.30.0
1156
+
*
1157
+
* @param Data_Request $data Data request object.
1158
+
* @return RequestInterface|callable|WP_Error Request object or callable on success, or WP_Error on failure.
1159
+
*
1160
+
* @throws Invalid_Datapoint_Exception Thrown if the datapoint does not exist.
1161
+
* @throws Invalid_Param_Exception Thrown if a parameter is invalid.
1162
+
* @throws Missing_Required_Param_Exception Thrown if a required parameter is missing or empty.
1163
+
*
1164
+
* phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
1165
+
*/
1166
+
protected function create_data_request( Data_Request $data ) {
1167
+
switch ( "{$data->method}:{$data->datapoint}" ) {
1168
+
case 'GET:accounts':
1169
+
return $this->get_service( 'analyticsadmin' )->accounts->listAccounts();
1170
+
case 'GET:account-summaries':
1171
+
return $this->get_service( 'analyticsadmin' )->accountSummaries->listAccountSummaries(
1172
+
array(
1173
+
'pageSize' => 200,
1174
+
'pageToken' => $data['pageToken'],
1175
+
)
1176
+
);
1177
+
case 'GET:ads-links':
1178
+
if ( empty( $data['propertyID'] ) ) {
1179
+
throw new Missing_Required_Param_Exception( 'propertyID' );
1180
+
}
1181
+
1182
+
$parent = self::normalize_property_id( $data['propertyID'] );
1183
+
1184
+
return $this->get_service( 'analyticsadmin' )->properties_googleAdsLinks->listPropertiesGoogleAdsLinks( $parent );
1185
+
case 'GET:adsense-links':
1186
+
if ( empty( $data['propertyID'] ) ) {
1187
+
throw new Missing_Required_Param_Exception( 'propertyID' );
1188
+
}
1189
+
1190
+
$parent = self::normalize_property_id( $data['propertyID'] );
1191
+
1192
+
return $this->get_analyticsadsenselinks_service()->properties_adSenseLinks->listPropertiesAdSenseLinks( $parent );
1193
+
case 'POST:create-audience':
1194
+
$settings = $this->get_settings()->get();
1195
+
if ( ! isset( $settings['propertyID'] ) ) {
1196
+
return new WP_Error(
1197
+
'missing_required_setting',
1198
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
1199
+
array( 'status' => 500 )
1200
+
);
1201
+
}
1202
+
1203
+
if ( ! isset( $data['audience'] ) ) {
1204
+
throw new Missing_Required_Param_Exception( 'audience' );
1205
+
}
1206
+
1207
+
$property_id = $settings['propertyID'];
1208
+
$audience = $data['audience'];
1209
+
1210
+
$fields = array(
1211
+
'displayName',
1212
+
'description',
1213
+
'membershipDurationDays',
1214
+
'eventTrigger',
1215
+
'exclusionDurationMode',
1216
+
'filterClauses',
1217
+
);
1218
+
1219
+
$invalid_keys = array_diff( array_keys( $audience ), $fields );
1220
+
1221
+
if ( ! empty( $invalid_keys ) ) {
1222
+
return new WP_Error(
1223
+
'invalid_property_name',
1224
+
/* translators: %s: Invalid property names */
1225
+
sprintf( __( 'Invalid properties in audience: %s.', 'google-site-kit' ), implode( ', ', $invalid_keys ) ),
1226
+
array( 'status' => 400 )
1227
+
);
1228
+
}
1229
+
1230
+
$property_id = self::normalize_property_id( $property_id );
1231
+
1232
+
$post_body = new GoogleAnalyticsAdminV1alphaAudience( $audience );
1233
+
1234
+
$analyticsadmin = $this->get_analyticsaudiences_service();
1235
+
1236
+
return $analyticsadmin
1237
+
->properties_audiences
1238
+
->create(
1239
+
$property_id,
1240
+
$post_body
1241
+
);
1242
+
case 'GET:properties':
1243
+
if ( ! isset( $data['accountID'] ) ) {
1244
+
return new WP_Error(
1245
+
'missing_required_param',
1246
+
/* translators: %s: Missing parameter name */
1247
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ),
1248
+
array( 'status' => 400 )
1249
+
);
1250
+
}
1251
+
1252
+
return $this->get_service( 'analyticsadmin' )->properties->listProperties(
1253
+
array(
1254
+
'filter' => 'parent:' . self::normalize_account_id( $data['accountID'] ),
1255
+
'pageSize' => 200,
1256
+
)
1257
+
);
1258
+
case 'GET:property':
1259
+
if ( ! isset( $data['propertyID'] ) ) {
1260
+
return new WP_Error(
1261
+
'missing_required_param',
1262
+
/* translators: %s: Missing parameter name */
1263
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'propertyID' ),
1264
+
array( 'status' => 400 )
1265
+
);
1266
+
}
1267
+
1268
+
return $this->get_service( 'analyticsadmin' )->properties->get( self::normalize_property_id( $data['propertyID'] ) );
1269
+
case 'GET:has-property-access':
1270
+
if ( ! isset( $data['propertyID'] ) ) {
1271
+
throw new Missing_Required_Param_Exception( 'propertyID' );
1272
+
}
1273
+
1274
+
// A simple way to check for property access is to attempt a minimal report request.
1275
+
// If the user does not have access, this will return a 403 error.
1276
+
$request = new Google_Service_AnalyticsData_RunReportRequest();
1277
+
$request->setDimensions( array( new Google_Service_AnalyticsData_Dimension( array( 'name' => 'date' ) ) ) );
1278
+
$request->setMetrics( array( new Google_Service_AnalyticsData_Metric( array( 'name' => 'sessions' ) ) ) );
1279
+
$request->setDateRanges(
1280
+
array(
1281
+
new Google_Service_AnalyticsData_DateRange(
1282
+
array(
1283
+
'start_date' => 'yesterday',
1284
+
'end_date' => 'today',
1285
+
)
1286
+
),
1287
+
)
1288
+
);
1289
+
$request->setLimit( 0 );
1290
+
1291
+
return $this->get_analyticsdata_service()->properties->runReport( $data['propertyID'], $request );
1292
+
case 'GET:report':
1293
+
if ( empty( $data['metrics'] ) ) {
1294
+
return new WP_Error(
1295
+
'missing_required_param',
1296
+
/* translators: %s: Missing parameter name */
1297
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'metrics' ),
1298
+
array( 'status' => 400 )
1299
+
);
1300
+
}
1301
+
1302
+
$settings = $this->get_settings()->get();
1303
+
if ( empty( $settings['propertyID'] ) ) {
1304
+
return new WP_Error(
1305
+
'missing_required_setting',
1306
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
1307
+
array( 'status' => 500 )
1308
+
);
1309
+
}
1310
+
1311
+
$report = new Analytics_4_Report_Request( $this->context );
1312
+
$request = $report->create_request( $data, $this->is_shared_data_request( $data ) );
1313
+
if ( is_wp_error( $request ) ) {
1314
+
return $request;
1315
+
}
1316
+
1317
+
$property_id = self::normalize_property_id( $settings['propertyID'] );
1318
+
$request->setProperty( $property_id );
1319
+
1320
+
return $this->get_analyticsdata_service()->properties->runReport( $property_id, $request );
1321
+
1322
+
case 'GET:batch-report':
1323
+
if ( empty( $data['requests'] ) ) {
1324
+
return new WP_Error(
1325
+
'missing_required_param',
1326
+
/* translators: %s: Missing parameter name */
1327
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'requests' ),
1328
+
array( 'status' => 400 )
1329
+
);
1330
+
}
1331
+
1332
+
if ( ! is_array( $data['requests'] ) || count( $data['requests'] ) > 5 ) {
1333
+
return new WP_Error(
1334
+
'invalid_batch_size',
1335
+
__( 'Batch report requests must be an array with 1-5 requests.', 'google-site-kit' ),
1336
+
array( 'status' => 400 )
1337
+
);
1338
+
}
1339
+
1340
+
$settings = $this->get_settings()->get();
1341
+
if ( empty( $settings['propertyID'] ) ) {
1342
+
return new WP_Error(
1343
+
'missing_required_setting',
1344
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
1345
+
array( 'status' => 500 )
1346
+
);
1347
+
}
1348
+
1349
+
$batch_requests = array();
1350
+
$report = new Analytics_4_Report_Request( $this->context );
1351
+
1352
+
foreach ( $data['requests'] as $request_data ) {
1353
+
$data_request = new Data_Request( 'GET', 'modules', $this->slug, 'report', $request_data );
1354
+
$request = $report->create_request(
1355
+
$data_request,
1356
+
$this->is_shared_data_request( $data_request )
1357
+
);
1358
+
if ( is_wp_error( $request ) ) {
1359
+
return $request;
1360
+
}
1361
+
$batch_requests[] = $request;
1362
+
}
1363
+
1364
+
$property_id = self::normalize_property_id( $settings['propertyID'] );
1365
+
1366
+
$batch_request = new Google_Service_AnalyticsData\BatchRunReportsRequest();
1367
+
$batch_request->setRequests( $batch_requests );
1368
+
1369
+
return $this->get_analyticsdata_service()->properties->batchRunReports(
1370
+
$property_id,
1371
+
$batch_request
1372
+
);
1373
+
1374
+
case 'GET:enhanced-measurement-settings':
1375
+
if ( ! isset( $data['propertyID'] ) ) {
1376
+
return new WP_Error(
1377
+
'missing_required_param',
1378
+
/* translators: %s: Missing parameter name */
1379
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'propertyID' ),
1380
+
array( 'status' => 400 )
1381
+
);
1382
+
}
1383
+
1384
+
if ( ! isset( $data['webDataStreamID'] ) ) {
1385
+
return new WP_Error(
1386
+
'missing_required_param',
1387
+
/* translators: %s: Missing parameter name */
1388
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'webDataStreamID' ),
1389
+
array( 'status' => 400 )
1390
+
);
1391
+
}
1392
+
1393
+
$name = self::normalize_property_id(
1394
+
$data['propertyID']
1395
+
) . '/dataStreams/' . $data['webDataStreamID'] . '/enhancedMeasurementSettings';
1396
+
1397
+
$analyticsadmin = $this->get_analyticsenhancedmeasurements_service();
1398
+
1399
+
return $analyticsadmin
1400
+
->properties_enhancedMeasurements // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1401
+
->getEnhancedMeasurementSettings( $name );
1402
+
case 'POST:enhanced-measurement-settings':
1403
+
if ( ! isset( $data['propertyID'] ) ) {
1404
+
return new WP_Error(
1405
+
'missing_required_param',
1406
+
/* translators: %s: Missing parameter name */
1407
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'propertyID' ),
1408
+
array( 'status' => 400 )
1409
+
);
1410
+
}
1411
+
1412
+
if ( ! isset( $data['webDataStreamID'] ) ) {
1413
+
return new WP_Error(
1414
+
'missing_required_param',
1415
+
/* translators: %s: Missing parameter name */
1416
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'webDataStreamID' ),
1417
+
array( 'status' => 400 )
1418
+
);
1419
+
}
1420
+
1421
+
if ( ! isset( $data['enhancedMeasurementSettings'] ) ) {
1422
+
return new WP_Error(
1423
+
'missing_required_param',
1424
+
/* translators: %s: Missing parameter name */
1425
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'enhancedMeasurementSettings' ),
1426
+
array( 'status' => 400 )
1427
+
);
1428
+
}
1429
+
1430
+
$enhanced_measurement_settings = $data['enhancedMeasurementSettings'];
1431
+
1432
+
$fields = array(
1433
+
'name',
1434
+
'streamEnabled',
1435
+
'scrollsEnabled',
1436
+
'outboundClicksEnabled',
1437
+
'siteSearchEnabled',
1438
+
'videoEngagementEnabled',
1439
+
'fileDownloadsEnabled',
1440
+
'pageChangesEnabled',
1441
+
'formInteractionsEnabled',
1442
+
'searchQueryParameter',
1443
+
'uriQueryParameter',
1444
+
);
1445
+
1446
+
$invalid_keys = array_diff( array_keys( $enhanced_measurement_settings ), $fields );
1447
+
1448
+
if ( ! empty( $invalid_keys ) ) {
1449
+
return new WP_Error(
1450
+
'invalid_property_name',
1451
+
/* translators: %s: Invalid property names */
1452
+
sprintf( __( 'Invalid properties in enhancedMeasurementSettings: %s.', 'google-site-kit' ), implode( ', ', $invalid_keys ) ),
1453
+
array( 'status' => 400 )
1454
+
);
1455
+
}
1456
+
1457
+
$name = self::normalize_property_id(
1458
+
$data['propertyID']
1459
+
) . '/dataStreams/' . $data['webDataStreamID'] . '/enhancedMeasurementSettings';
1460
+
1461
+
$post_body = new EnhancedMeasurementSettingsModel( $data['enhancedMeasurementSettings'] );
1462
+
1463
+
$analyticsadmin = $this->get_analyticsenhancedmeasurements_service();
1464
+
1465
+
return $analyticsadmin
1466
+
->properties_enhancedMeasurements // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1467
+
->updateEnhancedMeasurementSettings(
1468
+
$name,
1469
+
$post_body,
1470
+
array(
1471
+
'updateMask' => 'streamEnabled', // Only allow updating the streamEnabled field for now.
1472
+
)
1473
+
);
1474
+
case 'POST:create-custom-dimension':
1475
+
if ( ! isset( $data['propertyID'] ) ) {
1476
+
return new WP_Error(
1477
+
'missing_required_param',
1478
+
/* translators: %s: Missing parameter name */
1479
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'propertyID' ),
1480
+
array( 'status' => 400 )
1481
+
);
1482
+
}
1483
+
1484
+
if ( ! isset( $data['customDimension'] ) ) {
1485
+
return new WP_Error(
1486
+
'missing_required_param',
1487
+
/* translators: %s: Missing parameter name */
1488
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'customDimension' ),
1489
+
array( 'status' => 400 )
1490
+
);
1491
+
}
1492
+
1493
+
$custom_dimension_data = $data['customDimension'];
1494
+
1495
+
$fields = array(
1496
+
'parameterName',
1497
+
'displayName',
1498
+
'description',
1499
+
'scope',
1500
+
'disallowAdsPersonalization',
1501
+
);
1502
+
1503
+
$invalid_keys = array_diff( array_keys( $custom_dimension_data ), $fields );
1504
+
1505
+
if ( ! empty( $invalid_keys ) ) {
1506
+
return new WP_Error(
1507
+
'invalid_property_name',
1508
+
/* translators: %s: Invalid property names */
1509
+
sprintf( __( 'Invalid properties in customDimension: %s.', 'google-site-kit' ), implode( ', ', $invalid_keys ) ),
1510
+
array( 'status' => 400 )
1511
+
);
1512
+
}
1513
+
1514
+
// Define the valid `DimensionScope` enum values.
1515
+
$valid_scopes = array( 'EVENT', 'USER', 'ITEM' );
1516
+
1517
+
// If the scope field is not set, default to `EVENT`.
1518
+
// Otherwise, validate against the enum values.
1519
+
if ( ! isset( $custom_dimension_data['scope'] ) ) {
1520
+
$custom_dimension_data['scope'] = 'EVENT';
1521
+
} elseif ( ! in_array( $custom_dimension_data['scope'], $valid_scopes, true ) ) {
1522
+
return new WP_Error(
1523
+
'invalid_scope',
1524
+
/* translators: %s: Invalid scope */
1525
+
sprintf( __( 'Invalid scope: %s.', 'google-site-kit' ), $custom_dimension_data['scope'] ),
1526
+
array( 'status' => 400 )
1527
+
);
1528
+
}
1529
+
1530
+
$custom_dimension = new GoogleAnalyticsAdminV1betaCustomDimension();
1531
+
$custom_dimension->setParameterName( $custom_dimension_data['parameterName'] );
1532
+
$custom_dimension->setDisplayName( $custom_dimension_data['displayName'] );
1533
+
$custom_dimension->setScope( $custom_dimension_data['scope'] );
1534
+
1535
+
if ( isset( $custom_dimension_data['description'] ) ) {
1536
+
$custom_dimension->setDescription( $custom_dimension_data['description'] );
1537
+
}
1538
+
1539
+
if ( isset( $custom_dimension_data['disallowAdsPersonalization'] ) ) {
1540
+
$custom_dimension->setDisallowAdsPersonalization( $custom_dimension_data['disallowAdsPersonalization'] );
1541
+
}
1542
+
1543
+
$analyticsadmin = $this->get_service( 'analyticsadmin' );
1544
+
1545
+
return $analyticsadmin
1546
+
->properties_customDimensions // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1547
+
->create(
1548
+
self::normalize_property_id( $data['propertyID'] ),
1549
+
$custom_dimension
1550
+
);
1551
+
1552
+
case 'GET:audience-settings':
1553
+
return function () {
1554
+
$settings = $this->audience_settings->get();
1555
+
return current_user_can( Permissions::MANAGE_OPTIONS ) ? $settings : array_intersect_key( $settings, array_flip( $this->audience_settings->get_view_only_keys() ) );
1556
+
};
1557
+
1558
+
case 'POST:save-audience-settings':
1559
+
if ( ! current_user_can( Permissions::MANAGE_OPTIONS ) ) {
1560
+
return new WP_Error(
1561
+
'forbidden',
1562
+
__( 'User does not have permission to save audience settings.', 'google-site-kit' ),
1563
+
array( 'status' => 403 )
1564
+
);
1565
+
}
1566
+
1567
+
$settings = $data['settings'];
1568
+
1569
+
if (
1570
+
isset( $settings['audienceSegmentationSetupCompletedBy'] ) &&
1571
+
! is_int( $settings['audienceSegmentationSetupCompletedBy'] )
1572
+
) {
1573
+
throw new Invalid_Param_Exception( 'audienceSegmentationSetupCompletedBy' );
1574
+
}
1575
+
1576
+
return function () use ( $settings ) {
1577
+
$new_settings = array();
1578
+
1579
+
if ( isset( $settings['audienceSegmentationSetupCompletedBy'] ) ) {
1580
+
$new_settings['audienceSegmentationSetupCompletedBy'] = $settings['audienceSegmentationSetupCompletedBy'];
1581
+
}
1582
+
1583
+
$settings = $this->audience_settings->merge( $new_settings );
1584
+
1585
+
return $settings;
1586
+
};
1587
+
1588
+
case 'POST:sync-audiences':
1589
+
if ( ! $this->authentication->is_authenticated() ) {
1590
+
return new WP_Error(
1591
+
'forbidden',
1592
+
__( 'User must be authenticated to sync audiences.', 'google-site-kit' ),
1593
+
array( 'status' => 403 )
1594
+
);
1595
+
}
1596
+
1597
+
$settings = $this->get_settings()->get();
1598
+
if ( empty( $settings['propertyID'] ) ) {
1599
+
return new WP_Error(
1600
+
'missing_required_setting',
1601
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
1602
+
array( 'status' => 500 )
1603
+
);
1604
+
}
1605
+
1606
+
$analyticsadmin = $this->get_analyticsaudiences_service();
1607
+
$property_id = self::normalize_property_id( $settings['propertyID'] );
1608
+
1609
+
return $analyticsadmin
1610
+
->properties_audiences
1611
+
->listPropertiesAudiences( $property_id );
1612
+
case 'POST:sync-custom-dimensions':
1613
+
$settings = $this->get_settings()->get();
1614
+
if ( empty( $settings['propertyID'] ) ) {
1615
+
return new WP_Error(
1616
+
'missing_required_setting',
1617
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
1618
+
array( 'status' => 500 )
1619
+
);
1620
+
}
1621
+
1622
+
$analyticsadmin = $this->get_service( 'analyticsadmin' );
1623
+
1624
+
return $analyticsadmin
1625
+
->properties_customDimensions // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1626
+
->listPropertiesCustomDimensions( self::normalize_property_id( $settings['propertyID'] ) );
1627
+
case 'POST:custom-dimension-data-available':
1628
+
if ( ! isset( $data['customDimension'] ) ) {
1629
+
return new WP_Error(
1630
+
'missing_required_param',
1631
+
/* translators: %s: Missing parameter name */
1632
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'customDimension' ),
1633
+
array( 'status' => 400 )
1634
+
);
1635
+
}
1636
+
1637
+
if ( ! $this->custom_dimensions_data_available->is_valid_custom_dimension( $data['customDimension'] ) ) {
1638
+
return new WP_Error(
1639
+
'invalid_custom_dimension_slug',
1640
+
/* translators: %s: Invalid custom dimension slug */
1641
+
sprintf( __( 'Invalid custom dimension slug: %s.', 'google-site-kit' ), $data['customDimension'] ),
1642
+
array( 'status' => 400 )
1643
+
);
1644
+
}
1645
+
1646
+
return function () use ( $data ) {
1647
+
return $this->custom_dimensions_data_available->set_data_available( $data['customDimension'] );
1648
+
};
1649
+
case 'POST:save-resource-data-availability-date':
1650
+
if ( ! isset( $data['resourceType'] ) ) {
1651
+
throw new Missing_Required_Param_Exception( 'resourceType' );
1652
+
}
1653
+
1654
+
if ( ! isset( $data['resourceSlug'] ) ) {
1655
+
throw new Missing_Required_Param_Exception( 'resourceSlug' );
1656
+
}
1657
+
1658
+
if ( ! isset( $data['date'] ) ) {
1659
+
throw new Missing_Required_Param_Exception( 'date' );
1660
+
}
1661
+
1662
+
if ( ! $this->resource_data_availability_date->is_valid_resource_type( $data['resourceType'] ) ) {
1663
+
throw new Invalid_Param_Exception( 'resourceType' );
1664
+
}
1665
+
1666
+
if ( ! $this->resource_data_availability_date->is_valid_resource_slug( $data['resourceSlug'], $data['resourceType'] ) ) {
1667
+
throw new Invalid_Param_Exception( 'resourceSlug' );
1668
+
}
1669
+
1670
+
if ( ! is_int( $data['date'] ) ) {
1671
+
throw new Invalid_Param_Exception( 'date' );
1672
+
}
1673
+
1674
+
return function () use ( $data ) {
1675
+
return $this->resource_data_availability_date->set_resource_date( $data['resourceSlug'], $data['resourceType'], $data['date'] );
1676
+
};
1677
+
case 'GET:webdatastreams':
1678
+
if ( ! isset( $data['propertyID'] ) ) {
1679
+
return new WP_Error(
1680
+
'missing_required_param',
1681
+
/* translators: %s: Missing parameter name */
1682
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'propertyID' ),
1683
+
array( 'status' => 400 )
1684
+
);
1685
+
}
1686
+
1687
+
$analyticsadmin = $this->get_service( 'analyticsadmin' );
1688
+
1689
+
return $analyticsadmin
1690
+
->properties_dataStreams // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1691
+
->listPropertiesDataStreams(
1692
+
self::normalize_property_id( $data['propertyID'] )
1693
+
);
1694
+
case 'GET:webdatastreams-batch':
1695
+
if ( ! isset( $data['propertyIDs'] ) ) {
1696
+
return new WP_Error(
1697
+
'missing_required_param',
1698
+
/* translators: %s: Missing parameter name */
1699
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'propertyIDs' ),
1700
+
array( 'status' => 400 )
1701
+
);
1702
+
}
1703
+
1704
+
if ( ! is_array( $data['propertyIDs'] ) || count( $data['propertyIDs'] ) > 10 ) {
1705
+
return new WP_Error(
1706
+
'rest_invalid_param',
1707
+
/* translators: %s: List of invalid parameters. */
1708
+
sprintf( __( 'Invalid parameter(s): %s', 'google-site-kit' ), 'propertyIDs' ),
1709
+
array( 'status' => 400 )
1710
+
);
1711
+
}
1712
+
1713
+
$analyticsadmin = $this->get_service( 'analyticsadmin' );
1714
+
$batch_request = $analyticsadmin->createBatch();
1715
+
1716
+
foreach ( $data['propertyIDs'] as $property_id ) {
1717
+
$batch_request->add(
1718
+
$analyticsadmin
1719
+
->properties_dataStreams // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1720
+
->listPropertiesDataStreams(
1721
+
self::normalize_property_id( $property_id )
1722
+
)
1723
+
);
1724
+
}
1725
+
1726
+
return function () use ( $batch_request ) {
1727
+
return $batch_request->execute();
1728
+
};
1729
+
case 'GET:container-lookup':
1730
+
if ( ! isset( $data['destinationID'] ) ) {
1731
+
return new WP_Error(
1732
+
'missing_required_param',
1733
+
/* translators: %s: Missing parameter name */
1734
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'destinationID' ),
1735
+
array( 'status' => 400 )
1736
+
);
1737
+
}
1738
+
1739
+
return $this->get_tagmanager_service()->accounts_containers->lookup( array( 'destinationId' => $data['destinationID'] ) );
1740
+
case 'GET:container-destinations':
1741
+
if ( ! isset( $data['accountID'] ) ) {
1742
+
return new WP_Error(
1743
+
'missing_required_param',
1744
+
/* translators: %s: Missing parameter name */
1745
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'accountID' ),
1746
+
array( 'status' => 400 )
1747
+
);
1748
+
}
1749
+
if ( ! isset( $data['containerID'] ) ) {
1750
+
return new WP_Error(
1751
+
'missing_required_param',
1752
+
/* translators: %s: Missing parameter name */
1753
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'containerID' ),
1754
+
array( 'status' => 400 )
1755
+
);
1756
+
}
1757
+
1758
+
return $this->get_tagmanager_service()->accounts_containers_destinations->listAccountsContainersDestinations(
1759
+
"accounts/{$data['accountID']}/containers/{$data['containerID']}"
1760
+
);
1761
+
case 'GET:google-tag-settings':
1762
+
if ( ! isset( $data['measurementID'] ) ) {
1763
+
return new WP_Error(
1764
+
'missing_required_param',
1765
+
/* translators: %s: Missing parameter name */
1766
+
sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), 'measurementID' ),
1767
+
array( 'status' => 400 )
1768
+
);
1769
+
}
1770
+
1771
+
return $this->get_tagmanager_service()->accounts_containers->lookup( array( 'destinationId' => $data['measurementID'] ) );
1772
+
case 'GET:key-events':
1773
+
$settings = $this->get_settings()->get();
1774
+
if ( empty( $settings['propertyID'] ) ) {
1775
+
return new WP_Error(
1776
+
'missing_required_setting',
1777
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
1778
+
array( 'status' => 500 )
1779
+
);
1780
+
}
1781
+
1782
+
$analyticsadmin = $this->get_service( 'analyticsadmin' );
1783
+
$property_id = self::normalize_property_id( $settings['propertyID'] );
1784
+
1785
+
return $analyticsadmin
1786
+
->properties_keyEvents // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1787
+
->listPropertiesKeyEvents( $property_id );
1788
+
case 'POST:set-google-tag-id-mismatch':
1789
+
if ( ! isset( $data['hasMismatchedTag'] ) ) {
1790
+
throw new Missing_Required_Param_Exception( 'hasMismatchedTag' );
1791
+
}
1792
+
1793
+
if ( false === $data['hasMismatchedTag'] ) {
1794
+
return function () {
1795
+
$this->transients->delete( 'googlesitekit_inline_tag_id_mismatch' );
1796
+
return false;
1797
+
};
1798
+
}
1799
+
1800
+
return function () use ( $data ) {
1801
+
$this->transients->set( 'googlesitekit_inline_tag_id_mismatch', $data['hasMismatchedTag'] );
1802
+
return $data['hasMismatchedTag'];
1803
+
};
1804
+
case 'POST:set-is-web-data-stream-unavailable':
1805
+
if ( ! isset( $data['isWebDataStreamUnavailable'] ) ) {
1806
+
throw new Missing_Required_Param_Exception( 'isWebDataStreamUnavailable' );
1807
+
}
1808
+
1809
+
if ( true === $data['isWebDataStreamUnavailable'] ) {
1810
+
return function () {
1811
+
$settings = $this->get_settings()->get();
1812
+
$transient_key = 'googlesitekit_web_data_stream_unavailable_' . $settings['webDataStreamID'];
1813
+
$this->transients->set( $transient_key, true );
1814
+
return true;
1815
+
};
1816
+
}
1817
+
1818
+
return function () {
1819
+
$settings = $this->get_settings()->get();
1820
+
$transient_key = 'googlesitekit_web_data_stream_unavailable_' . $settings['webDataStreamID'];
1821
+
$this->transients->delete( $transient_key );
1822
+
return false;
1823
+
};
1824
+
}
1825
+
1826
+
return parent::create_data_request( $data );
1827
+
}
1828
+
1829
+
/**
1830
+
* Parses a response for the given datapoint.
1831
+
*
1832
+
* @since 1.30.0
1833
+
*
1834
+
* @param Data_Request $data Data request object.
1835
+
* @param mixed $response Request response.
1836
+
*
1837
+
* @return mixed Parsed response data on success, or WP_Error on failure.
1838
+
*/
1839
+
protected function parse_data_response( Data_Request $data, $response ) {
1840
+
switch ( "{$data->method}:{$data->datapoint}" ) {
1841
+
case 'GET:accounts':
1842
+
return array_map( array( self::class, 'filter_account_with_ids' ), $response->getAccounts() );
1843
+
case 'GET:ads-links':
1844
+
return (array) $response->getGoogleAdsLinks();
1845
+
case 'GET:adsense-links':
1846
+
return (array) $response->getAdsenseLinks();
1847
+
case 'GET:properties':
1848
+
return Sort::case_insensitive_list_sort(
1849
+
array_map( array( self::class, 'filter_property_with_ids' ), $response->getProperties() ),
1850
+
'displayName'
1851
+
);
1852
+
case 'GET:property':
1853
+
return self::filter_property_with_ids( $response );
1854
+
case 'GET:webdatastreams':
1855
+
/* @var GoogleAnalyticsAdminV1betaListDataStreamsResponse $response phpcs:ignore Squiz.PHP.CommentedOutCode.Found */
1856
+
$webdatastreams = self::filter_web_datastreams( $response->getDataStreams() );
1857
+
return array_map( array( self::class, 'filter_webdatastream_with_ids' ), $webdatastreams );
1858
+
case 'GET:webdatastreams-batch':
1859
+
return self::parse_webdatastreams_batch( $response );
1860
+
case 'GET:container-destinations':
1861
+
return (array) $response->getDestination();
1862
+
case 'GET:google-tag-settings':
1863
+
return $this->get_google_tag_settings_for_measurement_id( $response, $data['measurementID'] );
1864
+
case 'GET:key-events':
1865
+
return (array) $response->getKeyEvents();
1866
+
case 'GET:report':
1867
+
$report = new Analytics_4_Report_Response( $this->context );
1868
+
return $report->parse_response( $data, $response );
1869
+
case 'POST:sync-audiences':
1870
+
$audiences = $this->set_available_audiences( $response->getAudiences() );
1871
+
return $audiences;
1872
+
case 'POST:sync-custom-dimensions':
1873
+
if ( is_wp_error( $response ) ) {
1874
+
return $response;
1875
+
}
1876
+
1877
+
$custom_dimensions = wp_list_pluck( $response->getCustomDimensions(), 'parameterName' );
1878
+
$matching_dimensions = array_values(
1879
+
array_filter(
1880
+
$custom_dimensions,
1881
+
function ( $dimension ) {
1882
+
return strpos( $dimension, 'googlesitekit_' ) === 0;
1883
+
}
1884
+
)
1885
+
);
1886
+
$this->get_settings()->merge(
1887
+
array(
1888
+
'availableCustomDimensions' => $matching_dimensions,
1889
+
)
1890
+
);
1891
+
1892
+
// Reset the data available state for custom dimensions that are no longer available.
1893
+
$missing_custom_dimensions_with_data_available = array_diff(
1894
+
array_keys(
1895
+
// Only compare against custom dimensions that have data available.
1896
+
array_filter(
1897
+
$this->custom_dimensions_data_available->get_data_availability()
1898
+
)
1899
+
),
1900
+
$matching_dimensions
1901
+
);
1902
+
1903
+
if ( count( $missing_custom_dimensions_with_data_available ) > 0 ) {
1904
+
$this->custom_dimensions_data_available->reset_data_available(
1905
+
$missing_custom_dimensions_with_data_available
1906
+
);
1907
+
}
1908
+
1909
+
return $matching_dimensions;
1910
+
}
1911
+
1912
+
return parent::parse_data_response( $data, $response );
1913
+
}
1914
+
1915
+
/**
1916
+
* Gets the configured TagManager service instance.
1917
+
*
1918
+
* @since 1.92.0
1919
+
*
1920
+
* @return Google_Service_TagManager instance.
1921
+
* @throws Exception Thrown if the module did not correctly set up the service.
1922
+
*/
1923
+
private function get_tagmanager_service() {
1924
+
return $this->get_service( 'tagmanager' );
1925
+
}
1926
+
1927
+
/**
1928
+
* Sets up information about the module.
1929
+
*
1930
+
* @since 1.30.0
1931
+
* @since 1.123.0 Updated to include in the module setup.
1932
+
*
1933
+
* @return array Associative array of module info.
1934
+
*/
1935
+
protected function setup_info() {
1936
+
return array(
1937
+
'slug' => self::MODULE_SLUG,
1938
+
'name' => _x( 'Analytics', 'Service name', 'google-site-kit' ),
1939
+
'description' => __( 'Get a deeper understanding of your customers. Google Analytics gives you the free tools you need to analyze data for your business in one place.', 'google-site-kit' ),
1940
+
'homepage' => __( 'https://analytics.google.com/analytics/web', 'google-site-kit' ),
1941
+
);
1942
+
}
1943
+
1944
+
/**
1945
+
* Gets the configured Analytics Data service object instance.
1946
+
*
1947
+
* @since 1.93.0
1948
+
*
1949
+
* @return Google_Service_AnalyticsData The Analytics Data API service.
1950
+
*/
1951
+
protected function get_analyticsdata_service() {
1952
+
return $this->get_service( 'analyticsdata' );
1953
+
}
1954
+
1955
+
/**
1956
+
* Gets the configured Analytics Data service object instance.
1957
+
*
1958
+
* @since 1.110.0
1959
+
*
1960
+
* @return PropertiesEnhancedMeasurementService The Analytics Admin API service.
1961
+
*/
1962
+
protected function get_analyticsenhancedmeasurements_service() {
1963
+
return $this->get_service( 'analyticsenhancedmeasurement' );
1964
+
}
1965
+
1966
+
/**
1967
+
* Gets the configured Analytics Admin service object instance that includes `adSenseLinks` related methods.
1968
+
*
1969
+
* @since 1.120.0
1970
+
*
1971
+
* @return PropertiesAdSenseLinksService The Analytics Admin API service.
1972
+
*/
1973
+
protected function get_analyticsadsenselinks_service() {
1974
+
return $this->get_service( 'analyticsadsenselinks' );
1975
+
}
1976
+
1977
+
/**
1978
+
* Gets the configured Analytics Data service object instance.
1979
+
*
1980
+
* @since 1.120.0
1981
+
*
1982
+
* @return PropertiesAudiencesService The Analytics Admin API service.
1983
+
*/
1984
+
protected function get_analyticsaudiences_service() {
1985
+
return $this->get_service( 'analyticsaudiences' );
1986
+
}
1987
+
1988
+
/**
1989
+
* Sets up the Google services the module should use.
1990
+
*
1991
+
* This method is invoked once by {@see Module::get_service()} to lazily set up the services when one is requested
1992
+
* for the first time.
1993
+
*
1994
+
* @since 1.30.0
1995
+
*
1996
+
* @param Google_Site_Kit_Client $client Google client instance.
1997
+
* @return array Google services as $identifier => $service_instance pairs. Every $service_instance must be an
1998
+
* instance of Google_Service.
1999
+
*/
2000
+
protected function setup_services( Google_Site_Kit_Client $client ) {
2001
+
$google_proxy = $this->authentication->get_google_proxy();
2002
+
2003
+
return array(
2004
+
'analyticsadmin' => new Google_Service_GoogleAnalyticsAdmin( $client ),
2005
+
'analyticsdata' => new Google_Service_AnalyticsData( $client ),
2006
+
'analyticsprovisioning' => new AccountProvisioningService( $client, $google_proxy->url() ),
2007
+
'analyticsenhancedmeasurement' => new PropertiesEnhancedMeasurementService( $client ),
2008
+
'analyticsaudiences' => new PropertiesAudiencesService( $client ),
2009
+
'analyticsadsenselinks' => new PropertiesAdSenseLinksService( $client ),
2010
+
'tagmanager' => new Google_Service_TagManager( $client ),
2011
+
);
2012
+
}
2013
+
2014
+
/**
2015
+
* Sets up the module's settings instance.
2016
+
*
2017
+
* @since 1.30.0
2018
+
*
2019
+
* @return Module_Settings
2020
+
*/
2021
+
protected function setup_settings() {
2022
+
return new Settings( $this->options );
2023
+
}
2024
+
2025
+
/**
2026
+
* Sets up the module's assets to register.
2027
+
*
2028
+
* @since 1.31.0
2029
+
*
2030
+
* @return Asset[] List of Asset objects.
2031
+
*/
2032
+
protected function setup_assets() {
2033
+
$base_url = $this->context->url( 'dist/assets/' );
2034
+
2035
+
return array(
2036
+
new Script(
2037
+
'googlesitekit-modules-analytics-4',
2038
+
array(
2039
+
'src' => $base_url . 'js/googlesitekit-modules-analytics-4.js',
2040
+
'dependencies' => array(
2041
+
'googlesitekit-vendor',
2042
+
'googlesitekit-api',
2043
+
'googlesitekit-data',
2044
+
'googlesitekit-modules',
2045
+
'googlesitekit-notifications',
2046
+
'googlesitekit-datastore-site',
2047
+
'googlesitekit-datastore-user',
2048
+
'googlesitekit-datastore-forms',
2049
+
'googlesitekit-components',
2050
+
'googlesitekit-modules-data',
2051
+
),
2052
+
)
2053
+
),
2054
+
);
2055
+
}
2056
+
2057
+
/**
2058
+
* Gets the provisioning redirect URI that listens for the Terms of Service redirect.
2059
+
*
2060
+
* @since 1.98.0
2061
+
*
2062
+
* @return string Provisioning redirect URI.
2063
+
*/
2064
+
private function get_provisioning_redirect_uri() {
2065
+
return $this->authentication->get_google_proxy()
2066
+
->get_site_fields()['analytics_redirect_uri'];
2067
+
}
2068
+
2069
+
/**
2070
+
* Registers the Analytics 4 tag.
2071
+
*
2072
+
* @since 1.31.0
2073
+
* @since 1.104.0 Added support for AMP tag.
2074
+
* @since 1.119.0 Made method public.
2075
+
*/
2076
+
public function register_tag() {
2077
+
$tag = $this->context->is_amp()
2078
+
? new AMP_Tag( $this->get_measurement_id(), self::MODULE_SLUG ) // AMP currently only works with the measurement ID.
2079
+
: new Web_Tag( $this->get_tag_id(), self::MODULE_SLUG );
2080
+
2081
+
if ( $tag->is_tag_blocked() ) {
2082
+
return;
2083
+
}
2084
+
2085
+
$tag->use_guard( new Tag_Verify_Guard( $this->context->input() ) );
2086
+
$tag->use_guard( new Tag_Guard( $this->get_settings() ) );
2087
+
$tag->use_guard( new Tag_Environment_Type_Guard() );
2088
+
2089
+
if ( ! $tag->can_register() ) {
2090
+
return;
2091
+
}
2092
+
2093
+
$home_domain = URL::parse( $this->context->get_canonical_home_url(), PHP_URL_HOST );
2094
+
$tag->set_home_domain( $home_domain );
2095
+
2096
+
$custom_dimensions_data = $this->get_custom_dimensions_data();
2097
+
if ( ! empty( $custom_dimensions_data ) && $tag instanceof Tag_Interface ) {
2098
+
$tag->set_custom_dimensions( $custom_dimensions_data );
2099
+
}
2100
+
2101
+
$tag->register();
2102
+
}
2103
+
2104
+
/**
2105
+
* Returns the Module_Tag_Matchers instance.
2106
+
*
2107
+
* @since 1.119.0
2108
+
*
2109
+
* @return Module_Tag_Matchers Module_Tag_Matchers instance.
2110
+
*/
2111
+
public function get_tag_matchers() {
2112
+
return new Tag_Matchers();
2113
+
}
2114
+
2115
+
/**
2116
+
* Gets custom dimensions data based on available custom dimensions.
2117
+
*
2118
+
* @since 1.113.0
2119
+
*
2120
+
* @return array An associated array of custom dimensions data.
2121
+
*/
2122
+
private function get_custom_dimensions_data() {
2123
+
if ( ! is_singular() ) {
2124
+
return array();
2125
+
}
2126
+
2127
+
$settings = $this->get_settings()->get();
2128
+
if ( empty( $settings['availableCustomDimensions'] ) ) {
2129
+
return array();
2130
+
}
2131
+
2132
+
/**
2133
+
* Filters the allowed post types for custom dimensions tracking.
2134
+
*
2135
+
* @since 1.113.0
2136
+
*
2137
+
* @param array $allowed_post_types The array of allowed post types.
2138
+
*/
2139
+
$allowed_post_types = apply_filters( 'googlesitekit_custom_dimension_valid_post_types', array( 'post' ) );
2140
+
2141
+
$data = array();
2142
+
$post = get_queried_object();
2143
+
2144
+
if ( ! $post instanceof WP_Post ) {
2145
+
return $data;
2146
+
}
2147
+
2148
+
if ( in_array( 'googlesitekit_post_type', $settings['availableCustomDimensions'], true ) ) {
2149
+
$data['googlesitekit_post_type'] = $post->post_type;
2150
+
}
2151
+
2152
+
if ( is_singular( $allowed_post_types ) ) {
2153
+
foreach ( $settings['availableCustomDimensions'] as $custom_dimension ) {
2154
+
switch ( $custom_dimension ) {
2155
+
case 'googlesitekit_post_author':
2156
+
$author = get_userdata( $post->post_author );
2157
+
2158
+
if ( $author ) {
2159
+
$data[ $custom_dimension ] = $author->display_name ? $author->display_name : $author->user_login;
2160
+
}
2161
+
2162
+
break;
2163
+
case 'googlesitekit_post_categories':
2164
+
$categories = get_the_category( $post->ID );
2165
+
2166
+
if ( ! empty( $categories ) ) {
2167
+
$category_names = wp_list_pluck( $categories, 'name' );
2168
+
2169
+
$data[ $custom_dimension ] = implode( '; ', $category_names );
2170
+
}
2171
+
2172
+
break;
2173
+
case 'googlesitekit_post_date':
2174
+
$data[ $custom_dimension ] = get_the_date( 'Ymd', $post );
2175
+
break;
2176
+
}
2177
+
}
2178
+
}
2179
+
2180
+
return $data;
2181
+
}
2182
+
2183
+
/**
2184
+
* Parses account ID, adds it to the model object and returns updated model.
2185
+
*
2186
+
* @since 1.31.0
2187
+
*
2188
+
* @param Google_Model $account Account model.
2189
+
* @param string $id_key Attribute name that contains account id.
2190
+
* @return stdClass Updated model with _id attribute.
2191
+
*/
2192
+
public static function filter_account_with_ids( $account, $id_key = 'name' ) {
2193
+
$obj = $account->toSimpleObject();
2194
+
2195
+
$matches = array();
2196
+
if ( preg_match( '#accounts/([^/]+)#', $account[ $id_key ], $matches ) ) {
2197
+
$obj->_id = $matches[1];
2198
+
}
2199
+
2200
+
return $obj;
2201
+
}
2202
+
2203
+
/**
2204
+
* Parses account and property IDs, adds it to the model object and returns updated model.
2205
+
*
2206
+
* @since 1.31.0
2207
+
*
2208
+
* @param Google_Model $property Property model.
2209
+
* @param string $id_key Attribute name that contains property id.
2210
+
* @return stdClass Updated model with _id and _accountID attributes.
2211
+
*/
2212
+
public static function filter_property_with_ids( $property, $id_key = 'name' ) {
2213
+
$obj = $property->toSimpleObject();
2214
+
2215
+
$matches = array();
2216
+
if ( preg_match( '#properties/([^/]+)#', $property[ $id_key ] ?? '', $matches ) ) {
2217
+
$obj->_id = $matches[1];
2218
+
}
2219
+
2220
+
$matches = array();
2221
+
if ( preg_match( '#accounts/([^/]+)#', $property['parent'] ?? '', $matches ) ) {
2222
+
$obj->_accountID = $matches[1]; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
2223
+
}
2224
+
2225
+
return $obj;
2226
+
}
2227
+
2228
+
/**
2229
+
* Parses property and web datastream IDs, adds it to the model object and returns updated model.
2230
+
*
2231
+
* @since 1.31.0
2232
+
*
2233
+
* @param Google_Model $webdatastream Web datastream model.
2234
+
* @return stdClass Updated model with _id and _propertyID attributes.
2235
+
*/
2236
+
public static function filter_webdatastream_with_ids( $webdatastream ) {
2237
+
$obj = $webdatastream->toSimpleObject();
2238
+
2239
+
$matches = array();
2240
+
if ( preg_match( '#properties/([^/]+)/dataStreams/([^/]+)#', $webdatastream['name'], $matches ) ) {
2241
+
$obj->_id = $matches[2];
2242
+
$obj->_propertyID = $matches[1]; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
2243
+
}
2244
+
2245
+
return $obj;
2246
+
}
2247
+
2248
+
/**
2249
+
* Filters a list of data stream objects and returns only web data streams.
2250
+
*
2251
+
* @since 1.49.1
2252
+
*
2253
+
* @param GoogleAnalyticsAdminV1betaDataStream[] $datastreams Data streams to filter.
2254
+
* @return GoogleAnalyticsAdminV1betaDataStream[] Web data streams.
2255
+
*/
2256
+
public static function filter_web_datastreams( array $datastreams ) {
2257
+
return array_filter(
2258
+
$datastreams,
2259
+
function ( GoogleAnalyticsAdminV1betaDataStream $datastream ) {
2260
+
return $datastream->getType() === 'WEB_DATA_STREAM';
2261
+
}
2262
+
);
2263
+
}
2264
+
2265
+
/**
2266
+
* Parses a response, adding the _id and _propertyID params and converting to an array keyed by the propertyID and web datastream IDs.
2267
+
*
2268
+
* @since 1.39.0
2269
+
*
2270
+
* @param GoogleAnalyticsAdminV1betaListDataStreamsResponse[] $batch_response Array of GoogleAnalyticsAdminV1betaListWebDataStreamsResponse objects.
2271
+
* @return stdClass[] Array of models containing _id and _propertyID attributes, keyed by the propertyID.
2272
+
*/
2273
+
public static function parse_webdatastreams_batch( $batch_response ) {
2274
+
$mapped = array();
2275
+
2276
+
foreach ( $batch_response as $response ) {
2277
+
if ( $response instanceof Exception ) {
2278
+
continue;
2279
+
}
2280
+
2281
+
$webdatastreams = self::filter_web_datastreams( $response->getDataStreams() );
2282
+
2283
+
foreach ( $webdatastreams as $webdatastream ) {
2284
+
$value = self::filter_webdatastream_with_ids( $webdatastream );
2285
+
$key = $value->_propertyID; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
2286
+
$mapped[ $key ] = isset( $mapped[ $key ] ) ? $mapped[ $key ] : array();
2287
+
$mapped[ $key ][] = $value;
2288
+
}
2289
+
}
2290
+
2291
+
return $mapped;
2292
+
}
2293
+
2294
+
/**
2295
+
* Normalizes account ID and returns it.
2296
+
*
2297
+
* @since 1.31.0
2298
+
*
2299
+
* @param string $account_id Account ID.
2300
+
* @return string Updated account ID with "accounts/" prefix.
2301
+
*/
2302
+
public static function normalize_account_id( $account_id ) {
2303
+
return 'accounts/' . $account_id;
2304
+
}
2305
+
2306
+
/**
2307
+
* Normalizes property ID and returns it.
2308
+
*
2309
+
* @since 1.31.0
2310
+
*
2311
+
* @param string $property_id Property ID.
2312
+
* @return string Updated property ID with "properties/" prefix.
2313
+
*/
2314
+
public static function normalize_property_id( $property_id ) {
2315
+
return 'properties/' . $property_id;
2316
+
}
2317
+
2318
+
/**
2319
+
* Checks if the current user has access to the current configured service entity.
2320
+
*
2321
+
* @since 1.70.0
2322
+
*
2323
+
* @return boolean|WP_Error
2324
+
*/
2325
+
public function check_service_entity_access() {
2326
+
$settings = $this->get_settings()->get();
2327
+
2328
+
if ( empty( $settings['propertyID'] ) ) {
2329
+
return new WP_Error(
2330
+
'missing_required_setting',
2331
+
__( 'No connected Google Analytics property ID.', 'google-site-kit' ),
2332
+
array( 'status' => 500 )
2333
+
);
2334
+
}
2335
+
2336
+
return $this->has_property_access( $settings['propertyID'] );
2337
+
}
2338
+
2339
+
/**
2340
+
* Checks if the current user has access to the given property ID.
2341
+
*
2342
+
* @since 1.163.0
2343
+
*
2344
+
* @param string $property_id Property ID to check access for.
2345
+
* @return boolean|WP_Error True if the user has access, false if not, or WP_Error on any other error.
2346
+
*/
2347
+
public function has_property_access( $property_id ) {
2348
+
$request = $this->get_data( 'has-property-access', array( 'propertyID' => $property_id ) );
2349
+
2350
+
if ( is_wp_error( $request ) ) {
2351
+
// A 403 error implies that the user does not have access to the service entity.
2352
+
if ( $request->get_error_code() === 403 ) {
2353
+
return false;
2354
+
}
2355
+
2356
+
return $request;
2357
+
}
2358
+
2359
+
return true;
2360
+
}
2361
+
2362
+
/**
2363
+
* Gets the Google Tag Settings for the given measurement ID.
2364
+
*
2365
+
* @since 1.94.0
2366
+
*
2367
+
* @param Google_Service_TagManager_Container $container Tag Manager container.
2368
+
* @param string $measurement_id Measurement ID.
2369
+
* @return array Google Tag Settings.
2370
+
*/
2371
+
protected function get_google_tag_settings_for_measurement_id( $container, $measurement_id ) {
2372
+
return array(
2373
+
'googleTagAccountID' => $container->getAccountId(),
2374
+
'googleTagContainerID' => $container->getContainerId(),
2375
+
'googleTagID' => $this->determine_google_tag_id_from_tag_ids( $container->getTagIds(), $measurement_id ),
2376
+
);
2377
+
}
2378
+
2379
+
/**
2380
+
* Determines Google Tag ID from the given Tag IDs.
2381
+
*
2382
+
* @since 1.94.0
2383
+
*
2384
+
* @param array $tag_ids Tag IDs.
2385
+
* @param string $measurement_id Measurement ID.
2386
+
* @return string Google Tag ID.
2387
+
*/
2388
+
private function determine_google_tag_id_from_tag_ids( $tag_ids, $measurement_id ) {
2389
+
// If there is only one tag id in the array, return it.
2390
+
if ( count( $tag_ids ) === 1 ) {
2391
+
return $tag_ids[0];
2392
+
}
2393
+
2394
+
// If there are multiple tags, return the first one that starts with `GT-`.
2395
+
foreach ( $tag_ids as $tag_id ) {
2396
+
if ( substr( $tag_id, 0, 3 ) === 'GT-' ) { // strlen( 'GT-' ) === 3.
2397
+
return $tag_id;
2398
+
}
2399
+
}
2400
+
2401
+
// Otherwise, return the `$measurement_id` if it is in the array.
2402
+
if ( in_array( $measurement_id, $tag_ids, true ) ) {
2403
+
return $measurement_id;
2404
+
}
2405
+
2406
+
// Otherwise, return the first one that starts with `G-`.
2407
+
foreach ( $tag_ids as $tag_id ) {
2408
+
if ( substr( $tag_id, 0, 2 ) === 'G-' ) { // strlen( 'G-' ) === 2.
2409
+
return $tag_id;
2410
+
}
2411
+
}
2412
+
2413
+
// If none of the above, return the first one.
2414
+
return $tag_ids[0];
2415
+
}
2416
+
2417
+
/**
2418
+
* Gets the Google Analytics 4 tag ID.
2419
+
*
2420
+
* @since 1.96.0
2421
+
*
2422
+
* @return string Google Analytics 4 tag ID.
2423
+
*/
2424
+
private function get_tag_id() {
2425
+
$settings = $this->get_settings()->get();
2426
+
2427
+
if ( ! empty( $settings['googleTagID'] ) ) {
2428
+
return $settings['googleTagID'];
2429
+
}
2430
+
return $settings['measurementID'];
2431
+
}
2432
+
2433
+
/**
2434
+
* Gets the currently configured measurement ID.
2435
+
*
2436
+
* @since 1.104.0
2437
+
*
2438
+
* @return string Google Analytics 4 measurement ID.
2439
+
*/
2440
+
protected function get_measurement_id() {
2441
+
$settings = $this->get_settings()->get();
2442
+
2443
+
return $settings['measurementID'];
2444
+
}
2445
+
2446
+
/**
2447
+
* Populates custom dimension data to pass to JS via _googlesitekitModulesData.
2448
+
*
2449
+
* @since 1.113.0
2450
+
* @since 1.158.0 Renamed method to `get_inline_custom_dimensions_data()`, and modified it to return a new array rather than populating a passed filter value.
2451
+
*
2452
+
* @return array Inline modules data.
2453
+
*/
2454
+
private function get_inline_custom_dimensions_data() {
2455
+
if ( $this->is_connected() ) {
2456
+
return array(
2457
+
'customDimensionsDataAvailable' => $this->custom_dimensions_data_available->get_data_availability(),
2458
+
);
2459
+
}
2460
+
}
2461
+
2462
+
/**
2463
+
* Populates tag ID mismatch value to pass to JS via _googlesitekitModulesData.
2464
+
*
2465
+
* @since 1.130.0
2466
+
* @since 1.158.0 Renamed method to `get_inline_tag_id_mismatch()`, and modified it to return a new array rather than populating a passed filter value.
2467
+
*
2468
+
* @return array Inline modules data.
2469
+
*/
2470
+
private function get_inline_tag_id_mismatch() {
2471
+
if ( $this->is_connected() ) {
2472
+
$tag_id_mismatch = $this->transients->get( 'googlesitekit_inline_tag_id_mismatch' );
2473
+
2474
+
return array(
2475
+
'tagIDMismatch' => $tag_id_mismatch,
2476
+
);
2477
+
}
2478
+
2479
+
return array();
2480
+
}
2481
+
2482
+
/**
2483
+
* Populates resource availability dates data to pass to JS via _googlesitekitModulesData.
2484
+
*
2485
+
* @since 1.127.0
2486
+
* @since 1.158.0 Renamed method to `get_inline_resource_availability_dates_data()`, and modified it to return a new array rather than populating a passed filter value.
2487
+
*
2488
+
* @return array Inline modules data.
2489
+
*/
2490
+
private function get_inline_resource_availability_dates_data() {
2491
+
if ( $this->is_connected() ) {
2492
+
return array(
2493
+
'resourceAvailabilityDates' => $this->resource_data_availability_date->get_all_resource_dates(),
2494
+
);
2495
+
}
2496
+
2497
+
return array();
2498
+
}
2499
+
2500
+
/**
2501
+
* Filters whether or not the option to exclude certain users from tracking should be displayed.
2502
+
*
2503
+
* If the Analytics-4 module is enabled, and the snippet is enabled, then the option to exclude
2504
+
* the option to exclude certain users from tracking should be displayed.
2505
+
*
2506
+
* @since 1.101.0
2507
+
*
2508
+
* @param bool $allowed Whether to allow tracking exclusion.
2509
+
* @return bool Filtered value.
2510
+
*/
2511
+
private function filter_analytics_allow_tracking_disabled( $allowed ) {
2512
+
if ( $allowed ) {
2513
+
return $allowed;
2514
+
}
2515
+
2516
+
if ( $this->get_settings()->get()['useSnippet'] ) {
2517
+
return true;
2518
+
}
2519
+
2520
+
return $allowed;
2521
+
}
2522
+
2523
+
/**
2524
+
* Sets and returns available audiences.
2525
+
*
2526
+
* @since 1.126.0
2527
+
*
2528
+
* @param GoogleAnalyticsAdminV1alphaAudience[] $audiences The audiences to set.
2529
+
* @return array The available audiences.
2530
+
*/
2531
+
private function set_available_audiences( $audiences ) {
2532
+
$available_audiences = array_map(
2533
+
function ( GoogleAnalyticsAdminV1alphaAudience $audience ) {
2534
+
$display_name = $audience->getDisplayName();
2535
+
$audience_item = array(
2536
+
'name' => $audience->getName(),
2537
+
'displayName' => ( 'All Users' === $display_name ) ? 'All visitors' : $display_name,
2538
+
'description' => $audience->getDescription(),
2539
+
);
2540
+
2541
+
$audience_slug = $this->get_audience_slug( $audience );
2542
+
$audience_type = $this->get_audience_type( $audience_slug );
2543
+
2544
+
$audience_item['audienceType'] = $audience_type;
2545
+
$audience_item['audienceSlug'] = $audience_slug;
2546
+
2547
+
return $audience_item;
2548
+
},
2549
+
$audiences
2550
+
);
2551
+
2552
+
usort(
2553
+
$available_audiences,
2554
+
function ( $audience_a, $audience_b ) use ( $available_audiences ) {
2555
+
$audience_index_a = array_search( $audience_a, $available_audiences, true );
2556
+
$audience_index_b = array_search( $audience_b, $available_audiences, true );
2557
+
2558
+
if ( false === $audience_index_a || false === $audience_index_b ) {
2559
+
return 0;
2560
+
}
2561
+
2562
+
$audience_a = $available_audiences[ $audience_index_a ];
2563
+
$audience_b = $available_audiences[ $audience_index_b ];
2564
+
2565
+
$audience_type_a = $audience_a['audienceType'];
2566
+
$audience_type_b = $audience_b['audienceType'];
2567
+
2568
+
if ( $audience_type_a === $audience_type_b ) {
2569
+
if ( 'SITE_KIT_AUDIENCE' === $audience_type_b ) {
2570
+
return 'new-visitors' === $audience_a['audienceSlug'] ? -1 : 1;
2571
+
}
2572
+
2573
+
return $audience_index_a - $audience_index_b;
2574
+
}
2575
+
2576
+
$weight_a = self::AUDIENCE_TYPE_SORT_ORDER[ $audience_type_a ];
2577
+
$weight_b = self::AUDIENCE_TYPE_SORT_ORDER[ $audience_type_b ];
2578
+
2579
+
if ( $weight_a === $weight_b ) {
2580
+
return $audience_index_a - $audience_index_b;
2581
+
}
2582
+
2583
+
return $weight_a - $weight_b;
2584
+
}
2585
+
);
2586
+
2587
+
$this->audience_settings->merge(
2588
+
array(
2589
+
'availableAudiences' => $available_audiences,
2590
+
'availableAudiencesLastSyncedAt' => time(),
2591
+
)
2592
+
);
2593
+
2594
+
return $available_audiences;
2595
+
}
2596
+
2597
+
/**
2598
+
* Gets the audience slug.
2599
+
*
2600
+
* @since 1.126.0
2601
+
*
2602
+
* @param GoogleAnalyticsAdminV1alphaAudience $audience The audience object.
2603
+
* @return string The audience slug.
2604
+
*/
2605
+
private function get_audience_slug( GoogleAnalyticsAdminV1alphaAudience $audience ) {
2606
+
$display_name = $audience->getDisplayName();
2607
+
2608
+
if ( 'All Users' === $display_name ) {
2609
+
return 'all-users';
2610
+
}
2611
+
2612
+
if ( 'Purchasers' === $display_name ) {
2613
+
return 'purchasers';
2614
+
}
2615
+
2616
+
$filter_clauses = $audience->getFilterClauses();
2617
+
2618
+
if ( $filter_clauses ) {
2619
+
if ( $this->has_audience_site_kit_identifier(
2620
+
$filter_clauses,
2621
+
'new_visitors'
2622
+
) ) {
2623
+
return 'new-visitors';
2624
+
}
2625
+
2626
+
if ( $this->has_audience_site_kit_identifier(
2627
+
$filter_clauses,
2628
+
'returning_visitors'
2629
+
) ) {
2630
+
return 'returning-visitors';
2631
+
}
2632
+
}
2633
+
2634
+
// Return an empty string for user defined audiences.
2635
+
return '';
2636
+
}
2637
+
2638
+
/**
2639
+
* Gets the audience type based on the audience slug.
2640
+
*
2641
+
* @since 1.126.0
2642
+
*
2643
+
* @param string $audience_slug The audience slug.
2644
+
* @return string The audience type.
2645
+
*/
2646
+
private function get_audience_type( $audience_slug ) {
2647
+
if ( ! $audience_slug ) {
2648
+
return 'USER_AUDIENCE';
2649
+
}
2650
+
2651
+
switch ( $audience_slug ) {
2652
+
case 'all-users':
2653
+
case 'purchasers':
2654
+
return 'DEFAULT_AUDIENCE';
2655
+
case 'new-visitors':
2656
+
case 'returning-visitors':
2657
+
return 'SITE_KIT_AUDIENCE';
2658
+
}
2659
+
}
2660
+
2661
+
/**
2662
+
* Checks if an audience Site Kit identifier
2663
+
* (e.g. `created_by_googlesitekit:new_visitors`) exists in a nested array or object.
2664
+
*
2665
+
* @since 1.126.0
2666
+
*
2667
+
* @param array|object $data The array or object to search.
2668
+
* @param mixed $identifier The identifier to search for.
2669
+
* @return bool True if the value exists, false otherwise.
2670
+
*/
2671
+
private function has_audience_site_kit_identifier( $data, $identifier ) {
2672
+
if ( is_array( $data ) || is_object( $data ) ) {
2673
+
foreach ( $data as $key => $value ) {
2674
+
if ( is_array( $value ) || is_object( $value ) ) {
2675
+
// Recursively search the nested structure.
2676
+
if ( $this->has_audience_site_kit_identifier( $value, $identifier ) ) {
2677
+
return true;
2678
+
}
2679
+
} elseif (
2680
+
'fieldName' === $key &&
2681
+
'groupId' === $value &&
2682
+
isset( $data['stringFilter'] ) &&
2683
+
"created_by_googlesitekit:{$identifier}" === $data['stringFilter']['value']
2684
+
) {
2685
+
return true;
2686
+
}
2687
+
}
2688
+
}
2689
+
2690
+
return false;
2691
+
}
2692
+
2693
+
/**
2694
+
* Returns the Site Kit-created audience display names from the passed list of audiences.
2695
+
*
2696
+
* @since 1.129.0
2697
+
*
2698
+
* @param array $audiences List of audiences.
2699
+
*
2700
+
* @return array List of Site Kit-created audience display names.
2701
+
*/
2702
+
private function get_site_kit_audiences( $audiences ) {
2703
+
// Ensure that audiences are available, otherwise return an empty array.
2704
+
if ( empty( $audiences ) || ! is_array( $audiences ) ) {
2705
+
return array();
2706
+
}
2707
+
2708
+
$site_kit_audiences = array_filter( $audiences, fn ( $audience ) => ! empty( $audience['audienceType'] ) && ( 'SITE_KIT_AUDIENCE' === $audience['audienceType'] ) );
2709
+
2710
+
if ( empty( $site_kit_audiences ) ) {
2711
+
return array();
2712
+
}
2713
+
2714
+
return wp_list_pluck( $site_kit_audiences, 'displayName' );
2715
+
}
2716
+
2717
+
/**
2718
+
* Populates conversion reporting event data to pass to JS via _googlesitekitModulesData.
2719
+
*
2720
+
* @since 1.139.0
2721
+
* @since 1.158.0 Renamed method to `get_inline_conversion_reporting_events_detection()`, and modified it to return a new array rather than populating a passed filter value.
2722
+
*
2723
+
* @return array Inline modules data.
2724
+
*/
2725
+
private function get_inline_conversion_reporting_events_detection() {
2726
+
if ( ! $this->is_connected() ) {
2727
+
return array();
2728
+
}
2729
+
2730
+
$detected_events = $this->transients->get( Conversion_Reporting_Events_Sync::DETECTED_EVENTS_TRANSIENT );
2731
+
$lost_events = $this->transients->get( Conversion_Reporting_Events_Sync::LOST_EVENTS_TRANSIENT );
2732
+
$new_events_badge = $this->transients->get( Conversion_Reporting_New_Badge_Events_Sync::NEW_EVENTS_BADGE_TRANSIENT );
2733
+
2734
+
return array(
2735
+
'newEvents' => is_array( $detected_events ) ? $detected_events : array(),
2736
+
'lostEvents' => is_array( $lost_events ) ? $lost_events : array(),
2737
+
'newBadgeEvents' => is_array( $new_events_badge ) ? $new_events_badge['events'] : array(),
2738
+
);
2739
+
}
2740
+
2741
+
/**
2742
+
* Refines the requested scopes based on the current authentication and connection state.
2743
+
*
2744
+
* Specifically, the `EDIT_SCOPE` is only added if the user is not yet authenticated,
2745
+
* or if they are authenticated and have already granted the scope, or if the module
2746
+
* is not yet connected (i.e. during setup).
2747
+
*
2748
+
* @since 1.163.0
2749
+
*
2750
+
* @param string[] $scopes Array of requested scopes.
2751
+
* @return string[] Refined array of requested scopes.
2752
+
*/
2753
+
private function get_refined_scopes( $scopes = array() ) {
2754
+
if ( ! Feature_Flags::enabled( 'setupFlowRefresh' ) ) {
2755
+
return $scopes;
2756
+
}
2757
+
2758
+
if ( ! $this->authentication->is_authenticated() ) {
2759
+
$scopes[] = self::EDIT_SCOPE;
2760
+
return $scopes;
2761
+
}
2762
+
2763
+
$oauth_client = $this->authentication->get_oauth_client();
2764
+
$granted_scopes = $oauth_client->get_granted_scopes();
2765
+
$is_in_setup_process = ! $this->is_connected();
2766
+
2767
+
if ( in_array( self::EDIT_SCOPE, $granted_scopes, true ) || $is_in_setup_process ) {
2768
+
$scopes[] = self::EDIT_SCOPE;
2769
+
}
2770
+
2771
+
return $scopes;
2772
+
}
2773
+
2774
+
/**
2775
+
* Gets required inline data for the module.
2776
+
*
2777
+
* @since 1.158.0
2778
+
* @since 1.160.0 Include $modules_data parameter to match the interface.
2779
+
*
2780
+
* @param array $modules_data Inline modules data.
2781
+
* @return array An array of the module's inline data.
2782
+
*/
2783
+
public function get_inline_data( $modules_data ) {
2784
+
if ( ! $this->is_connected() ) {
2785
+
return $modules_data;
2786
+
}
2787
+
2788
+
$inline_data = array();
2789
+
2790
+
// Web data stream availability data.
2791
+
$settings = $this->get_settings()->get();
2792
+
$transient_key = 'googlesitekit_web_data_stream_unavailable_' . $settings['webDataStreamID'];
2793
+
$is_web_data_stream_unavailable = $this->transients->get( $transient_key );
2794
+
$inline_data['isWebDataStreamUnavailable'] = (bool) $is_web_data_stream_unavailable;
2795
+
2796
+
$inline_data = array_merge(
2797
+
$inline_data,
2798
+
$this->get_inline_custom_dimensions_data(),
2799
+
$this->get_inline_tag_id_mismatch(),
2800
+
$this->get_inline_resource_availability_dates_data(),
2801
+
$this->get_inline_conversion_reporting_events_detection()
2802
+
);
2803
+
2804
+
$modules_data[ self::MODULE_SLUG ] = $inline_data;
2805
+
return $modules_data;
2806
+
}
2807
+
}
2808
+