Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0520fd5
Initial total redesign from title prefixes to topic tags
iMattPro Sep 19, 2026
ce0dd54
Updates and tweaky fixes and more tests
iMattPro Sep 19, 2026
e47df67
Add UCP listeners assuming we get these events in the core
iMattPro Sep 19, 2026
3a78f49
Ensure topic tags stay with a topic after being moved to other forums
iMattPro Sep 20, 2026
a1398e0
Add orphan handling
iMattPro Sep 20, 2026
6f716ef
Fix CSS issues
iMattPro Sep 20, 2026
393ffe0
Fix tests
iMattPro Sep 20, 2026
5b05fc9
Fix dates
iMattPro Sep 20, 2026
caf3bb7
Refactor functional tests
iMattPro Sep 20, 2026
d94fd00
Performance improvements pass
iMattPro Sep 20, 2026
797ba46
Revert handling of tags when all users posts moved to a new forum
iMattPro Sep 20, 2026
ded6748
CSS improvements
iMattPro Sep 20, 2026
db7c63c
Avoid no-op filter links for global topics
iMattPro Sep 20, 2026
3c4ef93
Small code fixes and improvements
iMattPro Sep 20, 2026
bac6367
Fix tests
iMattPro Sep 20, 2026
1f8a7ee
Styling fixes in the ACP
iMattPro Sep 20, 2026
a3f5ea3
Prevent disabled tags from appearing as filter choices
iMattPro Sep 20, 2026
30280e6
Add subtle depth effect to tags so they’r enot so flat
iMattPro Sep 21, 2026
d4dc4c1
Add tooltips to tags
iMattPro Sep 21, 2026
e0a8c32
Added multibyte prefix test coverage using Japanese text
iMattPro Sep 21, 2026
3983d76
Fix MSSQL - as usual
iMattPro Sep 21, 2026
ce9955d
Handling for 4-byte emojis
iMattPro Sep 21, 2026
215f7e0
Enforce a 50 char limit for topic prefix/tags
iMattPro Sep 21, 2026
f713670
Make all three stateless text utilities static
iMattPro Sep 22, 2026
d9e7d99
Rename to Topic Tags
iMattPro Sep 22, 2026
d20a8c6
Loosen phpBB version requirements and add notes about
iMattPro Sep 22, 2026
857b2d8
Default panel shows only enabled tags available in forum
iMattPro Sep 23, 2026
cb6242f
Implement lightweight retained-tag tooltip
iMattPro Sep 23, 2026
16c8785
Adjust topic tags in search profile
iMattPro Sep 24, 2026
2980294
Mark version as “dev” for now [ci skip]
iMattPro Sep 24, 2026
43253cc
Migrate stored for last post subject titles
iMattPro Sep 25, 2026
362dc14
Disable tag filtering in search results
iMattPro Sep 25, 2026
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

### 2.0.0-dev - TBD

- Renamed phpBB Topic Prefixes to phpBB Topic Tags and replaced single text prefixes embedded in titles with relational topic metadata.
- Added multiple color-coded tags per topic with automatic badge text contrast and badges throughout forum, topic, MCP, and search displays.
- Added centralized ACP management for tag text, color, order, status, and availability across multiple forums.
- Added accessible tag selection when creating topics or editing their first posts.
- Added clickable forum filters that can combine multiple tags using AND matching.
- Preserved tags through topic moves, splits, forks, and merges, including tags retained after moving into forums where they are unavailable for new assignments.
- Added a safe, restartable upgrade that converts legacy prefixes and assignments into tags while removing matching prefix text from topic titles and first-post subjects.
- Raised minimum requirements to phpBB 3.3.0 and PHP 7.2.
- Note: implementations for showing tags on UCP main page, bookmarks, and subscriptions will take effect after phpBB adds new proposed events (targeted for 3.3.19).

### 1.0.2 - 2025-12-17

- Strengthened compatibility with both phpBB3 and phpBB4-alpha-1.
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
# phpBB Topic Prefixes
# phpBB Topic Tags

This is the repository for the development of the Topic Prefixes phpBB extension.
This is the repository for development of phpBB Topic Tags, formerly named Topic Prefixes.

