Module:Protected edit request/active
Template:Lua Template:Documentation subpage Template:Module rating This module is used internally by Module:Protected edit request and is not useful elsewhere.
require('Module:No globals') local yesno, makeMessageBox -- passed in from Module:Protected edit request local makeToolbar = require('Module:Toolbar')._main local getPagetype = require('Module:Pagetype')._main local effectiveProtectionLevel = require('Module:Effective protection level')._main ---------------------------------------------------------------------- -- Helper functions ---------------------------------------------------------------------- local function makeWikilink(page, display) if display then return mw.ustring.format('[[%s|%s]]', page, display) else return mw.ustring.format('[[%s]]', page) end end ---------------------------------------------------------------------- -- Title class ---------------------------------------------------------------------- -- This is basically the mw.title class with some extras thrown in. local title = {} title.__index = title function title.getProtectionLevelText(protectionLevel) -- Gets the text to use in anchors and urn links. local levels = {unprotected = 'editunprotected', autoconfirmed = 'editsemiprotected', extendedconfirmed = 'editextendedprotected', templateeditor = 'edittemplateprotected', sysop = 'editprotected', interfaceadmin = 'editinterfaceprotected'} return levels[protectionLevel] end function title.new(...) local success, obj = pcall(mw.title.new, ...) if not (success and obj) then return end title.init(obj) return obj end function title.init(obj) -- Add a protectionLevel property. obj.protectionLevel = effectiveProtectionLevel(obj.exists and 'edit' or 'create', obj) if obj.protectionLevel == '*' then -- Make unprotected pages return "unprotected". obj.protectionLevel = 'unprotected' elseif obj.protectionLevel == 'user' then -- If we just need to be registered, pretend we need to be autoconfirmed, since it's the closest thing we have. obj.protectionLevel = 'autoconfirmed' end -- Add a pagetype property. obj.pagetype = getPagetype{page = obj.prefixedText, defaultns = 'all'} -- Add link-making methods. function obj:makeUrlLink(query, display) return mw.ustring.format('[%s %s]', self:fullUrl(query), display) end function obj:makeViewLink(display) return self:makeUrlLink({redirect = 'no'}, display) end function obj:makeEditLink(display) return self:makeUrlLink({action = 'edit'}, display) end function obj:makeHistoryLink(display) return self:makeUrlLink({action = 'history'}, display) end function obj:makeLastEditLink(display) return self:makeUrlLink({diff = 'cur', oldid = 'prev'}, display) end function obj:makeWhatLinksHereLink(display) return makeWikilink('Special:WhatLinksHere/' .. self.prefixedText, display) end function obj:makeCompareLink(otherTitle, display) display = display or 'diff' local comparePagesTitle = title.new('Special:ComparePages') return comparePagesTitle:makeUrlLink({page1 = self.prefixedText, page2 = otherTitle.prefixedText}, display) end function obj:makeLogLink(logType, display) local logTitle = title.new('Special:Log') return logTitle:makeUrlLink({type = logType, page = self.prefixedText}, display) end function obj:urlEncode() return mw.uri.encode(self.prefixedText, 'WIKI') end function obj:makeUrnLink(boxProtectionLevel) -- Outputs a urn link. The protection level is taken from the template, rather than detected from page itself, -- as the detection may be inaccurate for cascade-protected and title-blacklisted pages as of Nov 2013. local protectionLinkText = title.getProtectionLevelText(boxProtectionLevel) return mw.ustring.format('[urn:x-wp-%s:%s <span></span>]', protectionLinkText, self:urlEncode()) end -- Get a subpage title object, but go through pcall rather than use the unprotected mw.title:subPageTitle. function obj:getSubpageTitle(subpage) return title.new(self.prefixedText .. '/' .. subpage) end function obj:getSandboxTitle() if self.isSubpage and self.contentModel == 'sanitized-css' then local success2, obj2 = pcall(mw.title.makeTitle, self.namespace, self.baseText .. '/sandbox/' .. self.subpageText) if success2 and obj2 then title.init(obj2) return obj2 end end return self:getSubpageTitle('sandbox') end end ---------------------------------------------------------------------- -- TitleTable class ---------------------------------------------------------------------- local titleTable = {} titleTable.__index = titleTable function titleTable.new(args) -- Get numerical arguments and make title objects for each of them. local nums = {} for k, v in pairs(args) do if type(k) == 'number' then table.insert(nums, k) end end table.sort(nums) local titles = {} for _, num in ipairs(nums) do local title = title.new(args[num]) table.insert(titles, title) end -- Get the current title, and get the subject title if no titles were specified. titles.currentTitle = mw.title.getCurrentTitle() if #titles < 1 then local subjectNs = titles.currentTitle.subjectNsText if subjectNs ~= '' then subjectNs = subjectNs .. ':' end table.insert(titles, title.new(subjectNs .. titles.currentTitle.text)) end -- Set the metatable. setmetatable(titles, titleTable) return titles end function titleTable:memoize(memoField, func, ...) if self[memoField] ~= nil then return self[memoField] else self[memoField] = func(...) return self[memoField] end end function titleTable:titleIterator() local i = 0 local n = #self return function() i = i + 1 if i <= n then return self[i] end end end function titleTable:hasSameProperty(memoField, getPropertyFunc) -- If the titles table has more than one title in it, check if they have the same property. -- The property is found using the getPropertyFunc function, which takes a title object as its single argument. local function hasSameProperty(getPropertyFunc) local property for i, obj in ipairs(self) do if i == 1 then property = getPropertyFunc(obj) elseif getPropertyFunc(obj) ~= property then return false end end return true end return self:memoize(memoField, hasSameProperty, getPropertyFunc) end function titleTable:hasSameExistenceStatus() -- Returns true if all the titles exist, or if they all don't exist. Returns false if there is a mixture of existence statuses. return self:hasSameProperty('sameExistenceStatus', function (title) return title.exists end) end function titleTable:hasSameProtectionStatus() -- Checks if all the titles have the same protection status (either for creation protection or for edit-protection - the two are not mixed). local sameExistenceStatus = self:hasSameExistenceStatus() if sameExistenceStatus then return self:hasSameProperty('sameProtectionStatus', function (title) return title.protectionLevel end) else return sameExistenceStatus end end function titleTable:hasSamePagetype() -- Checks if all the titles have the same pagetype. return self:hasSameProperty('samePagetype', function (title) return title.pagetype end) end function titleTable:propertyExists(memoField, getPropertyFunc) -- Checks if a title with a certain property exists. -- The property is found using the getPropertyFunc function, which takes a title object as its single argument -- and should return a boolean value. local function propertyExists(getPropertyFunc) for titleObj in self:titleIterator() do if getPropertyFunc(titleObj) then return true end end return false end return self:memoize(memoField, propertyExists, getPropertyFunc) end function titleTable:hasNonInterfacePage() return self:propertyExists('nonInterfacePage', function (titleObj) return titleObj.namespace ~= 8 end) end function titleTable:hasTemplateOrModule() return self:propertyExists('templateOrModule', function (titleObj) return titleObj.namespace == 10 or titleObj.namespace == 828 end) end function titleTable:hasNonTemplateOrModule() return self:propertyExists('nontemplateormodule', function (titleobj) return titleobj.namespace ~= 10 and titleobj.namespace ~= 828 end) end function titleTable:hasOtherProtectionLevel(level) for titleObj in self:titleIterator() do if titleObj.protectionLevel ~= level then return true end end return false end function titleTable:getProtectionLevels() local function getProtectionLevels() local levels = {} for titleObj in self:titleIterator() do local level = titleObj.protectionLevel levels[level] = true end return levels end return self:memoize('protectionLevels', getProtectionLevels) end ---------------------------------------------------------------------- -- Blurb class definition ---------------------------------------------------------------------- local blurb = {} blurb.__index = blurb function blurb.new(titleTable, boxProtectionLevel) local obj = {} obj.titles = titleTable obj.boxProtectionLevel = boxProtectionLevel obj.linkCount = 0 -- Counter for the number of total items in the object's link lists. setmetatable(obj, blurb) return obj end -- Static methods -- function blurb.makeParaText(name, val) local pipe = mw.text.nowiki('|') local equals = mw.text.nowiki('=') val = val and ("''" .. val .. "''") or '' return mw.ustring.format('<code style="white-space: nowrap;">%s%s%s%s</code>', pipe, name, equals, val) end function blurb.makeTemplateLink(s) return mw.ustring.format('%s[[Template:%s|%s]]%s', mw.text.nowiki('{{'), s, s, mw.text.nowiki('}}')) end function blurb:makeProtectionText() local boxProtectionLevel = self.boxProtectionLevel local levels = {['*'] = 'unprotected', autoconfirmed = 'semi-protected', extendedconfirmed = 'extended-confirmed-protected', templateeditor = 'template-protected', sysop = 'fully protected', interfaceadmin = 'interface-protected'} for level, protectionText in pairs(levels) do if level == boxProtectionLevel then return mw.ustring.format('[[Help:Protection|%s]]', protectionText) end end error('Unknown protection level ' .. boxProtectionLevel) end function blurb.getPagetypePlural(title) local pagetype = title.pagetype if pagetype == 'category' then return 'categories' else return pagetype .. 's' end end -- Normal methods -- function blurb:makeLinkList(title) local tbargs = {} -- The argument list to pass to Module:Toolbar tbargs.style = 'font-size: smaller;' tbargs.separator = 'dot' -- Page links. table.insert(tbargs, title:makeEditLink('edit')) table.insert(tbargs, title:makeHistoryLink('history')) table.insert(tbargs, title:makeLastEditLink('last')) table.insert(tbargs, title:makeWhatLinksHereLink('links')) -- Sandbox links. local sandboxTitle = title:getSandboxTitle() if sandboxTitle and sandboxTitle.exists then table.insert(tbargs, sandboxTitle:makeViewLink('sandbox')) table.insert(tbargs, sandboxTitle:makeEditLink('edit sandbox')) table.insert(tbargs, sandboxTitle:makeHistoryLink('sandbox history')) table.insert(tbargs, sandboxTitle:makeLastEditLink('sandbox last edit')) table.insert(tbargs, title:makeCompareLink(sandboxTitle, 'sandbox diff')) end -- Test cases links. local testcasesTitle = title:getSubpageTitle('testcases') if testcasesTitle and testcasesTitle.exists then table.insert(tbargs, testcasesTitle:makeViewLink('test cases')) end -- Transclusion count link. if title.namespace == 10 or title.namespace == 828 then -- Only add the transclusion count link for templates and modules. local tclink = mw.uri.new{ host = 'templatecount.toolforge.org', path = '/index.php', query = { lang = 'en', name = title.text, namespace = title.namespace, }, fragment = 'bottom' } tclink = string.format('[%s transclusion count]', tostring(tclink)) table.insert(tbargs, tclink) end -- Protection log link. if title.namespace ~= 8 then -- MediaWiki pages don't have protection log entries. table.insert(tbargs, title:makeLogLink('protect', 'protection log')) end self.linkCount = self.linkCount + #tbargs -- Keep track of the number of total links created by the object. return makeToolbar(tbargs) end function blurb:makeLinkLists() local titles = self.titles if #titles == 1 then return self:makeLinkList(titles[1]) else local ret = {} table.insert(ret, '<ul>') for i, titleObj in ipairs(titles) do table.insert(ret, mw.ustring.format('<li>%s %s</li>', titleObj:makeViewLink(titleObj.prefixedText), self:makeLinkList(titleObj))) end table.insert(ret, '</ul>') return table.concat(ret) end end function blurb:makeIntro() local titles = self.titles local requested = 'It is [[Wikipedia:Edit requests|requested]] that' local protectionText if titles:hasNonInterfacePage() then protectionText = ' ' .. self:makeProtectionText() else protectionText = '' -- Interface pages cannot be unprotected, so we don't need to explicitly say they are protected. end -- Deal with cases where we are passed multiple titles. if #titles > 1 then local pagetype if titles:hasSamePagetype() then pagetype = blurb.getPagetypePlural(titles[1]) else pagetype = 'pages' end return mw.ustring.format("'''%s edits be made to the following%s %s''':", requested, protectionText, pagetype) end -- Deal with cases where we are passed only one title. local title = titles[1] local stringToFormat if title.exists then stringToFormat = '%s an edit be made to the%s %s at %s.' else stringToFormat = '%s the%s %s at %s be created.' end stringToFormat = "'''" .. stringToFormat .. "'''" return mw.ustring.format(stringToFormat, requested, protectionText, title.pagetype, title:makeViewLink(title.prefixedText)) end function blurb:makeBody() local titles = self.titles local protectionLevels = titles:getProtectionLevels() local boxProtectionLevel = self.boxProtectionLevel local hasNonInterfacePage = titles:hasNonInterfacePage() local isPlural = false if #titles > 1 then isPlural = true end local descriptionText = "This template must be followed by a '''complete and specific description''' of the request, " if boxProtectionLevel == 'sysop' or boxProtectionLevel == 'templateeditor' then local editText = 'edit' if isPlural then editText = editText .. 's' end local descriptionCompleteText = mw.ustring.format('so that an editor unfamiliar with the subject matter could complete the requested %s immediately.', editText) descriptionText = descriptionText .. descriptionCompleteText else descriptionText = descriptionText .. 'that is, specify what text should be removed and a verbatim copy of the text that should replace it. ' .. [["Please change ''X''" is '''not acceptable''' and will be rejected; the request '''must''' be of the form "please change ''X'' to ''Y''".]] end local smallText = '' if boxProtectionLevel == 'sysop' or boxProtectionLevel == 'templateeditor' then local templateFullText if boxProtectionLevel == 'sysop' then templateFullText = 'fully protected' elseif boxProtectionLevel == 'templateeditor' then templateFullText = 'template-protected' end smallText = 'Edit requests to ' .. templateFullText .. " pages should only be used for edits that are either '''uncontroversial''' or supported by [[Wikipedia:Consensus|consensus]]." .. " If the proposed edit might be controversial, discuss it on the protected page's talk page '''before''' using this template." else local userText local responseTemplate if boxProtectionLevel == 'extendedconfirmed' then userText = '[[Wikipedia:User access levels#Extended confirmed users|extended confirmed]] user' responseTemplate = blurb.makeTemplateLink('EEp') elseif boxProtectionLevel == 'autoconfirmed' then userText = '[[Wikipedia:User access levels#Autoconfirmed|autoconfirmed]] user' responseTemplate = blurb.makeTemplateLink('ESp') elseif boxProtectionLevel == 'interfaceadmin' then userText = '[[Wikipedia:User access levels#Interface administrators|interface administrator]]' responseTemplate = blurb.makeTemplateLink('EIp') else userText = 'user' responseTemplate = blurb.makeTemplateLink('ESp') end local answeredPara = blurb.makeParaText('answered', 'no') local stringToFormat = 'The edit may be made by any %s. ' .. [[Remember to change the %s parameter to "'''yes'''" when the request has been accepted, rejected or on hold awaiting user input. ]] .. "This is so that inactive or completed requests don't needlessly fill up the edit requests category. " .. 'You may also wish to use the %s template in the response.' smallText = mw.ustring.format(stringToFormat, userText, answeredPara, responseTemplate) end if not isPlural then local title = titles[1] if title.namespace == 10 or title.namespace == 828 then local sandboxTitle = title:getSubpageTitle('sandbox') if sandboxTitle and sandboxTitle.exists then smallText = smallText .. ' Consider making changes first to the ' .. sandboxTitle:makeViewLink(title.pagetype .. "'s sandbox") local testcasesTitle = title:getSubpageTitle('testcases') if testcasesTitle and testcasesTitle.exists then smallText = smallText .. ' and ' .. testcasesTitle:makeViewLink('test them thoroughly here') end smallText = smallText .. ' before submitting an edit request.' end end end if hasNonInterfacePage then smallText = smallText .. ' To request that a page be protected or unprotected, make a [[Wikipedia:Requests for page protection|protection request]].' end if boxProtectionLevel == 'sysop' or boxProtectionLevel == 'templateeditor' or boxProtectionLevel == 'interfaceadmin' then smallText = smallText .. ' When the request has been completed or denied, please add the ' .. blurb.makeParaText('answered', 'yes') .. ' parameter to deactivate the template.' end return mw.ustring.format('%s\n<p style="font-size:smaller; line-height:1.3em;">\n%s\n</p>', descriptionText, smallText) end function blurb:export() local intro = self:makeIntro() local linkLists = self:makeLinkLists() local body = self:makeBody() -- Start long links lists on a new line. local linkListSep = ' ' if self.linkCount > 5 then linkListSep = '<br />' end return mw.ustring.format('%s%s%s\n\n%s', intro, linkListSep, linkLists, body) end ---------------------------------------------------------------------- -- Subclass of Module:Protected edit request's box class for active boxes ---------------------------------------------------------------------- local box = {} box.__index = box function box.new(protectionType, args) -- In the inheritance system used here, an object's metatable is its class, and a class's metatable is its superclass local obj = getmetatable(box).new(protectionType, args) setmetatable(obj, box) local boxProtectionLevels = {semi = 'autoconfirmed', extended = 'extendedconfirmed', template = 'templateeditor', full = 'sysop', interface = 'interfaceadmin'} obj.boxProtectionLevel = boxProtectionLevels[protectionType] obj.demo = yesno(args.demo) -- Set dependent objects. obj.titles = titleTable.new(args) if not yesno(args.force) and obj.titles:hasSameProperty('sameProtectionStatus', function (title) return title.protectionLevel end) and obj.titles[1].protectionLevel ~= 'unprotected' then obj.boxProtectionLevel = obj.titles[1].protectionLevel end obj.blurb = blurb.new(obj.titles, obj.boxProtectionLevel) return obj end function box:setImage() local titles = self.titles local boxProtectionLevel = self.boxProtectionLevel local padlock if boxProtectionLevel == 'sysop' then padlock = 'Full-protection-shackle.svg' elseif boxProtectionLevel == 'interfaceadmin' then padlock = 'Interface-protection-shackle.svg ' elseif boxProtectionLevel == 'templateeditor' then padlock = 'Template-protection-shackle.svg' elseif boxProtectionLevel == 'autoconfirmed' then padlock = 'Semi-protection-shackle.svg' elseif boxProtectionLevel == 'extendedconfirmed' then padlock = 'Extended-protection-shackle.svg' else padlock = 'Padlock-bronze-open.svg' end local stringToFormat = '[[File:%s|%dpx|alt=|link=]]' local smallPadlock = mw.ustring.format(stringToFormat, padlock, 25) local largePadlock = mw.ustring.format(stringToFormat, padlock, 60) self:setArg('smallimage', smallPadlock) self:setArg('image', largePadlock) end function box:buildUrnLinks() local ret = {} local boxProtectionLevel = self.boxProtectionLevel for titleObj in self.titles:titleIterator() do table.insert(ret, titleObj:makeUrnLink(boxProtectionLevel)) end return mw.ustring.format('<span class="plainlinks" style="display:none">%s</span>', table.concat(ret)) end function box:setBlurbText() self:setArg('text', self.blurb:export() .. self:buildUrnLinks()) end function box:exportRequestTmbox() self:setImage() self:setBlurbText() self:setArg('class', 'editrequest') self:setArg('id', title.getProtectionLevelText(self.boxProtectionLevel)) -- for anchor. yes, this leads to multiple elements with the same ID. we should probably fix this at some point return makeMessageBox('tmbox', self.tmboxArgs) end function box:exportRequestCategories() local cats = {} local boxProtectionLevel = self.boxProtectionLevel local function addCat(cat) table.insert(cats, mw.ustring.format('[[Category:%s]]', cat)) end local protectionCats = { autoconfirmed = 'Wikipedia semi-protected edit requests', extendedconfirmed = 'Wikipedia extended-confirmed-protected edit requests', templateeditor = 'Wikipedia template-protected edit requests', sysop = 'Wikipedia fully-protected edit requests', interfaceadmin = 'Wikipedia interface-protected edit requests' } addCat(protectionCats[boxProtectionLevel]) if self.titles:hasOtherProtectionLevel(boxProtectionLevel) then addCat('Wikipedia edit requests possibly using incorrect templates') end return table.concat(cats) end function box:export() if not self.titles.currentTitle.isTalkPage and not self.demo then return '<span class="error">Error: Protected edit requests can only be made on the talk page.</span>[[Category:Non-talk pages with an edit request template]]' end local ret = {} table.insert(ret, self:exportRequestTmbox()) if not self.demo then table.insert(ret, self:exportRequestCategories()) end return table.concat(ret) end ---------------------------------------------------------------------- -- Function exported to Module:Protected edit request ---------------------------------------------------------------------- return function(superclass, yn, mb) yesno = yn makeMessageBox = mb return setmetatable(box, superclass) end