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}}.
- -- Initialise necessary modules.
- require('Module:No globals')
- local makeFileLink = require('Module:File link')._main
- local effectiveProtectionLevel = require('Module:Effective protection level')._main
- local yesno = require('Module:Yesno')
- -- Lazily initialise modules and objects we don't always need.
- local getArgs, makeMessageBox, lang
- -- 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)
- 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
- }
- Protection.bannerConfigFields = {
- 'text',
- 'explanation',
- 'tooltip',
- 'alt',
- 'link',
- 'image'
- }
- 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
- if 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