Difference between revisions of "Module:Protection banner"

From Amanita Research
Jump to navigation Jump to search
(add a p.getPagetype function)
(add a p.renderImageLink function so that we can share image generation code between the padlock and the banner)
Line 258: Line 258:
 
end
 
end
  
function p.renderPadlock(data)
+
function p.renderImageLink(image, size, link, text, alt)
 
--[[
 
--[[
-- Renders the padlock seen in the top-right-hand corner or protected pages,
+
-- Renders the image link wikitext All parameters are optional
-- using the data provided in the data table.
+
-- apart from the display text.
 +
--
 +
-- @parameters:
 +
-- image - the image name
 +
-- size - the image size, as a number
 +
-- link - page linked to by the image
 +
-- text - the tooltip text
 +
-- alt - the alt text
 +
--
 +
-- All parameters are optional apart from the text parameter.
 +
--]]
 +
image = image or 'Transparent.gif'
 +
size = size or 20
 +
if link then
 +
link = '|link=' .. link
 +
else
 +
link = ''
 +
end
 +
text = text or error('No text parameter supplied to p.renderImageLink')
 +
if alt then
 +
alt = '|alt=' .. alt
 +
else
 +
alt = ''
 +
end
 +
return string.format('[[Image:%s|%dpx%s|%s%s]]', image, size, link, text, alt)
 +
end
 +
 
 +
function p.renderPadlock(image, right)
 +
--[[
 +
-- Renders the html of the padlock seen in the top-right-hand corner
 +
-- of protected pages.
 
--  
 
--  
-- Data fields:
+
-- @parameters:
-- data.right
+
-- image - the image wikitext
-- data.image
+
-- right - the "right" css property value, as a string
-- data.iconLink
+
--
-- data.iconText
+
-- Both parameters are optional.
-- data.altText
 
 
--]]
 
--]]
 
local root = mw.html.create('div')
 
local root = mw.html.create('div')
Line 274: Line 303:
 
:addClass('metadata topicon nopopups')
 
:addClass('metadata topicon nopopups')
 
:attr('id', 'protected-icon')
 
:attr('id', 'protected-icon')
:css{display = 'none', right = data.right or '55px'}
+
:css{display = 'none', right = right or '55px'}
:wikitext(format(
+
:wikitext(image)
'[[Image:%s|20px|link=%s|%s|alt=%s]]',
 
data.image, data.iconLink, data.iconText, data.altText
 
))
 
 
return tostring(root)
 
return tostring(root)
 
end
 
end
  
function p.renderBanner(data)
+
function p.renderBanner(page, image, text)
 
--[[
 
--[[
 
-- Renders the large protection banner placed at the top of articles,
 
-- Renders the large protection banner placed at the top of articles,
 
-- using the data provided in the data table.
 
-- using the data provided in the data table.
 
--  
 
--  
-- Data fields:
+
-- @parameters:
-- data.page
+
-- page - demo page parameter to pass to {{mbox}}
-- data.image
+
-- image - the image wikitext
-- data.text
+
-- text - the text to display
 +
--
 +
-- All parameters are optional.
 
--]]
 
--]]
 
mMessageBox = require('Module:Message box')
 
mMessageBox = require('Module:Message box')
 
local mbargs = { -- arguments for the message box module
 
local mbargs = { -- arguments for the message box module
page = data.page,
+
page = page,
 
type = 'protection',
 
type = 'protection',
image = data.image,
+
image = image,
text = data.text
+
text = text
 
}
 
}
 
return mMessageBox.main('mbox', mbargs)
 
return mMessageBox.main('mbox', mbargs)

Revision as of 10:36, 16 March 2014

Documentation for this module may be created at Module:Protection banner/doc

-- This module implements {{pp-meta}} and its daughter templates such as
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.

--------------------------------------------------------------------------------
-- Configuration
--------------------------------------------------------------------------------

