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