Questa pagina è protetta dallo spostamento
Questa pagina è protetta

Modulo:Collegamenti esterni

Da Wikipedia, l'enciclopedia libera.
Jump to navigation Jump to search

Modulo che implementa il template {{Collegamenti esterni}}.

Ha una sottopagina di configurazione per ciascun gruppo di collegamenti esterni:

Tipo di proprietà

Quasi tutti i collegamenti si basano su proprietà di tipo "identificativo esterno", quindi per ciascuno bisognerà inserire, nel relativo gruppo della sottopagina di configurazione, almeno l'ID della proprietà Wikidata ("pid") e l'URL di formattazione ("url"). Per i rari casi di utilizzo di proprietà di tipo "URL", al posto dell'URL di formattazione va specificato il titolo del collegamento ("titolo").

Ordine collegamenti

All'interno di ciascun gruppo i collegamenti sono visualizzati nell'ordine in cui sono stati inseriti nella pagina di configurazione (ovviamente se è presente la relativa proprietà Wikidata). Per cambiare l'ordine è quindi sufficiente cambiare l'ordine dei collegamenti nelle sottopagine di configurazione.

Disambiguare i collegamenti relativi allo stesso sito web

Quando più proprietà Wikidata si riferiscono allo stesso sito web (esempio per le diverse schede di un calciatore, allenatore, dirigente, ...) e possono essere usate contemporaneamente in uno stesso elemento Wikidata, ci sono tre modi per disambiguare i collegamenti generati:

  1. differenziarli con "tipo", esempio nel primo tipo = 'calciatore' e nel secondo tipo = 'allenatore'. La disambiguazione apparirà però sempre, anche quando presente una sola proprietà nell'elemento.
  2. differenziarli con "titolo", esempio nel primo titolo = 'Scheda giocatore di $1' e nel secondo titolo = 'Scheda allenatore di $1'. La disambiguazione apparirà però sempre, anche quando presente una sola proprietà nell'elemento, inoltre la frase apparirà in corsivo anche se non fosse effettivamente il titolo della pagina.
  3. differenziarli con "sitodis", esempio nel primo sitodis = 'giocatore' e nel secondo sitodis = 'allenatore'. In questo modo, rispetto ai due metodi precedenti: (1) la disambiguazione apparirà automaticamente solo quando è presente più di una proprietà che utilizza lo stesso sito web e (2) si potranno omettere titolo e sito, lasciandoli al loro valore predefinito, ossia il titolo della pagina e il dominio dell'URL.
Controllo duplicati

Verifica automatica di proprietà duplicate: nessun duplicato.


--[[
* Modulo che implementa il template Collegamenti esterni.
]]--

require('Modulo:No globals')

local getArgs = require('Modulo:Arguments').getArgs
local mWikidata = require('Modulo:Wikidata')
local mCitazione = require('Modulo:Citazione')
local mEditAtWikidata = require('Modulo:Modifica su Wikidata')
-- Permette di definire l'ordine di visualizzazione dei vari gruppi
local orderedGroupNames = {
	'Ufficiali', 'Enciclopedie', 'Politica', 'Calcio', 'Sport', 'Software', 'Videogiochi',
	'Fumetti', 'Cinema', 'Musica', 'MAB', 'Architettura', 'Geografia',
	'Biografie', 'Letteratura', 'Biologia'
}
-- Soglie di attenzione sul numero elevato di collegamenti, per categorie di controllo
local MOLTI_LINK = 12
local MOLTI_LINK_2 = 15
-- Categorie di servizio
local catLetta = ' letta da Wikidata'
local catMultiSenzaQual = ' multipla letta da Wikidata senza qualificatore'
local catMoltiLink = 'Voci con template Collegamenti esterni e molti collegamenti'
local catMoltiLink2 = 'Voci con template Collegamenti esterni e molti collegamenti (soglia maggiore)'
local catEmpty = 'Voci con template Collegamenti esterni senza dati da Wikidata'
local catUnknownQual = 'Voci con template Collegamenti esterni e qualificatori sconosciuti'

