Module:Protection banner

-- This module implements and its daughter templates such as --,  and.

-- Initialise necessary modules. local mArguments = require('Module:Arguments') local mProtectionLevel = require('Module:Effective protection level')._main local mFileLink = require('Module:File link') local yesno = require('Module:Yesno') local mMessageBox -- only needs to be loaded if we are outputting a banner, so lazily initialise

-- Load config local cfg = mw.loadData('Module:Protection banner/config')

local p = {}

-- Helper functions

local function validateField(tName, t, field, expectType, nilOk) local val = t[field] local valType = type(val) if not (valType == expectType or nilOk and valType == 'nil') then error(string.format( "type error in '%s' field '%s' (%s%s expected, got %s)", tName, tostring(field), expectType, nilOk and ' or nil' or '', valType ), 2)	end end

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 table.insert(t, table.remove(t, pos)) end

-- Main functions

function p.main(frame) local args = mArguments.getArgs(frame) return p._main(args) end

function p._main(args) local protectionLevel = p.getProtectionLevel(args) local isSmall = yesno(args.small) or false local bannerConfig = p.getBannerConfig(protectionLevel, args)

local ret = '' if isSmall then ret = ret .. p.makePadlock(protectionLevel, args, bannerConfig) else ret = ret .. p.makeBanner(protectionLevel, args, bannerConfig) end ret = ret .. p.getProtectionCategory(protectionLevel, args) ret = ret .. p.getTrackingCategories(protectionLevel, args) return ret end

-- Protection functions

function p.getProtectionLevel(args) local title if args.page then title = mw.title.new(args.page) else title = mw.title.getCurrentTitle end local protectionData = p.getProtectionData(title) protectionLevel = protectionData[args.action or 'edit'] return protectionLevel or '*' end

function p.getProtectionData(title) -- Gets a table containing protection data for the given title. The data -- is cached using a metatable, and so can be indexed as needed without -- a performance loss. local protectionData = {} local actions = { create = true, edit = true, move = true, autoreview = true }	setmetatable(protectionData, {		__index = function (t, key)			local level			if actions[key] then				level = mProtectionLevel(key, title)				if level == 'accountcreator' then					-- Lump titleblacklisted pages in with template-protected pages,					-- since templateeditors can do both.					level = 'templateeditor'				end			end			protectionData[key] = level			return level		end	}) return protectionData end

-- Banner config functions

function p.getBannerConfig(protectionLevel, args) end

-- Padlock functions

function p.makePadlock(protectionLevel, args, bannerConfig) local data = p.makePadlockData(protectionLevel, args, bannerConfig) return p.renderPadlock(data) end

function p.makePadlockData(protectionLevel, args, bannerConfig) end

function p.renderPadlock(data) data = data or {} local image = mFileLink.new(data.filename or 'Transparent.gif') :width(20) :link(data.link) :alt(data.alt) :render local root = mw.html.create('div') root :addClass('metadata topicon nopopups') :attr('id', 'protected-icon') :css{display = 'none', right = data.right or '55px'} :wikitext(image) return tostring(root) end

-- Banner functions

function p.makeBanner(protectionLevel, args, bannerConfig) local data = p.makeBannerData(protectionLevel, args, bannerConfig) return p.renderBanner(data) end

function p.makeBannerData(protectionLevel, args, bannerConfig) end

function p.renderBanner(data) data = data or {} local image = mFileLink.new(data.filename) :width(40) :caption(data.mouseover) :render local mbargs = { page = data.page, type = 'protection', image = image, text = string.format(			"%s%s",			data.reasonText,			data.explanationText and ' ' .. data.explanationText or ''		) }	return mMessageBox.main('mbox', mbargs) end

-- Protection category functions

function p.getProtectionCategory(protectionLevel, args) end

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 = cfg.categoryNamespaces[ns] if not nskey and ns % 2 == 1 then nskey = 'talk' end return nskey end

function p.getCategoryName(cats, action, level, 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 cfg.categories

--	-- Define the properties table. Each property is a table containing the	-- canonical order that the property is tested in, the position the	-- property has in the category key strings, and the property value itself.	-- local properties = { expiry = {order = 1, keypos = 5, val = expiry}, namespace = {order = 2, keypos = 3, val = p.matchNamespace(namespace)}, reason = {order = 3, keypos = 4, val = reason}, level = {order = 4, keypos = 2, val = level}, action = {order = 5, keypos = 1, val = action} }

--	-- Load the category order configuration for the reason specified.	-- The configuration is stored in the categoryOrder field of each reason	-- subtable of cfg.reasons. If the value is a table, then the order is the	-- values specified in the table. If the value is a string, then the	-- property corresponding to that string is tested last (i.e. it is the most	-- important, because it keeps its specified value the longest) and the	-- other properties are tested in the canonical order. If the value is of	-- any other type then the canonical order is used.	-- local reasonTable = reason and cfg.reasons[reason] local categoryOrder = reasonTable and reasonTable.categoryOrder local categoryOrderType = type(categoryOrder) local configOrder = {} if categoryOrderType == 'table' then local dupes = {} for i = 1, 5 do			local propertiesKey = categoryOrder[i] if not propertiesKey then local msg = 'no entry found for key ' .. i					.. ' in the cfg.reasons.' .. reason .. '.categoryOrder table' error(msg) end local property = properties[propertiesKey] if not property then local msg = 'invalid value "'					.. propertiesKey					.. '" detected in the cfg.reasons.' .. reason .. '.categoryOrder table' error(msg) end if dupes[propertiesKey] then local msg = 'duplicate values "'					.. propertiesKey					.. '" detected in the cfg.reasons.' .. reason .. '.categoryOrder table' error(msg) else dupes[propertiesKey] = true end configOrder[i] = property end else for propertiesKey, t in pairs(properties) do			configOrder[t.order] = t		end if categoryOrderType == 'string' then local property = properties[categoryOrder] if not property then local msg = '"'					.. categoryOrder					.. '" is not a valid value of cfg.reasons.' .. reason .. '.categoryOrder' error(msg) end toTableEnd(configOrder, property.order) end 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(configOrder) 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			if j > noActive then key[t.keypos] = 'all' else local quotient = i / 2 ^ (j - 1) quotient = math.ceil(quotient) if quotient % 2 == 1 then key[t.keypos] = t.val else key[t.keypos] = 'all' end end end key = table.concat(key, '-') 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

-- Tracking category functions

function p.getTrackingCategories(protectionLevel, args) end

--[[ When to add "Category:Wikipedia pages with incorrect protection templates":
 * If it uses a type which is incompatible with the actual protection level of a page, or
 * If an expiry date is set and is in the past

When to add "Category:Wikipedia protected pages without expiry": (This leaves reasons "dispute", "vandalism", "usertalk", and "sock") ]]
 * No expiry is set, and
 * Expiry can be set, and
 * The protection type is not "move" (could change this?), and
 * The reason doesn't have "indefinitely protected" categories (pp-blp, pp-semi-indef and pp-move-indef have these), and
 * The reason is not the generic pp-protected (could change this?)

return p