draft for managing follow requests
Some checks failed
PHP_CodeSniffer / phpcs (push) Has been cancelled
Unit Testing / phpunit (5.6, 6.2) (push) Has been cancelled
Unit Testing / phpunit (7.0) (push) Has been cancelled
Unit Testing / phpunit (7.2) (push) Has been cancelled
Unit Testing / phpunit (7.3) (push) Has been cancelled
Unit Testing / phpunit (7.4) (push) Has been cancelled
Unit Testing / phpunit (8.0) (push) Has been cancelled
Unit Testing / phpunit (8.1) (push) Has been cancelled
Unit Testing / phpunit (8.2) (push) Has been cancelled
Unit Testing / phpunit (latest) (push) Has been cancelled
Some checks failed
PHP_CodeSniffer / phpcs (push) Has been cancelled
Unit Testing / phpunit (5.6, 6.2) (push) Has been cancelled
Unit Testing / phpunit (7.0) (push) Has been cancelled
Unit Testing / phpunit (7.2) (push) Has been cancelled
Unit Testing / phpunit (7.3) (push) Has been cancelled
Unit Testing / phpunit (7.4) (push) Has been cancelled
Unit Testing / phpunit (8.0) (push) Has been cancelled
Unit Testing / phpunit (8.1) (push) Has been cancelled
Unit Testing / phpunit (8.2) (push) Has been cancelled
Unit Testing / phpunit (latest) (push) Has been cancelled
- the application actor is managed manually by default - no admin-options are included yet - the old new follower table only is used hardcoded by the application actor - no admin notifications are send yet - todo: a lot more
This commit is contained in:
parent
616667b0ba
commit
03b6a8e598
15 changed files with 749 additions and 88 deletions
|
@ -179,6 +179,14 @@ add_action(
|
||||||
0
|
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
|
* `get_plugin_data` wrapper
|
||||||
*
|
*
|
||||||
|
|
|
@ -197,3 +197,36 @@ input.blog-user-identifier {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-bottom: 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
jQuery( function( $ ) {
|
jQuery( function( $ ) {
|
||||||
|
const { __ } = wp.i18n;
|
||||||
// Accordion handling in various areas.
|
// Accordion handling in various areas.
|
||||||
$( '.activitypub-settings-accordion' ).on( 'click', '.activitypub-settings-accordion-trigger', function() {
|
$( '.activitypub-settings-accordion' ).on( 'click', '.activitypub-settings-accordion-trigger', function() {
|
||||||
var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) );
|
var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) );
|
||||||
|
@ -18,4 +19,50 @@ jQuery( function( $ ) {
|
||||||
}, 1200 );
|
}, 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 = $('<tr>').append($('<td>', { class: 'colspanchange', colspan: 7, text: text }));
|
||||||
|
tbody.append(newRow);
|
||||||
|
tbody.append("Some appended text.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function ( error ) {
|
||||||
|
// TODO: Handle the error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Activity_Dispatcher {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function send_activity_or_announce( $wp_object, $type ) {
|
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();
|
Migration::maybe_migrate();
|
||||||
|
|
||||||
if ( is_user_type_disabled( 'blog' ) ) {
|
if ( is_user_type_disabled( 'blog' ) ) {
|
||||||
|
@ -96,7 +96,7 @@ class Activity_Dispatcher {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$transformer = Factory::instance()->get_transformer( $wp_object );
|
$transformer = Factory::get_transformer( $wp_object );
|
||||||
|
|
||||||
if ( null !== $user_id && Users::APPLICATION_USER_ID !== $user_id ) {
|
if ( null !== $user_id && Users::APPLICATION_USER_ID !== $user_id ) {
|
||||||
$transformer->change_wp_user_id( $user_id );
|
$transformer->change_wp_user_id( $user_id );
|
||||||
|
|
|
@ -95,7 +95,7 @@ class Admin {
|
||||||
\load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/settings.php' );
|
\load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/settings.php' );
|
||||||
break;
|
break;
|
||||||
case 'followers':
|
case 'followers':
|
||||||
\load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/blog-user-followers-list.php' );
|
\load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/admin-followers-list.php' );
|
||||||
break;
|
break;
|
||||||
case 'welcome':
|
case 'welcome':
|
||||||
default:
|
default:
|
||||||
|
@ -300,8 +300,8 @@ class Admin {
|
||||||
|
|
||||||
public static function enqueue_scripts( $hook_suffix ) {
|
public static function enqueue_scripts( $hook_suffix ) {
|
||||||
if ( false !== strpos( $hook_suffix, 'activitypub' ) ) {
|
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_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.0', false );
|
wp_enqueue_script( 'activitypub-admin-styles', plugins_url( 'assets/js/activitypub-admin.js', ACTIVITYPUB_PLUGIN_FILE ), array( 'jquery' ), '1.0.1', false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
includes/collection/class-follow-requests.php
Normal file
53
includes/collection/class-follow-requests.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub\Collection;
|
||||||
|
|
||||||
|
use WP_Error;
|
||||||
|
use WP_Query;
|
||||||
|
use Activitypub\Http;
|
||||||
|
use Activitypub\Webfinger;
|
||||||
|
use Activitypub\Activity\Base_Object;
|
||||||
|
use Activitypub\Model\Follower;
|
||||||
|
|
||||||
|
use function Activitypub\is_tombstone;
|
||||||
|
use function Activitypub\get_remote_metadata_by_actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub Follow Requests Collection
|
||||||
|
*
|
||||||
|
* @author André Menrath
|
||||||
|
*/
|
||||||
|
class Follow_Requests {
|
||||||
|
/**
|
||||||
|
* Get a follow request together with information from the follower.
|
||||||
|
*
|
||||||
|
* @param int $user_id The ID of the WordPress User
|
||||||
|
* @param int $follower_id The follower ID
|
||||||
|
*
|
||||||
|
* @return \Activitypub\Model\Follow|null The Follower object or null
|
||||||
|
*/
|
||||||
|
public static function get_follow_requests_for_user( $user_id, $per_page, $page, $args ) {
|
||||||
|
$order = isset($args['order']) && strtolower($args['order']) === 'asc' ? 'ASC' : 'DESC';
|
||||||
|
$orderby = isset($args['orderby']) ? sanitize_text_field($args['orderby']) : 'published';
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
$follow_requests = $wpdb->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' );
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ use Activitypub\Http;
|
||||||
use Activitypub\Activity\Activity;
|
use Activitypub\Activity\Activity;
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Collection\Followers;
|
||||||
|
use Activitypub\Model\Follow_Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Follow requests
|
* Handle Follow requests
|
||||||
|
@ -20,7 +21,7 @@ class Follow {
|
||||||
);
|
);
|
||||||
|
|
||||||
\add_action(
|
\add_action(
|
||||||
'activitypub_followers_post_follow',
|
'activitypub_send_follow_response',
|
||||||
array( self::class, 'send_follow_response' ),
|
array( self::class, 'send_follow_response' ),
|
||||||
10,
|
10,
|
||||||
4
|
4
|
||||||
|
@ -50,60 +51,42 @@ class Follow {
|
||||||
$activity['actor']
|
$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 ) ) {
|
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
|
// we can not get the Remote-Inbox
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only send minimal data
|
// save follow request by this follower
|
||||||
$object = array_intersect_key(
|
$follow_request = Follow_Request::save( $follower, $user_id, $activity['id'] );
|
||||||
$object,
|
|
||||||
array_flip(
|
|
||||||
array(
|
|
||||||
'id',
|
|
||||||
'type',
|
|
||||||
'actor',
|
|
||||||
'object',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$user = Users::get_by_id( $user_id );
|
if ( ! $user->get_manually_approves_followers() ) {
|
||||||
|
$follow_request->accept();
|
||||||
|
}
|
||||||
|
|
||||||
// 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 = new Activity();
|
||||||
$activity->set_type( 'Accept' );
|
$activity->set_type( $type );
|
||||||
$activity->set_object( $object );
|
$activity->set_object( $object );
|
||||||
$activity->set_actor( $user->get_id() );
|
$activity->set_actor( $user->get_id() );
|
||||||
$activity->set_to( $actor );
|
$activity->set_to( $follower->get_id() );
|
||||||
$activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() );
|
$activity->set_id( $user->get_id() . '#accept-' . \preg_replace( '~^https?://~', '', $follower->get_id() ) . '-' . \time() );
|
||||||
|
|
||||||
$activity = $activity->to_json();
|
$activity = $activity->to_json();
|
||||||
|
|
||||||
Http::post( $inbox, $activity, $user_id );
|
Http::post( $follower->get_shared_inbox(), $activity, $user->get__id() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace Activitypub\Handler;
|
namespace Activitypub\Handler;
|
||||||
|
|
||||||
use Activitypub\Collection\Users;
|
use Activitypub\Collection\Users;
|
||||||
use Activitypub\Collection\Followers;
|
use Activitypub\Model\Follow_Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Undo requests
|
* Handle Undo requests
|
||||||
|
@ -39,9 +39,8 @@ class Undo {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user_id = $user->get__id();
|
$follow_request = Follow_Request::get_from_array( $activity['object'] );
|
||||||
|
$follow_request->delete();
|
||||||
Followers::remove_follower( $user_id, $activity['actor'] );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
215
includes/model/class-follow-request.php
Normal file
215
includes/model/class-follow-request.php
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub\Model;
|
||||||
|
|
||||||
|
use WP_Error;
|
||||||
|
|
||||||
|
use Activitypub\Activity\Activity;
|
||||||
|
use Activitypub\Activity\Base_Object;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
use Activitypub\Model\Follower;
|
||||||
|
use Activitypub\Http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub Follow Class
|
||||||
|
*
|
||||||
|
* This Object represents a Follow object.
|
||||||
|
* There is no direct reference to a WordPress User here.
|
||||||
|
*
|
||||||
|
* @author André Menrath
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/TR/activitypub/#follow-activity-inbox
|
||||||
|
*/
|
||||||
|
class Follow_Request extends Base_Object {
|
||||||
|
const FOLLOW_REQUEST_POST_TYPE = 'ap_follow_request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores theinternal WordPress post id of the post of type ap_follow_request
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id/URI of the follower
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal WordPress post id of the follower
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @return Follow_Request $follow_request
|
||||||
|
*/
|
||||||
|
public static function get_from_array( $array ) {
|
||||||
|
$follow_request = self::init_from_array( $array );
|
||||||
|
if ( ! self::is_valid( $follow_request ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( ! $follow_request->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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ use Activitypub\Collection\Followers;
|
||||||
*/
|
*/
|
||||||
class Follower extends Actor {
|
class Follower extends Actor {
|
||||||
/**
|
/**
|
||||||
* The complete Remote-Profile of the Follower
|
* The complete Remote-Profile of the Follower.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -34,6 +34,13 @@ class Follower extends Actor {
|
||||||
return get_post_meta( $this->_id, 'activitypub_errors' );
|
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.
|
* Get the Summary.
|
||||||
*
|
*
|
||||||
|
@ -304,9 +311,9 @@ class Follower extends Actor {
|
||||||
/**
|
/**
|
||||||
* Convert a Custom-Post-Type input to an Activitypub\Model\Follower.
|
* 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 ) {
|
public static function init_from_cpt( $post ) {
|
||||||
$actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true );
|
$actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true );
|
||||||
|
|
290
includes/table/class-follow-requests.php
Normal file
290
includes/table/class-follow-requests.php
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub\Table;
|
||||||
|
|
||||||
|
use WP_List_Table;
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
use Activitypub\Collection\Followers as FollowerCollection;
|
||||||
|
use Activitypub\Collection\Follow_Requests as FollowerRequestCollection;
|
||||||
|
use Activitypub\Model\Follow_Request;
|
||||||
|
|
||||||
|
use function Activitypub\object_to_uri;
|
||||||
|
|
||||||
|
if ( ! \class_exists( '\WP_List_Table' ) ) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table that shows all follow requests for a user and allows handling those requests.
|
||||||
|
*/
|
||||||
|
class Follow_Requests extends WP_List_Table {
|
||||||
|
private $user_id;
|
||||||
|
public $follow_requests_count = 0;
|
||||||
|
|
||||||
|
public function __construct( $user_id = null ) {
|
||||||
|
|
||||||
|
if ( $user_id ) {
|
||||||
|
$this->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' => '<input type="button">',
|
||||||
|
'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(
|
||||||
|
'<span class="activitypub-settings-label activitypub-settings-label-%s">%s</span>',
|
||||||
|
$color,
|
||||||
|
ucfirst( $status )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function column_avatar( $item ) {
|
||||||
|
return sprintf(
|
||||||
|
'<img src="%s" width="25px;" />',
|
||||||
|
$item['icon']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function column_url( $item ) {
|
||||||
|
return sprintf(
|
||||||
|
'<a href="%s" target="_blank">%s</a>',
|
||||||
|
$item['url'],
|
||||||
|
$item['url']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function column_cb( $item ) {
|
||||||
|
return sprintf( '<input type="checkbox" name="follow_requests[]" value="%s" />', 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(
|
||||||
|
'<input type="%s" class="button" value="%s" data-action="%s">',
|
||||||
|
esc_attr( $type ),
|
||||||
|
esc_attr__( ucfirst( $follow_action ), 'activitypub' ),
|
||||||
|
esc_url( $url )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function column_action($item) {
|
||||||
|
$status = $item['status'];
|
||||||
|
|
||||||
|
printf('<div class="activitypub-settings-action-buttons">');
|
||||||
|
|
||||||
|
// 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('</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ class Followers extends WP_List_Table {
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
if ( get_current_screen()->id === 'settings_page_activitypub' ) {
|
if ( get_current_screen()->id === 'settings_page_activitypub' ) {
|
||||||
$this->user_id = Users::APPLICATION_USER_ID;
|
$this->user_id = Users::BLOG_USER_ID;
|
||||||
} else {
|
} else {
|
||||||
$this->user_id = \get_current_user_id();
|
$this->user_id = \get_current_user_id();
|
||||||
}
|
}
|
||||||
|
|
57
templates/admin-followers-list.php
Normal file
57
templates/admin-followers-list.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Activitypub\Collection\Users;
|
||||||
|
|
||||||
|
\load_template(
|
||||||
|
__DIR__ . '/admin-header.php',
|
||||||
|
true,
|
||||||
|
array(
|
||||||
|
'settings' => '',
|
||||||
|
'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' );
|
||||||
|
?>
|
||||||
|
<div class="wrap activitypub-followers-page">
|
||||||
|
<p><?php \printf( \esc_html( $followers_template ), \esc_attr( $follower_count ) ); ?></p>
|
||||||
|
|
||||||
|
<form method="get">
|
||||||
|
<input type="hidden" name="page" value="activitypub" />
|
||||||
|
<input type="hidden" name="tab" value="followers" />
|
||||||
|
<?php
|
||||||
|
$table->prepare_items();
|
||||||
|
$table->search_box( 'Search', 'search' );
|
||||||
|
$table->display();
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php endif;
|
||||||
|
|
||||||
|
// Draw the the follow table for the application user with reject and accept options.
|
||||||
|
$table = new \Activitypub\Table\Follow_Requests( Users::APPLICATION_USER_ID );
|
||||||
|
$table->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' );
|
||||||
|
?>
|
||||||
|
<div class="wrap activitypub-followers-page">
|
||||||
|
<p><?php \printf( \esc_html( $followers_template ), \esc_attr( $follow_requests_count ) ); ?></p>
|
||||||
|
|
||||||
|
<form method="get">
|
||||||
|
<input type="hidden" name="page" value="activitypub" />
|
||||||
|
<input type="hidden" name="tab" value="followers" />
|
||||||
|
<?php
|
||||||
|
$table->search_box( 'Search', 'search' );
|
||||||
|
$table->display();
|
||||||
|
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -15,13 +15,10 @@
|
||||||
<?php \esc_html_e( 'Settings', 'activitypub' ); ?>
|
<?php \esc_html_e( 'Settings', 'activitypub' ); ?>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<?php if ( ! \Activitypub\is_user_disabled( \Activitypub\Collection\Users::BLOG_USER_ID ) ) : ?>
|
|
||||||
|
|
||||||
<a href="<?php echo \esc_url_raw( admin_url( 'options-general.php?page=activitypub&tab=followers' ) ); ?>" class="activitypub-settings-tab <?php echo \esc_attr( $args['followers'] ); ?>">
|
<a href="<?php echo \esc_url_raw( admin_url( 'options-general.php?page=activitypub&tab=followers' ) ); ?>" class="activitypub-settings-tab <?php echo \esc_attr( $args['followers'] ); ?>">
|
||||||
<?php \esc_html_e( 'Followers', 'activitypub' ); ?>
|
<?php \esc_html_e( 'Followers', 'activitypub' ); ?>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<hr class="wp-header-end">
|
<hr class="wp-header-end">
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
\load_template(
|
|
||||||
__DIR__ . '/admin-header.php',
|
|
||||||
true,
|
|
||||||
array(
|
|
||||||
'settings' => '',
|
|
||||||
'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' );
|
|
||||||
?>
|
|
||||||
<div class="wrap activitypub-followers-page">
|
|
||||||
<p><?php \printf( \esc_html( $followers_template ), \esc_attr( $follower_count ) ); ?></p>
|
|
||||||
|
|
||||||
<form method="get">
|
|
||||||
<input type="hidden" name="page" value="activitypub" />
|
|
||||||
<input type="hidden" name="tab" value="followers" />
|
|
||||||
<?php
|
|
||||||
$table->prepare_items();
|
|
||||||
$table->search_box( 'Search', 'search' );
|
|
||||||
$table->display();
|
|
||||||
?>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
Loading…
Reference in a new issue