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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ Upgrading from the original `sheer/knowledgebase` extension? See
[Upgrading from the original](docs/upgrading-from-original.md): your data carries
over.

Moving from the `kinerity/knowledgebase` extension? See
[Moving from kinerity/knowledgebase](docs/upgrading-from-kinerity.md): enabling this extension
imports its categories, articles and permissions.

Attachments are stored under `store/phpbbmodders_knowledgebase/files/` in the board's own folder,
with the rest of phpBB's data, so they stay when the extension's folder is replaced to upgrade it.
The folder is made when the extension is enabled and has its own `.htaccess`. **That only works on
Expand Down
76 changes: 76 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -1411,3 +1411,79 @@ phpBB 3.3.x and master. On master PHPUnit 10 also reports 53 deprecations (data
are not static), which do not fail the run. MySQL can answer `mysqladmin ping` on its socket before it
accepts TCP connections; tests started in that gap fail to connect, so wait a few seconds more
after a fresh container reports ready. The CI health check pings over TCP for that reason.

## Importing from kinerity/knowledgebase - 11/12/2026

New migrations `kinerity_tables_set_aside` (which `version_1_0_0` now depends on) and
`version_2_0_32`, with `inc/kinerity_import.php`, import a board's `kinerity/knowledgebase` data
when this extension is enabled; see [Moving from kinerity/knowledgebase](upgrading-from-kinerity.md).

**PHPUnit.** `tests/kb_kinerity_import_test.php` builds this extension's real tables from every
migration's `update_schema()`, so a column the import writes that a later migration removed fails
the test (it caught `bbcode_bitfield`, dropped by `version_1_0_3`). It covers moving kinerity's
tables aside and retrying that, kinerity 1.0.x without `article_edit_name`, the category tree,
plain-text details (including an emoji phpBB stored as an entity, which MySQL's utf8 tables cannot
hold raw), each article state, the lowest category as primary and the rest cross-listed, articles
in no category or only a deleted one, counters, permissions per group and user on every category,
the board-wide read and admin permissions, kinerity's settings, notification types and extension
entry removed, an import stopped part way, two requests converting the same batch at once, a purge
followed by a second import, a board that never ran kinerity, and a board that already has
categories. It runs on SQLite, MySQL and PostgreSQL, and on PostgreSQL also with a sequence made the
phpBB 3.3 way, which phpBB 4.0's tools leave behind when the table is dropped (without dropping it
too, this extension's table of the same name cannot be created). The full suite, 559 tests, passes on
phpBB 3.3.x and master with the MySQL 8.0 and PostgreSQL 16 fixtures, none skipped.

**Boards.** Claude built boards with seed-forum's `config/kinerity-33.yml`, `kinerity-40.yml` and
`kinerity-33-mssql.yml` and their `-switch` configs, and `bin/seed-kinerity-fixtures.php`: 2,000
categories up to five levels deep and 50,000 articles (about 40,000 approved, 4,000 waiting, 3,000
deleted and 3,000 denied; 14,000 in two to four categories; about 1,000 in no category or only a
deleted one; 70,000 category links), plus kinerity's own roles, a group and two members with
permissions of their own, and a kinerity notification. kinerity was enabled in the ACP (it cannot be
enabled from phpBB's command line; see below), seeded, disabled, and this extension enabled.
`tests/board/kinerity_import_check.php` then compared every backup row with what was imported.

| phpBB | Database | Enabled through | Articles | Time | Result |
| --- | --- | --- | --- | --- | --- |
| 3.3 | MySQL 8.4 | command line | 50,000 | 262 s | all 35 checks pass |
| 3.3 | PostgreSQL 16 | ACP | 50,000 | one page load | all 35 checks pass |
| 3.3 | SQLite | command line | 50,000 | 132 s | all 35 checks pass |
| 4.0 | MySQL 8.4 | command line | 50,000 | 246 s | all 35 checks pass |
| 4.0 | PostgreSQL 16 | ACP | 50,000 | 278 s, 2 attempts | all 35 checks pass |

`tests/browser/kb_kinerity_import.js` (14 checks, ids from `kb_kinerity_import_ids.php`) passed on a
phpBB 3.3 PostgreSQL board: guests refused as kinerity refused them, a member sees the imported
articles and sections, BBCode formatted, special characters shown once, a cross-listed article in its
second category, a deactivated article hidden, waiting articles in the queue as New, the ACP pages,
no kinerity module left, and search after building the index.

**Found and fixed on the way.**
- phpBB's ACP enables an extension in one page load (`meta_refresh()` only sets a template value;
the loop carries on), so a large import outlasts nginx's 60 seconds and ends on a 504 while PHP
carries on. On these boards PHP finished it. An admin who enables again meanwhile starts a second
import of the same batch; that created 12,000 duplicate articles on the 4.0 PostgreSQL board. Each
backup row is now claimed inside the batch's transaction before it is converted (`kb_new_id` is set
to -1 where it is still 0, and only the request that changed the row goes on), and the fallback
category is reused. The rebuilt board, with the same two overlapping attempts, passed. Retrying in
the ACP also needs the cache purged first, or phpBB stops on a duplicate key in its extension list;
the guide says so.
- Two existing bugs stopped this extension from being enabled on a new PostgreSQL board at all
(`version_2_0_30`'s option id, `version_2_0_31`'s index name on phpBB 3.3); fixed separately with
`tests/kb_options_id_migration_test.php`.

