From 2d09da5b0c71835617dc74349ec13ac08d598ff4 Mon Sep 17 00:00:00 2001 From: Andy Vandenberghe Date: Sun, 4 Oct 2026 21:51:10 +0200 Subject: [PATCH] Count "show all pages" topics after the forum list is built With rt_page_number on, display_recent_topics() ran the topic count that sets total_topics_limit before get_forum_list(). forum_ids was still null, so get_forums_visibility_sql() reduced the count to forums where the user has m_approve: 0 for most users, and a count of only their moderated forums for a moderator, which then truncated the list and broke the pagination. Run the count after get_forum_list() and its empty-list return. The sort order is set before both, as get_allowed_topics_sql() reads it. Closes #195 Co-Authored-By: Claude Opus 5.5 --- core/recenttopics.php | 29 ++++---- tests/core/topic_count_test.php | 118 ++++++++++++++++++++++++++++++++ tests/tests.md | 17 ++++- 3 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 tests/core/topic_count_test.php diff --git a/core/recenttopics.php b/core/recenttopics.php index 68dc026..74fca51 100644 --- a/core/recenttopics.php +++ b/core/recenttopics.php @@ -332,8 +332,23 @@ public function display_recent_topics($tpl_loopname = 'recent_topics', $context $this->excluded_topics = explode(',', $this->config['rt_anti_topics']); $min_topic_level = $this->config['rt_min_topic_level']; + $this->sort_topics = $this->config['rt_sort_start_time'] ? 'topic_time' : 'topic_last_post_time'; + // if user can set recent topic sorting order and it is set then use the preference + if ($this->auth->acl_get('u_rt_sort_start_time') && isset($this->user->data['user_rt_sort_start_time'])) + { + $this->sort_topics = $this->user->data['user_rt_sort_start_time'] ? 'topic_time' : 'topic_last_post_time'; + } + + $this->get_forum_list(); + // No forums to display + if (count($this->forum_ids) == 0) + { + return; + } + //limit number of pages to be shown // compute as product of topics per page and max number of pages. + // The count needs the forum list above, or it only counts m_approve forums (issue #195). $this->total_topics_limit = 0; if ((int) $this->config['rt_page_number'] == 0) { @@ -352,20 +367,6 @@ public function display_recent_topics($tpl_loopname = 'recent_topics', $context } - $this->sort_topics = $this->config['rt_sort_start_time'] ? 'topic_time' : 'topic_last_post_time'; - // if user can set recent topic sorting order and it is set then use the preference - if ($this->auth->acl_get('u_rt_sort_start_time') && isset($this->user->data['user_rt_sort_start_time'])) - { - $this->sort_topics = $this->user->data['user_rt_sort_start_time'] ? 'topic_time' : 'topic_last_post_time'; - } - - $this->get_forum_list(); - // No forums to display - if (count($this->forum_ids) == 0) - { - return; - } - $topics_count = $this->get_topic_list(); if (count($this->topic_list) == 0) diff --git a/tests/core/topic_count_test.php b/tests/core/topic_count_test.php new file mode 100644 index 0000000..aa79340 --- /dev/null +++ b/tests/core/topic_count_test.php @@ -0,0 +1,118 @@ +createMock(\phpbb\content_visibility::class); + $content_visibility->method('get_forums_visibility_sql') + ->willReturnCallback(function ($mode, $forum_ids) use (&$visibility_forum_ids) { + $visibility_forum_ids[] = $forum_ids; + return '1=1'; + }); + + $auth = $this->createMock(\phpbb\auth\auth::class); + $auth->method('acl_get')->willReturnCallback(function ($opt) { + return $opt === 'u_rt_view'; + }); + $auth->method('acl_getf')->willReturnCallback(function ($opt) { + return $opt === 'f_read' ? [1 => ['f_read' => 1], 2 => ['f_read' => 1]] : []; + }); + + $user = $this->createMock(\phpbb\user::class); + $user->data = ['user_id' => 2, 'user_rt_enable' => 1, 'is_registered' => true]; + $user->method('get_passworded_forums')->willReturn([]); + + $queried = []; + $db = $this->createMock(\phpbb\db\driver\driver_interface::class); + $db->method('sql_in_set')->willReturnCallback(function ($field, $ids) use (&$queried) { + if ($field === 'forum_id') + { + $queried = array_values($ids); + } + return '1=1'; + }); + $db->method('sql_build_query')->willReturn('SELECT 1'); + $db->method('sql_query')->willReturn('result'); + $db->method('sql_query_limit')->willReturn('result'); + $db->method('sql_fetchfield')->willReturn(0); + $db->method('sql_fetchrow')->willReturnCallback(function () use (&$queried) { + $forum_id = array_shift($queried); + return $forum_id === null ? false : ['forum_id' => $forum_id]; + }); + + $rt = new \avathar\recenttopics\core\recenttopics( + $auth, + $this->createMock(\phpbb\cache\service::class), + new \phpbb\config\config([ + 'rt_parents' => 0, + 'rt_location' => 'RT_TOP', + 'rt_unread_only' => 0, + 'rt_number' => 5, + 'rt_anti_topics' => '0', + 'rt_min_topic_level' => 0, + 'rt_page_number' => 1, + 'rt_page_numbermax' => 0, + 'rt_sort_start_time' => 0, + ]), + $this->createMock(\phpbb\language\language::class), + $content_visibility, + $db, + new \phpbb\event\dispatcher(), + $this->createMock(\phpbb\pagination::class), + $this->createMock(\phpbb\request\request_interface::class), + $this->createMock(\phpbb\template\template::class), + $user, + '/', + 'php', + $this->createMock(\phpbb\config\db_text::class) + ); + + $rt->display_recent_topics(); + + $this->assertNotEmpty($visibility_forum_ids, 'no topic query was built'); + foreach ($visibility_forum_ids as $forum_ids) + { + $forum_ids = array_values((array) $forum_ids); + sort($forum_ids); + $this->assertSame([1, 2], $forum_ids); + } + } +} +} diff --git a/tests/tests.md b/tests/tests.md index bc64b0d..1e19f90 100644 --- a/tests/tests.md +++ b/tests/tests.md @@ -43,6 +43,7 @@ tests/ │ └── page_controller_test.php Dedicated /rt and /rt/simple page controller ├── core/ │ ├── forum_list_test.php Which forums the list may draw topics from +│ ├── topic_count_test.php "Show all pages" count uses the forum list │ └── recenttopics_events_test.php Public event API contract (contrib/events.md) └── functional/ └── recenttopics_test.php End-to-end browser tests @@ -174,7 +175,21 @@ It builds `core\recenttopics` with a mocked `auth` (returning the readable forum --- -## 6. Functional tests (`functional/recenttopics_test.php`) +## 6. Page count (`core/topic_count_test.php`) + +**What this code does:** +With "Show all recent topic pages" (`rt_page_number`) on, `display_recent_topics()` counts the user's recent topics to set the page limit. The count must use the same forum list as the topic list itself. + +**What this test file does:** +It runs `display_recent_topics()` with mocked `auth`, `user`, `db` and `content_visibility`, a real event dispatcher, and `rt_page_number = 1`. The `content_visibility` mock records the forum ids each topic query is scoped to. + +| Test | Scenario | What it verifies | +|------|----------|-----------------| +| `test_page_count_query_uses_forum_list` | Readable forums 1 and 2; "Show all pages" on | Every topic query, including the page count, is scoped to forums 1 and 2. Before the fix the count ran before `get_forum_list()` and got `null`, so it only counted forums where the user has `m_approve` (#195) | + +--- + +## 7. Functional tests (`functional/recenttopics_test.php`) **What this code does:** These tests start a real phpBB installation (using the test framework's built-in install), enable the extension, and make HTTP requests using a real browser-like crawler (Symfony DomCrawler). They check the actual rendered HTML for specific elements.