PluginProbe
ActivityPub / 5.1.0
ActivityPub v5.1.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-collection.php

class-collection.php in ActivityPub 5.1.0, at includes/rest/class-collection.php

324 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 * Collections REST-Class file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Rest;
9
10 use WP_Error;
11 use WP_REST_Server;
12 use WP_REST_Response;
13 use Activitypub\Activity\Actor;
14 use Activitypub\Collection\Replies;
15 use Activitypub\Transformer\Factory;
16 use Activitypub\Activity\Base_Object;
17 use Activitypub\Collection\Actors;
18
19 use function Activitypub\esc_hashtag;
20 use function Activitypub\is_single_user;
21 use function Activitypub\get_rest_url_by_path;
22
23 /**
24 * ActivityPub Collections REST-Class.
25 *
26 * @author Matthias Pfefferle
27 *
28 * @see https://docs.joinmastodon.org/spec/activitypub/#featured
29 * @see https://docs.joinmastodon.org/spec/activitypub/#featuredTags
30 */
31 class Collection {
32 /**
33 * Initialize the class, registering WordPress hooks.
34 */
35 public static function init() {
36 self::register_routes();
37 }
38
39 /**
40 * Register routes.
41 */
42 public static function register_routes() {
43 \register_rest_route(
44 ACTIVITYPUB_REST_NAMESPACE,
45 '/(users|actors)/(?P<user_id>[\w\-\.]+)/collections/tags',
46 array(
47 array(
48 'methods' => WP_REST_Server::READABLE,
49 'callback' => array( self::class, 'tags_get' ),
50 'permission_callback' => '__return_true',
51 ),
52 )
53 );
54
55 \register_rest_route(
56 ACTIVITYPUB_REST_NAMESPACE,
57 '/(users|actors)/(?P<user_id>[\w\-\.]+)/collections/featured',
58 array(
59 array(
60 'methods' => WP_REST_Server::READABLE,
61 'callback' => array( self::class, 'featured_get' ),
62 'permission_callback' => '__return_true',
63 ),
64 )
65 );
66
67 \register_rest_route(
68 ACTIVITYPUB_REST_NAMESPACE,
69 '/collections/moderators',
70 array(
71 array(
72 'methods' => WP_REST_Server::READABLE,
73 'callback' => array( self::class, 'moderators_get' ),
74 'permission_callback' => '__return_true',
75 ),
76 )
77 );
78
79 \register_rest_route(
80 ACTIVITYPUB_REST_NAMESPACE,
81 '/(?P<type>[\w\-\.]+)s/(?P<id>[\w\-\.]+)/replies',
82 array(
83 array(
84 'methods' => WP_REST_Server::READABLE,
85 'callback' => array( self::class, 'replies_get' ),
86 'args' => self::request_parameters_for_replies(),
87 'permission_callback' => '__return_true',
88 ),
89 )
90 );
91 }
92
93 /**
94 * The endpoint for replies collections.
95 *
96 * @param \WP_REST_Request $request The request object.
97 *
98 * @return WP_REST_Response|\WP_Error The response object or WP_Error.
99 */
100 public static function replies_get( $request ) {
101 $type = $request->get_param( 'type' );
102 $id = (int) $request->get_param( 'id' );
103
104 // Get the WordPress object of that "owns" the requested replies.
105 switch ( $type ) {
106 case 'comment':
107 $wp_object = \get_comment( $id );
108 break;
109 case 'post':
110 default:
111 $wp_object = \get_post( $id );
112 break;
113 }
114
115 if ( ! isset( $wp_object ) || is_wp_error( $wp_object ) ) {
116 return new WP_Error(
117 'activitypub_replies_collection_does_not_exist',
118 \sprintf(
119 // translators: %s: The type (post, comment, etc.) for which no replies collection exists.
120 \__( 'No reply collection exists for the type %s.', 'activitypub' ),
121 $type
122 )
123 );
124 }
125
126 $page = (int) $request->get_param( 'page' );
127
128 // If the request parameter page is present get the CollectionPage otherwise the replies collection.
129 if ( isset( $page ) ) {
130 $response = Replies::get_collection_page( $wp_object, $page );
131 } else {
132 $response = Replies::get_collection( $wp_object );
133 }
134
135 if ( is_wp_error( $response ) ) {
136 return $response;
137 }
138
139 // Add ActivityPub Context.
140 $response = array_merge(
141 array( '@context' => Base_Object::JSON_LD_CONTEXT ),
142 $response
143 );
144
145 return new WP_REST_Response( $response, 200 );
146 }
147
148 /**
149 * The Featured Tags endpoint
150 *
151 * @param \WP_REST_Request $request The request object.
152 *
153 * @return WP_REST_Response|\WP_Error The response object or WP_Error.
154 */
155 public static function tags_get( $request ) {
156 $user_id = $request->get_param( 'user_id' );
157 $user = Actors::get_by_various( $user_id );
158
159 if ( is_wp_error( $user ) ) {
160 return $user;
161 }
162
163 $number = 4;
164
165 $tags = \get_terms(
166 array(
167 'taxonomy' => 'post_tag',
168 'orderby' => 'count',
169 'order' => 'DESC',
170 'number' => $number,
171 )
172 );
173
174 if ( is_wp_error( $tags ) ) {
175 $tags = array();
176 }
177
178 $response = array(
179 '@context' => Base_Object::JSON_LD_CONTEXT,
180 'id' => get_rest_url_by_path( sprintf( 'actors/%d/collections/tags', $user->get__id() ) ),
181 'type' => 'Collection',
182 'totalItems' => is_countable( $tags ) ? count( $tags ) : 0,
183 'items' => array(),
184 );
185
186 foreach ( $tags as $tag ) {
187 $response['items'][] = array(
188 'type' => 'Hashtag',
189 'href' => \esc_url( \get_tag_link( $tag ) ),
190 'name' => esc_hashtag( $tag->name ),
191 );
192 }
193
194 $rest_response = new WP_REST_Response( $response, 200 );
195 $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
196
197 return $rest_response;
198 }
199
200 /**
201 * Featured posts endpoint
202 *
203 * @param \WP_REST_Request $request The request object.
204 *
205 * @return WP_REST_Response|\WP_Error The response object or WP_Error.
206 */
207 public static function featured_get( $request ) {
208 $user_id = $request->get_param( 'user_id' );
209 $user = Actors::get_by_various( $user_id );
210
211 if ( is_wp_error( $user ) ) {
212 return $user;
213 }
214
215 $sticky_posts = \get_option( 'sticky_posts' );
216
217 if ( ! is_single_user() && Actors::BLOG_USER_ID === $user->get__id() ) {
218 $posts = array();
219 } elseif ( $sticky_posts && is_array( $sticky_posts ) ) {
220 // only show public posts.
221 $args = array(
222 'post__in' => $sticky_posts,
223 'ignore_sticky_posts' => 1,
224 'orderby' => 'date',
225 'order' => 'DESC',
226 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
227 'meta_query' => array(
228 array(
229 'key' => 'activitypub_content_visibility',
230 'compare' => 'NOT EXISTS',
231 ),
232 ),
233 );
234
235 if ( $user->get__id() > 0 ) {
236 $args['author'] = $user->get__id();
237 }
238
239 $posts = \get_posts( $args );
240 } else {
241 $posts = array();
242 }
243
244 $response = array(
245 '@context' => Base_Object::JSON_LD_CONTEXT,
246 'id' => get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $user_id ) ),
247 'type' => 'OrderedCollection',
248 'totalItems' => is_countable( $posts ) ? count( $posts ) : 0,
249 'orderedItems' => array(),
250 );
251
252 foreach ( $posts as $post ) {
253 $transformer = Factory::get_transformer( $post );
254
255 if ( \is_wp_error( $transformer ) ) {
256 continue;
257 }
258
259 $response['orderedItems'][] = $transformer->to_object()->to_array( false );
260 }
261
262 $rest_response = new WP_REST_Response( $response, 200 );
263 $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
264
265 return $rest_response;
266 }
267
268 /**
269 * Moderators endpoint.
270 *
271 * @return WP_REST_Response The response object.
272 */
273 public static function moderators_get() {
274 $response = array(
275 '@context' => Actor::JSON_LD_CONTEXT,
276 'id' => get_rest_url_by_path( 'collections/moderators' ),
277 'type' => 'OrderedCollection',
278 'orderedItems' => array(),
279 );
280
281 $users = Actors::get_collection();
282 $actors = array();
283
284 foreach ( $users as $user ) {
285 $actors[] = $user->get_id();
286 }
287
288 /**
289 * Filter the list of moderators.
290 *
291 * @param array $actors The list of moderators.
292 */
293 $response['orderedItems'] = apply_filters( 'activitypub_rest_moderators', $actors );
294
295 $rest_response = new WP_REST_Response( $response, 200 );
296 $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
297
298 return $rest_response;
299 }
300
301 /**
302 * The supported parameters.
303 *
304 * @return array list of parameters.
305 */
306 public static function request_parameters_for_replies() {
307 $params = array();
308
309 $params['type'] = array(
310 'required' => true,
311 'type' => 'string',
312 'enum' => array( 'post', 'comment' ),
313 );
314
315 $params['id'] = array(
316 'required' => true,
317 'type' => 'string',
318 'sanitize_callback' => 'sanitize_text_field',
319 );
320
321 return $params;
322 }
323 }
324