Module:Speedy
| Pre-alpha | This module is rated as pre-alpha. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure. | 
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.
Usage edit source
{{#invoke:Speedy|function_name}}
Full Table of Reasons edit source
See config page to edit
| Code | Aliases | Criterion | Name | Description | 
|---|---|---|---|---|
| g1 | nonsense | G1 | Patent nonsense | as a page that is patent nonsense, consisting purely of incoherent text or gibberish with no meaningful content or history | 
| g2 | test | G2 | Test page | as a test page | 
| g3 | vandalismhoax | G3 | Pure vandalism | as pure vandalism or a blatant hoax | 
| g4 | repost | G4 | Recreation of a page that was deleted per a deletion discussion | as 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 | 
| g5 | bannedblocked | G5 | Block or ban evasion | as 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 | 
| g6 | G6 | Housekeeping or uncontroversial maintenance | as a page that needs to be deleted to perform uncontroversial maintenance tasks, such as merging page histories or reversing redirects$2 | |
| g7 | authorblankedself | G7 | Author requests deletion | as a page where the author of the only substantial content has requested deletion and/or blanked the page in good faith | 
| g8 | G8 | Page dependent on non-existent page | as a page that is dependent on a page that has never existed or has been deleted$2 | |
| g9 | G9 | Office action | to enforce an office action | |
| g10 | attackattackorgpersonal attack | G10 | Attack page | as a page that serves no other purpose but to disparage or threaten its subject or some other entity | 
| g11 | promospam | G11 | Unambiguous advertising or promotion | because 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 | 
| g12 | copyvio | G12 | Unambiguous copyright infringement | as a copyright infringement$2 | 
| g13 | afcblankdraft | G13 | Abandoned AfC drafts | as 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 | 
| g14 | disambig | G14 | Unnecessary disambiguation | as 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 | 
| a1 | nocontext | A1 | No context | as a very short article lacking sufficient context to identify the subject of the article | 
| a2 | foreign | A2 | Foreign-language articles that exist on another Wikimedia project | as a foreign-language article that exists on another Wikimedia project$2 | 
| a3 | nocontentcontact | A3 | No content | as 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 | 
| a5 | transwiki | A5 | Transwikied articles | as an article that has already been transwikied (e.g., to Wiktionary or Wikisource)$2 | 
| a7 | A7 | No 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 | |
| a9 | albumsongdiscog | A9 | No 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 | 
| a10 | samedup | A10 | Recently created article that duplicates an existing topic | as 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 | 
| a11 | inventedmadeup | A11 | Obviously invented | as 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 | 
| r2 | rediruserredirdraft | R2 | Cross namespace redirect | as a redirect from the main/article space to any other namespace except the Category:, Template:, Wikipedia:, Help: and Portal: namespaces | 
| r3 | redirtypotypomisnomer | R3 | Implausible typo | as a recently created redirect from an implausible typo or misnomer, which is not in another language pertinent to the topic | 
| r4 | redircom | R4 | File namespace redirect that matches a Wikimedia Commons page | as a redirect in the File namespace that has the same name as a page on Wikimedia Commons: Commons:$1 and no incoming file links | 
| f1 | redundantfile | F1 | Redundant | as 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}}}}}}|}} | 
| f2 | nofilefpcfail | F2 | Corrupt, missing, or empty file | as a corrupt or empty file, or a file description page for a file on Commons | 
| f3 | noncom | F3 | Improper license | as an image or other media file licensed with an unacceptable license | 
| f4 | nsdnld | F4 | Lack of licensing information | as an image or other media file missing important licensing information, including the source and the licensing status | 
| f5 | orfud | F5 | Orphaned non-free file | as a non-free file that is currently not being used in any articles | 
| f5imm | F5 | Orphaned 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 | |
| f6 | nrd | F6 | Missing non-free use rationale | as a non-free file that has no explanation as to why the file meets the non-free content criteria | 
| f7 | badfairuse | F7 | Invalid fair use claim | as a non-free file from a commercial source (e.g. Associated Press, Getty), where the file itself is not the subject of sourced commentary | 
| f8 | nowcommons | F8 | Available as identical copy on Wikimedia Commons | as a file that is available as an identical copy of a file on Wikimedia Commons$2 | 
| f9 | filecopyvio | F9 | Unambiguous file copyright infringement | because 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 | 
| f10 | badfiletype | F10 | Useless non-media file | as a file other than an image, audio, or video file, which is not used in any articles and has no encyclopedic use | 
| f11 | npd | F11 | No evidence of permission | as a file in which the copyright holder has not provided evidence of permission or ownership | 
| c1 | catempty | C1 | Empty category | as 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. | 
| u1 | userreq | U1 | User request | as a user page or subpage requested to be deleted by its user | 
| u2 | nouser | U2 | User page of non-existent user | as a user page, subpage, or talk page of a user that does not exist ([[Special:CentralAuth/{{BASEPAGENAMEE}}|check]]) | 
| u5 | webhostnotwebhost | U5 | Misuse of Wikipedia as a webhost | as 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 | 
| p1 | P1 | Portal that would be eligible for speedy deletion as an article | as a portal page which would qualify for speedy deletion $2 if it were an article | |
| p2 | emptyportal | P2 | Underpopulated portal | as 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§ion=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§ion=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
