![](http://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Test_Template_Info-Icon_-_Version_%282%29.svg/50px-Test_Template_Info-Icon_-_Version_%282%29.svg.png)
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.
require('strict')-- Module to create sister project link boxlocal getArgs = require('Module:Arguments').getArgslocal sideBox = require('Module:Side box')._mainlocal 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",species="Wikispecies-logo.svg",iw="Wikipedia-logo-v2.svg",iw1="Wikipedia-logo-v2.svg",iw2="Wikipedia-logo-v2.svg",d="Wikidata-logo.svg",m="Wikimedia Community Logo.svg",mw="MediaWiki-2020-icon.svg",f="Wikifunctions-logo-en.svg"}local prefixList = {'wikt', 'c', 'n', 'q', 's', 'b', 'v', 'voy','species', 'species_author', 'iw', 'iw1', 'iw2', 'd', 'm', 'mw', 'f'}local sisterName = {wikt="Wiktionary",c="Commons",n="Wikinews",q="Wikiquote",s="Wikisource",b="Wikibooks",voy="Wikivoyage",v="Wikiversity",species="Wikispecies",iw="Wikipedia",iw1="Wikipedia",iw2="Wikipedia",d="Wikidata",m="Meta-Wiki",mw="MediaWiki", f="Wikifunctions"}local sisterInfo = {wikt="Definitions",c="Media",n="News",q="Quotations",s="Texts",b="Textbooks",voy="Travel guides",v="Resources",species="Taxa",species_author="Authorship",iw="edition",iw1="edition",iw2="edition",d="Data",m="Discussions",mw="Documentation", f="Functions"}local defaultSisters = {wikt=true,c=true,n=true,q=true,s=true,b=true,voy='auto',v=true,species='auto',species_author=false,iw=false,iw1=false,iw2=false,d=false,m=false,mw=false, f=false}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"}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-- 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 thenreturn {nil, nil}end-- if s is table/list, then assume already canonicalized and return unchangedif tostring(type(s)) == "table" thenreturn sends = mw.text.trim(tostring(s))if s == "" thenreturn {nil, nil}endlocal 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' thenreturn {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'thenreturn {nil, false}end -- Neither yes nor no recognized, leave string trimmedreturn {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 blanklocal function mergeArgs(argList,noAll)local test = nil -- default, return blank if all blankif noAll thentest = false -- return no if all noendlocal allSame = true-- Search through string for first non-no or non-blankfor _, arg in ipairs(argList) doif arg[2] thenreturn arg -- found non-no and non-blank, return itend-- test to see if argList is all blank / noallSame = allSame and (arg[2] == test)end-- if all blank / no, return blank / noif allSame thenreturn {nil, test} -- all match no/blank, return itend-- otherwise, return no / blankif noAll thenreturn {nil, nil}endreturn {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 defaultlocal function getSitelink(wiki,qid)-- return nil if some sort of lookup failurereturn 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) thenreturn nil --- either editor specified "no", or "blank" (and default=no), then skip this sisterendlocal sitelink = args.sitelink or fetchWikidata(args.sisterPrefix,args.qid)if args.auto and not sitelink and args[1][2] == nil thenreturn nil --- in auto mode, if link is blank and no sitelink, then skipend-- fallback order of sister link: first specified page, then wikidata, then searchlocal link = args[1][1] or sitelink or (args.search and "Special:"..args.search)if not link thenreturn nil --- no link found, just skipendif tracking then-- update state for tracking categoriesif args[1][1] and sitelink then-- transform supplied page name to be in wiki-formatlocal 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 thentracking.wdHidden = args.sisterPrefixelseif pageNS ~= sitelinkNS thentracking.wdNamespace = args.sisterPrefixelsetracking.wdMismatch = args.sisterPrefixend-- if no page link, nor a wikidata entry, and search is on, then warnelseif not (args[1][2] or sitelink) and args.search thentracking.defaultSearch = args.sisterPrefixendendreturn {prefix=args.sisterPrefix, link=link, logo=args.logo, name=args.name, information=args.information, prep=args.prep}end-- Function to handle special case of commons linklocal function commonsLinks(args, commonsPage)-- use [[Module:Commons link]] to determine best commons linklocal 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:") thencommonsPage[1] = (args.commonscat and "Category:" or "")..commonsPage[1]endlocal commonsSearch = "Search/"..(args.commonscat and "Category:" or "")..args[1]return {link=cLink, search=commonsSearch}end-- Function to handle special case for "author" and "cookbook"local function handleSubtype(args)local ns = args.nslocal ns_len = mw.ustring.len(ns)local result = {}result.sitelink = fetchWikidata(args.prefix, args.qid)local subtype = falseif args.page thenif mw.ustring.sub(args.page,1,ns_len) == ns then subtype = true elseif args.subtype then result.page = ns..args.page subtype = true endelseif result.sitelink thensubtype = mw.ustring.sub(result.sitelink,1,ns_len) == nselseif args.subtype thenresult.search = "Search/"..ns..args.defaultsubtype = trueendif subtype thenresult.info = args.infoendreturn 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 prefixif prefix == 'species_author' and not args.species[1] and args.species[2] and not args.species_author[1] and args.species_author[2] thenreturn nilendlocal default = defaultSisters[prefix]if default == 'auto' thendefault = args.autoend-- Handle exceptions by prefixlocal 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({prefix='s',qid=args.qid,subtype=args.author,page=page[1], ns='Author:',info=nil,default=args[1]}) elseif prefix == 'b' then subtype = handleSubtype({prefix='b',qid=args.qid,subtype=args.cookbook,page=page[1], ns='Cookbook:',info='Recipes',default=args[1]}) end if subtype then page[1] = subtype.page or page[1]search = subtype.search or searchsitelink = subtype.sitelink or sitelinkinfo = subtype.info or infoend 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" endend 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 endif lang == nil thenreturn nilend 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({ page, auto=args.auto, qid=args.qid, logo=logo, name=name, prep=prep, sitelink=sitelink, default=default, sisterPrefix = prefix, search=search, information=info}, tracking)endlocal function templatestyles_page(is_bar)local sandbox = inSandbox and 'sandbox/' or ''if is_bar thenreturn mw.ustring.format('Module:Sister project links/bar/%sstyles.css',sandbox)endreturn 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("[[File:"..link.logo.."|27x27px|middle|link=|alt=]]") -- html element for link local linkspan = li:tag('span') linkspan:addClass(sandbox("sister-link")) local linkText = "[["..link.prefix..":"..link.link.."|"..link.information .."]] "..link.prep.." "..link.name linkspan:wikitext(linkText) end list:allDone() return sideBox({role = 'navigation',labelledby = 'sister-projects',class = sandbox("sister-box") .. ' sistersitebox plainlinks',position = args.position,style = args.style,abovestyle = args.collapsible and 'clear: both' or nil,above = mw.ustring.format("<b>%s</b> at Wikipedia's [[Wikipedia:Wikimedia sister projects|<span id=\"sister-projects\">sister projects</span>]]",args.display or args[1]),text = tostring(list),collapsible = args.collapsible,templatestyles = templatestyles_page()})endlocal 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 [[Wikipedia:Wikimedia sister projects|"headerText = headerText..'<span id="sister-projects" style="white-space:nowrap;">sister projects</span>]]:'header:wikitext(headerText)if #sisterList == 1 and args.trackSingle thenheader:wikitext("[[Category:Pages with single-entry sister bar]]")endlocal container = nav:tag('ul')container:addClass(sandbox('sister-bar-content'))for _, link in ipairs(sisterList) dolocal item = container:tag('li')item:addClass(sandbox('sister-bar-item'))local logoSpan = item:tag('span')logoSpan:addClass(sandbox('sister-bar-logo'))logoSpan:wikitext("[[File:"..link.logo.."|21x19px|link=|alt=]]")local linkSpan = item:tag('span')linkSpan:addClass(sandbox('sister-bar-link'))linkSpan:wikitext("<b>[["..link.prefix..":"..link.link.."|"..link.information .."]]</b> "..link.prep.." "..link.name)endreturn navendfunction p._main(args)local titleObject = mw.title.getCurrentTitle()local ns = titleObject.namespace-- find qid, either supplied with args, from search string, or from current pageargs.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) doargs[k] = canonicalize(args[k])end-- Canonicalize cookbookargs.cookbook = canonicalize(args.cookbook)args.b = mergeArgs({args.b,args.cookbook})args.cookbook = args.cookbook[2]-- handle trackSingle parameterif args.trackSingle == nil then args.trackSingle = trueend if ns ~= 0 and ns ~= 14 then args.trackSingle = false end -- Canonicalize general parameters for _,k in pairs({"auto","commonscat","author","bar","tracking","sandbox","trackSingle"}) do args[k] = canonicalize(args[k])[2] end-- Initialize tracking categories if main namespacelocal 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 thentable.insert(sisterList, link)endend local box = mw.html.create() if args.bar and #sisterList > 0 then box:wikitext(mw.getCurrentFrame():extensionTag{name = 'templatestyles', args = { src = templatestyles_page(true) } }) box:node(createSisterBar(sisterList,args)) elseif #sisterList == 1 then -- Use single sister box instead of multi-sister box local sister = sisterList[1] local link = "[["..sister.prefix..":"..sister.link.."|<b><i>"..(args.display or args[1]).."</i></b>]]" if sister.name == 'Commons' then sister.name = 'Wikimedia Commons' -- make single sister commons box look like {{Commons}} 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 {{InterWiki}} text = "[["..sister.prefix..":"..sister.link.."|<b><i>"..sister.information.."</i></b>]] "..sister.prep.." [[Wikipedia]], the free encyclopedia" end box:wikitext(sideBox({ role = 'navigation', position=args.position, image="[[File:"..sister.logo.."|40x40px|class=noviewer|alt=|link=]]", metadata='no', class='plainlinks sistersitebox', text=text,templatestyles = templatestyles_page() })) 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({"No sister project links found in Wikidata. Try auto=0"})) end-- Append tracking categories to container div-- Alpha ordering is by sister prefixif tracking thenfor k, v in pairs(tracking) dobox:wikitext("[[Category:"..trackingType[k].."|"..v.."]]")end if #sisterList == 0 then box:wikitext("[[Category:Pages with empty sister project links]]") endendreturn tostring(box)end-- Main entry point for generating sister project links boxfunction p.main(frame)local args = getArgs(frame,{frameOnly=false,parentOnly=false,parentFirst=false})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) doargs[k] = canonicalize(args[k])end-- Canonicalize cookbookargs.cookbook = canonicalize(args.cookbook)args.b = mergeArgs({args.b,args.cookbook})args.cookbook = args.cookbook[2] -- Canonicalize general parameters for _,k in pairs({"auto","commonscat","author"}) do args[k] = canonicalize(args[k])[2] end args[1] = args[1] or mw.title.getCurrentTitle().textargs.qid = args.qid or mw.wikibase.getEntityIdForCurrentPage()args.qid = args.qid and args.qid:upper()local link = sisterLink(prefix, args,nil)if not link thenreturn ""endreturn "[["..link.prefix..":"..link.link.."|"..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)endreturn p
🔥 Top keywords: Main PageSpecial:SearchWikipedia:Featured picturesYasukeHarrison ButkerRobert FicoBridgertonCleopatraDeaths in 2024Joyce VincentXXXTentacionHank AdamsIt Ends with UsYouTubeNew Caledonia2024 Indian general electionHeeramandiDarren DutchyshenSlovakiaKingdom of the Planet of the ApesAttempted assassination of Robert FicoLawrence WongBaby ReindeerXXX: Return of Xander CageThelma HoustonFuriosa: A Mad Max SagaMegalopolis (film)Richard GaddKepler's SupernovaWicked (musical)Sunil ChhetriXXX (2002 film)Ashley MadisonAnya Taylor-JoyPlanet of the ApesNava MauYoung SheldonPortal:Current eventsX-Men '97