From 118e4a7f22b998658754fea06cdc08799f15c5aa Mon Sep 17 00:00:00 2001
From: Konstantin Obenland
Date: Fri, 28 Feb 2025 10:52:28 -0600
Subject: [PATCH 01/18] Combine sanitization functions
---
CHANGELOG.md | 1 +
includes/class-sanitize.php | 70 +++++++++++++
includes/wp-admin/class-settings.php | 44 +--------
readme.txt | 1 +
tests/includes/class-test-sanitize.php | 132 +++++++++++++++++++++++++
5 files changed, 207 insertions(+), 41 deletions(-)
create mode 100644 includes/class-sanitize.php
create mode 100644 tests/includes/class-test-sanitize.php
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7af50a343..1c81d1e15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
* Upgrade script to fix Follower json representations with unescaped backslashes.
+* Centralized place for sanitization functions.
### Changed
diff --git a/includes/class-sanitize.php b/includes/class-sanitize.php
new file mode 100644
index 000000000..b2e588e1d
--- /dev/null
+++ b/includes/class-sanitize.php
@@ -0,0 +1,70 @@
+ $sanitized,
+ 'search_columns' => array( 'user_login', 'user_nicename' ),
+ 'number' => 1,
+ 'hide_empty' => true,
+ 'fields' => 'ID',
+ )
+ );
+
+ if ( $user->get_results() ) {
+ \add_settings_error(
+ 'activitypub_blog_identifier',
+ 'activitypub_blog_identifier',
+ \esc_html__( 'You cannot use an existing author’s name for the blog profile ID.', 'activitypub' )
+ );
+
+ return Blog::get_default_username();
+ }
+
+ return $sanitized;
+ }
+}
diff --git a/includes/wp-admin/class-settings.php b/includes/wp-admin/class-settings.php
index 41be113a6..60207936f 100644
--- a/includes/wp-admin/class-settings.php
+++ b/includes/wp-admin/class-settings.php
@@ -9,6 +9,7 @@
use Activitypub\Collection\Actors;
use Activitypub\Model\Blog;
+use Activitypub\Sanitize;
use function Activitypub\is_user_disabled;
/**
@@ -137,12 +138,7 @@ public static function register_settings() {
'description' => \__( 'Websites allowed to credit you.', 'activitypub' ),
'default' => \Activitypub\home_host(),
'sanitize_callback' => function ( $value ) {
- $value = explode( PHP_EOL, $value );
- $value = array_filter( array_map( 'trim', $value ) );
- $value = array_filter( array_map( 'esc_attr', $value ) );
- $value = implode( PHP_EOL, $value );
-
- return $value;
+ return implode( PHP_EOL, Sanitize::url_list( $value ) );
},
)
);
@@ -197,41 +193,7 @@ public static function register_settings() {
'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
'show_in_rest' => true,
'default' => Blog::get_default_username(),
- 'sanitize_callback' => function ( $value ) {
- // Hack to allow dots in the username.
- $parts = explode( '.', $value );
- $sanitized = array();
-
- foreach ( $parts as $part ) {
- $sanitized[] = \sanitize_title( $part );
- }
-
- $sanitized = implode( '.', $sanitized );
-
- // Check for login or nicename.
- $user = new \WP_User_Query(
- array(
- 'search' => $sanitized,
- 'search_columns' => array( 'user_login', 'user_nicename' ),
- 'number' => 1,
- 'hide_empty' => true,
- 'fields' => 'ID',
- )
- );
-
- if ( $user->results ) {
- add_settings_error(
- 'activitypub_blog_identifier',
- 'activitypub_blog_identifier',
- \esc_html__( 'You cannot use an existing author\'s name for the blog profile ID.', 'activitypub' ),
- 'error'
- );
-
- return Blog::get_default_username();
- }
-
- return $sanitized;
- },
+ 'sanitize_callback' => array( Sanitize::class, 'blog_identifier' ),
)
);
diff --git a/readme.txt b/readme.txt
index 7f9e1bb7e..ab6009b0a 100644
--- a/readme.txt
+++ b/readme.txt
@@ -132,6 +132,7 @@ For reasons of data protection, it is not possible to see the followers of other
= Unreleased =
* Added: Upgrade script to fix Follower json representations with unescaped backslashes.
+* Added: Centralized place for sanitization functions.
* Changed: Bumped minimum required WordPress version to 6.4.
* Changed: Use a later hook for Posts to get published to the Outbox, to get sure all `post_meta`s and `taxonomy`s are set stored properly.
* Changed: Use webfinger as author email for comments from the Fediverse.
diff --git a/tests/includes/class-test-sanitize.php b/tests/includes/class-test-sanitize.php
new file mode 100644
index 000000000..dc6c06a2b
--- /dev/null
+++ b/tests/includes/class-test-sanitize.php
@@ -0,0 +1,132 @@
+ array(
+ array(
+ 'https://example.com',
+ 'https://example.com',
+ 'not-a-url',
+ 'https://wordpress.org',
+ ),
+ array(
+ 'https://example.com',
+ 'http://not-a-url',
+ 'https://wordpress.org',
+ ),
+ ),
+ 'mixed_urls_in_string_whitespace' => array(
+ "https://example.com\nnot-a-url\nhttps://wordpress.org ",
+ array(
+ 'https://example.com',
+ 'http://not-a-url',
+ 'https://wordpress.org',
+ ),
+ ),
+ 'special_characters' => array(
+ array(
+ 'https://example.com/path with spaces ',
+ 'https://example.com/über/path',
+ 'https://example.com/path?param=value¶m2=value2#section',
+ ),
+ array(
+ 'https://example.com/path%20with%20spaces',
+ 'https://example.com/über/path',
+ 'https://example.com/path?param=value¶m2=value2#section',
+ ),
+ ),
+ 'empty_array' => array( array(), array() ),
+ );
+ }
+
+ /**
+ * Test url_list with various inputs.
+ *
+ * @dataProvider url_list_provider
+ * @covers ::url_list
+ *
+ * @param mixed $input Input value.
+ * @param array $expected Expected output.
+ */
+ public function test_url_list( $input, $expected ) {
+ $this->assertEquals( $expected, Sanitize::url_list( $input ) );
+ }
+
+ /**
+ * Data provider for blog identifier tests.
+ *
+ * @return array Test data.
+ */
+ public function blog_identifier_provider() {
+ return array(
+ 'simple_string' => array( 'test-Blog', 'test-blog' ),
+ 'with_spaces' => array( 'test blog', 'test-blog' ),
+ 'with_dots' => array( 'test.blog', 'test.blog' ),
+ 'special_chars' => array( 'test@#$%^&*blog', 'testblog' ),
+ 'multiple_dots' => array( 'test.blog.name', 'test.blog.name' ),
+ );
+ }
+
+ /**
+ * Test blog_identifier with various inputs.
+ *
+ * @dataProvider blog_identifier_provider
+ * @covers ::blog_identifier
+ *
+ * @param string $input Input value.
+ * @param string $expected Expected output.
+ */
+ public function test_blog_identifier( $input, $expected ) {
+ $this->assertEquals( $expected, Sanitize::blog_identifier( $input ) );
+ }
+
+ /**
+ * Test blog_identifier with an existing username.
+ *
+ * @covers ::blog_identifier
+ */
+ public function test_blog_identifier_with_existing_user() {
+ $user_id = self::factory()->user->create(
+ array(
+ 'user_login' => 'existing-user',
+ 'user_nicename' => 'test-nicename',
+ )
+ );
+
+ $result = Sanitize::blog_identifier( 'existing-user' );
+
+ $this->assertEquals( \Activitypub\Model\Blog::get_default_username(), $result );
+ $this->assertNotEmpty( get_settings_errors( 'activitypub_blog_identifier' ) );
+
+ // Reset.
+ $GLOBALS['wp_settings_errors'] = array();
+
+ $result = Sanitize::blog_identifier( 'test-nicename' );
+
+ $this->assertEquals( \Activitypub\Model\Blog::get_default_username(), $result );
+ $this->assertNotEmpty( get_settings_errors( 'activitypub_blog_identifier' ) );
+
+ \wp_delete_user( $user_id );
+ }
+}
From d3027b4564bd54bd0c859111c1bc450006245677 Mon Sep 17 00:00:00 2001
From: Konstantin Obenland
Date: Fri, 28 Feb 2025 11:33:42 -0600
Subject: [PATCH 02/18] Add host_list sanitization
---
includes/class-sanitize.php | 28 +++++++++++++++++
includes/wp-admin/class-settings.php | 4 +--
tests/includes/class-test-sanitize.php | 43 ++++++++++++++++++++++++++
3 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/includes/class-sanitize.php b/includes/class-sanitize.php
index b2e588e1d..c669490ee 100644
--- a/includes/class-sanitize.php
+++ b/includes/class-sanitize.php
@@ -32,6 +32,34 @@ public static function url_list( $value ) {
return \array_values( $value );
}
+ /**
+ * Sanitize a list of hosts.
+ *
+ * @param string $value The value to sanitize.
+ * @return string The sanitized list of hosts.
+ */
+ public static function host_list( $value ) {
+ $value = \explode( PHP_EOL, $value );
+ $value = \array_map(
+ function ( $host ) {
+ $host = \trim( $host );
+ $host = \strtolower( $host );
+ $host = \set_url_scheme( $host );
+ $host = \sanitize_url( $host, array( 'http', 'https' ) );
+
+ // Remove protocol.
+ if ( \str_contains( $host, 'http' ) ) {
+ $host = \wp_parse_url( $host, PHP_URL_HOST );
+ }
+
+ return \filter_var( $host, FILTER_VALIDATE_DOMAIN );
+ },
+ $value
+ );
+
+ return \implode( PHP_EOL, \array_filter( $value ) );
+ }
+
/**
* Sanitize a blog identifier.
*
diff --git a/includes/wp-admin/class-settings.php b/includes/wp-admin/class-settings.php
index 60207936f..42805140c 100644
--- a/includes/wp-admin/class-settings.php
+++ b/includes/wp-admin/class-settings.php
@@ -137,9 +137,7 @@ public static function register_settings() {
'type' => 'string',
'description' => \__( 'Websites allowed to credit you.', 'activitypub' ),
'default' => \Activitypub\home_host(),
- 'sanitize_callback' => function ( $value ) {
- return implode( PHP_EOL, Sanitize::url_list( $value ) );
- },
+ 'sanitize_callback' => array( Sanitize::class, 'host_list' ),
)
);
diff --git a/tests/includes/class-test-sanitize.php b/tests/includes/class-test-sanitize.php
index dc6c06a2b..514cdb5bc 100644
--- a/tests/includes/class-test-sanitize.php
+++ b/tests/includes/class-test-sanitize.php
@@ -73,6 +73,49 @@ public function test_url_list( $input, $expected ) {
$this->assertEquals( $expected, Sanitize::url_list( $input ) );
}
+ /**
+ * Data provider for host list tests.
+ *
+ * @return array Test data.
+ */
+ public function host_list_provider() {
+ return array(
+ 'single_valid_host' => array(
+ 'example.com',
+ 'example.com',
+ ),
+ 'multiple_valid_hosts' => array(
+ "ftp://example.com\nhttp://wordpress.org\nhttps://test.example.com",
+ "example.com\nwordpress.org\ntest.example.com",
+ ),
+ 'mixed_case_hosts' => array(
+ "ExAmPlE.cOm\nWoRdPrEsS.oRg",
+ "example.com\nwordpress.org",
+ ),
+ 'invalid_hosts' => array(
+ " not-a-domain\n\nexample.com\n\t@invalid.com",
+ "not-a-domain\nexample.com\ninvalid.com",
+ ),
+ 'empty_string' => array(
+ '',
+ '',
+ ),
+ );
+ }
+
+ /**
+ * Test host_list with various inputs.
+ *
+ * @dataProvider host_list_provider
+ * @covers ::host_list
+ *
+ * @param string $input Input value.
+ * @param string $expected Expected output.
+ */
+ public function test_host_list( $input, $expected ) {
+ $this->assertEquals( $expected, Sanitize::host_list( $input ) );
+ }
+
/**
* Data provider for blog identifier tests.
*
From a86918648578c073cacf5b3d818bcd1def462b62 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 7 Feb 2025 17:28:45 +0100
Subject: [PATCH 03/18] add settings
---
includes/wp-admin/class-admin.php | 217 ++++++++++++++++++++++++++++++
1 file changed, 217 insertions(+)
diff --git a/includes/wp-admin/class-admin.php b/includes/wp-admin/class-admin.php
index b23844350..6bd571413 100644
--- a/includes/wp-admin/class-admin.php
+++ b/includes/wp-admin/class-admin.php
@@ -117,6 +117,223 @@ public static function followers_list_page() {
}
}
+ /**
+ * Register ActivityPub settings
+ */
+ public static function register_settings() {
+ \register_setting(
+ 'activitypub',
+ 'activitypub_post_content_type',
+ array(
+ 'type' => 'string',
+ 'description' => \__( 'Use title and link, summary, full or custom content', 'activitypub' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'enum' => array(
+ 'title',
+ 'excerpt',
+ 'content',
+ ),
+ ),
+ ),
+ 'default' => 'content',
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_custom_post_content',
+ array(
+ 'type' => 'string',
+ 'description' => \__( 'Define your own custom post template', 'activitypub' ),
+ 'show_in_rest' => true,
+ 'default' => ACTIVITYPUB_CUSTOM_POST_CONTENT,
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_max_image_attachments',
+ array(
+ 'type' => 'integer',
+ 'description' => \__( 'Number of images to attach to posts.', 'activitypub' ),
+ 'default' => ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS,
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_object_type',
+ array(
+ 'type' => 'string',
+ 'description' => \__( 'The Activity-Object-Type', 'activitypub' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'enum' => array(
+ 'note',
+ 'wordpress-post-format',
+ ),
+ ),
+ ),
+ 'default' => ACTIVITYPUB_DEFAULT_OBJECT_TYPE,
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_use_hashtags',
+ array(
+ 'type' => 'boolean',
+ 'description' => \__( 'Add hashtags in the content as native tags and replace the #tag with the tag-link', 'activitypub' ),
+ 'default' => '0',
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_use_opengraph',
+ array(
+ 'type' => 'boolean',
+ 'description' => \__( 'Automatically add "fediverse:creator" OpenGraph tags for Authors and the Blog-User.', 'activitypub' ),
+ 'default' => '1',
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_support_post_types',
+ array(
+ 'type' => 'string',
+ 'description' => \esc_html__( 'Enable ActivityPub support for post types', 'activitypub' ),
+ 'show_in_rest' => true,
+ 'default' => array( 'post' ),
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_actor_mode',
+ array(
+ 'type' => 'integer',
+ 'description' => \__( 'Choose your preferred Actor-Mode.', 'activitypub' ),
+ 'default' => ACTIVITYPUB_ACTOR_MODE,
+ )
+ );
+
+ \register_setting(
+ 'activitypub',
+ 'activitypub_attribution_domains',
+ array(
+ 'type' => 'string',
+ 'description' => \__( 'Websites allowed to credit you.', 'activitypub' ),
+ 'default' => home_host(),
+ 'sanitize_callback' => function ( $value ) {
+ $value = explode( PHP_EOL, $value );
+ $value = array_filter( array_map( 'trim', $value ) );
+ $value = array_filter( array_map( 'esc_attr', $value ) );
+ $value = implode( PHP_EOL, $value );
+
+ return $value;
+ },
+ )
+ );
+
+ \register_setting(
+ 'activitypub',
+ 'activitypub_authorized_fetch',
+ array(
+ 'type' => 'boolean',
+ 'description' => \__( 'Require HTTP signature authentication.', 'activitypub' ),
+ 'default' => false,
+ )
+ );
+
+ \register_setting(
+ 'activitypub',
+ 'activitypub_mailer_new_follower',
+ array(
+ 'type' => 'boolean',
+ 'description' => \__( 'Send notifications via e-mail when a new follower is added.', 'activitypub' ),
+ 'default' => '0',
+ )
+ );
+ \register_setting(
+ 'activitypub',
+ 'activitypub_mailer_new_dm',
+ array(
+ 'type' => 'boolean',
+ 'description' => \__( 'Send notifications via e-mail when a direct message is received.', 'activitypub' ),
+ 'default' => '0',
+ )
+ );
+
+ // Blog-User Settings.
+ \register_setting(
+ 'activitypub_blog',
+ 'activitypub_blog_description',
+ array(
+ 'type' => 'string',
+ 'description' => \esc_html__( 'The Description of the Blog-User', 'activitypub' ),
+ 'show_in_rest' => true,
+ 'default' => '',
+ )
+ );
+ \register_setting(
+ 'activitypub_blog',
+ 'activitypub_blog_identifier',
+ array(
+ 'type' => 'string',
+ 'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
+ 'show_in_rest' => true,
+ 'default' => Blog::get_default_username(),
+ 'sanitize_callback' => function ( $value ) {
+ // Hack to allow dots in the username.
+ $parts = explode( '.', $value );
+ $sanitized = array();
+
+ foreach ( $parts as $part ) {
+ $sanitized[] = \sanitize_title( $part );
+ }
+
+ $sanitized = implode( '.', $sanitized );
+
+ // Check for login or nicename.
+ $user = new WP_User_Query(
+ array(
+ 'search' => $sanitized,
+ 'search_columns' => array( 'user_login', 'user_nicename' ),
+ 'number' => 1,
+ 'hide_empty' => true,
+ 'fields' => 'ID',
+ )
+ );
+
+ if ( $user->results ) {
+ add_settings_error(
+ 'activitypub_blog_identifier',
+ 'activitypub_blog_identifier',
+ \esc_html__( 'You cannot use an existing author\'s name for the blog profile ID.', 'activitypub' ),
+ 'error'
+ );
+
+ return Blog::get_default_username();
+ }
+
+ return $sanitized;
+ },
+ )
+ );
+ \register_setting(
+ 'activitypub_blog',
+ 'activitypub_header_image',
+ array(
+ 'type' => 'integer',
+ 'description' => \__( 'The Attachment-ID of the Sites Header-Image', 'activitypub' ),
+ 'default' => null,
+ )
+ );
+ }
+
+ /**
+ * Adds the ActivityPub settings to the Help tab.
+ */
+ public static function add_settings_help_tab() {
+ require_once ACTIVITYPUB_PLUGIN_DIR . 'includes/help.php';
+ }
+
/**
* Adds the follower list to the Help tab.
*/
From a0e95fcebc84f8dcd6e21a19e02cc4951bdfac89 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 28 Feb 2025 12:14:17 +0100
Subject: [PATCH 04/18] Add relay settings
---
includes/wp-admin/class-admin.php | 210 ---------------------------
includes/wp-admin/class-settings.php | 22 +++
2 files changed, 22 insertions(+), 210 deletions(-)
diff --git a/includes/wp-admin/class-admin.php b/includes/wp-admin/class-admin.php
index 6bd571413..9561ef7e0 100644
--- a/includes/wp-admin/class-admin.php
+++ b/includes/wp-admin/class-admin.php
@@ -117,216 +117,6 @@ public static function followers_list_page() {
}
}
- /**
- * Register ActivityPub settings
- */
- public static function register_settings() {
- \register_setting(
- 'activitypub',
- 'activitypub_post_content_type',
- array(
- 'type' => 'string',
- 'description' => \__( 'Use title and link, summary, full or custom content', 'activitypub' ),
- 'show_in_rest' => array(
- 'schema' => array(
- 'enum' => array(
- 'title',
- 'excerpt',
- 'content',
- ),
- ),
- ),
- 'default' => 'content',
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_custom_post_content',
- array(
- 'type' => 'string',
- 'description' => \__( 'Define your own custom post template', 'activitypub' ),
- 'show_in_rest' => true,
- 'default' => ACTIVITYPUB_CUSTOM_POST_CONTENT,
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_max_image_attachments',
- array(
- 'type' => 'integer',
- 'description' => \__( 'Number of images to attach to posts.', 'activitypub' ),
- 'default' => ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS,
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_object_type',
- array(
- 'type' => 'string',
- 'description' => \__( 'The Activity-Object-Type', 'activitypub' ),
- 'show_in_rest' => array(
- 'schema' => array(
- 'enum' => array(
- 'note',
- 'wordpress-post-format',
- ),
- ),
- ),
- 'default' => ACTIVITYPUB_DEFAULT_OBJECT_TYPE,
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_use_hashtags',
- array(
- 'type' => 'boolean',
- 'description' => \__( 'Add hashtags in the content as native tags and replace the #tag with the tag-link', 'activitypub' ),
- 'default' => '0',
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_use_opengraph',
- array(
- 'type' => 'boolean',
- 'description' => \__( 'Automatically add "fediverse:creator" OpenGraph tags for Authors and the Blog-User.', 'activitypub' ),
- 'default' => '1',
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_support_post_types',
- array(
- 'type' => 'string',
- 'description' => \esc_html__( 'Enable ActivityPub support for post types', 'activitypub' ),
- 'show_in_rest' => true,
- 'default' => array( 'post' ),
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_actor_mode',
- array(
- 'type' => 'integer',
- 'description' => \__( 'Choose your preferred Actor-Mode.', 'activitypub' ),
- 'default' => ACTIVITYPUB_ACTOR_MODE,
- )
- );
-
- \register_setting(
- 'activitypub',
- 'activitypub_attribution_domains',
- array(
- 'type' => 'string',
- 'description' => \__( 'Websites allowed to credit you.', 'activitypub' ),
- 'default' => home_host(),
- 'sanitize_callback' => function ( $value ) {
- $value = explode( PHP_EOL, $value );
- $value = array_filter( array_map( 'trim', $value ) );
- $value = array_filter( array_map( 'esc_attr', $value ) );
- $value = implode( PHP_EOL, $value );
-
- return $value;
- },
- )
- );
-
- \register_setting(
- 'activitypub',
- 'activitypub_authorized_fetch',
- array(
- 'type' => 'boolean',
- 'description' => \__( 'Require HTTP signature authentication.', 'activitypub' ),
- 'default' => false,
- )
- );
-
- \register_setting(
- 'activitypub',
- 'activitypub_mailer_new_follower',
- array(
- 'type' => 'boolean',
- 'description' => \__( 'Send notifications via e-mail when a new follower is added.', 'activitypub' ),
- 'default' => '0',
- )
- );
- \register_setting(
- 'activitypub',
- 'activitypub_mailer_new_dm',
- array(
- 'type' => 'boolean',
- 'description' => \__( 'Send notifications via e-mail when a direct message is received.', 'activitypub' ),
- 'default' => '0',
- )
- );
-
- // Blog-User Settings.
- \register_setting(
- 'activitypub_blog',
- 'activitypub_blog_description',
- array(
- 'type' => 'string',
- 'description' => \esc_html__( 'The Description of the Blog-User', 'activitypub' ),
- 'show_in_rest' => true,
- 'default' => '',
- )
- );
- \register_setting(
- 'activitypub_blog',
- 'activitypub_blog_identifier',
- array(
- 'type' => 'string',
- 'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
- 'show_in_rest' => true,
- 'default' => Blog::get_default_username(),
- 'sanitize_callback' => function ( $value ) {
- // Hack to allow dots in the username.
- $parts = explode( '.', $value );
- $sanitized = array();
-
- foreach ( $parts as $part ) {
- $sanitized[] = \sanitize_title( $part );
- }
-
- $sanitized = implode( '.', $sanitized );
-
- // Check for login or nicename.
- $user = new WP_User_Query(
- array(
- 'search' => $sanitized,
- 'search_columns' => array( 'user_login', 'user_nicename' ),
- 'number' => 1,
- 'hide_empty' => true,
- 'fields' => 'ID',
- )
- );
-
- if ( $user->results ) {
- add_settings_error(
- 'activitypub_blog_identifier',
- 'activitypub_blog_identifier',
- \esc_html__( 'You cannot use an existing author\'s name for the blog profile ID.', 'activitypub' ),
- 'error'
- );
-
- return Blog::get_default_username();
- }
-
- return $sanitized;
- },
- )
- );
- \register_setting(
- 'activitypub_blog',
- 'activitypub_header_image',
- array(
- 'type' => 'integer',
- 'description' => \__( 'The Attachment-ID of the Sites Header-Image', 'activitypub' ),
- 'default' => null,
- )
- );
- }
-
/**
* Adds the ActivityPub settings to the Help tab.
*/
diff --git a/includes/wp-admin/class-settings.php b/includes/wp-admin/class-settings.php
index 42805140c..b377b2bf9 100644
--- a/includes/wp-admin/class-settings.php
+++ b/includes/wp-admin/class-settings.php
@@ -171,6 +171,28 @@ public static function register_settings() {
)
);
+
+ \register_setting(
+ 'activitypub',
+ 'activitypub_relays',
+ array(
+ 'type' => 'array',
+ 'description' => \__( 'Relays', 'activitypub' ),
+ 'default' => array(),
+ 'sanitize_callback' => function ( $value ) {
+ if ( ! is_array( $value ) ) {
+ $value = explode( PHP_EOL, $value );
+ }
+
+ $value = array_map( 'trim', $value );
+ $value = array_filter( $value );
+ $value = array_map( 'esc_url', $value );
+
+ return $value;
+ },
+ )
+ );
+
// Blog-User Settings.
\register_setting(
'activitypub_blog',
From 85049d0329bbb34a0a1ce7f211a59ce1d12efc9e Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 28 Feb 2025 12:34:02 +0100
Subject: [PATCH 05/18] Add settings
---
includes/wp-admin/class-admin.php | 7 ----
includes/wp-admin/class-settings-fields.php | 43 ++++++++++++++++++++-
includes/wp-admin/class-settings.php | 1 -
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/includes/wp-admin/class-admin.php b/includes/wp-admin/class-admin.php
index 9561ef7e0..b23844350 100644
--- a/includes/wp-admin/class-admin.php
+++ b/includes/wp-admin/class-admin.php
@@ -117,13 +117,6 @@ public static function followers_list_page() {
}
}
- /**
- * Adds the ActivityPub settings to the Help tab.
- */
- public static function add_settings_help_tab() {
- require_once ACTIVITYPUB_PLUGIN_DIR . 'includes/help.php';
- }
-
/**
* Adds the follower list to the Help tab.
*/
diff --git a/includes/wp-admin/class-settings-fields.php b/includes/wp-admin/class-settings-fields.php
index d1749dbae..191e2eb41 100644
--- a/includes/wp-admin/class-settings-fields.php
+++ b/includes/wp-admin/class-settings-fields.php
@@ -51,6 +51,13 @@ public static function register_settings_fields() {
'activitypub_settings'
);
+ add_settings_section(
+ 'activitypub_server',
+ __( 'Server', 'activitypub' ),
+ '__return_empty_string',
+ 'activitypub_settings'
+ );
+
// Add settings fields.
add_settings_field(
'activitypub_actor_mode',
@@ -129,7 +136,16 @@ public static function register_settings_fields() {
__( 'Blocklist', 'activitypub' ),
array( self::class, 'render_blocklist_field' ),
'activitypub_settings',
- 'activitypub_general'
+ 'activitypub_server'
+ );
+
+ add_settings_field(
+ 'activitypub_relays',
+ __( 'Relays', 'activitypub' ),
+ array( self::class, 'render_relays_field' ),
+ 'activitypub_settings',
+ 'activitypub_server',
+ array( 'label_for' => 'activitypub_relays' )
);
add_settings_field(
@@ -137,7 +153,7 @@ public static function register_settings_fields() {
__( 'Outbox Retention Period', 'activitypub' ),
array( self::class, 'render_outbox_purge_days_field' ),
'activitypub_settings',
- 'activitypub_general',
+ 'activitypub_server',
array( 'label_for' => 'activitypub_outbox_purge_days' )
);
@@ -460,4 +476,27 @@ public static function render_authorized_fetch_field() {
+
+ Fediverse-Relay distributes content across instances, expanding reach, engagement, and discoverability, especially for smaller instances.', 'activitypub' ), 'default' ); ?>
+
+
+
+ Inbox-URLs (e.g. https://relay.example.com/inbox
) of the relays you want to use, one per line.', 'activitypub' ), array( 'strong' => array() ) ); ?>
+ relaylist.com or on FediDB.', 'activitypub' ), 'default' ); ?>
+
+
Date: Fri, 28 Feb 2025 13:28:01 +0100
Subject: [PATCH 06/18] Add tests
---
includes/class-dispatcher.php | 27 +++++++++++
tests/includes/class-test-dispatcher.php | 60 ++++++++++++++++++++++++
2 files changed, 87 insertions(+)
diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php
index 4ef43f886..88d68c13c 100644
--- a/includes/class-dispatcher.php
+++ b/includes/class-dispatcher.php
@@ -97,6 +97,9 @@ public static function process_outbox( $id ) {
// Send to mentioned and replied-to users. Everyone other than followers.
self::send_to_interactees( $activity, $actor->get__id(), $outbox_item );
+ // Send to relays.
+ self::send_to_relays( $activity, $actor, $outbox_item );
+
if ( self::should_send_to_followers( $activity, $actor, $outbox_item ) ) {
Scheduler::async_batch(
self::$callback,
@@ -405,4 +408,28 @@ protected static function should_send_to_followers( $activity, $actor, $outbox_i
*/
return apply_filters( 'activitypub_send_activity_to_followers', $send, $activity, $actor->get__id(), $outbox_item );
}
+
+ /**
+ * Add Inboxes of Relays.
+ *
+ * @param Activity $activity The ActivityPub Activity.
+ * @param \Activitypub\Model\User|\Activitypub\Model\Blog $actor The Actor object.
+ * @param \WP_Post $outbox_item The Outbox item.
+ */
+ public static function send_to_relays( $activity, $actor, $outbox_item ) {
+ // Check if follower endpoint is set.
+ $cc = $activity->get_cc() ?? array();
+ $to = $activity->get_to() ?? array();
+
+ $audience = array_merge( $cc, $to );
+
+ // Check if activity is public.
+ if ( ! in_array( 'https://www.w3.org/ns/activitystreams#Public', $audience, true ) ) {
+ return;
+ }
+
+ $relays = \get_option( 'activitypub_relays', array() );
+
+ self::send_to_inboxes( $relays, $outbox_item->ID );
+ }
}
diff --git a/tests/includes/class-test-dispatcher.php b/tests/includes/class-test-dispatcher.php
index cd24a5c42..ba02809f3 100644
--- a/tests/includes/class-test-dispatcher.php
+++ b/tests/includes/class-test-dispatcher.php
@@ -7,6 +7,7 @@
use Activitypub\Activity\Activity;
use Activitypub\Collection\Actors;
+use Activitypub\Collection\Outbox;
use Activitypub\Collection\Followers;
use Activitypub\Dispatcher;
@@ -171,6 +172,65 @@ function () use ( $code, $message ) {
remove_all_filters( 'pre_http_request' );
}
+ public function test_send_to_relays() {
+ global $wp_actions;
+
+ $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( '/' ) ) );
+ $fake_request = function () {
+ return new \WP_Error( 'test', 'test' );
+ };
+
+ add_filter( 'pre_http_request', $fake_request, 10, 3 );
+
+ Dispatcher::send_to_relays( $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+
+ // Test how often the request was sent.
+ $this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );
+
+ $wp_actions = null;
+
+ // Add a relay.
+ $relays = array( 'https://relay1.example.com/inbox' );
+ update_option( 'activitypub_relays', $relays );
+
+ Dispatcher::send_to_relays( $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+
+ // Test how often the request was sent.
+ $this->assertEquals( 1, did_action( 'activitypub_sent_to_inbox' ) );
+
+ $wp_actions = null;
+
+ // Add a relay.
+ $relays = array( 'https://relay1.example.com/inbox', 'https://relay2.example.com/inbox' );
+ update_option( 'activitypub_relays', $relays );
+
+ Dispatcher::send_to_relays( $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+
+ // Test how often the request was sent.
+ $this->assertEquals( 2, did_action( 'activitypub_sent_to_inbox' ) );
+
+ $wp_actions = null;
+
+ $private_activity = Outbox::get_activity( $outbox_item->ID );
+ $private_activity->set_to( null );
+ $private_activity->set_cc( null );
+
+ // Clone object.
+ $private_activity = clone $private_activity;
+
+ Dispatcher::send_to_relays( $private_activity, Actors::get_by_id( self::$user_id ), $outbox_item );
+
+ // Test how often the request was sent.
+ $this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );
+
+ \remove_filter( 'pre_http_request', $fake_request, 10 );
+
+ \delete_option( 'activitypub_relays' );
+ \wp_delete_post( $post_id );
+ \wp_delete_post( $outbox_item->ID );
+ }
+
/**
* Returns a mock of an Activity object.
*
From 60eb3db526c4abff5fffbb628940933c9d533e47 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 28 Feb 2025 13:28:51 +0100
Subject: [PATCH 07/18] fixed phpcs
---
includes/wp-admin/class-settings.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/includes/wp-admin/class-settings.php b/includes/wp-admin/class-settings.php
index 2291f66f0..73526d455 100644
--- a/includes/wp-admin/class-settings.php
+++ b/includes/wp-admin/class-settings.php
@@ -175,9 +175,9 @@ public static function register_settings() {
'activitypub',
'activitypub_relays',
array(
- 'type' => 'array',
- 'description' => \__( 'Relays', 'activitypub' ),
- 'default' => array(),
+ 'type' => 'array',
+ 'description' => \__( 'Relays', 'activitypub' ),
+ 'default' => array(),
'sanitize_callback' => function ( $value ) {
if ( ! is_array( $value ) ) {
$value = explode( PHP_EOL, $value );
From f59c9d6ffbc78ba6244b83d43c14862a8a038331 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 28 Feb 2025 13:29:59 +0100
Subject: [PATCH 08/18] Added changelog
---
CHANGELOG.md | 1 +
readme.txt | 1 +
2 files changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c81d1e15..7d1057ed7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Upgrade script to fix Follower json representations with unescaped backslashes.
* Centralized place for sanitization functions.
+* Support for sending Activities to ActivityPub Relays, to improve discoverability of public content.
### Changed
diff --git a/readme.txt b/readme.txt
index ab6009b0a..b2b5ba3cd 100644
--- a/readme.txt
+++ b/readme.txt
@@ -133,6 +133,7 @@ For reasons of data protection, it is not possible to see the followers of other
* Added: Upgrade script to fix Follower json representations with unescaped backslashes.
* Added: Centralized place for sanitization functions.
+* Added: Support for sending Activities to ActivityPub Relays, to improve discoverability of public content.
* Changed: Bumped minimum required WordPress version to 6.4.
* Changed: Use a later hook for Posts to get published to the Outbox, to get sure all `post_meta`s and `taxonomy`s are set stored properly.
* Changed: Use webfinger as author email for comments from the Fediverse.
From 5400ead7fad7a4dd20e5642e7cffcf1a4c376e59 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 28 Feb 2025 13:34:37 +0100
Subject: [PATCH 09/18] Fix PHPCS
---
tests/includes/class-test-dispatcher.php | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/includes/class-test-dispatcher.php b/tests/includes/class-test-dispatcher.php
index ba02809f3..d961c56e8 100644
--- a/tests/includes/class-test-dispatcher.php
+++ b/tests/includes/class-test-dispatcher.php
@@ -172,6 +172,11 @@ function () use ( $code, $message ) {
remove_all_filters( 'pre_http_request' );
}
+ /**
+ * Test send_to_relays.
+ *
+ * @covers ::send_to_relays
+ */
public function test_send_to_relays() {
global $wp_actions;
@@ -188,6 +193,7 @@ public function test_send_to_relays() {
// Test how often the request was sent.
$this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$wp_actions = null;
// Add a relay.
@@ -199,6 +205,7 @@ public function test_send_to_relays() {
// Test how often the request was sent.
$this->assertEquals( 1, did_action( 'activitypub_sent_to_inbox' ) );
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$wp_actions = null;
// Add a relay.
@@ -210,6 +217,7 @@ public function test_send_to_relays() {
// Test how often the request was sent.
$this->assertEquals( 2, did_action( 'activitypub_sent_to_inbox' ) );
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$wp_actions = null;
$private_activity = Outbox::get_activity( $outbox_item->ID );
From 4734d587d1a7b6977949879165d38fe8706cf5e1 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Fri, 28 Feb 2025 13:49:51 +0100
Subject: [PATCH 10/18] check if relay is empty
---
includes/class-dispatcher.php | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php
index 88d68c13c..c3dbebfa4 100644
--- a/includes/class-dispatcher.php
+++ b/includes/class-dispatcher.php
@@ -430,6 +430,10 @@ public static function send_to_relays( $activity, $actor, $outbox_item ) {
$relays = \get_option( 'activitypub_relays', array() );
+ if ( empty( $relays ) ) {
+ return;
+ }
+
self::send_to_inboxes( $relays, $outbox_item->ID );
}
}
From 53666012785cb4d1247aadfa5b568495b7f20df0 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Mon, 3 Mar 2025 08:34:43 +0100
Subject: [PATCH 11/18] use sanitize class
---
includes/wp-admin/class-settings.php | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/includes/wp-admin/class-settings.php b/includes/wp-admin/class-settings.php
index 73526d455..3a42587e2 100644
--- a/includes/wp-admin/class-settings.php
+++ b/includes/wp-admin/class-settings.php
@@ -178,17 +178,7 @@ public static function register_settings() {
'type' => 'array',
'description' => \__( 'Relays', 'activitypub' ),
'default' => array(),
- 'sanitize_callback' => function ( $value ) {
- if ( ! is_array( $value ) ) {
- $value = explode( PHP_EOL, $value );
- }
-
- $value = array_map( 'trim', $value );
- $value = array_filter( $value );
- $value = array_map( 'esc_url', $value );
-
- return $value;
- },
+ 'sanitize_callback' => array( Sanitize::class, 'url_list' ),
)
);
From be40065fd9db793ca86aa1f372ccd4b1b0d5eb2f Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Mon, 3 Mar 2025 09:19:53 +0100
Subject: [PATCH 12/18] use a more generic filter and update the relays feature
to use the filter
---
includes/class-dispatcher.php | 47 ++++++++++++++----------
integration/class-stream-connector.php | 35 ------------------
tests/includes/class-test-dispatcher.php | 24 +++++++-----
3 files changed, 42 insertions(+), 64 deletions(-)
diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php
index c3dbebfa4..37e45376e 100644
--- a/includes/class-dispatcher.php
+++ b/includes/class-dispatcher.php
@@ -49,12 +49,13 @@ public static function init() {
\add_action( 'activitypub_process_outbox', array( self::class, 'process_outbox' ) );
// Default filters to add Inboxes to sent to.
- \add_filter( 'activitypub_interactees_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
- \add_filter( 'activitypub_interactees_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
+ \add_filter( 'activitypub_custom_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
+ \add_filter( 'activitypub_custom_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
+ \add_filter( 'activitypub_custom_inboxes', array( self::class, 'add_inboxes_of_relays' ), 10, 3 );
// Fallback for `activitypub_send_to_inboxes` filter.
\add_filter(
- 'activitypub_interactees_inboxes',
+ 'activitypub_custom_inboxes',
function ( $inboxes, $actor_id, $activity ) {
/**
* Filters the list of interactees inboxes to send the Activity to.
@@ -63,9 +64,13 @@ function ( $inboxes, $actor_id, $activity ) {
* @param int $actor_id The actor ID.
* @param Activity $activity The ActivityPub Activity.
*
- * @deprecated 5.2.0 Use `activitypub_interactees_inboxes` instead.
+ * @deprecated 5.2.0 Use `activitypub_custom_inboxes` instead.
+ * @deprecated 5.4.0 Use `activitypub_custom_inboxes` instead.
*/
- return \apply_filters_deprecated( 'activitypub_send_to_inboxes', array( $inboxes, $actor_id, $activity ), '5.2.0', 'activitypub_interactees_inboxes' );
+ $inboxes = \apply_filters_deprecated( 'activitypub_send_to_inboxes', array( $inboxes, $actor_id, $activity ), '5.2.0', 'activitypub_custom_inboxes' );
+ $inboxes = \apply_filters_deprecated( 'activitypub_interactees_inboxes', array( $inboxes, $actor_id, $activity ), '5.4.0', 'activitypub_custom_inboxes' );
+
+ return $inboxes;
},
10,
3
@@ -95,10 +100,7 @@ public static function process_outbox( $id ) {
$activity = Outbox::get_activity( $outbox_item );
// Send to mentioned and replied-to users. Everyone other than followers.
- self::send_to_interactees( $activity, $actor->get__id(), $outbox_item );
-
- // Send to relays.
- self::send_to_relays( $activity, $actor, $outbox_item );
+ self::send_to_custom_inboxes( $activity, $actor->get__id(), $outbox_item );
if ( self::should_send_to_followers( $activity, $actor, $outbox_item ) ) {
Scheduler::async_batch(
@@ -253,13 +255,15 @@ private static function schedule_retry( $retries, $outbox_item_id, $attempt = 1
}
/**
- * Send an Activity to all followers and mentioned users.
+ * Send an Activity to a custom list of inboxes, like mentioned users or replied-to posts.
+ *
+ * For all custom implementations, please use the `activitypub_custom_inboxes` filter.
*
* @param Activity $activity The ActivityPub Activity.
* @param int $actor_id The actor ID.
* @param \WP_Post $outbox_item The WordPress object.
*/
- private static function send_to_interactees( $activity, $actor_id, $outbox_item = null ) {
+ private static function send_to_custom_inboxes( $activity, $actor_id, $outbox_item = null ) {
/**
* Filters the list of inboxes to send the Activity to.
*
@@ -267,7 +271,7 @@ private static function send_to_interactees( $activity, $actor_id, $outbox_item
* @param int $actor_id The actor ID.
* @param Activity $activity The ActivityPub Activity.
*/
- $inboxes = apply_filters( 'activitypub_interactees_inboxes', array(), $actor_id, $activity );
+ $inboxes = apply_filters( 'activitypub_custom_inboxes', array(), $actor_id, $activity );
$inboxes = array_unique( $inboxes );
$retries = self::send_to_inboxes( $inboxes, $outbox_item->ID );
@@ -412,11 +416,13 @@ protected static function should_send_to_followers( $activity, $actor, $outbox_i
/**
* Add Inboxes of Relays.
*
- * @param Activity $activity The ActivityPub Activity.
- * @param \Activitypub\Model\User|\Activitypub\Model\Blog $actor The Actor object.
- * @param \WP_Post $outbox_item The Outbox item.
+ * @param array $inboxes The list of Inboxes.
+ * @param int $actor_id The Actor-ID.
+ * @param Activity $activity The ActivityPub Activity.
+ *
+ * @return array The filtered Inboxes.
*/
- public static function send_to_relays( $activity, $actor, $outbox_item ) {
+ public static function add_inboxes_of_relays( $inboxes, $actor_id, $activity ) {
// Check if follower endpoint is set.
$cc = $activity->get_cc() ?? array();
$to = $activity->get_to() ?? array();
@@ -425,15 +431,18 @@ public static function send_to_relays( $activity, $actor, $outbox_item ) {
// Check if activity is public.
if ( ! in_array( 'https://www.w3.org/ns/activitystreams#Public', $audience, true ) ) {
- return;
+ return $inboxes;
}
$relays = \get_option( 'activitypub_relays', array() );
if ( empty( $relays ) ) {
- return;
+ return $inboxes;
}
- self::send_to_inboxes( $relays, $outbox_item->ID );
+ $inboxes = array_merge( $inboxes, $relays );
+ $inboxes = array_unique( $inboxes );
+
+ return $inboxes;
}
}
diff --git a/integration/class-stream-connector.php b/integration/class-stream-connector.php
index 8a7931a97..e59c6009f 100644
--- a/integration/class-stream-connector.php
+++ b/integration/class-stream-connector.php
@@ -129,41 +129,6 @@ public function callback_activitypub_notification_follow( $notification ) {
);
}
- /**
- * Callback for activitypub_send_to_inboxes.
- *
- * @param array $result The result of the remote post request.
- * @param string $inbox The inbox URL.
- * @param string $json The ActivityPub Activity JSON.
- * @param int $actor_id The actor ID.
- * @param int $outbox_item_id The Outbox item ID.
- */
- public function callback_activitypub_sent_to_inbox( $result, $inbox, $json, $actor_id, $outbox_item_id ) {
- if ( ! \is_wp_error( $result ) ) {
- return;
- }
-
- $outbox_item = \get_post( $outbox_item_id );
- $outbox_data = $this->prepare_outbox_data_for_response( $outbox_item );
-
- $this->log(
- // translators: 1: post title.
- sprintf( __( 'Outbox error for "%1$s"', 'activitypub' ), $outbox_data['title'] ),
- array(
- 'error' => wp_json_encode(
- array(
- 'inbox' => $inbox,
- 'code' => $result->get_error_code(),
- 'message' => $result->get_error_message(),
- )
- ),
- ),
- $outbox_data['id'],
- $outbox_data['type'],
- 'processed'
- );
- }
-
/**
* Callback for activitypub_outbox_processing_complete.
*
diff --git a/tests/includes/class-test-dispatcher.php b/tests/includes/class-test-dispatcher.php
index d961c56e8..722fb985e 100644
--- a/tests/includes/class-test-dispatcher.php
+++ b/tests/includes/class-test-dispatcher.php
@@ -104,11 +104,11 @@ public function test_process_outbox() {
* This test can be removed when the filter is removed.
*
* @covers ::maybe_add_inboxes_of_blog_user
- * @expectedDeprecated activitypub_send_to_inboxes
+ * @expectedDeprecated activitypub_interactees_inboxes
*/
public function test_deprecated_filter() {
add_filter(
- 'activitypub_send_to_inboxes',
+ 'activitypub_interactees_inboxes',
function ( $inboxes ) {
$inboxes[] = 'https://example.com/inbox';
@@ -116,10 +116,10 @@ function ( $inboxes ) {
}
);
- $inboxes = apply_filters( 'activitypub_interactees_inboxes', array(), 1, $this->get_activity_mock() );
+ $inboxes = apply_filters( 'activitypub_custom_inboxes', array(), 1, $this->get_activity_mock() );
$this->assertContains( 'https://example.com/inbox', $inboxes );
- remove_all_filters( 'activitypub_send_to_inboxes' );
+ remove_all_filters( 'activitypub_interactees_inboxes' );
}
/**
@@ -173,9 +173,9 @@ function () use ( $code, $message ) {
}
/**
- * Test send_to_relays.
+ * Test send_to_custom_inboxes.
*
- * @covers ::send_to_relays
+ * @covers ::send_to_custom_inboxes
*/
public function test_send_to_relays() {
global $wp_actions;
@@ -188,7 +188,11 @@ public function test_send_to_relays() {
add_filter( 'pre_http_request', $fake_request, 10, 3 );
- Dispatcher::send_to_relays( $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+ // Make `Dispatcher::send_to_custom_inboxes` a public method.
+ $send_to_custom_inboxes = new ReflectionMethod( Dispatcher::class, 'send_to_custom_inboxes' );
+ $send_to_custom_inboxes->setAccessible( true );
+
+ $send_to_custom_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );
@@ -200,7 +204,7 @@ public function test_send_to_relays() {
$relays = array( 'https://relay1.example.com/inbox' );
update_option( 'activitypub_relays', $relays );
- Dispatcher::send_to_relays( $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_custom_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 1, did_action( 'activitypub_sent_to_inbox' ) );
@@ -212,7 +216,7 @@ public function test_send_to_relays() {
$relays = array( 'https://relay1.example.com/inbox', 'https://relay2.example.com/inbox' );
update_option( 'activitypub_relays', $relays );
- Dispatcher::send_to_relays( $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_custom_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 2, did_action( 'activitypub_sent_to_inbox' ) );
@@ -227,7 +231,7 @@ public function test_send_to_relays() {
// Clone object.
$private_activity = clone $private_activity;
- Dispatcher::send_to_relays( $private_activity, Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_custom_inboxes->invoke( null, $private_activity, Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );
From 2384b9f36314dfb5fb3b289595f9becf8fcdb24f Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Mon, 3 Mar 2025 19:35:44 +0100
Subject: [PATCH 13/18] add `code` to allowlist
props @obenland
---
includes/wp-admin/class-settings-fields.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/wp-admin/class-settings-fields.php b/includes/wp-admin/class-settings-fields.php
index 0461cb5a6..8bc29f3b0 100644
--- a/includes/wp-admin/class-settings-fields.php
+++ b/includes/wp-admin/class-settings-fields.php
@@ -500,7 +500,7 @@ class="large-text"
rows="5"
>
- Inbox-URLs (e.g. https://relay.example.com/inbox
) of the relays you want to use, one per line.', 'activitypub' ), array( 'strong' => array() ) ); ?>
+ Inbox-URLs (e.g. https://relay.example.com/inbox
) of the relays you want to use, one per line.', 'activitypub' ), array( 'strong' => array(), 'code' => array() ) ); ?>
relaylist.com or on FediDB.', 'activitypub' ), 'default' ); ?>
Date: Mon, 3 Mar 2025 19:37:12 +0100
Subject: [PATCH 14/18] remove duplicate code
props @obenland
---
includes/class-dispatcher.php | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php
index 37e45376e..6cc49400e 100644
--- a/includes/class-dispatcher.php
+++ b/includes/class-dispatcher.php
@@ -440,9 +440,6 @@ public static function add_inboxes_of_relays( $inboxes, $actor_id, $activity ) {
return $inboxes;
}
- $inboxes = array_merge( $inboxes, $relays );
- $inboxes = array_unique( $inboxes );
-
- return $inboxes;
+ return array_merge( $inboxes, $relays );
}
}
From b611e096c7bd692ccfab2df881d9a6c796bfdcef Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Mon, 3 Mar 2025 19:39:01 +0100
Subject: [PATCH 15/18] move description below textbox
props @obenland
---
includes/wp-admin/class-settings-fields.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/includes/wp-admin/class-settings-fields.php b/includes/wp-admin/class-settings-fields.php
index 8bc29f3b0..d5ef32897 100644
--- a/includes/wp-admin/class-settings-fields.php
+++ b/includes/wp-admin/class-settings-fields.php
@@ -489,9 +489,6 @@ public static function render_authorized_fetch_field() {
public static function render_relays_field() {
$value = get_option( 'activitypub_relays', array() );
?>
-
- Fediverse-Relay distributes content across instances, expanding reach, engagement, and discoverability, especially for smaller instances.', 'activitypub' ), 'default' ); ?>
-
+
+ Fediverse-Relay distributes content across instances, expanding reach, engagement, and discoverability, especially for smaller instances.', 'activitypub' ), 'default' ); ?>
+
Inbox-URLs (e.g. https://relay.example.com/inbox
) of the relays you want to use, one per line.', 'activitypub' ), array( 'strong' => array(), 'code' => array() ) ); ?>
relaylist.com or on FediDB.', 'activitypub' ), 'default' ); ?>
From 553a1cdbde12667b4f53ac5a9351d3139ee60b1c Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Mon, 3 Mar 2025 19:39:42 +0100
Subject: [PATCH 16/18] Fix phpcs
---
includes/wp-admin/class-settings-fields.php | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/includes/wp-admin/class-settings-fields.php b/includes/wp-admin/class-settings-fields.php
index d5ef32897..e1f356ef8 100644
--- a/includes/wp-admin/class-settings-fields.php
+++ b/includes/wp-admin/class-settings-fields.php
@@ -500,7 +500,15 @@ class="large-text"
Fediverse-Relay distributes content across instances, expanding reach, engagement, and discoverability, especially for smaller instances.', 'activitypub' ), 'default' ); ?>
- Inbox-URLs (e.g. https://relay.example.com/inbox
) of the relays you want to use, one per line.', 'activitypub' ), array( 'strong' => array(), 'code' => array() ) ); ?>
+ Inbox-URLs (e.g. https://relay.example.com/inbox
) of the relays you want to use, one per line.', 'activitypub' ),
+ array(
+ 'strong' => array(),
+ 'code' => array(),
+ )
+ );
+ ?>
relaylist.com or on FediDB.', 'activitypub' ), 'default' ); ?>
Date: Tue, 4 Mar 2025 17:33:54 +0100
Subject: [PATCH 17/18] rename filter
---
includes/class-dispatcher.php | 20 ++++++++++----------
tests/includes/class-test-dispatcher.php | 2 +-
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php
index 6cc49400e..bb80b2db5 100644
--- a/includes/class-dispatcher.php
+++ b/includes/class-dispatcher.php
@@ -49,13 +49,13 @@ public static function init() {
\add_action( 'activitypub_process_outbox', array( self::class, 'process_outbox' ) );
// Default filters to add Inboxes to sent to.
- \add_filter( 'activitypub_custom_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
- \add_filter( 'activitypub_custom_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
- \add_filter( 'activitypub_custom_inboxes', array( self::class, 'add_inboxes_of_relays' ), 10, 3 );
+ \add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
+ \add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
+ \add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_of_relays' ), 10, 3 );
// Fallback for `activitypub_send_to_inboxes` filter.
\add_filter(
- 'activitypub_custom_inboxes',
+ 'activitypub_additional_inboxes',
function ( $inboxes, $actor_id, $activity ) {
/**
* Filters the list of interactees inboxes to send the Activity to.
@@ -64,11 +64,11 @@ function ( $inboxes, $actor_id, $activity ) {
* @param int $actor_id The actor ID.
* @param Activity $activity The ActivityPub Activity.
*
- * @deprecated 5.2.0 Use `activitypub_custom_inboxes` instead.
- * @deprecated 5.4.0 Use `activitypub_custom_inboxes` instead.
+ * @deprecated 5.2.0 Use `activitypub_additional_inboxes` instead.
+ * @deprecated 5.4.0 Use `activitypub_additional_inboxes` instead.
*/
- $inboxes = \apply_filters_deprecated( 'activitypub_send_to_inboxes', array( $inboxes, $actor_id, $activity ), '5.2.0', 'activitypub_custom_inboxes' );
- $inboxes = \apply_filters_deprecated( 'activitypub_interactees_inboxes', array( $inboxes, $actor_id, $activity ), '5.4.0', 'activitypub_custom_inboxes' );
+ $inboxes = \apply_filters_deprecated( 'activitypub_send_to_inboxes', array( $inboxes, $actor_id, $activity ), '5.2.0', 'activitypub_additional_inboxes' );
+ $inboxes = \apply_filters_deprecated( 'activitypub_interactees_inboxes', array( $inboxes, $actor_id, $activity ), '5.4.0', 'activitypub_additional_inboxes' );
return $inboxes;
},
@@ -257,7 +257,7 @@ private static function schedule_retry( $retries, $outbox_item_id, $attempt = 1
/**
* Send an Activity to a custom list of inboxes, like mentioned users or replied-to posts.
*
- * For all custom implementations, please use the `activitypub_custom_inboxes` filter.
+ * For all custom implementations, please use the `activitypub_additional_inboxes` filter.
*
* @param Activity $activity The ActivityPub Activity.
* @param int $actor_id The actor ID.
@@ -271,7 +271,7 @@ private static function send_to_custom_inboxes( $activity, $actor_id, $outbox_it
* @param int $actor_id The actor ID.
* @param Activity $activity The ActivityPub Activity.
*/
- $inboxes = apply_filters( 'activitypub_custom_inboxes', array(), $actor_id, $activity );
+ $inboxes = apply_filters( 'activitypub_additional_inboxes', array(), $actor_id, $activity );
$inboxes = array_unique( $inboxes );
$retries = self::send_to_inboxes( $inboxes, $outbox_item->ID );
diff --git a/tests/includes/class-test-dispatcher.php b/tests/includes/class-test-dispatcher.php
index 722fb985e..8b0c179ff 100644
--- a/tests/includes/class-test-dispatcher.php
+++ b/tests/includes/class-test-dispatcher.php
@@ -116,7 +116,7 @@ function ( $inboxes ) {
}
);
- $inboxes = apply_filters( 'activitypub_custom_inboxes', array(), 1, $this->get_activity_mock() );
+ $inboxes = apply_filters( 'activitypub_additional_inboxes', array(), 1, $this->get_activity_mock() );
$this->assertContains( 'https://example.com/inbox', $inboxes );
remove_all_filters( 'activitypub_interactees_inboxes' );
From 4b3f3a6e7da20c6c2e3a4ebd8468962b5bd78f92 Mon Sep 17 00:00:00 2001
From: Matthias Pfefferle
Date: Tue, 4 Mar 2025 18:58:00 +0100
Subject: [PATCH 18/18] rename function
---
includes/class-dispatcher.php | 4 ++--
tests/includes/class-test-dispatcher.php | 18 +++++++++---------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php
index bb80b2db5..6fdfbd707 100644
--- a/includes/class-dispatcher.php
+++ b/includes/class-dispatcher.php
@@ -100,7 +100,7 @@ public static function process_outbox( $id ) {
$activity = Outbox::get_activity( $outbox_item );
// Send to mentioned and replied-to users. Everyone other than followers.
- self::send_to_custom_inboxes( $activity, $actor->get__id(), $outbox_item );
+ self::send_to_additional_inboxes( $activity, $actor->get__id(), $outbox_item );
if ( self::should_send_to_followers( $activity, $actor, $outbox_item ) ) {
Scheduler::async_batch(
@@ -263,7 +263,7 @@ private static function schedule_retry( $retries, $outbox_item_id, $attempt = 1
* @param int $actor_id The actor ID.
* @param \WP_Post $outbox_item The WordPress object.
*/
- private static function send_to_custom_inboxes( $activity, $actor_id, $outbox_item = null ) {
+ private static function send_to_additional_inboxes( $activity, $actor_id, $outbox_item = null ) {
/**
* Filters the list of inboxes to send the Activity to.
*
diff --git a/tests/includes/class-test-dispatcher.php b/tests/includes/class-test-dispatcher.php
index 8b0c179ff..296bd63d2 100644
--- a/tests/includes/class-test-dispatcher.php
+++ b/tests/includes/class-test-dispatcher.php
@@ -173,9 +173,9 @@ function () use ( $code, $message ) {
}
/**
- * Test send_to_custom_inboxes.
+ * Test send_to_additional_inboxes.
*
- * @covers ::send_to_custom_inboxes
+ * @covers ::send_to_additional_inboxes
*/
public function test_send_to_relays() {
global $wp_actions;
@@ -188,11 +188,11 @@ public function test_send_to_relays() {
add_filter( 'pre_http_request', $fake_request, 10, 3 );
- // Make `Dispatcher::send_to_custom_inboxes` a public method.
- $send_to_custom_inboxes = new ReflectionMethod( Dispatcher::class, 'send_to_custom_inboxes' );
- $send_to_custom_inboxes->setAccessible( true );
+ // Make `Dispatcher::send_to_additional_inboxes` a public method.
+ $send_to_additional_inboxes = new ReflectionMethod( Dispatcher::class, 'send_to_additional_inboxes' );
+ $send_to_additional_inboxes->setAccessible( true );
- $send_to_custom_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_additional_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );
@@ -204,7 +204,7 @@ public function test_send_to_relays() {
$relays = array( 'https://relay1.example.com/inbox' );
update_option( 'activitypub_relays', $relays );
- $send_to_custom_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_additional_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 1, did_action( 'activitypub_sent_to_inbox' ) );
@@ -216,7 +216,7 @@ public function test_send_to_relays() {
$relays = array( 'https://relay1.example.com/inbox', 'https://relay2.example.com/inbox' );
update_option( 'activitypub_relays', $relays );
- $send_to_custom_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_additional_inboxes->invoke( null, $this->get_activity_mock(), Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 2, did_action( 'activitypub_sent_to_inbox' ) );
@@ -231,7 +231,7 @@ public function test_send_to_relays() {
// Clone object.
$private_activity = clone $private_activity;
- $send_to_custom_inboxes->invoke( null, $private_activity, Actors::get_by_id( self::$user_id ), $outbox_item );
+ $send_to_additional_inboxes->invoke( null, $private_activity, Actors::get_by_id( self::$user_id ), $outbox_item );
// Test how often the request was sent.
$this->assertEquals( 0, did_action( 'activitypub_sent_to_inbox' ) );