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-collections-controller.php

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

273 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Collections_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\Model\Application;
13 use Activitypub\Model\Blog;
14 use Activitypub\Model\User;
15 use Activitypub\Transformer\Factory;
16
17 use function Activitypub\esc_hashtag;
18 use function Activitypub\is_single_user;
19 use function Activitypub\get_rest_url_by_path;
20
21 /**
22 * Collections_Controller class.
23 *
24 * @author Matthias Pfefferle
25 *
26 * @see https://docs.joinmastodon.org/spec/activitypub/#featured
27 * @see https://docs.joinmastodon.org/spec/activitypub/#featuredTags
28 * @see https://www.w3.org/TR/activitypub/#collections
29 */
30 class Collections_Controller extends Actors_Controller {
31 use Collection;
32
33 /**
34 * Register routes.
35 */
36 public function register_routes() {
37 \register_rest_route(
38 $this->namespace,
39 '/' . $this->rest_base . '/collections/(?P<type>[\w\-\.]+)',
40 array(
41 'args' => array(
42 'user_id' => array(
43 'description' => 'The user ID or username.',
44 'type' => 'string',
45 'required' => true,
46 ),
47 'type' => array(
48 'description' => 'The type of collection to query.',
49 'type' => 'string',
50 'enum' => array( 'tags', 'featured' ),
51 'required' => true,
52 ),
53 ),
54 array(
55 'methods' => \WP_REST_Server::READABLE,
56 'callback' => array( $this, 'get_items' ),
57 'permission_callback' => '__return_true',
58 'args' => array(
59 'page' => array(
60 'description' => 'Current page of the collection.',
61 'type' => 'integer',
62 'minimum' => 1,
63 // No default so we can differentiate between Collection and CollectionPage requests.
64 ),
65 'per_page' => array(
66 'description' => 'Maximum number of items to be returned in result set.',
67 'type' => 'integer',
68 'default' => 20,
69 'minimum' => 1,
70 ),
71 ),
72 ),
73 'schema' => array( $this, 'get_item_schema' ),
74 )
75 );
76 }
77
78 /**
79 * Retrieves a collection of featured tags.
80 *
81 * @param \WP_REST_Request $request The request object.
82 *
83 * @return \WP_REST_Response|\WP_Error Response object or WP_Error object.
84 */
85 public function get_items( $request ) {
86 $user_id = $request->get_param( 'user_id' );
87 $user = Actors::get_by_various( $user_id );
88
89 if ( \is_wp_error( $user ) ) {
90 return $user;
91 }
92
93 switch ( $request->get_param( 'type' ) ) {
94 case 'tags':
95 $response = $this->get_tags( $request, $user );
96 break;
97
98 case 'featured':
99 $response = $this->get_featured( $request, $user );
100 break;
101
102 default:
103 $response = new \WP_Error( 'rest_unknown_collection_type', 'Unknown collection type.', array( 'status' => 404 ) );
104 }
105
106 if ( \is_wp_error( $response ) ) {
107 return $response;
108 }
109
110 $response = \rest_ensure_response( $response );
111 $response->header( 'Content-Type', 'application/activity+json; charset=' . \get_option( 'blog_charset' ) );
112
113 return $response;
114 }
115
116 /**
117 * Retrieves a collection of featured tags.
118 *
119 * @param \WP_REST_Request $request The request object.
120 * @param User|Blog|Application $user Actor.
121 *
122 * @return array Collection of featured tags.
123 */
124 public function get_tags( $request, $user ) {
125 $tags = \get_terms(
126 array(
127 'taxonomy' => 'post_tag',
128 'orderby' => 'count',
129 'order' => 'DESC',
130 'number' => 4,
131 )
132 );
133
134 if ( \is_wp_error( $tags ) ) {
135 $tags = array();
136 }
137
138 $response = array(
139 '@context' => Base_Object::JSON_LD_CONTEXT,
140 'id' => get_rest_url_by_path( sprintf( 'actors/%d/collections/tags', $user->get__id() ) ),
141 'type' => 'Collection',
142 'totalItems' => \is_countable( $tags ) ? \count( $tags ) : 0,
143 'items' => array(),
144 );
145
146 foreach ( $tags as $tag ) {
147 $response['items'][] = array(
148 'type' => 'Hashtag',
149 'href' => \esc_url( \get_tag_link( $tag ) ),
150 'name' => esc_hashtag( $tag->name ),
151 );
152 }
153
154 return $this->prepare_collection_response( $response, $request );
155 }
156
157 /**
158 * Retrieves a collection of featured posts.
159 *
160 * @param \WP_REST_Request $request The request object.
161 * @param User|Blog|Application $user Actor.
162 *
163 * @return array Collection of featured posts.
164 */
165 public function get_featured( $request, $user ) {
166 $posts = array();
167
168 if ( is_single_user() || Actors::BLOG_USER_ID !== $user->get__id() ) {
169 $sticky_posts = \get_option( 'sticky_posts' );
170
171 if ( $sticky_posts && is_array( $sticky_posts ) ) {
172 // Only show public posts.
173 $args = array(
174 'post__in' => $sticky_posts,
175 'ignore_sticky_posts' => 1,
176 'orderby' => 'date',
177 'order' => 'DESC',
178 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
179 'meta_query' => array(
180 array(
181 'key' => 'activitypub_content_visibility',
182 'compare' => 'NOT EXISTS',
183 ),
184 ),
185 );
186
187 if ( $user->get__id() > 0 ) {
188 $args['author'] = $user->get__id();
189 }
190
191 $posts = \get_posts( $args );
192 }
193 }
194
195 $response = array(
196 '@context' => Base_Object::JSON_LD_CONTEXT,
197 'id' => get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $request->get_param( 'user_id' ) ) ),
198 'type' => 'OrderedCollection',
199 'totalItems' => \is_countable( $posts ) ? \count( $posts ) : 0,
200 'orderedItems' => array(),
201 );
202
203 foreach ( $posts as $post ) {
204 $transformer = Factory::get_transformer( $post );
205
206 if ( \is_wp_error( $transformer ) ) {
207 continue;
208 }
209
210 $response['orderedItems'][] = $transformer->to_object()->to_array( false );
211 }
212
213 return $this->prepare_collection_response( $response, $request );
214 }
215
216 /**
217 * Retrieves the schema for the Collections endpoint.
218 *
219 * @return array Schema data.
220 */
221 public function get_item_schema() {
222 if ( $this->schema ) {
223 return $this->add_additional_fields_schema( $this->schema );
224 }
225
226 $schema = $this->get_collection_schema();
227
228 // Add collections-specific properties.
229 $schema['title'] = 'featured';
230 $schema['properties']['generator'] = array(
231 'description' => 'The software used to generate the collection.',
232 'type' => 'string',
233 'format' => 'uri',
234 );
235 $schema['properties']['oneOf'] = array(
236 'orderedItems' => array(
237 'type' => 'array',
238 'items' => array(
239 'type' => 'object',
240 ),
241 ),
242 'items' => array(
243 'type' => 'array',
244 'items' => array(
245 'type' => 'object',
246 'properties' => array(
247 'type' => array(
248 'type' => 'string',
249 'enum' => array( 'Hashtag' ),
250 'required' => true,
251 ),
252 'href' => array(
253 'type' => 'string',
254 'format' => 'uri',
255 'required' => true,
256 ),
257 'name' => array(
258 'type' => 'string',
259 'required' => true,
260 ),
261 ),
262 ),
263 ),
264 );
265
266 unset( $schema['properties']['orderedItems'] );
267
268 $this->schema = $schema;
269
270 return $this->add_additional_fields_schema( $this->schema );
271 }
272 }
273