Difference between revisions of "Module:Protection banner"

From BESA® Wiki
Jump to: navigation, search
m (1 revision)
(make a start on the data structure)
(228 intermediate revisions by 5 users not shown)
Line 2: Line 2:
 
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.
 
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.
  
-- Initialise necessary modules.
+
local categories = {}
require('Module:No globals')
+
-- The naming scheme is: protection type - protection level - namespace - reason
local makeFileLink = require('Module:File link')._main
+
categories['edit-semi-user-all'] = 'Wikipedia semi-protected user and user talk pages'
local effectiveProtectionLevel = require('Module:Effective protection level')._main
+
categories['edit-semi-project-all'] = 'Semi-protected project pages'
local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main
+
categories['edit-semi-file-all'] = 'Semi-protected images'
local yesno = require('Module:Yesno')
+
categories['edit-semi-template-all'] = 'Wikipedia semi-protected templates'
 +
categories['edit-semi-portal-all'] = 'Semi-protected portals'
 +
categories['edit-semi-talk-all'] = 'Semi-protected talk pages'
 +
categories['edit-semi-other-vandalism'] 'Wikipedia pages semi-protected against vandalism'
 +
categories['edit-full-user-all'] = 'Wikipedia protected user and user talk pages'
 +
categories['edit-full-file-all'] = 'Protected images'
 +
categories['edit-full-project-all'] = 'Protected project pages'
 +
categories['edit-full-template-all'] = 'Wikipedia protected templates'
 +
categories['edit-full-talk-all'] = 'Protected talk pages'
 +
categories['edit-full-all-vandalism'] = 'Wikipedia pages protected against vandalism'
  
-- Lazily initialise modules and objects we don't always need.
+
local error_categories = {
local getArgs, makeMessageBox, lang
+
incorrect = 'Wikipedia pages with incorrect protection templates',
 
+
no_expiry = 'Wikipedia protected pages without expiry'
-- Set constants.
+
local CONFIG_MODULE = 'Module:Protection banner/config'
+
 
+
--------------------------------------------------------------------------------
+
-- Helper functions
+
--------------------------------------------------------------------------------
+
 
+
local function makeCategoryLink(cat, sort)
+
local nsText = mw.site.namespaces[14].name
+
if cat and sort then
+
return string.format(
+
'[[%s:%s|%s]]',
+
nsText,
+
cat,
+
sort
+
)
+
elseif cat then
+
return string.format(
+
'[[%s:%s]]',
+
nsText,
+
cat
+
)
+
else
+
return ''
+
end
+
end
+
 
+
-- Validation function for the expiry and the protection date
+
local function validateDate(dateString, dateType)
+
lang = lang or mw.language.getContentLanguage()
+
local success, result = pcall(lang.formatDate, lang, 'U', dateString)
+
if success then
+
result = tonumber(result)
+
if result then
+
return result
+
end
+
end
+
error(string.format(
+
'invalid %s ("%s")',
+
dateType,
+
tostring(dateString)
+
), 4)
+
end
+
 
+
local function makeFullUrl(page, query, display)
+
return string.format(
+
'[%s %s]',
+
tostring(mw.uri.fullUrl(page, query)),
+
display
+
)
+
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
+
 
+
local function walkHierarchy(hierarchy, start)
+
local toWalk, retval = {[start] = true}, {}
+
while true do
+
-- Can't use pairs() since we're adding and removing things as we're iterating
+
local k = next(toWalk) -- This always gets the "first" key
+
if k == nil then break end
+
toWalk[k] = nil
+
retval[k] = true
+
for _,v in ipairs(hierarchy[k]) do
+
if not retval[v] then
+
toWalk[v] = true
+
end
+
end
+
end
+
return retval
+
end
+
 
+
--------------------------------------------------------------------------------
+
-- Protection class
+
--------------------------------------------------------------------------------
+
 
+
local Protection = {}
+
Protection.__index = Protection
+
 
+
Protection.supportedActions = {
+
edit = true,
+
move = true,
+
autoreview = true,
+
upload = true
+
 
}
 
}
  
