Module:NewDYKnomination

-- This module implements.

local p = {}

local lang = mw.language.getContentLanguage

--- -- Output template ---

-- This template contains the final output of the module. Parameters like -- ${PARAMETER_NAME} are substituted with the results of the output template -- parameter functions below.

local OUTPUT_TEMPLATE = [=[ ]=]

--- -- Helper functions ---

-- Creates a formatted error message that can be used with substitution. local function formatError(msg) return string.format('', msg) end

-- Creates a boilerplate error message for invalid titles. local function formatInvalidTitleError(page, pageType) local msg = string.format(		'"%s" is not a valid %s; check for bad characters',		page, pageType	) return formatError(msg) end

-- Make a title object from the given page name. Return nil if the title is -- invalid. local function makeTitle(page) local title = mw.title.new(page) if title and title.text ~= "" then return title end end

-- Same as. If the page is an invalid title, returns nil. local function getRootPageText(page) local title = makeTitle(page) if title and title.rootPageTitle then return title.rootPageTitle.text end end

-- Makes template invocations for templates like and -- . local function makeFileTemplateInvocation(name, first, second) if not first then return nil end local ret = {} ret[#ret + 1] = ''' return table.concat(ret) end

-- Normalize positional parameters for a template invocation. If any of the -- parameters contain equals signs, the parameters are all prefixed with -- numbers. local function normalizeTemplateParameters(params) local ret = {} local hasEquals = false for i, param in ipairs(params) do		if param:find('=') then hasEquals = true break end end if hasEquals then for i, param in ipairs(params) do			ret[i] = string.format('%d=%s', i, param) end else for i, param in ipairs(params) do			ret[i] = param end end return ret end

-- Makes a link to a user's user page and talk page, like found in a standard -- signature. local function makeUserLinks(user) return string.format(		'',		user	) end

-- Returns an array of authors. If the user didn't specify any authors, the -- first one is the result of. local function getNormalisedAuthors(data) local authors = {} for i, author in ipairs(data.authors) do		authors[i] = author end authors[1] = authors[1] or data.revisionUser return authors end

-- Removes gaps from sparse arrays. This is used to process numbered arguments -- like author2 and ALT4. local compressSparseArray = require("Module:TableTools").compressSparseArray

-- Splits numbered arguments by their prefixes. A table of arguments like this: --   {foo1 = "foo1", foo2 = "foo2", bar3 = "bar3"} -- Would be turned into this: --   {foo = {[1] = "foo1", [2] = "foo2"}, bar = {[3] = "bar3"}} -- Note that the subtables of the returned tables are not normal arrays, but -- sparse arrays (there can be gaps between values). local function splitByPrefix(args) local ret = {} for key, val in pairs(args) do		if type(key) == 'string' then local prefix, num = key:match('^(.-)([1-9][0-9]*)$') if prefix then num = tonumber(num) ret[prefix] = ret[prefix] or {} ret[prefix][num] = val end end end return ret end

-- Returns an array of numbered arguments with the given prefixes. Earlier -- prefixes have precedence, and args[prefix] will overwrite args[prefix .. 1]. -- For example, for this arguments table: --  { --      author = "author", --     author1 = "author1", --     author2 = "author2, --      creator2 = "creator2" --   } -- The function call getPrefixedArgs(args, splitArgs, {'creator', 'author'}) -- will produce: --   {"author", "creator2"} -- -- Parameters: -- args - the table of arguments specified by the user -- splitArgs - the table of arguments as processed by splitByPrefix -- prefixes - an array of prefixes local function getPrefixedArgs(args, splitArgs, prefixes)	local ret = {}	for i, prefix in ipairs(prefixes) do		if splitArgs[prefix] then			for num, val in pairs(splitArgs[prefix]) do				if not ret[num] then					ret[num] = val				end			end		end	end	-- Allow prefix to overwrite prefix1.	for _, prefix in ipairs(prefixes) do		local val = args[prefix]		if val then			ret[1] = val			break		end	end	return compressSparseArray(ret) end

--- -- Output template parameter functions ---

-- The results of these functions are substituted into parameters in the -- output template. The parameters look like ${PARAMETER_NAME}. Trying to use -- a parameter that doesn't have a function defined here will result in an -- error. -- -- The functions take a data table as a single argument. This table contains -- the following fields: -- * errors - a table of formatted error messages that were found when --    processing the input. -- * args - the table of arguments supplied by the user. -- * articles - an array of the article names found in the arguments. -- * authors - an array of the expanders/creators/writers/authors found in the --    arguments. -- * revisionUser - the user that last edited the page. As this module is only --    substituted, this is always the current user. -- * alts - an array of the ALT hooks found in the arguments. -- * title - the mw.title object for the current page. -- -- Template parameter functions should return a string, false, or nil. -- Functions returning false or nil will be treated as outputting the empty -- string "".

local params = {}

-- Renders any errors that were found when processing the input. function params.INPUT_ERRORS(data) local nErrors = #data.errors if nErrors > 1 then return '\n* ' .. table.concat(data.errors, '\n* ') elseif nErrors == 1 then return '\n' .. data.errors[1] end end

-- The current month and year, e.g. "March 2015". function params.MONTH_AND_YEAR return lang:formatDate('F Y') end

-- The contents of the heading. function params.HEADING(data) return table.concat(data.articles, ', ') end

function params.NOMINATOR(data) return data.revisionUser end

-- The current subpage name. function params.NOM_SUBPAGE(data) if string.match(data.title.text,"/") then return string.gsub(data.title.text,data.title.rootText .. "/","") else return data.title.text end end

-- Other arguments for the nompage link template, separated by pipes. function params.NOMPAGE_LINK_ARGS(data) local vals = normalizeTemplateParameters(data.articles) return table.concat(vals, '|') end

-- All of the image display code. function params.IMAGE(data) local args = data.args if not args.image then return nil end local image = getRootPageText(args.image) if not image then image = formatInvalidTitleError(args.image, 'image name') end local caption = args.caption or args.rollover local template = [=[

]=]	return string.format(		template,		image,		caption or 'CAPTION TEXT GOES HERE'	) end

-- The template. function params.DYK_LISTEN(data) local args = data.args return makeFileTemplateInvocation(		'DYK Listen',		args.sound,		args.soundcaption	) end

-- The template. function params.DYK_WATCH(data) local args = data.args return makeFileTemplateInvocation(		'DYK Watch',		args.video,		args.videocaption	) end

-- The hook text. function params.HOOK(data) return data.args.hook or "... that ....?" end

-- All of the ALT hooks that were specified with the ALT1, ALT2, ... etc. -- parameters. function params.ALT_HOOKS(data) local ret = {} for i, alt in ipairs(data.alts) do		ret[i] = string.format("\n** ALT%d: %s", i, alt) end return table.concat(ret) end

-- A note saying which nomination the submitter reviewed. function params.REVIEWED(data) local args = data.args local ret = "\n** Reviewed: " if args.reviewed then local reviewedTitle = makeTitle(			'Template:Did you know nominations/' .. args.reviewed		) if reviewedTitle and reviewedTitle.exists then ret = ret .. string.format(				'%s',				reviewedTitle.prefixedText,				reviewedTitle.subpageText			) else local reviewedTitle = makeTitle(				'Wikipedia:Did you know nominations/' .. args.reviewed				) if reviewedTitle and reviewedTitle.exists then ret = ret .. string.format(					'%s',					reviewedTitle.prefixedText,					reviewedTitle.subpageText				) else ret = ret .. args.reviewed end end end return ret end

-- A comment. function params.COMMENT(data) if data.args.comment then return "\n** Comment: " .. data.args.comment end end

-- The status of the article when it was nominated for DYK. function params.STATUS(data) local status = data.args.status status = status and status:lower

-- Created if status == 'new' then return 'Created' -- Expanded elseif status == 'expanded' or status == 'expansion' then return '5x expanded' -- Moved to mainspace elseif status == 'mainspace' or status == 'moved' then return 'Moved to mainspace' -- Converted from a redirect elseif status == 'convert' or status == 'converted' or status == 'redirect' then return 'Converted from a redirect'

-- Improved to GA	elseif status == 'ga' then return 'Improved to Good Article status'

-- Default else return 'Created/expanded' end end

-- A list of the authors, with user and user talk links. function params.AUTHORS(data) local authors = getNormalisedAuthors(data) for i, author in ipairs(authors) do		authors[i] = makeUserLinks(author) end local separator = ', ' local conjunction if #authors > 2 then conjunction = ', and ' else conjunction = ' and ' end return mw.text.listToText(authors, separator, conjunction) end

function params.QPQS_REQUIRED(data) local qpqsNeeded, numPriorNoms = p.getRequiredQpqCount(data.revisionUser) if qpqsNeeded == 2 then return "2. DYK is currently in unreviewed backlog mode and nominator has "..numPriorNoms.." past nominations." elseif qpqsNeeded == 1 then return "1. Nominator has "..numPriorNoms.." past nominations." else -- if user has < 5 noms, exact count is unknown as it is not present on the json page return "0. Nominator has less than 5 past nominations." end end

-- Warning to check that the credits are correct. function params.CHECK_CREDITS_WARNING(data) if #data.articles > 1 then return 'Please check to make sure these auto-generated credits are correct.' end end

-- DYK credits. These are used by the bot to credit people on their talk pages -- and to tag articles. function params.CREDITS(data) local authors = getNormalisedAuthors(data) local articles = data.articles local nompage = params.NOM_SUBPAGE(data) local nominator = data.revisionUser local nominatorIsAuthor = false for i, author in ipairs(data.authors) do		if author == nominator then nominatorIsAuthor = true break end end

local ret = {}

local function addTemplate(template, article, user, subpage) local params = normalizeTemplateParameters{article, user} if subpage then table.insert(params, 'subpage=' .. subpage) end ret[#ret + 1] = string.format(			'* ',			template,			table.concat(params, '|')		) end

-- First article, a special case do local article = articles[1] addTemplate(			'DYKmake',			article,			authors[1],			nompage		) for i = 2, #authors do			addTemplate('DYKmake', article, authors[i], nompage) end if not nominatorIsAuthor then addTemplate('DYKnom', article, nominator) end end

-- Second article and up	for i = 2, #articles do		local article = articles[i] for j, author in ipairs(authors) do			addTemplate('DYKmake', article, author, nompage) end if not nominatorIsAuthor then addTemplate('DYKnom', article, nominator) end end

return table.concat(ret, '\n') end

-- If a file was displayed, use the template so that it doesn't spill -- over into the next nomination. function params.FILE_BREAK(data) local args = data.args if args.image or args.sound or args.video then return '' end end

--- -- Exports ---

function p._main(args, frame, title) -- Subst check. -- Check for the frame object as well to make debugging easier from the -- debug console. if frame and not mw.isSubsting then return ' ' .. 'This template must be substituted. ' ..			'Replace  with ' .. ' . '	end

-- Set default arguments. frame = frame or mw.getCurrentFrame title = title or mw.title.getCurrentTitle

-- Process data from the arguments. local splitArgs = splitByPrefix(args) local articles = getPrefixedArgs(args, splitArgs, {'article'}) local authors = getPrefixedArgs(		args,		splitArgs,		{'expander', 'creator', 'writer', 'author'}	) local alts = getPrefixedArgs(args, splitArgs, {'ALT'})

-- Input sanity checks. local errors = {} for i, article in ipairs(articles) do		local articleTitle = makeTitle(article) if not articleTitle then table.insert(errors, formatInvalidTitleError( article, 'article name' ))			articles[i] = '' end end if #articles < 1 then articles[1] = title.subpageText end for i, author in ipairs(authors) do		authors[i] = getRootPageText(author) if not authors[i] then table.insert(errors, formatInvalidTitleError(author, 'user name')) authors[i] = '' end end

-- Substitute the parameters in the output template. local data = { errors = errors, args = args, articles = articles, authors = authors, revisionUser = frame:preprocess(''), alts = alts, title = title, }	local ret = OUTPUT_TEMPLATE:gsub('${([%u_]+)}', function (funcName)		local func = params[funcName]		if not func then			error(string.format(				"invalid parameter '${%s}' " ..				"(no corresponding parameter function found)",				funcName			))		end		return func(data) or ''	end) return ret end

-- This function is also invoked from the JS wizard. Please do NOT rename or -- change the arguments or return format unless the JS wizard is also being -- edited concurrently. function p.getRequiredQpqCount(username) local backlogModeText = mw.title.new('Template:Did you know/Backlog mode?'):getContent local isInBacklogMode = backlogModeText ~= nil and backlogModeText:find('^true') ~= nil local nomsCountData = mw.loadJsonData('User:SDZeroBot/DYK nomination counts.json') local numPriorNoms = nomsCountData[username] or -1 local qpqsRequired = (isInBacklogMode and numPriorNoms >= 20 and 2) or		(numPriorNoms >= 5 and 1) or		0 return qpqsRequired, numPriorNoms end

function p.main(frame) local args = require('Module:Arguments').getArgs(frame, {		wrappers = 'Template:NewDYKnomination'	}) return p._main(args, frame) end

return p