PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.3
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.3
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 / jetpack_vendor / automattic / jetpack-agents-manager / src / class-agents-manager.php
jetpack / jetpack_vendor / automattic / jetpack-agents-manager / src Last commit date
js 3 weeks ago class-agents-manager.php 3 weeks ago class-open-state-store.php 2 months ago class-sidebar-open-preservation.php 3 weeks ago class-wp-rest-agents-manager-persisted-open-state.php 2 months ago class-wp-rest-jetpack-ai-jwt.php 2 months ago
class-agents-manager.php
1076 lines
1 <?php
2 /**
3 * Agents manager
4 *
5 * @package automattic/jetpack-agents-manager
6 */
7
8 namespace Automattic\Jetpack\Agents_Manager;
9
10 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
11 use Automattic\Jetpack\Constants;
12
13 /**
14 * Class Agents_Manager
15 */
16 class Agents_Manager {
17 /**
18 * The package version of the Agents Manager package.
19 *
20 * @var string
21 */
22 const PACKAGE_VERSION = '0.9.0';
23
24 /**
25 * Help Center URL for disconnected variants.
26 *
27 * @var string
28 */
29 private const HELP_CENTER_URL = 'https://wordpress.com/help?help-center=home';
30
31 /**
32 * Class instance.
33 *
34 * @var Agents_Manager
35 */
36 private static $instance = null;
37
38 /**
39 * Agents_Manager constructor.
40 */
41 private function __construct() {
42 add_action( 'rest_api_init', array( $this, 'register_rest_api' ) );
43 add_filter( 'calypso_preferences_update', array( $this, 'calypso_preferences_update' ) );
44
45 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ), 101 );
46 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 101 );
47 add_action( 'next_admin_init', array( $this, 'enqueue_scripts' ), 1001 );
48 add_filter( 'agents_manager_use_unified_experience', array( $this, 'should_use_unified_experience' ) );
49
50 Sidebar_Open_Preservation::init();
51 }
52
53 /**
54 * Check if the agents manager menu panel should be displayed.
55 *
56 * @return bool True if the menu panel should be displayed.
57 */
58 public function should_display_menu_panel() {
59 return self::is_unified_experience();
60 }
61
62 /**
63 * Get the SVG icon markup for a given icon name.
64 *
65 * @param string $icon_name The name of the icon to retrieve.
66 * @return string The SVG markup.
67 */
68 private function get_icon( $icon_name ) {
69 $icons = array(
70 'comment' => '<svg class="help-center-menu-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18 4H6c-1.1 0-2 .9-2 2v12.9c0 .6.5 1.1 1.1 1.1.3 0 .5-.1.8-.3L8.5 17H18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm.5 11c0 .3-.2.5-.5.5H7.9l-2.4 2.4V6c0-.3.2-.5.5-.5h12c.3 0 .5.2.5.5v9z" /></svg>',
71 'backup' => '<svg class="help-center-menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5.5 12h1.75l-2.5 3-2.5-3H4a8 8 0 113.134 6.35l.907-1.194A6.5 6.5 0 105.5 12zm9.53 1.97l-2.28-2.28V8.5a.75.75 0 00-1.5 0V12a.747.747 0 00.218.529l1.282-.84-1.28.842 2.5 2.5a.75.75 0 101.06-1.061z" /></svg>',
72 'page' => '<svg class="help-center-menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.5 7.5h-7V9h7V7.5Zm-7 3.5h7v1.5h-7V11Zm7 3.5h-7V16h7v-1.5Z" /><path d="M17 4H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2ZM7 5.5h10a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5H7a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 .5-.5Z" /></svg>',
73 'video' => '<svg class="help-center-menu-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.7 3H5.3C4 3 3 4 3 5.3v13.4C3 20 4 21 5.3 21h13.4c1.3 0 2.3-1 2.3-2.3V5.3C21 4 20 3 18.7 3zm.8 15.7c0 .4-.4.8-.8.8H5.3c-.4 0-.8-.4-.8-.8V5.3c0-.4.4-.8.8-.8h13.4c.4 0 .8.4.8.8v13.4zM10 15l5-3-5-3v6z" /></svg>',
74 'rss' => '<svg class="help-center-menu-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 10.2h-.8v1.5H5c1.9 0 3.8.8 5.1 2.1 1.4 1.4 2.1 3.2 2.1 5.1v.8h1.5V19c0-2.3-.9-4.5-2.6-6.2-1.6-1.6-3.8-2.6-6.1-2.6zm10.4-1.6C12.6 5.8 8.9 4.2 5 4.2h-.8v1.5H5c3.5 0 6.9 1.4 9.4 3.9s3.9 5.8 3.9 9.4v.8h1.5V19c0-3.9-1.6-7.6-4.4-10.4zM4 20h3v-3H4v3z" /></svg>',
75 );
76
77 return $icons[ $icon_name ] ?? '';
78 }
79
80 /**
81 * Add the Agents Manager Help "?" node (`agents-manager`) to the admin bar, replacing the
82 * legacy Help Center node (`help-center`).
83 *
84 * @param \WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
85 * @param bool $use_disconnected Disconnected variants link straight to the Help Center instead of opening the dropdown.
86 */
87 public function add_help_menu( $wp_admin_bar, $use_disconnected ) {
88 $wp_admin_bar->remove_node( 'help-center' );
89
90 $menu_args = array(
91 'id' => 'agents-manager',
92 'title' => '<span title="' . esc_attr__( 'Help Center', 'jetpack-agents-manager' ) . '"><svg id="agents-manager-icon" class="ab-icon" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
93 <path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm-1 16v-2h2v2h-2zm2-3v-1.141A3.991 3.991 0 0016 10a4 4 0 00-8 0h2c0-1.103.897-2 2-2s2 .897 2 2-.897 2-2 2a1 1 0 00-1 1v2h2z" />
94 </svg></span>',
95 'parent' => 'top-secondary',
96 );
97
98 if ( $use_disconnected ) {
99 $menu_args['href'] = self::HELP_CENTER_URL;
100 $menu_args['meta'] = array(
101 'target' => '_blank',
102 'rel' => 'noopener noreferrer',
103 );
104 } else {
105 $menu_args['meta'] = array(
106 'html' => '<div id="agents-manager-masterbar"></div>',
107 'class' => 'menupop',
108 'target' => '_blank',
109 'rel' => 'noopener noreferrer',
110 );
111 }
112
113 $wp_admin_bar->add_menu( $menu_args );
114 }
115
116 /**
117 * Add the agents manager menu panel to the admin bar.
118 *
119 * @param \WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
120 */
121 public function add_menu_panel( $wp_admin_bar ) {
122 // Add chat support group
123 $wp_admin_bar->add_group(
124 array(
125 'parent' => 'agents-manager',
126 'id' => 'agents-manager-menu-panel-chat',
127 'meta' => array(
128 'class' => 'ab-sub-secondary',
129 ),
130 )
131 );
132
133 // Add chat support menu item
134 $wp_admin_bar->add_node(
135 array(
136 'parent' => 'agents-manager-menu-panel-chat',
137 'id' => 'agents-manager-chat-support',
138 'title' => $this->get_icon( 'comment' ) . '<span>' . __( 'Chat support', 'jetpack-agents-manager' ) . '</span>',
139 )
140 );
141
142 // Add chat history menu item
143 $wp_admin_bar->add_node(
144 array(
145 'parent' => 'agents-manager-menu-panel-chat',
146 'id' => 'agents-manager-chat-history',
147 'title' => $this->get_icon( 'backup' ) . '<span>' . __( 'Chat history', 'jetpack-agents-manager' ) . '</span>',
148 )
149 );
150
151 // Add links group
152 $wp_admin_bar->add_group(
153 array(
154 'parent' => 'agents-manager',
155 'id' => 'agents-manager-menu-panel-links',
156 'meta' => array(
157 'class' => 'ab-sub-secondary',
158 ),
159 )
160 );
161
162 // Add support guides menu item
163 $wp_admin_bar->add_node(
164 array(
165 'parent' => 'agents-manager-menu-panel-links',
166 'id' => 'agents-manager-support-guides',
167 'title' => $this->get_icon( 'page' ) . '<span>' . __( 'Support guides', 'jetpack-agents-manager' ) . '</span>',
168 )
169 );
170
171 // Add courses menu item
172 $wp_admin_bar->add_node(
173 array(
174 'parent' => 'agents-manager-menu-panel-links',
175 'id' => 'agents-manager-courses',
176 'title' => $this->get_icon( 'video' ) . '<span>' . __( 'Courses', 'jetpack-agents-manager' ) . '</span>',
177 'href' => 'https://wordpress.com/support/courses/',
178 'meta' => array(
179 'target' => '_blank',
180 'rel' => 'noopener noreferrer',
181 ),
182 )
183 );
184
185 // Add product updates menu item
186 $wp_admin_bar->add_node(
187 array(
188 'parent' => 'agents-manager-menu-panel-links',
189 'id' => 'agents-manager-product-updates',
190 'title' => $this->get_icon( 'rss' ) . '<span>' . __( 'Product updates', 'jetpack-agents-manager' ) . '</span>',
191 'href' => 'https://wordpress.com/blog/category/product-features/',
192 'meta' => array(
193 'target' => '_blank',
194 'rel' => 'noopener noreferrer',
195 ),
196 )
197 );
198 }
199
200 /**
201 * Add the standalone AI chat button to the admin bar.
202 *
203 * @param \WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
204 */
205 public function add_ai_chat_button( $wp_admin_bar ) {
206 $wp_admin_bar->add_menu(
207 array(
208 'id' => 'agents-manager-ai-chat',
209 'parent' => 'top-secondary',
210 'title' => '<span title="' . esc_attr__( 'Ask AI', 'jetpack-agents-manager' ) . '"><svg class="ab-icon" role="img" aria-label="' . esc_attr__( 'Ask AI', 'jetpack-agents-manager' ) . '" width="24" height="24" viewBox="-45 -45 490 490" xmlns="http://www.w3.org/2000/svg">
211 <path fill="currentColor" d="M391.528 188.061L309.455 159.75C276.997 148.597 251.403 123.003 240.25 90.5451L211.939 8.47185C208.079 -2.82395 191.921 -2.82395 188.061 8.47185L159.75 90.5451C148.597 123.003 123.003 148.597 90.5451 159.75L8.47185 188.061C-2.82395 191.921 -2.82395 208.079 8.47185 211.939L90.5451 240.25C123.003 251.403 148.597 276.997 159.75 309.455L188.061 391.528C191.921 402.824 208.079 402.824 211.939 391.528L240.25 309.455C251.403 276.997 276.997 251.403 309.455 240.25L391.528 211.939C402.824 208.079 402.824 191.921 391.528 188.061ZM295.728 206.077L254.692 220.232C238.391 225.809 225.666 238.677 220.089 254.835L205.934 295.871C203.932 301.591 195.925 301.591 193.923 295.871L179.768 254.835C174.191 238.534 161.323 225.809 145.165 220.232L104.129 206.077C98.4093 204.075 98.4093 196.068 104.129 194.066L145.165 179.911C161.466 174.334 174.191 161.466 179.768 145.308L193.923 104.272C195.925 98.5523 203.932 98.5523 205.934 104.272L220.089 145.308C225.666 161.609 238.534 174.334 254.692 179.911L295.728 194.066C301.448 196.068 301.448 204.075 295.728 206.077Z" />
212 </svg></span>',
213 )
214 );
215 }
216
217 /**
218 * Enqueue Agents Manager scripts and add inline script data.
219 */
220 public function enqueue_scripts() {
221 // Early return for P2 frontend - don't add admin bar or enqueue scripts.
222 $stylesheet = get_stylesheet();
223 $is_p2 = str_contains( $stylesheet, 'pub/p2' ) || function_exists( '\WPForTeams\is_wpforteams_site' ) && \WPForTeams\is_wpforteams_site( get_current_blog_id() );
224
225 if ( ! is_admin() && $is_p2 ) {
226 return;
227 }
228
229 // Determine which variant to load (null = don't load).
230 $variant = self::get_active_variant();
231 if ( null === $variant ) {
232 return;
233 }
234 $use_disconnected = str_contains( $variant, 'disconnected' );
235 $is_gutenberg = $this->is_block_editor();
236
237 // In Gutenberg, dequeue Help Center so we don't end up with two buttons — but only
238 // in the full unified experience, where Agents Manager takes over the Help Center.
239 // In block-editor-only mode (e.g. ?flags=unified-big-sky) Agents Manager replaces
240 // Big Sky's native UI and Help Center should remain available.
241 // Agents Manager fires at priority 101, after Help Center at 100, so HC is already enqueued.
242 if ( $is_gutenberg && self::is_unified_experience() ) {
243 wp_dequeue_script( 'help-center' );
244 wp_dequeue_style( 'help-center-style' );
245 }
246
247 // For non-Gutenberg, non-CIAB environments, add to the admin bar. The fullscreen Gutenberg
248 // editor has no admin bar, so JS handles UI insertion — except under the omnibar, which is
249 // handled below. CIAB hides the admin bar and uses its own Site Hub.
250 $is_ciab = $this->is_ciab_environment();
251 if ( ! $is_gutenberg && ! $is_ciab ) {
252 add_action(
253 'admin_bar_menu',
254 function ( $wp_admin_bar ) use ( $use_disconnected ) {
255 $this->add_help_menu( $wp_admin_bar, $use_disconnected );
256 },
257 // Add the agents manager icon to the admin bar after the help center is added, so we can remove it.
258 100
259 );
260
261 // Initialize the agents manager menu panel (only for full variants, not disconnected)
262 if ( ! $use_disconnected ) {
263 add_action( 'admin_bar_menu', array( $this, 'add_menu_panel' ), 100 );
264 }
265
266 // Standalone AI chat button, shown only in the unified experience.
267 if ( ! $use_disconnected && self::is_unified_experience() ) {
268 add_action( 'admin_bar_menu', array( $this, 'add_ai_chat_button' ), 100 );
269 }
270 }
271
272 // When Gutenberg's "admin bar in editor" (omnibar) experiment is active, add the entry
273 // points to that editor admin bar. CIAB is excluded — it has its own Site Hub UI.
274 // Mirroring wp-admin: the Help "?" dropdown shows only in the full unified experience;
275 // the Ask AI button shows whenever Agents Manager is enabled in this editor.
276 if ( ! $is_ciab && ! $use_disconnected && self::is_admin_bar_in_editor() ) {
277 // Help "?" node + dropdown panel first, matching the wp-admin admin bar order.
278 if ( self::is_unified_experience() ) {
279 add_action(
280 'admin_bar_menu',
281 function ( $wp_admin_bar ) {
282 $this->add_help_menu( $wp_admin_bar, false );
283 },
284 100
285 );
286 add_action( 'admin_bar_menu', array( $this, 'add_menu_panel' ), 100 );
287 }
288
289 // Ask AI button — shown whenever Agents Manager is enabled in this editor context.
290 if ( self::is_enabled() ) {
291 add_action( 'admin_bar_menu', array( $this, 'add_ai_chat_button' ), 100 );
292 }
293 }
294
295 /**
296 * Filter to register agent provider modules for the Agents Manager.
297 *
298 * Plugins can hook into this filter to register script module IDs that export
299 * toolProvider and/or contextProvider. The Agents Manager JS will dynamically
300 * import these modules and merge their providers.
301 *
302 * @param array $providers Array of provider script module IDs.
303 */
304 $agent_providers = apply_filters( 'agents_manager_agent_providers', array() );
305
306 $use_unified_experience = self::is_unified_experience();
307
308 /**
309 * Filter the default agent ID for the Agents Manager.
310 *
311 * Allows host applications (e.g., CIAB, WooCommerce AI) to specify a custom
312 * workflow agent instead of the default orchestrator. The value is passed to
313 * the frontend as `agentsManagerData.agentId` and consumed by `useAgentConfig()`.
314 *
315 * @param string|null $agent_id The agent ID to use, or null for default behavior.
316 */
317 $agent_id = apply_filters( 'agents_manager_agent_id', null );
318
319 $this->enqueue_script( $variant );
320
321 $inline_data = array(
322 'agentProviders' => $agent_providers,
323 'useUnifiedExperience' => $use_unified_experience,
324 'isDevMode' => self::is_dev_mode(),
325 'sectionName' => apply_filters( 'agents_manager_section_name', $variant ),
326 'currentUser' => $this->get_current_user_data(),
327 'site' => $this->get_current_site(),
328 'helpCenterUrl' => self::HELP_CENTER_URL,
329 );
330
331 if ( $agent_id ) {
332 $inline_data['agentId'] = $agent_id;
333 }
334
335 /**
336 * Filter the data exposed to the Agents Manager frontend.
337 *
338 * @param array $inline_data Data encoded into `agentsManagerData`.
339 */
340 $filtered = apply_filters( 'jetpack_ai_sidebar_agents_manager_data', $inline_data );
341 $inline_data = is_array( $filtered ) ? $filtered : $inline_data;
342
343 wp_add_inline_script(
344 'agents-manager',
345 'const agentsManagerData = ' . wp_json_encode(
346 $inline_data,
347 JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
348 ) . ';',
349 'before'
350 );
351 }
352
353 /**
354 * The script variant active for this request, or null if none.
355 *
356 * Single source of truth for "is the Agents Manager app loaded on this
357 * request?". Used both to enqueue the app and to gate the server-side
358 * sidebar pre-render, so the pre-rendered shell can never appear on a page
359 * where the app won't mount to reconcile it.
360 *
361 * @return string|null The variant name, or null if scripts should not be loaded.
362 */
363 public static function get_active_variant() {
364 if ( self::is_plugin_information_iframe() ) {
365 return null;
366 }
367
368 /**
369 * Filter the script variant the Agents Manager loads for this request.
370 *
371 * @since 0.1.0
372 *
373 * @param string|null $variant The resolved variant, or null to not load.
374 */
375 return apply_filters( 'agents_manager_variant', self::get_variant() );
376 }
377
378 /**
379 * Whether the current request renders the plugin information iframe.
380 *
381 * The parent plugin screen may load Agents Manager, but the iframe must not
382 * bootstrap a second copy of the app.
383 *
384 * @return bool
385 */
386 private static function is_plugin_information_iframe() {
387 global $current_screen;
388
389 if ( ! $current_screen || 'plugin-install' !== $current_screen->id ) {
390 return false;
391 }
392
393 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a request context check, not a form submission.
394 return isset( $_GET['tab'] ) && 'plugin-information' === sanitize_text_field( wp_unslash( $_GET['tab'] ) );
395 }
396
397 /**
398 * Determine which script variant to load, or null if none should be loaded.
399 *
400 * Combines the gating logic (should we load at all?) with variant selection
401 * (which build to use?) into a single method so the two cannot get out of sync.
402 *
403 * @return string|null The variant name, or null if scripts should not be loaded.
404 */
405 private static function get_variant() {
406 // CIAB: Load either the connected or disconnected variants if enabled.
407 if ( self::is_ciab_environment() && self::is_enabled() ) {
408 return self::is_jetpack_disconnected() ? 'ciab-disconnected' : 'ciab';
409 }
410
411 // Frontend: load disconnected variant for eligible logged-in editors.
412 if ( ! is_admin() ) {
413 if ( self::is_loading_on_frontend() && self::is_enabled() ) {
414 return 'wp-admin-disconnected';
415 }
416 return null;
417 }
418
419 // Apply wp-admin exclusions (WooCommerce, customizer, preview contexts).
420 if ( ! self::passes_admin_checks() ) {
421 return null;
422 }
423
424 if ( ! self::is_enabled() ) {
425 return null;
426 }
427
428 $disconnected = self::is_jetpack_disconnected();
429
430 if ( self::is_block_editor() ) {
431 return $disconnected ? 'gutenberg-disconnected' : 'gutenberg';
432 }
433
434 return $disconnected ? 'wp-admin-disconnected' : 'wp-admin';
435 }
436
437 /**
438 * Whether the unified experience — the Help Center takeover — is active.
439 *
440 * "Unified" here means Agents Manager takes over the Help Center, unifying Odie and
441 * Dolly (the orchestrator) into a single chat experience. This is distinct from
442 * block-editor-only enablement, which replaces Big Sky's native UI without taking
443 * over the Help Center.
444 *
445 * @return bool
446 */
447 public static function is_unified_experience() {
448 /**
449 * Filter to determine if the user should see the unified chat experience.
450 *
451 * When true, Help Center will render UnifiedAIAgent instead of traditional UI.
452 * The filter is hooked by should_use_unified_experience() in this class.
453 *
454 * @param bool $use_unified_experience Whether to use unified experience. Default false.
455 */
456 return (bool) apply_filters( 'agents_manager_use_unified_experience', false );
457 }
458
459 /**
460 * Returns true if the Agents Manager should be loaded in the current context.
461 *
462 * @return bool
463 */
464 public static function is_enabled() {
465 // CIAB: Agents Manager is the default AI experience — enabled unless explicitly
466 // disabled via filter (e.g. for debugging or gradual rollout).
467 if ( self::is_ciab_environment() ) {
468 /**
469 * Filter whether Agents Manager is enabled in CIAB (Next Admin) environments.
470 *
471 * @param bool $enabled Whether Agents Manager should load. Default true.
472 */
473 return apply_filters( 'agents_manager_enabled_in_ciab', true );
474 }
475
476 // Full unified experience: Agents Manager with support guides, Help Center takeover, etc.
477 if ( self::is_unified_experience() ) {
478 return true;
479 }
480
481 // Block editor only: Agents Manager replaces Big Sky's native UI. Hooked by Big Sky.
482 if ( self::is_block_editor() && apply_filters( 'agents_manager_enabled_in_block_editor', false ) ) {
483 return true;
484 }
485
486 return false;
487 }
488
489 /**
490 * Returns true if the current wp-admin context passes all exclusion checks.
491 *
492 * Excludes WooCommerce Admin home, customizer preview, Gutenberg asset requests,
493 * and preview query param contexts.
494 *
495 * @return bool
496 */
497 private static function passes_admin_checks() {
498 // Don't load on WooCommerce Admin home page to avoid UI conflicts.
499 global $current_screen;
500 if ( $current_screen && $current_screen->id === 'woocommerce_page_wc-admin' ) {
501 return false;
502 }
503
504 // Don't load in customizer preview iframe.
505 if ( is_customize_preview() ) {
506 return false;
507 }
508
509 // Don't load during Gutenberg asset requests or preview contexts.
510 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
511 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a context check, not a form submission.
512 $is_preview = isset( $_GET['preview'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['preview'] ) );
513 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a context check, not a form submission.
514 $is_preview_overlay = isset( $_GET['preview_overlay'] );
515 if ( str_contains( $request_uri, 'wp-content/plugins/gutenberg-core' ) || $is_preview || $is_preview_overlay ) {
516 return false;
517 }
518
519 return true;
520 }
521
522 /**
523 * Enqueue Agents Manager script based on context.
524 *
525 * @param string $variant The variant of the asset file to get.
526 */
527 private function enqueue_script( $variant ) {
528 $cache_key = 'agents-manager-asset-' . $variant . '.asset.json';
529 $asset_file = get_transient( $cache_key );
530
531 if ( ! $asset_file ) {
532 $asset_file = self::get_assets_json( 'widgets.wp.com/agents-manager/agents-manager-' . $variant . '.asset.json' );
533 if ( ! $asset_file ) {
534 return;
535 }
536 set_transient( $cache_key, $asset_file, HOUR_IN_SECONDS );
537 }
538
539 // When the request is dev mode, use a random cache buster as the version for easier debugging.
540 $version = self::is_dev_mode() ? wp_rand() : $asset_file['version'];
541
542 $script_dependencies = $asset_file['dependencies'] ?? array();
543
544 // Load translations for connected variants from widgets.wp.com.
545 // Disconnected variants have no translatable UI, so skip them (as Help
546 // Center does). English needs no translation file.
547 if ( ! str_contains( $variant, 'disconnected' ) ) {
548 $locale = self::determine_iso_639_locale();
549
550 if ( 'en' !== $locale ) {
551 wp_enqueue_script(
552 'agents-manager-translations',
553 'https://widgets.wp.com/agents-manager/languages/' . $locale . '-v1.js',
554 array( 'wp-i18n' ),
555 $version,
556 true
557 );
558
559 $script_dependencies[] = 'agents-manager-translations';
560 }
561 }
562
563 wp_enqueue_script(
564 'agents-manager',
565 'https://widgets.wp.com/agents-manager/agents-manager-' . $variant . '.min.js',
566 $script_dependencies,
567 $version,
568 /**
569 * Filter the strategy to use when enqueuing the script.
570 *
571 * @param array|bool $args The arguments to pass to wp_enqueue_script. Default is true.
572 * @param string $handle The handle of the script.
573 */
574 apply_filters( 'agents_manager_enqueue_script_strategy', true, 'agents-manager' )
575 );
576
577 if ( 'gutenberg-disconnected' !== $variant && 'ciab-disconnected' !== $variant ) {
578 wp_enqueue_style(
579 'agents-manager-style',
580 'https://widgets.wp.com/agents-manager/agents-manager-' . $variant . ( is_rtl() ? '.rtl.css' : '.css' ),
581 array(),
582 $version
583 );
584 }
585 }
586
587 /**
588 * Returns the ISO 639 conforming locale string for the current user.
589 *
590 * Normalizes the WordPress user locale to match the widgets.wp.com translation
591 * file naming at languages/{code}-v1.js. Preserves the region for the few locales
592 * where it is meaningful (pt-br, zh-tw, zh-cn); strips the region for all others;
593 * falls back to 'en' when the locale is empty.
594 *
595 * @return string The ISO 639 locale string, e.g. "en".
596 */
597 private static function determine_iso_639_locale() {
598 $language = get_user_locale();
599 $language = strtolower( $language );
600
601 if ( in_array( $language, array( 'pt_br', 'pt-br', 'zh_tw', 'zh-tw', 'zh_cn', 'zh-cn' ), true ) ) {
602 $language = str_replace( '_', '-', $language );
603 } else {
604 $language = preg_replace( '/([-_].*)$/i', '', $language );
605 }
606
607 if ( empty( $language ) ) {
608 return 'en';
609 }
610
611 return $language;
612 }
613
614 /**
615 * Get the asset via file-system on wpcom and via network on Atomic sites.
616 *
617 * @param string $filepath The URL to download the asset file from.
618 * @return array|null The asset file data or null on failure.
619 */
620 private static function get_assets_json( $filepath ) {
621 $accessible_directly = file_exists( ABSPATH . $filepath );
622
623 if ( $accessible_directly ) {
624 $file_contents = file_get_contents( ABSPATH . $filepath );
625
626 if ( false === $file_contents ) {
627 return null;
628 }
629
630 return json_decode( $file_contents, true );
631 }
632
633 $request = wp_remote_get( 'https://' . $filepath );
634
635 if ( is_wp_error( $request ) ) {
636 return null;
637 }
638
639 $response_code = wp_remote_retrieve_response_code( $request );
640 if ( 200 !== $response_code ) {
641 return null;
642 }
643
644 $content_type = wp_remote_retrieve_header( $request, 'content-type' );
645 if ( is_string( $content_type ) && false === strpos( $content_type, 'json' ) ) {
646 return null;
647 }
648
649 $body = wp_remote_retrieve_body( $request );
650 if ( '' === $body ) {
651 return null;
652 }
653
654 $decoded = json_decode( $body, true );
655 if ( json_last_error() !== JSON_ERROR_NONE ) {
656 return null;
657 }
658
659 return $decoded;
660 }
661
662 /**
663 * Update the calypso preferences.
664 *
665 * @param \stdClass $preferences The preferences.
666 *
667 * @return \stdClass The preferences.
668 */
669 public function calypso_preferences_update( $preferences ) {
670 // Check if agents_manager_router_history exists and is a valid array structure
671 if ( ! isset( $preferences->agents_manager_router_history ) ||
672 ! is_array( $preferences->agents_manager_router_history ) ) {
673 return $preferences;
674 }
675
676 $router_history = $preferences->agents_manager_router_history;
677
678 // Check if entries exist and is an array
679 if ( ! isset( $router_history['entries'] ) ||
680 ! is_array( $router_history['entries'] ) ) {
681 return $preferences;
682 }
683
684 $entries = $router_history['entries'];
685
686 // Limit entries to 50 to prevent spamming entries in the router history.
687 if ( count( $entries ) > 50 ) {
688 // Keep only the last 49 entries and add the root entry at the beginning.
689 $entries = array_slice( $entries, -49 );
690 // Keep the start at root so the back button always works.
691 array_unshift(
692 $entries,
693 array(
694 'pathname' => '/',
695 'search' => '',
696 'hash' => '',
697 'key' => 'default',
698 'state' => null,
699 )
700 );
701
702 // Update the preferences object directly
703 $preferences->agents_manager_router_history['entries'] = $entries;
704 $preferences->agents_manager_router_history['index'] = 49;
705 }
706
707 return $preferences;
708 }
709
710 /**
711 * Creates instance.
712 *
713 * @return Agents_Manager
714 */
715 public static function init() {
716 if ( did_action( 'jetpack_agents_manager_initialized' ) ) {
717 return self::get_instance();
718 }
719
720 self::$instance = new self();
721
722 /**
723 * Fires once the Agents Manager class has been instantiated.
724 *
725 * @since 0.5.0
726 */
727 do_action( 'jetpack_agents_manager_initialized' );
728
729 return self::$instance;
730 }
731
732 /**
733 * Returns the instance of the Agents Manager class.
734 *
735 * @return Agents_Manager
736 */
737 public static function get_instance() {
738 return self::$instance;
739 }
740
741 /**
742 * Returns whether the current request is coming from the A8C proxy.
743 *
744 * @return bool
745 */
746 private static function is_proxied() {
747 // On Simple sites, use the wpcom function if available.
748 if ( function_exists( 'wpcom_is_proxied_request' ) ) {
749 return wpcom_is_proxied_request();
750 }
751
752 // On WoA/Garden sites, check server variable or constant.
753 return isset( $_SERVER['A8C_PROXIED_REQUEST'] )
754 ? (bool) sanitize_text_field( wp_unslash( $_SERVER['A8C_PROXIED_REQUEST'] ) )
755 : Constants::is_true( 'A8C_PROXIED_REQUEST' );
756 }
757
758 /**
759 * Enables "Development" features that should be accessible only for admins.
760 */
761 private static function is_dev_mode() {
762 // Known local environments.
763 $domain = wp_parse_url( get_site_url(), PHP_URL_HOST );
764 if (
765 $domain === 'localhost' ||
766 '.jurassic.tube' === stristr( $domain, '.jurassic.tube' ) ||
767 '.jurassic.ninja' === stristr( $domain, '.jurassic.ninja' )
768 ) {
769 return true;
770 }
771
772 // A8C development.
773 if ( self::is_proxied() ) {
774 return true;
775 }
776
777 if ( Constants::is_true( 'AT_PROXIED_REQUEST' ) && Constants::is_defined( 'ATOMIC_CLIENT_ID' ) ) {
778 switch ( Constants::get_constant( 'ATOMIC_CLIENT_ID' ) ) {
779 case 1:
780 case 2:
781 case 3: // Pressable
782 case 32:
783 case 118: // Commerce garden client (ciab)
784 return true;
785 }
786 }
787
788 return false;
789 }
790
791 /**
792 * Register the Agents Manager endpoints.
793 */
794 public function register_rest_api() {
795 ( new WP_REST_Agents_Manager_Persisted_Open_State() )->register_rest_route();
796 ( new WP_REST_Jetpack_AI_JWT() )->register_rest_route();
797 }
798
799 /**
800 * Determine if user should see unified experience.
801 *
802 * @param bool $use_unified_experience Whether to use unified experience.
803 * @return bool
804 */
805 public function should_use_unified_experience( $use_unified_experience = false ) {
806 // Early return for non-proxied/dev mode requests.
807 // This feature is currently only available to Automattic employees testing via proxy.
808 if ( ! self::is_dev_mode() ) {
809 return false;
810 }
811
812 $user_id = get_current_user_id();
813
814 if ( ! $user_id ) {
815 return false;
816 }
817
818 $is_simple_site = ( new \Automattic\Jetpack\Status\Host() )->is_wpcom_simple();
819 if ( $is_simple_site ) {
820 // On Simple sites, evaluate locally.
821 // Check Automattician and opt-in setting.
822 $is_automattician = function_exists( '\is_automattician' ) && \is_automattician( $user_id );
823 if ( $is_automattician && $this->has_unified_chat_opt_in_enabled( $user_id ) ) {
824 return true;
825 }
826 }
827
828 // On WoA and Garden sites, delegate to wpcom via the /agents-manager/state endpoint.
829 // This avoids duplicating rollout logic and handles cases where
830 // wpcom-specific functions (like get_user_attribute) aren't available.
831 if ( $this->fetch_unified_experience_preference() ) {
832 return true;
833 }
834
835 // Default to false, for now.
836 // In the future: users with a big sky site (similar to https://github.a8c.com/Automattic/wpcom/pull/196449/files), a big-sky free trial or a paid plan.
837 return $use_unified_experience;
838 }
839
840 /**
841 * Check if user has enabled unified chat opt-in in their Automattician options.
842 *
843 * This checks the unified_ai_chat calypso preference set via the wpcom profile settings.
844 * Only used on Simple sites where get_user_attribute is available.
845 *
846 * @param int $user_id User ID.
847 *
848 * @return bool
849 */
850 private function has_unified_chat_opt_in_enabled( $user_id ) {
851 if ( ! function_exists( '\get_user_attribute' ) ) {
852 return false;
853 }
854
855 $calypso_prefs = \get_user_attribute( $user_id, 'calypso_preferences' );
856 return ! empty( $calypso_prefs['unified_ai_chat'] );
857 }
858
859 /**
860 * Fetch unified experience preference from wpcom via Jetpack Connection.
861 *
862 * Used on Atomic sites to delegate the decision to wpcom, which has
863 * access to user attributes and can evaluate the rollout logic.
864 *
865 * Calls /agents-manager/state endpoint which is accessible via Jetpack user tokens.
866 *
867 * @return bool Whether user should see unified experience.
868 */
869 private function fetch_unified_experience_preference() {
870 $user_id = get_current_user_id();
871 if ( ! $user_id ) {
872 return false;
873 }
874
875 // Check transient cache first (per-user cache).
876 $cache_key = 'unified-experience-' . $user_id;
877 $cached_result = get_transient( $cache_key );
878 if ( false !== $cached_result ) {
879 return (bool) $cached_result;
880 }
881
882 // Check if user is connected before making API call.
883 if ( ! ( new Connection_Manager() )->is_user_connected( $user_id ) ) {
884 return false;
885 }
886
887 // Call dedicated agents-manager/state endpoint.
888 $wpcom_request = \Automattic\Jetpack\Connection\Client::wpcom_json_api_request_as_user(
889 '/agents-manager/state?key=unified_ai_chat',
890 '2',
891 array( 'method' => 'GET' )
892 );
893
894 if ( is_wp_error( $wpcom_request ) ) {
895 // Cache failures too to avoid hammering the API.
896 set_transient( $cache_key, 0, MINUTE_IN_SECONDS );
897 return false;
898 }
899
900 $response_code = wp_remote_retrieve_response_code( $wpcom_request );
901 if ( 200 !== $response_code ) {
902 set_transient( $cache_key, 0, MINUTE_IN_SECONDS );
903 return false;
904 }
905
906 $body = wp_remote_retrieve_body( $wpcom_request );
907 $decoded_body = json_decode( $body, true );
908
909 // The response is { "unified_ai_chat": true/false } when using key param.
910 $result = is_array( $decoded_body ) && ! empty( $decoded_body['unified_ai_chat'] );
911
912 // Cache for 1 minute.
913 set_transient( $cache_key, $result ? 1 : 0, MINUTE_IN_SECONDS );
914
915 return $result;
916 }
917
918 /**
919 * Returns true if the current request is on the frontend and the user can edit posts.
920 *
921 * Mirrors Help_Center::is_loading_on_frontend().
922 *
923 * @return bool True if loading on the frontend for an eligible user.
924 */
925 private static function is_loading_on_frontend() {
926 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a context check, not a form submission.
927 if ( isset( $_GET['na_site_preview'] ) || isset( $_GET['preview_overlay'] ) ) {
928 return false;
929 }
930
931 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a context check, not a form submission.
932 if ( isset( $_GET['preview'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['preview'] ) ) ) {
933 return false;
934 }
935
936 $can_edit_posts = current_user_can( 'edit_posts' ) && is_user_member_of_blog();
937 return ! is_admin() && ! self::is_block_editor() && $can_edit_posts;
938 }
939
940 /**
941 * Returns true if the current screen is the block editor.
942 *
943 * @return bool True if the current screen is the block editor.
944 */
945 private static function is_block_editor() {
946 if ( ! function_exists( 'get_current_screen' ) ) {
947 return false;
948 }
949
950 $current_screen = get_current_screen();
951 // The widgets screen has the block editor but no Gutenberg top bar.
952 return $current_screen && $current_screen->is_block_editor() && $current_screen->id !== 'widgets';
953 }
954
955 /**
956 * Returns true when Gutenberg's "admin bar in editor" (omnibar) experiment is active.
957 *
958 * Mirrors Gutenberg core's gate in `lib/experimental/omnibar/load.php`, and returns false when
959 * `gutenberg_is_experiment_enabled()` is unavailable. Gutenberg 23.5 renamed the experiment
960 * slug from `gutenberg-admin-bar-in-editor` to `gutenberg-omnibar`; both are checked since
961 * pre-rename builds are still deployed (e.g. wpcom's bundled gutenberg-core).
962 *
963 * @return bool
964 */
965 private static function is_admin_bar_in_editor() {
966 if (
967 ! self::is_block_editor()
968 || ! is_admin_bar_showing()
969 || ! function_exists( 'gutenberg_is_experiment_enabled' )
970 ) {
971 return false;
972 }
973
974 // @phan-suppress-next-line PhanUndeclaredFunction -- Guarded by function_exists() above.
975 return \gutenberg_is_experiment_enabled( 'gutenberg-omnibar' )
976 // @phan-suppress-next-line PhanUndeclaredFunction -- Guarded by function_exists() above.
977 || \gutenberg_is_experiment_enabled( 'gutenberg-admin-bar-in-editor' );
978 }
979
980 /**
981 * Check if current environment is CIAB (Commerce in a Box) / Next Admin.
982 *
983 * Uses the same detection method as Help Center: checks if next_admin_init has fired.
984 *
985 * @return bool True if CIAB/Next Admin environment.
986 */
987 private static function is_ciab_environment() {
988 return (bool) did_action( 'next_admin_init' );
989 }
990
991 /**
992 * Returns true if the current user is NOT connected through Jetpack.
993 *
994 * Mirrors the logic from Help_Center::is_jetpack_disconnected().
995 *
996 * @return bool True if the site uses Jetpack but the current user is not connected.
997 */
998 private static function is_jetpack_disconnected() {
999 $user_id = get_current_user_id();
1000 $blog_id = get_current_blog_id();
1001
1002 if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
1003 return ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected( $user_id );
1004 }
1005
1006 if ( true === apply_filters( 'is_jetpack_site', false, $blog_id ) ) {
1007 return ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected( $user_id );
1008 }
1009
1010 return false;
1011 }
1012
1013 /**
1014 * Get current user data for the agents manager.
1015 *
1016 * Mirrors the user data structure from Help Center's helpCenterData.
1017 *
1018 * @return array|null User data array or null if not logged in.
1019 */
1020 private function get_current_user_data() {
1021 $user_id = get_current_user_id();
1022 if ( ! $user_id ) {
1023 return null;
1024 }
1025
1026 $user_data = get_userdata( $user_id );
1027 if ( ! $user_data ) {
1028 return null;
1029 }
1030
1031 $user_email = $user_data->user_email;
1032
1033 // Use wpcom_get_avatar_url on Simple sites, fall back to get_avatar_url elsewhere.
1034 if ( function_exists( 'wpcom_get_avatar_url' ) ) {
1035 $avatar_url = wpcom_get_avatar_url( $user_email, 64, '', true )[0];
1036 } else {
1037 $avatar_url = get_avatar_url( $user_id );
1038 }
1039
1040 return array(
1041 'ID' => $user_id,
1042 'username' => $user_data->user_login,
1043 'display_name' => $user_data->display_name,
1044 'avatar_URL' => $avatar_url,
1045 'email' => $user_email,
1046 );
1047 }
1048
1049 /**
1050 * Get current site data for the agents manager.
1051 *
1052 * Returns minimal site data needed by AgentsManager (ID and domain only).
1053 * Uses jetpack_options['id'] on Atomic sites for the wpcom blog ID.
1054 *
1055 * @return array Site data with ID and domain.
1056 */
1057 private function get_current_site() {
1058 /*
1059 * Atomic sites have the WP.com blog ID stored as a Jetpack option.
1060 * This code deliberately doesn't use `Jetpack_Options::get_option`
1061 * so it works even when Jetpack has not been loaded.
1062 */
1063 $jetpack_options = get_option( 'jetpack_options' );
1064 if ( is_array( $jetpack_options ) && isset( $jetpack_options['id'] ) ) {
1065 $site_id = (int) $jetpack_options['id'];
1066 } else {
1067 $site_id = get_current_blog_id();
1068 }
1069
1070 return array(
1071 'ID' => $site_id,
1072 'domain' => wp_parse_url( home_url(), PHP_URL_HOST ),
1073 );
1074 }
1075 }
1076