This module is used for speedy deletion notices and aims to increase flexibility greatly.

Features to be included include substitution detection and the ability to print out a full speedy deletion table.

UsageEdit

{{#invoke:Speedy|function_name}}

Full Table of ReasonsEdit

See config page to edit

CodeAliasesCriterionNameDescription
g1nonsenseG1Patent nonsenseas a page that is patent nonsense, consisting purely of incoherent text or gibberish with no meaningful content or history
g2testG2Test pageas a test page
g3vandalismhoaxG3Pure vandalismas pure vandalism or a blatant hoax
g4repostG4Recreation of a page that was deleted per a deletion discussionas a page that was [{{fullurl:Special:Log|type=delete&page=$1}} previously deleted] via a deletion discussion, is substantially identical to the deleted version, and any changes do not address the reasons for which the material was deleted$2
g5bannedblockedG5Block or ban evasionas a page created by a banned or blocked user $2 in violation of the user's ban or block, with no substantial edits by others
g6G6Housekeeping or uncontroversial maintenanceas a page that needs to be deleted to perform uncontroversial maintenance tasks, such as merging page histories or reversing redirects$2
g7authorblankedselfG7Author requests deletionas a page where the author of the only substantial content has requested deletion and/or blanked the page in good faith
g8G8Page dependent on non-existent pageas a page that is dependent on a page that has never existed or has been deleted$2
g9G9Office actionto enforce an office action
g10attackattackorgpersonal attackG10Attack pageas a page that serves no other purpose but to disparage or threaten its subject or some other entity
g11promospamG11Unambiguous advertising or promotionbecause in its current form it serves only to promote or publicize an entity, person, product, or idea, and would require a fundamental rewrite in order to become encyclopedic
g12copyvioG12Unambiguous copyright infringementas a copyright infringement$2
g13afcblankdraftG13Abandoned AfC draftsas either a page in the draftspace, a declined/unsubmitted userspace Articles for Creation page, or a draft in either namespace with no content except the placeholder article wizard text that has not been edited (excluding bot edits) in over six months
g14disambigG14Unnecessary disambiguationas either a disambiguation page that disambiguates zero extant Wikipedia pages or disambiguates only one extant Wikipedia page and ends in "(disambiguation)", or a redirect that ends in "(disambiguation)" that targets a page that is not a disambiguation page or a page that performs a disambiguation-like function
a1nocontextA1No contextas a very short article lacking sufficient context to identify the subject of the article
a2foreignA2Foreign-language articles that exist on another Wikimedia projectas a foreign-language article that exists on another Wikimedia project$2
a3nocontentcontactA3No contentas an article that contains no content whatsoever, or consists only of external links, categories, a "see also" section, a rephrasing of the title, chat-like comments, template tags, and/or images
a5transwikiA5Transwikied articlesas an article that has already been transwikied (e.g., to Wiktionary or Wikisource)$2
a7A7No indication of importance (people, animals, organizations, web content, events)as an article about {{#if:$2|$2|a real person, individual animal, organization (band, club, company, etc.), web content or organized event}} that does not credibly indicate the {{#if:$3|importance or significance|importance or significance}} of the subject
a9albumsongdiscogA9No indication of importance (musical recordings)as an article about {{#if:$2|$2|a musical recording or list of musical recordings}} that does not credibly indicate the {{#if:$3|importance or significance|importance or significance}} of the subject
a10samedupA10Recently created article that duplicates an existing topicas a recently created article with no relevant page history that does not expand upon, detail, or improve information within the existing article(s) on the subject$2
a11inventedmadeupA11Obviously inventedas an article which plainly indicates that the subject was invented/coined/discovered by the article's creator or someone they know personally, and does not credibly indicate why its subject is important or significant
r2rediruserredirdraftR2Cross namespace redirectas a redirect from the main/article space to any other namespace except the Category:, Template:, Wikipedia:, Help: and Portal: namespaces
r3redirtypotypomisnomerR3Implausible typoas a recently created redirect from an implausible typo or misnomer, which is not in another language pertinent to the topic
r4redircomR4File namespace redirect that matches a Wikimedia Commons pageas a redirect in the File namespace that has the same name as a page on Wikimedia Commons: Commons:$1 and no incoming file links
f1redundantfileF1Redundantas an unused image or other media file that is a redundant copy, in the same file format, and at the same or lower quality/resolution, of $2, which is on Wikipedia, and all inward links have been updated {{#ifeq:{{Is Commons|$2}}|yes|.
{{error|[[:{{file title | $2 }}]] is on Commons, please use {{nowrap|{{tlx|db-f8}}}}}}|}}
f2nofilefpcfailF2Corrupt, missing, or empty fileas a corrupt or empty file, or a file description page for a file on Commons
f3noncomF3Improper licenseas an image or other media file licensed with an unacceptable license
f4nsdnldF4Lack of licensing informationas an image or other media file missing important licensing information, including the source and the licensing status
f5orfudF5Orphaned non-free fileas a non-free file that is currently not being used in any articles
f5immF5Orphaned non-free file (immediate)as a non-free file that is not used in any articles or was only used in a now deleted article and is very unlikely to have any use on any other valid article
f6nrdF6Missing non-free use rationaleas a non-free file that has no explanation as to why the file meets the non-free content criteria
f7badfairuseF7Invalid fair use claimas a non-free file from a commercial source (e.g. Associated Press, Getty), where the file itself is not the subject of sourced commentary
f8nowcommonsF8Available as identical copy on Wikimedia Commonsas a file that is available as an identical copy of a file on Wikimedia Commons$2
f9filecopyvioF9Unambiguous file copyright infringementbecause it appears to have been copied from {{#if:$2|$2, which does not|an unspecified source, which does not}} have a license compatible with Wikipedia, and the uploader does not assert fair use or make a credible claim of permission
f10badfiletypeF10Useless non-media fileas a file other than an image, audio, or video file, which is not used in any articles and has no encyclopedic use
f11npdF11No evidence of permissionas a file in which the copyright holder has not provided evidence of permission or ownership
c1catemptyC1Empty categoryas a category that is empty, is not currently under discussion at Wikipedia:Categories for discussion (or other such discussions), and is not a disambiguation category, category redirect, featured topics category, or a project category that by its nature may become empty on occasion.
u1userreqU1User requestas a user page or subpage requested to be deleted by its user
u2nouserU2User page of non-existent useras a user page, subpage, or talk page of a user that does not exist ([[Special:CentralAuth/{{BASEPAGENAMEE}}|check]])
u5webhostnotwebhostU5Misuse of Wikipedia as a webhostas a page in userspace consisting of writings, information, discussions, and/or activities not closely related to Wikipedia's goals, where the owner has made few or no edits outside of userspace
p1P1Portal that would be eligible for speedy deletion as an articleas a portal page which would qualify for speedy deletion $2 if it were an article
p2emptyportalP2Underpopulated portalas a portal based on a topic for which there is only a stub header article or fewer than three non-stub articles detailing subject matter that would be appropriate to present under the title of that portal



local getArgs = require("Module:Arguments").getArgs
local pageType = require("Module:Pagetype")
local mbox = require("Module:Message box")
local yesno = require("Module:Yesno")
local button = require('Module:Clickable button 2')
local preview = require('Module:If preview')
local p = {}
local config = mw.loadData('Module:Speedy/config')
local timeAgo = require('Module:Time ago')
----------------------------------------------------------------------------
-- message function from [[Module:Documentation]]
----------------------------------------------------------------------------
local fillStringWithArgs
local function message(cfgKey, valArray, expectType)
	--[[
	-- Gets a message from the cfg table and formats it if appropriate.
	-- The function raises an error if the value from the cfg table is not
	-- of the type expectType. The default type for expectType is 'string'.
	-- If the table valArray is present, strings such as $1, $2 etc. in the
	-- message are substituted with values from the table keys [1], [2] etc.
	-- For example, if the message "foo-message" had the value 'Foo $2 bar $1.',
	-- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz."
	--]]
	local msg = config.messages[cfgKey]
	expectType = expectType or 'string'
	if type(msg) ~= expectType then
		error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)
	end
	if not valArray then
		return msg
	end
	return fillStringWithArgs(msg, valArray)
end

function fillStringWithArgs(text, valArray)
	if not valArray then
		return text
	end

	local function getVal(match)
		match = tonumber(match)
		return valArray[match] or ''
	end

	return mw.ustring.gsub(text, '$([1-9][0-9]*)', getVal) .. ''
end

local function detectParameters(text)
	return text and mw.ustring.find(text, '$([1-9][0-9]*)') and true or false
end

local function makeUnorderedList(array)
	local ul = mw.html.create('ul')
	for k,v in pairs(array) do
		local li = ul:tag('li')
		li:wikitext(v)
		li:done()
	end
	ul:allDone()
	return tostring(ul)
end

local function makeWikiList(array)
	local out = ''
	for k,v in pairs(array) do
		out = out .. '* ' .. v .. '\n'
	end
	return out
end

----------------------------------------------------------------------------
-- Argument processing (from [[Module:Documentation]])
----------------------------------------------------------------------------

local function makeInvokeFunc(funcName)
	return function (frame)
		local args = getArgs(frame, {
			valueFunc = function (key, value)
				if type(value) == 'string' then
					value = value:match('^%s*(.-)%s*$') -- Remove whitespace.
					if key == 'heading' or value ~= '' then
						return value
					else
						return nil
					end
				else
					return value
				end
			end
		})
		return p[funcName](args)
	end
end

----------------------------------------------------------------------------
-- Miscellaneous functions related to speedy deletion
----------------------------------------------------------------------------

local function getDeletionEntry(code)
	return config.deletionCodes[code]
end

local function yn(input, default)
	local res = yesno(input, nil)
	if (res == nil) then return default
	else return res end
end

local function processDeletionArgs(iparams)
	local args = {
		deletionReasons = {},
		deletionReasonsNotice = {},
		entries = {},
		numberOfEntries = 0,
		hideButton = true,
		highestMessage = 0,
		drv = false,
		willProvide = false,
		hide = false,
		blank = false
	}
	local entry = nil
	local replaceParams = false
	local params = {iparams.page or mw.title.getCurrentTitle().fullText}
	local paramNo = 2
	local skipped = true
	local function cleanupLeftover(v)
		if entry then
			table.insert(args.deletionReasons, '<span style="font-style:normal;">' .. fillStringWithArgs(entry.description, params) .. '.</span> ' .. (entry.more and '<span style="font-weight:normal;"> ' .. entry.more .. '</span> ' or '') .. '<span style="font-style:normal;">' .. message('deleteIntroCriteriaLink', {entry.code}) .. '.</span>')
			table.insert(args.deletionReasonsNotice,  fillStringWithArgs(entry.description, params) .. ' ([[WP:CSD#' .. entry.code .. '|CSD ' .. entry.code .. ']]). ' .. (entry.additionalMessage and fillStringWithArgs(entry.additionalMessage, params) or ''))
			table.insert(args.entries, entry)
		else
			if (v ~= '') then
				table.insert(args.deletionReasons, v)
				table.insert(args.deletionReasonsNotice, v)
			end
			table.insert(args.entries, {})
		end
		params = {iparams.page or mw.title.getCurrentTitle().fullText}
		paramNo = 2
		args.customHeader = entry and ((entry.notice or 2) >= args.highestMessage) and entry.customHeader or args.customHeader
		args.customIntro = entry and ((entry.notice or 2) >= args.highestMessage) and entry.customIntro or args.customIntroDeleted
		args.customIntroDeleted = entry and ((entry.notice or 2) >= args.highestMessage) and entry.customIntroDeleted or args.customIntroDeleted
		args.customCloser = entry and ((entry.notice or 2) >= args.highestMessage) and entry.customCloser or args.customCloser
		args.numberOfEntries = args.numberOfEntries + 1
		args.hideButton = entry and (args.hideButton and (entry.notice or 2) == 0) or false
		args.highestMessage = entry and ((entry.notice or 2) >= args.highestMessage) and entry.notice or args.highestMessage
		args.drv = entry and (args.drv or entry.drv) or args.drv
		args.willProvide = entry and entry.willProvide or args.willProvide
		args.hide = entry and (entry.hide or args.hide) or args.hide
		args.blank = entry and (entry.blank or args.blank) or args.blank
	end
	for k,v in ipairs(iparams) do
		if type(k) == type(1) then
			skipped = false
			if (replaceParams) then
				local pName = fillStringWithArgs(entry and entry.inputFormat[paramNo - 1] or '$2', {iparams.page or mw.title.getCurrentTitle().fullText, v})
				paramNo = paramNo + 1
				table.insert(params, pName)
			else 
				entry = getDeletionEntry(v) or nil
				replaceParams = entry and detectParameters(entry.description) or false
			end
			if not replaceParams then
				cleanupLeftover(v)
			end
		end
	end
	if replaceParams then
		cleanupLeftover('')
	end
	if skipped then
		args.hideButton = false
	end
	args.help = yn(iparams.help, true)
	args.nocat = yn(iparams.nocat, false)
	args.bot = yn(iparams.bot, false)
	args.noHeader = yn(iparams.noheader, false)
	args.additionalNote = iparams.additionalnote
	args.pageName = iparams.page
	args.notice = yn(iparams.notice, false)
	args.date = iparams.date or mw.getCurrentFrame():preprocess('{{safesubst:REVISIONTIMESTAMP}}')
	return args
end

local function isSubstituted()
	return yn(mw.getCurrentFrame():preprocess('{{safesubst:Issubst}}'))
end

----------------------------------------------------------------------------
-- Entry point
----------------------------------------------------------------------------

p.main = makeInvokeFunc('_main')
function p._main(params)
	-- get page 
	local args = processDeletionArgs(params)
	local out = ''
	if args.notice then
		if not args.pageName then
			return preview._warning({'No page name specified. Proceeding will do nothing.'})
		end
		if not args.noHeader then
			out = out .. (args.customHeader and '== ' .. fillStringWithArgs(args.customHeader, {args.pageName}) .. ' ==' or "== " .. message("noticeHeader", {args.pageName}) .. " ==") .. '\n'
		end
		local messageType = args.highestMessage == 1 and 'welcome' or 'notice'
		out = out .. (message('level' .. args.highestMessage .. 'icon') == '' and '' or '[[' .. message('level' .. args.highestMessage .. 'icon') .. '|40px]] ')
		if mw.title.new(args.pageName).exists then
			if args.customIntro then
				out = out .. fillStringWithArgs(args.customIntro, {
					args.pageName,
					args.deletionReasonsNotice[1]
				}) .. '\n\n'
			elseif args.numberOfEntries == 1 then
				out = out .. message(messageType .. 'Message', {
					args.pageName,
					args.deletionReasonsNotice[1]
				}) .. '\n\n'
			elseif args.numberOfEntries > 1 then
				out = out .. message(messageType .. 'MessageMultiple', {
					args.pageName,
					'\n' .. makeWikiList(args.deletionReasonsNotice)
				}) .. '\n'
			else
				out = out .. message(messageType .. 'MessageMultiple', {
					args.pageName,
					message("seePageForWhy")
				}) .. '\n\n'
			end
			if args.customCloser then
				out = out .. args.customCloser
			else 
				out = out .. '{{#ifexist:' .. args.pageName .. '|'
				if args.hideButton then
					out = out .. message("removeSpeedyMessage", {args.pageName}) .. ' '
				else
					out = out .. message("contestMessage", {args.pageName, '{{button|' .. message("contestButton") .. '}}'}) .. ' '
					out = out .. message('closingWarning', {message('removeSpeedyWarning')}) .. ' '
				end
				if args.willProvide then out = out .. message('undeleteSuggestion', {message('pageIsDeleted'), args.pageName, message(args.drv and 'requestDeletionReview' or 'requestUndeletion')}) end
				out = out .. '|'
				out = out .. message('deletedAfterMessage') .. ' '
				if not args.hideButton then out = out .. message('closingWarning', {message('recreateWarning')}) .. ' ' end
				if args.willProvide then out = out .. message('undeleteSuggestion', {message('pageShouldNotHaveBeenDeleted'), args.pageName, message(args.drv and 'requestDeletionReview' or 'requestUndeletion')}) end
				out = out .. '}}'
			end
		else
			if args.customIntroDeleted then
				out = out .. fillStringWithArgs(args.customIntroDeleted, {
					args.pageName,
					args.deletionReasonsNotice[1]
				}) .. '\n\n'
			elseif args.numberOfEntries == 1 then
				out = out .. message(messageType .. 'MessageDeleted', {
					args.pageName,
					args.deletionReasonsNotice[1]
				}) .. '\n\n'
			elseif args.numberOfEntries > 1 then
				out = out .. message(messageType .. 'MessageDeletedMultiple', {
					args.pageName,
					'\n' .. makeWikiList(args.deletionReasonsNotice)
				}) .. '\n\n'
			else
				out = out .. message(messageType .. 'MessageDeletedMultiple', {
					args.pageName,
					message("seePageForWhy")
				}) .. '\n\n'
			end
			if args.customCloser then
				out = out .. args.customCloser
			else 
				out = out .. message('deletedAfterMessage')
				if not args.hideButton then out = out .. message('closingWarning', {message('recreateWarning')}) .. ' ' end
				if args.willProvide then out = out .. message('undeleteSuggestion', {message('pageShouldNotHaveBeenDeleted'), args.pageName, message(args.drv and 'requestDeletionReview' or 'requestUndeletion')}) end
			end
		end
		
		return mw.getCurrentFrame():preprocess(out)
	else
		if isSubstituted() then
			local out = '{{db/sandbox'
			for k,v in ipairs(params) do
				out = out .. '|' .. v
			end
			out = out .. (args.help and '' or '|help=off')
			out = out .. (args.nocat and '|nocat=yes' or '')
			out = out .. (args.bot and '|bot=yes' or '')
			out = out .. (args.additionalNote and '|additionalnote=' .. args.additionalNote or '')
			out = out .. (args.date and '|date=' .. args.date or '{{safesubst:REVISIONTIMESTAMP}}')
			out = out .. '}}'
			return out
		else
			local titleOfPage = mw.title.getCurrentTitle()
			local pt = pageType._main({page = titleOfPage.fullText})
			local introPrefixToUse = args.bot and 'bot' or 'delete'
			local intro = mw.html.create('span')
				intro:css{["font-style"] = "italic", ["font-weight"] = "bold"}
			if args.numberOfEntries == 1 then
				intro:wikitext(message(introPrefixToUse .. 'Intro', {
					pt,
					args.deletionReasons[1]
				}))
			elseif args.numberOfEntries > 1 then
				intro:wikitext(message(introPrefixToUse .. 'IntroMultiple', {
					pt,
					makeUnorderedList(args.deletionReasons)
				}))
			else
				intro:wikitext(message(introPrefixToUse .. 'IntroMultiple', {
					pt,
					message("noReasonWarning")
				}))
			end
			intro:allDone()
			out = out .. tostring(intro)
			if args.additionalNote then
				out = out .. ' ' .. message('additionalNote', {
					args.additionalNote
				})
			end
			out = out .. '\n'
			if args.hideButton then
				out = out .. '' .. message("removeNoticeNoButton", {pt}) .. ''
			else
				out = out .. '' .. message("removeNotice", {pt, message('removeNoticeWarning', {titleOfPage.isTalkPage and message('checkBelow') or '[[' .. titleOfPage.talkPageTitle.fullText .. '|' .. message('visitTheTalkPage') .. ']]'})}) .. ''
				out = out .. '\n\n'
				if args.numberOfEntries == 1 then
					out = out .. '<div style="margin-left:auto;margin-right:auto;text-align:center;">' .. button.luaMain({
						message("contestButton"),
						class="mw-ui-progressive",
						url="{{fullurl:" .. titleOfPage.talkPageTitle.fullText .. '|action=edit&section=new&preloadtitle={{urlencode:' .. message('contestPreloadTitle') .. '}}&preload={{urlencode:' .. (mw.title.new(message('contestPreload', {args.entries[1].code})).exists and message('contestPreload', {args.entries[1].code}) or message('contestPreloadGeneric')) .. '}}&editintro={{urlencode:' .. message('contestPreloadEditintro') .. '}}}}'
					}) .. '</div>'
				else
					out = out .. '<div style="margin-left:auto;margin-right:auto;text-align:center;">' .. button.luaMain({
						message("contestButton"),
						class="mw-ui-progressive",
						style="text-align:center;",
						url="{{fullurl:" .. titleOfPage.talkPageTitle.fullText .. '|action=edit&section=new&preload={{urlencode:' .. message('contestPreloadGeneric') .. '}}&editintro={{urlencode:' .. message('contestPreloadEditintro') .. '}}}}'
					}) .. '</div>'
				end
				out = out .. '\n\n'
				out = out .. '' .. message("deleteCloser", {
					pt,
					titleOfPage.isTalkPage and message("deleteCloserProvidedBelowNotice") or message("deleteCloserProvidedOnTalkPage")
				}) .. ""
				if (args.help) then
					out = out .. '\n\n'
					local templateCall = '<code><nowiki>{{subst:db|page=' .. titleOfPage.fullText
					for k,v in pairs(params) do
						templateCall = templateCall .. '|' .. k .. '=' .. v
					end
					templateCall = templateCall .. '|notice=yes}} ~~' .. '~~</nowiki></code>'
					out = out .. '' .. message('deleteNoticeTemplate', {templateCall}) .. ''
				end
				out = out .. '\n\n'
				if titleOfPage.talkPageTitle.exists then
					out = out .. '<span class="sysop-show">' .. message("hangOnAdmin", {pt, titleOfPage.isTalkPage and message("checkBelow") or message("hangOnTalkPage")}) .. "</span> "
				else
					out = out .. message('hangOn', {pt})
				end
			end
			if args.numberOfEntries == 1 then
				out = out .. (args.entries[1].notes and '\n\n' .. args.entries[1].notes .. "" or '')
			end
			local deleteReasonSummary = ''
			if args.numberOfEntries > 1 then
				deleteReasonSummary = 'Multiple criteria: '
				local isFirst = true
				for k,v in pairs(args.entries) do
					deleteReasonSummary = v.code and deleteReasonSummary .. (isFirst and '' or ', ') .. '[[WP:CSD#' .. v.code .. '|' .. v.code .. ']]' or ''
					isFirst = false
				end
			elseif args.numberOfEntries == 1 then
				deleteReasonSummary = args.entries[1].code and deleteReasonSummary .. '[[WP:CSD#' .. args.entries[1].code .. '|' .. args.entries[1].code .. ']]' or ''
			end
			if deleteReasonSummary == '' then
				deleteReasonSummary = '[[WP:CSD|Speedy]]'	
			end
			out = out .. '\n\n'
			local lastEditUser = mw.getCurrentFrame():callParserFunction('REVISIONUSER', titleOfPage.fullText)
			local editDate = mw.getCurrentFrame():callParserFunction('#time', 'H:i, j F Y', mw.getCurrentFrame():callParserFunction('REVISIONTIMESTAMP', titleOfPage.fullText))
			out = out .. '<span class="sysop-show">' .. message(args.bot and 'checkBot' or 'check', {args.bot and message('check', {deleteReasonSummary}) or deleteReasonSummary}) .. '</span> ' .. message('lastEdited', {
					'[[User:' .. lastEditUser .. '|' .. lastEditUser .. ']]',
					'<span class="plainlinks">[{{fullurl:' .. titleOfPage.fullText .. '|action=history}} ' .. editDate .. ']</span>',
					timeAgo.main({editDate})
				})
			-- categorize
			out = out .. '[[' .. (args.nocat and ':' or '') .. 'Category:' .. message('defaultCategory') .. ']]'
			for k,v in pairs(args.entries) do
				local categorizeTime = mw.getCurrentFrame():preprocess('{{#time:U|' .. args.date .. ' +' .. (v and v.delayCategorization or 0) .. ' days' .. '}}')
				local currentTime = mw.getCurrentFrame():preprocess('{{#time:U|now}}')
				if currentTime + 0 >= categorizeTime + 0 then
					for l,w in pairs(v and v.categories or {}) do
						out = out .. '[[' .. (args.nocat and ':' or '') .. 'Category:' .. w .. ']]'
					end
				end
			end
			local deletionBoxArgs = {
				type = "speedy",
				text = mw.getCurrentFrame():preprocess(out),
				style = "font-size:95%;word-break:break-word;",
				image = message('level' .. args.highestMessage .. 'icon') == '' and 'none' or '[[' .. message('level' .. args.highestMessage .. 'icon') .. '|40px]]'
			}
			local deletionBox = mbox.main('mbox', deletionBoxArgs)
			local blankedBox = ''
			local hiddenBox = ''
			local blanked = ''
			local hidden = ''
			if args.blank then
				blanked = blanked .. message('blanked')
				if mw.getCurrentFrame():preprocess('{{REVISIONSIZE}}') + 0 >= 35 then
					blanked = blanked .. ' <b>' .. message('pleaseBlank') .. '</b>'
				end
				local blankedBoxArgs = {
					type = "notice",
					text = blanked,
					style = "word-break:break-word;"
				}
				blankedBox = mbox.main('mbox', blankedBoxArgs)
			end
			if args.hide then
				hidden = hidden .. message('hidden')
				local hiddenBoxArgs = {
					type = "notice",
					text = hidden,
					style = "word-break:break-word;"
				}
				hiddenBox = mbox.main('mbox', hiddenBoxArgs)
			end
			if args.hide and not args.nocat then
				hiddenBox = hiddenBox .. '<div style="display:none">'
			end
			return deletionBox .. blankedBox .. hiddenBox
		end
	end
end

p.makeTable = makeInvokeFunc('_makeTable')
function p._makeTable(args)
	local usedCodes = {}
	local tb = mw.html.create("table")
	tb:addClass('wikitable')
	local th = tb:tag('tr')
	th:tag('th'):wikitext('Code'):done()
	th:tag('th'):wikitext('Aliases'):done()
	th:tag('th'):wikitext('Criterion'):done()
	th:tag('th'):wikitext('Name'):done()
	th:tag('th'):wikitext('Description'):done()
	for k,v in pairs(config.deletionReasonsSorting) do
		local entry = getDeletionEntry(v)
		if entry then
			if not usedCodes[v] then
				local tr = tb:tag('tr')
				tr:tag('td'):wikitext(mw.getCurrentFrame():preprocess('<code><nowiki>' .. v .. '</nowiki></code>')):done()
				local aliasStr = ''
				for _,alias in pairs(entry.aliases) do
					aliasStr = aliasStr .. '<code><nowiki>' .. alias .. '</nowiki></code>'
					usedCodes[alias] = true
				end
				tr:tag('td'):wikitext(mw.getCurrentFrame():preprocess(aliasStr))
				tr:tag('td'):wikitext('[[Project:CSD#' .. entry.code .. '|' .. entry.code .. ']]'):done()
				tr:tag('td'):wikitext(entry.name):done()
				tr:tag('td'):wikitext(entry.description):done()
				usedCodes[v] = true
			end
		end
	end
	for k,entry in pairs(config.deletionCodes) do
		if not usedCodes[k] then
			local tr = tb:tag('tr')
			tr:tag('td'):wikitext(mw.getCurrentFrame():preprocess('<code><nowiki>' .. k .. '</nowiki></code>')):done()
			local aliasStr = ''
			for _,alias in pairs(entry.aliases) do
				aliasStr = aliasStr .. '<code><nowiki>' .. alias .. '</nowiki></code>'
				usedCodes[alias] = true
			end
			tr:tag('td'):wikitext(mw.getCurrentFrame():preprocess(aliasStr))
			tr:tag('td'):wikitext('[[Project:CSD#' .. entry.code .. '|' .. entry.code .. ']]'):done()
			tr:tag('td'):wikitext(entry.name):done()
			tr:tag('td'):wikitext(entry.description):done()
			usedCodes[k] = true
		end
	end
	return tostring(tb) .. ''
end

p.makeTableWithExamples = makeInvokeFunc('_makeTableWithExamples')
function p._makeTableWithExamples(args)
	local usedCodes = {}
	local tb = mw.html.create("table")
	tb:addClass('wikitable')
	local th = tb:tag('tr')
	th:tag('th'):css{position = "sticky", top = 0, left = 0}:wikitext('Codes'):done()
	--th:tag('th'):wikitext('Criterion'):done()
	th:tag('th'):css{position = "sticky", top = 0}:wikitext('Deletion message'):done()
	th:tag('th'):css{position = "sticky", top = 0}:wikitext('Deletion notice'):done()
	for k,v in pairs(config.deletionReasonsSorting) do
		local entry = getDeletionEntry(v)
		if entry then
			if not usedCodes[v] then
				local tr = tb:tag('tr')
				local aliasStr = ''
				for _,alias in pairs(entry.aliases) do
					aliasStr = aliasStr .. '<br/><code><nowiki>{{db|' .. alias .. '}}</nowiki></code>'
					usedCodes[alias] = true
				end
				tr:tag('td'):css{position = "sticky", left = 0}:wikitext(mw.getCurrentFrame():preprocess('<code><nowiki>{{db|' .. v .. '}}</nowiki></code>' .. aliasStr)):done()
				--tr:tag('td'):wikitext('[[Project:CSD#' .. entry.code .. '|' .. entry.code .. ']]'):done()
				tr:tag('td'):wikitext('<div style="width:700px;">' .. mw.getCurrentFrame():preprocess('{{#invoke:Speedy|main|nocat=yes|' .. v .. '}}' .. '</div>' )):done()
				tr:tag('td'):wikitext(mw.getCurrentFrame():preprocess('<div style="width:700px;">' .. '{{#invoke:Speedy|main|page=Sandbox|notice=yes|' .. v .. '}}' .. '</div>')):done()
				usedCodes[v] = true
			end
		end
	end
	for v,entry in pairs(config.deletionCodes) do
		if not usedCodes[v] then
			local tr = tb:tag('tr')
			local aliasStr = ''
			for _,alias in pairs(entry.aliases) do
				aliasStr = aliasStr .. '<br/><code><nowiki>{{db|' .. alias .. '}}</nowiki></code>'
				usedCodes[alias] = true
			end
			tr:tag('td'):css{position = "sticky", left = 0}:wikitext(mw.getCurrentFrame():preprocess('<code><nowiki>{{db|' .. v .. '}}</nowiki></code>\n' .. aliasStr)):done()
			--tr:tag('td'):wikitext('[[Project:CSD#' .. entry.code .. '|' .. entry.code .. ']]'):done()
				tr:tag('td'):wikitext('<div style="width:700px;">' .. mw.getCurrentFrame():preprocess('{{#invoke:Speedy|main|nocat=yes|' .. v .. '}}' .. '</div>' )):done()
				tr:tag('td'):wikitext(mw.getCurrentFrame():preprocess('<div style="width:700px;">' .. '{{#invoke:Speedy|main|page=Sandbox|notice=yes|' .. v .. '}}' .. '</div>')):done()
			usedCodes[v] = true
		end
	end
	return tostring(tb) .. ''
end

return p