decouple the adding/removing of follow relationships from adding/removing followers
Some checks are pending
PHP_CodeSniffer / phpcs (push) Waiting to run
Unit Testing / phpunit (5.6, 6.2) (push) Waiting to run
Unit Testing / phpunit (7.0) (push) Waiting to run
Unit Testing / phpunit (7.2) (push) Waiting to run
Unit Testing / phpunit (7.3) (push) Waiting to run
Unit Testing / phpunit (7.4) (push) Waiting to run
Unit Testing / phpunit (8.0) (push) Waiting to run
Unit Testing / phpunit (8.1) (push) Waiting to run
Unit Testing / phpunit (8.2) (push) Waiting to run
Unit Testing / phpunit (latest) (push) Waiting to run

also use post_content, and post_content_filtered to store the followers json object and inbox

fix phpcs

remove commented out code
This commit is contained in:
André Menrath 2023-12-27 15:39:53 +01:00
parent 32e08d7f68
commit 32acc511b1
8 changed files with 121 additions and 123 deletions

View file

@ -572,7 +572,7 @@ class Base_Object {
/**
* Convert JSON input to an array.
*
* @return string The object array.
* @param string The object array.
*
* @return \Activitypub\Activity\Base_Object An Object built from the JSON string.
*/

View file

@ -444,18 +444,6 @@ class Activitypub {
)
);
register_post_meta(
Followers::POST_TYPE,
'activitypub_actor_json',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => function ( $value ) {
return sanitize_text_field( $value );
},
)
);
do_action( 'activitypub_after_register_post_type' );
}

View file

@ -21,7 +21,9 @@ class Followers {
const CACHE_KEY_INBOXES = 'follower_inboxes_%s';
/**
* Add new Follower
* Add new Follower.
*
* This does not add the follow relationship. It is added when the Accept respone is sent.
*
* @param int $user_id The ID of the WordPress User
* @param string $actor The Actor URL
@ -49,6 +51,16 @@ class Followers {
return $follower_id;
}
return $follower;
}
/**
* Add the follow relationship.
*
* @param int|string $user_id The internal id of the target WordPress user that gets followed.
* @param int|string $follower_id The internal id of the follower actor.
*/
public static function add_follow_relationship( $user_id, $follower_id ) {
$post_meta = get_post_meta( $follower_id, 'activitypub_user_id' );
// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
@ -59,8 +71,6 @@ class Followers {
// Reset the cached inboxes for the followed user
wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
}
return $follower;
}
/**
@ -71,7 +81,7 @@ class Followers {
*
* @return bool|WP_Error True on success, false or WP_Error on failure.
*/
public static function remove_follower( $user_id, $actor ) {
public static function remove_follow_relationship( $user_id, $actor ) {
wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
$follower = self::get_follower( $user_id, $actor );
@ -114,6 +124,18 @@ class Followers {
return null;
}
/**
* Get a Follower by the internal ID.
*
* @param int $user_id The post ID of the WordPress Follower custom post type post.
*
* @return \Activitypub\Model\Follower|null The Follower object or null
*/
public static function get_follower_by_id( $follower_id ) {
$post = get_post( $follower_id );
return Follower::init_from_cpt( $post );
}
/**
* Get a Follower by Actor indepenent from the User.
*
@ -181,7 +203,7 @@ class Followers {
),
),
);
// TODO: handle follower with empty post_content or inbox which is saved in post_content_filtered
$args = wp_parse_args( $args, $defaults );
$query = new WP_Query( $args );
$total = $query->found_posts;
@ -204,18 +226,6 @@ class Followers {
public static function get_all_followers() {
$args = array(
'nopaging' => true,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'activitypub_inbox',
'compare' => 'EXISTS',
),
array(
'key' => 'activitypub_actor_json',
'compare' => 'EXISTS',
),
),
);
return self::get_followers( null, null, null, $args );
}
@ -239,18 +249,10 @@ class Followers {
'key' => 'activitypub_user_id',
'value' => $user_id,
),
array(
'key' => 'activitypub_inbox',
'compare' => 'EXISTS',
),
array(
'key' => 'activitypub_actor_json',
'compare' => 'EXISTS',
),
),
)
);
// TODO: handle follower with empty post_content or inbox which is saved in post_content_filtered
return $query->found_posts;
}
@ -263,6 +265,7 @@ class Followers {
*/
public static function get_inboxes( $user_id ) {
$cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id );
// TODO: enable caching of the inboxes: this is only for debugging purpose.
// $inboxes = wp_cache_get( $cache_key, 'activitypub' );
// if ( $inboxes ) {
@ -278,19 +281,10 @@ class Followers {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'activitypub_inbox',
'compare' => 'EXISTS',
),
array(
'key' => 'activitypub_user_id',
'value' => $user_id,
),
array(
'key' => 'activitypub_inbox',
'value' => '',
'compare' => '!=',
),
),
)
);
@ -301,42 +295,16 @@ class Followers {
return array();
}
$user = Users::get_by_id( $user_id );
if ( $user->get_manually_approved_followers() ) {
$accepted_follow_requests_query = new WP_Query(
array(
'nopaging' => true,
'post_type' => 'ap_follow_request',
'fields' => 'id=>parent',
'post_status' => 'publish',
'post_parent__in' => $follower_ids,
// phpcs:ign ore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'activitypub_user_id',
'value' => $user_id,
),
),
)
);
}
$accepted_follow_requests = $accepted_follow_requests_query->get_posts();
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$results = $wpdb->get_col(
$inboxes = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT meta_value FROM {$wpdb->postmeta}
WHERE post_id IN (" . implode( ', ', array_fill( 0, count( $follower_ids ), '%d' ) ) . ")
AND meta_key = 'activitypub_inbox'
AND meta_value IS NOT NULL",
"SELECT DISTINCT post_content_filtered FROM {$wpdb->posts}
WHERE ID IN (" . implode( ', ', array_fill( 0, count( $follower_ids ), '%d' ) ) . ')',
$follower_ids
)
);
$inboxes = array_filter( $results );
wp_cache_set( $cache_key, $inboxes, 'activitypub' );
return $inboxes;

View file

@ -75,17 +75,17 @@ class Follow {
*
* @return void
*/
public static function send_follow_response( $user, $follower, $object, $type ) {
public static function send_follow_response( $user, $inbox, $object, $type ) {
// send activity
$activity = new Activity();
$activity->set_type( $type );
$activity->set_object( $object );
$activity->set_actor( $user->get_id() );
$activity->set_to( $follower->get_id() );
$activity->set_id( $user->get_id() . '#accept-' . \preg_replace( '~^https?://~', '', $follower->get_id() ) . '-' . \time() );
$activity->set_to( $object['actor'] );
$activity->set_id( $user->get_id() . '#accept-' . \preg_replace( '~^https?://~', '', $object['actor'] ) . '-' . \time() );
$activity = $activity->to_json();
Http::post( $follower->get_shared_inbox(), $activity, $user->get__id() );
Http::post( $inbox, $activity, $user->get__id() );
}
}

View file

@ -23,6 +23,8 @@ class Undo {
*
* @param array $activity The JSON "Undo" Activity
* @param int $user_id The ID of the ID of the WordPress User
*
* @return void
*/
public static function handle_undo( $activity ) {
if (

View file

@ -3,11 +3,11 @@ namespace Activitypub\Model;
use WP_Error;
use Activitypub\Activity\Activity;
use Activitypub\Activity\Base_Object;
use Activitypub\Collection\Followers;
use Activitypub\Collection\Users;
use Activitypub\Model\Follower;
use Activitypub\Http;
/**
* ActivityPub Follow Class
@ -21,6 +21,8 @@ use Activitypub\Http;
*/
class Follow_Request extends Base_Object {
const FOLLOW_REQUEST_POST_TYPE = 'ap_follow_request';
const FOLLOW_REQUEST_STATUS_APPROVED = 'approved';
const FOLLOW_REQUEST_STATUS_REJECTED = 'rejected';
/**
* Stores theinternal WordPress post id of the post of type ap_follow_request
@ -113,6 +115,13 @@ class Follow_Request extends Base_Object {
return $follow_request;
}
/**
* Get the internal ID of the follower who issues the follow request.
*/
private function get_follower_id() {
return wp_get_post_parent_id( $this->get__id() );
}
/**
* Save the current Follower-Object.
*
@ -155,34 +164,62 @@ class Follow_Request extends Base_Object {
}
/**
* Reject the follow request
* Get the URL/URI of the Follower that issued the follow request.
*
* @return string The url/uri of the follower.
*/
public function reject() {
private function get_follower_actor() {
$follower_id = wp_get_post_parent_id( $this->get__id() );
$follower = Follower::init_from_cpt( get_post( $follower_id ) );
return $follower->get_id();
}
/**
* Save the status of the follow request.
*
* @param string $status The new status of the follow request.
*/
private function save_follow_request_status( $status ) {
wp_update_post(
array(
'ID' => $this->get__id(),
'post_status' => 'rejected',
'post_status' => $status,
)
);
}
/**
* Reject the follow request.
*/
public function reject() {
$user_id = get_post_meta( $this->get__id(), 'activitypub_user_id', true );
$actor = $this->get_follower_actor();
Followers::remove_follow_relationship( $user_id, $actor );
$this->save_follow_request_status( self::FOLLOW_REQUEST_STATUS_REJECTED );
$this->send_response( 'Reject' );
$this->delete();
}
/**
* Approve the follow request
* Approve the follow request.
*/
public function approve() {
wp_update_post(
array(
'ID' => $this->get__id(),
'post_status' => 'approved',
)
);
$user_id = get_post_meta( $this->get__id(), 'activitypub_user_id', true );
$follower_id = $this->get_follower_id();
Followers::add_follow_relationship( $user_id, $follower_id );
$this->save_follow_request_status( self::FOLLOW_REQUEST_STATUS_APPROVED );
$this->send_response( 'Accept' );
}
/**
* Delete the follow request
* Delete the follow request.
*
* This should only be called after it has been rejected.
*/
@ -195,20 +232,19 @@ class Follow_Request extends Base_Object {
*/
public function send_response( $type ) {
$user_id = get_post_meta( $this->get__id(), 'activitypub_user_id', true );
$user = Users::get_by_id( $user_id );
$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 ) );
$follower_id = $this->get_follower_id();
$actor = $follower->get_id();
$follower_inbox = get_post_field( 'post_content_filtered', $follower_id );
$object = array(
// Reconstruct the follow_object.
$follow_object = array(
'id' => $this->get_id(),
'type' => $this->get_type(),
'actor' => $actor,
'object' => $user,
);
do_action( 'activitypub_send_follow_response', $user, $follower, $object, $type );
do_action( 'activitypub_send_follow_response', $user, $follower_inbox, $follow_object, $type );
}
}

View file

@ -170,15 +170,17 @@ class Follower extends Actor {
}
$args = array(
'ID' => $this->get__id(),
'guid' => esc_url_raw( $this->get_id() ),
'post_title' => wp_strip_all_tags( sanitize_text_field( $this->get_name() ) ),
'post_author' => 0,
'post_type' => Followers::POST_TYPE,
'post_name' => esc_url_raw( $this->get_id() ),
'post_excerpt' => sanitize_text_field( wp_kses( $this->get_summary(), 'user_description' ) ),
'post_status' => 'publish',
'meta_input' => $this->get_post_meta_input(),
'ID' => $this->get__id(),
'guid' => esc_url_raw( $this->get_id() ),
'post_title' => wp_strip_all_tags( sanitize_text_field( $this->get_name() ) ),
'post_author' => 0,
'post_type' => Followers::POST_TYPE,
'post_name' => esc_url_raw( $this->get_id() ),
'post_excerpt' => sanitize_text_field( wp_kses( $this->get_summary(), 'user_description' ) ),
'post_status' => 'publish',
'post_content' => $this->to_json(),
'post_mime_type' => 'application/json',
'post_content_filtered' => $this->get_shared_inbox(),
);
$post_id = wp_insert_post( $args );
@ -202,7 +204,7 @@ class Follower extends Actor {
* Beware that this os deleting a Follower for ALL users!!!
*
* To delete only the User connection (unfollow)
* @see \Activitypub\Rest\Followers::remove_follower()
* @see \Activitypub\Rest\Followers::remove_follow_relationship()
*
* @return void
*/
@ -210,18 +212,20 @@ class Follower extends Actor {
wp_delete_post( $this->_id );
}
/**
* Update the post meta.
*
* @return void
*/
protected function get_post_meta_input() {
$meta_input = array();
$meta_input['activitypub_inbox'] = $this->get_shared_inbox();
$meta_input['activitypub_actor_json'] = $this->to_json();
// /**
// * Update the post meta.
// *
// * @return void
// */
// protected function get_post_meta_input() {
// $meta_input = array();
// // TODO: migrations - moved to post_content
// $meta_input['activitypub_inbox'] = $this->get_shared_inbox();
// // TODO: migrations - moved to post_content_filtered
// $meta_input['activitypub_actor_json'] = $this->to_json();
return $meta_input;
}
// return $meta_input;
// }
/**
* Get the icon.

View file

@ -185,7 +185,7 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
$follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' );
$this->assertEquals( 'https://example.com/author/jon', $follower2->get_url() );
\Activitypub\Collection\Followers::remove_follower( 1, 'https://example.com/author/jon' );
\Activitypub\Collection\Followers::remove_follow_relationship( 1, 'https://example.com/author/jon' );
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' );
$this->assertNull( $follower );