local categories = {
	-- The key strings follow this format:
	-- type, level, ns, reason, expiry
	['edit-autoconfirmed-user-all-all'] = 'Wikipedia semi-protected user and user talk pages',
	['edit-autoconfirmed-project-all-all'] = 'Semi-protected project pages',
	['edit-autoconfirmed-file-all-all'] = 'Semi-protected images',
	['edit-autoconfirmed-template-all-all'] = 'Wikipedia semi-protected templates',
	['edit-autoconfirmed-portal-all-all'] = 'Semi-protected portals',
	['edit-autoconfirmed-talk-all-all'] = 'Semi-protected talk pages',
	['edit-autoconfirmed-all-vandalism-all'] = 'Wikipedia pages semi-protected against vandalism',
	['edit-sysop-user-all-all'] = 'Wikipedia protected user and user talk pages',
	['edit-sysop-file-all-all'] = 'Protected images',
	['edit-sysop-project-all-all'] = 'Protected project pages',
	['edit-sysop-template-all-all'] = 'Wikipedia protected templates',
	['edit-sysop-talk-all-all'] = 'Protected talk pages',
	['edit-sysop-all-vandalism-all'] = 'Wikipedia pages protected against vandalism',
	['edit-autoconfirmed-all-dispute-all'] = 'Wikipedia pages semi-protected due to dispute',
	['edit-sysop-all-dispute-all'] = 'Wikipedia pages protected due to dispute',
	['move-sysop-all-dispute-all'] = 'Wikipedia pages move-protected due to dispute',
	['move-sysop-user-all-all'] = 'Wikipedia move-protected user and user talk pages',
	['move-sysop-project-all-all'] = 'Wikipedia move-protected project pages',
	['move-sysop-portal-all-all'] = 'Wikipedia move-protected portals',
	['move-sysop-all-vandalism-all'] = 'Wikipedia pages move-protected due to vandalism',
	['edit-autoconfirmed-template-all-all'] = 'Wikipedia semi-protected templates',
	['move-sysop-template-all-all'] = 'Wikipedia move-protected templates',
	['edit-all-template-all-all'] = 'Wikipedia protected templates',
	['move-sysop-portal-all-all'] = 'Wikipedia move-protected portals',
	['edit-autoconfirmed-all-sock-all'] = 'Wikipedia pages semi-protected from banned users',
	['edit-sysop-all-sock-all'] = 'Wikipedia pages protected from banned users',
	['edit-autoconfirmed-all-blp-temp'] = 'Wikipedia temporarily semi-protected biographies of living people',
	['edit-sysop-all-blp-temp'] = 'Wikipedia temporarily protected biographies of living people',
	['edit-autoconfirmed-all-blp-all'] = 'Wikipedia indefinitely semi-protected biographies of living people',
	['edit-sysop-all-blp-all'] = 'Wikipedia indefinitely protected biographies of living people',
	['edit-autoconfirmed-all-all-indef'] = 'Wikipedia indefinitely semi-protected pages',
	['edit-autoconfirmed-category-all-all'] = 'Wikipedia semi-protected categories',
	['edit-sysop-category-all-all'] = 'Wikipedia protected categories',
	['move-sysop-talk-all-all'] = 'Wikipedia move-protected talk pages',
	['move-sysop-all-all-indef'] = 'Wikipedia indefinitely move-protected pages',
	['edit-autoconfirmed-all-all-all'] = 'Wikipedia semi-protected pages',
	['move-sysop-all-all-all'] = 'Wikipedia move-protected pages',
	['pc-autoconfirmed-all-all-all'] = 'Wikipedia pending changes protected pages (level 1)',
	['pc-reviewer-all-all-all'] = 'Wikipedia pending changes protected pages (level 2)',
	['all-all-all-office-all'] = 'Wikipedia Office-protected pages',
	['all-all-all-all-all'] = 'Wikipedia protected pages',
}

