From cec4ed2e3f25913f36bb9043ef63a9fefe6c376a Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 4 May 2023 15:17:05 +0200 Subject: [PATCH 01/55] init follower update scheduler --- activitypub.php | 127 ++++++++++++++++----------------- includes/class-activitypub.php | 58 +++++++++++++++ 2 files changed, 120 insertions(+), 65 deletions(-) diff --git a/activitypub.php b/activitypub.php index d61af85..1e0d44b 100644 --- a/activitypub.php +++ b/activitypub.php @@ -29,67 +29,66 @@ function init() { \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); - require_once \dirname( __FILE__ ) . '/includes/table/followers-list.php'; - require_once \dirname( __FILE__ ) . '/includes/class-signature.php'; - require_once \dirname( __FILE__ ) . '/includes/class-webfinger.php'; - require_once \dirname( __FILE__ ) . '/includes/peer/class-followers.php'; - require_once \dirname( __FILE__ ) . '/includes/functions.php'; + \define( 'ACTIVITYPUB_OBJECT', 'ACTIVITYPUB_OBJECT' ); - require_once \dirname( __FILE__ ) . '/includes/model/class-activity.php'; - require_once \dirname( __FILE__ ) . '/includes/model/class-post.php'; - - require_once \dirname( __FILE__ ) . '/includes/class-activity-dispatcher.php'; + Migration::init(); Activity_Dispatcher::init(); - - require_once \dirname( __FILE__ ) . '/includes/class-activitypub.php'; Activitypub::init(); + Collection\Followers::init(); // Configure the REST API route - require_once \dirname( __FILE__ ) . '/includes/rest/class-outbox.php'; Rest\Outbox::init(); - - require_once \dirname( __FILE__ ) . '/includes/rest/class-inbox.php'; Rest\Inbox::init(); - - require_once \dirname( __FILE__ ) . '/includes/rest/class-followers.php'; Rest\Followers::init(); - - require_once \dirname( __FILE__ ) . '/includes/rest/class-following.php'; Rest\Following::init(); - - require_once \dirname( __FILE__ ) . '/includes/rest/class-webfinger.php'; Rest\Webfinger::init(); - // load NodeInfo endpoints only if blog is public - if ( true === (bool) \get_option( 'blog_public', 1 ) ) { - require_once \dirname( __FILE__ ) . '/includes/rest/class-nodeinfo.php'; - Rest\NodeInfo::init(); - } - - require_once \dirname( __FILE__ ) . '/includes/class-admin.php'; Admin::init(); - - require_once \dirname( __FILE__ ) . '/includes/class-hashtag.php'; Hashtag::init(); - - require_once \dirname( __FILE__ ) . '/includes/class-shortcodes.php'; Shortcodes::init(); - - require_once \dirname( __FILE__ ) . '/includes/class-mention.php'; Mention::init(); - - require_once \dirname( __FILE__ ) . '/includes/class-debug.php'; Debug::init(); - - require_once \dirname( __FILE__ ) . '/includes/class-health-check.php'; Health_Check::init(); - - if ( \WP_DEBUG ) { - require_once \dirname( __FILE__ ) . '/includes/debug.php'; - } } \add_action( 'plugins_loaded', '\Activitypub\init' ); +spl_autoload_register( + function ( $class ) { + $base_dir = trailingslashit( __DIR__ ) . 'includes' . DIRECTORY_SEPARATOR; + $base = 'activitypub'; + + $class = strtolower( $class ); + + if ( strncmp( $class, $base, strlen( $base ) ) === 0 ) { + $class = str_replace( 'activitypub\\', '', $class ); + + if ( strpos( $class, '\\' ) ) { + list( $sub_dir, $class ) = explode( '\\', $class ); + $base_dir = $base_dir . $sub_dir . DIRECTORY_SEPARATOR; + } + + $filename = 'class-' . str_replace( '_', '-', $class ); + $file = $base_dir . $filename . '.php'; + + if ( file_exists( $file ) ) { + require_once $file; + } + } + } +); + +require_once \dirname( __FILE__ ) . '/includes/functions.php'; + +// load NodeInfo endpoints only if blog is public +if ( true === (bool) \get_option( 'blog_public', 1 ) ) { + require_once \dirname( __FILE__ ) . '/includes/rest/class-nodeinfo.php'; + Rest\NodeInfo::init(); +} + +if ( \WP_DEBUG ) { + require_once \dirname( __FILE__ ) . '/includes/debug.php'; +} + /** * Add plugin settings link */ @@ -102,34 +101,32 @@ function plugin_settings_link( $actions ) { return \array_merge( $settings_link, $actions ); } -\add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), '\Activitypub\plugin_settings_link' ); +\add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), __NAMESPACE__ . '\plugin_settings_link' ); -/** - * Add rewrite rules - */ -function add_rewrite_rules() { - if ( ! \class_exists( 'Webfinger' ) ) { - \add_rewrite_rule( '^.well-known/webfinger', 'index.php?rest_route=/activitypub/1.0/webfinger', 'top' ); - } +\register_activation_hook( + __FILE__, + array( + __NAMESPACE__ . '\Activitypub', + 'activate', + ) +); - if ( ! \class_exists( 'Nodeinfo' ) || ! (bool) \get_option( 'blog_public', 1 ) ) { - \add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', 'top' ); - \add_rewrite_rule( '^.well-known/x-nodeinfo2', 'index.php?rest_route=/activitypub/1.0/nodeinfo2', 'top' ); - } +\register_deactivation_hook( + __FILE__, + array( + __NAMESPACE__ . '\Activitypub', + 'deactivate', + ) +); - \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES ); -} -\add_action( 'init', '\Activitypub\add_rewrite_rules', 1 ); +register_uninstall_hook( + __FILE__, + array( + __NAMESPACE__ . '\Activitypub', + 'uninstall', + ) +); -/** - * Flush rewrite rules; - */ -function flush_rewrite_rules() { - \Activitypub\add_rewrite_rules(); - \flush_rewrite_rules(); -} -\register_activation_hook( __FILE__, '\Activitypub\flush_rewrite_rules' ); -\register_deactivation_hook( __FILE__, '\flush_rewrite_rules' ); /** * Only load code that needs BuddyPress to run once BP is loaded and initialized. diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 16a1f22..fd5aa1e 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -25,6 +25,40 @@ class Activitypub { \add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 ); \add_action( 'wp_trash_post', array( self::class, 'trash_post' ), 1 ); \add_action( 'untrash_post', array( self::class, 'untrash_post' ), 1 ); + + \add_action( 'init', array( self::class, 'add_rewrite_rules' ) ); + } + + /** + * Activation Hook + * + * @return void + */ + public static function activate() { + self::flush_rewrite_rules(); + + if ( ! \wp_next_scheduled( 'activitypub_update_followers' ) ) { + \wp_schedule_event( time(), 'hourly', 'activitypub_update_followers' ); + } + } + + /** + * Deactivation Hook + * + * @return void + */ + public static function deactivate() { + self::flush_rewrite_rules(); + + wp_unschedule_hook( 'activitypub_update_followers' ); + } + + /** + * Uninstall Hook + * + * @return void + */ + public static function uninstall() { } /** @@ -204,4 +238,28 @@ class Activitypub { public static function untrash_post( $post_id ) { \delete_post_meta( $post_id, 'activitypub_canonical_url' ); } + + /** + * Add rewrite rules + */ + public static function add_rewrite_rules() { + if ( ! \class_exists( 'Webfinger' ) ) { + \add_rewrite_rule( '^.well-known/webfinger', 'index.php?rest_route=/activitypub/1.0/webfinger', 'top' ); + } + + if ( ! \class_exists( 'Nodeinfo' ) || (bool) \get_option( 'blog_public', 1 ) ) { + \add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', 'top' ); + \add_rewrite_rule( '^.well-known/x-nodeinfo2', 'index.php?rest_route=/activitypub/1.0/nodeinfo2', 'top' ); + } + + \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES ); + } + + /** + * Flush rewrite rules; + */ + public static function flush_rewrite_rules() { + self::add_rewrite_rules(); + \flush_rewrite_rules(); + } } From 0fd11d25faf053807f3eab309e78b848ebfde02c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 4 May 2023 15:18:58 +0200 Subject: [PATCH 02/55] will be auto-loaded --- activitypub.php | 1 - 1 file changed, 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 0db8e51..e06e6f6 100644 --- a/activitypub.php +++ b/activitypub.php @@ -81,7 +81,6 @@ require_once \dirname( __FILE__ ) . '/includes/functions.php'; // load NodeInfo endpoints only if blog is public if ( true === (bool) \get_option( 'blog_public', 1 ) ) { - require_once \dirname( __FILE__ ) . '/includes/rest/class-nodeinfo.php'; Rest\NodeInfo::init(); } From 77112c441ffd6d107653b2fdf77d04bb084f81c5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 5 May 2023 09:57:47 +0200 Subject: [PATCH 03/55] formatting --- activitypub.php | 3 ++ includes/class-activitypub.php | 57 +++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/activitypub.php b/activitypub.php index e06e6f6..0db3c32 100644 --- a/activitypub.php +++ b/activitypub.php @@ -52,6 +52,9 @@ function init() { } \add_action( 'plugins_loaded', '\Activitypub\init' ); +/** + * Class Autoloader + */ spl_autoload_register( function ( $class ) { $base_dir = trailingslashit( __DIR__ ) . 'includes' . DIRECTORY_SEPARATOR; diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index fd5aa1e..9739b6c 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -40,6 +40,10 @@ class Activitypub { if ( ! \wp_next_scheduled( 'activitypub_update_followers' ) ) { \wp_schedule_event( time(), 'hourly', 'activitypub_update_followers' ); } + + if ( ! \wp_next_scheduled( 'activitypub_cleanup_followers' ) ) { + \wp_schedule_event( time(), 'daily', 'activitypub_cleanup_followers' ); + } } /** @@ -51,6 +55,7 @@ class Activitypub { self::flush_rewrite_rules(); wp_unschedule_hook( 'activitypub_update_followers' ); + wp_unschedule_hook( 'activitypub_cleanup_followers' ); } /** @@ -154,11 +159,23 @@ class Activitypub { $activitypub_post = new \Activitypub\Model\Post( $post ); if ( 'publish' === $new_status && 'publish' !== $old_status ) { - \wp_schedule_single_event( \time(), 'activitypub_send_create_activity', array( $activitypub_post ) ); + \wp_schedule_single_event( + \time(), + 'activitypub_send_create_activity', + array( $activitypub_post ) + ); } elseif ( 'publish' === $new_status ) { - \wp_schedule_single_event( \time(), 'activitypub_send_update_activity', array( $activitypub_post ) ); + \wp_schedule_single_event( + \time(), + 'activitypub_send_update_activity', + array( $activitypub_post ) + ); } elseif ( 'trash' === $new_status ) { - \wp_schedule_single_event( \time(), 'activitypub_send_delete_activity', array( $activitypub_post ) ); + \wp_schedule_single_event( + \time(), + 'activitypub_send_delete_activity', + array( $activitypub_post ) + ); } } @@ -180,7 +197,14 @@ class Activitypub { } $allowed_comment_types = \apply_filters( 'get_avatar_comment_types', array( 'comment' ) ); - if ( ! empty( $id_or_email->comment_type ) && ! \in_array( $id_or_email->comment_type, (array) $allowed_comment_types, true ) ) { + if ( + ! empty( $id_or_email->comment_type ) && + ! \in_array( + $id_or_email->comment_type, + (array) $allowed_comment_types, + true + ) + ) { $args['url'] = false; /** This filter is documented in wp-includes/link-template.php */ return \apply_filters( 'get_avatar_data', $args, $id_or_email ); @@ -225,7 +249,12 @@ class Activitypub { * @return void */ public static function trash_post( $post_id ) { - \add_post_meta( $post_id, 'activitypub_canonical_url', \get_permalink( $post_id ), true ); + \add_post_meta( + $post_id, + 'activitypub_canonical_url', + \get_permalink( $post_id ), + true + ); } /** @@ -244,12 +273,24 @@ class Activitypub { */ public static function add_rewrite_rules() { if ( ! \class_exists( 'Webfinger' ) ) { - \add_rewrite_rule( '^.well-known/webfinger', 'index.php?rest_route=/activitypub/1.0/webfinger', 'top' ); + \add_rewrite_rule( + '^.well-known/webfinger', + 'index.php?rest_route=/activitypub/1.0/webfinger', + 'top' + ); } if ( ! \class_exists( 'Nodeinfo' ) || (bool) \get_option( 'blog_public', 1 ) ) { - \add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', 'top' ); - \add_rewrite_rule( '^.well-known/x-nodeinfo2', 'index.php?rest_route=/activitypub/1.0/nodeinfo2', 'top' ); + \add_rewrite_rule( + '^.well-known/nodeinfo', + 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', + 'top' + ); + \add_rewrite_rule( + '^.well-known/x-nodeinfo2', + 'index.php?rest_route=/activitypub/1.0/nodeinfo2', + 'top' + ); } \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES ); From 6d96daa635bd505df7843e69153a05eaaa40c4c5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 8 May 2023 21:05:20 +0200 Subject: [PATCH 04/55] fix NodeInfo check --- includes/class-activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 9739b6c..417aa29 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -280,7 +280,7 @@ class Activitypub { ); } - if ( ! \class_exists( 'Nodeinfo' ) || (bool) \get_option( 'blog_public', 1 ) ) { + if ( ! \class_exists( 'Nodeinfo' ) && true === (bool) \get_option( 'blog_public', 1 ) ) { \add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', From f64a765129dadb137b541d5babab7cf36a542159 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 9 May 2023 10:08:51 +0200 Subject: [PATCH 05/55] phpdoc fixes --- includes/collection/class-followers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index c6c6223..1868bba 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -186,7 +186,7 @@ class Followers { } /** - * Handles "Unfollow" requests + * Handle "Unfollow" requests * * @param array $object The JSON "Undo" Activity * @param int $user_id The ID of the ID of the WordPress User @@ -202,7 +202,7 @@ class Followers { } /** - * Add a new Follower + * Add new Follower * * @param int $user_id The ID of the WordPress User * @param string $actor The Actor URL From 4abd5aefb45e651af0f31b328f47817de46999e8 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 9 May 2023 10:28:23 +0200 Subject: [PATCH 06/55] cache inbox list --- includes/collection/class-followers.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index c6c6223..a6a8ba4 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -19,6 +19,7 @@ use function Activitypub\get_remote_metadata_by_actor; */ class Followers { const TAXONOMY = 'activitypub-followers'; + const CACHE_KEY_INBOXES = 'activitypub_follower_inboxes_for_%s'; /** * Register WordPress hooks/actions and register Taxonomy @@ -225,6 +226,8 @@ class Followers { if ( is_wp_error( $result ) ) { return $result; } else { + $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); + wp_cache_delete( $cache_key ); return $follower; } } @@ -238,6 +241,8 @@ class Followers { * @return bool|WP_Error True on success, false or WP_Error on failure. */ public static function remove_follower( $user_id, $actor ) { + $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); + wp_cache_delete( $cache_key ); return wp_remove_object_terms( $user_id, $actor, self::TAXONOMY ); } @@ -369,6 +374,13 @@ class Followers { * @return array The list of Inboxes */ public static function get_inboxes( $user_id ) { + $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); + $inboxes = wp_cache_get( $cache_key ); + + if ( $inboxes ) { + return $inboxes; + } + // get all Followers of a ID of the WordPress User $terms = new WP_Term_Query( array( @@ -402,6 +414,9 @@ class Followers { ) ); - return array_filter( $results ); + $inboxes = array_filter( $results ); + wp_cache_set( $cache_key, $inboxes ); + + return $inboxes; } } From 74be5d6b5159bed33b67e377a7fbf6383d66fee1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 09:04:33 +0200 Subject: [PATCH 07/55] implemented feedback of @akirk --- includes/collection/class-followers.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index a6a8ba4..5e5e532 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; */ class Followers { const TAXONOMY = 'activitypub-followers'; - const CACHE_KEY_INBOXES = 'activitypub_follower_inboxes_for_%s'; + const CACHE_KEY_INBOXES = 'follower_inboxes_%s'; /** * Register WordPress hooks/actions and register Taxonomy @@ -226,8 +226,7 @@ class Followers { if ( is_wp_error( $result ) ) { return $result; } else { - $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); - wp_cache_delete( $cache_key ); + wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); return $follower; } } @@ -241,8 +240,7 @@ class Followers { * @return bool|WP_Error True on success, false or WP_Error on failure. */ public static function remove_follower( $user_id, $actor ) { - $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); - wp_cache_delete( $cache_key ); + wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); return wp_remove_object_terms( $user_id, $actor, self::TAXONOMY ); } @@ -375,7 +373,7 @@ class Followers { */ public static function get_inboxes( $user_id ) { $cache_key = sprintf( self::CACHE_KEY_INBOXES, $user_id ); - $inboxes = wp_cache_get( $cache_key ); + $inboxes = wp_cache_get( $cache_key, 'activitypub' ); if ( $inboxes ) { return $inboxes; @@ -415,7 +413,7 @@ class Followers { ); $inboxes = array_filter( $results ); - wp_cache_set( $cache_key, $inboxes ); + wp_cache_set( $cache_key, $inboxes, 'activitypub' ); return $inboxes; } From 17b66cb23d88b2ad0cbaf75606bf062f33a5d5e8 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 14:18:56 +0200 Subject: [PATCH 08/55] implement `cleanup_followers` and `update_followers` --- activitypub.php | 1 + includes/class-activitypub.php | 54 +------- includes/class-migration.php | 10 +- includes/class-scheduler.php | 126 ++++++++++++++++++ includes/class-shortcodes.php | 8 +- includes/collection/class-followers.php | 78 +++++++++++ includes/model/class-follower.php | 8 ++ tests/test-class-db-activitypub-followers.php | 54 ++++++++ 8 files changed, 281 insertions(+), 58 deletions(-) create mode 100644 includes/class-scheduler.php diff --git a/activitypub.php b/activitypub.php index 0db3c32..4fbe2fe 100644 --- a/activitypub.php +++ b/activitypub.php @@ -49,6 +49,7 @@ function init() { Mention::init(); Debug::init(); Health_Check::init(); + Scheduler::init(); } \add_action( 'plugins_loaded', '\Activitypub\init' ); diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 417aa29..37bedf4 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -22,7 +22,6 @@ class Activitypub { \add_post_type_support( $post_type, 'activitypub' ); } - \add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 ); \add_action( 'wp_trash_post', array( self::class, 'trash_post' ), 1 ); \add_action( 'untrash_post', array( self::class, 'untrash_post' ), 1 ); @@ -37,13 +36,7 @@ class Activitypub { public static function activate() { self::flush_rewrite_rules(); - if ( ! \wp_next_scheduled( 'activitypub_update_followers' ) ) { - \wp_schedule_event( time(), 'hourly', 'activitypub_update_followers' ); - } - - if ( ! \wp_next_scheduled( 'activitypub_cleanup_followers' ) ) { - \wp_schedule_event( time(), 'daily', 'activitypub_cleanup_followers' ); - } + Scheduler::register_schedules(); } /** @@ -54,8 +47,7 @@ class Activitypub { public static function deactivate() { self::flush_rewrite_rules(); - wp_unschedule_hook( 'activitypub_update_followers' ); - wp_unschedule_hook( 'activitypub_cleanup_followers' ); + Scheduler::deregister_schedules(); } /** @@ -137,48 +129,6 @@ class Activitypub { return $vars; } - /** - * Schedule Activities. - * - * @param string $new_status New post status. - * @param string $old_status Old post status. - * @param WP_Post $post Post object. - */ - public static function schedule_post_activity( $new_status, $old_status, $post ) { - // Do not send activities if post is password protected. - if ( \post_password_required( $post ) ) { - return; - } - - // Check if post-type supports ActivityPub. - $post_types = \get_post_types_by_support( 'activitypub' ); - if ( ! \in_array( $post->post_type, $post_types, true ) ) { - return; - } - - $activitypub_post = new \Activitypub\Model\Post( $post ); - - if ( 'publish' === $new_status && 'publish' !== $old_status ) { - \wp_schedule_single_event( - \time(), - 'activitypub_send_create_activity', - array( $activitypub_post ) - ); - } elseif ( 'publish' === $new_status ) { - \wp_schedule_single_event( - \time(), - 'activitypub_send_update_activity', - array( $activitypub_post ) - ); - } elseif ( 'trash' === $new_status ) { - \wp_schedule_single_event( - \time(), - 'activitypub_send_delete_activity', - array( $activitypub_post ) - ); - } - } - /** * Replaces the default avatar. * diff --git a/includes/class-migration.php b/includes/class-migration.php index 1e8c44b..e6e6fba 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -3,7 +3,15 @@ namespace Activitypub; use Acctivitypub\Model\Follower; +/** + * ActivityPub Migration Class + * + * @author Matthias Pfefferle + */ class Migration { + /** + * Initialize the class, registering WordPress hooks + */ public static function init() { \add_action( 'activitypub_schedule_migration', array( self::class, 'maybe_migrate' ) ); } @@ -59,7 +67,7 @@ class Migration { $followers = get_user_meta( $user_id, 'activitypub_followers', true ); if ( $followers ) { - foreach ( $followers as $follower ) { + foreach ( $followers as $actor ) { $meta = get_remote_metadata_by_actor( $actor ); $follower = new Follower( $actor ); diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php new file mode 100644 index 0000000..4291365 --- /dev/null +++ b/includes/class-scheduler.php @@ -0,0 +1,126 @@ +post_type, $post_types, true ) ) { + return; + } + + $activitypub_post = new Post( $post ); + + if ( 'publish' === $new_status && 'publish' !== $old_status ) { + \wp_schedule_single_event( + \time(), + 'activitypub_send_create_activity', + array( $activitypub_post ) + ); + } elseif ( 'publish' === $new_status ) { + \wp_schedule_single_event( + \time(), + 'activitypub_send_update_activity', + array( $activitypub_post ) + ); + } elseif ( 'trash' === $new_status ) { + \wp_schedule_single_event( + \time(), + 'activitypub_send_delete_activity', + array( $activitypub_post ) + ); + } + } + + /** + * Update followers + * + * @return void + */ + public static function update_followers() { + $followers = Followers::get_outdated_followers( ACTIVITYPUB_OBJECT ); + + foreach ( $followers as $follower ) { + $meta = get_remote_metadata_by_actor( $follower->get_actor() ); + + if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { + $follower->set_error( $meta ); + } else { + $follower->from_meta( $meta ); + } + + $follower->update(); + } + } + + /** + * Cleanup followers + * + * @return void + */ + public static function cleanup_followers() { + $followers = Followers::get_faulty_followers( ACTIVITYPUB_OBJECT ); + + foreach ( $followers as $follower ) { + $meta = get_remote_metadata_by_actor( $follower->get_actor() ); + + if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { + if ( 5 >= $follower->count_errors() ) { + $follower->delete(); + } else { + $follower->set_error( $meta ); + $follower->update(); + } + } else { + $follower->reset_errors(); + } + } + } +} diff --git a/includes/class-shortcodes.php b/includes/class-shortcodes.php index 7289808..f56d483 100644 --- a/includes/class-shortcodes.php +++ b/includes/class-shortcodes.php @@ -3,14 +3,12 @@ namespace Activitypub; class Shortcodes { /** - * Class constructor, registering WordPress then shortcodes - * - * @param WP_Post $post A WordPress Post Object + * Initialize the class, registering WordPress hooks */ public static function init() { - foreach ( get_class_methods( 'Activitypub\Shortcodes' ) as $shortcode ) { + foreach ( get_class_methods( self::class ) as $shortcode ) { if ( 'init' !== $shortcode ) { - add_shortcode( 'ap_' . $shortcode, array( 'Activitypub\Shortcodes', $shortcode ) ); + add_shortcode( 'ap_' . $shortcode, array( self::class, $shortcode ) ); } } } diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 1868bba..0fa4a6a 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -404,4 +404,82 @@ class Followers { return array_filter( $results ); } + + /** + * Undocumented function + * + * @return void + */ + public static function get_outdated_followers( $output = ARRAY_N, $number = 50, $older_than = 604800 ) { + $args = array( + 'taxonomy' => self::TAXONOMY, + 'number' => $number, + 'meta_key' => 'updated_at', + 'orderby' => 'meta_value_num', + 'order' => 'DESC', + 'meta_query' => array( + array( + 'key' => 'updated_at', + 'value' => strtotime( 'now' ) - $older_than, + 'type' => 'numeric', + 'compare' => '<=', + ), + ), + ); + + $terms = new WP_Term_Query( $args ); + + $items = array(); + + // change output format + switch ( $output ) { + case ACTIVITYPUB_OBJECT: + foreach ( $terms->get_terms() as $follower ) { + $items[] = new Follower( $follower->name ); // phpcs:ignore + } + return $items; + case OBJECT: + return $terms->get_terms(); + case ARRAY_N: + default: + foreach ( $terms->get_terms() as $follower ) { + $items[] = $follower->name; // phpcs:ignore + } + return $items; + } + } + + public static function get_faulty_followers( $output = ARRAY_N, $number = 10 ) { + $args = array( + 'taxonomy' => self::TAXONOMY, + 'number' => $number, + 'meta_query' => array( + array( + 'key' => 'errors', + 'compare' => 'EXISTS', + ), + ), + ); + + $terms = new WP_Term_Query( $args ); + + $items = array(); + + // change output format + switch ( $output ) { + case ACTIVITYPUB_OBJECT: + foreach ( $terms->get_terms() as $follower ) { + $items[] = new Follower( $follower->name ); // phpcs:ignore + } + return $items; + case OBJECT: + return $terms->get_terms(); + case ARRAY_N: + default: + foreach ( $terms->get_terms() as $follower ) { + $items[] = $follower->name; // phpcs:ignore + } + return $items; + } + } } diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 3a15690..2b152f1 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -210,6 +210,10 @@ class Follower { return $this->errors; } + public function reset_errors() { + delete_term_meta( $this->id, 'errors' ); + } + public function count_errors() { $errors = $this->get_errors(); @@ -292,6 +296,10 @@ class Follower { } } + public function delete() { + wp_delete_term( $this->id, Followers::TAXONOMY ); + } + protected function update_term_meta() { $attributes = array( 'inbox', 'shared_inbox', 'avatar', 'updated_at', 'name', 'username' ); diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index 640c4d1..cf8b53f 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -90,6 +90,60 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $this->assertNull( $follower ); } + public function test_get_outdated_followers() { + $followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' ); + + $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 ); + } + + $follower = new \Activitypub\Model\Follower( 'https://example.com/author/jon' ); + + update_term_meta( $follower->get_id(), 'updated_at', strtotime( 'now' ) - 804800 ); + + $followers = \Activitypub\Collection\Followers::get_outdated_followers(); + $this->assertEquals( 1, count( $followers ) ); + $this->assertEquals( 'https://example.com/author/jon', $followers[0] ); + } + + public function test_get_faulty_followers() { + $followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' ); + + $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 ); + } + + $follower = new \Activitypub\Model\Follower( 'http://sally.example.org' ); + + for ( $i = 1; $i <= 15; $i++ ) { + add_term_meta( $follower->get_id(), 'errors', 'error ' . $i ); + } + + $follower = new \Activitypub\Model\Follower( 'http://sally.example.org' ); + $count = $follower->count_errors(); + + $followers = \Activitypub\Collection\Followers::get_faulty_followers(); + + $this->assertEquals( 1, count( $followers ) ); + $this->assertEquals( 'http://sally.example.org', $followers[0] ); + + $follower->reset_errors(); + + $follower = new \Activitypub\Model\Follower( 'http://sally.example.org' ); + $count = $follower->count_errors(); + + $followers = \Activitypub\Collection\Followers::get_faulty_followers(); + + $this->assertEquals( 0, count( $followers ) ); + } + + public static function http_request_host_is_external( $in, $host ) { if ( in_array( $host, array( 'example.com', 'example.org' ), true ) ) { return true; From 2570928b00d9c6ff8d05b427f45a1a76684a51e9 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 14:55:10 +0200 Subject: [PATCH 09/55] PHPDoc --- includes/class-scheduler.php | 10 ++++++++++ includes/collection/class-followers.php | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 4291365..0373c3f 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -21,6 +21,11 @@ class Scheduler { \add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) ); } + /** + * Schedule all ActivityPub schedules. + * + * @return void + */ public static function register_schedules() { if ( ! \wp_next_scheduled( 'activitypub_update_followers' ) ) { \wp_schedule_event( time(), 'hourly', 'activitypub_update_followers' ); @@ -31,6 +36,11 @@ class Scheduler { } } + /** + * Unscedule all ActivityPub schedules. + * + * @return void + */ public static function deregister_schedules() { wp_unschedule_hook( 'activitypub_update_followers' ); wp_unschedule_hook( 'activitypub_cleanup_followers' ); diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 0fa4a6a..1d54fa2 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -406,9 +406,13 @@ class Followers { } /** - * Undocumented function + * Get all Followers that have not been updated for a given time * - * @return void + * @param enum $output The output format, supported ARRAY_N, OBJECT and ACTIVITYPUB_OBJECT. + * @param int $number Limits the result. + * @param int $older_than The time in seconds. + * + * @return mixed The Term list of Followers, the format depends on $output. */ public static function get_outdated_followers( $output = ARRAY_N, $number = 50, $older_than = 604800 ) { $args = array( @@ -449,6 +453,14 @@ class Followers { } } + /** + * Get all Followers that had errors + * + * @param enum $output The output format, supported ARRAY_N, OBJECT and ACTIVITYPUB_OBJECT + * @param integer $number The number of Followers to return. + * + * @return mixed The Term list of Followers, the format depends on $output. + */ public static function get_faulty_followers( $output = ARRAY_N, $number = 10 ) { $args = array( 'taxonomy' => self::TAXONOMY, From df02d2202e0492efee9acbc028dfe0135db00cd0 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 15:02:01 +0200 Subject: [PATCH 10/55] PHPDoc --- includes/model/class-follower.php | 76 +++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 2b152f1..10200c2 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -139,8 +139,8 @@ class Follower { /** * Magic function to implement getter and setter * - * @param string $method - * @param string $params + * @param string $method The method name. + * @param string $params The method params. * * @return void */ @@ -159,6 +159,13 @@ class Follower { } } + /** + * Prefill the Object with the meta data. + * + * @param array $meta The meta data. + * + * @return void + */ public function from_meta( $meta ) { $this->meta = $meta; @@ -181,6 +188,13 @@ class Follower { $this->updated_at = \strtotime( 'now' ); } + /** + * Get the data by the given attribute + * + * @param string $attribute The attribute name. + * + * @return mixed The attribute value. + */ public function get( $attribute ) { if ( $this->$attribute ) { return $this->$attribute; @@ -201,6 +215,11 @@ class Follower { return null; } + /** + * Get the errors. + * + * @return mixed + */ public function get_errors() { if ( $this->errors ) { return $this->errors; @@ -210,10 +229,20 @@ class Follower { return $this->errors; } + /** + * Reset (delete) all errors. + * + * @return void + */ public function reset_errors() { delete_term_meta( $this->id, 'errors' ); } + /** + * Count the errors. + * + * @return int The number of errors. + */ public function count_errors() { $errors = $this->get_errors(); @@ -224,6 +253,11 @@ class Follower { return 0; } + /** + * Return the latest error message. + * + * @return string The error message. + */ public function get_latest_error_message() { $errors = $this->get_errors(); @@ -234,6 +268,13 @@ class Follower { return ''; } + /** + * Get the meta data by the given attribute. + * + * @param string $attribute The attribute name. + * + * @return mixed $attribute The attribute value. + */ public function get_meta_by( $attribute ) { $meta = $this->get_meta(); @@ -252,6 +293,11 @@ class Follower { return null; } + /** + * Get the meta data. + * + * @return array $meta The meta data. + */ public function get_meta() { if ( $this->meta ) { return $this->meta; @@ -260,6 +306,11 @@ class Follower { return null; } + /** + * Update the current Follower-Object. + * + * @return void + */ public function update() { $term = wp_update_term( $this->id, @@ -273,6 +324,11 @@ class Follower { $this->update_term_meta(); } + /** + * Save the current Follower-Object. + * + * @return void + */ public function save() { $term = wp_insert_term( $this->actor, @@ -288,6 +344,11 @@ class Follower { $this->update_term_meta(); } + /** + * Upsert the current Follower-Object. + * + * @return void + */ public function upsert() { if ( $this->id ) { $this->update(); @@ -296,10 +357,20 @@ class Follower { } } + /** + * Delete the current Follower-Object. + * + * @return void + */ public function delete() { wp_delete_term( $this->id, Followers::TAXONOMY ); } + /** + * Update the term meta. + * + * @return void + */ protected function update_term_meta() { $attributes = array( 'inbox', 'shared_inbox', 'avatar', 'updated_at', 'name', 'username' ); @@ -320,6 +391,5 @@ class Follower { add_term_meta( $this->id, 'errors', $error ); } - } } From 463bff834b3c845ff6ab10acdad51c9d37f29cfe Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 17:21:59 +0200 Subject: [PATCH 11/55] delete if response code is 410 or 404 --- includes/class-scheduler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 0373c3f..0a7bba7 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -121,7 +121,9 @@ class Scheduler { foreach ( $followers as $follower ) { $meta = get_remote_metadata_by_actor( $follower->get_actor() ); - if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { + if ( is_tombstone( $meta ) ) { + $follower->delete(); + } elseif ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { if ( 5 >= $follower->count_errors() ) { $follower->delete(); } else { From 9da559be6aee7ae2fca88122b15dc05a899f0ad1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 18:45:32 +0200 Subject: [PATCH 12/55] Update includes/collection/class-followers.php Co-authored-by: Alex Kirk --- 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 1d54fa2..0948bfb 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -424,7 +424,7 @@ class Followers { 'meta_query' => array( array( 'key' => 'updated_at', - 'value' => strtotime( 'now' ) - $older_than, + 'value' => time() - $older_than, 'type' => 'numeric', 'compare' => '<=', ), From 3c027449257186cbe3306cf92584349b283fa410 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 18:45:48 +0200 Subject: [PATCH 13/55] Update activitypub.php Co-authored-by: Alex Kirk --- activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 4fbe2fe..598f03a 100644 --- a/activitypub.php +++ b/activitypub.php @@ -66,7 +66,7 @@ spl_autoload_register( if ( strncmp( $class, $base, strlen( $base ) ) === 0 ) { $class = str_replace( 'activitypub\\', '', $class ); - if ( strpos( $class, '\\' ) ) { + if ( false !== strpos( $class, '\\' ) ) { list( $sub_dir, $class ) = explode( '\\', $class ); $base_dir = $base_dir . $sub_dir . DIRECTORY_SEPARATOR; } From 6fce2c30d2416159e98fe08f6b05b52ee1ed93a2 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 18:47:46 +0200 Subject: [PATCH 14/55] Update includes/class-scheduler.php Co-authored-by: Alex Kirk --- includes/class-scheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 0a7bba7..4bfb8b8 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -124,7 +124,7 @@ class Scheduler { if ( is_tombstone( $meta ) ) { $follower->delete(); } elseif ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) { - if ( 5 >= $follower->count_errors() ) { + if ( 5 <= $follower->count_errors() ) { $follower->delete(); } else { $follower->set_error( $meta ); From 75c1c6a402c605cac157cd95582e43004a898350 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 18:50:20 +0200 Subject: [PATCH 15/55] Update activitypub.php Co-authored-by: Alex Kirk --- activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 598f03a..4d9b2e6 100644 --- a/activitypub.php +++ b/activitypub.php @@ -71,7 +71,7 @@ spl_autoload_register( $base_dir = $base_dir . $sub_dir . DIRECTORY_SEPARATOR; } - $filename = 'class-' . str_replace( '_', '-', $class ); + $filename = 'class-' . strtr( $class, '_', '-' ); $file = $base_dir . $filename . '.php'; if ( file_exists( $file ) ) { From 26a1dc9be5165b40369fba9acc36afd2ee1a4db1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 18:52:13 +0200 Subject: [PATCH 16/55] use time() instead of strtotime( 'now' ) --- includes/collection/class-followers.php | 2 +- includes/model/class-activity.php | 2 +- includes/model/class-follower.php | 4 ++-- tests/test-class-db-activitypub-followers.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 0948bfb..413af3b 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -143,7 +143,7 @@ class Followers { 'single' => true, 'sanitize_callback' => function( $value ) { if ( ! is_numeric( $value ) && (int) $value !== $value ) { - $value = strtotime( 'now' ); + $value = \time(); } return $value; diff --git a/includes/model/class-activity.php b/includes/model/class-activity.php index 34f6146..6a8d45a 100644 --- a/includes/model/class-activity.php +++ b/includes/model/class-activity.php @@ -44,7 +44,7 @@ class Activity { } $this->type = \ucfirst( $type ); - $this->published = \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( 'now' ) ); + $this->published = \gmdate( 'Y-m-d\TH:i:s\Z', \time() ); } public function __call( $method, $params ) { diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 10200c2..0668e4d 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -185,7 +185,7 @@ class Follower { $this->shared_inbox = $meta['inbox']; } - $this->updated_at = \strtotime( 'now' ); + $this->updated_at = \time(); } /** @@ -320,7 +320,7 @@ class Follower { ) ); - $this->updated_at = \strtotime( 'now' ); + $this->updated_at = \time(); $this->update_term_meta(); } diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index cf8b53f..2467363 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -102,7 +102,7 @@ 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', strtotime( 'now' ) - 804800 ); + update_term_meta( $follower->get_id(), 'updated_at', \time() - 804800 ); $followers = \Activitypub\Collection\Followers::get_outdated_followers(); $this->assertEquals( 1, count( $followers ) ); From baa8027e3fe5e691b6197ab45fc6c07b10f48838 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 10 May 2023 18:53:09 +0200 Subject: [PATCH 17/55] check if file is_readable thanks @akirk --- activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 4d9b2e6..117b5cb 100644 --- a/activitypub.php +++ b/activitypub.php @@ -74,7 +74,7 @@ spl_autoload_register( $filename = 'class-' . strtr( $class, '_', '-' ); $file = $base_dir . $filename . '.php'; - if ( file_exists( $file ) ) { + if ( file_exists( $file ) && is_readable( $file ) ) { require_once $file; } } From 7b545b4639e3af5d49c4c432b98de2f8459460e1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 09:09:13 +0200 Subject: [PATCH 18/55] remove DIRECTORY_SEPARATOR because its not used anywhere else --- activitypub.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activitypub.php b/activitypub.php index 117b5cb..0528965 100644 --- a/activitypub.php +++ b/activitypub.php @@ -58,7 +58,7 @@ function init() { */ spl_autoload_register( function ( $class ) { - $base_dir = trailingslashit( __DIR__ ) . 'includes' . DIRECTORY_SEPARATOR; + $base_dir = \dirname( __FILE__ ) . '/includes/'; $base = 'activitypub'; $class = strtolower( $class ); @@ -68,7 +68,7 @@ spl_autoload_register( if ( false !== strpos( $class, '\\' ) ) { list( $sub_dir, $class ) = explode( '\\', $class ); - $base_dir = $base_dir . $sub_dir . DIRECTORY_SEPARATOR; + $base_dir = $base_dir . $sub_dir . '/'; } $filename = 'class-' . strtr( $class, '_', '-' ); From 47b1b10955fec4118c3149553480fb1e4dc45110 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 09:45:01 +0200 Subject: [PATCH 19/55] Fix migration script --- includes/class-migration.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index e6e6fba..7e7cd9a 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -1,7 +1,8 @@ upsert(); - $result = wp_set_object_terms( $user_id, $follower->get_actor(), self::TAXONOMY, true ); + $result = wp_set_object_terms( $user_id, $follower->get_actor(), Followers::TAXONOMY, true ); } } } From b8039141809982567028a85a79914a77cee70ba8 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 09:46:26 +0200 Subject: [PATCH 20/55] removed output formatting --- activitypub.php | 2 - includes/class-scheduler.php | 4 +- includes/collection/class-followers.php | 66 +++++-------------- includes/model/class-follower.php | 9 +++ includes/rest/class-followers.php | 8 ++- includes/table/class-followers.php | 2 +- tests/test-class-db-activitypub-followers.php | 7 ++ 7 files changed, 41 insertions(+), 57 deletions(-) diff --git a/activitypub.php b/activitypub.php index 0528965..47e4167 100644 --- a/activitypub.php +++ b/activitypub.php @@ -29,8 +29,6 @@ function init() { \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); - \define( 'ACTIVITYPUB_OBJECT', 'ACTIVITYPUB_OBJECT' ); - Migration::init(); Activity_Dispatcher::init(); Activitypub::init(); diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 4bfb8b8..a79044e 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -95,7 +95,7 @@ class Scheduler { * @return void */ public static function update_followers() { - $followers = Followers::get_outdated_followers( ACTIVITYPUB_OBJECT ); + $followers = Followers::get_outdated_followers(); foreach ( $followers as $follower ) { $meta = get_remote_metadata_by_actor( $follower->get_actor() ); @@ -116,7 +116,7 @@ class Scheduler { * @return void */ public static function cleanup_followers() { - $followers = Followers::get_faulty_followers( ACTIVITYPUB_OBJECT ); + $followers = Followers::get_faulty_followers(); foreach ( $followers as $follower ) { $meta = get_remote_metadata_by_actor( $follower->get_actor() ); diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 413af3b..8a271f6 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -316,7 +316,7 @@ class Followers { * * @return array The Term list of Followers, the format depends on $output */ - public static function get_followers( $user_id, $output = ARRAY_N, $number = null, $offset = null, $args = array() ) { + public static function get_followers( $user_id, $number = null, $offset = null, $args = array() ) { $defaults = array( 'taxonomy' => self::TAXONOMY, 'hide_empty' => false, @@ -329,25 +329,13 @@ class Followers { $args = wp_parse_args( $args, $defaults ); $terms = new WP_Term_Query( $args ); - $items = array(); - // change output format - switch ( $output ) { - case ACTIVITYPUB_OBJECT: - foreach ( $terms->get_terms() as $follower ) { - $items[] = new Follower( $follower->name ); // phpcs:ignore - } - return $items; - case OBJECT: - return $terms->get_terms(); - case ARRAY_N: - default: - foreach ( $terms->get_terms() as $follower ) { - $items[] = $follower->name; // phpcs:ignore - } - return $items; + foreach ( $terms->get_terms() as $follower ) { + $items[] = new Follower( $follower->name ); // phpcs:ignore } + + return $items; } /** @@ -414,7 +402,7 @@ class Followers { * * @return mixed The Term list of Followers, the format depends on $output. */ - public static function get_outdated_followers( $output = ARRAY_N, $number = 50, $older_than = 604800 ) { + public static function get_outdated_followers( $number = 50, $older_than = 604800 ) { $args = array( 'taxonomy' => self::TAXONOMY, 'number' => $number, @@ -432,25 +420,13 @@ class Followers { ); $terms = new WP_Term_Query( $args ); - $items = array(); - // change output format - switch ( $output ) { - case ACTIVITYPUB_OBJECT: - foreach ( $terms->get_terms() as $follower ) { - $items[] = new Follower( $follower->name ); // phpcs:ignore - } - return $items; - case OBJECT: - return $terms->get_terms(); - case ARRAY_N: - default: - foreach ( $terms->get_terms() as $follower ) { - $items[] = $follower->name; // phpcs:ignore - } - return $items; + foreach ( $terms->get_terms() as $follower ) { + $items[] = new Follower( $follower->name ); // phpcs:ignore } + + return $items; } /** @@ -461,7 +437,7 @@ class Followers { * * @return mixed The Term list of Followers, the format depends on $output. */ - public static function get_faulty_followers( $output = ARRAY_N, $number = 10 ) { + public static function get_faulty_followers( $number = 10 ) { $args = array( 'taxonomy' => self::TAXONOMY, 'number' => $number, @@ -474,24 +450,12 @@ class Followers { ); $terms = new WP_Term_Query( $args ); - $items = array(); - // change output format - switch ( $output ) { - case ACTIVITYPUB_OBJECT: - foreach ( $terms->get_terms() as $follower ) { - $items[] = new Follower( $follower->name ); // phpcs:ignore - } - return $items; - case OBJECT: - return $terms->get_terms(); - case ARRAY_N: - default: - foreach ( $terms->get_terms() as $follower ) { - $items[] = $follower->name; // phpcs:ignore - } - return $items; + foreach ( $terms->get_terms() as $follower ) { + $items[] = new Follower( $follower->name ); // phpcs:ignore } + + return $items; } } diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 0668e4d..6c03fd8 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -159,6 +159,15 @@ class Follower { } } + /** + * Magic function to return the Actor-URL when the Object is used as a string + * + * @return string + */ + public function __toString() { + return $this->get_actor(); + } + /** * Prefill the Object with the meta data. * diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index 9a8b9b6..7dd49a8 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -81,7 +81,13 @@ class Followers { $json->partOf = \get_rest_url( null, "/activitypub/1.0/users/$user_id/followers" ); // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore $json->totalItems = FollowerCollection::count_followers( $user_id ); // phpcs:ignore - $json->orderedItems = FollowerCollection::get_followers( $user_id, ARRAY_N ); // phpcs:ignore + // phpcs:ignore + $json->orderedItems = array_map( + function( $item ) { + return $item->get_actor(); + }, + FollowerCollection::get_followers( $user_id ) + ); $response = new WP_REST_Response( $json, 200 ); $response->header( 'Content-Type', 'application/activity+json' ); diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index c9b5948..03fb70f 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -35,7 +35,7 @@ class Followers extends WP_List_Table { $page_num = $this->get_pagenum(); $per_page = 20; - $follower = FollowerCollection::get_followers( \get_current_user_id(), ACTIVITYPUB_OBJECT, $per_page, ( $page_num - 1 ) * $per_page ); + $follower = 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(); diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index 2467363..a04145a 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -58,6 +58,13 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { $this->assertEquals( 3, \count( $db_followers ) ); + $db_followers = array_map( + function( $item ) { + return $item->get_actor(); + }, + $db_followers + ); + $this->assertSame( array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' ), $db_followers ); } From b85b0167c0e9acf44d42f8c3b109cfb1246f7b0b Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 10:53:19 +0200 Subject: [PATCH 21/55] Update activitypub.php Co-authored-by: Alex Kirk --- activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 47e4167..cb67fa8 100644 --- a/activitypub.php +++ b/activitypub.php @@ -82,7 +82,7 @@ spl_autoload_register( require_once \dirname( __FILE__ ) . '/includes/functions.php'; // load NodeInfo endpoints only if blog is public -if ( true === (bool) \get_option( 'blog_public', 1 ) ) { +if ( \get_option( 'blog_public', 1 ) ) { Rest\NodeInfo::init(); } From cfa8974ffaf862b9479f61d0c2c4348011835cd4 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 14:38:57 +0200 Subject: [PATCH 22/55] support more more depth in the namespaces --- activitypub.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index cb67fa8..3e2d98f 100644 --- a/activitypub.php +++ b/activitypub.php @@ -65,7 +65,9 @@ spl_autoload_register( $class = str_replace( 'activitypub\\', '', $class ); if ( false !== strpos( $class, '\\' ) ) { - list( $sub_dir, $class ) = explode( '\\', $class ); + $parts = explode( '\\', $class ); + $class = array_pop( $parts ); + $sub_dir = implode( '/', $parts ); $base_dir = $base_dir . $sub_dir . '/'; } From 663c6315c96ec462ac272214f1b2c277d23ad542 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 14:40:47 +0200 Subject: [PATCH 23/55] make debug file optional --- activitypub.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/activitypub.php b/activitypub.php index 3e2d98f..1f42652 100644 --- a/activitypub.php +++ b/activitypub.php @@ -88,8 +88,9 @@ if ( \get_option( 'blog_public', 1 ) ) { Rest\NodeInfo::init(); } -if ( \WP_DEBUG ) { - require_once \dirname( __FILE__ ) . '/includes/debug.php'; +$debug_file = \dirname( __FILE__ ) . '/includes/debug.php'; +if ( \WP_DEBUG && file_exists( $debug_file ) && is_readable( $debug_file ) ) { + require_once $debug_file; } /** From 068576342454e910d5fc4690545c37e89a0c4b50 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 11 May 2023 14:55:11 +0200 Subject: [PATCH 24/55] return error if class does not exist or is not readable --- activitypub.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/activitypub.php b/activitypub.php index 1f42652..c0a95fe 100644 --- a/activitypub.php +++ b/activitypub.php @@ -55,11 +55,11 @@ function init() { * Class Autoloader */ spl_autoload_register( - function ( $class ) { + function ( $full_class ) { $base_dir = \dirname( __FILE__ ) . '/includes/'; $base = 'activitypub'; - $class = strtolower( $class ); + $class = strtolower( $full_class ); if ( strncmp( $class, $base, strlen( $base ) ) === 0 ) { $class = str_replace( 'activitypub\\', '', $class ); @@ -76,6 +76,9 @@ spl_autoload_register( if ( file_exists( $file ) && is_readable( $file ) ) { require_once $file; + } else { + // translators: %s is the class name + \wp_die( sprintf( esc_html__( 'Required class not found or not readable: %s', 'activitypub' ), esc_html( $full_class ) ) ); } } } From d16014911bffb0527a2597909d5005f713c5e7b5 Mon Sep 17 00:00:00 2001 From: Jeremy Herve Date: Thu, 11 May 2023 19:53:53 +0200 Subject: [PATCH 25/55] Compat: introduce a conditional to detect ActivityPub requests This conditional could be used within the plugin, but also by third-party plugins, to detect whether a request is an ActivityPub request, without having to manually check for query vars and headers every time. --- includes/functions.php | 44 ++++++++++++++++++++++++++++++++++++++++++ readme.txt | 1 + 2 files changed, 45 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index 19a3512..f1d6c65 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -228,3 +228,47 @@ function is_tombstone( $wp_error ) { return false; } + +/** + * Check if a request is for an ActivityPub request. + * + * @return bool False by default. + */ +function is_activitypub_request() { + global $wp_query; + + /* + * ActivityPub requests are currently only made for + * author archives, singular posts, and the homepage. + */ + if ( ! \is_author() && ! \is_singular() && ! \is_home() ) { + return false; + } + + // One can trigger an ActivityPub request by adding ?activitypub to the URL. + global $wp_query; + if ( isset( $wp_query->query_vars['activitypub'] ) ) { + return true; + } + + /* + * The other (more common) option to make an ActivityPub request + * is to send an Accept header. + */ + if ( isset( $_SERVER['HTTP_ACCEPT'] ) ) { + $accept = $_SERVER['HTTP_ACCEPT']; + + /* + * $accept can be a single value, or a comma separated list of values. + * We want to support both scenarios, + * and return true when the header includes at least one of the following: + * - application/activity+json + * - application/ld+json + */ + if ( preg_match( '/(application\/(ld\+json|activity\+json))/', $accept ) ) { + return true; + } + } + + return false; +} diff --git a/readme.txt b/readme.txt index 0004235..c08f455 100644 --- a/readme.txt +++ b/readme.txt @@ -115,6 +115,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu = Next = +* Compatibility: add a new conditional, `\Activitypub\is_activitypub_request()`, to allow third-party plugins to detect ActivityPub requests. * Compatibility: add hooks to allow modifying images returned in ActivityPub requests. * Compatibility: indicate that the plugin is compatible and has been tested with the latest version of WordPress, 6.2. From abfa7c796990bf6f09228c0164af2b490ecc3488 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Thu, 11 May 2023 13:25:30 -0500 Subject: [PATCH 26/55] Allow setting the REST namespace with `ACTIVITYPUB_REST_NAMESPACE` --- activitypub.php | 7 ++++--- includes/model/class-activity.php | 3 ++- includes/model/class-post.php | 3 ++- includes/rest/class-followers.php | 4 ++-- includes/rest/class-following.php | 4 ++-- includes/rest/class-inbox.php | 6 +++--- includes/rest/class-nodeinfo.php | 8 ++++---- includes/rest/class-ostatus.php | 2 +- includes/rest/class-outbox.php | 4 ++-- includes/rest/class-webfinger.php | 2 +- templates/author-json.php | 8 ++++---- templates/blog-json.php | 10 ++++++---- tests/test-class-activitypub-activity.php | 2 +- 13 files changed, 34 insertions(+), 29 deletions(-) diff --git a/activitypub.php b/activitypub.php index 2e731f2..6efc3c2 100644 --- a/activitypub.php +++ b/activitypub.php @@ -25,6 +25,7 @@ function init() { \defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=

)|(?<=
)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' ); \defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); \defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "[ap_title]\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" ); + defined( 'ACTIVITYPUB_REST_NAMESPACE' ) || define( 'ACTIVITYPUB_REST_NAMESPACE', 'activitypub/1.0' ); \define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); @@ -120,12 +121,12 @@ function plugin_settings_link( $actions ) { */ function add_rewrite_rules() { if ( ! \class_exists( 'Webfinger' ) ) { - \add_rewrite_rule( '^.well-known/webfinger', 'index.php?rest_route=/activitypub/1.0/webfinger', 'top' ); + \add_rewrite_rule( '^.well-known/webfinger', 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/webfinger', 'top' ); } if ( ! \class_exists( 'Nodeinfo' ) || ! (bool) \get_option( 'blog_public', 1 ) ) { - \add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', 'top' ); - \add_rewrite_rule( '^.well-known/x-nodeinfo2', 'index.php?rest_route=/activitypub/1.0/nodeinfo2', 'top' ); + \add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo/discovery', 'top' ); + \add_rewrite_rule( '^.well-known/x-nodeinfo2', 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo2', 'top' ); } \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES ); diff --git a/includes/model/class-activity.php b/includes/model/class-activity.php index b5372dc..ce99e87 100644 --- a/includes/model/class-activity.php +++ b/includes/model/class-activity.php @@ -148,7 +148,8 @@ class Activity { $this->published = $object['published']; } - $this->add_to( \get_rest_url( null, '/activitypub/1.0/users/' . intval( $post->get_post_author() ) . '/followers' ) ); + $url = sprintf( '/%/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, intval( $post->get_post_author() ) ); + $this->add_to( \get_rest_url( null, $url ) ); if ( isset( $this->object['attributedTo'] ) ) { $this->actor = $this->object['attributedTo']; diff --git a/includes/model/class-post.php b/includes/model/class-post.php index ccb161c..022e390 100644 --- a/includes/model/class-post.php +++ b/includes/model/class-post.php @@ -142,7 +142,8 @@ class Post { */ public function __construct( $post ) { $this->post = \get_post( $post ); - $this->add_to( \get_rest_url( null, '/activitypub/1.0/users/' . intval( $this->get_post_author() ) . '/followers' ) ); + $url = sprintf( '/%/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, intval( $post->get_post_author() ) ); + $this->add_to( \get_rest_url( null, $url ) ); } /** diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index 9a8b9b6..3abff6e 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -27,7 +27,7 @@ class Followers { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/users/(?P\d+)/followers', array( array( @@ -78,7 +78,7 @@ class Followers { $json->actor = \get_author_posts_url( $user_id ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, "/activitypub/1.0/users/$user_id/followers" ); // phpcs:ignore + $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore $json->totalItems = FollowerCollection::count_followers( $user_id ); // phpcs:ignore $json->orderedItems = FollowerCollection::get_followers( $user_id, ARRAY_N ); // phpcs:ignore diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index 52a95e7..2606df4 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -21,7 +21,7 @@ class Following { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/users/(?P\d+)/following', array( array( @@ -72,7 +72,7 @@ class Following { $json->actor = \get_author_posts_url( $user_id ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, "/activitypub/1.0/users/$user_id/following" ); // phpcs:ignore + $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/following', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore $json->orderedItems = apply_filters( 'activitypub_following', array(), $user ); // phpcs:ignore diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 5a23d02..16735df 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -26,7 +26,7 @@ class Inbox { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/inbox', array( array( @@ -39,7 +39,7 @@ class Inbox { ); \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/users/(?P\d+)/inbox', array( array( @@ -108,7 +108,7 @@ class Inbox { $json->id = \home_url( \add_query_arg( null, null ) ); $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, "/activitypub/1.0/users/$user_id/inbox" ); // phpcs:ignore + $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/inbox', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore diff --git a/includes/rest/class-nodeinfo.php b/includes/rest/class-nodeinfo.php index 980c24b..bafa7e4 100644 --- a/includes/rest/class-nodeinfo.php +++ b/includes/rest/class-nodeinfo.php @@ -23,7 +23,7 @@ class Nodeinfo { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/nodeinfo/discovery', array( array( @@ -35,7 +35,7 @@ class Nodeinfo { ); \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/nodeinfo', array( array( @@ -47,7 +47,7 @@ class Nodeinfo { ); \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/nodeinfo2', array( array( @@ -173,7 +173,7 @@ class Nodeinfo { $discovery['links'] = array( array( 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', - 'href' => \get_rest_url( null, 'activitypub/1.0/nodeinfo' ), + 'href' => \get_rest_url( null, ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo' ), ), ); diff --git a/includes/rest/class-ostatus.php b/includes/rest/class-ostatus.php index 45ff901..415d502 100644 --- a/includes/rest/class-ostatus.php +++ b/includes/rest/class-ostatus.php @@ -14,7 +14,7 @@ class Ostatus { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/ostatus/remote-follow', array( array( diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 905dfd5..5ae8e50 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -21,7 +21,7 @@ class Outbox { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/users/(?P\d+)/outbox', array( array( @@ -72,7 +72,7 @@ class Outbox { $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->actor = \get_author_posts_url( $user_id ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, "/activitypub/1.0/users/$user_id/outbox" ); // phpcs:ignore + $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/outbox', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore // phpcs:ignore diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index 10dcfa4..f75a3f7 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -25,7 +25,7 @@ class Webfinger { */ public static function register_routes() { \register_rest_route( - 'activitypub/1.0', + ACTIVITYPUB_REST_NAMESPACE, '/webfinger', array( array( diff --git a/templates/author-json.php b/templates/author-json.php index 3b355b9..090920d 100644 --- a/templates/author-json.php +++ b/templates/author-json.php @@ -28,10 +28,10 @@ if ( \has_header_image() ) { ); } -$json->inbox = \get_rest_url( null, "/activitypub/1.0/users/$author_id/inbox" ); -$json->outbox = \get_rest_url( null, "/activitypub/1.0/users/$author_id/outbox" ); -$json->followers = \get_rest_url( null, "/activitypub/1.0/users/$author_id/followers" ); -$json->following = \get_rest_url( null, "/activitypub/1.0/users/$author_id/following" ); +$json->inbox = \get_rest_url( null, sprintf( '/%s/users/%d/inbox', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); +$json->outbox = \get_rest_url( null, sprintf( '/%s/users/%d/outbox', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); +$json->followers = \get_rest_url( null, sprintf( '/%s/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); +$json->following = \get_rest_url( null, sprintf( '/%s/users/%d/following', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_approves_followers', \__return_false() ); // phpcs:ignore diff --git a/templates/blog-json.php b/templates/blog-json.php index 4a1efc8..38f0406 100644 --- a/templates/blog-json.php +++ b/templates/blog-json.php @@ -27,10 +27,12 @@ if ( \has_header_image() ) { ); } -$json->inbox = \get_rest_url( null, '/activitypub/1.0/blog/inbox' ); -$json->outbox = \get_rest_url( null, '/activitypub/1.0/blog/outbox' ); -$json->followers = \get_rest_url( null, '/activitypub/1.0/blog/followers' ); -$json->following = \get_rest_url( null, '/activitypub/1.0/blog/following' ); +$blog_base = sprintf( '/%s/blog/', ACTIVITYPUB_REST_NAMESPACE ); + +$json->inbox = \get_rest_url( null, $blog_base . 'inbox' ); +$json->outbox = \get_rest_url( null, $blog_base . 'outbox' ); +$json->followers = \get_rest_url( null, $blog_base . 'followers' ); +$json->following = \get_rest_url( null, $blog_base . 'following' ); $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_approves_followers', \__return_false() ); // phpcs:ignore diff --git a/tests/test-class-activitypub-activity.php b/tests/test-class-activitypub-activity.php index 7fe6551..b18c6ab 100644 --- a/tests/test-class-activitypub-activity.php +++ b/tests/test-class-activitypub-activity.php @@ -22,7 +22,7 @@ class Test_Activitypub_Activity extends WP_UnitTestCase { $activitypub_activity = new \Activitypub\Model\Activity( 'Create' ); $activitypub_activity->from_post( $activitypub_post ); - $this->assertContains( \get_rest_url( null, '/activitypub/1.0/users/1/followers' ), $activitypub_activity->get_to() ); + $this->assertContains( \get_rest_url( null, '/' . ACTIVITYPUB_REST_NAMESPACE . '/users/1/followers' ), $activitypub_activity->get_to() ); $this->assertContains( 'https://example.com/alex', $activitypub_activity->get_cc() ); remove_all_filters( 'activitypub_extract_mentions' ); From 94e5539d75ef610f41c9a23195e0ba9c674513ee Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 12 May 2023 10:23:58 +0200 Subject: [PATCH 27/55] reset errors if new is set --- includes/model/class-follower.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 6c03fd8..a663270 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -224,6 +224,18 @@ class Follower { return null; } + /** + * 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. * From 0b60944f93a947bbb458e82ad919338166ec087c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 03:05:09 +0000 Subject: [PATCH 28/55] Update dealerdirect/phpcodesniffer-composer-installer requirement Updates the requirements on [dealerdirect/phpcodesniffer-composer-installer](https://github.com/PHPCSStandards/composer-installer) to permit the latest version. - [Release notes](https://github.com/PHPCSStandards/composer-installer/releases) - [Changelog](https://github.com/PHPCSStandards/composer-installer/blob/main/.github_changelog_generator) - [Commits](https://github.com/PHPCSStandards/composer-installer/compare/v0.7.1...v1.0.0) --- updated-dependencies: - dependency-name: dealerdirect/phpcodesniffer-composer-installer dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 67bd2d3..b0117c4 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "squizlabs/php_codesniffer": "3.*", "wp-coding-standards/wpcs": "*", "yoast/phpunit-polyfills": "^1.0", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1" + "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0" }, "config": { "allow-plugins": true From 314ccf43a6915d87379862383a369be95c2e4bdd Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Fri, 12 May 2023 14:58:50 -0500 Subject: [PATCH 29/55] add a `get_rest_url_by_path` helper function, and use it --- includes/functions.php | 13 +++++++++++++ includes/model/class-activity.php | 4 ++-- includes/model/class-post.php | 4 ++-- includes/rest/class-followers.php | 2 +- includes/rest/class-following.php | 2 +- includes/rest/class-inbox.php | 2 +- includes/rest/class-nodeinfo.php | 2 +- includes/rest/class-outbox.php | 2 +- includes/rest/class-webfinger.php | 1 + templates/author-json.php | 8 ++++---- templates/blog-json.php | 10 ++++------ tests/test-class-activitypub-activity.php | 2 +- 12 files changed, 32 insertions(+), 20 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 19a3512..41f0d46 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -228,3 +228,16 @@ function is_tombstone( $wp_error ) { return false; } + +/** + * Get the REST URL relative to this plugin's namespace. + * + * @param string $path Optional. REST route path. Otherwise this plugin's namespaced root. + * @return string REST URL relative to this plugin's namespace. + */ +function get_rest_url_by_path( $path = '' ) { + // we'll handle the leading slash. + $path = ltrim( $path, '/' ); + $url = sprintf( '/%s/%s', ACTIVITYPUB_REST_NAMESPACE, $path ); + return \get_rest_url( null, $url ); +} \ No newline at end of file diff --git a/includes/model/class-activity.php b/includes/model/class-activity.php index ce99e87..32dd0f5 100644 --- a/includes/model/class-activity.php +++ b/includes/model/class-activity.php @@ -148,8 +148,8 @@ class Activity { $this->published = $object['published']; } - $url = sprintf( '/%/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, intval( $post->get_post_author() ) ); - $this->add_to( \get_rest_url( null, $url ) ); + $path = sprintf( 'users/%d/followers', intval( $post->get_post_author() ) ); + $this->add_to( get_rest_url_by_path( $path ) ); if ( isset( $this->object['attributedTo'] ) ) { $this->actor = $this->object['attributedTo']; diff --git a/includes/model/class-post.php b/includes/model/class-post.php index 022e390..528182c 100644 --- a/includes/model/class-post.php +++ b/includes/model/class-post.php @@ -142,8 +142,8 @@ class Post { */ public function __construct( $post ) { $this->post = \get_post( $post ); - $url = sprintf( '/%/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, intval( $post->get_post_author() ) ); - $this->add_to( \get_rest_url( null, $url ) ); + $path = sprintf( 'users/%d/followers', intval( $this->get_post_author() ) ); + $this->add_to( get_rest_url_by_path( $path ) ); } /** diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index 3abff6e..1ec2c78 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -78,7 +78,7 @@ class Followers { $json->actor = \get_author_posts_url( $user_id ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore + $json->partOf = get_rest_url_by_path( sprintf( 'users/%d/followers', $user_id ) ); // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore $json->totalItems = FollowerCollection::count_followers( $user_id ); // phpcs:ignore $json->orderedItems = FollowerCollection::get_followers( $user_id, ARRAY_N ); // phpcs:ignore diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index 2606df4..0c685d5 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -72,7 +72,7 @@ class Following { $json->actor = \get_author_posts_url( $user_id ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/following', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore + $json->partOf = get_rest_url_by_path( sprintf( 'users/%d/following', $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore $json->orderedItems = apply_filters( 'activitypub_following', array(), $user ); // phpcs:ignore diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 16735df..a73a783 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -108,7 +108,7 @@ class Inbox { $json->id = \home_url( \add_query_arg( null, null ) ); $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/inbox', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore + $json->partOf = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore diff --git a/includes/rest/class-nodeinfo.php b/includes/rest/class-nodeinfo.php index bafa7e4..3b3c6dc 100644 --- a/includes/rest/class-nodeinfo.php +++ b/includes/rest/class-nodeinfo.php @@ -173,7 +173,7 @@ class Nodeinfo { $discovery['links'] = array( array( 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', - 'href' => \get_rest_url( null, ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo' ), + 'href' => get_rest_url_by_path( 'nodeinfo' ), ), ); diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 5ae8e50..1a52750 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -72,7 +72,7 @@ class Outbox { $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->actor = \get_author_posts_url( $user_id ); $json->type = 'OrderedCollectionPage'; - $json->partOf = \get_rest_url( null, sprintf( '/%s/users/%d/outbox', ACTIVITYPUB_REST_NAMESPACE, $user_id ) ); // phpcs:ignore + $json->partOf = get_rest_url_by_path( sprintf( 'users/%d/outbox', $user_id ) ); // phpcs:ignore $json->totalItems = 0; // phpcs:ignore // phpcs:ignore diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index f75a3f7..b04839b 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -24,6 +24,7 @@ class Webfinger { * Register routes */ public static function register_routes() { + \l( 'register webfinger' ); \register_rest_route( ACTIVITYPUB_REST_NAMESPACE, '/webfinger', diff --git a/templates/author-json.php b/templates/author-json.php index 090920d..cf4b85e 100644 --- a/templates/author-json.php +++ b/templates/author-json.php @@ -28,10 +28,10 @@ if ( \has_header_image() ) { ); } -$json->inbox = \get_rest_url( null, sprintf( '/%s/users/%d/inbox', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); -$json->outbox = \get_rest_url( null, sprintf( '/%s/users/%d/outbox', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); -$json->followers = \get_rest_url( null, sprintf( '/%s/users/%d/followers', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); -$json->following = \get_rest_url( null, sprintf( '/%s/users/%d/following', ACTIVITYPUB_REST_NAMESPACE, $author_id ) ); +$json->inbox = get_rest_url_by_path( sprintf( 'users/%d/inbox', $author_id ) ); +$json->outbox = get_rest_url_by_path( sprintf( 'users/%d/outbox', $author_id ) ); +$json->followers = get_rest_url_by_path( sprintf( 'users/%d/followers', $author_id ) ); +$json->following = get_rest_url_by_path( sprintf( 'users/%d/following', $author_id ) ); $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_approves_followers', \__return_false() ); // phpcs:ignore diff --git a/templates/blog-json.php b/templates/blog-json.php index 38f0406..a988385 100644 --- a/templates/blog-json.php +++ b/templates/blog-json.php @@ -27,12 +27,10 @@ if ( \has_header_image() ) { ); } -$blog_base = sprintf( '/%s/blog/', ACTIVITYPUB_REST_NAMESPACE ); - -$json->inbox = \get_rest_url( null, $blog_base . 'inbox' ); -$json->outbox = \get_rest_url( null, $blog_base . 'outbox' ); -$json->followers = \get_rest_url( null, $blog_base . 'followers' ); -$json->following = \get_rest_url( null, $blog_base . 'following' ); +$json->inbox = get_rest_url_by_path( 'blog/inbox' ); +$json->outbox = get_rest_url_by_path( 'blog/outbox' ); +$json->followers = get_rest_url_by_path( 'blog/followers' ); +$json->following = get_rest_url_by_path( 'blog/following' ); $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_approves_followers', \__return_false() ); // phpcs:ignore diff --git a/tests/test-class-activitypub-activity.php b/tests/test-class-activitypub-activity.php index b18c6ab..6569652 100644 --- a/tests/test-class-activitypub-activity.php +++ b/tests/test-class-activitypub-activity.php @@ -22,7 +22,7 @@ class Test_Activitypub_Activity extends WP_UnitTestCase { $activitypub_activity = new \Activitypub\Model\Activity( 'Create' ); $activitypub_activity->from_post( $activitypub_post ); - $this->assertContains( \get_rest_url( null, '/' . ACTIVITYPUB_REST_NAMESPACE . '/users/1/followers' ), $activitypub_activity->get_to() ); + $this->assertContains( get_rest_url_by_path( 'users/1/followers' ), $activitypub_activity->get_to() ); $this->assertContains( 'https://example.com/alex', $activitypub_activity->get_cc() ); remove_all_filters( 'activitypub_extract_mentions' ); From 3fa5e4f37ec85ad3ab2eb912caed2d76d24e3186 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Fri, 12 May 2023 15:31:53 -0500 Subject: [PATCH 30/55] now with more `use` --- includes/model/class-activity.php | 2 ++ includes/model/class-post.php | 2 ++ includes/rest/class-followers.php | 1 + includes/rest/class-following.php | 2 ++ includes/rest/class-inbox.php | 1 + includes/rest/class-nodeinfo.php | 2 ++ includes/rest/class-outbox.php | 2 ++ templates/author-json.php | 2 ++ templates/blog-json.php | 2 ++ tests/test-class-activitypub-activity.php | 2 +- 10 files changed, 17 insertions(+), 1 deletion(-) diff --git a/includes/model/class-activity.php b/includes/model/class-activity.php index 32dd0f5..4bd0d5e 100644 --- a/includes/model/class-activity.php +++ b/includes/model/class-activity.php @@ -1,6 +1,8 @@ {'@context'} = \Activitypub\get_context(); diff --git a/tests/test-class-activitypub-activity.php b/tests/test-class-activitypub-activity.php index 6569652..af0ed55 100644 --- a/tests/test-class-activitypub-activity.php +++ b/tests/test-class-activitypub-activity.php @@ -22,7 +22,7 @@ class Test_Activitypub_Activity extends WP_UnitTestCase { $activitypub_activity = new \Activitypub\Model\Activity( 'Create' ); $activitypub_activity->from_post( $activitypub_post ); - $this->assertContains( get_rest_url_by_path( 'users/1/followers' ), $activitypub_activity->get_to() ); + $this->assertContains( \Activitypub\get_rest_url_by_path( 'users/1/followers' ), $activitypub_activity->get_to() ); $this->assertContains( 'https://example.com/alex', $activitypub_activity->get_cc() ); remove_all_filters( 'activitypub_extract_mentions' ); From 5a91fdcf0ad73a1555d5928ac57a1db0399df123 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Fri, 12 May 2023 15:43:04 -0500 Subject: [PATCH 31/55] remove debug log --- includes/rest/class-webfinger.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index b04839b..f75a3f7 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -24,7 +24,6 @@ class Webfinger { * Register routes */ public static function register_routes() { - \l( 'register webfinger' ); \register_rest_route( ACTIVITYPUB_REST_NAMESPACE, '/webfinger', From ec00ace234816cc7d4b4cbad6a7674d69834f508 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Fri, 12 May 2023 16:42:30 -0500 Subject: [PATCH 32/55] add a `activitypub_rest_url` filter --- includes/functions.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 39ba262..7d6f86c 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -238,8 +238,11 @@ function is_tombstone( $wp_error ) { function get_rest_url_by_path( $path = '' ) { // we'll handle the leading slash. $path = ltrim( $path, '/' ); - $url = sprintf( '/%s/%s', ACTIVITYPUB_REST_NAMESPACE, $path ); - return \get_rest_url( null, $url ); + $namespaced_path = sprintf( '/%s/%s', ACTIVITYPUB_REST_NAMESPACE, $path ); + $rest_url = \get_rest_url( null, $namespaced_path ); + // Just in case there are non-default ways of handling REST URLs. + $rest_url = \apply_filters( 'activitypub_rest_url', $rest_url, $path, ACTIVITYPUB_REST_NAMESPACE ); + return $rest_url; } /** From 31e7e44642258cdc7bd5311c426af478d2eea0bd Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Fri, 12 May 2023 18:25:49 -0500 Subject: [PATCH 33/55] remove filter --- includes/functions.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 7d6f86c..8ef0c35 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -239,10 +239,7 @@ function get_rest_url_by_path( $path = '' ) { // we'll handle the leading slash. $path = ltrim( $path, '/' ); $namespaced_path = sprintf( '/%s/%s', ACTIVITYPUB_REST_NAMESPACE, $path ); - $rest_url = \get_rest_url( null, $namespaced_path ); - // Just in case there are non-default ways of handling REST URLs. - $rest_url = \apply_filters( 'activitypub_rest_url', $rest_url, $path, ACTIVITYPUB_REST_NAMESPACE ); - return $rest_url; + return \get_rest_url( null, $namespaced_path ); } /** From 8b9026ab5ec3b427cfb9c23925c4506aba6d6b09 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 15 May 2023 10:55:07 +0200 Subject: [PATCH 34/55] fix get_post_content_template function --- includes/model/class-post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/model/class-post.php b/includes/model/class-post.php index ccb161c..e54c295 100644 --- a/includes/model/class-post.php +++ b/includes/model/class-post.php @@ -537,6 +537,6 @@ class Post { return "[ap_content]\n\n[ap_hashtags]\n\n[ap_permalink type=\"html\"]"; } - return $content; + return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ); } } From 9cd2a049557b9abd567df8a5b64f0acb767da9d4 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 16 May 2023 08:14:04 +0200 Subject: [PATCH 35/55] re-added some namespace consts --- activitypub.php | 2 +- includes/class-activitypub.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activitypub.php b/activitypub.php index 817f553..71e7261 100644 --- a/activitypub.php +++ b/activitypub.php @@ -25,7 +25,7 @@ function init() { \defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=

)|(?<=
)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' ); \defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); \defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "[ap_title]\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" ); - defined( 'ACTIVITYPUB_REST_NAMESPACE' ) || define( 'ACTIVITYPUB_REST_NAMESPACE', 'activitypub/1.0' ); + \defined( 'ACTIVITYPUB_REST_NAMESPACE' ) || \define( 'ACTIVITYPUB_REST_NAMESPACE', 'activitypub/1.0' ); \define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 37bedf4..c5d0e71 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -225,7 +225,7 @@ class Activitypub { if ( ! \class_exists( 'Webfinger' ) ) { \add_rewrite_rule( '^.well-known/webfinger', - 'index.php?rest_route=/activitypub/1.0/webfinger', + 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/webfinger', 'top' ); } @@ -233,12 +233,12 @@ class Activitypub { if ( ! \class_exists( 'Nodeinfo' ) && true === (bool) \get_option( 'blog_public', 1 ) ) { \add_rewrite_rule( '^.well-known/nodeinfo', - 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', + 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo/discovery', 'top' ); \add_rewrite_rule( '^.well-known/x-nodeinfo2', - 'index.php?rest_route=/activitypub/1.0/nodeinfo2', + 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo2', 'top' ); } From 3d16b8de1d46f13c6ec2d14cd5e5ee6e678e3d85 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 17 May 2023 09:01:28 +0200 Subject: [PATCH 36/55] use full function name in templates --- templates/author-json.php | 10 ++++------ templates/blog-json.php | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/templates/author-json.php b/templates/author-json.php index f132b4b..7b112ac 100644 --- a/templates/author-json.php +++ b/templates/author-json.php @@ -1,6 +1,4 @@ inbox = get_rest_url_by_path( sprintf( 'users/%d/inbox', $author_id ) ); -$json->outbox = get_rest_url_by_path( sprintf( 'users/%d/outbox', $author_id ) ); -$json->followers = get_rest_url_by_path( sprintf( 'users/%d/followers', $author_id ) ); -$json->following = get_rest_url_by_path( sprintf( 'users/%d/following', $author_id ) ); +$json->inbox = \Activitypub\get_rest_url_by_path( sprintf( 'users/%d/inbox', $author_id ) ); +$json->outbox = \Activitypub\get_rest_url_by_path( sprintf( 'users/%d/outbox', $author_id ) ); +$json->followers = \Activitypub\get_rest_url_by_path( sprintf( 'users/%d/followers', $author_id ) ); +$json->following = \Activitypub\get_rest_url_by_path( sprintf( 'users/%d/following', $author_id ) ); $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_approves_followers', \__return_false() ); // phpcs:ignore diff --git a/templates/blog-json.php b/templates/blog-json.php index 2da9c65..b87bc94 100644 --- a/templates/blog-json.php +++ b/templates/blog-json.php @@ -1,6 +1,4 @@ {'@context'} = \Activitypub\get_context(); @@ -29,10 +27,10 @@ if ( \has_header_image() ) { ); } -$json->inbox = get_rest_url_by_path( 'blog/inbox' ); -$json->outbox = get_rest_url_by_path( 'blog/outbox' ); -$json->followers = get_rest_url_by_path( 'blog/followers' ); -$json->following = get_rest_url_by_path( 'blog/following' ); +$json->inbox = \Activitypub\get_rest_url_by_path( 'blog/inbox' ); +$json->outbox = \Activitypub\get_rest_url_by_path( 'blog/outbox' ); +$json->followers = \Activitypub\get_rest_url_by_path( 'blog/followers' ); +$json->following = \Activitypub\get_rest_url_by_path( 'blog/following' ); $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_approves_followers', \__return_false() ); // phpcs:ignore From d89c05aa49801ad5974179345440c31bc55fd8e0 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 17 May 2023 09:02:09 +0200 Subject: [PATCH 37/55] init missing Nodeinfo endpoint --- activitypub.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/activitypub.php b/activitypub.php index 71e7261..3a77944 100644 --- a/activitypub.php +++ b/activitypub.php @@ -32,8 +32,8 @@ function init() { \define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); Migration::init(); - Activity_Dispatcher::init(); Activitypub::init(); + Activity_Dispatcher::init(); Collection\Followers::init(); // Configure the REST API route @@ -41,13 +41,13 @@ function init() { Rest\Inbox::init(); Rest\Followers::init(); Rest\Following::init(); + Rest\Nodeinfo::init(); Rest\Webfinger::init(); Admin::init(); Hashtag::init(); Shortcodes::init(); Mention::init(); - Debug::init(); Health_Check::init(); Scheduler::init(); } @@ -96,6 +96,7 @@ if ( \get_option( 'blog_public', 1 ) ) { $debug_file = \dirname( __FILE__ ) . '/includes/debug.php'; if ( \WP_DEBUG && file_exists( $debug_file ) && is_readable( $debug_file ) ) { require_once $debug_file; + Debug::init(); } /** From 60fc581e1de7e6bfba9ab7dc7610102f394359a0 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 17 May 2023 09:02:37 +0200 Subject: [PATCH 38/55] coding style --- includes/rest/class-followers.php | 1 + includes/rest/class-inbox.php | 1 + 2 files changed, 2 insertions(+) diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index b2983ae..fc05bdd 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -6,6 +6,7 @@ use stdClass; use WP_REST_Server; use WP_REST_Response; use Activitypub\Collection\Followers as FollowerCollection; + use function Activitypub\get_rest_url_by_path; /** diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 7e2da60..b4765d3 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -2,6 +2,7 @@ namespace Activitypub\Rest; use Activitypub\Model\Activity; + use function Activitypub\get_rest_url_by_path; /** From c34fb74b41b5bc84b38a33b0b9783fb8dd7c7d42 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 17 May 2023 09:03:26 +0200 Subject: [PATCH 39/55] coding style --- includes/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/functions.php b/includes/functions.php index 8ef0c35..5bcf97a 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -233,6 +233,7 @@ function is_tombstone( $wp_error ) { * Get the REST URL relative to this plugin's namespace. * * @param string $path Optional. REST route path. Otherwise this plugin's namespaced root. + * * @return string REST URL relative to this plugin's namespace. */ function get_rest_url_by_path( $path = '' ) { From a147d21fdabbb1655b6833567e2a34cc5784f020 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 17 May 2023 10:25:00 +0200 Subject: [PATCH 40/55] Update activitypub.php NodeInfo is only initialized when blog is public --- activitypub.php | 1 - 1 file changed, 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 3a77944..7448e46 100644 --- a/activitypub.php +++ b/activitypub.php @@ -41,7 +41,6 @@ function init() { Rest\Inbox::init(); Rest\Followers::init(); Rest\Following::init(); - Rest\Nodeinfo::init(); Rest\Webfinger::init(); Admin::init(); From ec3f8454c13a75b6e0f5f38e13e317c0b594e98a Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 17 May 2023 10:25:31 +0200 Subject: [PATCH 41/55] Update activitypub.php --- activitypub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.php b/activitypub.php index 7448e46..5b19dce 100644 --- a/activitypub.php +++ b/activitypub.php @@ -128,7 +128,7 @@ function plugin_settings_link( $actions ) { ) ); -register_uninstall_hook( +\register_uninstall_hook( __FILE__, array( __NAMESPACE__ . '\Activitypub', From 6a0fc43a05e56308965854126be745d17aaf109e Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Thu, 18 May 2023 19:30:08 -0500 Subject: [PATCH 42/55] Set `ACTIVITYPUB_REST_NAMESPACE` outside of `init` Needed to prevent activation errors. --- activitypub.php | 4 ++-- includes/rest/class-outbox.php | 3 +++ includes/rest/class-webfinger.php | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/activitypub.php b/activitypub.php index 5b19dce..3f582ea 100644 --- a/activitypub.php +++ b/activitypub.php @@ -15,6 +15,8 @@ namespace Activitypub; +\defined( 'ACTIVITYPUB_REST_NAMESPACE' ) || \define( 'ACTIVITYPUB_REST_NAMESPACE', 'activitypub/1.0' ); + /** * Initialize plugin */ @@ -25,8 +27,6 @@ function init() { \defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=

)|(?<=
)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' ); \defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); \defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "[ap_title]\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" ); - \defined( 'ACTIVITYPUB_REST_NAMESPACE' ) || \define( 'ACTIVITYPUB_REST_NAMESPACE', 'activitypub/1.0' ); - \define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index abffbe9..087c0ef 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -80,6 +80,8 @@ class Outbox { // phpcs:ignore $json->totalItems = 0; + // We can query this more directly based on the supplied post types. + // And cache these counts and invalidate them on publish_post. foreach ( $post_types as $post_type ) { $count_posts = \wp_count_posts( $post_type ); $json->totalItems += \intval( $count_posts->publish ); // phpcs:ignore @@ -142,6 +144,7 @@ class Outbox { 'required' => true, 'type' => 'integer', 'validate_callback' => function( $param, $request, $key ) { + // this is probably ok on multisite still? return user_can( $param, 'publish_posts' ); }, ); diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index f75a3f7..e7c00fa 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -62,6 +62,7 @@ class Webfinger { } $user = \get_user_by( 'login', \esc_sql( $resource_identifier ) ); + /// YIKES NOPE NOPE NOT ON DOTCOM! if ( ! $user || ! \user_can( $user, 'publish_posts' ) ) { return new WP_Error( 'activitypub_user_not_found', \__( 'User not found', 'activitypub' ), array( 'status' => 404 ) ); From d7d6ebbc1fd231ee4d470a2e23d3ad49eaa681e6 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 19 May 2023 11:43:54 +0200 Subject: [PATCH 43/55] remove comments @mattwiebe maybe you can add them as issues --- includes/rest/class-outbox.php | 1 - includes/rest/class-webfinger.php | 1 - 2 files changed, 2 deletions(-) diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 087c0ef..9fff8c7 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -144,7 +144,6 @@ class Outbox { 'required' => true, 'type' => 'integer', 'validate_callback' => function( $param, $request, $key ) { - // this is probably ok on multisite still? return user_can( $param, 'publish_posts' ); }, ); diff --git a/includes/rest/class-webfinger.php b/includes/rest/class-webfinger.php index e7c00fa..f75a3f7 100644 --- a/includes/rest/class-webfinger.php +++ b/includes/rest/class-webfinger.php @@ -62,7 +62,6 @@ class Webfinger { } $user = \get_user_by( 'login', \esc_sql( $resource_identifier ) ); - /// YIKES NOPE NOPE NOT ON DOTCOM! if ( ! $user || ! \user_can( $user, 'publish_posts' ) ) { return new WP_Error( 'activitypub_user_not_found', \__( 'User not found', 'activitypub' ), array( 'status' => 404 ) ); From 70c3b3fd517c3dbb7064e746e792b004d920182e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 19 May 2023 11:45:12 +0200 Subject: [PATCH 44/55] remove comments --- includes/rest/class-outbox.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 9fff8c7..abffbe9 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -80,8 +80,6 @@ class Outbox { // phpcs:ignore $json->totalItems = 0; - // We can query this more directly based on the supplied post types. - // And cache these counts and invalidate them on publish_post. foreach ( $post_types as $post_type ) { $count_posts = \wp_count_posts( $post_type ); $json->totalItems += \intval( $count_posts->publish ); // phpcs:ignore From dd486e552ff40e2bdbd9153ba04afd9d93a1782d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 19 May 2023 12:00:11 +0200 Subject: [PATCH 45/55] some code cleanups --- includes/rest/class-inbox.php | 28 +++++++++++++++++----------- includes/rest/class-outbox.php | 21 ++++++++++++++------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index b4765d3..2345769 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -1,9 +1,15 @@ \WP_REST_Server::EDITABLE, + 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( self::class, 'shared_inbox_post' ), 'args' => self::shared_inbox_post_parameters(), 'permission_callback' => '__return_true', @@ -45,13 +51,13 @@ class Inbox { '/users/(?P\d+)/inbox', array( array( - 'methods' => \WP_REST_Server::EDITABLE, + 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( self::class, 'user_inbox_post' ), 'args' => self::user_inbox_post_parameters(), 'permission_callback' => '__return_true', ), array( - 'methods' => \WP_REST_Server::READABLE, + 'methods' => WP_REST_Server::READABLE, 'callback' => array( self::class, 'user_inbox_get' ), 'args' => self::user_inbox_get_parameters(), 'permission_callback' => '__return_true', @@ -106,7 +112,7 @@ class Inbox { $json = new \stdClass(); - $json->{'@context'} = \Activitypub\get_context(); + $json->{'@context'} = get_context(); $json->id = \home_url( \add_query_arg( null, null ) ); $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->type = 'OrderedCollectionPage'; @@ -126,7 +132,7 @@ class Inbox { */ \do_action( 'activitypub_inbox_post' ); - $response = new \WP_REST_Response( $json, 200 ); + $response = new WP_REST_Response( $json, 200 ); $response->header( 'Content-Type', 'application/activity+json' ); @@ -150,7 +156,7 @@ class Inbox { \do_action( 'activitypub_inbox', $data, $user_id, $type ); \do_action( "activitypub_inbox_{$type}", $data, $user_id ); - return new \WP_REST_Response( array(), 202 ); + return new WP_REST_Response( array(), 202 ); } /** @@ -166,7 +172,7 @@ class Inbox { $users = self::extract_recipients( $data ); if ( ! $users ) { - return new \WP_Error( + return new WP_Error( 'rest_invalid_param', \__( 'No recipients found', 'activitypub' ), array( @@ -189,7 +195,7 @@ class Inbox { \do_action( "activitypub_inbox_{$type}", $data, $user->ID ); } - return new \WP_REST_Response( array(), 202 ); + return new WP_REST_Response( array(), 202 ); } /** @@ -350,7 +356,7 @@ class Inbox { * @param int $user_id The id of the local blog-user */ public static function handle_reaction( $object, $user_id ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] ); + $meta = get_remote_metadata_by_actor( $object['actor'] ); $comment_post_id = \url_to_postid( $object['object'] ); @@ -395,7 +401,7 @@ class Inbox { * @param int $user_id The id of the local blog-user */ public static function handle_create( $object, $user_id ) { - $meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] ); + $meta = get_remote_metadata_by_actor( $object['actor'] ); if ( ! isset( $object['object']['inReplyTo'] ) ) { return; @@ -502,7 +508,7 @@ class Inbox { $users = array(); foreach ( $recipients as $recipient ) { - $user_id = \Activitypub\url_to_authorid( $recipient ); + $user_id = url_to_authorid( $recipient ); $user = get_user_by( 'id', $user_id ); diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index abffbe9..d6b0757 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -1,6 +1,13 @@ \d+)/outbox', array( array( - 'methods' => \WP_REST_Server::READABLE, + 'methods' => WP_REST_Server::READABLE, 'callback' => array( self::class, 'user_outbox_get' ), 'args' => self::request_parameters(), 'permission_callback' => '__return_true', @@ -48,7 +55,7 @@ class Outbox { $post_types = \get_option( 'activitypub_support_post_types', array( 'post', 'page' ) ); if ( ! $author ) { - return new \WP_Error( + return new WP_Error( 'rest_invalid_param', \__( 'User not found', 'activitypub' ), array( @@ -67,9 +74,9 @@ class Outbox { */ \do_action( 'activitypub_outbox_pre' ); - $json = new \stdClass(); + $json = new stdClass(); - $json->{'@context'} = \Activitypub\get_context(); + $json->{'@context'} = get_context(); $json->id = \home_url( \add_query_arg( null, null ) ); $json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' ); $json->actor = \get_author_posts_url( $user_id ); @@ -103,8 +110,8 @@ class Outbox { ); foreach ( $posts as $post ) { - $activitypub_post = new \Activitypub\Model\Post( $post ); - $activitypub_activity = new \Activitypub\Model\Activity( 'Create', false ); + $activitypub_post = new Post( $post ); + $activitypub_activity = new Activity( 'Create', false ); $activitypub_activity->from_post( $activitypub_post ); $json->orderedItems[] = $activitypub_activity->to_array(); // phpcs:ignore @@ -119,7 +126,7 @@ class Outbox { */ \do_action( 'activitypub_outbox_post' ); - $response = new \WP_REST_Response( $json, 200 ); + $response = new WP_REST_Response( $json, 200 ); $response->header( 'Content-Type', 'application/activity+json' ); From a1753242f3ea19b085a19c83fd0a09753720016d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 19 May 2023 18:03:05 +0200 Subject: [PATCH 46/55] fix missing namespace --- includes/rest/class-outbox.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index d6b0757..2138f71 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -8,6 +8,7 @@ use WP_REST_Response; use Activitypub\Model\Post; use Activitypub\Model\Activity; +use function Activitypub\get_context; use function Activitypub\get_rest_url_by_path; /** From 25b53887efaeaa24e26b844f8218a02f0b6fb4bf Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Fri, 19 May 2023 22:37:05 +0200 Subject: [PATCH 47/55] code improvements --- activitypub.php | 4 ++-- integration/class-buddypress.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activitypub.php b/activitypub.php index 3f582ea..a1cd6df 100644 --- a/activitypub.php +++ b/activitypub.php @@ -50,7 +50,7 @@ function init() { Health_Check::init(); Scheduler::init(); } -\add_action( 'plugins_loaded', '\Activitypub\init' ); +\add_action( 'plugins_loaded', __NAMESPACE__ . '\init' ); /** * Class Autoloader @@ -143,7 +143,7 @@ function enable_buddypress_features() { require_once \dirname( __FILE__ ) . '/integration/class-buddypress.php'; Integration\Buddypress::init(); } -add_action( 'bp_include', '\Activitypub\enable_buddypress_features' ); +add_action( 'bp_include', __NAMESPACE__ . '\enable_buddypress_features' ); /** * `get_plugin_data` wrapper diff --git a/integration/class-buddypress.php b/integration/class-buddypress.php index 1087f47..e4ec40e 100644 --- a/integration/class-buddypress.php +++ b/integration/class-buddypress.php @@ -3,7 +3,7 @@ namespace Activitypub\Integration; class Buddypress { public static function init() { - \add_filter( 'activitypub_json_author_array', array( 'Activitypub\Integration\Buddypress', 'add_user_metadata' ), 11, 2 ); + \add_filter( 'activitypub_json_author_array', array( self::class, 'add_user_metadata' ), 11, 2 ); } public static function add_user_metadata( $object, $author_id ) { From 68002db291e8952fd3b79c24363333f87d564a49 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 22 May 2023 10:58:13 +0200 Subject: [PATCH 48/55] prevent sweeping of followers taxonomies thanks @akirk https://github.com/polylang/polylang/commit/b0db9db87ea4c765f85c4478454bc4b95ebf8917 --- activitypub.php | 25 ++++++++++--- includes/collection/class-followers.php | 19 ++++++++++ integration/class-wp-sweep.php | 50 +++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 integration/class-wp-sweep.php diff --git a/activitypub.php b/activitypub.php index a1cd6df..e19751f 100644 --- a/activitypub.php +++ b/activitypub.php @@ -27,6 +27,7 @@ function init() { \defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=

)|(?<=
)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' ); \defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' ); \defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "[ap_title]\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" ); + \define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); \define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) ); @@ -139,11 +140,25 @@ function plugin_settings_link( $actions ) { /** * Only load code that needs BuddyPress to run once BP is loaded and initialized. */ -function enable_buddypress_features() { - require_once \dirname( __FILE__ ) . '/integration/class-buddypress.php'; - Integration\Buddypress::init(); -} -add_action( 'bp_include', __NAMESPACE__ . '\enable_buddypress_features' ); +add_action( + 'bp_include', + function() { + require_once \dirname( __FILE__ ) . '/integration/class-buddypress.php'; + Integration\Buddypress::init(); + }, + 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/includes/collection/class-followers.php b/includes/collection/class-followers.php index 8a271f6..c7e55c2 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -338,6 +338,25 @@ class Followers { return $items; } + /** + * Get all Followers + * + * @param array $args The WP_Term_Query arguments. + * + * @return array The Term list of Followers. + */ + public static function get_all_followers( $args = array() ) { + $defaults = array( + 'taxonomy' => self::TAXONOMY, + 'hide_empty' => false, + ); + + $args = wp_parse_args( $args, $defaults ); + $terms = new WP_Term_Query( $args ); + + return $terms->get_terms(); + } + /** * Count the total number of followers * diff --git a/integration/class-wp-sweep.php b/integration/class-wp-sweep.php new file mode 100644 index 0000000..730edba --- /dev/null +++ b/integration/class-wp-sweep.php @@ -0,0 +1,50 @@ + 'ids' ) ); + + $excluded_term_ids = array_merge( $excluded_term_ids, $followers ); + + return array_unique( $excluded_term_ids ); + } +} From c1b644aee1f9059fba4714d402284755a94150b3 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 22 May 2023 11:33:02 +0200 Subject: [PATCH 49/55] Fix #339 --- README.md | 4 +++- readme.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d7a9662..e434e69 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ In order for webfinger to work, it must be mapped to the root directory of the U Add the following to the .htaccess file in the root directory: - RedirectMatch "^\/\.well-known(.*)$" "\/blog\/\.well-known$1" + RedirectMatch "^\/\.well-known/(webfinger|nodeinfo|x-nodeinfo2)(.*)$" "\/blog\/\.well-known$1" Where 'blog' is the path to the subdirectory at which your blog resides. @@ -115,6 +115,8 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu ### Next ### +* Compatibility: add a new conditional, `\Activitypub\is_activitypub_request()`, to allow third-party plugins to detect ActivityPub requests. +* Compatibility: add hooks to allow modifying images returned in ActivityPub requests. * Compatibility: indicate that the plugin is compatible and has been tested with the latest version of WordPress, 6.2. ### 0.17.0 ### diff --git a/readme.txt b/readme.txt index c08f455..85a02e8 100644 --- a/readme.txt +++ b/readme.txt @@ -94,7 +94,7 @@ In order for webfinger to work, it must be mapped to the root directory of the U Add the following to the .htaccess file in the root directory: - RedirectMatch "^\/\.well-known(.*)$" "\/blog\/\.well-known$1" + RedirectMatch "^\/\.well-known/(webfinger|nodeinfo|x-nodeinfo2)(.*)$" "\/blog\/\.well-known$1" Where 'blog' is the path to the subdirectory at which your blog resides. From 653b1f9fae989aa2ca3d3fff9569c83a44b076e1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 22 May 2023 13:38:44 +0200 Subject: [PATCH 50/55] added missing `$2` see #339 --- README.md | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e434e69..3a3a360 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ In order for webfinger to work, it must be mapped to the root directory of the U Add the following to the .htaccess file in the root directory: - RedirectMatch "^\/\.well-known/(webfinger|nodeinfo|x-nodeinfo2)(.*)$" "\/blog\/\.well-known$1" + RedirectMatch "^\/\.well-known/(webfinger|nodeinfo|x-nodeinfo2)(.*)$" "\/blog\/\.well-known$1$2" Where 'blog' is the path to the subdirectory at which your blog resides. diff --git a/readme.txt b/readme.txt index 85a02e8..0d2727b 100644 --- a/readme.txt +++ b/readme.txt @@ -94,7 +94,7 @@ In order for webfinger to work, it must be mapped to the root directory of the U Add the following to the .htaccess file in the root directory: - RedirectMatch "^\/\.well-known/(webfinger|nodeinfo|x-nodeinfo2)(.*)$" "\/blog\/\.well-known$1" + RedirectMatch "^\/\.well-known/(webfinger|nodeinfo|x-nodeinfo2)(.*)$" "\/blog\/\.well-known$1$2" Where 'blog' is the path to the subdirectory at which your blog resides. From 2c7f0687cc88c236defe00ede0eee7615ec44e6d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 22 May 2023 14:47:20 +0200 Subject: [PATCH 51/55] fix #271 thanks @janboddez --- templates/settings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/settings.php b/templates/settings.php index 2bfaed4..0daca17 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -120,8 +120,8 @@

  • - name, $support_post_types, true ) ); ?> /> - + name, $support_post_types, true ) ); ?> /> +
  • From 2aa7077ae750f3203cf5dcd6d6835aea314dd7fc Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 23 May 2023 12:26:02 +0200 Subject: [PATCH 52/55] add `wpautop` to user description fix #279 --- includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/functions.php b/includes/functions.php index 5bcf97a..2337a7c 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -205,7 +205,7 @@ function get_author_description( $user_id ) { if ( empty( $description ) ) { $description = get_user_meta( $user_id, 'description', true ); } - return $description; + return \wpautop( \wp_kses( $description, 'default' ) ); } /** From 2117f78106f075d647695bcb576f28f99bcbeb2f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 23 May 2023 12:28:57 +0200 Subject: [PATCH 53/55] fix #321 --- includes/model/class-post.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/includes/model/class-post.php b/includes/model/class-post.php index ff5ccb8..87d8dec 100644 --- a/includes/model/class-post.php +++ b/includes/model/class-post.php @@ -95,8 +95,13 @@ class Post { 'class' => array(), ), 'ul' => array(), - 'ol' => array(), - 'li' => array(), + 'ol' => array( + 'reversed' => array(), + 'start' => array(), + ), + 'li' => array( + 'value' => array(), + ), 'strong' => array( 'class' => array(), ), From a9b964a087fe33c1a5d2cd0f8823af696ab80937 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 23 May 2023 13:44:08 +0200 Subject: [PATCH 54/55] fix #237 --- templates/welcome.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/templates/welcome.php b/templates/welcome.php index 2b6a362..cb4d05f 100644 --- a/templates/welcome.php +++ b/templates/welcome.php @@ -50,6 +50,7 @@

    +

    -

    +

    + +

    + +

    From bc54598828105d6793ef0a72c54dcfe7a13cbc89 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 23 May 2023 19:10:30 +0200 Subject: [PATCH 55/55] Fix CI and added Calckey --- README.md | 7 ++++--- readme.txt | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3a3a360..8264b83 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,12 @@ The plugin works with the following tested federated platforms, but there may be * [Mastodon](https://joinmastodon.org/) * [Pleroma](https://pleroma.social/) -* [Friendica](https://friendi.ca/) -* [HubZilla](https://hubzilla.org/) +* [friendica](https://friendi.ca/) +* [Hubzilla](https://hubzilla.org/) * [Pixelfed](https://pixelfed.org/) -* [SocialHome](https://socialhome.network/) +* [Socialhome](https://socialhome.network/) * [Misskey](https://join.misskey.page/) +* [Calckey](https://calckey.org/) Here’s what that means and what you can expect. diff --git a/readme.txt b/readme.txt index 0d2727b..13ba977 100644 --- a/readme.txt +++ b/readme.txt @@ -20,11 +20,12 @@ The plugin works with the following tested federated platforms, but there may be * [Mastodon](https://joinmastodon.org/) * [Pleroma](https://pleroma.social/) -* [Friendica](https://friendi.ca/) -* [HubZilla](https://hubzilla.org/) +* [friendica](https://friendi.ca/) +* [Hubzilla](https://hubzilla.org/) * [Pixelfed](https://pixelfed.org/) -* [SocialHome](https://socialhome.network/) +* [Socialhome](https://socialhome.network/) * [Misskey](https://join.misskey.page/) +* [Calckey](https://calckey.org/) Here’s what that means and what you can expect.