From 87de87b2a587ba5eed65b047a85f7d9579c47e32 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Mon, 12 Jun 2023 11:38:15 -0500 Subject: [PATCH 01/18] Followers: use custom post types and postmeta to store --- includes/collection/class-followers.php | 185 ++++++++++-------------- includes/model/class-follower.php | 126 +++++++++------- 2 files changed, 154 insertions(+), 157 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 142a9a1..7217666 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -15,10 +15,11 @@ use function Activitypub\get_remote_metadata_by_actor; /** * ActivityPub Followers Collection * + * @author Matt Wiebe * @author Matthias Pfefferle */ class Followers { - const TAXONOMY = 'activitypub-followers'; + const POST_TYPE = 'activitypub_followers'; const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; /** @@ -27,8 +28,8 @@ class Followers { * @return void */ public static function init() { - // register "followers" taxonomy - self::register_taxonomy(); + // register "followers" post_type + self::register_post_type(); \add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow_request' ), 10, 2 ); \add_action( 'activitypub_inbox_undo', array( self::class, 'handle_undo_request' ), 10, 2 ); @@ -41,31 +42,26 @@ class Followers { * * @return void */ - public static function register_taxonomy() { - $args = array( - 'labels' => array( - 'name' => _x( 'Followers', 'taxonomy general name', 'activitypub' ), - 'singular_name' => _x( 'Followers', 'taxonomy singular name', 'activitypub' ), - 'menu_name' => __( 'Followers', 'activitypub' ), - ), - 'hierarchical' => false, - 'show_ui' => false, - 'show_in_menu' => false, - 'show_in_nav_menus' => false, - 'show_admin_column' => false, - 'query_var' => false, - 'rewrite' => false, - 'public' => false, - 'capabilities' => array( - 'edit_terms' => null, - ), + private static function register_post_type() { + register_post_type( + self::POST_TYPE, + array( + 'labels' => array( + 'name' => _x( 'Followers', 'post_type plural name', 'activitypub' ), + 'singular_name' => _x( 'Follower', 'post_type single name', 'activitypub' ), + ), + 'public' => false, + 'hierarchical' => false, + 'rewrite' => false, + 'query_var' => false, + 'delete_with_user' => false, + 'can_export' => true, + 'supports' => array(), + ) ); - register_taxonomy( self::TAXONOMY, 'user', $args ); - register_taxonomy_for_object_type( self::TAXONOMY, 'user' ); - - register_term_meta( - self::TAXONOMY, + register_post_meta( + self::POST_TYPE, 'name', array( 'type' => 'string', @@ -76,8 +72,8 @@ class Followers { ) ); - register_term_meta( - self::TAXONOMY, + register_post_meta( + self::POST_TYPE, 'username', array( 'type' => 'string', @@ -88,56 +84,48 @@ class Followers { ) ); - register_term_meta( - self::TAXONOMY, + register_post_meta( + self::POST_TYPE, 'avatar', array( 'type' => 'string', 'single' => true, - 'sanitize_callback' => function( $value ) { - if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { - return ''; - } - - return esc_url_raw( $value ); - }, + 'sanitize_callback' => array( self::class, 'sanitize_url' ), ) ); - register_term_meta( - self::TAXONOMY, + 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( 'type' => 'string', 'single' => true, - 'sanitize_callback' => function( $value ) { - if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { - throw new Exception( '"inbox" has to be a valid URL' ); - } - - return esc_url_raw( $value ); - }, + 'sanitize_callback' => array( self::class, 'sanitize_url' ), ) ); - register_term_meta( - self::TAXONOMY, + register_post_meta( + self::POST_TYPE, 'shared_inbox', array( 'type' => 'string', 'single' => true, - 'sanitize_callback' => function( $value ) { - if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { - return null; - } - - return esc_url_raw( $value ); - }, + 'sanitize_callback' => array( self::class, 'sanitize_url' ), ) ); - register_term_meta( - self::TAXONOMY, + register_post_meta( + self::POST_TYPE, 'updated_at', array( 'type' => 'string', @@ -152,8 +140,8 @@ class Followers { ) ); - register_term_meta( - self::TAXONOMY, + register_post_meta( + self::POST_TYPE, 'errors', array( 'type' => 'string', @@ -168,7 +156,15 @@ class Followers { ) ); - do_action( 'activitypub_after_register_taxonomy' ); + do_action( 'activitypub_after_register_post_type' ); + } + + public static function sanitize_url( $value ) { + if ( filter_var( $value, FILTER_VALIDATE_URL ) === false ) { + return null; + } + + return esc_url_raw( $value ); } /** @@ -217,12 +213,10 @@ class Followers { return $meta; } - $follower = new Follower( $actor ); + $follower = new Follower( $actor, $user_id ); $follower->from_meta( $meta ); $follower->upsert(); - $result = wp_set_object_terms( $user_id, $follower->get_actor(), self::TAXONOMY, true ); - if ( is_wp_error( $result ) ) { return $result; } else { @@ -241,7 +235,7 @@ class Followers { */ public static function remove_follower( $user_id, $actor ) { wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); - return wp_remove_object_terms( $user_id, $actor, self::TAXONOMY ); + return wp_remove_object_terms( $user_id, $actor, self::POST_TYPE ); } /** @@ -253,24 +247,8 @@ class Followers { * @return \Activitypub\Model\Follower The Follower object */ public static function get_follower( $user_id, $actor ) { - $terms = new WP_Term_Query( - array( - 'name' => $actor, - 'taxonomy' => self::TAXONOMY, - 'hide_empty' => false, - 'object_ids' => $user_id, - 'number' => 1, - ) - ); - - $term = $terms->get_terms(); - - if ( is_array( $term ) && ! empty( $term ) ) { - $term = reset( $term ); - return new Follower( $term->name ); - } - - return null; + $posts = self::get_followers( $user_id, null, null, array( 'name' => $actor ) ); + return is_empty( $posts ) ? null : $posts[0]; } /** @@ -321,21 +299,20 @@ class Followers { */ public static function get_followers( $user_id, $number = null, $offset = null, $args = array() ) { $defaults = array( - 'taxonomy' => self::TAXONOMY, - 'hide_empty' => false, - 'object_ids' => $user_id, + 'post_type' => self::POST_TYPE, + 'author' => $user_id, 'number' => $number, 'offset' => $offset, 'orderby' => 'id', - 'order' => 'ASC', + 'order' => 'DESC', ); $args = wp_parse_args( $args, $defaults ); - $terms = new WP_Term_Query( $args ); + $query = new WP_Query( $args ); $items = array(); - foreach ( $terms->get_terms() as $follower ) { - $items[] = new Follower( $follower->name ); // phpcs:ignore + foreach ( $query->get_posts() as $post ) { + $items[] = new Follower( $post ); // phpcs:ignore } return $items; @@ -348,16 +325,12 @@ class Followers { * * @return array The Term list of Followers. */ - public static function get_all_followers( $args = array() ) { - $defaults = array( - 'taxonomy' => self::TAXONOMY, - 'hide_empty' => false, + public static function get_all_followers( $user_id = null ) { + $args = array( + 'author' => null, + 'nopaging' => true, ); - - $args = wp_parse_args( $args, $defaults ); - $terms = new WP_Term_Query( $args ); - - return $terms->get_terms(); + return self::get_followers( $user_id, null, null, $args ); } /** @@ -367,8 +340,10 @@ class Followers { * * @return int The number of Followers */ - public static function count_followers( $user_id ) { - return count( self::get_followers( $user_id ) ); + public static function count_followers( $user_id = null ) { + // todo: rethink this. Don't we already get a total_posts count out of WP_Query? + // in the absence of that: caching. + return count( self::get_all_followers( $user_id ) ); } /** @@ -389,7 +364,7 @@ class Followers { // get all Followers of a ID of the WordPress User $terms = new WP_Term_Query( array( - 'taxonomy' => self::TAXONOMY, + 'taxonomy' => self::POST_TYPE, 'hide_empty' => false, 'object_ids' => $user_id, 'fields' => 'ids', @@ -436,7 +411,7 @@ class Followers { */ public static function get_outdated_followers( $number = 50, $older_than = 604800 ) { $args = array( - 'taxonomy' => self::TAXONOMY, + 'taxonomy' => self::POST_TYPE, 'number' => $number, 'meta_key' => 'updated_at', 'orderby' => 'meta_value_num', @@ -455,7 +430,7 @@ class Followers { $items = array(); foreach ( $terms->get_terms() as $follower ) { - $items[] = new Follower( $follower->name ); // phpcs:ignore + $items[] = new Follower( $follower ); // phpcs:ignore } return $items; @@ -471,7 +446,7 @@ class Followers { */ public static function get_faulty_followers( $number = 10 ) { $args = array( - 'taxonomy' => self::TAXONOMY, + 'taxonomy' => self::POST_TYPE, 'number' => $number, 'meta_query' => array( array( @@ -485,7 +460,7 @@ class Followers { $items = array(); foreach ( $terms->get_terms() as $follower ) { - $items[] = new Follower( $follower->name ); // phpcs:ignore + $items[] = new Follower( $follower ); // phpcs:ignore } return $items; diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index a663270..43a5934 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -9,6 +9,7 @@ use Activitypub\Collection\Followers; * This Object represents a single Follower. * There is no direct reference to a WordPress User here. * + * @author Matt Wiebe * @author Matthias Pfefferle * * @see https://www.w3.org/TR/activitypub/#follow-activity-inbox @@ -61,6 +62,11 @@ class Follower { */ private $avatar; + /** + * The URL to the Follower + */ + private $url; + /** * The URL to the Followers Inbox * @@ -108,6 +114,12 @@ class Follower { */ private $errors; + /** + * The WordPress User ID, or 0 for whole site. + * @var int + */ + private $user_id; + /** * Maps the meta fields to the local db fields * @@ -117,22 +129,29 @@ class Follower { 'name' => 'name', 'preferredUsername' => 'username', 'inbox' => 'inbox', + 'url' => 'url', ); /** * Constructor * - * @param WP_Post $post + * @param string|WP_Post $actor The Actor-URL or WP_Post Object. + * @param int $user_id The WordPress User ID. 0 Represents the whole site. */ - public function __construct( $actor ) { - $this->actor = $actor; + public function __construct( $actor, $user_id = 0 ) { + $this->user_id = $user_id; + if ( is_a( $actor, 'WP_Post' ) ) { + $post = $actor; + $this->actor = $post->post_name; + $this->user_id = $post->post_author; + } else { + $this->actor = $actor; + $post = Followers::get_follower( $user_id, $actor ); + } - $term = get_term_by( 'name', $actor, Followers::TAXONOMY ); - - if ( $term ) { - $this->id = $term->term_id; - $this->slug = $term->slug; - $this->meta = json_decode( $term->meta ); + if ( $post ) { + $this->id = $term->post_id; + $this->slug = $term->post_name; } } @@ -205,25 +224,38 @@ class Follower { * @return mixed The attribute value. */ public function get( $attribute ) { - if ( $this->$attribute ) { + if ( ! is_null( $this->$attribute ) ) { return $this->$attribute; } - - $attribute = get_term_meta( $this->id, $attribute, true ); - if ( $attribute ) { - $this->$attribute = $attribute; - return $attribute; + $attribute_value = get_post_meta( $this->id, $attribute, true ); + if ( $attribute_value ) { + $this->$attribute = $attribute_value; + return $attribute_value; } - $attribute = $this->get_meta_by( $attribute ); - if ( $attribute ) { - $this->$attribute = $attribute; - return $attribute; + $attribute_value = $this->get_meta_by( $attribute ); + if ( $attribute_value ) { + $this->$attribute = $attribute_value; + return $attribute_value; } return null; } + /** + * Get a URL for the follower. Creates one out of the actor if no URL was set. + */ + public function get_url() { + if ( $this->get( 'url' ) ) { + return $this->get( 'url' ); + } + $actor = $this->get_actor(); + // normalize + $actor = ltrim( $actor, '@' ); + $parts = explode( '@', $actor ); + return sprintf( 'https://%s/@%s', $parts[1], $parts[0] ); + } + /** * Set new Error * @@ -246,7 +278,7 @@ class Follower { return $this->errors; } - $this->errors = get_term_meta( $this->id, 'errors' ); + $this->errors = get_post_meta( $this->id, 'errors' ); return $this->errors; } @@ -298,7 +330,9 @@ class Follower { */ public function get_meta_by( $attribute ) { $meta = $this->get_meta(); - + if ( ! is_array( $meta ) ) { + return null; + } // try mapped data (see $this->map_meta) foreach ( $this->map_meta as $remote => $local ) { if ( $attribute === $local && isset( $meta[ $remote ] ) ) { @@ -306,11 +340,6 @@ class Follower { } } - // try ActivityPub attribtes - if ( ! empty( $this->map_meta[ $attribute ] ) ) { - return $this->map_meta[ $attribute ]; - } - return null; } @@ -333,16 +362,8 @@ class Follower { * @return void */ public function update() { - $term = wp_update_term( - $this->id, - Followers::TAXONOMY, - array( - 'description' => wp_json_encode( $this->get_meta( true ) ), - ) - ); - $this->updated_at = \time(); - $this->update_term_meta(); + $this->save( $this->id ); } /** @@ -350,19 +371,16 @@ class Follower { * * @return void */ - public function save() { - $term = wp_insert_term( - $this->actor, - Followers::TAXONOMY, - array( - 'slug' => sanitize_title( $this->get_actor() ), - 'description' => wp_json_encode( $this->get_meta() ), - ) + public function save( $post_id = null ) { + $args = array( + 'ID' => $post_id, + 'post_name' => $this->actor, + 'post_author' => $this->user_id, + 'post_type' => Followers::POST_TYPE, + 'meta_input' => $this->get_post_meta_input(), ); - - $this->id = $term['term_id']; - - $this->update_term_meta(); + $post = wp_insert_post( $args ); + $this->id = $post->ID; } /** @@ -384,7 +402,7 @@ class Follower { * @return void */ public function delete() { - wp_delete_term( $this->id, Followers::TAXONOMY ); + wp_delete_post( $this->id ); } /** @@ -392,12 +410,14 @@ class Follower { * * @return void */ - protected function update_term_meta() { - $attributes = array( 'inbox', 'shared_inbox', 'avatar', 'updated_at', 'name', 'username' ); + protected function get_post_meta_input() { + $attributes = array( 'inbox', 'shared_inbox', 'avatar', 'updated_at', 'name', 'username', 'url' ); + + $meta_input = array(); foreach ( $attributes as $attribute ) { if ( $this->get( $attribute ) ) { - update_term_meta( $this->id, $attribute, $this->get( $attribute ) ); + $meta_input[ $attribute ] = $this->get( $attribute ); } } @@ -410,7 +430,9 @@ class Follower { $error = __( 'Unknown Error or misconfigured Error-Message', 'activitypub' ); } - add_term_meta( $this->id, 'errors', $error ); + $meta_input['errors'] = array( $error ); } + + return $meta_input; } } From 133de30b68fae8dc1b349640423cad714018bd7e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 11:44:50 +0200 Subject: [PATCH 02/18] remove wp-sweep filter, because post-types are not a problem --- activitypub.php | 11 -------- integration/class-wp-sweep.php | 50 ---------------------------------- 2 files changed, 61 deletions(-) delete mode 100644 integration/class-wp-sweep.php diff --git a/activitypub.php b/activitypub.php index 8ca78f8..8f665fc 100644 --- a/activitypub.php +++ b/activitypub.php @@ -151,17 +151,6 @@ add_action( 0 ); -add_action( - 'plugins_loaded', - function() { - if ( defined( 'WP_SWEEP_VERSION' ) ) { - require_once \dirname( __FILE__ ) . '/integration/class-wp-sweep.php'; - Integration\Wp_Sweep::init(); - } - }, - 0 -); - /** * `get_plugin_data` wrapper * diff --git a/integration/class-wp-sweep.php b/integration/class-wp-sweep.php deleted file mode 100644 index 730edba..0000000 --- a/integration/class-wp-sweep.php +++ /dev/null @@ -1,50 +0,0 @@ - 'ids' ) ); - - $excluded_term_ids = array_merge( $excluded_term_ids, $followers ); - - return array_unique( $excluded_term_ids ); - } -} From 7ed998d81f8d9a70a4802057c803ab9956b382e8 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 11:45:25 +0200 Subject: [PATCH 03/18] fix follower table --- includes/table/class-followers.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index 03fb70f..b83403f 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -16,6 +16,7 @@ class Followers extends WP_List_Table { 'name' => \__( 'Name', 'activitypub' ), 'username' => \__( 'Username', 'activitypub' ), 'identifier' => \__( 'Identifier', 'activitypub' ), + 'updated_at' => \__( 'Last updated', 'activitypub' ), 'errors' => \__( 'Errors', 'activitypub' ), 'latest-error' => \__( 'Latest Error Message', 'activitypub' ), ); @@ -35,8 +36,8 @@ class Followers extends WP_List_Table { $page_num = $this->get_pagenum(); $per_page = 20; - $follower = FollowerCollection::get_followers( \get_current_user_id(), $per_page, ( $page_num - 1 ) * $per_page ); - $counter = FollowerCollection::count_followers( \get_current_user_id() ); + $followers = FollowerCollection::get_followers( \get_current_user_id(), $per_page, ( $page_num - 1 ) * $per_page ); + $counter = FollowerCollection::count_followers( \get_current_user_id() ); $this->items = array(); $this->set_pagination_args( @@ -47,12 +48,13 @@ class Followers extends WP_List_Table { ) ); - foreach ( $follower as $follower ) { + foreach ( $followers as $follower ) { $item = array( 'avatar' => esc_attr( $follower->get_avatar() ), 'name' => esc_attr( $follower->get_name() ), 'username' => esc_attr( $follower->get_username() ), 'identifier' => esc_attr( $follower->get_actor() ), + 'updated_at' => esc_attr( $follower->get_updated_at() ), 'errors' => $follower->count_errors(), 'latest-error' => $follower->get_latest_error_message(), ); From 8b7744a5eaf4f62f7dfced5af277c48384da95f7 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 11:47:50 +0200 Subject: [PATCH 04/18] fix queries --- includes/collection/class-followers.php | 77 +++++++++++++++---------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 7217666..940e9f2 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -3,7 +3,7 @@ namespace Activitypub\Collection; use WP_Error; use Exception; -use WP_Term_Query; +use WP_Query; use Activitypub\Http; use Activitypub\Webfinger; use Activitypub\Model\Activity; @@ -19,7 +19,7 @@ use function Activitypub\get_remote_metadata_by_actor; * @author Matthias Pfefferle */ class Followers { - const POST_TYPE = 'activitypub_followers'; + const POST_TYPE = 'activitypub_follower'; const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; /** @@ -50,7 +50,7 @@ class Followers { 'name' => _x( 'Followers', 'post_type plural name', 'activitypub' ), 'singular_name' => _x( 'Follower', 'post_type single name', 'activitypub' ), ), - 'public' => false, + 'public' => true, 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, @@ -213,16 +213,15 @@ class Followers { return $meta; } - $follower = new Follower( $actor, $user_id ); + $follower = new Follower( $actor ); $follower->from_meta( $meta ); $follower->upsert(); - if ( is_wp_error( $result ) ) { - return $result; - } else { - wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); - return $follower; - } + add_post_meta( $follower->get_id(), 'user_id', $user_id ); + + wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); + + return $follower; } /** @@ -235,20 +234,34 @@ class Followers { */ public static function remove_follower( $user_id, $actor ) { wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); + return wp_remove_object_terms( $user_id, $actor, self::POST_TYPE ); } /** - * Remove a Follower + * Get a Follower * * @param int $user_id The ID of the WordPress User * @param string $actor The Actor URL * * @return \Activitypub\Model\Follower The Follower object */ - public static function get_follower( $user_id, $actor ) { - $posts = self::get_followers( $user_id, null, null, array( 'name' => $actor ) ); - return is_empty( $posts ) ? null : $posts[0]; + public static function get_follower( $actor ) { + global $wpdb; + + $post_id = $wpdb->get_var( + $wpdb->prepare( + "SELECT ID FROM $wpdb->posts WHERE guid=%s", + esc_sql( $actor ) + ) + ); + + if ( $post_id ) { + $post = get_post( $post_id ); + return new Follower( $post ); + } + + return null; } /** @@ -299,12 +312,17 @@ class Followers { */ public static function get_followers( $user_id, $number = null, $offset = null, $args = array() ) { $defaults = array( - 'post_type' => self::POST_TYPE, - 'author' => $user_id, - 'number' => $number, - 'offset' => $offset, - 'orderby' => 'id', - 'order' => 'DESC', + 'post_type' => self::POST_TYPE, + 'posts_per_page' => $number, + 'offset' => $offset, + 'orderby' => 'ID', + 'order' => 'DESC', + 'meta_query' => array( + array( + 'key' => 'user_id', + 'value' => $user_id, + ), + ), ); $args = wp_parse_args( $args, $defaults ); @@ -362,11 +380,10 @@ class Followers { } // get all Followers of a ID of the WordPress User - $terms = new WP_Term_Query( + $terms = new WP_Query( array( - 'taxonomy' => self::POST_TYPE, - 'hide_empty' => false, - 'object_ids' => $user_id, + 'post_type' => self::POST_TYPE, + 'author' => $user_id, 'fields' => 'ids', 'meta_query' => array( array( @@ -377,16 +394,16 @@ class Followers { ) ); - $terms = $terms->get_terms(); + $posts = $posts->get_posts(); - if ( ! $terms ) { + if ( ! $posts ) { return array(); } global $wpdb; $results = $wpdb->get_col( $wpdb->prepare( - "SELECT DISTINCT meta_value FROM {$wpdb->termmeta} + "SELECT DISTINCT meta_value FROM {$wpdb->posts} WHERE term_id IN (" . implode( ', ', array_fill( 0, count( $terms ), '%d' ) ) . ") AND meta_key = 'shared_inbox' AND meta_value IS NOT NULL", @@ -446,9 +463,9 @@ class Followers { */ public static function get_faulty_followers( $number = 10 ) { $args = array( - 'taxonomy' => self::POST_TYPE, - 'number' => $number, - 'meta_query' => array( + 'post_type' => self::POST_TYPE, + 'posts_per_page' => $number, + 'meta_query' => array( array( 'key' => 'errors', 'compare' => 'EXISTS', From fc0fc295bb6d639e503815625b89f2719becc0ec Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 11:48:09 +0200 Subject: [PATCH 05/18] fix follower creation --- includes/model/class-follower.php | 70 +++++++++++++++++-------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 43a5934..9e85cd6 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -1,6 +1,7 @@ user_id = $user_id; - if ( is_a( $actor, 'WP_Post' ) ) { + public function __construct( $actor ) { + $post = null; + + if ( \is_a( $actor, 'WP_Post' ) ) { $post = $actor; - $this->actor = $post->post_name; - $this->user_id = $post->post_author; } else { - $this->actor = $actor; - $post = Followers::get_follower( $user_id, $actor ); + global $wpdb; + + $post_id = $wpdb->get_var( + $wpdb->prepare( + "SELECT ID FROM $wpdb->posts WHERE guid=%s", + esc_sql( $actor ) + ) + ); + + if ( $post_id ) { + $post = get_post( $post_id ); + } else { + $this->actor = $actor; + } } if ( $post ) { - $this->id = $term->post_id; - $this->slug = $term->post_name; + $this->id = $post->ID; + $this->actor = $post->guid; + $this->updated_at = $post->post_modified; } } @@ -227,7 +230,9 @@ class Follower { if ( ! is_null( $this->$attribute ) ) { return $this->$attribute; } + $attribute_value = get_post_meta( $this->id, $attribute, true ); + if ( $attribute_value ) { $this->$attribute = $attribute_value; return $attribute_value; @@ -288,7 +293,7 @@ class Follower { * @return void */ public function reset_errors() { - delete_term_meta( $this->id, 'errors' ); + delete_post_meta( $this->id, 'errors' ); } /** @@ -363,7 +368,7 @@ class Follower { */ public function update() { $this->updated_at = \time(); - $this->save( $this->id ); + $this->save(); } /** @@ -371,16 +376,19 @@ class Follower { * * @return void */ - public function save( $post_id = null ) { + public function save() { $args = array( - 'ID' => $post_id, - 'post_name' => $this->actor, - 'post_author' => $this->user_id, - 'post_type' => Followers::POST_TYPE, - 'meta_input' => $this->get_post_meta_input(), + 'ID' => $this->id, + 'guid' => $this->actor, + 'post_title' => $this->get_name(), + 'post_author' => 0, + 'post_type' => Followers::POST_TYPE, + 'post_content' => wp_json_encode( $this->meta ), + 'post_modified' => gmdate( 'Y-m-d H:i:s', $this->updated_at ), + 'meta_input' => $this->get_post_meta_input(), ); - $post = wp_insert_post( $args ); - $this->id = $post->ID; + $post_id = wp_insert_post( $args ); + $this->id = $post_id; } /** @@ -406,12 +414,12 @@ class Follower { } /** - * Update the term meta. + * Update the post meta. * * @return void */ protected function get_post_meta_input() { - $attributes = array( 'inbox', 'shared_inbox', 'avatar', 'updated_at', 'name', 'username', 'url' ); + $attributes = array( 'inbox', 'shared_inbox', 'avatar', 'name', 'username' ); $meta_input = array(); From 9036b644d1ad2521a0630de080069228fdec4ab6 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 11:48:43 +0200 Subject: [PATCH 06/18] add user connection --- includes/class-migration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index 7e7cd9a..36882cb 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -83,7 +83,7 @@ class Migration { $follower->upsert(); - $result = wp_set_object_terms( $user_id, $follower->get_actor(), Followers::TAXONOMY, true ); + add_post_meta( $follower->get_id(), 'user_id', $user_id ); } } } From fcf6740d36272cfee4dea01c4fe1e753b088bab0 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 11:53:07 +0200 Subject: [PATCH 07/18] fix query --- includes/collection/class-followers.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 940e9f2..f652d24 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -380,16 +380,19 @@ class Followers { } // get all Followers of a ID of the WordPress User - $terms = new WP_Query( + $posts = new WP_Query( array( 'post_type' => self::POST_TYPE, - 'author' => $user_id, 'fields' => 'ids', 'meta_query' => array( array( 'key' => 'inbox', 'compare' => 'EXISTS', ), + array( + 'key' => 'user_id', + 'value' => $user_id, + ), ), ) ); From 441412150278170bcaa8b1f836b326e0fbf0904c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 12:13:30 +0200 Subject: [PATCH 08/18] add missing user_id --- includes/collection/class-followers.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index f652d24..96d268b 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -241,12 +241,12 @@ class Followers { /** * Get a Follower * - * @param int $user_id The ID of the WordPress User - * @param string $actor The Actor URL + * @param int $user_id The ID of the WordPress User + * @param string $actor The Actor URL * * @return \Activitypub\Model\Follower The Follower object */ - public static function get_follower( $actor ) { + public static function get_follower( $user_id, $actor ) { global $wpdb; $post_id = $wpdb->get_var( From 37c61fbf07b7bd9dc50aae2b0f6e804ba29f83cd Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 12:17:48 +0200 Subject: [PATCH 09/18] fix queries --- includes/collection/class-followers.php | 29 +++++++++++-------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 96d268b..601d129 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -431,25 +431,22 @@ class Followers { */ public static function get_outdated_followers( $number = 50, $older_than = 604800 ) { $args = array( - 'taxonomy' => self::POST_TYPE, - 'number' => $number, - 'meta_key' => 'updated_at', - 'orderby' => 'meta_value_num', - 'order' => 'DESC', - 'meta_query' => array( + 'post_type' => self::POST_TYPE, + 'posts_per_page' => $number, + 'orderby' => 'modified', + 'order' => 'DESC', + 'date_query' => array( array( - 'key' => 'updated_at', - 'value' => time() - $older_than, - 'type' => 'numeric', - 'compare' => '<=', + 'column' => 'post_modified_gmt', + 'before' => 604800, ), ), ); - $terms = new WP_Term_Query( $args ); + $posts = new WP_Query( $args ); $items = array(); - foreach ( $terms->get_terms() as $follower ) { + foreach ( $posts->get_posts() as $follower ) { $items[] = new Follower( $follower ); // phpcs:ignore } @@ -470,16 +467,16 @@ class Followers { 'posts_per_page' => $number, 'meta_query' => array( array( - 'key' => 'errors', - 'compare' => 'EXISTS', + 'key' => 'errors', + 'compare' => 'EXISTS', ), ), ); - $terms = new WP_Term_Query( $args ); + $posts = new WP_Query( $args ); $items = array(); - foreach ( $terms->get_terms() as $follower ) { + foreach ( $posts->get_posts() as $follower ) { $items[] = new Follower( $follower ); // phpcs:ignore } From 46f376e05eb7d52b4a2757f17a0be23f99769bf5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Jun 2023 12:24:13 +0200 Subject: [PATCH 10/18] fix tests --- includes/model/class-follower.php | 5 ++++- tests/test-class-db-activitypub-followers.php | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 9e85cd6..7b41bb0 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -367,7 +367,10 @@ class Follower { * @return void */ public function update() { - $this->updated_at = \time(); + if ( ! $this->updated_at ) { + $this->updated_at = \time(); + } + $this->save(); } diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index a04145a..e660c00 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -109,7 +109,8 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $follower = new \Activitypub\Model\Follower( 'https://example.com/author/jon' ); - update_term_meta( $follower->get_id(), 'updated_at', \time() - 804800 ); + $follower->set_updates_at( \time() - 804800 ); + $follower->update(); $followers = \Activitypub\Collection\Followers::get_outdated_followers(); $this->assertEquals( 1, count( $followers ) ); @@ -129,7 +130,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $follower = new \Activitypub\Model\Follower( 'http://sally.example.org' ); for ( $i = 1; $i <= 15; $i++ ) { - add_term_meta( $follower->get_id(), 'errors', 'error ' . $i ); + add_post_meta( $follower->get_id(), 'errors', 'error ' . $i ); } $follower = new \Activitypub\Model\Follower( 'http://sally.example.org' ); From 793214cea2a438805db0e28653d8b2c0c5eff131 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 16 Jun 2023 11:40:26 +0200 Subject: [PATCH 11/18] now tests are green again --- docker-compose-test.yml | 8 ++++++ includes/collection/class-followers.php | 22 ++++++++-------- includes/model/class-follower.php | 6 ++--- tests/test-class-db-activitypub-followers.php | 26 ++++++++++++++++--- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 4c69e44..411f3fd 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -6,11 +6,19 @@ services: environment: MYSQL_DATABASE: activitypub-test MYSQL_ROOT_PASSWORD: activitypub-test + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3306"] + interval: 5s + timeout: 2s + retries: 5 test-php: build: context: . dockerfile: Dockerfile + depends_on: + test-db: + condition: service_healthy links: - test-db volumes: diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 601d129..ab5be32 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -217,7 +217,7 @@ class Followers { $follower->from_meta( $meta ); $follower->upsert(); - add_post_meta( $follower->get_id(), 'user_id', $user_id ); + update_post_meta( $follower->get_id(), 'user_id', $user_id, $user_id ); wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); @@ -339,14 +339,13 @@ class Followers { /** * Get all Followers * - * @param array $args The WP_Term_Query arguments. + * @param array $args The WP_Query arguments. * * @return array The Term list of Followers. */ public static function get_all_followers( $user_id = null ) { $args = array( - 'author' => null, - 'nopaging' => true, + 'meta_query' => array(), ); return self::get_followers( $user_id, null, null, $args ); } @@ -358,10 +357,10 @@ class Followers { * * @return int The number of Followers */ - public static function count_followers( $user_id = null ) { + public static function count_followers( $user_id ) { // todo: rethink this. Don't we already get a total_posts count out of WP_Query? // in the absence of that: caching. - return count( self::get_all_followers( $user_id ) ); + return count( self::get_followers( $user_id ) ); } /** @@ -406,11 +405,11 @@ class Followers { global $wpdb; $results = $wpdb->get_col( $wpdb->prepare( - "SELECT DISTINCT meta_value FROM {$wpdb->posts} - WHERE term_id IN (" . implode( ', ', array_fill( 0, count( $terms ), '%d' ) ) . ") + "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_value IS NOT NULL", - $terms + $posts ) ); @@ -435,10 +434,11 @@ class Followers { 'posts_per_page' => $number, 'orderby' => 'modified', 'order' => 'DESC', - 'date_query' => array( + 'post_status' => 'any', // 'any' includes 'trash + 'date_query' => array( array( 'column' => 'post_modified_gmt', - 'before' => 604800, + 'before' => gmdate( 'Y-m-d', \time() - $older_than ), ), ), ); diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 7b41bb0..0a6a6ef 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -367,10 +367,7 @@ class Follower { * @return void */ public function update() { - if ( ! $this->updated_at ) { - $this->updated_at = \time(); - } - + $this->updated_at = \time(); $this->save(); } @@ -387,6 +384,7 @@ class Follower { 'post_author' => 0, 'post_type' => Followers::POST_TYPE, 'post_content' => wp_json_encode( $this->meta ), + 'post_status' => 'publish', 'post_modified' => gmdate( 'Y-m-d H:i:s', $this->updated_at ), 'meta_input' => $this->get_post_meta_input(), ); diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index e660c00..87e0064 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -65,7 +65,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $db_followers ); - $this->assertSame( array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' ), $db_followers ); + $this->assertEquals( array( 'http://sally.example.org', 'https://example.org/author/doe', 'https://example.com/author/jon' ), $db_followers ); } public function test_add_follower() { @@ -109,8 +109,28 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $follower = new \Activitypub\Model\Follower( 'https://example.com/author/jon' ); - $follower->set_updates_at( \time() - 804800 ); - $follower->update(); + global $wpdb; + + //eg. time one year ago.. + $time = time() - 804800; + $mysql_time_format = 'Y-m-d H:i:s'; + + $post_modified = gmdate( $mysql_time_format, $time ); + $post_modified_gmt = gmdate( $mysql_time_format, ( $time + get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) ); + $post_id = $follower->get_id(); + + $wpdb->query( + $wpdb->prepare( + "UPDATE $wpdb->posts SET post_modified = %s, post_modified_gmt = %s WHERE ID = %s", + array( + $post_modified, + $post_modified_gmt, + $post_id, + ) + ) + ); + + clean_post_cache( $post_id ); $followers = \Activitypub\Collection\Followers::get_outdated_followers(); $this->assertEquals( 1, count( $followers ) ); From daf7acb1b047bd1898d55c506617ca16d34eb7de Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 16 Jun 2023 16:46:49 +0200 Subject: [PATCH 12/18] implement missing get_follower logic --- includes/collection/class-followers.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index ab5be32..16a55a2 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -235,7 +235,13 @@ class Followers { public static function remove_follower( $user_id, $actor ) { wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); - return wp_remove_object_terms( $user_id, $actor, self::POST_TYPE ); + $follower = self::get_follower( $user_id, $actor ); + + if ( ! $follower ) { + return false; + } + + return delete_post_meta( $follower->get_id(), 'user_id', $user_id ); } /** @@ -251,8 +257,12 @@ class Followers { $post_id = $wpdb->get_var( $wpdb->prepare( - "SELECT ID FROM $wpdb->posts WHERE guid=%s", - esc_sql( $actor ) + "SELECT 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", + array( + esc_sql( self::POST_TYPE ), + esc_sql( $user_id ), + esc_sql( $actor ), + ) ) ); From 28922d51ddbb96cb13506c025e8cf16187621f3a Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 16 Jun 2023 16:56:30 +0200 Subject: [PATCH 13/18] Fix follower list --- includes/collection/class-followers.php | 17 ++++++++++++++--- includes/table/class-followers.php | 7 ++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 16a55a2..e73ee1b 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -368,9 +368,20 @@ class Followers { * @return int The number of Followers */ public static function count_followers( $user_id ) { - // todo: rethink this. Don't we already get a total_posts count out of WP_Query? - // in the absence of that: caching. - return count( self::get_followers( $user_id ) ); + $query = new WP_Query( + array( + 'post_type' => self::POST_TYPE, + 'fields' => 'ids', + 'meta_query' => array( + array( + 'key' => 'user_id', + 'value' => $user_id, + ), + ), + ) + ); + + return $query->found_posts; } /** diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index b83403f..2bece58 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -112,7 +112,12 @@ class Followers extends WP_List_Table { switch ( $this->current_action() ) { case 'delete': - FollowerCollection::remove_follower( \get_current_user_id(), $followers ); + if ( ! is_array( $followers ) ) { + $followers = array( $followers ); + } + foreach ( $followers as $follower ) { + FollowerCollection::remove_follower( \get_current_user_id(), $follower ); + } break; } } From 08e3104a1ee24f5d9239364516242e6b913ade20 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 19 Jun 2023 11:04:45 +0200 Subject: [PATCH 14/18] better add_follower handling --- includes/collection/class-followers.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index e73ee1b..b7cbbd7 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -217,9 +217,12 @@ class Followers { $follower->from_meta( $meta ); $follower->upsert(); - update_post_meta( $follower->get_id(), 'user_id', $user_id, $user_id ); + $meta = get_post_meta( $follower->get_id(), 'user_id' ); - wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); + if ( is_array( $meta ) && ! in_array( $user_id, $meta, true ) ) { + add_post_meta( $follower->get_id(), 'user_id', $user_id ); + wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); + } return $follower; } From a71f79e979dc8303367e8c26632ecf0f05947ea4 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 19 Jun 2023 11:05:01 +0200 Subject: [PATCH 15/18] test remove_follower --- tests/test-class-db-activitypub-followers.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index 87e0064..a803c03 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -31,6 +31,12 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { 'name' => '12345', 'prefferedUsername' => '12345', ), + 'user2@example.com' => array( + 'url' => 'https://user2.example.com', + 'inbox' => 'https://user2.example.com/inbox', + 'name' => 'user2', + 'prefferedUsername' => 'user2', + ), ); public function set_up() { @@ -73,15 +79,21 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); $follower = 'https://12345.example.com'; + $follower2 = 'https://user2.example.com'; \Activitypub\Collection\Followers::add_follower( 1, $follower ); + \Activitypub\Collection\Followers::add_follower( 2, $follower ); + \Activitypub\Collection\Followers::add_follower( 2, $follower2 ); $db_followers = \Activitypub\Collection\Followers::get_followers( 1 ); + $db_followers2 = \Activitypub\Collection\Followers::get_followers( 2 ); $this->assertContains( $follower, $db_followers ); + $this->assertContains( $follower2, $db_followers2 ); } public function test_get_follower() { $followers = array( 'https://example.com/author/jon' ); + $followers2 = array( 'https://user2.example.com' ); $pre_http_request = new MockAction(); add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); @@ -90,11 +102,57 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { \Activitypub\Collection\Followers::add_follower( 1, $follower ); } + foreach ( $followers2 as $follower ) { + \Activitypub\Collection\Followers::add_follower( 2, $follower ); + } + $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); $this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() ); $follower = \Activitypub\Collection\Followers::get_follower( 1, 'http://sally.example.org' ); $this->assertNull( $follower ); + + $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://user2.example.com' ); + $this->assertNull( $follower ); + + $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); + $this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() ); + + $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://user2.example.com' ); + $this->assertEquals( 'https://user2.example.com', $follower2->get_actor() ); + } + + public function test_delete_follower() { + $followers = array( 'https://example.com/author/jon' ); + $followers2 = array( 'https://user2.example.com' ); + + $pre_http_request = new MockAction(); + add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 ); + + foreach ( $followers as $follower ) { + \Activitypub\Collection\Followers::add_follower( 1, $follower ); + \Activitypub\Collection\Followers::add_follower( 1, $follower ); + \Activitypub\Collection\Followers::add_follower( 1, $follower ); + \Activitypub\Collection\Followers::add_follower( 2, $follower ); + } + + foreach ( $followers2 as $follower2 ) { + \Activitypub\Collection\Followers::add_follower( 2, $follower2 ); + } + + $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); + $this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() ); + + $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' ); + $this->assertEquals( 'https://example.com/author/jon', $follower2->get_actor() ); + + \Activitypub\Collection\Followers::remove_follower( 1, 'https://example.com/author/jon' ); + + $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); + $this->assertNull( $follower ); + + $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' ); + $this->assertEquals( 'https://example.com/author/jon', $follower2->get_actor() ); } public function test_get_outdated_followers() { From bbf40a5fec7968d79f67a6b3bae48819e5d55581 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 19 Jun 2023 11:10:15 +0200 Subject: [PATCH 16/18] added more tests --- tests/test-class-db-activitypub-followers.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index a803c03..482cecc 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -123,7 +123,10 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { } public function test_delete_follower() { - $followers = array( 'https://example.com/author/jon' ); + $followers = array( + 'https://example.com/author/jon', + 'https://example.org/author/doe', + ); $followers2 = array( 'https://user2.example.com' ); $pre_http_request = new MockAction(); @@ -143,6 +146,9 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $follower = \Activitypub\Collection\Followers::get_follower( 1, 'https://example.com/author/jon' ); $this->assertEquals( 'https://example.com/author/jon', $follower->get_actor() ); + $followers = \Activitypub\Collection\Followers::get_followers( 1 ); + $this->assertEquals( 2, count( $followers ) ); + $follower2 = \Activitypub\Collection\Followers::get_follower( 2, 'https://example.com/author/jon' ); $this->assertEquals( 'https://example.com/author/jon', $follower2->get_actor() ); @@ -153,6 +159,9 @@ class Test_Db_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_actor() ); + + $followers = \Activitypub\Collection\Followers::get_followers( 1 ); + $this->assertEquals( 1, count( $followers ) ); } public function test_get_outdated_followers() { From e7bc9706a8d3dbe39ac99a8caaac368ce77b359f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 19 Jun 2023 11:36:59 +0200 Subject: [PATCH 17/18] remove url attribute --- includes/collection/class-followers.php | 2 +- includes/model/class-follower.php | 20 -------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index b7cbbd7..49569e5 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -50,7 +50,7 @@ class Followers { 'name' => _x( 'Followers', 'post_type plural name', 'activitypub' ), 'singular_name' => _x( 'Follower', 'post_type single name', 'activitypub' ), ), - 'public' => true, + 'public' => false, 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 0a6a6ef..660bec5 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -53,11 +53,6 @@ class Follower { */ private $avatar; - /** - * The URL to the Follower - */ - private $url; - /** * The URL to the Followers Inbox * @@ -120,7 +115,6 @@ class Follower { 'name' => 'name', 'preferredUsername' => 'username', 'inbox' => 'inbox', - 'url' => 'url', ); /** @@ -247,20 +241,6 @@ class Follower { return null; } - /** - * Get a URL for the follower. Creates one out of the actor if no URL was set. - */ - public function get_url() { - if ( $this->get( 'url' ) ) { - return $this->get( 'url' ); - } - $actor = $this->get_actor(); - // normalize - $actor = ltrim( $actor, '@' ); - $parts = explode( '@', $actor ); - return sprintf( 'https://%s/@%s', $parts[1], $parts[0] ); - } - /** * Set new Error * From a2152037778815d8d64fbeeb470df936a90adc82 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 20 Jun 2023 09:51:13 +0200 Subject: [PATCH 18/18] because post_types have length limitations, we should abbreviate the "activitypub" prefix, to be more flexible and consistent when adding other post_types in the future "Must not exceed 20 characters and may only contain lowercase alphanumeric characters, dashes, and underscores" --- includes/collection/class-followers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 49569e5..95f7aa8 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -19,7 +19,7 @@ use function Activitypub\get_remote_metadata_by_actor; * @author Matthias Pfefferle */ class Followers { - const POST_TYPE = 'activitypub_follower'; + const POST_TYPE = 'ap_follower'; const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; /**