1010use Activitypub \Activity \Activity ;
1111use Activitypub \Collection \Actors ;
1212use Activitypub \Collection \Followers ;
13+ use Activitypub \Collection \Inbox ;
1314use Activitypub \Collection \Remote_Actors ;
1415
1516use function Activitypub \add_to_outbox ;
@@ -27,6 +28,7 @@ class Quote_Request {
2728 public static function init () {
2829 \add_action ( 'activitypub_inbox_quote_request ' , array ( self ::class, 'handle_quote_request ' ), 10 , 2 );
2930 \add_action ( 'activitypub_rest_inbox_disallowed ' , array ( self ::class, 'handle_blocked_request ' ), 10 , 3 );
31+ \add_action ( 'delete_comment ' , array ( self ::class, 'handle_quote_delete ' ), 10 , 2 );
3032
3133 \add_filter ( 'activitypub_validate_object ' , array ( self ::class, 'validate_object ' ), 10 , 3 );
3234 }
@@ -100,6 +102,86 @@ public static function handle_blocked_request( $activity, $user_ids, $type ) {
100102 self ::queue_reject ( $ activity , $ user_id );
101103 }
102104
105+ /**
106+ * Handle deletion of a quote comment.
107+ *
108+ * When a local quote comment is deleted, send a Reject activity to revoke
109+ * the previously accepted QuoteRequest.
110+ *
111+ * @param int $comment_id The comment ID being deleted.
112+ * @param \WP_Comment $comment The comment object.
113+ */
114+ public static function handle_quote_delete ( $ comment_id , $ comment ) {
115+ // Only handle quote comments.
116+ if ( 'quote ' !== $ comment ->comment_type ) {
117+ return ;
118+ }
119+
120+ // Get the post being quoted.
121+ $ post_id = $ comment ->comment_post_ID ;
122+ if ( ! $ post_id ) {
123+ return ;
124+ }
125+
126+ // Get the instrument URL (the quote post URL) from comment meta.
127+ $ instrument_url = \get_comment_meta ( $ comment_id , 'source_url ' , true );
128+ if ( ! $ instrument_url ) {
129+ $ instrument_url = \get_comment_meta ( $ comment_id , 'source_id ' , true );
130+ }
131+
132+ if ( ! $ instrument_url ) {
133+ return ;
134+ }
135+
136+ // Get the post author (who accepted the quote).
137+ $ post = \get_post ( $ post_id );
138+ if ( ! $ post || ! $ post ->post_author ) {
139+ return ;
140+ }
141+
142+ /*
143+ * Try to retrieve the original QuoteRequest from the inbox.
144+ * For QuoteRequest activities, the inbox stores the instrument URL
145+ * in _activitypub_object_id, so we can query by that.
146+ */
147+ $ activity_object = null ;
148+ $ inbox_item = Inbox::get_by_type_and_object ( 'QuoteRequest ' , $ instrument_url );
149+
150+ if ( $ inbox_item instanceof \WP_Post ) {
151+ $ activity_object = \json_decode ( $ inbox_item ->post_content , true );
152+ if ( JSON_ERROR_NONE !== \json_last_error () ) {
153+ $ activity_object = null ;
154+ }
155+ }
156+
157+ // Fallback: If inbox item not found, reconstruct from available data.
158+ if ( ! $ activity_object ) {
159+ $ activity_object = array (
160+ 'type ' => 'QuoteRequest ' ,
161+ 'actor ' => $ comment ->comment_author_url ,
162+ 'object ' => \get_permalink ( $ post_id ),
163+ 'instrument ' => $ instrument_url ,
164+ 'published ' => \gmdate ( 'c ' ),
165+ );
166+ }
167+
168+ // Remove from _activitypub_quoted_by meta.
169+ \delete_post_meta ( $ post_id , '_activitypub_quoted_by ' , $ instrument_url );
170+
171+ // Send Reject activity to revoke the quote permission.
172+ self ::queue_reject ( $ activity_object , $ post ->post_author );
173+
174+ /**
175+ * Fires after a quote comment has been deleted and Reject activity sent.
176+ *
177+ * @param int $comment_id The deleted comment ID.
178+ * @param int $post_id The post ID that was quoted.
179+ * @param string $instrument_url The instrument URL (quote post).
180+ * @param array $activity_object The QuoteRequest activity that was rejected.
181+ */
182+ \do_action ( 'activitypub_quote_comment_deleted ' , $ comment_id , $ post_id , $ instrument_url , $ activity_object );
183+ }
184+
103185 /**
104186 * Send an Accept activity in response to the QuoteRequest.
105187 *
0 commit comments