Module:Plain sister/sandbox
Appearance
This is the module sandbox page for Module:Plain sister (diff). See also the companion subpage for test cases (run). |
This Lua module is used on approximately 696,000 pages, or roughly 15% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
This module depends on the following other modules: |
This module provides one method, which lists sister links to the current page. It is used by {{plain sister}} and Module:Header structure.
require('strict')
local p = {}
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local TableTools = require('Module:TableTools')
-- 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(args.class or 'sisicon')
:wikitext('[[File:' .. args.image .. '|frameless|18px|link=' .. args.link .. '|alt=' .. args.alt .. ']]')
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, '[[' .. target .. '|' .. 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('[[Special:sitematrix|sister projects]]: ' .. table.concat(links, ', '))
end
function p.interprojectPart(frame)
return p._interprojectPart(getArgs(frame))
end
local function construct_related_links(sourceArgs, linkArgs, sisiconArgs)
local links = {}
for k, v in pairs(sourceArgs) do
local key = string.gsub(string.gsub(string.gsub(string.gsub(k, ' ', ''), '_', ''), '-', ''), 's(%d*)$', '%1')
if string.match(key, '^' .. linkArgs.param .. '%d*$') then
local n = string.gsub(key, '^' .. linkArgs.param .. '(%d*)$', '%1')
n = tonumber(n) or 1
if not links[n] then
local items = mw.text.split(v, '%s*/%s*', false)
local itemLinks = {}
for _, item in pairs(items) do
if item ~= '' then
table.insert(itemLinks, '[[' .. linkArgs.nsPrefix .. item .. '|' .. item .. ']]')
end
end
links[n] = table.concat(itemLinks, ', ')
end
end
end
links = TableTools.compressSparseArray(links)
if #links == 0 then
return nil
end
return mw.html.create('li')
:addClass('sisitem')
:node(construct_sisicon_span(sisiconArgs))
:wikitext(linkArgs.linkPrefix .. table.concat(links, ', '))
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 dabText = 'Search for titles ' .. tostring(mw.html.create('span'):addClass('selfreference'):wikitext('[[Special:Search/intitle:"' .. pagename .. '"|containing]]')) .. ' or '
if current_title:inNamespaces(14) then
dabText = dabText .. '[[Special:Categories/' .. pagename .. '|beginning]]'
elseif current_title:inNamespaces(0) then
dabText = dabText .. '[[Special:PrefixIndex/' .. current_title.fullText .. '|beginning]]'
else
dabText = dabText .. '[[Special:PrefixIndex/' .. current_title.fullText .. '|beginning (in ' .. current_title.nsText .. 's)]]'
end
dabText = dabText .. ' with: "' .. pagename .. '"'
ul_list:tag('li')
:addClass('dabitem')
:node(construct_sisicon_span({
image = 'Disambiguation.svg',
link = 'WS:STYLE#Disambiguation, versions and translations pages',
alt = 'Style Guide for disambiguation, version and translation pages',
class = 'dabicon'
}))
:wikitext(dabText)
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('[[' .. edition_title.talkNsText .. ':' .. edition_title.text .. '|information about this edition]]')
end
local portalLI = construct_related_links(
args,
{
param = 'portal',
nsPrefix = 'Portal:',
linkPrefix = '[[Portal:Portals|related portals]]: '
},
{
image = 'Wikisource-logo.svg',
link = 'Portal:Portals',
alt = 'Related Portals'
}
)
if portalLI then
ul_list:node(portalLI)
end
local authorLI = construct_related_links(
args,
{
param = 'relatedauthor',
nsPrefix = 'Author:',
linkPrefix = '[[Wikisource:Authors|related authors]]: '
},
{
image = 'System-users.svg',
link = 'Wikisource:Authors',
alt = 'Related Authors'
}
)
if authorLI then
ul_list:node(authorLI)
end
local workLI = construct_related_links(
args,
{
param = 'relatedwork',
nsPrefix = '',
linkPrefix = '[[Wikisource:Works|related works]]: '
},
{
image = 'Nuvola apps bookcase.svg',
link = 'Wikisource:Works',
alt = 'Related Works'
}
)
if workLI then
ul_list:node(workLI)
end
local sisters = p._interprojectPart(args)
if sisters then
ul_list:node(sisters)
end
if yesno(args.wikidataswitch) and not item then
ul_list:tag('li')
:addClass('sisitem')
:node(construct_sisicon_span({
image = 'Wikidata-logo.svg',
link = 'Wikisource:Wikidata',
alt = 'Wikidata',
}))
:wikitext('[[d:Special:Search/' .. pagename .. '|Search Wikidata]]')
end
if not yesno(args.disambiguation) and not show_textinfo and not portalLI and not authorLI and not workLI 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'}})