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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ Recent Topics for phpBB 3.3
Extension for phpBB to display recent topics on the index page.
Originally based on NV Recent Topics by Joas Schilling ([nickvergessen](https://github.com/nickvergessen)), later maintained by PayBas. Now maintained by [avathar](https://www.avathar.be).

**Version:** 3.0.7 (06/04/2026)
**Version:** 3.0.8 (15/04/2026)
[![Latest Stable Version](https://img.shields.io/github/v/release/avatharbe/RecentTopics)](https://github.com/avatharbe/RecentTopics/releases)

#### Requirements
- phpBB 3.3.0 or higher
- PHP 8.1 or higher

#### Features
- Recent (or unread) topics list on the index page
- Recent (or unread) topics list on the index page and viewforum page
- Standalone pages at `/app.php/rt` (full) and `/app.php/rt/simple` (for iframe embedding)
- Three display locations: Top, Bottom or Side
- User-overridable preferences via UCP (enable/disable, location, count, sort order, unread only)
Expand All @@ -25,7 +25,7 @@ Originally based on NV Recent Topics by Joas Schilling ([nickvergessen](https://
- Custom PHP events for extension developers

#### ACP Options
- Enable/disable on index page
- Enable/disable on index page and viewforum page (independent settings)
- Per-forum include/exclude (in ACP Forum Management)
- Pagination: page limit, show all pages toggle
- Minimum topic type level (normal/sticky/announcement/global)
Expand Down
39 changes: 39 additions & 0 deletions acp/recenttopics_module.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,29 @@ public function main($id, $mode)
$rt_enable = $request->variable('rt_enable', 0);
$config->set('rt_index', $rt_enable);

$rt_viewforum = $request->variable('rt_viewforum', 0);
$config->set('rt_viewforum', $rt_viewforum);

$rt_viewforum_location = $request->variable('rt_viewforum_location', '');
$old_vf_location = $config['rt_viewforum_location'];
$config->set('rt_viewforum_location', $rt_viewforum_location);

$rt_location = $request->variable('rt_location', '');
$old_location = $config['rt_location'];
$config->set('rt_location', $rt_location);

// Propagate location changes to users who still have the old default
/** @var \phpbb\db\driver\driver_interface $db */
$db = $phpbb_container->get('dbal.conn');
if ($rt_viewforum_location !== $old_vf_location)
{
$db->sql_query('UPDATE ' . USERS_TABLE . " SET user_rt_viewforum_location = '" . $db->sql_escape($rt_viewforum_location) . "' WHERE user_rt_viewforum_location = '" . $db->sql_escape($old_vf_location) . "'");
}
if ($rt_location !== $old_location)
{
$db->sql_query('UPDATE ' . USERS_TABLE . " SET user_rt_location = '" . $db->sql_escape($rt_location) . "' WHERE user_rt_location = '" . $db->sql_escape($old_location) . "'");
}

$rt_sort_start_time = $request->variable('rt_sort_start_time', false);
$config->set('rt_sort_start_time', $rt_sort_start_time);

Expand Down Expand Up @@ -173,6 +193,23 @@ public function main($id, $mode)
);
}

$vf_display_types = array (
'RT_TOP' => $language->lang('RT_TOP'),
'RT_BOTTOM' => $language->lang('RT_BOTTOM'),
);

foreach ($vf_display_types as $key => $display_type)
{
$template->assign_block_vars(
'vf_location_row',
array(
'VALUE' => $key,
'SELECTED' => ($config['rt_viewforum_location'] == $key) ? ' selected="selected"' : '',
'OPTION' => $display_type,
)
);
}

$topic_link_options = array(
0 => $language->lang('RT_TOPIC_LINK_FIRST'),
1 => $language->lang('RT_TOPIC_LINK_LAST'),
Expand Down Expand Up @@ -200,6 +237,7 @@ public function main($id, $mode)
'U_RT_PAGE' => $helper->route('avathar_recenttopicsav_page', [], true, false, \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL),
'U_RT_SIMPLE_PAGE' => $helper->route('avathar_recenttopicsav_simple', [], true, false, \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL),
'RT_INDEX' => (int) $config['rt_index'],
'RT_VIEWFORUM' => (int) $config['rt_viewforum'],
'RT_PAGE_NUMBER' => ($config['rt_page_number'] == '1') ? 'checked="checked"' : '',
'RT_PAGE_NUMBERMAX' => (int) $config['rt_page_numbermax'],
'RT_ANTI_TOPICS' => $config['rt_anti_topics'],
Expand Down Expand Up @@ -230,6 +268,7 @@ public function main($id, $mode)
'user_rt_sort_start_time' => (int) $config['rt_sort_start_time'] ,
'user_rt_unread_only' => (int) $config['rt_unread_only'],
'user_rt_location' => $config['rt_location'],
'user_rt_viewforum_location' => $config['rt_viewforum_location'],
'user_rt_number' => ((int) $config['rt_number'] > 0 ? (int) $config['rt_number'] : 5 )
);

Expand Down
46 changes: 29 additions & 17 deletions adm/style/acp_recenttopics.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@ <h1>{{ lang('RECENT_TOPICS') }}</h1>
<dt><label for="rt_enable">{{ lang('INDEX') }}</label><br /><span>{{ lang('RT_DISPLAY_INDEX') }}</span></dt>
<dd><input type="checkbox" name="rt_enable" id="rt_enable" value="1"{% if RT_INDEX %} checked="checked"{% endif %} /></dd>
</dl>

<dl>
<dt><label for="rt_viewforum">{{ lang('FORUM') }}</label><br /><span>{{ lang('RT_DISPLAY_VIEWFORUM') }}</span></dt>
<dd><input type="checkbox" name="rt_viewforum" id="rt_viewforum" value="1"{% if RT_VIEWFORUM %} checked="checked"{% endif %} /></dd>
</dl>
<dl>
<dt><label for="rt_viewforum_location">{{ lang('RT_VIEWFORUM_LOCATION') }}{{ lang('COLON') }}</label><br /><span>{{ lang('RT_VIEWFORUM_LOCATION_EXP') }}</span></dt>
<dd>
<select name="rt_viewforum_location" class="input" id="rt_viewforum_location">
{% for vf_location_row in loops.vf_location_row %}
<option value="{{ vf_location_row.VALUE }}"{{ vf_location_row.SELECTED }}>{{ vf_location_row.OPTION }}</option>
{% endfor %}
</select>
</dd>
</dl>

<dl>
<dt><label for="rt_page_numbermax">{{ lang('RT_PAGE_NUMBERMAX') }}{{ lang('COLON') }}</label><br /><span>{{ lang('RT_PAGE_NUMBERMAX_EXP') }}</span></dt>
Expand Down Expand Up @@ -63,22 +76,6 @@ <h1>{{ lang('RECENT_TOPICS') }}</h1>
{% endif %}
</fieldset>

<fieldset>
<legend>{{ lang('RT_PAGES') }}</legend>
<dl>
<dt><label for="rt_page_enable">{{ lang('RT_PAGE_ENABLE') }}</label><br /><span>{{ lang('RT_PAGE_ENABLE_EXP') }}</span></dt>
<dd><input type="checkbox" name="rt_page_enable" id="rt_page_enable" value="1"{% if RT_PAGE_ENABLE %} checked="checked"{% endif %} /></dd>
</dl>
<dl>
<dt><label>{{ lang('RT_PAGE') }}{{ lang('COLON') }}</label><br /><span>{{ lang('RT_PAGE_EXP') }}</span></dt>
<dd><a href="{{ U_RT_PAGE }}" target="_blank">{{ U_RT_PAGE }}</a></dd>
</dl>
<dl>
<dt><label>{{ lang('RT_SIMPLE_PAGE') }}{{ lang('COLON') }}</label><br /><span>{{ lang('RT_SIMPLE_PAGE_EXP') }}</span></dt>
<dd><a href="{{ U_RT_SIMPLE_PAGE }}" target="_blank">{{ U_RT_SIMPLE_PAGE }}</a></dd>
</dl>
</fieldset>

<fieldset>
<legend>{{ lang('RT_OVERRIDABLE') }}</legend>
<dl>
Expand Down Expand Up @@ -115,6 +112,21 @@ <h1>{{ lang('RECENT_TOPICS') }}</h1>

</fieldset>

<fieldset>
<legend>{{ lang('RT_PAGES') }}</legend>
<dl>
<dt><label for="rt_page_enable">{{ lang('RT_PAGE_ENABLE') }}</label><br /><span>{{ lang('RT_PAGE_ENABLE_EXP') }}</span></dt>
<dd><input type="checkbox" name="rt_page_enable" id="rt_page_enable" value="1"{% if RT_PAGE_ENABLE %} checked="checked"{% endif %} /></dd>
</dl>
<dl>
<dt><label>{{ lang('RT_PAGE') }}{{ lang('COLON') }}</label><br /><span>{{ lang('RT_PAGE_EXP') }}</span></dt>
<dd><a href="{{ U_RT_PAGE }}" target="_blank">{{ U_RT_PAGE }}</a></dd>
</dl>
<dl>
<dt><label>{{ lang('RT_SIMPLE_PAGE') }}{{ lang('COLON') }}</label><br /><span>{{ lang('RT_SIMPLE_PAGE_EXP') }}</span></dt>
<dd><a href="{{ U_RT_SIMPLE_PAGE }}" target="_blank">{{ U_RT_SIMPLE_PAGE }}</a></dd>
</dl>
</fieldset>

<fieldset>
<legend>{{ lang('RT_ADS_SETTINGS') }}</legend>
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"type": "phpbb-extension",
"description": "Recent Topics Extension for phpBB 3.3. Adds a list with recent topics to the index page.",
"homepage": "https://www.avathar.be",
"version": "3.0.7",
"time": "2026-04-06",
"version": "3.0.8",
"time": "2026-04-15",
"keywords": ["phpbb", "extension", "Recent Topics"],
"license": "GPL-2.0-only",
"authors": [
Expand Down
3 changes: 3 additions & 0 deletions contrib/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
### Changelog

- 3.0.8 (15/04/2026)
- [NEW] Added optional recent topics display on viewforum pages (#178) — independent enable/disable and placement (top/bottom) from index page, with per-user UCP override

- 3.0.7 (06/04/2026)
- [FIX] Fixed PHP 8 TypeError in sql_fetchfield() when "show all pages" pagination is enabled (#148)
- [FIX] Fixed "Undefined array key 1" warning in viewonline listener (#176)
Expand Down
59 changes: 38 additions & 21 deletions core/recenttopics.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,9 @@ public function __construct(auth $auth,

/**
* @param string $tpl_loopname
* @param string $context 'index' or 'viewforum'
*/
public function display_recent_topics($tpl_loopname = 'recent_topics')
public function display_recent_topics($tpl_loopname = 'recent_topics', $context = 'index')
{
if (!function_exists('topic_status'))
{
Expand Down Expand Up @@ -275,11 +276,21 @@ public function display_recent_topics($tpl_loopname = 'recent_topics')
$this->display_parent_forums = $this->config['rt_parents'];

//rt block location
$this->location = $this->config['rt_location'];
// if user can set location and it is set then use the preference
if ($this->auth->acl_get('u_rt_location') && isset($this->user->data['user_rt_location']))
if ($context === 'viewforum')
{
$this->location = $this->user->data['user_rt_location'];
$this->location = $this->config['rt_viewforum_location'];
if ($this->auth->acl_get('u_rt_location') && isset($this->user->data['user_rt_viewforum_location']))
{
$this->location = $this->user->data['user_rt_viewforum_location'];
}
}
else
{
$this->location = $this->config['rt_location'];
if ($this->auth->acl_get('u_rt_location') && isset($this->user->data['user_rt_location']))
{
$this->location = $this->user->data['user_rt_location'];
}
}

$this->unread_only = $this->config['rt_unread_only'];
Expand Down Expand Up @@ -398,23 +409,29 @@ public function display_recent_topics($tpl_loopname = 'recent_topics')
$vars = ['ads_index_code'];
extract($this->dispatcher->trigger_event('avathar.recenttopicsav.modify_ads_code', compact($vars)));

$this->template->assign_vars(
array(
'RT_SORT_START_TIME' => $this->sort_topics === 'topic_time',
'S_RECENT_TOPICS' => true,
'S_LOCATION_TOP' => $this->location == 'RT_TOP',
'S_LOCATION_BOTTOM' => $this->location == 'RT_BOTTOM',
'S_LOCATION_SIDE' => $this->location == 'RT_SIDE',
'S_RT_SIDE_SHOW_DATE' => !empty($this->config['rt_side_show_date']),
'NEWEST_POST_IMG' => $this->user->img('icon_topic_newest', 'VIEW_NEWEST_POST'),
'LAST_POST_IMG' => $this->user->img('icon_topic_latest', 'VIEW_LATEST_POST'),
'POLL_IMG' => $this->user->img('icon_topic_poll', 'TOPIC_POLL'),
'ADS_INDEX_CODE' => $ads_index_code,
'S_POSTLOVE' => $this->topic_likes_service !== null,
strtoupper($tpl_loopname) . '_DISPLAY' => true,
)
$location_prefix = ($context === 'viewforum') ? 'S_VF_LOCATION_' : 'S_LOCATION_';

$tpl_vars = array(
'RT_SORT_START_TIME' => $this->sort_topics === 'topic_time',
'S_RECENT_TOPICS' => true,
$location_prefix . 'TOP' => $this->location == 'RT_TOP',
$location_prefix . 'BOTTOM' => $this->location == 'RT_BOTTOM',
'S_RT_SIDE_SHOW_DATE' => !empty($this->config['rt_side_show_date']),
'NEWEST_POST_IMG' => $this->user->img('icon_topic_newest', 'VIEW_NEWEST_POST'),
'LAST_POST_IMG' => $this->user->img('icon_topic_latest', 'VIEW_LATEST_POST'),
'POLL_IMG' => $this->user->img('icon_topic_poll', 'TOPIC_POLL'),
'ADS_INDEX_CODE' => $ads_index_code,
'S_POSTLOVE' => $this->topic_likes_service !== null,
strtoupper($tpl_loopname) . '_DISPLAY' => true,
);

if ($context !== 'viewforum')
{
$tpl_vars['S_LOCATION_SIDE'] = $this->location == 'RT_SIDE';
}

$this->template->assign_vars($tpl_vars);

$this->fill_template($tpl_loopname, $topic_tracking_info, $topics_count);
}

Expand Down Expand Up @@ -943,7 +960,7 @@ private function fill_template($tpl_loopname, $topic_tracking_info, int $topics_
}
}
$pagination_url = append_sid($this->root_path . $this->user->page['page_name'], $append_params);
$this->pagination->generate_template_pagination($pagination_url, 'pagination',
$this->pagination->generate_template_pagination($pagination_url, 'rt_pagination',
$tpl_loopname . '_start', $topics_count, $this->topics_per_page, max(0, min((int) $this->rtstart, $this->total_topics_limit)));
$this->template->assign_vars(
array (
Expand Down
12 changes: 12 additions & 0 deletions event/listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public static function getSubscribedEvents()
{
return array(
'core.index_modify_page_title' => 'display_rt',
'core.viewforum_generate_page_after' => 'display_rt_viewforum',
'core.viewonline_overwrite_location' => 'viewonline_overwrite_location',
'core.acp_manage_forums_request_data' => 'acp_manage_forums_request_data',
'core.acp_manage_forums_initialise_data' => 'acp_manage_forums_initialise_data',
Expand All @@ -86,6 +87,17 @@ public function display_rt()
}
}

/**
* Display recent topics on viewforum page
*/
public function display_rt_viewforum()
{
if (isset($this->config['rt_viewforum']) && $this->config['rt_viewforum'])
{
$this->rt_functions->display_recent_topics('recent_topics', 'viewforum');
}
}

/**
* Show users viewing Recent Topics on the Who Is Online page
*
Expand Down
48 changes: 38 additions & 10 deletions event/ucp_listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public function ucp_prefs_get_data($event)
$event['data'], array(
'rt_enable' => $this->request->variable('rt_enable', (int) $this->user->data['user_rt_enable']),
'rt_location' => $this->request->variable('rt_location', $this->user->data['user_rt_location']),
'rt_viewforum_location' => $this->request->variable('rt_viewforum_location', $this->user->data['user_rt_viewforum_location']),
'rt_number' => $this->request->variable('rt_number', (int) $this->user->data['user_rt_number']),
'rt_sort_start_time' => $this->request->variable('rt_sort_start_time', (int) $this->user->data['user_rt_sort_start_time']),
'rt_unread_only' => $this->request->variable('rt_unread_only', (int) $this->user->data['user_rt_unread_only']),
Expand Down Expand Up @@ -161,6 +162,31 @@ public function ucp_prefs_get_data($event)
)
);
}

// Viewforum location (top/bottom only)
if ($this->config['rt_viewforum'])
{
$template_vars += array(
'A_RT_VF_LOCATION' => true,
);

$vf_display_types = array (
'RT_TOP' => $this->language->lang('RT_TOP'),
'RT_BOTTOM' => $this->language->lang('RT_BOTTOM'),
);

foreach ($vf_display_types as $key => $display_type)
{
$this->template->assign_block_vars(
'vf_location_row',
array(
'VALUE' => $key,
'SELECTED' => ($event['data']['rt_viewforum_location'] == $key) ? ' selected="selected"' : '',
'OPTION' => $display_type,
)
);
}
}
}

if ($this->auth->acl_get('u_rt_number'))
Expand Down Expand Up @@ -198,11 +224,12 @@ public function ucp_prefs_set_data($event)
{
$event['sql_ary'] = array_merge(
$event['sql_ary'], array(
'user_rt_enable' => $event['data']['rt_enable'],
'user_rt_location' => $event['data']['rt_location'],
'user_rt_number' => $event['data']['rt_number'],
'user_rt_sort_start_time' => $event['data']['rt_sort_start_time'],
'user_rt_unread_only' => $event['data']['rt_unread_only'],
'user_rt_enable' => $event['data']['rt_enable'],
'user_rt_location' => $event['data']['rt_location'],
'user_rt_viewforum_location' => $event['data']['rt_viewforum_location'],
'user_rt_number' => $event['data']['rt_number'],
'user_rt_sort_start_time' => $event['data']['rt_sort_start_time'],
'user_rt_unread_only' => $event['data']['rt_unread_only'],
)
);
}
Expand All @@ -215,11 +242,12 @@ public function ucp_register_set_data($event)
{

$sql_ary = array(
'user_rt_enable' => (int) $this->config['rt_index'],
'user_rt_sort_start_time' => (int) $this->config['rt_sort_start_time'] ,
'user_rt_unread_only' => (int) $this->config['rt_unread_only'],
'user_rt_location' => $this->config['rt_location'],
'user_rt_number' => ((int) $this->config['rt_number'] > 0 ? (int) $this->config['rt_number'] : 5 )
'user_rt_enable' => (int) $this->config['rt_index'],
'user_rt_sort_start_time' => (int) $this->config['rt_sort_start_time'],
'user_rt_unread_only' => (int) $this->config['rt_unread_only'],
'user_rt_location' => $this->config['rt_location'],
'user_rt_viewforum_location' => $this->config['rt_viewforum_location'],
'user_rt_number' => ((int) $this->config['rt_number'] > 0 ? (int) $this->config['rt_number'] : 5)
);

$sql = 'UPDATE ' . USERS_TABLE . '
Expand Down
3 changes: 3 additions & 0 deletions language/ar/info_acp_recenttopics.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
//global settings
'RT_GLOBAL_SETTINGS' => 'الإعدادات العامة',
'RT_DISPLAY_INDEX' => 'العرض في الصفحة الرئيسية ',
'RT_DISPLAY_VIEWFORUM' => 'العرض في صفحة المنتدى',
'RT_VIEWFORUM_LOCATION' => 'موقع العرض في صفحة المنتدى',
'RT_VIEWFORUM_LOCATION_EXP' => 'حدد مكان عرض أحدث المواضيع في صفحة المنتدى. هذا الإعداد مستقل عن الصفحة الرئيسية.',
'RT_NUMBER' => 'عدد المواضيع ',
'RT_NUMBER_EXP' => 'عدد المواضيع التي تريد عرضها.',
'RT_PAGE_NUMBER' => 'عرض جميع الصفحات ',
Expand Down
2 changes: 2 additions & 0 deletions language/ar/recenttopics_ucp.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
'RT_TOP' => 'الأعلى',
'RT_LOCATION' => 'مكان العرض ',
'RT_LOCATION_EXP' => 'حدد المكان لظهور أحدث المواضيع.',
'RT_VIEWFORUM_LOCATION' => 'موقع العرض في صفحة المنتدى',
'RT_VIEWFORUM_LOCATION_EXP' => 'حدد مكان عرض أحدث المواضيع في صفحة المنتدى.',
'RT_NUMBER' => 'عدد المواضيع ',
'RT_NUMBER_EXP' => 'عدد المواضيع التي تريد عرضها.',
'RT_SORT_START_TIME' => 'الترتيب حسب وقت إضافة الموضوع ',
Expand Down
Loading