| Line 20: |
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 41: |
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 50: |
Line 43: |
| | end | | end |
| | error(string.format( | | error(string.format( |
| − | 'invalid %s ("%s")', | + | 'invalid %s: %s', |
| | dateType, | | dateType, |
| | tostring(dateString) | | tostring(dateString) |
| Line 64: |
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) -- This always gets the "first" key | | 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 84: |
Line 76: |
| | end | | end |
| | end | | end |
| − | return retval
| |
| | end | | end |
| | | | |
| Line 122: |
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 141: |
Line 132: |
| | elseif effectiveExpiry ~= 'unknown' then | | elseif effectiveExpiry ~= 'unknown' then |
| | obj.expiry = validateDate(effectiveExpiry, 'expiry date') | | obj.expiry = validateDate(effectiveExpiry, 'expiry date') |
| − | elseif 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 | | end |
| | | | |
| Line 214: |
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 244: |
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 330: |
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 362: |
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 495: |
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 761: |
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 777: |
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 793: |
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 828: |
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 841: |
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 880: |
Line 829: |
| | if protectionObj.action == 'edit' or | | if protectionObj.action == 'edit' or |
| | args.demolevel or | | args.demolevel or |
| − | not walkHierarchy( | + | not getReachableNodes( |
| | cfg.hierarchy, | | cfg.hierarchy, |
| | protectionObj.level | | protectionObj.level |