PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.9.2
Jetpack – WP Security, Backup, Speed, & Growth v13.9.2
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 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 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 / _inc / lib / core-api / wpcom-fields / post-fields-publicize-connections.php
jetpack / _inc / lib / core-api / wpcom-fields Last commit date
post-fields-publicize-connections.php 2 years ago
post-fields-publicize-connections.php
445 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Handle Publicize connection information for each post.
4 *
5 * @package automattic/jetpack
6 */
7
8 /**
9 * Add per-post Publicize Connection data.
10 *
11 * { # Post Object
12 * ...
13 * jetpack_publicize_connections: { # Defined below in this file. See schema for more detail.
14 * id: (string) Connection unique_id
15 * service_name: (string) Service slug
16 * display_name: (string) User name/display name of user/connection on Service
17 * profile_picture: (string) Profile picture of user/connection on Service
18 * enabled: (boolean) Is this connection slated to be shared to? context=edit only
19 * done: (boolean) Is this post (or connection) done sharing? context=edit only
20 * toggleable: (boolean) Can the current user change the `enabled` setting for this Connection+Post? context=edit only
21 * }
22 * ...
23 * meta: { # Not defined in this file. Handled in modules/publicize/publicize.php via `register_meta()`
24 * jetpack_publicize_feature_enabled: (boolean) Is this publicize feature enabled?
25 * jetpack_publicize_message: (string) The message to use instead of the post's title when sharing.
26 * jetpack_social_options: {
27 * attached_media: (array) List of media that will be attached to the social media post.
28 * image_generator_settings: (array) List of settings related to the generated image.
29 * }
30 * ...
31 * }
32 *
33 * @since 6.8.0
34 */
35 class WPCOM_REST_API_V2_Post_Publicize_Connections_Field extends WPCOM_REST_API_V2_Field_Controller {
36 /**
37 * Array of post types that can handle Publicize.
38 *
39 * @var array
40 */
41 protected $object_type = array( 'post' );
42
43 /**
44 * Field name
45 *
46 * @var string
47 */
48 protected $field_name = 'jetpack_publicize_connections';
49
50 /**
51 * Array of post IDs that have been updated.
52 *
53 * @var array
54 */
55 private $meta_saved = array();
56
57 /**
58 * Used to memoize the updates for a given post.
59 *
60 * @var array
61 */
62 public $memoized_updates = array();
63
64 /**
65 * Registers the jetpack_publicize_connections field. Called
66 * automatically on `rest_api_init()`.
67 */
68 public function register_fields() {
69 $this->object_type = get_post_types_by_support( 'publicize' );
70 foreach ( $this->object_type as $post_type ) {
71 if ( $this->is_registered( $post_type ) ) {
72 continue;
73 }
74 // Adds meta support for those post types that don't already have it.
75 // Only runs during REST API requests, so it doesn't impact UI.
76 if ( ! post_type_supports( $post_type, 'custom-fields' ) ) {
77 add_post_type_support( $post_type, 'custom-fields' );
78 }
79
80 add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'rest_pre_insert' ), 10, 2 );
81 add_action( 'rest_insert_' . $post_type, array( $this, 'rest_insert' ), 10, 3 );
82 }
83
84 parent::register_fields();
85 }
86
87 /**
88 * Defines data structure and what elements are visible in which contexts
89 */
90 public function get_schema() {
91 return array(
92 '$schema' => 'http://json-schema.org/draft-04/schema#',
93 'title' => 'jetpack-publicize-post-connections',
94 'type' => 'array',
95 'context' => array( 'view', 'edit' ),
96 'items' => $this->post_connection_schema(),
97 'default' => array(),
98 );
99 }
100
101 /**
102 * Schema for the endpoint.
103 */
104 private function post_connection_schema() {
105 return array(
106 '$schema' => 'http://json-schema.org/draft-04/schema#',
107 'title' => 'jetpack-publicize-post-connection',
108 'type' => 'object',
109 'properties' => array(
110 'id' => array(
111 'description' => __( 'Unique identifier for the Jetpack Social connection', 'jetpack' ),
112 'type' => 'string',
113 'context' => array( 'view', 'edit' ),
114 'readonly' => true,
115 ),
116 'service_name' => array(
117 'description' => __( 'Alphanumeric identifier for the Jetpack Social service', 'jetpack' ),
118 'type' => 'string',
119 'context' => array( 'view', 'edit' ),
120 'readonly' => true,
121 ),
122 'display_name' => array(
123 'description' => __( 'Display name of the connected account', 'jetpack' ),
124 'type' => 'string',
125 'context' => array( 'view', 'edit' ),
126 'readonly' => true,
127 ),
128 'username' => array(
129 'description' => __( 'Username of the connected account', 'jetpack' ),
130 'type' => 'string',
131 'context' => array( 'view', 'edit' ),
132 'readonly' => true,
133 ),
134 'profile_picture' => array(
135 'description' => __( 'Profile picture of the connected account', 'jetpack' ),
136 'type' => 'string',
137 'context' => array( 'view', 'edit' ),
138 'readonly' => true,
139 ),
140 'enabled' => array(
141 'description' => __( 'Whether to share to this connection', 'jetpack' ),
142 'type' => 'boolean',
143 'context' => array( 'edit' ),
144 ),
145 'done' => array(
146 'description' => __( 'Whether Jetpack Social has already finished sharing for this post', 'jetpack' ),
147 'type' => 'boolean',
148 'context' => array( 'edit' ),
149 'readonly' => true,
150 ),
151 'toggleable' => array(
152 'description' => __( 'Whether `enable` can be changed for this post/connection', 'jetpack' ),
153 'type' => 'boolean',
154 'context' => array( 'edit' ),
155 'readonly' => true,
156 ),
157 'external_id' => array(
158 'description' => __( 'The external ID of the connected account', 'jetpack' ),
159 'type' => 'string',
160 'context' => array( 'view', 'edit' ),
161 'readonly' => true,
162 ),
163 ),
164 );
165 }
166
167 /**
168 * Permission check, based on module availability and user capabilities.
169 *
170 * @param int $post_id Post ID.
171 *
172 * @return true|WP_Error
173 */
174 public function permission_check( $post_id ) {
175 global $publicize;
176
177 if ( ! $publicize ) {
178 return new WP_Error(
179 'publicize_not_available',
180 __( 'Sorry, Jetpack Social is not available on your site right now.', 'jetpack' ),
181 array( 'status' => rest_authorization_required_code() )
182 );
183 }
184
185 if ( $publicize->current_user_can_access_publicize_data( $post_id ) ) {
186 return true;
187 }
188
189 return new WP_Error(
190 'invalid_user_permission_publicize',
191 __( 'Sorry, you are not allowed to access Jetpack Social data for this post.', 'jetpack' ),
192 array( 'status' => rest_authorization_required_code() )
193 );
194 }
195
196 /**
197 * Getter permission check
198 *
199 * @param mixed $post_array Response from the post endpoint.
200 * @param WP_REST_Request $request API request.
201 *
202 * @return true|WP_Error
203 */
204 public function get_permission_check( $post_array, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
205 return $this->permission_check( isset( $post_array['id'] ) ? $post_array['id'] : 0 );
206 }
207
208 /**
209 * Setter permission check.
210 *
211 * @param mixed $value The new value for the field.
212 * @param WP_Post $post The post object.
213 * @param WP_REST_Request $request API request.
214 *
215 * @return true|WP_Error
216 */
217 public function update_permission_check( $value, $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
218 return $this->permission_check( isset( $post->ID ) ? $post->ID : 0 );
219 }
220
221 /**
222 * Getter: Retrieve current list of connected social accounts for a given post.
223 *
224 * @see Publicize::get_filtered_connection_data()
225 *
226 * @param array $post_array Response from Post Endpoint.
227 * @param WP_REST_Request $request API request.
228 *
229 * @return array List of connections
230 */
231 public function get( $post_array, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
232 global $publicize;
233
234 if ( ! $publicize ) {
235 return array();
236 }
237
238 $schema = $this->post_connection_schema();
239 $properties = array_keys( $schema['properties'] );
240
241 $connections = $publicize->get_filtered_connection_data( $post_array['id'] );
242
243 $output_connections = array();
244 foreach ( $connections as $connection ) {
245 $output_connection = array();
246 foreach ( $properties as $property ) {
247 if ( isset( $connection[ $property ] ) ) {
248 $output_connection[ $property ] = $connection[ $property ];
249 }
250 }
251 $output_connection['id'] = (string) $connection['unique_id'];
252 $output_connection['connection_id'] = (string) $connection['id'];
253
254 $output_connection['can_disconnect'] = current_user_can( 'edit_others_posts' ) || get_current_user_id() === (int) $connection['user_id'];
255 $output_connection['shared'] = $connection['global'];
256
257 $output_connections[] = $output_connection;
258 }
259
260 return $output_connections;
261 }
262
263 /**
264 * Prior to updating the post, first calculate which Services to
265 * Publicize to and which to skip.
266 *
267 * @param object $post Post data to insert/update.
268 * @param WP_REST_Request $request API request.
269 *
270 * @return object|WP_Error Filtered $post
271 */
272 public function rest_pre_insert( $post, $request ) {
273 if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
274 return $post;
275 }
276
277 $permission_check = $this->update_permission_check( $request['jetpack_publicize_connections'], $post, $request );
278 if ( is_wp_error( $permission_check ) ) {
279 return $permission_check;
280 }
281 // memoize.
282 $this->get_meta_to_update( $request['jetpack_publicize_connections'], isset( $post->ID ) ? $post->ID : 0 );
283
284 if ( isset( $post->ID ) ) {
285 // Set the meta before we mark the post as published so that publicize works as expected.
286 // If this is not the case post end up on social media when they are marked as skipped.
287 $this->update( $request['jetpack_publicize_connections'], $post, $request );
288 }
289
290 return $post;
291 }
292
293 /**
294 * After creating a new post, update our cached data to reflect
295 * the new post ID.
296 *
297 * @param WP_Post $post Post data to update.
298 * @param WP_REST_Request $request API request.
299 * @param bool $is_new Is this a new post.
300 */
301 public function rest_insert( $post, $request, $is_new ) {
302 if ( ! $is_new ) {
303 // An existing post was edited - no need to update
304 // our cache - we started out knowing the correct
305 // post ID.
306 return;
307 }
308
309 if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
310 return;
311 }
312
313 if ( ! isset( $this->memoized_updates[0] ) ) {
314 return;
315 }
316
317 $this->memoized_updates[ $post->ID ] = $this->memoized_updates[0];
318 unset( $this->memoized_updates[0] );
319 }
320
321 /**
322 * Get list of meta data to update per post ID.
323 *
324 * @param array $requested_connections Publicize connections to update.
325 * Items are either `{ id: (string) }` or `{ service_name: (string) }`.
326 * @param int $post_id Post ID.
327 */
328 protected function get_meta_to_update( $requested_connections, $post_id = 0 ) {
329 global $publicize;
330
331 if ( ! $publicize || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
332 return array();
333 }
334
335 $post = get_post( $post_id );
336 if ( isset( $post->post_status ) && 'publish' === $post->post_status ) {
337 return array();
338 }
339
340 if ( isset( $this->memoized_updates[ $post_id ] ) ) {
341 return $this->memoized_updates[ $post_id ];
342 }
343
344 $available_connections = $publicize->get_filtered_connection_data( $post_id );
345
346 $changed_connections = array();
347
348 // Build lookup mappings.
349 $available_connections_by_connection_id = array();
350 $available_connections_by_service_name = array();
351 foreach ( $available_connections as $available_connection ) {
352 $available_connections_by_connection_id[ $available_connection['id'] ] = $available_connection;
353
354 if ( ! isset( $available_connections_by_service_name[ $available_connection['service_name'] ] ) ) {
355 $available_connections_by_service_name[ $available_connection['service_name'] ] = array();
356 }
357 $available_connections_by_service_name[ $available_connection['service_name'] ][] = $available_connection;
358 }
359
360 // Handle { service_name: $service_name, enabled: (bool) }.
361 // If the service is not available, it will be skipped.
362 foreach ( $requested_connections as $requested_connection ) {
363 if ( ! isset( $requested_connection['service_name'] ) ) {
364 continue;
365 }
366
367 if ( ! isset( $available_connections_by_service_name[ $requested_connection['service_name'] ] ) ) {
368 continue;
369 }
370
371 foreach ( $available_connections_by_service_name[ $requested_connection['service_name'] ] as $available_connection ) {
372 if ( $requested_connection['connection_id'] === $available_connection['id'] ) {
373 $changed_connections[ $available_connection['id'] ] = $requested_connection['enabled'];
374 break;
375 }
376 }
377 }
378
379 // Handle { id: $id, enabled: (bool) }
380 // These override the service_name settings.
381 foreach ( $requested_connections as $requested_connection ) {
382 if ( ! isset( $requested_connection['connection_id'] ) ) {
383 continue;
384 }
385
386 if ( ! isset( $available_connections_by_connection_id[ $requested_connection['connection_id'] ] ) ) {
387 continue;
388 }
389
390 $changed_connections[ $requested_connection['connection_id'] ] = $requested_connection['enabled'];
391 }
392
393 // Set all changed connections to their new value.
394 foreach ( $changed_connections as $id => $enabled ) {
395 $connection = $available_connections_by_connection_id[ $id ];
396
397 if ( $connection['done'] || ! $connection['toggleable'] ) {
398 continue;
399 }
400
401 $available_connections_by_connection_id[ $id ]['enabled'] = $enabled;
402 }
403
404 $meta_to_update = array();
405 // For all connections, ensure correct post_meta.
406 foreach ( $available_connections_by_connection_id as $connection_id => $available_connection ) {
407 if ( $available_connection['enabled'] ) {
408 $meta_to_update[ $publicize->POST_SKIP_PUBLICIZE . $connection_id ] = null; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
409 } else {
410 $meta_to_update[ $publicize->POST_SKIP_PUBLICIZE . $connection_id ] = 1; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
411 }
412 }
413
414 $this->memoized_updates[ $post_id ] = $meta_to_update;
415
416 return $meta_to_update;
417 }
418
419 /**
420 * Update the connections slated to be shared to.
421 *
422 * @param array $requested_connections Publicize conenctions to update.
423 * Items are either `{ id: (string) }` or `{ service_name: (string) }`.
424 * @param WP_Post $post Post data.
425 * @param WP_REST_Request $request API request.
426 */
427 public function update( $requested_connections, $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
428 if ( isset( $this->meta_saved[ $post->ID ] ) ) { // Make sure we only save it once - per request.
429 return;
430 }
431 foreach ( $this->get_meta_to_update( $requested_connections, $post->ID ) as $meta_key => $meta_value ) {
432 if ( $meta_value === null ) {
433 delete_post_meta( $post->ID, $meta_key );
434 } else {
435 update_post_meta( $post->ID, $meta_key, $meta_value );
436 }
437 }
438 $this->meta_saved[ $post->ID ] = true;
439 }
440 }
441
442 if ( Jetpack::is_module_active( 'publicize' ) ) {
443 wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Post_Publicize_Connections_Field' );
444 }
445