Permanently protected module
From Wikipedia, the free encyclopedia


-------------------------------------------------------------------------------

--              Configuration data for [[Module:Article history]]

-------------------------------------------------------------------------------



local lang = mw.language.getContentLanguage()

local Category = require('Module:Article history/Category')



-------------------------------------------------------------------------------

-- Helper functions

-------------------------------------------------------------------------------



-- Makes a link to a template page surrounded by double curly braces. A

-- workalike for the {{tl}} template.

local function makeTemplateLink(s)

	local openb = mw.text.nowiki('{{')

	local closeb = mw.text.nowiki('}}')

	return string.format('%s[[Template:%s|%s]]%s', openb, s, s, closeb)

end



-- Gets the Good Article topic for the given key. Uses

-- [[Module:Good article topics]].

local function getGoodArticleTopic(key)

	if not key then

		return nil

	end

	return require('Module:Good article topics')._main(key)

end



-- Returns the Good Article page link and display value for a given Good Article

-- key. If the key wasn't valid, the default Good Article page and display value

-- is returned instead.

local function getGoodArticleTopicLink(key)

	local topic = getGoodArticleTopic(key)

	local link, display

	if topic then

		link = 'Wikipedia:Good articles/' .. topic

		display = topic .. ' good articles'

	else

		link = 'Wikipedia:Good articles'

		display = 'good articles'

	end

	return link, display

end



-- Wrapper function for mw.language:formatDate, going through pcall to catch

-- invalid input errors.

local function getDate(format, date)

	local success, result = pcall(lang.formatDate, lang, format, date)

	if success then

		return result

	end

end



-- Gets the date in the format YYYYMMDD, as a number. Months and dates are

-- zero-padded. Results from this function are intended to be used in date

-- calculations.

local function getYmdDate(date)

	date = getDate('Ymd', date)

	if date then

		return tonumber(date)

	else

		return nil

	end

end



-- Gets the date in the format Month d, YYYY.

local function getLongDate(date)

	return getDate('F j, Y', date)

end



-- Returns true if the given page is an existing title, and false or nil

-- otherwise

local function titleExists(page)

	local success, title = pcall(mw.title.new, page)

	return success and title.exists

end



-- Returns a truthy value if a date parameter for the given prefix has been

-- provided by the user.

local function isActiveDatedObject(articleHistoryObj, prefix)

	local args = articleHistoryObj.args

	local prefixArgs = articleHistoryObj.prefixArgs

	return argsprefix .. 'date' or prefixArgsprefix

end



-- Returns a date as formatted by getLongDate. If the date is invalid, it raises

-- an error using param as the parameter name containing the invalid date.

local function validateDate(param, date, articleHistoryObj)

	local longDate = getLongDate(date)

	if longDate then

		return longDate

	else

		articleHistoryObj:raiseError(

			string.format(

				"invalid date '%s' detected in parameter '%s'",

				tostring(date),

				param

			),

			'Template:Article history#Invalid date'

		)

	end

end



-- Generates a data table for a date-related notice such as DYK and ITN. prefix

-- is the parameter prefix for that notice type (e.g. "dyk"), and suffixes is

-- an array of parameter suffixes in addition to "date" that is used by that

-- notice type (e.g. "entry" for the "dykentry" and "dyk2entry" parameters).

local function makeDateData(articleHistoryObj, prefix, suffixes)

	local args = articleHistoryObj.args

	local prefixArgs = articleHistoryObj.prefixArgs



	-- Sanity checks

	if prefixArgsprefix then

		for _, t in ipairs(prefixArgsprefix]) do

			if not t.date then

				articleHistoryObj:raiseError(

					string.format(

						"an argument starting with '%s%d' was detected, " ..

							"but no '%s%ddate' parameter was specified",

						prefix, t1],

						prefix, t1

					),

					'Template:Article history#No date parameter'

				)

			end

		end

	end



	local data = {}



	-- Organise the input

	local function addData(sep)

		local t = {}

		local argPrefix = prefix .. sep

		do

			local key = argPrefix .. 'date'

			t.date = validateDate(key, argskey], articleHistoryObj)

			t.month, t.day, t.year = t.date:match('(%a+) (%d+), (%d+)')

			t.day = tonumber(t.day)

			t.year = tonumber(t.year)

			t.ymdDate = getYmdDate(t.date)

		end

		for _, suffix in ipairs(suffixes) do

			local key = argPrefix .. suffix

			tsuffix = argskey

		end

		t.argPrefix = argPrefix

		data#data + 1 = t

	end

	if argsprefix .. 'date' then

		addData('')

	end

	if prefixArgsprefix then

		for _, prefixData in ipairs(prefixArgsprefix]) do

			addData(tostring(prefixData1]))

		end

	end

	if #data < 1 then

		error(string.format(

			"no data items found for prefix '%s' and parameter checks failed'",

			tostring(prefix)

		))

	end



	return data

end



-- This makes the text for Main Page features such as DYKs and ITNs for the

-- dates contained in dateData (made with the makeDateData function).

-- The parameter $1 in the blurb will be replaced with the list of dates.

local function makeDateText(dateData, blurb, wantBold)

	local bold = wantBold and "'''" or ""

	local dates, doneLinks = {}, {}

	for i, t in ipairs(dateData) do

		local date

		if t.link and not doneLinkst.link then

			date = string.format('[[%s|%s]]', t.link, t.date)

			doneLinkst.link = true

		else

			date = t.date

		end

		datesi = bold .. date .. bold

	end

	local dateList = mw.text.listToText(dates, ', ', ', and ')

	return mw.message.newRawMessage(blurb, dateList):plain()

end



return {



-------------------------------------------------------------------------------

--                             CONFIG TABLE START

-------------------------------------------------------------------------------



-------------------------------------------------------------------------------

-- Statuses

-- Configuration for possible current statuses of the article.

-------------------------------------------------------------------------------



-- The statuses table contain configuration tables for possible current statuses

-- of the article.

-- Each table can have the following fields:

--

-- id: the main ID for the status. This should be the same as the configuration

--    table key.

-- aliases: a table of ID aliases that can be used to access the config table.

-- icon: The status icon.

-- iconSize: The icon size, including "px" suffix. The default is defined in

--    defaultStatusIconSize.

-- iconMultiSize: The icon size if we are outputting multiple status rows. The

--    default is defaultMultiStatusIconSize.

-- text: The status text. This may be a string or a function. If it is a

--    function, it takes an article history object as input, and should return

--    the text string. If it is a string, it can have the following parameters:

--    $1 - The full page name of the article or subject page

--    $2 - The page name without the namespace name

-- categories: The categories set by the status. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter, and the current status object as

--    the second parameter, and should return an array of category objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. The status

--    name is used by default. This can be a string or a function. If it is a

--    function, it takes an article history object as its first parameter, and

--    should return the caption text. If this is absent, the icon caption is

--    used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



statuses = {

	FA = {

		id = 'FA',

		name = 'Featured article',

		icon = 'Featured article star.svg',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local actions = articleHistoryObj:getActionObjects()

			local link

			for i = #actions, 1, -1 do

				local actionObj = actionsi

				if actionObj.id == 'FAC' then

					link = actionObj.link

					break

				end

			end

			link = link or 'Wikipedia:Featured article candidates/' .. articlePage

			local text = "'''%s''' is a [[Wikipedia:Featured articles|featured article]]; " ..

				"it (or a previous version of it) has been '''[[%s|identified]]''' " ..

				"as one of the best articles produced by the [[Wikipedia:Wikipedians|Wikipedia community]]. " ..

				"Even so, if you can update or improve it, [[Wikipedia:Be bold|please do so]]."

			return string.format(text, articlePage, link)

		end,

		categories = {'Wikipedia featured articles'}

	},

	FFA = {

		id = 'FFA',

		name = 'Former featured article',

		icon = 'Featured article star - cross.svg',

		iconSize = '48px',

		text = "'''$1''' is a [[Wikipedia:Former featured articles|former featured article]]. " ..

			"Please see the links under Article milestones below for its original nomination page " ..

			"(for older articles, check [[Wikipedia:Featured article candidates/Archived nominations/Index|the nomination archive]]) " ..

			"and why it was removed.",

		categories = {'Wikipedia former featured articles'}

	},

	FFAC = {

		id = 'FFAC',

		name = 'Former featured article candidate',

		aliases = {'FACFAILED'},

		icon = 'Cscr-former.svg',

		text = "'''$1''' is a former [[Wikipedia:Featured article candidates|featured article candidate]]. " ..

			"Please view the links under Article milestones below to see why " ..

			"the nomination failed. For older candidates, please check the " ..

			"[[Wikipedia:Featured article candidates/Archived nominations/Index|archive]]."

	},

	FL = {

		id = 'FL',

		name = 'Featured list',

		icon = 'Featured article star.svg',

		iconSize = '48px',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local actions = articleHistoryObj:getActionObjects()

			local link

			for i = #actions, 1, -1 do

				local actionObj = actionsi

				if actionObj.id == 'FLC' then

					link = actionObj.link

					break

				end

			end

			link = link or 'Wikipedia:Featured list candidates/' .. articlePage

			local text = "'''%s''' is a [[Wikipedia:Featured lists|featured list]], " ..

				"which means it has been '''[[%s|identified]]''' as one of the best " ..

				"[[Wikipedia:Stand-alone lists|lists]] produced by the [[Wikipedia:Wikipedians|Wikipedia community]]. " ..

				"If you can update or improve it, [[Wikipedia:Be bold|please do so]]."

			return string.format(text, articlePage, link)

		end,

		categories = {'Wikipedia featured lists'}

	},

	FFL = {

		id = 'FFL',

		name = 'Former featured list',

		icon = 'Cscr-featured-strike.svg',

		text = "'''$1''' is a [[Wikipedia:Former featured lists|former featured list]]. " ..

			"Please see the links under Article milestones below for its original " ..

			"nomination page and why it was removed. If it has improved again to " ..

			"[[Wikipedia:Featured list criteria|featured list standard]], you may " ..

			"[[Wikipedia:Featured list candidates|renominate]] the article to " ..

			"become a [[Wikipedia:Featured lists|featured list]]."

	},

	FFLC = {

		id = 'FFLC',

		name = 'Former featured list candidate',

		icon = 'Cscr-former.svg',

		iconCaption = 'Former FLC',

		text = "'''$1''' is a former [[Wikipedia:Featured list candidates|featured list candidate]]. " ..

			"Please view the link under Article milestones below to see why the nomination failed. " ..

			"Once the objections have been addressed you may " ..

			"[[Wikipedia:Featured list candidates#Resubmitting nominations|resubmit]] " ..

			"the article for featured list status.",

		categories = {'Wikipedia featured list candidates (contested)'}

	},

	'FFA/GA' = {

		id = 'FFA/GA',

		name = 'Former featured article, current good article',

		isMulti = true,

		statuses = {'FFA', 'GA'}

	},

	'FFAC/GA' = {

		id = 'FFAC/GA',

		name = 'Former featured article candidate, current good article',

		isMulti = true,

		statuses = {'FFAC', 'GA'}

	},

	GA = {

		id = 'GA',

		name = 'Good article',

		icon = 'Symbol support vote.svg',

		iconSize = '40px',

		iconMultiSize = '25px',

		text = function (articleHistoryObj)

			local link, display = getGoodArticleTopicLink(articleHistoryObj.args.topic)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local text = "'''%s''' has been listed as one of the '''''[[%s|%s]]''''' " ..

				"under the [[Wikipedia:Good article criteria|good article criteria]]. " ..

				"If you can improve it further, [[Wikipedia:Be bold|please do so]]. " ..

				"<small>''If it no longer meets these criteria, you can " ..

				"'''[[Wikipedia:Good article reassessment|reassess]]''' it''.</small>"

			return string.format(text, articlePage, link, display)

		end,

		categories = function (articleHistoryObj)

			local ret = {}

			local title = articleHistoryObj.currentTitle

			if title.namespace == 1 then

				ret#ret + 1 = Category.new('Wikipedia good articles')

				local topic = getGoodArticleTopic(articleHistoryObj.args.topic)

				if topic then

					ret#ret + 1 = Category.new(

						topic .. ' good articles',

						title.text

					)

				else

					ret#ret + 1 = Category.new(

						'Good articles without topic parameter',

						title.text

					)

				end

			end

			return ret

		end

	},

	FGAN = {

		id = 'FGAN',

		name = 'Former good article nominee',

		aliases = {'FAILEDGA'},

		icon = 'Symbol oppose vote.svg',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local link, display = getGoodArticleTopicLink(articleHistoryObj.args.topic)

			local text = "'''%s''' was a '''''[[%s|%s]]''''' nominee, " ..

				"but did not meet the [[Wikipedia:Good article criteria|good article criteria]] " ..

				"at the time. There may be suggestions below for improving the article. " ..

				"Once these issues have been addressed, the article can be " ..

				"[[Wikipedia:Good article nominations|renominated]]. " ..

				"Editors may also seek a '''[[Wikipedia:Good article reassessment|reassessment]]''' " ..

				"of the decision if they believe there was a mistake."

			return string.format(text, articlePage, link, display)

		end,

		categories = {'Former good article nominees'}

	},

	DGA = {

		id = 'DGA',

		name = 'Delisted good article',

		aliases = {'DELISTEDGA'},

		icon = 'Symbol unsupport vote.svg',

		iconCaption = 'Former good article',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local link, display = getGoodArticleTopicLink(articleHistoryObj.args.topic)

			local text = "'''%s''' was one of the '''''[[%s|%s]]''''', " ..

				"but it has been removed from the list. " ..

				"There are suggestions below for improving the article to meet the " ..

				"[[Wikipedia:Good article criteria|good article criteria]]. " ..

				"Once these issues have been addressed, the article can be " ..

				"[[Wikipedia:Good article nominations|renominated]]. " ..

				"Editors may also seek a '''[[Wikipedia:Good article reassessment|reassessment]]''' " ..

				"of the decision if they believe there was a mistake."

			return string.format(text, articlePage, link, display)

		end,

		categories = {'Delisted good articles'}

	},

	FFT = {

		id = 'FFT',

		name = 'Part of former featured topic',

		icon = 'Cscr-featured-strike.svg',

		iconCaption = 'Former featured topic',

		text = "This article is part of a " ..

			"''[[Wikipedia:Former featured topics|former featured topic]]'' series. " ..

			"If it has improved again to " ..

			"[[Wikipedia:Featured topic criteria|featured topic standard]], " ..

			"you may [[Wikipedia:Featured topic candidates|renominate]] " ..

			"the topic to become a [[Wikipedia:Featured topic|featured topic]]."

	},

	FFTC = {

		id = 'FFTC',

		name = 'Former featured topic candidate',

		icon = 'Cscr-former.svg',

		text = "This article is part of a ''former'' " ..

			"[[Wikipedia:Featured topic candidates|featured topic candidate]]. " ..

			"Please view the links under Article milestones below to see why " ..

			"the nomination failed."

	},

	FPO = {

		id = 'FPO',

		name = 'Featured portal',

		icon = 'Cscr-featured.svg',

		text = "The '''$2 Portal''' is a [[Wikipedia:Featured portals|featured portal]], " ..

			"which means it has been " ..

			"'''[[Wikipedia:Featured portal candidates/Portal:$2|identified]]''' " ..

			"as one of the best portals on [[Wikipedia]]. " ..

			"If you see a way this portal can be updated or improved without " ..

			"compromising previous work, please feel free to contribute.",

		categories = function (articleHistoryObj)

			return {Category.new(

				'Wikipedia featured portals',

				articleHistoryObj.currentTitle.text

			)}

		end

	},

	FFPO = {

		id = 'FFPO',

		name = 'Former featured portal',

		icon = 'Featured article star - cross.svg',

		text = "This portal is a [[Wikipedia:Former featured portals|former featured portal]]. " ..

			"Please see the links under Portal milestones below for its " ..

			"original nomination page and why it was removed.",

		categories = function (articleHistoryObj)

			return {Category.new(

				'Wikipedia former featured portals',

				articleHistoryObj.currentTitle.text

			)}

		end

	},

	FFPOC = {

		id = 'FFPOC',

		name = 'Former featured portal candidate',

		icon = 'Cscr-former.svg',

		text = "This portal is a '''''former''''' " ..

			"[[Wikipedia:Featured portal candidates|featured portal candidate]]. " ..

			"Please see the links under Portal milestones below for its " ..

			"original nomination page and why the nomination failed."

	},

	PR = {

		-- Peer review is a valid current status, but it doesn't trigger a

		-- header row.

		id = 'PR',

		name = 'Peer reviewed',

		noticeBarIcon = 'Nuvola apps kedit.svg'

	},

	NA = {

		-- A valid current status, but doesn't trigger a header row.

		id = 'NA',

		noticeBarIcon = 'Nuvola apps kedit.svg'

	},

	-- The following are invalid statuses.

	FAC = {

		id = 'FAC',

		text = function (articleHistoryObj)

			articleHistoryObj:raiseError(

				string.format(

					'use the template %s to nominate an article for Featured article status',

					makeTemplateLink('fac')

				),

				'Template:Article history#Featured article candidates'

			)

		end

	},

	FAR = {

		id = 'FAR',

		text = function (articleHistoryObj)

			articleHistoryObj:raiseError(

				string.format(

					'use the template %s to nominate an article for Featured article review',

					makeTemplateLink('FAR')

				),

				'Template:Article history#Featured article review'

			)

		end

	},

	STUB = {

		id = 'STUB',

		aliases = {'START', 'B', 'A'},

		text = function (articleHistoryObj)

			local currentStatusParam = articleHistoryObj.cfg.currentStatusParam

			articleHistoryObj:raiseError(

				string.format(

					"do not use '%s' as value of the '%s' parameter; these " ..

						'assessments are the responsibility of individual ' ..

						'WikiProjects',

					articleHistoryObj.argscurrentStatusParam],

					currentStatusParam

				),

				'Template:Article history#WikiProject assessments'

			)

		end

	},

},



