Questa pagina è protetta dallo spostamento
Questa pagina è protetta

Modulo:Bozza

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

Modulo che implementa le funzionalità del template:Bozza.

Legge i vari template:Richiesta revisione bozza presenti nella pagina e include il template:Bozza/avviso coi parametri adeguati.


--[[
* Modulo che implementa il template Bozza.
*
* Legge i template Richiesta revisione bozza presenti nel wikitesto della pagina e
* determina quali argomenti passare al sottotemplate Bozza/avviso.
]]

require('strict')

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

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

-- Verifica se una data stringa contiene categorie da disabilitare e in tal caso
-- restituisce la categoria di servizio pertinente in cui va inserita la pagina.
--
-- @param {string} str
-- @return {string}
local function checkForCategories(str)
	for colonprefix
	in str:gmatch('%[%[ *([Cc][Aa][Tt][Ee][Gg][Oo][Rr][YyIi][Aa]?) *: *[^%s][^\n]-%]%]')
	do
		local ns = mw.site.namespaces[colonprefix]

		if ns and ns.canonicalName == 'Category' then
			return '[[Categoria:Bozze con categorie da disabilitare]]'
		end
	end

	return ''
end

-- Restituisce un iteratore che consente di accedere agli elementi di una data
-- tabella in ordine decrescente delle sue chiavi.
-- Si assume che le chiavi della tabella siano valori timestamp.
--
-- @param {table} t
-- @return {function}
local function pairsByTimestamp(t)
	local a = {}
	local i = 0
	local comp = function (ts1, ts2)
		return ts1 > ts2
	end

	for ts in pairs(t) do
		table.insert(a, ts)
	end

	table.sort(a, comp)

	local iter = function ()
		i = i + 1
		if not a[i] then
			return
		else
			return a[i], t[a[i]], i
		end
	end

	return iter
end

-- Restituisce una data stringa dopo aver rimosso i commenti <!-- ... -->,
-- le porzioni di testo fra i tag nowiki e i template Categorie bozza.
--
-- @param {string} str
-- @return {string}
local function removeFalsePositives(str)
	return str
		:gsub('<!%-%-.-%-%->', '')
		:gsub('<nowiki>.-</nowiki>', '')
		:gsub('%{%{ *[Cc]ategorie *bozza *%|.-%}%}', '')
end

-- =============================================================================
--                           classe Submission
-- =============================================================================

local Submission = {}

-- Costruttore della classe Submission.
--
-- @param {string} wikitext Wikitesto del template Richiesta revisione bozza
-- @return {table} self
function Submission:new(wikitext)
	local self = {}
	setmetatable(self, { __index = Submission })

	-- elenco degli attributi privati
	self._formattedDate = nil
	self._reviewer = nil
	self._status = nil
	self._submitter = nil
	self._ts = nil
	self._wikitext = wikitext

	return self
end

-- Espande il template Richiesta revisione bozza se la richiesta è stata già
-- evasa e restituisce il testo espanso.
--
-- @param {boolean} showInfoButton Determina se mostrare il pulsante
--  'Chiedi chiarimenti' accanto all'esito dell'ultima richiesta di revisione
-- @return {string}
function Submission:expand(showInfoButton)
	if self:getStatus() == 'pending' then return '' end
	local wikitext = self._wikitext:gsub('%}%}$', '|mostra esito=x}}')
	if showInfoButton then
		wikitext = wikitext:gsub('%}%}$', '|mostra pulsante=x}}')
	end
	return mw.getCurrentFrame():preprocess(wikitext)
end

-- Restituisce la data formattata della richiesta di revisione.
--
-- @return {string}
function Submission:getFormattedDate()
	if self:getTimestamp() and not self._formattedDate then
		local lang = mw.language.getContentLanguage()
		local success, jFY = pcall(lang.formatDate, lang, 'j F Y', self:getTimestamp())
		if success then
			self._formattedDate = jFY
		end
	end
	return self._formattedDate
end

-- Restituisce il nome dell'utente che ha pubblicato l'esito.
--
-- @return {string}
function Submission:getReviewer()
	self._reviewer = self._reviewer or self:_parseArg('revisore', '.-') or
			self:_parseArg('utente', '.-') -- temporaneo per rinomina parametro
	return self._reviewer
end

-- Restituisce lo stato della richiesta di revisione (evasa o in sospeso).
--
-- @return {string}
function Submission:getStatus()
	self._status = self._status or
		self._wikitext:find('%| *esito *= *[^%s%|%}]') and 'old' or 'pending'
	return self._status
end

