PluginProbe
ActivityPub / 5.3.0
ActivityPub v5.3.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 / class-query.php

class-query.php in ActivityPub 5.3.0, at includes/class-query.php

290 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Query class.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub;
9
10 use Activitypub\Collection\Actors;
11 use Activitypub\Collection\Outbox;
12 use Activitypub\Transformer\Factory;
13
14 /**
15 * Singleton class to handle and store the ActivityPub query.
16 */
17 class Query {
18
19 /**
20 * The singleton instance.
21 *
22 * @var Query
23 */
24 private static $instance;
25
26 /**
27 * The ActivityPub object.
28 *
29 * @link https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object
30 *
31 * @var object
32 */
33 private $activitypub_object;
34
35 /**
36 * The ActivityPub object ID.
37 *
38 * @link https://www.w3.org/TR/activitystreams-vocabulary/#dfn-id
39 *
40 * @var string
41 */
42 private $activitypub_object_id;
43
44 /**
45 * Whether the current request is an ActivityPub request.
46 *
47 * @var bool
48 */
49 private $is_activitypub_request;
50
51 /**
52 * The constructor.
53 */
54 private function __construct() {
55 // Do nothing.
56 }
57
58 /**
59 * The destructor.
60 */
61 public function __destruct() {
62 self::$instance = null;
63 }
64
65 /**
66 * Get the singleton instance.
67 *
68 * @return Query The singleton instance.
69 */
70 public static function get_instance() {
71 if ( ! isset( self::$instance ) ) {
72 self::$instance = new self();
73 }
74
75 return self::$instance;
76 }
77
78 /**
79 * Get the ActivityPub object.
80 *
81 * @return object The ActivityPub object.
82 */
83 public function get_activitypub_object() {
84 if ( $this->activitypub_object ) {
85 return $this->activitypub_object;
86 }
87
88 $queried_object = $this->get_queried_object();
89
90 // Check for Outbox Activity.
91 if (
92 $queried_object instanceof \WP_Post &&
93 Outbox::POST_TYPE === $queried_object->post_type
94 ) {
95 $activitypub_object = Outbox::maybe_get_activity( $queried_object );
96
97 // Check if the Outbox Activity is public.
98 if ( ! \is_wp_error( $activitypub_object ) ) {
99 $this->activitypub_object = $activitypub_object;
100
101 return $this->activitypub_object;
102 }
103 }
104
105 if ( ! $queried_object ) {
106 // If the object is not a valid ActivityPub object, try to get a virtual object.
107 $activitypub_object = $this->maybe_get_virtual_object();
108
109 if ( $activitypub_object ) {
110 $this->activitypub_object = $activitypub_object;
111
112 return $this->activitypub_object;
113 }
114 }
115
116 $transformer = Factory::get_transformer( $queried_object );
117
118 if ( $transformer && ! \is_wp_error( $transformer ) ) {
119 $this->activitypub_object = $transformer->to_object();
120 }
121
122 return $this->activitypub_object;
123 }
124
125 /**
126 * Get the ActivityPub object ID.
127 *
128 * @return string The ActivityPub object ID.
129 */
130 public function get_activitypub_object_id() {
131 if ( $this->activitypub_object_id ) {
132 return $this->activitypub_object_id;
133 }
134
135 if ( $this->activitypub_object ) {
136 $this->activitypub_object_id = $this->activitypub_object->get_id();
137
138 return $this->activitypub_object_id;
139 }
140
141 return $this->get_activitypub_object();
142 }
143
144 /**
145 * Get the queried object.
146 *
147 * This adds support for Comments by `?c=123` IDs and Users by `?author=123` and `@username` IDs.
148 *
149 * @return \WP_Term|\WP_Post_Type|\WP_Post|\WP_User|\WP_Comment|null The queried object.
150 */
151 public function get_queried_object() {
152 $queried_object = \get_queried_object();
153
154 // Check Comment by ID.
155 if ( ! $queried_object ) {
156 $comment_id = \get_query_var( 'c' );
157 if ( $comment_id ) {
158 $queried_object = \get_comment( $comment_id );
159 }
160 }
161
162 // Check Post by ID (works for custom post types).
163 if ( ! $queried_object ) {
164 $post_id = \get_query_var( 'p' );
165 if ( $post_id ) {
166 $queried_object = \get_post( $post_id );
167 }
168 }
169
170 // Try to get Author by ID.
171 if ( ! $queried_object ) {
172 $url = $this->get_request_url();
173 $author_id = url_to_authorid( $url );
174 if ( $author_id ) {
175 $queried_object = \get_user_by( 'id', $author_id );
176 }
177 }
178
179 /**
180 * Filters the queried object.
181 *
182 * @param \WP_Term|\WP_Post_Type|\WP_Post|\WP_User|\WP_Comment|null $queried_object The queried object.
183 */
184 return apply_filters( 'activitypub_queried_object', $queried_object );
185 }
186
187 /**
188 * Get the virtual object.
189 *
190 * Virtual objects are objects that are not stored in the database, but are created on the fly.
191 * The plugins currently supports two virtual objects: The Blog-Actor and the Application-Actor.
192 *
193 * @see \Activitypub\Blog
194 * @see \Activitypub\Application
195 *
196 * @return object|null The virtual object.
197 */
198 protected function maybe_get_virtual_object() {
199 $url = $this->get_request_url();
200
201 if ( ! $url ) {
202 return null;
203 }
204
205 $author_id = url_to_authorid( $url );
206
207 if ( ! is_numeric( $author_id ) ) {
208 return null;
209 }
210
211 $user = Actors::get_by_id( $author_id );
212
213 if ( \is_wp_error( $user ) || ! $user ) {
214 return null;
215 }
216
217 return $user;
218 }
219
220 /**
221 * Get the request URL.
222 *
223 * @return string|null The request URL.
224 */
225 protected function get_request_url() {
226 if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
227 return null;
228 }
229
230 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
231 $url = \wp_unslash( $_SERVER['REQUEST_URI'] );
232 $url = \WP_Http::make_absolute_url( $url, \home_url() );
233 $url = \sanitize_url( $url );
234
235 return $url;
236 }
237
238 /**
239 * Check if the current request is an ActivityPub request.
240 *
241 * @return bool True if the request is an ActivityPub request, false otherwise.
242 */
243 public function is_activitypub_request() {
244 if ( isset( $this->is_activitypub_request ) ) {
245 return $this->is_activitypub_request;
246 }
247
248 global $wp_query;
249
250 // One can trigger an ActivityPub request by adding `?activitypub` to the URL.
251 if (
252 isset( $wp_query->query_vars['activitypub'] ) ||
253 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
254 isset( $_GET['activitypub'] )
255 ) {
256 defined( 'ACTIVITYPUB_REQUEST' ) || \define( 'ACTIVITYPUB_REQUEST', true );
257 $this->is_activitypub_request = true;
258
259 return true;
260 }
261
262 /*
263 * The other (more common) option to make an ActivityPub request
264 * is to send an Accept header.
265 */
266 if ( isset( $_SERVER['HTTP_ACCEPT'] ) ) {
267 $accept = \sanitize_text_field( \wp_unslash( $_SERVER['HTTP_ACCEPT'] ) );
268
269 /*
270 * $accept can be a single value, or a comma separated list of values.
271 * We want to support both scenarios,
272 * and return true when the header includes at least one of the following:
273 * - application/activity+json
274 * - application/ld+json
275 * - application/json
276 */
277 if ( \preg_match( '/(application\/(ld\+json|activity\+json|json))/i', $accept ) ) {
278 defined( 'ACTIVITYPUB_REQUEST' ) || \define( 'ACTIVITYPUB_REQUEST', true );
279 $this->is_activitypub_request = true;
280
281 return true;
282 }
283 }
284
285 $this->is_activitypub_request = false;
286
287 return false;
288 }
289 }
290