Zum Inhalt springen

Modul:Sitelinks

Aus Wikivoyage
Dokumentation für das Modul Sitelinks[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Anwendung

Das Modul wird direkt von den Vorlagen {{Wikipedia}} und {{Sitelinks}} aufgerufen. Parameterbeschreibung siehe dort. Im Projektnamensraum befindet sich die technische Dokumentation Wikivoyage:Sitelinks.

Versionsbezeichnung auf Wikidata: 2024-09-05 Ok!

Benötigte weitere Module

Dieses Modul benötigt folgende weitere Module: Sitelinks/i18n • UrlCheck • Wikidata utilities
Hinweise
-- module variable and administration
local sl = {
	moduleInterface = {
		suite  = 'Sitelinks',
		serial = '2024-09-05',
		item   = 111999211
	}
}

-- module import
-- require( 'strict' )
local si = require( 'Module:Sitelinks/i18n' )
local uc -- URL check module
local wu = require( 'Module:Wikidata utilities' )

-- maintenance tools
local maintenance      = {} -- table of error strings
local invalidParams    = {} -- table of unknown parameters
local duplicateAliases = {} -- table of duplicate parameter aliases

local function contains( new )
	for i = 1, #maintenance do
		if maintenance[ i ] == new then
			return true
		end
	end
	return false
end

local function addMaintenance( key, value )
	local s = key -- fallback
	local tab = si.maintenance[ key ]
	if tab then
		s = si.formats.category:format( tab.category ) ..
			( tab.err and si.formats.error:format( tab.err ) or '' ) ..
			( tab.hint and si.formats.hint:format( tab.hint ) or '' )
	end
	s = value and mw.ustring.format( s, value ) or s

	if not contains( s ) then
		table.insert( maintenance, s )
	end
end

local function getMaintenanceMsgs()
	if #invalidParams > 0 then
		if #invalidParams == 1 then
			addMaintenance( 'unknownParam', invalidParams[ 1 ] )
		else
			addMaintenance( 'unknownParams', table.concat( invalidParams, ', ' ) )
		end
	end
	if #duplicateAliases > 0 then
		addMaintenance( 'duplicateAliases',	table.concat( duplicateAliases, ', ' ) )
	end
	return table.concat( maintenance, '' )
end
	
local function isSet( arg )
	return arg and arg ~= ''
end

local function yesno( val )
	return si.yesno[ mw.ustring.lower( val ) ]
end

local function getPageData()
	local page = mw.title.getCurrentTitle()
	page.lang = mw.getContentLanguage():getCode()
	page.globalProject = mw.site.siteName:lower():gsub( 'wikipedia', 'wiki' )

	return page
end

-- args: template arguments consisting of argument name as key and a value
-- validKeys: table with argument name as key used by the script and
--    a string or a table of strings for argument names used by the local wiki
local function checkArguments( templateArgs, validKeys )
	local args = {}
	if not templateArgs or not validKeys or not next( validKeys ) then
		return args
	end

	local keys = {} -- list of wiki-dependent parameter names
	for key, params in pairs( validKeys ) do
		if type( params ) == 'string' then
			keys[ params ] = key
		else
			for i = 1, #params do
				keys[ params[ i ] ] = key
			end
		end
	end
	
	local targetKey
	for key, arg in pairs( templateArgs ) do
		targetKey = keys[ key ]
		if targetKey then
			if args[ targetKey ] then -- prevents duplicates
				table.insert( duplicateAliases, "''" .. key .. "''" )
			else
				args[ targetKey ] = arg
			end
		else
			table.insert( invalidParams, "''" .. key .. "''" )
		end
	end
	return args
end

local colorAdjust = { ['-webkit-print-color-adjust'] = 'exact', ['color-adjust'] = 'exact',
	['print-color-adjust'] = 'exact' }

local function makeSpan( s, class, isBdi, attr, css )
	return tostring( mw.html.create( isBdi and 'bdi' or 'span' )
		:addClass( class )
		:attr( attr or {} )
		:css( css or {} )
		:wikitext( s )
	)
end

local function addLinkIcon( classes, link, title, text, addSpace )
	local span = makeSpan( ' ', nil, false, { title = title, ['data-icon'] = text },
		colorAdjust ) -- space to keep the span tag
	local lFormat = ( link:find( '^https?://' ) or link:find( '^//' ) )
		and '[%s %s]' or '[[%s|%s]]'
	local iconLink = mw.ustring.format( lFormat, link, span )
	if addSpace then
		iconLink = makeSpan( ' ', 'voy-sitelinks-with-space', true ) .. iconLink
	end
	return makeSpan( iconLink, 'voy-sitelinks-icon ' .. classes )
end

-- getting sister project links
local function getWikiLink( langArray, wiki, entity, wikilang )
	local prefix = wiki == 'wiki' and 'w:' or 'voy:'
	local lang, link
	for i = 1, #langArray do
		lang = langArray[ i ]
		if lang ~= '' then
			link = wu.getFilteredSitelink( entity, lang .. wiki )
			if link then
				prefix = prefix .. ( lang ~= wikilang and ( lang .. ':' ) or '' )
				return prefix .. link
			end
		end
	end
	return ''
end

local function getSisterLinks( args, page, entity )
	local sisters = {
		commons    = '', -- link to Commons category
		wikidata   = '', -- link to Wikidata
		wikipedia  = '', -- link to Wikipedia
		wikivoyage = ''  -- link to another branch, usually en, as a sister link
	}

	if isSet( args.wikidata ) then
		sisters.wikidata = 'd:' .. args.wikidata
		sisters.wikipedia = getWikiLink( si.langs.wikipedia, 'wiki', entity, page.lang )
		if args.template == 'wikipedia' and sisters.wikipedia ~= '' then
			return sisters
		end
		if args.wikiPage == '' and args.isArticle == 'false' then
			sisters.wikivoyage = getWikiLink(
				si.langs.wikivoyage, page.globalProject, entity, page.lang
			)
			if sisters.wikivoyage ~= '' then
				addMaintenance( 'linkToOtherWV' )
			end
		end
	end
	
	return sisters
end

-- adding linked sister icons
local function makeSisterIcons( sisters, name )
	if not sisters then
		return ''
	end

	local icons = {}
	local id = sisters.wikidata:gsub( '^d:', '' )
	local span
	for _, key in ipairs( { 'wikivoyage', 'wikipedia', 'commons', 'wikidata' } ) do
		if isSet( sisters[ key ] ) then
			span = addLinkIcon( 'voy-sitelinks-sister-icon voy-sitelinks-sister-' .. key, sisters[ key ],
				mw.ustring.format( si.iconTitles[ key ], name, id ), key,
				key == 'wikidata' ) -- add leading space
			table.insert( icons, span )
		end
	end
	return ( #icons == 1 and '' or ' ' ) .. table.concat( icons, '' )
end

local function isUrl( url )
	if not uc then
		uc = require( 'Module:UrlCheck' )
	end
	return uc.isUrl( url, si.options.skipPathCheck )
end

-- url check
local function checkUrl( url )
	if isSet( url ) then
		local c = isUrl( url ) -- getting result code
		if c > 2 then
			addMaintenance( 'wrongUrl' )
			url = ''
		elseif c == 2 then -- URL contains IP address
			addMaintenance( 'urlWithIP' )
		end
	end
	return url
end

local function initialParameterCheck( frame, paramTable )
	local entity = nil
	local wrongQualifier = false

	-- checking keys and copying values to args
	local args = checkArguments( frame:getParent().args, paramTable )
	args.name = mw.text.trim( args.name or '' )
	args.wikidata = mw.text.trim( args.wikidata or '' )
	
	-- remove control characters
	for key, _ in pairs(paramTable ) do
		if args[ key ] then
			args[ key ] = args[ key ]:gsub( '[%z\1-\31]', ' ' )
		else
			args[ key ] = ''
		end
	end

	-- checking Wikidata entitity
--	args.wikidata, entity, wrongQualifier = wu.getEntityId( args.wikidata or '' )
	args.wikidata, entity, wrongQualifier = wu.getEntity( args.wikidata or '' )
	if wrongQualifier then
		addMaintenance( 'wrongQualifier' )
	end
	if not isSet( args.wikidata ) then
		addMaintenance( 'missingWikidata' )
	end

	if isSet( args.name ) then
		if args.name:find( '<', 1, true ) or args.name:find( '{{', 1, true ) or
			args.name:find( '[', 1, true ) then
			addMaintenance( 'malformedName' )

			-- remove links, tags, and templates
			args.name = args.name:gsub( '%[%[[^|]*|', '' ):gsub( '%[%[', '' )
				:gsub( '%[[^%[%] ]+ ', '' ):gsub( '[%[%]|]', '' )
				:gsub( '<[^>]+>', '' ):gsub( '{{[^}]+}}', '' )
		end
	end

	if isSet( args.styles ) then
		args.styles = si.nameStyles[ args.styles:lower() ] or args.styles
	else
		args.styles = nil
	end

	if isSet( args.addLang ) then
		if mw.language.fetchLanguageName( args.addLang ) ~= '' then
			table.insert( si.langs.wikipedia, args.addLang )
			table.insert( si.langs.wikivoyage, args.addLang )
		else
			addMaintenance( 'unknownLang' )
		end
	end

	return args, entity
end

-- getting data from Wikidata
local function getNameFromWikidata( args, page, entity )
	if isSet( args.name ) then
		return
	end
	local officialNames =
		wu.getMonolingualValues( entity, si.properties.officialName )
	args.name = officialNames[ page.lang ]
		or si.langs.name ~= '' and officialNames[ si.langs.name ]
		or ''
	-- if failed then get labels
	if args.name == '' then
		if page.lang == si.langs.name then
			args.name = wu.getLabel( entity ) or ''
		else
			args.name = wu.getLabel( entity )
				or si.langs.name ~= '' and wu.getLabel( entity, si.langs.name )
				or ''
		end
	end
	if args.name ~= '' then
		addMaintenance( 'nameFromWD' )
	end
end

local function getCommonsCategory( args, entity )
	-- getting commonscat from commonswiki sitelink before P373
	-- because sitelink is checked by Wikidata
	local t = wu.getSitelink( entity, 'commonswiki' ) or ''
	if t:match( '^Category:.+$' ) then
		t = t:gsub( '^Category:', '' )
	else
		t = wu.getValue( entity, si.properties.commonsCategory )
		if t == '' then
			local id = wu.getId( entity, si.properties.mainCategory )
			if id ~= '' then
				t = wu.getSitelink( id, 'commonswiki' ) or ''
				t = t:gsub( '^Category:', '' )
			end
		end
	end
	args.commonscat = t
end

-- getting link to Wikivoyage
local function getArticleLink( args, page, entity )
	args.wikiPage = ''
	args.isArticle = 'false'
	local title = wu.getFilteredSitelink( entity, page.lang .. page.globalProject )
	if title and title ~= page.text then  -- no link to the article itself
		args.wikiPage = title
	elseif title and title == page.text then -- is article itself
		args.isArticle = 'true'
	end
end

local function getDataFromWikidata( args, page, entity )
	if args.wikidata == '' then
		return {} -- empty sisters table
	end

	getNameFromWikidata( args, page, entity )
	getArticleLink( args, page, entity )

	if yesno( args.commonscat ) == 'y' then
		getCommonsCategory( args, entity )
	end
	if yesno( args.url ) == 'y' then
		args.url = wu.getValue( entity, si.properties.url )
	end

	return getSisterLinks( args, page, entity )
end

local function finalParameterCheck( args )
	for k, v in pairs( args ) do
		if yesno( v ) then -- remove boolean values
			args[ k ] = ''
		end
	end

	if args.name == '' then
		args.name = si.maintenance.missingName
	end

	args.url = checkUrl( args.url )
end

local function makeName( args, link, sisters, aClass )
	-- bdi: supporting right-to-left wikis
	local result = {}
	local name = args.name
	if isSet( link ) then
		name = '[[:' .. link .. '|' .. name .. ']]'
	elseif isSet( args.url ) then
		name = '[' .. args.url .. ' ' .. name .. ']'
	end
	local tag = tostring( mw.html.create( 'bdi' )
		:attr( 'class', 'voy-sitelinks-name' )
		:cssText( args.styles )
		:wikitext( name )
	)
	if isSet( link ) and isSet( args.url ) then
		tag = tag .. ' ' .. addLinkIcon( 'voy-sitelinks-url', args.url,
			si.iconTitles.internet, 'internet' )
	end

	local icons = makeSisterIcons( sisters, args.name )

	return makeSpan( tag .. icons, aClass )
end

local function getMaintenance( page )
	if si.nsNoMaintenance[ page.namespace ] then
		return ''
	end

	local s = getMaintenanceMsgs()
	if si.options.usePropertyCateg then
		s = s .. wu.getCategories( si.maintenance.properties ) -- format string
	end
	return s
end

-- main sitelinks functions

function sl.wikipedia( frame )
	local page = getPageData()
	-- copying frame:getParent().args to template arguments, args, parameter check
	local args, slEntity = initialParameterCheck( frame, si.wikipedia )

	result = ''
	if isSet( args.wikidata ) then
		args.url = 'n'
		args.commonscat = 'n'
		args.template = 'wikipedia'
		local sisters = getDataFromWikidata( args, page, slEntity )
		finalParameterCheck( args )

		local link = ''
		if isSet( sisters.wikipedia ) then
			link = sisters.wikipedia
		elseif isSet( args.wikiPage ) then
			link = args.wikiPage
		else
			link = sisters.wikivoyage or ''
		end
		result = makeName( args, link, nil, 'voy-sitelinks-wikipedia' )
	end

	return result .. getMaintenance( page )
end

function sl.sitelinks( frame )
	local page = getPageData()
	-- copying frame:getParent().args to template arguments, args, parameter check
	local args, slEntity = initialParameterCheck( frame, si.sitelinks )

	local result = ''
	if isSet( args.wikidata ) then
		if not isSet( args.url ) then
			args.url = 'y'
		end
		args.commonscat = 'y'
		args.template = 'sitelinks'
		local sisters = getDataFromWikidata( args, page, slEntity )
		if isSet( args.commonscat ) then
			sisters.commons = 'c:Category:' .. args.commonscat
		end
		finalParameterCheck( args )

		local link = ''
		if isSet( args.wikiPage ) then
			link = args.wikiPage
		end
		if link == '' and not isSet( args.url ) and args.isArticle == 'false' then
			if isSet( sisters.wikipedia ) then
				link = sisters.wikipedia
				sisters.wikipedia = nil
			elseif isSet( sisters.wikivoyage ) then
				link = sisters.wikivoyage
				sisters.wikivoyage = nil
			end
		end
		result = makeName( args, link, sisters, 'voy-sitelinks-projects' )
	end
	return result .. getMaintenance( page )
end

return sl