-- Restituisce il nome dell'utente che ha avanzato la richiesta di revisione.
--
-- @return {string}
function Submission:getSubmitter()
	self._submitter = self._submitter or self:_parseArg('richiedente', '.-')
	return self._submitter
end

-- Restituisce il timestamp della richiesta di revisione.
--
-- @return {string}
function Submission:getTimestamp()
	self._ts = self._ts or self:_parseArg('ts', '%d%d%d%d%d%d%d%d%d%d%d%d%d%d')
	return self._ts
end

-- Determina se la richiesta di revisione è valida.
--
-- @return {boolean}
function Submission:getValidity()
	return self:getFormattedDate() and true or false
end

-- Parsifica un dato parametro del template Richiesta revisione bozza e
-- ne restituisce il valore.
--
-- @return {string}
function Submission:_parseArg(arg, subpattern)
	local pattern = '%| *' .. arg .. ' *= *(' .. subpattern .. ') *[%|%}]'
	return self._wikitext:match(pattern)
end

-- =============================================================================
--                           classe SubmissionHistory
-- =============================================================================

local SubmissionHistory = {}

-- Costruttore della classe SubmissionHistory.
--
-- @param {table} submissions Array contenente tutte le richieste di revisione
--  della bozza già evase
-- @param {boolean} hideLastSubmission Determina se inserire anche l'esito dell'ultima
--  richiesta di revisione nel div compresso che contiene le richieste più vecchie
-- @param {boolean} showInfoButton Determina se mostrare il pulsante
--  'Chiedi chiarimenti' accanto all'esito dell'ultima richiesta di revisione
-- @return {table} self
function SubmissionHistory:new(submissions, hideLastSubmission, showInfoButton)
	local self = {}
	setmetatable(self, { __index = SubmissionHistory })

	-- elenco degli attributi privati
	self._collapsedDiv = nil
	self._header = nil
	self._hideLastSubmission = hideLastSubmission
	self._root = nil
	self._showInfoButton = showInfoButton
	self._submissionRows = {}
	self._submissions = submissions

	return self
end

-- Costruisce la cronologia di tutte le richieste di revisione della bozza già
-- evase e ne restituisce il codice HTML.
--
-- @return {string}
function SubmissionHistory:getHTML()
	self
		:_buildRoot()
		:_buildHeader()
		:_buildSubmissionRows()
		:_buildCollapsedDiv()
		:_appendChildNodes()

	return tostring(self._root)
end

-- Appende tutti gli elementi della cronologia ai rispettivi elementi padre.
--
-- @return {table} self
function SubmissionHistory:_appendChildNodes()
	if self._hideLastSubmission then
		self._collapsedDiv:node(self._header)
	else
		self._root:node(self._header)
	end

	for i, row in ipairs(self._submissionRows) do
		if i > (self._hideLastSubmission and 0 or 1) then
			self._collapsedDiv:node(row)
		else
			self._root:node(row)
		end
	end

	if self._collapsedDiv then
		self._root:node(self._collapsedDiv)
	end

	return self
end

-- Crea l'elemento div che mostra le richieste di revisione più vecchie,
-- qualora ve ne siano. È compresso di default.
--
-- @return {table} self
function SubmissionHistory:_buildCollapsedDiv()
	if #self._submissionRows >= (self._hideLastSubmission and 1 or 2) then
		self._collapsedDiv = mw.html.create('div')
			:addClass('nomobile mw-collapsible mw-collapsed')
			:attr('data-expandtext', 'mostra richieste precedenti')
			:attr('data-collapsetext', 'nascondi richieste precedenti')
	end
	return self
end

-- Crea l'elemento div che costituisce l'header della cronologia.
--
-- @return {table} self
function SubmissionHistory:_buildHeader()
	self._header = mw.html.create('div')
		:addClass('bozza-history-row nomobile bozza-history-header')
		:tag('div')
			:wikitext('Data richiesta')
			:done()
		:tag('div')
			:wikitext('Esito revisione')
			:done()
		:done()
	return self
end

-- Crea l'elemento div che costituisce la radice HTML della cronologia.
--
-- @return {table} self
function SubmissionHistory:_buildRoot()
	self._root = mw.html.create('div'):addClass('bozza-history')
	return self
end

-- Crea un elemento div per ciascuna richiesta di revisione da inserire
-- nella cronologia.
--
-- @return {table} self
function SubmissionHistory:_buildSubmissionRows()
	for ts, submission, i in pairsByTimestamp(self._submissions) do
		local row = mw.html.create('div')
			:addClass('bozza-history-row')
			:tag('div')
				:wikitext(submission:getFormattedDate())
				:done()
			:tag('div')
				:wikitext(submission:expand(i == 1 and self._showInfoButton))
				:done()
			:done()
		table.insert(self._submissionRows, row)
	end
	return self