-- =============================================================================
--                            Funzioni di utilità
-- =============================================================================

-- Restituisce la configurazione delle sottopagine.
--
-- @return {table}
local function readConfig()
	local ret = {}
	for _, groupName in ipairs(orderedGroupNames) do
		ret[groupName] = mw.loadData('Modulo:Collegamenti esterni/' .. groupName)
	end
	return ret
end

-- Restituisce il titolo della pagina corrente rimuovendo eventuale testo tra parentesi.
-- Se l'etichetta dell'elemento Wikidata contiene delle parentesi,
-- non le rimuove perché significa che fanno parte del titolo.
-- Con il parametro "from" (elemento Wikidata arbitrario) usa sempre l'etichetta.
--
-- @param {string} from
-- @return {string}
local function getCurrentTitle(from)
	local ret
	local label = mWikidata._getLabel({ from })
	if from then
		ret = label
	else
		ret = mw.title.getCurrentTitle().text
		if not (label and string.find(label, ' %(')) then
			ret = mw.text.split(ret, ' %(')[1]
		end
	end
	return ret
end

-- Restituisce il dominio dell'URL specificato.
--
-- @param {string} url
-- @return {string}
local function getDomain(url)
	return mw.uri.new(url).host:gsub('^www.', '')
end

-- Restituisce true se l'elemento collegato alla pagina o quello specificato in from
-- ha tra i valori della proprietà indicata uno degli elementi specificati.
-- @param {string} prop - codice della proprietà 'Pxxx'
-- @param {table} [entityIds] - array dei valori (strighe 'Qxxx') che può avere
-- @param {string} from
-- @return {boolean}
local function checkEntity(prop, entityIds, from)
	local args = { from = from }
	for _, entityId in ipairs(entityIds) do
		table.insert(args, entityId)
	end
	return mWikidata._propertyHasEntity(prop, args)
end

-- Converte un parametro ricevuto come 'Xxx, yyy' in { 'Xxx'=true, 'Yyy'=true }
local function ParametroElenco(param)
	local chiavi = {}
	local valori = param and mw.text.split(param, ',')
	if valori then
		for _, str in ipairs(valori) do
			chiavi[mw.getContentLanguage():ucfirst(mw.text.trim(str))] = true
		end
	end
	return chiavi
end

-- =============================================================================
--                            Classe ExtLink
-- =============================================================================

-- La classe ExtLink rappresenta un singolo collegamento esterno.
-- Al suo interno ha un riferimento alla propria configurazione (linkConf)
-- e nel caso la proprietà Wikidata abbia più valori li raccoglie tutti.

local ExtLink = {}

-- Costruttore della classe ExtLink.
--
-- @param {table} [url] - uno o più URL, quanti sono i valori della proprietà Wikidata
-- @param {table} [qualifier] - eventuali qualificatori da annettere al titolo per ciascun URL
-- @param {table} linkConf - la configurazione fissa per questo collegamento esterno
-- @param {table} extraConf - altri elementi di configurazione ricavati dall'item
-- @param {string} from - entityId se diverso da quello collegato alla pagina corrente
-- @return {table} un nuovo oggetto ExtLink
function ExtLink:new(url, qualifier, linkConf, extraConf, from)
	local self = {}
	setmetatable(self, { __index = ExtLink })

	-- sostituisce eventuali spazi con %20
	for i = 1, #url do
		url[i] = url[i]:gsub(' ', '%%20')
	end
	self.url = url
	self.qualifier = #url > 1 and qualifier
	self.linkConf = linkConf
	self.extraConf = extraConf
	self.from = from
	self.title = getCurrentTitle()
	self.title = self.from and mWikidata._getLabel({ self.from }) or
			(self.linkConf.titolo and
			self.linkConf.titolo:gsub('$1', self.title) or
			self.title)
	self.extraConf.medium = self.linkConf.medium or 'web'
	
	return self