-- This function allows the generation of custom status ID. It takes an

-- articleHistory object as the first parameter, and should output the status

-- ID.

getStatusIdFunction = function (articleHistoryObj)

	-- Get the status ID. The status code is the code passed in from the

	-- arguments, and the ID is the value contained in the config.

	local statusCode = articleHistoryObj.argsarticleHistoryObj.cfg.currentStatusParam

	local statusId = articleHistoryObj:getStatusIdForCode(statusCode)



	-- Check for former featured articles.

	if statusId ~= 'FA'

		and statusId ~= 'FL'

		and statusId ~= 'FFA'

		and statusId ~= 'FFL'

		and statusId ~= 'FFA/GA'

	then

		local ffaObj

		local actions = articleHistoryObj:getActionObjects()

		for i = #actions, 1, -1 do

			local actionObj = actionsi

			if actionObj.id == 'FAR' and actionObj.resultId == 'demoted' then

				ffaObj = actionObj

				break

			end

		end

		if ffaObj then

			if not statusId then

				articleHistoryObj:raiseError(

					'former featured articles should have a current status',

					'Template:Article history#Former featured articles'

				)

			elseif statusId == 'GA' then

				statusId = 'FFA/GA'

			elseif statusId == 'DGA' then

				statusId = 'FFA'

			else

				articleHistoryObj:raiseError(

					string.format(

						"'%s' is not a valid current status for former featured articles",

						tostring(statusCode)

					),

					'Template:Article history#Former featured articles'

				)

			end

		end

	end



	return statusId

end,



-------------------------------------------------------------------------------

-- Notices

-------------------------------------------------------------------------------



-- The notices table contains configuration tables for notices about the article

-- that are unrelated to its current status.

-- Each configuration table can have the following fields:

--

-- id: the main ID for the notice. This should be the same as the configuration

--    table key.

-- isActive: a function that should return a truthy value if the notice should

--    be displayed, and a falsy value if not. (Falsy values are false and nil,

--    and truthy values are everything else.) The function takes an article

--    history object as its first parameter.

-- makeData: a function that should return a table of data to be used by other

--    functions in this notice configuration table. It can be accessed using

--    noticeObj:getData().

-- icon: the filename of the notice icon, minus the "File:" prefix.

-- iconCaption: the icon caption.

-- iconSize: The icon size, including "px" suffix. The default is defined in

--    defaultIconSize.

-- text: The notice text. This may be a string or a function. If it is a

--    function, it takes an article history object as the first parameter, and

--    the current notice object as the second parameter, and should return the

--    text string.

-- categories: The categories set by the notice. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter, and the current notice object as

--    the second parameter, and should return an array of category objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. This can be

--    a string or a function. If it is a function, it takes an article history

--    object as its first parameter, and should return the caption text. If this

--    is absent, the icon caption is used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



notices = {

	{

		id = 'FT',

		isActive = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local prefixArgs = articleHistoryObj.prefixArgs

			-- ftmain is included here because it leads to better error

			-- messages than leaving it out, even though ftmain by itself is

			-- invalid.

			return args.ftname or args.ftmain or prefixArgs.ft

		end,

		makeData = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local prefixArgs = articleHistoryObj.prefixArgs

			local data = {}

			local getTopicStatus = require('Module:FeaturedTopicSum').status

			local yesno = require('Module:Yesno')



			local function makeTopicData(name, isMain, paramNum)

				if name then

					return {

						name = name,

						isMain = yesno(isMain) or false,

						status = getTopicStatus(name),

						paramNum = paramNum

					}

				elseif isMain then

					local num = paramNum and tostring(paramNum) or ''

					articleHistoryObj:raiseError(

						string.format(

							"parameter 'ft%smain' is set, but no featured " ..

								"topic name is set in parameter 'ft%sname'",

							num, num

						),

						'Template:Article history#Featured topic names'

					)

				else

					return nil

				end

			end

			data#data + 1 = makeTopicData(args.ftname, args.ftmain)

			if prefixArgs.ft then

				for _, t in ipairs(prefixArgs.ft) do

					if t1 > 1 then -- we use args.ftname instead of args.ft1name

						data#data + 1 = makeTopicData(t.name, t.main, t1])

					end

				end

			end



			-- Check for rogue ft.. parameters

			if #data < 1 then

				articleHistoryObj:raiseError(

					"a parameter starting with 'ft' was detected, but no " ..

						"featured topic names were specified; " ..

						"please check the parameter names",

					'Template:Article history#Featured topic names'

				)

			end



			-- Find if one of the topics is featured.

			local isInFeaturedTopic = false

			for _, topic in ipairs(data) do

				if topic.status == 'FT' then

					isInFeaturedTopic = true

					break

				end

			end

			data.isInFeaturedTopic = isInFeaturedTopic



			return data

		end,

		icon = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			if data.isInFeaturedTopic then

				return 'Cscr-featuredtopic.svg'

			else

				return 'Support cluster.svg'

			end

		end,

		iconCaption = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			if data.isInFeaturedTopic then

				return 'Featured topic star'

			else

				return 'Good topic star'

			end

		end,

		iconSize = '48px',

		text = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local article = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText



			local firstBlurb = "'''%s''' is %s the '''[[Wikipedia:Featured topics/%s|%s]] series''', %s."

			local otherBlurb = "It is also %s the '''[[Wikipedia:Featured topics/%s|%s]] series''', %s."

			local finalBlurb = "%s identified as among the best series of " ..

				"articles produced by the [[Wikipedia:Wikipedians|Wikipedia community]]. " ..

				"If you can update or improve %s, [[Wikipedia:Be bold|please do so]]."

			local main = 'the main article in'

			local notMain = 'part of'

			local featuredLink = 'a [[Wikipedia:Featured topics|featured topic]]'

			local featuredNoLink = 'a featured topic'

			local goodLink = 'a [[Wikipedia:Good topics|good topic]]'

			local goodNoLink = 'a good topic'

			local thisSingular = 'This is'

			local thisPlural = 'These are'

			local itSingular = 'it'

			local itPlural = 'them'



			local hasFeaturedLink = false

			local hasGoodLink = false

			local text = {}



			-- First topic

			do

				local topic = data1

				local link

				if topic.status == 'FT' then

					link = featuredLink

					hasFeaturedLink = true

				else

					link = goodLink

					hasGoodLink = true

				end

				text#text + 1 = string.format(

					firstBlurb,

					article,

					topic.isMain and main or notMain,

					topic.name,

					topic.name,

					link

				)

			end



			-- Other topics

			for i = 2, #data do

				local topic = datai

				local link

				if topic.status == 'FT' then

					if hasFeaturedLink then

						link = featuredNoLink

					else

						link = featuredLink

						hasFeaturedLink = true

					end

				else

					if hasGoodLink then

						link = goodNoLink

					else

						link = goodLink

						hasGoodLink = true

					end

				end

				text#text + 1 = string.format(

					otherBlurb,

					topic.isMain and main or notMain,

					topic.name,

					topic.name,

					link

				)

			end



			-- Final blurb

			do

				local isPlural = #data > 1

				text#text + 1 = string.format(

					finalBlurb,

					isPlural and thisPlural or thisSingular,

					isPlural and itPlural or itSingular

				)

			end



			return table.concat(text, ' ')

		end,

		categories = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local status = articleHistoryObj:getStatusId()

			local article = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local cats = {}



			local function addCat(cat, sort)

				cats#cats + 1 = Category.new(cat, sort)

			end



			-- Page-wide status categories

			if status == 'FA' then

				addCat('FA-Class Featured topics articles')

			elseif status == 'FL' then

				addCat('FL-Class Featured topics articles')

			elseif status == 'FFA/GA' or status == 'FFAC/GA' or status == 'GA' then

				addCat('GA-Class Featured topics articles')

			else

				addCat('Unassessed Featured topics articles')

			end



			-- Topic-specific status categories

			local function addTopicCats(catFormat)

				for _, topic in ipairs(data) do

					addCat(string.format(catFormat, topic.name))

				end

			end

			if status == 'FA' or status == 'FL' then

				addTopicCats('Wikipedia featured topics %s featured content')

			elseif status == 'FFA/GA' or 'GA' then

				addTopicCats('Wikipedia featured topics %s good content')

			else

				addTopicCats('Wikipedia featured topics %s')

			end



			-- Importance categories

			local hasTop, hasHigh, hasMid, hasLow -- These check for dupes

			for _, topic in ipairs(data) do

				local cat, sort

				if topic.status == 'FT' then

					if topic.isMain and not hasTop then

						cat = 'Top-importance Featured topics articles'

						sort = topic.name .. ' ' .. article

						hasTop = true

					elseif not topic.isMain and not hasHigh then

						cat = 'High-importance Featured topics articles'

						hasHigh = true

					end

				else

					if topic.isMain and not hasMid then

						cat = 'Mid-importance Featured topics articles'

						sort = topic.name .. ' ' .. article

						hasMid = true

					elseif not topic.isMain and not hasLow then

						cat = 'Low-importance Featured topics articles'

						hasLow = true

					end

				end

				if cat then

					addCat(cat, sort)

				end

			end



			return cats

		end

	},



	-- Main page date

	{

		id = 'MAINDATE',

		isActive = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local status = articleHistoryObj:getStatusId()

			return args.maindate or status == 'FA' or status == 'FL'

		end,

		makeData = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local status = articleHistoryObj:getStatusId()

			local data = {}



			local function validateMainDate(argName, dataName, dataTimestampName)

				datadataName = argsargName

				if datadataName then

					datadataTimestampName = getYmdDate(datadataName])

					if not datadataTimestampName then

						articleHistoryObj:raiseError(

							string.format(

								"invalid date '%s' detected in parameter '%s'",

								datadataName],

								argName

							),

							'Template:Article history#Invalid date'

						)

					end

				end

			end



			validateMainDate('maindate', 'mainDate', 'mainDateTimestamp')

			if data.mainDate then

				validateMainDate('maindate2', 'mainDate2', 'mainDate2Timestamp')

				if data.mainDate2 and data.mainDateTimestamp >= data.mainDate2Timestamp then

					articleHistoryObj:raiseError(

						"the date in the 'maindate' parameter must be earlier than the date in the 'maindate2' parameter",

						'Template:Article history#Main Page date order'

					)

				end

			end



			data.currentTimestamp = getYmdDate()





			-- Whether the page is a list or not for the purposes of the Main

			-- Page. The first Today's Featured List was on 13 June 2011, so

			-- lists that were featured before then count as articles.

			data.isList = (status == 'FL' or status == 'FFL')

				and (not data.mainDate or data.mainDateTimestamp >= 20110613)



			return data

		end,

		icon = 'Wikipedia-logo-v2.svg',

		iconCaption = 'Main Page trophy',

		text = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data or not data.mainDate then

				return nil

			end



			-- Build the blurb for all the possible combinations of past,

			-- present and future appearances of maindate and maindate2.

			local pagetype = data.isList and 'list' or 'article'

			local mainDateLong = getLongDate(data.mainDate)

			local mainDate2Long = data.mainDate2 and getLongDate(data.mainDate2)

			local todaysFA = "Today's featured " .. pagetype



			local function makeFeaturedLink(date, display)

				return string.format(

					"[[Wikipedia:Today's featured %s/%s|%s]]",

					pagetype,

					date,

					display or date

				)

			end



			local function isPast(timestamp)

				return timestamp < data.currentTimestamp

			end



			local function isCurrent(timestamp)

				return timestamp == data.currentTimestamp

			end



			local function isFuture(timestamp)

				return timestamp > data.currentTimestamp

			end



			if data.mainDate2 then

				if isPast(data.mainDateTimestamp) then

					if isPast(data.mainDate2Timestamp) then

						return string.format(

							"This article appeared on Wikipedia's Main Page as %s on %s, and on %s.",

							todaysFA,

							makeFeaturedLink(mainDateLong),

							makeFeaturedLink(mainDate2Long)

						)

					elseif isCurrent(data.mainDate2Timestamp) then

						return string.format(

							"This article is currently on Wikipedia's Main Page as %s. It also appeared previously on %s.",

							makeFeaturedLink(mainDate2Long, todaysFA),

							makeFeaturedLink(mainDateLong)

						)

					else

						return string.format(

							"This article appeared on Wikipedia's Main Page as %s on %s, and will appear again on %s.",

							todaysFA,

							makeFeaturedLink(mainDateLong),

							makeFeaturedLink(mainDate2Long)

						)

					end

				elseif isCurrent(data.mainDateTimestamp) then

					if isFuture(data.mainDate2Timestamp) then

						return string.format(

							"This article is currently on Wikipedia's Main Page as %s, and will appear again on %s.",

							makeFeaturedLink(mainDateLong, todaysFA),

							makeFeaturedLink(mainDate2Long)

						)

					else

						return nil

					end

				else

					if isFuture(data.mainDate2Timestamp) then

						return string.format(

							"This article will appear on Wikipedia's Main Page as %s on %s, and again on %s.",

							todaysFA,

							makeFeaturedLink(mainDateLong),

							makeFeaturedLink(mainDate2Long)

						)

					else

						return nil

					end

				end

			else

				if isPast(data.mainDateTimestamp) then

					return string.format(

						"This article appeared on Wikipedia's Main Page as %s on %s.",

						makeFeaturedLink(mainDateLong, todaysFA),

						mainDateLong

					)

				elseif isCurrent(data.mainDateTimestamp) then

					return string.format(

						"This article is currently on Wikipedia's Main Page as %s.",

						makeFeaturedLink(mainDateLong, todaysFA),

						mainDateLong

					)

				else

					return string.format(

						"This article will appear on Wikipedia's Main Page as %s on %s.",

						makeFeaturedLink(mainDateLong, todaysFA),

						mainDateLong

					)

				end

			end

		end,

		categories = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local status = articleHistoryObj:getStatusId()

			local cats = {}



			local pagetype = data.isList and 'lists' or 'articles'

			if data.mainDate and data.mainDateTimestamp <= data.currentTimestamp then

				cats#cats + 1 = Category.new(string.format(

					'Featured %s that have appeared on the main page',

					pagetype

				))

				if data.mainDate2 and data.mainDate2Timestamp <= data.currentTimestamp then

					cats#cats + 1 = Category.new(string.format(

						'Featured %s that have appeared on the main page twice',

						pagetype

					))

				else

					cats#cats + 1 = Category.new(string.format(

						'Featured %s that have appeared on the main page once',

						pagetype

					))

				end

			elseif status == 'FA' or status == 'FL' or data.mainDate then

				cats#cats + 1 = Category.new(string.format(

					'Featured %s that have not appeared on the main page',

					pagetype

				))

			end

			return cats

		end

	}

},



