Permanently protected module
From Wikipedia, the free encyclopedia


require('strict')



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 levelsprotectionLevel

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(argsnum])

		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 selfmemoField ~= nil then

		return selfmemoField

	else

		selfmemoField = func(...)

		return selfmemoField

	end

end



function titleTable:titleIterator()

	local i = 0

	local n = #self

	return function()

		i = i + 1

		if i <= n then

			return selfi

		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

			levelslevel = 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(titles1])

	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(titles1])

		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 = titles1

	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 = titles1

		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 = boxProtectionLevelsprotectionType

	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.titles1].protectionLevel ~= 'unprotected' then

		obj.boxProtectionLevel = obj.titles1].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(protectionCatsboxProtectionLevel])

	if self.titles:hasOtherProtectionLevel(boxProtectionLevel) then

		addCat('Wikipedia edit requests possibly using incorrect templates')

	end

	return table.concat(cats)

end



function box:export()

	local title = self.titles.currentTitle

	if not title.isTalkPage and not self.demo and not yesno(self.args.skiptalk) 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
Permanently protected module
From Wikipedia, the free encyclopedia


require('strict')



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 levelsprotectionLevel

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(argsnum])

		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 selfmemoField ~= nil then

		return selfmemoField

	else

		selfmemoField = func(...)

		return selfmemoField

	end

end



function titleTable:titleIterator()

	local i = 0

	local n = #self

	return function()

		i = i + 1

		if i <= n then

			return selfi

		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

			levelslevel = 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(titles1])

	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(titles1])

		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 = titles1

	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 = titles1

		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 = boxProtectionLevelsprotectionType

	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.titles1].protectionLevel ~= 'unprotected' then

		obj.boxProtectionLevel = obj.titles1].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(protectionCatsboxProtectionLevel])

	if self.titles:hasOtherProtectionLevel(boxProtectionLevel) then

		addCat('Wikipedia edit requests possibly using incorrect templates')

	end

	return table.concat(cats)

end



function box:export()

	local title = self.titles.currentTitle

	if not title.isTalkPage and not self.demo and not yesno(self.args.skiptalk) 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

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook