PluginProbe
ActivityPub / 1.0.0
ActivityPub v1.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 / transformer / class-post.php

class-post.php in ActivityPub 1.0.0, at includes/transformer/class-post.php

465 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Activitypub\Transformer;
3
4 use WP_Post;
5 use Activitypub\Collection\Users;
6 use Activitypub\Model\Blog_User;
7 use Activitypub\Activity\Base_Object;
8
9 use function Activitypub\esc_hashtag;
10 use function Activitypub\is_single_user;
11 use function Activitypub\get_rest_url_by_path;
12
13 /**
14 * WordPress Post Transformer
15 *
16 * The Post Transformer is responsible for transforming a WP_Post object into different othe
17 * Object-Types.
18 *
19 * Currently supported are:
20 *
21 * - Activitypub\Activity\Base_Object
22 */
23 class Post {
24
25 /**
26 * The WP_Post object.
27 *
28 * @var WP_Post
29 */
30 protected $wp_post;
31
32 /**
33 * The Allowed Tags, used in the content.
34 *
35 * @var array
36 */
37 protected $allowed_tags = array(
38 'a' => array(
39 'href' => array(),
40 'title' => array(),
41 'class' => array(),
42 'rel' => array(),
43 ),
44 'br' => array(),
45 'p' => array(
46 'class' => array(),
47 ),
48 'span' => array(
49 'class' => array(),
50 ),
51 'div' => array(
52 'class' => array(),
53 ),
54 'ul' => array(),
55 'ol' => array(
56 'reversed' => array(),
57 'start' => array(),
58 ),
59 'li' => array(
60 'value' => array(),
61 ),
62 'strong' => array(
63 'class' => array(),
64 ),
65 'b' => array(
66 'class' => array(),
67 ),
68 'i' => array(
69 'class' => array(),
70 ),
71 'em' => array(
72 'class' => array(),
73 ),
74 'blockquote' => array(),
75 'cite' => array(),
76 'code' => array(
77 'class' => array(),
78 ),
79 'pre' => array(
80 'class' => array(),
81 ),
82 );
83
84 /**
85 * Static function to Transform a WP_Post Object.
86 *
87 * This helps to chain the output of the Transformer.
88 *
89 * @param WP_Post $wp_post The WP_Post object
90 *
91 * @return void
92 */
93 public static function transform( WP_Post $wp_post ) {
94 return new static( $wp_post );
95 }
96
97 /**
98 *
99 *
100 * @param WP_Post $wp_post
101 */
102 public function __construct( WP_Post $wp_post ) {
103 $this->wp_post = $wp_post;
104 }
105
106 /**
107 * Transforms the WP_Post object to an ActivityPub Object
108 *
109 * @see \Activitypub\Activity\Base_Object
110 *
111 * @return \Activitypub\Activity\Base_Object The ActivityPub Object
112 */
113 public function to_object() {
114 $wp_post = $this->wp_post;
115 $object = new Base_Object();
116
117 $object->set_id( \esc_url( \get_permalink( $wp_post->ID ) ) );
118 $object->set_url( \esc_url( \get_permalink( $wp_post->ID ) ) );
119 $object->set_type( $this->get_object_type() );
120
121 $published = \strtotime( $wp_post->post_date_gmt );
122
123 $object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
124
125 $updated = \strtotime( $wp_post->post_modified_gmt );
126
127 if ( $updated > $published ) {
128 $object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
129 }
130
131 $object->set_attributed_to( $this->get_attributed_to() );
132 $object->set_content( $this->get_content() );
133 $object->set_content_map(
134 array(
135 \strstr( \get_locale(), '_', true ) => $this->get_content(),
136 )
137 );
138 $path = sprintf( 'users/%d/followers', intval( $wp_post->post_author ) );
139
140 $object->set_to(
141 array(
142 'https://www.w3.org/ns/activitystreams#Public',
143 get_rest_url_by_path( $path ),
144 )
145 );
146 $object->set_cc( $this->get_cc() );
147 $object->set_attachment( $this->get_attachments() );
148 $object->set_tag( $this->get_tags() );
149
150 return $object;
151 }
152
153 /**
154 * Returns the User-URL of the Author of the Post.
155 *
156 * If `single_user` mode is enabled, the URL of the Blog-User is returned.
157 *
158 * @return string The User-URL.
159 */
160 protected function get_attributed_to() {
161 if ( is_single_user() ) {
162 $user = new Blog_User();
163 return $user->get_url();
164 }
165
166 return Users::get_by_id( $this->wp_post->post_author )->get_url();
167 }
168
169 /**
170 * Generates all Image Attachments for a Post.
171 *
172 * @return array The Image Attachments.
173 */
174 protected function get_attachments() {
175 $max_images = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) );
176
177 $images = array();
178
179 // max images can't be negative or zero
180 if ( $max_images <= 0 ) {
181 return $images;
182 }
183
184 $id = $this->wp_post->ID;
185
186 $image_ids = array();
187
188 // list post thumbnail first if this post has one
189 if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
190 $image_ids[] = \get_post_thumbnail_id( $id );
191 --$max_images;
192 }
193
194 if ( $max_images > 0 ) {
195 // then list any image attachments
196 $query = new \WP_Query(
197 array(
198 'post_parent' => $id,
199 'post_status' => 'inherit',
200 'post_type' => 'attachment',
201 'post_mime_type' => 'image',
202 'order' => 'ASC',
203 'orderby' => 'menu_order ID',
204 'posts_per_page' => $max_images,
205 )
206 );
207 foreach ( $query->get_posts() as $attachment ) {
208 if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
209 $image_ids[] = $attachment->ID;
210 }
211 }
212 }
213
214 $image_ids = \array_unique( $image_ids );
215
216 // get URLs for each image
217 foreach ( $image_ids as $id ) {
218 $image_size = 'full';
219
220 /**
221 * Filter the image URL returned for each post.
222 *
223 * @param array|false $thumbnail The image URL, or false if no image is available.
224 * @param int $id The attachment ID.
225 * @param string $image_size The image size to retrieve. Set to 'full' by default.
226 */
227 $thumbnail = apply_filters(
228 'activitypub_get_image',
229 $this->get_image( $id, $image_size ),
230 $id,
231 $image_size
232 );
233
234 if ( $thumbnail ) {
235 $mimetype = \get_post_mime_type( $id );
236 $alt = \get_post_meta( $id, '_wp_attachment_image_alt', true );
237 $image = array(
238 'type' => 'Image',
239 'url' => $thumbnail[0],
240 'mediaType' => $mimetype,
241 );
242
243 if ( $alt ) {
244 $image['name'] = $alt;
245 }
246 $images[] = $image;
247 }
248 }
249
250 return $images;
251 }
252
253 /**
254 * Return details about an image attachment.
255 *
256 * @param int $id The attachment ID.
257 * @param string $image_size The image size to retrieve. Set to 'full' by default.
258 *
259 * @return array|false Array of image data, or boolean false if no image is available.
260 */
261 protected function get_image( $id, $image_size = 'full' ) {
262 /**
263 * Hook into the image retrieval process. Before image retrieval.
264 *
265 * @param int $id The attachment ID.
266 * @param string $image_size The image size to retrieve. Set to 'full' by default.
267 */
268 do_action( 'activitypub_get_image_pre', $id, $image_size );
269
270 $thumbnail = \wp_get_attachment_image_src( $id, $image_size );
271
272 /**
273 * Hook into the image retrieval process. After image retrieval.
274 *
275 * @param int $id The attachment ID.
276 * @param string $image_size The image size to retrieve. Set to 'full' by default.
277 */
278 do_action( 'activitypub_get_image_pre', $id, $image_size );
279
280 return $thumbnail;
281 }
282
283 /**
284 * Returns the ActivityStreams 2.0 Object-Type for a Post based on the
285 * settings and the Post-Type.
286 *
287 * @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
288 *
289 * @return string The Object-Type.
290 */
291 protected function get_object_type() {
292 if ( 'wordpress-post-format' !== \get_option( 'activitypub_object_type', 'note' ) ) {
293 return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) );
294 }
295
296 $post_type = \get_post_type( $this->wp_post );
297 switch ( $post_type ) {
298 case 'post':
299 $post_format = \get_post_format( $this->wp_post );
300 switch ( $post_format ) {
301 case 'aside':
302 case 'status':
303 case 'quote':
304 case 'note':
305 $object_type = 'Note';
306 break;
307 case 'gallery':
308 case 'image':
309 $object_type = 'Image';
310 break;
311 case 'video':
312 $object_type = 'Video';
313 break;
314 case 'audio':
315 $object_type = 'Audio';
316 break;
317 default:
318 $object_type = 'Article';
319 break;
320 }
321 break;
322 case 'page':
323 $object_type = 'Page';
324 break;
325 case 'attachment':
326 $mime_type = \get_post_mime_type();
327 $media_type = \preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type );
328 switch ( $media_type ) {
329 case 'audio':
330 $object_type = 'Audio';
331 break;
332 case 'video':
333 $object_type = 'Video';
334 break;
335 case 'image':
336 $object_type = 'Image';
337 break;
338 }
339 break;
340 default:
341 $object_type = 'Article';
342 break;
343 }
344
345 return $object_type;
346 }
347
348 /**
349 * Returns a list of Mentions, used in the Post.
350 *
351 * @see https://docs.joinmastodon.org/spec/activitypub/#Mention
352 *
353 * @return array The list of Mentions.
354 */
355 protected function get_cc() {
356 $cc = array();
357
358 $mentions = $this->get_mentions();
359 if ( $mentions ) {
360 foreach ( $mentions as $url ) {
361 $cc[] = $url;
362 }
363 }
364
365 return $cc;
366 }
367
368 /**
369 * Returns a list of Tags, used in the Post.
370 *
371 * This includes Hash-Tags and Mentions.
372 *
373 * @return array The list of Tags.
374 */
375 protected function get_tags() {
376 $tags = array();
377
378 $post_tags = \get_the_tags( $this->wp_post->ID );
379 if ( $post_tags ) {
380 foreach ( $post_tags as $post_tag ) {
381 $tag = array(
382 'type' => 'Hashtag',
383 'href' => \esc_url( \get_tag_link( $post_tag->term_id ) ),
384 'name' => esc_hashtag( $post_tag->name ),
385 );
386 $tags[] = $tag;
387 }
388 }
389
390 $mentions = $this->get_mentions();
391 if ( $mentions ) {
392 foreach ( $mentions as $mention => $url ) {
393 $tag = array(
394 'type' => 'Mention',
395 'href' => \esc_url( $url ),
396 'name' => \esc_html( $mention ),
397 );
398 $tags[] = $tag;
399 }
400 }
401
402 return $tags;
403 }
404
405 /**
406 * Returns the content for the ActivityPub Item.
407 *
408 * The content will be generated based on the user settings.
409 *
410 * @return string The content.
411 */
412 protected function get_content() {
413 global $post;
414
415 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
416 $post = $this->wp_post;
417 $content = $this->get_post_content_template();
418
419 // Fill in the shortcodes.
420 setup_postdata( $post );
421 $content = do_shortcode( $content );
422 wp_reset_postdata();
423
424 $content = \wp_kses( $content, $this->allowed_tags );
425 $content = \wpautop( $content );
426 $content = \preg_replace( '/[\n\r\t]/', '', $content );
427 $content = \trim( $content );
428
429 $content = \apply_filters( 'activitypub_the_content', $content, $post );
430 $content = \html_entity_decode( $content, \ENT_QUOTES, 'UTF-8' );
431
432 return $content;
433 }
434
435 /**
436 * Gets the template to use to generate the content of the activitypub item.
437 *
438 * @return string The Template.
439 */
440 protected function get_post_content_template() {
441 if ( 'excerpt' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
442 return "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
443 }
444
445 if ( 'title' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
446 return "[ap_title]\n\n[ap_permalink type=\"html\"]";
447 }
448
449 if ( 'content' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
450 return "[ap_content]\n\n[ap_hashtags]\n\n[ap_permalink type=\"html\"]";
451 }
452
453 return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
454 }
455
456 /**
457 * Helper function to get the @-Mentions from the post content.
458 *
459 * @return array The list of @-Mentions.
460 */
461 protected function get_mentions() {
462 return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_post->post_content, $this->wp_post );
463 }
464 }
465