Skip to content

Commit 37b9d7f

Browse files
authored
Refactor actor blocking with unified API (#2097)
1 parent f01dc70 commit 37b9d7f

File tree

9 files changed

+1044
-40
lines changed

9 files changed

+1044
-40
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: changed
3+
4+
Refactor actor blocking with unified API for better maintainability

includes/class-moderation.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,46 @@ public static function get_site_blocks() {
271271
);
272272
}
273273

274+
/**
275+
* Check if an actor is blocked by user or site-wide.
276+
*
277+
* @param string $actor_uri Actor URI to check.
278+
* @param int $user_id Optional. User ID to check user blocks for. Defaults to 0 (site-wide only).
279+
* @return bool True if blocked, false otherwise.
280+
*/
281+
public static function is_actor_blocked( $actor_uri, $user_id = 0 ) {
282+
if ( ! $actor_uri ) {
283+
return false;
284+
}
285+
286+
// Check site-wide blocks.
287+
$site_blocks = self::get_site_blocks();
288+
if ( \in_array( $actor_uri, $site_blocks['actors'], true ) ) {
289+
return true;
290+
}
291+
292+
// Check site-wide domain blocks.
293+
$actor_domain = \wp_parse_url( $actor_uri, PHP_URL_HOST );
294+
if ( $actor_domain && \in_array( $actor_domain, $site_blocks['domains'], true ) ) {
295+
return true;
296+
}
297+
298+
// Check user-specific blocks if user_id is provided.
299+
if ( $user_id > 0 ) {
300+
$user_blocks = self::get_user_blocks( $user_id );
301+
if ( \in_array( $actor_uri, $user_blocks['actors'], true ) ) {
302+
return true;
303+
}
304+
305+
// Check user-specific domain blocks.
306+
if ( $actor_domain && \in_array( $actor_domain, $user_blocks['domains'], true ) ) {
307+
return true;
308+
}
309+
}
310+
311+
return false;
312+
}
313+
274314
/**
275315
* Check activity against blocklists.
276316
*

includes/collection/class-actors.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,36 @@ private static function prepare_custom_post_type( $actor ) {
850850
);
851851
}
852852

853+
/**
854+
* Normalize actor identifier to a URI.
855+
*
856+
* Handles webfinger addresses, URLs without schemes, objects, and arrays.
857+
*
858+
* @param string|object|array $actor Actor URI, webfinger address, actor object, or array.
859+
* @return string|null Normalized actor URI or null if unable to resolve.
860+
*/
861+
public static function normalize_identifier( $actor ) {
862+
$actor = object_to_uri( $actor );
863+
if ( ! is_string( $actor ) ) {
864+
return null;
865+
}
866+
867+
$actor = \trim( $actor, '@' );
868+
869+
// If it's an email-like webfinger address, resolve it.
870+
if ( \filter_var( $actor, FILTER_VALIDATE_EMAIL ) ) {
871+
$resolved = \Activitypub\Webfinger::resolve( $actor );
872+
return \is_wp_error( $resolved ) ? null : object_to_uri( $resolved );
873+
}
874+
875+
// If it's a URL without scheme, add https://.
876+
if ( empty( \wp_parse_url( $actor, PHP_URL_SCHEME ) ) ) {
877+
$actor = \esc_url_raw( 'https://' . \ltrim( $actor, '/' ) );
878+
}
879+
880+
return $actor;
881+
}
882+
853883
/**
854884
* Return the public key for a given actor.
855885
*

includes/wp-admin/table/class-blocked-actors.php

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,24 +122,20 @@ public function process_action() {
122122
return;
123123
}
124124

125-
$profile = \sanitize_text_field( \wp_unslash( $_REQUEST['activitypub-profile'] ) );
126-
$profile = \trim( $profile, '@' );
127-
128-
if ( \filter_var( $profile, FILTER_VALIDATE_EMAIL ) ) {
129-
$remote_actor = Webfinger::resolve( $profile );
130-
if ( ! \is_wp_error( $remote_actor ) ) {
131-
$profile = object_to_uri( $remote_actor );
132-
}
133-
} elseif ( empty( \wp_parse_url( $profile, PHP_URL_SCHEME ) ) ) {
134-
// Add scheme if missing.
135-
$profile = \esc_url_raw( 'https://' . \ltrim( $profile, '/' ) );
125+
$original = \sanitize_text_field( \wp_unslash( $_REQUEST['activitypub-profile'] ) );
126+
$profile = Actors::normalize_identifier( $original );
127+
if ( ! $profile ) {
128+
/* translators: %s: Account profile that could not be blocked */
129+
\add_settings_error( 'activitypub', 'blocked', \sprintf( \__( 'Unable to block actor “%s”. Please verify the account exists and try again.', 'activitypub' ), \esc_html( $original ) ) );
130+
$redirect_to = \add_query_arg( 'resource', $original, $redirect_to );
131+
break;
136132
}
137133

