Module:Sister project links explained

require('strict')

-- Module to create sister project link boxlocal getArgs = require('Module:Arguments').getArgslocal sideBox = require('Module:Side box')._mainlocal p =

local inSandbox = mw.getCurrentFrame:getTitle:find('sandbox', 1, true)

-- Function to add "-sand" to classes when called from sandboxlocal function sandbox(s) return inSandbox and s.."-sand" or send

-- Information about how to handle each sister lives in separate data filelocal cfg = mw.loadData(sandbox('Module:Sister project links/config'))local logo = cfg.logolocal prefixList = cfg.prefixListlocal sisterName = cfg.sisterNamelocal sisterInfo = cfg.sisterInfolocal defaultSisters = cfg.defaultSisterslocal sisterDb = cfg.sisterDblocal trackingType = cfg.trackingType

-- Function to canonicalize string-- search for variants of "yes", and "no", and transform-- them into a standard form (like)-- Argument:-- s --- input string-- Result:-- list of length 2-- x = nil if s is canonicalized, otherwise has trimmed s-- y = canonical form of s (true if "yes" or other, false if "no", nil if blank)local function canonicalize(s) if s

nil then return end -- if s is table/list, then assume already canonicalized and return unchanged if tostring(type(s))

"table" then return s end s = mw.text.trim(tostring(s)) if s

"" then return end local lowerS = s:lower -- Check for various forms of "yes" if lowerS

'yes' or lowerS

'y' or lowerS

't' or lowerS

'1' or lowerS

'true' or lowerS

'on' then return end -- Check for various forms of "no" if lowerS

'no' or lowerS

'n' or lowerS

'f' or lowerS

'0' or lowerS

'false' or lowerS

'off'then return end -- Neither yes nor no recognized, leave string trimmed return end

-- Merge two or more canonicalized argument lists-- Arguments:-- argList = list of canonicalized arguments-- noAll = if true, return no when all argList is no.-- otherwise, return blank when all argList is blanklocal function mergeArgs(argList,noAll) local test = nil -- default, return blank if all blank if noAll then test = false -- return no if all no end local allSame = true -- Search through string for first non-no or non-blank for _, arg in ipairs(argList) do if arg[2] then return arg -- found non-no and non-blank, return it end -- test to see if argList is all blank / no allSame = allSame and (arg[2]

test) end -- if all blank / no, return blank / no if allSame then return -- all match no/blank, return it end -- otherwise, return no / blank if noAll then return end return end -- Function to get sitelink for a wiki-- Arguments:-- wiki = db name of wiki to lookup-- qid = QID of entity to search for, current page entity by defaultlocal function getSitelink(wiki,qid) -- return nil if some sort of lookup failure return qid and mw.wikibase.getSitelink(qid,wiki)end

-- Function to get sitelink for a wiki-- Arguments:-- prefix = prefix string for wiki to lookup-- qid = QID of entity to search for, current page entity by defaultlocal function fetchWikidata(prefix,qid) local sisterDbName = sisterDb[prefix] return sisterDbName and getSitelink(sisterDbName,qid)end