end

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

local p = {}

-- Funzione per il template {{Bozza}}.
function p.main(frame)
	local currentTitle = mw.title.getCurrentTitle()
	local ns = currentTitle.namespace
	local text = removeFalsePositives(currentTitle:getContent())
	local args = getArgs(frame, { parentOnly = true })
	local editedByReviewer = false
	local revisionUser = frame:preprocess('{{REVISIONUSER}}')
	local submissions = { pending = {}, old = {} }

	-- cerca i template nel wikitesto della pagina dopo aver rimosso
	-- temporaneamente le graffe { singole che causano falsi positivi
	for tmp in text:gsub('%f[%{]%{%f[^%{]', '\r'):gmatch('%{%b{}%}') do
		-- scarta i template che non siano i template Richiesta revisione bozza
		if tmp:find('^%{%{ *[Rr]ichiesta *revisione *bozza *[%|%}]') then
			-- ripristina le graffe { singole nella porzione di testo trovata e
			-- crea un oggetto Submission per ciascun template Richiesta revisione bozza
			local submission = Submission:new(tmp:gsub('\r', '{'))
			-- scarta l'oggetto Submission se la richiesta di revisione è mal compilata
			if submission:getValidity() == true then
				submissions[submission:getStatus()][submission:getTimestamp()] = submission
				if submission:getStatus() == 'old' then
					-- mantiene il conto delle richieste di revisione già evase nel
					-- parametro 'numero richieste respinte' del sottotemplate Bozza/avviso
					local k = 'numero richieste respinte'
					args[k] = args[k] and args[k] + 1 or 1
					-- verifica se l'ultima modifica della pagina è stata fatta
					-- da uno dei revisori
					if not editedByReviewer then
						editedByReviewer = revisionUser == submission:getReviewer()
					end
				end
			end
		end
	end

	local pendingSubmission = select(2, next(submissions.pending))

	-- conditional statement finalizzato a impostare lo stato (parametro
	-- posizionale 1) del sottotemplate Bozza/avviso
	if ns == 0 or ns == 118 then
		if args.sposta then
			args[1] = 'S'
		elseif pendingSubmission then
			args[1] = 'R'
			-- se c'è una richiesta di revisione in sospeso, passa il suo timestamp e
			-- il nome utente del richiedente al sottotemplate Bozza/avviso
			if pendingSubmission:getSubmitter() then
				args['ts ultima richiesta revisione'] = pendingSubmission:getTimestamp()
				args['autore richiesta revisione'] = pendingSubmission:getSubmitter()
			end
		elseif args.proroga then
			args[1] = 'A'
		elseif next(submissions.old) then
			args[1] = 'N'
		else
			args[1] = nil
		end
	else
		args[1] = mw.language.getContentLanguage():ucfirst(args[1] or args.stato or '')
	end

	local hideLastSubmission = args[1] == 'S' or args[1] == 'R'

	if next(submissions.old) then
		-- determina se una bozza può essere sottoposta a revisione in base al
		-- namespace in cui si trova la pagina e alla presenza di richieste inevase
		local canBeSubmitted = ns == 118 and not hideLastSubmission

		-- crea un oggetto SubmissionHistory che contiene la cronologie delle
		-- richieste di revisione della bozza già evase e passa il suo codice HTML
		-- al sottotemplate Bozza/avviso
		args['cronologia revisioni'] = SubmissionHistory
			:new(submissions.old, hideLastSubmission, canBeSubmitted)
			:getHTML()

		-- passa il timestamp dell'ultima richiesta evasa al sottotemplate Bozza/avviso
		if canBeSubmitted then
			local lastSubmission
			for _, submission in pairsByTimestamp(submissions.old) do
				lastSubmission = submission
				break
			end
			args['ts ultima revisione'] = lastSubmission:getTimestamp()
		end
	end

	-- controlla la visibilità del pulsante 'Richiedi revisione' che
	-- viene creato dal sottotemplate Bozza/avviso
	if ns ~= 118 or editedByReviewer or hideLastSubmission then
		args['mostra pulsante'] = 'no'
	else
		args['mostra pulsante'] = 'sì'
	end

	return frame:extensionTag{
			name = 'templatestyles',
			args = { src = 'Modulo:Bozza/styles.css' }
		} .. frame:expandTemplate{
			title = 'Template:Bozza/avviso',
			args = args
		} .. (ns == 118 and checkForCategories(text) or '')
end

return p