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-publicize / src / rest-api / class-render-messages-controller.php

class-render-messages-controller.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.5, at jetpack_vendor/automattic/jetpack-publicize/src/rest-api/class-render-messages-controller.php

267 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Publicize: Render Messages Controller
4 *
5 * @package automattic/jetpack-publicize
6 */
7
8 namespace Automattic\Jetpack\Publicize\REST_API;
9
10 use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
11 use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
12 use WP_Error;
13 use WP_REST_Request;
14 use WP_REST_Server;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit( 0 );
18 }
19
20 /**
21 * Publicize: Render Messages Controller class.
22 *
23 * Renders Publicize message templates for a given post and a batch of
24 * connection inputs in a single request, so the block-editor preview can
25 * fetch all enabled connections' previews in one round-trip on sites with
26 * social paid features.
27 *
28 * POST takes a JSON body of `{ post_id, items: [...], post_intent: {...} }`
29 * and returns one record per input item, in input order, keyed by the
30 * client-supplied `connection_id`. Body-based POST is used instead of a GET
31 * collection so multi-connection / long-message batches don't hit
32 * infrastructure URL caps.
33 *
34 * @phan-constructor-used-for-side-effects
35 */
36 class Render_Messages_Controller extends Base_Controller {
37
38 use WPCOM_REST_API_Proxy_Request;
39
40 /**
41 * Constructor.
42 */
43 public function __construct() {
44 parent::__construct();
45
46 $this->base_api_path = 'wpcom';
47 $this->version = 'v2';
48
49 $this->namespace = "{$this->base_api_path}/{$this->version}";
50 $this->rest_base = 'publicize/render-messages';
51
52 $this->allow_requests_as_blog = true;
53
54 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
55 }
56
57 /**
58 * Register the routes.
59 */
60 public function register_routes() {
61 register_rest_route(
62 $this->namespace,
63 '/' . $this->rest_base,
64 array(
65 'methods' => WP_REST_Server::CREATABLE,
66 'callback' => array( $this, 'render_messages' ),
67 'permission_callback' => array( $this, 'permissions_check' ),
68 'private_site_security_settings' => array(
69 'allow_blog_token_access' => true,
70 ),
71 'args' => array(
72 'post_id' => array(
73 'description' => __( 'The ID of the post to render the messages for.', 'jetpack-publicize-pkg' ),
74 'type' => 'integer',
75 'required' => true,
76 ),
77 'items' => array(
78 'description' => __( 'List of per-connection render inputs.', 'jetpack-publicize-pkg' ),
79 'type' => 'array',
80 'required' => true,
81 'minItems' => 1,
82 'items' => array(
83 'type' => 'object',
84 'additionalProperties' => false,
85 'required' => array( 'connection_id' ),
86 'properties' => array(
87 'connection_id' => array(
88 'description' => __( 'Publicize connection ID — used to dispatch the renderer and resolve the per-connection template.', 'jetpack-publicize-pkg' ),
89 'type' => 'string',
90 ),
91 'message' => array(
92 'description' => __( 'Optional message override. Empty walks the per-connection / site / network-default chain.', 'jetpack-publicize-pkg' ),
93 'type' => 'string',
94 'default' => '',
95 ),
96 'is_social_post' => array(
97 'description' => __( 'Whether the post will be shared as a social post (media attached) rather than a link share.', 'jetpack-publicize-pkg' ),
98 'type' => 'boolean',
99 'default' => false,
100 ),
101 ),
102 ),
103 ),
104 'post_intent' => array(
105 'description' => __( 'Edited post fields to use when rendering unsaved preview changes.', 'jetpack-publicize-pkg' ),
106 'type' => 'object',
107 'default' => array(),
108 'additionalProperties' => false,
109 'properties' => array(
110 'title' => array(
111 'description' => __( 'Edited post title.', 'jetpack-publicize-pkg' ),
112 'type' => 'string',
113 'default' => '',
114 ),
115 'excerpt' => array(
116 'description' => __( 'Edited post excerpt.', 'jetpack-publicize-pkg' ),
117 'type' => 'string',
118 'default' => '',
119 ),
120 'content' => array(
121 'description' => __( 'Edited post content.', 'jetpack-publicize-pkg' ),
122 'type' => 'string',
123 'default' => '',
124 ),
125 ),
126 ),
127 ),
128 'schema' => array( $this, 'get_public_item_schema' ),
129 )
130 );
131 }
132
133 /**
134 * Retrieves the JSON schema for a single rendered-message item.
135 *
136 * @return array Schema data.
137 */
138 public function get_item_schema() {
139 return array(
140 '$schema' => 'http://json-schema.org/draft-04/schema#',
141 'title' => 'publicize-render-messages-item',
142 'type' => 'object',
143 'properties' => array(
144 'connection_id' => array(
145 'description' => __( 'Connection identifier echoed back from the request.', 'jetpack-publicize-pkg' ),
146 'type' => 'string',
147 'readonly' => true,
148 'context' => array( 'view', 'edit' ),
149 ),
150 'rendered_message' => array(
151 'description' => __( 'The rendered message for this item. Absent when the item failed to render.', 'jetpack-publicize-pkg' ),
152 'type' => 'string',
153 'readonly' => true,
154 'context' => array( 'view', 'edit' ),
155 ),
156 'hyperlinks' => array(
157 'description' => __( 'Editor hyperlinks preserved from content or excerpt placeholders.', 'jetpack-publicize-pkg' ),
158 'type' => 'array',
159 'readonly' => true,
160 'context' => array( 'view', 'edit' ),
161 'items' => array(
162 'type' => 'object',
163 'properties' => array(
164 'text' => array( 'type' => 'string' ),
165 'href' => array( 'type' => 'string' ),
166 'occurrence' => array( 'type' => 'integer' ),
167 ),
168 ),
169 ),
170 'error' => array(
171 'description' => __( 'Per-item error. Present only when this item failed to render.', 'jetpack-publicize-pkg' ),
172 'type' => 'object',
173 'readonly' => true,
174 'context' => array( 'view', 'edit' ),
175 'properties' => array(
176 'code' => array( 'type' => 'string' ),
177 'message' => array( 'type' => 'string' ),
178 ),
179 ),
180 ),
181 );
182 }
183
184 /**
185 * Permission check.
186 *
187 * Preserves the blog-token proxy path via Base_Controller::publicize_permissions_check()
188 * (which returns true for authorized blog requests when allow_requests_as_blog is set),
189 * and enforces post-level `edit_post` capability for regular user requests.
190 *
191 * @param WP_REST_Request $request Full details about the request.
192 * @return true|WP_Error True if authorized, WP_Error otherwise.
193 */
194 public function permissions_check( $request ) {
195 $base_check = $this->publicize_permissions_check();
196
197 if ( is_wp_error( $base_check ) ) {
198 return $base_check;
199 }
200
201 // Blog-token proxy requests don't have a user context; the publicize check
202 // above already returned true for those.
203 if ( self::is_authorized_blog_request() ) {
204 return true;
205 }
206
207 $post_id = (int) $request->get_param( 'post_id' );
208
209 if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
210 return new WP_Error(
211 'invalid_user_permission_publicize',
212 __( 'Sorry, you are not allowed to access Jetpack Social data for this post.', 'jetpack-publicize-pkg' ),
213 array( 'status' => rest_authorization_required_code() )
214 );
215 }
216
217 return true;
218 }
219
220 /**
221 * Render the messages for the given post and items.
222 *
223 * Top-level errors (feature off, post not found) short-circuit the whole batch.
224 * Per-item failures are returned as `{ id, error: { code, message } }` so a
225 * single bad item never fails the batch.
226 *
227 * @param WP_REST_Request $request The request object.
228 * @return mixed The rendered items array, or a WP_Error for top-level failures.
229 */
230 public function render_messages( $request ) {
231 if ( Utils::is_wpcom() ) {
232 if ( ! wpcom_site_has_feature( \WPCOM_Features::SOCIAL_ENHANCED_PUBLISHING ) ) {
233 return new WP_Error(
234 'feature_not_enabled',
235 __( 'Publicize message templates are not enabled for this site.', 'jetpack-publicize-pkg' ),
236 array( 'status' => 403 )
237 );
238 }
239
240 $post_id = (int) $request->get_param( 'post_id' );
241 $items = (array) $request->get_param( 'items' );
242 $intent = (array) $request->get_param( 'post_intent' );
243
244 $post = get_post( $post_id );
245
246 if ( ! $post ) {
247 return new WP_Error(
248 'post_not_found',
249 __( 'Post not found.', 'jetpack-publicize-pkg' ),
250 array( 'status' => 404 )
251 );
252 }
253
254 require_lib( 'publicize/util/message-templates' );
255
256 return rest_ensure_response(
257 \Publicize\render_messages( $post, $items, $intent )
258 );
259 }
260
261 // Self-hosted Jetpack: proxy the request body to WPCOM.
262 return rest_ensure_response(
263 $this->proxy_request_to_wpcom_as_blog( $request )
264 );
265 }
266 }
267