Module:Sanctions/sandbox
Appearance
![]() | This is the module sandbox page for Module:Sanctions (diff). |
![]() | This module depends on the following other modules: |
This module provides utility for the community sanctions system. It should be called using one of its wrapper templates (eg {{Gs/talk notice}}).
To add, remove or modify sanctions, you only need to edit Module:Sanctions/data with your changes. Be careful when saving changes; syntax errors on /data will cause every transclusion of any GS template to show an ugly red error message.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local rawData = mw.loadData('Module:Sanctions/data/sandbox')
local data = rawData.sanctions
local commData = rawData.comm
local arbcomData = rawData.arbcom
local messageBox = require('Module:Message box')
-- Functions
local function tableContainsValue(needle, haystack)
for _, v in pairs(haystack) do
if v == needle then
return true
end
end
return false
end
local function _getTopicData(topicAlias)
if data[topicAlias] then
return data[topicAlias]
else
return false
end
end
-- Returns an invalid topic error, with a table of acceptable topics
local function syntaxHelp()
return '<span class="error">{{para|topic}} not specified. Available options:</span><div style="border-left: 5px dashed black; border-right: 5px dashed black; border-bottom: 5px dashed black; padding-left: 0.5em; padding-right: 0.5em; padding-bottom: 0.5em;">' .. mw.getCurrentFrame():preprocess('{{#invoke:Sanctions/sandbox|table}}') .. '</div>'
end
-- Topic class
local Topic = {}
Topic.__index = Topic
function Topic.new(topicCode, args)
local obj = {}
obj._args = args
obj._topicData = _getTopicData(topicCode)
return setmetatable(obj, Topic)
end
function Topic:get(arg)
return self._topicData[arg]
end
function Topic:exists()
return self._topicData and true
end
function Topic:hasGlobalRestriction(type)
return self._topicData.restrictions[type]
end
function Topic:hasLocalRestriction(type)
return yesno(self._args[type]) or false
end
function Topic:hasRestriction(type)
return self:hasGlobalRestriction(type) or self:hasLocalRestriction(type)
end
function Topic:hasRestrictions(arr)
for _, v in ipairs(arr) do
if self:hasRestriction(v) then
return true
end
end
return false
end
function Topic:hasAnyRevertRestrictions()
return self:hasRestrictions({'1rr', 'consensusrequired', 'brd', 'protection'})
end
function Topic:hasAnyRestrictions()
return self:hasAnyRevertRestrictions() or self:hasRestrictions({'restriction1'})
end
function Topic:getType()
return (self:get('type') == 'arbcom' and '[[WP:ARBCOM|Arbitration Committee]]' or 'community')
end
function Topic:getTypeUnlinked()
return (self:get('type') == 'arbcom' and 'arbitration' or 'community')
end
function Topic:getCustomRestrictions()
local customRestrictions = {}
local ri = 1
local checkArr = self._topicData.restrictions
local breakNext = false
while true do
if checkArr['restriction'..ri] then
table.insert(customRestrictions, checkArr['restriction'..ri])
ri = ri + 1
elseif breakNext then
break
else
ri = 1
checkArr = self._args
breakNext = true
end
end
return customRestrictions
end
-- End classes
-- This function builds a talk notice
-- TODO: split this up
--
-- @param frame
-- @param topic topic class instance
-- @param args arguments passed to wrapper template
-- @returns String representation of notice
local function buildTalkNotice(frame, topic, args)
local type = args['type'] or args[2] or 'mini'
local out = mw.html.create('')
local hasRestrictions = topic:hasAnyRestrictions()
local hasRevertRestrictions = topic:hasAnyRevertRestrictions()
if hasRestrictions then
type = 'long' -- force long displaywhere custom restrictions are applicable
out
:tag('span')
:css('font-size', '120%')
:wikitext("'''WARNING: ACTIVE " .. string.upper(topic:getTypeUnlinked()) .. " SANCTIONS'''")
end
if hasRestrictions then
out
:tag('p')
:wikitext("The article [[:{{SUBJECTPAGENAME}}]], along with other pages relating to "..topic:get('scope')..", is designated by the " .. topic:getType() .. " as a '''[[Wikipedia:Contentious topics|contentious topic]]'''. The current restrictions are:")
else
out
:tag('p')
:wikitext("<strong>The use of the [[Wikipedia:Contentious topics|contentious topics procedure]] has been authorized by the " .. topic:getType() .. " for pages related to ".. topic:get('scope') ..", including this page.</strong>" .. (type == 'mini' and ' Editors who repeatedly or seriously fail to adhere to the [[Wikipedia:Five_pillars|purpose of Wikipedia]], any expected [[Wikipedia:Etiquette|standards of behaviour]], or any [[Wikipedia:List_of_policies|normal editorial process]] may be sanctioned.' or ''))
end
if type ~= 'mini' then
local restrictionList = mw.html.create('ul')
-- 1RR
if topic:hasRestriction('1rr') then
restrictionList
:tag('li')
:wikitext("'''Limit of one revert in 24 hours:''' This article is under [[Wikipedia:Edit warring#Other revert rules|WP:1RR]] (one [[Wikipedia:Reverting|revert]] per editor per article ''per 24-hour period'')")
end
if topic:hasRestriction('protection') then
local protectionRestriction = ''
local shortCode = topic:get('restrictions')['protection']
if shortCode == "ecp" then
protectionRestriction = "'''Extended confirmed requirement:''' Editors must be logged in, [[WP:ECP|have made 500 edits, and an account age of 30 days]]"
elseif shortCode == "semi" then
protectionRestriction = "'''Auto confirmed requirement:'''Editors must be logged in, [[WP:SEMI|have made 10 edits, and an account age of 4 days]]"
end
restrictionList
:tag('li'):wikitext(protectionRestriction .. " in order to make edits within this topic")
end
-- Text for boilerplate/predefined restrictions
if topic:hasRestriction('consensusrequired') then
restrictionList
:tag('li')
:wikitext("'''Consensus required:''' All editors must obtain [[WP:Consensus|consensus]] on the talk page of this article before reinstating ''any edits that have been challenged (via reversion).'' This includes making edits similar to the ones that have been challenged. If in doubt, do not make the edit.")
end
if topic:hasRestriction('brd') then
restrictionList
:tag('li')
:wikitext("'''24-hr [[Wikipedia:BOLD, revert, discuss cycle|BRD cycle]]:''' If a change you make to this article is reverted, you may not reinstate that change unless you discuss the issue on the talk page and wait 24 hours (from the time of the original edit). Partial reverts/reinstatements that reasonably address objections of other editors [[Wikipedia:BOLD, revert, discuss cycle#WP:BRR|are preferable]] to wholesale reverts.")
end
local customRestrictions = topic:getCustomRestrictions()
for _, v in ipairs(customRestrictions) do
restrictionList
:tag('li')
:wikitext(v)
end
if hasRestrictions then
out:node(restrictionList)
end
out
:tag('p')
:wikitext("Editors who repeatedly or seriously fail to adhere to the [[Wikipedia:Five_pillars|purpose of Wikipedia]], any expected [[Wikipedia:Etiquette|standards of behaviour]], or any [[Wikipedia:List_of_policies|normal editorial process]] may be sanctioned.")
-- Further info box
if hasRestrictions then
local furtherInfo = mw.html.create('')
-- Enforcement procedures
furtherInfo
:tag('p')
:wikitext('Enforcement procedures:')
:done()
local enforcementProcedures = mw.html.create('ul')
if hasRestrictions then
enforcementProcedures
:tag('li')
:wikitext("Violations of any restrictions " .. (hasRevertRestrictions and "(excluding 1RR/reverting violations) " or "") .. "and other conduct issues should be reported to the " .. (topic:get('type') == "arbcom" and "[[WP:AE|arbitration enforcement noticeboard]]" or "[[WP:ANI|administrators' incidents noticeboard]]") .. "." .. (hasRevertRestrictions and " Violations of revert restrictions should be reported to the [[Wikipedia:Administrators' noticeboard/Edit warring|administrators' edit warring noticeboard]]." or ""))
:done()
:tag('li')
:wikitext("Editors who violate any listed restrictions may be blocked by any uninvolved administrator, even on a first offense.")
:done()
else
enforcementProcedures
:tag('li')
:wikitext("Problems should be reported to the " .. (topic:get('type') == "arbcom" and "[[WP:AE|arbitration enforcement noticeboard]]" or "[[WP:ANI|administrators' incidents noticeboard]]") .. ".")
:done()
end
enforcementProcedures
:tag('li')
:wikitext("An editor must be [[Wikipedia:Contentious topics#Awareness of contentious topics|aware]] before they can be sanctioned.")
:allDone()
furtherInfo:node(enforcementProcedures)
if hasRevertRestrictions then
furtherInfo
:tag('p')
:wikitext("With respect to any reverting restrictions:")
:done()
:tag('ul')
:tag('li')
:wikitext("Edits made solely to enforce any clearly established consensus are exempt from all edit-warring restrictions. In order to be considered \"clearly established\" the consensus must be proven by prior talk-page discussion.")
:done()
:tag('li')
:wikitext("Edits made which remove or otherwise change any material placed by clearly established consensus, without first obtaining consensus to do so, may be treated in the same manner as clear vandalism.")
:done()
:tag('li')
:wikitext("Clear vandalism of any origin may be reverted without restriction.")
:done()
:tag('li')
:wikitext("Reverts of edits made by anonymous (IP) editors that are not vandalism are exempt from the 1RR but are subject to [[Wikipedia:Edit warring|the usual rules on edit warring]]. If you are in doubt, contact an administrator for assistance.")
:allDone()
:tag('p')
:wikitext("If you are unsure if your edit is appropriate, discuss it here on this talk page first. <strong>Remember: When in doubt, don't revert!</strong>")
end
local collapsed = frame:expandTemplate{ title = 'collapse', args = {
tostring(furtherInfo),
'<span style="color:red">Remedy instructions and exemptions</span>',
['bg'] = '#EEE8AA'
}}
out
:newline()
:node(collapsed)
end
-- End further info box
end
local box = messageBox.main( 'tmbox', {
type = 'notice',
image = type == 'long' and '[[File:Commons-emblem-issue.svg|50px]]' or '[[File:Commons-emblem-hand-orange.svg|40px]]',
text = frame:preprocess(tostring(out))
})
return box
end
-- Builds an intro alert notice
--
-- @param frame
-- @param topic topic class instance
-- @returns String representation of notice
local function buildFirstAlert(frame, topic, sig)
local out = mw.html.create('table')
:addClass('gs-alert')
:cssText("border: 1px solid #AAA; background-color: #E5F8FF; padding: 0.5em; width: 100%; margin-bottom: 1em")
local insideNode = out
:tag('tr')
:tag('td')
:cssText("vertical-align:middle; padding-left:1px; padding-right:0.5em;")
:wikitext("[[File:Commons-emblem-notice.svg|50px]]")
:done()
:tag('td')
insideNode
:tag('p'):wikitext("You have recently edited a page related to <b>" .. topic:get('scope') .. "</b>, a topic designated as [[WP:CTOP|contentious]] by the " .. topic:getType() .. "."):done()
:tag('p'):wikitext("A special set of rules applies to certain topic areas, which are referred to as <i>contentious topics</i>. These are specially designated topics that have been identified by the community or the Arbitration Committee to attract more persistent disruptive editing than the rest of the project. When editing a contentious topic, Wikipedia’s norms and policies are more strictly enforced, and Wikipedia administrators have special powers in order to reduce disruption to the project."):done()
:tag('p'):wikitext("Within contentious topics, editors should edit <strong>carefully</strong> and <strong>constructively</strong>, refrain from disrupting the encyclopedia, and:"):done()
:tag('ul')
:tag('li'):wikitext('adhere to the [[WP:NOT|purposes of Wikipedia]];'):done()
:tag('li'):wikitext('comply with all applicable [[Wikipedia:Policies and guidelines|policies and guidelines]];'):done()
:tag('li'):wikitext('follow editorial and behavioural best practice;'):done()
:tag('li'):wikitext('comply with any page restrictions in force within the area of conflict; and'):done()
:tag('li'):wikitext('refrain from [[WP:GAME|gaming the system]].'):done()
:done()
if (topic:hasAnyRestrictions()) then
insideNode
:tag('p'):wikitext("Additionally, you must comply with the following restrictions:")
local restrictionList = insideNode:tag('ul')
if topic:hasRestriction('1rr') then
restrictionList:tag('li'):wikitext('You are not allowed to make more than 1 revert within 24 hours on a page within this topic.')
end
if topic:hasRestriction('protection') then
local protectionRestriction = ''
local shortCode = topic:get('restrictions')['protection']
if shortCode == "ecp" then
protectionRestriction = "logged in, have made 500 edits, and an account age of 30 days"
elseif shortCode == "semi" then
protectionRestriction = "logged in, have made 10 edits, and an account age of 4 days"
end
restrictionList:tag('li'):wikitext('You must be ' .. protectionRestriction .. '.')
end
local customRestrictions = topic:getCustomRestrictions()
for _, v in ipairs(customRestrictions) do
restrictionList
:tag('li')
:wikitext(v)
end
insideNode:done()
end
insideNode
:tag('p'):wikitext("Editors are advised to err on the side of caution if unsure whether making a particular edit is consistent with these expectations. If you have any questions about contentious topics ''procedures'' you may ask them at the [[WT:AC/C|arbitration clerks' noticeboard]] or you may learn more about this contentious topic at [[" .. topic:get('wikilink') .. "]]. You may also choose to note which contentious topics you know about by using the {{tl|ct/aware}} template."):done()
return frame:preprocess('== Introduction to contentious topics ==\n' .. tostring(out) .. '\n <!-- Derived from Template:Contentious topics/alert --> ')
end
-- Builds an alert notice
--
-- @param frame
-- @param topic topic class instance
-- @returns String representation of notice
local function buildAlert(frame, topic, sig)
local out = ''
out = out .. '[[File:Commons-emblem-notice.svg|link=|25px|alt=Information icon]] You have recently made edits related to ' .. topic:get('scope') .. '. This is a standard message to inform you that ' .. topic:get('scope') .. ' is a' .. (topic:get('type') == "comm" and ' community' or 'n Arbitration Committee') .. ' designated contentious topic. This message <em>does <strong>not</strong> imply that there are any issues with your editing</em>. '
if (topic:hasAnyRestrictions()) then
out = out .. "You must comply with the following restrictions within this topic area:"
local restrictionList = mw.html.create('ul')
if topic:hasRestriction('1rr') then
restrictionList:tag('li'):wikitext('You are not allowed to make more than 1 revert within 24 hours on a page within this topic.')
end
if topic:hasRestriction('protection') then
local protectionRestriction = ''
local shortCode = topic:get('restrictions')['protection']
if shortCode == "ecp" then
protectionRestriction = "logged in, have made 500 edits, and an account age of 30 days"
elseif shortCode == "semi" then
protectionRestriction = "logged in, have made 10 edits, and an account age of 4 days"
end
restrictionList:tag('li'):wikitext('You must be ' .. protectionRestriction .. '.')
end
local customRestrictions = topic:getCustomRestrictions()
for _, v in ipairs(customRestrictions) do
restrictionList
:tag('li')
:wikitext(v)
end
out = out .. tostring(restrictionList) .. '\n\n'
end
out = out .. 'For more information about the contentious topics system, please see [[Wikipedia:Contentious topics]].'
return frame:preprocess(tostring(out) .. '\n <!-- Derived from Template:Contentious topics/alert --> ')
end
-- Builds an edit notice
local function buildEditNotice(frame, topic, args)
local enHeader = 'This page relates to '.. topic:get('scope') .. ', which has been designated by the ' .. topic:getType() .. ' as a [[WP:CTOP|contentious topic]].'
local enText = mw.html.create('')
enText:tag('p'):wikitext('While editing this topic, you must adhere to the [[WP:Five pillars|purpose of Wikipedia]], [[WP:Etiquette|expected standards of behavior]], and [[WP:List of policies|Wikipedia\'s policies and norms]].'):done()
local restrictionMsgs = {}
if topic:hasRestriction('1rr') then
table.insert(restrictionMsgs, "Editors must not make more than one [[Help:Reverting|revert]] per 24 hours (subject to [[Wikipedia:Edit warring#Exemptions|exceptions]])")
end
if topic:hasRestriction('protection') then
local protectionRestriction = ''
local shortCode = topic:get('restrictions')['protection']
if shortCode == "ecp" then
protectionRestriction = "logged in, [[WP:ECP|have made 500 edits, and an account age of 30 days]]"
elseif shortCode == "semi" then
protectionRestriction = "logged in, [[WP:SEMI|have made 10 edits, and an account age of 4 days]]"
end
table.insert(restrictionMsgs, 'Editors must be ' .. protectionRestriction)
end
if topic:hasRestriction('consensusrequired') then
table.insert(restrictionMsgs, "All editors must obtain [[WP:Consensus|consensus]] on the talk page of this article before reinstating ''any edits that have been challenged (via reversion).'' This includes making edits similar to the ones that have been challenged. If in doubt, do not make the edit.")
end
if topic:hasRestriction('brd') then
table.insert(restrictionMsgs, "If a change you make to this article is reverted, you may not reinstate that change unless you discuss the issue on the talk page and wait 24 hours (from the time of the original edit). Partial reverts/reinstatements that reasonably address objections of other editors [[Wikipedia:BOLD, revert, discuss cycle#WP:BRR|are preferable]] to wholesale reverts.")
end
local customRestrictions = topic:getCustomRestrictions()
for _, v in ipairs(customRestrictions) do
table.insert(restrictionMsgs, v)
end
if #restrictionMsgs ~= 0 then
local list = mw.html.create('ul')
for _,v in ipairs(restrictionMsgs) do
list
:tag('li')
:wikitext(v)
:done()
end
enText:tag('p'):wikitext("In addition, while editing, you must follow these restrictions: "):done()
enText:wikitext(tostring(list))
end
enText
:tag('p')
:wikitext("<strong>Failure to do so may result in a block or other sanctions.</strong> Please edit carefully.")
:done()
local editnotice = frame:expandTemplate{ title = 'editnotice', args = {
expiry = "indefinite",
headerstyle = "font-size: 120%;",
style = "background: ivory;",
image = "Commons-emblem-issue.svg",
imagesize = "50px",
header = tostring(enHeader),
text = tostring(enText)
}}
return editnotice
end
--/////////--
-- EXPORTS
--/////////--
local p = {}
-- Returns a talk notice
-- For documentation, see [[Template:Gs/talk notice]]
function p.talknotice(frame)
local args = getArgs(frame)
local topic = Topic.new(args['topic'] or args[1], args)
if not topic:exists() then
return frame:preprocess(syntaxHelp())
end
return buildTalkNotice(frame, topic, args)
end
-- Returns an alert
-- For documentation, see [[Template:Gs/alert]]
function p.alert(frame)
local args = getArgs(frame)
local topic = Topic.new(args['topic'] or args[1], args)
if not topic:exists() then
return frame:preprocess(syntaxHelp())
elseif not topic:hasRestriction('ds') then
return frame:preprocess('<span class="error">This topic area is not designated as a contentious topic. Alert is not required.</span>')
end
return buildAlert(frame, topic, args['sig'])
end
function p.alertFirst(frame)
local args = getArgs(frame)
local topic = Topic.new(args['topic'] or args[1], args)
if not topic:exists() then
return frame:preprocess(syntaxHelp())
elseif not topic:hasRestriction('ds') then
return frame:preprocess('<span class="error">This topic area is not designated as a contentious topic. Alert is not required.</span>')
end
return buildFirstAlert(frame, topic, args['sig'])
end
-- Returns an edit notice
-- For documentation, see [[Template:Gs/editnotice]]
function p.editnotice(frame)
local args = getArgs(frame)
local topic = Topic.new(args['topic'] or args[1], args)
if not topic:exists() then
return frame:preprocess(syntaxHelp())
end
return buildEditNotice(frame, topic, args)
end
function p.table(frame)
local args = getArgs(frame)
local type = args['type']
local tbl = mw.html.create('table')
:addClass('wikitable'):addClass('sortable')
:css('font-size', '9pt')
:css('background', 'transparent')
if (type ~= "comm" and type ~= "arbcom" and type ~= nil) then
return '<span class="error">Invalid sanction type specified.</span>'
end
local topRow = tbl:tag('tr')
-- Headers
topRow:tag('th')
:wikitext("Topic code")
:done()
topRow:tag('th')
:wikitext("Area of conflict")
:done()
if (type == nil) then
topRow:tag('th')
:wikitext("Designated by")
:done()
end
topRow:tag('th')
:wikitext("Decision linked to")
:allDone()
-- sort alphabetically
local sortedTable = {}
if type == "comm" then
for n in pairs(commData) do
table.insert(sortedTable, n)
end
elseif type == "arbcom" then
for n in pairs(arbcomData) do
table.insert(sortedTable, n)
end
else
for n in pairs(data) do
table.insert(sortedTable, n)
end
end
table.sort(sortedTable)
local added = {}
for _,v in ipairs(sortedTable) do
local sanction = data[v]
local wt = (function(sanction, v)
for l,w in ipairs(sanction.aliases or {}) do
if (w == v) then return nil end
end
local out = "<code>" .. v .. "</code>"
for l,w in ipairs(sanction.aliases or {}) do
if (not added[w]) then
out = out .. ' ' .. "<code>" .. w .. "</code>"
added[w] = true
end
end
return out
end)(sanction, v)
if wt ~= nil then
if (not added[v]) then
tbl:tag('tr')
:tag('td')
:wikitext(wt)
:done()
:tag('td')
:wikitext(sanction.scope)
:done()
:tag('td')
:wikitext(sanction.type == "comm" and "the community" or "the Arbitration Committee")
:done()
:tag('td')
:wikitext("[[".. sanction.wikilink .."]]")
:allDone()
added[v] = true
end
end
end
return tostring(tbl)
end
function p.topicsHelper(frame)
local args = getArgs(frame)
if args['sanctions scope'] and data[args['sanctions scope']] then
return _getTopicData(args['sanctions scope']).scope
elseif args['sanctions link'] and data[args['sanctions link']] then
return mw.title.new(_getTopicData(args['sanctions link']).wikilink).redirectTarget
else
return "" -- ?
end
end
-- Returns true if the given topic name is a valid topic area
function p.checkIfValidTopic(topicName)
local topic = Topic.new(topicName, nil)
return topic:exists()
end
-- for debugging
p._Topic = Topic
return p