-- Function to generate the sister link itself-- Arguments:-- args = argument table for function-- args[1] = page to fetch-- args.default = link when blank-- args.auto = new auto mode (don't fall back to search)-- args.sitelink = wikidata sitelink (if available)-- args.qid = QID of entity-- args.search = fallback string to search for-- args.sisterPrefix = wikitext prefix for sister site-- args.information = type of info sister site contains-- tracking = tracking tablelocal function genSisterLink(args, tracking) if args[1][2]

false or (not args.default and args[1][2]

nil) then return nil --- either editor specified "no", or "blank" (and default=no), then skip this sister end local sitelink = args.sitelink or fetchWikidata(args.sisterPrefix,args.qid) if args.auto and not sitelink and args[1][2]

nil then return nil --- in auto mode, if link is blank and no sitelink, then skip end -- fallback order of sister link: first specified page, then wikidata, then search local link = args[1][1] or sitelink or (args.search and "Special:"..args.search) if not link then return nil --- no link found, just skip end if tracking then -- update state for tracking categories if args[1][1] and sitelink then -- transform supplied page name to be in wiki-format local page = mw.ustring.gsub(args[1][1],"_"," ") page = mw.ustring.sub(page,1,1):upper..mw.ustring.sub(page,2) local pageNS = mw.ustring.match(page,"^([^:]+):") local sitelinkNS = mw.ustring.match(sitelink,"^([^:]+):") if page

sitelink then tracking.wdHidden = args.sisterPrefix elseif pageNS ~= sitelinkNS then tracking.wdNamespace = args.sisterPrefix else tracking.wdMismatch = args.sisterPrefix end -- if no page link, nor a wikidata entry, and search is on, then warn elseif not (args[1][2] or sitelink) and args.search then tracking.defaultSearch = args.sisterPrefix end end return end

-- Function to handle special case of commons linklocal function commonsLinks(args, commonsPage) -- use to determine best commons link local commonsLink = require('Module:Commons link') local cLink = (not args.commonscat) and commonsLink._hasGallery(args.qid) or commonsLink._hasCategory(args.qid) if commonsPage[1] and not mw.ustring.match(commonsPage[1]:lower,"^category:") then commonsPage[1] = (args.commonscat and "Category:" or "")..commonsPage[1] end local commonsSearch = "Search/"..(args.commonscat and "Category:" or "")..args[1] return end

-- Function to handle special case for "author" and "cookbook"local function handleSubtype(args) local ns = args.ns local ns_len = mw.ustring.len(ns) local result = result.sitelink = fetchWikidata(args.prefix, args.qid) local subtype = false if args.page then if mw.ustring.sub(args.page,1,ns_len)

ns then subtype = true elseif args.subtype then result.page = ns..args.page subtype = true end elseif result.sitelink then subtype = mw.ustring.sub(result.sitelink,1,ns_len)

ns elseif args.subtype then result.search = "Search/"..ns..args.default subtype = true end if subtype then result.info = args.info end return resultend

-- Function to create a sister link, by prefix-- Arguments:-- prefix = sister prefix (e.g., "c" for commons)-- args = arguments for this sister (see p._sisterLink above)-- tracking = tracking tablelocal function sisterLink(prefix, args, tracking) -- determine arguments to genSisterLink according to prefix if prefix

'species_author' and not args.species[1] and args.species[2] and not args.species_author[1] and args.species_author[2] then return nil end local default = defaultSisters[prefix] if default

'auto' then default = args.auto end -- Handle exceptions by prefix local search = ((prefix

'd' and "ItemByTitle/enwiki/") or "Search/")..args[1] local sitelink = prefix

'd' and args.qid local page = args[prefix] local info = sisterInfo[prefix] -- special case handling of author and cookbook local subtype = nil if prefix

's' then subtype = handleSubtype elseif prefix

'b' then subtype = handleSubtype end if subtype then page[1] = subtype.page or page[1] search = subtype.search or search sitelink = subtype.sitelink or sitelink info = subtype.info or info end if prefix

'voy' then if not args.bar then info = "Travel information" end if page[1] then if mw.ustring.match(page[1],"phrasebook") then info = "Phrasebook" end elseif page[2] or args.auto then sitelink = sitelink or fetchWikidata('voy',args.qid) if sitelink and mw.ustring.match(sitelink,"phrasebook") then info = "Phrasebook" end end end info = args.information or info if prefix

'c' then local commons = commonsLinks(args, page) search = commons.search sitelink = commons.link end prefix = (prefix

'species_author' and 'species') or prefix local logo = logo[prefix] local name = sisterName[prefix] local prep = "from" if mw.ustring.sub(prefix,1,2)

'iw' then local lang = nil local iw_arg = args[prefix] if iw_arg[1] then lang = iw_arg[1] elseif iw_arg[2] then local P424 = mw.wikibase.getBestStatements(args.qid, "P424")[1] if P424 and P424.mainsnak.datavalue then lang = P424.mainsnak.datavalue.value end end if lang

nil then return nil end prefix = ':'..lang page[1] = "" page[2] = true local langname = mw.language.fetchLanguageName(lang, 'en') if not langname or #langname

0 then return nil end info = langname..' '..info prep = "of" end return genSisterLink(tracking)end

local function templatestyles_page(is_bar) local sandbox = inSandbox and 'sandbox/' or if is_bar then return mw.ustring.format('Module:Sister project links/bar/%sstyles.css', sandbox ) end return mw.ustring.format('Module:Sister project links/%sstyles.css', sandbox )end

-- Function to create html containers for sister project link list-- Arguments:-- args = table of arguments-- args.position: if 'left', position links to left-- args.collapsible: if non-empty, make box collapsible. If 'collapse', start box hidden-- args.style: CSS style string appended to end of default CSS-- args.display: boldface name to displaylocal function createSisterBox(sisterList, args)

local list = mw.html.create('ul') for i, link in ipairs(sisterList) do local li = list:tag('li') -- html element for 27px-high logo local logoSpan = li:tag('span') logoSpan:addClass(sandbox("sister-logo")) logoSpan:wikitext("") -- html element for link local linkspan = li:tag('span') linkspan:addClass(sandbox("sister-link")) local linkText = ""..link.information .." "..link.prep.." "..link.name linkspan:wikitext(linkText) end list:allDone return sideBoxend

local function createSisterBar(sisterList,args) local nav = mw.html.create('div') nav:addClass('noprint') nav:addClass('metadata') nav:addClass(sandbox('sister-bar')) nav:attr('role', 'navigation') nav:attr('aria-label', 'sister-projects') local header = nav:tag('div') header:addClass(sandbox('sister-bar-header')) local pagename = header:tag('b') pagename:wikitext(args.display or args[1]) local headerText = " at Wikipedia's " headerText = headerText..'sister projects:' header:wikitext(headerText) if #sisterList

1 and args.trackSingle then header:wikitext("") end local container = nav:tag('ul') container:addClass(sandbox('sister-bar-content')) for _, link in ipairs(sisterList) do local item = container:tag('li') item:addClass(sandbox('sister-bar-item')) local logoSpan = item:tag('span') logoSpan:addClass(sandbox('sister-bar-logo')) logoSpan:wikitext("") local linkSpan = item:tag('span') linkSpan:addClass(sandbox('sister-bar-link')) linkSpan:wikitext(""..link.information .." "..link.prep.." "..link.name) end return navend

function p._main(args) local titleObject = mw.title.getCurrentTitle local ns = titleObject.namespace -- find qid, either supplied with args, from search string, or from current page args.qid = args.qid or mw.wikibase.getEntityIdForTitle(args[1] or "") or mw.wikibase.getEntityIdForCurrentPage args.qid = args.qid and args.qid:upper -- search string defaults to PAGENAME args[1] = args[1] or mw.wikibase.getSitelink(args.qid or "") or titleObject.text -- handle redundant "commons"/"c" prefix args.c = args.c or args.commons -- Canonicalize all sister links (handle yes/no/empty) for _, k in ipairs(prefixList) do args[k] = canonicalize(args[k]) end -- Canonicalize cookbook args.cookbook = canonicalize(args.cookbook) args.b = mergeArgs args.cookbook = args.cookbook[2] -- handle trackSingle parameter if args.trackSingle

nil then args.trackSingle = true end if ns ~= 0 and ns ~= 14 then args.trackSingle = false end -- Canonicalize general parameters for _,k in pairs do args[k] = canonicalize(args[k])[2] end -- Initialize tracking categories if main namespace local tracking = (args.tracking or ns

0) and local sisterList = local prefix -- Loop through all sister projects, generate possible links for _, prefix in ipairs(prefixList) do local link = sisterLink(prefix, args, tracking) if link then table.insert(sisterList, link) end end local box = mw.html.create if args.bar and #sisterList > 0 then box:wikitext(mw.getCurrentFrame:extensionTag) box:node(createSisterBar(sisterList,args)) elseif #sisterList

1 then -- Use single sister box instead of multi-sister box local sister = sisterList[1] local link = ""..(args.display or args[1]).."" if sister.name

'Commons' then sister.name = 'Wikimedia Commons' -- make single sister commons box look like end local text = sister.name.." has "..mw.ustring.lower(sister.information).." related to "..link.."." if sister.name

'Wikipedia' then -- make single sister interwiki box look like text = ""..sister.information.." "..sister.prep.." Wikipedia, the free encyclopedia" end box:wikitext(sideBox) elseif #sisterList > 0 then -- else use sister box if non-empty box:wikitext(createSisterBox(sisterList,args)) end if #sisterList

0 and args.auto then local generateWarning = require('Module:If preview')._warning box:wikitext(generateWarning) end -- Append tracking categories to container div -- Alpha ordering is by sister prefix if tracking then for k, v in pairs(tracking) do box:wikitext("") end if #sisterList

0 then box:wikitext("") end end return tostring(box)end

-- Main entry point for generating sister project links boxfunction p.main(frame) local args = getArgs(frame,) return p._main(args)end

-- Lua entry point for generate one sister linkfunction p._sisterlink(args) local prefix = args.prefix -- Canonicalize all sister links (handle yes/no/empty) for _, k in ipairs(prefixList) do args[k] = canonicalize(args[k]) end -- Canonicalize cookbook args.cookbook = canonicalize(args.cookbook) args.b = mergeArgs args.cookbook = args.cookbook[2] -- Canonicalize general parameters for _,k in pairs do args[k] = canonicalize(args[k])[2] end args[1] = args[1] or mw.title.getCurrentTitle.text args.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage args.qid = args.qid and args.qid:upper local link = sisterLink(prefix, args,nil) if not link then return "" end return ""..link.information .." "..link.prep.." "..link.nameend

-- Template entry point for generating one sister linkfunction p.link(frame) local args = getArgs(frame) return p._sisterlink(args)end

return p