diff --git a/styles/all/template/kb_index_body.html b/styles/all/template/kb_index_body.html
index 95e55d9..56926ca 100644
--- a/styles/all/template/kb_index_body.html
+++ b/styles/all/template/kb_index_body.html
@@ -1,5 +1,6 @@
{% INCLUDE 'overall_header.html' %}
+{% INCLUDE '@phpbbmodders_knowledgebase/kb_intro.html' %}
{% INCLUDE '@phpbbmodders_knowledgebase/kb_nav_tabs.html' %}
{% if S_IS_SEARCH %}
diff --git a/styles/all/template/kb_intro.html b/styles/all/template/kb_intro.html
new file mode 100644
index 0000000..c63290e
--- /dev/null
+++ b/styles/all/template/kb_intro.html
@@ -0,0 +1,3 @@
+{% if S_KB_INTRO %}
+
{{ KB_INTRO }}
+{% endif %}
diff --git a/styles/all/theme/phpbbmodders_knowledgebase.css b/styles/all/theme/phpbbmodders_knowledgebase.css
index a001f57..4fdb9c1 100644
--- a/styles/all/theme/phpbbmodders_knowledgebase.css
+++ b/styles/all/theme/phpbbmodders_knowledgebase.css
@@ -406,3 +406,10 @@ table.kb-index-table .kb-index-author {
margin-right: 0;
}
}
+
+/* Introduction text under the Knowledge Base title */
+.kb-intro {
+ margin: 0 0 10px;
+ font-size: 1.1em;
+ line-height: 1.4em;
+}
diff --git a/tests/kb_intro_test.php b/tests/kb_intro_test.php
new file mode 100644
index 0000000..1afee79
--- /dev/null
+++ b/tests/kb_intro_test.php
@@ -0,0 +1,131 @@
+createMock(\phpbb\config\db_text::class);
+ $config_text->method('get')->willReturnCallback(function ($key) use (&$texts) {
+ return $texts[$key] ?? null;
+ });
+ $config_text->method('set')->willReturnCallback(function ($key, $value) use (&$texts) {
+ $texts[$key] = $value;
+ });
+
+ $article_text = $this->createMock(\phpbbmodders\knowledgebase\textformatter\article_text::class);
+ $article_text->method('store')->willReturnCallback(function (&$text, $markdown, &$uid, &$bitfield, &$options) {
+ $text = ($markdown ? '
' : '') . $text . '';
+ $uid = $markdown ? '' : 'abc123';
+ });
+ $article_text->method('display')->willReturnCallback(fn($stored, $uid, $markdown) => 'HTML[' . ($markdown ? 'md' : 'bb') . ':' . $uid . ':' . $stored . ']');
+ $article_text->method('for_edit')->willReturnCallback(fn($stored) => 'EDIT[' . $stored . ']');
+
+ $template = $this->createMock(\phpbb\template\template::class);
+ $template->method('assign_vars')->willReturnCallback(function ($vars) {
+ $this->assigned = array_merge($this->assigned, $vars);
+ });
+
+ return new intro($config, $config_text, $article_text, $template);
+ }
+
+ public function test_the_default_is_kinerity_text_stored_as_plain_text()
+ {
+ $this->assertStringStartsWith('This section contains detailed articles', default_content::lang('en', 'KB_INTRO_DEFAULT_TEXT'));
+ $this->assertSame('A & B "C" <D>', intro::stored_plain_text('A & B "C" '));
+ }
+
+ public function test_nothing_is_shown_while_it_is_switched_off()
+ {
+ $texts = ['kb_intro_text' => 'Hello'];
+ $this->intro(new \phpbb\config\config(['kb_intro_enabled' => 0]), $texts)->assign();
+
+ $this->assertSame(['S_KB_INTRO' => false, 'KB_INTRO' => ''], $this->assigned);
+ }
+
+ public function test_it_is_shown_when_switched_on()
+ {
+ $texts = ['kb_intro_text' => 'Hello'];
+ $config = new \phpbb\config\config(['kb_intro_enabled' => 1, 'kb_intro_uid' => 'u1', 'kb_intro_markdown' => 0]);
+ $this->intro($config, $texts)->assign();
+
+ $this->assertSame(['S_KB_INTRO' => true, 'KB_INTRO' => 'HTML[bb:u1:Hello]'], $this->assigned);
+ }
+
+ public function test_an_empty_text_shows_nothing_even_when_switched_on()
+ {
+ foreach (['', '', ' '] as $stored)
+ {
+ $this->assigned = [];
+ $texts = ['kb_intro_text' => $stored];
+ $this->intro(new \phpbb\config\config(['kb_intro_enabled' => 1]), $texts)->assign();
+ $this->assertFalse($this->assigned['S_KB_INTRO'], var_export($stored, true));
+ }
+ }
+
+ public function test_saving_stores_the_parsed_text_and_the_switches()
+ {
+ $texts = [];
+ $config = new \phpbb\config\config([]);
+ $intro = $this->intro($config, $texts);
+
+ $intro->save(true, '[b]Hi[/b]', false);
+ $this->assertSame('[b]Hi[/b]', $texts['kb_intro_text']);
+ $this->assertSame(['abc123', 0, 1], [$config['kb_intro_uid'], (int) $config['kb_intro_markdown'], (int) $config['kb_intro_enabled']]);
+ $this->assertSame('EDIT[[b]Hi[/b]]', $intro->for_edit());
+
+ $intro->save(false, '# Hi', true);
+ $this->assertSame('# Hi', $texts['kb_intro_text']);
+ $this->assertSame(['', 1, 0], [$config['kb_intro_uid'], (int) $config['kb_intro_markdown'], (int) $config['kb_intro_enabled']]);
+ $this->assertSame('HTML[md::# Hi]', $intro->html());
+
+ // Emptying the box clears the text
+ $intro->save(true, ' ', false);
+ $this->assertSame('', $texts['kb_intro_text']);
+ $this->assertSame('', $intro->html());
+ }
+
+ public function test_the_migration_adds_it_switched_off_and_switches_it_on_after_a_kinerity_import()
+ {
+ $migration = new \phpbbmodders\knowledgebase\migrations\version_2_0_33(new \phpbb\config\config([]), $this->createMock(\phpbb\db\driver\driver_interface::class), $this->createMock(\phpbb\db\tools\tools_interface::class), '', 'php', 'phpbb_', []);
+ $steps = $migration->update_data();
+
+ $this->assertContains(['config.add', ['kb_intro_enabled', 0]], $steps);
+ $this->assertContains(['config_text.add', ['kb_intro_text', intro::stored_plain_text(default_content::lang('en', 'KB_INTRO_DEFAULT_TEXT'))]], $steps);
+ $this->assertContains(['if', [['custom', [[$migration, 'came_from_kinerity']]], ['config.update', ['kb_intro_enabled', 1]]]], $steps);
+ $this->assertSame(['\phpbbmodders\knowledgebase\migrations\version_2_0_32'], \phpbbmodders\knowledgebase\migrations\version_2_0_33::depends_on());
+
+ foreach ([true, false] as $has_backup)
+ {
+ $tools = $this->createMock(\phpbb\db\tools\tools_interface::class);
+ $tools->method('sql_table_exists')->willReturn($has_backup);
+ $check = new \phpbbmodders\knowledgebase\migrations\version_2_0_33(new \phpbb\config\config([]), $this->createMock(\phpbb\db\driver\driver_interface::class), $tools, '', 'php', 'phpbb_', []);
+ $this->assertSame($has_backup, $check->came_from_kinerity());
+ }
+ }
+}
diff --git a/tests/kb_kinerity_import_test.php b/tests/kb_kinerity_import_test.php
index f07c23d..06d0653 100644
--- a/tests/kb_kinerity_import_test.php
+++ b/tests/kb_kinerity_import_test.php
@@ -12,6 +12,7 @@
require_once __DIR__ . '/kb_extensions_test.php';
if (!defined('IN_PHPBB')) define('IN_PHPBB', true);
+require_once (getenv('PHPBB_ROOT_PATH') ?: dirname(__DIR__, 4)) . '/includes/utf/utf_tools.php';
if (!defined('CONFIG_TABLE')) define('CONFIG_TABLE', 'phpbb_config');
require_once (getenv('PHPBB_ROOT_PATH') ?: dirname(__DIR__, 4)) . '/includes/functions.php';
require_once dirname(__DIR__) . '/inc/functions_kb.php';