Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions core/recenttopics.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
Expand Down
118 changes: 118 additions & 0 deletions tests/core/topic_count_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php
/**
*
* @package Recent Topics Extension
* @copyright (c) 2026 Andreas Vandenberghe
* @license GNU General Public License, version 2 (GPL-2.0)
*
* Tests for the "Show all recent topic pages" count in
* recenttopics::display_recent_topics().
*
* See: https://github.com/avatharbe/RecentTopics/issues/195
*/

// display_recent_topics() includes functions_display.php unless topic_status()
// already exists; the stub keeps the test independent of a board install.
namespace
{
if (!function_exists('topic_status'))
{
function topic_status($row, $replies, $unread_topic, &$folder_img, &$folder_alt, &$topic_type)
{
$folder_img = 'folder';
$folder_alt = 'TOPIC_READ';
$topic_type = '';
}
}
}

namespace avathar\recenttopics\tests\core
{

class topic_count_test extends \phpbb_test_case
{
/**
* With "Show all recent topic pages" on, every topic query, including the
* count that sets the page limit, must be scoped to the user's forum list
* (#195). Before the fix the count ran before get_forum_list() and got null,
* so it only counted forums where the user has m_approve.
*/
public function test_page_count_query_uses_forum_list()
{
$visibility_forum_ids = [];
$content_visibility = $this->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);
}
}
}
}
17 changes: 16 additions & 1 deletion tests/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
Loading