PluginProbe
ActivityPub / 8.0.2
ActivityPub v8.0.2
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 / functions-user.php

functions-user.php in ActivityPub 8.0.2, at includes/functions-user.php

367 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * User functions.
4 *
5 * Functions for working with users and actors in ActivityPub context.
6 *
7 * @package Activitypub
8 */
9
10 namespace Activitypub;
11
12 use Activitypub\Collection\Actors;
13 use Activitypub\Collection\Followers;
14
15 /**
16 * Returns a users WebFinger "resource".
17 *
18 * @deprecated 7.1.0 Use {@see \Activitypub\Webfinger::get_user_resource} instead.
19 *
20 * @param int $user_id The user ID.
21 *
22 * @return string The User resource.
23 */
24 function get_webfinger_resource( $user_id ) {
25 \_deprecated_function( __FUNCTION__, '7.1.0', 'Activitypub\Webfinger::get_user_resource' );
26
27 return Webfinger::get_user_resource( $user_id );
28 }
29
30 /**
31 * Returns the followers of a given user.
32 *
33 * @param int $user_id The user ID.
34 *
35 * @return array The followers.
36 */
37 function get_followers( $user_id ) {
38 return Followers::get_many( $user_id );
39 }
40
41 /**
42 * Count the number of followers for a given user.
43 *
44 * @param int $user_id The user ID.
45 *
46 * @return int The number of followers.
47 */
48 function count_followers( $user_id ) {
49 return Followers::count( $user_id );
50 }
51
52 /**
53 * Examine a url and try to determine the author ID it represents.
54 *
55 * Checks are supposedly from the hosted site blog.
56 *
57 * @param string $url Permalink to check.
58 *
59 * @return int|null User ID, or null on failure.
60 */
61 function url_to_authorid( $url ) {
62 global $wp_rewrite;
63
64 // Check if url has the same host.
65 $request_host = \wp_parse_url( $url, \PHP_URL_HOST );
66 if ( \wp_parse_url( \home_url(), \PHP_URL_HOST ) !== $request_host && get_option( 'activitypub_old_host' ) !== $request_host ) {
67 return null;
68 }
69
70 // First, check to see if there is an 'author=N' to match against.
71 if ( \preg_match( '/[?&]author=(\d+)/i', $url, $values ) ) {
72 return \absint( $values[1] );
73 }
74
75 // Check to see if we are using rewrite rules.
76 $rewrite = $wp_rewrite->wp_rewrite_rules();
77
78 // Not using rewrite rules, and 'author=N' method failed, so we're out of options.
79 if ( empty( $rewrite ) ) {
80 return null;
81 }
82
83 // Generate rewrite rule for the author url.
84 $author_rewrite = $wp_rewrite->get_author_permastruct();
85 $author_regexp = \str_replace( '%author%', '', $author_rewrite );
86
87 // Match the rewrite rule with the passed url.
88 if ( \preg_match( '/https?:\/\/(.+)' . \preg_quote( $author_regexp, '/' ) . '([^\/]+)/i', $url, $match ) ) {
89 $user = \get_user_by( 'slug', $match[2] );
90 if ( $user ) {
91 return $user->ID;
92 }
93 }
94
95 return null;
96 }
97
98 /**
99 * This function checks if a user is enabled for ActivityPub.
100 *
101 * @param int|string $user_id The user ID.
102 *
103 * @return boolean True if the user is enabled, false otherwise.
104 */
105 function user_can_activitypub( $user_id ) {
106 if ( ! is_numeric( $user_id ) ) {
107 return false;
108 }
109
110 switch ( $user_id ) {
111 case Actors::APPLICATION_USER_ID:
112 $enabled = true; // Application user is always enabled.
113 break;
114
115 case Actors::BLOG_USER_ID:
116 $enabled = ! is_user_type_disabled( 'blog' );
117 break;
118
119 default:
120 if ( ! \get_user_by( 'id', $user_id ) ) {
121 $enabled = false;
122 break;
123 }
124
125 if ( is_user_type_disabled( 'user' ) ) {
126 $enabled = false;
127 break;
128 }
129
130 $enabled = \user_can( $user_id, 'activitypub' );
131 }
132
133 /**
134 * Allow plugins to enable/disable users for ActivityPub.
135 *
136 * @param boolean $enabled True if the user is enabled, false otherwise.
137 * @param int $user_id The user ID.
138 */
139 return apply_filters( 'activitypub_user_can_activitypub', $enabled, $user_id );
140 }
141
142 /**
143 * Checks if a User-Type is disabled for ActivityPub.
144 *
145 * This function is used to check if the 'blog' or 'user'
146 * type is disabled for ActivityPub.
147 *
148 * @param string $type User type. 'blog' or 'user'.
149 *
150 * @return boolean True if the user type is disabled, false otherwise.
151 */
152 function is_user_type_disabled( $type ) {
153 switch ( $type ) {
154 case 'blog':
155 if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) ) {
156 if ( ACTIVITYPUB_SINGLE_USER_MODE ) {
157 $disabled = false;
158 break;
159 }
160 }
161
162 if ( \defined( 'ACTIVITYPUB_DISABLE_BLOG_USER' ) ) {
163 $disabled = ACTIVITYPUB_DISABLE_BLOG_USER;
164 break;
165 }
166
167 if ( ACTIVITYPUB_ACTOR_MODE === \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) {
168 $disabled = true;
169 break;
170 }
171
172 $disabled = false;
173 break;
174 case 'user':
175 if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) ) {
176 if ( ACTIVITYPUB_SINGLE_USER_MODE ) {
177 $disabled = true;
178 break;
179 }
180 }
181
182 if ( \defined( 'ACTIVITYPUB_DISABLE_USER' ) ) {
183 $disabled = ACTIVITYPUB_DISABLE_USER;
184 break;
185 }
186
187 if ( ACTIVITYPUB_BLOG_MODE === \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) {
188 $disabled = true;
189 break;
190 }
191
192 $disabled = false;
193 break;
194 default:
195 // Treat unknown user types as disabled to ensure a consistent boolean return value.
196 $disabled = true;
197 break;
198 }
199
200 /**
201 * Allow plugins to disable user types for ActivityPub.
202 *
203 * @param boolean $disabled True if the user type is disabled, false otherwise.
204 * @param string $type The User-Type.
205 */
206 return apply_filters( 'activitypub_is_user_type_disabled', $disabled, $type );
207 }
208
209 /**
210 * Check if the blog is in single-user mode.
211 *
212 * @return boolean True if the blog is in single-user mode, false otherwise.
213 */
214 function is_single_user() {
215 if (
216 false === is_user_type_disabled( 'blog' ) &&
217 true === is_user_type_disabled( 'user' )
218 ) {
219 return true;
220 }
221
222 return false;
223 }
224
225 /**
226 * Get active users based on a given duration.
227 *
228 * Counts users who published posts (of any ActivityPub-enabled post type)
229 * or approved comments within the given time period.
230 *
231 * @param int $duration Optional. The duration to check in month(s). Default 1.
232 *
233 * @return int The number of active users.
234 */
235 function get_active_users( $duration = 1 ) {
236 $duration = \intval( $duration );
237 $transient_key = \sprintf( 'monthly_active_users_%d', $duration );
238 $count = \get_transient( $transient_key );
239
240 if ( false === $count ) {
241 global $wpdb;
242
243 $post_types = \get_post_types_by_support( 'activitypub' );
244 $post_authors = array();
245
246 if ( ! empty( $post_types ) ) {
247 $placeholders = \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) );
248
249 // Get distinct user IDs who published posts of AP-enabled post types.
250 // phpcs:ignore WordPress.DB.DirectDatabaseQuery
251 $post_authors = $wpdb->get_col(
252 $wpdb->prepare(
253 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
254 "SELECT DISTINCT post_author FROM {$wpdb->posts} WHERE post_type IN ( {$placeholders} ) AND post_status = 'publish' AND post_date >= DATE_SUB( NOW(), INTERVAL %d MONTH )",
255 \array_merge( $post_types, array( $duration ) )
256 )
257 );
258 }
259
260 // Get distinct user IDs who made approved comments.
261 // phpcs:ignore WordPress.DB.DirectDatabaseQuery
262 $comment_authors = $wpdb->get_col(
263 $wpdb->prepare(
264 "SELECT DISTINCT user_id FROM {$wpdb->comments} WHERE comment_approved = '1' AND user_id != 0 AND comment_date >= DATE_SUB( NOW(), INTERVAL %d MONTH )",
265 $duration
266 )
267 );
268
269 // Deduplicate and filter out anonymous (0) entries.
270 $active_ids = \array_unique( \array_filter( \array_map( 'absint', \array_merge( $post_authors, $comment_authors ) ) ) );
271
272 if ( empty( $active_ids ) ) {
273 $count = 0;
274 } else {
275 // Count only users who have the activitypub capability.
276 $user_query = new \WP_User_Query(
277 array(
278 'capability__in' => array( 'activitypub' ),
279 'include' => $active_ids,
280 'number' => 1, // Minimize memory; get_total() still returns full count.
281 )
282 );
283 $count = $user_query->get_total();
284 }
285
286 \set_transient( $transient_key, $count, DAY_IN_SECONDS );
287 }
288
289 // If 0 authors were active.
290 if ( 0 === (int) $count ) {
291 return 0;
292 }
293
294 // If single user mode.
295 if ( is_single_user() ) {
296 return 1;
297 }
298
299 // If blog user is disabled.
300 if ( ! user_can_activitypub( Actors::BLOG_USER_ID ) ) {
301 $active = (int) $count;
302 } else {
303 // Also count blog user.
304 $active = (int) $count + 1;
305 }
306
307 // Ensure active users doesn't exceed total users.
308 return \min( $active, get_total_users() );
309 }
310
311 /**
312 * Get the total number of users.
313 *
314 * @return int The total number of users.
315 */
316 function get_total_users() {
317 // If single user mode.
318 if ( is_single_user() ) {
319 return 1;
320 }
321
322 $user_query = new \WP_User_Query(
323 array(
324 'capability__in' => array( 'activitypub' ),
325 'number' => 1,
326 )
327 );
328
329 $users = $user_query->get_total();
330
331 // If blog user is disabled.
332 if ( ! user_can_activitypub( Actors::BLOG_USER_ID ) ) {
333 return (int) $users;
334 }
335
336 return (int) $users + 1;
337 }
338
339 /**
340 * Get the ActivityPub ID of a User by the WordPress User ID.
341 *
342 * Fall back to blog user if in blog mode or if user is not found.
343 *
344 * @param int $id The WordPress User ID.
345 *
346 * @return string|false The ActivityPub ID (a URL) of the User or false if not found.
347 */
348 function get_user_id( $id ) {
349 $mode = \get_option( 'activitypub_actor_mode', 'default' );
350
351 if ( ACTIVITYPUB_BLOG_MODE === $mode ) {
352 $user = Actors::get_by_id( Actors::BLOG_USER_ID );
353 } else {
354 $user = Actors::get_by_id( $id );
355
356 if ( \is_wp_error( $user ) ) {
357 $user = Actors::get_by_id( Actors::BLOG_USER_ID );
358 }
359 }
360
361 if ( \is_wp_error( $user ) ) {
362 return false;
363 }
364
365 return $user->get_id();
366 }
367