diff --git a/README.md b/README.md index 756045d..75491e2 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ A wiki for phpBB with version history, side-by-side compare, and a moderation qu - Search: `/wiki/search` and a search box on the overview find articles by their title, description and text, with highlighted snippets. It has its own index, kept current as articles are saved, approved, set offline and deleted, and three backends to choose from in the ACP (native, MySQL fulltext, PostgreSQL fulltext). The board's own search can show the matching articles in a block of their own, with an "Also search the Wiki" option. See [Search](#search). - The Wiki link in the board header shows the Wikipedia icon by default. Under **Extensions > Wiki > Settings** in the ACP the icon can be changed to any [Font Awesome](https://fontawesome.com/v4.7.0/icons/) icon name, or left blank for no icon. - A **Start discussion** button on each article creates its discussion topic in a forum chosen under **Extensions > Wiki > Settings** in the ACP, and links the topic to the article. It shows to members who hold the "Can change the discussion topic for an article" permission and may post in that forum, and stays hidden until a forum is chosen. +- Custom BBCodes (the ones added on the ACP's **Posting > BBCodes** page) can be made wiki-only under **Extensions > Wiki > BBCodes**: a ticked one works in wiki articles and nowhere else, and its button shows only in the wiki editor. A forum post, private message or signature using it is refused with phpBB's usual "You cannot use certain BBCodes" message. Text saved before keeps its formatting. - Separate permissions for viewing, editing, version history, deleting and moderating. ## Screenshots diff --git a/acp/bbcodes_info.php b/acp/bbcodes_info.php new file mode 100644 index 0000000..c6c3af8 --- /dev/null +++ b/acp/bbcodes_info.php @@ -0,0 +1,32 @@ + '\phpbbmodders\wiki\acp\bbcodes_module', + 'title' => 'ACP_WIKI_BBCODES', + 'modes' => array( + 'settings' => array( + 'title' => 'ACP_WIKI_BBCODES', + 'auth' => 'ext_phpbbmodders/wiki && acl_a_bbcode', + 'cat' => array('ACP_WIKI_TITLE'), + ), + ), + ); + } +} diff --git a/acp/bbcodes_module.php b/acp/bbcodes_module.php new file mode 100644 index 0000000..f2bcb80 --- /dev/null +++ b/acp/bbcodes_module.php @@ -0,0 +1,78 @@ +get('language'); + $request = $phpbb_container->get('request'); + $template = $phpbb_container->get('template'); + $wiki_only_bbcodes = $phpbb_container->get('phpbbmodders.wiki.wiki_only_bbcodes'); + + if (!function_exists('adm_back_link')) + { + include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); + } + + $language->add_lang('acp_wiki', 'phpbbmodders/wiki'); + + $this->tpl_name = 'acp_wiki_bbcodes'; + $this->page_title = $language->lang('ACP_WIKI_BBCODES'); + add_form_key('phpbbmodders_wiki_bbcodes'); + + if ($request->is_set_post('submit')) + { + if (!check_form_key('phpbbmodders_wiki_bbcodes')) + { + trigger_error($language->lang('FORM_INVALID') . adm_back_link($this->u_action), E_USER_WARNING); + } + + $wiki_only_bbcodes->save($request->variable('wiki_only_bbcodes', array(0))); + + trigger_error($language->lang('CONFIG_UPDATED') . adm_back_link($this->u_action)); + } + + $wiki_only = $wiki_only_bbcodes->ids(); + foreach ($wiki_only_bbcodes->custom_bbcodes() as $row) + { + $template->assign_block_vars('wiki_only_bbcodes', array( + 'ID' => (int) $row['bbcode_id'], + 'TAG' => rtrim($row['bbcode_tag'], '='), + 'HELPLINE' => $language->is_set($row['bbcode_helpline']) ? $language->lang($row['bbcode_helpline']) : $row['bbcode_helpline'], + 'S_CHECKED' => in_array((int) $row['bbcode_id'], $wiki_only, true), + )); + } + + // The explanations link to phpBB's own BBCodes page + $u_bbcodes = append_sid("{$phpbb_admin_path}index.$phpEx", 'i=acp_bbcodes&mode=bbcodes'); + $template->assign_vars(array( + 'WIKI_ONLY_BBCODES_EXPLAIN' => $language->lang('WIKI_ONLY_BBCODES_EXPLAIN', $u_bbcodes), + 'WIKI_ONLY_BBCODES_NONE' => $language->lang('WIKI_ONLY_BBCODES_NONE', $u_bbcodes), + 'U_ACTION' => $this->u_action, + )); + } +} diff --git a/adm/style/acp_wiki_bbcodes.html b/adm/style/acp_wiki_bbcodes.html new file mode 100644 index 0000000..aa33783 --- /dev/null +++ b/adm/style/acp_wiki_bbcodes.html @@ -0,0 +1,29 @@ + + + +

