<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://zoophilia.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AProtection_banner</id>
	<title>Module:Protection banner - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://zoophilia.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AProtection_banner"/>
	<link rel="alternate" type="text/html" href="https://zoophilia.wiki/index.php?title=Module:Protection_banner&amp;action=history"/>
	<updated>2026-09-20T21:03:12Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://zoophilia.wiki/index.php?title=Module:Protection_banner&amp;diff=761&amp;oldid=prev</id>
		<title>imported&gt;SockyPaws: Update module with messages for Zoophilia Wiki and not Wikipedia</title>
		<link rel="alternate" type="text/html" href="https://zoophilia.wiki/index.php?title=Module:Protection_banner&amp;diff=761&amp;oldid=prev"/>
		<updated>2025-12-22T05:35:29Z</updated>

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