PluginProbe
ActivityPub / 5.3.1
ActivityPub v5.3.1
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 / collection / class-replies.php

class-replies.php in ActivityPub 5.3.1, at includes/collection/class-replies.php

216 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Replies collection file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Collection;
9
10 use WP_Post;
11 use WP_Comment;
12 use WP_Error;
13
14 use Activitypub\Comment;
15 use Activitypub\Transformer\Post as PostTransformer;
16 use Activitypub\Transformer\Comment as CommentTransformer;
17
18 use function Activitypub\is_post_disabled;
19 use function Activitypub\is_local_comment;
20 use function Activitypub\get_rest_url_by_path;
21
22 /**
23 * Class containing code for getting replies Collections and CollectionPages of posts and comments.
24 */
25 class Replies {
26 /**
27 * Build base arguments for fetching the comments of either a WordPress post or comment.
28 *
29 * @param WP_Post|WP_Comment|WP_Error $wp_object The post or comment to fetch replies for on success.
30 */
31 private static function build_args( $wp_object ) {
32 $args = array(
33 'status' => 'approve',
34 'orderby' => 'comment_date_gmt',
35 'order' => 'ASC',
36 'type' => 'comment',
37 );
38
39 if ( $wp_object instanceof WP_Post ) {
40 $args['parent'] = 0; // TODO: maybe this is unnecessary.
41 $args['post_id'] = $wp_object->ID;
42 } elseif ( $wp_object instanceof WP_Comment ) {
43 $args['parent'] = $wp_object->comment_ID;
44 } else {
45 return new WP_Error();
46 }
47
48 return $args;
49 }
50
51 /**
52 * Get the replies collections ID.
53 *
54 * @param WP_Post|WP_Comment $wp_object The post or comment to fetch replies for.
55 *
56 * @return string|WP_Error The rest URL of the replies collection or WP_Error if the object is not a post or comment.
57 */
58 private static function get_id( $wp_object ) {
59 if ( $wp_object instanceof WP_Post ) {
60 return get_rest_url_by_path( sprintf( 'posts/%d/replies', $wp_object->ID ) );
61 } elseif ( $wp_object instanceof WP_Comment ) {
62 return get_rest_url_by_path( sprintf( 'comments/%d/replies', $wp_object->comment_ID ) );
63 } else {
64 return new WP_Error( 'unsupported_object', 'The object is not a post or comment.' );
65 }
66 }
67
68 /**
69 * Get the Replies collection.
70 *
71 * @param WP_Post|WP_Comment $wp_object The post or comment to fetch replies for.
72 *
73 * @return array|\WP_Error|null An associative array containing the replies collection without JSON-LD context on success.
74 */
75 public static function get_collection( $wp_object ) {
76 $id = self::get_id( $wp_object );
77
78 if ( is_wp_error( $id ) ) {
79 return defined( 'REST_REQUEST' ) && REST_REQUEST ? $id : null;
80 }
81
82 $replies = array(
83 'id' => $id,
84 'type' => 'Collection',
85 );
86
87 $replies['first'] = self::get_collection_page( $wp_object, 1, $replies['id'] );
88
89 return $replies;
90 }
91
92 /**
93 * Returns a replies collection page as an associative array.
94 *
95 * @link https://www.w3.org/TR/activitystreams-vocabulary/#dfn-collectionpage
96 *
97 * @param WP_Post|WP_Comment $wp_object The post of comment the replies are for.
98 * @param int $page The current pagination page.
99 * @param string $part_of Optional. The collection id/url the returned CollectionPage belongs to. Default null.
100 *
101 * @return array|WP_Error|null A CollectionPage as an associative array on success, WP_Error or null on failure.
102 */
103 public static function get_collection_page( $wp_object, $page, $part_of = null ) {
104 // Build initial arguments for fetching approved comments.
105 $args = self::build_args( $wp_object );
106 if ( is_wp_error( $args ) ) {
107 return defined( 'REST_REQUEST' ) && REST_REQUEST ? $args : null;
108 }
109
110 // Retrieve the partOf if not already given.
111 $part_of = $part_of ?? self::get_id( $wp_object );
112
113 // If the collection page does not exist.
114 if ( is_wp_error( $part_of ) ) {
115 return defined( 'REST_REQUEST' ) && REST_REQUEST ? $part_of : null;
116 }
117
118 // Get to total replies count.
119 $total_replies = \get_comments( array_merge( $args, array( 'count' => true ) ) );
120
121 // If set to zero, we get errors below. You need at least one comment per page, here.
122 $args['number'] = max( (int) \get_option( 'comments_per_page' ), 1 );
123 $args['offset'] = intval( $page - 1 ) * $args['number'];
124
125 // Get the ActivityPub ID's of the comments, without local-only comments.
126 $comment_ids = self::get_reply_ids( \get_comments( $args ) );
127
128 // Build the associative CollectionPage array.
129 $collection_page = array(
130 'id' => \add_query_arg( 'page', $page, $part_of ),
131 'type' => 'CollectionPage',
132 'partOf' => $part_of,
133 'items' => $comment_ids,
134 );
135
136 if ( ( $total_replies / $args['number'] ) > $page ) {
137 $collection_page['next'] = \add_query_arg( 'page', $page + 1, $part_of );
138 }
139
140 if ( $page > 1 ) {
141 $collection_page['prev'] = \add_query_arg( 'page', $page - 1, $part_of );
142 }
143
144 return $collection_page;
145 }
146
147 /**
148 * Get the context collection for a post.
149 *
150 * @param int $post_id The post ID.
151 *
152 * @return array|false The context for the post or false if the post is not found or disabled.
153 */
154 public static function get_context_collection( $post_id ) {
155 $post = \get_post( $post_id );
156
157 if ( ! $post || is_post_disabled( $post_id ) ) {
158 return false;
159 }
160
161 $comments = \get_comments(
162 array(
163 'post_id' => $post_id,
164 'type' => 'comment',
165 'status' => 'approve',
166 'orderby' => 'comment_date_gmt',
167 'order' => 'ASC',
168 )
169 );
170 $ids = self::get_reply_ids( $comments, true );
171 $post_uri = ( new PostTransformer( $post ) )->to_id();
172 \array_unshift( $ids, $post_uri );
173
174 return array(
175 'type' => 'OrderedCollection',
176 'url' => \get_permalink( $post_id ),
177 'attributedTo' => Actors::get_by_id( $post->post_author )->get_id(),
178 'totalItems' => count( $ids ),
179 'items' => $ids,
180 );
181 }
182
183 /**
184 * Get the ActivityPub ID's from a list of comments.
185 *
186 * It takes only federated/non-local comments into account, others also do not have an
187 * ActivityPub ID available.
188 *
189 * @param WP_Comment[] $comments The comments to retrieve the ActivityPub ids from.
190 * @param boolean $include_blog_comments Optional. Include blog comments in the returned array. Default false.
191 *
192 * @return string[] A list of the ActivityPub ID's.
193 */
194 private static function get_reply_ids( $comments, $include_blog_comments = false ) {
195 $comment_ids = array();
196
197 foreach ( $comments as $comment ) {
198 if ( is_local_comment( $comment ) ) {
199 continue;
200 }
201
202 $public_comment_id = Comment::get_source_id( $comment->comment_ID );
203 if ( $public_comment_id ) {
204 $comment_ids[] = $public_comment_id;
205 continue;
206 }
207
208 if ( $include_blog_comments ) {
209 $comment_ids[] = ( new CommentTransformer( $comment ) )->to_id();
210 }
211 }
212
213 return \array_unique( $comment_ids );
214 }
215 }
216