Module:Protection banner/sandbox: Difference between revisions

Content deleted Content added
use a global variable to store the config module name so that it's more obvious when the sandbox is being used
sync to live module
 
(85 intermediate revisions by 16 users not shown)
Line 3:
 
-- Initialise necessary modules.
require('Module:No globalsstrict')
local newFileLinkmakeFileLink = require('Module:File link').new_main
local effectiveProtectionLevel = require('Module:Effective protection level')._main
local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main
local yesno = require('Module:Yesno')
 
Line 12 ⟶ 13:
 
-- Set constants.
local CONFIG_MODULE = 'Module:Protection banner/config/sandbox' -- ** CHANGESWITCH THIS BACK TO USE THE MAIN CONFIG PAGE BEFORE UPDATING!GOING **LIVE!
 
--------------------------------------------------------------------------------
Line 19 ⟶ 20:
 
local function makeCategoryLink(cat, sort)
if cat then
local nsText = mw.site.namespaces[14].name
if cat and sort then
return string.format(
'[[%s:%s|%s]]',
mw.site.namespaces[14].name,
nsText,
cat,
sort
)
elseif cat then
return string.format(
'[[%s:%s]]',
nsText,
cat
)
else
return ''
end
end
Line 40 ⟶ 32:
-- Validation function for the expiry and the protection date
local function validateDate(dateString, dateType)
langif =not lang orthen
lang = mw.language.getContentLanguage()
end
local success, result = pcall(lang.formatDate, lang, 'U', dateString)
if success then
Line 49 ⟶ 43:
end
error(string.format(
'invalid %s: ("%s")',
dateType,
tostring(dateString)
), 04)
end
 
Line 63 ⟶ 57:
end
 
-- Given a directed graph formatted as node -> table of direct successors,
local function toTableEnd(t, pos)
-- get a table of all nodes reachable from a given node (though always
-- Sends the value at position pos to the end of array t, and shifts the
-- including the given node).
-- other items down accordingly.
local function getReachableNodes(graph, start)
return table.insert(t, table.remove(t, pos))
local toWalk, retval = {[start] = true}, {}
while true do
-- Can't use pairs() since we're adding and removing things as we're iterating
local k = next(toWalk) -- This always gets the "first" key
if k == nil then
return retval
end
toWalk[k] = nil
retval[k] = true
for _,v in ipairs(graph[k]) do
if not retval[v] then
toWalk[v] = true
end
end
end
end
 
Line 79 ⟶ 88:
edit = true,
move = true,
autoreview = true,
upload = true
}
 
Line 103 ⟶ 113:
else
error(string.format(
'invalid action: ("%s")',
tostring(args.action)
), 03)
end
 
-- Set level
obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)
if not obj.level or (obj.action == 'move' and obj.level == 'accountcreatorautoconfirmed') then
-- Lump titleblacklisted pages in with template-protected pages,
-- since templateeditors can do both.
obj.level = 'templateeditor'
elseif 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.
Line 121 ⟶ 127:
 
-- Set expiry
local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)
if args.expiry then
if cfg.indefStrings[args.expiry]effectiveExpiry == 'infinity' then
obj.expiry = 'indef'
elseif type(args.expiry)effectiveExpiry =~= 'numberunknown' then
obj.expiry = args.validateDate(effectiveExpiry, 'expiry date')
else
obj.expiry = validateDate(args.expiry, 'expiry date')
end
end
 
Line 135 ⟶ 138:
obj.reason = mw.ustring.lower(args[1])
if obj.reason:find('|') then
error('reasons cannot contain the pipe character ("|")', 03)
end
end
Line 166 ⟶ 169:
end
return setmetatable(obj, Protection)
end
 
function Protection:isUserScript()
-- Whether the page is a user JavaScript or CSS page.
local title = self.title
return title.namespace == 2 and (
title.contentModel == 'javascript' or title.contentModel == 'css'
)
end
 
Line 171 ⟶ 182:
return self.level ~= '*'
end
 
function Protection:shouldShowLock()
-- Whether we should output a banner/padlock
return self:isProtected() and not self:isUserScript()
end
 
-- Whether this page needs a protection category.
Protection.shouldHaveProtectionCategory = Protection.shouldShowLock
 
function Protection:isTemporary()
Line 177 ⟶ 196:
 
function Protection:makeProtectionCategory()
if not self:shouldHaveProtectionCategory() then
return ''
end
 
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.
Line 194 ⟶ 212:
 
-- Get the namespace key fragment.
local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]
if not namespaceFragment and title.namespace % 2 == 1 then
do
namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]'talk'
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
Line 224 ⟶ 239:
-- instead.
--]]
iftable.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] thenand 2 or 3))
-- table.insert(order, 3, table.remove(order, 2))
toTableEnd(order, 2)
else
toTableEnd(order, 3)
end
--[[
Line 310 ⟶ 320:
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:isProtectedshouldHaveProtectionCategory()
or type(expiry) == 'number' and expiry < os.time()
end
Line 341 ⟶ 339:
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(
Line 475 ⟶ 467:
if level == 'autoconfirmed' then
requestType = 'semi'
elseif level == 'extendedconfirmed' then
requestType = 'extended'
elseif level == 'templateeditor' then
requestType = 'template'
Line 526 ⟶ 520:
level,
talkKey
), 8)
end
return self:_substituteParameters(msg)
Line 566 ⟶ 560:
return pagetypes[self._protectionObj.title.namespace]
or pagetypes.default
or error('no default pagetype defined', 8)
end
 
Line 581 ⟶ 575:
msg = protectionBlurbs.edit.default
else
error('no protection blurb defined for protectionBlurbs.edit.default', 8)
end
return self:_substituteParameters(msg)
Line 607 ⟶ 601:
msg = protectionLevels.edit.default
else
error('no protection level defined for protectionLevels.edit.default', 8)
end
return self:_substituteParameters(msg)
Line 658 ⟶ 652:
 
function Blurb:_makeVandalTemplateParameter()
return mw.getCurrentFrame():expandTemplate{
return require('Module:Vandal-m')._main{
title="vandal-m",
args={self._args.user or self._protectionObj.title.baseText}
}
end
Line 686 ⟶ 681:
tostring(key),
type(msg)
), 4)
end
return self:_substituteParameters(msg)
Line 741 ⟶ 736:
end
return setmetatable(obj, BannerTemplate)
end
 
