La documentazione per questo modulo può essere creata in Modulo:Interprogetto/man

-- 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('Modulo: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(ignore_wikidata)
    -- Carica i dati da Wikidata se esistono
    local self = {}
    self.entity = nil
    if not(ignore_wikidata) and 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:nap:Modulo:Interprogetto]] a sua volta
-- riadattata da "instanceof(arg)" su [[wikisource:nap: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('Errore interno modulo 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 modulo Interprogetto:' .. self.key_progetto)
    return ''
end

function Collegamento:Link_text()
    local default_progetto = cfg.parameters[self.key_progetto]
    return "* [[File:", default_progetto.icona, "|link=", default_progetto.prefix, "|", default_progetto.dimensione_icona, "|Collabora a ",
            default_progetto.nome_progetto, "]] '''[[", default_progetto.prefix, "|", default_progetto.nome_progetto, "]]''' contiene ",
            self.oggetto, self.lingua, " ", add_space(self.preposizione), self.testo_prima, "'''[[", default_progetto.prefix,
            self.collegamento, "|", self.etichetta, "]]'''", self.testo_dopo
end

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

function Collegamento:Link_ricette()
    local default_progetto = cfg.parameters[self.key_progetto]
    return "* [[File:", default_progetto.icona, "|link=", default_progetto.prefix, "|", default_progetto.dimensione_icona,
            "|Collabora a ", default_progetto.nome_progetto, "]] Il ''[[b:Libro di cucina|Libro di cucina]]'' di '''[[b:|Wikibooks]]''' contiene [[",
            default_progetto.prefix, self.collegamento, "|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='nap'}))
    return "* [[File:", default_progetto.icona, "|link=", main_page_link, "|", default_progetto.dimensione_icona, "|Collabora a ",
            default_progetto.nome_progetto, "]] '''<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='nap'})),
            " ", 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='nap'})), " ",
            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='nap'}))
    local main_page_incubator = tostring(mw.uri.fullUrl('incubator:Incubator:Main Page/nap', {uselang='nap'}))
    local main_page_progetto = ''
    if oggetto == 'wikipedia' then
        main_page_progetto = '[[Wikipedia]]'
    else
        main_page_progetto = table.concat({'[[', oggetto, ':nap:|', oggetto:gsub('^%l', string.upper), ']]'})
    end
    return "* [[File:", default_progetto.icona, "|link=", main_page_incubator, "|", default_progetto.dimensione_icona,
            "|Collabora a Incubator]] '''<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='nap'}))
    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('<br />(', cfg.parameters[progetto.key_progetto].nome_progetto, ')')
        end
        leftbar:wikitext('</li>')
    end
    root:tag('div')
        :attr('id', 'interProject')
        :addClass('toccolours')
        :cssText('display: none; 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(frame)

    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 frame == mw.getCurrentFrame() then
        origArgs = frame:getParent().args
    else
        origArgs = frame
    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 ignore_wikidata = false
    local nowikidata = origArgs.nowikidata and origArgs.nowikidata:lower()
    if nowikidata == 's' or nowikidata == 'sì' or nowikidata == 'si' then
        ignore_wikidata = true
    end
    local entity = Wikidata_entity:new(ignore_wikidata)
    -- ========================================================================================================
    -- 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('nap')
        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
    	local nobarra = origArgs.nobarra and origArgs.nobarra:lower()
    	if nobarra ~= 's' and nobarra ~= 'sì' and nobarra ~= 'si' then
        	RenderLeftBar(progetti)
        end
        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