PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / _inc / lib / admin-pages / class-jetpack-ai-page.php
jetpack / _inc / lib / admin-pages Last commit date
class-akismet-admin-chrome.php 1 month ago class-jetpack-about-page.php 2 months ago class-jetpack-ai-page.php 6 days ago class-jetpack-redux-state-helper.php 6 days ago class.jetpack-admin-page.php 3 weeks ago class.jetpack-landing-page.php 2 years ago class.jetpack-react-page.php 7 months ago class.jetpack-settings-page.php 3 weeks ago
class-jetpack-ai-page.php
228 lines
1 <?php
2 /**
3 * Jetpack AI admin page.
4 *
5 * Registers the "AI" submenu item under Jetpack and mounts the React-based
6 * MCP settings interface.
7 *
8 * @package automattic/jetpack
9 */
10
11 use Automattic\Jetpack\Admin_UI\Admin_Menu;
12 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
13 use Automattic\Jetpack\Redirect;
14 use Automattic\Jetpack\Status;
15 use Automattic\Jetpack\Status\Host;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit( 0 );
19 }
20
21 require_once __DIR__ . '/class.jetpack-admin-page.php';
22
23 /**
24 * Builds the Jetpack AI admin page and its sidebar menu entry.
25 */
26 class Jetpack_AI_Page extends Jetpack_Admin_Page {
27
28 /**
29 * Hide the "AI" sidebar entry when Jetpack is not yet connected.
30 * Other Jetpack products follow the same convention.
31 *
32 * @var bool
33 */
34 protected $dont_show_if_not_active = true;
35
36 /**
37 * Register the "AI" submenu under the Jetpack top-level menu.
38 *
39 * @return string|false Hook returned by Admin_Menu::add_menu().
40 */
41 public function get_page_hook() {
42 return Admin_Menu::add_menu(
43 // "Jetpack AI" is a product name and should not be translated.
44 'Jetpack AI',
45 'AI',
46 'manage_options',
47 'jetpack-ai',
48 array( $this, 'render' ),
49 4
50 );
51 }
52
53 /**
54 * Attach page-specific actions.
55 *
56 * @param string $hook The page hook returned by get_page_hook().
57 */
58 public function add_page_actions( $hook ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
59 // Nothing extra needed beyond the common hooks in Jetpack_Admin_Page::add_actions().
60 }
61
62 /**
63 * No additional styles needed: AdminPage from @automattic/jetpack-components
64 * owns the full layout and does not need the wrap_ui admin.css / style.min.css
65 * bundle (which zeroes out #wpcontent padding and conflicts with AdminPage's
66 * margin-left compensation).
67 */
68 public function additional_styles() {}
69
70 /**
71 * Enqueue scripts and styles for the AI admin page.
72 */
73 public function page_admin_scripts() {
74 $script_path = JETPACK__PLUGIN_DIR . '_inc/build/jetpack-ai-admin.asset.php';
75 $script_deps = array( 'wp-element', 'wp-components', 'wp-i18n', 'wp-polyfill' );
76 $script_version = JETPACK__VERSION;
77
78 if ( file_exists( $script_path ) ) {
79 $asset_manifest = include $script_path;
80 $script_deps = $asset_manifest['dependencies'];
81 $script_version = $asset_manifest['version'];
82 }
83
84 $blog_id = Connection_Manager::get_site_id( true );
85 $site_suffix = ( new Status() )->get_site_suffix();
86 // Use the plain hostname for the Atomic activity log URL — get_site_suffix() can
87 // include '::' for subdirectory installs, which would break the URL. This matches
88 // the approach used by jetpack-mu-wpcom for the sidebar Activity Log link.
89 $site_host = wp_parse_url( home_url(), PHP_URL_HOST );
90 $activity_log_site = ( is_string( $site_host ) && '' !== $site_host ) ? $site_host : $site_suffix;
91 // On Atomic link to WPCOM activity log; on self-hosted link to the local wp-admin page.
92 $activity_log_url = ( new Host() )->is_woa_site()
93 ? 'https://wordpress.com/activity-log/' . $activity_log_site
94 : admin_url( 'admin.php?page=jetpack-activity-log' );
95
96 /*
97 * Link SEO settings to the dedicated Jetpack SEO page where it exists,
98 * falling back to the Traffic settings card. Checking the `rsm_jetpack_seo`
99 * filter is required in addition to the cohort check: is_seo_surface_visible()
100 * alone returns true on all of wpcom-platform even while the flag is off —
101 * it answers only the cohort half, and page registration requires both
102 * (see packages/seo Initializer::init()).
103 */
104 $seo_settings_url = admin_url( 'admin.php?page=jetpack#/traffic' );
105 if (
106 // The exact-symbol guard matters: the autoloader can select an older
107 // jetpack-seo copy from another plugin that has the class but not
108 // this method, and class_exists alone would then fatal here.
109 method_exists( '\Automattic\Jetpack\SEO\Initializer', 'is_seo_surface_visible' )
110 && (bool) apply_filters( 'rsm_jetpack_seo', false )
111 && \Automattic\Jetpack\SEO\Initializer::is_seo_surface_visible()
112 ) {
113 $seo_settings_url = admin_url( 'admin.php?page=jetpack-seo' );
114 }
115
116 wp_enqueue_script(
117 'jetpack-ai-admin',
118 plugins_url( '_inc/build/jetpack-ai-admin.js', JETPACK__PLUGIN_FILE ),
119 $script_deps,
120 $script_version,
121 true
122 );
123
124 wp_set_script_translations( 'jetpack-ai-admin', 'jetpack' );
125
126 wp_add_inline_script(
127 'jetpack-ai-admin',
128 'var jetpackAiSettings = ' . wp_json_encode(
129 array(
130 'blogId' => $blog_id ? (int) $blog_id : 0,
131 'activityLogUrl' => $activity_log_url,
132 'seoSettingsUrl' => $seo_settings_url,
133 'siteAdminUrl' => admin_url(),
134 'apiRoot' => esc_url_raw( rest_url() ),
135 'apiNonce' => wp_create_nonce( 'wp_rest' ),
136 'pluginUrl' => plugins_url( '', JETPACK__PLUGIN_FILE ),
137 // Route through the Jetpack redirect service so the upgrade
138 // destination for the MCP upsell can be retargeted without
139 // shipping a code change.
140 'upgradeUrl' => Redirect::get_url( 'jetpack-ai-upgrade-url-for-jetpack-sites' ),
141 // Pre-release gate: only internal testing environments see
142 // the Features view. Remove when the view goes public.
143 'showFeaturesView' => jetpack_is_internal_testing_environment(),
144 // Tracks audience properties for the jetpack_mcp_* events, per the
145 // Tracks standards for AI product events (AIINT-586). The client
146 // sends them as the strings 'true'/'false' (AIINT-576).
147 'isA11n' => self::is_current_user_automattician(),
148 'isTest' => jetpack_is_internal_testing_environment(),
149 ),
150 JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
151 ) . ';',
152 'before'
153 );
154
155 /*
156 * `@automattic/jetpack-analytics` reads `window.jpTracksContext.blog_id` at
157 * event-fire time and attaches it to every Tracks event fired from this page.
158 * Without it, JS-fired events from self-hosted sites carry no blog_id — the
159 * Tracks pixel cannot resolve the site — so the events cannot be joined to
160 * plan or site data. Mirrors Connection\Initial_State::render().
161 */
162 wp_add_inline_script(
163 'jetpack-ai-admin',
164 sprintf(
165 'window.jpTracksContext = window.jpTracksContext || {}; window.jpTracksContext.blog_id = %s;',
166 absint( $blog_id )
167 ),
168 'before'
169 );
170
171 wp_enqueue_style(
172 'jetpack-ai-admin',
173 plugins_url( '_inc/build/jetpack-ai-admin.css', JETPACK__PLUGIN_FILE ),
174 array( 'wp-components' ),
175 $script_version
176 );
177 }
178
179 /**
180 * Whether the current user is an Automattician.
181 *
182 * Identity check for the Tracks `is_a11n` audience property — it answers
183 * "who is this", not "may they use the tool", so it deliberately does not
184 * consult the MCP allowlist: allowlisted external testers are not a11ns.
185 *
186 * On wpcom Simple/Atomic the platform's is_automattician() is authoritative.
187 * Self-hosted Jetpack has no platform check; there the Tracks identity of a
188 * connected user is their WordPress.com account, so the connected account's
189 * email domain is the identity signal.
190 *
191 * @return bool
192 */
193 private static function is_current_user_automattician() {
194 if ( function_exists( 'is_automattician' ) ) {
195 return (bool) is_automattician( get_current_user_id() );
196 }
197
198 $user_data = ( new Connection_Manager() )->get_connected_user_data();
199 $email = is_array( $user_data ) && ! empty( $user_data['email'] )
200 ? strtolower( (string) $user_data['email'] )
201 : '';
202
203 return '' !== $email && '@automattic.com' === substr( $email, -15 );
204 }
205
206 /**
207 * Override the base render() to skip wrap_ui entirely.
208 *
209 * Wrap_ui renders the Jetpack masthead header and static footer, which
210 * duplicate the header/footer that AdminPage (React) already provides.
211 * Calling page_render() directly lets AdminPage own the full layout.
212 */
213 public function render() {
214 $this->page_render();
215 }
216
217 /**
218 * Render the page container. The React app mounts into this div.
219 *
220 * AdminPage from @automattic/jetpack-components handles the full-page layout.
221 */
222 public function page_render() {
223 ?>
224 <div id="jetpack-ai-root"></div>
225 <?php
226 }
227 }
228