end

-- Restituisce il parametro titolo per il modulo Citazione.
--
-- @return {string}
function ExtLink:_getTitolo()
	local titolo = self.extraConf.titolo[1] or self.title
	if self.qualifier and self.qualifier[1] ~= nil then
		return string.format('%s (%s)', titolo, self.qualifier[1])
	else
		return titolo
	end
end

-- Restituisce il parametro altrilink per il modulo Citazione.
-- Nel caso di valori multipli, genera quelli successivi al primo.
--
-- @return {table}
function ExtLink:_getAltriLink()
	local tbl = {}
	local titolo, specifica
	for i = 2, #self.url do
		titolo = self.extraConf.titolo[i] or self.title
		if self.qualifier and self.qualifier[i] ~= nil then
			specifica = self.qualifier[i]
		else
			local ripetuto = false -- controllo se stesso titolo già usato
			for j = 1, i - 1 do
				if titolo == (self.extraConf.titolo[j] or self.title) then 
					ripetuto = true
					break
				end
			end
			if ripetuto then
				specifica = 'altra versione'
			else
				specifica = nil -- se titoli diversi, va bene anche senza specificazione
			end
		end
		if specifica then
			titolo = string.format('%s (%s)', titolo, specifica)
		end
		table.insert(tbl, { self.url[i], titolo })
	end
	return tbl
end