Protection.bannerConfigFields = {
+
local template_types = {
'text',
+
vandalism = {
'explanation',
+
namespaces = false, -- defaults to all namespaces
'tooltip',
+
editLevels = false, -- all edit protection levels
'alt',
+
moveLevels = false, -- all move protection levels
'link',
+
createLevels = false, -- all create protection levels
'image'
+
header = false,
}
+
reason = 'due to [[Wikipedia:Vandalism|vandalism]]',
 
+
icon_reason = 'due to vandalism',
function Protection.new(args, cfg, title)
+
local obj = {}
+
obj._cfg = cfg
+
obj.title = title or mw.title.getCurrentTitle()
+
 
+
-- Set action
+
if not args.action then
+
obj.action = 'edit'
+
elseif Protection.supportedActions[args.action] then
+
obj.action = args.action
+
else
+
error(string.format(
+
'invalid action ("%s")',
+
tostring(args.action)
+
), 3)
+
end
+
 
+
-- Set level
+
obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)
+
if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then
+
-- Users need to be autoconfirmed to move pages anyway, so treat
+
-- semi-move-protected pages as unprotected.
+
obj.level = '*'
+
end
+
 
+
-- Set expiry
+
local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)
+
if effectiveExpiry == 'infinity' then
+
obj.expiry = 'indef'
+
elseif effectiveExpiry ~= 'unknown' then
+
obj.expiry = validateDate(effectiveExpiry, 'expiry date')
+
elseif args.expiry then
+
if cfg.indefStrings[args.expiry] then
+
obj.expiry = 'indef'
+
elseif type(args.expiry) == 'number' then
+
obj.expiry = args.expiry
+
else
+
obj.expiry = validateDate(args.expiry, 'expiry date')
+
end
+
end
+
 
+
-- Set reason
+
if args[1] then
+
obj.reason = mw.ustring.lower(args[1])
+
if obj.reason:find('|') then
+
error('reasons cannot contain the pipe character ("|")', 3)
+
end
+
end
+
 
+
-- Set protection date
+
if args.date then
+
obj.protectionDate = validateDate(args.date, 'protection date')
+
end
+
+
-- Set banner config
+
do
+
obj.bannerConfig = {}
+
local configTables = {}
+
if cfg.banners[obj.action] then
+
configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]
+
end
+
if cfg.defaultBanners[obj.action] then
+
configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]
+
configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default
+
end
+
configTables[#configTables + 1] = cfg.masterBanner
+
for i, field in ipairs(Protection.bannerConfigFields) do
+
for j, t in ipairs(configTables) do
+
if t[field] then
+
obj.bannerConfig[field] = t[field]
+
break
+
end
+
end
+
end
+
end
+
return setmetatable(obj, Protection)
+
end
+
 
+
function Protection:isProtected()
+
return self.level ~= '*'
+
end
+
 
+
function Protection:isTemporary()
+
return type(self.expiry) == 'number'
+
end
+
 
+
function Protection:makeProtectionCategory()
+
local cfg = self._cfg
+
local title = self.title
+
+
-- Exit if the page is not protected.
+
if not self:isProtected() then
+
return ''
+
end
+
+
-- Get the expiry key fragment.
+
local expiryFragment
+
if self.expiry == 'indef' then
+
expiryFragment = self.expiry
+
elseif type(self.expiry) == 'number' then
+
expiryFragment = 'temp'
+
end
+
 
+
-- Get the namespace key fragment.
+
local namespaceFragment
+
do
+
namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]
+
if not namespaceFragment and title.namespace % 2 == 1 then
+
namespaceFragment = 'talk'
+
end
+
end
+
+
-- Define the order that key fragments are tested in. This is done with an
+
-- array of tables containing the value to be tested, along with its
+
-- position in the cfg.protectionCategories table.
+
local order = {
+
{val = expiryFragment,    keypos = 1},
+
{val = namespaceFragment, keypos = 2},
+
{val = self.reason,      keypos = 3},
+
{val = self.level,        keypos = 4},
+
{val = self.action,      keypos = 5}
+
 
}
 
}
 
