Module:Plain sister

require('strict')

local p = {}

local getArgs = require('Module:Arguments').getArgs local yesno = require('Module:Yesno')

-- table of site data local sites = { -- interwiki prefix:	parameter,		label and				site id (for Wikidata) ['w'] =				{'wikipedia',	'Wikipedia article',	'enwiki'}, ['c'] =				{'commons',		'Commons gallery',		'commonswiki'}, ['c:Category'] =	{'commonscat',	'Commons category',		'commonswiki'}, ['q'] =				{'wikiquote',	'quotes',				'enwikiquote'}, ['n'] =				{'wikinews',	'news',					'enwikinews'}, ['wikt'] =			{'wiktionary',	'definition',			'enwiktionary'}, ['b'] =				{'wikibooks',	'textbook',				'enwikibooks'}, ['v'] =				{'wikiversity',	'course',				'enwikiversity'}, ['wikispecies'] =	{'wikispecies',	'taxonomy',				'specieswiki'}, ['voy'] =			{'wikivoyage',	'travel guide',			'enwikivoyage'}, ['d'] =				{'wikidata',	'Wikidata item',		'wikidatawiki'}, ['m'] =				{'meta',		'Meta',					'metawiki'} }

-- sites is display order (keyed as above) local sites_in_order = {'w', 'c', 'c:Category', 'q', 'n', 'wikt', 'b', 'v', 'wikispecies', 'voy', 'd', 'm'}

-- some properties are not wanted from certain transitive links -- for example, the P921 (main topic) should not add the Commons category -- this is a map of WD property -> WD site ID keys local transitiveLinkBlacklist = { P921 = {'commonswiki', 'wikiquote', 'wikinews', 'wiktionary', 'wikiversity', 'wikivoyage', 'meta'}, }

-- Get the item associated with the current page, or specified by the 'wikidata' -- parameter (of either the module invocation, or the parent template). -- @return mw.wikibase.entity local function getItem(args) local item = nil -- Firstly, see if the calling tempate or module has a "wikidata" argument. if args.wikidata then item = mw.wikibase.getEntity(args.wikidata) end -- Failing that just use the current page's item. if item == nil then item = mw.wikibase.getEntity end return item end

-- Get the page title of the first sitelink found on the target item for the -- given property. -- @return string|nil local function getFirstSitelink(item, property, sitename) local statements = item:getBestStatements(property) if #statements > 0 then -- Go through each 'edition of' statement. for _, statement in pairs(statements) do			-- datavalue is missing if set to "unknown value" if statement['mainsnak']['datatype'] == 'wikibase-item' and statement['mainsnak']['datavalue'] then local otherItemId = statement['mainsnak']['datavalue']['value']['id'] local sitelink = mw.wikibase.getSitelink(otherItemId, sitename) -- If the parent has the required sitelink, return it. if sitelink ~= '' and sitelink ~= nil then -- mw.log(sitename, property, sitelink) return sitelink end end -- if		end end return nil end

local function listContains(list, item) for _, v in pairs(list) do		if v == item then return true end end return false end

local function transitivePropertyBlacklisted(prop, wdSitelinkKey) -- reject prop/key pairs that we don't want local blacklisted = transitiveLinkBlacklist[prop] and listContains(transitiveLinkBlacklist[prop], wdSitelinkKey) return blacklisted end