-- Formatta il collegamento esterno quando la proprietà è di tipo URL.
--
-- @return {string}
function ExtLink:_formatPropertyURL()
	local formattedLinks = {}
	local currTitle = getCurrentTitle(self.from)
	local claims = mWikidata._getClaims(self.linkConf.pid, { from = self.from }) or {}
	for idx, claim in ipairs(claims) do
		local langs = mWikidata._formatQualifiers(claim, 'P407', { formatting = 'raw' }, true)
		langs = (#langs == 1 and langs[1] == 'Q652') and {} or langs
		for i, lang in ipairs(langs) do
			langs[i] = mWikidata._getLabel({ lang })
		end
		local formattedLink = mCitazione.cita_da_modulo(
			self.extraConf.medium,
			{
				url = mWikidata._formatStatement(claim),
				titolo = self.linkConf.titolo:gsub('$1', currTitle),
				lingua = table.concat(langs, ','),
				cid = self.linkConf.cid
			})
		table.insert(formattedLinks, '* ' .. formattedLink ..
					 mEditAtWikidata._showMessage({ pid = self.linkConf.pid, qid = self.from }))
	end
	return table.concat(formattedLinks, '\n')
end

-- Formatta il collegamento esterno come elemento di un elenco puntato.
--
-- @return {string}
function ExtLink:getListItem()
	-- se è specificato l'URL di formattazione è una
	-- proprietà di tipo "identificativo esterno" altrimenti di tipo URL
	if self.linkConf.url then
		return '* ' .. mCitazione.cita_da_modulo(
			self.extraConf.medium,
			{
				url = self.url[1],
				titolo = self:_getTitolo(),
				altrilink = self:_getAltriLink(),
				sito = self.linkConf.opera and '' or self.extraConf.sito,
				opera = self.linkConf.opera,
				editore = self.linkConf.editore,
				lingua = self.linkConf.lingua,
				cid = self.linkConf.cid,
				autore = self.linkConf.autore or self.extraConf.autore,
				volume = self.extraConf.volume,
				p = self.extraConf.pagina,
				data = self.linkConf.data or self.extraConf.data,
				tipo = self.linkConf.tipo or self.sitodis
			}) .. mEditAtWikidata._showMessage({ pid = self.linkConf.pid, qid = self.from })
	else
		return self:_formatPropertyURL()
	end
end

-- =============================================================================
--                            Classe LinksManager
-- =============================================================================

-- La classe LinksManager è la classe principale del modulo.
-- Al suo interno ha un riferimento a tutti collegamenti esterni (extLinks)
-- presenti in un dato elemento Wikidata e li può restituire tutti formattati 
-- nel modo più appropriato.

local LinksManager = {}

-- Costruttore della classe LinksManager.
--
-- @param {table} args
-- @return {table} un nuovo oggetto LinksManager
function LinksManager:new(args)
	local self = {}
	setmetatable(self, { __index = LinksManager })

	self.numExtLinks = 0
	self.categories = {}
	self.catColon = ''
	self.from = args.from
	self.escludi = ParametroElenco(args.escludi)
	self.soloprop = ParametroElenco(args.prop)
	self.sologruppi = ParametroElenco(args.gruppo)
	self.solomedium = ParametroElenco(args.medium)
	-- la pagina dei test utilizza uno stub del modulo Wikidata
	if mw.title.getCurrentTitle().prefixedText ==
	   'Discussioni modulo:Collegamenti esterni/test' then
		self:_setStubWikidata(args.stubwd)
	end
	self.extLinks = self:_getExtLinks()

	return self
end

-- Permette di specificare uno stub del modulo Wikidata
-- in modo da ottenere i valori delle proprietà in modo deterministico,
-- non dipendenti da modifiche utente a un elemento Wikidata.
--
-- @param {table} stubwd
function LinksManager:_setStubWikidata(stubwd)
	mEditAtWikidata = { _showMessage = function(frame) return '' end }
	mWikidata = stubwd
	self.catColon = ':'
	self.debug = true
end

-- Controlla se un elemento fa parte delle esclusioni richieste dall'utente
function LinksManager:_Escluso(elemento, inclusi)
	return self.escludi[elemento] or (next(inclusi) and inclusi[elemento] ~= true)
end

-- Ottiene tutti i collegamenti esterni (configurati) presenti in un dato elemento Wikidata
-- suddivisi per gruppo.
--
-- @return {table}
function LinksManager:_getExtLinks()
	local ret, groupSites = {}, {}
	local cfg = readConfig()
	for _, groupName in ipairs(orderedGroupNames) do -- Per ogni gruppo tematico
		groupSites[groupName] = {}
		ret[groupName] = {}
		-- controlla se è un gruppo escluso manualmente dall'utente
		if self:_Escluso(groupName, self.sologruppi) then
			cfg[groupName] = {}
		end
		for _, linkConf in ipairs(cfg[groupName]) do -- Per ogni sito configurato
			local valido = true
			-- controlla se è un sito da escludere per soggetto non pertinente
			if type(linkConf.vincolo) == 'table' then
				valido = false
				local Nvincoli = 0
				for _, _ in ipairs(linkConf.vincolo) do Nvincoli = Nvincoli + 1 end
				for i = 2, Nvincoli, 2 do -- Per ogni coppia 'proprietà', {valori}
					valido = valido or checkEntity(linkConf.vincolo[i - 1], linkConf.vincolo[i], self.from)
				end
			end
			-- controlla se è un sito escluso manualmente dall'utente
			if self:_Escluso(linkConf.pid, self.soloprop) or self:_Escluso(mw.getContentLanguage():ucfirst(linkConf.medium or 'web'), self.solomedium) then
				valido = false
			end
			-- Se il sito è abilitato, avviene la lettura da Wikidata
			if valido then
				local url, qualifier, extraConf
				local Nqualtitoli = 0
				local claims = mWikidata._getClaims(linkConf.pid, { from = self.from, snaktype = 'value' })
				-- Qui, se ci sono valori, il sito viene aggiunto a quelli da mostrare
				if claims and #claims > 0 then
					-- Qualificatori generali
					-- In caso di valori multipli, il titolo viene letto per tutti; gli altri qualificatori solo per il primo (altrimenti bisogna rivedere la formattazione dei valori multipli)
					-- Ricordarsi di elencare i qual. generali anche in altriQualNoti
					extraConf = {}
					extraConf.autore = mWikidata._formatQualifiers(claims[1], 'P50') or mWikidata._formatQualifiers(claims[1], 'P2093') -- come item o stringa
					extraConf.volume = mWikidata._formatQualifiers(claims[1], 'P478')
					extraConf.pagina = mWikidata._formatQualifiers(claims[1], 'P304')
					extraConf.data = mWikidata._formatQualifiers(claims[1], 'P577')
					extraConf.titolo = {}
					for i, claim in ipairs(claims) do
						extraConf.titolo[i] = mWikidata._formatQualifiers(claim, 'P1476') or mWikidata._formatQualifiers(claim, 'P1810') or mWikidata._formatQualifiers(claim, 'P742') -- titolo o "indicato come" o pseudonimo
						if extraConf.titolo[i] then Nqualtitoli = Nqualtitoli + 1 end
					end
					-- Uno o più url ed eventuali qualificatori per distinguerli
					url = {}
					for i, claim in ipairs(claims) do
						if claim.qualifiers then
							local qualifierIds = ParametroElenco(linkConf.multi)
							for qualifierId, _ in pairs(qualifierIds) do
								if claim.qualifiers[qualifierId] then
									local formattedQualifier = mWikidata._formatQualifiers(claim, qualifierId, { nq = '1', formatting = 'raw' })
									if formattedQualifier then
										if not qualifier then qualifier = {} end
										qualifier[i] = mw.wikibase.getLabel(formattedQualifier)
										break
									end
								end
							end
							local altriQualNoti = {P407 = true, P50 = true, P2093 = true, P1476 = true, P1810 = true, P742 = true, P577 = true, P478 = true, P304 = true, P813 = true}
							for qualifierId in pairs(claim.qualifiers) do
								if qualifierIds[qualifierId] ~= true and altriQualNoti[qualifierId] ~= true then
									table.insert(self.categories, string.format('[[%sCategoria:%s]]', self.catColon, catUnknownQual))
									break
								end
							end
						end
						-- crea l'url
						claim = mWikidata._formatStatement(claim)
						if linkConf.url then
							-- se proprietà di tipo id, il valore viene sotituito a "$1"
							claim = mw.message.newRawMessage(linkConf.url, claim):plain()
						end
						table.insert(url, claim)
					end
					-- nome sito, di default il dominio estratto dall'url
					extraConf.sito = linkConf.sito or getDomain(linkConf.url)
					-- Creazione dell'oggetto collegamento esterno, con l'url (o gli url) e gli altri dati raccolti
					table.insert(ret[groupName], ExtLink:new(url, qualifier, linkConf, extraConf, self.from))
					-- categoria per proprietà letta; se multipla e indistinguibile, usa categoria di avviso
					local tail = #url > 1 and linkConf.url and (qualifier == nil or #qualifier ~= #url) and (Nqualtitoli < #url - 1) and
							catMultiSenzaQual or catLetta
					table.insert(self.categories, string.format('[[%sCategoria:%s%s]]', self.catColon, linkConf.pid, tail))
					-- per verificare se un sito è ripetuto nel gruppo
					groupSites[groupName][extraConf.sito] = (groupSites[groupName][extraConf.sito] or 0) + 1
					-- conteggio complessivo dei collegamenti
					self.numExtLinks = self.numExtLinks + 1
				end
			end
		end
	end
	-- verifica se un sito è ripetuto nel gruppo
	for _, groupName in ipairs(orderedGroupNames) do
		for _, extLink in ipairs(ret[groupName]) do
			-- necessaria la disambiguazione (più URL con lo stesso dominio nel gruppo),
			-- se configurata tramite "sitodis" nella configurazione.
			if groupSites[groupName][extLink.extraConf.sito] > 1 then
				extLink.sitodis = extLink.linkConf.sitodis
			end
		end
	end
	-- categorie di servizio sul numero di link
	local catnumero
	if self.numExtLinks == 0 then
		catnumero = catEmpty
	elseif self.numExtLinks > MOLTI_LINK then
		if self.numExtLinks > MOLTI_LINK_2 then catnumero = catMoltiLink2
		else catnumero = catMoltiLink end
	end
	if catnumero then
		table.insert(self.categories, string.format('[[%sCategoria:%s]]', self.catColon, catnumero))
	end
	return ret
end

-- Formatta i collegamenti esterni come elenco puntato.
--
-- @param {table} [groupNames]
-- @return {string}
function LinksManager:_formatList(groupNames)
	local formattedLinks = {}
	for _, groupName in ipairs(groupNames) do
		for _, extLink in ipairs(self.extLinks[groupName]) do
			table.insert(formattedLinks, extLink:getListItem())
		end
	end
	return table.concat(formattedLinks, '\n')
end

-- Restituisce tutti i collegamenti esterni formattandoli come elenco puntato
--
-- @return {string}
function LinksManager:getList()
	local categories, links
	-- categorie di servizio
	categories = (mw.title.getCurrentTitle().namespace == 0 or self.debug) and
				 table.concat(self.categories) or ''
	-- collegamenti
	links = self:_formatList(orderedGroupNames)
	return links .. categories
end

-- =============================================================================
--                            Funzioni esportate
-- =============================================================================

local p = {}

-- Funzioni di utilità per il manuale, danno la soglia di attenzione sul n° di link.
function p.threshold(frame)
	return MOLTI_LINK
end
function p.threshold2(frame)
	return MOLTI_LINK_2
end

-- Funzione di utilità per il manuale, restituisce un elenco
-- delle proprietà supportate, divise per gruppo.
function p.properties(frame)
	local res = {}
	local cfg = readConfig()
	table.sort(orderedGroupNames)
	for _, groupName in ipairs(orderedGroupNames) do
		local wdLinks = {}
		for _, linkConf in ipairs(cfg[groupName]) do
			local wdLink = string.format('<tr><td>[[d:Property:%s|%s (%s)]]</td><td>%s</td><td>%s</td></tr>',
				linkConf.pid, mWikidata._getLabel({ linkConf.pid }), linkConf.pid, linkConf.url or '', linkConf.cid or '')
			table.insert(wdLinks, wdLink) 
		end
		local group = frame.args[1] == 'modulo' and
			string.format('* [[Modulo:Collegamenti esterni/%s]] (%s)', groupName, #wdLinks) or
			mw.getCurrentFrame():expandTemplate {
				title = 'Cassetto',
				args = {
					titolo = string.format('[[Modulo:Collegamenti esterni/%s|%s]] (%s)', groupName, groupName, #wdLinks),
					testo = '<table class="wikitable sortable plainlinks"><tr><th>Proprietà</th><th>Formato URL</th><th>cid</th></tr>' .. table.concat(wdLinks, '') .. '</table>'
				}
			}
		table.insert(res, group)
	end
	return table.concat(res, '\n')
end

-- Funzione di utilità per il manuale, verifica l'assenza di proprietà duplicate.
function p.checkdup(frame)
	local ids, res = {}, {}
	local cfg = readConfig()
	for _, groupName in ipairs(orderedGroupNames) do
		for _, linkConf in ipairs(cfg[groupName]) do
			if ids[linkConf.pid] then
				table.insert(res, linkConf.pid)
			else
				ids[linkConf.pid] = true
			end
		end
	end
	return #res == 0 and 'nessun duplicato' or
		string.format('<span class="error">%s</span>', table.concat(res, ', ')) 
end

-- Funzione per l'utilizzo da un altro modulo.
function p._main(args)
	return LinksManager:new(args):getList()
end

-- Funzione per il template {{Collegamenti esterni}}.
function p.main(frame)
	return p._main(getArgs(frame, { parentOnly = true }))
end

return p