Skip to content

Commit 20d82b2

Browse files
dognose24claude
andauthored
Premium Analytics: Offer switching off only while there is a classic Stats page (#53792)
* Premium Analytics: Offer switching off only while there is a classic Stats page "Switch off the preview" writes the opt-in off and sends the reader to admin.php?page=stats. Once the Jetpack plugin stops registering that page, the entry would only loop back here with the opt-in off. The script data now says whether a plugin registers the classic Stats menu page, and the page options menu hides the entry when none does. A server that predates the flag keeps the entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Premium Analytics: Publish the classic Stats page URL instead of a flag menu_page_url() also finds the page when the Jetpack plugin registers it under its own menu, and the client follows the URL rather than assuming admin.php?page=stats. The changelog entries move to the plugins that bundle the package. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Premium Analytics: Order the page options menu imports Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 02ea4c3 commit 20d82b2

11 files changed

Lines changed: 119 additions & 5 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: changed
3+
4+
Page options: offer switching the dashboard off only while the site still has a classic Stats page to return to.

‎projects/packages/premium-analytics/packages/site-sync/src/jetpack-script-data.d.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ declare module '@automattic/jetpack-script-data' {
1515
has_videopress?: boolean;
1616
// Whether the reader may see Stats reports; a shop manager may see only the store's.
1717
can_view_stats?: boolean;
18+
// Where switching the dashboard off lands: the classic Stats page, or null while no plugin registers one.
19+
classic_stats_url?: string | null;
1820
// Slugs of the tabs the dashboard exposes. Absent until the section registry is hydrated.
1921
sections?: string[];
2022
// Slugs of the tabs whose dates follow the plan's Stats limits. Absent with `sections`.

‎projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-options-menu/__tests__/page-options-menu.test.tsx‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ describe( 'page-options-menu', () => {
112112
).not.toBeInTheDocument();
113113
} );
114114

115+
it( 'offers no switching off where there is no classic Stats page to return to', async () => {
116+
jest.mocked( getScriptData ).mockReturnValue( {
117+
premium_analytics: { classic_stats_url: null },
118+
} as ReturnType< typeof getScriptData > );
119+
const user = setupUser();
120+
render( <PageOptionsMenu /> );
121+
122+
await user.click( screen.getByRole( 'button', { name: 'Page options' } ) );
123+
124+
await expect(
125+
screen.findByRole( 'menuitem', { name: 'Any feedback?' } )
126+
).resolves.toBeInTheDocument();
127+
expect(
128+
screen.queryByRole( 'menuitem', { name: 'Switch off the preview' } )
129+
).not.toBeInTheDocument();
130+
} );
131+
115132
it( 'puts Customize first where the page has a layout to arrange', async () => {
116133
const user = setupUser();
117134
const onCustomize = jest.fn();

‎projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-options-menu/__tests__/return-to-classic-stats.test.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { getScriptData } from '@automattic/jetpack-script-data';
15
/**
26
* Internal dependencies
37
*/
@@ -26,4 +30,19 @@ describe( 'returnToClassicStats', () => {
2630

2731
expect( assign ).toHaveBeenCalledWith( 'https://example.com/wp-admin/admin.php?page=stats' );
2832
} );
33+
34+
it( 'lands wherever the server says the classic Stats page is', () => {
35+
jest.mocked( getScriptData ).mockReturnValue( {
36+
premium_analytics: {
37+
classic_stats_url: 'https://example.com/wp-admin/admin.php?page=jetpack-stats',
38+
},
39+
} as ReturnType< typeof getScriptData > );
40+
const assign = jest.fn();
41+
42+
returnToClassicStats( { assign } );
43+
44+
expect( assign ).toHaveBeenCalledWith(
45+
'https://example.com/wp-admin/admin.php?page=jetpack-stats'
46+
);
47+
} );
2948
} );
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { getAdminUrl, getScriptData } from '@automattic/jetpack-script-data';
5+
6+
/**
7+
* Where switching the dashboard off sends the reader: the classic Stats page, as the server
8+
* publishes it, or null while no plugin registers one.
9+
*
10+
* A server that predates the key gets the page at its historic address, so the shipped UI keeps working.
11+
*
12+
* @return The classic Stats page URL, or null.
13+
*/
14+
export function getClassicStatsUrl(): string | null {
15+
const url = getScriptData()?.premium_analytics?.classic_stats_url;
16+
17+
return url === undefined ? getAdminUrl( 'admin.php?page=stats' ) : url;
18+
}

‎projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-options-menu/page-options-menu.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useCallback, useState } from 'react';
1111
*/
1212
import { useTrackEvent } from '../../hooks/use-track-event';
1313
import { canSendFeedback } from './can-send-feedback';
14+
import { getClassicStatsUrl } from './classic-stats-url';
1415
import { FeedbackModal } from './feedback-modal';
1516
import { SwitchOffDialog } from './switch-off-dialog';
1617

