PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
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 / _inc / lib / core-api / wpcom-endpoints / class-wpcom-rest-api-v2-endpoint-agent-guidelines-ai.php

class-wpcom-rest-api-v2-endpoint-agent-guidelines-ai.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at _inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-agent-guidelines-ai.php

191 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * REST API proxy endpoint for AI-powered content guidelines suggestions.
4 *
5 * Proxies requests to the wpcom endpoint that generates guidelines
6 * using site content analysis.
7 *
8 * @package automattic/jetpack
9 */
10
11 use Automattic\Jetpack\Connection\Client;
12 use Automattic\Jetpack\Connection\Manager;
13 use Automattic\Jetpack\Status\Host;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit( 0 );
17 }
18
19 /**
20 * Class WPCOM_REST_API_V2_Endpoint_Agent_Guidelines_AI
21 */
22 class WPCOM_REST_API_V2_Endpoint_Agent_Guidelines_AI extends WP_REST_Controller {
23 /**
24 * Namespace prefix.
25 *
26 * @var string
27 */
28 public $namespace = 'wpcom/v2';
29
30 /**
31 * Endpoint base route.
32 *
33 * @var string
34 */
35 public $rest_base = 'jetpack-ai/suggest-guidelines';
36
37 /**
38 * Constructor.
39 */
40 public function __construct() {
41 $this->is_wpcom = true;
42 $this->wpcom_is_wpcom_only_endpoint = true;
43
44 add_action( 'rest_api_init', array( $this, 'maybe_register_routes' ) );
45 }
46
47 /**
48 * Register routes on `rest_api_init`, gating on the AI feature state.
49 *
50 * The Jetpack_AI_Helper check (which loads the helper and instantiates
51 * Status/Host classes) runs here rather than in the constructor so that code
52 * is only loaded when the REST API is actually in use, not on every
53 * front-end, cron, or login request.
54 */
55 public function maybe_register_routes() {
56 if ( ! class_exists( 'Jetpack_AI_Helper' ) ) {
57 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-ai-helper.php';
58 }
59
60 // Intentionally gated to Simple, Atomic, and WordPress VIP sites only.
61 // Broader self-hosted Jetpack support is deferred — we want to roll out
62 // on these platforms first before opening to all connected sites.
63 if ( ! \Jetpack_AI_Helper::is_enabled_for_content_guidelines() ) {
64 return;
65 }
66
67 $this->register_routes();
68 }
69
70 /**
71 * Register routes.
72 */
73 public function register_routes() {
74 register_rest_route(
75 $this->namespace,
76 '/' . $this->rest_base,
77 array(
78 'methods' => WP_REST_Server::CREATABLE,
79 'callback' => array( $this, 'suggest_guidelines' ),
80 'permission_callback' => array( $this, 'permission_callback' ),
81 'args' => array(
82 'categories' => array(
83 'description' => __( 'Categories to generate guidelines for.', 'jetpack' ),
84 'type' => 'object',
85 'required' => true,
86 ),
87 ),
88 )
89 );
90 }
91
92 /**
93 * Permission check — require manage_options, plus a connected user account
94 * on sites that proxy the request to WordPress.com over the connection.
95 *
96 * @return bool|WP_Error
97 */
98 public function permission_callback() {
99 if ( ! current_user_can( 'manage_options' ) ) {
100 return false;
101 }
102
103 // suggest_guidelines() signs with the user token off Simple, so without a
104 // user connection there is no identity to present and WordPress.com rejects
105 // the request on private sites.
106 if ( ! ( new Host() )->is_wpcom_simple() && ! ( new Manager() )->is_user_connected() ) {
107 return new WP_Error(
108 'rest_cannot_suggest_guidelines',
109 __( 'Please connect your user account to WordPress.com', 'jetpack' ),
110 array( 'status' => rest_authorization_required_code() )
111 );
112 }
113
114 return true;
115 }
116
117 /**
118 * Proxy the suggest-guidelines request to wpcom.
119 *
120 * @param WP_REST_Request $request The request object.
121 * @return mixed|WP_Error
122 */
123 public function suggest_guidelines( $request ) {
124 $blog_id = \Jetpack_Options::get_option( 'id' );
125
126 $body = array(
127 'categories' => $request->get_param( 'categories' ),
128 );
129
130 $path = sprintf( '/sites/%d/jetpack-ai/suggest-guidelines', $blog_id ) . '?force=wpcom';
131 $args = array(
132 'method' => 'POST',
133 'headers' => array( 'content-type' => 'application/json' ),
134 'timeout' => 90,
135 );
136
137 // On a private site WordPress.com resolves access from the requesting user,
138 // and a blog token carries no user_id — so the request is rejected before
139 // the endpoint runs, and again on the internal AI proxy dispatch. Signing
140 // as the user clears both, because the identity holds for the whole request.
141 // permission_callback() has already established that a user token exists.
142 //
143 // Simple sites keep the blog-token call: it short-circuits to an in-process
144 // WPCOM_API_Direct dispatch that never leaves the request, so the logged-in
145 // user is already present and there is no connection to authenticate against.
146 if ( ( new Host() )->is_wpcom_simple() ) {
147 $response = Client::wpcom_json_api_request_as_blog(
148 $path,
149 '2',
150 $args,
151 wp_json_encode( $body, JSON_UNESCAPED_SLASHES ),
152 'wpcom'
153 );
154 } else {
155 $response = Client::wpcom_json_api_request_as_user(
156 $path,
157 '2',
158 $args,
159 wp_json_encode( $body, JSON_UNESCAPED_SLASHES ),
160 'wpcom'
161 );
162 }
163
164 if ( is_wp_error( $response ) ) {
165 return $response;
166 }
167
168 $status_code = wp_remote_retrieve_response_code( $response );
169 $body_str = wp_remote_retrieve_body( $response );
170 $data = json_decode( $body_str, true );
171
172 if ( $status_code !== 200 ) {
173 $message = is_array( $data ) && isset( $data['message'] ) ? $data['message'] : __( 'Failed to generate guidelines.', 'jetpack' );
174 $code = is_array( $data ) && isset( $data['code'] ) ? $data['code'] : 'upstream_error';
175 return new WP_Error( $code, $message, array( 'status' => $status_code ) );
176 }
177
178 if ( JSON_ERROR_NONE !== json_last_error() ) {
179 return new WP_Error(
180 'invalid_response',
181 __( 'The guidelines service returned a malformed response.', 'jetpack' ),
182 array( 'status' => 502 )
183 );
184 }
185
186 return $data;
187 }
188 }
189
190 wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Agent_Guidelines_AI' );
191