--[[
 
-- The old protection templates used an ad-hoc protection category system,
 
-- with some templates prioritising namespaces in their categories, and
 
-- others prioritising the protection reason. To emulate this in this module
 
-- we use the config table cfg.reasonsWithNamespacePriority to set the
 
-- reasons for which namespaces have priority over protection reason.
 
-- If we are dealing with one of those reasons, move the namespace table to
 
-- the end of the order table, i.e. give it highest priority. If not, the
 
-- reason should have highest priority, so move that to the end of the table
 
-- instead.
 
--]]
 
if self.reason and cfg.reasonsWithNamespacePriority[self.reason] then
 
-- table.insert(order, 3, table.remove(order, 2))
 
toTableEnd(order, 2)
 
else
 
toTableEnd(order, 3)
 
end
 
 
--[[
 
-- Define the attempt order. Inactive subtables (subtables with nil "value"
 
-- fields) are moved to the end, where they will later be given the key
 
-- "all". This is to cut down on the number of table lookups in
 
-- cfg.protectionCategories, which grows exponentially with the number of
 
-- non-nil keys. We keep track of the number of active subtables with the
 
-- noActive parameter.
 
--]]
 
local noActive, attemptOrder
 
do
 
local active, inactive = {}, {}
 
for i, t in ipairs(order) do
 
if t.val then
 
