From a9029cc583223f2d97733cc1387e9253b2868641 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 28 Oct 2026 22:12:50 +0100 Subject: [PATCH] Deliver locally on subdirectory and plain-permalink installs --- .github/changelog/fix-local-inbox-rest-url | 4 +++ includes/class-dispatcher.php | 6 ++-- .../tests/includes/class-test-dispatcher.php | 29 +++++++++++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 .github/changelog/fix-local-inbox-rest-url diff --git a/.github/changelog/fix-local-inbox-rest-url b/.github/changelog/fix-local-inbox-rest-url new file mode 100644 index 0000000000..76c4c5a4df --- /dev/null +++ b/.github/changelog/fix-local-inbox-rest-url @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Activities between accounts on the same site are now delivered on sites installed in a subdirectory or using plain permalinks. diff --git a/includes/class-dispatcher.php b/includes/class-dispatcher.php index 7882ad8bc7..7d2264c680 100644 --- a/includes/class-dispatcher.php +++ b/includes/class-dispatcher.php @@ -330,9 +330,9 @@ private static function send_to_inboxes( $inboxes, $outbox_item_id ) { * @return array|\WP_Error The result in the format of a remote post response, or WP_Error on failure. */ private static function send_to_local_inbox( $inbox_url, $json ) { - // Parse the inbox URL to extract the REST route. - $path = \wp_parse_url( $inbox_url, PHP_URL_PATH ) ?? ''; - $rest_route = \preg_replace( '#^/' . \preg_quote( \rest_get_url_prefix(), '#' ) . '#', '', $path ); + // Core resolves the route for subdirectory installs and every permalink setting. + $url_request = \WP_REST_Request::from_url( $inbox_url ); + $rest_route = $url_request ? $url_request->get_route() : ''; /* * The inbox URL comes from a remote actor document, and signature verification is deferred below, diff --git a/tests/phpunit/tests/includes/class-test-dispatcher.php b/tests/phpunit/tests/includes/class-test-dispatcher.php index 8233f7f483..d1609999f6 100644 --- a/tests/phpunit/tests/includes/class-test-dispatcher.php +++ b/tests/phpunit/tests/includes/class-test-dispatcher.php @@ -831,12 +831,20 @@ public function test_local_inboxes_skip_http() { * The inbox URL comes from a remote actor document, and signature verification is deferred * for local deliveries, so any other route on this site must not be dispatched. * + * @dataProvider data_local_inbox_installs * @covers ::send_to_inboxes * @covers ::send_to_local_inbox + * + * @param string $structure The permalink structure. + * @param string $home_path The path WordPress is installed under. */ - public function test_local_delivery_only_reaches_inbox_routes() { - // Local delivery reads the route from the URL path, which plain permalinks do not have. - $this->set_permalink_structure( '/%postname%/' ); + public function test_local_delivery_only_reaches_inbox_routes( $structure, $home_path ) { + $this->set_permalink_structure( $structure ); + // The test config sets WP_HOME, which core applies on `option_home`, so filter after it. + $home = static function ( $url ) use ( $home_path ) { + return $url . $home_path; + }; + \add_filter( 'option_home', $home, 11 ); $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( '/' ) ) ); @@ -873,6 +881,7 @@ public function test_local_delivery_only_reaches_inbox_routes() { \remove_filter( 'rest_pre_dispatch', $record ); \remove_action( 'activitypub_sent_to_inbox', $collect ); + \remove_filter( 'option_home', $home, 11 ); $this->assertCount( 2, $dispatched, 'Only the two inboxes should be dispatched.' ); @@ -882,6 +891,20 @@ public function test_local_delivery_only_reaches_inbox_routes() { } } + /** + * Permalink settings and install paths that change the shape of a REST URL. + * + * @return array[] Permalink structure and install path. + */ + public function data_local_inbox_installs() { + return array( + 'pretty permalinks' => array( '/%postname%/', '' ), + 'plain permalinks' => array( '', '' ), + 'index.php' => array( '/index.php/%postname%/', '' ), + 'subdirectory' => array( '/%postname%/', '/blog' ), + ); + } + /** * Test that remote inboxes still use HTTP. *