Repository: Automattic/jetpack
Pull request #42978: Social: Improve share status logic
Author: login: manzoorwanijk
Labels: name: Enhancement
description: Changes to an existing feature — removing, adding, or changing parts of it
name: [Feature] Publicize
description: Now Jetpack Social, auto-sharing
name: [Package] Publicize
name: [Plugin] Social
description: Issues about the Jetpack Social plugin
name: [Tests] Includes Tests
name: I don't care about code coverage for this PR
description: Use this label to ignore the check for insufficient code coveage.
Created: 2025-04-09T08:53:14Z | Merged: 2025-04-10T07:29:22Z

Following #42593, we had to revert the corresponding change on WPCOM because of the inconsistent structure of the post shares meta.

The problem was that wpcom synced the shares meta back to the Jetpack sites in a different format then expected in those last changes which messed up the structure on Jetpack sites and thus sharing history was not shown and there were some PHP warnings about unknow array key `connection_id`.

Thus, we are changing the implementation of the meta syncing and moving the endpoint to the `wpcom/v2` namespace to make it available for WoA sites as well.

On wpcom, the related changes are made in 180073-ghe-Automattic/wpcom

## Proposed changes:
<!--- Explain what functional changes your PR includes -->
* Move `jetpack/v4/social/sync-shares/post` endpoint to `wpcom/v2/publicize/share-status/sync`
* Update `Share_Status` class to add safety checks
* Update the `wpcom/v2/publicize/share-status` endpoint to use the `Share_Status` class to avoid duplication and also remove the proxy call to wpcom API because meta is already on site
* Remove the unused post shares class from Social plugin
* Update the logic in Social to get the shares from the same class for the shares shortcode

### Other information:

- [ ] Have you written new tests for your changes, if applicable?
- [ ] Have you checked the E2E test CI results, and verified that your changes do not break them?
- [ ] Have you tested your changes on WordPress.com, if applicable (if so, you'll see a generated comment below with a script to run)?

## Jetpack product discussion
<!-- If you're an Automattician, include a shortlink to the p2 discussion with Jetpack Product here. -->
<!-- Make sure any changes to existing products have been discussed and agreed upon -->

## Does this pull request change what data or activity we track or use?
<!--- If so, please add the "[Status] Needs Privacy Updates" label and explain what changes there are. -->
<!--- Check existing Jetpack support documents for a preview of the information we need. -->

## Testing instructions:
<!-- If you were reviewing this PR, how would you like the instructions to be presented? -->
<!-- Please include detailed testing steps, explaining how to test your change. -->
<!-- Bear in mind that context you working on is not obvious for everyone.  -->
<!-- Adding "simple" configuration steps will help reviewers to get to your PR as quickly as possible. -->
<!-- "Before / After" screenshots can also be very helpful when the change is visual. -->

* We need to test these changes with older Jetpack and Social versions
* Apply 180073-ghe-Automattic/wpcom to your sandbox
* Point your site and the public API to your sandbox
* Share a post to a few networks
* Confirm that the share status gets reflected accordingly
* Reload the page
* Confirm that the sharing history button shows up on page load instead of waiting for the API call to finish
* You can also confirm the data from `JetpackScriptData.social.store_initial_state.shareStatus`
* Reshare an old post and confirm that share status works fine
* Test with Jetpack < `v13.7` and Social < `v5.0.0`
* Test share status on Atomic using the changes from #42962,
* Test it on Simple sites by defining this in `0-sandbox.php`
```php
define( 'JETPACK_SOCIAL_HAS_SHARE_STATUS', true );
```


Diff:
diff --git a/projects/packages/publicize/.phan/baseline.php b/projects/packages/publicize/.phan/baseline.php
index 1b7f85dc7056..ede1e0b9b81c 100644
--- a/projects/packages/publicize/.phan/baseline.php
+++ b/projects/packages/publicize/.phan/baseline.php
@@ -11,13 +11,12 @@
     // # Issue statistics:
     // PhanPluginMixedKeyNoKey : 8 occurrences
     // PhanPluginDuplicateConditionalNullCoalescing : 6 occurrences
-    // PhanPluginUnreachableCode : 4 occurrences
+    // PhanPluginUnreachableCode : 5 occurrences
     // PhanTypeMismatchArgument : 4 occurrences
     // PhanUndeclaredClassMethod : 3 occurrences
     // PhanNoopNew : 2 occurrences
     // PhanPossiblyUndeclaredVariable : 2 occurrences
     // PhanTypeMismatchArgumentNullable : 2 occurrences
-    // PhanTypeMismatchArgumentNullableInternal : 2 occurrences
     // PhanTypeMismatchReturnProbablyReal : 2 occurrences
     // PhanTypeMissingReturn : 2 occurrences
     // PhanImpossibleCondition : 1 occurrence
@@ -25,6 +24,7 @@
     // PhanPluginDuplicateExpressionAssignmentOperation : 1 occurrence
     // PhanPluginSimplifyExpressionBool : 1 occurrence
     // PhanSuspiciousMagicConstant : 1 occurrence
+    // PhanTypeMismatchArgumentNullableInternal : 1 occurrence
     // PhanTypeMismatchArgumentProbablyReal : 1 occurrence
     // PhanTypeMismatchDefault : 1 occurrence
     // PhanTypeMismatchDimFetch : 1 occurrence
@@ -41,7 +41,6 @@
         'src/class-publicize-ui.php' => ['PhanPluginDuplicateExpressionAssignmentOperation', 'PhanTypeMismatchReturnProbablyReal'],
         'src/class-publicize.php' => ['PhanParamSignatureMismatch', 'PhanPossiblyUndeclaredVariable', 'PhanTypeMismatchArgument', 'PhanTypeMissingReturn'],
         'src/class-rest-controller.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchReturnProbablyReal'],