active[#active + 1] = t
 
else
 
inactive[#inactive + 1] = t
 
end
 
end
 
noActive = #active
 
attemptOrder = active
 
for i, t in ipairs(inactive) do
 
attemptOrder[#attemptOrder + 1] = t
 
end
 
end
 
 
--[[
 
-- Check increasingly generic key combinations until we find a match. If a
 
-- specific category exists for the combination of key fragments we are
 
-- given, that match will be found first. If not, we keep trying different
 
-- key fragment combinations until we match using the key
 
-- "all-all-all-all-all".
 
--
 
-- To generate the keys, we index the key subtables using a binary matrix
 
-- with indexes i and j. j is only calculated up to the number of active
 
-- subtables. For example, if there were three active subtables, the matrix
 
-- would look like this, with 0 corresponding to the key fragment "all", and
 
-- 1 corresponding to other key fragments.
 
--
 
--  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 subtables are set
 
-- to the string "all".
 
--
 
-- A key for cfg.protectionCategories is constructed for each value of i.
 
-- The position of the value in the key is determined by the keypos field in
 
-- each subtable.
 
--]]
 
local cats = cfg.protectionCategories
 
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 makeCategoryLink(attempt, title.text)
 
end
 
end
 
return ''
 
end
 
 
function Protection:needsExpiry()
 
local cfg = self._cfg
 
local actionNeedsCheck = cfg.expiryCheckActions[self.action]
 
return not self.expiry and (
 
actionNeedsCheck or (
 
actionNeedsCheck == nil
 
and self.reason -- the old {{pp-protected}} didn't check for expiry
 
and not cfg.reasonsWithoutExpiryCheck[self.reason]
 
)
 
)
 
end
 
 
function Protection:isIncorrect()
 
local expiry = self.expiry
 
return not self:isProtected()
 
or type(expiry) == 'number' and expiry < os.time()
 
end
 
 
function Protection:isTemplateProtectedNonTemplate()
 
local action, namespace = self.action, self.title.namespace
 
return self.level == 'templateeditor'
 
and (
 
(action ~= 'edit' and action ~= 'move')
 
or (namespace ~= 10 and namespace ~= 828)
 
)
 
end
 
 
function Protection:makeCategoryLinks()
 
local msg = self._cfg.msg
 
local ret = { self:makeProtectionCategory() }
 
if self:needsExpiry() then
 
ret[#ret + 1] = makeCategoryLink(
 
msg['tracking-category-expiry'],
 
self.title.text
 
)
 
end
 
if self:isIncorrect() then
 
ret[#ret + 1] = makeCategoryLink(
 
msg['tracking-category-incorrect'],
 
self.title.text
 
)
 
end
 
if self:isTemplateProtectedNonTemplate() then
 
ret[#ret + 1] = makeCategoryLink(
 
msg['tracking-category-template'],
 
self.title.text
 
)
 
end
 
return table.concat(ret)
 
end
 
 
--------------------------------------------------------------------------------
 
-- Blurb class
 
--------------------------------------------------------------------------------
 
 
local Blurb = {}
 
Blurb.__index = Blurb
 
 
Blurb.bannerTextFields = {
 
text = true,
 
explanation = true,
 
tooltip = true,
 
alt = true,
 
link = true
 
 
}
 
}
 
function Blurb.new(protectionObj, args, cfg)
 
return setmetatable({
 
_cfg = cfg,
 
_protectionObj = protectionObj,
 
_args = args
 
}, Blurb)
 
end
 
 
-- Private methods --
 
 
function Blurb:_formatDate(num)
 
-- Formats a Unix timestamp into dd Month, YYYY format.
 
lang = lang or mw.language.getContentLanguage()
 
local success, date = pcall(
 
lang.formatDate,
 
lang,
 
self._cfg.msg['expiry-date-format'] or 'j F Y',
 
'@' .. tostring(num)
 
)
 
if success then
 
return date
 
end
 
end
 
 
function Blurb:_getExpandedMessage(msgKey)
 
return self:_substituteParameters(self._cfg.msg[msgKey])
 
end
 
 
function Blurb:_substituteParameters(msg)
 
if not self._params then
 
local parameterFuncs = {}
 
 
parameterFuncs.CURRENTVERSION    = self._makeCurrentVersionParameter
 
parameterFuncs.EDITREQUEST        = self._makeEditRequestParameter
 
parameterFuncs.EXPIRY            = self._makeExpiryParameter
 
parameterFuncs.EXPLANATIONBLURB  = self._makeExplanationBlurbParameter
 
parameterFuncs.IMAGELINK          = self._makeImageLinkParameter
 
parameterFuncs.INTROBLURB        = self._makeIntroBlurbParameter
 
parameterFuncs.INTROFRAGMENT      = self._makeIntroFragmentParameter
 
parameterFuncs.PAGETYPE          = self._makePagetypeParameter
 
parameterFuncs.PROTECTIONBLURB    = self._makeProtectionBlurbParameter
 
parameterFuncs.PROTECTIONDATE    = self._makeProtectionDateParameter
 
parameterFuncs.PROTECTIONLEVEL    = self._makeProtectionLevelParameter
 
parameterFuncs.PROTECTIONLOG      = self._makeProtectionLogParameter
 
parameterFuncs.TALKPAGE          = self._makeTalkPageParameter
 
parameterFuncs.TOOLTIPBLURB      = self._makeTooltipBlurbParameter
 
parameterFuncs.TOOLTIPFRAGMENT    = self._makeTooltipFragmentParameter
 
parameterFuncs.VANDAL            = self._makeVandalTemplateParameter
 
 
self._params = setmetatable({}, {
 
__index = function (t, k)
 
local param
 
if parameterFuncs[k] then
 
param = parameterFuncs[k](self)
 
end
 
param = param or ''
 
t[k] = param
 
return param
 
end
 
})
 
end
 
 
msg = msg:gsub('${(%u+)}', self._params)
 
return msg
 
end
 
 
function Blurb:_makeCurrentVersionParameter()
 
-- A link to the page history or the move log, depending on the kind of
 
-- protection.
 
local pagename = self._protectionObj.title.prefixedText
 
if self._protectionObj.action == 'move' then
 
-- We need the move log link.
 
return makeFullUrl(
 
'Special:Log',
 
{type = 'move', page = pagename},
 
self:_getExpandedMessage('current-version-move-display')
 
)
 
else
 
-- We need the history link.
 
return makeFullUrl(
 
pagename,
 
{action = 'history'},
 
self:_getExpandedMessage('current-version-edit-display')
 
)
 
end
 
end
 
 
function Blurb:_makeEditRequestParameter()
 
local mEditRequest = require('Module:Submit an edit request')
 
local action = self._protectionObj.action
 
local level = self._protectionObj.level
 
 
-- Get the edit request type.
 
local requestType
 
if action == 'edit' then
 
if level == 'autoconfirmed' then
 
requestType = 'semi'
 
elseif level == 'templateeditor' then
 
requestType = 'template'
 
end
 
end
 
requestType = requestType or 'full'
 
 
-- Get the display value.
 
local display = self:_getExpandedMessage('edit-request-display')
 
 
return mEditRequest._link{type = requestType, display = display}
 
end
 
 
function Blurb:_makeExpiryParameter()
 
local expiry = self._protectionObj.expiry
 
if type(expiry) == 'number' then
 
return self:_formatDate(expiry)
 
else
 
return expiry
 
end
 
end
 
 
function Blurb:_makeExplanationBlurbParameter()
 
-- Cover special cases first.
 
if self._protectionObj.title.namespace == 8 then
 
-- MediaWiki namespace
 
return self:_getExpandedMessage('explanation-blurb-nounprotect')
 
end
 
 
-- Get explanation blurb table keys
 
local action = self._protectionObj.action
 
local level = self._protectionObj.level
 
local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject'
 
 
-- Find the message in the explanation blurb table and substitute any
 
-- parameters.
 
local explanations = self._cfg.explanationBlurbs
 
local msg
 
if explanations[action][level] and explanations[action][level][talkKey] then
 
msg = explanations[action][level][talkKey]
 
elseif explanations[action][level] and explanations[action][level].default then
 
msg = explanations[action][level].default
 
elseif explanations[action].default and explanations[action].default[talkKey] then
 
msg = explanations[action].default[talkKey]
 
elseif explanations[action].default and explanations[action].default.default then
 
msg = explanations[action].default.default
 
else
 
error(string.format(
 
'could not find explanation blurb for action "%s", level "%s" and talk key "%s"',
 
action,
 
level,
 
talkKey
 
), 8)
 
end
 
return self:_substituteParameters(msg)
 
end
 
 
function Blurb:_makeImageLinkParameter()
 
local imageLinks = self._cfg.imageLinks
 
local action = self._protectionObj.action
 
local level = self._protectionObj.level
 
local msg
 
if imageLinks[action][level] then
 
msg = imageLinks[action][level]
 
elseif imageLinks[action].default then
 
msg = imageLinks[action].default
 
else
 
msg = imageLinks.edit.default
 
end
 
return self:_substituteParameters(msg)
 
end
 
 
function Blurb:_makeIntroBlurbParameter()
 
if self._protectionObj:isTemporary() then
 
return self:_getExpandedMessage('intro-blurb-expiry')
 
else
 
return self:_getExpandedMessage('intro-blurb-noexpiry')
 
end
 
end
 
 
function Blurb:_makeIntroFragmentParameter()
 
if self._protectionObj:isTemporary() then
 
return self:_getExpandedMessage('intro-fragment-expiry')
 
else
 
return self:_getExpandedMessage('intro-fragment-noexpiry')
 
end
 
end
 
 
function Blurb:_makePagetypeParameter()
 
local pagetypes = self._cfg.pagetypes
 
return pagetypes[self._protectionObj.title.namespace]
 
or pagetypes.default
 
or error('no default pagetype defined', 8)
 
end
 
 
function Blurb:_makeProtectionBlurbParameter()
 
local protectionBlurbs = self._cfg.protectionBlurbs
 
local action = self._protectionObj.action
 
local level = self._protectionObj.level
 
local msg
 
if protectionBlurbs[action][level] then
 
msg = protectionBlurbs[action][level]
 
elseif protectionBlurbs[action].default then
 
msg = protectionBlurbs[action].default
 
elseif protectionBlurbs.edit.default then
 
msg = protectionBlurbs.edit.default
 
else
 
error('no protection blurb defined for protectionBlurbs.edit.default', 8)
 
end
 
return self:_substituteParameters(msg)
 
end
 
 
function Blurb:_makeProtectionDateParameter()
 
local protectionDate = self._protectionObj.protectionDate
 
if type(protectionDate) == 'number' then
 
return self:_formatDate(protectionDate)
 
else
 
return protectionDate
 
end
 
end
 
 
function Blurb:_makeProtectionLevelParameter()
 
local protectionLevels = self._cfg.protectionLevels
 
local action = self._protectionObj.action
 
local level = self._protectionObj.level
 
local msg
 
if protectionLevels[action][level] then
 
msg = protectionLevels[action][level]
 
elseif protectionLevels[action].default then
 
msg = protectionLevels[action].default
 
elseif protectionLevels.edit.default then
 
msg = protectionLevels.edit.default
 
else
 
error('no protection level defined for protectionLevels.edit.default', 8)
 
end
 
return self:_substituteParameters(msg)
 
end
 
 
function Blurb:_makeProtectionLogParameter()
 
local pagename = self._protectionObj.title.prefixedText
 
if self._protectionObj.action == 'autoreview' then
 
-- We need the pending changes log.
 
return makeFullUrl(
 
'Special:Log',
 
{type = 'stable', page = pagename},
 
self:_getExpandedMessage('pc-log-display')
 
)
 
else
 
-- We need the protection log.
 
return makeFullUrl(
 
'Special:Log',
 
{type = 'protect', page = pagename},
 
self:_getExpandedMessage('protection-log-display')
 
)
 
end
 
end
 
 
function Blurb:_makeTalkPageParameter()
 
return string.format(
 
'[[%s:%s#%s|%s]]',
 
mw.site.namespaces[self._protectionObj.title.namespace].talk.name,
 
self._protectionObj.title.text,
 
self._args.section or 'top',
 
self:_getExpandedMessage('talk-page-link-display')
 
)
 
end
 
 
function Blurb:_makeTooltipBlurbParameter()
 
if self._protectionObj:isTemporary() then
 
return self:_getExpandedMessage('tooltip-blurb-expiry')
 
else
 
return self:_getExpandedMessage('tooltip-blurb-noexpiry')
 
end
 
end
 
 
function Blurb:_makeTooltipFragmentParameter()
 
if self._protectionObj:isTemporary() then
 
return self:_getExpandedMessage('tooltip-fragment-expiry')
 
else
 
return self:_getExpandedMessage('tooltip-fragment-noexpiry')
 
end
 
end
 
 
function Blurb:_makeVandalTemplateParameter()
 
return require('Module:Vandal-m')._main{
 
self._args.user or self._protectionObj.title.baseText
 
}
 
end
 
 
-- Public methods --
 
 
function Blurb:makeBannerText(key)
 
-- Validate input.
 
if not key or not Blurb.bannerTextFields[key] then
 
error(string.format(
 
'"%s" is not a valid banner config field',
 
tostring(key)
 
), 2)
 
end
 
 
-- Generate the text.
 
local msg = self._protectionObj.bannerConfig[key]
 
if type(msg) == 'string' then
 
return self:_substituteParameters(msg)
 
elseif type(msg) == 'function' then
 
msg = msg(self._protectionObj, self._args)
 
if type(msg) ~= 'string' then
 
error(string.format(
 
'bad output from banner config function with key "%s"'
 
.. ' (expected string, got %s)',
 
tostring(key),
 
type(msg)
 
), 4)
 
end
 
return self:_substituteParameters(msg)
 
end
 
end
 
 
--------------------------------------------------------------------------------
 
-- BannerTemplate class
 
--------------------------------------------------------------------------------
 
 
local BannerTemplate = {}
 
BannerTemplate.__index = BannerTemplate
 
 
function BannerTemplate.new(protectionObj, cfg)
 
local obj = {}
 
obj._cfg = cfg
 
 
-- Set the image filename.
 
local imageFilename = protectionObj.bannerConfig.image
 
if imageFilename then
 
obj._imageFilename = imageFilename
 
else
 
-- If an image filename isn't specified explicitly in the banner config,
 
-- generate it from the protection status and the namespace.
 
local action = protectionObj.action
 
local level = protectionObj.level
 
local namespace = protectionObj.title.namespace
 
local reason = protectionObj.reason
 
 
-- Deal with special cases first.
 
if (
 
namespace == 10
 
or namespace == 828
 
or reason and obj._cfg.indefImageReasons[reason]
 
)
 
and action == 'edit'
 
and level == 'sysop'
 
and not protectionObj:isTemporary()
 
then
 
-- Fully protected modules and templates get the special red "indef"
 
-- padlock.
 
obj._imageFilename = obj._cfg.msg['image-filename-indef']
 
else
 
-- Deal with regular protection types.
 
local images = obj._cfg.images
 
if images[action] then
 
if images[action][level] then
 
obj._imageFilename = images[action][level]
 
elseif images[action].default then
 
obj._imageFilename = images[action].default
 
end
 
end
 
end
 
end
 
return setmetatable(obj, BannerTemplate)
 
end
 
 
function BannerTemplate:setImageWidth(width)
 
self._imageWidth = width
 
end
 
 
function BannerTemplate:setImageTooltip(tooltip)
 
self._imageCaption = tooltip
 
end
 
 
function BannerTemplate:renderImage()
 
local filename = self._imageFilename
 
or self._cfg.msg['image-filename-default']
 
or 'Transparent.gif'
 
return makeFileLink{
 
file = filename,
 
size = (self._imageWidth or 20) .. 'px',
 
alt = self._imageAlt,
 
link = self._imageLink,
 
caption = self._imageCaption
 
}
 
end
 
 
--------------------------------------------------------------------------------
 
-- Banner class
 
--------------------------------------------------------------------------------
 
 
local Banner = setmetatable({}, BannerTemplate)
 
Banner.__index = Banner
 
 
function Banner.new(protectionObj, blurbObj, cfg)
 
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
 
obj:setImageWidth(40)
 
obj:setImageTooltip(blurbObj:makeBannerText('alt')) -- Large banners use the alt text for the tooltip.
 
obj._reasonText = blurbObj:makeBannerText('text')
 
obj._explanationText = blurbObj:makeBannerText('explanation')
 
obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.
 
return setmetatable(obj, Banner)
 
end
 
 
function Banner:__tostring()
 
-- Renders the banner.
 
makeMessageBox = makeMessageBox or require('Module:Message box').main
 
local reasonText = self._reasonText or error('no reason text set', 2)
 
local explanationText = self._explanationText
 
local mbargs = {
 
page = self._page,
 
type = 'protection',
 
image = self:renderImage(),
 
text = string.format(
 
"'''%s'''%s",
 
reasonText,
 
explanationText and '<br />' .. explanationText or ''
 
)
 
}
 
return makeMessageBox('mbox', mbargs)
 
end
 
 
--------------------------------------------------------------------------------
 
-- Padlock class
 
--------------------------------------------------------------------------------
 
 
local Padlock = setmetatable({}, BannerTemplate)
 
Padlock.__index = Padlock
 
 
function Padlock.new(protectionObj, blurbObj, cfg)
 
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
 
obj:setImageWidth(20)
 
obj:setImageTooltip(blurbObj:makeBannerText('tooltip'))
 
obj._imageAlt = blurbObj:makeBannerText('alt')
 
obj._imageLink = blurbObj:makeBannerText('link')
 
obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action]
 
or cfg.padlockIndicatorNames.default
 
or 'pp-default'
 
return setmetatable(obj, Padlock)
 
end
 
 
function Padlock:__tostring()
 
local frame = mw.getCurrentFrame()
 
-- The nowiki tag helps prevent whitespace at the top of articles.
 
local nowiki = frame:extensionTag{name = 'nowiki'}
 
local indicator = frame:extensionTag{
 
name = 'indicator',
 
args = {name = self._indicatorName},
 
content = self:renderImage()
 
}
 
return nowiki .. indicator
 
end
 
 
--------------------------------------------------------------------------------
 
-- Exports
 
--------------------------------------------------------------------------------
 
 
local p = {}
 
 
function p._exportClasses()
 
-- This is used for testing purposes.
 
return {
 
Protection = Protection,
 
Blurb = Blurb,
 
BannerTemplate = BannerTemplate,
 
Banner = Banner,
 
Padlock = Padlock,
 
}
 
end
 
 
function p._main(args, cfg, title)
 
args = args or {}
 
cfg = cfg or require(CONFIG_MODULE)
 
 
local protectionObj = Protection.new(args, cfg, title)
 
 
local ret = {}
 
 
-- If a page's edit protection is equally or more restrictive than its
 
-- protection from some other action, then don't bother displaying anything
 
-- for the other action (except categories).
 
if protectionObj.action == 'edit' or
 
args.demolevel or
 
not walkHierarchy(
 
cfg.hierarchy,
 
protectionObj.level
 
)[effectiveProtectionLevel('edit', protectionObj.title)]
 
then
 
-- Initialise the blurb object
 
local blurbObj = Blurb.new(protectionObj, args, cfg)
 
 
-- Render the banner
 
if protectionObj:isProtected() then
 
ret[#ret + 1] = tostring(
 
(yesno(args.small) and Padlock or Banner)
 
.new(protectionObj, blurbObj, cfg)
 
)
 
end
 
end
 
 
-- Render the categories
 
if yesno(args.category) ~= false then
 
ret[#ret + 1] = protectionObj:makeCategoryLinks()
 
end
 
 
return table.concat(ret)
 
end
 
 
function p.main(frame, cfg)
 
cfg = cfg or require(CONFIG_MODULE)
 
 
-- Find default args, if any.
 
local parent = frame.getParent and frame:getParent()
 
local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')]
 
 
-- Find user args, and use the parent frame if we are being called from a
 
-- wrapper template.
 
getArgs = getArgs or require('Module:Arguments').getArgs
 
local userArgs = getArgs(frame, {
 
parentOnly = defaultArgs,
 
frameOnly = not defaultArgs
 
})
 
 
-- Build the args table. User-specified args overwrite default args.
 
local args = {}
 
for k, v in pairs(defaultArgs or {}) do
 
args[k] = v
 
end
 
for k, v in pairs(userArgs) do
 
args[k] = v
 
end
 
return p._main(args, cfg)
 
end
 
 
return p
 

Revision as of 04:58, 25 February 2014

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

Script error: Lua error: Internal error: Error reading from pipe.

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

local categories = {}
-- The naming scheme is: protection type - protection level - namespace - reason
categories['edit-semi-user-all'] = 'Wikipedia semi-protected user and user talk pages'
categories['edit-semi-project-all'] = 'Semi-protected project pages'
categories['edit-semi-file-all'] = 'Semi-protected images'
categories['edit-semi-template-all'] = 'Wikipedia semi-protected templates'
categories['edit-semi-portal-all'] = 'Semi-protected portals'
categories['edit-semi-talk-all'] = 'Semi-protected talk pages'
categories['edit-semi-other-vandalism'] 'Wikipedia pages semi-protected against vandalism'
categories['edit-full-user-all'] = 'Wikipedia protected user and user talk pages'
categories['edit-full-file-all'] = 'Protected images'
categories['edit-full-project-all'] = 'Protected project pages'
categories['edit-full-template-all'] = 'Wikipedia protected templates'
categories['edit-full-talk-all'] = 'Protected talk pages'
categories['edit-full-all-vandalism'] = 'Wikipedia pages protected against vandalism'

local error_categories = {
	incorrect = 'Wikipedia pages with incorrect protection templates',
	no_expiry = 'Wikipedia protected pages without expiry'
}

local template_types = {
	vandalism = {
		namespaces = false, -- defaults to all namespaces
		editLevels = false, -- all edit protection levels
		moveLevels = false, -- all move protection levels
		createLevels = false, -- all create protection levels
		header = false,
		reason = 'due to [[Wikipedia:Vandalism|vandalism]]',
		icon_reason = 'due to vandalism',
	}
}