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 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 ) {
$array = wp_json_decode( $json, true );
public static function init_from_json( $json ) {
$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 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();
foreach ( $array as $key => $value ) {
@ -581,6 +581,29 @@ class Base_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.
*

View file

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

View file

@ -64,37 +64,7 @@ class Followers {
register_post_meta(
self::POST_TYPE,
'preferred_username',
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',
'activitypub_inbox',
array(
'type' => 'string',
'single' => true,
@ -104,17 +74,7 @@ class Followers {
register_post_meta(
self::POST_TYPE,
'_shared_inbox',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => array( self::class, 'sanitize_url' ),
)
);
register_post_meta(
self::POST_TYPE,
'_errors',
'activitypub_errors',
array(
'type' => 'string',
'single' => false,
@ -130,10 +90,10 @@ class Followers {
register_post_meta(
self::POST_TYPE,
'_actor',
'activitypub_user_id',
array(
'type' => 'string',
'single' => true,
'single' => false,
'sanitize_callback' => function( $value ) {
return esc_sql( $value );
},
@ -197,13 +157,28 @@ class Followers {
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();
$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 ) ) {
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' );
}
@ -227,7 +202,7 @@ class Followers {
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(
$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(
esc_sql( self::POST_TYPE ),
esc_sql( $user_id ),
@ -254,7 +229,7 @@ class Followers {
if ( $post_id ) {
$post = get_post( $post_id );
return Follower::from_custom_post_type( $post );
return Follower::init_from_cpt( $post );
}
return null;
@ -310,16 +285,16 @@ class Followers {
*
* @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(
'post_type' => self::POST_TYPE,
'posts_per_page' => $number,
'offset' => $offset,
'paged' => $page,
'orderby' => 'ID',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => '_user_id',
'key' => 'activitypub_user_id',
'value' => $user_id,
),
),
@ -330,7 +305,7 @@ class Followers {
$items = array();
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;
@ -343,11 +318,11 @@ class 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(
'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',
'meta_query' => array(
array(
'key' => '_user_id',
'key' => 'activitypub_user_id',
'value' => $user_id,
),
),
@ -396,11 +371,11 @@ class Followers {
'fields' => 'ids',
'meta_query' => array(
array(
'key' => '_shared_inbox',
'key' => 'activitypub_inbox',
'compare' => 'EXISTS',
),
array(
'key' => '_user_id',
'key' => 'activitypub_user_id',
'value' => $user_id,
),
),
@ -418,7 +393,7 @@ class Followers {
$wpdb->prepare(
"SELECT DISTINCT meta_value FROM {$wpdb->postmeta}
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",
$posts
)
@ -458,7 +433,7 @@ class Followers {
$items = array();
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;
@ -478,7 +453,7 @@ class Followers {
'posts_per_page' => $number,
'meta_query' => array(
array(
'key' => 'errors',
'key' => 'activitypub_errors',
'compare' => 'EXISTS',
),
),
@ -488,9 +463,28 @@ class Followers {
$items = array();
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;
}
/**
* 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
/**
* 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.
*
* @return mixed
*/
public function get_errors() {
if ( $this->_errors ) {
return $this->_errors;
}
$this->_errors = get_post_meta( $this->_id, 'errors' );
return $this->_errors;
return get_post_meta( $this->_id, 'activitypub_errors' );
}
/**
@ -86,7 +39,7 @@ class Follower extends Actor {
* @return void
*/
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.
*/
public function count_errors() {
$errors = $this->get__errors();
$errors = $this->get_errors();
if ( is_array( $errors ) && ! empty( $errors ) ) {
return count( $errors );
@ -104,21 +57,13 @@ class Follower extends Actor {
return 0;
}
public function get_url() {
if ( ! $this->url ) {
return $this->id;
}
return $this->url;
}
/**
* Return the latest error message.
*
* @return string The error message.
*/
public function get_latest_error_message() {
$errors = $this->get__errors();
$errors = $this->get_errors();
if ( is_array( $errors ) && ! empty( $errors ) ) {
return reset( $errors );
@ -142,16 +87,33 @@ class Follower extends Actor {
* @return void
*/
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(
'ID' => $this->get__id(),
'guid' => $this->get_id(),
'post_title' => $this->get_name(),
'post_author' => 0,
'post_type' => Followers::POST_TYPE,
'post_name' => $this->get_id(),
'post_content' => $this->get_summary(),
'post_status' => 'publish',
'meta_input' => $this->get_post_meta_input(),
'ID' => $this->get__id(),
'guid' => esc_url_raw( $this->get_id() ),
'post_title' => esc_html( $this->get_name() ),
'post_author' => 0,
'post_type' => Followers::POST_TYPE,
'post_name' => esc_url_raw( $this->get_id() ),
'post_excerpt' => esc_html( $this->get_summary() ) ? $this->get_summary() : '',
'post_content' => esc_sql( $this->to_json() ),
'post_status' => 'publish',
'meta_input' => $this->get_post_meta_input(),
);
$post_id = wp_insert_post( $args );
@ -164,16 +126,17 @@ class Follower extends Actor {
* @return void
*/
public function upsert() {
if ( $this->_id ) {
$this->update();
} else {
$this->save();
}
$this->save();
}
/**
* 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
*/
public function delete() {
@ -186,27 +149,8 @@ class Follower extends Actor {
* @return void
*/
protected function get_post_meta_input() {
$attributes = array( 'inbox', '_shared_inbox', 'icon', 'preferred_username', '_actor', 'url' );
$meta_input = array();
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;
}
$meta_input['activitypub_inbox'] = esc_url_raw( $this->get_shared_inbox() );
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 Activitypub\Model\Follower The Follower Object.
* @return string|null The URL to the shared inbox, the inbox or null.
*/
public static function from_array( $array ) {
$object = parent::from_array( $array );
$object->set__actor( $array );
global $wpdb;
$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 );
public function get_shared_inbox() {
if ( ! empty( $this->get_endpoints()['sharedInbox'] ) ) {
return $this->get_endpoints()['sharedInbox'];
} elseif ( ! empty( $this->get_inbox() ) ) {
return $this->get_inbox();
}
if ( ! empty( $object->get_endpoints()['sharedInbox'] ) ) {
$object->_shared_inbox = $object->get_endpoints()['sharedInbox'];
} elseif ( ! empty( $object->get_inbox() ) ) {
$object->_shared_inbox = $object->get_inbox();
}
return $object;
return null;
}
/**
@ -271,16 +196,10 @@ class Follower extends Actor {
*
* @return array Activitypub\Model\Follower
*/
public static function from_custom_post_type( $post ) {
$object = new static();
public static function init_from_cpt( $post ) {
$object = self::init_from_json( $post->post_content );
$object->set__id( $post->ID );
$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_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();
$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 );
$this->items = array();

View file

@ -1,6 +1,5 @@
<div class="wrap">
<h1><?php \esc_html_e( 'Followers', 'activitypub' ); ?></h1>
<?php Activitypub\Migration::maybe_migrate(); ?>
<?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>

View file

@ -37,7 +37,7 @@ class Test_Activitypub_Activity extends WP_UnitTestCase {
'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( $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',
'inbox' => 'https://example.org/users/username/inbox',
'name' => 'username',
'prefferedUsername' => 'username',
'preferredUsername' => 'username',
),
'jon@example.com' => array(
'id' => 'https://example.com/author/jon',
'url' => 'https://example.com/author/jon',
'inbox' => 'https://example.com/author/jon/inbox',
'name' => 'jon',
'prefferedUsername' => 'jon',
'preferredUsername' => 'jon',
),
'doe@example.org' => array(
'id' => 'https://example.org/author/doe',
'url' => 'https://example.org/author/doe',
'inbox' => 'https://example.org/author/doe/inbox',
'name' => 'doe',
'prefferedUsername' => 'doe',
'preferredUsername' => 'doe',
),
'sally@example.org' => array(
'id' => 'http://sally.example.org',
'url' => 'http://sally.example.org',
'inbox' => 'http://sally.example.org/inbox',
'name' => 'jon',
'prefferedUsername' => 'jon',
'preferredUsername' => 'jon',
),
'12345@example.com' => array(
'id' => 'https://12345.example.com',
'url' => 'https://12345.example.com',
'inbox' => 'https://12345.example.com/inbox',
'name' => '12345',
'prefferedUsername' => '12345',
'preferredUsername' => '12345',
),
'user2@example.com' => array(
'id' => 'https://user2.example.com',
'url' => 'https://user2.example.com',
'inbox' => 'https://user2.example.com/inbox',
'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' );
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' );