Moduuli:Sisarprojektit

Wikipediasta
Siirry navigaatioon Siirry hakuun

require('strict')

-- Module to create sister project link box
local getArgs = require('Module:Arguments').getArgs
local sideBox = require('Module:Side box')._main
local p = {}

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

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

-- Information about how to handle each sister lives in separate data file
local cfg = mw.loadData(sandbox('Moduuli:Sisarprojektit/config'))
local logo = cfg.logo
local prefixList = cfg.prefixList
local sisterName = cfg.sisterName
local sisterInfo = cfg.sisterInfo
local defaultSisters = cfg.defaultSisters
local sisterDb = cfg.sisterDb
local trackingType = cfg.trackingType 

-- Example usage:
-- {{#invoke:Sisarprojektit|main|wikidata=Q33|wikinews=Suomi|Wikivoyage=en:Finland}}

-- generate link with image and text
local function getSisterlinkFi(project, page)
	local projectnames={
		commons = 'Commonsissa',
		wikinews = 'Wikiuutisissa',
		wikiquote = 'Wikisitaateissa',
		wikispecies = 'Wikilajeissa',
		meta = 'Metassa',
		mediawiki = 'Mediawiki.org:ssa',
		wikiversity = 'Wikiopistossa',
		wikisource = 'Wikiaineistossa',
		wikibooks = 'Wikikirjastossa',
		wikivoyage = 'Wikimatkoissa',
		wiktionary = 'Wikisanakirjassa',
		wikidata = 'Wikidatassa',
		wiki = 'Wikipediassa'
	}
	local pagenames={
		commons = 'Kuvia',
		wikinews = 'Uutisia',
		wikiquote = 'Sitaattikokoelmia',
		wikispecies = 'Laji',
		meta = 'Keskustelu',
		mediawiki = 'Ohjeita',
		wikiversity = 'Oppimisympäristö',
		wikisource = 'Lähdetekstejä',
		wikibooks = 'Oppikirjoja',
		wikivoyage = 'Paikka',
		wiktionary = 'Määritelmiä',
		wikidata = 'Kohde',
		wiki = 'Wikipediassa'
	}

	local imagenames={
		commons = 'Commons-logo.svg',
		wikinews = 'Wikinews-logo.svg',
		wikiquote = 'Wikiquote-logo.svg',
		wikispecies = 'Wikispecies-logo.svg',
		meta = 'Wikimedia Community Logo.svg',
		mediawiki = 'MediaWiki-logo.svg',
		wikiversity = 'Wikiversity-logo-Snorky.svg',
		wikisource = 'Wikisource-logo.svg',
		wikibooks = 'Wikibooks-logo.svg',
		wikivoyage = 'Wikivoyage-Logo-v3-icon.svg',
		wiktionary = 'Wiktionary-logo-v2.svg',
		wikidata = 'Wikidata-logo.svg',
		wiki = 'Wikipedia-logo-v2.svg' 
	}

	-- TODO: vaihda luokat sister-logo ja sister-link, lisää css-sivun käyttö ja testaa
    --local labelcss="display: inline-block; margin-left: 4px; width: 182px; vertical-align: middle;";
    --local imagecss="display: inline-block; width: 31px; line-height: 31px; vertical-align: middle; text-align: center;";

	if projectnames[project] and pagenames[project] and imagenames[project] then
		local imagelink = "[[File:".. imagenames[project] .."|27x27px|middle|link=|alt=]]"
		local image="<span class='sister-logo'>".. imagelink .."</span>";
		local label="<span class='sister-link'>[[" .. page .."|" .. pagenames[project] .."]] "  .. projectnames[project] .."</span>";
		return image .. label
	end
	
	-- something went wrong
	return "[[Luokka:Viallinen sisarprojektimalline]]"
end

local function getHeader(parent, args)
	local titleText = args["display"] or mw.title.getCurrentTitle()
	--local title = tostring(mw.title.new(mw.text.trim(titleText), 'Malline'));
	
	local clearing = ""
	if args.collapsible then
		clearing = "clear: both;"
	end
	
	local text = 'Lisätietoja aiheesta\n<b style="display:block;">' .. tostring(titleText) .. '</b>'
	text = text .. 'Wikipedian [[Wikimedia Foundation|<span id="sister-projects">sisarhankkeissa</span>]]'
	
	-- <span id="sister-projects" style="white-space:nowrap;">sisarprojekteissa</span>]]
	local heading = parent:tag('div')
	heading 
		:cssText(clearing ..' padding-bottom: 0.75em; text-align: center;')
		:wikitext(text)
