You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In #12765, we abruptly prevent showing the widget if an event provider is deactivated after the widget was setup - this should not be the case for now. In the future, Issues #12773 and #12774 will implement a flow with appropriate notices to remove the widget with the user's full understanding. So for now, the Site Goals widgets should rely on a persisted state for their rendering: once a widget has been shown, it should stay shown even if the underlying conditions later change (e.g. the corresponding event provider plugin is deactivated, or events stop being detected).
Furthermore, both the new internal feature metric (#12776) and the Site Health debug field (#12601) need to know which Site Goals widgets (Online store performance / Lead generation performance) are currently shown to the user. Rather than recomputing the Widget Rendering Conditions (implemented in #12765) in multiple places, we should compute this state once and persist it server-side in the database, so it can be read by any consumer.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
A Site Goals widget should become "active" when its rendering conditions are met:
ecommerce: at least one e-commerce event is detected and at least one e-commerce event provider plugin (WooCommerce or Easy Digital Downloads) is active.
lead: at least one lead event is detected and at least one lead event provider plugin (Contact Form 7, Mailchimp, Ninja Forms, OptinMonster, Popup Maker or WPForms) is active.
The active state of the widgets should be persisted in the database as a new site-wide setting.
Once a widget has become active, it must remain active even if its rendering conditions are no longer met.
The persisted state should be readable in both the standard and view-only dashboard contexts.
Implementation Brief
Add categorized event-name constants to includes/Modules/Analytics_4/Conversion_Reporting/Conversion_Reporting_Events_Sync.php, mirroring the JS constants CONVERSION_REPORTING_LEAD_EVENTS / CONVERSION_REPORTING_ECOMMERCE_EVENTS in assets/js/modules/analytics-4/datastore/constants.ts:
In includes/Core/Conversion_Tracking/Conversion_Tracking.php, extract the provider-category logic currently inlined in inline_js_base_data() (which sets hasActiveLeadEventProviders / hasActiveEcommerceEventProviders from Hide Site Goals widgets if corresponding event provider plugins are not installed #12765) into a new public get_active_provider_categories() method returning the unique get_category() values of get_active_providers() (a subset of array( Conversion_Events_Provider::CATEGORY_LEAD, Conversion_Events_Provider::CATEGORY_ECOMMERCE )). Refactor inline_js_base_data() to use it.
Free up the Modules\Analytics_4\Site_Goals_Settings class name by moving the existing per-user Site Goals settings class (introduced in Add Site goals user settings and persist Site Goals side panel metric selections #12780, currently includes/Modules/Analytics_4/Site_Goals_Settings.php, extends User_Setting) into the Core/User namespace — similar to the existing Core/User/Audience_Settings ↔ Modules/Analytics_4/Audience_Settings split:
Move it to includes/Core/User/Site_Goals_Settings.php (Google\Site_Kit\Core\User\Site_Goals_Settings) and change its OPTION from googlesitekit_analytics-4_site_goals_settings to googlesitekit_site_goals_settings.
Update the use imports / references in Get_Site_Goals_Settings, Save_Site_Goals_Settings and Analytics_4 to the new namespace. To avoid confusion with the new site-wide class below, hold the per-user instance under a distinct property (e.g. $this->user_site_goals_settings) and update the datapoint wiring accordingly.
Add the new site-wide setting class to persist the active-widgets state, following the site-wide Audience_Settings pattern (includes/Modules/Analytics_4/Audience_Settings.php):
Create includes/Modules/Analytics_4/Site_Goals_Settings.php (Google\Site_Kit\Modules\Analytics_4\Site_Goals_Settings) extending Setting and implementing Setting_With_ViewOnly_Keys_Interface, with OPTION = 'googlesitekit_analytics-4_site_goals_settings' (the module-prefixed name, freed up by the move above).
Single key activeWidgets defaulting to array(); sanitize to a unique subset of array( 'ecommerce', 'lead' ) (reuse Sanitize::sanitize_string_list and filter to the allowed enum values).
Mark activeWidgets as a view-only key so it is exposed in the view-only dashboard.
Add a merge() helper (as the site-wide Audience_Settings does) that unions the incoming categories with the existing ones and never drops existing values.
Instantiate and register() it in Analytics_4::register() alongside audience_settings, and store it on a $site_goals_settings property (mirroring $this->audience_settings).
Persist the state during the detected-events sync. In Conversion_Reporting_Provider::cron_callback(), after sync_detected_events() computes the current detectedEvents:
Compute the qualifying widget categories: include ecommerce when ECOMMERCE_EVENT_NAMES intersects the detected events andConversion_Events_Provider::CATEGORY_ECOMMERCE is in Conversion_Tracking::get_active_provider_categories(); include lead analogously.
merge() these into the site-wide Site_Goals_Settings (union with existing activeWidgets), so the value only ever grows. Conversion_Tracking can be instantiated from the Context already available to the provider.
Make the Site Goals widgets render from the persisted state:
Expose activeWidgets to JS through the existing modules/analytics-4 settings datastore (it is part of the registered setting and a view-only key), and add a small selector/helper to read whether a given category is active.
Update the isActive callbacks of the analyticsOnlineStorePerformance and analyticsLeadGenerationPerformance widgets in assets/js/modules/analytics-4/widgets/index.js so a widget renders when its category is present in activeWidgets.
Test Coverage
Add a Site_Goals_SettingsTest for the new site-wide class (mirroring tests/phpunit/integration/Modules/Analytics_4/Audience_SettingsTest.php) asserting the default is an empty array, that the sanitize callback drops invalid values and de-duplicates, and that only ecommerce/lead are allowed. Move/update the existing per-user Site_Goals_Settings test to match its new Core/User namespace and option name.
In tests/phpunit/integration/Core/Conversion_Tracking/Conversion_TrackingTest.php, add test_get_active_provider_categories() covering no providers, lead only, ecommerce only, and both (using the existing FakeLeadEventProvider_Active / FakeEcommerceEventProvider_Active fixtures), and confirm the existing test_inline_js_base_data__* tests still pass after the refactor.
Detected events of a set with no active provider of that category => category is not added.
Active provider of a category with no detected events of that set => category is not added.
Both conditions met => category is added to activeWidgets.
Stickiness: once a category is persisted, a subsequent sync where the conditions are no longer met leaves it in activeWidgets.
Both categories can be present simultaneously.
Add JS tests for the widget isActive logic (alongside assets/js/modules/analytics-4/widgets/index.test.js) asserting that each Site Goals widget renders when its category is in activeWidgets even when the live conditions are not met, and renders via the live conditions when activeWidgets is still empty.
QA Brief
Setup SK with siteGoals feature flag, Analytics connected, Tester plugin installed.
After changing detected events/plugins, trigger the conversion-reporting events sync cron using WP Crontrol (googlesitekit_cron_conversion_reporting_events action). Inspect state with wp option get googlesitekit_analytics-4_site_goals_settings (or check in the wp_options table).
Tester Settings → Analytics → Conversion reporting, set an ecommerce event (e.g. purchase).
Activate WooCommerce (or Easy Digital Downloads) and trigger the sync.
Verify the Online store performance widget shows and activeWidgets contains ecommerce.
Deactivate the plugin and re-sync → verify the widget still shows (sticky).
Repeat with a lead event (e.g. contact) and a lead plugin (e.g. Contact Form 7).
Verify the Lead generation performance widget shows and activeWidgets contains lead.
Deactivate the plugin and re-sync → verify the widget still shows.
Event detected but no matching provider active → widget does not show / category not added.
Provider active but no matching event → widget does not show / category not added.
Confirm the persisted widget also renders in the view-only dashboard.
Changelog entry
Site Goals widget status are now site-wide with other settings being user-specific.
Feature Description
In #12765, we abruptly prevent showing the widget if an event provider is deactivated after the widget was setup - this should not be the case for now. In the future, Issues #12773 and #12774 will implement a flow with appropriate notices to remove the widget with the user's full understanding. So for now, the Site Goals widgets should rely on a persisted state for their rendering: once a widget has been shown, it should stay shown even if the underlying conditions later change (e.g. the corresponding event provider plugin is deactivated, or events stop being detected).
Furthermore, both the new internal feature metric (#12776) and the Site Health debug field (#12601) need to know which Site Goals widgets (
Online store performance/Lead generation performance) are currently shown to the user. Rather than recomputing the Widget Rendering Conditions (implemented in #12765) in multiple places, we should compute this state once and persist it server-side in the database, so it can be read by any consumer.Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
ecommerce: at least one e-commerce event is detected and at least one e-commerce event provider plugin (WooCommerce or Easy Digital Downloads) is active.lead: at least one lead event is detected and at least one lead event provider plugin (Contact Form 7, Mailchimp, Ninja Forms, OptinMonster, Popup Maker or WPForms) is active.Implementation Brief
includes/Modules/Analytics_4/Conversion_Reporting/Conversion_Reporting_Events_Sync.php, mirroring the JS constantsCONVERSION_REPORTING_LEAD_EVENTS/CONVERSION_REPORTING_ECOMMERCE_EVENTSinassets/js/modules/analytics-4/datastore/constants.ts:LEAD_EVENT_NAMES = array( 'contact', 'generate_lead', 'submit_lead_form' )ECOMMERCE_EVENT_NAMES = array( 'purchase', 'add_to_cart' )includes/Core/Conversion_Tracking/Conversion_Tracking.php, extract the provider-category logic currently inlined ininline_js_base_data()(which setshasActiveLeadEventProviders/hasActiveEcommerceEventProvidersfrom Hide Site Goals widgets if corresponding event provider plugins are not installed #12765) into a new publicget_active_provider_categories()method returning the uniqueget_category()values ofget_active_providers()(a subset ofarray( Conversion_Events_Provider::CATEGORY_LEAD, Conversion_Events_Provider::CATEGORY_ECOMMERCE )). Refactorinline_js_base_data()to use it.Modules\Analytics_4\Site_Goals_Settingsclass name by moving the existing per-user Site Goals settings class (introduced in Add Site goals user settings and persist Site Goals side panel metric selections #12780, currentlyincludes/Modules/Analytics_4/Site_Goals_Settings.php,extends User_Setting) into theCore/Usernamespace — similar to the existingCore/User/Audience_Settings↔Modules/Analytics_4/Audience_Settingssplit:includes/Core/User/Site_Goals_Settings.php(Google\Site_Kit\Core\User\Site_Goals_Settings) and change itsOPTIONfromgooglesitekit_analytics-4_site_goals_settingstogooglesitekit_site_goals_settings.useimports / references inGet_Site_Goals_Settings,Save_Site_Goals_SettingsandAnalytics_4to the new namespace. To avoid confusion with the new site-wide class below, hold the per-user instance under a distinct property (e.g.$this->user_site_goals_settings) and update the datapoint wiring accordingly.Audience_Settingspattern (includes/Modules/Analytics_4/Audience_Settings.php):includes/Modules/Analytics_4/Site_Goals_Settings.php(Google\Site_Kit\Modules\Analytics_4\Site_Goals_Settings) extendingSettingand implementingSetting_With_ViewOnly_Keys_Interface, withOPTION = 'googlesitekit_analytics-4_site_goals_settings'(the module-prefixed name, freed up by the move above).activeWidgetsdefaulting toarray(); sanitize to a unique subset ofarray( 'ecommerce', 'lead' )(reuseSanitize::sanitize_string_listand filter to the allowed enum values).activeWidgetsas a view-only key so it is exposed in the view-only dashboard.merge()helper (as the site-wideAudience_Settingsdoes) that unions the incoming categories with the existing ones and never drops existing values.register()it inAnalytics_4::register()alongsideaudience_settings, and store it on a$site_goals_settingsproperty (mirroring$this->audience_settings).Conversion_Reporting_Provider::cron_callback(), aftersync_detected_events()computes the currentdetectedEvents:ecommercewhenECOMMERCE_EVENT_NAMESintersects the detected events andConversion_Events_Provider::CATEGORY_ECOMMERCEis inConversion_Tracking::get_active_provider_categories(); includeleadanalogously.merge()these into the site-wideSite_Goals_Settings(union with existingactiveWidgets), so the value only ever grows.Conversion_Trackingcan be instantiated from theContextalready available to the provider.activeWidgetsto JS through the existingmodules/analytics-4settings datastore (it is part of the registered setting and a view-only key), and add a small selector/helper to read whether a given category is active.isActivecallbacks of theanalyticsOnlineStorePerformanceandanalyticsLeadGenerationPerformancewidgets inassets/js/modules/analytics-4/widgets/index.jsso a widget renders when its category is present inactiveWidgets.Test Coverage
Site_Goals_SettingsTestfor the new site-wide class (mirroringtests/phpunit/integration/Modules/Analytics_4/Audience_SettingsTest.php) asserting the default is an empty array, that the sanitize callback drops invalid values and de-duplicates, and that onlyecommerce/leadare allowed. Move/update the existing per-userSite_Goals_Settingstest to match its newCore/Usernamespace and option name.tests/phpunit/integration/Core/Conversion_Tracking/Conversion_TrackingTest.php, addtest_get_active_provider_categories()covering no providers, lead only, ecommerce only, and both (using the existingFakeLeadEventProvider_Active/FakeEcommerceEventProvider_Activefixtures), and confirm the existingtest_inline_js_base_data__*tests still pass after the refactor.Conversion_Reporting_Events_Sync/Conversion_Reporting_Providertests) asserting:activeWidgets.activeWidgets.isActivelogic (alongsideassets/js/modules/analytics-4/widgets/index.test.js) asserting that each Site Goals widget renders when its category is inactiveWidgetseven when the live conditions are not met, and renders via the live conditions whenactiveWidgetsis still empty.QA Brief
siteGoalsfeature flag, Analytics connected, Tester plugin installed.googlesitekit_cron_conversion_reporting_eventsaction). Inspect state withwp option get googlesitekit_analytics-4_site_goals_settings(or check in the wp_options table).purchase).activeWidgetscontainsecommerce.contact) and a lead plugin (e.g. Contact Form 7).activeWidgetscontainslead.Changelog entry