Skip to content

Commit ec5bc2b

Browse files
authored
Add QuoteRequest activity handler support (#2240)
1 parent 5bba6cb commit ec5bc2b

19 files changed

+1028
-12
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Added support for QuoteRequest activities (FEP-044f), enabling proper handling, validation, and policy-based acceptance or rejection of quote requests.

FEDERATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The WordPress plugin largely follows ActivityPub's server-to-server specificatio
2121
- [FEP-b2b8: Long-form Text](https://codeberg.org/fediverse/fep/src/branch/main/fep/b2b8/fep-b2b8.md)
2222
- [FEP-7888: Demystifying the context property](https://codeberg.org/fediverse/fep/src/branch/main/fep/7888/fep-7888.md)
2323
- [FEP-844e: Capability discovery](https://codeberg.org/fediverse/fep/src/branch/main/fep/844e/fep-844e.md)
24+
- [FEP-044f: Consent-respecting quote posts](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md)
2425

2526
Partially supported FEPs
2627

includes/activity/class-activity.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
class Activity extends Base_Object {
2323
const JSON_LD_CONTEXT = array(
2424
'https://www.w3.org/ns/activitystreams',
25+
array(
26+
'toot' => 'http://joinmastodon.org/ns#',
27+
'QuoteRequest' => 'toot:QuoteRequest',
28+
),
2529
);
2630

2731
/**
@@ -50,6 +54,7 @@ class Activity extends Base_Object {
5054
'Listen',
5155
'Move',
5256
'Offer',
57+
'QuoteRequest', // @see https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md
5358
'Read',
5459
'Reject',
5560
'Remove',

includes/activity/class-generic-object.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* @method string|string[]|null get_origin() Gets the origin property of the object.
4040
* @method string|null get_preferred_username() Gets the preferred username of the object.
4141
* @method string|null get_published() Gets the date and time the object was published in ISO 8601 format.
42+
* @method string|null get_result() Gets the result property of the object.
4243
* @method bool|null get_sensitive() Gets the sensitive property of the object.
4344
* @method string|null get_summary() Gets the natural language summary of the object.
4445
* @method string[]|null get_summary_map() Gets the summary map property of the object.
@@ -70,6 +71,7 @@
7071
* @method Base_Object set_object( string|array|Base_Object|null $data ) Sets the direct object of the activity.
7172
* @method Base_Object set_origin( string|array|null $origin ) Sets the origin property of the object.
7273
* @method Base_Object set_published( string|null $published ) Sets the date and time the object was published in ISO 8601 format.
74+
* @method Base_Object set_result( string|null $result ) Sets the result property of the object.
7375
* @method Base_Object set_sensitive( bool|null $sensitive ) Sets the sensitive property of the object.
7476
* @method Base_Object set_summary( string $summary ) Sets the natural language summary of the object.
7577
* @method Base_Object set_summary_map( array|null $summary_map ) Sets the summary property of the object.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
/**
3+
* Quote_Authorization is an implementation of the QuoteAuthorization activity type,
4+
* as defined in FEP-044f (https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md#quoteauthorization).
5+
*
6+
* This class represents a QuoteAuthorization activity for ActivityPub implementations.
7+
*
8+
* @package Activitypub
9+
*/
10+
11+
namespace Activitypub\Activity\Extended_Object;
12+
13+
use Activitypub\Activity\Base_Object;
14+
15+
/**
16+
* Class representing a QuoteAuthorization activity.
17+
*
18+
* @see https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md#quoteauthorization
19+
*
20+
* @since unreleased
21+
*
22+
* @method Base_Object|string|array|null get_interacting_object() Gets the interacting object property of the object.
23+
* @method Base_Object|string|array|null get_interaction_target() Gets the interaction target property of the object.
24+
*
25+
* @method Quote_Authorization set_interacting_object( string|array|Base_Object|null $data ) Sets the interacting object property of the object.
26+
* @method Quote_Authorization set_interaction_target( string|array|Base_Object|null $data ) Sets the interaction target property of the object.
27+
*/
28+
class Quote_Authorization extends Base_Object {
29+
/**
30+
* The JSON-LD context for the object.
31+
*
32+
* @var array
33+
*/
34+
const JSON_LD_CONTEXT = array(
35+
'https://www.w3.org/ns/activitystreams',
36+
array(
37+
'QuoteAuthorization' => 'https://w3id.org/fep/044f#QuoteAuthorization',
38+
'gts' => 'https://gotosocial.org/ns#',
39+
'interactingObject' => array(
40+
'@id' => 'gts:interactingObject',
41+
'@type' => '@id',
42+
),
43+
'interactionTarget' => array(
44+
'@id' => 'gts:interactionTarget',
45+
'@type' => '@id',
46+
),
47+
),
48+
);
49+
50+
/**
51+
* The type of the object.
52+
*
53+
* @var string
54+
*/
55+
protected $type = 'QuoteAuthorization';
56+
57+
/**
58+
* The object that is being interacted with.
59+
*
60+
* @var Base_Object|string|array|null
61+
*/
62+
protected $interacting_object;
63+
64+
/**
65+
* The target of the interaction.
66+
*
67+
* @var Base_Object|string|array|null
68+
*/
69+
protected $interaction_target;
70+
}

includes/class-comment.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ public static function generate_id( $comment ) {
473473
}
474474

475475
// Generate URI based on comment ID.
476-
return \add_query_arg( 'c', $comment->comment_ID, \trailingslashit( \home_url() ) );
476+
return \add_query_arg( 'c', $comment->comment_ID, \home_url( '/' ) );
477477
}
478478

479479
/**

includes/class-handler.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Activitypub\Handler\Inbox;
1616
use Activitypub\Handler\Like;
1717
use Activitypub\Handler\Move;
18+
use Activitypub\Handler\Quote_Request;
1819
use Activitypub\Handler\Reject;
1920
use Activitypub\Handler\Undo;
2021
use Activitypub\Handler\Update;
@@ -42,6 +43,7 @@ public static function register_handlers() {
4243
Inbox::init();
4344
Like::init();
4445
Move::init();
46+
Quote_Request::init();
4547
Reject::init();
4648
Undo::init();
4749
Update::init();

includes/class-query.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Activitypub;
99

10+
use Activitypub\Activity\Extended_Object\Quote_Authorization;
1011
use Activitypub\Collection\Actors;
1112
use Activitypub\Collection\Outbox;
1213
use Activitypub\Transformer\Factory;
@@ -138,6 +139,10 @@ public function get_activitypub_object_id() {
138139
private function prepare_activitypub_data() {
139140
$queried_object = $this->get_queried_object();
140141

142+
if ( $queried_object instanceof \WP_Post && \get_query_var( 'stamp' ) ) {
143+
return $this->maybe_get_stamp();
144+
}
145+
141146
// Check for Outbox Activity.
142147
if (
143148
$queried_object instanceof \WP_Post &&
@@ -311,7 +316,7 @@ public function is_activitypub_request() {
311316
*/
312317
public function should_negotiate_content() {
313318
$return = false;
314-
$always_negotiate = array( 'p', 'c', 'author', 'actor', 'preview', 'activitypub' );
319+
$always_negotiate = array( 'p', 'c', 'author', 'actor', 'stamp', 'preview', 'activitypub' );
315320
$url = \wp_parse_url( $this->get_request_url(), PHP_URL_QUERY );
316321
$query = array();
317322
\wp_parse_str( $url, $query );
@@ -368,4 +373,46 @@ public function is_old_host_request() {
368373
public function set_old_host_request( $state = true ) {
369374
$this->is_old_host_request = $state;
370375
}
376+
377+
/**
378+
* Maybe get a QuoteAuthorization object from a stamp.
379+
*
380+
* @return bool True if the object was prepared, false otherwise.
381+
*/
382+
private function maybe_get_stamp() {
383+
require_once ABSPATH . 'wp-admin/includes/post.php';
384+
385+
$stamp = \get_query_var( 'stamp' );
386+
$meta = \get_post_meta_by_id( (int) $stamp );
387+
388+
if ( ! $meta ) {
389+
return false;
390+
}
391+
392+
$post = $this->get_queried_object();
393+
$user_uri = get_user_id( $post->post_author );
394+
395+
if ( ! $user_uri ) {
396+
return false;
397+
}
398+
399+
$stamp_uri = \add_query_arg(
400+
array(
401+
'p' => $post->ID,
402+
'stamp' => $meta->meta_id,
403+
),
404+
\home_url( '/' )
405+
);
406+
407+
$activitypub_object = new Quote_Authorization();
408+
$activitypub_object->set_id( $stamp_uri );
409+
$activitypub_object->set_attributed_to( $user_uri );
410+
$activitypub_object->set_interacting_object( $meta->meta_value );
411+
$activitypub_object->set_interaction_target( get_post_id( $post->ID ) );
412+
413+
$this->activitypub_object = $activitypub_object;
414+
$this->activitypub_object_id = $activitypub_object->get_id();
415+
416+
return true;
417+
}
371418
}

includes/class-router.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ public static function redirect_canonical( $redirect_url, $requested_url ) {
199199

200200
$query_params = \wp_parse_args( $query );
201201
unset( $query_params['activitypub'] );
202+
unset( $query_params['stamp'] );
202203

203204
if ( 1 !== count( $query_params ) ) {
204205
return $redirect_url;
@@ -273,6 +274,7 @@ public static function add_query_vars( $vars ) {
273274
$vars[] = 'preview';
274275
$vars[] = 'author';
275276
$vars[] = 'actor';
277+
$vars[] = 'stamp';
276278
$vars[] = 'type';
277279
$vars[] = 'c';
278280
$vars[] = 'p';

includes/functions.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,14 +1334,15 @@ function get_user_id( $id ) {
13341334
* @return string The ActivityPub ID (a URL) of the Post.
13351335
*/
13361336
function get_post_id( $id ) {
1337-
$post = get_post( $id );
1337+
$last_legacy_id = (int) \get_option( 'activitypub_last_post_with_permalink_as_id', 0 );
1338+
$post_id = (int) $id;
13381339

1339-
if ( ! $post ) {
1340-
return false;
1340+
if ( $post_id > $last_legacy_id ) {
1341+
// Generate URI based on post ID.
1342+
return \add_query_arg( 'p', $post_id, \home_url( '/' ) );
13411343
}
13421344

1343-
$transformer = new Post( $post );
1344-
return $transformer->get_id();
1345+
return \get_permalink( $post_id );
13451346
}
13461347

13471348
/**

0 commit comments

Comments
 (0)