From 8f427e742c0d822f9ef1f6d586bbe976e565830e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Thu, 15 Oct 2026 13:23:12 +0200 Subject: [PATCH] Keep blogrolls on password-protected pages private --- includes/class-index.php | 2 ++ includes/class-opml.php | 8 +++++--- readme.md | 1 + tests/test-index.php | 11 +++++++++++ tests/test-opml.php | 13 +++++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/includes/class-index.php b/includes/class-index.php index 3115757..30e1b11 100644 --- a/includes/class-index.php +++ b/includes/class-index.php @@ -109,6 +109,8 @@ public static function get_posts() { array( 'post_type' => array( 'post', 'page' ), 'post_status' => 'publish', + // A page behind a password is not public, and neither is its list. + 'has_password' => false, 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC', diff --git a/includes/class-opml.php b/includes/class-opml.php index 801ce6c..7b2bb5f 100644 --- a/includes/class-opml.php +++ b/includes/class-opml.php @@ -589,8 +589,9 @@ public static function render() { // A page whose lists are all unlisted has no file of its own, and // neither has one that is asked for a list it does not hold. It is // then a page without a blogroll: the root still answers with the - // directory, every other page simply loads. - if ( $post && ! self::requested_groups( $post, $anchor ) ) { + // directory, every other page simply loads. So does a page behind + // a password, until the visitor has entered it. + if ( $post && ( \post_password_required( $post ) || ! self::requested_groups( $post, $anchor ) ) ) { $post = null; } $posts = ( ! $post && ( $directory || $following || self::is_blogroll_root() ) ) ? Index::get_posts() : array(); @@ -736,7 +737,8 @@ public static function title( $post ) { */ public static function discovery_link() { $post = Index::queried_post(); - if ( $post ) { + // A page behind a password does not give its list away. + if ( $post && ! \post_password_required( $post ) ) { $title = self::title( $post ); if ( self::listed_groups( $post ) ) { self::print_links( self::opml_url( $post ), \get_permalink( $post ), $title ); diff --git a/readme.md b/readme.md index 847d806..5046f23 100644 --- a/readme.md +++ b/readme.md @@ -140,6 +140,7 @@ Project and support maintained on github at [pfefferle/wordpress-blockroll](http * Apps that show who you follow can now find the feeds of your blogrolls at `/.well-known/following.opml`, as described in [FollowSpec](https://followspec.org/). If the SocialSpec plugin is active, the feeds are added to its list instead. See #47. * Without any blogroll, `/.well-known/recommendations.opml` now answers with an empty list instead of the home page. * Apps on other sites can now read every blogroll file in the browser, not only the list of followed feeds. +* A blogroll on a page behind a password stays behind it: it is no longer in the lists of the site, and its file only opens once the password was entered. ### 2.1.1 diff --git a/tests/test-index.php b/tests/test-index.php index 5a53706..5b40a63 100644 --- a/tests/test-index.php +++ b/tests/test-index.php @@ -40,6 +40,17 @@ public function test_get_posts_returns_only_published_tagged() { $this->assertSame( array( $id ), $found ); } + public function test_get_posts_leaves_out_password_protected_posts() { + $id = self::factory()->post->create( array( 'post_content' => self::BLOCK ) ); + self::factory()->post->create( + array( + 'post_content' => self::BLOCK, + 'post_password' => 'secret', + ) + ); + $this->assertSame( array( $id ), wp_list_pluck( \Blockroll\Index::get_posts(), 'ID' ) ); + } + public function test_has_blogroll_reads_the_content() { $id = self::factory()->post->create( array( 'post_content' => self::BLOCK ) ); // Simulate content that was never synced: drop the term. diff --git a/tests/test-opml.php b/tests/test-opml.php index 26807d3..c6cbf8b 100644 --- a/tests/test-opml.php +++ b/tests/test-opml.php @@ -494,6 +494,19 @@ public function test_singular_without_block_has_no_discovery_link() { $this->assertSame( '', ob_get_clean() ); } + public function test_password_protected_page_has_no_discovery_link() { + $id = self::factory()->post->create( + array( + 'post_content' => self::BLOCK, + 'post_password' => 'secret', + ) + ); + $this->go_to( get_permalink( $id ) ); + ob_start(); + \Blockroll\Opml::discovery_link(); + $this->assertSame( '', ob_get_clean() ); + } + const TWO_ANCHORED_BLOCKS = ''; public function test_directory_lists_individual_blogrolls_instead_of_the_page_file() {