Module:Sister project links
Documentation for this module may be created at Module:Sister project links/doc
-- Module to create sister project link box local getArgs = require('Module:Arguments').getArgs local commonsLink = require('Module:Commons link') local sideBox = require('Module:Side box')._main local p = {} local logo = { wikt="Wiktionary-logo-v2.svg", c="Commons-logo.svg", n="Wikinews-logo.svg", q="Wikiquote-logo.svg", s="Wikisource-logo.svg", b="Wikibooks-logo.svg", voy="Wikivoyage-Logo-v3-icon.svg", v="Wikiversity logo 2017.svg", d="Wikidata-logo.svg", species="Wikispecies-logo.svg", m="Wikimedia Community Logo.svg", mw="MediaWiki-2020-icon.svg"} local sisterName = { wikt="Wiktionary", c="Wikimedia Commons", n="Wikinews", q="Wikiquote", s="Wikisource", b="Wikibooks", voy="Wikivoyage", v="Wikiversity", d="Wikidata", species="Wikispecies", m="Meta-Wiki", mw="MediaWiki"} local sisterDb = { wikt="enwiktionary", n="enwikinews", q="enwikiquote", s="enwikisource", b="enwikibooks", voy="enwikivoyage", v="enwikiversity", species="specieswiki"} local trackingType = { wdMismatch="Pages using Sister project links with wikidata mismatch", wdNamespace="Pages using Sister project links with wikidata namespace mismatch", wdHidden="Pages using Sister project links with hidden wikidata", defaultSearch="Pages using Sister project links with default search"} -- Function to canonicalize string -- search for variants of "yes", and "no", and transform -- them into a standard form (like [[Template:YesNo]]) -- Argument: -- s --- input string -- Result: -- {x,y} 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 {nil, nil} 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 {nil, nil} end 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 {nil, true} 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 {nil, false} end -- Neither yes nor no recognized, leave string trimmed return {s, true} 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 blank local 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 {nil, test} -- all match no/blank, return it end -- otherwise, return no / blank if noAll then return {nil, nil} end return {nil, false} 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 default local function getSitelink(wiki,qid) -- return nil if some sort of lookup failure return qid and mw.wikibase.getSitelink(qid,wiki) 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 table local function sisterLink(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 sisterDbName = sisterDb[args.sisterPrefix] local sitelink = args.sitelink or (sisterDbName and getSitelink(sisterDbName,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 ~= nil 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 {prefix=args.sisterPrefix, link=link, information=args.information} end -- Function to add one sister to sister list -- Arguments: -- sisterList = accumulated list of sisters -- args = arguments for this sister (see p._sisterLink above) -- tracking = tracking table local function oneSister(sisterList,args,tracking) local link = sisterLink(args,tracking) if link then table.insert(sisterList, link) end 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 display local function createContainer(args) -- Outermost div (css from previous version of [[Template:Project sister links]]) local container = mw.html.create('div') container:attr("role","navigation") container:attr("aria-labelledby","sister-projects") container:addClass("metadata") container:addClass("plainlinks") container:addClass("sistersitebox") container:addClass("plainlist") if args.position and args.position:lower() == "left" then container:addClass("mbox-small-left") else container:addClass("mbox-small") end if args.collapsible then container:addClass("mw-collapsible") if args.collapsible == "collapsed" then container:addClass("mw-collapsed") end end container:css("border","1px solid #aaa") container:css("padding",0) container:css("background","#f9f9f9") container:cssText(args.style) -- Div for text header local header = container:tag('div') header:css("clear",args.collapsible and "both") header:css("padding","0.75em 0") header:css("text-align","center") -- pagename in bold as part of header local pagename = header:tag('b') pagename:css("display","block") pagename:wikitext(args.display or args[1]) local headerText = "at Wikipedia's [[Wikipedia:Wikimedia sister projects|" headerText = headerText..'<span id="sister-projects">sister projects</span>]]' header:wikitext(headerText) -- start the unordered list element here local ul = container:tag('ul') ul:addClass(args.collapsible and "mw-collapsible-content") ul:css("border-top","1px solid #aaa") ul:css("padding","0.75em 0") ul:css("width","217px") ul:css("margin","0 auto") -- pass ul element back to main, so sister links can be added return ul end local function createSisterBox(sisterList,args) container = createContainer(args) for i, link in ipairs(sisterList) do local li = container:tag('li') li:css("min-height","31px") -- html element for 27px-high logo local logoSpan = li:tag('span') logoSpan:css("display","inline-block") logoSpan:css("width","31px") logoSpan:css("line-height","31px") logoSpan:css("vertical-align","middle") logoSpan:css("text-align","center") logoSpan:wikitext("[[File:"..logo[link.prefix].."|27x27px|middle|link=|alt=]]") -- html element for link local linkspan = li:tag('span') linkspan:css("display","inline-block") linkspan:css("margin-left","4px") linkspan:css("width","182px") linkspan:css("vertical-align","middle") local linkText = "[["..link.prefix..":"..link.link.."|"..link.information .."]] from "..sisterName[link.prefix] linkspan:wikitext(linkText) end return container:allDone() end function p._main(args) local titleObject = mw.title.getCurrentTitle() -- Default title/search string is PAGENAME args[1] = args[1] or titleObject.text -- Canonicalize all sister links (handle yes/no/empty) for _, k in pairs({"wikt","c","commons","n","q","s","b","voy", "v","d","species","species_author","m","mw"}) do args[k] = canonicalize(args[k]) end -- Canonicalize general parameters for _,k in pairs({"auto","commonscat","author"}) do args[k] = canonicalize(args[k])[2] end -- Fill in args.qid with current qid (if available) args.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage() args.qid = args.qid and args.qid:upper() -- Initialize tracking categories if main namespace local tracking = nil if titleObject.namespace == 0 then tracking = {} end local sisterList = {} -- WIKTIONARY oneSister(sisterList,{args.wikt,auto=args.auto,qid=args.qid,default=true, sisterPrefix="wikt",search="Search/"..args[1], information="Definitions"},tracking) -- COMMONS -- use [[Module:Commons link]] to determine best commons link local cLink = (not args.commonscat) and commonsLink._hasGallery(args.qid) or commonsLink._hasCategory(args.qid) local commonsPage = mergeArgs({args.c,args.commons}) 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] oneSister(sisterList,{commonsPage,auto=args.auto,qid=args.qid, default=true,sitelink=cLink, sisterPrefix="c",search=commonsSearch,information="Media"}, tracking) -- WIKINEWS oneSister(sisterList,{args.n,auto=args.auto,qid=args.qid,default=true, sisterPrefix="n",search="Search/"..args[1], information="News"},tracking) -- WIKIQUOTE oneSister(sisterList,{args.q,auto=args.auto,qid=args.qid,default=true, sisterPrefix="q",search="Search/"..args[1], information="Quotations"},tracking) -- WIKISOURCE if args.author and args.s[1] then args.s[1] = "Author:"..args.s[1] end oneSister(sisterList,{args.s,auto=args.auto,qid=args.qid,default=true, sisterPrefix="s",search="Search/"..args[1], information="Texts"},tracking) -- WIKIBOOKS oneSister(sisterList,{args.b,auto=args.auto,qid=args.qid,default=true, sisterPrefix="b",search="Search/"..args[1], information="Textbooks"},tracking) -- WIKIVOYAGE -- Information="guide" when we can be assured that search cannot be called local voyInfo = (args.voy[1] or args.voy[2] == nil) and "guides" or "information" oneSister(sisterList,{args.voy,auto=args.auto,qid=args.qid,default=args.auto, sisterPrefix="voy",search="Search/"..args[1], information="Travel "..voyInfo},tracking) -- WIKIVERSITY oneSister(sisterList,{args.v,auto=args.auto,qid=args.qid,default=true, sisterPrefix="v",search="Search/"..args[1], information="Resources"},tracking) -- WIKIDATA oneSister(sisterList,{args.d,qid=args.qid,default=false,sisterPrefix="d", sitelink=args.qid,search="ItemByTitle/enwiki/"..args[1], information="Data"},tracking) -- WIKISPECIES oneSister(sisterList,{mergeArgs({args.species,args.species_author},1),auto=args.auto, qid=args.qid,default=args.auto, sisterPrefix="species",search="Search/"..args[1], information=(args.species[2] and "Taxons") or (args.species_author[2] and "Authorship") or "Taxons",},tracking) if args.species[2] and args.species_author[1] then -- If species_author is explicitly defined, and species is used, then make second row oneSister(sisterList,{args.species_author,auto=args.auto,qid=args.qid, default=args.auto, sisterPrefix="species",search="Search/"..args[1], information="Authorship"},tracking) end -- META oneSister(sisterList,{args.m,qid=args.qid,default=false, sisterPrefix="m",search="Search/"..args[1], information="Discussions",},tracking) -- MEDIAWIKI oneSister(sisterList,{args.mw,qid=args.qid,default=false, sisterPrefix="mw",search="Search/"..args[1], information="Documentation",},tracking) local box = mw.html.create('div') if #sisterList == 1 then -- Use single sister box instead of multi-sister box local sister = sisterList[1] local frame = mw.getCurrentFrame() local link = "[["..sister.prefix..":"..sister.link.."|<b><i>"..(args.display or args[1]).."</i></b>]]" box:wikitext(sideBox({position=args.position, image="[[File:"..logo[sister.prefix].."|40x40px|class=noviewer|alt=|link=]]", metadata='no', textclass='plainlist', class='plainlinks sistersitebox', text=sisterName[sister.prefix].." has "..mw.ustring.lower(sister.information).." related to "..link})) elseif #sisterList > 0 then -- else use sister box if non-empty box = createSisterBox(sisterList,args) end -- Append tracking categories to container div -- Alpha ordering is by sister prefix if tracking ~= nil then for k, v in pairs(tracking) do box:wikitext("[[Category:"..trackingType[k].."|"..v.."]]") end end return box end -- Main entry point for generating sister project links box function p.main(frame) local args = getArgs(frame,{frameOnly=false,parentOnly=false,parentFirst=false}) return tostring(p._main(args)) end -- Lua entry point for generate one sister link function p._sisterlink(args) args[1] = canonicalize(args[1]) args.auto = canonicalize(args.auto)[2] args.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage() args.qid = args.qid and args.qid:upper() local link = sisterLink(args,nil) return "[["..link.prefix..":"..link.link.."|"..link.information .."]] from "..sisterName[link.prefix] end -- Template entry point for generating one sister link function p.link(frame) local args = getArgs(frame,{frameOnly=false,parentOnly=false,parentFirst=false}) return p._sisterlink(args) end return p