-------------------------------------------------------------------------------

-- Actions

-------------------------------------------------------------------------------



-- The actions table contains configuration tables for actions such as featured

-- article candidacies and peer review, etc.

-- Each configuration table can have the following fields:

--

-- id: the main ID for the action. This should be the same as the configuration

--    table key.

-- name: the name of the action. This can be a string or a function. If it is

--    a function, it takes an article history object as its first parameter and

--    the action object as its second parameter, and should return the name.

-- results: a table of possible results for the action. Keys in the table should

--    be a result ID, e.g. "promoted" or "kept", and values should be a subtable

--    with the following fields:

--    id: the result ID. This should be the same as the table key. It will

--        also define a possible input value for the action's result parameter.

--    text: the displayed result text. This may be a string or a function. If it

--        is a function, it takes an article history object as the first

--        parameter and the current action object as the second parameter, and

--        should return the result string.

--    aliases: an array of result ID aliases. Each of these will define a valid

--        value for the action's result parameter.

-- text: The action text. This may be a string or a function. If it is a

--    function, it takes an article history object as the first parameter and

--    the current action object as the second parameter, and should return the

--    text string.

-- categories: The categories set by the notice. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter and the current action object as the

--    second parameter, and should return an array of category objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. This can be

--    a string or a function. If it is a function, it takes an article history

--    object as its first parameter, and should return the caption text. If this

--    is absent, the icon caption is used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



