Repository: Automattic/jetpack (PHP)

Issue:
REST API: Author Details are bare and missing extra data that can be gained from site users.
In #7707 we removed a check on the `user_id` property of an `author` that we are providing details for when building API responses like `/sites/$site/posts/$post_ID/replies/​` the removal of this check means that we now rarely go into the else statement of the code branch (when you submit a comment as a logged in user core makes sure to copy accross the email address from the user into the comment data so we pretty much always have an email address):

```
	function get_author( $author, $show_email_and_ip = false ) {
		$ip_address = isset( $author->comment_author_IP ) ? $author->comment_author_IP : '';
		if ( isset( $author->comment_author_email ) ) {
			$ID          = 0;
			$login       = '';
			$email       = $author->comment_author_email;
			$name        = $author->comment_author;
			$first_name  = '';
			$last_name   = '';
			$URL         = $author->comment_author_url;
			$avatar_URL  = $this->api->get_avatar_url( $author );
			$profile_URL = 'https://en.gravatar.com/' . md5( strtolower( trim( $email ) ) );
			$nice        = '';
			$site_id     = -1;
			// Comment author URLs and Emails are sent through wp_kses() on save, which replaces "&" with "&amp;"
			// "&" is the only email/URL character altered by wp_kses()
			foreach ( array( 'email', 'URL' ) as $field ) {
				$$field = str_replace( '&amp;', '&', $$field );
			}
		} else {
			if ( isset( $author->user_id ) && $author->user_id ) {
```
https://github.com/Automattic/jetpack/blob/master/class.json-api-endpoints.php#L1105

This means that we no longer decorate the author object with site_ids etc in the response.

In the short term it might be better to revert this change as the fix was intended to address a small corner case whereby a comment may have been edited or the user updated with different data from what was present when the comment was made.

Ideally we would always treat the comments table as the source of truth but also decorate the author with extra data if there is a `user_id`.



Discussion: Related: https://github.com/wordpress-mobile/WordPress-Android/issues/8109
<p>This issue has been marked as stale. This happened because:</p>
<ul>
  <li>It has been inactive in the past 6 months.</li>
  <li>It hasn’t been labeled `[Pri] Blocker`, `[Pri] High`.</li>
</ul>
<p>No further action is needed. But it's worth checking if this ticket has clear reproduction steps and it is still reproducible. Feel free to close this issue if you think it's not valid anymore — if you do, please add a brief explanation.</p>