end

-- Function tries to get items P373 value from wikidata
-- If P373 doesn't exist then it loops through properties
-- and tries to get their P373 values.

local function getLinks(parent, args)
	local id= args['wikidata']
    local targetlangs={"fi", "en", "sv"}
	local targetprojects={   
		commons=false,
		wikinews=true,
		wikiquote=true,
		wikispecies=false,
		meta=false,
--		mediawiki=false,
		wikiversity=true,
		wikisource=true,
		wikibooks=true,
		wikivoyage=true,
		wiktionary=true,
--		wikidata=false,
--		wiki=true
	}
	local shortnames={
		wikinews="n",
		wikiquote="q",
		wikiversity="v",
		wikisource="s",
		wikibooks="b",
		wikivoyage="voy",
		wiktionary="wikt",
		wikispecies="species",
		meta="m",
		mediawiki="mw",
	}

    -- Output
	local foundprojects={};
	local entity;
	local project;
	local multilang;

	-- Get wikidata item
	if id == nil or id == "" then
		entity = mw.wikibase.getEntityObject()
	else
		entity =  mw.wikibase.getEntityObject(id);
	end
	
	-- Check if there is any sitelinks
	if not entity or not entity.sitelinks then
		return nil
	end	
	
	-- Find suitable sitelinks
	for project,multilang in pairs(targetprojects) do
		if multilang then
			for m, lang in pairs(targetlangs) do
				local key=lang .. project;
				if entity.sitelinks[key] then
					foundprojects[project]=":" .. project ..":" .. lang ..":".. entity.sitelinks[key]["title"]
					break;
				end
			end
		else
			local key= project;
			if key=="commons" then
				key="commonswiki";
			end
			if entity.sitelinks[key] then
				foundprojects[project]=":" .. project ..":".. entity.sitelinks[key]["title"]
			end
		end
	end
	
	if foundprojects["commons"] == nil then
		-- Check commonscat
		if entity.claims then
			if entity.claims["P373"] then
				for i, j in pairs(entity.claims["P373"]) do
					foundprojects["commons"]=":commons:category:" .. j["mainsnak"]["datavalue"]["value"]
					break;
				end
			end
		end	
	end	
	
	-- can't use this before the lookup
	-- Commons cat template parameter handler 
	if args["commonscat"] and args["commonscat"] ~= "" and project and args[project] then
		foundprojects["commons"]=":commons:category:" .. args[project];
	end

    local sisterList = {}
	
	-- Render sitelinks
	for project,multilang in pairs(targetprojects) do
		-- Local overrides
		if args[project] then
			if args[project] ~= "" then
				if args[project] == "-" then
					foundprojects[project]="-"
				elseif multilang == true then
					foundprojects[project]=":fi:" ..shortnames[project] ..":" .. args[project];
				else
					foundprojects[project]=":" ..project ..":" .. args[project];
				end
			end
		end

		-- Render if not locally disabled
		if foundprojects[project] and foundprojects[project] ~="-" then
			local link = getSisterlinkFi(project, foundprojects[project])
	    	if link then
				table.insert(sisterList, link)
			end
		end
	end

	return sisterList
end 

