Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Add `aiManageUrl` to the My Jetpack script data type.
2 changes: 2 additions & 0 deletions projects/js-packages/script-data/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export interface MyJetpackScriptData {
assetsUrl?: string;
/** Navigation data for released footers; current My Jetpack always points to Features. */
productsSection?: { slug: 'features'; label: string } | null;
/** Where Jetpack AI is managed: the Jetpack AI Hub, or My Jetpack's AI page while the Hub is not open to the site. */
aiManageUrl?: string;
}

export interface JetpackScriptData {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add getJetpackAiManageUrl(), the URL where Jetpack AI is managed on the site.
1 change: 1 addition & 0 deletions projects/js-packages/shared-extension-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export { getBlockIconComponent, getBlockIconProp } from './src/get-block-icon-fr
export { default as getJetpackBlocksVariation } from './src/get-jetpack-blocks-variation';
export { store, JETPACK_MODULES_STORE_ID } from '@automattic/jetpack-shared-stores';
export { default as isMyJetpackAvailable } from './src/is-my-jetpack-available';
export { default as getJetpackAiManageUrl } from './src/get-jetpack-ai-manage-url';
export { default as hasFeatureFlag } from './src/has-feature-flag';
export * from './src/libs';
export { default as useUpgradeFlow } from './src/hooks/use-upgrade-flow';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getMyJetpackUrl, getScriptData } from '@automattic/jetpack-script-data';

