summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/_inc/lib/core-api')
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php330
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php10
-rw-r--r--plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php75
-rw-r--r--plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php40
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php22
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php117
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php186
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php159
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php37
-rw-r--r--plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php337
10 files changed, 1311 insertions, 2 deletions
diff --git a/plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php b/plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php
new file mode 100644
index 00000000..e599b275
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/class-wpcom-rest-field-controller.php
@@ -0,0 +1,330 @@
+<?php
+
+// @todo - nicer API for array values?
+
+/**
+ * `WP_REST_Controller` is basically a wrapper for `register_rest_route()`
+ * `WPCOM_REST_API_V2_Field_Controller` is a mostly-analogous wrapper for `register_rest_field()`
+ */
+abstract class WPCOM_REST_API_V2_Field_Controller {
+ /**
+ * @var string|string[] $object_type The REST Object Type(s) to which the field should be added.
+ */
+ protected $object_type;
+
+ /**
+ * @var string $field_name The name of the REST API field to add.
+ */
+ protected $field_name;
+
+ public function __construct() {
+ if ( ! $this->object_type ) {
+ /* translators: %s: object_type */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::$object_type', sprintf( __( "Property '%s' must be overridden.", 'jetpack' ), 'object_type' ), 'Jetpack 6.8' );
+ return;
+ }
+
+ if ( ! $this->field_name ) {
+ /* translators: %s: field_name */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::$field_name', sprintf( __( "Property '%s' must be overridden.", 'jetpack' ), 'field_name' ), 'Jetpack 6.8' );
+ return;
+ }
+
+ add_action( 'rest_api_init', array( $this, 'register_fields' ) );
+ }
+
+ /**
+ * Registers the field with the appropriate schema and callbacks.
+ */
+ public function register_fields() {
+ foreach ( (array) $this->object_type as $object_type ) {
+ register_rest_field(
+ $object_type,
+ $this->field_name,
+ array(
+ 'get_callback' => array( $this, 'get_for_response' ),
+ 'update_callback' => array( $this, 'update_from_request' ),
+ 'schema' => $this->get_schema(),
+ )
+ );
+ }
+ }
+
+ /**
+ * Ensures the response matches the schema and request context.
+ *
+ * @param mixed $value
+ * @param WP_REST_Request $request
+ * @return mixed
+ */
+ private function prepare_for_response( $value, $request ) {
+ $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
+ $schema = $this->get_schema();
+
+ $is_valid = rest_validate_value_from_schema( $value, $schema, $this->field_name );
+ if ( is_wp_error( $is_valid ) ) {
+ return $is_valid;
+ }
+
+ return $this->filter_response_by_context( $value, $schema, $context );
+ }
+
+ /**
+ * Returns the schema's default value
+ *
+ * If there is no default, returns the type's falsey value.
+ *
+ * @param array $schema
+ * @return mixed
+ */
+ final public function get_default_value( $schema ) {
+ if ( isset( $schema['default'] ) ) {
+ return $schema['default'];
+ }
+
+ // If you have something more complicated, use $schema['default'];
+ switch ( isset( $schema['type'] ) ? $schema['type'] : 'null' ) {
+ case 'string':
+ return '';
+ case 'integer':
+ case 'number':
+ return 0;
+ case 'object':
+ return (object) array();
+ case 'array':
+ return array();
+ case 'boolean':
+ return false;
+ case 'null':
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * The field's wrapped getter. Does permission checks and output preparation.
+ *
+ * This cannot be extended: implement `->get()` instead.
+ *
+ * @param mixed $object_data Probably an array. Whatever the endpoint returns.
+ * @param string $field_name Should always match `->field_name`
+ * @param WP_REST_Request $request
+ * @param string $object_type Should always match `->object_type`
+ * @return mixed
+ */
+ final public function get_for_response( $object_data, $field_name, $request, $object_type ) {
+ $permission_check = $this->get_permission_check( $object_data, $request );
+
+ if ( ! $permission_check ) {
+ /* translators: %s: get_permission_check() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get_permission_check', sprintf( __( "Method '%s' must return either true or WP_Error.", 'jetpack' ), 'get_permission_check' ), 'Jetpack 6.8' );
+ return $this->get_default_value( $this->get_schema() );
+ }
+
+ if ( is_wp_error( $permission_check ) ) {
+ return $this->get_default_value( $this->get_schema() );
+ }
+
+ $value = $this->get( $object_data, $request );
+
+ return $this->prepare_for_response( $value, $request );
+ }
+
+ /**
+ * The field's wrapped setter. Does permission checks.
+ *
+ * This cannot be extended: implement `->update()` instead.
+ *
+ * @param mixed $value The new value for the field.
+ * @param mixed $object_data Probably a WordPress object (e.g., WP_Post)
+ * @param string $field_name Should always match `->field_name`
+ * @param WP_REST_Request $request
+ * @param string $object_type Should always match `->object_type`
+ * @return void|WP_Error
+ */
+ final public function update_from_request( $value, $object_data, $field_name, $request, $object_type ) {
+ $permission_check = $this->update_permission_check( $value, $object_data, $request );
+
+ if ( ! $permission_check ) {
+ /* translators: %s: update_permission_check() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::update_permission_check', sprintf( __( "Method '%s' must return either true or WP_Error.", 'jetpack' ), 'update_permission_check' ), 'Jetpack 6.8' );
+ /* translators: %s: the name of an API response field */
+ return new WP_Error( 'invalid_user_permission', sprintf( __( "You are not allowed to access the '%s' field.", 'jetpack' ), $this->field_name ) );
+ }
+
+ if ( is_wp_error( $permission_check ) ) {
+ return $permission_check;
+ }
+
+ $updated = $this->update( $value, $object_data, $request );
+
+ if ( is_wp_error( $updated ) ) {
+ return $updated;
+ }
+ }
+
+ /**
+ * Permission Check for the field's getter. Must be implemented in the inheriting class.
+ *
+ * @param mixed $object_data Whatever the endpoint would return for its response.
+ * @param WP_REST_Request $request
+ * @return true|WP_Error
+ */
+ public function get_permission_check( $object_data, $request ) {
+ /* translators: %s: get_permission_check() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get_permission_check', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
+ }
+
+ /**
+ * The field's "raw" getter. Must be implemented in the inheriting class.
+ *
+ * @param mixed $object_data Whatever the endpoint would return for its response.
+ * @param WP_REST_Request $request
+ * @return mixed
+ */
+ public function get( $object_data, $request ) {
+ /* translators: %s: get() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
+ }
+
+ /**
+ * Permission Check for the field's setter. Must be implemented in the inheriting class.
+ *
+ * @param mixed $value The new value for the field.
+ * @param mixed $object_data Probably a WordPress object (e.g., WP_Post)
+ * @param WP_REST_Request $request
+ * @return true|WP_Error
+ */
+ public function update_permission_check( $value, $object_data, $request ) {
+ /* translators: %s: update_permission_check() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::update_permission_check', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
+ }
+
+ /**
+ * The field's "raw" setter. Must be implemented in the inheriting class.
+ *
+ * @param mixed $value The new value for the field.
+ * @param mixed $object_data Probably a WordPress object (e.g., WP_Post)
+ * @param WP_REST_Request $request
+ * @return mixed
+ */
+ public function update( $value, $object_data, $request ) {
+ /* translators: %s: update() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::update', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
+ }
+
+ /**
+ * The JSON Schema for the field
+ *
+ * @link https://json-schema.org/understanding-json-schema/
+ * As of WordPress 5.0, Core currently understands:
+ * * type
+ * * string - not minLength, not maxLength, not pattern
+ * * integer - minimum, maximum, exclusiveMinimum, exclusiveMaximum, not multipleOf
+ * * number - minimum, maximum, exclusiveMinimum, exclusiveMaximum, not multipleOf
+ * * boolean
+ * * null
+ * * object - properties, additionalProperties, not propertyNames, not dependencies, not patternProperties, not required
+ * * array: only lists, not tuples - items, not minItems, not maxItems, not uniqueItems, not contains
+ * * enum
+ * * format
+ * * date-time
+ * * email
+ * * ip
+ * * uri
+ * As of WordPress 5.0, Core does not support:
+ * * Multiple type: `type: [ 'string', 'integer' ]`
+ * * $ref, allOf, anyOf, oneOf, not, const
+ *
+ * @return array
+ */
+ public function get_schema() {
+ /* translators: %s: get_schema() */
+ _doing_it_wrong( 'WPCOM_REST_API_V2_Field_Controller::get_schema', sprintf( __( "Method '%s' must be overridden.", 'jetpack' ), __METHOD__ ), 'Jetpack 6.8' );
+ }
+
+ /**
+ * @param array $schema
+ * @param string $context REST API Request context
+ * @return bool
+ */
+ private function is_valid_for_context( $schema, $context ) {
+ return empty( $schema['context'] ) || in_array( $context, $schema['context'], true );
+ }
+
+ /**
+ * Removes properties that should not appear in the current
+ * request's context
+ *
+ * $context is a Core REST API Framework request attribute that is
+ * always one of:
+ * * view (what you see on the blog)
+ * * edit (what you see in an editor)
+ * * embed (what you see in, e.g., an oembed)
+ *
+ * Fields (and sub-fields, and sub-sub-...) can be flagged for a
+ * set of specific contexts via the field's schema.
+ *
+ * The Core API will filter out top-level fields with the wrong
+ * context, but will not recurse deeply enough into arrays/objects
+ * to remove all levels of sub-fields with the wrong context.
+ *
+ * This function handles that recursion.
+ *
+ * @param mixed $value
+ * @param array $schema
+ * @param string $context REST API Request context
+ * @return mixed Filtered $value
+ */
+ final public function filter_response_by_context( $value, $schema, $context ) {
+ if ( ! $this->is_valid_for_context( $schema, $context ) ) {
+ // We use this intentionally odd looking WP_Error object
+ // internally only in this recursive function (see below
+ // in the `object` case). It will never be output by the REST API.
+ // If we return this for the top level object, Core
+ // correctly remove the top level object from the response
+ // for us.
+ return new WP_Error( '__wrong-context__' );
+ }
+
+ switch ( $schema['type'] ) {
+ case 'array':
+ if ( ! isset( $schema['items'] ) ) {
+ return $value;
+ }
+
+ // Shortcircuit if we know none of the items are valid for this context.
+ // This would only happen in a strangely written schema.
+ if ( ! $this->is_valid_for_context( $schema['items'], $context ) ) {
+ return array();
+ }
+
+ // Recurse to prune sub-properties of each item.
+ foreach ( $value as $key => $item ) {
+ $value[ $key ] = $this->filter_response_by_context( $item, $schema['items'], $context );
+ }
+
+ return $value;
+ case 'object':
+ if ( ! isset( $schema['properties'] ) ) {
+ return $value;
+ }
+
+ foreach ( $value as $field_name => $field_value ) {
+ if ( isset( $schema['properties'][ $field_name ] ) ) {
+ $field_value = $this->filter_response_by_context( $field_value, $schema['properties'][ $field_name ], $context );
+ if ( is_wp_error( $field_value ) && '__wrong-context__' === $field_value->get_error_code() ) {
+ unset( $value[ $field_name ] );
+ } else {
+ // Respect recursion that pruned sub-properties of each property.
+ $value[ $field_name ] = $field_value;
+ }
+ }
+ }
+
+ return (object) $value;
+ }
+
+ return $value;
+ }
+}
diff --git a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php
index ac912269..45d10d14 100644
--- a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php
+++ b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php
@@ -746,8 +746,14 @@ class Jetpack_Core_API_Data extends Jetpack_Core_API_XMLRPC_Consumer_Endpoint {
case 'bing':
case 'pinterest':
case 'yandex':
- $grouped_options = $grouped_options_current = (array) get_option( 'verification_services_codes' );
- $grouped_options[$option] = $value;
+ $grouped_options = $grouped_options_current = (array) get_option( 'verification_services_codes' );
+
+ // Extracts the content attribute from the HTML meta tag if needed
+ if ( preg_match( '#.*<meta name="(?:[^"]+)" content="([^"]+)" />.*#i', $value, $matches ) ) {
+ $grouped_options[ $option ] = $matches[1];
+ } else {
+ $grouped_options[ $option ] = $value;
+ }
// If option value was the same, consider it done.
$updated = $grouped_options_current != $grouped_options ? update_option( 'verification_services_codes', $grouped_options ) : true;
diff --git a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php
index 68327f51..c8fba69c 100644
--- a/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php
+++ b/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-site-endpoints.php
@@ -48,6 +48,69 @@ class Jetpack_Core_API_Site_Endpoint {
}
/**
+ * Returns the result of `/sites/%s/posts/%d/related` endpoint call.
+ * Results are not cached and are retrieved in real time.
+ *
+ * @since 6.7.0
+ *
+ * @param int ID of the post to get related posts of
+ *
+ * @return array
+ */
+ public static function get_related_posts( $api_request ) {
+ $params = $api_request->get_params();
+ $post_id = ! empty( $params['post_id'] ) ? absint( $params['post_id'] ) : 0;
+
+ if ( ! $post_id ) {
+ return new WP_Error(
+ 'incorrect_post_id',
+ esc_html__( 'You need to specify a correct ID of a post to return related posts for.', 'jetpack' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ // Make the API request
+ $request = sprintf( '/sites/%d/posts/%d/related', Jetpack_Options::get_option( 'id' ), $post_id );
+ $request_args = array(
+ 'headers' => array(
+ 'Content-Type' => 'application/json',
+ ),
+ 'timeout' => 10,
+ 'method' => 'POST',
+ );
+ $response = Jetpack_Client::wpcom_json_api_request_as_blog( $request, '1.1', $request_args );
+
+ // Bail if there was an error or malformed response
+ if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
+ return new WP_Error(
+ 'failed_to_fetch_data',
+ esc_html__( 'Unable to fetch the requested data.', 'jetpack' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ // Decode the results
+ $results = json_decode( wp_remote_retrieve_body( $response ), true );
+
+ $related_posts = array();
+ if ( isset( $results['hits'] ) && is_array( $results['hits'] ) ) {
+ $related_posts_ids = array_map( array( 'Jetpack_Core_API_Site_Endpoint', 'get_related_post_id' ), $results['hits'] );
+
+ $related_posts_instance = Jetpack_RelatedPosts::init();
+ foreach ( $related_posts_ids as $related_post_id ) {
+ $related_posts[] = $related_posts_instance->get_related_post_data_for_post( $related_post_id, 0, 0 );
+ }
+ }
+
+ return rest_ensure_response( array(
+ 'code' => 'success',
+ 'message' => esc_html__( 'Related posts retrieved successfully.', 'jetpack' ),
+ 'posts' => $related_posts,
+ )
+ );
+ }
+
+ /**
* Check that the current user has permissions to request information about this site.
*
* @since 5.1.0
@@ -57,4 +120,16 @@ class Jetpack_Core_API_Site_Endpoint {
public static function can_request() {
return current_user_can( 'jetpack_manage_modules' );
}
+
+ /**
+ * Returns the post ID out of a related post entry from the
+ * `/sites/%s/posts/%d/related` WP.com endpoint.
+ *
+ * @since 6.7.0
+ *
+ * @return int
+ */
+ public static function get_related_post_id( $item ) {
+ return $item['fields']['post_id'];
+ }
}
diff --git a/plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php b/plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php
new file mode 100644
index 00000000..2b26f78c
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/load-wpcom-endpoints.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * Loader for WP REST API endpoints that are synced with WP.com.
+ *
+ * On WP.com see:
+ * - wp-content/mu-plugins/rest-api.php
+ * - wp-content/rest-api-plugins/jetpack-endpoints/
+ */
+
+function wpcom_rest_api_v2_load_plugin_files( $file_pattern ) {
+ $plugins = glob( dirname( __FILE__ ) . '/' . $file_pattern );
+
+ if ( ! is_array( $plugins ) ) {
+ return;
+ }
+
+ foreach ( array_filter( $plugins, 'is_file' ) as $plugin ) {
+ require_once $plugin;
+ }
+}
+
+// API v2 plugins: define a class, then call this function.
+function wpcom_rest_api_v2_load_plugin( $class_name ) {
+ global $wpcom_rest_api_v2_plugins;
+
+ if ( ! isset( $wpcom_rest_api_v2_plugins ) ) {
+ $_GLOBALS['wpcom_rest_api_v2_plugins'] = $wpcom_rest_api_v2_plugins = array();
+ }
+
+ if ( ! isset( $wpcom_rest_api_v2_plugins[ $class_name ] ) ) {
+ $wpcom_rest_api_v2_plugins[ $class_name ] = new $class_name;
+ }
+}
+
+require dirname( __FILE__ ) . '/class-wpcom-rest-field-controller.php';
+
+// Now load the endpoint files.
+wpcom_rest_api_v2_load_plugin_files( 'wpcom-endpoints/*.php' );
+wpcom_rest_api_v2_load_plugin_files( 'wpcom-fields/*.php' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php
new file mode 100644
index 00000000..a05769b2
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/hello.php
@@ -0,0 +1,22 @@
+<?php
+
+class WPCOM_REST_API_V2_Endpoint_Hello {
+ public function __construct() {
+ add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+ }
+
+ public function register_routes() {
+ register_rest_route( 'wpcom/v2', '/hello', array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_data' ),
+ ),
+ ) );
+ }
+
+ public function get_data( $request ) {
+ return array( 'hello' => 'world' );
+ }
+}
+
+wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Hello' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php
new file mode 100644
index 00000000..86019880
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connection-test-results.php
@@ -0,0 +1,117 @@
+<?php
+
+require_once dirname( __FILE__ ) . '/publicize-connections.php';
+
+/**
+ * Publicize: List Connection Test Result Data
+ *
+ * All the same data as the Publicize Connections Endpoint, plus test results.
+ *
+ * @since 6.8
+ */
+class WPCOM_REST_API_V2_Endpoint_List_Publicize_Connection_Test_Results extends WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections {
+ public function __construct() {
+ $this->namespace = 'wpcom/v2';
+ $this->rest_base = 'publicize/connection-test-results';
+
+ add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+ }
+
+ /**
+ * Called automatically on `rest_api_init()`.
+ */
+ public function register_routes() {
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_items' ),
+ 'permission_callback' => array( $this, 'get_items_permission_check' ),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+ }
+
+ /**
+ * Adds the test results properties to the Connection schema.
+ *
+ * @return array
+ */
+ public function get_item_schema() {
+ $schema = array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'jetpack-publicize-connection-test-results',
+ 'type' => 'object',
+ 'properties' => $this->get_connection_schema_properties() + array(
+ 'test_success' => array(
+ 'description' => __( 'Did the Publicize Connection test pass?', 'jetpack' ),
+ 'type' => 'boolean',
+ ),
+ 'test_message' => array(
+ 'description' => __( 'Publicize Connection success or error message', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'can_refresh' => array(
+ 'description' => __( 'Can the current user refresh the Publicize Connection?', 'jetpack' ),
+ 'type' => 'boolean',
+ ),
+ 'refresh_text' => array(
+ 'description' => __( 'Message instructing the user to refresh their Connection to the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'refresh_url' => array(
+ 'description' => __( 'URL for refreshing the Connection to the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ 'format' => 'uri',
+ ),
+ ),
+ );
+
+ return $this->add_additional_fields_schema( $schema );
+ }
+
+ /**
+ * @param WP_REST_Request
+ * @see Publicize::get_publicize_conns_test_results()
+ * @return WP_REST_Response suitable for 1-page collection
+ */
+ public function get_items( $request ) {
+ global $publicize;
+
+ $items = $this->get_connections();
+
+ $test_results = $publicize->get_publicize_conns_test_results();
+ $test_results_by_unique_id = array();
+ foreach ( $test_results as $test_result ) {
+ $test_results_by_unique_id[ $test_result['unique_id'] ] = $test_result;
+ }
+
+ $mapping = array(
+ 'test_success' => 'connectionTestPassed',
+ 'test_message' => 'connectionTestMessage',
+ 'can_refresh' => 'userCanRefresh',
+ 'refresh_text' => 'refreshText',
+ 'refresh_url' => 'refreshURL',
+ );
+
+ foreach ( $items as &$item ) {
+ $test_result = $test_results_by_unique_id[ $item['id'] ];
+
+ foreach ( $mapping as $field => $test_result_field ) {
+ $item[ $field ] = $test_result[ $test_result_field ];
+ }
+ }
+
+ $response = rest_ensure_response( $items );
+
+ $response->header( 'X-WP-Total', count( $items ) );
+ $response->header( 'X-WP-TotalPages', 1 );
+
+ return $response;
+ }
+}
+
+wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_List_Publicize_Connection_Test_Results' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php
new file mode 100644
index 00000000..f7e9b351
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-connections.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * Publicize: List Connections
+ *
+ * [
+ * { # Connnection Object. See schema for more detail.
+ * id: (string) Connection unique_id
+ * service_name: (string) Service slug
+ * display_name: (string) User name/display name of user/connection on Service
+ * global: (boolean) Is the Connection available to all users of the site?
+ * },
+ * ...
+ * ]
+ *
+ * @since 6.8
+ */
+class WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections extends WP_REST_Controller {
+ /**
+ * Flag to help WordPress.com decide where it should look for
+ * Publicize data. Ignored for direct requests to Jetpack sites.
+ *
+ * @var bool $wpcom_is_wpcom_only_endpoint
+ */
+ public $wpcom_is_wpcom_only_endpoint = true;
+
+ public function __construct() {
+ $this->namespace = 'wpcom/v2';
+ $this->rest_base = 'publicize/connections';
+
+ add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+ }
+
+ /**
+ * Called automatically on `rest_api_init()`.
+ */
+ public function register_routes() {
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_items' ),
+ 'permission_callback' => array( $this, 'get_items_permission_check' ),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+ }
+
+ /**
+ * Helper for generating schema. Used by this endpoint and by the
+ * Connection Test Result endpoint.
+ *
+ * @internal
+ * @return array
+ */
+ protected function get_connection_schema_properties() {
+ return array(
+ 'id' => array(
+ 'description' => __( 'Unique identifier for the Publicize Connection', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'service_name' => array(
+ 'description' => __( 'Alphanumeric identifier for the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'display_name' => array(
+ 'description' => __( 'Username of the connected account', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'global' => array(
+ 'description' => __( 'Is this connection available to all users?', 'jetpack' ),
+ 'type' => 'boolean',
+ ),
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function get_item_schema() {
+ $schema = array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'jetpack-publicize-connection',
+ 'type' => 'object',
+ 'properties' => $this->get_connection_schema_properties(),
+ );
+
+ return $this->add_additional_fields_schema( $schema );
+ }
+
+ /**
+ * Helper for retrieving Connections. Used by this endpoint and by
+ * the Connection Test Result endpoint.
+ *
+ * @internal
+ * @return array
+ */
+ protected function get_connections() {
+ global $publicize;
+
+ $items = array();
+
+ foreach ( (array) $publicize->get_services( 'connected' ) as $service_name => $connections ) {
+ foreach ( $connections as $connection ) {
+ $connection_meta = $publicize->get_connection_meta( $connection );
+ $connection_data = $connection_meta['connection_data'];
+
+ $items[] = array(
+ 'id' => (string) $publicize->get_connection_unique_id( $connection ),
+ 'service_name' => $service_name,
+ 'display_name' => $publicize->get_display_name( $service_name, $connection ),
+ // We expect an integer, but do loose comparison below in case some other type is stored
+ 'global' => 0 == $connection_data['user_id'],
+ );
+ }
+ }
+
+ return $items;
+ }
+
+ /**
+ * @param WP_REST_Request $request
+ * @return WP_REST_Response suitable for 1-page collection
+ */
+ public function get_items( $request ) {
+ $items = array();
+
+ foreach ( $this->get_connections() as $item ) {
+ $items[] = $this->prepare_item_for_response( $item, $request );
+ }
+
+ $response = rest_ensure_response( $items );
+ $response->header( 'X-WP-Total', count( $items ) );
+ $response->header( 'X-WP-TotalPages', 1 );
+
+ return $response;
+ }
+
+ /**
+ * Filters out data based on ?_fields= request parameter
+ *
+ * @param array $connection
+ * @param WP_REST_Request $request
+ * @return array filtered $connection
+ */
+ public function prepare_item_for_response( $connection, $request ) {
+ if ( ! is_callable( array( $this, 'get_fields_for_response' ) ) ) {
+ return $connection;
+ }
+
+ $fields = $this->get_fields_for_response( $request );
+
+ $response_data = array();
+ foreach ( $connection as $field => $value ) {
+ if ( in_array( $field, $fields, true ) ) {
+ $response_data[ $field ] = $value;
+ }
+ }
+
+ return $response_data;
+ }
+
+ /**
+ * Verify that user can access Publicize data
+ *
+ * @return true|WP_Error
+ */
+ public function get_items_permission_check() {
+ global $publicize;
+
+ if ( $publicize->current_user_can_access_publicize_data() ) {
+ return true;
+ }
+
+ return new WP_Error(
+ 'invalid_user_permission_publicize',
+ __( 'Sorry, you are not allowed to access Publicize data on this site.', 'jetpack' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
+ }
+}
+
+wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_List_Publicize_Connections' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php
new file mode 100644
index 00000000..fb418263
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/publicize-services.php
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * Publicize: List Publicize Services
+ *
+ * [
+ * { # Service Object. See schema for more detail.
+ * name: (string) Service slug
+ * label: (string) Human readable label for the Service
+ * url: (string) Connect URL
+ * },
+ * ...
+ * ]
+ *
+ * @since 6.8
+ */
+class WPCOM_REST_API_V2_Endpoint_List_Publicize_Services extends WP_REST_Controller {
+ /**
+ * Flag to help WordPress.com decide where it should look for
+ * Publicize data. Ignored for direct requests to Jetpack sites.
+ *
+ * @var bool $wpcom_is_wpcom_only_endpoint
+ */
+ public $wpcom_is_wpcom_only_endpoint = true;
+
+ public function __construct() {
+ $this->namespace = 'wpcom/v2';
+ $this->rest_base = 'publicize/services';
+
+ add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+ }
+
+ /**
+ * Called automatically on `rest_api_init()`.
+ */
+ public function register_routes() {
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_items' ),
+ 'permission_callback' => array( $this, 'get_items_permission_check' ),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function get_item_schema() {
+ $schema = array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'jetpack-publicize-service',
+ 'type' => 'object',
+ 'properties' => array(
+ 'name' => array(
+ 'description' => __( 'Alphanumeric identifier for the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'label' => array(
+ 'description' => __( 'Human readable label for the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ ),
+ 'url' => array(
+ 'description' => __( 'The URL used to connect to the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ 'format' => 'uri',
+ ),
+ ),
+ );
+
+ return $this->add_additional_fields_schema( $schema );
+ }
+
+ /**
+ * Retrieves available Publicize Services.
+ *
+ * @see Publicize::get_available_service_data()
+ *
+ * @param WP_REST_Request $request
+ * @return WP_REST_Response suitable for 1-page collection
+ */
+ public function get_items( $request ) {
+ global $publicize;
+ /**
+ * We need this because Publicize::get_available_service_data() uses `Jetpack_Keyring_Service_Helper`
+ * and `Jetpack_Keyring_Service_Helper` relies on `menu_page_url()`.
+ *
+ * We also need add_submenu_page(), as the URLs for connecting each service
+ * rely on the `sharing` menu subpage being present.
+ */
+ include_once ABSPATH . 'wp-admin/includes/plugin.php';
+
+ // The `sharing` submenu page must exist for service connect URLs to be correct.
+ add_submenu_page( 'options-general.php', '', '', 'manage_options', 'sharing', '__return_empty_string' );
+
+ $services_data = $publicize->get_available_service_data();
+
+ $services = array();
+ foreach ( $services_data as $service_data ) {
+ $services[] = $this->prepare_item_for_response( $service_data, $request );
+ }
+
+ $response = rest_ensure_response( $services );
+ $response->header( 'X-WP-Total', count( $services ) );
+ $response->header( 'X-WP-TotalPages', 1 );
+
+ return $response;
+ }
+
+ /**
+ * Filters out data based on ?_fields= request parameter
+ *
+ * @param array $service
+ * @param WP_REST_Request $request
+ * @return array filtered $service
+ */
+ public function prepare_item_for_response( $service, $request ) {
+ if ( ! is_callable( array( $this, 'get_fields_for_response' ) ) ) {
+ return $service;
+ }
+
+ $fields = $this->get_fields_for_response( $request );
+
+ $response_data = array();
+ foreach ( $service as $field => $value ) {
+ if ( in_array( $field, $fields, true ) ) {
+ $response_data[ $field ] = $value;
+ }
+ }
+
+ return $response_data;
+ }
+
+ /**
+ * Verify that user can access Publicize data
+ *
+ * @return true|WP_Error
+ */
+ public function get_items_permission_check() {
+ global $publicize;
+
+ if ( $publicize->current_user_can_access_publicize_data() ) {
+ return true;
+ }
+
+ return new WP_Error(
+ 'invalid_user_permission_publicize',
+ __( 'Sorry, you are not allowed to access Publicize data on this site.', 'jetpack' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
+ }
+}
+
+wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_List_Publicize_Services' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php
new file mode 100644
index 00000000..4c34161c
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/sites-posts-featured-media-url.php
@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * Plugin Name: WPCOM Add Featured Media URL
+ *
+ * Adds `jetpack_featured_media_url` to post responses
+ */
+
+class WPCOM_REST_API_V2_Sites_Posts_Add_Featured_Media_URL {
+ function __construct() {
+ add_action( 'rest_api_init', array( $this, 'add_featured_media_url' ) );
+ }
+
+ function add_featured_media_url() {
+ register_rest_field( 'post', 'jetpack_featured_media_url',
+ array(
+ 'get_callback' => array( $this, 'get_featured_media_url' ),
+ 'update_callback' => null,
+ 'schema' => null,
+ )
+ );
+ }
+
+ function get_featured_media_url( $object, $field_name, $request ) {
+ $featured_media_url = '';
+ $image_attributes = wp_get_attachment_image_src(
+ get_post_thumbnail_id( $object['id'] ),
+ 'full'
+ );
+ if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
+ $featured_media_url = (string) $image_attributes[0];
+ }
+ return $featured_media_url;
+ }
+}
+
+wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Sites_Posts_Add_Featured_Media_URL' );
diff --git a/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php b/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php
new file mode 100644
index 00000000..1aa8ec86
--- /dev/null
+++ b/plugins/jetpack/_inc/lib/core-api/wpcom-fields/post-fields-publicize-connections.php
@@ -0,0 +1,337 @@
+<?php
+
+/**
+ * Add per-post Publicize Connection data.
+ *
+ * { # Post Object
+ * ...
+ * jetpack_publicize_connections: { # Defined below in this file. See schema for more detail.
+ * id: (string) Connection unique_id
+ * service_name: (string) Service slug
+ * display_name: (string) User name/display name of user/connection on Service
+ * enabled: (boolean) Is this connection slated to be shared to? context=edit only
+ * done: (boolean) Is this post (or connection) done sharing? context=edit only
+ * toggleable: (boolean) Can the current user change the `enabled` setting for this Connection+Post? context=edit only
+ * }
+ * ...
+ * meta: { # Not defined in this file. Handled in modules/publicize/publicize.php via `register_meta()`
+ * jetpack_publicize_message: (string) The message to use instead of the post's title when sharing.
+ * }
+ * ...
+ * }
+ *
+ * @since 6.8.0
+ */
+class WPCOM_REST_API_V2_Post_Publicize_Connections_Field extends WPCOM_REST_API_V2_Field_Controller {
+ protected $object_type = 'post';
+ protected $field_name = 'jetpack_publicize_connections';
+
+ public $memoized_updates = array();
+
+ /**
+ * Registers the jetpack_publicize_connections field. Called
+ * automatically on `rest_api_init()`.
+ */
+ public function register_fields() {
+ $this->object_type = get_post_types_by_support( 'publicize' );
+
+ foreach ( $this->object_type as $post_type ) {
+ // Adds meta support for those post types that don't already have it.
+ // Only runs during REST API requests, so it doesn't impact UI.
+ if ( ! post_type_supports( $post_type, 'custom-fields' ) ) {
+ add_post_type_support( $post_type, 'custom-fields' );
+ }
+
+ add_filter( 'rest_pre_insert_' . $post_type, array( $this, 'rest_pre_insert' ), 10, 2 );
+ add_action( 'rest_insert_' . $post_type, array( $this, 'rest_insert' ), 10, 3 );
+ }
+
+ parent::register_fields();
+ }
+
+ /**
+ * Defines data structure and what elements are visible in which contexts
+ */
+ public function get_schema() {
+ return array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'jetpack-publicize-post-connections',
+ 'type' => 'array',
+ 'context' => array( 'view', 'edit' ),
+ 'items' => $this->post_connection_schema(),
+ 'default' => array(),
+ );
+ }
+
+ private function post_connection_schema() {
+ return array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'jetpack-publicize-post-connection',
+ 'type' => 'object',
+ 'properties' => array(
+ 'id' => array(
+ 'description' => __( 'Unique identifier for the Publicize Connection', 'jetpack' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'service_name' => array(
+ 'description' => __( 'Alphanumeric identifier for the Publicize Service', 'jetpack' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'display_name' => array(
+ 'description' => __( 'Username of the connected account', 'jetpack' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'enabled' => array(
+ 'description' => __( 'Whether to share to this connection', 'jetpack' ),
+ 'type' => 'boolean',
+ 'context' => array( 'edit' ),
+ ),
+ 'done' => array(
+ 'description' => __( 'Whether Publicize has already finished sharing for this post', 'jetpack' ),
+ 'type' => 'boolean',
+ 'context' => array( 'edit' ),
+ 'readonly' => true,
+ ),
+ 'toggleable' => array(
+ 'description' => __( 'Whether `enable` can be changed for this post/connection', 'jetpack' ),
+ 'type' => 'boolean',
+ 'context' => array( 'edit' ),
+ 'readonly' => true,
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @param int $post_id
+ * @return true|WP_Error
+ */
+ function permission_check( $post_id ) {
+ global $publicize;
+
+ if ( $publicize->current_user_can_access_publicize_data( $post_id ) ) {
+ return true;
+ }
+
+ return new WP_Error(
+ 'invalid_user_permission_publicize',
+ __( 'Sorry, you are not allowed to access Publicize data for this post.', 'jetpack' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
+ }
+
+ /**
+ * Getter permission check
+ *
+ * @param array $post_array Response data from Post Endpoint
+ * @return true|WP_Error
+ */
+ function get_permission_check( $post_array, $request ) {
+ return $this->permission_check( isset( $post_array['id'] ) ? $post_array['id'] : 0 );
+
+ }
+
+ /**
+ * Setter permission check
+ *
+ * @param WP_Post $post
+ * @return true|WP_Error
+ */
+ public function update_permission_check( $value, $post, $request ) {
+ return $this->permission_check( isset( $post->ID ) ? $post->ID : 0 );
+ }
+
+ /**
+ * Getter: Retrieve current list of connected social accounts for a given post.
+ *
+ * @see Publicize::get_filtered_connection_data()
+ *
+ * @param array $post_array Response from Post Endpoint
+ * @param WP_REST_Request
+ *
+ * @return array List of connections
+ */
+ public function get( $post_array, $request ) {
+ global $publicize;
+
+ $schema = $this->post_connection_schema();
+ $properties = array_keys( $schema['properties'] );
+
+ $connections = $publicize->get_filtered_connection_data( $post_array['id'] );
+
+ $output_connections = array();
+ foreach ( $connections as $connection ) {
+ $output_connection = array();
+ foreach ( $properties as $property ) {
+ if ( isset( $connection[ $property ] ) ) {
+ $output_connection[ $property ] = $connection[ $property ];
+ }
+ }
+
+ $output_connection['id'] = (string) $connection['unique_id'];
+
+ $output_connections[] = $output_connection;
+ }
+
+ return $output_connections;
+ }
+
+ /**
+ * Prior to updating the post, first calculate which Services to
+ * Publicize to and which to skip.
+ *
+ * @param object $post Post data to insert/update.
+ * @param WP_REST_Request $request
+ * @return Filtered $post
+ */
+ public function rest_pre_insert( $post, $request ) {
+ if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
+ return $post;
+ }
+
+ $permission_check = $this->update_permission_check( $request['jetpack_publicize_connections'], $post, $request );
+
+ if ( is_wp_error( $permission_check ) ) {
+ return $permission_check;
+ }
+
+ // memoize
+ $this->get_meta_to_update( $request['jetpack_publicize_connections'], isset( $post->ID ) ? $post->ID : 0 );
+
+ return $post;
+ }
+
+ /**
+ * After creating a new post, update our cached data to reflect
+ * the new post ID.
+ *
+ * @param WP_Post $post
+ * @param WP_REST_Request $request
+ * @param bool $is_new
+ */
+ public function rest_insert( $post, $request, $is_new ) {
+ if ( ! $is_new ) {
+ // An existing post was edited - no need to update
+ // our cache - we started out knowing the correct
+ // post ID.
+ return;
+ }
+
+ if ( ! isset( $request['jetpack_publicize_connections'] ) ) {
+ return;
+ }
+
+ if ( ! isset( $this->memoized_updates[0] ) ) {
+ return;
+ }
+
+ $this->memoized_updates[ $post->ID ] = $this->memoized_updates[0];
+ unset( $this->memoized_updates[0] );
+ }
+
+ protected function get_meta_to_update( $requested_connections, $post_id = 0 ) {
+ global $publicize;
+
+ if ( isset( $this->memoized_updates[$post_id] ) ) {
+ return $this->memoized_updates[$post_id];
+ }
+
+ $available_connections = $publicize->get_filtered_connection_data( $post_id );
+
+ $changed_connections = array();
+
+ // Build lookup mappings
+ $available_connections_by_unique_id = array();
+ $available_connections_by_service_name = array();
+ foreach ( $available_connections as $available_connection ) {
+ $available_connections_by_unique_id[ $available_connection['unique_id'] ] = $available_connection;
+
+ if ( ! isset( $available_connections_by_service_name[ $available_connection['service_name'] ] ) ) {
+ $available_connections_by_service_name[ $available_connection['service_name'] ] = array();
+ }
+ $available_connections_by_service_name[ $available_connection['service_name'] ][] = $available_connection;
+ }
+
+ // Handle { service_name: $service_name, enabled: (bool) }
+ foreach ( $requested_connections as $requested_connection ) {
+ if ( ! isset( $requested_connection['service_name'] ) ) {
+ continue;
+ }
+
+ if ( ! isset( $available_connections_by_service_name[ $requested_connection['service_name'] ] ) ) {
+ continue;
+ }
+
+ foreach ( $available_connections_by_service_name[ $requested_connection['service_name'] ] as $available_connection ) {
+ $changed_connections[ $available_connection['unique_id'] ] = $requested_connection['enabled'];
+ }
+ }
+
+ // Handle { id: $id, enabled: (bool) }
+ // These override the service_name settings
+ foreach ( $requested_connections as $requested_connection ) {
+ if ( ! isset( $requested_connection['id'] ) ) {
+ continue;
+ }
+
+ if ( ! isset( $available_connections_by_unique_id[ $requested_connection['id'] ] ) ) {
+ continue;
+ }
+
+ $changed_connections[ $requested_connection['id'] ] = $requested_connection['enabled'];
+ }
+
+ // Set all changed connections to their new value
+ foreach ( $changed_connections as $unique_id => $enabled ) {
+ $connection = $available_connections_by_unique_id[ $unique_id ];
+
+ if ( $connection['done'] || ! $connection['toggleable'] ) {
+ continue;
+ }
+
+ $available_connections_by_unique_id[ $unique_id ]['enabled'] = $enabled;
+ }
+
+ $meta_to_update = array();
+ // For all connections, ensure correct post_meta
+ foreach ( $available_connections_by_unique_id as $unique_id => $available_connection ) {
+ if ( $available_connection['enabled'] ) {
+ $meta_to_update[$publicize->POST_SKIP . $unique_id] = null;
+ } else {
+ $meta_to_update[$publicize->POST_SKIP . $unique_id] = 1;
+ }
+ }
+
+ $this->memoized_updates[$post_id] = $meta_to_update;
+
+ return $meta_to_update;
+ }
+
+ /**
+ * Update the connections slated to be shared to.
+ *
+ * @param array $requested_connections
+ * Items are either `{ id: (string) }` or `{ service_name: (string) }`
+ * @param WP_Post $post
+ * @param WP_REST_Request
+ */
+ public function update( $requested_connections, $post, $request ) {
+ foreach ( $this->get_meta_to_update( $requested_connections, $post->ID ) as $meta_key => $meta_value ) {
+ if ( is_null( $meta_value ) ) {
+ delete_post_meta( $post->ID, $meta_key );
+ } else {
+ update_post_meta( $post->ID, $meta_key, $meta_value );
+ }
+ }
+ }
+}
+
+if ( Jetpack::is_module_active( 'publicize' ) ) {
+ wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Post_Publicize_Connections_Field' );
+} \ No newline at end of file