Module:Subpages 7
local yesno = require('Module:Yesno')
local templateconfig = require('Module:Subpages 7/config')
-- localize functions local insert = table.insert local len = string.len local sub = string.sub local gsub = string.gsub local lc = string.lower local match = string.match
local args = {}
-- From Module:Infobox -- Returns a table containing the numbers of the arguments that exist -- for the specified prefix. For example, if the prefix was 'data', and -- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}. local function getArgNums(prefix) local nums = {} for k, v in pairs(args) do local num = tostring(k):match('^' .. prefix .. '(%d*)$') if num then insert(nums, num) end end table.sort(nums) return nums end
local interwikiClasses = { wikia = "sm-Fandom", fandom = 'sm-Fandom', wgg = 'sm-WikiGG', mh = 'sm-Miraheze', wt = 'sm-Miraheze', wikisource = 'wm-Wikisource', }
local function interwikilink (target, text) local prefix = match(target, '^[^:]+') if prefix == 'https' or prefix == 'http' then return '[' .. target .. ' ' .. text .. ']' else return ('' .. text .. ''), interwikiClasses[ lc(prefix or ) ] end end
-- Gather tab data to save to bucket local function get_extra_tabs_for_main_page (page, template) local extraTabs = {} if template == 'work' then for _,lbl in ipairs{'source', 'fanwiki'} do local nums = getArgNums(lbl) if #nums > 0 then extraTabs[lbl] = {} for _,num in ipairs(nums) do local datum = { param = lbl, link = args[lbl .. num], label = args[lbl .. 'name' .. num] } insert( extraTabs[lbl], datum ) bucket( 'tab' ).put( datum ) end end end end -- Wikipedia section uses the label a bit different -- This section is what you call 'business logic' local wppage = args.wikipedia or args.wppage local no_wp = yesno(args.nowp) -- Only save to DB if it's not the default behavior if template == 'work' then extraTabs.wikipedia = { param = 'wikipedia', link = (not no_wp) and (wppage or page) or nil, label = (no_wp and 'nowp') or (not wppage and 'default') or nil } if wppage or no_wp then mw.logObject( extraTabs.wikipedia ) bucket( 'tab' ).put( extraTabs.wikipedia ) end else if wppage and not no_wp then extraTabs.wikipedia = { param = 'wikipedia', link = wppage, label = nil } bucket( 'tab' ).put( extraTabs.wikipedia ) end end return extraTabs end
-- Gather tab data from Bucket
local function get_extra_tabs_for_subpage (page)
local extraTabs = {}
local tabData = bucket('tab').select('param', 'link', 'label').where('page_name', page).run()
for _,d in ipairs(tabData) do
if extraTabs[d.param] then
insert(extraTabs[d.param], d)
else
extraTabs[d.param] = { d }
end
end
if extraTabs.wikipedia then
extraTabs.wikipedia = extraTabs.wikipedia[1]
end
return extraTabs
end
-- This function is the main page menu type, with dropdowns for subpages
function sp._menu ( frame, template )
local title = mw.title.getCurrentTitle()
local page = args['page'] or title.rootText
-- create variant top level page variables local mainpage = page; if template == "indextrope" then mainpage = ":" .. page page = page:gsub("Category:", "", 1) end local pageslash = page..'/'
local config = templateconfig[template] assert( config.page_order, "Module error: invalid template argument" )
-- Get Wikipedia, Source, Fan Wiki tabs local extraTabs, trackingCats = {}, {} if title.text == page then -- get args and save to bucket extraTabs = get_extra_tabs_for_main_page(page, template) if extraTabs.wikipedia then if extraTabs.wikipedia.label == 'nowp' then insert(trackingCats, ) elseif extraTabs.wikipedia.label ~= 'default' then insert(trackingCats, ) end end if extraTabs.fanwiki then insert(trackingCats, ) end if extraTabs.source then insert(trackingCats, ) end else -- Add short descriptions to subpages automatically subpage_short_description( title, page, config ) -- get args from bucket, so users only have to fill the top level page extraTabs = get_extra_tabs_for_subpage(page) if template == 'work' and not extraTabs.wikipedia then extraTabs.wikipedia = { link = page, label = 'default' } end end
local tree = build_subpage_tree(page, frame) local menu = mw.html.create('ul') :attr('role', 'menubar')
-- initialize result with Main link menu:tag('li') :addClass('sm-main') :wikitext('Main\n') :attr('role', 'menuitem')
local missingPages = { }
for _, subpage in ipairs(config.page_order) do local inserted = build_list( menu, { [subpage] = tree[subpage] }, pageslash, 1 ) if not inserted then insert(missingPages, subpage) end tree[subpage] = nil end
local crowners_tab = mw.html.create('li') :addClass('sm-' .. config.crowner_tab) :attr({["aria-haspopup"] = 'menu', role = 'menuitem'}) crowners_tab:tag('span') :wikitext(config.crowner_tab) :addClass('sm-crownermenu') local crowners_menu = crowners_tab:tag('ul')
for _, subpage in ipairs(config.crowner_order) do local inserted = build_list( crowners_menu, { [subpage] = tree[subpage] }, pageslash, 2 ) if not inserted then insert(missingPages, subpage) end -- exhaust this node of the tree, so that leftovers can be gathered for all pages tree[subpage] = nil end for _, subpage in ipairs(config.crowner_extra or {}) do local inserted = build_list( crowners_menu, { [subpage] = tree[subpage] }, pageslash, 2 ) -- exhaust this node of the tree, so that leftovers can be gathered for all pages tree[subpage] = nil end if #crowners_menu.nodes > 0 then menu:node( crowners_tab ) end
--deal with the leftover subpages local allSubpages = menu:tag('li'):addClass('sm-Allothers') :wikitext( 'All Subpages' ) :attr('role', 'menuitem') if next(tree) then local otherMenu = mw.html.create('ul') local ordered_keys = {} for k in pairs(tree) do insert(ordered_keys, k) end table.sort(ordered_keys)
for i = 1, #ordered_keys do local subpage = ordered_keys[i] inserted = build_list( otherMenu, { [subpage] = tree[subpage] }, pageslash, 2 ) end if #otherMenu.nodes > 0 then allSubpages:node( otherMenu ) end end
-- make extra tabs if extraTabs.wikipedia and extraTabs.wikipedia.label ~= 'nowp' then local li = menu:tag('li') :addClass('sm-Wikipedia') :attr('role', 'menuitem') :wikitext('Wikipedia') if extraTabs.wikipedia.label == 'default' then li:addClass('sm-wpdefault') end end -- Out of laziness, just make a second source tab even if we already have subpages -- This one might end up with a different label anyway local eTabName = { fanwiki = "Fan Wiki", source = "Source" } local eTabCSS = { fanwiki = "sm-Fanwiki", source = "sm=Source" } for _,tab in ipairs({'fanwiki', 'source'}) do if extraTabs[tab] then if #extraTabs[tab] == 1 then link, cssclass = interwikilink(extraTabs[tab][1].link, extraTabs[tab][1].label or eTabName[tab]) menu:tag('li') :addClass(cssclass or eTabCSS[tab]) :attr('role', 'menuitem') :wikitext(link) else local li = menu:tag('li') :addClass('sm-Fanwiki') :attr({["aria-haspopup"] = 'menu', role = 'menuitem'}) li:tag('strong'):wikitext(eTabName[tab]..'s') local ul = li:tag('ul') for k,v in ipairs(extraTabs[tab]) do link, cssclass = interwikilink(v.link, v.label or eTabName[tab]) local li = ul:tag('li') :addClass(cssclass or eTabCSS[tab]) :wikitext(link) :attr('role', 'menuitem') end end end end
-- Attach some JS data so the missing pages are created in the browser -- so that we don't create redlinks inside MW. It's totally okay if these pages -- are missing, so we don't need them reported in internal statistics. local missingPageList = table.concat( missingPages, "|" ) -- Discourage some tab creation missingPageList = gsub(missingPageList, '|Laconic', , 1) missingPageList = gsub(missingPageList, '|Source', , 1) createNewMenu = menu:tag('li') :addClass('sm-New') createNewMenu:tag('strong') :wikitext('Create New') createNewMenu:tag('ul') :addClass('sm-wantedpages') :attr({ ["data-toplevelpage"] = page, ["data-wanted"] = missingPageList, ["data-templatetype"] = template })
if template == 'work' then italictitle( frame, page ) end for _,cat in ipairs(trackingCats) do menu:wikitext(cat) end
return tostring( menu ) end
-- Another output function, which makes an ordinary bullet tree of subpages function sp.tree ( frame, args ) if frame == nil then frame = mw.getCurrentFrame() end
local page = frame.args[1] or tostring( mw.title.getCurrentTitle().rootPageTitle ) local filter = frame.args['filter'] or
local tree = build_subpage_tree(page, frame)
if filter ~= then for _, subpage in ipairs(templateconfig[filter].page_order) do tree[ subpage ] = nil end if filter ~= 'trope' then for _, subpage in ipairs(templateconfig[filter].crowner_order) do tree[ subpage ] = nil end end end
-- Weird category-trope workaround if page:match("^Category:") then page = ":"..page end
-- output local thispage = mw.title.getCurrentTitle().text ~= page and "; "..page.."\n" or local menu = mw.html.create('ul') build_list( menu, tree, page..'/', 1 ) return thispage .. tostring( menu ) end
-- transforms an array (table) of subpages into a tree data structure function build_subpage_tree ( page, frame ) local path_tree = { } local pagelenoffset = len(page) + 2 local gmatch = string.gmatch
-- Fetch as linked, to avoid Extension:Subpage Fun from calling wfEscapeWikiText -- which throws off character count local allsptxt = frame:callParserFunction('SUBPAGES', { page, linked = '1', sep = })
for fullpath in gmatch(allsptxt, '[^%[%]%:][^%[%]]+') do local path = sub(fullpath, pagelenoffset) local lastdir = path_tree
for dir in gmatch(path, '[^/]+') do if lastdir[dir] == nil then lastdir[dir] = { } end -- poor man's recursion, since we always go one level lower lastdir = lastdir[dir] end end return path_tree end
-- given a tree of subpages, outputs an indented list -- recursively calls itself to build deeper trees -- Params: -- rootElem: an mw.html class instance -- tree: a (sub)tree from build_tree -- prefix: the page name that is the parent of all items in the tree -- level: our depth in the tree, used for some CSS function build_list ( rootElem, tree, prefix, level ) -- don't create an element if there are no subpages if next(tree) == nil then return false end
-- Lua can't directly give us tables sorted by keys, so we work around it -- (which is basically Lua in a nutshell) local ordered_subpages = {} for page,_ in pairs(tree) do insert(ordered_subpages, page) end table.sort(ordered_subpages)
for i = 1, #ordered_subpages do local page = ordered_subpages[i] local branch = tree[page] local li = rootElem:tag('li') :attr('role', 'menuitem') :wikitext( ''..page..'')
if level <= 2 then li:addClass('sm-'..gsub(page, ' ', '_')) end
-- build subpages if next(branch) then local nextprefix = (prefix..page..'/') li:attr("aria-haspopup", 'menu') sublist = li:tag('ul') build_list(sublist, branch, nextprefix, level+1) end end return true end
function italictitle(frame, basepage) local title = mw.title.getCurrentTitle()
-- don't italicize subpages -- but we can't just naively split on '/' due to top level pages with slashes local subpages = sub(title.text, len(basepage)+1 )
-- Find the parts before and after the disambiguation parentheses, if any. local prefix, parentheses = mw.ustring.match(basepage, '^(.+) (%([^%(%)]+%))$')
-- If parentheses were found, italicise only the part before them. Otherwise -- italicise the whole title. local result if prefix and parentheses then result = "" .. prefix .. " " .. parentheses .. subpages else result = "" .. basepage .. "" .. subpages end
-- Add the namespace if we're not in mainspace. if title.namespace ~= 0 then result = mw.site.namespaces[title.namespace].name .. ':' .. result end
-- Call displaytitle with the text we generated. return mw.getCurrentFrame():callParserFunction( 'DISPLAYTITLE', result, 'noreplace' ) end
function subpage_short_description( title, basepage, config ) local descr = config.descriptions or {} if templateconfig._default == nil then templateconfig._default = { descriptions = {} } end -- Basepage could have a slash, so look after (also why we don't use basename, etc) local titlesections = mw.text.split(sub(title.text, len(basepage)+2 ), '/' ) local subtype = titlesections[1] local this_description = descr[subtype] or templateconfig._default.descriptions[subtype] or descr._default or templateconfig._default.descriptions._default
-- Check down to level 4, because we might want 'S3 E2' for a second param local fmtarg2 = titlesections[3] and (titlesections[2] .. ' ' .. titlesections[3]) or titlesections[2] or
if this_description then mw.getCurrentFrame():callParserFunction( 'SHORTDESC', string.format( this_description, basepage, fmtarg2 ) ); end end
function sp.menu (frame) args=getArgs (frame,{ wrappers = {'Template:Examples on subpages'} }) return sp._menu(frame, 'work'); end
-- Make functions for each item in config, currying the name -- Allows us to get one param from the template while saving time in a passthrough -- of the others. -- At this time, implements sp.trope, sp.work, sp.index, sp.creator for template,_ in pairs(templateconfig) do sp[template] = function (frame) args = getArgs(frame,{ wrappers = {'Template:Work', 'Template:Trope', 'Template:IndexTrope', 'Template:Creator', 'Template:Meta-trope', 'Template:Useful Notes', 'Template:Just for Fun', 'Template:Work test', 'Template:Trope test'} }) return sp._menu(frame, template) end end
sp.subpage_tree = build_subpage_tree sp.italictitle = italictitle sp.subpage_short_description = subpage_short_description
return sp