diff --git a/activitypub.php b/activitypub.php index 3ec6cb5..ab98d1c 100644 --- a/activitypub.php +++ b/activitypub.php @@ -179,6 +179,14 @@ add_action( 0 ); +add_action( + 'wp_ajax_activitypub_handle_follow_request', + function () { + $wp_list_table = new \Activitypub\Table\Follow_Requests(); + $wp_list_table->ajax_response(); + } +); + /** * `get_plugin_data` wrapper * diff --git a/assets/css/activitypub-admin.css b/assets/css/activitypub-admin.css index 07aadca..4f1f48b 100644 --- a/assets/css/activitypub-admin.css +++ b/assets/css/activitypub-admin.css @@ -197,3 +197,36 @@ input.blog-user-identifier { border-bottom: none; margin-bottom: 0; } + +.activitypub-settings-label { + display: inline-block; + padding: .25em .4em; + font-size: 85%; + font-weight: 700; + line-height: 1.25; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25rem; +} + +.activitypub-settings-label-success { + color: #fff; + background-color: #28a745; +} + +.activitypub-settings-label-warning { + color: #212529; + background-color: #ffc107; +} + +.activitypub-settings-label-danger { + color: #fff; + background-color: #dc3545; +} + +.activitypub-settings-action-buttons { + display: flex; + gap: 5px; + flex-wrap: nowrap; +} diff --git a/assets/js/activitypub-admin.js b/assets/js/activitypub-admin.js index 37117ca..0d703ad 100644 --- a/assets/js/activitypub-admin.js +++ b/assets/js/activitypub-admin.js @@ -1,4 +1,5 @@ jQuery( function( $ ) { + const { __ } = wp.i18n; // Accordion handling in various areas. $( '.activitypub-settings-accordion' ).on( 'click', '.activitypub-settings-accordion-trigger', function() { var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) ); @@ -17,5 +18,51 @@ jQuery( function( $ ) { $( '.activate-now' ).removeClass( 'thickbox open-plugin-details-modal' ); }, 1200 ); } ); + + $( '.activitypub-settings-action-buttons' ).on( 'click', '.button', function() { + var button = $ (this ); + var actionValue = button.data('action'); + window.console.log( actionValue ); + $.ajax({ + type: 'POST', + url: actionValue, + success: function ( response ) { + var statusText = button.closest( 'td' ).siblings( '.column-status' ).children( 'span' ).first(); + if ( 'deleted' === response ) { + button.closest( 'tr' ).remove(); + } + if ( 'approved' === response ) { + button.parent().find( '[data-action*="follow_action=reject"] ').attr( 'type', 'button' ); + button.parent().find( '[data-action*="follow_action=delete"]' ).attr( 'type', 'hidden' ); + statusText.text( __( 'Approved', 'activitypub' ) ); + statusText.removeClass( 'activitypub-settings-label-danger' ); + statusText.removeClass( 'activitypub-settings-label-warning' ); + statusText.addClass( 'activitypub-settings-label-success' ); + } + if ( 'rejected' === response ) { + // TODO: clarify this behavior together with Mobilizon and others. + button.closest( 'tr' ).remove(); + // statusText.text( __( 'Rejected', 'activitypub' ) ); + // statusText.removeClass( 'activitypub-settings-label-success' ); + // statusText.removeClass( 'activitypub-settings-label-warning' ); + // statusText.addClass( 'activitypub-settings-label-danger' ); + // button.parent().find( '[data-action*="follow_action=approve"]' ).attr( 'type', 'button' ); + // button.parent().find( '[data-action*="follow_action=delete"]' ).attr( 'type', 'button' ); + } + button.attr( 'type', 'hidden' ); + // Check if table is completely empty. + var tbody = button.closest( 'tbody' ); + if ( 0 == tbody.find( 'tr' ).length ) { + var text = __( 'No items found.', 'core' ); + var newRow = $('').append($('', { class: 'colspanchange', colspan: 7, text: text })); + tbody.append(newRow); + tbody.append("Some appended text."); + } + }, + error: function ( error ) { + // TODO: Handle the error + } + }); + } ); } ); diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 0e955e4..b21da0f 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -41,7 +41,7 @@ class Activity_Dispatcher { * @return void */ public static function send_activity_or_announce( $wp_object, $type ) { - // check if a migration is needed before sending new posts + // check if a migration is needed before sending new post Migration::maybe_migrate(); if ( is_user_type_disabled( 'blog' ) ) { @@ -96,7 +96,7 @@ class Activity_Dispatcher { return; } - $transformer = Factory::instance()->get_transformer( $wp_object ); + $transformer = Factory::get_transformer( $wp_object ); if ( null !== $user_id && Users::APPLICATION_USER_ID !== $user_id ) { $transformer->change_wp_user_id( $user_id ); diff --git a/includes/class-admin.php b/includes/class-admin.php index dca3aee..31e1c11 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -95,7 +95,7 @@ class Admin { \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/settings.php' ); break; case 'followers': - \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/blog-user-followers-list.php' ); + \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/admin-followers-list.php' ); break; case 'welcome': default: @@ -300,8 +300,8 @@ class Admin { public static function enqueue_scripts( $hook_suffix ) { if ( false !== strpos( $hook_suffix, 'activitypub' ) ) { - wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), '1.0.0' ); - wp_enqueue_script( 'activitypub-admin-styles', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), '1.0.0', false ); + wp_enqueue_style( 'activitypub-admin-styles', plugins_url( 'assets/css/activitypub-admin.css', ACTIVITYPUB_PLUGIN_FILE ), array(), '1.0.1' ); + wp_enqueue_script( 'activitypub-admin-styles', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), '1.0.1', false ); } } } diff --git a/includes/collection/class-follow-requests.php b/includes/collection/class-follow-requests.php new file mode 100644 index 0000000..7f75d48 --- /dev/null +++ b/includes/collection/class-follow-requests.php @@ -0,0 +1,53 @@ +get_results( + $wpdb->prepare( + "SELECT SQL_CALC_FOUND_ROWS follow_request.ID AS id, follow_request.post_date AS published, follow_request.guid, follow_request.post_status AS 'status', follower.post_title AS 'post_title', follower.guid AS follower_guid, follower.id AS follower_id, follower.post_modified AS follower_modified + FROM {$wpdb->posts} AS follow_request + LEFT JOIN {$wpdb->posts} AS follower ON follow_request.post_parent = follower.ID + LEFT JOIN {$wpdb->postmeta} AS meta ON follow_request.ID = meta.post_id + WHERE follow_request.post_type = 'ap_follow_request' + AND meta.meta_key = 'activitypub_user_id' + AND meta.meta_value = %s + ORDER BY {$orderby} {$order} + LIMIT %d OFFSET %d", + $user_id, + $per_page, + 0 + ) + ); + $total_items = $wpdb->get_var("SELECT FOUND_ROWS()"); + + return compact( 'follow_requests', 'total_items' ); + } +} diff --git a/includes/handler/class-follow.php b/includes/handler/class-follow.php index 810680b..a14b36f 100644 --- a/includes/handler/class-follow.php +++ b/includes/handler/class-follow.php @@ -5,6 +5,7 @@ use Activitypub\Http; use Activitypub\Activity\Activity; use Activitypub\Collection\Users; use Activitypub\Collection\Followers; +use Activitypub\Model\Follow_Request; /** * Handle Follow requests @@ -20,7 +21,7 @@ class Follow { ); \add_action( - 'activitypub_followers_post_follow', + 'activitypub_send_follow_response', array( self::class, 'send_follow_response' ), 10, 4 @@ -50,60 +51,42 @@ class Follow { $activity['actor'] ); - do_action( - 'activitypub_followers_post_follow', - $activity['actor'], - $activity, - $user_id, - $follower - ); - } - - /** - * Send Accept response - * - * @param string $actor The Actor URL - * @param array $object The Activity object - * @param int $user_id The ID of the WordPress User - * @param Activitypub\Model\Follower $follower The Follower object - * - * @return void - */ - public static function send_follow_response( $actor, $object, $user_id, $follower ) { if ( \is_wp_error( $follower ) ) { - // it is not even possible to send a "Reject" because + // it is not even possible to send a "Reject" or "Accept" because // we can not get the Remote-Inbox return; } + + // save follow request by this follower + $follow_request = Follow_Request::save( $follower, $user_id, $activity['id'] ); - // only send minimal data - $object = array_intersect_key( - $object, - array_flip( - array( - 'id', - 'type', - 'actor', - 'object', - ) - ) - ); + if ( ! $user->get_manually_approves_followers() ) { + $follow_request->accept(); + } + + } - $user = Users::get_by_id( $user_id ); - - // get inbox - $inbox = $follower->get_shared_inbox(); - - // send "Accept" activity + /** + * Send Follow response + * + * @param Activitypub\Model\User $user The Target Users ActivityPub object + * @param Activitypub\Model\Follower $follower The Followers ActivityPub object + * @param array|object $object The ActivityPub follow object + * @param string $type The reponse object type: 'Accept' or 'Reject' + * + * @return void + */ + public static function send_follow_response( $user, $follower, $object, $type ) { + // send activity $activity = new Activity(); - $activity->set_type( 'Accept' ); + $activity->set_type( $type ); $activity->set_object( $object ); $activity->set_actor( $user->get_id() ); - $activity->set_to( $actor ); - $activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() ); + $activity->set_to( $follower->get_id() ); + $activity->set_id( $user->get_id() . '#accept-' . \preg_replace( '~^https?://~', '', $follower->get_id() ) . '-' . \time() ); $activity = $activity->to_json(); - Http::post( $inbox, $activity, $user_id ); + Http::post( $follower->get_shared_inbox(), $activity, $user->get__id() ); } } diff --git a/includes/handler/class-undo.php b/includes/handler/class-undo.php index 74d3dca..70d6c4e 100644 --- a/includes/handler/class-undo.php +++ b/includes/handler/class-undo.php @@ -2,7 +2,7 @@ namespace Activitypub\Handler; use Activitypub\Collection\Users; -use Activitypub\Collection\Followers; +use Activitypub\Model\Follow_Request; /** * Handle Undo requests @@ -39,9 +39,8 @@ class Undo { return; } - $user_id = $user->get__id(); - - Followers::remove_follower( $user_id, $activity['actor'] ); + $follow_request = Follow_Request::get_from_array( $activity['object'] ); + $follow_request->delete(); } } } diff --git a/includes/model/class-follow-request.php b/includes/model/class-follow-request.php new file mode 100644 index 0000000..be83c79 --- /dev/null +++ b/includes/model/class-follow-request.php @@ -0,0 +1,215 @@ +get__id() ) { + $follow_request->set__id( self::get_follow_request_id_by_uri( $follow_request->get_id() ) ); + } + return $follow_request; + } + + /** + * Retrieve the WordPress post id of the follow request by its id (URI) + */ + public static function get_follow_request_id_by_uri( $uri ) { + global $wpdb; + return $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE guid=%s", esc_sql( $uri ) ) ); + } + + + /** + * Check if the follow request is valid which means it fits to the already stored data. + * + * @param Follow_Request $follow_request The follow request to be checked. + * @return bool Whether the follow request is valid. + */ + public static function is_valid( $follow_request ) { + if ( self::class != get_class( $follow_request ) ) { + return false; + } + if ( 'Follow' != $follow_request->get_type() ) { + return false; + } + + $id = self::get_follow_request_id_by_uri( $follow_request->get_id() ); + if ( ! $id || is_wp_error( $id ) ) { + return false; + } + if ( self::FOLLOW_REQUEST_POST_TYPE != get_post_type( $id) ) { + return false; + } + + $post = get_post( $id ); + $follower = get_post_parent( $id ); + if ( $follower->guid != $follow_request->get_actor() ) { + return false; + } + return true; + } + + /** + * @param int $id + * @return Follow_Request $follow_request + */ + public static function from_wp_id( $id ) { + if ( self::FOLLOW_REQUEST_POST_TYPE != get_post_type( $id ) ){ + return; + } + $post = get_post( $id ); + + $follow_request = new static; + $follow_request->set_id( $post->guid ); + $follow_request->set__id( $post->ID ); + $follow_request->set_type( 'Follow' ); + + return $follow_request; + } + + /** + * Save the current Follower-Object. + * + * @param Follower $follower + * + * @return Follow_Request|WP_Error The Follow_Request or an WP_Error. + */ + public static function save( $follower, $user_id, $activity_id ) { + $follower_id = $follower->get__id(); + $meta_input = array( + 'activitypub_user_id' => $user_id, + ); + + $args = array( + 'guid' => $activity_id, + 'post_author' => 0, + 'post_type' => self::FOLLOW_REQUEST_POST_TYPE, + 'post_status' => 'pending', + 'post_parent' => $follower_id, + 'meta_input' => $meta_input, + 'mime_type' => 'text/plain' + ); + + $post_id = wp_insert_post( $args ); + + + return self::from_wp_id( $post_id ); + } + + /** + * Check if the user is allowed to handle this follow request. + * + * Usually needed for the ajax functions. + * @return bool Whether the user is allowed. + */ + public function can_handle_follow_request() { + $target_actor = get_post_meta( $this->get__id(), 'activitypub_user_id'); + if ( get_current_user_id() == $target_actor || current_user_can( 'manage_options' )) { + return true; + } + } + + /** + * Reject the follow request + */ + public function reject() { + wp_update_post( + array( + 'ID' => $this->get__id(), + 'post_status' => 'rejected', + ) + ); + $this->send_response( 'Reject' ); + $this->delete(); + } + + /** + * Approve the follow request + */ + public function approve() { + wp_update_post( + array( + 'ID' => $this->get__id(), + 'post_status' => 'approved', + ) + ); + $this->send_response( 'Accept' ); + } + + /** + * Delete the follow request + * + * This should only be called after it has been rejected. + */ + public function delete() { + wp_delete_post( $this->get__id() ); + } + + /** + * Prepere the sending of the follow request response and hand it over to the sending handler. + */ + public function send_response( $type ) { + $user_id = get_post_meta( $this->get__id(), 'activitypub_user_id')[0]; + $user = Users::get_by_id( $user_id ); + + $follower_id = wp_get_post_parent_id( $this->get__id() ); + $follower = Follower::init_from_cpt( get_post( $follower_id ) ); + + $actor = $follower->get_id(); + + $object = array( + 'id' => $this->get_id(), + 'type' => $this->get_type(), + 'actor' => $actor, + 'object' => $user, + ); + + do_action( 'activitypub_send_follow_response', $user, $follower, $object, $type); + } +} diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index b2833e9..591f01a 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -19,7 +19,7 @@ use Activitypub\Collection\Followers; */ class Follower extends Actor { /** - * The complete Remote-Profile of the Follower + * The complete Remote-Profile of the Follower. * * @var array */ @@ -34,6 +34,13 @@ class Follower extends Actor { return get_post_meta( $this->_id, 'activitypub_errors' ); } + /** + * Getter function for the internal WordPress id. + */ + public function get__id() { + return $this->_id; + } + /** * Get the Summary. * @@ -304,9 +311,9 @@ class Follower extends Actor { /** * Convert a Custom-Post-Type input to an Activitypub\Model\Follower. * - * @return string The JSON string. + * @param WP_Post The JSON string. * - * @return array Activitypub\Model\Follower + * @return \Activitypub\Model\Follower $object */ public static function init_from_cpt( $post ) { $actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true ); diff --git a/includes/table/class-follow-requests.php b/includes/table/class-follow-requests.php new file mode 100644 index 0000000..5cbd965 --- /dev/null +++ b/includes/table/class-follow-requests.php @@ -0,0 +1,290 @@ +user_id = $user_id; + } else { + $this->user_id = \get_current_user_id(); + } + + parent::__construct( + array( + 'singular' => \__( 'Follower', 'activitypub' ), + 'plural' => \__( 'Followers', 'activitypub' ), + 'ajax' => true, + ) + ); + } + + public function get_columns() { + return array( + 'cb' => '', + 'action' => \__( 'Action', 'activitypub' ), + 'name' => \__( 'Name', 'activitypub' ), + 'url' => \__( 'URL', 'activitypub' ), + 'status' => \__( 'Status', 'activitypub' ), + 'published' => \__( 'Created', 'activitypub' ), + 'modified' => \__( 'Last updated', 'activitypub' ), + ); + } + + public function get_sortable_columns() { + $sortable_columns = array( + 'status' => array( 'status', false), + 'name' => array( 'name', true ), + 'modified' => array( 'modified', false ), + 'published' => array( 'published', false ), + ); + + return $sortable_columns; + } + + public function prepare_items() { + $columns = $this->get_columns(); + $hidden = array(); + + $this->process_action(); + $this->_column_headers = array( $columns, $hidden, $this->get_sortable_columns() ); + + $page_num = $this->get_pagenum(); + $per_page = 20; + + $args = array(); + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['orderby'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['order'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['s'] ) && isset( $_REQUEST['_wpnonce'] ) ) { + $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); + if ( wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $args['s'] = sanitize_text_field( wp_unslash( $_GET['s'] ) ); + } + } + + $follow_requests_with_count = FollowerRequestCollection::get_follow_requests_for_user( $this->user_id, $per_page, $page_num, $args ); + + $follow_requests = $follow_requests_with_count['follow_requests']; + $counter = $follow_requests_with_count['total_items']; + $this->follow_requests_count = $counter; + + $this->items = array(); + $this->set_pagination_args( + array( + 'total_items' => $counter, + 'total_pages' => ceil( $counter / $per_page ), + 'per_page' => $per_page, + ) + ); + + foreach ( $follow_requests as $follow_request ) { + $item = array( + 'status' => esc_attr( $follow_request->status ), + 'name' => esc_attr( $follow_request->post_title ), + 'url' => esc_attr( $follow_request->follower_guid ), + 'guid' => esc_attr( $follow_request->guid ), + 'id' => esc_attr( $follow_request->id ), + 'published' => esc_attr( $follow_request->published ), + 'modified' => esc_attr( $follow_request->follower_modified ), + ); + + $this->items[] = $item; + } + } + + public function get_bulk_actions() { + return array( + 'reject' => __( 'Reject', 'activitypub' ), + 'approve' => __( 'Approve', 'activitypub' ), + ); + } + + public function column_default( $item, $column_name ) { + if ( ! array_key_exists( $column_name, $item ) ) { + return __( 'None', 'activitypub' ); + } + return $item[ $column_name ]; + } + + public function column_status( $item ) { + $status = $item['status']; + switch ( $status ) { + case 'approved': + $color = 'success'; + break; + case 'pending': + $color = 'warning'; + break; + case 'rejected': + $color = 'danger'; + break; + default: + $color = 'warning'; + } + return sprintf( + '%s', + $color, + ucfirst( $status ) + ); + } + + public function column_avatar( $item ) { + return sprintf( + '', + $item['icon'] + ); + } + + public function column_url( $item ) { + return sprintf( + '%s', + $item['url'], + $item['url'] + ); + } + + public function column_cb( $item ) { + return sprintf( '', esc_attr( $item['id'] ) ); + } + + public function ajax_response() { + $follow_action = $_REQUEST['follow_action']; + $id = $_REQUEST['follow_request']; + wp_verify_nonce( $_REQUEST['_wpnonce'], "activitypub_{$follow_action}_follow_request" ); + $follow_request = Follow_Request::from_wp_id( $id ); + if ( $follow_request->can_handle_follow_request() ) { + switch ( $follow_action ) { + case 'approve': + $follow_request->approve(); + wp_die( 'approved' ); + case 'reject': + $follow_request->reject(); + wp_die( 'rejected' ); + case 'delete': + $follow_request->delete(); + wp_die( 'deleted' ); + } + } + return; + } + + private static function display_follow_request_action_button( $id, $follow_action, $display = true ) { + $url = add_query_arg( + array( + 'follow_request' => $id, + 'action' => 'activitypub_handle_follow_request', + 'follow_action' => $follow_action, + '_wpnonce' => wp_create_nonce( "activitypub_{$follow_action}_follow_request" ), + ), + admin_url( 'admin-ajax.php' ) + ); + if ( $display ) { + $type = 'button'; + } else { + $type = 'hidden'; + } + printf( + '', + esc_attr( $type ), + esc_attr__( ucfirst( $follow_action ), 'activitypub' ), + esc_url( $url ) + ); + } + + public function column_action($item) { + $status = $item['status']; + + printf('
'); + + // TODO this can be written smarter, but at least it is readable. + if ( 'pending' === $status ) { + self::display_follow_request_action_button( $item['id'], 'approve'); + self::display_follow_request_action_button( $item['id'], 'reject'); + self::display_follow_request_action_button( $item['id'], 'delete', false); + } + + if ( 'approved' === $status ) { + self::display_follow_request_action_button( $item['id'], 'approve', false); + self::display_follow_request_action_button( $item['id'], 'reject'); + self::display_follow_request_action_button( $item['id'], 'delete', false); + } + + if ( 'rejected' === $status ) { + self::display_follow_request_action_button( $item['id'], 'approve', false); // TODO: Clarify with Mobilizon + self::display_follow_request_action_button( $item['id'], 'reject', false); + self::display_follow_request_action_button( $item['id'], 'delete'); + } + + printf('
'); + } + + public function process_action() { + if ( ! isset( $_REQUEST['follow_requests'] ) || ! isset( $_REQUEST['_wpnonce'] ) ) { + return false; + } + $nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ); + if ( ! wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { + return false; + } + + if ( ! current_user_can( 'edit_user', $this->user_id ) ) { + return false; + } + + $follow_requests = $_REQUEST['follow_requests']; // phpcs:ignore + + switch ( $this->current_action() ) { + case 'reject': + if ( ! is_array( $follow_requests ) ) { + $follow_requests = array( $follow_requests ); + } + foreach ( $follow_requests as $follow_request ) { + Follow_Request::from_wp_id( $follow_request )->reject(); + } + break; + case 'approve': + if ( ! is_array( $follow_requests ) ) { + $follow_requests = array( $follow_requests ); + } + foreach ( $follow_requests as $follow_request ) { + Follow_Request::from_wp_id( $follow_request )->approve(); + } + break; + } + } + + public function follow_requests_count() { + return $this->follow_requests_count; + } +} diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index 76b1f83..df9747b 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -16,7 +16,7 @@ class Followers extends WP_List_Table { public function __construct() { if ( get_current_screen()->id === 'settings_page_activitypub' ) { - $this->user_id = Users::APPLICATION_USER_ID; + $this->user_id = Users::BLOG_USER_ID; } else { $this->user_id = \get_current_user_id(); } diff --git a/templates/admin-followers-list.php b/templates/admin-followers-list.php new file mode 100644 index 0000000..216102c --- /dev/null +++ b/templates/admin-followers-list.php @@ -0,0 +1,57 @@ + '', + 'welcome' => '', + 'followers' => 'active', + ) +); + +// Draw the follow table for the blog user if it is activated. +if ( ! \Activitypub\is_user_disabled( \Activitypub\Collection\Users::BLOG_USER_ID ) ) : + $table = new \Activitypub\Table\Followers(); + $follower_count = $table->get_user_count(); + // translators: The follower count. + $followers_template = _n( 'Your blog profile currently has %s follower.', 'Your blog profile currently has %s followers.', $follower_count, 'activitypub' ); + ?> +
+

+ +
+ + + prepare_items(); + $table->search_box( 'Search', 'search' ); + $table->display(); + ?> +
+
+ +prepare_items(); +$follow_requests_count = $table->follow_requests_count; +// translators: The follower count. +$followers_template = _n( 'Your WordPress site currently has %s follow request.', 'Your WordPress site currently has %s follow requests.', $follow_requests_count, 'activitypub' ); +?> +
+

+ +
+ + + search_box( 'Search', 'search' ); + $table->display(); + + ?> +
+
diff --git a/templates/admin-header.php b/templates/admin-header.php index 67b91ba..49fe53c 100644 --- a/templates/admin-header.php +++ b/templates/admin-header.php @@ -15,13 +15,10 @@ - - -
diff --git a/templates/blog-user-followers-list.php b/templates/blog-user-followers-list.php deleted file mode 100644 index 1eaa7ee..0000000 --- a/templates/blog-user-followers-list.php +++ /dev/null @@ -1,28 +0,0 @@ - '', - 'welcome' => '', - 'followers' => 'active', - ) -); -$table = new \Activitypub\Table\Followers(); -$follower_count = $table->get_user_count(); -// translators: The follower count. -$followers_template = _n( 'Your blog profile currently has %s follower.', 'Your blog profile currently has %s followers.', $follower_count, 'activitypub' ); -?> -
-

- -
- - - prepare_items(); - $table->search_box( 'Search', 'search' ); - $table->display(); - ?> -
-