PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-connection / src / abilities / class-connection-abilities.php

class-connection-abilities.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.5, at jetpack_vendor/automattic/jetpack-connection/src/abilities/class-connection-abilities.php

222 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Jetpack Connection Abilities Registration.
4 *
5 * Registers Jetpack Connection abilities with the WordPress Abilities API so
6 * AI agents can inspect the site's connection state through the standard
7 * `wp-abilities/v1` REST surface.
8 *
9 * @package automattic/jetpack-connection
10 */
11
12 namespace Automattic\Jetpack\Connection\Abilities;
13
14 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
15 use Automattic\Jetpack\Connection\Package_Version;
16 use Automattic\Jetpack\WP_Abilities\Registrar;
17 use Jetpack_Options;
18
19 /**
20 * Registers Jetpack Connection abilities with the WordPress Abilities API.
21 *
22 * Exposes a single read-only ability for site-level connection state so AI
23 * agents can answer "is this site registered?" without having to
24 * reverse-engineer Jetpack_Options keys.
25 *
26 * Writes (registering a new site, disconnecting a user, transferring
27 * ownership) are deliberately deferred to a follow-up PR.
28 */
29 class Connection_Abilities extends Registrar {
30
31 const CATEGORY_SLUG = 'jetpack';
32
33 /**
34 * {@inheritDoc}
35 */
36 public static function get_category_slug(): string {
37 return self::CATEGORY_SLUG;
38 }
39
40 /**
41 * {@inheritDoc}
42 *
43 * The `jetpack` ability-category is shared with other Jetpack registrars
44 * (e.g. the Modules_Abilities class in the Jetpack plugin). Only the first
45 * registration wins, so the English source string is kept byte-identical
46 * across registrars to keep the visible category text consistent
47 * regardless of load order.
48 */
49 public static function get_category_definition(): array {
50 return array(
51 // "Jetpack" is a product name and should not be translated.
52 'label' => 'Jetpack',
53 'description' => __( 'Abilities provided by Jetpack.', 'jetpack-connection' ),
54 );
55 }
56
57 /**
58 * {@inheritDoc}
59 */
60 public static function get_abilities(): array {
61 return array(
62 'jetpack/get-connection-status' => self::spec_get_connection_status(),
63 );
64 }
65
66 /*
67 ---------------------------------------------------------------------
68 * Ability specs
69 * ---------------------------------------------------------------------
70 */
71
72 /**
73 * Spec: jetpack/get-connection-status.
74 */
75 private static function spec_get_connection_status(): array {
76 return array(
77 'label' => __( 'Get Jetpack connection status', 'jetpack-connection' ),
78 'description' => __(
79 'Return the site-level Jetpack connection state in one zero-argument call. Shape: { site_registered, user_connected, master_user, blog_id, registration_url, connection_version }. `site_registered` is true when the site has a blog id and a blog token. `user_connected` is true when at least one user has linked their WordPress.com account. `master_user` is the local user id of the connection owner (the user who registered the site), or null if there is no owner. `blog_id` is the WordPress.com site id, or null when the site has not been registered. `registration_url` is the wp-admin URL the site owner should visit to register the site when `site_registered` is false; null once the site is registered. `connection_version` is the running Jetpack Connection package version. Read-only and idempotent — safe to poll.',
80 'jetpack-connection'
81 ),
82 'input_schema' => array(
83 'type' => 'object',
84 'properties' => new \stdClass(),
85 'additionalProperties' => false,
86 ),
87 'output_schema' => array(
88 'type' => 'object',
89 'properties' => array(
90 'site_registered' => array( 'type' => 'boolean' ),
91 'user_connected' => array( 'type' => 'boolean' ),
92 'master_user' => array( 'type' => array( 'integer', 'null' ) ),
93 'blog_id' => array( 'type' => array( 'integer', 'null' ) ),
94 'registration_url' => array( 'type' => array( 'string', 'null' ) ),
95 'connection_version' => array( 'type' => 'string' ),
96 ),
97 ),
98 'execute_callback' => array( __CLASS__, 'get_connection_status' ),
99 'permission_callback' => array( __CLASS__, 'can_view_connection' ),
100 'meta' => array(
101 'annotations' => array(
102 'readonly' => true,
103 'destructive' => false,
104 'idempotent' => true,
105 ),
106 'show_in_rest' => true,
107 'mcp' => array(
108 'public' => true,
109 'type' => 'tool', // default is already "tool", but can be explicit.
110 ),
111 ),
112 );
113 }
114
115 /*
116 ---------------------------------------------------------------------
117 * Permission callbacks
118 * ---------------------------------------------------------------------
119 */
120
121 /**
122 * Permission check: mirrors the capability used by the Jetpack admin page.
123 *
124 * Connection state is not sensitive in itself (the same data is exposed
125 * on the Jetpack admin page and through several existing REST endpoints),
126 * but subscribers and contributors have no legitimate need to inspect it.
127 * Gating on `jetpack_admin_page` aligns with how {@see Modules_Abilities}
128 * scopes its read.
129 *
130 * @return bool
131 */
132 public static function can_view_connection(): bool {
133 return current_user_can( 'jetpack_admin_page' );
134 }
135
136 /*
137 ---------------------------------------------------------------------
138 * Execute callbacks
139 * ---------------------------------------------------------------------
140 */
141
142 /**
143 * Execute: get-connection-status.
144 *
145 * @param array|null $input Ignored — zero-arg ability.
146 * @return array
147 */
148 public static function get_connection_status( $input = null ) {
149 unset( $input );
150
151 $manager = self::get_manager();
152 $site_registered = (bool) $manager->is_connected();
153 $user_connected = (bool) $manager->has_connected_user();
154
155 $master_user_raw = Jetpack_Options::get_option( 'master_user' );
156 $master_user = is_numeric( $master_user_raw ) && (int) $master_user_raw > 0 ? (int) $master_user_raw : null;
157
158 $blog_id_raw = Jetpack_Options::get_option( 'id' );
159 $blog_id = is_numeric( $blog_id_raw ) && (int) $blog_id_raw > 0 ? (int) $blog_id_raw : null;
160
161 return array(
162 'site_registered' => $site_registered,
163 'user_connected' => $user_connected,
164 'master_user' => $master_user,
165 'blog_id' => $blog_id,
166 'registration_url' => $site_registered ? null : self::registration_url(),
167 'connection_version' => Package_Version::PACKAGE_VERSION,
168 );
169 }
170
171 /*
172 ---------------------------------------------------------------------
173 * Helpers
174 * ---------------------------------------------------------------------
175 */
176
177 /**
178 * Return a Connection_Manager instance. Filterable for tests so they can
179 * inject a partial mock without having to seed Jetpack_Options + tokens.
180 *
181 * @return Connection_Manager
182 */
183 protected static function get_manager(): Connection_Manager {
184 /**
185 * Filters the Connection_Manager instance used by the Connection abilities.
186 *
187 * Tests inject a partial mock here; production callers should leave
188 * the default. The filter callback receives the package-default
189 * instance and must return a Connection_Manager — non-Manager
190 * returns are discarded.
191 *
192 * @since 8.4.0
193 *
194 * @param Connection_Manager $manager The default instance.
195 */
196 $instance = apply_filters( 'jetpack_connection_abilities_manager', new Connection_Manager() );
197 return $instance instanceof Connection_Manager ? $instance : new Connection_Manager();
198 }
199
200 /**
201 * Build the wp-admin URL the site owner should visit to register the
202 * site to WordPress.com. We deliberately return a stable admin URL (no
203 * secret generation, no XML-RPC roundtrip) so this read stays side-effect
204 * free and cheap to poll. The destination page handles the actual
205 * registration handshake from there.
206 *
207 * WP 7.0+ ships a core "Connectors" screen at `wp-admin/options-connectors.php`
208 * with a Jetpack card registered by {@see Jetpack_Connector}. We probe
209 * for the file directly (rather than a `class_exists()` on the registry)
210 * because the file is what actually serves the URL — if it isn't on
211 * disk, the redirect 404s regardless of which classes have loaded.
212 *
213 * @return string
214 */
215 private static function registration_url(): string {
216 if ( defined( 'ABSPATH' ) && file_exists( ABSPATH . 'wp-admin/options-connectors.php' ) ) {
217 return admin_url( 'options-connectors.php' );
218 }
219 return admin_url( 'admin.php?page=jetpack' );
220 }
221 }
222