[![Build Status](https://github.com/phpbb-extensions/topicprefixes/actions/workflows/tests.yml/badge.svg)](https://github.com/phpbb-extensions/topicprefixes/actions)
[![codecov](https://codecov.io/gh/phpbb-extensions/topicprefixes/branch/master/graph/badge.svg?token=Dc0GWOeQWj)](https://codecov.io/gh/phpbb-extensions/topicprefixes)

The phpBB Topic Prefixes extension allows phpBB board administrators to create topic prefix tags that users can easily add to their topic titles. Features include:
The phpBB Topic Tags extension provides administrator-curated topic tags. Tags are structured topic metadata and are displayed separately from topic titles as colored badges. Features include:

- Create unique and unlimited prefixes for each forum in your phpBB board.
- Admins can sort, enable and disable prefix tags.
- Users will see a topic prefix menu when creating a new topic in a forum that uses topic prefixes.
- Topic prefixes can easily be changed when a user is editing their original first post.
- Because the topic prefix is added directly to the topic title, if an admin disables or deletes a topic prefix, existing topic titles using the deleted prefix will not be changed.
- Create, edit, order, enable, disable, and color topic tags.
- Make each tag available in one or more forums.
- Assign multiple tags while creating a topic or editing its first post.
- Display readable colored badges in topic lists, topic pages, and search results.
- Filter forum topic lists by one or more tags using AND semantics.
- Upgrade existing title prefixes safely into relational tag assignments.
- This is the same extension currently in use at phpbb.com in the Extensions and Styles in development forums.

📦 [Download](https://www.phpbb.com/customise/db/extension/topicprefixes/) the latest release of this extension.
Expand Down
2 changes: 1 addition & 1 deletion acp/topic_prefixes_module.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function main()
$lang->add_lang('acp/forums');
$lang->add_lang('acp_topic_prefixes', 'phpbb/topicprefixes');
$this->tpl_name = 'acp_topic_prefixes';
$this->page_title = 'TOPIC_PREFIXES';
$this->page_title = 'TOPIC_TAGS';

$admin_controller = $phpbb_container->get('phpbb.topicprefixes.admin_controller');
$admin_controller->set_u_action($this->u_action)->main();
Expand Down
74 changes: 72 additions & 2 deletions adm/style/acp_topic_prefixes.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,80 @@
font-weight: 900;
}

.tp-toggle-on:before {
.tp-toggle-on::before {
content: "\f205";
}

.tp-toggle-off:before {
.tp-toggle-off::before {
content: "\f204";
}

.topic-tag {
background-image: linear-gradient(to top, transparent, rgba(255, 255, 255, 0.35));
border-radius: 3px;
display: inline-block;
font-weight: 600;
line-height: 1.5;
padding: 1px 7px;
}

.topic-tags-empty {
text-align: center;
}

.topic-tag-forums {
line-height: 1.8;
}

.topic-tag-forum {
display: inline-block;
white-space: nowrap;
}

.topic-tag-forum-separator {
color: #6B7278;
margin-right: 0.5em;
}

.topic-tag-color,
.topic-tag-color:hover,
.topic-tag-color:focus {
background: #FFFFFF;
border: 1px solid #AFAEAA;
border-radius: 4px;
box-sizing: border-box;
color: inherit;
cursor: pointer;
height: 24px;
margin: 2px 0;
padding: 3px;
width: 48px;
}

.topic-tag-color:hover {
border-color: #666666;
}

.topic-tag-color:focus {
border-color: #BC2A4D;
box-shadow: 0 0 0 1px #BC2A4D;
outline: none;
}

.topic-tag-color::-webkit-color-swatch-wrapper {
padding: 0;
}

.topic-tag-color::-webkit-color-swatch {
border: 0;
border-radius: 2px;
}

.topic-tag-color::-moz-color-swatch {
border: 0;
border-radius: 2px;
}

#forum_ids {
min-width: 22em;
}
135 changes: 73 additions & 62 deletions adm/style/acp_topic_prefixes.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,81 @@

<a id="maincontent"></a>

<h1>{{ lang('TOPIC_PREFIXES') }}</h1>
<h1>{{ lang('TOPIC_TAGS') }}</h1>
<p>{{ lang('TOPIC_TAGS_EXPLAIN') }}</p>

<p>{{ lang('TOPIC_PREFIXES_EXPLAIN') }}</p>
<table class="table1 zebra-table fixed-width-table">
<thead>
<tr>
<th>{{ lang('TOPIC_TAG') }}</th>
<th style="width:50%">{{ lang('TOPIC_TAG_FORUMS') }}</th>
<th>{{ lang('TOPIC_TAG_ENABLED') }}</th>
<th>{{ lang('ACTION') }}</th>
</tr>
</thead>
<tbody>
{% for tag in loops.tags %}
<tr>
<td><span class="topic-tag" style="background-color: {{ tag.TAG_COLOR }}; color: {{ tag.TAG_TEXT_COLOR }}">{{ tag.TAG_NAME }}</span></td>
<td>
{% if tag.FORUM_NAMES %}
<span class="topic-tag-forums" role="list">
{% for forum_name in tag.FORUM_NAMES %}
<span class="topic-tag-forum" role="listitem">{{ forum_name }}{% if not loop.last %}<span class="topic-tag-forum-separator" aria-hidden="true"> &middot;</span>{% endif %}</span>
{% endfor %}
</span>
{% else %}
{{ lang('TOPIC_TAG_NO_FORUMS') }}
{% endif %}
</td>
<td>
<a href="{{ tag.U_TOGGLE }}" class="topic-tag-toggle" title="{{ lang('TOPIC_TAG_TOGGLE_STATE') }}" data-ajax="tp_toggle">
<i class="fa fa-fw tp-toggle-{{ tag.TAG_ENABLED ? 'on' : 'off' }} acp-icon"></i>
</a>
</td>
<td class="actions">
<span class="up-disabled" style="display:none">{{ ICON_MOVE_UP_DISABLED }}</span>
<span class="up"><a href="{{ tag.U_MOVE_UP }}" data-ajax="row_up">{{ ICON_MOVE_UP }}</a></span>
<span class="down-disabled" style="display:none">{{ ICON_MOVE_DOWN_DISABLED }}</span>
<span class="down"><a href="{{ tag.U_MOVE_DOWN }}" data-ajax="row_down">{{ ICON_MOVE_DOWN }}</a></span>
<a href="{{ tag.U_EDIT }}">{{ ICON_EDIT }}</a>
<a href="{{ tag.U_DELETE }}">{{ ICON_DELETE }}</a>
</td>
</tr>
{% else %}
<tr><td colspan="4" class="topic-tags-empty">{{ lang('TOPIC_TAGS_EMPTY') }}</td></tr>
{% endfor %}
</tbody>
</table>

<form id="acp_topic_prefixes_forum" method="post" action="{{ U_ACTION }}">
<fieldset>
<dl>
<dt><label for="select_forum">{{ lang('SELECT_FORUM') ~ lang('COLON') }}</label></dt>
<dd>
<select id="select_forum" name="forum_id"><option value="0">{{ lang('SELECT_FORUM') }}</option>{{ S_FORUM_OPTIONS }}</select>
<input type="submit" value="{{ lang('GO') }}" class="button2">
</dd>
</dl>
</fieldset>
</form>

{% if FORUM_ID %}

<table class="table1 zebra-table fixed-width-table">
<thead>
<tr>
<th style="width:75%">{{ lang('TOPIC_PREFIX_TAG') }}</th>
<th>{{ lang('TOPIC_PREFIX_ENABLED') }}</th>
<th>{{ lang('ACTION') }}</th>
</tr>
</thead>
<tbody>
{% for prefix in loops.prefixes %}
<tr>
<td><strong>{{ prefix.PREFIX_TAG }}</strong></td>
<td>
<a href="{{ prefix.U_EDIT }}" style="text-decoration: none" title="{{ lang('TOPIC_PREFIX_TOGGLE_STATE') }}" data-ajax="tp_toggle">
<i class="fa fa-fw tp-toggle-{{ prefix.PREFIX_ENABLED ? 'on' : 'off' }} acp-icon"></i>
</a>
</td>
<td class="actions">
<span class="up-disabled" style="display:none;">{{ ICON_MOVE_UP_DISABLED }}</span>
<span class="up"><a href="{{ prefix.U_MOVE_UP }}" data-ajax="row_up">{{ ICON_MOVE_UP }}</a></span>
<span class="down-disabled" style="display:none;">{{ ICON_MOVE_DOWN_DISABLED }}</span>
<span class="down"><a href="{{ prefix.U_MOVE_DOWN }}" data-ajax="row_down">{{ ICON_MOVE_DOWN }}</a></span>
<a href="{{ prefix.U_DELETE }}" data-ajax="row_delete">{{ ICON_DELETE }}</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="3" style="text-align: center;">{{ lang('TOPIC_PREFIXES_EMPTY') }}</td>
</tr>
{% endfor %}
</tbody>
</table>

<form id="acp_add_topic_prefix" method="post" action="{{ U_ACTION }}">
<fieldset class="quick">
{{ lang('CREATE_TOPIC_PREFIX') ~ lang('COLON') }}
<input type="text" name="prefix_tag" value="" maxlength="200" placeholder="{{ lang('TOPIC_PREFIX_PLACEHOLDER') }}">
<input class="button2" name="submit" type="submit" value="{{ lang('SUBMIT') }}">
<input type="hidden" name="action" value="add">
<input type="hidden" name="forum_id" value="{{ FORUM_ID }}">
{{ S_FORM_TOKEN }}
{{ S_HIDDEN_FIELDS }}
</fieldset>
</form>

{% endif %}
<form id="acp_topic_tag" method="post" action="{{ U_ACTION }}">
<fieldset>
<legend>{{ lang(S_EDIT_TAG ? 'EDIT_TOPIC_TAG' : 'CREATE_TOPIC_TAG') }}</legend>
<dl>
<dt><label for="tag_name">{{ lang('TOPIC_TAG_TEXT') ~ lang('COLON') }}</label></dt>
<dd><input type="text" id="tag_name" name="tag_name" value="{{ TAG_NAME }}" maxlength="50" required></dd>
</dl>
<dl>
<dt><label for="tag_color">{{ lang('TOPIC_TAG_COLOR') ~ lang('COLON') }}</label><br><span>{{ lang('TOPIC_TAG_COLOR_EXPLAIN') }}</span></dt>
<dd><input type="color" class="topic-tag-color" id="tag_color" name="tag_color" value="{{ TAG_COLOR }}"></dd>
</dl>
<dl>
<dt><label for="tag_enabled">{{ lang('TOPIC_TAG_ENABLED') ~ lang('COLON') }}</label></dt>
<dd><input type="checkbox" class="radio" id="tag_enabled" name="tag_enabled" value="1"{% if TAG_ENABLED %} checked{% endif %}></dd>
</dl>
<dl>
<dt><label for="forum_ids">{{ lang('TOPIC_TAG_FORUMS') ~ lang('COLON') }}</label><br><span>{{ lang('TOPIC_TAG_FORUMS_EXPLAIN') }}</span></dt>
<dd><select id="forum_ids" name="forum_ids[]" multiple size="10">{{ S_FORUM_OPTIONS }}</select></dd>
</dl>
</fieldset>
<fieldset class="submit-buttons">
<input class="button1" name="submit" type="submit" value="{{ lang('SUBMIT') }}">
{% if S_EDIT_TAG %}<a class="button2" href="{{ U_ACTION }}">{{ lang('CANCEL') }}</a>{% endif %}
<input type="hidden" name="action" value="save">
<input type="hidden" name="tag_id" value="{{ TAG_ID }}">
{{ S_FORM_TOKEN }}
</fieldset>
</form>

{% INCLUDEJS '@phpbb_topicprefixes/acp_topic_prefixes.js' %}
{% INCLUDECSS '@phpbb_topicprefixes/acp_topic_prefixes.css' %}
Expand Down
4 changes: 0 additions & 4 deletions adm/style/acp_topic_prefixes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
(function() {
'use strict';

document.getElementById('select_forum').addEventListener('change', function() {
this.closest('form').submit();
});

phpbb.addAjaxCallback('tp_toggle', function(res) {
if (typeof res.success === 'undefined' || !res.success) {
return;
Expand Down
2 changes: 1 addition & 1 deletion build.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Topic Prefixes Extension Builder" description="Builds an extension.zip from a git repository" default="all">
<project name="Topic Tags Extension Builder" description="Builds an extension.zip from a git repository" default="all">
<property name="vendor-name" value="phpbb" />
<property name="extension-name" value="topicprefixes" />
<!--
Expand Down
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "phpbb/topicprefixes",
"type": "phpbb-extension",
"description": "An official phpBB extension for topic prefixes.",
"description": "An official phpBB extension for administrator-curated topic tags.",
"homepage": "https://www.phpbb.com/customise/db/extension/topicprefixes/",
"version": "1.0.2",
"version": "2.0.0-dev",
"license": "GPL-2.0-only",
"authors": [
{
Expand All @@ -13,21 +13,21 @@
},
{
"name": "Ruslan Uzdenov",
"homepage": "http://www.phpbbguru.net",
"homepage": "https://www.phpbbguru.net",
"role": "Extensions Development Team"
}
],
"require": {
"php": ">=5.4",
"php": ">=7.2",
"composer/installers": "^1.0 || ^2.0"
},
"require-dev": {
"phing/phing": "~2.4"
},
"extra": {
"display-name": "phpBB Topic Prefixes",
"display-name": "phpBB Topic Tags",
"soft-require": {
"phpbb/phpbb": ">=3.2.0"
"phpbb/phpbb": ">=3.3.5"
},
"version-check": {
"host": "www.phpbb.com",
Expand Down
Loading