http://tinlizzie.org/IA/index.php?title=Module:Protection_banner&feed=atom&action=history
Module:Protection banner - Revision history
2024-03-28T22:28:22Z
Revision history for this page on the wiki
MediaWiki 1.29.1
http://tinlizzie.org/IA/index.php?title=Module:Protection_banner&diff=691&oldid=prev
Ohshima: 1 revision imported
2017-11-30T04:45:08Z
<p>1 revision imported</p>
<table class="diff diff-contentalign-left" data-mw="interface">
<tr style='vertical-align: top;' lang='en'>
<td colspan='1' style="background-color: white; color:black; text-align: center;">← Older revision</td>
<td colspan='1' style="background-color: white; color:black; text-align: center;">Revision as of 04:45, 30 November 2017</td>
</tr><tr><td colspan='2' style='text-align: center;' lang='en'><div class="mw-diff-empty">(No difference)</div>
</td></tr></table>
Ohshima
http://tinlizzie.org/IA/index.php?title=Module:Protection_banner&diff=690&oldid=prev
Cenarium: remove expiry param (now also automatically retrieved for autoreview) and needsexpiry check
2016-09-29T18:32:41Z
<p>remove expiry param (now also automatically retrieved for autoreview) and needsexpiry check</p>
<p><b>New page</b></p><div>-- This module implements {{pp-meta}} and its daughter templates such as<br />
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.<br />
<br />
-- Initialise necessary modules.<br />
require('Module:No globals')<br />
local makeFileLink = require('Module:File link')._main<br />
local effectiveProtectionLevel = require('Module:Effective protection level')._main<br />
local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main<br />
local yesno = require('Module:Yesno')<br />
<br />
-- Lazily initialise modules and objects we don't always need.<br />
local getArgs, makeMessageBox, lang<br />
<br />
-- Set constants.<br />
local CONFIG_MODULE = 'Module:Protection banner/config'<br />
<br />
--------------------------------------------------------------------------------<br />
-- Helper functions<br />
--------------------------------------------------------------------------------<br />
<br />
local function makeCategoryLink(cat, sort)<br />
if cat then<br />
return string.format(<br />
'[[%s:%s|%s]]',<br />
mw.site.namespaces[14].name,<br />
cat,<br />
sort<br />
)<br />
end<br />
end<br />
<br />
-- Validation function for the expiry and the protection date<br />
local function validateDate(dateString, dateType)<br />
if not lang then<br />
lang = mw.language.getContentLanguage()<br />
end<br />
local success, result = pcall(lang.formatDate, lang, 'U', dateString)<br />
if success then<br />
result = tonumber(result)<br />
if result then<br />
return result<br />
end<br />
end<br />
error(string.format(<br />
'invalid %s: %s',<br />
dateType,<br />
tostring(dateString)<br />
), 4)<br />
end<br />
<br />
local function makeFullUrl(page, query, display)<br />
return string.format(<br />
'[%s %s]',<br />
tostring(mw.uri.fullUrl(page, query)),<br />
display<br />
)<br />
end<br />
<br />
-- Given a directed graph formatted as node -> table of direct successors,<br />
-- get a table of all nodes reachable from a given node (though always<br />
-- including the given node).<br />
local function getReachableNodes(graph, start)<br />
local toWalk, retval = {[start] = true}, {}<br />
while true do<br />
-- Can't use pairs() since we're adding and removing things as we're iterating<br />
local k = next(toWalk) -- This always gets the "first" key<br />
if k == nil then<br />
return retval<br />
end<br />
toWalk[k] = nil<br />
retval[k] = true<br />
for _,v in ipairs(graph[k]) do<br />
if not retval[v] then<br />
toWalk[v] = true<br />
end<br />
end<br />
end<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Protection class<br />
--------------------------------------------------------------------------------<br />
<br />
local Protection = {}<br />
Protection.__index = Protection<br />
<br />
Protection.supportedActions = {<br />
edit = true,<br />
move = true,<br />
autoreview = true,<br />
upload = true<br />
}<br />
<br />
Protection.bannerConfigFields = {<br />
'text',<br />
'explanation',<br />
'tooltip',<br />
'alt',<br />
'link',<br />
'image'<br />
}<br />
<br />
function Protection.new(args, cfg, title)<br />
local obj = {}<br />
obj._cfg = cfg<br />
obj.title = title or mw.title.getCurrentTitle()<br />
<br />
-- Set action<br />
if not args.action then<br />
obj.action = 'edit'<br />
elseif Protection.supportedActions[args.action] then<br />
obj.action = args.action<br />
else<br />
error(string.format(<br />
'invalid action: %s',<br />
tostring(args.action)<br />
), 3)<br />
end<br />
<br />
-- Set level<br />
obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)<br />
if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then<br />
-- Users need to be autoconfirmed to move pages anyway, so treat<br />
-- semi-move-protected pages as unprotected.<br />
obj.level = '*'<br />
end<br />
<br />
-- Set expiry<br />
local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)<br />
if effectiveExpiry == 'infinity' then<br />
obj.expiry = 'indef'<br />
elseif effectiveExpiry ~= 'unknown' then<br />
obj.expiry = validateDate(effectiveExpiry, 'expiry date')<br />
end<br />
<br />
-- Set reason<br />
if args[1] then<br />
obj.reason = mw.ustring.lower(args[1])<br />
if obj.reason:find('|') then<br />
error('reasons cannot contain the pipe character ("|")', 3)<br />
end<br />
end<br />
<br />
-- Set protection date<br />
if args.date then<br />
obj.protectionDate = validateDate(args.date, 'protection date')<br />
end<br />
<br />
-- Set banner config<br />
do<br />
obj.bannerConfig = {}<br />
local configTables = {}<br />
if cfg.banners[obj.action] then<br />
configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]<br />
end<br />
if cfg.defaultBanners[obj.action] then<br />
configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]<br />
configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default<br />
end<br />
configTables[#configTables + 1] = cfg.masterBanner<br />
for i, field in ipairs(Protection.bannerConfigFields) do<br />
for j, t in ipairs(configTables) do<br />
if t[field] then<br />
obj.bannerConfig[field] = t[field]<br />
break<br />
end<br />
end<br />
end<br />
end<br />
return setmetatable(obj, Protection)<br />
end<br />
<br />
function Protection:isProtected()<br />
return self.level ~= '*'<br />
end<br />
<br />
function Protection:isTemporary()<br />
return type(self.expiry) == 'number'<br />
end<br />
<br />
function Protection:makeProtectionCategory()<br />
local cfg = self._cfg<br />
local title = self.title<br />
<br />
-- Exit if the page is not protected.<br />
if not self:isProtected() then<br />
return ''<br />
end<br />
<br />
-- Get the expiry key fragment.<br />
local expiryFragment<br />
if self.expiry == 'indef' then<br />
expiryFragment = self.expiry<br />
elseif type(self.expiry) == 'number' then<br />
expiryFragment = 'temp'<br />
end<br />
<br />
-- Get the namespace key fragment.<br />
local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]<br />
if not namespaceFragment and title.namespace % 2 == 1 then<br />
namespaceFragment = 'talk'<br />
end<br />
<br />
-- Define the order that key fragments are tested in. This is done with an<br />
-- array of tables containing the value to be tested, along with its<br />
-- position in the cfg.protectionCategories table.<br />
local order = {<br />
{val = expiryFragment, keypos = 1},<br />
{val = namespaceFragment, keypos = 2},<br />
{val = self.reason, keypos = 3},<br />
{val = self.level, keypos = 4},<br />
{val = self.action, keypos = 5}<br />
}<br />
<br />
--[[<br />
-- The old protection templates used an ad-hoc protection category system,<br />
-- with some templates prioritising namespaces in their categories, and<br />
-- others prioritising the protection reason. To emulate this in this module<br />
-- we use the config table cfg.reasonsWithNamespacePriority to set the<br />
-- reasons for which namespaces have priority over protection reason.<br />
-- If we are dealing with one of those reasons, move the namespace table to<br />
-- the end of the order table, i.e. give it highest priority. If not, the<br />
-- reason should have highest priority, so move that to the end of the table<br />
-- instead.<br />
--]]<br />
table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3))<br />
<br />
--[[<br />
-- Define the attempt order. Inactive subtables (subtables with nil "value"<br />
-- fields) are moved to the end, where they will later be given the key<br />
-- "all". This is to cut down on the number of table lookups in<br />
-- cfg.protectionCategories, which grows exponentially with the number of<br />
-- non-nil keys. We keep track of the number of active subtables with the<br />
-- noActive parameter.<br />
--]]<br />
local noActive, attemptOrder<br />
do<br />
local active, inactive = {}, {}<br />
for i, t in ipairs(order) do<br />
if t.val then<br />
active[#active + 1] = t<br />
else<br />
inactive[#inactive + 1] = t<br />
end<br />
end<br />
noActive = #active<br />
attemptOrder = active<br />
for i, t in ipairs(inactive) do<br />
attemptOrder[#attemptOrder + 1] = t<br />
end<br />
end<br />
<br />
--[[<br />
-- Check increasingly generic key combinations until we find a match. If a<br />
-- specific category exists for the combination of key fragments we are<br />
-- given, that match will be found first. If not, we keep trying different<br />
-- key fragment combinations until we match using the key<br />
-- "all-all-all-all-all".<br />
--<br />
-- To generate the keys, we index the key subtables using a binary matrix<br />
-- with indexes i and j. j is only calculated up to the number of active<br />
-- subtables. For example, if there were three active subtables, the matrix<br />
-- would look like this, with 0 corresponding to the key fragment "all", and<br />
-- 1 corresponding to other key fragments.<br />
-- <br />
-- j 1 2 3<br />
-- i <br />
-- 1 1 1 1<br />
-- 2 0 1 1<br />
-- 3 1 0 1<br />
-- 4 0 0 1<br />
-- 5 1 1 0<br />
-- 6 0 1 0<br />
-- 7 1 0 0<br />
-- 8 0 0 0<br />
-- <br />
-- Values of j higher than the number of active subtables are set<br />
-- to the string "all".<br />
--<br />
-- A key for cfg.protectionCategories is constructed for each value of i.<br />
-- The position of the value in the key is determined by the keypos field in<br />
-- each subtable.<br />
--]]<br />
local cats = cfg.protectionCategories<br />
for i = 1, 2^noActive do<br />
local key = {}<br />
for j, t in ipairs(attemptOrder) do<br />
if j > noActive then<br />
key[t.keypos] = 'all'<br />
else<br />
local quotient = i / 2 ^ (j - 1)<br />
quotient = math.ceil(quotient)<br />
if quotient % 2 == 1 then<br />
key[t.keypos] = t.val<br />
else<br />
key[t.keypos] = 'all'<br />
end<br />
end<br />
end<br />
key = table.concat(key, '|')<br />
local attempt = cats[key]<br />
if attempt then<br />
return makeCategoryLink(attempt, title.text)<br />
end<br />
end<br />
return ''<br />
end<br />
<br />
function Protection:isIncorrect()<br />
local expiry = self.expiry<br />
return not self:isProtected()<br />
or type(expiry) == 'number' and expiry < os.time()<br />
end<br />
<br />
function Protection:isTemplateProtectedNonTemplate()<br />
local action, namespace = self.action, self.title.namespace<br />
return self.level == 'templateeditor'<br />
and (<br />
(action ~= 'edit' and action ~= 'move')<br />
or (namespace ~= 10 and namespace ~= 828)<br />
)<br />
end<br />
<br />
function Protection:makeCategoryLinks()<br />
local msg = self._cfg.msg<br />
local ret = { self:makeProtectionCategory() }<br />
if self:isIncorrect() then<br />
ret[#ret + 1] = makeCategoryLink(<br />
msg['tracking-category-incorrect'],<br />
self.title.text<br />
)<br />
end<br />
if self:isTemplateProtectedNonTemplate() then<br />
ret[#ret + 1] = makeCategoryLink(<br />
msg['tracking-category-template'],<br />
self.title.text<br />
)<br />
end<br />
return table.concat(ret)<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Blurb class<br />
--------------------------------------------------------------------------------<br />
<br />
local Blurb = {}<br />
Blurb.__index = Blurb<br />
<br />
Blurb.bannerTextFields = {<br />
text = true,<br />
explanation = true,<br />
tooltip = true,<br />
alt = true,<br />
link = true<br />
}<br />
<br />
function Blurb.new(protectionObj, args, cfg)<br />
return setmetatable({<br />
_cfg = cfg,<br />
_protectionObj = protectionObj,<br />
_args = args<br />
}, Blurb)<br />
end<br />
<br />
-- Private methods --<br />
<br />
function Blurb:_formatDate(num)<br />
-- Formats a Unix timestamp into dd Month, YYYY format.<br />
lang = lang or mw.language.getContentLanguage()<br />
local success, date = pcall(<br />
lang.formatDate,<br />
lang,<br />
self._cfg.msg['expiry-date-format'] or 'j F Y',<br />
'@' .. tostring(num)<br />
)<br />
if success then<br />
return date<br />
end<br />
end<br />
<br />
function Blurb:_getExpandedMessage(msgKey)<br />
return self:_substituteParameters(self._cfg.msg[msgKey])<br />
end<br />
<br />
function Blurb:_substituteParameters(msg)<br />
if not self._params then<br />
local parameterFuncs = {}<br />
<br />
parameterFuncs.CURRENTVERSION = self._makeCurrentVersionParameter<br />
parameterFuncs.EDITREQUEST = self._makeEditRequestParameter<br />
parameterFuncs.EXPIRY = self._makeExpiryParameter<br />
parameterFuncs.EXPLANATIONBLURB = self._makeExplanationBlurbParameter<br />
parameterFuncs.IMAGELINK = self._makeImageLinkParameter<br />
parameterFuncs.INTROBLURB = self._makeIntroBlurbParameter<br />
parameterFuncs.INTROFRAGMENT = self._makeIntroFragmentParameter<br />
parameterFuncs.PAGETYPE = self._makePagetypeParameter<br />
parameterFuncs.PROTECTIONBLURB = self._makeProtectionBlurbParameter<br />
parameterFuncs.PROTECTIONDATE = self._makeProtectionDateParameter<br />
parameterFuncs.PROTECTIONLEVEL = self._makeProtectionLevelParameter<br />
parameterFuncs.PROTECTIONLOG = self._makeProtectionLogParameter<br />
parameterFuncs.TALKPAGE = self._makeTalkPageParameter<br />
parameterFuncs.TOOLTIPBLURB = self._makeTooltipBlurbParameter<br />
parameterFuncs.TOOLTIPFRAGMENT = self._makeTooltipFragmentParameter<br />
parameterFuncs.VANDAL = self._makeVandalTemplateParameter<br />
<br />
self._params = setmetatable({}, {<br />
__index = function (t, k)<br />
local param<br />
if parameterFuncs[k] then<br />
param = parameterFuncs[k](self)<br />
end<br />
param = param or ''<br />
t[k] = param<br />
return param<br />
end<br />
})<br />
end<br />
<br />
msg = msg:gsub('${(%u+)}', self._params)<br />
return msg<br />
end<br />
<br />
function Blurb:_makeCurrentVersionParameter()<br />
-- A link to the page history or the move log, depending on the kind of<br />
-- protection.<br />
local pagename = self._protectionObj.title.prefixedText<br />
if self._protectionObj.action == 'move' then<br />
-- We need the move log link.<br />
return makeFullUrl(<br />
'Special:Log',<br />
{type = 'move', page = pagename},<br />
self:_getExpandedMessage('current-version-move-display')<br />
)<br />
else<br />
-- We need the history link.<br />
return makeFullUrl(<br />
pagename,<br />
{action = 'history'},<br />
self:_getExpandedMessage('current-version-edit-display')<br />
)<br />
end<br />
end<br />
<br />
function Blurb:_makeEditRequestParameter()<br />
local mEditRequest = require('Module:Submit an edit request')<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
<br />
-- Get the edit request type.<br />
local requestType<br />
if action == 'edit' then<br />
if level == 'autoconfirmed' then<br />
requestType = 'semi'<br />
elseif level == 'extendedconfirmed' then<br />
requestType = 'extended'<br />
elseif level == 'templateeditor' then<br />
requestType = 'template'<br />
end<br />
end<br />
requestType = requestType or 'full'<br />
<br />
-- Get the display value.<br />
local display = self:_getExpandedMessage('edit-request-display')<br />
<br />
return mEditRequest._link{type = requestType, display = display}<br />
end<br />
<br />
function Blurb:_makeExpiryParameter()<br />
local expiry = self._protectionObj.expiry<br />
if type(expiry) == 'number' then<br />
return self:_formatDate(expiry)<br />
else<br />
return expiry<br />
end<br />
end<br />
<br />
function Blurb:_makeExplanationBlurbParameter()<br />
-- Cover special cases first.<br />
if self._protectionObj.title.namespace == 8 then<br />
-- MediaWiki namespace<br />
return self:_getExpandedMessage('explanation-blurb-nounprotect')<br />
end<br />
<br />
-- Get explanation blurb table keys<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject'<br />
<br />
-- Find the message in the explanation blurb table and substitute any<br />
-- parameters.<br />
local explanations = self._cfg.explanationBlurbs<br />
local msg<br />
if explanations[action][level] and explanations[action][level][talkKey] then<br />
msg = explanations[action][level][talkKey]<br />
elseif explanations[action][level] and explanations[action][level].default then<br />
msg = explanations[action][level].default<br />
elseif explanations[action].default and explanations[action].default[talkKey] then<br />
msg = explanations[action].default[talkKey]<br />
elseif explanations[action].default and explanations[action].default.default then<br />
msg = explanations[action].default.default<br />
else<br />
error(string.format(<br />
'could not find explanation blurb for action "%s", level "%s" and talk key "%s"',<br />
action,<br />
level,<br />
talkKey<br />
), 8)<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeImageLinkParameter()<br />
local imageLinks = self._cfg.imageLinks<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local msg<br />
if imageLinks[action][level] then<br />
msg = imageLinks[action][level]<br />
elseif imageLinks[action].default then<br />
msg = imageLinks[action].default<br />
else<br />
msg = imageLinks.edit.default<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeIntroBlurbParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('intro-blurb-expiry')<br />
else<br />
return self:_getExpandedMessage('intro-blurb-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makeIntroFragmentParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('intro-fragment-expiry')<br />
else<br />
return self:_getExpandedMessage('intro-fragment-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makePagetypeParameter()<br />
local pagetypes = self._cfg.pagetypes<br />
return pagetypes[self._protectionObj.title.namespace]<br />
or pagetypes.default<br />
or error('no default pagetype defined', 8)<br />
end<br />
<br />
function Blurb:_makeProtectionBlurbParameter()<br />
local protectionBlurbs = self._cfg.protectionBlurbs<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local msg<br />
if protectionBlurbs[action][level] then<br />
msg = protectionBlurbs[action][level]<br />
elseif protectionBlurbs[action].default then<br />
msg = protectionBlurbs[action].default<br />
elseif protectionBlurbs.edit.default then<br />
msg = protectionBlurbs.edit.default<br />
else<br />
error('no protection blurb defined for protectionBlurbs.edit.default', 8)<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeProtectionDateParameter()<br />
local protectionDate = self._protectionObj.protectionDate<br />
if type(protectionDate) == 'number' then<br />
return self:_formatDate(protectionDate)<br />
else<br />
return protectionDate<br />
end<br />
end<br />
<br />
function Blurb:_makeProtectionLevelParameter()<br />
local protectionLevels = self._cfg.protectionLevels<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local msg<br />
if protectionLevels[action][level] then<br />
msg = protectionLevels[action][level]<br />
elseif protectionLevels[action].default then<br />
msg = protectionLevels[action].default<br />
elseif protectionLevels.edit.default then<br />
msg = protectionLevels.edit.default<br />
else<br />
error('no protection level defined for protectionLevels.edit.default', 8)<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeProtectionLogParameter()<br />
local pagename = self._protectionObj.title.prefixedText<br />
if self._protectionObj.action == 'autoreview' then<br />
-- We need the pending changes log.<br />
return makeFullUrl(<br />
'Special:Log',<br />
{type = 'stable', page = pagename},<br />
self:_getExpandedMessage('pc-log-display')<br />
)<br />
else<br />
-- We need the protection log.<br />
return makeFullUrl(<br />
'Special:Log',<br />
{type = 'protect', page = pagename},<br />
self:_getExpandedMessage('protection-log-display')<br />
)<br />
end<br />
end<br />
<br />
function Blurb:_makeTalkPageParameter()<br />
return string.format(<br />
'[[%s:%s#%s|%s]]',<br />
mw.site.namespaces[self._protectionObj.title.namespace].talk.name,<br />
self._protectionObj.title.text,<br />
self._args.section or 'top',<br />
self:_getExpandedMessage('talk-page-link-display')<br />
)<br />
end<br />
<br />
function Blurb:_makeTooltipBlurbParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('tooltip-blurb-expiry')<br />
else<br />
return self:_getExpandedMessage('tooltip-blurb-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makeTooltipFragmentParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('tooltip-fragment-expiry')<br />
else<br />
return self:_getExpandedMessage('tooltip-fragment-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makeVandalTemplateParameter()<br />
return require('Module:Vandal-m')._main{<br />
self._args.user or self._protectionObj.title.baseText<br />
}<br />
end<br />
<br />
-- Public methods --<br />
<br />
function Blurb:makeBannerText(key)<br />
-- Validate input.<br />
if not key or not Blurb.bannerTextFields[key] then<br />
error(string.format(<br />
'"%s" is not a valid banner config field',<br />
tostring(key)<br />
), 2)<br />
end<br />
<br />
-- Generate the text.<br />
local msg = self._protectionObj.bannerConfig[key]<br />
if type(msg) == 'string' then<br />
return self:_substituteParameters(msg)<br />
elseif type(msg) == 'function' then<br />
msg = msg(self._protectionObj, self._args)<br />
if type(msg) ~= 'string' then<br />
error(string.format(<br />
'bad output from banner config function with key "%s"'<br />
.. ' (expected string, got %s)',<br />
tostring(key),<br />
type(msg)<br />
), 4)<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- BannerTemplate class<br />
--------------------------------------------------------------------------------<br />
<br />
local BannerTemplate = {}<br />
BannerTemplate.__index = BannerTemplate<br />
<br />
function BannerTemplate.new(protectionObj, cfg)<br />
local obj = {}<br />
obj._cfg = cfg<br />
<br />
-- Set the image filename.<br />
local imageFilename = protectionObj.bannerConfig.image<br />
if imageFilename then<br />
obj._imageFilename = imageFilename<br />
else<br />
-- If an image filename isn't specified explicitly in the banner config,<br />
-- generate it from the protection status and the namespace.<br />
local action = protectionObj.action<br />
local level = protectionObj.level<br />
local namespace = protectionObj.title.namespace<br />
local reason = protectionObj.reason<br />
<br />
-- Deal with special cases first.<br />
if (<br />
namespace == 10<br />
or namespace == 828<br />
or reason and obj._cfg.indefImageReasons[reason]<br />
)<br />
and action == 'edit'<br />
and level == 'sysop'<br />
and not protectionObj:isTemporary()<br />
then<br />
-- Fully protected modules and templates get the special red "indef"<br />
-- padlock.<br />
obj._imageFilename = obj._cfg.msg['image-filename-indef']<br />
else<br />
-- Deal with regular protection types.<br />
local images = obj._cfg.images<br />
if images[action] then<br />
if images[action][level] then<br />
obj._imageFilename = images[action][level]<br />
elseif images[action].default then<br />
obj._imageFilename = images[action].default<br />
end<br />
end<br />
end<br />
end<br />
return setmetatable(obj, BannerTemplate)<br />
end<br />
<br />
function BannerTemplate:renderImage()<br />
local filename = self._imageFilename<br />
or self._cfg.msg['image-filename-default']<br />
or 'Transparent.gif'<br />
return makeFileLink{<br />
file = filename,<br />
size = (self.imageWidth or 20) .. 'px',<br />
alt = self._imageAlt,<br />
link = self._imageLink,<br />
caption = self.imageCaption<br />
}<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Banner class<br />
--------------------------------------------------------------------------------<br />
<br />
local Banner = setmetatable({}, BannerTemplate)<br />
Banner.__index = Banner<br />
<br />
function Banner.new(protectionObj, blurbObj, cfg)<br />
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.<br />
obj.imageWidth = 40<br />
obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip.<br />
obj._reasonText = blurbObj:makeBannerText('text')<br />
obj._explanationText = blurbObj:makeBannerText('explanation')<br />
obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.<br />
return setmetatable(obj, Banner)<br />
end<br />
<br />
function Banner:__tostring()<br />
-- Renders the banner.<br />
makeMessageBox = makeMessageBox or require('Module:Message box').main<br />
local reasonText = self._reasonText or error('no reason text set', 2)<br />
local explanationText = self._explanationText<br />
local mbargs = {<br />
page = self._page,<br />
type = 'protection',<br />
image = self:renderImage(),<br />
text = string.format(<br />
"'''%s'''%s",<br />
reasonText,<br />
explanationText and '<br />' .. explanationText or ''<br />
)<br />
}<br />
return makeMessageBox('mbox', mbargs)<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Padlock class<br />
--------------------------------------------------------------------------------<br />
<br />
local Padlock = setmetatable({}, BannerTemplate)<br />
Padlock.__index = Padlock<br />
<br />
function Padlock.new(protectionObj, blurbObj, cfg)<br />
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.<br />
obj.imageWidth = 20<br />
obj.imageCaption = blurbObj:makeBannerText('tooltip')<br />
obj._imageAlt = blurbObj:makeBannerText('alt')<br />
obj._imageLink = blurbObj:makeBannerText('link')<br />
obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action]<br />
or cfg.padlockIndicatorNames.default<br />
or 'pp-default'<br />
return setmetatable(obj, Padlock)<br />
end<br />
<br />
function Padlock:__tostring()<br />
local frame = mw.getCurrentFrame()<br />
-- The nowiki tag helps prevent whitespace at the top of articles.<br />
return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{<br />
name = 'indicator',<br />
args = {name = self._indicatorName},<br />
content = self:renderImage()<br />
}<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Exports<br />
--------------------------------------------------------------------------------<br />
<br />
local p = {}<br />
<br />
function p._exportClasses()<br />
-- This is used for testing purposes.<br />
return {<br />
Protection = Protection,<br />
Blurb = Blurb,<br />
BannerTemplate = BannerTemplate,<br />
Banner = Banner,<br />
Padlock = Padlock,<br />
}<br />
end<br />
<br />
function p._main(args, cfg, title)<br />
args = args or {}<br />
cfg = cfg or require(CONFIG_MODULE)<br />
<br />
local protectionObj = Protection.new(args, cfg, title)<br />
<br />
local ret = {}<br />
<br />
-- If a page's edit protection is equally or more restrictive than its<br />
-- protection from some other action, then don't bother displaying anything<br />
-- for the other action (except categories).<br />
if protectionObj.action == 'edit' or<br />
args.demolevel or<br />
not getReachableNodes(<br />
cfg.hierarchy,<br />
protectionObj.level<br />
)[effectiveProtectionLevel('edit', protectionObj.title)]<br />
then<br />
-- Initialise the blurb object<br />
local blurbObj = Blurb.new(protectionObj, args, cfg)<br />
<br />
-- Render the banner<br />
if protectionObj:isProtected() then<br />
ret[#ret + 1] = tostring(<br />
(yesno(args.small) and Padlock or Banner)<br />
.new(protectionObj, blurbObj, cfg)<br />
)<br />
end<br />
end<br />
<br />
-- Render the categories<br />
if yesno(args.category) ~= false then<br />
ret[#ret + 1] = protectionObj:makeCategoryLinks()<br />
end<br />
<br />
return table.concat(ret) <br />
end<br />
<br />
function p.main(frame, cfg)<br />
cfg = cfg or require(CONFIG_MODULE)<br />
<br />
-- Find default args, if any.<br />
local parent = frame.getParent and frame:getParent()<br />
local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')]<br />
<br />
-- Find user args, and use the parent frame if we are being called from a<br />
-- wrapper template.<br />
getArgs = getArgs or require('Module:Arguments').getArgs<br />
local userArgs = getArgs(frame, {<br />
parentOnly = defaultArgs,<br />
frameOnly = not defaultArgs<br />
})<br />
<br />
-- Build the args table. User-specified args overwrite default args.<br />
local args = {}<br />
for k, v in pairs(defaultArgs or {}) do<br />
args[k] = v<br />
end<br />
for k, v in pairs(userArgs) do<br />
args[k] = v<br />
end<br />
return p._main(args, cfg)<br />
end<br />
<br />
return p</div>
Cenarium