# Module:Interprogetto

-- Modulo per implementare le funzionalità di template:Interprogetto  
require('Module:No globals')  
local p = {} -- per l'esportazione delle funzioni del modulo  

local cfg = mw.loadData('Module:Interprogetto/Configurazione')  
local root = mw.html.create('') -- radice del markup html  
local debug = {} -- per debug  
local categories = {} -- categorie di errore da aggiungere  
local errors_msg = {} -- messaggi di errore da aggiungere  

-- ============================================================================================================  
-- Ritorna la stringa se è definita e diversa da stringa vuota, altrimenti nil  
-- ============================================================================================================  
local function is_defined(s)  
if s and s ~= '' then  
return s  
else  
return nil  
end  
end  

-- ============================================================================================================  
-- Aggiunge uno spazio alla stringa se non termina per "'" (apostrofo)  
-- ============================================================================================================  
local function add_space(s)  
if not is_defined(s) then  
return ''  
elseif mw.ustring.sub(s, -1) == "'" then  
return s  
else  
return s .. ' '  
end  
end  

-- ============================================================================================================  
-- Aggiunge la categoria "category" alla lista di categorie da aggiungere alla voce  
-- ============================================================================================================  
local function add_category(category)  
if category then  
categories[category] = true  
end  
end  

-- ============================================================================================================  
-- Aggiunge il messaggio di errore "error_msg" alla lista di messaggi di errore da visualizzare.  
-- Se viene precisata una categoria di errore la aggiunge alle categorie in cui inserire la voce,  
-- altrimenti inserisce la categoria di errore standard  
-- ============================================================================================================  
local function add_error(error_msg, category)  
if error_msg then  
errors_msg[#errors_msg+1] = error_msg  
if category then  
add_category(category)  
else  
add_category('Errori di compilazione del template Interprogetto')  
end  
end  
end  

-- ============================================================================================================  
-- Ritorna un collegamento di default dato il nome di un progetto, nil in caso di errori  
-- ============================================================================================================  
local function get_default_collegamento(key_progetto, fullpagename)  
if cfg.parameters[key_progetto] then  
if cfg.parameters[key_progetto]['collegamento_non_esistente'] then  
add_error(cfg.parameters[key_progetto]['collegamento_non_esistente'])  
return nil  
elseif cfg.parameters[key_progetto].collegamento_default_minuscolo then  
return mw.ustring.gsub(fullpagename, '^%u', string.lower)  
else  
return fullpagename  
end  
else  
return nil  
end  
end  

-- ============================================================================================================  
-- Ordina una tabella in funzione della chiave "ordine" degli elementi della tabella  
-- ============================================================================================================  
local function sort_by_ordine(t1, t2)  
if t1.ordine < t2.ordine then  
return true  
end  
end  

-- ============================================================================================================  
-- Classe per gestire la raccolta di informazioni da Wikidata  
-- ============================================================================================================  
local Wikidata_entity = {}  

function Wikidata_entity:new  
-- Carica i dati da Wikidata se esistono  
local self = {}  
self.entity = nil  
if mw.wikibase then  
self.entity = mw.wikibase.getEntityObject  
end  
setmetatable(self, { __index = Wikidata_entity,  
__tostring = function(t) return self:__tostring end })  
self.collegamenti = {}  
self.badge = {}  
self:loadCollegamenti  
-- Imposta il corsivo se la pagina su Wikidata ha la proprietà P31 ("Istance of", wikidata:Property:P31)  
-- con valore corrispondente a un'opera, come definito in cfg.check_opera  
self.corsivo = self:hasPropertyValue('P31', cfg.check_opera)  
-- Considera disambigua se la pagina su Wikidata ha la proprietà P31 ("Istance of", wikidata:Property:P31)  
-- con valore 4167410 ("Wikimedia disambiguation page", wikidata:Q4167410)  
self.disambigua = self:hasPropertyValue('P31', '4167410')  
return self  
end  

function Wikidata_entity:getLabel(label)  
if self.entity then  
return self.entity:getLabel(label)  
else  
return nil  
end  
end  

-- ============================================================================================================  
-- Carica collegamenti e badge da Wikidata controllando i progetti elencati in cfg.automatic_link  
-- ============================================================================================================  
function Wikidata_entity:loadCollegamenti  
if self.entity then  
for key_progetto, progetto in pairs(cfg.automatic_link) do  
-- Carica i collegamenti di un progetto solo se non disambigua o tipo di progetto abilitato in disambigua  
if not self.disambigua or cfg.progetti[key_progetto].abilita_in_disambigua then  
local sitelink = self.entity:getSitelink(progetto.interwiki)  
if sitelink then  
self.collegamenti[key_progetto] = sitelink  
if self.entity.sitelinks[progetto.interwiki].badges then  
local badge_class = {}  
local badge_title = {}  
for _, badge_quality in ipairs(self.entity.sitelinks[progetto.interwiki].badges) do  
if cfg.badges[badge_quality] then  
badge_class[#badge_class+1] = cfg.badges[badge_quality].class  
badge_title[#badge_title+1] = cfg.badges[badge_quality].title  
end  
end  
self.badge[key_progetto] = {}  
self.badge[key_progetto].class = table.concat(badge_class, ' ' )  
self.badge[key_progetto].title = table.concat(badge_title, ', ' )  
end  
elseif progetto.property_category then  
local property_id = progetto.property_category  
if self.entity.claims  
and self.entity.claims[property_id]  
and self.entity.claims[property_id][1]  
and self.entity.claims[property_id][1].mainsnak  
and self.entity.claims[property_id][1].mainsnak.datavalue  
and self.entity.claims[property_id][1].mainsnak.datavalue.type == 'string' then  
self.collegamenti[key_progetto] = 'Category:' .. self.entity.claims[property_id][1].mainsnak.datavalue.value  
end  
end  
end  
end  
end  
end  

-- ============================================================================================================  
-- Verifica se una determinata proprietà ha uno dei valori specificati nella lista "values".  
-- Riadattata da "hasPropertyValue" su wikiquote:it:Modulo:Interprogetto a sua volta  
-- riadattata da "instanceof(arg)" su wikisource:it:Modulo:Autore  
-- ============================================================================================================  
function Wikidata_entity:hasPropertyValue(propertyId, values)  
if self.entity and self.entity.claims and self.entity.claims[propertyId] then  
local claims = self.entity.claims[propertyId]  
for _, claim in pairs(claims) do  
if claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value then  
local datavalue = claim.mainsnak.datavalue  
if datavalue.type == 'wikibase-entityid' then  
if datavalue.value['entity-type'] == 'item' then  
if type(values) == 'table' then  
if values[tostring(datavalue.value['numeric-id'])] then  
return true  
end  
elseif values == tostring(datavalue.value['numeric-id']) then  
return true  
end  
end  
end  
end  
end  
end  
return false  
end  
-- ============================================================================================================  
-- Fine definizione della classe Wikidata_entity  
-- ============================================================================================================  

-- ============================================================================================================  
-- Classe per gestire i collegamenti interprogetto  
-- ============================================================================================================  
local Collegamento = {}  

function Collegamento:new(key_progetto, args, entity, default)  
-- Crea un collegamento a un progetto, riceve il nome del progetto, gli argomenti da usare per determinare  
-- i valori dei vari parametri del collegamento. Si appoggia alla tabella esterna cfg.parameters per i  
-- valori di default del progetto e alla tabella globale default per i valori di default generali  

local self = {}  
setmetatable(self, { __index = Collegamento,  
__tostring = function(t) return self:__tostring end })  
local default_progetto = cfg.parameters[key_progetto]  
if default_progetto == nil then  
-- Progetto non riconosciuto  
return nil  
end  
self.collegamento = args[key_progetto]  
if not is_defined(self.collegamento) then  
-- Collegamento non definito correttamente  
return nil  
else  
self.key_progetto = key_progetto  
self.ordine = default_progetto.ordine  
self.badge_leftbar = {}  
self.badge_leftbar.class = (entity.badge[key_progetto] and entity.badge[key_progetto].class) or ''  
self.badge_leftbar.title = (entity.badge[key_progetto] and entity.badge[key_progetto].title) or ''  
self.etichetta = is_defined(args[key_progetto .. '_etichetta']) or (default_progetto.etichetta_lower and default.etichetta_lower) or default.etichetta  
self.oggetto = args[key_progetto .. '_oggetto'] or default.oggetto or default_progetto.oggetto  
if default.frase_unificata or default_progetto.preposizione then  
self.preposizione = args[key_progetto .. '_preposizione'] or default.preposizione or default_progetto.preposizione  
else  
self.preposizione = ''  
end  
if default.frase_unificata then  
self.testo_prima = ''  
self.testo_dopo = ''  
else  
self.testo_prima = default_progetto.testo_prima  
self.testo_dopo = default_progetto.testo_dopo  
end  
if key_progetto == 'notizia' and is_defined(args.data) then  
self.testo_dopo = ' <small>' .. args.data .. '</small>'  
end  
if default_progetto.lingua and args[key_progetto .. '_lingua'] then  
self.lingua = ' in lingua ' .. args[key_progetto .. '_lingua']  
else  
self.lingua = ''  
end  
return self  
end  
end  

function Collegamento:Link  
if cfg.parameters[self.key_progetto].link == 'Link' then return self:Link_text end  
if cfg.parameters[self.key_progetto].link == 'LinkWithLanguage' then return self:Link_language end  
if cfg.parameters[self.key_progetto].link == 'LinkRicette' then return self:Link_ricette end  
if cfg.parameters[self.key_progetto].link == 'LinkIncubator' then return self:Link_incubator end  
add_error('Error interno module Interprogetto:' .. self.key_progetto)  
return ''  
end  

function Collegamento:Link_lb  
if cfg.parameters[self.key_progetto].link == 'Link' then return self:Link_text_lb end  
if cfg.parameters[self.key_progetto].link == 'LinkWithLanguage' then return self:Link_language_lb end  
if cfg.parameters[self.key_progetto].link == 'LinkRicette' then return self:Link_text_lb end  
if cfg.parameters[self.key_progetto].link == 'LinkIncubator' then return self:Link_incubator_lb end  
add_error('Errore interno module Interprogetto:' .. self.key_progetto)  
return ''  
end  

function Collegamento:Link_text  
local default_progetto = cfg.parameters[self.key_progetto]  
return "* **", default_progetto.nome_progetto, "** contiene ",  
self.oggetto, self.lingua, " ", add_space(self.preposizione), self.testo_prima, "**", self.etichetta, "**", self.testo_dopo  
end  

function Collegamento:Link_text_lb  
local default_progetto = cfg.parameters[self.key_progetto]  
return "", default_progetto.nome_leftbar or default_progetto.nome_progetto, ""  
end  

function Collegamento:Link_ricette  
local default_progetto = cfg.parameters[self.key_progetto]  
return "* Il *Libro di cucina* di **Wikibooks** contiene ricette relative a questo argomento"  
end  

function Collegamento:Link_language  
local default_progetto = cfg.parameters[self.key_progetto]  
local main_page_link = tostring(mw.uri.fullUrl(default_progetto.prefix, {uselang='it'}))  
return "* '''<span class=\"plainlinks\">[", main_page_link, " ", default_progetto.nome_progetto,  
"]</span>''' contiene ", self.oggetto, " ", add_space(self.preposizione), self.testo_prima,  
"'''<span class=\"plainlinks\">[", tostring(mw.uri.fullUrl(default_progetto.prefix .. self.collegamento, {uselang='it'})),  
" ", self.etichetta, "]</span>'''", self.testo_dopo  
end  

function Collegamento:Link_language_lb  
local default_progetto = cfg.parameters[self.key_progetto]  
return "<span class=\"plainlinks\" title=\"", default_progetto.prefix , self.collegamento, "\">[",  
tostring(mw.uri.fullUrl(default_progetto.prefix .. self.collegamento, {uselang='it'})), " ",  
default_progetto.nome_leftbar or default_progetto.nome_progetto, "]</span>"  
end  

function Collegamento:Link_incubator  
local default_progetto = cfg.parameters[self.key_progetto]  
local oggetto = self.oggetto  
if not cfg.prefix_incubator[oggetto] then  
oggetto = default_progetto.oggetto  
end  
local collegamento = tostring(mw.uri.fullUrl(table.concat({'incubator:', cfg.prefix_incubator[oggetto],  
'/', self.collegamento}), {uselang='it'}))  
local main_page_incubator = tostring(mw.uri.fullUrl('incubator:Incubator:Main Page/it', {uselang='it'}))  
local main_page_progetto = ''  
if oggetto == 'wikipedia' then  
main_page_progetto = 'Wikipedia'  
else  
main_page_progetto = table.concat({*, oggetto:gsub('^%l', string.upper), *})  
end  
return "* **<span class=\"plainlinks\">[", main_page_incubator, " Incubator]</span>** contiene un test su ",  
main_page_progetto, self.lingua, " ", add_space(self.preposizione), "**<span class=\"plainlinks\">[", collegamento, " ", self.etichetta, "]</span>**"  
end  

function Collegamento:Link_incubator_lb  
local default_progetto = cfg.parameters[self.key_progetto]  
local oggetto = self.oggetto  
if not cfg.prefix_incubator[oggetto] then  
oggetto = default_progetto.oggetto  
end  
local collegamento = tostring(mw.uri.fullUrl(table.concat({'incubator:', cfg.prefix_incubator[oggetto],  
'/', self.collegamento}), {uselang='it'}))  
return mw.message.newRawMessage("<span class=\"plainlinks\" title=\"$1\">[$2 Incubator]<span>", {self.etichetta, collegamento}):plain  
end  
-- ============================================================================================================  
-- Fine definizione della classe Collegamento  
-- ============================================================================================================  

-- ============================================================================================================  
-- Scandisce la tabella progetti e produce il codice html per l'elenco dei collegamenti nella barra di sinistra  
-- Imposta il tag div id="interProject" (vedi MediaWiki:InterProject.js incluso da Mediawiki:Common.js)  
-- ============================================================================================================  
local function RenderLeftBar(progetti)  

local leftbar = mw.html.create('div'):attr('title', 'Collegamenti verso gli altri progetti Wikimedia')  
for _, progetto in ipairs(progetti) do  
leftbar:newline  
leftbar:wikitext('<li class=\"', progetto.badge_leftbar.class, '\" title=\"', progetto.badge_leftbar.title, '\">')  
leftbar:wikitext(progetto:Link_lb)  
if cfg.parameters[progetto.key_progetto].nome_leftbar then  
leftbar:wikitext('  
(', cfg.parameters[progetto.key_progetto].nome_progetto, ')')  
end  
leftbar:wikitext('</li>')  
end  
root:tag('div')  
:attr('id', 'interProject')  
:addClass('toccolours')  
:cssText('display: ; clear: both; margin-top: 2em')  
:tag('p')  
:attr('id', 'sisterProjects')  
:cssText('background-color: #efefef; font-weight: bold; margin: 0')  
:tag('span')  
:wikitext('Altri progetti')  
:done  
:done  
:node(leftbar)  
end  

-- ============================================================================================================  
-- Scandisce la tabella progetti e produce il codice html per l'elenco puntato dei collegamenti interprogetto  
-- ============================================================================================================  
local function RenderLinksInText(progetti)  
for _, progetto in ipairs(progetti) do  
root:newline  
root:wikitext(progetto:Link)  
end  
end  

-- ============================================================================================================  
-- Funzione principale richiamata dal template Interprogetto  
-- ============================================================================================================  
function p.interprogetto  

local origArgs  
-- ========================================================================================================  
-- Se chiamata mediante #invoke, usa gli argomenti passati al template invocante.  
-- Altrimenti a scopo di test assume che gli argomenti siano passati direttamente  
-- ========================================================================================================  
if == mw.getCurrentFrame then  
origArgs = :getParent.args  
else  
origArgs =  
end  
-- Tabella con i parametri di default valorizzati  
local default = {}  
-- ========================================================================================================  
-- Carica il nome della pagina corrente  
-- ========================================================================================================  
local current_page = mw.title.getCurrentTitle  
local current_namespace = current_page.namespace  
local current_pagename = current_page.text  
-- Per i namespace usa il nome canonico (inglese) per non avere differenze tra progetti:  
-- esempio: Utente/User, Categoria/Category, ma anche Wikiquote/Wikipedia (Project), ecc.  
if current_namespace ~= 0 then  
default.current_fullpagename = mw.site.namespaces[current_namespace].canonicalName .. ':' .. current_pagename  
else  
default.current_fullpagename = current_pagename  
end  
-- ========================================================================================================  
-- Carica i dati da Wikidata nell'oggetto "entity"  
-- ========================================================================================================  
local entity = Wikidata_entity:new  
-- ========================================================================================================  
-- Calcola l'etichetta di default per i collegamenti, in ordine di priorità:  
-- 1) Se è definita l'etichetta in lingua italiana su Wikidata usa questa (elimiando un eventuale  
-- "Categoria:" di fronte alla voce)  
-- 2) Altrimenti usa il nome della pagina corrente, eliminando un'eventuale disambigua in coda alla voce,  
-- e definisce etichetta_lower come il nome della voce con iniziale minuscola.  
-- Se la voce è segnalata come opera su Wikidata allora l'etichetta è in corsivo (senza iniziale minuscola)  
-- ========================================================================================================  
if is_defined(origArgs.etichetta) then  
default.etichetta = origArgs.etichetta  
else  
local wikidata_label = entity:getLabel('it')  
if is_defined(wikidata_label) then  
-- Elimina un eventuale "Categoria:" in fronte del nome  
default.etichetta = mw.ustring.gsub(wikidata_label, '^Categoria:', '')  
else  
-- Elimina un'eventuale disambigua dal nome  
default.etichetta = mw.ustring.gsub(current_pagename, ' %(.*%)$', '')  
if current_namespace == 0 then  
default.etichetta_lower = mw.ustring.gsub(default.etichetta, '^%u', string.lower)  
end  
end  
if entity.corsivo then  
default.etichetta = '<span style=\"font-style:italic;\">' .. default.etichetta .. '</span>'  
default.etichetta_lower = '<span style=\"font-style:italic;\">' .. default.etichetta .. '</span>'  
end  
end  
-- ========================================================================================================  
-- Calcola preposizione e oggetto di default, modificandoli se il namespace è 14 (categoria)  
-- ========================================================================================================  
if current_namespace ~= 14 then  
default.preposizione = origArgs.preposizione  
default.oggetto = origArgs.oggetto  
else  
default.preposizione = origArgs.preposizione or "sull'argomento"  
default.oggetto = origArgs.oggetto or "una categoria"  
default.frase_unificata = true  
end  
-- ========================================================================================================  
-- Copia i parametri in una nuova tabella, creando coppie progetto/collegamento per i parametri posizionali  
-- e controllando per parametri duplicati e nomi di progetto non conosciuti  
-- ========================================================================================================  
local newArgs = {}  
local nolink = false  
for key, value in pairs(origArgs) do  
if tonumber(key) then  
local key_progetto = mw.text.trim(value)  
if cfg.parameters[key_progetto] then  
if origArgs[key_progetto] then  
add_error('Collegamento a \"' .. value .. '\" inserito sia come parametro posizionale che nominale')  
else  
newArgs[key_progetto] = get_default_collegamento(key_progetto, default.current_fullpagename)  
end  
else  
if key == 1 and key_progetto == 'nolink' then  
nolink = true  
else  
add_error('Il parametro \"' .. value .. '\" non corrisponde a nessun progetto riconosciuto dal template')  
end  
end  
else  
newArgs[key] = value  
end  
end  
-- ========================================================================================================  
-- Controlla i collegamenti inseriti manualmente integrandoli eventualmente con quelli presenti in Wikidata.  
-- Salta questo passo se c'è un collegamento a "notizia" dato che in questo caso deve essere unico e quindi  
-- non deve aggiungere i collegamenti da Wikidata. Inoltre, in caso di "disambigua", salta i progetti non  
-- abilitati in disambigua che in ogni caso non devono essere aggiunti  
-- ========================================================================================================  
if not newArgs.notizia then  
-- ====================================================================================================  
-- Controlla il collegamento compilato manualmente e quello caricato da Wikidata  
-- ====================================================================================================  
for key_progetto, collegamento in pairs(newArgs) do  
if cfg.parameters[key_progetto] and cfg.automatic_link[key_progetto] and entity.entity then  
if not entity.disambigua or cfg.parameters[key_progetto].abilita_in_disambigua then  
local entity_collegamento = entity.collegamenti[key_progetto]  
if not is_defined(entity_collegamento) then  
if is_defined(collegamento) then  
-- Segnala che è presente il collegamento compilato manualmente ma non quello in Wikidata  
add_category(cfg.automatic_link[key_progetto].category_wikidata_missing)  
end  
elseif tostring(mw.uri.localUrl(collegamento)) ~= tostring(mw.uri.localUrl(entity_collegamento)) then  
-- Segnala che il collegamento compilato manualmente è diverso da quello in Wikidata  
add_category(cfg.automatic_link[key_progetto].category_wikidata_diff)  
end  
end  
end  
end  
-- ====================================================================================================  
-- Aggiunge il collegamento da Wikidata se non è presente quello compilato manualmente  
-- ====================================================================================================  
for key_progetto, collegamento in pairs(entity.collegamenti) do  
if not entity.disambigua or (cfg.parameters[key_progetto] and cfg.parameters[key_progetto].abilita_in_disambigua) then  
if not newArgs[key_progetto] and not (cfg.automatic_link[key_progetto].alias and newArgs[cfg.automatic_link[key_progetto].alias]) then  
newArgs[key_progetto] = collegamento  
-- Segnala che il collegamento è stato aggiunto da Wikidata  
add_category(cfg.automatic_link[key_progetto].category_wikidata)  
end  
end  
end  
end  
-- ========================================================================================================  
-- Sulla base della lista di argomenti ripulita costruisce la lista dei collegamenti da inserire  
-- ========================================================================================================  
local progetti = {}  
for key_progetto, collegamento in pairs(newArgs) do  
if cfg.parameters[key_progetto] then  
-- Salta i collegamenti a Wikidata per le voci nel namespace principale eccetto che per la Pagina principale  
if key_progetto ~= 'wikidata' or current_namespace ~= 0 or current_pagename == 'Pagina principale' then  
-- Se è disambigua registra solo i valori per i progetti abilitati in disambigua  
if not entity.disambigua or cfg.parameters[key_progetto].abilita_in_disambigua then  
local progetto = Collegamento:new(key_progetto, newArgs, entity, default)  
if progetto then  
progetti[#progetti+1] = progetto  
add_category(cfg.parameters[key_progetto].categoria)  
end  
end  
end  
end  
end  
table.sort(progetti, sort_by_ordine)  
-- ========================================================================================================  
-- Genera il codice html  
-- ========================================================================================================  
if #progetti == 0 then  
add_error('Template interprogetto vuoto e senza dati da recuperare da Wikidata', 'Errori di compilazione del template Interprogetto - template vuoto')  
else  
RenderLeftBar(progetti)  
if not nolink then  
RenderLinksInText(progetti)  
end  
end  
-- =================================================================================================================================  
-- Nei namespace ammessi inserisce le categorie di segnalazione di errori/avvisi  
-- =================================================================================================================================  
if cfg.whitelist_category[current_namespace] then  
for category, _ in pairs(categories) do  
root:wikitext('Categoria:' .. category .. '')  
end  
end  
-- =================================================================================================================================  
-- Aggiunge i messaggi di errore  
-- =================================================================================================================================  
if #errors_msg > 0 then  
if #progetti > 0 then  
root:wikitext('\n')  
end  
root:wikitext('<strong class=\"error\">' .. table.concat(errors_msg, '; ') .. '</strong>')  
end  
return tostring(root)  
end  

return p