local categoryNamespaces = {
	[2] = 'user',
	[3] = 'user',
	[4] = 'project',
	[6] = 'file',
	[10] = 'template',
	[12] = 'project',
	[14] = 'category',
	[100] = 'portal',
}

local behaviors = {
	vandalism = 'namespaceFirst',
	dispute = 'reasonFirst',
	blp = 'reasonFirst',
	sock = 'reasonFirst',
	office = 'reasonFirst',
}

local pagetypeNamespaces = {
	[0] = 'article',
	[6] = 'file',
	[10] = 'template',
	[14] = 'category',
	[828] = 'module',
	default = 'page'
}

--[[
-- Not currently used
local error_categories = {
	incorrect = 'Wikipedia pages with incorrect protection templates',
	no_expiry = 'Wikipedia protected pages without expiry',
	create = 'Wikipedia pages tagged as create-protected',
	template = 'Wikipedia template-protected pages other than templates and modules'
}
--]]

--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------

-- Initialise necessary modules.
local mArguments = require('Module:Arguments')
local mMessageBox -- only needs to be loaded if we are outputting a banner, so lazily initialise

-- Define often-used functions as local variables.
local tconcat = table.concat
local tinsert = table.insert
local tremove = table.remove
local ceil = math.ceil
local format = string.format

local function toTableEnd(t, pos)
	-- Sends the value at position pos to the end of array t, and shifts the
	-- other items down accordingly.
	return tinsert(t, tremove(t, pos))
end

local p = {}

function p.getPagetype(ns)
	-- Returns a string with the page's type. Takes a namespace number as input.
	local pagetype = pagetypeNamespaces[ns] or pagetypeNamespaces.default
	if not pagetype then
		error('the page type could not be found; please define a name for the key "default"')
	end
	return pagetype
end

function p.matchNamespace(ns)
	-- Matches a namespace number to a string that can be passed to the
	-- namespace parameter of p.getCategoryName.
	if not ns or type(ns) ~= 'number' then
		return nil
	end
	local nskey = categoryNamespaces[ns]
	if not nskey and ns % 2 == 1 then
			nskey = 'talk'
	end
	return nskey
end