**kinerity itself**, as found by the fixture (not changed here): its first migration reads the
current user's name, so it cannot be enabled from phpBB's command line; on PostgreSQL it inserts its
sample category and article with id 1 without moving the sequences on, so the next insert fails; its
article list loads every article at once and runs out of memory with tens of thousands; and it stops
with an error on an article with no category. `SEED_KINERITY_LEFTOVERS=0` leaves those articles out
for boards meant to be viewed in kinerity.

**MSSQL.** kinerity cannot be installed on MSSQL: its first migration inserts its sample category
with a fixed id, which MSSQL refuses for an identity column. So no board can bring kinerity data from
MSSQL, and there is no MSSQL row above. Trying it showed that this extension cannot be enabled on a
new MSSQL board either: `version_1_0_0` stops on its own fixed-id permission options in the same way,
and, with that worked around, on a later step that compares an `ntext` column with a number. That
is a separate piece of work, not part of this change.

**Not covered.** Boards upgraded from phpBB 3.3 to 4.0 with kinerity data (the sequence case is
covered by PHPUnit only); a custom style; kinerity versions before 1.2.1 on a board (1.0.x's missing
column is covered by PHPUnit only); MariaDB.
132 changes: 132 additions & 0 deletions docs/upgrading-from-kinerity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Moving from the kinerity/knowledgebase extension

This extension can import a board's knowledge base from the
`kinerity/knowledgebase` extension (also published as Crizz0's
`knowledgebase` fork; version 1.2.1 is the one tested, and 1.0.x and 1.1.x
boards are handled too). Your categories, articles and permissions come
across when you enable this extension. Nothing has to be converted by hand.

Both extensions use database tables called `kb_articles` and `kb_categories`,
with different columns. When this extension is enabled, it first copies
kinerity's tables to backup tables, `kb_kinerity_articles`,
`kb_kinerity_categories` and `kb_kinerity_article_category`, and removes the
originals. Then it creates its own tables and imports into them. The backup
tables are kept, unchanged.

## Steps

1. Back up the database.
2. In the Administration Control Panel go to **Customise → Manage extensions**
and **Disable** `Knowledge Base` (kinerity). Do not use *Delete data*: that
removes kinerity's tables, and with them everything there is to import. This
extension refuses to enable while kinerity is still enabled and says so.
3. Copy this extension to `ext/phpbbmodders/knowledgebase`.
4. **Enable** it. The import runs while it is being enabled.
- On a large knowledge base, use the command line. It has no page time
limit and shows each step as it runs:

```bash
php bin/phpbbcli.php extension:enable phpbbmodders/knowledgebase
```

- In the Administration Control Panel, phpBB enables an extension in a
single page load, so a large import can run past the server's time limit
and end on a time-out or error page. Nothing is lost: the categories and
articles are imported in batches, and each finished batch is kept. Go to
**General → Purge the cache**, then enable the extension again; it carries
on where it stopped. Repeat until phpBB says the extension was enabled.
(Purging the cache matters: without it phpBB can stop with a "duplicate
key" error on its list of extensions.)

Either way, an import that stops part way carries on where it stopped the
next time the extension is enabled, and imports nothing twice.
5. Go to **Extensions → Knowledge Base → Search** and create the search index,
so the imported articles can be found by the Knowledge Base search.
6. Check your categories, articles and permissions under
**Extensions → Knowledge Base**.
7. Delete the `ext/kinerity/knowledgebase` folder. Don't enable kinerity again
on this board: its pages would read this extension's tables.

## What is imported

- **Categories**, with their order and nesting. A category's description
becomes plain text, because this extension's category details have no
BBCode, and is shortened to 255 characters if it is longer.
- **Articles**, with their title, text (BBCode, smilies and links as they
were), author, dates and view count. Descriptions longer than 255 characters
are shortened, ending in "…".
- Approved articles are published.
- Articles waiting for approval go to the approval queue.
- Articles kinerity marked as deleted or denied are kept, deactivated, so a
moderator can reactivate or delete them.
- An article that was in several categories goes in the one kinerity created
first (the lowest category id) and is cross-listed in the others.
- Articles kinerity filed in no category, or only in a category that no
longer exists, go in a new top-level category called
"Imported articles without a category".
- **Permissions.** kinerity's permissions applied to the whole knowledge base;
this extension's apply per category. Each group's and user's kinerity
setting (Yes or Never, set directly or through a role) is given to them on
every imported category:

| kinerity permission | This extension's permission |
| --- | --- |
| `u_kb_read` | Can view Knowledge Base (`u_kb_view`, board-wide) |
| `u_kb_post` | Can add articles |
| `u_kb_noapprove` | Can add / modify articles without prior approval |
| `u_kb_edit` | Can edit own articles |
| `u_kb_delete` | Can delete own articles |
| `m_kb_edit` | Can edit articles |
| `m_kb_delete` | Can delete articles |
| `m_kb_approve` | Can approve articles |
| `m_kb_chgposter` | Can change the primary author of submitted articles |
| `a_kb_manage` | Can manage Knowledge Base (`a_manage_kb`, board-wide) |

A group or user that already has a setting for `u_kb_view` or `a_manage_kb`
keeps it.

## What is removed

After the import, kinerity's permissions (with their settings in roles,
groups and users), its ACP modules, its three settings (the on/off switch, the
header link and its icon, which this extension has no equivalent of), its
notification types with their notifications and users' settings for them,
and its entry in the extensions list are removed.

