PluginProbe
ActivityPub / 2.6.1
ActivityPub v2.6.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 / class-admin.php

class-admin.php in ActivityPub 2.6.1, at includes/class-admin.php

650 lines 19.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Activitypub;
3
4 use WP_User_Query;
5 use Activitypub\Model\Blog;
6 use Activitypub\Activitypub;
7 use Activitypub\Collection\Users;
8
9 use function Activitypub\count_followers;
10 use function Activitypub\is_user_disabled;
11 use function Activitypub\was_comment_received;
12 use function Activitypub\is_comment_federatable;
13 use function Activitypub\add_default_actor_extra_fields;
14
15 /**
16 * ActivityPub Admin Class
17 *
18 * @author Matthias Pfefferle
19 */
20 class Admin {
21 /**
22 * Initialize the class, registering WordPress hooks
23 */
24 public static function init() {
25 \add_action( 'admin_menu', array( self::class, 'admin_menu' ) );
26 \add_action( 'admin_init', array( self::class, 'register_settings' ) );
27 \add_action( 'load-comment.php', array( self::class, 'edit_comment' ) );
28 \add_action( 'load-post.php', array( self::class, 'edit_post' ) );
29 \add_action( 'load-edit.php', array( self::class, 'list_posts' ) );
30 \add_action( 'personal_options_update', array( self::class, 'save_user_description' ) );
31 \add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
32 \add_action( 'admin_notices', array( self::class, 'admin_notices' ) );
33
34 \add_filter( 'comment_row_actions', array( self::class, 'comment_row_actions' ), 10, 2 );
35 \add_filter( 'manage_edit-comments_columns', array( static::class, 'manage_comment_columns' ) );
36 \add_action( 'manage_comments_custom_column', array( static::class, 'manage_comments_custom_column' ), 9, 2 );
37
38 \add_filter( 'manage_posts_columns', array( static::class, 'manage_post_columns' ), 10, 2 );
39 \add_action( 'manage_posts_custom_column', array( self::class, 'manage_posts_custom_column' ), 10, 2 );
40
41 \add_filter( 'manage_users_columns', array( self::class, 'manage_users_columns' ), 10, 1 );
42 \add_action( 'manage_users_custom_column', array( self::class, 'manage_users_custom_column' ), 10, 3 );
43 \add_filter( 'bulk_actions-users', array( self::class, 'user_bulk_options' ) );
44 \add_filter( 'handle_bulk_actions-users', array( self::class, 'handle_bulk_request' ), 10, 3 );
45
46 if ( ! is_user_disabled( get_current_user_id() ) ) {
47 \add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
48 }
49
50 \add_filter( 'dashboard_glance_items', array( self::class, 'dashboard_glance_items' ) );
51 }
52
53 /**
54 * Add admin menu entry
55 */
56 public static function admin_menu() {
57 $settings_page = \add_options_page(
58 'Welcome',
59 'ActivityPub',
60 'manage_options',
61 'activitypub',
62 array( self::class, 'settings_page' )
63 );
64
65 \add_action( 'load-' . $settings_page, array( self::class, 'add_settings_help_tab' ) );
66
67 // user has to be able to publish posts
68 if ( ! is_user_disabled( get_current_user_id() ) ) {
69 $followers_list_page = \add_users_page( \__( 'Followers', 'activitypub' ), \__( 'Followers', 'activitypub' ), 'read', 'activitypub-followers-list', array( self::class, 'followers_list_page' ) );
70
71 \add_action( 'load-' . $followers_list_page, array( self::class, 'add_followers_list_help_tab' ) );
72
73 \add_users_page( \__( 'Extra Fields', 'activitypub' ), \__( 'Extra Fields', 'activitypub' ), 'read', esc_url( admin_url( '/edit.php?post_type=ap_extrafield' ) ) );
74 }
75 }
76
77 /**
78 * Display admin menu notices about configuration problems or conflicts.
79 *
80 * @return void
81 */
82 public static function admin_notices() {
83 $permalink_structure = \get_option( 'permalink_structure' );
84 if ( empty( $permalink_structure ) ) {
85 $admin_notice = \__( 'You are using the ActivityPub plugin with a permalink structure of "plain". This will prevent ActivityPub from working. Please go to "Settings" / "Permalinks" and choose a permalink structure other than "plain".', 'activitypub' );
86 self::show_admin_notice( $admin_notice, 'error' );
87 }
88
89 $current_screen = get_current_screen();
90
91 if ( isset( $current_screen->id ) && 'edit-ap_extrafield' === $current_screen->id ) {
92 ?>
93 <div class="notice" style="margin: 0; background: none; border: none; box-shadow: none; padding: 15px 0 0 0; font-size: 14px;">
94 <?php esc_html_e( 'These are extra fields that are used for your ActivityPub profile. You can use your homepage, social profiles, pronouns, age, anything you want.', 'activitypub' ); ?>
95 </div>
96 <?php
97 }
98 }
99
100 /**
101 * Display one admin menu notice about configuration problems or conflicts.
102 *
103 * @param string $admin_notice The notice to display.
104 * @param string $level The level of the notice (error, warning, success, info).
105 *
106 * @return void
107 */
108 private static function show_admin_notice( $admin_notice, $level ) {
109 ?>
110
111 <div class="notice notice-<?php echo esc_attr( $level ); ?>">
112 <p><?php echo wp_kses( $admin_notice, 'data' ); ?></p>
113 </div>
114
115 <?php
116 }
117
118 /**
119 * Load settings page
120 */
121 public static function settings_page() {
122 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
123 if ( empty( $_GET['tab'] ) ) {
124 $tab = 'welcome';
125 } else {
126 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
127 $tab = sanitize_key( $_GET['tab'] );
128 }
129
130 switch ( $tab ) {
131 case 'settings':
132 \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/settings.php' );
133 break;
134 case 'followers':
135 \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/blog-user-followers-list.php' );
136 break;
137 case 'welcome':
138 default:
139 wp_enqueue_script( 'plugin-install' );
140 add_thickbox();
141 wp_enqueue_script( 'updates' );
142
143 \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/welcome.php' );
144 break;
145 }
146 }
147
148 /**
149 * Load user settings page
150 */
151 public static function followers_list_page() {
152 // user has to be able to publish posts
153 if ( ! is_user_disabled( get_current_user_id() ) ) {
154 \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/user-followers-list.php' );
155 }
156 }
157
158 /**
159 * Register ActivityPub settings
160 */
161 public static function register_settings() {
162 \register_setting(
163 'activitypub',
164 'activitypub_post_content_type',
165 array(
166 'type' => 'string',
167 'description' => \__( 'Use title and link, summary, full or custom content', 'activitypub' ),
168 'show_in_rest' => array(
169 'schema' => array(
170 'enum' => array(
171 'title',
172 'excerpt',
173 'content',
174 ),
175 ),
176 ),
177 'default' => 'content',
178 )
179 );
180 \register_setting(
181 'activitypub',
182 'activitypub_custom_post_content',
183 array(
184 'type' => 'string',
185 'description' => \__( 'Define your own custom post template', 'activitypub' ),
186 'show_in_rest' => true,
187 'default' => ACTIVITYPUB_CUSTOM_POST_CONTENT,
188 )
189 );
190 \register_setting(
191 'activitypub',
192 'activitypub_max_image_attachments',
193 array(
194 'type' => 'integer',
195 'description' => \__( 'Number of images to attach to posts.', 'activitypub' ),
196 'default' => ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS,
197 )
198 );
199 \register_setting(
200 'activitypub',
201 'activitypub_object_type',
202 array(
203 'type' => 'string',
204 'description' => \__( 'The Activity-Object-Type', 'activitypub' ),
205 'show_in_rest' => array(
206 'schema' => array(
207 'enum' => array(
208 'note',
209 'wordpress-post-format',
210 ),
211 ),
212 ),
213 'default' => 'note',
214 )
215 );
216 \register_setting(
217 'activitypub',
218 'activitypub_use_hashtags',
219 array(
220 'type' => 'boolean',
221 'description' => \__( 'Add hashtags in the content as native tags and replace the #tag with the tag-link', 'activitypub' ),
222 'default' => '0',
223 )
224 );
225 \register_setting(
226 'activitypub',
227 'activitypub_support_post_types',
228 array(
229 'type' => 'string',
230 'description' => \esc_html__( 'Enable ActivityPub support for post types', 'activitypub' ),
231 'show_in_rest' => true,
232 'default' => array( 'post' ),
233 )
234 );
235 \register_setting(
236 'activitypub',
237 'activitypub_blog_user_identifier',
238 array(
239 'type' => 'string',
240 'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
241 'show_in_rest' => true,
242 'default' => Blog::get_default_username(),
243 'sanitize_callback' => function ( $value ) {
244 // hack to allow dots in the username
245 $parts = explode( '.', $value );
246 $sanitized = array();
247
248 foreach ( $parts as $part ) {
249 $sanitized[] = \sanitize_title( $part );
250 }
251
252 $sanitized = implode( '.', $sanitized );
253
254 // check for login or nicename.
255 $user = new WP_User_Query(
256 array(
257 'search' => $sanitized,
258 'search_columns' => array( 'user_login', 'user_nicename' ),
259 'number' => 1,
260 'hide_empty' => true,
261 'fields' => 'ID',
262 )
263 );
264
265 if ( $user->results ) {
266 add_settings_error(
267 'activitypub_blog_user_identifier',
268 'activitypub_blog_user_identifier',
269 \esc_html__( 'You cannot use an existing author\'s name for the blog profile ID.', 'activitypub' ),
270 'error'
271 );
272
273 return Blog::get_default_username();
274 }
275
276 return $sanitized;
277 },
278 )
279 );
280 \register_setting(
281 'activitypub',
282 'activitypub_enable_users',
283 array(
284 'type' => 'boolean',
285 'description' => \__( 'Every Author on this Blog (with the publish_posts capability) gets his own ActivityPub enabled Profile.', 'activitypub' ),
286 'default' => '1',
287 )
288 );
289 \register_setting(
290 'activitypub',
291 'activitypub_enable_blog_user',
292 array(
293 'type' => 'boolean',
294 'description' => \__( 'Your Blog becomes an ActivityPub compatible Profile.', 'activitypub' ),
295 'default' => '0',
296 )
297 );
298 }
299
300 public static function add_settings_help_tab() {
301 require_once ACTIVITYPUB_PLUGIN_DIR . 'includes/help.php';
302 }
303
304 public static function add_followers_list_help_tab() {
305 // todo
306 }
307
308 public static function add_profile( $user ) {
309 $description = get_user_meta( $user->ID, 'activitypub_user_description', true );
310
311 \load_template(
312 ACTIVITYPUB_PLUGIN_DIR . 'templates/user-settings.php',
313 true,
314 array(
315 'description' => $description,
316 )
317 );
318 }
319
320 public static function save_user_description( $user_id ) {
321 if ( ! isset( $_REQUEST['_apnonce'] ) ) {
322 return false;
323 }
324 $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_apnonce'] ) );
325 if (
326 ! wp_verify_nonce( $nonce, 'activitypub-user-description' ) ||
327 ! current_user_can( 'edit_user', $user_id )
328 ) {
329 return false;
330 }
331 $description = ! empty( $_POST['activitypub-user-description'] ) ? sanitize_text_field( wp_unslash( $_POST['activitypub-user-description'] ) ) : false;
332 if ( $description ) {
333 update_user_meta( $user_id, 'activitypub_user_description', $description );
334 }
335 }
336
337 public static function enqueue_scripts( $hook_suffix ) {
338 if ( false !== strpos( $hook_suffix, 'activitypub' ) ) {
339 wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), get_plugin_version() );
340 wp_enqueue_script( 'activitypub-admin-script', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), get_plugin_version(), false );
341 }
342
343 if ( 'index.php' === $hook_suffix ) {
344 wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), get_plugin_version() );
345 }
346 }
347
348 /**
349 * Hook into the edit_comment functionality
350 *
351 * * Disable the edit_comment capability for federated comments.
352 *
353 * @return void
354 */
355 public static function edit_comment() {
356 // Disable the edit_comment capability for federated comments.
357 \add_filter(
358 'user_has_cap',
359 function ( $allcaps, $caps, $arg ) {
360 if ( 'edit_comment' !== $arg[0] ) {
361 return $allcaps;
362 }
363
364 if ( was_comment_received( $arg[2] ) ) {
365 return false;
366 }
367
368 return $allcaps;
369 },
370 1,
371 3
372 );
373 }
374
375 public static function edit_post() {
376 // Disable the edit_post capability for federated posts.
377 \add_filter(
378 'user_has_cap',
379 function ( $allcaps, $caps, $arg ) {
380 if ( 'edit_post' !== $arg[0] ) {
381 return $allcaps;
382 }
383
384 $post = get_post( $arg[2] );
385
386 if ( 'ap_extrafield' !== $post->post_type ) {
387 return $allcaps;
388 }
389
390 if ( (int) get_current_user_id() !== (int) $post->post_author ) {
391 return false;
392 }
393
394 return $allcaps;
395 },
396 1,
397 3
398 );
399 }
400
401 /**
402 * Add ActivityPub specific actions/filters to the post list view
403 *
404 * @return void
405 */
406 public static function list_posts() {
407 // Show only the user's extra fields.
408 \add_action(
409 'pre_get_posts',
410 function ( $query ) {
411 if ( $query->get( 'post_type' ) === 'ap_extrafield' ) {
412 $query->set( 'author', get_current_user_id() );
413 }
414 }
415 );
416
417 // Remove all views for the extra fields.
418 $screen_id = get_current_screen()->id;
419
420 add_filter(
421 "views_{$screen_id}",
422 function ( $views ) {
423 if ( 'ap_extrafield' === get_post_type() ) {
424 return array();
425 }
426
427 return $views;
428 }
429 );
430
431 // Set defaults for new extra fields.
432 if ( 'edit-ap_extrafield' === $screen_id ) {
433 Activitypub::default_actor_extra_fields( array(), get_current_user_id() );
434 }
435 }
436
437 public static function comment_row_actions( $actions, $comment ) {
438 if ( was_comment_received( $comment ) ) {
439 unset( $actions['edit'] );
440 unset( $actions['quickedit'] );
441 }
442
443 return $actions;
444 }
445
446 /**
447 * Add a column "activitypub"
448 *
449 * This column shows if the user has the capability to use ActivityPub.
450 *
451 * @param array $columns The columns.
452 *
453 * @return array The columns extended by the activitypub.
454 */
455 public static function manage_users_columns( $columns ) {
456 $columns['activitypub'] = __( 'ActivityPub', 'activitypub' );
457 return $columns;
458 }
459
460 /**
461 * Add "comment-type" and "protocol" as column in WP-Admin
462 *
463 * @param array $columns the list of column names
464 */
465 public static function manage_comment_columns( $columns ) {
466 $columns['comment_type'] = esc_attr__( 'Comment-Type', 'activitypub' );
467 $columns['comment_protocol'] = esc_attr__( 'Protocol', 'activitypub' );
468
469 return $columns;
470 }
471
472 /**
473 * Add "post_content" as column for Extra-Fields in WP-Admin
474 *
475 * @param array $columns Tthe list of column names.
476 * @param string $post_type The post type.
477 */
478 public static function manage_post_columns( $columns, $post_type ) {
479 if ( 'ap_extrafield' === $post_type ) {
480 $after_key = 'title';
481 $index = array_search( $after_key, array_keys( $columns ), true );
482 $columns = array_slice( $columns, 0, $index + 1 ) + array( 'extra_field_content' => esc_attr__( 'Content', 'activitypub' ) ) + $columns;
483 }
484
485 return $columns;
486 }
487
488 /**
489 * Add "comment-type" and "protocol" as column in WP-Admin
490 *
491 * @param array $column The column to implement
492 * @param int $comment_id The comment id
493 */
494 public static function manage_comments_custom_column( $column, $comment_id ) {
495 if ( 'comment_type' === $column && ! defined( 'WEBMENTION_PLUGIN_DIR' ) ) {
496 echo esc_attr( ucfirst( get_comment_type( $comment_id ) ) );
497 } elseif ( 'comment_protocol' === $column ) {
498 $protocol = get_comment_meta( $comment_id, 'protocol', true );
499
500 if ( $protocol ) {
501 echo esc_attr( ucfirst( str_replace( 'activitypub', 'ActivityPub', $protocol ) ) );
502 } else {
503 esc_attr_e( 'Local', 'activitypub' );
504 }
505 }
506 }
507
508 /**
509 * Return the results for the activitypub column.
510 *
511 * @param string $output Custom column output. Default empty.
512 * @param string $column_name Column name.
513 * @param int $user_id ID of the currently-listed user.
514 *
515 * @return string The column contents.
516 */
517 public static function manage_users_custom_column( $output, $column_name, $user_id ) {
518 if ( 'activitypub' !== $column_name ) {
519 return $output;
520 }
521
522 if ( \user_can( $user_id, 'activitypub' ) ) {
523 return '<span aria-hidden="true">&#x2713;</span><span class="screen-reader-text">' . esc_html__( 'ActivityPub enabled for this author', 'activitypub' ) . '</span>';
524 } else {
525 return '<span aria-hidden="true">&#x2717;</span><span class="screen-reader-text">' . esc_html__( 'ActivityPub disabled for this author', 'activitypub' ) . '</span>';
526 }
527 }
528
529 /**
530 * Add a column "extra_field_content" to the post list view
531 *
532 * @param string $column_name The column name.
533 * @param int $post_id The post ID.
534 *
535 * @return void
536 */
537 public static function manage_posts_custom_column( $column_name, $post_id ) {
538 $post = get_post( $post_id );
539
540 if ( 'extra_field_content' === $column_name ) {
541 $post = get_post( $post_id );
542 if ( 'ap_extrafield' === $post->post_type ) {
543 echo esc_attr( wp_strip_all_tags( $post->post_content ) );
544 }
545 }
546 }
547
548 /**
549 * Add options to the Bulk dropdown on the users page
550 *
551 * @param array $actions The existing bulk options.
552 *
553 * @return array The extended bulk options.
554 */
555 public static function user_bulk_options( $actions ) {
556 $actions['add_activitypub_cap'] = __( 'Enable for ActivityPub', 'activitypub' );
557 $actions['remove_activitypub_cap'] = __( 'Disable for ActivityPub', 'activitypub' );
558
559 return $actions;
560 }
561
562 /**
563 * Handle bulk activitypub requests
564 *
565 * * `add_activitypub_cap` - Add the activitypub capability to the selected users.
566 * * `remove_activitypub_cap` - Remove the activitypub capability from the selected users.
567 *
568 * @param string $sendback The URL to send the user back to.
569 * @param string $action The requested action.
570 * @param array $users The selected users.
571 *
572 * @return string The URL to send the user back to.
573 */
574 public static function handle_bulk_request( $sendback, $action, $users ) {
575 if (
576 'remove_activitypub_cap' !== $action &&
577 'add_activitypub_cap' !== $action
578 ) {
579 return $sendback;
580 }
581
582 foreach ( $users as $user_id ) {
583 $user = new \WP_User( $user_id );
584 if (
585 'add_activitypub_cap' === $action &&
586 user_can( $user_id, 'publish_posts' )
587 ) {
588 $user->add_cap( 'activitypub' );
589 } elseif ( 'remove_activitypub_cap' === $action ) {
590 $user->remove_cap( 'activitypub' );
591 }
592 }
593
594 return $sendback;
595 }
596
597 /**
598 * Add ActivityPub infos to the dashboard glance items
599 *
600 * @param array $items The existing glance items.
601 *
602 * @return array The extended glance items.
603 */
604 public static function dashboard_glance_items( $items ) {
605 \add_filter( 'number_format_i18n', '\Activitypub\custom_large_numbers', 10, 3 );
606
607 if ( ! is_user_disabled( get_current_user_id() ) ) {
608 $follower_count = sprintf(
609 // translators: %s: number of followers
610 _n(
611 '%s Follower',
612 '%s Followers',
613 count_followers( \get_current_user_id() ),
614 'activitypub'
615 ),
616 \number_format_i18n( count_followers( \get_current_user_id() ) )
617 );
618 $items['activitypub-followers-user'] = sprintf(
619 '<a class="activitypub-followers" href="%1$s" title="%2$s">%3$s</a>',
620 \esc_url( \admin_url( 'users.php?page=activitypub-followers-list' ) ),
621 \esc_attr__( 'Your followers', 'activitypub' ),
622 \esc_html( $follower_count )
623 );
624 }
625
626 if ( ! is_user_type_disabled( 'blog' ) && current_user_can( 'manage_options' ) ) {
627 $follower_count = sprintf(
628 // translators: %s: number of followers
629 _n(
630 '%s Follower (Blog)',
631 '%s Followers (Blog)',
632 count_followers( Users::BLOG_USER_ID ),
633 'activitypub'
634 ),
635 \number_format_i18n( count_followers( Users::BLOG_USER_ID ) )
636 );
637 $items['activitypub-followers-blog'] = sprintf(
638 '<a class="activitypub-followers" href="%1$s" title="%2$s">%3$s</a>',
639 \esc_url( \admin_url( 'options-general.php?page=activitypub&tab=followers' ) ),
640 \esc_attr__( 'The Blog\'s followers', 'activitypub' ),
641 \esc_html( $follower_count )
642 );
643 }
644
645 \remove_filter( 'number_format_i18n', '\Activitypub\custom_large_numbers', 10, 3 );
646
647 return $items;
648 }
649 }
650