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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ composer.lock export-ignore
phpunit.xml.* export-ignore
tests export-ignore
docs export-ignore
.editorconfig export-ignore
38 changes: 38 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Lint

# PHP syntax check on the lowest PHP version this extension supports and the
# newest one. tests.yml runs phpBB's own checks (code sniffer, EPV) and
# the PHPUnit suite.
on:
push:
branches:
- main
pull_request:
branches:
- main

permissions:
contents: read

jobs:
lint:
name: PHP ${{ matrix.php }} lint
runs-on: ubuntu-24.04
strategy:
matrix:
php: ['8.1', '8.5']
steps:
- uses: actions/checkout@v4

- name: Set up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
tools: composer:v2

- name: PHP syntax check
run: find . -name '*.php' -not -path './.git/*' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l

- name: Validate composer.json
run: composer validate --no-check-all --no-check-publish
44 changes: 27 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
# Knowledge Base

A knowledge base for phpBB: articles organised in categories, with attachments, Markdown or BBCode text, revisions, an approval workflow, search, and its own permission system, all managed from the ACP.
[![Tests](https://github.com/phpbbmodders/knowledgebase/actions/workflows/tests.yml/badge.svg)](https://github.com/phpbbmodders/knowledgebase/actions/workflows/tests.yml) [![Lint](https://github.com/phpbbmodders/knowledgebase/actions/workflows/lint.yml/badge.svg)](https://github.com/phpbbmodders/knowledgebase/actions/workflows/lint.yml)

A knowledge base for phpBB: categorised articles in Markdown or BBCode, with revisions, an approval workflow, search, and RSS and Atom feeds.

## Features

- Articles in categories, shown as one flat index with a category dropdown (the default) or as a category tree, in each member's board style, with an optional colour stylesheet.
- Articles written in BBCode or Markdown, with attachments, custom fields (a dropdown, radio buttons or a checkbox list, each optionally required) and a plain-text description.
- Private drafts, revisions, and an approval queue where moderators assign, review, approve or reject articles and edits; authors get notifications and, by default, private messages.
- Co-authors, and **Change author** to hand an article to another member or a Knowledge Base team. Every transfer is logged.
- Article status (draft, pending, pending revision) on article pages, category lists and queues, without showing what a draft says.
- Search with phpBB-style native, MySQL fulltext or PostgreSQL fulltext backends, and a Knowledge Base block in the board's own search results.
- A recent changes page with RSS and Atom feeds, optional comment topics, and an optional changelog forum.
- Its own per-category permissions, managed with the rest of the settings in the ACP.
- An optional introduction above the article index, in BBCode or Markdown.
- Takes over the data of the original `sheer/knowledgebase` extension, and imports `kinerity/knowledgebase`.

## Screenshots

Expand All @@ -17,15 +32,11 @@ A knowledge base for phpBB: articles organised in categories, with attachments,

More screenshots, including the article form, the approval queue and the ACP settings: [docs/images/screenshots](docs/images/screenshots/README.md).

Primary authorship of submitted articles can be transferred to another active
member or a configured KB team using **Change author** on the article page.
Grant moderators **Can change the primary author of submitted articles** in
Knowledge Base category permissions. KB administrators already have access.
Transfers are logged and retain co-authors and submitted revisions. Private
drafts and articles with private draft edits must be submitted or discarded
before authorship can change.
## Requirements

Article pages show status below the category. Category lists and review queues also show status, pending revisions, and “Has draft” for authors and authorized editors. Draft contents remain private. Markdown search excerpts retain their formatting.
- phpBB 3.3.19 or later; phpBB 4.0 works too (tested with its development version)
- PHP 8.1 or later
- MySQL/MariaDB, PostgreSQL, SQLite or MSSQL

## Installation

Expand Down Expand Up @@ -83,17 +94,17 @@ sudo chmod -R 775 store

Without shell access, set the `store` folder and everything in it to 775 with your FTP program (777 only if your host allows nothing else). Reload the Configuration page: the message goes away as soon as the folders can be made. Attachments that earlier versions kept in `ext/phpbbmodders/knowledgebase/files/` are moved across when the extension is updated; any that could not be moved stay in that folder and keep the old problem, so run the commands above and disable and enable the extension again.

## Automated testing
## TODO

See [Testing](docs/testing.md) for local regression tests and disposable
MySQL/PostgreSQL fixtures. The current GitHub workflow runs coding and
extension validation checks, not these regression tests.
Ideas not yet built, practical and speculative alike: [`docs/TODO.md`](docs/TODO.md).

[![Tests](https://github.com/phpbbmodders/knowledgebase/actions/workflows/tests.yml/badge.svg)](https://github.com/phpbbmodders/knowledgebase/actions/workflows/tests.yml)
## Contributing

## TODO
Contributions are welcome!

Ideas not yet built, practical and speculative alike: [`docs/TODO.md`](docs/TODO.md).
- **Bug reports**: [Open an issue](https://github.com/phpbbmodders/knowledgebase/issues).
- **Everything else** (questions, feature requests, ideas, general discussion): [Use Discussions](https://github.com/orgs/phpbbmodders/discussions), or the [community forum](https://www.phpbbmodders.com/community/).
- Pull requests are welcome for bug fixes or discussed features. See [Testing](docs/testing.md) for the PHPUnit suite and the browser checks.

## Acknowledgments

Expand All @@ -102,7 +113,6 @@ Based on the original [knowledgebase](https://github.com/sutrus/knowledgebase) e
- Code review, bug fixes, and documentation assisted by [Claude](https://www.anthropic.com/claude).
- Code review, bug fixes, and documentation assisted by [Codex](https://openai.com/codex/).


## License

Licensed under the [GNU General Public License v2](license.txt)
6 changes: 3 additions & 3 deletions acp/attachments_info.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ public function module(): array
],
'orphan' => [
'title' => 'ACP_LIBRARY_ATTACHMENTS_ORPHAN',
'auth' => 'ext_phpbbmodders/knowledgebase && acl_a_board',
'auth' => 'ext_phpbbmodders/knowledgebase && acl_a_board && acl_a_manage_kb',
'cat' => ['ACP_KNOWLEDGE_BASE'],
],
'extra_files' => [
'title' => 'ACP_LIBRARY_ATTACHMENTS_EXTRA_FILES',
'auth' => 'ext_phpbbmodders/knowledgebase && acl_a_board',
'auth' => 'ext_phpbbmodders/knowledgebase && acl_a_board && acl_a_manage_kb',
'cat' => ['ACP_KNOWLEDGE_BASE'],
],
'lost_files' => [
'title' => 'ACP_LIBRARY_ATTACHMENTS_LOST_FILES',
'auth' => 'ext_phpbbmodders/knowledgebase && acl_a_board',
'auth' => 'ext_phpbbmodders/knowledgebase && acl_a_board && acl_a_manage_kb',
'cat' => ['ACP_KNOWLEDGE_BASE'],
],
],
Expand Down
20 changes: 16 additions & 4 deletions acp/manage_module.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,22 @@ public function main(string $id, string $mode): void

$action = $request->variable('action', '');
$update = ($request->is_set_post('update'));
$category_id = $request->variable('f', '');
$category_id = $request->variable('f', 0);
$parent_id = $request->variable('parent_id', 0);
$copy_perm_from_id = $request->variable('cat_perm_from', 0);

// Every change below needs this page's form token (add, edit,
// delete) or its link hash (move up/down, sync)
add_form_key('kb_acp_manage');
if ($update && !check_form_key('kb_acp_manage'))
{
trigger_error($language->lang('FORM_INVALID') . adm_back_link($this->u_action . '&parent_id=' . $parent_id), E_USER_WARNING);
}
if (in_array($action, ['move_up', 'move_down', 'sync'], true) && !check_link_hash($request->variable('hash', ''), 'kb_acp_manage'))
{
trigger_error($language->lang('FORM_INVALID') . adm_back_link($this->u_action . '&parent_id=' . $parent_id), E_USER_WARNING);
}

if ($update)
{
switch ($action)
Expand Down Expand Up @@ -331,11 +343,11 @@ public function main(string $id, string $mode): void
'CATEGORY_DESCR' => $row['category_details'],
'ARTICLES' => $row['number_articles'],
'U_CATEGORY' => $this->u_action . '&parent_id=' . $row['category_id'],
'U_MOVE_UP' => $url . '&action=move_up',
'U_MOVE_DOWN' => $url . '&action=move_down',
'U_MOVE_UP' => $url . '&action=move_up&hash=' . generate_link_hash('kb_acp_manage'),
'U_MOVE_DOWN' => $url . '&action=move_down&hash=' . generate_link_hash('kb_acp_manage'),
'U_EDIT' => $url . '&action=edit',
'U_DELETE' => $url . '&action=delete',
'U_SYNC' => $url . '&action=sync',
'U_SYNC' => $url . '&action=sync&hash=' . generate_link_hash('kb_acp_manage'),
]
);
} while ($row = $db->sql_fetchrow($result));
Expand Down
4 changes: 4 additions & 0 deletions acp/permissions_module.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ public function main(string $id, string $mode): void
$delete = $request->variable('delete', false);
$category_id = $all_cats ? $admin_controller->make_array_categoryid() : $request->variable('category_id', [0]);

// The token for every form on these pages; get_mask() checks it
// before saving permissions
add_form_key('kb_acp_permissions');

// Map usernames to ids and vice versa
if ($usernames)
{
Expand Down
4 changes: 2 additions & 2 deletions adm/style/acp_knowledgebase_category.html
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ <h3>{{ lang('WARNING') }}</h3>
<fieldset class="quick">
{{ lang('SELECT_CATEGORY') }}{{ lang('COLON') }}
<label>
<select name="parent_id"
<select name="parent_id" aria-label="{{ lang('SELECT_CATEGORY') }}"
onchange="if(this.options[this.selectedIndex].value !== -1){ this.form.submit(); }">{{ CATS_BOX }}
</select>
</label>
Expand All @@ -214,7 +214,7 @@ <h3>{{ lang('WARNING') }}</h3>
{% endif %}
<fieldset class="quick">
<input type="hidden" name="action" value="add"/>
<label><input type="text" name="category_name" value="" maxlength="255"/></label>
<label><input type="text" name="category_name" value="" maxlength="255" aria-label="{{ lang('ADD_CATEGORY') }}"/></label>
<input class="button2" name="addcat" type="submit" value="{{ lang('ADD_CATEGORY') }}"/>
{{ S_FORM_TOKEN }}
</fieldset>
Expand Down
5 changes: 3 additions & 2 deletions adm/style/acp_knowledgebase_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,15 @@ <h1>{{ lang('ACP_KNOWLEDGE_BASE_CONFIGURE') }}</h1>
<dd>
<input type="number" id="extgroup_filesize" min="0" max="999999999999999" step="any" name="max_filesize"
value="{{ EXTGROUP_FILESIZE }}"/>
<label><select name="size_select"> {{ S_EXT_GROUP_SIZE_OPTIONS }}</select></label>
<label><select name="size_select" aria-label="{{ lang('ATTACH_MAX_FILESIZE') }}"> {{ S_EXT_GROUP_SIZE_OPTIONS }}</select></label>
</dd>
</dl>
</fieldset>
<fieldset>
<legend>{{ lang('KB_TEAMS') }}</legend>
<dl>{{ lang('KB_TEAMS_EXPLAIN') }}</dl>
<p>{{ lang('KB_TEAMS_EXPLAIN') }}</p>
<dl>
<dt>{{ lang('KB_TEAMS') }}{{ lang('COLON') }}</dt>
{% for row in loops.team_group %}
<dd>
<label>
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "phpbbmodders/knowledgebase",
"type": "phpbb-extension",
"description": "Knowledge Base",
"description": "A knowledge base for phpBB: categorised articles in Markdown or BBCode, with revisions, an approval workflow, search, and RSS and Atom feeds.",
"homepage": "https://www.phpbbmodders.com/",
"version": "2.1.0-a1-dev",
"keywords": [
Expand Down
8 changes: 8 additions & 0 deletions controller/acp_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -1421,8 +1421,10 @@ public function move_article(): void
// move from
$info = $this->kb->get_kb_article_info($article_id);

add_form_key('kb_acp_articles');
if ($submit)
{
$this->require_form_key('kb_acp_articles');
$this->kb->kb_move_article($article_id, $info['article_title'], $info['article_category_id'], $to_id, $info['display_order']);
meta_refresh(2, $this->u_action);
trigger_error($this->language->lang('ARTICLE_MOVED'));
Expand Down Expand Up @@ -2547,6 +2549,12 @@ public function get_mask($mode, $user_mode, $group_id, $category_id, $user_id):
];

$apply_all_permissions = $this->request->variable('apply_all_permissions', false);
// Saving permissions changes them, so it needs the form token
// permissions_module adds to every form on its pages
if ($apply_all_permissions || $this->request->is_set_post('submit'))
{
$this->require_form_key('kb_acp_permissions');
}

if (!empty($user_id) && $user_mode != 'group')
{
Expand Down
8 changes: 7 additions & 1 deletion controller/approve.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,6 @@ public function approve_article(): \Symfony\Component\HttpFoundation\Response

$kb_article_info = $this->kb->get_kb_article_info($art_id);
$kb_category_info = $this->kb->get_cat_info($kb_article_info['article_category_id']);
$category_name = $kb_category_info['category_name'];
$status = (int) $kb_article_info['status'];

// A draft was never submitted - not even reachable by a moderator
Expand All @@ -339,6 +338,13 @@ public function approve_article(): \Symfony\Component\HttpFoundation\Response
$this->auth->acl_get('a_manage_kb') ||
$this->kb->acl_kb_get_any($this->kb->get_article_categories($art_id), 'kb_m_approve');

// The page shows unapproved articles and pending revisions in full,
// so only moderators and the team reviewing the article may open it
if (!$is_moderator && !($effective_team_id && $this->kb->is_team_member([$effective_team_id])))
{
trigger_error('NOT_AUTHORISED');
}

// Actions submitted from the Administration queue's own row form
// (deactivate/reactivate/review) should return there, not to the
// category page - the moderator is working through a list, not
Expand Down
Binary file modified docs/images/screenshots/07-acp-configuration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/screenshots/18-acp-introduction.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 17 additions & 2 deletions docs/images/screenshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ browsing on for the category-tree capture and restores its previous setting.
The current fixture is the seed-forum board at `http://localhost:8102`.

The two ACP pages are taken in full, so a setting further down the page is not cut off.
The ACP configuration page and the two introduction shots (11/12/2026) come from
`phpbb-release-tools/phpbb-test-board/screenshots.py` instead, on a board built by its
`setup-board.sh` with seed-forum's standard fixtures, which is why they are narrower than the rest.

## The flat article index

Expand Down Expand Up @@ -65,9 +68,9 @@ Results of a search on the Knowledge Base's own search page.

## ACP configuration

**Extensions > Knowledge Base > Configuration**, the whole page: the announcement and changelog forums, hierarchical browsing, the alternative stylesheet, the **Recent changes feed** (RSS, Atom or both), the number of articles per page, the description length and the sorting order, then the attachment settings, the review teams and the contact group.
**Extensions > Knowledge Base > Configuration**, the whole page: the announcement and changelog forums, hierarchical browsing, the introduction above the index, the alternative stylesheet, the **Recent changes feed** (RSS, Atom or both), the number of articles per page, the description length and the sorting order, then the attachment settings, the review teams and the contact group.

<a href="07-acp-configuration.png"><img src="07-acp-configuration.png" width="960" height="1094" alt="The configuration page in the ACP"></a>
<a href="07-acp-configuration.png"><img src="07-acp-configuration.png" width="960" height="1277" alt="The configuration page in the ACP"></a>

## Knowledge Base articles in the board's search

Expand Down Expand Up @@ -111,3 +114,15 @@ A blocked transfer appears in the extension's Notice tab, with the board styling
Markdown headings, lists and emphasis render in a shortened search excerpt rather than appearing as source syntax.

<a href="16-markdown-search.png"><img src="16-markdown-search.png" width="960" height="673" alt="Rendered Markdown search"></a>

## Index introduction

With **Show an introduction** ticked in the ACP configuration, a short text sits under the Knowledge Base title on the index. It can be BBCode or Markdown; a new board gets a default text, and it is off until switched on.

<a href="17-index-introduction.png"><img src="17-index-introduction.png" width="960" height="675" alt="The Knowledge Base index with an introduction under its title"></a>

## ACP introduction settings

The **Configuration** block of the ACP configuration page with **Show an introduction** ticked: the introduction text, which starts as the default text, and whether it is written in Markdown.

<a href="18-acp-introduction.png"><img src="18-acp-introduction.png" width="960" height="807" alt="The introduction settings in the ACP configuration"></a>
Loading
Loading