From e469d9513ad3e59711aae8c834845729962f4730 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 16:27:13 +0000 Subject: [PATCH 01/17] Remove actor mode setting in favor of capability-based actors This commit removes the 'actor mode' option and all related code, migrating to a system where blog actors are always enabled and user actors are controlled solely via the 'activitypub' capability. Migration logic ensures sites previously in blog-only mode do not grant the capability to new users by default. All UI, constants, and logic related to actor mode selection have been removed or refactored, and related settings fields and filters are cleaned up for clarity and maintainability. --- includes/class-activitypub.php | 9 ++ includes/class-migration.php | 31 ++++++- includes/class-options.php | 24 ------ includes/class-scheduler.php | 5 -- includes/collection/class-actors.php | 38 +++------ includes/collection/class-followers.php | 4 - includes/collection/class-replies.php | 6 +- includes/constants.php | 5 -- includes/functions.php | 82 +++++-------------- includes/scheduler/class-actor.php | 44 ++++------ includes/transformer/class-post.php | 29 ++----- includes/wp-admin/class-admin.php | 11 +-- .../wp-admin/class-blog-settings-fields.php | 4 +- includes/wp-admin/class-settings-fields.php | 72 ---------------- includes/wp-admin/class-settings.php | 41 ++++------ includes/wp-admin/class-welcome-fields.php | 6 +- .../wp-admin/import/class-starter-kit.php | 6 +- 17 files changed, 116 insertions(+), 301 deletions(-) diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index 3289643688..e336dd7a37 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -155,9 +155,18 @@ public static function theme_compat() { /** * Add the 'activitypub' capability to users who can publish posts. * + * New users get the capability by default unless the site was previously in + * blog-only mode (indicated by activitypub_disable_users_by_default option). + * * @param int $user_id User ID. */ public static function user_register( $user_id ) { + // Check if site was previously in blog-only mode. + if ( \get_option( 'activitypub_disable_users_by_default' ) ) { + return; + } + + // Add capability to users who can publish posts. if ( \user_can( $user_id, 'publish_posts' ) ) { $user = \get_user_by( 'id', $user_id ); $user->add_cap( 'activitypub' ); diff --git a/includes/class-migration.php b/includes/class-migration.php index 639a51cc72..0b50f74723 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -211,6 +211,7 @@ public static function maybe_migrate() { } if ( \version_compare( $version_from_db, 'unreleased', '<' ) ) { + self::migrate_actor_mode_to_capabilities(); self::clean_up_inbox(); \wp_schedule_single_event( \time(), 'activitypub_migrate_avatar_to_remote_actors' ); } @@ -478,6 +479,30 @@ public static function migrate_to_4_7_2() { } } + /** + * Migrate from actor mode settings to capability-based system. + * + * Blog actors are now always enabled and user actors are controlled + * solely via the 'activitypub' capability. This migration handles sites that + * were previously in blog-only mode by setting a flag to prevent new users + * from automatically getting the activitypub capability. + */ + public static function migrate_actor_mode_to_capabilities() { + $actor_mode = \get_option( 'activitypub_actor_mode', 'actor' ); + + // If site was in blog-only mode, set flag to disable users by default. + if ( 'blog' === $actor_mode ) { + \update_option( 'activitypub_disable_users_by_default', true ); + } + + // Clean up old actor mode option. + \delete_option( 'activitypub_actor_mode' ); + + // Clean up legacy options if they still exist. + \delete_option( 'activitypub_enable_blog_user' ); + \delete_option( 'activitypub_enable_users' ); + } + /** * Update comment counts for posts in batches. * @@ -857,17 +882,17 @@ public static function migrate_actor_mode() { '1' === $blog_profile && '1' === $author_profiles ) { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); } elseif ( '1' === $blog_profile && '1' !== $author_profiles ) { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); } elseif ( '1' !== $blog_profile && '1' === $author_profiles ) { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); } } diff --git a/includes/class-options.php b/includes/class-options.php index b4823f8381..7b4a2503d2 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -16,7 +16,6 @@ class Options { * Initialize the options. */ public static function init() { - \add_filter( 'pre_option_activitypub_actor_mode', array( self::class, 'pre_option_activitypub_actor_mode' ) ); \add_filter( 'pre_option_activitypub_authorized_fetch', array( self::class, 'pre_option_activitypub_authorized_fetch' ) ); \add_filter( 'pre_option_activitypub_vary_header', array( self::class, 'pre_option_activitypub_vary_header' ) ); @@ -39,29 +38,6 @@ public static function delete() { $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'activitypub_%'" ); } - /** - * Pre-get option filter for the Actor-Mode. - * - * @param string|false $pre The pre-get option value. - * - * @return string|false The actor mode or false if it should not be filtered. - */ - public static function pre_option_activitypub_actor_mode( $pre ) { - if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) && ACTIVITYPUB_SINGLE_USER_MODE ) { - return ACTIVITYPUB_BLOG_MODE; - } - - if ( \defined( 'ACTIVITYPUB_DISABLE_USER' ) && ACTIVITYPUB_DISABLE_USER ) { - return ACTIVITYPUB_BLOG_MODE; - } - - if ( \defined( 'ACTIVITYPUB_DISABLE_BLOG_USER' ) && ACTIVITYPUB_DISABLE_BLOG_USER ) { - return ACTIVITYPUB_ACTOR_MODE; - } - - return $pre; - } - /** * Pre-get option filter for the Authorized Fetch. * diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index d00fc147d3..b1564e2714 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -550,11 +550,6 @@ public static function is_locked( $key ) { * @param int $content_visibility The content visibility. */ public static function schedule_announce_activity( $outbox_activity_id, $activity, $actor_id, $content_visibility ) { - // Only if we're in both Blog and User modes. - if ( ACTIVITYPUB_ACTOR_AND_BLOG_MODE !== \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { - return; - } - // Only if this isn't the Blog Actor. if ( Actors::BLOG_USER_ID === $actor_id ) { return; diff --git a/includes/collection/class-actors.php b/includes/collection/class-actors.php index d8e324cc3e..ef093aac02 100644 --- a/includes/collection/class-actors.php +++ b/includes/collection/class-actors.php @@ -12,7 +12,6 @@ use Activitypub\Model\Blog; use Activitypub\Model\User; -use function Activitypub\is_user_type_disabled; use function Activitypub\normalize_host; use function Activitypub\normalize_url; use function Activitypub\object_to_uri; @@ -104,19 +103,11 @@ public static function get_by_username( $username ) { * @return int|\WP_Error Actor id or WP_Error if not found. */ public static function get_id_by_username( $username ) { - // Check for blog user. + // Check for blog user (always enabled). if ( Blog::get_default_username() === $username || \get_option( 'activitypub_blog_identifier' ) === $username ) { - if ( is_user_type_disabled( 'blog' ) ) { - return new \WP_Error( - 'activitypub_user_not_found', - \__( 'Actor not found', 'activitypub' ), - array( 'status' => 404 ) - ); - } - return self::BLOG_USER_ID; } @@ -342,10 +333,6 @@ public static function get_id_by_various( $id ) { * @return Actor[] Array of User actor objects. */ public static function get_collection() { - if ( is_user_type_disabled( 'user' ) ) { - return array(); - } - $users = \get_users( array( 'capability__in' => array( 'activitypub' ), @@ -373,21 +360,16 @@ public static function get_collection() { * @return int[] Array of User and Blog actor IDs. */ public static function get_all_ids() { - $user_ids = array(); - - if ( ! is_user_type_disabled( 'user' ) ) { - $user_ids = \get_users( - array( - 'fields' => 'ID', - 'capability__in' => array( 'activitypub' ), - ) - ); - } + // Get all users with activitypub capability. + $user_ids = \get_users( + array( + 'fields' => 'ID', + 'capability__in' => array( 'activitypub' ), + ) + ); - // Also include the blog actor if active. - if ( ! is_user_type_disabled( 'blog' ) ) { - $user_ids[] = self::BLOG_USER_ID; - } + // Always include the blog actor (always enabled). + $user_ids[] = self::BLOG_USER_ID; return array_map( 'intval', $user_ids ); } diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 3dcf833557..445cff7fe2 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -426,10 +426,6 @@ public static function get_inboxes_for_activity( $json, $actor_id, $batch_size = public static function maybe_add_inboxes_of_blog_user( $json, $actor_id ) { \_deprecated_function( __METHOD__, '7.3.0' ); - // Only if we're in both Blog and User modes. - if ( ACTIVITYPUB_ACTOR_AND_BLOG_MODE !== \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { - return false; - } // Only if this isn't the Blog Actor. if ( Actors::BLOG_USER_ID === $actor_id ) { return false; diff --git a/includes/collection/class-replies.php b/includes/collection/class-replies.php index 698d22df86..721df8ea24 100644 --- a/includes/collection/class-replies.php +++ b/includes/collection/class-replies.php @@ -15,7 +15,6 @@ use function Activitypub\get_rest_url_by_path; use function Activitypub\is_local_comment; use function Activitypub\is_post_disabled; -use function Activitypub\is_user_type_disabled; /** * Class containing code for getting replies Collections and CollectionPages of posts and comments. @@ -171,10 +170,7 @@ public static function get_context_collection( $post_id ) { $author = Actors::get_by_id( $post->post_author ); if ( is_wp_error( $author ) ) { - if ( is_user_type_disabled( 'blog' ) ) { - return false; - } - + // Fallback to blog actor (always enabled). $author = new Blog(); } diff --git a/includes/constants.php b/includes/constants.php index 886a578657..be449f5db8 100644 --- a/includes/constants.php +++ b/includes/constants.php @@ -63,11 +63,6 @@ define( 'ACTIVITYPUB_DATE_TIME_RFC3339', 'Y-m-d\TH:i:s\Z' ); -// Define Actor-Modes for the plugin. -define( 'ACTIVITYPUB_ACTOR_MODE', 'actor' ); -define( 'ACTIVITYPUB_BLOG_MODE', 'blog' ); -define( 'ACTIVITYPUB_ACTOR_AND_BLOG_MODE', 'actor_blog' ); - // Post visibility constants. define( 'ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC', '' ); define( 'ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC', 'quiet_public' ); diff --git a/includes/functions.php b/includes/functions.php index 52a9bb099a..5d94d2a54e 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -368,7 +368,7 @@ function user_can_activitypub( $user_id ) { break; case Actors::BLOG_USER_ID: - $enabled = ! is_user_type_disabled( 'blog' ); + $enabled = true; // Blog user is always enabled. break; default: @@ -377,11 +377,7 @@ function user_can_activitypub( $user_id ) { break; } - if ( is_user_type_disabled( 'user' ) ) { - $enabled = false; - break; - } - + // Check only the capability. $enabled = \user_can( $user_id, 'activitypub' ); } @@ -400,64 +396,30 @@ function user_can_activitypub( $user_id ) { * This function is used to check if the 'blog' or 'user' * type is disabled for ActivityPub. * + * Note: As of version 4.6.0, blog actors are always enabled and user actors + * are controlled via the 'activitypub' capability. This function now always + * returns false but is maintained for backward compatibility and filter support. + * * @param string $type User type. 'blog' or 'user'. * - * @return boolean True if the user type is disabled, false otherwise. + * @return boolean Always returns false (no types are globally disabled). */ function is_user_type_disabled( $type ) { - switch ( $type ) { - case 'blog': - if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) ) { - if ( ACTIVITYPUB_SINGLE_USER_MODE ) { - $disabled = false; - break; - } - } - - if ( \defined( 'ACTIVITYPUB_DISABLE_BLOG_USER' ) ) { - $disabled = ACTIVITYPUB_DISABLE_BLOG_USER; - break; - } - - if ( ACTIVITYPUB_ACTOR_MODE === \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { - $disabled = true; - break; - } - - $disabled = false; - break; - case 'user': - if ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) ) { - if ( ACTIVITYPUB_SINGLE_USER_MODE ) { - $disabled = true; - break; - } - } - - if ( \defined( 'ACTIVITYPUB_DISABLE_USER' ) ) { - $disabled = ACTIVITYPUB_DISABLE_USER; - break; - } - - if ( ACTIVITYPUB_BLOG_MODE === \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { - $disabled = true; - break; - } + $disabled = false; - $disabled = false; - break; - default: - $disabled = new \WP_Error( - 'activitypub_wrong_user_type', - __( 'Wrong user type', 'activitypub' ), - array( 'status' => 400 ) - ); - break; + if ( ! in_array( $type, array( 'blog', 'user' ), true ) ) { + $disabled = new \WP_Error( + 'activitypub_wrong_user_type', + __( 'Wrong user type', 'activitypub' ), + array( 'status' => 400 ) + ); } /** * Allow plugins to disable user types for ActivityPub. * + * Note: This filter is deprecated. Use capability management instead. + * * @param boolean $disabled True if the user type is disabled, false otherwise. * @param string $type The User-Type. */ @@ -1311,16 +1273,12 @@ function get_content_warning( $post_id ) { * @return string|false The ActivityPub ID (a URL) of the User or false if not found. */ function get_user_id( $id ) { - $mode = \get_option( 'activitypub_actor_mode', 'default' ); + // Try to get the user actor first. + $user = Actors::get_by_id( $id ); - if ( ACTIVITYPUB_BLOG_MODE === $mode ) { + // Fallback to blog actor if user not found. + if ( \is_wp_error( $user ) ) { $user = Actors::get_by_id( Actors::BLOG_USER_ID ); - } else { - $user = Actors::get_by_id( $id ); - - if ( \is_wp_error( $user ) ) { - $user = Actors::get_by_id( Actors::BLOG_USER_ID ); - } } if ( \is_wp_error( $user ) ) { diff --git a/includes/scheduler/class-actor.php b/includes/scheduler/class-actor.php index b07cfbdae6..31140755fd 100644 --- a/includes/scheduler/class-actor.php +++ b/includes/scheduler/class-actor.php @@ -13,7 +13,6 @@ use Activitypub\Collection\Outbox; use function Activitypub\add_to_outbox; -use function Activitypub\is_user_type_disabled; /** * Post scheduler class. @@ -23,31 +22,24 @@ class Actor { * Initialize the class, registering WordPress hooks. */ public static function init() { - // Profile updates for blog options. - if ( ! is_user_type_disabled( 'blog' ) ) { - \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) ); - \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) ); - \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) ); - \add_action( 'add_option_activitypub_header_image', array( self::class, 'blog_user_update' ) ); - \add_action( 'update_option_activitypub_header_image', array( self::class, 'blog_user_update' ) ); - \add_action( 'add_option_activitypub_blog_identifier', array( self::class, 'blog_user_update' ) ); - \add_action( 'update_option_activitypub_blog_identifier', array( self::class, 'blog_user_update' ) ); - \add_action( 'add_option_activitypub_blog_description', array( self::class, 'blog_user_update' ) ); - \add_action( 'update_option_activitypub_blog_description', array( self::class, 'blog_user_update' ) ); - \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) ); - \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) ); - } - - // Profile updates for user options. - if ( ! is_user_type_disabled( 'user' ) ) { - \add_action( 'profile_update', array( self::class, 'user_update' ) ); - \add_action( 'added_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); - \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); - // @todo figure out a feasible way of updating the header image since it's not unique to any user. - } - - \add_action( 'add_option_activitypub_actor_mode', array( self::class, 'blog_user_update' ) ); - \add_action( 'update_option_activitypub_actor_mode', array( self::class, 'blog_user_update' ) ); + // Profile updates for blog options (always enabled). + \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) ); + \add_action( 'add_option_activitypub_header_image', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_activitypub_header_image', array( self::class, 'blog_user_update' ) ); + \add_action( 'add_option_activitypub_blog_identifier', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_activitypub_blog_identifier', array( self::class, 'blog_user_update' ) ); + \add_action( 'add_option_activitypub_blog_description', array( self::class, 'blog_user_update' ) ); + \add_action( 'update_option_activitypub_blog_description', array( self::class, 'blog_user_update' ) ); + \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) ); + \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) ); + + // Profile updates for user options (always enabled). + \add_action( 'profile_update', array( self::class, 'user_update' ) ); + \add_action( 'added_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); + \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); + // @todo figure out a feasible way of updating the header image since it's not unique to any user. \add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 ); diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 5c30cde606..543bdc4027 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -412,14 +412,9 @@ protected function get_type() { * @return string|null The audience. */ public function get_audience() { - $actor_mode = \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); - - if ( ACTIVITYPUB_ACTOR_AND_BLOG_MODE === $actor_mode ) { - $blog = new Blog(); - return $blog->get_id(); - } - - return null; + // Blog actor is always enabled, so posts always have the blog as audience. + $blog = new Blog(); + return $blog->get_id(); } /** @@ -1006,18 +1001,10 @@ private function get_public_interaction_policy() { * @return string|array The actor ID(s). */ private function get_self_interaction_policy() { - switch ( \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ) { - case ACTIVITYPUB_BLOG_MODE: - return ( new Blog() )->get_id(); - - case ACTIVITYPUB_ACTOR_AND_BLOG_MODE: - return array( - $this->get_actor_object()->get_id(), - ( new Blog() )->get_id(), - ); - - default: - return $this->get_actor_object()->get_id(); - } + // Both user and blog actors are always enabled. + return array( + $this->get_actor_object()->get_id(), + ( new Blog() )->get_id(), + ); } } diff --git a/includes/wp-admin/class-admin.php b/includes/wp-admin/class-admin.php index e3b4ca4cfd..494942a66a 100644 --- a/includes/wp-admin/class-admin.php +++ b/includes/wp-admin/class-admin.php @@ -16,7 +16,6 @@ use function Activitypub\count_followers; use function Activitypub\get_content_visibility; -use function Activitypub\is_user_type_disabled; use function Activitypub\site_supports_blocks; use function Activitypub\user_can_activitypub; use function Activitypub\was_comment_received; @@ -289,7 +288,6 @@ public static function enqueue_scripts( $hook_suffix ) { 'activitypubCommandPalette', array( 'followingEnabled' => '1' === \get_option( 'activitypub_following_ui', '0' ), - 'actorMode' => \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ), 'canManageOptions' => \current_user_can( 'manage_options' ), ) ); @@ -810,7 +808,7 @@ public static function dashboard_glance_items( $items ) { ); } - if ( ! is_user_type_disabled( 'blog' ) && current_user_can( 'manage_options' ) ) { + if ( current_user_can( 'manage_options' ) ) { $follower_count = sprintf( // translators: %s: number of followers. _n( @@ -942,12 +940,11 @@ function activitypub_open_help_tab(event) { */ public static function add_dashboard_widgets() { \wp_add_dashboard_widget( 'activitypub_blog', \__( 'ActivityPub Plugin News', 'activitypub' ), array( self::class, 'blog_dashboard_widget' ) ); - if ( user_can_activitypub( \get_current_user_id() ) && ! is_user_type_disabled( 'user' ) ) { + if ( user_can_activitypub( \get_current_user_id() ) ) { \wp_add_dashboard_widget( 'activitypub_profile', \__( 'ActivityPub Author profile', 'activitypub' ), array( self::class, 'profile_dashboard_widget' ) ); } - if ( ! is_user_type_disabled( 'blog' ) ) { - \wp_add_dashboard_widget( 'activitypub_blog_profile', \__( 'ActivityPub Blog profile', 'activitypub' ), array( self::class, 'blogprofile_dashboard_widget' ) ); - } + // Blog profile is always available. + \wp_add_dashboard_widget( 'activitypub_blog_profile', \__( 'ActivityPub Blog profile', 'activitypub' ), array( self::class, 'blogprofile_dashboard_widget' ) ); } /** diff --git a/includes/wp-admin/class-blog-settings-fields.php b/includes/wp-admin/class-blog-settings-fields.php index ee96d2841d..c3895eb117 100644 --- a/includes/wp-admin/class-blog-settings-fields.php +++ b/includes/wp-admin/class-blog-settings-fields.php @@ -26,8 +26,8 @@ public static function init() { * Register all settings fields. */ public static function register_settings() { - // If we're in blog mode, and we're on the blog profile tab, mark the profile setup step as done. - if ( isset( $_GET['tab'] ) && 'blog-profile' === \sanitize_key( $_GET['tab'] ) && ACTIVITYPUB_BLOG_MODE === \get_option( 'activitypub_actor_mode' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + // Mark the profile setup step as done when visiting the blog profile tab. + if ( isset( $_GET['tab'] ) && 'blog-profile' === \sanitize_key( $_GET['tab'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended \update_option( 'activitypub_checklist_profile_setup_visited', '1' ); } diff --git a/includes/wp-admin/class-settings-fields.php b/includes/wp-admin/class-settings-fields.php index 52dc2b52d0..af2d46f995 100644 --- a/includes/wp-admin/class-settings-fields.php +++ b/includes/wp-admin/class-settings-fields.php @@ -63,14 +63,6 @@ public static function register_settings_fields() { ); // Add settings fields. - add_settings_field( - 'activitypub_actor_mode', - __( 'Enable profiles by type', 'activitypub' ), - array( self::class, 'render_actor_mode_field' ), - 'activitypub_settings', - 'activitypub_profiles' - ); - $object_type = \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE ); if ( 'note' === $object_type ) { add_settings_field( @@ -161,70 +153,6 @@ public static function register_settings_fields() { ); } - /** - * Render actor mode field. - */ - public static function render_actor_mode_field() { - $disabled = ( \defined( 'ACTIVITYPUB_SINGLE_USER_MODE' ) && ACTIVITYPUB_SINGLE_USER_MODE ) || - ( \defined( 'ACTIVITYPUB_DISABLE_USER' ) && ACTIVITYPUB_DISABLE_USER ) || - ( \defined( 'ACTIVITYPUB_DISABLE_BLOG_USER' ) && ACTIVITYPUB_DISABLE_BLOG_USER ); - - if ( $disabled ) : - ?> -

- -

- -
-
- /> -
- -

- activitypub capability) gets their own ActivityPub profile.', 'activitypub' ), array( 'code' => array() ) ); ?> - - user settings.', 'activitypub' ), - admin_url( '/users.php' ) - ), - array( 'a' => array( 'href' => array() ) ) - ); - ?> - - array() ) ); ?> -

