diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index d45bc53c2..42eaf2a26 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -253,6 +253,41 @@ jobs: exit 1 fi + # Every page the sidebar names gets a navigation row, and the two ends of the sequence get + # one link rather than two. The order lives in MediaWiki:Sidebar and Module:Sequence reads + # it (#455); before that it was restated in each call, and what went wrong twice was + # exactly this -- a page in the sidebar with no row, and the last page with none either. + missing= + while IFS= read -r page; do + file="dist/$(printf '%s' "$page" | tr ' ' '_').html" + # The link div, not the wrapper: prevnext always emits the wrapper, so matching that + # passes an empty row -- which is exactly what a broken module produces, and what the + # first draft of this check waved through. + if [ ! -s "$file" ] || ! grep -q 'class="wikven-prevnext-' "$file"; then + missing="$missing $page" + fi + done < <(sed -n 's@^\*\* *Special:MyLanguage/\([^|]*\).*@\1@p' 'docs/MediaWiki:Sidebar.wikitext') + if [ -n "$missing" ]; then + echo "::error::sidebar pages with no navigation row:$missing" + exit 1 + fi + + # The two ends of the sequence get one link rather than two, which the old shape could not + # spell: a lone argument was the next step, so the last page had no way to say it had none. + # + # Matched on the div rather than the class name. TemplateStyles inlines prevnext/styles.css + # into every page that has a row, so both class names appear in the page's own stylesheet + # whether or not either link was rendered -- which is what the first draft of this check + # tripped over. + if grep -q 'class="wikven-prevnext-prev"' dist/Why_wikitext.html; then + echo "::error::Why wikitext heads the sequence and should have no previous link" + exit 1 + fi + if grep -q 'class="wikven-prevnext-next"' dist/Licenses.html; then + echo "::error::Licenses ends the sequence and should have no next link" + exit 1 + fi + # A prevnext link is labelled with the target page's own title, so on a translated page it # must carry the translated one. The label is not in the calling page's source: the call # sits outside the translate tags and passes a page name, and the template reads the diff --git a/docs/Commands.wikitext b/docs/Commands.wikitext index 33f239cbb..69709312b 100644 --- a/docs/Commands.wikitext +++ b/docs/Commands.wikitext @@ -193,4 +193,4 @@ Runs translate check over a source tree and reports each finding as |} -{{prevnext|Standalone binary|Development}} +{{prevnext}} diff --git a/docs/Configuration.wikitext b/docs/Configuration.wikitext index 5f85d733d..125b2b2e8 100644 --- a/docs/Configuration.wikitext +++ b/docs/Configuration.wikitext @@ -222,4 +222,4 @@ docker run --rm --entrypoint cat ghcr.io/chaotic-ground/wikven extensions/Wikven It is also in the [https://github.com/chaotic-ground/wikven/blob/main/default.yml repository]. -{{prevnext|Troubleshooting|Standalone binary}} +{{prevnext}} diff --git a/docs/Deploying.wikitext b/docs/Deploying.wikitext index 9b3cc62fa..f9d7ea236 100644 --- a/docs/Deploying.wikitext +++ b/docs/Deploying.wikitext @@ -182,4 +182,4 @@ A commit that follows a branch is the same manifest with the source This site is the working example. Its manifests are in [https://github.com/chaotic-ground/wikven/tree/main/updatecli/updatecli.d updatecli/updatecli.d/] (one release tag per reference pin, one branch head per commit pin), and [https://github.com/chaotic-ground/wikven/blob/main/.github/workflows/updatecli.yml a weekly workflow] applies them, opening one pull request per pipeline. [https://www.updatecli.io/docs/ Upstream's own documentation] covers everything a manifest can do beyond the two shapes above. -{{prevnext|Translating|Troubleshooting}} +{{prevnext}} diff --git a/docs/Development.wikitext b/docs/Development.wikitext index 892983e38..377976416 100644 --- a/docs/Development.wikitext +++ b/docs/Development.wikitext @@ -196,4 +196,4 @@ The e2e run serves dist/ under a /wikven/ path prefix, Versions follow [https://www.conventionalcommits.org/ Conventional Commits], which release-please uses to generate the changelog and bump the version. -{{prevnext|Commands|Writing an extension}} +{{prevnext}} diff --git a/docs/Editing sidebar.wikitext b/docs/Editing sidebar.wikitext index 71de7bc39..5b7d6f679 100644 --- a/docs/Editing sidebar.wikitext +++ b/docs/Editing sidebar.wikitext @@ -55,4 +55,4 @@ Some default sidebar blocks need a live wiki, so {{SITENAME}} drops them from th * [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Interface/Sidebar Manual:Interface/Sidebar] -{{prevnext|Pages|Images}} +{{prevnext}} diff --git a/docs/Extensions.wikitext b/docs/Extensions.wikitext index 478ac886a..50b9ce4eb 100644 --- a/docs/Extensions.wikitext +++ b/docs/Extensions.wikitext @@ -88,4 +88,4 @@ Scribunto is one of those, and the one most wikis reach for: it has a page of it An extension of your own can also be made to survive a build deliberately; [[Special:MyLanguage/Writing an extension|Writing an extension]] is the contract for that. -{{prevnext|Skins|Lua modules}} +{{prevnext}} diff --git a/docs/Getting Started.wikitext b/docs/Getting Started.wikitext index ce10984fc..c9782e601 100644 --- a/docs/Getting Started.wikitext +++ b/docs/Getting Started.wikitext @@ -128,4 +128,4 @@ echo '{{Note|Back up your wiki first.}}' >> src/index.wikitext Build again: the home page now shows the note. See [[Special:MyLanguage/Pages#Templates|Pages#Templates]] for parameters and more. -{{prevnext|Installation|Pages}} +{{prevnext}} diff --git a/docs/Images.wikitext b/docs/Images.wikitext index baffbb3a0..dbe4e0933 100644 --- a/docs/Images.wikitext +++ b/docs/Images.wikitext @@ -31,4 +31,4 @@ The thumbnailing backend is detected from the tools present at build time: [File: namespace, the same way other namespaced pages are named. For My photo.png, that is File:My photo.png.wikitext. -{{prevnext|Editing sidebar|Skins}} +{{prevnext}} diff --git a/docs/Installation.wikitext b/docs/Installation.wikitext index 534574f29..4485a46b8 100644 --- a/docs/Installation.wikitext +++ b/docs/Installation.wikitext @@ -61,4 +61,4 @@ This produces a local wikven image you run exactly like the publish Once you have {{SITENAME}}, head to [[Special:MyLanguage/Getting Started|Getting Started]] to build your first site. -{{prevnext|Why wikitext|Getting Started}} +{{prevnext}} diff --git a/docs/JavaScript.wikitext b/docs/JavaScript.wikitext index ff854f631..418f1dac6 100644 --- a/docs/JavaScript.wikitext +++ b/docs/JavaScript.wikitext @@ -38,4 +38,4 @@ Only gadgets marked default are loaded: a static site has no logged These docs ship one such gadget, PersistTabber, which remembers whether you pick the Docker or the binary tab in the install steps. Every other tabber on the page switches to the tab you picked, and the choice is still there when you move to another page. -{{prevnext|Lua modules|Searching}} +{{prevnext}} diff --git a/docs/Licenses.wikitext b/docs/Licenses.wikitext index 9538c9bf1..b6f1d8946 100644 --- a/docs/Licenses.wikitext +++ b/docs/Licenses.wikitext @@ -24,4 +24,4 @@ __TOC__ The standalone binary is compiled with [https://frankenphp.dev/ FrankenPHP], which also links several Caddy modules; see the FrankenPHP project for their licenses. The Docker image keeps MediaWiki's own COPYING, and each component's full license text is available from its project. -{{prevnext|prev=Writing an extension}} +{{prevnext}} diff --git a/docs/Lua modules.wikitext b/docs/Lua modules.wikitext index 0cc623886..ec209e4cd 100644 --- a/docs/Lua modules.wikitext +++ b/docs/Lua modules.wikitext @@ -195,4 +195,4 @@ A refused build says so in its exit status as well as in words, so a script that This documentation site uses a module itself, and the page you are reading is baked with the image on every change, with the module's answer asserted to be in it. The binary is held to the same rule on a source tree of its own, x86-64 on every change and arm64 on every nightly, so each row of the table above is something a machine checks rather than something this page claims. -{{prevnext|Extensions|JavaScript}} +{{prevnext}} diff --git a/docs/Module:Sequence.lua b/docs/Module:Sequence.lua new file mode 100644 index 000000000..0f7cdee04 --- /dev/null +++ b/docs/Module:Sequence.lua @@ -0,0 +1,91 @@ +-- The order of the documentation, read from the one place it is written down. +-- +-- MediaWiki:Sidebar lists the pages in order, and Template:prevnext used to list them again as its +-- arguments. Nothing checked that the two agreed, and twice they did not: a page was added to the +-- sidebar without a chain entry, and the chain's last page was left with no row at all (#455). Both +-- are the same failure -- adding a page meant editing three files and forgetting one was invisible. +-- +-- The direction is forced. MediaWiki:Sidebar is read line by line by the skin rather than parsed as +-- wikitext, so it cannot call anything; but it is an ordinary page, so anything can read it. +-- +-- What the sidebar contributes is order alone. The link labels stay where they were, in +-- Template:prevnext/label, which reads each target's own translated title. +-- +-- Named with the .lua marker, which Special:MyLanguage/Lua modules describes without recommending: +-- the page is Module:Sequence.lua, and Template:prevnext spells the invoke that way. +local p = {} + +-- Where the order lives, and the entry line to read out of it: "** Special:MyLanguage/Page|key". +local SIDEBAR = 'MediaWiki:Sidebar' +local ENTRY = '^%*%*%s*Special:MyLanguage/([^|\n]+)' + +-- Both sidebar groups, in one sequence. The chain has always crossed from the docs group into the +-- references group, and a reader at the end of the first group is better sent on than stopped. +local function sequence() + local sidebar = mw.title.new(SIDEBAR) + local content = sidebar and sidebar:getContent() + local pages = {} + if content then + for line in mw.text.gsplit(content, '\n') do + local target = line:match(ENTRY) + if target then + pages[#pages + 1] = mw.text.trim(target) + end + end + end + return pages +end + +-- Where this page sits in the sequence, or nil if the sidebar does not name it -- which is every +-- page that is not a step of the documentation. The main page is one of those: it is where a reader +-- arrives rather than a step they walk, and it names its own way on in its opening lines. +-- +-- A translation counts as its source page: "Searching/ko" is the sidebar's "Searching" in another +-- language, and the sidebar names the page it was translated from. Matched as a prefix rather than +-- by asking the title for its root, because rootText only strips a subpage where the namespace has +-- them turned on, and this one does not -- there, "Searching/ko" is one whole title. Anchoring on +-- the names the sidebar gives keeps that out of it: no entry is another entry plus a slash. +local function positionOf(pages, here) + for i, page in ipairs(pages) do + if here == page or here:sub(1, #page + 1) == page .. '/' then + return i + end + end + return nil +end + +-- The page on either side of this one, or nil at an end of the sequence. +local function neighbour(step) + local pages = sequence() + -- Loud rather than empty. A row that renders as nothing is the failure this module exists to + -- end, and it is the invisible kind: the wrapper div is still there and the page still looks + -- finished. If the sidebar stops being readable, that should stop a build, not reach a reader. + if #pages == 0 then + error(SIDEBAR .. ' has no "** Special:MyLanguage/Page" entries to read the order from', 0) + end + local here = positionOf(pages, mw.title.getCurrentTitle().text) + return here and pages[here + step] or nil +end + +local function link(frame, page, before, after) + local label = frame:expandTemplate{ title = 'prevnext/label', args = { page } } + return before .. '[[Special:MyLanguage/' .. page .. '|' .. label .. ']]' .. after +end + +-- The row: a previous link, a next link, or both. A page the sidebar does not name gets neither, +-- which is what every page outside the sequence -- a template, a category, a File: page -- should +-- get if it ever calls this. +function p.row(frame) + local out = {} + local previous = neighbour(-1) + if previous then + out[#out + 1] = '
' .. link(frame, previous, '← ', '') .. '
' + end + local following = neighbour(1) + if following then + out[#out + 1] = '
' .. link(frame, following, '', ' →') .. '
' + end + return table.concat(out) +end + +return p diff --git a/docs/Pages.wikitext b/docs/Pages.wikitext index 2a7b7e2f2..82c5112de 100644 --- a/docs/Pages.wikitext +++ b/docs/Pages.wikitext @@ -87,4 +87,4 @@ saved as Template:Note.wikitext and used on a page as: renders as "'''Note:''' Back up your wiki first." Templates take positional ({{{1}}}) and named parameters and run [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Parser_functions parser functions] exactly as in MediaWiki. The syntax itself is standard MediaWiki; see [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Templates Help:Templates]. -{{prevnext|Getting Started|Editing sidebar}} +{{prevnext}} diff --git a/docs/Searching.wikitext b/docs/Searching.wikitext index 67791c5f5..824d7ac91 100644 --- a/docs/Searching.wikitext +++ b/docs/Searching.wikitext @@ -81,4 +81,4 @@ containing…" action and its Enter key go to that page. This site uses a page titled Search. -{{prevnext|JavaScript|Translating}} +{{prevnext}} diff --git a/docs/Skins.wikitext b/docs/Skins.wikitext index 4f3243d66..b246babb4 100644 --- a/docs/Skins.wikitext +++ b/docs/Skins.wikitext @@ -153,4 +153,4 @@ The other six the skin reads off the configuration itself, so they behave as doc What the inert ones would have configured, {{SITENAME}} supplies its own way where it can: the colour theme is on the Settings page the build writes in place of Special:MobileOptions, and the site's own navigation is written into the main menu of every rendered page. -{{prevnext|Images|Extensions}} +{{prevnext}} diff --git a/docs/Standalone binary.wikitext b/docs/Standalone binary.wikitext index a65fae12e..ca51b8f22 100644 --- a/docs/Standalone binary.wikitext +++ b/docs/Standalone binary.wikitext @@ -133,4 +133,4 @@ The binary renders on its own, but uses these host programs when they are presen {{note|Fetching over HTTPS, Wikimedia Commons images via InstantCommons and any [[Special:MyLanguage/Configuration#WikvenRepositories|third-party extensions or skins]], relies on your system's CA certificates. On a minimal system without them, install your distribution's CA bundle (for example the ca-certificates package). A site with only local content and images needs no network access.}} -{{prevnext|Configuration|Commands}} +{{prevnext}} diff --git a/docs/Template:prevnext.wikitext b/docs/Template:prevnext.wikitext index e1b050d03..13fc49e8a 100644 --- a/docs/Template:prevnext.wikitext +++ b/docs/Template:prevnext.wikitext @@ -1,11 +1,7 @@
{{#if:{{{2|}}}{{{prev|}}}| -
← [[Special:MyLanguage/{{{prev|{{{1|}}}}}}|{{prevnext/label|{{{prev|{{{1|}}}}}}}}]]
- }}{{#if:{{{2|{{{1|}}}}}}|
[[Special:MyLanguage/{{{2|{{{1|}}}}}}|{{prevnext/label|{{{2|{{{1|}}}}}}}}]] →
}}{{#invoke:Sequence.lua|row}}
{{prevnext/doc}} diff --git a/docs/Template:prevnext/doc.wikitext b/docs/Template:prevnext/doc.wikitext index aa06ca596..417d3f2a6 100644 --- a/docs/Template:prevnext/doc.wikitext +++ b/docs/Template:prevnext/doc.wikitext @@ -2,24 +2,31 @@ Bottom-of-page navigation between the steps of the documentation, as a row of on == Usage == - {{prevnext|next step}} -{{prevnext|previous step|next step}} -{{prevnext|prev=previous step}} + {{prevnext}} -Each argument is a page name, not a link label: it is the link target, and the link text is the -target's own title. +No arguments. The order of the documentation is [[MediaWiki:Sidebar]], and +Module:Sequence.lua reads it: the page before this one in the sidebar becomes the previous +link, the page after it the next. The first and last pages of the sequence get one link rather than +two, without having to say so, and a page the sidebar does not name gets no row at all. -The next page is the last positional argument, so one of them is a next and two are a previous and -a next. The last page of the chain is the one form that leaves out: it has a previous and no next, -and an empty second argument reads the same as an absent one, so there is no positional way to say -it. That page names prev= instead, which is otherwise the same previous link. +It used to take the page names, which meant the order was written down twice -- once as the +sidebar's and once across twenty-one calls -- with nothing checking that the two agreed. Twice they +did not: a page reached the sidebar without a call naming it, and the last page of the chain had no +row, because a lone argument read as the *next* step and there was no way to spell "nothing after +this". Adding a page meant three edits, and forgetting the third was invisible. Now it is two: the +sidebar, and a {{prevnext}} at the foot of the new page. + +The main page has no row. It is not a sidebar entry -- every skin already points its logo at it -- +and it is where a reader arrives rather than a step they walk, so it names its own way on in its +opening lines instead. == Translated pages == A prevnext sits outside the <translate> tags, so it is copied whole into every -translation of the page it is on. That is why it takes page names alone: a label passed in would be -a second copy of the target's title in every page that links to it, and one nothing checks, so -renaming a page would leave the links to it quietly wrong in every language. +translation of the page it is on. That is why no label is written into the call, and was not when +the call still took page names: a label passed in would be a second copy of the target's title in +every page that links to it, and one nothing checks, so renaming a page would leave the links to it +quietly wrong in every language. Template:prevnext/label reads that title instead, from the one place it is translated: the target's title unit, which the build writes to Translate's "Page display title" diff --git a/docs/Translating.wikitext b/docs/Translating.wikitext index cd30f70b1..3232f8aa9 100644 --- a/docs/Translating.wikitext +++ b/docs/Translating.wikitext @@ -147,4 +147,4 @@ $ docker run --rm -v "$PWD/src:/workspace/src" ghcr.io/chaotic-ground/wikven tra The [https://github.com/chaotic-ground/wikven/tree/main/actions/check-translations check-translations action] runs the same check on pull requests, reporting each one as an annotation on the file it belongs to. It fails the build on a broken source page and never on a translation that is behind; pass gate: false to report even a broken page without failing. This documentation site is checked this way. To bring a translation back up to date, edit it to match the new source, then translate stamp it again. -{{prevnext|Searching|Deploying}} +{{prevnext}} diff --git a/docs/Troubleshooting.wikitext b/docs/Troubleshooting.wikitext index 7a3f53570..948341536 100644 --- a/docs/Troubleshooting.wikitext +++ b/docs/Troubleshooting.wikitext @@ -75,4 +75,4 @@ Every parse of a page embedding a Commons image asks Commons for its thumbnail. On a site with several skins, each is rendered by its own process and the build reports each one that failed by name (build failed for skin ...). The output of each pass is printed under its own heading, so read the section for the named skin rather than the end of the log. If the machine is short of memory, WIKVEN_BUILD_JOBS=1 runs the passes one at a time; see [[Special:MyLanguage/Commands|Commands]]. -{{prevnext|Deploying|Configuration}} +{{prevnext}} diff --git a/docs/Why wikitext.wikitext b/docs/Why wikitext.wikitext index fa18af7ac..a73f5f113 100644 --- a/docs/Why wikitext.wikitext +++ b/docs/Why wikitext.wikitext @@ -30,4 +30,4 @@ __TOC__ That last cost, running MediaWiki, is the one {{SITENAME}} removes: it renders your wikitext through MediaWiki once, at build time, and ships plain static HTML. You get templates, parser functions and the rest with nothing to run in production. Reach for {{SITENAME}} when that trade is worth it, typically when your content is already wikitext, or when templates and transclusion would save real work. -{{prevnext|Installation}} +{{prevnext}} diff --git a/docs/Writing an extension.wikitext b/docs/Writing an extension.wikitext index 650a50411..4bfdf34af 100644 --- a/docs/Writing an extension.wikitext +++ b/docs/Writing an extension.wikitext @@ -150,4 +150,4 @@ Worth checking specifically: * [[Special:MyLanguage/JavaScript|JavaScript]] — how scripts and gadgets are bundled for a static host. -{{prevnext|Development|Licenses}} +{{prevnext}} diff --git a/docs/index.wikitext b/docs/index.wikitext index 138083de1..eb1651038 100644 --- a/docs/index.wikitext +++ b/docs/index.wikitext @@ -54,5 +54,4 @@ These need a live wiki, so they are not part of the static export: What replaces some of them is written down: the reader's own display choices are on a [[Special:MyLanguage/Skins#Settings|Settings page]] the build writes, and searching is [[Special:MyLanguage/Searching|done in the browser]]. -{{prevnext|Why wikitext}} {{DISPLAYTITLE:Wikven}}