PluginProbe
ActivityPub / 7.0.0
ActivityPub v7.0.0
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / rest / class-outbox-controller.php

class-outbox-controller.php in ActivityPub 7.0.0, at includes/rest/class-outbox-controller.php

276 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Outbox Controller file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Rest;
9
10 use Activitypub\Activity\Base_Object;
11 use Activitypub\Collection\Actors;
12 use Activitypub\Collection\Outbox;
13 use function Activitypub\get_masked_wp_version;
14 use function Activitypub\get_rest_url_by_path;
15
16 /**
17 * ActivityPub Outbox Controller.
18 *
19 * @author Matthias Pfefferle
20 *
21 * @see https://www.w3.org/TR/activitypub/#outbox
22 */
23 class Outbox_Controller extends \WP_REST_Controller {
24 use Collection;
25
26 /**
27 * The namespace of this controller's route.
28 *
29 * @var string
30 */
31 protected $namespace = ACTIVITYPUB_REST_NAMESPACE;
32
33 /**
34 * The base of this controller's route.
35 *
36 * @var string
37 */
38 protected $rest_base = '(?:users|actors)/(?P<user_id>[\w\-\.]+)/outbox';
39
40 /**
41 * Register routes.
42 */
43 public function register_routes() {
44 \register_rest_route(
45 $this->namespace,
46 '/' . $this->rest_base,
47 array(
48 'args' => array(
49 'user_id' => array(
50 'description' => 'The ID of the user or actor.',
51 'type' => 'string',
52 'validate_callback' => array( $this, 'validate_user_id' ),
53 ),
54 ),
55 array(
56 'methods' => \WP_REST_Server::READABLE,
57 'callback' => array( $this, 'get_items' ),
58 'permission_callback' => array( 'Activitypub\Rest\Server', 'verify_signature' ),
59 'args' => array(
60 'page' => array(
61 'description' => 'Current page of the collection.',
62 'type' => 'integer',
63 'minimum' => 1,
64 // No default so we can differentiate between Collection and CollectionPage requests.
65 ),
66 'per_page' => array(
67 'description' => 'Maximum number of items to be returned in result set.',
68 'type' => 'integer',
69 'default' => 20,
70 'minimum' => 1,
71 'maximum' => 100,
72 ),
73 ),
74 ),
75 'schema' => array( $this, 'get_item_schema' ),
76 )
77 );
78 }
79
80 /**
81 * Validates the user_id parameter.
82 *
83 * @param mixed $user_id The user_id parameter.
84 * @return bool|\WP_Error True if the user_id is valid, WP_Error otherwise.
85 */
86 public function validate_user_id( $user_id ) {
87 $user = Actors::get_by_various( $user_id );
88 if ( \is_wp_error( $user ) ) {
89 return $user;
90 }
91
92 return true;
93 }
94
95 /**
96 * Retrieves a collection of outbox items.
97 *
98 * @param \WP_REST_Request $request Full details about the request.
99 * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
100 */
101 public function get_items( $request ) {
102 $page = $request->get_param( 'page' ) ?? 1;
103 $user = Actors::get_by_various( $request->get_param( 'user_id' ) );
104 $user_id = $user->get__id();
105
106 /**
107 * Action triggered prior to the ActivityPub profile being created and sent to the client.
108 *
109 * @param \WP_REST_Request $request The request object.
110 */
111 \do_action( 'activitypub_rest_outbox_pre', $request );
112
113 /**
114 * Filters the list of activity types to include in the outbox.
115 *
116 * @param string[] $activity_types The list of activity types.
117 */
118 $activity_types = apply_filters( 'rest_activitypub_outbox_activity_types', array( 'Announce', 'Create', 'Like', 'Update' ) );
119
120 $args = array(
121 'posts_per_page' => $request->get_param( 'per_page' ),
122 'author' => $user_id > 0 ? $user_id : null,
123 'paged' => $page,
124 'post_type' => Outbox::POST_TYPE,
125 'post_status' => 'any',
126
127 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
128 'meta_query' => array(
129 array(
130 'key' => '_activitypub_activity_actor',
131 'value' => Actors::get_type_by_id( $user_id ),
132 ),
133 ),
134 );
135
136 if ( get_current_user_id() !== $user_id && ! current_user_can( 'activitypub' ) ) {
137 $args['meta_query'][] = array(
138 'key' => '_activitypub_activity_type',
139 'value' => $activity_types,
140 'compare' => 'IN',
141 );
142
143 $args['meta_query'][] = array(
144 'relation' => 'OR',
145 array(
146 'key' => 'activitypub_content_visibility',
147 'compare' => 'NOT EXISTS',
148 ),
149 array(
150 'key' => 'activitypub_content_visibility',
151 'value' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
152 ),
153 );
154 }
155
156 $args = \apply_filters_deprecated( 'rest_activitypub_outbox_query', array( $args, $request ), '5.9.0', 'activitypub_rest_outbox_query' );
157
158 /**
159 * Filters WP_Query arguments when querying Outbox items via the REST API.
160 *
161 * Enables adding extra arguments or setting defaults for an outbox collection request.
162 *
163 * @param array $args Array of arguments for WP_Query.
164 * @param \WP_REST_Request $request The REST API request.
165 */
166 $args = \apply_filters( 'activitypub_rest_outbox_query', $args, $request );
167
168 $outbox_query = new \WP_Query();
169 $query_result = $outbox_query->query( $args );
170
171 $response = array(
172 '@context' => Base_Object::JSON_LD_CONTEXT,
173 'id' => get_rest_url_by_path( sprintf( 'actors/%d/outbox', $user_id ) ),
174 'generator' => 'https://wordpress.org/?v=' . get_masked_wp_version(),
175 'actor' => $user->get_id(),
176 'type' => 'OrderedCollection',
177 'totalItems' => $outbox_query->found_posts,
178 'orderedItems' => array(),
179 );
180
181 \update_postmeta_cache( \wp_list_pluck( $query_result, 'ID' ) );
182 foreach ( $query_result as $outbox_item ) {
183 if ( ! $outbox_item instanceof \WP_Post ) {
184 /**
185 * Action triggered when an outbox item is not a WP_Post.
186 *
187 * @param mixed $outbox_item The outbox item.
188 * @param array $args The arguments used to query the outbox.
189 * @param array $query_result The result of the query.
190 * @param \WP_REST_Request $request The request object.
191 */
192 do_action( 'activitypub_rest_outbox_item_error', $outbox_item, $args, $query_result, $request );
193
194 continue;
195 }
196
197 $response['orderedItems'][] = $this->prepare_item_for_response( $outbox_item, $request );
198 }
199
200 $response = $this->prepare_collection_response( $response, $request );
201 if ( is_wp_error( $response ) ) {
202 return $response;
203 }
204
205 /**
206 * Filter the ActivityPub outbox array.
207 *
208 * @param array $response The ActivityPub outbox array.
209 * @param \WP_REST_Request $request The request object.
210 */
211 $response = \apply_filters( 'activitypub_rest_outbox_array', $response, $request );
212
213 \do_action_deprecated( 'activitypub_outbox_post', array( $request ), 'unreleased', 'activitypub_rest_outbox_post' );
214
215 /**
216 * Action triggered after the ActivityPub profile has been created and sent to the client.
217 *
218 * @param \WP_REST_Request $request The request object.
219 */
220 \do_action( 'activitypub_rest_outbox_post', $request );
221
222 $response = \rest_ensure_response( $response );
223 $response->header( 'Content-Type', 'application/activity+json; charset=' . \get_option( 'blog_charset' ) );
224
225 return $response;
226 }
227
228 /**
229 * Prepares the item for the REST response.
230 *
231 * @param mixed $item WordPress representation of the item.
232 * @param \WP_REST_Request $request Request object.
233 * @return array Response object on success, or WP_Error object on failure.
234 */
235 public function prepare_item_for_response( $item, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
236 $activity = Outbox::get_activity( $item->ID );
237
238 return $activity->to_array( false );
239 }
240
241 /**
242 * Retrieves the outbox schema, conforming to JSON Schema.
243 *
244 * @return array Collection schema data.
245 */
246 public function get_item_schema() {
247 if ( $this->schema ) {
248 return $this->add_additional_fields_schema( $this->schema );
249 }
250
251 $item_schema = array(
252 'type' => 'object',
253 );
254
255 $schema = $this->get_collection_schema( $item_schema );
256
257 // Add outbox-specific properties.
258 $schema['title'] = 'outbox';
259 $schema['properties']['actor'] = array(
260 'description' => 'The actor who owns this outbox.',
261 'type' => 'string',
262 'format' => 'uri',
263 'required' => true,
264 );
265 $schema['properties']['generator'] = array(
266 'description' => 'The software used to generate the collection.',
267 'type' => 'string',
268 'format' => 'uri',
269 );
270
271 $this->schema = $schema;
272
273 return $this->add_additional_fields_schema( $this->schema );
274 }
275 }
276