PluginProbe
ActivityPub / trunk
ActivityPub vtrunk
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 trunk, at includes/collection/class-replies.php

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