function p.getCategoryName(cats, protType, protLevel, namespace, reason, expiry)
	--[[
	-- Gets a category name from the category table, given a combination of
	-- the protection type, the protection level, the namespace number, the
	-- reason for protection, and the expiry date.
	--]]
	cats = cats or categories

	--[[
	-- Define the initial order to test properties in. The subtable position
	-- is the order the properties will be tested in, and the pos value in
	-- each subtable is the position of the value in the category key.
	--]]
	local properties = {
		{pos = 5, val = expiry},
		{pos = 3, val = p.matchNamespace(namespace)},
		{pos = 4, val = reason},
		{pos = 2, val = protLevel},
		{pos = 1, val = protType}
	}

	--[[
	-- Validate reason, and if it is specified as a "namespaceFirst" reason,
	-- move the namespace subtable to the end of the properties table.
	-- This is necessary to accommodate reasons like "vandalism", as the old
	-- {{pp-vandalism}} template used namespace categories rather than
	-- vandalism categories if they were available.
	--]]
	local behavior
	if not reason then
		behavior = 'reasonFirst'
	else
		behavior = behaviors[reason]
	end
	if behavior == 'namespaceFirst' then
		toTableEnd(properties, 2) -- move namespace to end
	elseif behavior == 'reasonFirst' then
		toTableEnd(properties, 3) -- move reason to end
	else
		error(reason .. ' is not a valid reason')
	end

	--[[
	-- Define the attempt order. Properties with no value defined are moved
	-- to the end, where they will later be given the value "all". This is
	-- to cut down on the number of table lookups in the cats table, which
	-- grows exponentially with the number of properties with valid values.
	-- We keep track of the number of active properties with the noActive
	-- parameter.
	--]]
	local active, inactive = {}, {}
	for i, t in ipairs(properties) do
		if t.val then
			active[#active + 1] = t
		else
			inactive[#inactive + 1] = t
		end
	end
	local noActive = #active
	local attemptOrder = active
	for i, t in ipairs(inactive) do
		attemptOrder[#attemptOrder + 1] = t
	end

	--[[
	-- Check increasingly generic key combinations until we find a match.
	-- If a specific category exists for the combination of properties
	-- we are given, that match will be found first. If not, we keep
	-- trying different key combinations until we match using the key
	-- "all-all-all-all-all".
	--
	-- To generate the keys, we index the property subtables using a
	-- binary matrix with indexes i and j. j is only calculated up to
	-- the number of active properties. For example, if there were three
	-- active properties, the matrix would look like this, with 0
	-- corresponding to the string "all", and 1 corresponding to the
	-- val field in the property table:
	-- 
	--   j 1  2  3
	-- i  
	-- 1   1  1  1
	-- 2   0  1  1
	-- 3   1  0  1
	-- 4   0  0  1
	-- 5   1  1  0
	-- 6   0  1  0
	-- 7   1  0  0
	-- 8   0  0  0
	-- 
	-- Values of j higher than the number of active properties are set
	-- to the string "all".
	--
	-- A key for the category table is constructed for each value of i.
	-- The correct position of the value in the key is determined by the
	-- pos field in the property table.
	--]]
	for i = 1, 2^noActive do
		local key = {}
		for j, t in ipairs(attemptOrder) do
			local pos = t.pos
			local val = t.val
			if j > noActive then
				key[pos] = 'all'
			else
				local quotient = i / 2 ^ (j - 1)
				quotient = ceil(quotient)
				if quotient % 2 == 1 then
					key[pos] = val
				else
					key[pos] = 'all'
				end
			end
		end
		key = tconcat(key, '-')
		mw.log(key) -- for debugging
		local attempt = cats[key]
		if attempt then
			return attempt
		end
	end
	error('No category match found; please define the category for key "all-all-all-all-all"')
end

function p.renderImageLink(image, size, link, text, alt)
	--[[
	-- Renders the image link wikitext All parameters are optional
	-- apart from the display text.
	--
	-- @parameters:
	-- image - the image name
	-- size - the image size, as a number
	-- link - page linked to by the image
	-- text - the tooltip text
	-- alt - the alt text
	--
	-- All parameters are optional apart from the text parameter.
	--]]
	image = image or 'Transparent.gif'
	size = size or 20
	if link then
		link = '|link=' .. link
	else
		link = ''
	end
	text = text or error('No text parameter supplied to p.renderImageLink')
	if alt then
		alt = '|alt=' .. alt
	else
		alt = ''
	end
	return string.format('[[Image:%s|%dpx%s|%s%s]]', image, size, link, text, alt)
end

function p.renderPadlock(image, right)
	--[[
	-- Renders the html of the padlock seen in the top-right-hand corner
	-- of protected pages.
	-- 
	-- @parameters:
	-- image - the image wikitext
	-- right - the "right" css property value, as a string
	--
	-- Both parameters are optional.
	--]]
	local root = mw.html.create('div')
	root
		:addClass('metadata topicon nopopups')
		:attr('id', 'protected-icon')
		:css{display = 'none', right = right or '55px'}
		:wikitext(image)
	return tostring(root)
end

function p.renderBanner(page, image, text)
	--[[
	-- Renders the large protection banner placed at the top of articles,
	-- using the data provided in the data table.
	-- 
	-- @parameters:
	-- page - demo page parameter to pass to {{mbox}}
	-- image - the image wikitext
	-- text - the text to display
	--
	-- All parameters are optional.
	--]]
	mMessageBox = require('Module:Message box')
	local mbargs = { -- arguments for the message box module
		page = page,
		type = 'protection',
		image = image,
		text = text
	}
	return mMessageBox.main('mbox', mbargs)
end

return p