PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-connection/src/abilities/class-connection-abilities.php +221 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,221 @@
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 +}