@@ -36,8 +37,9 @@ export function PageOptionsMenu( { onCustomize }: PageOptionsMenuProps ) {
3637
const [ isFeedbackOpen, setIsFeedbackOpen ] = useState( false );
3738
const [ isSwitchOffOpen, setIsSwitchOffOpen ] = useState( false );
3839

39-
// The opt-in is a site setting, so switching it off takes the same capability.
40-
const canSwitchOff = currentUserCan( 'manage_options' );
40+
// The opt-in is a site setting, so switching it off takes the same capability; and it
41+
// sends the reader to classic Stats, so there has to be one.
42+
const canSwitchOff = currentUserCan( 'manage_options' ) && null !== getClassicStatsUrl();
4143
const canGiveFeedback = canSendFeedback();
4244

4345
const openFeedback = useCallback( () => {

‎projects/packages/premium-analytics/packages/widgets-toolkit/src/components/page-options-menu/return-to-classic-stats.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
2-
* WordPress dependencies
2+
* Internal dependencies
33
*/
4-
import { getAdminUrl } from '@automattic/jetpack-script-data';
4+
import { getClassicStatsUrl } from './classic-stats-url';
55

66
/**
77
* Leaves the dashboard for classic Stats, where the invitation back lives.
@@ -14,5 +14,9 @@ import { getAdminUrl } from '@automattic/jetpack-script-data';
1414
export function returnToClassicStats(
1515
location: Pick< Location, 'assign' > = window.location
1616
): void {
17-
location.assign( getAdminUrl( 'admin.php?page=stats' ) );
17+
const url = getClassicStatsUrl();
18+
19+
if ( url ) {
20+
location.assign( url );
21+
}
1822
}

‎projects/packages/premium-analytics/src/dashboard-sections.php‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ function inject_dashboard_sections_script_data( array $data ): array {
158158
// Surfaces outside any section, such as feedback, post to Stats endpoints.
159159
$data['premium_analytics']['can_view_stats'] = Capabilities::current_user_can_view_stats();
160160

161+
// Switching the dashboard off sends the reader to classic Stats, so the client is told where that is.
162+
$data['premium_analytics']['classic_stats_url'] = get_classic_stats_url();
163+
161164
$sections = get_available_dashboard_section_slugs();
162165

163166
if ( null !== $sections ) {
@@ -168,6 +171,22 @@ function inject_dashboard_sections_script_data( array $data ): array {
168171
return $data;
169172
}
170173

174+
/**
175+
* The URL of the classic Stats page, or null while no plugin registers a `stats` menu page.
176+
*
177+
* Read from the menu rather than `$admin_page_hooks`, which keys top-level pages only: with
178+
* Odyssey Stats off, the Jetpack plugin registers the page under its own menu.
179+
*
180+
* @since $$next-version$$
181+
*
182+
* @return string|null
183+
*/
184+
function get_classic_stats_url(): ?string {
185+
$url = function_exists( 'menu_page_url' ) ? menu_page_url( 'stats', false ) : '';
186+
187+
return '' === $url ? null : $url;
188+
}
189+
171190
/**
172191
* Whether the current user can access dashboard section routes.
173192
*

‎projects/packages/premium-analytics/tests/php/Dashboard_Section_Test.php‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use WP_REST_Request;
1818
use WP_REST_Server;
1919

20+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
2021
require_once __DIR__ . '/../../src/dashboard-sections.php';
2122
require_once __DIR__ . '/../../src/default-dashboard-sections.php';
2223
require_once __DIR__ . '/traits/trait-analytics-capabilities.php';
@@ -1440,13 +1441,33 @@ public function test_section_script_data_keeps_the_other_premium_analytics_keys(
14401441
array(
14411442
'has_videopress' => true,
14421443
'can_view_stats' => true,
1444+
'classic_stats_url' => null,
14431445
'sections' => array( 'traffic', 'insights', 'subscribers' ),
14441446
'plan_date_limited_sections' => array( 'traffic' ),
14451447
),
14461448
$data['premium_analytics']
14471449
);
14481450
}
14491451

1452+
/**
1453+
* Switching the dashboard off lands on classic Stats, so the client is told where that page
1454+
* is, also when the Jetpack plugin registers it under its own menu rather than top-level.
1455+
*/
1456+
public function test_section_script_data_carries_the_classic_stats_page_url() {
1457+
$this->assertNull( inject_dashboard_sections_script_data( array() )['premium_analytics']['classic_stats_url'] );
1458+
1459+
$GLOBALS['_parent_pages']['jetpack'] = false;
1460+
$GLOBALS['_parent_pages']['stats'] = 'jetpack';
1461+
try {
1462+
$this->assertSame(
1463+
admin_url( 'admin.php?page=stats' ),
1464+
inject_dashboard_sections_script_data( array() )['premium_analytics']['classic_stats_url']
1465+
);
1466+
} finally {
1467+
unset( $GLOBALS['_parent_pages']['jetpack'], $GLOBALS['_parent_pages']['stats'] );
1468+
}
1469+
}
1470+
14501471
/**
14511472
* The sections route drops the Subscribers tab once it is unavailable.
14521473
*/
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: enhancement
3+
4+
Premium Analytics: Offer switching the dashboard off only while the site still has a classic Stats page to return to.

0 commit comments

Comments
 (0)