From 2381c50a73755efa71f2c170e0023c35feff8bd4 Mon Sep 17 00:00:00 2001 From: William Jacoby Date: Mon, 12 Oct 2026 20:32:47 -0500 Subject: [PATCH] Show a template's documentation and its text on its own page The page of a Template: article showed as text and worked out its own calls with no parameters. It now shows the documentation (the noinclude parts, with their calls worked out so they can show examples), then the template text as written, and how many articles use it. includeonly text is left off the page. Co-Authored-By: Claude Sonnet 5.5 --- README.md | 2 +- config/services.yml | 1 + docs/TODO/todo-wiki-markup.md | 1 - language/en/link.php | 6 +++ styles/prosilver/template/article_body.html | 6 +++ styles/prosilver/theme/style.css | 11 +++++ tests/unit/templates_test.php | 9 ++++ wiki/templates.php | 49 +++++++++++++++++++++ wiki/view.php | 33 ++++++++++++-- 9 files changed, 112 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b8815f2..af82822 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ An article - Limits, so one article cannot slow the board down: at most 500 template calls written in an article, 2000 calls and functions in all (including those inside templates), and about 500,000 characters of output. Past a limit the rest of the calls show "Too many templates" where they stand. - A template can include other templates, ten levels deep at most; a template that includes itself, directly or through another template, shows an error where the loop is (it cannot stop its own recursion with `{{#if:}}`, as on Wikipedia). A call to a template that does not exist is a red link to `Template:Name`. - The text of a template is parsed like article text, so it can use BBCode, `[[links]]`, headings and tables, and nothing an article could not. -- **What links here** on a template page lists the articles that use it. +- The page of a template shows its documentation (what is inside ``, with its calls worked out, so it can show examples), then **Template text**: the text an article gets, as it is written and not worked out, with how many articles use it. `` text is left off the page. **What links here** lists the articles that use the template. - Like headings and tables, this works in wiki articles only; in a forum post `{{Name}}` stays plain text. The first letter of a template name is not case-sensitive (`{{out of date}}` finds `Template:Out of date`). - **Parser functions** decide what a template shows. Only the part that is chosen is worked out, so the branch that is not taken can hold anything. diff --git a/config/services.yml b/config/services.yml index 3d4448f..331b163 100644 --- a/config/services.yml +++ b/config/services.yml @@ -168,6 +168,7 @@ services: - '@phpbbmodders.wiki.discussion' - '@phpbbmodders.wiki.files' - '@phpbbmodders.wiki.watch' + - '@phpbbmodders.wiki.templates' phpbbmodders.wiki.discussion: class: phpbbmodders\wiki\wiki\discussion arguments: diff --git a/docs/TODO/todo-wiki-markup.md b/docs/TODO/todo-wiki-markup.md index 0baf3da..42e7eee 100644 --- a/docs/TODO/todo-wiki-markup.md +++ b/docs/TODO/todo-wiki-markup.md @@ -163,7 +163,6 @@ out. Still to do: 400 calls to a template took about 400 ms longer than the same table written out), one extra query for the template pages however many calls there are, and at most 500 calls and 2000 functions per article. Add the cache only if articles with hundreds of calls become common. -- Template pages show `` and `` as text; a tidier template page view. - A template call holds other calls in its parameters three levels deep, and a default value holds one level of `{{template}}`; deeper nesting stays text. - A list of templates and a "Template" start-point in the article picker. diff --git a/language/en/link.php b/language/en/link.php index 7194ddb..ec728c6 100644 --- a/language/en/link.php +++ b/language/en/link.php @@ -44,6 +44,12 @@ 'WIKI_LINK_NEW_ARTICLE' => 'This article doesn’t exist yet. Follow the link to create it.', 'WIKI_WHAT_LINKS_HERE' => 'What links here', 'WIKI_TEMPLATE_LOOP' => 'Template loop: “%s” includes itself', + 'WIKI_TEMPLATE_TEXT' => 'Template text', + 'WIKI_TEMPLATE_USED_ON' => array( + 0 => 'Not used in any article yet.', + 1 => 'Used in 1 article.', + 2 => 'Used in %d articles.', + ), 'WIKI_TEMPLATE_TOO_DEEP' => 'Too many templates inside templates at “%s”', 'WIKI_TEMPLATE_UNKNOWN_FUNCTION' => 'Unknown function “%s”', 'WIKI_TIME_INVALID' => 'Invalid time', diff --git a/styles/prosilver/template/article_body.html b/styles/prosilver/template/article_body.html index 2df1dfb..3b4ba21 100644 --- a/styles/prosilver/template/article_body.html +++ b/styles/prosilver/template/article_body.html @@ -48,6 +48,12 @@