-        'src/class-share-status.php' => ['PhanTypeMismatchArgumentNullableInternal'],
         'src/rest-api/class-connections-controller.php' => ['PhanPluginMixedKeyNoKey', 'PhanTypeSuspiciousNonTraversableForeach'],
         'src/rest-api/class-connections-post-field.php' => ['PhanPluginDuplicateConditionalNullCoalescing'],
         'src/rest-api/class-scheduled-actions-controller.php' => ['PhanPluginMixedKeyNoKey'],
@@ -53,6 +52,7 @@
         'src/social-image-generator/class-settings.php' => ['PhanPluginDuplicateConditionalNullCoalescing'],
         'src/social-image-generator/class-setup.php' => ['PhanTypeMismatchArgumentNullable'],
         'tests/php/Connections_Post_Field_Test.php' => ['PhanPluginUnreachableCode', 'PhanTypeMismatchArgument'],
+        'tests/php/Share_Status_Test.php' => ['PhanPluginUnreachableCode'],
     ],
     // 'directory_suppressions' => ['src/directory_name' => ['PhanIssueName1', 'PhanIssueName2']] can be manually added if needed.
     // (directory_suppressions will currently be ignored by subsequent calls to --save-baseline, but may be preserved in future Phan releases)
diff --git a/projects/packages/publicize/changelog/update-social-share-status-logic b/projects/packages/publicize/changelog/update-social-share-status-logic
new file mode 100644
index 000000000000..f97ddc6fbe67
--- /dev/null
+++ b/projects/packages/publicize/changelog/update-social-share-status-logic
@@ -0,0 +1,4 @@
+Significance: patch
+Type: changed
+
+Improved the logic for share status
diff --git a/projects/packages/publicize/src/class-rest-controller.php b/projects/packages/publicize/src/class-rest-controller.php
index 3d841fe5239e..b8e1eed4ec92 100644
--- a/projects/packages/publicize/src/class-rest-controller.php
+++ b/projects/packages/publicize/src/class-rest-controller.php
@@ -34,8 +34,6 @@ class REST_Controller {
 	 */
 	const JETPACK_SOCIAL_V1_YEARLY = 'jetpack_social_v1_yearly';
 
-	const SOCIAL_SHARES_POST_META_KEY = '_publicize_shares';
-
 	/**
 	 * Constructor
 	 *
@@ -537,16 +535,24 @@ protected function get_blog_id() {
 	 * @param WP_REST_Request $request Full details about the request.
 	 */
 	public function update_post_shares( $request ) {
+
+		Publicize_Utils::endpoint_deprecated_warning(
+			__METHOD__,
+			'jetpack-14.6, jetpack-social-6.4.0',
+			'jetpack/v4/social/sync-shares/post/:id',
+			'wpcom/v2/publicize/share-status/sync'
+		);
+
 		$request_body = $request->get_json_params();
 
 		$post_id   = $request->get_param( 'id' );
 		$post_meta = $request_body['meta'];
 		$post      = get_post( $post_id );
 
-		if ( $post && 'publish' === $post->post_status && isset( $post_meta[ self::SOCIAL_SHARES_POST_META_KEY ] ) ) {
-			update_post_meta( $post_id, self::SOCIAL_SHARES_POST_META_KEY, $post_meta[ self::SOCIAL_SHARES_POST_META_KEY ] );
+		if ( $post && 'publish' === $post->post_status && isset( $post_meta[ Share_Status::SHARES_META_KEY ] ) ) {
+			update_post_meta( $post_id, Share_Status::SHARES_META_KEY, $post_meta[ Share_Status::SHARES_META_KEY ] );
 			$urls = array();
-			foreach ( $post_meta[ self::SOCIAL_SHARES_POST_META_KEY ] as $share ) {
+			foreach ( $post_meta[ Share_Status::SHARES_META_KEY ] as $share ) {
 				if ( isset( $share['status'] ) && 'success' === $share['status'] ) {
 					$urls[] = array(
 						'url'     => $share['message'],
@@ -579,11 +585,20 @@ public function update_post_shares( $request ) {
 	 *
 	 * GET `jetpack/v4/social/share-status/<post_id>`
 	 *
+	 * @deprecated $$next-version$$
+	 *
 	 * @param WP_REST_Request $request The request object.
 	 */
 	public function get_post_share_status( WP_REST_Request $request ) {
 		$post_id = $request->get_param( 'post_id' );
 
-		return rest_ensure_response( SHare_Status::get_post_share_status( $post_id ) );
+		Publicize_Utils::endpoint_deprecated_warning(
+			__METHOD__,
+			'jetpack-14.6, jetpack-social-6.4.0',
+			'jetpack/v4/social/share-status/:postId',
+			'wpcom/v2/publicize/share-status'
+		);
+
+		return rest_ensure_response( Share_Status::get_post_share_status( $post_id ) );
 	}
 }
diff --git a/projects/packages/publicize/src/class-share-status.php b/projects/packages/publicize/src/class-share-status.php
index 9fb7648f9d65..98c8fc7a7ac4 100644
--- a/projects/packages/publicize/src/class-share-status.php
+++ b/projects/packages/publicize/src/class-share-status.php
@@ -11,45 +11,62 @@
  * Publicize Services class.
  */
 class Share_Status {
+	const SHARES_META_KEY = '_publicize_shares';
+
 	/**
 	 * Gets the share status for a post.
 	 *
-	 * @param int $post_id The post ID.
+	 * @param int  $post_id          The post ID.
+	 * @param bool $filter_by_access Whether to filter the shares by access.
 	 */
-	public static function get_post_share_status( $post_id ) {
-		$shares = get_post_meta( $post_id, REST_Controller::SOCIAL_SHARES_POST_META_KEY, true );
+	public static function get_post_share_status( $post_id, $filter_by_access = true ) {
+
+		$post = get_post( $post_id );
+
+		if ( empty( $post ) || ! metadata_exists( 'post', $post_id, self::SHARES_META_KEY ) ) {
+			return array(
+				'done'   => false,
+				'shares' => array(),
+			);
+		}
+
+		$shares = get_post_meta( $post_id, self::SHARES_META_KEY, true );
 
 		// If the data is in an associative array format, we fetch it without true to get all the shares.
 		// This is needed to support the old WPCOM format.
 		if ( isset( $shares ) && is_array( $shares ) && ! array_is_list( $shares ) ) {
-			$shares = get_post_meta( $post_id, REST_Controller::SOCIAL_SHARES_POST_META_KEY );
+			$shares = get_post_meta( $post_id, self::SHARES_META_KEY );
 		}
 
-		// If the data is not an array, it means that sharing is not done yet.
-		$done = is_array( $shares );
+		// Better safe than sorry.
+		if ( empty( $shares ) ) {
+			$shares = array();
+		}
 
-		if ( $done ) {
+		if ( $filter_by_access ) {
 			// The site could have multiple admins, editors and authors connected. Load shares information that only the current user has access to.
 			$connection_ids = wp_list_pluck( Connections::get_all_for_user(), 'connection_id', 'connection_id' );
 
 			$shares = array_filter(
 				$shares,
 				function ( $share ) use ( $connection_ids ) {
-					return isset( $connection_ids[ $share['connection_id'] ] );
-				}
-			);
-
-			usort(
-				$shares,
-				function ( $a, $b ) {
-					return $b['timestamp'] - $a['timestamp'];
+					return isset( $share['connection_id'] ) && isset( $connection_ids[ $share['connection_id'] ] );
 				}
 			);
 		}
 
+		usort(
+			$shares,
+			function ( $a, $b ) {
+				return $b['timestamp'] - $a['timestamp'];
+			}
+		);
+
+		$shares = array_values( $shares );
+
 		return array(
-			'shares' => $done ? $shares : array(),
-			'done'   => $done,
+			'shares' => $shares,
+			'done'   => true,
 		);
 	}
 }
diff --git a/projects/packages/publicize/src/rest-api/class-share-status-controller.php b/projects/packages/publicize/src/rest-api/class-share-status-controller.php
index 5857e7d287ca..064b01ffd8bf 100644
--- a/projects/packages/publicize/src/rest-api/class-share-status-controller.php
+++ b/projects/packages/publicize/src/rest-api/class-share-status-controller.php
@@ -7,9 +7,9 @@
 
 namespace Automattic\Jetpack\Publicize\REST_API;
 
+use Automattic\Jetpack\Connection\Rest_Authentication;
 use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
-use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
-use Automattic\Jetpack\Publicize\REST_Controller;
+use Automattic\Jetpack\Publicize\Share_Status;
 use WP_Error;
 use WP_REST_Request;
 use WP_REST_Response;
@@ -60,6 +60,33 @@ public function register_routes() {
 				'schema' => array( $this, 'get_public_item_schema' ),
 			)
 		);
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base . '/sync',
+			array(
+				array(
+					'methods'             => WP_REST_Server::CREATABLE,
+					'callback'            => array( $this, 'receive_share_status' ),
+					'permission_callback' => array( Rest_Authentication::class, 'is_signed_with_blog_token' ),
+					'args'                => array(
+						'post_id' => array(
+							'type'        => 'integer',
+							'required'    => true,
+							'description' => __( 'The post ID to update the data for.', 'jetpack-publicize-pkg' ),
+						),
+						'shares'  => array(
+							'type'        => 'array',
+							'required'    => true,
+							'description' => __( 'The share status items.', 'jetpack-publicize-pkg' ),
+							'items'       => array(
+								'type'       => 'object',
+								'properties' => $this->get_share_item_schema(),
+							),
+						),
+					),
+				),
+			)
+		);
 	}
 
 	/**
@@ -71,41 +98,25 @@ public function register_routes() {
 	public function get_items( $request ) {
 		$post_id = $request->get_param( 'post_id' );
 
-		if ( Utils::is_wpcom() ) {
-			$post = get_post( $post_id );
-
-			if ( empty( $post ) ) {
-				return new WP_Error( 'not_found', 'Cannot find that post', array( 'status' => 404 ) );
-			}
-			if ( 'publish' !== $post->post_status ) {
-				return new WP_Error( 'not_published', 'Cannot get share status for an unpublished post', array( 'status' => 400 ) );
-			}
-
-			$shares = get_post_meta( $post_id, REST_CONTROLLER::SOCIAL_SHARES_POST_META_KEY, true );
+		$post = get_post( $post_id );
 
-			// If the data is in an associative array format, we fetch it without true to get all the shares.
-			// This is needed to support the old WPCOM format.
-			if ( isset( $shares ) && is_array( $shares ) && ! array_is_list( $shares ) ) {
-				$shares = get_post_meta( $post_id, REST_CONTROLLER::SOCIAL_SHARES_POST_META_KEY );
-			}
-
-			$done = metadata_exists( 'post', $post_id, REST_CONTROLLER::SOCIAL_SHARES_POST_META_KEY );
-
-			$response = array(
-				'shares' => $done ? $shares : array(),
-				'done'   => $done,
+		if ( empty( $post ) ) {
+			return new WP_Error(
+				'post_not_found',
+				__( 'Cannot find that post.', 'jetpack-publicize-pkg' ),
+				array( 'status' => 404 )
 			);
-
-			return rest_ensure_response( $response );
 		}
 
-		$response = $this->proxy_request_to_wpcom_as_user( $request );
-
-		if ( is_wp_error( $response ) ) {
-			return rest_ensure_response( $response );
+		if ( 'publish' !== $post->post_status ) {
+			return new WP_Error(
+				'post_not_published',
+				__( 'Cannot get share status for an unpublished post', 'jetpack-publicize-pkg' ),
+				array( 'status' => 400 )
+			);
 		}
 
-		return rest_ensure_response( $response );
+		return rest_ensure_response( Share_Status::get_post_share_status( $post_id ) );
 	}
 
 	/**
@@ -118,6 +129,56 @@ public function get_items_permissions_check( $request ) {// phpcs:ignore Variabl
 		return $this->publicize_permissions_check();
 	}
 
+	/**
+	 * Schema for a share item.
+	 *
+	 * @return array
+	 */
+	public function get_share_item_schema() {
+		return array(
+			'status'          => array(
+				'description' => __( 'Status of the share.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'message'         => array(
+				'description' => __( 'Share message or link.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'timestamp'       => array(
+				'description' => __( 'Timestamp of the share.', 'jetpack-publicize-pkg' ),
+				'type'        => 'integer',
+			),
+			'service'         => array(
+				'description' => __( 'The service to which it was shared.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'connection_id'   => array(
+				'description' => __( 'Connection ID for the share.', 'jetpack-publicize-pkg' ),
+				'type'        => 'integer',
+			),
+			'external_id'     => array(
+				'description' => __( 'External ID of the shared post.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'external_name'   => array(
+				'description' => __( 'External name of the shared post.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'profile_picture' => array(
+				'description' => __( 'Profile picture URL of the account sharing.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'profile_link'    => array(
+				'description' => __( 'Profile link of the sharing account.', 'jetpack-publicize-pkg' ),
+				'type'        => 'string',
+			),
+			'wpcom_user_id'   => array(
+				'type'        => 'integer',
+				'description' => __( 'wordpress.com ID of the user the connection belongs to.', 'jetpack-publicize-pkg' ),
+			),
+		);
+	}
+
 	/**
 	 * Schema for the endpoint.
 	 *
@@ -134,44 +195,7 @@ public function get_item_schema() {
 					'type'        => 'array',
 					'items'       => array(
 						'type'       => 'object',
-						'properties' => array(
-							'status'          => array(
-								'description' => __( 'Status of the share.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-							'message'         => array(
-								'description' => __( 'Share message or link.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-							'timestamp'       => array(
-								'description' => __( 'Timestamp of the share.', 'jetpack-publicize-pkg' ),
-								'type'        => 'integer',
-							),
-							'service'         => array(
-								'description' => __( 'The service to which it was shared.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-							'connection_id'   => array(
-								'description' => __( 'Connection ID for the share.', 'jetpack-publicize-pkg' ),
-								'type'        => 'integer',
-							),
-							'external_id'     => array(
-								'description' => __( 'External ID of the shared post.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-							'external_name'   => array(
-								'description' => __( 'External name of the shared post.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-							'profile_picture' => array(
-								'description' => __( 'Profile picture URL of the account sharing.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-							'profile_link'    => array(
-								'description' => __( 'Profile link of the sharing account.', 'jetpack-publicize-pkg' ),
-								'type'        => 'string',
-							),
-						),
+						'properties' => $this->get_share_item_schema(),
 					),
 				),
 				'done'   => array(
@@ -183,4 +207,69 @@ public function get_item_schema() {
 
 		return $this->add_additional_fields_schema( $schema );
 	}
+
+	/**
+	 * Receive share status from WPCOM.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error
+	 */
+	public function receive_share_status( $request ) {
+
+		$post_id = $request->get_param( 'post_id' );
+		$post    = get_post( $post_id );
+
+		if ( empty( $post ) ) {
+			return new WP_Error(
+				'post_not_found',
+				__( 'Cannot find that post.', 'jetpack-publicize-pkg' ),
+				array( 'status' => 404 )
+			);
+		}
+
+		if ( 'publish' !== $post->post_status ) {
+			return new WP_Error(
+				'post_not_published',
+				__( 'Cannot update share status for an unpublished post.', 'jetpack-publicize-pkg' ),
+				array( 'status' => 400 )
+			);
+		}
+
+		$shares = $request->get_param( 'shares' );
+
+		// This check ensures that the shares data is in the expected format.
+		if ( ! empty( $shares ) && empty( $shares[0]['status'] ) ) {
+			return new WP_Error(
+				'invalid_shares',
+				__( 'Invalid shares data.', 'jetpack-publicize-pkg' ),
+				array( 'status' => 400 )
+			);
+		}
+
+		update_post_meta( $post_id, Share_Status::SHARES_META_KEY, $shares );
+
+		$urls = array();
+
+		foreach ( $shares as $share ) {
+			if ( isset( $share['status'] ) && 'success' === $share['status'] ) {
+				$urls[] = array(
+					'url'     => $share['message'],
+					'service' => $share['service'],
+				);
+			}
+		}
+
+		/**
+		 * Fires after Publicize Shares post meta has been saved.
+		 *
+		 * @param array $urls {
+		 *     An array of social media shares.
+		 *     @type array $url URL to the social media post.
+		 *     @type string $service Social media service shared to.
+		 * }
+		 */
+		do_action( 'jetpack_publicize_share_urls_saved', $urls );
+
+		return rest_ensure_response( new WP_REST_Response() );
+	}
 }
diff --git a/projects/packages/publicize/tests/php/Share_Status_Test.php b/projects/packages/publicize/tests/php/Share_Status_Test.php
new file mode 100644
index 000000000000..af1ded0dced2
--- /dev/null
+++ b/projects/packages/publicize/tests/php/Share_Status_Test.php
@@ -0,0 +1,290 @@
+<?php // phpcs:disable WordPress.Files.FileName.InvalidClassFileName, Squiz.Commenting.InlineComment.WrongStyle
+/**
+ * Tests for Share Status.
+ */
+
+namespace Automattic\Jetpack\Publicize;
+
+use PHPUnit\Framework\TestCase;
+use WorDBless\Options as WorDBless_Options;
+use WorDBless\PostMeta as WorDBless_PostMeta;
+use WorDBless\Posts as WorDBless_Posts;
+use WorDBless\Users as WorDBless_Users;
+
+/**
+ * Class Share_Status_Test
+ */
+class Share_Status_Test extends TestCase {
+
+	/**
+	 * The admin user ID.
+	 *
+	 * @var int
+	 */
+	private static $admin_id = 0;
+
+	/**
+	 * The post ID.
+	 *
+	 * @var int
+	 */
+	private static $post_id = 0;
+
+	/**
+	 * Setting up the test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		/**
+		 * This test is disabled because of a bug in the WorDBless library.
+		 *
+		 * @see https://github.com/Automattic/wordbless/issues/76
+		 *
+		 * TODO: Remove this comment when the bug is fixed.
+		 */
+		$this->markTestIncomplete();
+
+		static::$admin_id = wp_insert_user(
+			array(
+				'user_login' => 'dummy_admin',
+				'user_pass'  => 'dummy_pass',
+				'role'       => 'administrator',
+			)
+		);
+
+		static::$post_id = wp_insert_post(
+			array(
+				'post_author'  => static::$admin_id,
+				'post_title'   => 'acd',
+				'post_excerpt' => 'dsad',
+				'post_status'  => 'publish',
+				'post_type'    => 'post',
+			)
+		);
+
+		$connections = array(
+			array(
+				'connection_id' => '111',
+				'display_name'  => 'Tumblr Connection',
+				'service_name'  => 'tumblr',
+				'service_label' => 'Tumblr',
+				'shared'        => true,
+				'wpcom_user_id' => 0,
+			),
+			array(
+				'connection_id' => '112',
+				'display_name'  => 'Mastodon Connection',
+				'service_name'  => 'mastodon',
+				'service_label' => 'Mastodon',
+				'shared'        => true,
+				'wpcom_user_id' => 0,
+			),
+		);
+
+		set_transient( Connections::CONNECTIONS_TRANSIENT, $connections, DAY_IN_SECONDS );
+
+		wp_set_current_user( static::$admin_id );
+	}
+
+	/**
+	 * Returning the environment into its initial state.
+	 */
+	public function tearDown(): void {
+		parent::tearDown();
+		wp_set_current_user( 0 );
+
+		WorDBless_Options::init()->clear_options();
+		WorDBless_Posts::init()->clear_all_posts();
+		WorDBless_Users::init()->clear_all_users();
+		WorDBless_PostMeta::init()->clear_all_meta();
+
+		delete_transient( Connections::CONNECTIONS_TRANSIENT );
+	}
+
+	/**
+	 * Test for get_post_share_status for an invalid post.
+	 */
+	public function test_get_post_share_status__for__invalid_post() {
+		$shares = Share_Status::get_post_share_status( -1 );
+
+		$this->assertEquals(
+			array(
+				'done'   => false,
+				'shares' => array(),
+			),
+			$shares
+		);
+	}
+
+	/**
+	 * Test for get_post_share_status for non-existent meta.
+	 */
+	public function test_get_post_share_status__for__non_existent_meta() {
+		$shares = Share_Status::get_post_share_status( static::$post_id );
+
+		$this->assertEquals(
+			array(
+				'done'   => false,
+				'shares' => array(),
+			),
+			$shares
+		);
+	}
+
+	/**
+	 * Test for get_post_share_status for existent meta.
+	 */
+	public function test_get_post_share_status__for__existent_meta() {
+		$shares = array(
+			array(
+				'connection_id' => '111',
+				'service'       => 'tumblr',
+				'timestamp'     => 1234567890,
+				'status'        => 'success',
+			),
+			array(
+				'connection_id' => '123', // Non-existent connection.
+				'service'       => 'bluesky',
+				'timestamp'     => 1234567890,
+				'status'        => 'success',
+			),
+		);
+		update_post_meta(
+			static::$post_id,
+			Share_Status::SHARES_META_KEY,
+			$shares
+		);
+
+		// By default it should filter by access.
+		$shares_data = Share_Status::get_post_share_status(
+			static::$post_id
+		);
+		$this->assertEquals(
+			array(
+				'done'   => true,
+				'shares' => array(
+					$shares[0],
+				),
+			),
+			$shares_data
+		);
+
+		// Now it should not filter by access.
+		$shares_data = Share_Status::get_post_share_status(
+			static::$post_id,
+			false
+		);
+		$this->assertEquals(
+			array(
+				'done'   => true,
+				'shares' => $shares,
+			),
+			$shares_data
+		);
+	}
+
+	/**
+	 * Test for get_post_share_status for inconsistent format.
+	 */
+	public function test_get_post_share_status__for__inconsistent_format() {
+		$shares = array(
+			array(
+				'connection_id' => '111',
+				'service'       => 'tumblr',
+				'timestamp'     => 1234567890,
+				'status'        => 'success',
+			),
+			array(
+				'connection_id' => '112',
+				'service'       => 'mastodon',
+				'timestamp'     => 1234567890,
+				'status'        => 'success',
+			),
+		);
+		add_post_meta(
+			static::$post_id,
+			Share_Status::SHARES_META_KEY,
+			$shares[0]
+		);
+		add_post_meta(
+			static::$post_id,
+			Share_Status::SHARES_META_KEY,
+			$shares[1]
+		);
+
+		// Let us verify that $single=true returns the first element.
+		$this->assertEquals(
+			get_post_meta(
+				static::$post_id,
+				Share_Status::SHARES_META_KEY,
+				true
+			),
+			$shares[0]
+		);
+
+		// Let us verify that $single=false returns all elements.
+		$this->assertEquals(
+			get_post_meta(
+				static::$post_id,
+				Share_Status::SHARES_META_KEY
+			),
+			array(
+				$shares[0],
+				$shares[1],
+			)
+		);
+
+		// Now it should handle the inconsistent format.
+		$shares_data = Share_Status::get_post_share_status(
+			static::$post_id
+		);
+		$this->assertEquals(
+			array(
+				'done'   => true,
+				'shares' => $shares,
+			),
+			$shares_data
+		);
+	}
+
+	/**
+	 * Test for get_post_share_status for sort order.
+	 */
+	public function test_get_post_share_status__for__sort_order() {
+		$shares = array(
+			array(
+				'connection_id' => '111',
+				'service'       => 'tumblr',
+				'timestamp'     => 1234567890,
+				'status'        => 'success',
+			),
+			array(
+				'connection_id' => '112',
+				'service'       => 'mastodon',
+				'timestamp'     => 2345678901,
+				'status'        => 'success',
+			),
+		);
+		update_post_meta(
+			static::$post_id,
+			Share_Status::SHARES_META_KEY,
+			$shares
+		);
+
+		$shares_data = Share_Status::get_post_share_status(
+			static::$post_id
+		);
+		$this->assertEquals(
+			array(
+				'done'   => true,
+				'shares' => array(
+					// It should sort the shares by timestamp in descending order.
+					$shares[1],
+					$shares[0],
+				),
+			),
+			$shares_data
+		);
+	}
+}
diff --git a/projects/plugins/social/changelog/update-social-share-status-logic b/projects/plugins/social/changelog/update-social-share-status-logic
new file mode 100644
index 000000000000..f97ddc6fbe67
--- /dev/null
+++ b/projects/plugins/social/changelog/update-social-share-status-logic
@@ -0,0 +1,4 @@
+Significance: patch
+Type: changed
+
+Improved the logic for share status
diff --git a/projects/plugins/social/src/class-jetpack-social.php b/projects/plugins/social/src/class-jetpack-social.php
index 3ecfa8bd8048..26020dda2b1e 100644
--- a/projects/plugins/social/src/class-jetpack-social.php
+++ b/projects/plugins/social/src/class-jetpack-social.php
@@ -100,7 +100,6 @@ function () {
 
 		// Add REST routes.
 		add_action( 'rest_api_init', array( new Automattic\Jetpack\Social\REST_Settings_Controller(), 'register_rest_routes' ) );
-		add_action( 'rest_api_init', array( new Automattic\Jetpack\Social\REST_Social_Note_Controller(), 'register_rest_routes' ) );
 
 		// Adds the review prompt initial state.
 		add_action( 'jetpack_social_admin_script_data', array( $this, 'add_review_initial_state' ), 10, 1 );
diff --git a/projects/plugins/social/src/class-rest-social-note-controller.php b/projects/plugins/social/src/class-rest-social-note-controller.php
deleted file mode 100644
index b19aeb690063..000000000000
--- a/projects/plugins/social/src/class-rest-social-note-controller.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * The Social Note Controller class.
- * Registers the REST routes for Social.
- *
- * @package automattic/jetpack-social-plugin
- */
-
-namespace Automattic\Jetpack\Social;
-
-use Automattic\Jetpack\Connection\Rest_Authentication;
-use WP_Error;
-use WP_REST_Controller;
-use WP_REST_Request;
-use WP_REST_Response;
-use WP_REST_Server;
-
-/**
- * Registers the REST routes for Social.
- */
-class REST_Social_Note_Controller extends WP_REST_Controller {
-	const SOCIAL_SHARES_POST_META_KEY = '_publicize_shares';
-	/**
-	 * Registers the REST routes for Social.
-	 *
-	 * @access public
-	 * @static
-	 */
-	public function register_rest_routes() {
-		register_rest_route(
-			'jetpack/v4',
-			'/social/shares/post/(?P<id>\d+)',
-			array(
-				array(
-					'methods'             => WP_REST_Server::EDITABLE,
-					'callback'            => array( $this, 'update_post_shares' ),
-					'permission_callback' => array( $this, 'update_post_shares_permission_callback' ),
-					'args'                => array(
-						'meta' => array(
-							'type'       => 'object',
-							'required'   => true,
-							'properties' => array(
-								'_publicize_shares' => array(
-									'type'     => 'array',
-									'required' => true,
-								),
-							),
-						),
-					),
-				),
-			)
-		);
-	}
-
-	/**
-	 * Update the post with information about shares.
-	 *
-	 * @param WP_REST_Request $request Full details about the request.
-	 */
-	public function update_post_shares( $request ) {
-		$request_body = $request->get_json_params();
-
-		$post_id   = $request->get_param( 'id' );
-		$post_meta = $request_body['meta'];
-		$post      = get_post( $post_id );
-
-		if ( $post && $post->post_type === Note::JETPACK_SOCIAL_NOTE_CPT && $post->post_status === 'publish' && isset( $post_meta[ self::SOCIAL_SHARES_POST_META_KEY ] ) ) {
-			update_post_meta( $post_id, self::SOCIAL_SHARES_POST_META_KEY, $post_meta[ self::SOCIAL_SHARES_POST_META_KEY ] );
-			$urls = array();
-			foreach ( $post_meta[ self::SOCIAL_SHARES_POST_META_KEY ] as $share ) {
-				if ( isset( $share['status'] ) && 'success' === $share['status'] ) {
-					$urls[] = array(
-						'url'     => $share['message'],
-						'service' => $share['service'],
-					);
-				}
-			}
-			/** This action is documented in src/class-rest-controller.php */
-			do_action( 'jetpack_publicize_share_urls_saved', $urls );
-			return rest_ensure_response( new WP_REST_Response() );
-		}
-
-		return new WP_Error(
-			'rest_cannot_edit',
-			__( 'Failed to update the post meta', 'jetpack-social' ),
-			array( 'status' => 500 )
-		);
-	}
-
-	/**
-	 * Permissions callback.
-	 */
-	public function update_post_shares_permission_callback() {
-		if ( Rest_Authentication::is_signed_with_blog_token() ) {
-			return true;
-		}
-
-		$error_msg = esc_html__(
-			'You are not allowed to perform this action.',
-			'jetpack-social'
-		);
-
-		return new WP_Error( 'rest_forbidden', $error_msg, array( 'status' => rest_authorization_required_code() ) );
-	}
-}
diff --git a/projects/plugins/social/src/class-social-shares.php b/projects/plugins/social/src/class-social-shares.php
index c87b61f678b2..21e800242b5d 100644
--- a/projects/plugins/social/src/class-social-shares.php
+++ b/projects/plugins/social/src/class-social-shares.php
@@ -5,11 +5,12 @@
  * @package automattic/jetpack-social-plugin
  */
 
+use Automattic\Jetpack\Publicize\Share_Status;
+
 /**
  * Register the Jetpack Social Shares Class.
  */
 class Social_Shares {
-	const SOCIAL_SHARES_POST_META_KEY = '_publicize_shares';
 
 	/**
 	 * Return a list of of social shares.
@@ -19,23 +20,21 @@ class Social_Shares {
 	 * @return array
 	 */
 	public static function get_social_shares( $post_id = null ) {
-		if ( empty( $post_id ) ) {
-			$post    = get_post();
-			$post_id = $post->ID ?? 0;
-		}
 
-		if ( empty( $post_id ) ) {
+		$post = get_post( $post_id );
+
+		if ( empty( $post ) ) {
 			return array();
 		}
 
-		$shares = get_post_meta( $post_id, self::SOCIAL_SHARES_POST_META_KEY );
+		$shares = Share_Status::get_post_share_status( $post->ID, false );
 
-		if ( empty( $shares ) ) {
+		if ( empty( $shares['shares'] ) ) {
 			return array();
 		}
 
 		$succesful_shares = array_filter(
-			$shares[0],
+			$shares['shares'],
 			function ( $share ) {
 				return isset( $share['status'] ) && 'success' === $share['status'];
 			}
@@ -66,13 +65,12 @@ public static function get_the_social_shares( $post_id = 0 ) {
 		$html = '<div class="jp_social_shares">';
 
 		if ( ! empty( $shares ) ) {
-			$html .= '<h3>' . __( 'Also on:', 'jetpack-social' ) . '</h5><ul>';
+			$html .= '<h5>' . __( 'Also on:', 'jetpack-social' ) . '</h5><ul>';
 			foreach ( $shares as $service => $item ) {
 				$message = esc_url( $item['message'] );
 				$html   .= '<li><a href="' . $message . '">' . self::get_service_display_name( $service ) . '</a></li>';
 			}
 			$html .= '</ul>';
-			$html .= '</div>';
 		}
 
 		$html .= '</div>';


Reviews:
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T07:09:16Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T07:10:49Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-10T07:12:35Z
user: login: gmjuhasz
body: Ok this worked well with the wpcom changes, let's merge this then give it some more testing with trunk! 180073-ghe-Automattic/wpcom
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T07:28:55Z

Review comments:
path: projects/packages/publicize/tests/php/Share_Status_Test.php
user: login: gmjuhasz
body: Thanks for adding this!
created_at: 2025-04-10T07:09:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,13 +65,12 @@ public static function get_the_social_shares( $post_id = 0 ) {
 		$html = '<div class="jp_social_shares">';
 
 		if ( ! empty( $shares ) ) {
-			$html .= '<h3>' . __( 'Also on:', 'jetpack-social' ) . '</h5><ul>';
+			$html .= '<h5>' . __( 'Also on:', 'jetpack-social' ) . '</h5><ul>';
path: projects/plugins/social/src/class-social-shares.php
user: login: gmjuhasz
body: I wonder if this wanted to be h5 or h3 😄 
created_at: 2025-04-10T07:10:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,13 +65,12 @@ public static function get_the_social_shares( $post_id = 0 ) {
 		$html = '<div class="jp_social_shares">';
 
 		if ( ! empty( $shares ) ) {
-			$html .= '<h3>' . __( 'Also on:', 'jetpack-social' ) . '</h5><ul>';
+			$html .= '<h5>' . __( 'Also on:', 'jetpack-social' ) . '</h5><ul>';
path: projects/plugins/social/src/class-social-shares.php
user: login: manzoorwanijk
body: As per this https://github.com/Automattic/jetpack/pull/36328#discussion_r1537572473, it's `h5`
created_at: 2025-04-10T07:12:35Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-09T08:54:13Z
body: <!-- wpcom-reminder-comment -->
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

- To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate [the Jetpack Beta plugin](https://jetpack.com/download-jetpack-beta/). Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the `update/social-share-status-logic` branch.
- To test on Simple, run the following command on your sandbox:
```
bin/jetpack-downloader test jetpack update/social-share-status-logic
```

*Interested in more tips and information?*

- In your local development environment, use the `jetpack rsync` command to sync your changes to a WoA dev blog.
- Read more about our development workflow here: PCYsg-eg0-p2
- Figure out **when your changes will be shipped to customers** here: PCYsg-eg5-p2
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-09T08:54:15Z
body: **Thank you for your PR!**

When contributing to Jetpack, we have [a few suggestions](https://github.com/Automattic/jetpack/blob/trunk/.github/PULL_REQUEST_TEMPLATE.md) that can help us test and review your patch:<br>
- :white_check_mark: Include a description of your PR changes.<br>
- :white_check_mark: Add a "[Status]" label (In Progress, Needs Review, ...).<br>
- :white_check_mark: Add a "[Type]" label (Bug, Enhancement, Janitorial, Task).<br>
- :white_check_mark: Add testing instructions.<br>
- :white_check_mark: Specify whether this PR includes any changes to data or privacy.<br>
- :white_check_mark: Add changelog entries to affected projects<br>


This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation :robot:

******

**Follow this PR Review Process:**

1. Ensure all required checks appearing at the bottom of this PR are passing.
2. Make sure to test your changes on all platforms that it applies to. *You're responsible for the quality of the code you ship*.
3. You can use [GitHub's Reviewers functionality](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review) to request a review.
4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!
******

**Social plugin:**

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

author_association: CONTRIBUTOR
user: login: jp-launch-control[bot]
created_at: 2025-04-09T09:05:28Z
body: ### Code Coverage Summary

Coverage changed in 5 files.

| File | Coverage | Δ% | Δ Uncovered |
| :--- | :------: | :---: | :---: |
| projects/packages/publicize/src/class-share-status.php | 0/30 (0.00%) | 0.00% | 8 💔 |
| projects/packages/publicize/src/class-rest-controller.php | 124/230 (53.91%) | -0.95% | 4 💔 |
| projects/plugins/social/src/class-jetpack-social.php | 47/98 (47.96%) | -0.53% | 0 💚 |
| projects/plugins/social/src/class-social-shares.php | 0/47 (0.00%) | 0.00% | -3 💚 |
| projects/packages/publicize/src/rest-api/class-share-status-controller.php | 88/161 (54.66%) | 36.21% | -11 💚 |



[Full summary](https://jetpackcodecoverage.atomicsites.blog/prs/42978/) · [PHP report](https://jetpackcodecoverage.atomicsites.blog/prs/42978/php/) · [JS report](https://jetpackcodecoverage.atomicsites.blog/prs/42978/js/)

<sub>Coverage check overridden by https://github.com/Automattic/jetpack/labels/I%20don%27t%20care%20about%20code%20coverage%20for%20this%20PR.</sub>
author_association: NONE
user: login: manzoorwanijk
created_at: 2025-04-10T06:57:31Z
body: I have reverted the unit test changes because of the bug in Wordbless - https://github.com/Automattic/wordbless/issues/76
author_association: MEMBER