138134
$result = Moderation::add_user_block( $this->user_id, Moderation::TYPE_ACTOR, $profile );
139135
if ( \is_wp_error( $result ) ) {
140136
/* translators: %s: Account profile that could not be blocked */
141-
\add_settings_error( 'activitypub', 'blocked', \sprintf( \__( 'Unable to block actor “%s”. Please verify the account exists and try again.', 'activitypub' ), \esc_html( $profile ) ) );
142-
$redirect_to = \add_query_arg( 'resource', $profile, $redirect_to );
137+
\add_settings_error( 'activitypub', 'blocked', \sprintf( \__( 'Unable to block actor “%s”. Please verify the account exists and try again.', 'activitypub' ), \esc_html( $original ) ) );
138+
$redirect_to = \add_query_arg( 'resource', $original, $redirect_to );
143139
} else {
144140
\add_settings_error( 'activitypub', 'blocked', \__( 'Actor blocked.', 'activitypub' ), 'success' );
145141
}

includes/wp-admin/table/class-following.php

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,41 +123,28 @@ public function process_action() {
123123
return;
124124
}
125125

126-
$profile = \sanitize_text_field( \wp_unslash( $_REQUEST['activitypub-profile'] ) );
127-
$profile = \trim( $profile, '@' );
128-
129-
if ( \filter_var( $profile, FILTER_VALIDATE_EMAIL ) ) {
130-
$remote_actor = Webfinger::resolve( $profile );
131-
if ( ! \is_wp_error( $remote_actor ) ) {
132-
$original = $profile;
133-
$profile = object_to_uri( $remote_actor );
134-
}
135-
} elseif ( empty( \wp_parse_url( $profile, PHP_URL_SCHEME ) ) ) {
136-
// Add scheme if missing.
137-
$profile = \esc_url_raw( 'https://' . \ltrim( $profile, '/' ) );
138-
}
139-
140-
// Check if user has blocked the account.
141-
if ( \in_array( $profile, Moderation::get_user_blocks( $this->user_id )['actors'], true ) ) {
126+
$original = \sanitize_text_field( \wp_unslash( $_REQUEST['activitypub-profile'] ) );
127+
$profile = Actors::normalize_identifier( $original );
128+
if ( ! $profile ) {
142129
/* translators: %s: Account profile that could not be followed */
143-
\add_settings_error( 'activitypub', 'followed', \sprintf( \__( 'Unable to follow account “%s”. The account is blocked.', 'activitypub' ), \esc_html( $profile ) ) );
144-
$redirect_to = \add_query_arg( 'resource', $original ?? $profile, $redirect_to );
130+
\add_settings_error( 'activitypub', 'followed', \sprintf( \__( 'Unable to follow account “%s”. Please verify the account exists and try again.', 'activitypub' ), \esc_html( $profile ) ) );
131+
$redirect_to = \add_query_arg( 'resource', $original, $redirect_to );
145132
break;
146133
}
147134

148-
// Check if site has blocked the account.
149-
if ( \in_array( $profile, Moderation::get_site_blocks()['actors'], true ) ) {
135+
// Check if actor is blocked.
136+
if ( Moderation::is_actor_blocked( $profile, $this->user_id ) ) {
150137
/* translators: %s: Account profile that could not be followed */
151-
\add_settings_error( 'activitypub', 'followed', \sprintf( \__( 'Unable to follow account “%s”. The account is blocked site-wide.', 'activitypub' ), \esc_html( $profile ) ) );
152-
$redirect_to = \add_query_arg( 'resource', $original ?? $profile, $redirect_to );
138+
\add_settings_error( 'activitypub', 'followed', \sprintf( \__( 'Unable to follow account “%s”. The account is blocked.', 'activitypub' ), \esc_html( $profile ) ) );
139+
$redirect_to = \add_query_arg( 'resource', $original, $redirect_to );
153140
break;
154141
}
155142

156143
$result = follow( $profile, $this->user_id );
157144
if ( \is_wp_error( $result ) ) {
158145
/* translators: %s: Account profile that could not be followed */
159146
\add_settings_error( 'activitypub', 'followed', \sprintf( \__( 'Unable to follow account “%s”. Please verify the account exists and try again.', 'activitypub' ), \esc_html( $profile ) ) );
160-
$redirect_to = \add_query_arg( 'resource', $original ?? $profile, $redirect_to );
147+
$redirect_to = \add_query_arg( 'resource', $original, $redirect_to );
161148
} else {
162149
\add_settings_error( 'activitypub', 'followed', \__( 'Account followed.', 'activitypub' ), 'success' );
163150
}

0 commit comments

Comments
 (0)