Fix (patch):
diff --git a/projects/plugins/jetpack/changelog/fix-author-details-are-missing b/projects/plugins/jetpack/changelog/fix-author-details-are-missing
new file mode 100644
index 0000000000000..42ee0ea85b33b
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/fix-author-details-are-missing
@@ -0,0 +1,3 @@
+Significance: patch
+Type: enhancement
+Comment: REST API: Enhance author details by including additional data when either user_id or site_id are available.
\ No newline at end of file
diff --git a/projects/plugins/jetpack/class.json-api-endpoints.php b/projects/plugins/jetpack/class.json-api-endpoints.php
index 794bd049d6545..5f76b89f85411 100644
--- a/projects/plugins/jetpack/class.json-api-endpoints.php
+++ b/projects/plugins/jetpack/class.json-api-endpoints.php
@@ -1388,109 +1388,118 @@ public function get_author( $author, $show_email_and_ip = false ) {
 		$nice       = null;
 		$url        = null;
 		$ip_address = isset( $author->comment_author_IP ) ? $author->comment_author_IP : '';
+		$site_id    = -1;
 
 		if ( isset( $author->comment_author_email ) ) {
-			$id          = ( isset( $author->user_id ) && $author->user_id ) ? $author->user_id : 0;
-			$login       = '';
-			$email       = $author->comment_author_email;
-			$name        = $author->comment_author;
-			$first_name  = '';
-			$last_name   = '';
-			$url         = $author->comment_author_url;
-			$avatar_url  = $this->api->get_avatar_url( $author );
-			$profile_url = 'https://gravatar.com/' . md5( strtolower( trim( $email ) ) );
-			$nice        = '';
-			$site_id     = -1;
+			$id         = empty( $author->user_id ) ? 0 : (int) $author->user_id;
+			$login      = '';
+			$email      = $author->comment_author_email;
+			$name       = $author->comment_author;
+			$first_name = '';
+			$last_name  = '';
+			$url        = $author->comment_author_url;
+			$avatar_url = $this->api->get_avatar_url( $author );
+			$nice       = '';
+
+			// Add additional user data to the response if a valid user ID is available.
+			if ( 0 < $id ) {
+				$user = get_user_by( 'id', $id );
+				if ( $user instanceof WP_User ) {
+					$login      = $user->user_login ?? '';
+					$first_name = $user->first_name ?? '';
+					$last_name  = $user->last_name ?? '';
+					$nice       = $user->user_nicename ?? '';
+				} else {
+					trigger_error( 'Unknown user', E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+				}
+			}
 
 			// Comment author URLs and Emails are sent through wp_kses() on save, which replaces "&" with "&amp;"
 			// "&" is the only email/URL character altered by wp_kses().
 			foreach ( array( 'email', 'url' ) as $field ) {
 				$$field = str_replace( '&amp;', '&', $$field );
 			}
-		} else {
-			if ( $author instanceof WP_User || isset( $author->user_email ) ) {
-				$author = $author->ID;
-			} elseif ( isset( $author->user_id ) && $author->user_id ) {
-				$author = $author->user_id;
-			} elseif ( isset( $author->post_author ) ) {
-				// then $author is a Post Object.
-				if ( ! $author->post_author ) {
-					return null;
-				}
-				/**
-				 * Filter whether the current site is a Jetpack site.
-				 *
-				 * @module json-api
-				 *
-				 * @since 3.3.0
-				 *
-				 * @param bool false Is the current site a Jetpack site. Default to false.
-				 * @param int get_current_blog_id() Blog ID.
-				 */
-				$is_jetpack = true === apply_filters( 'is_jetpack_site', false, get_current_blog_id() );
-				$post_id    = $author->ID;
-				if ( $is_jetpack && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
-					$id         = get_post_meta( $post_id, '_jetpack_post_author_external_id', true );
-					$email      = get_post_meta( $post_id, '_jetpack_author_email', true );
-					$login      = '';
-					$name       = get_post_meta( $post_id, '_jetpack_author', true );
-					$first_name = '';
-					$last_name  = '';
-					$url        = '';
-					$nice       = '';
-				} else {
-					$author = $author->post_author;
-				}
+		} elseif ( $author instanceof WP_User || isset( $author->user_email ) ) {
+			$author = $author->ID;
+		} elseif ( isset( $author->user_id ) && $author->user_id ) {
+			$author = $author->user_id;
+		} elseif ( isset( $author->post_author ) ) {
+			// then $author is a Post Object.
+			if ( ! $author->post_author ) {
+				return null;
 			}
+			/**
+			 * Filter whether the current site is a Jetpack site.
+			 *
+			 * @module json-api
+			 *
+			 * @since 3.3.0
+			 *
+			 * @param bool false Is the current site a Jetpack site. Default to false.
+			 * @param int get_current_blog_id() Blog ID.
+			 */
+			$is_jetpack = true === apply_filters( 'is_jetpack_site', false, get_current_blog_id() );
+			$post_id    = $author->ID;
+			if ( $is_jetpack && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
+				$id         = get_post_meta( $post_id, '_jetpack_post_author_external_id', true );
+				$email      = get_post_meta( $post_id, '_jetpack_author_email', true );
+				$login      = '';
+				$name       = get_post_meta( $post_id, '_jetpack_author', true );
+				$first_name = '';
+				$last_name  = '';
+				$url        = '';
+				$nice       = '';
+			} else {
+				$author = $author->post_author;
+			}
+		}
 
-			if ( ! isset( $id ) ) {
-				$user = get_user_by( 'id', $author );
-				if ( ! $user || is_wp_error( $user ) ) {
-					trigger_error( 'Unknown user', E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+		if ( ! isset( $id ) ) {
+			$user = get_user_by( 'id', $author );
+			if ( ! $user || is_wp_error( $user ) ) {
+				trigger_error( 'Unknown user', E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
 
-					return null;
-				}
-				$id         = $user->ID;
-				$email      = $user->user_email;
-				$login      = $user->user_login;
-				$name       = $user->display_name;
-				$first_name = $user->first_name;
-				$last_name  = $user->last_name;
-				$url        = $user->user_url;
-				$nice       = $user->user_nicename;
+				return null;
 			}
-			if ( defined( 'IS_WPCOM' ) && IS_WPCOM && ! $is_jetpack ) {
-				$site_id = -1;
-
-				/**
-				 * Allow customizing the blog ID returned with the author in WordPress.com REST API queries.
-				 *
-				 * @since 12.9
-				 *
-				 * @module json-api
-				 *
-				 * @param bool|int $active_blog  Blog ID, or false by default.
-				 * @param int      $id           User ID.
-				 */
-				$active_blog = apply_filters( 'wpcom_api_pre_get_active_blog_author', false, $id );
-				if ( false === $active_blog ) {
-					$active_blog = get_active_blog_for_user( $id );
-				}
-				if ( ! empty( $active_blog ) ) {
-					$site_id = $active_blog->blog_id;
-				}
-				if ( $site_id > -1 ) {
-					$site_visible = (
-						-1 !== (int) $active_blog->public ||
-						is_private_blog_user( $site_id, get_current_user_id() )
-					);
-				}
-				$profile_url = "https://gravatar.com/{$login}";
-			} else {
-				$profile_url = 'https://gravatar.com/' . md5( strtolower( trim( $email ) ) );
-				$site_id     = -1;
+			$id         = $user->ID;
+			$email      = $user->user_email;
+			$login      = $user->user_login;
+			$name       = $user->display_name;
+			$first_name = $user->first_name;
+			$last_name  = $user->last_name;
+			$url        = $user->user_url;
+			$nice       = $user->user_nicename;
+		}
+		if ( defined( 'IS_WPCOM' ) && IS_WPCOM && ! $is_jetpack ) {
+			/**
+			 * Allow customizing the blog ID returned with the author in WordPress.com REST API queries.
+			 *
+			 * @since 12.9
+			 *
+			 * @module json-api
+			 *
+			 * @param bool|int $active_blog  Blog ID, or false by default.
+			 * @param int      $id           User ID.
+			 */
+			$active_blog = apply_filters( 'wpcom_api_pre_get_active_blog_author', false, $id );
+			if ( false === $active_blog ) {
+				$active_blog = get_active_blog_for_user( $id );
 			}
+			if ( ! empty( $active_blog ) ) {
+				$site_id = $active_blog->blog_id;
+			}
+			if ( $site_id > - 1 ) {
+				$site_visible = (
+					- 1 !== (int) $active_blog->public ||
+					is_private_blog_user( $site_id, get_current_user_id() )
+				);
+			}
+			$profile_url = "https://gravatar.com/{$login}";
+		} else {
+			$profile_url = 'https://gravatar.com/' . md5( strtolower( trim( $email ) ) );
+		}
 
+		if ( ! isset( $avatar_url ) ) {
 			$avatar_url = $this->api->get_avatar_url( $email );
 		}