وحدة:Requested move
توثيق الوحدة [أنشئ] [محو الاختزان][استخدامات] [قوالب]
-- This module implements {{requested move}}.
-- 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 = '<!-- {{نسخ:نقل|'
if count ~= 1 then
commented = commented .. 'إلى1='
end
commented = commented .. numargs[1]['إلى']
for i = 2,count do
commented = commented .. string.format('|من%i=%s', i, (numargs[i]['من'] or ''))
commented = commented .. string.format('|إلى%i=%s', i, (numargs[i]['إلى'] or ''))
end
if reason then
commented = commented .. '|السبب=' .. reason
end
commented = 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 = 'حرف غير صالح "'
.. invalidChar
.. '" في الوسيط "'
.. paramName
.. paramNum
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
.. '"; افحص [[:en:Wikipedia:Page name#'
.. 'Technical restrictions and limitations|الأحرف غير المسموح به]]'
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
.. '"; has [[مساعدة:وصلات إنترويكي|بادئة انترويكي]] "'
.. titleObj.interwiki
.. ':"'
return false, msg
end
return true, titleObj
end
--------------------------------------------------------------------------------
-- Validate title entry point (used at [[قالب: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 [[قالب:Requested move/dated]])
--------------------------------------------------------------------------------
local function _protected(pagetitle)
local levels = mw.title.new(pagetitle).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 == 'editeditorprotected'
or levelEdit == 'editeditorprotected'
or levelCreate == 'editeditorprotected' then
return 'editor'
end
end
function p.protected(frame)
local args = getArgs(frame, {parentOnly = true})
if args.protected then
return _protected(args.protected)
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
-- {{requested move}} and {{move-multi}} will both be supported.
--
-- The "multi" variable tracks whether we are using the syntax previously
-- produced by {{requested move}}, or the syntax previously produced by
-- {{move-multi}}. For the former, multi is false, and for the latter it is
-- true.
--]]
if not args["من1"] then
args["من1"] = 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["إلى1"] then
firstNewParamPrefix = 'إلى'
elseif args[1] then
args["إلى1"] = 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 = mw.ustring.match(k, '^(%a*)([1-9][0-9]*)$')
if prefix == 'من' or prefix == 'إلى' 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["إلى"]
local num = t.num
if not new or new == 'عنوان جديد لـ ' .. tostring(num) then
argsByNum[i]["إلى"] = defaultNewPagename
end
end
else
local new = argsByNum[1]["إلى"]
if not new or new == 'عنوان جديد' then
argsByNum[1]["إلى"] = defaultNewPagename
end
end
----------------------------------------------------------------------------
-- Error checks
----------------------------------------------------------------------------
-- Subst check
if not mw.isSubsting() then
local lb = mw.text.nowiki('{{')
local rb = mw.text.nowiki('}}')
local msg = '<strong class="error">'
.. 'هذا القالب يجب أن [[:en:Wikipedia:Template substitution|يُنسخ]];'
.. ' استبدل بـ%sنقل%s %sنسخ:نقل%s'
.. '</strong>'
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 = '[[قالب:نقل]] يجب أن يستعمل في صفحة نقاش, مثلا [[%s:%s]]'
msg = string.format(msg, mw.site.namespaces[title.namespace].talk.name, title.text)
return err(msg, argsByNum, args["السبب"], argsByNumCount)
end
-- Check the arguments
local currentDupes, newDupes = {}, {}
for i, t in ipairs(argsByNum) do
local current = t["من"]
local new = t["إلى"]
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 = 'وسيط "من%d" مفقود;'
.. ' أضفه أو أزل الوسيط "إلى%d"'
msg = string.format(msg, num, num)
return err(msg, argsByNum, args["السبب"], 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, 'من', num)
if not validCurrent then
-- If invalid, the second parameter is the error message.
local msg = currentTitle
return err(msg, argsByNum, args["السبب"], argsByNumCount)
end
-- Category namespace check
subjectSpace = mw.site.namespaces[currentTitle.namespace].subject.id
if subjectSpace == 14 then
local msg = '[[قالب:نقل]] is not for categories,'
.. ' see [[Wikipedia:Categories for discussion]]'
return err(msg, argsByNum, args["السبب"], argsByNumCount)
-- File namespace check
elseif subjectSpace == 6 then
local msg = '[[قالب:نقل]] is not for files;'
.. ' see [[Wikipedia:Moving a page#Moving a file page]]'
.. ' (use [[قالب:Rename media]] instead)'
return err(msg, argsByNum, args["السبب"], argsByNumCount)
-- Draft and User namespace check
elseif subjectSpace == 2 or subjectSpace == 118 then
local msg = '[[قالب:نقل]] is not for moves from draft or user space.'
.. '<br>If you would like to submit your draft for review, add <code>{{tlf|subst:submit}}</code>'
.. 'to the top of the page.'
.. '<br>Otherwise, see [[مساعدة:How to move a page]] for instructions.'
.. '<br>If you cannot move it yourself, see [[Wikipedia:Requested moves#Requesting technical moves|Requesting technical moves]].'
return err(msg, argsByNum, args["السبب"], 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["من1"] ~= title.subjectPageTitle.prefixedText then
local idealpage = mw.title.new(args["من1"]).talkPageTitle
local rtarget = mRedirect.getTarget(idealpage)
if rtarget == title.prefixedText then
multi = true
else
local msg = 'Request to move a single page must be placed on that page\'s talk or the page its talk redirects to'
return err(msg, argsByNum, args["السبب"], argsByNumCount)
end
end
-- Check for non-existent titles.
if not currentTitle.exists then
local msg = 'يجب إنشاء [[:%s]] قبل طلب نقلها'
msg = string.format(msg, current)
return err(msg, argsByNum, args["السبب"], 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["السبب"], 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 'إلى' or firstNewParamPrefix,
num
)
if not validNew then
-- If invalid, the second parameter is the error message.
local msg = newTitle
return err(msg, argsByNum, args["السبب"], 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["السبب"], 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]['من'])] = true
titles[mw.title.new(argsByNum[i]['إلى'])] = 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 == 'editeditorprotected'
or levelEdit == 'editeditorprotected'
or levelCreate == 'editeditorprotected' then
highestProtection = 'editeditorprotected'
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['عنونة'] or args.header
local useHeading = yesno(heading, heading)
local lang = mw.language.getContentLanguage()
if heading and useHeading == heading then
heading = '== ' .. heading .. ' ==\n\n'
elseif useHeading == false then
heading = ''
else
local headingDate = lang:formatDate('j F Y')
heading = '== طلب نقل ' .. headingDate .. ' ==\n\n'
end
----------------------------------------------------------------------------
-- Build the {{requested move/dated}} invocation
----------------------------------------------------------------------------
local rmd = {}
rmd[#rmd + 1] = '{{نقل/مؤرخ'
if multi then
rmd[#rmd + 1] = '|متعددة=1\n'
end
rmd[#rmd + 1] = '|من1=' .. argsByNum[1]["من"]
--[[
-- 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 'إلى1=' or ''
rmd[#rmd + 1] = '|' .. new1param .. argsByNum[1]["إلى"]
-- Add more arguments for multi.
if multi then
for i = 2, argsByNumCount do
local t = argsByNum[i]
local numString = tostring(i)
local current = t["من"]
local new = t["إلى"]
rmd[#rmd + 1] = '|من' .. numString .. '=' .. current
rmd[#rmd + 1] = '|إلى' .. numString .. '=' .. new
end
end
-- date
rmd[#rmd + 1] = '|تاريخ=' .. lang:formatDate('Y-m-d')
rmd[#rmd + 1] = '|حالة=مفتوح'
rmd[#rmd + 1] = '|الخلاصة='
-- Highest page protection (if admin or template-editor)
if highestProtection == 'sysop' or highestProtection == 'editor' 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
-- 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["من"]
local new = t["إلى"]
local msg = '\n%s[[:%s]] ← '
if new ~= defaultNewPagename then
msg = msg .. '{{no redirect|%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["السبب"] 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 = 'Requested move/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
function p.message(frame)
local args = getArgs(frame, {parentOnly = true})
local title = mw.title.getCurrentTitle()
local targetpage = args['إلى1'] or args[1]
local sourcepage = args['من1']
local subjectpage = title.subjectPageTitle.prefixedText
local spage = sourcepage or subjectpage
local state = args["حالة"] or args["الحالة"] or "مفتوح"
local protected = args["protected"] or "no"
local multi = args["متعددة"] and args["متعددة"] == "1" or false
local ret = ""
if state ~= "مفتوح" then
ret = "هنا نقاش مقترح مغلق لنقل اسم الصفحة " .. (sourcepage and ("من [[" .. sourcepage .. "]]") or "") .. " إلى [[" .. targetpage .. "]]. <span style=\"color:red\">'''رجاءً لا تغير النص التالي '''</span>\n\n"
local closer = args['خلاصة'] or args['الخلاصة']
if closer then
ret = ret .. "[[ملف:Cheese.svg|وصلة=|36بك]] '''خلاصة:''' " .. closer .. "\n\n"
if state == "مغلق لما ينفذ" then
ret = ret .."(التنفيذ يحتاج إلى صلاحيات إدارية)"
end
end
else
local function ns_name(ns_num)
return ns_num == 0 and "المقالات" or mw.site.namespaces[ns_num].name
end
if protected ~= "no" then
ret = "[[تصنيف:نقل عناوين المقالات المحمية]]<div style=\"float:left\">"
local protectionlvl = _protected(protected)
if protectionlvl == 'sysop' then
ret =ret .. "[[ملف:Move-protection-shackle.svg|20px|link=Wikipedia:Move protection]]"
elseif protectionlvl == 'editor' then
ret =ret .. "[[ملف:Template-protection-shackle.svg|20px|link=Wikipedia:Template protection]]"
end
ret = ret .. "</div>"
end
ret=ret .. "'''اقتُرح في هذا القسم [[ويكيبيديا:نقل صفحة|نقل]] "
if multi and args["من2"] then
ret = ret .. "عدة صفحات'''"
else
ret = ret .. "[[:" .. (spage) .. "]]''' إلى "
end
if targetpage == "?" then
ret = ret .. " وأسماء أخرى، بالأسماء المنصوص عليها بالأسفل"
elseif targetpage then
local target_title = mw.title.new(targetpage)
if target_title.exists and target_title.isRedirect then
ret = ret .. "[" .. target_title:fullUrl("redirect=no") .. " " .. targetpage .. "]"
else
ret = ret .. "[[:" .. targetpage .. "]]"
end
end
ret=ret .. "."
if args["تاريخ"] then
ret = ret .. '<div class="floatleft"><small style="line-height:1.2em;">' .. args["تاريخ"] .. "</small></div>"
end
ret=ret .. "<br>"
if spage and targetpage then
local source_ns = spage and mw.title.new(spage).namespace or nil
local target_ns = targetpage and mw.title.new(targetpage).namespace or nil
if not multi and source_ns ~= target_ns then
ret = ret .. [[<small style="line-height:1.2em;">''هذا مقترح نقل عابر للنطاقات من ]] .. ns_name(source_ns) .. " إلى " .. ns_name(target_ns) .. " [[ويكيبيديا:نطاق]].''</small>"
end
end
ret = ret .. "ربما يغلق النقاش بعد 7 أيام من فتحه إذا حصل التوافق . الرجاء أن تكون العناوين موافقة [[ويكيبيديا:عناوين المقالات#Deciding on an article title|لسياسة عناوين المقالات]]، والحفاظ على نقاش موضوعي و[[ويكيبيديا:أدب|مؤدب]].\n----\n"
ret=ret .. "<small style=\"line-height:1.2em;\">''استخدم {{[[قالب:نقل|نسخ:نقل]]}}. و'''لا''' تستخدم {{[[قالب:نقل/مؤرخ|نقل/مؤرخ]]}} مباشرة.'' </small>"
-- if multi then
ret = ret .. "<div class=\"floatleft\"><small style=\"line-height:1.2em;\">''وصلات:'' [" .. tostring(mw.uri.fullUrl("Special:Log/move", "page=" .. spage)) .. " سجل الحالية] "
if targetpage and targetpage ~= "?" then
ret= ret .. " • [" .. tostring(mw.uri.fullUrl("Special:Log/move", "page=" .. targetpage)) .. " سجل الوجهة] • [" .. tostring(mw.uri.fullUrl("Special:MovePage", "wpOldTitle=" .. spage .. "&wpNewTitle=" .. targetpage .. "&wpReason=" .. "[[ويكيبيديا:طلبات النقل|نقاش النقل]]، انظر [[" .. mw.title.new(targetpage).talkPageTitle.prefixedText .. "]]&wpMovetalk=1")) .. " تنفيذ النقل]"
end
-- end
ret= ret .. "</small></div>"
end
return ret
end
function p.catit(frame)
local args = getArgs(frame, {parentOnly = true})
local title = mw.title.getCurrentTitle()
local ret = {}
local state = args["حالة"] or args["الحالة"] or "مفتوح"
if not title.isTalkPage then
return ''
end
if state == "مغلق" then
ret[1] = "[[تصنيف:مقترحات نقل مغلقة]]"
elseif state == "مغلق لما ينفذ" then
ret[1] = "[[تصنيف:صفحات للنقل]]"
else
local content = mw.title.new(title.subjectPageTitle.prefixedText):getContent() or '';
if args["تاريخ"] then
local lang = mw.language.getContentLanguage()
ret[#ret+1] = "[[تصنيف:مقترحات نقل منذ " .. lang:formatDate('F Y',args["تاريخ"]) .. "]]"
end
if not content then
return table.concat(ret,'');
end
local start = content:find('{{ *شريط بوابات[%s\n]*[|}]');
if start then
local config_template = content:match ('%b{}', start);
if config_template then
local params_t = mw.text.split (config_template:gsub ('^{{%s*', ''):gsub ('[%s\n]*}}$', ''), '[%s\n]*|[%s\n]*');
table.remove (params_t, 1);
for _, param in ipairs (params_t) do
ret[#ret+1] = '[[تصنيف:مقترحات نقل لمقالات بوابة ' .. param .. ']]'
end
end
end
end
return table.concat(ret,'');
end
return p