-- vanhan menetelmän siirto, siivotaan mallineesta
local function makeLinkBox(args)
	local mbox_class = "mbox-small"
	local coll_class = ""
	local collapsible = args['collapsible'] or ''
	local position = args['position'] or ''
	local style = args['style'] or ''
	position = position:lower()
	if position == "left" then
		mbox_class = "mbox-small-left" 
	end
	if collapsible ~= "" then
		if collapsed == "collapsed" then
			coll_class = "mw-collapsible mw-collapsed"
		else
			coll_class = "mw-collapsible"
		end
	end
	
	-- TOOD: vaihda sideboxin käyttöön
	local rootnode = mw.html.create()
	rootnode:wikitext(mw.getCurrentFrame():extensionTag{
		name = 'templatestyles', args = { src = 'Moduuli:Sisarprojektit/styles.css' }
	})
	
	local rootdiv = rootnode:tag('div')
	rootdiv:attr( 'role', 'navigation' )
	rootdiv:attr( 'aria-labelledby' , 'sister-projects' )
	rootdiv:addClass( 'metadata')
	--rootdiv:addClass( 'sister-box')
	--rootdiv:addClass( 'sistersitebox')
	rootdiv:addClass( 'plainlinks')
	rootdiv:addClass( 'plainlist')
	rootdiv:addClass( mbox_class)
	--if coll_class ~= "" then
	--	rootdiv:addClass( coll_class)
	--end
	rootdiv:cssText("border:1px solid #aaa; padding:0.75em; background:#f9f9f9; " .. style)

	getHeader(rootdiv, args)

	-- CSS rules
    local listcss="border-top:1px solid #aaa; padding-top: 0.75em;";
    local sisterList = {}
	sisterList = getLinks(rootdiv, args)
	if sisterList then
	    -- Loop through all sister projects, generate possible links
		local outlist = rootdiv:tag('ul')
		outlist:cssText(listcss)
	    for _, link in ipairs(sisterList) do
	    	local linktag = outlist:tag('li')
			linktag
				:wikitext(link)
				:done()
	    end
	end

	return tostring(rootnode)
end

-- 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
	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 {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 get sitelink for a wiki
-- Arguments:
--   prefix = prefix string for wiki to lookup
--   qid = QID of entity to search for, current page entity by default
local 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 table
local 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 {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 link
local function commonsLinks(args, commonsPage)
	-- use [[Module:Commons link]] 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 {link=cLink, search=commonsSearch}
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 result
end

-- 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 table
local 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({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 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
	    -- vaihtoehtoiset kielet?
	    --local langname = mw.language.fetchLanguageName( lang, 'en')
	    local langname = mw.language.fetchLanguageName( lang, 'fi')
	    if not langname or #langname == 0 then
	    	return nil
	    end
	    info = langname..' '..info
	    prep = ""
    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)
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 display
local 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>  Wikipedian [[Wikipedia:Wikimedia sister projects|<span id=\"sister-projects\">sisarprojekteissa</span>]]",
			args.display or args[1]
		),
		text = tostring(list),
		collapsible = args.collapsible,
		templatestyles = templatestyles_page()
	})
end

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 = " Wikipedian [[Wikipedia:Wikimedia sister projects|"
	headerText = headerText..'<span id="sister-projects" style="white-space:nowrap;">sisarprojekteissa</span>]]:'
	header:wikitext(headerText)
	if #sisterList == 1 and args.trackSingle then
		header:wikitext("[[Category:Pages with single-entry sister bar]]")
	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("[[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)
	end
	return nav
end

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.b,args.cookbook})
	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({"auto","commonscat","author","bar","tracking","sandbox","trackSingle"}) 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{
			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 prefix
	if tracking then
		for k, v in pairs(tracking) do
			box:wikitext("[[Category:"..trackingType[k].."|"..v.."]]")
		end
    	if #sisterList == 0 then
    		box:wikitext("[[Category:Pages with empty sister project links]]")
    	end
	end
	return tostring(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 makeLinkBox(args)
	--return p._main(args)
end

-- Lua entry point for generate one sister link
function 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.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().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.prefix..":"..link.link.."|"..link.information .."]] "..link.prep.." "..link.name
end

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

return p