kinerity's migration records are kept on purpose. Without them, enabling
kinerity again would run its first migration, which fails on this extension's
tables, and phpBB would then drop those tables while undoing it.

## Not imported

- Who last edited an article (kinerity's "edited by" name). The edit date is
kept.
- A category's article count above 65,535, the most that column holds; the
articles themselves are all imported.
- kinerity's per-article BBCode, smilies and links switches: the stored text
already shows them the way kinerity did.
- Old links. Articles and categories get new ids, so links to kinerity's pages
(`/kb/viewarticle?a=…` and the like) in posts or search engines don't lead to
the imported articles. The backup tables record each article's old and new
id (`article_id` and `kb_new_id`).

## The backup tables

The `kb_kinerity_*` tables hold kinerity's data as it was, plus the id each
row was given here. Purging this extension leaves them in place, and enabling
it again imports them again. Once you are happy with the result you can drop
them with your database tool, for example:

```sql
DROP TABLE phpbb_kb_kinerity_articles;
DROP TABLE phpbb_kb_kinerity_categories;
DROP TABLE phpbb_kb_kinerity_article_category;
```

(Use your board's table prefix in place of `phpbb_`.)

## If something goes wrong

Restore the database backup from step 1. Enabling stops and reports an error
rather than carrying on. kinerity's tables are only removed once every row has
been copied to the backup tables and the copies counted.
12 changes: 12 additions & 0 deletions ext.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class ext extends \phpbb\extension\base
/** The original package this extension can take over from; it must be disabled first. */
public const ORIGINAL_EXTENSION = 'sheer/knowledgebase';

/** Another knowledge base this extension can import from; it must be disabled first. */
public const KINERITY_EXTENSION = 'kinerity/knowledgebase';

/**
* Check whether the extension can be enabled.
* The current phpBB version should meet or exceed
Expand Down Expand Up @@ -53,6 +56,15 @@ public function is_enableable()
$messages[] = $language->lang('INSTALL_FAILED_ORIGINAL_ENABLED', self::ORIGINAL_EXTENSION);
}

// kinerity/knowledgebase uses the same table names and /kb pages.
// Once it is disabled, enabling this one imports its data (see
// migrations kinerity_tables_set_aside and version_2_0_32).
if ($this->container->get('ext.manager')->is_enabled(self::KINERITY_EXTENSION))
{
$language->add_lang('install_failed', 'phpbbmodders/knowledgebase');
$messages[] = $language->lang('INSTALL_FAILED_KINERITY_ENABLED', self::KINERITY_EXTENSION);
}

return $messages ?: true;
}

Expand Down
50 changes: 50 additions & 0 deletions inc/default_content.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
*
* Knowledge Base extension for the phpBB Forum Software package
*
* @copyright (c) 2026, phpBB Modders, https://www.phpbbmodders.com/
* @license GNU General Public License, version 2 (GPL-2.0)
*
*/

namespace phpbbmodders\knowledgebase\inc;

/**
* Text from language/<lang>/default_content.php that migrations write into
* the board as content, such as a category name. Migrations have no language
* service, so the file is read directly, in the board's default language when
* it has one, otherwise in English.
*/
class default_content
{
/**
* @param string $language A language folder name, such as the board's default_lang
* @param string $key A key in default_content.php
* @return string The text, or the key when no language file has it
*/
public static function lang(string $language, string $key): string
{
foreach (array_unique([preg_replace('/[^a-z0-9_\-]/i', '', $language), 'en']) as $folder)
{
$file = dirname(__DIR__) . '/language/' . $folder . '/default_content.php';
if ($folder === '' || !is_file($file))
{
continue;
}

$lang = [];
if (!defined('IN_PHPBB'))
{
define('IN_PHPBB', true);
}
include $file;
if (isset($lang[$key]))
{
return $lang[$key];
}
}

return $key;
}
}
Loading
Loading