Line 6: |
Line 6: |
| local makeFileLink = require('Module:File link')._main | | local makeFileLink = require('Module:File link')._main |
| local effectiveProtectionLevel = require('Module:Effective protection level')._main | | local effectiveProtectionLevel = require('Module:Effective protection level')._main |
| + | local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main |
| local yesno = require('Module:Yesno') | | local yesno = require('Module:Yesno') |
| | | |
Line 19: |
Line 20: |
| | | |
| local function makeCategoryLink(cat, sort) | | local function makeCategoryLink(cat, sort) |
− | local nsText = mw.site.namespaces[14].name
| + | if cat then |
− | if cat and sort then | |
| return string.format( | | return string.format( |
| '[[%s:%s|%s]]', | | '[[%s:%s|%s]]', |
− | nsText, | + | mw.site.namespaces[14].name, |
| cat, | | cat, |
| sort | | sort |
| ) | | ) |
− | elseif cat then
| |
− | return string.format(
| |
− | '[[%s:%s]]',
| |
− | nsText,
| |
− | cat
| |
− | )
| |
− | else
| |
− | return ''
| |
| end | | end |
| end | | end |
Line 40: |
Line 32: |
| -- Validation function for the expiry and the protection date | | -- Validation function for the expiry and the protection date |
| local function validateDate(dateString, dateType) | | local function validateDate(dateString, dateType) |
− | lang = lang or mw.language.getContentLanguage() | + | if not lang then |
| + | lang = mw.language.getContentLanguage() |
| + | end |
| local success, result = pcall(lang.formatDate, lang, 'U', dateString) | | local success, result = pcall(lang.formatDate, lang, 'U', dateString) |
| if success then | | if success then |
Line 49: |
Line 43: |
| end | | end |
| error(string.format( | | error(string.format( |
− | 'invalid %s ("%s")', | + | 'invalid %s: %s', |
| dateType, | | dateType, |
| tostring(dateString) | | tostring(dateString) |
Line 63: |
Line 57: |
| end | | end |
| | | |
− | local function toTableEnd(t, pos)
| + | -- Given a directed graph formatted as node -> table of direct successors, |
− | -- Sends the value at position pos to the end of array t, and shifts the
| + | -- get a table of all nodes reachable from a given node (though always |
− | -- other items down accordingly.
| + | -- including the given node). |
− | return table.insert(t, table.remove(t, pos))
| + | local function getReachableNodes(graph, start) |
− | end
| |
− | | |
− | local function walkHierarchy(hierarchy, start) | |
| local toWalk, retval = {[start] = true}, {} | | local toWalk, retval = {[start] = true}, {} |
| while true do | | while true do |
| -- Can't use pairs() since we're adding and removing things as we're iterating | | -- Can't use pairs() since we're adding and removing things as we're iterating |
− | local k = next(toWalk) | + | local k = next(toWalk) -- This always gets the "first" key |
− | if k == nil then break end | + | if k == nil then |
| + | return retval |
| + | end |
| toWalk[k] = nil | | toWalk[k] = nil |
| retval[k] = true | | retval[k] = true |
− | for _,v in ipairs(hierarchy[k]) do | + | for _,v in ipairs(graph[k]) do |
| if not retval[v] then | | if not retval[v] then |
| toWalk[v] = true | | toWalk[v] = true |
Line 83: |
Line 76: |
| end | | end |
| end | | end |
− | return retval
| |
| end | | end |
| | | |
Line 96: |
Line 88: |
| edit = true, | | edit = true, |
| move = true, | | move = true, |
− | autoreview = true | + | autoreview = true, |
| + | upload = true |
| } | | } |
| | | |
Line 120: |
Line 113: |
| else | | else |
| error(string.format( | | error(string.format( |
− | 'invalid action ("%s")', | + | 'invalid action: %s', |
| tostring(args.action) | | tostring(args.action) |
| ), 3) | | ), 3) |
Line 134: |
Line 127: |
| | | |
| -- Set expiry | | -- Set expiry |
− | if args.expiry then | + | local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title) |
− | if cfg.indefStrings[args.expiry] then
| + | if effectiveExpiry == 'infinity' then |
− | obj.expiry = 'indef'
| + | obj.expiry = 'indef' |
− | elseif type(args.expiry) == 'number' then
| + | elseif effectiveExpiry ~= 'unknown' then |
− | obj.expiry = args.expiry
| + | obj.expiry = validateDate(effectiveExpiry, 'expiry date') |
− | else | |
− | obj.expiry = validateDate(args.expiry, 'expiry date')
| |
− | end
| |
| end | | end |
| | | |
Line 207: |
Line 197: |
| | | |
| -- Get the namespace key fragment. | | -- Get the namespace key fragment. |
− | local namespaceFragment | + | local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace] |
− | do
| + | if not namespaceFragment and title.namespace % 2 == 1 then |
− | namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]
| + | namespaceFragment = 'talk' |
− | if not namespaceFragment and title.namespace % 2 == 1 then
| |
− | namespaceFragment = 'talk'
| |
− | end
| |
| end | | end |
| | | |
Line 237: |
Line 224: |
| -- instead. | | -- instead. |
| --]] | | --]] |
− | if self.reason and cfg.reasonsWithNamespacePriority[self.reason] then | + | table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3)) |
− | -- table.insert(order, 3, table.remove(order, 2))
| |
− | toTableEnd(order, 2)
| |
− | else
| |
− | toTableEnd(order, 3)
| |
− | end
| |
| | | |
| --[[ | | --[[ |
Line 323: |
Line 305: |
| end | | end |
| return '' | | 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 | | end |
| | | |
Line 355: |
Line 325: |
| local msg = self._cfg.msg | | local msg = self._cfg.msg |
| local ret = { self:makeProtectionCategory() } | | local ret = { self:makeProtectionCategory() } |
− | if self:needsExpiry() then
| |
− | ret[#ret + 1] = makeCategoryLink(
| |
− | msg['tracking-category-expiry'],
| |
− | self.title.text
| |
− | )
| |
− | end
| |
| if self:isIncorrect() then | | if self:isIncorrect() then |
| ret[#ret + 1] = makeCategoryLink( | | ret[#ret + 1] = makeCategoryLink( |
Line 488: |
Line 452: |
| if level == 'autoconfirmed' then | | if level == 'autoconfirmed' then |
| requestType = 'semi' | | requestType = 'semi' |
| + | elseif level == 'extendedconfirmed' then |
| + | requestType = 'extended' |
| elseif level == 'templateeditor' then | | elseif level == 'templateeditor' then |
| requestType = 'template' | | requestType = 'template' |
Line 754: |
Line 720: |
| end | | end |
| return setmetatable(obj, BannerTemplate) | | return setmetatable(obj, BannerTemplate) |
− | end
| |
− |
| |
− | function BannerTemplate:setImageWidth(width)
| |
− | self._imageWidth = width
| |
− | end
| |
− |
| |
− | function BannerTemplate:setImageTooltip(tooltip)
| |
− | self._imageCaption = tooltip
| |
| end | | end |
| | | |
Line 770: |
Line 728: |
| return makeFileLink{ | | return makeFileLink{ |
| file = filename, | | file = filename, |
− | size = (self._imageWidth or 20) .. 'px', | + | size = (self.imageWidth or 20) .. 'px', |
| alt = self._imageAlt, | | alt = self._imageAlt, |
| link = self._imageLink, | | link = self._imageLink, |
− | caption = self._imageCaption | + | caption = self.imageCaption |
| } | | } |
| end | | end |
Line 786: |
Line 744: |
| function Banner.new(protectionObj, blurbObj, cfg) | | function Banner.new(protectionObj, blurbObj, cfg) |
| local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. | | local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. |
− | obj:setImageWidth(40) | + | obj.imageWidth = 40 |
− | obj:setImageTooltip(blurbObj:makeBannerText('alt')) -- Large banners use the alt text for the tooltip. | + | obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip. |
| obj._reasonText = blurbObj:makeBannerText('text') | | obj._reasonText = blurbObj:makeBannerText('text') |
| obj._explanationText = blurbObj:makeBannerText('explanation') | | obj._explanationText = blurbObj:makeBannerText('explanation') |
Line 821: |
Line 779: |
| function Padlock.new(protectionObj, blurbObj, cfg) | | function Padlock.new(protectionObj, blurbObj, cfg) |
| local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. | | local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb. |
− | obj:setImageWidth(20) | + | obj.imageWidth = 20 |
− | obj:setImageTooltip(blurbObj:makeBannerText('tooltip')) | + | obj.imageCaption = blurbObj:makeBannerText('tooltip') |
| obj._imageAlt = blurbObj:makeBannerText('alt') | | obj._imageAlt = blurbObj:makeBannerText('alt') |
| obj._imageLink = blurbObj:makeBannerText('link') | | obj._imageLink = blurbObj:makeBannerText('link') |
Line 834: |
Line 792: |
| local frame = mw.getCurrentFrame() | | local frame = mw.getCurrentFrame() |
| -- The nowiki tag helps prevent whitespace at the top of articles. | | -- The nowiki tag helps prevent whitespace at the top of articles. |
− | local nowiki = frame:extensionTag{name = 'nowiki'} | + | return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{ |
− | local indicator = frame:extensionTag{
| |
| name = 'indicator', | | name = 'indicator', |
| args = {name = self._indicatorName}, | | args = {name = self._indicatorName}, |
| content = self:renderImage() | | content = self:renderImage() |
| } | | } |
− | return nowiki .. indicator
| |
| end | | end |
| | | |
Line 868: |
Line 824: |
| local ret = {} | | local ret = {} |
| | | |
− | -- If a page's edit protection is equally or more restrictive than its protection from some other action, | + | -- If a page's edit protection is equally or more restrictive than its |
− | -- then don't bother displaying anything for the other action (except categories).
| + | -- protection from some other action, then don't bother displaying anything |
− | if protectionObj.action == 'edit' or args.demolevel or not walkHierarchy(cfg.hierarchy, protectionObj.level)[effectiveProtectionLevel('edit', protectionObj.title)] then | + | -- for the other action (except categories). |
| + | if protectionObj.action == 'edit' or |
| + | args.demolevel or |
| + | not getReachableNodes( |
| + | cfg.hierarchy, |
| + | protectionObj.level |
| + | )[effectiveProtectionLevel('edit', protectionObj.title)] |
| + | then |
| -- Initialise the blurb object | | -- Initialise the blurb object |
| local blurbObj = Blurb.new(protectionObj, args, cfg) | | local blurbObj = Blurb.new(protectionObj, args, cfg) |