{ARTICLE_TITLE} - {L_VER + +

{L_WIKI_TEMPLATE_TEXT}

+

{TEMPLATE_USED_ON}

+
{TEMPLATE_SOURCE}
+ + {L_SET_ACTIVE} diff --git a/styles/prosilver/theme/style.css b/styles/prosilver/theme/style.css index caee79c..5c4977e 100644 --- a/styles/prosilver/theme/style.css +++ b/styles/prosilver/theme/style.css @@ -134,3 +134,14 @@ a.wikilink-new { .wiki .content ul.post-buttons.wiki-buttons { margin-top: 0; } + +/* The text of a template, shown as it is written on the template's page */ +pre.wiki-template-source { + margin: 0.5em 0; + padding: 0.6em 0.8em; + overflow: auto; + white-space: pre-wrap; + border: 1px solid #ccc; + background-color: rgba(128, 128, 128, 0.08); + font-family: monospace; +} diff --git a/tests/unit/templates_test.php b/tests/unit/templates_test.php index 036557b..851eb57 100644 --- a/tests/unit/templates_test.php +++ b/tests/unit/templates_test.php @@ -90,4 +90,13 @@ public function test_noinclude_and_includeonly_decide_what_is_included() $this->assertSame('Body only here', templates::transclusion_text($text)); } + + public function test_the_documentation_of_a_template_is_what_stands_in_noinclude() + { + $text = "First note.Body{{{1}}} hidden \nSecond note.\n"; + + $this->assertSame("First note.\n\nSecond note.", templates::documentation($text)); + $this->assertSame('', templates::documentation('Body only')); + $this->assertSame('Body{{{1}}} hidden ', templates::transclusion_text($text)); + } } diff --git a/wiki/templates.php b/wiki/templates.php index 76f9c69..e3c99ba 100644 --- a/wiki/templates.php +++ b/wiki/templates.php @@ -787,6 +787,55 @@ public static function transclusion_text($text) return preg_replace('##i', '', $text); } + /** + * What to show on the page of a template: its documentation, which is what stands + * inside (worked out like an article, so it can show examples), and + * its text as a template, which is what an article gets, shown as it is written. + * + * @param string $article_text The stored text of the page + * @param string $bbcode_uid The page's bbcode_uid + * @return array The rendered documentation ("" when there is none) and the template text + */ + public function page_view($article_text, $bbcode_uid) + { + $edit = generate_text_for_edit($article_text, $bbcode_uid, OPTION_FLAG_BBCODE | OPTION_FLAG_SMILIES | OPTION_FLAG_LINKS); + $raw = htmlspecialchars_decode($edit['text'], ENT_COMPAT); + + $documentation = self::documentation($raw); + $html = ''; + if ($documentation !== '') + { + $uid = $bitfield = ''; + $flags = 0; + $was_active = markup::is_active(); + markup::set_active(true); + try + { + generate_text_for_storage($documentation, $uid, $bitfield, $flags, true, true, true); + } + finally + { + markup::set_active($was_active); + } + $html = generate_text_for_display($documentation, $uid, $bitfield, $flags); + } + + return array($html, trim(self::transclusion_text($raw))); + } + + /** + * The text inside the parts of a template page. + * + * @param string $text The template page's text + * @return string The parts one after the other, "" when there are none + */ + public static function documentation($text) + { + preg_match_all('#(.*?)#is', $text, $matches); + + return trim(implode("\n\n", array_map('trim', $matches[1]))); + } + /** * The text of the live version of a template page, as an editor would see it. * diff --git a/wiki/view.php b/wiki/view.php index bd9c7a4..a7d4b3b 100644 --- a/wiki/view.php +++ b/wiki/view.php @@ -48,6 +48,9 @@ class view extends \phpbbmodders\wiki\wiki\functions /** @var \phpbbmodders\wiki\wiki\watch */ protected $watch; + /** @var \phpbbmodders\wiki\wiki\templates */ + protected $templates; + /** @var string phpbb_root_path */ protected $phpbb_root_path; @@ -74,9 +77,11 @@ class view extends \phpbbmodders\wiki\wiki\functions * @param \phpbbmodders\wiki\wiki\discussion $discussion Start discussion object * @param \phpbbmodders\wiki\wiki\files $files Uploaded files object * @param \phpbbmodders\wiki\wiki\watch $watch Watched articles object + * @param \phpbbmodders\wiki\wiki\templates $templates Wiki templates object */ - public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver_interface $db, \phpbb\controller\helper $helper, \phpbb\template\template $template, \phpbb\user $user, \phpbbmodders\wiki\wiki\compare $compare, \phpbbmodders\wiki\wiki\edit $edit, $article_table, $phpbb_root_path, $php_ext, \phpbbmodders\wiki\wiki\links $links, \phpbbmodders\wiki\wiki\discussion $discussion, \phpbbmodders\wiki\wiki\files $files, \phpbbmodders\wiki\wiki\watch $watch) + public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver_interface $db, \phpbb\controller\helper $helper, \phpbb\template\template $template, \phpbb\user $user, \phpbbmodders\wiki\wiki\compare $compare, \phpbbmodders\wiki\wiki\edit $edit, $article_table, $phpbb_root_path, $php_ext, \phpbbmodders\wiki\wiki\links $links, \phpbbmodders\wiki\wiki\discussion $discussion, \phpbbmodders\wiki\wiki\files $files, \phpbbmodders\wiki\wiki\watch $watch, \phpbbmodders\wiki\wiki\templates $templates) { + $this->templates = $templates; $this->discussion = $discussion; $this->files = $files; $this->watch = $watch; @@ -196,7 +201,17 @@ public function view_article($article, $id = 0) $allow_bbcode = $allow_magic_url = $allow_smilies = true; // [[File:name]] in the text refers to this article's files \phpbbmodders\wiki\wiki\files::set_context($this->data['article_url']); - $this->message_parser->format_display($allow_bbcode, $allow_magic_url, $allow_smilies); + $template_source = null; + if (strpos($this->data['article_url'], \phpbbmodders\wiki\wiki\templates::PREFIX) === 0) + { + // The page of a template shows its documentation, and its text as it is written + list($documentation, $template_source) = $this->templates->page_view($this->data['article_text'], $this->data['bbcode_uid']); + $this->message_parser->message = $documentation; + } + else + { + $this->message_parser->format_display($allow_bbcode, $allow_magic_url, $allow_smilies); + } \phpbbmodders\wiki\wiki\files::set_context(null); if (!empty($this->data['article_redirect'])) @@ -242,8 +257,18 @@ public function view_article($article, $id = 0) $this->files->assign_to_template($this->data['article_url']); $this->watch->assign_to_template($this->data['article_url']); - // Other articles whose live version links here with [[ ]]. - foreach ($this->links->what_links_here($this->data['article_url']) as $linking) + $linking_articles = $this->links->what_links_here($this->data['article_url']); + if ($template_source !== null) + { + $this->template->assign_vars(array( + 'S_TEMPLATE_PAGE' => true, + 'TEMPLATE_SOURCE' => htmlspecialchars($template_source, ENT_COMPAT, 'UTF-8'), + 'TEMPLATE_USED_ON' => $this->user->lang('WIKI_TEMPLATE_USED_ON', count($linking_articles)), + )); + } + + // Other articles whose live version links here with [[ ]], or uses it as a template + foreach ($linking_articles as $linking) { $this->template->assign_block_vars('what_links_here', array( 'TITLE' => $linking['article_title'],