9
9
10
10
use Activitypub \Collection \Actors ;
11
11
use Activitypub \Collection \Followers as Follower_Collection ;
12
+ use Activitypub \Collection \Following ;
13
+ use Activitypub \Sanitize ;
14
+ use Activitypub \Webfinger ;
12
15
13
16
use function Activitypub \object_to_uri ;
14
17
@@ -27,14 +30,23 @@ class Followers extends \WP_List_Table {
27
30
*/
28
31
private $ user_id ;
29
32
33
+ /**
34
+ * Follow URL.
35
+ *
36
+ * @var string
37
+ */
38
+ public $ follow_url ;
39
+
30
40
/**
31
41
* Constructor.
32
42
*/
33
43
public function __construct () {
34
44
if ( get_current_screen ()->id === 'settings_page_activitypub ' ) {
35
- $ this ->user_id = Actors::BLOG_USER_ID ;
45
+ $ this ->user_id = Actors::BLOG_USER_ID ;
46
+ $ this ->follow_url = \admin_url ( 'options-general.php?page=activitypub&tab=following ' );
36
47
} else {
37
- $ this ->user_id = \get_current_user_id ();
48
+ $ this ->user_id = \get_current_user_id ();
49
+ $ this ->follow_url = \admin_url ( 'users.php?page=activitypub-following ' );
38
50
39
51
\add_action ( 'admin_notices ' , array ( $ this , 'process_admin_notices ' ) );
40
52
}
@@ -132,6 +144,7 @@ public function get_columns() {
132
144
'cb ' => '<input type="checkbox" /> ' ,
133
145
'username ' => \esc_html__ ( 'Username ' , 'activitypub ' ),
134
146
'post_title ' => \esc_html__ ( 'Name ' , 'activitypub ' ),
147
+ 'webfinger ' => \esc_html__ ( 'Profile ' , 'activitypub ' ),
135
148
'modified ' => \esc_html__ ( 'Last updated ' , 'activitypub ' ),
136
149
);
137
150
}
@@ -192,13 +205,21 @@ public function prepare_items() {
192
205
);
193
206
194
207
foreach ( $ followers as $ follower ) {
195
- $ actor = Actors::get_actor ( $ follower );
208
+ $ actor = Actors::get_actor ( $ follower );
209
+ $ url = object_to_uri ( $ actor ->get_url () ?? $ actor ->get_id () );
210
+ $ webfinger = Webfinger::uri_to_acct ( $ url );
211
+
212
+ if ( is_wp_error ( $ webfinger ) ) {
213
+ $ webfinger = Webfinger::guess ( $ url );
214
+ }
215
+
196
216
$ this ->items [] = array (
197
217
'id ' => $ follower ->ID ,
198
218
'icon ' => $ actor ->get_icon ()['url ' ] ?? '' ,
199
- 'post_title ' => $ actor ->get_name (),
219
+ 'post_title ' => $ actor ->get_name () ?? $ actor -> get_preferred_username () ,
200
220
'username ' => $ actor ->get_preferred_username (),
201
- 'url ' => object_to_uri ( $ actor ->get_url () ),
221
+ 'url ' => $ url ,
222
+ 'webfinger ' => $ webfinger ,
202
223
'identifier ' => $ actor ->get_id (),
203
224
'modified ' => $ follower ->post_modified_gmt ,
204
225
);
@@ -283,14 +304,30 @@ public function column_cb( $item ) {
283
304
*/
284
305
public function column_username ( $ item ) {
285
306
return \sprintf (
286
- '<img src="%1$s" width="32" height="32" alt="%2$s" loading="lazy"/> <strong><a href="%3$s">%4$s</a></strong><br /> ' ,
307
+ '<img src="%1$s" width="32" height="32" alt="%2$s" loading="lazy"/> <strong><a href="%3$s" target="_blank" >%4$s</a></strong><br /> ' ,
287
308
\esc_url ( $ item ['icon ' ] ),
288
309
\esc_attr ( $ item ['username ' ] ),
289
310
\esc_url ( $ item ['url ' ] ),
290
311
\esc_html ( $ item ['username ' ] )
291
312
);
292
313
}
293
314
315
+ /**
316
+ * Column webfinger.
317
+ *
318
+ * @param array $item Item.
319
+ * @return string
320
+ */
321
+ public function column_webfinger ( $ item ) {
322
+ $ webfinger = Sanitize::webfinger ( $ item ['webfinger ' ] );
323
+
324
+ return \sprintf (
325
+ '<a href="%1$s" target="_blank" title="%1$s">@%2$s</a> ' ,
326
+ \esc_url ( $ item ['url ' ] ),
327
+ \esc_html ( $ webfinger )
328
+ );
329
+ }
330
+
294
331
/**
295
332
* Column modified.
296
333
*
@@ -310,7 +347,24 @@ public function column_modified( $item ) {
310
347
* Message to be displayed when there are no followers.
311
348
*/
312
349
public function no_items () {
313
- \esc_html_e ( 'No followers found. ' , 'activitypub ' );
350
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
351
+ $ search = \sanitize_text_field ( \wp_unslash ( $ _GET ['s ' ] ?? '' ) );
352
+ $ actor_or_false = $ this ->_is_followable ( $ search );
353
+
354
+ if ( $ actor_or_false ) {
355
+ \printf (
356
+ /* translators: %s: Actor name. */
357
+ \esc_html__ ( '%1$s is not following you, would you like to %2$s instead? ' , 'activitypub ' ),
358
+ \esc_html ( $ actor_or_false ->post_title ),
359
+ \sprintf (
360
+ '<a href="%s">%s</a> ' ,
361
+ \esc_url ( \add_query_arg ( 'resource ' , $ search , $ this ->follow_url ) ),
362
+ \esc_html__ ( 'follow them ' , 'activitypub ' )
363
+ )
364
+ );
365
+ } else {
366
+ \esc_html_e ( 'No followers found. ' , 'activitypub ' );
367
+ }
314
368
}
315
369
316
370
/**
@@ -346,4 +400,39 @@ protected function handle_row_actions( $item, $column_name, $primary ) {
346
400
347
401
return $ this ->row_actions ( $ actions );
348
402
}
403
+
404
+ /**
405
+ * Checks if the searched actor can be followed.
406
+ *
407
+ * @param string $search The search string.
408
+ *
409
+ * @return \WP_Post|false The actor post or false.
410
+ */
411
+ private function _is_followable ( $ search ) { // phpcs:ignore
412
+ if ( empty ( $ search ) ) {
413
+ return false ;
414
+ }
415
+
416
+ $ search = Sanitize::webfinger ( $ search );
417
+ if ( ! \filter_var ( $ search , FILTER_VALIDATE_EMAIL ) ) {
418
+ return false ;
419
+ }
420
+
421
+ $ search = Webfinger::resolve ( $ search );
422
+ if ( \is_wp_error ( $ search ) || ! \filter_var ( $ search , FILTER_VALIDATE_URL ) ) {
423
+ return false ;
424
+ }
425
+
426
+ $ actor = Actors::fetch_remote_by_uri ( $ search );
427
+ if ( \is_wp_error ( $ actor ) ) {
428
+ return false ;
429
+ }
430
+
431
+ $ does_follow = Following::check_status ( $ this ->user_id , $ actor ->ID );
432
+ if ( $ does_follow ) {
433
+ return false ;
434
+ }
435
+
436
+ return $ actor ;
437
+ }
349
438
}
0 commit comments