PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.1.5
Jetpack – WP Security, Backup, Speed, & Growth v13.1.5
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 3 years ago
post-fields-publicize-connections.php
436 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 ),
158 );
159 }
160
161 /**
162 * Permission check, based on module availability and user capabilities.
163 *
164 * @param int $post_id Post ID.
165 *
166 * @return true|WP_Error
167 */
168 public function permission_check( $post_id ) {
169 global $publicize;
170
171 if ( ! $publicize ) {
172 return new WP_Error(
173 'publicize_not_available',
174 __( 'Sorry, Jetpack Social is not available on your site right now.', 'jetpack' ),
175 array( 'status' => rest_authorization_required_code() )
176 );
177 }
178
179 if ( $publicize->current_user_can_access_publicize_data( $post_id ) ) {
180 return true;
181 }
182
183 return new WP_Error(
184 'invalid_user_permission_publicize',
185 __( 'Sorry, you are not allowed to access Jetpack Social data for this post.', 'jetpack' ),
186 array( 'status' => rest_authorization_required_code() )
187 );
188 }
189
190 /**
191 * Getter permission check
192 *
193 * @param mixed $post_array Response from the post endpoint.
194 * @param WP_REST_Request $request API request.
195 *
196 * @return true|WP_Error
197 */
198 public function get_permission_check( $post_array, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
199 return $this->permission_check( isset( $post_array['id'] ) ? $post_array['id'] : 0 );
200 }
201
202 /**
203 * Setter permission check.
204 *
205 * @param mixed $value The new value for the field.
206 * @param WP_Post $post The post object.
207 * @param WP_REST_Request $request API request.
208 *
209 * @return true|WP_Error
210 */
211 public function update_permission_check( $value, $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
212 return $this->permission_check( isset( $post->ID ) ? $post->ID : 0 );
213 }
214
215 /**
216 * Getter: Retrieve current list of connected social accounts for a given post.
217 *
218 * @see Publicize::get_filtered_connection_data()
219 *
220 * @param array $post_array Response from Post Endpoint.
221 * @param WP_REST_Request $request API request.
222 *
223 * @return array List of connections
224 */
225 public function get( $post_array, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
226 global $publicize;
227
228 if ( ! $publicize ) {
229 return array();
230 }
231
232 $schema = $this->post_connection_schema();
233 $properties = array_keys( $schema['properties'] );
234
235 $connections = $publicize->get_filtered_connection_data( $post_array['id'] );
236
237 $output_connections = array();
238 foreach ( $connections as $connection ) {
239 $output_connection = array();
240 foreach ( $properties as $property ) {
241 if ( isset( $connection[ $property ] ) ) {
242 $output_connection[ $property ] = $connection[ $property ];
243 }
244 }
245 $output_connection['id'] = (string) $connection['unique_id'];
246 $output_connection['connection_id'] = (string) $connection['id'];
247
248 $output_connections[] = $output_connection;
249 }
250
251 return $output_connections;
252 }
253
254 /**
255 * Prior to updating the post, first calculate which Services to
256 * Publicize to and which to skip.
257 *
258 * @param object $post Post data to insert/update.
259 * @param WP_REST_Request $request API request.
260 *
261 * @return Filtered $post
262 */
263 public function rest_pre_insert( $post, $request ) {
264 if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
265 return $post;
266 }
267
268 $permission_check = $this->update_permission_check( $request['jetpack_publicize_connections'], $post, $request );
269 if ( is_wp_error( $permission_check ) ) {
270 return $permission_check;
271 }
272 // memoize.
273 $this->get_meta_to_update( $request['jetpack_publicize_connections'], isset( $post->ID ) ? $post->ID : 0 );
274
275 if ( isset( $post->ID ) ) {
276 // Set the meta before we mark the post as published so that publicize works as expected.
277 // If this is not the case post end up on social media when they are marked as skipped.
278 $this->update( $request['jetpack_publicize_connections'], $post, $request );
279 }
280
281 return $post;
282 }
283
284 /**
285 * After creating a new post, update our cached data to reflect
286 * the new post ID.
287 *
288 * @param WP_Post $post Post data to update.
289 * @param WP_REST_Request $request API request.
290 * @param bool $is_new Is this a new post.
291 */
292 public function rest_insert( $post, $request, $is_new ) {
293 if ( ! $is_new ) {
294 // An existing post was edited - no need to update
295 // our cache - we started out knowing the correct
296 // post ID.
297 return;
298 }
299
300 if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
301 return;
302 }
303
304 if ( ! isset( $this->memoized_updates[0] ) ) {
305 return;
306 }
307
308 $this->memoized_updates[ $post->ID ] = $this->memoized_updates[0];
309 unset( $this->memoized_updates[0] );
310 }
311
312 /**
313 * Get list of meta data to update per post ID.
314 *
315 * @param array $requested_connections Publicize connections to update.
316 * Items are either `{ id: (string) }` or `{ service_name: (string) }`.
317 * @param int $post_id Post ID.
318 */
319 protected function get_meta_to_update( $requested_connections, $post_id = 0 ) {
320 global $publicize;
321
322 if ( ! $publicize || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
323 return array();
324 }
325
326 $post = get_post( $post_id );
327 if ( isset( $post->post_status ) && 'publish' === $post->post_status ) {
328 return array();
329 }
330
331 if ( isset( $this->memoized_updates[ $post_id ] ) ) {
332 return $this->memoized_updates[ $post_id ];
333 }
334
335 $available_connections = $publicize->get_filtered_connection_data( $post_id );
336
337 $changed_connections = array();
338
339 // Build lookup mappings.
340 $available_connections_by_connection_id = array();
341 $available_connections_by_service_name = array();
342 foreach ( $available_connections as $available_connection ) {
343 $available_connections_by_connection_id[ $available_connection['id'] ] = $available_connection;
344
345 if ( ! isset( $available_connections_by_service_name[ $available_connection['service_name'] ] ) ) {
346 $available_connections_by_service_name[ $available_connection['service_name'] ] = array();
347 }
348 $available_connections_by_service_name[ $available_connection['service_name'] ][] = $available_connection;
349 }
350
351 // Handle { service_name: $service_name, enabled: (bool) }.
352 // If the service is not available, it will be skipped.
353 foreach ( $requested_connections as $requested_connection ) {
354 if ( ! isset( $requested_connection['service_name'] ) ) {
355 continue;
356 }
357
358 if ( ! isset( $available_connections_by_service_name[ $requested_connection['service_name'] ] ) ) {
359 continue;
360 }
361
362 foreach ( $available_connections_by_service_name[ $requested_connection['service_name'] ] as $available_connection ) {
363 if ( $requested_connection['connection_id'] === $available_connection['id'] ) {
364 $changed_connections[ $available_connection['id'] ] = $requested_connection['enabled'];
365 break;
366 }
367 }
368 }
369
370 // Handle { id: $id, enabled: (bool) }
371 // These override the service_name settings.
372 foreach ( $requested_connections as $requested_connection ) {
373 if ( ! isset( $requested_connection['connection_id'] ) ) {
374 continue;
375 }
376
377 if ( ! isset( $available_connections_by_connection_id[ $requested_connection['connection_id'] ] ) ) {
378 continue;
379 }
380
381 $changed_connections[ $requested_connection['connection_id'] ] = $requested_connection['enabled'];
382 }
383
384 // Set all changed connections to their new value.
385 foreach ( $changed_connections as $id => $enabled ) {
386 $connection = $available_connections_by_connection_id[ $id ];
387
388 if ( $connection['done'] || ! $connection['toggleable'] ) {
389 continue;
390 }
391
392 $available_connections_by_connection_id[ $id ]['enabled'] = $enabled;
393 }
394
395 $meta_to_update = array();
396 // For all connections, ensure correct post_meta.
397 foreach ( $available_connections_by_connection_id as $connection_id => $available_connection ) {
398 if ( $available_connection['enabled'] ) {
399 $meta_to_update[ $publicize->POST_SKIP_PUBLICIZE . $connection_id ] = null; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
400 } else {
401 $meta_to_update[ $publicize->POST_SKIP_PUBLICIZE . $connection_id ] = 1; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
402 }
403 }
404
405 $this->memoized_updates[ $post_id ] = $meta_to_update;
406
407 return $meta_to_update;
408 }
409
410 /**
411 * Update the connections slated to be shared to.
412 *
413 * @param array $requested_connections Publicize conenctions to update.
414 * Items are either `{ id: (string) }` or `{ service_name: (string) }`.
415 * @param WP_Post $post Post data.
416 * @param WP_REST_Request $request API request.
417 */
418 public function update( $requested_connections, $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
419 if ( isset( $this->meta_saved[ $post->ID ] ) ) { // Make sure we only save it once - per request.
420 return;
421 }
422 foreach ( $this->get_meta_to_update( $requested_connections, $post->ID ) as $meta_key => $meta_value ) {
423 if ( $meta_value === null ) {
424 delete_post_meta( $post->ID, $meta_key );
425 } else {
426 update_post_meta( $post->ID, $meta_key, $meta_value );
427 }
428 }
429 $this->meta_saved[ $post->ID ] = true;
430 }
431 }
432
433 if ( Jetpack::is_module_active( 'publicize' ) ) {
434 wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Post_Publicize_Connections_Field' );
435 }
436