diff --git a/.gitattributes b/.gitattributes
index 1fa9d63..c5a925a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,3 +12,4 @@ composer.lock export-ignore
phpunit.xml.* export-ignore
tests export-ignore
docs export-ignore
+.editorconfig export-ignore
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..8f2bd8b
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -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
diff --git a/README.md b/README.md
index 7f0538a..9ebfefc 100644
--- a/README.md
+++ b/README.md
@@ -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.
+[](https://github.com/phpbbmodders/knowledgebase/actions/workflows/tests.yml) [](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
@@ -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
@@ -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).
-[](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
@@ -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)
diff --git a/acp/attachments_info.php b/acp/attachments_info.php
index 7f94a1b..4f08a81 100644
--- a/acp/attachments_info.php
+++ b/acp/attachments_info.php
@@ -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'],
],
],
diff --git a/acp/manage_module.php b/acp/manage_module.php
index b96e9a3..eae3f09 100644
--- a/acp/manage_module.php
+++ b/acp/manage_module.php
@@ -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)
@@ -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));
diff --git a/acp/permissions_module.php b/acp/permissions_module.php
index 4c4cdd2..62983d3 100644
--- a/acp/permissions_module.php
+++ b/acp/permissions_module.php
@@ -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)
{
diff --git a/adm/style/acp_knowledgebase_category.html b/adm/style/acp_knowledgebase_category.html
index df0714b..a033efc 100644
--- a/adm/style/acp_knowledgebase_category.html
+++ b/adm/style/acp_knowledgebase_category.html
@@ -203,7 +203,7 @@