actions = {

	FAC = {

		id = 'FAC',

		name = 'Featured article candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FAR = {

		id = 'FAR',

		name = 'Featured article review',

		aliases = {'FARC'},

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			local result = actionObj.resultId

			if result == 'demoted' or result == 'merged' then

				local status = articleHistoryObj:getStatusId()

				local sortKey = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

				if status == 'FA' or status == 'FL' then

					sortKey = '#' .. sortKey

				end

				ret#ret + 1 = Category.new(

					'Wikipedia former featured articles',

					sortKey

				)

			end

			return ret

		end

	},

	BP = {

		id = 'BP',

		name = 'Brilliant prose',

		results = {

			nominated = {

				id = 'nominated',

				text = 'Nominated',

				aliases = {'pass', 'promoted', 'nom'}

			}

		}

	},

	RBP = {

		id = 'RBP',

		name = 'Refreshing brilliant prose',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			'not kept' = {

				id = 'not kept',

				text = 'Not kept',

				aliases = {'fail', 'failed', 'remove', 'removed', 'demoted'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			if actionObj.resultId == 'not kept' then

				ret#ret + 1 = Category.new(

					'Wikipedia former brilliant prose',

					articleHistoryObj.currentTitle.text

				)

			end

			return ret

		end

	},

	FLC = {

		id = 'FLC',

		name = 'Featured list candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FLR = {

		id = 'FLR',

		name = 'Featured list removal candidate',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			local result = actionObj.resultId

			if result == 'demoted' or result == 'merged' then

				local sortKey

				if articleHistoryObj:getStatusId() == 'FL' then

					sortKey = '#' .. articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

				else

					sortKey = articleHistoryObj.currentTitle.text

				end

				ret#ret + 1 = Category.new(

					'Wikipedia former featured lists',

					sortKey

				)

			end

			return ret

		end

	},

	FTC = {

		id = 'FTC',

		name = 'Featured topic candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FTR = {

		id = 'FTR',

		name = 'Featured topic removal candidate',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		}

	},

	FPOC = {

		id = 'FPOC',

		name = 'Featured portal candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FPOR = {

		id = 'FPOR',

		name = 'Featured portal review',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		}

	},

	GAN = {

		id = 'GAN',

		name = 'Good article nominee',

		aliases = {'GAC'},

		results = {

			listed = {

				id = 'listed',

				text = 'Listed',

				aliases = {'pass', 'passed', 'promoted'}

			},

			'not listed' = {

				id = 'not listed',

				text = 'Not listed',

				aliases = {'fail', 'failed', 'not promoted'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			if actionObj.resultId == 'not listed' then

				local status = articleHistoryObj:getStatusId()

				if status ~= 'FA'

					and status ~= 'GA'

					and status ~= 'FFA'

				then

					ret#ret + 1 = Category.new(

						'Former good article nominees',

						articleHistoryObj.currentTitle.text

					)

				end

			end

			return ret

		end

	},

	GAR = {

		id = 'GAR',

		name = 'Good article reassessment',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			delisted = {

				id = 'delisted',

				text = 'Delisted',

				aliases = {'fail', 'failed'}

			},

			listed = {

				id = 'listed',

				text = 'Listed'

			},

			'not listed' = {

				id = 'not listed',

				text = 'Not listed'

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			if actionObj.resultId == 'delisted' then

				local status = articleHistoryObj:getStatusId()

				if status ~= 'FA'

					and status ~= 'GA'

				then

					ret#ret + 1 = Category.new(

						'Delisted good articles',

						articleHistoryObj.currentTitle.text

					)

				end

			end

		end

	},

	GTC = {

		id = 'GTC',

		name = 'Good topic candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	GTR = {

		id = 'GTR',

		name = 'Good topic removal candidate',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		}

	},

	PR = {

		id = 'PR',

		name = 'Peer review',

		results = {

			reviewed = {

				id = 'reviewed',

				text = 'Reviewed',

				aliases = {'_BLANK'}

			},

			'not reviewed' = {

				id = 'not reviewed',

				text = 'Not reviewed',

			}

		},

		categories = {'Old requests for peer review'}

	},

	WPR = {

		id = 'WPR',

		name = function (articleHistoryObj, actionObj)

			local names = {

				approved = 'WikiProject approved revision',

				copyedited = 'Guild of Copy Editors',

				collaboration = 'WikiProject collaboration',

				maindate = "Today's featured article"

			}

			local result = actionObj.resultId

			return result and namesresult or 'WikiProject peer review'

		end,

		results = {

			approved = {

				id = 'approved',

				text = function(articleHistoryObj, actionObj)

					if actionObj.oldid then

						local url = mw.uri.fullUrl(

							articleHistoryObj.currentTitle.prefixedText,

							{diff = 'cur', oldid = actionObj.oldid}

						)

						return string.format(

							'[%s %s]',

							tostring(url),

							'Diff to current version'

						)

					else

						error(string.format(

							"No oldid detected for the approved version; " ..

								"please set the 'action%doldid' parameter " ..

								"or give the 'action%dresult' parameter a " ..

								"different value.",

							actionObj.paramNum,

							actionObj.paramNum

						))

					end

				end,

				aliases = {'approved version'}

			},

			copyedited = {

				id = 'copyedited',

				text = 'Copyedited',

				aliases = {'copyedit', 'proofread'}

			},

			maindate = {

				id = 'maindate',

				text = 'Main Page'

			},

			collaborated = {

				id = 'collaborated',

				text = 'Collaborated',

				aliases = {'cotw', 'collaboration'}

			},

			reviewed = {

				id = 'reviewed',

				text = 'Reviewed',

				aliases = {'_BLANK'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			local result = actionObj.resultId

			if result == 'copyedited' then

				ret1 = Category.new('Articles copy edited by the Guild of Copy Editors')

			end

			return ret

		end

	},

	WAR = {

		id = 'WAR',

		name = 'WikiProject A-class review',

		results = {

			approved = {

				id = 'approved',

				text = 'Approved',

				aliases = {'pass', 'passed'}

			},

			'not approved' = {

				id = 'not approved',

				text = 'Not approved',

				aliases = {'fail', 'failed', 'not reviewed'}

			},

			reviewed = {

				id = 'reviewed',

				text = 'Reviewed',

				aliases = {'_BLANK'}

			},

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'demote'}

			}

		}

	},

	AFD = {

		id = 'AFD',

		name = 'Articles for deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' ={

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	MFD = {

		id = 'MFD',

		name = 'Miscellany for deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' = {

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	TFD = {

		id = 'TFD',

		name = 'Templates for discussion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' = {

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				text = 'Redirected',

				aliases = {'redirect'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	CSD = {

		id = 'CSD',

		name = 'Candidate for speedy deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete', 'speedily deleted', 'speedy delete'}

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			prod = {

				id = 'prod',

				text = 'Converted to [[WP:PROD|proposed deletion]]',

				aliases = {'prodded'}

			},

			afd = {

				id = 'afd',

				text = 'Sent to [[WP:AFD|articles for deletion]]',

				aliases = {'afded'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	PROD = {

		id = 'PROD',

		name = 'Proposed deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' = {

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			afd = {

				id = 'afd',

				text = 'Sent to [[WP:AFD|articles for deletion]]',

				aliases = {'afded'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	DRV = {

		id = 'DRV',

		name = 'Deletion review',

		results = {

			endorsed = {

				id = 'endorsed',

				text = 'Endorsed',

				aliases = {'endorse'}

			},

			relisted = {

				id = 'relisted',

				text = 'Relisted',

				aliases = {'relist'}

			},

			overturned = {

				id = 'overturned',

				text = 'Overturned',

				aliases = {'overturn'}

			},

			restored = {

				id = 'restored',

				text = 'Restored',

				aliases = {'restore'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			}

		}

	}

},



-------------------------------------------------------------------------------

-- Collapsible notices

-------------------------------------------------------------------------------



-- The collapsibleNotices table contains configuration tables for notices that

-- go in the collapsible part of the template, underneath the actions.

-- Each configuration table can have the following fields:

--

-- id: the main ID for the notice. This should be the same as the configuration

--    table key.

-- isActive: a function that should return a truthy value if the notice should

--    be displayed, and a falsy value if not. (Falsy values are false and nil,

--    and truthy values are everything else.) The function takes an article

--    history object as its first parameter.

-- makeData: a function that should return a table of data to be used by other

--    functions in this notice configuration table. It can be accessed using

--    noticeObj:getData().

-- icon: the filename of the notice icon, minus the "File:" prefix.

-- iconCaption: the icon caption.

-- iconSize: The icon size, including "px" suffix. The default is defined in

--    defaultIconSize.

-- text: The notice text. This may be a string or a function. If it is a

--    function, it takes an article history object as the first parameter, and

--    the current collapsible notice object as the second parameter, and should

--    return the text string.

-- categories: The categories set by the notice. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter, and the current collapsible notice

--    object as the second parameter, and should return an array of category

--    objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. This can be

--    a string or a function. If it is a function, it takes an article history

--    object as its first parameter, and should return the caption text. If this

--    is absent, the icon caption is used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



collapsibleNotices = {

	-- DYK

	{

		id = 'DYK',

		icon = 'Symbol question.svg',

		iconCaption = 'Did You Know',

		noticeBarIcon = true,

		isActive = function (articleHistoryObj)

			return isActiveDatedObject(articleHistoryObj, 'dyk')

		end,

		makeData = function (articleHistoryObj)

			return makeDateData(articleHistoryObj, 'dyk', {'entry', 'nom', 'ignoreerror'})

		end,

		text = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			for _, t in ipairs(data) do

				local raPage = 'Wikipedia:Recent additions/' ..

					getDate('Y/F#j F Y', t.date)

				if not titleExists(raPage) then

					raPage = 'Wikipedia:Recent additions'

				end

				t.link = raPage

			end

			local fact = 'fact from this article'

			local nomPage = data1].nom or ('Template:Did you know nominations/' .. articleHistoryObj.currentTitle.text)

			if titleExists(nomPage) then

				fact = '[[' .. nomPage .. '|' .. fact .. ']]'

			end

			local blurb = "A " .. fact .. " appeared on " ..

				"Wikipedia's [[Main Page]] in the " ..

				"''\"[[:Template:Did you know|Did you know?]]\"'' " ..

				"column on $1."

			return makeDateText(data, blurb, true)

		end,

		collapsibleText = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local ctext = {}

			if #data == 1 and data1].entry then

				ctext#ctext + 1 = string.format(

					"The text of the entry was: ''Did you know %s''",

					data1].entry

				)

			else

				local entries = {}

				local lastEntryDate

				for _, t in ipairs(data) do

					entries#entries + 1 = t.entry

					lastEntryDate = t.date

				end

				if #entries == 1 then

					ctext#ctext + 1 = string.format(

						"The text of the entry for %s was: ''Did you know %s''",

						lastEntryDate, entries1

					)

				elseif #entries > 1 then

					ctext#ctext + 1 = 'The text of the entries was:\n'

					local list = mw.html.create('ul')

					for _, t in ipairs(data) do

						if t.entry then

							list:tag('li'):wikitext(string.format(

								"%s: ''Did you know %s''",

								t.date, t.entry

							))

						end

					end

					ctext#ctext + 1 = tostring(list)

				end

			end

			if #ctext > 0 then

				return table.concat(ctext)

			else

				return nil

			end

		end,

		categories = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local cats = {}

			do

				local status = articleHistoryObj:getStatusId()

				local statusCat

				if status == 'FA' then

					statusCat = 'Wikipedia Did you know articles that are featured articles'

				elseif status == 'FL' then

					statusCat = 'Wikipedia Did you know articles that are featured lists'

				elseif status == 'GA' or status == 'FFA/GA' then

					statusCat = 'Wikipedia Did you know articles that are good articles'

				else

					statusCat = 'Wikipedia Did you know articles'

				end

				cats#cats + 1 = Category.new(statusCat)

			end

			for _, t in ipairs(data) do

				if not t.ignoreerror then

					if t.entry then

						local mCheckDYKEntry = require('Module:Check DYK hook')

						if not mCheckDYKEntry._isValidHook(t.entry) then

							cats#cats + 1 = Category.new('Pages with a malformed DYK entry')

						end

					else

						cats#cats + 1 = Category.new('Pages with a missing DYK entry')

					end

				end

			end

			return cats

		end

	},



	-- ITN

	{

		id = 'ITN',

		isActive = function (articleHistoryObj)

			return isActiveDatedObject(articleHistoryObj, 'itn')

		end,

		makeData = function (articleHistoryObj)

			return makeDateData(articleHistoryObj, 'itn', {'link'})

		end,

		icon = 'Globe current.svg',

		iconCaption = 'In the news',

		noticeBarIcon = true,

		noticeBarIconSize = '20px',

		text = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local dates = {}

			for _, t in ipairs(data) do

				local date = {}

				if t.link then

					date.link = t.link

				elseif t.ymdDate >= 20110701 then

					date.link = string.format(

						'Wikipedia:In the news/Candidates/%s %d',

						t.month,

						t.year

					)

				elseif t.ymdDate >= 20090101 then

					date.link = string.format(

						'Wikipedia:ITN archives/%d/%s',

						t.year,

						t.month

					)

				elseif t.ymdDate >= 20050101 then

					date.link = string.format(

						'Portal:Current events/%d %s %d',

						t.year,

						t.month,

						t.day

					)

				end

				date.date = t.date

				dates#dates + 1 = date

			end

			local intro

			if #data > 1 then

				intro = 'News items involving this article were'

			else

				intro = 'A news item involving this article was'

			end

			local blurb = intro ..

				" featured on Wikipedia's [[Main Page]] in the " ..

				"''\"[[Template:In the news|In the news]]\"'' column on $1."

			return makeDateText(dates, blurb)

		end,

		categories = function (articleHistoryObj, collapsibleNoticeObj)

			local cats = {}

			cats1 = Category.new('Wikipedia In the news articles')

			return cats

		end

	},



	-- On This Day

	{

		id = 'OTD',

		isActive = function (articleHistoryObj)

			return isActiveDatedObject(articleHistoryObj, 'otd')

		end,

		makeData = function (articleHistoryObj)

			return makeDateData(articleHistoryObj, 'otd', {'link', 'oldid'})

			-- TODO: remove 'link' after it is no longer needed for tracking

		end,

		icon = 'Nuvola apps date.svg',

		iconCaption = 'On this day...',

		noticeBarIcon = true,

		noticeBarIconSize = '20px',

		text = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local dates = {}

			for _, t in ipairs(data) do

				local date = {}

				date.date = t.date

				date.link = t.link

				if t.oldid then

					-- TODO: Move this inside the main module

					local oldid = tonumber(t.oldid)

					if oldid and

						math.floor(oldid) == oldid and

						oldid > 0 and

						oldid < math.huge

					then

						-- If the oldid is valid, it takes precedence over

						-- explicit links.

						date.link = 'Special:PermaLink/' .. t.oldid

					else

						collapsibleNoticeObj:addWarning(

							string.format(

								"invalid oldid '%s' detected in parameter '%s'; " ..

									"if an oldid is specified it must be a positive integer",

								t.oldid,

								t.argPrefix .. 'oldid'

							),

							'Template:Article history#Invalid oldid'

						)

					end

				end

				dates#dates + 1 = date

			end

			local intro

			if #data > 1 then

				intro = 'Facts from this article were'

			else

				intro = 'A fact from this article was'

			end

			local blurb = intro ..

				" featured on Wikipedia's [[Main Page]] in the " ..

				"''\"[[Wikipedia:Selected anniversaries|On this day...]]\"'' " ..

				"column on $1."

			return makeDateText(dates, blurb)

		end,

		categories = function (articleHistoryObj, collapsibleNoticeObj)

			local cats = {}

			cats1 = Category.new('Selected anniversaries articles')

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if data then

				for _, t in ipairs(data) do

					if t.link then

						cats#cats + 1 = Category.new(

							'Article history templates with linked otd dates'

						)

						break

					end

				end

			end

			return cats

		end

	},



	-- Article Collaboration and Improvement Drive

	{

		id = 'ACID',

		isActive = function (articleHistoryObj)

			return articleHistoryObj.args.aciddate

		end,

		icon = 'Article Collaboration and Improvement Drive.svg',

		iconCaption = 'Article Collaboration and Improvement Drive',

		noticeBarIcon = true,

		noticeBarIconSize = '20px',

		text = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local blurb = 'This article was on the ' ..

				'[[WP:ACID|Article Collaboration and Improvement Drive]] ' ..

				'for the week of %s.'

			local date = validateDate('aciddate', args.aciddate)

			return string.format(blurb, date)

		end

	},



	-- League of Copy Editors

	{

		id = 'LOCE',

		isActive = function (articleHistoryObj)

			return articleHistoryObj.args.loceNotAnActiveOption

		end,

		icon = 'LoCiconRevised.png',

		iconCaption = 'League of Copyeditors',

		iconSize = '25px',

		noticeBarIcon = true,

		text = 'This article, or a portion of it, was copyedited by the ' ..

			'[[WP:LoCE|League of Copyeditors]].'

	}

},



-------------------------------------------------------------------------------

-- Notice bar icons

-------------------------------------------------------------------------------



-- This table holds configuration tables for notice bar icons that don't appear

-- as part of a row. Other notice bar icons are handled in the statuses,

-- notices, actions, and collapsibleNotices tables.

-- It accepts the following fields:

-- isActive: a function that should return a truthy value if the notice should

--    be displayed, and a falsy value if not. (Falsy values are false and nil,

--    and truthy values are everything else.) The function takes an article

--    history object as its first parameter.

-- icon: the filename of the notice bar icon, minus the "File:" prefix.



noticeBarIcons = {

	-- Peer review, or NA status

	{

		isActive = function (articleHistoryObj)

			local status = articleHistoryObj:getStatusId()

			-- @XXX: This is what the template does, but we should take into

			-- account peer review actions as well.

			return not status or status == 'PR' or status == 'NA'

		end,

		icon = 'Nuvola apps kedit.svg'

	},



	-- Wikipedia 1.0

	{

		isActive = function (articleHistoryObj)

			return articleHistoryObj.args'v1.0NotAnActiveOption'

		end,

		icon = 'WP1 0 Icon.svg'

	}

},



-------------------------------------------------------------------------------

-- Extra categories

-------------------------------------------------------------------------------



-- This table contains categories that don't appear as part of a row. It is an

-- array of functions; each function takes an article history object as input

-- and must return an array of category objects as output.



extraCategories = {

	-- Four award

	function (articleHistoryObj)

		local yesno = require('Module:Yesno')

		local ret = {}

		local isFour = yesno(articleHistoryObj.args.four)

		if isFour then

			ret#ret + 1 = Category.new('Wikipedia four award articles')

		elseif isFour == false then

			ret#ret + 1 = Category.new('Wikipedia articles rejected for Four awards')

		elseif articleHistoryObj:getStatusId() == 'FA' then

			local isDYK = false

			for _, obj in ipairs(articleHistoryObj:getCollapsibleNoticeObjects()) do

				if obj.id == 'DYK' then

					isDYK = true

					break

				end

			end

			if isDYK then

				for _, obj in ipairs(articleHistoryObj:getActionObjects()) do

					if obj.id == 'GAN' and obj.resultId == 'listed' then

						ret#ret + 1 = Category.new('Possible Wikipedia four award articles')

						break

					end

				end

			end

		end

		return ret

	end,



	-- Deletion to Quality award

	function (articleHistoryObj)

		local ret = {}

		local status = articleHistoryObj:getStatusId()

		if status == 'FA' or status == 'FL' or status == 'GA' then

			local iAfd = 0

			local hasAfd = false

			local actionObjects = articleHistoryObj:getActionObjects()

			for i, obj in ipairs(actionObjects) do

				if obj.id == 'AFD' then

					iAfd = i

					hasAfd = true

					break

				end

			end

			if hasAfd then

				local function hasNomination(id, result)

					for i = iAfd + 1, #actionObjects do

						local obj = actionObjectsi

						if obj.id == id and obj.resultId == result then

							return true

						end

					end

					return false

				end

				if status == 'GA' and hasNomination('GAN', 'listed') or

					status == 'FL' and hasNomination('FLC', 'promoted') or

					status == 'FA' and hasNomination('FAC', 'promoted')

				then

					ret#ret + 1 = Category.new('Deletion to Quality Award candidates')

				end

			end

		end

		return ret

	end,

},



-------------------------------------------------------------------------------

-- Parameters

-------------------------------------------------------------------------------



-- The parameter values used to generate the page actions. These are used as

-- Lua patterns, so any of the magic characters *+-.^$%[] should be escaped

-- with a preceding % symbol.

actionParamPrefix = 'action',

actionParamSuffixes = {

	'' = 'code',

	date = 'date',

	link = 'link',

	result = 'resultCode',

	oldid = 'oldid'

},



-- The parameter used to set the current status.

currentStatusParam = 'currentstatus',



-------------------------------------------------------------------------------

-- Other settings

-------------------------------------------------------------------------------



-- If this number or fewer of collapsible rows are present (including actions

-- and collapsible notices) they will not be collapsed. If this is set to the

-- string "all", all rows will always be visible. Otherwise, the input must be

-- a number. The default is three rows.

uncollapsedRows = 3,



-- The default size for icons. The default is 30px.

defaultIconSize = '30px',



-- The default size for status icons. The default is 50px.

defaultStatusIconSize = '50px',



-- The default size for status icons for multi status templates. The default is 30px.

defaultMultiStatusIconSize = '30px',



-- The default size for notice bar icons. The default is 15px.

defaultNoticeBarIconSize = '15px',



-- The default size for collapsible status icons. The default is 50px.

defaultCollapsibleNoticeIconSize = '20px',



-------------------------------------------------------------------------------

-- Messages

-------------------------------------------------------------------------------



msg = {



-- The heading for the collapsible table of actions if we are in the main

-- namespace or the talk namespace.

'milestones-header' = 'Article milestones',



-- The heading for the collapsible table of actions if we are in a different

-- namespace.

-- $1 - the subject namespace name.

'milestones-header-other-ns' = '$1 milestones',



-- The milestones date header.

'milestones-date-header' = 'Date',



-- The milestones process header.

'milestones-process-header' = 'Process',



-- The milestones result header.

'milestones-result-header' = 'Result',



-- The format to display the action dates in. The syntax is the same as the

-- #time parser function.

'action-date-format' = 'F j, Y',



-- The category to use if any errors are detected.

'error-category' = 'Article history templates with errors',



-- Define boilerplate text for error messages and warnings, both with and

-- without help links.

-- $1 - the error message

-- $2 - a link to a help page and section for the error

'error-message-help' = 'Error: $1 ([[$2|help]]).',

'error-message-nohelp' = 'Error: $1.',

'warning-help' = 'Warning: $1 ([[$2|help]]).',

'warning-nohelp' = 'Warning: $1.',



-- Error for row objects that should output notice bar icons but for which no

-- icon values could be found.

'row-error-missing-icon' = "notice bar icon config set to 'true' but no " ..

	'image could be found',



-- A help link for row-error-missing-icon

'row-error-missing-icon-help' = 'Template:Article history#Missing icon',



-- Error for action objects that aren't passed a code.

-- $1 - the parameter name for the code

'action-error-no-code' = "no action code found in the '$1' parameter; " ..

	"please add a code or remove other parameters starting with '$1'",



-- A help link for action-error-no-code

'action-error-no-code-help' = 'Template:Article history#Action codes',



-- Error for action objects that are passed an invalid code.

-- $1 - the code that the user input

-- $2 - the parameter name for the code

'action-error-invalid-code' = "invalid action code '$1' passed to the '$2' parameter",



-- A help link for action-error-invalid-code

'action-error-invalid-code-help' = 'Template:Article history#Action codes',



-- Error for action objects with blank result parameters, where result

-- parameters are required for the action's ID.

-- $1 - the action ID

-- $2 - the result parameter name

'action-error-blank-result' = "the '$1' action requires a result code; " ..

	"please add a result code to parameter '$2'",



-- A help link for action-error-blank-result

'action-error-blank-result-help' = 'Template:Article history#Action results',



-- Error for action objects with invalid result parameters.

-- $1 - the result code that the user input

-- $2 - the action ID

-- $3 - the result parameter name

'action-error-invalid-result' = "invalid result '$1' for action '$2' " ..

	"detected in parameter '$3'",



-- A help link for action-error-invalid-result

'action-error-invalid-result-help' = 'Template:Article history#Action results',



-- Warning for action objects with invalid dates.

-- $1 - the date input by the user

-- $2 - the date parameter name

'action-warning-invalid-date' = "invalid date '$1' detected in parameter '$2'",



-- A help link for action-warning-invalid-date

'action-warning-invalid-date-help' = 'Template:Article history#Invalid date',



-- Error for action objects with no dates.

-- $1 - the parameter number

-- $2 - the date parameter name

-- $3 - the action parameter name

'action-warning-no-date' = "no date specified for action $1; " ..

	"please add a date to parameter '$2' or remove the other parameters " ..

	"beginning with '$3'",



-- A help link for action-warning-no-date

'action-warning-no-date-help' = 'Template:Article history#No date',



-- The text to display in place of the action date if it is missing.

'action-date-missing' = '?',



-- Error for action objects with invalid oldids.

-- $1 - the oldid input by the user

-- $2 - the oldid parameter name

'action-warning-invalid-oldid' = "invalid oldid '$1' detected in parameter '$2'; " ..

	"if an oldid is specified it must be a positive integer",



-- A help link for action-warning-invalid-oldid

'action-warning-invalid-oldid-help' = 'Template:Article history#Invalid oldid',



-- Error for invalid current status codes.

-- $1 - the code input by the user

'articlehistory-warning-invalid-status' = "'$1' is not a valid status code",



-- A help link for articlehistory-warning-invalid-status

'articlehistory-warning-invalid-status-help' = 'Template:Article history#Invalid status',



-- Warning for invocations that specify a current status without specifying any

-- actions.

'articlehistory-warning-status-no-actions' = "a current status was supplied " ..

	'without any actions',



-- A help link for articlehistory-warning-status-no-actions

'articlehistory-warning-status-no-actions-help' = 'Template:Article history#No actions',



-- The text to display the current status at the bottom of the collapsible

-- table.

-- $1 - the current status name

'status-blurb' = "Current status: '''$1'''",



-- The text to display at the bottom of the collapsible table if the current

-- status is unknown.

'status-unknown' = '?',



}



-------------------------------------------------------------------------------

--                              CONFIG TABLE END

-------------------------------------------------------------------------------



}
Permanently protected module
From Wikipedia, the free encyclopedia


-------------------------------------------------------------------------------

--              Configuration data for [[Module:Article history]]

-------------------------------------------------------------------------------



local lang = mw.language.getContentLanguage()

local Category = require('Module:Article history/Category')



-------------------------------------------------------------------------------

-- Helper functions

-------------------------------------------------------------------------------



-- Makes a link to a template page surrounded by double curly braces. A

-- workalike for the {{tl}} template.

local function makeTemplateLink(s)

	local openb = mw.text.nowiki('{{')

	local closeb = mw.text.nowiki('}}')

	return string.format('%s[[Template:%s|%s]]%s', openb, s, s, closeb)

end



-- Gets the Good Article topic for the given key. Uses

-- [[Module:Good article topics]].

local function getGoodArticleTopic(key)

	if not key then

		return nil

	end

	return require('Module:Good article topics')._main(key)

end



-- Returns the Good Article page link and display value for a given Good Article

-- key. If the key wasn't valid, the default Good Article page and display value

-- is returned instead.

local function getGoodArticleTopicLink(key)

	local topic = getGoodArticleTopic(key)

	local link, display

	if topic then

		link = 'Wikipedia:Good articles/' .. topic

		display = topic .. ' good articles'

	else

		link = 'Wikipedia:Good articles'

		display = 'good articles'

	end

	return link, display

end



-- Wrapper function for mw.language:formatDate, going through pcall to catch

-- invalid input errors.

local function getDate(format, date)

	local success, result = pcall(lang.formatDate, lang, format, date)

	if success then

		return result

	end

end



-- Gets the date in the format YYYYMMDD, as a number. Months and dates are

-- zero-padded. Results from this function are intended to be used in date

-- calculations.

local function getYmdDate(date)

	date = getDate('Ymd', date)

	if date then

		return tonumber(date)

	else

		return nil

	end

end



-- Gets the date in the format Month d, YYYY.

local function getLongDate(date)

	return getDate('F j, Y', date)

end



-- Returns true if the given page is an existing title, and false or nil

-- otherwise

local function titleExists(page)

	local success, title = pcall(mw.title.new, page)

	return success and title.exists

end



-- Returns a truthy value if a date parameter for the given prefix has been

-- provided by the user.

local function isActiveDatedObject(articleHistoryObj, prefix)

	local args = articleHistoryObj.args

	local prefixArgs = articleHistoryObj.prefixArgs

	return argsprefix .. 'date' or prefixArgsprefix

end



-- Returns a date as formatted by getLongDate. If the date is invalid, it raises

-- an error using param as the parameter name containing the invalid date.

local function validateDate(param, date, articleHistoryObj)

	local longDate = getLongDate(date)

	if longDate then

		return longDate

	else

		articleHistoryObj:raiseError(

			string.format(

				"invalid date '%s' detected in parameter '%s'",

				tostring(date),

				param

			),

			'Template:Article history#Invalid date'

		)

	end

end



-- Generates a data table for a date-related notice such as DYK and ITN. prefix

-- is the parameter prefix for that notice type (e.g. "dyk"), and suffixes is

-- an array of parameter suffixes in addition to "date" that is used by that

-- notice type (e.g. "entry" for the "dykentry" and "dyk2entry" parameters).

local function makeDateData(articleHistoryObj, prefix, suffixes)

	local args = articleHistoryObj.args

	local prefixArgs = articleHistoryObj.prefixArgs



	-- Sanity checks

	if prefixArgsprefix then

		for _, t in ipairs(prefixArgsprefix]) do

			if not t.date then

				articleHistoryObj:raiseError(

					string.format(

						"an argument starting with '%s%d' was detected, " ..

							"but no '%s%ddate' parameter was specified",

						prefix, t1],

						prefix, t1

					),

					'Template:Article history#No date parameter'

				)

			end

		end

	end



	local data = {}



	-- Organise the input

	local function addData(sep)

		local t = {}

		local argPrefix = prefix .. sep

		do

			local key = argPrefix .. 'date'

			t.date = validateDate(key, argskey], articleHistoryObj)

			t.month, t.day, t.year = t.date:match('(%a+) (%d+), (%d+)')

			t.day = tonumber(t.day)

			t.year = tonumber(t.year)

			t.ymdDate = getYmdDate(t.date)

		end

		for _, suffix in ipairs(suffixes) do

			local key = argPrefix .. suffix

			tsuffix = argskey

		end

		t.argPrefix = argPrefix

		data#data + 1 = t

	end

	if argsprefix .. 'date' then

		addData('')

	end

	if prefixArgsprefix then

		for _, prefixData in ipairs(prefixArgsprefix]) do

			addData(tostring(prefixData1]))

		end

	end

	if #data < 1 then

		error(string.format(

			"no data items found for prefix '%s' and parameter checks failed'",

			tostring(prefix)

		))

	end



	return data

end



-- This makes the text for Main Page features such as DYKs and ITNs for the

-- dates contained in dateData (made with the makeDateData function).

-- The parameter $1 in the blurb will be replaced with the list of dates.

local function makeDateText(dateData, blurb, wantBold)

	local bold = wantBold and "'''" or ""

	local dates, doneLinks = {}, {}

	for i, t in ipairs(dateData) do

		local date

		if t.link and not doneLinkst.link then

			date = string.format('[[%s|%s]]', t.link, t.date)

			doneLinkst.link = true

		else

			date = t.date

		end

		datesi = bold .. date .. bold

	end

	local dateList = mw.text.listToText(dates, ', ', ', and ')

	return mw.message.newRawMessage(blurb, dateList):plain()

end



return {



-------------------------------------------------------------------------------

--                             CONFIG TABLE START

-------------------------------------------------------------------------------



-------------------------------------------------------------------------------

-- Statuses

-- Configuration for possible current statuses of the article.

-------------------------------------------------------------------------------



-- The statuses table contain configuration tables for possible current statuses

-- of the article.

-- Each table can have the following fields:

--

-- id: the main ID for the status. This should be the same as the configuration

--    table key.

-- aliases: a table of ID aliases that can be used to access the config table.

-- icon: The status icon.

-- iconSize: The icon size, including "px" suffix. The default is defined in

--    defaultStatusIconSize.

-- iconMultiSize: The icon size if we are outputting multiple status rows. The

--    default is defaultMultiStatusIconSize.

-- text: The status text. This may be a string or a function. If it is a

--    function, it takes an article history object as input, and should return

--    the text string. If it is a string, it can have the following parameters:

--    $1 - The full page name of the article or subject page

--    $2 - The page name without the namespace name

-- categories: The categories set by the status. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter, and the current status object as

--    the second parameter, and should return an array of category objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. The status

--    name is used by default. This can be a string or a function. If it is a

--    function, it takes an article history object as its first parameter, and

--    should return the caption text. If this is absent, the icon caption is

--    used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



statuses = {

	FA = {

		id = 'FA',

		name = 'Featured article',

		icon = 'Featured article star.svg',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local actions = articleHistoryObj:getActionObjects()

			local link

			for i = #actions, 1, -1 do

				local actionObj = actionsi

				if actionObj.id == 'FAC' then

					link = actionObj.link

					break

				end

			end

			link = link or 'Wikipedia:Featured article candidates/' .. articlePage

			local text = "'''%s''' is a [[Wikipedia:Featured articles|featured article]]; " ..

				"it (or a previous version of it) has been '''[[%s|identified]]''' " ..

				"as one of the best articles produced by the [[Wikipedia:Wikipedians|Wikipedia community]]. " ..

				"Even so, if you can update or improve it, [[Wikipedia:Be bold|please do so]]."

			return string.format(text, articlePage, link)

		end,

		categories = {'Wikipedia featured articles'}

	},

	FFA = {

		id = 'FFA',

		name = 'Former featured article',

		icon = 'Featured article star - cross.svg',

		iconSize = '48px',

		text = "'''$1''' is a [[Wikipedia:Former featured articles|former featured article]]. " ..

			"Please see the links under Article milestones below for its original nomination page " ..

			"(for older articles, check [[Wikipedia:Featured article candidates/Archived nominations/Index|the nomination archive]]) " ..

			"and why it was removed.",

		categories = {'Wikipedia former featured articles'}

	},

	FFAC = {

		id = 'FFAC',

		name = 'Former featured article candidate',

		aliases = {'FACFAILED'},

		icon = 'Cscr-former.svg',

		text = "'''$1''' is a former [[Wikipedia:Featured article candidates|featured article candidate]]. " ..

			"Please view the links under Article milestones below to see why " ..

			"the nomination failed. For older candidates, please check the " ..

			"[[Wikipedia:Featured article candidates/Archived nominations/Index|archive]]."

	},

	FL = {

		id = 'FL',

		name = 'Featured list',

		icon = 'Featured article star.svg',

		iconSize = '48px',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local actions = articleHistoryObj:getActionObjects()

			local link

			for i = #actions, 1, -1 do

				local actionObj = actionsi

				if actionObj.id == 'FLC' then

					link = actionObj.link

					break

				end

			end

			link = link or 'Wikipedia:Featured list candidates/' .. articlePage

			local text = "'''%s''' is a [[Wikipedia:Featured lists|featured list]], " ..

				"which means it has been '''[[%s|identified]]''' as one of the best " ..

				"[[Wikipedia:Stand-alone lists|lists]] produced by the [[Wikipedia:Wikipedians|Wikipedia community]]. " ..

				"If you can update or improve it, [[Wikipedia:Be bold|please do so]]."

			return string.format(text, articlePage, link)

		end,

		categories = {'Wikipedia featured lists'}

	},

	FFL = {

		id = 'FFL',

		name = 'Former featured list',

		icon = 'Cscr-featured-strike.svg',

		text = "'''$1''' is a [[Wikipedia:Former featured lists|former featured list]]. " ..

			"Please see the links under Article milestones below for its original " ..

			"nomination page and why it was removed. If it has improved again to " ..

			"[[Wikipedia:Featured list criteria|featured list standard]], you may " ..

			"[[Wikipedia:Featured list candidates|renominate]] the article to " ..

			"become a [[Wikipedia:Featured lists|featured list]]."

	},

	FFLC = {

		id = 'FFLC',

		name = 'Former featured list candidate',

		icon = 'Cscr-former.svg',

		iconCaption = 'Former FLC',

		text = "'''$1''' is a former [[Wikipedia:Featured list candidates|featured list candidate]]. " ..

			"Please view the link under Article milestones below to see why the nomination failed. " ..

			"Once the objections have been addressed you may " ..

			"[[Wikipedia:Featured list candidates#Resubmitting nominations|resubmit]] " ..

			"the article for featured list status.",

		categories = {'Wikipedia featured list candidates (contested)'}

	},

	'FFA/GA' = {

		id = 'FFA/GA',

		name = 'Former featured article, current good article',

		isMulti = true,

		statuses = {'FFA', 'GA'}

	},

	'FFAC/GA' = {

		id = 'FFAC/GA',

		name = 'Former featured article candidate, current good article',

		isMulti = true,

		statuses = {'FFAC', 'GA'}

	},

	GA = {

		id = 'GA',

		name = 'Good article',

		icon = 'Symbol support vote.svg',

		iconSize = '40px',

		iconMultiSize = '25px',

		text = function (articleHistoryObj)

			local link, display = getGoodArticleTopicLink(articleHistoryObj.args.topic)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local text = "'''%s''' has been listed as one of the '''''[[%s|%s]]''''' " ..

				"under the [[Wikipedia:Good article criteria|good article criteria]]. " ..

				"If you can improve it further, [[Wikipedia:Be bold|please do so]]. " ..

				"<small>''If it no longer meets these criteria, you can " ..

				"'''[[Wikipedia:Good article reassessment|reassess]]''' it''.</small>"

			return string.format(text, articlePage, link, display)

		end,

		categories = function (articleHistoryObj)

			local ret = {}

			local title = articleHistoryObj.currentTitle

			if title.namespace == 1 then

				ret#ret + 1 = Category.new('Wikipedia good articles')

				local topic = getGoodArticleTopic(articleHistoryObj.args.topic)

				if topic then

					ret#ret + 1 = Category.new(

						topic .. ' good articles',

						title.text

					)

				else

					ret#ret + 1 = Category.new(

						'Good articles without topic parameter',

						title.text

					)

				end

			end

			return ret

		end

	},

	FGAN = {

		id = 'FGAN',

		name = 'Former good article nominee',

		aliases = {'FAILEDGA'},

		icon = 'Symbol oppose vote.svg',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local link, display = getGoodArticleTopicLink(articleHistoryObj.args.topic)

			local text = "'''%s''' was a '''''[[%s|%s]]''''' nominee, " ..

				"but did not meet the [[Wikipedia:Good article criteria|good article criteria]] " ..

				"at the time. There may be suggestions below for improving the article. " ..

				"Once these issues have been addressed, the article can be " ..

				"[[Wikipedia:Good article nominations|renominated]]. " ..

				"Editors may also seek a '''[[Wikipedia:Good article reassessment|reassessment]]''' " ..

				"of the decision if they believe there was a mistake."

			return string.format(text, articlePage, link, display)

		end,

		categories = {'Former good article nominees'}

	},

	DGA = {

		id = 'DGA',

		name = 'Delisted good article',

		aliases = {'DELISTEDGA'},

		icon = 'Symbol unsupport vote.svg',

		iconCaption = 'Former good article',

		text = function (articleHistoryObj)

			local articlePage = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local link, display = getGoodArticleTopicLink(articleHistoryObj.args.topic)

			local text = "'''%s''' was one of the '''''[[%s|%s]]''''', " ..

				"but it has been removed from the list. " ..

				"There are suggestions below for improving the article to meet the " ..

				"[[Wikipedia:Good article criteria|good article criteria]]. " ..

				"Once these issues have been addressed, the article can be " ..

				"[[Wikipedia:Good article nominations|renominated]]. " ..

				"Editors may also seek a '''[[Wikipedia:Good article reassessment|reassessment]]''' " ..

				"of the decision if they believe there was a mistake."

			return string.format(text, articlePage, link, display)

		end,

		categories = {'Delisted good articles'}

	},

	FFT = {

		id = 'FFT',

		name = 'Part of former featured topic',

		icon = 'Cscr-featured-strike.svg',

		iconCaption = 'Former featured topic',

		text = "This article is part of a " ..

			"''[[Wikipedia:Former featured topics|former featured topic]]'' series. " ..

			"If it has improved again to " ..

			"[[Wikipedia:Featured topic criteria|featured topic standard]], " ..

			"you may [[Wikipedia:Featured topic candidates|renominate]] " ..

			"the topic to become a [[Wikipedia:Featured topic|featured topic]]."

	},

	FFTC = {

		id = 'FFTC',

		name = 'Former featured topic candidate',

		icon = 'Cscr-former.svg',

		text = "This article is part of a ''former'' " ..

			"[[Wikipedia:Featured topic candidates|featured topic candidate]]. " ..

			"Please view the links under Article milestones below to see why " ..

			"the nomination failed."

	},

	FPO = {

		id = 'FPO',

		name = 'Featured portal',

		icon = 'Cscr-featured.svg',

		text = "The '''$2 Portal''' is a [[Wikipedia:Featured portals|featured portal]], " ..

			"which means it has been " ..

			"'''[[Wikipedia:Featured portal candidates/Portal:$2|identified]]''' " ..

			"as one of the best portals on [[Wikipedia]]. " ..

			"If you see a way this portal can be updated or improved without " ..

			"compromising previous work, please feel free to contribute.",

		categories = function (articleHistoryObj)

			return {Category.new(

				'Wikipedia featured portals',

				articleHistoryObj.currentTitle.text

			)}

		end

	},

	FFPO = {

		id = 'FFPO',

		name = 'Former featured portal',

		icon = 'Featured article star - cross.svg',

		text = "This portal is a [[Wikipedia:Former featured portals|former featured portal]]. " ..

			"Please see the links under Portal milestones below for its " ..

			"original nomination page and why it was removed.",

		categories = function (articleHistoryObj)

			return {Category.new(

				'Wikipedia former featured portals',

				articleHistoryObj.currentTitle.text

			)}

		end

	},

	FFPOC = {

		id = 'FFPOC',

		name = 'Former featured portal candidate',

		icon = 'Cscr-former.svg',

		text = "This portal is a '''''former''''' " ..

			"[[Wikipedia:Featured portal candidates|featured portal candidate]]. " ..

			"Please see the links under Portal milestones below for its " ..

			"original nomination page and why the nomination failed."

	},

	PR = {

		-- Peer review is a valid current status, but it doesn't trigger a

		-- header row.

		id = 'PR',

		name = 'Peer reviewed',

		noticeBarIcon = 'Nuvola apps kedit.svg'

	},

	NA = {

		-- A valid current status, but doesn't trigger a header row.

		id = 'NA',

		noticeBarIcon = 'Nuvola apps kedit.svg'

	},

	-- The following are invalid statuses.

	FAC = {

		id = 'FAC',

		text = function (articleHistoryObj)

			articleHistoryObj:raiseError(

				string.format(

					'use the template %s to nominate an article for Featured article status',

					makeTemplateLink('fac')

				),

				'Template:Article history#Featured article candidates'

			)

		end

	},

	FAR = {

		id = 'FAR',

		text = function (articleHistoryObj)

			articleHistoryObj:raiseError(

				string.format(

					'use the template %s to nominate an article for Featured article review',

					makeTemplateLink('FAR')

				),

				'Template:Article history#Featured article review'

			)

		end

	},

	STUB = {

		id = 'STUB',

		aliases = {'START', 'B', 'A'},

		text = function (articleHistoryObj)

			local currentStatusParam = articleHistoryObj.cfg.currentStatusParam

			articleHistoryObj:raiseError(

				string.format(

					"do not use '%s' as value of the '%s' parameter; these " ..

						'assessments are the responsibility of individual ' ..

						'WikiProjects',

					articleHistoryObj.argscurrentStatusParam],

					currentStatusParam

				),

				'Template:Article history#WikiProject assessments'

			)

		end

	},

},



-- This function allows the generation of custom status ID. It takes an

-- articleHistory object as the first parameter, and should output the status

-- ID.

getStatusIdFunction = function (articleHistoryObj)

	-- Get the status ID. The status code is the code passed in from the

	-- arguments, and the ID is the value contained in the config.

	local statusCode = articleHistoryObj.argsarticleHistoryObj.cfg.currentStatusParam

	local statusId = articleHistoryObj:getStatusIdForCode(statusCode)



	-- Check for former featured articles.

	if statusId ~= 'FA'

		and statusId ~= 'FL'

		and statusId ~= 'FFA'

		and statusId ~= 'FFL'

		and statusId ~= 'FFA/GA'

	then

		local ffaObj

		local actions = articleHistoryObj:getActionObjects()

		for i = #actions, 1, -1 do

			local actionObj = actionsi

			if actionObj.id == 'FAR' and actionObj.resultId == 'demoted' then

				ffaObj = actionObj

				break

			end

		end

		if ffaObj then

			if not statusId then

				articleHistoryObj:raiseError(

					'former featured articles should have a current status',

					'Template:Article history#Former featured articles'

				)

			elseif statusId == 'GA' then

				statusId = 'FFA/GA'

			elseif statusId == 'DGA' then

				statusId = 'FFA'

			else

				articleHistoryObj:raiseError(

					string.format(

						"'%s' is not a valid current status for former featured articles",

						tostring(statusCode)

					),

					'Template:Article history#Former featured articles'

				)

			end

		end

	end



	return statusId

end,



-------------------------------------------------------------------------------

-- Notices

-------------------------------------------------------------------------------



-- The notices table contains configuration tables for notices about the article

-- that are unrelated to its current status.

-- Each configuration table can have the following fields:

--

-- id: the main ID for the notice. This should be the same as the configuration

--    table key.

-- isActive: a function that should return a truthy value if the notice should

--    be displayed, and a falsy value if not. (Falsy values are false and nil,

--    and truthy values are everything else.) The function takes an article

--    history object as its first parameter.

-- makeData: a function that should return a table of data to be used by other

--    functions in this notice configuration table. It can be accessed using

--    noticeObj:getData().

-- icon: the filename of the notice icon, minus the "File:" prefix.

-- iconCaption: the icon caption.

-- iconSize: The icon size, including "px" suffix. The default is defined in

--    defaultIconSize.

-- text: The notice text. This may be a string or a function. If it is a

--    function, it takes an article history object as the first parameter, and

--    the current notice object as the second parameter, and should return the

--    text string.

-- categories: The categories set by the notice. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter, and the current notice object as

--    the second parameter, and should return an array of category objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. This can be

--    a string or a function. If it is a function, it takes an article history

--    object as its first parameter, and should return the caption text. If this

--    is absent, the icon caption is used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



notices = {

	{

		id = 'FT',

		isActive = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local prefixArgs = articleHistoryObj.prefixArgs

			-- ftmain is included here because it leads to better error

			-- messages than leaving it out, even though ftmain by itself is

			-- invalid.

			return args.ftname or args.ftmain or prefixArgs.ft

		end,

		makeData = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local prefixArgs = articleHistoryObj.prefixArgs

			local data = {}

			local getTopicStatus = require('Module:FeaturedTopicSum').status

			local yesno = require('Module:Yesno')



			local function makeTopicData(name, isMain, paramNum)

				if name then

					return {

						name = name,

						isMain = yesno(isMain) or false,

						status = getTopicStatus(name),

						paramNum = paramNum

					}

				elseif isMain then

					local num = paramNum and tostring(paramNum) or ''

					articleHistoryObj:raiseError(

						string.format(

							"parameter 'ft%smain' is set, but no featured " ..

								"topic name is set in parameter 'ft%sname'",

							num, num

						),

						'Template:Article history#Featured topic names'

					)

				else

					return nil

				end

			end

			data#data + 1 = makeTopicData(args.ftname, args.ftmain)

			if prefixArgs.ft then

				for _, t in ipairs(prefixArgs.ft) do

					if t1 > 1 then -- we use args.ftname instead of args.ft1name

						data#data + 1 = makeTopicData(t.name, t.main, t1])

					end

				end

			end



			-- Check for rogue ft.. parameters

			if #data < 1 then

				articleHistoryObj:raiseError(

					"a parameter starting with 'ft' was detected, but no " ..

						"featured topic names were specified; " ..

						"please check the parameter names",

					'Template:Article history#Featured topic names'

				)

			end



			-- Find if one of the topics is featured.

			local isInFeaturedTopic = false

			for _, topic in ipairs(data) do

				if topic.status == 'FT' then

					isInFeaturedTopic = true

					break

				end

			end

			data.isInFeaturedTopic = isInFeaturedTopic



			return data

		end,

		icon = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			if data.isInFeaturedTopic then

				return 'Cscr-featuredtopic.svg'

			else

				return 'Support cluster.svg'

			end

		end,

		iconCaption = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			if data.isInFeaturedTopic then

				return 'Featured topic star'

			else

				return 'Good topic star'

			end

		end,

		iconSize = '48px',

		text = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local article = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText



			local firstBlurb = "'''%s''' is %s the '''[[Wikipedia:Featured topics/%s|%s]] series''', %s."

			local otherBlurb = "It is also %s the '''[[Wikipedia:Featured topics/%s|%s]] series''', %s."

			local finalBlurb = "%s identified as among the best series of " ..

				"articles produced by the [[Wikipedia:Wikipedians|Wikipedia community]]. " ..

				"If you can update or improve %s, [[Wikipedia:Be bold|please do so]]."

			local main = 'the main article in'

			local notMain = 'part of'

			local featuredLink = 'a [[Wikipedia:Featured topics|featured topic]]'

			local featuredNoLink = 'a featured topic'

			local goodLink = 'a [[Wikipedia:Good topics|good topic]]'

			local goodNoLink = 'a good topic'

			local thisSingular = 'This is'

			local thisPlural = 'These are'

			local itSingular = 'it'

			local itPlural = 'them'



			local hasFeaturedLink = false

			local hasGoodLink = false

			local text = {}



			-- First topic

			do

				local topic = data1

				local link

				if topic.status == 'FT' then

					link = featuredLink

					hasFeaturedLink = true

				else

					link = goodLink

					hasGoodLink = true

				end

				text#text + 1 = string.format(

					firstBlurb,

					article,

					topic.isMain and main or notMain,

					topic.name,

					topic.name,

					link

				)

			end



			-- Other topics

			for i = 2, #data do

				local topic = datai

				local link

				if topic.status == 'FT' then

					if hasFeaturedLink then

						link = featuredNoLink

					else

						link = featuredLink

						hasFeaturedLink = true

					end

				else

					if hasGoodLink then

						link = goodNoLink

					else

						link = goodLink

						hasGoodLink = true

					end

				end

				text#text + 1 = string.format(

					otherBlurb,

					topic.isMain and main or notMain,

					topic.name,

					topic.name,

					link

				)

			end



			-- Final blurb

			do

				local isPlural = #data > 1

				text#text + 1 = string.format(

					finalBlurb,

					isPlural and thisPlural or thisSingular,

					isPlural and itPlural or itSingular

				)

			end



			return table.concat(text, ' ')

		end,

		categories = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local status = articleHistoryObj:getStatusId()

			local article = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

			local cats = {}



			local function addCat(cat, sort)

				cats#cats + 1 = Category.new(cat, sort)

			end



			-- Page-wide status categories

			if status == 'FA' then

				addCat('FA-Class Featured topics articles')

			elseif status == 'FL' then

				addCat('FL-Class Featured topics articles')

			elseif status == 'FFA/GA' or status == 'FFAC/GA' or status == 'GA' then

				addCat('GA-Class Featured topics articles')

			else

				addCat('Unassessed Featured topics articles')

			end



			-- Topic-specific status categories

			local function addTopicCats(catFormat)

				for _, topic in ipairs(data) do

					addCat(string.format(catFormat, topic.name))

				end

			end

			if status == 'FA' or status == 'FL' then

				addTopicCats('Wikipedia featured topics %s featured content')

			elseif status == 'FFA/GA' or 'GA' then

				addTopicCats('Wikipedia featured topics %s good content')

			else

				addTopicCats('Wikipedia featured topics %s')

			end



			-- Importance categories

			local hasTop, hasHigh, hasMid, hasLow -- These check for dupes

			for _, topic in ipairs(data) do

				local cat, sort

				if topic.status == 'FT' then

					if topic.isMain and not hasTop then

						cat = 'Top-importance Featured topics articles'

						sort = topic.name .. ' ' .. article

						hasTop = true

					elseif not topic.isMain and not hasHigh then

						cat = 'High-importance Featured topics articles'

						hasHigh = true

					end

				else

					if topic.isMain and not hasMid then

						cat = 'Mid-importance Featured topics articles'

						sort = topic.name .. ' ' .. article

						hasMid = true

					elseif not topic.isMain and not hasLow then

						cat = 'Low-importance Featured topics articles'

						hasLow = true

					end

				end

				if cat then

					addCat(cat, sort)

				end

			end



			return cats

		end

	},



	-- Main page date

	{

		id = 'MAINDATE',

		isActive = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local status = articleHistoryObj:getStatusId()

			return args.maindate or status == 'FA' or status == 'FL'

		end,

		makeData = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local status = articleHistoryObj:getStatusId()

			local data = {}



			local function validateMainDate(argName, dataName, dataTimestampName)

				datadataName = argsargName

				if datadataName then

					datadataTimestampName = getYmdDate(datadataName])

					if not datadataTimestampName then

						articleHistoryObj:raiseError(

							string.format(

								"invalid date '%s' detected in parameter '%s'",

								datadataName],

								argName

							),

							'Template:Article history#Invalid date'

						)

					end

				end

			end



			validateMainDate('maindate', 'mainDate', 'mainDateTimestamp')

			if data.mainDate then

				validateMainDate('maindate2', 'mainDate2', 'mainDate2Timestamp')

				if data.mainDate2 and data.mainDateTimestamp >= data.mainDate2Timestamp then

					articleHistoryObj:raiseError(

						"the date in the 'maindate' parameter must be earlier than the date in the 'maindate2' parameter",

						'Template:Article history#Main Page date order'

					)

				end

			end



			data.currentTimestamp = getYmdDate()





			-- Whether the page is a list or not for the purposes of the Main

			-- Page. The first Today's Featured List was on 13 June 2011, so

			-- lists that were featured before then count as articles.

			data.isList = (status == 'FL' or status == 'FFL')

				and (not data.mainDate or data.mainDateTimestamp >= 20110613)



			return data

		end,

		icon = 'Wikipedia-logo-v2.svg',

		iconCaption = 'Main Page trophy',

		text = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data or not data.mainDate then

				return nil

			end



			-- Build the blurb for all the possible combinations of past,

			-- present and future appearances of maindate and maindate2.

			local pagetype = data.isList and 'list' or 'article'

			local mainDateLong = getLongDate(data.mainDate)

			local mainDate2Long = data.mainDate2 and getLongDate(data.mainDate2)

			local todaysFA = "Today's featured " .. pagetype



			local function makeFeaturedLink(date, display)

				return string.format(

					"[[Wikipedia:Today's featured %s/%s|%s]]",

					pagetype,

					date,

					display or date

				)

			end



			local function isPast(timestamp)

				return timestamp < data.currentTimestamp

			end



			local function isCurrent(timestamp)

				return timestamp == data.currentTimestamp

			end



			local function isFuture(timestamp)

				return timestamp > data.currentTimestamp

			end



			if data.mainDate2 then

				if isPast(data.mainDateTimestamp) then

					if isPast(data.mainDate2Timestamp) then

						return string.format(

							"This article appeared on Wikipedia's Main Page as %s on %s, and on %s.",

							todaysFA,

							makeFeaturedLink(mainDateLong),

							makeFeaturedLink(mainDate2Long)

						)

					elseif isCurrent(data.mainDate2Timestamp) then

						return string.format(

							"This article is currently on Wikipedia's Main Page as %s. It also appeared previously on %s.",

							makeFeaturedLink(mainDate2Long, todaysFA),

							makeFeaturedLink(mainDateLong)

						)

					else

						return string.format(

							"This article appeared on Wikipedia's Main Page as %s on %s, and will appear again on %s.",

							todaysFA,

							makeFeaturedLink(mainDateLong),

							makeFeaturedLink(mainDate2Long)

						)

					end

				elseif isCurrent(data.mainDateTimestamp) then

					if isFuture(data.mainDate2Timestamp) then

						return string.format(

							"This article is currently on Wikipedia's Main Page as %s, and will appear again on %s.",

							makeFeaturedLink(mainDateLong, todaysFA),

							makeFeaturedLink(mainDate2Long)

						)

					else

						return nil

					end

				else

					if isFuture(data.mainDate2Timestamp) then

						return string.format(

							"This article will appear on Wikipedia's Main Page as %s on %s, and again on %s.",

							todaysFA,

							makeFeaturedLink(mainDateLong),

							makeFeaturedLink(mainDate2Long)

						)

					else

						return nil

					end

				end

			else

				if isPast(data.mainDateTimestamp) then

					return string.format(

						"This article appeared on Wikipedia's Main Page as %s on %s.",

						makeFeaturedLink(mainDateLong, todaysFA),

						mainDateLong

					)

				elseif isCurrent(data.mainDateTimestamp) then

					return string.format(

						"This article is currently on Wikipedia's Main Page as %s.",

						makeFeaturedLink(mainDateLong, todaysFA),

						mainDateLong

					)

				else

					return string.format(

						"This article will appear on Wikipedia's Main Page as %s on %s.",

						makeFeaturedLink(mainDateLong, todaysFA),

						mainDateLong

					)

				end

			end

		end,

		categories = function (articleHistoryObj, noticeObj)

			local data = noticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local status = articleHistoryObj:getStatusId()

			local cats = {}



			local pagetype = data.isList and 'lists' or 'articles'

			if data.mainDate and data.mainDateTimestamp <= data.currentTimestamp then

				cats#cats + 1 = Category.new(string.format(

					'Featured %s that have appeared on the main page',

					pagetype

				))

				if data.mainDate2 and data.mainDate2Timestamp <= data.currentTimestamp then

					cats#cats + 1 = Category.new(string.format(

						'Featured %s that have appeared on the main page twice',

						pagetype

					))

				else

					cats#cats + 1 = Category.new(string.format(

						'Featured %s that have appeared on the main page once',

						pagetype

					))

				end

			elseif status == 'FA' or status == 'FL' or data.mainDate then

				cats#cats + 1 = Category.new(string.format(

					'Featured %s that have not appeared on the main page',

					pagetype

				))

			end

			return cats

		end

	}

},



