Module documentation

The master version of this module comes from English Wikipedia: w:Module:TrainingPages.

However here on multilingual Meta, it is being fixed to handle translations using the Translate extension:

  • This code requires template expansion when parsing the index list pages (containing transclusions of TNTN (because "Special:MyLanguage/" prefixes are still not supported)
  • Wikilinks that are present in noinclude sections of the index page will no longer be indexed for navigation (these links are also hidden when trascluding index pages in the main training page); but are accessible when viewing the index pages directly (from the link present under the topic name in the training page header)
  • It fixes another issue with the "/en" suffix which may appear in links once a training page has been prepared for translation (not all of them are translated at once, this avoids breaking existing trainings or new trainings in preparation that are still not ready for translation).

The module will then now work with or without translations (but most trainings in Meta or Commons should be translated). A localized Wiki may decide to only import one translated version and make it the default by dropping language code suffixes, or by renaming them after the import (leaving redirects which may break things)

The current code assumes English is the default language of the current wiki (true on Meta-Wiki) but is easy to fix if you import this module on a non-English localized wiki.

--[[
The purpose of this module is to take a list of linked page,
and use it to determine the next and previous page in the list
as well as the total number of pages.
--]]
local p = {}
 
function anonymize(name)
    return mw.ustring.gsub(name, "^" .. mw.site.siteName, "Project") or name
end

function out(name)
    return mw.ustring.gsub(name, "^Project", mw.site.siteName) or name
end

function keyize(pagename)
    -- there was a complaint about "_" breaking things.  Do all lookups with _ in place of any space.
    -- also spaces in the index file (non-module) were causing trouble
    pagename = mw.text.trim(pagename)
    pagename = mw.ustring.gsub(pagename, " ", "_")
    pagename = mw.uri.decode(pagename)
    pagename = anonymize(pagename)
    return pagename
end

function p.main(frame, displacement, varstoreturn)
    local parent = frame.getParent(frame)
    local currentpage, indexmodule, defaultpage, noerr, anonymizereturn
    local returnpage, currentpagenumber, totalpages
    ---- args in the #invoke itself trump args in the parent frame
    currentpage = frame.args.page and mw.text.trim(frame.args.page)
    defaultpage = frame.args.defaultpage and mw.text.trim(frame.args.defaultpage)
    indexmodule = frame.args.index and mw.text.trim(frame.args.index)
    displacement = displacement or frame.args.displacement -- can be passed from the other function names at the end
    noerr = frame.args.noerr -- used as boolean
    anonymizereturn = frame.args.anonymize -- used as boolean
    ---- args in the parent frame come next
    if parent then
        currentpage = currentpage or (parent.args.page and mw.text.trim(parent.args.page))
        indexmodule = indexmodule or (parent.args.index and mw.text.trim(parent.args.index)) -- index is a module: return {'page1', 'page2', ...} or a page containing wikilinks
        defaultpage = defaultpage or (parent.args.defaultpage and mw.text.trim(parent.args.defaultpage))
        noerr = noerr or parent.args.noerr
        anonymizereturn = anonymizereturn or parent.args.anonymize
    end
    -- Default values if parameters aren't provided.
    defaultpage = defaultpage or "" -- Don't know where to send people by default.
    if not(indexmodule) then
        returnpage = "[[Module:TrainingPages]] error: no index parameter specified"
        if noerr then returnpage = "" end
        if not(varstoreturn) then return returnpage else return returnpage, nil, nil, true end
    end
    if not(currentpage) then
        local pp = mw.title.getCurrentTitle()
        if not pp then -- This shouldn't happen anyway, I think...
            returnpage = "[[Module:TrainingPages]] error: failed to access getCurrentTitle"
            if noerr then returnpage = "" end
            if not(varstoreturn) then return returnpage else return returnpage, nil, nil, true end
        end
        currentpage = pp.fullText
    end
    local index = {}
    if mw.ustring.sub(indexmodule, 1, 6) == "Module" then
        -- Get a table of the pages in order from indexmodule.
        index = mw.loadData(indexmodule)
    else pp = mw.title.new(indexmodule)
        -- Get a table of the pages in order from a page containing a set of wikilinks.
        if not(pp) or pp.id == 0 then
            returnpage = "[[Module:TrainingPages]] error (''index'' parameter): failed to access mw.title.new(\"" .. tostring(indexmodule) .. "\") to load the index file"
            if noerr then returnpage = "" end
            if not(varstoreturn) then return returnpage else return returnpage, nil, nil, true end
        end
        --[==[
        Note that this still does not work with wikilinks going to the "Special:"
        namespace, notably "Special:MyLanguage/<namespace>:<pagename>".
        A workaround is to use [[{{TNTN|<namespace>:<pagename>}}|...]] in listed
        links. So each wikilink target containing  transclusions must be
        expanded.
        The links which may be present in noinclude parts of the index page are
        now also excluded from the navigation (and not counted) by this expansion
        (this hides links only accessible when viewing the index page directly).
        ]==]
        local textindex = frame:expandTemplate{title = ':' .. pp.prefixedText} -- was = pp.getContent(pp)
        if not textindex then
            returnpage = "[[Module:TrainingPages]] error (''index'' parameter): failed to access mw.title.new(\"" .. indexmodule .. "\"):getContent() to load the index data"
            if noerr then returnpage = "" end
            if not(varstoreturn) then return returnpage else return returnpage, nil, nil, true end
        end
        index = {}
        prowl = mw.ustring.gmatch(textindex, "%[%[(.-)[%]|]") -- first half of any wikilink
        while true do
            link = prowl() -- iterate to next match
            if not(link) then break end -- no more match
            link = mw.text.trim(link)
            if link ~= "" then table.insert(index, link) end
        end
    end
    displacement = displacement or 0 -- assume a null parameter is just display the same
    --[[
    Set up the reverse lookup in lookup.
    It would be faster to set this up in the indexmodule,
    but we don't want inconsistencies from user input!
    ]]
    local lookup = {}
    local i = 1
    while true do
        local j = index[i]
        if not j then break end
        lookup[keyize(j)] = i -- lookup["namespace:pagename"] => page number
        i = i + 1
    end
    --- get the page to return
    totalpages = #index
    if tonumber(currentpage) then
        currentpagenumber = tonumber(currentpage)
        returnpage = index[currentpagenumber + displacement] or defaultpage
    elseif (lookup[keyize(currentpage)]) then
        currentpagenumber = lookup[keyize(currentpage)]
        returnpage = index[currentpagenumber + displacement] or defaultpage
    elseif (lookup[keyize(currentpage .. "/en")]) then
	    --[==[
	    English may exist both as base of translation and as a translated subpage
	    The index page will expand to one or the other, depending if the page
	    was prepared for translation or not.
	    This is not necessary for other translations using language suffixes
	    (but their suffixe is not variable. This code assumes that there will
	    never be any page named like "namespace:pagename/languagecode/en")
	    --]==]
        currentpagenumber = lookup[keyize(currentpage .. "/en")]
        returnpage = index[currentpagenumber + displacement] or defaultpage
    else
        returnpage = defaultpage
    end
    if anonymizereturn then
        returnpage = anonymize(returnpage)
    else
        returnpage = out(returnpage)
    end
    if returnpage then returnpage = mw.text.trim(returnpage) end
    returnpage = tostring(returnpage)
    if not(varstoreturn) then return returnpage else return returnpage, currentpagenumber, totalpages end
end
 
--[[
Return the next page in the index.
* Used like this if on the current page that is part of the index:
    {{#invoke:TrainingPages|next_page
    | index = Project:Training/For students/Editing module index
    }}
* Used like this to find the next page after a specified page:
    {{#invoke:TrainingPages|next_page
    | index = Project:Training/For students/Editing module index
    | currentpage = Project:Training/For students/My sandbox
    }}
]]
function p.next_page(frame)
    local returnpage, pagenumber, totalpages, errcode = p.main(frame, 1, true)
    return returnpage
end
-- Abbreviated alias.
p.next = p.next_page

-- Same as above, but returns the previous page.
function p.last_page(frame)
    local returnpage, pagenumber, totalpages, errcode = p.main(frame, -1, true)
    return returnpage
end
-- Abbreviated alias.
p.last = p.last_page

-- Return the current page number as ordered in the index.
function p.page_number(frame)
    local returnpage, pagenumber, totalpages, errcode = p.main(frame, 0, true)
    if errcode then return returnpage else return pagenumber end
end
-- Abbreviated alias.
p.page = p.page_number

-- Return the total number of pages listed in the index.
function p.total_pages(frame)
    local returnpage, pagenumber, totalpages, errcode = p.main(frame, 0, true)
    if errcode then return returnpage else return totalpages end
end
-- Abbreviated alias.
p.total = p.total_pages

return p