function p.getLinks(args) local item = getItem(args) local links = {} -- Build all the wikitext links. for prefix, site in pairs(sites) do		local val = nil local wd_sitelink_key = site[3] local arg_name = site[1] -- Allow overriding of individual sitelinks. if args[arg_name] then val = args[arg_name] end if not val and wd_sitelink_key ~= '' and item then -- fetch it from wikidata val = item:getSitelink(wd_sitelink_key) if wd_sitelink_key == 'wikidatawiki' and item.id then val = item.id 			elseif wd_sitelink_key == 'commonswiki' and val then -- we have link to commons local catFlag = (#val>9 and string.sub(val, 1, 9) == 'Category:') if (arg_name == 'commonscat' and catFlag==false) or (arg_name=='commons' and catFlag==true) then val = nil -- link is to a wrong namespace so let's nuke it				elseif (arg_name =='commonscat' and catFlag==true) then val = string.sub(val,10) -- trim 'Category:' from the front end end end -- Commons gallery. if not val and arg_name == 'commons' and item then local statements = item:getBestStatements('P935') -- get commons gallery page from P935 property if statements[1] and statements[1].mainsnak.datavalue then val = statements[1].mainsnak.datavalue.value end end -- Commons category. if not val and arg_name == 'commonscat' and item then local statements = item:getBestStatements('P373') -- get commons category page from P373 property if statements[1] and statements[1].mainsnak.datavalue then val = statements[1].mainsnak.datavalue.value end end

-- edition or translation of (P629) -- category's main topic (P301) -- Wikimedia portal's main topic (P1204) -- main subject (P921) if item then for _,prop in pairs({ 'P629', 'P301', 'P1204', 'P921' }) do				if not val and not transitivePropertyBlacklisted(prop, wd_sitelink_key) then local workSitelink = getFirstSitelink(item, prop, wd_sitelink_key) if workSitelink ~= nil then val = workSitelink break end end end end

if val then links[prefix] = val end end -- tidy up redundancies in the WD data -- strip redundant commons category prefix if links['c:Category'] then links['c:Category'] = links['c:Category']:gsub('^Category:', '') end -- the gallery is exactly the same as the category, so just keep the category if links['c'] and links['c:Category'] and ('Category:' .. links['c:Category']) == links['c'] then links['c'] = nil end return links end

local function construct_sisicon_span(args) return mw.html.create('span') :addClass('sisicon') :wikitext('') end

-- Get an HTML list of all links to all sister projects. function p._interprojectPart(args) local item = getItem(args) local link_data = p.getLinks(args) local links = {} -- iterate the links in the desired order and construct Wikitext links for k, v in pairs(sites_in_order) do		if link_data[v] then local display = sites[v][2] local target = v .. ':' .. link_data[v] table.insert(links,  .. display .. ) end end if #links == 0 then -- links table length is zero return nil end return mw.html.create('li') :addClass('sisitem') :node(construct_sisicon_span({ image = 'Wikimedia-logo.svg', link = 'Special:sitematrix', alt = 'Sister Projects.' }))		:wikitext('sister projects: ' .. table.concat(links, ', ') .. '.') end

function p.interprojectPart(frame) return p._interprojectPart(getArgs(frame)) end

local function slashed_list_to_links(itemString, prefix) local items = mw.text.split(itemString, '%s*/%s*', false) local itemLinks = {} for _, item in pairs(items) do		if item ~= '' then table.insert(itemLinks,  .. item .. ) end end return table.concat(itemLinks, ', ') end

function p._plain_sister(args) local current_frame = mw.getCurrentFrame local current_title = mw.title.getCurrentTitle local pagename = current_title.text local item = getItem(args) -- construct list local ul_list = mw.html.create('ul'):addClass('plainSister') if yesno(args.disambiguation) then local dabItem = ' ' .. 'Search for titles containing or ' if current_title:inNamespaces(14) then dabItem = dabItem .. 'beginning' elseif current_title:inNamespaces(0) then dabItem = dabItem .. 'beginning' else dabItem = dabItem .. 'beginning (in ' .. current_title.nsText .. 's)' end dabItem = dabItem .. ' with: "' .. pagename .. '."' ul_list:tag('li') :addClass('dabitem') :wikitext(dabItem) end local show_textinfo = args.textinfotitle or yesno(args.textinfo or args.edition) if show_textinfo then local edition_title if args.textinfotitle then edition_title = mw.title.new(args.textinfotitle) else edition_title = current_title end ul_list:tag('li') :addClass('sisitem') :node(construct_sisicon_span({ image = 'Information_icon.svg', link = 'Template:Textinfo', alt = 'Documentation for the TextInfo template.', }))			:wikitext('information about this edition.') end local portal = args.portal or args.portals if portal then ul_list:tag('li') :addClass('sisitem') :node(construct_sisicon_span({ image = 'Wikisource-logo.svg', link = 'Portal:Portals', alt = 'Related Portals.' }))			:wikitext('related portals: ' .. slashed_list_to_links(portal, 'Portal:') .. '.') end local related_author = args.related_author or args.related_authors or args['related-author'] or args['related-authors'] if related_author then ul_list:tag('li') :addClass('sisitem') :node(construct_sisicon_span({ image = 'System-users.svg', link = 'Wikisource:Authors', alt = 'Related Authors.' }))			:wikitext('related authors: ' .. slashed_list_to_links(related_author, 'Author:') .. '.') end local related_work = args.related_work or args.related_works or args['related-work'] or args['related-works'] if related_work then ul_list:tag('li') :addClass('sisitem') :node(construct_sisicon_span({ image = 'Nuvola apps bookcase.svg', link = 'Wikisource:Works', alt = 'Related Works.' }))			:wikitext('related works: ' .. slashed_list_to_links(related_work) .. '.') end local sisters = p._interprojectPart(args) if sisters then ul_list:node(sisters) end if yesno(args.wikidataswitch) and not item then local wdSearch = ' ' .. 'Search Wikidata.' ul_list:tag('li') :addClass('sisitem') :wikitext(wdSearch) end if not yesno(args.disambiguation) and not show_textinfo and not portal and not related_author and not related_work and not sisters and not yesno(args.wikidataswitch) then return nil end return current_frame:extensionTag('templatestyles', '', {src = 'Plain sister/styles.css'}) .. tostring(ul_list) end

function p.plain_sister(frame) return p._plain_sister(getArgs(frame)) end

return p

-- Debug console testing: -- =p.interprojectPart(mw.getCurrentFrame:newChild{title='nop',args={wikidata='Q23308118'}})