Modiwl:Plain sister

Documentation for this module may be created at Modiwl:Plain sister/doc

local p = {}

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

-- 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
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 thar 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
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 otherItem = mw.wikibase.getEntity( otherItemId )
				local sitelink = otherItem:getSitelink( sitename )
				-- If the parent has the required sitelink, return it.
				if sitelink ~= '' then
					-- mw.log(sitename, property, sitelink)
					return sitelink
				end
			end -- if
		end
	end
	return nil
end

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

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

--------------------------------------------------------------------------------
-- Get an HTML list of all links to all sister projects.
function p.interprojetPart( frame )
	local args = getArgs(frame)
	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 ''
	end

	return '<li class="sisitem">'
		.. '<span class="sisicon" style="padding-right:1ex;">[[Image:Wikimedia-logo.svg|frameless|18px|link=Special:sitematrix|alt=Sister Projects.]]</span>'
		.. '[[Special:sitematrix|sister projects]]:&#32;' .. table.concat( links, ',&#32;' )
		.. '.</li>'
end

return p

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