-------------------------------------------------------------------------------

-- Actions

-------------------------------------------------------------------------------



-- The actions table contains configuration tables for actions such as featured

-- article candidacies and peer review, etc.

-- Each configuration table can have the following fields:

--

-- id: the main ID for the action. This should be the same as the configuration

--    table key.

-- name: the name of the action. This can be a string or a function. If it is

--    a function, it takes an article history object as its first parameter and

--    the action object as its second parameter, and should return the name.

-- results: a table of possible results for the action. Keys in the table should

--    be a result ID, e.g. "promoted" or "kept", and values should be a subtable

--    with the following fields:

--    id: the result ID. This should be the same as the table key. It will

--        also define a possible input value for the action's result parameter.

--    text: the displayed result text. This may be a string or a function. If it

--        is a function, it takes an article history object as the first

--        parameter and the current action object as the second parameter, and

--        should return the result string.

--    aliases: an array of result ID aliases. Each of these will define a valid

--        value for the action's result parameter.

-- text: The action text. This may be a string or a function. If it is a

--    function, it takes an article history object as the first parameter and

--    the current action object as the second parameter, and should return the

--    text string.

-- categories: The categories set by the notice. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter and the current action object as the

--    second parameter, and should return an array of category objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. This can be

--    a string or a function. If it is a function, it takes an article history

