มอดูล:Requested move

-- This module implements .
-- Load necessary modules
local getArgs = require('Module:Arguments').getArgs
local tableTools = require('Module:TableTools')
local yesno = require('Module:Yesno')
-- Set static values
local defaultNewPagename = '?' -- Name of new pages that haven't been specified
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function err(msg, numargs, reason, count)
	-- Generates a wikitext error message
	local commented = 
	return string.format('%s', msg) .. commented
end
local function validateTitle(page, paramName, paramNum) 
	-- Validates a page name, and if it is valid, returns true and the title
	-- object for that page. If it is not valid, returns false and the
	-- appropriate error message.
	-- Check for a small subset of characters that cannot be used in MediaWiki
	-- titles. For the full set of restrictions, see
	-- Wikipedia:Page name#Technical restrictions and limitations. This is
	-- also covered by the invalid title check, but with this check we can give
	-- a more specific error message.
	local invalidChar = page:match('#%%|{}')
	if invalidChar then
		local msg = 'Invalid character "'
			.. invalidChar
			.. '" found in the "'
			.. paramName
			.. paramNum
			.. '" parameter'
		return false, msg
	end
	-- Get the title object. This also checks for invalid titles that aren't
	-- covered by the previous check.
	local titleObj = mw.title.new(page)
	if not titleObj then
		local msg = 'Invalid title detected in parameter "'
			.. paramName
			.. paramNum 
			.. '"; check for invalid characters'
		return false, msg
	end
	-- Check for interwiki links. Titles with interwikis make valid title
	-- objects, but cannot be created on the local wiki.
	local interwiki = titleObj.interwiki
	if interwiki and interwiki ~=  then
		local msg = 'Invalid title detected in parameter "'
			.. paramName
			.. paramNum 
			.. '"; has interwiki prefix "'
			.. titleObj.interwiki
			.. ':"'
		return false, msg
	end
	return true, titleObj
end
--------------------------------------------------------------------------------
-- Validate title entry point (used at Template:RMassist/core)
--------------------------------------------------------------------------------
function p.validateTitle(frame)
	local value = frame.args1
	local validTitle, currentTitle = validateTitle(value or , '1', )
	if not validTitle then
		-- If invalid, the second parameter is the error message.
		local msg = currentTitle
		return msg
	end
	return 'yes'
