diff --git a/includes/Integrations/Site_Kit.php b/includes/Integrations/Site_Kit.php index 837f94c99434..145a05e5ccbc 100644 --- a/includes/Integrations/Site_Kit.php +++ b/includes/Integrations/Site_Kit.php @@ -31,6 +31,7 @@ use Google\Web_Stories\Analytics; use Google\Web_Stories\Context; use Google\Web_Stories\Service_Base; +use Google\Web_Stories\Settings; /** * Class Site_Kit. @@ -64,17 +65,26 @@ class Site_Kit extends Service_Base { */ private Plugin_Status $plugin_status; + /** + * Settings instance. + * + * @var Settings Settings instance. + */ + private Settings $settings; + /** * Constructor. * * @param Analytics $analytics Analytics instance. * @param Context $context Context instance. * @param Plugin_Status $plugin_status Plugin_Status instance. + * @param Settings $settings Settings instance. */ - public function __construct( Analytics $analytics, Context $context, Plugin_Status $plugin_status ) { + public function __construct( Analytics $analytics, Context $context, Plugin_Status $plugin_status, Settings $settings ) { $this->analytics = $analytics; $this->context = $context; $this->plugin_status = $plugin_status; + $this->settings = $settings; } /** @@ -85,7 +95,19 @@ public function __construct( Analytics $analytics, Context $context, Plugin_Stat public function register(): void { add_filter( 'googlesitekit_amp_gtag_opt', [ $this, 'filter_site_kit_gtag_opt' ] ); - if ( $this->is_analytics_module_active() ) { + $handler = $this->settings->get_setting( $this->settings::SETTING_NAME_TRACKING_HANDLER ); + + if ( 'web-stories' === $handler ) { + add_filter( + 'googlesitekit_analytics-4_tag_amp_blocked', + function ( $blocked ) { + if ( $this->context->is_web_story() ) { + return true; + } + return $blocked; + } + ); + } elseif ( 'site-kit' === $handler && $this->is_analytics_module_active() ) { remove_action( 'web_stories_print_analytics', [ $this->analytics, 'print_analytics_tag' ] ); } } diff --git a/includes/Settings.php b/includes/Settings.php index 9c2b0f736c10..d61e424c977e 100644 --- a/includes/Settings.php +++ b/includes/Settings.php @@ -144,6 +144,11 @@ class Settings implements Service, Registerable, PluginUninstallAware { */ public const SETTING_NAME_DEFAULT_PAGE_DURATION = 'web_stories_default_page_duration'; + /** + * GA Tracking Handler. + */ + public const SETTING_NAME_TRACKING_HANDLER = 'web_stories_ga_tracking_handler'; + /** * Shopping_Vendors instance. * @@ -403,6 +408,22 @@ public function register(): void { 'show_in_rest' => true, ] ); + + register_setting( + self::SETTING_GROUP, + self::SETTING_NAME_TRACKING_HANDLER, + [ + 'description' => __( 'Tracking Handler', 'web-stories' ), + 'type' => 'string', + 'default' => 'site-kit', + 'show_in_rest' => [ + 'schema' => [ + 'type' => 'string', + 'enum' => [ 'site-kit', 'web-stories', 'both' ], + ], + ], + ] + ); } /** @@ -495,6 +516,7 @@ public function on_plugin_uninstall(): void { delete_option( self::SETTING_NAME_SHOPIFY_ACCESS_TOKEN ); delete_option( self::SETTING_NAME_DEFAULT_PAGE_DURATION ); delete_option( self::SETTING_NAME_AUTO_ADVANCE ); + delete_option( self::SETTING_NAME_TRACKING_HANDLER ); } /** diff --git a/packages/e2e-tests/src/specs/integrations/sitekit.js b/packages/e2e-tests/src/specs/integrations/sitekit.js index 7783935c49ff..59fe07717d09 100644 --- a/packages/e2e-tests/src/specs/integrations/sitekit.js +++ b/packages/e2e-tests/src/specs/integrations/sitekit.js @@ -33,16 +33,6 @@ describe('Site Kit plugin integration', () => { describe('Google Analytics', () => { withPlugin('e2e-tests-site-kit-analytics-mock'); - describe('Dashboard', () => { - it('should see Site Kit specific message', async () => { - await visitSettings(); - - await expect(page).toMatchTextContent( - 'Site Kit by Google has already enabled Google Analytics for your Web Stories' - ); - }); - }); - describe('Editor', () => { afterAll(async () => { await trashAllPosts('web-story'); @@ -69,7 +59,7 @@ describe('Site Kit plugin integration', () => { withPlugin('e2e-tests-site-kit-adsense-mock'); describe('Dashboard', () => { - it('should see Site Kit specific message', async () => { + it('should see Google AdSense specific message', async () => { await visitSettings(); await expect(page).toMatchTextContent( diff --git a/packages/wp-dashboard/src/api/reducers/settings.js b/packages/wp-dashboard/src/api/reducers/settings.js index c59ce1d23ffe..26c5cb780792 100644 --- a/packages/wp-dashboard/src/api/reducers/settings.js +++ b/packages/wp-dashboard/src/api/reducers/settings.js @@ -20,6 +20,7 @@ import { AD_NETWORK_TYPE, ARCHIVE_TYPE, SHOPPING_PROVIDER_TYPE, + GOOGLE_ANALYTICS_HANDLER_TYPE, } from '../../constants'; export const ACTION_TYPES = { @@ -49,6 +50,7 @@ export const defaultSettingsState = { shopifyAccessToken: '', autoAdvance: true, defaultPageDuration: 7, + googleAnalyticsHandler: GOOGLE_ANALYTICS_HANDLER_TYPE.SITE_KIT, }; function settingsReducer(state, action) { @@ -89,6 +91,7 @@ function settingsReducer(state, action) { shopifyAccessToken: action.payload.shopifyAccessToken, autoAdvance: action.payload.autoAdvance, defaultPageDuration: action.payload.defaultPageDuration, + googleAnalyticsHandler: action.payload.googleAnalyticsHandler, }; } diff --git a/packages/wp-dashboard/src/api/settings.js b/packages/wp-dashboard/src/api/settings.js index 0566079d7df4..faaef4e44362 100644 --- a/packages/wp-dashboard/src/api/settings.js +++ b/packages/wp-dashboard/src/api/settings.js @@ -46,6 +46,7 @@ const transformSettingResponse = (response) => ({ shopifyAccessToken: response.web_stories_shopify_access_token, autoAdvance: Boolean(response.web_stories_auto_advance), defaultPageDuration: response.web_stories_default_page_duration, + googleAnalyticsHandler: response.web_stories_ga_tracking_handler, }); /** @@ -86,6 +87,7 @@ export function updateSettings(apiPath, queryParams) { shopifyAccessToken, autoAdvance, defaultPageDuration, + googleAnalyticsHandler, } = queryParams; const query = {}; @@ -154,6 +156,10 @@ export function updateSettings(apiPath, queryParams) { query.web_stories_default_page_duration = defaultPageDuration; } + if (googleAnalyticsHandler !== undefined) { + query.web_stories_ga_tracking_handler = googleAnalyticsHandler; + } + const path = addQueryArgs(apiPath, query); return apiFetch({ diff --git a/packages/wp-dashboard/src/components/editorSettings/editorSettings.js b/packages/wp-dashboard/src/components/editorSettings/editorSettings.js index 3c9645fd643b..faf8acc5003a 100644 --- a/packages/wp-dashboard/src/components/editorSettings/editorSettings.js +++ b/packages/wp-dashboard/src/components/editorSettings/editorSettings.js @@ -85,6 +85,7 @@ function EditorSettings() { shopifyAccessToken, autoAdvance, defaultPageDuration, + googleAnalyticsHandler, } = useEditorSettings( ({ actions: { @@ -117,6 +118,7 @@ function EditorSettings() { shopifyAccessToken, autoAdvance, defaultPageDuration, + googleAnalyticsHandler, }, media: { isLoading: isMediaLoading, newlyCreatedMediaIds }, publisherLogos: { publisherLogos }, @@ -155,6 +157,7 @@ function EditorSettings() { shopifyAccessToken, autoAdvance, defaultPageDuration, + googleAnalyticsHandler, }) ); @@ -329,6 +332,12 @@ function EditorSettings() { [setPublisherLogoAsDefault] ); + const handleUpdateGoogleAnalyticsHandler = useCallback( + (newGoogleAnalyticsHandler) => + updateSettings({ googleAnalyticsHandler: newGoogleAnalyticsHandler }), + [updateSettings] + ); + return ( @@ -346,6 +355,10 @@ function EditorSettings() { usingLegacyAnalytics={usingLegacyAnalytics} handleMigrateLegacyAnalytics={handleMigrateLegacyAnalytics} siteKitStatus={siteKit} + googleAnalyticsHandler={googleAnalyticsHandler} + handleUpdateGoogleAnalyticsHandler={ + handleUpdateGoogleAnalyticsHandler + } /> Note: If Site Kit is active, it will be used to set up Google Analytics by default. However, you can customize the behavior in case you need more flexibility.', 'web-stories' ), + ANALYTICS_DROPDOWN_LABEL: __('Analytics Type', 'web-stories'), + ANALYTICS_DROPDOWN_PLACEHOLDER: __('Select Analytics Type', 'web-stories'), }; +export const ANALYTICS_DROPDOWN_OPTIONS = [ + { + value: GOOGLE_ANALYTICS_HANDLER_TYPE.SITE_KIT, + label: __('Use Site Kit for analytics (default)', 'web-stories'), + }, + { + value: GOOGLE_ANALYTICS_HANDLER_TYPE.WEB_STORIES, + label: __('Use only Web Stories for analytics', 'web-stories'), + }, + { + value: GOOGLE_ANALYTICS_HANDLER_TYPE.BOTH, + label: __('Use both', 'web-stories'), + }, +]; + +const DropdownContainer = styled.div` + padding-top: 12px; +`; + const WarningContainer = styled.div` display: flex; gap: 8px; @@ -111,8 +134,13 @@ function GoogleAnalyticsSettings({ usingLegacyAnalytics, handleMigrateLegacyAnalytics, siteKitStatus = {}, + googleAnalyticsHandler, + handleUpdateGoogleAnalyticsHandler, }) { const [analyticsId, setAnalyticsId] = useState(googleAnalyticsId); + const [analyticsHandler, setAnalyticsHandler] = useState( + googleAnalyticsHandler + ); const [inputError, setInputError] = useState(''); const canSave = analyticsId !== googleAnalyticsId && !inputError; const disableSaveButton = !canSave; @@ -123,6 +151,10 @@ function GoogleAnalyticsSettings({ setAnalyticsId(googleAnalyticsId); }, [googleAnalyticsId]); + useEffect(() => { + setAnalyticsHandler(googleAnalyticsHandler); + }, [googleAnalyticsHandler]); + const onUpdateAnalyticsId = useCallback((event) => { const { value } = event.target; setAnalyticsId(value); @@ -136,6 +168,14 @@ function GoogleAnalyticsSettings({ setInputError(TEXT.INPUT_ERROR); }, []); + const onUpdateAnalyticsHandler = useCallback( + (value) => { + setAnalyticsHandler(value); + handleUpdateGoogleAnalyticsHandler(value); + }, + [handleUpdateGoogleAnalyticsHandler] + ); + const handleOnSave = useCallback(() => { if (canSave) { handleUpdateAnalyticsId(analyticsId); @@ -247,97 +287,107 @@ function GoogleAnalyticsSettings({ )}
- {analyticsActive ? ( - - {TEXT.SITE_KIT_IN_USE} - - ) : ( + + + {TEXT.ARIA_LABEL} + + + + {TEXT.SUBMIT_BUTTON} + + + + + ), + }} + > + {TEXT.CONTEXT} + + + {analyticsActive && ( <> - - - {TEXT.ARIA_LABEL} - - - - {TEXT.SUBMIT_BUTTON} - - + {TEXT.SITE_KIT_IN_USE} + + + { + onUpdateAnalyticsHandler(newValue); + }} + selectedValue={analyticsHandler} + /> + + + )} + {!googleAnalyticsId || googleAnalyticsId.startsWith('UA-') ? ( + + + + trackClick(evt, 'click_ua_deprecation_docs') + } + /> + ), + a2: ( + trackClick(evt, 'click_ga4_docs')} /> ), }} > - {TEXT.CONTEXT} + {__( + 'As previously announced, Universal Analytics will stop processing new visits starting July 1, 2023. We recommend switching to Google Analytics 4 (GA4), our analytics product of record.', + 'web-stories' + )} - - {!googleAnalyticsId || googleAnalyticsId.startsWith('UA-') ? ( - - - - - trackClick(evt, 'click_ua_deprecation_docs') - } - /> - ), - a2: ( - trackClick(evt, 'click_ga4_docs')} - /> - ), - }} - > - {__( - 'As previously announced, Universal Analytics will stop processing new visits starting July 1, 2023. We recommend switching to Google Analytics 4 (GA4), our analytics product of record.', - 'web-stories' - )} - - - - ) : null} - - )} + + + ) : null}
); @@ -355,6 +405,8 @@ GoogleAnalyticsSettings.propTypes = { analyticsActive: PropTypes.bool, analyticsLink: PropTypes.string, }), + googleAnalyticsHandler: PropTypes.string, + handleUpdateGoogleAnalyticsHandler: PropTypes.func, }; export default GoogleAnalyticsSettings; diff --git a/packages/wp-dashboard/src/components/editorSettings/googleAnalytics/test/googleAnalytics.js b/packages/wp-dashboard/src/components/editorSettings/googleAnalytics/test/googleAnalytics.js index aa0edcec9e79..249904279797 100644 --- a/packages/wp-dashboard/src/components/editorSettings/googleAnalytics/test/googleAnalytics.js +++ b/packages/wp-dashboard/src/components/editorSettings/googleAnalytics/test/googleAnalytics.js @@ -21,12 +21,15 @@ import { fireEvent, screen } from '@testing-library/react'; /** * Internal dependencies */ -import GoogleAnalyticsSettings, { TEXT } from '..'; +import GoogleAnalyticsSettings, { TEXT, ANALYTICS_DROPDOWN_OPTIONS } from '..'; import { renderWithProviders } from '../../../../testUtils'; +const SITE_KIT_MESSAGE = TEXT.SITE_KIT_IN_USE.replace('Note: ', ''); + describe('Editor Settings: Google Analytics ', () => { let googleAnalyticsId; let mockUpdate; + let mockHandleUpdateGoogleAnalyticsHandler; const defaultSiteKitStatus = { installed: false, analyticsActive: false, @@ -38,6 +41,7 @@ describe('Editor Settings: Google Analytics ', () => { mockUpdate = jest.fn((id) => { googleAnalyticsId = id; }); + mockHandleUpdateGoogleAnalyticsHandler = jest.fn(); }); afterEach(() => { @@ -76,7 +80,7 @@ describe('Editor Settings: Google Analytics ', () => { expect(label).toBeInTheDocument(); }); - it('should not display any input field when analytics module is active', () => { + it('should display input field when analytics module is active', () => { renderWithProviders( ', () => { ); const input = screen.queryByRole('textbox'); - expect(input).not.toBeInTheDocument(); + expect(input).toBeInTheDocument(); }); - it('should allow the input to be active when Site Kit is installed but analytics module is not active', () => { + it('should display analytics type dropdown when analytics module is active', () => { renderWithProviders( ); - const input = screen.getByRole('textbox'); - expect(input).toBeEnabled(); + const dropdown = screen.getByLabelText(TEXT.ANALYTICS_DROPDOWN_LABEL); + expect(dropdown).toBeInTheDocument(); }); it('should call mockUpdate when enter is keyed on input', () => { @@ -212,4 +216,49 @@ describe('Editor Settings: Google Analytics ', () => { expect(mockUpdate).toHaveBeenCalledTimes(2); }); + + it('should display Site Kit message when analytics module is active', () => { + renderWithProviders( + + ); + + expect(screen.getByText(SITE_KIT_MESSAGE)).toBeInTheDocument(); + }); + + it('should call handleUpdateGoogleAnalyticsHandler when the dropdown value changes', () => { + renderWithProviders( + + ); + + const dropdown = screen.getByLabelText(TEXT.ANALYTICS_DROPDOWN_LABEL); + fireEvent.click(dropdown); + + ANALYTICS_DROPDOWN_OPTIONS.forEach((option) => { + const optionElement = screen.getByText(option.label); + fireEvent.click(optionElement); + expect(mockHandleUpdateGoogleAnalyticsHandler).toHaveBeenCalledWith( + option.value + ); + }); + }); }); diff --git a/packages/wp-dashboard/src/constants/settings.js b/packages/wp-dashboard/src/constants/settings.js index 57b267d12014..652ee7c3996a 100644 --- a/packages/wp-dashboard/src/constants/settings.js +++ b/packages/wp-dashboard/src/constants/settings.js @@ -54,3 +54,9 @@ export const SHOPPING_PROVIDER_TYPE = { WOOCOMMERCE: 'woocommerce', SHOPIFY: 'shopify', }; + +export const GOOGLE_ANALYTICS_HANDLER_TYPE = { + SITE_KIT: 'site-kit', + WEB_STORIES: 'web-stories', + BOTH: 'both', +}; diff --git a/tests/phpunit/integration/tests/Integrations/Site_Kit.php b/tests/phpunit/integration/tests/Integrations/Site_Kit.php index 878ee8e63e1b..27d22a9cb1a0 100644 --- a/tests/phpunit/integration/tests/Integrations/Site_Kit.php +++ b/tests/phpunit/integration/tests/Integrations/Site_Kit.php @@ -23,6 +23,7 @@ use Google\Web_Stories\Analytics; use Google\Web_Stories\Context; use Google\Web_Stories\Integrations\Plugin_Status; +use Google\Web_Stories\Settings; use Google\Web_Stories\Story_Post_Type; use Google\Web_Stories\Tests\Integration\DependencyInjectedTestCase; @@ -130,7 +131,8 @@ public function test_filter_site_kit_gtag_opt_single_story(): void { $this->instance = new \Google\Web_Stories\Integrations\Site_Kit( $analytics, $this->injector->make( Context::class ), - $this->injector->make( Plugin_Status::class ) + $this->injector->make( Plugin_Status::class ), + $this->injector->make( Settings::class ), ); $actual = $this->instance->filter_site_kit_gtag_opt( $gtag );