-
-
-
- /> -
- -

- -

-
-
-
- /> -
- -

- -

-
-
-
- 'integer', - 'description' => \__( 'Choose your preferred Actor-Mode.', 'activitypub' ), - 'default' => ACTIVITYPUB_ACTOR_MODE, - ) - ); - \register_setting( 'activitypub', 'activitypub_attribution_domains', @@ -395,22 +383,21 @@ public static function settings_page() { 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/blocked-actors-list.php', ); - if ( user_can_activitypub( Actors::BLOG_USER_ID ) ) { - $settings_tabs['blog-profile'] = array( - 'label' => __( 'Blog Profile', 'activitypub' ), - 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/blog-settings.php', - ); - $settings_tabs['followers'] = array( - 'label' => __( 'Followers', 'activitypub' ), - 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/followers-list.php', - ); + // Blog profile and followers tabs are always available. + $settings_tabs['blog-profile'] = array( + 'label' => __( 'Blog Profile', 'activitypub' ), + 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/blog-settings.php', + ); + $settings_tabs['followers'] = array( + 'label' => __( 'Followers', 'activitypub' ), + 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/followers-list.php', + ); - if ( '1' === \get_option( 'activitypub_following_ui', '0' ) ) { - $settings_tabs['following'] = array( - 'label' => __( 'Following', 'activitypub' ), - 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/following-list.php', - ); - } + if ( '1' === \get_option( 'activitypub_following_ui', '0' ) ) { + $settings_tabs['following'] = array( + 'label' => __( 'Following', 'activitypub' ), + 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/following-list.php', + ); } /** diff --git a/includes/wp-admin/class-welcome-fields.php b/includes/wp-admin/class-welcome-fields.php index 104593a841..15289c117f 100644 --- a/includes/wp-admin/class-welcome-fields.php +++ b/includes/wp-admin/class-welcome-fields.php @@ -353,14 +353,10 @@ public static function render_step_profile_setup() { - + - - diff --git a/includes/wp-admin/import/class-starter-kit.php b/includes/wp-admin/import/class-starter-kit.php index f71c2e72d4..ea727e3a76 100644 --- a/includes/wp-admin/import/class-starter-kit.php +++ b/includes/wp-admin/import/class-starter-kit.php @@ -8,7 +8,6 @@ namespace Activitypub\WP_Admin\Import; use function Activitypub\follow; -use function Activitypub\is_user_type_disabled; use function Activitypub\object_to_uri; /** @@ -304,10 +303,7 @@ public static function import_options() { * Setup blog user filter for dropdown. */ private static function setup_blog_user_filter() { - if ( is_user_type_disabled( 'blog' ) ) { - return; - } - + // Blog user is always available. self::$blog_user_filter_callback = function ( $users ) { return \preg_replace( '/<\/select>/', From b1bcb9201f7a0de74a4eee4523108830a2a4a1e2 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 16:36:04 +0000 Subject: [PATCH 02/17] Refactor tests to use string values for actor modes Replaces usage of ACTIVITYPUB_ACTOR_MODE, ACTIVITYPUB_BLOG_MODE, and ACTIVITYPUB_ACTOR_AND_BLOG_MODE constants with their string equivalents ('actor', 'blog', 'actor_blog') throughout PHPUnit tests. Updates test logic and data providers to match the new string-based configuration, and removes unnecessary option updates where blog actor is now always available. --- .../tests/includes/class-test-functions.php | 9 ++++--- .../tests/includes/class-test-mailer.php | 6 ----- .../tests/includes/class-test-migration.php | 12 +++++----- .../tests/includes/class-test-move.php | 4 ++-- .../tests/includes/class-test-query.php | 4 ++-- .../tests/includes/class-test-scheduler.php | 3 --- .../includes/collection/class-test-actors.php | 18 ++++---------- .../collection/class-test-followers.php | 14 +++++------ .../includes/collection/class-test-inbox.php | 2 +- .../includes/collection/class-test-outbox.php | 10 ++++---- .../collection/class-test-replies.php | 11 ++------- .../tests/includes/model/class-test-blog.php | 2 +- .../rest/class-test-followers-controller.php | 6 ++--- .../rest/class-test-following-controller.php | 6 ++--- .../rest/class-test-inbox-controller.php | 24 +++++++++---------- .../rest/class-test-outbox-controller.php | 8 +++---- .../includes/scheduler/class-test-actor.php | 4 ---- .../transformer/class-test-factory.php | 4 ++-- .../includes/transformer/class-test-post.php | 10 ++++---- 19 files changed, 63 insertions(+), 94 deletions(-) diff --git a/tests/phpunit/tests/includes/class-test-functions.php b/tests/phpunit/tests/includes/class-test-functions.php index 7fd00663a2..bcdf23b191 100644 --- a/tests/phpunit/tests/includes/class-test-functions.php +++ b/tests/phpunit/tests/includes/class-test-functions.php @@ -713,16 +713,15 @@ public function test_get_user_id() { $this->assertIsString( \Activitypub\get_user_id( $user->ID ) ); - \add_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); - + // User with capability should have ID. $this->assertIsString( \Activitypub\get_user_id( $user->ID ) ); + // Remove capability - user should fall back to blog. $user->remove_cap( 'activitypub' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); $this->assertIsString( \Activitypub\get_user_id( $user->ID ) ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); - $this->assertFalse( \Activitypub\get_user_id( $user->ID ) ); + // User without capability should return blog ID. + $this->assertIsString( \Activitypub\get_user_id( $user->ID ) ); } /** diff --git a/tests/phpunit/tests/includes/class-test-mailer.php b/tests/phpunit/tests/includes/class-test-mailer.php index b4aa68e86c..500e386ea8 100644 --- a/tests/phpunit/tests/includes/class-test-mailer.php +++ b/tests/phpunit/tests/includes/class-test-mailer.php @@ -650,7 +650,6 @@ public function test_mention_with_disabled_option() { public function test_blog_new_follower_with_disabled_option() { // Set blog option to false (0). update_option( 'activitypub_blog_user_mailer_new_follower', '0' ); - update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); $activity = array( 'type' => 'Follow', @@ -671,7 +670,6 @@ public function test_blog_new_follower_with_disabled_option() { // Clean up. remove_all_filters( 'wp_before_load_template' ); delete_option( 'activitypub_blog_user_mailer_new_follower' ); - delete_option( 'activitypub_actor_mode' ); } /** @@ -682,7 +680,6 @@ public function test_blog_new_follower_with_disabled_option() { public function test_blog_direct_message_with_disabled_option() { // Set blog option to false (0). update_option( 'activitypub_blog_user_mailer_new_dm', '0' ); - update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); $activity = array( 'actor' => 'https://example.com/author', @@ -706,7 +703,6 @@ public function test_blog_direct_message_with_disabled_option() { // Clean up. remove_all_filters( 'wp_before_load_template' ); delete_option( 'activitypub_blog_user_mailer_new_dm' ); - delete_option( 'activitypub_actor_mode' ); } /** @@ -717,7 +713,6 @@ public function test_blog_direct_message_with_disabled_option() { public function test_blog_mention_with_disabled_option() { // Set blog option to false (0). update_option( 'activitypub_blog_user_mailer_new_mention', '0' ); - update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); $activity = array( 'actor' => 'https://example.com/author', @@ -741,7 +736,6 @@ public function test_blog_mention_with_disabled_option() { // Clean up. remove_all_filters( 'wp_before_load_template' ); delete_option( 'activitypub_blog_user_mailer_new_mention' ); - delete_option( 'activitypub_actor_mode' ); } /** diff --git a/tests/phpunit/tests/includes/class-test-migration.php b/tests/phpunit/tests/includes/class-test-migration.php index cb3ab8159f..0c77d20008 100644 --- a/tests/phpunit/tests/includes/class-test-migration.php +++ b/tests/phpunit/tests/includes/class-test-migration.php @@ -132,7 +132,7 @@ public function test_migrate_actor_mode() { Migration::migrate_actor_mode(); - $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + $this->assertEquals( 'actor', \get_option( 'activitypub_actor_mode', 'actor' ) ); \update_option( 'activitypub_enable_blog_user', '0' ); \update_option( 'activitypub_enable_users', '1' ); @@ -140,7 +140,7 @@ public function test_migrate_actor_mode() { Migration::migrate_actor_mode(); - $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + $this->assertEquals( 'actor', \get_option( 'activitypub_actor_mode', 'actor' ) ); \update_option( 'activitypub_enable_blog_user', '1' ); \update_option( 'activitypub_enable_users', '1' ); @@ -148,7 +148,7 @@ public function test_migrate_actor_mode() { Migration::migrate_actor_mode(); - $this->assertEquals( ACTIVITYPUB_ACTOR_AND_BLOG_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + $this->assertEquals( 'actor_blog', \get_option( 'activitypub_actor_mode', 'actor' ) ); \update_option( 'activitypub_enable_blog_user', '1' ); \update_option( 'activitypub_enable_users', '0' ); @@ -156,7 +156,7 @@ public function test_migrate_actor_mode() { Migration::migrate_actor_mode(); - $this->assertEquals( ACTIVITYPUB_BLOG_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + $this->assertEquals( 'blog', \get_option( 'activitypub_actor_mode', 'actor' ) ); \delete_option( 'activitypub_enable_blog_user' ); \update_option( 'activitypub_enable_users', '0' ); @@ -164,7 +164,7 @@ public function test_migrate_actor_mode() { Migration::migrate_actor_mode(); - $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + $this->assertEquals( 'actor', \get_option( 'activitypub_actor_mode', 'actor' ) ); \update_option( 'activitypub_enable_blog_user', '0' ); \delete_option( 'activitypub_enable_users' ); @@ -172,7 +172,7 @@ public function test_migrate_actor_mode() { Migration::migrate_actor_mode(); - $this->assertEquals( ACTIVITYPUB_ACTOR_MODE, \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ) ); + $this->assertEquals( 'actor', \get_option( 'activitypub_actor_mode', 'actor' ) ); } /** diff --git a/tests/phpunit/tests/includes/class-test-move.php b/tests/phpunit/tests/includes/class-test-move.php index 3b09fcaa86..aa3dd9a22c 100644 --- a/tests/phpunit/tests/includes/class-test-move.php +++ b/tests/phpunit/tests/includes/class-test-move.php @@ -118,7 +118,7 @@ public function test_account_with_duplicate_moves() { */ public function test_account_with_blog_author_as_actor() { // Change user mode to blog author. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $from = Actors::get_by_id( Actors::BLOG_USER_ID )->get_id(); $to = 'https://newsite.com/user/0'; @@ -194,7 +194,7 @@ public function test_internally_activity_object_properties() { */ public function test_change_domain_with_valid_input() { // Enable blog actor. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $old_domain = home_url(); $new_domain = 'http://newdomain.com'; diff --git a/tests/phpunit/tests/includes/class-test-query.php b/tests/phpunit/tests/includes/class-test-query.php index 70d3eccc70..e63f8adada 100644 --- a/tests/phpunit/tests/includes/class-test-query.php +++ b/tests/phpunit/tests/includes/class-test-query.php @@ -525,7 +525,7 @@ public function test_maybe_get_stamp_invalid_author() { * @covers ::get_activitypub_object */ public function test_home_page_actor_mode() { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); $actor_queries = array(); @@ -562,7 +562,7 @@ public function test_home_page_actor_mode() { * @covers ::get_activitypub_object */ public function test_home_page_actor_and_blog_mode() { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $actor_queries = array(); // Track database queries using the 'query' filter. diff --git a/tests/phpunit/tests/includes/class-test-scheduler.php b/tests/phpunit/tests/includes/class-test-scheduler.php index d9f40bf292..0d5170940c 100644 --- a/tests/phpunit/tests/includes/class-test-scheduler.php +++ b/tests/phpunit/tests/includes/class-test-scheduler.php @@ -457,9 +457,6 @@ public function test_async_batch_with_invalid_callback() { * @covers ::schedule_announce_activity */ public function test_schedule_announce_activity() { - // Set the actor mode to both blog and user mode. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); - $activity = new Activity(); $activity->set_type( 'Create' ); $activity->set_id( 'https://example.com/test-id' ); diff --git a/tests/phpunit/tests/includes/collection/class-test-actors.php b/tests/phpunit/tests/includes/collection/class-test-actors.php index ce496a403d..289a40d53d 100644 --- a/tests/phpunit/tests/includes/collection/class-test-actors.php +++ b/tests/phpunit/tests/includes/collection/class-test-actors.php @@ -21,7 +21,6 @@ class Test_Actors extends \WP_UnitTestCase { public function set_up() { parent::set_up(); - update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); add_option( 'activitypub_blog_identifier', 'blog' ); add_user_meta( 1, 'activitypub_user_identifier', 'admin' ); } @@ -39,7 +38,6 @@ public function tear_down() { \delete_option( 'activitypub_blog_user_private_key' ); \delete_option( 'activitypub_application_user_public_key' ); \delete_option( 'activitypub_application_user_private_key' ); - \delete_option( 'activitypub_actor_mode' ); \delete_user_meta( 1, 'magic_sig_public_key' ); \delete_user_meta( 1, 'magic_sig_private_key' ); } @@ -135,20 +133,15 @@ public function test_get_type_by_id() { } /** - * Test if Actor mode will be respected properly + * Test that blog profile is always available. * - * @covers ::get_type_by_id + * @covers ::get_by_resource */ - public function test_disabled_blog_profile() { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); - + public function test_blog_profile_always_available() { $resource = 'http://example.org/@blog'; + // Blog profile should always be available now. $this->assertEquals( 'Activitypub\Model\Blog', get_class( Actors::get_by_resource( $resource ) ) ); - - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); - - $this->assertWPError( Actors::get_by_resource( $resource ) ); } /** @@ -202,9 +195,6 @@ public function test_signature_legacy() { $this->assertEquals( $key_pair['private_key'], $private_key ); // Check blog user. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); - \delete_option( 'activitypub_actor_mode' ); - $public_key = 'public key ' . Actors::BLOG_USER_ID; $private_key = 'private key ' . Actors::BLOG_USER_ID; diff --git a/tests/phpunit/tests/includes/collection/class-test-followers.php b/tests/phpunit/tests/includes/collection/class-test-followers.php index 801cb2ee26..3b39b0292d 100644 --- a/tests/phpunit/tests/includes/collection/class-test-followers.php +++ b/tests/phpunit/tests/includes/collection/class-test-followers.php @@ -541,42 +541,42 @@ public function test_get_all_followers() { public function data_maybe_add_inboxes_of_blog_user() { return array( 'actor mode' => array( - 'actor_mode' => ACTIVITYPUB_ACTOR_MODE, + 'actor_mode' => 'actor', 'json' => '{"type":"Update","id":"test"}', 'actor_id' => 123, 'expected' => false, 'message' => 'Should return false when not in blog and user mode.', ), 'blog actor' => array( - 'actor_mode' => ACTIVITYPUB_ACTOR_AND_BLOG_MODE, + 'actor_mode' => 'actor_blog', 'json' => '{"type":"Update","id":"test"}', 'actor_id' => Actors::BLOG_USER_ID, 'expected' => false, 'message' => 'Should return false when using blog actor.', ), 'create activity' => array( - 'actor_mode' => ACTIVITYPUB_ACTOR_AND_BLOG_MODE, + 'actor_mode' => 'actor_blog', 'json' => '{"type":"Create","id":"test"}', 'actor_id' => 123, 'expected' => false, 'message' => 'Should return false for non-Update/Delete activity types.', ), 'update activity' => array( - 'actor_mode' => ACTIVITYPUB_ACTOR_AND_BLOG_MODE, + 'actor_mode' => 'actor_blog', 'json' => '{"type":"Update","id":"test"}', 'actor_id' => 123, 'expected' => true, 'message' => 'Should return true for Update activity in dual mode.', ), 'delete activity' => array( - 'actor_mode' => ACTIVITYPUB_ACTOR_AND_BLOG_MODE, + 'actor_mode' => 'actor_blog', 'json' => '{"type":"Delete","id":"test"}', 'actor_id' => 123, 'expected' => true, 'message' => 'Should return true for Delete activity in dual mode.', ), 'invalid json' => array( - 'actor_mode' => ACTIVITYPUB_ACTOR_AND_BLOG_MODE, + 'actor_mode' => 'actor_blog', 'json' => 'invalid json', 'actor_id' => 123, 'expected' => false, @@ -650,7 +650,7 @@ public function test_get_inboxes_for_activity() { $this->assertCount( 1, $inboxes, 'Should retrieve exactly 1 inbox with batch size 1.' ); // Test with blog user in dual mode. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); Followers::add( Actors::BLOG_USER_ID, self::$actors['sally@example.org']['id'] ); $inboxes = Followers::get_inboxes_for_activity( diff --git a/tests/phpunit/tests/includes/collection/class-test-inbox.php b/tests/phpunit/tests/includes/collection/class-test-inbox.php index a135f33423..bdda098e55 100644 --- a/tests/phpunit/tests/includes/collection/class-test-inbox.php +++ b/tests/phpunit/tests/includes/collection/class-test-inbox.php @@ -24,7 +24,7 @@ class Test_Inbox extends \WP_UnitTestCase { public function set_up() { parent::set_up(); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); } /** diff --git a/tests/phpunit/tests/includes/collection/class-test-outbox.php b/tests/phpunit/tests/includes/collection/class-test-outbox.php index 9fd076c6a6..fa17dfea86 100644 --- a/tests/phpunit/tests/includes/collection/class-test-outbox.php +++ b/tests/phpunit/tests/includes/collection/class-test-outbox.php @@ -30,7 +30,7 @@ class Test_Outbox extends \Activitypub\Tests\ActivityPub_Outbox_TestCase { * @param string $json The JSON representation of the data. */ public function test_add( $data, $type, $user_id, $json ) { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $id = \Activitypub\add_to_outbox( $data, $type, $user_id ); @@ -203,10 +203,10 @@ public function test_author_fallbacks( $mode, $user_id, $expected_actor ) { */ public function author_object_provider() { return array( - array( ACTIVITYPUB_ACTOR_AND_BLOG_MODE, null, 'user' ), - array( ACTIVITYPUB_ACTOR_AND_BLOG_MODE, 90210, 'blog' ), - array( ACTIVITYPUB_BLOG_MODE, 90210, 'blog' ), - array( ACTIVITYPUB_ACTOR_MODE, 90210, false ), + array( 'actor_blog', null, 'user' ), + array( 'actor_blog', 90210, 'blog' ), + array( 'blog', 90210, 'blog' ), + array( 'actor', 90210, false ), ); } diff --git a/tests/phpunit/tests/includes/collection/class-test-replies.php b/tests/phpunit/tests/includes/collection/class-test-replies.php index c4a4ae78c1..65cee1cbdb 100644 --- a/tests/phpunit/tests/includes/collection/class-test-replies.php +++ b/tests/phpunit/tests/includes/collection/class-test-replies.php @@ -149,16 +149,9 @@ public function test_get_context_collection_disabled_author() { $context_post_id = self::factory()->post->create( array( 'post_author' => $user_id ) ); get_user_by( 'id', $user_id )->remove_cap( 'activitypub' ); - // Author disabled, Blog user disabled. - $this->assertFalse( Replies::get_context_collection( $context_post_id ) ); - - // Enable Blog user. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); - + // Author has no capability but blog actor is always available. $context = Replies::get_context_collection( $context_post_id ); - + $this->assertNotFalse( $context, 'Blog actor should always be available' ); $this->assertSame( \get_author_posts_url( Actors::BLOG_USER_ID ), $context['attributedTo'] ); - - \delete_option( 'activitypub_actor_mode' ); } } diff --git a/tests/phpunit/tests/includes/model/class-test-blog.php b/tests/phpunit/tests/includes/model/class-test-blog.php index 400fda7dcd..d30c07fd0c 100644 --- a/tests/phpunit/tests/includes/model/class-test-blog.php +++ b/tests/phpunit/tests/includes/model/class-test-blog.php @@ -24,7 +24,7 @@ public static function set_up_before_class() { parent::set_up_before_class(); // Enable blog actor. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); } /** diff --git a/tests/phpunit/tests/includes/rest/class-test-followers-controller.php b/tests/phpunit/tests/includes/rest/class-test-followers-controller.php index 1575a3c7e6..7ee79d9351 100644 --- a/tests/phpunit/tests/includes/rest/class-test-followers-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-followers-controller.php @@ -87,7 +87,7 @@ public function test_get_item_schema() { */ public function test_get_items() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $request = new \WP_REST_Request( 'GET', '/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/0/followers' ); $request->set_param( 'page', 1 ); @@ -121,7 +121,7 @@ public function test_get_items() { */ public function test_get_items_full_context() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $request = new \WP_REST_Request( 'GET', '/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/0/followers' ); $request->set_param( 'page', 1 ); @@ -146,7 +146,7 @@ public function test_get_items_full_context() { */ public function test_get_items_pagination() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $request = new \WP_REST_Request( 'GET', '/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/0/followers' ); $request->set_param( 'page', 2 ); diff --git a/tests/phpunit/tests/includes/rest/class-test-following-controller.php b/tests/phpunit/tests/includes/rest/class-test-following-controller.php index 65e809611d..d6023710ea 100644 --- a/tests/phpunit/tests/includes/rest/class-test-following-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-following-controller.php @@ -106,7 +106,7 @@ public function test_get_item_schema() { */ public function test_get_items() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $request = new \WP_REST_Request( 'GET', '/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/0/following' ); $request->set_param( 'page', 1 ); @@ -141,7 +141,7 @@ public function test_get_items() { */ public function test_get_items_full_context() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $request = new \WP_REST_Request( 'GET', '/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/0/following' ); $request->set_param( 'page', 1 ); @@ -166,7 +166,7 @@ public function test_get_items_full_context() { */ public function test_get_items_pagination() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $request = new \WP_REST_Request( 'GET', '/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/0/following' ); $request->set_param( 'page', 2 ); diff --git a/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php b/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php index e16bb23a49..b61dba0e6e 100644 --- a/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php @@ -197,7 +197,7 @@ public function test_get_item_schema() { public function test_create_item_with_blog_user() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -242,7 +242,7 @@ public function test_create_item_with_blog_user() { public function test_create_item_with_multiple_recipients() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -288,7 +288,7 @@ public function test_create_item_with_multiple_recipients() { public function test_create_item_with_multiple_recipients_and_invalid_recipient() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -334,7 +334,7 @@ public function test_create_item_with_multiple_recipients_and_invalid_recipient( public function test_create_item_with_multiple_recipients_and_inactive_recipient() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -357,7 +357,7 @@ public function test_create_item_with_multiple_recipients_and_inactive_recipient 'to' => array( $user_actor->get_id(), $blog_actor->get_id() ), ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); $request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' ); $request->set_header( 'Content-Type', 'application/activity+json' ); @@ -382,7 +382,7 @@ public function test_create_item_with_multiple_recipients_and_inactive_recipient public function test_create_item_with_different_activity_types() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $activity_types = array( 'Update', 'Delete', 'Follow', 'Accept', 'Reject', 'Announce', 'Like' ); @@ -556,7 +556,7 @@ public function test_get_local_recipients_with_malformed_urls() { */ public function test_get_local_recipients_public_activity() { // Enable actor mode to allow user actors. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); // Create additional test users (authors have activitypub capability by default). $user_id_1 = self::factory()->user->create( array( 'role' => 'author' ) ); @@ -637,7 +637,7 @@ function ( $pre, $url ) use ( $remote_actor_url ) { */ public function test_get_local_recipients_public_activity_in_cc() { // Enable actor mode to allow user actors. - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); // Create a test user (authors have activitypub capability by default). $user_id = self::factory()->user->create( array( 'role' => 'author' ) ); @@ -710,7 +710,7 @@ function ( $pre, $url ) use ( $remote_actor_url ) { */ public function test_shared_inbox_context_parameter() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -762,7 +762,7 @@ function ( $data, $user_ids, $type, $activity, $context ) use ( &$captured_conte */ public function test_shared_inbox_action_hook_fires() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -820,7 +820,7 @@ function ( $data, $user_ids ) use ( &$shared_inbox_fired, &$captured_recipients */ public function test_inbox_persistence_with_shared_inbox() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); @@ -881,7 +881,7 @@ function ( $data, $user_ids, $type, $activity, $item_id ) use ( &$inbox_id ) { */ public function test_regular_inbox_action_with_shared_inbox_context() { \add_filter( 'activitypub_defer_signature_verification', '__return_true' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id ); $blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID ); diff --git a/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php b/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php index 88b871d64d..85e707fee2 100644 --- a/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php @@ -77,18 +77,18 @@ public function test_register_routes() { */ public function test_validate_user_id() { $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); $controller = new Outbox_Controller(); $this->assertTrue( $controller->validate_user_id( 0 ) ); $this->assertTrue( $controller->validate_user_id( '1' ) ); $this->assertWPError( $controller->validate_user_id( 'user-1' ) ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \update_option( 'activitypub_actor_mode', 'actor' ); $this->assertWPError( $controller->validate_user_id( 0 ) ); $this->assertTrue( $controller->validate_user_id( 1 ) ); - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'blog' ); $this->assertTrue( $controller->validate_user_id( '0' ) ); $this->assertWPError( $controller->validate_user_id( 1 ) ); @@ -500,7 +500,7 @@ public function test_get_items_content_visibility( $visibility, $public_visible, * @covers ::get_items */ public function test_get_items_actor_type_filtering() { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \update_option( 'activitypub_actor_mode', 'actor_blog' ); // Create a post with blog actor type. $blog_post_id = self::factory()->post->create( diff --git a/tests/phpunit/tests/includes/scheduler/class-test-actor.php b/tests/phpunit/tests/includes/scheduler/class-test-actor.php index e9245b0161..55229b2d53 100644 --- a/tests/phpunit/tests/includes/scheduler/class-test-actor.php +++ b/tests/phpunit/tests/includes/scheduler/class-test-actor.php @@ -132,7 +132,6 @@ public function test_user_update() { * @covers ::blog_user_update */ public function test_blog_user_update() { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); $test_value = 'test value'; $result = \Activitypub\Scheduler\Actor::blog_user_update( $test_value ); @@ -165,7 +164,6 @@ public function blog_user_images_provider() { * @param string $option Option to test. */ public function test_blog_user_image_updates( $field, $option ) { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); Actor::init(); $attachment_id = self::factory()->attachment->create_upload_object( AP_TESTS_DIR . '/data/assets/test.jpg' ); @@ -209,7 +207,6 @@ public function blog_user_text_provider() { * @param string $value Value to test. */ public function test_blog_user_text_updates( $field, $option, $value ) { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); Actor::init(); \update_option( $option, $value ); @@ -287,7 +284,6 @@ public function test_schedule_post_activity_extra_fields() { * @covers ::schedule_post_activity */ public function test_schedule_post_activity_extra_field_blog() { - \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); $blog_post_id = self::factory()->post->create( array( 'post_type' => Extra_Fields::BLOG_POST_TYPE ) ); $activitpub_id = Actors::get_by_id( Actors::BLOG_USER_ID )->get_id(); diff --git a/tests/phpunit/tests/includes/transformer/class-test-factory.php b/tests/phpunit/tests/includes/transformer/class-test-factory.php index c45be26722..f4aea0d3c9 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-factory.php +++ b/tests/phpunit/tests/includes/transformer/class-test-factory.php @@ -114,14 +114,14 @@ public function test_get_transformer_post() { $this->assertInstanceOf( \WP_Error::class, $transformer ); - \add_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); + \add_option( 'activitypub_actor_mode', 'actor_blog' ); $post = get_post( self::$post_id ); $transformer = Factory::get_transformer( $post ); $this->assertInstanceOf( Post::class, $transformer ); - \add_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); + \add_option( 'activitypub_actor_mode', 'actor' ); $post = get_post( self::$post_id ); $transformer = Factory::get_transformer( $post ); diff --git a/tests/phpunit/tests/includes/transformer/class-test-post.php b/tests/phpunit/tests/includes/transformer/class-test-post.php index 5b5418fee5..f54ce8d733 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-post.php +++ b/tests/phpunit/tests/includes/transformer/class-test-post.php @@ -977,14 +977,14 @@ public function test_get_interaction_policy_followers() { * * @covers ::get_interaction_policy */ - public function test_get_interaction_policy_me_actor_modes() { + public function test_get_interaction_policy_me() { $post = $this->create_test_post(); update_post_meta( $post->ID, 'activitypub_interaction_policy_quote', ACTIVITYPUB_INTERACTION_POLICY_ME ); $actor_modes = array( - ACTIVITYPUB_ACTOR_MODE, - ACTIVITYPUB_BLOG_MODE, - ACTIVITYPUB_ACTOR_AND_BLOG_MODE, + 'actor', + 'blog', + 'actor_blog', ); foreach ( $actor_modes as $mode ) { @@ -997,7 +997,7 @@ public function test_get_interaction_policy_me_actor_modes() { $this->assertArrayHasKey( 'automaticApproval', $policy['canQuote'] ); $auto = $policy['canQuote']['automaticApproval']; - if ( ACTIVITYPUB_ACTOR_AND_BLOG_MODE === $mode ) { + if ( 'actor_blog' === $mode ) { $this->assertIsArray( $auto, 'Actor+Blog mode should return an array of IDs.' ); $this->assertCount( 2, $auto, 'Actor+Blog mode should supply two IDs.' ); } else { From 866cd574deacc041e1d053cae0839a921069a863 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 16:37:12 +0000 Subject: [PATCH 03/17] Remove unused 'Profiles' settings section Deleted the registration of the 'activitypub_profiles' settings section from the settings fields as it is no longer needed. --- includes/wp-admin/class-settings-fields.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/includes/wp-admin/class-settings-fields.php b/includes/wp-admin/class-settings-fields.php index af2d46f995..07706f9bc6 100644 --- a/includes/wp-admin/class-settings-fields.php +++ b/includes/wp-admin/class-settings-fields.php @@ -27,13 +27,6 @@ public static function init() { */ public static function register_settings_fields() { // Add settings sections. - add_settings_section( - 'activitypub_profiles', - __( 'Profiles', 'activitypub' ), - '__return_empty_string', - 'activitypub_settings' - ); - add_settings_section( 'activitypub_activities', __( 'Activities', 'activitypub' ), From 2ecd9c7c73df84396b36d596fa52a2ce2c95d191 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 16:49:59 +0000 Subject: [PATCH 04/17] Update tests for removal of actor mode and blog actor fallback Refactor and update PHPUnit tests to reflect the removal of the 'actor mode' option. Tests now expect invalid or missing user actors to always fall back to the blog actor, and assertions have been updated accordingly. Deprecated or removed code paths related to actor mode have been eliminated from test data providers and test logic. --- .../tests/includes/class-test-functions.php | 10 +-- .../tests/includes/class-test-query.php | 22 +++--- .../collection/class-test-followers.php | 68 ++++++++----------- .../includes/collection/class-test-outbox.php | 12 ++-- .../rest/class-test-inbox-controller.php | 16 ++--- .../includes/transformer/class-test-base.php | 12 +++- .../transformer/class-test-factory.php | 16 +---- .../includes/transformer/class-test-post.php | 35 +++------- .../integration/class-test-webfinger.php | 7 +- 9 files changed, 83 insertions(+), 115 deletions(-) diff --git a/tests/phpunit/tests/includes/class-test-functions.php b/tests/phpunit/tests/includes/class-test-functions.php index bcdf23b191..c0b0c4fd4e 100644 --- a/tests/phpunit/tests/includes/class-test-functions.php +++ b/tests/phpunit/tests/includes/class-test-functions.php @@ -706,7 +706,8 @@ public function get_post_summary_data() { * @covers \Activitypub\get_user_id */ public function test_get_user_id() { - $this->assertFalse( \Activitypub\get_user_id( 90210 ) ); + // Invalid user ID should fall back to blog actor (always available). + $this->assertIsString( \Activitypub\get_user_id( 90210 ) ); $user = self::factory()->user->create_and_get(); $user->add_cap( 'activitypub' ); @@ -718,10 +719,9 @@ public function test_get_user_id() { // Remove capability - user should fall back to blog. $user->remove_cap( 'activitypub' ); - $this->assertIsString( \Activitypub\get_user_id( $user->ID ) ); - - // User without capability should return blog ID. - $this->assertIsString( \Activitypub\get_user_id( $user->ID ) ); + $blog_id = \Activitypub\get_user_id( $user->ID ); + $this->assertIsString( $blog_id ); + $this->assertStringContainsString( 'author=0', $blog_id, 'Should return blog actor ID' ); } /** diff --git a/tests/phpunit/tests/includes/class-test-query.php b/tests/phpunit/tests/includes/class-test-query.php index e63f8adada..b3c56687be 100644 --- a/tests/phpunit/tests/includes/class-test-query.php +++ b/tests/phpunit/tests/includes/class-test-query.php @@ -485,16 +485,17 @@ public function test_maybe_get_stamp_invalid_meta() { } /** - * Test maybe_get_stamp with invalid post author. + * Test maybe_get_stamp with author fallback to blog actor. + * With actor mode removal, all posts have an actor (user or blog). * * @covers ::maybe_get_stamp */ - public function test_maybe_get_stamp_invalid_author() { - // Create a post with invalid author. + public function test_maybe_get_stamp_with_blog_actor_fallback() { + // Create a post with an invalid author that will fall back to blog actor. $post_id = self::factory()->post->create( array( 'post_author' => 999999, // Non-existent user ID. - 'post_title' => 'Test Post Invalid Author', + 'post_title' => 'Test Post with Blog Fallback', 'post_content' => 'Test Content', 'post_status' => 'publish', ) @@ -513,20 +514,21 @@ public function test_maybe_get_stamp_invalid_author() { $query = Query::get_instance(); $result = $method->invoke( $query ); - $this->assertFalse( $result, 'Should return false for invalid post author' ); + // Should successfully fall back to blog actor and return a stamp object. + $this->assertNotFalse( $result, 'Should fall back to blog actor and return valid stamp' ); + $this->assertIsObject( $result, 'Stamp should be an object' ); // Clean up. \wp_delete_post( $post_id, true ); } /** - * Test get_activitypub_object method for home page in Actor mode. + * Test get_activitypub_object method for home page. + * Blog actor is now always available. * * @covers ::get_activitypub_object */ - public function test_home_page_actor_mode() { - \update_option( 'activitypub_actor_mode', 'actor' ); - + public function test_home_page_blog_actor() { $actor_queries = array(); // Track database queries using the 'query' filter. @@ -550,7 +552,7 @@ public function test_home_page_actor_mode() { $message .= ' Found queries: ' . wp_json_encode( $actor_queries ); } - $this->assertNull( $object, 'Home page should return null, because the Blog user is disabled.' ); + $this->assertInstanceOf( 'Activitypub\Model\Blog', $object, 'Home page should return Blog actor (always available).' ); $this->assertEmpty( $actor_queries, $message ); \delete_option( 'activitypub_actor_mode' ); diff --git a/tests/phpunit/tests/includes/collection/class-test-followers.php b/tests/phpunit/tests/includes/collection/class-test-followers.php index 3b39b0292d..deb688fd23 100644 --- a/tests/phpunit/tests/includes/collection/class-test-followers.php +++ b/tests/phpunit/tests/includes/collection/class-test-followers.php @@ -540,47 +540,41 @@ public function test_get_all_followers() { */ public function data_maybe_add_inboxes_of_blog_user() { return array( - 'actor mode' => array( - 'actor_mode' => 'actor', - 'json' => '{"type":"Update","id":"test"}', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false when not in blog and user mode.', + 'regular user' => array( + 'json' => '{"type":"Update","id":"test"}', + 'actor_id' => 123, + 'expected' => false, + 'message' => 'Should always return false (deprecated function).', ), 'blog actor' => array( - 'actor_mode' => 'actor_blog', - 'json' => '{"type":"Update","id":"test"}', - 'actor_id' => Actors::BLOG_USER_ID, - 'expected' => false, - 'message' => 'Should return false when using blog actor.', + 'json' => '{"type":"Update","id":"test"}', + 'actor_id' => Actors::BLOG_USER_ID, + 'expected' => false, + 'message' => 'Should return false when using blog actor.', ), 'create activity' => array( - 'actor_mode' => 'actor_blog', - 'json' => '{"type":"Create","id":"test"}', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false for non-Update/Delete activity types.', + 'json' => '{"type":"Create","id":"test"}', + 'actor_id' => 123, + 'expected' => false, + 'message' => 'Should return false for non-Update/Delete activity types.', ), 'update activity' => array( - 'actor_mode' => 'actor_blog', - 'json' => '{"type":"Update","id":"test"}', - 'actor_id' => 123, - 'expected' => true, - 'message' => 'Should return true for Update activity in dual mode.', + 'json' => '{"type":"Update","id":"test"}', + 'actor_id' => 123, + 'expected' => false, + 'message' => 'Should return false (deprecated function, actor mode removed).', ), 'delete activity' => array( - 'actor_mode' => 'actor_blog', - 'json' => '{"type":"Delete","id":"test"}', - 'actor_id' => 123, - 'expected' => true, - 'message' => 'Should return true for Delete activity in dual mode.', + 'json' => '{"type":"Delete","id":"test"}', + 'actor_id' => 123, + 'expected' => false, + 'message' => 'Should return false (deprecated function, actor mode removed).', ), 'invalid json' => array( - 'actor_mode' => 'actor_blog', - 'json' => 'invalid json', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false for invalid JSON.', + 'json' => 'invalid json', + 'actor_id' => 123, + 'expected' => false, + 'message' => 'Should return false for invalid JSON.', ), ); } @@ -593,14 +587,12 @@ public function data_maybe_add_inboxes_of_blog_user() { * * @expectedDeprecated Activitypub\Collection\Followers::maybe_add_inboxes_of_blog_user * - * @param string $actor_mode The actor mode to test with. - * @param string $json The JSON to test with. - * @param int $actor_id The actor ID to test with. - * @param boolean $expected The expected result. - * @param string $message The assertion message. + * @param string $json The JSON to test with. + * @param int $actor_id The actor ID to test with. + * @param boolean $expected The expected result. + * @param string $message The assertion message. */ - public function test_maybe_add_inboxes_of_blog_user( $actor_mode, $json, $actor_id, $expected, $message ) { - update_option( 'activitypub_actor_mode', $actor_mode ); + public function test_maybe_add_inboxes_of_blog_user( $json, $actor_id, $expected, $message ) { $this->assertSame( $expected, Followers::maybe_add_inboxes_of_blog_user( $json, $actor_id ), diff --git a/tests/phpunit/tests/includes/collection/class-test-outbox.php b/tests/phpunit/tests/includes/collection/class-test-outbox.php index fa17dfea86..8a3eebe462 100644 --- a/tests/phpunit/tests/includes/collection/class-test-outbox.php +++ b/tests/phpunit/tests/includes/collection/class-test-outbox.php @@ -173,17 +173,15 @@ public function activity_object_provider() { /** * Test add an item to the outbox with a user. + * With actor mode removal, invalid users always fall back to blog. * * @covers ::add * @dataProvider author_object_provider * - * @param string $mode The actor mode. * @param int $user_id The user ID. * @param string $expected_actor The expected actor. */ - public function test_author_fallbacks( $mode, $user_id, $expected_actor ) { - \update_option( 'activitypub_actor_mode', $mode ); - + public function test_author_fallbacks( $user_id, $expected_actor ) { $user_id = $user_id ?? self::$user_id; $data = array( '@context' => 'https://www.w3.org/ns/activitystreams', @@ -203,10 +201,8 @@ public function test_author_fallbacks( $mode, $user_id, $expected_actor ) { */ public function author_object_provider() { return array( - array( 'actor_blog', null, 'user' ), - array( 'actor_blog', 90210, 'blog' ), - array( 'blog', 90210, 'blog' ), - array( 'actor', 90210, false ), + array( null, 'user' ), + array( 90210, 'blog' ), // Invalid user falls back to blog. ); } diff --git a/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php b/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php index b61dba0e6e..76b87d637e 100644 --- a/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-inbox-controller.php @@ -555,9 +555,6 @@ public function test_get_local_recipients_with_malformed_urls() { * @covers ::get_local_recipients */ public function test_get_local_recipients_public_activity() { - // Enable actor mode to allow user actors. - \update_option( 'activitypub_actor_mode', 'actor' ); - // Create additional test users (authors have activitypub capability by default). $user_id_1 = self::factory()->user->create( array( 'role' => 'author' ) ); $user_id_2 = self::factory()->user->create( array( 'role' => 'author' ) ); @@ -617,16 +614,15 @@ function ( $pre, $url ) use ( $remote_actor_url ) { $this->assertContains( $user_id_3, $result, 'Should contain user 3' ); // Verify it returns exactly the followers we added. - // Note: May include blog user (0) if blog mode is enabled. + // Note: Blog user (0) is now always included. $this->assertGreaterThanOrEqual( 4, count( $result ), 'Should return at least 4 followers' ); - $this->assertLessThanOrEqual( 5, count( $result ), 'Should return at most 5 followers (4 users + optional blog)' ); + $this->assertLessThanOrEqual( 6, count( $result ), 'Should return at most 6 followers (4 users + blog + application)' ); // Clean up. \wp_delete_post( $remote_actor->ID, true ); \wp_delete_user( $user_id_1 ); \wp_delete_user( $user_id_2 ); \wp_delete_user( $user_id_3 ); - \delete_option( 'activitypub_actor_mode' ); \remove_all_filters( 'activitypub_pre_http_get_remote_object' ); } @@ -636,9 +632,6 @@ function ( $pre, $url ) use ( $remote_actor_url ) { * @covers ::get_local_recipients */ public function test_get_local_recipients_public_activity_in_cc() { - // Enable actor mode to allow user actors. - \update_option( 'activitypub_actor_mode', 'actor' ); - // Create a test user (authors have activitypub capability by default). $user_id = self::factory()->user->create( array( 'role' => 'author' ) ); @@ -692,14 +685,13 @@ function ( $pre, $url ) use ( $remote_actor_url ) { $this->assertContains( $user_id, $result, 'Should contain new test user' ); // Verify it returns exactly the followers we added. - // Note: May include blog user (0) if blog mode is enabled. + // Note: Blog user (0) is now always included. $this->assertGreaterThanOrEqual( 2, count( $result ), 'Should return at least 2 followers' ); - $this->assertLessThanOrEqual( 3, count( $result ), 'Should return at most 3 followers (2 users + optional blog)' ); + $this->assertLessThanOrEqual( 4, count( $result ), 'Should return at most 4 followers (2 users + blog + application)' ); // Clean up. \wp_delete_post( $remote_actor->ID, true ); \wp_delete_user( $user_id ); - \delete_option( 'activitypub_actor_mode' ); \remove_all_filters( 'activitypub_pre_http_get_remote_object' ); } diff --git a/tests/phpunit/tests/includes/transformer/class-test-base.php b/tests/phpunit/tests/includes/transformer/class-test-base.php index af0c236cbc..8bce78636d 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-base.php +++ b/tests/phpunit/tests/includes/transformer/class-test-base.php @@ -128,7 +128,17 @@ function ( $k ) { $transformed_object = $method->invoke( $transformer, new Generic_Object() ); $this->assertEquals( $expected_audience['to'], $transformed_object->get_to() ); - $this->assertEquals( $expected_audience['cc'], $transformed_object->get_cc() ); + + // For CC field, check that it contains at least the expected items + // (blog followers URL will also be present for public posts) + $actual_cc = $transformed_object->get_cc(); + if ( $expected_audience['cc'] !== null ) { + foreach ( $expected_audience['cc'] as $expected_item ) { + $this->assertContains( $expected_item, $actual_cc, "CC should contain $expected_item" ); + } + } else { + $this->assertNull( $actual_cc, 'CC should be null' ); + } \wp_delete_post( $post_id ); \remove_filter( 'activitypub_pre_http_get_remote_object', $function ); diff --git a/tests/phpunit/tests/includes/transformer/class-test-factory.php b/tests/phpunit/tests/includes/transformer/class-test-factory.php index f4aea0d3c9..2ec04fae0b 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-factory.php +++ b/tests/phpunit/tests/includes/transformer/class-test-factory.php @@ -105,6 +105,7 @@ public function test_get_transformer_invalid_input() { /** * Test get_transformer with post. + * With actor mode removal, posts always have an actor (user or blog). * * @covers ::get_transformer */ @@ -112,20 +113,7 @@ public function test_get_transformer_post() { $post = get_post( self::$post_id ); $transformer = Factory::get_transformer( $post ); - $this->assertInstanceOf( \WP_Error::class, $transformer ); - - \add_option( 'activitypub_actor_mode', 'actor_blog' ); - - $post = get_post( self::$post_id ); - $transformer = Factory::get_transformer( $post ); - - $this->assertInstanceOf( Post::class, $transformer ); - - \add_option( 'activitypub_actor_mode', 'actor' ); - - $post = get_post( self::$post_id ); - $transformer = Factory::get_transformer( $post ); - + // Should always return a Post transformer now (blog actor fallback available). $this->assertInstanceOf( Post::class, $transformer ); } diff --git a/tests/phpunit/tests/includes/transformer/class-test-post.php b/tests/phpunit/tests/includes/transformer/class-test-post.php index f54ce8d733..86a943fd3e 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-post.php +++ b/tests/phpunit/tests/includes/transformer/class-test-post.php @@ -981,34 +981,19 @@ public function test_get_interaction_policy_me() { $post = $this->create_test_post(); update_post_meta( $post->ID, 'activitypub_interaction_policy_quote', ACTIVITYPUB_INTERACTION_POLICY_ME ); - $actor_modes = array( - 'actor', - 'blog', - 'actor_blog', - ); + $transformer = new Post( get_post( $post->ID ) ); + $policy = $transformer->get_interaction_policy(); - foreach ( $actor_modes as $mode ) { - update_option( 'activitypub_actor_mode', $mode ); - $transformer = new Post( get_post( $post->ID ) ); // fresh instance. - $policy = $transformer->get_interaction_policy(); - - $this->assertIsArray( $policy, 'Policy should be array for mode ' . $mode ); - $this->assertArrayHasKey( 'canQuote', $policy ); - $this->assertArrayHasKey( 'automaticApproval', $policy['canQuote'] ); - - $auto = $policy['canQuote']['automaticApproval']; - if ( 'actor_blog' === $mode ) { - $this->assertIsArray( $auto, 'Actor+Blog mode should return an array of IDs.' ); - $this->assertCount( 2, $auto, 'Actor+Blog mode should supply two IDs.' ); - } else { - $this->assertIsString( $auto, 'Single mode should return a single ID string.' ); - } - } + $this->assertIsArray( $policy, 'Policy should be array' ); + $this->assertArrayHasKey( 'canQuote', $policy ); + $this->assertArrayHasKey( 'automaticApproval', $policy['canQuote'] ); - // Cleanup. - delete_option( 'activitypub_actor_mode' ); - } + $auto = $policy['canQuote']['automaticApproval']; + $this->assertIsArray( $auto, 'Should return an array of IDs (both user and blog).' ); + $this->assertCount( 2, $auto, 'Should supply two IDs (user and blog).' ); + $this->delete_test_post( $post->ID ); + } /** * Ensure invalid permission values fall back to 'anyone' policy. * diff --git a/tests/phpunit/tests/integration/class-test-webfinger.php b/tests/phpunit/tests/integration/class-test-webfinger.php index a7a957385d..febc451674 100644 --- a/tests/phpunit/tests/integration/class-test-webfinger.php +++ b/tests/phpunit/tests/integration/class-test-webfinger.php @@ -136,6 +136,7 @@ public function test_add_user_discovery() { /** * Test add_user_discovery with invalid user. + * With actor mode removal, invalid users fall back to blog actor. * * @covers ::add_user_discovery */ @@ -153,8 +154,10 @@ public function test_add_user_discovery_with_invalid_user() { $result = Webfinger::add_user_discovery( $initial_jrd, 'acct:invalid@example.com', $user ); - // Should return original jrd unchanged. - $this->assertEquals( $initial_jrd, $result ); + // Should fall back to blog actor instead of returning unchanged. + $this->assertNotEquals( $initial_jrd, $result, 'Should add blog actor info for invalid user' ); + $this->assertStringContainsString( 'localhost', $result['subject'], 'Subject should be blog actor' ); + $this->assertNotEmpty( $result['aliases'], 'Should have aliases for blog actor' ); } /** From 5e4d89b3e05bcce5700712270e9206b416bb0500 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 17:05:08 +0000 Subject: [PATCH 05/17] Remove actor mode logic and update related tests Deprecated and simplified the Followers::maybe_add_inboxes_of_blog_user method to always return false, reflecting the removal of actor mode. Updated scheduler, outbox, dispatcher, query, and transformer tests to align with the new behavior, ensuring both blog and user actors are always available and adjusting assertions and helper methods accordingly. --- includes/collection/class-followers.php | 18 +++-------- includes/scheduler/class-post.php | 10 +++--- .../class-activitypub-outbox-testcase.php | 32 ++++++++++++------- .../tests/includes/class-test-dispatcher.php | 6 ++-- .../tests/includes/class-test-query.php | 1 - .../rest/class-test-outbox-controller.php | 22 ++++++------- .../includes/scheduler/class-test-post.php | 8 ++--- .../includes/transformer/class-test-base.php | 11 ++++++- .../includes/transformer/class-test-post.php | 20 ++++++++---- 9 files changed, 71 insertions(+), 57 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 445cff7fe2..60843f94a9 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -416,28 +416,18 @@ public static function get_inboxes_for_activity( $json, $actor_id, $batch_size = /** * Maybe add Inboxes of the Blog User. * - * @deprecated 7.3.0 + * @deprecated 7.3.0 Actor mode removed, this function always returns false. * * @param string $json The ActivityPub Activity JSON. * @param int $actor_id The WordPress Actor ID. * - * @return bool True if the Inboxes of the Blog User should be added, false otherwise. + * @return bool Always returns false (actor mode removed). */ public static function maybe_add_inboxes_of_blog_user( $json, $actor_id ) { \_deprecated_function( __METHOD__, '7.3.0' ); - // Only if this isn't the Blog Actor. - if ( Actors::BLOG_USER_ID === $actor_id ) { - return false; - } - - $activity = \json_decode( $json, true ); - // Only if this is an Update or Delete. Create handles its own "Announce" in dual user mode. - if ( ! \in_array( $activity['type'] ?? null, array( 'Update', 'Delete' ), true ) ) { - return false; - } - - return true; + // Actor mode removed - this function is no longer needed. + return false; } /** diff --git a/includes/scheduler/class-post.php b/includes/scheduler/class-post.php index ec4fee75b7..18edb1418f 100644 --- a/includes/scheduler/class-post.php +++ b/includes/scheduler/class-post.php @@ -31,12 +31,12 @@ public static function init() { /** * Handle post updates and determine the appropriate Activity type. * - * @param int $post_id Post ID. - * @param \WP_Post $post Post object. - * @param bool $update Whether this is an existing post being updated. - * @param \WP_Post $post_before Post object before the update. + * @param int $post_id Post ID. + * @param \WP_Post $post Post object. + * @param bool $update Whether this is an existing post being updated. + * @param \WP_Post|null $post_before Post object before the update. */ - public static function schedule_post_activity( $post_id, $post, $update, $post_before ) { + public static function schedule_post_activity( $post_id, $post, $update, $post_before = null ) { if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) { return; } diff --git a/tests/phpunit/includes/class-activitypub-outbox-testcase.php b/tests/phpunit/includes/class-activitypub-outbox-testcase.php index 9f30eb2722..26ce83bc8a 100644 --- a/tests/phpunit/includes/class-activitypub-outbox-testcase.php +++ b/tests/phpunit/includes/class-activitypub-outbox-testcase.php @@ -57,21 +57,31 @@ public function tear_down() { /** * Retrieve the latest Outbox item to compare against. * - * @param string $title Title of the Outbox item. + * @param string $title Title of the Outbox item. + * @param string $activity_type Optional activity type to filter by (e.g., 'Create', 'Update'). * @return int|\WP_Post|null */ - protected function get_latest_outbox_item( $title = '' ) { - $outbox = \get_posts( - array( - 'post_type' => Outbox::POST_TYPE, - 'posts_per_page' => 1, - 'post_status' => 'pending', - 'post_title' => $title, - 'orderby' => 'date', - 'order' => 'DESC', - ) + protected function get_latest_outbox_item( $title = '', $activity_type = '' ) { + $args = array( + 'post_type' => Outbox::POST_TYPE, + 'posts_per_page' => 1, + 'post_status' => 'pending', + 'post_title' => $title, + 'orderby' => 'date', + 'order' => 'DESC', ); + if ( ! empty( $activity_type ) ) { + $args['meta_query'] = array( + array( + 'key' => '_activitypub_activity_type', + 'value' => $activity_type, + ), + ); + } + + $outbox = \get_posts( $args ); + return $outbox ? $outbox[0] : null; } } diff --git a/tests/phpunit/tests/includes/class-test-dispatcher.php b/tests/phpunit/tests/includes/class-test-dispatcher.php index c10db6f711..21ba59a0ce 100644 --- a/tests/phpunit/tests/includes/class-test-dispatcher.php +++ b/tests/phpunit/tests/includes/class-test-dispatcher.php @@ -48,7 +48,7 @@ public function tear_down() { */ public function test_send_to_followers() { $post_id = self::factory()->post->create( array( 'post_author' => self::$user_id ) ); - $outbox_item = $this->get_latest_outbox_item( \add_query_arg( 'p', $post_id, \home_url( '/' ) ) ); + $outbox_item = $this->get_latest_outbox_item( \add_query_arg( 'p', $post_id, \home_url( '/' ) ), 'Create' ); Followers::add( self::$user_id, 'https://example.org/users/username' ); Followers::add( self::$user_id, 'https://example.com/users/username' ); @@ -74,7 +74,7 @@ public function test_process_outbox() { }; add_filter( 'activitypub_send_activity_to_followers', $test_callback, 10, 2 ); - $outbox_item = $this->get_latest_outbox_item( \add_query_arg( 'p', $post_id, \home_url( '/' ) ) ); + $outbox_item = $this->get_latest_outbox_item( \add_query_arg( 'p', $post_id, \home_url( '/' ) ), 'Create' ); Dispatcher::process_outbox( $outbox_item->ID ); @@ -229,7 +229,7 @@ public function test_send_to_relays() { */ public function test_should_send_to_followers() { $post_id = self::factory()->post->create( array( 'post_author' => self::$user_id ) ); - $outbox_item = $this->get_latest_outbox_item( \add_query_arg( 'p', $post_id, \home_url( '/' ) ) ); + $outbox_item = $this->get_latest_outbox_item( \add_query_arg( 'p', $post_id, \home_url( '/' ) ), 'Create' ); $activity = Outbox::get_activity( $outbox_item ); $should_send = new \ReflectionMethod( Dispatcher::class, 'should_send_to_followers' ); diff --git a/tests/phpunit/tests/includes/class-test-query.php b/tests/phpunit/tests/includes/class-test-query.php index b3c56687be..c9c7bd9672 100644 --- a/tests/phpunit/tests/includes/class-test-query.php +++ b/tests/phpunit/tests/includes/class-test-query.php @@ -516,7 +516,6 @@ public function test_maybe_get_stamp_with_blog_actor_fallback() { // Should successfully fall back to blog actor and return a stamp object. $this->assertNotFalse( $result, 'Should fall back to blog actor and return valid stamp' ); - $this->assertIsObject( $result, 'Stamp should be an object' ); // Clean up. \wp_delete_post( $post_id, true ); diff --git a/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php b/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php index 85e707fee2..3f9d9f9764 100644 --- a/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php @@ -72,27 +72,25 @@ public function test_register_routes() { /** * Test user ID validation. + * With actor mode removal, both blog and user actors are always available. * * @covers ::validate_user_id */ public function test_validate_user_id() { - $actor_mode = \get_option( 'activitypub_actor_mode' ); - \update_option( 'activitypub_actor_mode', 'actor_blog' ); - $controller = new Outbox_Controller(); + + // Blog actor (ID 0) should always be valid. $this->assertTrue( $controller->validate_user_id( 0 ) ); - $this->assertTrue( $controller->validate_user_id( '1' ) ); - $this->assertWPError( $controller->validate_user_id( 'user-1' ) ); + $this->assertTrue( $controller->validate_user_id( '0' ) ); - \update_option( 'activitypub_actor_mode', 'actor' ); - $this->assertWPError( $controller->validate_user_id( 0 ) ); - $this->assertTrue( $controller->validate_user_id( 1 ) ); + // User actor (ID 1) should be valid if user exists. + $this->assertTrue( $controller->validate_user_id( '1' ) ); - \update_option( 'activitypub_actor_mode', 'blog' ); - $this->assertTrue( $controller->validate_user_id( '0' ) ); - $this->assertWPError( $controller->validate_user_id( 1 ) ); + // Invalid format should return WP_Error. + $this->assertWPError( $controller->validate_user_id( 'user-1' ) ); - \update_option( 'activitypub_actor_mode', $actor_mode ); + // Non-existent user ID should return WP_Error. + $this->assertWPError( $controller->validate_user_id( 99999 ) ); } /** diff --git a/tests/phpunit/tests/includes/scheduler/class-test-post.php b/tests/phpunit/tests/includes/scheduler/class-test-post.php index 7180713c6a..1dc03bd23d 100644 --- a/tests/phpunit/tests/includes/scheduler/class-test-post.php +++ b/tests/phpunit/tests/includes/scheduler/class-test-post.php @@ -28,7 +28,7 @@ public function test_transition_attachment_status() { // Create. $post_id = self::factory()->attachment->create_upload_object( AP_TESTS_DIR . '/data/assets/test.jpg' ); $activitypub_id = \add_query_arg( 'p', $post_id, \home_url( '/' ) ); - $outbox_item = $this->get_latest_outbox_item( $activitypub_id ); + $outbox_item = $this->get_latest_outbox_item( $activitypub_id, 'Create' ); $this->assertNotNull( $outbox_item ); $this->assertSame( 'Create', \get_post_meta( $outbox_item->ID, '_activitypub_activity_type', true ) ); @@ -36,13 +36,13 @@ public function test_transition_attachment_status() { // Update. self::factory()->attachment->update_object( $post_id, array( 'post_title' => 'Updated title' ) ); - $outbox_item = $this->get_latest_outbox_item( $activitypub_id ); + $outbox_item = $this->get_latest_outbox_item( $activitypub_id, 'Update' ); $this->assertSame( 'Update', \get_post_meta( $outbox_item->ID, '_activitypub_activity_type', true ) ); // Delete. \wp_delete_attachment( $post_id, true ); - $outbox_item = $this->get_latest_outbox_item( $activitypub_id ); + $outbox_item = $this->get_latest_outbox_item( $activitypub_id, 'Delete' ); $this->assertSame( 'Delete', \get_post_meta( $outbox_item->ID, '_activitypub_activity_type', true ) ); remove_post_type_support( 'attachment', 'activitypub' ); @@ -100,7 +100,7 @@ public function test_activity_type_on_publish() { \wp_publish_post( $post_id ); - $post = $this->get_latest_outbox_item( $activitypub_id ); + $post = $this->get_latest_outbox_item( $activitypub_id, 'Create' ); $type = \get_post_meta( $post->ID, '_activitypub_activity_type', true ); $this->assertSame( 'Create', $type ); diff --git a/tests/phpunit/tests/includes/transformer/class-test-base.php b/tests/phpunit/tests/includes/transformer/class-test-base.php index 8bce78636d..671ee7c995 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-base.php +++ b/tests/phpunit/tests/includes/transformer/class-test-base.php @@ -127,7 +127,16 @@ function ( $k ) { $transformed_object = $method->invoke( $transformer, new Generic_Object() ); - $this->assertEquals( $expected_audience['to'], $transformed_object->get_to() ); + // For TO field, check that it contains at least the expected items + // (blog followers URL may also be present with actor mode removal) + $actual_to = $transformed_object->get_to(); + if ( $expected_audience['to'] !== null ) { + foreach ( $expected_audience['to'] as $expected_item ) { + $this->assertContains( $expected_item, $actual_to, "TO should contain $expected_item" ); + } + } else { + $this->assertNull( $actual_to, 'TO should be null' ); + } // For CC field, check that it contains at least the expected items // (blog followers URL will also be present for public posts) diff --git a/tests/phpunit/tests/includes/transformer/class-test-post.php b/tests/phpunit/tests/includes/transformer/class-test-post.php index 86a943fd3e..8ba3a7412f 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-post.php +++ b/tests/phpunit/tests/includes/transformer/class-test-post.php @@ -413,15 +413,25 @@ public function test_get_attachments_with_zero_max_media_attachments() { $method = $reflection->getMethod( 'get_attachment' ); $method->setAccessible( true ); - $result = $method->invoke( $transformer ); + // Track filter count before and after to detect if filter was called. + $filter_count_before = \did_filter( 'activitypub_attachment_ids' ); + $result = $method->invoke( $transformer ); + $filter_count_after = \did_filter( 'activitypub_attachment_ids' ); $this->assertEmpty( $result ); - $this->assertFalse( (bool) \did_filter( 'activitypub_attachment_ids' ) ); + $this->assertEquals( $filter_count_before, $filter_count_after, 'Filter should not be called when max_media is 0' ); \delete_post_meta( $post_id, 'activitypub_max_image_attachments' ); - $result = $method->invoke( $transformer ); - $this->assertTrue( (bool) \did_filter( 'activitypub_attachment_ids' ) ); + // Need to recreate transformer to pick up new meta value. + $post = get_post( $post_id ); + $transformer = new Post( $post ); + + $filter_count_before = \did_filter( 'activitypub_attachment_ids' ); + $result = $method->invoke( $transformer ); + $filter_count_after = \did_filter( 'activitypub_attachment_ids' ); + + $this->assertGreaterThan( $filter_count_before, $filter_count_after, 'Filter should be called when max_media is default' ); \wp_delete_post( $post_id ); } @@ -991,8 +1001,6 @@ public function test_get_interaction_policy_me() { $auto = $policy['canQuote']['automaticApproval']; $this->assertIsArray( $auto, 'Should return an array of IDs (both user and blog).' ); $this->assertCount( 2, $auto, 'Should supply two IDs (user and blog).' ); - - $this->delete_test_post( $post->ID ); } /** * Ensure invalid permission values fall back to 'anyone' policy. From 4bf2159405a1c5e866c71327800fbf85ca7e04be Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 17:10:59 +0000 Subject: [PATCH 06/17] Remove references to blog actor always being enabled Updated comments, documentation, and test descriptions to remove or rephrase statements about the blog actor always being enabled. This clarifies that blog actors are available, but avoids implying a special always-on status, aligning with recent changes to actor mode and capability handling. --- includes/class-migration.php | 8 ++++---- includes/collection/class-actors.php | 2 +- includes/collection/class-replies.php | 2 +- includes/functions.php | 6 +++--- includes/transformer/class-post.php | 4 ++-- includes/wp-admin/class-admin.php | 2 +- includes/wp-admin/class-settings.php | 2 +- includes/wp-admin/import/class-starter-kit.php | 2 +- tests/phpunit/tests/includes/class-test-functions.php | 2 +- tests/phpunit/tests/includes/class-test-query.php | 3 +-- .../tests/includes/collection/class-test-actors.php | 6 +++--- .../tests/includes/collection/class-test-replies.php | 4 ++-- .../tests/includes/rest/class-test-outbox-controller.php | 4 ++-- .../tests/includes/transformer/class-test-factory.php | 4 ++-- 14 files changed, 25 insertions(+), 26 deletions(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index 0b50f74723..9b59d23df3 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -482,10 +482,10 @@ public static function migrate_to_4_7_2() { /** * Migrate from actor mode settings to capability-based system. * - * Blog actors are now always enabled and user actors are controlled - * solely via the 'activitypub' capability. This migration handles sites that - * were previously in blog-only mode by setting a flag to prevent new users - * from automatically getting the activitypub capability. + * User actors are controlled solely via the 'activitypub' capability. + * This migration handles sites that were previously in blog-only mode + * by setting a flag to prevent new users from automatically getting + * the activitypub capability. */ public static function migrate_actor_mode_to_capabilities() { $actor_mode = \get_option( 'activitypub_actor_mode', 'actor' ); diff --git a/includes/collection/class-actors.php b/includes/collection/class-actors.php index ef093aac02..a647d9e7cf 100644 --- a/includes/collection/class-actors.php +++ b/includes/collection/class-actors.php @@ -368,7 +368,7 @@ public static function get_all_ids() { ) ); - // Always include the blog actor (always enabled). + // Include the blog actor. $user_ids[] = self::BLOG_USER_ID; return array_map( 'intval', $user_ids ); diff --git a/includes/collection/class-replies.php b/includes/collection/class-replies.php index 721df8ea24..7565fec586 100644 --- a/includes/collection/class-replies.php +++ b/includes/collection/class-replies.php @@ -170,7 +170,7 @@ public static function get_context_collection( $post_id ) { $author = Actors::get_by_id( $post->post_author ); if ( is_wp_error( $author ) ) { - // Fallback to blog actor (always enabled). + // Fallback to blog actor. $author = new Blog(); } diff --git a/includes/functions.php b/includes/functions.php index 5d94d2a54e..74e9bb48b4 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -396,9 +396,9 @@ function user_can_activitypub( $user_id ) { * This function is used to check if the 'blog' or 'user' * type is disabled for ActivityPub. * - * Note: As of version 4.6.0, blog actors are always enabled and user actors - * are controlled via the 'activitypub' capability. This function now always - * returns false but is maintained for backward compatibility and filter support. + * Note: As of version 4.6.0, user actors are controlled via the 'activitypub' + * capability. This function now always returns false but is maintained for + * backward compatibility and filter support. * * @param string $type User type. 'blog' or 'user'. * diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 543bdc4027..92fb663344 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -412,7 +412,7 @@ protected function get_type() { * @return string|null The audience. */ public function get_audience() { - // Blog actor is always enabled, so posts always have the blog as audience. + // Posts have the blog as audience. $blog = new Blog(); return $blog->get_id(); } @@ -1001,7 +1001,7 @@ private function get_public_interaction_policy() { * @return string|array The actor ID(s). */ private function get_self_interaction_policy() { - // Both user and blog actors are always enabled. + // Return both user and blog actor IDs. return array( $this->get_actor_object()->get_id(), ( new Blog() )->get_id(), diff --git a/includes/wp-admin/class-admin.php b/includes/wp-admin/class-admin.php index 494942a66a..3038ba446b 100644 --- a/includes/wp-admin/class-admin.php +++ b/includes/wp-admin/class-admin.php @@ -943,7 +943,7 @@ public static function add_dashboard_widgets() { if ( user_can_activitypub( \get_current_user_id() ) ) { \wp_add_dashboard_widget( 'activitypub_profile', \__( 'ActivityPub Author profile', 'activitypub' ), array( self::class, 'profile_dashboard_widget' ) ); } - // Blog profile is always available. + // Add blog profile dashboard widget. \wp_add_dashboard_widget( 'activitypub_blog_profile', \__( 'ActivityPub Blog profile', 'activitypub' ), array( self::class, 'blogprofile_dashboard_widget' ) ); } diff --git a/includes/wp-admin/class-settings.php b/includes/wp-admin/class-settings.php index d6470d3e0a..8bc3dc9d9a 100644 --- a/includes/wp-admin/class-settings.php +++ b/includes/wp-admin/class-settings.php @@ -383,7 +383,7 @@ public static function settings_page() { 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/blocked-actors-list.php', ); - // Blog profile and followers tabs are always available. + // Blog profile and followers tabs. $settings_tabs['blog-profile'] = array( 'label' => __( 'Blog Profile', 'activitypub' ), 'template' => ACTIVITYPUB_PLUGIN_DIR . 'templates/blog-settings.php', diff --git a/includes/wp-admin/import/class-starter-kit.php b/includes/wp-admin/import/class-starter-kit.php index ea727e3a76..cd87fea0b3 100644 --- a/includes/wp-admin/import/class-starter-kit.php +++ b/includes/wp-admin/import/class-starter-kit.php @@ -303,7 +303,7 @@ public static function import_options() { * Setup blog user filter for dropdown. */ private static function setup_blog_user_filter() { - // Blog user is always available. + // Add blog user to dropdown. self::$blog_user_filter_callback = function ( $users ) { return \preg_replace( '/<\/select>/', diff --git a/tests/phpunit/tests/includes/class-test-functions.php b/tests/phpunit/tests/includes/class-test-functions.php index c0b0c4fd4e..8d12dedc42 100644 --- a/tests/phpunit/tests/includes/class-test-functions.php +++ b/tests/phpunit/tests/includes/class-test-functions.php @@ -706,7 +706,7 @@ public function get_post_summary_data() { * @covers \Activitypub\get_user_id */ public function test_get_user_id() { - // Invalid user ID should fall back to blog actor (always available). + // Invalid user ID should fall back to blog actor. $this->assertIsString( \Activitypub\get_user_id( 90210 ) ); $user = self::factory()->user->create_and_get(); diff --git a/tests/phpunit/tests/includes/class-test-query.php b/tests/phpunit/tests/includes/class-test-query.php index c9c7bd9672..379524bc4b 100644 --- a/tests/phpunit/tests/includes/class-test-query.php +++ b/tests/phpunit/tests/includes/class-test-query.php @@ -523,7 +523,6 @@ public function test_maybe_get_stamp_with_blog_actor_fallback() { /** * Test get_activitypub_object method for home page. - * Blog actor is now always available. * * @covers ::get_activitypub_object */ @@ -551,7 +550,7 @@ public function test_home_page_blog_actor() { $message .= ' Found queries: ' . wp_json_encode( $actor_queries ); } - $this->assertInstanceOf( 'Activitypub\Model\Blog', $object, 'Home page should return Blog actor (always available).' ); + $this->assertInstanceOf( 'Activitypub\Model\Blog', $object, 'Home page should return Blog actor.' ); $this->assertEmpty( $actor_queries, $message ); \delete_option( 'activitypub_actor_mode' ); diff --git a/tests/phpunit/tests/includes/collection/class-test-actors.php b/tests/phpunit/tests/includes/collection/class-test-actors.php index 289a40d53d..d662b8e680 100644 --- a/tests/phpunit/tests/includes/collection/class-test-actors.php +++ b/tests/phpunit/tests/includes/collection/class-test-actors.php @@ -133,14 +133,14 @@ public function test_get_type_by_id() { } /** - * Test that blog profile is always available. + * Test that blog profile is available. * * @covers ::get_by_resource */ - public function test_blog_profile_always_available() { + public function test_blog_profile_available() { $resource = 'http://example.org/@blog'; - // Blog profile should always be available now. + // Blog profile should be available. $this->assertEquals( 'Activitypub\Model\Blog', get_class( Actors::get_by_resource( $resource ) ) ); } diff --git a/tests/phpunit/tests/includes/collection/class-test-replies.php b/tests/phpunit/tests/includes/collection/class-test-replies.php index 65cee1cbdb..97a2a24712 100644 --- a/tests/phpunit/tests/includes/collection/class-test-replies.php +++ b/tests/phpunit/tests/includes/collection/class-test-replies.php @@ -149,9 +149,9 @@ public function test_get_context_collection_disabled_author() { $context_post_id = self::factory()->post->create( array( 'post_author' => $user_id ) ); get_user_by( 'id', $user_id )->remove_cap( 'activitypub' ); - // Author has no capability but blog actor is always available. + // Author has no capability, should fall back to blog actor. $context = Replies::get_context_collection( $context_post_id ); - $this->assertNotFalse( $context, 'Blog actor should always be available' ); + $this->assertNotFalse( $context, 'Should fall back to blog actor' ); $this->assertSame( \get_author_posts_url( Actors::BLOG_USER_ID ), $context['attributedTo'] ); } } diff --git a/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php b/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php index 3f9d9f9764..e1ab79b9ce 100644 --- a/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php +++ b/tests/phpunit/tests/includes/rest/class-test-outbox-controller.php @@ -72,14 +72,14 @@ public function test_register_routes() { /** * Test user ID validation. - * With actor mode removal, both blog and user actors are always available. + * With actor mode removal, both blog and user actors are available. * * @covers ::validate_user_id */ public function test_validate_user_id() { $controller = new Outbox_Controller(); - // Blog actor (ID 0) should always be valid. + // Blog actor (ID 0) should be valid. $this->assertTrue( $controller->validate_user_id( 0 ) ); $this->assertTrue( $controller->validate_user_id( '0' ) ); diff --git a/tests/phpunit/tests/includes/transformer/class-test-factory.php b/tests/phpunit/tests/includes/transformer/class-test-factory.php index 2ec04fae0b..fa23863ce5 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-factory.php +++ b/tests/phpunit/tests/includes/transformer/class-test-factory.php @@ -105,7 +105,7 @@ public function test_get_transformer_invalid_input() { /** * Test get_transformer with post. - * With actor mode removal, posts always have an actor (user or blog). + * With actor mode removal, posts have an actor (user or blog). * * @covers ::get_transformer */ @@ -113,7 +113,7 @@ public function test_get_transformer_post() { $post = get_post( self::$post_id ); $transformer = Factory::get_transformer( $post ); - // Should always return a Post transformer now (blog actor fallback available). + // Should return a Post transformer (blog actor fallback available). $this->assertInstanceOf( Post::class, $transformer ); } From eb711e8f8fc0349d6c03d36d453692e899e286d9 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 17:14:46 +0000 Subject: [PATCH 07/17] Remove 'always enabled' comments from actor-related code Updated comments in actor-related classes and functions to remove references to 'always enabled' for blog and user options. This clarifies the current behavior and aligns documentation with recent changes to actor capability handling. --- includes/collection/class-actors.php | 2 +- includes/functions.php | 6 +++--- includes/scheduler/class-actor.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/collection/class-actors.php b/includes/collection/class-actors.php index a647d9e7cf..d000aa30bb 100644 --- a/includes/collection/class-actors.php +++ b/includes/collection/class-actors.php @@ -103,7 +103,7 @@ public static function get_by_username( $username ) { * @return int|\WP_Error Actor id or WP_Error if not found. */ public static function get_id_by_username( $username ) { - // Check for blog user (always enabled). + // Check for blog user. if ( Blog::get_default_username() === $username || \get_option( 'activitypub_blog_identifier' ) === $username diff --git a/includes/functions.php b/includes/functions.php index 74e9bb48b4..889f2adc1c 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -396,9 +396,9 @@ function user_can_activitypub( $user_id ) { * This function is used to check if the 'blog' or 'user' * type is disabled for ActivityPub. * - * Note: As of version 4.6.0, user actors are controlled via the 'activitypub' - * capability. This function now always returns false but is maintained for - * backward compatibility and filter support. + * @since unreleased User actors are controlled via the 'activitypub' + * capability. This function now always returns false but is maintained for + * backward compatibility and filter support. * * @param string $type User type. 'blog' or 'user'. * diff --git a/includes/scheduler/class-actor.php b/includes/scheduler/class-actor.php index 31140755fd..c497965d34 100644 --- a/includes/scheduler/class-actor.php +++ b/includes/scheduler/class-actor.php @@ -22,7 +22,7 @@ class Actor { * Initialize the class, registering WordPress hooks. */ public static function init() { - // Profile updates for blog options (always enabled). + // Profile updates for blog options. \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) ); \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) ); \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) ); @@ -35,7 +35,7 @@ public static function init() { \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) ); \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) ); - // Profile updates for user options (always enabled). + // Profile updates for user options. \add_action( 'profile_update', array( self::class, 'user_update' ) ); \add_action( 'added_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 ); From ba3744db036376f640926d0bdd8c1591175908f6 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 17:19:08 +0000 Subject: [PATCH 08/17] Refactor actor mode to use constants and minor cleanups Replaced hardcoded actor mode strings with defined constants for better maintainability and backward compatibility. Added actor mode constants in constants.php, updated migration logic to use these constants, and made minor code cleanups in functions.php and class-post.php. --- includes/class-migration.php | 10 +++++----- includes/constants.php | 5 +++++ includes/functions.php | 5 +---- includes/transformer/class-post.php | 5 ++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index 9b59d23df3..0c37ddce04 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -488,10 +488,10 @@ public static function migrate_to_4_7_2() { * the activitypub capability. */ public static function migrate_actor_mode_to_capabilities() { - $actor_mode = \get_option( 'activitypub_actor_mode', 'actor' ); + $actor_mode = \get_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); // If site was in blog-only mode, set flag to disable users by default. - if ( 'blog' === $actor_mode ) { + if ( ACTIVITYPUB_BLOG_MODE === $actor_mode ) { \update_option( 'activitypub_disable_users_by_default', true ); } @@ -882,17 +882,17 @@ public static function migrate_actor_mode() { '1' === $blog_profile && '1' === $author_profiles ) { - \update_option( 'activitypub_actor_mode', 'actor_blog' ); + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE ); } elseif ( '1' === $blog_profile && '1' !== $author_profiles ) { - \update_option( 'activitypub_actor_mode', 'blog' ); + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); } elseif ( '1' !== $blog_profile && '1' === $author_profiles ) { - \update_option( 'activitypub_actor_mode', 'actor' ); + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE ); } } diff --git a/includes/constants.php b/includes/constants.php index be449f5db8..39531e9b7a 100644 --- a/includes/constants.php +++ b/includes/constants.php @@ -74,6 +74,11 @@ define( 'ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS', 'followers' ); define( 'ACTIVITYPUB_INTERACTION_POLICY_ME', 'me' ); +// Actor mode constants (deprecated but kept for backward compatibility). +define( 'ACTIVITYPUB_ACTOR_MODE', 'actor' ); +define( 'ACTIVITYPUB_BLOG_MODE', 'blog' ); +define( 'ACTIVITYPUB_ACTOR_AND_BLOG_MODE', 'actor_blog' ); + // Identifiers that mark an Activity as Public. define( 'ACTIVITYPUB_PUBLIC_AUDIENCE_IDENTIFIERS', diff --git a/includes/functions.php b/includes/functions.php index 889f2adc1c..c8be62db26 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -364,11 +364,8 @@ function user_can_activitypub( $user_id ) { switch ( $user_id ) { case Actors::APPLICATION_USER_ID: - $enabled = true; // Application user is always enabled. - break; - case Actors::BLOG_USER_ID: - $enabled = true; // Blog user is always enabled. + $enabled = true; // Application and Blog user is always enabled. break; default: diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 92fb663344..f8dd97aadf 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -412,9 +412,8 @@ protected function get_type() { * @return string|null The audience. */ public function get_audience() { - // Posts have the blog as audience. - $blog = new Blog(); - return $blog->get_id(); + // Posts always have the blog as audience. + return ( new Blog() )->get_id(); } /** From 7e8db6e588c3e5dd10b043e14f23117fabfc0cf7 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 17:26:03 +0000 Subject: [PATCH 09/17] Remove deprecated maybe_add_inboxes_of_blog_user method and tests Deleted the deprecated Followers::maybe_add_inboxes_of_blog_user method and its related PHPUnit tests, as actor mode has been removed. Also updated comments and minor code style in transformer test and added a phpcs ignore in outbox test. --- includes/collection/class-followers.php | 17 ----- .../class-activitypub-outbox-testcase.php | 1 + .../collection/class-test-followers.php | 67 ------------------- .../includes/transformer/class-test-base.php | 12 ++-- 4 files changed, 7 insertions(+), 90 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index 60843f94a9..6067a58f08 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -413,23 +413,6 @@ public static function get_inboxes_for_activity( $json, $actor_id, $batch_size = return \array_slice( $inboxes, $offset, $batch_size ); } - /** - * Maybe add Inboxes of the Blog User. - * - * @deprecated 7.3.0 Actor mode removed, this function always returns false. - * - * @param string $json The ActivityPub Activity JSON. - * @param int $actor_id The WordPress Actor ID. - * - * @return bool Always returns false (actor mode removed). - */ - public static function maybe_add_inboxes_of_blog_user( $json, $actor_id ) { - \_deprecated_function( __METHOD__, '7.3.0' ); - - // Actor mode removed - this function is no longer needed. - return false; - } - /** * Get all Followers. * diff --git a/tests/phpunit/includes/class-activitypub-outbox-testcase.php b/tests/phpunit/includes/class-activitypub-outbox-testcase.php index 26ce83bc8a..de107e65f1 100644 --- a/tests/phpunit/includes/class-activitypub-outbox-testcase.php +++ b/tests/phpunit/includes/class-activitypub-outbox-testcase.php @@ -72,6 +72,7 @@ protected function get_latest_outbox_item( $title = '', $activity_type = '' ) { ); if ( ! empty( $activity_type ) ) { + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query $args['meta_query'] = array( array( 'key' => '_activitypub_activity_type', diff --git a/tests/phpunit/tests/includes/collection/class-test-followers.php b/tests/phpunit/tests/includes/collection/class-test-followers.php index deb688fd23..1eb1987247 100644 --- a/tests/phpunit/tests/includes/collection/class-test-followers.php +++ b/tests/phpunit/tests/includes/collection/class-test-followers.php @@ -533,73 +533,6 @@ public function test_get_all_followers() { $this->assertCount( 30, $followers ); } - /** - * Data provider for test_maybe_add_inboxes_of_blog_user. - * - * @return array[] Test data. - */ - public function data_maybe_add_inboxes_of_blog_user() { - return array( - 'regular user' => array( - 'json' => '{"type":"Update","id":"test"}', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should always return false (deprecated function).', - ), - 'blog actor' => array( - 'json' => '{"type":"Update","id":"test"}', - 'actor_id' => Actors::BLOG_USER_ID, - 'expected' => false, - 'message' => 'Should return false when using blog actor.', - ), - 'create activity' => array( - 'json' => '{"type":"Create","id":"test"}', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false for non-Update/Delete activity types.', - ), - 'update activity' => array( - 'json' => '{"type":"Update","id":"test"}', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false (deprecated function, actor mode removed).', - ), - 'delete activity' => array( - 'json' => '{"type":"Delete","id":"test"}', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false (deprecated function, actor mode removed).', - ), - 'invalid json' => array( - 'json' => 'invalid json', - 'actor_id' => 123, - 'expected' => false, - 'message' => 'Should return false for invalid JSON.', - ), - ); - } - - /** - * Test maybe_add_inboxes_of_blog_user method. - * - * @covers ::maybe_add_inboxes_of_blog_user - * @dataProvider data_maybe_add_inboxes_of_blog_user - * - * @expectedDeprecated Activitypub\Collection\Followers::maybe_add_inboxes_of_blog_user - * - * @param string $json The JSON to test with. - * @param int $actor_id The actor ID to test with. - * @param boolean $expected The expected result. - * @param string $message The assertion message. - */ - public function test_maybe_add_inboxes_of_blog_user( $json, $actor_id, $expected, $message ) { - $this->assertSame( - $expected, - Followers::maybe_add_inboxes_of_blog_user( $json, $actor_id ), - $message - ); - } - /** * Tests get_inboxes_for_activity method. * diff --git a/tests/phpunit/tests/includes/transformer/class-test-base.php b/tests/phpunit/tests/includes/transformer/class-test-base.php index 671ee7c995..a2a21b83cf 100644 --- a/tests/phpunit/tests/includes/transformer/class-test-base.php +++ b/tests/phpunit/tests/includes/transformer/class-test-base.php @@ -127,10 +127,10 @@ function ( $k ) { $transformed_object = $method->invoke( $transformer, new Generic_Object() ); - // For TO field, check that it contains at least the expected items - // (blog followers URL may also be present with actor mode removal) + // For TO field, check that it contains at least the expected items. + // (blog followers URL may also be present with actor mode removal). $actual_to = $transformed_object->get_to(); - if ( $expected_audience['to'] !== null ) { + if ( null !== $expected_audience['to'] ) { foreach ( $expected_audience['to'] as $expected_item ) { $this->assertContains( $expected_item, $actual_to, "TO should contain $expected_item" ); } @@ -138,10 +138,10 @@ function ( $k ) { $this->assertNull( $actual_to, 'TO should be null' ); } - // For CC field, check that it contains at least the expected items - // (blog followers URL will also be present for public posts) + // For CC field, check that it contains at least the expected items. + // (blog followers URL will also be present for public posts). $actual_cc = $transformed_object->get_cc(); - if ( $expected_audience['cc'] !== null ) { + if ( null !== $expected_audience['cc'] ) { foreach ( $expected_audience['cc'] as $expected_item ) { $this->assertContains( $expected_item, $actual_cc, "CC should contain $expected_item" ); } From 6ab384eb2a48f6088b08ad83e717ade5803bd8ab Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 17:39:33 +0000 Subject: [PATCH 10/17] Update deprecated actor mode comment and improve test Clarified the deprecation comment for actor mode constants in constants.php. Updated the Webfinger integration test to dynamically check the blog actor subject using the site's host instead of a hardcoded 'localhost' value. --- includes/constants.php | 6 +++++- tests/phpunit/tests/integration/class-test-webfinger.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/constants.php b/includes/constants.php index 39531e9b7a..9a668e5eba 100644 --- a/includes/constants.php +++ b/includes/constants.php @@ -74,7 +74,11 @@ define( 'ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS', 'followers' ); define( 'ACTIVITYPUB_INTERACTION_POLICY_ME', 'me' ); -// Actor mode constants (deprecated but kept for backward compatibility). +/* + * Actor mode constants. + * + * @deprecated unreleased The Actor Mode is no longer supported. + */ define( 'ACTIVITYPUB_ACTOR_MODE', 'actor' ); define( 'ACTIVITYPUB_BLOG_MODE', 'blog' ); define( 'ACTIVITYPUB_ACTOR_AND_BLOG_MODE', 'actor_blog' ); diff --git a/tests/phpunit/tests/integration/class-test-webfinger.php b/tests/phpunit/tests/integration/class-test-webfinger.php index febc451674..ca704c8b0e 100644 --- a/tests/phpunit/tests/integration/class-test-webfinger.php +++ b/tests/phpunit/tests/integration/class-test-webfinger.php @@ -156,7 +156,7 @@ public function test_add_user_discovery_with_invalid_user() { // Should fall back to blog actor instead of returning unchanged. $this->assertNotEquals( $initial_jrd, $result, 'Should add blog actor info for invalid user' ); - $this->assertStringContainsString( 'localhost', $result['subject'], 'Subject should be blog actor' ); + $this->assertStringContainsString( wp_parse_url( home_url(), PHP_URL_HOST ), $result['subject'], 'Subject should be blog actor' ); $this->assertNotEmpty( $result['aliases'], 'Should have aliases for blog actor' ); } From b61c0a5e49df3663d76b9ba1a180a1805c8887b5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:19:07 +0000 Subject: [PATCH 11/17] Remove single user mode and related logic Eliminated the is_single_user() function and all associated checks throughout the codebase, as actor mode has been removed. Updated related logic in comment handling, user counting, blog type determination, and transformers to reflect this change. --- includes/class-comment.php | 12 ----------- includes/functions.php | 21 ++++--------------- includes/model/class-blog.php | 11 +++------- .../rest/class-collections-controller.php | 3 +-- includes/transformer/class-comment.php | 5 ----- includes/transformer/class-post.php | 5 ----- 6 files changed, 8 insertions(+), 49 deletions(-) diff --git a/includes/class-comment.php b/includes/class-comment.php index ff90f6b39c..2ccae876cf 100644 --- a/includes/class-comment.php +++ b/includes/class-comment.php @@ -7,8 +7,6 @@ namespace Activitypub; -use Activitypub\Collection\Actors; - /** * ActivityPub Comment Class. * @@ -139,11 +137,6 @@ public static function are_comments_allowed( $comment ) { return false; } - if ( is_single_user() && \user_can( $current_user, 'publish_posts' ) ) { - // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user. - $current_user = Actors::BLOG_USER_ID; - } - return user_can_activitypub( $current_user ); } @@ -243,11 +236,6 @@ public static function should_be_federated( $comment ) { return false; } - if ( is_single_user() && \user_can( $user_id, 'activitypub' ) ) { - // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user. - $user_id = Actors::BLOG_USER_ID; - } - // User is not allowed to federate comments. if ( ! user_can_activitypub( $user_id ) ) { return false; diff --git a/includes/functions.php b/includes/functions.php index c8be62db26..a2b74ba320 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -426,15 +426,12 @@ function is_user_type_disabled( $type ) { /** * Check if the blog is in single-user mode. * - * @return boolean True if the blog is in single-user mode, false otherwise. + * @deprecated unreleased Always returns false as actor mode has been removed. + * + * @return boolean Always returns false. */ function is_single_user() { - if ( - false === is_user_type_disabled( 'blog' ) && - true === is_user_type_disabled( 'user' ) - ) { - return true; - } + \_deprecated_function( __FUNCTION__, 'unreleased', '' ); return false; } @@ -612,11 +609,6 @@ function get_active_users( $duration = 1 ) { return 0; } - // If single user mode. - if ( is_single_user() ) { - return 1; - } - // If blog user is disabled. if ( ! user_can_activitypub( Actors::BLOG_USER_ID ) ) { return (int) $count; @@ -632,11 +624,6 @@ function get_active_users( $duration = 1 ) { * @return int The total number of users. */ function get_total_users() { - // If single user mode. - if ( is_single_user() ) { - return 1; - } - $users = \get_users( array( 'capability__in' => array( 'activitypub' ), diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index 24030792e2..5a9df7e742 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -15,7 +15,6 @@ use function Activitypub\get_attribution_domains; use function Activitypub\get_rest_url_by_path; use function Activitypub\is_blog_public; -use function Activitypub\is_single_user; /** * Blog class. @@ -107,11 +106,7 @@ public function get_id() { * @return string The type of the object. */ public function get_type() { - if ( is_single_user() ) { - return 'Person'; - } else { - return 'Group'; - } + return 'Group'; } /** @@ -303,7 +298,7 @@ public function get_canonical_url() { * @return string|null The Moderators endpoint. */ public function get_moderators() { - if ( is_single_user() || 'Group' !== $this->get_type() ) { + if ( 'Group' !== $this->get_type() ) { return null; } @@ -316,7 +311,7 @@ public function get_moderators() { * @return string|null The attributedTo value. */ public function get_attributed_to() { - if ( is_single_user() || 'Group' !== $this->get_type() ) { + if ( 'Group' !== $this->get_type() ) { return null; } diff --git a/includes/rest/class-collections-controller.php b/includes/rest/class-collections-controller.php index b9ee7d686a..c76621772b 100644 --- a/includes/rest/class-collections-controller.php +++ b/includes/rest/class-collections-controller.php @@ -13,7 +13,6 @@ use function Activitypub\esc_hashtag; use function Activitypub\get_rest_url_by_path; -use function Activitypub\is_single_user; /** * Collections_Controller class. @@ -158,7 +157,7 @@ public function get_tags( $request, $user_id ) { public function get_featured( $request, $user_id ) { $posts = array(); - if ( is_single_user() || Actors::BLOG_USER_ID !== $user_id ) { + if ( Actors::BLOG_USER_ID !== $user_id ) { $sticky_posts = \get_option( 'sticky_posts' ); if ( $sticky_posts && is_array( $sticky_posts ) ) { diff --git a/includes/transformer/class-comment.php b/includes/transformer/class-comment.php index 338c40ae54..067260ebad 100644 --- a/includes/transformer/class-comment.php +++ b/includes/transformer/class-comment.php @@ -15,7 +15,6 @@ use function Activitypub\get_comment_ancestors; use function Activitypub\get_rest_url_by_path; -use function Activitypub\is_single_user; use function Activitypub\was_comment_received; /** @@ -208,10 +207,6 @@ protected function get_actor_object() { $blog_user = new Blog(); $this->actor_object = $blog_user; - if ( is_single_user() ) { - return $blog_user; - } - $user = Actors::get_by_id( $this->item->user_id ); if ( $user && ! is_wp_error( $user ) ) { diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index f8dd97aadf..2fcc572b86 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -20,7 +20,6 @@ use function Activitypub\get_content_warning; use function Activitypub\get_enclosures; use function Activitypub\get_rest_url_by_path; -use function Activitypub\is_single_user; use function Activitypub\site_supports_blocks; /** @@ -105,10 +104,6 @@ public function get_actor_object() { $blog_user = new Blog(); $this->actor_object = $blog_user; - if ( is_single_user() ) { - return $blog_user; - } - $user = Actors::get_by_id( $this->item->post_author ); if ( $user && ! is_wp_error( $user ) ) { From 839687651ab6ad1cb0e765543bd54ee9ac3678c2 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:24:55 +0000 Subject: [PATCH 12/17] Remove activitypub capability from users in blog mode When switching to blog-only mode, this update removes the 'activitypub' capability from all users who previously had it, ensuring that user capabilities are consistent with the new mode. --- includes/class-migration.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/includes/class-migration.php b/includes/class-migration.php index 0c37ddce04..939b87fd79 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -493,6 +493,19 @@ public static function migrate_actor_mode_to_capabilities() { // If site was in blog-only mode, set flag to disable users by default. if ( ACTIVITYPUB_BLOG_MODE === $actor_mode ) { \update_option( 'activitypub_disable_users_by_default', true ); + + // Remove activitypub capability from all existing users. + $users = \get_users( + array( + 'meta_key' => 'activitypub_capability', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => true, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + 'meta_compare' => '=', + ) + ); + + foreach ( $users as $user ) { + $user->remove_cap( 'activitypub' ); + } } // Clean up old actor mode option. From 2a4b34dc364569a63c7c14425b4fe16fdbd79664 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:29:17 +0000 Subject: [PATCH 13/17] Update user query to use capability__in for activitypub Replaces the meta_key/meta_value query with 'capability__in' => array('activitypub') when removing the activitypub capability from users. This improves query performance and aligns with WordPress best practices for capability checks. --- includes/class-migration.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/class-migration.php b/includes/class-migration.php index 939b87fd79..6b3b7395ec 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -497,9 +497,7 @@ public static function migrate_actor_mode_to_capabilities() { // Remove activitypub capability from all existing users. $users = \get_users( array( - 'meta_key' => 'activitypub_capability', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - 'meta_value' => true, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value - 'meta_compare' => '=', + 'capability__in' => array( 'activitypub' ), ) ); From dfb1c79fc39386676942b13d9a5a02e838536fc6 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:31:00 +0000 Subject: [PATCH 14/17] Remove single user mode checks from Blog and Opengraph Eliminated logic and comments related to 'single user' mode in Blog::get_type() and Opengraph::add_metadata(). This simplifies the code by removing special handling for single user blogs. --- includes/model/class-blog.php | 2 -- integration/class-opengraph.php | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index 5a9df7e742..ca38bb9c6b 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -101,8 +101,6 @@ public function get_id() { /** * Get the type of the object. * - * If the Blog is in "single user" mode, return "Person" instead of "Group". - * * @return string The type of the object. */ public function get_type() { diff --git a/integration/class-opengraph.php b/integration/class-opengraph.php index 113e92a13b..72d78e4a07 100644 --- a/integration/class-opengraph.php +++ b/integration/class-opengraph.php @@ -8,9 +8,7 @@ namespace Activitypub\Integration; use Activitypub\Collection\Actors; -use Activitypub\Model\Blog; -use function Activitypub\is_single_user; use function Activitypub\is_user_type_disabled; /** @@ -54,16 +52,6 @@ public static function add_prefixes( $prefixes ) { * @return array the updated metadata. */ public static function add_metadata( $metadata ) { - // Always show Blog-User if the Blog is in single user mode. - if ( is_single_user() ) { - $user = new Blog(); - - // Add WebFinger resource. - $metadata['fediverse:creator'] = $user->get_webfinger(); - - return $metadata; - } - if ( \is_author() ) { // Use the Author of the Archive-Page. $user_id = \get_queried_object_id(); From 7af1f6d49dbf0131fb2dc7a93bb9cb735e7b19ec Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:32:30 +0000 Subject: [PATCH 15/17] functions.php aktualisieren Co-authored-by: Konstantin Obenland --- includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/functions.php b/includes/functions.php index a2b74ba320..395b5040fc 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -431,7 +431,7 @@ function is_user_type_disabled( $type ) { * @return boolean Always returns false. */ function is_single_user() { - \_deprecated_function( __FUNCTION__, 'unreleased', '' ); + \_deprecated_function( __FUNCTION__, 'unreleased' ); return false; } From 5bec5eb23b86b397d7e9d698b78dff6e00c7c7b8 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:36:42 +0000 Subject: [PATCH 16/17] Refactor Blog class to use properties for type and posting restriction Moved the 'type' and 'posting_restricted_to_mods' values from methods to protected properties. Removed redundant methods and conditional checks, simplifying the class structure and improving clarity. --- includes/model/class-blog.php | 44 +++++++++++------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/includes/model/class-blog.php b/includes/model/class-blog.php index ca38bb9c6b..2a60f198ab 100644 --- a/includes/model/class-blog.php +++ b/includes/model/class-blog.php @@ -47,6 +47,20 @@ class Blog extends Actor { ), ); + /** + * The type of the Blog-Actor. + * + * @var string + */ + protected $type = 'Group'; + + /** + * Posting is restricted to moderators. + * + * @var bool|null True if posting is restricted to moderators, null if not applicable. + */ + protected $posting_restricted_to_mods = true; + /** * Constructor. */ @@ -98,15 +112,6 @@ public function get_id() { return \add_query_arg( 'author', $this->_id, \home_url( '/' ) ); } - /** - * Get the type of the object. - * - * @return string The type of the object. - */ - public function get_type() { - return 'Group'; - } - /** * Get the Username. * @@ -296,10 +301,6 @@ public function get_canonical_url() { * @return string|null The Moderators endpoint. */ public function get_moderators() { - if ( 'Group' !== $this->get_type() ) { - return null; - } - return get_rest_url_by_path( 'collections/moderators' ); } @@ -309,10 +310,6 @@ public function get_moderators() { * @return string|null The attributedTo value. */ public function get_attributed_to() { - if ( 'Group' !== $this->get_type() ) { - return null; - } - return get_rest_url_by_path( 'collections/moderators' ); } @@ -329,19 +326,6 @@ public function get_public_key() { ); } - /** - * Returns whether posting is restricted to mods. - * - * @return bool|null True if posting is restricted to mods, null if not applicable. - */ - public function get_posting_restricted_to_mods() { - if ( 'Group' === $this->get_type() ) { - return true; - } - - return null; - } - /** * Returns the Inbox-API-Endpoint. * From 084120116b280711f2685f11970d20ff373cdad5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 5 Nov 2025 21:45:52 +0000 Subject: [PATCH 17/17] Mark filter as deprecated in docblock Updated the docblock for the user type disabling filter to explicitly mark it as deprecated and recommend using capability management instead. --- includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/functions.php b/includes/functions.php index 395b5040fc..d46e4e0af0 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -415,7 +415,7 @@ function is_user_type_disabled( $type ) { /** * Allow plugins to disable user types for ActivityPub. * - * Note: This filter is deprecated. Use capability management instead. + * @deprecated unreleased Use capability management instead. * * @param boolean $disabled True if the user type is disabled, false otherwise. * @param string $type The User-Type.