function BannerTemplate:setImageWidth(width)
self._imageWidth = width
end
 
function BannerTemplate:setImageTooltip(tooltip)
self._imageCaption = tooltip
end
 
Line 755 ⟶ 742:
or self._cfg.msg['image-filename-default']
or 'Transparent.gif'
return newFileLink(filename)makeFileLink{
file = filename,
:width(self._imageWidth or 20)
:altsize = (self._imageAltimageWidth or 20) .. 'px',
:link(alt = self._imageLink)_imageAlt,
:caption(link = self._imageCaption)_imageLink,
caption = self.imageCaption
:render()
}
end
 
Line 772 ⟶ 760:
function Banner.new(protectionObj, blurbObj, cfg)
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
obj:setImageWidth(.imageWidth = 40)
obj:setImageTooltip(.imageCaption = blurbObj:makeBannerText('alt')) -- Large banners use the alt text for the tooltip.
obj._reasonText = blurbObj:makeBannerText('text')
obj._explanationText = blurbObj:makeBannerText('explanation')
Line 783 ⟶ 771:
-- 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 = {
Line 807 ⟶ 795:
function Padlock.new(protectionObj, blurbObj, cfg)
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
obj:setImageWidth(.imageWidth = 20)
obj:setImageTooltip(.imageCaption = blurbObj:makeBannerText('tooltip'))
obj._imageAlt = blurbObj:makeBannerText('alt')
obj._imageLink = blurbObj:makeBannerText('link')
obj._right_indicatorName = cfg.padlockPositionspadlockIndicatorNames[protectionObj.action]
or cfg.padlockPositionspadlockIndicatorNames.default
or '55pxpp-default'
return setmetatable(obj, Padlock)
end
 
function Padlock:__tostring()
local rootframe = mw.html.creategetCurrentFrame('div')
-- The nowiki tag helps prevent whitespace at the top of articles.
root
return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{
:addClass('metadata topicon nopopups')
name = 'indicator',
:attr('id', 'protected-icon')
:css{displayargs = 'none', right{name = self._right_indicatorName},
:wikitext(content = self:renderImage())
}
return tostring(root)
end
 
Line 846 ⟶ 834:
function p._main(args, cfg, title)
args = args or {}
cfg = cfg or require('Module:Protection banner/config'CONFIG_MODULE)
 
local protectionObj = Protection.new(args, cfg, title)
-- Initialise the protection object and check for errors
local protectionObjCreated, protectionObj = pcall(
Protection.new,
args,
cfg,
title
)
if not protectionObjCreated then
local errorBlurb = cfg.msg['error-message-blurb'] or 'Error: $1.'
local errorText = mw.message.newRawMessage(errorBlurb)
:params(protectionObj) -- protectionObj is the error message
:plain()
return string.format(
'<strong class="error">%s</strong>%s',
errorText,
makeCategoryLink(
cfg.msg['tracking-category-incorrect'],
title and title.text or mw.title.getCurrentTitle().text
)
)
end
-- Initialise the blurb object
local blurbObj = Blurb.new(protectionObj, args, cfg)
 
local ret = {}
 
-- If a page's edit protection is equally or more restrictive than its
-- Render the banner
-- protection from some other action, then don't bother displaying anything
if protectionObj:isProtected() then
-- for the other action (except categories).
ret[#ret + 1] = tostring(
( if not yesno(args.smallcatonly) and Padlock(protectionObj.action or== Banner)'edit' or
args.demolevel or
.new(protectionObj, blurbObj, cfg)
not getReachableNodes(
)
cfg.hierarchy,
end
protectionObj.level
)[effectiveProtectionLevel('edit', protectionObj.title)])
then
-- Initialise the blurb object
local blurbObj = Blurb.new(protectionObj, args, cfg)
-- Render the banner
if protectionObj:shouldShowLock() 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
-- For arbitration enforcement, flagging [[WP:PIA]] pages to enable [[Special:AbuseFilter/1339]] to flag edits to them
if protectionObj.level == "extendedconfirmed" then
if require("Module:TableTools").inArray(protectionObj.title.talkPageTitle.categories, "Wikipedia pages subject to the extended confirmed restriction related to the Arab-Israeli conflict") then
ret[#ret + 1] = "<p class='PIA-flag' style='display:none; visibility:hidden;' title='This page is subject to the extended confirmed restriction related to the Arab-Israeli conflict.'></p>"
end
end
Line 892 ⟶ 878:
 
function p.main(frame, cfg)
getArgs = getArgs or require('Module:Arguments').getArgs
cfg = cfg or require(CONFIG_MODULE)
 
local parentTitle = frame:getParent():getTitle()
-- Find default args, if any.
parentTitle = parentTitle:gsub('/sandbox$', '')
local parent = frame.getParent and frame:getParent()
local defaultArgs = cfg.wrappers[parentTitle] or {}
local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')]
local args = getArgs(frame, {parentOnly = defaultArgs and true})
 
for k, v in pairs(defaultArgs) do
-- 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