--    object as its first parameter, and should return the caption text. If this

--    is absent, the icon caption is used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



actions = {

	FAC = {

		id = 'FAC',

		name = 'Featured article candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FAR = {

		id = 'FAR',

		name = 'Featured article review',

		aliases = {'FARC'},

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			local result = actionObj.resultId

			if result == 'demoted' or result == 'merged' then

				local status = articleHistoryObj:getStatusId()

				local sortKey = articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

				if status == 'FA' or status == 'FL' then

					sortKey = '#' .. sortKey

				end

				ret#ret + 1 = Category.new(

					'Wikipedia former featured articles',

					sortKey

				)

			end

			return ret

		end

	},

	BP = {

		id = 'BP',

		name = 'Brilliant prose',

		results = {

			nominated = {

				id = 'nominated',

				text = 'Nominated',

				aliases = {'pass', 'promoted', 'nom'}

			}

		}

	},

	RBP = {

		id = 'RBP',

		name = 'Refreshing brilliant prose',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			'not kept' = {

				id = 'not kept',

				text = 'Not kept',

				aliases = {'fail', 'failed', 'remove', 'removed', 'demoted'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			if actionObj.resultId == 'not kept' then

				ret#ret + 1 = Category.new(

					'Wikipedia former brilliant prose',

					articleHistoryObj.currentTitle.text

				)

			end

			return ret

		end

	},

	FLC = {

		id = 'FLC',

		name = 'Featured list candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FLR = {

		id = 'FLR',

		name = 'Featured list removal candidate',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			local result = actionObj.resultId

			if result == 'demoted' or result == 'merged' then

				local sortKey

				if articleHistoryObj:getStatusId() == 'FL' then

					sortKey = '#' .. articleHistoryObj.currentTitle.subjectPageTitle.prefixedText

				else

					sortKey = articleHistoryObj.currentTitle.text

				end

				ret#ret + 1 = Category.new(

					'Wikipedia former featured lists',

					sortKey

				)

			end

			return ret

		end

	},

	FTC = {

		id = 'FTC',

		name = 'Featured topic candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FTR = {

		id = 'FTR',

		name = 'Featured topic removal candidate',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		}

	},

	FPOC = {

		id = 'FPOC',

		name = 'Featured portal candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	FPOR = {

		id = 'FPOR',

		name = 'Featured portal review',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		}

	},

	GAN = {

		id = 'GAN',

		name = 'Good article nominee',

		aliases = {'GAC'},

		results = {

			listed = {

				id = 'listed',

				text = 'Listed',

				aliases = {'pass', 'passed', 'promoted'}

			},

			'not listed' = {

				id = 'not listed',

				text = 'Not listed',

				aliases = {'fail', 'failed', 'not promoted'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			if actionObj.resultId == 'not listed' then

				local status = articleHistoryObj:getStatusId()

				if status ~= 'FA'

					and status ~= 'GA'

					and status ~= 'FFA'

				then

					ret#ret + 1 = Category.new(

						'Former good article nominees',

						articleHistoryObj.currentTitle.text

					)

				end

			end

			return ret

		end

	},

	GAR = {

		id = 'GAR',

		name = 'Good article reassessment',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			delisted = {

				id = 'delisted',

				text = 'Delisted',

				aliases = {'fail', 'failed'}

			},

			listed = {

				id = 'listed',

				text = 'Listed'

			},

			'not listed' = {

				id = 'not listed',

				text = 'Not listed'

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			if actionObj.resultId == 'delisted' then

				local status = articleHistoryObj:getStatusId()

				if status ~= 'FA'

					and status ~= 'GA'

				then

					ret#ret + 1 = Category.new(

						'Delisted good articles',

						articleHistoryObj.currentTitle.text

					)

				end

			end

		end

	},

	GTC = {

		id = 'GTC',

		name = 'Good topic candidate',

		results = {

			promoted = {

				id = 'promoted',

				text = 'Promoted',

				aliases = {'pass', 'passed'}

			},

			'not promoted' = {

				id = 'not promoted',

				text = 'Not promoted',

				aliases = {'fail', 'failed'}

			}

		}

	},

	GTR = {

		id = 'GTR',

		name = 'Good topic removal candidate',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'pass', 'passed', 'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'fail', 'failed', 'remove', 'removed'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			}

		}

	},

	PR = {

		id = 'PR',

		name = 'Peer review',

		results = {

			reviewed = {

				id = 'reviewed',

				text = 'Reviewed',

				aliases = {'_BLANK'}

			},

			'not reviewed' = {

				id = 'not reviewed',

				text = 'Not reviewed',

			}

		},

		categories = {'Old requests for peer review'}

	},

	WPR = {

		id = 'WPR',

		name = function (articleHistoryObj, actionObj)

			local names = {

				approved = 'WikiProject approved revision',

				copyedited = 'Guild of Copy Editors',

				collaboration = 'WikiProject collaboration',

				maindate = "Today's featured article"

			}

			local result = actionObj.resultId

			return result and namesresult or 'WikiProject peer review'

		end,

		results = {

			approved = {

				id = 'approved',

				text = function(articleHistoryObj, actionObj)

					if actionObj.oldid then

						local url = mw.uri.fullUrl(

							articleHistoryObj.currentTitle.prefixedText,

							{diff = 'cur', oldid = actionObj.oldid}

						)

						return string.format(

							'[%s %s]',

							tostring(url),

							'Diff to current version'

						)

					else

						error(string.format(

							"No oldid detected for the approved version; " ..

								"please set the 'action%doldid' parameter " ..

								"or give the 'action%dresult' parameter a " ..

								"different value.",

							actionObj.paramNum,

							actionObj.paramNum

						))

					end

				end,

				aliases = {'approved version'}

			},

			copyedited = {

				id = 'copyedited',

				text = 'Copyedited',

				aliases = {'copyedit', 'proofread'}

			},

			maindate = {

				id = 'maindate',

				text = 'Main Page'

			},

			collaborated = {

				id = 'collaborated',

				text = 'Collaborated',

				aliases = {'cotw', 'collaboration'}

			},

			reviewed = {

				id = 'reviewed',

				text = 'Reviewed',

				aliases = {'_BLANK'}

			}

		},

		categories = function (articleHistoryObj, actionObj)

			local ret = {}

			local result = actionObj.resultId

			if result == 'copyedited' then

				ret1 = Category.new('Articles copy edited by the Guild of Copy Editors')

			end

			return ret

		end

	},

	WAR = {

		id = 'WAR',

		name = 'WikiProject A-class review',

		results = {

			approved = {

				id = 'approved',

				text = 'Approved',

				aliases = {'pass', 'passed'}

			},

			'not approved' = {

				id = 'not approved',

				text = 'Not approved',

				aliases = {'fail', 'failed', 'not reviewed'}

			},

			reviewed = {

				id = 'reviewed',

				text = 'Reviewed',

				aliases = {'_BLANK'}

			},

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'keep'}

			},

			demoted = {

				id = 'demoted',

				text = 'Demoted',

				aliases = {'demote'}

			}

		}

	},

	AFD = {

		id = 'AFD',

		name = 'Articles for deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' ={

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	MFD = {

		id = 'MFD',

		name = 'Miscellany for deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' = {

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	TFD = {

		id = 'TFD',

		name = 'Templates for discussion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			merged = {

				id = 'merged',

				text = 'Merged',

				aliases = {'merge'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' = {

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				text = 'Redirected',

				aliases = {'redirect'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	CSD = {

		id = 'CSD',

		name = 'Candidate for speedy deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete', 'speedily deleted', 'speedy delete'}

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			prod = {

				id = 'prod',

				text = 'Converted to [[WP:PROD|proposed deletion]]',

				aliases = {'prodded'}

			},

			afd = {

				id = 'afd',

				text = 'Sent to [[WP:AFD|articles for deletion]]',

				aliases = {'afded'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	PROD = {

		id = 'PROD',

		name = 'Proposed deletion',

		results = {

			kept = {

				id = 'kept',

				text = 'Kept',

				aliases = {'withdrawn', 'keep'}

			},

			deleted = {

				id = 'deleted',

				text = 'Deleted',

				aliases = {'delete'}

			},

			'speedily kept' = {

				id = 'speedily kept',

				text = 'Speedily kept',

				aliases = {'speedy keep'}

			},

			'speedily deleted' = {

				id = 'speedily deleted',

				text = 'Speedily deleted',

				aliases = {'speedy delete'}

			},

			redirected = {

				id = 'redirected',

				text = 'Redirected',

				aliases = {'redirect'}

			},

			afd = {

				id = 'afd',

				text = 'Sent to [[WP:AFD|articles for deletion]]',

				aliases = {'afded'}

			},

			renamed = {

				id = 'renamed',

				text = 'Renamed',

				aliases = {'rename', 'move', 'moved'}

			},

		}

	},

	DRV = {

		id = 'DRV',

		name = 'Deletion review',

		results = {

			endorsed = {

				id = 'endorsed',

				text = 'Endorsed',

				aliases = {'endorse'}

			},

			relisted = {

				id = 'relisted',

				text = 'Relisted',

				aliases = {'relist'}

			},

			overturned = {

				id = 'overturned',

				text = 'Overturned',

				aliases = {'overturn'}

			},

			restored = {

				id = 'restored',

				text = 'Restored',

				aliases = {'restore'}

			},

			'no consensus' = {

				id = 'no consensus',

				text = 'No consensus'

			}

		}

	}

},



-------------------------------------------------------------------------------

-- Collapsible notices

-------------------------------------------------------------------------------



-- The collapsibleNotices table contains configuration tables for notices that

-- go in the collapsible part of the template, underneath the actions.

-- Each configuration table can have the following fields:

--

-- id: the main ID for the notice. This should be the same as the configuration

--    table key.

-- isActive: a function that should return a truthy value if the notice should

--    be displayed, and a falsy value if not. (Falsy values are false and nil,

--    and truthy values are everything else.) The function takes an article

--    history object as its first parameter.

-- makeData: a function that should return a table of data to be used by other

--    functions in this notice configuration table. It can be accessed using

--    noticeObj:getData().

-- icon: the filename of the notice icon, minus the "File:" prefix.

-- iconCaption: the icon caption.

-- iconSize: The icon size, including "px" suffix. The default is defined in

--    defaultIconSize.

-- text: The notice text. This may be a string or a function. If it is a

--    function, it takes an article history object as the first parameter, and

--    the current collapsible notice object as the second parameter, and should

--    return the text string.

-- categories: The categories set by the notice. This may be an array of

--    category names, or a function. If it is a function, it takes an article

--    history object as the first parameter, and the current collapsible notice

--    object as the second parameter, and should return an array of category

--    objects.

-- noticeBarIcon: the icon to use for the notice bar. This can be a string, or

--    a function, or true. If it is a function it takes an article history

--    object as the first parameter, and should output the icon filename. If it

--    is true, it uses the value of icon. If it is nil then no notice bar icon

--    will be displayed.

-- noticeBarIconCaption: the caption to use for the notice bar icon. This can be

--    a string or a function. If it is a function, it takes an article history

--    object as its first parameter, and should return the caption text. If this

--    is absent, the icon caption is used instead.

-- noticeBarIconSize: the size of the notice bar icon, including "px" suffix.

--    The default is set by defaultNoticeBarIconSize.



collapsibleNotices = {

	-- DYK

	{

		id = 'DYK',

		icon = 'Symbol question.svg',

		iconCaption = 'Did You Know',

		noticeBarIcon = true,

		isActive = function (articleHistoryObj)

			return isActiveDatedObject(articleHistoryObj, 'dyk')

		end,

		makeData = function (articleHistoryObj)

			return makeDateData(articleHistoryObj, 'dyk', {'entry', 'nom', 'ignoreerror'})

		end,

		text = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			for _, t in ipairs(data) do

				local raPage = 'Wikipedia:Recent additions/' ..

					getDate('Y/F#j F Y', t.date)

				if not titleExists(raPage) then

					raPage = 'Wikipedia:Recent additions'

				end

				t.link = raPage

			end

			local fact = 'fact from this article'

			local nomPage = data1].nom or ('Template:Did you know nominations/' .. articleHistoryObj.currentTitle.text)

			if titleExists(nomPage) then

				fact = '[[' .. nomPage .. '|' .. fact .. ']]'

			end

			local blurb = "A " .. fact .. " appeared on " ..

				"Wikipedia's [[Main Page]] in the " ..

				"''\"[[:Template:Did you know|Did you know?]]\"'' " ..

				"column on $1."

			return makeDateText(data, blurb, true)

		end,

		collapsibleText = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local ctext = {}

			if #data == 1 and data1].entry then

				ctext#ctext + 1 = string.format(

					"The text of the entry was: ''Did you know %s''",

					data1].entry

				)

			else

				local entries = {}

				local lastEntryDate

				for _, t in ipairs(data) do

					entries#entries + 1 = t.entry

					lastEntryDate = t.date

				end

				if #entries == 1 then

					ctext#ctext + 1 = string.format(

						"The text of the entry for %s was: ''Did you know %s''",

						lastEntryDate, entries1

					)

				elseif #entries > 1 then

					ctext#ctext + 1 = 'The text of the entries was:\n'

					local list = mw.html.create('ul')

					for _, t in ipairs(data) do

						if t.entry then

							list:tag('li'):wikitext(string.format(

								"%s: ''Did you know %s''",

								t.date, t.entry

							))

						end

					end

					ctext#ctext + 1 = tostring(list)

				end

			end

			if #ctext > 0 then

				return table.concat(ctext)

			else

				return nil

			end

		end,

		categories = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local cats = {}

			do

				local status = articleHistoryObj:getStatusId()

				local statusCat

				if status == 'FA' then

					statusCat = 'Wikipedia Did you know articles that are featured articles'

				elseif status == 'FL' then

					statusCat = 'Wikipedia Did you know articles that are featured lists'

				elseif status == 'GA' or status == 'FFA/GA' then

					statusCat = 'Wikipedia Did you know articles that are good articles'

				else

					statusCat = 'Wikipedia Did you know articles'

				end

				cats#cats + 1 = Category.new(statusCat)

			end

			for _, t in ipairs(data) do

				if not t.ignoreerror then

					if t.entry then

						local mCheckDYKEntry = require('Module:Check DYK hook')

						if not mCheckDYKEntry._isValidHook(t.entry) then

							cats#cats + 1 = Category.new('Pages with a malformed DYK entry')

						end

					else

						cats#cats + 1 = Category.new('Pages with a missing DYK entry')

					end

				end

			end

			return cats

		end

	},



	-- ITN

	{

		id = 'ITN',

		isActive = function (articleHistoryObj)

			return isActiveDatedObject(articleHistoryObj, 'itn')

		end,

		makeData = function (articleHistoryObj)

			return makeDateData(articleHistoryObj, 'itn', {'link'})

		end,

		icon = 'Globe current.svg',

		iconCaption = 'In the news',

		noticeBarIcon = true,

		noticeBarIconSize = '20px',

		text = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local dates = {}

			for _, t in ipairs(data) do

				local date = {}

				if t.link then

					date.link = t.link

				elseif t.ymdDate >= 20110701 then

					date.link = string.format(

						'Wikipedia:In the news/Candidates/%s %d',

						t.month,

						t.year

					)

				elseif t.ymdDate >= 20090101 then

					date.link = string.format(

						'Wikipedia:ITN archives/%d/%s',

						t.year,

						t.month

					)

				elseif t.ymdDate >= 20050101 then

					date.link = string.format(

						'Portal:Current events/%d %s %d',

						t.year,

						t.month,

						t.day

					)

				end

				date.date = t.date

				dates#dates + 1 = date

			end

			local intro

			if #data > 1 then

				intro = 'News items involving this article were'

			else

				intro = 'A news item involving this article was'

			end

			local blurb = intro ..

				" featured on Wikipedia's [[Main Page]] in the " ..

				"''\"[[Template:In the news|In the news]]\"'' column on $1."

			return makeDateText(dates, blurb)

		end,

		categories = function (articleHistoryObj, collapsibleNoticeObj)

			local cats = {}

			cats1 = Category.new('Wikipedia In the news articles')

			return cats

		end

	},



	-- On This Day

	{

		id = 'OTD',

		isActive = function (articleHistoryObj)

			return isActiveDatedObject(articleHistoryObj, 'otd')

		end,

		makeData = function (articleHistoryObj)

			return makeDateData(articleHistoryObj, 'otd', {'link', 'oldid'})

			-- TODO: remove 'link' after it is no longer needed for tracking

		end,

		icon = 'Nuvola apps date.svg',

		iconCaption = 'On this day...',

		noticeBarIcon = true,

		noticeBarIconSize = '20px',

		text = function (articleHistoryObj, collapsibleNoticeObj)

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if not data then

				return nil

			end

			local dates = {}

			for _, t in ipairs(data) do

				local date = {}

				date.date = t.date

				date.link = t.link

				if t.oldid then

					-- TODO: Move this inside the main module

					local oldid = tonumber(t.oldid)

					if oldid and

						math.floor(oldid) == oldid and

						oldid > 0 and

						oldid < math.huge

					then

						-- If the oldid is valid, it takes precedence over

						-- explicit links.

						date.link = 'Special:PermaLink/' .. t.oldid

					else

						collapsibleNoticeObj:addWarning(

							string.format(

								"invalid oldid '%s' detected in parameter '%s'; " ..

									"if an oldid is specified it must be a positive integer",

								t.oldid,

								t.argPrefix .. 'oldid'

							),

							'Template:Article history#Invalid oldid'

						)

					end

				end

				dates#dates + 1 = date

			end

			local intro

			if #data > 1 then

				intro = 'Facts from this article were'

			else

				intro = 'A fact from this article was'

			end

			local blurb = intro ..

				" featured on Wikipedia's [[Main Page]] in the " ..

				"''\"[[Wikipedia:Selected anniversaries|On this day...]]\"'' " ..

				"column on $1."

			return makeDateText(dates, blurb)

		end,

		categories = function (articleHistoryObj, collapsibleNoticeObj)

			local cats = {}

			cats1 = Category.new('Selected anniversaries articles')

			local data = collapsibleNoticeObj:getData(articleHistoryObj)

			if data then

				for _, t in ipairs(data) do

					if t.link then

						cats#cats + 1 = Category.new(

							'Article history templates with linked otd dates'

						)

						break

					end

				end

			end

			return cats

		end

	},



	-- Article Collaboration and Improvement Drive

	{

		id = 'ACID',

		isActive = function (articleHistoryObj)

			return articleHistoryObj.args.aciddate

		end,

		icon = 'Article Collaboration and Improvement Drive.svg',

		iconCaption = 'Article Collaboration and Improvement Drive',

		noticeBarIcon = true,

		noticeBarIconSize = '20px',

		text = function (articleHistoryObj)

			local args = articleHistoryObj.args

			local blurb = 'This article was on the ' ..

				'[[WP:ACID|Article Collaboration and Improvement Drive]] ' ..

				'for the week of %s.'

			local date = validateDate('aciddate', args.aciddate)

			return string.format(blurb, date)

		end

	},



	-- League of Copy Editors

	{

		id = 'LOCE',

		isActive = function (articleHistoryObj)

			return articleHistoryObj.args.loceNotAnActiveOption

		end,

		icon = 'LoCiconRevised.png',

		iconCaption = 'League of Copyeditors',

		iconSize = '25px',

		noticeBarIcon = true,

		text = 'This article, or a portion of it, was copyedited by the ' ..

			'[[WP:LoCE|League of Copyeditors]].'

	}

},



-------------------------------------------------------------------------------

-- Notice bar icons

-------------------------------------------------------------------------------



-- This table holds configuration tables for notice bar icons that don't appear

-- as part of a row. Other notice bar icons are handled in the statuses,

-- notices, actions, and collapsibleNotices tables.

-- It accepts the following fields:

-- isActive: a function that should return a truthy value if the notice should

--    be displayed, and a falsy value if not. (Falsy values are false and nil,

--    and truthy values are everything else.) The function takes an article

--    history object as its first parameter.

-- icon: the filename of the notice bar icon, minus the "File:" prefix.



noticeBarIcons = {

	-- Peer review, or NA status

	{

		isActive = function (articleHistoryObj)

			local status = articleHistoryObj:getStatusId()

			-- @XXX: This is what the template does, but we should take into

			-- account peer review actions as well.

			return not status or status == 'PR' or status == 'NA'

		end,

		icon = 'Nuvola apps kedit.svg'

	},



	-- Wikipedia 1.0

	{

		isActive = function (articleHistoryObj)

			return articleHistoryObj.args'v1.0NotAnActiveOption'

		end,

		icon = 'WP1 0 Icon.svg'

	}

},



-------------------------------------------------------------------------------

-- Extra categories

-------------------------------------------------------------------------------



-- This table contains categories that don't appear as part of a row. It is an

-- array of functions; each function takes an article history object as input

-- and must return an array of category objects as output.



extraCategories = {

	-- Four award

	function (articleHistoryObj)

		local yesno = require('Module:Yesno')

		local ret = {}

		local isFour = yesno(articleHistoryObj.args.four)

		if isFour then

			ret#ret + 1 = Category.new('Wikipedia four award articles')

		elseif isFour == false then

			ret#ret + 1 = Category.new('Wikipedia articles rejected for Four awards')

		elseif articleHistoryObj:getStatusId() == 'FA' then

			local isDYK = false

			for _, obj in ipairs(articleHistoryObj:getCollapsibleNoticeObjects()) do

				if obj.id == 'DYK' then

					isDYK = true

					break

				end

			end

			if isDYK then

				for _, obj in ipairs(articleHistoryObj:getActionObjects()) do

					if obj.id == 'GAN' and obj.resultId == 'listed' then

						ret#ret + 1 = Category.new('Possible Wikipedia four award articles')

						break

					end

				end

			end

		end

		return ret

	end,



	-- Deletion to Quality award

	function (articleHistoryObj)

		local ret = {}

		local status = articleHistoryObj:getStatusId()

		if status == 'FA' or status == 'FL' or status == 'GA' then

			local iAfd = 0

			local hasAfd = false

			local actionObjects = articleHistoryObj:getActionObjects()

			for i, obj in ipairs(actionObjects) do

				if obj.id == 'AFD' then

					iAfd = i

					hasAfd = true

					break

				end

			end

			if hasAfd then

				local function hasNomination(id, result)

					for i = iAfd + 1, #actionObjects do

						local obj = actionObjectsi

						if obj.id == id and obj.resultId == result then

							return true

						end

					end

					return false

				end

				if status == 'GA' and hasNomination('GAN', 'listed') or

					status == 'FL' and hasNomination('FLC', 'promoted') or

					status == 'FA' and hasNomination('FAC', 'promoted')

				then

					ret#ret + 1 = Category.new('Deletion to Quality Award candidates')

				end

			end

		end

		return ret

	end,

},



-------------------------------------------------------------------------------

-- Parameters

-------------------------------------------------------------------------------



-- The parameter values used to generate the page actions. These are used as

-- Lua patterns, so any of the magic characters *+-.^$%[] should be escaped

-- with a preceding % symbol.

actionParamPrefix = 'action',

actionParamSuffixes = {

	'' = 'code',

	date = 'date',

	link = 'link',

	result = 'resultCode',

	oldid = 'oldid'

},



-- The parameter used to set the current status.

currentStatusParam = 'currentstatus',



-------------------------------------------------------------------------------

-- Other settings

-------------------------------------------------------------------------------



-- If this number or fewer of collapsible rows are present (including actions

-- and collapsible notices) they will not be collapsed. If this is set to the

-- string "all", all rows will always be visible. Otherwise, the input must be

-- a number. The default is three rows.

uncollapsedRows = 3,



-- The default size for icons. The default is 30px.

defaultIconSize = '30px',



-- The default size for status icons. The default is 50px.

defaultStatusIconSize = '50px',



-- The default size for status icons for multi status templates. The default is 30px.

defaultMultiStatusIconSize = '30px',



-- The default size for notice bar icons. The default is 15px.

defaultNoticeBarIconSize = '15px',



-- The default size for collapsible status icons. The default is 50px.

defaultCollapsibleNoticeIconSize = '20px',



-------------------------------------------------------------------------------

-- Messages

-------------------------------------------------------------------------------



msg = {



-- The heading for the collapsible table of actions if we are in the main

-- namespace or the talk namespace.

'milestones-header' = 'Article milestones',



-- The heading for the collapsible table of actions if we are in a different

-- namespace.

-- $1 - the subject namespace name.

'milestones-header-other-ns' = '$1 milestones',



-- The milestones date header.

'milestones-date-header' = 'Date',



-- The milestones process header.

'milestones-process-header' = 'Process',



-- The milestones result header.

'milestones-result-header' = 'Result',



-- The format to display the action dates in. The syntax is the same as the

-- #time parser function.

'action-date-format' = 'F j, Y',



-- The category to use if any errors are detected.

'error-category' = 'Article history templates with errors',



-- Define boilerplate text for error messages and warnings, both with and

-- without help links.

-- $1 - the error message

-- $2 - a link to a help page and section for the error

'error-message-help' = 'Error: $1 ([[$2|help]]).',

'error-message-nohelp' = 'Error: $1.',

'warning-help' = 'Warning: $1 ([[$2|help]]).',

'warning-nohelp' = 'Warning: $1.',



-- Error for row objects that should output notice bar icons but for which no

-- icon values could be found.

'row-error-missing-icon' = "notice bar icon config set to 'true' but no " ..

	'image could be found',



-- A help link for row-error-missing-icon

'row-error-missing-icon-help' = 'Template:Article history#Missing icon',



-- Error for action objects that aren't passed a code.

-- $1 - the parameter name for the code

'action-error-no-code' = "no action code found in the '$1' parameter; " ..

	"please add a code or remove other parameters starting with '$1'",



-- A help link for action-error-no-code

'action-error-no-code-help' = 'Template:Article history#Action codes',



-- Error for action objects that are passed an invalid code.

-- $1 - the code that the user input

-- $2 - the parameter name for the code

'action-error-invalid-code' = "invalid action code '$1' passed to the '$2' parameter",



-- A help link for action-error-invalid-code

'action-error-invalid-code-help' = 'Template:Article history#Action codes',



-- Error for action objects with blank result parameters, where result

-- parameters are required for the action's ID.

-- $1 - the action ID

-- $2 - the result parameter name

'action-error-blank-result' = "the '$1' action requires a result code; " ..

	"please add a result code to parameter '$2'",



-- A help link for action-error-blank-result

'action-error-blank-result-help' = 'Template:Article history#Action results',



-- Error for action objects with invalid result parameters.

-- $1 - the result code that the user input

-- $2 - the action ID

-- $3 - the result parameter name

'action-error-invalid-result' = "invalid result '$1' for action '$2' " ..

	"detected in parameter '$3'",



-- A help link for action-error-invalid-result

'action-error-invalid-result-help' = 'Template:Article history#Action results',



-- Warning for action objects with invalid dates.

-- $1 - the date input by the user

-- $2 - the date parameter name

'action-warning-invalid-date' = "invalid date '$1' detected in parameter '$2'",



-- A help link for action-warning-invalid-date

'action-warning-invalid-date-help' = 'Template:Article history#Invalid date',



-- Error for action objects with no dates.

-- $1 - the parameter number

-- $2 - the date parameter name

-- $3 - the action parameter name

'action-warning-no-date' = "no date specified for action $1; " ..

	"please add a date to parameter '$2' or remove the other parameters " ..

	"beginning with '$3'",



-- A help link for action-warning-no-date

'action-warning-no-date-help' = 'Template:Article history#No date',



-- The text to display in place of the action date if it is missing.

'action-date-missing' = '?',



-- Error for action objects with invalid oldids.

-- $1 - the oldid input by the user

-- $2 - the oldid parameter name

'action-warning-invalid-oldid' = "invalid oldid '$1' detected in parameter '$2'; " ..

	"if an oldid is specified it must be a positive integer",



-- A help link for action-warning-invalid-oldid

'action-warning-invalid-oldid-help' = 'Template:Article history#Invalid oldid',



-- Error for invalid current status codes.

-- $1 - the code input by the user

'articlehistory-warning-invalid-status' = "'$1' is not a valid status code",



-- A help link for articlehistory-warning-invalid-status

'articlehistory-warning-invalid-status-help' = 'Template:Article history#Invalid status',



-- Warning for invocations that specify a current status without specifying any

-- actions.

'articlehistory-warning-status-no-actions' = "a current status was supplied " ..

	'without any actions',



-- A help link for articlehistory-warning-status-no-actions

'articlehistory-warning-status-no-actions-help' = 'Template:Article history#No actions',



-- The text to display the current status at the bottom of the collapsible

-- table.

-- $1 - the current status name

'status-blurb' = "Current status: '''$1'''",



-- The text to display at the bottom of the collapsible table if the current

-- status is unknown.

'status-unknown' = '?',



}



-------------------------------------------------------------------------------

--                              CONFIG TABLE END

-------------------------------------------------------------------------------



}

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook