optimize and simplify followers

This commit is contained in:
Matthias Pfefferle 2023-07-06 14:42:18 +02:00
parent c1da689d66
commit 96c1e92151
8 changed files with 146 additions and 213 deletions

View file

@ -555,12 +555,12 @@ class Base_Object {
* *
* @return string The JSON string. * @return string The JSON string.
* *
* @return array An Object built from the JSON string. * @return \Activitypub\Activity\Base_Object An Object built from the JSON string.
*/ */
public static function from_json( $json ) { public static function init_from_json( $json ) {
$array = wp_json_decode( $json, true ); $array = \json_decode( $json, true );
return self::from_array( $array ); return self::init_from_array( $array );
} }
/** /**
@ -568,9 +568,9 @@ class Base_Object {
* *
* @return string The object array. * @return string The object array.
* *
* @return array An Object built from the JSON string. * @return \Activitypub\Activity\Base_Object An Object built from the JSON string.
*/ */
public static function from_array( $array ) { public static function init_from_array( $array ) {
$object = new static(); $object = new static();
foreach ( $array as $key => $value ) { foreach ( $array as $key => $value ) {
@ -581,6 +581,29 @@ class Base_Object {
return $object; return $object;
} }
/**
* Convert JSON input to an array and pre-fill the object.
*
* @param string $json The JSON string.
*/
public function from_json( $json ) {
$array = \json_decode( $json, true );
$this->from_array( $array );
}
/**
* Convert JSON input to an array and pre-fill the object.
*
* @param array $array The array.
*/
public function from_array( $array ) {
foreach ( $array as $key => $value ) {
$key = camel_to_snake_case( $key );
$this->set( $key, $value );
}
}
/** /**
* Convert Object to an array. * Convert Object to an array.
* *

View file

@ -111,14 +111,13 @@ class Scheduler {
$meta = get_remote_metadata_by_actor( $follower->get_url(), true ); $meta = get_remote_metadata_by_actor( $follower->get_url(), true );
if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
$follower->set_error( $meta ); Followers::add_error( $follower->get__id(), $meta );
} else { } else {
$follower->from_meta( $meta ); $follower->from_array( $meta );
}
$follower->update(); $follower->update();
} }
} }
}
/** /**
* Cleanup followers * Cleanup followers
@ -137,8 +136,7 @@ class Scheduler {
if ( 5 <= $follower->count_errors() ) { if ( 5 <= $follower->count_errors() ) {
$follower->delete(); $follower->delete();
} else { } else {
$follower->set_error( $meta ); Followers::add_error( $follower->get__id(), $meta );
$follower->update();
} }
} else { } else {
$follower->reset_errors(); $follower->reset_errors();

View file

@ -64,37 +64,7 @@ class Followers {
register_post_meta( register_post_meta(
self::POST_TYPE, self::POST_TYPE,
'preferred_username', 'activitypub_inbox',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => function( $value ) {
return sanitize_user( $value, true );
},
)
);
register_post_meta(
self::POST_TYPE,
'icon',
array(
'single' => true,
)
);
register_post_meta(
self::POST_TYPE,
'url',
array(
'type' => 'string',
'single' => false,
'sanitize_callback' => array( self::class, 'sanitize_url' ),
)
);
register_post_meta(
self::POST_TYPE,
'inbox',
array( array(
'type' => 'string', 'type' => 'string',
'single' => true, 'single' => true,
@ -104,17 +74,7 @@ class Followers {
register_post_meta( register_post_meta(
self::POST_TYPE, self::POST_TYPE,
'_shared_inbox', 'activitypub_errors',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => array( self::class, 'sanitize_url' ),
)
);
register_post_meta(
self::POST_TYPE,
'_errors',
array( array(
'type' => 'string', 'type' => 'string',
'single' => false, 'single' => false,
@ -130,10 +90,10 @@ class Followers {
register_post_meta( register_post_meta(
self::POST_TYPE, self::POST_TYPE,
'_actor', 'activitypub_user_id',
array( array(
'type' => 'string', 'type' => 'string',
'single' => true, 'single' => false,
'sanitize_callback' => function( $value ) { 'sanitize_callback' => function( $value ) {
return esc_sql( $value ); return esc_sql( $value );
}, },
@ -197,13 +157,28 @@ class Followers {
return $meta; return $meta;
} }
$follower = Follower::from_array( $meta ); $error = null;
$follower = new Follower();
if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
$follower->set_id( $actor );
$follower->set_url( $actor );
$error = $meta;
} else {
$follower->from_array( $meta );
}
$follower->upsert(); $follower->upsert();
$meta = get_post_meta( $follower->get__id(), '_user_id' ); $meta = get_post_meta( $follower->get__id(), 'activitypub_user_id' );
if ( $error ) {
self::add_error( $follower->get__id(), $error );
}
if ( is_array( $meta ) && ! in_array( $user_id, $meta, true ) ) { if ( is_array( $meta ) && ! in_array( $user_id, $meta, true ) ) {
add_post_meta( $follower->get__id(), '_user_id', $user_id ); add_post_meta( $follower->get__id(), 'activitypub_user_id', $user_id );
wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
} }
@ -227,7 +202,7 @@ class Followers {
return false; return false;
} }
return delete_post_meta( $follower->get__id(), '_user_id', $user_id ); return delete_post_meta( $follower->get__id(), 'activitypub_user_id', $user_id );
} }
/** /**
@ -243,7 +218,7 @@ class Followers {
$post_id = $wpdb->get_var( $post_id = $wpdb->get_var(
$wpdb->prepare( $wpdb->prepare(
"SELECT DISTINCT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = '_user_id' AND pm.meta_value = %d AND p.guid = %s", "SELECT DISTINCT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = 'activitypub_user_id' AND pm.meta_value = %d AND p.guid = %s",
array( array(
esc_sql( self::POST_TYPE ), esc_sql( self::POST_TYPE ),
esc_sql( $user_id ), esc_sql( $user_id ),
@ -254,7 +229,7 @@ class Followers {
if ( $post_id ) { if ( $post_id ) {
$post = get_post( $post_id ); $post = get_post( $post_id );
return Follower::from_custom_post_type( $post ); return Follower::init_from_cpt( $post );
} }
return null; return null;
@ -310,16 +285,16 @@ class Followers {
* *
* @return array The Term list of Followers, the format depends on $output * @return array The Term list of Followers, the format depends on $output
*/ */
public static function get_followers( $user_id, $number = null, $offset = null, $args = array() ) { public static function get_followers( $user_id, $number = null, $page = null, $args = array() ) {
$defaults = array( $defaults = array(
'post_type' => self::POST_TYPE, 'post_type' => self::POST_TYPE,
'posts_per_page' => $number, 'posts_per_page' => $number,
'offset' => $offset, 'paged' => $page,
'orderby' => 'ID', 'orderby' => 'ID',
'order' => 'DESC', 'order' => 'DESC',
'meta_query' => array( 'meta_query' => array(
array( array(
'key' => '_user_id', 'key' => 'activitypub_user_id',
'value' => $user_id, 'value' => $user_id,
), ),
), ),
@ -330,7 +305,7 @@ class Followers {
$items = array(); $items = array();
foreach ( $query->get_posts() as $post ) { foreach ( $query->get_posts() as $post ) {
$items[] = Follower::from_custom_post_type( $post ); // phpcs:ignore $items[] = Follower::init_from_cpt( $post ); // phpcs:ignore
} }
return $items; return $items;
@ -343,11 +318,11 @@ class Followers {
* *
* @return array The Term list of Followers. * @return array The Term list of Followers.
*/ */
public static function get_all_followers( $user_id = null ) { public static function get_all_followers() {
$args = array( $args = array(
'meta_query' => array(), 'meta_query' => array(),
); );
return self::get_followers( $user_id, null, null, $args ); return self::get_followers( null, null, null, $args );
} }
/** /**
@ -364,7 +339,7 @@ class Followers {
'fields' => 'ids', 'fields' => 'ids',
'meta_query' => array( 'meta_query' => array(
array( array(
'key' => '_user_id', 'key' => 'activitypub_user_id',
'value' => $user_id, 'value' => $user_id,
), ),
), ),
@ -396,11 +371,11 @@ class Followers {
'fields' => 'ids', 'fields' => 'ids',
'meta_query' => array( 'meta_query' => array(
array( array(
'key' => '_shared_inbox', 'key' => 'activitypub_inbox',
'compare' => 'EXISTS', 'compare' => 'EXISTS',
), ),
array( array(
'key' => '_user_id', 'key' => 'activitypub_user_id',
'value' => $user_id, 'value' => $user_id,
), ),
), ),
@ -418,7 +393,7 @@ class Followers {
$wpdb->prepare( $wpdb->prepare(
"SELECT DISTINCT meta_value FROM {$wpdb->postmeta} "SELECT DISTINCT meta_value FROM {$wpdb->postmeta}
WHERE post_id IN (" . implode( ', ', array_fill( 0, count( $posts ), '%d' ) ) . ") WHERE post_id IN (" . implode( ', ', array_fill( 0, count( $posts ), '%d' ) ) . ")
AND meta_key = '_shared_inbox' AND meta_key = 'activitypub_inbox'
AND meta_value IS NOT NULL", AND meta_value IS NOT NULL",
$posts $posts
) )
@ -458,7 +433,7 @@ class Followers {
$items = array(); $items = array();
foreach ( $posts->get_posts() as $follower ) { foreach ( $posts->get_posts() as $follower ) {
$items[] = Follower::from_custom_post_type( $follower ); // phpcs:ignore $items[] = Follower::init_from_cpt( $follower ); // phpcs:ignore
} }
return $items; return $items;
@ -478,7 +453,7 @@ class Followers {
'posts_per_page' => $number, 'posts_per_page' => $number,
'meta_query' => array( 'meta_query' => array(
array( array(
'key' => 'errors', 'key' => 'activitypub_errors',
'compare' => 'EXISTS', 'compare' => 'EXISTS',
), ),
), ),
@ -488,9 +463,28 @@ class Followers {
$items = array(); $items = array();
foreach ( $posts->get_posts() as $follower ) { foreach ( $posts->get_posts() as $follower ) {
$items[] = Follower::from_custom_post_type( $follower ); // phpcs:ignore $items[] = Follower::init_from_cpt( $follower ); // phpcs:ignore
} }
return $items; return $items;
} }
/**
* Undocumented function
*
* @param [type] $post_id
* @param [type] $error
* @return void
*/
public static function add_error( $post_id, $error ) {
if ( is_string( $error ) ) {
$error_message = $error;
} elseif ( is_wp_error( $error ) ) {
$error_message = $error->get_error_message();
} else {
$error_message = __( 'Unknown Error or misconfigured Error-Message', 'activitypub' );
}
return add_post_meta( $post_id, 'activitypub_errors', $error_message );
}
} }

View file

@ -24,60 +24,13 @@ class Follower extends Actor {
*/ */
protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* The complete Remote-Profile of the Follower
*
* @var array
*/
protected $_shared_inbox; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* The complete Remote-Profile of the Follower
*
* @var array
*/
protected $_actor; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* The latest received error.
*
* This will only temporary and will saved to $this->errors
*
* @var string
*/
protected $_error; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* A list of errors
*
* @var array
*/
protected $_errors; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* Set new Error
*
* @param mixed $error The latest HTTP-Error.
*
* @return void
*/
public function set_error( $error ) {
$this->_errors = array();
$this->_error = $error;
}
/** /**
* Get the errors. * Get the errors.
* *
* @return mixed * @return mixed
*/ */
public function get_errors() { public function get_errors() {
if ( $this->_errors ) { return get_post_meta( $this->_id, 'activitypub_errors' );
return $this->_errors;
}
$this->_errors = get_post_meta( $this->_id, 'errors' );
return $this->_errors;
} }
/** /**
@ -86,7 +39,7 @@ class Follower extends Actor {
* @return void * @return void
*/ */
public function reset_errors() { public function reset_errors() {
delete_post_meta( $this->_id, 'errors' ); delete_post_meta( $this->_id, 'activitypub_errors' );
} }
/** /**
@ -95,7 +48,7 @@ class Follower extends Actor {
* @return int The number of errors. * @return int The number of errors.
*/ */
public function count_errors() { public function count_errors() {
$errors = $this->get__errors(); $errors = $this->get_errors();
if ( is_array( $errors ) && ! empty( $errors ) ) { if ( is_array( $errors ) && ! empty( $errors ) ) {
return count( $errors ); return count( $errors );
@ -104,21 +57,13 @@ class Follower extends Actor {
return 0; return 0;
} }
public function get_url() {
if ( ! $this->url ) {
return $this->id;
}
return $this->url;
}
/** /**
* Return the latest error message. * Return the latest error message.
* *
* @return string The error message. * @return string The error message.
*/ */
public function get_latest_error_message() { public function get_latest_error_message() {
$errors = $this->get__errors(); $errors = $this->get_errors();
if ( is_array( $errors ) && ! empty( $errors ) ) { if ( is_array( $errors ) && ! empty( $errors ) ) {
return reset( $errors ); return reset( $errors );
@ -142,14 +87,31 @@ class Follower extends Actor {
* @return void * @return void
*/ */
public function save() { public function save() {
if ( ! $this->get__id() ) {
global $wpdb;
$post_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
esc_sql( $this->get_id() )
)
);
if ( $post_id ) {
$post = get_post( $post_id );
$this->set__id( $post->ID );
}
}
$args = array( $args = array(
'ID' => $this->get__id(), 'ID' => $this->get__id(),
'guid' => $this->get_id(), 'guid' => esc_url_raw( $this->get_id() ),
'post_title' => $this->get_name(), 'post_title' => esc_html( $this->get_name() ),
'post_author' => 0, 'post_author' => 0,
'post_type' => Followers::POST_TYPE, 'post_type' => Followers::POST_TYPE,
'post_name' => $this->get_id(), 'post_name' => esc_url_raw( $this->get_id() ),
'post_content' => $this->get_summary(), 'post_excerpt' => esc_html( $this->get_summary() ) ? $this->get_summary() : '',
'post_content' => esc_sql( $this->to_json() ),
'post_status' => 'publish', 'post_status' => 'publish',
'meta_input' => $this->get_post_meta_input(), 'meta_input' => $this->get_post_meta_input(),
); );
@ -164,16 +126,17 @@ class Follower extends Actor {
* @return void * @return void
*/ */
public function upsert() { public function upsert() {
if ( $this->_id ) {
$this->update();
} else {
$this->save(); $this->save();
} }
}
/** /**
* Delete the current Follower-Object. * Delete the current Follower-Object.
* *
* Beware that this os deleting a Follower for ALL users!!!
*
* To delete only the User connection (unfollow)
* @see \Activitypub\Rest\Followers::remove_follower()
*
* @return void * @return void
*/ */
public function delete() { public function delete() {
@ -186,27 +149,8 @@ class Follower extends Actor {
* @return void * @return void
*/ */
protected function get_post_meta_input() { protected function get_post_meta_input() {
$attributes = array( 'inbox', '_shared_inbox', 'icon', 'preferred_username', '_actor', 'url' );
$meta_input = array(); $meta_input = array();
$meta_input['activitypub_inbox'] = esc_url_raw( $this->get_shared_inbox() );
foreach ( $attributes as $attribute ) {
if ( $this->get( $attribute ) ) {
$meta_input[ $attribute ] = $this->get( $attribute );
}
}
if ( $this->_error ) {
if ( is_string( $this->_error ) ) {
$_error = $this->_error;
} elseif ( is_wp_error( $this->_error ) ) {
$_error = $this->_error->get_error_message();
} else {
$_error = __( 'Unknown Error or misconfigured Error-Message', 'activitypub' );
}
$meta_input['_errors'] = $_error;
}
return $meta_input; return $meta_input;
} }
@ -231,37 +175,18 @@ class Follower extends Actor {
} }
/** /**
* Converts an ActivityPub Array to an Follower Object. * Get the shared inbox, with a fallback to the inbox.
* *
* @param array $array The ActivityPub Array. * @return string|null The URL to the shared inbox, the inbox or null.
*
* @return Activitypub\Model\Follower The Follower Object.
*/ */
public static function from_array( $array ) { public function get_shared_inbox() {
$object = parent::from_array( $array ); if ( ! empty( $this->get_endpoints()['sharedInbox'] ) ) {
$object->set__actor( $array ); return $this->get_endpoints()['sharedInbox'];
} elseif ( ! empty( $this->get_inbox() ) ) {
global $wpdb; return $this->get_inbox();
$post_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE guid=%s",
esc_sql( $object->get_id() )
)
);
if ( $post_id ) {
$post = get_post( $post_id );
$object->set__id( $post->ID );
} }
if ( ! empty( $object->get_endpoints()['sharedInbox'] ) ) { return null;
$object->_shared_inbox = $object->get_endpoints()['sharedInbox'];
} elseif ( ! empty( $object->get_inbox() ) ) {
$object->_shared_inbox = $object->get_inbox();
}
return $object;
} }
/** /**
@ -271,16 +196,10 @@ class Follower extends Actor {
* *
* @return array Activitypub\Model\Follower * @return array Activitypub\Model\Follower
*/ */
public static function from_custom_post_type( $post ) { public static function init_from_cpt( $post ) {
$object = new static(); $object = self::init_from_json( $post->post_content );
$object->set__id( $post->ID ); $object->set__id( $post->ID );
$object->set_id( $post->guid ); $object->set_id( $post->guid );
$object->set_name( $post->post_title );
$object->set_summary( $post->post_content );
$object->set_url( get_post_meta( $post->ID, 'url', true ) );
$object->set_icon( get_post_meta( $post->ID, 'icon', true ) );
$object->set_preferred_username( get_post_meta( $post->ID, 'preferred_username', true ) );
$object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_published ) ) ); $object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_published ) ) );
$object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) ); $object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) );

View file

@ -55,7 +55,7 @@ class Followers extends WP_List_Table {
$page_num = $this->get_pagenum(); $page_num = $this->get_pagenum();
$per_page = 20; $per_page = 20;
$followers = FollowerCollection::get_followers( $this->user_id, $per_page, ( $page_num - 1 ) * $per_page ); $followers = FollowerCollection::get_followers( $this->user_id, $per_page, $page_num );
$counter = FollowerCollection::count_followers( $this->user_id ); $counter = FollowerCollection::count_followers( $this->user_id );
$this->items = array(); $this->items = array();

View file

@ -1,6 +1,5 @@
<div class="wrap"> <div class="wrap">
<h1><?php \esc_html_e( 'Followers', 'activitypub' ); ?></h1> <h1><?php \esc_html_e( 'Followers', 'activitypub' ); ?></h1>
<?php Activitypub\Migration::maybe_migrate(); ?>
<?php // translators: ?> <?php // translators: ?>
<p><?php \printf( \esc_html__( 'You currently have %s followers.', 'activitypub' ), \esc_attr( \Activitypub\Collection\Followers::count_followers( \get_current_user_id() ) ) ); ?></p> <p><?php \printf( \esc_html__( 'You currently have %s followers.', 'activitypub' ), \esc_attr( \Activitypub\Collection\Followers::count_followers( \get_current_user_id() ) ) ); ?></p>

View file

@ -37,7 +37,7 @@ class Test_Activitypub_Activity extends WP_UnitTestCase {
'content' => 'Hello world!', 'content' => 'Hello world!',
); );
$object = \Activitypub\Activity\Base_Object::from_array( $test_array ); $object = \Activitypub\Activity\Base_Object::init_from_array( $test_array );
$this->assertEquals( 'Hello world!', $object->get_content() ); $this->assertEquals( 'Hello world!', $object->get_content() );
$this->assertEquals( $test_array, $object->to_array() ); $this->assertEquals( $test_array, $object->to_array() );

View file

@ -6,42 +6,42 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
'url' => 'https://example.org/users/username', 'url' => 'https://example.org/users/username',
'inbox' => 'https://example.org/users/username/inbox', 'inbox' => 'https://example.org/users/username/inbox',
'name' => 'username', 'name' => 'username',
'prefferedUsername' => 'username', 'preferredUsername' => 'username',
), ),
'jon@example.com' => array( 'jon@example.com' => array(
'id' => 'https://example.com/author/jon', 'id' => 'https://example.com/author/jon',
'url' => 'https://example.com/author/jon', 'url' => 'https://example.com/author/jon',
'inbox' => 'https://example.com/author/jon/inbox', 'inbox' => 'https://example.com/author/jon/inbox',
'name' => 'jon', 'name' => 'jon',
'prefferedUsername' => 'jon', 'preferredUsername' => 'jon',
), ),
'doe@example.org' => array( 'doe@example.org' => array(
'id' => 'https://example.org/author/doe', 'id' => 'https://example.org/author/doe',
'url' => 'https://example.org/author/doe', 'url' => 'https://example.org/author/doe',
'inbox' => 'https://example.org/author/doe/inbox', 'inbox' => 'https://example.org/author/doe/inbox',
'name' => 'doe', 'name' => 'doe',
'prefferedUsername' => 'doe', 'preferredUsername' => 'doe',
), ),
'sally@example.org' => array( 'sally@example.org' => array(
'id' => 'http://sally.example.org', 'id' => 'http://sally.example.org',
'url' => 'http://sally.example.org', 'url' => 'http://sally.example.org',
'inbox' => 'http://sally.example.org/inbox', 'inbox' => 'http://sally.example.org/inbox',
'name' => 'jon', 'name' => 'jon',
'prefferedUsername' => 'jon', 'preferredUsername' => 'jon',
), ),
'12345@example.com' => array( '12345@example.com' => array(
'id' => 'https://12345.example.com', 'id' => 'https://12345.example.com',
'url' => 'https://12345.example.com', 'url' => 'https://12345.example.com',
'inbox' => 'https://12345.example.com/inbox', 'inbox' => 'https://12345.example.com/inbox',
'name' => '12345', 'name' => '12345',
'prefferedUsername' => '12345', 'preferredUsername' => '12345',
), ),
'user2@example.com' => array( 'user2@example.com' => array(
'id' => 'https://user2.example.com', 'id' => 'https://user2.example.com',
'url' => 'https://user2.example.com', 'url' => 'https://user2.example.com',
'inbox' => 'https://user2.example.com/inbox', 'inbox' => 'https://user2.example.com/inbox',
'name' => 'user2', 'name' => 'user2',
'prefferedUsername' => 'user2', 'preferredUsername' => 'user2',
), ),
); );
@ -223,7 +223,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase {
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' ); $follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' );
for ( $i = 1; $i <= 15; $i++ ) { for ( $i = 1; $i <= 15; $i++ ) {
add_post_meta( $follower->get__id(), 'errors', 'error ' . $i ); add_post_meta( $follower->get__id(), 'activitypub_errors', 'error ' . $i );
} }
$follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' ); $follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' );