diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php
index fa947907a4..34d8782672 100644
--- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php
+++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php
@@ -24,6 +24,13 @@
*/
class Upload_Handler {
+ /**
+ * How the reply confirming an author's upload starts, in the review conversation.
+ *
+ * @var string
+ */
+ const UPLOAD_CONFIRMATION = 'This is an automated message to confirm that we have received your updated plugin file.';
+
/**
* Path to temporary plugin folder.
*
@@ -1017,7 +1024,7 @@ public static function update_review_email( $post, $attachment ) {
return false;
}
- $text = "This is an automated message to confirm that we have received your updated plugin file.\n\n";
+ $text = self::UPLOAD_CONFIRMATION . "\n\n";
$text .= sprintf(
"File updated by %s, version %s.\n",
wp_get_current_user()->user_login,
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-abilities/plugins/plugin-directory/tools/class-get-plugin-status.php b/wordpress.org/public_html/wp-content/plugins/wporg-abilities/plugins/plugin-directory/tools/class-get-plugin-status.php
index 91d6bc86e0..31af7dbe80 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-abilities/plugins/plugin-directory/tools/class-get-plugin-status.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-abilities/plugins/plugin-directory/tools/class-get-plugin-status.php
@@ -13,6 +13,7 @@
use WordPressdotorg\Plugin_Directory\Clients\FreeScout as FreeScout_Client;
use WordPressdotorg\Plugin_Directory\Clients\HelpScout as HelpScout_Client;
use WordPressdotorg\Plugin_Directory\Plugin_Directory;
+use WordPressdotorg\Plugin_Directory\Shortcodes\Upload_Handler;
use WordPressdotorg\Plugin_Directory\Template;
use WordPressdotorg\Plugin_Directory\Tools\Helpscout;
@@ -275,7 +276,7 @@ private static function get_feedback( \WP_Post $post, bool $full_history = false
// When only the latest reviewer message is needed, find it directly.
if ( ! $full_history ) {
foreach ( $threads as $thread ) {
- if ( in_array( $thread->type ?? '', array( 'reply', 'message' ), true ) ) {
+ if ( 'reviewer' === self::get_sender( $thread ) ) {
$body = $thread->body ?? '';
if ( $body ) {
$body = self::html_to_text( $body );
@@ -301,7 +302,8 @@ private static function get_feedback( \WP_Post $post, bool $full_history = false
// Full history: process all threads.
foreach ( $threads as $thread ) {
// Skip internal notes and system events — only include customer and agent messages.
- if ( ! in_array( $thread->type ?? '', array( 'customer', 'reply', 'message' ), true ) ) {
+ $from = self::get_sender( $thread );
+ if ( ! $from ) {
continue;
}
@@ -313,7 +315,7 @@ private static function get_feedback( \WP_Post $post, bool $full_history = false
}
$feedback[] = array(
- 'from' => 'customer' === $thread->type ? 'author' : 'reviewer',
+ 'from' => $from,
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- HelpScout API property.
'date' => $thread->createdAt ?? '',
'body' => $body,
@@ -325,6 +327,32 @@ private static function get_feedback( \WP_Post $post, bool $full_history = false
return array_reverse( $feedback );
}
+ /**
+ * Who a thread is from.
+ *
+ * The plugin directory confirms an author's upload with a reply of its own, in either helpdesk, which is the
+ * author's doing rather than the reviewer's feedback.
+ *
+ * @param object $thread A thread, shaped like HelpScout's.
+ * @return string `author` or `reviewer`; empty for notes and system events.
+ */
+ private static function get_sender( object $thread ): string {
+ $type = $thread->type ?? '';
+
+ if ( 'customer' === $type ) {
+ return 'author';
+ }
+
+ if ( ! in_array( $type, array( 'reply', 'message' ), true ) ) {
+ return '';
+ }
+
+ // At the start only, so a reviewer quoting it is still the reviewer.
+ $text = trim( wp_strip_all_tags( (string) ( $thread->body ?? '' ) ) );
+
+ return str_starts_with( $text, Upload_Handler::UPLOAD_CONFIRMATION ) ? 'author' : 'reviewer';
+ }
+
/**
* Strip boilerplate from review feedback, keeping only the issues sections.
*