end
--------------------------------------------------------------------------------
-- Main function
--------------------------------------------------------------------------------
function p.main(frame)
	----------------------------------------------------------------------------
	-- Initialise variables and preprocess the arguments
	----------------------------------------------------------------------------
	
	local args = getArgs(frame, {parentOnly = true})
	local title = mw.title.getCurrentTitle()
	--
	-- To iterate over the current1, new1, current2, new2, ... arguments
	-- we get an array of tables sorted by number and compressed so that
	-- it can be traversed with ipairs.	The table format looks like this:
	-- {
	--  {current = x, new = y, num = 1},
	--  {current = z, new = q, num = 2},
	--  ...
	-- }
	-- The "num" field is used to correctly preserve the number of the parameter
	-- that was used, in case users skip any numbers in the invocation.
	--
	-- The current1 parameter is a special case, as it does not need to be
	-- specified. To avoid clashes with later current parameters, we need to
	-- add it to the args table manually.
	--
	-- Also, we allow the first positional parameter to be an alias for the
	-- new1 parameter, so that the syntax for the old templates
	--  and  will both be supported.
	--
	-- The "multi" variable tracks whether we are using the syntax previously
	-- produced by , or the syntax previously produced by
	-- . For the former, multi is false, and for the latter it is
	-- true.
	--
	if not args.current1 then
		args.current1 = title.subjectPageTitle.prefixedText
	end
	-- Find the first new page title, if specified, and keep a record of the
	-- prefix used to make it; the prefix will be used later to make error
	-- messages.
	local firstNewParam
	if args.new1 then
		firstNewParamPrefix = 'new'
	elseif args1 then
		args.new1 = args1
		firstNewParamPrefix = 
	else
		firstNewParamPrefix = 
	end
	-- Build the sorted argument table.
	local argsByNum = {}
	for k, v in pairs(args) do
		k = tostring(k)
		local prefix, num = k:match('^(%l*)(1-90-9*)$')
		if prefix == 'current' or prefix == 'new' then
			num = tonumber(num)
			local subtable = argsByNumnum or {}
			subtableprefix = v
			subtable.num = num
			argsByNumnum = subtable
		end
	end
	argsByNum = tableTools.compressSparseArray(argsByNum)
	-- Calculate the number of arguments and whether we are dealing with a
	-- multiple nomination.
	local argsByNumCount = #argsByNum
	local multi
	if argsByNumCount >= 2 then
		multi = true
	else
		multi = false
	end
	
	--
	-- Validate new params.
	-- This check ensures we don't have any absent new parameters, and that
	-- users haven't simply copied in the values from the documentation page.
	--
	if multi then
		for i, t in ipairs(argsByNum) do
			local new = t.new
			local num = t.num
			if not new or new == 'New title for page ' .. tostring(num) then
				argsByNumi.new = defaultNewPagename
			end
		end
	else
		local new = argsByNum1.new
		if not new or new == 'NewName' then
			argsByNum1.new = defaultNewPagename
		end
	end
	----------------------------------------------------------------------------
	-- Error checks
	----------------------------------------------------------------------------
	
	-- Subst check
	if not mw.isSubsting() then
		local lb = mw.text.nowiki()
		local msg = 
			.. 'This template must be substituted;'
			.. ' replace %srequested move%s with %ssubst:requested move%s'
			.. 
		msg = string.format(msg, lb, rb, lb, rb)
		return msg
	end
	
	-- Check we are on a talk page
	if not title.isTalkPage then
		local msg = 'Template:Requested move must be used in a TALKSPACE, e.g., %s:%s'
		msg = string.format(msg, mw.site.namespacestitle.namespace.talk.name, title.text)
		return err(msg, argsByNum, args.reason, argsByNumCount)
	end
	
	-- Request to move a single page must be placed on that page's talk
	if not multi and args.current1 ~= title.subjectPageTitle.prefixedText then
		local msg = 'Request to move a single page must be placed on that page\'s talk'
		return err(msg, argsByNum, args.reason, argsByNumCount)
	end
	
	-- Check the arguments
	local currentDupes, newDupes = {}, {}
	for i, t in ipairs(argsByNum) do
		local current = t.current
		local new = t.new
		local num = t.num
		local validCurrent
		local currentTitle
		local subjectSpace
		-- Check for invalid or missing currentn parameters
		-- This check must come first, as mw.title.new will give an error if
		-- it is given invalid input.
		if not current then
			local msg = '"current%d" parameter missing;'
				.. ' please add it or remove the "new%d" parameter'
			msg = string.format(msg, num, num)
			return err(msg, argsByNum, args.reason, argsByNumCount)
		end
		-- Get the currentn title object, and check for invalid titles. This check
		-- must come before the namespace and existence checks, as they will
		-- produce script errors if the title object doesn't exist.
		validCurrent, currentTitle = validateTitle(current, 'current', num)
		if not validCurrent then
			-- If invalid, the second parameter is the error message.
			local msg = currentTitle 
			return err(msg, argsByNum, args.reason, argsByNumCount)
		end
		-- Category namespace check
		subjectSpace = mw.site.namespacescurrentTitle.namespace.subject.id
		if subjectSpace == 14 then
			local msg = 'Template:Requested move is not for categories,'
				.. ' see Wikipedia:Categories for discussion'
			return err(msg, argsByNum, args.reason, argsByNumCount)
		
		-- File namespace check
		elseif subjectSpace == 6 then
			local msg = 'Template:Requested move is not for files;'
				.. ' see Wikipedia:Moving a page#Moving a file page'
				.. ' (use Template:Rename media instead)'
			return err(msg, argsByNum, args.reason, argsByNumCount)
		-- Draft and User namespace check
		elseif subjectSpace == 2 or subjectSpace == 118 then
			local msg = 'Template:Requested move is not for moves from draft or user space.'
				.. 'If you would like to submit your draft for review, add subst:submit'
				    .. 'to the top of the page.'
				.. 'Otherwise, see Help:How to move a page for instructions.'
				.. 'If you cannot move it yourself, see Requesting technical moves.'
			return err(msg, argsByNum, args.reason, argsByNumCount)
		end
		-- Check for non-existent titles.
		if not currentTitle.exists then
			local msg = 'Must create :%s before requesting that it be moved'
			msg = string.format(msg, current)
			return err(msg, argsByNum, args.reason, argsByNumCount)
		end
		-- Check for duplicate current titles
		-- We know the id isn't zero because we have already checked for
		-- existence.
		local currentId = currentTitle.id
		if currentDupescurrentId then
			local msg = 'Duplicate title detected ("'
				.. currentTitle.prefixedText
				.. '"); cannot move the same page to two different places'
			return err(msg, argsByNum, args.reason, argsByNumCount)
		else
			currentDupescurrentId = true
		end
		-- Check for invalid new titles. This check must come before the
		-- duplicate title check for new titles, as it will produce a script
		-- error if the title object doesn't exist.
		local validNew, newTitle = validateTitle(
			new,
			multi and 'new' or firstNewParamPrefix,
			num
		)
		if not validNew then
			-- If invalid, the second parameter is the error message.
			local msg = newTitle
			return err(msg, argsByNum, args.reason, argsByNumCount)
		end
		-- Check for duplicate new titles.
		-- We can't use the page_id, as new pages might not exist, and therefore
		-- multiple pages may have an id of 0. Use the prefixedText as a
		-- reasonable fallback. We also need to check that we aren't using the
		-- default new page name, as we don't want it to be treated as a duplicate
		-- page if more than one new page name has been omitted.
		local newPrefixedText = newTitle.prefixedText
		if newPrefixedText ~= defaultNewPagename then
			if newDupesnewPrefixedText then
				local msg = 'Duplicate title detected ("'
					.. newTitle.prefixedText
					.. '"); cannot move two different pages to the same place'
				return err(msg, argsByNum, args.reason, argsByNumCount)
			else
				newDupesnewPrefixedText = true
			end
		end
	end
	----------------------------------------------------------------------------
	-- Generate the heading
	----------------------------------------------------------------------------
	-- For custom values of |heading=, use those.
	-- For |heading=no, |heading=n, etc., don't include a heading.
	-- Otherwise use the current date as a heading.
	local heading = args.heading or args.header
	local useHeading = yesno(heading, heading)
	if heading and useHeading == heading then
		heading = '== ' .. heading .. ' ==\n\n'
	elseif useHeading == false then
		heading = 
	else
		local lang = mw.language.getContentLanguage()
		local headingDate = lang:formatDate('j F xkY')
		heading = '== เสนอย้าย ' .. headingDate .. ' ==\n\n'
	end
	
	----------------------------------------------------------------------------
	-- Build the  invocation
	----------------------------------------------------------------------------
	local rmd = {}
	rmd#rmd + 1 = 
	rmd = table.concat(rmd)
	----------------------------------------------------------------------------
	-- Generate the list of links to the pages to be moved
	----------------------------------------------------------------------------
	local linkList = {}
	for i, t in ipairs(argsByNum) do
		local current = t.current
		local new = t.new
		local msg = '\n%s:%s → '
		if new ~= defaultNewPagename then
			msg = msg .. '%s'
		else
			msg = msg .. '%s'
		end
		local item = string.format(
			msg,
			multi and '* ' or , -- Don't make a list for single page moves.
			current,
			new
		)
		linkList#linkList + 1 = item
	end
	linkList = table.concat(linkList)
	----------------------------------------------------------------------------
	-- Reason and talk blurb
	----------------------------------------------------------------------------
	-- Reason
	local reason = args.reason or args2 or 'Please place your rationale for the proposed move here.'
	if not multi then
		reason = '– ' .. reason
	end
	if yesno(args.sign or args.sig or args.signature or 'unspecified', not reason:match("~~~$")) then
		reason = reason .. ' ~~~~'
	end
	-- Talk blurb
	local talk
	if yesno(args.talk, true) then
		talk = frame:expandTemplate{title = 'Requested move/talk'}
	else
		talk = 
	end
	----------------------------------------------------------------------------
	-- Assemble the output
	----------------------------------------------------------------------------
	-- The old templates start with a line break, so we will do that too.
	local ret = string.format(
		'\n%s%s\n%s%s%s%s',
		heading,
		rmd,
		linkList,
		multi and '\n' or ' ',
		reason,
		talk
	)
	return ret
end
return p