-- This module implements .  -- Load necessary modules local getArgs = require('Module:Arguments').getArgs local tableTools = require('Module:TableTools') local yesno = require('Module:Yesno') local mRedirect = require('Module:Redirect')  -- 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('', 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 = 'অবৈধ চিহ্ন "' 			.. 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 = 'প্যারামিটারে অবৈধ শিরোনাম পাওয়া গেছে "' 			.. paramName 			.. paramNum  			.. '"; check for [[উইকিপিডিয়া:পাতার নাম#' 			.. 'কারিগরি বিধিনিষেধ ও সীমাবদ্ধতা|অবৈধ চিহ্ন]]' 		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 = 'প্যারামিটারে অবৈধ শিরোনাম পাওয়া গেছে "' 			.. paramName 			.. paramNum  			.. '"; যাতে আন্তঃউইকি উপসর্গ আছে "' 			.. 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.args[1] 	local validTitle, currentTitle = validateTitle(value or , '1', ) 	if not validTitle then 		-- If invalid, the second parameter is the error message. 		return currentTitle 	end 	return 'yes' end  -- Confirm protection levels (used at টেমপ্লেট:স্থানান্তরের অনুরোধ/তারিখযুক্ত)  function p.protected(frame) 	local args = getArgs(frame, {parentOnly = true}) 	if args.protected then 		local levels = mw.title.new(args.protected).protectionLevels 		local levelMove = levels['move'] and levels['move'][1] 		local levelEdit = levels['edit'] and levels['edit'][1] 		local levelCreate = levels['create'] and levels['create'][1] 		if levelMove == 'sysop'  		or levelEdit == 'sysop'  		or levelEdit == 'editprotected'  		or levelCreate == 'sysop' then 			return 'sysop' 		elseif levelMove == 'templateeditor'  		or levelEdit == 'templateeditor'  		or levelCreate == 'templateeditor' then 			return 'templateeditor' 		end 	end 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 args[1] then 		args.new1 = args[1] 		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-9][0-9]*)$') 		if prefix == 'current' or prefix == 'new' then 			num = tonumber(num) 			local subtable = argsByNum[num] or {} 			subtable[prefix] = v 			subtable.num = num 			argsByNum[num] = 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 = (argsByNumCount >= 2)  	--[[ 	-- 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 				argsByNum[i].new = defaultNewPagename 			end 		end 	else 		local new = argsByNum[1].new 		if not new or new == 'নতুননাম' then 			argsByNum[1].new = defaultNewPagename 		end 	end 	 	---------------------------------------------------------------------------- 	-- Error checks 	---------------------------------------------------------------------------- 	 	-- Subst check 	if not mw.isSubsting() then 		local lb = mw.text.nowiki('{{') 		local rb = mw.text.nowiki('}}') 		local msg = '' 			.. 'এই টেমপ্লেটকে উপকল্পন করতে হবে;' 			.. ' %sস্থানান্তরের অনুরোধ%s লেখাকে %ssubst:স্থানান্তরের অনুরোধ%s প্রতিস্থাপন করুন' 			.. '' 		msg = string.format(msg, lb, rb, lb, rb) 		return msg 	end 	  	 	 	 if not title == 'উইকিপিডিয়া:স্থানান্তরের অনুরোধ'	then  	if not title.isTalkPage then 		local msg = 'টেমপ্লেট:স্থানান্তরের অনুরোধ আলাপ পাতায় ব্যবহার করতে হবে, যেমন, %s:%s' 		msg = string.format(msg, mw.site.namespaces[title.namespace].talk.name, title.text) 		return err(msg, argsByNum, args.reason, argsByNumCount) 	end 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" প্যারামিটার পাওয়া যায়নি;' 				.. ' অনুগ্রহপূর্বক তা যুক্ত করুন অথবা "new%d" প্যারামিটার সরিয়ে দিন' 			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.namespaces[currentTitle.namespace].subject.id 		if subjectSpace == 14 then 			local msg = 'টেমপ্লেট:স্থানান্তরের অনুরোধ বিষয়শ্রেণীর জন্য নয়,' 				.. ' উইকিপিডিয়া:আলোচনার জন্য বিষয়শ্রেণী দেখুন' 			return err(msg, argsByNum, args.reason, argsByNumCount) 		 		-- File namespace check 		elseif subjectSpace == 6 then 			local msg = 'টেমপ্লেট:স্থানান্তরের অনুরোধ ফাইলের জন্য নয়;' 				.. ' উইকিপিডিয়া:একটি পাতা স্থানান্তর#একটি ফাইল পাতা স্থানান্তর দেখুন' 				.. ' (পরিবর্তে টেমপ্লেট:মিডিয়া পুনঃনাম ব্যবহার করুন)' 			return err(msg, argsByNum, args.reason, argsByNumCount)  		-- Draft and User namespace check 		elseif subjectSpace == 2 or subjectSpace == 118 then 			local msg = 'টেমপ্লেট:স্থানান্তরের অনুরোধ ব্যবহারকারী পাতা বা খসড়া থেকে স্থানান্তর কাজের জন্য নয়।' 				.. 'পর্যালোচনার জন্য নিজের খসড়া জমা দিতে চাইলে,  যুক্ত করুন' 				    .. 'পাতার শীর্ষে।' 				.. 'অন্যথায়, নির্দেশনার জন্য সাহায্য:কিভাবে একটি পাতা স্থানান্তর করবেন দেখুন।' 				.. 'নিজে থেকে স্থানান্তর করতে না পারলে, অনুরোধকৃত কারিগরি স্থানান্তর দেখুন।' 			return err(msg, argsByNum, args.reason, argsByNumCount) 		end  		-- Request to move a single page must be placed on that page's talk, or the page it redirects to 		if not multi and args.current1 ~= title.subjectPageTitle.prefixedText then 			local idealpage = mw.title.new(args.current1).talkPageTitle 			local rtarget = mRedirect.getTarget(idealpage) 			if rtarget == title.prefixedText then 				multi = true 			else 				local msg = 'একক পাতা স্থানান্তরের অনুরোধ সেই পাতার আলাপ পাতা কিংবা যে পাতার আলাপ পাতাটি পুনর্নির্দেশ করা আছে সেখানে বসাতে হবে' 				return err(msg, argsByNum, args.reason, argsByNumCount) 			end 		end  		-- Check for non-existent titles. 		if not currentTitle.exists then 			local msg = ':%s স্থানান্তর অনুরোধের পূর্বে :%s সৃষ্টি করা আবশ্যক' 			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 currentDupes[currentId] then 			local msg = 'একই নামে দুটি শিরোনাম পাওয়া গেছে ("' 				.. currentTitle.prefixedText 				.. '"); একই পাতা দুটি ভিন্ন স্থানে স্থানান্তর করা যাবেনা' 			return err(msg, argsByNum, args.reason, argsByNumCount) 		else 			currentDupes[currentId] = 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 newDupes[newPrefixedText] then 				local msg = 'একই নামে দুটি শিরোনাম পাওয়া গেছে ("' 					.. newTitle.prefixedText 					.. '"); একই স্থানে দুটি ভিন্ন পাতা স্থানান্তর করা যাবেনা' 				return err(msg, argsByNum, args.reason, argsByNumCount) 			else 				newDupes[newPrefixedText] = true 			end 		end 	end 	 	---------------------------------------------------------------------------- 	-- Check for page protection 	---------------------------------------------------------------------------- 	 	local highestProtection =  	local protectedTitle = 	 	-- Checking page protection requires use of .protectionLevels(), one of the  	-- "expensive" parser functions, which stop working after 500 uses total. 	-- Without some limit set, this starts breaking near 250 distinct titles. 	local titleLimit = 80 	local titlesChecked = 0 	local titles = {} 	-- Consolidate duplicate titles (i.e., when moving A to B and B to C) 	for i = 1,argsByNumCount do 		titles[mw.title.new(argsByNum[i]['current'])] = true 		titles[mw.title.new(argsByNum[i]['new'])] = true 	end 	-- Check each title t, while ignoring the "true" value 	for t, _ in pairs(titles) do 		if titlesChecked < titleLimit then 			local levels = t.protectionLevels 			titlesChecked = titlesChecked + 1 			local levelMove = levels['move'] and levels['move'][1] 			local levelEdit = levels['edit'] and levels['edit'][1] 			local levelCreate = levels['create'] and levels['create'][1] 			if levelMove == 'sysop'  			or levelEdit == 'sysop'  			or levelEdit == 'editprotected'  			or levelCreate == 'sysop' then 				highestProtection = 'sysop' 				protectedTitle = tostring(t) 				break 			elseif levelMove == 'templateeditor'  			or levelEdit == 'templateeditor'  			or levelCreate == 'templateeditor' then 				highestProtection = 'templateeditor' 				protectedTitle = tostring(t) 			end 		else 		-- End the "for" loop if the titleLimit is reached 			break 		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 Y') 		heading = '== স্থানান্তরের অনুরোধ ' .. headingDate .. ' ==\n\n' 	end 	 	---------------------------------------------------------------------------- 	-- Build the  invocation 	----------------------------------------------------------------------------  	local rmd = {} 	rmd[#rmd + 1] = '{{স্থানান্তরের অনুরোধ/তারিখযুক্ত'  	if multi then 		rmd[#rmd + 1] = '|multiple=yes' 		rmd[#rmd + 1] = '\n|current1=' .. argsByNum[1].current 	end  	--[[ 	-- The first new title. This is used both by single and by multi; for single 	-- it is the only parameter used. For single the parameter name is the first 	-- positional parameter, and for multi the parameter name is "new1". 	--]] 	local new1param = multi and 'new1=' or 	rmd[#rmd + 1] = '|' .. new1param .. argsByNum[1].new  	-- Add more arguments for multi. 	if multi then 		for i = 2, argsByNumCount do 			local t = argsByNum[i] 			local numString = tostring(i) 			local current = t.current 			local new = t.new 			rmd[#rmd + 1] = '|current' .. numString .. '=' .. current 			rmd[#rmd + 1] = '|new' .. numString .. '=' .. new 		end 	end  	 	-- Highest page protection (if admin or template-editor) 	if highestProtection == 'sysop' or highestProtection == 'templateeditor' then 		rmd[#rmd + 1] = '|protected=' .. protectedTitle 	end 	 	-- Pass through demo=yes to the  	if args.demo ~= nil then 		rmd[#rmd + 1] = '|demo=' 		rmd[#rmd + 1] = args.demo 	end 	if args[2] then 		rmd[#rmd + 1] = '|পুরাতননাম=' 		rmd[#rmd + 1] = args[2] 	end  		 	-- The old multi template always has a bar before the closing curly 	-- braces, so we will do that too. 	if multi then 		rmd[#rmd + 1] = '|' 	end 	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 .. '' 		else 			msg = msg .. '%s' 		end 		local item = string.format( 			msg, 			multi and '* ' or , -- Don't make a list for single page moves. 			args[2] or current, 			new 		) 		linkList[#linkList + 1] = item 	end 	linkList = table.concat(linkList)  	---------------------------------------------------------------------------- 	-- Reason and talk blurb 	----------------------------------------------------------------------------  	-- Reason 	local reason = args.reason or args[2] or 'অনুগ্রহ করে প্রস্তাবিত স্থানান্তরের জন্য আপনার যুক্তি লিখুন।' 	reason = '– ' .. reason 	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 = 'স্থানান্তরের অনুরোধ/আলাপ'} 	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