/**
* Return the URL where Jetpack AI is managed on this site, as printed by My Jetpack.
*
* Falls back to My Jetpack's AI page when the site did not print one.
*
* @return {string} The Jetpack AI manage URL.
*/
export default function getJetpackAiManageUrl() {
return getScriptData()?.myJetpack?.aiManageUrl || getMyJetpackUrl( '#/jetpack-ai' );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import getJetpackAiManageUrl from '../get-jetpack-ai-manage-url';

const adminUrl = 'https://example.com/wp-admin/';
const hubUrl = `${ adminUrl }admin.php?page=jetpack-ai`;

describe( 'getJetpackAiManageUrl', () => {
afterEach( () => {
delete window.JetpackScriptData;
} );

it.each( [
[ 'the printed URL', hubUrl, hubUrl ],
[
"My Jetpack's AI page when none is printed",
undefined,
`${ adminUrl }admin.php?page=my-jetpack#/jetpack-ai`,
],
] )( 'returns %s', ( _, aiManageUrl, expected ) => {
window.JetpackScriptData = { site: { admin_url: adminUrl }, myJetpack: { aiManageUrl } };

expect( getJetpackAiManageUrl() ).toBe( expected );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Print where Jetpack AI is managed in the admin script data, so other screens can link to it.
7 changes: 6 additions & 1 deletion projects/packages/my-jetpack/src/class-initializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ public static function get_products_section() {
}

/**
* Add My Jetpack availability and image base URL to admin script data.
* Add My Jetpack's availability, image base URL and link targets to admin script data.
*
* Printed on every admin page by Script_Data, so the connection screen can resolve its
* illustrations and Jetpack footers know whether to link to My Jetpack.
Expand All @@ -710,6 +710,11 @@ public static function add_admin_script_data( $data ) {
$data['myJetpack']['assetsUrl'] = self::get_assets_url();
$data['myJetpack']['productsSection'] = self::get_products_section();

// Only administrators can open the pages this points at, so spare everyone else the lookup.
if ( current_user_can( 'manage_options' ) ) {
$data['myJetpack']['aiManageUrl'] = esc_url_raw( Products\Jetpack_Ai::get_manage_url() );
}

if (
( new Status() )->is_offline_mode()
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ public static function is_feature_ui_enabled() {
/**
* Get the URL where the user manages the product.
*
* @return ?string
* @return string
*/
public static function get_manage_url() {
if ( self::is_feature_ui_enabled() ) {
Expand Down
21 changes: 21 additions & 0 deletions projects/packages/my-jetpack/tests/php/Script_Data_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,25 @@ public function test_the_image_base_url_is_registered_where_my_jetpack_is_off()

remove_filter( 'jetpack_my_jetpack_should_initialize', '__return_false' );
}

public function test_prints_where_jetpack_ai_is_managed_to_administrators_only() {
wp_set_current_user(
wp_insert_user(
array(
'user_login' => 'ai-admin',
'user_pass' => 'password',
'role' => 'administrator',
)
)
);

$this->assertSame(
Products\Jetpack_Ai::get_manage_url(),
Initializer::add_admin_script_data( array() )['myJetpack']['aiManageUrl']
);

wp_set_current_user( 0 );

$this->assertArrayNotHasKey( 'aiManageUrl', Initializer::add_admin_script_data( array() )['myJetpack'] );
}
}
10 changes: 9 additions & 1 deletion projects/plugins/jetpack/_inc/client/writing/composing.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { getRedirectUrl } from '@automattic/jetpack-components';
import { currentUserCan } from '@automattic/jetpack-script-data';
import { getJetpackAiManageUrl } from '@automattic/jetpack-shared-extension-utils';
import { __, _x } from '@wordpress/i18n';
import { Component } from 'react';
import { connect } from 'react-redux';
Expand Down Expand Up @@ -210,7 +212,13 @@ export class Composing extends Component {
),
aiAssistantLink = (
<CompactCard className="jp-settings-card__configure-link">
<a href={ `${ this.props.siteAdminUrl }admin.php?page=my-jetpack#/jetpack-ai` }>
<a
href={
currentUserCan( 'manage_options' )
? getJetpackAiManageUrl()
: getRedirectUrl( 'org-ai' )
}
>
{ __( 'Learn more about all Jetpack AI features', 'jetpack' ) }
</a>
</CompactCard>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: enhancement

AI: Send the editor's and Jetpack Settings' AI links to the Jetpack AI Hub where it is available, and to jetpack.com for users who cannot manage the site.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* External dependencies
*/
import { getRedirectUrl } from '@automattic/jetpack-components';
import { isSimpleSite, getMyJetpackUrl } from '@automattic/jetpack-script-data';
import { isSimpleSite, currentUserCan } from '@automattic/jetpack-script-data';
import {
getJetpackAiManageUrl,
isMyJetpackAvailable,
useAutosaveAndRedirect,
} from '@automattic/jetpack-shared-extension-utils';
Expand All @@ -19,9 +20,11 @@ export default function useAiProductPage(): {
isMyJetpackAvailable: boolean;
} {
const isMyJetpackReallyAvailable = isMyJetpackAvailable() && ! isSimpleSite();
const productPageUrl = isMyJetpackReallyAvailable
? getMyJetpackUrl( '#/jetpack-ai' )
: getRedirectUrl( 'org-ai' );
// The Jetpack AI Hub needs manage_options; everyone else gets the public page.
const productPageUrl =
isMyJetpackReallyAvailable && currentUserCan( 'manage_options' )
? getJetpackAiManageUrl()
: getRedirectUrl( 'org-ai' );

const { autosaveAndRedirect, isRedirecting } = useAutosaveAndRedirect( productPageUrl );

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { renderHook } from '@testing-library/react';
import useAiProductPage from '..';

jest.mock( '@automattic/jetpack-components', () => ( {
getRedirectUrl: ( source: string ) => `https://jetpack.com/redirect/?source=${ source }`,
} ) );

jest.mock( '@automattic/jetpack-shared-extension-utils', () => ( {
...jest.requireActual( '@automattic/jetpack-shared-extension-utils' ),
isMyJetpackAvailable: () => true,
useAutosaveAndRedirect: () => ( { autosaveAndRedirect: jest.fn(), isRedirecting: false } ),
} ) );

const adminUrl = 'https://example.com/wp-admin/';
const hubUrl = `${ adminUrl }admin.php?page=jetpack-ai`;

describe( 'useAiProductPage', () => {
afterEach( () => {
delete window.JetpackScriptData;
} );

it.each( [
[ 'an administrator to the Jetpack AI Hub when the site prints it', true, hubUrl, hubUrl ],
[
'everyone else to the public Jetpack AI page',
false,
hubUrl,
'https://jetpack.com/redirect/?source=org-ai',
],
] )( 'sends %s', ( _, manageOptions, aiManageUrl, expected ) => {
window.JetpackScriptData = {
site: { admin_url: adminUrl },
user: { current_user: { capabilities: { manage_options: manageOptions } } },
myJetpack: { aiManageUrl },
} as unknown as Window[ 'JetpackScriptData' ];

const { result } = renderHook( () => useAiProductPage() );

expect( result.current.productPageUrl ).toBe( expected );
} );
} );
Loading