{L_ACP_WIKI_BBCODES}

+ +

{WIKI_ONLY_BBCODES_EXPLAIN}

+ +
+
+ {L_WIKI_ONLY_BBCODES} + +
+

{wiki_only_bbcodes.HELPLINE}
+
+
+ +

{WIKI_ONLY_BBCODES_NONE}

+ +
+ +
+ {L_SUBMIT} + + + {S_FORM_TOKEN} +
+
+ + diff --git a/config/services.yml b/config/services.yml index a57c590..0f7a49f 100644 --- a/config/services.yml +++ b/config/services.yml @@ -318,6 +318,13 @@ services: - '@language' - '%tables.wiki.article%' - '@user' + # The custom BBCodes an admin has made wiki-only + phpbbmodders.wiki.wiki_only_bbcodes: + class: phpbbmodders\wiki\wiki\wiki_only_bbcodes + arguments: + - '@config' + - '@dbal.conn' + - '%tables.bbcodes%' phpbbmodders.wiki.wikilink_listener: class: phpbbmodders\wiki\event\wikilink_listener arguments: @@ -326,6 +333,7 @@ services: - '@phpbbmodders.wiki.links' - '@phpbbmodders.wiki.files' - '@phpbbmodders.wiki.templates' + - '@phpbbmodders.wiki.wiki_only_bbcodes' tags: - { name: event.listener } diff --git a/docs/TODO/todo-wiki-markup.md b/docs/TODO/todo-wiki-markup.md index fcc2916..27b19bb 100644 --- a/docs/TODO/todo-wiki-markup.md +++ b/docs/TODO/todo-wiki-markup.md @@ -16,9 +16,9 @@ and `WIKIMAGIC` tags and the `html:` table tags on in `core.text_formatter_s9e_p `php bin/phpbbcli.php reparser:reparse phpbbmodders_wiki_article`. Templates are built too, in their first form: `{{Name}}`, `{{Name|a|b}}`, `{{Name|key=value}}`, `{{{param}}}` and `{{{param|default}}}`, `` and ``, expanded when an article is shown (see `wiki/templates.php`), nested ten deep with a loop check, with the parser functions (`#if`, -`#switch`, `#expr`, `#time`, string functions and others) and categories (`[[Category:Name]]`) on top. What is -left is listed under "Still to do" in "Templates and infoboxes" below, and in "Scope" (making admins' own -BBCodes wiki-only). +`#switch`, `#expr`, `#time`, string functions and others) and categories (`[[Category:Name]]`) on top. Admins can +make their own BBCodes wiki-only too (see "Scope"). What is left is listed under "Still to do" in +"Templates and infoboxes" below. ## Scope: wiki articles only @@ -57,8 +57,12 @@ Limits: Built, in a slightly different form: the wiki-only tags are switched on in `core.text_formatter_s9e_parse_before` only while an article is saved or -previewed (see "Built so far"). The ACP setting for making admins' own BBCodes -wiki-only is not built. +previewed (see "Built so far"). Admins' own BBCodes are ticked under +**Extensions > Wiki > BBCodes** and kept by id, so renaming one keeps the +choice (see `wiki/wiki_only_bbcodes.php`). Their buttons are left off every +editor but the wiki's, and a forum post that uses one is refused with +phpBB's own "You cannot use certain BBCodes" message rather than shown as +plain text. ## Syntax to add diff --git a/docs/images/wiki-acp-bbcodes.png b/docs/images/wiki-acp-bbcodes.png new file mode 100644 index 0000000..ef63199 Binary files /dev/null and b/docs/images/wiki-acp-bbcodes.png differ diff --git a/event/wikilink_listener.php b/event/wikilink_listener.php index 3bec75a..6ead3b5 100644 --- a/event/wikilink_listener.php +++ b/event/wikilink_listener.php @@ -58,15 +58,20 @@ class wikilink_listener implements EventSubscriberInterface /** @var \phpbbmodders\wiki\wiki\templates */ protected $templates; + /** @var \phpbbmodders\wiki\wiki\wiki_only_bbcodes */ + protected $wiki_only_bbcodes; + /** * @param \phpbb\controller\helper $helper Controller helper object * @param \phpbb\language\language $language Language object * @param \phpbbmodders\wiki\wiki\links $links Wiki links service * @param \phpbbmodders\wiki\wiki\files $files Wiki files service * @param \phpbbmodders\wiki\wiki\templates $templates Wiki templates service + * @param \phpbbmodders\wiki\wiki\wiki_only_bbcodes $wiki_only_bbcodes The admin's BBCodes kept to the wiki */ - public function __construct(\phpbb\controller\helper $helper, \phpbb\language\language $language, \phpbbmodders\wiki\wiki\links $links, \phpbbmodders\wiki\wiki\files $files, \phpbbmodders\wiki\wiki\templates $templates) + public function __construct(\phpbb\controller\helper $helper, \phpbb\language\language $language, \phpbbmodders\wiki\wiki\links $links, \phpbbmodders\wiki\wiki\files $files, \phpbbmodders\wiki\wiki\templates $templates, \phpbbmodders\wiki\wiki\wiki_only_bbcodes $wiki_only_bbcodes) { + $this->wiki_only_bbcodes = $wiki_only_bbcodes; $this->templates = $templates; $this->files = $files; $this->helper = $helper; @@ -83,6 +88,7 @@ public static function getSubscribedEvents() 'core.text_formatter_s9e_configure_after' => 'configure_wikilink', 'core.text_formatter_s9e_parse_before' => 'toggle_wiki_markup', 'core.text_formatter_s9e_render_after' => 'link_wikilinks', + 'core.display_custom_bbcodes_modify_sql' => 'hide_wiki_only_buttons', ); } @@ -264,8 +270,8 @@ public static function wiki_tags() } /** - * Point rendered wiki links at their articles, and mark the ones whose - * article doesn't exist yet. + * Let the parser read the wiki's own tags, and the BBCodes the admin made wiki-only, only + * while a wiki article is saved. * * @param \phpbb\event\data $event The event object * @return void @@ -274,7 +280,7 @@ public function toggle_wiki_markup($event) { $parser = $event['parser']->get_parser(); - foreach (self::wiki_tags() as $tag) + foreach (array_merge(self::wiki_tags(), $this->wiki_only_bbcodes->tag_names()) as $tag) { if (\phpbbmodders\wiki\wiki\markup::is_active()) { @@ -297,6 +303,32 @@ public function toggle_wiki_markup($event) } } + /** + * Leave the wiki-only BBCodes' buttons off every editor but the wiki's. + * + * @param \phpbb\event\data $event The event object + * @return void + */ + public function hide_wiki_only_buttons($event) + { + $ids = $this->wiki_only_bbcodes->ids(); + if (!$ids || \phpbbmodders\wiki\wiki\markup::is_active()) + { + return; + } + + $sql_ary = $event['sql_ary']; + $sql_ary['WHERE'] = '(' . $sql_ary['WHERE'] . ') AND b.bbcode_id NOT IN (' . implode(', ', $ids) . ')'; + $event['sql_ary'] = $sql_ary; + } + + /** + * Point rendered wiki links at their articles, and mark the ones whose + * article doesn't exist yet. + * + * @param \phpbb\event\data $event The event object + * @return void + */ public function link_wikilinks($event) { $html = $event['html']; diff --git a/language/en/acp_wiki.php b/language/en/acp_wiki.php index f6ade35..4b929fa 100644 --- a/language/en/acp_wiki.php +++ b/language/en/acp_wiki.php @@ -57,6 +57,11 @@ 'WIKI_EDIT_CONFLICT_INVALID' => 'The choice for when two people edit an article is not one of the choices.', 'WIKI_EDIT_LOCK_MINUTES' => 'Minutes an editor holds an article', 'WIKI_EDIT_LOCK_MINUTES_EXPLAIN' => 'How long the notice names an editor, or the lock lasts, after their last sign of work. It is extended every few minutes while their edit form is open, and when they preview, so it only runs out once they have left.', + + 'WIKI_ONLY_BBCODES' => 'BBCodes only for the wiki', + 'WIKI_ONLY_BBCODES_EXPLAIN' => 'Tick a custom BBCode to use it only in wiki articles. It then stops working in posts, private messages and signatures, and its button shows only in the wiki editor. Text saved before you tick it keeps its formatting until it is edited. Custom BBCodes are the ones added on the Posting » BBCodes page of the ACP.', + 'WIKI_ONLY_BBCODE' => 'Only in wiki articles', + 'WIKI_ONLY_BBCODES_NONE' => 'This board has no custom BBCodes yet. Add them on the Posting » BBCodes page of the ACP.', 'WIKI_EDIT_LOCK_MINUTES_INVALID' => 'Minutes an editor holds an article must be a number from %1$d to %2$d.', 'ACP_WIKI_STARTER_TEMPLATES_EXPLAIN' => 'Starter templates are ready-made templates for the wiki, adapted from Wikipedia and other wikis, from the phpbbmodders/wiki-templates repository. They are shared under the CC BY-SA 4.0 licence; each one says where it comes from at the top of its page. Get the templates first, then choose which to install. Nothing is installed until you press Install.', diff --git a/language/en/info_acp_wiki.php b/language/en/info_acp_wiki.php index 0b2eb6a..a8f3e1a 100644 --- a/language/en/info_acp_wiki.php +++ b/language/en/info_acp_wiki.php @@ -23,6 +23,7 @@ 'ACP_WIKI_SETTINGS' => 'Settings', 'ACP_WIKI_SEARCH' => 'Search', 'ACP_WIKI_STARTER_TEMPLATES' => 'Starter templates', + 'ACP_WIKI_BBCODES' => 'BBCodes', 'LOG_WIKI_STARTER_CHECK_FAILED' => 'Could not check the wiki starter templates repository for new templates
» GitHub could not be reached, or did not answer as expected.', 'LOG_WIKI_STARTER_INSTALLED' => 'Installed wiki starter templates
» %s', )); diff --git a/migrations/add_wiki_only_bbcodes.php b/migrations/add_wiki_only_bbcodes.php new file mode 100644 index 0000000..3af4120 --- /dev/null +++ b/migrations/add_wiki_only_bbcodes.php @@ -0,0 +1,41 @@ + '\phpbbmodders\wiki\acp\bbcodes_module', + 'modes' => array('settings'), + ), + )), + ); + } +} diff --git a/tests/unit/files_test.php b/tests/unit/files_test.php index a3a95d3..d13bf46 100644 --- a/tests/unit/files_test.php +++ b/tests/unit/files_test.php @@ -69,7 +69,8 @@ private function parse(string $text): string $this->createMock(\phpbb\language\language::class), $this->createMock(\phpbbmodders\wiki\wiki\links::class), $this->createMock(files::class), - $this->createMock(\phpbbmodders\wiki\wiki\templates::class) + $this->createMock(\phpbbmodders\wiki\wiki\templates::class), + $this->createMock(\phpbbmodders\wiki\wiki\wiki_only_bbcodes::class) ); $configurator = new \s9e\TextFormatter\Configurator; diff --git a/tests/unit/markup_test.php b/tests/unit/markup_test.php index 5034651..cb740dd 100644 --- a/tests/unit/markup_test.php +++ b/tests/unit/markup_test.php @@ -142,7 +142,8 @@ private function parse(string $text, bool $wiki_markup = true): string $this->createMock(\phpbb\language\language::class), $this->createMock(\phpbbmodders\wiki\wiki\links::class), $this->createMock(\phpbbmodders\wiki\wiki\files::class), - $this->createMock(\phpbbmodders\wiki\wiki\templates::class) + $this->createMock(\phpbbmodders\wiki\wiki\templates::class), + $this->createMock(\phpbbmodders\wiki\wiki\wiki_only_bbcodes::class) ); $configurator = new \s9e\TextFormatter\Configurator; @@ -406,7 +407,8 @@ private function render(string $text): string $this->createMock(\phpbb\language\language::class), $this->createMock(\phpbbmodders\wiki\wiki\links::class), $this->createMock(\phpbbmodders\wiki\wiki\files::class), - $this->createMock(\phpbbmodders\wiki\wiki\templates::class) + $this->createMock(\phpbbmodders\wiki\wiki\templates::class), + $this->createMock(\phpbbmodders\wiki\wiki\wiki_only_bbcodes::class) ); $configurator = new \s9e\TextFormatter\Configurator; diff --git a/tests/unit/wiki_only_bbcodes_test.php b/tests/unit/wiki_only_bbcodes_test.php new file mode 100644 index 0000000..dde81f2 --- /dev/null +++ b/tests/unit/wiki_only_bbcodes_test.php @@ -0,0 +1,132 @@ +assertSame(array(13, 15), wiki_only_bbcodes::parse_ids('13,15')); + $this->assertSame(array(13, 15), wiki_only_bbcodes::parse_ids(' 13 , 15,13,0,-2,x')); + $this->assertSame(array(), wiki_only_bbcodes::parse_ids('')); + } + + public function test_tag_names_match_the_parser() + { + $this->assertSame('YOUTUBE', wiki_only_bbcodes::tag_name('youtube')); + $this->assertSame('FONT', wiki_only_bbcodes::tag_name('font=')); + } + + public function test_save_keeps_only_custom_bbcodes() + { + $config = new \phpbb\config\config(array('wiki_only_bbcodes' => '')); + $bbcodes = new class($config, $this->createMock(\phpbb\db\driver\driver_interface::class), 'phpbb_bbcodes') extends wiki_only_bbcodes { + public function custom_bbcodes() + { + return array( + array('bbcode_id' => '13', 'bbcode_tag' => 'youtube', 'bbcode_helpline' => ''), + array('bbcode_id' => '15', 'bbcode_tag' => 'font=', 'bbcode_helpline' => ''), + ); + } + }; + + $bbcodes->save(array(15, 99, 13, 15)); + $this->assertSame('13,15', $config['wiki_only_bbcodes']); + $this->assertSame(array(13, 15), $bbcodes->ids()); + + $bbcodes->save(array()); + $this->assertSame('', $config['wiki_only_bbcodes']); + $this->assertSame(array(), $bbcodes->tag_names(), 'no query when nothing is wiki-only'); + } + + public function test_a_wiki_only_bbcode_is_read_only_in_wiki_articles() + { + $bbcodes = $this->createMock(wiki_only_bbcodes::class); + $bbcodes->method('tag_names')->willReturn(array('YOUTUBE')); + $listener = new \phpbbmodders\wiki\event\wikilink_listener( + $this->createMock(\phpbb\controller\helper::class), + $this->createMock(\phpbb\language\language::class), + $this->createMock(\phpbbmodders\wiki\wiki\links::class), + $this->createMock(\phpbbmodders\wiki\wiki\files::class), + $this->createMock(\phpbbmodders\wiki\wiki\templates::class), + $bbcodes + ); + + $configurator = new \s9e\TextFormatter\Configurator; + $configurator->BBCodes->addCustom('[youtube]{URL}[/youtube]', '{URL}'); + $configurator->BBCodes->addCustom('[note]{TEXT}[/note]', '{TEXT}'); + $listener->configure_wikilink(array('configurator' => $configurator)); + extract($configurator->finalize()); + $wrapper = new class($parser) { + private $parser; + public function __construct($parser) + { + $this->parser = $parser; + } + public function get_parser() + { + return $this->parser; + } + }; + $text = '[youtube]https://example.com/v[/youtube] [note]n[/note]'; + + $listener->toggle_wiki_markup(array('parser' => $wrapper)); + $xml = $parser->parse($text); + $this->assertStringNotContainsString('assertStringContainsString('toggle_wiki_markup(array('parser' => $wrapper)); + $this->assertStringContainsString('parse($text), 'on in a wiki article'); + } + finally + { + \phpbbmodders\wiki\wiki\markup::set_active(false); + } + } + + public function test_wiki_only_buttons_are_left_out_of_other_editors() + { + $bbcodes = $this->createMock(wiki_only_bbcodes::class); + $bbcodes->method('ids')->willReturn(array(13, 15)); + $listener = new \phpbbmodders\wiki\event\wikilink_listener( + $this->createMock(\phpbb\controller\helper::class), + $this->createMock(\phpbb\language\language::class), + $this->createMock(\phpbbmodders\wiki\wiki\links::class), + $this->createMock(\phpbbmodders\wiki\wiki\files::class), + $this->createMock(\phpbbmodders\wiki\wiki\templates::class), + $bbcodes + ); + + $event = new \phpbb\event\data(array('sql_ary' => array('WHERE' => 'b.display_on_posting = 1'))); + $listener->hide_wiki_only_buttons($event); + $this->assertSame('(b.display_on_posting = 1) AND b.bbcode_id NOT IN (13, 15)', $event['sql_ary']['WHERE']); + + \phpbbmodders\wiki\wiki\markup::set_active(true); + try + { + $event = new \phpbb\event\data(array('sql_ary' => array('WHERE' => 'b.display_on_posting = 1'))); + $listener->hide_wiki_only_buttons($event); + $this->assertSame('b.display_on_posting = 1', $event['sql_ary']['WHERE'], 'the wiki editor keeps them'); + } + finally + { + \phpbbmodders\wiki\wiki\markup::set_active(false); + } + } +} diff --git a/wiki/functions.php b/wiki/functions.php index 35efcaf..88a2634 100644 --- a/wiki/functions.php +++ b/wiki/functions.php @@ -147,7 +147,16 @@ protected function set_active_version($id) protected function display_edit_form($preview = false) { generate_smilies('inline', 0); - display_custom_bbcodes(); + // With wiki markup on, the BBCodes the admin made wiki-only keep their buttons here + \phpbbmodders\wiki\wiki\markup::set_active(true); + try + { + display_custom_bbcodes(); + } + finally + { + \phpbbmodders\wiki\wiki\markup::set_active(false); + } add_form_key('article'); $this->template->assign_vars(array( diff --git a/wiki/wiki_only_bbcodes.php b/wiki/wiki_only_bbcodes.php new file mode 100644 index 0000000..d406b01 --- /dev/null +++ b/wiki/wiki_only_bbcodes.php @@ -0,0 +1,143 @@ +config = $config; + $this->db = $db; + $this->bbcodes_table = $bbcodes_table; + } + + /** + * @return int[] The ids of the wiki-only BBCodes + */ + public function ids() + { + $value = isset($this->config[self::CONFIG]) ? (string) $this->config[self::CONFIG] : ''; + + return self::parse_ids($value); + } + + /** + * @param string $value A comma-separated list + * @return int[] The positive ids in it, each once + */ + public static function parse_ids($value) + { + $ids = array_filter(array_map('intval', explode(',', $value)), function ($id) { + return $id > 0; + }); + + return array_values(array_unique($ids)); + } + + /** + * Keep a new choice, leaving out ids that are not custom BBCodes. + * + * @param int[] $ids + * @return void + */ + public function save(array $ids) + { + $ids = self::parse_ids(implode(',', $ids)); + $known = array_column($this->custom_bbcodes(), 'bbcode_id'); + $ids = array_values(array_intersect($ids, array_map('intval', $known))); + sort($ids); + + $this->config->set(self::CONFIG, implode(',', $ids)); + $this->tags = null; + } + + /** + * @return array The board's custom BBCodes (bbcode_id, bbcode_tag, bbcode_helpline), by tag + */ + public function custom_bbcodes() + { + $sql = 'SELECT bbcode_id, bbcode_tag, bbcode_helpline + FROM ' . $this->bbcodes_table . ' + ORDER BY bbcode_tag'; + $result = $this->db->sql_query($sql); + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + return $rows; + } + + /** + * The parser's names for the wiki-only BBCodes. phpBB clears this query's cache whenever + * a BBCode is added, changed or deleted in the ACP. + * + * @return string[] + */ + public function tag_names() + { + if ($this->tags !== null) + { + return $this->tags; + } + + $this->tags = array(); + $ids = $this->ids(); + if (!$ids) + { + return $this->tags; + } + + $sql = 'SELECT bbcode_tag + FROM ' . $this->bbcodes_table . ' + WHERE ' . $this->db->sql_in_set('bbcode_id', $ids); + $result = $this->db->sql_query($sql, 3600); + while ($row = $this->db->sql_fetchrow($result)) + { + $this->tags[] = self::tag_name($row['bbcode_tag']); + } + $this->db->sql_freeresult($result); + + return $this->tags; + } + + /** + * @param string $bbcode_tag As phpBB stores it, "color=" for [color=...] + * @return string The tag's name in the parser + */ + public static function tag_name($bbcode_tag) + { + return strtoupper(rtrim($bbcode_tag, '=')); + } +}