Module:Sister project links
Appearance
![]() | This Lua module is used on approximately 18,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
![]() | This module depends on the following other modules: |
![]() | This module uses TemplateStyles: |
Implements {{Sister project links}}
See {{Sister project links/testcases}} for test cases for box, {{Sister bar/testcases}} for bar.
Note: in order to make the test cases work, the Sandbox CSS classes have "-sand" appended to their names. If you wish to update the CSS, copy the contents of each class from Module:Sister project links/sandbox/styles.css to Module:Sister project links/styles.css, but do not alter the class names, nor just copy-paste the entire CSS file. For the current difference in CSS between Sandbox and Main, see here.
-- Module to create sister project link box
local getArgs = require('Module:Arguments').getArgs
local commonsLink = require('Module:Commons link')
local p = {}
-- 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
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)
qid = qid or mw.wikibase.getEntityIdForCurrentPage()
qid = qid and qid:upper()
-- return nil if some sort of lookup failure
return qid and mw.wikibase.getSitelink(qid,wiki)
end
-- Function to generate one sister link
-- Arguments:
-- container = parent HTML object
-- args = argument table for function
-- args[1] = canonicalized 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.sisterDbName = DB name of sister site (to get sitelink)
-- args.qid = QID of entity (for debugging)
-- args.search = fallback string to search for
-- args.logo = filename of sister logo (no namespace)
-- args.sisterPrefix = wikitext prefix for sister site
-- args.information = type of info sister site contains
-- args.sisterName = friendly human name of sister site
function p._oneSister(container,args)
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 (args.sisterDbName and getSitelink(args.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
local li = container and container:tag('li') or mw.html.create('li')
li:css("min-height","31px")
-- html element for 27px-high logo
local logo = li:tag('span')
logo:css("display","inline-block")
logo:css("width","31px")
logo:css("line-height","31px")
logo:css("vertical-align","middle")
logo:css("text-align","center")
logo:wikitext("[[File:"..args.logo.."|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")
link = "[["..args.sisterPrefix..":"..link.."|"..args.information .."]] from "..args.sisterName
linkspan:wikitext(link)
return li
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
function p._main(args)
-- Default title/search string is PAGENAME
args[1] = args[1] or mw.title.getCurrentTitle().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
local ul = createContainer(args)
-- WIKTIONARY
p._oneSister(ul,{args.wikt,auto=args.auto,qid=args.qid,default=true,
logo="Wiktionary-logo-v2.svg",sisterPrefix="wikt",
search="Search/"..args[1],information="Definitions",
sisterName="Wiktionary",sisterDbName="enwiktionary"})
-- 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)
p._oneSister(ul,{mergeArgs({args.c,args.commons}),auto=args.auto,qid=args.qid,
default=true,sitelink=cLink,logo="Commons-logo.svg",
sisterPrefix="c",search="Search/"..args[1],information="Media",
sisterName="Wikimedia Commons"})
-- WIKINEWS
p._oneSister(ul,{args.n,auto=args.auto,qid=args.qid,default=true,
logo="Wikinews-logo.svg",sisterPrefix="n",search="Search/"..args[1],
information="News",sisterName="Wikinews",sisterDbName="enwikinews"})
-- WIKIQUOTE
p._oneSister(ul,{args.q,auto=args.auto,qid=args.qid,default=true,
logo="Wikiquote-logo.svg",sisterPrefix="q",search="Search/"..args[1],
information="Quotations",sisterName="Wikiquote",sisterDbName="enwikiquote"})
-- WIKISOURCE
if args.author and args.s[1] then
args.s[1] = "Author:"..args.s[1]
end
p._oneSister(ul,{args.s,auto=args.auto,qid=args.qid,default=true,
logo="Wikisource-logo.svg",sisterPrefix="s",search="Search/"..args[1],
information="Texts",sisterName="Wikisource",sisterDbName="enwikisource"})
-- WIKIBOOKS
p._oneSister(ul,{args.b,auto=args.auto,qid=args.qid,default=true,
logo="Wikibooks-logo.svg",sisterPrefix="b",search="Search/"..args[1],
information="Textbooks",sisterName="Wikibooks",sisterDbName="enwikibooks"})
-- WIKIVOYAGE
p._oneSister(ul,{args.voy,auto=args.auto,qid=args.qid,default=args.auto,
logo="Wikivoyage-Logo-v3-icon.svg",sisterPrefix="voy",search="Search/"..args[1],
information="Travel"..(args.voy[2] and "guide" or "information"),
sisterName="Wikivoyage",sisterDbName="enwikivoyage"})
-- WIKIVERSITY
p._oneSister(ul,{args.v,auto=args.auto,qid=args.qid,default=true,
logo="Wikiversity logo 2017.svg",sisterPrefix="v",search="Search/"..args[1],
information="Resources",sisterName="Wikiversity",sisterDbName="enwikiversity"})
-- WIKIDATA
p._oneSister(ul,{args.d,qid=args.qid,default=false,logo="Wikidata-logo.svg",sisterPrefix="d",
search="ItemByTitle/enwiki/"..args[1],information="Data",sisterName="Wikidata"})
-- WIKISPECIES
p._oneSister(ul,{mergeArgs({args.species,args.species_author},1),auto=args.auto,
qid=args.qid,default=args.auto,logo="Wikispecies-logo.svg",
sisterPrefix="species",search="Search/"..args[1],
information=(args.species[2] and "Taxonomy")
or (args.species_author[2] and "Species author")
or "Taxonomy",
sisterName="Wikispecies",sisterDbName="specieswiki"})
if args.species[2] and args.species_author[1] then
-- If species_author is explicitly defined, and species is used, then make second row
p._oneSister(ul,{args.species_author,auto=args.auto,qid=args.qid,
default=args.auto,logo="Wikispecies-logo.svg",
sisterPrefix="species",search="Search/"..args[1],
information="Species author",sisterName="Wikispecies"})
end
-- META
p._oneSister(ul,{args.m,qid=args.qid,default=false,logo="Wikimedia Community Logo.svg",
sisterPrefix="m",search="Search/"..args[1],information="Discussion",sisterName="Meta-Wiki"})
-- MEDIAWIKI
p._oneSister(ul,{args.mw,qid=args.qid,default=false,logo="MediaWiki-logo.svg",
sisterPrefix="mw",search="Search/"..args[1],information="Documentation",sisterName="MediaWiki"})
return ul:allDone()
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
-- Test entry point for generating one sister link
function p.oneSister(frame)
local args = getArgs(frame,{frameOnly=false,parentOnly=false,parentFirst=false})
args[1] = canonicalize(args[1])
args.auto = canonicalize(args.auto)[2]
return tostring(p._oneSister(nil,args))
end
return p